These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / sound / soc / codecs / wm8960.c
index 8d7f632..5380798 100644 (file)
@@ -48,6 +48,9 @@
 #define WM8960_DISOP     0x40
 #define WM8960_DRES_MASK 0x30
 
+static bool is_pll_freq_available(unsigned int source, unsigned int target);
+static int wm8960_set_pll(struct snd_soc_codec *codec,
+               unsigned int freq_in, unsigned int freq_out);
 /*
  * wm8960 register cache
  * We can't read the WM8960 register space when we are
@@ -126,7 +129,12 @@ struct wm8960_priv {
        struct snd_soc_dapm_widget *rout1;
        struct snd_soc_dapm_widget *out3;
        bool deemph;
-       int playback_fs;
+       int lrclk;
+       int bclk;
+       int sysclk;
+       int clk_id;
+       int freq_in;
+       bool is_stream_in_use[2];
        struct wm8960_data pdata;
 };
 
@@ -162,8 +170,8 @@ static int wm8960_set_deemph(struct snd_soc_codec *codec)
        if (wm8960->deemph) {
                best = 1;
                for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
-                       if (abs(deemph_settings[i] - wm8960->playback_fs) <
-                           abs(deemph_settings[best] - wm8960->playback_fs))
+                       if (abs(deemph_settings[i] - wm8960->lrclk) <
+                           abs(deemph_settings[best] - wm8960->lrclk))
                                best = i;
                }
 
@@ -193,7 +201,7 @@ static int wm8960_put_deemph(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
-       int deemph = ucontrol->value.integer.value[0];
+       unsigned int deemph = ucontrol->value.integer.value[0];
 
        if (deemph > 1)
                return -EINVAL;
@@ -203,28 +211,38 @@ static int wm8960_put_deemph(struct snd_kcontrol *kcontrol,
        return wm8960_set_deemph(codec);
 }
 
-static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0);
-static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
 static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0);
 static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
-static const DECLARE_TLV_DB_SCALE(boost_tlv, -1200, 300, 1);
+static const DECLARE_TLV_DB_SCALE(lineinboost_tlv, -1500, 300, 1);
+static const unsigned int micboost_tlv[] = {
+       TLV_DB_RANGE_HEAD(2),
+       0, 1, TLV_DB_SCALE_ITEM(0, 1300, 0),
+       2, 3, TLV_DB_SCALE_ITEM(2000, 900, 0),
+};
 
 static const struct snd_kcontrol_new wm8960_snd_controls[] = {
 SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
-                0, 63, 0, adc_tlv),
+                0, 63, 0, inpga_tlv),
 SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,
        6, 1, 0),
 SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL,
-       7, 1, 0),
+       7, 1, 1),
 
 SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume",
-              WM8960_INBMIX1, 4, 7, 0, boost_tlv),
+              WM8960_INBMIX1, 4, 7, 0, lineinboost_tlv),
 SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume",
-              WM8960_INBMIX1, 1, 7, 0, boost_tlv),
+              WM8960_INBMIX1, 1, 7, 0, lineinboost_tlv),
 SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume",
-              WM8960_INBMIX2, 4, 7, 0, boost_tlv),
+              WM8960_INBMIX2, 4, 7, 0, lineinboost_tlv),
 SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume",
-              WM8960_INBMIX2, 1, 7, 0, boost_tlv),
+              WM8960_INBMIX2, 1, 7, 0, lineinboost_tlv),
+SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT1 Volume",
+               WM8960_RINPATH, 4, 3, 0, micboost_tlv),
+SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT1 Volume",
+               WM8960_LINPATH, 4, 3, 0, micboost_tlv),
 
 SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC,
                 0, 255, 0, dac_tlv),
@@ -445,7 +463,7 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec)
 {
        struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
        struct wm8960_data *pdata = &wm8960->pdata;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
        struct snd_soc_dapm_widget *w;
 
        snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets,
@@ -476,7 +494,7 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec)
         * and save the result.
         */
        list_for_each_entry(w, &codec->component.card->widgets, list) {
-               if (w->dapm != &codec->dapm)
+               if (w->dapm != dapm)
                        continue;
                if (strcmp(w->name, "LOUT1 PGA") == 0)
                        wm8960->lout1 = w;
@@ -563,6 +581,124 @@ static struct {
        {  8000, 5 },
 };
 
+/* -1 for reserved value */
+static const int sysclk_divs[] = { 1, -1, 2, -1 };
+
+/* Multiply 256 for internal 256 div */
+static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 };
+
+/* Multiply 10 to eliminate decimials */
+static const int bclk_divs[] = {
+       10, 15, 20, 30, 40, 55, 60, 80, 110,
+       120, 160, 220, 240, 320, 320, 320
+};
+
+static int wm8960_configure_clocking(struct snd_soc_codec *codec)
+{
+       struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+       int sysclk, bclk, lrclk, freq_out, freq_in;
+       u16 iface1 = snd_soc_read(codec, WM8960_IFACE1);
+       int i, j, k;
+
+       if (!(iface1 & (1<<6))) {
+               dev_dbg(codec->dev,
+                       "Codec is slave mode, no need to configure clock\n");
+               return 0;
+       }
+
+       if (wm8960->clk_id != WM8960_SYSCLK_MCLK && !wm8960->freq_in) {
+               dev_err(codec->dev, "No MCLK configured\n");
+               return -EINVAL;
+       }
+
+       freq_in = wm8960->freq_in;
+       bclk = wm8960->bclk;
+       lrclk = wm8960->lrclk;
+       /*
+        * If it's sysclk auto mode, check if the MCLK can provide sysclk or
+        * not. If MCLK can provide sysclk, using MCLK to provide sysclk
+        * directly. Otherwise, auto select a available pll out frequency
+        * and set PLL.
+        */
+       if (wm8960->clk_id == WM8960_SYSCLK_AUTO) {
+               /* disable the PLL and using MCLK to provide sysclk */
+               wm8960_set_pll(codec, 0, 0);
+               freq_out = freq_in;
+       } else if (wm8960->sysclk) {
+               freq_out = wm8960->sysclk;
+       } else {
+               dev_err(codec->dev, "No SYSCLK configured\n");
+               return -EINVAL;
+       }
+
+       /* check if the sysclk frequency is available. */
+       for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
+               if (sysclk_divs[i] == -1)
+                       continue;
+               sysclk = freq_out / sysclk_divs[i];
+               for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
+                       if (sysclk == dac_divs[j] * lrclk) {
+                               for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k)
+                                       if (sysclk == bclk * bclk_divs[k] / 10)
+                                               break;
+                               if (k != ARRAY_SIZE(bclk_divs))
+                                       break;
+                       }
+               }
+               if (j != ARRAY_SIZE(dac_divs))
+                       break;
+       }
+
+       if (i != ARRAY_SIZE(sysclk_divs)) {
+               goto configure_clock;
+       } else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) {
+               dev_err(codec->dev, "failed to configure clock\n");
+               return -EINVAL;
+       }
+       /* get a available pll out frequency and set pll */
+       for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
+               if (sysclk_divs[i] == -1)
+                       continue;
+               for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
+                       sysclk = lrclk * dac_divs[j];
+                       freq_out = sysclk * sysclk_divs[i];
+
+                       for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) {
+                               if (sysclk == bclk * bclk_divs[k] / 10 &&
+                                   is_pll_freq_available(freq_in, freq_out)) {
+                                       wm8960_set_pll(codec,
+                                                      freq_in, freq_out);
+                                       break;
+                               } else {
+                                       continue;
+                               }
+                       }
+                       if (k != ARRAY_SIZE(bclk_divs))
+                               break;
+               }
+               if (j != ARRAY_SIZE(dac_divs))
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(sysclk_divs)) {
+               dev_err(codec->dev, "failed to configure clock\n");
+               return -EINVAL;
+       }
+
+configure_clock:
+       /* configure sysclk clock */
+       snd_soc_update_bits(codec, WM8960_CLOCK1, 3 << 1, i << 1);
+
+       /* configure frame clock */
+       snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, j << 3);
+       snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, j << 6);
+
+       /* configure bit clock */
+       snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, k);
+
+       return 0;
+}
+
 static int wm8960_hw_params(struct snd_pcm_substream *substream,
                            struct snd_pcm_hw_params *params,
                            struct snd_soc_dai *dai)
@@ -570,8 +706,13 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_codec *codec = dai->codec;
        struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
        u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3;
+       bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
        int i;
 
+       wm8960->bclk = snd_soc_params_to_bclk(params);
+       if (params_channels(params) == 1)
+               wm8960->bclk *= 2;
+
        /* bit size */
        switch (params_width(params)) {
        case 16:
@@ -582,15 +723,21 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
        case 24:
                iface |= 0x0008;
                break;
+       case 32:
+               /* right justify mode does not support 32 word length */
+               if ((iface & 0x3) != 0) {
+                       iface |= 0x000c;
+                       break;
+               }
        default:
                dev_err(codec->dev, "unsupported width %d\n",
                        params_width(params));
                return -EINVAL;
        }
 
+       wm8960->lrclk = params_rate(params);
        /* Update filters for the new rate */
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               wm8960->playback_fs = params_rate(params);
+       if (tx) {
                wm8960_set_deemph(codec);
        } else {
                for (i = 0; i < ARRAY_SIZE(alc_rates); i++)
@@ -602,6 +749,25 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
 
        /* set iface */
        snd_soc_write(codec, WM8960_IFACE1, iface);
+
+       wm8960->is_stream_in_use[tx] = true;
+
+       if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON &&
+           !wm8960->is_stream_in_use[!tx])
+               return wm8960_configure_clocking(codec);
+
+       return 0;
+}
+
+static int wm8960_hw_free(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+       bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+       wm8960->is_stream_in_use[tx] = false;
+
        return 0;
 }
 
@@ -620,6 +786,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
                                      enum snd_soc_bias_level level)
 {
        struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+       u16 pm2 = snd_soc_read(codec, WM8960_POWER2);
        int ret;
 
        switch (level) {
@@ -627,7 +794,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
                break;
 
        case SND_SOC_BIAS_PREPARE:
-               switch (codec->dapm.bias_level) {
+               switch (snd_soc_codec_get_bias_level(codec)) {
                case SND_SOC_BIAS_STANDBY:
                        if (!IS_ERR(wm8960->mclk)) {
                                ret = clk_prepare_enable(wm8960->mclk);
@@ -639,11 +806,22 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
                                }
                        }
 
+                       ret = wm8960_configure_clocking(codec);
+                       if (ret)
+                               return ret;
+
                        /* Set VMID to 2x50k */
                        snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80);
                        break;
 
                case SND_SOC_BIAS_ON:
+                       /*
+                        * If it's sysclk auto mode, and the pll is enabled,
+                        * disable the pll
+                        */
+                       if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1))
+                               wm8960_set_pll(codec, 0, 0);
+
                        if (!IS_ERR(wm8960->mclk))
                                clk_disable_unprepare(wm8960->mclk);
                        break;
@@ -655,7 +833,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
                break;
 
        case SND_SOC_BIAS_STANDBY:
-               if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+               if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
                        regcache_sync(wm8960->regmap);
 
                        /* Enable anti-pop features */
@@ -691,8 +869,6 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
                break;
        }
 
-       codec->dapm.bias_level = level;
-
        return 0;
 }
 
@@ -700,6 +876,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
                                         enum snd_soc_bias_level level)
 {
        struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+       u16 pm2 = snd_soc_read(codec, WM8960_POWER2);
        int reg, ret;
 
        switch (level) {
@@ -707,7 +884,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
                break;
 
        case SND_SOC_BIAS_PREPARE:
-               switch (codec->dapm.bias_level) {
+               switch (snd_soc_codec_get_bias_level(codec)) {
                case SND_SOC_BIAS_STANDBY:
                        /* Enable anti pop mode */
                        snd_soc_update_bits(codec, WM8960_APOP1,
@@ -751,9 +928,21 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
                                        return ret;
                                }
                        }
+
+                       ret = wm8960_configure_clocking(codec);
+                       if (ret)
+                               return ret;
+
                        break;
 
                case SND_SOC_BIAS_ON:
+                       /*
+                        * If it's sysclk auto mode, and the pll is enabled,
+                        * disable the pll
+                        */
+                       if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1))
+                               wm8960_set_pll(codec, 0, 0);
+
                        if (!IS_ERR(wm8960->mclk))
                                clk_disable_unprepare(wm8960->mclk);
 
@@ -778,7 +967,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
                break;
 
        case SND_SOC_BIAS_STANDBY:
-               switch (codec->dapm.bias_level) {
+               switch (snd_soc_codec_get_bias_level(codec)) {
                case SND_SOC_BIAS_PREPARE:
                        /* Disable HP discharge */
                        snd_soc_update_bits(codec, WM8960_APOP2,
@@ -802,8 +991,6 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
                break;
        }
 
-       codec->dapm.bias_level = level;
-
        return 0;
 }
 
@@ -814,6 +1001,28 @@ struct _pll_div {
        u32 k:24;
 };
 
+static bool is_pll_freq_available(unsigned int source, unsigned int target)
+{
+       unsigned int Ndiv;
+
+       if (source == 0 || target == 0)
+               return false;
+
+       /* Scale up target to PLL operating frequency */
+       target *= 4;
+       Ndiv = target / source;
+
+       if (Ndiv < 6) {
+               source >>= 1;
+               Ndiv = target / source;
+       }
+
+       if ((Ndiv < 6) || (Ndiv > 12))
+               return false;
+
+       return true;
+}
+
 /* The size in bits of the pll divide multiplied by 10
  * to allow rounding later */
 #define FIXED_PLL_SIZE ((1 << 24) * 10)
@@ -865,10 +1074,9 @@ static int pll_factors(unsigned int source, unsigned int target,
        return 0;
 }
 
-static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
-               int source, unsigned int freq_in, unsigned int freq_out)
+static int wm8960_set_pll(struct snd_soc_codec *codec,
+               unsigned int freq_in, unsigned int freq_out)
 {
-       struct snd_soc_codec *codec = codec_dai->codec;
        u16 reg;
        static struct _pll_div pll_div;
        int ret;
@@ -908,6 +1116,20 @@ static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
        return 0;
 }
 
+static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+               int source, unsigned int freq_in, unsigned int freq_out)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+
+       wm8960->freq_in = freq_in;
+
+       if (pll_id == WM8960_SYSCLK_AUTO)
+               return 0;
+
+       return wm8960_set_pll(codec, freq_in, freq_out);
+}
+
 static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
                int div_id, int div)
 {
@@ -950,18 +1172,47 @@ static int wm8960_set_bias_level(struct snd_soc_codec *codec,
        return wm8960->set_bias_level(codec, level);
 }
 
+static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+                                       unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+
+       switch (clk_id) {
+       case WM8960_SYSCLK_MCLK:
+               snd_soc_update_bits(codec, WM8960_CLOCK1,
+                                       0x1, WM8960_SYSCLK_MCLK);
+               break;
+       case WM8960_SYSCLK_PLL:
+               snd_soc_update_bits(codec, WM8960_CLOCK1,
+                                       0x1, WM8960_SYSCLK_PLL);
+               break;
+       case WM8960_SYSCLK_AUTO:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       wm8960->sysclk = freq;
+       wm8960->clk_id = clk_id;
+
+       return 0;
+}
+
 #define WM8960_RATES SNDRV_PCM_RATE_8000_48000
 
 #define WM8960_FORMATS \
        (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
-       SNDRV_PCM_FMTBIT_S24_LE)
+       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
 
 static const struct snd_soc_dai_ops wm8960_dai_ops = {
        .hw_params = wm8960_hw_params,
+       .hw_free = wm8960_hw_free,
        .digital_mute = wm8960_mute,
        .set_fmt = wm8960_set_dai_fmt,
        .set_clkdiv = wm8960_set_dai_clkdiv,
        .set_pll = wm8960_set_dai_pll,
+       .set_sysclk = wm8960_set_dai_sysclk,
 };
 
 static struct snd_soc_dai_driver wm8960_dai = {
@@ -1113,7 +1364,6 @@ MODULE_DEVICE_TABLE(of, wm8960_of_match);
 static struct i2c_driver wm8960_i2c_driver = {
        .driver = {
                .name = "wm8960",
-               .owner = THIS_MODULE,
                .of_match_table = wm8960_of_match,
        },
        .probe =    wm8960_i2c_probe,