u-boot-mkimage-gta01-native: re-add till uboot-utils actually works
[openembedded.git] / packages / linux / linux-ezx-2.6.21 / patches / asoc-pxa-ssp.patch
1 Index: linux-2.6.21/sound/soc/pxa/pxa2xx-ssp.c
2 ===================================================================
3 --- /dev/null   1970-01-01 00:00:00.000000000 +0000
4 +++ linux-2.6.21/sound/soc/pxa/pxa2xx-ssp.c     2007-05-14 21:14:38.000000000 -0300
5 @@ -0,0 +1,671 @@
6 +/*
7 + * pxa2xx-ssp.c  --  ALSA Soc Audio Layer
8 + *
9 + * Copyright 2005 Wolfson Microelectronics PLC.
10 + * Author: Liam Girdwood
11 + *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
12 + *
13 + *  This program is free software; you can redistribute  it and/or modify it
14 + *  under  the terms of  the GNU General  Public License as published by the
15 + *  Free Software Foundation;  either version 2 of the  License, or (at your
16 + *  option) any later version.
17 + *
18 + *  Revision history
19 + *    12th Aug 2005   Initial version.
20 + *
21 + * TODO:
22 + *  o The SSP driver _mostly_ works, however is in need of testing and
23 + *     someone with time to complete it.
24 + *  o Test network mode for > 16bit sample size
25 + */
26 +
27 +#include <linux/init.h>
28 +#include <linux/module.h>
29 +#include <linux/platform_device.h>
30 +
31 +#include <sound/driver.h>
32 +#include <sound/core.h>
33 +#include <sound/pcm.h>
34 +#include <sound/initval.h>
35 +#include <sound/pcm_params.h>
36 +#include <sound/soc.h>
37 +
38 +#include <asm/hardware.h>
39 +#include <asm/arch/pxa-regs.h>
40 +#include <asm/arch/audio.h>
41 +#include <asm/arch/ssp.h>
42 +
43 +#include "pxa2xx-pcm.h"
44 +#include "pxa2xx-ssp.h"
45 +
46 +#define PXA_SSP_DEBUG 1
47 +
48 +/*
49 + * The following should be defined in pxa-regs.h
50 + */
51 +#define SSCR0_ACS              (1 << 30)       /* Audio Clock Select */
52 +#define SSACD_SCDB             (1 << 3)        /* SSPSYSCLK Divider Bypass (SSCR0[ACS] must be set) */
53 +#define SSACD_ACPS(x)  (x << 4)        /* Audio clock PLL select */
54 +#define SSACD_ACDS(x)  (x << 0)        /* Audio clock divider select */
55 +
56 +/*
57 + * SSP audio private data
58 + */
59 +struct ssp_priv {
60 +       unsigned int sysclk;
61 +};
62 +
63 +static struct ssp_priv ssp_clk[3];
64 +static struct ssp_dev ssp[3];
65 +#ifdef CONFIG_PM
66 +static struct ssp_state ssp_state[3];
67 +#endif
68 +
69 +static struct pxa2xx_pcm_dma_params pxa2xx_ssp1_pcm_mono_out = {
70 +       .name                   = "SSP1 PCM Mono out",
71 +       .dev_addr               = __PREG(SSDR_P1),
72 +       .drcmr                  = &DRCMRTXSSDR,
73 +       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
74 +                                 DCMD_BURST16 | DCMD_WIDTH2,
75 +};
76 +
77 +static struct pxa2xx_pcm_dma_params pxa2xx_ssp1_pcm_mono_in = {
78 +       .name                   = "SSP1 PCM Mono in",
79 +       .dev_addr               = __PREG(SSDR_P1),
80 +       .drcmr                  = &DRCMRRXSSDR,
81 +       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
82 +                                 DCMD_BURST16 | DCMD_WIDTH2,
83 +};
84 +
85 +static struct pxa2xx_pcm_dma_params pxa2xx_ssp1_pcm_stereo_out = {
86 +       .name                   = "SSP1 PCM Stereo out",
87 +       .dev_addr               = __PREG(SSDR_P1),
88 +       .drcmr                  = &DRCMRTXSSDR,
89 +       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
90 +                                 DCMD_BURST16 | DCMD_WIDTH4,
91 +};
92 +
93 +static struct pxa2xx_pcm_dma_params pxa2xx_ssp1_pcm_stereo_in = {
94 +       .name                   = "SSP1 PCM Stereo in",
95 +       .dev_addr               = __PREG(SSDR_P1),
96 +       .drcmr                  = &DRCMRRXSSDR,
97 +       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
98 +                                 DCMD_BURST16 | DCMD_WIDTH4,
99 +};
100 +
101 +static struct pxa2xx_pcm_dma_params pxa2xx_ssp2_pcm_mono_out = {
102 +       .name                   = "SSP2 PCM Mono out",
103 +       .dev_addr               = __PREG(SSDR_P2),
104 +       .drcmr                  = &DRCMRTXSS2DR,
105 +       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
106 +                                 DCMD_BURST16 | DCMD_WIDTH2,
107 +};
108 +
109 +static struct pxa2xx_pcm_dma_params pxa2xx_ssp2_pcm_mono_in = {
110 +       .name                   = "SSP2 PCM Mono in",
111 +       .dev_addr               = __PREG(SSDR_P2),
112 +       .drcmr                  = &DRCMRRXSS2DR,
113 +       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
114 +                                 DCMD_BURST16 | DCMD_WIDTH2,
115 +};
116 +
117 +static struct pxa2xx_pcm_dma_params pxa2xx_ssp2_pcm_stereo_out = {
118 +       .name                   = "SSP2 PCM Stereo out",
119 +       .dev_addr               = __PREG(SSDR_P2),
120 +       .drcmr                  = &DRCMRTXSS2DR,
121 +       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
122 +                                 DCMD_BURST16 | DCMD_WIDTH4,
123 +};
124 +
125 +static struct pxa2xx_pcm_dma_params pxa2xx_ssp2_pcm_stereo_in = {
126 +       .name                   = "SSP2 PCM Stereo in",
127 +       .dev_addr               = __PREG(SSDR_P2),
128 +       .drcmr                  = &DRCMRRXSS2DR,
129 +       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
130 +                                 DCMD_BURST16 | DCMD_WIDTH4,
131 +};
132 +
133 +static struct pxa2xx_pcm_dma_params pxa2xx_ssp3_pcm_mono_out = {
134 +       .name                   = "SSP3 PCM Mono out",
135 +       .dev_addr               = __PREG(SSDR_P3),
136 +       .drcmr                  = &DRCMRTXSS3DR,
137 +       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
138 +                                 DCMD_BURST16 | DCMD_WIDTH2,
139 +};
140 +
141 +static struct pxa2xx_pcm_dma_params pxa2xx_ssp3_pcm_mono_in = {
142 +       .name                   = "SSP3 PCM Mono in",
143 +       .dev_addr               = __PREG(SSDR_P3),
144 +       .drcmr                  = &DRCMRRXSS3DR,
145 +       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
146 +                                 DCMD_BURST16 | DCMD_WIDTH2,
147 +};
148 +
149 +static struct pxa2xx_pcm_dma_params pxa2xx_ssp3_pcm_stereo_out = {
150 +       .name                   = "SSP3 PCM Stereo out",
151 +       .dev_addr               = __PREG(SSDR_P3),
152 +       .drcmr                  = &DRCMRTXSS3DR,
153 +       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
154 +                                 DCMD_BURST16 | DCMD_WIDTH4,
155 +};
156 +
157 +static struct pxa2xx_pcm_dma_params pxa2xx_ssp3_pcm_stereo_in = {
158 +       .name                   = "SSP3 PCM Stereo in",
159 +       .dev_addr               = __PREG(SSDR_P3),
160 +       .drcmr                  = &DRCMRRXSS3DR,
161 +       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
162 +                                 DCMD_BURST16 | DCMD_WIDTH4,
163 +};
164 +
165 +static struct pxa2xx_pcm_dma_params *ssp_dma_params[3][4] = {
166 +       {&pxa2xx_ssp1_pcm_mono_out, &pxa2xx_ssp1_pcm_mono_in,
167 +       &pxa2xx_ssp1_pcm_stereo_out,&pxa2xx_ssp1_pcm_stereo_in,},
168 +       {&pxa2xx_ssp2_pcm_mono_out, &pxa2xx_ssp2_pcm_mono_in,
169 +       &pxa2xx_ssp2_pcm_stereo_out, &pxa2xx_ssp2_pcm_stereo_in,},
170 +       {&pxa2xx_ssp3_pcm_mono_out, &pxa2xx_ssp3_pcm_mono_in,
171 +       &pxa2xx_ssp3_pcm_stereo_out,&pxa2xx_ssp3_pcm_stereo_in,},
172 +};
173 +
174 +static int pxa2xx_ssp_startup(struct snd_pcm_substream *substream)
175 +{
176 +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
177 +       struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
178 +       int ret = 0;
179 +
180 +       if (!rtd->dai->cpu_dai->active) {
181 +               ret = ssp_init (&ssp[cpu_dai->id], cpu_dai->id + 1,
182 +                       SSP_NO_IRQ);
183 +               if (ret < 0)
184 +                       return ret;
185 +               ssp_disable(&ssp[cpu_dai->id]);
186 +       }
187 +       return ret;
188 +}
189 +
190 +static void pxa2xx_ssp_shutdown(struct snd_pcm_substream *substream)
191 +{
192 +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
193 +       struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
194 +
195 +       if (!cpu_dai->active) {
196 +               ssp_disable(&ssp[cpu_dai->id]);
197 +               ssp_exit(&ssp[cpu_dai->id]);
198 +       }
199 +}
200 +
201 +#if defined (CONFIG_PXA27x)
202 +static int cken[3] = {CKEN23_SSP1, CKEN3_SSP2, CKEN4_SSP3};
203 +#else
204 +static int cken[3] = {CKEN3_SSP, CKEN9_NSSP, CKEN10_ASSP};
205 +#endif
206 +
207 +#ifdef CONFIG_PM
208 +
209 +static int pxa2xx_ssp_suspend(struct platform_device *pdev,
210 +       struct snd_soc_cpu_dai *dai)
211 +{
212 +       if (!dai->active)
213 +               return 0;
214 +
215 +       ssp_save_state(&ssp[dai->id], &ssp_state[dai->id]);
216 +       pxa_set_cken(cken[dai->id], 0);
217 +       return 0;
218 +}
219 +
220 +static int pxa2xx_ssp_resume(struct platform_device *pdev,
221 +       struct snd_soc_cpu_dai *dai)
222 +{
223 +       if (!dai->active)
224 +               return 0;
225 +
226 +       pxa_set_cken(cken[dai->id], 1);
227 +       ssp_restore_state(&ssp[dai->id], &ssp_state[dai->id]);
228 +       ssp_enable(&ssp[dai->id]);
229 +
230 +       return 0;
231 +}
232 +
233 +#else
234 +#define pxa2xx_ssp_suspend     NULL
235 +#define pxa2xx_ssp_resume      NULL
236 +#endif
237 +
238 +/*
239 + * Set the SSP ports SYSCLK.
240 + */
241 +static int pxa2xx_ssp_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
242 +       int clk_id, unsigned int freq, int dir)
243 +{
244 +       int port = cpu_dai->id + 1;
245 +       u32 sscr0 = SSCR0_P(port) &
246 +               ~(SSCR0_ECS |  SSCR0_NCS | SSCR0_MOD | SSCR0_ACS);
247 +
248 +       switch (clk_id) {
249 +       case PXA2XX_SSP_CLK_PLL:
250 +               /* Internal PLL is fixed on pxa25x and pxa27x */
251 +#ifdef CONFIG_PXA27x
252 +               ssp_clk[cpu_dai->id].sysclk = 13000000;
253 +#else
254 +               ssp_clk[cpu_dai->id].sysclk = 1843200;
255 +#endif
256 +               break;
257 +       case PXA2XX_SSP_CLK_EXT:
258 +               ssp_clk[cpu_dai->id].sysclk = freq;
259 +               sscr0 |= SSCR0_ECS;
260 +               break;
261 +       case PXA2XX_SSP_CLK_NET:
262 +               ssp_clk[cpu_dai->id].sysclk = freq;
263 +               sscr0 |= SSCR0_NCS | SSCR0_MOD;
264 +               break;
265 +       case PXA2XX_SSP_CLK_AUDIO:
266 +               ssp_clk[cpu_dai->id].sysclk = 0;
267 +               SSCR0_P(port) |= SSCR0_SerClkDiv(1);
268 +               sscr0 |= SSCR0_ACS;
269 +               break;
270 +       default:
271 +               return -ENODEV;
272 +       }
273 +
274 +       /* the SSP CKEN clock must be disabled when changing SSP clock mode */
275 +       pxa_set_cken(cken[cpu_dai->id], 0);
276 +       SSCR0_P(port) |= sscr0;
277 +       pxa_set_cken(cken[cpu_dai->id], 1);
278 +       return 0;
279 +}
280 +
281 +/*
282 + * Set the SSP clock dividers.
283 + */
284 +static int pxa2xx_ssp_set_dai_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
285 +       int div_id, int div)
286 +{
287 +       int port = cpu_dai->id + 1;
288 +
289 +       switch (div_id) {
290 +       case PXA2XX_SSP_AUDIO_DIV_ACDS:
291 +               SSACD_P(port) &= ~ 0x7;
292 +               SSACD_P(port) |= SSACD_ACDS(div);
293 +               break;
294 +       case PXA2XX_SSP_AUDIO_DIV_SCDB:
295 +               SSACD_P(port) &= ~0x8;
296 +               if (div == PXA2XX_SSP_CLK_SCDB_1)
297 +                       SSACD_P(port) |= SSACD_SCDB;
298 +               break;
299 +       case PXA2XX_SSP_DIV_SCR:
300 +               SSCR0_P(port) &= ~SSCR0_SCR;
301 +               SSCR0_P(port) |= SSCR0_SerClkDiv(div);
302 +               break;
303 +       default:
304 +               return -ENODEV;
305 +       }
306 +
307 +       return 0;
308 +}
309 +
310 +/*
311 + * Configure the PLL frequency pxa27x and (afaik - pxa320 only)
312 + */
313 +static int pxa2xx_ssp_set_dai_pll(struct snd_soc_cpu_dai *cpu_dai,
314 +       int pll_id, unsigned int freq_in, unsigned int freq_out)
315 +{
316 +       int port = cpu_dai->id + 1;
317 +
318 +       SSACD_P(port) &= ~0x70;
319 +       switch (freq_out) {
320 +       case 5622000:
321 +               break;
322 +       case 11345000:
323 +               SSACD_P(port) |= (0x1 << 4);
324 +               break;
325 +       case 12235000:
326 +               SSACD_P(port) |= (0x2 << 4);
327 +               break;
328 +       case 14857000:
329 +               SSACD_P(port) |= (0x3 << 4);
330 +               break;
331 +       case 32842000:
332 +               SSACD_P(port) |= (0x4 << 4);
333 +               break;
334 +       case 48000000:
335 +               SSACD_P(port) |= (0x5 << 4);
336 +               break;
337 +       }
338 +       return 0;
339 +}
340 +
341 +/*
342 + * Set the active slots in TDM/Network mode
343 + */
344 +static int pxa2xx_ssp_set_dai_tdm_slot(struct snd_soc_cpu_dai *cpu_dai,
345 +       unsigned int mask, int slots)
346 +{
347 +       int port = cpu_dai->id + 1;
348 +
349 +       SSCR0_P(port) &= ~SSCR0_SlotsPerFrm(7);
350 +
351 +       /* set number of active slots */
352 +       SSCR0_P(port) |= SSCR0_SlotsPerFrm(slots);
353 +
354 +       /* set active slot mask */
355 +       SSTSA_P(port) = mask;
356 +       SSRSA_P(port) = mask;
357 +       return 0;
358 +}
359 +
360 +/*
361 + * Tristate the SSP DAI lines
362 + */
363 +static int pxa2xx_ssp_set_dai_tristate(struct snd_soc_cpu_dai *cpu_dai,
364 +       int tristate)
365 +{
366 +       int port = cpu_dai->id + 1;
367 +
368 +       if (tristate)
369 +               SSCR1_P(port) &= ~SSCR1_TTE;
370 +       else
371 +               SSCR1_P(port) |= SSCR1_TTE;
372 +
373 +       return 0;
374 +}
375 +
376 +/*
377 + * Set up the SSP DAI format.
378 + * The SSP Port must be inactive before calling this function as the
379 + * physical interface format is changed.
380 + */
381 +static int pxa2xx_ssp_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
382 +               unsigned int fmt)
383 +{
384 +       int port = cpu_dai->id + 1;
385 +
386 +       /* reset port settings */
387 +       SSCR0_P(port) = 0;
388 +       SSCR1_P(port) = 0;
389 +       SSPSP_P(port) = 0;
390 +
391 +       /* NOTE: I2S emulation is still very much work in progress here */
392 +
393 +       /* FIXME: this is what wince uses for msb */
394 +       if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_MSB) {
395 +               SSCR0_P(port) = SSCR0_EDSS | SSCR0_TISSP | SSCR0_DataSize(16);
396 +
397 +//             SSCR1_P(port) = SSCR1_RxTresh(8) | SSCR1_TxTresh(8); /* doesn't seem to be needed */
398 +               return 0;
399 +       }
400 +
401 +       /* check for I2S emulation mode - handle it separately  */
402 +       if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) ||
403 +               ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_MSB)) {
404 +               /* 8.4.11 */
405 +
406 +               /* Only SSCR0[NCS] or SSCR0[ECS] bit fields settings are optional */
407 +               SSCR0_P(port) = SSCR0_EDSS | SSCR0_PSP | SSCR0_DataSize(16);
408 +
409 +               /* SSCR1 = 0x203C3C03 */
410 +               /* SSCR1[SCLKDIR] and SSCR1[SFRMDIR] must be cleared (master only ???),
411 +                * all other bit fields settings are optional. */
412 +               //SSCR1_P(port) &= ~(SSCR1_SCLKDIR | SSCR1_SFRMDIR);
413 +
414 +               /* set FIFO thresholds */
415 +               SSCR1_P(port) = SSCR1_RxTresh(14) | SSCR1_TxTresh(1);
416 +
417 +               /* normal: */
418 +               /* all bit fields must be cleared except: FSRT = 1 and
419 +                * SFRMWDTH = 16, DMYSTART=0,1) */
420 +               SSPSP_P(port) = SSPSP_FSRT | SSPSP_SFRMWDTH(16) | SSPSP_DMYSTRT(0);
421 +               return 0;
422 +       }
423 +
424 +       SSCR0_P(port) |= SSCR0_PSP;
425 +       SSCR1_P(port) = SSCR1_RxTresh(14) | SSCR1_TxTresh(1) |
426 +               SSCR1_TRAIL | SSCR1_RWOT;
427 +
428 +       switch(fmt & SND_SOC_DAIFMT_MASTER_MASK) {
429 +       case SND_SOC_DAIFMT_CBM_CFM:
430 +               SSCR1_P(port) |= (SSCR1_SCLKDIR | SSCR1_SFRMDIR);
431 +               break;
432 +       case SND_SOC_DAIFMT_CBM_CFS:
433 +               SSCR1_P(port) |= SSCR1_SCLKDIR;
434 +               break;
435 +       case SND_SOC_DAIFMT_CBS_CFM:
436 +               SSCR1_P(port) |= SSCR1_SFRMDIR;
437 +               break;
438 +       case SND_SOC_DAIFMT_CBS_CFS:
439 +               break;
440 +       default:
441 +               return -EINVAL;
442 +       }
443 +
444 +       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
445 +       case SND_SOC_DAIFMT_NB_NF:
446 +               SSPSP_P(port) |= SSPSP_SFRMP | SSPSP_FSRT;
447 +               break;
448 +       case SND_SOC_DAIFMT_IB_IF:
449 +               break;
450 +       default:
451 +               return -EINVAL;
452 +       }
453 +
454 +       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
455 +       case SND_SOC_DAIFMT_DSP_A:
456 +               SSPSP_P(port) |= SSPSP_DMYSTRT(1);
457 +       case SND_SOC_DAIFMT_DSP_B:
458 +               SSPSP_P(port) |= SSPSP_SCMODE(2);
459 +               break;
460 +       case SND_SOC_DAIFMT_I2S:
461 +       case SND_SOC_DAIFMT_MSB:
462 +               /* handled above */
463 +               break;
464 +       default:
465 +               return -EINVAL;
466 +       }
467 +
468 +       return 0;
469 +}
470 +
471 +/*
472 + * Set the SSP audio DMA parameters and sample size.
473 + * Can be called multiple times by oss emulation.
474 + */
475 +static int pxa2xx_ssp_hw_params(struct snd_pcm_substream *substream,
476 +                               struct snd_pcm_hw_params *params)
477 +{
478 +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
479 +       struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
480 +       int dma = 0, chn = params_channels(params);
481 +       int port = cpu_dai->id + 1;
482 +
483 +       /* select correct DMA params */
484 +       if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
485 +               dma = 1; /* capture DMA offset is 1,3 */
486 +       if (chn == 2)
487 +               dma += 2; /* stereo DMA offset is 2, mono is 0 */
488 +       cpu_dai->dma_data = ssp_dma_params[cpu_dai->id][dma];
489 +
490 +       /* we can only change the settings if the port is not in use */
491 +       if (SSCR0_P(port) & SSCR0_SSE)
492 +               return 0;
493 +
494 +       /* clear selected SSP bits */
495 +       SSCR0_P(port) &= ~(SSCR0_DSS | SSCR0_EDSS);
496 +
497 +       /* bit size */
498 +       switch(params_format(params)) {
499 +       case SNDRV_PCM_FORMAT_S16_LE:
500 +               SSCR0_P(port) |= SSCR0_DataSize(16);
501 +               break;
502 +       case SNDRV_PCM_FORMAT_S24_LE:
503 +               SSCR0_P(port) |=(SSCR0_EDSS | SSCR0_DataSize(8));
504 +               /* we must be in network mode (2 slots) for 24 bit stereo */
505 +               break;
506 +       case SNDRV_PCM_FORMAT_S32_LE:
507 +               SSCR0_P(port) |= (SSCR0_EDSS | SSCR0_DataSize(16));
508 +               /* we must be in network mode (2 slots) for 32 bit stereo */
509 +               break;
510 +       }
511 +
512 +#if PXA_SSP_DEBUG
513 +       printk("SSCR0 %x SSCR1 %x SSTO %x SSPSP %x SSSR %x SSACD %x\n",
514 +               SSCR0_P(port), SSCR1_P(port),
515 +               SSTO_P(port), SSPSP_P(port),
516 +               SSSR_P(port), SSACD_P(port));
517 +#endif
518 +       return 0;
519 +}
520 +
521 +static int pxa2xx_ssp_trigger(struct snd_pcm_substream *substream, int cmd)
522 +{
523 +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
524 +       struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
525 +       int ret = 0;
526 +       int port = cpu_dai->id + 1;
527 +
528 +       switch (cmd) {
529 +       case SNDRV_PCM_TRIGGER_RESUME:
530 +               ssp_enable(&ssp[cpu_dai->id]);
531 +               break;
532 +       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
533 +               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
534 +                       SSCR1_P(port) |= SSCR1_TSRE;
535 +               else
536 +                       SSCR1_P(port) |= SSCR1_RSRE;
537 +               SSSR_P(port) |= SSSR_P(port);
538 +               break;
539 +       case SNDRV_PCM_TRIGGER_START:
540 +               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
541 +                       SSCR1_P(port) |= SSCR1_TSRE;
542 +               else
543 +                       SSCR1_P(port) |= SSCR1_RSRE;
544 +               ssp_enable(&ssp[cpu_dai->id]);
545 +               break;
546 +       case SNDRV_PCM_TRIGGER_STOP:
547 +               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
548 +                       SSCR1_P(port) &= ~SSCR1_TSRE;
549 +               else
550 +                       SSCR1_P(port) &= ~SSCR1_RSRE;
551 +               break;
552 +       case SNDRV_PCM_TRIGGER_SUSPEND:
553 +               ssp_disable(&ssp[cpu_dai->id]);
554 +               break;
555 +       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
556 +               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
557 +                       SSCR1_P(port) &= ~SSCR1_TSRE;
558 +               else
559 +                       SSCR1_P(port) &= ~SSCR1_RSRE;
560 +               break;
561 +
562 +       default:
563 +               ret = -EINVAL;
564 +       }
565 +#if PXA_SSP_DEBUG
566 +       printk("trig cmd %d\n", cmd);
567 +       printk("SSCR0 %x SSCR1 %x SSTO %x SSPSP %x SSSR %x\n",
568 +               SSCR0_P(port), SSCR1_P(port),
569 +               SSTO_P(port), SSPSP_P(port),
570 +               SSSR_P(port));
571 +#endif
572 +       return ret;
573 +}
574 +
575 +#define PXA2XX_SSP_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
576 +               SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
577 +               SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
578 +
579 +#define PXA2XX_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
580 +       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
581 +
582 +struct snd_soc_cpu_dai pxa_ssp_dai[] = {
583 +       {       .name = "pxa2xx-ssp1",
584 +               .id = 0,
585 +               .type = SND_SOC_DAI_PCM,
586 +               .suspend = pxa2xx_ssp_suspend,
587 +               .resume = pxa2xx_ssp_resume,
588 +               .playback = {
589 +                       .channels_min = 1,
590 +                       .channels_max = 2,
591 +                       .rates = PXA2XX_SSP_RATES,
592 +                       .formats = PXA2XX_SSP_FORMATS,},
593 +               .capture = {
594 +                       .channels_min = 1,
595 +                       .channels_max = 2,
596 +                       .rates = PXA2XX_SSP_RATES,
597 +                       .formats = PXA2XX_SSP_FORMATS,},
598 +               .ops = {
599 +                       .startup = pxa2xx_ssp_startup,
600 +                       .shutdown = pxa2xx_ssp_shutdown,
601 +                       .trigger = pxa2xx_ssp_trigger,
602 +                       .hw_params = pxa2xx_ssp_hw_params,},
603 +               .dai_ops = {
604 +                       .set_sysclk = pxa2xx_ssp_set_dai_sysclk,
605 +                       .set_clkdiv = pxa2xx_ssp_set_dai_clkdiv,
606 +                       .set_pll = pxa2xx_ssp_set_dai_pll,
607 +                       .set_fmt = pxa2xx_ssp_set_dai_fmt,
608 +                       .set_tdm_slot = pxa2xx_ssp_set_dai_tdm_slot,
609 +                       .set_tristate = pxa2xx_ssp_set_dai_tristate,
610 +               },
611 +       },
612 +       {       .name = "pxa2xx-ssp2",
613 +               .id = 1,
614 +               .type = SND_SOC_DAI_PCM,
615 +               .suspend = pxa2xx_ssp_suspend,
616 +               .resume = pxa2xx_ssp_resume,
617 +               .playback = {
618 +                       .channels_min = 1,
619 +                       .channels_max = 2,
620 +                       .rates = PXA2XX_SSP_RATES,
621 +                       .formats = PXA2XX_SSP_FORMATS,},
622 +               .capture = {
623 +                       .channels_min = 1,
624 +                       .channels_max = 2,
625 +                       .rates = PXA2XX_SSP_RATES,
626 +                       .formats = PXA2XX_SSP_FORMATS,},
627 +               .ops = {
628 +                       .startup = pxa2xx_ssp_startup,
629 +                       .shutdown = pxa2xx_ssp_shutdown,
630 +                       .trigger = pxa2xx_ssp_trigger,
631 +                       .hw_params = pxa2xx_ssp_hw_params,},
632 +               .dai_ops = {
633 +                       .set_sysclk = pxa2xx_ssp_set_dai_sysclk,
634 +                       .set_clkdiv = pxa2xx_ssp_set_dai_clkdiv,
635 +                       .set_pll = pxa2xx_ssp_set_dai_pll,
636 +                       .set_fmt = pxa2xx_ssp_set_dai_fmt,
637 +                       .set_tdm_slot = pxa2xx_ssp_set_dai_tdm_slot,
638 +                       .set_tristate = pxa2xx_ssp_set_dai_tristate,
639 +               },
640 +       },
641 +       {       .name = "pxa2xx-ssp3",
642 +               .id = 2,
643 +               .type = SND_SOC_DAI_PCM,
644 +               .suspend = pxa2xx_ssp_suspend,
645 +               .resume = pxa2xx_ssp_resume,
646 +               .playback = {
647 +                       .channels_min = 1,
648 +                       .channels_max = 2,
649 +                       .rates = PXA2XX_SSP_RATES,
650 +                       .formats = PXA2XX_SSP_FORMATS,},
651 +               .capture = {
652 +                       .channels_min = 1,
653 +                       .channels_max = 2,
654 +                       .rates = PXA2XX_SSP_RATES,
655 +                       .formats = PXA2XX_SSP_FORMATS,},
656 +               .ops = {
657 +                       .startup = pxa2xx_ssp_startup,
658 +                       .shutdown = pxa2xx_ssp_shutdown,
659 +                       .trigger = pxa2xx_ssp_trigger,
660 +                       .hw_params = pxa2xx_ssp_hw_params,},
661 +               .dai_ops = {
662 +                       .set_sysclk = pxa2xx_ssp_set_dai_sysclk,
663 +                       .set_clkdiv = pxa2xx_ssp_set_dai_clkdiv,
664 +                       .set_pll = pxa2xx_ssp_set_dai_pll,
665 +                       .set_fmt = pxa2xx_ssp_set_dai_fmt,
666 +                       .set_tdm_slot = pxa2xx_ssp_set_dai_tdm_slot,
667 +                       .set_tristate = pxa2xx_ssp_set_dai_tristate,
668 +               },
669 +       },
670 +};
671 +EXPORT_SYMBOL_GPL(pxa_ssp_dai);
672 +
673 +/* Module information */
674 +MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
675 +MODULE_DESCRIPTION("pxa2xx SSP/PCM SoC Interface");
676 +MODULE_LICENSE("GPL");
677 Index: linux-2.6.21/sound/soc/pxa/pxa2xx-ssp.h
678 ===================================================================
679 --- /dev/null   1970-01-01 00:00:00.000000000 +0000
680 +++ linux-2.6.21/sound/soc/pxa/pxa2xx-ssp.h     2007-05-14 21:14:38.000000000 -0300
681 @@ -0,0 +1,42 @@
682 +/*
683 + * linux/sound/arm/pxa2xx-ssp.h
684 + *
685 + * This program is free software; you can redistribute it and/or modify
686 + * it under the terms of the GNU General Public License version 2 as
687 + * published by the Free Software Foundation.
688 + */
689 +
690 +#ifndef _PXA2XX_SSP_H
691 +#define _PXA2XX_SSP_H
692 +
693 +/* pxa2xx DAI SSP ID's */
694 +#define PXA2XX_DAI_SSP1                        0
695 +#define PXA2XX_DAI_SSP2                        1
696 +#define PXA2XX_DAI_SSP3                        2
697 +
698 +/* SSP clock sources */
699 +#define PXA2XX_SSP_CLK_PLL     0
700 +#define PXA2XX_SSP_CLK_EXT     1
701 +#define PXA2XX_SSP_CLK_NET     2
702 +#define PXA2XX_SSP_CLK_AUDIO   3
703 +
704 +/* SSP audio dividers */
705 +#define PXA2XX_SSP_AUDIO_DIV_ACDS              0
706 +#define PXA2XX_SSP_AUDIO_DIV_SCDB              1
707 +#define PXA2XX_SSP_DIV_SCR                             2
708 +
709 +/* SSP ACDS audio dividers values */
710 +#define PXA2XX_SSP_CLK_AUDIO_DIV_1             0
711 +#define PXA2XX_SSP_CLK_AUDIO_DIV_2             1
712 +#define PXA2XX_SSP_CLK_AUDIO_DIV_4             2
713 +#define PXA2XX_SSP_CLK_AUDIO_DIV_8             3
714 +#define PXA2XX_SSP_CLK_AUDIO_DIV_16    4
715 +#define PXA2XX_SSP_CLK_AUDIO_DIV_32    5
716 +
717 +/* SSP divider bypass */
718 +#define PXA2XX_SSP_CLK_SCDB_4          0
719 +#define PXA2XX_SSP_CLK_SCDB_1          1
720 +
721 +extern struct snd_soc_cpu_dai pxa_ssp_dai[3];
722 +
723 +#endif
724 Index: linux-2.6.21/sound/soc/pxa/Kconfig
725 ===================================================================
726 --- linux-2.6.21.orig/sound/soc/pxa/Kconfig     2007-05-14 21:16:22.000000000 -0300
727 +++ linux-2.6.21/sound/soc/pxa/Kconfig  2007-05-14 21:17:01.000000000 -0300
728 @@ -20,6 +20,10 @@
729  config SND_PXA2XX_SOC_I2S
730         tristate
731  
732 +config SND_PXA2XX_SOC_SSP
733 +       tristate
734 +       select PXA_SSP
735 +
736  config SND_PXA2XX_SOC_CORGI
737         tristate "SoC Audio support for Sharp Zaurus SL-C7x0"
738         depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx
739 Index: linux-2.6.21/sound/soc/pxa/Makefile
740 ===================================================================
741 --- linux-2.6.21.orig/sound/soc/pxa/Makefile    2007-05-14 21:14:52.000000000 -0300
742 +++ linux-2.6.21/sound/soc/pxa/Makefile 2007-05-14 21:16:10.000000000 -0300
743 @@ -2,10 +2,12 @@
744  snd-soc-pxa2xx-objs := pxa2xx-pcm.o
745  snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o
746  snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o
747 +snd-soc-pxa2xx-ssp-objs := pxa2xx-ssp.o
748  
749  obj-$(CONFIG_SND_PXA2XX_SOC) += snd-soc-pxa2xx.o
750  obj-$(CONFIG_SND_PXA2XX_SOC_AC97) += snd-soc-pxa2xx-ac97.o
751  obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o
752 +obj-$(CONFIG_SND_PXA2XX_SOC_SSP) += snd-soc-pxa2xx-ssp.o
753  
754  # PXA Machine Support
755  snd-soc-corgi-objs := corgi.o