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_LINEOUT_CTRL0 0x05 53 #define SUN50I_ADDA_LINEOUT_CTRL0_LEN 7 54 #define SUN50I_ADDA_LINEOUT_CTRL0_REN 6 55 #define SUN50I_ADDA_LINEOUT_CTRL0_LSRC_SEL 5 56 #define SUN50I_ADDA_LINEOUT_CTRL0_RSRC_SEL 4 57 58 #define SUN50I_ADDA_LINEOUT_CTRL1 0x06 59 #define SUN50I_ADDA_LINEOUT_CTRL1_VOL 0 60 61 #define SUN50I_ADDA_MIC1_CTRL 0x07 62 #define SUN50I_ADDA_MIC1_CTRL_MIC1G 4 63 #define SUN50I_ADDA_MIC1_CTRL_MIC1AMPEN 3 64 #define SUN50I_ADDA_MIC1_CTRL_MIC1BOOST 0 65 66 #define SUN50I_ADDA_MIC2_CTRL 0x08 67 #define SUN50I_ADDA_MIC2_CTRL_MIC2G 4 68 #define SUN50I_ADDA_MIC2_CTRL_MIC2AMPEN 3 69 #define SUN50I_ADDA_MIC2_CTRL_MIC2BOOST 0 70 71 #define SUN50I_ADDA_LINEIN_CTRL 0x09 72 #define SUN50I_ADDA_LINEIN_CTRL_LINEING 0 73 74 #define SUN50I_ADDA_MIX_DAC_CTRL 0x0a 75 #define SUN50I_ADDA_MIX_DAC_CTRL_DACAREN 7 76 #define SUN50I_ADDA_MIX_DAC_CTRL_DACALEN 6 77 #define SUN50I_ADDA_MIX_DAC_CTRL_RMIXEN 5 78 #define SUN50I_ADDA_MIX_DAC_CTRL_LMIXEN 4 79 #define SUN50I_ADDA_MIX_DAC_CTRL_RHPPAMUTE 3 80 #define SUN50I_ADDA_MIX_DAC_CTRL_LHPPAMUTE 2 81 #define SUN50I_ADDA_MIX_DAC_CTRL_RHPIS 1 82 #define SUN50I_ADDA_MIX_DAC_CTRL_LHPIS 0 83 84 #define SUN50I_ADDA_L_ADCMIX_SRC 0x0b 85 #define SUN50I_ADDA_L_ADCMIX_SRC_MIC1 6 86 #define SUN50I_ADDA_L_ADCMIX_SRC_MIC2 5 87 #define SUN50I_ADDA_L_ADCMIX_SRC_PHONE 4 88 #define SUN50I_ADDA_L_ADCMIX_SRC_PHONEN 3 89 #define SUN50I_ADDA_L_ADCMIX_SRC_LINEINL 2 90 #define SUN50I_ADDA_L_ADCMIX_SRC_OMIXRL 1 91 #define SUN50I_ADDA_L_ADCMIX_SRC_OMIXRR 0 92 93 #define SUN50I_ADDA_R_ADCMIX_SRC 0x0c 94 #define SUN50I_ADDA_R_ADCMIX_SRC_MIC1 6 95 #define SUN50I_ADDA_R_ADCMIX_SRC_MIC2 5 96 #define SUN50I_ADDA_R_ADCMIX_SRC_PHONE 4 97 #define SUN50I_ADDA_R_ADCMIX_SRC_PHONEP 3 98 #define SUN50I_ADDA_R_ADCMIX_SRC_LINEINR 2 99 #define SUN50I_ADDA_R_ADCMIX_SRC_OMIXR 1 100 #define SUN50I_ADDA_R_ADCMIX_SRC_OMIXL 0 101 102 #define SUN50I_ADDA_ADC_CTRL 0x0d 103 #define SUN50I_ADDA_ADC_CTRL_ADCREN 7 104 #define SUN50I_ADDA_ADC_CTRL_ADCLEN 6 105 #define SUN50I_ADDA_ADC_CTRL_ADCG 0 106 107 #define SUN50I_ADDA_HS_MBIAS_CTRL 0x0e 108 #define SUN50I_ADDA_HS_MBIAS_CTRL_MMICBIASEN 7 109 110 #define SUN50I_ADDA_JACK_MIC_CTRL 0x1d 111 #define SUN50I_ADDA_JACK_MIC_CTRL_HMICBIASEN 5 112 113 /* mixer controls */ 114 static const struct snd_kcontrol_new sun50i_a64_codec_mixer_controls[] = { 115 SOC_DAPM_DOUBLE_R("DAC Playback Switch", 116 SUN50I_ADDA_OL_MIX_CTRL, 117 SUN50I_ADDA_OR_MIX_CTRL, 118 SUN50I_ADDA_OL_MIX_CTRL_DACL, 1, 0), 119 SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch", 120 SUN50I_ADDA_OL_MIX_CTRL, 121 SUN50I_ADDA_OR_MIX_CTRL, 122 SUN50I_ADDA_OL_MIX_CTRL_DACR, 1, 0), 123 SOC_DAPM_DOUBLE_R("Line In Playback Switch", 124 SUN50I_ADDA_OL_MIX_CTRL, 125 SUN50I_ADDA_OR_MIX_CTRL, 126 SUN50I_ADDA_OL_MIX_CTRL_LINEINL, 1, 0), 127 SOC_DAPM_DOUBLE_R("Mic1 Playback Switch", 128 SUN50I_ADDA_OL_MIX_CTRL, 129 SUN50I_ADDA_OR_MIX_CTRL, 130 SUN50I_ADDA_OL_MIX_CTRL_MIC1, 1, 0), 131 SOC_DAPM_DOUBLE_R("Mic2 Playback Switch", 132 SUN50I_ADDA_OL_MIX_CTRL, 133 SUN50I_ADDA_OR_MIX_CTRL, 134 SUN50I_ADDA_OL_MIX_CTRL_MIC2, 1, 0), 135 }; 136 137 /* ADC mixer controls */ 138 static const struct snd_kcontrol_new sun50i_codec_adc_mixer_controls[] = { 139 SOC_DAPM_DOUBLE_R("Mixer Capture Switch", 140 SUN50I_ADDA_L_ADCMIX_SRC, 141 SUN50I_ADDA_R_ADCMIX_SRC, 142 SUN50I_ADDA_L_ADCMIX_SRC_OMIXRL, 1, 0), 143 SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch", 144 SUN50I_ADDA_L_ADCMIX_SRC, 145 SUN50I_ADDA_R_ADCMIX_SRC, 146 SUN50I_ADDA_L_ADCMIX_SRC_OMIXRR, 1, 0), 147 SOC_DAPM_DOUBLE_R("Line In Capture Switch", 148 SUN50I_ADDA_L_ADCMIX_SRC, 149 SUN50I_ADDA_R_ADCMIX_SRC, 150 SUN50I_ADDA_L_ADCMIX_SRC_LINEINL, 1, 0), 151 SOC_DAPM_DOUBLE_R("Mic1 Capture Switch", 152 SUN50I_ADDA_L_ADCMIX_SRC, 153 SUN50I_ADDA_R_ADCMIX_SRC, 154 SUN50I_ADDA_L_ADCMIX_SRC_MIC1, 1, 0), 155 SOC_DAPM_DOUBLE_R("Mic2 Capture Switch", 156 SUN50I_ADDA_L_ADCMIX_SRC, 157 SUN50I_ADDA_R_ADCMIX_SRC, 158 SUN50I_ADDA_L_ADCMIX_SRC_MIC2, 1, 0), 159 }; 160 161 static const DECLARE_TLV_DB_SCALE(sun50i_codec_out_mixer_pregain_scale, 162 -450, 150, 0); 163 static const DECLARE_TLV_DB_RANGE(sun50i_codec_mic_gain_scale, 164 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), 165 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0), 166 ); 167 168 static const DECLARE_TLV_DB_SCALE(sun50i_codec_hp_vol_scale, -6300, 100, 1); 169 170 static const DECLARE_TLV_DB_RANGE(sun50i_codec_lineout_vol_scale, 171 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), 172 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0), 173 ); 174 175 176 /* volume / mute controls */ 177 static const struct snd_kcontrol_new sun50i_a64_codec_controls[] = { 178 SOC_SINGLE_TLV("Headphone Playback Volume", 179 SUN50I_ADDA_HP_CTRL, 180 SUN50I_ADDA_HP_CTRL_HPVOL, 0x3f, 0, 181 sun50i_codec_hp_vol_scale), 182 183 SOC_DOUBLE("Headphone Playback Switch", 184 SUN50I_ADDA_MIX_DAC_CTRL, 185 SUN50I_ADDA_MIX_DAC_CTRL_LHPPAMUTE, 186 SUN50I_ADDA_MIX_DAC_CTRL_RHPPAMUTE, 1, 0), 187 188 /* Mixer pre-gain */ 189 SOC_SINGLE_TLV("Mic1 Playback Volume", SUN50I_ADDA_MIC1_CTRL, 190 SUN50I_ADDA_MIC1_CTRL_MIC1G, 191 0x7, 0, sun50i_codec_out_mixer_pregain_scale), 192 193 /* Microphone Amp boost gain */ 194 SOC_SINGLE_TLV("Mic1 Boost Volume", SUN50I_ADDA_MIC1_CTRL, 195 SUN50I_ADDA_MIC1_CTRL_MIC1BOOST, 0x7, 0, 196 sun50i_codec_mic_gain_scale), 197 198 /* Mixer pre-gain */ 199 SOC_SINGLE_TLV("Mic2 Playback Volume", 200 SUN50I_ADDA_MIC2_CTRL, SUN50I_ADDA_MIC2_CTRL_MIC2G, 201 0x7, 0, sun50i_codec_out_mixer_pregain_scale), 202 203 /* Microphone Amp boost gain */ 204 SOC_SINGLE_TLV("Mic2 Boost Volume", SUN50I_ADDA_MIC2_CTRL, 205 SUN50I_ADDA_MIC2_CTRL_MIC2BOOST, 0x7, 0, 206 sun50i_codec_mic_gain_scale), 207 208 /* ADC */ 209 SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN50I_ADDA_ADC_CTRL, 210 SUN50I_ADDA_ADC_CTRL_ADCG, 0x7, 0, 211 sun50i_codec_out_mixer_pregain_scale), 212 213 /* Mixer pre-gain */ 214 SOC_SINGLE_TLV("Line In Playback Volume", SUN50I_ADDA_LINEIN_CTRL, 215 SUN50I_ADDA_LINEIN_CTRL_LINEING, 216 0x7, 0, sun50i_codec_out_mixer_pregain_scale), 217 218 SOC_SINGLE_TLV("Line Out Playback Volume", 219 SUN50I_ADDA_LINEOUT_CTRL1, 220 SUN50I_ADDA_LINEOUT_CTRL1_VOL, 0x1f, 0, 221 sun50i_codec_lineout_vol_scale), 222 223 SOC_DOUBLE("Line Out Playback Switch", 224 SUN50I_ADDA_LINEOUT_CTRL0, 225 SUN50I_ADDA_LINEOUT_CTRL0_LEN, 226 SUN50I_ADDA_LINEOUT_CTRL0_REN, 1, 0), 227 228 }; 229 230 static const char * const sun50i_codec_hp_src_enum_text[] = { 231 "DAC", "Mixer", 232 }; 233 234 static SOC_ENUM_DOUBLE_DECL(sun50i_codec_hp_src_enum, 235 SUN50I_ADDA_MIX_DAC_CTRL, 236 SUN50I_ADDA_MIX_DAC_CTRL_LHPIS, 237 SUN50I_ADDA_MIX_DAC_CTRL_RHPIS, 238 sun50i_codec_hp_src_enum_text); 239 240 static const struct snd_kcontrol_new sun50i_codec_hp_src[] = { 241 SOC_DAPM_ENUM("Headphone Source Playback Route", 242 sun50i_codec_hp_src_enum), 243 }; 244 245 static const char * const sun50i_codec_lineout_src_enum_text[] = { 246 "Stereo", "Mono Differential", 247 }; 248 249 static SOC_ENUM_DOUBLE_DECL(sun50i_codec_lineout_src_enum, 250 SUN50I_ADDA_LINEOUT_CTRL0, 251 SUN50I_ADDA_LINEOUT_CTRL0_LSRC_SEL, 252 SUN50I_ADDA_LINEOUT_CTRL0_RSRC_SEL, 253 sun50i_codec_lineout_src_enum_text); 254 255 static const struct snd_kcontrol_new sun50i_codec_lineout_src[] = { 256 SOC_DAPM_ENUM("Line Out Source Playback Route", 257 sun50i_codec_lineout_src_enum), 258 }; 259 260 static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets[] = { 261 /* DAC */ 262 SND_SOC_DAPM_DAC("Left DAC", NULL, SUN50I_ADDA_MIX_DAC_CTRL, 263 SUN50I_ADDA_MIX_DAC_CTRL_DACALEN, 0), 264 SND_SOC_DAPM_DAC("Right DAC", NULL, SUN50I_ADDA_MIX_DAC_CTRL, 265 SUN50I_ADDA_MIX_DAC_CTRL_DACAREN, 0), 266 /* ADC */ 267 SND_SOC_DAPM_ADC("Left ADC", NULL, SUN50I_ADDA_ADC_CTRL, 268 SUN50I_ADDA_ADC_CTRL_ADCLEN, 0), 269 SND_SOC_DAPM_ADC("Right ADC", NULL, SUN50I_ADDA_ADC_CTRL, 270 SUN50I_ADDA_ADC_CTRL_ADCREN, 0), 271 /* 272 * Due to this component and the codec belonging to separate DAPM 273 * contexts, we need to manually link the above widgets to their 274 * stream widgets at the card level. 275 */ 276 277 SND_SOC_DAPM_REGULATOR_SUPPLY("hpvcc", 0, 0), 278 SND_SOC_DAPM_MUX("Headphone Source Playback Route", 279 SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src), 280 SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN50I_ADDA_HP_CTRL, 281 SUN50I_ADDA_HP_CTRL_HPPA_EN, 0, NULL, 0), 282 SND_SOC_DAPM_OUTPUT("HP"), 283 284 SND_SOC_DAPM_MUX("Line Out Source Playback Route", 285 SND_SOC_NOPM, 0, 0, sun50i_codec_lineout_src), 286 SND_SOC_DAPM_OUTPUT("LINEOUT"), 287 288 /* Microphone inputs */ 289 SND_SOC_DAPM_INPUT("MIC1"), 290 291 /* Microphone Bias */ 292 SND_SOC_DAPM_SUPPLY("MBIAS", SUN50I_ADDA_HS_MBIAS_CTRL, 293 SUN50I_ADDA_HS_MBIAS_CTRL_MMICBIASEN, 294 0, NULL, 0), 295 296 /* Mic input path */ 297 SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN50I_ADDA_MIC1_CTRL, 298 SUN50I_ADDA_MIC1_CTRL_MIC1AMPEN, 0, NULL, 0), 299 300 /* Microphone input */ 301 SND_SOC_DAPM_INPUT("MIC2"), 302 303 /* Microphone Bias */ 304 SND_SOC_DAPM_SUPPLY("HBIAS", SUN50I_ADDA_JACK_MIC_CTRL, 305 SUN50I_ADDA_JACK_MIC_CTRL_HMICBIASEN, 306 0, NULL, 0), 307 308 /* Mic input path */ 309 SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN50I_ADDA_MIC2_CTRL, 310 SUN50I_ADDA_MIC2_CTRL_MIC2AMPEN, 0, NULL, 0), 311 312 /* Line input */ 313 SND_SOC_DAPM_INPUT("LINEIN"), 314 315 /* Mixers */ 316 SND_SOC_DAPM_MIXER("Left Mixer", SUN50I_ADDA_MIX_DAC_CTRL, 317 SUN50I_ADDA_MIX_DAC_CTRL_LMIXEN, 0, 318 sun50i_a64_codec_mixer_controls, 319 ARRAY_SIZE(sun50i_a64_codec_mixer_controls)), 320 SND_SOC_DAPM_MIXER("Right Mixer", SUN50I_ADDA_MIX_DAC_CTRL, 321 SUN50I_ADDA_MIX_DAC_CTRL_RMIXEN, 0, 322 sun50i_a64_codec_mixer_controls, 323 ARRAY_SIZE(sun50i_a64_codec_mixer_controls)), 324 SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN50I_ADDA_ADC_CTRL, 325 SUN50I_ADDA_ADC_CTRL_ADCLEN, 0, 326 sun50i_codec_adc_mixer_controls, 327 ARRAY_SIZE(sun50i_codec_adc_mixer_controls)), 328 SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN50I_ADDA_ADC_CTRL, 329 SUN50I_ADDA_ADC_CTRL_ADCREN, 0, 330 sun50i_codec_adc_mixer_controls, 331 ARRAY_SIZE(sun50i_codec_adc_mixer_controls)), 332 }; 333 334 static const struct snd_soc_dapm_route sun50i_a64_codec_routes[] = { 335 /* Left Mixer Routes */ 336 { "Left Mixer", "DAC Playback Switch", "Left DAC" }, 337 { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" }, 338 { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, 339 340 /* Right Mixer Routes */ 341 { "Right Mixer", "DAC Playback Switch", "Right DAC" }, 342 { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" }, 343 { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, 344 345 /* Left ADC Mixer Routes */ 346 { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" }, 347 { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" }, 348 { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, 349 350 /* Right ADC Mixer Routes */ 351 { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" }, 352 { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" }, 353 { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, 354 355 /* ADC Routes */ 356 { "Left ADC", NULL, "Left ADC Mixer" }, 357 { "Right ADC", NULL, "Right ADC Mixer" }, 358 359 /* Headphone Routes */ 360 { "Headphone Source Playback Route", "DAC", "Left DAC" }, 361 { "Headphone Source Playback Route", "DAC", "Right DAC" }, 362 { "Headphone Source Playback Route", "Mixer", "Left Mixer" }, 363 { "Headphone Source Playback Route", "Mixer", "Right Mixer" }, 364 { "Headphone Amp", NULL, "Headphone Source Playback Route" }, 365 { "Headphone Amp", NULL, "hpvcc" }, 366 { "HP", NULL, "Headphone Amp" }, 367 368 /* Microphone Routes */ 369 { "Mic1 Amplifier", NULL, "MIC1"}, 370 371 /* Microphone Routes */ 372 { "Mic2 Amplifier", NULL, "MIC2"}, 373 { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, 374 { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, 375 { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, 376 { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, 377 378 /* Line-in Routes */ 379 { "Left Mixer", "Line In Playback Switch", "LINEIN" }, 380 { "Right Mixer", "Line In Playback Switch", "LINEIN" }, 381 { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" }, 382 { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" }, 383 384 /* Line-out Routes */ 385 { "Line Out Source Playback Route", "Stereo", "Left Mixer" }, 386 { "Line Out Source Playback Route", "Stereo", "Right Mixer" }, 387 { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" }, 388 { "Line Out Source Playback Route", "Mono Differential", 389 "Right Mixer" }, 390 { "LINEOUT", NULL, "Line Out Source Playback Route" }, 391 }; 392 393 static const struct snd_soc_component_driver sun50i_codec_analog_cmpnt_drv = { 394 .controls = sun50i_a64_codec_controls, 395 .num_controls = ARRAY_SIZE(sun50i_a64_codec_controls), 396 .dapm_widgets = sun50i_a64_codec_widgets, 397 .num_dapm_widgets = ARRAY_SIZE(sun50i_a64_codec_widgets), 398 .dapm_routes = sun50i_a64_codec_routes, 399 .num_dapm_routes = ARRAY_SIZE(sun50i_a64_codec_routes), 400 }; 401 402 static const struct of_device_id sun50i_codec_analog_of_match[] = { 403 { 404 .compatible = "allwinner,sun50i-a64-codec-analog", 405 }, 406 {} 407 }; 408 MODULE_DEVICE_TABLE(of, sun50i_codec_analog_of_match); 409 410 static int sun50i_codec_analog_probe(struct platform_device *pdev) 411 { 412 struct resource *res; 413 struct regmap *regmap; 414 void __iomem *base; 415 416 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 417 base = devm_ioremap_resource(&pdev->dev, res); 418 if (IS_ERR(base)) { 419 dev_err(&pdev->dev, "Failed to map the registers\n"); 420 return PTR_ERR(base); 421 } 422 423 regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base); 424 if (IS_ERR(regmap)) { 425 dev_err(&pdev->dev, "Failed to create regmap\n"); 426 return PTR_ERR(regmap); 427 } 428 429 return devm_snd_soc_register_component(&pdev->dev, 430 &sun50i_codec_analog_cmpnt_drv, 431 NULL, 0); 432 } 433 434 static struct platform_driver sun50i_codec_analog_driver = { 435 .driver = { 436 .name = "sun50i-codec-analog", 437 .of_match_table = sun50i_codec_analog_of_match, 438 }, 439 .probe = sun50i_codec_analog_probe, 440 }; 441 module_platform_driver(sun50i_codec_analog_driver); 442 443 MODULE_DESCRIPTION("Allwinner internal codec analog controls driver for A64"); 444 MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>"); 445 MODULE_LICENSE("GPL"); 446 MODULE_ALIAS("platform:sun50i-codec-analog"); 447