[alsa-devel] [PATCH 0/4] Implement 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.
I have tested this on NVIDIA Tegra Seaboard, specifically with the WM8903 "ADC Input" control, and a locally-enabled digital mic setup.
I have not compiled nor tested any other platforms.
This patchset does not implement control sharing for dapm_new_mixer() yet. I can do so after discussion/review of this patch series, although note that I don't have anything to test the actual sharing on for such controls.
Stephen Warren (4): ASoC: s/w->kcontrols/w->kcontrol_news/g ASoC: Add w->kcontrols, and populate it ASoC: Store a list of widgets in a DAPM mux/mixer kcontrol ASoC: Implement mux control sharing
include/sound/soc-dapm.h | 75 +++++---- sound/soc/codecs/88pm860x-codec.c | 2 +- sound/soc/soc-dapm.c | 315 ++++++++++++++++++++++++++----------- 3 files changed, 266 insertions(+), 126 deletions(-)
A future change will modify struct snd_soc_dapm_widget to store the actual kcontrol pointers for each kcontrol_new in a field named kcontrols. Rename the existing kcontrols field to enable this.
Signed-off-by: Stephen Warren swarren@nvidia.com --- include/sound/soc-dapm.h | 68 ++++++++++++++++++------------------ sound/soc/codecs/88pm860x-codec.c | 2 +- sound/soc/soc-dapm.c | 28 ++++++++------- 3 files changed, 50 insertions(+), 48 deletions(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index d5f1b9a..3733e4d 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -39,30 +39,30 @@
/* codec domain */ #define SND_SOC_DAPM_VMID(wname) \ -{ .id = snd_soc_dapm_vmid, .name = wname, .kcontrols = NULL, \ +{ .id = snd_soc_dapm_vmid, .name = wname, .kcontrol_news = NULL, \ .num_kcontrols = 0}
/* platform domain */ #define SND_SOC_DAPM_INPUT(wname) \ -{ .id = snd_soc_dapm_input, .name = wname, .kcontrols = NULL, \ +{ .id = snd_soc_dapm_input, .name = wname, .kcontrol_news = NULL, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM } #define SND_SOC_DAPM_OUTPUT(wname) \ -{ .id = snd_soc_dapm_output, .name = wname, .kcontrols = NULL, \ +{ .id = snd_soc_dapm_output, .name = wname, .kcontrol_news = NULL, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM } #define SND_SOC_DAPM_MIC(wname, wevent) \ -{ .id = snd_soc_dapm_mic, .name = wname, .kcontrols = NULL, \ +{ .id = snd_soc_dapm_mic, .name = wname, .kcontrol_news = NULL, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD} #define SND_SOC_DAPM_HP(wname, wevent) \ -{ .id = snd_soc_dapm_hp, .name = wname, .kcontrols = NULL, \ +{ .id = snd_soc_dapm_hp, .name = wname, .kcontrol_news = NULL, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} #define SND_SOC_DAPM_SPK(wname, wevent) \ -{ .id = snd_soc_dapm_spk, .name = wname, .kcontrols = NULL, \ +{ .id = snd_soc_dapm_spk, .name = wname, .kcontrol_news = NULL, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} #define SND_SOC_DAPM_LINE(wname, wevent) \ -{ .id = snd_soc_dapm_line, .name = wname, .kcontrols = NULL, \ +{ .id = snd_soc_dapm_line, .name = wname, .kcontrol_news = NULL, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
@@ -70,91 +70,91 @@ #define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,\ wcontrols, wncontrols) \ { .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols} + .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} #define SND_SOC_DAPM_OUT_DRV(wname, wreg, wshift, winvert,\ wcontrols, wncontrols) \ { .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols} + .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} #define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \ wcontrols, wncontrols)\ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols} + .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} #define SND_SOC_DAPM_MIXER_NAMED_CTL(wname, wreg, wshift, winvert, \ wcontrols, wncontrols)\ { .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .kcontrols = wcontrols, \ + .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \ .num_kcontrols = wncontrols} #define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \ { .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = NULL, .num_kcontrols = 0} + .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0} #define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols) \ { .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} + .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1} #define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \ { .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} + .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1} #define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \ { .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} + .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1} #define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \ { .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .kcontrols = wcontrols, \ + .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \ .num_kcontrols = 1}
/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */ #define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\ wcontrols) \ { .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} + .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} #define SOC_MIXER_ARRAY(wname, wreg, wshift, winvert, \ wcontrols)\ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} + .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} #define SOC_MIXER_NAMED_CTL_ARRAY(wname, wreg, wshift, winvert, \ wcontrols)\ { .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .kcontrols = wcontrols, \ + .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \ .num_kcontrols = ARRAY_SIZE(wcontrols)}
/* path domain with event - event handler must return 0 for success */ #define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \ wncontrols, wevent, wflags) \ { .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \ + .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_OUT_DRV_E(wname, wreg, wshift, winvert, wcontrols, \ wncontrols, wevent, wflags) \ { .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \ + .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \ wncontrols, wevent, wflags) \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \ + .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_MIXER_NAMED_CTL_E(wname, wreg, wshift, winvert, \ wcontrols, wncontrols, wevent, wflags) \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, \ + .invert = winvert, .kcontrol_news = wcontrols, \ .num_kcontrols = wncontrols, .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \ { .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = NULL, .num_kcontrols = 0, \ + .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ { .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \ + .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ { .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \ + .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ { .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \ + .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \ .event = wevent, .event_flags = wflags}
/* additional sequencing control within an event type */ @@ -173,26 +173,26 @@ #define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ { .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ + .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ .event = wevent, .event_flags = wflags} #define SOC_MIXER_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ + .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ .event = wevent, .event_flags = wflags} #define SOC_MIXER_NAMED_CTL_E_ARRAY(wname, wreg, wshift, winvert, \ wcontrols, wevent, wflags) \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, \ + .invert = winvert, .kcontrol_news = wcontrols, \ .num_kcontrols = ARRAY_SIZE(wcontrols), .event = wevent, .event_flags = wflags}
/* events that are pre and post DAPM */ #define SND_SOC_DAPM_PRE(wname, wevent) \ -{ .id = snd_soc_dapm_pre, .name = wname, .kcontrols = NULL, \ +{ .id = snd_soc_dapm_pre, .name = wname, .kcontrol_news = NULL, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD} #define SND_SOC_DAPM_POST(wname, wevent) \ -{ .id = snd_soc_dapm_post, .name = wname, .kcontrols = NULL, \ +{ .id = snd_soc_dapm_post, .name = wname, .kcontrol_news = NULL, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD}
@@ -232,7 +232,7 @@
/* generic widgets */ #define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \ -{ .id = wid, .name = wname, .kcontrols = NULL, .num_kcontrols = 0, \ +{ .id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \ .reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \ .on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD} @@ -472,7 +472,7 @@ struct snd_soc_dapm_widget {
/* kcontrols that relate to this widget */ int num_kcontrols; - const struct snd_kcontrol_new *kcontrols; + const struct snd_kcontrol_new *kcontrol_news;
/* widget input and outputs */ struct list_head sources; diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index 06b6981..1924157 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -120,7 +120,7 @@ */ #define PM860X_DAPM_OUTPUT(wname, wevent) \ { .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \ - .shift = 0, .invert = 0, .kcontrols = NULL, \ + .shift = 0, .invert = 0, .kcontrol_news = NULL, \ .num_kcontrols = 0, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD, }
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 378f08a..401bb0f 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -187,7 +187,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, case snd_soc_dapm_mixer_named_ctl: { int val; struct soc_mixer_control *mc = (struct soc_mixer_control *) - w->kcontrols[i].private_value; + w->kcontrol_news[i].private_value; unsigned int reg = mc->reg; unsigned int shift = mc->shift; int max = mc->max; @@ -204,7 +204,8 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, } break; case snd_soc_dapm_mux: { - struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value; + struct soc_enum *e = (struct soc_enum *) + w->kcontrol_news[i].private_value; int val, item, bitmask;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1) @@ -220,7 +221,8 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, } break; case snd_soc_dapm_virt_mux: { - struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value; + struct soc_enum *e = (struct soc_enum *) + w->kcontrol_news[i].private_value;
p->connect = 0; /* since a virtual mux has no backing registers to @@ -235,7 +237,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, break; case snd_soc_dapm_value_mux: { struct soc_enum *e = (struct soc_enum *) - w->kcontrols[i].private_value; + w->kcontrol_news[i].private_value; int val, item;
val = snd_soc_read(w->codec, e->reg); @@ -310,11 +312,11 @@ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
/* search for mixer kcontrol */ for (i = 0; i < dest->num_kcontrols; i++) { - if (!strcmp(control_name, dest->kcontrols[i].name)) { + if (!strcmp(control_name, dest->kcontrol_news[i].name)) { list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &dest->sources); list_add(&path->list_source, &src->sinks); - path->name = dest->kcontrols[i].name; + path->name = dest->kcontrol_news[i].name; dapm_set_path_status(dest, path, i); return 0; } @@ -349,7 +351,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, list_for_each_entry(path, &w->sources, list_sink) {
/* mixer/mux paths name must match control name */ - if (path->name != (char*)w->kcontrols[i].name) + if (path->name != (char *)w->kcontrol_news[i].name) continue;
/* add dapm control with long name. @@ -358,7 +360,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, * for dapm_mixer_named_ctl this is simply the * kcontrol name. */ - name_len = strlen(w->kcontrols[i].name) + 1; + name_len = strlen(w->kcontrol_news[i].name) + 1; if (w->id != snd_soc_dapm_mixer_named_ctl) name_len += 1 + strlen(w->name);
@@ -377,17 +379,17 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, */ snprintf(path->long_name, name_len, "%s %s", w->name + prefix_len, - w->kcontrols[i].name); + w->kcontrol_news[i].name); break; case snd_soc_dapm_mixer_named_ctl: snprintf(path->long_name, name_len, "%s", - w->kcontrols[i].name); + w->kcontrol_news[i].name); break; }
path->long_name[name_len - 1] = '\0';
- path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w, + path->kcontrol = snd_soc_cnew(&w->kcontrol_news[i], w, path->long_name, prefix); ret = snd_ctl_add(card, path->kcontrol); if (ret < 0) { @@ -433,7 +435,7 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm, * process but we're also using the same prefix for widgets so * cut the prefix off the front of the widget name. */ - kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name + prefix_len, + kcontrol = snd_soc_cnew(&w->kcontrol_news[0], w, w->name + prefix_len, prefix); ret = snd_ctl_add(card, kcontrol);
@@ -1623,7 +1625,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_virt_mux: case snd_soc_dapm_value_mux: ret = dapm_connect_mux(dapm, wsource, wsink, path, control, - &wsink->kcontrols[0]); + &wsink->kcontrol_news[0]); if (ret != 0) goto err; break;
Future changes will need reference to the kcontrol created for a given kcontrol_new. Store the created kcontrol values now.
Signed-off-by: Stephen Warren swarren@nvidia.com --- include/sound/soc-dapm.h | 1 + sound/soc/soc-dapm.c | 12 ++++++++++++ 2 files changed, 13 insertions(+), 0 deletions(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3733e4d..8c4b90b 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -473,6 +473,7 @@ struct snd_soc_dapm_widget { /* kcontrols that relate to this widget */ int num_kcontrols; const struct snd_kcontrol_new *kcontrol_news; + struct snd_kcontrol **kcontrols;
/* widget input and outputs */ struct list_head sources; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 401bb0f..24d19a0 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -400,6 +400,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, path->long_name = NULL; return ret; } + w->kcontrols[i] = path->kcontrol; } } return ret; @@ -442,6 +443,8 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm, if (ret < 0) goto err;
+ w->kcontrols[0] = kcontrol; + list_for_each_entry(path, &w->sources, list_sink) path->kcontrol = kcontrol;
@@ -1455,6 +1458,7 @@ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm) kfree(p->long_name); kfree(p); } + kfree(w->kcontrols); kfree(w->name); kfree(w); } @@ -1705,6 +1709,14 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) if (w->new) continue;
+ if (w->num_kcontrols) { + w->kcontrols = kzalloc(w->num_kcontrols * + sizeof(struct snd_kcontrol *), + GFP_KERNEL); + if (!w->kcontrols) + return -ENOMEM; + } + switch(w->id) { case snd_soc_dapm_switch: case snd_soc_dapm_mixer:
A future change will allow multiple widgets to be affected by the same control. For example, a single register bit that controls separate muxes in both the L and R audio paths.
This change updates the code that handles relevant controls to be able to iterate over a list of affected widgets. Note that only the put functions need significant modification to implement the iteration; the get functions do not need to iterate, nor unify the results, since all affected widgets reference the same kcontrol.
When creating the list of widgets, always create a 1-sized list, since the control sharing is not implemented in this change.
Signed-off-by: Stephen Warren swarren@nvidia.com --- include/sound/soc-dapm.h | 6 ++ sound/soc/soc-dapm.c | 187 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 137 insertions(+), 56 deletions(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 8c4b90b..2a32b14 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -517,4 +517,10 @@ struct snd_soc_dapm_context { #endif };
+/* A list of widgets associated with an object, typically a snd_kcontrol */ +struct snd_soc_dapm_widget_list { + int num_widgets; + struct snd_soc_dapm_widget *widgets[0]; +}; + #endif diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 24d19a0..e140618 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -333,6 +333,8 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_path *path; struct snd_card *card = dapm->card->snd_card; const char *prefix; + struct snd_soc_dapm_widget_list *wlist; + size_t wlistsize;
if (dapm->codec) prefix = dapm->codec->name_prefix; @@ -354,6 +356,18 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, if (path->name != (char *)w->kcontrol_news[i].name) continue;
+ wlistsize = sizeof(struct snd_soc_dapm_widget_list) + + sizeof(struct snd_soc_dapm_widget *), + wlist = kzalloc(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; + /* add dapm control with long name. * for dapm_mixer this is the concatenation of the * mixer and kcontrol name. @@ -366,8 +380,10 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
path->long_name = kmalloc(name_len, GFP_KERNEL);
- if (path->long_name == NULL) + if (path->long_name == NULL) { + kfree(wlist); return -ENOMEM; + }
switch (w->id) { default: @@ -389,13 +405,15 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
path->long_name[name_len - 1] = '\0';
- path->kcontrol = snd_soc_cnew(&w->kcontrol_news[i], w, - path->long_name, prefix); + path->kcontrol = snd_soc_cnew(&w->kcontrol_news[i], + wlist, path->long_name, + prefix); ret = snd_ctl_add(card, path->kcontrol); if (ret < 0) { dev_err(dapm->dev, "asoc: failed to add dapm kcontrol %s: %d\n", path->long_name, ret); + kfree(wlist); kfree(path->long_name); path->long_name = NULL; return ret; @@ -416,12 +434,25 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm, const char *prefix; size_t prefix_len; int ret = 0; + struct snd_soc_dapm_widget_list *wlist; + size_t wlistsize;
if (!w->num_kcontrols) { dev_err(dapm->dev, "asoc: mux %s has no controls\n", w->name); return -EINVAL; }
+ wlistsize = sizeof(struct snd_soc_dapm_widget_list) + + sizeof(struct snd_soc_dapm_widget *), + wlist = kzalloc(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; + if (dapm->codec) prefix = dapm->codec->name_prefix; else @@ -436,8 +467,8 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm, * process but we're also using the same prefix for widgets so * cut the prefix off the front of the widget name. */ - kcontrol = snd_soc_cnew(&w->kcontrol_news[0], w, w->name + prefix_len, - prefix); + kcontrol = snd_soc_cnew(&w->kcontrol_news[0], wlist, + w->name + prefix_len, prefix); ret = snd_ctl_add(card, kcontrol);
if (ret < 0) @@ -452,6 +483,7 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm,
err: dev_err(dapm->dev, "asoc: failed to add kcontrol %s\n", w->name); + kfree(wlist); return ret; }
@@ -1791,7 +1823,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -1830,7 +1863,9 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw); int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = widget->codec; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -1841,6 +1876,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, unsigned int val; int connect, change; struct snd_soc_dapm_update update; + int wi;
val = (ucontrol->value.integer.value[0] & mask);
@@ -1849,31 +1885,36 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, mask = mask << shift; val = val << shift;
- mutex_lock(&widget->codec->mutex); - widget->value = val; + if (val) + /* new connection */ + connect = invert ? 0 : 1; + else + /* old connection must be powered down */ + connect = invert ? 1 : 0; + + mutex_lock(&codec->mutex);
change = snd_soc_test_bits(widget->codec, reg, mask, val); if (change) { - if (val) - /* new connection */ - connect = invert ? 0:1; - else - /* old connection must be powered down */ - connect = invert ? 1:0; + for (wi = 0; wi < wlist->num_widgets; wi++) { + widget = wlist->widgets[wi];
- update.kcontrol = kcontrol; - update.widget = widget; - update.reg = reg; - update.mask = mask; - update.val = val; - widget->dapm->update = &update; + widget->value = val;
- dapm_mixer_update_power(widget, kcontrol, connect); + update.kcontrol = kcontrol; + update.widget = widget; + update.reg = reg; + update.mask = mask; + update.val = val; + widget->dapm->update = &update;
- widget->dapm->update = NULL; + dapm_mixer_update_power(widget, kcontrol, connect); + + widget->dapm->update = NULL; + } }
- mutex_unlock(&widget->codec->mutex); + mutex_unlock(&codec->mutex); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); @@ -1890,7 +1931,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, bitmask;
@@ -1918,11 +1960,14 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double); int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = widget->codec; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; unsigned int mask, bitmask; struct snd_soc_dapm_update update; + int wi;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1) ; @@ -1938,22 +1983,29 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mask |= (bitmask - 1) << e->shift_r; }
- mutex_lock(&widget->codec->mutex); - widget->value = val; + mutex_lock(&codec->mutex); + change = snd_soc_test_bits(widget->codec, e->reg, mask, val); + if (change) { + for (wi = 0; wi < wlist->num_widgets; wi++) { + widget = wlist->widgets[wi];
- update.kcontrol = kcontrol; - update.widget = widget; - update.reg = e->reg; - update.mask = mask; - update.val = val; - widget->dapm->update = &update; + widget->value = val;
- dapm_mux_update_power(widget, kcontrol, change, mux, e); + update.kcontrol = kcontrol; + update.widget = widget; + update.reg = e->reg; + update.mask = mask; + update.val = val; + widget->dapm->update = &update;
- widget->dapm->update = NULL; + dapm_mux_update_power(widget, kcontrol, change, mux, e);
- mutex_unlock(&widget->codec->mutex); + widget->dapm->update = NULL; + } + } + + mutex_unlock(&codec->mutex); return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); @@ -1968,7 +2020,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0];
ucontrol->value.enumerated.item[0] = widget->value;
@@ -1986,22 +2039,33 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt); int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = widget->codec; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int change; int ret = 0; + int wi;
if (ucontrol->value.enumerated.item[0] >= e->max) return -EINVAL;
- mutex_lock(&widget->codec->mutex); + mutex_lock(&codec->mutex);
change = widget->value != ucontrol->value.enumerated.item[0]; - widget->value = ucontrol->value.enumerated.item[0]; - dapm_mux_update_power(widget, kcontrol, change, widget->value, e); + if (change) { + for (wi = 0; wi < wlist->num_widgets; wi++) { + widget = wlist->widgets[wi]; + + widget->value = ucontrol->value.enumerated.item[0]; + + dapm_mux_update_power(widget, kcontrol, change, + widget->value, e); + } + }
- mutex_unlock(&widget->codec->mutex); + mutex_unlock(&codec->mutex); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); @@ -2022,7 +2086,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int reg_val, val, mux;
@@ -2062,11 +2127,14 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double); int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = widget->codec; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; unsigned int mask; struct snd_soc_dapm_update update; + int wi;
if (ucontrol->value.enumerated.item[0] > e->max - 1) return -EINVAL; @@ -2080,22 +2148,29 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, mask |= e->mask << e->shift_r; }
- mutex_lock(&widget->codec->mutex); - widget->value = val; + mutex_lock(&codec->mutex); + change = snd_soc_test_bits(widget->codec, e->reg, mask, val); + if (change) { + for (wi = 0; wi < wlist->num_widgets; wi++) { + widget = wlist->widgets[wi];
- update.kcontrol = kcontrol; - update.widget = widget; - update.reg = e->reg; - update.mask = mask; - update.val = val; - widget->dapm->update = &update; + widget->value = val;
- dapm_mux_update_power(widget, kcontrol, change, mux, e); + update.kcontrol = kcontrol; + update.widget = widget; + update.reg = e->reg; + update.mask = mask; + update.val = val; + widget->dapm->update = &update;
- widget->dapm->update = NULL; + dapm_mux_update_power(widget, kcontrol, change, mux, e);
- mutex_unlock(&widget->codec->mutex); + widget->dapm->update = NULL; + } + } + + mutex_unlock(&codec->mutex); return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double);
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 --- sound/soc/soc-dapm.c | 106 +++++++++++++++++++++++++++++++++++--------------- 1 files changed, 75 insertions(+), 31 deletions(-)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index e140618..77e6934 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, return -ENODEV; }
+static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm, + const struct snd_kcontrol_new *kcontrol_new, + struct snd_kcontrol **kcontrol) +{ + struct snd_soc_dapm_widget *w; + int i; + + *kcontrol = NULL; + + list_for_each_entry(w, &dapm->card->widgets, list) { + for (i = 0; i < w->num_kcontrols; i++) { + if (&w->kcontrol_news[i] == kcontrol_new) { + if (w->kcontrols) + *kcontrol = w->kcontrols[i]; + return 1; + } + } + } + + return 0; +} + /* create new dapm mixer control */ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *w) @@ -433,58 +455,80 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm, struct snd_card *card = dapm->card->snd_card; const char *prefix; size_t prefix_len; - int ret = 0; + int ret; struct snd_soc_dapm_widget_list *wlist; + int shared, wlistentries; size_t wlistsize; + char *name;
- if (!w->num_kcontrols) { - dev_err(dapm->dev, "asoc: mux %s has no controls\n", w->name); + if (w->num_kcontrols != 1) { + dev_err(dapm->dev, + "asoc: mux %s has incorrect number of controls\n", + w->name); return -EINVAL; }
+ shared = dapm_is_shared_kcontrol(dapm, &w->kcontrol_news[0], + &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; - - if (dapm->codec) - prefix = dapm->codec->name_prefix; - else - prefix = NULL; + wlist->num_widgets = wlistentries; + wlist->widgets[wlistentries - 1] = w;
- if (prefix) - prefix_len = strlen(prefix) + 1; - else - prefix_len = 0; + if (!kcontrol) { + if (dapm->codec) + prefix = dapm->codec->name_prefix; + else + prefix = NULL; + + if (shared) { + name = w->kcontrol_news[0].name; + prefix_len = 0; + } else { + name = w->name; + if (prefix) + prefix_len = strlen(prefix) + 1; + else + prefix_len = 0; + }
- /* The control will get a prefix from the control creation - * process but we're also using the same prefix for widgets so - * cut the prefix off the front of the widget name. - */ - kcontrol = snd_soc_cnew(&w->kcontrol_news[0], wlist, - w->name + prefix_len, prefix); - ret = snd_ctl_add(card, kcontrol); + /* + * The control will get a prefix from the control creation + * process but we're also using the same prefix for widgets so + * cut the prefix off the front of the widget name. + */ + kcontrol = snd_soc_cnew(&w->kcontrol_news[0], wlist, + name + prefix_len, prefix); + ret = snd_ctl_add(card, kcontrol); + if (ret < 0) { + dev_err(dapm->dev, + "asoc: failed to add kcontrol %s\n", w->name); + kfree(wlist); + return ret; + } + }
- if (ret < 0) - goto err; + kcontrol->private_data = wlist;
w->kcontrols[0] = kcontrol;
list_for_each_entry(path, &w->sources, list_sink) path->kcontrol = kcontrol;
- return ret; - -err: - dev_err(dapm->dev, "asoc: failed to add kcontrol %s\n", w->name); - kfree(wlist); - return ret; + return 0; }
/* create new dapm volume control */
On Thu, 2011-04-28 at 17:37 -0600, Stephen Warren wrote:
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.
I have tested this on NVIDIA Tegra Seaboard, specifically with the WM8903 "ADC Input" control, and a locally-enabled digital mic setup.
I have not compiled nor tested any other platforms.
This patchset does not implement control sharing for dapm_new_mixer() yet. I can do so after discussion/review of this patch series, although note that I don't have anything to test the actual sharing on for such controls.
Stephen Warren (4): ASoC: s/w->kcontrols/w->kcontrol_news/g ASoC: Add w->kcontrols, and populate it ASoC: Store a list of widgets in a DAPM mux/mixer kcontrol ASoC: Implement mux control sharing
include/sound/soc-dapm.h | 75 +++++---- sound/soc/codecs/88pm860x-codec.c | 2 +- sound/soc/soc-dapm.c | 315 ++++++++++++++++++++++++++----------- 3 files changed, 266 insertions(+), 126 deletions(-)
All
Acked-by: Liam Girdwood lrg@ti.com
participants (3)
-
Liam Girdwood
-
Mark Brown
-
Stephen Warren