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