2 * rk29_i2s.c -- ALSA SoC ROCKCHIP IIS Audio Layer Platform driver
4 * Driver for rockchip iis audio
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
14 #include <linux/init.h>
15 #include <linux/module.h>
16 #include <linux/interrupt.h>
17 #include <linux/device.h>
18 #include <linux/delay.h>
19 #include <linux/clk.h>
22 #include <sound/core.h>
23 #include <sound/pcm.h>
24 #include <sound/pcm_params.h>
25 #include <sound/initval.h>
26 #include <sound/soc.h>
29 #include <mach/hardware.h>
30 #include <mach/board.h>
31 #include <mach/rk29_iomap.h>
32 #include <mach/rk29-dma-pl330.h>
40 #define DBG(x...) printk(KERN_INFO x)
42 #define DBG(x...) do { } while (0)
45 #define pheadi2s ((pI2S_REG)(i2s->regs))
49 struct rk29_i2s_info {
60 struct rockchip_pcm_dma_params *dma_playback;
61 struct rockchip_pcm_dma_params *dma_capture;
68 static struct rk29_dma_client rk29_dma_client_out = {
69 .name = "I2S PCM Stereo Out"
72 static struct rk29_dma_client rk29_dma_client_in = {
73 .name = "I2S PCM Stereo In"
76 static inline struct rk29_i2s_info *to_info(struct snd_soc_dai *cpu_dai)
78 return cpu_dai->private_data;
81 static struct rockchip_pcm_dma_params rk29_i2s_pcm_stereo_out[MAX_I2S];
82 static struct rockchip_pcm_dma_params rk29_i2s_pcm_stereo_in[MAX_I2S];
83 static struct rk29_i2s_info rk29_i2s[MAX_I2S];
85 struct snd_soc_dai rk29_i2s_dai[MAX_I2S];
86 EXPORT_SYMBOL_GPL(rk29_i2s_dai);
89 static struct rockchip_pcm_dma_params rockchip_i2s_pcm_stereo_out[MAX_I2S] = {
91 .client = &rk29_dma_client_out,
92 .channel = DMACH_I2S_2CH_TX, ///0, //DMACH_I2S_OUT,
93 .dma_addr = RK29_I2S_2CH_PHYS + I2S_TXR_BUFF,
97 .client = &rk29_dma_client_out,
98 .channel = DMACH_I2S_8CH_TX, ///0, //DMACH_I2S_OUT,
99 .dma_addr = RK29_I2S_8CH_PHYS + I2S_TXR_BUFF,
104 static struct rockchip_pcm_dma_params rockchip_i2s_pcm_stereo_in[MAX_I2S] = {
106 .client = &rk29_dma_client_in,
107 .channel = DMACH_I2S_2CH_RX, ///1, //DMACH_I2S_IN,
108 .dma_addr = RK29_I2S_2CH_PHYS + I2S_RXR_BUFF,
112 .client = &rk29_dma_client_in,
113 .channel = DMACH_I2S_8CH_RX, ///1, //DMACH_I2S_IN,
114 .dma_addr = RK29_I2S_8CH_PHYS + I2S_RXR_BUFF,
124 *Turn on or off the transmission path.
126 static void rockchip_snd_txctrl(struct rk29_i2s_info *i2s, int on)
128 //struct rk29_i2s_info *i2s = &rockchip_i2s;
131 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
133 opr = readl(&(pheadi2s->I2S_OPR));
134 fifosts = readl(&(pheadi2s->I2S_FIFOSTS));
135 fifosts = (fifosts & (~(0x0f<<16))) | TX_HALF_FULL | RX_HALF_FULL;
136 writel(fifosts, &(pheadi2s->I2S_FIFOSTS));
139 opr = (opr & (~(RESET_RX | I2S_DMA_REQ2_DISABLE | TX_START | RX_START))) | (RESET_TX | I2S_DMA_REQ1_DISABLE);
140 writel(opr, &(pheadi2s->I2S_OPR));
142 opr = (opr & (~(I2S_DMA_REQ1_DISABLE | I2S_DMA_REQ1_RX_ENABLE | RX_START))) | I2S_DMA_REQ1_ENABLE | I2S_DMA_REQ1_TX_ENABLE | TX_START;
143 writel(opr, &(pheadi2s->I2S_OPR));
147 opr = (opr & (~TX_START)) | I2S_DMA_REQ1_DISABLE;
148 writel(opr, &(pheadi2s->I2S_OPR));
152 static void rockchip_snd_rxctrl(struct rk29_i2s_info *i2s, int on)
154 //struct rk29_i2s_info *i2s = &rockchip_i2s;
157 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
158 opr = readl(&(pheadi2s->I2S_OPR));
159 fifosts = readl(&(pheadi2s->I2S_FIFOSTS));
160 fifosts = (fifosts & (~(0x0f<<16))) | TX_HALF_FULL | RX_HALF_FULL;
161 writel(fifosts, &(pheadi2s->I2S_FIFOSTS));
164 opr = (opr & (~(RESET_TX | I2S_DMA_REQ1_DISABLE| TX_START | RX_START))) | (RESET_RX | I2S_DMA_REQ2_DISABLE);
165 writel(opr, &(pheadi2s->I2S_OPR));
167 opr = (opr & (~(I2S_DMA_REQ2_DISABLE | I2S_DMA_REQ2_TX_ENABLE | TX_START))) | I2S_DMA_REQ2_ENABLE | I2S_DMA_REQ2_RX_ENABLE | RX_START;
168 writel(opr, &(pheadi2s->I2S_OPR));
172 opr = (opr & (~RX_START)) | I2S_DMA_REQ2_DISABLE;
173 writel(opr, &(pheadi2s->I2S_OPR));
178 * Set Rockchip I2S DAI format
180 static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
183 struct rk29_i2s_info *i2s = to_info(cpu_dai);
185 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
187 tx_ctl = readl(&(pheadi2s->I2S_TXCTL));
188 tx_ctl &= (~MASTER_MODE);
190 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
191 case SND_SOC_DAIFMT_CBM_CFM:
192 tx_ctl |= MASTER_MODE;
194 case SND_SOC_DAIFMT_CBS_CFS:
195 tx_ctl |= SLAVE_MODE;
198 DBG("unknwon master/slave format\n");
201 tx_ctl &= ~IISMOD_SDF_MASK;
203 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
204 case SND_SOC_DAIFMT_RIGHT_J:
205 tx_ctl |= RIGHT_JUSTIFIED;
207 case SND_SOC_DAIFMT_LEFT_J:
208 tx_ctl |= LEFT_JUSTIFIED;
210 case SND_SOC_DAIFMT_I2S:
214 DBG("Unknown data format\n");
217 tx_ctl = tx_ctl & (~(0xff<<8)) & (~(0x03<<16)) & (~(1<<3));
218 tx_ctl = tx_ctl | OVERSAMPLING_RATE_64FS | SCK_RATE4 | STEREO_MODE;
219 writel(tx_ctl, &(pheadi2s->I2S_TXCTL));
220 rx_ctl = tx_ctl | CLEAR_RXFIFO;
221 writel(rx_ctl, &(pheadi2s->I2S_RXCTL));
225 static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
226 struct snd_pcm_hw_params *params, struct snd_soc_dai *socdai)
228 struct snd_soc_pcm_runtime *rtd = substream->private_data;
229 struct snd_soc_dai_link *dai = rtd->dai;
230 struct rk29_i2s_info *i2s = to_info(dai->cpu_dai);
233 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
234 /*by Vincent Hsiung for EQ Vol Change*/
235 #define HW_PARAMS_FLAG_EQVOL_ON 0x21
236 #define HW_PARAMS_FLAG_EQVOL_OFF 0x22
237 if ((params->flags == HW_PARAMS_FLAG_EQVOL_ON)||(params->flags == HW_PARAMS_FLAG_EQVOL_OFF))
242 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
243 dai->cpu_dai->dma_data = i2s->dma_playback->client;
245 dai->cpu_dai->dma_data = i2s->dma_capture->client;
247 /* Working copies of register */
248 iismod = readl(&(pheadi2s->I2S_TXCTL));
249 iismod &= (~SAMPLE_DATA_MASK);
250 switch (params_format(params)) {
251 case SNDRV_PCM_FORMAT_S8:
252 iismod |= SAMPLE_DATA_8bit;
254 case SNDRV_PCM_FORMAT_S16_LE:
255 iismod |= SAMPLE_DATA_16bit;
258 /*stereo mode MCLK/SCK=4*/
259 iismod = iismod & (~(0xff<<8)) & (~(0x03<<16)) & (~(1<<3));
260 iismod = iismod | OVERSAMPLING_RATE_64FS | SCK_RATE4 | STEREO_MODE;
262 writel(iismod, &(pheadi2s->I2S_TXCTL));
263 iismod = iismod | CLEAR_RXFIFO;
264 writel(iismod, &(pheadi2s->I2S_RXCTL));
269 static int rockchip_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
272 struct snd_soc_pcm_runtime *rtd = substream->private_data;
273 struct rk29_i2s_info *i2s = to_info(rtd->dai->cpu_dai);
275 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
278 case SNDRV_PCM_TRIGGER_START:
279 case SNDRV_PCM_TRIGGER_RESUME:
280 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
281 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
282 rockchip_snd_rxctrl(i2s, 1);
284 rockchip_snd_txctrl(i2s, 1);
286 case SNDRV_PCM_TRIGGER_STOP:
287 case SNDRV_PCM_TRIGGER_SUSPEND:
288 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
289 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
290 rockchip_snd_rxctrl(i2s, 0);
292 rockchip_snd_txctrl(i2s, 0);
302 * Set Rockchip Clock source
304 static int rockchip_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
305 int clk_id, unsigned int freq, int dir)
307 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
308 /*add scu clk source and enable clk*/
314 * Set Rockchip Clock dividers
316 static int rockchip_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
321 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
322 /*when i2s in master mode ,must set codec pll div*/
324 case ROCKCHIP_DIV_BCLK:
325 //reg = readl(&(pheadi2s->I2S_TXCTL)) & ~S3C2410_IISMOD_FS_MASK;
326 //writel(reg | div, &(pheadi2s->I2S_TXCTL));
328 case ROCKCHIP_DIV_MCLK:
329 //reg = readl(rockchip_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
330 //writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
332 case ROCKCHIP_DIV_PRESCALER:
333 //writel(div, s3c24xx_i2s.regs + S3C2410_IISPSR);
334 //reg = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
335 //writel(reg | S3C2410_IISCON_PSCEN, s3c24xx_i2s.regs + S3C2410_IISCON);
343 static int rockchip_set_sysclk(struct snd_soc_dai *cpu_dai,
344 int clk_id, unsigned int freq, int dir)
351 * To avoid duplicating clock code, allow machine driver to
352 * get the clockrate from here.
354 u32 rockchip_i2s_get_clockrate(void)
356 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
357 return 0; ///clk_get_rate(s3c24xx_i2s.iis_clk);
359 EXPORT_SYMBOL_GPL(rockchip_i2s_get_clockrate);
362 int rockchip_i2s_suspend(struct snd_soc_dai *cpu_dai)
364 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
369 int rockchip_i2s_resume(struct snd_soc_dai *cpu_dai)
371 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
376 #define rockchip_i2s_suspend NULL
377 #define rockchip_i2s_resume NULL
380 #define ROCKCHIP_I2S_RATES SNDRV_PCM_RATE_48000
382 static struct snd_soc_dai_ops rockchip_i2s_dai_ops = {
383 .trigger = rockchip_i2s_trigger,
384 .hw_params = rockchip_i2s_hw_params,
385 .set_fmt = rockchip_i2s_set_fmt,
386 .set_clkdiv = rockchip_i2s_set_clkdiv,
387 .set_sysclk = rockchip_i2s_set_sysclk,
390 static int rockchip_i2s_dai_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
392 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
397 static int rk29_i2s_probe(struct platform_device *pdev,
398 struct snd_soc_dai *dai,
399 struct rk29_i2s_info *i2s,
402 struct device *dev = &pdev->dev;
404 struct resource *res;
408 /* record our i2s structure for later use in the callbacks */
409 dai->private_data = i2s;
412 res = platform_get_resource(pdev,
416 dev_err(dev, "Unable to get register resource\n");
420 if (!request_mem_region(res->start, resource_size(res),
422 dev_err(dev, "Unable to request register region\n");
429 i2s->regs = ioremap(base, resource_size(res));
430 if (i2s->regs == NULL) {
431 dev_err(dev, "cannot ioremap registers\n");
435 i2s->iis_pclk = clk_get(dev, "i2s");
436 if (IS_ERR(i2s->iis_pclk)) {
437 dev_err(dev, "failed to get iis_clock\n");
442 clk_enable(i2s->iis_pclk);
444 /* Mark ourselves as in TXRX mode so we can run through our cleanup
445 * process without warnings. */
447 rockchip_snd_txctrl(i2s, 0);
448 rockchip_snd_rxctrl(i2s, 0);
453 static int rk29_i2s_register_dai(struct snd_soc_dai *dai)
455 struct snd_soc_dai_ops *ops = dai->ops;
457 ops->trigger = rockchip_i2s_trigger;
459 ops->hw_params = rockchip_i2s_hw_params;
460 ops->set_fmt = rockchip_i2s_set_fmt;
461 ops->set_clkdiv = rockchip_i2s_set_clkdiv;
462 ops->set_sysclk = rockchip_set_sysclk;
464 dai->suspend = rockchip_i2s_suspend;
465 dai->resume = rockchip_i2s_resume;
467 return snd_soc_register_dai(dai);
470 static int __devinit rockchip_i2s_probe(struct platform_device *pdev)
472 struct rk29_i2s_info *i2s;
473 struct snd_soc_dai *dai;
476 if(pdev->id >= MAX_I2S) {
477 dev_err(&pdev->dev, "id %d out of range\n", pdev->id);
481 i2s = &rk29_i2s[pdev->id];
482 dai = &rk29_i2s_dai[pdev->id];
483 dai->dev = &pdev->dev;
484 dai->name = "rk29-i2s";
486 dai->symmetric_rates = 1;
487 dai->playback.channels_min = 2;
488 dai->playback.channels_max = 2;
489 dai->playback.rates = ROCKCHIP_I2S_RATES;
490 dai->playback.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE;
491 dai->capture.channels_min = 2;
492 dai->capture.channels_max = 2;
493 dai->capture.rates = ROCKCHIP_I2S_RATES;
494 dai->capture.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE;
495 dai->probe = rockchip_i2s_dai_probe;
496 dai->ops = &rockchip_i2s_dai_ops;
498 //i2s->feature |= S3C_FEATURE_CDCLKCON;
500 i2s->dma_capture = &rk29_i2s_pcm_stereo_in[pdev->id];
501 i2s->dma_playback = &rk29_i2s_pcm_stereo_out[pdev->id];
504 i2s->dma_capture->channel = DMACH_I2S_2CH_RX;
505 i2s->dma_capture->dma_addr = RK29_I2S_2CH_PHYS + I2S_RXR_BUFF;
506 i2s->dma_playback->channel = DMACH_I2S_2CH_TX;
507 i2s->dma_playback->dma_addr = RK29_I2S_2CH_PHYS + I2S_TXR_BUFF;
509 i2s->dma_capture->channel = DMACH_I2S_8CH_RX;
510 i2s->dma_capture->dma_addr = RK29_I2S_8CH_PHYS + I2S_RXR_BUFF;
511 i2s->dma_playback->channel = DMACH_I2S_8CH_TX;
512 i2s->dma_playback->dma_addr = RK29_I2S_8CH_PHYS + I2S_TXR_BUFF;
515 i2s->dma_capture->client = &rk29_dma_client_in;
516 i2s->dma_capture->dma_size = 4;
517 i2s->dma_playback->client = &rk29_dma_client_out;
518 i2s->dma_playback->dma_size = 4;
520 i2s->iis_clk = clk_get(&pdev->dev, "i2s");
521 if (IS_ERR(i2s->iis_clk)) {
522 dev_err(&pdev->dev, "failed to get i2s clk\n");
523 ret = PTR_ERR(i2s->iis_clk);
527 clk_enable(i2s->iis_clk);
529 ret = rk29_i2s_probe(pdev, dai, i2s, 0);
533 ret = rk29_i2s_register_dai(dai);
540 /* Not implemented for I2Sv2 core yet */
542 clk_put(i2s->iis_clk);
548 static int __devexit rockchip_i2s_remove(struct platform_device *pdev)
550 snd_soc_unregister_dai(&rk29_i2s_dai);
555 static struct platform_driver rockchip_i2s_driver = {
556 .probe = rockchip_i2s_probe,
557 .remove = __devexit_p(rockchip_i2s_remove),
560 .owner = THIS_MODULE,
564 static int __init rockchip_i2s_init(void)
567 "Enter Func = %s\n", __func__);
568 return platform_driver_register(&rockchip_i2s_driver);
570 module_init(rockchip_i2s_init);
572 static void __exit rockchip_i2s_exit(void)
574 platform_driver_unregister(&rockchip_i2s_driver);
576 module_exit(rockchip_i2s_exit);
578 /* Module information */
579 MODULE_AUTHOR("rockchip");
580 MODULE_DESCRIPTION("ROCKCHIP IIS ASoC Interface");
581 MODULE_LICENSE("GPL");