[alsa-devel] [PATCH 1/3] ASoC: wm_hubs: Special case headphones for digital paths in more use cases
The optimisations which we can do with caching the headphone DCS result in wm_hubs have only been enabled in cases where class W is enabled. However, there are more use cases which can benefit from the cache, especially with WM8994 series devices with their more advanced digital routing.
Rather than keying off the class W information from the CODECs have a check in wm_hubs for a suitable path and use that to determine if we can deploy our headphone optimisations.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/wm8993.c | 2 -- sound/soc/codecs/wm8994.c | 4 +--- sound/soc/codecs/wm_hubs.c | 48 ++++++++++++++++++++++++++++++++++++++------ sound/soc/codecs/wm_hubs.h | 5 ++--- 4 files changed, 45 insertions(+), 14 deletions(-)
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index d256a93..8bb0059 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -852,7 +852,6 @@ static int class_w_put(struct snd_kcontrol *kcontrol, 0); } wm8993->class_w_users++; - wm8993->hubs_data.class_w = true; }
/* Implement the change */ @@ -869,7 +868,6 @@ static int class_w_put(struct snd_kcontrol *kcontrol, WM8993_CP_DYN_V); } wm8993->class_w_users--; - wm8993->hubs_data.class_w = false; }
dev_dbg(codec->dev, "Indirect DAC use count now %d\n", diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 587062b..5b7bb10 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -998,13 +998,11 @@ static void wm8994_update_class_w(struct snd_soc_codec *codec) WM8994_CP_DYN_PWR | WM8994_CP_DYN_SRC_SEL_MASK, source | WM8994_CP_DYN_PWR); - wm8994->hubs.class_w = true;
} else { dev_dbg(codec->dev, "Class W disabled\n"); snd_soc_update_bits(codec, WM8994_CLASS_W_1, WM8994_CP_DYN_PWR, 0); - wm8994->hubs.class_w = false; } }
@@ -3777,7 +3775,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm8994->hubs.dcs_readback_mode = 2; wm8994->hubs.no_series_update = 1; wm8994->hubs.hp_startup_mode = 1; - wm8994->hubs.no_cache_class_w = true; + wm8994->hubs.no_cache_dac_hp_direct = true; wm8994->fll_byp = true;
switch (wm8994->revision) { diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 6c028c4..df44083 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -109,6 +109,42 @@ irqreturn_t wm_hubs_dcs_done(int irq, void *data) } EXPORT_SYMBOL_GPL(wm_hubs_dcs_done);
+static bool wm_hubs_dac_hp_direct(struct snd_soc_codec *codec) +{ + int reg; + + /* If we're going via the mixer we'll need to do additional checks */ + reg = snd_soc_read(codec, WM8993_OUTPUT_MIXER1); + if (!(reg & WM8993_DACL_TO_HPOUT1L)) { + if (reg & ~WM8993_DACL_TO_MIXOUTL) { + dev_vdbg(codec->dev, "Analogue paths connected: %x\n", + reg & ~WM8993_DACL_TO_HPOUT1L); + return false; + } else { + dev_vdbg(codec->dev, "HPL connected to mixer\n"); + return false; + } + } else { + dev_vdbg(codec->dev, "HPL connected to DAC\n"); + } + + reg = snd_soc_read(codec, WM8993_OUTPUT_MIXER2); + if (!(reg & WM8993_DACR_TO_HPOUT1R)) { + if (reg & ~WM8993_DACR_TO_MIXOUTR) { + dev_vdbg(codec->dev, "Analogue paths connected: %x\n", + reg & ~WM8993_DACR_TO_HPOUT1R); + return false; + } else { + dev_vdbg(codec->dev, "HPR connected to mixer\n"); + return false; + } + } else { + dev_vdbg(codec->dev, "HPR connected to DAC\n"); + } + + return true; +} + /* * Startup calibration of the DC servo */ @@ -129,10 +165,10 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
/* If we're using a digital only path and have a previously * callibrated DC servo offset stored then use that. */ - if (hubs->class_w && hubs->class_w_dcs) { + if (wm_hubs_dac_hp_direct(codec) && hubs->dac_hp_direct_dcs) { dev_dbg(codec->dev, "Using cached DC servo offset %x\n", - hubs->class_w_dcs); - snd_soc_write(codec, dcs_reg, hubs->class_w_dcs); + hubs->dac_hp_direct_dcs); + snd_soc_write(codec, dcs_reg, hubs->dac_hp_direct_dcs); wait_for_dc_servo(codec, WM8993_DCS_TRIG_DAC_WR_0 | WM8993_DCS_TRIG_DAC_WR_1); @@ -207,8 +243,8 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
/* Save the callibrated offset if we're in class W mode and * therefore don't have any analogue signal mixed in. */ - if (hubs->class_w && !hubs->no_cache_class_w) - hubs->class_w_dcs = dcs_cfg; + if (wm_hubs_dac_hp_direct(codec) && !hubs->no_cache_dac_hp_direct) + hubs->dac_hp_direct_dcs = dcs_cfg; }
/* @@ -224,7 +260,7 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol, ret = snd_soc_put_volsw(kcontrol, ucontrol);
/* Updating the analogue gains invalidates the DC servo cache */ - hubs->class_w_dcs = 0; + hubs->dac_hp_direct_dcs = 0;
/* If we're applying an offset correction then updating the * callibration would be likely to introduce further offsets. */ diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h index 5705276..8bb9f1b 100644 --- a/sound/soc/codecs/wm_hubs.h +++ b/sound/soc/codecs/wm_hubs.h @@ -30,9 +30,8 @@ struct wm_hubs_data { int series_startup; int no_series_update;
- bool no_cache_class_w; - bool class_w; - u16 class_w_dcs; + bool no_cache_dac_hp_direct; + u16 dac_hp_direct_dcs;
bool lineout1_se; bool lineout1n_ena;
Since the analogue portions of the checks for class W are the same over all the devices factor out these checks into wm_hubs and while we're at it also use wm_hubs_dac_hp_direct() to enable class W optimisations on more paths.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/wm8993.c | 84 +--------------------------------------- sound/soc/codecs/wm8994.c | 91 ++++++++------------------------------------ sound/soc/codecs/wm_hubs.c | 59 ++++++++++++++++++++++++++++ sound/soc/codecs/wm_hubs.h | 6 +++ 4 files changed, 82 insertions(+), 158 deletions(-)
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 8bb0059..36acfcc 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -218,7 +218,6 @@ struct wm8993_priv { unsigned int sysclk_rate; unsigned int fs; unsigned int bclk; - int class_w_users; unsigned int fll_fref; unsigned int fll_fout; int fll_src; @@ -824,82 +823,6 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w, return 0; }
-/* - * When used with DAC outputs only the WM8993 charge pump supports - * operation in class W mode, providing very low power consumption - * when used with digital sources. Enable and disable this mode - * automatically depending on the mixer configuration. - * - * Currently the only supported paths are the direct DAC->headphone - * paths (which provide minimum power consumption anyway). - */ -static int class_w_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - 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 wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec); - int ret; - - /* Turn it off if we're using the main output mixer */ - if (ucontrol->value.integer.value[0] == 0) { - if (wm8993->class_w_users == 0) { - dev_dbg(codec->dev, "Disabling Class W\n"); - snd_soc_update_bits(codec, WM8993_CLASS_W_0, - WM8993_CP_DYN_FREQ | - WM8993_CP_DYN_V, - 0); - } - wm8993->class_w_users++; - } - - /* Implement the change */ - ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); - - /* Enable it if we're using the direct DAC path */ - if (ucontrol->value.integer.value[0] == 1) { - if (wm8993->class_w_users == 1) { - dev_dbg(codec->dev, "Enabling Class W\n"); - snd_soc_update_bits(codec, WM8993_CLASS_W_0, - WM8993_CP_DYN_FREQ | - WM8993_CP_DYN_V, - WM8993_CP_DYN_FREQ | - WM8993_CP_DYN_V); - } - wm8993->class_w_users--; - } - - dev_dbg(codec->dev, "Indirect DAC use count now %d\n", - wm8993->class_w_users); - - return ret; -} - -#define SOC_DAPM_ENUM_W(xname, xenum) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = snd_soc_info_enum_double, \ - .get = snd_soc_dapm_get_enum_double, \ - .put = class_w_put, \ - .private_value = (unsigned long)&xenum } - -static const char *hp_mux_text[] = { - "Mixer", - "DAC", -}; - -static const struct soc_enum hpl_enum = - SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER1, 8, 2, hp_mux_text); - -static const struct snd_kcontrol_new hpl_mux = - SOC_DAPM_ENUM_W("Left Headphone Mux", hpl_enum); - -static const struct soc_enum hpr_enum = - SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER2, 8, 2, hp_mux_text); - -static const struct snd_kcontrol_new hpr_mux = - SOC_DAPM_ENUM_W("Right Headphone Mux", hpr_enum); - static const struct snd_kcontrol_new left_speaker_mixer[] = { SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 7, 1, 0), SOC_DAPM_SINGLE("IN1LP Switch", WM8993_SPEAKER_MIXER, 5, 1, 0), @@ -986,8 +909,8 @@ SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &sidetoner_mux), SND_SOC_DAPM_DAC("DACL", NULL, WM8993_POWER_MANAGEMENT_3, 1, 0), SND_SOC_DAPM_DAC("DACR", NULL, WM8993_POWER_MANAGEMENT_3, 0, 0),
-SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux), -SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux), +SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux), +SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux),
SND_SOC_DAPM_MIXER("SPKL", WM8993_POWER_MANAGEMENT_3, 8, 0, left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)), @@ -1577,9 +1500,6 @@ static int wm8993_probe(struct snd_soc_codec *codec) return ret; }
- /* By default we're using the output mixers */ - wm8993->class_w_users = 2; - /* Latch volume update bits and default ZC on */ snd_soc_update_bits(codec, WM8993_RIGHT_DAC_DIGITAL_VOLUME, WM8993_DAC_VU, WM8993_DAC_VU); diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 5b7bb10..b3b2d5f 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -945,27 +945,12 @@ static int vmid_event(struct snd_soc_dapm_widget *w, return 0; }
-static void wm8994_update_class_w(struct snd_soc_codec *codec) +static bool wm8994_check_class_w_digital(struct snd_soc_codec *codec) { - struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - int enable = 1; int source = 0; /* GCC flow analysis can't track enable */ int reg, reg_r;
- /* Only support direct DAC->headphone paths */ - reg = snd_soc_read(codec, WM8994_OUTPUT_MIXER_1); - if (!(reg & WM8994_DAC1L_TO_HPOUT1L)) { - dev_vdbg(codec->dev, "HPL connected to output mixer\n"); - enable = 0; - } - - reg = snd_soc_read(codec, WM8994_OUTPUT_MIXER_2); - if (!(reg & WM8994_DAC1R_TO_HPOUT1R)) { - dev_vdbg(codec->dev, "HPR connected to output mixer\n"); - enable = 0; - } - - /* We also need the same setting for L/R and only one path */ + /* We also need the same AIF source for L/R and only one path */ reg = snd_soc_read(codec, WM8994_DAC1_LEFT_MIXER_ROUTING); switch (reg) { case WM8994_AIF2DACL_TO_DAC1L: @@ -982,28 +967,20 @@ static void wm8994_update_class_w(struct snd_soc_codec *codec) break; default: dev_vdbg(codec->dev, "DAC mixer setting: %x\n", reg); - enable = 0; - break; + return false; }
reg_r = snd_soc_read(codec, WM8994_DAC1_RIGHT_MIXER_ROUTING); if (reg_r != reg) { dev_vdbg(codec->dev, "Left and right DAC mixers different\n"); - enable = 0; + return false; }
- if (enable) { - dev_dbg(codec->dev, "Class W enabled\n"); - snd_soc_update_bits(codec, WM8994_CLASS_W_1, - WM8994_CP_DYN_PWR | - WM8994_CP_DYN_SRC_SEL_MASK, - source | WM8994_CP_DYN_PWR); + /* Set the source up */ + snd_soc_update_bits(codec, WM8994_CLASS_W_1, + WM8994_CP_DYN_SRC_SEL_MASK, source);
- } else { - dev_dbg(codec->dev, "Class W disabled\n"); - snd_soc_update_bits(codec, WM8994_CLASS_W_1, - WM8994_CP_DYN_PWR, 0); - } + return true; }
static int aif1clk_ev(struct snd_soc_dapm_widget *w, @@ -1286,45 +1263,6 @@ static int dac_ev(struct snd_soc_dapm_widget *w, return 0; }
-static const char *hp_mux_text[] = { - "Mixer", - "DAC", -}; - -#define WM8994_HP_ENUM(xname, xenum) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = snd_soc_info_enum_double, \ - .get = snd_soc_dapm_get_enum_double, \ - .put = wm8994_put_hp_enum, \ - .private_value = (unsigned long)&xenum } - -static int wm8994_put_hp_enum(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *w = wlist->widgets[0]; - struct snd_soc_codec *codec = w->codec; - int ret; - - ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); - - wm8994_update_class_w(codec); - - return ret; -} - -static const struct soc_enum hpl_enum = - SOC_ENUM_SINGLE(WM8994_OUTPUT_MIXER_1, 8, 2, hp_mux_text); - -static const struct snd_kcontrol_new hpl_mux = - WM8994_HP_ENUM("Left Headphone Mux", hpl_enum); - -static const struct soc_enum hpr_enum = - SOC_ENUM_SINGLE(WM8994_OUTPUT_MIXER_2, 8, 2, hp_mux_text); - -static const struct snd_kcontrol_new hpr_mux = - WM8994_HP_ENUM("Right Headphone Mux", hpr_enum); - static const char *adc_mux_text[] = { "ADC", "DMIC", @@ -1436,7 +1374,7 @@ static int wm8994_put_class_w(struct snd_kcontrol *kcontrol,
ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
- wm8994_update_class_w(codec); + wm_hubs_update_class_w(codec);
return ret; } @@ -1579,9 +1517,9 @@ SND_SOC_DAPM_MIXER_E("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0, SND_SOC_DAPM_MIXER_E("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0, right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer), late_enable_ev, SND_SOC_DAPM_PRE_PMU), -SND_SOC_DAPM_MUX_E("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux, +SND_SOC_DAPM_MUX_E("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux, late_enable_ev, SND_SOC_DAPM_PRE_PMU), -SND_SOC_DAPM_MUX_E("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux, +SND_SOC_DAPM_MUX_E("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux, late_enable_ev, SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev) @@ -1597,8 +1535,8 @@ SND_SOC_DAPM_MIXER("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0, left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)), SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0, right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)), -SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux), -SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux), +SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux), +SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux), };
static const struct snd_soc_dapm_widget wm8994_dac_revd_widgets[] = { @@ -3997,7 +3935,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) break; }
- wm8994_update_class_w(codec); + wm8994->hubs.check_class_w_digital = wm8994_check_class_w_digital; + wm_hubs_update_class_w(codec);
wm8994_handle_pdata(wm8994);
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index df44083..803b3b6 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -566,6 +566,65 @@ static int lineout_event(struct snd_soc_dapm_widget *w, return 0; }
+void wm_hubs_update_class_w(struct snd_soc_codec *codec) +{ + struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); + int enable = WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ; + + if (!wm_hubs_dac_hp_direct(codec)) + enable = false; + + if (hubs->check_class_w_digital && !hubs->check_class_w_digital(codec)) + enable = false; + + dev_vdbg(codec->dev, "Class W %s\n", enable ? "enabled" : "disabled"); + + snd_soc_update_bits(codec, WM8993_CLASS_W_0, + WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ, enable); +} +EXPORT_SYMBOL_GPL(wm_hubs_update_class_w); + +#define WM_HUBS_ENUM_W(xname, xenum) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_enum_double, \ + .get = snd_soc_dapm_get_enum_double, \ + .put = class_w_put, \ + .private_value = (unsigned long)&xenum } + +static int class_w_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + 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; + int ret; + + ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); + + wm_hubs_update_class_w(codec); + + return ret; +} + +static const char *hp_mux_text[] = { + "Mixer", + "DAC", +}; + +static const struct soc_enum hpl_enum = + SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER1, 8, 2, hp_mux_text); + +const struct snd_kcontrol_new wm_hubs_hpl_mux = + WM_HUBS_ENUM_W("Left Headphone Mux", hpl_enum); +EXPORT_SYMBOL_GPL(wm_hubs_hpl_mux); + +static const struct soc_enum hpr_enum = + SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER2, 8, 2, hp_mux_text); + +const struct snd_kcontrol_new wm_hubs_hpr_mux = + WM_HUBS_ENUM_W("Right Headphone Mux", hpr_enum); +EXPORT_SYMBOL_GPL(wm_hubs_hpr_mux); + static const struct snd_kcontrol_new in1l_pga[] = { SOC_DAPM_SINGLE("IN1LP Switch", WM8993_INPUT_MIXER2, 5, 1, 0), SOC_DAPM_SINGLE("IN1LN Switch", WM8993_INPUT_MIXER2, 4, 1, 0), diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h index 8bb9f1b..71861fc 100644 --- a/sound/soc/codecs/wm_hubs.h +++ b/sound/soc/codecs/wm_hubs.h @@ -16,6 +16,7 @@
#include <linux/completion.h> #include <linux/interrupt.h> +#include <sound/control.h>
struct snd_soc_codec;
@@ -32,6 +33,7 @@ struct wm_hubs_data {
bool no_cache_dac_hp_direct; u16 dac_hp_direct_dcs; + bool (*check_class_w_digital)(struct snd_soc_codec *);
bool lineout1_se; bool lineout1n_ena; @@ -57,5 +59,9 @@ extern irqreturn_t wm_hubs_dcs_done(int irq, void *data); extern void wm_hubs_vmid_ena(struct snd_soc_codec *codec); extern void wm_hubs_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level); +extern void wm_hubs_update_class_w(struct snd_soc_codec *codec); + +extern const struct snd_kcontrol_new wm_hubs_hpl_mux; +extern const struct snd_kcontrol_new wm_hubs_hpr_mux;
#endif
Class W can be used for any path where only data from the DAC is routed to the headphones. Currently we only enable it when the direct DAC to headphone path is used but it can also be enabled for paths that go via the output mixer providing the DAC is the only input to the output mixer. Implement support for this, including updates to the class W status when the output mixer configuration is changed. This also allows us to enable the DC servo optimisations for DAC to headphone paths where the output mixer is used.
In general the direct DAC path is still preferred as this will offer better performance on most wm_hubs devices but these additional paths can simplify use case management.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/wm_hubs.c | 61 +++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 21 deletions(-)
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 803b3b6..8a29843 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -122,7 +122,6 @@ static bool wm_hubs_dac_hp_direct(struct snd_soc_codec *codec) return false; } else { dev_vdbg(codec->dev, "HPL connected to mixer\n"); - return false; } } else { dev_vdbg(codec->dev, "HPL connected to DAC\n"); @@ -136,7 +135,6 @@ static bool wm_hubs_dac_hp_direct(struct snd_soc_codec *codec) return false; } else { dev_vdbg(codec->dev, "HPR connected to mixer\n"); - return false; } } else { dev_vdbg(codec->dev, "HPR connected to DAC\n"); @@ -584,15 +582,36 @@ void wm_hubs_update_class_w(struct snd_soc_codec *codec) } EXPORT_SYMBOL_GPL(wm_hubs_update_class_w);
+#define WM_HUBS_SINGLE_W(xname, reg, shift, max, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .get = snd_soc_dapm_get_volsw, .put = class_w_put_volsw, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + +static int class_w_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + 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; + int ret; + + ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); + + wm_hubs_update_class_w(codec); + + return ret; +} + #define WM_HUBS_ENUM_W(xname, xenum) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_enum_double, \ .get = snd_soc_dapm_get_enum_double, \ - .put = class_w_put, \ + .put = class_w_put_double, \ .private_value = (unsigned long)&xenum }
-static int class_w_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int class_w_put_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; @@ -656,25 +675,25 @@ SOC_DAPM_SINGLE("IN1R Switch", WM8993_INPUT_MIXER4, 5, 1, 0), };
static const struct snd_kcontrol_new left_output_mixer[] = { -SOC_DAPM_SINGLE("Right Input Switch", WM8993_OUTPUT_MIXER1, 7, 1, 0), -SOC_DAPM_SINGLE("Left Input Switch", WM8993_OUTPUT_MIXER1, 6, 1, 0), -SOC_DAPM_SINGLE("IN2RN Switch", WM8993_OUTPUT_MIXER1, 5, 1, 0), -SOC_DAPM_SINGLE("IN2LN Switch", WM8993_OUTPUT_MIXER1, 4, 1, 0), -SOC_DAPM_SINGLE("IN2LP Switch", WM8993_OUTPUT_MIXER1, 1, 1, 0), -SOC_DAPM_SINGLE("IN1R Switch", WM8993_OUTPUT_MIXER1, 3, 1, 0), -SOC_DAPM_SINGLE("IN1L Switch", WM8993_OUTPUT_MIXER1, 2, 1, 0), -SOC_DAPM_SINGLE("DAC Switch", WM8993_OUTPUT_MIXER1, 0, 1, 0), +WM_HUBS_SINGLE_W("Right Input Switch", WM8993_OUTPUT_MIXER1, 7, 1, 0), +WM_HUBS_SINGLE_W("Left Input Switch", WM8993_OUTPUT_MIXER1, 6, 1, 0), +WM_HUBS_SINGLE_W("IN2RN Switch", WM8993_OUTPUT_MIXER1, 5, 1, 0), +WM_HUBS_SINGLE_W("IN2LN Switch", WM8993_OUTPUT_MIXER1, 4, 1, 0), +WM_HUBS_SINGLE_W("IN2LP Switch", WM8993_OUTPUT_MIXER1, 1, 1, 0), +WM_HUBS_SINGLE_W("IN1R Switch", WM8993_OUTPUT_MIXER1, 3, 1, 0), +WM_HUBS_SINGLE_W("IN1L Switch", WM8993_OUTPUT_MIXER1, 2, 1, 0), +WM_HUBS_SINGLE_W("DAC Switch", WM8993_OUTPUT_MIXER1, 0, 1, 0), };
static const struct snd_kcontrol_new right_output_mixer[] = { -SOC_DAPM_SINGLE("Left Input Switch", WM8993_OUTPUT_MIXER2, 7, 1, 0), -SOC_DAPM_SINGLE("Right Input Switch", WM8993_OUTPUT_MIXER2, 6, 1, 0), -SOC_DAPM_SINGLE("IN2LN Switch", WM8993_OUTPUT_MIXER2, 5, 1, 0), -SOC_DAPM_SINGLE("IN2RN Switch", WM8993_OUTPUT_MIXER2, 4, 1, 0), -SOC_DAPM_SINGLE("IN1L Switch", WM8993_OUTPUT_MIXER2, 3, 1, 0), -SOC_DAPM_SINGLE("IN1R Switch", WM8993_OUTPUT_MIXER2, 2, 1, 0), -SOC_DAPM_SINGLE("IN2RP Switch", WM8993_OUTPUT_MIXER2, 1, 1, 0), -SOC_DAPM_SINGLE("DAC Switch", WM8993_OUTPUT_MIXER2, 0, 1, 0), +WM_HUBS_SINGLE_W("Left Input Switch", WM8993_OUTPUT_MIXER2, 7, 1, 0), +WM_HUBS_SINGLE_W("Right Input Switch", WM8993_OUTPUT_MIXER2, 6, 1, 0), +WM_HUBS_SINGLE_W("IN2LN Switch", WM8993_OUTPUT_MIXER2, 5, 1, 0), +WM_HUBS_SINGLE_W("IN2RN Switch", WM8993_OUTPUT_MIXER2, 4, 1, 0), +WM_HUBS_SINGLE_W("IN1L Switch", WM8993_OUTPUT_MIXER2, 3, 1, 0), +WM_HUBS_SINGLE_W("IN1R Switch", WM8993_OUTPUT_MIXER2, 2, 1, 0), +WM_HUBS_SINGLE_W("IN2RP Switch", WM8993_OUTPUT_MIXER2, 1, 1, 0), +WM_HUBS_SINGLE_W("DAC Switch", WM8993_OUTPUT_MIXER2, 0, 1, 0), };
static const struct snd_kcontrol_new earpiece_mixer[] = {
participants (1)
-
Mark Brown