[alsa-devel] [PATCH 1/4] ASoC: when initializing CPU DAI, don't duplicate any CODEC init
From: Stephen Warren swarren@nvidia.com
If the CPU-side of a DAI link is a CODEC rather than a standalone DAI, the codec initialization will call try_module_get() and create the DAI widgets. Ensure that this isn't duplicated when the CPU DAI itself is probed, if the CPU DAI is part of a CODEC.
Note that this is not a complete fix on its own, since there's no guarantee that the CODEC itself will be initialized - currently that only happens if the CODEC is also used as the CODEC-side of a DAI link, and that initialization may happen before or after the DAIs within the CODEC are initialized. However, such a scenario doesn't necessarily currently work, and I don't think this change alone makes it any worse. This is fixed in a couple patches time.
Signed-off-by: Stephen Warren swarren@nvidia.com --- sound/soc/soc-core.c | 14 +++++++++----- 1 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 3d803f3..448d4a7 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -983,7 +983,9 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order) } cpu_dai->probed = 0; list_del(&cpu_dai->card_list); - module_put(cpu_dai->dev->driver->owner); + + if (!cpu_dai->codec) + module_put(cpu_dai->dev->driver->owner); } }
@@ -1257,11 +1259,13 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order) /* probe the cpu_dai */ if (!cpu_dai->probed && cpu_dai->driver->probe_order == order) { - cpu_dai->dapm.card = card; - if (!try_module_get(cpu_dai->dev->driver->owner)) - return -ENODEV; + if (!cpu_dai->codec) { + cpu_dai->dapm.card = card; + if (!try_module_get(cpu_dai->dev->driver->owner)) + return -ENODEV;
- snd_soc_dapm_new_dai_widgets(&cpu_dai->dapm, cpu_dai); + snd_soc_dapm_new_dai_widgets(&cpu_dai->dapm, cpu_dai); + }
if (cpu_dai->driver->probe) { ret = cpu_dai->driver->probe(cpu_dai);
From: Stephen Warren swarren@nvidia.com
When a standalone CPU DAI (one not part of a CODEC) is probed, widgets are created for it. Add a call to snd_soc_dapm_free() in order to clean these up when the CPU DAI is removed.
In order for snd_soc_dapm_free() to work, the CPU DAI's DAPM context's list member must be initialized, since snd_soc_dapm_free() removes that from the list it's part of. Add it to the card's list of DAPM contexts.
Signed-off-by: Stephen Warren swarren@nvidia.com --- sound/soc/soc-core.c | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 448d4a7..621c5bd 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -984,8 +984,10 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order) cpu_dai->probed = 0; list_del(&cpu_dai->card_list);
- if (!cpu_dai->codec) + if (!cpu_dai->codec) { + snd_soc_dapm_free(&cpu_dai->dapm); module_put(cpu_dai->dev->driver->owner); + } } }
@@ -1264,6 +1266,7 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order) if (!try_module_get(cpu_dai->dev->driver->owner)) return -ENODEV;
+ list_add(&cpu_dai->dapm.list, &card->dapm_list); snd_soc_dapm_new_dai_widgets(&cpu_dai->dapm, cpu_dai); }
From: Stephen Warren swarren@nvidia.com
This change simply factors out part of soc_remove_dai_link() into a standalone function. This makes platform and CODEC removal much more similar at the call-sites.
Signed-off-by: Stephen Warren swarren@nvidia.com --- sound/soc/soc-core.c | 40 ++++++++++++++++++++++++---------------- 1 files changed, 24 insertions(+), 16 deletions(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 621c5bd..a539ade 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -898,6 +898,28 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) return 0; }
+static int soc_remove_platform(struct snd_soc_platform *platform) +{ + int ret; + + if (platform->driver->remove) { + ret = platform->driver->remove(platform); + if (ret < 0) + pr_err("asoc: failed to remove %s: %d\n", + platform->name, ret); + } + + /* Make sure all DAPM widgets are freed */ + snd_soc_dapm_free(&platform->dapm); + + soc_cleanup_platform_debugfs(platform); + platform->probed = 0; + list_del(&platform->card_list); + module_put(platform->dev->driver->owner); + + return 0; +} + static void soc_remove_codec(struct snd_soc_codec *codec) { int err; @@ -950,22 +972,8 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order)
/* remove the platform */ if (platform && platform->probed && - platform->driver->remove_order == order) { - if (platform->driver->remove) { - err = platform->driver->remove(platform); - if (err < 0) - pr_err("asoc: failed to remove %s: %d\n", - platform->name, err); - } - - /* Make sure all DAPM widgets are freed */ - snd_soc_dapm_free(&platform->dapm); - - soc_cleanup_platform_debugfs(platform); - platform->probed = 0; - list_del(&platform->card_list); - module_put(platform->dev->driver->owner); - } + platform->driver->remove_order == order) + soc_remove_platform(platform);
/* remove the CODEC */ if (codec && codec->probed &&
From: Stephen Warren swarren@nvidia.com
soc_probe_dai_link() currently inter-mixes the probing of CODECs, platforms, and DAIs. This can lead to problems such as a CODEC's DAI being probed before the CODEC, if that DAI is used as the CPU-side of a DAI link without any other of the CODEC's DAIs having been used as the CODEC-side of any DAI link that was probed earlier.
To solve this, split soc_probe_dai_link() into soc_probe_link_components() and soc_probe_link_dais(). The former is used to probe all CODECs and platforms used by a card first, and then the latter is used to probe all the DAIs and links later.
A similar change is made to soc_remove_dai_links().
Signed-off-by: Stephen Warren swarren@nvidia.com --- sound/soc/soc-core.c | 129 ++++++++++++++++++++++++++++++++++++------------- 1 files changed, 95 insertions(+), 34 deletions(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index a539ade..fe16135 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -941,11 +941,9 @@ static void soc_remove_codec(struct snd_soc_codec *codec) module_put(codec->dev->driver->owner); }
-static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order) +static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order) { struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai; int err;
@@ -970,16 +968,6 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order) list_del(&codec_dai->card_list); }
- /* remove the platform */ - if (platform && platform->probed && - platform->driver->remove_order == order) - soc_remove_platform(platform); - - /* remove the CODEC */ - if (codec && codec->probed && - codec->driver->remove_order == order) - soc_remove_codec(codec); - /* remove the cpu_dai */ if (cpu_dai && cpu_dai->probed && cpu_dai->driver->remove_order == order) { @@ -999,6 +987,38 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order) } }
+static void soc_remove_link_components(struct snd_soc_card *card, int num, + int order) +{ + struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_codec *codec; + + /* remove the platform */ + if (platform && platform->probed && + platform->driver->remove_order == order) { + soc_remove_platform(platform); + } + + /* remove the CODEC-side CODEC */ + if (codec_dai) { + codec = codec_dai->codec; + if (codec && codec->probed && + codec->driver->remove_order == order) + soc_remove_codec(codec); + } + + /* remove any CPU-side CODEC */ + if (cpu_dai) { + codec = cpu_dai->codec; + if (codec && codec->probed && + codec->driver->remove_order == order) + soc_remove_codec(codec); + } +} + static void soc_remove_dai_links(struct snd_soc_card *card) { int dai, order; @@ -1006,8 +1026,15 @@ static void soc_remove_dai_links(struct snd_soc_card *card) for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; order++) { for (dai = 0; dai < card->num_rtd; dai++) - soc_remove_dai_link(card, dai, order); + soc_remove_link_dais(card, dai, order); + } + + for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; + order++) { + for (dai = 0; dai < card->num_rtd; dai++) + soc_remove_link_components(card, dai, order); } + card->num_rtd = 0; }
@@ -1244,7 +1271,44 @@ out: return 0; }
-static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order) +static int soc_probe_link_components(struct snd_soc_card *card, int num, + int order) +{ + struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_platform *platform = rtd->platform; + int ret; + + /* probe the CPU-side component, if it is a CODEC */ + if (cpu_dai->codec && + !cpu_dai->codec->probed && + cpu_dai->codec->driver->probe_order == order) { + ret = soc_probe_codec(card, cpu_dai->codec); + if (ret < 0) + return ret; + } + + /* probe the CODEC-side component */ + if (!codec_dai->codec->probed && + codec_dai->codec->driver->probe_order == order) { + ret = soc_probe_codec(card, codec_dai->codec); + if (ret < 0) + return ret; + } + + /* probe the platform */ + if (!platform->probed && + platform->driver->probe_order == order) { + ret = soc_probe_platform(card, platform); + if (ret < 0) + return ret; + } + + return 0; +} + +static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) { struct snd_soc_dai_link *dai_link = &card->dai_link[num]; struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; @@ -1292,22 +1356,6 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order) list_add(&cpu_dai->card_list, &card->dai_dev_list); }
- /* probe the CODEC */ - if (!codec->probed && - codec->driver->probe_order == order) { - ret = soc_probe_codec(card, codec); - if (ret < 0) - return ret; - } - - /* probe the platform */ - if (!platform->probed && - platform->driver->probe_order == order) { - ret = soc_probe_platform(card, platform); - if (ret < 0) - return ret; - } - /* probe the CODEC DAI */ if (!codec_dai->probed && codec_dai->driver->probe_order == order) { if (codec_dai->driver->probe) { @@ -1582,14 +1630,27 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) goto card_probe_error; }
- /* early DAI link probe */ + /* probe all components used by DAI links on this card */ for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; order++) { for (i = 0; i < card->num_links; i++) { - ret = soc_probe_dai_link(card, i, order); + ret = soc_probe_link_components(card, i, order); if (ret < 0) { pr_err("asoc: failed to instantiate card %s: %d\n", - card->name, ret); + card->name, ret); + goto probe_dai_err; + } + } + } + + /* probe all DAI links on this card */ + for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; + order++) { + for (i = 0; i < card->num_links; i++) { + ret = soc_probe_link_dais(card, i, order); + if (ret < 0) { + pr_err("asoc: failed to instantiate card %s: %d\n", + card->name, ret); goto probe_dai_err; } }
On Fri, Jun 08, 2012 at 12:34:20PM -0600, Stephen Warren wrote:
From: Stephen Warren swarren@nvidia.com
If the CPU-side of a DAI link is a CODEC rather than a standalone DAI, the codec initialization will call try_module_get() and create the DAI widgets. Ensure that this isn't duplicated when the CPU DAI itself is probed, if the CPU DAI is part of a CODEC.
Applied all, thanks.
participants (2)
-
Mark Brown
-
Stephen Warren