Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * This driver supports the analog controls for the internal codec
0004  * found in Allwinner's A31s, A23, A33 and H3 SoCs.
0005  *
0006  * Copyright 2016 Chen-Yu Tsai <wens@csie.org>
0007  */
0008 
0009 #include <linux/io.h>
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/of.h>
0013 #include <linux/of_device.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/regmap.h>
0016 
0017 #include <sound/soc.h>
0018 #include <sound/soc-dapm.h>
0019 #include <sound/tlv.h>
0020 
0021 #include "sun8i-adda-pr-regmap.h"
0022 
0023 /* Codec analog control register offsets and bit fields */
0024 #define SUN8I_ADDA_HP_VOLC      0x00
0025 #define SUN8I_ADDA_HP_VOLC_PA_CLK_GATE      7
0026 #define SUN8I_ADDA_HP_VOLC_HP_VOL       0
0027 #define SUN8I_ADDA_LOMIXSC      0x01
0028 #define SUN8I_ADDA_LOMIXSC_MIC1         6
0029 #define SUN8I_ADDA_LOMIXSC_MIC2         5
0030 #define SUN8I_ADDA_LOMIXSC_PHONE        4
0031 #define SUN8I_ADDA_LOMIXSC_PHONEN       3
0032 #define SUN8I_ADDA_LOMIXSC_LINEINL      2
0033 #define SUN8I_ADDA_LOMIXSC_DACL         1
0034 #define SUN8I_ADDA_LOMIXSC_DACR         0
0035 #define SUN8I_ADDA_ROMIXSC      0x02
0036 #define SUN8I_ADDA_ROMIXSC_MIC1         6
0037 #define SUN8I_ADDA_ROMIXSC_MIC2         5
0038 #define SUN8I_ADDA_ROMIXSC_PHONE        4
0039 #define SUN8I_ADDA_ROMIXSC_PHONEP       3
0040 #define SUN8I_ADDA_ROMIXSC_LINEINR      2
0041 #define SUN8I_ADDA_ROMIXSC_DACR         1
0042 #define SUN8I_ADDA_ROMIXSC_DACL         0
0043 #define SUN8I_ADDA_DAC_PA_SRC       0x03
0044 #define SUN8I_ADDA_DAC_PA_SRC_DACAREN       7
0045 #define SUN8I_ADDA_DAC_PA_SRC_DACALEN       6
0046 #define SUN8I_ADDA_DAC_PA_SRC_RMIXEN        5
0047 #define SUN8I_ADDA_DAC_PA_SRC_LMIXEN        4
0048 #define SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE     3
0049 #define SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE     2
0050 #define SUN8I_ADDA_DAC_PA_SRC_RHPIS     1
0051 #define SUN8I_ADDA_DAC_PA_SRC_LHPIS     0
0052 #define SUN8I_ADDA_PHONEIN_GCTRL    0x04
0053 #define SUN8I_ADDA_PHONEIN_GCTRL_PHONEPG    4
0054 #define SUN8I_ADDA_PHONEIN_GCTRL_PHONENG    0
0055 #define SUN8I_ADDA_LINEIN_GCTRL     0x05
0056 #define SUN8I_ADDA_LINEIN_GCTRL_LINEING     4
0057 #define SUN8I_ADDA_LINEIN_GCTRL_PHONEG      0
0058 #define SUN8I_ADDA_MICIN_GCTRL      0x06
0059 #define SUN8I_ADDA_MICIN_GCTRL_MIC1G        4
0060 #define SUN8I_ADDA_MICIN_GCTRL_MIC2G        0
0061 #define SUN8I_ADDA_PAEN_HP_CTRL     0x07
0062 #define SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN      7
0063 #define SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN   7   /* H3 specific */
0064 #define SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC    5
0065 #define SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN     4
0066 #define SUN8I_ADDA_PAEN_HP_CTRL_PA_ANTI_POP_CTRL    2
0067 #define SUN8I_ADDA_PAEN_HP_CTRL_LTRNMUTE    1
0068 #define SUN8I_ADDA_PAEN_HP_CTRL_RTLNMUTE    0
0069 #define SUN8I_ADDA_PHONEOUT_CTRL    0x08
0070 #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTG  5
0071 #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTEN 4
0072 #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC1  3
0073 #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC2  2
0074 #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_RMIX  1
0075 #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_LMIX  0
0076 #define SUN8I_ADDA_PHONE_GAIN_CTRL  0x09
0077 #define SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL  3
0078 #define SUN8I_ADDA_PHONE_GAIN_CTRL_PHONEPREG    0
0079 #define SUN8I_ADDA_MIC2G_CTRL       0x0a
0080 #define SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN     7
0081 #define SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST     4
0082 #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN    3
0083 #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN    2
0084 #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC   1
0085 #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC   0
0086 #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL   0x0b
0087 #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN    7
0088 #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN    6
0089 #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIAS_MODE 5
0090 #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN     3
0091 #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST     0
0092 #define SUN8I_ADDA_LADCMIXSC        0x0c
0093 #define SUN8I_ADDA_LADCMIXSC_MIC1       6
0094 #define SUN8I_ADDA_LADCMIXSC_MIC2       5
0095 #define SUN8I_ADDA_LADCMIXSC_PHONE      4
0096 #define SUN8I_ADDA_LADCMIXSC_PHONEN     3
0097 #define SUN8I_ADDA_LADCMIXSC_LINEINL        2
0098 #define SUN8I_ADDA_LADCMIXSC_OMIXRL     1
0099 #define SUN8I_ADDA_LADCMIXSC_OMIXRR     0
0100 #define SUN8I_ADDA_RADCMIXSC        0x0d
0101 #define SUN8I_ADDA_RADCMIXSC_MIC1       6
0102 #define SUN8I_ADDA_RADCMIXSC_MIC2       5
0103 #define SUN8I_ADDA_RADCMIXSC_PHONE      4
0104 #define SUN8I_ADDA_RADCMIXSC_PHONEP     3
0105 #define SUN8I_ADDA_RADCMIXSC_LINEINR        2
0106 #define SUN8I_ADDA_RADCMIXSC_OMIXR      1
0107 #define SUN8I_ADDA_RADCMIXSC_OMIXL      0
0108 #define SUN8I_ADDA_RES          0x0e
0109 #define SUN8I_ADDA_RES_MMICBIAS_SEL     4
0110 #define SUN8I_ADDA_RES_PA_ANTI_POP_CTRL     0
0111 #define SUN8I_ADDA_ADC_AP_EN        0x0f
0112 #define SUN8I_ADDA_ADC_AP_EN_ADCREN     7
0113 #define SUN8I_ADDA_ADC_AP_EN_ADCLEN     6
0114 #define SUN8I_ADDA_ADC_AP_EN_ADCG       0
0115 
0116 /* mixer controls */
0117 static const struct snd_kcontrol_new sun8i_codec_mixer_controls[] = {
0118     SOC_DAPM_DOUBLE_R("DAC Playback Switch",
0119               SUN8I_ADDA_LOMIXSC,
0120               SUN8I_ADDA_ROMIXSC,
0121               SUN8I_ADDA_LOMIXSC_DACL, 1, 0),
0122     SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch",
0123               SUN8I_ADDA_LOMIXSC,
0124               SUN8I_ADDA_ROMIXSC,
0125               SUN8I_ADDA_LOMIXSC_DACR, 1, 0),
0126     SOC_DAPM_DOUBLE_R("Line In Playback Switch",
0127               SUN8I_ADDA_LOMIXSC,
0128               SUN8I_ADDA_ROMIXSC,
0129               SUN8I_ADDA_LOMIXSC_LINEINL, 1, 0),
0130     SOC_DAPM_DOUBLE_R("Mic1 Playback Switch",
0131               SUN8I_ADDA_LOMIXSC,
0132               SUN8I_ADDA_ROMIXSC,
0133               SUN8I_ADDA_LOMIXSC_MIC1, 1, 0),
0134     SOC_DAPM_DOUBLE_R("Mic2 Playback Switch",
0135               SUN8I_ADDA_LOMIXSC,
0136               SUN8I_ADDA_ROMIXSC,
0137               SUN8I_ADDA_LOMIXSC_MIC2, 1, 0),
0138 };
0139 
0140 /* mixer controls */
0141 static const struct snd_kcontrol_new sun8i_v3s_codec_mixer_controls[] = {
0142     SOC_DAPM_DOUBLE_R("DAC Playback Switch",
0143               SUN8I_ADDA_LOMIXSC,
0144               SUN8I_ADDA_ROMIXSC,
0145               SUN8I_ADDA_LOMIXSC_DACL, 1, 0),
0146     SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch",
0147               SUN8I_ADDA_LOMIXSC,
0148               SUN8I_ADDA_ROMIXSC,
0149               SUN8I_ADDA_LOMIXSC_DACR, 1, 0),
0150     SOC_DAPM_DOUBLE_R("Mic1 Playback Switch",
0151               SUN8I_ADDA_LOMIXSC,
0152               SUN8I_ADDA_ROMIXSC,
0153               SUN8I_ADDA_LOMIXSC_MIC1, 1, 0),
0154 };
0155 
0156 /* ADC mixer controls */
0157 static const struct snd_kcontrol_new sun8i_codec_adc_mixer_controls[] = {
0158     SOC_DAPM_DOUBLE_R("Mixer Capture Switch",
0159               SUN8I_ADDA_LADCMIXSC,
0160               SUN8I_ADDA_RADCMIXSC,
0161               SUN8I_ADDA_LADCMIXSC_OMIXRL, 1, 0),
0162     SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch",
0163               SUN8I_ADDA_LADCMIXSC,
0164               SUN8I_ADDA_RADCMIXSC,
0165               SUN8I_ADDA_LADCMIXSC_OMIXRR, 1, 0),
0166     SOC_DAPM_DOUBLE_R("Line In Capture Switch",
0167               SUN8I_ADDA_LADCMIXSC,
0168               SUN8I_ADDA_RADCMIXSC,
0169               SUN8I_ADDA_LADCMIXSC_LINEINL, 1, 0),
0170     SOC_DAPM_DOUBLE_R("Mic1 Capture Switch",
0171               SUN8I_ADDA_LADCMIXSC,
0172               SUN8I_ADDA_RADCMIXSC,
0173               SUN8I_ADDA_LADCMIXSC_MIC1, 1, 0),
0174     SOC_DAPM_DOUBLE_R("Mic2 Capture Switch",
0175               SUN8I_ADDA_LADCMIXSC,
0176               SUN8I_ADDA_RADCMIXSC,
0177               SUN8I_ADDA_LADCMIXSC_MIC2, 1, 0),
0178 };
0179 
0180 /* ADC mixer controls */
0181 static const struct snd_kcontrol_new sun8i_v3s_codec_adc_mixer_controls[] = {
0182     SOC_DAPM_DOUBLE_R("Mixer Capture Switch",
0183               SUN8I_ADDA_LADCMIXSC,
0184               SUN8I_ADDA_RADCMIXSC,
0185               SUN8I_ADDA_LADCMIXSC_OMIXRL, 1, 0),
0186     SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch",
0187               SUN8I_ADDA_LADCMIXSC,
0188               SUN8I_ADDA_RADCMIXSC,
0189               SUN8I_ADDA_LADCMIXSC_OMIXRR, 1, 0),
0190     SOC_DAPM_DOUBLE_R("Mic1 Capture Switch",
0191               SUN8I_ADDA_LADCMIXSC,
0192               SUN8I_ADDA_RADCMIXSC,
0193               SUN8I_ADDA_LADCMIXSC_MIC1, 1, 0),
0194 };
0195 
0196 /* volume / mute controls */
0197 static const DECLARE_TLV_DB_SCALE(sun8i_codec_out_mixer_pregain_scale,
0198                   -450, 150, 0);
0199 static const DECLARE_TLV_DB_RANGE(sun8i_codec_mic_gain_scale,
0200     0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
0201     1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
0202 );
0203 
0204 static const struct snd_kcontrol_new sun8i_codec_common_controls[] = {
0205     /* Mixer pre-gain */
0206     SOC_SINGLE_TLV("Mic1 Playback Volume", SUN8I_ADDA_MICIN_GCTRL,
0207                SUN8I_ADDA_MICIN_GCTRL_MIC1G,
0208                0x7, 0, sun8i_codec_out_mixer_pregain_scale),
0209 
0210     /* Microphone Amp boost gain */
0211     SOC_SINGLE_TLV("Mic1 Boost Volume", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
0212                SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST, 0x7, 0,
0213                sun8i_codec_mic_gain_scale),
0214 
0215     /* ADC */
0216     SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN8I_ADDA_ADC_AP_EN,
0217                SUN8I_ADDA_ADC_AP_EN_ADCG, 0x7, 0,
0218                sun8i_codec_out_mixer_pregain_scale),
0219 };
0220 
0221 static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = {
0222     /* ADC */
0223     SND_SOC_DAPM_ADC("Left ADC", NULL, SUN8I_ADDA_ADC_AP_EN,
0224              SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0),
0225     SND_SOC_DAPM_ADC("Right ADC", NULL, SUN8I_ADDA_ADC_AP_EN,
0226              SUN8I_ADDA_ADC_AP_EN_ADCREN, 0),
0227 
0228     /* DAC */
0229     SND_SOC_DAPM_DAC("Left DAC", NULL, SUN8I_ADDA_DAC_PA_SRC,
0230              SUN8I_ADDA_DAC_PA_SRC_DACALEN, 0),
0231     SND_SOC_DAPM_DAC("Right DAC", NULL, SUN8I_ADDA_DAC_PA_SRC,
0232              SUN8I_ADDA_DAC_PA_SRC_DACAREN, 0),
0233     /*
0234      * Due to this component and the codec belonging to separate DAPM
0235      * contexts, we need to manually link the above widgets to their
0236      * stream widgets at the card level.
0237      */
0238 
0239     /* Microphone input */
0240     SND_SOC_DAPM_INPUT("MIC1"),
0241 
0242     /* Mic input path */
0243     SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
0244              SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN, 0, NULL, 0),
0245 };
0246 
0247 static const struct snd_soc_dapm_widget sun8i_codec_mixer_widgets[] = {
0248     SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC,
0249                SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0,
0250                sun8i_codec_mixer_controls,
0251                ARRAY_SIZE(sun8i_codec_mixer_controls)),
0252     SND_SOC_DAPM_MIXER("Right Mixer", SUN8I_ADDA_DAC_PA_SRC,
0253                SUN8I_ADDA_DAC_PA_SRC_RMIXEN, 0,
0254                sun8i_codec_mixer_controls,
0255                ARRAY_SIZE(sun8i_codec_mixer_controls)),
0256     SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
0257                SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0,
0258                sun8i_codec_adc_mixer_controls,
0259                ARRAY_SIZE(sun8i_codec_adc_mixer_controls)),
0260     SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
0261                SUN8I_ADDA_ADC_AP_EN_ADCREN, 0,
0262                sun8i_codec_adc_mixer_controls,
0263                ARRAY_SIZE(sun8i_codec_adc_mixer_controls)),
0264 };
0265 
0266 static const struct snd_soc_dapm_widget sun8i_v3s_codec_mixer_widgets[] = {
0267     SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC,
0268                SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0,
0269                sun8i_v3s_codec_mixer_controls,
0270                ARRAY_SIZE(sun8i_v3s_codec_mixer_controls)),
0271     SND_SOC_DAPM_MIXER("Right Mixer", SUN8I_ADDA_DAC_PA_SRC,
0272                SUN8I_ADDA_DAC_PA_SRC_RMIXEN, 0,
0273                sun8i_v3s_codec_mixer_controls,
0274                ARRAY_SIZE(sun8i_v3s_codec_mixer_controls)),
0275     SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
0276                SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0,
0277                sun8i_v3s_codec_adc_mixer_controls,
0278                ARRAY_SIZE(sun8i_v3s_codec_adc_mixer_controls)),
0279     SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN8I_ADDA_ADC_AP_EN,
0280                SUN8I_ADDA_ADC_AP_EN_ADCREN, 0,
0281                sun8i_v3s_codec_adc_mixer_controls,
0282                ARRAY_SIZE(sun8i_v3s_codec_adc_mixer_controls)),
0283 };
0284 
0285 static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = {
0286     /* Microphone Routes */
0287     { "Mic1 Amplifier", NULL, "MIC1"},
0288 };
0289 
0290 static const struct snd_soc_dapm_route sun8i_codec_mixer_routes[] = {
0291     /* Left Mixer Routes */
0292     { "Left Mixer", "DAC Playback Switch", "Left DAC" },
0293     { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
0294     { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
0295 
0296     /* Right Mixer Routes */
0297     { "Right Mixer", "DAC Playback Switch", "Right DAC" },
0298     { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
0299     { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
0300 
0301     /* Left ADC Mixer Routes */
0302     { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
0303     { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
0304     { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
0305 
0306     /* Right ADC Mixer Routes */
0307     { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
0308     { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
0309     { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
0310 
0311     /* ADC Routes */
0312     { "Left ADC", NULL, "Left ADC Mixer" },
0313     { "Right ADC", NULL, "Right ADC Mixer" },
0314 };
0315 
0316 /* headphone specific controls, widgets, and routes */
0317 static const DECLARE_TLV_DB_SCALE(sun8i_codec_hp_vol_scale, -6300, 100, 1);
0318 static const struct snd_kcontrol_new sun8i_codec_headphone_controls[] = {
0319     SOC_SINGLE_TLV("Headphone Playback Volume",
0320                SUN8I_ADDA_HP_VOLC,
0321                SUN8I_ADDA_HP_VOLC_HP_VOL, 0x3f, 0,
0322                sun8i_codec_hp_vol_scale),
0323     SOC_DOUBLE("Headphone Playback Switch",
0324            SUN8I_ADDA_DAC_PA_SRC,
0325            SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE,
0326            SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE, 1, 0),
0327 };
0328 
0329 static const char * const sun8i_codec_hp_src_enum_text[] = {
0330     "DAC", "Mixer",
0331 };
0332 
0333 static SOC_ENUM_DOUBLE_DECL(sun8i_codec_hp_src_enum,
0334                 SUN8I_ADDA_DAC_PA_SRC,
0335                 SUN8I_ADDA_DAC_PA_SRC_LHPIS,
0336                 SUN8I_ADDA_DAC_PA_SRC_RHPIS,
0337                 sun8i_codec_hp_src_enum_text);
0338 
0339 static const struct snd_kcontrol_new sun8i_codec_hp_src[] = {
0340     SOC_DAPM_ENUM("Headphone Source Playback Route",
0341               sun8i_codec_hp_src_enum),
0342 };
0343 
0344 static int sun8i_headphone_amp_event(struct snd_soc_dapm_widget *w,
0345                      struct snd_kcontrol *k, int event)
0346 {
0347     struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
0348 
0349     if (SND_SOC_DAPM_EVENT_ON(event)) {
0350         snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL,
0351                           BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN),
0352                           BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN));
0353         /*
0354          * Need a delay to have the amplifier up. 700ms seems the best
0355          * compromise between the time to let the amplifier up and the
0356          * time not to feel this delay while playing a sound.
0357          */
0358         msleep(700);
0359     } else if (SND_SOC_DAPM_EVENT_OFF(event)) {
0360         snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL,
0361                           BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN),
0362                           0x0);
0363     }
0364 
0365     return 0;
0366 }
0367 
0368 static const struct snd_soc_dapm_widget sun8i_codec_headphone_widgets[] = {
0369     SND_SOC_DAPM_MUX("Headphone Source Playback Route",
0370              SND_SOC_NOPM, 0, 0, sun8i_codec_hp_src),
0371     SND_SOC_DAPM_OUT_DRV_E("Headphone Amp", SUN8I_ADDA_PAEN_HP_CTRL,
0372                    SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0, NULL, 0,
0373                    sun8i_headphone_amp_event,
0374                    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
0375     SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN8I_ADDA_PAEN_HP_CTRL,
0376                 SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN, 0, NULL, 0),
0377     SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN8I_ADDA_PAEN_HP_CTRL,
0378              SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC, 0x3, 0x3, 0),
0379     SND_SOC_DAPM_OUTPUT("HP"),
0380 };
0381 
0382 static const struct snd_soc_dapm_route sun8i_codec_headphone_routes[] = {
0383     { "Headphone Source Playback Route", "DAC", "Left DAC" },
0384     { "Headphone Source Playback Route", "DAC", "Right DAC" },
0385     { "Headphone Source Playback Route", "Mixer", "Left Mixer" },
0386     { "Headphone Source Playback Route", "Mixer", "Right Mixer" },
0387     { "Headphone Amp", NULL, "Headphone Source Playback Route" },
0388     { "HPCOM", NULL, "HPCOM Protection" },
0389     { "HP", NULL, "Headphone Amp" },
0390 };
0391 
0392 static int sun8i_codec_add_headphone(struct snd_soc_component *cmpnt)
0393 {
0394     struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
0395     struct device *dev = cmpnt->dev;
0396     int ret;
0397 
0398     ret = snd_soc_add_component_controls(cmpnt,
0399                          sun8i_codec_headphone_controls,
0400                          ARRAY_SIZE(sun8i_codec_headphone_controls));
0401     if (ret) {
0402         dev_err(dev, "Failed to add Headphone controls: %d\n", ret);
0403         return ret;
0404     }
0405 
0406     ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_headphone_widgets,
0407                     ARRAY_SIZE(sun8i_codec_headphone_widgets));
0408     if (ret) {
0409         dev_err(dev, "Failed to add Headphone DAPM widgets: %d\n", ret);
0410         return ret;
0411     }
0412 
0413     ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_headphone_routes,
0414                       ARRAY_SIZE(sun8i_codec_headphone_routes));
0415     if (ret) {
0416         dev_err(dev, "Failed to add Headphone DAPM routes: %d\n", ret);
0417         return ret;
0418     }
0419 
0420     return 0;
0421 }
0422 
0423 /* mbias specific widget */
0424 static const struct snd_soc_dapm_widget sun8i_codec_mbias_widgets[] = {
0425     SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
0426                 SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN,
0427                 0, NULL, 0),
0428 };
0429 
0430 static int sun8i_codec_add_mbias(struct snd_soc_component *cmpnt)
0431 {
0432     struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
0433     struct device *dev = cmpnt->dev;
0434     int ret;
0435 
0436     ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_mbias_widgets,
0437                     ARRAY_SIZE(sun8i_codec_mbias_widgets));
0438     if (ret)
0439         dev_err(dev, "Failed to add MBIAS DAPM widgets: %d\n", ret);
0440 
0441     return ret;
0442 }
0443 
0444 /* hmic specific widget */
0445 static const struct snd_soc_dapm_widget sun8i_codec_hmic_widgets[] = {
0446     SND_SOC_DAPM_SUPPLY("HBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
0447                 SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN,
0448                 0, NULL, 0),
0449 };
0450 
0451 static int sun8i_codec_add_hmic(struct snd_soc_component *cmpnt)
0452 {
0453     struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
0454     struct device *dev = cmpnt->dev;
0455     int ret;
0456 
0457     ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_hmic_widgets,
0458                     ARRAY_SIZE(sun8i_codec_hmic_widgets));
0459     if (ret)
0460         dev_err(dev, "Failed to add Mic3 DAPM widgets: %d\n", ret);
0461 
0462     return ret;
0463 }
0464 
0465 /* line in specific controls, widgets and rines */
0466 static const struct snd_kcontrol_new sun8i_codec_linein_controls[] = {
0467     /* Mixer pre-gain */
0468     SOC_SINGLE_TLV("Line In Playback Volume", SUN8I_ADDA_LINEIN_GCTRL,
0469                SUN8I_ADDA_LINEIN_GCTRL_LINEING,
0470                0x7, 0, sun8i_codec_out_mixer_pregain_scale),
0471 };
0472 
0473 static const struct snd_soc_dapm_widget sun8i_codec_linein_widgets[] = {
0474     /* Line input */
0475     SND_SOC_DAPM_INPUT("LINEIN"),
0476 };
0477 
0478 static const struct snd_soc_dapm_route sun8i_codec_linein_routes[] = {
0479     { "Left Mixer", "Line In Playback Switch", "LINEIN" },
0480 
0481     { "Right Mixer", "Line In Playback Switch", "LINEIN" },
0482 
0483     { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
0484 
0485     { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
0486 };
0487 
0488 static int sun8i_codec_add_linein(struct snd_soc_component *cmpnt)
0489 {
0490     struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
0491     struct device *dev = cmpnt->dev;
0492     int ret;
0493 
0494     ret = snd_soc_add_component_controls(cmpnt,
0495                          sun8i_codec_linein_controls,
0496                          ARRAY_SIZE(sun8i_codec_linein_controls));
0497     if (ret) {
0498         dev_err(dev, "Failed to add Line In controls: %d\n", ret);
0499         return ret;
0500     }
0501 
0502     ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_linein_widgets,
0503                     ARRAY_SIZE(sun8i_codec_linein_widgets));
0504     if (ret) {
0505         dev_err(dev, "Failed to add Line In DAPM widgets: %d\n", ret);
0506         return ret;
0507     }
0508 
0509     ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_linein_routes,
0510                       ARRAY_SIZE(sun8i_codec_linein_routes));
0511     if (ret) {
0512         dev_err(dev, "Failed to add Line In DAPM routes: %d\n", ret);
0513         return ret;
0514     }
0515 
0516     return 0;
0517 }
0518 
0519 
0520 /* line out specific controls, widgets and routes */
0521 static const DECLARE_TLV_DB_RANGE(sun8i_codec_lineout_vol_scale,
0522     0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
0523     2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),
0524 );
0525 static const struct snd_kcontrol_new sun8i_codec_lineout_controls[] = {
0526     SOC_SINGLE_TLV("Line Out Playback Volume",
0527                SUN8I_ADDA_PHONE_GAIN_CTRL,
0528                SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL, 0x1f, 0,
0529                sun8i_codec_lineout_vol_scale),
0530     SOC_DOUBLE("Line Out Playback Switch",
0531            SUN8I_ADDA_MIC2G_CTRL,
0532            SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN,
0533            SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN, 1, 0),
0534 };
0535 
0536 static const char * const sun8i_codec_lineout_src_enum_text[] = {
0537     "Stereo", "Mono Differential",
0538 };
0539 
0540 static SOC_ENUM_DOUBLE_DECL(sun8i_codec_lineout_src_enum,
0541                 SUN8I_ADDA_MIC2G_CTRL,
0542                 SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC,
0543                 SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC,
0544                 sun8i_codec_lineout_src_enum_text);
0545 
0546 static const struct snd_kcontrol_new sun8i_codec_lineout_src[] = {
0547     SOC_DAPM_ENUM("Line Out Source Playback Route",
0548               sun8i_codec_lineout_src_enum),
0549 };
0550 
0551 static const struct snd_soc_dapm_widget sun8i_codec_lineout_widgets[] = {
0552     SND_SOC_DAPM_MUX("Line Out Source Playback Route",
0553              SND_SOC_NOPM, 0, 0, sun8i_codec_lineout_src),
0554     /* It is unclear if this is a buffer or gate, model it as a supply */
0555     SND_SOC_DAPM_SUPPLY("Line Out Enable", SUN8I_ADDA_PAEN_HP_CTRL,
0556                 SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN, 0, NULL, 0),
0557     SND_SOC_DAPM_OUTPUT("LINEOUT"),
0558 };
0559 
0560 static const struct snd_soc_dapm_route sun8i_codec_lineout_routes[] = {
0561     { "Line Out Source Playback Route", "Stereo", "Left Mixer" },
0562     { "Line Out Source Playback Route", "Stereo", "Right Mixer" },
0563     { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
0564     { "Line Out Source Playback Route", "Mono Differential", "Right Mixer" },
0565     { "LINEOUT", NULL, "Line Out Source Playback Route" },
0566     { "LINEOUT", NULL, "Line Out Enable", },
0567 };
0568 
0569 static int sun8i_codec_add_lineout(struct snd_soc_component *cmpnt)
0570 {
0571     struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
0572     struct device *dev = cmpnt->dev;
0573     int ret;
0574 
0575     ret = snd_soc_add_component_controls(cmpnt,
0576                          sun8i_codec_lineout_controls,
0577                          ARRAY_SIZE(sun8i_codec_lineout_controls));
0578     if (ret) {
0579         dev_err(dev, "Failed to add Line Out controls: %d\n", ret);
0580         return ret;
0581     }
0582 
0583     ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_lineout_widgets,
0584                     ARRAY_SIZE(sun8i_codec_lineout_widgets));
0585     if (ret) {
0586         dev_err(dev, "Failed to add Line Out DAPM widgets: %d\n", ret);
0587         return ret;
0588     }
0589 
0590     ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_lineout_routes,
0591                       ARRAY_SIZE(sun8i_codec_lineout_routes));
0592     if (ret) {
0593         dev_err(dev, "Failed to add Line Out DAPM routes: %d\n", ret);
0594         return ret;
0595     }
0596 
0597     return 0;
0598 }
0599 
0600 /* mic2 specific controls, widgets and routes */
0601 static const struct snd_kcontrol_new sun8i_codec_mic2_controls[] = {
0602     /* Mixer pre-gain */
0603     SOC_SINGLE_TLV("Mic2 Playback Volume",
0604                SUN8I_ADDA_MICIN_GCTRL, SUN8I_ADDA_MICIN_GCTRL_MIC2G,
0605                0x7, 0, sun8i_codec_out_mixer_pregain_scale),
0606 
0607     /* Microphone Amp boost gain */
0608     SOC_SINGLE_TLV("Mic2 Boost Volume", SUN8I_ADDA_MIC2G_CTRL,
0609                SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST, 0x7, 0,
0610                sun8i_codec_mic_gain_scale),
0611 };
0612 
0613 static const struct snd_soc_dapm_widget sun8i_codec_mic2_widgets[] = {
0614     /* Microphone input */
0615     SND_SOC_DAPM_INPUT("MIC2"),
0616 
0617     /* Mic input path */
0618     SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN8I_ADDA_MIC2G_CTRL,
0619              SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN, 0, NULL, 0),
0620 };
0621 
0622 static const struct snd_soc_dapm_route sun8i_codec_mic2_routes[] = {
0623     { "Mic2 Amplifier", NULL, "MIC2"},
0624 
0625     { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
0626 
0627     { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
0628 
0629     { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
0630 
0631     { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
0632 };
0633 
0634 static int sun8i_codec_add_mic2(struct snd_soc_component *cmpnt)
0635 {
0636     struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
0637     struct device *dev = cmpnt->dev;
0638     int ret;
0639 
0640     ret = snd_soc_add_component_controls(cmpnt,
0641                          sun8i_codec_mic2_controls,
0642                          ARRAY_SIZE(sun8i_codec_mic2_controls));
0643     if (ret) {
0644         dev_err(dev, "Failed to add MIC2 controls: %d\n", ret);
0645         return ret;
0646     }
0647 
0648     ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_mic2_widgets,
0649                     ARRAY_SIZE(sun8i_codec_mic2_widgets));
0650     if (ret) {
0651         dev_err(dev, "Failed to add MIC2 DAPM widgets: %d\n", ret);
0652         return ret;
0653     }
0654 
0655     ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_mic2_routes,
0656                       ARRAY_SIZE(sun8i_codec_mic2_routes));
0657     if (ret) {
0658         dev_err(dev, "Failed to add MIC2 DAPM routes: %d\n", ret);
0659         return ret;
0660     }
0661 
0662     return 0;
0663 }
0664 
0665 struct sun8i_codec_analog_quirks {
0666     bool has_headphone;
0667     bool has_hmic;
0668     bool has_linein;
0669     bool has_lineout;
0670     bool has_mbias;
0671     bool has_mic2;
0672 };
0673 
0674 static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = {
0675     .has_headphone  = true,
0676     .has_hmic   = true,
0677     .has_linein = true,
0678     .has_mbias  = true,
0679     .has_mic2   = true,
0680 };
0681 
0682 static const struct sun8i_codec_analog_quirks sun8i_h3_quirks = {
0683     .has_linein = true,
0684     .has_lineout    = true,
0685     .has_mbias  = true,
0686     .has_mic2   = true,
0687 };
0688 
0689 static int sun8i_codec_analog_add_mixer(struct snd_soc_component *cmpnt,
0690                     const struct sun8i_codec_analog_quirks *quirks)
0691 {
0692     struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
0693     struct device *dev = cmpnt->dev;
0694     int ret;
0695 
0696     if (!quirks->has_mic2 && !quirks->has_linein) {
0697         /*
0698          * Apply the special widget set which has uses a control
0699          * without MIC2 and Line In, for SoCs without these.
0700          * TODO: not all special cases are supported now, this case
0701          * is present because it's the case of V3s.
0702          */
0703         ret = snd_soc_dapm_new_controls(dapm,
0704                         sun8i_v3s_codec_mixer_widgets,
0705                         ARRAY_SIZE(sun8i_v3s_codec_mixer_widgets));
0706         if (ret) {
0707             dev_err(dev, "Failed to add V3s Mixer DAPM widgets: %d\n", ret);
0708             return ret;
0709         }
0710     } else {
0711         /* Apply the generic mixer widget set. */
0712         ret = snd_soc_dapm_new_controls(dapm,
0713                         sun8i_codec_mixer_widgets,
0714                         ARRAY_SIZE(sun8i_codec_mixer_widgets));
0715         if (ret) {
0716             dev_err(dev, "Failed to add Mixer DAPM widgets: %d\n", ret);
0717             return ret;
0718         }
0719     }
0720 
0721     ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_mixer_routes,
0722                       ARRAY_SIZE(sun8i_codec_mixer_routes));
0723     if (ret) {
0724         dev_err(dev, "Failed to add Mixer DAPM routes: %d\n", ret);
0725         return ret;
0726     }
0727 
0728     return 0;
0729 }
0730 
0731 static const struct sun8i_codec_analog_quirks sun8i_v3s_quirks = {
0732     .has_headphone  = true,
0733     .has_hmic   = true,
0734 };
0735 
0736 static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt)
0737 {
0738     struct device *dev = cmpnt->dev;
0739     const struct sun8i_codec_analog_quirks *quirks;
0740     int ret;
0741 
0742     /*
0743      * This would never return NULL unless someone directly registers a
0744      * platform device matching this driver's name, without specifying a
0745      * device tree node.
0746      */
0747     quirks = of_device_get_match_data(dev);
0748 
0749     /* Add controls, widgets, and routes for individual features */
0750     ret = sun8i_codec_analog_add_mixer(cmpnt, quirks);
0751     if (ret)
0752         return ret;
0753 
0754     if (quirks->has_headphone) {
0755         ret = sun8i_codec_add_headphone(cmpnt);
0756         if (ret)
0757             return ret;
0758     }
0759 
0760     if (quirks->has_hmic) {
0761         ret = sun8i_codec_add_hmic(cmpnt);
0762         if (ret)
0763             return ret;
0764     }
0765 
0766     if (quirks->has_linein) {
0767         ret = sun8i_codec_add_linein(cmpnt);
0768         if (ret)
0769             return ret;
0770     }
0771 
0772     if (quirks->has_lineout) {
0773         ret = sun8i_codec_add_lineout(cmpnt);
0774         if (ret)
0775             return ret;
0776     }
0777 
0778     if (quirks->has_mbias) {
0779         ret = sun8i_codec_add_mbias(cmpnt);
0780         if (ret)
0781             return ret;
0782     }
0783 
0784     if (quirks->has_mic2) {
0785         ret = sun8i_codec_add_mic2(cmpnt);
0786         if (ret)
0787             return ret;
0788     }
0789 
0790     return 0;
0791 }
0792 
0793 static const struct snd_soc_component_driver sun8i_codec_analog_cmpnt_drv = {
0794     .controls       = sun8i_codec_common_controls,
0795     .num_controls       = ARRAY_SIZE(sun8i_codec_common_controls),
0796     .dapm_widgets       = sun8i_codec_common_widgets,
0797     .num_dapm_widgets   = ARRAY_SIZE(sun8i_codec_common_widgets),
0798     .dapm_routes        = sun8i_codec_common_routes,
0799     .num_dapm_routes    = ARRAY_SIZE(sun8i_codec_common_routes),
0800     .probe          = sun8i_codec_analog_cmpnt_probe,
0801 };
0802 
0803 static const struct of_device_id sun8i_codec_analog_of_match[] = {
0804     {
0805         .compatible = "allwinner,sun8i-a23-codec-analog",
0806         .data = &sun8i_a23_quirks,
0807     },
0808     {
0809         .compatible = "allwinner,sun8i-h3-codec-analog",
0810         .data = &sun8i_h3_quirks,
0811     },
0812     {
0813         .compatible = "allwinner,sun8i-v3s-codec-analog",
0814         .data = &sun8i_v3s_quirks,
0815     },
0816     {}
0817 };
0818 MODULE_DEVICE_TABLE(of, sun8i_codec_analog_of_match);
0819 
0820 static int sun8i_codec_analog_probe(struct platform_device *pdev)
0821 {
0822     struct regmap *regmap;
0823     void __iomem *base;
0824 
0825     base = devm_platform_ioremap_resource(pdev, 0);
0826     if (IS_ERR(base)) {
0827         dev_err(&pdev->dev, "Failed to map the registers\n");
0828         return PTR_ERR(base);
0829     }
0830 
0831     regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base);
0832     if (IS_ERR(regmap)) {
0833         dev_err(&pdev->dev, "Failed to create regmap\n");
0834         return PTR_ERR(regmap);
0835     }
0836 
0837     return devm_snd_soc_register_component(&pdev->dev,
0838                            &sun8i_codec_analog_cmpnt_drv,
0839                            NULL, 0);
0840 }
0841 
0842 static struct platform_driver sun8i_codec_analog_driver = {
0843     .driver = {
0844         .name = "sun8i-codec-analog",
0845         .of_match_table = sun8i_codec_analog_of_match,
0846     },
0847     .probe = sun8i_codec_analog_probe,
0848 };
0849 module_platform_driver(sun8i_codec_analog_driver);
0850 
0851 MODULE_DESCRIPTION("Allwinner internal codec analog controls driver");
0852 MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
0853 MODULE_LICENSE("GPL");
0854 MODULE_ALIAS("platform:sun8i-codec-analog");