Merge branch 'for-5.0' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie...
[sfrench/cifs-2.6.git] / sound / soc / soc-topology.c
index 731b963b6995f6022c167fc0e921f9cf1bbf841e..25fca7055464a894e5e260bf872de8c410136f56 100644 (file)
@@ -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 */
 }
 
@@ -541,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,
@@ -1120,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;
@@ -1141,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(
@@ -1364,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);
@@ -1583,6 +1674,9 @@ widget:
        if (ret < 0)
                goto ready_err;
 
+       kfree(template.sname);
+       kfree(template.name);
+
        return 0;
 
 ready_err:
@@ -2093,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;
 }
 
@@ -2573,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;
@@ -2582,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);