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