[alsa-devel] [PATCH] ASoC: Implement mixer control sharing

Stephen Warren swarren at wwwdotorg.org
Fri Mar 29 00:53:10 CET 2013


From: Stephen Warren <swarren at nvidia.com>

This is the equivalent of commit af46800 "ASoC: Implement mux control
sharing", but applied to mixers instead of muxes.

This allows a single control to affect multiple mixer widgets at once,
which is useful when there is a single set of register bits that affects
both multiple mixers in HW, for example both the L and R mixers of a
stereo path.

Without this, you either:

1) End up with multiple controls that affect the same register bits, but
whose DAPM state falls out of sync with HW, since the DAPM state is only
updated for the specific control that is modified, and not for other
paths that are affected only by the other control.

2) False paths through DAPM, since you end up merging unconnected stereo
paths together into a single widget which hosts the single control, and
then branching back out again, thus conjoining the enable states of the
two input paths.

Signed-off-by: Stephen Warren <swarren at nvidia.com>
---
Mark, the goto here might be considered icky. It avoids heavy indent
levels. Should I separate out the control creation part of dapm_new_mixer()
into a separate function to avoid this?

Also, this commit has the potential for user-space impact, since control
names could change for existing drivers. However, in this case I suspect
the affected controls wouldn't have worked very well anyway, since they'd
be affected by one of the problems I mention in the commit description
above anyway, so I doubt this will be a practical problem.

FYI, this feature for muxes will be useful for the rt5640.c driver, which
has a single register for the HPO MUX, which separately affects both a L
and R path to the HP outputs.

 sound/soc/soc-dapm.c |   57 +++++++++++++++++++++++++++++++-------------------
 1 file changed, 36 insertions(+), 21 deletions(-)

diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 33acd8b..9a87aa5 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -508,13 +508,15 @@ static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,
 static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
 {
 	struct snd_soc_dapm_context *dapm = w->dapm;
-	int i, ret = 0;
+	int i, ret;
 	size_t name_len, prefix_len;
 	struct snd_soc_dapm_path *path;
 	struct snd_card *card = dapm->card->snd_card;
 	const char *prefix;
 	struct snd_soc_dapm_widget_list *wlist;
+	int shared, wlistentries;
 	size_t wlistsize;
+	struct snd_kcontrol *kcontrol;
 
 	if (dapm->codec)
 		prefix = dapm->codec->name_prefix;
@@ -531,7 +533,6 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
 
 		/* match name */
 		list_for_each_entry(path, &w->sources, list_sink) {
-
 			/* mixer/mux paths name must match control name */
 			if (path->name != (char *)w->kcontrol_news[i].name)
 				continue;
@@ -541,26 +542,41 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
 				continue;
 			}
 
+			shared = dapm_is_shared_kcontrol(dapm, w,
+					&w->kcontrol_news[i], &kcontrol);
+
+			if (kcontrol) {
+				wlist = kcontrol->private_data;
+				wlistentries = wlist->num_widgets + 1;
+			} else {
+				wlist = NULL;
+				wlistentries = 1;
+			}
+
 			wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
-				    sizeof(struct snd_soc_dapm_widget *),
-			wlist = kzalloc(wlistsize, GFP_KERNEL);
+					wlistentries *
+					sizeof(struct snd_soc_dapm_widget *),
+			wlist = krealloc(wlist, wlistsize, GFP_KERNEL);
 			if (wlist == NULL) {
 				dev_err(dapm->dev,
 					"ASoC: can't allocate widget list for %s\n",
 					w->name);
 				return -ENOMEM;
 			}
-			wlist->num_widgets = 1;
-			wlist->widgets[0] = w;
+			wlist->num_widgets = wlistentries;
+			wlist->widgets[wlistentries - 1] = w;
+
+			if (kcontrol)
+				goto skip_new_control;
 
 			/* add dapm control with long name.
 			 * for dapm_mixer this is the concatenation of the
 			 * mixer and kcontrol name.
-			 * for dapm_mixer_named_ctl this is simply the
-			 * kcontrol name.
+			 * for dapm_mixer_named_ctl or shared controls, this
+			 * is simply the kcontrol name.
 			 */
 			name_len = strlen(w->kcontrol_news[i].name) + 1;
-			if (w->id != snd_soc_dapm_mixer_named_ctl)
+			if (!shared && w->id != snd_soc_dapm_mixer_named_ctl)
 				name_len += 1 + strlen(w->name);
 
 			path->long_name = kmalloc(name_len, GFP_KERNEL);
@@ -570,8 +586,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
 				return -ENOMEM;
 			}
 
-			switch (w->id) {
-			default:
+			if (!shared && w->id != snd_soc_dapm_mixer_named_ctl)
 				/* The control will get a prefix from
 				 * the control creation process but
 				 * we're also using the same prefix
@@ -581,19 +596,15 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
 				snprintf((char *)path->long_name, name_len,
 					 "%s %s", w->name + prefix_len,
 					 w->kcontrol_news[i].name);
-				break;
-			case snd_soc_dapm_mixer_named_ctl:
+			else
 				snprintf((char *)path->long_name, name_len,
 					 "%s", w->kcontrol_news[i].name);
-				break;
-			}
 
 			((char *)path->long_name)[name_len - 1] = '\0';
 
-			path->kcontrol = snd_soc_cnew(&w->kcontrol_news[i],
-						      wlist, path->long_name,
-						      prefix);
-			ret = snd_ctl_add(card, path->kcontrol);
+			kcontrol = snd_soc_cnew(&w->kcontrol_news[i], wlist,
+						path->long_name, prefix);
+			ret = snd_ctl_add(card, kcontrol);
 			if (ret < 0) {
 				dev_err(dapm->dev, "ASoC: failed to add widget"
 					" %s dapm kcontrol %s: %d\n",
@@ -603,10 +614,14 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
 				path->long_name = NULL;
 				return ret;
 			}
-			w->kcontrols[i] = path->kcontrol;
+
+skip_new_control:
+			path->kcontrol = kcontrol;
+			kcontrol->private_data = wlist;
+			w->kcontrols[i] = kcontrol;
 		}
 	}
-	return ret;
+	return 0;
 }
 
 /* create new dapm mux control */
-- 
1.7.10.4



More information about the Alsa-devel mailing list