diff options
| -rw-r--r-- | sound/soc/soc-topology.c | 163 |
1 files changed, 87 insertions, 76 deletions
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 4dfdc656cce6..63e1a50f2161 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c | |||
| @@ -486,21 +486,24 @@ static void remove_widget(struct snd_soc_component *comp, | |||
| 486 | dobj->ops->widget_unload(comp, dobj); | 486 | dobj->ops->widget_unload(comp, dobj); |
| 487 | 487 | ||
| 488 | /* | 488 | /* |
| 489 | * Dynamic Widgets either have 1 enum kcontrol or 1..N mixers. | 489 | * Dynamic Widgets either have 1..N enum kcontrols or mixers. |
| 490 | * The enum may either have an array of values or strings. | 490 | * The enum may either have an array of values or strings. |
| 491 | */ | 491 | */ |
| 492 | if (dobj->widget.kcontrol_enum) { | 492 | if (dobj->widget.kcontrol_enum) { |
| 493 | /* enumerated widget mixer */ | 493 | /* enumerated widget mixer */ |
| 494 | struct soc_enum *se = | 494 | for (i = 0; i < w->num_kcontrols; i++) { |
| 495 | (struct soc_enum *)w->kcontrols[0]->private_value; | 495 | struct snd_kcontrol *kcontrol = w->kcontrols[i]; |
| 496 | struct soc_enum *se = | ||
| 497 | (struct soc_enum *)kcontrol->private_value; | ||
| 496 | 498 | ||
| 497 | snd_ctl_remove(card, w->kcontrols[0]); | 499 | snd_ctl_remove(card, kcontrol); |
| 498 | 500 | ||
| 499 | kfree(se->dobj.control.dvalues); | 501 | kfree(se->dobj.control.dvalues); |
| 500 | for (i = 0; i < se->items; i++) | 502 | for (i = 0; i < se->items; i++) |
| 501 | kfree(se->dobj.control.dtexts[i]); | 503 | kfree(se->dobj.control.dtexts[i]); |
| 502 | 504 | ||
| 503 | kfree(se); | 505 | kfree(se); |
| 506 | } | ||
| 504 | kfree(w->kcontrol_news); | 507 | kfree(w->kcontrol_news); |
| 505 | } else { | 508 | } else { |
| 506 | /* non enumerated widget mixer */ | 509 | /* non enumerated widget mixer */ |
| @@ -1256,98 +1259,105 @@ err: | |||
| 1256 | } | 1259 | } |
| 1257 | 1260 | ||
| 1258 | static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create( | 1261 | static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create( |
| 1259 | struct soc_tplg *tplg) | 1262 | struct soc_tplg *tplg, int num_kcontrols) |
| 1260 | { | 1263 | { |
| 1261 | struct snd_kcontrol_new *kc; | 1264 | struct snd_kcontrol_new *kc; |
| 1262 | struct snd_soc_tplg_enum_control *ec; | 1265 | struct snd_soc_tplg_enum_control *ec; |
| 1263 | struct soc_enum *se; | 1266 | struct soc_enum *se; |
| 1264 | int i, err; | 1267 | int i, j, err; |
| 1265 | |||
| 1266 | ec = (struct snd_soc_tplg_enum_control *)tplg->pos; | ||
| 1267 | tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + | ||
| 1268 | ec->priv.size); | ||
| 1269 | |||
| 1270 | /* validate kcontrol */ | ||
| 1271 | if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == | ||
| 1272 | SNDRV_CTL_ELEM_ID_NAME_MAXLEN) | ||
| 1273 | return NULL; | ||
| 1274 | 1268 | ||
| 1275 | kc = kzalloc(sizeof(*kc), GFP_KERNEL); | 1269 | kc = kcalloc(num_kcontrols, sizeof(*kc), GFP_KERNEL); |
| 1276 | if (kc == NULL) | 1270 | if (kc == NULL) |
| 1277 | return NULL; | 1271 | return NULL; |
| 1278 | 1272 | ||
| 1279 | se = kzalloc(sizeof(*se), GFP_KERNEL); | 1273 | for (i = 0; i < num_kcontrols; i++) { |
| 1280 | if (se == NULL) | 1274 | ec = (struct snd_soc_tplg_enum_control *)tplg->pos; |
| 1281 | goto err; | 1275 | /* validate kcontrol */ |
| 1276 | if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == | ||
| 1277 | SNDRV_CTL_ELEM_ID_NAME_MAXLEN) | ||
| 1278 | return NULL; | ||
| 1279 | |||
| 1280 | se = kzalloc(sizeof(*se), GFP_KERNEL); | ||
| 1281 | if (se == NULL) | ||
| 1282 | goto err; | ||
| 1282 | 1283 | ||
| 1283 | dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n", | 1284 | dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n", |
| 1284 | ec->hdr.name); | 1285 | ec->hdr.name); |
| 1285 | 1286 | ||
| 1286 | kc->name = ec->hdr.name; | 1287 | kc[i].name = ec->hdr.name; |
| 1287 | kc->private_value = (long)se; | 1288 | kc[i].private_value = (long)se; |
| 1288 | kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; | 1289 | kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; |
| 1289 | kc->access = ec->hdr.access; | 1290 | kc[i].access = ec->hdr.access; |
| 1290 | 1291 | ||
| 1291 | /* we only support FL/FR channel mapping atm */ | 1292 | /* we only support FL/FR channel mapping atm */ |
| 1292 | se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); | 1293 | se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); |
| 1293 | se->shift_l = tplc_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FL); | 1294 | se->shift_l = tplc_chan_get_shift(tplg, ec->channel, |
| 1294 | se->shift_r = tplc_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FR); | 1295 | SNDRV_CHMAP_FL); |
| 1296 | se->shift_r = tplc_chan_get_shift(tplg, ec->channel, | ||
| 1297 | SNDRV_CHMAP_FR); | ||
| 1295 | 1298 | ||
| 1296 | se->items = ec->items; | 1299 | se->items = ec->items; |
| 1297 | se->mask = ec->mask; | 1300 | se->mask = ec->mask; |
| 1298 | se->dobj.index = tplg->index; | 1301 | se->dobj.index = tplg->index; |
| 1299 | 1302 | ||
| 1300 | switch (ec->hdr.ops.info) { | 1303 | switch (ec->hdr.ops.info) { |
| 1301 | case SND_SOC_TPLG_CTL_ENUM_VALUE: | 1304 | case SND_SOC_TPLG_CTL_ENUM_VALUE: |
| 1302 | case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: | 1305 | case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: |
| 1303 | err = soc_tplg_denum_create_values(se, ec); | 1306 | err = soc_tplg_denum_create_values(se, ec); |
| 1304 | if (err < 0) { | 1307 | if (err < 0) { |
| 1305 | dev_err(tplg->dev, "ASoC: could not create values for %s\n", | 1308 | dev_err(tplg->dev, "ASoC: could not create values for %s\n", |
| 1306 | ec->hdr.name); | 1309 | ec->hdr.name); |
| 1310 | goto err_se; | ||
| 1311 | } | ||
| 1312 | /* fall through to create texts */ | ||
| 1313 | case SND_SOC_TPLG_CTL_ENUM: | ||
| 1314 | case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: | ||
| 1315 | case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: | ||
| 1316 | err = soc_tplg_denum_create_texts(se, ec); | ||
| 1317 | if (err < 0) { | ||
| 1318 | dev_err(tplg->dev, "ASoC: could not create texts for %s\n", | ||
| 1319 | ec->hdr.name); | ||
| 1320 | goto err_se; | ||
| 1321 | } | ||
| 1322 | break; | ||
| 1323 | default: | ||
| 1324 | dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n", | ||
| 1325 | ec->hdr.ops.info, ec->hdr.name); | ||
| 1307 | goto err_se; | 1326 | goto err_se; |
| 1308 | } | 1327 | } |
| 1309 | /* fall through to create texts */ | 1328 | |
| 1310 | case SND_SOC_TPLG_CTL_ENUM: | 1329 | /* map io handlers */ |
| 1311 | case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: | 1330 | err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc[i], tplg); |
| 1312 | case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: | 1331 | if (err) { |
| 1313 | err = soc_tplg_denum_create_texts(se, ec); | 1332 | soc_control_err(tplg, &ec->hdr, ec->hdr.name); |
| 1333 | goto err_se; | ||
| 1334 | } | ||
| 1335 | |||
| 1336 | /* pass control to driver for optional further init */ | ||
| 1337 | err = soc_tplg_init_kcontrol(tplg, &kc[i], | ||
| 1338 | (struct snd_soc_tplg_ctl_hdr *)ec); | ||
| 1314 | if (err < 0) { | 1339 | if (err < 0) { |
| 1315 | dev_err(tplg->dev, "ASoC: could not create texts for %s\n", | 1340 | dev_err(tplg->dev, "ASoC: failed to init %s\n", |
| 1316 | ec->hdr.name); | 1341 | ec->hdr.name); |
| 1317 | goto err_se; | 1342 | goto err_se; |
| 1318 | } | 1343 | } |
| 1319 | break; | ||
| 1320 | default: | ||
| 1321 | dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n", | ||
| 1322 | ec->hdr.ops.info, ec->hdr.name); | ||
| 1323 | goto err_se; | ||
| 1324 | } | ||
| 1325 | 1344 | ||
| 1326 | /* map io handlers */ | 1345 | tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + |
| 1327 | err = soc_tplg_kcontrol_bind_io(&ec->hdr, kc, tplg); | 1346 | ec->priv.size); |
| 1328 | if (err) { | ||
| 1329 | soc_control_err(tplg, &ec->hdr, ec->hdr.name); | ||
| 1330 | goto err_se; | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | /* pass control to driver for optional further init */ | ||
| 1334 | err = soc_tplg_init_kcontrol(tplg, kc, | ||
| 1335 | (struct snd_soc_tplg_ctl_hdr *)ec); | ||
| 1336 | if (err < 0) { | ||
| 1337 | dev_err(tplg->dev, "ASoC: failed to init %s\n", | ||
| 1338 | ec->hdr.name); | ||
| 1339 | goto err_se; | ||
| 1340 | } | 1347 | } |
| 1341 | 1348 | ||
| 1342 | return kc; | 1349 | return kc; |
| 1343 | 1350 | ||
| 1344 | err_se: | 1351 | err_se: |
| 1345 | /* free values and texts */ | 1352 | for (; i >= 0; i--) { |
| 1346 | kfree(se->dobj.control.dvalues); | 1353 | /* free values and texts */ |
| 1347 | for (i = 0; i < ec->items; i++) | 1354 | se = (struct soc_enum *)kc[i].private_value; |
| 1348 | kfree(se->dobj.control.dtexts[i]); | 1355 | kfree(se->dobj.control.dvalues); |
| 1356 | for (j = 0; j < ec->items; j++) | ||
| 1357 | kfree(se->dobj.control.dtexts[j]); | ||
| 1349 | 1358 | ||
| 1350 | kfree(se); | 1359 | kfree(se); |
| 1360 | } | ||
| 1351 | err: | 1361 | err: |
| 1352 | kfree(kc); | 1362 | kfree(kc); |
| 1353 | 1363 | ||
| @@ -1499,9 +1509,10 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, | |||
| 1499 | case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: | 1509 | case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: |
| 1500 | case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: | 1510 | case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: |
| 1501 | template.dobj.widget.kcontrol_enum = 1; | 1511 | template.dobj.widget.kcontrol_enum = 1; |
| 1502 | template.num_kcontrols = 1; | 1512 | template.num_kcontrols = w->num_kcontrols; |
| 1503 | template.kcontrol_news = | 1513 | template.kcontrol_news = |
| 1504 | soc_tplg_dapm_widget_denum_create(tplg); | 1514 | soc_tplg_dapm_widget_denum_create(tplg, |
| 1515 | template.num_kcontrols); | ||
| 1505 | if (!template.kcontrol_news) { | 1516 | if (!template.kcontrol_news) { |
| 1506 | ret = -ENOMEM; | 1517 | ret = -ENOMEM; |
| 1507 | goto hdr_err; | 1518 | goto hdr_err; |
