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