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

source code of linux/sound/soc/sunxi/sun50i-codec-analog.c