upload tizen1.0 source
[kernel/linux-2.6.36.git] / sound / soc / s3c24xx / s5p-i2s_sec.c
1 /*
2  * s5p-i2s_sec.c  --  Secondary Fifo driver for I2S_v5
3  *
4  * (c) 2009 Samsung Electronics Co. Ltd
5  *   - Jaswinder Singh Brar <jassi.brar@samsung.com>
6  *
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.
11  */
12
13 #include <linux/io.h>
14 #include <linux/delay.h>
15 #include <sound/core.h>
16 #include <sound/pcm.h>
17 #include <sound/pcm_params.h>
18 #include <sound/soc.h>
19
20 #include <mach/map.h>
21 #include <mach/regs-clock.h>
22 /*#include <plat/regs-iis.h>*/
23
24 #include <mach/dma.h>
25 #include <mach/regs-audss.h>
26
27 #include "regs-i2s-v2.h"
28 #include "s3c-i2s-v2.h"
29 #include "s3c-dma.h"
30 #include "s3c-idma.h"
31
32 static void __iomem *s5p_i2s0_regs;
33
34 static struct s3c2410_dma_client s5p_dma_client_outs = {
35         .name           = "I2S_Sec PCM Stereo out"
36 };
37
38 static struct s3c_dma_params s5p_i2s_sec_pcm_out = {
39         .channel        = DMACH_I2S0S_TX,
40         .client         = &s5p_dma_client_outs,
41         .dma_size       = 4,
42 };
43
44 static void s5p_snd_txctrl(int on)
45 {
46         u32 iiscon, iismod;
47
48         iiscon = readl(s5p_i2s0_regs + S3C2412_IISCON);
49         iismod = readl(s5p_i2s0_regs + S3C2412_IISMOD);
50
51         if (on) {
52                 iiscon |= S3C2412_IISCON_IIS_ACTIVE;
53                 iiscon &= ~S3C2412_IISCON_TXCH_PAUSE;
54                 iiscon &= ~S5P_IISCON_TXSDMAPAUSE;
55                 iiscon |= S5P_IISCON_TXSDMACTIVE;
56
57                 switch (iismod & S3C2412_IISMOD_MODE_MASK) {
58                 case S3C2412_IISMOD_MODE_TXONLY:
59                 case S3C2412_IISMOD_MODE_TXRX:
60                         /* do nothing, we are in the right mode */
61                         break;
62
63                 case S3C2412_IISMOD_MODE_RXONLY:
64                         iismod &= ~S3C2412_IISMOD_MODE_MASK;
65                         iismod |= S3C2412_IISMOD_MODE_TXRX;
66                         break;
67
68                 default:
69                         printk(KERN_ERR "TXEN: Invalid MODE %x in IISMOD\n",
70                                 iismod & S3C2412_IISMOD_MODE_MASK);
71                         break;
72                 }
73
74                 writel(iiscon, s5p_i2s0_regs + S3C2412_IISCON);
75                 writel(iismod, s5p_i2s0_regs + S3C2412_IISMOD);
76         } else {
77                 iiscon |= S5P_IISCON_TXSDMAPAUSE;
78                 iiscon &= ~S5P_IISCON_TXSDMACTIVE;
79
80                 /* return if primary is active */
81                 if (iiscon & S3C2412_IISCON_TXDMA_ACTIVE) {
82                         writel(iiscon, s5p_i2s0_regs + S3C2412_IISCON);
83                         return;
84                 }
85
86                 iiscon |= S3C2412_IISCON_TXCH_PAUSE;
87
88                 switch (iismod & S3C2412_IISMOD_MODE_MASK) {
89                 case S3C2412_IISMOD_MODE_TXRX:
90                         iismod &= ~S3C2412_IISMOD_MODE_MASK;
91                         iismod |= S3C2412_IISMOD_MODE_RXONLY;
92                         break;
93
94                 case S3C2412_IISMOD_MODE_TXONLY:
95                         iismod &= ~S3C2412_IISMOD_MODE_MASK;
96                         iiscon &= ~S3C2412_IISCON_IIS_ACTIVE;
97                         break;
98
99                 default:
100                         printk(KERN_ERR "TXDIS: Invalid MODE %x in IISMOD\n",
101                                 iismod & S3C2412_IISMOD_MODE_MASK);
102                         break;
103                 }
104
105                 writel(iismod, s5p_i2s0_regs + S3C2412_IISMOD);
106                 writel(iiscon, s5p_i2s0_regs + S3C2412_IISCON);
107         }
108 }
109
110 #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
111
112 /*
113  * Wait for the LR signal to allow synchronisation to the L/R clock
114  * from the codec. May only be needed for slave mode.
115  */
116 static int s5p_snd_lrsync(void)
117 {
118         u32 iiscon;
119         unsigned long loops = msecs_to_loops(1);
120
121         pr_debug("Entered %s\n", __func__);
122
123         while (--loops) {
124                 iiscon = readl(s5p_i2s0_regs + S3C2412_IISCON);
125                 if (iiscon & S3C2412_IISCON_LRINDEX)
126                         break;
127
128                 cpu_relax();
129         }
130
131         if (!loops) {
132                 pr_debug("%s: timeout\n", __func__);
133                 return -ETIMEDOUT;
134         }
135
136         return 0;
137 }
138
139 int s5p_i2s_hw_params(struct snd_pcm_substream *substream,
140                 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
141 {
142         struct snd_soc_pcm_runtime *rtd = substream->private_data;
143         u32 iismod;
144
145         snd_soc_dai_set_dma_data(rtd->dai->cpu_dai, substream,
146                                         &s5p_i2s_sec_pcm_out);
147
148         iismod = readl(s5p_i2s0_regs + S3C2412_IISMOD);
149
150         /* Copy the same bps as Primary */
151         iismod &= ~S5P_IISMOD_BLCSMASK;
152         iismod |= ((iismod & S5P_IISMOD_BLCPMASK) << 2);
153
154         writel(iismod, s5p_i2s0_regs + S3C2412_IISMOD);
155
156         return 0;
157 }
158 EXPORT_SYMBOL_GPL(s5p_i2s_hw_params);
159
160 int s5p_i2s_startup(struct snd_soc_dai *dai)
161 {
162         u32 iiscon, iisfic;
163         u32 iismod, iisahb;
164
165         iiscon = readl(s5p_i2s0_regs + S3C2412_IISCON);
166         iismod = readl(s5p_i2s0_regs + S3C2412_IISMOD);
167         iisahb = readl(s5p_i2s0_regs + S5P_IISAHB);
168
169         iisahb |= (S5P_IISAHB_DMARLD | S5P_IISAHB_DISRLDINT);
170         iismod |= S5P_IISMOD_TXSLP;
171
172         writel(iisahb, s5p_i2s0_regs + S5P_IISAHB);
173         writel(iismod, s5p_i2s0_regs + S3C2412_IISMOD);
174
175         /* FIFOs must be flushed before enabling PSR
176         *  and other MOD bits, so we do it here. */
177         if (iiscon & S5P_IISCON_TXSDMACTIVE)
178                 return 0;
179
180         iisfic = readl(s5p_i2s0_regs + S5P_IISFICS);
181         iisfic |= S3C2412_IISFIC_TXFLUSH;
182         writel(iisfic, s5p_i2s0_regs + S5P_IISFICS);
183
184         do {
185                 cpu_relax();
186         } while ((__raw_readl(s5p_i2s0_regs + S5P_IISFICS) >> 8) & 0x7f);
187
188         iisfic = readl(s5p_i2s0_regs + S5P_IISFICS);
189         iisfic &= ~S3C2412_IISFIC_TXFLUSH;
190         writel(iisfic, s5p_i2s0_regs + S5P_IISFICS);
191
192         return 0;
193 }
194 EXPORT_SYMBOL_GPL(s5p_i2s_startup);
195
196 int s5p_i2s_trigger(struct snd_pcm_substream *substream,
197                 int cmd, struct snd_soc_dai *dai)
198 {
199         switch (cmd) {
200         case SNDRV_PCM_TRIGGER_START:
201         case SNDRV_PCM_TRIGGER_RESUME:
202         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
203                 /* We don't configure clocks from this Sec i/f.
204                  * So, we simply wait enough time for LRSYNC to
205                  * get synced and not check return 'error'
206                  */
207                 s5p_snd_lrsync();
208                 s5p_snd_txctrl(1);
209                 break;
210
211         case SNDRV_PCM_TRIGGER_STOP:
212         case SNDRV_PCM_TRIGGER_SUSPEND:
213         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
214                 s5p_snd_txctrl(0);
215                 break;
216         }
217
218         return 0;
219 }
220 EXPORT_SYMBOL_GPL(s5p_i2s_trigger);
221
222 void s5p_i2s_sec_init(void *regs, dma_addr_t phys_base)
223 {
224 #ifdef CONFIG_ARCH_S5PV210
225 /* S5PC110 or S5PV210 */
226 #define S3C_VA_AUDSS    S3C_ADDR(0x01600000)    /* Audio SubSystem */
227 #include <mach/regs-audss.h>
228         /* We use I2SCLK for rate generation, so set EPLLout as
229          * the parent of I2SCLK.
230          */
231         u32 val;
232
233         val = readl(S5P_CLKSRC_AUDSS);
234         val &= ~(0x3<<2);
235         val |= (1<<0);
236         writel(val, S5P_CLKSRC_AUDSS);
237
238         val = readl(S5P_CLKGATE_AUDSS);
239         val |= (0x7f<<0);
240         writel(val, S5P_CLKGATE_AUDSS);
241
242 #elif defined(CONFIG_ARCH_S5PV310) || defined(CONFIG_ARCH_S5PC210)
243         u32 val;
244
245 #if defined(CONFIG_SND_SOC_SMDK_WM8994_MASTER) || defined(CONFIG_SND_SOC_C1_MC1N2)
246         /* I2S ratio for Codec master (3+1) = EPLL/4 = 181/4 = 45MHz */
247         val = 0x300;
248 #else
249         /* I2S ratio for AP master (0+1) = EPLL/1 = 181/1 = 181MHz */
250         val = 0x000;
251 #endif
252         /* SRP ratio (15+1)= EPLL/16 = 181/16 = 11MHz
253            BUS ratio (1+1) = SRP/2 = 11/2 = 5MHz */
254         val |= 0x01F;
255         writel(val, S5P_CLKDIV_AUDSS);
256
257         writel(0x001, S5P_CLKSRC_AUDSS);        /* I2S=Main CLK, ASS=FOUT_EPLL*/
258
259 /* CLKGATE should not be controled in here
260         writel(0x1FF, S5P_CLKGATE_AUDSS);
261 */
262 #else
263         #error INITIALIZE HERE!
264 #endif
265
266         s5p_i2s0_regs = regs;
267         s5p_i2s_startup(0);
268         s5p_snd_txctrl(0);
269         s5p_idma_init(regs);
270 }
271
272 /* Module information */
273 MODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>");
274 MODULE_DESCRIPTION("S5P I2S-SecFifo SoC Interface");
275 MODULE_LICENSE("GPL");