Version:  2.0.40 2.2.26 2.4.37 3.13 3.14 3.15 3.16 3.17 3.18 3.19 4.0 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 4.10

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

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