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