summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorMengdong Lin <mengdong.lin@linux.intel.com>2016-01-15 16:13:37 +0800
committerMark Brown <broonie@kernel.org>2016-02-15 19:38:19 +0000
commitacfc7d46cddcf71cf18772bbe8717b84eac5f672 (patch)
tree7ed01825b54581695daa640c56a156b32fc054e5 /sound
parent64527e8a352968bda529f01df1c9dd5fe581ff04 (diff)
downloadlinux-acfc7d46cddcf71cf18772bbe8717b84eac5f672.tar.gz
linux-acfc7d46cddcf71cf18772bbe8717b84eac5f672.tar.xz
ASoC: topology: Add FE DAI links dynamically
Topology will also create FE DAI links dynamically from the PCM objects. These links will be removed when the component is removed and its topology info is unloaded. The component driver can implement link_load/unload ops for extra intialization (e.g. error check) and destruction. Signed-off-by: Mengdong Lin <mengdong.lin@linux.intel.com> Acked-by: Liam Girdwood <liam.r.girdwood@linux.intel.com> Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/soc-topology.c70
1 files changed, 69 insertions, 1 deletions
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 446ac9a93aef..0eb01e8155f1 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -340,6 +340,16 @@ static int soc_tplg_dai_load(struct soc_tplg *tplg,
return 0;
}
+/* pass link configurations to component driver for extra intialization */
+static int soc_tplg_dai_link_load(struct soc_tplg *tplg,
+ struct snd_soc_dai_link *link)
+{
+ if (tplg->comp && tplg->ops && tplg->ops->link_load)
+ return tplg->ops->link_load(tplg->comp, link);
+
+ return 0;
+}
+
/* tell the component driver that all firmware has been loaded in this request */
static void soc_tplg_complete(struct soc_tplg *tplg)
{
@@ -512,6 +522,24 @@ static void remove_dai(struct snd_soc_component *comp,
kfree(dai_drv);
}
+/* remove link configurations */
+static void remove_link(struct snd_soc_component *comp,
+ struct snd_soc_dobj *dobj, int pass)
+{
+ struct snd_soc_dai_link *link =
+ container_of(dobj, struct snd_soc_dai_link, dobj);
+
+ if (pass != SOC_TPLG_PASS_PCM_DAI)
+ return;
+
+ if (dobj->ops && dobj->ops->link_unload)
+ dobj->ops->link_unload(comp, dobj);
+
+ list_del(&dobj->list);
+ snd_soc_remove_dai_link(comp->card, link);
+ kfree(link);
+}
+
/* bind a kcontrol to it's IO handlers */
static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr,
struct snd_kcontrol_new *k,
@@ -1603,10 +1631,47 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
return snd_soc_register_dai(tplg->comp, dai_drv);
}
+static int soc_tplg_link_create(struct soc_tplg *tplg,
+ struct snd_soc_tplg_pcm *pcm)
+{
+ struct snd_soc_dai_link *link;
+ int ret;
+
+ link = kzalloc(sizeof(struct snd_soc_dai_link), GFP_KERNEL);
+ if (link == NULL)
+ return -ENOMEM;
+
+ link->name = pcm->pcm_name;
+ link->stream_name = pcm->pcm_name;
+
+ /* pass control to component driver for optional further init */
+ ret = soc_tplg_dai_link_load(tplg, link);
+ if (ret < 0) {
+ dev_err(tplg->comp->dev, "ASoC: FE link loading failed\n");
+ kfree(link);
+ return ret;
+ }
+
+ link->dobj.index = tplg->index;
+ link->dobj.ops = tplg->ops;
+ link->dobj.type = SND_SOC_DOBJ_DAI_LINK;
+ list_add(&link->dobj.list, &tplg->comp->dobj_list);
+
+ snd_soc_add_dai_link(tplg->comp->card, link);
+ return 0;
+}
+
+/* create a FE DAI and DAI link from the PCM object */
static int soc_tplg_pcm_create(struct soc_tplg *tplg,
struct snd_soc_tplg_pcm *pcm)
{
- return soc_tplg_dai_create(tplg, pcm);
+ int ret;
+
+ ret = soc_tplg_dai_create(tplg, pcm);
+ if (ret < 0)
+ return ret;
+
+ return soc_tplg_link_create(tplg, pcm);
}
static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
@@ -1890,6 +1955,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_DAI_LINK:
+ remove_link(comp, dobj, pass);
+ break;
default:
dev_err(comp->dev, "ASoC: invalid component type %d for removal\n",
dobj->type);