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