Add helper function to register DAI controls that need to be linked to pcm device. A list is handled in case controls are created before dai_link probe
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@st.com --- include/sound/soc-dai.h | 1 + include/sound/soc.h | 2 + sound/soc/soc-core.c | 164 +++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 146 insertions(+), 21 deletions(-)
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 964b7de..6e0fcb0 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -292,6 +292,7 @@ struct snd_soc_dai { unsigned int rx_mask;
struct list_head list; + struct list_head list_pcm_ctl; };
static inline void *snd_soc_dai_get_dma_data(const struct snd_soc_dai *dai, diff --git a/include/sound/soc.h b/include/sound/soc.h index 02b4a21..044adcf 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -598,6 +598,8 @@ int snd_soc_add_card_controls(struct snd_soc_card *soc_card, const struct snd_kcontrol_new *controls, int num_controls); int snd_soc_add_dai_controls(struct snd_soc_dai *dai, const struct snd_kcontrol_new *controls, int num_controls); +int snd_soc_add_dai_pcm_controls(struct snd_soc_dai *dai, + const struct snd_kcontrol_new *controls, int num_controls); int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 790ee2b..95aae5e 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1582,11 +1582,63 @@ static int soc_link_dai_widgets(struct snd_soc_card *card, return 0; }
+static int snd_soc_add_controls(struct snd_card *card, struct device *dev, + const struct snd_kcontrol_new *controls, int num_controls, + const char *prefix, void *data) +{ + int err, i; + + for (i = 0; i < num_controls; i++) { + const struct snd_kcontrol_new *control = &controls[i]; + + err = snd_ctl_add(card, snd_soc_cnew(control, data, + control->name, prefix)); + if (err < 0) { + dev_err(dev, "ASoC: Failed to add %s: %d\n", + control->name, err); + return err; + } + } + + return 0; +} + +struct snd_soc_dai_pcm_ctls { + struct snd_kcontrol_new *controls; + int num_controls; + struct list_head list; +}; + +static int soc_link_dai_pcm_controls(struct snd_soc_card *card, + struct snd_soc_dai *dai, + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai_pcm_ctls *ctls, *_ctls; + struct snd_kcontrol_new *kctl; + int i, ret; + + list_for_each_entry_safe(ctls, _ctls, &dai->list_pcm_ctl, list) { + kctl = ctls->controls; + for (i = 0; i < ctls->num_controls; i++) + kctl[i].device = rtd->pcm->device; + + ret = snd_soc_add_controls(card->snd_card, dai->dev, kctl, + ctls->num_controls, NULL, dai); + kfree(kctl); + list_del(&ctls->list); + kfree(ctls); + if (ret < 0) + return ret; + } + + return 0; +} + static int soc_probe_link_dais(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd, int order) { struct snd_soc_dai_link *dai_link = rtd->dai_link; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *dai, *cpu_dai = rtd->cpu_dai; int i, ret;
dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n", @@ -1651,6 +1703,35 @@ static int soc_probe_link_dais(struct snd_soc_card *card, dai_link->stream_name, ret); return ret; } + + /* link CPU DAI pcm controls to pcm device */ + if (!list_empty(&cpu_dai->list_pcm_ctl)) + ret = soc_link_dai_pcm_controls(card, cpu_dai, + rtd); + if (ret < 0) { + dev_err(card->dev, + "ASoC: Can't link %s control to %s :%d\n", + cpu_dai->name, dai_link->stream_name, + ret); + return ret; + } + + /* link CODEC DAI pcm control to pcm device */ + for (i = 0; i < rtd->num_codecs; i++) { + dai = rtd->codec_dais[i]; + if (!list_empty(&dai->list_pcm_ctl)) + ret = soc_link_dai_pcm_controls(card, + dai, rtd); + if (ret < 0) + break; + } + if (ret < 0) { + dev_err(card->dev, + "ASoC: Can't link %s control to %s :%d\n", + dai->name, dai_link->stream_name, ret); + return ret; + } + } else { INIT_DELAYED_WORK(&rtd->delayed_work, codec2codec_close_delayed_work); @@ -2187,26 +2268,6 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, } EXPORT_SYMBOL_GPL(snd_soc_cnew);
-static int snd_soc_add_controls(struct snd_card *card, struct device *dev, - const struct snd_kcontrol_new *controls, int num_controls, - const char *prefix, void *data) -{ - int err, i; - - for (i = 0; i < num_controls; i++) { - const struct snd_kcontrol_new *control = &controls[i]; - err = snd_ctl_add(card, snd_soc_cnew(control, data, - control->name, prefix)); - if (err < 0) { - dev_err(dev, "ASoC: Failed to add %s: %d\n", - control->name, err); - return err; - } - } - - return 0; -} - struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, const char *name) { @@ -2320,6 +2381,65 @@ int snd_soc_add_dai_controls(struct snd_soc_dai *dai, EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls);
/** + * snd_soc_add_dai_pcm_controls - add an array of pcm controls to a DAI. + * Convenience function to add a list of DAI controls linked to the PCM device. + * + * @dai: DAI to add controls to + * @controls: array of controls to add + * @num_controls: number of elements in the array + * + * Return 0 for success, else error. + */ +int snd_soc_add_dai_pcm_controls(struct snd_soc_dai *dai, + const struct snd_kcontrol_new *controls, + int num_controls) +{ + struct snd_soc_card *card = dai->component->card; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai_pcm_ctls *ctls_list; + struct snd_kcontrol_new *kctl; + int i, dai_found = 0; + + for (i = 0; i < num_controls; i++) { + if (controls[i].iface != SNDRV_CTL_ELEM_IFACE_PCM) { + dev_err(dai->dev, "%s: not a pcm device control !!!\n", + controls[i].name); + return -EINVAL; + } + } + + kctl = kcalloc(num_controls, sizeof(*kctl), GFP_KERNEL); + memcpy(kctl, controls, sizeof(*kctl) * num_controls); + + if (dai->probed) { + /* pcm device exists. Control can be linked to it */ + list_for_each_entry(rtd, &card->rtd_list, list) { + if (dai == rtd->cpu_dai) { + dai_found = 1; + break; + } + } + if (!dai_found) + return -EINVAL; + + for (i = 0; i < num_controls; i++) + kctl[i].device = rtd->pcm->device; + snd_soc_add_controls(card->snd_card, dai->dev, kctl, + num_controls, NULL, dai); + kfree(kctl); + } else { + /* pcm device does not exists. Postpone to dai link creation */ + ctls_list = kzalloc(sizeof(*ctls_list), GFP_KERNEL); + ctls_list->controls = kctl; + ctls_list->num_controls = num_controls; + list_add(&ctls_list->list, &dai->list_pcm_ctl); + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_add_dai_pcm_controls); + +/** * snd_soc_dai_set_sysclk - configure DAI system or master clock. * @dai: DAI * @clk_id: DAI specific clock ID @@ -2795,6 +2915,8 @@ static struct snd_soc_dai *soc_add_dai(struct snd_soc_component *component, if (!dai->driver->ops) dai->driver->ops = &null_dai_ops;
+ INIT_LIST_HEAD(&dai->list_pcm_ctl); + list_add(&dai->list, &component->dai_list); component->num_dai++;