diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/wm8804.c | 140 |
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 | ||
69 | static int txsrc_get(struct snd_kcontrol *kcontrol, | 69 | int aif_pwr; |
70 | struct snd_ctl_elem_value *ucontrol); | 70 | }; |
71 | 71 | ||
72 | static int txsrc_put(struct snd_kcontrol *kcontrol, | 72 | static int txsrc_put(struct snd_kcontrol *kcontrol, |
73 | struct snd_ctl_elem_value *ucontrol); | 73 | struct snd_ctl_elem_value *ucontrol); |
74 | 74 | ||
75 | static 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) | |||
93 | WM8804_REGULATOR_EVENT(1) | 96 | WM8804_REGULATOR_EVENT(1) |
94 | 97 | ||
95 | static const char *txsrc_text[] = { "S/PDIF RX", "AIF" }; | 98 | static const char *txsrc_text[] = { "S/PDIF RX", "AIF" }; |
96 | static SOC_ENUM_SINGLE_EXT_DECL(txsrc, txsrc_text); | 99 | static const SOC_ENUM_SINGLE_DECL(txsrc, WM8804_SPDTX4, 6, txsrc_text); |
97 | 100 | ||
98 | static const struct snd_kcontrol_new wm8804_snd_controls[] = { | 101 | static 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 | ||
104 | static int txsrc_get(struct snd_kcontrol *kcontrol, | 106 | static const struct snd_soc_dapm_widget wm8804_dapm_widgets[] = { |
105 | struct snd_ctl_elem_value *ucontrol) | 107 | SND_SOC_DAPM_OUTPUT("SPDIF Out"), |
106 | { | 108 | SND_SOC_DAPM_INPUT("SPDIF In"), |
107 | struct snd_soc_codec *codec; | 109 | |
108 | unsigned int src; | 110 | SND_SOC_DAPM_PGA("SPDIFTX", WM8804_PWRDN, 2, 1, NULL, 0), |
111 | SND_SOC_DAPM_PGA("SPDIFRX", WM8804_PWRDN, 1, 1, NULL, 0), | ||
112 | |||
113 | SND_SOC_DAPM_MUX("Tx Source", SND_SOC_NOPM, 6, 0, wm8804_tx_source_mux), | ||
114 | |||
115 | SND_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), | ||
117 | SND_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 | |||
121 | static 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 | 135 | static 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, | |||
120 | static int txsrc_put(struct snd_kcontrol *kcontrol, | 159 | static 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 | ||
565 | const struct regmap_config wm8804_regmap_config = { | 591 | const struct regmap_config wm8804_regmap_config = { |