wm8711.c (12211B)
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/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/regmap.h> 19#include <linux/spi/spi.h> 20#include <linux/slab.h> 21#include <linux/of_device.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 */ 32struct 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 */ 43static 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 48static 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 60static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); 61 62static const struct snd_kcontrol_new wm8711_snd_controls[] = { 63 64SOC_DOUBLE_R_TLV("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V, 65 0, 127, 0, out_tlv), 66SOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V, 67 7, 1, 0), 68 69}; 70 71/* Output Mixer */ 72static const struct snd_kcontrol_new wm8711_output_mixer_controls[] = { 73SOC_DAPM_SINGLE("Line Bypass Switch", WM8711_APANA, 3, 1, 0), 74SOC_DAPM_SINGLE("HiFi Playback Switch", WM8711_APANA, 4, 1, 0), 75}; 76 77static const struct snd_soc_dapm_widget wm8711_dapm_widgets[] = { 78SND_SOC_DAPM_MIXER("Output Mixer", WM8711_PWR, 4, 1, 79 &wm8711_output_mixer_controls[0], 80 ARRAY_SIZE(wm8711_output_mixer_controls)), 81SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8711_PWR, 3, 1), 82SND_SOC_DAPM_OUTPUT("LOUT"), 83SND_SOC_DAPM_OUTPUT("LHPOUT"), 84SND_SOC_DAPM_OUTPUT("ROUT"), 85SND_SOC_DAPM_OUTPUT("RHPOUT"), 86}; 87 88static 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 100struct _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 */ 110static 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 144static 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 155static 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 184static 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 195static 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 207static 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 220static 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 238static 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 297static 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 328static 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 338static 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 350static 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 368static 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 .non_legacy_dai_naming = 1, 382}; 383 384static const struct of_device_id wm8711_of_match[] = { 385 { .compatible = "wlf,wm8711", }, 386 { } 387}; 388MODULE_DEVICE_TABLE(of, wm8711_of_match); 389 390static const struct regmap_config wm8711_regmap = { 391 .reg_bits = 7, 392 .val_bits = 9, 393 .max_register = WM8711_RESET, 394 395 .reg_defaults = wm8711_reg_defaults, 396 .num_reg_defaults = ARRAY_SIZE(wm8711_reg_defaults), 397 .cache_type = REGCACHE_RBTREE, 398 399 .volatile_reg = wm8711_volatile, 400}; 401 402#if defined(CONFIG_SPI_MASTER) 403static int wm8711_spi_probe(struct spi_device *spi) 404{ 405 struct wm8711_priv *wm8711; 406 int ret; 407 408 wm8711 = devm_kzalloc(&spi->dev, sizeof(struct wm8711_priv), 409 GFP_KERNEL); 410 if (wm8711 == NULL) 411 return -ENOMEM; 412 413 wm8711->regmap = devm_regmap_init_spi(spi, &wm8711_regmap); 414 if (IS_ERR(wm8711->regmap)) 415 return PTR_ERR(wm8711->regmap); 416 417 spi_set_drvdata(spi, wm8711); 418 419 ret = devm_snd_soc_register_component(&spi->dev, 420 &soc_component_dev_wm8711, &wm8711_dai, 1); 421 422 return ret; 423} 424 425static struct spi_driver wm8711_spi_driver = { 426 .driver = { 427 .name = "wm8711", 428 .of_match_table = wm8711_of_match, 429 }, 430 .probe = wm8711_spi_probe, 431}; 432#endif /* CONFIG_SPI_MASTER */ 433 434#if IS_ENABLED(CONFIG_I2C) 435static int wm8711_i2c_probe(struct i2c_client *client) 436{ 437 struct wm8711_priv *wm8711; 438 int ret; 439 440 wm8711 = devm_kzalloc(&client->dev, sizeof(struct wm8711_priv), 441 GFP_KERNEL); 442 if (wm8711 == NULL) 443 return -ENOMEM; 444 445 wm8711->regmap = devm_regmap_init_i2c(client, &wm8711_regmap); 446 if (IS_ERR(wm8711->regmap)) 447 return PTR_ERR(wm8711->regmap); 448 449 i2c_set_clientdata(client, wm8711); 450 451 ret = devm_snd_soc_register_component(&client->dev, 452 &soc_component_dev_wm8711, &wm8711_dai, 1); 453 454 return ret; 455} 456 457static const struct i2c_device_id wm8711_i2c_id[] = { 458 { "wm8711", 0 }, 459 { } 460}; 461MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id); 462 463static struct i2c_driver wm8711_i2c_driver = { 464 .driver = { 465 .name = "wm8711", 466 .of_match_table = wm8711_of_match, 467 }, 468 .probe_new = wm8711_i2c_probe, 469 .id_table = wm8711_i2c_id, 470}; 471#endif 472 473static int __init wm8711_modinit(void) 474{ 475 int ret; 476#if IS_ENABLED(CONFIG_I2C) 477 ret = i2c_add_driver(&wm8711_i2c_driver); 478 if (ret != 0) { 479 printk(KERN_ERR "Failed to register WM8711 I2C driver: %d\n", 480 ret); 481 } 482#endif 483#if defined(CONFIG_SPI_MASTER) 484 ret = spi_register_driver(&wm8711_spi_driver); 485 if (ret != 0) { 486 printk(KERN_ERR "Failed to register WM8711 SPI driver: %d\n", 487 ret); 488 } 489#endif 490 return 0; 491} 492module_init(wm8711_modinit); 493 494static void __exit wm8711_exit(void) 495{ 496#if IS_ENABLED(CONFIG_I2C) 497 i2c_del_driver(&wm8711_i2c_driver); 498#endif 499#if defined(CONFIG_SPI_MASTER) 500 spi_unregister_driver(&wm8711_spi_driver); 501#endif 502} 503module_exit(wm8711_exit); 504 505MODULE_DESCRIPTION("ASoC WM8711 driver"); 506MODULE_AUTHOR("Mike Arthur"); 507MODULE_LICENSE("GPL");