sun50i-codec-analog.c (19118B)
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 */ 123static 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 */ 147static 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 170static const DECLARE_TLV_DB_SCALE(sun50i_codec_out_mixer_pregain_scale, 171 -450, 150, 0); 172static 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 177static const DECLARE_TLV_DB_SCALE(sun50i_codec_hp_vol_scale, -6300, 100, 1); 178 179static 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 184static 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 */ 190static 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 237static const char * const sun50i_codec_hp_src_enum_text[] = { 238 "DAC", "Mixer", 239}; 240 241static 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 247static 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 252static 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 258static const char * const sun50i_codec_lineout_src_enum_text[] = { 259 "Stereo", "Mono Differential", 260}; 261 262static 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 268static 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 273static 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 279static const char * const sun50i_codec_earpiece_src_enum_text[] = { 280 "DACR", "DACL", "Right Mixer", "Left Mixer", 281}; 282 283static 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 288static 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 293static 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 299static 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 396static 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 474static 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 481static 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 487static 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 498static const struct of_device_id sun50i_codec_analog_of_match[] = { 499 { 500 .compatible = "allwinner,sun50i-a64-codec-analog", 501 }, 502 {} 503}; 504MODULE_DEVICE_TABLE(of, sun50i_codec_analog_of_match); 505 506static 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 528static 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}; 535module_platform_driver(sun50i_codec_analog_driver); 536 537MODULE_DESCRIPTION("Allwinner internal codec analog controls driver for A64"); 538MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>"); 539MODULE_LICENSE("GPL"); 540MODULE_ALIAS("platform:sun50i-codec-analog");