]> git.karo-electronics.de Git - mv-sheeva.git/commitdiff
Merge branch 'for-3.2' into for-3.3
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Fri, 2 Dec 2011 17:44:55 +0000 (17:44 +0000)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Fri, 2 Dec 2011 17:44:55 +0000 (17:44 +0000)
1  2 
sound/soc/codecs/wm8994.c

index 83e8033f5719aec1ba7035bf82b91d0c6d2c695c,d0c545b73d7865c04b9fefd286b85b7fe63fc5b2..02ca2573214d3e05d4c27c90233ded58b6c9faa8
  #include "wm8994.h"
  #include "wm_hubs.h"
  
 +#define WM1811_JACKDET_MODE_NONE  0x0000
 +#define WM1811_JACKDET_MODE_JACK  0x0100
 +#define WM1811_JACKDET_MODE_MIC   0x0080
 +#define WM1811_JACKDET_MODE_AUDIO 0x0180
 +
  #define WM8994_NUM_DRC 3
  #define WM8994_NUM_EQ  3
  
@@@ -58,75 -53,10 +58,75 @@@ static int wm8994_retune_mobile_base[] 
        WM8994_AIF2_EQ_GAINS_1,
  };
  
 +static void wm8958_default_micdet(u16 status, void *data);
 +
 +static const struct wm8958_micd_rate micdet_rates[] = {
 +      { 32768,       true,  1, 4 },
 +      { 32768,       false, 1, 1 },
 +      { 44100 * 256, true,  7, 10 },
 +      { 44100 * 256, false, 7, 10 },
 +};
 +
 +static const struct wm8958_micd_rate jackdet_rates[] = {
 +      { 32768,       true,  0, 1 },
 +      { 32768,       false, 0, 1 },
 +      { 44100 * 256, true,  7, 10 },
 +      { 44100 * 256, false, 7, 10 },
 +};
 +
 +static void wm8958_micd_set_rate(struct snd_soc_codec *codec)
 +{
 +      struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 +      int best, i, sysclk, val;
 +      bool idle;
 +      const struct wm8958_micd_rate *rates;
 +      int num_rates;
 +
 +      if (wm8994->jack_cb != wm8958_default_micdet)
 +              return;
 +
 +      idle = !wm8994->jack_mic;
 +
 +      sysclk = snd_soc_read(codec, WM8994_CLOCKING_1);
 +      if (sysclk & WM8994_SYSCLK_SRC)
 +              sysclk = wm8994->aifclk[1];
 +      else
 +              sysclk = wm8994->aifclk[0];
 +
 +      if (wm8994->pdata && wm8994->pdata->micd_rates) {
 +              rates = wm8994->pdata->micd_rates;
 +              num_rates = wm8994->pdata->num_micd_rates;
 +      } else if (wm8994->jackdet) {
 +              rates = jackdet_rates;
 +              num_rates = ARRAY_SIZE(jackdet_rates);
 +      } else {
 +              rates = micdet_rates;
 +              num_rates = ARRAY_SIZE(micdet_rates);
 +      }
 +
 +      best = 0;
 +      for (i = 0; i < num_rates; i++) {
 +              if (rates[i].idle != idle)
 +                      continue;
 +              if (abs(rates[i].sysclk - sysclk) <
 +                  abs(rates[best].sysclk - sysclk))
 +                      best = i;
 +              else if (rates[best].idle != idle)
 +                      best = i;
 +      }
 +
 +      val = rates[best].start << WM8958_MICD_BIAS_STARTTIME_SHIFT
 +              | rates[best].rate << WM8958_MICD_RATE_SHIFT;
 +
 +      snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
 +                          WM8958_MICD_BIAS_STARTTIME_MASK |
 +                          WM8958_MICD_RATE_MASK, val);
 +}
 +
  static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg)
  {
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 -      struct wm8994 *control = codec->control_data;
 +      struct wm8994 *control = wm8994->wm8994;
  
        switch (reg) {
        case WM8994_GPIO_1:
@@@ -291,10 -221,8 +291,10 @@@ static int configure_clock(struct snd_s
         */
  
        /* If they're equal it doesn't matter which is used */
 -      if (wm8994->aifclk[0] == wm8994->aifclk[1])
 +      if (wm8994->aifclk[0] == wm8994->aifclk[1]) {
 +              wm8958_micd_set_rate(codec);
                return 0;
 +      }
  
        if (wm8994->aifclk[0] < wm8994->aifclk[1])
                new = WM8994_SYSCLK_SRC;
  
        change = snd_soc_update_bits(codec, WM8994_CLOCKING_1,
                                     WM8994_SYSCLK_SRC, new);
 -      if (!change)
 -              return 0;
 +      if (change)
 +              snd_soc_dapm_sync(&codec->dapm);
  
 -      snd_soc_dapm_sync(&codec->dapm);
 +      wm8958_micd_set_rate(codec);
  
        return 0;
  }
@@@ -780,74 -708,6 +780,74 @@@ SOC_SINGLE_TLV("MIXINL IN1RP Boost Volu
               mixin_boost_tlv),
  };
  
 +/* We run all mode setting through a function to enforce audio mode */
 +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->active_refcount)
 +              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)
 +{
 +      struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 +
 +      mutex_lock(&wm8994->accdet_lock);
 +
 +      wm8994->active_refcount++;
 +
 +      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);
 +              }
 +      }
 +
 +      mutex_unlock(&wm8994->accdet_lock);
 +}
 +
 +static void active_dereference(struct snd_soc_codec *codec)
 +{
 +      struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 +      u16 mode;
 +
 +      mutex_lock(&wm8994->accdet_lock);
 +
 +      wm8994->active_refcount--;
 +
 +      dev_dbg(codec->dev, "Active refcount decremented, now %d\n",
 +              wm8994->active_refcount);
 +
 +      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;
 +
 +                      snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
 +                                          WM1811_JACKDET_MODE_MASK,
 +                                          mode);
 +              }
 +      }
 +
 +      mutex_unlock(&wm8994->accdet_lock);
 +}
 +
  static int clk_sys_event(struct snd_soc_dapm_widget *w,
                         struct snd_kcontrol *kcontrol, int event)
  {
@@@ -1465,15 -1325,15 +1465,15 @@@ SND_SOC_DAPM_DAC("DAC1R", NULL, WM8994_
  };
  
  static const struct snd_soc_dapm_widget wm8994_adc_revd_widgets[] = {
- SND_SOC_DAPM_MUX_E("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux,
-                  adc_mux_ev, SND_SOC_DAPM_PRE_PMU),
- SND_SOC_DAPM_MUX_E("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux,
-                  adc_mux_ev, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_VIRT_MUX_E("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux,
+                       adc_mux_ev, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_VIRT_MUX_E("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux,
+                       adc_mux_ev, SND_SOC_DAPM_PRE_PMU),
  };
  
  static const struct snd_soc_dapm_widget wm8994_adc_widgets[] = {
- SND_SOC_DAPM_MUX("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux),
- SND_SOC_DAPM_MUX("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux),
+ SND_SOC_DAPM_VIRT_MUX("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux),
+ SND_SOC_DAPM_VIRT_MUX("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux),
  };
  
  static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = {
@@@ -1908,7 -1768,7 +1908,7 @@@ static int _wm8994_set_fll(struct snd_s
                          unsigned int freq_in, unsigned int freq_out)
  {
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 -      struct wm8994 *control = codec->control_data;
 +      struct wm8994 *control = wm8994->wm8994;
        int reg_offset, ret;
        struct fll_div fll;
        u16 reg, aif1, aif2;
        if (freq_out) {
                /* Enable VMID if we need it */
                if (!was_enabled) {
 +                      active_reference(codec);
 +
                        switch (control->type) {
                        case WM8994:
                                vmid_reference(codec);
                        default:
                                break;
                        }
 +
 +                      active_dereference(codec);
                }
        }
  
@@@ -2161,29 -2017,14 +2161,29 @@@ static int wm8994_set_dai_sysclk(struc
  static int wm8994_set_bias_level(struct snd_soc_codec *codec,
                                 enum snd_soc_bias_level level)
  {
 -      struct wm8994 *control = codec->control_data;
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 +      struct wm8994 *control = wm8994->wm8994;
  
        switch (level) {
        case SND_SOC_BIAS_ON:
                break;
  
        case SND_SOC_BIAS_PREPARE:
 +              /* MICBIAS into regulating mode */
 +              switch (control->type) {
 +              case WM8958:
 +              case WM1811:
 +                      snd_soc_update_bits(codec, WM8958_MICBIAS1,
 +                                          WM8958_MICB1_MODE, 0);
 +                      snd_soc_update_bits(codec, WM8958_MICBIAS2,
 +                                          WM8958_MICB2_MODE, 0);
 +                      break;
 +              default:
 +                      break;
 +              }
 +
 +              if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY)
 +                      active_reference(codec);
                break;
  
        case SND_SOC_BIAS_STANDBY:
                                            WM8994_LINEOUT2_DISCH);
                }
  
 +              if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE)
 +                      active_dereference(codec);
  
 +              /* MICBIAS into bypass mode on newer devices */
 +              switch (control->type) {
 +              case WM8958:
 +              case WM1811:
 +                      snd_soc_update_bits(codec, WM8958_MICBIAS1,
 +                                          WM8958_MICB1_MODE,
 +                                          WM8958_MICB1_MODE);
 +                      snd_soc_update_bits(codec, WM8958_MICBIAS2,
 +                                          WM8958_MICB2_MODE,
 +                                          WM8958_MICB2_MODE);
 +                      break;
 +              default:
 +                      break;
 +              }
                break;
  
        case SND_SOC_BIAS_OFF:
                break;
        }
        codec->dapm.bias_level = level;
 +
        return 0;
  }
  
  static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
  {
        struct snd_soc_codec *codec = dai->codec;
 -      struct wm8994 *control = codec->control_data;
 +      struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 +      struct wm8994 *control = wm8994->wm8994;
        int ms_reg;
        int aif1_reg;
        int ms = 0;
@@@ -2572,8 -2395,7 +2572,8 @@@ static int wm8994_aif3_hw_params(struc
                                 struct snd_soc_dai *dai)
  {
        struct snd_soc_codec *codec = dai->codec;
 -      struct wm8994 *control = codec->control_data;
 +      struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 +      struct wm8994 *control = wm8994->wm8994;
        int aif1_reg;
        int aif1 = 0;
  
@@@ -2714,7 -2536,7 +2714,7 @@@ static int wm8994_aif2_probe(struct snd
  #define WM8994_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
  
 -static struct snd_soc_dai_ops wm8994_aif1_dai_ops = {
 +static const struct snd_soc_dai_ops wm8994_aif1_dai_ops = {
        .set_sysclk     = wm8994_set_dai_sysclk,
        .set_fmt        = wm8994_set_dai_fmt,
        .hw_params      = wm8994_hw_params,
        .set_tristate   = wm8994_set_tristate,
  };
  
 -static struct snd_soc_dai_ops wm8994_aif2_dai_ops = {
 +static const struct snd_soc_dai_ops wm8994_aif2_dai_ops = {
        .set_sysclk     = wm8994_set_dai_sysclk,
        .set_fmt        = wm8994_set_dai_fmt,
        .hw_params      = wm8994_hw_params,
        .set_tristate   = wm8994_set_tristate,
  };
  
 -static struct snd_soc_dai_ops wm8994_aif3_dai_ops = {
 +static const struct snd_soc_dai_ops wm8994_aif3_dai_ops = {
        .hw_params      = wm8994_aif3_hw_params,
        .set_tristate   = wm8994_set_tristate,
  };
@@@ -2801,10 -2623,10 +2801,10 @@@ static struct snd_soc_dai_driver wm8994
  };
  
  #ifdef CONFIG_PM
 -static int wm8994_suspend(struct snd_soc_codec *codec, pm_message_t state)
 +static int wm8994_suspend(struct snd_soc_codec *codec)
  {
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 -      struct wm8994 *control = codec->control_data;
 +      struct wm8994 *control = wm8994->wm8994;
        int i, ret;
  
        switch (control->type) {
                snd_soc_update_bits(codec, WM8994_MICBIAS, WM8994_MICD_ENA, 0);
                break;
        case WM1811:
 +              snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
 +                                  WM1811_JACKDET_MODE_MASK, 0);
 +              /* Fall through */
        case WM8958:
                snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
                                    WM8958_MICD_ENA, 0);
  static int wm8994_resume(struct snd_soc_codec *codec)
  {
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 -      struct wm8994 *control = codec->control_data;
 +      struct wm8994 *control = wm8994->wm8994;
        int i, ret;
        unsigned int val, mask;
  
                                            WM8994_MICD_ENA, WM8994_MICD_ENA);
                break;
        case WM1811:
 +              if (wm8994->jackdet && wm8994->jack_cb) {
 +                      /* Restart from idle */
 +                      snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
 +                                          WM1811_JACKDET_MODE_MASK,
 +                                          WM1811_JACKDET_MODE_JACK);
 +                      break;
 +              }
        case WM8958:
                if (wm8994->jack_cb)
                        snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
@@@ -3003,8 -2815,8 +3003,8 @@@ static void wm8994_handle_pdata(struct 
                };
  
                /* We need an array of texts for the enum API */
 -              wm8994->drc_texts = kmalloc(sizeof(char *)
 -                                          * pdata->num_drc_cfgs, GFP_KERNEL);
 +              wm8994->drc_texts = devm_kzalloc(wm8994->codec->dev,
 +                          sizeof(char *) * pdata->num_drc_cfgs, GFP_KERNEL);
                if (!wm8994->drc_texts) {
                        dev_err(wm8994->codec->dev,
                                "Failed to allocate %d DRC config texts\n",
@@@ -3067,7 -2879,7 +3067,7 @@@ int wm8994_mic_detect(struct snd_soc_co
  {
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
        struct wm8994_micdet *micdet;
 -      struct wm8994 *control = codec->control_data;
 +      struct wm8994 *control = wm8994->wm8994;
        int reg;
  
        if (control->type != WM8994)
@@@ -3150,136 -2962,21 +3150,136 @@@ static void wm8958_default_micdet(u16 s
  {
        struct snd_soc_codec *codec = data;
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 -      int report = 0;
 +      int report;
 +
 +      dev_dbg(codec->dev, "MICDET %x\n", status);
 +
 +      /* Either nothing present or just starting detection */
 +      if (!(status & WM8958_MICD_STS)) {
 +              if (!wm8994->jackdet) {
 +                      /* If nothing present then clear our statuses */
 +                      dev_dbg(codec->dev, "Detected open circuit\n");
 +                      wm8994->jack_mic = false;
 +                      wm8994->mic_detecting = true;
 +
 +                      wm8958_micd_set_rate(codec);
 +
 +                      snd_soc_jack_report(wm8994->micdet[0].jack, 0,
 +                                          wm8994->btn_mask |
 +                                           SND_JACK_HEADSET);
 +              }
 +              return;
 +      }
 +
 +      /* If the measurement is showing a high impedence we've got a
 +       * microphone.
 +       */
 +      if (wm8994->mic_detecting && (status & 0x600)) {
 +              dev_dbg(codec->dev, "Detected microphone\n");
  
 -      /* If nothing present then clear our statuses */
 -      if (!(status & WM8958_MICD_STS))
 -              goto done;
 +              wm8994->mic_detecting = false;
 +              wm8994->jack_mic = true;
  
 -      report = SND_JACK_MICROPHONE;
 +              wm8958_micd_set_rate(codec);
  
 -      /* Everything else is buttons; just assign slots */
 -      if (status & 0x1c)
 -              report |= SND_JACK_BTN_0;
 +              snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADSET,
 +                                  SND_JACK_HEADSET);
 +      }
 +
 +
 +      if (wm8994->mic_detecting && status & 0x4) {
 +              dev_dbg(codec->dev, "Detected headphone\n");
 +              wm8994->mic_detecting = false;
 +
 +              wm8958_micd_set_rate(codec);
 +
 +              snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE,
 +                                  SND_JACK_HEADSET);
 +
 +              /* If we have jackdet that will detect removal */
 +              if (wm8994->jackdet) {
 +                      snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
 +                                          WM8958_MICD_ENA, 0);
 +
 +                      wm1811_jackdet_set_mode(codec,
 +                                              WM1811_JACKDET_MODE_JACK);
 +              }
 +      }
 +
 +      /* Report short circuit as a button */
 +      if (wm8994->jack_mic) {
 +              report = 0;
 +              if (status & 0x4)
 +                      report |= SND_JACK_BTN_0;
 +
 +              if (status & 0x8)
 +                      report |= SND_JACK_BTN_1;
  
 -done:
 -      snd_soc_jack_report(wm8994->micdet[0].jack, report,
 -                          SND_JACK_BTN_0 | SND_JACK_MICROPHONE);
 +              if (status & 0x10)
 +                      report |= SND_JACK_BTN_2;
 +
 +              if (status & 0x20)
 +                      report |= SND_JACK_BTN_3;
 +
 +              if (status & 0x40)
 +                      report |= SND_JACK_BTN_4;
 +
 +              if (status & 0x80)
 +                      report |= SND_JACK_BTN_5;
 +
 +              snd_soc_jack_report(wm8994->micdet[0].jack, report,
 +                                  wm8994->btn_mask);
 +      }
 +}
 +
 +static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
 +{
 +      struct wm8994_priv *wm8994 = data;
 +      struct snd_soc_codec *codec = wm8994->codec;
 +      int reg;
 +
 +      mutex_lock(&wm8994->accdet_lock);
 +
 +      reg = snd_soc_read(codec, WM1811_JACKDET_CTRL);
 +      if (reg < 0) {
 +              dev_err(codec->dev, "Failed to read jack status: %d\n", reg);
 +              mutex_unlock(&wm8994->accdet_lock);
 +              return IRQ_NONE;
 +      }
 +
 +      dev_dbg(codec->dev, "JACKDET %x\n", reg);
 +
 +      if (reg & WM1811_JACKDET_LVL) {
 +              dev_dbg(codec->dev, "Jack detected\n");
 +
 +              snd_soc_jack_report(wm8994->micdet[0].jack,
 +                                  SND_JACK_MECHANICAL, SND_JACK_MECHANICAL);
 +
 +              /*
 +               * Start off measument of microphone impedence to find
 +               * out what's actually there.
 +               */
 +              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);
 +
 +              wm8994->mic_detecting = false;
 +              wm8994->jack_mic = false;
 +              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);
 +
 +      return IRQ_HANDLED;
  }
  
  /**
@@@ -3302,8 -2999,7 +3302,8 @@@ int wm8958_mic_detect(struct snd_soc_co
                      wm8958_micdet_cb cb, void *cb_data)
  {
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 -      struct wm8994 *control = codec->control_data;
 +      struct wm8994 *control = wm8994->wm8994;
 +      u16 micd_lvl_sel;
  
        switch (control->type) {
        case WM1811:
                        cb_data = codec;
                }
  
 +              snd_soc_dapm_force_enable_pin(&codec->dapm, "CLK_SYS");
 +
                wm8994->micdet[0].jack = jack;
                wm8994->jack_cb = cb;
                wm8994->jack_cb_data = cb_data;
  
 -              snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
 -                                  WM8958_MICD_ENA, WM8958_MICD_ENA);
 +              wm8994->mic_detecting = true;
 +              wm8994->jack_mic = false;
 +
 +              wm8958_micd_set_rate(codec);
 +
 +              /* Detect microphones and short circuits by default */
 +              if (wm8994->pdata->micd_lvl_sel)
 +                      micd_lvl_sel = wm8994->pdata->micd_lvl_sel;
 +              else
 +                      micd_lvl_sel = 0x41;
 +
 +              wm8994->btn_mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 |
 +                      SND_JACK_BTN_2 | SND_JACK_BTN_3 |
 +                      SND_JACK_BTN_4 | SND_JACK_BTN_5;
 +
 +              snd_soc_update_bits(codec, WM8958_MIC_DETECT_2,
 +                                  WM8958_MICD_LVL_SEL_MASK, micd_lvl_sel);
 +
 +              WARN_ON(codec->dapm.bias_level > SND_SOC_BIAS_STANDBY);
 +
 +              /*
 +               * If we can use jack detection start off with that,
 +               * otherwise jump straight to microphone detection.
 +               */
 +              if (wm8994->jackdet) {
 +                      snd_soc_update_bits(codec, WM8994_LDO_1,
 +                                          WM8994_LDO1_DISCH, 0);
 +                      wm1811_jackdet_set_mode(codec,
 +                                              WM1811_JACKDET_MODE_JACK);
 +              } else {
 +                      snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
 +                                          WM8958_MICD_ENA, WM8958_MICD_ENA);
 +              }
 +
        } else {
                snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
                                    WM8958_MICD_ENA, 0);
 +              snd_soc_dapm_disable_pin(&codec->dapm, "CLK_SYS");
        }
  
        return 0;
@@@ -3376,18 -3037,6 +3376,18 @@@ static irqreturn_t wm8958_mic_irq(int i
        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);
 +              return IRQ_HANDLED;
 +      }
 +
        /* We may occasionally read a detection without an impedence
         * range being provided - if that happens loop again.
         */
        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);
                dev_warn(codec->dev, "Accessory detection with no callback\n");
  
  out:
 +      mutex_unlock(&wm8994->accdet_lock);
 +
        return IRQ_HANDLED;
  }
  
@@@ -3468,19 -3114,14 +3468,19 @@@ static int wm8994_codec_probe(struct sn
        codec->control_data = dev_get_drvdata(codec->dev->parent);
        control = codec->control_data;
  
 -      wm8994 = kzalloc(sizeof(struct wm8994_priv), GFP_KERNEL);
 +      wm8994 = devm_kzalloc(codec->dev, sizeof(struct wm8994_priv),
 +                            GFP_KERNEL);
        if (wm8994 == NULL)
                return -ENOMEM;
        snd_soc_codec_set_drvdata(codec, wm8994);
  
 +
 +      wm8994->wm8994 = dev_get_drvdata(codec->dev->parent);
        wm8994->pdata = dev_get_platdata(codec->dev->parent);
        wm8994->codec = codec;
  
 +      mutex_init(&wm8994->accdet_lock);
 +
        for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
                init_completion(&wm8994->fll_locked[i]);
  
                break;
        }
  
 -      wm8994_request_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR,
 +      wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_FIFOS_ERR,
                           wm8994_fifo_error, "FIFO error", codec);
 -      wm8994_request_irq(codec->control_data, WM8994_IRQ_TEMP_WARN,
 +      wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_TEMP_WARN,
                           wm8994_temp_warn, "Thermal warning", codec);
 -      wm8994_request_irq(codec->control_data, WM8994_IRQ_TEMP_SHUT,
 +      wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT,
                           wm8994_temp_shut, "Thermal shutdown", codec);
  
 -      ret = wm8994_request_irq(codec->control_data, WM8994_IRQ_DCS_DONE,
 +      ret = wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE,
                                 wm_hubs_dcs_done, "DC servo done",
                                 &wm8994->hubs);
        if (ret == 0)
                                         ret);
                }
  
 -              ret = wm8994_request_irq(codec->control_data,
 +              ret = wm8994_request_irq(wm8994->wm8994,
                                         WM8994_IRQ_MIC1_SHRT,
                                         wm8994_mic_irq, "Mic 1 short",
                                         wm8994);
                                 "Failed to request Mic1 short IRQ: %d\n",
                                 ret);
  
 -              ret = wm8994_request_irq(codec->control_data,
 +              ret = wm8994_request_irq(wm8994->wm8994,
                                         WM8994_IRQ_MIC2_DET,
                                         wm8994_mic_irq, "Mic 2 detect",
                                         wm8994);
                                 "Failed to request Mic2 detect IRQ: %d\n",
                                 ret);
  
 -              ret = wm8994_request_irq(codec->control_data,
 +              ret = wm8994_request_irq(wm8994->wm8994,
                                         WM8994_IRQ_MIC2_SHRT,
                                         wm8994_mic_irq, "Mic 2 short",
                                         wm8994);
                }
        }
  
 +      switch (control->type) {
 +      case WM1811:
 +              if (wm8994->revision > 1) {
 +                      ret = wm8994_request_irq(wm8994->wm8994,
 +                                               WM8994_IRQ_GPIO(6),
 +                                               wm1811_jackdet_irq, "JACKDET",
 +                                               wm8994);
 +                      if (ret == 0)
 +                              wm8994->jackdet = true;
 +              }
 +              break;
 +      default:
 +              break;
 +      }
 +
        wm8994->fll_locked_irq = true;
        for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) {
 -              ret = wm8994_request_irq(codec->control_data,
 +              ret = wm8994_request_irq(wm8994->wm8994,
                                         WM8994_IRQ_FLL1_LOCK + i,
                                         wm8994_fll_locked_irq, "FLL lock",
                                         &wm8994->fll_locked[i]);
                break;
        }
  
 +      /* Put MICBIAS into bypass mode by default on newer devices */
 +      switch (control->type) {
 +      case WM8958:
 +      case WM1811:
 +              snd_soc_update_bits(codec, WM8958_MICBIAS1,
 +                                  WM8958_MICB1_MODE, WM8958_MICB1_MODE);
 +              snd_soc_update_bits(codec, WM8958_MICBIAS2,
 +                                  WM8958_MICB2_MODE, WM8958_MICB2_MODE);
 +              break;
 +      default:
 +              break;
 +      }
 +
        wm8994_update_class_w(codec);
  
        wm8994_handle_pdata(wm8994);
        return 0;
  
  err_irq:
 -      wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT, wm8994);
 -      wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET, wm8994);
 -      wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, wm8994);
 +      if (wm8994->jackdet)
 +              wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_GPIO(6), wm8994);
 +      wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC2_SHRT, wm8994);
 +      wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC2_DET, wm8994);
 +      wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC1_SHRT, wm8994);
        if (wm8994->micdet_irq)
                free_irq(wm8994->micdet_irq, wm8994);
        for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
 -              wm8994_free_irq(codec->control_data, WM8994_IRQ_FLL1_LOCK + i,
 +              wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i,
                                &wm8994->fll_locked[i]);
 -      wm8994_free_irq(codec->control_data, WM8994_IRQ_DCS_DONE,
 +      wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE,
                        &wm8994->hubs);
 -      wm8994_free_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR, codec);
 -      wm8994_free_irq(codec->control_data, WM8994_IRQ_TEMP_SHUT, codec);
 -      wm8994_free_irq(codec->control_data, WM8994_IRQ_TEMP_WARN, codec);
 +      wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FIFOS_ERR, codec);
 +      wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT, codec);
 +      wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_WARN, codec);
  err:
 -      kfree(wm8994);
        return ret;
  }
  
  static int  wm8994_codec_remove(struct snd_soc_codec *codec)
  {
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 -      struct wm8994 *control = codec->control_data;
 +      struct wm8994 *control = wm8994->wm8994;
        int i;
  
        wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF);
        pm_runtime_disable(codec->dev);
  
        for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
 -              wm8994_free_irq(codec->control_data, WM8994_IRQ_FLL1_LOCK + i,
 +              wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i,
                                &wm8994->fll_locked[i]);
  
 -      wm8994_free_irq(codec->control_data, WM8994_IRQ_DCS_DONE,
 +      wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE,
                        &wm8994->hubs);
 -      wm8994_free_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR, codec);
 -      wm8994_free_irq(codec->control_data, WM8994_IRQ_TEMP_SHUT, codec);
 -      wm8994_free_irq(codec->control_data, WM8994_IRQ_TEMP_WARN, codec);
 +      wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FIFOS_ERR, codec);
 +      wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT, codec);
 +      wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_WARN, codec);
 +
 +      if (wm8994->jackdet)
 +              wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_GPIO(6), wm8994);
  
        switch (control->type) {
        case WM8994:
                if (wm8994->micdet_irq)
                        free_irq(wm8994->micdet_irq, wm8994);
 -              wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET,
 +              wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC2_DET,
                                wm8994);
 -              wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT,
 +              wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC1_SHRT,
                                wm8994);
 -              wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET,
 +              wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC1_DET,
                                wm8994);
                break;
  
        if (wm8994->enh_eq)
                release_firmware(wm8994->enh_eq);
        kfree(wm8994->retune_mobile_texts);
 -      kfree(wm8994->drc_texts);
 -      kfree(wm8994);
  
        return 0;
  }
@@@ -3975,7 -3586,18 +3975,7 @@@ static struct platform_driver wm8994_co
        .remove = __devexit_p(wm8994_remove),
  };
  
 -static __init int wm8994_init(void)
 -{
 -      return platform_driver_register(&wm8994_codec_driver);
 -}
 -module_init(wm8994_init);
 -
 -static __exit void wm8994_exit(void)
 -{
 -      platform_driver_unregister(&wm8994_codec_driver);
 -}
 -module_exit(wm8994_exit);
 -
 +module_platform_driver(wm8994_codec_driver);
  
  MODULE_DESCRIPTION("ASoC WM8994 driver");
  MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");