aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/soc-topology.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/soc-topology.c')
-rw-r--r--sound/soc/soc-topology.c179
1 files changed, 150 insertions, 29 deletions
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 045ef136903d..25fca7055464 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -382,10 +382,10 @@ static void remove_mixer(struct snd_soc_component *comp,
if (dobj->ops && dobj->ops->control_unload)
dobj->ops->control_unload(comp, dobj);
- if (sm->dobj.control.kcontrol->tlv.p)
- p = sm->dobj.control.kcontrol->tlv.p;
- snd_ctl_remove(card, sm->dobj.control.kcontrol);
- list_del(&sm->dobj.list);
+ if (dobj->control.kcontrol->tlv.p)
+ p = dobj->control.kcontrol->tlv.p;
+ snd_ctl_remove(card, dobj->control.kcontrol);
+ list_del(&dobj->list);
kfree(sm);
kfree(p);
}
@@ -404,12 +404,13 @@ static void remove_enum(struct snd_soc_component *comp,
if (dobj->ops && dobj->ops->control_unload)
dobj->ops->control_unload(comp, dobj);
- snd_ctl_remove(card, se->dobj.control.kcontrol);
- list_del(&se->dobj.list);
+ snd_ctl_remove(card, dobj->control.kcontrol);
+ list_del(&dobj->list);
- kfree(se->dobj.control.dvalues);
+ kfree(dobj->control.dvalues);
for (i = 0; i < se->items; i++)
- kfree(se->dobj.control.dtexts[i]);
+ kfree(dobj->control.dtexts[i]);
+ kfree(dobj->control.dtexts);
kfree(se);
}
@@ -427,11 +428,28 @@ static void remove_bytes(struct snd_soc_component *comp,
if (dobj->ops && dobj->ops->control_unload)
dobj->ops->control_unload(comp, dobj);
- snd_ctl_remove(card, sb->dobj.control.kcontrol);
- list_del(&sb->dobj.list);
+ snd_ctl_remove(card, dobj->control.kcontrol);
+ list_del(&dobj->list);
kfree(sb);
}
+/* remove a route */
+static void remove_route(struct snd_soc_component *comp,
+ struct snd_soc_dobj *dobj, int pass)
+{
+ struct snd_soc_dapm_route *route =
+ container_of(dobj, struct snd_soc_dapm_route, dobj);
+
+ if (pass != SOC_TPLG_PASS_GRAPH)
+ return;
+
+ if (dobj->ops && dobj->ops->dapm_route_unload)
+ dobj->ops->dapm_route_unload(comp, dobj);
+
+ list_del(&dobj->list);
+ kfree(route);
+}
+
/* remove a widget and it's kcontrols - routes must be removed first */
static void remove_widget(struct snd_soc_component *comp,
struct snd_soc_dobj *dobj, int pass)
@@ -464,9 +482,10 @@ static void remove_widget(struct snd_soc_component *comp,
snd_ctl_remove(card, kcontrol);
- kfree(se->dobj.control.dvalues);
+ kfree(dobj->control.dvalues);
for (j = 0; j < se->items; j++)
- kfree(se->dobj.control.dtexts[j]);
+ kfree(dobj->control.dtexts[j]);
+ kfree(dobj->control.dtexts);
kfree(se);
kfree(w->kcontrol_news[i].name);
@@ -493,6 +512,8 @@ static void remove_widget(struct snd_soc_component *comp,
free_news:
kfree(w->kcontrol_news);
+ list_del(&dobj->list);
+
/* widget w is freed by soc-dapm.c */
}
@@ -502,6 +523,7 @@ static void remove_dai(struct snd_soc_component *comp,
{
struct snd_soc_dai_driver *dai_drv =
container_of(dobj, struct snd_soc_dai_driver, dobj);
+ struct snd_soc_dai *dai;
if (pass != SOC_TPLG_PASS_PCM_DAI)
return;
@@ -509,6 +531,10 @@ static void remove_dai(struct snd_soc_component *comp,
if (dobj->ops && dobj->ops->dai_unload)
dobj->ops->dai_unload(comp, dobj);
+ list_for_each_entry(dai, &comp->dai_list, list)
+ if (dai->driver == dai_drv)
+ dai->driver = NULL;
+
kfree(dai_drv->name);
list_del(&dobj->list);
kfree(dai_drv);
@@ -536,6 +562,25 @@ static void remove_link(struct snd_soc_component *comp,
kfree(link);
}
+/* unload dai link */
+static void remove_backend_link(struct snd_soc_component *comp,
+ struct snd_soc_dobj *dobj, int pass)
+{
+ if (pass != SOC_TPLG_PASS_LINK)
+ return;
+
+ if (dobj->ops && dobj->ops->link_unload)
+ dobj->ops->link_unload(comp, dobj);
+
+ /*
+ * We don't free the link here as what remove_link() do since BE
+ * links are not allocated by topology.
+ * We however need to reset the dobj type to its initial values
+ */
+ dobj->type = SND_SOC_DOBJ_NONE;
+ list_del(&dobj->list);
+}
+
/* 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,
@@ -1115,9 +1160,10 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg,
struct snd_soc_tplg_hdr *hdr)
{
struct snd_soc_dapm_context *dapm = &tplg->comp->dapm;
- struct snd_soc_dapm_route route;
struct snd_soc_tplg_dapm_graph_elem *elem;
- int count = hdr->count, i;
+ struct snd_soc_dapm_route **routes;
+ int count = hdr->count, i, j;
+ int ret = 0;
if (tplg->pass != SOC_TPLG_PASS_GRAPH) {
tplg->pos += hdr->size + hdr->payload_size;
@@ -1136,36 +1182,85 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg,
dev_dbg(tplg->dev, "ASoC: adding %d DAPM routes for index %d\n", count,
hdr->index);
+ /* allocate memory for pointer to array of dapm routes */
+ routes = kcalloc(count, sizeof(struct snd_soc_dapm_route *),
+ GFP_KERNEL);
+ if (!routes)
+ return -ENOMEM;
+
+ /*
+ * allocate memory for each dapm route in the array.
+ * This needs to be done individually so that
+ * each route can be freed when it is removed in remove_route().
+ */
+ for (i = 0; i < count; i++) {
+ routes[i] = kzalloc(sizeof(*routes[i]), GFP_KERNEL);
+ if (!routes[i]) {
+ /* free previously allocated memory */
+ for (j = 0; j < i; j++)
+ kfree(routes[j]);
+
+ kfree(routes);
+ return -ENOMEM;
+ }
+ }
+
for (i = 0; i < count; i++) {
elem = (struct snd_soc_tplg_dapm_graph_elem *)tplg->pos;
tplg->pos += sizeof(struct snd_soc_tplg_dapm_graph_elem);
/* validate routes */
if (strnlen(elem->source, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
- SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
- return -EINVAL;
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN) {
+ ret = -EINVAL;
+ break;
+ }
if (strnlen(elem->sink, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
- SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
- return -EINVAL;
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN) {
+ ret = -EINVAL;
+ break;
+ }
if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
- SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
- return -EINVAL;
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN) {
+ ret = -EINVAL;
+ break;
+ }
+
+ routes[i]->source = elem->source;
+ routes[i]->sink = elem->sink;
- route.source = elem->source;
- route.sink = elem->sink;
- route.connected = NULL; /* set to NULL atm for tplg users */
+ /* set to NULL atm for tplg users */
+ routes[i]->connected = NULL;
if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 0)
- route.control = NULL;
+ routes[i]->control = NULL;
else
- route.control = elem->control;
+ routes[i]->control = elem->control;
- soc_tplg_add_route(tplg, &route);
+ /* add route dobj to dobj_list */
+ routes[i]->dobj.type = SND_SOC_DOBJ_GRAPH;
+ routes[i]->dobj.ops = tplg->ops;
+ routes[i]->dobj.index = tplg->index;
+ list_add(&routes[i]->dobj.list, &tplg->comp->dobj_list);
+
+ soc_tplg_add_route(tplg, routes[i]);
/* add route, but keep going if some fail */
- snd_soc_dapm_add_routes(dapm, &route, 1);
+ snd_soc_dapm_add_routes(dapm, routes[i], 1);
}
- return 0;
+ /* free memory allocated for all dapm routes in case of error */
+ if (ret < 0)
+ for (i = 0; i < count ; i++)
+ kfree(routes[i]);
+
+ /*
+ * free pointer to array of dapm routes as this is no longer needed.
+ * The memory allocated for each dapm route will be freed
+ * when it is removed in remove_route().
+ */
+ kfree(routes);
+
+ return ret;
}
static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create(
@@ -1359,6 +1454,7 @@ err_se:
kfree(se->dobj.control.dvalues);
for (j = 0; j < ec->items; j++)
kfree(se->dobj.control.dtexts[j]);
+ kfree(se->dobj.control.dtexts);
kfree(se);
kfree(kc[i].name);
@@ -1578,6 +1674,9 @@ widget:
if (ret < 0)
goto ready_err;
+ kfree(template.sname);
+ kfree(template.name);
+
return 0;
ready_err:
@@ -2088,6 +2187,12 @@ static int soc_tplg_link_config(struct soc_tplg *tplg,
return ret;
}
+ /* for unloading it in snd_soc_tplg_component_remove */
+ link->dobj.index = tplg->index;
+ link->dobj.ops = tplg->ops;
+ link->dobj.type = SND_SOC_DOBJ_BACKEND_LINK;
+ list_add(&link->dobj.list, &tplg->comp->dobj_list);
+
return 0;
}
@@ -2482,6 +2587,7 @@ int snd_soc_tplg_component_load(struct snd_soc_component *comp,
struct snd_soc_tplg_ops *ops, const struct firmware *fw, u32 id)
{
struct soc_tplg tplg;
+ int ret;
/* setup parsing context */
memset(&tplg, 0, sizeof(tplg));
@@ -2495,7 +2601,12 @@ int snd_soc_tplg_component_load(struct snd_soc_component *comp,
tplg.bytes_ext_ops = ops->bytes_ext_ops;
tplg.bytes_ext_ops_count = ops->bytes_ext_ops_count;
- return soc_tplg_load(&tplg);
+ ret = soc_tplg_load(&tplg);
+ /* free the created components if fail to load topology */
+ if (ret)
+ snd_soc_tplg_component_remove(comp, SND_SOC_TPLG_INDEX_ALL);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_tplg_component_load);
@@ -2562,6 +2673,9 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp, u32 index)
case SND_SOC_DOBJ_BYTES:
remove_bytes(comp, dobj, pass);
break;
+ case SND_SOC_DOBJ_GRAPH:
+ remove_route(comp, dobj, pass);
+ break;
case SND_SOC_DOBJ_WIDGET:
remove_widget(comp, dobj, pass);
break;
@@ -2571,6 +2685,13 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp, u32 index)
case SND_SOC_DOBJ_DAI_LINK:
remove_link(comp, dobj, pass);
break;
+ case SND_SOC_DOBJ_BACKEND_LINK:
+ /*
+ * call link_unload ops if extra
+ * deinitialization is needed.
+ */
+ remove_backend_link(comp, dobj, pass);
+ break;
default:
dev_err(comp->dev, "ASoC: invalid component type %d for removal\n",
dobj->type);