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