• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <sound/core.h>
22 #include <sound/pcm.h>
23 #include <sound/pcm_params.h>
24 #include <sound/soc.h>
25 #include <sound/soc-dapm.h>
26 #include <sound/initval.h>
27 #include <sound/tlv.h>
28 
29 #include "wm8728.h"
30 
31 struct snd_soc_codec_device soc_codec_dev_wm8728;
32 
33 /*
34  * We can't read the WM8728 register space so we cache them instead.
35  * Note that the defaults here aren't the physical defaults, we latch
36  * the volume update bits, mute the output and enable infinite zero
37  * detect.
38  */
39 static const u16 wm8728_reg_defaults[] = {
40 	0x1ff,
41 	0x1ff,
42 	0x001,
43 	0x100,
44 };
45 
wm8728_read_reg_cache(struct snd_soc_codec * codec,unsigned int reg)46 static inline unsigned int wm8728_read_reg_cache(struct snd_soc_codec *codec,
47 	unsigned int reg)
48 {
49 	u16 *cache = codec->reg_cache;
50 	BUG_ON(reg > ARRAY_SIZE(wm8728_reg_defaults));
51 	return cache[reg];
52 }
53 
wm8728_write_reg_cache(struct snd_soc_codec * codec,u16 reg,unsigned int value)54 static inline void wm8728_write_reg_cache(struct snd_soc_codec *codec,
55 	u16 reg, unsigned int value)
56 {
57 	u16 *cache = codec->reg_cache;
58 	BUG_ON(reg > ARRAY_SIZE(wm8728_reg_defaults));
59 	cache[reg] = value;
60 }
61 
62 /*
63  * write to the WM8728 register space
64  */
wm8728_write(struct snd_soc_codec * codec,unsigned int reg,unsigned int value)65 static int wm8728_write(struct snd_soc_codec *codec, unsigned int reg,
66 	unsigned int value)
67 {
68 	u8 data[2];
69 
70 	/* data is
71 	 *   D15..D9 WM8728 register offset
72 	 *   D8...D0 register data
73 	 */
74 	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
75 	data[1] = value & 0x00ff;
76 
77 	wm8728_write_reg_cache(codec, reg, value);
78 
79 	if (codec->hw_write(codec->control_data, data, 2) == 2)
80 		return 0;
81 	else
82 		return -EIO;
83 }
84 
85 static const DECLARE_TLV_DB_SCALE(wm8728_tlv, -12750, 50, 1);
86 
87 static const struct snd_kcontrol_new wm8728_snd_controls[] = {
88 
89 SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8728_DACLVOL, WM8728_DACRVOL,
90 		 0, 255, 0, wm8728_tlv),
91 
92 SOC_SINGLE("Deemphasis", WM8728_DACCTL, 1, 1, 0),
93 };
94 
wm8728_add_controls(struct snd_soc_codec * codec)95 static int wm8728_add_controls(struct snd_soc_codec *codec)
96 {
97 	int err, i;
98 
99 	for (i = 0; i < ARRAY_SIZE(wm8728_snd_controls); i++) {
100 		err = snd_ctl_add(codec->card,
101 				  snd_soc_cnew(&wm8728_snd_controls[i],
102 						codec, NULL));
103 		if (err < 0)
104 			return err;
105 	}
106 
107 	return 0;
108 }
109 
110 /*
111  * DAPM controls.
112  */
113 static const struct snd_soc_dapm_widget wm8728_dapm_widgets[] = {
114 SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SND_SOC_NOPM, 0, 0),
115 SND_SOC_DAPM_OUTPUT("VOUTL"),
116 SND_SOC_DAPM_OUTPUT("VOUTR"),
117 };
118 
119 static const struct snd_soc_dapm_route intercon[] = {
120 	{"VOUTL", NULL, "DAC"},
121 	{"VOUTR", NULL, "DAC"},
122 };
123 
wm8728_add_widgets(struct snd_soc_codec * codec)124 static int wm8728_add_widgets(struct snd_soc_codec *codec)
125 {
126 	snd_soc_dapm_new_controls(codec, wm8728_dapm_widgets,
127 				  ARRAY_SIZE(wm8728_dapm_widgets));
128 
129 	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
130 
131 	snd_soc_dapm_new_widgets(codec);
132 
133 	return 0;
134 }
135 
wm8728_mute(struct snd_soc_dai * dai,int mute)136 static int wm8728_mute(struct snd_soc_dai *dai, int mute)
137 {
138 	struct snd_soc_codec *codec = dai->codec;
139 	u16 mute_reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
140 
141 	if (mute)
142 		wm8728_write(codec, WM8728_DACCTL, mute_reg | 1);
143 	else
144 		wm8728_write(codec, WM8728_DACCTL, mute_reg & ~1);
145 
146 	return 0;
147 }
148 
wm8728_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)149 static int wm8728_hw_params(struct snd_pcm_substream *substream,
150 	struct snd_pcm_hw_params *params,
151 	struct snd_soc_dai *dai)
152 {
153 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
154 	struct snd_soc_device *socdev = rtd->socdev;
155 	struct snd_soc_codec *codec = socdev->codec;
156 	u16 dac = wm8728_read_reg_cache(codec, WM8728_DACCTL);
157 
158 	dac &= ~0x18;
159 
160 	switch (params_format(params)) {
161 	case SNDRV_PCM_FORMAT_S16_LE:
162 		break;
163 	case SNDRV_PCM_FORMAT_S20_3LE:
164 		dac |= 0x10;
165 		break;
166 	case SNDRV_PCM_FORMAT_S24_LE:
167 		dac |= 0x08;
168 		break;
169 	default:
170 		return -EINVAL;
171 	}
172 
173 	wm8728_write(codec, WM8728_DACCTL, dac);
174 
175 	return 0;
176 }
177 
wm8728_set_dai_fmt(struct snd_soc_dai * codec_dai,unsigned int fmt)178 static int wm8728_set_dai_fmt(struct snd_soc_dai *codec_dai,
179 		unsigned int fmt)
180 {
181 	struct snd_soc_codec *codec = codec_dai->codec;
182 	u16 iface = wm8728_read_reg_cache(codec, WM8728_IFCTL);
183 
184 	/* Currently only I2S is supported by the driver, though the
185 	 * hardware is more flexible.
186 	 */
187 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
188 	case SND_SOC_DAIFMT_I2S:
189 		iface |= 1;
190 		break;
191 	default:
192 		return -EINVAL;
193 	}
194 
195 	/* The hardware only support full slave mode */
196 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
197 	case SND_SOC_DAIFMT_CBS_CFS:
198 		break;
199 	default:
200 		return -EINVAL;
201 	}
202 
203 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
204 	case SND_SOC_DAIFMT_NB_NF:
205 		iface &= ~0x22;
206 		break;
207 	case SND_SOC_DAIFMT_IB_NF:
208 		iface |=  0x20;
209 		iface &= ~0x02;
210 		break;
211 	case SND_SOC_DAIFMT_NB_IF:
212 		iface |= 0x02;
213 		iface &= ~0x20;
214 		break;
215 	case SND_SOC_DAIFMT_IB_IF:
216 		iface |= 0x22;
217 		break;
218 	default:
219 		return -EINVAL;
220 	}
221 
222 	wm8728_write(codec, WM8728_IFCTL, iface);
223 	return 0;
224 }
225 
wm8728_set_bias_level(struct snd_soc_codec * codec,enum snd_soc_bias_level level)226 static int wm8728_set_bias_level(struct snd_soc_codec *codec,
227 				 enum snd_soc_bias_level level)
228 {
229 	u16 reg;
230 	int i;
231 
232 	switch (level) {
233 	case SND_SOC_BIAS_ON:
234 	case SND_SOC_BIAS_PREPARE:
235 	case SND_SOC_BIAS_STANDBY:
236 		if (codec->bias_level == SND_SOC_BIAS_OFF) {
237 			/* Power everything up... */
238 			reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
239 			wm8728_write(codec, WM8728_DACCTL, reg & ~0x4);
240 
241 			/* ..then sync in the register cache. */
242 			for (i = 0; i < ARRAY_SIZE(wm8728_reg_defaults); i++)
243 				wm8728_write(codec, i,
244 					     wm8728_read_reg_cache(codec, i));
245 		}
246 		break;
247 
248 	case SND_SOC_BIAS_OFF:
249 		reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
250 		wm8728_write(codec, WM8728_DACCTL, reg | 0x4);
251 		break;
252 	}
253 	codec->bias_level = level;
254 	return 0;
255 }
256 
257 #define WM8728_RATES (SNDRV_PCM_RATE_8000_192000)
258 
259 #define WM8728_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
260 	SNDRV_PCM_FMTBIT_S24_LE)
261 
262 struct snd_soc_dai wm8728_dai = {
263 	.name = "WM8728",
264 	.playback = {
265 		.stream_name = "Playback",
266 		.channels_min = 2,
267 		.channels_max = 2,
268 		.rates = WM8728_RATES,
269 		.formats = WM8728_FORMATS,
270 	},
271 	.ops = {
272 		 .hw_params = wm8728_hw_params,
273 		 .digital_mute = wm8728_mute,
274 		 .set_fmt = wm8728_set_dai_fmt,
275 	}
276 };
277 EXPORT_SYMBOL_GPL(wm8728_dai);
278 
wm8728_suspend(struct platform_device * pdev,pm_message_t state)279 static int wm8728_suspend(struct platform_device *pdev, pm_message_t state)
280 {
281 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
282 	struct snd_soc_codec *codec = socdev->codec;
283 
284 	wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
285 
286 	return 0;
287 }
288 
wm8728_resume(struct platform_device * pdev)289 static int wm8728_resume(struct platform_device *pdev)
290 {
291 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
292 	struct snd_soc_codec *codec = socdev->codec;
293 
294 	wm8728_set_bias_level(codec, codec->suspend_bias_level);
295 
296 	return 0;
297 }
298 
299 /*
300  * initialise the WM8728 driver
301  * register the mixer and dsp interfaces with the kernel
302  */
wm8728_init(struct snd_soc_device * socdev)303 static int wm8728_init(struct snd_soc_device *socdev)
304 {
305 	struct snd_soc_codec *codec = socdev->codec;
306 	int ret = 0;
307 
308 	codec->name = "WM8728";
309 	codec->owner = THIS_MODULE;
310 	codec->read = wm8728_read_reg_cache;
311 	codec->write = wm8728_write;
312 	codec->set_bias_level = wm8728_set_bias_level;
313 	codec->dai = &wm8728_dai;
314 	codec->num_dai = 1;
315 	codec->bias_level = SND_SOC_BIAS_OFF;
316 	codec->reg_cache_size = ARRAY_SIZE(wm8728_reg_defaults);
317 	codec->reg_cache = kmemdup(wm8728_reg_defaults,
318 				   sizeof(wm8728_reg_defaults),
319 				   GFP_KERNEL);
320 	if (codec->reg_cache == NULL)
321 		return -ENOMEM;
322 
323 	/* register pcms */
324 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
325 	if (ret < 0) {
326 		printk(KERN_ERR "wm8728: failed to create pcms\n");
327 		goto pcm_err;
328 	}
329 
330 	/* power on device */
331 	wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
332 
333 	wm8728_add_controls(codec);
334 	wm8728_add_widgets(codec);
335 	ret = snd_soc_init_card(socdev);
336 	if (ret < 0) {
337 		printk(KERN_ERR "wm8728: failed to register card\n");
338 		goto card_err;
339 	}
340 
341 	return ret;
342 
343 card_err:
344 	snd_soc_free_pcms(socdev);
345 	snd_soc_dapm_free(socdev);
346 pcm_err:
347 	kfree(codec->reg_cache);
348 	return ret;
349 }
350 
351 static struct snd_soc_device *wm8728_socdev;
352 
353 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
354 
355 /*
356  * WM8728 2 wire address is determined by GPIO5
357  * state during powerup.
358  *    low  = 0x1a
359  *    high = 0x1b
360  */
361 
wm8728_i2c_probe(struct i2c_client * i2c,const struct i2c_device_id * id)362 static int wm8728_i2c_probe(struct i2c_client *i2c,
363 			    const struct i2c_device_id *id)
364 {
365 	struct snd_soc_device *socdev = wm8728_socdev;
366 	struct snd_soc_codec *codec = socdev->codec;
367 	int ret;
368 
369 	i2c_set_clientdata(i2c, codec);
370 	codec->control_data = i2c;
371 
372 	ret = wm8728_init(socdev);
373 	if (ret < 0)
374 		pr_err("failed to initialise WM8728\n");
375 
376 	return ret;
377 }
378 
wm8728_i2c_remove(struct i2c_client * client)379 static int wm8728_i2c_remove(struct i2c_client *client)
380 {
381 	struct snd_soc_codec *codec = i2c_get_clientdata(client);
382 	kfree(codec->reg_cache);
383 	return 0;
384 }
385 
386 static const struct i2c_device_id wm8728_i2c_id[] = {
387 	{ "wm8728", 0 },
388 	{ }
389 };
390 MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id);
391 
392 static struct i2c_driver wm8728_i2c_driver = {
393 	.driver = {
394 		.name = "WM8728 I2C Codec",
395 		.owner = THIS_MODULE,
396 	},
397 	.probe =    wm8728_i2c_probe,
398 	.remove =   wm8728_i2c_remove,
399 	.id_table = wm8728_i2c_id,
400 };
401 
wm8728_add_i2c_device(struct platform_device * pdev,const struct wm8728_setup_data * setup)402 static int wm8728_add_i2c_device(struct platform_device *pdev,
403 				 const struct wm8728_setup_data *setup)
404 {
405 	struct i2c_board_info info;
406 	struct i2c_adapter *adapter;
407 	struct i2c_client *client;
408 	int ret;
409 
410 	ret = i2c_add_driver(&wm8728_i2c_driver);
411 	if (ret != 0) {
412 		dev_err(&pdev->dev, "can't add i2c driver\n");
413 		return ret;
414 	}
415 
416 	memset(&info, 0, sizeof(struct i2c_board_info));
417 	info.addr = setup->i2c_address;
418 	strlcpy(info.type, "wm8728", I2C_NAME_SIZE);
419 
420 	adapter = i2c_get_adapter(setup->i2c_bus);
421 	if (!adapter) {
422 		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
423 			setup->i2c_bus);
424 		goto err_driver;
425 	}
426 
427 	client = i2c_new_device(adapter, &info);
428 	i2c_put_adapter(adapter);
429 	if (!client) {
430 		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
431 			(unsigned int)info.addr);
432 		goto err_driver;
433 	}
434 
435 	return 0;
436 
437 err_driver:
438 	i2c_del_driver(&wm8728_i2c_driver);
439 	return -ENODEV;
440 }
441 #endif
442 
443 #if defined(CONFIG_SPI_MASTER)
wm8728_spi_probe(struct spi_device * spi)444 static int __devinit wm8728_spi_probe(struct spi_device *spi)
445 {
446 	struct snd_soc_device *socdev = wm8728_socdev;
447 	struct snd_soc_codec *codec = socdev->codec;
448 	int ret;
449 
450 	codec->control_data = spi;
451 
452 	ret = wm8728_init(socdev);
453 	if (ret < 0)
454 		dev_err(&spi->dev, "failed to initialise WM8728\n");
455 
456 	return ret;
457 }
458 
wm8728_spi_remove(struct spi_device * spi)459 static int __devexit wm8728_spi_remove(struct spi_device *spi)
460 {
461 	return 0;
462 }
463 
464 static struct spi_driver wm8728_spi_driver = {
465 	.driver = {
466 		.name	= "wm8728",
467 		.bus	= &spi_bus_type,
468 		.owner	= THIS_MODULE,
469 	},
470 	.probe		= wm8728_spi_probe,
471 	.remove		= __devexit_p(wm8728_spi_remove),
472 };
473 
wm8728_spi_write(struct spi_device * spi,const char * data,int len)474 static int wm8728_spi_write(struct spi_device *spi, const char *data, int len)
475 {
476 	struct spi_transfer t;
477 	struct spi_message m;
478 	u8 msg[2];
479 
480 	if (len <= 0)
481 		return 0;
482 
483 	msg[0] = data[0];
484 	msg[1] = data[1];
485 
486 	spi_message_init(&m);
487 	memset(&t, 0, (sizeof t));
488 
489 	t.tx_buf = &msg[0];
490 	t.len = len;
491 
492 	spi_message_add_tail(&t, &m);
493 	spi_sync(spi, &m);
494 
495 	return len;
496 }
497 #endif /* CONFIG_SPI_MASTER */
498 
wm8728_probe(struct platform_device * pdev)499 static int wm8728_probe(struct platform_device *pdev)
500 {
501 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
502 	struct wm8728_setup_data *setup;
503 	struct snd_soc_codec *codec;
504 	int ret = 0;
505 
506 	setup = socdev->codec_data;
507 	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
508 	if (codec == NULL)
509 		return -ENOMEM;
510 
511 	socdev->codec = codec;
512 	mutex_init(&codec->mutex);
513 	INIT_LIST_HEAD(&codec->dapm_widgets);
514 	INIT_LIST_HEAD(&codec->dapm_paths);
515 
516 	wm8728_socdev = socdev;
517 	ret = -ENODEV;
518 
519 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
520 	if (setup->i2c_address) {
521 		codec->hw_write = (hw_write_t)i2c_master_send;
522 		ret = wm8728_add_i2c_device(pdev, setup);
523 	}
524 #endif
525 #if defined(CONFIG_SPI_MASTER)
526 	if (setup->spi) {
527 		codec->hw_write = (hw_write_t)wm8728_spi_write;
528 		ret = spi_register_driver(&wm8728_spi_driver);
529 		if (ret != 0)
530 			printk(KERN_ERR "can't add spi driver");
531 	}
532 #endif
533 
534 	if (ret != 0)
535 		kfree(codec);
536 
537 	return ret;
538 }
539 
540 /* power down chip */
wm8728_remove(struct platform_device * pdev)541 static int wm8728_remove(struct platform_device *pdev)
542 {
543 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
544 	struct snd_soc_codec *codec = socdev->codec;
545 
546 	if (codec->control_data)
547 		wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
548 
549 	snd_soc_free_pcms(socdev);
550 	snd_soc_dapm_free(socdev);
551 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
552 	i2c_unregister_device(codec->control_data);
553 	i2c_del_driver(&wm8728_i2c_driver);
554 #endif
555 #if defined(CONFIG_SPI_MASTER)
556 	spi_unregister_driver(&wm8728_spi_driver);
557 #endif
558 	kfree(codec);
559 
560 	return 0;
561 }
562 
563 struct snd_soc_codec_device soc_codec_dev_wm8728 = {
564 	.probe = 	wm8728_probe,
565 	.remove = 	wm8728_remove,
566 	.suspend = 	wm8728_suspend,
567 	.resume =	wm8728_resume,
568 };
569 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8728);
570 
wm8728_modinit(void)571 static int __init wm8728_modinit(void)
572 {
573 	return snd_soc_register_dai(&wm8728_dai);
574 }
575 module_init(wm8728_modinit);
576 
wm8728_exit(void)577 static void __exit wm8728_exit(void)
578 {
579 	snd_soc_unregister_dai(&wm8728_dai);
580 }
581 module_exit(wm8728_exit);
582 
583 MODULE_DESCRIPTION("ASoC WM8728 driver");
584 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
585 MODULE_LICENSE("GPL");
586