[alsa-devel] [PATCH RFC 0/3] ASoC: Add Multi CPU DAI support
From: Bard Liao yung-chuan.liao@linux.intel.com
As discussed in [1], ASoC core supports multi codec DAIs on a DAI link. However it does not do so for CPU DAIs.
So, add support for multi CPU DAIs on a DAI Link by adding multi CPU DAI in Card instantiation, suspend and resume functions, PCM ops, stream handling functions and DAPM.
[1]: https://www.spinics.net/lists/alsa-devel/msg71369.html
Shreyas NC (3): ASoC: Add initial support for multiple CPU DAIs ASoC: Add multiple CPU DAI support for PCM ops ASoC: Add multiple CPU DAI support in DAPM
include/sound/soc.h | 15 ++ sound/soc/soc-core.c | 183 ++++++++++-------- sound/soc/soc-dapm.c | 131 +++++++------ sound/soc/soc-pcm.c | 432 ++++++++++++++++++++++++++++--------------- 4 files changed, 483 insertions(+), 278 deletions(-)
From: Shreyas NC shreyas.nc@intel.com
ASoC core supports multiple codec DAIs but supports only a CPU DAI. To support multiple cpu DAIs, add cpu_dai and num_cpu_dai in snd_soc_dai_link and snd_soc_pcm_runtime structures similar to support for codec_dai. This is intended as a preparatory patch to eventually support the unification of the Codec and CPU DAI.
Inline with multiple codec DAI approach, add support to allocate, init, bind and probe multiple cpu_dai on init if driver specifies that. Also add support to loop over multiple cpu_dai during suspend and resume.
This is intended as a preparatory patch to eventually unify the CPU and Codec DAI into DAI components.
Signed-off-by: Shreyas NC shreyas.nc@intel.com Signed-off-by: Bard Liao yung-chuan.liao@linux.intel.com --- include/sound/soc.h | 15 ++++ sound/soc/soc-core.c | 183 +++++++++++++++++++++++++------------------ 2 files changed, 120 insertions(+), 78 deletions(-)
diff --git a/include/sound/soc.h b/include/sound/soc.h index 262896799826..ab18c8ab95f0 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -868,6 +868,11 @@ struct snd_soc_dai_link { ((platform) = &link->platforms[i]); \ (i)++)
+#define for_each_link_cpus(link, i, cpu) \ + for ((i) = 0; \ + ((i) < link->num_cpus) && ((cpu) = &link->cpus[i]); \ + (i)++) + /* * Sample 1 : Single CPU/Codec/Platform * @@ -1149,6 +1154,9 @@ struct snd_soc_pcm_runtime { struct snd_soc_dai **codec_dais; unsigned int num_codecs;
+ struct snd_soc_dai **cpu_dais; + unsigned int num_cpu_dai; + struct delayed_work delayed_work; void (*close_delayed_work_func)(struct snd_soc_pcm_runtime *rtd); #ifdef CONFIG_DEBUG_FS @@ -1170,6 +1178,13 @@ struct snd_soc_pcm_runtime { #define for_each_rtd_codec_dai_rollback(rtd, i, dai) \ for (; ((--i) >= 0) && ((dai) = rtd->codec_dais[i]);)
+#define for_each_rtd_cpu_dai(rtd, i, dai)\ + for ((i) = 0; \ + ((i) < rtd->num_cpu_dai) && ((dai) = rtd->cpu_dais[i]); \ + (i)++) +#define for_each_rtd_cpu_dai_rollback(rtd, i, dai) \ + for (; ((--i) >= 0) && ((dai) = rtd->cpu_dais[i]);) +
/* mixer control */ struct soc_mixer_control { diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 5e54d02ad653..b8d678211ddc 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -491,6 +491,14 @@ static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( if (!rtd->codec_dais) goto free_rtd;
+ /* + * for rtd->cpu_dais + */ + rtd->cpu_dais = devm_kcalloc(dev, dai_link->num_cpus, + sizeof(struct snd_soc_dai *), + GFP_KERNEL); + if (!rtd->cpu_dais) + goto free_rtd; /* * rtd remaining settings */ @@ -587,13 +595,15 @@ int snd_soc_suspend(struct device *dev) card->suspend_pre(card);
for_each_card_rtds(card, rtd) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai;
if (rtd->dai_link->ignore_suspend) continue;
- if (!cpu_dai->driver->bus_control) - snd_soc_dai_suspend(cpu_dai); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (!cpu_dai->driver->bus_control) + snd_soc_dai_suspend(cpu_dai); + } }
/* close any waiting streams */ @@ -658,16 +668,18 @@ int snd_soc_suspend(struct device *dev) }
for_each_card_rtds(card, rtd) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai;
if (rtd->dai_link->ignore_suspend) continue;
- if (cpu_dai->driver->bus_control) - snd_soc_dai_suspend(cpu_dai); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (cpu_dai->driver->bus_control) + snd_soc_dai_suspend(cpu_dai);
- /* deactivate pins to sleep state */ - pinctrl_pm_select_sleep_state(cpu_dai->dev); + /* deactivate pins to sleep state */ + pinctrl_pm_select_sleep_state(cpu_dai->dev); + } }
if (card->suspend_post) @@ -705,13 +717,15 @@ static void soc_resume_deferred(struct work_struct *work)
/* resume control bus DAIs */ for_each_card_rtds(card, rtd) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai;
if (rtd->dai_link->ignore_suspend) continue;
- if (cpu_dai->driver->bus_control) - snd_soc_dai_resume(cpu_dai); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (cpu_dai->driver->bus_control) + snd_soc_dai_resume(cpu_dai); + } }
for_each_card_components(card, component) { @@ -748,13 +762,15 @@ static void soc_resume_deferred(struct work_struct *work) }
for_each_card_rtds(card, rtd) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai;
if (rtd->dai_link->ignore_suspend) continue;
- if (!cpu_dai->driver->bus_control) - snd_soc_dai_resume(cpu_dai); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (!cpu_dai->driver->bus_control) + snd_soc_dai_resume(cpu_dai); + } }
if (card->resume_post) @@ -777,6 +793,7 @@ int snd_soc_resume(struct device *dev) bool bus_control = false; struct snd_soc_pcm_runtime *rtd; struct snd_soc_dai *codec_dai; + struct snd_soc_dai *cpu_dai; int i;
/* If the card is not initialized yet there is nothing to do */ @@ -785,10 +802,10 @@ int snd_soc_resume(struct device *dev)
/* activate pins from sleep state */ for_each_card_rtds(card, rtd) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - - if (cpu_dai->active) - pinctrl_pm_select_default_state(cpu_dai->dev); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (cpu_dai->active) + pinctrl_pm_select_default_state(cpu_dai->dev); + }
for_each_rtd_codec_dai(rtd, i, codec_dai) { if (codec_dai->active) @@ -803,9 +820,8 @@ int snd_soc_resume(struct device *dev) * due to I/O costs and anti-pop so handle them out of line. */ for_each_card_rtds(card, rtd) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - - bus_control |= cpu_dai->driver->bus_control; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + bus_control |= cpu_dai->driver->bus_control; } if (bus_control) { dev_dbg(dev, "ASoC: Resuming control bus master immediately\n"); @@ -1020,14 +1036,6 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card, return -EPROBE_DEFER; }
- /* FIXME */ - if (link->num_cpus > 1) { - dev_err(card->dev, - "ASoC: multi cpu is not yet supported %s\n", - link->name); - return -EINVAL; - } - /* * CPU device may be specified by either name or OF node, but * can be left unspecified, and will be matched based on DAI @@ -1109,7 +1117,7 @@ int snd_soc_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) { struct snd_soc_pcm_runtime *rtd; - struct snd_soc_dai_link_component *codec, *platform; + struct snd_soc_dai_link_component *codec, *platform, *cpu; struct snd_soc_component *component; int i, ret;
@@ -1134,14 +1142,19 @@ int snd_soc_add_dai_link(struct snd_soc_card *card, if (!rtd) return -ENOMEM;
- /* FIXME: we need multi CPU support in the future */ - rtd->cpu_dai = snd_soc_find_dai(dai_link->cpus); - if (!rtd->cpu_dai) { - dev_info(card->dev, "ASoC: CPU DAI %s not registered\n", - dai_link->cpus->dai_name); - goto _err_defer; + rtd->num_cpu_dai = dai_link->num_cpus; + for_each_link_cpus(dai_link, i, cpu) { + rtd->cpu_dais[i] = snd_soc_find_dai(cpu); + if (!rtd->cpu_dais[i]) { + dev_info(card->dev, "ASoC: CPU DAI %s not registered\n", + cpu->dai_name); + goto _err_defer; + } + snd_soc_rtdcom_add(rtd, rtd->cpu_dais[i]->component); } - snd_soc_rtdcom_add(rtd, rtd->cpu_dai->component); + + /* Single cpu links expect cpu and cpu_dai in runtime data */ + rtd->cpu_dai = rtd->cpu_dais[0];
/* Find CODEC from registered CODECs */ rtd->num_codecs = dai_link->num_codecs; @@ -1372,6 +1385,7 @@ static void soc_remove_link_dais(struct snd_soc_card *card) { int i; struct snd_soc_dai *codec_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_pcm_runtime *rtd; int order;
@@ -1381,14 +1395,15 @@ static void soc_remove_link_dais(struct snd_soc_card *card) for_each_rtd_codec_dai(rtd, i, codec_dai) soc_remove_dai(codec_dai, order);
- soc_remove_dai(rtd->cpu_dai, order); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + soc_remove_dai(cpu_dai, order); } } }
static int soc_probe_link_dais(struct snd_soc_card *card) { - struct snd_soc_dai *codec_dai; + struct snd_soc_dai *codec_dai, *cpu_dai; struct snd_soc_pcm_runtime *rtd; int i, order, ret;
@@ -1399,9 +1414,12 @@ static int soc_probe_link_dais(struct snd_soc_card *card) "ASoC: probe %s dai link %d late %d\n", card->name, rtd->num, order);
- ret = soc_probe_dai(rtd->cpu_dai, order); - if (ret) - return ret; + /* probe the CPU DAI */ + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + ret = soc_probe_dai(cpu_dai, order); + if (ret) + return ret; + }
/* probe the CODEC DAI */ for_each_rtd_codec_dai(rtd, i, codec_dai) { @@ -1494,10 +1512,11 @@ static int soc_link_init(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai_link *dai_link = rtd->dai_link; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_component *component; int ret, num; + int i;
/* set default power off timeout */ rtd->pmdown_time = pmdown_time; @@ -1539,12 +1558,14 @@ static int soc_link_init(struct snd_soc_card *card, }
/* create compress_device if possible */ - ret = snd_soc_dai_compress_new(cpu_dai, rtd, num); - if (ret != -ENOTSUPP) { - if (ret < 0) - dev_err(card->dev, "ASoC: can't create compress %s\n", + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + ret = snd_soc_dai_compress_new(cpu_dai, rtd, num); + if (ret != -ENOTSUPP) { + if (ret < 0) + dev_err(card->dev, "ASoC: can't create compress %s\n", dai_link->stream_name); - return ret; + return ret; + } }
/* create the pcm */ @@ -1554,7 +1575,7 @@ static int soc_link_init(struct snd_soc_card *card, dai_link->stream_name, ret); return ret; } - ret = soc_link_dai_pcm_new(&cpu_dai, 1, rtd); + ret = soc_link_dai_pcm_new(rtd->cpu_dais, rtd->num_cpu_dai, rtd); if (ret < 0) return ret; ret = soc_link_dai_pcm_new(rtd->codec_dais, @@ -1640,7 +1661,7 @@ static void soc_remove_aux_devices(struct snd_soc_card *card) int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, unsigned int dai_fmt) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; unsigned int i; int ret; @@ -1658,33 +1679,35 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, * Flip the polarity for the "CPU" end of a CODEC<->CODEC link * the component which has non_legacy_dai_naming is Codec */ - if (cpu_dai->component->driver->non_legacy_dai_naming) { - unsigned int inv_dai_fmt; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (cpu_dai->component->driver->non_legacy_dai_naming) { + unsigned int inv_dai_fmt; + + inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK; + switch (dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + break; + case SND_SOC_DAIFMT_CBM_CFS: + inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; + break; + case SND_SOC_DAIFMT_CBS_CFM: + inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; + break; + case SND_SOC_DAIFMT_CBS_CFS: + inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + break; + }
- inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK; - switch (dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; - break; - case SND_SOC_DAIFMT_CBM_CFS: - inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; - break; - case SND_SOC_DAIFMT_CBS_CFM: - inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; - break; - case SND_SOC_DAIFMT_CBS_CFS: - inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; - break; + dai_fmt = inv_dai_fmt; }
- dai_fmt = inv_dai_fmt; - } - - ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt); - if (ret != 0 && ret != -ENOTSUPP) { - dev_warn(cpu_dai->dev, - "ASoC: Failed to set DAI format: %d\n", ret); - return ret; + ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt); + if (ret != 0 && ret != -ENOTSUPP) { + dev_warn(cpu_dai->dev, + "ASoC: Failed to set DAI format: %d\n", ret); + return ret; + } }
return 0; @@ -2149,8 +2172,10 @@ static int snd_soc_bind_card(struct snd_soc_card *card) pinctrl_pm_select_sleep_state(dai->dev); }
- if (!rtd->cpu_dai->active) - pinctrl_pm_select_sleep_state(rtd->cpu_dai->dev); + for_each_rtd_cpu_dai(rtd, i, dai) { + if (!dai->active) + pinctrl_pm_select_sleep_state(dai->dev); + } }
probe_end: @@ -2212,11 +2237,13 @@ int snd_soc_poweroff(struct device *dev)
/* deactivate pins to sleep state */ for_each_card_rtds(card, rtd) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i;
- pinctrl_pm_select_sleep_state(cpu_dai->dev); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + pinctrl_pm_select_sleep_state(cpu_dai->dev); + } for_each_rtd_codec_dai(rtd, i, codec_dai) { pinctrl_pm_select_sleep_state(codec_dai->dev); }
Hi Bard
Actually I have plan to post semi-multi CPU DAI support as part of ASoC cleanup. The reason why "semi-multi" is that I can't test all features. It cares Multi-CPU as much as possible.
Thus, your and my patches are 100% conflict :) But, it is OK for me. I'm happy to adjust to yours.
Some comment from me
From: Shreyas NC shreyas.nc@intel.com
ASoC core supports multiple codec DAIs but supports only a CPU DAI. To support multiple cpu DAIs, add cpu_dai and num_cpu_dai in snd_soc_dai_link and snd_soc_pcm_runtime structures similar to support for codec_dai. This is intended as a preparatory patch to eventually support the unification of the Codec and CPU DAI.
Inline with multiple codec DAI approach, add support to allocate, init, bind and probe multiple cpu_dai on init if driver specifies that. Also add support to loop over multiple cpu_dai during suspend and resume.
This is intended as a preparatory patch to eventually unify the CPU and Codec DAI into DAI components.
Signed-off-by: Shreyas NC shreyas.nc@intel.com Signed-off-by: Bard Liao yung-chuan.liao@linux.intel.com
(snip)
@@ -1149,6 +1154,9 @@ struct snd_soc_pcm_runtime { struct snd_soc_dai **codec_dais; unsigned int num_codecs;
- struct snd_soc_dai **cpu_dais;
- unsigned int num_cpu_dai;
Codec is using "num_codecs"; So, I think "num_cpus" is better for CPU.
@@ -1020,14 +1036,6 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card, return -EPROBE_DEFER; }
- /* FIXME */
- if (link->num_cpus > 1) {
dev_err(card->dev,
"ASoC: multi cpu is not yet supported %s\n",
link->name);
return -EINVAL;
- }
- /*
- CPU device may be specified by either name or OF node, but
- can be left unspecified, and will be matched based on DAI
I think you want to sanity check for each CPU DAI by using for_each_rtd_cpu_dai() here, not only removing FIXME ?
In my quick check, I think your patch-set doesn't care these functions ? # Mine version is also doesn't care. Thus it indicates warning message there. # Because I'm not sure how to handle these...
- soc_init_pcm_runtime() - snd_dmaengine_pcm_prepare_slave_config() - dmaengine_pcm_set_runtime_hwparams() - dmaengine_pcm_compat_request_channel() - dpcm_state_read_file() - dpcm_path_get() - dpcm_runtime_merge_chan() - soc_new_pcm() - soc_dpcm_fe_runtime_update()
For soc_new_pcm(), I think you need to care playback/capture flag for Multi-CPU/Codec.
For soc_dpcm_fe_runtime_update(), you need to care about Multi-CPU. But, it doesn't care Multi-Codec, either...
I attached mine version.
Thank you for your help !! Best regards --- Kuninori Morimoto
-----Original Message----- From: Kuninori Morimoto [mailto:kuninori.morimoto.gx@renesas.com] Sent: Thursday, December 26, 2019 4:17 PM To: Bard liao yung-chuan.liao@linux.intel.com Cc: broonie@kernel.org; alsa-devel@alsa-project.org; tiwai@suse.de; liam.r.girdwood@linux.intel.com; pierre-louis.bossart@linux.intel.com; Liao, Bard bard.liao@intel.com Subject: Re: [PATCH RFC 1/3] ASoC: Add initial support for multiple CPU DAIs
Hi Bard
Actually I have plan to post semi-multi CPU DAI support as part of ASoC cleanup. The reason why "semi-multi" is that I can't test all features. It cares Multi-CPU as much as possible.
Thus, your and my patches are 100% conflict :) But, it is OK for me. I'm happy to adjust to yours.
Some comment from me
From: Shreyas NC shreyas.nc@intel.com
ASoC core supports multiple codec DAIs but supports only a CPU DAI. To support multiple cpu DAIs, add cpu_dai and num_cpu_dai in snd_soc_dai_link and snd_soc_pcm_runtime structures similar to support for codec_dai. This is intended as a preparatory patch to eventually support the unification of the Codec and CPU DAI.
Inline with multiple codec DAI approach, add support to allocate, init, bind and probe multiple cpu_dai on init if driver specifies that. Also add support to loop over multiple cpu_dai during suspend and resume.
This is intended as a preparatory patch to eventually unify the CPU and Codec DAI into DAI components.
Signed-off-by: Shreyas NC shreyas.nc@intel.com Signed-off-by: Bard Liao yung-chuan.liao@linux.intel.com
(snip)
@@ -1149,6 +1154,9 @@ struct snd_soc_pcm_runtime { struct snd_soc_dai **codec_dais; unsigned int num_codecs;
- struct snd_soc_dai **cpu_dais;
- unsigned int num_cpu_dai;
Codec is using "num_codecs"; So, I think "num_cpus" is better for CPU.
Agree. I will update it.
@@ -1020,14 +1036,6 @@ static int soc_dai_link_sanity_check(struct
snd_soc_card *card,
return -EPROBE_DEFER;
}
- /* FIXME */
- if (link->num_cpus > 1) {
dev_err(card->dev,
"ASoC: multi cpu is not yet supported %s\n",
link->name);
return -EINVAL;
- }
- /*
- CPU device may be specified by either name or OF node, but
- can be left unspecified, and will be matched based on DAI
I think you want to sanity check for each CPU DAI by using for_each_rtd_cpu_dai() here, not only removing FIXME ?
Sure, thanks for reminding.
In my quick check, I think your patch-set doesn't care these functions ? # Mine version is also doesn't care. Thus it indicates warning message there. # Because I'm not sure how to handle these...
- soc_init_pcm_runtime()
- snd_dmaengine_pcm_prepare_slave_config()
- dmaengine_pcm_set_runtime_hwparams()
- dmaengine_pcm_compat_request_channel()
- dpcm_state_read_file()
- dpcm_path_get()
- dpcm_runtime_merge_chan()
- soc_new_pcm()
- soc_dpcm_fe_runtime_update()
For soc_new_pcm(), I think you need to care playback/capture flag for Multi- CPU/Codec.
For soc_dpcm_fe_runtime_update(), you need to care about Multi-CPU. But, it doesn't care Multi-Codec, either...
I attached mine version.
Thank you very much for the comments and patch. I will read your version and merge our versions. Thanks again for the quick reply.
Thank you for your help !! Best regards
Kuninori Morimoto
From: Shreyas NC shreyas.nc@intel.com
Add support in PCM operations to invoke multiple cpu dais as we do for multiple codec dais. Also the symmetry calculations are updated to reflect multiple cpu dais.
Signed-off-by: Vinod Koul vkoul@kernel.org Signed-off-by: Shreyas NC shreyas.nc@intel.com Signed-off-by: Bard Liao yung-chuan.liao@linux.intel.com --- sound/soc/soc-pcm.c | 432 +++++++++++++++++++++++++++++--------------- 1 file changed, 287 insertions(+), 145 deletions(-)
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 01e7bc03d92f..f0c7f2d6df06 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -40,24 +40,28 @@ */ void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i;
lockdep_assert_held(&rtd->card->pcm_mutex);
if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - cpu_dai->playback_active++; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + cpu_dai->playback_active++; for_each_rtd_codec_dai(rtd, i, codec_dai) codec_dai->playback_active++; } else { - cpu_dai->capture_active++; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + cpu_dai->capture_active++; for_each_rtd_codec_dai(rtd, i, codec_dai) codec_dai->capture_active++; }
- cpu_dai->active++; - cpu_dai->component->active++; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + cpu_dai->active++; + cpu_dai->component->active++; + } for_each_rtd_codec_dai(rtd, i, codec_dai) { codec_dai->active++; codec_dai->component->active++; @@ -76,24 +80,28 @@ void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream) */ void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i;
lockdep_assert_held(&rtd->card->pcm_mutex);
if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - cpu_dai->playback_active--; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + cpu_dai->playback_active--; for_each_rtd_codec_dai(rtd, i, codec_dai) codec_dai->playback_active--; } else { - cpu_dai->capture_active--; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + cpu_dai->capture_active--; for_each_rtd_codec_dai(rtd, i, codec_dai) codec_dai->capture_active--; }
- cpu_dai->active--; - cpu_dai->component->active--; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + cpu_dai->active--; + cpu_dai->component->active--; + } for_each_rtd_codec_dai(rtd, i, codec_dai) { codec_dai->component->active--; codec_dai->active--; @@ -233,7 +241,7 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; unsigned int rate, channels, sample_bits, symmetry, i;
@@ -242,40 +250,54 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, sample_bits = snd_pcm_format_physical_width(params_format(params));
/* reject unmatched parameters when applying symmetry */ - symmetry = cpu_dai->driver->symmetric_rates || - rtd->dai_link->symmetric_rates; + symmetry = rtd->dai_link->symmetric_rates; + + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + symmetry |= cpu_dai->driver->symmetric_rates;
for_each_rtd_codec_dai(rtd, i, codec_dai) symmetry |= codec_dai->driver->symmetric_rates;
- if (symmetry && cpu_dai->rate && cpu_dai->rate != rate) { - dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n", + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (symmetry && cpu_dai->rate && cpu_dai->rate != rate) { + dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n", cpu_dai->rate, rate); - return -EINVAL; + return -EINVAL; + } }
- symmetry = cpu_dai->driver->symmetric_channels || - rtd->dai_link->symmetric_channels; + symmetry = rtd->dai_link->symmetric_channels; + + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + symmetry |= cpu_dai->driver->symmetric_channels;
for_each_rtd_codec_dai(rtd, i, codec_dai) symmetry |= codec_dai->driver->symmetric_channels;
- if (symmetry && cpu_dai->channels && cpu_dai->channels != channels) { - dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n", + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (symmetry && cpu_dai->channels && + cpu_dai->channels != channels) { + dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n", cpu_dai->channels, channels); - return -EINVAL; + return -EINVAL; + } }
- symmetry = cpu_dai->driver->symmetric_samplebits || - rtd->dai_link->symmetric_samplebits; + symmetry = rtd->dai_link->symmetric_samplebits; + + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + symmetry |= cpu_dai->driver->symmetric_samplebits;
for_each_rtd_codec_dai(rtd, i, codec_dai) symmetry |= codec_dai->driver->symmetric_samplebits;
- if (symmetry && cpu_dai->sample_bits && cpu_dai->sample_bits != sample_bits) { - dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n", + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (symmetry && cpu_dai->sample_bits && + cpu_dai->sample_bits != sample_bits) { + dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n", cpu_dai->sample_bits, sample_bits); - return -EINVAL; + return -EINVAL; + } }
return 0; @@ -287,11 +309,18 @@ static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream) struct snd_soc_dai_driver *cpu_driver = rtd->cpu_dai->driver; struct snd_soc_dai_link *link = rtd->dai_link; struct snd_soc_dai *codec_dai; + struct snd_soc_dai *cpu_dai; unsigned int symmetry, i;
- symmetry = cpu_driver->symmetric_rates || link->symmetric_rates || - cpu_driver->symmetric_channels || link->symmetric_channels || - cpu_driver->symmetric_samplebits || link->symmetric_samplebits; + symmetry = link->symmetric_rates || + link->symmetric_channels || + link->symmetric_samplebits; + + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + symmetry = symmetry || + cpu_driver->symmetric_rates || + cpu_driver->symmetric_channels || + cpu_driver->symmetric_samplebits;
for_each_rtd_codec_dai(rtd, i, codec_dai) symmetry = symmetry || @@ -322,7 +351,7 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; int i; - unsigned int bits = 0, cpu_bits; + unsigned int bits = 0, cpu_bits = 0;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { for_each_rtd_codec_dai(rtd, i, codec_dai) { @@ -332,7 +361,14 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream) } bits = max(codec_dai->driver->playback.sig_bits, bits); } - cpu_bits = cpu_dai->driver->playback.sig_bits; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (cpu_dai->driver->playback.sig_bits == 0) { + cpu_bits = 0; + break; + } + cpu_bits = max(cpu_dai->driver->playback.sig_bits, + bits); + } } else { for_each_rtd_codec_dai(rtd, i, codec_dai) { if (codec_dai->driver->capture.sig_bits == 0) { @@ -341,7 +377,13 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream) } bits = max(codec_dai->driver->capture.sig_bits, bits); } - cpu_bits = cpu_dai->driver->capture.sig_bits; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (cpu_dai->driver->capture.sig_bits == 0) { + cpu_bits = 0; + break; + } + cpu_bits = max(cpu_dai->driver->capture.sig_bits, bits); + } }
soc_pcm_set_msb(substream, bits); @@ -354,22 +396,40 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) struct snd_pcm_hardware *hw = &runtime->hw; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai; - struct snd_soc_dai_driver *cpu_dai_drv = rtd->cpu_dai->driver; + struct snd_soc_dai *cpu_dai; + struct snd_soc_dai_driver *cpu_dai_drv; struct snd_soc_dai_driver *codec_dai_drv; struct snd_soc_pcm_stream *codec_stream; struct snd_soc_pcm_stream *cpu_stream; unsigned int chan_min = 0, chan_max = UINT_MAX; + unsigned int cpu_chan_min = 0, cpu_chan_max = UINT_MAX; unsigned int rate_min = 0, rate_max = UINT_MAX; - unsigned int rates = UINT_MAX; + unsigned int cpu_rate_min = 0, cpu_rate_max = UINT_MAX; + unsigned int rates = UINT_MAX, cpu_rates = UINT_MAX; u64 formats = ULLONG_MAX; int i;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - cpu_stream = &cpu_dai_drv->playback; - else - cpu_stream = &cpu_dai_drv->capture; + /* first calculate min/max only for CPUs in the DAI link */ + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (!snd_soc_dai_stream_valid(cpu_dai, + substream->stream)) + continue; + + cpu_dai_drv = cpu_dai->driver; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + cpu_stream = &cpu_dai_drv->playback; + else + cpu_stream = &cpu_dai_drv->capture; + cpu_chan_min = max(cpu_chan_min, cpu_stream->channels_min); + cpu_chan_max = min(cpu_chan_max, cpu_stream->channels_max); + cpu_rate_min = max(cpu_rate_min, cpu_stream->rate_min); + cpu_rate_max = min_not_zero(cpu_rate_max, cpu_stream->rate_max); + formats &= cpu_stream->formats; + cpu_rates = snd_pcm_rate_mask_intersect(cpu_stream->rates, + rates); + }
- /* first calculate min/max only for CODECs in the DAI link */ + /* second calculate min/max only for CODECs in the DAI link */ for_each_rtd_codec_dai(rtd, i, codec_dai) {
/* @@ -400,27 +460,28 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
/* * chan min/max cannot be enforced if there are multiple CODEC DAIs - * connected to a single CPU DAI, use CPU DAI's directly and let + * connected to CPU DAI(s), use CPU DAI's directly and let * channel allocation be fixed up later */ if (rtd->num_codecs > 1) { - chan_min = cpu_stream->channels_min; - chan_max = cpu_stream->channels_max; + chan_min = cpu_chan_min; + chan_max = cpu_chan_max; }
- hw->channels_min = max(chan_min, cpu_stream->channels_min); - hw->channels_max = min(chan_max, cpu_stream->channels_max); + /* finally find a intersection between CODECs and CPUs */ + hw->channels_min = max(chan_min, cpu_chan_min); + hw->channels_max = min(chan_max, cpu_chan_max); if (hw->formats) - hw->formats &= formats & cpu_stream->formats; + hw->formats &= formats; else - hw->formats = formats & cpu_stream->formats; - hw->rates = snd_pcm_rate_mask_intersect(rates, cpu_stream->rates); + hw->formats = formats; + hw->rates = snd_pcm_rate_mask_intersect(rates, cpu_rates);
snd_pcm_limit_hw_rates(runtime);
- hw->rate_min = max(hw->rate_min, cpu_stream->rate_min); + hw->rate_min = max(hw->rate_min, cpu_rate_min); hw->rate_min = max(hw->rate_min, rate_min); - hw->rate_max = min_not_zero(hw->rate_max, cpu_stream->rate_max); + hw->rate_max = min_not_zero(hw->rate_max, cpu_rate_max); hw->rate_max = min_not_zero(hw->rate_max, rate_max); }
@@ -485,12 +546,14 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; const char *codec_dai_name = "multicodec"; + const char *cpu_dai_name = "multicpu"; int i, ret = 0;
- pinctrl_pm_select_default_state(cpu_dai->dev); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + pinctrl_pm_select_default_state(cpu_dai->dev); for_each_rtd_codec_dai(rtd, i, codec_dai) pinctrl_pm_select_default_state(codec_dai->dev);
@@ -501,11 +564,13 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
/* startup the audio subsystem */ - ret = snd_soc_dai_startup(cpu_dai, substream); - if (ret < 0) { - dev_err(cpu_dai->dev, "ASoC: can't open interface %s: %d\n", - cpu_dai->name, ret); - goto out; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + ret = snd_soc_dai_startup(cpu_dai, substream); + if (ret < 0) { + dev_err(cpu_dai->dev, "ASoC: can't open interface %s: %d\n", + cpu_dai_name, ret); + goto interface_err; + } }
ret = soc_pcm_components_open(substream, &component); @@ -546,34 +611,39 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) if (rtd->num_codecs == 1) codec_dai_name = rtd->codec_dai->name;
+ if (rtd->num_cpu_dai == 1) + cpu_dai_name = rtd->cpu_dai->name; + if (soc_pcm_has_symmetry(substream)) runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
ret = -EINVAL; if (!runtime->hw.rates) { printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n", - codec_dai_name, cpu_dai->name); + codec_dai_name, cpu_dai_name); goto config_err; } if (!runtime->hw.formats) { printk(KERN_ERR "ASoC: %s <-> %s No matching formats\n", - codec_dai_name, cpu_dai->name); + codec_dai_name, cpu_dai_name); goto config_err; } if (!runtime->hw.channels_min || !runtime->hw.channels_max || runtime->hw.channels_min > runtime->hw.channels_max) { printk(KERN_ERR "ASoC: %s <-> %s No matching channels\n", - codec_dai_name, cpu_dai->name); + codec_dai_name, cpu_dai_name); goto config_err; }
soc_pcm_apply_msb(substream);
/* Symmetry only applies if we've already got an active stream. */ - if (cpu_dai->active) { - ret = soc_pcm_apply_symmetry(substream, cpu_dai); - if (ret != 0) - goto config_err; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (cpu_dai->active) { + ret = soc_pcm_apply_symmetry(substream, cpu_dai); + if (ret != 0) + goto config_err; + } }
for_each_rtd_codec_dai(rtd, i, codec_dai) { @@ -585,7 +655,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) }
pr_debug("ASoC: %s <-> %s info:\n", - codec_dai_name, cpu_dai->name); + codec_dai_name, cpu_dai_name); pr_debug("ASoC: rate mask 0x%x\n", runtime->hw.rates); pr_debug("ASoC: min ch %d max ch %d\n", runtime->hw.channels_min, runtime->hw.channels_max); @@ -613,8 +683,12 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) component_err: soc_pcm_components_close(substream, component);
- snd_soc_dai_shutdown(cpu_dai, substream); -out: + i = rtd->num_cpu_dai; + +interface_err: + for_each_rtd_cpu_dai_rollback(rtd, i, cpu_dai) + snd_soc_dai_shutdown(cpu_dai, substream); + mutex_unlock(&rtd->card->pcm_mutex);
for_each_rtd_components(rtd, rtdcom, component) { @@ -626,8 +700,11 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) if (!codec_dai->active) pinctrl_pm_select_sleep_state(codec_dai->dev); } - if (!cpu_dai->active) - pinctrl_pm_select_sleep_state(cpu_dai->dev); + + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (!cpu_dai->active) + pinctrl_pm_select_sleep_state(cpu_dai->dev); + }
return ret; } @@ -678,7 +755,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i;
@@ -687,17 +764,21 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) snd_soc_runtime_deactivate(rtd, substream->stream);
/* clear the corresponding DAIs rate when inactive */ - if (!cpu_dai->active) - cpu_dai->rate = 0; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (!cpu_dai->active) + cpu_dai->rate = 0; + }
for_each_rtd_codec_dai(rtd, i, codec_dai) { if (!codec_dai->active) codec_dai->rate = 0; }
- snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream);
- snd_soc_dai_shutdown(cpu_dai, substream); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + snd_soc_dai_shutdown(cpu_dai, substream);
for_each_rtd_codec_dai(rtd, i, codec_dai) snd_soc_dai_shutdown(codec_dai, substream); @@ -737,8 +818,11 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) if (!codec_dai->active) pinctrl_pm_select_sleep_state(codec_dai->dev); } - if (!cpu_dai->active) - pinctrl_pm_select_sleep_state(cpu_dai->dev); + + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (!cpu_dai->active) + pinctrl_pm_select_sleep_state(cpu_dai->dev); + }
return 0; } @@ -753,7 +837,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i, ret = 0;
@@ -787,11 +871,13 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } }
- ret = snd_soc_dai_prepare(cpu_dai, substream); - if (ret < 0) { - dev_err(cpu_dai->dev, - "ASoC: cpu DAI prepare error: %d\n", ret); - goto out; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + ret = snd_soc_dai_prepare(cpu_dai, substream); + if (ret < 0) { + dev_err(cpu_dai->dev, + "ASoC: cpu DAI prepare error: %d\n", ret); + goto out; + } }
/* cancel any delayed stream shutdown that is pending */ @@ -807,7 +893,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) for_each_rtd_codec_dai(rtd, i, codec_dai) snd_soc_dai_digital_mute(codec_dai, 0, substream->stream); - snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream);
out: mutex_unlock(&rtd->card->pcm_mutex); @@ -854,7 +941,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i, ret = 0;
@@ -920,17 +1007,19 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, snd_soc_dapm_update_dai(substream, &codec_params, codec_dai); }
- ret = snd_soc_dai_hw_params(cpu_dai, substream, params); - if (ret < 0) - goto interface_err; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + ret = snd_soc_dai_hw_params(cpu_dai, substream, params); + if (ret < 0) + goto interface_err;
- /* store the parameters for each DAIs */ - cpu_dai->rate = params_rate(params); - cpu_dai->channels = params_channels(params); - cpu_dai->sample_bits = - snd_pcm_format_physical_width(params_format(params)); + /* store the parameters for each DAI */ + cpu_dai->rate = params_rate(params); + cpu_dai->channels = params_channels(params); + cpu_dai->sample_bits = + snd_pcm_format_physical_width(params_format(params));
- snd_soc_dapm_update_dai(substream, params, cpu_dai); + snd_soc_dapm_update_dai(substream, params, cpu_dai); + }
for_each_rtd_components(rtd, rtdcom, component) { ret = snd_soc_component_hw_params(component, substream, params); @@ -950,10 +1039,17 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, component_err: soc_pcm_components_hw_free(substream, component);
- snd_soc_dai_hw_free(cpu_dai, substream); - cpu_dai->rate = 0; + i = rtd->num_cpu_dai;
interface_err: + for_each_rtd_cpu_dai_rollback(rtd, i, cpu_dai) { + if (!snd_soc_dai_stream_valid(cpu_dai, substream->stream)) + continue; + + snd_soc_dai_hw_free(cpu_dai, substream); + cpu_dai->rate = 0; + } + i = rtd->num_codecs;
codec_err: @@ -978,20 +1074,23 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, static int soc_pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; int i;
mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
- /* clear the corresponding DAIs parameters when going to be inactive */ - if (cpu_dai->active == 1) { - cpu_dai->rate = 0; - cpu_dai->channels = 0; - cpu_dai->sample_bits = 0; + /* clear all CPU DAIs parameters when inactive */ + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (cpu_dai->active == 1) { + cpu_dai->rate = 0; + cpu_dai->channels = 0; + cpu_dai->sample_bits = 0; + } }
+ /* clear all CODEC DAIs parameters when inactive */ for_each_rtd_codec_dai(rtd, i, codec_dai) { if (codec_dai->active == 1) { codec_dai->rate = 0; @@ -1023,7 +1122,12 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) snd_soc_dai_hw_free(codec_dai, substream); }
- snd_soc_dai_hw_free(cpu_dai, substream); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (!snd_soc_dai_stream_valid(cpu_dai, substream->stream)) + continue; + + snd_soc_dai_hw_free(cpu_dai, substream); + }
mutex_unlock(&rtd->card->pcm_mutex); return 0; @@ -1034,7 +1138,7 @@ static int soc_pcm_trigger_start(struct snd_pcm_substream *substream, int cmd) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i, ret;
@@ -1050,9 +1154,11 @@ static int soc_pcm_trigger_start(struct snd_pcm_substream *substream, int cmd) return ret; }
- ret = snd_soc_dai_trigger(cpu_dai, substream, cmd); - if (ret < 0) - return ret; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + ret = snd_soc_dai_trigger(cpu_dai, substream, cmd); + if (ret < 0) + return ret; + }
for_each_rtd_codec_dai(rtd, i, codec_dai) { ret = snd_soc_dai_trigger(codec_dai, substream, cmd); @@ -1068,7 +1174,7 @@ static int soc_pcm_trigger_stop(struct snd_pcm_substream *substream, int cmd) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i, ret;
@@ -1078,9 +1184,11 @@ static int soc_pcm_trigger_stop(struct snd_pcm_substream *substream, int cmd) return ret; }
- ret = snd_soc_dai_trigger(cpu_dai, substream, cmd); - if (ret < 0) - return ret; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + ret = snd_soc_dai_trigger(cpu_dai, substream, cmd); + if (ret < 0) + return ret; + }
for_each_rtd_components(rtd, rtdcom, component) { ret = snd_soc_component_trigger(component, substream, cmd); @@ -1147,12 +1255,13 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream, static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t offset = 0; snd_pcm_sframes_t delay = 0; snd_pcm_sframes_t codec_delay = 0; + snd_pcm_sframes_t cpu_delay = 0; int i;
/* clearing the previous total delay */ @@ -1163,7 +1272,11 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) /* base delay if assigned in pointer callback */ delay = runtime->delay;
- delay += snd_soc_dai_delay(cpu_dai, substream); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + cpu_delay = max(cpu_delay, + snd_soc_dai_delay(cpu_dai, substream)); + } + delay += cpu_delay;
for_each_rtd_codec_dai(rtd, i, codec_dai) { codec_delay = max(codec_delay, @@ -1289,6 +1402,7 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, { struct snd_soc_pcm_runtime *be; struct snd_soc_dai *dai; + struct snd_soc_dai *cpu_dai; int i;
dev_dbg(card->dev, "ASoC: find BE for widget %s\n", widget->name); @@ -1299,12 +1413,15 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, if (!be->dai_link->no_pcm) continue;
- dev_dbg(card->dev, "ASoC: try BE : %s\n", - be->cpu_dai->playback_widget ? - be->cpu_dai->playback_widget->name : "(not set)"); + for_each_rtd_cpu_dai(be, i, cpu_dai) { + dev_dbg(card->dev, "ASoC: try BE : %s\n", + cpu_dai->playback_widget ? + cpu_dai->playback_widget->name : + "(not set)");
- if (be->cpu_dai->playback_widget == widget) - return be; + if (cpu_dai->playback_widget == widget) + return be; + }
for_each_rtd_codec_dai(be, i, dai) { if (dai->playback_widget == widget) @@ -1318,12 +1435,15 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, if (!be->dai_link->no_pcm) continue;
- dev_dbg(card->dev, "ASoC: try BE %s\n", - be->cpu_dai->capture_widget ? - be->cpu_dai->capture_widget->name : "(not set)"); + for_each_rtd_cpu_dai(be, i, cpu_dai) { + dev_dbg(card->dev, "ASoC: try BE %s\n", + cpu_dai->capture_widget ? + cpu_dai->capture_widget->name : + "(not set)");
- if (be->cpu_dai->capture_widget == widget) - return be; + if (cpu_dai->capture_widget == widget) + return be; + }
for_each_rtd_codec_dai(be, i, dai) { if (dai->capture_widget == widget) @@ -1373,8 +1493,10 @@ static bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, if (!rtd->dai_link->no_pcm) continue;
- if (rtd->cpu_dai->playback_widget == widget) - return true; + for_each_rtd_cpu_dai(rtd, i, dai) { + if (dai->playback_widget == widget) + return true; + }
for_each_rtd_codec_dai(rtd, i, dai) { if (dai->playback_widget == widget) @@ -1386,8 +1508,10 @@ static bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, if (!rtd->dai_link->no_pcm) continue;
- if (rtd->cpu_dai->capture_widget == widget) - return true; + for_each_rtd_cpu_dai(rtd, i, dai) { + if (dai->capture_widget == widget) + return true; + }
for_each_rtd_codec_dai(rtd, i, dai) { if (dai->capture_widget == widget) @@ -1430,10 +1554,18 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, unsigned int i;
/* is there a valid CPU DAI widget for this BE */ - widget = dai_get_widget(dpcm->be->cpu_dai, stream); + do_prune = 1; + for_each_rtd_cpu_dai(dpcm->be, i, dai) { + widget = dai_get_widget(dai, stream);
- /* prune the BE if it's no longer in our active list */ - if (widget && widget_in_list(list, widget)) + /* + * The BE is pruned only if none of the cpu_dai + * widgets are in the active list. + */ + if (widget && widget_in_list(list, widget)) + do_prune = 0; + } + if (!do_prune) continue;
/* is there a valid CODEC DAI widget for this BE */ @@ -1827,13 +1959,17 @@ static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; + struct snd_soc_dai *cpu_dai; + struct snd_soc_dai_driver *cpu_dai_drv; + int i;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback); - else - dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + cpu_dai_drv = cpu_dai->driver; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback); + else + dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture); + }
dpcm_runtime_merge_format(substream, &runtime->hw.formats); dpcm_runtime_merge_chan(substream, &runtime->hw.channels_min, @@ -1870,18 +2006,21 @@ static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream, { struct snd_soc_dpcm *dpcm; struct snd_soc_pcm_runtime *fe = fe_substream->private_data; - struct snd_soc_dai *fe_cpu_dai = fe->cpu_dai; + struct snd_soc_dai *fe_cpu_dai; int err; + int i;
/* apply symmetry for FE */ if (soc_pcm_has_symmetry(fe_substream)) fe_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
/* Symmetry only applies if we've got an active stream. */ - if (fe_cpu_dai->active) { - err = soc_pcm_apply_symmetry(fe_substream, fe_cpu_dai); - if (err < 0) - return err; + for_each_rtd_cpu_dai(fe, i, fe_cpu_dai) { + if (fe_cpu_dai->active) { + err = soc_pcm_apply_symmetry(fe_substream, fe_cpu_dai); + if (err < 0) + return err; + } }
/* apply symmetry for BE */ @@ -1891,6 +2030,7 @@ static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream, snd_soc_dpcm_get_substream(be, stream); struct snd_soc_pcm_runtime *rtd; struct snd_soc_dai *codec_dai; + struct snd_soc_dai *cpu_dai; int i;
/* A backend may not have the requested substream */ @@ -1905,11 +2045,13 @@ static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream, be_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
/* Symmetry only applies if we've got an active stream. */ - if (rtd->cpu_dai->active) { - err = soc_pcm_apply_symmetry(fe_substream, - rtd->cpu_dai); - if (err < 0) - return err; + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + if (cpu_dai->active) { + err = soc_pcm_apply_symmetry(fe_substream, + cpu_dai); + if (err < 0) + return err; + } }
for_each_rtd_codec_dai(rtd, i, codec_dai) { @@ -3038,7 +3180,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) out: dev_info(rtd->card->dev, "%s <-> %s mapping ok\n", (rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name, - cpu_dai->name); + (rtd->num_cpu_dai > 1) ? "multicpu" : rtd->cpu_dai->name); return ret; }
From: Shreyas NC shreyas.nc@intel.com
DAPM handles DAIs during soc_dapm_stream_event() and during addition and creation of DAI widgets i.e., dapm_add_valid_dai_widget() and dapm_connect_dai_link_widgets().
Extend these functions to handle multiple cpu dai.
Signed-off-by: Vinod Koul vkoul@kernel.org Signed-off-by: Shreyas NC shreyas.nc@intel.com Signed-off-by: Bard Liao yung-chuan.liao@linux.intel.com --- sound/soc/soc-dapm.c | 131 +++++++++++++++++++++++++------------------ 1 file changed, 76 insertions(+), 55 deletions(-)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b6378f025836..bf0789b4cdcd 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -4274,16 +4274,15 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) return 0; }
-static void dapm_connect_dai_link_widgets(struct snd_soc_card *card, - struct snd_soc_pcm_runtime *rtd) +static void dapm_add_valid_dai_widget(struct snd_soc_card *card, + struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *codec_dai, + struct snd_soc_dai *cpu_dai) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai; struct snd_soc_dapm_widget *playback = NULL, *capture = NULL; struct snd_soc_dapm_widget *codec, *playback_cpu, *capture_cpu; struct snd_pcm_substream *substream; struct snd_pcm_str *streams = rtd->pcm->streams; - int i;
if (rtd->dai_link->params) { playback_cpu = cpu_dai->capture_widget; @@ -4295,63 +4294,83 @@ static void dapm_connect_dai_link_widgets(struct snd_soc_card *card, capture_cpu = capture; }
- for_each_rtd_codec_dai(rtd, i, codec_dai) { - /* connect BE DAI playback if widgets are valid */ - codec = codec_dai->playback_widget; - - if (playback_cpu && codec) { - if (!playback) { - substream = streams[SNDRV_PCM_STREAM_PLAYBACK].substream; - playback = snd_soc_dapm_new_dai(card, substream, - "playback"); - if (IS_ERR(playback)) { - dev_err(rtd->dev, - "ASoC: Failed to create DAI %s: %ld\n", - codec_dai->name, - PTR_ERR(playback)); - continue; - } - - snd_soc_dapm_add_path(&card->dapm, playback_cpu, - playback, NULL, NULL); + /* connect BE DAI playback if widgets are valid */ + codec = codec_dai->playback_widget; + + if (playback_cpu && codec) { + if (!playback) { + substream = streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + playback = snd_soc_dapm_new_dai(card, substream, + "playback"); + if (IS_ERR(playback)) { + dev_err(rtd->dev, + "ASoC: Failed to create DAI %s: %ld\n", + codec_dai->name, + PTR_ERR(playback)); + return; }
- dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n", - cpu_dai->component->name, playback_cpu->name, - codec_dai->component->name, codec->name); - - snd_soc_dapm_add_path(&card->dapm, playback, codec, - NULL, NULL); + snd_soc_dapm_add_path(&card->dapm, playback_cpu, + playback, NULL, NULL); } + + dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n", + cpu_dai->component->name, playback_cpu->name, + codec_dai->component->name, codec->name); + + snd_soc_dapm_add_path(&card->dapm, playback, codec, + NULL, NULL); }
- for_each_rtd_codec_dai(rtd, i, codec_dai) { - /* connect BE DAI capture if widgets are valid */ - codec = codec_dai->capture_widget; - - if (codec && capture_cpu) { - if (!capture) { - substream = streams[SNDRV_PCM_STREAM_CAPTURE].substream; - capture = snd_soc_dapm_new_dai(card, substream, - "capture"); - if (IS_ERR(capture)) { - dev_err(rtd->dev, - "ASoC: Failed to create DAI %s: %ld\n", - codec_dai->name, - PTR_ERR(capture)); - continue; - } - - snd_soc_dapm_add_path(&card->dapm, capture, - capture_cpu, NULL, NULL); + /* connect BE DAI capture if widgets are valid */ + codec = codec_dai->capture_widget; + + if (codec && capture_cpu) { + if (!capture) { + substream = streams[SNDRV_PCM_STREAM_CAPTURE].substream; + capture = snd_soc_dapm_new_dai(card, substream, + "capture"); + if (IS_ERR(capture)) { + dev_err(rtd->dev, + "ASoC: Failed to create DAI %s: %ld\n", + codec_dai->name, + PTR_ERR(capture)); + return; }
- dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n", - codec_dai->component->name, codec->name, - cpu_dai->component->name, capture_cpu->name); + snd_soc_dapm_add_path(&card->dapm, capture, + capture_cpu, NULL, NULL); + } + + dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n", + codec_dai->component->name, codec->name, + cpu_dai->component->name, capture_cpu->name);
- snd_soc_dapm_add_path(&card->dapm, codec, capture, - NULL, NULL); + snd_soc_dapm_add_path(&card->dapm, codec, capture, + NULL, NULL); + } +} + +static void dapm_connect_dai_link_widgets(struct snd_soc_card *card, + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai; + int i; + + if (rtd->num_cpu_dai > 1) { + if (rtd->num_codecs == rtd->num_cpu_dai) + for_each_rtd_codec_dai(rtd, i, codec_dai) { + dapm_add_valid_dai_widget(card, rtd, + codec_dai, + rtd->cpu_dais[i]); + } + else + dev_err(card->dev, + "N cpus to M codecs link is not supported yet\n"); + } else { + for_each_rtd_codec_dai(rtd, i, codec_dai) { + dapm_add_valid_dai_widget(card, rtd, + codec_dai, rtd->cpu_dais[0]); } } } @@ -4417,9 +4436,11 @@ static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, int event) { struct snd_soc_dai *codec_dai; + struct snd_soc_dai *cpu_dai; int i;
- soc_dapm_dai_stream_event(rtd->cpu_dai, stream, event); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + soc_dapm_dai_stream_event(cpu_dai, stream, event); for_each_rtd_codec_dai(rtd, i, codec_dai) soc_dapm_dai_stream_event(codec_dai, stream, event);
On Thu, Dec 26, 2019 at 03:14:58AM +0800, Bard liao wrote:
From: Bard Liao yung-chuan.liao@linux.intel.com
As discussed in [1], ASoC core supports multi codec DAIs on a DAI link. However it does not do so for CPU DAIs.
This approach seems broadly fine - I see you'd already agreed some updates with Morimoto-san which look sensible.
-----Original Message----- From: Mark Brown [mailto:broonie@kernel.org] Sent: Tuesday, January 14, 2020 11:48 PM To: Bard liao yung-chuan.liao@linux.intel.com Cc: alsa-devel@alsa-project.org; tiwai@suse.de; liam.r.girdwood@linux.intel.com; pierre-louis.bossart@linux.intel.com; Liao, Bard bard.liao@intel.com; kuninori.morimoto.gx@renesas.com Subject: Re: [PATCH RFC 0/3] ASoC: Add Multi CPU DAI support
On Thu, Dec 26, 2019 at 03:14:58AM +0800, Bard liao wrote:
From: Bard Liao yung-chuan.liao@linux.intel.com
As discussed in [1], ASoC core supports multi codec DAIs on a DAI link. However it does not do so for CPU DAIs.
This approach seems broadly fine - I see you'd already agreed some updates with Morimoto-san which look sensible.
Thanks. I will send 2nd version soon.
participants (4)
-
Bard liao
-
Kuninori Morimoto
-
Liao, Bard
-
Mark Brown