1 /* 2 * wm8728.c -- WM8728 ALSA SoC Audio driver 3 * 4 * Copyright 2008 Wolfson Microelectronics plc 5 * 6 * Author: Mark Brown <broonie@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/platform_device.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 "wm8728.h" 30 31 /* 32 * We can't read the WM8728 register space so we cache them instead. 33 * Note that the defaults here aren't the physical defaults, we latch 34 * the volume update bits, mute the output and enable infinite zero 35 * detect. 36 */ 37 static const u16 wm8728_reg_defaults[] = { 38 0x1ff, 39 0x1ff, 40 0x001, 41 0x100, 42 }; 43 44 /* codec private data */ 45 struct wm8728_priv { 46 enum snd_soc_control_type control_type; 47 }; 48 49 static const DECLARE_TLV_DB_SCALE(wm8728_tlv, -12750, 50, 1); 50 51 static const struct snd_kcontrol_new wm8728_snd_controls[] = { 52 53 SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8728_DACLVOL, WM8728_DACRVOL, 54 0, 255, 0, wm8728_tlv), 55 56 SOC_SINGLE("Deemphasis", WM8728_DACCTL, 1, 1, 0), 57 }; 58 59 /* 60 * DAPM controls. 61 */ 62 static const struct snd_soc_dapm_widget wm8728_dapm_widgets[] = { 63 SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SND_SOC_NOPM, 0, 0), 64 SND_SOC_DAPM_OUTPUT("VOUTL"), 65 SND_SOC_DAPM_OUTPUT("VOUTR"), 66 }; 67 68 static const struct snd_soc_dapm_route intercon[] = { 69 {"VOUTL", NULL, "DAC"}, 70 {"VOUTR", NULL, "DAC"}, 71 }; 72 73 static int wm8728_add_widgets(struct snd_soc_codec *codec) 74 { 75 struct snd_soc_dapm_context *dapm = &codec->dapm; 76 77 snd_soc_dapm_new_controls(dapm, wm8728_dapm_widgets, 78 ARRAY_SIZE(wm8728_dapm_widgets)); 79 snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); 80 81 return 0; 82 } 83 84 static int wm8728_mute(struct snd_soc_dai *dai, int mute) 85 { 86 struct snd_soc_codec *codec = dai->codec; 87 u16 mute_reg = snd_soc_read(codec, WM8728_DACCTL); 88 89 if (mute) 90 snd_soc_write(codec, WM8728_DACCTL, mute_reg | 1); 91 else 92 snd_soc_write(codec, WM8728_DACCTL, mute_reg & ~1); 93 94 return 0; 95 } 96 97 static int wm8728_hw_params(struct snd_pcm_substream *substream, 98 struct snd_pcm_hw_params *params, 99 struct snd_soc_dai *dai) 100 { 101 struct snd_soc_pcm_runtime *rtd = substream->private_data; 102 struct snd_soc_codec *codec = rtd->codec; 103 u16 dac = snd_soc_read(codec, WM8728_DACCTL); 104 105 dac &= ~0x18; 106 107 switch (params_format(params)) { 108 case SNDRV_PCM_FORMAT_S16_LE: 109 break; 110 case SNDRV_PCM_FORMAT_S20_3LE: 111 dac |= 0x10; 112 break; 113 case SNDRV_PCM_FORMAT_S24_LE: 114 dac |= 0x08; 115 break; 116 default: 117 return -EINVAL; 118 } 119 120 snd_soc_write(codec, WM8728_DACCTL, dac); 121 122 return 0; 123 } 124 125 static int wm8728_set_dai_fmt(struct snd_soc_dai *codec_dai, 126 unsigned int fmt) 127 { 128 struct snd_soc_codec *codec = codec_dai->codec; 129 u16 iface = snd_soc_read(codec, WM8728_IFCTL); 130 131 /* Currently only I2S is supported by the driver, though the 132 * hardware is more flexible. 133 */ 134 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 135 case SND_SOC_DAIFMT_I2S: 136 iface |= 1; 137 break; 138 default: 139 return -EINVAL; 140 } 141 142 /* The hardware only support full slave mode */ 143 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 144 case SND_SOC_DAIFMT_CBS_CFS: 145 break; 146 default: 147 return -EINVAL; 148 } 149 150 switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 151 case SND_SOC_DAIFMT_NB_NF: 152 iface &= ~0x22; 153 break; 154 case SND_SOC_DAIFMT_IB_NF: 155 iface |= 0x20; 156 iface &= ~0x02; 157 break; 158 case SND_SOC_DAIFMT_NB_IF: 159 iface |= 0x02; 160 iface &= ~0x20; 161 break; 162 case SND_SOC_DAIFMT_IB_IF: 163 iface |= 0x22; 164 break; 165 default: 166 return -EINVAL; 167 } 168 169 snd_soc_write(codec, WM8728_IFCTL, iface); 170 return 0; 171 } 172 173 static int wm8728_set_bias_level(struct snd_soc_codec *codec, 174 enum snd_soc_bias_level level) 175 { 176 u16 reg; 177 int i; 178 179 switch (level) { 180 case SND_SOC_BIAS_ON: 181 case SND_SOC_BIAS_PREPARE: 182 case SND_SOC_BIAS_STANDBY: 183 if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { 184 /* Power everything up... */ 185 reg = snd_soc_read(codec, WM8728_DACCTL); 186 snd_soc_write(codec, WM8728_DACCTL, reg & ~0x4); 187 188 /* ..then sync in the register cache. */ 189 for (i = 0; i < ARRAY_SIZE(wm8728_reg_defaults); i++) 190 snd_soc_write(codec, i, 191 snd_soc_read(codec, i)); 192 } 193 break; 194 195 case SND_SOC_BIAS_OFF: 196 reg = snd_soc_read(codec, WM8728_DACCTL); 197 snd_soc_write(codec, WM8728_DACCTL, reg | 0x4); 198 break; 199 } 200 codec->dapm.bias_level = level; 201 return 0; 202 } 203 204 #define WM8728_RATES (SNDRV_PCM_RATE_8000_192000) 205 206 #define WM8728_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ 207 SNDRV_PCM_FMTBIT_S24_LE) 208 209 static struct snd_soc_dai_ops wm8728_dai_ops = { 210 .hw_params = wm8728_hw_params, 211 .digital_mute = wm8728_mute, 212 .set_fmt = wm8728_set_dai_fmt, 213 }; 214 215 static struct snd_soc_dai_driver wm8728_dai = { 216 .name = "wm8728-hifi", 217 .playback = { 218 .stream_name = "Playback", 219 .channels_min = 2, 220 .channels_max = 2, 221 .rates = WM8728_RATES, 222 .formats = WM8728_FORMATS, 223 }, 224 .ops = &wm8728_dai_ops, 225 }; 226 227 static int wm8728_suspend(struct snd_soc_codec *codec, pm_message_t state) 228 { 229 wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF); 230 231 return 0; 232 } 233 234 static int wm8728_resume(struct snd_soc_codec *codec) 235 { 236 wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 237 238 return 0; 239 } 240 241 static int wm8728_probe(struct snd_soc_codec *codec) 242 { 243 struct wm8728_priv *wm8728 = snd_soc_codec_get_drvdata(codec); 244 int ret; 245 246 ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8728->control_type); 247 if (ret < 0) { 248 printk(KERN_ERR "wm8728: failed to configure cache I/O: %d\n", 249 ret); 250 return ret; 251 } 252 253 /* power on device */ 254 wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 255 256 snd_soc_add_controls(codec, wm8728_snd_controls, 257 ARRAY_SIZE(wm8728_snd_controls)); 258 wm8728_add_widgets(codec); 259 260 return ret; 261 } 262 263 static int wm8728_remove(struct snd_soc_codec *codec) 264 { 265 wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF); 266 return 0; 267 } 268 269 static struct snd_soc_codec_driver soc_codec_dev_wm8728 = { 270 .probe = wm8728_probe, 271 .remove = wm8728_remove, 272 .suspend = wm8728_suspend, 273 .resume = wm8728_resume, 274 .set_bias_level = wm8728_set_bias_level, 275 .reg_cache_size = ARRAY_SIZE(wm8728_reg_defaults), 276 .reg_word_size = sizeof(u16), 277 .reg_cache_default = wm8728_reg_defaults, 278 }; 279 280 #if defined(CONFIG_SPI_MASTER) 281 static int __devinit wm8728_spi_probe(struct spi_device *spi) 282 { 283 struct wm8728_priv *wm8728; 284 int ret; 285 286 wm8728 = kzalloc(sizeof(struct wm8728_priv), GFP_KERNEL); 287 if (wm8728 == NULL) 288 return -ENOMEM; 289 290 wm8728->control_type = SND_SOC_SPI; 291 spi_set_drvdata(spi, wm8728); 292 293 ret = snd_soc_register_codec(&spi->dev, 294 &soc_codec_dev_wm8728, &wm8728_dai, 1); 295 if (ret < 0) 296 kfree(wm8728); 297 return ret; 298 } 299 300 static int __devexit wm8728_spi_remove(struct spi_device *spi) 301 { 302 snd_soc_unregister_codec(&spi->dev); 303 kfree(spi_get_drvdata(spi)); 304 return 0; 305 } 306 307 static struct spi_driver wm8728_spi_driver = { 308 .driver = { 309 .name = "wm8728-codec", 310 .owner = THIS_MODULE, 311 }, 312 .probe = wm8728_spi_probe, 313 .remove = __devexit_p(wm8728_spi_remove), 314 }; 315 #endif /* CONFIG_SPI_MASTER */ 316 317 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 318 static __devinit int wm8728_i2c_probe(struct i2c_client *i2c, 319 const struct i2c_device_id *id) 320 { 321 struct wm8728_priv *wm8728; 322 int ret; 323 324 wm8728 = kzalloc(sizeof(struct wm8728_priv), GFP_KERNEL); 325 if (wm8728 == NULL) 326 return -ENOMEM; 327 328 i2c_set_clientdata(i2c, wm8728); 329 wm8728->control_type = SND_SOC_I2C; 330 331 ret = snd_soc_register_codec(&i2c->dev, 332 &soc_codec_dev_wm8728, &wm8728_dai, 1); 333 if (ret < 0) 334 kfree(wm8728); 335 return ret; 336 } 337 338 static __devexit int wm8728_i2c_remove(struct i2c_client *client) 339 { 340 snd_soc_unregister_codec(&client->dev); 341 kfree(i2c_get_clientdata(client)); 342 return 0; 343 } 344 345 static const struct i2c_device_id wm8728_i2c_id[] = { 346 { "wm8728", 0 }, 347 { } 348 }; 349 MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id); 350 351 static struct i2c_driver wm8728_i2c_driver = { 352 .driver = { 353 .name = "wm8728-codec", 354 .owner = THIS_MODULE, 355 }, 356 .probe = wm8728_i2c_probe, 357 .remove = __devexit_p(wm8728_i2c_remove), 358 .id_table = wm8728_i2c_id, 359 }; 360 #endif 361 362 static int __init wm8728_modinit(void) 363 { 364 int ret = 0; 365 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 366 ret = i2c_add_driver(&wm8728_i2c_driver); 367 if (ret != 0) { 368 printk(KERN_ERR "Failed to register wm8728 I2C driver: %d\n", 369 ret); 370 } 371 #endif 372 #if defined(CONFIG_SPI_MASTER) 373 ret = spi_register_driver(&wm8728_spi_driver); 374 if (ret != 0) { 375 printk(KERN_ERR "Failed to register wm8728 SPI driver: %d\n", 376 ret); 377 } 378 #endif 379 return ret; 380 } 381 module_init(wm8728_modinit); 382 383 static void __exit wm8728_exit(void) 384 { 385 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 386 i2c_del_driver(&wm8728_i2c_driver); 387 #endif 388 #if defined(CONFIG_SPI_MASTER) 389 spi_unregister_driver(&wm8728_spi_driver); 390 #endif 391 } 392 module_exit(wm8728_exit); 393 394 MODULE_DESCRIPTION("ASoC WM8728 driver"); 395 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 396 MODULE_LICENSE("GPL"); 397