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"); 855