1
2
3
4
5
6
7
8
9
10
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
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_LINEOUT_CTRL0 0x05
53#define SUN50I_ADDA_LINEOUT_CTRL0_LEN 7
54#define SUN50I_ADDA_LINEOUT_CTRL0_REN 6
55#define SUN50I_ADDA_LINEOUT_CTRL0_LSRC_SEL 5
56#define SUN50I_ADDA_LINEOUT_CTRL0_RSRC_SEL 4
57
58#define SUN50I_ADDA_LINEOUT_CTRL1 0x06
59#define SUN50I_ADDA_LINEOUT_CTRL1_VOL 0
60
61#define SUN50I_ADDA_MIC1_CTRL 0x07
62#define SUN50I_ADDA_MIC1_CTRL_MIC1G 4
63#define SUN50I_ADDA_MIC1_CTRL_MIC1AMPEN 3
64#define SUN50I_ADDA_MIC1_CTRL_MIC1BOOST 0
65
66#define SUN50I_ADDA_MIC2_CTRL 0x08
67#define SUN50I_ADDA_MIC2_CTRL_MIC2G 4
68#define SUN50I_ADDA_MIC2_CTRL_MIC2AMPEN 3
69#define SUN50I_ADDA_MIC2_CTRL_MIC2BOOST 0
70
71#define SUN50I_ADDA_LINEIN_CTRL 0x09
72#define SUN50I_ADDA_LINEIN_CTRL_LINEING 0
73
74#define SUN50I_ADDA_MIX_DAC_CTRL 0x0a
75#define SUN50I_ADDA_MIX_DAC_CTRL_DACAREN 7
76#define SUN50I_ADDA_MIX_DAC_CTRL_DACALEN 6
77#define SUN50I_ADDA_MIX_DAC_CTRL_RMIXEN 5
78#define SUN50I_ADDA_MIX_DAC_CTRL_LMIXEN 4
79#define SUN50I_ADDA_MIX_DAC_CTRL_RHPPAMUTE 3
80#define SUN50I_ADDA_MIX_DAC_CTRL_LHPPAMUTE 2
81#define SUN50I_ADDA_MIX_DAC_CTRL_RHPIS 1
82#define SUN50I_ADDA_MIX_DAC_CTRL_LHPIS 0
83
84#define SUN50I_ADDA_L_ADCMIX_SRC 0x0b
85#define SUN50I_ADDA_L_ADCMIX_SRC_MIC1 6
86#define SUN50I_ADDA_L_ADCMIX_SRC_MIC2 5
87#define SUN50I_ADDA_L_ADCMIX_SRC_PHONE 4
88#define SUN50I_ADDA_L_ADCMIX_SRC_PHONEN 3
89#define SUN50I_ADDA_L_ADCMIX_SRC_LINEINL 2
90#define SUN50I_ADDA_L_ADCMIX_SRC_OMIXRL 1
91#define SUN50I_ADDA_L_ADCMIX_SRC_OMIXRR 0
92
93#define SUN50I_ADDA_R_ADCMIX_SRC 0x0c
94#define SUN50I_ADDA_R_ADCMIX_SRC_MIC1 6
95#define SUN50I_ADDA_R_ADCMIX_SRC_MIC2 5
96#define SUN50I_ADDA_R_ADCMIX_SRC_PHONE 4
97#define SUN50I_ADDA_R_ADCMIX_SRC_PHONEP 3
98#define SUN50I_ADDA_R_ADCMIX_SRC_LINEINR 2
99#define SUN50I_ADDA_R_ADCMIX_SRC_OMIXR 1
100#define SUN50I_ADDA_R_ADCMIX_SRC_OMIXL 0
101
102#define SUN50I_ADDA_ADC_CTRL 0x0d
103#define SUN50I_ADDA_ADC_CTRL_ADCREN 7
104#define SUN50I_ADDA_ADC_CTRL_ADCLEN 6
105#define SUN50I_ADDA_ADC_CTRL_ADCG 0
106
107#define SUN50I_ADDA_HS_MBIAS_CTRL 0x0e
108#define SUN50I_ADDA_HS_MBIAS_CTRL_MMICBIASEN 7
109
110#define SUN50I_ADDA_JACK_MIC_CTRL 0x1d
111#define SUN50I_ADDA_JACK_MIC_CTRL_HMICBIASEN 5
112
113
114static const struct snd_kcontrol_new sun50i_a64_codec_mixer_controls[] = {
115 SOC_DAPM_DOUBLE_R("DAC Playback Switch",
116 SUN50I_ADDA_OL_MIX_CTRL,
117 SUN50I_ADDA_OR_MIX_CTRL,
118 SUN50I_ADDA_OL_MIX_CTRL_DACL, 1, 0),
119 SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch",
120 SUN50I_ADDA_OL_MIX_CTRL,
121 SUN50I_ADDA_OR_MIX_CTRL,
122 SUN50I_ADDA_OL_MIX_CTRL_DACR, 1, 0),
123 SOC_DAPM_DOUBLE_R("Line In Playback Switch",
124 SUN50I_ADDA_OL_MIX_CTRL,
125 SUN50I_ADDA_OR_MIX_CTRL,
126 SUN50I_ADDA_OL_MIX_CTRL_LINEINL, 1, 0),
127 SOC_DAPM_DOUBLE_R("Mic1 Playback Switch",
128 SUN50I_ADDA_OL_MIX_CTRL,
129 SUN50I_ADDA_OR_MIX_CTRL,
130 SUN50I_ADDA_OL_MIX_CTRL_MIC1, 1, 0),
131 SOC_DAPM_DOUBLE_R("Mic2 Playback Switch",
132 SUN50I_ADDA_OL_MIX_CTRL,
133 SUN50I_ADDA_OR_MIX_CTRL,
134 SUN50I_ADDA_OL_MIX_CTRL_MIC2, 1, 0),
135};
136
137
138static const struct snd_kcontrol_new sun50i_codec_adc_mixer_controls[] = {
139 SOC_DAPM_DOUBLE_R("Mixer Capture Switch",
140 SUN50I_ADDA_L_ADCMIX_SRC,
141 SUN50I_ADDA_R_ADCMIX_SRC,
142 SUN50I_ADDA_L_ADCMIX_SRC_OMIXRL, 1, 0),
143 SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch",
144 SUN50I_ADDA_L_ADCMIX_SRC,
145 SUN50I_ADDA_R_ADCMIX_SRC,
146 SUN50I_ADDA_L_ADCMIX_SRC_OMIXRR, 1, 0),
147 SOC_DAPM_DOUBLE_R("Line In Capture Switch",
148 SUN50I_ADDA_L_ADCMIX_SRC,
149 SUN50I_ADDA_R_ADCMIX_SRC,
150 SUN50I_ADDA_L_ADCMIX_SRC_LINEINL, 1, 0),
151 SOC_DAPM_DOUBLE_R("Mic1 Capture Switch",
152 SUN50I_ADDA_L_ADCMIX_SRC,
153 SUN50I_ADDA_R_ADCMIX_SRC,
154 SUN50I_ADDA_L_ADCMIX_SRC_MIC1, 1, 0),
155 SOC_DAPM_DOUBLE_R("Mic2 Capture Switch",
156 SUN50I_ADDA_L_ADCMIX_SRC,
157 SUN50I_ADDA_R_ADCMIX_SRC,
158 SUN50I_ADDA_L_ADCMIX_SRC_MIC2, 1, 0),
159};
160
161static const DECLARE_TLV_DB_SCALE(sun50i_codec_out_mixer_pregain_scale,
162 -450, 150, 0);
163static const DECLARE_TLV_DB_RANGE(sun50i_codec_mic_gain_scale,
164 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
165 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
166);
167
168static const DECLARE_TLV_DB_SCALE(sun50i_codec_hp_vol_scale, -6300, 100, 1);
169
170static const DECLARE_TLV_DB_RANGE(sun50i_codec_lineout_vol_scale,
171 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
172 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),
173);
174
175
176
177static const struct snd_kcontrol_new sun50i_a64_codec_controls[] = {
178 SOC_SINGLE_TLV("Headphone Playback Volume",
179 SUN50I_ADDA_HP_CTRL,
180 SUN50I_ADDA_HP_CTRL_HPVOL, 0x3f, 0,
181 sun50i_codec_hp_vol_scale),
182
183 SOC_DOUBLE("Headphone Playback Switch",
184 SUN50I_ADDA_MIX_DAC_CTRL,
185 SUN50I_ADDA_MIX_DAC_CTRL_LHPPAMUTE,
186 SUN50I_ADDA_MIX_DAC_CTRL_RHPPAMUTE, 1, 0),
187
188
189 SOC_SINGLE_TLV("Mic1 Playback Volume", SUN50I_ADDA_MIC1_CTRL,
190 SUN50I_ADDA_MIC1_CTRL_MIC1G,
191 0x7, 0, sun50i_codec_out_mixer_pregain_scale),
192
193
194 SOC_SINGLE_TLV("Mic1 Boost Volume", SUN50I_ADDA_MIC1_CTRL,
195 SUN50I_ADDA_MIC1_CTRL_MIC1BOOST, 0x7, 0,
196 sun50i_codec_mic_gain_scale),
197
198
199 SOC_SINGLE_TLV("Mic2 Playback Volume",
200 SUN50I_ADDA_MIC2_CTRL, SUN50I_ADDA_MIC2_CTRL_MIC2G,
201 0x7, 0, sun50i_codec_out_mixer_pregain_scale),
202
203
204 SOC_SINGLE_TLV("Mic2 Boost Volume", SUN50I_ADDA_MIC2_CTRL,
205 SUN50I_ADDA_MIC2_CTRL_MIC2BOOST, 0x7, 0,
206 sun50i_codec_mic_gain_scale),
207
208
209 SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN50I_ADDA_ADC_CTRL,
210 SUN50I_ADDA_ADC_CTRL_ADCG, 0x7, 0,
211 sun50i_codec_out_mixer_pregain_scale),
212
213
214 SOC_SINGLE_TLV("Line In Playback Volume", SUN50I_ADDA_LINEIN_CTRL,
215 SUN50I_ADDA_LINEIN_CTRL_LINEING,
216 0x7, 0, sun50i_codec_out_mixer_pregain_scale),
217
218 SOC_SINGLE_TLV("Line Out Playback Volume",
219 SUN50I_ADDA_LINEOUT_CTRL1,
220 SUN50I_ADDA_LINEOUT_CTRL1_VOL, 0x1f, 0,
221 sun50i_codec_lineout_vol_scale),
222
223 SOC_DOUBLE("Line Out Playback Switch",
224 SUN50I_ADDA_LINEOUT_CTRL0,
225 SUN50I_ADDA_LINEOUT_CTRL0_LEN,
226 SUN50I_ADDA_LINEOUT_CTRL0_REN, 1, 0),
227
228};
229
230static const char * const sun50i_codec_hp_src_enum_text[] = {
231 "DAC", "Mixer",
232};
233
234static SOC_ENUM_DOUBLE_DECL(sun50i_codec_hp_src_enum,
235 SUN50I_ADDA_MIX_DAC_CTRL,
236 SUN50I_ADDA_MIX_DAC_CTRL_LHPIS,
237 SUN50I_ADDA_MIX_DAC_CTRL_RHPIS,
238 sun50i_codec_hp_src_enum_text);
239
240static const struct snd_kcontrol_new sun50i_codec_hp_src[] = {
241 SOC_DAPM_ENUM("Headphone Source Playback Route",
242 sun50i_codec_hp_src_enum),
243};
244
245static const char * const sun50i_codec_lineout_src_enum_text[] = {
246 "Stereo", "Mono Differential",
247};
248
249static SOC_ENUM_DOUBLE_DECL(sun50i_codec_lineout_src_enum,
250 SUN50I_ADDA_LINEOUT_CTRL0,
251 SUN50I_ADDA_LINEOUT_CTRL0_LSRC_SEL,
252 SUN50I_ADDA_LINEOUT_CTRL0_RSRC_SEL,
253 sun50i_codec_lineout_src_enum_text);
254
255static const struct snd_kcontrol_new sun50i_codec_lineout_src[] = {
256 SOC_DAPM_ENUM("Line Out Source Playback Route",
257 sun50i_codec_lineout_src_enum),
258};
259
260static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets[] = {
261
262 SND_SOC_DAPM_DAC("Left DAC", NULL, SUN50I_ADDA_MIX_DAC_CTRL,
263 SUN50I_ADDA_MIX_DAC_CTRL_DACALEN, 0),
264 SND_SOC_DAPM_DAC("Right DAC", NULL, SUN50I_ADDA_MIX_DAC_CTRL,
265 SUN50I_ADDA_MIX_DAC_CTRL_DACAREN, 0),
266
267 SND_SOC_DAPM_ADC("Left ADC", NULL, SUN50I_ADDA_ADC_CTRL,
268 SUN50I_ADDA_ADC_CTRL_ADCLEN, 0),
269 SND_SOC_DAPM_ADC("Right ADC", NULL, SUN50I_ADDA_ADC_CTRL,
270 SUN50I_ADDA_ADC_CTRL_ADCREN, 0),
271
272
273
274
275
276
277 SND_SOC_DAPM_REGULATOR_SUPPLY("cpvdd", 0, 0),
278 SND_SOC_DAPM_MUX("Headphone Source Playback Route",
279 SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src),
280 SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN50I_ADDA_HP_CTRL,
281 SUN50I_ADDA_HP_CTRL_HPPA_EN, 0, NULL, 0),
282 SND_SOC_DAPM_OUTPUT("HP"),
283
284 SND_SOC_DAPM_MUX("Line Out Source Playback Route",
285 SND_SOC_NOPM, 0, 0, sun50i_codec_lineout_src),
286 SND_SOC_DAPM_OUTPUT("LINEOUT"),
287
288
289 SND_SOC_DAPM_INPUT("MIC1"),
290
291
292 SND_SOC_DAPM_SUPPLY("MBIAS", SUN50I_ADDA_HS_MBIAS_CTRL,
293 SUN50I_ADDA_HS_MBIAS_CTRL_MMICBIASEN,
294 0, NULL, 0),
295
296
297 SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN50I_ADDA_MIC1_CTRL,
298 SUN50I_ADDA_MIC1_CTRL_MIC1AMPEN, 0, NULL, 0),
299
300
301 SND_SOC_DAPM_INPUT("MIC2"),
302
303
304 SND_SOC_DAPM_SUPPLY("HBIAS", SUN50I_ADDA_JACK_MIC_CTRL,
305 SUN50I_ADDA_JACK_MIC_CTRL_HMICBIASEN,
306 0, NULL, 0),
307
308
309 SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN50I_ADDA_MIC2_CTRL,
310 SUN50I_ADDA_MIC2_CTRL_MIC2AMPEN, 0, NULL, 0),
311
312
313 SND_SOC_DAPM_INPUT("LINEIN"),
314
315
316 SND_SOC_DAPM_MIXER("Left Mixer", SUN50I_ADDA_MIX_DAC_CTRL,
317 SUN50I_ADDA_MIX_DAC_CTRL_LMIXEN, 0,
318 sun50i_a64_codec_mixer_controls,
319 ARRAY_SIZE(sun50i_a64_codec_mixer_controls)),
320 SND_SOC_DAPM_MIXER("Right Mixer", SUN50I_ADDA_MIX_DAC_CTRL,
321 SUN50I_ADDA_MIX_DAC_CTRL_RMIXEN, 0,
322 sun50i_a64_codec_mixer_controls,
323 ARRAY_SIZE(sun50i_a64_codec_mixer_controls)),
324 SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN50I_ADDA_ADC_CTRL,
325 SUN50I_ADDA_ADC_CTRL_ADCLEN, 0,
326 sun50i_codec_adc_mixer_controls,
327 ARRAY_SIZE(sun50i_codec_adc_mixer_controls)),
328 SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN50I_ADDA_ADC_CTRL,
329 SUN50I_ADDA_ADC_CTRL_ADCREN, 0,
330 sun50i_codec_adc_mixer_controls,
331 ARRAY_SIZE(sun50i_codec_adc_mixer_controls)),
332};
333
334static const struct snd_soc_dapm_route sun50i_a64_codec_routes[] = {
335
336 { "Left Mixer", "DAC Playback Switch", "Left DAC" },
337 { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
338 { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
339
340
341 { "Right Mixer", "DAC Playback Switch", "Right DAC" },
342 { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
343 { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
344
345
346 { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
347 { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
348 { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
349
350
351 { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
352 { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
353 { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
354
355
356 { "Left ADC", NULL, "Left ADC Mixer" },
357 { "Right ADC", NULL, "Right ADC Mixer" },
358
359
360 { "Headphone Source Playback Route", "DAC", "Left DAC" },
361 { "Headphone Source Playback Route", "DAC", "Right DAC" },
362 { "Headphone Source Playback Route", "Mixer", "Left Mixer" },
363 { "Headphone Source Playback Route", "Mixer", "Right Mixer" },
364 { "Headphone Amp", NULL, "Headphone Source Playback Route" },
365 { "Headphone Amp", NULL, "cpvdd" },
366 { "HP", NULL, "Headphone Amp" },
367
368
369 { "Mic1 Amplifier", NULL, "MIC1"},
370
371
372 { "Mic2 Amplifier", NULL, "MIC2"},
373 { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
374 { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
375 { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
376 { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
377
378
379 { "Left Mixer", "Line In Playback Switch", "LINEIN" },
380 { "Right Mixer", "Line In Playback Switch", "LINEIN" },
381 { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
382 { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
383
384
385 { "Line Out Source Playback Route", "Stereo", "Left Mixer" },
386 { "Line Out Source Playback Route", "Stereo", "Right Mixer" },
387 { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
388 { "Line Out Source Playback Route", "Mono Differential",
389 "Right Mixer" },
390 { "LINEOUT", NULL, "Line Out Source Playback Route" },
391};
392
393static const struct snd_soc_component_driver sun50i_codec_analog_cmpnt_drv = {
394 .controls = sun50i_a64_codec_controls,
395 .num_controls = ARRAY_SIZE(sun50i_a64_codec_controls),
396 .dapm_widgets = sun50i_a64_codec_widgets,
397 .num_dapm_widgets = ARRAY_SIZE(sun50i_a64_codec_widgets),
398 .dapm_routes = sun50i_a64_codec_routes,
399 .num_dapm_routes = ARRAY_SIZE(sun50i_a64_codec_routes),
400};
401
402static const struct of_device_id sun50i_codec_analog_of_match[] = {
403 {
404 .compatible = "allwinner,sun50i-a64-codec-analog",
405 },
406 {}
407};
408MODULE_DEVICE_TABLE(of, sun50i_codec_analog_of_match);
409
410static int sun50i_codec_analog_probe(struct platform_device *pdev)
411{
412 struct resource *res;
413 struct regmap *regmap;
414 void __iomem *base;
415
416 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
417 base = devm_ioremap_resource(&pdev->dev, res);
418 if (IS_ERR(base)) {
419 dev_err(&pdev->dev, "Failed to map the registers\n");
420 return PTR_ERR(base);
421 }
422
423 regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base);
424 if (IS_ERR(regmap)) {
425 dev_err(&pdev->dev, "Failed to create regmap\n");
426 return PTR_ERR(regmap);
427 }
428
429 return devm_snd_soc_register_component(&pdev->dev,
430 &sun50i_codec_analog_cmpnt_drv,
431 NULL, 0);
432}
433
434static struct platform_driver sun50i_codec_analog_driver = {
435 .driver = {
436 .name = "sun50i-codec-analog",
437 .of_match_table = sun50i_codec_analog_of_match,
438 },
439 .probe = sun50i_codec_analog_probe,
440};
441module_platform_driver(sun50i_codec_analog_driver);
442
443MODULE_DESCRIPTION("Allwinner internal codec analog controls driver for A64");
444MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>");
445MODULE_LICENSE("GPL");
446MODULE_ALIAS("platform:sun50i-codec-analog");
447