ASoC: Acknowledge WM8962 interrupts before acting on them
[firefly-linux-kernel-4.4.55.git] / sound / soc / codecs / wm8962.c
index b0cb4368d1b4aca2d498f29d15d7c99b6a831b6a..60d740ebeb5bb8b7502dd93b5b2ece605407d70a 100644 (file)
@@ -1984,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.
@@ -2001,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]);
 
@@ -2121,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[] = {
@@ -2186,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;
 
@@ -2205,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:
@@ -2765,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);
@@ -2846,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:
@@ -2878,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 */
@@ -2920,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)
@@ -2931,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;
@@ -2987,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);
 
@@ -3263,16 +3294,31 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
 
        dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
 
-       /* This should be a massive overestimate */
-       timeout = msecs_to_jiffies(1);
+       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);
 
-       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)
@@ -3363,6 +3409,9 @@ 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);
@@ -3387,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;
 }
 
@@ -3871,6 +3917,9 @@ 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 */