Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / sound / soc / samsung / s3c24xx-i2s.c
1 /*
2  * s3c24xx-i2s.c  --  ALSA Soc Audio Layer
3  *
4  * (c) 2006 Wolfson Microelectronics PLC.
5  * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
6  *
7  * Copyright 2004-2005 Simtec Electronics
8  *      http://armlinux.simtec.co.uk/
9  *      Ben Dooks <ben@simtec.co.uk>
10  *
11  *  This program is free software; you can redistribute  it and/or modify it
12  *  under  the terms of  the GNU General  Public License as published by the
13  *  Free Software Foundation;  either version 2 of the  License, or (at your
14  *  option) any later version.
15  */
16
17 #include <linux/delay.h>
18 #include <linux/clk.h>
19 #include <linux/io.h>
20 #include <linux/gpio.h>
21 #include <linux/module.h>
22
23 #include <sound/soc.h>
24 #include <sound/pcm_params.h>
25
26 #include <mach/dma.h>
27 #include <mach/gpio-samsung.h>
28 #include <plat/gpio-cfg.h>
29 #include "regs-iis.h"
30
31 #include "dma.h"
32 #include "s3c24xx-i2s.h"
33
34 static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_out = {
35         .channel        = DMACH_I2S_OUT,
36         .ch_name        = "tx",
37         .dma_size       = 2,
38 };
39
40 static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_in = {
41         .channel        = DMACH_I2S_IN,
42         .ch_name        = "rx",
43         .dma_size       = 2,
44 };
45
46 struct s3c24xx_i2s_info {
47         void __iomem    *regs;
48         struct clk      *iis_clk;
49         u32             iiscon;
50         u32             iismod;
51         u32             iisfcon;
52         u32             iispsr;
53 };
54 static struct s3c24xx_i2s_info s3c24xx_i2s;
55
56 static void s3c24xx_snd_txctrl(int on)
57 {
58         u32 iisfcon;
59         u32 iiscon;
60         u32 iismod;
61
62         pr_debug("Entered %s\n", __func__);
63
64         iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
65         iiscon  = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
66         iismod  = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
67
68         pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
69
70         if (on) {
71                 iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE;
72                 iiscon  |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN;
73                 iiscon  &= ~S3C2410_IISCON_TXIDLE;
74                 iismod  |= S3C2410_IISMOD_TXMODE;
75
76                 writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
77                 writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
78                 writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
79         } else {
80                 /* note, we have to disable the FIFOs otherwise bad things
81                  * seem to happen when the DMA stops. According to the
82                  * Samsung supplied kernel, this should allow the DMA
83                  * engine and FIFOs to reset. If this isn't allowed, the
84                  * DMA engine will simply freeze randomly.
85                  */
86
87                 iisfcon &= ~S3C2410_IISFCON_TXENABLE;
88                 iisfcon &= ~S3C2410_IISFCON_TXDMA;
89                 iiscon  |=  S3C2410_IISCON_TXIDLE;
90                 iiscon  &= ~S3C2410_IISCON_TXDMAEN;
91                 iismod  &= ~S3C2410_IISMOD_TXMODE;
92
93                 writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
94                 writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
95                 writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
96         }
97
98         pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
99 }
100
101 static void s3c24xx_snd_rxctrl(int on)
102 {
103         u32 iisfcon;
104         u32 iiscon;
105         u32 iismod;
106
107         pr_debug("Entered %s\n", __func__);
108
109         iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
110         iiscon  = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
111         iismod  = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
112
113         pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
114
115         if (on) {
116                 iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE;
117                 iiscon  |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN;
118                 iiscon  &= ~S3C2410_IISCON_RXIDLE;
119                 iismod  |= S3C2410_IISMOD_RXMODE;
120
121                 writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
122                 writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
123                 writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
124         } else {
125                 /* note, we have to disable the FIFOs otherwise bad things
126                  * seem to happen when the DMA stops. According to the
127                  * Samsung supplied kernel, this should allow the DMA
128                  * engine and FIFOs to reset. If this isn't allowed, the
129                  * DMA engine will simply freeze randomly.
130                  */
131
132                 iisfcon &= ~S3C2410_IISFCON_RXENABLE;
133                 iisfcon &= ~S3C2410_IISFCON_RXDMA;
134                 iiscon  |= S3C2410_IISCON_RXIDLE;
135                 iiscon  &= ~S3C2410_IISCON_RXDMAEN;
136                 iismod  &= ~S3C2410_IISMOD_RXMODE;
137
138                 writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
139                 writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
140                 writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
141         }
142
143         pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
144 }
145
146 /*
147  * Wait for the LR signal to allow synchronisation to the L/R clock
148  * from the codec. May only be needed for slave mode.
149  */
150 static int s3c24xx_snd_lrsync(void)
151 {
152         u32 iiscon;
153         int timeout = 50; /* 5ms */
154
155         pr_debug("Entered %s\n", __func__);
156
157         while (1) {
158                 iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
159                 if (iiscon & S3C2410_IISCON_LRINDEX)
160                         break;
161
162                 if (!timeout--)
163                         return -ETIMEDOUT;
164                 udelay(100);
165         }
166
167         return 0;
168 }
169
170 /*
171  * Check whether CPU is the master or slave
172  */
173 static inline int s3c24xx_snd_is_clkmaster(void)
174 {
175         pr_debug("Entered %s\n", __func__);
176
177         return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1;
178 }
179
180 /*
181  * Set S3C24xx I2S DAI format
182  */
183 static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
184                 unsigned int fmt)
185 {
186         u32 iismod;
187
188         pr_debug("Entered %s\n", __func__);
189
190         iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
191         pr_debug("hw_params r: IISMOD: %x \n", iismod);
192
193         switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
194         case SND_SOC_DAIFMT_CBM_CFM:
195                 iismod |= S3C2410_IISMOD_SLAVE;
196                 break;
197         case SND_SOC_DAIFMT_CBS_CFS:
198                 iismod &= ~S3C2410_IISMOD_SLAVE;
199                 break;
200         default:
201                 return -EINVAL;
202         }
203
204         switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
205         case SND_SOC_DAIFMT_LEFT_J:
206                 iismod |= S3C2410_IISMOD_MSB;
207                 break;
208         case SND_SOC_DAIFMT_I2S:
209                 iismod &= ~S3C2410_IISMOD_MSB;
210                 break;
211         default:
212                 return -EINVAL;
213         }
214
215         writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
216         pr_debug("hw_params w: IISMOD: %x \n", iismod);
217         return 0;
218 }
219
220 static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
221                                  struct snd_pcm_hw_params *params,
222                                  struct snd_soc_dai *dai)
223 {
224         struct snd_dmaengine_dai_dma_data *dma_data;
225         u32 iismod;
226
227         pr_debug("Entered %s\n", __func__);
228
229         dma_data = snd_soc_dai_get_dma_data(dai, substream);
230
231         /* Working copies of register */
232         iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
233         pr_debug("hw_params r: IISMOD: %x\n", iismod);
234
235         switch (params_width(params)) {
236         case 8:
237                 iismod &= ~S3C2410_IISMOD_16BIT;
238                 dma_data->addr_width = 1;
239                 break;
240         case 16:
241                 iismod |= S3C2410_IISMOD_16BIT;
242                 dma_data->addr_width = 2;
243                 break;
244         default:
245                 return -EINVAL;
246         }
247
248         writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
249         pr_debug("hw_params w: IISMOD: %x\n", iismod);
250         return 0;
251 }
252
253 static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
254                                struct snd_soc_dai *dai)
255 {
256         int ret = 0;
257
258         pr_debug("Entered %s\n", __func__);
259
260         switch (cmd) {
261         case SNDRV_PCM_TRIGGER_START:
262         case SNDRV_PCM_TRIGGER_RESUME:
263         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
264                 if (!s3c24xx_snd_is_clkmaster()) {
265                         ret = s3c24xx_snd_lrsync();
266                         if (ret)
267                                 goto exit_err;
268                 }
269
270                 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
271                         s3c24xx_snd_rxctrl(1);
272                 else
273                         s3c24xx_snd_txctrl(1);
274
275                 break;
276         case SNDRV_PCM_TRIGGER_STOP:
277         case SNDRV_PCM_TRIGGER_SUSPEND:
278         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
279                 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
280                         s3c24xx_snd_rxctrl(0);
281                 else
282                         s3c24xx_snd_txctrl(0);
283                 break;
284         default:
285                 ret = -EINVAL;
286                 break;
287         }
288
289 exit_err:
290         return ret;
291 }
292
293 /*
294  * Set S3C24xx Clock source
295  */
296 static int s3c24xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
297         int clk_id, unsigned int freq, int dir)
298 {
299         u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
300
301         pr_debug("Entered %s\n", __func__);
302
303         iismod &= ~S3C2440_IISMOD_MPLL;
304
305         switch (clk_id) {
306         case S3C24XX_CLKSRC_PCLK:
307                 break;
308         case S3C24XX_CLKSRC_MPLL:
309                 iismod |= S3C2440_IISMOD_MPLL;
310                 break;
311         default:
312                 return -EINVAL;
313         }
314
315         writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
316         return 0;
317 }
318
319 /*
320  * Set S3C24xx Clock dividers
321  */
322 static int s3c24xx_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
323         int div_id, int div)
324 {
325         u32 reg;
326
327         pr_debug("Entered %s\n", __func__);
328
329         switch (div_id) {
330         case S3C24XX_DIV_BCLK:
331                 reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK;
332                 writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
333                 break;
334         case S3C24XX_DIV_MCLK:
335                 reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
336                 writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
337                 break;
338         case S3C24XX_DIV_PRESCALER:
339                 writel(div, s3c24xx_i2s.regs + S3C2410_IISPSR);
340                 reg = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
341                 writel(reg | S3C2410_IISCON_PSCEN, s3c24xx_i2s.regs + S3C2410_IISCON);
342                 break;
343         default:
344                 return -EINVAL;
345         }
346
347         return 0;
348 }
349
350 /*
351  * To avoid duplicating clock code, allow machine driver to
352  * get the clockrate from here.
353  */
354 u32 s3c24xx_i2s_get_clockrate(void)
355 {
356         return clk_get_rate(s3c24xx_i2s.iis_clk);
357 }
358 EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate);
359
360 static int s3c24xx_i2s_probe(struct snd_soc_dai *dai)
361 {
362         pr_debug("Entered %s\n", __func__);
363
364         samsung_asoc_init_dma_data(dai, &s3c24xx_i2s_pcm_stereo_out,
365                 &s3c24xx_i2s_pcm_stereo_in);
366
367         s3c24xx_i2s.iis_clk = devm_clk_get(dai->dev, "iis");
368         if (IS_ERR(s3c24xx_i2s.iis_clk)) {
369                 pr_err("failed to get iis_clock\n");
370                 return PTR_ERR(s3c24xx_i2s.iis_clk);
371         }
372         clk_prepare_enable(s3c24xx_i2s.iis_clk);
373
374         /* Configure the I2S pins (GPE0...GPE4) in correct mode */
375         s3c_gpio_cfgall_range(S3C2410_GPE(0), 5, S3C_GPIO_SFN(2),
376                               S3C_GPIO_PULL_NONE);
377
378         writel(S3C2410_IISCON_IISEN, s3c24xx_i2s.regs + S3C2410_IISCON);
379
380         s3c24xx_snd_txctrl(0);
381         s3c24xx_snd_rxctrl(0);
382
383         return 0;
384 }
385
386 #ifdef CONFIG_PM
387 static int s3c24xx_i2s_suspend(struct snd_soc_dai *cpu_dai)
388 {
389         pr_debug("Entered %s\n", __func__);
390
391         s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
392         s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
393         s3c24xx_i2s.iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
394         s3c24xx_i2s.iispsr = readl(s3c24xx_i2s.regs + S3C2410_IISPSR);
395
396         clk_disable_unprepare(s3c24xx_i2s.iis_clk);
397
398         return 0;
399 }
400
401 static int s3c24xx_i2s_resume(struct snd_soc_dai *cpu_dai)
402 {
403         pr_debug("Entered %s\n", __func__);
404         clk_prepare_enable(s3c24xx_i2s.iis_clk);
405
406         writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
407         writel(s3c24xx_i2s.iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
408         writel(s3c24xx_i2s.iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
409         writel(s3c24xx_i2s.iispsr, s3c24xx_i2s.regs + S3C2410_IISPSR);
410
411         return 0;
412 }
413 #else
414 #define s3c24xx_i2s_suspend NULL
415 #define s3c24xx_i2s_resume NULL
416 #endif
417
418
419 #define S3C24XX_I2S_RATES \
420         (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
421         SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
422         SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
423
424 static const struct snd_soc_dai_ops s3c24xx_i2s_dai_ops = {
425         .trigger        = s3c24xx_i2s_trigger,
426         .hw_params      = s3c24xx_i2s_hw_params,
427         .set_fmt        = s3c24xx_i2s_set_fmt,
428         .set_clkdiv     = s3c24xx_i2s_set_clkdiv,
429         .set_sysclk     = s3c24xx_i2s_set_sysclk,
430 };
431
432 static struct snd_soc_dai_driver s3c24xx_i2s_dai = {
433         .probe = s3c24xx_i2s_probe,
434         .suspend = s3c24xx_i2s_suspend,
435         .resume = s3c24xx_i2s_resume,
436         .playback = {
437                 .channels_min = 2,
438                 .channels_max = 2,
439                 .rates = S3C24XX_I2S_RATES,
440                 .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
441         .capture = {
442                 .channels_min = 2,
443                 .channels_max = 2,
444                 .rates = S3C24XX_I2S_RATES,
445                 .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
446         .ops = &s3c24xx_i2s_dai_ops,
447 };
448
449 static const struct snd_soc_component_driver s3c24xx_i2s_component = {
450         .name           = "s3c24xx-i2s",
451 };
452
453 static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
454 {
455         int ret = 0;
456         struct resource *res;
457
458         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
459         if (!res) {
460                 dev_err(&pdev->dev, "Can't get IO resource.\n");
461                 return -ENOENT;
462         }
463         s3c24xx_i2s.regs = devm_ioremap_resource(&pdev->dev, res);
464         if (IS_ERR(s3c24xx_i2s.regs))
465                 return PTR_ERR(s3c24xx_i2s.regs);
466
467         s3c24xx_i2s_pcm_stereo_out.dma_addr = res->start + S3C2410_IISFIFO;
468         s3c24xx_i2s_pcm_stereo_in.dma_addr = res->start + S3C2410_IISFIFO;
469
470         ret = devm_snd_soc_register_component(&pdev->dev,
471                         &s3c24xx_i2s_component, &s3c24xx_i2s_dai, 1);
472         if (ret) {
473                 pr_err("failed to register the dai\n");
474                 return ret;
475         }
476
477         ret = samsung_asoc_dma_platform_register(&pdev->dev);
478         if (ret)
479                 pr_err("failed to register the dma: %d\n", ret);
480
481         return ret;
482 }
483
484 static struct platform_driver s3c24xx_iis_driver = {
485         .probe  = s3c24xx_iis_dev_probe,
486         .driver = {
487                 .name = "s3c24xx-iis",
488         },
489 };
490
491 module_platform_driver(s3c24xx_iis_driver);
492
493 /* Module information */
494 MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
495 MODULE_DESCRIPTION("s3c24xx I2S SoC Interface");
496 MODULE_LICENSE("GPL");
497 MODULE_ALIAS("platform:s3c24xx-iis");