[ALSA] soc - 0.13 ASoC DAPM bug fix for unnamed streams
[deliverable/linux.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 * Revision history
14 * 12th Aug 2005 Initial version.
15 */
16
17 #include <linux/init.h>
18 #include <linux/module.h>
19 #include <linux/device.h>
20 #include <linux/delay.h>
21 #include <sound/driver.h>
22 #include <sound/core.h>
23 #include <sound/pcm.h>
24 #include <sound/initval.h>
25 #include <sound/soc.h>
26
27 #include <asm/hardware.h>
28 #include <asm/arch/pxa-regs.h>
29 #include <asm/arch/audio.h>
30
31 #include "pxa2xx-pcm.h"
32
33 /* used to disable sysclk if external crystal is used */
34 static int extclk;
35 module_param(extclk, int, 0);
36 MODULE_PARM_DESC(extclk, "set to 1 to disable pxa2xx i2s sysclk");
37
38 struct pxa_i2s_port {
39 u32 sadiv;
40 u32 sacr0;
41 u32 sacr1;
42 u32 saimr;
43 int master;
44 };
45 static struct pxa_i2s_port pxa_i2s;
46
47 #define PXA_I2S_DAIFMT \
48 (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF)
49
50 #define PXA_I2S_DIR \
51 (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
52
53 #define PXA_I2S_RATES \
54 (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
55 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
56 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
57
58 /* priv is divider */
59 static struct snd_soc_dai_mode pxa2xx_i2s_modes[] = {
60 /* pxa2xx I2S frame and clock master modes */
61 {
62 .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
63 .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
64 .pcmrate = SNDRV_PCM_RATE_8000,
65 .pcmdir = PXA_I2S_DIR,
66 .flags = SND_SOC_DAI_BFS_DIV,
67 .fs = 256,
68 .bfs = SND_SOC_FSBD(4),
69 .priv = 0x48,
70 },
71 {
72 .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
73 .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
74 .pcmrate = SNDRV_PCM_RATE_11025,
75 .pcmdir = PXA_I2S_DIR,
76 .flags = SND_SOC_DAI_BFS_DIV,
77 .fs = 256,
78 .bfs = SND_SOC_FSBD(4),
79 .priv = 0x34,
80 },
81 {
82 .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
83 .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
84 .pcmrate = SNDRV_PCM_RATE_16000,
85 .pcmdir = PXA_I2S_DIR,
86 .flags = SND_SOC_DAI_BFS_DIV,
87 .fs = 256,
88 .bfs = SND_SOC_FSBD(4),
89 .priv = 0x24,
90 },
91 {
92 .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
93 .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
94 .pcmrate = SNDRV_PCM_RATE_22050,
95 .pcmdir = PXA_I2S_DIR,
96 .flags = SND_SOC_DAI_BFS_DIV,
97 .fs = 256,
98 .bfs = SND_SOC_FSBD(4),
99 .priv = 0x1a,
100 },
101 {
102 .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
103 .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
104 .pcmrate = SNDRV_PCM_RATE_44100,
105 .pcmdir = PXA_I2S_DIR,
106 .flags = SND_SOC_DAI_BFS_DIV,
107 .fs = 256,
108 .bfs = SND_SOC_FSBD(4),
109 .priv = 0xd,
110 },
111 {
112 .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
113 .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
114 .pcmrate = SNDRV_PCM_RATE_48000,
115 .pcmdir = PXA_I2S_DIR,
116 .flags = SND_SOC_DAI_BFS_DIV,
117 .fs = 256,
118 .bfs = SND_SOC_FSBD(4),
119 .priv = 0xc,
120 },
121
122 /* pxa2xx I2S frame master and clock slave mode */
123 {
124 .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBM_CFS,
125 .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
126 .pcmrate = PXA_I2S_RATES,
127 .pcmdir = PXA_I2S_DIR,
128 .fs = SND_SOC_FS_ALL,
129 .flags = SND_SOC_DAI_BFS_RATE,
130 .bfs = 64,
131 .priv = 0x48,
132 },
133 };
134
135 static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = {
136 .name = "I2S PCM Stereo out",
137 .dev_addr = __PREG(SADR),
138 .drcmr = &DRCMRTXSADR,
139 .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
140 DCMD_BURST32 | DCMD_WIDTH4,
141 };
142
143 static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = {
144 .name = "I2S PCM Stereo in",
145 .dev_addr = __PREG(SADR),
146 .drcmr = &DRCMRRXSADR,
147 .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
148 DCMD_BURST32 | DCMD_WIDTH4,
149 };
150
151 static struct pxa2xx_gpio gpio_bus[] = {
152 { /* I2S SoC Slave */
153 .rx = GPIO29_SDATA_IN_I2S_MD,
154 .tx = GPIO30_SDATA_OUT_I2S_MD,
155 .clk = GPIO28_BITCLK_IN_I2S_MD,
156 .frm = GPIO31_SYNC_I2S_MD,
157 },
158 { /* I2S SoC Master */
159 #ifdef CONFIG_PXA27x
160 .sys = GPIO113_I2S_SYSCLK_MD,
161 #else
162 .sys = GPIO32_SYSCLK_I2S_MD,
163 #endif
164 .rx = GPIO29_SDATA_IN_I2S_MD,
165 .tx = GPIO30_SDATA_OUT_I2S_MD,
166 .clk = GPIO28_BITCLK_OUT_I2S_MD,
167 .frm = GPIO31_SYNC_I2S_MD,
168 },
169 };
170
171 static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream)
172 {
173 struct snd_soc_pcm_runtime *rtd = substream->private_data;
174
175 if (!rtd->cpu_dai->active) {
176 SACR0 |= SACR0_RST;
177 SACR0 = 0;
178 }
179
180 return 0;
181 }
182
183 /* wait for I2S controller to be ready */
184 static int pxa_i2s_wait(void)
185 {
186 int i;
187
188 /* flush the Rx FIFO */
189 for(i = 0; i < 16; i++)
190 SADR;
191 return 0;
192 }
193
194 static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
195 struct snd_pcm_hw_params *params)
196 {
197 struct snd_soc_pcm_runtime *rtd = substream->private_data;
198
199 pxa_i2s.master = 0;
200 if (rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CBS_CFS)
201 pxa_i2s.master = 1;
202
203 if (pxa_i2s.master && !extclk)
204 pxa_gpio_mode(gpio_bus[pxa_i2s.master].sys);
205
206 pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx);
207 pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx);
208 pxa_gpio_mode(gpio_bus[pxa_i2s.master].frm);
209 pxa_gpio_mode(gpio_bus[pxa_i2s.master].clk);
210 pxa_set_cken(CKEN8_I2S, 1);
211 pxa_i2s_wait();
212
213 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
214 rtd->cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_out;
215 else
216 rtd->cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_in;
217
218 /* is port used by another stream */
219 if (!(SACR0 & SACR0_ENB)) {
220
221 SACR0 = 0;
222 SACR1 = 0;
223 if (pxa_i2s.master)
224 SACR0 |= SACR0_BCKD;
225
226 SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1);
227
228 if (rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_LEFT_J)
229 SACR1 |= SACR1_AMSL;
230 }
231 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
232 SAIMR |= SAIMR_TFS;
233 else
234 SAIMR |= SAIMR_RFS;
235
236 SADIV = rtd->cpu_dai->dai_runtime.priv;
237 return 0;
238 }
239
240 static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
241 {
242 int ret = 0;
243
244 switch (cmd) {
245 case SNDRV_PCM_TRIGGER_START:
246 SACR0 |= SACR0_ENB;
247 break;
248 case SNDRV_PCM_TRIGGER_RESUME:
249 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
250 case SNDRV_PCM_TRIGGER_STOP:
251 case SNDRV_PCM_TRIGGER_SUSPEND:
252 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
253 break;
254 default:
255 ret = -EINVAL;
256 }
257
258 return ret;
259 }
260
261 static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream)
262 {
263 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
264 SACR1 |= SACR1_DRPL;
265 SAIMR &= ~SAIMR_TFS;
266 } else {
267 SACR1 |= SACR1_DREC;
268 SAIMR &= ~SAIMR_RFS;
269 }
270
271 if (SACR1 & (SACR1_DREC | SACR1_DRPL)) {
272 SACR0 &= ~SACR0_ENB;
273 pxa_i2s_wait();
274 pxa_set_cken(CKEN8_I2S, 0);
275 }
276 }
277
278 #ifdef CONFIG_PM
279 static int pxa2xx_i2s_suspend(struct platform_device *dev,
280 struct snd_soc_cpu_dai *dai)
281 {
282 if (!dai->active)
283 return 0;
284
285 /* store registers */
286 pxa_i2s.sacr0 = SACR0;
287 pxa_i2s.sacr1 = SACR1;
288 pxa_i2s.saimr = SAIMR;
289 pxa_i2s.sadiv = SADIV;
290
291 /* deactivate link */
292 SACR0 &= ~SACR0_ENB;
293 pxa_i2s_wait();
294 return 0;
295 }
296
297 static int pxa2xx_i2s_resume(struct platform_device *pdev,
298 struct snd_soc_cpu_dai *dai)
299 {
300 if (!dai->active)
301 return 0;
302
303 pxa_i2s_wait();
304
305 SACR0 = pxa_i2s.sacr0 &= ~SACR0_ENB;
306 SACR1 = pxa_i2s.sacr1;
307 SAIMR = pxa_i2s.saimr;
308 SADIV = pxa_i2s.sadiv;
309 SACR0 |= SACR0_ENB;
310
311 return 0;
312 }
313
314 #else
315 #define pxa2xx_i2s_suspend NULL
316 #define pxa2xx_i2s_resume NULL
317 #endif
318
319 /* pxa2xx I2S sysclock is always 256 FS */
320 static unsigned int pxa_i2s_config_sysclk(struct snd_soc_cpu_dai *iface,
321 struct snd_soc_clock_info *info, unsigned int clk)
322 {
323 return info->rate << 8;
324 }
325
326 struct snd_soc_cpu_dai pxa_i2s_dai = {
327 .name = "pxa2xx-i2s",
328 .id = 0,
329 .type = SND_SOC_DAI_I2S,
330 .suspend = pxa2xx_i2s_suspend,
331 .resume = pxa2xx_i2s_resume,
332 .config_sysclk = pxa_i2s_config_sysclk,
333 .playback = {
334 .channels_min = 2,
335 .channels_max = 2,},
336 .capture = {
337 .channels_min = 2,
338 .channels_max = 2,},
339 .ops = {
340 .startup = pxa2xx_i2s_startup,
341 .shutdown = pxa2xx_i2s_shutdown,
342 .trigger = pxa2xx_i2s_trigger,
343 .hw_params = pxa2xx_i2s_hw_params,},
344 .caps = {
345 .num_modes = ARRAY_SIZE(pxa2xx_i2s_modes),
346 .mode = pxa2xx_i2s_modes,},
347 };
348
349 EXPORT_SYMBOL_GPL(pxa_i2s_dai);
350
351 /* Module information */
352 MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
353 MODULE_DESCRIPTION("pxa2xx I2S SoC Interface");
354 MODULE_LICENSE("GPL");
This page took 0.039091 seconds and 5 git commands to generate.