1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * This driver supports the analog controls for the internal codec
4*4882a593Smuzhiyun * found in Allwinner's A64 SoC.
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Copyright (C) 2016 Chen-Yu Tsai <wens@csie.org>
7*4882a593Smuzhiyun * Copyright (C) 2017 Marcus Cooper <codekipper@gmail.com>
8*4882a593Smuzhiyun * Copyright (C) 2018 Vasily Khoruzhick <anarsoul@gmail.com>
9*4882a593Smuzhiyun *
10*4882a593Smuzhiyun * Based on sun8i-codec-analog.c
11*4882a593Smuzhiyun *
12*4882a593Smuzhiyun */
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #include <linux/io.h>
15*4882a593Smuzhiyun #include <linux/kernel.h>
16*4882a593Smuzhiyun #include <linux/module.h>
17*4882a593Smuzhiyun #include <linux/of.h>
18*4882a593Smuzhiyun #include <linux/of_device.h>
19*4882a593Smuzhiyun #include <linux/platform_device.h>
20*4882a593Smuzhiyun #include <linux/regmap.h>
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #include <sound/soc.h>
23*4882a593Smuzhiyun #include <sound/soc-dapm.h>
24*4882a593Smuzhiyun #include <sound/tlv.h>
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun #include "sun8i-adda-pr-regmap.h"
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun /* Codec analog control register offsets and bit fields */
29*4882a593Smuzhiyun #define SUN50I_ADDA_HP_CTRL 0x00
30*4882a593Smuzhiyun #define SUN50I_ADDA_HP_CTRL_PA_CLK_GATE 7
31*4882a593Smuzhiyun #define SUN50I_ADDA_HP_CTRL_HPPA_EN 6
32*4882a593Smuzhiyun #define SUN50I_ADDA_HP_CTRL_HPVOL 0
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun #define SUN50I_ADDA_OL_MIX_CTRL 0x01
35*4882a593Smuzhiyun #define SUN50I_ADDA_OL_MIX_CTRL_MIC1 6
36*4882a593Smuzhiyun #define SUN50I_ADDA_OL_MIX_CTRL_MIC2 5
37*4882a593Smuzhiyun #define SUN50I_ADDA_OL_MIX_CTRL_PHONE 4
38*4882a593Smuzhiyun #define SUN50I_ADDA_OL_MIX_CTRL_PHONEN 3
39*4882a593Smuzhiyun #define SUN50I_ADDA_OL_MIX_CTRL_LINEINL 2
40*4882a593Smuzhiyun #define SUN50I_ADDA_OL_MIX_CTRL_DACL 1
41*4882a593Smuzhiyun #define SUN50I_ADDA_OL_MIX_CTRL_DACR 0
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun #define SUN50I_ADDA_OR_MIX_CTRL 0x02
44*4882a593Smuzhiyun #define SUN50I_ADDA_OR_MIX_CTRL_MIC1 6
45*4882a593Smuzhiyun #define SUN50I_ADDA_OR_MIX_CTRL_MIC2 5
46*4882a593Smuzhiyun #define SUN50I_ADDA_OR_MIX_CTRL_PHONE 4
47*4882a593Smuzhiyun #define SUN50I_ADDA_OR_MIX_CTRL_PHONEP 3
48*4882a593Smuzhiyun #define SUN50I_ADDA_OR_MIX_CTRL_LINEINR 2
49*4882a593Smuzhiyun #define SUN50I_ADDA_OR_MIX_CTRL_DACR 1
50*4882a593Smuzhiyun #define SUN50I_ADDA_OR_MIX_CTRL_DACL 0
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun #define SUN50I_ADDA_EARPIECE_CTRL0 0x03
53*4882a593Smuzhiyun #define SUN50I_ADDA_EARPIECE_CTRL0_EAR_RAMP_TIME 4
54*4882a593Smuzhiyun #define SUN50I_ADDA_EARPIECE_CTRL0_ESPSR 0
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun #define SUN50I_ADDA_EARPIECE_CTRL1 0x04
57*4882a593Smuzhiyun #define SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_EN 7
58*4882a593Smuzhiyun #define SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_MUTE 6
59*4882a593Smuzhiyun #define SUN50I_ADDA_EARPIECE_CTRL1_ESP_VOL 0
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun #define SUN50I_ADDA_LINEOUT_CTRL0 0x05
62*4882a593Smuzhiyun #define SUN50I_ADDA_LINEOUT_CTRL0_LEN 7
63*4882a593Smuzhiyun #define SUN50I_ADDA_LINEOUT_CTRL0_REN 6
64*4882a593Smuzhiyun #define SUN50I_ADDA_LINEOUT_CTRL0_LSRC_SEL 5
65*4882a593Smuzhiyun #define SUN50I_ADDA_LINEOUT_CTRL0_RSRC_SEL 4
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun #define SUN50I_ADDA_LINEOUT_CTRL1 0x06
68*4882a593Smuzhiyun #define SUN50I_ADDA_LINEOUT_CTRL1_VOL 0
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun #define SUN50I_ADDA_MIC1_CTRL 0x07
71*4882a593Smuzhiyun #define SUN50I_ADDA_MIC1_CTRL_MIC1G 4
72*4882a593Smuzhiyun #define SUN50I_ADDA_MIC1_CTRL_MIC1AMPEN 3
73*4882a593Smuzhiyun #define SUN50I_ADDA_MIC1_CTRL_MIC1BOOST 0
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun #define SUN50I_ADDA_MIC2_CTRL 0x08
76*4882a593Smuzhiyun #define SUN50I_ADDA_MIC2_CTRL_MIC2G 4
77*4882a593Smuzhiyun #define SUN50I_ADDA_MIC2_CTRL_MIC2AMPEN 3
78*4882a593Smuzhiyun #define SUN50I_ADDA_MIC2_CTRL_MIC2BOOST 0
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun #define SUN50I_ADDA_LINEIN_CTRL 0x09
81*4882a593Smuzhiyun #define SUN50I_ADDA_LINEIN_CTRL_LINEING 0
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun #define SUN50I_ADDA_MIX_DAC_CTRL 0x0a
84*4882a593Smuzhiyun #define SUN50I_ADDA_MIX_DAC_CTRL_DACAREN 7
85*4882a593Smuzhiyun #define SUN50I_ADDA_MIX_DAC_CTRL_DACALEN 6
86*4882a593Smuzhiyun #define SUN50I_ADDA_MIX_DAC_CTRL_RMIXEN 5
87*4882a593Smuzhiyun #define SUN50I_ADDA_MIX_DAC_CTRL_LMIXEN 4
88*4882a593Smuzhiyun #define SUN50I_ADDA_MIX_DAC_CTRL_RHPPAMUTE 3
89*4882a593Smuzhiyun #define SUN50I_ADDA_MIX_DAC_CTRL_LHPPAMUTE 2
90*4882a593Smuzhiyun #define SUN50I_ADDA_MIX_DAC_CTRL_RHPIS 1
91*4882a593Smuzhiyun #define SUN50I_ADDA_MIX_DAC_CTRL_LHPIS 0
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun #define SUN50I_ADDA_L_ADCMIX_SRC 0x0b
94*4882a593Smuzhiyun #define SUN50I_ADDA_L_ADCMIX_SRC_MIC1 6
95*4882a593Smuzhiyun #define SUN50I_ADDA_L_ADCMIX_SRC_MIC2 5
96*4882a593Smuzhiyun #define SUN50I_ADDA_L_ADCMIX_SRC_PHONE 4
97*4882a593Smuzhiyun #define SUN50I_ADDA_L_ADCMIX_SRC_PHONEN 3
98*4882a593Smuzhiyun #define SUN50I_ADDA_L_ADCMIX_SRC_LINEINL 2
99*4882a593Smuzhiyun #define SUN50I_ADDA_L_ADCMIX_SRC_OMIXRL 1
100*4882a593Smuzhiyun #define SUN50I_ADDA_L_ADCMIX_SRC_OMIXRR 0
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun #define SUN50I_ADDA_R_ADCMIX_SRC 0x0c
103*4882a593Smuzhiyun #define SUN50I_ADDA_R_ADCMIX_SRC_MIC1 6
104*4882a593Smuzhiyun #define SUN50I_ADDA_R_ADCMIX_SRC_MIC2 5
105*4882a593Smuzhiyun #define SUN50I_ADDA_R_ADCMIX_SRC_PHONE 4
106*4882a593Smuzhiyun #define SUN50I_ADDA_R_ADCMIX_SRC_PHONEP 3
107*4882a593Smuzhiyun #define SUN50I_ADDA_R_ADCMIX_SRC_LINEINR 2
108*4882a593Smuzhiyun #define SUN50I_ADDA_R_ADCMIX_SRC_OMIXR 1
109*4882a593Smuzhiyun #define SUN50I_ADDA_R_ADCMIX_SRC_OMIXL 0
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun #define SUN50I_ADDA_ADC_CTRL 0x0d
112*4882a593Smuzhiyun #define SUN50I_ADDA_ADC_CTRL_ADCREN 7
113*4882a593Smuzhiyun #define SUN50I_ADDA_ADC_CTRL_ADCLEN 6
114*4882a593Smuzhiyun #define SUN50I_ADDA_ADC_CTRL_ADCG 0
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun #define SUN50I_ADDA_HS_MBIAS_CTRL 0x0e
117*4882a593Smuzhiyun #define SUN50I_ADDA_HS_MBIAS_CTRL_MMICBIASEN 7
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun #define SUN50I_ADDA_JACK_MIC_CTRL 0x1d
120*4882a593Smuzhiyun #define SUN50I_ADDA_JACK_MIC_CTRL_HMICBIASEN 5
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun /* mixer controls */
123*4882a593Smuzhiyun static const struct snd_kcontrol_new sun50i_a64_codec_mixer_controls[] = {
124*4882a593Smuzhiyun SOC_DAPM_DOUBLE_R("Mic1 Playback Switch",
125*4882a593Smuzhiyun SUN50I_ADDA_OL_MIX_CTRL,
126*4882a593Smuzhiyun SUN50I_ADDA_OR_MIX_CTRL,
127*4882a593Smuzhiyun SUN50I_ADDA_OL_MIX_CTRL_MIC1, 1, 0),
128*4882a593Smuzhiyun SOC_DAPM_DOUBLE_R("Mic2 Playback Switch",
129*4882a593Smuzhiyun SUN50I_ADDA_OL_MIX_CTRL,
130*4882a593Smuzhiyun SUN50I_ADDA_OR_MIX_CTRL,
131*4882a593Smuzhiyun SUN50I_ADDA_OL_MIX_CTRL_MIC2, 1, 0),
132*4882a593Smuzhiyun SOC_DAPM_DOUBLE_R("Line In Playback Switch",
133*4882a593Smuzhiyun SUN50I_ADDA_OL_MIX_CTRL,
134*4882a593Smuzhiyun SUN50I_ADDA_OR_MIX_CTRL,
135*4882a593Smuzhiyun SUN50I_ADDA_OL_MIX_CTRL_LINEINL, 1, 0),
136*4882a593Smuzhiyun SOC_DAPM_DOUBLE_R("DAC Playback Switch",
137*4882a593Smuzhiyun SUN50I_ADDA_OL_MIX_CTRL,
138*4882a593Smuzhiyun SUN50I_ADDA_OR_MIX_CTRL,
139*4882a593Smuzhiyun SUN50I_ADDA_OL_MIX_CTRL_DACL, 1, 0),
140*4882a593Smuzhiyun SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch",
141*4882a593Smuzhiyun SUN50I_ADDA_OL_MIX_CTRL,
142*4882a593Smuzhiyun SUN50I_ADDA_OR_MIX_CTRL,
143*4882a593Smuzhiyun SUN50I_ADDA_OL_MIX_CTRL_DACR, 1, 0),
144*4882a593Smuzhiyun };
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun /* ADC mixer controls */
147*4882a593Smuzhiyun static const struct snd_kcontrol_new sun50i_codec_adc_mixer_controls[] = {
148*4882a593Smuzhiyun SOC_DAPM_DOUBLE_R("Mic1 Capture Switch",
149*4882a593Smuzhiyun SUN50I_ADDA_L_ADCMIX_SRC,
150*4882a593Smuzhiyun SUN50I_ADDA_R_ADCMIX_SRC,
151*4882a593Smuzhiyun SUN50I_ADDA_L_ADCMIX_SRC_MIC1, 1, 0),
152*4882a593Smuzhiyun SOC_DAPM_DOUBLE_R("Mic2 Capture Switch",
153*4882a593Smuzhiyun SUN50I_ADDA_L_ADCMIX_SRC,
154*4882a593Smuzhiyun SUN50I_ADDA_R_ADCMIX_SRC,
155*4882a593Smuzhiyun SUN50I_ADDA_L_ADCMIX_SRC_MIC2, 1, 0),
156*4882a593Smuzhiyun SOC_DAPM_DOUBLE_R("Line In Capture Switch",
157*4882a593Smuzhiyun SUN50I_ADDA_L_ADCMIX_SRC,
158*4882a593Smuzhiyun SUN50I_ADDA_R_ADCMIX_SRC,
159*4882a593Smuzhiyun SUN50I_ADDA_L_ADCMIX_SRC_LINEINL, 1, 0),
160*4882a593Smuzhiyun SOC_DAPM_DOUBLE_R("Mixer Capture Switch",
161*4882a593Smuzhiyun SUN50I_ADDA_L_ADCMIX_SRC,
162*4882a593Smuzhiyun SUN50I_ADDA_R_ADCMIX_SRC,
163*4882a593Smuzhiyun SUN50I_ADDA_L_ADCMIX_SRC_OMIXRL, 1, 0),
164*4882a593Smuzhiyun SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch",
165*4882a593Smuzhiyun SUN50I_ADDA_L_ADCMIX_SRC,
166*4882a593Smuzhiyun SUN50I_ADDA_R_ADCMIX_SRC,
167*4882a593Smuzhiyun SUN50I_ADDA_L_ADCMIX_SRC_OMIXRR, 1, 0),
168*4882a593Smuzhiyun };
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun static const DECLARE_TLV_DB_SCALE(sun50i_codec_out_mixer_pregain_scale,
171*4882a593Smuzhiyun -450, 150, 0);
172*4882a593Smuzhiyun static const DECLARE_TLV_DB_RANGE(sun50i_codec_mic_gain_scale,
173*4882a593Smuzhiyun 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
174*4882a593Smuzhiyun 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
175*4882a593Smuzhiyun );
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun static const DECLARE_TLV_DB_SCALE(sun50i_codec_hp_vol_scale, -6300, 100, 1);
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun static const DECLARE_TLV_DB_RANGE(sun50i_codec_lineout_vol_scale,
180*4882a593Smuzhiyun 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
181*4882a593Smuzhiyun 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),
182*4882a593Smuzhiyun );
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun static const DECLARE_TLV_DB_RANGE(sun50i_codec_earpiece_vol_scale,
185*4882a593Smuzhiyun 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
186*4882a593Smuzhiyun 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),
187*4882a593Smuzhiyun );
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun /* volume / mute controls */
190*4882a593Smuzhiyun static const struct snd_kcontrol_new sun50i_a64_codec_controls[] = {
191*4882a593Smuzhiyun SOC_SINGLE_TLV("Headphone Playback Volume",
192*4882a593Smuzhiyun SUN50I_ADDA_HP_CTRL,
193*4882a593Smuzhiyun SUN50I_ADDA_HP_CTRL_HPVOL, 0x3f, 0,
194*4882a593Smuzhiyun sun50i_codec_hp_vol_scale),
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun /* Mixer pre-gain */
197*4882a593Smuzhiyun SOC_SINGLE_TLV("Mic1 Playback Volume", SUN50I_ADDA_MIC1_CTRL,
198*4882a593Smuzhiyun SUN50I_ADDA_MIC1_CTRL_MIC1G,
199*4882a593Smuzhiyun 0x7, 0, sun50i_codec_out_mixer_pregain_scale),
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun /* Microphone Amp boost gain */
202*4882a593Smuzhiyun SOC_SINGLE_TLV("Mic1 Boost Volume", SUN50I_ADDA_MIC1_CTRL,
203*4882a593Smuzhiyun SUN50I_ADDA_MIC1_CTRL_MIC1BOOST, 0x7, 0,
204*4882a593Smuzhiyun sun50i_codec_mic_gain_scale),
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun /* Mixer pre-gain */
207*4882a593Smuzhiyun SOC_SINGLE_TLV("Mic2 Playback Volume",
208*4882a593Smuzhiyun SUN50I_ADDA_MIC2_CTRL, SUN50I_ADDA_MIC2_CTRL_MIC2G,
209*4882a593Smuzhiyun 0x7, 0, sun50i_codec_out_mixer_pregain_scale),
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun /* Microphone Amp boost gain */
212*4882a593Smuzhiyun SOC_SINGLE_TLV("Mic2 Boost Volume", SUN50I_ADDA_MIC2_CTRL,
213*4882a593Smuzhiyun SUN50I_ADDA_MIC2_CTRL_MIC2BOOST, 0x7, 0,
214*4882a593Smuzhiyun sun50i_codec_mic_gain_scale),
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun /* ADC */
217*4882a593Smuzhiyun SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN50I_ADDA_ADC_CTRL,
218*4882a593Smuzhiyun SUN50I_ADDA_ADC_CTRL_ADCG, 0x7, 0,
219*4882a593Smuzhiyun sun50i_codec_out_mixer_pregain_scale),
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun /* Mixer pre-gain */
222*4882a593Smuzhiyun SOC_SINGLE_TLV("Line In Playback Volume", SUN50I_ADDA_LINEIN_CTRL,
223*4882a593Smuzhiyun SUN50I_ADDA_LINEIN_CTRL_LINEING,
224*4882a593Smuzhiyun 0x7, 0, sun50i_codec_out_mixer_pregain_scale),
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun SOC_SINGLE_TLV("Line Out Playback Volume",
227*4882a593Smuzhiyun SUN50I_ADDA_LINEOUT_CTRL1,
228*4882a593Smuzhiyun SUN50I_ADDA_LINEOUT_CTRL1_VOL, 0x1f, 0,
229*4882a593Smuzhiyun sun50i_codec_lineout_vol_scale),
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun SOC_SINGLE_TLV("Earpiece Playback Volume",
232*4882a593Smuzhiyun SUN50I_ADDA_EARPIECE_CTRL1,
233*4882a593Smuzhiyun SUN50I_ADDA_EARPIECE_CTRL1_ESP_VOL, 0x1f, 0,
234*4882a593Smuzhiyun sun50i_codec_earpiece_vol_scale),
235*4882a593Smuzhiyun };
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun static const char * const sun50i_codec_hp_src_enum_text[] = {
238*4882a593Smuzhiyun "DAC", "Mixer",
239*4882a593Smuzhiyun };
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun static SOC_ENUM_DOUBLE_DECL(sun50i_codec_hp_src_enum,
242*4882a593Smuzhiyun SUN50I_ADDA_MIX_DAC_CTRL,
243*4882a593Smuzhiyun SUN50I_ADDA_MIX_DAC_CTRL_LHPIS,
244*4882a593Smuzhiyun SUN50I_ADDA_MIX_DAC_CTRL_RHPIS,
245*4882a593Smuzhiyun sun50i_codec_hp_src_enum_text);
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun static const struct snd_kcontrol_new sun50i_codec_hp_src[] = {
248*4882a593Smuzhiyun SOC_DAPM_ENUM("Headphone Source Playback Route",
249*4882a593Smuzhiyun sun50i_codec_hp_src_enum),
250*4882a593Smuzhiyun };
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun static const struct snd_kcontrol_new sun50i_codec_hp_switch =
253*4882a593Smuzhiyun SOC_DAPM_DOUBLE("Headphone Playback Switch",
254*4882a593Smuzhiyun SUN50I_ADDA_MIX_DAC_CTRL,
255*4882a593Smuzhiyun SUN50I_ADDA_MIX_DAC_CTRL_LHPPAMUTE,
256*4882a593Smuzhiyun SUN50I_ADDA_MIX_DAC_CTRL_RHPPAMUTE, 1, 0);
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun static const char * const sun50i_codec_lineout_src_enum_text[] = {
259*4882a593Smuzhiyun "Stereo", "Mono Differential",
260*4882a593Smuzhiyun };
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun static SOC_ENUM_DOUBLE_DECL(sun50i_codec_lineout_src_enum,
263*4882a593Smuzhiyun SUN50I_ADDA_LINEOUT_CTRL0,
264*4882a593Smuzhiyun SUN50I_ADDA_LINEOUT_CTRL0_LSRC_SEL,
265*4882a593Smuzhiyun SUN50I_ADDA_LINEOUT_CTRL0_RSRC_SEL,
266*4882a593Smuzhiyun sun50i_codec_lineout_src_enum_text);
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun static const struct snd_kcontrol_new sun50i_codec_lineout_src[] = {
269*4882a593Smuzhiyun SOC_DAPM_ENUM("Line Out Source Playback Route",
270*4882a593Smuzhiyun sun50i_codec_lineout_src_enum),
271*4882a593Smuzhiyun };
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun static const struct snd_kcontrol_new sun50i_codec_lineout_switch =
274*4882a593Smuzhiyun SOC_DAPM_DOUBLE("Line Out Playback Switch",
275*4882a593Smuzhiyun SUN50I_ADDA_LINEOUT_CTRL0,
276*4882a593Smuzhiyun SUN50I_ADDA_LINEOUT_CTRL0_LEN,
277*4882a593Smuzhiyun SUN50I_ADDA_LINEOUT_CTRL0_REN, 1, 0);
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun static const char * const sun50i_codec_earpiece_src_enum_text[] = {
280*4882a593Smuzhiyun "DACR", "DACL", "Right Mixer", "Left Mixer",
281*4882a593Smuzhiyun };
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun static SOC_ENUM_SINGLE_DECL(sun50i_codec_earpiece_src_enum,
284*4882a593Smuzhiyun SUN50I_ADDA_EARPIECE_CTRL0,
285*4882a593Smuzhiyun SUN50I_ADDA_EARPIECE_CTRL0_ESPSR,
286*4882a593Smuzhiyun sun50i_codec_earpiece_src_enum_text);
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun static const struct snd_kcontrol_new sun50i_codec_earpiece_src[] = {
289*4882a593Smuzhiyun SOC_DAPM_ENUM("Earpiece Source Playback Route",
290*4882a593Smuzhiyun sun50i_codec_earpiece_src_enum),
291*4882a593Smuzhiyun };
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun static const struct snd_kcontrol_new sun50i_codec_earpiece_switch[] = {
294*4882a593Smuzhiyun SOC_DAPM_SINGLE("Earpiece Playback Switch",
295*4882a593Smuzhiyun SUN50I_ADDA_EARPIECE_CTRL1,
296*4882a593Smuzhiyun SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_MUTE, 1, 0),
297*4882a593Smuzhiyun };
298*4882a593Smuzhiyun
299*4882a593Smuzhiyun static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets[] = {
300*4882a593Smuzhiyun /* DAC */
301*4882a593Smuzhiyun SND_SOC_DAPM_DAC("Left DAC", NULL, SUN50I_ADDA_MIX_DAC_CTRL,
302*4882a593Smuzhiyun SUN50I_ADDA_MIX_DAC_CTRL_DACALEN, 0),
303*4882a593Smuzhiyun SND_SOC_DAPM_DAC("Right DAC", NULL, SUN50I_ADDA_MIX_DAC_CTRL,
304*4882a593Smuzhiyun SUN50I_ADDA_MIX_DAC_CTRL_DACAREN, 0),
305*4882a593Smuzhiyun /* ADC */
306*4882a593Smuzhiyun SND_SOC_DAPM_ADC("Left ADC", NULL, SUN50I_ADDA_ADC_CTRL,
307*4882a593Smuzhiyun SUN50I_ADDA_ADC_CTRL_ADCLEN, 0),
308*4882a593Smuzhiyun SND_SOC_DAPM_ADC("Right ADC", NULL, SUN50I_ADDA_ADC_CTRL,
309*4882a593Smuzhiyun SUN50I_ADDA_ADC_CTRL_ADCREN, 0),
310*4882a593Smuzhiyun /*
311*4882a593Smuzhiyun * Due to this component and the codec belonging to separate DAPM
312*4882a593Smuzhiyun * contexts, we need to manually link the above widgets to their
313*4882a593Smuzhiyun * stream widgets at the card level.
314*4882a593Smuzhiyun */
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun SND_SOC_DAPM_REGULATOR_SUPPLY("cpvdd", 0, 0),
317*4882a593Smuzhiyun SND_SOC_DAPM_MUX("Left Headphone Source",
318*4882a593Smuzhiyun SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src),
319*4882a593Smuzhiyun SND_SOC_DAPM_MUX("Right Headphone Source",
320*4882a593Smuzhiyun SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src),
321*4882a593Smuzhiyun SND_SOC_DAPM_SWITCH("Left Headphone Switch",
322*4882a593Smuzhiyun SND_SOC_NOPM, 0, 0, &sun50i_codec_hp_switch),
323*4882a593Smuzhiyun SND_SOC_DAPM_SWITCH("Right Headphone Switch",
324*4882a593Smuzhiyun SND_SOC_NOPM, 0, 0, &sun50i_codec_hp_switch),
325*4882a593Smuzhiyun SND_SOC_DAPM_OUT_DRV("Left Headphone Amp",
326*4882a593Smuzhiyun SND_SOC_NOPM, 0, 0, NULL, 0),
327*4882a593Smuzhiyun SND_SOC_DAPM_OUT_DRV("Right Headphone Amp",
328*4882a593Smuzhiyun SND_SOC_NOPM, 0, 0, NULL, 0),
329*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY("Headphone Amp", SUN50I_ADDA_HP_CTRL,
330*4882a593Smuzhiyun SUN50I_ADDA_HP_CTRL_HPPA_EN, 0, NULL, 0),
331*4882a593Smuzhiyun SND_SOC_DAPM_OUTPUT("HP"),
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun SND_SOC_DAPM_MUX("Left Line Out Source",
334*4882a593Smuzhiyun SND_SOC_NOPM, 0, 0, sun50i_codec_lineout_src),
335*4882a593Smuzhiyun SND_SOC_DAPM_MUX("Right Line Out Source",
336*4882a593Smuzhiyun SND_SOC_NOPM, 0, 0, sun50i_codec_lineout_src),
337*4882a593Smuzhiyun SND_SOC_DAPM_SWITCH("Left Line Out Switch",
338*4882a593Smuzhiyun SND_SOC_NOPM, 0, 0, &sun50i_codec_lineout_switch),
339*4882a593Smuzhiyun SND_SOC_DAPM_SWITCH("Right Line Out Switch",
340*4882a593Smuzhiyun SND_SOC_NOPM, 0, 0, &sun50i_codec_lineout_switch),
341*4882a593Smuzhiyun SND_SOC_DAPM_OUTPUT("LINEOUT"),
342*4882a593Smuzhiyun
343*4882a593Smuzhiyun SND_SOC_DAPM_MUX("Earpiece Source Playback Route",
344*4882a593Smuzhiyun SND_SOC_NOPM, 0, 0, sun50i_codec_earpiece_src),
345*4882a593Smuzhiyun SOC_MIXER_NAMED_CTL_ARRAY("Earpiece Switch",
346*4882a593Smuzhiyun SND_SOC_NOPM, 0, 0,
347*4882a593Smuzhiyun sun50i_codec_earpiece_switch),
348*4882a593Smuzhiyun SND_SOC_DAPM_OUT_DRV("Earpiece Amp", SUN50I_ADDA_EARPIECE_CTRL1,
349*4882a593Smuzhiyun SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_EN, 0, NULL, 0),
350*4882a593Smuzhiyun SND_SOC_DAPM_OUTPUT("EARPIECE"),
351*4882a593Smuzhiyun
352*4882a593Smuzhiyun /* Microphone inputs */
353*4882a593Smuzhiyun SND_SOC_DAPM_INPUT("MIC1"),
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun /* Microphone Bias */
356*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY("MBIAS", SUN50I_ADDA_HS_MBIAS_CTRL,
357*4882a593Smuzhiyun SUN50I_ADDA_HS_MBIAS_CTRL_MMICBIASEN,
358*4882a593Smuzhiyun 0, NULL, 0),
359*4882a593Smuzhiyun
360*4882a593Smuzhiyun /* Mic input path */
361*4882a593Smuzhiyun SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN50I_ADDA_MIC1_CTRL,
362*4882a593Smuzhiyun SUN50I_ADDA_MIC1_CTRL_MIC1AMPEN, 0, NULL, 0),
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun /* Microphone input */
365*4882a593Smuzhiyun SND_SOC_DAPM_INPUT("MIC2"),
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun /* Microphone Bias */
368*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY("HBIAS", SUN50I_ADDA_JACK_MIC_CTRL,
369*4882a593Smuzhiyun SUN50I_ADDA_JACK_MIC_CTRL_HMICBIASEN,
370*4882a593Smuzhiyun 0, NULL, 0),
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun /* Mic input path */
373*4882a593Smuzhiyun SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN50I_ADDA_MIC2_CTRL,
374*4882a593Smuzhiyun SUN50I_ADDA_MIC2_CTRL_MIC2AMPEN, 0, NULL, 0),
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun /* Line input */
377*4882a593Smuzhiyun SND_SOC_DAPM_INPUT("LINEIN"),
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun /* Mixers */
380*4882a593Smuzhiyun SND_SOC_DAPM_MIXER("Left Mixer", SUN50I_ADDA_MIX_DAC_CTRL,
381*4882a593Smuzhiyun SUN50I_ADDA_MIX_DAC_CTRL_LMIXEN, 0,
382*4882a593Smuzhiyun sun50i_a64_codec_mixer_controls,
383*4882a593Smuzhiyun ARRAY_SIZE(sun50i_a64_codec_mixer_controls)),
384*4882a593Smuzhiyun SND_SOC_DAPM_MIXER("Right Mixer", SUN50I_ADDA_MIX_DAC_CTRL,
385*4882a593Smuzhiyun SUN50I_ADDA_MIX_DAC_CTRL_RMIXEN, 0,
386*4882a593Smuzhiyun sun50i_a64_codec_mixer_controls,
387*4882a593Smuzhiyun ARRAY_SIZE(sun50i_a64_codec_mixer_controls)),
388*4882a593Smuzhiyun SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
389*4882a593Smuzhiyun sun50i_codec_adc_mixer_controls,
390*4882a593Smuzhiyun ARRAY_SIZE(sun50i_codec_adc_mixer_controls)),
391*4882a593Smuzhiyun SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
392*4882a593Smuzhiyun sun50i_codec_adc_mixer_controls,
393*4882a593Smuzhiyun ARRAY_SIZE(sun50i_codec_adc_mixer_controls)),
394*4882a593Smuzhiyun };
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun static const struct snd_soc_dapm_route sun50i_a64_codec_routes[] = {
397*4882a593Smuzhiyun /* Left Mixer Routes */
398*4882a593Smuzhiyun { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
399*4882a593Smuzhiyun { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
400*4882a593Smuzhiyun { "Left Mixer", "Line In Playback Switch", "LINEIN" },
401*4882a593Smuzhiyun { "Left Mixer", "DAC Playback Switch", "Left DAC" },
402*4882a593Smuzhiyun { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun /* Right Mixer Routes */
405*4882a593Smuzhiyun { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
406*4882a593Smuzhiyun { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
407*4882a593Smuzhiyun { "Right Mixer", "Line In Playback Switch", "LINEIN" },
408*4882a593Smuzhiyun { "Right Mixer", "DAC Playback Switch", "Right DAC" },
409*4882a593Smuzhiyun { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
410*4882a593Smuzhiyun
411*4882a593Smuzhiyun /* Left ADC Mixer Routes */
412*4882a593Smuzhiyun { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
413*4882a593Smuzhiyun { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
414*4882a593Smuzhiyun { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
415*4882a593Smuzhiyun { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
416*4882a593Smuzhiyun { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun /* Right ADC Mixer Routes */
419*4882a593Smuzhiyun { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
420*4882a593Smuzhiyun { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
421*4882a593Smuzhiyun { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
422*4882a593Smuzhiyun { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
423*4882a593Smuzhiyun { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun /* ADC Routes */
426*4882a593Smuzhiyun { "Left ADC", NULL, "Left ADC Mixer" },
427*4882a593Smuzhiyun { "Right ADC", NULL, "Right ADC Mixer" },
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun /* Headphone Routes */
430*4882a593Smuzhiyun { "Left Headphone Source", "DAC", "Left DAC" },
431*4882a593Smuzhiyun { "Left Headphone Source", "Mixer", "Left Mixer" },
432*4882a593Smuzhiyun { "Left Headphone Switch", "Headphone Playback Switch", "Left Headphone Source" },
433*4882a593Smuzhiyun { "Left Headphone Amp", NULL, "Left Headphone Switch" },
434*4882a593Smuzhiyun { "Left Headphone Amp", NULL, "Headphone Amp" },
435*4882a593Smuzhiyun { "HP", NULL, "Left Headphone Amp" },
436*4882a593Smuzhiyun
437*4882a593Smuzhiyun { "Right Headphone Source", "DAC", "Right DAC" },
438*4882a593Smuzhiyun { "Right Headphone Source", "Mixer", "Right Mixer" },
439*4882a593Smuzhiyun { "Right Headphone Switch", "Headphone Playback Switch", "Right Headphone Source" },
440*4882a593Smuzhiyun { "Right Headphone Amp", NULL, "Right Headphone Switch" },
441*4882a593Smuzhiyun { "Right Headphone Amp", NULL, "Headphone Amp" },
442*4882a593Smuzhiyun { "HP", NULL, "Right Headphone Amp" },
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun { "Headphone Amp", NULL, "cpvdd" },
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun /* Microphone Routes */
447*4882a593Smuzhiyun { "Mic1 Amplifier", NULL, "MIC1"},
448*4882a593Smuzhiyun
449*4882a593Smuzhiyun /* Microphone Routes */
450*4882a593Smuzhiyun { "Mic2 Amplifier", NULL, "MIC2"},
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun /* Line-out Routes */
453*4882a593Smuzhiyun { "Left Line Out Source", "Stereo", "Left Mixer" },
454*4882a593Smuzhiyun { "Left Line Out Source", "Mono Differential", "Left Mixer" },
455*4882a593Smuzhiyun { "Left Line Out Source", "Mono Differential", "Right Mixer" },
456*4882a593Smuzhiyun { "Left Line Out Switch", "Line Out Playback Switch", "Left Line Out Source" },
457*4882a593Smuzhiyun { "LINEOUT", NULL, "Left Line Out Switch" },
458*4882a593Smuzhiyun
459*4882a593Smuzhiyun { "Right Line Out Switch", "Line Out Playback Switch", "Right Mixer" },
460*4882a593Smuzhiyun { "Right Line Out Source", "Stereo", "Right Line Out Switch" },
461*4882a593Smuzhiyun { "Right Line Out Source", "Mono Differential", "Left Line Out Switch" },
462*4882a593Smuzhiyun { "LINEOUT", NULL, "Right Line Out Source" },
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun /* Earpiece Routes */
465*4882a593Smuzhiyun { "Earpiece Source Playback Route", "DACL", "Left DAC" },
466*4882a593Smuzhiyun { "Earpiece Source Playback Route", "DACR", "Right DAC" },
467*4882a593Smuzhiyun { "Earpiece Source Playback Route", "Left Mixer", "Left Mixer" },
468*4882a593Smuzhiyun { "Earpiece Source Playback Route", "Right Mixer", "Right Mixer" },
469*4882a593Smuzhiyun { "Earpiece Switch", "Earpiece Playback Switch", "Earpiece Source Playback Route" },
470*4882a593Smuzhiyun { "Earpiece Amp", NULL, "Earpiece Switch" },
471*4882a593Smuzhiyun { "EARPIECE", NULL, "Earpiece Amp" },
472*4882a593Smuzhiyun };
473*4882a593Smuzhiyun
sun50i_a64_codec_suspend(struct snd_soc_component * component)474*4882a593Smuzhiyun static int sun50i_a64_codec_suspend(struct snd_soc_component *component)
475*4882a593Smuzhiyun {
476*4882a593Smuzhiyun return regmap_update_bits(component->regmap, SUN50I_ADDA_HP_CTRL,
477*4882a593Smuzhiyun BIT(SUN50I_ADDA_HP_CTRL_PA_CLK_GATE),
478*4882a593Smuzhiyun BIT(SUN50I_ADDA_HP_CTRL_PA_CLK_GATE));
479*4882a593Smuzhiyun }
480*4882a593Smuzhiyun
sun50i_a64_codec_resume(struct snd_soc_component * component)481*4882a593Smuzhiyun static int sun50i_a64_codec_resume(struct snd_soc_component *component)
482*4882a593Smuzhiyun {
483*4882a593Smuzhiyun return regmap_update_bits(component->regmap, SUN50I_ADDA_HP_CTRL,
484*4882a593Smuzhiyun BIT(SUN50I_ADDA_HP_CTRL_PA_CLK_GATE), 0);
485*4882a593Smuzhiyun }
486*4882a593Smuzhiyun
487*4882a593Smuzhiyun static const struct snd_soc_component_driver sun50i_codec_analog_cmpnt_drv = {
488*4882a593Smuzhiyun .controls = sun50i_a64_codec_controls,
489*4882a593Smuzhiyun .num_controls = ARRAY_SIZE(sun50i_a64_codec_controls),
490*4882a593Smuzhiyun .dapm_widgets = sun50i_a64_codec_widgets,
491*4882a593Smuzhiyun .num_dapm_widgets = ARRAY_SIZE(sun50i_a64_codec_widgets),
492*4882a593Smuzhiyun .dapm_routes = sun50i_a64_codec_routes,
493*4882a593Smuzhiyun .num_dapm_routes = ARRAY_SIZE(sun50i_a64_codec_routes),
494*4882a593Smuzhiyun .suspend = sun50i_a64_codec_suspend,
495*4882a593Smuzhiyun .resume = sun50i_a64_codec_resume,
496*4882a593Smuzhiyun };
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun static const struct of_device_id sun50i_codec_analog_of_match[] = {
499*4882a593Smuzhiyun {
500*4882a593Smuzhiyun .compatible = "allwinner,sun50i-a64-codec-analog",
501*4882a593Smuzhiyun },
502*4882a593Smuzhiyun {}
503*4882a593Smuzhiyun };
504*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, sun50i_codec_analog_of_match);
505*4882a593Smuzhiyun
sun50i_codec_analog_probe(struct platform_device * pdev)506*4882a593Smuzhiyun static int sun50i_codec_analog_probe(struct platform_device *pdev)
507*4882a593Smuzhiyun {
508*4882a593Smuzhiyun struct regmap *regmap;
509*4882a593Smuzhiyun void __iomem *base;
510*4882a593Smuzhiyun
511*4882a593Smuzhiyun base = devm_platform_ioremap_resource(pdev, 0);
512*4882a593Smuzhiyun if (IS_ERR(base)) {
513*4882a593Smuzhiyun dev_err(&pdev->dev, "Failed to map the registers\n");
514*4882a593Smuzhiyun return PTR_ERR(base);
515*4882a593Smuzhiyun }
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base);
518*4882a593Smuzhiyun if (IS_ERR(regmap)) {
519*4882a593Smuzhiyun dev_err(&pdev->dev, "Failed to create regmap\n");
520*4882a593Smuzhiyun return PTR_ERR(regmap);
521*4882a593Smuzhiyun }
522*4882a593Smuzhiyun
523*4882a593Smuzhiyun return devm_snd_soc_register_component(&pdev->dev,
524*4882a593Smuzhiyun &sun50i_codec_analog_cmpnt_drv,
525*4882a593Smuzhiyun NULL, 0);
526*4882a593Smuzhiyun }
527*4882a593Smuzhiyun
528*4882a593Smuzhiyun static struct platform_driver sun50i_codec_analog_driver = {
529*4882a593Smuzhiyun .driver = {
530*4882a593Smuzhiyun .name = "sun50i-codec-analog",
531*4882a593Smuzhiyun .of_match_table = sun50i_codec_analog_of_match,
532*4882a593Smuzhiyun },
533*4882a593Smuzhiyun .probe = sun50i_codec_analog_probe,
534*4882a593Smuzhiyun };
535*4882a593Smuzhiyun module_platform_driver(sun50i_codec_analog_driver);
536*4882a593Smuzhiyun
537*4882a593Smuzhiyun MODULE_DESCRIPTION("Allwinner internal codec analog controls driver for A64");
538*4882a593Smuzhiyun MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>");
539*4882a593Smuzhiyun MODULE_LICENSE("GPL");
540*4882a593Smuzhiyun MODULE_ALIAS("platform:sun50i-codec-analog");
541