ASoC: wm_adsp: Enable DVFS for ADSP2
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / sound / soc / codecs / wm_adsp.c
index c72d3fa8572740f9c317a73fc5ed8bbbb7c5a767..a41742da20cbe84a7e20b1b4bfb21c7815dfd3a2 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #define ADSP1_START_SHIFT                      0  /* DSP1_START */
 #define ADSP1_START_WIDTH                      1  /* DSP1_START */
 
-#define ADSP2_CONTROL 0
-#define ADSP2_STATUS1 4
+#define ADSP2_CONTROL  0
+#define ADSP2_CLOCKING 1
+#define ADSP2_STATUS1  4
 
 /*
  * ADSP2 Control
 #define ADSP2_START_SHIFT                      0  /* DSP1_START */
 #define ADSP2_START_WIDTH                      1  /* DSP1_START */
 
+/*
+ * ADSP2 clocking
+ */
+#define ADSP2_CLK_SEL_MASK                0x0007  /* CLK_SEL_ENA */
+#define ADSP2_CLK_SEL_SHIFT                    0  /* CLK_SEL_ENA */
+#define ADSP2_CLK_SEL_WIDTH                    3  /* CLK_SEL_ENA */
+
 /*
  * ADSP2 Status 1
  */
@@ -530,10 +539,41 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
        struct snd_soc_codec *codec = w->codec;
        struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
        struct wm_adsp *dsp = &dsps[w->shift];
+       unsigned int val;
        int ret;
 
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
+               if (dsp->dvfs) {
+                       ret = regmap_read(dsp->regmap,
+                                         dsp->base + ADSP2_CLOCKING, &val);
+                       if (ret != 0) {
+                               dev_err(dsp->dev,
+                                       "Failed to read clocking: %d\n", ret);
+                               return ret;
+                       }
+
+                       if (val & ADSP2_CLK_SEL_MASK >= 3) {
+                               ret = regulator_enable(dsp->dvfs);
+                               if (ret != 0) {
+                                       dev_err(dsp->dev,
+                                               "Failed to enable supply: %d\n",
+                                               ret);
+                                       return ret;
+                               }
+
+                               ret = regulator_set_voltage(dsp->dvfs,
+                                                           1800000,
+                                                           1800000);
+                               if (ret != 0) {
+                                       dev_err(dsp->dev,
+                                               "Failed to raise supply: %d\n",
+                                               ret);
+                                       return ret;
+                               }
+                       }
+               }
+
                ret = wm_adsp2_ena(dsp);
                if (ret != 0)
                        return ret;
@@ -556,6 +596,21 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
        case SND_SOC_DAPM_PRE_PMD:
                regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
                                   ADSP2_SYS_ENA | ADSP2_START, 0);
+
+               if (dsp->dvfs) {
+                       ret = regulator_set_voltage(dsp->dvfs, 1200000,
+                                                   1800000);
+                       if (ret != 0)
+                               dev_warn(dsp->dev,
+                                        "Failed to lower supply: %d\n",
+                                        ret);
+
+                       ret = regulator_disable(dsp->dvfs);
+                       if (ret != 0)
+                               dev_err(dsp->dev,
+                                       "Failed to enable supply: %d\n",
+                                       ret);
+               }
                break;
 
        default:
@@ -569,3 +624,41 @@ err:
        return ret;
 }
 EXPORT_SYMBOL_GPL(wm_adsp2_event);
+
+int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
+{
+       int ret;
+
+       if (dvfs) {
+               adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
+               if (IS_ERR(adsp->dvfs)) {
+                       ret = PTR_ERR(adsp->dvfs);
+                       dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret);
+                       return ret;
+               }
+
+               ret = regulator_enable(adsp->dvfs);
+               if (ret != 0) {
+                       dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
+                               ret);
+                       return ret;
+               }
+
+               ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
+               if (ret != 0) {
+                       dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
+                               ret);
+                       return ret;
+               }
+
+               ret = regulator_disable(adsp->dvfs);
+               if (ret != 0) {
+                       dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
+                               ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_init);