aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorSapthagiri Baratam <sapthagiri.baratam@incubesol.com>2015-04-07 07:55:09 -0400
committerMark Brown <broonie@kernel.org>2015-04-08 14:25:40 -0400
commit5631f18763f1b0989cec7cbf8f3badcb74dfe469 (patch)
tree3f9e63cd0cf6ccb130b040b56f0ae665b2e09877 /sound
parent9b5c352e5425622b4371387d4718060da5d3ae32 (diff)
ASoC: wm8804: Add DAPM widgets for SPDIF/AIF
This change converts the driver to use DAPM to control the power for the various blocks on the chip. As part of this change the existing controls "TX Playback Switch" (controlled power for the SPDIF TX block) and "AIF Playback Switch" (controlled power for the AIF block) are both removed, as they are now redundant since the power state of those blocks is controlled automatically by DAPM. There are several benefits of this change, the most important of which is this change adds support for powering down the SPDIF RX block. The RX block will automatically assume control of the PLL on the chip when it is receiving a signal, so leaving this enabled all the time as was currently done in the driver can be problematic. An incoming SPDIF signal that is not being used can completely destroy the clocking for an in use TX signal. But this change ensures that the RX block will only be powered when the user intends to be receiving data, thus avoiding this issue. Additional benefits include the chip being simpler to operate as the power no longer needs to be manually controlled between use-cases and a small power saving (although it is acknowledged that this is likely unimportant in the typical use-cases for this chip). Signed-off-by: Sapthagiri Baratam <sapthagiri.baratam@incubesol.com> Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com> Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/wm8804.c140
1 files changed, 83 insertions, 57 deletions
diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c
index cc168c4a4be0..cff34be61f88 100644
--- a/sound/soc/codecs/wm8804.c
+++ b/sound/soc/codecs/wm8804.c
@@ -25,6 +25,7 @@
25#include <sound/soc.h> 25#include <sound/soc.h>
26#include <sound/initval.h> 26#include <sound/initval.h>
27#include <sound/tlv.h> 27#include <sound/tlv.h>
28#include <sound/soc-dapm.h>
28 29
29#include "wm8804.h" 30#include "wm8804.h"
30 31
@@ -64,14 +65,16 @@ struct wm8804_priv {
64 int mclk_div; 65 int mclk_div;
65 66
66 struct gpio_desc *reset; 67 struct gpio_desc *reset;
67};
68 68
69static int txsrc_get(struct snd_kcontrol *kcontrol, 69 int aif_pwr;
70 struct snd_ctl_elem_value *ucontrol); 70};
71 71
72static int txsrc_put(struct snd_kcontrol *kcontrol, 72static int txsrc_put(struct snd_kcontrol *kcontrol,
73 struct snd_ctl_elem_value *ucontrol); 73 struct snd_ctl_elem_value *ucontrol);
74 74
75static int wm8804_aif_event(struct snd_soc_dapm_widget *w,
76 struct snd_kcontrol *kcontrol, int event);
77
75/* 78/*
76 * We can't use the same notifier block for more than one supply and 79 * We can't use the same notifier block for more than one supply and
77 * there's no way I can see to get from a callback to the caller 80 * there's no way I can see to get from a callback to the caller
@@ -93,26 +96,62 @@ WM8804_REGULATOR_EVENT(0)
93WM8804_REGULATOR_EVENT(1) 96WM8804_REGULATOR_EVENT(1)
94 97
95static const char *txsrc_text[] = { "S/PDIF RX", "AIF" }; 98static const char *txsrc_text[] = { "S/PDIF RX", "AIF" };
96static SOC_ENUM_SINGLE_EXT_DECL(txsrc, txsrc_text); 99static const SOC_ENUM_SINGLE_DECL(txsrc, WM8804_SPDTX4, 6, txsrc_text);
97 100
98static const struct snd_kcontrol_new wm8804_snd_controls[] = { 101static const struct snd_kcontrol_new wm8804_tx_source_mux[] = {
99 SOC_ENUM_EXT("Input Source", txsrc, txsrc_get, txsrc_put), 102 SOC_DAPM_ENUM_EXT("Input Source", txsrc,
100 SOC_SINGLE("TX Playback Switch", WM8804_PWRDN, 2, 1, 1), 103 snd_soc_dapm_get_enum_double, txsrc_put),
101 SOC_SINGLE("AIF Playback Switch", WM8804_PWRDN, 4, 1, 1)
102}; 104};
103 105
104static int txsrc_get(struct snd_kcontrol *kcontrol, 106static const struct snd_soc_dapm_widget wm8804_dapm_widgets[] = {
105 struct snd_ctl_elem_value *ucontrol) 107SND_SOC_DAPM_OUTPUT("SPDIF Out"),
106{ 108SND_SOC_DAPM_INPUT("SPDIF In"),
107 struct snd_soc_codec *codec; 109
108 unsigned int src; 110SND_SOC_DAPM_PGA("SPDIFTX", WM8804_PWRDN, 2, 1, NULL, 0),
111SND_SOC_DAPM_PGA("SPDIFRX", WM8804_PWRDN, 1, 1, NULL, 0),
112
113SND_SOC_DAPM_MUX("Tx Source", SND_SOC_NOPM, 6, 0, wm8804_tx_source_mux),
114
115SND_SOC_DAPM_AIF_OUT_E("AIFTX", NULL, 0, SND_SOC_NOPM, 0, 0, wm8804_aif_event,
116 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
117SND_SOC_DAPM_AIF_IN_E("AIFRX", NULL, 0, SND_SOC_NOPM, 0, 0, wm8804_aif_event,
118 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
119};
120
121static const struct snd_soc_dapm_route wm8804_dapm_routes[] = {
122 { "AIFRX", NULL, "Playback" },
123 { "Tx Source", "AIF", "AIFRX" },
124
125 { "SPDIFRX", NULL, "SPDIF In" },
126 { "Tx Source", "S/PDIF RX", "SPDIFRX" },
127
128 { "SPDIFTX", NULL, "Tx Source" },
129 { "SPDIF Out", NULL, "SPDIFTX" },
109 130
110 codec = snd_soc_kcontrol_codec(kcontrol); 131 { "AIFTX", NULL, "SPDIFRX" },
111 src = snd_soc_read(codec, WM8804_SPDTX4); 132 { "Capture", NULL, "AIFTX" },
112 if (src & 0x40) 133};
113 ucontrol->value.integer.value[0] = 1; 134
114 else 135static int wm8804_aif_event(struct snd_soc_dapm_widget *w,
115 ucontrol->value.integer.value[0] = 0; 136 struct snd_kcontrol *kcontrol, int event)
137{
138 struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
139 struct wm8804_priv *wm8804 = snd_soc_codec_get_drvdata(codec);
140
141 switch (event) {
142 case SND_SOC_DAPM_POST_PMU:
143 /* power up the aif */
144 if (!wm8804->aif_pwr)
145 snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0x0);
146 wm8804->aif_pwr++;
147 break;
148 case SND_SOC_DAPM_POST_PMD:
149 /* power down only both paths are disabled */
150 wm8804->aif_pwr--;
151 if (!wm8804->aif_pwr)
152 snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0x10);
153 break;
154 }
116 155
117 return 0; 156 return 0;
118} 157}
@@ -120,48 +159,33 @@ static int txsrc_get(struct snd_kcontrol *kcontrol,
120static int txsrc_put(struct snd_kcontrol *kcontrol, 159static int txsrc_put(struct snd_kcontrol *kcontrol,
121 struct snd_ctl_elem_value *ucontrol) 160 struct snd_ctl_elem_value *ucontrol)
122{ 161{
123 struct snd_soc_codec *codec; 162 struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
124 unsigned int src, txpwr; 163 struct snd_soc_dapm_context *dapm = &codec->dapm;
164 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
165 unsigned int val = ucontrol->value.enumerated.item[0] << e->shift_l;
166 unsigned int mask = 1 << e->shift_l;
167 unsigned int txpwr;
168
169 if (val != 0 && val != mask)
170 return -EINVAL;
125 171
126 codec = snd_soc_kcontrol_codec(kcontrol); 172 snd_soc_dapm_mutex_lock(dapm);
127 173
128 if (ucontrol->value.integer.value[0] != 0 174 if (snd_soc_test_bits(codec, e->reg, mask, val)) {
129 && ucontrol->value.integer.value[0] != 1) 175 /* save the current power state of the transmitter */
130 return -EINVAL; 176 txpwr = snd_soc_read(codec, WM8804_PWRDN) & 0x4;
131 177
132 src = snd_soc_read(codec, WM8804_SPDTX4); 178 /* power down the transmitter */
133 switch ((src & 0x40) >> 6) { 179 snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x4);
134 case 0:
135 if (!ucontrol->value.integer.value[0])
136 return 0;
137 break;
138 case 1:
139 if (ucontrol->value.integer.value[1])
140 return 0;
141 break;
142 }
143 180
144 /* save the current power state of the transmitter */ 181 /* set the tx source */
145 txpwr = snd_soc_read(codec, WM8804_PWRDN) & 0x4; 182 snd_soc_update_bits(codec, e->reg, mask, val);
146 /* power down the transmitter */ 183
147 snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x4); 184 /* restore the transmitter's configuration */
148 /* set the tx source */ 185 snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, txpwr);
149 snd_soc_update_bits(codec, WM8804_SPDTX4, 0x40,
150 ucontrol->value.integer.value[0] << 6);
151
152 if (ucontrol->value.integer.value[0]) {
153 /* power down the receiver */
154 snd_soc_update_bits(codec, WM8804_PWRDN, 0x2, 0x2);
155 /* power up the AIF */
156 snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0);
157 } else {
158 /* don't power down the AIF -- may be used as an output */
159 /* power up the receiver */
160 snd_soc_update_bits(codec, WM8804_PWRDN, 0x2, 0);
161 } 186 }
162 187
163 /* restore the transmitter's configuration */ 188 snd_soc_dapm_mutex_unlock(dapm);
164 snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, txpwr);
165 189
166 return 0; 190 return 0;
167} 191}
@@ -558,8 +582,10 @@ static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = {
558 .set_bias_level = wm8804_set_bias_level, 582 .set_bias_level = wm8804_set_bias_level,
559 .idle_bias_off = true, 583 .idle_bias_off = true,
560 584
561 .controls = wm8804_snd_controls, 585 .dapm_widgets = wm8804_dapm_widgets,
562 .num_controls = ARRAY_SIZE(wm8804_snd_controls), 586 .num_dapm_widgets = ARRAY_SIZE(wm8804_dapm_widgets),
587 .dapm_routes = wm8804_dapm_routes,
588 .num_dapm_routes = ARRAY_SIZE(wm8804_dapm_routes),
563}; 589};
564 590
565const struct regmap_config wm8804_regmap_config = { 591const struct regmap_config wm8804_regmap_config = {