~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/sunxi/sun8i-codec-analog.c

Version: ~ [ linux-5.9-rc6 ] ~ [ linux-5.8.10 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.66 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.146 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.198 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.236 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.236 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.85 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp