1 /* 2 * cs42l51.c 3 * 4 * ASoC Driver for Cirrus Logic CS42L51 codecs 5 * 6 * Copyright (c) 2010 Arnaud Patard <apatard@mandriva.com> 7 * 8 * Based on cs4270.c - Copyright (c) Freescale Semiconductor 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * For now: 20 * - Only I2C is support. Not SPI 21 * - master mode *NOT* supported 22 */ 23 24 #include <linux/clk.h> 25 #include <linux/module.h> 26 #include <linux/slab.h> 27 #include <sound/core.h> 28 #include <sound/soc.h> 29 #include <sound/tlv.h> 30 #include <sound/initval.h> 31 #include <sound/pcm_params.h> 32 #include <sound/pcm.h> 33 #include <linux/regmap.h> 34 35 #include "cs42l51.h" 36 37 enum master_slave_mode { 38 MODE_SLAVE, 39 MODE_SLAVE_AUTO, 40 MODE_MASTER, 41 }; 42 43 struct cs42l51_private { 44 unsigned int mclk; 45 struct clk *mclk_handle; 46 unsigned int audio_mode; /* The mode (I2S or left-justified) */ 47 enum master_slave_mode func; 48 }; 49 50 #define CS42L51_FORMATS ( \ 51 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ 52 SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ 53 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \ 54 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE) 55 56 static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol, 57 struct snd_ctl_elem_value *ucontrol) 58 { 59 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 60 unsigned long value = snd_soc_component_read32(component, CS42L51_PCM_MIXER)&3; 61 62 switch (value) { 63 default: 64 case 0: 65 ucontrol->value.enumerated.item[0] = 0; 66 break; 67 /* same value : (L+R)/2 and (R+L)/2 */ 68 case 1: 69 case 2: 70 ucontrol->value.enumerated.item[0] = 1; 71 break; 72 case 3: 73 ucontrol->value.enumerated.item[0] = 2; 74 break; 75 } 76 77 return 0; 78 } 79 80 #define CHAN_MIX_NORMAL 0x00 81 #define CHAN_MIX_BOTH 0x55 82 #define CHAN_MIX_SWAP 0xFF 83 84 static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol, 85 struct snd_ctl_elem_value *ucontrol) 86 { 87 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 88 unsigned char val; 89 90 switch (ucontrol->value.enumerated.item[0]) { 91 default: 92 case 0: 93 val = CHAN_MIX_NORMAL; 94 break; 95 case 1: 96 val = CHAN_MIX_BOTH; 97 break; 98 case 2: 99 val = CHAN_MIX_SWAP; 100 break; 101 } 102 103 snd_soc_component_write(component, CS42L51_PCM_MIXER, val); 104 105 return 1; 106 } 107 108 static const DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -5150, 50, 0); 109 static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0); 110 111 static const DECLARE_TLV_DB_SCALE(aout_tlv, -10200, 50, 0); 112 113 static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0); 114 static const char *chan_mix[] = { 115 "L R", 116 "L+R", 117 "R L", 118 }; 119 120 static SOC_ENUM_SINGLE_EXT_DECL(cs42l51_chan_mix, chan_mix); 121 122 static const struct snd_kcontrol_new cs42l51_snd_controls[] = { 123 SOC_DOUBLE_R_SX_TLV("PCM Playback Volume", 124 CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, 125 0, 0x19, 0x7F, adc_pcm_tlv), 126 SOC_DOUBLE_R("PCM Playback Switch", 127 CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, 7, 1, 1), 128 SOC_DOUBLE_R_SX_TLV("Analog Playback Volume", 129 CS42L51_AOUTA_VOL, CS42L51_AOUTB_VOL, 130 0, 0x34, 0xE4, aout_tlv), 131 SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume", 132 CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 133 0, 0x19, 0x7F, adc_pcm_tlv), 134 SOC_DOUBLE_R("ADC Mixer Switch", 135 CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1), 136 SOC_SINGLE("Playback Deemphasis Switch", CS42L51_DAC_CTL, 3, 1, 0), 137 SOC_SINGLE("Auto-Mute Switch", CS42L51_DAC_CTL, 2, 1, 0), 138 SOC_SINGLE("Soft Ramp Switch", CS42L51_DAC_CTL, 1, 1, 0), 139 SOC_SINGLE("Zero Cross Switch", CS42L51_DAC_CTL, 0, 0, 0), 140 SOC_DOUBLE_TLV("Mic Boost Volume", 141 CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv), 142 SOC_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv), 143 SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv), 144 SOC_ENUM_EXT("PCM channel mixer", 145 cs42l51_chan_mix, 146 cs42l51_get_chan_mix, cs42l51_set_chan_mix), 147 }; 148 149 /* 150 * to power down, one must: 151 * 1.) Enable the PDN bit 152 * 2.) enable power-down for the select channels 153 * 3.) disable the PDN bit. 154 */ 155 static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w, 156 struct snd_kcontrol *kcontrol, int event) 157 { 158 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 159 160 switch (event) { 161 case SND_SOC_DAPM_PRE_PMD: 162 snd_soc_component_update_bits(component, CS42L51_POWER_CTL1, 163 CS42L51_POWER_CTL1_PDN, 164 CS42L51_POWER_CTL1_PDN); 165 break; 166 default: 167 case SND_SOC_DAPM_POST_PMD: 168 snd_soc_component_update_bits(component, CS42L51_POWER_CTL1, 169 CS42L51_POWER_CTL1_PDN, 0); 170 break; 171 } 172 173 return 0; 174 } 175 176 static const char *cs42l51_dac_names[] = {"Direct PCM", 177 "DSP PCM", "ADC"}; 178 static SOC_ENUM_SINGLE_DECL(cs42l51_dac_mux_enum, 179 CS42L51_DAC_CTL, 6, cs42l51_dac_names); 180 static const struct snd_kcontrol_new cs42l51_dac_mux_controls = 181 SOC_DAPM_ENUM("Route", cs42l51_dac_mux_enum); 182 183 static const char *cs42l51_adcl_names[] = {"AIN1 Left", "AIN2 Left", 184 "MIC Left", "MIC+preamp Left"}; 185 static SOC_ENUM_SINGLE_DECL(cs42l51_adcl_mux_enum, 186 CS42L51_ADC_INPUT, 4, cs42l51_adcl_names); 187 static const struct snd_kcontrol_new cs42l51_adcl_mux_controls = 188 SOC_DAPM_ENUM("Route", cs42l51_adcl_mux_enum); 189 190 static const char *cs42l51_adcr_names[] = {"AIN1 Right", "AIN2 Right", 191 "MIC Right", "MIC+preamp Right"}; 192 static SOC_ENUM_SINGLE_DECL(cs42l51_adcr_mux_enum, 193 CS42L51_ADC_INPUT, 6, cs42l51_adcr_names); 194 static const struct snd_kcontrol_new cs42l51_adcr_mux_controls = 195 SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum); 196 197 static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = { 198 SND_SOC_DAPM_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1), 199 SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0, 200 cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), 201 SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0, 202 cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), 203 SND_SOC_DAPM_ADC_E("Left ADC", "Left HiFi Capture", 204 CS42L51_POWER_CTL1, 1, 1, 205 cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), 206 SND_SOC_DAPM_ADC_E("Right ADC", "Right HiFi Capture", 207 CS42L51_POWER_CTL1, 2, 1, 208 cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), 209 SND_SOC_DAPM_DAC_E("Left DAC", "Left HiFi Playback", 210 CS42L51_POWER_CTL1, 5, 1, 211 cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), 212 SND_SOC_DAPM_DAC_E("Right DAC", "Right HiFi Playback", 213 CS42L51_POWER_CTL1, 6, 1, 214 cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), 215 216 /* analog/mic */ 217 SND_SOC_DAPM_INPUT("AIN1L"), 218 SND_SOC_DAPM_INPUT("AIN1R"), 219 SND_SOC_DAPM_INPUT("AIN2L"), 220 SND_SOC_DAPM_INPUT("AIN2R"), 221 SND_SOC_DAPM_INPUT("MICL"), 222 SND_SOC_DAPM_INPUT("MICR"), 223 224 SND_SOC_DAPM_MIXER("Mic Preamp Left", 225 CS42L51_MIC_POWER_CTL, 2, 1, NULL, 0), 226 SND_SOC_DAPM_MIXER("Mic Preamp Right", 227 CS42L51_MIC_POWER_CTL, 3, 1, NULL, 0), 228 229 /* HP */ 230 SND_SOC_DAPM_OUTPUT("HPL"), 231 SND_SOC_DAPM_OUTPUT("HPR"), 232 233 /* mux */ 234 SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0, 235 &cs42l51_dac_mux_controls), 236 SND_SOC_DAPM_MUX("PGA-ADC Mux Left", SND_SOC_NOPM, 0, 0, 237 &cs42l51_adcl_mux_controls), 238 SND_SOC_DAPM_MUX("PGA-ADC Mux Right", SND_SOC_NOPM, 0, 0, 239 &cs42l51_adcr_mux_controls), 240 }; 241 242 static const struct snd_soc_dapm_widget cs42l51_dapm_mclk_widgets[] = { 243 SND_SOC_DAPM_CLOCK_SUPPLY("MCLK") 244 }; 245 246 static const struct snd_soc_dapm_route cs42l51_routes[] = { 247 {"HPL", NULL, "Left DAC"}, 248 {"HPR", NULL, "Right DAC"}, 249 250 {"Left ADC", NULL, "Left PGA"}, 251 {"Right ADC", NULL, "Right PGA"}, 252 253 {"Mic Preamp Left", NULL, "MICL"}, 254 {"Mic Preamp Right", NULL, "MICR"}, 255 256 {"PGA-ADC Mux Left", "AIN1 Left", "AIN1L" }, 257 {"PGA-ADC Mux Left", "AIN2 Left", "AIN2L" }, 258 {"PGA-ADC Mux Left", "MIC Left", "MICL" }, 259 {"PGA-ADC Mux Left", "MIC+preamp Left", "Mic Preamp Left" }, 260 {"PGA-ADC Mux Right", "AIN1 Right", "AIN1R" }, 261 {"PGA-ADC Mux Right", "AIN2 Right", "AIN2R" }, 262 {"PGA-ADC Mux Right", "MIC Right", "MICR" }, 263 {"PGA-ADC Mux Right", "MIC+preamp Right", "Mic Preamp Right" }, 264 265 {"Left PGA", NULL, "PGA-ADC Mux Left"}, 266 {"Right PGA", NULL, "PGA-ADC Mux Right"}, 267 }; 268 269 static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai, 270 unsigned int format) 271 { 272 struct snd_soc_component *component = codec_dai->component; 273 struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(component); 274 275 switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { 276 case SND_SOC_DAIFMT_I2S: 277 case SND_SOC_DAIFMT_LEFT_J: 278 case SND_SOC_DAIFMT_RIGHT_J: 279 cs42l51->audio_mode = format & SND_SOC_DAIFMT_FORMAT_MASK; 280 break; 281 default: 282 dev_err(component->dev, "invalid DAI format\n"); 283 return -EINVAL; 284 } 285 286 switch (format & SND_SOC_DAIFMT_MASTER_MASK) { 287 case SND_SOC_DAIFMT_CBM_CFM: 288 cs42l51->func = MODE_MASTER; 289 break; 290 case SND_SOC_DAIFMT_CBS_CFS: 291 cs42l51->func = MODE_SLAVE_AUTO; 292 break; 293 default: 294 dev_err(component->dev, "Unknown master/slave configuration\n"); 295 return -EINVAL; 296 } 297 298 return 0; 299 } 300 301 struct cs42l51_ratios { 302 unsigned int ratio; 303 unsigned char speed_mode; 304 unsigned char mclk; 305 }; 306 307 static struct cs42l51_ratios slave_ratios[] = { 308 { 512, CS42L51_QSM_MODE, 0 }, { 768, CS42L51_QSM_MODE, 0 }, 309 { 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 }, 310 { 2048, CS42L51_QSM_MODE, 0 }, { 3072, CS42L51_QSM_MODE, 0 }, 311 { 256, CS42L51_HSM_MODE, 0 }, { 384, CS42L51_HSM_MODE, 0 }, 312 { 512, CS42L51_HSM_MODE, 0 }, { 768, CS42L51_HSM_MODE, 0 }, 313 { 1024, CS42L51_HSM_MODE, 0 }, { 1536, CS42L51_HSM_MODE, 0 }, 314 { 128, CS42L51_SSM_MODE, 0 }, { 192, CS42L51_SSM_MODE, 0 }, 315 { 256, CS42L51_SSM_MODE, 0 }, { 384, CS42L51_SSM_MODE, 0 }, 316 { 512, CS42L51_SSM_MODE, 0 }, { 768, CS42L51_SSM_MODE, 0 }, 317 { 128, CS42L51_DSM_MODE, 0 }, { 192, CS42L51_DSM_MODE, 0 }, 318 { 256, CS42L51_DSM_MODE, 0 }, { 384, CS42L51_DSM_MODE, 0 }, 319 }; 320 321 static struct cs42l51_ratios slave_auto_ratios[] = { 322 { 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 }, 323 { 2048, CS42L51_QSM_MODE, 1 }, { 3072, CS42L51_QSM_MODE, 1 }, 324 { 512, CS42L51_HSM_MODE, 0 }, { 768, CS42L51_HSM_MODE, 0 }, 325 { 1024, CS42L51_HSM_MODE, 1 }, { 1536, CS42L51_HSM_MODE, 1 }, 326 { 256, CS42L51_SSM_MODE, 0 }, { 384, CS42L51_SSM_MODE, 0 }, 327 { 512, CS42L51_SSM_MODE, 1 }, { 768, CS42L51_SSM_MODE, 1 }, 328 { 128, CS42L51_DSM_MODE, 0 }, { 192, CS42L51_DSM_MODE, 0 }, 329 { 256, CS42L51_DSM_MODE, 1 }, { 384, CS42L51_DSM_MODE, 1 }, 330 }; 331 332 static int cs42l51_set_dai_sysclk(struct snd_soc_dai *codec_dai, 333 int clk_id, unsigned int freq, int dir) 334 { 335 struct snd_soc_component *component = codec_dai->component; 336 struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(component); 337 338 cs42l51->mclk = freq; 339 return 0; 340 } 341 342 static int cs42l51_hw_params(struct snd_pcm_substream *substream, 343 struct snd_pcm_hw_params *params, 344 struct snd_soc_dai *dai) 345 { 346 struct snd_soc_component *component = dai->component; 347 struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(component); 348 int ret; 349 unsigned int i; 350 unsigned int rate; 351 unsigned int ratio; 352 struct cs42l51_ratios *ratios = NULL; 353 int nr_ratios = 0; 354 int intf_ctl, power_ctl, fmt; 355 356 switch (cs42l51->func) { 357 case MODE_MASTER: 358 return -EINVAL; 359 case MODE_SLAVE: 360 ratios = slave_ratios; 361 nr_ratios = ARRAY_SIZE(slave_ratios); 362 break; 363 case MODE_SLAVE_AUTO: 364 ratios = slave_auto_ratios; 365 nr_ratios = ARRAY_SIZE(slave_auto_ratios); 366 break; 367 } 368 369 /* Figure out which MCLK/LRCK ratio to use */ 370 rate = params_rate(params); /* Sampling rate, in Hz */ 371 ratio = cs42l51->mclk / rate; /* MCLK/LRCK ratio */ 372 for (i = 0; i < nr_ratios; i++) { 373 if (ratios[i].ratio == ratio) 374 break; 375 } 376 377 if (i == nr_ratios) { 378 /* We did not find a matching ratio */ 379 dev_err(component->dev, "could not find matching ratio\n"); 380 return -EINVAL; 381 } 382 383 intf_ctl = snd_soc_component_read32(component, CS42L51_INTF_CTL); 384 power_ctl = snd_soc_component_read32(component, CS42L51_MIC_POWER_CTL); 385 386 intf_ctl &= ~(CS42L51_INTF_CTL_MASTER | CS42L51_INTF_CTL_ADC_I2S 387 | CS42L51_INTF_CTL_DAC_FORMAT(7)); 388 power_ctl &= ~(CS42L51_MIC_POWER_CTL_SPEED(3) 389 | CS42L51_MIC_POWER_CTL_MCLK_DIV2); 390 391 switch (cs42l51->func) { 392 case MODE_MASTER: 393 intf_ctl |= CS42L51_INTF_CTL_MASTER; 394 power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode); 395 break; 396 case MODE_SLAVE: 397 power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode); 398 break; 399 case MODE_SLAVE_AUTO: 400 power_ctl |= CS42L51_MIC_POWER_CTL_AUTO; 401 break; 402 } 403 404 switch (cs42l51->audio_mode) { 405 case SND_SOC_DAIFMT_I2S: 406 intf_ctl |= CS42L51_INTF_CTL_ADC_I2S; 407 intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_I2S); 408 break; 409 case SND_SOC_DAIFMT_LEFT_J: 410 intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_LJ24); 411 break; 412 case SND_SOC_DAIFMT_RIGHT_J: 413 switch (params_width(params)) { 414 case 16: 415 fmt = CS42L51_DAC_DIF_RJ16; 416 break; 417 case 18: 418 fmt = CS42L51_DAC_DIF_RJ18; 419 break; 420 case 20: 421 fmt = CS42L51_DAC_DIF_RJ20; 422 break; 423 case 24: 424 fmt = CS42L51_DAC_DIF_RJ24; 425 break; 426 default: 427 dev_err(component->dev, "unknown format\n"); 428 return -EINVAL; 429 } 430 intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(fmt); 431 break; 432 default: 433 dev_err(component->dev, "unknown format\n"); 434 return -EINVAL; 435 } 436 437 if (ratios[i].mclk) 438 power_ctl |= CS42L51_MIC_POWER_CTL_MCLK_DIV2; 439 440 ret = snd_soc_component_write(component, CS42L51_INTF_CTL, intf_ctl); 441 if (ret < 0) 442 return ret; 443 444 ret = snd_soc_component_write(component, CS42L51_MIC_POWER_CTL, power_ctl); 445 if (ret < 0) 446 return ret; 447 448 return 0; 449 } 450 451 static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute) 452 { 453 struct snd_soc_component *component = dai->component; 454 int reg; 455 int mask = CS42L51_DAC_OUT_CTL_DACA_MUTE|CS42L51_DAC_OUT_CTL_DACB_MUTE; 456 457 reg = snd_soc_component_read32(component, CS42L51_DAC_OUT_CTL); 458 459 if (mute) 460 reg |= mask; 461 else 462 reg &= ~mask; 463 464 return snd_soc_component_write(component, CS42L51_DAC_OUT_CTL, reg); 465 } 466 467 static const struct snd_soc_dai_ops cs42l51_dai_ops = { 468 .hw_params = cs42l51_hw_params, 469 .set_sysclk = cs42l51_set_dai_sysclk, 470 .set_fmt = cs42l51_set_dai_fmt, 471 .digital_mute = cs42l51_dai_mute, 472 }; 473 474 static struct snd_soc_dai_driver cs42l51_dai = { 475 .name = "cs42l51-hifi", 476 .playback = { 477 .stream_name = "Playback", 478 .channels_min = 1, 479 .channels_max = 2, 480 .rates = SNDRV_PCM_RATE_8000_96000, 481 .formats = CS42L51_FORMATS, 482 }, 483 .capture = { 484 .stream_name = "Capture", 485 .channels_min = 1, 486 .channels_max = 2, 487 .rates = SNDRV_PCM_RATE_8000_96000, 488 .formats = CS42L51_FORMATS, 489 }, 490 .ops = &cs42l51_dai_ops, 491 }; 492 493 static int cs42l51_component_probe(struct snd_soc_component *component) 494 { 495 int ret, reg; 496 struct snd_soc_dapm_context *dapm; 497 struct cs42l51_private *cs42l51; 498 499 cs42l51 = snd_soc_component_get_drvdata(component); 500 dapm = snd_soc_component_get_dapm(component); 501 502 if (cs42l51->mclk_handle) 503 snd_soc_dapm_new_controls(dapm, cs42l51_dapm_mclk_widgets, 1); 504 505 /* 506 * DAC configuration 507 * - Use signal processor 508 * - auto mute 509 * - vol changes immediate 510 * - no de-emphasize 511 */ 512 reg = CS42L51_DAC_CTL_DATA_SEL(1) 513 | CS42L51_DAC_CTL_AMUTE | CS42L51_DAC_CTL_DACSZ(0); 514 ret = snd_soc_component_write(component, CS42L51_DAC_CTL, reg); 515 if (ret < 0) 516 return ret; 517 518 return 0; 519 } 520 521 static const struct snd_soc_component_driver soc_component_device_cs42l51 = { 522 .probe = cs42l51_component_probe, 523 .controls = cs42l51_snd_controls, 524 .num_controls = ARRAY_SIZE(cs42l51_snd_controls), 525 .dapm_widgets = cs42l51_dapm_widgets, 526 .num_dapm_widgets = ARRAY_SIZE(cs42l51_dapm_widgets), 527 .dapm_routes = cs42l51_routes, 528 .num_dapm_routes = ARRAY_SIZE(cs42l51_routes), 529 .idle_bias_on = 1, 530 .use_pmdown_time = 1, 531 .endianness = 1, 532 .non_legacy_dai_naming = 1, 533 }; 534 535 const struct regmap_config cs42l51_regmap = { 536 .max_register = CS42L51_CHARGE_FREQ, 537 .cache_type = REGCACHE_RBTREE, 538 }; 539 EXPORT_SYMBOL_GPL(cs42l51_regmap); 540 541 int cs42l51_probe(struct device *dev, struct regmap *regmap) 542 { 543 struct cs42l51_private *cs42l51; 544 unsigned int val; 545 int ret; 546 547 if (IS_ERR(regmap)) 548 return PTR_ERR(regmap); 549 550 cs42l51 = devm_kzalloc(dev, sizeof(struct cs42l51_private), 551 GFP_KERNEL); 552 if (!cs42l51) 553 return -ENOMEM; 554 555 dev_set_drvdata(dev, cs42l51); 556 557 cs42l51->mclk_handle = devm_clk_get(dev, "MCLK"); 558 if (IS_ERR(cs42l51->mclk_handle)) { 559 if (PTR_ERR(cs42l51->mclk_handle) != -ENOENT) 560 return PTR_ERR(cs42l51->mclk_handle); 561 cs42l51->mclk_handle = NULL; 562 } 563 564 /* Verify that we have a CS42L51 */ 565 ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val); 566 if (ret < 0) { 567 dev_err(dev, "failed to read I2C\n"); 568 goto error; 569 } 570 571 if ((val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) && 572 (val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) { 573 dev_err(dev, "Invalid chip id: %x\n", val); 574 ret = -ENODEV; 575 goto error; 576 } 577 dev_info(dev, "Cirrus Logic CS42L51, Revision: %02X\n", 578 val & CS42L51_CHIP_REV_MASK); 579 580 ret = devm_snd_soc_register_component(dev, 581 &soc_component_device_cs42l51, &cs42l51_dai, 1); 582 error: 583 return ret; 584 } 585 EXPORT_SYMBOL_GPL(cs42l51_probe); 586 587 const struct of_device_id cs42l51_of_match[] = { 588 { .compatible = "cirrus,cs42l51", }, 589 { } 590 }; 591 MODULE_DEVICE_TABLE(of, cs42l51_of_match); 592 EXPORT_SYMBOL_GPL(cs42l51_of_match); 593 594 MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>"); 595 MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver"); 596 MODULE_LICENSE("GPL"); 597