Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: WM8804: Initial driver

The WM8804 is a high performance consumer mode S/PDIF transceiver with
support for 1 received channel and 1 transmitted channel.

Signed-off-by: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

authored by

Dimitris Papastamos and committed by
Mark Brown
33cf45c8 6b90b55c

+887
+4
sound/soc/codecs/Kconfig
··· 56 56 select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI 57 57 select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI 58 58 select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI 59 + select SND_SOC_WM8804 if SND_SOC_I2C_AND_SPI 59 60 select SND_SOC_WM8900 if I2C 60 61 select SND_SOC_WM8903 if I2C 61 62 select SND_SOC_WM8904 if I2C ··· 236 235 tristate 237 236 238 237 config SND_SOC_WM8776 238 + tristate 239 + 240 + config SND_SOC_WM8804 239 241 tristate 240 242 241 243 config SND_SOC_WM8900
+2
sound/soc/codecs/Makefile
··· 41 41 snd-soc-wm8750-objs := wm8750.o 42 42 snd-soc-wm8753-objs := wm8753.o 43 43 snd-soc-wm8776-objs := wm8776.o 44 + snd-soc-wm8804-objs := wm8804.o 44 45 snd-soc-wm8900-objs := wm8900.o 45 46 snd-soc-wm8903-objs := wm8903.o 46 47 snd-soc-wm8904-objs := wm8904.o ··· 115 114 obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o 116 115 obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o 117 116 obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o 117 + obj-$(CONFIG_SND_SOC_WM8804) += snd-soc-wm8804.o 118 118 obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o 119 119 obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o 120 120 obj-$(CONFIG_SND_SOC_WM8904) += snd-soc-wm8904.o
+820
sound/soc/codecs/wm8804.c
··· 1 + /* 2 + * wm8804.c -- WM8804 S/PDIF transceiver driver 3 + * 4 + * Copyright 2010 Wolfson Microelectronics plc 5 + * 6 + * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com> 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License version 2 as 10 + * published by the Free Software Foundation. 11 + */ 12 + 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/spi/spi.h> 20 + #include <linux/regulator/consumer.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/soc-dapm.h> 27 + #include <sound/initval.h> 28 + #include <sound/tlv.h> 29 + 30 + #include "wm8804.h" 31 + 32 + #define WM8804_NUM_SUPPLIES 2 33 + static const char *wm8804_supply_names[WM8804_NUM_SUPPLIES] = { 34 + "PVDD", 35 + "DVDD" 36 + }; 37 + 38 + static const u8 wm8804_reg_defs[] = { 39 + 0x05, /* R0 - RST/DEVID1 */ 40 + 0x88, /* R1 - DEVID2 */ 41 + 0x04, /* R2 - DEVREV */ 42 + 0x21, /* R3 - PLL1 */ 43 + 0xFD, /* R4 - PLL2 */ 44 + 0x36, /* R5 - PLL3 */ 45 + 0x07, /* R6 - PLL4 */ 46 + 0x16, /* R7 - PLL5 */ 47 + 0x18, /* R8 - PLL6 */ 48 + 0xFF, /* R9 - SPDMODE */ 49 + 0x00, /* R10 - INTMASK */ 50 + 0x00, /* R11 - INTSTAT */ 51 + 0x00, /* R12 - SPDSTAT */ 52 + 0x00, /* R13 - RXCHAN1 */ 53 + 0x00, /* R14 - RXCHAN2 */ 54 + 0x00, /* R15 - RXCHAN3 */ 55 + 0x00, /* R16 - RXCHAN4 */ 56 + 0x00, /* R17 - RXCHAN5 */ 57 + 0x00, /* R18 - SPDTX1 */ 58 + 0x00, /* R19 - SPDTX2 */ 59 + 0x00, /* R20 - SPDTX3 */ 60 + 0x71, /* R21 - SPDTX4 */ 61 + 0x0B, /* R22 - SPDTX5 */ 62 + 0x70, /* R23 - GPO0 */ 63 + 0x57, /* R24 - GPO1 */ 64 + 0x00, /* R25 */ 65 + 0x42, /* R26 - GPO2 */ 66 + 0x06, /* R27 - AIFTX */ 67 + 0x06, /* R28 - AIFRX */ 68 + 0x80, /* R29 - SPDRX1 */ 69 + 0x07, /* R30 - PWRDN */ 70 + }; 71 + 72 + struct wm8804_priv { 73 + enum snd_soc_control_type control_type; 74 + struct regulator_bulk_data supplies[WM8804_NUM_SUPPLIES]; 75 + struct notifier_block disable_nb[WM8804_NUM_SUPPLIES]; 76 + struct snd_soc_codec *codec; 77 + }; 78 + 79 + static int txsrc_get(struct snd_kcontrol *kcontrol, 80 + struct snd_ctl_elem_value *ucontrol); 81 + 82 + static int txsrc_put(struct snd_kcontrol *kcontrol, 83 + struct snd_ctl_elem_value *ucontrol); 84 + 85 + /* 86 + * We can't use the same notifier block for more than one supply and 87 + * there's no way I can see to get from a callback to the caller 88 + * except container_of(). 89 + */ 90 + #define WM8804_REGULATOR_EVENT(n) \ 91 + static int wm8804_regulator_event_##n(struct notifier_block *nb, \ 92 + unsigned long event, void *data) \ 93 + { \ 94 + struct wm8804_priv *wm8804 = container_of(nb, struct wm8804_priv, \ 95 + disable_nb[n]); \ 96 + if (event & REGULATOR_EVENT_DISABLE) { \ 97 + wm8804->codec->cache_sync = 1; \ 98 + } \ 99 + return 0; \ 100 + } 101 + 102 + WM8804_REGULATOR_EVENT(0) 103 + WM8804_REGULATOR_EVENT(1) 104 + 105 + static const char *txsrc_text[] = { "S/PDIF RX", "AIF" }; 106 + static const SOC_ENUM_SINGLE_EXT_DECL(txsrc, txsrc_text); 107 + 108 + static const struct snd_kcontrol_new wm8804_snd_controls[] = { 109 + SOC_ENUM_EXT("Input Source", txsrc, txsrc_get, txsrc_put), 110 + SOC_SINGLE("TX Playback Switch", WM8804_PWRDN, 2, 1, 1), 111 + SOC_SINGLE("AIF Playback Switch", WM8804_PWRDN, 4, 1, 1) 112 + }; 113 + 114 + static int txsrc_get(struct snd_kcontrol *kcontrol, 115 + struct snd_ctl_elem_value *ucontrol) 116 + { 117 + struct snd_soc_codec *codec; 118 + unsigned int src; 119 + 120 + codec = snd_kcontrol_chip(kcontrol); 121 + src = snd_soc_read(codec, WM8804_SPDTX4); 122 + if (src & 0x40) 123 + ucontrol->value.integer.value[0] = 1; 124 + else 125 + ucontrol->value.integer.value[0] = 0; 126 + 127 + return 0; 128 + } 129 + 130 + static int txsrc_put(struct snd_kcontrol *kcontrol, 131 + struct snd_ctl_elem_value *ucontrol) 132 + { 133 + struct snd_soc_codec *codec; 134 + unsigned int src, txpwr; 135 + 136 + codec = snd_kcontrol_chip(kcontrol); 137 + 138 + if (ucontrol->value.integer.value[0] != 0 139 + && ucontrol->value.integer.value[0] != 1) 140 + return -EINVAL; 141 + 142 + src = snd_soc_read(codec, WM8804_SPDTX4); 143 + switch ((src & 0x40) >> 6) { 144 + case 0: 145 + if (!ucontrol->value.integer.value[0]) 146 + return 0; 147 + break; 148 + case 1: 149 + if (ucontrol->value.integer.value[1]) 150 + return 0; 151 + break; 152 + } 153 + 154 + /* save the current power state of the transmitter */ 155 + txpwr = snd_soc_read(codec, WM8804_PWRDN) & 0x4; 156 + /* power down the transmitter */ 157 + snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x4); 158 + /* set the tx source */ 159 + snd_soc_update_bits(codec, WM8804_SPDTX4, 0x40, 160 + ucontrol->value.integer.value[0] << 6); 161 + 162 + if (ucontrol->value.integer.value[0]) { 163 + /* power down the receiver */ 164 + snd_soc_update_bits(codec, WM8804_PWRDN, 0x2, 0x2); 165 + /* power up the AIF */ 166 + snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0); 167 + } else { 168 + /* don't power down the AIF -- may be used as an output */ 169 + /* power up the receiver */ 170 + snd_soc_update_bits(codec, WM8804_PWRDN, 0x2, 0); 171 + } 172 + 173 + /* restore the transmitter's configuration */ 174 + snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, txpwr); 175 + 176 + return 0; 177 + } 178 + 179 + static int wm8804_volatile(unsigned int reg) 180 + { 181 + switch (reg) { 182 + case WM8804_RST_DEVID1: 183 + case WM8804_DEVID2: 184 + case WM8804_DEVREV: 185 + case WM8804_INTSTAT: 186 + case WM8804_SPDSTAT: 187 + case WM8804_RXCHAN1: 188 + case WM8804_RXCHAN2: 189 + case WM8804_RXCHAN3: 190 + case WM8804_RXCHAN4: 191 + case WM8804_RXCHAN5: 192 + return 1; 193 + default: 194 + break; 195 + } 196 + 197 + return 0; 198 + } 199 + 200 + static int wm8804_reset(struct snd_soc_codec *codec) 201 + { 202 + return snd_soc_write(codec, WM8804_RST_DEVID1, 0x0); 203 + } 204 + 205 + static int wm8804_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 206 + { 207 + struct snd_soc_codec *codec; 208 + u16 format, master, bcp, lrp; 209 + 210 + codec = dai->codec; 211 + 212 + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 213 + case SND_SOC_DAIFMT_I2S: 214 + format = 0x2; 215 + break; 216 + case SND_SOC_DAIFMT_RIGHT_J: 217 + format = 0x0; 218 + break; 219 + case SND_SOC_DAIFMT_LEFT_J: 220 + format = 0x1; 221 + break; 222 + case SND_SOC_DAIFMT_DSP_A: 223 + case SND_SOC_DAIFMT_DSP_B: 224 + format = 0x3; 225 + break; 226 + default: 227 + dev_err(dai->dev, "Unknown dai format\n"); 228 + return -EINVAL; 229 + } 230 + 231 + /* set data format */ 232 + snd_soc_update_bits(codec, WM8804_AIFTX, 0x3, format); 233 + snd_soc_update_bits(codec, WM8804_AIFRX, 0x3, format); 234 + 235 + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 236 + case SND_SOC_DAIFMT_CBM_CFM: 237 + master = 1; 238 + break; 239 + case SND_SOC_DAIFMT_CBS_CFS: 240 + master = 0; 241 + break; 242 + default: 243 + dev_err(dai->dev, "Unknown master/slave configuration\n"); 244 + return -EINVAL; 245 + } 246 + 247 + /* set master/slave mode */ 248 + snd_soc_update_bits(codec, WM8804_AIFRX, 0x40, master << 6); 249 + 250 + bcp = lrp = 0; 251 + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 252 + case SND_SOC_DAIFMT_NB_NF: 253 + break; 254 + case SND_SOC_DAIFMT_IB_IF: 255 + bcp = lrp = 1; 256 + break; 257 + case SND_SOC_DAIFMT_IB_NF: 258 + bcp = 1; 259 + break; 260 + case SND_SOC_DAIFMT_NB_IF: 261 + lrp = 1; 262 + break; 263 + default: 264 + dev_err(dai->dev, "Unknown polarity configuration\n"); 265 + return -EINVAL; 266 + } 267 + 268 + /* set frame inversion */ 269 + snd_soc_update_bits(codec, WM8804_AIFTX, 0x10 | 0x20, 270 + (bcp << 4) | (lrp << 5)); 271 + snd_soc_update_bits(codec, WM8804_AIFRX, 0x10 | 0x20, 272 + (bcp << 4) | (lrp << 5)); 273 + return 0; 274 + } 275 + 276 + static int wm8804_hw_params(struct snd_pcm_substream *substream, 277 + struct snd_pcm_hw_params *params, 278 + struct snd_soc_dai *dai) 279 + { 280 + struct snd_soc_codec *codec; 281 + u16 blen; 282 + 283 + codec = dai->codec; 284 + 285 + switch (params_format(params)) { 286 + case SNDRV_PCM_FORMAT_S16_LE: 287 + blen = 0x0; 288 + break; 289 + case SNDRV_PCM_FORMAT_S20_3LE: 290 + blen = 0x1; 291 + break; 292 + case SNDRV_PCM_FORMAT_S24_LE: 293 + blen = 0x2; 294 + break; 295 + default: 296 + dev_err(dai->dev, "Unsupported word length: %u\n", 297 + params_format(params)); 298 + return -EINVAL; 299 + } 300 + 301 + /* set word length */ 302 + snd_soc_update_bits(codec, WM8804_AIFTX, 0xc, blen << 2); 303 + snd_soc_update_bits(codec, WM8804_AIFRX, 0xc, blen << 2); 304 + 305 + return 0; 306 + } 307 + 308 + struct pll_div { 309 + u32 prescale:1; 310 + u32 mclkdiv:1; 311 + u32 freqmode:2; 312 + u32 n:4; 313 + u32 k:22; 314 + }; 315 + 316 + /* PLL rate to output rate divisions */ 317 + static struct { 318 + unsigned int div; 319 + unsigned int freqmode; 320 + unsigned int mclkdiv; 321 + } post_table[] = { 322 + { 2, 0, 0 }, 323 + { 4, 0, 1 }, 324 + { 4, 1, 0 }, 325 + { 8, 1, 1 }, 326 + { 8, 2, 0 }, 327 + { 16, 2, 1 }, 328 + { 12, 3, 0 }, 329 + { 24, 3, 1 } 330 + }; 331 + 332 + #define FIXED_PLL_SIZE ((1ULL << 22) * 10) 333 + static int pll_factors(struct pll_div *pll_div, unsigned int target, 334 + unsigned int source) 335 + { 336 + u64 Kpart; 337 + unsigned long int K, Ndiv, Nmod, tmp; 338 + int i; 339 + 340 + /* 341 + * Scale the output frequency up; the PLL should run in the 342 + * region of 90-100MHz. 343 + */ 344 + for (i = 0; i < ARRAY_SIZE(post_table); i++) { 345 + tmp = target * post_table[i].div; 346 + if (tmp >= 90000000 && tmp <= 100000000) { 347 + pll_div->freqmode = post_table[i].freqmode; 348 + pll_div->mclkdiv = post_table[i].mclkdiv; 349 + target *= post_table[i].div; 350 + break; 351 + } 352 + } 353 + 354 + if (i == ARRAY_SIZE(post_table)) { 355 + pr_err("%s: Unable to scale output frequency: %uHz\n", 356 + __func__, target); 357 + return -EINVAL; 358 + } 359 + 360 + pll_div->prescale = 0; 361 + Ndiv = target / source; 362 + if (Ndiv < 5) { 363 + source >>= 1; 364 + pll_div->prescale = 1; 365 + Ndiv = target / source; 366 + } 367 + 368 + if (Ndiv < 5 || Ndiv > 13) { 369 + pr_err("%s: WM8804 N value is not within the recommended range: %lu\n", 370 + __func__, Ndiv); 371 + return -EINVAL; 372 + } 373 + pll_div->n = Ndiv; 374 + 375 + Nmod = target % source; 376 + Kpart = FIXED_PLL_SIZE * (u64)Nmod; 377 + 378 + do_div(Kpart, source); 379 + 380 + K = Kpart & 0xffffffff; 381 + if ((K % 10) >= 5) 382 + K += 5; 383 + K /= 10; 384 + pll_div->k = K; 385 + 386 + return 0; 387 + } 388 + 389 + static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id, 390 + int source, unsigned int freq_in, 391 + unsigned int freq_out) 392 + { 393 + int ret; 394 + struct snd_soc_codec *codec; 395 + struct pll_div pll_div = { 0 }; 396 + 397 + codec = dai->codec; 398 + if (freq_in && freq_out) { 399 + ret = pll_factors(&pll_div, freq_out, freq_in); 400 + if (ret) 401 + return ret; 402 + } 403 + 404 + /* power down the PLL before reprogramming it */ 405 + snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0); 406 + 407 + if (!freq_in || !freq_out) 408 + return 0; 409 + 410 + /* set PLLN and PRESCALE */ 411 + snd_soc_update_bits(codec, WM8804_PLL4, 0xf | 0x10, 412 + pll_div.n | (pll_div.prescale << 4)); 413 + /* set mclkdiv and freqmode */ 414 + snd_soc_update_bits(codec, WM8804_PLL5, 0x3 | 0x8, 415 + pll_div.freqmode | (pll_div.mclkdiv << 3)); 416 + /* set PLLK */ 417 + snd_soc_write(codec, WM8804_PLL1, pll_div.k & 0xff); 418 + snd_soc_write(codec, WM8804_PLL2, (pll_div.k >> 8) & 0xff); 419 + snd_soc_write(codec, WM8804_PLL3, pll_div.k >> 16); 420 + 421 + /* power up the PLL */ 422 + snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0x1); 423 + 424 + return 0; 425 + } 426 + 427 + static int wm8804_set_sysclk(struct snd_soc_dai *dai, 428 + int clk_id, unsigned int freq, int dir) 429 + { 430 + struct snd_soc_codec *codec; 431 + 432 + codec = dai->codec; 433 + 434 + switch (clk_id) { 435 + case WM8804_TX_CLKSRC_MCLK: 436 + if ((freq >= 10000000 && freq <= 14400000) 437 + || (freq >= 16280000 && freq <= 27000000)) 438 + snd_soc_update_bits(codec, WM8804_PLL6, 0x80, 0x80); 439 + else { 440 + dev_err(dai->dev, "OSCCLOCK is not within the " 441 + "recommended range: %uHz\n", freq); 442 + return -EINVAL; 443 + } 444 + break; 445 + case WM8804_TX_CLKSRC_PLL: 446 + snd_soc_update_bits(codec, WM8804_PLL6, 0x80, 0); 447 + break; 448 + case WM8804_CLKOUT_SRC_CLK1: 449 + snd_soc_update_bits(codec, WM8804_PLL6, 0x8, 0); 450 + break; 451 + case WM8804_CLKOUT_SRC_OSCCLK: 452 + snd_soc_update_bits(codec, WM8804_PLL6, 0x8, 0x8); 453 + break; 454 + default: 455 + dev_err(dai->dev, "Unknown clock source: %d\n", clk_id); 456 + return -EINVAL; 457 + } 458 + 459 + return 0; 460 + } 461 + 462 + static int wm8804_set_clkdiv(struct snd_soc_dai *dai, 463 + int div_id, int div) 464 + { 465 + struct snd_soc_codec *codec; 466 + 467 + codec = dai->codec; 468 + switch (div_id) { 469 + case WM8804_CLKOUT_DIV: 470 + snd_soc_update_bits(codec, WM8804_PLL5, 0x30, 471 + (div & 0x3) << 4); 472 + break; 473 + default: 474 + dev_err(dai->dev, "Unknown clock divider: %d\n", div_id); 475 + return -EINVAL; 476 + } 477 + return 0; 478 + } 479 + 480 + static void wm8804_sync_cache(struct snd_soc_codec *codec) 481 + { 482 + short i; 483 + u8 *cache; 484 + 485 + if (!codec->cache_sync) 486 + return; 487 + 488 + codec->cache_only = 0; 489 + cache = codec->reg_cache; 490 + for (i = 0; i < codec->driver->reg_cache_size; i++) { 491 + if (i == WM8804_RST_DEVID1 || cache[i] == wm8804_reg_defs[i]) 492 + continue; 493 + snd_soc_write(codec, i, cache[i]); 494 + } 495 + codec->cache_sync = 0; 496 + } 497 + 498 + static int wm8804_set_bias_level(struct snd_soc_codec *codec, 499 + enum snd_soc_bias_level level) 500 + { 501 + int ret; 502 + struct wm8804_priv *wm8804; 503 + 504 + wm8804 = snd_soc_codec_get_drvdata(codec); 505 + switch (level) { 506 + case SND_SOC_BIAS_ON: 507 + break; 508 + case SND_SOC_BIAS_PREPARE: 509 + /* power up the OSC and the PLL */ 510 + snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0); 511 + break; 512 + case SND_SOC_BIAS_STANDBY: 513 + if (codec->bias_level == SND_SOC_BIAS_OFF) { 514 + ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies), 515 + wm8804->supplies); 516 + if (ret) { 517 + dev_err(codec->dev, 518 + "Failed to enable supplies: %d\n", 519 + ret); 520 + return ret; 521 + } 522 + wm8804_sync_cache(codec); 523 + } 524 + /* power down the OSC and the PLL */ 525 + snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0x9); 526 + break; 527 + case SND_SOC_BIAS_OFF: 528 + /* power down the OSC and the PLL */ 529 + snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0x9); 530 + regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), 531 + wm8804->supplies); 532 + break; 533 + } 534 + 535 + codec->bias_level = level; 536 + return 0; 537 + } 538 + 539 + #ifdef CONFIG_PM 540 + static int wm8804_suspend(struct snd_soc_codec *codec, pm_message_t state) 541 + { 542 + wm8804_set_bias_level(codec, SND_SOC_BIAS_OFF); 543 + return 0; 544 + } 545 + 546 + static int wm8804_resume(struct snd_soc_codec *codec) 547 + { 548 + wm8804_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 549 + return 0; 550 + } 551 + #else 552 + #define wm8804_suspend NULL 553 + #define wm8804_resume NULL 554 + #endif 555 + 556 + static int wm8804_remove(struct snd_soc_codec *codec) 557 + { 558 + struct wm8804_priv *wm8804; 559 + int i; 560 + 561 + wm8804 = snd_soc_codec_get_drvdata(codec); 562 + wm8804_set_bias_level(codec, SND_SOC_BIAS_OFF); 563 + 564 + for (i = 0; i < ARRAY_SIZE(wm8804->supplies); ++i) 565 + regulator_unregister_notifier(wm8804->supplies[i].consumer, 566 + &wm8804->disable_nb[i]); 567 + regulator_bulk_free(ARRAY_SIZE(wm8804->supplies), wm8804->supplies); 568 + return 0; 569 + } 570 + 571 + static int wm8804_probe(struct snd_soc_codec *codec) 572 + { 573 + struct wm8804_priv *wm8804; 574 + int i, id1, id2, ret; 575 + 576 + wm8804 = snd_soc_codec_get_drvdata(codec); 577 + wm8804->codec = codec; 578 + 579 + codec->idle_bias_off = 1; 580 + 581 + ret = snd_soc_codec_set_cache_io(codec, 8, 8, wm8804->control_type); 582 + if (ret < 0) { 583 + dev_err(codec->dev, "Failed to set cache i/o: %d\n", ret); 584 + return ret; 585 + } 586 + 587 + for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) 588 + wm8804->supplies[i].supply = wm8804_supply_names[i]; 589 + 590 + ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8804->supplies), 591 + wm8804->supplies); 592 + if (ret) { 593 + dev_err(codec->dev, "Failed to request supplies: %d\n", ret); 594 + return ret; 595 + } 596 + 597 + wm8804->disable_nb[0].notifier_call = wm8804_regulator_event_0; 598 + wm8804->disable_nb[1].notifier_call = wm8804_regulator_event_1; 599 + 600 + /* This should really be moved into the regulator core */ 601 + for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) { 602 + ret = regulator_register_notifier(wm8804->supplies[i].consumer, 603 + &wm8804->disable_nb[i]); 604 + if (ret != 0) { 605 + dev_err(codec->dev, 606 + "Failed to register regulator notifier: %d\n", 607 + ret); 608 + } 609 + } 610 + 611 + ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies), 612 + wm8804->supplies); 613 + if (ret) { 614 + dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); 615 + goto err_reg_get; 616 + } 617 + 618 + id1 = snd_soc_read(codec, WM8804_RST_DEVID1); 619 + if (id1 < 0) { 620 + dev_err(codec->dev, "Failed to read device ID: %d\n", id1); 621 + ret = id1; 622 + goto err_reg_enable; 623 + } 624 + 625 + id2 = snd_soc_read(codec, WM8804_DEVID2); 626 + if (id2 < 0) { 627 + dev_err(codec->dev, "Failed to read device ID: %d\n", id2); 628 + ret = id2; 629 + goto err_reg_enable; 630 + } 631 + 632 + id2 = (id2 << 8) | id1; 633 + 634 + if (id2 != ((wm8804_reg_defs[WM8804_DEVID2] << 8) 635 + | wm8804_reg_defs[WM8804_RST_DEVID1])) { 636 + dev_err(codec->dev, "Invalid device ID: %#x\n", id2); 637 + ret = -EINVAL; 638 + goto err_reg_enable; 639 + } 640 + 641 + ret = wm8804_reset(codec); 642 + if (ret < 0) { 643 + dev_err(codec->dev, "Failed to issue reset: %d\n", ret); 644 + goto err_reg_enable; 645 + } 646 + 647 + wm8804_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 648 + 649 + snd_soc_add_controls(codec, wm8804_snd_controls, 650 + ARRAY_SIZE(wm8804_snd_controls)); 651 + return 0; 652 + 653 + err_reg_enable: 654 + regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies); 655 + err_reg_get: 656 + regulator_bulk_free(ARRAY_SIZE(wm8804->supplies), wm8804->supplies); 657 + return ret; 658 + } 659 + 660 + static struct snd_soc_dai_ops wm8804_dai_ops = { 661 + .hw_params = wm8804_hw_params, 662 + .set_fmt = wm8804_set_fmt, 663 + .set_sysclk = wm8804_set_sysclk, 664 + .set_clkdiv = wm8804_set_clkdiv, 665 + .set_pll = wm8804_set_pll 666 + }; 667 + 668 + #define WM8804_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ 669 + SNDRV_PCM_FMTBIT_S24_LE) 670 + 671 + static struct snd_soc_dai_driver wm8804_dai = { 672 + .name = "wm8804-s/pdif", 673 + .playback = { 674 + .stream_name = "Playback", 675 + .channels_min = 2, 676 + .channels_max = 2, 677 + .rates = SNDRV_PCM_RATE_8000_192000, 678 + .formats = WM8804_FORMATS, 679 + }, 680 + .capture = { 681 + .stream_name = "Capture", 682 + .channels_min = 2, 683 + .channels_max = 2, 684 + .rates = SNDRV_PCM_RATE_8000_192000, 685 + .formats = WM8804_FORMATS, 686 + }, 687 + .ops = &wm8804_dai_ops, 688 + .symmetric_rates = 1 689 + }; 690 + 691 + static struct snd_soc_codec_driver soc_codec_dev_wm8804 = { 692 + .probe = wm8804_probe, 693 + .remove = wm8804_remove, 694 + .suspend = wm8804_suspend, 695 + .resume = wm8804_resume, 696 + .set_bias_level = wm8804_set_bias_level, 697 + .reg_cache_size = ARRAY_SIZE(wm8804_reg_defs), 698 + .reg_word_size = sizeof(u8), 699 + .reg_cache_default = wm8804_reg_defs, 700 + .volatile_register = wm8804_volatile 701 + }; 702 + 703 + #if defined(CONFIG_SPI_MASTER) 704 + static int __devinit wm8804_spi_probe(struct spi_device *spi) 705 + { 706 + struct wm8804_priv *wm8804; 707 + int ret; 708 + 709 + wm8804 = kzalloc(sizeof *wm8804, GFP_KERNEL); 710 + if (IS_ERR(wm8804)) 711 + return PTR_ERR(wm8804); 712 + 713 + wm8804->control_type = SND_SOC_SPI; 714 + spi_set_drvdata(spi, wm8804); 715 + 716 + ret = snd_soc_register_codec(&spi->dev, 717 + &soc_codec_dev_wm8804, &wm8804_dai, 1); 718 + if (ret < 0) 719 + kfree(wm8804); 720 + return ret; 721 + } 722 + 723 + static int __devexit wm8804_spi_remove(struct spi_device *spi) 724 + { 725 + snd_soc_unregister_codec(&spi->dev); 726 + kfree(spi_get_drvdata(spi)); 727 + return 0; 728 + } 729 + 730 + static struct spi_driver wm8804_spi_driver = { 731 + .driver = { 732 + .name = "wm8804", 733 + .owner = THIS_MODULE, 734 + }, 735 + .probe = wm8804_spi_probe, 736 + .remove = __devexit_p(wm8804_spi_remove) 737 + }; 738 + #endif 739 + 740 + #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 741 + static __devinit int wm8804_i2c_probe(struct i2c_client *i2c, 742 + const struct i2c_device_id *id) 743 + { 744 + struct wm8804_priv *wm8804; 745 + int ret; 746 + 747 + wm8804 = kzalloc(sizeof *wm8804, GFP_KERNEL); 748 + if (IS_ERR(wm8804)) 749 + return PTR_ERR(wm8804); 750 + 751 + wm8804->control_type = SND_SOC_I2C; 752 + i2c_set_clientdata(i2c, wm8804); 753 + 754 + ret = snd_soc_register_codec(&i2c->dev, 755 + &soc_codec_dev_wm8804, &wm8804_dai, 1); 756 + if (ret < 0) 757 + kfree(wm8804); 758 + return ret; 759 + } 760 + 761 + static __devexit int wm8804_i2c_remove(struct i2c_client *client) 762 + { 763 + snd_soc_unregister_codec(&client->dev); 764 + kfree(i2c_get_clientdata(client)); 765 + return 0; 766 + } 767 + 768 + static const struct i2c_device_id wm8804_i2c_id[] = { 769 + { "wm8804", 0 }, 770 + { } 771 + }; 772 + MODULE_DEVICE_TABLE(i2c, wm8804_i2c_id); 773 + 774 + static struct i2c_driver wm8804_i2c_driver = { 775 + .driver = { 776 + .name = "wm8804", 777 + .owner = THIS_MODULE, 778 + }, 779 + .probe = wm8804_i2c_probe, 780 + .remove = __devexit_p(wm8804_i2c_remove), 781 + .id_table = wm8804_i2c_id 782 + }; 783 + #endif 784 + 785 + static int __init wm8804_modinit(void) 786 + { 787 + int ret = 0; 788 + 789 + #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 790 + ret = i2c_add_driver(&wm8804_i2c_driver); 791 + if (ret) { 792 + printk(KERN_ERR "Failed to register wm8804 I2C driver: %d\n", 793 + ret); 794 + } 795 + #endif 796 + #if defined(CONFIG_SPI_MASTER) 797 + ret = spi_register_driver(&wm8804_spi_driver); 798 + if (ret != 0) { 799 + printk(KERN_ERR "Failed to register wm8804 SPI driver: %d\n", 800 + ret); 801 + } 802 + #endif 803 + return ret; 804 + } 805 + module_init(wm8804_modinit); 806 + 807 + static void __exit wm8804_exit(void) 808 + { 809 + #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 810 + i2c_del_driver(&wm8804_i2c_driver); 811 + #endif 812 + #if defined(CONFIG_SPI_MASTER) 813 + spi_unregister_driver(&wm8804_spi_driver); 814 + #endif 815 + } 816 + module_exit(wm8804_exit); 817 + 818 + MODULE_DESCRIPTION("ASoC WM8804 driver"); 819 + MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>"); 820 + MODULE_LICENSE("GPL");
+61
sound/soc/codecs/wm8804.h
··· 1 + /* 2 + * wm8804.h -- WM8804 S/PDIF transceiver driver 3 + * 4 + * Copyright 2010 Wolfson Microelectronics plc 5 + * 6 + * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com> 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License version 2 as 10 + * published by the Free Software Foundation. 11 + */ 12 + 13 + #ifndef _WM8804_H 14 + #define _WM8804_H 15 + 16 + /* 17 + * Register values. 18 + */ 19 + #define WM8804_RST_DEVID1 0x00 20 + #define WM8804_DEVID2 0x01 21 + #define WM8804_DEVREV 0x02 22 + #define WM8804_PLL1 0x03 23 + #define WM8804_PLL2 0x04 24 + #define WM8804_PLL3 0x05 25 + #define WM8804_PLL4 0x06 26 + #define WM8804_PLL5 0x07 27 + #define WM8804_PLL6 0x08 28 + #define WM8804_SPDMODE 0x09 29 + #define WM8804_INTMASK 0x0A 30 + #define WM8804_INTSTAT 0x0B 31 + #define WM8804_SPDSTAT 0x0C 32 + #define WM8804_RXCHAN1 0x0D 33 + #define WM8804_RXCHAN2 0x0E 34 + #define WM8804_RXCHAN3 0x0F 35 + #define WM8804_RXCHAN4 0x10 36 + #define WM8804_RXCHAN5 0x11 37 + #define WM8804_SPDTX1 0x12 38 + #define WM8804_SPDTX2 0x13 39 + #define WM8804_SPDTX3 0x14 40 + #define WM8804_SPDTX4 0x15 41 + #define WM8804_SPDTX5 0x16 42 + #define WM8804_GPO0 0x17 43 + #define WM8804_GPO1 0x18 44 + #define WM8804_GPO2 0x1A 45 + #define WM8804_AIFTX 0x1B 46 + #define WM8804_AIFRX 0x1C 47 + #define WM8804_SPDRX1 0x1D 48 + #define WM8804_PWRDN 0x1E 49 + 50 + #define WM8804_REGISTER_COUNT 30 51 + #define WM8804_MAX_REGISTER 0x1E 52 + 53 + #define WM8804_TX_CLKSRC_MCLK 1 54 + #define WM8804_TX_CLKSRC_PLL 2 55 + 56 + #define WM8804_CLKOUT_SRC_CLK1 3 57 + #define WM8804_CLKOUT_SRC_OSCCLK 4 58 + 59 + #define WM8804_CLKOUT_DIV 1 60 + 61 + #endif /* _WM8804_H */