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

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

Version: ~ [ linux-5.5-rc7 ] ~ [ linux-5.4.13 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.97 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.166 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.210 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.210 ] ~ [ 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.81 ] ~ [ 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-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ 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 #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");
866 

~ [ 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