• 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/kirkwood/
1/*
2 * kirkwood-i2s.c
3 *
4 * (c) 2010 Arnaud Patard <apatard@mandriva.com>
5 *
6 *  This program is free software; you can redistribute  it and/or modify it
7 *  under  the terms of  the GNU General  Public License as published by the
8 *  Free Software Foundation;  either version 2 of the  License, or (at your
9 *  option) any later version.
10 */
11
12#include <linux/init.h>
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/io.h>
16#include <linux/slab.h>
17#include <linux/mbus.h>
18#include <linux/delay.h>
19#include <sound/pcm.h>
20#include <sound/pcm_params.h>
21#include <sound/soc.h>
22#include <plat/audio.h>
23#include "kirkwood-i2s.h"
24#include "kirkwood.h"
25
26#define DRV_NAME	"kirkwood-i2s"
27
28#define KIRKWOOD_I2S_RATES \
29	(SNDRV_PCM_RATE_44100 | \
30	 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
31#define KIRKWOOD_I2S_FORMATS \
32	(SNDRV_PCM_FMTBIT_S16_LE | \
33	 SNDRV_PCM_FMTBIT_S24_LE | \
34	 SNDRV_PCM_FMTBIT_S32_LE)
35
36
37struct snd_soc_dai kirkwood_i2s_dai;
38static struct kirkwood_dma_data *priv;
39
40static int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
41		unsigned int fmt)
42{
43	unsigned long mask;
44	unsigned long value;
45
46	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
47	case SND_SOC_DAIFMT_RIGHT_J:
48		mask = KIRKWOOD_I2S_CTL_RJ;
49		break;
50	case SND_SOC_DAIFMT_LEFT_J:
51		mask = KIRKWOOD_I2S_CTL_LJ;
52		break;
53	case SND_SOC_DAIFMT_I2S:
54		mask = KIRKWOOD_I2S_CTL_I2S;
55		break;
56	default:
57		return -EINVAL;
58	}
59
60	/*
61	 * Set same format for playback and record
62	 * This avoids some troubles.
63	 */
64	value = readl(priv->io+KIRKWOOD_I2S_PLAYCTL);
65	value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
66	value |= mask;
67	writel(value, priv->io+KIRKWOOD_I2S_PLAYCTL);
68
69	value = readl(priv->io+KIRKWOOD_I2S_RECCTL);
70	value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
71	value |= mask;
72	writel(value, priv->io+KIRKWOOD_I2S_RECCTL);
73
74	return 0;
75}
76
77static inline void kirkwood_set_dco(void __iomem *io, unsigned long rate)
78{
79	unsigned long value;
80
81	value = KIRKWOOD_DCO_CTL_OFFSET_0;
82	switch (rate) {
83	default:
84	case 44100:
85		value |= KIRKWOOD_DCO_CTL_FREQ_11;
86		break;
87	case 48000:
88		value |= KIRKWOOD_DCO_CTL_FREQ_12;
89		break;
90	case 96000:
91		value |= KIRKWOOD_DCO_CTL_FREQ_24;
92		break;
93	}
94	writel(value, io + KIRKWOOD_DCO_CTL);
95
96	/* wait for dco locked */
97	do {
98		cpu_relax();
99		value = readl(io + KIRKWOOD_DCO_SPCR_STATUS);
100		value &= KIRKWOOD_DCO_SPCR_STATUS;
101	} while (value == 0);
102}
103
104static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
105				 struct snd_pcm_hw_params *params,
106				 struct snd_soc_dai *dai)
107{
108	unsigned int i2s_reg, reg;
109	unsigned long i2s_value, value;
110
111	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
112		i2s_reg = KIRKWOOD_I2S_PLAYCTL;
113		reg = KIRKWOOD_PLAYCTL;
114	} else {
115		i2s_reg = KIRKWOOD_I2S_RECCTL;
116		reg = KIRKWOOD_RECCTL;
117	}
118
119	/* set dco conf */
120	kirkwood_set_dco(priv->io, params_rate(params));
121
122	i2s_value = readl(priv->io+i2s_reg);
123	i2s_value &= ~KIRKWOOD_I2S_CTL_SIZE_MASK;
124
125	value = readl(priv->io+reg);
126	value &= ~KIRKWOOD_PLAYCTL_SIZE_MASK;
127
128	/*
129	 * Size settings in play/rec i2s control regs and play/rec control
130	 * regs must be the same.
131	 */
132	switch (params_format(params)) {
133	case SNDRV_PCM_FORMAT_S16_LE:
134		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16;
135		value |= KIRKWOOD_PLAYCTL_SIZE_16_C;
136		break;
137	/*
138	 * doesn't work... S20_3LE != kirkwood 20bit format ?
139	 *
140	case SNDRV_PCM_FORMAT_S20_3LE:
141		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20;
142		value |= KIRKWOOD_PLAYCTL_SIZE_20;
143		break;
144	*/
145	case SNDRV_PCM_FORMAT_S24_LE:
146		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24;
147		value |= KIRKWOOD_PLAYCTL_SIZE_24;
148		break;
149	case SNDRV_PCM_FORMAT_S32_LE:
150		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32;
151		value |= KIRKWOOD_PLAYCTL_SIZE_32;
152		break;
153	default:
154		return -EINVAL;
155	}
156
157	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
158		value &= ~KIRKWOOD_PLAYCTL_MONO_MASK;
159		if (params_channels(params) == 1)
160			value |= KIRKWOOD_PLAYCTL_MONO_BOTH;
161		else
162			value |= KIRKWOOD_PLAYCTL_MONO_OFF;
163	}
164
165	writel(i2s_value, priv->io+i2s_reg);
166	writel(value, priv->io+reg);
167
168	return 0;
169}
170
171static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
172				int cmd, struct snd_soc_dai *dai)
173{
174	unsigned long value;
175
176	/*
177	 * specs says KIRKWOOD_PLAYCTL must be read 2 times before
178	 * changing it. So read 1 time here and 1 later.
179	 */
180	value = readl(priv->io + KIRKWOOD_PLAYCTL);
181
182	switch (cmd) {
183	case SNDRV_PCM_TRIGGER_START:
184		/* stop audio, enable interrupts */
185		value = readl(priv->io + KIRKWOOD_PLAYCTL);
186		value |= KIRKWOOD_PLAYCTL_PAUSE;
187		writel(value, priv->io + KIRKWOOD_PLAYCTL);
188
189		value = readl(priv->io + KIRKWOOD_INT_MASK);
190		value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES;
191		writel(value, priv->io + KIRKWOOD_INT_MASK);
192
193		/* configure audio & enable i2s playback */
194		value = readl(priv->io + KIRKWOOD_PLAYCTL);
195		value &= ~KIRKWOOD_PLAYCTL_BURST_MASK;
196		value &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE
197				| KIRKWOOD_PLAYCTL_SPDIF_EN);
198
199		if (priv->burst == 32)
200			value |= KIRKWOOD_PLAYCTL_BURST_32;
201		else
202			value |= KIRKWOOD_PLAYCTL_BURST_128;
203		value |= KIRKWOOD_PLAYCTL_I2S_EN;
204		writel(value, priv->io + KIRKWOOD_PLAYCTL);
205		break;
206
207	case SNDRV_PCM_TRIGGER_STOP:
208		/* stop audio, disable interrupts */
209		value = readl(priv->io + KIRKWOOD_PLAYCTL);
210		value |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE;
211		writel(value, priv->io + KIRKWOOD_PLAYCTL);
212
213		value = readl(priv->io + KIRKWOOD_INT_MASK);
214		value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES;
215		writel(value, priv->io + KIRKWOOD_INT_MASK);
216
217		/* disable all playbacks */
218		value = readl(priv->io + KIRKWOOD_PLAYCTL);
219		value &= ~(KIRKWOOD_PLAYCTL_I2S_EN | KIRKWOOD_PLAYCTL_SPDIF_EN);
220		writel(value, priv->io + KIRKWOOD_PLAYCTL);
221		break;
222
223	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
224	case SNDRV_PCM_TRIGGER_SUSPEND:
225		value = readl(priv->io + KIRKWOOD_PLAYCTL);
226		value |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE;
227		writel(value, priv->io + KIRKWOOD_PLAYCTL);
228		break;
229
230	case SNDRV_PCM_TRIGGER_RESUME:
231	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
232		value = readl(priv->io + KIRKWOOD_PLAYCTL);
233		value &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE);
234		writel(value, priv->io + KIRKWOOD_PLAYCTL);
235		break;
236
237	default:
238		return -EINVAL;
239	}
240
241	return 0;
242}
243
244static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
245				int cmd, struct snd_soc_dai *dai)
246{
247	unsigned long value;
248
249	value = readl(priv->io + KIRKWOOD_RECCTL);
250
251	switch (cmd) {
252	case SNDRV_PCM_TRIGGER_START:
253		/* stop audio, enable interrupts */
254		value = readl(priv->io + KIRKWOOD_RECCTL);
255		value |= KIRKWOOD_RECCTL_PAUSE;
256		writel(value, priv->io + KIRKWOOD_RECCTL);
257
258		value = readl(priv->io + KIRKWOOD_INT_MASK);
259		value |= KIRKWOOD_INT_CAUSE_REC_BYTES;
260		writel(value, priv->io + KIRKWOOD_INT_MASK);
261
262		/* configure audio & enable i2s record */
263		value = readl(priv->io + KIRKWOOD_RECCTL);
264		value &= ~KIRKWOOD_RECCTL_BURST_MASK;
265		value &= ~KIRKWOOD_RECCTL_MONO;
266		value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE
267			| KIRKWOOD_RECCTL_SPDIF_EN);
268
269		if (priv->burst == 32)
270			value |= KIRKWOOD_RECCTL_BURST_32;
271		else
272			value |= KIRKWOOD_RECCTL_BURST_128;
273		value |= KIRKWOOD_RECCTL_I2S_EN;
274
275		writel(value, priv->io + KIRKWOOD_RECCTL);
276		break;
277
278	case SNDRV_PCM_TRIGGER_STOP:
279		/* stop audio, disable interrupts */
280		value = readl(priv->io + KIRKWOOD_RECCTL);
281		value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
282		writel(value, priv->io + KIRKWOOD_RECCTL);
283
284		value = readl(priv->io + KIRKWOOD_INT_MASK);
285		value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES;
286		writel(value, priv->io + KIRKWOOD_INT_MASK);
287
288		/* disable all records */
289		value = readl(priv->io + KIRKWOOD_RECCTL);
290		value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN);
291		writel(value, priv->io + KIRKWOOD_RECCTL);
292		break;
293
294	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
295	case SNDRV_PCM_TRIGGER_SUSPEND:
296		value = readl(priv->io + KIRKWOOD_RECCTL);
297		value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
298		writel(value, priv->io + KIRKWOOD_RECCTL);
299		break;
300
301	case SNDRV_PCM_TRIGGER_RESUME:
302	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
303		value = readl(priv->io + KIRKWOOD_RECCTL);
304		value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE);
305		writel(value, priv->io + KIRKWOOD_RECCTL);
306		break;
307
308	default:
309		return -EINVAL;
310	}
311
312	return 0;
313}
314
315static int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
316			       struct snd_soc_dai *dai)
317{
318	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
319		return kirkwood_i2s_play_trigger(substream, cmd, dai);
320	else
321		return kirkwood_i2s_rec_trigger(substream, cmd, dai);
322
323	return 0;
324}
325
326static int kirkwood_i2s_probe(struct platform_device *pdev,
327			     struct snd_soc_dai *dai)
328{
329	unsigned long value;
330	unsigned int reg_data;
331
332	/* put system in a "safe" state : */
333	/* disable audio interrupts */
334	writel(0xffffffff, priv->io + KIRKWOOD_INT_CAUSE);
335	writel(0, priv->io + KIRKWOOD_INT_MASK);
336
337	reg_data = readl(priv->io + 0x1200);
338	reg_data &= (~(0x333FF8));
339	reg_data |= 0x111D18;
340	writel(reg_data, priv->io + 0x1200);
341
342	msleep(500);
343
344	reg_data = readl(priv->io + 0x1200);
345	reg_data &= (~(0x333FF8));
346	reg_data |= 0x111D18;
347	writel(reg_data, priv->io + 0x1200);
348
349	/* disable playback/record */
350	value = readl(priv->io + KIRKWOOD_PLAYCTL);
351	value &= ~(KIRKWOOD_PLAYCTL_I2S_EN|KIRKWOOD_PLAYCTL_SPDIF_EN);
352	writel(value, priv->io + KIRKWOOD_PLAYCTL);
353
354	value = readl(priv->io + KIRKWOOD_RECCTL);
355	value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN);
356	writel(value, priv->io + KIRKWOOD_RECCTL);
357
358	return 0;
359
360}
361
362static void kirkwood_i2s_remove(struct platform_device *pdev,
363				struct snd_soc_dai *dai)
364{
365}
366
367static struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
368	.trigger	= kirkwood_i2s_trigger,
369	.hw_params      = kirkwood_i2s_hw_params,
370	.set_fmt        = kirkwood_i2s_set_fmt,
371};
372
373
374struct snd_soc_dai kirkwood_i2s_dai = {
375	.name = DRV_NAME,
376	.id = 0,
377	.probe = kirkwood_i2s_probe,
378	.remove = kirkwood_i2s_remove,
379	.playback = {
380		.channels_min = 1,
381		.channels_max = 2,
382		.rates = KIRKWOOD_I2S_RATES,
383		.formats = KIRKWOOD_I2S_FORMATS,},
384	.capture = {
385		.channels_min = 1,
386		.channels_max = 2,
387		.rates = KIRKWOOD_I2S_RATES,
388		.formats = KIRKWOOD_I2S_FORMATS,},
389	.ops = &kirkwood_i2s_dai_ops,
390};
391EXPORT_SYMBOL_GPL(kirkwood_i2s_dai);
392
393static __devinit int kirkwood_i2s_dev_probe(struct platform_device *pdev)
394{
395	struct resource *mem;
396	struct kirkwood_asoc_platform_data *data =
397		pdev->dev.platform_data;
398	int err;
399
400	priv = kzalloc(sizeof(struct kirkwood_dma_data), GFP_KERNEL);
401	if (!priv) {
402		dev_err(&pdev->dev, "allocation failed\n");
403		err = -ENOMEM;
404		goto error;
405	}
406
407	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
408	if (!mem) {
409		dev_err(&pdev->dev, "platform_get_resource failed\n");
410		err = -ENXIO;
411		goto err_alloc;
412	}
413
414	priv->mem = request_mem_region(mem->start, SZ_16K, DRV_NAME);
415	if (!priv->mem) {
416		dev_err(&pdev->dev, "request_mem_region failed\n");
417		err = -EBUSY;
418		goto error;
419	}
420
421	priv->io = ioremap(priv->mem->start, SZ_16K);
422	if (!priv->io) {
423		dev_err(&pdev->dev, "ioremap failed\n");
424		err = -ENOMEM;
425		goto err_iomem;
426	}
427
428	priv->irq = platform_get_irq(pdev, 0);
429	if (priv->irq <= 0) {
430		dev_err(&pdev->dev, "platform_get_irq failed\n");
431		err = -ENXIO;
432		goto err_ioremap;
433	}
434
435	if (!data || !data->dram) {
436		dev_err(&pdev->dev, "no platform data ?!\n");
437		err = -EINVAL;
438		goto err_ioremap;
439	}
440
441	priv->dram = data->dram;
442	priv->burst = data->burst;
443
444	kirkwood_i2s_dai.capture.dma_data = priv;
445	kirkwood_i2s_dai.playback.dma_data = priv;
446
447	return snd_soc_register_dai(&kirkwood_i2s_dai);
448
449err_ioremap:
450	iounmap(priv->io);
451err_iomem:
452	release_mem_region(priv->mem->start, SZ_16K);
453err_alloc:
454	kfree(priv);
455error:
456	return err;
457}
458
459static __devexit int kirkwood_i2s_dev_remove(struct platform_device *pdev)
460{
461	if (priv) {
462		iounmap(priv->io);
463		release_mem_region(priv->mem->start, SZ_16K);
464		kfree(priv);
465	}
466	snd_soc_unregister_dai(&kirkwood_i2s_dai);
467	return 0;
468}
469
470static struct platform_driver kirkwood_i2s_driver = {
471	.probe  = kirkwood_i2s_dev_probe,
472	.remove = kirkwood_i2s_dev_remove,
473	.driver = {
474		.name = DRV_NAME,
475		.owner = THIS_MODULE,
476	},
477};
478
479static int __init kirkwood_i2s_init(void)
480{
481	return platform_driver_register(&kirkwood_i2s_driver);
482}
483module_init(kirkwood_i2s_init);
484
485static void __exit kirkwood_i2s_exit(void)
486{
487	platform_driver_unregister(&kirkwood_i2s_driver);
488}
489module_exit(kirkwood_i2s_exit);
490
491/* Module information */
492MODULE_AUTHOR("Arnaud Patard, <apatard@mandriva.com>");
493MODULE_DESCRIPTION("Kirkwood I2S SoC Interface");
494MODULE_LICENSE("GPL");
495MODULE_ALIAS("platform:kirkwood-i2s");
496