These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / sound / soc / codecs / wm5110.c
index 3ee6cfd..52b9ccf 100644 (file)
 struct wm5110_priv {
        struct arizona_priv core;
        struct arizona_fll fll[2];
+
+       unsigned int in_value;
+       int in_pre_pending;
+       int in_post_pending;
+
+       unsigned int in_pga_cache[6];
 };
 
 static const struct wm_adsp_region wm5110_dsp1_regions[] = {
@@ -131,6 +137,25 @@ static const struct reg_default wm5110_sysclk_revd_patch[] = {
        { 0x33fb, 0xfe00 },
 };
 
+static const struct reg_default wm5110_sysclk_reve_patch[] = {
+       { 0x3270, 0xE410 },
+       { 0x3271, 0x3078 },
+       { 0x3272, 0xE410 },
+       { 0x3273, 0x3070 },
+       { 0x3274, 0xE410 },
+       { 0x3275, 0x3066 },
+       { 0x3276, 0xE410 },
+       { 0x3277, 0x3056 },
+       { 0x327A, 0xE414 },
+       { 0x327B, 0x3078 },
+       { 0x327C, 0xE414 },
+       { 0x327D, 0x3070 },
+       { 0x327E, 0xE414 },
+       { 0x327F, 0x3066 },
+       { 0x3280, 0xE414 },
+       { 0x3281, 0x3056 },
+};
+
 static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w,
                            struct snd_kcontrol *kcontrol, int event)
 {
@@ -146,7 +171,9 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w,
                patch_size = ARRAY_SIZE(wm5110_sysclk_revd_patch);
                break;
        default:
-               return 0;
+               patch = wm5110_sysclk_reve_patch;
+               patch_size = ARRAY_SIZE(wm5110_sysclk_reve_patch);
+               break;
        }
 
        switch (event) {
@@ -164,6 +191,368 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+static const struct reg_sequence wm5110_no_dre_left_enable[] = {
+       { 0x3024, 0xE410 },
+       { 0x3025, 0x0056 },
+       { 0x301B, 0x0224 },
+       { 0x301F, 0x4263 },
+       { 0x3021, 0x5291 },
+       { 0x3030, 0xE410 },
+       { 0x3031, 0x3066 },
+       { 0x3032, 0xE410 },
+       { 0x3033, 0x3070 },
+       { 0x3034, 0xE410 },
+       { 0x3035, 0x3078 },
+       { 0x3036, 0xE410 },
+       { 0x3037, 0x3080 },
+       { 0x3038, 0xE410 },
+       { 0x3039, 0x3080 },
+};
+
+static const struct reg_sequence wm5110_dre_left_enable[] = {
+       { 0x3024, 0x0231 },
+       { 0x3025, 0x0B00 },
+       { 0x301B, 0x0227 },
+       { 0x301F, 0x4266 },
+       { 0x3021, 0x5294 },
+       { 0x3030, 0xE231 },
+       { 0x3031, 0x0266 },
+       { 0x3032, 0x8231 },
+       { 0x3033, 0x4B15 },
+       { 0x3034, 0x8231 },
+       { 0x3035, 0x0B15 },
+       { 0x3036, 0xE231 },
+       { 0x3037, 0x5294 },
+       { 0x3038, 0x0231 },
+       { 0x3039, 0x0B00 },
+};
+
+static const struct reg_sequence wm5110_no_dre_right_enable[] = {
+       { 0x3074, 0xE414 },
+       { 0x3075, 0x0056 },
+       { 0x306B, 0x0224 },
+       { 0x306F, 0x4263 },
+       { 0x3071, 0x5291 },
+       { 0x3080, 0xE414 },
+       { 0x3081, 0x3066 },
+       { 0x3082, 0xE414 },
+       { 0x3083, 0x3070 },
+       { 0x3084, 0xE414 },
+       { 0x3085, 0x3078 },
+       { 0x3086, 0xE414 },
+       { 0x3087, 0x3080 },
+       { 0x3088, 0xE414 },
+       { 0x3089, 0x3080 },
+};
+
+static const struct reg_sequence wm5110_dre_right_enable[] = {
+       { 0x3074, 0x0231 },
+       { 0x3075, 0x0B00 },
+       { 0x306B, 0x0227 },
+       { 0x306F, 0x4266 },
+       { 0x3071, 0x5294 },
+       { 0x3080, 0xE231 },
+       { 0x3081, 0x0266 },
+       { 0x3082, 0x8231 },
+       { 0x3083, 0x4B17 },
+       { 0x3084, 0x8231 },
+       { 0x3085, 0x0B17 },
+       { 0x3086, 0xE231 },
+       { 0x3087, 0x5294 },
+       { 0x3088, 0x0231 },
+       { 0x3089, 0x0B00 },
+};
+
+static int wm5110_hp_pre_enable(struct snd_soc_dapm_widget *w)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = priv->arizona;
+       unsigned int val = snd_soc_read(codec, ARIZONA_DRE_ENABLE);
+       const struct reg_sequence *wseq;
+       int nregs;
+
+       switch (w->shift) {
+       case ARIZONA_OUT1L_ENA_SHIFT:
+               if (val & ARIZONA_DRE1L_ENA_MASK) {
+                       wseq = wm5110_dre_left_enable;
+                       nregs = ARRAY_SIZE(wm5110_dre_left_enable);
+               } else {
+                       wseq = wm5110_no_dre_left_enable;
+                       nregs = ARRAY_SIZE(wm5110_no_dre_left_enable);
+                       priv->out_up_delay += 10;
+               }
+               break;
+       case ARIZONA_OUT1R_ENA_SHIFT:
+               if (val & ARIZONA_DRE1R_ENA_MASK) {
+                       wseq = wm5110_dre_right_enable;
+                       nregs = ARRAY_SIZE(wm5110_dre_right_enable);
+               } else {
+                       wseq = wm5110_no_dre_right_enable;
+                       nregs = ARRAY_SIZE(wm5110_no_dre_right_enable);
+                       priv->out_up_delay += 10;
+               }
+               break;
+       default:
+               return 0;
+       }
+
+       return regmap_multi_reg_write(arizona->regmap, wseq, nregs);
+}
+
+static int wm5110_hp_pre_disable(struct snd_soc_dapm_widget *w)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       unsigned int val = snd_soc_read(codec, ARIZONA_DRE_ENABLE);
+
+       switch (w->shift) {
+       case ARIZONA_OUT1L_ENA_SHIFT:
+               if (!(val & ARIZONA_DRE1L_ENA_MASK)) {
+                       snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS,
+                                           ARIZONA_WS_TRG1, ARIZONA_WS_TRG1);
+                       snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS,
+                                           ARIZONA_WS_TRG1, 0);
+                       priv->out_down_delay += 27;
+               }
+               break;
+       case ARIZONA_OUT1R_ENA_SHIFT:
+               if (!(val & ARIZONA_DRE1R_ENA_MASK)) {
+                       snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS,
+                                           ARIZONA_WS_TRG2, ARIZONA_WS_TRG2);
+                       snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS,
+                                           ARIZONA_WS_TRG2, 0);
+                       priv->out_down_delay += 27;
+               }
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int wm5110_hp_ev(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+       switch (priv->arizona->rev) {
+       case 0 ... 3:
+               break;
+       default:
+               switch (event) {
+               case SND_SOC_DAPM_PRE_PMU:
+                       wm5110_hp_pre_enable(w);
+                       break;
+               case SND_SOC_DAPM_PRE_PMD:
+                       wm5110_hp_pre_disable(w);
+                       break;
+               default:
+                       break;
+               }
+               break;
+       }
+
+       return arizona_hp_ev(w, kcontrol, event);
+}
+
+static int wm5110_clear_pga_volume(struct arizona *arizona, int output)
+{
+       unsigned int reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + output * 4;
+       int ret;
+
+       ret = regmap_write(arizona->regmap, reg, 0x80);
+       if (ret)
+               dev_err(arizona->dev, "Failed to clear PGA (0x%x): %d\n",
+                       reg, ret);
+
+       return ret;
+}
+
+static int wm5110_put_dre(struct snd_kcontrol *kcontrol,
+                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int ena, dre;
+       unsigned int mask = (0x1 << mc->shift) | (0x1 << mc->rshift);
+       unsigned int lnew = (!!ucontrol->value.integer.value[0]) << mc->shift;
+       unsigned int rnew = (!!ucontrol->value.integer.value[1]) << mc->rshift;
+       unsigned int lold, rold;
+       unsigned int lena, rena;
+       int ret;
+
+       snd_soc_dapm_mutex_lock(dapm);
+
+       ret = regmap_read(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, &ena);
+       if (ret) {
+               dev_err(arizona->dev, "Failed to read output state: %d\n", ret);
+               goto err;
+       }
+       ret = regmap_read(arizona->regmap, ARIZONA_DRE_ENABLE, &dre);
+       if (ret) {
+               dev_err(arizona->dev, "Failed to read DRE state: %d\n", ret);
+               goto err;
+       }
+
+       lold = dre & (1 << mc->shift);
+       rold = dre & (1 << mc->rshift);
+       /* Enables are channel wise swapped from the DRE enables */
+       lena = ena & (1 << mc->rshift);
+       rena = ena & (1 << mc->shift);
+
+       if ((lena && lnew != lold) || (rena && rnew != rold)) {
+               dev_err(arizona->dev, "Can't change DRE on active outputs\n");
+               ret = -EBUSY;
+               goto err;
+       }
+
+       ret = regmap_update_bits(arizona->regmap, ARIZONA_DRE_ENABLE,
+                                mask, lnew | rnew);
+       if (ret) {
+               dev_err(arizona->dev, "Failed to set DRE: %d\n", ret);
+               goto err;
+       }
+
+       /* Force reset of PGA volumes, if turning DRE off */
+       if (!lnew && lold)
+               wm5110_clear_pga_volume(arizona, mc->shift);
+
+       if (!rnew && rold)
+               wm5110_clear_pga_volume(arizona, mc->rshift);
+
+err:
+       snd_soc_dapm_mutex_unlock(dapm);
+
+       return ret;
+}
+
+static int wm5110_in_pga_get(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       struct snd_soc_card *card = dapm->card;
+       int ret;
+
+       /*
+        * PGA Volume is also used as part of the enable sequence, so
+        * usage of it should be avoided whilst that is running.
+        */
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+       ret = snd_soc_get_volsw_range(kcontrol, ucontrol);
+
+       mutex_unlock(&card->dapm_mutex);
+
+       return ret;
+}
+
+static int wm5110_in_pga_put(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       struct snd_soc_card *card = dapm->card;
+       int ret;
+
+       /*
+        * PGA Volume is also used as part of the enable sequence, so
+        * usage of it should be avoided whilst that is running.
+        */
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+       ret = snd_soc_put_volsw_range(kcontrol, ucontrol);
+
+       mutex_unlock(&card->dapm_mutex);
+
+       return ret;
+}
+
+static int wm5110_in_analog_ev(struct snd_soc_dapm_widget *w,
+                              struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct wm5110_priv *wm5110 = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = priv->arizona;
+       unsigned int reg, mask;
+       struct reg_sequence analog_seq[] = {
+               { 0x80, 0x3 },
+               { 0x35d, 0 },
+               { 0x80, 0x0 },
+       };
+
+       reg = ARIZONA_IN1L_CONTROL + ((w->shift ^ 0x1) * 4);
+       mask = ARIZONA_IN1L_PGA_VOL_MASK;
+
+       switch (event) {
+       case SND_SOC_DAPM_WILL_PMU:
+               wm5110->in_value |= 0x3 << ((w->shift ^ 0x1) * 2);
+               wm5110->in_pre_pending++;
+               wm5110->in_post_pending++;
+               return 0;
+       case SND_SOC_DAPM_PRE_PMU:
+               wm5110->in_pga_cache[w->shift] = snd_soc_read(codec, reg);
+
+               snd_soc_update_bits(codec, reg, mask,
+                                   0x40 << ARIZONA_IN1L_PGA_VOL_SHIFT);
+
+               wm5110->in_pre_pending--;
+               if (wm5110->in_pre_pending == 0) {
+                       analog_seq[1].def = wm5110->in_value;
+                       regmap_multi_reg_write_bypassed(arizona->regmap,
+                                                       analog_seq,
+                                                       ARRAY_SIZE(analog_seq));
+
+                       msleep(55);
+
+                       wm5110->in_value = 0;
+               }
+
+               break;
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_update_bits(codec, reg, mask,
+                                   wm5110->in_pga_cache[w->shift]);
+
+               wm5110->in_post_pending--;
+               if (wm5110->in_post_pending == 0)
+                       regmap_multi_reg_write_bypassed(arizona->regmap,
+                                                       analog_seq,
+                                                       ARRAY_SIZE(analog_seq));
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int wm5110_in_ev(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = priv->arizona;
+
+       switch (arizona->rev) {
+       case 0 ... 4:
+               if (arizona_input_analog(codec, w->shift))
+                       wm5110_in_analog_ev(w, kcontrol, event);
+
+               break;
+       default:
+               break;
+       }
+
+       return arizona_in_ev(w, kcontrol, event);
+}
+
 static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
 static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
 static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
@@ -190,18 +579,24 @@ SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
 SOC_ENUM("IN3 OSR", arizona_in_dmic_osr[2]),
 SOC_ENUM("IN4 OSR", arizona_in_dmic_osr[3]),
 
-SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
-                    ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
-                    ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
-                    ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
-                    ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
-                    ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
-                    ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+                        ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+                        ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
+                        ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
+                        ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
+                        ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
+                        ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
 
 SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum),
 
@@ -247,8 +642,7 @@ ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
 
-SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19),
-SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE, 1, 0),
+ARIZONA_EQ_CONTROL("EQ1 Coefficients", ARIZONA_EQ1_2),
 SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
               24, 0, eq_tlv),
 SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT,
@@ -260,8 +654,7 @@ SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT,
 SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT,
               24, 0, eq_tlv),
 
-SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19),
-SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE, 1, 0),
+ARIZONA_EQ_CONTROL("EQ2 Coefficients", ARIZONA_EQ2_2),
 SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT,
               24, 0, eq_tlv),
 SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT,
@@ -273,8 +666,7 @@ SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT,
 SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT,
               24, 0, eq_tlv),
 
-SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19),
-SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE, 1, 0),
+ARIZONA_EQ_CONTROL("EQ3 Coefficients", ARIZONA_EQ3_2),
 SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT,
               24, 0, eq_tlv),
 SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT,
@@ -286,8 +678,7 @@ SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT,
 SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT,
               24, 0, eq_tlv),
 
-SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19),
-SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE, 1, 0),
+ARIZONA_EQ_CONTROL("EQ4 Coefficients", ARIZONA_EQ4_2),
 SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT,
               24, 0, eq_tlv),
 SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT,
@@ -314,10 +705,10 @@ ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE),
 
-SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1),
-SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1),
-SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1),
-SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1),
+ARIZONA_LHPF_CONTROL("LHPF1 Coefficients", ARIZONA_HPLPF1_2),
+ARIZONA_LHPF_CONTROL("LHPF2 Coefficients", ARIZONA_HPLPF2_2),
+ARIZONA_LHPF_CONTROL("LHPF3 Coefficients", ARIZONA_HPLPF3_2),
+ARIZONA_LHPF_CONTROL("LHPF4 Coefficients", ARIZONA_HPLPF4_2),
 
 SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode),
 SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
@@ -409,12 +800,15 @@ SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
 SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT,
           ARIZONA_SPK2R_MUTE_SHIFT, 1, 1),
 
-SOC_DOUBLE("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE,
-          ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0),
-SOC_DOUBLE("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE,
-          ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0),
-SOC_DOUBLE("HPOUT3 DRE Switch", ARIZONA_DRE_ENABLE,
-          ARIZONA_DRE3L_ENA_SHIFT, ARIZONA_DRE3R_ENA_SHIFT, 1, 0),
+SOC_DOUBLE_EXT("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE,
+          ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0,
+          snd_soc_get_volsw, wm5110_put_dre),
+SOC_DOUBLE_EXT("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE,
+          ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0,
+          snd_soc_get_volsw, wm5110_put_dre),
+SOC_DOUBLE_EXT("HPOUT3 DRE Switch", ARIZONA_DRE_ENABLE,
+          ARIZONA_DRE3L_ENA_SHIFT, ARIZONA_DRE3R_ENA_SHIFT, 1, 0,
+          snd_soc_get_volsw, wm5110_put_dre),
 
 SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
 SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
@@ -633,29 +1027,35 @@ SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
 SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
 
 SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN4L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4L_ENA_SHIFT,
                   0, NULL, 0, arizona_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
@@ -904,11 +1304,11 @@ SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
                    ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
 
 SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
-                  ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+                  ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, wm5110_hp_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
                   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
 SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
-                  ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+                  ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, wm5110_hp_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
                   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
 SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1,
@@ -1598,29 +1998,46 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
 
 static int wm5110_codec_probe(struct snd_soc_codec *codec)
 {
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
        struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
-       int ret;
+       int i, ret;
 
-       priv->core.arizona->dapm = &codec->dapm;
+       priv->core.arizona->dapm = dapm;
 
        arizona_init_spk(codec);
        arizona_init_gpio(codec);
        arizona_init_mono(codec);
 
-       ret = snd_soc_add_codec_controls(codec, wm_adsp2_fw_controls, 8);
-       if (ret != 0)
-               return ret;
+       for (i = 0; i < WM5110_NUM_ADSP; ++i) {
+               ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec);
+               if (ret)
+                       goto err_adsp2_codec_probe;
+       }
 
-       snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
+       ret = snd_soc_add_codec_controls(codec,
+                                        arizona_adsp2_rate_controls,
+                                        WM5110_NUM_ADSP);
+       if (ret)
+               goto err_adsp2_codec_probe;
 
-       priv->core.arizona->dapm = &codec->dapm;
+       snd_soc_dapm_disable_pin(dapm, "HAPTICS");
 
        return 0;
+
+err_adsp2_codec_probe:
+       for (--i; i >= 0; --i)
+               wm_adsp2_codec_remove(&priv->core.adsp[i], codec);
+
+       return ret;
 }
 
 static int wm5110_codec_remove(struct snd_soc_codec *codec)
 {
        struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
+       int i;
+
+       for (i = 0; i < WM5110_NUM_ADSP; ++i)
+               wm_adsp2_codec_remove(&priv->core.adsp[i], codec);
 
        priv->core.arizona->dapm = NULL;
 
@@ -1697,7 +2114,7 @@ static int wm5110_probe(struct platform_device *pdev)
                wm5110->core.adsp[i].num_mems
                        = ARRAY_SIZE(wm5110_dsp1_regions);
 
-               ret = wm_adsp2_init(&wm5110->core.adsp[i], false);
+               ret = wm_adsp2_init(&wm5110->core.adsp[i]);
                if (ret != 0)
                        return ret;
        }