3d456955b294495fa45a27ffcc31ab3c513e4f23
[firefly-linux-kernel-4.4.55.git] / sound / soc / rk2818 / rk2818_i2s.c
1 /*
2  * rk2818_i2s.c  --  ALSA SoC ROCKCHIP IIS Audio Layer Platform driver
3  *
4  * Driver for rockchip iis audio
5  *  Copyright (C) 2009 lhh
6  *
7  *
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.
12  *
13  */
14
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>
21
22 #include <asm/dma.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>
28 #include <asm/io.h>
29 #include <mach/hardware.h>
30 #include <mach/dma.h>
31 #include <mach/rk2818_iomap.h>
32 #include <mach/iomux.h>
33 #include <mach/scu.h> 
34
35 #include "rk2818_pcm.h"
36 #include "rk2818_i2s.h"
37
38
39 #if 0
40 #define DBG(x...) printk(KERN_INFO x)
41 #else
42 #define DBG(x...) do { } while (0)
43 #endif
44
45 #define pheadi2s  ((pI2S_REG)(i2s->regs))
46
47 struct rockchip_i2s_info {
48         struct device   *dev;
49         void __iomem    *regs;
50         struct clk      *iis_clk;
51         struct clk      *iis_pclk;
52
53         u32              suspend_iismod;
54         u32              suspend_iiscon;
55         u32              suspend_iispsr;
56 };
57
58 static struct rockchip_i2s_info rockchip_i2s;
59
60 static struct rockchip_dma_client rockchip_dma_client_out = {
61         //.name         = "I2S PCM Stereo out"
62         .name = "i2s"
63 };
64
65 static struct rockchip_dma_client rockchip_dma_client_in = {
66         //.name         = "I2S PCM Stereo in"
67         .name = "i2s"
68 };
69
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,
74         .dma_size       = 4,
75 };
76
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,
81         .dma_size       = 4,
82 };
83
84
85
86
87 /* 
88  *Turn on or off the transmission path. 
89  */
90 static void rockchip_snd_txctrl(int on)
91 {
92     struct rockchip_i2s_info *i2s = &rockchip_i2s;        
93           u32 opr,fifosts;
94     
95     DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
96     
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));
101     if (on) 
102     {
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));
105         udelay(5);
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));
108     }
109     else
110     {  
111         opr = (opr & (~TX_START)) | I2S_DMA_REQ1_DISABLE;
112         writel(opr, &(pheadi2s->I2S_OPR));
113     }
114 }
115 static void rockchip_snd_rxctrl(int on)
116 {
117     struct rockchip_i2s_info *i2s = &rockchip_i2s;
118         u32 opr,fifosts;
119           
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));
125     if (on) 
126     {
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));
129         udelay(5);
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));
132     }
133     else
134     {
135         opr = (opr & (~RX_START)) | I2S_DMA_REQ2_DISABLE;
136         writel(opr, &(pheadi2s->I2S_OPR));
137     }   
138 }
139
140 /*
141  * Set Rockchip I2S DAI format
142  */
143 static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
144                 unsigned int fmt)
145 {
146     struct rockchip_i2s_info *i2s = &rockchip_i2s;      
147     u32 tx_ctl,rx_ctl;
148     DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
149     
150     tx_ctl = readl(&(pheadi2s->I2S_TXCTL));
151     tx_ctl &= (~MASTER_MODE);
152     
153     switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
154           case SND_SOC_DAIFMT_CBM_CFM:          
155                 tx_ctl |= MASTER_MODE;  
156                 break;
157           case SND_SOC_DAIFMT_CBS_CFS:
158                 tx_ctl |= SLAVE_MODE;  
159                 break;
160           default:
161                 DBG("unknwon master/slave format\n");
162                 return -EINVAL;
163           }
164     tx_ctl &= ~IISMOD_SDF_MASK;
165     
166     switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
167     case SND_SOC_DAIFMT_RIGHT_J:
168         tx_ctl |= RIGHT_JUSTIFIED;
169         break;
170     case SND_SOC_DAIFMT_LEFT_J:
171         tx_ctl |= LEFT_JUSTIFIED;
172         break;
173     case SND_SOC_DAIFMT_I2S:
174         tx_ctl |= I2S_MODE;
175         break;
176     default:
177         DBG("Unknown data format\n");
178         return -EINVAL;
179           }
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));
185     return 0;
186 }
187
188 static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
189                                 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
190 {
191     struct snd_soc_pcm_runtime *rtd = substream->private_data;
192     struct rockchip_i2s_info *i2s = &rockchip_i2s;      
193         u32 iismod;
194           
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))
200         {
201                 return 0;
202         }
203            
204         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
205                 rtd->dai->cpu_dai->dma_data = &rockchip_i2s_pcm_stereo_out;
206         else
207                 rtd->dai->cpu_dai->dma_data = &rockchip_i2s_pcm_stereo_in;
208     
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;
215                 break;
216           case SNDRV_PCM_FORMAT_S16_LE:
217                 iismod |= SAMPLE_DATA_16bit;
218                 break;
219           } 
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; 
223           
224         writel(iismod, &(pheadi2s->I2S_TXCTL));
225     iismod = iismod | CLEAR_RXFIFO;
226     writel(iismod, &(pheadi2s->I2S_RXCTL));
227     return 0;
228 }
229
230
231 static int rockchip_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
232 {    
233     int ret = 0;
234
235     DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
236     
237     switch (cmd) {
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);
243         else
244                 rockchip_snd_txctrl(1);
245         break;
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);
251         else
252                 rockchip_snd_txctrl(0);
253         break;
254     default:
255         ret = -EINVAL;
256         break;
257     }
258     
259         return ret;
260 }
261 /*
262  * Set Rockchip Clock source
263  */
264 static int rockchip_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
265         int clk_id, unsigned int freq, int dir)
266 {
267     DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
268     /*add scu clk source and enable clk*/
269     
270     return 0;
271 }
272
273 /*
274  * Set Rockchip Clock dividers
275  */
276 static int rockchip_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
277         int div_id, int div)
278 {
279     //u32 reg;
280     
281     DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
282     /*when i2s in master mode ,must set codec pll div*/
283     switch (div_id) {
284     case ROCKCHIP_DIV_BCLK:
285         //reg = readl(&(pheadi2s->I2S_TXCTL)) & ~S3C2410_IISMOD_FS_MASK;
286         //writel(reg | div, &(pheadi2s->I2S_TXCTL));
287         break;
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);
291         break;
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);
296         break;
297     default:
298         return -EINVAL;
299         }
300     return 0;
301 }
302 /*
303  * To avoid duplicating clock code, allow machine driver to
304  * get the clockrate from here.
305  */
306 u32 rockchip_i2s_get_clockrate(void)
307 {
308     DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
309         return 0;  ///clk_get_rate(s3c24xx_i2s.iis_clk);
310 }
311 EXPORT_SYMBOL_GPL(rockchip_i2s_get_clockrate);
312
313 static int rockchip_i2s_dai_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
314 {       
315         DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
316     
317     /* Configure the I2S pins in correct mode */
318     rk2818_mux_api_set(CXGPIO_I2S_SEL_NAME,IOMUXB_I2S_INTERFACE);
319     
320     rockchip_snd_txctrl(0);
321         rockchip_snd_rxctrl(0);
322     
323     return 0;
324 }
325
326 #ifdef CONFIG_PM
327 int rockchip_i2s_suspend(struct snd_soc_dai *cpu_dai)
328 {
329     DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
330     //clk_disable(clk);
331           return 0;
332 }
333
334 int rockchip_i2s_resume(struct snd_soc_dai *cpu_dai)
335 {
336     DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
337     //clk_enable(clk);
338     return 0;
339 }               
340 #else
341 #define rockchip_i2s_suspend NULL
342 #define rockchip_i2s_resume NULL
343 #endif
344
345 #define ROCKCHIP_I2S_RATES SNDRV_PCM_RATE_48000
346
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,
353 };
354
355 struct snd_soc_dai rk2818_i2s_dai = {
356         .name = "rk2818_i2s",
357         .id = -1,
358         .probe = rockchip_i2s_dai_probe,
359         .suspend = rockchip_i2s_suspend,
360         .resume = rockchip_i2s_resume,
361         .playback = {
362                 .channels_min = 1,
363                 .channels_max = 2,
364                 .rates = ROCKCHIP_I2S_RATES,
365                 .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
366         .capture = {
367                 .channels_min = 2,
368                 .channels_max = 2,
369                 .rates = ROCKCHIP_I2S_RATES,
370                 .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
371         .ops = &rockchip_i2s_dai_ops,
372 };
373
374
375 EXPORT_SYMBOL_GPL(rk2818_i2s_dai);
376
377 static int __devinit rockchip_i2s_probe(struct platform_device *pdev)
378 {
379         struct resource         *regs;
380
381     DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
382
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);
387         if (!regs) {
388                 DBG("platform get resource fail\n");
389                 return -ENXIO;  
390         }
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);
395                 return -EBUSY;
396         }
397     
398         return snd_soc_register_dai(&rk2818_i2s_dai);
399 }
400
401
402 static int __devexit rockchip_i2s_remove(struct platform_device *pdev)
403 {
404         snd_soc_unregister_dai(&rk2818_i2s_dai);
405
406         return 0;
407 }
408
409 static struct platform_driver rockchip_i2s_driver = {
410         .probe  = rockchip_i2s_probe,
411         .remove = __devexit_p(rockchip_i2s_remove),
412         .driver = {
413                 .name   = "rk2818_i2s",
414                 .owner  = THIS_MODULE,
415         },
416 };
417
418 static int __init rockchip_i2s_init(void)
419 {
420         return  platform_driver_register(&rockchip_i2s_driver);
421 }
422 module_init(rockchip_i2s_init);
423
424 static void __exit rockchip_i2s_exit(void)
425 {
426         platform_driver_unregister(&rockchip_i2s_driver);
427 }
428 module_exit(rockchip_i2s_exit);
429
430 /* Module information */
431 MODULE_AUTHOR("lhh lhh@rock-chips.com");
432 MODULE_DESCRIPTION("ROCKCHIP IIS ASoC Interface");
433 MODULE_LICENSE("GPL");