aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Fitzgerald <rf@opensource.wolfsonmicro.com>2015-06-02 06:53:33 -0400
committerMark Brown <broonie@kernel.org>2015-06-02 16:06:20 -0400
commit346d96836ca4af39dbfe65eceb7db812b1bfe68f (patch)
treefe2e5ceecf9a4b204f4738b724acf629260fc071
parentb601b58785d1440236a4180b1e3ec7bc7c1e5664 (diff)
ASoC: arizona: Export functions to control subsystem DVFS
The WM5102 and WM8997 codecs have an internal dynamic clock booster. When this booster is active, the DCVDD voltage must be increased. If all the currently active audio paths can run with the root SYSCLK we can disable the booster, allowing us to turn down DCVDD voltage to save power. Previously this was being done by having the booster enable bit set as a side-effect of the LDO1 regulator driver, which is unexpected behaviour of a regulator and not compatible with using an external regulator. [Originally this was documented as a feature of the internal LDO -- broonie] This patch exports functions to handle the booster enable and DCVDD voltage, with each relevant subsystem flagging whether it can currently run without the booster. Note that these subsystems are stateless and none of them are nestable, so there's no need for reference counting, we only need a simple boolean for each subsystem of whether their current condition could require the booster or will allow us to turn the codec down to lower operating power. Signed-off-by: Richard Fitzgerald <rf@opensource.wolfsonmicro.com> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--sound/soc/codecs/arizona.c128
-rw-r--r--sound/soc/codecs/arizona.h13
-rw-r--r--sound/soc/codecs/wm5102.c12
-rw-r--r--sound/soc/codecs/wm8997.c11
4 files changed, 157 insertions, 7 deletions
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index eff4b4d512b7..b2d8b048b825 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -851,6 +851,134 @@ int arizona_hp_ev(struct snd_soc_dapm_widget *w,
851} 851}
852EXPORT_SYMBOL_GPL(arizona_hp_ev); 852EXPORT_SYMBOL_GPL(arizona_hp_ev);
853 853
854static int arizona_dvfs_enable(struct snd_soc_codec *codec)
855{
856 const struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
857 struct arizona *arizona = priv->arizona;
858 int ret;
859
860 ret = regulator_set_voltage(arizona->dcvdd, 1800000, 1800000);
861 if (ret) {
862 dev_err(codec->dev, "Failed to boost DCVDD: %d\n", ret);
863 return ret;
864 }
865
866 ret = regmap_update_bits(arizona->regmap,
867 ARIZONA_DYNAMIC_FREQUENCY_SCALING_1,
868 ARIZONA_SUBSYS_MAX_FREQ,
869 ARIZONA_SUBSYS_MAX_FREQ);
870 if (ret) {
871 dev_err(codec->dev, "Failed to enable subsys max: %d\n", ret);
872 regulator_set_voltage(arizona->dcvdd, 1200000, 1800000);
873 return ret;
874 }
875
876 return 0;
877}
878
879static int arizona_dvfs_disable(struct snd_soc_codec *codec)
880{
881 const struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
882 struct arizona *arizona = priv->arizona;
883 int ret;
884
885 ret = regmap_update_bits(arizona->regmap,
886 ARIZONA_DYNAMIC_FREQUENCY_SCALING_1,
887 ARIZONA_SUBSYS_MAX_FREQ, 0);
888 if (ret) {
889 dev_err(codec->dev, "Failed to disable subsys max: %d\n", ret);
890 return ret;
891 }
892
893 ret = regulator_set_voltage(arizona->dcvdd, 1200000, 1800000);
894 if (ret) {
895 dev_err(codec->dev, "Failed to unboost DCVDD: %d\n", ret);
896 return ret;
897 }
898
899 return 0;
900}
901
902int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags)
903{
904 struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
905 int ret = 0;
906
907 mutex_lock(&priv->dvfs_lock);
908
909 if (!priv->dvfs_cached && !priv->dvfs_reqs) {
910 ret = arizona_dvfs_enable(codec);
911 if (ret)
912 goto err;
913 }
914
915 priv->dvfs_reqs |= flags;
916err:
917 mutex_unlock(&priv->dvfs_lock);
918 return ret;
919}
920EXPORT_SYMBOL_GPL(arizona_dvfs_up);
921
922int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags)
923{
924 struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
925 unsigned int old_reqs;
926 int ret = 0;
927
928 mutex_lock(&priv->dvfs_lock);
929
930 old_reqs = priv->dvfs_reqs;
931 priv->dvfs_reqs &= ~flags;
932
933 if (!priv->dvfs_cached && old_reqs && !priv->dvfs_reqs)
934 ret = arizona_dvfs_disable(codec);
935
936 mutex_unlock(&priv->dvfs_lock);
937 return ret;
938}
939EXPORT_SYMBOL_GPL(arizona_dvfs_down);
940
941int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
942 struct snd_kcontrol *kcontrol, int event)
943{
944 struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
945 struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
946 int ret = 0;
947
948 mutex_lock(&priv->dvfs_lock);
949
950 switch (event) {
951 case SND_SOC_DAPM_POST_PMU:
952 if (priv->dvfs_reqs)
953 ret = arizona_dvfs_enable(codec);
954
955 priv->dvfs_cached = false;
956 break;
957 case SND_SOC_DAPM_PRE_PMD:
958 /* We must ensure DVFS is disabled before the codec goes into
959 * suspend so that we are never in an illegal state of DVFS
960 * enabled without enough DCVDD
961 */
962 priv->dvfs_cached = true;
963
964 if (priv->dvfs_reqs)
965 ret = arizona_dvfs_disable(codec);
966 break;
967 default:
968 break;
969 }
970
971 mutex_unlock(&priv->dvfs_lock);
972 return ret;
973}
974EXPORT_SYMBOL_GPL(arizona_dvfs_sysclk_ev);
975
976void arizona_init_dvfs(struct arizona_priv *priv)
977{
978 mutex_init(&priv->dvfs_lock);
979}
980EXPORT_SYMBOL_GPL(arizona_init_dvfs);
981
854static unsigned int arizona_sysclk_48k_rates[] = { 982static unsigned int arizona_sysclk_48k_rates[] = {
855 6144000, 983 6144000,
856 12288000, 984 12288000,
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index bacc296a7d72..84e119a56515 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -60,6 +60,9 @@
60#define ARIZONA_MAX_DAI 6 60#define ARIZONA_MAX_DAI 6
61#define ARIZONA_MAX_ADSP 4 61#define ARIZONA_MAX_ADSP 4
62 62
63#define ARIZONA_DVFS_SR1_RQ 0x001
64#define ARIZONA_DVFS_ADSP1_RQ 0x100
65
63struct arizona; 66struct arizona;
64struct wm_adsp; 67struct wm_adsp;
65 68
@@ -84,6 +87,10 @@ struct arizona_priv {
84 87
85 unsigned int spk_ena:2; 88 unsigned int spk_ena:2;
86 unsigned int spk_ena_pending:1; 89 unsigned int spk_ena_pending:1;
90
91 unsigned int dvfs_reqs;
92 struct mutex dvfs_lock;
93 bool dvfs_cached;
87}; 94};
88 95
89#define ARIZONA_NUM_MIXER_INPUTS 103 96#define ARIZONA_NUM_MIXER_INPUTS 103
@@ -245,6 +252,12 @@ struct arizona_fll {
245 char clock_ok_name[ARIZONA_FLL_NAME_LEN]; 252 char clock_ok_name[ARIZONA_FLL_NAME_LEN];
246}; 253};
247 254
255extern int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags);
256extern int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags);
257extern int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
258 struct snd_kcontrol *kcontrol, int event);
259extern void arizona_init_dvfs(struct arizona_priv *priv);
260
248extern int arizona_init_fll(struct arizona *arizona, int id, int base, 261extern int arizona_init_fll(struct arizona *arizona, int id, int base,
249 int lock_irq, int ok_irq, struct arizona_fll *fll); 262 int lock_irq, int ok_irq, struct arizona_fll *fll);
250extern int arizona_set_fll_refclk(struct arizona_fll *fll, int source, 263extern int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 0c6d1bc0526e..b73e3a3da2d2 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -605,12 +605,13 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w,
605 regmap_write_async(regmap, patch[i].reg, 605 regmap_write_async(regmap, patch[i].reg,
606 patch[i].def); 606 patch[i].def);
607 break; 607 break;
608 608 case SND_SOC_DAPM_PRE_PMD:
609 default:
610 break; 609 break;
610 default:
611 return 0;
611 } 612 }
612 613
613 return 0; 614 return arizona_dvfs_sysclk_ev(w, kcontrol, event);
614} 615}
615 616
616static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, 617static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol,
@@ -1036,7 +1037,8 @@ static const struct snd_kcontrol_new wm5102_aec_loopback_mux =
1036 1037
1037static const struct snd_soc_dapm_widget wm5102_dapm_widgets[] = { 1038static const struct snd_soc_dapm_widget wm5102_dapm_widgets[] = {
1038SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT, 1039SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
1039 0, wm5102_sysclk_ev, SND_SOC_DAPM_POST_PMU), 1040 0, wm5102_sysclk_ev,
1041 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
1040SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, 1042SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
1041 ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), 1043 ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
1042SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, 1044SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
@@ -1909,6 +1911,8 @@ static int wm5102_probe(struct platform_device *pdev)
1909 wm5102->core.arizona = arizona; 1911 wm5102->core.arizona = arizona;
1910 wm5102->core.num_inputs = 6; 1912 wm5102->core.num_inputs = 6;
1911 1913
1914 arizona_init_dvfs(&wm5102->core);
1915
1912 wm5102->core.adsp[0].part = "wm5102"; 1916 wm5102->core.adsp[0].part = "wm5102";
1913 wm5102->core.adsp[0].num = 1; 1917 wm5102->core.adsp[0].num = 1;
1914 wm5102->core.adsp[0].type = WMFW_ADSP2; 1918 wm5102->core.adsp[0].type = WMFW_ADSP2;
diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c
index a4d11770630c..2a129dcf5f92 100644
--- a/sound/soc/codecs/wm8997.c
+++ b/sound/soc/codecs/wm8997.c
@@ -106,11 +106,13 @@ static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w,
106 regmap_write_async(regmap, patch[i].reg, 106 regmap_write_async(regmap, patch[i].reg,
107 patch[i].def); 107 patch[i].def);
108 break; 108 break;
109 default: 109 case SND_SOC_DAPM_PRE_PMD:
110 break; 110 break;
111 default:
112 return 0;
111 } 113 }
112 114
113 return 0; 115 return arizona_dvfs_sysclk_ev(w, kcontrol, event);
114} 116}
115 117
116static const char *wm8997_osr_text[] = { 118static const char *wm8997_osr_text[] = {
@@ -409,7 +411,8 @@ static const struct snd_kcontrol_new wm8997_aec_loopback_mux =
409 411
410static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = { 412static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = {
411SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT, 413SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
412 0, wm8997_sysclk_ev, SND_SOC_DAPM_POST_PMU), 414 0, wm8997_sysclk_ev,
415 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
413SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, 416SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
414 ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), 417 ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
415SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, 418SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
@@ -1126,6 +1129,8 @@ static int wm8997_probe(struct platform_device *pdev)
1126 wm8997->core.arizona = arizona; 1129 wm8997->core.arizona = arizona;
1127 wm8997->core.num_inputs = 4; 1130 wm8997->core.num_inputs = 4;
1128 1131
1132 arizona_init_dvfs(&wm8997->core);
1133
1129 for (i = 0; i < ARRAY_SIZE(wm8997->fll); i++) 1134 for (i = 0; i < ARRAY_SIZE(wm8997->fll); i++)
1130 wm8997->fll[i].vco_mult = 1; 1135 wm8997->fll[i].vco_mult = 1;
1131 1136