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