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 <linux/of_device.h> 23 #include <sound/core.h> 24 #include <sound/pcm.h> 25 #include <sound/pcm_params.h> 26 #include <sound/soc.h> 27 #include <sound/initval.h> 28 #include <sound/tlv.h> 29 30 #include "wm8728.h" 31 32 /* 33 * We can't read the WM8728 register space so we cache them instead. 34 * Note that the defaults here aren't the physical defaults, we latch 35 * the volume update bits, mute the output and enable infinite zero 36 * detect. 37 */ 38 static const u16 wm8728_reg_defaults[] = { 39 0x1ff, 40 0x1ff, 41 0x001, 42 0x100, 43 }; 44 45 /* codec private data */ 46 struct wm8728_priv { 47 enum snd_soc_control_type control_type; 48 }; 49 50 static const DECLARE_TLV_DB_SCALE(wm8728_tlv, -12750, 50, 1); 51 52 static const struct snd_kcontrol_new wm8728_snd_controls[] = { 53 54 SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8728_DACLVOL, WM8728_DACRVOL, 55 0, 255, 0, wm8728_tlv), 56 57 SOC_SINGLE("Deemphasis", WM8728_DACCTL, 1, 1, 0), 58 }; 59 60 /* 61 * DAPM controls. 62 */ 63 static const struct snd_soc_dapm_widget wm8728_dapm_widgets[] = { 64 SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SND_SOC_NOPM, 0, 0), 65 SND_SOC_DAPM_OUTPUT("VOUTL"), 66 SND_SOC_DAPM_OUTPUT("VOUTR"), 67 }; 68 69 static const struct snd_soc_dapm_route wm8728_intercon[] = { 70 {"VOUTL", NULL, "DAC"}, 71 {"VOUTR", NULL, "DAC"}, 72 }; 73 74 static int wm8728_mute(struct snd_soc_dai *dai, int mute) 75 { 76 struct snd_soc_codec *codec = dai->codec; 77 u16 mute_reg = snd_soc_read(codec, WM8728_DACCTL); 78 79 if (mute) 80 snd_soc_write(codec, WM8728_DACCTL, mute_reg | 1); 81 else 82 snd_soc_write(codec, WM8728_DACCTL, mute_reg & ~1); 83 84 return 0; 85 } 86 87 static int wm8728_hw_params(struct snd_pcm_substream *substream, 88 struct snd_pcm_hw_params *params, 89 struct snd_soc_dai *dai) 90 { 91 struct snd_soc_codec *codec = dai->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 const 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) 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 return ret; 246 } 247 248 static int wm8728_remove(struct snd_soc_codec *codec) 249 { 250 wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF); 251 return 0; 252 } 253 254 static struct snd_soc_codec_driver soc_codec_dev_wm8728 = { 255 .probe = wm8728_probe, 256 .remove = wm8728_remove, 257 .suspend = wm8728_suspend, 258 .resume = wm8728_resume, 259 .set_bias_level = wm8728_set_bias_level, 260 .reg_cache_size = ARRAY_SIZE(wm8728_reg_defaults), 261 .reg_word_size = sizeof(u16), 262 .reg_cache_default = wm8728_reg_defaults, 263 .controls = wm8728_snd_controls, 264 .num_controls = ARRAY_SIZE(wm8728_snd_controls), 265 .dapm_widgets = wm8728_dapm_widgets, 266 .num_dapm_widgets = ARRAY_SIZE(wm8728_dapm_widgets), 267 .dapm_routes = wm8728_intercon, 268 .num_dapm_routes = ARRAY_SIZE(wm8728_intercon), 269 }; 270 271 static const struct of_device_id wm8728_of_match[] = { 272 { .compatible = "wlf,wm8728", }, 273 { } 274 }; 275 MODULE_DEVICE_TABLE(of, wm8728_of_match); 276 277 #if defined(CONFIG_SPI_MASTER) 278 static int __devinit wm8728_spi_probe(struct spi_device *spi) 279 { 280 struct wm8728_priv *wm8728; 281 int ret; 282 283 wm8728 = kzalloc(sizeof(struct wm8728_priv), GFP_KERNEL); 284 if (wm8728 == NULL) 285 return -ENOMEM; 286 287 wm8728->control_type = SND_SOC_SPI; 288 spi_set_drvdata(spi, wm8728); 289 290 ret = snd_soc_register_codec(&spi->dev, 291 &soc_codec_dev_wm8728, &wm8728_dai, 1); 292 if (ret < 0) 293 kfree(wm8728); 294 return ret; 295 } 296 297 static int __devexit wm8728_spi_remove(struct spi_device *spi) 298 { 299 snd_soc_unregister_codec(&spi->dev); 300 kfree(spi_get_drvdata(spi)); 301 return 0; 302 } 303 304 static struct spi_driver wm8728_spi_driver = { 305 .driver = { 306 .name = "wm8728", 307 .owner = THIS_MODULE, 308 .of_match_table = wm8728_of_match, 309 }, 310 .probe = wm8728_spi_probe, 311 .remove = __devexit_p(wm8728_spi_remove), 312 }; 313 #endif /* CONFIG_SPI_MASTER */ 314 315 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 316 static __devinit int wm8728_i2c_probe(struct i2c_client *i2c, 317 const struct i2c_device_id *id) 318 { 319 struct wm8728_priv *wm8728; 320 int ret; 321 322 wm8728 = kzalloc(sizeof(struct wm8728_priv), GFP_KERNEL); 323 if (wm8728 == NULL) 324 return -ENOMEM; 325 326 i2c_set_clientdata(i2c, wm8728); 327 wm8728->control_type = SND_SOC_I2C; 328 329 ret = snd_soc_register_codec(&i2c->dev, 330 &soc_codec_dev_wm8728, &wm8728_dai, 1); 331 if (ret < 0) 332 kfree(wm8728); 333 return ret; 334 } 335 336 static __devexit int wm8728_i2c_remove(struct i2c_client *client) 337 { 338 snd_soc_unregister_codec(&client->dev); 339 kfree(i2c_get_clientdata(client)); 340 return 0; 341 } 342 343 static const struct i2c_device_id wm8728_i2c_id[] = { 344 { "wm8728", 0 }, 345 { } 346 }; 347 MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id); 348 349 static struct i2c_driver wm8728_i2c_driver = { 350 .driver = { 351 .name = "wm8728", 352 .owner = THIS_MODULE, 353 .of_match_table = wm8728_of_match, 354 }, 355 .probe = wm8728_i2c_probe, 356 .remove = __devexit_p(wm8728_i2c_remove), 357 .id_table = wm8728_i2c_id, 358 }; 359 #endif 360 361 static int __init wm8728_modinit(void) 362 { 363 int ret = 0; 364 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 365 ret = i2c_add_driver(&wm8728_i2c_driver); 366 if (ret != 0) { 367 printk(KERN_ERR "Failed to register wm8728 I2C driver: %d\n", 368 ret); 369 } 370 #endif 371 #if defined(CONFIG_SPI_MASTER) 372 ret = spi_register_driver(&wm8728_spi_driver); 373 if (ret != 0) { 374 printk(KERN_ERR "Failed to register wm8728 SPI driver: %d\n", 375 ret); 376 } 377 #endif 378 return ret; 379 } 380 module_init(wm8728_modinit); 381 382 static void __exit wm8728_exit(void) 383 { 384 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 385 i2c_del_driver(&wm8728_i2c_driver); 386 #endif 387 #if defined(CONFIG_SPI_MASTER) 388 spi_unregister_driver(&wm8728_spi_driver); 389 #endif 390 } 391 module_exit(wm8728_exit); 392 393 MODULE_DESCRIPTION("ASoC WM8728 driver"); 394 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 395 MODULE_LICENSE("GPL"); 396