From: Mengdong Lin mengdong.lin@linux.intel.com
Topology will check with ASoC if a BE DAI already exists by checking its name. If the BE DAI doesn't exist, topology will create a new one and the dai_load ops will be called for device specific init.
Here is why we need topology to create BE DAIs (and BE DAI links in later patches): Intel platform supports too many different types of DAIs based on the platform variant e.g. I2S, HDA, DMIC and Soundwire in the future. Due to the SoC pin count limitation, not all the interface pins are taken out of the SoC to connect various peripherals. So even though technically hardware supports many physical interfaces but the platform may have limited what can be connected on the platform. Our attempt is to create BE DAIs based on what is actually supported on a platform, e.g. if the platform does not support HDA then we will not create the HDA DAIs. Ideally, the driver should query BIOS to get the physical pin output types and connections to peripherals for a specific platform, but unfortunately BIOS sometimes does not have accurate info, so there is still hard code of BE DAI & DAI links in driver. To share a common driver across Intel platforms and remove the hard code, we choose topology to give us such info e.g. SKL Chrome topology does not use HDA Codec and so there are only I2S and DMIC DAIs.
Signed-off-by: Guneshwor Singh guneshwor.o.singh@intel.com Signed-off-by: Mengdong Lin mengdong.lin@linux.intel.com
diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index d318fe4..9ebb9f2 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -39,6 +39,7 @@ enum snd_soc_dobj_type { SND_SOC_DOBJ_ENUM, SND_SOC_DOBJ_BYTES, SND_SOC_DOBJ_PCM, + SND_SOC_DOBJ_BE_DAI, SND_SOC_DOBJ_DAI_LINK, SND_SOC_DOBJ_CODEC_LINK, SND_SOC_DOBJ_WIDGET, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c0bbcd9..810b1d7 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2871,7 +2871,8 @@ int snd_soc_register_dai(struct snd_soc_component *component, struct snd_soc_dai *dai; int ret;
- if (dai_drv->dobj.type != SND_SOC_DOBJ_PCM) { + if (!(dai_drv->dobj.type == SND_SOC_DOBJ_PCM || + dai_drv->dobj.type == SND_SOC_DOBJ_BE_DAI)) { dev_err(component->dev, "Invalid dai type %d\n", dai_drv->dobj.type); return -EINVAL; diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 6b05047..b271248 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1711,42 +1711,14 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg, return 0; }
-/* * - * soc_tplg_be_dai_config - Find and configure an existing BE DAI. - * @tplg: topology context - * @be: topology BE DAI configs. - * - * The BE dai should already be registered by the platform driver. The - * platform driver should specify the BE DAI name and ID for matching. - */ -static int soc_tplg_be_dai_config(struct soc_tplg *tplg, +static int config_be_dai(struct soc_tplg *tplg, + struct snd_soc_dai_driver *dai_drv, struct snd_soc_tplg_be_dai *be) { - struct snd_soc_dai_link_component dai_component = {0}; - struct snd_soc_dai *dai; - struct snd_soc_dai_driver *dai_drv; struct snd_soc_pcm_stream *stream; struct snd_soc_tplg_stream_caps *caps; int ret;
- dai_component.dai_name = be->dai_name; - dai = snd_soc_find_dai(&dai_component); - if (!dai) { - dev_err(tplg->dev, "ASoC: BE DAI %s not registered\n", - be->dai_name); - return -EINVAL; - } - - if (be->dai_id != dai->id) { - dev_err(tplg->dev, "ASoC: BE DAI %s id mismatch\n", - be->dai_name); - return -EINVAL; - } - - dai_drv = dai->driver; - if (!dai_drv) - return -EINVAL; - if (be->playback) { stream = &dai_drv->playback; caps = &be->caps[SND_SOC_TPLG_STREAM_PLAYBACK]; @@ -1765,13 +1737,80 @@ static int soc_tplg_be_dai_config(struct soc_tplg *tplg, /* pass control to component driver for optional further init */ ret = soc_tplg_dai_load(tplg, dai_drv); if (ret < 0) { - dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); + dev_err(tplg->comp->dev, "ASoC: BE DAI loading failed\n"); return ret; }
return 0; }
+static int soc_tplg_be_dai_create(struct soc_tplg *tplg, + struct snd_soc_tplg_be_dai *be) +{ + struct snd_soc_dai_driver *dai_drv; + int ret; + + dai_drv = kzalloc(sizeof(struct snd_soc_dai_driver), GFP_KERNEL); + if (dai_drv == NULL) + return -ENOMEM; + + dai_drv->name = be->dai_name; + dai_drv->id = be->dai_id; + + ret = config_be_dai(tplg, dai_drv, be); + if (ret < 0) { + kfree(dai_drv); + return ret; + } + + dai_drv->dobj.index = tplg->index; + dai_drv->dobj.ops = tplg->ops; + dai_drv->dobj.type = SND_SOC_DOBJ_BE_DAI; + list_add(&dai_drv->dobj.list, &tplg->comp->dobj_list); + + /* register the DAI to the component */ + return snd_soc_register_dai(tplg->comp, dai_drv); +} + +/* * + * soc_tplg_be_dai_config - Create a new BE DAI or configure an existing one. + * @tplg: topology context + * @be: topology BE DAI configs. + * + * The BE dai should already be registered by the platform driver. The + * platform driver should specify the BE DAI name and ID for matching. + */ +static int soc_tplg_be_dai_config(struct soc_tplg *tplg, + struct snd_soc_tplg_be_dai *be) +{ + struct snd_soc_dai_link_component dai_component = {0}; + struct snd_soc_dai *dai; + struct snd_soc_dai_driver *dai_drv; + + if (!strlen(be->dai_name)) { + dev_err(tplg->dev, "ASoC: Invalid BE DAI name\n"); + return -EINVAL; + } + + dai_component.dai_name = be->dai_name; + dai = snd_soc_find_dai(&dai_component); + if (!dai) /* BE DAI doesn't exist, create it */ + return soc_tplg_be_dai_create(tplg, be); + + /* configure an existing BE DAI */ + if (be->dai_id != dai->id) { + dev_err(tplg->dev, "ASoC: BE DAI %s id mismatch\n", + be->dai_name); + return -EINVAL; + } + + dai_drv = dai->driver; + if (!dai_drv) + return -EINVAL; + + return config_be_dai(tplg, dai_drv, be); +} + static int soc_tplg_be_dai_elems_load(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) { @@ -2063,6 +2102,9 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp, u32 index) case SND_SOC_DOBJ_PCM: remove_dai(comp, dobj, pass); break; + case SND_SOC_DOBJ_BE_DAI: + remove_dai(comp, dobj, pass); + break; case SND_SOC_DOBJ_DAI_LINK: remove_link(comp, dobj, pass); break;