1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * This driver supports the analog controls for the internal codec 4 * found in Allwinner's A64 SoC. 5 * 6 * Copyright (C) 2016 Chen-Yu Tsai <wens@csie.org> 7 * Copyright (C) 2017 Marcus Cooper <codekipper@gmail.com> 8 * Copyright (C) 2018 Vasily Khoruzhick <anarsoul@gmail.com> 9 * 10 * Based on sun8i-codec-analog.c 11 * 12 */ 13 14 #include <linux/io.h> 15 #include <linux/kernel.h> 16 #include <linux/mod_devicetable.h> 17 #include <linux/module.h> 18 #include <linux/platform_device.h> 19 #include <linux/regmap.h> 20 21 #include <sound/soc.h> 22 #include <sound/soc-dapm.h> 23 #include <sound/tlv.h> 24 25 #include "sun8i-adda-pr-regmap.h" 26 27 /* Codec analog control register offsets and bit fields */ 28 #define SUN50I_ADDA_HP_CTRL 0x00 29 #define SUN50I_ADDA_HP_CTRL_PA_CLK_GATE 7 30 #define SUN50I_ADDA_HP_CTRL_HPPA_EN 6 31 #define SUN50I_ADDA_HP_CTRL_HPVOL 0 32 33 #define SUN50I_ADDA_OL_MIX_CTRL 0x01 34 #define SUN50I_ADDA_OL_MIX_CTRL_MIC1 6 35 #define SUN50I_ADDA_OL_MIX_CTRL_MIC2 5 36 #define SUN50I_ADDA_OL_MIX_CTRL_PHONE 4 37 #define SUN50I_ADDA_OL_MIX_CTRL_PHONEN 3 38 #define SUN50I_ADDA_OL_MIX_CTRL_LINEINL 2 39 #define SUN50I_ADDA_OL_MIX_CTRL_DACL 1 40 #define SUN50I_ADDA_OL_MIX_CTRL_DACR 0 41 42 #define SUN50I_ADDA_OR_MIX_CTRL 0x02 43 #define SUN50I_ADDA_OR_MIX_CTRL_MIC1 6 44 #define SUN50I_ADDA_OR_MIX_CTRL_MIC2 5 45 #define SUN50I_ADDA_OR_MIX_CTRL_PHONE 4 46 #define SUN50I_ADDA_OR_MIX_CTRL_PHONEP 3 47 #define SUN50I_ADDA_OR_MIX_CTRL_LINEINR 2 48 #define SUN50I_ADDA_OR_MIX_CTRL_DACR 1 49 #define SUN50I_ADDA_OR_MIX_CTRL_DACL 0 50 51 #define SUN50I_ADDA_EARPIECE_CTRL0 0x03 52 #define SUN50I_ADDA_EARPIECE_CTRL0_EAR_RAMP_TIME 4 53 #define SUN50I_ADDA_EARPIECE_CTRL0_ESPSR 0 54 55 #define SUN50I_ADDA_EARPIECE_CTRL1 0x04 56 #define SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_EN 7 57 #define SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_MUTE 6 58 #define SUN50I_ADDA_EARPIECE_CTRL1_ESP_VOL 0 59 60 #define SUN50I_ADDA_LINEOUT_CTRL0 0x05 61 #define SUN50I_ADDA_LINEOUT_CTRL0_LEN 7 62 #define SUN50I_ADDA_LINEOUT_CTRL0_REN 6 63 #define SUN50I_ADDA_LINEOUT_CTRL0_LSRC_SEL 5 64 #define SUN50I_ADDA_LINEOUT_CTRL0_RSRC_SEL 4 65 66 #define SUN50I_ADDA_LINEOUT_CTRL1 0x06 67 #define SUN50I_ADDA_LINEOUT_CTRL1_VOL 0 68 69 #define SUN50I_ADDA_MIC1_CTRL 0x07 70 #define SUN50I_ADDA_MIC1_CTRL_MIC1G 4 71 #define SUN50I_ADDA_MIC1_CTRL_MIC1AMPEN 3 72 #define SUN50I_ADDA_MIC1_CTRL_MIC1BOOST 0 73 74 #define SUN50I_ADDA_MIC2_CTRL 0x08 75 #define SUN50I_ADDA_MIC2_CTRL_MIC2G 4 76 #define SUN50I_ADDA_MIC2_CTRL_MIC2AMPEN 3 77 #define SUN50I_ADDA_MIC2_CTRL_MIC2BOOST 0 78 79 #define SUN50I_ADDA_LINEIN_CTRL 0x09 80 #define SUN50I_ADDA_LINEIN_CTRL_LINEING 0 81 82 #define SUN50I_ADDA_MIX_DAC_CTRL 0x0a 83 #define SUN50I_ADDA_MIX_DAC_CTRL_DACAREN 7 84 #define SUN50I_ADDA_MIX_DAC_CTRL_DACALEN 6 85 #define SUN50I_ADDA_MIX_DAC_CTRL_RMIXEN 5 86 #define SUN50I_ADDA_MIX_DAC_CTRL_LMIXEN 4 87 #define SUN50I_ADDA_MIX_DAC_CTRL_RHPPAMUTE 3 88 #define SUN50I_ADDA_MIX_DAC_CTRL_LHPPAMUTE 2 89 #define SUN50I_ADDA_MIX_DAC_CTRL_RHPIS 1 90 #define SUN50I_ADDA_MIX_DAC_CTRL_LHPIS 0 91 92 #define SUN50I_ADDA_L_ADCMIX_SRC 0x0b 93 #define SUN50I_ADDA_L_ADCMIX_SRC_MIC1 6 94 #define SUN50I_ADDA_L_ADCMIX_SRC_MIC2 5 95 #define SUN50I_ADDA_L_ADCMIX_SRC_PHONE 4 96 #define SUN50I_ADDA_L_ADCMIX_SRC_PHONEN 3 97 #define SUN50I_ADDA_L_ADCMIX_SRC_LINEINL 2 98 #define SUN50I_ADDA_L_ADCMIX_SRC_OMIXRL 1 99 #define SUN50I_ADDA_L_ADCMIX_SRC_OMIXRR 0 100 101 #define SUN50I_ADDA_R_ADCMIX_SRC 0x0c 102 #define SUN50I_ADDA_R_ADCMIX_SRC_MIC1 6 103 #define SUN50I_ADDA_R_ADCMIX_SRC_MIC2 5 104 #define SUN50I_ADDA_R_ADCMIX_SRC_PHONE 4 105 #define SUN50I_ADDA_R_ADCMIX_SRC_PHONEP 3 106 #define SUN50I_ADDA_R_ADCMIX_SRC_LINEINR 2 107 #define SUN50I_ADDA_R_ADCMIX_SRC_OMIXR 1 108 #define SUN50I_ADDA_R_ADCMIX_SRC_OMIXL 0 109 110 #define SUN50I_ADDA_ADC_CTRL 0x0d 111 #define SUN50I_ADDA_ADC_CTRL_ADCREN 7 112 #define SUN50I_ADDA_ADC_CTRL_ADCLEN 6 113 #define SUN50I_ADDA_ADC_CTRL_ADCG 0 114 115 #define SUN50I_ADDA_HS_MBIAS_CTRL 0x0e 116 #define SUN50I_ADDA_HS_MBIAS_CTRL_MMICBIASEN 7 117 118 #define SUN50I_ADDA_MDET_CTRL 0x1c 119 #define SUN50I_ADDA_MDET_CTRL_SELDETADC_FS 4 120 #define SUN50I_ADDA_MDET_CTRL_SELDETADC_DB 2 121 #define SUN50I_ADDA_MDET_CTRL_SELDETADC_BF 0 122 123 #define SUN50I_ADDA_JACK_MIC_CTRL 0x1d 124 #define SUN50I_ADDA_JACK_MIC_CTRL_JACKDETEN 7 125 #define SUN50I_ADDA_JACK_MIC_CTRL_INNERRESEN 6 126 #define SUN50I_ADDA_JACK_MIC_CTRL_HMICBIASEN 5 127 #define SUN50I_ADDA_JACK_MIC_CTRL_MICADCEN 4 128 129 /* mixer controls */ 130 static const struct snd_kcontrol_new sun50i_a64_codec_mixer_controls[] = { 131 SOC_DAPM_DOUBLE_R("Mic1 Playback Switch", 132 SUN50I_ADDA_OL_MIX_CTRL, 133 SUN50I_ADDA_OR_MIX_CTRL, 134 SUN50I_ADDA_OL_MIX_CTRL_MIC1, 1, 0), 135 SOC_DAPM_DOUBLE_R("Mic2 Playback Switch", 136 SUN50I_ADDA_OL_MIX_CTRL, 137 SUN50I_ADDA_OR_MIX_CTRL, 138 SUN50I_ADDA_OL_MIX_CTRL_MIC2, 1, 0), 139 SOC_DAPM_DOUBLE_R("Line In Playback Switch", 140 SUN50I_ADDA_OL_MIX_CTRL, 141 SUN50I_ADDA_OR_MIX_CTRL, 142 SUN50I_ADDA_OL_MIX_CTRL_LINEINL, 1, 0), 143 SOC_DAPM_DOUBLE_R("DAC Playback Switch", 144 SUN50I_ADDA_OL_MIX_CTRL, 145 SUN50I_ADDA_OR_MIX_CTRL, 146 SUN50I_ADDA_OL_MIX_CTRL_DACL, 1, 0), 147 SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch", 148 SUN50I_ADDA_OL_MIX_CTRL, 149 SUN50I_ADDA_OR_MIX_CTRL, 150 SUN50I_ADDA_OL_MIX_CTRL_DACR, 1, 0), 151 }; 152 153 /* ADC mixer controls */ 154 static const struct snd_kcontrol_new sun50i_codec_adc_mixer_controls[] = { 155 SOC_DAPM_DOUBLE_R("Mic1 Capture Switch", 156 SUN50I_ADDA_L_ADCMIX_SRC, 157 SUN50I_ADDA_R_ADCMIX_SRC, 158 SUN50I_ADDA_L_ADCMIX_SRC_MIC1, 1, 0), 159 SOC_DAPM_DOUBLE_R("Mic2 Capture Switch", 160 SUN50I_ADDA_L_ADCMIX_SRC, 161 SUN50I_ADDA_R_ADCMIX_SRC, 162 SUN50I_ADDA_L_ADCMIX_SRC_MIC2, 1, 0), 163 SOC_DAPM_DOUBLE_R("Line In Capture Switch", 164 SUN50I_ADDA_L_ADCMIX_SRC, 165 SUN50I_ADDA_R_ADCMIX_SRC, 166 SUN50I_ADDA_L_ADCMIX_SRC_LINEINL, 1, 0), 167 SOC_DAPM_DOUBLE_R("Mixer Capture Switch", 168 SUN50I_ADDA_L_ADCMIX_SRC, 169 SUN50I_ADDA_R_ADCMIX_SRC, 170 SUN50I_ADDA_L_ADCMIX_SRC_OMIXRL, 1, 0), 171 SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch", 172 SUN50I_ADDA_L_ADCMIX_SRC, 173 SUN50I_ADDA_R_ADCMIX_SRC, 174 SUN50I_ADDA_L_ADCMIX_SRC_OMIXRR, 1, 0), 175 }; 176 177 static const DECLARE_TLV_DB_SCALE(sun50i_codec_out_mixer_pregain_scale, 178 -450, 150, 0); 179 static const DECLARE_TLV_DB_RANGE(sun50i_codec_mic_gain_scale, 180 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), 181 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0), 182 ); 183 184 static const DECLARE_TLV_DB_SCALE(sun50i_codec_hp_vol_scale, -6300, 100, 1); 185 186 static const DECLARE_TLV_DB_RANGE(sun50i_codec_lineout_vol_scale, 187 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), 188 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0), 189 ); 190 191 static const DECLARE_TLV_DB_RANGE(sun50i_codec_earpiece_vol_scale, 192 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), 193 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0), 194 ); 195 196 /* volume / mute controls */ 197 static const struct snd_kcontrol_new sun50i_a64_codec_controls[] = { 198 SOC_SINGLE_TLV("Headphone Playback Volume", 199 SUN50I_ADDA_HP_CTRL, 200 SUN50I_ADDA_HP_CTRL_HPVOL, 0x3f, 0, 201 sun50i_codec_hp_vol_scale), 202 203 /* Mixer pre-gain */ 204 SOC_SINGLE_TLV("Mic1 Playback Volume", SUN50I_ADDA_MIC1_CTRL, 205 SUN50I_ADDA_MIC1_CTRL_MIC1G, 206 0x7, 0, sun50i_codec_out_mixer_pregain_scale), 207 208 /* Microphone Amp boost gain */ 209 SOC_SINGLE_TLV("Mic1 Boost Volume", SUN50I_ADDA_MIC1_CTRL, 210 SUN50I_ADDA_MIC1_CTRL_MIC1BOOST, 0x7, 0, 211 sun50i_codec_mic_gain_scale), 212 213 /* Mixer pre-gain */ 214 SOC_SINGLE_TLV("Mic2 Playback Volume", 215 SUN50I_ADDA_MIC2_CTRL, SUN50I_ADDA_MIC2_CTRL_MIC2G, 216 0x7, 0, sun50i_codec_out_mixer_pregain_scale), 217 218 /* Microphone Amp boost gain */ 219 SOC_SINGLE_TLV("Mic2 Boost Volume", SUN50I_ADDA_MIC2_CTRL, 220 SUN50I_ADDA_MIC2_CTRL_MIC2BOOST, 0x7, 0, 221 sun50i_codec_mic_gain_scale), 222 223 /* ADC */ 224 SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN50I_ADDA_ADC_CTRL, 225 SUN50I_ADDA_ADC_CTRL_ADCG, 0x7, 0, 226 sun50i_codec_out_mixer_pregain_scale), 227 228 /* Mixer pre-gain */ 229 SOC_SINGLE_TLV("Line In Playback Volume", SUN50I_ADDA_LINEIN_CTRL, 230 SUN50I_ADDA_LINEIN_CTRL_LINEING, 231 0x7, 0, sun50i_codec_out_mixer_pregain_scale), 232 233 SOC_SINGLE_TLV("Line Out Playback Volume", 234 SUN50I_ADDA_LINEOUT_CTRL1, 235 SUN50I_ADDA_LINEOUT_CTRL1_VOL, 0x1f, 0, 236 sun50i_codec_lineout_vol_scale), 237 238 SOC_SINGLE_TLV("Earpiece Playback Volume", 239 SUN50I_ADDA_EARPIECE_CTRL1, 240 SUN50I_ADDA_EARPIECE_CTRL1_ESP_VOL, 0x1f, 0, 241 sun50i_codec_earpiece_vol_scale), 242 }; 243 244 static const char * const sun50i_codec_hp_src_enum_text[] = { 245 "DAC", "Mixer", 246 }; 247 248 static SOC_ENUM_DOUBLE_DECL(sun50i_codec_hp_src_enum, 249 SUN50I_ADDA_MIX_DAC_CTRL, 250 SUN50I_ADDA_MIX_DAC_CTRL_LHPIS, 251 SUN50I_ADDA_MIX_DAC_CTRL_RHPIS, 252 sun50i_codec_hp_src_enum_text); 253 254 static const struct snd_kcontrol_new sun50i_codec_hp_src[] = { 255 SOC_DAPM_ENUM("Headphone Source Playback Route", 256 sun50i_codec_hp_src_enum), 257 }; 258 259 static const struct snd_kcontrol_new sun50i_codec_hp_switch = 260 SOC_DAPM_DOUBLE("Headphone Playback Switch", 261 SUN50I_ADDA_MIX_DAC_CTRL, 262 SUN50I_ADDA_MIX_DAC_CTRL_LHPPAMUTE, 263 SUN50I_ADDA_MIX_DAC_CTRL_RHPPAMUTE, 1, 0); 264 265 static const char * const sun50i_codec_lineout_src_enum_text[] = { 266 "Stereo", "Mono Differential", 267 }; 268 269 static SOC_ENUM_DOUBLE_DECL(sun50i_codec_lineout_src_enum, 270 SUN50I_ADDA_LINEOUT_CTRL0, 271 SUN50I_ADDA_LINEOUT_CTRL0_LSRC_SEL, 272 SUN50I_ADDA_LINEOUT_CTRL0_RSRC_SEL, 273 sun50i_codec_lineout_src_enum_text); 274 275 static const struct snd_kcontrol_new sun50i_codec_lineout_src[] = { 276 SOC_DAPM_ENUM("Line Out Source Playback Route", 277 sun50i_codec_lineout_src_enum), 278 }; 279 280 static const struct snd_kcontrol_new sun50i_codec_lineout_switch = 281 SOC_DAPM_DOUBLE("Line Out Playback Switch", 282 SUN50I_ADDA_LINEOUT_CTRL0, 283 SUN50I_ADDA_LINEOUT_CTRL0_LEN, 284 SUN50I_ADDA_LINEOUT_CTRL0_REN, 1, 0); 285 286 static const char * const sun50i_codec_earpiece_src_enum_text[] = { 287 "DACR", "DACL", "Right Mixer", "Left Mixer", 288 }; 289 290 static SOC_ENUM_SINGLE_DECL(sun50i_codec_earpiece_src_enum, 291 SUN50I_ADDA_EARPIECE_CTRL0, 292 SUN50I_ADDA_EARPIECE_CTRL0_ESPSR, 293 sun50i_codec_earpiece_src_enum_text); 294 295 static const struct snd_kcontrol_new sun50i_codec_earpiece_src[] = { 296 SOC_DAPM_ENUM("Earpiece Source Playback Route", 297 sun50i_codec_earpiece_src_enum), 298 }; 299 300 static const struct snd_kcontrol_new sun50i_codec_earpiece_switch[] = { 301 SOC_DAPM_SINGLE("Earpiece Playback Switch", 302 SUN50I_ADDA_EARPIECE_CTRL1, 303 SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_MUTE, 1, 0), 304 }; 305 306 static int sun50i_codec_hbias_event(struct snd_soc_dapm_widget *w, 307 struct snd_kcontrol *kcontrol, int event) 308 { 309 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 310 u32 value = !!SND_SOC_DAPM_EVENT_ON(event); 311 312 regmap_update_bits(component->regmap, SUN50I_ADDA_JACK_MIC_CTRL, 313 BIT(SUN50I_ADDA_JACK_MIC_CTRL_MICADCEN), 314 value << SUN50I_ADDA_JACK_MIC_CTRL_MICADCEN); 315 316 return 0; 317 } 318 319 static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets[] = { 320 /* DAC */ 321 SND_SOC_DAPM_DAC("Left DAC", NULL, SUN50I_ADDA_MIX_DAC_CTRL, 322 SUN50I_ADDA_MIX_DAC_CTRL_DACALEN, 0), 323 SND_SOC_DAPM_DAC("Right DAC", NULL, SUN50I_ADDA_MIX_DAC_CTRL, 324 SUN50I_ADDA_MIX_DAC_CTRL_DACAREN, 0), 325 /* ADC */ 326 SND_SOC_DAPM_ADC("Left ADC", NULL, SUN50I_ADDA_ADC_CTRL, 327 SUN50I_ADDA_ADC_CTRL_ADCLEN, 0), 328 SND_SOC_DAPM_ADC("Right ADC", NULL, SUN50I_ADDA_ADC_CTRL, 329 SUN50I_ADDA_ADC_CTRL_ADCREN, 0), 330 /* 331 * Due to this component and the codec belonging to separate DAPM 332 * contexts, we need to manually link the above widgets to their 333 * stream widgets at the card level. 334 */ 335 336 SND_SOC_DAPM_REGULATOR_SUPPLY("cpvdd", 0, 0), 337 SND_SOC_DAPM_MUX("Left Headphone Source", 338 SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src), 339 SND_SOC_DAPM_MUX("Right Headphone Source", 340 SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src), 341 SND_SOC_DAPM_SWITCH("Left Headphone Switch", 342 SND_SOC_NOPM, 0, 0, &sun50i_codec_hp_switch), 343 SND_SOC_DAPM_SWITCH("Right Headphone Switch", 344 SND_SOC_NOPM, 0, 0, &sun50i_codec_hp_switch), 345 SND_SOC_DAPM_OUT_DRV("Left Headphone Amp", 346 SND_SOC_NOPM, 0, 0, NULL, 0), 347 SND_SOC_DAPM_OUT_DRV("Right Headphone Amp", 348 SND_SOC_NOPM, 0, 0, NULL, 0), 349 SND_SOC_DAPM_SUPPLY("Headphone Amp", SUN50I_ADDA_HP_CTRL, 350 SUN50I_ADDA_HP_CTRL_HPPA_EN, 0, NULL, 0), 351 SND_SOC_DAPM_OUTPUT("HP"), 352 353 SND_SOC_DAPM_MUX("Left Line Out Source", 354 SND_SOC_NOPM, 0, 0, sun50i_codec_lineout_src), 355 SND_SOC_DAPM_MUX("Right Line Out Source", 356 SND_SOC_NOPM, 0, 0, sun50i_codec_lineout_src), 357 SND_SOC_DAPM_SWITCH("Left Line Out Switch", 358 SND_SOC_NOPM, 0, 0, &sun50i_codec_lineout_switch), 359 SND_SOC_DAPM_SWITCH("Right Line Out Switch", 360 SND_SOC_NOPM, 0, 0, &sun50i_codec_lineout_switch), 361 SND_SOC_DAPM_OUTPUT("LINEOUT"), 362 363 SND_SOC_DAPM_MUX("Earpiece Source Playback Route", 364 SND_SOC_NOPM, 0, 0, sun50i_codec_earpiece_src), 365 SOC_MIXER_NAMED_CTL_ARRAY("Earpiece Switch", 366 SND_SOC_NOPM, 0, 0, 367 sun50i_codec_earpiece_switch), 368 SND_SOC_DAPM_OUT_DRV("Earpiece Amp", SUN50I_ADDA_EARPIECE_CTRL1, 369 SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_EN, 0, NULL, 0), 370 SND_SOC_DAPM_OUTPUT("EARPIECE"), 371 372 /* Microphone inputs */ 373 SND_SOC_DAPM_INPUT("MIC1"), 374 375 /* Microphone Bias */ 376 SND_SOC_DAPM_SUPPLY("MBIAS", SUN50I_ADDA_HS_MBIAS_CTRL, 377 SUN50I_ADDA_HS_MBIAS_CTRL_MMICBIASEN, 378 0, NULL, 0), 379 380 /* Mic input path */ 381 SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN50I_ADDA_MIC1_CTRL, 382 SUN50I_ADDA_MIC1_CTRL_MIC1AMPEN, 0, NULL, 0), 383 384 /* Microphone input */ 385 SND_SOC_DAPM_INPUT("MIC2"), 386 387 /* Microphone Bias */ 388 SND_SOC_DAPM_SUPPLY("HBIAS", SUN50I_ADDA_JACK_MIC_CTRL, 389 SUN50I_ADDA_JACK_MIC_CTRL_HMICBIASEN, 390 0, sun50i_codec_hbias_event, 391 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), 392 393 /* Mic input path */ 394 SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN50I_ADDA_MIC2_CTRL, 395 SUN50I_ADDA_MIC2_CTRL_MIC2AMPEN, 0, NULL, 0), 396 397 /* Line input */ 398 SND_SOC_DAPM_INPUT("LINEIN"), 399 400 /* Mixers */ 401 SND_SOC_DAPM_MIXER("Left Mixer", SUN50I_ADDA_MIX_DAC_CTRL, 402 SUN50I_ADDA_MIX_DAC_CTRL_LMIXEN, 0, 403 sun50i_a64_codec_mixer_controls, 404 ARRAY_SIZE(sun50i_a64_codec_mixer_controls)), 405 SND_SOC_DAPM_MIXER("Right Mixer", SUN50I_ADDA_MIX_DAC_CTRL, 406 SUN50I_ADDA_MIX_DAC_CTRL_RMIXEN, 0, 407 sun50i_a64_codec_mixer_controls, 408 ARRAY_SIZE(sun50i_a64_codec_mixer_controls)), 409 SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0, 410 sun50i_codec_adc_mixer_controls, 411 ARRAY_SIZE(sun50i_codec_adc_mixer_controls)), 412 SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0, 413 sun50i_codec_adc_mixer_controls, 414 ARRAY_SIZE(sun50i_codec_adc_mixer_controls)), 415 }; 416 417 static const struct snd_soc_dapm_route sun50i_a64_codec_routes[] = { 418 /* Left Mixer Routes */ 419 { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, 420 { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, 421 { "Left Mixer", "Line In Playback Switch", "LINEIN" }, 422 { "Left Mixer", "DAC Playback Switch", "Left DAC" }, 423 { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" }, 424 425 /* Right Mixer Routes */ 426 { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, 427 { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, 428 { "Right Mixer", "Line In Playback Switch", "LINEIN" }, 429 { "Right Mixer", "DAC Playback Switch", "Right DAC" }, 430 { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" }, 431 432 /* Left ADC Mixer Routes */ 433 { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, 434 { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, 435 { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" }, 436 { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" }, 437 { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" }, 438 439 /* Right ADC Mixer Routes */ 440 { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, 441 { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, 442 { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" }, 443 { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" }, 444 { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" }, 445 446 /* ADC Routes */ 447 { "Left ADC", NULL, "Left ADC Mixer" }, 448 { "Right ADC", NULL, "Right ADC Mixer" }, 449 450 /* Headphone Routes */ 451 { "Left Headphone Source", "DAC", "Left DAC" }, 452 { "Left Headphone Source", "Mixer", "Left Mixer" }, 453 { "Left Headphone Switch", "Headphone Playback Switch", "Left Headphone Source" }, 454 { "Left Headphone Amp", NULL, "Left Headphone Switch" }, 455 { "Left Headphone Amp", NULL, "Headphone Amp" }, 456 { "HP", NULL, "Left Headphone Amp" }, 457 458 { "Right Headphone Source", "DAC", "Right DAC" }, 459 { "Right Headphone Source", "Mixer", "Right Mixer" }, 460 { "Right Headphone Switch", "Headphone Playback Switch", "Right Headphone Source" }, 461 { "Right Headphone Amp", NULL, "Right Headphone Switch" }, 462 { "Right Headphone Amp", NULL, "Headphone Amp" }, 463 { "HP", NULL, "Right Headphone Amp" }, 464 465 { "Headphone Amp", NULL, "cpvdd" }, 466 467 /* Microphone Routes */ 468 { "Mic1 Amplifier", NULL, "MIC1"}, 469 470 /* Microphone Routes */ 471 { "Mic2 Amplifier", NULL, "MIC2"}, 472 473 /* Line-out Routes */ 474 { "Left Line Out Source", "Stereo", "Left Mixer" }, 475 { "Left Line Out Source", "Mono Differential", "Left Mixer" }, 476 { "Left Line Out Source", "Mono Differential", "Right Mixer" }, 477 { "Left Line Out Switch", "Line Out Playback Switch", "Left Line Out Source" }, 478 { "LINEOUT", NULL, "Left Line Out Switch" }, 479 480 { "Right Line Out Switch", "Line Out Playback Switch", "Right Mixer" }, 481 { "Right Line Out Source", "Stereo", "Right Line Out Switch" }, 482 { "Right Line Out Source", "Mono Differential", "Left Line Out Switch" }, 483 { "LINEOUT", NULL, "Right Line Out Source" }, 484 485 /* Earpiece Routes */ 486 { "Earpiece Source Playback Route", "DACL", "Left DAC" }, 487 { "Earpiece Source Playback Route", "DACR", "Right DAC" }, 488 { "Earpiece Source Playback Route", "Left Mixer", "Left Mixer" }, 489 { "Earpiece Source Playback Route", "Right Mixer", "Right Mixer" }, 490 { "Earpiece Switch", "Earpiece Playback Switch", "Earpiece Source Playback Route" }, 491 { "Earpiece Amp", NULL, "Earpiece Switch" }, 492 { "EARPIECE", NULL, "Earpiece Amp" }, 493 }; 494 495 static int sun50i_a64_codec_set_bias_level(struct snd_soc_component *component, 496 enum snd_soc_bias_level level) 497 { 498 struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 499 int hbias; 500 501 switch (level) { 502 case SND_SOC_BIAS_OFF: 503 regmap_clear_bits(component->regmap, SUN50I_ADDA_JACK_MIC_CTRL, 504 BIT(SUN50I_ADDA_JACK_MIC_CTRL_JACKDETEN) | 505 BIT(SUN50I_ADDA_JACK_MIC_CTRL_MICADCEN)); 506 507 regmap_set_bits(component->regmap, SUN50I_ADDA_HP_CTRL, 508 BIT(SUN50I_ADDA_HP_CTRL_PA_CLK_GATE)); 509 break; 510 case SND_SOC_BIAS_STANDBY: 511 regmap_clear_bits(component->regmap, SUN50I_ADDA_HP_CTRL, 512 BIT(SUN50I_ADDA_HP_CTRL_PA_CLK_GATE)); 513 514 hbias = snd_soc_dapm_get_pin_status(dapm, "HBIAS"); 515 regmap_update_bits(component->regmap, SUN50I_ADDA_JACK_MIC_CTRL, 516 BIT(SUN50I_ADDA_JACK_MIC_CTRL_JACKDETEN) | 517 BIT(SUN50I_ADDA_JACK_MIC_CTRL_MICADCEN), 518 BIT(SUN50I_ADDA_JACK_MIC_CTRL_JACKDETEN) | 519 hbias << SUN50I_ADDA_JACK_MIC_CTRL_MICADCEN); 520 break; 521 default: 522 break; 523 } 524 525 return 0; 526 } 527 528 static const struct snd_soc_component_driver sun50i_codec_analog_cmpnt_drv = { 529 .controls = sun50i_a64_codec_controls, 530 .num_controls = ARRAY_SIZE(sun50i_a64_codec_controls), 531 .dapm_widgets = sun50i_a64_codec_widgets, 532 .num_dapm_widgets = ARRAY_SIZE(sun50i_a64_codec_widgets), 533 .dapm_routes = sun50i_a64_codec_routes, 534 .num_dapm_routes = ARRAY_SIZE(sun50i_a64_codec_routes), 535 .set_bias_level = sun50i_a64_codec_set_bias_level, 536 .idle_bias_on = true, 537 .suspend_bias_off = true, 538 }; 539 540 static const struct of_device_id sun50i_codec_analog_of_match[] = { 541 { 542 .compatible = "allwinner,sun50i-a64-codec-analog", 543 }, 544 {} 545 }; 546 MODULE_DEVICE_TABLE(of, sun50i_codec_analog_of_match); 547 548 static int sun50i_codec_analog_probe(struct platform_device *pdev) 549 { 550 struct regmap *regmap; 551 void __iomem *base; 552 bool enable; 553 554 base = devm_platform_ioremap_resource(pdev, 0); 555 if (IS_ERR(base)) { 556 dev_err(&pdev->dev, "Failed to map the registers\n"); 557 return PTR_ERR(base); 558 } 559 560 regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base); 561 if (IS_ERR(regmap)) { 562 dev_err(&pdev->dev, "Failed to create regmap\n"); 563 return PTR_ERR(regmap); 564 } 565 566 enable = device_property_read_bool(&pdev->dev, 567 "allwinner,internal-bias-resistor"); 568 regmap_update_bits(regmap, SUN50I_ADDA_JACK_MIC_CTRL, 569 BIT(SUN50I_ADDA_JACK_MIC_CTRL_INNERRESEN), 570 enable << SUN50I_ADDA_JACK_MIC_CTRL_INNERRESEN); 571 572 /* Select sample interval of the ADC sample to 16ms */ 573 regmap_update_bits(regmap, SUN50I_ADDA_MDET_CTRL, 574 0x7 << SUN50I_ADDA_MDET_CTRL_SELDETADC_FS | 575 0x3 << SUN50I_ADDA_MDET_CTRL_SELDETADC_BF, 576 0x3 << SUN50I_ADDA_MDET_CTRL_SELDETADC_FS | 577 0x3 << SUN50I_ADDA_MDET_CTRL_SELDETADC_BF); 578 579 return devm_snd_soc_register_component(&pdev->dev, 580 &sun50i_codec_analog_cmpnt_drv, 581 NULL, 0); 582 } 583 584 static struct platform_driver sun50i_codec_analog_driver = { 585 .driver = { 586 .name = "sun50i-codec-analog", 587 .of_match_table = sun50i_codec_analog_of_match, 588 }, 589 .probe = sun50i_codec_analog_probe, 590 }; 591 module_platform_driver(sun50i_codec_analog_driver); 592 593 MODULE_DESCRIPTION("Allwinner internal codec analog controls driver for A64"); 594 MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>"); 595 MODULE_LICENSE("GPL"); 596 MODULE_ALIAS("platform:sun50i-codec-analog"); 597