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