mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 20:32:04 +09:00
add rk29 i2s and pcm
This commit is contained in:
41
sound/soc/rk29/Kconfig
Executable file
41
sound/soc/rk29/Kconfig
Executable file
@@ -0,0 +1,41 @@
|
||||
config SND_RK29_SOC
|
||||
tristate "SoC Audio for the rockchip RK29 System-on-Chip"
|
||||
depends on ARCH_RK29 && SND_SOC
|
||||
select SND_RK29_SOC_I2S
|
||||
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_RK29_SOC_I2S
|
||||
tristate
|
||||
|
||||
config SND_RK29_SOC_WM8988
|
||||
tristate "SoC I2S Audio support for rockchip - WM8988"
|
||||
depends on SND_RK29_SOC
|
||||
select SND_RK29_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_RK29_SOC_RK1000
|
||||
tristate "SoC I2S Audio support for rockchip - RK1000"
|
||||
depends on SND_RK29_SOC && RK1000_CONTROL
|
||||
select SND_RK29_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_RK29_SOC_WM8988 || SND_RK29_SOC_RK1000 || SND_RK29_SOC_WM8994
|
||||
choice
|
||||
prompt "Set i2s type"
|
||||
|
||||
config SND_CODEC_SOC_MASTER
|
||||
tristate "Codec run in Master"
|
||||
|
||||
config SND_CODEC_SOC_SLAVE
|
||||
tristate "Codec run in Slave"
|
||||
endchoice
|
||||
endif
|
||||
15
sound/soc/rk29/Makefile
Executable file
15
sound/soc/rk29/Makefile
Executable file
@@ -0,0 +1,15 @@
|
||||
# ROCKCHIP Platform Support
|
||||
snd-soc-rockchip-objs := rk29_pcm.o
|
||||
snd-soc-rockchip-i2s-objs := rk29_i2s.o
|
||||
|
||||
obj-$(CONFIG_SND_RK29_SOC) += snd-soc-rockchip.o
|
||||
obj-$(CONFIG_SND_RK29_SOC_I2S) += snd-soc-rockchip-i2s.o
|
||||
|
||||
# ROCKCHIP Machine Support
|
||||
snd-soc-wm8988-objs := rk29_wm8988.o
|
||||
snd-soc-rk1000-objs := rk29_rk1000codec.o
|
||||
snd-soc-wm8994-objs := rk29_wm8994.o
|
||||
|
||||
obj-$(CONFIG_SND_RK29_SOC_WM8994) += snd-soc-wm8994.o
|
||||
obj-$(CONFIG_SND_RK29_SOC_WM8988) += snd-soc-wm8988.o
|
||||
obj-$(CONFIG_SND_RK29_SOC_RK1000) += snd-soc-rk1000.o
|
||||
583
sound/soc/rk29/rk29_i2s.c
Executable file
583
sound/soc/rk29/rk29_i2s.c
Executable file
@@ -0,0 +1,583 @@
|
||||
/*
|
||||
* rk29_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/board.h>
|
||||
#include <mach/rk29_iomap.h>
|
||||
#include <mach/rk29-dma-pl330.h>
|
||||
|
||||
|
||||
#include "rk29_pcm.h"
|
||||
#include "rk29_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))
|
||||
|
||||
#define MAX_I2S 2
|
||||
|
||||
struct rk29_i2s_info {
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
|
||||
u32 feature;
|
||||
|
||||
struct clk *iis_clk;
|
||||
struct clk *iis_pclk;
|
||||
|
||||
unsigned char master;
|
||||
|
||||
struct rockchip_pcm_dma_params *dma_playback;
|
||||
struct rockchip_pcm_dma_params *dma_capture;
|
||||
|
||||
u32 suspend_iismod;
|
||||
u32 suspend_iiscon;
|
||||
u32 suspend_iispsr;
|
||||
};
|
||||
|
||||
static struct rk29_dma_client rk29_dma_client_out = {
|
||||
.name = "I2S PCM Stereo Out"
|
||||
};
|
||||
|
||||
static struct rk29_dma_client rk29_dma_client_in = {
|
||||
.name = "I2S PCM Stereo In"
|
||||
};
|
||||
|
||||
static inline struct rk29_i2s_info *to_info(struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
return cpu_dai->private_data;
|
||||
}
|
||||
|
||||
static struct rockchip_pcm_dma_params rk29_i2s_pcm_stereo_out[MAX_I2S];
|
||||
static struct rockchip_pcm_dma_params rk29_i2s_pcm_stereo_in[MAX_I2S];
|
||||
static struct rk29_i2s_info rk29_i2s[MAX_I2S];
|
||||
|
||||
struct snd_soc_dai rk29_i2s_dai[MAX_I2S];
|
||||
EXPORT_SYMBOL_GPL(rk29_i2s_dai);
|
||||
|
||||
/*
|
||||
static struct rockchip_pcm_dma_params rockchip_i2s_pcm_stereo_out[MAX_I2S] = {
|
||||
[0] = {
|
||||
.client = &rk29_dma_client_out,
|
||||
.channel = DMACH_I2S_2CH_TX, ///0, //DMACH_I2S_OUT,
|
||||
.dma_addr = RK29_I2S_2CH_PHYS + I2S_TXR_BUFF,
|
||||
.dma_size = 4,
|
||||
},
|
||||
[1] = {
|
||||
.client = &rk29_dma_client_out,
|
||||
.channel = DMACH_I2S_8CH_TX, ///0, //DMACH_I2S_OUT,
|
||||
.dma_addr = RK29_I2S_8CH_PHYS + I2S_TXR_BUFF,
|
||||
.dma_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static struct rockchip_pcm_dma_params rockchip_i2s_pcm_stereo_in[MAX_I2S] = {
|
||||
[0] = {
|
||||
.client = &rk29_dma_client_in,
|
||||
.channel = DMACH_I2S_2CH_RX, ///1, //DMACH_I2S_IN,
|
||||
.dma_addr = RK29_I2S_2CH_PHYS + I2S_RXR_BUFF,
|
||||
.dma_size = 4,
|
||||
},
|
||||
[1] = {
|
||||
.client = &rk29_dma_client_in,
|
||||
.channel = DMACH_I2S_8CH_RX, ///1, //DMACH_I2S_IN,
|
||||
.dma_addr = RK29_I2S_8CH_PHYS + I2S_RXR_BUFF,
|
||||
.dma_size = 4,
|
||||
},
|
||||
};
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*Turn on or off the transmission path.
|
||||
*/
|
||||
static void rockchip_snd_txctrl(struct rk29_i2s_info *i2s, int on)
|
||||
{
|
||||
//struct rk29_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(struct rk29_i2s_info *i2s, int on)
|
||||
{
|
||||
//struct rk29_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 rk29_i2s_info *i2s = to_info(cpu_dai);
|
||||
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 *socdai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai_link *dai = rtd->dai;
|
||||
struct rk29_i2s_info *i2s = to_info(dai->cpu_dai);
|
||||
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)
|
||||
dai->cpu_dai->dma_data = i2s->dma_playback->client;
|
||||
else
|
||||
dai->cpu_dai->dma_data = i2s->dma_capture->client;
|
||||
|
||||
/* 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;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct rk29_i2s_info *i2s = to_info(rtd->dai->cpu_dai);
|
||||
|
||||
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(i2s, 1);
|
||||
else
|
||||
rockchip_snd_txctrl(i2s, 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(i2s, 0);
|
||||
else
|
||||
rockchip_snd_txctrl(i2s, 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;
|
||||
}
|
||||
|
||||
static int rockchip_set_sysclk(struct snd_soc_dai *cpu_dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
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);
|
||||
|
||||
#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_48000
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
static int rockchip_i2s_dai_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
|
||||
{
|
||||
DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk29_i2s_probe(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai,
|
||||
struct rk29_i2s_info *i2s,
|
||||
unsigned long base)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
unsigned int iismod;
|
||||
struct resource *res;
|
||||
|
||||
i2s->dev = dev;
|
||||
|
||||
/* record our i2s structure for later use in the callbacks */
|
||||
dai->private_data = i2s;
|
||||
|
||||
if (!base) {
|
||||
res = platform_get_resource(pdev,
|
||||
IORESOURCE_MEM,
|
||||
0);
|
||||
if (!res) {
|
||||
dev_err(dev, "Unable to get register resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (!request_mem_region(res->start, resource_size(res),
|
||||
"rk29-i2s")) {
|
||||
dev_err(dev, "Unable to request register region\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
base = res->start;
|
||||
}
|
||||
|
||||
i2s->regs = ioremap(base, resource_size(res));
|
||||
if (i2s->regs == NULL) {
|
||||
dev_err(dev, "cannot ioremap registers\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
i2s->iis_pclk = clk_get(dev, "i2s");
|
||||
if (IS_ERR(i2s->iis_pclk)) {
|
||||
dev_err(dev, "failed to get iis_clock\n");
|
||||
iounmap(i2s->regs);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
clk_enable(i2s->iis_pclk);
|
||||
|
||||
/* Mark ourselves as in TXRX mode so we can run through our cleanup
|
||||
* process without warnings. */
|
||||
|
||||
rockchip_snd_txctrl(i2s, 0);
|
||||
rockchip_snd_rxctrl(i2s, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk29_i2s_register_dai(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_dai_ops *ops = dai->ops;
|
||||
|
||||
ops->trigger = rockchip_i2s_trigger;
|
||||
if (!ops->hw_params)
|
||||
ops->hw_params = rockchip_i2s_hw_params;
|
||||
ops->set_fmt = rockchip_i2s_set_fmt;
|
||||
ops->set_clkdiv = rockchip_i2s_set_clkdiv;
|
||||
ops->set_sysclk = rockchip_set_sysclk;
|
||||
|
||||
dai->suspend = rockchip_i2s_suspend;
|
||||
dai->resume = rockchip_i2s_resume;
|
||||
|
||||
return snd_soc_register_dai(dai);
|
||||
}
|
||||
|
||||
static int __devinit rockchip_i2s_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rk29_i2s_info *i2s;
|
||||
struct snd_soc_dai *dai;
|
||||
int ret;
|
||||
|
||||
if(pdev->id >= MAX_I2S) {
|
||||
dev_err(&pdev->dev, "id %d out of range\n", pdev->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
i2s = &rk29_i2s[pdev->id];
|
||||
dai = &rk29_i2s_dai[pdev->id];
|
||||
dai->dev = &pdev->dev;
|
||||
dai->name = "rk29-i2s";
|
||||
dai->id = pdev->id;
|
||||
dai->symmetric_rates = 1;
|
||||
dai->playback.channels_min = 2;
|
||||
dai->playback.channels_max = 2;
|
||||
dai->playback.rates = ROCKCHIP_I2S_RATES;
|
||||
dai->playback.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE;
|
||||
dai->capture.channels_min = 2;
|
||||
dai->capture.channels_max = 2;
|
||||
dai->capture.rates = ROCKCHIP_I2S_RATES;
|
||||
dai->capture.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE;
|
||||
dai->probe = rockchip_i2s_dai_probe;
|
||||
dai->ops = &rockchip_i2s_dai_ops;
|
||||
|
||||
//i2s->feature |= S3C_FEATURE_CDCLKCON;
|
||||
|
||||
i2s->dma_capture = &rk29_i2s_pcm_stereo_in[pdev->id];
|
||||
i2s->dma_playback = &rk29_i2s_pcm_stereo_out[pdev->id];
|
||||
|
||||
if (pdev->id == 0) {
|
||||
i2s->dma_capture->channel = DMACH_I2S_2CH_RX;
|
||||
i2s->dma_capture->dma_addr = RK29_I2S_2CH_PHYS + I2S_RXR_BUFF;
|
||||
i2s->dma_playback->channel = DMACH_I2S_2CH_TX;
|
||||
i2s->dma_playback->dma_addr = RK29_I2S_2CH_PHYS + I2S_TXR_BUFF;
|
||||
} else {
|
||||
i2s->dma_capture->channel = DMACH_I2S_8CH_RX;
|
||||
i2s->dma_capture->dma_addr = RK29_I2S_8CH_PHYS + I2S_RXR_BUFF;
|
||||
i2s->dma_playback->channel = DMACH_I2S_8CH_TX;
|
||||
i2s->dma_playback->dma_addr = RK29_I2S_8CH_PHYS + I2S_TXR_BUFF;
|
||||
}
|
||||
|
||||
i2s->dma_capture->client = &rk29_dma_client_in;
|
||||
i2s->dma_capture->dma_size = 4;
|
||||
i2s->dma_playback->client = &rk29_dma_client_out;
|
||||
i2s->dma_playback->dma_size = 4;
|
||||
|
||||
i2s->iis_clk = clk_get(&pdev->dev, "i2s");
|
||||
if (IS_ERR(i2s->iis_clk)) {
|
||||
dev_err(&pdev->dev, "failed to get i2s clk\n");
|
||||
ret = PTR_ERR(i2s->iis_clk);
|
||||
goto err;
|
||||
}
|
||||
|
||||
clk_enable(i2s->iis_clk);
|
||||
|
||||
ret = rk29_i2s_probe(pdev, dai, i2s, 0);
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
|
||||
ret = rk29_i2s_register_dai(dai);
|
||||
if (ret != 0)
|
||||
goto err_i2sv2;
|
||||
|
||||
return 0;
|
||||
|
||||
err_i2sv2:
|
||||
/* Not implemented for I2Sv2 core yet */
|
||||
err_clk:
|
||||
clk_put(i2s->iis_clk);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int __devexit rockchip_i2s_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_dai(&rk29_i2s_dai);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver rockchip_i2s_driver = {
|
||||
.probe = rockchip_i2s_probe,
|
||||
.remove = __devexit_p(rockchip_i2s_remove),
|
||||
.driver = {
|
||||
.name = "rk29-i2s",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init rockchip_i2s_init(void)
|
||||
{
|
||||
printk(KERN_INFO
|
||||
"Enter Func = %s\n", __func__);
|
||||
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");
|
||||
|
||||
83
sound/soc/rk29/rk29_i2s.h
Executable file
83
sound/soc/rk29/rk29_i2s.h
Executable file
@@ -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 rk29_i2s_dai[];
|
||||
//extern void rockchip_add_device_i2s(void);
|
||||
#endif /* _ROCKCHIP_IIS_H */
|
||||
|
||||
682
sound/soc/rk29/rk29_pcm.c
Executable file
682
sound/soc/rk29/rk29_pcm.c
Executable file
@@ -0,0 +1,682 @@
|
||||
/*
|
||||
* rk29_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 "rk29_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 = 2,
|
||||
.channels_max = 8,
|
||||
.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_ATOMIC);/* ddl@rock-chips.com:GFP_KERNEL->GFP_ATOMIC */
|
||||
|
||||
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, DMA_IRQ_RIGHTNOW_MODE);
|
||||
__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, DMA_IRQ_RIGHTNOW_MODE);
|
||||
__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);
|
||||
ret = rk29_dma_enqueue(prtd->params->channel,
|
||||
substream, 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);
|
||||
}
|
||||
|
||||
|
||||
void rk29_audio_buffdone(struct rk29_dma_chan *ch,
|
||||
void *dev_id, int size,
|
||||
enum rk29_dma_buffresult result)
|
||||
{
|
||||
struct snd_pcm_substream *substream = dev_id;
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
ret = rk29_dma_request(prtd->params->channel, prtd->params->client, NULL);
|
||||
/*
|
||||
if(ret){
|
||||
for(prtd->params->channel=5;prtd->params->channel>0;prtd->params->channel--){
|
||||
ret = request_dma(prtd->params->channel, "i2s");
|
||||
if(!ret)break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
if (ret) {
|
||||
DBG(KERN_ERR "failed to get dma channel\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
rk29_dma_set_buffdone_fn(prtd->params->channel, rk29_audio_buffdone);
|
||||
|
||||
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);
|
||||
rk29_dma_free(prtd->params->channel, prtd->params->client);
|
||||
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;
|
||||
|
||||
if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
rk29_dma_devconfig(prtd->params->channel,
|
||||
RK29_DMASRC_MEM,
|
||||
prtd->params->dma_addr);
|
||||
}else{
|
||||
rk29_dma_devconfig(prtd->params->channel,
|
||||
RK29_DMASRC_HW,
|
||||
prtd->params->dma_addr);
|
||||
}
|
||||
|
||||
rk29_dma_config(prtd->params->channel,
|
||||
prtd->params->dma_size);
|
||||
rk29_dma_ctrl(prtd->params, RK29_DMAOP_FLUSH);
|
||||
|
||||
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;
|
||||
/**************add by qiuen for volume*****/
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *pCodec_dai = rtd->dai->codec_dai;
|
||||
int vol = 0;
|
||||
int streamType = 0;
|
||||
|
||||
DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
|
||||
|
||||
if(cmd==SNDRV_PCM_TRIGGER_VOLUME){
|
||||
vol = substream->number % 100;
|
||||
streamType = (substream->number / 100) % 100;
|
||||
DBG("enter:vol=%d,streamType=%d\n",vol,streamType);
|
||||
pCodec_dai->ops->set_volume(streamType, vol);
|
||||
}
|
||||
/****************************************************/
|
||||
spin_lock(&prtd->lock);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
DBG(" START \n");
|
||||
prtd->state |= ST_RUNNING;
|
||||
rk29_dma_ctrl(prtd->params->channel, RK29_DMAOP_START);
|
||||
/*
|
||||
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;
|
||||
rk29_dma_ctrl(prtd->params->channel, RK29_DMAOP_STOP);
|
||||
//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);
|
||||
|
||||
//get_dma_position(prtd->params->channel, &src, &dst);
|
||||
rk29_dma_getposition(prtd->params->channel, &src, &dst);
|
||||
//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 rk29_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(rk29_soc_platform);
|
||||
|
||||
static int __init rockchip_soc_platform_init(void)
|
||||
{
|
||||
return snd_soc_register_platform(&rk29_soc_platform);
|
||||
}
|
||||
module_init(rockchip_soc_platform_init);
|
||||
|
||||
static void __exit rockchip_soc_platform_exit(void)
|
||||
{
|
||||
snd_soc_unregister_platform(&rk29_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");
|
||||
|
||||
35
sound/soc/rk29/rk29_pcm.h
Executable file
35
sound/soc/rk29/rk29_pcm.h
Executable file
@@ -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 rk29_soc_platform;
|
||||
|
||||
#endif /* _ROCKCHIP_PCM_H */
|
||||
186
sound/soc/rk29/rk29_rk1000codec.c
Normal file
186
sound/soc/rk29/rk29_rk1000codec.c
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* rk29_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/rk29_iomap.h>
|
||||
#include "../codecs/rk1000_codec.h"
|
||||
#include "rk29_pcm.h"
|
||||
#include "rk29_i2s.h"
|
||||
|
||||
#if 0
|
||||
#define DBG(x...) printk(KERN_INFO x)
|
||||
#else
|
||||
#define DBG(x...)
|
||||
#endif
|
||||
|
||||
static int rk29_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 rk29_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 rk1000 codec as connected on a rockchip board.
|
||||
*/
|
||||
static int rk29_rk1000_codec_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, rk29_dapm_widgets,
|
||||
ARRAY_SIZE(rk29_dapm_widgets));
|
||||
|
||||
/* 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 rk29_ops = {
|
||||
.hw_params = rk29_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link rk29_dai = {
|
||||
.name = "RK1000_CODEC",
|
||||
.stream_name = "RK1000 CODEC PCM",
|
||||
.cpu_dai = &rk29_i2s_dai,
|
||||
.codec_dai = &rk1000_codec_dai,
|
||||
.init = rk29_rk1000_codec_init,
|
||||
.ops = &rk29_ops,
|
||||
};
|
||||
|
||||
static struct snd_soc_card snd_soc_card_rk29 = {
|
||||
.name = "RK1000_CODEC",
|
||||
.platform = &rk29_soc_platform,
|
||||
.dai_link = &rk29_dai,
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
|
||||
static struct snd_soc_device rk29_snd_devdata = {
|
||||
.card = &snd_soc_card_rk29,
|
||||
.codec_dev = &soc_codec_dev_rk1000_codec,
|
||||
};
|
||||
|
||||
static struct platform_device *rk29_snd_device;
|
||||
|
||||
static int __init audio_card_init(void)
|
||||
{
|
||||
int ret =0;
|
||||
DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
|
||||
rk29_snd_device = platform_device_alloc("soc-audio", -1);
|
||||
if (!rk29_snd_device) {
|
||||
DBG("platform device allocation failed\n");
|
||||
ret = -ENOMEM;
|
||||
return ret;
|
||||
}
|
||||
DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
|
||||
platform_set_drvdata(rk29_snd_device, &rk29_snd_devdata);
|
||||
DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
|
||||
rk29_snd_devdata.dev = &rk29_snd_device->dev;
|
||||
ret = platform_device_add(rk29_snd_device);
|
||||
DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
|
||||
if (ret) {
|
||||
DBG("platform device add failed\n");
|
||||
platform_device_put(rk29_snd_device);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static void __exit audio_card_exit(void)
|
||||
{
|
||||
platform_device_unregister(rk29_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");
|
||||
185
sound/soc/rk29/rk29_wm8988.c
Executable file
185
sound/soc/rk29/rk29_wm8988.c
Executable file
@@ -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/rk29_iomap.h>
|
||||
#include "../codecs/wm8988.h"
|
||||
#include "rk29_pcm.h"
|
||||
#include "rk29_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");
|
||||
Reference in New Issue
Block a user