root/sound/soc/sunxi/sun8i-codec-analog.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sun8i_headphone_amp_event
  2. sun8i_codec_add_headphone
  3. sun8i_codec_add_mbias
  4. sun8i_codec_add_hmic
  5. sun8i_codec_add_linein
  6. sun8i_codec_add_lineout
  7. sun8i_codec_add_mic2
  8. sun8i_codec_analog_add_mixer
  9. sun8i_codec_analog_cmpnt_probe
  10. sun8i_codec_analog_probe

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

/* [<][>][^][v][top][bottom][index][help] */