diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/wm2200.c | 9 | ||||
-rw-r--r-- | sound/soc/codecs/wm5100.c | 7 | ||||
-rw-r--r-- | sound/soc/codecs/wm8731.c | 34 | ||||
-rw-r--r-- | sound/soc/codecs/wm8804-i2c.c | 1 | ||||
-rw-r--r-- | sound/soc/codecs/wm8804-spi.c | 1 | ||||
-rw-r--r-- | sound/soc/codecs/wm8804.c | 297 | ||||
-rw-r--r-- | sound/soc/codecs/wm8804.h | 1 | ||||
-rw-r--r-- | sound/soc/codecs/wm8996.c | 12 |
8 files changed, 227 insertions, 135 deletions
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 15599845a660..5a9da28f4f33 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c | |||
@@ -1554,7 +1554,6 @@ static int wm2200_probe(struct snd_soc_codec *codec) | |||
1554 | int ret; | 1554 | int ret; |
1555 | 1555 | ||
1556 | wm2200->codec = codec; | 1556 | wm2200->codec = codec; |
1557 | codec->dapm.bias_level = SND_SOC_BIAS_OFF; | ||
1558 | 1557 | ||
1559 | ret = snd_soc_add_codec_controls(codec, wm_adsp1_fw_controls, 2); | 1558 | ret = snd_soc_add_codec_controls(codec, wm_adsp1_fw_controls, 2); |
1560 | if (ret != 0) | 1559 | if (ret != 0) |
@@ -1942,6 +1941,7 @@ static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source, | |||
1942 | struct wm2200_priv *wm2200 = snd_soc_codec_get_drvdata(codec); | 1941 | struct wm2200_priv *wm2200 = snd_soc_codec_get_drvdata(codec); |
1943 | struct _fll_div factors; | 1942 | struct _fll_div factors; |
1944 | int ret, i, timeout; | 1943 | int ret, i, timeout; |
1944 | unsigned long time_left; | ||
1945 | 1945 | ||
1946 | if (!Fout) { | 1946 | if (!Fout) { |
1947 | dev_dbg(codec->dev, "FLL disabled"); | 1947 | dev_dbg(codec->dev, "FLL disabled"); |
@@ -2021,9 +2021,10 @@ static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source, | |||
2021 | /* Poll for the lock; will use the interrupt to exit quickly */ | 2021 | /* Poll for the lock; will use the interrupt to exit quickly */ |
2022 | for (i = 0; i < timeout; i++) { | 2022 | for (i = 0; i < timeout; i++) { |
2023 | if (i2c->irq) { | 2023 | if (i2c->irq) { |
2024 | ret = wait_for_completion_timeout(&wm2200->fll_lock, | 2024 | time_left = wait_for_completion_timeout( |
2025 | msecs_to_jiffies(25)); | 2025 | &wm2200->fll_lock, |
2026 | if (ret > 0) | 2026 | msecs_to_jiffies(25)); |
2027 | if (time_left > 0) | ||
2027 | break; | 2028 | break; |
2028 | } else { | 2029 | } else { |
2029 | msleep(1); | 2030 | msleep(1); |
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index ea09db585aa1..96740379b711 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c | |||
@@ -1762,6 +1762,7 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source, | |||
1762 | struct _fll_div factors; | 1762 | struct _fll_div factors; |
1763 | struct wm5100_fll *fll; | 1763 | struct wm5100_fll *fll; |
1764 | int ret, base, lock, i, timeout; | 1764 | int ret, base, lock, i, timeout; |
1765 | unsigned long time_left; | ||
1765 | 1766 | ||
1766 | switch (fll_id) { | 1767 | switch (fll_id) { |
1767 | case WM5100_FLL1: | 1768 | case WM5100_FLL1: |
@@ -1842,9 +1843,9 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source, | |||
1842 | /* Poll for the lock; will use interrupt when we can test */ | 1843 | /* Poll for the lock; will use interrupt when we can test */ |
1843 | for (i = 0; i < timeout; i++) { | 1844 | for (i = 0; i < timeout; i++) { |
1844 | if (i2c->irq) { | 1845 | if (i2c->irq) { |
1845 | ret = wait_for_completion_timeout(&fll->lock, | 1846 | time_left = wait_for_completion_timeout(&fll->lock, |
1846 | msecs_to_jiffies(25)); | 1847 | msecs_to_jiffies(25)); |
1847 | if (ret > 0) | 1848 | if (time_left > 0) |
1848 | break; | 1849 | break; |
1849 | } else { | 1850 | } else { |
1850 | msleep(1); | 1851 | msleep(1); |
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index c6d10533e2bd..2245b6a32f3d 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/spi/spi.h> | 25 | #include <linux/spi/spi.h> |
26 | #include <linux/of_device.h> | 26 | #include <linux/of_device.h> |
27 | #include <linux/mutex.h> | 27 | #include <linux/mutex.h> |
28 | #include <linux/clk.h> | ||
28 | #include <sound/core.h> | 29 | #include <sound/core.h> |
29 | #include <sound/pcm.h> | 30 | #include <sound/pcm.h> |
30 | #include <sound/pcm_params.h> | 31 | #include <sound/pcm_params.h> |
@@ -45,6 +46,7 @@ static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = { | |||
45 | /* codec private data */ | 46 | /* codec private data */ |
46 | struct wm8731_priv { | 47 | struct wm8731_priv { |
47 | struct regmap *regmap; | 48 | struct regmap *regmap; |
49 | struct clk *mclk; | ||
48 | struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES]; | 50 | struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES]; |
49 | const struct snd_pcm_hw_constraint_list *constraints; | 51 | const struct snd_pcm_hw_constraint_list *constraints; |
50 | unsigned int sysclk; | 52 | unsigned int sysclk; |
@@ -390,6 +392,8 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai, | |||
390 | switch (clk_id) { | 392 | switch (clk_id) { |
391 | case WM8731_SYSCLK_XTAL: | 393 | case WM8731_SYSCLK_XTAL: |
392 | case WM8731_SYSCLK_MCLK: | 394 | case WM8731_SYSCLK_MCLK: |
395 | if (wm8731->mclk && clk_set_rate(wm8731->mclk, freq)) | ||
396 | return -EINVAL; | ||
393 | wm8731->sysclk_type = clk_id; | 397 | wm8731->sysclk_type = clk_id; |
394 | break; | 398 | break; |
395 | default: | 399 | default: |
@@ -491,6 +495,8 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec, | |||
491 | 495 | ||
492 | switch (level) { | 496 | switch (level) { |
493 | case SND_SOC_BIAS_ON: | 497 | case SND_SOC_BIAS_ON: |
498 | if (wm8731->mclk) | ||
499 | clk_prepare_enable(wm8731->mclk); | ||
494 | break; | 500 | break; |
495 | case SND_SOC_BIAS_PREPARE: | 501 | case SND_SOC_BIAS_PREPARE: |
496 | break; | 502 | break; |
@@ -509,6 +515,8 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec, | |||
509 | snd_soc_write(codec, WM8731_PWR, reg | 0x0040); | 515 | snd_soc_write(codec, WM8731_PWR, reg | 0x0040); |
510 | break; | 516 | break; |
511 | case SND_SOC_BIAS_OFF: | 517 | case SND_SOC_BIAS_OFF: |
518 | if (wm8731->mclk) | ||
519 | clk_disable_unprepare(wm8731->mclk); | ||
512 | snd_soc_write(codec, WM8731_PWR, 0xffff); | 520 | snd_soc_write(codec, WM8731_PWR, 0xffff); |
513 | regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), | 521 | regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), |
514 | wm8731->supplies); | 522 | wm8731->supplies); |
@@ -667,6 +675,19 @@ static int wm8731_spi_probe(struct spi_device *spi) | |||
667 | if (wm8731 == NULL) | 675 | if (wm8731 == NULL) |
668 | return -ENOMEM; | 676 | return -ENOMEM; |
669 | 677 | ||
678 | wm8731->mclk = devm_clk_get(&spi->dev, "mclk"); | ||
679 | if (IS_ERR(wm8731->mclk)) { | ||
680 | ret = PTR_ERR(wm8731->mclk); | ||
681 | if (ret == -ENOENT) { | ||
682 | wm8731->mclk = NULL; | ||
683 | dev_warn(&spi->dev, "Assuming static MCLK\n"); | ||
684 | } else { | ||
685 | dev_err(&spi->dev, "Failed to get MCLK: %d\n", | ||
686 | ret); | ||
687 | return ret; | ||
688 | } | ||
689 | } | ||
690 | |||
670 | mutex_init(&wm8731->lock); | 691 | mutex_init(&wm8731->lock); |
671 | 692 | ||
672 | wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap); | 693 | wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap); |
@@ -718,6 +739,19 @@ static int wm8731_i2c_probe(struct i2c_client *i2c, | |||
718 | if (wm8731 == NULL) | 739 | if (wm8731 == NULL) |
719 | return -ENOMEM; | 740 | return -ENOMEM; |
720 | 741 | ||
742 | wm8731->mclk = devm_clk_get(&i2c->dev, "mclk"); | ||
743 | if (IS_ERR(wm8731->mclk)) { | ||
744 | ret = PTR_ERR(wm8731->mclk); | ||
745 | if (ret == -ENOENT) { | ||
746 | wm8731->mclk = NULL; | ||
747 | dev_warn(&i2c->dev, "Assuming static MCLK\n"); | ||
748 | } else { | ||
749 | dev_err(&i2c->dev, "Failed to get MCLK: %d\n", | ||
750 | ret); | ||
751 | return ret; | ||
752 | } | ||
753 | } | ||
754 | |||
721 | mutex_init(&wm8731->lock); | 755 | mutex_init(&wm8731->lock); |
722 | 756 | ||
723 | wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap); | 757 | wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap); |
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 1bd4ace29594..1e403f67cf16 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c | |||
@@ -13,8 +13,10 @@ | |||
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <linux/moduleparam.h> | 14 | #include <linux/moduleparam.h> |
15 | #include <linux/init.h> | 15 | #include <linux/init.h> |
16 | #include <linux/gpio/consumer.h> | ||
16 | #include <linux/delay.h> | 17 | #include <linux/delay.h> |
17 | #include <linux/pm.h> | 18 | #include <linux/pm.h> |
19 | #include <linux/pm_runtime.h> | ||
18 | #include <linux/of_device.h> | 20 | #include <linux/of_device.h> |
19 | #include <linux/regulator/consumer.h> | 21 | #include <linux/regulator/consumer.h> |
20 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
@@ -24,6 +26,7 @@ | |||
24 | #include <sound/soc.h> | 26 | #include <sound/soc.h> |
25 | #include <sound/initval.h> | 27 | #include <sound/initval.h> |
26 | #include <sound/tlv.h> | 28 | #include <sound/tlv.h> |
29 | #include <sound/soc-dapm.h> | ||
27 | 30 | ||
28 | #include "wm8804.h" | 31 | #include "wm8804.h" |
29 | 32 | ||
@@ -57,18 +60,23 @@ static const struct reg_default wm8804_reg_defaults[] = { | |||
57 | }; | 60 | }; |
58 | 61 | ||
59 | struct wm8804_priv { | 62 | struct wm8804_priv { |
63 | struct device *dev; | ||
60 | struct regmap *regmap; | 64 | struct regmap *regmap; |
61 | struct regulator_bulk_data supplies[WM8804_NUM_SUPPLIES]; | 65 | struct regulator_bulk_data supplies[WM8804_NUM_SUPPLIES]; |
62 | struct notifier_block disable_nb[WM8804_NUM_SUPPLIES]; | 66 | struct notifier_block disable_nb[WM8804_NUM_SUPPLIES]; |
63 | int mclk_div; | 67 | int mclk_div; |
64 | }; | ||
65 | 68 | ||
66 | static int txsrc_get(struct snd_kcontrol *kcontrol, | 69 | struct gpio_desc *reset; |
67 | struct snd_ctl_elem_value *ucontrol); | 70 | |
71 | int aif_pwr; | ||
72 | }; | ||
68 | 73 | ||
69 | static int txsrc_put(struct snd_kcontrol *kcontrol, | 74 | static int txsrc_put(struct snd_kcontrol *kcontrol, |
70 | struct snd_ctl_elem_value *ucontrol); | 75 | struct snd_ctl_elem_value *ucontrol); |
71 | 76 | ||
77 | static int wm8804_aif_event(struct snd_soc_dapm_widget *w, | ||
78 | struct snd_kcontrol *kcontrol, int event); | ||
79 | |||
72 | /* | 80 | /* |
73 | * We can't use the same notifier block for more than one supply and | 81 | * We can't use the same notifier block for more than one supply and |
74 | * there's no way I can see to get from a callback to the caller | 82 | * there's no way I can see to get from a callback to the caller |
@@ -90,26 +98,62 @@ WM8804_REGULATOR_EVENT(0) | |||
90 | WM8804_REGULATOR_EVENT(1) | 98 | WM8804_REGULATOR_EVENT(1) |
91 | 99 | ||
92 | static const char *txsrc_text[] = { "S/PDIF RX", "AIF" }; | 100 | static const char *txsrc_text[] = { "S/PDIF RX", "AIF" }; |
93 | static SOC_ENUM_SINGLE_EXT_DECL(txsrc, txsrc_text); | 101 | static const SOC_ENUM_SINGLE_DECL(txsrc, WM8804_SPDTX4, 6, txsrc_text); |
94 | 102 | ||
95 | static const struct snd_kcontrol_new wm8804_snd_controls[] = { | 103 | static const struct snd_kcontrol_new wm8804_tx_source_mux[] = { |
96 | SOC_ENUM_EXT("Input Source", txsrc, txsrc_get, txsrc_put), | 104 | SOC_DAPM_ENUM_EXT("Input Source", txsrc, |
97 | SOC_SINGLE("TX Playback Switch", WM8804_PWRDN, 2, 1, 1), | 105 | snd_soc_dapm_get_enum_double, txsrc_put), |
98 | SOC_SINGLE("AIF Playback Switch", WM8804_PWRDN, 4, 1, 1) | ||
99 | }; | 106 | }; |
100 | 107 | ||
101 | static int txsrc_get(struct snd_kcontrol *kcontrol, | 108 | static const struct snd_soc_dapm_widget wm8804_dapm_widgets[] = { |
102 | struct snd_ctl_elem_value *ucontrol) | 109 | SND_SOC_DAPM_OUTPUT("SPDIF Out"), |
103 | { | 110 | SND_SOC_DAPM_INPUT("SPDIF In"), |
104 | struct snd_soc_codec *codec; | 111 | |
105 | unsigned int src; | 112 | SND_SOC_DAPM_PGA("SPDIFTX", WM8804_PWRDN, 2, 1, NULL, 0), |
113 | SND_SOC_DAPM_PGA("SPDIFRX", WM8804_PWRDN, 1, 1, NULL, 0), | ||
114 | |||
115 | SND_SOC_DAPM_MUX("Tx Source", SND_SOC_NOPM, 6, 0, wm8804_tx_source_mux), | ||
116 | |||
117 | SND_SOC_DAPM_AIF_OUT_E("AIFTX", NULL, 0, SND_SOC_NOPM, 0, 0, wm8804_aif_event, | ||
118 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | ||
119 | SND_SOC_DAPM_AIF_IN_E("AIFRX", NULL, 0, SND_SOC_NOPM, 0, 0, wm8804_aif_event, | ||
120 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | ||
121 | }; | ||
122 | |||
123 | static const struct snd_soc_dapm_route wm8804_dapm_routes[] = { | ||
124 | { "AIFRX", NULL, "Playback" }, | ||
125 | { "Tx Source", "AIF", "AIFRX" }, | ||
126 | |||
127 | { "SPDIFRX", NULL, "SPDIF In" }, | ||
128 | { "Tx Source", "S/PDIF RX", "SPDIFRX" }, | ||
106 | 129 | ||
107 | codec = snd_soc_kcontrol_codec(kcontrol); | 130 | { "SPDIFTX", NULL, "Tx Source" }, |
108 | src = snd_soc_read(codec, WM8804_SPDTX4); | 131 | { "SPDIF Out", NULL, "SPDIFTX" }, |
109 | if (src & 0x40) | 132 | |
110 | ucontrol->value.integer.value[0] = 1; | 133 | { "AIFTX", NULL, "SPDIFRX" }, |
111 | else | 134 | { "Capture", NULL, "AIFTX" }, |
112 | ucontrol->value.integer.value[0] = 0; | 135 | }; |
136 | |||
137 | static int wm8804_aif_event(struct snd_soc_dapm_widget *w, | ||
138 | struct snd_kcontrol *kcontrol, int event) | ||
139 | { | ||
140 | struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); | ||
141 | struct wm8804_priv *wm8804 = snd_soc_codec_get_drvdata(codec); | ||
142 | |||
143 | switch (event) { | ||
144 | case SND_SOC_DAPM_POST_PMU: | ||
145 | /* power up the aif */ | ||
146 | if (!wm8804->aif_pwr) | ||
147 | snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0x0); | ||
148 | wm8804->aif_pwr++; | ||
149 | break; | ||
150 | case SND_SOC_DAPM_POST_PMD: | ||
151 | /* power down only both paths are disabled */ | ||
152 | wm8804->aif_pwr--; | ||
153 | if (!wm8804->aif_pwr) | ||
154 | snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0x10); | ||
155 | break; | ||
156 | } | ||
113 | 157 | ||
114 | return 0; | 158 | return 0; |
115 | } | 159 | } |
@@ -117,48 +161,33 @@ static int txsrc_get(struct snd_kcontrol *kcontrol, | |||
117 | static int txsrc_put(struct snd_kcontrol *kcontrol, | 161 | static int txsrc_put(struct snd_kcontrol *kcontrol, |
118 | struct snd_ctl_elem_value *ucontrol) | 162 | struct snd_ctl_elem_value *ucontrol) |
119 | { | 163 | { |
120 | struct snd_soc_codec *codec; | 164 | struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); |
121 | unsigned int src, txpwr; | 165 | struct snd_soc_dapm_context *dapm = &codec->dapm; |
166 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | ||
167 | unsigned int val = ucontrol->value.enumerated.item[0] << e->shift_l; | ||
168 | unsigned int mask = 1 << e->shift_l; | ||
169 | unsigned int txpwr; | ||
170 | |||
171 | if (val != 0 && val != mask) | ||
172 | return -EINVAL; | ||
122 | 173 | ||
123 | codec = snd_soc_kcontrol_codec(kcontrol); | 174 | snd_soc_dapm_mutex_lock(dapm); |
124 | 175 | ||
125 | if (ucontrol->value.integer.value[0] != 0 | 176 | if (snd_soc_test_bits(codec, e->reg, mask, val)) { |
126 | && ucontrol->value.integer.value[0] != 1) | 177 | /* save the current power state of the transmitter */ |
127 | return -EINVAL; | 178 | txpwr = snd_soc_read(codec, WM8804_PWRDN) & 0x4; |
128 | 179 | ||
129 | src = snd_soc_read(codec, WM8804_SPDTX4); | 180 | /* power down the transmitter */ |
130 | switch ((src & 0x40) >> 6) { | 181 | snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x4); |
131 | case 0: | ||
132 | if (!ucontrol->value.integer.value[0]) | ||
133 | return 0; | ||
134 | break; | ||
135 | case 1: | ||
136 | if (ucontrol->value.integer.value[1]) | ||
137 | return 0; | ||
138 | break; | ||
139 | } | ||
140 | 182 | ||
141 | /* save the current power state of the transmitter */ | 183 | /* set the tx source */ |
142 | txpwr = snd_soc_read(codec, WM8804_PWRDN) & 0x4; | 184 | snd_soc_update_bits(codec, e->reg, mask, val); |
143 | /* power down the transmitter */ | 185 | |
144 | snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x4); | 186 | /* restore the transmitter's configuration */ |
145 | /* set the tx source */ | 187 | snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, txpwr); |
146 | snd_soc_update_bits(codec, WM8804_SPDTX4, 0x40, | ||
147 | ucontrol->value.integer.value[0] << 6); | ||
148 | |||
149 | if (ucontrol->value.integer.value[0]) { | ||
150 | /* power down the receiver */ | ||
151 | snd_soc_update_bits(codec, WM8804_PWRDN, 0x2, 0x2); | ||
152 | /* power up the AIF */ | ||
153 | snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0); | ||
154 | } else { | ||
155 | /* don't power down the AIF -- may be used as an output */ | ||
156 | /* power up the receiver */ | ||
157 | snd_soc_update_bits(codec, WM8804_PWRDN, 0x2, 0); | ||
158 | } | 188 | } |
159 | 189 | ||
160 | /* restore the transmitter's configuration */ | 190 | snd_soc_dapm_mutex_unlock(dapm); |
161 | snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, txpwr); | ||
162 | 191 | ||
163 | return 0; | 192 | return 0; |
164 | } | 193 | } |
@@ -182,7 +211,7 @@ static bool wm8804_volatile(struct device *dev, unsigned int reg) | |||
182 | } | 211 | } |
183 | } | 212 | } |
184 | 213 | ||
185 | static int wm8804_reset(struct wm8804_priv *wm8804) | 214 | static int wm8804_soft_reset(struct wm8804_priv *wm8804) |
186 | { | 215 | { |
187 | return regmap_write(wm8804->regmap, WM8804_RST_DEVID1, 0x0); | 216 | return regmap_write(wm8804->regmap, WM8804_RST_DEVID1, 0x0); |
188 | } | 217 | } |
@@ -376,19 +405,19 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id, | |||
376 | int source, unsigned int freq_in, | 405 | int source, unsigned int freq_in, |
377 | unsigned int freq_out) | 406 | unsigned int freq_out) |
378 | { | 407 | { |
379 | 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; | ||
380 | 411 | ||
381 | codec = dai->codec; | ||
382 | if (!freq_in || !freq_out) { | 412 | if (!freq_in || !freq_out) { |
383 | /* disable the PLL */ | 413 | /* disable the PLL */ |
384 | snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0x1); | 414 | regmap_update_bits_check(wm8804->regmap, WM8804_PWRDN, |
385 | return 0; | 415 | 0x1, 0x1, &change); |
416 | if (change) | ||
417 | pm_runtime_put(wm8804->dev); | ||
386 | } else { | 418 | } else { |
387 | int ret; | 419 | int ret; |
388 | struct pll_div pll_div; | 420 | struct pll_div pll_div; |
389 | struct wm8804_priv *wm8804; | ||
390 | |||
391 | wm8804 = snd_soc_codec_get_drvdata(codec); | ||
392 | 421 | ||
393 | ret = pll_factors(&pll_div, freq_out, freq_in, | 422 | ret = pll_factors(&pll_div, freq_out, freq_in, |
394 | wm8804->mclk_div); | 423 | wm8804->mclk_div); |
@@ -396,7 +425,10 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id, | |||
396 | return ret; | 425 | return ret; |
397 | 426 | ||
398 | /* power down the PLL before reprogramming it */ | 427 | /* power down the PLL before reprogramming it */ |
399 | 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); | ||
400 | 432 | ||
401 | /* set PLLN and PRESCALE */ | 433 | /* set PLLN and PRESCALE */ |
402 | snd_soc_update_bits(codec, WM8804_PLL4, 0xf | 0x10, | 434 | snd_soc_update_bits(codec, WM8804_PLL4, 0xf | 0x10, |
@@ -474,47 +506,6 @@ static int wm8804_set_clkdiv(struct snd_soc_dai *dai, | |||
474 | return 0; | 506 | return 0; |
475 | } | 507 | } |
476 | 508 | ||
477 | static int wm8804_set_bias_level(struct snd_soc_codec *codec, | ||
478 | enum snd_soc_bias_level level) | ||
479 | { | ||
480 | int ret; | ||
481 | struct wm8804_priv *wm8804; | ||
482 | |||
483 | wm8804 = snd_soc_codec_get_drvdata(codec); | ||
484 | switch (level) { | ||
485 | case SND_SOC_BIAS_ON: | ||
486 | break; | ||
487 | case SND_SOC_BIAS_PREPARE: | ||
488 | /* power up the OSC and the PLL */ | ||
489 | snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0); | ||
490 | break; | ||
491 | case SND_SOC_BIAS_STANDBY: | ||
492 | if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { | ||
493 | ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies), | ||
494 | wm8804->supplies); | ||
495 | if (ret) { | ||
496 | dev_err(codec->dev, | ||
497 | "Failed to enable supplies: %d\n", | ||
498 | ret); | ||
499 | return ret; | ||
500 | } | ||
501 | regcache_sync(wm8804->regmap); | ||
502 | } | ||
503 | /* power down the OSC and the PLL */ | ||
504 | snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0x9); | ||
505 | break; | ||
506 | case SND_SOC_BIAS_OFF: | ||
507 | /* power down the OSC and the PLL */ | ||
508 | snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0x9); | ||
509 | regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), | ||
510 | wm8804->supplies); | ||
511 | break; | ||
512 | } | ||
513 | |||
514 | codec->dapm.bias_level = level; | ||
515 | return 0; | ||
516 | } | ||
517 | |||
518 | static const struct snd_soc_dai_ops wm8804_dai_ops = { | 509 | static const struct snd_soc_dai_ops wm8804_dai_ops = { |
519 | .hw_params = wm8804_hw_params, | 510 | .hw_params = wm8804_hw_params, |
520 | .set_fmt = wm8804_set_fmt, | 511 | .set_fmt = wm8804_set_fmt, |
@@ -552,11 +543,12 @@ static struct snd_soc_dai_driver wm8804_dai = { | |||
552 | }; | 543 | }; |
553 | 544 | ||
554 | static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = { | 545 | static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = { |
555 | .set_bias_level = wm8804_set_bias_level, | ||
556 | .idle_bias_off = true, | 546 | .idle_bias_off = true, |
557 | 547 | ||
558 | .controls = wm8804_snd_controls, | 548 | .dapm_widgets = wm8804_dapm_widgets, |
559 | .num_controls = ARRAY_SIZE(wm8804_snd_controls), | 549 | .num_dapm_widgets = ARRAY_SIZE(wm8804_dapm_widgets), |
550 | .dapm_routes = wm8804_dapm_routes, | ||
551 | .num_dapm_routes = ARRAY_SIZE(wm8804_dapm_routes), | ||
560 | }; | 552 | }; |
561 | 553 | ||
562 | const struct regmap_config wm8804_regmap_config = { | 554 | const struct regmap_config wm8804_regmap_config = { |
@@ -584,8 +576,17 @@ int wm8804_probe(struct device *dev, struct regmap *regmap) | |||
584 | 576 | ||
585 | dev_set_drvdata(dev, wm8804); | 577 | dev_set_drvdata(dev, wm8804); |
586 | 578 | ||
579 | wm8804->dev = dev; | ||
587 | wm8804->regmap = regmap; | 580 | wm8804->regmap = regmap; |
588 | 581 | ||
582 | wm8804->reset = devm_gpiod_get_optional(dev, "wlf,reset", | ||
583 | GPIOD_OUT_LOW); | ||
584 | if (IS_ERR(wm8804->reset)) { | ||
585 | ret = PTR_ERR(wm8804->reset); | ||
586 | dev_err(dev, "Failed to get reset line: %d\n", ret); | ||
587 | return ret; | ||
588 | } | ||
589 | |||
589 | for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) | 590 | for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) |
590 | wm8804->supplies[i].supply = wm8804_supply_names[i]; | 591 | wm8804->supplies[i].supply = wm8804_supply_names[i]; |
591 | 592 | ||
@@ -601,12 +602,15 @@ int wm8804_probe(struct device *dev, struct regmap *regmap) | |||
601 | 602 | ||
602 | /* This should really be moved into the regulator core */ | 603 | /* This should really be moved into the regulator core */ |
603 | for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) { | 604 | for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) { |
604 | ret = regulator_register_notifier(wm8804->supplies[i].consumer, | 605 | struct regulator *regulator = wm8804->supplies[i].consumer; |
605 | &wm8804->disable_nb[i]); | 606 | |
607 | ret = devm_regulator_register_notifier(regulator, | ||
608 | &wm8804->disable_nb[i]); | ||
606 | if (ret != 0) { | 609 | if (ret != 0) { |
607 | dev_err(dev, | 610 | dev_err(dev, |
608 | "Failed to register regulator notifier: %d\n", | 611 | "Failed to register regulator notifier: %d\n", |
609 | ret); | 612 | ret); |
613 | return ret; | ||
610 | } | 614 | } |
611 | } | 615 | } |
612 | 616 | ||
@@ -614,9 +618,12 @@ int wm8804_probe(struct device *dev, struct regmap *regmap) | |||
614 | wm8804->supplies); | 618 | wm8804->supplies); |
615 | if (ret) { | 619 | if (ret) { |
616 | dev_err(dev, "Failed to enable supplies: %d\n", ret); | 620 | dev_err(dev, "Failed to enable supplies: %d\n", ret); |
617 | goto err_reg_enable; | 621 | return ret; |
618 | } | 622 | } |
619 | 623 | ||
624 | if (wm8804->reset) | ||
625 | gpiod_set_value_cansleep(wm8804->reset, 1); | ||
626 | |||
620 | ret = regmap_read(regmap, WM8804_RST_DEVID1, &id1); | 627 | ret = regmap_read(regmap, WM8804_RST_DEVID1, &id1); |
621 | if (ret < 0) { | 628 | if (ret < 0) { |
622 | dev_err(dev, "Failed to read device ID: %d\n", ret); | 629 | dev_err(dev, "Failed to read device ID: %d\n", ret); |
@@ -645,14 +652,26 @@ int wm8804_probe(struct device *dev, struct regmap *regmap) | |||
645 | } | 652 | } |
646 | dev_info(dev, "revision %c\n", id1 + 'A'); | 653 | dev_info(dev, "revision %c\n", id1 + 'A'); |
647 | 654 | ||
648 | ret = wm8804_reset(wm8804); | 655 | if (!wm8804->reset) { |
656 | ret = wm8804_soft_reset(wm8804); | ||
657 | if (ret < 0) { | ||
658 | dev_err(dev, "Failed to issue reset: %d\n", ret); | ||
659 | goto err_reg_enable; | ||
660 | } | ||
661 | } | ||
662 | |||
663 | ret = snd_soc_register_codec(dev, &soc_codec_dev_wm8804, | ||
664 | &wm8804_dai, 1); | ||
649 | if (ret < 0) { | 665 | if (ret < 0) { |
650 | dev_err(dev, "Failed to issue reset: %d\n", ret); | 666 | dev_err(dev, "Failed to register CODEC: %d\n", ret); |
651 | goto err_reg_enable; | 667 | goto err_reg_enable; |
652 | } | 668 | } |
653 | 669 | ||
654 | return snd_soc_register_codec(dev, &soc_codec_dev_wm8804, | 670 | pm_runtime_set_active(dev); |
655 | &wm8804_dai, 1); | 671 | pm_runtime_enable(dev); |
672 | pm_runtime_idle(dev); | ||
673 | |||
674 | return 0; | ||
656 | 675 | ||
657 | err_reg_enable: | 676 | err_reg_enable: |
658 | regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies); | 677 | regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies); |
@@ -662,18 +681,50 @@ EXPORT_SYMBOL_GPL(wm8804_probe); | |||
662 | 681 | ||
663 | void wm8804_remove(struct device *dev) | 682 | void wm8804_remove(struct device *dev) |
664 | { | 683 | { |
665 | struct wm8804_priv *wm8804; | 684 | pm_runtime_disable(dev); |
666 | int i; | 685 | snd_soc_unregister_codec(dev); |
686 | } | ||
687 | EXPORT_SYMBOL_GPL(wm8804_remove); | ||
667 | 688 | ||
668 | wm8804 = dev_get_drvdata(dev); | 689 | #if IS_ENABLED(CONFIG_PM) |
690 | static int wm8804_runtime_resume(struct device *dev) | ||
691 | { | ||
692 | struct wm8804_priv *wm8804 = dev_get_drvdata(dev); | ||
693 | int ret; | ||
669 | 694 | ||
670 | for (i = 0; i < ARRAY_SIZE(wm8804->supplies); ++i) | 695 | ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies), |
671 | regulator_unregister_notifier(wm8804->supplies[i].consumer, | 696 | wm8804->supplies); |
672 | &wm8804->disable_nb[i]); | 697 | if (ret) { |
698 | dev_err(wm8804->dev, "Failed to enable supplies: %d\n", ret); | ||
699 | return ret; | ||
700 | } | ||
673 | 701 | ||
674 | snd_soc_unregister_codec(dev); | 702 | regcache_sync(wm8804->regmap); |
703 | |||
704 | /* Power up OSCCLK */ | ||
705 | regmap_update_bits(wm8804->regmap, WM8804_PWRDN, 0x8, 0x0); | ||
706 | |||
707 | return 0; | ||
675 | } | 708 | } |
676 | EXPORT_SYMBOL_GPL(wm8804_remove); | 709 | |
710 | static 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 | |||
724 | const struct dev_pm_ops wm8804_pm = { | ||
725 | SET_RUNTIME_PM_OPS(wm8804_runtime_suspend, wm8804_runtime_resume, NULL) | ||
726 | }; | ||
727 | EXPORT_SYMBOL_GPL(wm8804_pm); | ||
677 | 728 | ||
678 | MODULE_DESCRIPTION("ASoC WM8804 driver"); | 729 | MODULE_DESCRIPTION("ASoC WM8804 driver"); |
679 | MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>"); | 730 | MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>"); |
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 | ||
67 | extern const struct regmap_config wm8804_regmap_config; | 67 | extern const struct regmap_config wm8804_regmap_config; |
68 | extern const struct dev_pm_ops wm8804_pm; | ||
68 | 69 | ||
69 | int wm8804_probe(struct device *dev, struct regmap *regmap); | 70 | int wm8804_probe(struct device *dev, struct regmap *regmap); |
70 | void wm8804_remove(struct device *dev); | 71 | void wm8804_remove(struct device *dev); |
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index dc92d5e4e942..308748a022c5 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c | |||
@@ -2009,7 +2009,7 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source, | |||
2009 | struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); | 2009 | struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); |
2010 | struct i2c_client *i2c = to_i2c_client(codec->dev); | 2010 | struct i2c_client *i2c = to_i2c_client(codec->dev); |
2011 | struct _fll_div fll_div; | 2011 | struct _fll_div fll_div; |
2012 | unsigned long timeout; | 2012 | unsigned long timeout, time_left; |
2013 | int ret, reg, retry; | 2013 | int ret, reg, retry; |
2014 | 2014 | ||
2015 | /* Any change? */ | 2015 | /* Any change? */ |
@@ -2110,13 +2110,15 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source, | |||
2110 | if (i2c->irq) | 2110 | if (i2c->irq) |
2111 | timeout *= 10; | 2111 | timeout *= 10; |
2112 | else | 2112 | else |
2113 | timeout /= 2; | 2113 | /* ensure timeout of atleast 1 jiffies */ |
2114 | timeout = timeout/2 ? : 1; | ||
2114 | 2115 | ||
2115 | for (retry = 0; retry < 10; retry++) { | 2116 | for (retry = 0; retry < 10; retry++) { |
2116 | ret = wait_for_completion_timeout(&wm8996->fll_lock, | 2117 | time_left = wait_for_completion_timeout(&wm8996->fll_lock, |
2117 | timeout); | 2118 | timeout); |
2118 | if (ret != 0) { | 2119 | if (time_left != 0) { |
2119 | WARN_ON(!i2c->irq); | 2120 | WARN_ON(!i2c->irq); |
2121 | ret = 1; | ||
2120 | break; | 2122 | break; |
2121 | } | 2123 | } |
2122 | 2124 | ||