1 // SPDX-License-Identifier: GPL-2.0+
3 * This driver supports the analog controls for the internal codec
4 * found in Allwinner's A64 SoC.
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>
10 * Based on sun8i-codec-analog.c
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>
21 #include <sound/soc.h>
22 #include <sound/soc-dapm.h>
23 #include <sound/tlv.h>
25 #include "sun8i-adda-pr-regmap.h"
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
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
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
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
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
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
66 #define SUN50I_ADDA_LINEOUT_CTRL1 0x06
67 #define SUN50I_ADDA_LINEOUT_CTRL1_VOL 0
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
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
79 #define SUN50I_ADDA_LINEIN_CTRL 0x09
80 #define SUN50I_ADDA_LINEIN_CTRL_LINEING 0
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
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
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
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
115 #define SUN50I_ADDA_HS_MBIAS_CTRL 0x0e
116 #define SUN50I_ADDA_HS_MBIAS_CTRL_MMICBIASEN 7
118 #define SUN50I_ADDA_MDET_CTRL 0x1c
119 #define SUN50I_ADDA_MDET_CTRL_SELDETADC_FS 4
120 #define SUN50I_ADDA_MDET_CTRL_SELDETADC_DB 2
121 #define SUN50I_ADDA_MDET_CTRL_SELDETADC_BF 0
123 #define SUN50I_ADDA_JACK_MIC_CTRL 0x1d
124 #define SUN50I_ADDA_JACK_MIC_CTRL_JACKDETEN 7
125 #define SUN50I_ADDA_JACK_MIC_CTRL_INNERRESEN 6
126 #define SUN50I_ADDA_JACK_MIC_CTRL_HMICBIASEN 5
127 #define SUN50I_ADDA_JACK_MIC_CTRL_MICADCEN 4
130 static const struct snd_kcontrol_new sun50i_a64_codec_mixer_controls
[] = {
131 SOC_DAPM_DOUBLE_R("Mic1 Playback Switch",
132 SUN50I_ADDA_OL_MIX_CTRL
,
133 SUN50I_ADDA_OR_MIX_CTRL
,
134 SUN50I_ADDA_OL_MIX_CTRL_MIC1
, 1, 0),
135 SOC_DAPM_DOUBLE_R("Mic2 Playback Switch",
136 SUN50I_ADDA_OL_MIX_CTRL
,
137 SUN50I_ADDA_OR_MIX_CTRL
,
138 SUN50I_ADDA_OL_MIX_CTRL_MIC2
, 1, 0),
139 SOC_DAPM_DOUBLE_R("Line In Playback Switch",
140 SUN50I_ADDA_OL_MIX_CTRL
,
141 SUN50I_ADDA_OR_MIX_CTRL
,
142 SUN50I_ADDA_OL_MIX_CTRL_LINEINL
, 1, 0),
143 SOC_DAPM_DOUBLE_R("DAC Playback Switch",
144 SUN50I_ADDA_OL_MIX_CTRL
,
145 SUN50I_ADDA_OR_MIX_CTRL
,
146 SUN50I_ADDA_OL_MIX_CTRL_DACL
, 1, 0),
147 SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch",
148 SUN50I_ADDA_OL_MIX_CTRL
,
149 SUN50I_ADDA_OR_MIX_CTRL
,
150 SUN50I_ADDA_OL_MIX_CTRL_DACR
, 1, 0),
153 /* ADC mixer controls */
154 static const struct snd_kcontrol_new sun50i_codec_adc_mixer_controls
[] = {
155 SOC_DAPM_DOUBLE_R("Mic1 Capture Switch",
156 SUN50I_ADDA_L_ADCMIX_SRC
,
157 SUN50I_ADDA_R_ADCMIX_SRC
,
158 SUN50I_ADDA_L_ADCMIX_SRC_MIC1
, 1, 0),
159 SOC_DAPM_DOUBLE_R("Mic2 Capture Switch",
160 SUN50I_ADDA_L_ADCMIX_SRC
,
161 SUN50I_ADDA_R_ADCMIX_SRC
,
162 SUN50I_ADDA_L_ADCMIX_SRC_MIC2
, 1, 0),
163 SOC_DAPM_DOUBLE_R("Line In Capture Switch",
164 SUN50I_ADDA_L_ADCMIX_SRC
,
165 SUN50I_ADDA_R_ADCMIX_SRC
,
166 SUN50I_ADDA_L_ADCMIX_SRC_LINEINL
, 1, 0),
167 SOC_DAPM_DOUBLE_R("Mixer Capture Switch",
168 SUN50I_ADDA_L_ADCMIX_SRC
,
169 SUN50I_ADDA_R_ADCMIX_SRC
,
170 SUN50I_ADDA_L_ADCMIX_SRC_OMIXRL
, 1, 0),
171 SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch",
172 SUN50I_ADDA_L_ADCMIX_SRC
,
173 SUN50I_ADDA_R_ADCMIX_SRC
,
174 SUN50I_ADDA_L_ADCMIX_SRC_OMIXRR
, 1, 0),
177 static const DECLARE_TLV_DB_SCALE(sun50i_codec_out_mixer_pregain_scale
,
179 static const DECLARE_TLV_DB_RANGE(sun50i_codec_mic_gain_scale
,
180 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
181 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
184 static const DECLARE_TLV_DB_SCALE(sun50i_codec_hp_vol_scale
, -6300, 100, 1);
186 static const DECLARE_TLV_DB_RANGE(sun50i_codec_lineout_vol_scale
,
187 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE
, 0, 1),
188 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),
191 static const DECLARE_TLV_DB_RANGE(sun50i_codec_earpiece_vol_scale
,
192 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE
, 0, 1),
193 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),
196 /* volume / mute controls */
197 static const struct snd_kcontrol_new sun50i_a64_codec_controls
[] = {
198 SOC_SINGLE_TLV("Headphone Playback Volume",
200 SUN50I_ADDA_HP_CTRL_HPVOL
, 0x3f, 0,
201 sun50i_codec_hp_vol_scale
),
204 SOC_SINGLE_TLV("Mic1 Playback Volume", SUN50I_ADDA_MIC1_CTRL
,
205 SUN50I_ADDA_MIC1_CTRL_MIC1G
,
206 0x7, 0, sun50i_codec_out_mixer_pregain_scale
),
208 /* Microphone Amp boost gain */
209 SOC_SINGLE_TLV("Mic1 Boost Volume", SUN50I_ADDA_MIC1_CTRL
,
210 SUN50I_ADDA_MIC1_CTRL_MIC1BOOST
, 0x7, 0,
211 sun50i_codec_mic_gain_scale
),
214 SOC_SINGLE_TLV("Mic2 Playback Volume",
215 SUN50I_ADDA_MIC2_CTRL
, SUN50I_ADDA_MIC2_CTRL_MIC2G
,
216 0x7, 0, sun50i_codec_out_mixer_pregain_scale
),
218 /* Microphone Amp boost gain */
219 SOC_SINGLE_TLV("Mic2 Boost Volume", SUN50I_ADDA_MIC2_CTRL
,
220 SUN50I_ADDA_MIC2_CTRL_MIC2BOOST
, 0x7, 0,
221 sun50i_codec_mic_gain_scale
),
224 SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN50I_ADDA_ADC_CTRL
,
225 SUN50I_ADDA_ADC_CTRL_ADCG
, 0x7, 0,
226 sun50i_codec_out_mixer_pregain_scale
),
229 SOC_SINGLE_TLV("Line In Playback Volume", SUN50I_ADDA_LINEIN_CTRL
,
230 SUN50I_ADDA_LINEIN_CTRL_LINEING
,
231 0x7, 0, sun50i_codec_out_mixer_pregain_scale
),
233 SOC_SINGLE_TLV("Line Out Playback Volume",
234 SUN50I_ADDA_LINEOUT_CTRL1
,
235 SUN50I_ADDA_LINEOUT_CTRL1_VOL
, 0x1f, 0,
236 sun50i_codec_lineout_vol_scale
),
238 SOC_SINGLE_TLV("Earpiece Playback Volume",
239 SUN50I_ADDA_EARPIECE_CTRL1
,
240 SUN50I_ADDA_EARPIECE_CTRL1_ESP_VOL
, 0x1f, 0,
241 sun50i_codec_earpiece_vol_scale
),
244 static const char * const sun50i_codec_hp_src_enum_text
[] = {
248 static SOC_ENUM_DOUBLE_DECL(sun50i_codec_hp_src_enum
,
249 SUN50I_ADDA_MIX_DAC_CTRL
,
250 SUN50I_ADDA_MIX_DAC_CTRL_LHPIS
,
251 SUN50I_ADDA_MIX_DAC_CTRL_RHPIS
,
252 sun50i_codec_hp_src_enum_text
);
254 static const struct snd_kcontrol_new sun50i_codec_hp_src
[] = {
255 SOC_DAPM_ENUM("Headphone Source Playback Route",
256 sun50i_codec_hp_src_enum
),
259 static const struct snd_kcontrol_new sun50i_codec_hp_switch
=
260 SOC_DAPM_DOUBLE("Headphone Playback Switch",
261 SUN50I_ADDA_MIX_DAC_CTRL
,
262 SUN50I_ADDA_MIX_DAC_CTRL_LHPPAMUTE
,
263 SUN50I_ADDA_MIX_DAC_CTRL_RHPPAMUTE
, 1, 0);
265 static const char * const sun50i_codec_lineout_src_enum_text
[] = {
266 "Stereo", "Mono Differential",
269 static SOC_ENUM_DOUBLE_DECL(sun50i_codec_lineout_src_enum
,
270 SUN50I_ADDA_LINEOUT_CTRL0
,
271 SUN50I_ADDA_LINEOUT_CTRL0_LSRC_SEL
,
272 SUN50I_ADDA_LINEOUT_CTRL0_RSRC_SEL
,
273 sun50i_codec_lineout_src_enum_text
);
275 static const struct snd_kcontrol_new sun50i_codec_lineout_src
[] = {
276 SOC_DAPM_ENUM("Line Out Source Playback Route",
277 sun50i_codec_lineout_src_enum
),
280 static const struct snd_kcontrol_new sun50i_codec_lineout_switch
=
281 SOC_DAPM_DOUBLE("Line Out Playback Switch",
282 SUN50I_ADDA_LINEOUT_CTRL0
,
283 SUN50I_ADDA_LINEOUT_CTRL0_LEN
,
284 SUN50I_ADDA_LINEOUT_CTRL0_REN
, 1, 0);
286 static const char * const sun50i_codec_earpiece_src_enum_text
[] = {
287 "DACR", "DACL", "Right Mixer", "Left Mixer",
290 static SOC_ENUM_SINGLE_DECL(sun50i_codec_earpiece_src_enum
,
291 SUN50I_ADDA_EARPIECE_CTRL0
,
292 SUN50I_ADDA_EARPIECE_CTRL0_ESPSR
,
293 sun50i_codec_earpiece_src_enum_text
);
295 static const struct snd_kcontrol_new sun50i_codec_earpiece_src
[] = {
296 SOC_DAPM_ENUM("Earpiece Source Playback Route",
297 sun50i_codec_earpiece_src_enum
),
300 static const struct snd_kcontrol_new sun50i_codec_earpiece_switch
[] = {
301 SOC_DAPM_SINGLE("Earpiece Playback Switch",
302 SUN50I_ADDA_EARPIECE_CTRL1
,
303 SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_MUTE
, 1, 0),
306 static int sun50i_codec_hbias_event(struct snd_soc_dapm_widget
*w
,
307 struct snd_kcontrol
*kcontrol
, int event
)
309 struct snd_soc_component
*component
= snd_soc_dapm_to_component(w
->dapm
);
310 u32 value
= !!SND_SOC_DAPM_EVENT_ON(event
);
312 regmap_update_bits(component
->regmap
, SUN50I_ADDA_JACK_MIC_CTRL
,
313 BIT(SUN50I_ADDA_JACK_MIC_CTRL_MICADCEN
),
314 value
<< SUN50I_ADDA_JACK_MIC_CTRL_MICADCEN
);
319 static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets
[] = {
321 SND_SOC_DAPM_DAC("Left DAC", NULL
, SUN50I_ADDA_MIX_DAC_CTRL
,
322 SUN50I_ADDA_MIX_DAC_CTRL_DACALEN
, 0),
323 SND_SOC_DAPM_DAC("Right DAC", NULL
, SUN50I_ADDA_MIX_DAC_CTRL
,
324 SUN50I_ADDA_MIX_DAC_CTRL_DACAREN
, 0),
326 SND_SOC_DAPM_ADC("Left ADC", NULL
, SUN50I_ADDA_ADC_CTRL
,
327 SUN50I_ADDA_ADC_CTRL_ADCLEN
, 0),
328 SND_SOC_DAPM_ADC("Right ADC", NULL
, SUN50I_ADDA_ADC_CTRL
,
329 SUN50I_ADDA_ADC_CTRL_ADCREN
, 0),
331 * Due to this component and the codec belonging to separate DAPM
332 * contexts, we need to manually link the above widgets to their
333 * stream widgets at the card level.
336 SND_SOC_DAPM_REGULATOR_SUPPLY("cpvdd", 0, 0),
337 SND_SOC_DAPM_MUX("Left Headphone Source",
338 SND_SOC_NOPM
, 0, 0, sun50i_codec_hp_src
),
339 SND_SOC_DAPM_MUX("Right Headphone Source",
340 SND_SOC_NOPM
, 0, 0, sun50i_codec_hp_src
),
341 SND_SOC_DAPM_SWITCH("Left Headphone Switch",
342 SND_SOC_NOPM
, 0, 0, &sun50i_codec_hp_switch
),
343 SND_SOC_DAPM_SWITCH("Right Headphone Switch",
344 SND_SOC_NOPM
, 0, 0, &sun50i_codec_hp_switch
),
345 SND_SOC_DAPM_OUT_DRV("Left Headphone Amp",
346 SND_SOC_NOPM
, 0, 0, NULL
, 0),
347 SND_SOC_DAPM_OUT_DRV("Right Headphone Amp",
348 SND_SOC_NOPM
, 0, 0, NULL
, 0),
349 SND_SOC_DAPM_SUPPLY("Headphone Amp", SUN50I_ADDA_HP_CTRL
,
350 SUN50I_ADDA_HP_CTRL_HPPA_EN
, 0, NULL
, 0),
351 SND_SOC_DAPM_OUTPUT("HP"),
353 SND_SOC_DAPM_MUX("Left Line Out Source",
354 SND_SOC_NOPM
, 0, 0, sun50i_codec_lineout_src
),
355 SND_SOC_DAPM_MUX("Right Line Out Source",
356 SND_SOC_NOPM
, 0, 0, sun50i_codec_lineout_src
),
357 SND_SOC_DAPM_SWITCH("Left Line Out Switch",
358 SND_SOC_NOPM
, 0, 0, &sun50i_codec_lineout_switch
),
359 SND_SOC_DAPM_SWITCH("Right Line Out Switch",
360 SND_SOC_NOPM
, 0, 0, &sun50i_codec_lineout_switch
),
361 SND_SOC_DAPM_OUTPUT("LINEOUT"),
363 SND_SOC_DAPM_MUX("Earpiece Source Playback Route",
364 SND_SOC_NOPM
, 0, 0, sun50i_codec_earpiece_src
),
365 SOC_MIXER_NAMED_CTL_ARRAY("Earpiece Switch",
367 sun50i_codec_earpiece_switch
),
368 SND_SOC_DAPM_OUT_DRV("Earpiece Amp", SUN50I_ADDA_EARPIECE_CTRL1
,
369 SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_EN
, 0, NULL
, 0),
370 SND_SOC_DAPM_OUTPUT("EARPIECE"),
372 /* Microphone inputs */
373 SND_SOC_DAPM_INPUT("MIC1"),
375 /* Microphone Bias */
376 SND_SOC_DAPM_SUPPLY("MBIAS", SUN50I_ADDA_HS_MBIAS_CTRL
,
377 SUN50I_ADDA_HS_MBIAS_CTRL_MMICBIASEN
,
381 SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN50I_ADDA_MIC1_CTRL
,
382 SUN50I_ADDA_MIC1_CTRL_MIC1AMPEN
, 0, NULL
, 0),
384 /* Microphone input */
385 SND_SOC_DAPM_INPUT("MIC2"),
387 /* Microphone Bias */
388 SND_SOC_DAPM_SUPPLY("HBIAS", SUN50I_ADDA_JACK_MIC_CTRL
,
389 SUN50I_ADDA_JACK_MIC_CTRL_HMICBIASEN
,
390 0, sun50i_codec_hbias_event
,
391 SND_SOC_DAPM_POST_PMU
| SND_SOC_DAPM_PRE_PMD
),
394 SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN50I_ADDA_MIC2_CTRL
,
395 SUN50I_ADDA_MIC2_CTRL_MIC2AMPEN
, 0, NULL
, 0),
398 SND_SOC_DAPM_INPUT("LINEIN"),
401 SND_SOC_DAPM_MIXER("Left Mixer", SUN50I_ADDA_MIX_DAC_CTRL
,
402 SUN50I_ADDA_MIX_DAC_CTRL_LMIXEN
, 0,
403 sun50i_a64_codec_mixer_controls
,
404 ARRAY_SIZE(sun50i_a64_codec_mixer_controls
)),
405 SND_SOC_DAPM_MIXER("Right Mixer", SUN50I_ADDA_MIX_DAC_CTRL
,
406 SUN50I_ADDA_MIX_DAC_CTRL_RMIXEN
, 0,
407 sun50i_a64_codec_mixer_controls
,
408 ARRAY_SIZE(sun50i_a64_codec_mixer_controls
)),
409 SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM
, 0, 0,
410 sun50i_codec_adc_mixer_controls
,
411 ARRAY_SIZE(sun50i_codec_adc_mixer_controls
)),
412 SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM
, 0, 0,
413 sun50i_codec_adc_mixer_controls
,
414 ARRAY_SIZE(sun50i_codec_adc_mixer_controls
)),
417 static const struct snd_soc_dapm_route sun50i_a64_codec_routes
[] = {
418 /* Left Mixer Routes */
419 { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
420 { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
421 { "Left Mixer", "Line In Playback Switch", "LINEIN" },
422 { "Left Mixer", "DAC Playback Switch", "Left DAC" },
423 { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
425 /* Right Mixer Routes */
426 { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
427 { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
428 { "Right Mixer", "Line In Playback Switch", "LINEIN" },
429 { "Right Mixer", "DAC Playback Switch", "Right DAC" },
430 { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
432 /* Left ADC Mixer Routes */
433 { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
434 { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
435 { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
436 { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
437 { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
439 /* Right ADC Mixer Routes */
440 { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
441 { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
442 { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
443 { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
444 { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
447 { "Left ADC", NULL
, "Left ADC Mixer" },
448 { "Right ADC", NULL
, "Right ADC Mixer" },
450 /* Headphone Routes */
451 { "Left Headphone Source", "DAC", "Left DAC" },
452 { "Left Headphone Source", "Mixer", "Left Mixer" },
453 { "Left Headphone Switch", "Headphone Playback Switch", "Left Headphone Source" },
454 { "Left Headphone Amp", NULL
, "Left Headphone Switch" },
455 { "Left Headphone Amp", NULL
, "Headphone Amp" },
456 { "HP", NULL
, "Left Headphone Amp" },
458 { "Right Headphone Source", "DAC", "Right DAC" },
459 { "Right Headphone Source", "Mixer", "Right Mixer" },
460 { "Right Headphone Switch", "Headphone Playback Switch", "Right Headphone Source" },
461 { "Right Headphone Amp", NULL
, "Right Headphone Switch" },
462 { "Right Headphone Amp", NULL
, "Headphone Amp" },
463 { "HP", NULL
, "Right Headphone Amp" },
465 { "Headphone Amp", NULL
, "cpvdd" },
467 /* Microphone Routes */
468 { "Mic1 Amplifier", NULL
, "MIC1"},
470 /* Microphone Routes */
471 { "Mic2 Amplifier", NULL
, "MIC2"},
473 /* Line-out Routes */
474 { "Left Line Out Source", "Stereo", "Left Mixer" },
475 { "Left Line Out Source", "Mono Differential", "Left Mixer" },
476 { "Left Line Out Source", "Mono Differential", "Right Mixer" },
477 { "Left Line Out Switch", "Line Out Playback Switch", "Left Line Out Source" },
478 { "LINEOUT", NULL
, "Left Line Out Switch" },
480 { "Right Line Out Switch", "Line Out Playback Switch", "Right Mixer" },
481 { "Right Line Out Source", "Stereo", "Right Line Out Switch" },
482 { "Right Line Out Source", "Mono Differential", "Left Line Out Switch" },
483 { "LINEOUT", NULL
, "Right Line Out Source" },
485 /* Earpiece Routes */
486 { "Earpiece Source Playback Route", "DACL", "Left DAC" },
487 { "Earpiece Source Playback Route", "DACR", "Right DAC" },
488 { "Earpiece Source Playback Route", "Left Mixer", "Left Mixer" },
489 { "Earpiece Source Playback Route", "Right Mixer", "Right Mixer" },
490 { "Earpiece Switch", "Earpiece Playback Switch", "Earpiece Source Playback Route" },
491 { "Earpiece Amp", NULL
, "Earpiece Switch" },
492 { "EARPIECE", NULL
, "Earpiece Amp" },
495 static int sun50i_a64_codec_set_bias_level(struct snd_soc_component
*component
,
496 enum snd_soc_bias_level level
)
498 struct snd_soc_dapm_context
*dapm
= snd_soc_component_get_dapm(component
);
502 case SND_SOC_BIAS_OFF
:
503 regmap_clear_bits(component
->regmap
, SUN50I_ADDA_JACK_MIC_CTRL
,
504 BIT(SUN50I_ADDA_JACK_MIC_CTRL_JACKDETEN
) |
505 BIT(SUN50I_ADDA_JACK_MIC_CTRL_MICADCEN
));
507 regmap_set_bits(component
->regmap
, SUN50I_ADDA_HP_CTRL
,
508 BIT(SUN50I_ADDA_HP_CTRL_PA_CLK_GATE
));
510 case SND_SOC_BIAS_STANDBY
:
511 regmap_clear_bits(component
->regmap
, SUN50I_ADDA_HP_CTRL
,
512 BIT(SUN50I_ADDA_HP_CTRL_PA_CLK_GATE
));
514 hbias
= snd_soc_dapm_get_pin_status(dapm
, "HBIAS");
515 regmap_update_bits(component
->regmap
, SUN50I_ADDA_JACK_MIC_CTRL
,
516 BIT(SUN50I_ADDA_JACK_MIC_CTRL_JACKDETEN
) |
517 BIT(SUN50I_ADDA_JACK_MIC_CTRL_MICADCEN
),
518 BIT(SUN50I_ADDA_JACK_MIC_CTRL_JACKDETEN
) |
519 hbias
<< SUN50I_ADDA_JACK_MIC_CTRL_MICADCEN
);
528 static const struct snd_soc_component_driver sun50i_codec_analog_cmpnt_drv
= {
529 .controls
= sun50i_a64_codec_controls
,
530 .num_controls
= ARRAY_SIZE(sun50i_a64_codec_controls
),
531 .dapm_widgets
= sun50i_a64_codec_widgets
,
532 .num_dapm_widgets
= ARRAY_SIZE(sun50i_a64_codec_widgets
),
533 .dapm_routes
= sun50i_a64_codec_routes
,
534 .num_dapm_routes
= ARRAY_SIZE(sun50i_a64_codec_routes
),
535 .set_bias_level
= sun50i_a64_codec_set_bias_level
,
536 .idle_bias_on
= true,
537 .suspend_bias_off
= true,
540 static const struct of_device_id sun50i_codec_analog_of_match
[] = {
542 .compatible
= "allwinner,sun50i-a64-codec-analog",
546 MODULE_DEVICE_TABLE(of
, sun50i_codec_analog_of_match
);
548 static int sun50i_codec_analog_probe(struct platform_device
*pdev
)
550 struct regmap
*regmap
;
554 base
= devm_platform_ioremap_resource(pdev
, 0);
556 dev_err(&pdev
->dev
, "Failed to map the registers\n");
557 return PTR_ERR(base
);
560 regmap
= sun8i_adda_pr_regmap_init(&pdev
->dev
, base
);
561 if (IS_ERR(regmap
)) {
562 dev_err(&pdev
->dev
, "Failed to create regmap\n");
563 return PTR_ERR(regmap
);
566 enable
= device_property_read_bool(&pdev
->dev
,
567 "allwinner,internal-bias-resistor");
568 regmap_update_bits(regmap
, SUN50I_ADDA_JACK_MIC_CTRL
,
569 BIT(SUN50I_ADDA_JACK_MIC_CTRL_INNERRESEN
),
570 enable
<< SUN50I_ADDA_JACK_MIC_CTRL_INNERRESEN
);
572 /* Select sample interval of the ADC sample to 16ms */
573 regmap_update_bits(regmap
, SUN50I_ADDA_MDET_CTRL
,
574 0x7 << SUN50I_ADDA_MDET_CTRL_SELDETADC_FS
|
575 0x3 << SUN50I_ADDA_MDET_CTRL_SELDETADC_BF
,
576 0x3 << SUN50I_ADDA_MDET_CTRL_SELDETADC_FS
|
577 0x3 << SUN50I_ADDA_MDET_CTRL_SELDETADC_BF
);
579 return devm_snd_soc_register_component(&pdev
->dev
,
580 &sun50i_codec_analog_cmpnt_drv
,
584 static struct platform_driver sun50i_codec_analog_driver
= {
586 .name
= "sun50i-codec-analog",
587 .of_match_table
= sun50i_codec_analog_of_match
,
589 .probe
= sun50i_codec_analog_probe
,
591 module_platform_driver(sun50i_codec_analog_driver
);
593 MODULE_DESCRIPTION("Allwinner internal codec analog controls driver for A64");
594 MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>");
595 MODULE_LICENSE("GPL");
596 MODULE_ALIAS("platform:sun50i-codec-analog");