]>
Commit | Line | Data |
---|---|---|
0cbbec09 | 1 | /* |
36b8a8bb | 2 | * at91-ssc.c -- ALSA SoC AT91 SSC Audio Layer Platform driver |
0cbbec09 FM |
3 | * |
4 | * Author: Frank Mandarino <fmandarino@endrelia.com> | |
5 | * Endrelia Technologies Inc. | |
6 | * | |
7 | * Based on pxa2xx Platform drivers by | |
8 | * Liam Girdwood <liam.girdwood@wolfsonmicro.com> | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify it | |
11 | * under the terms of the GNU General Public License as published by the | |
12 | * Free Software Foundation; either version 2 of the License, or (at your | |
13 | * option) any later version. | |
14 | * | |
0cbbec09 FM |
15 | */ |
16 | ||
17 | #include <linux/init.h> | |
18 | #include <linux/module.h> | |
19 | #include <linux/interrupt.h> | |
20 | #include <linux/device.h> | |
21 | #include <linux/delay.h> | |
22 | #include <linux/clk.h> | |
fa8f8787 FM |
23 | #include <linux/atmel_pdc.h> |
24 | ||
0cbbec09 FM |
25 | #include <sound/core.h> |
26 | #include <sound/pcm.h> | |
36b8a8bb | 27 | #include <sound/pcm_params.h> |
0cbbec09 FM |
28 | #include <sound/initval.h> |
29 | #include <sound/soc.h> | |
30 | ||
a09e64fb RK |
31 | #include <mach/hardware.h> |
32 | #include <mach/at91_pmc.h> | |
33 | #include <mach/at91_ssc.h> | |
0cbbec09 | 34 | |
9f0ac6e1 | 35 | #include "at91-pcm.h" |
36b8a8bb | 36 | #include "at91-ssc.h" |
0cbbec09 FM |
37 | |
38 | #if 0 | |
36b8a8bb | 39 | #define DBG(x...) printk(KERN_DEBUG "at91-ssc:" x) |
0cbbec09 FM |
40 | #else |
41 | #define DBG(x...) | |
42 | #endif | |
43 | ||
61352667 | 44 | #if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20) |
9f0ac6e1 FM |
45 | #define NUM_SSC_DEVICES 1 |
46 | #else | |
47 | #define NUM_SSC_DEVICES 3 | |
48 | #endif | |
49 | ||
50 | ||
0cbbec09 | 51 | /* |
9f0ac6e1 | 52 | * SSC PDC registers required by the PCM DMA engine. |
0cbbec09 | 53 | */ |
9f0ac6e1 | 54 | static struct at91_pdc_regs pdc_tx_reg = { |
fa8f8787 FM |
55 | .xpr = ATMEL_PDC_TPR, |
56 | .xcr = ATMEL_PDC_TCR, | |
57 | .xnpr = ATMEL_PDC_TNPR, | |
58 | .xncr = ATMEL_PDC_TNCR, | |
0cbbec09 FM |
59 | }; |
60 | ||
9f0ac6e1 | 61 | static struct at91_pdc_regs pdc_rx_reg = { |
fa8f8787 FM |
62 | .xpr = ATMEL_PDC_RPR, |
63 | .xcr = ATMEL_PDC_RCR, | |
64 | .xnpr = ATMEL_PDC_RNPR, | |
65 | .xncr = ATMEL_PDC_RNCR, | |
0cbbec09 FM |
66 | }; |
67 | ||
68 | /* | |
69 | * SSC & PDC status bits for transmit and receive. | |
70 | */ | |
9f0ac6e1 | 71 | static struct at91_ssc_mask ssc_tx_mask = { |
0cbbec09 FM |
72 | .ssc_enable = AT91_SSC_TXEN, |
73 | .ssc_disable = AT91_SSC_TXDIS, | |
74 | .ssc_endx = AT91_SSC_ENDTX, | |
75 | .ssc_endbuf = AT91_SSC_TXBUFE, | |
fa8f8787 FM |
76 | .pdc_enable = ATMEL_PDC_TXTEN, |
77 | .pdc_disable = ATMEL_PDC_TXTDIS, | |
0cbbec09 FM |
78 | }; |
79 | ||
9f0ac6e1 | 80 | static struct at91_ssc_mask ssc_rx_mask = { |
0cbbec09 FM |
81 | .ssc_enable = AT91_SSC_RXEN, |
82 | .ssc_disable = AT91_SSC_RXDIS, | |
83 | .ssc_endx = AT91_SSC_ENDRX, | |
84 | .ssc_endbuf = AT91_SSC_RXBUFF, | |
fa8f8787 FM |
85 | .pdc_enable = ATMEL_PDC_RXTEN, |
86 | .pdc_disable = ATMEL_PDC_RXTDIS, | |
0cbbec09 FM |
87 | }; |
88 | ||
0cbbec09 FM |
89 | |
90 | /* | |
91 | * DMA parameters. | |
92 | */ | |
9f0ac6e1 | 93 | static struct at91_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = { |
0cbbec09 | 94 | {{ |
36b8a8bb | 95 | .name = "SSC0 PCM out", |
9f0ac6e1 | 96 | .pdc = &pdc_tx_reg, |
0cbbec09 FM |
97 | .mask = &ssc_tx_mask, |
98 | }, | |
99 | { | |
36b8a8bb | 100 | .name = "SSC0 PCM in", |
9f0ac6e1 | 101 | .pdc = &pdc_rx_reg, |
0cbbec09 FM |
102 | .mask = &ssc_rx_mask, |
103 | }}, | |
9f0ac6e1 | 104 | #if NUM_SSC_DEVICES == 3 |
0cbbec09 | 105 | {{ |
36b8a8bb | 106 | .name = "SSC1 PCM out", |
9f0ac6e1 | 107 | .pdc = &pdc_tx_reg, |
0cbbec09 FM |
108 | .mask = &ssc_tx_mask, |
109 | }, | |
110 | { | |
36b8a8bb | 111 | .name = "SSC1 PCM in", |
9f0ac6e1 | 112 | .pdc = &pdc_rx_reg, |
0cbbec09 FM |
113 | .mask = &ssc_rx_mask, |
114 | }}, | |
115 | {{ | |
36b8a8bb | 116 | .name = "SSC2 PCM out", |
9f0ac6e1 | 117 | .pdc = &pdc_tx_reg, |
0cbbec09 FM |
118 | .mask = &ssc_tx_mask, |
119 | }, | |
120 | { | |
36b8a8bb | 121 | .name = "SSC2 PCM in", |
9f0ac6e1 | 122 | .pdc = &pdc_rx_reg, |
0cbbec09 FM |
123 | .mask = &ssc_rx_mask, |
124 | }}, | |
9f0ac6e1 | 125 | #endif |
0cbbec09 FM |
126 | }; |
127 | ||
9f0ac6e1 | 128 | struct at91_ssc_state { |
0cbbec09 FM |
129 | u32 ssc_cmr; |
130 | u32 ssc_rcmr; | |
131 | u32 ssc_rfmr; | |
132 | u32 ssc_tcmr; | |
133 | u32 ssc_tfmr; | |
134 | u32 ssc_sr; | |
135 | u32 ssc_imr; | |
136 | }; | |
137 | ||
9f0ac6e1 | 138 | static struct at91_ssc_info { |
0cbbec09 | 139 | char *name; |
9f0ac6e1 | 140 | struct at91_ssc_periph ssc; |
0cbbec09 | 141 | spinlock_t lock; /* lock for dir_mask */ |
171eb8f8 FM |
142 | unsigned short dir_mask; /* 0=unused, 1=playback, 2=capture */ |
143 | unsigned short initialized; /* 1=SSC has been initialized */ | |
144 | unsigned short daifmt; | |
145 | unsigned short cmr_div; | |
146 | unsigned short tcmr_period; | |
147 | unsigned short rcmr_period; | |
9f0ac6e1 FM |
148 | struct at91_pcm_dma_params *dma_params[2]; |
149 | struct at91_ssc_state ssc_state; | |
0cbbec09 | 150 | |
9f0ac6e1 | 151 | } ssc_info[NUM_SSC_DEVICES] = { |
0cbbec09 FM |
152 | { |
153 | .name = "ssc0", | |
c9758b21 | 154 | .lock = __SPIN_LOCK_UNLOCKED(ssc_info[0].lock), |
0cbbec09 | 155 | .dir_mask = 0, |
0cbbec09 FM |
156 | .initialized = 0, |
157 | }, | |
9f0ac6e1 | 158 | #if NUM_SSC_DEVICES == 3 |
0cbbec09 FM |
159 | { |
160 | .name = "ssc1", | |
c9758b21 | 161 | .lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock), |
0cbbec09 | 162 | .dir_mask = 0, |
0cbbec09 FM |
163 | .initialized = 0, |
164 | }, | |
165 | { | |
166 | .name = "ssc2", | |
c9758b21 | 167 | .lock = __SPIN_LOCK_UNLOCKED(ssc_info[2].lock), |
0cbbec09 | 168 | .dir_mask = 0, |
0cbbec09 FM |
169 | .initialized = 0, |
170 | }, | |
9f0ac6e1 | 171 | #endif |
0cbbec09 FM |
172 | }; |
173 | ||
36b8a8bb | 174 | static unsigned int at91_ssc_sysclk; |
0cbbec09 | 175 | |
171eb8f8 FM |
176 | /* |
177 | * SSC interrupt handler. Passes PDC interrupts to the DMA | |
178 | * interrupt handler in the PCM driver. | |
179 | */ | |
36b8a8bb | 180 | static irqreturn_t at91_ssc_interrupt(int irq, void *dev_id) |
0cbbec09 | 181 | { |
9f0ac6e1 FM |
182 | struct at91_ssc_info *ssc_p = dev_id; |
183 | struct at91_pcm_dma_params *dma_params; | |
0cbbec09 FM |
184 | u32 ssc_sr; |
185 | int i; | |
186 | ||
9f0ac6e1 FM |
187 | ssc_sr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR) |
188 | & at91_ssc_read(ssc_p->ssc.base + AT91_SSC_IMR); | |
0cbbec09 FM |
189 | |
190 | /* | |
191 | * Loop through the substreams attached to this SSC. If | |
192 | * a DMA-related interrupt occurred on that substream, call | |
193 | * the DMA interrupt handler function, if one has been | |
194 | * registered in the dma_params structure by the PCM driver. | |
195 | */ | |
196 | for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) { | |
197 | dma_params = ssc_p->dma_params[i]; | |
198 | ||
199 | if (dma_params != NULL && dma_params->dma_intr_handler != NULL && | |
200 | (ssc_sr & | |
201 | (dma_params->mask->ssc_endx | dma_params->mask->ssc_endbuf))) | |
202 | ||
203 | dma_params->dma_intr_handler(ssc_sr, dma_params->substream); | |
204 | } | |
205 | ||
206 | return IRQ_HANDLED; | |
207 | } | |
208 | ||
171eb8f8 FM |
209 | /* |
210 | * Startup. Only that one substream allowed in each direction. | |
211 | */ | |
36b8a8bb | 212 | static int at91_ssc_startup(struct snd_pcm_substream *substream) |
0cbbec09 FM |
213 | { |
214 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
171eb8f8 | 215 | struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id]; |
0cbbec09 FM |
216 | int dir_mask; |
217 | ||
36b8a8bb | 218 | DBG("ssc_startup: SSC_SR=0x%08lx\n", |
9f0ac6e1 | 219 | at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR)); |
0cbbec09 FM |
220 | dir_mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0x1 : 0x2; |
221 | ||
222 | spin_lock_irq(&ssc_p->lock); | |
223 | if (ssc_p->dir_mask & dir_mask) { | |
224 | spin_unlock_irq(&ssc_p->lock); | |
225 | return -EBUSY; | |
226 | } | |
227 | ssc_p->dir_mask |= dir_mask; | |
228 | spin_unlock_irq(&ssc_p->lock); | |
229 | ||
230 | return 0; | |
231 | } | |
232 | ||
171eb8f8 FM |
233 | /* |
234 | * Shutdown. Clear DMA parameters and shutdown the SSC if there | |
235 | * are no other substreams open. | |
236 | */ | |
36b8a8bb | 237 | static void at91_ssc_shutdown(struct snd_pcm_substream *substream) |
0cbbec09 FM |
238 | { |
239 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
171eb8f8 FM |
240 | struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id]; |
241 | struct at91_pcm_dma_params *dma_params; | |
0cbbec09 FM |
242 | int dir, dir_mask; |
243 | ||
244 | dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; | |
171eb8f8 | 245 | dma_params = ssc_p->dma_params[dir]; |
0cbbec09 FM |
246 | |
247 | if (dma_params != NULL) { | |
9f0ac6e1 FM |
248 | at91_ssc_write(dma_params->ssc_base + AT91_SSC_CR, |
249 | dma_params->mask->ssc_disable); | |
0cbbec09 | 250 | DBG("%s disabled SSC_SR=0x%08lx\n", (dir ? "receive" : "transmit"), |
9f0ac6e1 | 251 | at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR)); |
0cbbec09 | 252 | |
9f0ac6e1 | 253 | dma_params->ssc_base = NULL; |
0cbbec09 FM |
254 | dma_params->substream = NULL; |
255 | ssc_p->dma_params[dir] = NULL; | |
256 | } | |
257 | ||
258 | dir_mask = 1 << dir; | |
259 | ||
260 | spin_lock_irq(&ssc_p->lock); | |
261 | ssc_p->dir_mask &= ~dir_mask; | |
262 | if (!ssc_p->dir_mask) { | |
263 | /* Shutdown the SSC clock. */ | |
9f0ac6e1 FM |
264 | DBG("Stopping pid %d clock\n", ssc_p->ssc.pid); |
265 | at91_sys_write(AT91_PMC_PCDR, 1<<ssc_p->ssc.pid); | |
0cbbec09 | 266 | |
171eb8f8 | 267 | if (ssc_p->initialized) { |
9f0ac6e1 | 268 | free_irq(ssc_p->ssc.pid, ssc_p); |
171eb8f8 FM |
269 | ssc_p->initialized = 0; |
270 | } | |
0cbbec09 FM |
271 | |
272 | /* Reset the SSC */ | |
9f0ac6e1 | 273 | at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, AT91_SSC_SWRST); |
0cbbec09 | 274 | |
171eb8f8 FM |
275 | /* Clear the SSC dividers */ |
276 | ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0; | |
0cbbec09 FM |
277 | } |
278 | spin_unlock_irq(&ssc_p->lock); | |
279 | } | |
280 | ||
171eb8f8 FM |
281 | /* |
282 | * Record the SSC system clock rate. | |
283 | */ | |
d37ae539 | 284 | static int at91_ssc_set_dai_sysclk(struct snd_soc_dai *cpu_dai, |
171eb8f8 | 285 | int clk_id, unsigned int freq, int dir) |
0cbbec09 | 286 | { |
171eb8f8 FM |
287 | /* |
288 | * The only clock supplied to the SSC is the AT91 master clock, | |
289 | * which is only used if the SSC is generating BCLK and/or | |
290 | * LRC clocks. | |
291 | */ | |
292 | switch (clk_id) { | |
293 | case AT91_SYSCLK_MCK: | |
36b8a8bb | 294 | at91_ssc_sysclk = freq; |
171eb8f8 FM |
295 | break; |
296 | default: | |
297 | return -EINVAL; | |
298 | } | |
0cbbec09 FM |
299 | |
300 | return 0; | |
301 | } | |
302 | ||
171eb8f8 FM |
303 | /* |
304 | * Record the DAI format for use in hw_params(). | |
305 | */ | |
d37ae539 | 306 | static int at91_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai, |
171eb8f8 | 307 | unsigned int fmt) |
0cbbec09 | 308 | { |
171eb8f8 | 309 | struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id]; |
0cbbec09 | 310 | |
171eb8f8 | 311 | ssc_p->daifmt = fmt; |
0cbbec09 FM |
312 | return 0; |
313 | } | |
314 | ||
171eb8f8 FM |
315 | /* |
316 | * Record SSC clock dividers for use in hw_params(). | |
317 | */ | |
d37ae539 | 318 | static int at91_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, |
171eb8f8 | 319 | int div_id, int div) |
0cbbec09 | 320 | { |
171eb8f8 FM |
321 | struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id]; |
322 | ||
323 | switch (div_id) { | |
324 | case AT91SSC_CMR_DIV: | |
325 | /* | |
326 | * The same master clock divider is used for both | |
327 | * transmit and receive, so if a value has already | |
328 | * been set, it must match this value. | |
329 | */ | |
330 | if (ssc_p->cmr_div == 0) | |
331 | ssc_p->cmr_div = div; | |
332 | else | |
333 | if (div != ssc_p->cmr_div) | |
334 | return -EBUSY; | |
335 | break; | |
336 | ||
337 | case AT91SSC_TCMR_PERIOD: | |
338 | ssc_p->tcmr_period = div; | |
339 | break; | |
340 | ||
341 | case AT91SSC_RCMR_PERIOD: | |
342 | ssc_p->rcmr_period = div; | |
343 | break; | |
344 | ||
345 | default: | |
346 | return -EINVAL; | |
347 | } | |
348 | ||
349 | return 0; | |
0cbbec09 FM |
350 | } |
351 | ||
171eb8f8 FM |
352 | /* |
353 | * Configure the SSC. | |
354 | */ | |
36b8a8bb | 355 | static int at91_ssc_hw_params(struct snd_pcm_substream *substream, |
0cbbec09 FM |
356 | struct snd_pcm_hw_params *params) |
357 | { | |
358 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
171eb8f8 | 359 | int id = rtd->dai->cpu_dai->id; |
9f0ac6e1 FM |
360 | struct at91_ssc_info *ssc_p = &ssc_info[id]; |
361 | struct at91_pcm_dma_params *dma_params; | |
0cbbec09 | 362 | int dir, channels, bits; |
171eb8f8 FM |
363 | u32 tfmr, rfmr, tcmr, rcmr; |
364 | int start_event; | |
0cbbec09 FM |
365 | int ret; |
366 | ||
367 | /* | |
368 | * Currently, there is only one set of dma params for | |
369 | * each direction. If more are added, this code will | |
370 | * have to be changed to select the proper set. | |
371 | */ | |
372 | dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; | |
373 | ||
374 | dma_params = &ssc_dma_params[id][dir]; | |
9f0ac6e1 | 375 | dma_params->ssc_base = ssc_p->ssc.base; |
0cbbec09 FM |
376 | dma_params->substream = substream; |
377 | ||
378 | ssc_p->dma_params[dir] = dma_params; | |
0cbbec09 | 379 | |
171eb8f8 FM |
380 | /* |
381 | * The cpu_dai->dma_data field is only used to communicate the | |
382 | * appropriate DMA parameters to the pcm driver hw_params() | |
383 | * function. It should not be used for other purposes | |
384 | * as it is common to all substreams. | |
385 | */ | |
386 | rtd->dai->cpu_dai->dma_data = dma_params; | |
0cbbec09 | 387 | |
171eb8f8 | 388 | channels = params_channels(params); |
0cbbec09 | 389 | |
36b8a8bb FM |
390 | /* |
391 | * Determine sample size in bits and the PDC increment. | |
392 | */ | |
393 | switch(params_format(params)) { | |
394 | case SNDRV_PCM_FORMAT_S8: | |
395 | bits = 8; | |
396 | dma_params->pdc_xfer_size = 1; | |
397 | break; | |
398 | case SNDRV_PCM_FORMAT_S16_LE: | |
399 | bits = 16; | |
400 | dma_params->pdc_xfer_size = 2; | |
401 | break; | |
402 | case SNDRV_PCM_FORMAT_S24_LE: | |
403 | bits = 24; | |
404 | dma_params->pdc_xfer_size = 4; | |
405 | break; | |
406 | case SNDRV_PCM_FORMAT_S32_LE: | |
407 | bits = 32; | |
408 | dma_params->pdc_xfer_size = 4; | |
409 | break; | |
410 | default: | |
9b6e12e4 | 411 | printk(KERN_WARNING "at91-ssc: unsupported PCM format\n"); |
36b8a8bb FM |
412 | return -EINVAL; |
413 | } | |
414 | ||
171eb8f8 FM |
415 | /* |
416 | * The SSC only supports up to 16-bit samples in I2S format, due | |
36b8a8bb | 417 | * to the size of the Frame Mode Register FSLEN field. |
171eb8f8 | 418 | */ |
36b8a8bb FM |
419 | if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S |
420 | && bits > 16) { | |
421 | printk(KERN_WARNING | |
422 | "at91-ssc: sample size %d is too large for I2S\n", bits); | |
423 | return -EINVAL; | |
424 | } | |
0cbbec09 FM |
425 | |
426 | /* | |
171eb8f8 | 427 | * Compute SSC register settings. |
0cbbec09 | 428 | */ |
36b8a8bb FM |
429 | switch (ssc_p->daifmt |
430 | & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) { | |
431 | ||
432 | case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS: | |
171eb8f8 | 433 | /* |
36b8a8bb | 434 | * I2S format, SSC provides BCLK and LRC clocks. |
171eb8f8 FM |
435 | * |
436 | * The SSC transmit and receive clocks are generated from the | |
437 | * MCK divider, and the BCLK signal is output on the SSC TK line. | |
438 | */ | |
439 | rcmr = (( ssc_p->rcmr_period << 24) & AT91_SSC_PERIOD) | |
440 | | (( 1 << 16) & AT91_SSC_STTDLY) | |
441 | | (( AT91_SSC_START_FALLING_RF ) & AT91_SSC_START) | |
442 | | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI) | |
443 | | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO) | |
444 | | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS); | |
445 | ||
446 | rfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) | |
447 | | (( AT91_SSC_FSOS_NEGATIVE ) & AT91_SSC_FSOS) | |
448 | | (((bits - 1) << 16) & AT91_SSC_FSLEN) | |
449 | | (((channels - 1) << 8) & AT91_SSC_DATNB) | |
450 | | (( 1 << 7) & AT91_SSC_MSBF) | |
451 | | (( 0 << 5) & AT91_SSC_LOOP) | |
452 | | (((bits - 1) << 0) & AT91_SSC_DATALEN); | |
453 | ||
454 | tcmr = (( ssc_p->tcmr_period << 24) & AT91_SSC_PERIOD) | |
455 | | (( 1 << 16) & AT91_SSC_STTDLY) | |
456 | | (( AT91_SSC_START_FALLING_RF ) & AT91_SSC_START) | |
457 | | (( AT91_SSC_CKI_FALLING ) & AT91_SSC_CKI) | |
458 | | (( AT91_SSC_CKO_CONTINUOUS ) & AT91_SSC_CKO) | |
459 | | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS); | |
460 | ||
461 | tfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) | |
462 | | (( 0 << 23) & AT91_SSC_FSDEN) | |
463 | | (( AT91_SSC_FSOS_NEGATIVE ) & AT91_SSC_FSOS) | |
464 | | (((bits - 1) << 16) & AT91_SSC_FSLEN) | |
465 | | (((channels - 1) << 8) & AT91_SSC_DATNB) | |
466 | | (( 1 << 7) & AT91_SSC_MSBF) | |
467 | | (( 0 << 5) & AT91_SSC_DATDEF) | |
468 | | (((bits - 1) << 0) & AT91_SSC_DATALEN); | |
469 | break; | |
470 | ||
36b8a8bb | 471 | case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM: |
171eb8f8 | 472 | /* |
36b8a8bb | 473 | * I2S format, CODEC supplies BCLK and LRC clocks. |
171eb8f8 FM |
474 | * |
475 | * The SSC transmit clock is obtained from the BCLK signal on | |
476 | * on the TK line, and the SSC receive clock is generated from the | |
477 | * transmit clock. | |
478 | * | |
479 | * For single channel data, one sample is transferred on the falling | |
480 | * edge of the LRC clock. For two channel data, one sample is | |
481 | * transferred on both edges of the LRC clock. | |
482 | */ | |
483 | start_event = channels == 1 | |
484 | ? AT91_SSC_START_FALLING_RF | |
485 | : AT91_SSC_START_EDGE_RF; | |
486 | ||
487 | rcmr = (( 0 << 24) & AT91_SSC_PERIOD) | |
488 | | (( 1 << 16) & AT91_SSC_STTDLY) | |
489 | | (( start_event ) & AT91_SSC_START) | |
490 | | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI) | |
491 | | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO) | |
492 | | (( AT91_SSC_CKS_CLOCK ) & AT91_SSC_CKS); | |
493 | ||
494 | rfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) | |
495 | | (( AT91_SSC_FSOS_NONE ) & AT91_SSC_FSOS) | |
496 | | (( 0 << 16) & AT91_SSC_FSLEN) | |
497 | | (( 0 << 8) & AT91_SSC_DATNB) | |
498 | | (( 1 << 7) & AT91_SSC_MSBF) | |
499 | | (( 0 << 5) & AT91_SSC_LOOP) | |
500 | | (((bits - 1) << 0) & AT91_SSC_DATALEN); | |
501 | ||
502 | tcmr = (( 0 << 24) & AT91_SSC_PERIOD) | |
503 | | (( 1 << 16) & AT91_SSC_STTDLY) | |
504 | | (( start_event ) & AT91_SSC_START) | |
505 | | (( AT91_SSC_CKI_FALLING ) & AT91_SSC_CKI) | |
506 | | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO) | |
507 | | (( AT91_SSC_CKS_PIN ) & AT91_SSC_CKS); | |
508 | ||
509 | tfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) | |
510 | | (( 0 << 23) & AT91_SSC_FSDEN) | |
511 | | (( AT91_SSC_FSOS_NONE ) & AT91_SSC_FSOS) | |
512 | | (( 0 << 16) & AT91_SSC_FSLEN) | |
513 | | (( 0 << 8) & AT91_SSC_DATNB) | |
514 | | (( 1 << 7) & AT91_SSC_MSBF) | |
515 | | (( 0 << 5) & AT91_SSC_DATDEF) | |
516 | | (((bits - 1) << 0) & AT91_SSC_DATALEN); | |
517 | break; | |
518 | ||
36b8a8bb FM |
519 | case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS: |
520 | /* | |
521 | * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks. | |
522 | * | |
523 | * The SSC transmit and receive clocks are generated from the | |
524 | * MCK divider, and the BCLK signal is output on the SSC TK line. | |
525 | */ | |
526 | rcmr = (( ssc_p->rcmr_period << 24) & AT91_SSC_PERIOD) | |
527 | | (( 1 << 16) & AT91_SSC_STTDLY) | |
528 | | (( AT91_SSC_START_RISING_RF ) & AT91_SSC_START) | |
529 | | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI) | |
530 | | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO) | |
531 | | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS); | |
532 | ||
533 | rfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) | |
534 | | (( AT91_SSC_FSOS_POSITIVE ) & AT91_SSC_FSOS) | |
535 | | (( 0 << 16) & AT91_SSC_FSLEN) | |
536 | | (((channels - 1) << 8) & AT91_SSC_DATNB) | |
537 | | (( 1 << 7) & AT91_SSC_MSBF) | |
538 | | (( 0 << 5) & AT91_SSC_LOOP) | |
539 | | (((bits - 1) << 0) & AT91_SSC_DATALEN); | |
540 | ||
541 | tcmr = (( ssc_p->tcmr_period << 24) & AT91_SSC_PERIOD) | |
542 | | (( 1 << 16) & AT91_SSC_STTDLY) | |
543 | | (( AT91_SSC_START_RISING_RF ) & AT91_SSC_START) | |
544 | | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI) | |
545 | | (( AT91_SSC_CKO_CONTINUOUS ) & AT91_SSC_CKO) | |
546 | | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS); | |
547 | ||
548 | tfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) | |
549 | | (( 0 << 23) & AT91_SSC_FSDEN) | |
550 | | (( AT91_SSC_FSOS_POSITIVE ) & AT91_SSC_FSOS) | |
551 | | (( 0 << 16) & AT91_SSC_FSLEN) | |
552 | | (((channels - 1) << 8) & AT91_SSC_DATNB) | |
553 | | (( 1 << 7) & AT91_SSC_MSBF) | |
554 | | (( 0 << 5) & AT91_SSC_DATDEF) | |
555 | | (((bits - 1) << 0) & AT91_SSC_DATALEN); | |
556 | ||
557 | ||
558 | ||
559 | break; | |
560 | ||
561 | case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM: | |
171eb8f8 | 562 | default: |
36b8a8bb | 563 | printk(KERN_WARNING "at91-ssc: unsupported DAI format 0x%x.\n", |
171eb8f8 FM |
564 | ssc_p->daifmt); |
565 | return -EINVAL; | |
566 | break; | |
567 | } | |
568 | DBG("RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n", rcmr, rfmr, tcmr, tfmr); | |
569 | ||
570 | if (!ssc_p->initialized) { | |
571 | ||
0cbbec09 | 572 | /* Enable PMC peripheral clock for this SSC */ |
9f0ac6e1 FM |
573 | DBG("Starting pid %d clock\n", ssc_p->ssc.pid); |
574 | at91_sys_write(AT91_PMC_PCER, 1<<ssc_p->ssc.pid); | |
0cbbec09 | 575 | |
171eb8f8 | 576 | /* Reset the SSC and its PDC registers */ |
9f0ac6e1 | 577 | at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, AT91_SSC_SWRST); |
0cbbec09 | 578 | |
fa8f8787 FM |
579 | at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RPR, 0); |
580 | at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RCR, 0); | |
581 | at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RNPR, 0); | |
582 | at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RNCR, 0); | |
583 | at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TPR, 0); | |
584 | at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TCR, 0); | |
585 | at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TNPR, 0); | |
586 | at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TNCR, 0); | |
0cbbec09 | 587 | |
36b8a8bb | 588 | if ((ret = request_irq(ssc_p->ssc.pid, at91_ssc_interrupt, |
0cbbec09 | 589 | 0, ssc_p->name, ssc_p)) < 0) { |
36b8a8bb | 590 | printk(KERN_WARNING "at91-ssc: request_irq failure\n"); |
171eb8f8 FM |
591 | |
592 | DBG("Stopping pid %d clock\n", ssc_p->ssc.pid); | |
e3a2efa6 | 593 | at91_sys_write(AT91_PMC_PCDR, 1<<ssc_p->ssc.pid); |
0cbbec09 FM |
594 | return ret; |
595 | } | |
596 | ||
0cbbec09 | 597 | ssc_p->initialized = 1; |
0cbbec09 FM |
598 | } |
599 | ||
171eb8f8 FM |
600 | /* set SSC clock mode register */ |
601 | at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, ssc_p->cmr_div); | |
602 | ||
603 | /* set receive clock mode and format */ | |
604 | at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, rcmr); | |
605 | at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RFMR, rfmr); | |
606 | ||
607 | /* set transmit clock mode and format */ | |
608 | at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TCMR, tcmr); | |
609 | at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TFMR, tfmr); | |
0cbbec09 | 610 | |
171eb8f8 | 611 | DBG("hw_params: SSC initialized\n"); |
0cbbec09 FM |
612 | return 0; |
613 | } | |
614 | ||
615 | ||
36b8a8bb | 616 | static int at91_ssc_prepare(struct snd_pcm_substream *substream) |
0cbbec09 FM |
617 | { |
618 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
171eb8f8 FM |
619 | struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id]; |
620 | struct at91_pcm_dma_params *dma_params; | |
621 | int dir; | |
622 | ||
623 | dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; | |
624 | dma_params = ssc_p->dma_params[dir]; | |
0cbbec09 | 625 | |
9f0ac6e1 FM |
626 | at91_ssc_write(dma_params->ssc_base + AT91_SSC_CR, |
627 | dma_params->mask->ssc_enable); | |
0cbbec09 | 628 | |
171eb8f8 FM |
629 | DBG("%s enabled SSC_SR=0x%08lx\n", dir ? "receive" : "transmit", |
630 | at91_ssc_read(dma_params->ssc_base + AT91_SSC_SR)); | |
0cbbec09 FM |
631 | return 0; |
632 | } | |
633 | ||
634 | ||
171eb8f8 | 635 | #ifdef CONFIG_PM |
36b8a8bb | 636 | static int at91_ssc_suspend(struct platform_device *pdev, |
d37ae539 | 637 | struct snd_soc_dai *cpu_dai) |
171eb8f8 FM |
638 | { |
639 | struct at91_ssc_info *ssc_p; | |
640 | ||
641 | if(!cpu_dai->active) | |
642 | return 0; | |
643 | ||
644 | ssc_p = &ssc_info[cpu_dai->id]; | |
645 | ||
646 | /* Save the status register before disabling transmit and receive. */ | |
647 | ssc_p->ssc_state.ssc_sr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR); | |
648 | at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, | |
649 | AT91_SSC_TXDIS | AT91_SSC_RXDIS); | |
650 | ||
651 | /* Save the current interrupt mask, then disable unmasked interrupts. */ | |
652 | ssc_p->ssc_state.ssc_imr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_IMR); | |
653 | at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IDR, ssc_p->ssc_state.ssc_imr); | |
654 | ||
655 | ssc_p->ssc_state.ssc_cmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_CMR); | |
656 | ssc_p->ssc_state.ssc_rcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR); | |
657 | ssc_p->ssc_state.ssc_rfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RFMR); | |
658 | ssc_p->ssc_state.ssc_tcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_TCMR); | |
659 | ssc_p->ssc_state.ssc_tfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_TFMR); | |
660 | ||
661 | return 0; | |
662 | } | |
663 | ||
36b8a8bb | 664 | static int at91_ssc_resume(struct platform_device *pdev, |
d37ae539 | 665 | struct snd_soc_dai *cpu_dai) |
171eb8f8 FM |
666 | { |
667 | struct at91_ssc_info *ssc_p; | |
668 | ||
669 | if(!cpu_dai->active) | |
670 | return 0; | |
671 | ||
672 | ssc_p = &ssc_info[cpu_dai->id]; | |
673 | ||
674 | at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TFMR, ssc_p->ssc_state.ssc_tfmr); | |
675 | at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TCMR, ssc_p->ssc_state.ssc_tcmr); | |
676 | at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RFMR, ssc_p->ssc_state.ssc_rfmr); | |
677 | at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->ssc_state.ssc_rcmr); | |
678 | at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, ssc_p->ssc_state.ssc_cmr); | |
679 | ||
680 | at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IER, ssc_p->ssc_state.ssc_imr); | |
681 | ||
682 | at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, | |
683 | ((ssc_p->ssc_state.ssc_sr & AT91_SSC_RXENA) ? AT91_SSC_RXEN : 0) | | |
684 | ((ssc_p->ssc_state.ssc_sr & AT91_SSC_TXENA) ? AT91_SSC_TXEN : 0)); | |
685 | ||
686 | return 0; | |
687 | } | |
688 | ||
689 | #else | |
36b8a8bb FM |
690 | #define at91_ssc_suspend NULL |
691 | #define at91_ssc_resume NULL | |
171eb8f8 FM |
692 | #endif |
693 | ||
36b8a8bb | 694 | #define AT91_SSC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ |
171eb8f8 FM |
695 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ |
696 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ | |
697 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ | |
698 | SNDRV_PCM_RATE_96000) | |
699 | ||
36b8a8bb FM |
700 | #define AT91_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ |
701 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) | |
702 | ||
d37ae539 | 703 | struct snd_soc_dai at91_ssc_dai[NUM_SSC_DEVICES] = { |
36b8a8bb | 704 | { .name = "at91-ssc0", |
0cbbec09 | 705 | .id = 0, |
36b8a8bb FM |
706 | .type = SND_SOC_DAI_PCM, |
707 | .suspend = at91_ssc_suspend, | |
708 | .resume = at91_ssc_resume, | |
0cbbec09 FM |
709 | .playback = { |
710 | .channels_min = 1, | |
171eb8f8 | 711 | .channels_max = 2, |
36b8a8bb FM |
712 | .rates = AT91_SSC_RATES, |
713 | .formats = AT91_SSC_FORMATS,}, | |
0cbbec09 FM |
714 | .capture = { |
715 | .channels_min = 1, | |
171eb8f8 | 716 | .channels_max = 2, |
36b8a8bb FM |
717 | .rates = AT91_SSC_RATES, |
718 | .formats = AT91_SSC_FORMATS,}, | |
0cbbec09 | 719 | .ops = { |
36b8a8bb FM |
720 | .startup = at91_ssc_startup, |
721 | .shutdown = at91_ssc_shutdown, | |
722 | .prepare = at91_ssc_prepare, | |
723 | .hw_params = at91_ssc_hw_params,}, | |
171eb8f8 | 724 | .dai_ops = { |
36b8a8bb FM |
725 | .set_sysclk = at91_ssc_set_dai_sysclk, |
726 | .set_fmt = at91_ssc_set_dai_fmt, | |
727 | .set_clkdiv = at91_ssc_set_dai_clkdiv,}, | |
9f0ac6e1 | 728 | .private_data = &ssc_info[0].ssc, |
0cbbec09 | 729 | }, |
9f0ac6e1 | 730 | #if NUM_SSC_DEVICES == 3 |
36b8a8bb | 731 | { .name = "at91-ssc1", |
0cbbec09 | 732 | .id = 1, |
36b8a8bb FM |
733 | .type = SND_SOC_DAI_PCM, |
734 | .suspend = at91_ssc_suspend, | |
735 | .resume = at91_ssc_resume, | |
0cbbec09 FM |
736 | .playback = { |
737 | .channels_min = 1, | |
171eb8f8 | 738 | .channels_max = 2, |
36b8a8bb FM |
739 | .rates = AT91_SSC_RATES, |
740 | .formats = AT91_SSC_FORMATS,}, | |
0cbbec09 FM |
741 | .capture = { |
742 | .channels_min = 1, | |
171eb8f8 | 743 | .channels_max = 2, |
36b8a8bb FM |
744 | .rates = AT91_SSC_RATES, |
745 | .formats = AT91_SSC_FORMATS,}, | |
0cbbec09 | 746 | .ops = { |
36b8a8bb FM |
747 | .startup = at91_ssc_startup, |
748 | .shutdown = at91_ssc_shutdown, | |
749 | .prepare = at91_ssc_prepare, | |
750 | .hw_params = at91_ssc_hw_params,}, | |
171eb8f8 | 751 | .dai_ops = { |
36b8a8bb FM |
752 | .set_sysclk = at91_ssc_set_dai_sysclk, |
753 | .set_fmt = at91_ssc_set_dai_fmt, | |
754 | .set_clkdiv = at91_ssc_set_dai_clkdiv,}, | |
9f0ac6e1 | 755 | .private_data = &ssc_info[1].ssc, |
0cbbec09 | 756 | }, |
36b8a8bb | 757 | { .name = "at91-ssc2", |
0cbbec09 | 758 | .id = 2, |
36b8a8bb FM |
759 | .type = SND_SOC_DAI_PCM, |
760 | .suspend = at91_ssc_suspend, | |
761 | .resume = at91_ssc_resume, | |
0cbbec09 FM |
762 | .playback = { |
763 | .channels_min = 1, | |
171eb8f8 | 764 | .channels_max = 2, |
36b8a8bb FM |
765 | .rates = AT91_SSC_RATES, |
766 | .formats = AT91_SSC_FORMATS,}, | |
0cbbec09 FM |
767 | .capture = { |
768 | .channels_min = 1, | |
171eb8f8 | 769 | .channels_max = 2, |
36b8a8bb FM |
770 | .rates = AT91_SSC_RATES, |
771 | .formats = AT91_SSC_FORMATS,}, | |
0cbbec09 | 772 | .ops = { |
36b8a8bb FM |
773 | .startup = at91_ssc_startup, |
774 | .shutdown = at91_ssc_shutdown, | |
775 | .prepare = at91_ssc_prepare, | |
776 | .hw_params = at91_ssc_hw_params,}, | |
171eb8f8 | 777 | .dai_ops = { |
36b8a8bb FM |
778 | .set_sysclk = at91_ssc_set_dai_sysclk, |
779 | .set_fmt = at91_ssc_set_dai_fmt, | |
780 | .set_clkdiv = at91_ssc_set_dai_clkdiv,}, | |
9f0ac6e1 | 781 | .private_data = &ssc_info[2].ssc, |
0cbbec09 | 782 | }, |
9f0ac6e1 | 783 | #endif |
0cbbec09 FM |
784 | }; |
785 | ||
36b8a8bb | 786 | EXPORT_SYMBOL_GPL(at91_ssc_dai); |
0cbbec09 FM |
787 | |
788 | /* Module information */ | |
789 | MODULE_AUTHOR("Frank Mandarino, fmandarino@endrelia.com, www.endrelia.com"); | |
36b8a8bb | 790 | MODULE_DESCRIPTION("AT91 SSC ASoC Interface"); |
0cbbec09 | 791 | MODULE_LICENSE("GPL"); |