1 /*
2  * This driver supports the analog controls for the internal codec
3  * found in Allwinner's A31s, A23, A33 and H3 SoCs.
4  *
5  * Copyright 2016 Chen-Yu Tsai <wens@csie.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  */
17 
18 #include <linux/io.h>
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/of.h>
22 #include <linux/of_device.h>
23 #include <linux/platform_device.h>
24 #include <linux/regmap.h>
25 
26 #include <sound/soc.h>
27 #include <sound/soc-dapm.h>
28 #include <sound/tlv.h>
29 
30 /* Codec analog control register offsets and bit fields */
31 #define SUN8I_ADDA_HP_VOLC		0x00
32 #define SUN8I_ADDA_HP_VOLC_PA_CLK_GATE		7
33 #define SUN8I_ADDA_HP_VOLC_HP_VOL		0
34 #define SUN8I_ADDA_LOMIXSC		0x01
35 #define SUN8I_ADDA_LOMIXSC_MIC1			6
36 #define SUN8I_ADDA_LOMIXSC_MIC2			5
37 #define SUN8I_ADDA_LOMIXSC_PHONE		4
38 #define SUN8I_ADDA_LOMIXSC_PHONEN		3
39 #define SUN8I_ADDA_LOMIXSC_LINEINL		2
40 #define SUN8I_ADDA_LOMIXSC_DACL			1
41 #define SUN8I_ADDA_LOMIXSC_DACR			0
42 #define SUN8I_ADDA_ROMIXSC		0x02
43 #define SUN8I_ADDA_ROMIXSC_MIC1			6
44 #define SUN8I_ADDA_ROMIXSC_MIC2			5
45 #define SUN8I_ADDA_ROMIXSC_PHONE		4
46 #define SUN8I_ADDA_ROMIXSC_PHONEP		3
47 #define SUN8I_ADDA_ROMIXSC_LINEINR		2
48 #define SUN8I_ADDA_ROMIXSC_DACR			1
49 #define SUN8I_ADDA_ROMIXSC_DACL			0
50 #define SUN8I_ADDA_DAC_PA_SRC		0x03
51 #define SUN8I_ADDA_DAC_PA_SRC_DACAREN		7
52 #define SUN8I_ADDA_DAC_PA_SRC_DACALEN		6
53 #define SUN8I_ADDA_DAC_PA_SRC_RMIXEN		5
54 #define SUN8I_ADDA_DAC_PA_SRC_LMIXEN		4
55 #define SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE		3
56 #define SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE		2
57 #define SUN8I_ADDA_DAC_PA_SRC_RHPIS		1
58 #define SUN8I_ADDA_DAC_PA_SRC_LHPIS		0
59 #define SUN8I_ADDA_PHONEIN_GCTRL	0x04
60 #define SUN8I_ADDA_PHONEIN_GCTRL_PHONEPG	4
61 #define SUN8I_ADDA_PHONEIN_GCTRL_PHONENG	0
62 #define SUN8I_ADDA_LINEIN_GCTRL		0x05
63 #define SUN8I_ADDA_LINEIN_GCTRL_LINEING		4
64 #define SUN8I_ADDA_LINEIN_GCTRL_PHONEG		0
65 #define SUN8I_ADDA_MICIN_GCTRL		0x06
66 #define SUN8I_ADDA_MICIN_GCTRL_MIC1G		4
67 #define SUN8I_ADDA_MICIN_GCTRL_MIC2G		0
68 #define SUN8I_ADDA_PAEN_HP_CTRL		0x07
69 #define SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN		7
70 #define SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN	7	/* H3 specific */
71 #define SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC	5
72 #define SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN		4
73 #define SUN8I_ADDA_PAEN_HP_CTRL_PA_ANTI_POP_CTRL	2
74 #define SUN8I_ADDA_PAEN_HP_CTRL_LTRNMUTE	1
75 #define SUN8I_ADDA_PAEN_HP_CTRL_RTLNMUTE	0
76 #define SUN8I_ADDA_PHONEOUT_CTRL	0x08
77 #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTG	5
78 #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTEN	4
79 #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC1	3
80 #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC2	2
81 #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_RMIX	1
82 #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_LMIX	0
83 #define SUN8I_ADDA_PHONE_GAIN_CTRL	0x09
84 #define SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL	3
85 #define SUN8I_ADDA_PHONE_GAIN_CTRL_PHONEPREG	0
86 #define SUN8I_ADDA_MIC2G_CTRL		0x0a
87 #define SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN		7
88 #define SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST		4
89 #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN	3
90 #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN	2
91 #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC	1
92 #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC	0
93 #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL	0x0b
94 #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN	7
95 #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN	6
96 #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIAS_MODE	5
97 #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN		3
98 #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST		0
99 #define SUN8I_ADDA_LADCMIXSC		0x0c
100 #define SUN8I_ADDA_LADCMIXSC_MIC1		6
101 #define SUN8I_ADDA_LADCMIXSC_MIC2		5
102 #define SUN8I_ADDA_LADCMIXSC_PHONE		4
103 #define SUN8I_ADDA_LADCMIXSC_PHONEN		3
104 #define SUN8I_ADDA_LADCMIXSC_LINEINL		2
105 #define SUN8I_ADDA_LADCMIXSC_OMIXRL		1
106 #define SUN8I_ADDA_LADCMIXSC_OMIXRR		0
107 #define SUN8I_ADDA_RADCMIXSC		0x0d
108 #define SUN8I_ADDA_RADCMIXSC_MIC1		6
109 #define SUN8I_ADDA_RADCMIXSC_MIC2		5
110 #define SUN8I_ADDA_RADCMIXSC_PHONE		4
111 #define SUN8I_ADDA_RADCMIXSC_PHONEP		3
112 #define SUN8I_ADDA_RADCMIXSC_LINEINR		2
113 #define SUN8I_ADDA_RADCMIXSC_OMIXR		1
114 #define SUN8I_ADDA_RADCMIXSC_OMIXL		0
115 #define SUN8I_ADDA_RES			0x0e
116 #define SUN8I_ADDA_RES_MMICBIAS_SEL		4
117 #define SUN8I_ADDA_RES_PA_ANTI_POP_CTRL		0
118 #define SUN8I_ADDA_ADC_AP_EN		0x0f
119 #define SUN8I_ADDA_ADC_AP_EN_ADCREN		7
120 #define SUN8I_ADDA_ADC_AP_EN_ADCLEN		6
121 #define SUN8I_ADDA_ADC_AP_EN_ADCG		0
122 
123 /* Analog control register access bits */
124 #define ADDA_PR			0x0		/* PRCM base + 0x1c0 */
125 #define ADDA_PR_RESET			BIT(28)
126 #define ADDA_PR_WRITE			BIT(24)
127 #define ADDA_PR_ADDR_SHIFT		16
128 #define ADDA_PR_ADDR_MASK		GENMASK(4, 0)
129 #define ADDA_PR_DATA_IN_SHIFT		8
130 #define ADDA_PR_DATA_IN_MASK		GENMASK(7, 0)
131 #define ADDA_PR_DATA_OUT_SHIFT		0
132 #define ADDA_PR_DATA_OUT_MASK		GENMASK(7, 0)
133 
134 /* regmap access bits */
135 static int adda_reg_read(void *context, unsigned int reg, unsigned int *val)
136 {
137 	void __iomem *base = (void __iomem *)context;
138 	u32 tmp;
139 
140 	/* De-assert reset */
141 	writel(readl(base) | ADDA_PR_RESET, base);
142 
143 	/* Clear write bit */
144 	writel(readl(base) & ~ADDA_PR_WRITE, base);
145 
146 	/* Set register address */
147 	tmp = readl(base);
148 	tmp &= ~(ADDA_PR_ADDR_MASK << ADDA_PR_ADDR_SHIFT);
149 	tmp |= (reg & ADDA_PR_ADDR_MASK) << ADDA_PR_ADDR_SHIFT;
150 	writel(tmp, base);
151 
152 	/* Read back value */
153 	*val = readl(base) & ADDA_PR_DATA_OUT_MASK;
154 
155 	return 0;
156 }
157 
158 static int adda_reg_write(void *context, unsigned int reg, unsigned int val)
159 {
160 	void __iomem *base = (void __iomem *)context;
161 	u32 tmp;
162 
163 	/* De-assert reset */
164 	writel(readl(base) | ADDA_PR_RESET, base);
165 
166 	/* Set register address */
167 	tmp = readl(base);
168 	tmp &= ~(ADDA_PR_ADDR_MASK << ADDA_PR_ADDR_SHIFT);
169 	tmp |= (reg & ADDA_PR_ADDR_MASK) << ADDA_PR_ADDR_SHIFT;
170 	writel(tmp, base);
171 
172 	/* Set data to write */
173 	tmp = readl(base);
174 	tmp &= ~(ADDA_PR_DATA_IN_MASK << ADDA_PR_DATA_IN_SHIFT);
175 	tmp |= (val & ADDA_PR_DATA_IN_MASK) << ADDA_PR_DATA_IN_SHIFT;
176 	writel(tmp, base);
177 
178 	/* Set write bit to signal a write */
179 	writel(readl(base) | ADDA_PR_WRITE, base);
180 
181 	/* Clear write bit */
182 	writel(readl(base) & ~ADDA_PR_WRITE, base);
183 
184 	return 0;
185 }
186 
187 static const struct regmap_config adda_pr_regmap_cfg = {
188 	.name		= "adda-pr",
189 	.reg_bits	= 5,
190 	.reg_stride	= 1,
191 	.val_bits	= 8,
192 	.reg_read	= adda_reg_read,
193 	.reg_write	= adda_reg_write,
194 	.fast_io	= true,
195 	.max_register	= 24,
196 };
197 
198 /* mixer controls */
199 static const struct snd_kcontrol_new sun8i_codec_mixer_controls[] = {
200 	SOC_DAPM_DOUBLE_R("DAC Playback Switch",
201 			  SUN8I_ADDA_LOMIXSC,
202 			  SUN8I_ADDA_ROMIXSC,
203 			  SUN8I_ADDA_LOMIXSC_DACL, 1, 0),
204 	SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch",
205 			  SUN8I_ADDA_LOMIXSC,
206 			  SUN8I_ADDA_ROMIXSC,
207 			  SUN8I_ADDA_LOMIXSC_DACR, 1, 0),
208 	SOC_DAPM_DOUBLE_R("Line In Playback Switch",
209 			  SUN8I_ADDA_LOMIXSC,
210 			  SUN8I_ADDA_ROMIXSC,
211 			  SUN8I_ADDA_LOMIXSC_LINEINL, 1, 0),
212 	SOC_DAPM_DOUBLE_R("Mic1 Playback Switch",
213 			  SUN8I_ADDA_LOMIXSC,
214 			  SUN8I_ADDA_ROMIXSC,
215 			  SUN8I_ADDA_LOMIXSC_MIC1, 1, 0),
216 	SOC_DAPM_DOUBLE_R("Mic2 Playback Switch",
217 			  SUN8I_ADDA_LOMIXSC,
218 			  SUN8I_ADDA_ROMIXSC,
219 			  SUN8I_ADDA_LOMIXSC_MIC2, 1, 0),
220 };
221 
222 /* ADC mixer controls */
223 static const struct snd_kcontrol_new sun8i_codec_adc_mixer_controls[] = {
224 	SOC_DAPM_DOUBLE_R("Mixer Capture Switch",
225 			  SUN8I_ADDA_LADCMIXSC,
226 			  SUN8I_ADDA_RADCMIXSC,
227 			  SUN8I_ADDA_LADCMIXSC_OMIXRL, 1, 0),
228 	SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch",
229 			  SUN8I_ADDA_LADCMIXSC,
230 			  SUN8I_ADDA_RADCMIXSC,
231 			  SUN8I_ADDA_LADCMIXSC_OMIXRR, 1, 0),
232 	SOC_DAPM_DOUBLE_R("Line In Capture Switch",
233 			  SUN8I_ADDA_LADCMIXSC,
234 			  SUN8I_ADDA_RADCMIXSC,
235 			  SUN8I_ADDA_LADCMIXSC_LINEINL, 1, 0),
236 	SOC_DAPM_DOUBLE_R("Mic1 Capture Switch",
237 			  SUN8I_ADDA_LADCMIXSC,
238 			  SUN8I_ADDA_RADCMIXSC,
239 			  SUN8I_ADDA_LADCMIXSC_MIC1, 1, 0),
240 	SOC_DAPM_DOUBLE_R("Mic2 Capture Switch",
241 			  SUN8I_ADDA_LADCMIXSC,
242 			  SUN8I_ADDA_RADCMIXSC,
243 			  SUN8I_ADDA_LADCMIXSC_MIC2, 1, 0),
244 };
245 
246 /* volume / mute controls */
247 static const DECLARE_TLV_DB_SCALE(sun8i_codec_out_mixer_pregain_scale,
248 				  -450, 150, 0);
249 static const DECLARE_TLV_DB_RANGE(sun8i_codec_mic_gain_scale,
250 	0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
251 	1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
252 );
253 
254 static const struct snd_kcontrol_new sun8i_codec_common_controls[] = {
255 	/* Mixer pre-gain */
256 	SOC_SINGLE_TLV("Mic1 Playback Volume", SUN8I_ADDA_MICIN_GCTRL,
257 		       SUN8I_ADDA_MICIN_GCTRL_MIC1G,
258 		       0x7, 0, sun8i_codec_out_mixer_pregain_scale),
259 
260 	/* Microphone Amp boost gain */
261 	SOC_SINGLE_TLV("Mic1 Boost Volume", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
262 		       SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST, 0x7, 0,
263 		       sun8i_codec_mic_gain_scale),
264 
265 	/* ADC */
266 	SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN8I_ADDA_ADC_AP_EN,
267 		       SUN8I_ADDA_ADC_AP_EN_ADCG, 0x7, 0,
268 		       sun8i_codec_out_mixer_pregain_scale),
269 };
270 
271 static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = {
272 	/* ADC */
273 	SND_SOC_DAPM_ADC("Left ADC", NULL, SUN8I_ADDA_ADC_AP_EN,
274 			 SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0),
275 	SND_SOC_DAPM_ADC("Right ADC", NULL, SUN8I_ADDA_ADC_AP_EN,
276 			 SUN8I_ADDA_ADC_AP_EN_ADCREN, 0),
277 
278 	/* DAC */
279 	SND_SOC_DAPM_DAC("Left DAC", NULL, SUN8I_ADDA_DAC_PA_SRC,
280 			 SUN8I_ADDA_DAC_PA_SRC_DACALEN, 0),
281 	SND_SOC_DAPM_DAC("Right DAC", NULL, SUN8I_ADDA_DAC_PA_SRC,
282 			 SUN8I_ADDA_DAC_PA_SRC_DACAREN, 0),
283 	/*
284 	 * Due to this component and the codec belonging to separate DAPM
285 	 * contexts, we need to manually link the above widgets to their
286 	 * stream widgets at the card level.
287 	 */
288 
289 	/* Microphone input */
290 	SND_SOC_DAPM_INPUT("MIC1"),
291 
292 	/* Microphone Bias */
293 	SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
294 			    SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN,
295 			    0, NULL, 0),
296 
297 	/* Mic input path */
298 	SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
299 			 SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN, 0, NULL, 0),
300 
301 	/* Mixers */
302 	SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC,
303 			   SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0,
304 			   sun8i_codec_mixer_controls,
305 			   ARRAY_SIZE(sun8i_codec_mixer_controls)),
306 	SND_SOC_DAPM_MIXER("Right Mixer", SUN8I_ADDA_DAC_PA_SRC,
307 			   SUN8I_ADDA_DAC_PA_SRC_RMIXEN, 0,
308 			   sun8i_codec_mixer_controls,
309 			   ARRAY_SIZE(sun8i_codec_mixer_controls)),
310 	SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
311 			   SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0,
312 			   sun8i_codec_adc_mixer_controls,
313 			   ARRAY_SIZE(sun8i_codec_adc_mixer_controls)),
314 	SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
315 			   SUN8I_ADDA_ADC_AP_EN_ADCREN, 0,
316 			   sun8i_codec_adc_mixer_controls,
317 			   ARRAY_SIZE(sun8i_codec_adc_mixer_controls)),
318 };
319 
320 static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = {
321 	/* Microphone Routes */
322 	{ "Mic1 Amplifier", NULL, "MIC1"},
323 
324 	/* Left Mixer Routes */
325 	{ "Left Mixer", "DAC Playback Switch", "Left DAC" },
326 	{ "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
327 	{ "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
328 
329 	/* Right Mixer Routes */
330 	{ "Right Mixer", "DAC Playback Switch", "Right DAC" },
331 	{ "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
332 	{ "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
333 
334 	/* Left ADC Mixer Routes */
335 	{ "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
336 	{ "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
337 	{ "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
338 
339 	/* Right ADC Mixer Routes */
340 	{ "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
341 	{ "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
342 	{ "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
343 
344 	/* ADC Routes */
345 	{ "Left ADC", NULL, "Left ADC Mixer" },
346 	{ "Right ADC", NULL, "Right ADC Mixer" },
347 };
348 
349 /* headphone specific controls, widgets, and routes */
350 static const DECLARE_TLV_DB_SCALE(sun8i_codec_hp_vol_scale, -6300, 100, 1);
351 static const struct snd_kcontrol_new sun8i_codec_headphone_controls[] = {
352 	SOC_SINGLE_TLV("Headphone Playback Volume",
353 		       SUN8I_ADDA_HP_VOLC,
354 		       SUN8I_ADDA_HP_VOLC_HP_VOL, 0x3f, 0,
355 		       sun8i_codec_hp_vol_scale),
356 	SOC_DOUBLE("Headphone Playback Switch",
357 		   SUN8I_ADDA_DAC_PA_SRC,
358 		   SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE,
359 		   SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE, 1, 0),
360 };
361 
362 static const char * const sun8i_codec_hp_src_enum_text[] = {
363 	"DAC", "Mixer",
364 };
365 
366 static SOC_ENUM_DOUBLE_DECL(sun8i_codec_hp_src_enum,
367 			    SUN8I_ADDA_DAC_PA_SRC,
368 			    SUN8I_ADDA_DAC_PA_SRC_LHPIS,
369 			    SUN8I_ADDA_DAC_PA_SRC_RHPIS,
370 			    sun8i_codec_hp_src_enum_text);
371 
372 static const struct snd_kcontrol_new sun8i_codec_hp_src[] = {
373 	SOC_DAPM_ENUM("Headphone Source Playback Route",
374 		      sun8i_codec_hp_src_enum),
375 };
376 
377 static int sun8i_headphone_amp_event(struct snd_soc_dapm_widget *w,
378 				     struct snd_kcontrol *k, int event)
379 {
380 	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
381 
382 	if (SND_SOC_DAPM_EVENT_ON(event)) {
383 		snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL,
384 					      BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN),
385 					      BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN));
386 		/*
387 		 * Need a delay to have the amplifier up. 700ms seems the best
388 		 * compromise between the time to let the amplifier up and the
389 		 * time not to feel this delay while playing a sound.
390 		 */
391 		msleep(700);
392 	} else if (SND_SOC_DAPM_EVENT_OFF(event)) {
393 		snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL,
394 					      BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN),
395 					      0x0);
396 	}
397 
398 	return 0;
399 }
400 
401 static const struct snd_soc_dapm_widget sun8i_codec_headphone_widgets[] = {
402 	SND_SOC_DAPM_MUX("Headphone Source Playback Route",
403 			 SND_SOC_NOPM, 0, 0, sun8i_codec_hp_src),
404 	SND_SOC_DAPM_OUT_DRV_E("Headphone Amp", SUN8I_ADDA_PAEN_HP_CTRL,
405 			       SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0, NULL, 0,
406 			       sun8i_headphone_amp_event,
407 			       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
408 	SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN8I_ADDA_PAEN_HP_CTRL,
409 			    SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN, 0, NULL, 0),
410 	SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN8I_ADDA_PAEN_HP_CTRL,
411 			 SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC, 0x3, 0x3, 0),
412 	SND_SOC_DAPM_OUTPUT("HP"),
413 };
414 
415 static const struct snd_soc_dapm_route sun8i_codec_headphone_routes[] = {
416 	{ "Headphone Source Playback Route", "DAC", "Left DAC" },
417 	{ "Headphone Source Playback Route", "DAC", "Right DAC" },
418 	{ "Headphone Source Playback Route", "Mixer", "Left Mixer" },
419 	{ "Headphone Source Playback Route", "Mixer", "Right Mixer" },
420 	{ "Headphone Amp", NULL, "Headphone Source Playback Route" },
421 	{ "HPCOM", NULL, "HPCOM Protection" },
422 	{ "HP", NULL, "Headphone Amp" },
423 };
424 
425 static int sun8i_codec_add_headphone(struct snd_soc_component *cmpnt)
426 {
427 	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
428 	struct device *dev = cmpnt->dev;
429 	int ret;
430 
431 	ret = snd_soc_add_component_controls(cmpnt,
432 					     sun8i_codec_headphone_controls,
433 					     ARRAY_SIZE(sun8i_codec_headphone_controls));
434 	if (ret) {
435 		dev_err(dev, "Failed to add Headphone controls: %d\n", ret);
436 		return ret;
437 	}
438 
439 	ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_headphone_widgets,
440 					ARRAY_SIZE(sun8i_codec_headphone_widgets));
441 	if (ret) {
442 		dev_err(dev, "Failed to add Headphone DAPM widgets: %d\n", ret);
443 		return ret;
444 	}
445 
446 	ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_headphone_routes,
447 				      ARRAY_SIZE(sun8i_codec_headphone_routes));
448 	if (ret) {
449 		dev_err(dev, "Failed to add Headphone DAPM routes: %d\n", ret);
450 		return ret;
451 	}
452 
453 	return 0;
454 }
455 
456 /* hmic specific widget */
457 static const struct snd_soc_dapm_widget sun8i_codec_hmic_widgets[] = {
458 	SND_SOC_DAPM_SUPPLY("HBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
459 			    SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN,
460 			    0, NULL, 0),
461 };
462 
463 static int sun8i_codec_add_hmic(struct snd_soc_component *cmpnt)
464 {
465 	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
466 	struct device *dev = cmpnt->dev;
467 	int ret;
468 
469 	ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_hmic_widgets,
470 					ARRAY_SIZE(sun8i_codec_hmic_widgets));
471 	if (ret)
472 		dev_err(dev, "Failed to add Mic3 DAPM widgets: %d\n", ret);
473 
474 	return ret;
475 }
476 
477 /* line in specific controls, widgets and rines */
478 static const struct snd_kcontrol_new sun8i_codec_linein_controls[] = {
479 	/* Mixer pre-gain */
480 	SOC_SINGLE_TLV("Line In Playback Volume", SUN8I_ADDA_LINEIN_GCTRL,
481 		       SUN8I_ADDA_LINEIN_GCTRL_LINEING,
482 		       0x7, 0, sun8i_codec_out_mixer_pregain_scale),
483 };
484 
485 static const struct snd_soc_dapm_widget sun8i_codec_linein_widgets[] = {
486 	/* Line input */
487 	SND_SOC_DAPM_INPUT("LINEIN"),
488 };
489 
490 static const struct snd_soc_dapm_route sun8i_codec_linein_routes[] = {
491 	{ "Left Mixer", "Line In Playback Switch", "LINEIN" },
492 
493 	{ "Right Mixer", "Line In Playback Switch", "LINEIN" },
494 
495 	{ "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
496 
497 	{ "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
498 };
499 
500 static int sun8i_codec_add_linein(struct snd_soc_component *cmpnt)
501 {
502 	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
503 	struct device *dev = cmpnt->dev;
504 	int ret;
505 
506 	ret = snd_soc_add_component_controls(cmpnt,
507 					     sun8i_codec_linein_controls,
508 					     ARRAY_SIZE(sun8i_codec_linein_controls));
509 	if (ret) {
510 		dev_err(dev, "Failed to add Line In controls: %d\n", ret);
511 		return ret;
512 	}
513 
514 	ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_linein_widgets,
515 					ARRAY_SIZE(sun8i_codec_linein_widgets));
516 	if (ret) {
517 		dev_err(dev, "Failed to add Line In DAPM widgets: %d\n", ret);
518 		return ret;
519 	}
520 
521 	ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_linein_routes,
522 				      ARRAY_SIZE(sun8i_codec_linein_routes));
523 	if (ret) {
524 		dev_err(dev, "Failed to add Line In DAPM routes: %d\n", ret);
525 		return ret;
526 	}
527 
528 	return 0;
529 }
530 
531 
532 /* line out specific controls, widgets and routes */
533 static const DECLARE_TLV_DB_RANGE(sun8i_codec_lineout_vol_scale,
534 	0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
535 	2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),
536 );
537 static const struct snd_kcontrol_new sun8i_codec_lineout_controls[] = {
538 	SOC_SINGLE_TLV("Line Out Playback Volume",
539 		       SUN8I_ADDA_PHONE_GAIN_CTRL,
540 		       SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL, 0x1f, 0,
541 		       sun8i_codec_lineout_vol_scale),
542 	SOC_DOUBLE("Line Out Playback Switch",
543 		   SUN8I_ADDA_MIC2G_CTRL,
544 		   SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN,
545 		   SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN, 1, 0),
546 };
547 
548 static const char * const sun8i_codec_lineout_src_enum_text[] = {
549 	"Stereo", "Mono Differential",
550 };
551 
552 static SOC_ENUM_DOUBLE_DECL(sun8i_codec_lineout_src_enum,
553 			    SUN8I_ADDA_MIC2G_CTRL,
554 			    SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC,
555 			    SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC,
556 			    sun8i_codec_lineout_src_enum_text);
557 
558 static const struct snd_kcontrol_new sun8i_codec_lineout_src[] = {
559 	SOC_DAPM_ENUM("Line Out Source Playback Route",
560 		      sun8i_codec_lineout_src_enum),
561 };
562 
563 static const struct snd_soc_dapm_widget sun8i_codec_lineout_widgets[] = {
564 	SND_SOC_DAPM_MUX("Line Out Source Playback Route",
565 			 SND_SOC_NOPM, 0, 0, sun8i_codec_lineout_src),
566 	/* It is unclear if this is a buffer or gate, model it as a supply */
567 	SND_SOC_DAPM_SUPPLY("Line Out Enable", SUN8I_ADDA_PAEN_HP_CTRL,
568 			    SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN, 0, NULL, 0),
569 	SND_SOC_DAPM_OUTPUT("LINEOUT"),
570 };
571 
572 static const struct snd_soc_dapm_route sun8i_codec_lineout_routes[] = {
573 	{ "Line Out Source Playback Route", "Stereo", "Left Mixer" },
574 	{ "Line Out Source Playback Route", "Stereo", "Right Mixer" },
575 	{ "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
576 	{ "Line Out Source Playback Route", "Mono Differential", "Right Mixer" },
577 	{ "LINEOUT", NULL, "Line Out Source Playback Route" },
578 	{ "LINEOUT", NULL, "Line Out Enable", },
579 };
580 
581 static int sun8i_codec_add_lineout(struct snd_soc_component *cmpnt)
582 {
583 	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
584 	struct device *dev = cmpnt->dev;
585 	int ret;
586 
587 	ret = snd_soc_add_component_controls(cmpnt,
588 					     sun8i_codec_lineout_controls,
589 					     ARRAY_SIZE(sun8i_codec_lineout_controls));
590 	if (ret) {
591 		dev_err(dev, "Failed to add Line Out controls: %d\n", ret);
592 		return ret;
593 	}
594 
595 	ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_lineout_widgets,
596 					ARRAY_SIZE(sun8i_codec_lineout_widgets));
597 	if (ret) {
598 		dev_err(dev, "Failed to add Line Out DAPM widgets: %d\n", ret);
599 		return ret;
600 	}
601 
602 	ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_lineout_routes,
603 				      ARRAY_SIZE(sun8i_codec_lineout_routes));
604 	if (ret) {
605 		dev_err(dev, "Failed to add Line Out DAPM routes: %d\n", ret);
606 		return ret;
607 	}
608 
609 	return 0;
610 }
611 
612 /* mic2 specific controls, widgets and routes */
613 static const struct snd_kcontrol_new sun8i_codec_mic2_controls[] = {
614 	/* Mixer pre-gain */
615 	SOC_SINGLE_TLV("Mic2 Playback Volume",
616 		       SUN8I_ADDA_MICIN_GCTRL, SUN8I_ADDA_MICIN_GCTRL_MIC2G,
617 		       0x7, 0, sun8i_codec_out_mixer_pregain_scale),
618 
619 	/* Microphone Amp boost gain */
620 	SOC_SINGLE_TLV("Mic2 Boost Volume", SUN8I_ADDA_MIC2G_CTRL,
621 		       SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST, 0x7, 0,
622 		       sun8i_codec_mic_gain_scale),
623 };
624 
625 static const struct snd_soc_dapm_widget sun8i_codec_mic2_widgets[] = {
626 	/* Microphone input */
627 	SND_SOC_DAPM_INPUT("MIC2"),
628 
629 	/* Mic input path */
630 	SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN8I_ADDA_MIC2G_CTRL,
631 			 SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN, 0, NULL, 0),
632 };
633 
634 static const struct snd_soc_dapm_route sun8i_codec_mic2_routes[] = {
635 	{ "Mic2 Amplifier", NULL, "MIC2"},
636 
637 	{ "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
638 
639 	{ "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
640 
641 	{ "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
642 
643 	{ "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
644 };
645 
646 static int sun8i_codec_add_mic2(struct snd_soc_component *cmpnt)
647 {
648 	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
649 	struct device *dev = cmpnt->dev;
650 	int ret;
651 
652 	ret = snd_soc_add_component_controls(cmpnt,
653 					     sun8i_codec_mic2_controls,
654 					     ARRAY_SIZE(sun8i_codec_mic2_controls));
655 	if (ret) {
656 		dev_err(dev, "Failed to add MIC2 controls: %d\n", ret);
657 		return ret;
658 	}
659 
660 	ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_mic2_widgets,
661 					ARRAY_SIZE(sun8i_codec_mic2_widgets));
662 	if (ret) {
663 		dev_err(dev, "Failed to add MIC2 DAPM widgets: %d\n", ret);
664 		return ret;
665 	}
666 
667 	ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_mic2_routes,
668 				      ARRAY_SIZE(sun8i_codec_mic2_routes));
669 	if (ret) {
670 		dev_err(dev, "Failed to add MIC2 DAPM routes: %d\n", ret);
671 		return ret;
672 	}
673 
674 	return 0;
675 }
676 
677 struct sun8i_codec_analog_quirks {
678 	bool has_headphone;
679 	bool has_hmic;
680 	bool has_linein;
681 	bool has_lineout;
682 	bool has_mic2;
683 };
684 
685 static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = {
686 	.has_headphone	= true,
687 	.has_hmic	= true,
688 	.has_linein	= true,
689 	.has_mic2	= true,
690 };
691 
692 static const struct sun8i_codec_analog_quirks sun8i_h3_quirks = {
693 	.has_linein	= true,
694 	.has_lineout	= true,
695 	.has_mic2	= true,
696 };
697 
698 static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt)
699 {
700 	struct device *dev = cmpnt->dev;
701 	const struct sun8i_codec_analog_quirks *quirks;
702 	int ret;
703 
704 	/*
705 	 * This would never return NULL unless someone directly registers a
706 	 * platform device matching this driver's name, without specifying a
707 	 * device tree node.
708 	 */
709 	quirks = of_device_get_match_data(dev);
710 
711 	/* Add controls, widgets, and routes for individual features */
712 
713 	if (quirks->has_headphone) {
714 		ret = sun8i_codec_add_headphone(cmpnt);
715 		if (ret)
716 			return ret;
717 	}
718 
719 	if (quirks->has_hmic) {
720 		ret = sun8i_codec_add_hmic(cmpnt);
721 		if (ret)
722 			return ret;
723 	}
724 
725 	if (quirks->has_linein) {
726 		ret = sun8i_codec_add_linein(cmpnt);
727 		if (ret)
728 			return ret;
729 	}
730 
731 	if (quirks->has_lineout) {
732 		ret = sun8i_codec_add_lineout(cmpnt);
733 		if (ret)
734 			return ret;
735 	}
736 
737 	if (quirks->has_mic2) {
738 		ret = sun8i_codec_add_mic2(cmpnt);
739 		if (ret)
740 			return ret;
741 	}
742 
743 	return 0;
744 }
745 
746 static const struct snd_soc_component_driver sun8i_codec_analog_cmpnt_drv = {
747 	.controls		= sun8i_codec_common_controls,
748 	.num_controls		= ARRAY_SIZE(sun8i_codec_common_controls),
749 	.dapm_widgets		= sun8i_codec_common_widgets,
750 	.num_dapm_widgets	= ARRAY_SIZE(sun8i_codec_common_widgets),
751 	.dapm_routes		= sun8i_codec_common_routes,
752 	.num_dapm_routes	= ARRAY_SIZE(sun8i_codec_common_routes),
753 	.probe			= sun8i_codec_analog_cmpnt_probe,
754 };
755 
756 static const struct of_device_id sun8i_codec_analog_of_match[] = {
757 	{
758 		.compatible = "allwinner,sun8i-a23-codec-analog",
759 		.data = &sun8i_a23_quirks,
760 	},
761 	{
762 		.compatible = "allwinner,sun8i-h3-codec-analog",
763 		.data = &sun8i_h3_quirks,
764 	},
765 	{}
766 };
767 MODULE_DEVICE_TABLE(of, sun8i_codec_analog_of_match);
768 
769 static int sun8i_codec_analog_probe(struct platform_device *pdev)
770 {
771 	struct resource *res;
772 	struct regmap *regmap;
773 	void __iomem *base;
774 
775 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
776 	base = devm_ioremap_resource(&pdev->dev, res);
777 	if (IS_ERR(base)) {
778 		dev_err(&pdev->dev, "Failed to map the registers\n");
779 		return PTR_ERR(base);
780 	}
781 
782 	regmap = devm_regmap_init(&pdev->dev, NULL, base, &adda_pr_regmap_cfg);
783 	if (IS_ERR(regmap)) {
784 		dev_err(&pdev->dev, "Failed to create regmap\n");
785 		return PTR_ERR(regmap);
786 	}
787 
788 	return devm_snd_soc_register_component(&pdev->dev,
789 					       &sun8i_codec_analog_cmpnt_drv,
790 					       NULL, 0);
791 }
792 
793 static struct platform_driver sun8i_codec_analog_driver = {
794 	.driver = {
795 		.name = "sun8i-codec-analog",
796 		.of_match_table = sun8i_codec_analog_of_match,
797 	},
798 	.probe = sun8i_codec_analog_probe,
799 };
800 module_platform_driver(sun8i_codec_analog_driver);
801 
802 MODULE_DESCRIPTION("Allwinner internal codec analog controls driver");
803 MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
804 MODULE_LICENSE("GPL");
805 MODULE_ALIAS("platform:sun8i-codec-analog");
806