ASoC: Acknowledge WM8962 interrupts before acting on them
[firefly-linux-kernel-4.4.55.git] / sound / soc / codecs / wm8962.c
index 500011eb8b2bdb31d88fa2a8df819c72626a97bc..60d740ebeb5bb8b7502dd93b5b2ece605407d70a 100644 (file)
@@ -58,6 +58,7 @@ struct wm8962_priv {
        int bclk;  /* Desired BCLK */
        int lrclk;
 
+       struct completion fll_lock;
        int fll_src;
        int fll_fref;
        int fll_fout;
@@ -77,6 +78,8 @@ struct wm8962_priv {
 #ifdef CONFIG_GPIOLIB
        struct gpio_chip gpio_chip;
 #endif
+
+       int irq;
 };
 
 /* We can't use the same notifier block for more than one supply and
@@ -1981,6 +1984,7 @@ static const unsigned int classd_tlv[] = {
        0, 6, TLV_DB_SCALE_ITEM(0, 150, 0),
        7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0),
 };
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
 
 /* The VU bits for the headphones are in a different register to the mute
  * bits and only take effect on the PGA if it is actually powered.
@@ -1998,12 +2002,12 @@ static int wm8962_put_hp_sw(struct snd_kcontrol *kcontrol,
                return 0;
 
        /* If the left PGA is enabled hit that VU bit... */
-       if (reg_cache[WM8962_PWR_MGMT_2] & WM8962_HPOUTL_PGA_ENA)
+       if (snd_soc_read(codec, WM8962_PWR_MGMT_2) & WM8962_HPOUTL_PGA_ENA)
                return snd_soc_write(codec, WM8962_HPOUTL_VOLUME,
                                     reg_cache[WM8962_HPOUTL_VOLUME]);
 
        /* ...otherwise the right.  The VU is stereo. */
-       if (reg_cache[WM8962_PWR_MGMT_2] & WM8962_HPOUTR_PGA_ENA)
+       if (snd_soc_read(codec, WM8962_PWR_MGMT_2) & WM8962_HPOUTR_PGA_ENA)
                return snd_soc_write(codec, WM8962_HPOUTR_VOLUME,
                                     reg_cache[WM8962_HPOUTR_VOLUME]);
 
@@ -2038,6 +2042,13 @@ static int wm8962_put_spk_sw(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
+static const char *cap_hpf_mode_text[] = {
+       "Hi-fi", "Application"
+};
+
+static const struct soc_enum cap_hpf_mode =
+       SOC_ENUM_SINGLE(WM8962_ADC_DAC_CONTROL_2, 10, 2, cap_hpf_mode_text);
+
 static const struct snd_kcontrol_new wm8962_snd_controls[] = {
 SOC_DOUBLE("Input Mixer Switch", WM8962_INPUT_MIXER_CONTROL_1, 3, 2, 1, 1),
 
@@ -2063,6 +2074,9 @@ SOC_DOUBLE_R("Capture Switch", WM8962_LEFT_INPUT_VOLUME,
             WM8962_RIGHT_INPUT_VOLUME, 7, 1, 1),
 SOC_DOUBLE_R("Capture ZC Switch", WM8962_LEFT_INPUT_VOLUME,
             WM8962_RIGHT_INPUT_VOLUME, 6, 1, 1),
+SOC_SINGLE("Capture HPF Switch", WM8962_ADC_DAC_CONTROL_1, 0, 1, 1),
+SOC_ENUM("Capture HPF Mode", cap_hpf_mode),
+SOC_SINGLE("Capture HPF Cutoff", WM8962_ADC_DAC_CONTROL_2, 7, 7, 0),
 
 SOC_DOUBLE_R_TLV("Sidetone Volume", WM8962_DAC_DSP_MIXING_1,
                 WM8962_DAC_DSP_MIXING_2, 4, 12, 0, st_tlv),
@@ -2108,6 +2122,18 @@ SOC_SINGLE_TLV("HPMIXR MIXINR Volume", WM8962_HEADPHONE_MIXER_4,
 
 SOC_SINGLE_TLV("Speaker Boost Volume", WM8962_CLASS_D_CONTROL_2, 0, 7, 0,
               classd_tlv),
+
+SOC_SINGLE("EQ Switch", WM8962_EQ1, WM8962_EQ_ENA_SHIFT, 1, 0),
+SOC_DOUBLE_R_TLV("EQ1 Volume", WM8962_EQ2, WM8962_EQ22,
+                WM8962_EQL_B1_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ2 Volume", WM8962_EQ2, WM8962_EQ22,
+                WM8962_EQL_B2_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ3 Volume", WM8962_EQ2, WM8962_EQ22,
+                WM8962_EQL_B3_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ4 Volume", WM8962_EQ3, WM8962_EQ23,
+                WM8962_EQL_B4_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23,
+                WM8962_EQL_B5_GAIN_SHIFT, 31, 0, eq_tlv),
 };
 
 static const struct snd_kcontrol_new wm8962_spk_mono_controls[] = {
@@ -2173,6 +2199,8 @@ static int sysclk_event(struct snd_soc_dapm_widget *w,
                        struct snd_kcontrol *kcontrol, int event)
 {
        struct snd_soc_codec *codec = w->codec;
+       struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+       unsigned long timeout;
        int src;
        int fll;
 
@@ -2192,9 +2220,19 @@ static int sysclk_event(struct snd_soc_dapm_widget *w,
 
        switch (event) {
        case SND_SOC_DAPM_PRE_PMU:
-               if (fll)
+               if (fll) {
                        snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1,
                                            WM8962_FLL_ENA, WM8962_FLL_ENA);
+                       if (wm8962->irq) {
+                               timeout = msecs_to_jiffies(5);
+                               timeout = wait_for_completion_timeout(&wm8962->fll_lock,
+                                                                     timeout);
+
+                               if (timeout == 0)
+                                       dev_err(codec->dev,
+                                               "Timed out starting FLL\n");
+                       }
+               }
                break;
 
        case SND_SOC_DAPM_POST_PMD:
@@ -2467,6 +2505,7 @@ SND_SOC_DAPM_INPUT("IN3R"),
 SND_SOC_DAPM_INPUT("IN4L"),
 SND_SOC_DAPM_INPUT("IN4R"),
 SND_SOC_DAPM_INPUT("Beep"),
+SND_SOC_DAPM_INPUT("DMICDAT"),
 
 SND_SOC_DAPM_MICBIAS("MICBIAS", WM8962_PWR_MGMT_1, 1, 0),
 
@@ -2486,6 +2525,8 @@ SND_SOC_DAPM_MIXER("MIXINL", WM8962_PWR_MGMT_1, 5, 0,
 SND_SOC_DAPM_MIXER("MIXINR", WM8962_PWR_MGMT_1, 4, 0,
                   mixinr, ARRAY_SIZE(mixinr)),
 
+SND_SOC_DAPM_AIF_IN("DMIC", NULL, 0, WM8962_PWR_MGMT_1, 10, 0),
+
 SND_SOC_DAPM_ADC("ADCL", "Capture", WM8962_PWR_MGMT_1, 3, 0),
 SND_SOC_DAPM_ADC("ADCR", "Capture", WM8962_PWR_MGMT_1, 2, 0),
 
@@ -2563,13 +2604,17 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = {
 
        { "MICBIAS", NULL, "SYSCLK" },
 
+       { "DMIC", NULL, "DMICDAT" },
+
        { "ADCL", NULL, "SYSCLK" },
        { "ADCL", NULL, "TOCLK" },
        { "ADCL", NULL, "MIXINL" },
+       { "ADCL", NULL, "DMIC" },
 
        { "ADCR", NULL, "SYSCLK" },
        { "ADCR", NULL, "TOCLK" },
        { "ADCR", NULL, "MIXINR" },
+       { "ADCR", NULL, "DMIC" },
 
        { "STL", "Left", "ADCL" },
        { "STL", "Right", "ADCR" },
@@ -2745,18 +2790,44 @@ static const int bclk_divs[] = {
        1, -1, 2, 3, 4, -1, 6, 8, -1, 12, 16, 24, -1, 32, 32, 32
 };
 
+static const int sysclk_rates[] = {
+       64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536,
+};
+
 static void wm8962_configure_bclk(struct snd_soc_codec *codec)
 {
        struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
        int dspclk, i;
        int clocking2 = 0;
+       int clocking4 = 0;
        int aif2 = 0;
 
-       if (!wm8962->bclk) {
-               dev_dbg(codec->dev, "No BCLK rate configured\n");
+       if (!wm8962->sysclk_rate) {
+               dev_dbg(codec->dev, "No SYSCLK configured\n");
                return;
        }
 
+       if (!wm8962->bclk || !wm8962->lrclk) {
+               dev_dbg(codec->dev, "No audio clocks configured\n");
+               return;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(sysclk_rates); i++) {
+               if (sysclk_rates[i] == wm8962->sysclk_rate / wm8962->lrclk) {
+                       clocking4 |= i << WM8962_SYSCLK_RATE_SHIFT;
+                       break;
+               }
+       }
+
+       if (i == ARRAY_SIZE(sysclk_rates)) {
+               dev_err(codec->dev, "Unsupported sysclk ratio %d\n",
+                       wm8962->sysclk_rate / wm8962->lrclk);
+               return;
+       }
+
+       snd_soc_update_bits(codec, WM8962_CLOCKING_4,
+                           WM8962_SYSCLK_RATE_MASK, clocking4);
+
        dspclk = snd_soc_read(codec, WM8962_CLOCKING1);
        if (dspclk < 0) {
                dev_err(codec->dev, "Failed to read DSPCLK: %d\n", dspclk);
@@ -2826,6 +2897,8 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec,
                /* VMID 2*50k */
                snd_soc_update_bits(codec, WM8962_PWR_MGMT_1,
                                    WM8962_VMID_SEL_MASK, 0x80);
+
+               wm8962_configure_bclk(codec);
                break;
 
        case SND_SOC_BIAS_STANDBY:
@@ -2858,8 +2931,6 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec,
                        snd_soc_update_bits(codec, WM8962_CLOCKING2,
                                            WM8962_CLKREG_OVD,
                                            WM8962_CLKREG_OVD);
-
-                       wm8962_configure_bclk(codec);
                }
 
                /* VMID 2*250k */
@@ -2900,10 +2971,6 @@ static const struct {
        { 96000, 6 },
 };
 
-static const int sysclk_rates[] = {
-       64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536,
-};
-
 static int wm8962_hw_params(struct snd_pcm_substream *substream,
                            struct snd_pcm_hw_params *params,
                            struct snd_soc_dai *dai)
@@ -2911,41 +2978,27 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_codec *codec = rtd->codec;
        struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
-       int rate = params_rate(params);
        int i;
        int aif0 = 0;
        int adctl3 = 0;
-       int clocking4 = 0;
 
        wm8962->bclk = snd_soc_params_to_bclk(params);
        wm8962->lrclk = params_rate(params);
 
        for (i = 0; i < ARRAY_SIZE(sr_vals); i++) {
-               if (sr_vals[i].rate == rate) {
+               if (sr_vals[i].rate == wm8962->lrclk) {
                        adctl3 |= sr_vals[i].reg;
                        break;
                }
        }
        if (i == ARRAY_SIZE(sr_vals)) {
-               dev_err(codec->dev, "Unsupported rate %dHz\n", rate);
+               dev_err(codec->dev, "Unsupported rate %dHz\n", wm8962->lrclk);
                return -EINVAL;
        }
 
-       if (rate % 8000 == 0)
+       if (wm8962->lrclk % 8000 == 0)
                adctl3 |= WM8962_SAMPLE_RATE_INT_MODE;
 
-       for (i = 0; i < ARRAY_SIZE(sysclk_rates); i++) {
-               if (sysclk_rates[i] == wm8962->sysclk_rate / rate) {
-                       clocking4 |= i << WM8962_SYSCLK_RATE_SHIFT;
-                       break;
-               }
-       }
-       if (i == ARRAY_SIZE(sysclk_rates)) {
-               dev_err(codec->dev, "Unsupported sysclk ratio %d\n",
-                       wm8962->sysclk_rate / rate);
-               return -EINVAL;
-       }
-
        switch (params_format(params)) {
        case SNDRV_PCM_FORMAT_S16_LE:
                break;
@@ -2967,8 +3020,6 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
        snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_3,
                            WM8962_SAMPLE_RATE_INT_MODE |
                            WM8962_SAMPLE_RATE_MASK, adctl3);
-       snd_soc_update_bits(codec, WM8962_CLOCKING_4,
-                           WM8962_SYSCLK_RATE_MASK, clocking4);
 
        wm8962_configure_bclk(codec);
 
@@ -2990,7 +3041,6 @@ static int wm8962_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
        case WM8962_SYSCLK_FLL:
                wm8962->sysclk = WM8962_SYSCLK_FLL;
                src = 1 << WM8962_SYSCLK_SRC_SHIFT;
-               WARN_ON(freq != wm8962->fll_fout);
                break;
        default:
                return -EINVAL;
@@ -3172,12 +3222,12 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
        return 0;
 }
 
-static int wm8962_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
+static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
                          unsigned int Fref, unsigned int Fout)
 {
-       struct snd_soc_codec *codec = dai->codec;
        struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
        struct _fll_div fll_div;
+       unsigned long timeout;
        int ret;
        int fll1 = snd_soc_read(codec, WM8962_FLL_CONTROL_1) & WM8962_FLL_ENA;
 
@@ -3244,11 +3294,31 @@ static int wm8962_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
 
        dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
 
+       ret = 0;
+
+       if (fll1 & WM8962_FLL_ENA) {
+               /* This should be a massive overestimate but go even
+                * higher if we'll error out
+                */
+               if (wm8962->irq)
+                       timeout = msecs_to_jiffies(5);
+               else
+                       timeout = msecs_to_jiffies(1);
+
+               timeout = wait_for_completion_timeout(&wm8962->fll_lock,
+                                                     timeout);
+
+               if (timeout == 0 && wm8962->irq) {
+                       dev_err(codec->dev, "FLL lock timed out");
+                       ret = -ETIMEDOUT;
+               }
+       }
+
        wm8962->fll_fref = Fref;
        wm8962->fll_fout = Fout;
        wm8962->fll_src = source;
 
-       return 0;
+       return ret;
 }
 
 static int wm8962_mute(struct snd_soc_dai *dai, int mute)
@@ -3274,7 +3344,6 @@ static struct snd_soc_dai_ops wm8962_dai_ops = {
        .hw_params = wm8962_hw_params,
        .set_sysclk = wm8962_set_dai_sysclk,
        .set_fmt = wm8962_set_dai_fmt,
-       .set_pll = wm8962_set_fll,
        .digital_mute = wm8962_mute,
 };
 
@@ -3340,6 +3409,14 @@ static irqreturn_t wm8962_irq(int irq, void *data)
        active = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2);
        active &= ~mask;
 
+       /* Acknowledge the interrupts */
+       snd_soc_write(codec, WM8962_INTERRUPT_STATUS_2, active);
+
+       if (active & WM8962_FLL_LOCK_EINT) {
+               dev_dbg(codec->dev, "FLL locked\n");
+               complete(&wm8962->fll_lock);
+       }
+
        if (active & WM8962_FIFOS_ERR_EINT)
                dev_err(codec->dev, "FIFO error\n");
 
@@ -3359,9 +3436,6 @@ static irqreturn_t wm8962_irq(int irq, void *data)
                                      msecs_to_jiffies(250));
        }
 
-       /* Acknowledge the interrupts */
-       snd_soc_write(codec, WM8962_INTERRUPT_STATUS_2, active);
-
        return IRQ_HANDLED;
 }
 
@@ -3705,13 +3779,13 @@ static int wm8962_probe(struct snd_soc_codec *codec)
        int ret;
        struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
        struct wm8962_pdata *pdata = dev_get_platdata(codec->dev);
-       struct i2c_client *i2c = container_of(codec->dev, struct i2c_client,
-                                             dev);
        u16 *reg_cache = codec->reg_cache;
        int i, trigger, irq_pol;
+       bool dmicclk, dmicdat;
 
        wm8962->codec = codec;
        INIT_DELAYED_WORK(&wm8962->mic_work, wm8962_mic_work);
+       init_completion(&wm8962->fll_lock);
 
        codec->cache_sync = 1;
        codec->dapm.idle_bias_off = 1;
@@ -3843,12 +3917,38 @@ static int wm8962_probe(struct snd_soc_codec *codec)
        snd_soc_update_bits(codec, WM8962_HPOUTR_VOLUME,
                            WM8962_HPOUT_VU, WM8962_HPOUT_VU);
 
+       /* Stereo control for EQ */
+       snd_soc_update_bits(codec, WM8962_EQ1, WM8962_EQ_SHARED_COEFF, 0);
+
        wm8962_add_widgets(codec);
 
+       /* Save boards having to disable DMIC when not in use */
+       dmicclk = false;
+       dmicdat = false;
+       for (i = 0; i < WM8962_MAX_GPIO; i++) {
+               switch (snd_soc_read(codec, WM8962_GPIO_BASE + i)
+                       & WM8962_GP2_FN_MASK) {
+               case WM8962_GPIO_FN_DMICCLK:
+                       dmicclk = true;
+                       break;
+               case WM8962_GPIO_FN_DMICDAT:
+                       dmicdat = true;
+                       break;
+               default:
+                       break;
+               }
+       }
+       if (!dmicclk || !dmicdat) {
+               dev_dbg(codec->dev, "DMIC not in use, disabling\n");
+               snd_soc_dapm_nc_pin(&codec->dapm, "DMICDAT");
+       }
+       if (dmicclk != dmicdat)
+               dev_warn(codec->dev, "DMIC GPIOs partially configured\n");
+
        wm8962_init_beep(codec);
        wm8962_init_gpio(codec);
 
-       if (i2c->irq) {
+       if (wm8962->irq) {
                if (pdata && pdata->irq_active_low) {
                        trigger = IRQF_TRIGGER_LOW;
                        irq_pol = WM8962_IRQ_POL;
@@ -3860,17 +3960,19 @@ static int wm8962_probe(struct snd_soc_codec *codec)
                snd_soc_update_bits(codec, WM8962_INTERRUPT_CONTROL,
                                    WM8962_IRQ_POL, irq_pol);
 
-               ret = request_threaded_irq(i2c->irq, NULL, wm8962_irq,
+               ret = request_threaded_irq(wm8962->irq, NULL, wm8962_irq,
                                           trigger | IRQF_ONESHOT,
                                           "wm8962", codec);
                if (ret != 0) {
                        dev_err(codec->dev, "Failed to request IRQ %d: %d\n",
-                               i2c->irq, ret);
+                               wm8962->irq, ret);
+                       wm8962->irq = 0;
                        /* Non-fatal */
                } else {
-                       /* Enable error reporting IRQs by default */
+                       /* Enable some IRQs by default */
                        snd_soc_update_bits(codec,
                                            WM8962_INTERRUPT_STATUS_2_MASK,
+                                           WM8962_FLL_LOCK_EINT |
                                            WM8962_TEMP_SHUT_EINT |
                                            WM8962_FIFOS_ERR_EINT, 0);
                }
@@ -3889,12 +3991,10 @@ err:
 static int wm8962_remove(struct snd_soc_codec *codec)
 {
        struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
-       struct i2c_client *i2c = container_of(codec->dev, struct i2c_client,
-                                             dev);
        int i;
 
-       if (i2c->irq)
-               free_irq(i2c->irq, codec);
+       if (wm8962->irq)
+               free_irq(wm8962->irq, codec);
 
        cancel_delayed_work_sync(&wm8962->mic_work);
 
@@ -3918,6 +4018,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8962 = {
        .reg_cache_default = wm8962_reg,
        .volatile_register = wm8962_volatile_register,
        .readable_register = wm8962_readable_register,
+       .set_pll = wm8962_set_fll,
 };
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
@@ -3933,6 +4034,8 @@ static __devinit int wm8962_i2c_probe(struct i2c_client *i2c,
 
        i2c_set_clientdata(i2c, wm8962);
 
+       wm8962->irq = i2c->irq;
+
        ret = snd_soc_register_codec(&i2c->dev,
                                     &soc_codec_dev_wm8962, &wm8962_dai, 1);
        if (ret < 0)