• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/sound/soc/codecs/
1/*
2 * wm8741.c  --  WM8741 ALSA SoC Audio driver
3 *
4 * Copyright 2010 Wolfson Microelectronics plc
5 *
6 * Author: Ian Lartey <ian@opensource.wolfsonmicro.com>
7 *
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/module.h>
15#include <linux/moduleparam.h>
16#include <linux/init.h>
17#include <linux/delay.h>
18#include <linux/pm.h>
19#include <linux/i2c.h>
20#include <linux/platform_device.h>
21#include <linux/regulator/consumer.h>
22#include <linux/slab.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/soc-dapm.h>
28#include <sound/initval.h>
29#include <sound/tlv.h>
30
31#include "wm8741.h"
32
33static struct snd_soc_codec *wm8741_codec;
34struct snd_soc_codec_device soc_codec_dev_wm8741;
35
36#define WM8741_NUM_SUPPLIES 2
37static const char *wm8741_supply_names[WM8741_NUM_SUPPLIES] = {
38	"AVDD",
39	"DVDD",
40};
41
42#define WM8741_NUM_RATES 4
43
44/* codec private data */
45struct wm8741_priv {
46	struct snd_soc_codec codec;
47	u16 reg_cache[WM8741_REGISTER_COUNT];
48	struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES];
49	unsigned int sysclk;
50	unsigned int rate_constraint_list[WM8741_NUM_RATES];
51	struct snd_pcm_hw_constraint_list rate_constraint;
52};
53
54static const u16 wm8741_reg_defaults[WM8741_REGISTER_COUNT] = {
55	0x0000,     /* R0  - DACLLSB Attenuation */
56	0x0000,     /* R1  - DACLMSB Attenuation */
57	0x0000,     /* R2  - DACRLSB Attenuation */
58	0x0000,     /* R3  - DACRMSB Attenuation */
59	0x0000,     /* R4  - Volume Control */
60	0x000A,     /* R5  - Format Control */
61	0x0000,     /* R6  - Filter Control */
62	0x0000,     /* R7  - Mode Control 1 */
63	0x0002,     /* R8  - Mode Control 2 */
64	0x0000,	    /* R9  - Reset */
65	0x0002,     /* R32 - ADDITONAL_CONTROL_1 */
66};
67
68
69static int wm8741_reset(struct snd_soc_codec *codec)
70{
71	return snd_soc_write(codec, WM8741_RESET, 0);
72}
73
74static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0);
75static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 0);
76
77static const struct snd_kcontrol_new wm8741_snd_controls[] = {
78SOC_DOUBLE_R_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION,
79		 WM8741_DACRLSB_ATTENUATION, 1, 255, 1, dac_tlv_fine),
80SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION,
81		 WM8741_DACRMSB_ATTENUATION, 0, 511, 1, dac_tlv),
82};
83
84static const struct snd_soc_dapm_widget wm8741_dapm_widgets[] = {
85SND_SOC_DAPM_DAC("DACL", "Playback", SND_SOC_NOPM, 0, 0),
86SND_SOC_DAPM_DAC("DACR", "Playback", SND_SOC_NOPM, 0, 0),
87SND_SOC_DAPM_OUTPUT("VOUTLP"),
88SND_SOC_DAPM_OUTPUT("VOUTLN"),
89SND_SOC_DAPM_OUTPUT("VOUTRP"),
90SND_SOC_DAPM_OUTPUT("VOUTRN"),
91};
92
93static const struct snd_soc_dapm_route intercon[] = {
94	{ "VOUTLP", NULL, "DACL" },
95	{ "VOUTLN", NULL, "DACL" },
96	{ "VOUTRP", NULL, "DACR" },
97	{ "VOUTRN", NULL, "DACR" },
98};
99
100static int wm8741_add_widgets(struct snd_soc_codec *codec)
101{
102	snd_soc_dapm_new_controls(codec, wm8741_dapm_widgets,
103				  ARRAY_SIZE(wm8741_dapm_widgets));
104
105	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
106
107	return 0;
108}
109
110static struct {
111	int value;
112	int ratio;
113} lrclk_ratios[WM8741_NUM_RATES] = {
114	{ 1, 256 },
115	{ 2, 384 },
116	{ 3, 512 },
117	{ 4, 768 },
118};
119
120
121static int wm8741_startup(struct snd_pcm_substream *substream,
122			  struct snd_soc_dai *dai)
123{
124	struct snd_soc_codec *codec = dai->codec;
125	struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
126
127	/* The set of sample rates that can be supported depends on the
128	 * MCLK supplied to the CODEC - enforce this.
129	 */
130	if (!wm8741->sysclk) {
131		dev_err(codec->dev,
132			"No MCLK configured, call set_sysclk() on init\n");
133		return -EINVAL;
134	}
135
136	snd_pcm_hw_constraint_list(substream->runtime, 0,
137				   SNDRV_PCM_HW_PARAM_RATE,
138				   &wm8741->rate_constraint);
139
140	return 0;
141}
142
143static int wm8741_hw_params(struct snd_pcm_substream *substream,
144			    struct snd_pcm_hw_params *params,
145			    struct snd_soc_dai *dai)
146{
147	struct snd_soc_pcm_runtime *rtd = substream->private_data;
148	struct snd_soc_device *socdev = rtd->socdev;
149	struct snd_soc_codec *codec = socdev->card->codec;
150	struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
151	u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1FC;
152	int i;
153
154	/* Find a supported LRCLK ratio */
155	for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
156		if (wm8741->sysclk / params_rate(params) ==
157		    lrclk_ratios[i].ratio)
158			break;
159	}
160
161	/* Should never happen, should be handled by constraints */
162	if (i == ARRAY_SIZE(lrclk_ratios)) {
163		dev_err(codec->dev, "MCLK/fs ratio %d unsupported\n",
164			wm8741->sysclk / params_rate(params));
165		return -EINVAL;
166	}
167
168	/* bit size */
169	switch (params_format(params)) {
170	case SNDRV_PCM_FORMAT_S16_LE:
171		break;
172	case SNDRV_PCM_FORMAT_S20_3LE:
173		iface |= 0x0001;
174		break;
175	case SNDRV_PCM_FORMAT_S24_LE:
176		iface |= 0x0002;
177		break;
178	case SNDRV_PCM_FORMAT_S32_LE:
179		iface |= 0x0003;
180		break;
181	default:
182		dev_dbg(codec->dev, "wm8741_hw_params:    Unsupported bit size param = %d",
183			params_format(params));
184		return -EINVAL;
185	}
186
187	dev_dbg(codec->dev, "wm8741_hw_params:    bit size param = %d",
188		params_format(params));
189
190	snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface);
191	return 0;
192}
193
194static int wm8741_set_dai_sysclk(struct snd_soc_dai *codec_dai,
195		int clk_id, unsigned int freq, int dir)
196{
197	struct snd_soc_codec *codec = codec_dai->codec;
198	struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
199	unsigned int val;
200	int i;
201
202	dev_dbg(codec->dev, "wm8741_set_dai_sysclk info: freq=%dHz\n", freq);
203
204	wm8741->sysclk = freq;
205
206	wm8741->rate_constraint.count = 0;
207
208	for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
209		dev_dbg(codec->dev, "index = %d, ratio = %d, freq = %d",
210				i, lrclk_ratios[i].ratio, freq);
211
212		val = freq / lrclk_ratios[i].ratio;
213		/* Check that it's a standard rate since core can't
214		 * cope with others and having the odd rates confuses
215		 * constraint matching.
216		 */
217		switch (val) {
218		case 32000:
219		case 44100:
220		case 48000:
221		case 64000:
222		case 88200:
223		case 96000:
224			dev_dbg(codec->dev, "Supported sample rate: %dHz\n",
225				val);
226			wm8741->rate_constraint_list[i] = val;
227			wm8741->rate_constraint.count++;
228			break;
229		default:
230			dev_dbg(codec->dev, "Skipping sample rate: %dHz\n",
231				val);
232		}
233	}
234
235	/* Need at least one supported rate... */
236	if (wm8741->rate_constraint.count == 0)
237		return -EINVAL;
238
239	return 0;
240}
241
242static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai,
243		unsigned int fmt)
244{
245	struct snd_soc_codec *codec = codec_dai->codec;
246	u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1C3;
247
248	/* check master/slave audio interface */
249	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
250	case SND_SOC_DAIFMT_CBS_CFS:
251		break;
252	default:
253		return -EINVAL;
254	}
255
256	/* interface format */
257	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
258	case SND_SOC_DAIFMT_I2S:
259		iface |= 0x0008;
260		break;
261	case SND_SOC_DAIFMT_RIGHT_J:
262		break;
263	case SND_SOC_DAIFMT_LEFT_J:
264		iface |= 0x0004;
265		break;
266	case SND_SOC_DAIFMT_DSP_A:
267		iface |= 0x0003;
268		break;
269	case SND_SOC_DAIFMT_DSP_B:
270		iface |= 0x0013;
271		break;
272	default:
273		return -EINVAL;
274	}
275
276	/* clock inversion */
277	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
278	case SND_SOC_DAIFMT_NB_NF:
279		break;
280	case SND_SOC_DAIFMT_IB_IF:
281		iface |= 0x0010;
282		break;
283	case SND_SOC_DAIFMT_IB_NF:
284		iface |= 0x0020;
285		break;
286	case SND_SOC_DAIFMT_NB_IF:
287		iface |= 0x0030;
288		break;
289	default:
290		return -EINVAL;
291	}
292
293
294	dev_dbg(codec->dev, "wm8741_set_dai_fmt:    Format=%x, Clock Inv=%x\n",
295				fmt & SND_SOC_DAIFMT_FORMAT_MASK,
296				((fmt & SND_SOC_DAIFMT_INV_MASK)));
297
298	snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface);
299	return 0;
300}
301
302#define WM8741_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
303			SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
304			SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \
305			SNDRV_PCM_RATE_192000)
306
307#define WM8741_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
308			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
309
310static struct snd_soc_dai_ops wm8741_dai_ops = {
311	.startup	= wm8741_startup,
312	.hw_params	= wm8741_hw_params,
313	.set_sysclk	= wm8741_set_dai_sysclk,
314	.set_fmt	= wm8741_set_dai_fmt,
315};
316
317struct snd_soc_dai wm8741_dai = {
318	.name = "WM8741",
319	.playback = {
320		.stream_name = "Playback",
321		.channels_min = 2,  /* Mono modes not yet supported */
322		.channels_max = 2,
323		.rates = WM8741_RATES,
324		.formats = WM8741_FORMATS,
325	},
326	.ops = &wm8741_dai_ops,
327};
328EXPORT_SYMBOL_GPL(wm8741_dai);
329
330#ifdef CONFIG_PM
331static int wm8741_resume(struct platform_device *pdev)
332{
333	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
334	struct snd_soc_codec *codec = socdev->card->codec;
335	u16 *cache = codec->reg_cache;
336	int i;
337
338	/* RESTORE REG Cache */
339	for (i = 0; i < WM8741_REGISTER_COUNT; i++) {
340		if (cache[i] == wm8741_reg_defaults[i] || WM8741_RESET == i)
341			continue;
342		snd_soc_write(codec, i, cache[i]);
343	}
344	return 0;
345}
346#else
347#define wm8741_suspend NULL
348#define wm8741_resume NULL
349#endif
350
351static int wm8741_probe(struct platform_device *pdev)
352{
353	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
354	struct snd_soc_codec *codec;
355	int ret = 0;
356
357	if (wm8741_codec == NULL) {
358		dev_err(&pdev->dev, "Codec device not registered\n");
359		return -ENODEV;
360	}
361
362	socdev->card->codec = wm8741_codec;
363	codec = wm8741_codec;
364
365	/* register pcms */
366	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
367	if (ret < 0) {
368		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
369		goto pcm_err;
370	}
371
372	snd_soc_add_controls(codec, wm8741_snd_controls,
373			     ARRAY_SIZE(wm8741_snd_controls));
374	wm8741_add_widgets(codec);
375
376	return ret;
377
378pcm_err:
379	return ret;
380}
381
382static int wm8741_remove(struct platform_device *pdev)
383{
384	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
385
386	snd_soc_free_pcms(socdev);
387	snd_soc_dapm_free(socdev);
388
389	return 0;
390}
391
392struct snd_soc_codec_device soc_codec_dev_wm8741 = {
393	.probe =	wm8741_probe,
394	.remove =	wm8741_remove,
395	.resume =	wm8741_resume,
396};
397EXPORT_SYMBOL_GPL(soc_codec_dev_wm8741);
398
399static int wm8741_register(struct wm8741_priv *wm8741,
400			   enum snd_soc_control_type control)
401{
402	int ret;
403	struct snd_soc_codec *codec = &wm8741->codec;
404	int i;
405
406	if (wm8741_codec) {
407		dev_err(codec->dev, "Another WM8741 is registered\n");
408		return -EINVAL;
409	}
410
411	mutex_init(&codec->mutex);
412	INIT_LIST_HEAD(&codec->dapm_widgets);
413	INIT_LIST_HEAD(&codec->dapm_paths);
414
415	snd_soc_codec_set_drvdata(codec, wm8741);
416	codec->name = "WM8741";
417	codec->owner = THIS_MODULE;
418	codec->bias_level = SND_SOC_BIAS_OFF;
419	codec->set_bias_level = NULL;
420	codec->dai = &wm8741_dai;
421	codec->num_dai = 1;
422	codec->reg_cache_size = WM8741_REGISTER_COUNT;
423	codec->reg_cache = &wm8741->reg_cache;
424
425	wm8741->rate_constraint.list = &wm8741->rate_constraint_list[0];
426	wm8741->rate_constraint.count =
427		ARRAY_SIZE(wm8741->rate_constraint_list);
428
429	memcpy(codec->reg_cache, wm8741_reg_defaults,
430		sizeof(wm8741->reg_cache));
431
432	ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
433	if (ret != 0) {
434		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
435		goto err;
436	}
437
438	for (i = 0; i < ARRAY_SIZE(wm8741->supplies); i++)
439		wm8741->supplies[i].supply = wm8741_supply_names[i];
440
441	ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8741->supplies),
442				 wm8741->supplies);
443	if (ret != 0) {
444		dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
445		goto err;
446	}
447
448	ret = regulator_bulk_enable(ARRAY_SIZE(wm8741->supplies),
449				    wm8741->supplies);
450	if (ret != 0) {
451		dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
452		goto err_get;
453	}
454
455	ret = wm8741_reset(codec);
456	if (ret < 0) {
457		dev_err(codec->dev, "Failed to issue reset\n");
458		goto err_enable;
459	}
460
461	wm8741_dai.dev = codec->dev;
462
463	/* Change some default settings - latch VU */
464	wm8741->reg_cache[WM8741_DACLLSB_ATTENUATION] |= WM8741_UPDATELL;
465	wm8741->reg_cache[WM8741_DACLMSB_ATTENUATION] |= WM8741_UPDATELM;
466	wm8741->reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERL;
467	wm8741->reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERM;
468
469	wm8741_codec = codec;
470
471	ret = snd_soc_register_codec(codec);
472	if (ret != 0) {
473		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
474		return ret;
475	}
476
477	ret = snd_soc_register_dai(&wm8741_dai);
478	if (ret != 0) {
479		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
480		snd_soc_unregister_codec(codec);
481		return ret;
482	}
483
484	dev_dbg(codec->dev, "Successful registration\n");
485	return 0;
486
487err_enable:
488	regulator_bulk_disable(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
489
490err_get:
491	regulator_bulk_free(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
492
493err:
494	kfree(wm8741);
495	return ret;
496}
497
498static void wm8741_unregister(struct wm8741_priv *wm8741)
499{
500	regulator_bulk_free(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
501
502	snd_soc_unregister_dai(&wm8741_dai);
503	snd_soc_unregister_codec(&wm8741->codec);
504	kfree(wm8741);
505	wm8741_codec = NULL;
506}
507
508#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
509static __devinit int wm8741_i2c_probe(struct i2c_client *i2c,
510				      const struct i2c_device_id *id)
511{
512	struct wm8741_priv *wm8741;
513	struct snd_soc_codec *codec;
514
515	wm8741 = kzalloc(sizeof(struct wm8741_priv), GFP_KERNEL);
516	if (wm8741 == NULL)
517		return -ENOMEM;
518
519	codec = &wm8741->codec;
520	codec->hw_write = (hw_write_t)i2c_master_send;
521
522	i2c_set_clientdata(i2c, wm8741);
523	codec->control_data = i2c;
524
525	codec->dev = &i2c->dev;
526
527	return wm8741_register(wm8741, SND_SOC_I2C);
528}
529
530static __devexit int wm8741_i2c_remove(struct i2c_client *client)
531{
532	struct wm8741_priv *wm8741 = i2c_get_clientdata(client);
533	wm8741_unregister(wm8741);
534	return 0;
535}
536
537static const struct i2c_device_id wm8741_i2c_id[] = {
538	{ "wm8741", 0 },
539	{ }
540};
541MODULE_DEVICE_TABLE(i2c, wm8741_i2c_id);
542
543
544static struct i2c_driver wm8741_i2c_driver = {
545	.driver = {
546		.name = "WM8741",
547		.owner = THIS_MODULE,
548	},
549	.probe =    wm8741_i2c_probe,
550	.remove =   __devexit_p(wm8741_i2c_remove),
551	.id_table = wm8741_i2c_id,
552};
553#endif
554
555static int __init wm8741_modinit(void)
556{
557	int ret;
558#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
559	ret = i2c_add_driver(&wm8741_i2c_driver);
560	if (ret != 0) {
561		printk(KERN_ERR "Failed to register WM8741 I2C driver: %d\n",
562		       ret);
563	}
564#endif
565	return 0;
566}
567module_init(wm8741_modinit);
568
569static void __exit wm8741_exit(void)
570{
571#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
572	i2c_del_driver(&wm8741_i2c_driver);
573#endif
574}
575module_exit(wm8741_exit);
576
577MODULE_DESCRIPTION("ASoC WM8741 driver");
578MODULE_AUTHOR("Ian Lartey <ian@opensource.wolfsonmicro.com>");
579MODULE_LICENSE("GPL");
580