From 346d96836ca4af39dbfe65eceb7db812b1bfe68f Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 2 Jun 2015 11:53:33 +0100 Subject: 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 Signed-off-by: Mark Brown --- sound/soc/codecs/wm5102.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'sound/soc/codecs/wm5102.c') diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 0c6d1bc..b73e3a3 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, regmap_write_async(regmap, patch[i].reg, patch[i].def); break; - - default: + case SND_SOC_DAPM_PRE_PMD: break; + default: + return 0; } - return 0; + return arizona_dvfs_sysclk_ev(w, kcontrol, event); } static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, @@ -1036,7 +1037,8 @@ static const struct snd_kcontrol_new wm5102_aec_loopback_mux = static const struct snd_soc_dapm_widget wm5102_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT, - 0, wm5102_sysclk_ev, SND_SOC_DAPM_POST_PMU), + 0, wm5102_sysclk_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, @@ -1909,6 +1911,8 @@ static int wm5102_probe(struct platform_device *pdev) wm5102->core.arizona = arizona; wm5102->core.num_inputs = 6; + arizona_init_dvfs(&wm5102->core); + wm5102->core.adsp[0].part = "wm5102"; wm5102->core.adsp[0].num = 1; wm5102->core.adsp[0].type = WMFW_ADSP2; -- cgit v1.1 From 81ac58b13f815d7c7838bc347dd5d102707a11b7 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 2 Jun 2015 11:53:34 +0100 Subject: ASoC: wm_adsp: Move DVFS control into codec driver In theory the ADSP driver should not need to know anything about the codec it is part of. But the WM5102 needs DVFS control based on ADSP clocking speed. This was being handled by bundling part of the knowledge of this into the ADSP driver. This change moves this handling out of the ADSP driver and into the WM5102 driver. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown --- sound/soc/codecs/wm5102.c | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) (limited to 'sound/soc/codecs/wm5102.c') diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index b73e3a3..11eba0e 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -614,6 +614,49 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w, return arizona_dvfs_sysclk_ev(w, kcontrol, event); } +static int wm5102_adsp_power_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 *arizona = dev_get_drvdata(codec->dev->parent); + unsigned int v; + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v); + if (ret != 0) { + dev_err(codec->dev, + "Failed to read SYSCLK state: %d\n", ret); + return -EIO; + } + + v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT; + + if (v >= 3) { + ret = arizona_dvfs_up(codec, ARIZONA_DVFS_ADSP1_RQ); + if (ret) { + dev_err(codec->dev, + "Failed to raise DVFS: %d\n", ret); + return ret; + } + } + break; + + case SND_SOC_DAPM_POST_PMD: + ret = arizona_dvfs_down(codec, ARIZONA_DVFS_ADSP1_RQ); + if (ret) + dev_warn(codec->dev, + "Failed to lower DVFS: %d\n", ret); + break; + + default: + break; + } + + return wm_adsp2_early_event(w, kcontrol, event); +} + static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1369,7 +1412,7 @@ ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), -WM_ADSP2("DSP1", 0), +WM_ADSP2_E("DSP1", 0, wm5102_adsp_power_ev), SND_SOC_DAPM_OUTPUT("HPOUT1L"), SND_SOC_DAPM_OUTPUT("HPOUT1R"), @@ -1922,7 +1965,7 @@ static int wm5102_probe(struct platform_device *pdev) wm5102->core.adsp[0].mem = wm5102_dsp1_regions; wm5102->core.adsp[0].num_mems = ARRAY_SIZE(wm5102_dsp1_regions); - ret = wm_adsp2_init(&wm5102->core.adsp[0], true); + ret = wm_adsp2_init(&wm5102->core.adsp[0]); if (ret != 0) return ret; -- cgit v1.1