1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * This driver supports the analog controls for the internal codec |
4 | * found in Allwinner's A64 SoC. |
5 | * |
6 | * Copyright (C) 2016 Chen-Yu Tsai <wens@csie.org> |
7 | * Copyright (C) 2017 Marcus Cooper <codekipper@gmail.com> |
8 | * Copyright (C) 2018 Vasily Khoruzhick <anarsoul@gmail.com> |
9 | * |
10 | * Based on sun8i-codec-analog.c |
11 | * |
12 | */ |
13 | |
14 | #include <linux/io.h> |
15 | #include <linux/kernel.h> |
16 | #include <linux/mod_devicetable.h> |
17 | #include <linux/module.h> |
18 | #include <linux/platform_device.h> |
19 | #include <linux/regmap.h> |
20 | |
21 | #include <sound/soc.h> |
22 | #include <sound/soc-dapm.h> |
23 | #include <sound/tlv.h> |
24 | |
25 | #include "sun8i-adda-pr-regmap.h" |
26 | |
27 | /* Codec analog control register offsets and bit fields */ |
28 | #define SUN50I_ADDA_HP_CTRL 0x00 |
29 | #define SUN50I_ADDA_HP_CTRL_PA_CLK_GATE 7 |
30 | #define SUN50I_ADDA_HP_CTRL_HPPA_EN 6 |
31 | #define SUN50I_ADDA_HP_CTRL_HPVOL 0 |
32 | |
33 | #define SUN50I_ADDA_OL_MIX_CTRL 0x01 |
34 | #define SUN50I_ADDA_OL_MIX_CTRL_MIC1 6 |
35 | #define SUN50I_ADDA_OL_MIX_CTRL_MIC2 5 |
36 | #define SUN50I_ADDA_OL_MIX_CTRL_PHONE 4 |
37 | #define SUN50I_ADDA_OL_MIX_CTRL_PHONEN 3 |
38 | #define SUN50I_ADDA_OL_MIX_CTRL_LINEINL 2 |
39 | #define SUN50I_ADDA_OL_MIX_CTRL_DACL 1 |
40 | #define SUN50I_ADDA_OL_MIX_CTRL_DACR 0 |
41 | |
42 | #define SUN50I_ADDA_OR_MIX_CTRL 0x02 |
43 | #define SUN50I_ADDA_OR_MIX_CTRL_MIC1 6 |
44 | #define SUN50I_ADDA_OR_MIX_CTRL_MIC2 5 |
45 | #define SUN50I_ADDA_OR_MIX_CTRL_PHONE 4 |
46 | #define SUN50I_ADDA_OR_MIX_CTRL_PHONEP 3 |
47 | #define SUN50I_ADDA_OR_MIX_CTRL_LINEINR 2 |
48 | #define SUN50I_ADDA_OR_MIX_CTRL_DACR 1 |
49 | #define SUN50I_ADDA_OR_MIX_CTRL_DACL 0 |
50 | |
51 | #define SUN50I_ADDA_EARPIECE_CTRL0 0x03 |
52 | #define SUN50I_ADDA_EARPIECE_CTRL0_EAR_RAMP_TIME 4 |
53 | #define SUN50I_ADDA_EARPIECE_CTRL0_ESPSR 0 |
54 | |
55 | #define SUN50I_ADDA_EARPIECE_CTRL1 0x04 |
56 | #define SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_EN 7 |
57 | #define SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_MUTE 6 |
58 | #define SUN50I_ADDA_EARPIECE_CTRL1_ESP_VOL 0 |
59 | |
60 | #define SUN50I_ADDA_LINEOUT_CTRL0 0x05 |
61 | #define SUN50I_ADDA_LINEOUT_CTRL0_LEN 7 |
62 | #define SUN50I_ADDA_LINEOUT_CTRL0_REN 6 |
63 | #define SUN50I_ADDA_LINEOUT_CTRL0_LSRC_SEL 5 |
64 | #define SUN50I_ADDA_LINEOUT_CTRL0_RSRC_SEL 4 |
65 | |
66 | #define SUN50I_ADDA_LINEOUT_CTRL1 0x06 |
67 | #define SUN50I_ADDA_LINEOUT_CTRL1_VOL 0 |
68 | |
69 | #define SUN50I_ADDA_MIC1_CTRL 0x07 |
70 | #define SUN50I_ADDA_MIC1_CTRL_MIC1G 4 |
71 | #define SUN50I_ADDA_MIC1_CTRL_MIC1AMPEN 3 |
72 | #define SUN50I_ADDA_MIC1_CTRL_MIC1BOOST 0 |
73 | |
74 | #define SUN50I_ADDA_MIC2_CTRL 0x08 |
75 | #define SUN50I_ADDA_MIC2_CTRL_MIC2G 4 |
76 | #define SUN50I_ADDA_MIC2_CTRL_MIC2AMPEN 3 |
77 | #define SUN50I_ADDA_MIC2_CTRL_MIC2BOOST 0 |
78 | |
79 | #define SUN50I_ADDA_LINEIN_CTRL 0x09 |
80 | #define SUN50I_ADDA_LINEIN_CTRL_LINEING 0 |
81 | |
82 | #define SUN50I_ADDA_MIX_DAC_CTRL 0x0a |
83 | #define SUN50I_ADDA_MIX_DAC_CTRL_DACAREN 7 |
84 | #define SUN50I_ADDA_MIX_DAC_CTRL_DACALEN 6 |
85 | #define SUN50I_ADDA_MIX_DAC_CTRL_RMIXEN 5 |
86 | #define SUN50I_ADDA_MIX_DAC_CTRL_LMIXEN 4 |
87 | #define SUN50I_ADDA_MIX_DAC_CTRL_RHPPAMUTE 3 |
88 | #define SUN50I_ADDA_MIX_DAC_CTRL_LHPPAMUTE 2 |
89 | #define SUN50I_ADDA_MIX_DAC_CTRL_RHPIS 1 |
90 | #define SUN50I_ADDA_MIX_DAC_CTRL_LHPIS 0 |
91 | |
92 | #define SUN50I_ADDA_L_ADCMIX_SRC 0x0b |
93 | #define SUN50I_ADDA_L_ADCMIX_SRC_MIC1 6 |
94 | #define SUN50I_ADDA_L_ADCMIX_SRC_MIC2 5 |
95 | #define SUN50I_ADDA_L_ADCMIX_SRC_PHONE 4 |
96 | #define SUN50I_ADDA_L_ADCMIX_SRC_PHONEN 3 |
97 | #define SUN50I_ADDA_L_ADCMIX_SRC_LINEINL 2 |
98 | #define SUN50I_ADDA_L_ADCMIX_SRC_OMIXRL 1 |
99 | #define SUN50I_ADDA_L_ADCMIX_SRC_OMIXRR 0 |
100 | |
101 | #define SUN50I_ADDA_R_ADCMIX_SRC 0x0c |
102 | #define SUN50I_ADDA_R_ADCMIX_SRC_MIC1 6 |
103 | #define SUN50I_ADDA_R_ADCMIX_SRC_MIC2 5 |
104 | #define SUN50I_ADDA_R_ADCMIX_SRC_PHONE 4 |
105 | #define SUN50I_ADDA_R_ADCMIX_SRC_PHONEP 3 |
106 | #define SUN50I_ADDA_R_ADCMIX_SRC_LINEINR 2 |
107 | #define SUN50I_ADDA_R_ADCMIX_SRC_OMIXR 1 |
108 | #define SUN50I_ADDA_R_ADCMIX_SRC_OMIXL 0 |
109 | |
110 | #define SUN50I_ADDA_ADC_CTRL 0x0d |
111 | #define SUN50I_ADDA_ADC_CTRL_ADCREN 7 |
112 | #define SUN50I_ADDA_ADC_CTRL_ADCLEN 6 |
113 | #define SUN50I_ADDA_ADC_CTRL_ADCG 0 |
114 | |
115 | #define SUN50I_ADDA_HS_MBIAS_CTRL 0x0e |
116 | #define SUN50I_ADDA_HS_MBIAS_CTRL_MMICBIASEN 7 |
117 | |
118 | #define SUN50I_ADDA_JACK_MIC_CTRL 0x1d |
119 | #define SUN50I_ADDA_JACK_MIC_CTRL_INNERRESEN 6 |
120 | #define SUN50I_ADDA_JACK_MIC_CTRL_HMICBIASEN 5 |
121 | |
122 | /* mixer controls */ |
123 | static const struct snd_kcontrol_new sun50i_a64_codec_mixer_controls[] = { |
124 | SOC_DAPM_DOUBLE_R("Mic1 Playback Switch" , |
125 | SUN50I_ADDA_OL_MIX_CTRL, |
126 | SUN50I_ADDA_OR_MIX_CTRL, |
127 | SUN50I_ADDA_OL_MIX_CTRL_MIC1, 1, 0), |
128 | SOC_DAPM_DOUBLE_R("Mic2 Playback Switch" , |
129 | SUN50I_ADDA_OL_MIX_CTRL, |
130 | SUN50I_ADDA_OR_MIX_CTRL, |
131 | SUN50I_ADDA_OL_MIX_CTRL_MIC2, 1, 0), |
132 | SOC_DAPM_DOUBLE_R("Line In Playback Switch" , |
133 | SUN50I_ADDA_OL_MIX_CTRL, |
134 | SUN50I_ADDA_OR_MIX_CTRL, |
135 | SUN50I_ADDA_OL_MIX_CTRL_LINEINL, 1, 0), |
136 | SOC_DAPM_DOUBLE_R("DAC Playback Switch" , |
137 | SUN50I_ADDA_OL_MIX_CTRL, |
138 | SUN50I_ADDA_OR_MIX_CTRL, |
139 | SUN50I_ADDA_OL_MIX_CTRL_DACL, 1, 0), |
140 | SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch" , |
141 | SUN50I_ADDA_OL_MIX_CTRL, |
142 | SUN50I_ADDA_OR_MIX_CTRL, |
143 | SUN50I_ADDA_OL_MIX_CTRL_DACR, 1, 0), |
144 | }; |
145 | |
146 | /* ADC mixer controls */ |
147 | static const struct snd_kcontrol_new sun50i_codec_adc_mixer_controls[] = { |
148 | SOC_DAPM_DOUBLE_R("Mic1 Capture Switch" , |
149 | SUN50I_ADDA_L_ADCMIX_SRC, |
150 | SUN50I_ADDA_R_ADCMIX_SRC, |
151 | SUN50I_ADDA_L_ADCMIX_SRC_MIC1, 1, 0), |
152 | SOC_DAPM_DOUBLE_R("Mic2 Capture Switch" , |
153 | SUN50I_ADDA_L_ADCMIX_SRC, |
154 | SUN50I_ADDA_R_ADCMIX_SRC, |
155 | SUN50I_ADDA_L_ADCMIX_SRC_MIC2, 1, 0), |
156 | SOC_DAPM_DOUBLE_R("Line In Capture Switch" , |
157 | SUN50I_ADDA_L_ADCMIX_SRC, |
158 | SUN50I_ADDA_R_ADCMIX_SRC, |
159 | SUN50I_ADDA_L_ADCMIX_SRC_LINEINL, 1, 0), |
160 | SOC_DAPM_DOUBLE_R("Mixer Capture Switch" , |
161 | SUN50I_ADDA_L_ADCMIX_SRC, |
162 | SUN50I_ADDA_R_ADCMIX_SRC, |
163 | SUN50I_ADDA_L_ADCMIX_SRC_OMIXRL, 1, 0), |
164 | SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch" , |
165 | SUN50I_ADDA_L_ADCMIX_SRC, |
166 | SUN50I_ADDA_R_ADCMIX_SRC, |
167 | SUN50I_ADDA_L_ADCMIX_SRC_OMIXRR, 1, 0), |
168 | }; |
169 | |
170 | static const DECLARE_TLV_DB_SCALE(sun50i_codec_out_mixer_pregain_scale, |
171 | -450, 150, 0); |
172 | static const DECLARE_TLV_DB_RANGE(sun50i_codec_mic_gain_scale, |
173 | 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), |
174 | 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0), |
175 | ); |
176 | |
177 | static const DECLARE_TLV_DB_SCALE(sun50i_codec_hp_vol_scale, -6300, 100, 1); |
178 | |
179 | static const DECLARE_TLV_DB_RANGE(sun50i_codec_lineout_vol_scale, |
180 | 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), |
181 | 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0), |
182 | ); |
183 | |
184 | static const DECLARE_TLV_DB_RANGE(sun50i_codec_earpiece_vol_scale, |
185 | 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), |
186 | 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0), |
187 | ); |
188 | |
189 | /* volume / mute controls */ |
190 | static const struct snd_kcontrol_new sun50i_a64_codec_controls[] = { |
191 | SOC_SINGLE_TLV("Headphone Playback Volume" , |
192 | SUN50I_ADDA_HP_CTRL, |
193 | SUN50I_ADDA_HP_CTRL_HPVOL, 0x3f, 0, |
194 | sun50i_codec_hp_vol_scale), |
195 | |
196 | /* Mixer pre-gain */ |
197 | SOC_SINGLE_TLV("Mic1 Playback Volume" , SUN50I_ADDA_MIC1_CTRL, |
198 | SUN50I_ADDA_MIC1_CTRL_MIC1G, |
199 | 0x7, 0, sun50i_codec_out_mixer_pregain_scale), |
200 | |
201 | /* Microphone Amp boost gain */ |
202 | SOC_SINGLE_TLV("Mic1 Boost Volume" , SUN50I_ADDA_MIC1_CTRL, |
203 | SUN50I_ADDA_MIC1_CTRL_MIC1BOOST, 0x7, 0, |
204 | sun50i_codec_mic_gain_scale), |
205 | |
206 | /* Mixer pre-gain */ |
207 | SOC_SINGLE_TLV("Mic2 Playback Volume" , |
208 | SUN50I_ADDA_MIC2_CTRL, SUN50I_ADDA_MIC2_CTRL_MIC2G, |
209 | 0x7, 0, sun50i_codec_out_mixer_pregain_scale), |
210 | |
211 | /* Microphone Amp boost gain */ |
212 | SOC_SINGLE_TLV("Mic2 Boost Volume" , SUN50I_ADDA_MIC2_CTRL, |
213 | SUN50I_ADDA_MIC2_CTRL_MIC2BOOST, 0x7, 0, |
214 | sun50i_codec_mic_gain_scale), |
215 | |
216 | /* ADC */ |
217 | SOC_SINGLE_TLV("ADC Gain Capture Volume" , SUN50I_ADDA_ADC_CTRL, |
218 | SUN50I_ADDA_ADC_CTRL_ADCG, 0x7, 0, |
219 | sun50i_codec_out_mixer_pregain_scale), |
220 | |
221 | /* Mixer pre-gain */ |
222 | SOC_SINGLE_TLV("Line In Playback Volume" , SUN50I_ADDA_LINEIN_CTRL, |
223 | SUN50I_ADDA_LINEIN_CTRL_LINEING, |
224 | 0x7, 0, sun50i_codec_out_mixer_pregain_scale), |
225 | |
226 | SOC_SINGLE_TLV("Line Out Playback Volume" , |
227 | SUN50I_ADDA_LINEOUT_CTRL1, |
228 | SUN50I_ADDA_LINEOUT_CTRL1_VOL, 0x1f, 0, |
229 | sun50i_codec_lineout_vol_scale), |
230 | |
231 | SOC_SINGLE_TLV("Earpiece Playback Volume" , |
232 | SUN50I_ADDA_EARPIECE_CTRL1, |
233 | SUN50I_ADDA_EARPIECE_CTRL1_ESP_VOL, 0x1f, 0, |
234 | sun50i_codec_earpiece_vol_scale), |
235 | }; |
236 | |
237 | static const char * const sun50i_codec_hp_src_enum_text[] = { |
238 | "DAC" , "Mixer" , |
239 | }; |
240 | |
241 | static SOC_ENUM_DOUBLE_DECL(sun50i_codec_hp_src_enum, |
242 | SUN50I_ADDA_MIX_DAC_CTRL, |
243 | SUN50I_ADDA_MIX_DAC_CTRL_LHPIS, |
244 | SUN50I_ADDA_MIX_DAC_CTRL_RHPIS, |
245 | sun50i_codec_hp_src_enum_text); |
246 | |
247 | static const struct snd_kcontrol_new sun50i_codec_hp_src[] = { |
248 | SOC_DAPM_ENUM("Headphone Source Playback Route" , |
249 | sun50i_codec_hp_src_enum), |
250 | }; |
251 | |
252 | static const struct snd_kcontrol_new sun50i_codec_hp_switch = |
253 | SOC_DAPM_DOUBLE("Headphone Playback Switch" , |
254 | SUN50I_ADDA_MIX_DAC_CTRL, |
255 | SUN50I_ADDA_MIX_DAC_CTRL_LHPPAMUTE, |
256 | SUN50I_ADDA_MIX_DAC_CTRL_RHPPAMUTE, 1, 0); |
257 | |
258 | static const char * const sun50i_codec_lineout_src_enum_text[] = { |
259 | "Stereo" , "Mono Differential" , |
260 | }; |
261 | |
262 | static SOC_ENUM_DOUBLE_DECL(sun50i_codec_lineout_src_enum, |
263 | SUN50I_ADDA_LINEOUT_CTRL0, |
264 | SUN50I_ADDA_LINEOUT_CTRL0_LSRC_SEL, |
265 | SUN50I_ADDA_LINEOUT_CTRL0_RSRC_SEL, |
266 | sun50i_codec_lineout_src_enum_text); |
267 | |
268 | static const struct snd_kcontrol_new sun50i_codec_lineout_src[] = { |
269 | SOC_DAPM_ENUM("Line Out Source Playback Route" , |
270 | sun50i_codec_lineout_src_enum), |
271 | }; |
272 | |
273 | static const struct snd_kcontrol_new sun50i_codec_lineout_switch = |
274 | SOC_DAPM_DOUBLE("Line Out Playback Switch" , |
275 | SUN50I_ADDA_LINEOUT_CTRL0, |
276 | SUN50I_ADDA_LINEOUT_CTRL0_LEN, |
277 | SUN50I_ADDA_LINEOUT_CTRL0_REN, 1, 0); |
278 | |
279 | static const char * const sun50i_codec_earpiece_src_enum_text[] = { |
280 | "DACR" , "DACL" , "Right Mixer" , "Left Mixer" , |
281 | }; |
282 | |
283 | static SOC_ENUM_SINGLE_DECL(sun50i_codec_earpiece_src_enum, |
284 | SUN50I_ADDA_EARPIECE_CTRL0, |
285 | SUN50I_ADDA_EARPIECE_CTRL0_ESPSR, |
286 | sun50i_codec_earpiece_src_enum_text); |
287 | |
288 | static const struct snd_kcontrol_new sun50i_codec_earpiece_src[] = { |
289 | SOC_DAPM_ENUM("Earpiece Source Playback Route" , |
290 | sun50i_codec_earpiece_src_enum), |
291 | }; |
292 | |
293 | static const struct snd_kcontrol_new sun50i_codec_earpiece_switch[] = { |
294 | SOC_DAPM_SINGLE("Earpiece Playback Switch" , |
295 | SUN50I_ADDA_EARPIECE_CTRL1, |
296 | SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_MUTE, 1, 0), |
297 | }; |
298 | |
299 | static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets[] = { |
300 | /* DAC */ |
301 | SND_SOC_DAPM_DAC("Left DAC" , NULL, SUN50I_ADDA_MIX_DAC_CTRL, |
302 | SUN50I_ADDA_MIX_DAC_CTRL_DACALEN, 0), |
303 | SND_SOC_DAPM_DAC("Right DAC" , NULL, SUN50I_ADDA_MIX_DAC_CTRL, |
304 | SUN50I_ADDA_MIX_DAC_CTRL_DACAREN, 0), |
305 | /* ADC */ |
306 | SND_SOC_DAPM_ADC("Left ADC" , NULL, SUN50I_ADDA_ADC_CTRL, |
307 | SUN50I_ADDA_ADC_CTRL_ADCLEN, 0), |
308 | SND_SOC_DAPM_ADC("Right ADC" , NULL, SUN50I_ADDA_ADC_CTRL, |
309 | SUN50I_ADDA_ADC_CTRL_ADCREN, 0), |
310 | /* |
311 | * Due to this component and the codec belonging to separate DAPM |
312 | * contexts, we need to manually link the above widgets to their |
313 | * stream widgets at the card level. |
314 | */ |
315 | |
316 | SND_SOC_DAPM_REGULATOR_SUPPLY("cpvdd" , 0, 0), |
317 | SND_SOC_DAPM_MUX("Left Headphone Source" , |
318 | SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src), |
319 | SND_SOC_DAPM_MUX("Right Headphone Source" , |
320 | SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src), |
321 | SND_SOC_DAPM_SWITCH("Left Headphone Switch" , |
322 | SND_SOC_NOPM, 0, 0, &sun50i_codec_hp_switch), |
323 | SND_SOC_DAPM_SWITCH("Right Headphone Switch" , |
324 | SND_SOC_NOPM, 0, 0, &sun50i_codec_hp_switch), |
325 | SND_SOC_DAPM_OUT_DRV("Left Headphone Amp" , |
326 | SND_SOC_NOPM, 0, 0, NULL, 0), |
327 | SND_SOC_DAPM_OUT_DRV("Right Headphone Amp" , |
328 | SND_SOC_NOPM, 0, 0, NULL, 0), |
329 | SND_SOC_DAPM_SUPPLY("Headphone Amp" , SUN50I_ADDA_HP_CTRL, |
330 | SUN50I_ADDA_HP_CTRL_HPPA_EN, 0, NULL, 0), |
331 | SND_SOC_DAPM_OUTPUT("HP" ), |
332 | |
333 | SND_SOC_DAPM_MUX("Left Line Out Source" , |
334 | SND_SOC_NOPM, 0, 0, sun50i_codec_lineout_src), |
335 | SND_SOC_DAPM_MUX("Right Line Out Source" , |
336 | SND_SOC_NOPM, 0, 0, sun50i_codec_lineout_src), |
337 | SND_SOC_DAPM_SWITCH("Left Line Out Switch" , |
338 | SND_SOC_NOPM, 0, 0, &sun50i_codec_lineout_switch), |
339 | SND_SOC_DAPM_SWITCH("Right Line Out Switch" , |
340 | SND_SOC_NOPM, 0, 0, &sun50i_codec_lineout_switch), |
341 | SND_SOC_DAPM_OUTPUT("LINEOUT" ), |
342 | |
343 | SND_SOC_DAPM_MUX("Earpiece Source Playback Route" , |
344 | SND_SOC_NOPM, 0, 0, sun50i_codec_earpiece_src), |
345 | SOC_MIXER_NAMED_CTL_ARRAY("Earpiece Switch" , |
346 | SND_SOC_NOPM, 0, 0, |
347 | sun50i_codec_earpiece_switch), |
348 | SND_SOC_DAPM_OUT_DRV("Earpiece Amp" , SUN50I_ADDA_EARPIECE_CTRL1, |
349 | SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_EN, 0, NULL, 0), |
350 | SND_SOC_DAPM_OUTPUT("EARPIECE" ), |
351 | |
352 | /* Microphone inputs */ |
353 | SND_SOC_DAPM_INPUT("MIC1" ), |
354 | |
355 | /* Microphone Bias */ |
356 | SND_SOC_DAPM_SUPPLY("MBIAS" , SUN50I_ADDA_HS_MBIAS_CTRL, |
357 | SUN50I_ADDA_HS_MBIAS_CTRL_MMICBIASEN, |
358 | 0, NULL, 0), |
359 | |
360 | /* Mic input path */ |
361 | SND_SOC_DAPM_PGA("Mic1 Amplifier" , SUN50I_ADDA_MIC1_CTRL, |
362 | SUN50I_ADDA_MIC1_CTRL_MIC1AMPEN, 0, NULL, 0), |
363 | |
364 | /* Microphone input */ |
365 | SND_SOC_DAPM_INPUT("MIC2" ), |
366 | |
367 | /* Microphone Bias */ |
368 | SND_SOC_DAPM_SUPPLY("HBIAS" , SUN50I_ADDA_JACK_MIC_CTRL, |
369 | SUN50I_ADDA_JACK_MIC_CTRL_HMICBIASEN, |
370 | 0, NULL, 0), |
371 | |
372 | /* Mic input path */ |
373 | SND_SOC_DAPM_PGA("Mic2 Amplifier" , SUN50I_ADDA_MIC2_CTRL, |
374 | SUN50I_ADDA_MIC2_CTRL_MIC2AMPEN, 0, NULL, 0), |
375 | |
376 | /* Line input */ |
377 | SND_SOC_DAPM_INPUT("LINEIN" ), |
378 | |
379 | /* Mixers */ |
380 | SND_SOC_DAPM_MIXER("Left Mixer" , SUN50I_ADDA_MIX_DAC_CTRL, |
381 | SUN50I_ADDA_MIX_DAC_CTRL_LMIXEN, 0, |
382 | sun50i_a64_codec_mixer_controls, |
383 | ARRAY_SIZE(sun50i_a64_codec_mixer_controls)), |
384 | SND_SOC_DAPM_MIXER("Right Mixer" , SUN50I_ADDA_MIX_DAC_CTRL, |
385 | SUN50I_ADDA_MIX_DAC_CTRL_RMIXEN, 0, |
386 | sun50i_a64_codec_mixer_controls, |
387 | ARRAY_SIZE(sun50i_a64_codec_mixer_controls)), |
388 | SND_SOC_DAPM_MIXER("Left ADC Mixer" , SND_SOC_NOPM, 0, 0, |
389 | sun50i_codec_adc_mixer_controls, |
390 | ARRAY_SIZE(sun50i_codec_adc_mixer_controls)), |
391 | SND_SOC_DAPM_MIXER("Right ADC Mixer" , SND_SOC_NOPM, 0, 0, |
392 | sun50i_codec_adc_mixer_controls, |
393 | ARRAY_SIZE(sun50i_codec_adc_mixer_controls)), |
394 | }; |
395 | |
396 | static const struct snd_soc_dapm_route sun50i_a64_codec_routes[] = { |
397 | /* Left Mixer Routes */ |
398 | { "Left Mixer" , "Mic1 Playback Switch" , "Mic1 Amplifier" }, |
399 | { "Left Mixer" , "Mic2 Playback Switch" , "Mic2 Amplifier" }, |
400 | { "Left Mixer" , "Line In Playback Switch" , "LINEIN" }, |
401 | { "Left Mixer" , "DAC Playback Switch" , "Left DAC" }, |
402 | { "Left Mixer" , "DAC Reversed Playback Switch" , "Right DAC" }, |
403 | |
404 | /* Right Mixer Routes */ |
405 | { "Right Mixer" , "Mic1 Playback Switch" , "Mic1 Amplifier" }, |
406 | { "Right Mixer" , "Mic2 Playback Switch" , "Mic2 Amplifier" }, |
407 | { "Right Mixer" , "Line In Playback Switch" , "LINEIN" }, |
408 | { "Right Mixer" , "DAC Playback Switch" , "Right DAC" }, |
409 | { "Right Mixer" , "DAC Reversed Playback Switch" , "Left DAC" }, |
410 | |
411 | /* Left ADC Mixer Routes */ |
412 | { "Left ADC Mixer" , "Mic1 Capture Switch" , "Mic1 Amplifier" }, |
413 | { "Left ADC Mixer" , "Mic2 Capture Switch" , "Mic2 Amplifier" }, |
414 | { "Left ADC Mixer" , "Line In Capture Switch" , "LINEIN" }, |
415 | { "Left ADC Mixer" , "Mixer Capture Switch" , "Left Mixer" }, |
416 | { "Left ADC Mixer" , "Mixer Reversed Capture Switch" , "Right Mixer" }, |
417 | |
418 | /* Right ADC Mixer Routes */ |
419 | { "Right ADC Mixer" , "Mic1 Capture Switch" , "Mic1 Amplifier" }, |
420 | { "Right ADC Mixer" , "Mic2 Capture Switch" , "Mic2 Amplifier" }, |
421 | { "Right ADC Mixer" , "Line In Capture Switch" , "LINEIN" }, |
422 | { "Right ADC Mixer" , "Mixer Capture Switch" , "Right Mixer" }, |
423 | { "Right ADC Mixer" , "Mixer Reversed Capture Switch" , "Left Mixer" }, |
424 | |
425 | /* ADC Routes */ |
426 | { "Left ADC" , NULL, "Left ADC Mixer" }, |
427 | { "Right ADC" , NULL, "Right ADC Mixer" }, |
428 | |
429 | /* Headphone Routes */ |
430 | { "Left Headphone Source" , "DAC" , "Left DAC" }, |
431 | { "Left Headphone Source" , "Mixer" , "Left Mixer" }, |
432 | { "Left Headphone Switch" , "Headphone Playback Switch" , "Left Headphone Source" }, |
433 | { "Left Headphone Amp" , NULL, "Left Headphone Switch" }, |
434 | { "Left Headphone Amp" , NULL, "Headphone Amp" }, |
435 | { "HP" , NULL, "Left Headphone Amp" }, |
436 | |
437 | { "Right Headphone Source" , "DAC" , "Right DAC" }, |
438 | { "Right Headphone Source" , "Mixer" , "Right Mixer" }, |
439 | { "Right Headphone Switch" , "Headphone Playback Switch" , "Right Headphone Source" }, |
440 | { "Right Headphone Amp" , NULL, "Right Headphone Switch" }, |
441 | { "Right Headphone Amp" , NULL, "Headphone Amp" }, |
442 | { "HP" , NULL, "Right Headphone Amp" }, |
443 | |
444 | { "Headphone Amp" , NULL, "cpvdd" }, |
445 | |
446 | /* Microphone Routes */ |
447 | { "Mic1 Amplifier" , NULL, "MIC1" }, |
448 | |
449 | /* Microphone Routes */ |
450 | { "Mic2 Amplifier" , NULL, "MIC2" }, |
451 | |
452 | /* Line-out Routes */ |
453 | { "Left Line Out Source" , "Stereo" , "Left Mixer" }, |
454 | { "Left Line Out Source" , "Mono Differential" , "Left Mixer" }, |
455 | { "Left Line Out Source" , "Mono Differential" , "Right Mixer" }, |
456 | { "Left Line Out Switch" , "Line Out Playback Switch" , "Left Line Out Source" }, |
457 | { "LINEOUT" , NULL, "Left Line Out Switch" }, |
458 | |
459 | { "Right Line Out Switch" , "Line Out Playback Switch" , "Right Mixer" }, |
460 | { "Right Line Out Source" , "Stereo" , "Right Line Out Switch" }, |
461 | { "Right Line Out Source" , "Mono Differential" , "Left Line Out Switch" }, |
462 | { "LINEOUT" , NULL, "Right Line Out Source" }, |
463 | |
464 | /* Earpiece Routes */ |
465 | { "Earpiece Source Playback Route" , "DACL" , "Left DAC" }, |
466 | { "Earpiece Source Playback Route" , "DACR" , "Right DAC" }, |
467 | { "Earpiece Source Playback Route" , "Left Mixer" , "Left Mixer" }, |
468 | { "Earpiece Source Playback Route" , "Right Mixer" , "Right Mixer" }, |
469 | { "Earpiece Switch" , "Earpiece Playback Switch" , "Earpiece Source Playback Route" }, |
470 | { "Earpiece Amp" , NULL, "Earpiece Switch" }, |
471 | { "EARPIECE" , NULL, "Earpiece Amp" }, |
472 | }; |
473 | |
474 | static int sun50i_a64_codec_suspend(struct snd_soc_component *component) |
475 | { |
476 | return regmap_update_bits(map: component->regmap, SUN50I_ADDA_HP_CTRL, |
477 | BIT(SUN50I_ADDA_HP_CTRL_PA_CLK_GATE), |
478 | BIT(SUN50I_ADDA_HP_CTRL_PA_CLK_GATE)); |
479 | } |
480 | |
481 | static int sun50i_a64_codec_resume(struct snd_soc_component *component) |
482 | { |
483 | return regmap_update_bits(map: component->regmap, SUN50I_ADDA_HP_CTRL, |
484 | BIT(SUN50I_ADDA_HP_CTRL_PA_CLK_GATE), val: 0); |
485 | } |
486 | |
487 | static const struct snd_soc_component_driver sun50i_codec_analog_cmpnt_drv = { |
488 | .controls = sun50i_a64_codec_controls, |
489 | .num_controls = ARRAY_SIZE(sun50i_a64_codec_controls), |
490 | .dapm_widgets = sun50i_a64_codec_widgets, |
491 | .num_dapm_widgets = ARRAY_SIZE(sun50i_a64_codec_widgets), |
492 | .dapm_routes = sun50i_a64_codec_routes, |
493 | .num_dapm_routes = ARRAY_SIZE(sun50i_a64_codec_routes), |
494 | .suspend = sun50i_a64_codec_suspend, |
495 | .resume = sun50i_a64_codec_resume, |
496 | }; |
497 | |
498 | static const struct of_device_id sun50i_codec_analog_of_match[] = { |
499 | { |
500 | .compatible = "allwinner,sun50i-a64-codec-analog" , |
501 | }, |
502 | {} |
503 | }; |
504 | MODULE_DEVICE_TABLE(of, sun50i_codec_analog_of_match); |
505 | |
506 | static int sun50i_codec_analog_probe(struct platform_device *pdev) |
507 | { |
508 | struct regmap *regmap; |
509 | void __iomem *base; |
510 | bool enable; |
511 | |
512 | base = devm_platform_ioremap_resource(pdev, index: 0); |
513 | if (IS_ERR(ptr: base)) { |
514 | dev_err(&pdev->dev, "Failed to map the registers\n" ); |
515 | return PTR_ERR(ptr: base); |
516 | } |
517 | |
518 | regmap = sun8i_adda_pr_regmap_init(dev: &pdev->dev, base); |
519 | if (IS_ERR(ptr: regmap)) { |
520 | dev_err(&pdev->dev, "Failed to create regmap\n" ); |
521 | return PTR_ERR(ptr: regmap); |
522 | } |
523 | |
524 | enable = device_property_read_bool(dev: &pdev->dev, |
525 | propname: "allwinner,internal-bias-resistor" ); |
526 | regmap_update_bits(map: regmap, SUN50I_ADDA_JACK_MIC_CTRL, |
527 | BIT(SUN50I_ADDA_JACK_MIC_CTRL_INNERRESEN), |
528 | val: enable << SUN50I_ADDA_JACK_MIC_CTRL_INNERRESEN); |
529 | |
530 | return devm_snd_soc_register_component(dev: &pdev->dev, |
531 | component_driver: &sun50i_codec_analog_cmpnt_drv, |
532 | NULL, num_dai: 0); |
533 | } |
534 | |
535 | static struct platform_driver sun50i_codec_analog_driver = { |
536 | .driver = { |
537 | .name = "sun50i-codec-analog" , |
538 | .of_match_table = sun50i_codec_analog_of_match, |
539 | }, |
540 | .probe = sun50i_codec_analog_probe, |
541 | }; |
542 | module_platform_driver(sun50i_codec_analog_driver); |
543 | |
544 | MODULE_DESCRIPTION("Allwinner internal codec analog controls driver for A64" ); |
545 | MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>" ); |
546 | MODULE_LICENSE("GPL" ); |
547 | MODULE_ALIAS("platform:sun50i-codec-analog" ); |
548 | |