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