aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
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 = {