2 * rk2818_i2s.c -- ALSA SoC ROCKCHIP IIS Audio Layer Platform driver
4 * Driver for rockchip iis audio
5 * Copyright (C) 2009 lhh
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
15 #include <linux/init.h>
16 #include <linux/module.h>
17 #include <linux/interrupt.h>
18 #include <linux/device.h>
19 #include <linux/delay.h>
20 #include <linux/clk.h>
23 #include <sound/core.h>
24 #include <sound/pcm.h>
25 #include <sound/pcm_params.h>
26 #include <sound/initval.h>
27 #include <sound/soc.h>
29 #include <mach/hardware.h>
31 #include <mach/rk2818_iomap.h>
32 #include <mach/iomux.h>
35 #include "rk2818_pcm.h"
36 #include "rk2818_i2s.h"
40 #define DBG(x...) printk(KERN_INFO x)
42 #define DBG(x...) do { } while (0)
45 #define pheadi2s ((pI2S_REG)(i2s->regs))
47 struct rockchip_i2s_info {
58 static struct rockchip_i2s_info rockchip_i2s;
60 static struct rockchip_dma_client rockchip_dma_client_out = {
61 //.name = "I2S PCM Stereo out"
65 static struct rockchip_dma_client rockchip_dma_client_in = {
66 //.name = "I2S PCM Stereo in"
70 static struct rockchip_pcm_dma_params rockchip_i2s_pcm_stereo_out = {
71 .client = &rockchip_dma_client_out,
72 .channel = RK28_DMA_CH4, ///0, //DMACH_I2S_OUT,
73 .dma_addr = RK2818_I2S_PHYS + I2S_TXR_BUFF,
77 static struct rockchip_pcm_dma_params rockchip_i2s_pcm_stereo_in = {
78 .client = &rockchip_dma_client_in,
79 .channel = RK28_DMA_CH5, ///1, //DMACH_I2S_IN,
80 .dma_addr = RK2818_I2S_PHYS + I2S_RXR_BUFF,
88 *Turn on or off the transmission path.
90 static void rockchip_snd_txctrl(int on)
92 struct rockchip_i2s_info *i2s = &rockchip_i2s;
95 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
97 opr = readl(&(pheadi2s->I2S_OPR));
98 fifosts = readl(&(pheadi2s->I2S_FIFOSTS));
99 fifosts = (fifosts & (~(0x0f<<16))) | TX_HALF_FULL | RX_HALF_FULL;
100 writel(fifosts, &(pheadi2s->I2S_FIFOSTS));
103 opr = (opr & (~(RESET_RX | I2S_DMA_REQ2_DISABLE | TX_START | RX_START))) | (RESET_TX | I2S_DMA_REQ1_DISABLE);
104 writel(opr, &(pheadi2s->I2S_OPR));
106 opr = (opr & (~(I2S_DMA_REQ1_DISABLE | I2S_DMA_REQ1_RX_ENABLE | RX_START))) | I2S_DMA_REQ1_ENABLE | I2S_DMA_REQ1_TX_ENABLE | TX_START;
107 writel(opr, &(pheadi2s->I2S_OPR));
111 opr = (opr & (~TX_START)) | I2S_DMA_REQ1_DISABLE;
112 writel(opr, &(pheadi2s->I2S_OPR));
115 static void rockchip_snd_rxctrl(int on)
117 struct rockchip_i2s_info *i2s = &rockchip_i2s;
120 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
121 opr = readl(&(pheadi2s->I2S_OPR));
122 fifosts = readl(&(pheadi2s->I2S_FIFOSTS));
123 fifosts = (fifosts & (~(0x0f<<16))) | TX_HALF_FULL | RX_HALF_FULL;
124 writel(fifosts, &(pheadi2s->I2S_FIFOSTS));
127 opr = (opr & (~(RESET_TX | I2S_DMA_REQ1_DISABLE| TX_START | RX_START))) | (RESET_RX | I2S_DMA_REQ2_DISABLE);
128 writel(opr, &(pheadi2s->I2S_OPR));
130 opr = (opr & (~(I2S_DMA_REQ2_DISABLE | I2S_DMA_REQ2_TX_ENABLE | TX_START))) | I2S_DMA_REQ2_ENABLE | I2S_DMA_REQ2_RX_ENABLE | RX_START;
131 writel(opr, &(pheadi2s->I2S_OPR));
135 opr = (opr & (~RX_START)) | I2S_DMA_REQ2_DISABLE;
136 writel(opr, &(pheadi2s->I2S_OPR));
141 * Set Rockchip I2S DAI format
143 static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
146 struct rockchip_i2s_info *i2s = &rockchip_i2s;
148 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
150 tx_ctl = readl(&(pheadi2s->I2S_TXCTL));
151 tx_ctl &= (~MASTER_MODE);
153 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
154 case SND_SOC_DAIFMT_CBM_CFM:
155 tx_ctl |= MASTER_MODE;
157 case SND_SOC_DAIFMT_CBS_CFS:
158 tx_ctl |= SLAVE_MODE;
161 DBG("unknwon master/slave format\n");
164 tx_ctl &= ~IISMOD_SDF_MASK;
166 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
167 case SND_SOC_DAIFMT_RIGHT_J:
168 tx_ctl |= RIGHT_JUSTIFIED;
170 case SND_SOC_DAIFMT_LEFT_J:
171 tx_ctl |= LEFT_JUSTIFIED;
173 case SND_SOC_DAIFMT_I2S:
177 DBG("Unknown data format\n");
180 tx_ctl = tx_ctl & (~(0xff<<8)) & (~(0x03<<16)) & (~(1<<3));
181 tx_ctl = tx_ctl | OVERSAMPLING_RATE_64FS | SCK_RATE4 | STEREO_MODE;
182 writel(tx_ctl, &(pheadi2s->I2S_TXCTL));
183 rx_ctl = tx_ctl | CLEAR_RXFIFO;
184 writel(rx_ctl, &(pheadi2s->I2S_RXCTL));
188 static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
189 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
191 struct snd_soc_pcm_runtime *rtd = substream->private_data;
192 struct rockchip_i2s_info *i2s = &rockchip_i2s;
195 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
196 /*by Vincent Hsiung for EQ Vol Change*/
197 #define HW_PARAMS_FLAG_EQVOL_ON 0x21
198 #define HW_PARAMS_FLAG_EQVOL_OFF 0x22
199 if ((params->flags == HW_PARAMS_FLAG_EQVOL_ON)||(params->flags == HW_PARAMS_FLAG_EQVOL_OFF))
204 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
205 rtd->dai->cpu_dai->dma_data = &rockchip_i2s_pcm_stereo_out;
207 rtd->dai->cpu_dai->dma_data = &rockchip_i2s_pcm_stereo_in;
209 /* Working copies of register */
210 iismod = readl(&(pheadi2s->I2S_TXCTL));
211 iismod &= (~SAMPLE_DATA_MASK);
212 switch (params_format(params)) {
213 case SNDRV_PCM_FORMAT_S8:
214 iismod |= SAMPLE_DATA_8bit;
216 case SNDRV_PCM_FORMAT_S16_LE:
217 iismod |= SAMPLE_DATA_16bit;
220 /*stereo mode MCLK/SCK=4*/
221 iismod = iismod & (~(0xff<<8)) & (~(0x03<<16)) & (~(1<<3));
222 iismod = iismod | OVERSAMPLING_RATE_64FS | SCK_RATE4 | STEREO_MODE;
224 writel(iismod, &(pheadi2s->I2S_TXCTL));
225 iismod = iismod | CLEAR_RXFIFO;
226 writel(iismod, &(pheadi2s->I2S_RXCTL));
231 static int rockchip_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
235 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
238 case SNDRV_PCM_TRIGGER_START:
239 case SNDRV_PCM_TRIGGER_RESUME:
240 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
241 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
242 rockchip_snd_rxctrl(1);
244 rockchip_snd_txctrl(1);
246 case SNDRV_PCM_TRIGGER_STOP:
247 case SNDRV_PCM_TRIGGER_SUSPEND:
248 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
249 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
250 rockchip_snd_rxctrl(0);
252 rockchip_snd_txctrl(0);
262 * Set Rockchip Clock source
264 static int rockchip_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
265 int clk_id, unsigned int freq, int dir)
267 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
268 /*add scu clk source and enable clk*/
274 * Set Rockchip Clock dividers
276 static int rockchip_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
281 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
282 /*when i2s in master mode ,must set codec pll div*/
284 case ROCKCHIP_DIV_BCLK:
285 //reg = readl(&(pheadi2s->I2S_TXCTL)) & ~S3C2410_IISMOD_FS_MASK;
286 //writel(reg | div, &(pheadi2s->I2S_TXCTL));
288 case ROCKCHIP_DIV_MCLK:
289 //reg = readl(rockchip_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
290 //writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
292 case ROCKCHIP_DIV_PRESCALER:
293 //writel(div, s3c24xx_i2s.regs + S3C2410_IISPSR);
294 //reg = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
295 //writel(reg | S3C2410_IISCON_PSCEN, s3c24xx_i2s.regs + S3C2410_IISCON);
303 * To avoid duplicating clock code, allow machine driver to
304 * get the clockrate from here.
306 u32 rockchip_i2s_get_clockrate(void)
308 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
309 return 0; ///clk_get_rate(s3c24xx_i2s.iis_clk);
311 EXPORT_SYMBOL_GPL(rockchip_i2s_get_clockrate);
313 static int rockchip_i2s_dai_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
315 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
317 /* Configure the I2S pins in correct mode */
318 rk2818_mux_api_set(CXGPIO_I2S_SEL_NAME,IOMUXB_I2S_INTERFACE);
320 rockchip_snd_txctrl(0);
321 rockchip_snd_rxctrl(0);
327 int rockchip_i2s_suspend(struct snd_soc_dai *cpu_dai)
329 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
334 int rockchip_i2s_resume(struct snd_soc_dai *cpu_dai)
336 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
341 #define rockchip_i2s_suspend NULL
342 #define rockchip_i2s_resume NULL
345 #define ROCKCHIP_I2S_RATES SNDRV_PCM_RATE_48000
347 static struct snd_soc_dai_ops rockchip_i2s_dai_ops = {
348 .trigger = rockchip_i2s_trigger,
349 .hw_params = rockchip_i2s_hw_params,
350 .set_fmt = rockchip_i2s_set_fmt,
351 .set_clkdiv = rockchip_i2s_set_clkdiv,
352 .set_sysclk = rockchip_i2s_set_sysclk,
355 struct snd_soc_dai rk2818_i2s_dai = {
356 .name = "rk2818_i2s",
358 .probe = rockchip_i2s_dai_probe,
359 .suspend = rockchip_i2s_suspend,
360 .resume = rockchip_i2s_resume,
364 .rates = ROCKCHIP_I2S_RATES,
365 .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
369 .rates = ROCKCHIP_I2S_RATES,
370 .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
371 .ops = &rockchip_i2s_dai_ops,
375 EXPORT_SYMBOL_GPL(rk2818_i2s_dai);
377 static int __devinit rockchip_i2s_probe(struct platform_device *pdev)
379 struct resource *regs;
381 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
383 rk2818_i2s_dai.dev = &pdev->dev;
384 rockchip_i2s.iis_clk = clk_get(&pdev->dev, "i2s");
385 clk_enable(rockchip_i2s.iis_clk);
386 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
388 DBG("platform get resource fail\n");
391 rockchip_i2s.regs = ioremap(regs->start, (regs->end - regs->start) + 1);
392 if (rockchip_i2s.regs == NULL) {
393 DBG("rk2818 i2s ioremap err\n");
394 release_mem_region(regs->start, (regs->end - regs->start) + 1);
398 return snd_soc_register_dai(&rk2818_i2s_dai);
402 static int __devexit rockchip_i2s_remove(struct platform_device *pdev)
404 snd_soc_unregister_dai(&rk2818_i2s_dai);
409 static struct platform_driver rockchip_i2s_driver = {
410 .probe = rockchip_i2s_probe,
411 .remove = __devexit_p(rockchip_i2s_remove),
413 .name = "rk2818_i2s",
414 .owner = THIS_MODULE,
418 static int __init rockchip_i2s_init(void)
420 return platform_driver_register(&rockchip_i2s_driver);
422 module_init(rockchip_i2s_init);
424 static void __exit rockchip_i2s_exit(void)
426 platform_driver_unregister(&rockchip_i2s_driver);
428 module_exit(rockchip_i2s_exit);
430 /* Module information */
431 MODULE_AUTHOR("lhh lhh@rock-chips.com");
432 MODULE_DESCRIPTION("ROCKCHIP IIS ASoC Interface");
433 MODULE_LICENSE("GPL");