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 /* ADC mixer controls */ 223 static const struct snd_kcontrol_new sun8i_codec_adc_mixer_controls[] = { 224 SOC_DAPM_DOUBLE_R("Mixer Capture Switch", 225 SUN8I_ADDA_LADCMIXSC, 226 SUN8I_ADDA_RADCMIXSC, 227 SUN8I_ADDA_LADCMIXSC_OMIXRL, 1, 0), 228 SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch", 229 SUN8I_ADDA_LADCMIXSC, 230 SUN8I_ADDA_RADCMIXSC, 231 SUN8I_ADDA_LADCMIXSC_OMIXRR, 1, 0), 232 SOC_DAPM_DOUBLE_R("Line In Capture Switch", 233 SUN8I_ADDA_LADCMIXSC, 234 SUN8I_ADDA_RADCMIXSC, 235 SUN8I_ADDA_LADCMIXSC_LINEINL, 1, 0), 236 SOC_DAPM_DOUBLE_R("Mic1 Capture Switch", 237 SUN8I_ADDA_LADCMIXSC, 238 SUN8I_ADDA_RADCMIXSC, 239 SUN8I_ADDA_LADCMIXSC_MIC1, 1, 0), 240 SOC_DAPM_DOUBLE_R("Mic2 Capture Switch", 241 SUN8I_ADDA_LADCMIXSC, 242 SUN8I_ADDA_RADCMIXSC, 243 SUN8I_ADDA_LADCMIXSC_MIC2, 1, 0), 244 }; 245 246 /* volume / mute controls */ 247 static const DECLARE_TLV_DB_SCALE(sun8i_codec_out_mixer_pregain_scale, 248 -450, 150, 0); 249 static const DECLARE_TLV_DB_RANGE(sun8i_codec_mic_gain_scale, 250 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), 251 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0), 252 ); 253 254 static const struct snd_kcontrol_new sun8i_codec_common_controls[] = { 255 /* Mixer pre-gains */ 256 SOC_SINGLE_TLV("Line In Playback Volume", SUN8I_ADDA_LINEIN_GCTRL, 257 SUN8I_ADDA_LINEIN_GCTRL_LINEING, 258 0x7, 0, sun8i_codec_out_mixer_pregain_scale), 259 SOC_SINGLE_TLV("Mic1 Playback Volume", SUN8I_ADDA_MICIN_GCTRL, 260 SUN8I_ADDA_MICIN_GCTRL_MIC1G, 261 0x7, 0, sun8i_codec_out_mixer_pregain_scale), 262 SOC_SINGLE_TLV("Mic2 Playback Volume", 263 SUN8I_ADDA_MICIN_GCTRL, SUN8I_ADDA_MICIN_GCTRL_MIC2G, 264 0x7, 0, sun8i_codec_out_mixer_pregain_scale), 265 266 /* Microphone Amp boost gains */ 267 SOC_SINGLE_TLV("Mic1 Boost Volume", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, 268 SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST, 0x7, 0, 269 sun8i_codec_mic_gain_scale), 270 SOC_SINGLE_TLV("Mic2 Boost Volume", SUN8I_ADDA_MIC2G_CTRL, 271 SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST, 0x7, 0, 272 sun8i_codec_mic_gain_scale), 273 274 /* ADC */ 275 SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN8I_ADDA_ADC_AP_EN, 276 SUN8I_ADDA_ADC_AP_EN_ADCG, 0x7, 0, 277 sun8i_codec_out_mixer_pregain_scale), 278 }; 279 280 static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = { 281 /* ADC */ 282 SND_SOC_DAPM_ADC("Left ADC", NULL, SUN8I_ADDA_ADC_AP_EN, 283 SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0), 284 SND_SOC_DAPM_ADC("Right ADC", NULL, SUN8I_ADDA_ADC_AP_EN, 285 SUN8I_ADDA_ADC_AP_EN_ADCREN, 0), 286 287 /* DAC */ 288 SND_SOC_DAPM_DAC("Left DAC", NULL, SUN8I_ADDA_DAC_PA_SRC, 289 SUN8I_ADDA_DAC_PA_SRC_DACALEN, 0), 290 SND_SOC_DAPM_DAC("Right DAC", NULL, SUN8I_ADDA_DAC_PA_SRC, 291 SUN8I_ADDA_DAC_PA_SRC_DACAREN, 0), 292 /* 293 * Due to this component and the codec belonging to separate DAPM 294 * contexts, we need to manually link the above widgets to their 295 * stream widgets at the card level. 296 */ 297 298 /* Line In */ 299 SND_SOC_DAPM_INPUT("LINEIN"), 300 301 /* Microphone inputs */ 302 SND_SOC_DAPM_INPUT("MIC1"), 303 SND_SOC_DAPM_INPUT("MIC2"), 304 305 /* Microphone Bias */ 306 SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, 307 SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN, 308 0, NULL, 0), 309 310 /* Mic input path */ 311 SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, 312 SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN, 0, NULL, 0), 313 SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN8I_ADDA_MIC2G_CTRL, 314 SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN, 0, NULL, 0), 315 316 /* Mixers */ 317 SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC, 318 SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0, 319 sun8i_codec_mixer_controls, 320 ARRAY_SIZE(sun8i_codec_mixer_controls)), 321 SND_SOC_DAPM_MIXER("Right Mixer", SUN8I_ADDA_DAC_PA_SRC, 322 SUN8I_ADDA_DAC_PA_SRC_RMIXEN, 0, 323 sun8i_codec_mixer_controls, 324 ARRAY_SIZE(sun8i_codec_mixer_controls)), 325 SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN8I_ADDA_ADC_AP_EN, 326 SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0, 327 sun8i_codec_adc_mixer_controls, 328 ARRAY_SIZE(sun8i_codec_adc_mixer_controls)), 329 SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN8I_ADDA_ADC_AP_EN, 330 SUN8I_ADDA_ADC_AP_EN_ADCREN, 0, 331 sun8i_codec_adc_mixer_controls, 332 ARRAY_SIZE(sun8i_codec_adc_mixer_controls)), 333 }; 334 335 static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = { 336 /* Microphone Routes */ 337 { "Mic1 Amplifier", NULL, "MIC1"}, 338 { "Mic2 Amplifier", NULL, "MIC2"}, 339 340 /* Left Mixer Routes */ 341 { "Left Mixer", "DAC Playback Switch", "Left DAC" }, 342 { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" }, 343 { "Left Mixer", "Line In Playback Switch", "LINEIN" }, 344 { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, 345 { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, 346 347 /* Right Mixer Routes */ 348 { "Right Mixer", "DAC Playback Switch", "Right DAC" }, 349 { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" }, 350 { "Right Mixer", "Line In Playback Switch", "LINEIN" }, 351 { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, 352 { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, 353 354 /* Left ADC Mixer Routes */ 355 { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" }, 356 { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" }, 357 { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" }, 358 { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, 359 { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, 360 361 /* Right ADC Mixer Routes */ 362 { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" }, 363 { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" }, 364 { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" }, 365 { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, 366 { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, 367 368 /* ADC Routes */ 369 { "Left ADC", NULL, "Left ADC Mixer" }, 370 { "Right ADC", NULL, "Right ADC Mixer" }, 371 }; 372 373 /* headphone specific controls, widgets, and routes */ 374 static const DECLARE_TLV_DB_SCALE(sun8i_codec_hp_vol_scale, -6300, 100, 1); 375 static const struct snd_kcontrol_new sun8i_codec_headphone_controls[] = { 376 SOC_SINGLE_TLV("Headphone Playback Volume", 377 SUN8I_ADDA_HP_VOLC, 378 SUN8I_ADDA_HP_VOLC_HP_VOL, 0x3f, 0, 379 sun8i_codec_hp_vol_scale), 380 SOC_DOUBLE("Headphone Playback Switch", 381 SUN8I_ADDA_DAC_PA_SRC, 382 SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE, 383 SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE, 1, 0), 384 }; 385 386 static const char * const sun8i_codec_hp_src_enum_text[] = { 387 "DAC", "Mixer", 388 }; 389 390 static SOC_ENUM_DOUBLE_DECL(sun8i_codec_hp_src_enum, 391 SUN8I_ADDA_DAC_PA_SRC, 392 SUN8I_ADDA_DAC_PA_SRC_LHPIS, 393 SUN8I_ADDA_DAC_PA_SRC_RHPIS, 394 sun8i_codec_hp_src_enum_text); 395 396 static const struct snd_kcontrol_new sun8i_codec_hp_src[] = { 397 SOC_DAPM_ENUM("Headphone Source Playback Route", 398 sun8i_codec_hp_src_enum), 399 }; 400 401 static int sun8i_headphone_amp_event(struct snd_soc_dapm_widget *w, 402 struct snd_kcontrol *k, int event) 403 { 404 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 405 406 if (SND_SOC_DAPM_EVENT_ON(event)) { 407 snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL, 408 BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN), 409 BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN)); 410 /* 411 * Need a delay to have the amplifier up. 700ms seems the best 412 * compromise between the time to let the amplifier up and the 413 * time not to feel this delay while playing a sound. 414 */ 415 msleep(700); 416 } else if (SND_SOC_DAPM_EVENT_OFF(event)) { 417 snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL, 418 BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN), 419 0x0); 420 } 421 422 return 0; 423 } 424 425 static const struct snd_soc_dapm_widget sun8i_codec_headphone_widgets[] = { 426 SND_SOC_DAPM_MUX("Headphone Source Playback Route", 427 SND_SOC_NOPM, 0, 0, sun8i_codec_hp_src), 428 SND_SOC_DAPM_OUT_DRV_E("Headphone Amp", SUN8I_ADDA_PAEN_HP_CTRL, 429 SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0, NULL, 0, 430 sun8i_headphone_amp_event, 431 SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), 432 SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN8I_ADDA_PAEN_HP_CTRL, 433 SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN, 0, NULL, 0), 434 SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN8I_ADDA_PAEN_HP_CTRL, 435 SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC, 0x3, 0x3, 0), 436 SND_SOC_DAPM_OUTPUT("HP"), 437 }; 438 439 static const struct snd_soc_dapm_route sun8i_codec_headphone_routes[] = { 440 { "Headphone Source Playback Route", "DAC", "Left DAC" }, 441 { "Headphone Source Playback Route", "DAC", "Right DAC" }, 442 { "Headphone Source Playback Route", "Mixer", "Left Mixer" }, 443 { "Headphone Source Playback Route", "Mixer", "Right Mixer" }, 444 { "Headphone Amp", NULL, "Headphone Source Playback Route" }, 445 { "HPCOM", NULL, "HPCOM Protection" }, 446 { "HP", NULL, "Headphone Amp" }, 447 }; 448 449 static int sun8i_codec_add_headphone(struct snd_soc_component *cmpnt) 450 { 451 struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); 452 struct device *dev = cmpnt->dev; 453 int ret; 454 455 ret = snd_soc_add_component_controls(cmpnt, 456 sun8i_codec_headphone_controls, 457 ARRAY_SIZE(sun8i_codec_headphone_controls)); 458 if (ret) { 459 dev_err(dev, "Failed to add Headphone controls: %d\n", ret); 460 return ret; 461 } 462 463 ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_headphone_widgets, 464 ARRAY_SIZE(sun8i_codec_headphone_widgets)); 465 if (ret) { 466 dev_err(dev, "Failed to add Headphone DAPM widgets: %d\n", ret); 467 return ret; 468 } 469 470 ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_headphone_routes, 471 ARRAY_SIZE(sun8i_codec_headphone_routes)); 472 if (ret) { 473 dev_err(dev, "Failed to add Headphone DAPM routes: %d\n", ret); 474 return ret; 475 } 476 477 return 0; 478 } 479 480 /* hmic specific widget */ 481 static const struct snd_soc_dapm_widget sun8i_codec_hmic_widgets[] = { 482 SND_SOC_DAPM_SUPPLY("HBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, 483 SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN, 484 0, NULL, 0), 485 }; 486 487 static int sun8i_codec_add_hmic(struct snd_soc_component *cmpnt) 488 { 489 struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); 490 struct device *dev = cmpnt->dev; 491 int ret; 492 493 ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_hmic_widgets, 494 ARRAY_SIZE(sun8i_codec_hmic_widgets)); 495 if (ret) 496 dev_err(dev, "Failed to add Mic3 DAPM widgets: %d\n", ret); 497 498 return ret; 499 } 500 501 /* line out specific controls, widgets and routes */ 502 static const DECLARE_TLV_DB_RANGE(sun8i_codec_lineout_vol_scale, 503 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), 504 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0), 505 ); 506 static const struct snd_kcontrol_new sun8i_codec_lineout_controls[] = { 507 SOC_SINGLE_TLV("Line Out Playback Volume", 508 SUN8I_ADDA_PHONE_GAIN_CTRL, 509 SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL, 0x1f, 0, 510 sun8i_codec_lineout_vol_scale), 511 SOC_DOUBLE("Line Out Playback Switch", 512 SUN8I_ADDA_MIC2G_CTRL, 513 SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN, 514 SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN, 1, 0), 515 }; 516 517 static const char * const sun8i_codec_lineout_src_enum_text[] = { 518 "Stereo", "Mono Differential", 519 }; 520 521 static SOC_ENUM_DOUBLE_DECL(sun8i_codec_lineout_src_enum, 522 SUN8I_ADDA_MIC2G_CTRL, 523 SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC, 524 SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC, 525 sun8i_codec_lineout_src_enum_text); 526 527 static const struct snd_kcontrol_new sun8i_codec_lineout_src[] = { 528 SOC_DAPM_ENUM("Line Out Source Playback Route", 529 sun8i_codec_lineout_src_enum), 530 }; 531 532 static const struct snd_soc_dapm_widget sun8i_codec_lineout_widgets[] = { 533 SND_SOC_DAPM_MUX("Line Out Source Playback Route", 534 SND_SOC_NOPM, 0, 0, sun8i_codec_lineout_src), 535 /* It is unclear if this is a buffer or gate, model it as a supply */ 536 SND_SOC_DAPM_SUPPLY("Line Out Enable", SUN8I_ADDA_PAEN_HP_CTRL, 537 SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN, 0, NULL, 0), 538 SND_SOC_DAPM_OUTPUT("LINEOUT"), 539 }; 540 541 static const struct snd_soc_dapm_route sun8i_codec_lineout_routes[] = { 542 { "Line Out Source Playback Route", "Stereo", "Left Mixer" }, 543 { "Line Out Source Playback Route", "Stereo", "Right Mixer" }, 544 { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" }, 545 { "Line Out Source Playback Route", "Mono Differential", "Right Mixer" }, 546 { "LINEOUT", NULL, "Line Out Source Playback Route" }, 547 { "LINEOUT", NULL, "Line Out Enable", }, 548 }; 549 550 static int sun8i_codec_add_lineout(struct snd_soc_component *cmpnt) 551 { 552 struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); 553 struct device *dev = cmpnt->dev; 554 int ret; 555 556 ret = snd_soc_add_component_controls(cmpnt, 557 sun8i_codec_lineout_controls, 558 ARRAY_SIZE(sun8i_codec_lineout_controls)); 559 if (ret) { 560 dev_err(dev, "Failed to add Line Out controls: %d\n", ret); 561 return ret; 562 } 563 564 ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_lineout_widgets, 565 ARRAY_SIZE(sun8i_codec_lineout_widgets)); 566 if (ret) { 567 dev_err(dev, "Failed to add Line Out DAPM widgets: %d\n", ret); 568 return ret; 569 } 570 571 ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_lineout_routes, 572 ARRAY_SIZE(sun8i_codec_lineout_routes)); 573 if (ret) { 574 dev_err(dev, "Failed to add Line Out DAPM routes: %d\n", ret); 575 return ret; 576 } 577 578 return 0; 579 } 580 581 struct sun8i_codec_analog_quirks { 582 bool has_headphone; 583 bool has_hmic; 584 bool has_lineout; 585 }; 586 587 static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = { 588 .has_headphone = true, 589 .has_hmic = true, 590 }; 591 592 static const struct sun8i_codec_analog_quirks sun8i_h3_quirks = { 593 .has_lineout = true, 594 }; 595 596 static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt) 597 { 598 struct device *dev = cmpnt->dev; 599 const struct sun8i_codec_analog_quirks *quirks; 600 int ret; 601 602 /* 603 * This would never return NULL unless someone directly registers a 604 * platform device matching this driver's name, without specifying a 605 * device tree node. 606 */ 607 quirks = of_device_get_match_data(dev); 608 609 /* Add controls, widgets, and routes for individual features */ 610 611 if (quirks->has_headphone) { 612 ret = sun8i_codec_add_headphone(cmpnt); 613 if (ret) 614 return ret; 615 } 616 617 if (quirks->has_hmic) { 618 ret = sun8i_codec_add_hmic(cmpnt); 619 if (ret) 620 return ret; 621 } 622 623 if (quirks->has_lineout) { 624 ret = sun8i_codec_add_lineout(cmpnt); 625 if (ret) 626 return ret; 627 } 628 629 return 0; 630 } 631 632 static const struct snd_soc_component_driver sun8i_codec_analog_cmpnt_drv = { 633 .controls = sun8i_codec_common_controls, 634 .num_controls = ARRAY_SIZE(sun8i_codec_common_controls), 635 .dapm_widgets = sun8i_codec_common_widgets, 636 .num_dapm_widgets = ARRAY_SIZE(sun8i_codec_common_widgets), 637 .dapm_routes = sun8i_codec_common_routes, 638 .num_dapm_routes = ARRAY_SIZE(sun8i_codec_common_routes), 639 .probe = sun8i_codec_analog_cmpnt_probe, 640 }; 641 642 static const struct of_device_id sun8i_codec_analog_of_match[] = { 643 { 644 .compatible = "allwinner,sun8i-a23-codec-analog", 645 .data = &sun8i_a23_quirks, 646 }, 647 { 648 .compatible = "allwinner,sun8i-h3-codec-analog", 649 .data = &sun8i_h3_quirks, 650 }, 651 {} 652 }; 653 MODULE_DEVICE_TABLE(of, sun8i_codec_analog_of_match); 654 655 static int sun8i_codec_analog_probe(struct platform_device *pdev) 656 { 657 struct resource *res; 658 struct regmap *regmap; 659 void __iomem *base; 660 661 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 662 base = devm_ioremap_resource(&pdev->dev, res); 663 if (IS_ERR(base)) { 664 dev_err(&pdev->dev, "Failed to map the registers\n"); 665 return PTR_ERR(base); 666 } 667 668 regmap = devm_regmap_init(&pdev->dev, NULL, base, &adda_pr_regmap_cfg); 669 if (IS_ERR(regmap)) { 670 dev_err(&pdev->dev, "Failed to create regmap\n"); 671 return PTR_ERR(regmap); 672 } 673 674 return devm_snd_soc_register_component(&pdev->dev, 675 &sun8i_codec_analog_cmpnt_drv, 676 NULL, 0); 677 } 678 679 static struct platform_driver sun8i_codec_analog_driver = { 680 .driver = { 681 .name = "sun8i-codec-analog", 682 .of_match_table = sun8i_codec_analog_of_match, 683 }, 684 .probe = sun8i_codec_analog_probe, 685 }; 686 module_platform_driver(sun8i_codec_analog_driver); 687 688 MODULE_DESCRIPTION("Allwinner internal codec analog controls driver"); 689 MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>"); 690 MODULE_LICENSE("GPL"); 691 MODULE_ALIAS("platform:sun8i-codec-analog"); 692