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_HMICBIASEN	5
121 
122 /* mixer controls */
123 static const struct snd_kcontrol_new sun50i_a64_codec_mixer_controls[] = {
124 	SOC_DAPM_DOUBLE_R("DAC Playback Switch",
125 			  SUN50I_ADDA_OL_MIX_CTRL,
126 			  SUN50I_ADDA_OR_MIX_CTRL,
127 			  SUN50I_ADDA_OL_MIX_CTRL_DACL, 1, 0),
128 	SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch",
129 			  SUN50I_ADDA_OL_MIX_CTRL,
130 			  SUN50I_ADDA_OR_MIX_CTRL,
131 			  SUN50I_ADDA_OL_MIX_CTRL_DACR, 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("Mic1 Playback Switch",
137 			  SUN50I_ADDA_OL_MIX_CTRL,
138 			  SUN50I_ADDA_OR_MIX_CTRL,
139 			  SUN50I_ADDA_OL_MIX_CTRL_MIC1, 1, 0),
140 	SOC_DAPM_DOUBLE_R("Mic2 Playback Switch",
141 			  SUN50I_ADDA_OL_MIX_CTRL,
142 			  SUN50I_ADDA_OR_MIX_CTRL,
143 			  SUN50I_ADDA_OL_MIX_CTRL_MIC2, 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("Mixer Capture Switch",
149 			  SUN50I_ADDA_L_ADCMIX_SRC,
150 			  SUN50I_ADDA_R_ADCMIX_SRC,
151 			  SUN50I_ADDA_L_ADCMIX_SRC_OMIXRL, 1, 0),
152 	SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch",
153 			  SUN50I_ADDA_L_ADCMIX_SRC,
154 			  SUN50I_ADDA_R_ADCMIX_SRC,
155 			  SUN50I_ADDA_L_ADCMIX_SRC_OMIXRR, 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("Mic1 Capture Switch",
161 			  SUN50I_ADDA_L_ADCMIX_SRC,
162 			  SUN50I_ADDA_R_ADCMIX_SRC,
163 			  SUN50I_ADDA_L_ADCMIX_SRC_MIC1, 1, 0),
164 	SOC_DAPM_DOUBLE_R("Mic2 Capture Switch",
165 			  SUN50I_ADDA_L_ADCMIX_SRC,
166 			  SUN50I_ADDA_R_ADCMIX_SRC,
167 			  SUN50I_ADDA_L_ADCMIX_SRC_MIC2, 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 	SOC_DOUBLE("Headphone Playback Switch",
197 		   SUN50I_ADDA_MIX_DAC_CTRL,
198 		   SUN50I_ADDA_MIX_DAC_CTRL_LHPPAMUTE,
199 		   SUN50I_ADDA_MIX_DAC_CTRL_RHPPAMUTE, 1, 0),
200 
201 	/* Mixer pre-gain */
202 	SOC_SINGLE_TLV("Mic1 Playback Volume", SUN50I_ADDA_MIC1_CTRL,
203 		       SUN50I_ADDA_MIC1_CTRL_MIC1G,
204 		       0x7, 0, sun50i_codec_out_mixer_pregain_scale),
205 
206 	/* Microphone Amp boost gain */
207 	SOC_SINGLE_TLV("Mic1 Boost Volume", SUN50I_ADDA_MIC1_CTRL,
208 		       SUN50I_ADDA_MIC1_CTRL_MIC1BOOST, 0x7, 0,
209 		       sun50i_codec_mic_gain_scale),
210 
211 	/* Mixer pre-gain */
212 	SOC_SINGLE_TLV("Mic2 Playback Volume",
213 		       SUN50I_ADDA_MIC2_CTRL, SUN50I_ADDA_MIC2_CTRL_MIC2G,
214 		       0x7, 0, sun50i_codec_out_mixer_pregain_scale),
215 
216 	/* Microphone Amp boost gain */
217 	SOC_SINGLE_TLV("Mic2 Boost Volume", SUN50I_ADDA_MIC2_CTRL,
218 		       SUN50I_ADDA_MIC2_CTRL_MIC2BOOST, 0x7, 0,
219 		       sun50i_codec_mic_gain_scale),
220 
221 	/* ADC */
222 	SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN50I_ADDA_ADC_CTRL,
223 		       SUN50I_ADDA_ADC_CTRL_ADCG, 0x7, 0,
224 		       sun50i_codec_out_mixer_pregain_scale),
225 
226 	/* Mixer pre-gain */
227 	SOC_SINGLE_TLV("Line In Playback Volume", SUN50I_ADDA_LINEIN_CTRL,
228 		       SUN50I_ADDA_LINEIN_CTRL_LINEING,
229 		       0x7, 0, sun50i_codec_out_mixer_pregain_scale),
230 
231 	SOC_SINGLE_TLV("Line Out Playback Volume",
232 		       SUN50I_ADDA_LINEOUT_CTRL1,
233 		       SUN50I_ADDA_LINEOUT_CTRL1_VOL, 0x1f, 0,
234 		       sun50i_codec_lineout_vol_scale),
235 
236 	SOC_DOUBLE("Line Out Playback Switch",
237 		   SUN50I_ADDA_LINEOUT_CTRL0,
238 		   SUN50I_ADDA_LINEOUT_CTRL0_LEN,
239 		   SUN50I_ADDA_LINEOUT_CTRL0_REN, 1, 0),
240 
241 	SOC_SINGLE_TLV("Earpiece Playback Volume",
242 		       SUN50I_ADDA_EARPIECE_CTRL1,
243 		       SUN50I_ADDA_EARPIECE_CTRL1_ESP_VOL, 0x1f, 0,
244 		       sun50i_codec_earpiece_vol_scale),
245 
246 	SOC_SINGLE("Earpiece Playback Switch",
247 		   SUN50I_ADDA_EARPIECE_CTRL1,
248 		   SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_MUTE, 1, 0),
249 
250 };
251 
252 static const char * const sun50i_codec_hp_src_enum_text[] = {
253 	"DAC", "Mixer",
254 };
255 
256 static SOC_ENUM_DOUBLE_DECL(sun50i_codec_hp_src_enum,
257 			    SUN50I_ADDA_MIX_DAC_CTRL,
258 			    SUN50I_ADDA_MIX_DAC_CTRL_LHPIS,
259 			    SUN50I_ADDA_MIX_DAC_CTRL_RHPIS,
260 			    sun50i_codec_hp_src_enum_text);
261 
262 static const struct snd_kcontrol_new sun50i_codec_hp_src[] = {
263 	SOC_DAPM_ENUM("Headphone Source Playback Route",
264 		      sun50i_codec_hp_src_enum),
265 };
266 
267 static const char * const sun50i_codec_lineout_src_enum_text[] = {
268 	"Stereo", "Mono Differential",
269 };
270 
271 static SOC_ENUM_DOUBLE_DECL(sun50i_codec_lineout_src_enum,
272 			    SUN50I_ADDA_LINEOUT_CTRL0,
273 			    SUN50I_ADDA_LINEOUT_CTRL0_LSRC_SEL,
274 			    SUN50I_ADDA_LINEOUT_CTRL0_RSRC_SEL,
275 			    sun50i_codec_lineout_src_enum_text);
276 
277 static const struct snd_kcontrol_new sun50i_codec_lineout_src[] = {
278 	SOC_DAPM_ENUM("Line Out Source Playback Route",
279 		      sun50i_codec_lineout_src_enum),
280 };
281 
282 static const char * const sun50i_codec_earpiece_src_enum_text[] = {
283 	"DACR", "DACL", "Right Mixer", "Left Mixer",
284 };
285 
286 static SOC_ENUM_SINGLE_DECL(sun50i_codec_earpiece_src_enum,
287 			    SUN50I_ADDA_EARPIECE_CTRL0,
288 			    SUN50I_ADDA_EARPIECE_CTRL0_ESPSR,
289 			    sun50i_codec_earpiece_src_enum_text);
290 
291 static const struct snd_kcontrol_new sun50i_codec_earpiece_src[] = {
292 	SOC_DAPM_ENUM("Earpiece Source Playback Route",
293 		      sun50i_codec_earpiece_src_enum),
294 };
295 
296 static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets[] = {
297 	/* DAC */
298 	SND_SOC_DAPM_DAC("Left DAC", NULL, SUN50I_ADDA_MIX_DAC_CTRL,
299 			 SUN50I_ADDA_MIX_DAC_CTRL_DACALEN, 0),
300 	SND_SOC_DAPM_DAC("Right DAC", NULL, SUN50I_ADDA_MIX_DAC_CTRL,
301 			 SUN50I_ADDA_MIX_DAC_CTRL_DACAREN, 0),
302 	/* ADC */
303 	SND_SOC_DAPM_ADC("Left ADC", NULL, SUN50I_ADDA_ADC_CTRL,
304 			 SUN50I_ADDA_ADC_CTRL_ADCLEN, 0),
305 	SND_SOC_DAPM_ADC("Right ADC", NULL, SUN50I_ADDA_ADC_CTRL,
306 			 SUN50I_ADDA_ADC_CTRL_ADCREN, 0),
307 	/*
308 	 * Due to this component and the codec belonging to separate DAPM
309 	 * contexts, we need to manually link the above widgets to their
310 	 * stream widgets at the card level.
311 	 */
312 
313 	SND_SOC_DAPM_REGULATOR_SUPPLY("cpvdd", 0, 0),
314 	SND_SOC_DAPM_MUX("Headphone Source Playback Route",
315 			 SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src),
316 	SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN50I_ADDA_HP_CTRL,
317 			     SUN50I_ADDA_HP_CTRL_HPPA_EN, 0, NULL, 0),
318 	SND_SOC_DAPM_OUTPUT("HP"),
319 
320 	SND_SOC_DAPM_MUX("Line Out Source Playback Route",
321 			 SND_SOC_NOPM, 0, 0, sun50i_codec_lineout_src),
322 	SND_SOC_DAPM_OUTPUT("LINEOUT"),
323 
324 	SND_SOC_DAPM_MUX("Earpiece Source Playback Route",
325 			 SND_SOC_NOPM, 0, 0, sun50i_codec_earpiece_src),
326 	SND_SOC_DAPM_OUT_DRV("Earpiece Amp", SUN50I_ADDA_EARPIECE_CTRL1,
327 			     SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_EN, 0, NULL, 0),
328 	SND_SOC_DAPM_OUTPUT("EARPIECE"),
329 
330 	/* Microphone inputs */
331 	SND_SOC_DAPM_INPUT("MIC1"),
332 
333 	/* Microphone Bias */
334 	SND_SOC_DAPM_SUPPLY("MBIAS", SUN50I_ADDA_HS_MBIAS_CTRL,
335 			    SUN50I_ADDA_HS_MBIAS_CTRL_MMICBIASEN,
336 			    0, NULL, 0),
337 
338 	/* Mic input path */
339 	SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN50I_ADDA_MIC1_CTRL,
340 			 SUN50I_ADDA_MIC1_CTRL_MIC1AMPEN, 0, NULL, 0),
341 
342 	/* Microphone input */
343 	SND_SOC_DAPM_INPUT("MIC2"),
344 
345 	/* Microphone Bias */
346 	SND_SOC_DAPM_SUPPLY("HBIAS", SUN50I_ADDA_JACK_MIC_CTRL,
347 			    SUN50I_ADDA_JACK_MIC_CTRL_HMICBIASEN,
348 			    0, NULL, 0),
349 
350 	/* Mic input path */
351 	SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN50I_ADDA_MIC2_CTRL,
352 			 SUN50I_ADDA_MIC2_CTRL_MIC2AMPEN, 0, NULL, 0),
353 
354 	/* Line input */
355 	SND_SOC_DAPM_INPUT("LINEIN"),
356 
357 	/* Mixers */
358 	SND_SOC_DAPM_MIXER("Left Mixer", SUN50I_ADDA_MIX_DAC_CTRL,
359 			   SUN50I_ADDA_MIX_DAC_CTRL_LMIXEN, 0,
360 			   sun50i_a64_codec_mixer_controls,
361 			   ARRAY_SIZE(sun50i_a64_codec_mixer_controls)),
362 	SND_SOC_DAPM_MIXER("Right Mixer", SUN50I_ADDA_MIX_DAC_CTRL,
363 			   SUN50I_ADDA_MIX_DAC_CTRL_RMIXEN, 0,
364 			   sun50i_a64_codec_mixer_controls,
365 			   ARRAY_SIZE(sun50i_a64_codec_mixer_controls)),
366 	SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN50I_ADDA_ADC_CTRL,
367 			   SUN50I_ADDA_ADC_CTRL_ADCLEN, 0,
368 			   sun50i_codec_adc_mixer_controls,
369 			   ARRAY_SIZE(sun50i_codec_adc_mixer_controls)),
370 	SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN50I_ADDA_ADC_CTRL,
371 			   SUN50I_ADDA_ADC_CTRL_ADCREN, 0,
372 			   sun50i_codec_adc_mixer_controls,
373 			   ARRAY_SIZE(sun50i_codec_adc_mixer_controls)),
374 };
375 
376 static const struct snd_soc_dapm_route sun50i_a64_codec_routes[] = {
377 	/* Left Mixer Routes */
378 	{ "Left Mixer", "DAC Playback Switch", "Left DAC" },
379 	{ "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
380 	{ "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
381 
382 	/* Right Mixer Routes */
383 	{ "Right Mixer", "DAC Playback Switch", "Right DAC" },
384 	{ "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
385 	{ "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
386 
387 	/* Left ADC Mixer Routes */
388 	{ "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
389 	{ "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
390 	{ "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
391 
392 	/* Right ADC Mixer Routes */
393 	{ "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
394 	{ "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
395 	{ "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
396 
397 	/* ADC Routes */
398 	{ "Left ADC", NULL, "Left ADC Mixer" },
399 	{ "Right ADC", NULL, "Right ADC Mixer" },
400 
401 	/* Headphone Routes */
402 	{ "Headphone Source Playback Route", "DAC", "Left DAC" },
403 	{ "Headphone Source Playback Route", "DAC", "Right DAC" },
404 	{ "Headphone Source Playback Route", "Mixer", "Left Mixer" },
405 	{ "Headphone Source Playback Route", "Mixer", "Right Mixer" },
406 	{ "Headphone Amp", NULL, "Headphone Source Playback Route" },
407 	{ "Headphone Amp", NULL, "cpvdd" },
408 	{ "HP", NULL, "Headphone Amp" },
409 
410 	/* Microphone Routes */
411 	{ "Mic1 Amplifier", NULL, "MIC1"},
412 
413 	/* Microphone Routes */
414 	{ "Mic2 Amplifier", NULL, "MIC2"},
415 	{ "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
416 	{ "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
417 	{ "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
418 	{ "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
419 
420 	/* Line-in Routes */
421 	{ "Left Mixer", "Line In Playback Switch", "LINEIN" },
422 	{ "Right Mixer", "Line In Playback Switch", "LINEIN" },
423 	{ "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
424 	{ "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
425 
426 	/* Line-out Routes */
427 	{ "Line Out Source Playback Route", "Stereo", "Left Mixer" },
428 	{ "Line Out Source Playback Route", "Stereo", "Right Mixer" },
429 	{ "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
430 	{ "Line Out Source Playback Route", "Mono Differential",
431 		"Right Mixer" },
432 	{ "LINEOUT", NULL, "Line Out Source Playback Route" },
433 
434 	/* Earpiece Routes */
435 	{ "Earpiece Source Playback Route", "DACL", "Left DAC" },
436 	{ "Earpiece Source Playback Route", "DACR", "Right DAC" },
437 	{ "Earpiece Source Playback Route", "Left Mixer", "Left Mixer" },
438 	{ "Earpiece Source Playback Route", "Right Mixer", "Right Mixer" },
439 	{ "Earpiece Amp", NULL, "Earpiece Source Playback Route" },
440 	{ "EARPIECE", NULL, "Earpiece Amp" },
441 };
442 
443 static const struct snd_soc_component_driver sun50i_codec_analog_cmpnt_drv = {
444 	.controls		= sun50i_a64_codec_controls,
445 	.num_controls		= ARRAY_SIZE(sun50i_a64_codec_controls),
446 	.dapm_widgets		= sun50i_a64_codec_widgets,
447 	.num_dapm_widgets	= ARRAY_SIZE(sun50i_a64_codec_widgets),
448 	.dapm_routes		= sun50i_a64_codec_routes,
449 	.num_dapm_routes	= ARRAY_SIZE(sun50i_a64_codec_routes),
450 };
451 
452 static const struct of_device_id sun50i_codec_analog_of_match[] = {
453 	{
454 		.compatible = "allwinner,sun50i-a64-codec-analog",
455 	},
456 	{}
457 };
458 MODULE_DEVICE_TABLE(of, sun50i_codec_analog_of_match);
459 
460 static int sun50i_codec_analog_probe(struct platform_device *pdev)
461 {
462 	struct regmap *regmap;
463 	void __iomem *base;
464 
465 	base = devm_platform_ioremap_resource(pdev, 0);
466 	if (IS_ERR(base)) {
467 		dev_err(&pdev->dev, "Failed to map the registers\n");
468 		return PTR_ERR(base);
469 	}
470 
471 	regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base);
472 	if (IS_ERR(regmap)) {
473 		dev_err(&pdev->dev, "Failed to create regmap\n");
474 		return PTR_ERR(regmap);
475 	}
476 
477 	return devm_snd_soc_register_component(&pdev->dev,
478 					       &sun50i_codec_analog_cmpnt_drv,
479 					       NULL, 0);
480 }
481 
482 static struct platform_driver sun50i_codec_analog_driver = {
483 	.driver = {
484 		.name = "sun50i-codec-analog",
485 		.of_match_table = sun50i_codec_analog_of_match,
486 	},
487 	.probe = sun50i_codec_analog_probe,
488 };
489 module_platform_driver(sun50i_codec_analog_driver);
490 
491 MODULE_DESCRIPTION("Allwinner internal codec analog controls driver for A64");
492 MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>");
493 MODULE_LICENSE("GPL");
494 MODULE_ALIAS("platform:sun50i-codec-analog");
495