add i2s codec pcm driver
author林辉辉 <lhh@rock-chips.com>
Fri, 28 May 2010 14:53:30 +0000 (14:53 +0000)
committer黄涛 <huangtao@rock-chips.com>
Mon, 21 Jun 2010 05:35:19 +0000 (13:35 +0800)
sound/soc/rk2818/Kconfig [new file with mode: 0755]
sound/soc/rk2818/Makefile [new file with mode: 0755]
sound/soc/rk2818/rk2818_i2s.c [new file with mode: 0755]
sound/soc/rk2818/rk2818_i2s.h [new file with mode: 0755]
sound/soc/rk2818/rk2818_pcm.c [new file with mode: 0755]
sound/soc/rk2818/rk2818_pcm.h [new file with mode: 0755]
sound/soc/rk2818/rk2818_wm8988.c [new file with mode: 0755]

diff --git a/sound/soc/rk2818/Kconfig b/sound/soc/rk2818/Kconfig
new file mode 100755 (executable)
index 0000000..5e5c5da
--- /dev/null
@@ -0,0 +1,48 @@
+config SND_ROCKCHIP_SOC
+       tristate "SoC Audio for the rockchip RK2818 System-on-Chip"
+       depends on ARCH_RK2818 && SND_SOC
+       help
+         Say Y or M if you want to add support for codecs attached to
+         the ROCKCHIP IIS interface. You will also need
+         to select the audio interfaces to support below.
+
+config SND_ROCKCHIP_SOC_I2S
+       tristate 
+
+config SND_ROCKCHIP_SOC_WM8988
+       tristate "SoC I2S Audio support for rockchip - WM8988"
+       depends on SND_ROCKCHIP_SOC 
+       select SND_ROCKCHIP_SOC_I2S
+       select SND_SOC_WM8988
+       help
+         Say Y if you want to add support for SoC audio on rockchip
+         with the WM8988.
+
+config SND_ROCKCHIP_SOC_WM8994
+       tristate "SoC I2S Audio support for rockchip - WM8994"
+       depends on SND_ROCKCHIP_SOC 
+       select SND_ROCKCHIP_SOC_I2S
+       select SND_SOC_WM8994
+       help
+         Say Y if you want to add support for SoC audio on rockchip
+         with the WM8994.
+         
+config SND_ROCKCHIP_SOC_RK1000
+       tristate "SoC I2S Audio support for rockchip - RK1000"
+       depends on SND_ROCKCHIP_SOC 
+       select SND_ROCKCHIP_SOC_I2S
+       select SND_SOC_RK1000
+       help
+         Say Y if you want to add support for SoC audio on rockchip
+         with the RK1000.       
+          
+if SND_ROCKCHIP_SOC_WM8988 || SND_ROCKCHIP_SOC_RK1000 || SND_ROCKCHIP_SOC_WM8994
+choice
+  prompt "set i2s type"
+       config SND_ROCKCHIP_SOC_SLAVE
+               tristate "I2s run in slave Mode codec run in Master"
+
+       config  SND_ROCKCHIP_SOC_MASTER
+               tristate  "I2s run in master Mode codec run in Master"
+endchoice
+endif
diff --git a/sound/soc/rk2818/Makefile b/sound/soc/rk2818/Makefile
new file mode 100755 (executable)
index 0000000..5aa86f7
--- /dev/null
@@ -0,0 +1,15 @@
+# ROCKCHIP Platform Support
+snd-soc-rockchip-objs := rk2818_pcm.o
+snd-soc-rockchip-i2s-objs := rk2818_i2s.o
+
+obj-$(CONFIG_SND_ROCKCHIP_SOC) += snd-soc-rockchip.o
+obj-$(CONFIG_SND_ROCKCHIP_SOC_I2S) += snd-soc-rockchip-i2s.o
+
+# ROCKCHIP Machine Support
+snd-soc-wm8988-objs := rk2818_wm8988.o
+snd-soc-rk1000-objs := rk2818_rk1000codec.o
+snd-soc-wm8994-objs := rk2818_wm8994.o
+
+obj-$(CONFIG_SND_ROCKCHIP_SOC_WM8994) += snd-soc-wm8994.o
+obj-$(CONFIG_SND_ROCKCHIP_SOC_WM8988) += snd-soc-wm8988.o
+obj-$(CONFIG_SND_ROCKCHIP_SOC_RK1000) += snd-soc-rk1000.o
\ No newline at end of file
diff --git a/sound/soc/rk2818/rk2818_i2s.c b/sound/soc/rk2818/rk2818_i2s.c
new file mode 100755 (executable)
index 0000000..cba0e83
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+ * rk2818_i2s.c  --  ALSA SoC ROCKCHIP IIS Audio Layer Platform driver
+ *
+ * Driver for rockchip iis audio
+ *  Copyright (C) 2009 lhh
+ *
+ *
+ *  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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include <asm/dma.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <asm/io.h>
+#include <mach/hardware.h>
+#include <mach/dma.h>
+#include <mach/rk2818_iomap.h>
+#include <mach/iomux.h>
+#include <mach/scu.h> 
+
+#include "rk2818_pcm.h"
+#include "rk2818_i2s.h"
+
+
+#if 0
+#define DBG(x...) printk(KERN_INFO x)
+#else
+#define DBG(x...) do { } while (0)
+#endif
+
+#define pheadi2s  ((pI2S_REG)(i2s->regs))
+
+struct rockchip_i2s_info {
+       struct device   *dev;
+       void __iomem    *regs;
+       struct clk      *iis_clk;
+       struct clk      *iis_pclk;
+
+       u32              suspend_iismod;
+       u32              suspend_iiscon;
+       u32              suspend_iispsr;
+};
+
+static struct rockchip_i2s_info rockchip_i2s;
+
+static struct rockchip_dma_client rockchip_dma_client_out = {
+       //.name         = "I2S PCM Stereo out"
+       .name = "i2s"
+};
+
+static struct rockchip_dma_client rockchip_dma_client_in = {
+       //.name         = "I2S PCM Stereo in"
+       .name = "i2s"
+};
+
+static struct rockchip_pcm_dma_params rockchip_i2s_pcm_stereo_out = {
+       .client         = &rockchip_dma_client_out,
+       .channel        = RK28_DMA_CH2, ///0,  //DMACH_I2S_OUT,
+       .dma_addr       = RK2818_I2S_PHYS + I2S_TXR_BUFF,
+       .dma_size       = 4,
+};
+
+static struct rockchip_pcm_dma_params rockchip_i2s_pcm_stereo_in = {
+       .client         = &rockchip_dma_client_in,
+       .channel        = RK28_DMA_CH2,  ///1,  //DMACH_I2S_IN,
+       .dma_addr       = RK2818_I2S_PHYS + I2S_RXR_BUFF,
+       .dma_size       = 4,
+};
+
+
+
+
+/* 
+ *Turn on or off the transmission path. 
+ */
+static void rockchip_snd_txctrl(int on)
+{
+    struct rockchip_i2s_info *i2s = &rockchip_i2s;       
+         u32 opr,fifosts;
+    
+    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+    
+    opr = readl(&(pheadi2s->I2S_OPR));
+    fifosts = readl(&(pheadi2s->I2S_FIFOSTS));
+    fifosts = (fifosts & (~(0x0f<<16))) | TX_HALF_FULL | RX_HALF_FULL;
+    writel(fifosts, &(pheadi2s->I2S_FIFOSTS));
+    if (on) 
+    {
+        opr = (opr & (~(RESET_RX | I2S_DMA_REQ2_DISABLE | TX_START | RX_START))) | (RESET_TX | I2S_DMA_REQ1_DISABLE);
+        writel(opr, &(pheadi2s->I2S_OPR));
+        udelay(5);
+        opr = (opr & (~(I2S_DMA_REQ1_DISABLE | I2S_DMA_REQ1_RX_ENABLE | RX_START))) | I2S_DMA_REQ1_ENABLE | I2S_DMA_REQ1_TX_ENABLE | TX_START;
+        writel(opr, &(pheadi2s->I2S_OPR));
+    }
+    else
+    {  
+        opr = (opr & (~TX_START)) | I2S_DMA_REQ1_DISABLE;
+        writel(opr, &(pheadi2s->I2S_OPR));
+    }
+}
+static void rockchip_snd_rxctrl(int on)
+{
+    struct rockchip_i2s_info *i2s = &rockchip_i2s;
+       u32 opr,fifosts;
+         
+    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+    opr = readl(&(pheadi2s->I2S_OPR));
+    fifosts = readl(&(pheadi2s->I2S_FIFOSTS));
+    fifosts = (fifosts & (~(0x0f<<16))) | TX_HALF_FULL | RX_HALF_FULL;
+    writel(fifosts, &(pheadi2s->I2S_FIFOSTS));
+    if (on) 
+    {
+        opr = (opr & (~(RESET_TX | I2S_DMA_REQ1_DISABLE| TX_START | RX_START))) | (RESET_RX | I2S_DMA_REQ2_DISABLE);
+        writel(opr, &(pheadi2s->I2S_OPR));
+        udelay(5);
+        opr = (opr & (~(I2S_DMA_REQ2_DISABLE | I2S_DMA_REQ2_TX_ENABLE | TX_START))) | I2S_DMA_REQ2_ENABLE | I2S_DMA_REQ2_RX_ENABLE | RX_START;
+        writel(opr, &(pheadi2s->I2S_OPR));
+    }
+    else
+    {
+        opr = (opr & (~RX_START)) | I2S_DMA_REQ2_DISABLE;
+        writel(opr, &(pheadi2s->I2S_OPR));
+    }   
+}
+
+/*
+ * Set Rockchip I2S DAI format
+ */
+static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
+               unsigned int fmt)
+{
+    struct rockchip_i2s_info *i2s = &rockchip_i2s;     
+    u32 tx_ctl,rx_ctl;
+    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+    
+    tx_ctl = readl(&(pheadi2s->I2S_TXCTL));
+    tx_ctl &= (~MASTER_MODE);
+    
+    switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+         case SND_SOC_DAIFMT_CBM_CFM:          
+               tx_ctl |= MASTER_MODE;  
+               break;
+         case SND_SOC_DAIFMT_CBS_CFS:
+               tx_ctl |= SLAVE_MODE;  
+               break;
+         default:
+               DBG("unknwon master/slave format\n");
+               return -EINVAL;
+         }
+    tx_ctl &= ~IISMOD_SDF_MASK;
+    
+    switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+    case SND_SOC_DAIFMT_RIGHT_J:
+       tx_ctl |= RIGHT_JUSTIFIED;
+       break;
+    case SND_SOC_DAIFMT_LEFT_J:
+       tx_ctl |= LEFT_JUSTIFIED;
+       break;
+    case SND_SOC_DAIFMT_I2S:
+       tx_ctl |= I2S_MODE;
+       break;
+    default:
+       DBG("Unknown data format\n");
+       return -EINVAL;
+         }
+       tx_ctl = tx_ctl & (~(0xff<<8)) & (~(0x03<<16)) & (~(1<<3));  
+       tx_ctl = tx_ctl | OVERSAMPLING_RATE_64FS | SCK_RATE4 | STEREO_MODE;   
+    writel(tx_ctl, &(pheadi2s->I2S_TXCTL));
+    rx_ctl = tx_ctl | CLEAR_RXFIFO;
+    writel(rx_ctl, &(pheadi2s->I2S_RXCTL));
+    return 0;
+}
+
+static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+    struct snd_soc_pcm_runtime *rtd = substream->private_data;
+    struct rockchip_i2s_info *i2s = &rockchip_i2s;     
+       u32 iismod;
+         
+    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+       /*by Vincent Hsiung for EQ Vol Change*/
+       #define HW_PARAMS_FLAG_EQVOL_ON 0x21
+       #define HW_PARAMS_FLAG_EQVOL_OFF 0x22
+    if ((params->flags == HW_PARAMS_FLAG_EQVOL_ON)||(params->flags == HW_PARAMS_FLAG_EQVOL_OFF))
+       {
+               return 0;
+       }
+           
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               rtd->dai->cpu_dai->dma_data = &rockchip_i2s_pcm_stereo_out;
+       else
+               rtd->dai->cpu_dai->dma_data = &rockchip_i2s_pcm_stereo_in;
+    
+       /* Working copies of register */
+       iismod = readl(&(pheadi2s->I2S_TXCTL));
+    iismod &= (~SAMPLE_DATA_MASK);
+       switch (params_format(params)) {
+         case SNDRV_PCM_FORMAT_S8:
+               iismod |= SAMPLE_DATA_8bit;
+               break;
+         case SNDRV_PCM_FORMAT_S16_LE:
+               iismod |= SAMPLE_DATA_16bit;
+               break;
+         } 
+       /*stereo mode MCLK/SCK=4*/  
+       iismod = iismod & (~(0xff<<8)) & (~(0x03<<16)) & (~(1<<3));
+       iismod = iismod | OVERSAMPLING_RATE_64FS | SCK_RATE4 | STEREO_MODE; 
+         
+       writel(iismod, &(pheadi2s->I2S_TXCTL));
+    iismod = iismod | CLEAR_RXFIFO;
+    writel(iismod, &(pheadi2s->I2S_RXCTL));
+    return 0;
+}
+
+
+static int rockchip_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
+{    
+    int ret = 0;
+
+    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+    
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+    case SNDRV_PCM_TRIGGER_RESUME:
+    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:   
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+               rockchip_snd_rxctrl(1);
+       else
+               rockchip_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)
+               rockchip_snd_rxctrl(0);
+       else
+               rockchip_snd_txctrl(0);
+       break;
+    default:
+       ret = -EINVAL;
+       break;
+    }
+    
+       return ret;
+}
+/*
+ * Set Rockchip Clock source
+ */
+static int rockchip_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
+       int clk_id, unsigned int freq, int dir)
+{
+    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+    /*add scu clk source and enable clk*/
+    
+    return 0;
+}
+
+/*
+ * Set Rockchip Clock dividers
+ */
+static int rockchip_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
+       int div_id, int div)
+{
+    //u32 reg;
+    
+    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+    /*when i2s in master mode ,must set codec pll div*/
+    switch (div_id) {
+    case ROCKCHIP_DIV_BCLK:
+       //reg = readl(&(pheadi2s->I2S_TXCTL)) & ~S3C2410_IISMOD_FS_MASK;
+       //writel(reg | div, &(pheadi2s->I2S_TXCTL));
+       break;
+    case ROCKCHIP_DIV_MCLK:
+       //reg = readl(rockchip_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
+       //writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
+       break;
+    case ROCKCHIP_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 rockchip_i2s_get_clockrate(void)
+{
+    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+       return 0;  ///clk_get_rate(s3c24xx_i2s.iis_clk);
+}
+EXPORT_SYMBOL_GPL(rockchip_i2s_get_clockrate);
+
+static int rockchip_i2s_dai_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
+{      
+       DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+    
+    /* Configure the I2S pins in correct mode */
+    rk2818_mux_api_set(CXGPIO_I2S_SEL_NAME,IOMUXB_I2S_INTERFACE);
+    
+    rockchip_snd_txctrl(0);
+       rockchip_snd_rxctrl(0);
+    
+    return 0;
+}
+
+#ifdef CONFIG_PM
+int rockchip_i2s_suspend(struct snd_soc_dai *cpu_dai)
+{
+    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+    //clk_disable(clk);
+         return 0;
+}
+
+int rockchip_i2s_resume(struct snd_soc_dai *cpu_dai)
+{
+    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+    //clk_enable(clk);
+    return 0;
+}              
+#else
+#define rockchip_i2s_suspend NULL
+#define rockchip_i2s_resume NULL
+#endif
+
+
+#define ROCKCHIP_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)
+
+static struct snd_soc_dai_ops rockchip_i2s_dai_ops = {
+       .trigger = rockchip_i2s_trigger,
+       .hw_params = rockchip_i2s_hw_params,
+       .set_fmt = rockchip_i2s_set_fmt,
+       .set_clkdiv = rockchip_i2s_set_clkdiv,
+       .set_sysclk = rockchip_i2s_set_sysclk,
+};
+
+struct snd_soc_dai rk2818_i2s_dai = {
+       .name = "rk2818_i2s",
+       .id = -1,
+       .probe = rockchip_i2s_dai_probe,
+       .suspend = rockchip_i2s_suspend,
+       .resume = rockchip_i2s_resume,
+       .playback = {
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = ROCKCHIP_I2S_RATES,
+               .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
+       .capture = {
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = ROCKCHIP_I2S_RATES,
+               .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
+       .ops = &rockchip_i2s_dai_ops,
+};
+
+
+EXPORT_SYMBOL_GPL(rk2818_i2s_dai);
+
+static int __devinit rockchip_i2s_probe(struct platform_device *pdev)
+{
+       struct resource         *regs;
+
+    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+
+       rk2818_i2s_dai.dev = &pdev->dev;
+       rockchip_i2s.iis_clk = clk_get(&pdev->dev, "i2s");
+    clk_enable(rockchip_i2s.iis_clk);  
+    regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!regs) {
+               DBG("platform get resource fail\n");
+               return -ENXIO;  
+       }
+       rockchip_i2s.regs = ioremap(regs->start, (regs->end - regs->start) + 1);
+       if (rockchip_i2s.regs == NULL) {
+               DBG("rk2818 i2s ioremap err\n");
+       release_mem_region(regs->start, (regs->end - regs->start) + 1);
+               return -EBUSY;
+       }
+    
+       return snd_soc_register_dai(&rk2818_i2s_dai);
+}
+
+
+static void __devexit rockchip_i2s_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_dai(&rk2818_i2s_dai);
+}
+
+static struct platform_driver rockchip_i2s_driver = {
+       .probe  = rockchip_i2s_probe,
+       .remove = __devexit_p(rockchip_i2s_remove),
+       .driver = {
+               .name   = "rk2818_i2s",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init rockchip_i2s_init(void)
+{
+       return  platform_driver_register(&rockchip_i2s_driver);
+}
+module_init(rockchip_i2s_init);
+
+static void __exit rockchip_i2s_exit(void)
+{
+       platform_driver_unregister(&rockchip_i2s_driver);
+}
+module_exit(rockchip_i2s_exit);
+
+/* Module information */
+MODULE_AUTHOR("lhh lhh@rock-chips.com");
+MODULE_DESCRIPTION("ROCKCHIP IIS ASoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/rk2818/rk2818_i2s.h b/sound/soc/rk2818/rk2818_i2s.h
new file mode 100755 (executable)
index 0000000..619bfda
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * rockchip-iis.h - ALSA IIS interface for the Rockchip rk28 SoC
+ *
+ * Driver for rockchip iis audio
+ *  Copyright (C) 2009 lhh
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _ROCKCHIP_IIS_H
+#define _ROCKCHIP_IIS_H
+
+//I2S_OPR
+#define RESET_TX                (1<<17)
+#define RESET_RX                (1<<16)
+#define I2S_DMA_REQ1_DISABLE    (1<<6)
+#define I2S_DMA_REQ1_ENABLE     (0)
+#define I2S_DMA_REQ2_DISABLE    (1<<5)
+#define I2S_DMA_REQ2_ENABLE     (0)
+#define I2S_DMA_REQ1_TX_ENABLE  (0)
+#define I2S_DMA_REQ1_RX_ENABLE  (1<<4)
+#define I2S_DMA_REQ2_TX_ENABLE  (0)
+#define I2S_DMA_REQ2_RX_ENABLE  (1<<3)
+#define TX_START                (1<<1)
+#define RX_START                (1)
+
+
+
+//I2S_TXCTL I2S_RXCTL
+#define CLEAR_RXFIFO            (1<<24)
+#define TRAN_DEVICES0           (0)
+#define TRAN_DEVICES1           (1<<18)
+#define TRAN_DEVICES2           (2<<18)
+#define TRAN_DEVICES3           (3<<18)
+#define OVERSAMPLING_RATE_32FS  (0)
+#define OVERSAMPLING_RATE_64FS  (1<<16)
+#define OVERSAMPLING_RATE_128FS (2<<16)
+#define SCK_RATE2               (0x02<<8)
+#define SCK_RATE4               (0x04<<8)
+#define SCK_RATE8               (0x08<<8)
+#define SAMPLE_DATA_8bit        (0)
+#define SAMPLE_DATA_16bit       (1<<4)
+#define SAMPLE_DATA_MASK        (3<<4)
+#define MONO_MODE               (1<<3)
+#define STEREO_MODE             (0)
+#define I2S_MODE                (0)
+#define LEFT_JUSTIFIED          (1<<1)
+#define RIGHT_JUSTIFIED         (2<<1)
+#define IISMOD_SDF_MASK         (3<<1)
+#define MASTER_MODE             (1)
+#define SLAVE_MODE              (0)
+
+//I2S_FIFOSTS
+#define TX_HALF_FULL            (1<<18)
+#define RX_HALF_FULL            (1<<16)
+
+#define I2S_TXR_BUFF            0x04
+#define I2S_RXR_BUFF            0x08
+
+/* Clock dividers */
+#define ROCKCHIP_DIV_MCLK      0
+#define ROCKCHIP_DIV_BCLK      1
+#define ROCKCHIP_DIV_PRESCALER 2
+
+//I2S Registers
+typedef volatile struct tagIIS_STRUCT
+{
+    unsigned int I2S_OPR;
+    unsigned int I2S_TXR;
+    unsigned int I2S_RXR;
+    unsigned int I2S_TXCTL;
+    unsigned int I2S_RXCTL;
+    unsigned int I2S_FIFOSTS;
+    unsigned int I2S_IER;
+    unsigned int I2S_ISR;
+}I2S_REG,*pI2S_REG;
+
+extern struct snd_soc_dai rk2818_i2s_dai;
+//extern void rockchip_add_device_i2s(void);
+#endif /* _ROCKCHIP_IIS_H */
+
diff --git a/sound/soc/rk2818/rk2818_pcm.c b/sound/soc/rk2818/rk2818_pcm.c
new file mode 100755 (executable)
index 0000000..bcdd924
--- /dev/null
@@ -0,0 +1,610 @@
+/*
+ * rk2818_pcm.c  --  ALSA SoC ROCKCHIP PCM Audio Layer Platform driver
+ *
+ * Driver for rockchip pcm audio
+ *  Copyright (C) 2009 lhh
+ *
+ *
+ *  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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/dma.h>
+#include <mach/hardware.h>
+#include <mach/dma.h>
+
+#include "rk2818_pcm.h"
+
+#ifdef CONFIG_ANDROID_POWER
+#include <linux/android_power.h>
+static android_suspend_lock_t audio_lock;
+#endif
+
+#if 0
+#define DBG(x...) printk(KERN_INFO x)
+#else
+#define DBG(x...) do { } while (0)
+#endif
+
+
+static const struct snd_pcm_hardware rockchip_pcm_hardware = {
+       .info                   = SNDRV_PCM_INFO_INTERLEAVED |
+                                   SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                   SNDRV_PCM_INFO_MMAP |
+                                   SNDRV_PCM_INFO_MMAP_VALID |
+                                   SNDRV_PCM_INFO_PAUSE |
+                                   SNDRV_PCM_INFO_RESUME,
+       .formats                = SNDRV_PCM_FMTBIT_S16_LE |
+                                   SNDRV_PCM_FMTBIT_U16_LE |
+                                   SNDRV_PCM_FMTBIT_U8 |
+                                   SNDRV_PCM_FMTBIT_S8,
+       .channels_min           = 1,
+       .channels_max           = 2,
+       .buffer_bytes_max       = 128*1024,
+       .period_bytes_min       = 64,  ///PAGE_SIZE,
+       .period_bytes_max       = 2047*4,///PAGE_SIZE*2,
+       .periods_min            = 3,///2,
+       .periods_max            = 128,
+       .fifo_size              = 16,
+};
+
+
+struct rockchip_dma_buf_set {
+       struct rockchip_dma_buf_set     *next;
+       struct scatterlist sg;
+};
+
+struct rockchip_runtime_data {
+       spinlock_t lock;
+       int state;
+       int transfer_first;
+       unsigned int dma_loaded;
+       unsigned int dma_limit;
+       unsigned int dma_period;
+       dma_addr_t dma_start;
+       dma_addr_t dma_pos;
+       dma_addr_t dma_end;
+       struct rockchip_pcm_dma_params *params;
+       struct rockchip_dma_buf_set     *curr;          /* current dma buffer set */
+       struct rockchip_dma_buf_set     *next;          /* next buffer set to load */
+       struct rockchip_dma_buf_set     *end;           /* end of queue set*/
+};
+
+
+/* rockchip__dma_buf_enqueue
+ *
+ *queue an given buffer for dma transfer set.
+ *data       the physical address of the buffer data
+ *size       the size of the buffer in bytes
+*/
+static int rockchip_dma_buffer_set_enqueue(struct rockchip_runtime_data *prtd, dma_addr_t data, int size)
+{   
+       struct rockchip_dma_buf_set *sg_buf;
+       
+    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+       sg_buf = kzalloc(sizeof(struct rockchip_dma_buf_set), GFP_KERNEL);
+       
+       if (sg_buf == NULL) {
+               DBG("scatter sg buffer allocate failed,no memory!\n");
+               return -ENOMEM;
+       }
+       sg_buf->next = NULL;
+       sg_buf->sg.dma_address = data;
+       sg_buf->sg.length = size/4;  ////4;
+       if( prtd->curr == NULL) {
+               prtd->curr = sg_buf;
+               prtd->end  = sg_buf;
+               prtd->next = NULL;
+       } else {
+               if (prtd->end == NULL)
+                       DBG("prtd->end is NULL\n");
+                       prtd->end->next = sg_buf;
+                       prtd->end = sg_buf;
+       }
+       /* if necessary, update the next buffer field */
+       if (prtd->next == NULL)
+               prtd->next = sg_buf;
+       return 0;
+}
+
+void rockchip_pcm_dma_irq(s32 ch, void *data);
+
+void audio_start_dma(struct snd_pcm_substream *substream, int mode)
+{
+       struct rockchip_runtime_data *prtd;
+       unsigned long flags;
+       struct rockchip_dma_buf_set *sg_buf;
+    
+    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+
+       prtd = substream->runtime->private_data;
+
+       switch (mode) {
+       case DMA_MODE_WRITE:
+               if (prtd->transfer_first == 1) {
+                       prtd->transfer_first = 0;
+               } else {
+                       sg_buf = prtd->curr;
+                       if (sg_buf != NULL) {
+                               prtd->curr = sg_buf->next;
+                               prtd->next = sg_buf->next;
+                               sg_buf->next  = NULL;
+                               kfree(sg_buf);
+                               sg_buf = NULL;
+                       }
+               }
+
+               sg_buf = prtd->next;
+               DBG("Enter::%s----%d---length=%x---dma_address=%x\n",__FUNCTION__,__LINE__,sg_buf->sg.length,sg_buf->sg.dma_address);           
+               if (sg_buf) {                   
+                       spin_lock_irqsave(&prtd->lock, flags);
+                       disable_dma(prtd->params->channel);
+                       //set_dma_sg(prtd->params->channel, &(sg_buf->sg), 1);
+                       set_dma_mode(prtd->params->channel, DMA_MODE_WRITE);
+                       set_dma_handler(prtd->params->channel, rockchip_pcm_dma_irq, substream);
+                       __set_dma_addr(prtd->params->channel, (void *)(sg_buf->sg.dma_address));
+                       set_dma_count(prtd->params->channel, sg_buf->sg.length);
+                       enable_dma(prtd->params->channel);
+                       spin_unlock_irqrestore(&prtd->lock, flags);
+               } else {
+                       DBG("next buffer is NULL for playback\n");
+                       return;
+               }
+               break;
+       case DMA_MODE_READ:
+               if (prtd->transfer_first == 1) {
+                       prtd->transfer_first = 0;
+               } else {
+                       sg_buf = prtd->curr;
+                       if (sg_buf != NULL) {
+                               prtd->curr = sg_buf->next;
+                               prtd->next = sg_buf->next;
+                               sg_buf->next  = NULL;
+                               kfree(sg_buf);
+                               sg_buf = NULL;
+                       }
+               }
+
+               sg_buf = prtd->next;
+               if (sg_buf) {                   
+                       spin_lock_irqsave(&prtd->lock, flags);
+                       disable_dma(prtd->params->channel);
+                       //set_dma_sg(prtd->params->channel, &(sg_buf->sg), 1);
+                       set_dma_mode(prtd->params->channel, DMA_MODE_READ);
+                       set_dma_handler(prtd->params->channel, rockchip_pcm_dma_irq, substream);                        
+                       __set_dma_addr(prtd->params->channel, (void *)(sg_buf->sg.dma_address));
+                       set_dma_count(prtd->params->channel, sg_buf->sg.length);
+                       enable_dma(prtd->params->channel);
+                       spin_unlock_irqrestore(&prtd->lock, flags);
+               } else {
+                       DBG("next buffer is NULL for capture\n");
+                       return;
+               }
+               break;
+       }
+}
+
+/* rockchip_pcm_enqueue
+ *
+ * place a dma buffer onto the queue for the dma system
+ * to handle.
+*/
+static void rockchip_pcm_enqueue(struct snd_pcm_substream *substream)
+{
+       struct rockchip_runtime_data *prtd = substream->runtime->private_data;  
+       dma_addr_t pos = prtd->dma_pos;
+       int ret;
+    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+        
+    while (prtd->dma_loaded < prtd->dma_limit) {
+               unsigned long len = prtd->dma_period;
+               
+        DBG("dma_loaded: %d\n", prtd->dma_loaded);
+               if ((pos + len) > prtd->dma_end) {
+                       len  = prtd->dma_end - pos;
+               }
+               ret = rockchip_dma_buffer_set_enqueue(prtd, pos, len);
+               if (ret == 0) {
+                       prtd->dma_loaded++;
+                       pos += prtd->dma_period;
+                       if (pos >= prtd->dma_end)
+                               pos = prtd->dma_start;
+               } else 
+                       break;
+       }
+
+       prtd->dma_pos = pos;
+}
+
+void rockchip_pcm_dma_irq(s32 ch, void *data)
+{    
+    struct snd_pcm_substream *substream = data;
+       struct rockchip_runtime_data *prtd;
+       unsigned long flags;
+       
+       DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+
+       prtd = substream->runtime->private_data;
+       if (substream)
+               snd_pcm_period_elapsed(substream);
+       spin_lock(&prtd->lock);
+       prtd->dma_loaded--;
+       if (prtd->state & ST_RUNNING) {
+               rockchip_pcm_enqueue(substream);
+       }
+    spin_unlock(&prtd->lock);
+    local_irq_save(flags);
+       if (prtd->state & ST_RUNNING) {
+               if (prtd->dma_loaded) {
+                       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                               audio_start_dma(substream, DMA_MODE_WRITE);
+                       else
+                               audio_start_dma(substream, DMA_MODE_READ);
+               }
+       }
+       local_irq_restore(flags);   
+}
+
+
+static int rockchip_pcm_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct rockchip_runtime_data *prtd = runtime->private_data;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct rockchip_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data;
+       unsigned long totbytes = params_buffer_bytes(params);
+       int ret = 0;
+
+       DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+       /*by Vincent Hsiung for EQ Vol Change*/
+       #define HW_PARAMS_FLAG_EQVOL_ON 0x21
+       #define HW_PARAMS_FLAG_EQVOL_OFF 0x22
+    if ((params->flags == HW_PARAMS_FLAG_EQVOL_ON)||(params->flags == HW_PARAMS_FLAG_EQVOL_OFF))
+       {
+               return 0;
+       }
+
+       /* return if this is a bufferless transfer e.g.
+        * codec <--> BT codec or GSM modem -- lg FIXME */
+       if (!dma)
+               return 0;
+
+       /* this may get called several times by oss emulation
+        * with different params -HW */
+       if (prtd->params == NULL) {
+               /* prepare DMA */
+               prtd->params = dma;
+
+               DBG("params %p, client %p, channel %d\n", prtd->params,
+                       prtd->params->client, prtd->params->channel);
+
+               ret = request_dma(prtd->params->channel, "i2s");  ///prtd->params->client->name);
+               if (ret) {
+                       DBG(KERN_ERR "failed to get dma channel\n");
+                       return ret;
+               }
+       }
+
+
+       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+       runtime->dma_bytes = totbytes;
+
+       spin_lock_irq(&prtd->lock);
+       prtd->dma_loaded = 0;
+       prtd->dma_limit = runtime->hw.periods_min;
+       prtd->dma_period = params_period_bytes(params);
+       prtd->dma_start = runtime->dma_addr;
+       prtd->dma_pos = prtd->dma_start;
+       prtd->dma_end = prtd->dma_start + totbytes;
+       prtd->transfer_first = 1;
+       prtd->curr = NULL;
+       prtd->next = NULL;
+       prtd->end = NULL;
+       spin_unlock_irq(&prtd->lock);
+       return 0;
+}
+
+static int rockchip_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       struct rockchip_runtime_data *prtd = substream->runtime->private_data;
+
+       DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+       /* TODO - do we need to ensure DMA flushed */
+       snd_pcm_set_runtime_buffer(substream, NULL);
+
+       if (prtd->params) {
+               free_dma(prtd->params->channel);
+               prtd->params = NULL;
+       }
+
+       return 0;
+}
+
+static int rockchip_pcm_prepare(struct snd_pcm_substream *substream)
+{
+       struct rockchip_runtime_data *prtd = substream->runtime->private_data;
+       int ret = 0;
+
+       DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+
+       /* return if this is a bufferless transfer e.g.
+        * codec <--> BT codec or GSM modem -- lg FIXME */
+       if (!prtd->params)
+               return 0;
+       prtd->dma_loaded = 0;
+       prtd->dma_pos = prtd->dma_start;
+
+       /* enqueue dma buffers */
+       rockchip_pcm_enqueue(substream);
+       return ret;
+}
+
+static int rockchip_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct rockchip_runtime_data *prtd = substream->runtime->private_data;
+       int ret = 0;
+
+       DBG("Enter::%s----%d---%d\n",__FUNCTION__,__LINE__,cmd);
+
+       spin_lock(&prtd->lock);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               DBG(" START \n");
+           prtd->state |= ST_RUNNING;
+           if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                   audio_start_dma(substream, DMA_MODE_WRITE);
+               } else {
+                   audio_start_dma(substream, DMA_MODE_READ);
+               }
+#ifdef CONFIG_ANDROID_POWER        
+        android_lock_suspend(&audio_lock);
+        DBG("%s::start audio , lock system suspend\n" , __func__ );
+#endif         
+               break;
+       case SNDRV_PCM_TRIGGER_RESUME:
+           DBG(" RESUME \n");
+           break;
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               DBG(" RESTART \n");
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+           DBG(" STOPS \n");
+               prtd->state &= ~ST_RUNNING;
+               disable_dma(prtd->params->channel);
+#ifdef CONFIG_ANDROID_POWER        
+        android_unlock_suspend(&audio_lock );
+        DBG("%s::stop audio , unlock system suspend\n" , __func__ );
+#endif
+               
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       spin_unlock(&prtd->lock);
+       return ret;
+}
+
+
+static snd_pcm_uframes_t
+rockchip_pcm_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct rockchip_runtime_data *prtd = runtime->private_data;
+       unsigned long res;
+       dma_addr_t src, dst;
+       snd_pcm_uframes_t ret;
+    
+       DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+       spin_lock(&prtd->lock);
+       dma_getposition(prtd->params->channel, &src, &dst);
+       
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+               res = dst - prtd->dma_start;
+       else
+               res = src - prtd->dma_start;
+
+       spin_unlock(&prtd->lock);
+
+       DBG("Pointer %x %x\n",src,dst); 
+
+       ret = bytes_to_frames(runtime, res);
+       if (ret == runtime->buffer_size)
+               ret = 0;
+       return ret;     
+}
+
+
+static int rockchip_pcm_open(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct rockchip_runtime_data *prtd;
+
+       DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+
+       snd_soc_set_runtime_hwparams(substream, &rockchip_pcm_hardware);
+
+       prtd = kzalloc(sizeof(struct rockchip_runtime_data), GFP_KERNEL);
+       if (prtd == NULL)
+               return -ENOMEM;
+
+       spin_lock_init(&prtd->lock);
+
+       runtime->private_data = prtd;
+       return 0;
+}
+
+static int rockchip_pcm_close(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct rockchip_runtime_data *prtd = runtime->private_data;
+    struct rockchip_dma_buf_set *sg_buf = NULL;
+       DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+
+       if (!prtd)
+               DBG("rockchip_pcm_close called with prtd == NULL\n");
+    if (prtd) 
+               sg_buf = prtd->curr;
+
+       while (sg_buf != NULL) {
+               prtd->curr = sg_buf->next;
+               prtd->next = sg_buf->next;
+               sg_buf->next  = NULL;
+               kfree(sg_buf);
+               sg_buf = NULL;
+               sg_buf = prtd->curr;
+       }
+       kfree(prtd);
+
+       return 0;
+}
+
+static int rockchip_pcm_mmap(struct snd_pcm_substream *substream,
+       struct vm_area_struct *vma)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+
+       return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+                                    runtime->dma_area,
+                                    runtime->dma_addr,
+                                    runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops rockchip_pcm_ops = {
+       .open           = rockchip_pcm_open,
+       .close          = rockchip_pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = rockchip_pcm_hw_params,
+       .hw_free        = rockchip_pcm_hw_free,
+       .prepare        = rockchip_pcm_prepare,
+       .trigger        = rockchip_pcm_trigger,
+       .pointer        = rockchip_pcm_pointer,
+       .mmap           = rockchip_pcm_mmap,
+};
+
+static int rockchip_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+       struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+       struct snd_dma_buffer *buf = &substream->dma_buffer;
+       size_t size = rockchip_pcm_hardware.buffer_bytes_max;
+
+       DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+
+       buf->dev.type = SNDRV_DMA_TYPE_DEV;
+       buf->dev.dev = pcm->card->dev;
+       buf->private_data = NULL;
+       buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+                                          &buf->addr, GFP_KERNEL);
+       if (!buf->area)
+               return -ENOMEM;
+       buf->bytes = size;
+       return 0;
+}
+
+static void rockchip_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+       struct snd_pcm_substream *substream;
+       struct snd_dma_buffer *buf;
+       int stream;
+
+       DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+
+       for (stream = 0; stream < 2; stream++) {
+               substream = pcm->streams[stream].substream;
+               if (!substream)
+                       continue;
+
+               buf = &substream->dma_buffer;
+               if (!buf->area)
+                       continue;
+
+               dma_free_writecombine(pcm->card->dev, buf->bytes,
+                                     buf->area, buf->addr);
+               buf->area = NULL;
+       }
+}
+
+static u64 rockchip_pcm_dmamask = DMA_BIT_MASK(32);
+
+static int rockchip_pcm_new(struct snd_card *card,
+       struct snd_soc_dai *dai, struct snd_pcm *pcm)
+{
+       int ret = 0;
+
+       DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+
+#ifdef CONFIG_ANDROID_POWER
+       audio_lock.name = "rk-audio";
+       android_init_suspend_lock(&audio_lock);
+#endif
+
+       if (!card->dev->dma_mask)
+               card->dev->dma_mask = &rockchip_pcm_dmamask;
+       if (!card->dev->coherent_dma_mask)
+               card->dev->coherent_dma_mask = 0xffffffff;
+
+       if (dai->playback.channels_min) {
+               ret = rockchip_pcm_preallocate_dma_buffer(pcm,
+                       SNDRV_PCM_STREAM_PLAYBACK);
+               if (ret)
+                       goto out;
+       }
+
+       if (dai->capture.channels_min) {
+               ret = rockchip_pcm_preallocate_dma_buffer(pcm,
+                       SNDRV_PCM_STREAM_CAPTURE);
+               if (ret)
+                       goto out;
+       }
+ out:
+       return ret;
+}
+
+struct snd_soc_platform rk2818_soc_platform = {
+       .name           = "rockchip-audio",
+       .pcm_ops        = &rockchip_pcm_ops,
+       .pcm_new        = rockchip_pcm_new,
+       .pcm_free       = rockchip_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(rk2818_soc_platform);
+
+static int __init rockchip_soc_platform_init(void)
+{
+       return snd_soc_register_platform(&rk2818_soc_platform);
+}
+module_init(rockchip_soc_platform_init);
+
+static void __exit rockchip_soc_platform_exit(void)
+{
+       snd_soc_unregister_platform(&rk2818_soc_platform);
+}
+module_exit(rockchip_soc_platform_exit);
+
+/* Module information */
+MODULE_AUTHOR("lhh lhh@rock-chips.com");
+MODULE_DESCRIPTION("ROCKCHIP PCM ASoC Interface");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/rk2818/rk2818_pcm.h b/sound/soc/rk2818/rk2818_pcm.h
new file mode 100755 (executable)
index 0000000..ad4ac2b
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * rockchip-pcm.h - ALSA PCM interface for the Rockchip rk28 SoC
+ *
+ * Driver for rockchip iis audio
+ *  Copyright (C) 2009 lhh
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _ROCKCHIP_PCM_H
+#define _ROCKCHIP_PCM_H
+
+#include <mach/hardware.h>
+
+#define ST_RUNNING             (1<<0)
+#define ST_OPENED              (1<<1)
+
+/* dma buffer */
+struct rockchip_dma_client {
+       char                *name;
+};
+
+struct rockchip_pcm_dma_params {
+       struct rockchip_dma_client *client;     /* stream identifier */
+       int channel;                            /* Channel ID */
+       dma_addr_t dma_addr;
+       int dma_size;                   /* Size of the DMA transfer */
+};
+
+extern struct snd_soc_platform rk2818_soc_platform;
+
+#endif /* _ROCKCHIP_PCM_H */
diff --git a/sound/soc/rk2818/rk2818_wm8988.c b/sound/soc/rk2818/rk2818_wm8988.c
new file mode 100755 (executable)
index 0000000..5dfeb33
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * rk2818_wm8988.c  --  SoC audio for rockchip
+ *
+ * Driver for rockchip wm8988 audio
+ *  Copyright (C) 2009 lhh
+ *
+ *  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.
+ *
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <asm/io.h>
+#include <mach/hardware.h>
+#include <mach/rk2818_iomap.h>
+#include "../codecs/wm8988.h"
+#include "rk2818_pcm.h"
+#include "rk2818_i2s.h"
+
+#if 0
+#define        DBG(x...)       printk(KERN_INFO x)
+#else
+#define        DBG(x...)
+#endif
+
+static int rk2818_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+    struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       int ret;
+         
+    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);    
+       /*by Vincent Hsiung for EQ Vol Change*/
+       #define HW_PARAMS_FLAG_EQVOL_ON 0x21
+       #define HW_PARAMS_FLAG_EQVOL_OFF 0x22
+    if ((params->flags == HW_PARAMS_FLAG_EQVOL_ON)||(params->flags == HW_PARAMS_FLAG_EQVOL_OFF))
+    {
+       ret = codec_dai->ops->hw_params(substream, params, codec_dai); //by Vincent
+       DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+    }
+    else
+    {
+           /* set codec DAI configuration */
+           #if defined (CONFIG_SND_ROCKCHIP_SOC_MASTER) 
+           ret = codec_dai->ops->set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); 
+           #endif      
+           #if defined (CONFIG_SND_ROCKCHIP_SOC_SLAVE) 
+           ret = codec_dai->ops->set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM ); 
+           #endif
+           if (ret < 0)
+                 return ret; 
+           /* set cpu DAI configuration */
+           #if defined (CONFIG_SND_ROCKCHIP_SOC_MASTER) 
+           ret = cpu_dai->ops->set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+           #endif      
+           #if defined (CONFIG_SND_ROCKCHIP_SOC_SLAVE) 
+           ret = cpu_dai->ops->set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); 
+           #endif              
+           if (ret < 0)
+                 return ret;
+         }
+    
+         return 0;
+}
+
+static const struct snd_soc_dapm_widget rk2818_dapm_widgets[] = {
+       SND_SOC_DAPM_LINE("Audio Out", NULL),
+       SND_SOC_DAPM_LINE("Line in", NULL),
+       SND_SOC_DAPM_MIC("Micn", NULL),
+       SND_SOC_DAPM_MIC("Micp", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[]= {
+       
+       {"Audio Out", NULL, "LOUT1"},
+       {"Audio Out", NULL, "ROUT1"},
+       {"Line in", NULL, "RINPUT1"},
+       {"Line in", NULL, "LINPUT1"},
+       {"Micn", NULL, "RINPUT2"},
+       {"Micp", NULL, "LINPUT2"},
+};
+
+/*
+ * Logic for a wm8988 as connected on a rockchip board.
+ */
+static int rk2818_wm8988_init(struct snd_soc_codec *codec)
+{
+       struct snd_soc_dai *codec_dai = &codec->dai[0];
+       int ret;
+         
+    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+    
+    ret = snd_soc_dai_set_sysclk(codec_dai, 0,
+               12000000, SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               printk(KERN_ERR "Failed to set WM8988 SYSCLK: %d\n", ret);
+               return ret;
+       }
+       
+    /* Add specific widgets */
+       snd_soc_dapm_new_controls(codec, rk2818_dapm_widgets,
+                                 ARRAY_SIZE(rk2818_dapm_widgets));
+       snd_soc_dapm_nc_pin(codec, "LOUT2");
+       snd_soc_dapm_nc_pin(codec, "ROUT2");
+       
+    /* Set up specific audio path audio_mapnects */
+    snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+       
+    snd_soc_dapm_sync(codec);
+    return 0;
+}
+
+static struct snd_soc_ops rk2818_ops = {
+         .hw_params = rk2818_hw_params,
+};
+
+static struct snd_soc_dai_link rk2818_dai = {
+         .name = "WM8988",
+         .stream_name = "WM8988 PCM",
+         .cpu_dai = &rk2818_i2s_dai,
+         .codec_dai = &wm8988_dai,
+         .init = rk2818_wm8988_init,
+         .ops = &rk2818_ops,
+};
+
+static struct snd_soc_card snd_soc_card_rk2818 = {
+         .name = "RK2818_WM8988",
+         .platform = &rk2818_soc_platform,
+         .dai_link = &rk2818_dai,
+         .num_links = 1,
+};
+
+
+static struct snd_soc_device rk2818_snd_devdata = {
+         .card = &snd_soc_card_rk2818,
+         .codec_dev = &soc_codec_dev_wm8988,
+};
+
+static struct platform_device *rk2818_snd_device;
+
+static int __init audio_card_init(void)
+{
+       int ret =0;     
+    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+       rk2818_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!rk2818_snd_device) {
+                 DBG("platform device allocation failed\n");
+                 ret = -ENOMEM;
+                 return ret;
+       }
+       platform_set_drvdata(rk2818_snd_device, &rk2818_snd_devdata);
+       rk2818_snd_devdata.dev = &rk2818_snd_device->dev;
+       ret = platform_device_add(rk2818_snd_device);
+       if (ret) {
+           DBG("platform device add failed\n");
+           platform_device_put(rk2818_snd_device);
+       }
+       return ret;
+}
+static void __exit audio_card_exit(void)
+{
+       platform_device_unregister(rk2818_snd_device);
+}
+
+module_init(audio_card_init);
+module_exit(audio_card_exit);
+/* Module information */
+MODULE_AUTHOR("lhh lhh@rock-chips.com");
+MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface");
+MODULE_LICENSE("GPL");