ASoC: Drop unused state parameter from CODEC suspend callback
[deliverable/linux.git] / sound / soc / codecs / wm8994.c
index 6b73efd269917367293021edde0f330ee7c3b722..d9faa39d826d1972758b6c5eab6bec44f2dd6d90 100644 (file)
 #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
 
@@ -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 = wm8994->control_data;
+       struct wm8994 *control = wm8994->wm8994;
 
        switch (reg) {
        case WM8994_GPIO_1:
@@ -221,8 +291,10 @@ static int configure_clock(struct snd_soc_codec *codec)
         */
 
        /* 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;
@@ -231,10 +303,10 @@ static int configure_clock(struct snd_soc_codec *codec)
 
        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;
 }
@@ -708,6 +780,74 @@ SOC_SINGLE_TLV("MIXINL IN1RP Boost Volume", WM8994_INPUT_MIXER_1, 8, 1, 0,
               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)
 {
@@ -1768,7 +1908,7 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
                          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;
@@ -1865,6 +2005,8 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
        if (freq_out) {
                /* Enable VMID if we need it */
                if (!was_enabled) {
+                       active_reference(codec);
+
                        switch (control->type) {
                        case WM8994:
                                vmid_reference(codec);
@@ -1908,6 +2050,8 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
                        default:
                                break;
                        }
+
+                       active_dereference(codec);
                }
        }
 
@@ -2017,14 +2161,29 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
 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:
@@ -2077,7 +2236,23 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
                                            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:
@@ -2089,13 +2264,15 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
                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;
@@ -2357,6 +2534,11 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream,
        bclk |= best << WM8994_AIF1_BCLK_DIV_SHIFT;
 
        lrclk = bclk_rate / params_rate(params);
+       if (!lrclk) {
+               dev_err(dai->dev, "Unable to generate LRCLK from %dHz BCLK\n",
+                       bclk_rate);
+               return -EINVAL;
+       }
        dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n",
                lrclk, bclk_rate / lrclk);
 
@@ -2390,7 +2572,8 @@ static int wm8994_aif3_hw_params(struct snd_pcm_substream *substream,
                                 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;
 
@@ -2531,7 +2714,7 @@ static int wm8994_aif2_probe(struct snd_soc_dai *dai)
 #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,
@@ -2541,7 +2724,7 @@ static struct snd_soc_dai_ops wm8994_aif1_dai_ops = {
        .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,
@@ -2551,7 +2734,7 @@ static struct snd_soc_dai_ops wm8994_aif2_dai_ops = {
        .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,
 };
@@ -2618,10 +2801,10 @@ static struct snd_soc_dai_driver wm8994_dai[] = {
 };
 
 #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) {
@@ -2629,6 +2812,9 @@ static int wm8994_suspend(struct snd_soc_codec *codec, pm_message_t state)
                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);
@@ -2652,7 +2838,7 @@ static int wm8994_suspend(struct snd_soc_codec *codec, pm_message_t state)
 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;
 
@@ -2698,6 +2884,13 @@ static int wm8994_resume(struct snd_soc_codec *codec)
                                            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,
@@ -2810,8 +3003,8 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
                };
 
                /* 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",
@@ -2874,7 +3067,7 @@ int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
 {
        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)
@@ -2957,21 +3150,136 @@ static void wm8958_default_micdet(u16 status, void *data)
 {
        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");
+
+               wm8994->mic_detecting = false;
+               wm8994->jack_mic = true;
 
-       /* If nothing present then clear our statuses */
-       if (!(status & WM8958_MICD_STS))
-               goto done;
+               wm8958_micd_set_rate(codec);
 
-       report = SND_JACK_MICROPHONE;
+               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;
 
-       /* Everything else is buttons; just assign slots */
-       if (status & 0x1c)
-               report |= SND_JACK_BTN_0;
+               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);
 
-done:
-       snd_soc_jack_report(wm8994->micdet[0].jack, report,
-                           SND_JACK_BTN_0 | SND_JACK_MICROPHONE);
+                       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;
+
+               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;
 }
 
 /**
@@ -2994,7 +3302,8 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
                      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:
@@ -3011,15 +3320,50 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
                        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;
@@ -3030,20 +3374,48 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
 {
        struct wm8994_priv *wm8994 = data;
        struct snd_soc_codec *codec = wm8994->codec;
-       int reg;
+       int reg, count;
 
-       reg = snd_soc_read(codec, WM8958_MIC_DETECT_3);
-       if (reg < 0) {
-               dev_err(codec->dev, "Failed to read mic detect status: %d\n",
-                       reg);
-               return IRQ_NONE;
-       }
+       mutex_lock(&wm8994->accdet_lock);
 
-       if (!(reg & WM8958_MICD_VALID)) {
-               dev_dbg(codec->dev, "Mic detect data not valid\n");
-               goto out;
+       /*
+        * 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.
+        */
+       count = 10;
+       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);
+                       return IRQ_NONE;
+               }
+
+               if (!(reg & WM8958_MICD_VALID)) {
+                       dev_dbg(codec->dev, "Mic detect data not valid\n");
+                       goto out;
+               }
+
+               if (!(reg & WM8958_MICD_STS) || (reg & WM8958_MICD_LVL_MASK))
+                       break;
+
+               msleep(1);
+       } while (count--);
+
+       if (count == 0)
+               dev_warn(codec->dev, "No impedence range reported for jack\n");
+
 #ifndef CONFIG_SND_SOC_WM8994_MODULE
        trace_snd_soc_jack_irq(dev_name(codec->dev));
 #endif
@@ -3054,6 +3426,8 @@ 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;
 }
 
@@ -3094,14 +3468,19 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
        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]);
 
@@ -3163,6 +3542,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
                switch (wm8994->revision) {
                case 0:
                case 1:
+               case 2:
+               case 3:
                        wm8994->hubs.dcs_codes_l = -9;
                        wm8994->hubs.dcs_codes_r = -5;
                        break;
@@ -3178,14 +3559,14 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
                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(wm8994->control_data, WM8994_IRQ_TEMP_WARN,
+       wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_TEMP_WARN,
                           wm8994_temp_warn, "Thermal warning", codec);
-       wm8994_request_irq(wm8994->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)
@@ -3205,7 +3586,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
                                         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);
@@ -3214,7 +3595,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
                                 "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);
@@ -3223,7 +3604,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
                                 "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);
@@ -3248,9 +3629,24 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
                }
        }
 
+       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]);
@@ -3346,6 +3742,19 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
                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);
@@ -3457,28 +3866,28 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
        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);
-err:
-       kfree(wm8994);
+       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);
        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);
@@ -3486,24 +3895,27 @@ static int  wm8994_codec_remove(struct snd_soc_codec *codec)
        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;
 
@@ -3520,8 +3932,6 @@ static int  wm8994_codec_remove(struct snd_soc_codec *codec)
        if (wm8994->enh_eq)
                release_firmware(wm8994->enh_eq);
        kfree(wm8994->retune_mobile_texts);
-       kfree(wm8994->drc_texts);
-       kfree(wm8994);
 
        return 0;
 }
@@ -3564,18 +3974,7 @@ static struct platform_driver wm8994_codec_driver = {
        .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>");
This page took 0.037929 seconds and 5 git commands to generate.