Merge tag 'regmap-3.4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap
[~shefty/rdma-dev.git] / sound / soc / codecs / wm8994.c
index 7c686abf8bd9a43a2366b1373da3df8bd56a8bc8..fe7fbaeb714658c716d0091894ee892d97e0b5d8 100644 (file)
@@ -686,14 +686,23 @@ static void wm1811_jackdet_set_mode(struct snd_soc_codec *codec, u16 mode)
 {
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
+       if (!wm8994->jackdet || !wm8994->jack_cb)
+               return;
+
        if (wm8994->active_refcount)
                mode = WM1811_JACKDET_MODE_AUDIO;
 
+       if (mode == wm8994->jackdet_mode)
+               return;
+
+       wm8994->jackdet_mode = mode;
+
+       /* Always use audio mode to detect while the system is active */
+       if (mode != WM1811_JACKDET_MODE_NONE)
+               mode = WM1811_JACKDET_MODE_AUDIO;
+
        snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
                            WM1811_JACKDET_MODE_MASK, mode);
-
-       if (mode == WM1811_JACKDET_MODE_MIC)
-               msleep(2);
 }
 
 static void active_reference(struct snd_soc_codec *codec)
@@ -707,15 +716,8 @@ static void active_reference(struct snd_soc_codec *codec)
        dev_dbg(codec->dev, "Active refcount incremented, now %d\n",
                wm8994->active_refcount);
 
-       if (wm8994->active_refcount == 1) {
-               /* If we're using jack detection go into audio mode */
-               if (wm8994->jackdet && wm8994->jack_cb) {
-                       snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
-                                           WM1811_JACKDET_MODE_MASK,
-                                           WM1811_JACKDET_MODE_AUDIO);
-                       msleep(2);
-               }
-       }
+       /* If we're using jack detection go into audio mode */
+       wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_AUDIO);
 
        mutex_unlock(&wm8994->accdet_lock);
 }
@@ -734,16 +736,12 @@ static void active_dereference(struct snd_soc_codec *codec)
 
        if (wm8994->active_refcount == 0) {
                /* Go into appropriate detection only mode */
-               if (wm8994->jackdet && wm8994->jack_cb) {
-                       if (wm8994->jack_mic || wm8994->mic_detecting)
-                               mode = WM1811_JACKDET_MODE_MIC;
-                       else
-                               mode = WM1811_JACKDET_MODE_JACK;
+               if (wm8994->jack_mic || wm8994->mic_detecting)
+                       mode = WM1811_JACKDET_MODE_MIC;
+               else
+                       mode = WM1811_JACKDET_MODE_JACK;
 
-                       snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
-                                           WM1811_JACKDET_MODE_MASK,
-                                           mode);
-               }
+               wm1811_jackdet_set_mode(codec, mode);
        }
 
        mutex_unlock(&wm8994->accdet_lock);
@@ -778,27 +776,69 @@ static void vmid_reference(struct snd_soc_codec *codec)
                wm8994->vmid_refcount);
 
        if (wm8994->vmid_refcount == 1) {
-               /* Startup bias, VMID ramp & buffer */
-               snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
-                                   WM8994_STARTUP_BIAS_ENA |
-                                   WM8994_VMID_BUF_ENA |
-                                   WM8994_VMID_RAMP_MASK,
-                                   WM8994_STARTUP_BIAS_ENA |
-                                   WM8994_VMID_BUF_ENA |
-                                   (0x3 << WM8994_VMID_RAMP_SHIFT));
-
-               /* Remove discharge for line out */
                snd_soc_update_bits(codec, WM8994_ANTIPOP_1,
                                    WM8994_LINEOUT1_DISCH |
                                    WM8994_LINEOUT2_DISCH, 0);
 
-               /* Main bias enable, VMID=2x40k */
-               snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
-                                   WM8994_BIAS_ENA |
-                                   WM8994_VMID_SEL_MASK,
-                                   WM8994_BIAS_ENA | 0x2);
+               wm_hubs_vmid_ena(codec);
+
+               switch (wm8994->vmid_mode) {
+               default:
+                       WARN_ON(0 == "Invalid VMID mode");
+               case WM8994_VMID_NORMAL:
+                       /* Startup bias, VMID ramp & buffer */
+                       snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+                                           WM8994_BIAS_SRC |
+                                           WM8994_VMID_DISCH |
+                                           WM8994_STARTUP_BIAS_ENA |
+                                           WM8994_VMID_BUF_ENA |
+                                           WM8994_VMID_RAMP_MASK,
+                                           WM8994_BIAS_SRC |
+                                           WM8994_STARTUP_BIAS_ENA |
+                                           WM8994_VMID_BUF_ENA |
+                                           (0x3 << WM8994_VMID_RAMP_SHIFT));
+
+                       /* Main bias enable, VMID=2x40k */
+                       snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
+                                           WM8994_BIAS_ENA |
+                                           WM8994_VMID_SEL_MASK,
+                                           WM8994_BIAS_ENA | 0x2);
+
+                       msleep(50);
+
+                       snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+                                           WM8994_VMID_RAMP_MASK |
+                                           WM8994_BIAS_SRC,
+                                           0);
+                       break;
+
+               case WM8994_VMID_FORCE:
+                       /* Startup bias, slow VMID ramp & buffer */
+                       snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+                                           WM8994_BIAS_SRC |
+                                           WM8994_VMID_DISCH |
+                                           WM8994_STARTUP_BIAS_ENA |
+                                           WM8994_VMID_BUF_ENA |
+                                           WM8994_VMID_RAMP_MASK,
+                                           WM8994_BIAS_SRC |
+                                           WM8994_STARTUP_BIAS_ENA |
+                                           WM8994_VMID_BUF_ENA |
+                                           (0x2 << WM8994_VMID_RAMP_SHIFT));
+
+                       /* Main bias enable, VMID=2x40k */
+                       snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
+                                           WM8994_BIAS_ENA |
+                                           WM8994_VMID_SEL_MASK,
+                                           WM8994_BIAS_ENA | 0x2);
+
+                       msleep(400);
 
-               msleep(20);
+                       snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+                                           WM8994_VMID_RAMP_MASK |
+                                           WM8994_BIAS_SRC,
+                                           0);
+                       break;
+               }
        }
 }
 
@@ -812,30 +852,55 @@ static void vmid_dereference(struct snd_soc_codec *codec)
                wm8994->vmid_refcount);
 
        if (wm8994->vmid_refcount == 0) {
-               /* Switch over to startup biases */
+               if (wm8994->hubs.lineout1_se)
+                       snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_3,
+                                           WM8994_LINEOUT1N_ENA |
+                                           WM8994_LINEOUT1P_ENA,
+                                           WM8994_LINEOUT1N_ENA |
+                                           WM8994_LINEOUT1P_ENA);
+
+               if (wm8994->hubs.lineout2_se)
+                       snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_3,
+                                           WM8994_LINEOUT2N_ENA |
+                                           WM8994_LINEOUT2P_ENA,
+                                           WM8994_LINEOUT2N_ENA |
+                                           WM8994_LINEOUT2P_ENA);
+
+               /* Start discharging VMID */
                snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
                                    WM8994_BIAS_SRC |
-                                   WM8994_STARTUP_BIAS_ENA |
-                                   WM8994_VMID_BUF_ENA |
-                                   WM8994_VMID_RAMP_MASK,
+                                   WM8994_VMID_DISCH,
                                    WM8994_BIAS_SRC |
-                                   WM8994_STARTUP_BIAS_ENA |
-                                   WM8994_VMID_BUF_ENA |
-                                   (1 << WM8994_VMID_RAMP_SHIFT));
+                                   WM8994_VMID_DISCH);
 
-               /* Disable main biases */
-               snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
-                                   WM8994_BIAS_ENA |
-                                   WM8994_VMID_SEL_MASK, 0);
+               switch (wm8994->vmid_mode) {
+               case WM8994_VMID_FORCE:
+                       msleep(350);
+                       break;
+               default:
+                       break;
+               }
+
+               snd_soc_update_bits(codec, WM8994_ADDITIONAL_CONTROL,
+                                   WM8994_VROI, WM8994_VROI);
 
-               /* Discharge line */
+               /* Active discharge */
                snd_soc_update_bits(codec, WM8994_ANTIPOP_1,
                                    WM8994_LINEOUT1_DISCH |
                                    WM8994_LINEOUT2_DISCH,
                                    WM8994_LINEOUT1_DISCH |
                                    WM8994_LINEOUT2_DISCH);
 
-               msleep(5);
+               msleep(150);
+
+               snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_3,
+                                   WM8994_LINEOUT1N_ENA |
+                                   WM8994_LINEOUT1P_ENA |
+                                   WM8994_LINEOUT2N_ENA |
+                                   WM8994_LINEOUT2P_ENA, 0);
+
+               snd_soc_update_bits(codec, WM8994_ADDITIONAL_CONTROL,
+                                   WM8994_VROI, 0);
 
                /* Switch off startup biases */
                snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
@@ -843,6 +908,12 @@ static void vmid_dereference(struct snd_soc_codec *codec)
                                    WM8994_STARTUP_BIAS_ENA |
                                    WM8994_VMID_BUF_ENA |
                                    WM8994_VMID_RAMP_MASK, 0);
+
+               snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
+                                   WM8994_BIAS_ENA | WM8994_VMID_SEL_MASK, 0);
+
+               snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+                                   WM8994_VMID_RAMP_MASK, 0);
        }
 
        pm_runtime_put(codec->dev);
@@ -1459,17 +1530,17 @@ SND_SOC_DAPM_AIF_IN_E("AIF2DACR", NULL, 0,
                      WM8994_POWER_MANAGEMENT_5, 12, 0, wm8958_aif_ev,
                      SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 
-SND_SOC_DAPM_AIF_IN("AIF1DACDAT", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
-SND_SOC_DAPM_AIF_IN("AIF2DACDAT", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1ADCDAT", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
-SND_SOC_DAPM_AIF_OUT("AIF2ADCDAT", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIF1DACDAT", NULL, 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIF2DACDAT", NULL, 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1ADCDAT", NULL, 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2ADCDAT",  NULL, 0, SND_SOC_NOPM, 0, 0),
 
 SND_SOC_DAPM_MUX("AIF1DAC Mux", SND_SOC_NOPM, 0, 0, &aif1dac_mux),
 SND_SOC_DAPM_MUX("AIF2DAC Mux", SND_SOC_NOPM, 0, 0, &aif2dac_mux),
 SND_SOC_DAPM_MUX("AIF2ADC Mux", SND_SOC_NOPM, 0, 0, &aif2adc_mux),
 
-SND_SOC_DAPM_AIF_IN("AIF3DACDAT", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0),
-SND_SOC_DAPM_AIF_OUT("AIF3ADCDAT", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIF3DACDAT", NULL, 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3ADCDAT", NULL, 0, SND_SOC_NOPM, 0, 0),
 
 SND_SOC_DAPM_SUPPLY("TOCLK", WM8994_CLOCKING_1, 4, 0, NULL, 0),
 
@@ -1584,6 +1655,14 @@ static const struct snd_soc_dapm_route intercon[] = {
 
        { "TOCLK", NULL, "CLK_SYS" },
 
+       { "AIF1DACDAT", NULL, "AIF1 Playback" },
+       { "AIF2DACDAT", NULL, "AIF2 Playback" },
+       { "AIF3DACDAT", NULL, "AIF3 Playback" },
+
+       { "AIF1 Capture", NULL, "AIF1ADCDAT" },
+       { "AIF2 Capture", NULL, "AIF2ADCDAT" },
+       { "AIF3 Capture", NULL, "AIF3ADCDAT" },
+
        /* AIF1 outputs */
        { "AIF1ADC1L", NULL, "AIF1ADC1L Mixer" },
        { "AIF1ADC1L Mixer", "ADC/DMIC Switch", "ADCL Mux" },
@@ -1896,7 +1975,8 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
                            WM8994_FLL1_OUTDIV_MASK |
                            WM8994_FLL1_FRATIO_MASK, reg);
 
-       snd_soc_write(codec, WM8994_FLL1_CONTROL_3 + reg_offset, fll.k);
+       snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_3 + reg_offset,
+                           WM8994_FLL1_K_MASK, fll.k);
 
        snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_4 + reg_offset,
                            WM8994_FLL1_N_MASK,
@@ -2074,6 +2154,8 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
        struct wm8994 *control = wm8994->wm8994;
 
+       wm_hubs_set_bias_level(codec, level);
+
        switch (level) {
        case SND_SOC_BIAS_ON:
                break;
@@ -2145,11 +2227,61 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
                        wm8994->cur_fw = NULL;
                break;
        }
+
        codec->dapm.bias_level = level;
 
        return 0;
 }
 
+int wm8994_vmid_mode(struct snd_soc_codec *codec, enum wm8994_vmid_mode mode)
+{
+       struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+       switch (mode) {
+       case WM8994_VMID_NORMAL:
+               if (wm8994->hubs.lineout1_se) {
+                       snd_soc_dapm_disable_pin(&codec->dapm,
+                                                "LINEOUT1N Driver");
+                       snd_soc_dapm_disable_pin(&codec->dapm,
+                                                "LINEOUT1P Driver");
+               }
+               if (wm8994->hubs.lineout2_se) {
+                       snd_soc_dapm_disable_pin(&codec->dapm,
+                                                "LINEOUT2N Driver");
+                       snd_soc_dapm_disable_pin(&codec->dapm,
+                                                "LINEOUT2P Driver");
+               }
+
+               /* Do the sync with the old mode to allow it to clean up */
+               snd_soc_dapm_sync(&codec->dapm);
+               wm8994->vmid_mode = mode;
+               break;
+
+       case WM8994_VMID_FORCE:
+               if (wm8994->hubs.lineout1_se) {
+                       snd_soc_dapm_force_enable_pin(&codec->dapm,
+                                                     "LINEOUT1N Driver");
+                       snd_soc_dapm_force_enable_pin(&codec->dapm,
+                                                     "LINEOUT1P Driver");
+               }
+               if (wm8994->hubs.lineout2_se) {
+                       snd_soc_dapm_force_enable_pin(&codec->dapm,
+                                                     "LINEOUT2N Driver");
+                       snd_soc_dapm_force_enable_pin(&codec->dapm,
+                                                     "LINEOUT2P Driver");
+               }
+
+               wm8994->vmid_mode = mode;
+               snd_soc_dapm_sync(&codec->dapm);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 {
        struct snd_soc_codec *codec = dai->codec;
@@ -2631,6 +2763,7 @@ static struct snd_soc_dai_driver wm8994_dai[] = {
                        .channels_max = 2,
                        .rates = WM8994_RATES,
                        .formats = WM8994_FORMATS,
+                       .sig_bits = 24,
                },
                .capture = {
                        .stream_name = "AIF1 Capture",
@@ -2638,6 +2771,7 @@ static struct snd_soc_dai_driver wm8994_dai[] = {
                        .channels_max = 2,
                        .rates = WM8994_RATES,
                        .formats = WM8994_FORMATS,
+                       .sig_bits = 24,
                 },
                .ops = &wm8994_aif1_dai_ops,
        },
@@ -2650,6 +2784,7 @@ static struct snd_soc_dai_driver wm8994_dai[] = {
                        .channels_max = 2,
                        .rates = WM8994_RATES,
                        .formats = WM8994_FORMATS,
+                       .sig_bits = 24,
                },
                .capture = {
                        .stream_name = "AIF2 Capture",
@@ -2657,6 +2792,7 @@ static struct snd_soc_dai_driver wm8994_dai[] = {
                        .channels_max = 2,
                        .rates = WM8994_RATES,
                        .formats = WM8994_FORMATS,
+                       .sig_bits = 24,
                },
                .probe = wm8994_aif2_probe,
                .ops = &wm8994_aif2_dai_ops,
@@ -2670,6 +2806,7 @@ static struct snd_soc_dai_driver wm8994_dai[] = {
                        .channels_max = 2,
                        .rates = WM8994_RATES,
                        .formats = WM8994_FORMATS,
+                       .sig_bits = 24,
                },
                .capture = {
                        .stream_name = "AIF3 Capture",
@@ -2677,13 +2814,14 @@ static struct snd_soc_dai_driver wm8994_dai[] = {
                        .channels_max = 2,
                        .rates = WM8994_RATES,
                        .formats = WM8994_FORMATS,
-               },
+                       .sig_bits = 24,
+                },
                .ops = &wm8994_aif3_dai_ops,
        }
 };
 
 #ifdef CONFIG_PM
-static int wm8994_suspend(struct snd_soc_codec *codec)
+static int wm8994_codec_suspend(struct snd_soc_codec *codec)
 {
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
        struct wm8994 *control = wm8994->wm8994;
@@ -2717,7 +2855,7 @@ static int wm8994_suspend(struct snd_soc_codec *codec)
        return 0;
 }
 
-static int wm8994_resume(struct snd_soc_codec *codec)
+static int wm8994_codec_resume(struct snd_soc_codec *codec)
 {
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
        struct wm8994 *control = wm8994->wm8994;
@@ -2739,8 +2877,6 @@ static int wm8994_resume(struct snd_soc_codec *codec)
                codec->cache_only = 0;
        }
 
-       wm8994_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
                if (!wm8994->fll_suspend[i].out)
                        continue;
@@ -2768,6 +2904,7 @@ static int wm8994_resume(struct snd_soc_codec *codec)
                                            WM1811_JACKDET_MODE_JACK);
                        break;
                }
+               break;
        case WM8958:
                if (wm8994->jack_cb)
                        snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
@@ -2778,8 +2915,8 @@ static int wm8994_resume(struct snd_soc_codec *codec)
        return 0;
 }
 #else
-#define wm8994_suspend NULL
-#define wm8994_resume NULL
+#define wm8994_codec_suspend NULL
+#define wm8994_codec_resume NULL
 #endif
 
 static void wm8994_handle_retune_mobile_pdata(struct wm8994_priv *wm8994)
@@ -2842,7 +2979,7 @@ static void wm8994_handle_retune_mobile_pdata(struct wm8994_priv *wm8994)
        wm8994->retune_mobile_enum.max = wm8994->num_retune_mobile_texts;
        wm8994->retune_mobile_enum.texts = wm8994->retune_mobile_texts;
 
-       ret = snd_soc_add_controls(wm8994->codec, controls,
+       ret = snd_soc_add_codec_controls(wm8994->codec, controls,
                                   ARRAY_SIZE(controls));
        if (ret != 0)
                dev_err(wm8994->codec->dev,
@@ -2895,7 +3032,7 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
                wm8994->drc_enum.max = pdata->num_drc_cfgs;
                wm8994->drc_enum.texts = wm8994->drc_texts;
 
-               ret = snd_soc_add_controls(wm8994->codec, controls,
+               ret = snd_soc_add_codec_controls(wm8994->codec, controls,
                                           ARRAY_SIZE(controls));
                if (ret != 0)
                        dev_err(wm8994->codec->dev,
@@ -2911,7 +3048,7 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
        if (pdata->num_retune_mobile_cfgs)
                wm8994_handle_retune_mobile_pdata(wm8994);
        else
-               snd_soc_add_controls(wm8994->codec, wm8994_eq_controls,
+               snd_soc_add_codec_controls(wm8994->codec, wm8994_eq_controls,
                                     ARRAY_SIZE(wm8994_eq_controls));
 
        for (i = 0; i < ARRAY_SIZE(pdata->micbias); i++) {
@@ -2928,8 +3065,6 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
  * @codec:   WM8994 codec
  * @jack:    jack to report detection events on
  * @micbias: microphone bias to detect on
- * @det:     value to report for presence detection
- * @shrt:    value to report for short detection
  *
  * Enable microphone detection via IRQ on the WM8994.  If GPIOs are
  * being used to bring out signals to the processor then only platform
@@ -2940,43 +3075,63 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
  * and micbias2_lvl platform data members.
  */
 int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
-                     int micbias, int det, int shrt)
+                     int micbias)
 {
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
        struct wm8994_micdet *micdet;
        struct wm8994 *control = wm8994->wm8994;
-       int reg;
+       int reg, ret;
 
-       if (control->type != WM8994)
+       if (control->type != WM8994) {
+               dev_warn(codec->dev, "Not a WM8994\n");
                return -EINVAL;
+       }
 
        switch (micbias) {
        case 1:
                micdet = &wm8994->micdet[0];
+               if (jack)
+                       ret = snd_soc_dapm_force_enable_pin(&codec->dapm,
+                                                           "MICBIAS1");
+               else
+                       ret = snd_soc_dapm_disable_pin(&codec->dapm,
+                                                      "MICBIAS1");
                break;
        case 2:
                micdet = &wm8994->micdet[1];
+               if (jack)
+                       ret = snd_soc_dapm_force_enable_pin(&codec->dapm,
+                                                           "MICBIAS1");
+               else
+                       ret = snd_soc_dapm_disable_pin(&codec->dapm,
+                                                      "MICBIAS1");
                break;
        default:
+               dev_warn(codec->dev, "Invalid MICBIAS %d\n", micbias);
                return -EINVAL;
-       }       
+       }
 
-       dev_dbg(codec->dev, "Configuring microphone detection on %d: %x %x\n",
-               micbias, det, shrt);
+       if (ret != 0)
+               dev_warn(codec->dev, "Failed to configure MICBIAS%d: %d\n",
+                        micbias, ret);
+
+       dev_dbg(codec->dev, "Configuring microphone detection on %d %p\n",
+               micbias, jack);
 
        /* Store the configuration */
        micdet->jack = jack;
-       micdet->det = det;
-       micdet->shrt = shrt;
+       micdet->detecting = true;
 
        /* If either of the jacks is set up then enable detection */
        if (wm8994->micdet[0].jack || wm8994->micdet[1].jack)
                reg = WM8994_MICD_ENA;
-       else 
+       else
                reg = 0;
 
        snd_soc_update_bits(codec, WM8994_MICBIAS, WM8994_MICD_ENA, reg);
 
+       snd_soc_dapm_sync(&codec->dapm);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(wm8994_mic_detect);
@@ -3002,20 +3157,42 @@ static irqreturn_t wm8994_mic_irq(int irq, void *data)
        dev_dbg(codec->dev, "Microphone status: %x\n", reg);
 
        report = 0;
-       if (reg & WM8994_MIC1_DET_STS)
-               report |= priv->micdet[0].det;
-       if (reg & WM8994_MIC1_SHRT_STS)
-               report |= priv->micdet[0].shrt;
+       if (reg & WM8994_MIC1_DET_STS) {
+               if (priv->micdet[0].detecting)
+                       report = SND_JACK_HEADSET;
+       }
+       if (reg & WM8994_MIC1_SHRT_STS) {
+               if (priv->micdet[0].detecting)
+                       report = SND_JACK_HEADPHONE;
+               else
+                       report |= SND_JACK_BTN_0;
+       }
+       if (report)
+               priv->micdet[0].detecting = false;
+       else
+               priv->micdet[0].detecting = true;
+
        snd_soc_jack_report(priv->micdet[0].jack, report,
-                           priv->micdet[0].det | priv->micdet[0].shrt);
+                           SND_JACK_HEADSET | SND_JACK_BTN_0);
 
        report = 0;
-       if (reg & WM8994_MIC2_DET_STS)
-               report |= priv->micdet[1].det;
-       if (reg & WM8994_MIC2_SHRT_STS)
-               report |= priv->micdet[1].shrt;
+       if (reg & WM8994_MIC2_DET_STS) {
+               if (priv->micdet[1].detecting)
+                       report = SND_JACK_HEADSET;
+       }
+       if (reg & WM8994_MIC2_SHRT_STS) {
+               if (priv->micdet[1].detecting)
+                       report = SND_JACK_HEADPHONE;
+               else
+                       report |= SND_JACK_BTN_0;
+       }
+       if (report)
+               priv->micdet[1].detecting = false;
+       else
+               priv->micdet[1].detecting = true;
+
        snd_soc_jack_report(priv->micdet[1].jack, report,
-                           priv->micdet[1].det | priv->micdet[1].shrt);
+                           SND_JACK_HEADSET | SND_JACK_BTN_0);
 
        return IRQ_HANDLED;
 }
@@ -3064,7 +3241,7 @@ static void wm8958_default_micdet(u16 status, void *data)
        }
 
 
-       if (wm8994->mic_detecting && status & 0x4) {
+       if (wm8994->mic_detecting && status & 0xfc) {
                dev_dbg(codec->dev, "Detected headphone\n");
                wm8994->mic_detecting = false;
 
@@ -3075,11 +3252,23 @@ static void wm8958_default_micdet(u16 status, void *data)
 
                /* If we have jackdet that will detect removal */
                if (wm8994->jackdet) {
+                       mutex_lock(&wm8994->accdet_lock);
+
                        snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
                                            WM8958_MICD_ENA, 0);
 
                        wm1811_jackdet_set_mode(codec,
                                                WM1811_JACKDET_MODE_JACK);
+
+                       mutex_unlock(&wm8994->accdet_lock);
+
+                       if (wm8994->pdata->jd_ext_cap) {
+                               mutex_lock(&codec->mutex);
+                               snd_soc_dapm_disable_pin(&codec->dapm,
+                                                        "MICBIAS2");
+                               snd_soc_dapm_sync(&codec->dapm);
+                               mutex_unlock(&codec->mutex);
+                       }
                }
        }
 
@@ -3114,6 +3303,7 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
        struct wm8994_priv *wm8994 = data;
        struct snd_soc_codec *codec = wm8994->codec;
        int reg;
+       bool present;
 
        mutex_lock(&wm8994->accdet_lock);
 
@@ -3126,11 +3316,17 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
 
        dev_dbg(codec->dev, "JACKDET %x\n", reg);
 
-       if (reg & WM1811_JACKDET_LVL) {
+       present = reg & WM1811_JACKDET_LVL;
+
+       if (present) {
                dev_dbg(codec->dev, "Jack detected\n");
 
-               snd_soc_jack_report(wm8994->micdet[0].jack,
-                                   SND_JACK_MECHANICAL, SND_JACK_MECHANICAL);
+               snd_soc_update_bits(codec, WM8958_MICBIAS2,
+                                   WM8958_MICB2_DISCH, 0);
+
+               /* Disable debounce while inserted */
+               snd_soc_update_bits(codec, WM1811_JACKDET_CTRL,
+                                   WM1811_JACKDET_DB, 0);
 
                /*
                 * Start off measument of microphone impedence to find
@@ -3138,14 +3334,18 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
                 */
                wm8994->mic_detecting = true;
                wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_MIC);
+
                snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
                                    WM8958_MICD_ENA, WM8958_MICD_ENA);
        } else {
                dev_dbg(codec->dev, "Jack not detected\n");
 
-               snd_soc_jack_report(wm8994->micdet[0].jack, 0,
-                                   SND_JACK_MECHANICAL | SND_JACK_HEADSET |
-                                   wm8994->btn_mask);
+               snd_soc_update_bits(codec, WM8958_MICBIAS2,
+                                   WM8958_MICB2_DISCH, WM8958_MICB2_DISCH);
+
+               /* Enable debounce while removed */
+               snd_soc_update_bits(codec, WM1811_JACKDET_CTRL,
+                                   WM1811_JACKDET_DB, WM1811_JACKDET_DB);
 
                wm8994->mic_detecting = false;
                wm8994->jack_mic = false;
@@ -3156,6 +3356,28 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
 
        mutex_unlock(&wm8994->accdet_lock);
 
+       /* If required for an external cap force MICBIAS on */
+       if (wm8994->pdata->jd_ext_cap) {
+               mutex_lock(&codec->mutex);
+
+               if (present)
+                       snd_soc_dapm_force_enable_pin(&codec->dapm,
+                                                     "MICBIAS2");
+               else
+                       snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS2");
+
+               snd_soc_dapm_sync(&codec->dapm);
+               mutex_unlock(&codec->mutex);
+       }
+
+       if (present)
+               snd_soc_jack_report(wm8994->micdet[0].jack,
+                                   SND_JACK_MECHANICAL, SND_JACK_MECHANICAL);
+       else
+               snd_soc_jack_report(wm8994->micdet[0].jack, 0,
+                                   SND_JACK_MECHANICAL | SND_JACK_HEADSET |
+                                   wm8994->btn_mask);
+
        return IRQ_HANDLED;
 }
 
@@ -3198,6 +3420,7 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
                }
 
                snd_soc_dapm_force_enable_pin(&codec->dapm, "CLK_SYS");
+               snd_soc_dapm_sync(&codec->dapm);
 
                wm8994->micdet[0].jack = jack;
                wm8994->jack_cb = cb;
@@ -3228,6 +3451,9 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
                 * otherwise jump straight to microphone detection.
                 */
                if (wm8994->jackdet) {
+                       snd_soc_update_bits(codec, WM8958_MICBIAS2,
+                                           WM8958_MICB2_DISCH,
+                                           WM8958_MICB2_DISCH);
                        snd_soc_update_bits(codec, WM8994_LDO_1,
                                            WM8994_LDO1_DISCH, 0);
                        wm1811_jackdet_set_mode(codec,
@@ -3240,7 +3466,9 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
        } else {
                snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
                                    WM8958_MICD_ENA, 0);
+               wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_NONE);
                snd_soc_dapm_disable_pin(&codec->dapm, "CLK_SYS");
+               snd_soc_dapm_sync(&codec->dapm);
        }
 
        return 0;
@@ -3253,17 +3481,13 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
        struct snd_soc_codec *codec = wm8994->codec;
        int reg, count;
 
-       mutex_lock(&wm8994->accdet_lock);
-
        /*
         * Jack detection may have detected a removal simulataneously
         * with an update of the MICDET status; if so it will have
         * stopped detection and we can ignore this interrupt.
         */
-       if (!(snd_soc_read(codec, WM8958_MIC_DETECT_1) & WM8958_MICD_ENA)) {
-               mutex_unlock(&wm8994->accdet_lock);
+       if (!(snd_soc_read(codec, WM8958_MIC_DETECT_1) & WM8958_MICD_ENA))
                return IRQ_HANDLED;
-       }
 
        /* We may occasionally read a detection without an impedence
         * range being provided - if that happens loop again.
@@ -3272,7 +3496,6 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
        do {
                reg = snd_soc_read(codec, WM8958_MIC_DETECT_3);
                if (reg < 0) {
-                       mutex_unlock(&wm8994->accdet_lock);
                        dev_err(codec->dev,
                                "Failed to read mic detect status: %d\n",
                                reg);
@@ -3303,8 +3526,6 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
                dev_warn(codec->dev, "Accessory detection with no callback\n");
 
 out:
-       mutex_unlock(&wm8994->accdet_lock);
-
        return IRQ_HANDLED;
 }
 
@@ -3338,23 +3559,16 @@ static irqreturn_t wm8994_temp_shut(int irq, void *data)
 static int wm8994_codec_probe(struct snd_soc_codec *codec)
 {
        struct wm8994 *control = dev_get_drvdata(codec->dev->parent);
-       struct wm8994_priv *wm8994;
+       struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
        struct snd_soc_dapm_context *dapm = &codec->dapm;
        unsigned int reg;
        int ret, i;
 
+       wm8994->codec = codec;
        codec->control_data = control->regmap;
 
-       wm8994 = devm_kzalloc(codec->dev, sizeof(struct wm8994_priv),
-                             GFP_KERNEL);
-       if (wm8994 == NULL)
-               return -ENOMEM;
-       snd_soc_codec_set_drvdata(codec, wm8994);
-
        snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_REGMAP);
 
-       wm8994->wm8994 = dev_get_drvdata(codec->dev->parent);
-       wm8994->pdata = dev_get_platdata(codec->dev->parent);
        wm8994->codec = codec;
 
        mutex_init(&wm8994->accdet_lock);
@@ -3369,12 +3583,20 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
                                     WM8994_IRQ_MIC1_DET;
 
        pm_runtime_enable(codec->dev);
-       pm_runtime_resume(codec->dev);
+       pm_runtime_idle(codec->dev);
+
+       /* By default use idle_bias_off, will override for WM8994 */
+       codec->dapm.idle_bias_off = 1;
 
        /* Set revision-specific configuration */
        wm8994->revision = snd_soc_read(codec, WM8994_CHIP_REVISION);
        switch (control->type) {
        case WM8994:
+               /* Single ended line outputs should have VMID on. */
+               if (!wm8994->pdata->lineout1_diff ||
+                   !wm8994->pdata->lineout2_diff)
+                       codec->dapm.idle_bias_off = 0;
+
                switch (wm8994->revision) {
                case 2:
                case 3:
@@ -3392,11 +3614,14 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
 
        case WM8958:
                wm8994->hubs.dcs_readback_mode = 1;
+               wm8994->hubs.hp_startup_mode = 1;
                break;
 
        case WM1811:
                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;
 
                switch (wm8994->revision) {
                case 0:
@@ -3513,6 +3738,9 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
                        wm8994->fll_locked_irq = false;
        }
 
+       /* Make sure we can read from the GPIOs if they're inputs */
+       pm_runtime_get_sync(codec->dev);
+
        /* Remember if AIFnLRCLK is configured as a GPIO.  This should be
         * configured on init - if a system wants to do this dynamically
         * at runtime we can deal with that then.
@@ -3541,7 +3769,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
                wm8994->lrclk_shared[1] = 0;
        }
 
-       wm8994_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       pm_runtime_put(codec->dev);
 
        /* Latch volume updates (right only; we always do left then right). */
        snd_soc_update_bits(codec, WM8994_AIF1_DAC1_LEFT_VOLUME,
@@ -3619,7 +3847,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
        wm8994_handle_pdata(wm8994);
 
        wm_hubs_add_analogue_controls(codec);
-       snd_soc_add_controls(codec, wm8994_snd_controls,
+       snd_soc_add_codec_controls(codec, wm8994_snd_controls,
                             ARRAY_SIZE(wm8994_snd_controls));
        snd_soc_dapm_new_controls(dapm, wm8994_dapm_widgets,
                                  ARRAY_SIZE(wm8994_dapm_widgets));
@@ -3645,7 +3873,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
                }
                break;
        case WM8958:
-               snd_soc_add_controls(codec, wm8958_snd_controls,
+               snd_soc_add_codec_controls(codec, wm8958_snd_controls,
                                     ARRAY_SIZE(wm8958_snd_controls));
                snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets,
                                          ARRAY_SIZE(wm8958_dapm_widgets));
@@ -3667,7 +3895,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
                break;
 
        case WM1811:
-               snd_soc_add_controls(codec, wm8958_snd_controls,
+               snd_soc_add_codec_controls(codec, wm8958_snd_controls,
                                     ARRAY_SIZE(wm8958_snd_controls));
                snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets,
                                          ARRAY_SIZE(wm8958_dapm_widgets));
@@ -3796,24 +4024,27 @@ static int  wm8994_codec_remove(struct snd_soc_codec *codec)
        return 0;
 }
 
-static int wm8994_soc_volatile(struct snd_soc_codec *codec,
-                              unsigned int reg)
-{
-       return true;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8994 = {
        .probe =        wm8994_codec_probe,
        .remove =       wm8994_codec_remove,
-       .suspend =      wm8994_suspend,
-       .resume =       wm8994_resume,
+       .suspend =      wm8994_codec_suspend,
+       .resume =       wm8994_codec_resume,
        .set_bias_level = wm8994_set_bias_level,
-       .reg_cache_size = WM8994_MAX_REGISTER,
-       .volatile_register = wm8994_soc_volatile,
 };
 
 static int __devinit wm8994_probe(struct platform_device *pdev)
 {
+       struct wm8994_priv *wm8994;
+
+       wm8994 = devm_kzalloc(&pdev->dev, sizeof(struct wm8994_priv),
+                             GFP_KERNEL);
+       if (wm8994 == NULL)
+               return -ENOMEM;
+       platform_set_drvdata(pdev, wm8994);
+
+       wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent);
+       wm8994->pdata = dev_get_platdata(pdev->dev.parent);
+
        return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8994,
                        wm8994_dai, ARRAY_SIZE(wm8994_dai));
 }
@@ -3824,11 +4055,43 @@ static int __devexit wm8994_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int wm8994_suspend(struct device *dev)
+{
+       struct wm8994_priv *wm8994 = dev_get_drvdata(dev);
+
+       /* Drop down to power saving mode when system is suspended */
+       if (wm8994->jackdet && !wm8994->active_refcount)
+               regmap_update_bits(wm8994->wm8994->regmap, WM8994_ANTIPOP_2,
+                                  WM1811_JACKDET_MODE_MASK,
+                                  wm8994->jackdet_mode);
+
+       return 0;
+}
+
+static int wm8994_resume(struct device *dev)
+{
+       struct wm8994_priv *wm8994 = dev_get_drvdata(dev);
+
+       if (wm8994->jackdet && wm8994->jack_cb)
+               regmap_update_bits(wm8994->wm8994->regmap, WM8994_ANTIPOP_2,
+                                  WM1811_JACKDET_MODE_MASK,
+                                  WM1811_JACKDET_MODE_AUDIO);
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops wm8994_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(wm8994_suspend, wm8994_resume)
+};
+
 static struct platform_driver wm8994_codec_driver = {
        .driver = {
-                  .name = "wm8994-codec",
-                  .owner = THIS_MODULE,
-                  },
+               .name = "wm8994-codec",
+               .owner = THIS_MODULE,
+               .pm = &wm8994_pm_ops,
+       },
        .probe = wm8994_probe,
        .remove = __devexit_p(wm8994_remove),
 };