Commit 62c1c401 authored by Mark Brown's avatar Mark Brown

ASoC: wm5100: Use pm_runtime for powerdown managment

Using pm_runtime to decide if the device should go into full power down
has the dual advantage of allowing easier integration with non-DAPM
reasons to power on the device (like the FLL) and allowing userspace to
control the final power down which is useful for tuning retention of
DSP firmware.
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
parent 17e3e57b
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/gcd.h> #include <linux/gcd.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/regulator/fixed.h> #include <linux/regulator/fixed.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -1261,54 +1262,6 @@ static const __devinitdata struct reg_default wm5100_reva_patches[] = { ...@@ -1261,54 +1262,6 @@ static const __devinitdata struct reg_default wm5100_reva_patches[] = {
{ WM5100_AUDIO_IF_3_19, 1 }, { WM5100_AUDIO_IF_3_19, 1 },
}; };
static int wm5100_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
int ret;
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm5100->core_supplies),
wm5100->core_supplies);
if (ret != 0) {
dev_err(codec->dev,
"Failed to enable supplies: %d\n",
ret);
return ret;
}
if (wm5100->pdata.ldo_ena) {
gpio_set_value_cansleep(wm5100->pdata.ldo_ena,
1);
msleep(2);
}
regcache_cache_only(wm5100->regmap, false);
regcache_sync(wm5100->regmap);
}
break;
case SND_SOC_BIAS_OFF:
regcache_cache_only(wm5100->regmap, true);
if (wm5100->pdata.ldo_ena)
gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0);
regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies),
wm5100->core_supplies);
break;
}
codec->dapm.bias_level = level;
return 0;
}
static int wm5100_dai_to_base(struct snd_soc_dai *dai) static int wm5100_dai_to_base(struct snd_soc_dai *dai)
{ {
switch (dai->id) { switch (dai->id) {
...@@ -1836,6 +1789,8 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source, ...@@ -1836,6 +1789,8 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
if (!Fout) { if (!Fout) {
dev_dbg(codec->dev, "FLL%d disabled", fll_id); dev_dbg(codec->dev, "FLL%d disabled", fll_id);
if (fll->fout)
pm_runtime_put(codec->dev);
fll->fout = 0; fll->fout = 0;
snd_soc_update_bits(codec, base + 1, WM5100_FLL1_ENA, 0); snd_soc_update_bits(codec, base + 1, WM5100_FLL1_ENA, 0);
return 0; return 0;
...@@ -1880,6 +1835,8 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source, ...@@ -1880,6 +1835,8 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
/* Clear any pending completions */ /* Clear any pending completions */
try_wait_for_completion(&fll->lock); try_wait_for_completion(&fll->lock);
pm_runtime_get_sync(codec->dev);
snd_soc_update_bits(codec, base + 1, WM5100_FLL1_ENA, WM5100_FLL1_ENA); snd_soc_update_bits(codec, base + 1, WM5100_FLL1_ENA, WM5100_FLL1_ENA);
if (i2c->irq) if (i2c->irq)
...@@ -1914,6 +1871,7 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source, ...@@ -1914,6 +1871,7 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
} }
if (i == timeout) { if (i == timeout) {
dev_err(codec->dev, "FLL%d lock timed out\n", fll_id); dev_err(codec->dev, "FLL%d lock timed out\n", fll_id);
pm_runtime_put(codec->dev);
return -ETIMEDOUT; return -ETIMEDOUT;
} }
...@@ -2377,9 +2335,6 @@ static int wm5100_probe(struct snd_soc_codec *codec) ...@@ -2377,9 +2335,6 @@ static int wm5100_probe(struct snd_soc_codec *codec)
return ret; return ret;
} }
regcache_cache_only(wm5100->regmap, true);
for (i = 0; i < ARRAY_SIZE(wm5100_dig_vu); i++) for (i = 0; i < ARRAY_SIZE(wm5100_dig_vu); i++)
snd_soc_update_bits(codec, wm5100_dig_vu[i], WM5100_OUT_VU, snd_soc_update_bits(codec, wm5100_dig_vu[i], WM5100_OUT_VU,
WM5100_OUT_VU); WM5100_OUT_VU);
...@@ -2405,14 +2360,6 @@ static int wm5100_probe(struct snd_soc_codec *codec) ...@@ -2405,14 +2360,6 @@ static int wm5100_probe(struct snd_soc_codec *codec)
} }
} }
/* We'll get woken up again when the system has something useful
* for us to do.
*/
if (wm5100->pdata.ldo_ena)
gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0);
regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies),
wm5100->core_supplies);
return 0; return 0;
err_gpio: err_gpio:
...@@ -2444,7 +2391,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5100 = { ...@@ -2444,7 +2391,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5100 = {
.set_sysclk = wm5100_set_sysclk, .set_sysclk = wm5100_set_sysclk,
.set_pll = wm5100_set_fll, .set_pll = wm5100_set_fll,
.set_bias_level = wm5100_set_bias_level,
.idle_bias_off = 1, .idle_bias_off = 1,
.reg_cache_size = WM5100_MAX_REGISTER, .reg_cache_size = WM5100_MAX_REGISTER,
.volatile_register = wm5100_soc_volatile, .volatile_register = wm5100_soc_volatile,
...@@ -2661,6 +2607,10 @@ static __devinit int wm5100_i2c_probe(struct i2c_client *i2c, ...@@ -2661,6 +2607,10 @@ static __devinit int wm5100_i2c_probe(struct i2c_client *i2c,
} }
} }
pm_runtime_set_active(&i2c->dev);
pm_runtime_enable(&i2c->dev);
pm_request_idle(&i2c->dev);
ret = snd_soc_register_codec(&i2c->dev, ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm5100, wm5100_dai, &soc_codec_dev_wm5100, wm5100_dai,
ARRAY_SIZE(wm5100_dai)); ARRAY_SIZE(wm5100_dai));
...@@ -2714,6 +2664,51 @@ static __devexit int wm5100_i2c_remove(struct i2c_client *i2c) ...@@ -2714,6 +2664,51 @@ static __devexit int wm5100_i2c_remove(struct i2c_client *i2c)
return 0; return 0;
} }
#ifdef CONFIG_PM_RUNTIME
static int wm5100_runtime_suspend(struct device *dev)
{
struct wm5100_priv *wm5100 = dev_get_drvdata(dev);
regcache_cache_only(wm5100->regmap, true);
regcache_mark_dirty(wm5100->regmap);
if (wm5100->pdata.ldo_ena)
gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0);
regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies),
wm5100->core_supplies);
return 0;
}
static int wm5100_runtime_resume(struct device *dev)
{
struct wm5100_priv *wm5100 = dev_get_drvdata(dev);
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(wm5100->core_supplies),
wm5100->core_supplies);
if (ret != 0) {
dev_err(dev, "Failed to enable supplies: %d\n",
ret);
return ret;
}
if (wm5100->pdata.ldo_ena) {
gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 1);
msleep(2);
}
regcache_cache_only(wm5100->regmap, false);
regcache_sync(wm5100->regmap);
return 0;
}
#endif
static struct dev_pm_ops wm5100_pm = {
SET_RUNTIME_PM_OPS(wm5100_runtime_suspend, wm5100_runtime_resume,
NULL)
};
static const struct i2c_device_id wm5100_i2c_id[] = { static const struct i2c_device_id wm5100_i2c_id[] = {
{ "wm5100", 0 }, { "wm5100", 0 },
{ } { }
...@@ -2724,6 +2719,7 @@ static struct i2c_driver wm5100_i2c_driver = { ...@@ -2724,6 +2719,7 @@ static struct i2c_driver wm5100_i2c_driver = {
.driver = { .driver = {
.name = "wm5100", .name = "wm5100",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &wm5100_pm,
}, },
.probe = wm5100_i2c_probe, .probe = wm5100_i2c_probe,
.remove = __devexit_p(wm5100_i2c_remove), .remove = __devexit_p(wm5100_i2c_remove),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment