]> git.karo-electronics.de Git - mv-sheeva.git/blob - sound/soc/kirkwood/kirkwood-i2s.c
ASoC: kirkwood: Add i2s support
[mv-sheeva.git] / sound / soc / kirkwood / kirkwood-i2s.c
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         writel(i2s_value, priv->io+i2s_reg);
157         writel(value, priv->io+reg);
158
159         return 0;
160 }
161
162 static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
163                                 int cmd, struct snd_soc_dai *dai)
164 {
165         unsigned long value;
166
167         /*
168          * specs says KIRKWOOD_PLAYCTL must be read 2 times before
169          * changing it. So read 1 time here and 1 later.
170          */
171         value = readl(priv->io + KIRKWOOD_PLAYCTL);
172
173         switch (cmd) {
174         case SNDRV_PCM_TRIGGER_START:
175                 /* stop audio, enable interrupts */
176                 value = readl(priv->io + KIRKWOOD_PLAYCTL);
177                 value |= KIRKWOOD_PLAYCTL_PAUSE;
178                 writel(value, priv->io + KIRKWOOD_PLAYCTL);
179
180                 value = readl(priv->io + KIRKWOOD_INT_MASK);
181                 value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES;
182                 writel(value, priv->io + KIRKWOOD_INT_MASK);
183
184                 /* configure audio & enable i2s playback */
185                 value = readl(priv->io + KIRKWOOD_PLAYCTL);
186                 value &= ~KIRKWOOD_PLAYCTL_BURST_MASK;
187                 value &= ~(KIRKWOOD_PLAYCTL_PAUSE|KIRKWOOD_PLAYCTL_SPDIF_EN);
188
189                 if (priv->burst == 32)
190                         value |= KIRKWOOD_PLAYCTL_BURST_32;
191                 else
192                         value |= KIRKWOOD_PLAYCTL_BURST_128;
193                 value |= KIRKWOOD_PLAYCTL_I2S_EN;
194                 writel(value, priv->io + KIRKWOOD_PLAYCTL);
195                 break;
196
197         case SNDRV_PCM_TRIGGER_STOP:
198                 /* stop audio, disable interrupts */
199                 value = readl(priv->io + KIRKWOOD_PLAYCTL);
200                 value |= KIRKWOOD_PLAYCTL_PAUSE;
201                 writel(value, priv->io + KIRKWOOD_PLAYCTL);
202
203                 value = readl(priv->io + KIRKWOOD_INT_MASK);
204                 value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES;
205                 writel(value, priv->io + KIRKWOOD_INT_MASK);
206
207                 /* disable all playbacks */
208                 value = readl(priv->io + KIRKWOOD_PLAYCTL);
209                 value &= ~(KIRKWOOD_PLAYCTL_I2S_EN | KIRKWOOD_PLAYCTL_SPDIF_EN);
210                 writel(value, priv->io + KIRKWOOD_PLAYCTL);
211                 break;
212
213         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
214         case SNDRV_PCM_TRIGGER_SUSPEND:
215                 value = readl(priv->io + KIRKWOOD_PLAYCTL);
216                 value |= KIRKWOOD_PLAYCTL_PAUSE;
217                 writel(value, priv->io + KIRKWOOD_PLAYCTL);
218                 break;
219
220         case SNDRV_PCM_TRIGGER_RESUME:
221         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
222                 value = readl(priv->io + KIRKWOOD_PLAYCTL);
223                 value &= ~KIRKWOOD_PLAYCTL_PAUSE;
224                 writel(value, priv->io + KIRKWOOD_PLAYCTL);
225                 break;
226
227         default:
228                 return -EINVAL;
229         }
230
231         return 0;
232 }
233
234 static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
235                                 int cmd, struct snd_soc_dai *dai)
236 {
237         unsigned long value;
238
239         value = readl(priv->io + KIRKWOOD_RECCTL);
240
241         switch (cmd) {
242         case SNDRV_PCM_TRIGGER_START:
243                 /* stop audio, enable interrupts */
244                 value = readl(priv->io + KIRKWOOD_RECCTL);
245                 value |= KIRKWOOD_RECCTL_PAUSE;
246                 writel(value, priv->io + KIRKWOOD_RECCTL);
247
248                 value = readl(priv->io + KIRKWOOD_INT_MASK);
249                 value |= KIRKWOOD_INT_CAUSE_REC_BYTES;
250                 writel(value, priv->io + KIRKWOOD_INT_MASK);
251
252                 /* configure audio & enable i2s record */
253                 value = readl(priv->io + KIRKWOOD_RECCTL);
254                 value &= ~KIRKWOOD_RECCTL_BURST_MASK;
255                 value &= ~KIRKWOOD_RECCTL_MONO;
256                 value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_SPDIF_EN);
257
258                 if (priv->burst == 32)
259                         value |= KIRKWOOD_RECCTL_BURST_32;
260                 else
261                         value |= KIRKWOOD_RECCTL_BURST_128;
262                 value |= KIRKWOOD_RECCTL_I2S_EN;
263
264                 writel(value, priv->io + KIRKWOOD_RECCTL);
265                 break;
266
267         case SNDRV_PCM_TRIGGER_STOP:
268                 /* stop audio, disable interrupts */
269                 value = readl(priv->io + KIRKWOOD_RECCTL);
270                 value |= KIRKWOOD_RECCTL_PAUSE;
271                 writel(value, priv->io + KIRKWOOD_RECCTL);
272
273                 value = readl(priv->io + KIRKWOOD_INT_MASK);
274                 value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES;
275                 writel(value, priv->io + KIRKWOOD_INT_MASK);
276
277                 /* disable all records */
278                 value = readl(priv->io + KIRKWOOD_RECCTL);
279                 value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN);
280                 writel(value, priv->io + KIRKWOOD_RECCTL);
281                 break;
282
283         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
284         case SNDRV_PCM_TRIGGER_SUSPEND:
285                 value = readl(priv->io + KIRKWOOD_RECCTL);
286                 value |= KIRKWOOD_RECCTL_PAUSE;
287                 writel(value, priv->io + KIRKWOOD_RECCTL);
288                 break;
289
290         case SNDRV_PCM_TRIGGER_RESUME:
291         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
292                 value = readl(priv->io + KIRKWOOD_RECCTL);
293                 value &= ~KIRKWOOD_RECCTL_PAUSE;
294                 writel(value, priv->io + KIRKWOOD_RECCTL);
295                 break;
296
297         default:
298                 return -EINVAL;
299                 break;
300         }
301
302         return 0;
303 }
304
305 static int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
306                                struct snd_soc_dai *dai)
307 {
308         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
309                 return kirkwood_i2s_play_trigger(substream, cmd, dai);
310         else
311                 return kirkwood_i2s_rec_trigger(substream, cmd, dai);
312
313         return 0;
314 }
315
316 static int kirkwood_i2s_probe(struct platform_device *pdev,
317                              struct snd_soc_dai *dai)
318 {
319         unsigned long value;
320         unsigned int reg_data;
321
322         /* put system in a "safe" state : */
323         /* disable audio interrupts */
324         writel(0xffffffff, priv->io + KIRKWOOD_INT_CAUSE);
325         writel(0, priv->io + KIRKWOOD_INT_MASK);
326
327         reg_data = readl(priv->io + 0x1200);
328         reg_data &= (~(0x333FF8));
329         reg_data |= 0x111D18;
330         writel(reg_data, priv->io + 0x1200);
331
332         msleep(500);
333
334         reg_data = readl(priv->io + 0x1200);
335         reg_data &= (~(0x333FF8));
336         reg_data |= 0x111D18;
337         writel(reg_data, priv->io + 0x1200);
338
339         /* disable playback/record */
340         value = readl(priv->io + KIRKWOOD_PLAYCTL);
341         value &= ~(KIRKWOOD_PLAYCTL_I2S_EN|KIRKWOOD_PLAYCTL_SPDIF_EN);
342         writel(value, priv->io + KIRKWOOD_PLAYCTL);
343
344         value = readl(priv->io + KIRKWOOD_RECCTL);
345         value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN);
346         writel(value, priv->io + KIRKWOOD_RECCTL);
347
348         return 0;
349
350 }
351
352 static void kirkwood_i2s_remove(struct platform_device *pdev,
353                                 struct snd_soc_dai *dai)
354 {
355 }
356
357 static struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
358         .trigger        = kirkwood_i2s_trigger,
359         .hw_params      = kirkwood_i2s_hw_params,
360         .set_fmt        = kirkwood_i2s_set_fmt,
361 };
362
363
364 struct snd_soc_dai kirkwood_i2s_dai = {
365         .name = DRV_NAME,
366         .id = 0,
367         .probe = kirkwood_i2s_probe,
368         .remove = kirkwood_i2s_remove,
369         .playback = {
370                 .channels_min = 1,
371                 .channels_max = 2,
372                 .rates = KIRKWOOD_I2S_RATES,
373                 .formats = KIRKWOOD_I2S_FORMATS,},
374         .capture = {
375                 .channels_min = 1,
376                 .channels_max = 2,
377                 .rates = KIRKWOOD_I2S_RATES,
378                 .formats = KIRKWOOD_I2S_FORMATS,},
379         .ops = &kirkwood_i2s_dai_ops,
380 };
381 EXPORT_SYMBOL_GPL(kirkwood_i2s_dai);
382
383 static __devinit int kirkwood_i2s_dev_probe(struct platform_device *pdev)
384 {
385         struct resource *mem;
386         struct kirkwood_asoc_platform_data *data =
387                 pdev->dev.platform_data;
388         int err;
389
390         priv = kzalloc(sizeof(struct kirkwood_dma_data), GFP_KERNEL);
391         if (!priv) {
392                 dev_err(&pdev->dev, "allocation failed\n");
393                 err = -ENOMEM;
394                 goto error;
395         }
396
397         mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
398         if (!mem) {
399                 dev_err(&pdev->dev, "platform_get_resource failed\n");
400                 err = -ENXIO;
401                 goto err_alloc;
402         }
403
404         priv->mem = request_mem_region(mem->start, SZ_16K, DRV_NAME);
405         if (!priv->mem) {
406                 dev_err(&pdev->dev, "request_mem_region failed\n");
407                 err = -EBUSY;
408                 goto error;
409         }
410
411         priv->io = ioremap(priv->mem->start, SZ_16K);
412         if (!priv->io) {
413                 dev_err(&pdev->dev, "ioremap failed\n");
414                 err = -ENOMEM;
415                 goto err_iomem;
416         }
417
418         priv->irq = platform_get_irq(pdev, 0);
419         if (priv->irq <= 0) {
420                 dev_err(&pdev->dev, "platform_get_irq failed\n");
421                 err = -ENXIO;
422                 goto err_ioremap;
423         }
424
425         if (!data || !data->dram) {
426                 dev_err(&pdev->dev, "no platform data ?!\n");
427                 err = -EINVAL;
428                 goto err_ioremap;
429         }
430
431         priv->dram = data->dram;
432         priv->burst = data->burst;
433
434         kirkwood_i2s_dai.capture.dma_data = priv;
435         kirkwood_i2s_dai.playback.dma_data = priv;
436
437         return snd_soc_register_dai(&kirkwood_i2s_dai);
438
439 err_ioremap:
440         iounmap(priv->io);
441 err_iomem:
442         release_mem_region(priv->mem->start, SZ_16K);
443 err_alloc:
444         kfree(priv);
445 error:
446         return err;
447 }
448
449 static __devexit int kirkwood_i2s_dev_remove(struct platform_device *pdev)
450 {
451         if (priv) {
452                 iounmap(priv->io);
453                 release_mem_region(priv->mem->start, SZ_16K);
454                 kfree(priv);
455         }
456         snd_soc_unregister_dai(&kirkwood_i2s_dai);
457         return 0;
458 }
459
460 static struct platform_driver kirkwood_i2s_driver = {
461         .probe  = kirkwood_i2s_dev_probe,
462         .remove = kirkwood_i2s_dev_remove,
463         .driver = {
464                 .name = DRV_NAME,
465                 .owner = THIS_MODULE,
466         },
467 };
468
469 static int __init kirkwood_i2s_init(void)
470 {
471         return platform_driver_register(&kirkwood_i2s_driver);
472 }
473 module_init(kirkwood_i2s_init);
474
475 static void __exit kirkwood_i2s_exit(void)
476 {
477         platform_driver_unregister(&kirkwood_i2s_driver);
478 }
479 module_exit(kirkwood_i2s_exit);
480
481 /* Module information */
482 MODULE_AUTHOR("Arnaud Patard, <apatard@mandriva.com>");
483 MODULE_DESCRIPTION("Kirkwood I2S SoC Interface");
484 MODULE_LICENSE("GPL");
485 MODULE_ALIAS("platform:kirkwood-i2s");