1 /*$_FOR_ROCKCHIP_RBOX_$*/
2 /*$_rbox_$_modify_$_huangzhibao for spdif output*/
4 /* sound/soc/rockchip/spdif.c
6 * ALSA SoC Audio Layer - rockchip S/PDIF Controller driver
8 * Copyright (c) 2010 rockchip Electronics Co. Ltd
9 * http://www.rockchip.com/
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
16 #include <linux/init.h>
17 #include <linux/module.h>
18 #include <linux/interrupt.h>
19 #include <linux/device.h>
20 #include <linux/delay.h>
21 #include <linux/clk.h>
22 #include <linux/version.h>
25 #include <sound/core.h>
26 #include <sound/pcm.h>
27 #include <sound/pcm_params.h>
28 #include <sound/initval.h>
29 #include <sound/soc.h>
32 #include <mach/board.h>
33 #include <mach/hardware.h>
35 #include <mach/gpio.h>
36 #include <mach/iomux.h>
38 #if defined (CONFIG_ARCH_RK29)
39 #include <mach/rk29-dma-pl330.h>
42 #if defined (CONFIG_ARCH_RK30)
43 #include <mach/dma-pl330.h>
46 #if defined (CONFIG_ARCH_RK3188)
47 #include <mach/dma-pl330.h>
53 #define RK_SPDIF_DBG(x...) printk(KERN_INFO "rk_spdif:"x)
55 #define RK_SPDIF_DBG(x...) do { } while (0)
68 #define DATA_OUTBUF 0x20
70 #define CFGR_MASK 0x0ffffff
71 #define CFGR_VALID_DATA_16bit (00)
72 #define CFGR_VALID_DATA_20bit (01)
73 #define CFGR_VALID_DATA_24bit (10)
74 #define CFGR_VALID_DATA_MASK (11)
76 #define CFGR_HALFWORD_TX_ENABLE (0x1 << 2)
77 #define CFGR_HALFWORD_TX_DISABLE (0x0 << 2)
78 #define CFGR_HALFWORD_TX_MASK (0x1 << 2)
80 #define CFGR_CLK_RATE_MASK (0xFF<<16)
82 #define CFGR_JUSTIFIED_RIGHT (0<<3)
83 #define CFGR_JUSTIFIED_LEFT (1<<3)
84 #define CFGR_JUSTIFIED_MASK (1<<3)
86 #define XFER_TRAN_STOP (0)
87 #define XFER_TRAN_START (1)
90 #define DMACR_TRAN_DMA_DISABLE (0<<5)
91 #define DMACR_TRAN_DMA_ENABLE (1<<5)
92 #define DMACR_TRAN_DMA_CTL_MASK (1<<5)
94 #define DMACR_TRAN_DATA_LEVEL 0x10
95 #define DMACR_TRAN_DATA_LEVEL_MASK 0x1F
97 #define DMACR_TRAN_DMA_MASK (0x3F)
101 struct rockchip_spdif_info {
105 unsigned long clk_rate;
111 struct rockchip_pcm_dma_params *dma_playback;
114 static struct rk29_dma_client spdif_dma_client_out = {
115 .name = "SPDIF Stereo out"
118 static struct rockchip_pcm_dma_params spdif_stereo_out;
120 static struct rockchip_spdif_info spdif_info;
122 static inline struct rockchip_spdif_info *to_info(struct snd_soc_dai *cpu_dai)
124 return snd_soc_dai_get_drvdata(cpu_dai);
127 static void spdif_snd_txctrl(struct rockchip_spdif_info *spdif, int on)
129 void __iomem *regs = spdif->regs;
132 RK_SPDIF_DBG( "Entered %s\n", __func__);
134 xfer = readl(regs + XFER) & XFER_MASK;
135 opr = readl(regs + DMACR) & DMACR_TRAN_DMA_MASK & (~DMACR_TRAN_DMA_CTL_MASK);
138 xfer |= XFER_TRAN_START;
139 opr |= DMACR_TRAN_DMA_ENABLE;
140 writel(xfer, regs + XFER);
141 writel(opr|0x10, regs + DMACR);
142 RK_SPDIF_DBG("on xfer=0x%x,opr=0x%x\n",readl(regs + XFER),readl(regs + DMACR));
144 xfer &= ~XFER_TRAN_START;
145 opr &= ~DMACR_TRAN_DMA_ENABLE;
146 writel(xfer, regs + XFER);
147 writel(opr|0x10, regs + DMACR);
151 static int spdif_set_syclk(struct snd_soc_dai *cpu_dai,
152 int clk_id, unsigned int freq, int dir)
154 struct rockchip_spdif_info *spdif = to_info(cpu_dai);
157 RK_SPDIF_DBG("Entered %s\n", __func__);
159 spdif->clk_rate = freq;
164 static int spdif_trigger(struct snd_pcm_substream *substream, int cmd,
165 struct snd_soc_dai *dai)
167 struct snd_soc_pcm_runtime *rtd = substream->private_data;
168 struct rockchip_spdif_info *spdif = to_info(rtd->cpu_dai);
171 RK_SPDIF_DBG( "Entered %s\n", __func__);
174 case SNDRV_PCM_TRIGGER_START:
175 case SNDRV_PCM_TRIGGER_RESUME:
176 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
177 spin_lock_irqsave(&spdif->lock, flags);
178 spdif_snd_txctrl(spdif, 1);
179 spin_unlock_irqrestore(&spdif->lock, flags);
181 case SNDRV_PCM_TRIGGER_STOP:
182 case SNDRV_PCM_TRIGGER_SUSPEND:
183 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
184 spin_lock_irqsave(&spdif->lock, flags);
185 spdif_snd_txctrl(spdif, 0);
186 spin_unlock_irqrestore(&spdif->lock, flags);
196 static int spdif_hw_params(struct snd_pcm_substream *substream,
197 struct snd_pcm_hw_params *params,
198 struct snd_soc_dai *socdai)
200 struct snd_soc_pcm_runtime *rtd = substream->private_data;
201 struct rockchip_spdif_info *spdif = to_info(rtd->cpu_dai);
202 void __iomem *regs = spdif->regs;
203 struct rockchip_pcm_dma_params *dma_data;
207 RK_SPDIF_DBG("Entered %s\n", __func__);
209 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
210 dma_data = spdif->dma_playback;
212 printk("spdif:Capture is not supported\n");
216 snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data);
217 spin_lock_irqsave(&spdif->lock, flags);
219 cfgr = readl(regs + CFGR) & CFGR_VALID_DATA_MASK;
221 cfgr &= ~CFGR_VALID_DATA_MASK;
222 switch (params_format(params)) {
223 case SNDRV_PCM_FORMAT_S16_LE:
224 cfgr |= CFGR_VALID_DATA_16bit;
226 case SNDRV_PCM_FMTBIT_S20_3LE :
227 cfgr |= CFGR_VALID_DATA_20bit;
229 case SNDRV_PCM_FORMAT_S24_LE:
230 cfgr |= CFGR_VALID_DATA_24bit;
236 cfgr &= ~CFGR_HALFWORD_TX_MASK;
237 cfgr |= CFGR_HALFWORD_TX_ENABLE;
239 cfgr &= ~CFGR_CLK_RATE_MASK;
242 cfgr &= ~CFGR_JUSTIFIED_MASK;
243 cfgr |= CFGR_JUSTIFIED_RIGHT;
245 writel(cfgr, regs + CFGR);
247 dmac = readl(regs + DMACR) & DMACR_TRAN_DMA_MASK & (~DMACR_TRAN_DATA_LEVEL_MASK);
249 writel(dmac, regs + DMACR);
251 spin_unlock_irqrestore(&spdif->lock, flags);
255 spin_unlock_irqrestore(&spdif->lock, flags);
259 static void spdif_shutdown(struct snd_pcm_substream *substream,
260 struct snd_soc_dai *dai)
262 struct snd_soc_pcm_runtime *rtd = substream->private_data;
263 struct rockchip_spdif_info *spdif = to_info(rtd->cpu_dai);
264 void __iomem *regs = spdif->regs;
267 RK_SPDIF_DBG( "spdif:Entered %s\n", __func__);
272 static int spdif_suspend(struct snd_soc_dai *cpu_dai)
274 struct rockchip_spdif_info *spdif = to_info(cpu_dai);
275 u32 con = spdif->saved_con;
277 RK_SPDIF_DBG( "spdif:Entered %s\n", __func__);
282 static int spdif_resume(struct snd_soc_dai *cpu_dai)
284 struct rockchip_spdif_info *spdif = to_info(cpu_dai);
286 RK_SPDIF_DBG( "spdif:Entered %s\n", __func__);
291 #define spdif_suspend NULL
292 #define spdif_resume NULL
295 static struct snd_soc_dai_ops spdif_dai_ops = {
296 .set_sysclk = spdif_set_syclk,
297 .trigger = spdif_trigger,
298 .hw_params = spdif_hw_params,
299 .shutdown = spdif_shutdown,
302 struct snd_soc_dai_driver rockchip_spdif_dai = {
305 .stream_name = "SPDIF Playback",
308 .rates = (SNDRV_PCM_RATE_32000 |
309 SNDRV_PCM_RATE_44100 |
310 SNDRV_PCM_RATE_48000 |
311 SNDRV_PCM_RATE_96000),
312 .formats = SNDRV_PCM_FMTBIT_S16_LE|
313 SNDRV_PCM_FMTBIT_S20_3LE|
314 SNDRV_PCM_FMTBIT_S24_LE, },
315 .ops = &spdif_dai_ops,
316 .suspend = spdif_suspend,
317 .resume = spdif_resume,
321 static __devinit int spdif_probe(struct platform_device *pdev)
323 struct s3c_audio_pdata *spdif_pdata;
324 struct resource *mem_res, *dma_res;
325 struct rockchip_spdif_info *spdif;
328 spdif_pdata = pdev->dev.platform_data;
330 RK_SPDIF_DBG("Entered %s\n", __func__);
332 #if defined (CONFIG_ARCH_RK29)
333 rk29_mux_api_set(GPIO4A7_SPDIFTX_NAME, GPIO4L_SPDIF_TX);
336 #if defined (CONFIG_ARCH_RK30)
337 #if defined (CONFIG_ARCH_RK3066B)
340 rk30_mux_api_set(GPIO1B2_SPDIFTX_NAME, GPIO1B_SPDIF_TX);
342 #elif defined (CONFIG_ARCH_RK3188)
346 dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "spdif_dma");
348 printk("spdif:Unable to get dma resource.\n");
352 mem_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "spdif_base");
354 printk("spdif:Unable to get register resource.\n");
359 spdif->dev = &pdev->dev;
361 spin_lock_init(&spdif->lock);
363 spdif->clk = clk_get(&pdev->dev, "spdif");
364 if (IS_ERR(spdif->clk)) {
365 printk("spdif:failed to get internal source clock\n");
369 clk_enable(spdif->clk);
370 clk_set_rate(spdif->clk, 11289600);
372 spdif->hclk = clk_get(&pdev->dev, "hclk_spdif");
373 if (IS_ERR(spdif->hclk)) {
374 printk("spdif:failed to get spdif hclk\n");
378 clk_enable(spdif->hclk);
379 clk_set_rate(spdif->hclk, 11289600);
381 /* Request S/PDIF Register's memory region */
382 if (!request_mem_region(mem_res->start,
383 resource_size(mem_res), "rockchip-spdif")) {
384 printk("spdif:Unable to request register region\n");
389 spdif->regs = ioremap(mem_res->start, mem_res->end - mem_res->start + 1);
390 if (spdif->regs == NULL) {
391 printk("spdif:Cannot ioremap registers\n");
396 dev_set_drvdata(&pdev->dev, spdif);
398 ret = snd_soc_register_dai(&pdev->dev, &rockchip_spdif_dai);
400 printk("spdif:fail to register dai\n");
404 spdif_stereo_out.dma_size = 4;
405 spdif_stereo_out.client = &spdif_dma_client_out;
406 spdif_stereo_out.dma_addr = mem_res->start + DATA_OUTBUF;
407 spdif_stereo_out.channel = dma_res->start;
409 spdif->dma_playback = &spdif_stereo_out;
410 #ifdef CONFIG_SND_I2S_DMA_EVENT_STATIC
411 WARN_ON(rk29_dma_request(spdif_stereo_out.channel, spdif_stereo_out.client, NULL));
414 RK_SPDIF_DBG("spdif:spdif probe ok!\n");
419 iounmap(spdif->regs);
421 release_mem_region(mem_res->start, resource_size(mem_res));
423 clk_disable(spdif->clk);
426 clk_disable(spdif->hclk);
427 clk_put(spdif->hclk);
432 static __devexit int spdif_remove(struct platform_device *pdev)
434 struct rockchip_spdif_info *spdif = &spdif_info;
435 struct resource *mem_res;
437 RK_SPDIF_DBG("Entered %s\n", __func__);
439 snd_soc_unregister_dai(&pdev->dev);
441 iounmap(spdif->regs);
443 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
445 release_mem_region(mem_res->start, resource_size(mem_res));
447 clk_disable(spdif->clk);
449 clk_disable(spdif->hclk);
450 clk_put(spdif->hclk);
456 static struct platform_driver rockchip_spdif_driver = {
457 .probe = spdif_probe,
458 .remove = spdif_remove,
461 .owner = THIS_MODULE,
466 static int __init spdif_init(void)
468 RK_SPDIF_DBG("Entered %s\n", __func__);
469 return platform_driver_register(&rockchip_spdif_driver);
471 module_init(spdif_init);
473 static void __exit spdif_exit(void)
475 RK_SPDIF_DBG("Entered %s\n", __func__);
476 platform_driver_unregister(&rockchip_spdif_driver);
478 module_exit(spdif_exit);
480 MODULE_AUTHOR("Seungwhan Youn, <sw.youn@rockchip.com>");
481 MODULE_DESCRIPTION("rockchip S/PDIF Controller Driver");
482 MODULE_LICENSE("GPL");
483 MODULE_ALIAS("platform:rockchip-spdif");