xref: /openbmc/linux/sound/soc/sunxi/sun8i-codec-analog.c (revision 4ed91d48259d9ddd378424d008f2e6559f7e78f8)
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-gains */
256 	SOC_SINGLE_TLV("Line In Playback Volume", SUN8I_ADDA_LINEIN_GCTRL,
257 		       SUN8I_ADDA_LINEIN_GCTRL_LINEING,
258 		       0x7, 0, sun8i_codec_out_mixer_pregain_scale),
259 	SOC_SINGLE_TLV("Mic1 Playback Volume", SUN8I_ADDA_MICIN_GCTRL,
260 		       SUN8I_ADDA_MICIN_GCTRL_MIC1G,
261 		       0x7, 0, sun8i_codec_out_mixer_pregain_scale),
262 	SOC_SINGLE_TLV("Mic2 Playback Volume",
263 		       SUN8I_ADDA_MICIN_GCTRL, SUN8I_ADDA_MICIN_GCTRL_MIC2G,
264 		       0x7, 0, sun8i_codec_out_mixer_pregain_scale),
265 
266 	/* Microphone Amp boost gains */
267 	SOC_SINGLE_TLV("Mic1 Boost Volume", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
268 		       SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST, 0x7, 0,
269 		       sun8i_codec_mic_gain_scale),
270 	SOC_SINGLE_TLV("Mic2 Boost Volume", SUN8I_ADDA_MIC2G_CTRL,
271 		       SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST, 0x7, 0,
272 		       sun8i_codec_mic_gain_scale),
273 
274 	/* ADC */
275 	SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN8I_ADDA_ADC_AP_EN,
276 		       SUN8I_ADDA_ADC_AP_EN_ADCG, 0x7, 0,
277 		       sun8i_codec_out_mixer_pregain_scale),
278 };
279 
280 static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = {
281 	/* ADC */
282 	SND_SOC_DAPM_ADC("Left ADC", NULL, SUN8I_ADDA_ADC_AP_EN,
283 			 SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0),
284 	SND_SOC_DAPM_ADC("Right ADC", NULL, SUN8I_ADDA_ADC_AP_EN,
285 			 SUN8I_ADDA_ADC_AP_EN_ADCREN, 0),
286 
287 	/* DAC */
288 	SND_SOC_DAPM_DAC("Left DAC", NULL, SUN8I_ADDA_DAC_PA_SRC,
289 			 SUN8I_ADDA_DAC_PA_SRC_DACALEN, 0),
290 	SND_SOC_DAPM_DAC("Right DAC", NULL, SUN8I_ADDA_DAC_PA_SRC,
291 			 SUN8I_ADDA_DAC_PA_SRC_DACAREN, 0),
292 	/*
293 	 * Due to this component and the codec belonging to separate DAPM
294 	 * contexts, we need to manually link the above widgets to their
295 	 * stream widgets at the card level.
296 	 */
297 
298 	/* Line In */
299 	SND_SOC_DAPM_INPUT("LINEIN"),
300 
301 	/* Microphone inputs */
302 	SND_SOC_DAPM_INPUT("MIC1"),
303 	SND_SOC_DAPM_INPUT("MIC2"),
304 
305 	/* Microphone Bias */
306 	SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
307 			    SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN,
308 			    0, NULL, 0),
309 
310 	/* Mic input path */
311 	SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
312 			 SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN, 0, NULL, 0),
313 	SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN8I_ADDA_MIC2G_CTRL,
314 			 SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN, 0, NULL, 0),
315 
316 	/* Mixers */
317 	SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC,
318 			   SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0,
319 			   sun8i_codec_mixer_controls,
320 			   ARRAY_SIZE(sun8i_codec_mixer_controls)),
321 	SND_SOC_DAPM_MIXER("Right Mixer", SUN8I_ADDA_DAC_PA_SRC,
322 			   SUN8I_ADDA_DAC_PA_SRC_RMIXEN, 0,
323 			   sun8i_codec_mixer_controls,
324 			   ARRAY_SIZE(sun8i_codec_mixer_controls)),
325 	SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
326 			   SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0,
327 			   sun8i_codec_adc_mixer_controls,
328 			   ARRAY_SIZE(sun8i_codec_adc_mixer_controls)),
329 	SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
330 			   SUN8I_ADDA_ADC_AP_EN_ADCREN, 0,
331 			   sun8i_codec_adc_mixer_controls,
332 			   ARRAY_SIZE(sun8i_codec_adc_mixer_controls)),
333 };
334 
335 static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = {
336 	/* Microphone Routes */
337 	{ "Mic1 Amplifier", NULL, "MIC1"},
338 	{ "Mic2 Amplifier", NULL, "MIC2"},
339 
340 	/* Left Mixer Routes */
341 	{ "Left Mixer", "DAC Playback Switch", "Left DAC" },
342 	{ "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
343 	{ "Left Mixer", "Line In Playback Switch", "LINEIN" },
344 	{ "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
345 	{ "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
346 
347 	/* Right Mixer Routes */
348 	{ "Right Mixer", "DAC Playback Switch", "Right DAC" },
349 	{ "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
350 	{ "Right Mixer", "Line In Playback Switch", "LINEIN" },
351 	{ "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
352 	{ "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
353 
354 	/* Left ADC Mixer Routes */
355 	{ "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
356 	{ "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
357 	{ "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
358 	{ "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
359 	{ "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
360 
361 	/* Right ADC Mixer Routes */
362 	{ "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
363 	{ "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
364 	{ "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
365 	{ "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
366 	{ "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
367 
368 	/* ADC Routes */
369 	{ "Left ADC", NULL, "Left ADC Mixer" },
370 	{ "Right ADC", NULL, "Right ADC Mixer" },
371 };
372 
373 /* headphone specific controls, widgets, and routes */
374 static const DECLARE_TLV_DB_SCALE(sun8i_codec_hp_vol_scale, -6300, 100, 1);
375 static const struct snd_kcontrol_new sun8i_codec_headphone_controls[] = {
376 	SOC_SINGLE_TLV("Headphone Playback Volume",
377 		       SUN8I_ADDA_HP_VOLC,
378 		       SUN8I_ADDA_HP_VOLC_HP_VOL, 0x3f, 0,
379 		       sun8i_codec_hp_vol_scale),
380 	SOC_DOUBLE("Headphone Playback Switch",
381 		   SUN8I_ADDA_DAC_PA_SRC,
382 		   SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE,
383 		   SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE, 1, 0),
384 };
385 
386 static const char * const sun8i_codec_hp_src_enum_text[] = {
387 	"DAC", "Mixer",
388 };
389 
390 static SOC_ENUM_DOUBLE_DECL(sun8i_codec_hp_src_enum,
391 			    SUN8I_ADDA_DAC_PA_SRC,
392 			    SUN8I_ADDA_DAC_PA_SRC_LHPIS,
393 			    SUN8I_ADDA_DAC_PA_SRC_RHPIS,
394 			    sun8i_codec_hp_src_enum_text);
395 
396 static const struct snd_kcontrol_new sun8i_codec_hp_src[] = {
397 	SOC_DAPM_ENUM("Headphone Source Playback Route",
398 		      sun8i_codec_hp_src_enum),
399 };
400 
401 static int sun8i_headphone_amp_event(struct snd_soc_dapm_widget *w,
402 				     struct snd_kcontrol *k, int event)
403 {
404 	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
405 
406 	if (SND_SOC_DAPM_EVENT_ON(event)) {
407 		snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL,
408 					      BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN),
409 					      BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN));
410 		/*
411 		 * Need a delay to have the amplifier up. 700ms seems the best
412 		 * compromise between the time to let the amplifier up and the
413 		 * time not to feel this delay while playing a sound.
414 		 */
415 		msleep(700);
416 	} else if (SND_SOC_DAPM_EVENT_OFF(event)) {
417 		snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL,
418 					      BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN),
419 					      0x0);
420 	}
421 
422 	return 0;
423 }
424 
425 static const struct snd_soc_dapm_widget sun8i_codec_headphone_widgets[] = {
426 	SND_SOC_DAPM_MUX("Headphone Source Playback Route",
427 			 SND_SOC_NOPM, 0, 0, sun8i_codec_hp_src),
428 	SND_SOC_DAPM_OUT_DRV_E("Headphone Amp", SUN8I_ADDA_PAEN_HP_CTRL,
429 			       SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0, NULL, 0,
430 			       sun8i_headphone_amp_event,
431 			       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
432 	SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN8I_ADDA_PAEN_HP_CTRL,
433 			    SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN, 0, NULL, 0),
434 	SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN8I_ADDA_PAEN_HP_CTRL,
435 			 SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC, 0x3, 0x3, 0),
436 	SND_SOC_DAPM_OUTPUT("HP"),
437 };
438 
439 static const struct snd_soc_dapm_route sun8i_codec_headphone_routes[] = {
440 	{ "Headphone Source Playback Route", "DAC", "Left DAC" },
441 	{ "Headphone Source Playback Route", "DAC", "Right DAC" },
442 	{ "Headphone Source Playback Route", "Mixer", "Left Mixer" },
443 	{ "Headphone Source Playback Route", "Mixer", "Right Mixer" },
444 	{ "Headphone Amp", NULL, "Headphone Source Playback Route" },
445 	{ "HPCOM", NULL, "HPCOM Protection" },
446 	{ "HP", NULL, "Headphone Amp" },
447 };
448 
449 static int sun8i_codec_add_headphone(struct snd_soc_component *cmpnt)
450 {
451 	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
452 	struct device *dev = cmpnt->dev;
453 	int ret;
454 
455 	ret = snd_soc_add_component_controls(cmpnt,
456 					     sun8i_codec_headphone_controls,
457 					     ARRAY_SIZE(sun8i_codec_headphone_controls));
458 	if (ret) {
459 		dev_err(dev, "Failed to add Headphone controls: %d\n", ret);
460 		return ret;
461 	}
462 
463 	ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_headphone_widgets,
464 					ARRAY_SIZE(sun8i_codec_headphone_widgets));
465 	if (ret) {
466 		dev_err(dev, "Failed to add Headphone DAPM widgets: %d\n", ret);
467 		return ret;
468 	}
469 
470 	ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_headphone_routes,
471 				      ARRAY_SIZE(sun8i_codec_headphone_routes));
472 	if (ret) {
473 		dev_err(dev, "Failed to add Headphone DAPM routes: %d\n", ret);
474 		return ret;
475 	}
476 
477 	return 0;
478 }
479 
480 /* hmic specific widget */
481 static const struct snd_soc_dapm_widget sun8i_codec_hmic_widgets[] = {
482 	SND_SOC_DAPM_SUPPLY("HBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
483 			    SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN,
484 			    0, NULL, 0),
485 };
486 
487 static int sun8i_codec_add_hmic(struct snd_soc_component *cmpnt)
488 {
489 	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
490 	struct device *dev = cmpnt->dev;
491 	int ret;
492 
493 	ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_hmic_widgets,
494 					ARRAY_SIZE(sun8i_codec_hmic_widgets));
495 	if (ret)
496 		dev_err(dev, "Failed to add Mic3 DAPM widgets: %d\n", ret);
497 
498 	return ret;
499 }
500 
501 /* line out specific controls, widgets and routes */
502 static const DECLARE_TLV_DB_RANGE(sun8i_codec_lineout_vol_scale,
503 	0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
504 	2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),
505 );
506 static const struct snd_kcontrol_new sun8i_codec_lineout_controls[] = {
507 	SOC_SINGLE_TLV("Line Out Playback Volume",
508 		       SUN8I_ADDA_PHONE_GAIN_CTRL,
509 		       SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL, 0x1f, 0,
510 		       sun8i_codec_lineout_vol_scale),
511 	SOC_DOUBLE("Line Out Playback Switch",
512 		   SUN8I_ADDA_MIC2G_CTRL,
513 		   SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN,
514 		   SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN, 1, 0),
515 };
516 
517 static const char * const sun8i_codec_lineout_src_enum_text[] = {
518 	"Stereo", "Mono Differential",
519 };
520 
521 static SOC_ENUM_DOUBLE_DECL(sun8i_codec_lineout_src_enum,
522 			    SUN8I_ADDA_MIC2G_CTRL,
523 			    SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC,
524 			    SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC,
525 			    sun8i_codec_lineout_src_enum_text);
526 
527 static const struct snd_kcontrol_new sun8i_codec_lineout_src[] = {
528 	SOC_DAPM_ENUM("Line Out Source Playback Route",
529 		      sun8i_codec_lineout_src_enum),
530 };
531 
532 static const struct snd_soc_dapm_widget sun8i_codec_lineout_widgets[] = {
533 	SND_SOC_DAPM_MUX("Line Out Source Playback Route",
534 			 SND_SOC_NOPM, 0, 0, sun8i_codec_lineout_src),
535 	/* It is unclear if this is a buffer or gate, model it as a supply */
536 	SND_SOC_DAPM_SUPPLY("Line Out Enable", SUN8I_ADDA_PAEN_HP_CTRL,
537 			    SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN, 0, NULL, 0),
538 	SND_SOC_DAPM_OUTPUT("LINEOUT"),
539 };
540 
541 static const struct snd_soc_dapm_route sun8i_codec_lineout_routes[] = {
542 	{ "Line Out Source Playback Route", "Stereo", "Left Mixer" },
543 	{ "Line Out Source Playback Route", "Stereo", "Right Mixer" },
544 	{ "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
545 	{ "Line Out Source Playback Route", "Mono Differential", "Right Mixer" },
546 	{ "LINEOUT", NULL, "Line Out Source Playback Route" },
547 	{ "LINEOUT", NULL, "Line Out Enable", },
548 };
549 
550 static int sun8i_codec_add_lineout(struct snd_soc_component *cmpnt)
551 {
552 	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
553 	struct device *dev = cmpnt->dev;
554 	int ret;
555 
556 	ret = snd_soc_add_component_controls(cmpnt,
557 					     sun8i_codec_lineout_controls,
558 					     ARRAY_SIZE(sun8i_codec_lineout_controls));
559 	if (ret) {
560 		dev_err(dev, "Failed to add Line Out controls: %d\n", ret);
561 		return ret;
562 	}
563 
564 	ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_lineout_widgets,
565 					ARRAY_SIZE(sun8i_codec_lineout_widgets));
566 	if (ret) {
567 		dev_err(dev, "Failed to add Line Out DAPM widgets: %d\n", ret);
568 		return ret;
569 	}
570 
571 	ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_lineout_routes,
572 				      ARRAY_SIZE(sun8i_codec_lineout_routes));
573 	if (ret) {
574 		dev_err(dev, "Failed to add Line Out DAPM routes: %d\n", ret);
575 		return ret;
576 	}
577 
578 	return 0;
579 }
580 
581 struct sun8i_codec_analog_quirks {
582 	bool has_headphone;
583 	bool has_hmic;
584 	bool has_lineout;
585 };
586 
587 static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = {
588 	.has_headphone	= true,
589 	.has_hmic	= true,
590 };
591 
592 static const struct sun8i_codec_analog_quirks sun8i_h3_quirks = {
593 	.has_lineout	= true,
594 };
595 
596 static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt)
597 {
598 	struct device *dev = cmpnt->dev;
599 	const struct sun8i_codec_analog_quirks *quirks;
600 	int ret;
601 
602 	/*
603 	 * This would never return NULL unless someone directly registers a
604 	 * platform device matching this driver's name, without specifying a
605 	 * device tree node.
606 	 */
607 	quirks = of_device_get_match_data(dev);
608 
609 	/* Add controls, widgets, and routes for individual features */
610 
611 	if (quirks->has_headphone) {
612 		ret = sun8i_codec_add_headphone(cmpnt);
613 		if (ret)
614 			return ret;
615 	}
616 
617 	if (quirks->has_hmic) {
618 		ret = sun8i_codec_add_hmic(cmpnt);
619 		if (ret)
620 			return ret;
621 	}
622 
623 	if (quirks->has_lineout) {
624 		ret = sun8i_codec_add_lineout(cmpnt);
625 		if (ret)
626 			return ret;
627 	}
628 
629 	return 0;
630 }
631 
632 static const struct snd_soc_component_driver sun8i_codec_analog_cmpnt_drv = {
633 	.controls		= sun8i_codec_common_controls,
634 	.num_controls		= ARRAY_SIZE(sun8i_codec_common_controls),
635 	.dapm_widgets		= sun8i_codec_common_widgets,
636 	.num_dapm_widgets	= ARRAY_SIZE(sun8i_codec_common_widgets),
637 	.dapm_routes		= sun8i_codec_common_routes,
638 	.num_dapm_routes	= ARRAY_SIZE(sun8i_codec_common_routes),
639 	.probe			= sun8i_codec_analog_cmpnt_probe,
640 };
641 
642 static const struct of_device_id sun8i_codec_analog_of_match[] = {
643 	{
644 		.compatible = "allwinner,sun8i-a23-codec-analog",
645 		.data = &sun8i_a23_quirks,
646 	},
647 	{
648 		.compatible = "allwinner,sun8i-h3-codec-analog",
649 		.data = &sun8i_h3_quirks,
650 	},
651 	{}
652 };
653 MODULE_DEVICE_TABLE(of, sun8i_codec_analog_of_match);
654 
655 static int sun8i_codec_analog_probe(struct platform_device *pdev)
656 {
657 	struct resource *res;
658 	struct regmap *regmap;
659 	void __iomem *base;
660 
661 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
662 	base = devm_ioremap_resource(&pdev->dev, res);
663 	if (IS_ERR(base)) {
664 		dev_err(&pdev->dev, "Failed to map the registers\n");
665 		return PTR_ERR(base);
666 	}
667 
668 	regmap = devm_regmap_init(&pdev->dev, NULL, base, &adda_pr_regmap_cfg);
669 	if (IS_ERR(regmap)) {
670 		dev_err(&pdev->dev, "Failed to create regmap\n");
671 		return PTR_ERR(regmap);
672 	}
673 
674 	return devm_snd_soc_register_component(&pdev->dev,
675 					       &sun8i_codec_analog_cmpnt_drv,
676 					       NULL, 0);
677 }
678 
679 static struct platform_driver sun8i_codec_analog_driver = {
680 	.driver = {
681 		.name = "sun8i-codec-analog",
682 		.of_match_table = sun8i_codec_analog_of_match,
683 	},
684 	.probe = sun8i_codec_analog_probe,
685 };
686 module_platform_driver(sun8i_codec_analog_driver);
687 
688 MODULE_DESCRIPTION("Allwinner internal codec analog controls driver");
689 MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
690 MODULE_LICENSE("GPL");
691 MODULE_ALIAS("platform:sun8i-codec-analog");
692