aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc
diff options
context:
space:
mode:
authorCharles Keepax <ckeepax@opensource.wolfsonmicro.com>2015-04-07 07:55:10 -0400
committerMark Brown <broonie@kernel.org>2015-04-08 14:25:40 -0400
commit1a60667fc81fdab3733a1d41480da3a5d0ccecea (patch)
treed146cc70eb718062b3f7546a896c21bbaf9e4b1d /sound/soc
parent5631f18763f1b0989cec7cbf8f3badcb74dfe469 (diff)
ASoC: wm8804: Enable runtime PM
Currently both the oscillator and the PLL are powered up in set_bias_level. This can be problematic when using output clocks from the wm8804 for other devices. The snd_soc_codec_set_pll API defines that a clock should be available once the call returns, however, with all the clocking controlled in set_bias_level this is not currently the case. This patch enables pm_runtime for the wm8804, enabling both the regulators and the oscillator when the chip resumes, and enabling the PLL in the snd_soc_codec_set_pll call. Naturally the enabling the PLL will also cause the chip to resume. Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com> Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/codecs/wm8804-i2c.c1
-rw-r--r--sound/soc/codecs/wm8804-spi.c1
-rw-r--r--sound/soc/codecs/wm8804.c109
-rw-r--r--sound/soc/codecs/wm8804.h1
4 files changed, 62 insertions, 50 deletions
diff --git a/sound/soc/codecs/wm8804-i2c.c b/sound/soc/codecs/wm8804-i2c.c
index 5bd4af2b4059..6596f5f3a0c3 100644
--- a/sound/soc/codecs/wm8804-i2c.c
+++ b/sound/soc/codecs/wm8804-i2c.c
@@ -50,6 +50,7 @@ static struct i2c_driver wm8804_i2c_driver = {
50 .driver = { 50 .driver = {
51 .name = "wm8804", 51 .name = "wm8804",
52 .owner = THIS_MODULE, 52 .owner = THIS_MODULE,
53 .pm = &wm8804_pm,
53 .of_match_table = wm8804_of_match, 54 .of_match_table = wm8804_of_match,
54 }, 55 },
55 .probe = wm8804_i2c_probe, 56 .probe = wm8804_i2c_probe,
diff --git a/sound/soc/codecs/wm8804-spi.c b/sound/soc/codecs/wm8804-spi.c
index 287e11e90794..407a3cf391e5 100644
--- a/sound/soc/codecs/wm8804-spi.c
+++ b/sound/soc/codecs/wm8804-spi.c
@@ -43,6 +43,7 @@ static struct spi_driver wm8804_spi_driver = {
43 .driver = { 43 .driver = {
44 .name = "wm8804", 44 .name = "wm8804",
45 .owner = THIS_MODULE, 45 .owner = THIS_MODULE,
46 .pm = &wm8804_pm,
46 .of_match_table = wm8804_of_match, 47 .of_match_table = wm8804_of_match,
47 }, 48 },
48 .probe = wm8804_spi_probe, 49 .probe = wm8804_spi_probe,
diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c
index cff34be61f88..1e403f67cf16 100644
--- a/sound/soc/codecs/wm8804.c
+++ b/sound/soc/codecs/wm8804.c
@@ -16,6 +16,7 @@
16#include <linux/gpio/consumer.h> 16#include <linux/gpio/consumer.h>
17#include <linux/delay.h> 17#include <linux/delay.h>
18#include <linux/pm.h> 18#include <linux/pm.h>
19#include <linux/pm_runtime.h>
19#include <linux/of_device.h> 20#include <linux/of_device.h>
20#include <linux/regulator/consumer.h> 21#include <linux/regulator/consumer.h>
21#include <linux/slab.h> 22#include <linux/slab.h>
@@ -59,6 +60,7 @@ static const struct reg_default wm8804_reg_defaults[] = {
59}; 60};
60 61
61struct wm8804_priv { 62struct wm8804_priv {
63 struct device *dev;
62 struct regmap *regmap; 64 struct regmap *regmap;
63 struct regulator_bulk_data supplies[WM8804_NUM_SUPPLIES]; 65 struct regulator_bulk_data supplies[WM8804_NUM_SUPPLIES];
64 struct notifier_block disable_nb[WM8804_NUM_SUPPLIES]; 66 struct notifier_block disable_nb[WM8804_NUM_SUPPLIES];
@@ -403,19 +405,19 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id,
403 int source, unsigned int freq_in, 405 int source, unsigned int freq_in,
404 unsigned int freq_out) 406 unsigned int freq_out)
405{ 407{
406 struct snd_soc_codec *codec; 408 struct snd_soc_codec *codec = dai->codec;
409 struct wm8804_priv *wm8804 = snd_soc_codec_get_drvdata(codec);
410 bool change;
407 411
408 codec = dai->codec;
409 if (!freq_in || !freq_out) { 412 if (!freq_in || !freq_out) {
410 /* disable the PLL */ 413 /* disable the PLL */
411 snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0x1); 414 regmap_update_bits_check(wm8804->regmap, WM8804_PWRDN,
412 return 0; 415 0x1, 0x1, &change);
416 if (change)
417 pm_runtime_put(wm8804->dev);
413 } else { 418 } else {
414 int ret; 419 int ret;
415 struct pll_div pll_div; 420 struct pll_div pll_div;
416 struct wm8804_priv *wm8804;
417
418 wm8804 = snd_soc_codec_get_drvdata(codec);
419 421
420 ret = pll_factors(&pll_div, freq_out, freq_in, 422 ret = pll_factors(&pll_div, freq_out, freq_in,
421 wm8804->mclk_div); 423 wm8804->mclk_div);
@@ -423,7 +425,10 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id,
423 return ret; 425 return ret;
424 426
425 /* power down the PLL before reprogramming it */ 427 /* power down the PLL before reprogramming it */
426 snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0x1); 428 regmap_update_bits_check(wm8804->regmap, WM8804_PWRDN,
429 0x1, 0x1, &change);
430 if (!change)
431 pm_runtime_get_sync(wm8804->dev);
427 432
428 /* set PLLN and PRESCALE */ 433 /* set PLLN and PRESCALE */
429 snd_soc_update_bits(codec, WM8804_PLL4, 0xf | 0x10, 434 snd_soc_update_bits(codec, WM8804_PLL4, 0xf | 0x10,
@@ -501,47 +506,6 @@ static int wm8804_set_clkdiv(struct snd_soc_dai *dai,
501 return 0; 506 return 0;
502} 507}
503 508
504static int wm8804_set_bias_level(struct snd_soc_codec *codec,
505 enum snd_soc_bias_level level)
506{
507 int ret;
508 struct wm8804_priv *wm8804;
509
510 wm8804 = snd_soc_codec_get_drvdata(codec);
511 switch (level) {
512 case SND_SOC_BIAS_ON:
513 break;
514 case SND_SOC_BIAS_PREPARE:
515 /* power up the OSC and the PLL */
516 snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0);
517 break;
518 case SND_SOC_BIAS_STANDBY:
519 if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
520 ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies),
521 wm8804->supplies);
522 if (ret) {
523 dev_err(codec->dev,
524 "Failed to enable supplies: %d\n",
525 ret);
526 return ret;
527 }
528 regcache_sync(wm8804->regmap);
529 }
530 /* power down the OSC and the PLL */
531 snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0x9);
532 break;
533 case SND_SOC_BIAS_OFF:
534 /* power down the OSC and the PLL */
535 snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0x9);
536 regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies),
537 wm8804->supplies);
538 break;
539 }
540
541 codec->dapm.bias_level = level;
542 return 0;
543}
544
545static const struct snd_soc_dai_ops wm8804_dai_ops = { 509static const struct snd_soc_dai_ops wm8804_dai_ops = {
546 .hw_params = wm8804_hw_params, 510 .hw_params = wm8804_hw_params,
547 .set_fmt = wm8804_set_fmt, 511 .set_fmt = wm8804_set_fmt,
@@ -579,7 +543,6 @@ static struct snd_soc_dai_driver wm8804_dai = {
579}; 543};
580 544
581static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = { 545static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = {
582 .set_bias_level = wm8804_set_bias_level,
583 .idle_bias_off = true, 546 .idle_bias_off = true,
584 547
585 .dapm_widgets = wm8804_dapm_widgets, 548 .dapm_widgets = wm8804_dapm_widgets,
@@ -613,6 +576,7 @@ int wm8804_probe(struct device *dev, struct regmap *regmap)
613 576
614 dev_set_drvdata(dev, wm8804); 577 dev_set_drvdata(dev, wm8804);
615 578
579 wm8804->dev = dev;
616 wm8804->regmap = regmap; 580 wm8804->regmap = regmap;
617 581
618 wm8804->reset = devm_gpiod_get_optional(dev, "wlf,reset", 582 wm8804->reset = devm_gpiod_get_optional(dev, "wlf,reset",
@@ -703,6 +667,10 @@ int wm8804_probe(struct device *dev, struct regmap *regmap)
703 goto err_reg_enable; 667 goto err_reg_enable;
704 } 668 }
705 669
670 pm_runtime_set_active(dev);
671 pm_runtime_enable(dev);
672 pm_runtime_idle(dev);
673
706 return 0; 674 return 0;
707 675
708err_reg_enable: 676err_reg_enable:
@@ -713,10 +681,51 @@ EXPORT_SYMBOL_GPL(wm8804_probe);
713 681
714void wm8804_remove(struct device *dev) 682void wm8804_remove(struct device *dev)
715{ 683{
684 pm_runtime_disable(dev);
716 snd_soc_unregister_codec(dev); 685 snd_soc_unregister_codec(dev);
717} 686}
718EXPORT_SYMBOL_GPL(wm8804_remove); 687EXPORT_SYMBOL_GPL(wm8804_remove);
719 688
689#if IS_ENABLED(CONFIG_PM)
690static int wm8804_runtime_resume(struct device *dev)
691{
692 struct wm8804_priv *wm8804 = dev_get_drvdata(dev);
693 int ret;
694
695 ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies),
696 wm8804->supplies);
697 if (ret) {
698 dev_err(wm8804->dev, "Failed to enable supplies: %d\n", ret);
699 return ret;
700 }
701
702 regcache_sync(wm8804->regmap);
703
704 /* Power up OSCCLK */
705 regmap_update_bits(wm8804->regmap, WM8804_PWRDN, 0x8, 0x0);
706
707 return 0;
708}
709
710static int wm8804_runtime_suspend(struct device *dev)
711{
712 struct wm8804_priv *wm8804 = dev_get_drvdata(dev);
713
714 /* Power down OSCCLK */
715 regmap_update_bits(wm8804->regmap, WM8804_PWRDN, 0x8, 0x8);
716
717 regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies),
718 wm8804->supplies);
719
720 return 0;
721}
722#endif
723
724const struct dev_pm_ops wm8804_pm = {
725 SET_RUNTIME_PM_OPS(wm8804_runtime_suspend, wm8804_runtime_resume, NULL)
726};
727EXPORT_SYMBOL_GPL(wm8804_pm);
728
720MODULE_DESCRIPTION("ASoC WM8804 driver"); 729MODULE_DESCRIPTION("ASoC WM8804 driver");
721MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>"); 730MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>");
722MODULE_LICENSE("GPL"); 731MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8804.h b/sound/soc/codecs/wm8804.h
index a39a2563dc67..aa72fa66c932 100644
--- a/sound/soc/codecs/wm8804.h
+++ b/sound/soc/codecs/wm8804.h
@@ -65,6 +65,7 @@
65#define WM8804_MCLKDIV_128FS 1 65#define WM8804_MCLKDIV_128FS 1
66 66
67extern const struct regmap_config wm8804_regmap_config; 67extern const struct regmap_config wm8804_regmap_config;
68extern const struct dev_pm_ops wm8804_pm;
68 69
69int wm8804_probe(struct device *dev, struct regmap *regmap); 70int wm8804_probe(struct device *dev, struct regmap *regmap);
70void wm8804_remove(struct device *dev); 71void wm8804_remove(struct device *dev);