rk3188: spdif support

This commit is contained in:
yzq
2013-04-28 17:47:53 +08:00
parent 1c5268cf1c
commit 20b654aab5
7 changed files with 833 additions and 152 deletions

View File

@@ -1112,6 +1112,35 @@ static void __init rk30_init_sdmmc(void)
#endif
}
#ifdef CONFIG_SND_RK_SOC_SPDIF
static struct resource resources_spdif[] = {
[0] = {
.name = "spdif_base",
.start = RK30_SPDIF_PHYS,
.end = RK30_SPDIF_PHYS + RK30_SPDIF_SIZE - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.name = "spdif_irq",
.start = IRQ_SPDIF,
.end = IRQ_SPDIF,
.flags = IORESOURCE_IRQ,
},
[2] = {
.name = "spdif_dma",
.start = DMACH_SPDIF_TX,
.end = DMACH_SPDIF_TX,
.flags = IORESOURCE_DMA,
},
};
struct platform_device rk29_device_spdif = {
.name = "rk-spdif",
.id = 0,
.num_resources = ARRAY_SIZE(resources_spdif),
.resource = resources_spdif,
};
#endif
#ifdef CONFIG_RK29_VMAC
static u64 eth_dmamask = DMA_BIT_MASK(32);
static struct resource resources_vmac[] = {
@@ -1220,6 +1249,10 @@ static int __init rk30_init_devices(void)
rk_serial_debug_init(DEBUG_UART_BASE, IRQ_DEBUG_UART, IRQ_UART_SIGNAL, -1);
#endif
rk30_init_i2s();
#ifdef CONFIG_SND_RK_SOC_SPDIF
platform_device_register(&rk29_device_spdif);
#endif
#ifdef CONFIG_RK29_VMAC
platform_device_register(&device_vmac);
#endif

View File

@@ -42,6 +42,9 @@ config SND_RK_SOC_I2S2_2CH
help
This supports the use of the 2 Channel I2S2 interface on rk30 processors.
config SND_ROCKCHIP_SPDIF
tristate
select SND_SOC_SPDIF
if SND_RK29_SOC_I2S_2CH || SND_RK29_SOC_I2S_8CH || SND_RK_SOC_I2S2_2CH
choice
bool "Set i2s on DMA event mode"
@@ -53,7 +56,14 @@ choice
tristate "static mode"
endchoice
endif
config SND_RK_SOC_SPDIF
bool "spdif support for rockchip rk29 or rk30"
depends on SND_RK29_SOC
select SND_ROCKCHIP_SPDIF
help
Say Y if you want to add support for SoC audio on rockchip
with the spdif.
config SND_RK29_SOC_SPDIF
bool "Soc RK29 SPDIF support"
depends on SND_RK29_SOC

View File

@@ -5,11 +5,11 @@ snd-soc-rockchip-i2s-objs := rk29_i2s.o
else
snd-soc-rockchip-i2s-objs := rk30_i2s.o
endif
snd-soc-rockchip-spdif-objs := rk29_spdif.o
snd-soc-rockchip-spdif-objs := spdif.o
obj-$(CONFIG_SND_RK29_SOC) += snd-soc-rockchip.o
obj-$(CONFIG_SND_RK29_SOC_I2S) += snd-soc-rockchip-i2s.o
obj-$(CONFIG_SND_RK29_SOC_SPDIF) += snd-soc-rockchip-spdif.o
obj-$(CONFIG_SND_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o
# ROCKCHIP Machine Support
snd-soc-wm8900-objs := rk29_wm8900.o
@@ -33,6 +33,7 @@ snd-soc-rk616-objs := rk_rk616.o
snd-soc-aic3262-objs := rk29_aic3262.o
snd-soc-rk2928-objs := rk2928-card.o
snd-soc-es8323-objs := rk29_es8323.o
snd-soc-rk_spdif-objs := rk_spdif.o
obj-$(CONFIG_SND_RK29_SOC_WM8994) += snd-soc-wm8994.o
obj-$(CONFIG_SND_RK29_SOC_WM8988) += snd-soc-wm8988.o
@@ -53,5 +54,6 @@ obj-$(CONFIG_SND_RK29_SOC_AIC3262) += snd-soc-aic3262.o
obj-$(CONFIG_SND_RK29_SOC_HDMI) += snd-soc-hdmi.o
obj-$(CONFIG_SND_RK29_SOC_RK610) += snd-soc-rk610.o
obj-$(CONFIG_SND_RK29_SOC_RK616) += snd-soc-rk616.o
obj-$(CONFIG_SND_RK_SOC_SPDIF) += snd-soc-rk_spdif.o
obj-$(CONFIG_SND_RK_SOC_RK2928) += snd-soc-rk2928.o
obj-$(CONFIG_SND_RK29_SOC_ES8323) += snd-soc-es8323.o

View File

@@ -28,7 +28,6 @@
#include "rk29_pcm.h"
#define PCM_DMA_DEBUG 0
#if 0
#define DBG(x...) printk(KERN_INFO x)
@@ -36,12 +35,6 @@
#define DBG(x...) do { } while (0)
#endif
#define INFIN_LOOP
#ifdef INFIN_LOOP
#define DMA_INFIN_LOOP() rk29_dma_has_infiniteloop()
#else
#define DMA_INFIN_LOOP() 0
#endif
static const struct snd_pcm_hardware rockchip_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
@@ -55,17 +48,9 @@ static const struct snd_pcm_hardware rockchip_pcm_hardware = {
SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 2,
.channels_max = 8,
#ifdef CONFIG_RK_SRAM_DMA
.buffer_bytes_max = 24*1024,//period_bytes_max * periods_max
#else
.buffer_bytes_max = 128*1024,
#endif
.period_bytes_min = 64, ///PAGE_SIZE,
#ifdef CONFIG_RK_SRAM_DMA
.period_bytes_max = 8*1024,
#else
.period_bytes_max = 2048*4,///PAGE_SIZE*2,
#endif
.periods_min = 3,///2,
.periods_max = 128,
.fifo_size = 16,
@@ -103,125 +88,92 @@ 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;
unsigned int limit;
int ret;
DBG("Enter::%s----%d prtd->dma_period = %d prtd->dma_limit = %d\n",__FUNCTION__,__LINE__,prtd->dma_period,prtd->dma_limit);
if (rk29_dma_has_circular())
limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period;
else
limit = prtd->dma_limit;
if (DMA_INFIN_LOOP()) {
if(prtd->dma_period % (prtd->params->dma_size*16)){
printk("dma_period(%d) is not an integer multiple of dma_size(%d)",prtd->dma_period,prtd->params->dma_size*16);
rk29_dma_config(prtd->params->channel,
prtd->params->dma_size, 1);
}
else
rk29_dma_config(prtd->params->channel,
prtd->params->dma_size, 16);
ret = rk29_dma_enqueue_ring(prtd->params->channel,
substream, pos, prtd->dma_period, limit ,true);
if (ret == 0)
pos = prtd->dma_start;
} else {
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;
}
if((len%(prtd->params->dma_size*16) == 0) && (prtd->params->flag == 1))
{
ret = rk29_dma_config(prtd->params->channel,
prtd->params->dma_size, 16);
prtd->params->flag = 0;
DBG("size = 16, channel = %d, flag = %d\n",prtd->params->channel,prtd->params->flag);
}
else if((len%(prtd->params->dma_size*16) != 0) && (prtd->params->flag == 0))
{
ret = rk29_dma_config(prtd->params->channel,
prtd->params->dma_size, 1);
prtd->params->flag = 1;
DBG("size = 1, channel = %d, flag = %d\n",prtd->params->channel,prtd->params->flag);
}
ret = rk29_dma_enqueue(prtd->params->channel,substream, pos, len);
// if(prtd->params->channel == 2)
DBG("Enter::%s, %d, ret=%d, Channel=%d, Addr=0x%X, Len=%lu\n",
__FUNCTION__,__LINE__, ret, prtd->params->channel, pos, len);
if (ret == 0) {
prtd->dma_loaded++;
pos += prtd->dma_period;
if (pos >= prtd->dma_end)
pos = prtd->dma_start;
} else
break;
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;
}
if((len%(prtd->params->dma_size*16) == 0) && (prtd->params->flag == 1))
{
ret = rk29_dma_config(prtd->params->channel,
prtd->params->dma_size, 16);
prtd->params->flag = 0;
DBG("size = 16, channel = %d, flag = %d\n",prtd->params->channel,prtd->params->flag);
}
else if((len%(prtd->params->dma_size*16) != 0) && (prtd->params->flag == 0))
{
ret = rk29_dma_config(prtd->params->channel,
prtd->params->dma_size, 1);
prtd->params->flag = 1;
DBG("size = 1, channel = %d, flag = %d\n",prtd->params->channel,prtd->params->flag);
}
ret = rk29_dma_enqueue(prtd->params->channel,
substream, pos, len);
DBG("Enter::%s, %d, ret=%d, Channel=%d, Addr=0x%X, Len=%lu\n",
__FUNCTION__,__LINE__, ret, prtd->params->channel, 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 rk29_audio_buffdone(void *dev_id, int size,
enum rk29_dma_buffresult result)
{
struct snd_pcm_substream *substream = dev_id;
struct rockchip_runtime_data *prtd;
#if PCM_DMA_DEBUG
static ktime_t before = {0},after = {0};
s64 t;
before = after;
after = ktime_get();
t = ktime_to_us(ktime_sub(after, before));
if(result == RK29_RES_OK)
{
if(t > prtd->dma_period/4/44100 +73 && t != ktime_to_us(after)) // (23220)4096/4/44100 + 32/44100
{
printk(KERN_DEBUG "Time out:: Audio DMA buffdone time out!!! the time = %lld!\n", t);
}
printk(KERN_DEBUG "audio DMA callback time = %lld\n", t);
}
// printk(KERN_DEBUG "a %d %d\n", size, result);
#endif
DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
if (!substream){
DBG("substream is free\n");
if (!substream)
return;
}
if (!substream->runtime){
DBG("substream->runtime is free\n");
if (!substream->runtime)
return;
}
switch(result)
{
case RK29_RES_OK:
DBG("::%s----%d RK29_RES_OK\n",__FUNCTION__,__LINE__);
break;
case RK29_RES_ERR:
DBG("::%s----%d RK29_RES_ERR\n",__FUNCTION__,__LINE__);
break;
case RK29_RES_ABORT:
DBG("Enter::%s dma about or error result = %d \n",__FUNCTION__,result);
DBG("Enter::%s----%d RK29_RES_ABORT \n",__FUNCTION__,__LINE__);
return;
}
prtd = substream->runtime->private_data;
// if(prtd->params->channel == 2)
DBG("Enter::%s----%d channel =%d \n",__FUNCTION__,__LINE__,prtd->params->channel);
if(!(prtd->state & ST_RUNNING))
return;
DBG("Enter::%s----%d, substream=%p, prtd=%p\n",__FUNCTION__,__LINE__, substream, prtd);
if (substream){
snd_pcm_period_elapsed(substream);
}
spin_lock(&prtd->lock);
if (!DMA_INFIN_LOOP() && prtd->state & ST_RUNNING) {
prtd->dma_loaded--;
prtd->dma_loaded--;
if (prtd->state & ST_RUNNING) {
rockchip_pcm_enqueue(substream);
}
spin_unlock(&prtd->lock);
spin_unlock(&prtd->lock);
}
static int rockchip_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -241,39 +193,54 @@ static int rockchip_pcm_hw_params(struct snd_pcm_substream *substream,
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;
DBG("Enter::11111%s----%d\n",__FUNCTION__,__LINE__);
/* this may get called several times by oss emulation
* with different params -HW */
if (prtd->params == NULL) {
/* prepare DMA */
prtd->params = dma;
#ifdef CONFIG_SND_I2S_DMA_EVENT_DYNAMIC
DBG("params %p, client %p, channel %d\n", prtd->params,prtd->params->client, prtd->params->channel);
ret = rk29_dma_request(prtd->params->channel, prtd->params->client, NULL);
DBG("Enter::%s, %d, ret=%d, Channel=%d\n", __FUNCTION__, __LINE__, ret, prtd->params->channel);
DBG("params %p, client %p, channel %d\n", prtd->params,
prtd->params->client, prtd->params->channel);
ret = rk29_dma_request(prtd->params->channel, prtd->params->client, NULL);
DBG("Enter::%s, %d, ret=%d, Channel=%d\n", __FUNCTION__, __LINE__, ret, prtd->params->channel);
/*
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;
}
#endif
}
rk29_dma_set_buffdone_fn(prtd->params->channel, rk29_audio_buffdone);
ret = rk29_dma_set_buffdone_fn(prtd->params->channel, rk29_audio_buffdone);
if(ret < 0){
DBG(KERN_ERR "failed to rk29_dma_set_buffdone_fn\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 = params_periods(params);//runtime->hw.periods_min;
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;
@@ -283,7 +250,8 @@ static int rockchip_pcm_hw_params(struct snd_pcm_substream *substream,
prtd->next = NULL;
prtd->end = NULL;
spin_unlock_irq(&prtd->lock);
return ret;
return 0;
}
static int rockchip_pcm_hw_free(struct snd_pcm_substream *substream)
@@ -292,13 +260,12 @@ static int rockchip_pcm_hw_free(struct snd_pcm_substream *substream)
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) {
#ifdef CONFIG_SND_I2S_DMA_EVENT_DYNAMIC
rk29_dma_free(prtd->params->channel, prtd->params->client);
prtd->params = NULL;
#endif
}
return 0;
@@ -381,6 +348,7 @@ static int rockchip_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_START:
DBG(" START \n");
prtd->state |= ST_RUNNING;
rk29_dma_ctrl(prtd->params->channel, RK29_DMAOP_START);
break;
case SNDRV_PCM_TRIGGER_RESUME:
@@ -395,7 +363,12 @@ static int rockchip_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
DBG(" STOPS \n");
prtd->state &= ~ST_RUNNING;
rk29_dma_ctrl(prtd->params->channel, RK29_DMAOP_STOP);
#ifdef CONFIG_ANDROID_POWER
android_unlock_suspend(&audio_lock );
DBG("%s::stop audio , unlock system suspend\n" , __func__ );
#endif
break;
default:
ret = -EINVAL;
@@ -415,12 +388,12 @@ rockchip_pcm_pointer(struct snd_pcm_substream *substream)
unsigned long res;
dma_addr_t src, dst;
snd_pcm_uframes_t ret;
DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
spin_lock(&prtd->lock);
rk29_dma_getposition(prtd->params->channel, &src, &dst);
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
res = dst - prtd->dma_start;
else
@@ -428,13 +401,11 @@ rockchip_pcm_pointer(struct snd_pcm_substream *substream)
spin_unlock(&prtd->lock);
DBG("Pointer %x %x\n",src,dst);
ret = bytes_to_frames(runtime, res);
if (ret == runtime->buffer_size)
ret = 0;
if(prtd->params->channel == 2)
DBG("Enter:%s src = %x res = %x ret = %d\n",__FUNCTION__,src,res,ret);
return ret;
}
@@ -472,6 +443,7 @@ static int rockchip_pcm_close(struct snd_pcm_substream *substream)
if (prtd->params)
rk29_dma_set_buffdone_fn(prtd->params->channel, NULL);
sg_buf = prtd->curr;
while (sg_buf != NULL) {
@@ -492,19 +464,12 @@ static int rockchip_pcm_mmap(struct snd_pcm_substream *substream,
{
struct snd_pcm_runtime *runtime = substream->runtime;
DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
#ifdef CONFIG_RK_SRAM_DMA
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
return remap_pfn_range(vma, vma->vm_start,
substream->dma_buffer.addr >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
#else
return dma_mmap_writecombine(substream->pcm->card->dev, vma,
runtime->dma_area,
runtime->dma_addr,
runtime->dma_bytes);
#endif
}
static struct snd_pcm_ops rockchip_pcm_ops = {
@@ -519,14 +484,6 @@ static struct snd_pcm_ops rockchip_pcm_ops = {
.mmap = rockchip_pcm_mmap,
};
#if defined(CONFIG_ARCH_RK3066B)
#elif defined(CONFIG_ARCH_RK30)
#define SRAM_DMA_PHYS_PLAYBACK (dma_addr_t)(RK30_IMEM_PHYS + 16*1024)
#define SRAM_DMA_START_PLAYBACK (RK30_IMEM_NONCACHED + 16*1024)
#define SRAM_DMA_PHYS_CAPTURE (dma_addr_t)(SRAM_DMA_PHYS_PLAYBACK + 24*1024)
#define SRAM_DMA_START_CAPTURE (SRAM_DMA_START_PLAYBACK + 24*1024)
#endif
static int rockchip_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
@@ -538,18 +495,8 @@ static int rockchip_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev.dev = pcm->card->dev;
buf->private_data = NULL;
#ifdef CONFIG_RK_SRAM_DMA
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
buf->area = SRAM_DMA_START_PLAYBACK;
buf->addr = SRAM_DMA_PHYS_PLAYBACK;
} else{
buf->area = SRAM_DMA_START_CAPTURE;
buf->addr = SRAM_DMA_PHYS_CAPTURE;
}
#else
buf->area = dma_alloc_writecombine(pcm->card->dev, size,
&buf->addr, GFP_KERNEL);
#endif
if (!buf->area)
return -ENOMEM;
buf->bytes = size;

186
sound/soc/rk29/rk_spdif.c Normal file
View File

@@ -0,0 +1,186 @@
/*$_FOR_ROCKCHIP_RBOX_$*/
/*$_rbox_$_modify_$_huangzhibao for spdif output*/
/*
* smdk_spdif.c -- S/PDIF audio for SMDK
*
* Copyright 2010 Samsung Electronics Co. Ltd.
*
* 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/clk.h>
#include <sound/soc.h>
#include "spdif.h"
#include <mach/iomux.h>
#if 0
#define RK_SPDIF_DBG(x...) printk(KERN_INFO "rk_spdif:"x)
#else
#define RK_SPDIF_DBG(x...) do { } while (0)
#endif
static int set_audio_clock_rate(unsigned long pll_rate,
unsigned long audio_rate)
{
struct clk *hclk_spdif, *sclk_spdif;
#if defined (CONFIG_ARCH_RK30) || (CONFIG_ARCH_RK3188)
hclk_spdif = clk_get(NULL, "hclk_spdif");
if (IS_ERR(hclk_spdif)) {
printk(KERN_ERR "spdif:failed to get hclk_spdif\n");
return -ENOENT;
}
clk_set_rate(hclk_spdif, pll_rate);
clk_put(hclk_spdif);
#endif
sclk_spdif = clk_get(NULL, "spdif");
if (IS_ERR(sclk_spdif)) {
printk(KERN_ERR "spdif:failed to get sclk_spdif\n");
return -ENOENT;
}
clk_set_rate(sclk_spdif, audio_rate);
clk_put(sclk_spdif);
return 0;
}
static int rk_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 *cpu_dai = rtd->cpu_dai;
unsigned long pll_out, rclk_rate;
int ret, ratio;
RK_SPDIF_DBG("spdif:Entered %s\n", __func__);
switch (params_rate(params)) {
case 44100:
pll_out = 11289600;
break;
case 32000:
pll_out = 8192000;
break;
case 48000:
pll_out = 12288000;
break;
case 96000:
pll_out = 24576000;
break;
default:
printk("rk_spdif: params not support\n");
return -EINVAL;
}
ratio = 256;
rclk_rate = params_rate(params) * ratio;
/* Set audio source clock rates */
ret = set_audio_clock_rate(pll_out, rclk_rate);
if (ret < 0)
return ret;
/* Set S/PDIF uses internal source clock */
//ret = snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK,
//rclk_rate, SND_SOC_CLOCK_IN);
//if (ret < 0)
//return ret;
return ret;
}
static struct snd_soc_ops rk_spdif_ops = {
.hw_params = rk_hw_params,
};
static struct snd_soc_dai_link rk_dai = {
.name = "SPDIF",
.stream_name = "SPDIF PCM Playback",
.platform_name = "rockchip-audio",
.cpu_dai_name = "rk-spdif.0",
.codec_dai_name = "dit-hifi",
.codec_name = "spdif-dit",
.ops = &rk_spdif_ops,
};
static struct snd_soc_card rk_spdif = {
.name = "ROCKCHIP-SPDIF",
.dai_link = &rk_dai,
.num_links = 1,
};
static struct platform_device *rk_snd_spdif_dit_device;
static struct platform_device *rk_snd_spdif_device;
static int __init rk_spdif_init(void)
{
int ret;
RK_SPDIF_DBG("Entered %s\n", __func__);
rk_snd_spdif_dit_device = platform_device_alloc("spdif-dit", -1);
if (!rk_snd_spdif_dit_device){
printk("spdif:platform_device_alloc spdif-dit\n");
return -ENOMEM;
}
ret = platform_device_add(rk_snd_spdif_dit_device);
if (ret)
goto err1;
rk_snd_spdif_device = platform_device_alloc("soc-audio", -3);
if (!rk_snd_spdif_device) {
printk("spdif:platform_device_alloc rk_soc-audio\n");
ret = -ENOMEM;
goto err2;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37))
platform_set_drvdata(rk_snd_spdif_device, &rk_spdif);
#else
platform_set_drvdata(rk_snd_spdif_device, &rk_spdif);
rk_spdif.dev = &rk_snd_spdif_device->dev;
#endif
//platform_set_drvdata(rk_snd_spdif_device, &rk_spdif);
ret = platform_device_add(rk_snd_spdif_device);
if (ret)
goto err3;
RK_SPDIF_DBG("rk_spdif_init ok\n");
return ret;
err3:
platform_device_put(rk_snd_spdif_device);
err2:
platform_device_del(rk_snd_spdif_dit_device);
err1:
platform_device_put(rk_snd_spdif_dit_device);
return ret;
}
static void __exit rk_spdif_exit(void)
{
platform_device_unregister(rk_snd_spdif_device);
platform_device_unregister(rk_snd_spdif_dit_device);
}
//using late_initcall to make sure spdif is after board codec. added by zxg.
//module_init(rk_spdif_init);
late_initcall(rk_spdif_init);
module_exit(rk_spdif_exit);
MODULE_AUTHOR("hzb, <hzb@rock-chips.com>");
MODULE_DESCRIPTION("ALSA SoC RK+S/PDIF");
MODULE_LICENSE("GPL");

485
sound/soc/rk29/spdif.c Normal file
View File

@@ -0,0 +1,485 @@
/*$_FOR_ROCKCHIP_RBOX_$*/
/*$_rbox_$_modify_$_huangzhibao for spdif output*/
/* sound/soc/rockchip/spdif.c
*
* ALSA SoC Audio Layer - rockchip S/PDIF Controller driver
*
* Copyright (c) 2010 rockchip Electronics Co. Ltd
* http://www.rockchip.com/
*
* 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.
*/
#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 <linux/version.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/board.h>
#include <mach/hardware.h>
#include <mach/io.h>
#include <mach/gpio.h>
#include <mach/iomux.h>
#if defined (CONFIG_ARCH_RK29)
#include <mach/rk29-dma-pl330.h>
#endif
#if defined (CONFIG_ARCH_RK30)
#include <mach/dma-pl330.h>
#endif
#if defined (CONFIG_ARCH_RK3188)
#include <mach/dma-pl330.h>
#endif
#include "rk29_pcm.h"
#include "spdif.h"
#if 1
#define RK_SPDIF_DBG(x...) printk(KERN_INFO "spdif:"x)
#else
#define RK_SPDIF_DBG(x...) do { } while (0)
#endif
/* Registers */
#define CFGR 0x00
#define SDBLR 0x04
#define DMACR 0x08
#define INTCR 0x0C
#define INTSR 0x10
#define XFER 0x18
#define SMPDR 0x20
#define DATA_OUTBUF 0x20
#define CFGR_MASK 0x0ffffff
#define CFGR_VALID_DATA_16bit (00)
#define CFGR_VALID_DATA_20bit (01)
#define CFGR_VALID_DATA_24bit (10)
#define CFGR_VALID_DATA_MASK (11)
#define CFGR_HALFWORD_TX_ENABLE (0x1 << 2)
#define CFGR_HALFWORD_TX_DISABLE (0x0 << 2)
#define CFGR_HALFWORD_TX_MASK (0x1 << 2)
#define CFGR_CLK_RATE_MASK (0xFF<<16)
#define CFGR_JUSTIFIED_RIGHT (0<<3)
#define CFGR_JUSTIFIED_LEFT (1<<3)
#define CFGR_JUSTIFIED_MASK (1<<3)
#define XFER_TRAN_STOP (0)
#define XFER_TRAN_START (1)
#define XFER_MASK (1)
#define DMACR_TRAN_DMA_DISABLE (0<<5)
#define DMACR_TRAN_DMA_ENABLE (1<<5)
#define DMACR_TRAN_DMA_CTL_MASK (1<<5)
#define DMACR_TRAN_DATA_LEVEL 0x10
#define DMACR_TRAN_DATA_LEVEL_MASK 0x1F
#define DMACR_TRAN_DMA_MASK (0x3F)
struct rockchip_spdif_info {
spinlock_t lock;
struct device *dev;
void __iomem *regs;
unsigned long clk_rate;
struct clk *hclk;
struct clk *clk;
u32 saved_clkcon;
u32 saved_con;
u32 saved_cstas;
struct rockchip_pcm_dma_params *dma_playback;
};
static struct rk29_dma_client spdif_dma_client_out = {
.name = "SPDIF Stereo out"
};
static struct rockchip_pcm_dma_params spdif_stereo_out;
static struct rockchip_spdif_info spdif_info;
static inline struct rockchip_spdif_info *to_info(struct snd_soc_dai *cpu_dai)
{
return snd_soc_dai_get_drvdata(cpu_dai);
}
static void spdif_snd_txctrl(struct rockchip_spdif_info *spdif, int on)
{
void __iomem *regs = spdif->regs;
u32 opr,xfer;
RK_SPDIF_DBG( "Entered %s\n", __func__);
xfer = readl(regs + XFER) & XFER_MASK;
opr = readl(regs + DMACR) & DMACR_TRAN_DMA_MASK & (~DMACR_TRAN_DMA_CTL_MASK);
if (on){
xfer |= XFER_TRAN_START;
opr |= DMACR_TRAN_DMA_ENABLE;
writel(xfer, regs + XFER);
writel(opr|0x10, regs + DMACR);
RK_SPDIF_DBG("on xfer=0x%x,opr=0x%x\n",readl(regs + XFER),readl(regs + DMACR));
} else{
xfer &= ~XFER_TRAN_START;
opr &= ~DMACR_TRAN_DMA_ENABLE;
writel(xfer, regs + XFER);
writel(opr|0x10, regs + DMACR);
}
}
static int spdif_set_syclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
struct rockchip_spdif_info *spdif = to_info(cpu_dai);
u32 clkcon;
RK_SPDIF_DBG("Entered %s\n", __func__);
spdif->clk_rate = freq;
return 0;
}
static int spdif_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct rockchip_spdif_info *spdif = to_info(rtd->cpu_dai);
unsigned long flags;
RK_SPDIF_DBG( "Entered %s\n", __func__);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
spin_lock_irqsave(&spdif->lock, flags);
spdif_snd_txctrl(spdif, 1);
spin_unlock_irqrestore(&spdif->lock, flags);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
spin_lock_irqsave(&spdif->lock, flags);
spdif_snd_txctrl(spdif, 0);
spin_unlock_irqrestore(&spdif->lock, flags);
break;
default:
return -EINVAL;
}
return 0;
}
static int spdif_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 rockchip_spdif_info *spdif = to_info(rtd->cpu_dai);
void __iomem *regs = spdif->regs;
struct rockchip_pcm_dma_params *dma_data;
unsigned long flags;
int i, cfgr, dmac;
RK_SPDIF_DBG("Entered %s\n", __func__);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
dma_data = spdif->dma_playback;
else {
printk("spdif:Capture is not supported\n");
return -EINVAL;
}
snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data);
spin_lock_irqsave(&spdif->lock, flags);
cfgr = readl(regs + CFGR) & CFGR_VALID_DATA_MASK;
cfgr &= ~CFGR_VALID_DATA_MASK;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
cfgr |= CFGR_VALID_DATA_16bit;
break;
case SNDRV_PCM_FMTBIT_S20_3LE :
cfgr |= CFGR_VALID_DATA_20bit;
break;
case SNDRV_PCM_FORMAT_S24_LE:
cfgr |= CFGR_VALID_DATA_24bit;
break;
default:
goto err;
}
cfgr &= ~CFGR_HALFWORD_TX_MASK;
cfgr |= CFGR_HALFWORD_TX_ENABLE;
cfgr &= ~CFGR_CLK_RATE_MASK;
cfgr |= (1<<16);
cfgr &= ~CFGR_JUSTIFIED_MASK;
cfgr |= CFGR_JUSTIFIED_RIGHT;
writel(cfgr, regs + CFGR);
dmac = readl(regs + DMACR) & DMACR_TRAN_DMA_MASK & (~DMACR_TRAN_DATA_LEVEL_MASK);
dmac |= 0x10;
writel(dmac, regs + DMACR);
spin_unlock_irqrestore(&spdif->lock, flags);
return 0;
err:
spin_unlock_irqrestore(&spdif->lock, flags);
return -EINVAL;
}
static void spdif_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct rockchip_spdif_info *spdif = to_info(rtd->cpu_dai);
void __iomem *regs = spdif->regs;
u32 con, clkcon;
RK_SPDIF_DBG( "spdif:Entered %s\n", __func__);
}
#ifdef CONFIG_PM
static int spdif_suspend(struct snd_soc_dai *cpu_dai)
{
struct rockchip_spdif_info *spdif = to_info(cpu_dai);
u32 con = spdif->saved_con;
RK_SPDIF_DBG( "spdif:Entered %s\n", __func__);
return 0;
}
static int spdif_resume(struct snd_soc_dai *cpu_dai)
{
struct rockchip_spdif_info *spdif = to_info(cpu_dai);
RK_SPDIF_DBG( "spdif:Entered %s\n", __func__);
return 0;
}
#else
#define spdif_suspend NULL
#define spdif_resume NULL
#endif
static struct snd_soc_dai_ops spdif_dai_ops = {
.set_sysclk = spdif_set_syclk,
.trigger = spdif_trigger,
.hw_params = spdif_hw_params,
.shutdown = spdif_shutdown,
};
struct snd_soc_dai_driver rockchip_spdif_dai = {
.name = "rk-spdif",
.playback = {
.stream_name = "SPDIF Playback",
.channels_min = 2,
.channels_max = 2,
.rates = (SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_96000),
.formats = SNDRV_PCM_FMTBIT_S16_LE|
SNDRV_PCM_FMTBIT_S20_3LE|
SNDRV_PCM_FMTBIT_S24_LE, },
.ops = &spdif_dai_ops,
.suspend = spdif_suspend,
.resume = spdif_resume,
};
static __devinit int spdif_probe(struct platform_device *pdev)
{
struct s3c_audio_pdata *spdif_pdata;
struct resource *mem_res, *dma_res;
struct rockchip_spdif_info *spdif;
int ret;
spdif_pdata = pdev->dev.platform_data;
RK_SPDIF_DBG("Entered %s\n", __func__);
#if defined (CONFIG_ARCH_RK29)
rk29_mux_api_set(GPIO4A7_SPDIFTX_NAME, GPIO4L_SPDIF_TX);
#endif
#if defined (CONFIG_ARCH_RK30)
rk30_mux_api_set(GPIO1B2_SPDIFTX_NAME, GPIO1B_SPDIF_TX);
#elif defined (CONFIG_ARCH_RK3188)
iomux_set(SPDIF_TX);
#endif
dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "spdif_dma");
if (!dma_res) {
printk("spdif:Unable to get dma resource.\n");
return -ENXIO;
}
mem_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "spdif_base");
if (!mem_res) {
printk("spdif:Unable to get register resource.\n");
return -ENXIO;
}
spdif = &spdif_info;
spdif->dev = &pdev->dev;
spin_lock_init(&spdif->lock);
spdif->clk = clk_get(&pdev->dev, "spdif");
if (IS_ERR(spdif->clk)) {
printk("spdif:failed to get internal source clock\n");
ret = -ENOENT;
goto err1;
}
clk_enable(spdif->clk);
clk_set_rate(spdif->clk, 11289600);
#if 1// defined (CONFIG_ARCH_RK30)
spdif->hclk = clk_get(&pdev->dev, "hclk_spdif");
if (IS_ERR(spdif->hclk)) {
printk("spdif:failed to get spdif hclk\n");
ret = -ENOENT;
goto err0;
}
clk_enable(spdif->hclk);
clk_set_rate(spdif->hclk, 11289600);
#endif
/* Request S/PDIF Register's memory region */
if (!request_mem_region(mem_res->start,
resource_size(mem_res), "rockchip-spdif")) {
printk("spdif:Unable to request register region\n");
ret = -EBUSY;
goto err2;
}
spdif->regs = ioremap(mem_res->start, mem_res->end - mem_res->start + 1);
if (spdif->regs == NULL) {
printk("spdif:Cannot ioremap registers\n");
ret = -ENXIO;
goto err3;
}
dev_set_drvdata(&pdev->dev, spdif);
ret = snd_soc_register_dai(&pdev->dev, &rockchip_spdif_dai);
if (ret != 0) {
printk("spdif:fail to register dai\n");
goto err4;
}
spdif_stereo_out.dma_size = 4;
spdif_stereo_out.client = &spdif_dma_client_out;
spdif_stereo_out.dma_addr = mem_res->start + DATA_OUTBUF;
spdif_stereo_out.channel = dma_res->start;
spdif->dma_playback = &spdif_stereo_out;
#ifdef CONFIG_SND_DMA_EVENT_STATIC
WARN_ON(rk29_dma_request(spdif_stereo_out.channel, spdif_stereo_out.client, NULL));
#endif
RK_SPDIF_DBG("spdif:spdif probe ok!\n");
return 0;
err4:
iounmap(spdif->regs);
err3:
release_mem_region(mem_res->start, resource_size(mem_res));
err2:
clk_disable(spdif->clk);
clk_put(spdif->clk);
err1:
clk_disable(spdif->hclk);
clk_put(spdif->hclk);
#if 1//defined (CONFIG_ARCH_RK30)
err0:
#endif
return ret;
}
static __devexit int spdif_remove(struct platform_device *pdev)
{
struct rockchip_spdif_info *spdif = &spdif_info;
struct resource *mem_res;
RK_SPDIF_DBG("Entered %s\n", __func__);
snd_soc_unregister_dai(&pdev->dev);
iounmap(spdif->regs);
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (mem_res)
release_mem_region(mem_res->start, resource_size(mem_res));
clk_disable(spdif->clk);
clk_put(spdif->clk);
clk_disable(spdif->hclk);
clk_put(spdif->hclk);
return 0;
}
static struct platform_driver rockchip_spdif_driver = {
.probe = spdif_probe,
.remove = spdif_remove,
.driver = {
.name = "rk-spdif",
.owner = THIS_MODULE,
},
};
static int __init spdif_init(void)
{
RK_SPDIF_DBG("Entered %s\n", __func__);
return platform_driver_register(&rockchip_spdif_driver);
}
module_init(spdif_init);
static void __exit spdif_exit(void)
{
RK_SPDIF_DBG("Entered %s\n", __func__);
platform_driver_unregister(&rockchip_spdif_driver);
}
module_exit(spdif_exit);
MODULE_AUTHOR("Seungwhan Youn, <sw.youn@rockchip.com>");
MODULE_DESCRIPTION("rockchip S/PDIF Controller Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:rockchip-spdif");

18
sound/soc/rk29/spdif.h Normal file
View File

@@ -0,0 +1,18 @@
/*$_FOR_ROCKCHIP_RBOX_$*/
/*$_rbox_$_modify_$_huangzhibao for spdif output*/
/*
*
* ALSA SoC Audio Layer -
*
*
* 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 __SND_SOC_SAMSUNG_SPDIF_H
#define __SND_SOC_SAMSUNG_SPDIF_H __FILE__
#endif /* __SND_SOC_RK_SPDIF_H */