From 20b654aab5ddc3650aa3bb7d8f5359c43df8567d Mon Sep 17 00:00:00 2001 From: yzq Date: Sun, 28 Apr 2013 17:47:53 +0800 Subject: [PATCH] rk3188: spdif support --- arch/arm/mach-rk30/devices.c | 33 +++ sound/soc/rk29/Kconfig | 12 +- sound/soc/rk29/Makefile | 6 +- sound/soc/rk29/rk29_pcm.c | 241 +++++++---------- sound/soc/rk29/rk_spdif.c | 186 ++++++++++++++ sound/soc/rk29/spdif.c | 485 +++++++++++++++++++++++++++++++++++ sound/soc/rk29/spdif.h | 18 ++ 7 files changed, 831 insertions(+), 150 deletions(-) create mode 100644 sound/soc/rk29/rk_spdif.c create mode 100644 sound/soc/rk29/spdif.c create mode 100644 sound/soc/rk29/spdif.h diff --git a/arch/arm/mach-rk30/devices.c b/arch/arm/mach-rk30/devices.c index 32ddb4b9f1a4..a51ae35545c6 100755 --- a/arch/arm/mach-rk30/devices.c +++ b/arch/arm/mach-rk30/devices.c @@ -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 diff --git a/sound/soc/rk29/Kconfig b/sound/soc/rk29/Kconfig index 739f55e24c49..682fb18917b4 100755 --- a/sound/soc/rk29/Kconfig +++ b/sound/soc/rk29/Kconfig @@ -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 diff --git a/sound/soc/rk29/Makefile b/sound/soc/rk29/Makefile index cfd5e3bbc1a2..728bdae8dcde 100755 --- a/sound/soc/rk29/Makefile +++ b/sound/soc/rk29/Makefile @@ -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 diff --git a/sound/soc/rk29/rk29_pcm.c b/sound/soc/rk29/rk29_pcm.c index 69b871a3bd98..4c37b4dd0ccf 100755 --- a/sound/soc/rk29/rk29_pcm.c +++ b/sound/soc/rk29/rk29_pcm.c @@ -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; - } + 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); - } + 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; - } + 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; diff --git a/sound/soc/rk29/rk_spdif.c b/sound/soc/rk29/rk_spdif.c new file mode 100644 index 000000000000..6ec311ed63bd --- /dev/null +++ b/sound/soc/rk29/rk_spdif.c @@ -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 + +#include + +#include "spdif.h" +#include + +#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, "); +MODULE_DESCRIPTION("ALSA SoC RK+S/PDIF"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/rk29/spdif.c b/sound/soc/rk29/spdif.c new file mode 100644 index 000000000000..53e4e7b12992 --- /dev/null +++ b/sound/soc/rk29/spdif.c @@ -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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if defined (CONFIG_ARCH_RK29) +#include +#endif + +#if defined (CONFIG_ARCH_RK30) +#include +#endif + +#if defined (CONFIG_ARCH_RK3188) +#include +#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, "); +MODULE_DESCRIPTION("rockchip S/PDIF Controller Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rockchip-spdif"); diff --git a/sound/soc/rk29/spdif.h b/sound/soc/rk29/spdif.h new file mode 100644 index 000000000000..d669f7defa8e --- /dev/null +++ b/sound/soc/rk29/spdif.h @@ -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 */ -- 2.34.1