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