From 244b2c5ca8b14627e4a17755e5922221e121c771 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Wed, 09 Oct 2024 06:15:07 +0000
Subject: [PATCH] change system file
---
kernel/sound/soc/soc-topology.c | 1055 ++++++++++++++++++++++++++++++++++++++--------------------
1 files changed, 687 insertions(+), 368 deletions(-)
diff --git a/kernel/sound/soc/soc-topology.c b/kernel/sound/soc/soc-topology.c
index ccf6dd9..23a5f9a 100644
--- a/kernel/sound/soc/soc-topology.c
+++ b/kernel/sound/soc/soc-topology.c
@@ -30,6 +30,8 @@
#include <sound/soc-topology.h>
#include <sound/tlv.h>
+#define SOC_TPLG_MAGIC_BIG_ENDIAN 0x436F5341 /* ASoC in reverse */
+
/*
* We make several passes over the data (since it wont necessarily be ordered)
* and process objects in the following order. This guarantees the component
@@ -78,12 +80,8 @@
static int soc_tplg_process_headers(struct soc_tplg *tplg);
static void soc_tplg_complete(struct soc_tplg *tplg);
-struct snd_soc_dapm_widget *
-snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
- const struct snd_soc_dapm_widget *widget);
-struct snd_soc_dapm_widget *
-snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
- const struct snd_soc_dapm_widget *widget);
+static void soc_tplg_denum_remove_texts(struct soc_enum *se);
+static void soc_tplg_denum_remove_values(struct soc_enum *se);
/* check we dont overflow the data for this control chunk */
static int soc_tplg_check_elem_count(struct soc_tplg *tplg, size_t elem_size,
@@ -197,8 +195,8 @@
int i;
for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++) {
- if (chan[i].id == map)
- return chan[i].reg;
+ if (le32_to_cpu(chan[i].id) == map)
+ return le32_to_cpu(chan[i].reg);
}
return -EINVAL;
@@ -210,8 +208,8 @@
int i;
for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++) {
- if (chan[i].id == map)
- return chan[i].shift;
+ if (le32_to_cpu(chan[i].id) == map)
+ return le32_to_cpu(chan[i].shift);
}
return -EINVAL;
@@ -248,12 +246,12 @@
}
/* pass vendor data to component driver for processing */
-static int soc_tplg_vendor_load_(struct soc_tplg *tplg,
- struct snd_soc_tplg_hdr *hdr)
+static int soc_tplg_vendor_load(struct soc_tplg *tplg,
+ struct snd_soc_tplg_hdr *hdr)
{
int ret = 0;
- if (tplg->comp && tplg->ops && tplg->ops->vendor_load)
+ if (tplg->ops && tplg->ops->vendor_load)
ret = tplg->ops->vendor_load(tplg->comp, tplg->index, hdr);
else {
dev_err(tplg->dev, "ASoC: no vendor load callback for ID %d\n",
@@ -270,22 +268,12 @@
return ret;
}
-/* pass vendor data to component driver for processing */
-static int soc_tplg_vendor_load(struct soc_tplg *tplg,
- struct snd_soc_tplg_hdr *hdr)
-{
- if (tplg->pass != SOC_TPLG_PASS_VENDOR)
- return 0;
-
- return soc_tplg_vendor_load_(tplg, hdr);
-}
-
/* optionally pass new dynamic widget to component driver. This is mainly for
* external widgets where we can assign private data/ops */
static int soc_tplg_widget_load(struct soc_tplg *tplg,
struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w)
{
- if (tplg->comp && tplg->ops && tplg->ops->widget_load)
+ if (tplg->ops && tplg->ops->widget_load)
return tplg->ops->widget_load(tplg->comp, tplg->index, w,
tplg_w);
@@ -297,7 +285,7 @@
static int soc_tplg_widget_ready(struct soc_tplg *tplg,
struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w)
{
- if (tplg->comp && tplg->ops && tplg->ops->widget_ready)
+ if (tplg->ops && tplg->ops->widget_ready)
return tplg->ops->widget_ready(tplg->comp, tplg->index, w,
tplg_w);
@@ -309,7 +297,7 @@
struct snd_soc_dai_driver *dai_drv,
struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai)
{
- if (tplg->comp && tplg->ops && tplg->ops->dai_load)
+ if (tplg->ops && tplg->ops->dai_load)
return tplg->ops->dai_load(tplg->comp, tplg->index, dai_drv,
pcm, dai);
@@ -320,7 +308,7 @@
static int soc_tplg_dai_link_load(struct soc_tplg *tplg,
struct snd_soc_dai_link *link, struct snd_soc_tplg_link_config *cfg)
{
- if (tplg->comp && tplg->ops && tplg->ops->link_load)
+ if (tplg->ops && tplg->ops->link_load)
return tplg->ops->link_load(tplg->comp, tplg->index, link, cfg);
return 0;
@@ -329,7 +317,7 @@
/* tell the component driver that all firmware has been loaded in this request */
static void soc_tplg_complete(struct soc_tplg *tplg)
{
- if (tplg->comp && tplg->ops && tplg->ops->complete)
+ if (tplg->ops && tplg->ops->complete)
tplg->ops->complete(tplg->comp);
}
@@ -382,10 +370,10 @@
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);
}
@@ -396,7 +384,6 @@
{
struct snd_card *card = comp->card->snd_card;
struct soc_enum *se = container_of(dobj, struct soc_enum, dobj);
- int i;
if (pass != SOC_TPLG_PASS_MIXER)
return;
@@ -404,12 +391,11 @@
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);
- for (i = 0; i < se->items; i++)
- kfree(se->dobj.control.dtexts[i]);
+ soc_tplg_denum_remove_values(se);
+ soc_tplg_denum_remove_texts(se);
kfree(se);
}
@@ -427,9 +413,26 @@
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 */
@@ -460,13 +463,12 @@
struct snd_kcontrol *kcontrol = w->kcontrols[i];
struct soc_enum *se =
(struct soc_enum *)kcontrol->private_value;
- int j;
snd_ctl_remove(card, kcontrol);
- kfree(se->dobj.control.dvalues);
- for (j = 0; j < se->items; j++)
- kfree(se->dobj.control.dtexts[j]);
+ /* free enum kcontrol's dvalues and dtexts */
+ soc_tplg_denum_remove_values(se);
+ soc_tplg_denum_remove_texts(se);
kfree(se);
kfree(w->kcontrol_news[i].name);
@@ -493,6 +495,8 @@
free_news:
kfree(w->kcontrol_news);
+ list_del(&dobj->list);
+
/* widget w is freed by soc-dapm.c */
}
@@ -502,6 +506,7 @@
{
struct snd_soc_dai_driver *dai_drv =
container_of(dobj, struct snd_soc_dai_driver, dobj);
+ struct snd_soc_dai *dai, *_dai;
if (pass != SOC_TPLG_PASS_PCM_DAI)
return;
@@ -509,6 +514,12 @@
if (dobj->ops && dobj->ops->dai_unload)
dobj->ops->dai_unload(comp, dobj);
+ for_each_component_dais_safe(comp, dai, _dai)
+ if (dai->driver == dai_drv)
+ snd_soc_unregister_dai(dai);
+
+ kfree(dai_drv->playback.stream_name);
+ kfree(dai_drv->capture.stream_name);
kfree(dai_drv->name);
list_del(&dobj->list);
kfree(dai_drv);
@@ -527,13 +538,33 @@
if (dobj->ops && dobj->ops->link_unload)
dobj->ops->link_unload(comp, dobj);
+ list_del(&dobj->list);
+ snd_soc_remove_pcm_runtime(comp->card,
+ snd_soc_get_pcm_runtime(comp->card, link));
+
kfree(link->name);
kfree(link->stream_name);
- kfree(link->cpu_dai_name);
-
- list_del(&dobj->list);
- snd_soc_remove_dai_link(comp->card, link);
+ kfree(link->cpus->dai_name);
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 */
@@ -545,9 +576,10 @@
const struct snd_soc_tplg_bytes_ext_ops *ext_ops;
int num_ops, i;
- if (hdr->ops.info == SND_SOC_TPLG_CTL_BYTES
+ if (le32_to_cpu(hdr->ops.info) == SND_SOC_TPLG_CTL_BYTES
&& k->iface & SNDRV_CTL_ELEM_IFACE_MIXER
- && k->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE
+ && (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ
+ || k->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
&& k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
struct soc_bytes_ext *sbe;
struct snd_soc_tplg_bytes_control *be;
@@ -561,19 +593,33 @@
k->info = snd_soc_bytes_info_ext;
k->tlv.c = snd_soc_bytes_tlv_callback;
+ /*
+ * When a topology-based implementation abuses the
+ * control interface and uses bytes_ext controls of
+ * more than 512 bytes, we need to disable the size
+ * checks, otherwise accesses to such controls will
+ * return an -EINVAL error and prevent the card from
+ * being configured.
+ */
+ if (IS_ENABLED(CONFIG_SND_CTL_VALIDATION) && sbe->max > 512)
+ k->access |= SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK;
+
ext_ops = tplg->bytes_ext_ops;
num_ops = tplg->bytes_ext_ops_count;
for (i = 0; i < num_ops; i++) {
- if (!sbe->put && ext_ops[i].id == be->ext_ops.put)
+ if (!sbe->put &&
+ ext_ops[i].id == le32_to_cpu(be->ext_ops.put))
sbe->put = ext_ops[i].put;
- if (!sbe->get && ext_ops[i].id == be->ext_ops.get)
+ if (!sbe->get &&
+ ext_ops[i].id == le32_to_cpu(be->ext_ops.get))
sbe->get = ext_ops[i].get;
}
- if (sbe->put && sbe->get)
- return 0;
- else
+ if ((k->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) && !sbe->get)
return -EINVAL;
+ if ((k->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) && !sbe->put)
+ return -EINVAL;
+ return 0;
}
/* try and map vendor specific kcontrol handlers first */
@@ -581,11 +627,11 @@
num_ops = tplg->io_ops_count;
for (i = 0; i < num_ops; i++) {
- if (k->put == NULL && ops[i].id == hdr->ops.put)
+ if (k->put == NULL && ops[i].id == le32_to_cpu(hdr->ops.put))
k->put = ops[i].put;
- if (k->get == NULL && ops[i].id == hdr->ops.get)
+ if (k->get == NULL && ops[i].id == le32_to_cpu(hdr->ops.get))
k->get = ops[i].get;
- if (k->info == NULL && ops[i].id == hdr->ops.info)
+ if (k->info == NULL && ops[i].id == le32_to_cpu(hdr->ops.info))
k->info = ops[i].info;
}
@@ -598,11 +644,11 @@
num_ops = ARRAY_SIZE(io_ops);
for (i = 0; i < num_ops; i++) {
- if (k->put == NULL && ops[i].id == hdr->ops.put)
+ if (k->put == NULL && ops[i].id == le32_to_cpu(hdr->ops.put))
k->put = ops[i].put;
- if (k->get == NULL && ops[i].id == hdr->ops.get)
+ if (k->get == NULL && ops[i].id == le32_to_cpu(hdr->ops.get))
k->get = ops[i].get;
- if (k->info == NULL && ops[i].id == hdr->ops.info)
+ if (k->info == NULL && ops[i].id == le32_to_cpu(hdr->ops.info))
k->info = ops[i].info;
}
@@ -641,7 +687,7 @@
static int soc_tplg_init_kcontrol(struct soc_tplg *tplg,
struct snd_kcontrol_new *k, struct snd_soc_tplg_ctl_hdr *hdr)
{
- if (tplg->comp && tplg->ops && tplg->ops->control_load)
+ if (tplg->ops && tplg->ops->control_load)
return tplg->ops->control_load(tplg->comp, tplg->index, k,
hdr);
@@ -661,9 +707,9 @@
p[0] = SNDRV_CTL_TLVT_DB_SCALE;
p[1] = item_len;
- p[2] = scale->min;
- p[3] = (scale->step & TLV_DB_SCALE_MASK)
- | (scale->mute ? TLV_DB_SCALE_MUTE : 0);
+ p[2] = le32_to_cpu(scale->min);
+ p[3] = (le32_to_cpu(scale->step) & TLV_DB_SCALE_MASK)
+ | (le32_to_cpu(scale->mute) ? TLV_DB_SCALE_MUTE : 0);
kc->tlv.p = (void *)p;
return 0;
@@ -673,13 +719,14 @@
struct snd_kcontrol_new *kc, struct snd_soc_tplg_ctl_hdr *tc)
{
struct snd_soc_tplg_ctl_tlv *tplg_tlv;
+ u32 access = le32_to_cpu(tc->access);
- if (!(tc->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE))
+ if (!(access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE))
return 0;
- if (!(tc->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)) {
+ if (!(access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)) {
tplg_tlv = &tc->tlv;
- switch (tplg_tlv->type) {
+ switch (le32_to_cpu(tplg_tlv->type)) {
case SNDRV_CTL_TLVT_DB_SCALE:
return soc_tplg_create_tlv_db_scale(tplg, kc,
&tplg_tlv->scale);
@@ -707,7 +754,8 @@
struct snd_soc_tplg_bytes_control *be;
struct soc_bytes_ext *sbe;
struct snd_kcontrol_new kc;
- int i, err;
+ int i;
+ int err = 0;
if (soc_tplg_check_elem_count(tplg,
sizeof(struct snd_soc_tplg_bytes_control), count,
@@ -730,7 +778,7 @@
return -ENOMEM;
tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) +
- be->priv.size);
+ le32_to_cpu(be->priv.size));
dev_dbg(tplg->dev,
"ASoC: adding bytes kcontrol %s with access 0x%x\n",
@@ -740,9 +788,9 @@
kc.name = be->hdr.name;
kc.private_value = (long)sbe;
kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc.access = be->hdr.access;
+ kc.access = le32_to_cpu(be->hdr.access);
- sbe->max = be->max;
+ sbe->max = le32_to_cpu(be->max);
sbe->dobj.type = SND_SOC_DOBJ_BYTES;
sbe->dobj.ops = tplg->ops;
INIT_LIST_HEAD(&sbe->dobj.list);
@@ -752,7 +800,7 @@
if (err) {
soc_control_err(tplg, &be->hdr, be->hdr.name);
kfree(sbe);
- continue;
+ break;
}
/* pass control to driver for optional further init */
@@ -762,7 +810,7 @@
dev_err(tplg->dev, "ASoC: failed to init %s\n",
be->hdr.name);
kfree(sbe);
- continue;
+ break;
}
/* register control here */
@@ -772,12 +820,12 @@
dev_err(tplg->dev, "ASoC: failed to add %s\n",
be->hdr.name);
kfree(sbe);
- continue;
+ break;
}
list_add(&sbe->dobj.list, &tplg->comp->dobj_list);
}
- return 0;
+ return err;
}
@@ -787,7 +835,8 @@
struct snd_soc_tplg_mixer_control *mc;
struct soc_mixer_control *sm;
struct snd_kcontrol_new kc;
- int i, err;
+ int i;
+ int err = 0;
if (soc_tplg_check_elem_count(tplg,
sizeof(struct snd_soc_tplg_mixer_control),
@@ -810,7 +859,7 @@
if (sm == NULL)
return -ENOMEM;
tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) +
- mc->priv.size);
+ le32_to_cpu(mc->priv.size));
dev_dbg(tplg->dev,
"ASoC: adding mixer kcontrol %s with access 0x%x\n",
@@ -820,7 +869,7 @@
kc.name = mc->hdr.name;
kc.private_value = (long)sm;
kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc.access = mc->hdr.access;
+ kc.access = le32_to_cpu(mc->hdr.access);
/* we only support FL/FR channel mapping atm */
sm->reg = tplc_chan_get_reg(tplg, mc->channel,
@@ -832,10 +881,10 @@
sm->rshift = tplc_chan_get_shift(tplg, mc->channel,
SNDRV_CHMAP_FR);
- sm->max = mc->max;
- sm->min = mc->min;
- sm->invert = mc->invert;
- sm->platform_max = mc->platform_max;
+ sm->max = le32_to_cpu(mc->max);
+ sm->min = le32_to_cpu(mc->min);
+ sm->invert = le32_to_cpu(mc->invert);
+ sm->platform_max = le32_to_cpu(mc->platform_max);
sm->dobj.index = tplg->index;
sm->dobj.ops = tplg->ops;
sm->dobj.type = SND_SOC_DOBJ_MIXER;
@@ -846,7 +895,16 @@
if (err) {
soc_control_err(tplg, &mc->hdr, mc->hdr.name);
kfree(sm);
- continue;
+ break;
+ }
+
+ /* create any TLV data */
+ err = soc_tplg_create_tlv(tplg, &kc, &mc->hdr);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: failed to create TLV %s\n",
+ mc->hdr.name);
+ kfree(sm);
+ break;
}
/* pass control to driver for optional further init */
@@ -855,12 +913,10 @@
if (err < 0) {
dev_err(tplg->dev, "ASoC: failed to init %s\n",
mc->hdr.name);
+ soc_tplg_free_tlv(tplg, &kc);
kfree(sm);
- continue;
+ break;
}
-
- /* create any TLV data */
- soc_tplg_create_tlv(tplg, &kc, &mc->hdr);
/* register control here */
err = soc_tplg_add_kcontrol(tplg, &kc,
@@ -870,13 +926,13 @@
mc->hdr.name);
soc_tplg_free_tlv(tplg, &kc);
kfree(sm);
- continue;
+ break;
}
list_add(&sm->dobj.list, &tplg->comp->dobj_list);
}
- return 0;
+ return err;
}
static int soc_tplg_denum_create_texts(struct soc_enum *se,
@@ -885,11 +941,11 @@
int i, ret;
se->dobj.control.dtexts =
- kcalloc(ec->items, sizeof(char *), GFP_KERNEL);
+ kcalloc(le32_to_cpu(ec->items), sizeof(char *), GFP_KERNEL);
if (se->dobj.control.dtexts == NULL)
return -ENOMEM;
- for (i = 0; i < ec->items; i++) {
+ for (i = 0; i < le32_to_cpu(ec->items); i++) {
if (strnlen(ec->texts[i], SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
SNDRV_CTL_ELEM_ID_NAME_MAXLEN) {
@@ -904,29 +960,50 @@
}
}
+ se->items = le32_to_cpu(ec->items);
se->texts = (const char * const *)se->dobj.control.dtexts;
return 0;
err:
+ se->items = i;
+ soc_tplg_denum_remove_texts(se);
+ return ret;
+}
+
+static inline void soc_tplg_denum_remove_texts(struct soc_enum *se)
+{
+ int i = se->items;
+
for (--i; i >= 0; i--)
kfree(se->dobj.control.dtexts[i]);
kfree(se->dobj.control.dtexts);
- return ret;
}
static int soc_tplg_denum_create_values(struct soc_enum *se,
struct snd_soc_tplg_enum_control *ec)
{
- if (ec->items > sizeof(*ec->values))
+ int i;
+
+ if (le32_to_cpu(ec->items) > sizeof(*ec->values))
return -EINVAL;
- se->dobj.control.dvalues = kmemdup(ec->values,
- ec->items * sizeof(u32),
+ se->dobj.control.dvalues = kzalloc(le32_to_cpu(ec->items) *
+ sizeof(*se->dobj.control.dvalues),
GFP_KERNEL);
if (!se->dobj.control.dvalues)
return -ENOMEM;
+ /* convert from little-endian */
+ for (i = 0; i < le32_to_cpu(ec->items); i++) {
+ se->dobj.control.dvalues[i] = le32_to_cpu(ec->values[i]);
+ }
+
return 0;
+}
+
+static inline void soc_tplg_denum_remove_values(struct soc_enum *se)
+{
+ kfree(se->dobj.control.dvalues);
}
static int soc_tplg_denum_create(struct soc_tplg *tplg, unsigned int count,
@@ -935,7 +1012,8 @@
struct snd_soc_tplg_enum_control *ec;
struct soc_enum *se;
struct snd_kcontrol_new kc;
- int i, ret, err;
+ int i;
+ int err = 0;
if (soc_tplg_check_elem_count(tplg,
sizeof(struct snd_soc_tplg_enum_control),
@@ -948,8 +1026,6 @@
for (i = 0; i < count; i++) {
ec = (struct snd_soc_tplg_enum_control *)tplg->pos;
- tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
- ec->priv.size);
/* validate kcontrol */
if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
@@ -960,6 +1036,9 @@
if (se == NULL)
return -ENOMEM;
+ tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
+ le32_to_cpu(ec->priv.size));
+
dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n",
ec->hdr.name, ec->items);
@@ -967,7 +1046,7 @@
kc.name = ec->hdr.name;
kc.private_value = (long)se;
kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc.access = ec->hdr.access;
+ kc.access = le32_to_cpu(ec->hdr.access);
se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
se->shift_l = tplc_chan_get_shift(tplg, ec->channel,
@@ -975,14 +1054,13 @@
se->shift_r = tplc_chan_get_shift(tplg, ec->channel,
SNDRV_CHMAP_FL);
- se->items = ec->items;
- se->mask = ec->mask;
+ se->mask = le32_to_cpu(ec->mask);
se->dobj.index = tplg->index;
se->dobj.type = SND_SOC_DOBJ_ENUM;
se->dobj.ops = tplg->ops;
INIT_LIST_HEAD(&se->dobj.list);
- switch (ec->hdr.ops.info) {
+ switch (le32_to_cpu(ec->hdr.ops.info)) {
case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
case SND_SOC_TPLG_CTL_ENUM_VALUE:
err = soc_tplg_denum_create_values(se, ec);
@@ -990,10 +1068,9 @@
dev_err(tplg->dev,
"ASoC: could not create values for %s\n",
ec->hdr.name);
- kfree(se);
- continue;
+ goto err_denum;
}
- /* fall through and create texts */
+ fallthrough;
case SND_SOC_TPLG_CTL_ENUM:
case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
@@ -1002,24 +1079,22 @@
dev_err(tplg->dev,
"ASoC: could not create texts for %s\n",
ec->hdr.name);
- kfree(se);
- continue;
+ goto err_denum;
}
break;
default:
+ err = -EINVAL;
dev_err(tplg->dev,
"ASoC: invalid enum control type %d for %s\n",
ec->hdr.ops.info, ec->hdr.name);
- kfree(se);
- continue;
+ goto err_denum;
}
/* map io handlers */
err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, tplg);
if (err) {
soc_control_err(tplg, &ec->hdr, ec->hdr.name);
- kfree(se);
- continue;
+ goto err_denum;
}
/* pass control to driver for optional further init */
@@ -1028,50 +1103,47 @@
if (err < 0) {
dev_err(tplg->dev, "ASoC: failed to init %s\n",
ec->hdr.name);
- kfree(se);
- continue;
+ goto err_denum;
}
/* register control here */
- ret = soc_tplg_add_kcontrol(tplg,
- &kc, &se->dobj.control.kcontrol);
- if (ret < 0) {
+ err = soc_tplg_add_kcontrol(tplg,
+ &kc, &se->dobj.control.kcontrol);
+ if (err < 0) {
dev_err(tplg->dev, "ASoC: could not add kcontrol %s\n",
ec->hdr.name);
- kfree(se);
- continue;
+ goto err_denum;
}
list_add(&se->dobj.list, &tplg->comp->dobj_list);
}
-
return 0;
+
+err_denum:
+ kfree(se);
+ return err;
}
static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg,
struct snd_soc_tplg_hdr *hdr)
{
struct snd_soc_tplg_ctl_hdr *control_hdr;
+ int ret;
int i;
-
- if (tplg->pass != SOC_TPLG_PASS_MIXER) {
- tplg->pos += hdr->size + hdr->payload_size;
- return 0;
- }
dev_dbg(tplg->dev, "ASoC: adding %d kcontrols at 0x%lx\n", hdr->count,
soc_tplg_get_offset(tplg));
- for (i = 0; i < hdr->count; i++) {
+ for (i = 0; i < le32_to_cpu(hdr->count); i++) {
control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos;
- if (control_hdr->size != sizeof(*control_hdr)) {
+ if (le32_to_cpu(control_hdr->size) != sizeof(*control_hdr)) {
dev_err(tplg->dev, "ASoC: invalid control size\n");
return -EINVAL;
}
- switch (control_hdr->ops.info) {
+ switch (le32_to_cpu(control_hdr->ops.info)) {
case SND_SOC_TPLG_CTL_VOLSW:
case SND_SOC_TPLG_CTL_STROBE:
case SND_SOC_TPLG_CTL_VOLSW_SX:
@@ -1079,22 +1151,30 @@
case SND_SOC_TPLG_CTL_RANGE:
case SND_SOC_TPLG_DAPM_CTL_VOLSW:
case SND_SOC_TPLG_DAPM_CTL_PIN:
- soc_tplg_dmixer_create(tplg, 1, hdr->payload_size);
+ ret = soc_tplg_dmixer_create(tplg, 1,
+ le32_to_cpu(hdr->payload_size));
break;
case SND_SOC_TPLG_CTL_ENUM:
case SND_SOC_TPLG_CTL_ENUM_VALUE:
case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
- soc_tplg_denum_create(tplg, 1, hdr->payload_size);
+ ret = soc_tplg_denum_create(tplg, 1,
+ le32_to_cpu(hdr->payload_size));
break;
case SND_SOC_TPLG_CTL_BYTES:
- soc_tplg_dbytes_create(tplg, 1, hdr->payload_size);
+ ret = soc_tplg_dbytes_create(tplg, 1,
+ le32_to_cpu(hdr->payload_size));
break;
default:
soc_bind_err(tplg, control_hdr, i);
return -EINVAL;
}
+ if (ret < 0) {
+ dev_err(tplg->dev, "ASoC: invalid control\n");
+ return ret;
+ }
+
}
return 0;
@@ -1104,7 +1184,7 @@
static int soc_tplg_add_route(struct soc_tplg *tplg,
struct snd_soc_dapm_route *route)
{
- if (tplg->comp && tplg->ops && tplg->ops->dapm_route_load)
+ if (tplg->ops && tplg->ops->dapm_route_load)
return tplg->ops->dapm_route_load(tplg->comp, tplg->index,
route);
@@ -1115,18 +1195,16 @@
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, i, j;
+ int ret = 0;
- if (tplg->pass != SOC_TPLG_PASS_GRAPH) {
- tplg->pos += hdr->size + hdr->payload_size;
- return 0;
- }
+ count = le32_to_cpu(hdr->count);
if (soc_tplg_check_elem_count(tplg,
sizeof(struct snd_soc_tplg_dapm_graph_elem),
- count, hdr->payload_size, "graph")) {
+ count, le32_to_cpu(hdr->payload_size), "graph")) {
dev_err(tplg->dev, "ASoC: invalid count %d for DAPM routes\n",
count);
@@ -1136,36 +1214,100 @@
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;
+ }
- route.source = elem->source;
- route.sink = elem->sink;
- route.connected = NULL; /* set to NULL atm for tplg users */
+ routes[i]->source = elem->source;
+ routes[i]->sink = elem->sink;
+
+ /* 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);
+
+ ret = soc_tplg_add_route(tplg, routes[i]);
+ if (ret < 0) {
+ dev_err(tplg->dev, "ASoC: topology: add_route failed: %d\n", ret);
+ /*
+ * this route was added to the list, it will
+ * be freed in remove_route() so increment the
+ * counter to skip it in the error handling
+ * below.
+ */
+ i++;
+ break;
+ }
/* 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 not added to the
+ * list in case of error
+ */
+ if (ret < 0) {
+ while (i < count)
+ 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(
@@ -1182,27 +1324,28 @@
for (i = 0; i < num_kcontrols; i++) {
mc = (struct snd_soc_tplg_mixer_control *)tplg->pos;
- sm = kzalloc(sizeof(*sm), GFP_KERNEL);
- if (sm == NULL)
- goto err;
-
- tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) +
- mc->priv.size);
/* validate kcontrol */
if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
- goto err_str;
+ goto err_sm;
+
+ sm = kzalloc(sizeof(*sm), GFP_KERNEL);
+ if (sm == NULL)
+ goto err_sm;
+
+ tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) +
+ le32_to_cpu(mc->priv.size));
dev_dbg(tplg->dev, " adding DAPM widget mixer control %s at %d\n",
mc->hdr.name, i);
+ kc[i].private_value = (long)sm;
kc[i].name = kstrdup(mc->hdr.name, GFP_KERNEL);
if (kc[i].name == NULL)
- goto err_str;
- kc[i].private_value = (long)sm;
+ goto err_sm;
kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc[i].access = mc->hdr.access;
+ kc[i].access = le32_to_cpu(mc->hdr.access);
/* we only support FL/FR channel mapping atm */
sm->reg = tplc_chan_get_reg(tplg, mc->channel,
@@ -1214,10 +1357,10 @@
sm->rshift = tplc_chan_get_shift(tplg, mc->channel,
SNDRV_CHMAP_FR);
- sm->max = mc->max;
- sm->min = mc->min;
- sm->invert = mc->invert;
- sm->platform_max = mc->platform_max;
+ sm->max = le32_to_cpu(mc->max);
+ sm->min = le32_to_cpu(mc->min);
+ sm->invert = le32_to_cpu(mc->invert);
+ sm->platform_max = le32_to_cpu(mc->platform_max);
sm->dobj.index = tplg->index;
INIT_LIST_HEAD(&sm->dobj.list);
@@ -1225,8 +1368,15 @@
err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc[i], tplg);
if (err) {
soc_control_err(tplg, &mc->hdr, mc->hdr.name);
- kfree(sm);
- continue;
+ goto err_sm;
+ }
+
+ /* create any TLV data */
+ err = soc_tplg_create_tlv(tplg, &kc[i], &mc->hdr);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: failed to create TLV %s\n",
+ mc->hdr.name);
+ goto err_sm;
}
/* pass control to driver for optional further init */
@@ -1235,23 +1385,20 @@
if (err < 0) {
dev_err(tplg->dev, "ASoC: failed to init %s\n",
mc->hdr.name);
- kfree(sm);
- continue;
+ goto err_sm;
}
-
- /* create any TLV data */
- soc_tplg_create_tlv(tplg, &kc[i], &mc->hdr);
}
return kc;
-err_str:
- kfree(sm);
-err:
- for (--i; i >= 0; i--) {
- kfree((void *)kc[i].private_value);
+err_sm:
+ for (; i >= 0; i--) {
+ soc_tplg_free_tlv(tplg, &kc[i]);
+ sm = (struct soc_mixer_control *)kc[i].private_value;
+ kfree(sm);
kfree(kc[i].name);
}
kfree(kc);
+
return NULL;
}
@@ -1261,7 +1408,7 @@
struct snd_kcontrol_new *kc;
struct snd_soc_tplg_enum_control *ec;
struct soc_enum *se;
- int i, j, err;
+ int i, err;
kc = kcalloc(num_kcontrols, sizeof(*kc), GFP_KERNEL);
if (kc == NULL)
@@ -1272,23 +1419,24 @@
/* validate kcontrol */
if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
- goto err;
+ goto err_se;
se = kzalloc(sizeof(*se), GFP_KERNEL);
if (se == NULL)
- goto err;
+ goto err_se;
+
+ tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
+ le32_to_cpu(ec->priv.size));
dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n",
ec->hdr.name);
- kc[i].name = kstrdup(ec->hdr.name, GFP_KERNEL);
- if (kc[i].name == NULL) {
- kfree(se);
- goto err_se;
- }
kc[i].private_value = (long)se;
+ kc[i].name = kstrdup(ec->hdr.name, GFP_KERNEL);
+ if (kc[i].name == NULL)
+ goto err_se;
kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc[i].access = ec->hdr.access;
+ kc[i].access = le32_to_cpu(ec->hdr.access);
/* we only support FL/FR channel mapping atm */
se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
@@ -1297,11 +1445,11 @@
se->shift_r = tplc_chan_get_shift(tplg, ec->channel,
SNDRV_CHMAP_FR);
- se->items = ec->items;
- se->mask = ec->mask;
+ se->items = le32_to_cpu(ec->items);
+ se->mask = le32_to_cpu(ec->mask);
se->dobj.index = tplg->index;
- switch (ec->hdr.ops.info) {
+ switch (le32_to_cpu(ec->hdr.ops.info)) {
case SND_SOC_TPLG_CTL_ENUM_VALUE:
case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
err = soc_tplg_denum_create_values(se, ec);
@@ -1310,7 +1458,7 @@
ec->hdr.name);
goto err_se;
}
- /* fall through to create texts */
+ fallthrough;
case SND_SOC_TPLG_CTL_ENUM:
case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
@@ -1342,9 +1490,6 @@
ec->hdr.name);
goto err_se;
}
-
- tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
- ec->priv.size);
}
return kc;
@@ -1353,71 +1498,66 @@
for (; i >= 0; i--) {
/* free values and texts */
se = (struct soc_enum *)kc[i].private_value;
- if (!se)
- continue;
- kfree(se->dobj.control.dvalues);
- for (j = 0; j < ec->items; j++)
- kfree(se->dobj.control.dtexts[j]);
+ if (se) {
+ soc_tplg_denum_remove_values(se);
+ soc_tplg_denum_remove_texts(se);
+ }
kfree(se);
kfree(kc[i].name);
}
-err:
kfree(kc);
return NULL;
}
static struct snd_kcontrol_new *soc_tplg_dapm_widget_dbytes_create(
- struct soc_tplg *tplg, int count)
+ struct soc_tplg *tplg, int num_kcontrols)
{
struct snd_soc_tplg_bytes_control *be;
- struct soc_bytes_ext *sbe;
+ struct soc_bytes_ext *sbe;
struct snd_kcontrol_new *kc;
int i, err;
- kc = kcalloc(count, sizeof(*kc), GFP_KERNEL);
+ kc = kcalloc(num_kcontrols, sizeof(*kc), GFP_KERNEL);
if (!kc)
return NULL;
- for (i = 0; i < count; i++) {
+ for (i = 0; i < num_kcontrols; i++) {
be = (struct snd_soc_tplg_bytes_control *)tplg->pos;
/* validate kcontrol */
if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
- goto err;
+ goto err_sbe;
sbe = kzalloc(sizeof(*sbe), GFP_KERNEL);
if (sbe == NULL)
- goto err;
+ goto err_sbe;
tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) +
- be->priv.size);
+ le32_to_cpu(be->priv.size));
dev_dbg(tplg->dev,
"ASoC: adding bytes kcontrol %s with access 0x%x\n",
be->hdr.name, be->hdr.access);
- kc[i].name = kstrdup(be->hdr.name, GFP_KERNEL);
- if (kc[i].name == NULL) {
- kfree(sbe);
- goto err;
- }
kc[i].private_value = (long)sbe;
+ kc[i].name = kstrdup(be->hdr.name, GFP_KERNEL);
+ if (kc[i].name == NULL)
+ goto err_sbe;
kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc[i].access = be->hdr.access;
+ kc[i].access = le32_to_cpu(be->hdr.access);
- sbe->max = be->max;
+ sbe->max = le32_to_cpu(be->max);
INIT_LIST_HEAD(&sbe->dobj.list);
/* map standard io handlers and check for external handlers */
err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc[i], tplg);
if (err) {
soc_control_err(tplg, &be->hdr, be->hdr.name);
- kfree(sbe);
- continue;
+ goto err_sbe;
}
/* pass control to driver for optional further init */
@@ -1426,20 +1566,20 @@
if (err < 0) {
dev_err(tplg->dev, "ASoC: failed to init %s\n",
be->hdr.name);
- kfree(sbe);
- continue;
+ goto err_sbe;
}
}
return kc;
-err:
- for (--i; i >= 0; i--) {
- kfree((void *)kc[i].private_value);
+err_sbe:
+ for (; i >= 0; i--) {
+ sbe = (struct soc_bytes_ext *)kc[i].private_value;
+ kfree(sbe);
kfree(kc[i].name);
}
-
kfree(kc);
+
return NULL;
}
@@ -1466,8 +1606,8 @@
memset(&template, 0, sizeof(template));
/* map user to kernel widget ID */
- template.id = get_widget_id(w->id);
- if (template.id < 0)
+ template.id = get_widget_id(le32_to_cpu(w->id));
+ if ((int)template.id < 0)
return template.id;
/* strings are allocated here, but used and freed by the widget */
@@ -1479,18 +1619,20 @@
ret = -ENOMEM;
goto err;
}
- template.reg = w->reg;
- template.shift = w->shift;
- template.mask = w->mask;
- template.subseq = w->subseq;
+ template.reg = le32_to_cpu(w->reg);
+ template.shift = le32_to_cpu(w->shift);
+ template.mask = le32_to_cpu(w->mask);
+ template.subseq = le32_to_cpu(w->subseq);
template.on_val = w->invert ? 0 : 1;
template.off_val = w->invert ? 1 : 0;
- template.ignore_suspend = w->ignore_suspend;
- template.event_flags = w->event_flags;
+ template.ignore_suspend = le32_to_cpu(w->ignore_suspend);
+ template.event_flags = le16_to_cpu(w->event_flags);
template.dobj.index = tplg->index;
tplg->pos +=
- (sizeof(struct snd_soc_tplg_dapm_widget) + w->priv.size);
+ (sizeof(struct snd_soc_tplg_dapm_widget) +
+ le32_to_cpu(w->priv.size));
+
if (w->num_kcontrols == 0) {
kcontrol_type = 0;
template.num_kcontrols = 0;
@@ -1501,7 +1643,7 @@
dev_dbg(tplg->dev, "ASoC: template %s has %d controls of type %x\n",
w->name, w->num_kcontrols, control_hdr->type);
- switch (control_hdr->ops.info) {
+ switch (le32_to_cpu(control_hdr->ops.info)) {
case SND_SOC_TPLG_CTL_VOLSW:
case SND_SOC_TPLG_CTL_STROBE:
case SND_SOC_TPLG_CTL_VOLSW_SX:
@@ -1509,7 +1651,7 @@
case SND_SOC_TPLG_CTL_RANGE:
case SND_SOC_TPLG_DAPM_CTL_VOLSW:
kcontrol_type = SND_SOC_TPLG_TYPE_MIXER; /* volume mixer */
- template.num_kcontrols = w->num_kcontrols;
+ template.num_kcontrols = le32_to_cpu(w->num_kcontrols);
template.kcontrol_news =
soc_tplg_dapm_widget_dmixer_create(tplg,
template.num_kcontrols);
@@ -1524,7 +1666,7 @@
case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
kcontrol_type = SND_SOC_TPLG_TYPE_ENUM; /* enumerated mixer */
- template.num_kcontrols = w->num_kcontrols;
+ template.num_kcontrols = le32_to_cpu(w->num_kcontrols);
template.kcontrol_news =
soc_tplg_dapm_widget_denum_create(tplg,
template.num_kcontrols);
@@ -1535,7 +1677,7 @@
break;
case SND_SOC_TPLG_CTL_BYTES:
kcontrol_type = SND_SOC_TPLG_TYPE_BYTES; /* bytes control */
- template.num_kcontrols = w->num_kcontrols;
+ template.num_kcontrols = le32_to_cpu(w->num_kcontrols);
template.kcontrol_news =
soc_tplg_dapm_widget_dbytes_create(tplg,
template.num_kcontrols);
@@ -1547,7 +1689,7 @@
default:
dev_err(tplg->dev, "ASoC: invalid widget control type %d:%d:%d\n",
control_hdr->ops.get, control_hdr->ops.put,
- control_hdr->ops.info);
+ le32_to_cpu(control_hdr->ops.info));
ret = -EINVAL;
goto hdr_err;
}
@@ -1565,17 +1707,6 @@
widget = snd_soc_dapm_new_control_unlocked(dapm, &template);
if (IS_ERR(widget)) {
ret = PTR_ERR(widget);
- /* Do not nag about probe deferrals */
- if (ret != -EPROBE_DEFER)
- dev_err(tplg->dev,
- "ASoC: failed to create widget %s controls (%d)\n",
- w->name, ret);
- goto hdr_err;
- }
- if (widget == NULL) {
- dev_err(tplg->dev, "ASoC: failed to create widget %s controls\n",
- w->name);
- ret = -ENOMEM;
goto hdr_err;
}
@@ -1588,6 +1719,9 @@
ret = soc_tplg_widget_ready(tplg, widget, w);
if (ret < 0)
goto ready_err;
+
+ kfree(template.sname);
+ kfree(template.name);
return 0;
@@ -1605,16 +1739,15 @@
struct snd_soc_tplg_hdr *hdr)
{
struct snd_soc_tplg_dapm_widget *widget;
- int ret, count = hdr->count, i;
+ int ret, count, i;
- if (tplg->pass != SOC_TPLG_PASS_WIDGET)
- return 0;
+ count = le32_to_cpu(hdr->count);
dev_dbg(tplg->dev, "ASoC: adding %d DAPM widgets\n", count);
for (i = 0; i < count; i++) {
widget = (struct snd_soc_tplg_dapm_widget *) tplg->pos;
- if (widget->size != sizeof(*widget)) {
+ if (le32_to_cpu(widget->size) != sizeof(*widget)) {
dev_err(tplg->dev, "ASoC: invalid widget size\n");
return -EINVAL;
}
@@ -1652,17 +1785,22 @@
return 0;
}
-static void set_stream_info(struct snd_soc_pcm_stream *stream,
+static int set_stream_info(struct snd_soc_pcm_stream *stream,
struct snd_soc_tplg_stream_caps *caps)
{
stream->stream_name = kstrdup(caps->name, GFP_KERNEL);
- stream->channels_min = caps->channels_min;
- stream->channels_max = caps->channels_max;
- stream->rates = caps->rates;
- stream->rate_min = caps->rate_min;
- stream->rate_max = caps->rate_max;
- stream->formats = caps->formats;
- stream->sig_bits = caps->sig_bits;
+ if (!stream->stream_name)
+ return -ENOMEM;
+
+ stream->channels_min = le32_to_cpu(caps->channels_min);
+ stream->channels_max = le32_to_cpu(caps->channels_max);
+ stream->rates = le32_to_cpu(caps->rates);
+ stream->rate_min = le32_to_cpu(caps->rate_min);
+ stream->rate_max = le32_to_cpu(caps->rate_max);
+ stream->formats = le64_to_cpu(caps->formats);
+ stream->sig_bits = le32_to_cpu(caps->sig_bits);
+
+ return 0;
}
static void set_dai_flags(struct snd_soc_dai_driver *dai_drv,
@@ -1689,26 +1827,38 @@
struct snd_soc_dai_driver *dai_drv;
struct snd_soc_pcm_stream *stream;
struct snd_soc_tplg_stream_caps *caps;
+ struct snd_soc_dai *dai;
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(tplg->comp);
int ret;
dai_drv = kzalloc(sizeof(struct snd_soc_dai_driver), GFP_KERNEL);
if (dai_drv == NULL)
return -ENOMEM;
- if (strlen(pcm->dai_name))
+ if (strlen(pcm->dai_name)) {
dai_drv->name = kstrdup(pcm->dai_name, GFP_KERNEL);
- dai_drv->id = pcm->dai_id;
+ if (!dai_drv->name) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ }
+ dai_drv->id = le32_to_cpu(pcm->dai_id);
if (pcm->playback) {
stream = &dai_drv->playback;
caps = &pcm->caps[SND_SOC_TPLG_STREAM_PLAYBACK];
- set_stream_info(stream, caps);
+ ret = set_stream_info(stream, caps);
+ if (ret < 0)
+ goto err;
}
if (pcm->capture) {
stream = &dai_drv->capture;
caps = &pcm->caps[SND_SOC_TPLG_STREAM_CAPTURE];
- set_stream_info(stream, caps);
+ ret = set_stream_info(stream, caps);
+ if (ret < 0)
+ goto err;
}
if (pcm->compress)
@@ -1718,8 +1868,7 @@
ret = soc_tplg_dai_load(tplg, dai_drv, pcm, NULL);
if (ret < 0) {
dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n");
- kfree(dai_drv);
- return ret;
+ goto err;
}
dai_drv->dobj.index = tplg->index;
@@ -1728,7 +1877,27 @@
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);
+ dai = snd_soc_register_dai(tplg->comp, dai_drv, false);
+ if (!dai)
+ return -ENOMEM;
+
+ /* Create the DAI widgets here */
+ ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
+ if (ret != 0) {
+ dev_err(dai->dev, "Failed to create DAI widgets %d\n", ret);
+ snd_soc_unregister_dai(dai);
+ return ret;
+ }
+
+ return 0;
+
+err:
+ kfree(dai_drv->playback.stream_name);
+ kfree(dai_drv->capture.stream_name);
+ kfree(dai_drv->name);
+ kfree(dai_drv);
+
+ return ret;
}
static void set_link_flags(struct snd_soc_dai_link *link,
@@ -1759,46 +1928,82 @@
struct snd_soc_tplg_pcm *pcm)
{
struct snd_soc_dai_link *link;
+ struct snd_soc_dai_link_component *dlc;
int ret;
- link = kzalloc(sizeof(struct snd_soc_dai_link), GFP_KERNEL);
+ /* link + cpu + codec + platform */
+ link = kzalloc(sizeof(*link) + (3 * sizeof(*dlc)), GFP_KERNEL);
if (link == NULL)
return -ENOMEM;
+
+ dlc = (struct snd_soc_dai_link_component *)(link + 1);
+
+ link->cpus = &dlc[0];
+ link->codecs = &dlc[1];
+ link->platforms = &dlc[2];
+
+ link->num_cpus = 1;
+ link->num_codecs = 1;
+ link->num_platforms = 1;
+
+ link->dobj.index = tplg->index;
+ link->dobj.ops = tplg->ops;
+ link->dobj.type = SND_SOC_DOBJ_DAI_LINK;
if (strlen(pcm->pcm_name)) {
link->name = kstrdup(pcm->pcm_name, GFP_KERNEL);
link->stream_name = kstrdup(pcm->pcm_name, GFP_KERNEL);
+ if (!link->name || !link->stream_name) {
+ ret = -ENOMEM;
+ goto err;
+ }
}
- link->id = pcm->pcm_id;
+ link->id = le32_to_cpu(pcm->pcm_id);
- if (strlen(pcm->dai_name))
- link->cpu_dai_name = kstrdup(pcm->dai_name, GFP_KERNEL);
+ if (strlen(pcm->dai_name)) {
+ link->cpus->dai_name = kstrdup(pcm->dai_name, GFP_KERNEL);
+ if (!link->cpus->dai_name) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ }
- link->codec_name = "snd-soc-dummy";
- link->codec_dai_name = "snd-soc-dummy-dai";
+ link->codecs->name = "snd-soc-dummy";
+ link->codecs->dai_name = "snd-soc-dummy-dai";
+
+ link->platforms->name = "snd-soc-dummy";
/* enable DPCM */
link->dynamic = 1;
- link->dpcm_playback = pcm->playback;
- link->dpcm_capture = pcm->capture;
+ link->dpcm_playback = le32_to_cpu(pcm->playback);
+ link->dpcm_capture = le32_to_cpu(pcm->capture);
if (pcm->flag_mask)
- set_link_flags(link, pcm->flag_mask, pcm->flags);
+ set_link_flags(link,
+ le32_to_cpu(pcm->flag_mask),
+ le32_to_cpu(pcm->flags));
/* pass control to component driver for optional further init */
ret = soc_tplg_dai_link_load(tplg, link, NULL);
if (ret < 0) {
dev_err(tplg->comp->dev, "ASoC: FE link loading failed\n");
- kfree(link);
- return ret;
+ goto err;
}
- link->dobj.index = tplg->index;
- link->dobj.ops = tplg->ops;
- link->dobj.type = SND_SOC_DOBJ_DAI_LINK;
+ ret = snd_soc_add_pcm_runtime(tplg->comp->card, link);
+ if (ret < 0) {
+ dev_err(tplg->comp->dev, "ASoC: adding FE link failed\n");
+ goto err;
+ }
+
list_add(&link->dobj.list, &tplg->comp->dobj_list);
- snd_soc_add_dai_link(tplg->comp->card, link);
return 0;
+err:
+ kfree(link->name);
+ kfree(link->stream_name);
+ kfree(link->cpus->dai_name);
+ kfree(link);
+ return ret;
}
/* create a FE DAI and DAI link from the PCM object */
@@ -1818,7 +2023,7 @@
static void stream_caps_new_ver(struct snd_soc_tplg_stream_caps *dest,
struct snd_soc_tplg_stream_caps_v4 *src)
{
- dest->size = sizeof(*dest);
+ dest->size = cpu_to_le32(sizeof(*dest));
memcpy(dest->name, src->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
dest->formats = src->formats;
dest->rates = src->rates;
@@ -1852,7 +2057,7 @@
*pcm = NULL;
- if (src->size != sizeof(*src_v4)) {
+ if (le32_to_cpu(src->size) != sizeof(*src_v4)) {
dev_err(tplg->dev, "ASoC: invalid PCM size\n");
return -EINVAL;
}
@@ -1863,7 +2068,7 @@
if (!dest)
return -ENOMEM;
- dest->size = sizeof(*dest); /* size of latest abi version */
+ dest->size = cpu_to_le32(sizeof(*dest)); /* size of latest abi version */
memcpy(dest->pcm_name, src_v4->pcm_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
memcpy(dest->dai_name, src_v4->dai_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
dest->pcm_id = src_v4->pcm_id;
@@ -1872,7 +2077,7 @@
dest->capture = src_v4->capture;
dest->compress = src_v4->compress;
dest->num_streams = src_v4->num_streams;
- for (i = 0; i < dest->num_streams; i++)
+ for (i = 0; i < le32_to_cpu(dest->num_streams); i++)
memcpy(&dest->stream[i], &src_v4->stream[i],
sizeof(struct snd_soc_tplg_stream));
@@ -1887,26 +2092,28 @@
struct snd_soc_tplg_hdr *hdr)
{
struct snd_soc_tplg_pcm *pcm, *_pcm;
- int count = hdr->count;
+ int count;
+ int size;
int i;
bool abi_match;
int ret;
- if (tplg->pass != SOC_TPLG_PASS_PCM_DAI)
- return 0;
+ count = le32_to_cpu(hdr->count);
/* check the element size and count */
pcm = (struct snd_soc_tplg_pcm *)tplg->pos;
- if (pcm->size > sizeof(struct snd_soc_tplg_pcm)
- || pcm->size < sizeof(struct snd_soc_tplg_pcm_v4)) {
+ size = le32_to_cpu(pcm->size);
+ if (size > sizeof(struct snd_soc_tplg_pcm)
+ || size < sizeof(struct snd_soc_tplg_pcm_v4)) {
dev_err(tplg->dev, "ASoC: invalid size %d for PCM elems\n",
- pcm->size);
+ size);
return -EINVAL;
}
if (soc_tplg_check_elem_count(tplg,
- pcm->size, count,
- hdr->payload_size, "PCM DAI")) {
+ size, count,
+ le32_to_cpu(hdr->payload_size),
+ "PCM DAI")) {
dev_err(tplg->dev, "ASoC: invalid count %d for PCM DAI elems\n",
count);
return -EINVAL;
@@ -1914,11 +2121,12 @@
for (i = 0; i < count; i++) {
pcm = (struct snd_soc_tplg_pcm *)tplg->pos;
+ size = le32_to_cpu(pcm->size);
/* check ABI version by size, create a new version of pcm
* if abi not match.
*/
- if (pcm->size == sizeof(*pcm)) {
+ if (size == sizeof(*pcm)) {
abi_match = true;
_pcm = pcm;
} else {
@@ -1939,7 +2147,7 @@
/* offset by version-specific struct size and
* real priv data size
*/
- tplg->pos += pcm->size + _pcm->priv.size;
+ tplg->pos += size + le32_to_cpu(_pcm->priv.size);
if (!abi_match)
kfree(_pcm); /* free the duplicated one */
@@ -1967,12 +2175,13 @@
unsigned char invert_bclk, invert_fsync;
int i;
- for (i = 0; i < cfg->num_hw_configs; i++) {
+ for (i = 0; i < le32_to_cpu(cfg->num_hw_configs); i++) {
hw_config = &cfg->hw_config[i];
if (hw_config->id != cfg->default_hw_config_id)
continue;
- link->dai_fmt = hw_config->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ link->dai_fmt = le32_to_cpu(hw_config->fmt) &
+ SND_SOC_DAIFMT_FORMAT_MASK;
/* clock gating */
switch (hw_config->clock_gated) {
@@ -2036,7 +2245,8 @@
*link = NULL;
- if (src->size != sizeof(struct snd_soc_tplg_link_config_v4)) {
+ if (le32_to_cpu(src->size) !=
+ sizeof(struct snd_soc_tplg_link_config_v4)) {
dev_err(tplg->dev, "ASoC: invalid physical link config size\n");
return -EINVAL;
}
@@ -2048,15 +2258,56 @@
if (!dest)
return -ENOMEM;
- dest->size = sizeof(*dest);
+ dest->size = cpu_to_le32(sizeof(*dest));
dest->id = src_v4->id;
dest->num_streams = src_v4->num_streams;
- for (i = 0; i < dest->num_streams; i++)
+ for (i = 0; i < le32_to_cpu(dest->num_streams); i++)
memcpy(&dest->stream[i], &src_v4->stream[i],
sizeof(struct snd_soc_tplg_stream));
*link = dest;
return 0;
+}
+
+/**
+ * snd_soc_find_dai_link - Find a DAI link
+ *
+ * @card: soc card
+ * @id: DAI link ID to match
+ * @name: DAI link name to match, optional
+ * @stream_name: DAI link stream name to match, optional
+ *
+ * This function will search all existing DAI links of the soc card to
+ * find the link of the same ID. Since DAI links may not have their
+ * unique ID, so name and stream name should also match if being
+ * specified.
+ *
+ * Return: pointer of DAI link, or NULL if not found.
+ */
+static struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card,
+ int id, const char *name,
+ const char *stream_name)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai_link *link;
+
+ for_each_card_rtds(card, rtd) {
+ link = rtd->dai_link;
+
+ if (link->id != id)
+ continue;
+
+ if (name && (!link->name || strcmp(name, link->name)))
+ continue;
+
+ if (stream_name && (!link->stream_name
+ || strcmp(stream_name, link->stream_name)))
+ continue;
+
+ return link;
+ }
+
+ return NULL;
}
/* Find and configure an existing physical DAI link */
@@ -2084,7 +2335,7 @@
else
stream_name = NULL;
- link = snd_soc_find_dai_link(tplg->comp->card, cfg->id,
+ link = snd_soc_find_dai_link(tplg->comp->card, le32_to_cpu(cfg->id),
name, stream_name);
if (!link) {
dev_err(tplg->dev, "ASoC: physical link %s (id %d) not exist\n",
@@ -2098,7 +2349,9 @@
/* flags */
if (cfg->flag_mask)
- set_link_flags(link, cfg->flag_mask, cfg->flags);
+ set_link_flags(link,
+ le32_to_cpu(cfg->flag_mask),
+ le32_to_cpu(cfg->flags));
/* pass control to component driver for optional further init */
ret = soc_tplg_dai_link_load(tplg, link, cfg);
@@ -2106,6 +2359,12 @@
dev_err(tplg->dev, "ASoC: physical link loading failed\n");
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;
}
@@ -2116,27 +2375,27 @@
struct snd_soc_tplg_hdr *hdr)
{
struct snd_soc_tplg_link_config *link, *_link;
- int count = hdr->count;
+ int count;
+ int size;
int i, ret;
bool abi_match;
- if (tplg->pass != SOC_TPLG_PASS_LINK) {
- tplg->pos += hdr->size + hdr->payload_size;
- return 0;
- };
+ count = le32_to_cpu(hdr->count);
/* check the element size and count */
link = (struct snd_soc_tplg_link_config *)tplg->pos;
- if (link->size > sizeof(struct snd_soc_tplg_link_config)
- || link->size < sizeof(struct snd_soc_tplg_link_config_v4)) {
+ size = le32_to_cpu(link->size);
+ if (size > sizeof(struct snd_soc_tplg_link_config)
+ || size < sizeof(struct snd_soc_tplg_link_config_v4)) {
dev_err(tplg->dev, "ASoC: invalid size %d for physical link elems\n",
- link->size);
+ size);
return -EINVAL;
}
if (soc_tplg_check_elem_count(tplg,
- link->size, count,
- hdr->payload_size, "physical link config")) {
+ size, count,
+ le32_to_cpu(hdr->payload_size),
+ "physical link config")) {
dev_err(tplg->dev, "ASoC: invalid count %d for physical link elems\n",
count);
return -EINVAL;
@@ -2145,7 +2404,8 @@
/* config physical DAI links */
for (i = 0; i < count; i++) {
link = (struct snd_soc_tplg_link_config *)tplg->pos;
- if (link->size == sizeof(*link)) {
+ size = le32_to_cpu(link->size);
+ if (size == sizeof(*link)) {
abi_match = true;
_link = link;
} else {
@@ -2165,7 +2425,7 @@
/* offset by version-specific struct size and
* real priv data size
*/
- tplg->pos += link->size + _link->priv.size;
+ tplg->pos += size + le32_to_cpu(_link->priv.size);
if (!abi_match)
kfree(_link); /* free the duplicated one */
@@ -2185,12 +2445,14 @@
static int soc_tplg_dai_config(struct soc_tplg *tplg,
struct snd_soc_tplg_dai *d)
{
- struct snd_soc_dai_link_component dai_component = {0};
+ struct snd_soc_dai_link_component dai_component;
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;
+
+ memset(&dai_component, 0, sizeof(dai_component));
dai_component.dai_name = d->dai_name;
dai = snd_soc_find_dai(&dai_component);
@@ -2200,7 +2462,7 @@
return -EINVAL;
}
- if (d->dai_id != dai->id) {
+ if (le32_to_cpu(d->dai_id) != dai->id) {
dev_err(tplg->dev, "ASoC: physical DAI %s id mismatch\n",
d->dai_name);
return -EINVAL;
@@ -2213,26 +2475,37 @@
if (d->playback) {
stream = &dai_drv->playback;
caps = &d->caps[SND_SOC_TPLG_STREAM_PLAYBACK];
- set_stream_info(stream, caps);
+ ret = set_stream_info(stream, caps);
+ if (ret < 0)
+ goto err;
}
if (d->capture) {
stream = &dai_drv->capture;
caps = &d->caps[SND_SOC_TPLG_STREAM_CAPTURE];
- set_stream_info(stream, caps);
+ ret = set_stream_info(stream, caps);
+ if (ret < 0)
+ goto err;
}
if (d->flag_mask)
- set_dai_flags(dai_drv, d->flag_mask, d->flags);
+ set_dai_flags(dai_drv,
+ le32_to_cpu(d->flag_mask),
+ le32_to_cpu(d->flags));
/* pass control to component driver for optional further init */
ret = soc_tplg_dai_load(tplg, dai_drv, NULL, dai);
if (ret < 0) {
dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n");
- return ret;
+ goto err;
}
return 0;
+
+err:
+ kfree(dai_drv->playback.stream_name);
+ kfree(dai_drv->capture.stream_name);
+ return ret;
}
/* load physical DAI elements */
@@ -2240,22 +2513,26 @@
struct snd_soc_tplg_hdr *hdr)
{
struct snd_soc_tplg_dai *dai;
- int count = hdr->count;
- int i;
+ int count;
+ int i, ret;
- if (tplg->pass != SOC_TPLG_PASS_BE_DAI)
- return 0;
+ count = le32_to_cpu(hdr->count);
/* config the existing BE DAIs */
for (i = 0; i < count; i++) {
dai = (struct snd_soc_tplg_dai *)tplg->pos;
- if (dai->size != sizeof(*dai)) {
+ if (le32_to_cpu(dai->size) != sizeof(*dai)) {
dev_err(tplg->dev, "ASoC: invalid physical DAI size\n");
return -EINVAL;
}
- soc_tplg_dai_config(tplg, dai);
- tplg->pos += (sizeof(*dai) + dai->priv.size);
+ ret = soc_tplg_dai_config(tplg, dai);
+ if (ret < 0) {
+ dev_err(tplg->dev, "ASoC: failed to configure DAI\n");
+ return ret;
+ }
+
+ tplg->pos += (sizeof(*dai) + le32_to_cpu(dai->priv.size));
}
dev_dbg(tplg->dev, "ASoC: Configure %d BE DAIs\n", count);
@@ -2277,25 +2554,28 @@
{
struct snd_soc_tplg_manifest *dest;
struct snd_soc_tplg_manifest_v4 *src_v4;
+ int size;
*manifest = NULL;
- if (src->size != sizeof(*src_v4)) {
+ size = le32_to_cpu(src->size);
+ if (size != sizeof(*src_v4)) {
dev_warn(tplg->dev, "ASoC: invalid manifest size %d\n",
- src->size);
- if (src->size)
+ size);
+ if (size)
return -EINVAL;
- src->size = sizeof(*src_v4);
+ src->size = cpu_to_le32(sizeof(*src_v4));
}
dev_warn(tplg->dev, "ASoC: old version of manifest\n");
src_v4 = (struct snd_soc_tplg_manifest_v4 *)src;
- dest = kzalloc(sizeof(*dest) + src_v4->priv.size, GFP_KERNEL);
+ dest = kzalloc(sizeof(*dest) + le32_to_cpu(src_v4->priv.size),
+ GFP_KERNEL);
if (!dest)
return -ENOMEM;
- dest->size = sizeof(*dest); /* size of latest abi version */
+ dest->size = cpu_to_le32(sizeof(*dest)); /* size of latest abi version */
dest->control_elems = src_v4->control_elems;
dest->widget_elems = src_v4->widget_elems;
dest->graph_elems = src_v4->graph_elems;
@@ -2304,7 +2584,7 @@
dest->priv.size = src_v4->priv.size;
if (dest->priv.size)
memcpy(dest->priv.data, src_v4->priv.data,
- src_v4->priv.size);
+ le32_to_cpu(src_v4->priv.size));
*manifest = dest;
return 0;
@@ -2317,13 +2597,10 @@
bool abi_match;
int ret = 0;
- if (tplg->pass != SOC_TPLG_PASS_MANIFEST)
- return 0;
-
manifest = (struct snd_soc_tplg_manifest *)tplg->pos;
/* check ABI version by size, create a new manifest if abi not match */
- if (manifest->size == sizeof(*manifest)) {
+ if (le32_to_cpu(manifest->size) == sizeof(*manifest)) {
abi_match = true;
_manifest = manifest;
} else {
@@ -2334,7 +2611,7 @@
}
/* pass control to component driver for optional further init */
- if (tplg->comp && tplg->ops && tplg->ops->manifest)
+ if (tplg->ops && tplg->ops->manifest)
ret = tplg->ops->manifest(tplg->comp, tplg->index, _manifest);
if (!abi_match) /* free the duplicated one */
@@ -2350,16 +2627,16 @@
if (soc_tplg_get_hdr_offset(tplg) >= tplg->fw->size)
return 0;
- if (hdr->size != sizeof(*hdr)) {
+ if (le32_to_cpu(hdr->size) != sizeof(*hdr)) {
dev_err(tplg->dev,
"ASoC: invalid header size for type %d at offset 0x%lx size 0x%zx.\n",
- hdr->type, soc_tplg_get_hdr_offset(tplg),
+ le32_to_cpu(hdr->type), soc_tplg_get_hdr_offset(tplg),
tplg->fw->size);
return -EINVAL;
}
/* big endian firmware objects not supported atm */
- if (hdr->magic == cpu_to_be32(SND_SOC_TPLG_MAGIC)) {
+ if (le32_to_cpu(hdr->magic) == SOC_TPLG_MAGIC_BIG_ENDIAN) {
dev_err(tplg->dev,
"ASoC: pass %d big endian not supported header got %x at offset 0x%lx size 0x%zx.\n",
tplg->pass, hdr->magic,
@@ -2367,7 +2644,7 @@
return -EINVAL;
}
- if (hdr->magic != SND_SOC_TPLG_MAGIC) {
+ if (le32_to_cpu(hdr->magic) != SND_SOC_TPLG_MAGIC) {
dev_err(tplg->dev,
"ASoC: pass %d does not have a valid header got %x at offset 0x%lx size 0x%zx.\n",
tplg->pass, hdr->magic,
@@ -2376,8 +2653,8 @@
}
/* Support ABI from version 4 */
- if (hdr->abi > SND_SOC_TPLG_ABI_VERSION
- || hdr->abi < SND_SOC_TPLG_ABI_VERSION_MIN) {
+ if (le32_to_cpu(hdr->abi) > SND_SOC_TPLG_ABI_VERSION ||
+ le32_to_cpu(hdr->abi) < SND_SOC_TPLG_ABI_VERSION_MIN) {
dev_err(tplg->dev,
"ASoC: pass %d invalid ABI version got 0x%x need 0x%x at offset 0x%lx size 0x%zx.\n",
tplg->pass, hdr->abi,
@@ -2392,12 +2669,6 @@
return -EINVAL;
}
- if (tplg->pass == hdr->type)
- dev_dbg(tplg->dev,
- "ASoC: Got 0x%x bytes of type %d version %d vendor %d at pass %d\n",
- hdr->payload_size, hdr->type, hdr->version,
- hdr->vendor_type, tplg->pass);
-
return 1;
}
@@ -2405,37 +2676,65 @@
static int soc_tplg_load_header(struct soc_tplg *tplg,
struct snd_soc_tplg_hdr *hdr)
{
+ int (*elem_load)(struct soc_tplg *tplg,
+ struct snd_soc_tplg_hdr *hdr);
+ unsigned int hdr_pass;
+
tplg->pos = tplg->hdr_pos + sizeof(struct snd_soc_tplg_hdr);
/* check for matching ID */
- if (hdr->index != tplg->req_index &&
+ if (le32_to_cpu(hdr->index) != tplg->req_index &&
tplg->req_index != SND_SOC_TPLG_INDEX_ALL)
return 0;
- tplg->index = hdr->index;
+ tplg->index = le32_to_cpu(hdr->index);
- switch (hdr->type) {
+ switch (le32_to_cpu(hdr->type)) {
case SND_SOC_TPLG_TYPE_MIXER:
case SND_SOC_TPLG_TYPE_ENUM:
case SND_SOC_TPLG_TYPE_BYTES:
- return soc_tplg_kcontrol_elems_load(tplg, hdr);
+ hdr_pass = SOC_TPLG_PASS_MIXER;
+ elem_load = soc_tplg_kcontrol_elems_load;
+ break;
case SND_SOC_TPLG_TYPE_DAPM_GRAPH:
- return soc_tplg_dapm_graph_elems_load(tplg, hdr);
+ hdr_pass = SOC_TPLG_PASS_GRAPH;
+ elem_load = soc_tplg_dapm_graph_elems_load;
+ break;
case SND_SOC_TPLG_TYPE_DAPM_WIDGET:
- return soc_tplg_dapm_widget_elems_load(tplg, hdr);
+ hdr_pass = SOC_TPLG_PASS_WIDGET;
+ elem_load = soc_tplg_dapm_widget_elems_load;
+ break;
case SND_SOC_TPLG_TYPE_PCM:
- return soc_tplg_pcm_elems_load(tplg, hdr);
+ hdr_pass = SOC_TPLG_PASS_PCM_DAI;
+ elem_load = soc_tplg_pcm_elems_load;
+ break;
case SND_SOC_TPLG_TYPE_DAI:
- return soc_tplg_dai_elems_load(tplg, hdr);
+ hdr_pass = SOC_TPLG_PASS_BE_DAI;
+ elem_load = soc_tplg_dai_elems_load;
+ break;
case SND_SOC_TPLG_TYPE_DAI_LINK:
case SND_SOC_TPLG_TYPE_BACKEND_LINK:
/* physical link configurations */
- return soc_tplg_link_elems_load(tplg, hdr);
+ hdr_pass = SOC_TPLG_PASS_LINK;
+ elem_load = soc_tplg_link_elems_load;
+ break;
case SND_SOC_TPLG_TYPE_MANIFEST:
- return soc_tplg_manifest_load(tplg, hdr);
+ hdr_pass = SOC_TPLG_PASS_MANIFEST;
+ elem_load = soc_tplg_manifest_load;
+ break;
default:
/* bespoke vendor data object */
- return soc_tplg_vendor_load(tplg, hdr);
+ hdr_pass = SOC_TPLG_PASS_VENDOR;
+ elem_load = soc_tplg_vendor_load;
+ break;
+ }
+
+ if (tplg->pass == hdr_pass) {
+ dev_dbg(tplg->dev,
+ "ASoC: Got 0x%x bytes of type %d version %d vendor %d at pass %d\n",
+ hdr->payload_size, hdr->type, hdr->version,
+ hdr->vendor_type, tplg->pass);
+ return elem_load(tplg, hdr);
}
return 0;
@@ -2459,18 +2758,24 @@
/* make sure header is valid before loading */
ret = soc_valid_header(tplg, hdr);
- if (ret < 0)
+ if (ret < 0) {
+ dev_err(tplg->dev,
+ "ASoC: topology: invalid header: %d\n", ret);
return ret;
- else if (ret == 0)
+ } else if (ret == 0) {
break;
+ }
/* load the header object */
ret = soc_tplg_load_header(tplg, hdr);
- if (ret < 0)
+ if (ret < 0) {
+ dev_err(tplg->dev,
+ "ASoC: topology: could not load header: %d\n", ret);
return ret;
+ }
/* goto next header */
- tplg->hdr_pos += hdr->payload_size +
+ tplg->hdr_pos += le32_to_cpu(hdr->payload_size) +
sizeof(struct snd_soc_tplg_hdr);
hdr = (struct snd_soc_tplg_hdr *)tplg->hdr_pos;
}
@@ -2505,6 +2810,10 @@
{
struct soc_tplg tplg;
int ret;
+
+ /* component needs to exist to keep and reference data while parsing */
+ if (!comp)
+ return -EINVAL;
/* setup parsing context */
memset(&tplg, 0, sizeof(tplg));
@@ -2544,7 +2853,7 @@
{
struct snd_soc_dapm_widget *w, *next_w;
- list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) {
+ for_each_card_widgets_safe(dapm->card, w, next_w) {
/* make sure we are a widget with correct context */
if (w->dobj.type != SND_SOC_DOBJ_WIDGET || w->dapm != dapm)
@@ -2592,6 +2901,9 @@
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;
@@ -2601,6 +2913,13 @@
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);
--
Gitblit v1.6.2