aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/soc-dapm.c
diff options
context:
space:
mode:
authorStephen Warren <swarren@nvidia.com>2011-04-28 19:38:01 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2011-05-03 14:29:15 -0400
commitaf46800b9a3947724baeffb1a1649276971297c7 (patch)
tree19666a4fe66b232918e38c172afc03077129963c /sound/soc/soc-dapm.c
parentfafd2176f72148e83c64a1f818ff33fceed83d08 (diff)
ASoC: Implement mux control sharing
Control sharing is enabled when two widgets include pointers to the same kcontrol_new in their definition. Specifically: static const struct snd_kcontrol_new adcinput_mux = SOC_DAPM_ENUM("ADC Input", adcinput_enum); static const struct snd_soc_dapm_widget wm8903_dapm_widgets[] = { SND_SOC_DAPM_MUX("Left ADC Input", SND_SOC_NOPM, 0, 0, &adcinput_mux), SND_SOC_DAPM_MUX("Right ADC Input", SND_SOC_NOPM, 0, 0, &adcinput_mux), }; This is useful when a single register bit or field affects multiple muxes at once. The common case is to have separate control bits or fields for each mux (channel). An alternative way of looking at this is that the mux is a stereo (or even n-channel) mux, rather than independant mono muxes. Without this change, a separate kcontrol will be created for each DAPM_MUX. This has the following disadvantages: * Confuses the user/programmer with redundant controls that don't map to separate hardware. * When one of the controls is changed, ASoC fails to update the DAPM logic for paths solely affected by the other controls impacted by the same register bits. This causes some paths not to be correctly powered up or down. Prior to this change, to work around this, the user or programmer had to manually toggle all duplicate controls away from the intended setting, and then back to it. Control sharing implies that the control is named based on the kcontrol_new itself, not any of the widgets that are affected by it. Control sharing is implemented by: When creating kcontrols, if a kcontrol does not yet exist for a particular kcontrol_new, then a new kcontrol is created with a list of widgets containing just a single entry. This is the normal case. However, if a kcontrol does already exists for the given kcontrol_new, the current widget is simply added to that kcontrol's list of affected widgets. Signed-off-by: Stephen Warren <swarren@nvidia.com> Acked-by: Liam Girdwood <lrg@ti.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/soc-dapm.c')
-rw-r--r--sound/soc/soc-dapm.c106
1 files changed, 75 insertions, 31 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 79b836c1045d..456617e63789 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -324,6 +324,28 @@ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
324 return -ENODEV; 324 return -ENODEV;
325} 325}
326 326
327static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,
328 const struct snd_kcontrol_new *kcontrol_new,
329 struct snd_kcontrol **kcontrol)
330{
331 struct snd_soc_dapm_widget *w;
332 int i;
333
334 *kcontrol = NULL;
335
336 list_for_each_entry(w, &dapm->card->widgets, list) {
337 for (i = 0; i < w->num_kcontrols; i++) {
338 if (&w->kcontrol_news[i] == kcontrol_new) {
339 if (w->kcontrols)
340 *kcontrol = w->kcontrols[i];
341 return 1;
342 }
343 }
344 }
345
346 return 0;
347}
348
327/* create new dapm mixer control */ 349/* create new dapm mixer control */
328static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, 350static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
329 struct snd_soc_dapm_widget *w) 351 struct snd_soc_dapm_widget *w)
@@ -433,58 +455,80 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm,
433 struct snd_card *card = dapm->card->snd_card; 455 struct snd_card *card = dapm->card->snd_card;
434 const char *prefix; 456 const char *prefix;
435 size_t prefix_len; 457 size_t prefix_len;
436 int ret = 0; 458 int ret;
437 struct snd_soc_dapm_widget_list *wlist; 459 struct snd_soc_dapm_widget_list *wlist;
460 int shared, wlistentries;
438 size_t wlistsize; 461 size_t wlistsize;
462 char *name;
439 463
440 if (!w->num_kcontrols) { 464 if (w->num_kcontrols != 1) {
441 dev_err(dapm->dev, "asoc: mux %s has no controls\n", w->name); 465 dev_err(dapm->dev,
466 "asoc: mux %s has incorrect number of controls\n",
467 w->name);
442 return -EINVAL; 468 return -EINVAL;
443 } 469 }
444 470
471 shared = dapm_is_shared_kcontrol(dapm, &w->kcontrol_news[0],
472 &kcontrol);
473 if (kcontrol) {
474 wlist = kcontrol->private_data;
475 wlistentries = wlist->num_widgets + 1;
476 } else {
477 wlist = NULL;
478 wlistentries = 1;
479 }
445 wlistsize = sizeof(struct snd_soc_dapm_widget_list) + 480 wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
446 sizeof(struct snd_soc_dapm_widget *), 481 wlistentries * sizeof(struct snd_soc_dapm_widget *),
447 wlist = kzalloc(wlistsize, GFP_KERNEL); 482 wlist = krealloc(wlist, wlistsize, GFP_KERNEL);
448 if (wlist == NULL) { 483 if (wlist == NULL) {
449 dev_err(dapm->dev, 484 dev_err(dapm->dev,
450 "asoc: can't allocate widget list for %s\n", w->name); 485 "asoc: can't allocate widget list for %s\n", w->name);
451 return -ENOMEM; 486 return -ENOMEM;
452 } 487 }
453 wlist->num_widgets = 1; 488 wlist->num_widgets = wlistentries;
454 wlist->widgets[0] = w; 489 wlist->widgets[wlistentries - 1] = w;
455 490
456 if (dapm->codec) 491 if (!kcontrol) {
457 prefix = dapm->codec->name_prefix; 492 if (dapm->codec)
458 else 493 prefix = dapm->codec->name_prefix;
459 prefix = NULL; 494 else
460 495 prefix = NULL;
461 if (prefix) 496
462 prefix_len = strlen(prefix) + 1; 497 if (shared) {
463 else 498 name = w->kcontrol_news[0].name;
464 prefix_len = 0; 499 prefix_len = 0;
500 } else {
501 name = w->name;
502 if (prefix)
503 prefix_len = strlen(prefix) + 1;
504 else
505 prefix_len = 0;
506 }
465 507
466 /* The control will get a prefix from the control creation 508 /*
467 * process but we're also using the same prefix for widgets so 509 * The control will get a prefix from the control creation
468 * cut the prefix off the front of the widget name. 510 * process but we're also using the same prefix for widgets so
469 */ 511 * cut the prefix off the front of the widget name.
470 kcontrol = snd_soc_cnew(&w->kcontrol_news[0], wlist, 512 */
471 w->name + prefix_len, prefix); 513 kcontrol = snd_soc_cnew(&w->kcontrol_news[0], wlist,
472 ret = snd_ctl_add(card, kcontrol); 514 name + prefix_len, prefix);
515 ret = snd_ctl_add(card, kcontrol);
516 if (ret < 0) {
517 dev_err(dapm->dev,
518 "asoc: failed to add kcontrol %s\n", w->name);
519 kfree(wlist);
520 return ret;
521 }
522 }
473 523
474 if (ret < 0) 524 kcontrol->private_data = wlist;
475 goto err;
476 525
477 w->kcontrols[0] = kcontrol; 526 w->kcontrols[0] = kcontrol;
478 527
479 list_for_each_entry(path, &w->sources, list_sink) 528 list_for_each_entry(path, &w->sources, list_sink)
480 path->kcontrol = kcontrol; 529 path->kcontrol = kcontrol;
481 530
482 return ret; 531 return 0;
483
484err:
485 dev_err(dapm->dev, "asoc: failed to add kcontrol %s\n", w->name);
486 kfree(wlist);
487 return ret;
488} 532}
489 533
490/* create new dapm volume control */ 534/* create new dapm volume control */