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