1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * wm8711.c -- WM8711 ALSA SoC Audio driver 4 * 5 * Copyright 2006 Wolfson Microelectronics 6 * 7 * Author: Mike Arthur <Mike.Arthur@wolfsonmicro.com> 8 * 9 * Based on wm8731.c by Richard Purdie 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/tlv.h> 27 #include <sound/initval.h> 28 29 #include "wm8711.h" 30 31 /* codec private data */ 32 struct wm8711_priv { 33 struct regmap *regmap; 34 unsigned int sysclk; 35 }; 36 37 /* 38 * wm8711 register cache 39 * We can't read the WM8711 register space when we are 40 * using 2 wire for device control, so we cache them instead. 41 * There is no point in caching the reset register 42 */ 43 static const struct reg_default wm8711_reg_defaults[] = { 44 { 0, 0x0079 }, { 1, 0x0079 }, { 2, 0x000a }, { 3, 0x0008 }, 45 { 4, 0x009f }, { 5, 0x000a }, { 6, 0x0000 }, { 7, 0x0000 }, 46 }; 47 48 static bool wm8711_volatile(struct device *dev, unsigned int reg) 49 { 50 switch (reg) { 51 case WM8711_RESET: 52 return true; 53 default: 54 return false; 55 } 56 } 57 58 #define wm8711_reset(c) snd_soc_component_write(c, WM8711_RESET, 0) 59 60 static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); 61 62 static const struct snd_kcontrol_new wm8711_snd_controls[] = { 63 64 SOC_DOUBLE_R_TLV("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V, 65 0, 127, 0, out_tlv), 66 SOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V, 67 7, 1, 0), 68 69 }; 70 71 /* Output Mixer */ 72 static const struct snd_kcontrol_new wm8711_output_mixer_controls[] = { 73 SOC_DAPM_SINGLE("Line Bypass Switch", WM8711_APANA, 3, 1, 0), 74 SOC_DAPM_SINGLE("HiFi Playback Switch", WM8711_APANA, 4, 1, 0), 75 }; 76 77 static const struct snd_soc_dapm_widget wm8711_dapm_widgets[] = { 78 SND_SOC_DAPM_MIXER("Output Mixer", WM8711_PWR, 4, 1, 79 &wm8711_output_mixer_controls[0], 80 ARRAY_SIZE(wm8711_output_mixer_controls)), 81 SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8711_PWR, 3, 1), 82 SND_SOC_DAPM_OUTPUT("LOUT"), 83 SND_SOC_DAPM_OUTPUT("LHPOUT"), 84 SND_SOC_DAPM_OUTPUT("ROUT"), 85 SND_SOC_DAPM_OUTPUT("RHPOUT"), 86 }; 87 88 static const struct snd_soc_dapm_route wm8711_intercon[] = { 89 /* output mixer */ 90 {"Output Mixer", "Line Bypass Switch", "Line Input"}, 91 {"Output Mixer", "HiFi Playback Switch", "DAC"}, 92 93 /* outputs */ 94 {"RHPOUT", NULL, "Output Mixer"}, 95 {"ROUT", NULL, "Output Mixer"}, 96 {"LHPOUT", NULL, "Output Mixer"}, 97 {"LOUT", NULL, "Output Mixer"}, 98 }; 99 100 struct _coeff_div { 101 u32 mclk; 102 u32 rate; 103 u16 fs; 104 u8 sr:4; 105 u8 bosr:1; 106 u8 usb:1; 107 }; 108 109 /* codec mclk clock divider coefficients */ 110 static const struct _coeff_div coeff_div[] = { 111 /* 48k */ 112 {12288000, 48000, 256, 0x0, 0x0, 0x0}, 113 {18432000, 48000, 384, 0x0, 0x1, 0x0}, 114 {12000000, 48000, 250, 0x0, 0x0, 0x1}, 115 116 /* 32k */ 117 {12288000, 32000, 384, 0x6, 0x0, 0x0}, 118 {18432000, 32000, 576, 0x6, 0x1, 0x0}, 119 {12000000, 32000, 375, 0x6, 0x0, 0x1}, 120 121 /* 8k */ 122 {12288000, 8000, 1536, 0x3, 0x0, 0x0}, 123 {18432000, 8000, 2304, 0x3, 0x1, 0x0}, 124 {11289600, 8000, 1408, 0xb, 0x0, 0x0}, 125 {16934400, 8000, 2112, 0xb, 0x1, 0x0}, 126 {12000000, 8000, 1500, 0x3, 0x0, 0x1}, 127 128 /* 96k */ 129 {12288000, 96000, 128, 0x7, 0x0, 0x0}, 130 {18432000, 96000, 192, 0x7, 0x1, 0x0}, 131 {12000000, 96000, 125, 0x7, 0x0, 0x1}, 132 133 /* 44.1k */ 134 {11289600, 44100, 256, 0x8, 0x0, 0x0}, 135 {16934400, 44100, 384, 0x8, 0x1, 0x0}, 136 {12000000, 44100, 272, 0x8, 0x1, 0x1}, 137 138 /* 88.2k */ 139 {11289600, 88200, 128, 0xf, 0x0, 0x0}, 140 {16934400, 88200, 192, 0xf, 0x1, 0x0}, 141 {12000000, 88200, 136, 0xf, 0x1, 0x1}, 142 }; 143 144 static inline int get_coeff(int mclk, int rate) 145 { 146 int i; 147 148 for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { 149 if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) 150 return i; 151 } 152 return 0; 153 } 154 155 static int wm8711_hw_params(struct snd_pcm_substream *substream, 156 struct snd_pcm_hw_params *params, 157 struct snd_soc_dai *dai) 158 { 159 struct snd_soc_component *component = dai->component; 160 struct wm8711_priv *wm8711 = snd_soc_component_get_drvdata(component); 161 u16 iface = snd_soc_component_read(component, WM8711_IFACE) & 0xfff3; 162 int i = get_coeff(wm8711->sysclk, params_rate(params)); 163 u16 srate = (coeff_div[i].sr << 2) | 164 (coeff_div[i].bosr << 1) | coeff_div[i].usb; 165 166 snd_soc_component_write(component, WM8711_SRATE, srate); 167 168 /* bit size */ 169 switch (params_width(params)) { 170 case 16: 171 break; 172 case 20: 173 iface |= 0x0004; 174 break; 175 case 24: 176 iface |= 0x0008; 177 break; 178 } 179 180 snd_soc_component_write(component, WM8711_IFACE, iface); 181 return 0; 182 } 183 184 static int wm8711_pcm_prepare(struct snd_pcm_substream *substream, 185 struct snd_soc_dai *dai) 186 { 187 struct snd_soc_component *component = dai->component; 188 189 /* set active */ 190 snd_soc_component_write(component, WM8711_ACTIVE, 0x0001); 191 192 return 0; 193 } 194 195 static void wm8711_shutdown(struct snd_pcm_substream *substream, 196 struct snd_soc_dai *dai) 197 { 198 struct snd_soc_component *component = dai->component; 199 200 /* deactivate */ 201 if (!snd_soc_component_active(component)) { 202 udelay(50); 203 snd_soc_component_write(component, WM8711_ACTIVE, 0x0); 204 } 205 } 206 207 static int wm8711_mute(struct snd_soc_dai *dai, int mute, int direction) 208 { 209 struct snd_soc_component *component = dai->component; 210 u16 mute_reg = snd_soc_component_read(component, WM8711_APDIGI) & 0xfff7; 211 212 if (mute) 213 snd_soc_component_write(component, WM8711_APDIGI, mute_reg | 0x8); 214 else 215 snd_soc_component_write(component, WM8711_APDIGI, mute_reg); 216 217 return 0; 218 } 219 220 static int wm8711_set_dai_sysclk(struct snd_soc_dai *codec_dai, 221 int clk_id, unsigned int freq, int dir) 222 { 223 struct snd_soc_component *component = codec_dai->component; 224 struct wm8711_priv *wm8711 = snd_soc_component_get_drvdata(component); 225 226 switch (freq) { 227 case 11289600: 228 case 12000000: 229 case 12288000: 230 case 16934400: 231 case 18432000: 232 wm8711->sysclk = freq; 233 return 0; 234 } 235 return -EINVAL; 236 } 237 238 static int wm8711_set_dai_fmt(struct snd_soc_dai *codec_dai, 239 unsigned int fmt) 240 { 241 struct snd_soc_component *component = codec_dai->component; 242 u16 iface = snd_soc_component_read(component, WM8711_IFACE) & 0x000c; 243 244 /* set master/slave audio interface */ 245 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 246 case SND_SOC_DAIFMT_CBM_CFM: 247 iface |= 0x0040; 248 break; 249 case SND_SOC_DAIFMT_CBS_CFS: 250 break; 251 default: 252 return -EINVAL; 253 } 254 255 /* interface format */ 256 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 257 case SND_SOC_DAIFMT_I2S: 258 iface |= 0x0002; 259 break; 260 case SND_SOC_DAIFMT_RIGHT_J: 261 break; 262 case SND_SOC_DAIFMT_LEFT_J: 263 iface |= 0x0001; 264 break; 265 case SND_SOC_DAIFMT_DSP_A: 266 iface |= 0x0003; 267 break; 268 case SND_SOC_DAIFMT_DSP_B: 269 iface |= 0x0013; 270 break; 271 default: 272 return -EINVAL; 273 } 274 275 /* clock inversion */ 276 switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 277 case SND_SOC_DAIFMT_NB_NF: 278 break; 279 case SND_SOC_DAIFMT_IB_IF: 280 iface |= 0x0090; 281 break; 282 case SND_SOC_DAIFMT_IB_NF: 283 iface |= 0x0080; 284 break; 285 case SND_SOC_DAIFMT_NB_IF: 286 iface |= 0x0010; 287 break; 288 default: 289 return -EINVAL; 290 } 291 292 /* set iface */ 293 snd_soc_component_write(component, WM8711_IFACE, iface); 294 return 0; 295 } 296 297 static int wm8711_set_bias_level(struct snd_soc_component *component, 298 enum snd_soc_bias_level level) 299 { 300 struct wm8711_priv *wm8711 = snd_soc_component_get_drvdata(component); 301 u16 reg = snd_soc_component_read(component, WM8711_PWR) & 0xff7f; 302 303 switch (level) { 304 case SND_SOC_BIAS_ON: 305 snd_soc_component_write(component, WM8711_PWR, reg); 306 break; 307 case SND_SOC_BIAS_PREPARE: 308 break; 309 case SND_SOC_BIAS_STANDBY: 310 if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) 311 regcache_sync(wm8711->regmap); 312 313 snd_soc_component_write(component, WM8711_PWR, reg | 0x0040); 314 break; 315 case SND_SOC_BIAS_OFF: 316 snd_soc_component_write(component, WM8711_ACTIVE, 0x0); 317 snd_soc_component_write(component, WM8711_PWR, 0xffff); 318 break; 319 } 320 return 0; 321 } 322 323 #define WM8711_RATES SNDRV_PCM_RATE_8000_96000 324 325 #define WM8711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ 326 SNDRV_PCM_FMTBIT_S24_LE) 327 328 static const struct snd_soc_dai_ops wm8711_ops = { 329 .prepare = wm8711_pcm_prepare, 330 .hw_params = wm8711_hw_params, 331 .shutdown = wm8711_shutdown, 332 .mute_stream = wm8711_mute, 333 .set_sysclk = wm8711_set_dai_sysclk, 334 .set_fmt = wm8711_set_dai_fmt, 335 .no_capture_mute = 1, 336 }; 337 338 static struct snd_soc_dai_driver wm8711_dai = { 339 .name = "wm8711-hifi", 340 .playback = { 341 .stream_name = "Playback", 342 .channels_min = 1, 343 .channels_max = 2, 344 .rates = WM8711_RATES, 345 .formats = WM8711_FORMATS, 346 }, 347 .ops = &wm8711_ops, 348 }; 349 350 static int wm8711_probe(struct snd_soc_component *component) 351 { 352 int ret; 353 354 ret = wm8711_reset(component); 355 if (ret < 0) { 356 dev_err(component->dev, "Failed to issue reset\n"); 357 return ret; 358 } 359 360 /* Latch the update bits */ 361 snd_soc_component_update_bits(component, WM8711_LOUT1V, 0x0100, 0x0100); 362 snd_soc_component_update_bits(component, WM8711_ROUT1V, 0x0100, 0x0100); 363 364 return ret; 365 366 } 367 368 static const struct snd_soc_component_driver soc_component_dev_wm8711 = { 369 .probe = wm8711_probe, 370 .set_bias_level = wm8711_set_bias_level, 371 .controls = wm8711_snd_controls, 372 .num_controls = ARRAY_SIZE(wm8711_snd_controls), 373 .dapm_widgets = wm8711_dapm_widgets, 374 .num_dapm_widgets = ARRAY_SIZE(wm8711_dapm_widgets), 375 .dapm_routes = wm8711_intercon, 376 .num_dapm_routes = ARRAY_SIZE(wm8711_intercon), 377 .suspend_bias_off = 1, 378 .idle_bias_on = 1, 379 .use_pmdown_time = 1, 380 .endianness = 1, 381 }; 382 383 static const struct of_device_id wm8711_of_match[] = { 384 { .compatible = "wlf,wm8711", }, 385 { } 386 }; 387 MODULE_DEVICE_TABLE(of, wm8711_of_match); 388 389 static const struct regmap_config wm8711_regmap = { 390 .reg_bits = 7, 391 .val_bits = 9, 392 .max_register = WM8711_RESET, 393 394 .reg_defaults = wm8711_reg_defaults, 395 .num_reg_defaults = ARRAY_SIZE(wm8711_reg_defaults), 396 .cache_type = REGCACHE_MAPLE, 397 398 .volatile_reg = wm8711_volatile, 399 }; 400 401 #if defined(CONFIG_SPI_MASTER) 402 static int wm8711_spi_probe(struct spi_device *spi) 403 { 404 struct wm8711_priv *wm8711; 405 int ret; 406 407 wm8711 = devm_kzalloc(&spi->dev, sizeof(struct wm8711_priv), 408 GFP_KERNEL); 409 if (wm8711 == NULL) 410 return -ENOMEM; 411 412 wm8711->regmap = devm_regmap_init_spi(spi, &wm8711_regmap); 413 if (IS_ERR(wm8711->regmap)) 414 return PTR_ERR(wm8711->regmap); 415 416 spi_set_drvdata(spi, wm8711); 417 418 ret = devm_snd_soc_register_component(&spi->dev, 419 &soc_component_dev_wm8711, &wm8711_dai, 1); 420 421 return ret; 422 } 423 424 static struct spi_driver wm8711_spi_driver = { 425 .driver = { 426 .name = "wm8711", 427 .of_match_table = wm8711_of_match, 428 }, 429 .probe = wm8711_spi_probe, 430 }; 431 #endif /* CONFIG_SPI_MASTER */ 432 433 #if IS_ENABLED(CONFIG_I2C) 434 static int wm8711_i2c_probe(struct i2c_client *client) 435 { 436 struct wm8711_priv *wm8711; 437 int ret; 438 439 wm8711 = devm_kzalloc(&client->dev, sizeof(struct wm8711_priv), 440 GFP_KERNEL); 441 if (wm8711 == NULL) 442 return -ENOMEM; 443 444 wm8711->regmap = devm_regmap_init_i2c(client, &wm8711_regmap); 445 if (IS_ERR(wm8711->regmap)) 446 return PTR_ERR(wm8711->regmap); 447 448 i2c_set_clientdata(client, wm8711); 449 450 ret = devm_snd_soc_register_component(&client->dev, 451 &soc_component_dev_wm8711, &wm8711_dai, 1); 452 453 return ret; 454 } 455 456 static const struct i2c_device_id wm8711_i2c_id[] = { 457 { "wm8711" }, 458 { } 459 }; 460 MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id); 461 462 static struct i2c_driver wm8711_i2c_driver = { 463 .driver = { 464 .name = "wm8711", 465 .of_match_table = wm8711_of_match, 466 }, 467 .probe = wm8711_i2c_probe, 468 .id_table = wm8711_i2c_id, 469 }; 470 #endif 471 472 static int __init wm8711_modinit(void) 473 { 474 int ret; 475 #if IS_ENABLED(CONFIG_I2C) 476 ret = i2c_add_driver(&wm8711_i2c_driver); 477 if (ret != 0) { 478 printk(KERN_ERR "Failed to register WM8711 I2C driver: %d\n", 479 ret); 480 } 481 #endif 482 #if defined(CONFIG_SPI_MASTER) 483 ret = spi_register_driver(&wm8711_spi_driver); 484 if (ret != 0) { 485 printk(KERN_ERR "Failed to register WM8711 SPI driver: %d\n", 486 ret); 487 } 488 #endif 489 return 0; 490 } 491 module_init(wm8711_modinit); 492 493 static void __exit wm8711_exit(void) 494 { 495 #if IS_ENABLED(CONFIG_I2C) 496 i2c_del_driver(&wm8711_i2c_driver); 497 #endif 498 #if defined(CONFIG_SPI_MASTER) 499 spi_unregister_driver(&wm8711_spi_driver); 500 #endif 501 } 502 module_exit(wm8711_exit); 503 504 MODULE_DESCRIPTION("ASoC WM8711 driver"); 505 MODULE_AUTHOR("Mike Arthur"); 506 MODULE_LICENSE("GPL"); 507
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.