From 3256ef984b016fc8491f34cad594168b4b500317 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Mon, 2 Nov 2020 20:40:08 +0530 Subject: [PATCH 1/7] ASoC: soc-core: Fix component name_prefix parsing The "prefix" can be defined in DAI link node or it can be specified as part of the component node itself. Currently "sound-name-prefix" defined in a component is not taking effect. Actually the property is not getting parsed. It can be fixed by parsing "sound-name-prefix" property whenever "prefix" is missing in DAI link Codec node. Signed-off-by: Sameer Pujar Link: https://lore.kernel.org/r/1604329814-24779-2-git-send-email-spujar@nvidia.com Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index ea3986a46c12..cb7333da58f1 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1124,7 +1124,8 @@ static void soc_set_name_prefix(struct snd_soc_card *card, for (i = 0; i < card->num_configs; i++) { struct snd_soc_codec_conf *map = &card->codec_conf[i]; - if (snd_soc_is_matching_component(&map->dlc, component)) { + if (snd_soc_is_matching_component(&map->dlc, component) && + map->name_prefix) { component->name_prefix = map->name_prefix; return; } From aa293777bfeb75fb8872565ef99cc0e8b98b5c7d Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Mon, 2 Nov 2020 20:40:09 +0530 Subject: [PATCH 2/7] ASoC: soc-pcm: Get all BEs along DAPM path dpcm_end_walk_at_be() stops the graph walk when first BE is found for the given FE component. In a component model we may want to connect multiple DAIs from different components. A new flag is introduced in 'snd_soc_card', which when set allows DAI/component chaining. Later PCM operations can be called for all these listed components for a valid DAPM path. Signed-off-by: Sameer Pujar Link: https://lore.kernel.org/r/1604329814-24779-3-git-send-email-spujar@nvidia.com Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/soc-pcm.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index 5ac578c9340c..4a9958b9b532 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1084,6 +1084,7 @@ struct snd_soc_card { unsigned int fully_routed:1; unsigned int disable_route_checks:1; unsigned int probed:1; + unsigned int component_chaining:1; void *drvdata; }; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 2a39f355b3a4..aaab5cfe986b 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1259,7 +1259,8 @@ int dpcm_path_get(struct snd_soc_pcm_runtime *fe, /* get number of valid DAI paths and their widgets */ paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list, - dpcm_end_walk_at_be); + fe->card->component_chaining ? + NULL : dpcm_end_walk_at_be); dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths, stream ? "capture" : "playback"); From e6aeb375d25dba56c4089b1d6aa0a77fe218ef3b Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Mon, 2 Nov 2020 20:40:10 +0530 Subject: [PATCH 3/7] ASoC: audio-graph: Use of_node and DAI for DPCM DAI link names For multiple instances of components, using DAI name alone for DAI links is causing conflicts. Components can define multiple DAIs and hence using just a device name won't help either. Thus DT device node reference and DAI names are used to uniquely represent DAI link names. Signed-off-by: Sameer Pujar Acked-by: Kuninori Morimoto Link: https://lore.kernel.org/r/1604329814-24779-4-git-send-email-spujar@nvidia.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 97b4f5480a31..1e205629e89a 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -253,7 +253,8 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv, goto out_put_node; ret = asoc_simple_set_dailink_name(dev, dai_link, - "fe.%s", + "fe.%pOFP.%s", + cpus->of_node, cpus->dai_name); if (ret < 0) goto out_put_node; @@ -287,7 +288,8 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv, goto out_put_node; ret = asoc_simple_set_dailink_name(dev, dai_link, - "be.%s", + "be.%pOFP.%s", + codecs->of_node, codecs->dai_name); if (ret < 0) goto out_put_node; From c21cbb526c0a105d582299839a9c4244dd6bf38a Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Mon, 2 Nov 2020 20:40:11 +0530 Subject: [PATCH 4/7] ASoC: audio-graph: Identify 'no_pcm' DAI links for DPCM PCM devices are created for FE dai links with 'no-pcm' flag as '0'. Such DAI links have CPU component which implement either pcm_construct() or pcm_new() at component or dai level respectively. Based on this, current patch exposes a helper function to identify such components and populate 'no_pcm' flag for DPCM DAI link. This helps to have BE<->BE component links where PCM devices need not be created for CPU component involved in such links. Signed-off-by: Sameer Pujar Cc: Kuninori Morimoto Link: https://lore.kernel.org/r/1604329814-24779-5-git-send-email-spujar@nvidia.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 1e205629e89a..9b0684173a35 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -111,6 +111,17 @@ static int graph_get_dai_id(struct device_node *ep) return id; } +static bool soc_component_is_pcm(struct snd_soc_dai_link_component *dlc) +{ + struct snd_soc_dai *dai = snd_soc_find_dai_with_mutex(dlc); + + if (dai && (dai->component->driver->pcm_construct || + dai->driver->pcm_new)) + return true; + + return false; +} + static int asoc_simple_parse_dai(struct device_node *ep, struct snd_soc_dai_link_component *dlc, int *is_single_link) @@ -205,6 +216,7 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv, int dup_codec) { struct device *dev = simple_priv_to_dev(priv); + struct snd_soc_card *card = simple_priv_to_card(priv); struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); struct device_node *top = dev->of_node; @@ -259,6 +271,19 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv, if (ret < 0) goto out_put_node; + /* + * In BE<->BE connections it is not required to create + * PCM devices at CPU end of the dai link and thus 'no_pcm' + * flag needs to be set. It is useful when there are many + * BE components and some of these have to be connected to + * form a valid audio path. + * + * For example: FE <-> BE1 <-> BE2 <-> ... <-> BEn where + * there are 'n' BE components in the path. + */ + if (card->component_chaining && !soc_component_is_pcm(cpus)) + dai_link->no_pcm = 1; + /* card->num_links includes Codec */ asoc_simple_canonicalize_cpu(dai_link, is_single_links); } else { From 930dd47d74023e7c94a7c256279e12924c14475d Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Mon, 2 Nov 2020 20:40:12 +0530 Subject: [PATCH 5/7] ASoC: audio-graph: Support empty Codec endpoint For open platforms, which can support pluggable audio cards, Codec endpoint is not fixed always. It actually depends on the compatible HW module that is going to be connected. From SoC side the given I/O interface is always available. Hence such links have fixed CPU endpoint but no Codec endpoint. This patch helps to support such links where user can populate Codec endpoint only and its fields in Platform DT depending on the plugged HW. Signed-off-by: Sameer Pujar Cc: Kuninori Morimoto Link: https://lore.kernel.org/r/1604329814-24779-6-git-send-email-spujar@nvidia.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 9b0684173a35..0ba50be9ccdb 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -229,6 +229,14 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv, struct snd_soc_dai_link_component *codecs = dai_link->codecs; int ret; + /* + * Codec endpoint can be NULL for pluggable audio HW. + * Platform DT can populate the Codec endpoint depending on the + * plugged HW. + */ + if (!li->cpu && !codec_ep) + return 0; + /* Do it all CPU endpoint, and 1st Codec endpoint */ if (!li->cpu && dup_codec) return 0; @@ -565,7 +573,7 @@ static int graph_count_dpcm(struct asoc_simple_priv *priv, li->link++; /* 1xCPU-dummy */ li->dais++; /* 1xCPU */ - if (!dup_codec) { + if (!dup_codec && codec_ep) { li->link++; /* 1xdummy-Codec */ li->conf++; /* 1xdummy-Codec */ li->dais++; /* 1xCodec */ From d09c774f2f9ff25817866b70f1fb9603e5196971 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Mon, 2 Nov 2020 20:40:13 +0530 Subject: [PATCH 6/7] ASoC: audio-graph: Expose new members for asoc_simple_priv Add new members in struct 'asoc_simple_priv'. Idea is to leverage simple or graph card driver as much as possible and vendor can maintain a thin driver to control the behavior by populating these newly exposed members. Following are the members added in 'asoc_simple_priv': - 'ops' struct: In some cases SoC vendor drivers may want to implement 'snd_soc_ops' callbacks differently. In such cases custom callbacks would be used. - 'force_dpcm' flag: Right now simple or graph card drivers detect DAI links as DPCM links if: * The dpcm_selectable is set AND * Codec is connected to multiple CPU endpoints or aconvert property is used for rate/channels. So there is no way to directly specify usage of DPCM alone. So a flag is exposed to mark all links as DPCM. Vendor driver can set this if required. - 'dpcm_selectable': Currently simple or audio graph drivers provide a way to enable this for specific compatibles. However vendor driver may want to define some additional info. Thus expose this variable where vendor drivers can set this if required. Audio graph driver is updated to consider above flags or callbacks. Subsequent patches in the series illustrate usage for above. Signed-off-by: Sameer Pujar Cc: Kuninori Morimoto Link: https://lore.kernel.org/r/1604329814-24779-7-git-send-email-spujar@nvidia.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 3 ++ sound/soc/generic/audio-graph-card.c | 41 ++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 86a1e956991e..ba4a3e1897b9 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -56,6 +56,9 @@ struct asoc_simple_priv { struct asoc_simple_dai *dais; struct snd_soc_codec_conf *codec_conf; struct gpio_desc *pa_gpio; + const struct snd_soc_ops *ops; + unsigned int dpcm_selectable:1; + unsigned int force_dpcm:1; }; #define simple_priv_to_card(priv) (&(priv)->snd_card) #define simple_priv_to_props(priv, i) ((priv)->dai_props + (i)) diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 0ba50be9ccdb..4db9c0e5445d 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -355,6 +355,11 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv, snd_soc_dai_link_set_capabilities(dai_link); dai_link->ops = &graph_ops; + + /* Use custom snd_soc_ops callbacks if available */ + if (priv->ops) + dai_link->ops = priv->ops; + dai_link->init = asoc_simple_dai_init; out_put_node: @@ -439,6 +444,28 @@ static int graph_dai_link_of(struct asoc_simple_priv *priv, return 0; } +static inline bool parse_as_dpcm_link(struct asoc_simple_priv *priv, + struct device_node *codec_port, + struct asoc_simple_data *adata) +{ + if (priv->force_dpcm) + return true; + + if (!priv->dpcm_selectable) + return false; + + /* + * It is DPCM + * if Codec port has many endpoints, + * or has convert-xxx property + */ + if ((of_get_child_count(codec_port) > 1) || + (adata->convert_rate || adata->convert_channels)) + return true; + + return false; +} + static int graph_for_each_link(struct asoc_simple_priv *priv, struct link_info *li, int (*func_noml)(struct asoc_simple_priv *priv, @@ -459,7 +486,6 @@ static int graph_for_each_link(struct asoc_simple_priv *priv, struct device_node *codec_port; struct device_node *codec_port_old = NULL; struct asoc_simple_data adata; - uintptr_t dpcm_selectable = (uintptr_t)of_device_get_match_data(dev); int rc, ret; /* loop for all listed CPU port */ @@ -482,14 +508,8 @@ static int graph_for_each_link(struct asoc_simple_priv *priv, graph_parse_convert(dev, codec_ep, &adata); graph_parse_convert(dev, cpu_ep, &adata); - /* - * It is DPCM - * if Codec port has many endpoints, - * or has convert-xxx property - */ - if (dpcm_selectable && - ((of_get_child_count(codec_port) > 1) || - adata.convert_rate || adata.convert_channels)) + /* check if link requires DPCM parsing */ + if (parse_as_dpcm_link(priv, codec_port, &adata)) ret = func_dpcm(priv, cpu_ep, codec_ep, li, (codec_port_old == codec_port)); /* else normal sound */ @@ -678,6 +698,9 @@ static int graph_probe(struct platform_device *pdev) card->num_dapm_widgets = ARRAY_SIZE(graph_dapm_widgets); card->probe = graph_card_probe; + if (of_device_get_match_data(dev)) + priv->dpcm_selectable = 1; + memset(&li, 0, sizeof(li)); graph_get_dais_count(priv, &li); if (!li.link || !li.dais) From e32b100bc6ecbc390aae728fc7d2a3e247faa8a7 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Mon, 2 Nov 2020 20:40:14 +0530 Subject: [PATCH 7/7] ASoC: audio-graph: Expose helpers from audio graph This commit exposes following functions which can be used by a sound card driver based on generic audio graph driver. Idea is vendors can have a thin driver and re-use common stuff from audio graph driver. - graph_card_probe() - graph_parse_of() In doing so a new header file is added for above. The graph_probe() function is simplified by moving more common stuff to graph_parse_of(). Signed-off-by: Sameer Pujar Cc: Kuninori Morimoto Link: https://lore.kernel.org/r/1604329814-24779-8-git-send-email-spujar@nvidia.com Signed-off-by: Mark Brown --- include/sound/graph_card.h | 16 +++++ sound/soc/generic/audio-graph-card.c | 95 +++++++++++++++------------- 2 files changed, 66 insertions(+), 45 deletions(-) create mode 100644 include/sound/graph_card.h diff --git a/include/sound/graph_card.h b/include/sound/graph_card.h new file mode 100644 index 000000000000..bbb5a137855c --- /dev/null +++ b/include/sound/graph_card.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * ASoC audio graph card support + * + */ + +#ifndef __GRAPH_CARD_H +#define __GRAPH_CARD_H + +#include + +int graph_card_probe(struct snd_soc_card *card); + +int graph_parse_of(struct asoc_simple_priv *priv, struct device *dev); + +#endif /* __GRAPH_CARD_H */ diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 4db9c0e5445d..16a04a678828 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #define DPCM_SELECTABLE 1 @@ -529,12 +529,34 @@ static int graph_for_each_link(struct asoc_simple_priv *priv, return 0; } -static int graph_parse_of(struct asoc_simple_priv *priv) +static void graph_get_dais_count(struct asoc_simple_priv *priv, + struct link_info *li); + +int graph_parse_of(struct asoc_simple_priv *priv, struct device *dev) { struct snd_soc_card *card = simple_priv_to_card(priv); struct link_info li; int ret; + card->owner = THIS_MODULE; + card->dev = dev; + + memset(&li, 0, sizeof(li)); + graph_get_dais_count(priv, &li); + if (!li.link || !li.dais) + return -EINVAL; + + ret = asoc_simple_init_priv(priv, &li); + if (ret < 0) + return ret; + + priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); + if (IS_ERR(priv->pa_gpio)) { + ret = PTR_ERR(priv->pa_gpio); + dev_err(dev, "failed to get amplifier gpio: %d\n", ret); + return ret; + } + ret = asoc_simple_parse_widgets(card, NULL); if (ret < 0) return ret; @@ -561,11 +583,32 @@ static int graph_parse_of(struct asoc_simple_priv *priv) graph_dai_link_of, graph_dai_link_of_dpcm); if (ret < 0) - return ret; + goto err; } - return asoc_simple_parse_card_name(card, NULL); + ret = asoc_simple_parse_card_name(card, NULL); + if (ret < 0) + goto err; + + snd_soc_card_set_drvdata(card, priv); + + asoc_simple_debug_info(priv); + + ret = devm_snd_soc_register_card(dev, card); + if (ret < 0) + goto err; + + return 0; + +err: + asoc_simple_clean_reference(card); + + if (ret != -EPROBE_DEFER) + dev_err(dev, "parse error %d\n", ret); + + return ret; } +EXPORT_SYMBOL_GPL(graph_parse_of); static int graph_count_noml(struct asoc_simple_priv *priv, struct device_node *cpu_ep, @@ -662,7 +705,7 @@ static void graph_get_dais_count(struct asoc_simple_priv *priv, li->link, li->dais, li->conf); } -static int graph_card_probe(struct snd_soc_card *card) +int graph_card_probe(struct snd_soc_card *card) { struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card); int ret; @@ -677,14 +720,13 @@ static int graph_card_probe(struct snd_soc_card *card) return 0; } +EXPORT_SYMBOL_GPL(graph_card_probe); static int graph_probe(struct platform_device *pdev) { struct asoc_simple_priv *priv; struct device *dev = &pdev->dev; struct snd_soc_card *card; - struct link_info li; - int ret; /* Allocate the private data and the DAI link array */ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -692,8 +734,6 @@ static int graph_probe(struct platform_device *pdev) return -ENOMEM; card = simple_priv_to_card(priv); - card->owner = THIS_MODULE; - card->dev = dev; card->dapm_widgets = graph_dapm_widgets; card->num_dapm_widgets = ARRAY_SIZE(graph_dapm_widgets); card->probe = graph_card_probe; @@ -701,42 +741,7 @@ static int graph_probe(struct platform_device *pdev) if (of_device_get_match_data(dev)) priv->dpcm_selectable = 1; - memset(&li, 0, sizeof(li)); - graph_get_dais_count(priv, &li); - if (!li.link || !li.dais) - return -EINVAL; - - ret = asoc_simple_init_priv(priv, &li); - if (ret < 0) - return ret; - - priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); - if (IS_ERR(priv->pa_gpio)) { - ret = PTR_ERR(priv->pa_gpio); - dev_err(dev, "failed to get amplifier gpio: %d\n", ret); - return ret; - } - - ret = graph_parse_of(priv); - if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "parse error %d\n", ret); - goto err; - } - - snd_soc_card_set_drvdata(card, priv); - - asoc_simple_debug_info(priv); - - ret = devm_snd_soc_register_card(dev, card); - if (ret < 0) - goto err; - - return 0; -err: - asoc_simple_clean_reference(card); - - return ret; + return graph_parse_of(priv, dev); } static int graph_remove(struct platform_device *pdev)