[ALSA] ASoC: Remove in-code changelogs
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / sound / soc / pxa / pxa2xx-i2s.c
1 /*
2 * pxa2xx-i2s.c -- ALSA Soc Audio Layer
3 *
4 * Copyright 2005 Wolfson Microelectronics PLC.
5 * Author: Liam Girdwood
6 * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
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 #include <linux/init.h>
15 #include <linux/module.h>
16 #include <linux/device.h>
17 #include <linux/delay.h>
18 #include <sound/core.h>
19 #include <sound/pcm.h>
20 #include <sound/initval.h>
21 #include <sound/soc.h>
22
23 #include <asm/hardware.h>
24 #include <asm/arch/pxa-regs.h>
25 #include <asm/arch/pxa2xx-gpio.h>
26 #include <asm/arch/audio.h>
27
28 #include "pxa2xx-pcm.h"
29 #include "pxa2xx-i2s.h"
30
31 struct pxa_i2s_port {
32 u32 sadiv;
33 u32 sacr0;
34 u32 sacr1;
35 u32 saimr;
36 int master;
37 u32 fmt;
38 };
39 static struct pxa_i2s_port pxa_i2s;
40
41 static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = {
42 .name = "I2S PCM Stereo out",
43 .dev_addr = __PREG(SADR),
44 .drcmr = &DRCMRTXSADR,
45 .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
46 DCMD_BURST32 | DCMD_WIDTH4,
47 };
48
49 static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = {
50 .name = "I2S PCM Stereo in",
51 .dev_addr = __PREG(SADR),
52 .drcmr = &DRCMRRXSADR,
53 .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
54 DCMD_BURST32 | DCMD_WIDTH4,
55 };
56
57 static struct pxa2xx_gpio gpio_bus[] = {
58 { /* I2S SoC Slave */
59 .rx = GPIO29_SDATA_IN_I2S_MD,
60 .tx = GPIO30_SDATA_OUT_I2S_MD,
61 .clk = GPIO28_BITCLK_IN_I2S_MD,
62 .frm = GPIO31_SYNC_I2S_MD,
63 },
64 { /* I2S SoC Master */
65 #ifdef CONFIG_PXA27x
66 .sys = GPIO113_I2S_SYSCLK_MD,
67 #else
68 .sys = GPIO32_SYSCLK_I2S_MD,
69 #endif
70 .rx = GPIO29_SDATA_IN_I2S_MD,
71 .tx = GPIO30_SDATA_OUT_I2S_MD,
72 .clk = GPIO28_BITCLK_OUT_I2S_MD,
73 .frm = GPIO31_SYNC_I2S_MD,
74 },
75 };
76
77 static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream)
78 {
79 struct snd_soc_pcm_runtime *rtd = substream->private_data;
80 struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
81
82 if (!cpu_dai->active) {
83 SACR0 |= SACR0_RST;
84 SACR0 = 0;
85 }
86
87 return 0;
88 }
89
90 /* wait for I2S controller to be ready */
91 static int pxa_i2s_wait(void)
92 {
93 int i;
94
95 /* flush the Rx FIFO */
96 for(i = 0; i < 16; i++)
97 SADR;
98 return 0;
99 }
100
101 static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
102 unsigned int fmt)
103 {
104 /* interface format */
105 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
106 case SND_SOC_DAIFMT_I2S:
107 pxa_i2s.fmt = 0;
108 break;
109 case SND_SOC_DAIFMT_LEFT_J:
110 pxa_i2s.fmt = SACR1_AMSL;
111 break;
112 }
113
114 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
115 case SND_SOC_DAIFMT_CBS_CFS:
116 pxa_i2s.master = 1;
117 break;
118 case SND_SOC_DAIFMT_CBM_CFS:
119 pxa_i2s.master = 0;
120 break;
121 default:
122 break;
123 }
124 return 0;
125 }
126
127 static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
128 int clk_id, unsigned int freq, int dir)
129 {
130 if (clk_id != PXA2XX_I2S_SYSCLK)
131 return -ENODEV;
132
133 if (pxa_i2s.master && dir == SND_SOC_CLOCK_OUT)
134 pxa_gpio_mode(gpio_bus[pxa_i2s.master].sys);
135
136 return 0;
137 }
138
139 static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
140 struct snd_pcm_hw_params *params)
141 {
142 struct snd_soc_pcm_runtime *rtd = substream->private_data;
143 struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
144
145 pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx);
146 pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx);
147 pxa_gpio_mode(gpio_bus[pxa_i2s.master].frm);
148 pxa_gpio_mode(gpio_bus[pxa_i2s.master].clk);
149 pxa_set_cken(CKEN_I2S, 1);
150 pxa_i2s_wait();
151
152 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
153 cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_out;
154 else
155 cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_in;
156
157 /* is port used by another stream */
158 if (!(SACR0 & SACR0_ENB)) {
159
160 SACR0 = 0;
161 SACR1 = 0;
162 if (pxa_i2s.master)
163 SACR0 |= SACR0_BCKD;
164
165 SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1);
166 SACR1 |= pxa_i2s.fmt;
167 }
168 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
169 SAIMR |= SAIMR_TFS;
170 else
171 SAIMR |= SAIMR_RFS;
172
173 switch (params_rate(params)) {
174 case 8000:
175 SADIV = 0x48;
176 break;
177 case 11025:
178 SADIV = 0x34;
179 break;
180 case 16000:
181 SADIV = 0x24;
182 break;
183 case 22050:
184 SADIV = 0x1a;
185 break;
186 case 44100:
187 SADIV = 0xd;
188 break;
189 case 48000:
190 SADIV = 0xc;
191 break;
192 case 96000: /* not in manual and possibly slightly inaccurate */
193 SADIV = 0x6;
194 break;
195 }
196
197 return 0;
198 }
199
200 static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
201 {
202 int ret = 0;
203
204 switch (cmd) {
205 case SNDRV_PCM_TRIGGER_START:
206 SACR0 |= SACR0_ENB;
207 break;
208 case SNDRV_PCM_TRIGGER_RESUME:
209 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
210 case SNDRV_PCM_TRIGGER_STOP:
211 case SNDRV_PCM_TRIGGER_SUSPEND:
212 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
213 break;
214 default:
215 ret = -EINVAL;
216 }
217
218 return ret;
219 }
220
221 static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream)
222 {
223 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
224 SACR1 |= SACR1_DRPL;
225 SAIMR &= ~SAIMR_TFS;
226 } else {
227 SACR1 |= SACR1_DREC;
228 SAIMR &= ~SAIMR_RFS;
229 }
230
231 if (SACR1 & (SACR1_DREC | SACR1_DRPL)) {
232 SACR0 &= ~SACR0_ENB;
233 pxa_i2s_wait();
234 pxa_set_cken(CKEN_I2S, 0);
235 }
236 }
237
238 #ifdef CONFIG_PM
239 static int pxa2xx_i2s_suspend(struct platform_device *dev,
240 struct snd_soc_cpu_dai *dai)
241 {
242 if (!dai->active)
243 return 0;
244
245 /* store registers */
246 pxa_i2s.sacr0 = SACR0;
247 pxa_i2s.sacr1 = SACR1;
248 pxa_i2s.saimr = SAIMR;
249 pxa_i2s.sadiv = SADIV;
250
251 /* deactivate link */
252 SACR0 &= ~SACR0_ENB;
253 pxa_i2s_wait();
254 return 0;
255 }
256
257 static int pxa2xx_i2s_resume(struct platform_device *pdev,
258 struct snd_soc_cpu_dai *dai)
259 {
260 if (!dai->active)
261 return 0;
262
263 pxa_i2s_wait();
264
265 SACR0 = pxa_i2s.sacr0 &= ~SACR0_ENB;
266 SACR1 = pxa_i2s.sacr1;
267 SAIMR = pxa_i2s.saimr;
268 SADIV = pxa_i2s.sadiv;
269 SACR0 |= SACR0_ENB;
270
271 return 0;
272 }
273
274 #else
275 #define pxa2xx_i2s_suspend NULL
276 #define pxa2xx_i2s_resume NULL
277 #endif
278
279 #define PXA2XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
280 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
281 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
282
283 struct snd_soc_cpu_dai pxa_i2s_dai = {
284 .name = "pxa2xx-i2s",
285 .id = 0,
286 .type = SND_SOC_DAI_I2S,
287 .suspend = pxa2xx_i2s_suspend,
288 .resume = pxa2xx_i2s_resume,
289 .playback = {
290 .channels_min = 2,
291 .channels_max = 2,
292 .rates = PXA2XX_I2S_RATES,
293 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
294 .capture = {
295 .channels_min = 2,
296 .channels_max = 2,
297 .rates = PXA2XX_I2S_RATES,
298 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
299 .ops = {
300 .startup = pxa2xx_i2s_startup,
301 .shutdown = pxa2xx_i2s_shutdown,
302 .trigger = pxa2xx_i2s_trigger,
303 .hw_params = pxa2xx_i2s_hw_params,},
304 .dai_ops = {
305 .set_fmt = pxa2xx_i2s_set_dai_fmt,
306 .set_sysclk = pxa2xx_i2s_set_dai_sysclk,
307 },
308 };
309
310 EXPORT_SYMBOL_GPL(pxa_i2s_dai);
311
312 /* Module information */
313 MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
314 MODULE_DESCRIPTION("pxa2xx I2S SoC Interface");
315 MODULE_LICENSE("GPL");