OSDN Git Service

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