1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * wm8776.c -- WM8776 ALSA SoC Audio driver 4 * 5 * Copyright 2009-12 Wolfson Microelectronics plc 6 * 7 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 8 * 9 * TODO: Input ALC/limiter support 10 */ 11 12 #include <linux/mod_devicetable.h> 13 #include <linux/module.h> 14 #include <linux/moduleparam.h> 15 #include <linux/init.h> 16 #include <linux/delay.h> 17 #include <linux/pm.h> 18 #include <linux/i2c.h> 19 #include <linux/regmap.h> 20 #include <linux/spi/spi.h> 21 #include <linux/slab.h> 22 #include <sound/core.h> 23 #include <sound/pcm.h> 24 #include <sound/pcm_params.h> 25 #include <sound/soc.h> 26 #include <sound/initval.h> 27 #include <sound/tlv.h> 28 29 #include "wm8776.h" 30 31 enum wm8776_chip_type { 32 WM8775 = 1, 33 WM8776, 34 }; 35 36 /* codec private data */ 37 struct wm8776_priv { 38 struct regmap *regmap; 39 int sysclk[2]; 40 }; 41 42 static const struct reg_default wm8776_reg_defaults[] = { 43 { 0, 0x79 }, 44 { 1, 0x79 }, 45 { 2, 0x79 }, 46 { 3, 0xff }, 47 { 4, 0xff }, 48 { 5, 0xff }, 49 { 6, 0x00 }, 50 { 7, 0x90 }, 51 { 8, 0x00 }, 52 { 9, 0x00 }, 53 { 10, 0x22 }, 54 { 11, 0x22 }, 55 { 12, 0x22 }, 56 { 13, 0x08 }, 57 { 14, 0xcf }, 58 { 15, 0xcf }, 59 { 16, 0x7b }, 60 { 17, 0x00 }, 61 { 18, 0x32 }, 62 { 19, 0x00 }, 63 { 20, 0xa6 }, 64 { 21, 0x01 }, 65 { 22, 0x01 }, 66 }; 67 68 static bool wm8776_volatile(struct device *dev, unsigned int reg) 69 { 70 switch (reg) { 71 case WM8776_RESET: 72 return true; 73 default: 74 return false; 75 } 76 } 77 78 static int wm8776_reset(struct snd_soc_component *component) 79 { 80 return snd_soc_component_write(component, WM8776_RESET, 0); 81 } 82 83 static const DECLARE_TLV_DB_SCALE(hp_tlv, -12100, 100, 1); 84 static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); 85 static const DECLARE_TLV_DB_SCALE(adc_tlv, -10350, 50, 1); 86 87 static const struct snd_kcontrol_new wm8776_snd_controls[] = { 88 SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8776_HPLVOL, WM8776_HPRVOL, 89 0, 127, 0, hp_tlv), 90 SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8776_DACLVOL, WM8776_DACRVOL, 91 0, 255, 0, dac_tlv), 92 SOC_SINGLE("Digital Playback ZC Switch", WM8776_DACCTRL1, 0, 1, 0), 93 94 SOC_SINGLE("Deemphasis Switch", WM8776_DACCTRL2, 0, 1, 0), 95 96 SOC_DOUBLE_R_TLV("Capture Volume", WM8776_ADCLVOL, WM8776_ADCRVOL, 97 0, 255, 0, adc_tlv), 98 SOC_DOUBLE("Capture Switch", WM8776_ADCMUX, 7, 6, 1, 1), 99 SOC_DOUBLE_R("Capture ZC Switch", WM8776_ADCLVOL, WM8776_ADCRVOL, 8, 1, 0), 100 SOC_SINGLE("Capture HPF Switch", WM8776_ADCIFCTRL, 8, 1, 1), 101 }; 102 103 static const struct snd_kcontrol_new inmix_controls[] = { 104 SOC_DAPM_SINGLE("AIN1 Switch", WM8776_ADCMUX, 0, 1, 0), 105 SOC_DAPM_SINGLE("AIN2 Switch", WM8776_ADCMUX, 1, 1, 0), 106 SOC_DAPM_SINGLE("AIN3 Switch", WM8776_ADCMUX, 2, 1, 0), 107 SOC_DAPM_SINGLE("AIN4 Switch", WM8776_ADCMUX, 3, 1, 0), 108 SOC_DAPM_SINGLE("AIN5 Switch", WM8776_ADCMUX, 4, 1, 0), 109 }; 110 111 static const struct snd_kcontrol_new outmix_controls[] = { 112 SOC_DAPM_SINGLE("DAC Switch", WM8776_OUTMUX, 0, 1, 0), 113 SOC_DAPM_SINGLE("AUX Switch", WM8776_OUTMUX, 1, 1, 0), 114 SOC_DAPM_SINGLE("Bypass Switch", WM8776_OUTMUX, 2, 1, 0), 115 }; 116 117 static const struct snd_soc_dapm_widget wm8776_dapm_widgets[] = { 118 SND_SOC_DAPM_INPUT("AUX"), 119 120 SND_SOC_DAPM_INPUT("AIN1"), 121 SND_SOC_DAPM_INPUT("AIN2"), 122 SND_SOC_DAPM_INPUT("AIN3"), 123 SND_SOC_DAPM_INPUT("AIN4"), 124 SND_SOC_DAPM_INPUT("AIN5"), 125 126 SND_SOC_DAPM_MIXER("Input Mixer", WM8776_PWRDOWN, 6, 1, 127 inmix_controls, ARRAY_SIZE(inmix_controls)), 128 129 SND_SOC_DAPM_ADC("ADC", "Capture", WM8776_PWRDOWN, 1, 1), 130 SND_SOC_DAPM_DAC("DAC", "Playback", WM8776_PWRDOWN, 2, 1), 131 132 SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, 133 outmix_controls, ARRAY_SIZE(outmix_controls)), 134 135 SND_SOC_DAPM_PGA("Headphone PGA", WM8776_PWRDOWN, 3, 1, NULL, 0), 136 137 SND_SOC_DAPM_OUTPUT("VOUT"), 138 139 SND_SOC_DAPM_OUTPUT("HPOUTL"), 140 SND_SOC_DAPM_OUTPUT("HPOUTR"), 141 }; 142 143 static const struct snd_soc_dapm_route routes[] = { 144 { "Input Mixer", "AIN1 Switch", "AIN1" }, 145 { "Input Mixer", "AIN2 Switch", "AIN2" }, 146 { "Input Mixer", "AIN3 Switch", "AIN3" }, 147 { "Input Mixer", "AIN4 Switch", "AIN4" }, 148 { "Input Mixer", "AIN5 Switch", "AIN5" }, 149 150 { "ADC", NULL, "Input Mixer" }, 151 152 { "Output Mixer", "DAC Switch", "DAC" }, 153 { "Output Mixer", "AUX Switch", "AUX" }, 154 { "Output Mixer", "Bypass Switch", "Input Mixer" }, 155 156 { "VOUT", NULL, "Output Mixer" }, 157 158 { "Headphone PGA", NULL, "Output Mixer" }, 159 160 { "HPOUTL", NULL, "Headphone PGA" }, 161 { "HPOUTR", NULL, "Headphone PGA" }, 162 }; 163 164 static int wm8776_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 165 { 166 struct snd_soc_component *component = dai->component; 167 int reg, iface, master; 168 169 switch (dai->driver->id) { 170 case WM8776_DAI_DAC: 171 reg = WM8776_DACIFCTRL; 172 master = 0x80; 173 break; 174 case WM8776_DAI_ADC: 175 reg = WM8776_ADCIFCTRL; 176 master = 0x100; 177 break; 178 default: 179 return -EINVAL; 180 } 181 182 iface = 0; 183 184 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 185 case SND_SOC_DAIFMT_CBM_CFM: 186 break; 187 case SND_SOC_DAIFMT_CBS_CFS: 188 master = 0; 189 break; 190 default: 191 return -EINVAL; 192 } 193 194 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 195 case SND_SOC_DAIFMT_I2S: 196 iface |= 0x0002; 197 break; 198 case SND_SOC_DAIFMT_RIGHT_J: 199 break; 200 case SND_SOC_DAIFMT_LEFT_J: 201 iface |= 0x0001; 202 break; 203 default: 204 return -EINVAL; 205 } 206 207 switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 208 case SND_SOC_DAIFMT_NB_NF: 209 break; 210 case SND_SOC_DAIFMT_IB_IF: 211 iface |= 0x00c; 212 break; 213 case SND_SOC_DAIFMT_IB_NF: 214 iface |= 0x008; 215 break; 216 case SND_SOC_DAIFMT_NB_IF: 217 iface |= 0x004; 218 break; 219 default: 220 return -EINVAL; 221 } 222 223 /* Finally, write out the values */ 224 snd_soc_component_update_bits(component, reg, 0xf, iface); 225 snd_soc_component_update_bits(component, WM8776_MSTRCTRL, 0x180, master); 226 227 return 0; 228 } 229 230 static int mclk_ratios[] = { 231 128, 232 192, 233 256, 234 384, 235 512, 236 768, 237 }; 238 239 static int wm8776_hw_params(struct snd_pcm_substream *substream, 240 struct snd_pcm_hw_params *params, 241 struct snd_soc_dai *dai) 242 { 243 struct snd_soc_component *component = dai->component; 244 struct wm8776_priv *wm8776 = snd_soc_component_get_drvdata(component); 245 int iface_reg, iface; 246 int ratio_shift, master; 247 int i; 248 249 switch (dai->driver->id) { 250 case WM8776_DAI_DAC: 251 iface_reg = WM8776_DACIFCTRL; 252 master = 0x80; 253 ratio_shift = 4; 254 break; 255 case WM8776_DAI_ADC: 256 iface_reg = WM8776_ADCIFCTRL; 257 master = 0x100; 258 ratio_shift = 0; 259 break; 260 default: 261 return -EINVAL; 262 } 263 264 /* Set word length */ 265 switch (params_width(params)) { 266 case 16: 267 iface = 0; 268 break; 269 case 20: 270 iface = 0x10; 271 break; 272 case 24: 273 iface = 0x20; 274 break; 275 case 32: 276 iface = 0x30; 277 break; 278 default: 279 dev_err(component->dev, "Unsupported sample size: %i\n", 280 params_width(params)); 281 return -EINVAL; 282 } 283 284 /* Only need to set MCLK/LRCLK ratio if we're master */ 285 if (snd_soc_component_read(component, WM8776_MSTRCTRL) & master) { 286 for (i = 0; i < ARRAY_SIZE(mclk_ratios); i++) { 287 if (wm8776->sysclk[dai->driver->id] / params_rate(params) 288 == mclk_ratios[i]) 289 break; 290 } 291 292 if (i == ARRAY_SIZE(mclk_ratios)) { 293 dev_err(component->dev, 294 "Unable to configure MCLK ratio %d/%d\n", 295 wm8776->sysclk[dai->driver->id], params_rate(params)); 296 return -EINVAL; 297 } 298 299 dev_dbg(component->dev, "MCLK is %dfs\n", mclk_ratios[i]); 300 301 snd_soc_component_update_bits(component, WM8776_MSTRCTRL, 302 0x7 << ratio_shift, i << ratio_shift); 303 } else { 304 dev_dbg(component->dev, "DAI in slave mode\n"); 305 } 306 307 snd_soc_component_update_bits(component, iface_reg, 0x30, iface); 308 309 return 0; 310 } 311 312 static int wm8776_mute(struct snd_soc_dai *dai, int mute, int direction) 313 { 314 struct snd_soc_component *component = dai->component; 315 316 return snd_soc_component_write(component, WM8776_DACMUTE, !!mute); 317 } 318 319 static int wm8776_set_sysclk(struct snd_soc_dai *dai, 320 int clk_id, unsigned int freq, int dir) 321 { 322 struct snd_soc_component *component = dai->component; 323 struct wm8776_priv *wm8776 = snd_soc_component_get_drvdata(component); 324 325 if (WARN_ON(dai->driver->id >= ARRAY_SIZE(wm8776->sysclk))) 326 return -EINVAL; 327 328 wm8776->sysclk[dai->driver->id] = freq; 329 330 return 0; 331 } 332 333 static int wm8776_set_bias_level(struct snd_soc_component *component, 334 enum snd_soc_bias_level level) 335 { 336 struct wm8776_priv *wm8776 = snd_soc_component_get_drvdata(component); 337 338 switch (level) { 339 case SND_SOC_BIAS_ON: 340 break; 341 case SND_SOC_BIAS_PREPARE: 342 break; 343 case SND_SOC_BIAS_STANDBY: 344 if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { 345 regcache_sync(wm8776->regmap); 346 347 /* Disable the global powerdown; DAPM does the rest */ 348 snd_soc_component_update_bits(component, WM8776_PWRDOWN, 1, 0); 349 } 350 351 break; 352 case SND_SOC_BIAS_OFF: 353 snd_soc_component_update_bits(component, WM8776_PWRDOWN, 1, 1); 354 break; 355 } 356 357 return 0; 358 } 359 360 #define WM8776_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ 361 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) 362 363 static const struct snd_soc_dai_ops wm8776_dac_ops = { 364 .mute_stream = wm8776_mute, 365 .hw_params = wm8776_hw_params, 366 .set_fmt = wm8776_set_fmt, 367 .set_sysclk = wm8776_set_sysclk, 368 .no_capture_mute = 1, 369 }; 370 371 static const struct snd_soc_dai_ops wm8776_adc_ops = { 372 .hw_params = wm8776_hw_params, 373 .set_fmt = wm8776_set_fmt, 374 .set_sysclk = wm8776_set_sysclk, 375 }; 376 377 static struct snd_soc_dai_driver wm8776_dai[] = { 378 { 379 .name = "wm8776-hifi-playback", 380 .id = WM8776_DAI_DAC, 381 .playback = { 382 .stream_name = "Playback", 383 .channels_min = 2, 384 .channels_max = 2, 385 .rates = SNDRV_PCM_RATE_CONTINUOUS, 386 .rate_min = 32000, 387 .rate_max = 192000, 388 .formats = WM8776_FORMATS, 389 }, 390 .ops = &wm8776_dac_ops, 391 }, 392 { 393 .name = "wm8776-hifi-capture", 394 .id = WM8776_DAI_ADC, 395 .capture = { 396 .stream_name = "Capture", 397 .channels_min = 2, 398 .channels_max = 2, 399 .rates = SNDRV_PCM_RATE_CONTINUOUS, 400 .rate_min = 32000, 401 .rate_max = 96000, 402 .formats = WM8776_FORMATS, 403 }, 404 .ops = &wm8776_adc_ops, 405 }, 406 }; 407 408 static int wm8776_probe(struct snd_soc_component *component) 409 { 410 int ret = 0; 411 412 ret = wm8776_reset(component); 413 if (ret < 0) { 414 dev_err(component->dev, "Failed to issue reset: %d\n", ret); 415 return ret; 416 } 417 418 /* Latch the update bits; right channel only since we always 419 * update both. */ 420 snd_soc_component_update_bits(component, WM8776_HPRVOL, 0x100, 0x100); 421 snd_soc_component_update_bits(component, WM8776_DACRVOL, 0x100, 0x100); 422 423 return ret; 424 } 425 426 static const struct snd_soc_component_driver soc_component_dev_wm8776 = { 427 .probe = wm8776_probe, 428 .set_bias_level = wm8776_set_bias_level, 429 .controls = wm8776_snd_controls, 430 .num_controls = ARRAY_SIZE(wm8776_snd_controls), 431 .dapm_widgets = wm8776_dapm_widgets, 432 .num_dapm_widgets = ARRAY_SIZE(wm8776_dapm_widgets), 433 .dapm_routes = routes, 434 .num_dapm_routes = ARRAY_SIZE(routes), 435 .suspend_bias_off = 1, 436 .idle_bias_on = 1, 437 .use_pmdown_time = 1, 438 .endianness = 1, 439 }; 440 441 static const struct of_device_id wm8776_of_match[] = { 442 { .compatible = "wlf,wm8776", }, 443 { } 444 }; 445 MODULE_DEVICE_TABLE(of, wm8776_of_match); 446 447 static const struct regmap_config wm8776_regmap = { 448 .reg_bits = 7, 449 .val_bits = 9, 450 .max_register = WM8776_RESET, 451 452 .reg_defaults = wm8776_reg_defaults, 453 .num_reg_defaults = ARRAY_SIZE(wm8776_reg_defaults), 454 .cache_type = REGCACHE_MAPLE, 455 456 .volatile_reg = wm8776_volatile, 457 }; 458 459 #if defined(CONFIG_SPI_MASTER) 460 static int wm8776_spi_probe(struct spi_device *spi) 461 { 462 struct wm8776_priv *wm8776; 463 int ret; 464 465 wm8776 = devm_kzalloc(&spi->dev, sizeof(struct wm8776_priv), 466 GFP_KERNEL); 467 if (wm8776 == NULL) 468 return -ENOMEM; 469 470 wm8776->regmap = devm_regmap_init_spi(spi, &wm8776_regmap); 471 if (IS_ERR(wm8776->regmap)) 472 return PTR_ERR(wm8776->regmap); 473 474 spi_set_drvdata(spi, wm8776); 475 476 ret = devm_snd_soc_register_component(&spi->dev, 477 &soc_component_dev_wm8776, wm8776_dai, ARRAY_SIZE(wm8776_dai)); 478 479 return ret; 480 } 481 482 static struct spi_driver wm8776_spi_driver = { 483 .driver = { 484 .name = "wm8776", 485 .of_match_table = wm8776_of_match, 486 }, 487 .probe = wm8776_spi_probe, 488 }; 489 #endif /* CONFIG_SPI_MASTER */ 490 491 #if IS_ENABLED(CONFIG_I2C) 492 static int wm8776_i2c_probe(struct i2c_client *i2c) 493 { 494 struct wm8776_priv *wm8776; 495 int ret; 496 497 wm8776 = devm_kzalloc(&i2c->dev, sizeof(struct wm8776_priv), 498 GFP_KERNEL); 499 if (wm8776 == NULL) 500 return -ENOMEM; 501 502 wm8776->regmap = devm_regmap_init_i2c(i2c, &wm8776_regmap); 503 if (IS_ERR(wm8776->regmap)) 504 return PTR_ERR(wm8776->regmap); 505 506 i2c_set_clientdata(i2c, wm8776); 507 508 ret = devm_snd_soc_register_component(&i2c->dev, 509 &soc_component_dev_wm8776, wm8776_dai, ARRAY_SIZE(wm8776_dai)); 510 511 return ret; 512 } 513 514 static const struct i2c_device_id wm8776_i2c_id[] = { 515 { "wm8775", WM8775 }, 516 { "wm8776", WM8776 }, 517 { } 518 }; 519 MODULE_DEVICE_TABLE(i2c, wm8776_i2c_id); 520 521 static struct i2c_driver wm8776_i2c_driver = { 522 .driver = { 523 .name = "wm8776", 524 .of_match_table = wm8776_of_match, 525 }, 526 .probe = wm8776_i2c_probe, 527 .id_table = wm8776_i2c_id, 528 }; 529 #endif 530 531 static int __init wm8776_modinit(void) 532 { 533 int ret = 0; 534 #if IS_ENABLED(CONFIG_I2C) 535 ret = i2c_add_driver(&wm8776_i2c_driver); 536 if (ret != 0) { 537 printk(KERN_ERR "Failed to register wm8776 I2C driver: %d\n", 538 ret); 539 } 540 #endif 541 #if defined(CONFIG_SPI_MASTER) 542 ret = spi_register_driver(&wm8776_spi_driver); 543 if (ret != 0) { 544 printk(KERN_ERR "Failed to register wm8776 SPI driver: %d\n", 545 ret); 546 } 547 #endif 548 return ret; 549 } 550 module_init(wm8776_modinit); 551 552 static void __exit wm8776_exit(void) 553 { 554 #if IS_ENABLED(CONFIG_I2C) 555 i2c_del_driver(&wm8776_i2c_driver); 556 #endif 557 #if defined(CONFIG_SPI_MASTER) 558 spi_unregister_driver(&wm8776_spi_driver); 559 #endif 560 } 561 module_exit(wm8776_exit); 562 563 MODULE_DESCRIPTION("ASoC WM8776 driver"); 564 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 565 MODULE_LICENSE("GPL"); 566
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.