xref: /linux/sound/soc/kirkwood/kirkwood-i2s.c (revision b424ec953344e0ea612a8cc2d8e59742a0273ac1)
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 
37 struct snd_soc_dai kirkwood_i2s_dai;
38 static struct kirkwood_dma_data *priv;
39 
40 static 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 
77 static 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 
104 static 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 
171 static 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 
244 static 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 
315 static 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 
326 static 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 
362 static void kirkwood_i2s_remove(struct platform_device *pdev,
363 				struct snd_soc_dai *dai)
364 {
365 }
366 
367 static 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 
374 struct 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 };
391 EXPORT_SYMBOL_GPL(kirkwood_i2s_dai);
392 
393 static __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 
449 err_ioremap:
450 	iounmap(priv->io);
451 err_iomem:
452 	release_mem_region(priv->mem->start, SZ_16K);
453 err_alloc:
454 	kfree(priv);
455 error:
456 	return err;
457 }
458 
459 static __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 
470 static 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 
479 static int __init kirkwood_i2s_init(void)
480 {
481 	return platform_driver_register(&kirkwood_i2s_driver);
482 }
483 module_init(kirkwood_i2s_init);
484 
485 static void __exit kirkwood_i2s_exit(void)
486 {
487 	platform_driver_unregister(&kirkwood_i2s_driver);
488 }
489 module_exit(kirkwood_i2s_exit);
490 
491 /* Module information */
492 MODULE_AUTHOR("Arnaud Patard, <apatard@mandriva.com>");
493 MODULE_DESCRIPTION("Kirkwood I2S SoC Interface");
494 MODULE_LICENSE("GPL");
495 MODULE_ALIAS("platform:kirkwood-i2s");
496