OSDN Git Service

Merge remote-tracking branches 'asoc/topic/adau1977', 'asoc/topic/adav80x', 'asoc...
[uclinux-h8/linux.git] / sound / soc / codecs / arizona.c
index eff4b4d..88f6df2 100644 (file)
@@ -208,11 +208,12 @@ static const struct snd_soc_dapm_widget arizona_spkr =
 
 int arizona_init_spk(struct snd_soc_codec *codec)
 {
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
        struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
        struct arizona *arizona = priv->arizona;
        int ret;
 
-       ret = snd_soc_dapm_new_controls(&codec->dapm, &arizona_spkl, 1);
+       ret = snd_soc_dapm_new_controls(dapm, &arizona_spkl, 1);
        if (ret != 0)
                return ret;
 
@@ -220,8 +221,7 @@ int arizona_init_spk(struct snd_soc_codec *codec)
        case WM8997:
                break;
        default:
-               ret = snd_soc_dapm_new_controls(&codec->dapm,
-                                               &arizona_spkr, 1);
+               ret = snd_soc_dapm_new_controls(dapm, &arizona_spkr, 1);
                if (ret != 0)
                        return ret;
                break;
@@ -258,13 +258,14 @@ static const struct snd_soc_dapm_route arizona_mono_routes[] = {
 
 int arizona_init_mono(struct snd_soc_codec *codec)
 {
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
        struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
        struct arizona *arizona = priv->arizona;
        int i;
 
        for (i = 0; i < ARIZONA_MAX_OUTPUT; ++i) {
                if (arizona->pdata.out_mono[i])
-                       snd_soc_dapm_add_routes(&codec->dapm,
+                       snd_soc_dapm_add_routes(dapm,
                                                &arizona_mono_routes[i], 1);
        }
 
@@ -274,6 +275,7 @@ EXPORT_SYMBOL_GPL(arizona_init_mono);
 
 int arizona_init_gpio(struct snd_soc_codec *codec)
 {
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
        struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
        struct arizona *arizona = priv->arizona;
        int i;
@@ -281,23 +283,21 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
        switch (arizona->type) {
        case WM5110:
        case WM8280:
-               snd_soc_dapm_disable_pin(&codec->dapm, "DRC2 Signal Activity");
+               snd_soc_dapm_disable_pin(dapm, "DRC2 Signal Activity");
                break;
        default:
                break;
        }
 
-       snd_soc_dapm_disable_pin(&codec->dapm, "DRC1 Signal Activity");
+       snd_soc_dapm_disable_pin(dapm, "DRC1 Signal Activity");
 
        for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
                switch (arizona->pdata.gpio_defaults[i] & ARIZONA_GPN_FN_MASK) {
                case ARIZONA_GP_FN_DRC1_SIGNAL_DETECT:
-                       snd_soc_dapm_enable_pin(&codec->dapm,
-                                               "DRC1 Signal Activity");
+                       snd_soc_dapm_enable_pin(dapm, "DRC1 Signal Activity");
                        break;
                case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT:
-                       snd_soc_dapm_enable_pin(&codec->dapm,
-                                               "DRC2 Signal Activity");
+                       snd_soc_dapm_enable_pin(dapm, "DRC2 Signal Activity");
                        break;
                default:
                        break;
@@ -851,6 +851,134 @@ int arizona_hp_ev(struct snd_soc_dapm_widget *w,
 }
 EXPORT_SYMBOL_GPL(arizona_hp_ev);
 
+static int arizona_dvfs_enable(struct snd_soc_codec *codec)
+{
+       const struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = priv->arizona;
+       int ret;
+
+       ret = regulator_set_voltage(arizona->dcvdd, 1800000, 1800000);
+       if (ret) {
+               dev_err(codec->dev, "Failed to boost DCVDD: %d\n", ret);
+               return ret;
+       }
+
+       ret = regmap_update_bits(arizona->regmap,
+                                ARIZONA_DYNAMIC_FREQUENCY_SCALING_1,
+                                ARIZONA_SUBSYS_MAX_FREQ,
+                                ARIZONA_SUBSYS_MAX_FREQ);
+       if (ret) {
+               dev_err(codec->dev, "Failed to enable subsys max: %d\n", ret);
+               regulator_set_voltage(arizona->dcvdd, 1200000, 1800000);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int arizona_dvfs_disable(struct snd_soc_codec *codec)
+{
+       const struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = priv->arizona;
+       int ret;
+
+       ret = regmap_update_bits(arizona->regmap,
+                                ARIZONA_DYNAMIC_FREQUENCY_SCALING_1,
+                                ARIZONA_SUBSYS_MAX_FREQ, 0);
+       if (ret) {
+               dev_err(codec->dev, "Failed to disable subsys max: %d\n", ret);
+               return ret;
+       }
+
+       ret = regulator_set_voltage(arizona->dcvdd, 1200000, 1800000);
+       if (ret) {
+               dev_err(codec->dev, "Failed to unboost DCVDD: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags)
+{
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+
+       mutex_lock(&priv->dvfs_lock);
+
+       if (!priv->dvfs_cached && !priv->dvfs_reqs) {
+               ret = arizona_dvfs_enable(codec);
+               if (ret)
+                       goto err;
+       }
+
+       priv->dvfs_reqs |= flags;
+err:
+       mutex_unlock(&priv->dvfs_lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_dvfs_up);
+
+int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags)
+{
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       unsigned int old_reqs;
+       int ret = 0;
+
+       mutex_lock(&priv->dvfs_lock);
+
+       old_reqs = priv->dvfs_reqs;
+       priv->dvfs_reqs &= ~flags;
+
+       if (!priv->dvfs_cached && old_reqs && !priv->dvfs_reqs)
+               ret = arizona_dvfs_disable(codec);
+
+       mutex_unlock(&priv->dvfs_lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_dvfs_down);
+
+int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
+                          struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+
+       mutex_lock(&priv->dvfs_lock);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               if (priv->dvfs_reqs)
+                       ret = arizona_dvfs_enable(codec);
+
+               priv->dvfs_cached = false;
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               /* We must ensure DVFS is disabled before the codec goes into
+                * suspend so that we are never in an illegal state of DVFS
+                * enabled without enough DCVDD
+                */
+               priv->dvfs_cached = true;
+
+               if (priv->dvfs_reqs)
+                       ret = arizona_dvfs_disable(codec);
+               break;
+       default:
+               break;
+       }
+
+       mutex_unlock(&priv->dvfs_lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_dvfs_sysclk_ev);
+
+void arizona_init_dvfs(struct arizona_priv *priv)
+{
+       mutex_init(&priv->dvfs_lock);
+}
+EXPORT_SYMBOL_GPL(arizona_init_dvfs);
+
 static unsigned int arizona_sysclk_48k_rates[] = {
        6144000,
        12288000,
@@ -1266,7 +1394,7 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
        struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
        struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
        int base = dai->driver->base;
-       int i, sr_val;
+       int i, sr_val, ret;
 
        /*
         * We will need to be more flexible than this in future,
@@ -1282,6 +1410,23 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
        }
        sr_val = i;
 
+       switch (priv->arizona->type) {
+       case WM5102:
+       case WM8997:
+               if (arizona_sr_vals[sr_val] >= 88200)
+                       ret = arizona_dvfs_up(codec, ARIZONA_DVFS_SR1_RQ);
+               else
+                       ret = arizona_dvfs_down(codec, ARIZONA_DVFS_SR1_RQ);
+
+               if (ret) {
+                       arizona_aif_err(dai, "Failed to change DVFS %d\n", ret);
+                       return ret;
+               }
+               break;
+       default:
+               break;
+       }
+
        switch (dai_priv->clk) {
        case ARIZONA_CLK_SYSCLK:
                switch (priv->arizona->type) {
@@ -1474,6 +1619,7 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
                                  int clk_id, unsigned int freq, int dir)
 {
        struct snd_soc_codec *codec = dai->codec;
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
        struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
        struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
        struct snd_soc_dapm_route routes[2];
@@ -1504,15 +1650,15 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
 
        routes[0].source = arizona_dai_clk_str(dai_priv->clk);
        routes[1].source = arizona_dai_clk_str(dai_priv->clk);
-       snd_soc_dapm_del_routes(&codec->dapm, routes, ARRAY_SIZE(routes));
+       snd_soc_dapm_del_routes(dapm, routes, ARRAY_SIZE(routes));
 
        routes[0].source = arizona_dai_clk_str(clk_id);
        routes[1].source = arizona_dai_clk_str(clk_id);
-       snd_soc_dapm_add_routes(&codec->dapm, routes, ARRAY_SIZE(routes));
+       snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes));
 
        dai_priv->clk = clk_id;
 
-       return snd_soc_dapm_sync(&codec->dapm);
+       return snd_soc_dapm_sync(dapm);
 }
 
 static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate)