summaryrefslogtreecommitdiff
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.c1187
1 files changed, 363 insertions, 824 deletions
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index c3be24b2fac5..064b8d76b955 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -44,9 +44,8 @@
#define SOC_TPLG_PASS_WIDGET 3
#define SOC_TPLG_PASS_PCM_DAI 4
#define SOC_TPLG_PASS_GRAPH 5
-#define SOC_TPLG_PASS_PINS 6
-#define SOC_TPLG_PASS_BE_DAI 7
-#define SOC_TPLG_PASS_LINK 8
+#define SOC_TPLG_PASS_BE_DAI 6
+#define SOC_TPLG_PASS_LINK 7
#define SOC_TPLG_PASS_START SOC_TPLG_PASS_MANIFEST
#define SOC_TPLG_PASS_END SOC_TPLG_PASS_LINK
@@ -74,12 +73,9 @@ struct soc_tplg {
int bytes_ext_ops_count;
/* optional fw loading callbacks to component drivers */
- struct snd_soc_tplg_ops *ops;
+ const struct snd_soc_tplg_ops *ops;
};
-static int soc_tplg_process_headers(struct soc_tplg *tplg);
-static int soc_tplg_complete(struct soc_tplg *tplg);
-
/* 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,
unsigned int count, size_t bytes, const char *elem_type)
@@ -135,8 +131,8 @@ static const struct snd_soc_tplg_kcontrol_ops io_ops[] = {
snd_soc_put_enum_double, NULL},
{SND_SOC_TPLG_CTL_BYTES, snd_soc_bytes_get,
snd_soc_bytes_put, snd_soc_bytes_info},
- {SND_SOC_TPLG_CTL_RANGE, snd_soc_get_volsw_range,
- snd_soc_put_volsw_range, snd_soc_info_volsw_range},
+ {SND_SOC_TPLG_CTL_RANGE, snd_soc_get_volsw,
+ snd_soc_put_volsw, snd_soc_info_volsw},
{SND_SOC_TPLG_CTL_VOLSW_XR_SX, snd_soc_get_xr_sx,
snd_soc_put_xr_sx, snd_soc_info_xr_sx},
{SND_SOC_TPLG_CTL_STROBE, snd_soc_get_strobe,
@@ -186,7 +182,7 @@ static const struct soc_tplg_map dapm_map[] = {
{SND_SOC_TPLG_DAPM_DECODER, snd_soc_dapm_decoder},
};
-static int tplc_chan_get_reg(struct soc_tplg *tplg,
+static int tplg_chan_get_reg(struct soc_tplg *tplg,
struct snd_soc_tplg_channel *chan, int map)
{
int i;
@@ -199,7 +195,7 @@ static int tplc_chan_get_reg(struct soc_tplg *tplg,
return -EINVAL;
}
-static int tplc_chan_get_shift(struct soc_tplg *tplg,
+static int tplg_chan_get_shift(struct soc_tplg *tplg,
struct snd_soc_tplg_channel *chan, int map)
{
int i;
@@ -224,15 +220,6 @@ static int get_widget_id(int tplg_type)
return -EINVAL;
}
-static inline void soc_bind_err(struct soc_tplg *tplg,
- struct snd_soc_tplg_ctl_hdr *hdr, int index)
-{
- dev_err(tplg->dev,
- "ASoC: invalid control type (g,p,i) %d:%d:%d index %d at 0x%lx\n",
- hdr->ops.get, hdr->ops.put, hdr->ops.info, index,
- soc_tplg_get_offset(tplg));
-}
-
static inline void soc_control_err(struct soc_tplg *tplg,
struct snd_soc_tplg_ctl_hdr *hdr, const char *name)
{
@@ -354,69 +341,37 @@ static int soc_tplg_add_kcontrol(struct soc_tplg *tplg,
tplg->dev, k, comp->name_prefix, comp, kcontrol);
}
-/* remove a mixer kcontrol */
-static void remove_mixer(struct snd_soc_component *comp,
- struct snd_soc_dobj *dobj, int pass)
+/* remove kcontrol */
+static void soc_tplg_remove_kcontrol(struct snd_soc_component *comp, struct snd_soc_dobj *dobj,
+ int pass)
{
struct snd_card *card = comp->card->snd_card;
if (pass != SOC_TPLG_PASS_CONTROL)
return;
- if (dobj->ops && dobj->ops->control_unload)
- dobj->ops->control_unload(comp, dobj);
-
- snd_ctl_remove(card, dobj->control.kcontrol);
- list_del(&dobj->list);
-}
-
-/* remove an enum kcontrol */
-static void remove_enum(struct snd_soc_component *comp,
- struct snd_soc_dobj *dobj, int pass)
-{
- struct snd_card *card = comp->card->snd_card;
-
- if (pass != SOC_TPLG_PASS_CONTROL)
- return;
-
- if (dobj->ops && dobj->ops->control_unload)
- dobj->ops->control_unload(comp, dobj);
-
- snd_ctl_remove(card, dobj->control.kcontrol);
- list_del(&dobj->list);
-}
-
-/* remove a byte kcontrol */
-static void remove_bytes(struct snd_soc_component *comp,
- struct snd_soc_dobj *dobj, int pass)
-{
- struct snd_card *card = comp->card->snd_card;
-
- if (pass != SOC_TPLG_PASS_CONTROL)
- return;
-
- if (dobj->ops && dobj->ops->control_unload)
- dobj->ops->control_unload(comp, dobj);
+ if (dobj->unload)
+ dobj->unload(comp, dobj);
snd_ctl_remove(card, dobj->control.kcontrol);
list_del(&dobj->list);
}
/* remove a route */
-static void remove_route(struct snd_soc_component *comp,
+static void soc_tplg_remove_route(struct snd_soc_component *comp,
struct snd_soc_dobj *dobj, int pass)
{
if (pass != SOC_TPLG_PASS_GRAPH)
return;
- if (dobj->ops && dobj->ops->dapm_route_unload)
- dobj->ops->dapm_route_unload(comp, dobj);
+ if (dobj->unload)
+ dobj->unload(comp, dobj);
list_del(&dobj->list);
}
/* remove a widget and it's kcontrols - routes must be removed first */
-static void remove_widget(struct snd_soc_component *comp,
+static void soc_tplg_remove_widget(struct snd_soc_component *comp,
struct snd_soc_dobj *dobj, int pass)
{
struct snd_card *card = comp->card->snd_card;
@@ -427,16 +382,12 @@ static void remove_widget(struct snd_soc_component *comp,
if (pass != SOC_TPLG_PASS_WIDGET)
return;
- if (dobj->ops && dobj->ops->widget_unload)
- dobj->ops->widget_unload(comp, dobj);
-
- if (!w->kcontrols)
- goto free_news;
+ if (dobj->unload)
+ dobj->unload(comp, dobj);
- for (i = 0; w->kcontrols && i < w->num_kcontrols; i++)
- snd_ctl_remove(card, w->kcontrols[i]);
-
-free_news:
+ if (w->kcontrols)
+ for (i = 0; i < w->num_kcontrols; i++)
+ snd_ctl_remove(card, w->kcontrols[i]);
list_del(&dobj->list);
@@ -444,7 +395,7 @@ free_news:
}
/* remove DAI configurations */
-static void remove_dai(struct snd_soc_component *comp,
+static void soc_tplg_remove_dai(struct snd_soc_component *comp,
struct snd_soc_dobj *dobj, int pass)
{
struct snd_soc_dai_driver *dai_drv =
@@ -454,8 +405,8 @@ static void remove_dai(struct snd_soc_component *comp,
if (pass != SOC_TPLG_PASS_PCM_DAI)
return;
- if (dobj->ops && dobj->ops->dai_unload)
- dobj->ops->dai_unload(comp, dobj);
+ if (dobj->unload)
+ dobj->unload(comp, dobj);
for_each_component_dais_safe(comp, dai, _dai)
if (dai->driver == dai_drv)
@@ -465,7 +416,7 @@ static void remove_dai(struct snd_soc_component *comp,
}
/* remove link configurations */
-static void remove_link(struct snd_soc_component *comp,
+static void soc_tplg_remove_link(struct snd_soc_component *comp,
struct snd_soc_dobj *dobj, int pass)
{
struct snd_soc_dai_link *link =
@@ -474,12 +425,15 @@ static void remove_link(struct snd_soc_component *comp,
if (pass != SOC_TPLG_PASS_PCM_DAI)
return;
- if (dobj->ops && dobj->ops->link_unload)
- dobj->ops->link_unload(comp, dobj);
+ if (dobj->unload)
+ dobj->unload(comp, dobj);
list_del(&dobj->list);
- snd_soc_remove_pcm_runtime(comp->card,
- snd_soc_get_pcm_runtime(comp->card, link));
+
+ /* Ignored links do not need to be removed, they are not added */
+ if (!link->ignore)
+ snd_soc_remove_pcm_runtime(comp->card,
+ snd_soc_get_pcm_runtime(comp->card, link));
}
/* unload dai link */
@@ -489,11 +443,11 @@ static void remove_backend_link(struct snd_soc_component *comp,
if (pass != SOC_TPLG_PASS_LINK)
return;
- if (dobj->ops && dobj->ops->link_unload)
- dobj->ops->link_unload(comp, dobj);
+ if (dobj->unload)
+ dobj->unload(comp, dobj);
/*
- * We don't free the link here as what remove_link() do since BE
+ * We don't free the link here as what soc_tplg_remove_link() do since BE
* links are not allocated by topology.
* We however need to reset the dobj type to its initial values
*/
@@ -621,11 +575,15 @@ EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_bind_event);
static int soc_tplg_control_load(struct soc_tplg *tplg,
struct snd_kcontrol_new *k, struct snd_soc_tplg_ctl_hdr *hdr)
{
+ int ret = 0;
+
if (tplg->ops && tplg->ops->control_load)
- return tplg->ops->control_load(tplg->comp, tplg->index, k,
- hdr);
+ ret = tplg->ops->control_load(tplg->comp, tplg->index, k, hdr);
- return 0;
+ if (ret)
+ dev_err(tplg->dev, "ASoC: failed to init %s\n", hdr->name);
+
+ return ret;
}
@@ -676,156 +634,62 @@ static int soc_tplg_create_tlv(struct soc_tplg *tplg,
return 0;
}
-static int soc_tplg_dbytes_create(struct soc_tplg *tplg, size_t size)
-{
- struct snd_soc_tplg_bytes_control *be;
- struct soc_bytes_ext *sbe;
- struct snd_kcontrol_new kc;
- int ret = 0;
-
- if (soc_tplg_check_elem_count(tplg,
- sizeof(struct snd_soc_tplg_bytes_control),
- 1, size, "mixer bytes"))
- return -EINVAL;
-
- 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)
- return -EINVAL;
-
- sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL);
- if (sbe == NULL)
- return -ENOMEM;
-
- tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) +
- 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);
-
- memset(&kc, 0, sizeof(kc));
- kc.name = be->hdr.name;
- kc.private_value = (long)sbe;
- kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc.access = le32_to_cpu(be->hdr.access);
-
- 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);
-
- /* map io handlers */
- ret = soc_tplg_kcontrol_bind_io(&be->hdr, &kc, tplg);
- if (ret) {
- soc_control_err(tplg, &be->hdr, be->hdr.name);
- goto err;
- }
-
- /* pass control to driver for optional further init */
- ret = soc_tplg_control_load(tplg, &kc, (struct snd_soc_tplg_ctl_hdr *)be);
- if (ret < 0) {
- dev_err(tplg->dev, "ASoC: failed to init %s\n", be->hdr.name);
- goto err;
- }
-
- /* register control here */
- ret = soc_tplg_add_kcontrol(tplg, &kc, &sbe->dobj.control.kcontrol);
- if (ret < 0) {
- dev_err(tplg->dev, "ASoC: failed to add %s\n", be->hdr.name);
- goto err;
- }
-
- list_add(&sbe->dobj.list, &tplg->comp->dobj_list);
-
-err:
- return ret;
-}
-
-static int soc_tplg_dmixer_create(struct soc_tplg *tplg, size_t size)
+static int soc_tplg_control_dmixer_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc)
{
struct snd_soc_tplg_mixer_control *mc;
struct soc_mixer_control *sm;
- struct snd_kcontrol_new kc;
- int ret = 0;
-
- if (soc_tplg_check_elem_count(tplg,
- sizeof(struct snd_soc_tplg_mixer_control),
- 1, size, "mixers"))
- return -EINVAL;
+ int err;
mc = (struct snd_soc_tplg_mixer_control *)tplg->pos;
/* validate kcontrol */
- if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
- SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
return -EINVAL;
sm = devm_kzalloc(tplg->dev, sizeof(*sm), GFP_KERNEL);
- if (sm == NULL)
+ if (!sm)
return -ENOMEM;
- tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) +
- le32_to_cpu(mc->priv.size));
- dev_dbg(tplg->dev,
- "ASoC: adding mixer kcontrol %s with access 0x%x\n",
+ tplg->pos += sizeof(struct snd_soc_tplg_mixer_control) + le32_to_cpu(mc->priv.size);
+
+ dev_dbg(tplg->dev, "ASoC: adding mixer kcontrol %s with access 0x%x\n",
mc->hdr.name, mc->hdr.access);
- memset(&kc, 0, sizeof(kc));
- kc.name = mc->hdr.name;
- kc.private_value = (long)sm;
- kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc.access = le32_to_cpu(mc->hdr.access);
+ kc->name = devm_kstrdup(tplg->dev, mc->hdr.name, GFP_KERNEL);
+ if (!kc->name)
+ return -ENOMEM;
+ kc->private_value = (long)sm;
+ kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ 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, SNDRV_CHMAP_FL);
- sm->rreg = tplc_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FR);
- sm->shift = tplc_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FL);
- sm->rshift = tplc_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FR);
+ sm->reg = tplg_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FL);
+ sm->rreg = tplg_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FR);
+ sm->shift = tplg_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FL);
+ sm->rshift = tplg_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FR);
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;
- INIT_LIST_HEAD(&sm->dobj.list);
+ sm->num_channels = le32_to_cpu(mc->num_channels);
/* map io handlers */
- ret = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc, tplg);
- if (ret) {
+ err = soc_tplg_kcontrol_bind_io(&mc->hdr, kc, tplg);
+ if (err) {
soc_control_err(tplg, &mc->hdr, mc->hdr.name);
- goto err;
+ return err;
}
/* create any TLV data */
- ret = soc_tplg_create_tlv(tplg, &kc, &mc->hdr);
- if (ret < 0) {
+ 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);
- goto err;
+ return err;
}
/* pass control to driver for optional further init */
- ret = soc_tplg_control_load(tplg, &kc, (struct snd_soc_tplg_ctl_hdr *)mc);
- if (ret < 0) {
- dev_err(tplg->dev, "ASoC: failed to init %s\n", mc->hdr.name);
- goto err;
- }
-
- /* register control here */
- ret = soc_tplg_add_kcontrol(tplg, &kc, &sm->dobj.control.kcontrol);
- if (ret < 0) {
- dev_err(tplg->dev, "ASoC: failed to add %s\n", mc->hdr.name);
- goto err;
- }
-
- list_add(&sm->dobj.list, &tplg->comp->dobj_list);
-
-err:
- return ret;
+ return soc_tplg_control_load(tplg, kc, &mc->hdr);
}
static int soc_tplg_denum_create_texts(struct soc_tplg *tplg, struct soc_enum *se,
@@ -889,110 +753,220 @@ static int soc_tplg_denum_create_values(struct soc_tplg *tplg, struct soc_enum *
se->dobj.control.dvalues[i] = le32_to_cpu(ec->values[i]);
}
+ se->items = le32_to_cpu(ec->items);
+ se->values = (const unsigned int *)se->dobj.control.dvalues;
return 0;
}
-static int soc_tplg_denum_create(struct soc_tplg *tplg, size_t size)
+static int soc_tplg_control_denum_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc)
{
struct snd_soc_tplg_enum_control *ec;
struct soc_enum *se;
- struct snd_kcontrol_new kc;
- int ret = 0;
-
- if (soc_tplg_check_elem_count(tplg,
- sizeof(struct snd_soc_tplg_enum_control),
- 1, size, "enums"))
- return -EINVAL;
+ int err;
ec = (struct snd_soc_tplg_enum_control *)tplg->pos;
/* validate kcontrol */
- if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
- SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
return -EINVAL;
- se = devm_kzalloc(tplg->dev, (sizeof(*se)), GFP_KERNEL);
- if (se == NULL)
+ se = devm_kzalloc(tplg->dev, sizeof(*se), GFP_KERNEL);
+ if (!se)
return -ENOMEM;
- tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
- le32_to_cpu(ec->priv.size));
+ 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);
+ dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n", ec->hdr.name, ec->items);
- memset(&kc, 0, sizeof(kc));
- kc.name = ec->hdr.name;
- kc.private_value = (long)se;
- kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc.access = le32_to_cpu(ec->hdr.access);
+ kc->name = devm_kstrdup(tplg->dev, ec->hdr.name, GFP_KERNEL);
+ if (!kc->name)
+ return -ENOMEM;
+ kc->private_value = (long)se;
+ kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ 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,
- SNDRV_CHMAP_FL);
- se->shift_r = tplc_chan_get_shift(tplg, ec->channel,
- SNDRV_CHMAP_FL);
+ /* we only support FL/FR channel mapping atm */
+ se->reg = tplg_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
+ se->shift_l = tplg_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FL);
+ se->shift_r = tplg_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FR);
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 (le32_to_cpu(ec->hdr.ops.info)) {
- case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
case SND_SOC_TPLG_CTL_ENUM_VALUE:
- ret = soc_tplg_denum_create_values(tplg, se, ec);
- if (ret < 0) {
- dev_err(tplg->dev,
- "ASoC: could not create values for %s\n",
- ec->hdr.name);
- goto err;
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
+ err = soc_tplg_denum_create_values(tplg, se, ec);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: could not create values for %s\n", ec->hdr.name);
+ return err;
}
fallthrough;
case SND_SOC_TPLG_CTL_ENUM:
case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
- ret = soc_tplg_denum_create_texts(tplg, se, ec);
- if (ret < 0) {
- dev_err(tplg->dev,
- "ASoC: could not create texts for %s\n",
- ec->hdr.name);
- goto err;
+ err = soc_tplg_denum_create_texts(tplg, se, ec);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: could not create texts for %s\n", ec->hdr.name);
+ return err;
}
break;
default:
- ret = -EINVAL;
- dev_err(tplg->dev,
- "ASoC: invalid enum control type %d for %s\n",
+ dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n",
ec->hdr.ops.info, ec->hdr.name);
- goto err;
+ return -EINVAL;
}
/* map io handlers */
- ret = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, tplg);
- if (ret) {
+ err = soc_tplg_kcontrol_bind_io(&ec->hdr, kc, tplg);
+ if (err) {
soc_control_err(tplg, &ec->hdr, ec->hdr.name);
- goto err;
+ return err;
}
/* pass control to driver for optional further init */
- ret = soc_tplg_control_load(tplg, &kc, (struct snd_soc_tplg_ctl_hdr *)ec);
- if (ret < 0) {
- dev_err(tplg->dev, "ASoC: failed to init %s\n", ec->hdr.name);
- goto err;
+ return soc_tplg_control_load(tplg, kc, &ec->hdr);
+}
+
+static int soc_tplg_control_dbytes_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc)
+{
+ struct snd_soc_tplg_bytes_control *be;
+ struct soc_bytes_ext *sbe;
+ int err;
+
+ 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)
+ return -EINVAL;
+
+ sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL);
+ if (!sbe)
+ return -ENOMEM;
+
+ tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + 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->name = devm_kstrdup(tplg->dev, be->hdr.name, GFP_KERNEL);
+ if (!kc->name)
+ return -ENOMEM;
+ kc->private_value = (long)sbe;
+ kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kc->access = le32_to_cpu(be->hdr.access);
+
+ sbe->max = le32_to_cpu(be->max);
+
+ /* map standard io handlers and check for external handlers */
+ err = soc_tplg_kcontrol_bind_io(&be->hdr, kc, tplg);
+ if (err) {
+ soc_control_err(tplg, &be->hdr, be->hdr.name);
+ return err;
}
- /* register control here */
+ /* pass control to driver for optional further init */
+ return soc_tplg_control_load(tplg, kc, &be->hdr);
+}
+
+static int soc_tplg_dbytes_create(struct soc_tplg *tplg, size_t size)
+{
+ struct snd_kcontrol_new kc = {0};
+ struct soc_bytes_ext *sbe;
+ int ret;
+
+ if (soc_tplg_check_elem_count(tplg,
+ sizeof(struct snd_soc_tplg_bytes_control),
+ 1, size, "mixer bytes"))
+ return -EINVAL;
+
+ ret = soc_tplg_control_dbytes_create(tplg, &kc);
+ if (ret)
+ return ret;
+
+ /* register dynamic object */
+ sbe = (struct soc_bytes_ext *)kc.private_value;
+
+ INIT_LIST_HEAD(&sbe->dobj.list);
+ sbe->dobj.type = SND_SOC_DOBJ_BYTES;
+ sbe->dobj.index = tplg->index;
+ if (tplg->ops)
+ sbe->dobj.unload = tplg->ops->control_unload;
+
+ /* create control directly */
+ ret = soc_tplg_add_kcontrol(tplg, &kc, &sbe->dobj.control.kcontrol);
+ if (ret < 0)
+ return ret;
+
+ list_add(&sbe->dobj.list, &tplg->comp->dobj_list);
+
+ return ret;
+}
+
+static int soc_tplg_dmixer_create(struct soc_tplg *tplg, size_t size)
+{
+ struct snd_kcontrol_new kc = {0};
+ struct soc_mixer_control *sm;
+ int ret;
+
+ if (soc_tplg_check_elem_count(tplg,
+ sizeof(struct snd_soc_tplg_mixer_control),
+ 1, size, "mixers"))
+ return -EINVAL;
+
+ ret = soc_tplg_control_dmixer_create(tplg, &kc);
+ if (ret)
+ return ret;
+
+ /* register dynamic object */
+ sm = (struct soc_mixer_control *)kc.private_value;
+
+ INIT_LIST_HEAD(&sm->dobj.list);
+ sm->dobj.type = SND_SOC_DOBJ_MIXER;
+ sm->dobj.index = tplg->index;
+ if (tplg->ops)
+ sm->dobj.unload = tplg->ops->control_unload;
+
+ /* create control directly */
+ ret = soc_tplg_add_kcontrol(tplg, &kc, &sm->dobj.control.kcontrol);
+ if (ret < 0)
+ return ret;
+
+ list_add(&sm->dobj.list, &tplg->comp->dobj_list);
+
+ return ret;
+}
+
+static int soc_tplg_denum_create(struct soc_tplg *tplg, size_t size)
+{
+ struct snd_kcontrol_new kc = {0};
+ struct soc_enum *se;
+ int ret;
+
+ if (soc_tplg_check_elem_count(tplg,
+ sizeof(struct snd_soc_tplg_enum_control),
+ 1, size, "enums"))
+ return -EINVAL;
+
+ ret = soc_tplg_control_denum_create(tplg, &kc);
+ if (ret)
+ return ret;
+
+ /* register dynamic object */
+ se = (struct soc_enum *)kc.private_value;
+
+ INIT_LIST_HEAD(&se->dobj.list);
+ se->dobj.type = SND_SOC_DOBJ_ENUM;
+ se->dobj.index = tplg->index;
+ if (tplg->ops)
+ se->dobj.unload = tplg->ops->control_unload;
+
+ /* create control directly */
ret = soc_tplg_add_kcontrol(tplg, &kc, &se->dobj.control.kcontrol);
- if (ret < 0) {
- dev_err(tplg->dev, "ASoC: could not add kcontrol %s\n", ec->hdr.name);
- goto err;
- }
+ if (ret < 0)
+ return ret;
list_add(&se->dobj.list, &tplg->comp->dobj_list);
-err:
return ret;
}
@@ -1013,35 +987,26 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg,
return -EINVAL;
}
- 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:
- case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
- case SND_SOC_TPLG_CTL_RANGE:
- case SND_SOC_TPLG_DAPM_CTL_VOLSW:
- case SND_SOC_TPLG_DAPM_CTL_PIN:
+ switch (le32_to_cpu(control_hdr->type)) {
+ case SND_SOC_TPLG_TYPE_MIXER:
ret = soc_tplg_dmixer_create(tplg, 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:
+ case SND_SOC_TPLG_TYPE_ENUM:
ret = soc_tplg_denum_create(tplg, le32_to_cpu(hdr->payload_size));
break;
- case SND_SOC_TPLG_CTL_BYTES:
+ case SND_SOC_TPLG_TYPE_BYTES:
ret = soc_tplg_dbytes_create(tplg, le32_to_cpu(hdr->payload_size));
break;
default:
- soc_bind_err(tplg, control_hdr, i);
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
+
if (ret < 0) {
- dev_err(tplg->dev, "ASoC: invalid control\n");
+ dev_err(tplg->dev, "ASoC: invalid control type: %d, index: %d at 0x%lx\n",
+ control_hdr->type, i, soc_tplg_get_offset(tplg));
return ret;
}
-
}
return 0;
@@ -1061,7 +1026,8 @@ static int soc_tplg_add_route(struct soc_tplg *tplg,
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_context *dapm = snd_soc_component_to_dapm(tplg->comp);
+ const size_t maxlen = SNDRV_CTL_ELEM_ID_NAME_MAXLEN;
struct snd_soc_tplg_dapm_graph_elem *elem;
struct snd_soc_dapm_route *route;
int count, i;
@@ -1085,35 +1051,32 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg,
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) {
- ret = -EINVAL;
- break;
- }
- if (strnlen(elem->sink, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
- SNDRV_CTL_ELEM_ID_NAME_MAXLEN) {
+ if ((strnlen(elem->source, maxlen) == maxlen) ||
+ (strnlen(elem->sink, maxlen) == maxlen) ||
+ (strnlen(elem->control, maxlen) == maxlen)) {
ret = -EINVAL;
break;
}
- if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
- SNDRV_CTL_ELEM_ID_NAME_MAXLEN) {
- ret = -EINVAL;
+
+ route->source = devm_kstrdup(tplg->dev, elem->source, GFP_KERNEL);
+ route->sink = devm_kstrdup(tplg->dev, elem->sink, GFP_KERNEL);
+ if (!route->source || !route->sink) {
+ ret = -ENOMEM;
break;
}
- route->source = elem->source;
- route->sink = elem->sink;
-
- /* set to NULL atm for tplg users */
- route->connected = NULL;
- if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 0)
- route->control = NULL;
- else
- route->control = elem->control;
+ if (strnlen(elem->control, maxlen) != 0) {
+ route->control = devm_kstrdup(tplg->dev, elem->control, GFP_KERNEL);
+ if (!route->control) {
+ ret = -ENOMEM;
+ break;
+ }
+ }
/* add route dobj to dobj_list */
route->dobj.type = SND_SOC_DOBJ_GRAPH;
- route->dobj.ops = tplg->ops;
+ if (tplg->ops)
+ route->dobj.unload = tplg->ops->dapm_route_unload;
route->dobj.index = tplg->index;
list_add(&route->dobj.list, &tplg->comp->dobj_list);
@@ -1123,226 +1086,18 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg,
break;
}
- /* add route, but keep going if some fail */
- snd_soc_dapm_add_routes(dapm, route, 1);
+ ret = snd_soc_dapm_add_routes(dapm, route, 1);
+ if (ret)
+ break;
}
return ret;
}
-static int soc_tplg_dapm_widget_dmixer_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc)
-{
- struct soc_mixer_control *sm;
- struct snd_soc_tplg_mixer_control *mc;
- int err;
-
- mc = (struct snd_soc_tplg_mixer_control *)tplg->pos;
-
- /* validate kcontrol */
- if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
- SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
- return -EINVAL;
-
- sm = devm_kzalloc(tplg->dev, sizeof(*sm), GFP_KERNEL);
- if (!sm)
- return -ENOMEM;
-
- 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\n",
- mc->hdr.name);
-
- kc->private_value = (long)sm;
- kc->name = devm_kstrdup(tplg->dev, mc->hdr.name, GFP_KERNEL);
- if (!kc->name)
- return -ENOMEM;
- kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- 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,
- SNDRV_CHMAP_FL);
- sm->rreg = tplc_chan_get_reg(tplg, mc->channel,
- SNDRV_CHMAP_FR);
- sm->shift = tplc_chan_get_shift(tplg, mc->channel,
- SNDRV_CHMAP_FL);
- sm->rshift = tplc_chan_get_shift(tplg, mc->channel,
- SNDRV_CHMAP_FR);
-
- 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);
-
- /* map io handlers */
- err = soc_tplg_kcontrol_bind_io(&mc->hdr, kc, tplg);
- if (err) {
- soc_control_err(tplg, &mc->hdr, mc->hdr.name);
- return err;
- }
-
- /* 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);
- return err;
- }
-
- /* pass control to driver for optional further init */
- err = soc_tplg_control_load(tplg, kc, (struct snd_soc_tplg_ctl_hdr *)mc);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: failed to init %s\n",
- mc->hdr.name);
- return err;
- }
-
- return 0;
-}
-
-static int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc)
-{
- struct snd_soc_tplg_enum_control *ec;
- struct soc_enum *se;
- int err;
-
- ec = (struct snd_soc_tplg_enum_control *)tplg->pos;
- /* validate kcontrol */
- if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
- SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
- return -EINVAL;
-
- se = devm_kzalloc(tplg->dev, sizeof(*se), GFP_KERNEL);
- if (!se)
- return -ENOMEM;
-
- 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->private_value = (long)se;
- kc->name = devm_kstrdup(tplg->dev, ec->hdr.name, GFP_KERNEL);
- if (!kc->name)
- return -ENOMEM;
- kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc->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);
- se->shift_l = tplc_chan_get_shift(tplg, ec->channel,
- SNDRV_CHMAP_FL);
- se->shift_r = tplc_chan_get_shift(tplg, ec->channel,
- SNDRV_CHMAP_FR);
-
- se->items = le32_to_cpu(ec->items);
- se->mask = le32_to_cpu(ec->mask);
- se->dobj.index = tplg->index;
-
- 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(tplg, se, ec);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: could not create values for %s\n",
- ec->hdr.name);
- return err;
- }
- fallthrough;
- case SND_SOC_TPLG_CTL_ENUM:
- case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
- case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
- err = soc_tplg_denum_create_texts(tplg, se, ec);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: could not create texts for %s\n",
- ec->hdr.name);
- return err;
- }
- break;
- default:
- dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n",
- ec->hdr.ops.info, ec->hdr.name);
- return -EINVAL;
- }
-
- /* map io handlers */
- err = soc_tplg_kcontrol_bind_io(&ec->hdr, kc, tplg);
- if (err) {
- soc_control_err(tplg, &ec->hdr, ec->hdr.name);
- return err;
- }
-
- /* pass control to driver for optional further init */
- err = soc_tplg_control_load(tplg, kc, (struct snd_soc_tplg_ctl_hdr *)ec);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: failed to init %s\n",
- ec->hdr.name);
- return err;
- }
-
- return 0;
-}
-
-static int soc_tplg_dapm_widget_dbytes_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc)
-{
- struct snd_soc_tplg_bytes_control *be;
- struct soc_bytes_ext *sbe;
- int err;
-
- 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)
- return -EINVAL;
-
- sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL);
- if (!sbe)
- return -ENOMEM;
-
- tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) +
- 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->private_value = (long)sbe;
- kc->name = devm_kstrdup(tplg->dev, be->hdr.name, GFP_KERNEL);
- if (!kc->name)
- return -ENOMEM;
- kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc->access = le32_to_cpu(be->hdr.access);
-
- 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, tplg);
- if (err) {
- soc_control_err(tplg, &be->hdr, be->hdr.name);
- return err;
- }
-
- /* pass control to driver for optional further init */
- err = soc_tplg_control_load(tplg, kc, (struct snd_soc_tplg_ctl_hdr *)be);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: failed to init %s\n",
- be->hdr.name);
- return err;
- }
-
- return 0;
-}
-
static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
struct snd_soc_tplg_dapm_widget *w)
{
- struct snd_soc_dapm_context *dapm = &tplg->comp->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(tplg->comp);
struct snd_soc_dapm_widget template, *widget;
struct snd_soc_tplg_ctl_hdr *control_hdr;
struct snd_soc_card *card = tplg->comp->card;
@@ -1401,50 +1156,46 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
template.num_kcontrols = le32_to_cpu(w->num_kcontrols);
kc = devm_kcalloc(tplg->dev, le32_to_cpu(w->num_kcontrols), sizeof(*kc), GFP_KERNEL);
- if (!kc)
+ if (!kc) {
+ ret = -ENOMEM;
goto hdr_err;
+ }
kcontrol_type = devm_kcalloc(tplg->dev, le32_to_cpu(w->num_kcontrols), sizeof(unsigned int),
GFP_KERNEL);
- if (!kcontrol_type)
+ if (!kcontrol_type) {
+ ret = -ENOMEM;
goto hdr_err;
+ }
for (i = 0; i < le32_to_cpu(w->num_kcontrols); i++) {
control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos;
- 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:
- case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
- case SND_SOC_TPLG_CTL_RANGE:
- case SND_SOC_TPLG_DAPM_CTL_VOLSW:
+
+ switch (le32_to_cpu(control_hdr->type)) {
+ case SND_SOC_TPLG_TYPE_MIXER:
/* volume mixer */
kc[i].index = mixer_count;
kcontrol_type[i] = SND_SOC_TPLG_TYPE_MIXER;
mixer_count++;
- ret = soc_tplg_dapm_widget_dmixer_create(tplg, &kc[i]);
+ ret = soc_tplg_control_dmixer_create(tplg, &kc[i]);
if (ret < 0)
goto hdr_err;
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:
+ case SND_SOC_TPLG_TYPE_ENUM:
/* enumerated mixer */
kc[i].index = enum_count;
kcontrol_type[i] = SND_SOC_TPLG_TYPE_ENUM;
enum_count++;
- ret = soc_tplg_dapm_widget_denum_create(tplg, &kc[i]);
+ ret = soc_tplg_control_denum_create(tplg, &kc[i]);
if (ret < 0)
goto hdr_err;
break;
- case SND_SOC_TPLG_CTL_BYTES:
+ case SND_SOC_TPLG_TYPE_BYTES:
/* bytes control */
kc[i].index = bytes_count;
kcontrol_type[i] = SND_SOC_TPLG_TYPE_BYTES;
bytes_count++;
- ret = soc_tplg_dapm_widget_dbytes_create(tplg, &kc[i]);
+ ret = soc_tplg_control_dbytes_create(tplg, &kc[i]);
if (ret < 0)
goto hdr_err;
break;
@@ -1468,7 +1219,7 @@ widget:
/* card dapm mutex is held by the core if we are loading topology
* data during sound card init. */
- if (card->instantiated)
+ if (snd_soc_card_is_instantiated(card))
widget = snd_soc_dapm_new_control(dapm, &template);
else
widget = snd_soc_dapm_new_control_unlocked(dapm, &template);
@@ -1479,7 +1230,8 @@ widget:
widget->dobj.type = SND_SOC_DOBJ_WIDGET;
widget->dobj.widget.kcontrol_type = kcontrol_type;
- widget->dobj.ops = tplg->ops;
+ if (tplg->ops)
+ widget->dobj.unload = tplg->ops->widget_unload;
widget->dobj.index = tplg->index;
list_add(&widget->dobj.list, &tplg->comp->dobj_list);
@@ -1493,7 +1245,8 @@ widget:
return 0;
ready_err:
- remove_widget(widget->dapm->component, &widget->dobj, SOC_TPLG_PASS_WIDGET);
+ soc_tplg_remove_widget(snd_soc_dapm_to_component(widget->dapm),
+ &widget->dobj, SOC_TPLG_PASS_WIDGET);
snd_soc_dapm_free_widget(widget);
hdr_err:
kfree(template.sname);
@@ -1556,18 +1309,16 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg)
/* Card might not have been registered at this point.
* If so, just return success.
*/
- if (!card || !card->instantiated) {
- dev_warn(tplg->dev, "ASoC: Parent card not yet available,"
- " widget card binding deferred\n");
+ if (!snd_soc_card_is_instantiated(card)) {
+ dev_warn(tplg->dev, "ASoC: Parent card not yet available, widget card binding deferred\n");
return 0;
}
ret = snd_soc_dapm_new_widgets(card);
if (ret < 0)
- dev_err(tplg->dev, "ASoC: failed to create new widgets %d\n",
- ret);
+ dev_err(tplg->dev, "ASoC: failed to create new widgets %d\n", ret);
- return 0;
+ return ret;
}
static int set_stream_info(struct soc_tplg *tplg, struct snd_soc_pcm_stream *stream,
@@ -1606,6 +1357,10 @@ static void set_dai_flags(struct snd_soc_dai_driver *dai_drv,
1 : 0;
}
+static const struct snd_soc_dai_ops tplg_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
static int soc_tplg_dai_create(struct soc_tplg *tplg,
struct snd_soc_tplg_pcm *pcm)
{
@@ -1613,8 +1368,7 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
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);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(tplg->comp);
int ret;
dai_drv = devm_kzalloc(tplg->dev, sizeof(struct snd_soc_dai_driver), GFP_KERNEL);
@@ -1647,7 +1401,7 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
}
if (pcm->compress)
- dai_drv->compress_new = snd_soc_new_compress;
+ dai_drv->ops = &tplg_dai_ops;
/* pass control to component driver for optional further init */
ret = soc_tplg_dai_load(tplg, dai_drv, pcm, NULL);
@@ -1657,8 +1411,9 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
}
dai_drv->dobj.index = tplg->index;
- dai_drv->dobj.ops = tplg->ops;
dai_drv->dobj.type = SND_SOC_DOBJ_PCM;
+ if (tplg->ops)
+ dai_drv->dobj.unload = tplg->ops->dai_unload;
list_add(&dai_drv->dobj.list, &tplg->comp->dobj_list);
/* register the DAI to the component */
@@ -1719,16 +1474,12 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg,
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 (tplg->ops)
+ link->dobj.unload = tplg->ops->link_unload;
if (strlen(pcm->pcm_name)) {
link->name = devm_kstrdup(tplg->dev, pcm->pcm_name, GFP_KERNEL);
@@ -1748,16 +1499,25 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg,
}
}
- link->codecs->name = "snd-soc-dummy";
- link->codecs->dai_name = "snd-soc-dummy-dai";
+ /*
+ * Many topology are assuming link has Codec / Platform, and
+ * these might be overwritten at soc_tplg_dai_link_load().
+ * Don't use &snd_soc_dummy_dlc here.
+ */
+ link->codecs = &dlc[1]; /* Don't use &snd_soc_dummy_dlc here */
+ link->codecs->name = "snd-soc-dummy";
+ link->codecs->dai_name = "snd-soc-dummy-dai";
+ link->num_codecs = 1;
- link->platforms->name = "snd-soc-dummy";
+ link->platforms = &dlc[2]; /* Don't use &snd_soc_dummy_dlc here */
+ link->platforms->name = "snd-soc-dummy";
+ link->num_platforms = 1;
/* enable DPCM */
link->dynamic = 1;
link->ignore_pmdown_time = 1;
- link->dpcm_playback = le32_to_cpu(pcm->playback);
- link->dpcm_capture = le32_to_cpu(pcm->capture);
+ link->playback_only = le32_to_cpu(pcm->playback) && !le32_to_cpu(pcm->capture);
+ link->capture_only = !le32_to_cpu(pcm->playback) && le32_to_cpu(pcm->capture);
if (pcm->flag_mask)
set_link_flags(link,
le32_to_cpu(pcm->flag_mask),
@@ -1770,9 +1530,10 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg,
goto err;
}
- ret = snd_soc_add_pcm_runtime(tplg->comp->card, link);
+ ret = snd_soc_add_pcm_runtimes(tplg->comp->card, link, 1);
if (ret < 0) {
- dev_err(tplg->dev, "ASoC: adding FE link failed\n");
+ if (ret != -EPROBE_DEFER)
+ dev_err(tplg->dev, "ASoC: adding FE link failed\n");
goto err;
}
@@ -1796,83 +1557,13 @@ static int soc_tplg_pcm_create(struct soc_tplg *tplg,
return soc_tplg_fe_link_create(tplg, pcm);
}
-/* copy stream caps from the old version 4 of source */
-static void stream_caps_new_ver(struct snd_soc_tplg_stream_caps *dest,
- struct snd_soc_tplg_stream_caps_v4 *src)
-{
- 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;
- dest->rate_min = src->rate_min;
- dest->rate_max = src->rate_max;
- dest->channels_min = src->channels_min;
- dest->channels_max = src->channels_max;
- dest->periods_min = src->periods_min;
- dest->periods_max = src->periods_max;
- dest->period_size_min = src->period_size_min;
- dest->period_size_max = src->period_size_max;
- dest->buffer_size_min = src->buffer_size_min;
- dest->buffer_size_max = src->buffer_size_max;
-}
-
-/**
- * pcm_new_ver - Create the new version of PCM from the old version.
- * @tplg: topology context
- * @src: older version of pcm as a source
- * @pcm: latest version of pcm created from the source
- *
- * Support from version 4. User should free the returned pcm manually.
- */
-static int pcm_new_ver(struct soc_tplg *tplg,
- struct snd_soc_tplg_pcm *src,
- struct snd_soc_tplg_pcm **pcm)
-{
- struct snd_soc_tplg_pcm *dest;
- struct snd_soc_tplg_pcm_v4 *src_v4;
- int i;
-
- *pcm = NULL;
-
- if (le32_to_cpu(src->size) != sizeof(*src_v4)) {
- dev_err(tplg->dev, "ASoC: invalid PCM size\n");
- return -EINVAL;
- }
-
- dev_warn(tplg->dev, "ASoC: old version of PCM\n");
- src_v4 = (struct snd_soc_tplg_pcm_v4 *)src;
- dest = kzalloc(sizeof(*dest), GFP_KERNEL);
- if (!dest)
- return -ENOMEM;
-
- 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;
- dest->dai_id = src_v4->dai_id;
- dest->playback = src_v4->playback;
- dest->capture = src_v4->capture;
- dest->compress = src_v4->compress;
- dest->num_streams = src_v4->num_streams;
- 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));
-
- for (i = 0; i < 2; i++)
- stream_caps_new_ver(&dest->caps[i], &src_v4->caps[i]);
-
- *pcm = dest;
- return 0;
-}
-
static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
struct snd_soc_tplg_hdr *hdr)
{
- struct snd_soc_tplg_pcm *pcm, *_pcm;
+ struct snd_soc_tplg_pcm *pcm;
int count;
int size;
int i;
- bool abi_match;
int ret;
count = le32_to_cpu(hdr->count);
@@ -1880,8 +1571,7 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
/* check the element size and count */
pcm = (struct snd_soc_tplg_pcm *)tplg->pos;
size = le32_to_cpu(pcm->size);
- if (size > sizeof(struct snd_soc_tplg_pcm)
- || size < sizeof(struct snd_soc_tplg_pcm_v4)) {
+ if (size > sizeof(struct snd_soc_tplg_pcm)) {
dev_err(tplg->dev, "ASoC: invalid size %d for PCM elems\n",
size);
return -EINVAL;
@@ -1900,31 +1590,18 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
/* check ABI version by size, create a new version of pcm
* if abi not match.
*/
- if (size == sizeof(*pcm)) {
- abi_match = true;
- _pcm = pcm;
- } else {
- abi_match = false;
- ret = pcm_new_ver(tplg, pcm, &_pcm);
- if (ret < 0)
- return ret;
- }
+ if (size != sizeof(*pcm))
+ return -EINVAL;
/* create the FE DAIs and DAI links */
- ret = soc_tplg_pcm_create(tplg, _pcm);
- if (ret < 0) {
- if (!abi_match)
- kfree(_pcm);
+ ret = soc_tplg_pcm_create(tplg, pcm);
+ if (ret < 0)
return ret;
- }
/* offset by version-specific struct size and
* real priv data size
*/
- tplg->pos += size + le32_to_cpu(_pcm->priv.size);
-
- if (!abi_match)
- kfree(_pcm); /* free the duplicated one */
+ tplg->pos += size + le32_to_cpu(pcm->priv.size);
}
dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count);
@@ -2001,49 +1678,6 @@ static void set_link_hw_format(struct snd_soc_dai_link *link,
}
/**
- * link_new_ver - Create a new physical link config from the old
- * version of source.
- * @tplg: topology context
- * @src: old version of phyical link config as a source
- * @link: latest version of physical link config created from the source
- *
- * Support from version 4. User need free the returned link config manually.
- */
-static int link_new_ver(struct soc_tplg *tplg,
- struct snd_soc_tplg_link_config *src,
- struct snd_soc_tplg_link_config **link)
-{
- struct snd_soc_tplg_link_config *dest;
- struct snd_soc_tplg_link_config_v4 *src_v4;
- int i;
-
- *link = NULL;
-
- 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;
- }
-
- dev_warn(tplg->dev, "ASoC: old version of physical link config\n");
-
- src_v4 = (struct snd_soc_tplg_link_config_v4 *)src;
- dest = kzalloc(sizeof(*dest), GFP_KERNEL);
- if (!dest)
- return -ENOMEM;
-
- dest->size = cpu_to_le32(sizeof(*dest));
- dest->id = src_v4->id;
- dest->num_streams = src_v4->num_streams;
- 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
@@ -2070,11 +1704,11 @@ static struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card,
if (link->id != id)
continue;
- if (name && (!link->name || strcmp(name, link->name)))
+ if (name && (!link->name || !strstr(link->name, name)))
continue;
- if (stream_name && (!link->stream_name
- || strcmp(stream_name, link->stream_name)))
+ if (stream_name && (!link->stream_name ||
+ !strstr(link->stream_name, stream_name)))
continue;
return link;
@@ -2135,8 +1769,9 @@ static int soc_tplg_link_config(struct soc_tplg *tplg,
/* 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;
+ if (tplg->ops)
+ link->dobj.unload = tplg->ops->link_unload;
list_add(&link->dobj.list, &tplg->comp->dobj_list);
return 0;
@@ -2147,19 +1782,17 @@ static int soc_tplg_link_config(struct soc_tplg *tplg,
static int soc_tplg_link_elems_load(struct soc_tplg *tplg,
struct snd_soc_tplg_hdr *hdr)
{
- struct snd_soc_tplg_link_config *link, *_link;
+ struct snd_soc_tplg_link_config *link;
int count;
int size;
int i, ret;
- bool abi_match;
count = le32_to_cpu(hdr->count);
/* check the element size and count */
link = (struct snd_soc_tplg_link_config *)tplg->pos;
size = le32_to_cpu(link->size);
- if (size > sizeof(struct snd_soc_tplg_link_config)
- || size < sizeof(struct snd_soc_tplg_link_config_v4)) {
+ if (size > sizeof(struct snd_soc_tplg_link_config)) {
dev_err(tplg->dev, "ASoC: invalid size %d for physical link elems\n",
size);
return -EINVAL;
@@ -2174,30 +1807,17 @@ static int soc_tplg_link_elems_load(struct soc_tplg *tplg,
for (i = 0; i < count; i++) {
link = (struct snd_soc_tplg_link_config *)tplg->pos;
size = le32_to_cpu(link->size);
- if (size == sizeof(*link)) {
- abi_match = true;
- _link = link;
- } else {
- abi_match = false;
- ret = link_new_ver(tplg, link, &_link);
- if (ret < 0)
- return ret;
- }
+ if (size != sizeof(*link))
+ return -EINVAL;
- ret = soc_tplg_link_config(tplg, _link);
- if (ret < 0) {
- if (!abi_match)
- kfree(_link);
+ ret = soc_tplg_link_config(tplg, link);
+ if (ret < 0)
return ret;
- }
/* offset by version-specific struct size and
* real priv data size
*/
- tplg->pos += size + le32_to_cpu(_link->priv.size);
-
- if (!abi_match)
- kfree(_link); /* free the duplicated one */
+ tplg->pos += size + le32_to_cpu(link->priv.size);
}
return 0;
@@ -2246,7 +1866,7 @@ static int soc_tplg_dai_config(struct soc_tplg *tplg,
caps = &d->caps[SND_SOC_TPLG_STREAM_PLAYBACK];
ret = set_stream_info(tplg, stream, caps);
if (ret < 0)
- goto err;
+ return ret;
}
if (d->capture) {
@@ -2254,7 +1874,7 @@ static int soc_tplg_dai_config(struct soc_tplg *tplg,
caps = &d->caps[SND_SOC_TPLG_STREAM_CAPTURE];
ret = set_stream_info(tplg, stream, caps);
if (ret < 0)
- goto err;
+ return ret;
}
if (d->flag_mask)
@@ -2266,13 +1886,10 @@ static int soc_tplg_dai_config(struct soc_tplg *tplg,
ret = soc_tplg_dai_load(tplg, dai_drv, NULL, dai);
if (ret < 0) {
dev_err(tplg->dev, "ASoC: DAI loading failed\n");
- goto err;
+ return ret;
}
return 0;
-
-err:
- return ret;
}
/* load physical DAI elements */
@@ -2307,95 +1924,29 @@ static int soc_tplg_dai_elems_load(struct soc_tplg *tplg,
return 0;
}
-/**
- * manifest_new_ver - Create a new version of manifest from the old version
- * of source.
- * @tplg: topology context
- * @src: old version of manifest as a source
- * @manifest: latest version of manifest created from the source
- *
- * Support from version 4. Users need free the returned manifest manually.
- */
-static int manifest_new_ver(struct soc_tplg *tplg,
- struct snd_soc_tplg_manifest *src,
- struct snd_soc_tplg_manifest **manifest)
-{
- struct snd_soc_tplg_manifest *dest;
- struct snd_soc_tplg_manifest_v4 *src_v4;
- int size;
-
- *manifest = NULL;
-
- size = le32_to_cpu(src->size);
- if (size != sizeof(*src_v4)) {
- dev_warn(tplg->dev, "ASoC: invalid manifest size %d\n",
- size);
- if (size)
- return -EINVAL;
- 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) + le32_to_cpu(src_v4->priv.size),
- GFP_KERNEL);
- if (!dest)
- return -ENOMEM;
-
- 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;
- dest->pcm_elems = src_v4->pcm_elems;
- dest->dai_link_elems = src_v4->dai_link_elems;
- dest->priv.size = src_v4->priv.size;
- if (dest->priv.size)
- memcpy(dest->priv.data, src_v4->priv.data,
- le32_to_cpu(src_v4->priv.size));
-
- *manifest = dest;
- return 0;
-}
-
static int soc_tplg_manifest_load(struct soc_tplg *tplg,
struct snd_soc_tplg_hdr *hdr)
{
- struct snd_soc_tplg_manifest *manifest, *_manifest;
- bool abi_match;
+ struct snd_soc_tplg_manifest *manifest;
int ret = 0;
manifest = (struct snd_soc_tplg_manifest *)tplg->pos;
/* check ABI version by size, create a new manifest if abi not match */
- if (le32_to_cpu(manifest->size) == sizeof(*manifest)) {
- abi_match = true;
- _manifest = manifest;
- } else {
- abi_match = false;
-
- ret = manifest_new_ver(tplg, manifest, &_manifest);
- if (ret < 0)
- return ret;
- }
+ if (le32_to_cpu(manifest->size) != sizeof(*manifest))
+ return -EINVAL;
/* pass control to component driver for optional further init */
if (tplg->ops && tplg->ops->manifest)
- ret = tplg->ops->manifest(tplg->comp, tplg->index, _manifest);
-
- if (!abi_match) /* free the duplicated one */
- kfree(_manifest);
+ ret = tplg->ops->manifest(tplg->comp, tplg->index, manifest);
return ret;
}
/* validate header magic, size and type */
-static int soc_valid_header(struct soc_tplg *tplg,
+static int soc_tplg_valid_header(struct soc_tplg *tplg,
struct snd_soc_tplg_hdr *hdr)
{
- if (soc_tplg_get_hdr_offset(tplg) >= tplg->fw->size)
- return 0;
-
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",
@@ -2404,7 +1955,7 @@ static int soc_valid_header(struct soc_tplg *tplg,
return -EINVAL;
}
- if (soc_tplg_get_hdr_offset(tplg) + hdr->payload_size >= tplg->fw->size) {
+ if (soc_tplg_get_hdr_offset(tplg) + le32_to_cpu(hdr->payload_size) >= tplg->fw->size) {
dev_err(tplg->dev,
"ASoC: invalid header of type %d at offset %ld payload_size %d\n",
le32_to_cpu(hdr->type), soc_tplg_get_hdr_offset(tplg),
@@ -2446,7 +1997,7 @@ static int soc_valid_header(struct soc_tplg *tplg,
return -EINVAL;
}
- return 1;
+ return 0;
}
/* check header type and call appropriate handler */
@@ -2527,20 +2078,18 @@ static int soc_tplg_process_headers(struct soc_tplg *tplg)
while (!soc_tplg_is_eof(tplg)) {
/* make sure header is valid before loading */
- ret = soc_valid_header(tplg, hdr);
- if (ret < 0) {
- dev_err(tplg->dev,
- "ASoC: topology: invalid header: %d\n", ret);
+ ret = soc_tplg_valid_header(tplg, hdr);
+ if (ret < 0)
return ret;
- } else if (ret == 0) {
- break;
- }
/* load the header object */
ret = soc_tplg_load_header(tplg, hdr);
if (ret < 0) {
- dev_err(tplg->dev,
- "ASoC: topology: could not load header: %d\n", ret);
+ if (ret != -EPROBE_DEFER) {
+ dev_err(tplg->dev,
+ "ASoC: topology: could not load header: %d\n",
+ ret);
+ }
return ret;
}
@@ -2554,9 +2103,6 @@ static int soc_tplg_process_headers(struct soc_tplg *tplg)
/* signal DAPM we are complete */
ret = soc_tplg_dapm_complete(tplg);
- if (ret < 0)
- dev_err(tplg->dev,
- "ASoC: failed to initialise DAPM from Firmware\n");
return ret;
}
@@ -2574,7 +2120,7 @@ static int soc_tplg_load(struct soc_tplg *tplg)
/* load audio component topology from "firmware" file */
int snd_soc_tplg_component_load(struct snd_soc_component *comp,
- struct snd_soc_tplg_ops *ops, const struct firmware *fw)
+ const struct snd_soc_tplg_ops *ops, const struct firmware *fw)
{
struct soc_tplg tplg;
int ret;
@@ -2614,7 +2160,6 @@ EXPORT_SYMBOL_GPL(snd_soc_tplg_component_load);
/* remove dynamic controls from the component driver */
int snd_soc_tplg_component_remove(struct snd_soc_component *comp)
{
- struct snd_card *card = comp->card->snd_card;
struct snd_soc_dobj *dobj, *next_dobj;
int pass;
@@ -2622,31 +2167,26 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp)
for (pass = SOC_TPLG_PASS_END; pass >= SOC_TPLG_PASS_START; pass--) {
/* remove mixer controls */
- down_write(&card->controls_rwsem);
list_for_each_entry_safe(dobj, next_dobj, &comp->dobj_list,
list) {
switch (dobj->type) {
- case SND_SOC_DOBJ_MIXER:
- remove_mixer(comp, dobj, pass);
- break;
- case SND_SOC_DOBJ_ENUM:
- remove_enum(comp, dobj, pass);
- break;
case SND_SOC_DOBJ_BYTES:
- remove_bytes(comp, dobj, pass);
+ case SND_SOC_DOBJ_ENUM:
+ case SND_SOC_DOBJ_MIXER:
+ soc_tplg_remove_kcontrol(comp, dobj, pass);
break;
case SND_SOC_DOBJ_GRAPH:
- remove_route(comp, dobj, pass);
+ soc_tplg_remove_route(comp, dobj, pass);
break;
case SND_SOC_DOBJ_WIDGET:
- remove_widget(comp, dobj, pass);
+ soc_tplg_remove_widget(comp, dobj, pass);
break;
case SND_SOC_DOBJ_PCM:
- remove_dai(comp, dobj, pass);
+ soc_tplg_remove_dai(comp, dobj, pass);
break;
case SND_SOC_DOBJ_DAI_LINK:
- remove_link(comp, dobj, pass);
+ soc_tplg_remove_link(comp, dobj, pass);
break;
case SND_SOC_DOBJ_BACKEND_LINK:
/*
@@ -2661,7 +2201,6 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp)
break;
}
}
- up_write(&card->controls_rwsem);
}
/* let caller know if FW can be freed when no objects are left */