Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=c1422a6658ef6101fc5a979021487c732cb177a1
Commit:     c1422a6658ef6101fc5a979021487c732cb177a1
Parent:     129a84de2347002f09721cda3155ccfd19fade40
Author:     Ben Dooks <[EMAIL PROTECTED]>
AuthorDate: Wed Feb 14 13:17:49 2007 +0100
Committer:  Jaroslav Kysela <[EMAIL PROTECTED]>
CommitDate: Fri May 11 16:55:35 2007 +0200

    [ALSA] ASoC Samsung S3C24xx I2S support
    
    This patch by Ben Dooks from Simtec Electronics adds ASoC I2S support
    for the Samsung S3C24xx CPU.
    
    Signed-off-by: Ben Dooks <[EMAIL PROTECTED]>
    Signed-off-by: Graeme Gregory <[EMAIL PROTECTED]>
    Signed-off-by: Liam Girdwood <[EMAIL PROTECTED]>
    Signed-off-by: Takashi Iwai <[EMAIL PROTECTED]>
    Signed-off-by: Jaroslav Kysela <[EMAIL PROTECTED]>
---
 sound/soc/s3c24xx/s3c24xx-i2s.c |  439 +++++++++++++++++++++++++++++++++++++++
 sound/soc/s3c24xx/s3c24xx-i2s.h |   35 +++
 2 files changed, 474 insertions(+), 0 deletions(-)

diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c
new file mode 100644
index 0000000..df655a5
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.c
@@ -0,0 +1,439 @@
+/*
+ * s3c24xx-i2s.c  --  ALSA Soc Audio Layer
+ *
+ * (c) 2006 Wolfson Microelectronics PLC.
+ * Graeme Gregory [EMAIL PROTECTED] or [EMAIL PROTECTED]
+ *
+ * (c) 2004-2005 Simtec Electronics
+ *     http://armlinux.simtec.co.uk/
+ *     Ben Dooks <[EMAIL PROTECTED]>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *
+ *  Revision history
+ *    11th Dec 2006   Merged with Simtec driver
+ *    10th Nov 2006   Initial version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/arch/regs-iis.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/regs-clock.h>
+#include <asm/arch/audio.h>
+#include <asm/dma.h>
+#include <asm/arch/dma.h>
+
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-i2s.h"
+
+#define S3C24XX_I2S_DEBUG 0
+#if S3C24XX_I2S_DEBUG
+#define DBG(x...) printk(KERN_DEBUG x)
+#else
+#define DBG(x...)
+#endif
+
+static struct s3c2410_dma_client s3c24xx_dma_client_out = {
+       .name = "I2S PCM Stereo out"
+};
+
+static struct s3c2410_dma_client s3c24xx_dma_client_in = {
+       .name = "I2S PCM Stereo in"
+};
+
+static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_out = {
+       .client         = &s3c24xx_dma_client_out,
+       .channel        = DMACH_I2S_OUT,
+       .dma_addr       = S3C2410_PA_IIS + S3C2410_IISFIFO
+};
+
+static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_in = {
+       .client         = &s3c24xx_dma_client_in,
+       .channel        = DMACH_I2S_IN,
+       .dma_addr       = S3C2410_PA_IIS + S3C2410_IISFIFO
+};
+
+struct s3c24xx_i2s_info {
+       void __iomem    *regs;
+       struct clk      *iis_clk;
+};
+static struct s3c24xx_i2s_info s3c24xx_i2s;
+
+static void s3c24xx_snd_txctrl(int on)
+{
+       u32 iisfcon;
+       u32 iiscon;
+       u32 iismod;
+
+       DBG("Entered %s\n", __FUNCTION__);
+
+       iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
+       iiscon  = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
+       iismod  = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
+
+       DBG("r: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, 
iisfcon);
+
+       if (on) {
+               iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE;
+               iiscon  |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN;
+               iiscon  &= ~S3C2410_IISCON_TXIDLE;
+               iismod  |= S3C2410_IISMOD_TXMODE;
+
+               writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
+               writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
+               writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
+       } else {
+               /* note, we have to disable the FIFOs otherwise bad things
+                * seem to happen when the DMA stops. According to the
+                * Samsung supplied kernel, this should allow the DMA
+                * engine and FIFOs to reset. If this isn't allowed, the
+                * DMA engine will simply freeze randomly.
+                */
+
+               iisfcon &= ~S3C2410_IISFCON_TXENABLE;
+               iisfcon &= ~S3C2410_IISFCON_TXDMA;
+               iiscon  |=  S3C2410_IISCON_TXIDLE;
+               iiscon  &= ~S3C2410_IISCON_TXDMAEN;
+               iismod  &= ~S3C2410_IISMOD_TXMODE;
+
+               writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
+               writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
+               writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
+       }
+
+       DBG("w: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, 
iisfcon);
+}
+
+static void s3c24xx_snd_rxctrl(int on)
+{
+       u32 iisfcon;
+       u32 iiscon;
+       u32 iismod;
+
+       DBG("Entered %s\n", __FUNCTION__);
+
+       iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
+       iiscon  = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
+       iismod  = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
+
+       DBG("r: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, 
iisfcon);
+
+       if (on) {
+               iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE;
+               iiscon  |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN;
+               iiscon  &= ~S3C2410_IISCON_RXIDLE;
+               iismod  |= S3C2410_IISMOD_RXMODE;
+
+               writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
+               writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
+               writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
+       } else {
+               /* note, we have to disable the FIFOs otherwise bad things
+                * seem to happen when the DMA stops. According to the
+                * Samsung supplied kernel, this should allow the DMA
+                * engine and FIFOs to reset. If this isn't allowed, the
+                * DMA engine will simply freeze randomly.
+                */
+
+        iisfcon &= ~S3C2410_IISFCON_RXENABLE;
+        iisfcon &= ~S3C2410_IISFCON_RXDMA;
+        iiscon  |= S3C2410_IISCON_RXIDLE;
+        iiscon  &= ~S3C2410_IISCON_RXDMAEN;
+               iismod  &= ~S3C2410_IISMOD_RXMODE;
+
+               writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
+               writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
+               writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
+       }
+
+       DBG("w: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, 
iisfcon);
+}
+
+/*
+ * Wait for the LR signal to allow synchronisation to the L/R clock
+ * from the codec. May only be needed for slave mode.
+ */
+static int s3c24xx_snd_lrsync(void)
+{
+       u32 iiscon;
+       unsigned long timeout = jiffies + msecs_to_jiffies(5);
+
+       DBG("Entered %s\n", __FUNCTION__);
+
+       while (1) {
+               iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
+               if (iiscon & S3C2410_IISCON_LRINDEX)
+                       break;
+
+               if (timeout < jiffies)
+                       return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+/*
+ * Check whether CPU is the master or slave
+ */
+static inline int s3c24xx_snd_is_clkmaster(void)
+{
+       DBG("Entered %s\n", __FUNCTION__);
+
+       return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & 
S3C2410_IISMOD_SLAVE) ? 0:1;
+}
+
+/*
+ * Set S3C24xx I2S DAI format
+ */
+static int s3c24xx_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai,
+               unsigned int fmt)
+{
+       u32 iismod;
+
+       DBG("Entered %s\n", __FUNCTION__);
+
+       iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
+       DBG("hw_params r: IISMOD: %lx \n", iismod);
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               iismod |= S3C2410_IISMOD_SLAVE;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_LEFT_J:
+               iismod |= S3C2410_IISMOD_MSB;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
+       DBG("hw_params w: IISMOD: %lx \n", iismod);
+       return 0;
+}
+
+static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       u32 iismod;
+
+       DBG("Entered %s\n", __FUNCTION__);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               rtd->dai->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_out;
+       else
+               rtd->dai->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_in;
+
+       /* Working copies of register */
+       iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
+       DBG("hw_params r: IISMOD: %lx\n", iismod);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S8:
+               break;
+       case SNDRV_PCM_FORMAT_S16_LE:
+               iismod |= S3C2410_IISMOD_16BIT;
+               break;
+       }
+
+       writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
+       DBG("hw_params w: IISMOD: %lx\n", iismod);
+       return 0;
+}
+
+static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       int ret = 0;
+
+       DBG("Entered %s\n", __FUNCTION__);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (!s3c24xx_snd_is_clkmaster()) {
+                       ret = s3c24xx_snd_lrsync();
+                       if (ret)
+                               goto exit_err;
+               }
+
+               if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+                       s3c24xx_snd_rxctrl(1);
+               else
+                       s3c24xx_snd_txctrl(1);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+                       s3c24xx_snd_rxctrl(0);
+               else
+                       s3c24xx_snd_txctrl(0);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+exit_err:
+       return ret;
+}
+
+/*
+ * Set S3C24xx Clock source
+ */
+static int s3c24xx_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+       int clk_id, unsigned int freq, int dir)
+{
+       u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
+
+       DBG("Entered %s\n", __FUNCTION__);
+
+       iismod &= ~S3C2440_IISMOD_MPLL;
+
+       switch (clk_id) {
+       case S3C24XX_CLKSRC_PCLK:
+               break;
+       case S3C24XX_CLKSRC_MPLL:
+               iismod |= S3C2440_IISMOD_MPLL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
+       return 0;
+}
+
+/*
+ * Set S3C24xx Clock dividers
+ */
+static int s3c24xx_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
+       int div_id, int div)
+{
+       u32 reg;
+
+       DBG("Entered %s\n", __FUNCTION__);
+
+       switch (div_id) {
+       case S3C24XX_DIV_MCLK:
+               reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & 
~S3C2410_IISMOD_FS_MASK;
+               writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
+               break;
+       case S3C24XX_DIV_BCLK:
+               reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & 
~(S3C2410_IISMOD_384FS);
+               writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
+               break;
+       case S3C24XX_DIV_PRESCALER:
+               writel(div, s3c24xx_i2s.regs + S3C2410_IISPSR);
+               reg = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
+               writel(reg | S3C2410_IISCON_PSCEN, s3c24xx_i2s.regs + 
S3C2410_IISCON);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * To avoid duplicating clock code, allow machine driver to
+ * get the clockrate from here.
+ */
+u32 s3c24xx_i2s_get_clockrate(void)
+{
+       return clk_get_rate(s3c24xx_i2s.iis_clk);
+}
+EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate);
+
+static int s3c24xx_i2s_probe(struct platform_device *pdev)
+{
+       DBG("Entered %s\n", __FUNCTION__);
+
+       s3c24xx_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100);
+       if (s3c24xx_i2s.regs == NULL)
+               return -ENXIO;
+
+       s3c24xx_i2s.iis_clk=clk_get(&pdev->dev, "iis");
+       if (s3c24xx_i2s.iis_clk == NULL) {
+               DBG("failed to get iis_clock\n");
+               return -ENODEV;
+       }
+       clk_enable(s3c24xx_i2s.iis_clk);
+
+       /* Configure the I2S pins in correct mode */
+       s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK);
+       s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_I2SSCLK);
+       s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_CDCLK);
+       s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI);
+       s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO);
+
+       writel(S3C2410_IISCON_IISEN, s3c24xx_i2s.regs + S3C2410_IISCON);
+
+       s3c24xx_snd_txctrl(0);
+       s3c24xx_snd_rxctrl(0);
+
+       return 0;
+}
+
+#define S3C24XX_I2S_RATES \
+       (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
+       SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+       SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+struct snd_soc_cpu_dai s3c24xx_i2s_dai = {
+       .name = "s3c24xx-i2s",
+       .id = 0,
+       .type = SND_SOC_DAI_I2S,
+       .probe = s3c24xx_i2s_probe,
+       .playback = {
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = S3C24XX_I2S_RATES,
+               .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
+       .capture = {
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = S3C24XX_I2S_RATES,
+               .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
+       .ops = {
+               .trigger = s3c24xx_i2s_trigger,
+               .hw_params = s3c24xx_i2s_hw_params,},
+       .dai_ops = {
+               .set_fmt = s3c24xx_i2s_set_fmt,
+               .set_clkdiv = s3c24xx_i2s_set_clkdiv,
+               .set_sysclk = s3c24xx_i2s_set_sysclk,
+       },
+};
+EXPORT_SYMBOL_GPL(s3c24xx_i2s_dai);
+
+/* Module information */
+MODULE_AUTHOR("Ben Dooks, <[EMAIL PROTECTED]>");
+MODULE_DESCRIPTION("s3c24xx I2S SoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.h b/sound/soc/s3c24xx/s3c24xx-i2s.h
new file mode 100644
index 0000000..f9ca04e
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.h
@@ -0,0 +1,35 @@
+/*
+ * s3c24xx-i2s.c  --  ALSA Soc Audio Layer
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         [EMAIL PROTECTED] or [EMAIL PROTECTED]
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  Revision history
+ *    10th Nov 2006   Initial version.
+ */
+
+#ifndef S3C24XXI2S_H_
+#define S3C24XXI2S_H_
+
+/* clock sources */
+#define S3C24XX_CLKSRC_PCLK 0
+#define S3C24XX_CLKSRC_MPLL 1
+
+/* Clock dividers */
+#define S3C24XX_DIV_MCLK       0
+#define S3C24XX_DIV_BCLK       1
+#define S3C24XX_DIV_PRESCALER  2
+
+/* prescaler */
+#define S3C24XX_PRESCALE(a,b) \
+       (((a - 1) << S3C2410_IISPSR_INTSHIFT) | ((b - 1) << 
S3C2410_IISPSR_EXTSHFIT))
+
+u32 s3c24xx_i2s_get_clockrate(void);
+
+#endif /*S3C24XXI2S_H_*/
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to