diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/wm8994.c | 68 |
1 files changed, 67 insertions, 1 deletions
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 66aec0c3b7bb..bc26852d0aff 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -3140,9 +3140,67 @@ static struct snd_soc_dai_driver wm8994_dai[] = { }; #ifdef CONFIG_PM +static void wm8994_store_context(struct wm8994 *wm8994) +{ + struct device *dev = wm8994->dev; + int ret; + + /* Disable LDO pulldowns while the device is suspended if we + * don't know that something will be driving them. */ + if (!wm8994->ldo_ena_always_driven) + wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2, + WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD, + WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD); + + /* Explicitly put the device into reset in case regulators + * don't get disabled in order to ensure consistent restart. + */ + wm8994_reg_write(wm8994, WM8994_SOFTWARE_RESET, + wm8994_reg_read(wm8994, WM8994_SOFTWARE_RESET)); + + regcache_mark_dirty(wm8994->regmap); + + /* Restore GPIO registers to prevent problems with mismatched + * pin configurations. + */ + ret = regcache_sync_region(wm8994->regmap, WM8994_GPIO_1, + WM8994_GPIO_11); + if (ret != 0) + dev_err(dev, "Failed to restore GPIO registers: %d\n", ret); + + /* In case one of the GPIOs is used as a wake input. */ + ret = regcache_sync_region(wm8994->regmap, + WM8994_INTERRUPT_STATUS_1_MASK, + WM8994_INTERRUPT_STATUS_1_MASK); + if (ret != 0) + dev_err(dev, "Failed to restore interrupt mask: %d\n", ret); + + regcache_cache_only(wm8994->regmap, true); +} + +static int wm8994_load_context(struct wm8994 *wm8994) +{ + struct device *dev = wm8994->dev; + int ret; + + regcache_cache_only(wm8994->regmap, false); + ret = regcache_sync(wm8994->regmap); + if (ret != 0) { + dev_err(dev, "Failed to restore register map: %d\n", ret); + return ret; + } + + /* Disable LDO pulldowns while the device is active */ + wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2, + WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD, 0); + + return 0; +} + static int wm8994_codec_suspend(struct snd_soc_codec *codec) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994 *control = wm8994->wm8994; int i, ret; for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) { @@ -3154,16 +3212,24 @@ static int wm8994_codec_suspend(struct snd_soc_codec *codec) i + 1, ret); } - wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF); + wm8994_store_context(control); + wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } static int wm8994_codec_resume(struct snd_soc_codec *codec) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994 *control = wm8994->wm8994; int i, ret; + ret = wm8994_load_context(control); + if (ret != 0) { + dev_err(codec->dev, "Failed to load context: %d\n", ret); + return ret; + } + for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) { if (!wm8994->fll_suspend[i].out) continue; |