From d4c78dfc499b5583ed375a605a85529086cad936 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Tue, 13 Oct 2015 18:25:19 +0800 Subject: [PATCH] MLK-11570-02 ASoC: wm8994: store/restore context in codec driver suspend/resume For lpsr mode, the codec will be power down, the register value will be lost, so we should store the context at the end of codec suspend, and load the registers at the beginning of codec resume. Signed-off-by: Zidan Wang (cherry picked from commit e35e15983e14caf48dadf235bc6b33014818c62e) --- sound/soc/codecs/wm8994.c | 67 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 3896523b71e9..e79cc4cbbcc5 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -3150,9 +3150,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++) { @@ -3164,6 +3222,8 @@ static int wm8994_codec_suspend(struct snd_soc_codec *codec) i + 1, ret); } + wm8994_store_context(control); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); return 0; @@ -3172,8 +3232,15 @@ static int wm8994_codec_suspend(struct snd_soc_codec *codec) 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; -- 2.17.1