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