]>
Commit | Line | Data |
---|---|---|
c942fddf | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
ba2ff302 CYT |
2 | /* |
3 | * This driver supports the analog controls for the internal codec | |
4 | * found in Allwinner's A31s, A23, A33 and H3 SoCs. | |
5 | * | |
6 | * Copyright 2016 Chen-Yu Tsai <wens@csie.org> | |
ba2ff302 CYT |
7 | */ |
8 | ||
9 | #include <linux/io.h> | |
10 | #include <linux/kernel.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/of.h> | |
13 | #include <linux/of_device.h> | |
14 | #include <linux/platform_device.h> | |
15 | #include <linux/regmap.h> | |
16 | ||
17 | #include <sound/soc.h> | |
18 | #include <sound/soc-dapm.h> | |
19 | #include <sound/tlv.h> | |
20 | ||
55b407f6 VK |
21 | #include "sun8i-adda-pr-regmap.h" |
22 | ||
ba2ff302 CYT |
23 | /* Codec analog control register offsets and bit fields */ |
24 | #define SUN8I_ADDA_HP_VOLC 0x00 | |
25 | #define SUN8I_ADDA_HP_VOLC_PA_CLK_GATE 7 | |
26 | #define SUN8I_ADDA_HP_VOLC_HP_VOL 0 | |
27 | #define SUN8I_ADDA_LOMIXSC 0x01 | |
28 | #define SUN8I_ADDA_LOMIXSC_MIC1 6 | |
29 | #define SUN8I_ADDA_LOMIXSC_MIC2 5 | |
30 | #define SUN8I_ADDA_LOMIXSC_PHONE 4 | |
31 | #define SUN8I_ADDA_LOMIXSC_PHONEN 3 | |
32 | #define SUN8I_ADDA_LOMIXSC_LINEINL 2 | |
33 | #define SUN8I_ADDA_LOMIXSC_DACL 1 | |
34 | #define SUN8I_ADDA_LOMIXSC_DACR 0 | |
35 | #define SUN8I_ADDA_ROMIXSC 0x02 | |
36 | #define SUN8I_ADDA_ROMIXSC_MIC1 6 | |
37 | #define SUN8I_ADDA_ROMIXSC_MIC2 5 | |
38 | #define SUN8I_ADDA_ROMIXSC_PHONE 4 | |
39 | #define SUN8I_ADDA_ROMIXSC_PHONEP 3 | |
40 | #define SUN8I_ADDA_ROMIXSC_LINEINR 2 | |
41 | #define SUN8I_ADDA_ROMIXSC_DACR 1 | |
42 | #define SUN8I_ADDA_ROMIXSC_DACL 0 | |
43 | #define SUN8I_ADDA_DAC_PA_SRC 0x03 | |
44 | #define SUN8I_ADDA_DAC_PA_SRC_DACAREN 7 | |
45 | #define SUN8I_ADDA_DAC_PA_SRC_DACALEN 6 | |
46 | #define SUN8I_ADDA_DAC_PA_SRC_RMIXEN 5 | |
47 | #define SUN8I_ADDA_DAC_PA_SRC_LMIXEN 4 | |
48 | #define SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE 3 | |
49 | #define SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE 2 | |
50 | #define SUN8I_ADDA_DAC_PA_SRC_RHPIS 1 | |
51 | #define SUN8I_ADDA_DAC_PA_SRC_LHPIS 0 | |
52 | #define SUN8I_ADDA_PHONEIN_GCTRL 0x04 | |
53 | #define SUN8I_ADDA_PHONEIN_GCTRL_PHONEPG 4 | |
54 | #define SUN8I_ADDA_PHONEIN_GCTRL_PHONENG 0 | |
55 | #define SUN8I_ADDA_LINEIN_GCTRL 0x05 | |
56 | #define SUN8I_ADDA_LINEIN_GCTRL_LINEING 4 | |
57 | #define SUN8I_ADDA_LINEIN_GCTRL_PHONEG 0 | |
58 | #define SUN8I_ADDA_MICIN_GCTRL 0x06 | |
59 | #define SUN8I_ADDA_MICIN_GCTRL_MIC1G 4 | |
60 | #define SUN8I_ADDA_MICIN_GCTRL_MIC2G 0 | |
61 | #define SUN8I_ADDA_PAEN_HP_CTRL 0x07 | |
62 | #define SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN 7 | |
63 | #define SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN 7 /* H3 specific */ | |
64 | #define SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC 5 | |
65 | #define SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN 4 | |
66 | #define SUN8I_ADDA_PAEN_HP_CTRL_PA_ANTI_POP_CTRL 2 | |
67 | #define SUN8I_ADDA_PAEN_HP_CTRL_LTRNMUTE 1 | |
68 | #define SUN8I_ADDA_PAEN_HP_CTRL_RTLNMUTE 0 | |
69 | #define SUN8I_ADDA_PHONEOUT_CTRL 0x08 | |
70 | #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTG 5 | |
71 | #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTEN 4 | |
72 | #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC1 3 | |
73 | #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC2 2 | |
74 | #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_RMIX 1 | |
75 | #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_LMIX 0 | |
76 | #define SUN8I_ADDA_PHONE_GAIN_CTRL 0x09 | |
77 | #define SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL 3 | |
78 | #define SUN8I_ADDA_PHONE_GAIN_CTRL_PHONEPREG 0 | |
79 | #define SUN8I_ADDA_MIC2G_CTRL 0x0a | |
80 | #define SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN 7 | |
81 | #define SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST 4 | |
82 | #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN 3 | |
83 | #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN 2 | |
84 | #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC 1 | |
85 | #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC 0 | |
86 | #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL 0x0b | |
87 | #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN 7 | |
88 | #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN 6 | |
89 | #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIAS_MODE 5 | |
90 | #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN 3 | |
91 | #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST 0 | |
92 | #define SUN8I_ADDA_LADCMIXSC 0x0c | |
93 | #define SUN8I_ADDA_LADCMIXSC_MIC1 6 | |
94 | #define SUN8I_ADDA_LADCMIXSC_MIC2 5 | |
95 | #define SUN8I_ADDA_LADCMIXSC_PHONE 4 | |
96 | #define SUN8I_ADDA_LADCMIXSC_PHONEN 3 | |
97 | #define SUN8I_ADDA_LADCMIXSC_LINEINL 2 | |
98 | #define SUN8I_ADDA_LADCMIXSC_OMIXRL 1 | |
99 | #define SUN8I_ADDA_LADCMIXSC_OMIXRR 0 | |
100 | #define SUN8I_ADDA_RADCMIXSC 0x0d | |
101 | #define SUN8I_ADDA_RADCMIXSC_MIC1 6 | |
102 | #define SUN8I_ADDA_RADCMIXSC_MIC2 5 | |
103 | #define SUN8I_ADDA_RADCMIXSC_PHONE 4 | |
104 | #define SUN8I_ADDA_RADCMIXSC_PHONEP 3 | |
105 | #define SUN8I_ADDA_RADCMIXSC_LINEINR 2 | |
106 | #define SUN8I_ADDA_RADCMIXSC_OMIXR 1 | |
107 | #define SUN8I_ADDA_RADCMIXSC_OMIXL 0 | |
108 | #define SUN8I_ADDA_RES 0x0e | |
109 | #define SUN8I_ADDA_RES_MMICBIAS_SEL 4 | |
110 | #define SUN8I_ADDA_RES_PA_ANTI_POP_CTRL 0 | |
111 | #define SUN8I_ADDA_ADC_AP_EN 0x0f | |
112 | #define SUN8I_ADDA_ADC_AP_EN_ADCREN 7 | |
113 | #define SUN8I_ADDA_ADC_AP_EN_ADCLEN 6 | |
114 | #define SUN8I_ADDA_ADC_AP_EN_ADCG 0 | |
115 | ||
ba2ff302 CYT |
116 | /* mixer controls */ |
117 | static const struct snd_kcontrol_new sun8i_codec_mixer_controls[] = { | |
118 | SOC_DAPM_DOUBLE_R("DAC Playback Switch", | |
119 | SUN8I_ADDA_LOMIXSC, | |
120 | SUN8I_ADDA_ROMIXSC, | |
121 | SUN8I_ADDA_LOMIXSC_DACL, 1, 0), | |
122 | SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch", | |
123 | SUN8I_ADDA_LOMIXSC, | |
124 | SUN8I_ADDA_ROMIXSC, | |
125 | SUN8I_ADDA_LOMIXSC_DACR, 1, 0), | |
126 | SOC_DAPM_DOUBLE_R("Line In Playback Switch", | |
127 | SUN8I_ADDA_LOMIXSC, | |
128 | SUN8I_ADDA_ROMIXSC, | |
129 | SUN8I_ADDA_LOMIXSC_LINEINL, 1, 0), | |
130 | SOC_DAPM_DOUBLE_R("Mic1 Playback Switch", | |
131 | SUN8I_ADDA_LOMIXSC, | |
132 | SUN8I_ADDA_ROMIXSC, | |
133 | SUN8I_ADDA_LOMIXSC_MIC1, 1, 0), | |
134 | SOC_DAPM_DOUBLE_R("Mic2 Playback Switch", | |
135 | SUN8I_ADDA_LOMIXSC, | |
136 | SUN8I_ADDA_ROMIXSC, | |
137 | SUN8I_ADDA_LOMIXSC_MIC2, 1, 0), | |
138 | }; | |
139 | ||
50aadc14 IZ |
140 | /* mixer controls */ |
141 | static const struct snd_kcontrol_new sun8i_v3s_codec_mixer_controls[] = { | |
142 | SOC_DAPM_DOUBLE_R("DAC Playback Switch", | |
143 | SUN8I_ADDA_LOMIXSC, | |
144 | SUN8I_ADDA_ROMIXSC, | |
145 | SUN8I_ADDA_LOMIXSC_DACL, 1, 0), | |
146 | SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch", | |
147 | SUN8I_ADDA_LOMIXSC, | |
148 | SUN8I_ADDA_ROMIXSC, | |
149 | SUN8I_ADDA_LOMIXSC_DACR, 1, 0), | |
150 | SOC_DAPM_DOUBLE_R("Mic1 Playback Switch", | |
151 | SUN8I_ADDA_LOMIXSC, | |
152 | SUN8I_ADDA_ROMIXSC, | |
153 | SUN8I_ADDA_LOMIXSC_MIC1, 1, 0), | |
154 | }; | |
155 | ||
ba2ff302 CYT |
156 | /* ADC mixer controls */ |
157 | static const struct snd_kcontrol_new sun8i_codec_adc_mixer_controls[] = { | |
158 | SOC_DAPM_DOUBLE_R("Mixer Capture Switch", | |
159 | SUN8I_ADDA_LADCMIXSC, | |
160 | SUN8I_ADDA_RADCMIXSC, | |
161 | SUN8I_ADDA_LADCMIXSC_OMIXRL, 1, 0), | |
162 | SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch", | |
163 | SUN8I_ADDA_LADCMIXSC, | |
164 | SUN8I_ADDA_RADCMIXSC, | |
165 | SUN8I_ADDA_LADCMIXSC_OMIXRR, 1, 0), | |
166 | SOC_DAPM_DOUBLE_R("Line In Capture Switch", | |
167 | SUN8I_ADDA_LADCMIXSC, | |
168 | SUN8I_ADDA_RADCMIXSC, | |
169 | SUN8I_ADDA_LADCMIXSC_LINEINL, 1, 0), | |
170 | SOC_DAPM_DOUBLE_R("Mic1 Capture Switch", | |
171 | SUN8I_ADDA_LADCMIXSC, | |
172 | SUN8I_ADDA_RADCMIXSC, | |
173 | SUN8I_ADDA_LADCMIXSC_MIC1, 1, 0), | |
174 | SOC_DAPM_DOUBLE_R("Mic2 Capture Switch", | |
175 | SUN8I_ADDA_LADCMIXSC, | |
176 | SUN8I_ADDA_RADCMIXSC, | |
177 | SUN8I_ADDA_LADCMIXSC_MIC2, 1, 0), | |
178 | }; | |
179 | ||
50aadc14 IZ |
180 | /* ADC mixer controls */ |
181 | static const struct snd_kcontrol_new sun8i_v3s_codec_adc_mixer_controls[] = { | |
182 | SOC_DAPM_DOUBLE_R("Mixer Capture Switch", | |
183 | SUN8I_ADDA_LADCMIXSC, | |
184 | SUN8I_ADDA_RADCMIXSC, | |
185 | SUN8I_ADDA_LADCMIXSC_OMIXRL, 1, 0), | |
186 | SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch", | |
187 | SUN8I_ADDA_LADCMIXSC, | |
188 | SUN8I_ADDA_RADCMIXSC, | |
189 | SUN8I_ADDA_LADCMIXSC_OMIXRR, 1, 0), | |
190 | SOC_DAPM_DOUBLE_R("Mic1 Capture Switch", | |
191 | SUN8I_ADDA_LADCMIXSC, | |
192 | SUN8I_ADDA_RADCMIXSC, | |
193 | SUN8I_ADDA_LADCMIXSC_MIC1, 1, 0), | |
194 | }; | |
195 | ||
ba2ff302 CYT |
196 | /* volume / mute controls */ |
197 | static const DECLARE_TLV_DB_SCALE(sun8i_codec_out_mixer_pregain_scale, | |
198 | -450, 150, 0); | |
199 | static const DECLARE_TLV_DB_RANGE(sun8i_codec_mic_gain_scale, | |
200 | 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), | |
201 | 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0), | |
202 | ); | |
203 | ||
204 | static const struct snd_kcontrol_new sun8i_codec_common_controls[] = { | |
6ff4eb7e | 205 | /* Mixer pre-gain */ |
ba2ff302 CYT |
206 | SOC_SINGLE_TLV("Mic1 Playback Volume", SUN8I_ADDA_MICIN_GCTRL, |
207 | SUN8I_ADDA_MICIN_GCTRL_MIC1G, | |
208 | 0x7, 0, sun8i_codec_out_mixer_pregain_scale), | |
ba2ff302 | 209 | |
3f48947d | 210 | /* Microphone Amp boost gain */ |
ba2ff302 CYT |
211 | SOC_SINGLE_TLV("Mic1 Boost Volume", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, |
212 | SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST, 0x7, 0, | |
213 | sun8i_codec_mic_gain_scale), | |
ba2ff302 CYT |
214 | |
215 | /* ADC */ | |
216 | SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN8I_ADDA_ADC_AP_EN, | |
217 | SUN8I_ADDA_ADC_AP_EN_ADCG, 0x7, 0, | |
218 | sun8i_codec_out_mixer_pregain_scale), | |
219 | }; | |
220 | ||
221 | static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = { | |
222 | /* ADC */ | |
223 | SND_SOC_DAPM_ADC("Left ADC", NULL, SUN8I_ADDA_ADC_AP_EN, | |
224 | SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0), | |
225 | SND_SOC_DAPM_ADC("Right ADC", NULL, SUN8I_ADDA_ADC_AP_EN, | |
226 | SUN8I_ADDA_ADC_AP_EN_ADCREN, 0), | |
227 | ||
228 | /* DAC */ | |
229 | SND_SOC_DAPM_DAC("Left DAC", NULL, SUN8I_ADDA_DAC_PA_SRC, | |
230 | SUN8I_ADDA_DAC_PA_SRC_DACALEN, 0), | |
231 | SND_SOC_DAPM_DAC("Right DAC", NULL, SUN8I_ADDA_DAC_PA_SRC, | |
232 | SUN8I_ADDA_DAC_PA_SRC_DACAREN, 0), | |
233 | /* | |
234 | * Due to this component and the codec belonging to separate DAPM | |
235 | * contexts, we need to manually link the above widgets to their | |
236 | * stream widgets at the card level. | |
237 | */ | |
238 | ||
3f48947d | 239 | /* Microphone input */ |
ba2ff302 | 240 | SND_SOC_DAPM_INPUT("MIC1"), |
ba2ff302 | 241 | |
ba2ff302 CYT |
242 | /* Mic input path */ |
243 | SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, | |
244 | SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN, 0, NULL, 0), | |
50aadc14 | 245 | }; |
ba2ff302 | 246 | |
50aadc14 | 247 | static const struct snd_soc_dapm_widget sun8i_codec_mixer_widgets[] = { |
ba2ff302 CYT |
248 | SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC, |
249 | SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0, | |
250 | sun8i_codec_mixer_controls, | |
251 | ARRAY_SIZE(sun8i_codec_mixer_controls)), | |
252 | SND_SOC_DAPM_MIXER("Right Mixer", SUN8I_ADDA_DAC_PA_SRC, | |
253 | SUN8I_ADDA_DAC_PA_SRC_RMIXEN, 0, | |
254 | sun8i_codec_mixer_controls, | |
255 | ARRAY_SIZE(sun8i_codec_mixer_controls)), | |
256 | SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN8I_ADDA_ADC_AP_EN, | |
257 | SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0, | |
258 | sun8i_codec_adc_mixer_controls, | |
259 | ARRAY_SIZE(sun8i_codec_adc_mixer_controls)), | |
260 | SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN8I_ADDA_ADC_AP_EN, | |
261 | SUN8I_ADDA_ADC_AP_EN_ADCREN, 0, | |
262 | sun8i_codec_adc_mixer_controls, | |
263 | ARRAY_SIZE(sun8i_codec_adc_mixer_controls)), | |
264 | }; | |
265 | ||
50aadc14 IZ |
266 | static const struct snd_soc_dapm_widget sun8i_v3s_codec_mixer_widgets[] = { |
267 | SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC, | |
268 | SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0, | |
269 | sun8i_v3s_codec_mixer_controls, | |
270 | ARRAY_SIZE(sun8i_v3s_codec_mixer_controls)), | |
271 | SND_SOC_DAPM_MIXER("Right Mixer", SUN8I_ADDA_DAC_PA_SRC, | |
272 | SUN8I_ADDA_DAC_PA_SRC_RMIXEN, 0, | |
273 | sun8i_v3s_codec_mixer_controls, | |
274 | ARRAY_SIZE(sun8i_v3s_codec_mixer_controls)), | |
275 | SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN8I_ADDA_ADC_AP_EN, | |
276 | SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0, | |
277 | sun8i_v3s_codec_adc_mixer_controls, | |
278 | ARRAY_SIZE(sun8i_v3s_codec_adc_mixer_controls)), | |
279 | SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN8I_ADDA_ADC_AP_EN, | |
280 | SUN8I_ADDA_ADC_AP_EN_ADCREN, 0, | |
281 | sun8i_v3s_codec_adc_mixer_controls, | |
282 | ARRAY_SIZE(sun8i_v3s_codec_adc_mixer_controls)), | |
283 | }; | |
284 | ||
ba2ff302 CYT |
285 | static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = { |
286 | /* Microphone Routes */ | |
287 | { "Mic1 Amplifier", NULL, "MIC1"}, | |
50aadc14 | 288 | }; |
ba2ff302 | 289 | |
50aadc14 | 290 | static const struct snd_soc_dapm_route sun8i_codec_mixer_routes[] = { |
ba2ff302 CYT |
291 | /* Left Mixer Routes */ |
292 | { "Left Mixer", "DAC Playback Switch", "Left DAC" }, | |
293 | { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" }, | |
ba2ff302 | 294 | { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, |
ba2ff302 CYT |
295 | |
296 | /* Right Mixer Routes */ | |
297 | { "Right Mixer", "DAC Playback Switch", "Right DAC" }, | |
298 | { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" }, | |
ba2ff302 | 299 | { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, |
ba2ff302 CYT |
300 | |
301 | /* Left ADC Mixer Routes */ | |
302 | { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" }, | |
303 | { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" }, | |
ba2ff302 | 304 | { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, |
ba2ff302 CYT |
305 | |
306 | /* Right ADC Mixer Routes */ | |
307 | { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" }, | |
308 | { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" }, | |
ba2ff302 | 309 | { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, |
ba2ff302 CYT |
310 | |
311 | /* ADC Routes */ | |
312 | { "Left ADC", NULL, "Left ADC Mixer" }, | |
313 | { "Right ADC", NULL, "Right ADC Mixer" }, | |
314 | }; | |
315 | ||
316 | /* headphone specific controls, widgets, and routes */ | |
317 | static const DECLARE_TLV_DB_SCALE(sun8i_codec_hp_vol_scale, -6300, 100, 1); | |
318 | static const struct snd_kcontrol_new sun8i_codec_headphone_controls[] = { | |
319 | SOC_SINGLE_TLV("Headphone Playback Volume", | |
320 | SUN8I_ADDA_HP_VOLC, | |
321 | SUN8I_ADDA_HP_VOLC_HP_VOL, 0x3f, 0, | |
322 | sun8i_codec_hp_vol_scale), | |
323 | SOC_DOUBLE("Headphone Playback Switch", | |
324 | SUN8I_ADDA_DAC_PA_SRC, | |
325 | SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE, | |
326 | SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE, 1, 0), | |
327 | }; | |
328 | ||
329 | static const char * const sun8i_codec_hp_src_enum_text[] = { | |
330 | "DAC", "Mixer", | |
331 | }; | |
332 | ||
333 | static SOC_ENUM_DOUBLE_DECL(sun8i_codec_hp_src_enum, | |
334 | SUN8I_ADDA_DAC_PA_SRC, | |
335 | SUN8I_ADDA_DAC_PA_SRC_LHPIS, | |
336 | SUN8I_ADDA_DAC_PA_SRC_RHPIS, | |
337 | sun8i_codec_hp_src_enum_text); | |
338 | ||
339 | static const struct snd_kcontrol_new sun8i_codec_hp_src[] = { | |
340 | SOC_DAPM_ENUM("Headphone Source Playback Route", | |
341 | sun8i_codec_hp_src_enum), | |
342 | }; | |
343 | ||
bf14da7e MJ |
344 | static int sun8i_headphone_amp_event(struct snd_soc_dapm_widget *w, |
345 | struct snd_kcontrol *k, int event) | |
346 | { | |
347 | struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); | |
348 | ||
349 | if (SND_SOC_DAPM_EVENT_ON(event)) { | |
350 | snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL, | |
351 | BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN), | |
352 | BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN)); | |
353 | /* | |
354 | * Need a delay to have the amplifier up. 700ms seems the best | |
355 | * compromise between the time to let the amplifier up and the | |
356 | * time not to feel this delay while playing a sound. | |
357 | */ | |
358 | msleep(700); | |
359 | } else if (SND_SOC_DAPM_EVENT_OFF(event)) { | |
360 | snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL, | |
361 | BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN), | |
362 | 0x0); | |
363 | } | |
364 | ||
365 | return 0; | |
366 | } | |
367 | ||
ba2ff302 CYT |
368 | static const struct snd_soc_dapm_widget sun8i_codec_headphone_widgets[] = { |
369 | SND_SOC_DAPM_MUX("Headphone Source Playback Route", | |
370 | SND_SOC_NOPM, 0, 0, sun8i_codec_hp_src), | |
bf14da7e MJ |
371 | SND_SOC_DAPM_OUT_DRV_E("Headphone Amp", SUN8I_ADDA_PAEN_HP_CTRL, |
372 | SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0, NULL, 0, | |
373 | sun8i_headphone_amp_event, | |
374 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), | |
ba2ff302 CYT |
375 | SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN8I_ADDA_PAEN_HP_CTRL, |
376 | SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN, 0, NULL, 0), | |
377 | SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN8I_ADDA_PAEN_HP_CTRL, | |
378 | SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC, 0x3, 0x3, 0), | |
379 | SND_SOC_DAPM_OUTPUT("HP"), | |
380 | }; | |
381 | ||
382 | static const struct snd_soc_dapm_route sun8i_codec_headphone_routes[] = { | |
383 | { "Headphone Source Playback Route", "DAC", "Left DAC" }, | |
384 | { "Headphone Source Playback Route", "DAC", "Right DAC" }, | |
385 | { "Headphone Source Playback Route", "Mixer", "Left Mixer" }, | |
386 | { "Headphone Source Playback Route", "Mixer", "Right Mixer" }, | |
387 | { "Headphone Amp", NULL, "Headphone Source Playback Route" }, | |
388 | { "HPCOM", NULL, "HPCOM Protection" }, | |
389 | { "HP", NULL, "Headphone Amp" }, | |
390 | }; | |
391 | ||
392 | static int sun8i_codec_add_headphone(struct snd_soc_component *cmpnt) | |
393 | { | |
394 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); | |
395 | struct device *dev = cmpnt->dev; | |
396 | int ret; | |
397 | ||
398 | ret = snd_soc_add_component_controls(cmpnt, | |
399 | sun8i_codec_headphone_controls, | |
400 | ARRAY_SIZE(sun8i_codec_headphone_controls)); | |
401 | if (ret) { | |
402 | dev_err(dev, "Failed to add Headphone controls: %d\n", ret); | |
403 | return ret; | |
404 | } | |
405 | ||
406 | ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_headphone_widgets, | |
407 | ARRAY_SIZE(sun8i_codec_headphone_widgets)); | |
408 | if (ret) { | |
409 | dev_err(dev, "Failed to add Headphone DAPM widgets: %d\n", ret); | |
410 | return ret; | |
411 | } | |
412 | ||
413 | ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_headphone_routes, | |
414 | ARRAY_SIZE(sun8i_codec_headphone_routes)); | |
415 | if (ret) { | |
416 | dev_err(dev, "Failed to add Headphone DAPM routes: %d\n", ret); | |
417 | return ret; | |
418 | } | |
419 | ||
420 | return 0; | |
421 | } | |
422 | ||
b08a20f5 IZ |
423 | /* mbias specific widget */ |
424 | static const struct snd_soc_dapm_widget sun8i_codec_mbias_widgets[] = { | |
425 | SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, | |
426 | SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN, | |
427 | 0, NULL, 0), | |
428 | }; | |
429 | ||
430 | static int sun8i_codec_add_mbias(struct snd_soc_component *cmpnt) | |
431 | { | |
432 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); | |
433 | struct device *dev = cmpnt->dev; | |
434 | int ret; | |
435 | ||
436 | ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_mbias_widgets, | |
437 | ARRAY_SIZE(sun8i_codec_mbias_widgets)); | |
438 | if (ret) | |
439 | dev_err(dev, "Failed to add MBIAS DAPM widgets: %d\n", ret); | |
440 | ||
441 | return ret; | |
442 | } | |
443 | ||
ba2ff302 CYT |
444 | /* hmic specific widget */ |
445 | static const struct snd_soc_dapm_widget sun8i_codec_hmic_widgets[] = { | |
446 | SND_SOC_DAPM_SUPPLY("HBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, | |
447 | SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN, | |
448 | 0, NULL, 0), | |
449 | }; | |
450 | ||
451 | static int sun8i_codec_add_hmic(struct snd_soc_component *cmpnt) | |
452 | { | |
453 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); | |
454 | struct device *dev = cmpnt->dev; | |
455 | int ret; | |
456 | ||
457 | ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_hmic_widgets, | |
458 | ARRAY_SIZE(sun8i_codec_hmic_widgets)); | |
459 | if (ret) | |
460 | dev_err(dev, "Failed to add Mic3 DAPM widgets: %d\n", ret); | |
461 | ||
462 | return ret; | |
463 | } | |
464 | ||
6ff4eb7e IZ |
465 | /* line in specific controls, widgets and rines */ |
466 | static const struct snd_kcontrol_new sun8i_codec_linein_controls[] = { | |
467 | /* Mixer pre-gain */ | |
468 | SOC_SINGLE_TLV("Line In Playback Volume", SUN8I_ADDA_LINEIN_GCTRL, | |
469 | SUN8I_ADDA_LINEIN_GCTRL_LINEING, | |
470 | 0x7, 0, sun8i_codec_out_mixer_pregain_scale), | |
471 | }; | |
472 | ||
473 | static const struct snd_soc_dapm_widget sun8i_codec_linein_widgets[] = { | |
474 | /* Line input */ | |
475 | SND_SOC_DAPM_INPUT("LINEIN"), | |
476 | }; | |
477 | ||
478 | static const struct snd_soc_dapm_route sun8i_codec_linein_routes[] = { | |
479 | { "Left Mixer", "Line In Playback Switch", "LINEIN" }, | |
480 | ||
481 | { "Right Mixer", "Line In Playback Switch", "LINEIN" }, | |
482 | ||
483 | { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" }, | |
484 | ||
485 | { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" }, | |
486 | }; | |
487 | ||
488 | static int sun8i_codec_add_linein(struct snd_soc_component *cmpnt) | |
489 | { | |
490 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); | |
491 | struct device *dev = cmpnt->dev; | |
492 | int ret; | |
493 | ||
494 | ret = snd_soc_add_component_controls(cmpnt, | |
495 | sun8i_codec_linein_controls, | |
496 | ARRAY_SIZE(sun8i_codec_linein_controls)); | |
497 | if (ret) { | |
498 | dev_err(dev, "Failed to add Line In controls: %d\n", ret); | |
499 | return ret; | |
500 | } | |
501 | ||
502 | ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_linein_widgets, | |
503 | ARRAY_SIZE(sun8i_codec_linein_widgets)); | |
504 | if (ret) { | |
505 | dev_err(dev, "Failed to add Line In DAPM widgets: %d\n", ret); | |
506 | return ret; | |
507 | } | |
508 | ||
509 | ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_linein_routes, | |
510 | ARRAY_SIZE(sun8i_codec_linein_routes)); | |
511 | if (ret) { | |
512 | dev_err(dev, "Failed to add Line In DAPM routes: %d\n", ret); | |
513 | return ret; | |
514 | } | |
515 | ||
516 | return 0; | |
517 | } | |
518 | ||
519 | ||
ba2ff302 CYT |
520 | /* line out specific controls, widgets and routes */ |
521 | static const DECLARE_TLV_DB_RANGE(sun8i_codec_lineout_vol_scale, | |
522 | 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), | |
523 | 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0), | |
524 | ); | |
525 | static const struct snd_kcontrol_new sun8i_codec_lineout_controls[] = { | |
526 | SOC_SINGLE_TLV("Line Out Playback Volume", | |
527 | SUN8I_ADDA_PHONE_GAIN_CTRL, | |
528 | SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL, 0x1f, 0, | |
529 | sun8i_codec_lineout_vol_scale), | |
530 | SOC_DOUBLE("Line Out Playback Switch", | |
531 | SUN8I_ADDA_MIC2G_CTRL, | |
532 | SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN, | |
533 | SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN, 1, 0), | |
534 | }; | |
535 | ||
536 | static const char * const sun8i_codec_lineout_src_enum_text[] = { | |
537 | "Stereo", "Mono Differential", | |
538 | }; | |
539 | ||
540 | static SOC_ENUM_DOUBLE_DECL(sun8i_codec_lineout_src_enum, | |
541 | SUN8I_ADDA_MIC2G_CTRL, | |
542 | SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC, | |
543 | SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC, | |
544 | sun8i_codec_lineout_src_enum_text); | |
545 | ||
546 | static const struct snd_kcontrol_new sun8i_codec_lineout_src[] = { | |
547 | SOC_DAPM_ENUM("Line Out Source Playback Route", | |
548 | sun8i_codec_lineout_src_enum), | |
549 | }; | |
550 | ||
551 | static const struct snd_soc_dapm_widget sun8i_codec_lineout_widgets[] = { | |
552 | SND_SOC_DAPM_MUX("Line Out Source Playback Route", | |
553 | SND_SOC_NOPM, 0, 0, sun8i_codec_lineout_src), | |
554 | /* It is unclear if this is a buffer or gate, model it as a supply */ | |
555 | SND_SOC_DAPM_SUPPLY("Line Out Enable", SUN8I_ADDA_PAEN_HP_CTRL, | |
556 | SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN, 0, NULL, 0), | |
557 | SND_SOC_DAPM_OUTPUT("LINEOUT"), | |
558 | }; | |
559 | ||
560 | static const struct snd_soc_dapm_route sun8i_codec_lineout_routes[] = { | |
561 | { "Line Out Source Playback Route", "Stereo", "Left Mixer" }, | |
562 | { "Line Out Source Playback Route", "Stereo", "Right Mixer" }, | |
563 | { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" }, | |
564 | { "Line Out Source Playback Route", "Mono Differential", "Right Mixer" }, | |
565 | { "LINEOUT", NULL, "Line Out Source Playback Route" }, | |
566 | { "LINEOUT", NULL, "Line Out Enable", }, | |
567 | }; | |
568 | ||
569 | static int sun8i_codec_add_lineout(struct snd_soc_component *cmpnt) | |
570 | { | |
571 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); | |
572 | struct device *dev = cmpnt->dev; | |
573 | int ret; | |
574 | ||
575 | ret = snd_soc_add_component_controls(cmpnt, | |
576 | sun8i_codec_lineout_controls, | |
577 | ARRAY_SIZE(sun8i_codec_lineout_controls)); | |
578 | if (ret) { | |
579 | dev_err(dev, "Failed to add Line Out controls: %d\n", ret); | |
580 | return ret; | |
581 | } | |
582 | ||
583 | ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_lineout_widgets, | |
584 | ARRAY_SIZE(sun8i_codec_lineout_widgets)); | |
585 | if (ret) { | |
586 | dev_err(dev, "Failed to add Line Out DAPM widgets: %d\n", ret); | |
587 | return ret; | |
588 | } | |
589 | ||
590 | ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_lineout_routes, | |
591 | ARRAY_SIZE(sun8i_codec_lineout_routes)); | |
592 | if (ret) { | |
593 | dev_err(dev, "Failed to add Line Out DAPM routes: %d\n", ret); | |
594 | return ret; | |
595 | } | |
596 | ||
597 | return 0; | |
598 | } | |
599 | ||
3f48947d IZ |
600 | /* mic2 specific controls, widgets and routes */ |
601 | static const struct snd_kcontrol_new sun8i_codec_mic2_controls[] = { | |
602 | /* Mixer pre-gain */ | |
603 | SOC_SINGLE_TLV("Mic2 Playback Volume", | |
604 | SUN8I_ADDA_MICIN_GCTRL, SUN8I_ADDA_MICIN_GCTRL_MIC2G, | |
605 | 0x7, 0, sun8i_codec_out_mixer_pregain_scale), | |
606 | ||
607 | /* Microphone Amp boost gain */ | |
608 | SOC_SINGLE_TLV("Mic2 Boost Volume", SUN8I_ADDA_MIC2G_CTRL, | |
609 | SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST, 0x7, 0, | |
610 | sun8i_codec_mic_gain_scale), | |
611 | }; | |
612 | ||
613 | static const struct snd_soc_dapm_widget sun8i_codec_mic2_widgets[] = { | |
614 | /* Microphone input */ | |
615 | SND_SOC_DAPM_INPUT("MIC2"), | |
616 | ||
617 | /* Mic input path */ | |
618 | SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN8I_ADDA_MIC2G_CTRL, | |
619 | SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN, 0, NULL, 0), | |
620 | }; | |
621 | ||
622 | static const struct snd_soc_dapm_route sun8i_codec_mic2_routes[] = { | |
623 | { "Mic2 Amplifier", NULL, "MIC2"}, | |
624 | ||
625 | { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, | |
626 | ||
627 | { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, | |
628 | ||
629 | { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, | |
630 | ||
631 | { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, | |
632 | }; | |
633 | ||
634 | static int sun8i_codec_add_mic2(struct snd_soc_component *cmpnt) | |
635 | { | |
636 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); | |
637 | struct device *dev = cmpnt->dev; | |
638 | int ret; | |
639 | ||
640 | ret = snd_soc_add_component_controls(cmpnt, | |
641 | sun8i_codec_mic2_controls, | |
642 | ARRAY_SIZE(sun8i_codec_mic2_controls)); | |
643 | if (ret) { | |
644 | dev_err(dev, "Failed to add MIC2 controls: %d\n", ret); | |
645 | return ret; | |
646 | } | |
647 | ||
648 | ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_mic2_widgets, | |
649 | ARRAY_SIZE(sun8i_codec_mic2_widgets)); | |
650 | if (ret) { | |
651 | dev_err(dev, "Failed to add MIC2 DAPM widgets: %d\n", ret); | |
652 | return ret; | |
653 | } | |
654 | ||
655 | ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_mic2_routes, | |
656 | ARRAY_SIZE(sun8i_codec_mic2_routes)); | |
657 | if (ret) { | |
658 | dev_err(dev, "Failed to add MIC2 DAPM routes: %d\n", ret); | |
659 | return ret; | |
660 | } | |
661 | ||
662 | return 0; | |
663 | } | |
664 | ||
ba2ff302 CYT |
665 | struct sun8i_codec_analog_quirks { |
666 | bool has_headphone; | |
667 | bool has_hmic; | |
6ff4eb7e | 668 | bool has_linein; |
ba2ff302 | 669 | bool has_lineout; |
b08a20f5 | 670 | bool has_mbias; |
3f48947d | 671 | bool has_mic2; |
ba2ff302 CYT |
672 | }; |
673 | ||
674 | static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = { | |
675 | .has_headphone = true, | |
676 | .has_hmic = true, | |
6ff4eb7e | 677 | .has_linein = true, |
b08a20f5 | 678 | .has_mbias = true, |
3f48947d | 679 | .has_mic2 = true, |
ba2ff302 CYT |
680 | }; |
681 | ||
682 | static const struct sun8i_codec_analog_quirks sun8i_h3_quirks = { | |
6ff4eb7e | 683 | .has_linein = true, |
ba2ff302 | 684 | .has_lineout = true, |
b08a20f5 | 685 | .has_mbias = true, |
3f48947d | 686 | .has_mic2 = true, |
ba2ff302 CYT |
687 | }; |
688 | ||
50aadc14 IZ |
689 | static int sun8i_codec_analog_add_mixer(struct snd_soc_component *cmpnt, |
690 | const struct sun8i_codec_analog_quirks *quirks) | |
691 | { | |
692 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); | |
693 | struct device *dev = cmpnt->dev; | |
694 | int ret; | |
695 | ||
696 | if (!quirks->has_mic2 && !quirks->has_linein) { | |
697 | /* | |
698 | * Apply the special widget set which has uses a control | |
699 | * without MIC2 and Line In, for SoCs without these. | |
700 | * TODO: not all special cases are supported now, this case | |
701 | * is present because it's the case of V3s. | |
702 | */ | |
703 | ret = snd_soc_dapm_new_controls(dapm, | |
704 | sun8i_v3s_codec_mixer_widgets, | |
705 | ARRAY_SIZE(sun8i_v3s_codec_mixer_widgets)); | |
706 | if (ret) { | |
707 | dev_err(dev, "Failed to add V3s Mixer DAPM widgets: %d\n", ret); | |
708 | return ret; | |
709 | } | |
710 | } else { | |
711 | /* Apply the generic mixer widget set. */ | |
712 | ret = snd_soc_dapm_new_controls(dapm, | |
713 | sun8i_codec_mixer_widgets, | |
714 | ARRAY_SIZE(sun8i_codec_mixer_widgets)); | |
715 | if (ret) { | |
716 | dev_err(dev, "Failed to add Mixer DAPM widgets: %d\n", ret); | |
717 | return ret; | |
718 | } | |
719 | } | |
720 | ||
721 | ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_mixer_routes, | |
722 | ARRAY_SIZE(sun8i_codec_mixer_routes)); | |
723 | if (ret) { | |
724 | dev_err(dev, "Failed to add Mixer DAPM routes: %d\n", ret); | |
725 | return ret; | |
726 | } | |
727 | ||
728 | return 0; | |
729 | } | |
730 | ||
2cfeaec0 IZ |
731 | static const struct sun8i_codec_analog_quirks sun8i_v3s_quirks = { |
732 | .has_headphone = true, | |
733 | .has_hmic = true, | |
734 | }; | |
735 | ||
ba2ff302 CYT |
736 | static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt) |
737 | { | |
738 | struct device *dev = cmpnt->dev; | |
739 | const struct sun8i_codec_analog_quirks *quirks; | |
740 | int ret; | |
741 | ||
742 | /* | |
743 | * This would never return NULL unless someone directly registers a | |
744 | * platform device matching this driver's name, without specifying a | |
745 | * device tree node. | |
746 | */ | |
747 | quirks = of_device_get_match_data(dev); | |
748 | ||
749 | /* Add controls, widgets, and routes for individual features */ | |
50aadc14 IZ |
750 | ret = sun8i_codec_analog_add_mixer(cmpnt, quirks); |
751 | if (ret) | |
752 | return ret; | |
ba2ff302 CYT |
753 | |
754 | if (quirks->has_headphone) { | |
755 | ret = sun8i_codec_add_headphone(cmpnt); | |
756 | if (ret) | |
757 | return ret; | |
758 | } | |
759 | ||
760 | if (quirks->has_hmic) { | |
999982ef | 761 | ret = sun8i_codec_add_hmic(cmpnt); |
ba2ff302 | 762 | if (ret) |
6ff4eb7e IZ |
763 | return ret; |
764 | } | |
765 | ||
766 | if (quirks->has_linein) { | |
767 | ret = sun8i_codec_add_linein(cmpnt); | |
768 | if (ret) | |
ba2ff302 CYT |
769 | return ret; |
770 | } | |
771 | ||
772 | if (quirks->has_lineout) { | |
773 | ret = sun8i_codec_add_lineout(cmpnt); | |
774 | if (ret) | |
775 | return ret; | |
776 | } | |
777 | ||
b08a20f5 IZ |
778 | if (quirks->has_mbias) { |
779 | ret = sun8i_codec_add_mbias(cmpnt); | |
780 | if (ret) | |
781 | return ret; | |
782 | } | |
783 | ||
3f48947d IZ |
784 | if (quirks->has_mic2) { |
785 | ret = sun8i_codec_add_mic2(cmpnt); | |
786 | if (ret) | |
787 | return ret; | |
788 | } | |
789 | ||
ba2ff302 CYT |
790 | return 0; |
791 | } | |
792 | ||
793 | static const struct snd_soc_component_driver sun8i_codec_analog_cmpnt_drv = { | |
794 | .controls = sun8i_codec_common_controls, | |
795 | .num_controls = ARRAY_SIZE(sun8i_codec_common_controls), | |
796 | .dapm_widgets = sun8i_codec_common_widgets, | |
797 | .num_dapm_widgets = ARRAY_SIZE(sun8i_codec_common_widgets), | |
798 | .dapm_routes = sun8i_codec_common_routes, | |
799 | .num_dapm_routes = ARRAY_SIZE(sun8i_codec_common_routes), | |
800 | .probe = sun8i_codec_analog_cmpnt_probe, | |
801 | }; | |
802 | ||
803 | static const struct of_device_id sun8i_codec_analog_of_match[] = { | |
804 | { | |
805 | .compatible = "allwinner,sun8i-a23-codec-analog", | |
806 | .data = &sun8i_a23_quirks, | |
807 | }, | |
808 | { | |
809 | .compatible = "allwinner,sun8i-h3-codec-analog", | |
810 | .data = &sun8i_h3_quirks, | |
811 | }, | |
2cfeaec0 IZ |
812 | { |
813 | .compatible = "allwinner,sun8i-v3s-codec-analog", | |
814 | .data = &sun8i_v3s_quirks, | |
815 | }, | |
ba2ff302 CYT |
816 | {} |
817 | }; | |
818 | MODULE_DEVICE_TABLE(of, sun8i_codec_analog_of_match); | |
819 | ||
820 | static int sun8i_codec_analog_probe(struct platform_device *pdev) | |
821 | { | |
822 | struct resource *res; | |
823 | struct regmap *regmap; | |
824 | void __iomem *base; | |
825 | ||
826 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
827 | base = devm_ioremap_resource(&pdev->dev, res); | |
828 | if (IS_ERR(base)) { | |
829 | dev_err(&pdev->dev, "Failed to map the registers\n"); | |
830 | return PTR_ERR(base); | |
831 | } | |
832 | ||
55b407f6 | 833 | regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base); |
ba2ff302 CYT |
834 | if (IS_ERR(regmap)) { |
835 | dev_err(&pdev->dev, "Failed to create regmap\n"); | |
836 | return PTR_ERR(regmap); | |
837 | } | |
838 | ||
839 | return devm_snd_soc_register_component(&pdev->dev, | |
840 | &sun8i_codec_analog_cmpnt_drv, | |
841 | NULL, 0); | |
842 | } | |
843 | ||
844 | static struct platform_driver sun8i_codec_analog_driver = { | |
845 | .driver = { | |
846 | .name = "sun8i-codec-analog", | |
847 | .of_match_table = sun8i_codec_analog_of_match, | |
848 | }, | |
849 | .probe = sun8i_codec_analog_probe, | |
850 | }; | |
851 | module_platform_driver(sun8i_codec_analog_driver); | |
852 | ||
853 | MODULE_DESCRIPTION("Allwinner internal codec analog controls driver"); | |
854 | MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>"); | |
855 | MODULE_LICENSE("GPL"); | |
856 | MODULE_ALIAS("platform:sun8i-codec-analog"); |