ASoC: wm_hubs: Factor out class W management
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Thu, 26 Apr 2012 20:29:29 +0000 (21:29 +0100)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Fri, 27 Apr 2012 17:42:11 +0000 (18:42 +0100)
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
sound/soc/codecs/wm8994.c
sound/soc/codecs/wm_hubs.c
sound/soc/codecs/wm_hubs.h

index 8bb005926aa08c995648ae415757e7b6b79aa11f..36acfccab99986fa7ee5e3bd3f770d4212c2d29a 100644 (file)
@@ -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);
index 2475e1c10334ae748fbd64553b035af22a6d6121..d2db63022607fa634fb851e4aa01c4c7d1a848c1 100644 (file)
@@ -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 late_enable_ev(struct snd_soc_dapm_widget *w,
@@ -1120,45 +1097,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",
@@ -1270,7 +1208,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;
 }
@@ -1413,9 +1351,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)
@@ -1429,8 +1367,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[] = {
@@ -3829,7 +3767,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);
 
index 15aed8b7d981ab35747bd75327712ba963e80c18..9106334216c4152c6aebc369f028962be4a7343e 100644 (file)
@@ -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),
index 8bb9f1b51bf3561d8d9e03760c58dba8481e52ce..71861fc580a3c327fd869d5accf5d03b3f2d516b 100644 (file)
@@ -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