[alsa-devel] [PATCH 1/7] ASoC: wm8962: Update the clocking when setting system clock
Make sure we update for any changes in cases where we reconfigure while live (eg, for analogue bypass).
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/wm8962.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 4a081dd..3f37a91 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2705,6 +2705,8 @@ static int wm8962_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
wm8962->sysclk_rate = freq;
+ wm8962_configure_bclk(codec); + return 0; }
Otherwise we might not have a sensible clocking setup ready.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/wm8962.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 3f37a91..95428dc 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2675,7 +2675,8 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream, WM8962_SAMPLE_RATE_INT_MODE | WM8962_SAMPLE_RATE_MASK, adctl3);
- wm8962_configure_bclk(codec); + if (codec->dapm.bias_level == SND_SOC_BIAS_ON) + wm8962_configure_bclk(codec);
return 0; }
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/wm8962.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 95428dc..798ee01 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2479,6 +2479,8 @@ static void wm8962_configure_bclk(struct snd_soc_codec *codec) return; }
+ dev_dbg(codec->dev, "Selected sysclk ratio %d\n", sysclk_rates[i]); + snd_soc_update_bits(codec, WM8962_CLOCKING_4, WM8962_SYSCLK_RATE_MASK, clocking4);
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/wm8962.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 798ee01..deeaffe 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2445,7 +2445,7 @@ static const int bclk_divs[] = { };
static const int sysclk_rates[] = { - 64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536, + 64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536, 3072, 6144 };
static void wm8962_configure_bclk(struct snd_soc_codec *codec)
No longer needed with regmap.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/wm8962.c | 9 --------- 1 files changed, 0 insertions(+), 9 deletions(-)
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index deeaffe..d2a68d6 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3655,20 +3655,11 @@ static int wm8962_remove(struct snd_soc_codec *codec) return 0; }
-static int wm8962_soc_volatile(struct snd_soc_codec *codec, - unsigned int reg) -{ - return true; -} - - static struct snd_soc_codec_driver soc_codec_dev_wm8962 = { .probe = wm8962_probe, .remove = wm8962_remove, .set_bias_level = wm8962_set_bias_level, .set_pll = wm8962_set_fll, - .reg_cache_size = WM8962_MAX_REGISTER, - .volatile_register = wm8962_soc_volatile, .idle_bias_off = true, };
Only enable and disable the FLL when explicitly told to, supporting some additional use cases and making the driver behaviour more standard.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/wm8962.c | 72 ++++----------------------------------------- 1 files changed, 6 insertions(+), 66 deletions(-)
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index d2a68d6..a328b7b 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -1832,65 +1832,6 @@ SOC_SINGLE_TLV("SPKOUTR Mixer DACR Volume", WM8962_SPEAKER_MIXER_5, 4, 1, 0, inmix_tlv), };
-static int sysclk_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_codec *codec = w->codec; - struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); - unsigned long timeout; - int src; - int fll; - - /* Ignore attempts to run the event during startup */ - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) - return 0; - - src = snd_soc_read(codec, WM8962_CLOCKING2) & WM8962_SYSCLK_SRC_MASK; - - switch (src) { - case 0: /* MCLK */ - fll = 0; - break; - case 0x200: /* FLL */ - fll = 1; - break; - default: - dev_err(codec->dev, "Unknown SYSCLK source %x\n", src); - return -EINVAL; - } - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - if (fll) { - try_wait_for_completion(&wm8962->fll_lock); - - snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1, - WM8962_FLL_ENA, WM8962_FLL_ENA); - - timeout = msecs_to_jiffies(5); - timeout = wait_for_completion_timeout(&wm8962->fll_lock, - timeout); - - if (wm8962->irq && timeout == 0) - dev_err(codec->dev, - "Timed out starting FLL\n"); - } - break; - - case SND_SOC_DAPM_POST_PMD: - if (fll) - snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1, - WM8962_FLL_ENA, 0); - break; - - default: - BUG(); - return -EINVAL; - } - - return 0; -} - static int cp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -2176,8 +2117,7 @@ SND_SOC_DAPM_INPUT("DMICDAT"), SND_SOC_DAPM_SUPPLY("MICBIAS", WM8962_PWR_MGMT_1, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Class G", WM8962_CHARGE_PUMP_B, 0, 1, NULL, 0), -SND_SOC_DAPM_SUPPLY("SYSCLK", WM8962_CLOCKING2, 5, 0, sysclk_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("SYSCLK", WM8962_CLOCKING2, 5, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("Charge Pump", WM8962_CHARGE_PUMP_1, 0, 0, cp_event, SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("TOCLK", WM8962_ADDITIONAL_CONTROL_1, 0, 0, NULL, 0), @@ -2888,8 +2828,7 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source, struct _fll_div fll_div; unsigned long timeout; int ret; - int fll1 = snd_soc_read(codec, WM8962_FLL_CONTROL_1) & WM8962_FLL_ENA; - int sysclk = snd_soc_read(codec, WM8962_CLOCKING2) & WM8962_SYSCLK_ENA; + int fll1 = 0;
/* Any change? */ if (source == wm8962->fll_src && Fref == wm8962->fll_fref && @@ -2912,6 +2851,9 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source, if (ret != 0) return ret;
+ /* Parameters good, disable so we can reprogram */ + snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1, WM8962_FLL_ENA, 0); + switch (fll_id) { case WM8962_FLL_MCLK: case WM8962_FLL_BCLK: @@ -2950,12 +2892,10 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
try_wait_for_completion(&wm8962->fll_lock);
- if (sysclk) - fll1 |= WM8962_FLL_ENA;
snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1, WM8962_FLL_FRAC | WM8962_FLL_REFCLK_SRC_MASK | - WM8962_FLL_ENA, fll1); + WM8962_FLL_ENA, fll1 | WM8962_FLL_ENA);
dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
This allows userspace control of final power off, allowing policy decisions for register configuration retention.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/wm8962.c | 113 ++++++++++++++++++++++++++++---------------- 1 files changed, 72 insertions(+), 41 deletions(-)
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index a328b7b..2f06886 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -20,6 +20,7 @@ #include <linux/gpio.h> #include <linux/i2c.h> #include <linux/input.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> @@ -2479,9 +2480,6 @@ static void wm8962_configure_bclk(struct snd_soc_codec *codec) static int wm8962_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); - int ret; - if (level == codec->dapm.bias_level) return 0;
@@ -2498,51 +2496,15 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec, break;
case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { - ret = regulator_bulk_enable(ARRAY_SIZE(wm8962->supplies), - wm8962->supplies); - if (ret != 0) { - dev_err(codec->dev, - "Failed to enable supplies: %d\n", - ret); - return ret; - } - - regcache_cache_only(wm8962->regmap, false); - regcache_sync(wm8962->regmap); - - snd_soc_update_bits(codec, WM8962_ANTI_POP, - WM8962_STARTUP_BIAS_ENA | - WM8962_VMID_BUF_ENA, - WM8962_STARTUP_BIAS_ENA | - WM8962_VMID_BUF_ENA); - - /* Bias enable at 2*50k for ramp */ - snd_soc_update_bits(codec, WM8962_PWR_MGMT_1, - WM8962_VMID_SEL_MASK | - WM8962_BIAS_ENA, - WM8962_BIAS_ENA | 0x180); - - msleep(5); - } - /* VMID 2*250k */ snd_soc_update_bits(codec, WM8962_PWR_MGMT_1, WM8962_VMID_SEL_MASK, 0x100); break;
case SND_SOC_BIAS_OFF: - snd_soc_update_bits(codec, WM8962_PWR_MGMT_1, - WM8962_VMID_SEL_MASK | WM8962_BIAS_ENA, 0); - - snd_soc_update_bits(codec, WM8962_ANTI_POP, - WM8962_STARTUP_BIAS_ENA | - WM8962_VMID_BUF_ENA, 0); - - regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), - wm8962->supplies); break; } + codec->dapm.bias_level = level; return 0; } @@ -2844,6 +2806,8 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source, snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1, WM8962_FLL_ENA, 0);
+ pm_runtime_put(codec->dev); + return 0; }
@@ -2892,6 +2856,7 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
try_wait_for_completion(&wm8962->fll_lock);
+ pm_runtime_get_sync(codec->dev);
snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1, WM8962_FLL_FRAC | WM8962_FLL_REFCLK_SRC_MASK | @@ -3714,7 +3679,9 @@ static __devinit int wm8962_i2c_probe(struct i2c_client *i2c, ret); }
- regcache_cache_only(wm8962->regmap, true); + pm_runtime_set_active(&i2c->dev); + pm_runtime_enable(&i2c->dev); + pm_request_idle(&i2c->dev);
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8962, &wm8962_dai, 1); @@ -3746,6 +3713,69 @@ static __devexit int wm8962_i2c_remove(struct i2c_client *client) return 0; }
+#ifdef CONFIG_PM_RUNTIME +static int wm8962_runtime_resume(struct device *dev) +{ + struct wm8962_priv *wm8962 = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8962->supplies), + wm8962->supplies); + if (ret != 0) { + dev_err(dev, + "Failed to enable supplies: %d\n", ret); + return ret; + } + + regcache_cache_only(wm8962->regmap, false); + regcache_sync(wm8962->regmap); + + regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP, + WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA, + WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA); + + /* Bias enable at 2*50k for ramp */ + regmap_update_bits(wm8962->regmap, WM8962_PWR_MGMT_1, + WM8962_VMID_SEL_MASK | WM8962_BIAS_ENA, + WM8962_BIAS_ENA | 0x180); + + msleep(5); + + /* VMID back to 2x250k for standby */ + regmap_update_bits(wm8962->regmap, WM8962_PWR_MGMT_1, + WM8962_VMID_SEL_MASK, 0x100); + + dev_crit(dev, "RESUME\n"); + + return 0; +} + +static int wm8962_runtime_suspend(struct device *dev) +{ + struct wm8962_priv *wm8962 = dev_get_drvdata(dev); + + dev_crit(dev, "SUSPEND\n"); + + regmap_update_bits(wm8962->regmap, WM8962_PWR_MGMT_1, + WM8962_VMID_SEL_MASK | WM8962_BIAS_ENA, 0); + + regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP, + WM8962_STARTUP_BIAS_ENA | + WM8962_VMID_BUF_ENA, 0); + + regcache_cache_only(wm8962->regmap, true); + + regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), + wm8962->supplies); + + return 0; +} +#endif + +static struct dev_pm_ops wm8962_pm = { + SET_RUNTIME_PM_OPS(wm8962_runtime_suspend, wm8962_runtime_resume, NULL) +}; + static const struct i2c_device_id wm8962_i2c_id[] = { { "wm8962", 0 }, { } @@ -3756,6 +3786,7 @@ static struct i2c_driver wm8962_i2c_driver = { .driver = { .name = "wm8962", .owner = THIS_MODULE, + .pm = &wm8962_pm, }, .probe = wm8962_i2c_probe, .remove = __devexit_p(wm8962_i2c_remove),
participants (1)
-
Mark Brown