ASoC: topology: Allow a widget to have multiple enum controls
This patch can create multiple enumerated mixer controls for a widget. Previously topology kernel driver assumes a widget can have only one emumerated mixer control. We need to remove this restriction for Broxton. Its firmware modules (widgets) may need multiple enum controls based on the channel and MIC combination. No ABI change is needed. The ABI allows a widget to embed multiple controls. Reported-by: G Kranthi <gudishax.kranthikumar@intel.com> Signed-off-by: Mengdong Lin <mengdong.lin@linux.intel.com> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Родитель
17e593e32c
Коммит
1a7dd6e2f1
|
@ -486,21 +486,24 @@ static void remove_widget(struct snd_soc_component *comp,
|
||||||
dobj->ops->widget_unload(comp, dobj);
|
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.
|
* The enum may either have an array of values or strings.
|
||||||
*/
|
*/
|
||||||
if (dobj->widget.kcontrol_enum) {
|
if (dobj->widget.kcontrol_enum) {
|
||||||
/* enumerated widget mixer */
|
/* enumerated widget mixer */
|
||||||
struct soc_enum *se =
|
for (i = 0; i < w->num_kcontrols; i++) {
|
||||||
(struct soc_enum *)w->kcontrols[0]->private_value;
|
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);
|
kfree(se->dobj.control.dvalues);
|
||||||
for (i = 0; i < se->items; i++)
|
for (i = 0; i < se->items; i++)
|
||||||
kfree(se->dobj.control.dtexts[i]);
|
kfree(se->dobj.control.dtexts[i]);
|
||||||
|
|
||||||
kfree(se);
|
kfree(se);
|
||||||
|
}
|
||||||
kfree(w->kcontrol_news);
|
kfree(w->kcontrol_news);
|
||||||
} else {
|
} else {
|
||||||
/* non enumerated widget mixer */
|
/* non enumerated widget mixer */
|
||||||
|
@ -1256,98 +1259,105 @@ err:
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create(
|
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_kcontrol_new *kc;
|
||||||
struct snd_soc_tplg_enum_control *ec;
|
struct snd_soc_tplg_enum_control *ec;
|
||||||
struct soc_enum *se;
|
struct soc_enum *se;
|
||||||
int i, err;
|
int i, j, err;
|
||||||
|
|
||||||
ec = (struct snd_soc_tplg_enum_control *)tplg->pos;
|
kc = kcalloc(num_kcontrols, sizeof(*kc), GFP_KERNEL);
|
||||||
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;
|
|
||||||
|
|
||||||
kc = kzalloc(sizeof(*kc), GFP_KERNEL);
|
|
||||||
if (kc == NULL)
|
if (kc == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
se = kzalloc(sizeof(*se), GFP_KERNEL);
|
for (i = 0; i < num_kcontrols; i++) {
|
||||||
if (se == NULL)
|
ec = (struct snd_soc_tplg_enum_control *)tplg->pos;
|
||||||
goto err;
|
/* validate kcontrol */
|
||||||
|
if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
|
||||||
|
SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n",
|
se = kzalloc(sizeof(*se), GFP_KERNEL);
|
||||||
ec->hdr.name);
|
if (se == NULL)
|
||||||
|
goto err;
|
||||||
|
|
||||||
kc->name = ec->hdr.name;
|
dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n",
|
||||||
kc->private_value = (long)se;
|
|
||||||
kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
|
||||||
kc->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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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);
|
ec->hdr.name);
|
||||||
goto err_se;
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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: 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;
|
return kc;
|
||||||
|
|
||||||
err_se:
|
err_se:
|
||||||
/* free values and texts */
|
for (; i >= 0; i--) {
|
||||||
kfree(se->dobj.control.dvalues);
|
/* free values and texts */
|
||||||
for (i = 0; i < ec->items; i++)
|
se = (struct soc_enum *)kc[i].private_value;
|
||||||
kfree(se->dobj.control.dtexts[i]);
|
kfree(se->dobj.control.dvalues);
|
||||||
|
for (j = 0; j < ec->items; j++)
|
||||||
|
kfree(se->dobj.control.dtexts[j]);
|
||||||
|
|
||||||
kfree(se);
|
kfree(se);
|
||||||
|
}
|
||||||
err:
|
err:
|
||||||
kfree(kc);
|
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_VIRT:
|
||||||
case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
|
case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
|
||||||
template.dobj.widget.kcontrol_enum = 1;
|
template.dobj.widget.kcontrol_enum = 1;
|
||||||
template.num_kcontrols = 1;
|
template.num_kcontrols = w->num_kcontrols;
|
||||||
template.kcontrol_news =
|
template.kcontrol_news =
|
||||||
soc_tplg_dapm_widget_denum_create(tplg);
|
soc_tplg_dapm_widget_denum_create(tplg,
|
||||||
|
template.num_kcontrols);
|
||||||
if (!template.kcontrol_news) {
|
if (!template.kcontrol_news) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto hdr_err;
|
goto hdr_err;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче