wm8776.c (13449B)
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/module.h> 13#include <linux/moduleparam.h> 14#include <linux/init.h> 15#include <linux/delay.h> 16#include <linux/pm.h> 17#include <linux/i2c.h> 18#include <linux/of_device.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 31enum wm8776_chip_type { 32 WM8775 = 1, 33 WM8776, 34}; 35 36/* codec private data */ 37struct wm8776_priv { 38 struct regmap *regmap; 39 int sysclk[2]; 40}; 41 42static 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 68static 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 78static int wm8776_reset(struct snd_soc_component *component) 79{ 80 return snd_soc_component_write(component, WM8776_RESET, 0); 81} 82 83static const DECLARE_TLV_DB_SCALE(hp_tlv, -12100, 100, 1); 84static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); 85static const DECLARE_TLV_DB_SCALE(adc_tlv, -10350, 50, 1); 86 87static const struct snd_kcontrol_new wm8776_snd_controls[] = { 88SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8776_HPLVOL, WM8776_HPRVOL, 89 0, 127, 0, hp_tlv), 90SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8776_DACLVOL, WM8776_DACRVOL, 91 0, 255, 0, dac_tlv), 92SOC_SINGLE("Digital Playback ZC Switch", WM8776_DACCTRL1, 0, 1, 0), 93 94SOC_SINGLE("Deemphasis Switch", WM8776_DACCTRL2, 0, 1, 0), 95 96SOC_DOUBLE_R_TLV("Capture Volume", WM8776_ADCLVOL, WM8776_ADCRVOL, 97 0, 255, 0, adc_tlv), 98SOC_DOUBLE("Capture Switch", WM8776_ADCMUX, 7, 6, 1, 1), 99SOC_DOUBLE_R("Capture ZC Switch", WM8776_ADCLVOL, WM8776_ADCRVOL, 8, 1, 0), 100SOC_SINGLE("Capture HPF Switch", WM8776_ADCIFCTRL, 8, 1, 1), 101}; 102 103static const struct snd_kcontrol_new inmix_controls[] = { 104SOC_DAPM_SINGLE("AIN1 Switch", WM8776_ADCMUX, 0, 1, 0), 105SOC_DAPM_SINGLE("AIN2 Switch", WM8776_ADCMUX, 1, 1, 0), 106SOC_DAPM_SINGLE("AIN3 Switch", WM8776_ADCMUX, 2, 1, 0), 107SOC_DAPM_SINGLE("AIN4 Switch", WM8776_ADCMUX, 3, 1, 0), 108SOC_DAPM_SINGLE("AIN5 Switch", WM8776_ADCMUX, 4, 1, 0), 109}; 110 111static const struct snd_kcontrol_new outmix_controls[] = { 112SOC_DAPM_SINGLE("DAC Switch", WM8776_OUTMUX, 0, 1, 0), 113SOC_DAPM_SINGLE("AUX Switch", WM8776_OUTMUX, 1, 1, 0), 114SOC_DAPM_SINGLE("Bypass Switch", WM8776_OUTMUX, 2, 1, 0), 115}; 116 117static const struct snd_soc_dapm_widget wm8776_dapm_widgets[] = { 118SND_SOC_DAPM_INPUT("AUX"), 119 120SND_SOC_DAPM_INPUT("AIN1"), 121SND_SOC_DAPM_INPUT("AIN2"), 122SND_SOC_DAPM_INPUT("AIN3"), 123SND_SOC_DAPM_INPUT("AIN4"), 124SND_SOC_DAPM_INPUT("AIN5"), 125 126SND_SOC_DAPM_MIXER("Input Mixer", WM8776_PWRDOWN, 6, 1, 127 inmix_controls, ARRAY_SIZE(inmix_controls)), 128 129SND_SOC_DAPM_ADC("ADC", "Capture", WM8776_PWRDOWN, 1, 1), 130SND_SOC_DAPM_DAC("DAC", "Playback", WM8776_PWRDOWN, 2, 1), 131 132SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, 133 outmix_controls, ARRAY_SIZE(outmix_controls)), 134 135SND_SOC_DAPM_PGA("Headphone PGA", WM8776_PWRDOWN, 3, 1, NULL, 0), 136 137SND_SOC_DAPM_OUTPUT("VOUT"), 138 139SND_SOC_DAPM_OUTPUT("HPOUTL"), 140SND_SOC_DAPM_OUTPUT("HPOUTR"), 141}; 142 143static 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 164static 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 230static int mclk_ratios[] = { 231 128, 232 192, 233 256, 234 384, 235 512, 236 768, 237}; 238 239static 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 312static 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 319static 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 333static 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 363static 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 371static 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 377static 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 408static 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 426static 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 .non_legacy_dai_naming = 1, 440}; 441 442static const struct of_device_id wm8776_of_match[] = { 443 { .compatible = "wlf,wm8776", }, 444 { } 445}; 446MODULE_DEVICE_TABLE(of, wm8776_of_match); 447 448static const struct regmap_config wm8776_regmap = { 449 .reg_bits = 7, 450 .val_bits = 9, 451 .max_register = WM8776_RESET, 452 453 .reg_defaults = wm8776_reg_defaults, 454 .num_reg_defaults = ARRAY_SIZE(wm8776_reg_defaults), 455 .cache_type = REGCACHE_RBTREE, 456 457 .volatile_reg = wm8776_volatile, 458}; 459 460#if defined(CONFIG_SPI_MASTER) 461static int wm8776_spi_probe(struct spi_device *spi) 462{ 463 struct wm8776_priv *wm8776; 464 int ret; 465 466 wm8776 = devm_kzalloc(&spi->dev, sizeof(struct wm8776_priv), 467 GFP_KERNEL); 468 if (wm8776 == NULL) 469 return -ENOMEM; 470 471 wm8776->regmap = devm_regmap_init_spi(spi, &wm8776_regmap); 472 if (IS_ERR(wm8776->regmap)) 473 return PTR_ERR(wm8776->regmap); 474 475 spi_set_drvdata(spi, wm8776); 476 477 ret = devm_snd_soc_register_component(&spi->dev, 478 &soc_component_dev_wm8776, wm8776_dai, ARRAY_SIZE(wm8776_dai)); 479 480 return ret; 481} 482 483static struct spi_driver wm8776_spi_driver = { 484 .driver = { 485 .name = "wm8776", 486 .of_match_table = wm8776_of_match, 487 }, 488 .probe = wm8776_spi_probe, 489}; 490#endif /* CONFIG_SPI_MASTER */ 491 492#if IS_ENABLED(CONFIG_I2C) 493static int wm8776_i2c_probe(struct i2c_client *i2c) 494{ 495 struct wm8776_priv *wm8776; 496 int ret; 497 498 wm8776 = devm_kzalloc(&i2c->dev, sizeof(struct wm8776_priv), 499 GFP_KERNEL); 500 if (wm8776 == NULL) 501 return -ENOMEM; 502 503 wm8776->regmap = devm_regmap_init_i2c(i2c, &wm8776_regmap); 504 if (IS_ERR(wm8776->regmap)) 505 return PTR_ERR(wm8776->regmap); 506 507 i2c_set_clientdata(i2c, wm8776); 508 509 ret = devm_snd_soc_register_component(&i2c->dev, 510 &soc_component_dev_wm8776, wm8776_dai, ARRAY_SIZE(wm8776_dai)); 511 512 return ret; 513} 514 515static const struct i2c_device_id wm8776_i2c_id[] = { 516 { "wm8775", WM8775 }, 517 { "wm8776", WM8776 }, 518 { } 519}; 520MODULE_DEVICE_TABLE(i2c, wm8776_i2c_id); 521 522static struct i2c_driver wm8776_i2c_driver = { 523 .driver = { 524 .name = "wm8776", 525 .of_match_table = wm8776_of_match, 526 }, 527 .probe_new = wm8776_i2c_probe, 528 .id_table = wm8776_i2c_id, 529}; 530#endif 531 532static int __init wm8776_modinit(void) 533{ 534 int ret = 0; 535#if IS_ENABLED(CONFIG_I2C) 536 ret = i2c_add_driver(&wm8776_i2c_driver); 537 if (ret != 0) { 538 printk(KERN_ERR "Failed to register wm8776 I2C driver: %d\n", 539 ret); 540 } 541#endif 542#if defined(CONFIG_SPI_MASTER) 543 ret = spi_register_driver(&wm8776_spi_driver); 544 if (ret != 0) { 545 printk(KERN_ERR "Failed to register wm8776 SPI driver: %d\n", 546 ret); 547 } 548#endif 549 return ret; 550} 551module_init(wm8776_modinit); 552 553static void __exit wm8776_exit(void) 554{ 555#if IS_ENABLED(CONFIG_I2C) 556 i2c_del_driver(&wm8776_i2c_driver); 557#endif 558#if defined(CONFIG_SPI_MASTER) 559 spi_unregister_driver(&wm8776_spi_driver); 560#endif 561} 562module_exit(wm8776_exit); 563 564MODULE_DESCRIPTION("ASoC WM8776 driver"); 565MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 566MODULE_LICENSE("GPL");