|
@@ -486,21 +486,24 @@ static void remove_widget(struct snd_soc_component *comp,
|
|
|
dobj->ops->widget_unload(comp, dobj);
|
|
|
|
|
|
/*
|
|
|
- * Dynamic Widgets either have 1 enum kcontrol or 1..N mixers.
|
|
|
+ * Dynamic Widgets either have 1..N enum kcontrols or mixers.
|
|
|
* The enum may either have an array of values or strings.
|
|
|
*/
|
|
|
if (dobj->widget.kcontrol_enum) {
|
|
|
/* enumerated widget mixer */
|
|
|
- struct soc_enum *se =
|
|
|
- (struct soc_enum *)w->kcontrols[0]->private_value;
|
|
|
+ for (i = 0; i < w->num_kcontrols; i++) {
|
|
|
+ struct snd_kcontrol *kcontrol = w->kcontrols[i];
|
|
|
+ struct soc_enum *se =
|
|
|
+ (struct soc_enum *)kcontrol->private_value;
|
|
|
|
|
|
- snd_ctl_remove(card, w->kcontrols[0]);
|
|
|
+ snd_ctl_remove(card, kcontrol);
|
|
|
|
|
|
- kfree(se->dobj.control.dvalues);
|
|
|
- for (i = 0; i < se->items; i++)
|
|
|
- kfree(se->dobj.control.dtexts[i]);
|
|
|
+ kfree(se->dobj.control.dvalues);
|
|
|
+ for (i = 0; i < se->items; i++)
|
|
|
+ kfree(se->dobj.control.dtexts[i]);
|
|
|
|
|
|
- kfree(se);
|
|
|
+ kfree(se);
|
|
|
+ }
|
|
|
kfree(w->kcontrol_news);
|
|
|
} else {
|
|
|
/* non enumerated widget mixer */
|
|
@@ -1256,98 +1259,105 @@ err:
|
|
|
}
|
|
|
|
|
|
static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create(
|
|
|
- struct soc_tplg *tplg)
|
|
|
+ struct soc_tplg *tplg, int num_kcontrols)
|
|
|
{
|
|
|
struct snd_kcontrol_new *kc;
|
|
|
struct snd_soc_tplg_enum_control *ec;
|
|
|
struct soc_enum *se;
|
|
|
- int i, err;
|
|
|
-
|
|
|
- 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) ==
|
|
|
- SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
|
|
|
- return NULL;
|
|
|
+ int i, j, err;
|
|
|
|
|
|
- kc = kzalloc(sizeof(*kc), GFP_KERNEL);
|
|
|
+ kc = kcalloc(num_kcontrols, sizeof(*kc), GFP_KERNEL);
|
|
|
if (kc == NULL)
|
|
|
return NULL;
|
|
|
|
|
|
- se = kzalloc(sizeof(*se), GFP_KERNEL);
|
|
|
- if (se == NULL)
|
|
|
- goto err;
|
|
|
+ for (i = 0; i < num_kcontrols; i++) {
|
|
|
+ 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 NULL;
|
|
|
+
|
|
|
+ se = kzalloc(sizeof(*se), GFP_KERNEL);
|
|
|
+ if (se == NULL)
|
|
|
+ goto err;
|
|
|
|
|
|
- dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n",
|
|
|
- ec->hdr.name);
|
|
|
+ dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n",
|
|
|
+ ec->hdr.name);
|
|
|
|
|
|
- kc->name = ec->hdr.name;
|
|
|
- kc->private_value = (long)se;
|
|
|
- kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
|
|
- kc->access = ec->hdr.access;
|
|
|
+ kc[i].name = ec->hdr.name;
|
|
|
+ kc[i].private_value = (long)se;
|
|
|
+ kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
|
|
+ kc[i].access = 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);
|
|
|
+ /* 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 = ec->items;
|
|
|
- se->mask = ec->mask;
|
|
|
- se->dobj.index = tplg->index;
|
|
|
+ se->items = ec->items;
|
|
|
+ se->mask = ec->mask;
|
|
|
+ se->dobj.index = tplg->index;
|
|
|
|
|
|
- switch (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);
|
|
|
- if (err < 0) {
|
|
|
- dev_err(tplg->dev, "ASoC: could not create values for %s\n",
|
|
|
- ec->hdr.name);
|
|
|
+ switch (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);
|
|
|
+ if (err < 0) {
|
|
|
+ dev_err(tplg->dev, "ASoC: could not create values for %s\n",
|
|
|
+ ec->hdr.name);
|
|
|
+ goto err_se;
|
|
|
+ }
|
|
|
+ /* fall through to create texts */
|
|
|
+ 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(se, ec);
|
|
|
+ if (err < 0) {
|
|
|
+ dev_err(tplg->dev, "ASoC: could not create texts for %s\n",
|
|
|
+ ec->hdr.name);
|
|
|
+ goto err_se;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n",
|
|
|
+ ec->hdr.ops.info, ec->hdr.name);
|
|
|
goto err_se;
|
|
|
}
|
|
|
- /* fall through to create texts */
|
|
|
- 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(se, ec);
|
|
|
+
|
|
|
+ /* map io handlers */
|
|
|
+ err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc[i], tplg);
|
|
|
+ if (err) {
|
|
|
+ soc_control_err(tplg, &ec->hdr, ec->hdr.name);
|
|
|
+ goto err_se;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* pass control to driver for optional further init */
|
|
|
+ err = soc_tplg_init_kcontrol(tplg, &kc[i],
|
|
|
+ (struct snd_soc_tplg_ctl_hdr *)ec);
|
|
|
if (err < 0) {
|
|
|
- dev_err(tplg->dev, "ASoC: could not create texts for %s\n",
|
|
|
+ dev_err(tplg->dev, "ASoC: failed to init %s\n",
|
|
|
ec->hdr.name);
|
|
|
goto err_se;
|
|
|
}
|
|
|
- break;
|
|
|
- default:
|
|
|
- dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n",
|
|
|
- ec->hdr.ops.info, ec->hdr.name);
|
|
|
- goto err_se;
|
|
|
- }
|
|
|
|
|
|
- /* map io handlers */
|
|
|
- err = soc_tplg_kcontrol_bind_io(&ec->hdr, kc, tplg);
|
|
|
- if (err) {
|
|
|
- soc_control_err(tplg, &ec->hdr, ec->hdr.name);
|
|
|
- goto err_se;
|
|
|
- }
|
|
|
-
|
|
|
- /* pass control to driver for optional further init */
|
|
|
- err = soc_tplg_init_kcontrol(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);
|
|
|
- goto err_se;
|
|
|
+ tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
|
|
|
+ ec->priv.size);
|
|
|
}
|
|
|
|
|
|
return kc;
|
|
|
|
|
|
err_se:
|
|
|
- /* free values and texts */
|
|
|
- kfree(se->dobj.control.dvalues);
|
|
|
- for (i = 0; i < ec->items; i++)
|
|
|
- kfree(se->dobj.control.dtexts[i]);
|
|
|
+ for (; i >= 0; i--) {
|
|
|
+ /* free values and texts */
|
|
|
+ se = (struct soc_enum *)kc[i].private_value;
|
|
|
+ kfree(se->dobj.control.dvalues);
|
|
|
+ for (j = 0; j < ec->items; j++)
|
|
|
+ kfree(se->dobj.control.dtexts[j]);
|
|
|
|
|
|
- kfree(se);
|
|
|
+ kfree(se);
|
|
|
+ }
|
|
|
err:
|
|
|
kfree(kc);
|
|
|
|
|
@@ -1499,9 +1509,10 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
|
|
|
case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
|
|
|
case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
|
|
|
template.dobj.widget.kcontrol_enum = 1;
|
|
|
- template.num_kcontrols = 1;
|
|
|
+ template.num_kcontrols = w->num_kcontrols;
|
|
|
template.kcontrol_news =
|
|
|
- soc_tplg_dapm_widget_denum_create(tplg);
|
|
|
+ soc_tplg_dapm_widget_denum_create(tplg,
|
|
|
+ template.num_kcontrols);
|
|
|
if (!template.kcontrol_news) {
|
|
|
ret = -ENOMEM;
|
|
|
goto hdr_err;
|