Version:  2.0.40 2.2.26 2.4.37 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17 3.18

Linux/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 (s3c24xx_i2s.regs == NULL)
465                 return -ENXIO;
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                 .owner = THIS_MODULE,
489         },
490 };
491 
492 module_platform_driver(s3c24xx_iis_driver);
493 
494 /* Module information */
495 MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
496 MODULE_DESCRIPTION("s3c24xx I2S SoC Interface");
497 MODULE_LICENSE("GPL");
498 MODULE_ALIAS("platform:s3c24xx-iis");
499 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us