diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-01-26 12:28:49 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-01-26 13:39:24 -0500 |
commit | 62c1c40127e351de7fbc1d8c782f7508f8314aab (patch) | |
tree | 10f0a111dfc209c9c9473ccae90b557e91384fea /sound/soc/codecs/wm5100.c | |
parent | 17e3e57b65720628754e9afc6919e30776c0c822 (diff) |
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: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/codecs/wm5100.c')
-rw-r--r-- | sound/soc/codecs/wm5100.c | 116 |
1 files changed, 56 insertions, 60 deletions
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 39de946bf259..c6c382197fe2 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/gcd.h> | 18 | #include <linux/gcd.h> |
19 | #include <linux/gpio.h> | 19 | #include <linux/gpio.h> |
20 | #include <linux/i2c.h> | 20 | #include <linux/i2c.h> |
21 | #include <linux/pm_runtime.h> | ||
21 | #include <linux/regulator/consumer.h> | 22 | #include <linux/regulator/consumer.h> |
22 | #include <linux/regulator/fixed.h> | 23 | #include <linux/regulator/fixed.h> |
23 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
@@ -1261,54 +1262,6 @@ static const __devinitdata struct reg_default wm5100_reva_patches[] = { | |||
1261 | { WM5100_AUDIO_IF_3_19, 1 }, | 1262 | { WM5100_AUDIO_IF_3_19, 1 }, |
1262 | }; | 1263 | }; |
1263 | 1264 | ||
1264 | static int wm5100_set_bias_level(struct snd_soc_codec *codec, | ||
1265 | enum snd_soc_bias_level level) | ||
1266 | { | ||
1267 | struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec); | ||
1268 | int ret; | ||
1269 | |||
1270 | switch (level) { | ||
1271 | case SND_SOC_BIAS_ON: | ||
1272 | break; | ||
1273 | |||
1274 | case SND_SOC_BIAS_PREPARE: | ||
1275 | break; | ||
1276 | |||
1277 | case SND_SOC_BIAS_STANDBY: | ||
1278 | if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { | ||
1279 | ret = regulator_bulk_enable(ARRAY_SIZE(wm5100->core_supplies), | ||
1280 | wm5100->core_supplies); | ||
1281 | if (ret != 0) { | ||
1282 | dev_err(codec->dev, | ||
1283 | "Failed to enable supplies: %d\n", | ||
1284 | ret); | ||
1285 | return ret; | ||
1286 | } | ||
1287 | |||
1288 | if (wm5100->pdata.ldo_ena) { | ||
1289 | gpio_set_value_cansleep(wm5100->pdata.ldo_ena, | ||
1290 | 1); | ||
1291 | msleep(2); | ||
1292 | } | ||
1293 | |||
1294 | regcache_cache_only(wm5100->regmap, false); | ||
1295 | regcache_sync(wm5100->regmap); | ||
1296 | } | ||
1297 | break; | ||
1298 | |||
1299 | case SND_SOC_BIAS_OFF: | ||
1300 | regcache_cache_only(wm5100->regmap, true); | ||
1301 | if (wm5100->pdata.ldo_ena) | ||
1302 | gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0); | ||
1303 | regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies), | ||
1304 | wm5100->core_supplies); | ||
1305 | break; | ||
1306 | } | ||
1307 | codec->dapm.bias_level = level; | ||
1308 | |||
1309 | return 0; | ||
1310 | } | ||
1311 | |||
1312 | static int wm5100_dai_to_base(struct snd_soc_dai *dai) | 1265 | static int wm5100_dai_to_base(struct snd_soc_dai *dai) |
1313 | { | 1266 | { |
1314 | switch (dai->id) { | 1267 | switch (dai->id) { |
@@ -1836,6 +1789,8 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source, | |||
1836 | 1789 | ||
1837 | if (!Fout) { | 1790 | if (!Fout) { |
1838 | dev_dbg(codec->dev, "FLL%d disabled", fll_id); | 1791 | dev_dbg(codec->dev, "FLL%d disabled", fll_id); |
1792 | if (fll->fout) | ||
1793 | pm_runtime_put(codec->dev); | ||
1839 | fll->fout = 0; | 1794 | fll->fout = 0; |
1840 | snd_soc_update_bits(codec, base + 1, WM5100_FLL1_ENA, 0); | 1795 | snd_soc_update_bits(codec, base + 1, WM5100_FLL1_ENA, 0); |
1841 | return 0; | 1796 | return 0; |
@@ -1880,6 +1835,8 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source, | |||
1880 | /* Clear any pending completions */ | 1835 | /* Clear any pending completions */ |
1881 | try_wait_for_completion(&fll->lock); | 1836 | try_wait_for_completion(&fll->lock); |
1882 | 1837 | ||
1838 | pm_runtime_get_sync(codec->dev); | ||
1839 | |||
1883 | snd_soc_update_bits(codec, base + 1, WM5100_FLL1_ENA, WM5100_FLL1_ENA); | 1840 | snd_soc_update_bits(codec, base + 1, WM5100_FLL1_ENA, WM5100_FLL1_ENA); |
1884 | 1841 | ||
1885 | if (i2c->irq) | 1842 | if (i2c->irq) |
@@ -1914,6 +1871,7 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source, | |||
1914 | } | 1871 | } |
1915 | if (i == timeout) { | 1872 | if (i == timeout) { |
1916 | dev_err(codec->dev, "FLL%d lock timed out\n", fll_id); | 1873 | dev_err(codec->dev, "FLL%d lock timed out\n", fll_id); |
1874 | pm_runtime_put(codec->dev); | ||
1917 | return -ETIMEDOUT; | 1875 | return -ETIMEDOUT; |
1918 | } | 1876 | } |
1919 | 1877 | ||
@@ -2377,9 +2335,6 @@ static int wm5100_probe(struct snd_soc_codec *codec) | |||
2377 | return ret; | 2335 | return ret; |
2378 | } | 2336 | } |
2379 | 2337 | ||
2380 | regcache_cache_only(wm5100->regmap, true); | ||
2381 | |||
2382 | |||
2383 | for (i = 0; i < ARRAY_SIZE(wm5100_dig_vu); i++) | 2338 | for (i = 0; i < ARRAY_SIZE(wm5100_dig_vu); i++) |
2384 | snd_soc_update_bits(codec, wm5100_dig_vu[i], WM5100_OUT_VU, | 2339 | snd_soc_update_bits(codec, wm5100_dig_vu[i], WM5100_OUT_VU, |
2385 | WM5100_OUT_VU); | 2340 | WM5100_OUT_VU); |
@@ -2405,14 +2360,6 @@ static int wm5100_probe(struct snd_soc_codec *codec) | |||
2405 | } | 2360 | } |
2406 | } | 2361 | } |
2407 | 2362 | ||
2408 | /* We'll get woken up again when the system has something useful | ||
2409 | * for us to do. | ||
2410 | */ | ||
2411 | if (wm5100->pdata.ldo_ena) | ||
2412 | gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0); | ||
2413 | regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies), | ||
2414 | wm5100->core_supplies); | ||
2415 | |||
2416 | return 0; | 2363 | return 0; |
2417 | 2364 | ||
2418 | err_gpio: | 2365 | err_gpio: |
@@ -2444,7 +2391,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5100 = { | |||
2444 | 2391 | ||
2445 | .set_sysclk = wm5100_set_sysclk, | 2392 | .set_sysclk = wm5100_set_sysclk, |
2446 | .set_pll = wm5100_set_fll, | 2393 | .set_pll = wm5100_set_fll, |
2447 | .set_bias_level = wm5100_set_bias_level, | ||
2448 | .idle_bias_off = 1, | 2394 | .idle_bias_off = 1, |
2449 | .reg_cache_size = WM5100_MAX_REGISTER, | 2395 | .reg_cache_size = WM5100_MAX_REGISTER, |
2450 | .volatile_register = wm5100_soc_volatile, | 2396 | .volatile_register = wm5100_soc_volatile, |
@@ -2661,6 +2607,10 @@ static __devinit int wm5100_i2c_probe(struct i2c_client *i2c, | |||
2661 | } | 2607 | } |
2662 | } | 2608 | } |
2663 | 2609 | ||
2610 | pm_runtime_set_active(&i2c->dev); | ||
2611 | pm_runtime_enable(&i2c->dev); | ||
2612 | pm_request_idle(&i2c->dev); | ||
2613 | |||
2664 | ret = snd_soc_register_codec(&i2c->dev, | 2614 | ret = snd_soc_register_codec(&i2c->dev, |
2665 | &soc_codec_dev_wm5100, wm5100_dai, | 2615 | &soc_codec_dev_wm5100, wm5100_dai, |
2666 | ARRAY_SIZE(wm5100_dai)); | 2616 | ARRAY_SIZE(wm5100_dai)); |
@@ -2714,6 +2664,51 @@ static __devexit int wm5100_i2c_remove(struct i2c_client *i2c) | |||
2714 | return 0; | 2664 | return 0; |
2715 | } | 2665 | } |
2716 | 2666 | ||
2667 | #ifdef CONFIG_PM_RUNTIME | ||
2668 | static int wm5100_runtime_suspend(struct device *dev) | ||
2669 | { | ||
2670 | struct wm5100_priv *wm5100 = dev_get_drvdata(dev); | ||
2671 | |||
2672 | regcache_cache_only(wm5100->regmap, true); | ||
2673 | regcache_mark_dirty(wm5100->regmap); | ||
2674 | if (wm5100->pdata.ldo_ena) | ||
2675 | gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0); | ||
2676 | regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies), | ||
2677 | wm5100->core_supplies); | ||
2678 | |||
2679 | return 0; | ||
2680 | } | ||
2681 | |||
2682 | static int wm5100_runtime_resume(struct device *dev) | ||
2683 | { | ||
2684 | struct wm5100_priv *wm5100 = dev_get_drvdata(dev); | ||
2685 | int ret; | ||
2686 | |||
2687 | ret = regulator_bulk_enable(ARRAY_SIZE(wm5100->core_supplies), | ||
2688 | wm5100->core_supplies); | ||
2689 | if (ret != 0) { | ||
2690 | dev_err(dev, "Failed to enable supplies: %d\n", | ||
2691 | ret); | ||
2692 | return ret; | ||
2693 | } | ||
2694 | |||
2695 | if (wm5100->pdata.ldo_ena) { | ||
2696 | gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 1); | ||
2697 | msleep(2); | ||
2698 | } | ||
2699 | |||
2700 | regcache_cache_only(wm5100->regmap, false); | ||
2701 | regcache_sync(wm5100->regmap); | ||
2702 | |||
2703 | return 0; | ||
2704 | } | ||
2705 | #endif | ||
2706 | |||
2707 | static struct dev_pm_ops wm5100_pm = { | ||
2708 | SET_RUNTIME_PM_OPS(wm5100_runtime_suspend, wm5100_runtime_resume, | ||
2709 | NULL) | ||
2710 | }; | ||
2711 | |||
2717 | static const struct i2c_device_id wm5100_i2c_id[] = { | 2712 | static const struct i2c_device_id wm5100_i2c_id[] = { |
2718 | { "wm5100", 0 }, | 2713 | { "wm5100", 0 }, |
2719 | { } | 2714 | { } |
@@ -2724,6 +2719,7 @@ static struct i2c_driver wm5100_i2c_driver = { | |||
2724 | .driver = { | 2719 | .driver = { |
2725 | .name = "wm5100", | 2720 | .name = "wm5100", |
2726 | .owner = THIS_MODULE, | 2721 | .owner = THIS_MODULE, |
2722 | .pm = &wm5100_pm, | ||
2727 | }, | 2723 | }, |
2728 | .probe = wm5100_i2c_probe, | 2724 | .probe = wm5100_i2c_probe, |
2729 | .remove = __devexit_p(wm5100_i2c_remove), | 2725 | .remove = __devexit_p(wm5100_i2c_remove), |