summaryrefslogtreecommitdiff
path: root/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2010-04-09 01:21:53 +0000
committerMichael Sevakis <jethead71@rockbox.org>2010-04-09 01:21:53 +0000
commit7abf2b53a462612808d46d6d77a7f35261a0e5a3 (patch)
tree241304f7cd2b5d1c2a9e091fe56a33d2d2f8e816 /firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c
parent43304b87b0662d1619ac60e5297a1694aa580310 (diff)
downloadrockbox-7abf2b53a462612808d46d6d77a7f35261a0e5a3.tar.gz
rockbox-7abf2b53a462612808d46d6d77a7f35261a0e5a3.zip
Gigabeat S/i.MX31: Sort files in the /target tree into things that are SoC-generic (into /imx31) and player-specific (into /gigabeat-s, based upon current appearances). Move i2s clock init into the appropriate file. Housekeeping only-- no functional changes.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25547 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c')
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c555
1 files changed, 555 insertions, 0 deletions
diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c
new file mode 100644
index 0000000000..6cec3ecdd3
--- /dev/null
+++ b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c
@@ -0,0 +1,555 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2008 by Michael Sevakis
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include <stdlib.h>
22#include "system.h"
23#include "kernel.h"
24#include "audio.h"
25#include "sound.h"
26#include "ccm-imx31.h"
27#include "sdma-imx31.h"
28#include "mmu-imx31.h"
29
30#define DMA_PLAY_CH_NUM 2
31#define DMA_REC_CH_NUM 1
32#define DMA_PLAY_CH_PRIORITY 6
33#define DMA_REC_CH_PRIORITY 6
34
35static struct buffer_descriptor dma_play_bd DEVBSS_ATTR;
36static struct channel_descriptor dma_play_cd DEVBSS_ATTR;
37
38struct dma_data
39{
40 int locked;
41 int callback_pending; /* DMA interrupt happened while locked */
42 int state;
43};
44
45static struct dma_data dma_play_data =
46{
47 /* Initialize to a locked, stopped state */
48 .locked = 0,
49 .callback_pending = 0,
50 .state = 0
51};
52
53static void play_dma_callback(void)
54{
55 unsigned char *start;
56 size_t size = 0;
57 pcm_more_callback_type get_more = pcm_callback_for_more;
58
59 if (dma_play_data.locked != 0)
60 {
61 /* Callback is locked out */
62 dma_play_data.callback_pending = dma_play_data.state;
63 return;
64 }
65
66 if (dma_play_bd.mode.status & BD_RROR)
67 {
68 /* Stop on error */
69 }
70 else if (get_more != NULL && (get_more(&start, &size), size != 0))
71 {
72 start = (void*)(((unsigned long)start + 3) & ~3);
73 size &= ~3;
74
75 /* Flush any pending cache writes */
76 clean_dcache_range(start, size);
77 dma_play_bd.buf_addr = (void *)addr_virt_to_phys((unsigned long)start);
78 dma_play_bd.mode.count = size;
79 dma_play_bd.mode.command = TRANSFER_16BIT;
80 dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
81 sdma_channel_run(DMA_PLAY_CH_NUM);
82 return;
83 }
84
85 /* Error, callback missing or no more DMA to do */
86 pcm_play_dma_stop();
87 pcm_play_dma_stopped_callback();
88}
89
90void pcm_play_lock(void)
91{
92 if (++dma_play_data.locked == 1)
93 imx31_regclr32(&SSI_SIER2, SSI_SIER_TDMAE);
94}
95
96void pcm_play_unlock(void)
97{
98 if (--dma_play_data.locked == 0 && dma_play_data.state != 0)
99 {
100 int oldstatus = disable_irq_save();
101 int pending = dma_play_data.callback_pending;
102 dma_play_data.callback_pending = 0;
103 SSI_SIER2 |= SSI_SIER_TDMAE;
104 restore_irq(oldstatus);
105
106 /* Should an interrupt be forced instead? The upper pcm layer can
107 * call producer's callback in thread context so technically this is
108 * acceptable. */
109 if (pending != 0)
110 play_dma_callback();
111 }
112}
113
114void pcm_dma_apply_settings(void)
115{
116 audiohw_set_frequency(pcm_fsel);
117}
118
119void pcm_play_dma_init(void)
120{
121 /* Init channel information */
122 dma_play_cd.bd_count = 1;
123 dma_play_cd.callback = play_dma_callback;
124 dma_play_cd.shp_addr = SDMA_PER_ADDR_SSI2_TX1;
125 dma_play_cd.wml = SDMA_SSI_TXFIFO_WML*2;
126 dma_play_cd.per_type = SDMA_PER_SSI_SHP; /* SSI2 shared with SDMA core */
127 dma_play_cd.tran_type = SDMA_TRAN_EMI_2_PER;
128 dma_play_cd.event_id1 = SDMA_REQ_SSI2_TX1;
129
130 sdma_channel_init(DMA_PLAY_CH_NUM, &dma_play_cd, &dma_play_bd);
131 sdma_channel_set_priority(DMA_PLAY_CH_NUM, DMA_PLAY_CH_PRIORITY);
132
133 ccm_module_clock_gating(CG_SSI1, CGM_ON_RUN_WAIT);
134 ccm_module_clock_gating(CG_SSI2, CGM_ON_RUN_WAIT);
135
136 /* Reset & disable SSIs */
137 SSI_SCR1 &= ~SSI_SCR_SSIEN;
138 SSI_SCR2 &= ~SSI_SCR_SSIEN;
139
140 SSI_SIER1 = 0;
141 SSI_SIER2 = 0;
142
143 /* Set up audio mux */
144
145 /* Port 2 (internally connected to SSI2)
146 * All clocking is output sourced from port 4 */
147 AUDMUX_PTCR2 = AUDMUX_PTCR_TFS_DIR | AUDMUX_PTCR_TFSEL_PORT4 |
148 AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT4 |
149 AUDMUX_PTCR_SYN;
150
151 /* Receive data from port 4 */
152 AUDMUX_PDCR2 = AUDMUX_PDCR_RXDSEL_PORT4;
153 /* All clock lines are inputs sourced from the master mode codec and
154 * sent back to SSI2 through port 2 */
155 AUDMUX_PTCR4 = AUDMUX_PTCR_SYN;
156
157 /* Receive data from port 2 */
158 AUDMUX_PDCR4 = AUDMUX_PDCR_RXDSEL_PORT2;
159
160 /* PORT1 (internally connected to SSI1) routes clocking to PORT5 to
161 * provide MCLK to the codec */
162 /* TX clocks are inputs taken from SSI2 */
163 /* RX clocks are outputs taken from PORT4 */
164 AUDMUX_PTCR1 = AUDMUX_PTCR_RFS_DIR | AUDMUX_PTCR_RFSSEL_PORT4 |
165 AUDMUX_PTCR_RCLKDIR | AUDMUX_PTCR_RCSEL_PORT4;
166 /* RX data taken from PORT4 */
167 AUDMUX_PDCR1 = AUDMUX_PDCR_RXDSEL_PORT4;
168
169 /* PORT5 outputs TCLK sourced from PORT1 (SSI1) */
170 AUDMUX_PTCR5 = AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT1;
171 AUDMUX_PDCR5 = 0;
172
173 /* Setup SSIs */
174
175 /* SSI2 - SoC software interface for all I2S data out */
176 SSI_SCR2 = SSI_SCR_SYN | SSI_SCR_I2S_MODE_SLAVE;
177 SSI_STCR2 = SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSI |
178 SSI_STCR_TEFS | SSI_STCR_TFEN0;
179
180 /* 16 bits per word, 2 words per frame */
181 SSI_STCCR2 = SSI_STRCCR_WL16 | ((2-1) << SSI_STRCCR_DC_POS) |
182 ((4-1) << SSI_STRCCR_PM_POS);
183
184 /* Transmit low watermark */
185 SSI_SFCSR2 = (SSI_SFCSR2 & ~SSI_SFCSR_TFWM0) |
186 ((8-SDMA_SSI_TXFIFO_WML) << SSI_SFCSR_TFWM0_POS);
187 SSI_STMSK2 = 0;
188
189 /* SSI1 - provides MCLK to codec. Receives data from codec. */
190 SSI_STCR1 = SSI_STCR_TXDIR;
191
192 /* f(INT_BIT_CLK) =
193 * f(SYS_CLK) / [(DIV2 + 1)*(7*PSR + 1)*(PM + 1)*2] =
194 * 677737600 / [(1 + 1)*(7*0 + 1)*(0 + 1)*2] =
195 * 677737600 / 4 = 169344000 Hz
196 *
197 * 45.4.2.2 DIV2, PSR, and PM Bit Description states:
198 * Bits DIV2, PSR, and PM should not be all set to zero at the same
199 * time.
200 *
201 * The hardware seems to force a divide by 4 even if all bits are
202 * zero but comply by setting DIV2 and the others to zero.
203 */
204 SSI_STCCR1 = SSI_STRCCR_DIV2 | ((1-1) << SSI_STRCCR_PM_POS);
205
206 /* SSI1 - receive - asynchronous clocks */
207 SSI_SCR1 = SSI_SCR_I2S_MODE_SLAVE;
208
209 SSI_SRCR1 = SSI_SRCR_RXBIT0 | SSI_SRCR_RSCKP | SSI_SRCR_RFSI |
210 SSI_SRCR_REFS;
211
212 /* 16 bits per word, 2 words per frame */
213 SSI_SRCCR1 = SSI_STRCCR_WL16 | ((2-1) << SSI_STRCCR_DC_POS) |
214 ((4-1) << SSI_STRCCR_PM_POS);
215
216 /* Receive high watermark */
217 SSI_SFCSR1 = (SSI_SFCSR1 & ~SSI_SFCSR_RFWM0) |
218 (SDMA_SSI_RXFIFO_WML << SSI_SFCSR_RFWM0_POS);
219 SSI_SRMSK1 = 0;
220
221 /* Enable SSI1 (codec clock) */
222 SSI_SCR1 |= SSI_SCR_SSIEN;
223
224 audiohw_init();
225}
226
227void pcm_postinit(void)
228{
229 audiohw_postinit();
230}
231
232static void play_start_pcm(void)
233{
234 /* Stop transmission (if in progress) */
235 SSI_SCR2 &= ~SSI_SCR_TE;
236
237 SSI_SCR2 |= SSI_SCR_SSIEN; /* Enable SSI */
238 SSI_STCR2 |= SSI_STCR_TFEN0; /* Enable TX FIFO */
239
240 dma_play_data.state = 1; /* Enable DMA requests on unlock */
241
242 /* Do prefill to prevent swapped channels (see TLSbo61214 in MCIMX31CE).
243 * No actual solution was offered but this appears to work. */
244 SSI_STX0_2 = 0;
245 SSI_STX0_2 = 0;
246 SSI_STX0_2 = 0;
247 SSI_STX0_2 = 0;
248
249 SSI_SCR2 |= SSI_SCR_TE; /* Start transmitting */
250}
251
252static void play_stop_pcm(void)
253{
254 /* Wait for FIFO to empty */
255 while (SSI_SFCSR_TFCNT0 & SSI_SFCSR2);
256
257 /* Disable transmission */
258 SSI_STCR2 &= ~SSI_STCR_TFEN0;
259 SSI_SCR2 &= ~(SSI_SCR_TE | SSI_SCR_SSIEN);
260
261 /* Set state before pending to prevent race with interrupt */
262 /* Do not enable DMA requests on unlock */
263 dma_play_data.state = 0;
264 dma_play_data.callback_pending = 0;
265}
266
267void pcm_play_dma_start(const void *addr, size_t size)
268{
269 sdma_channel_stop(DMA_PLAY_CH_NUM);
270
271 /* Disable transmission */
272 SSI_STCR2 &= ~SSI_STCR_TFEN0;
273 SSI_SCR2 &= ~(SSI_SCR_TE | SSI_SCR_SSIEN);
274
275 addr = (void *)(((unsigned long)addr + 3) & ~3);
276 size &= ~3;
277
278 if (size <= 0)
279 return;
280
281 if (!sdma_channel_reset(DMA_PLAY_CH_NUM))
282 return;
283
284 clean_dcache_range(addr, size);
285 dma_play_bd.buf_addr =
286 (void *)addr_virt_to_phys((unsigned long)(void *)addr);
287 dma_play_bd.mode.count = size;
288 dma_play_bd.mode.command = TRANSFER_16BIT;
289 dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
290
291 play_start_pcm();
292 sdma_channel_run(DMA_PLAY_CH_NUM);
293}
294
295void pcm_play_dma_stop(void)
296{
297 sdma_channel_stop(DMA_PLAY_CH_NUM);
298 play_stop_pcm();
299}
300
301void pcm_play_dma_pause(bool pause)
302{
303 if (pause)
304 {
305 sdma_channel_pause(DMA_PLAY_CH_NUM);
306 play_stop_pcm();
307 }
308 else
309 {
310 play_start_pcm();
311 sdma_channel_run(DMA_PLAY_CH_NUM);
312 }
313}
314
315/* Return the number of bytes waiting - full L-R sample pairs only */
316size_t pcm_get_bytes_waiting(void)
317{
318 static unsigned long dsa DEVBSS_ATTR;
319 long offs, size;
320 int oldstatus;
321
322 /* read burst dma source address register in channel context */
323 sdma_read_words(&dsa, CHANNEL_CONTEXT_ADDR(DMA_PLAY_CH_NUM)+0x0b, 1);
324
325 oldstatus = disable_irq_save();
326 offs = dsa - (unsigned long)dma_play_bd.buf_addr;
327 size = dma_play_bd.mode.count;
328 restore_irq(oldstatus);
329
330 /* Be addresses are coherent (no buffer change during read) */
331 if (offs >= 0 && offs < size)
332 {
333 return (size - offs) & ~3;
334 }
335
336 return 0;
337}
338
339/* Return a pointer to the samples and the number of them in *count */
340const void * pcm_play_dma_get_peak_buffer(int *count)
341{
342 static unsigned long dsa DEVBSS_ATTR;
343 unsigned long addr;
344 long offs, size;
345 int oldstatus;
346
347 /* read burst dma source address register in channel context */
348 sdma_read_words(&dsa, CHANNEL_CONTEXT_ADDR(DMA_PLAY_CH_NUM)+0x0b, 1);
349
350 oldstatus = disable_irq_save();
351 addr = dsa;
352 offs = addr - (unsigned long)dma_play_bd.buf_addr;
353 size = dma_play_bd.mode.count;
354 restore_irq(oldstatus);
355
356 /* Be addresses are coherent (no buffer change during read) */
357 if (offs >= 0 && offs < size)
358 {
359 *count = (size - offs) >> 2;
360 return (void *)((addr + 2) & ~3);
361 }
362
363 *count = 0;
364 return NULL;
365}
366
367void * pcm_dma_addr(void *addr)
368{
369 return (void *)addr_virt_to_phys((unsigned long)addr);
370}
371
372#ifdef HAVE_RECORDING
373static struct buffer_descriptor dma_rec_bd DEVBSS_ATTR;
374static struct channel_descriptor dma_rec_cd DEVBSS_ATTR;
375
376static struct dma_data dma_rec_data =
377{
378 /* Initialize to a locked, stopped state */
379 .locked = 0,
380 .callback_pending = 0,
381 .state = 0
382};
383
384static void rec_dma_callback(void)
385{
386 pcm_more_callback_type2 more_ready;
387 int status = 0;
388
389 if (dma_rec_data.locked != 0)
390 {
391 dma_rec_data.callback_pending = dma_rec_data.state;
392 return; /* Callback is locked out */
393 }
394
395 if (dma_rec_bd.mode.status & BD_RROR)
396 status = DMA_REC_ERROR_DMA;
397
398 more_ready = pcm_callback_more_ready;
399
400 if (more_ready != NULL && more_ready(status) >= 0)
401 {
402 sdma_channel_run(DMA_REC_CH_NUM);
403 return;
404 }
405
406 /* Finished recording */
407 pcm_rec_dma_stop();
408 pcm_rec_dma_stopped_callback();
409}
410
411void pcm_rec_lock(void)
412{
413 if (++dma_rec_data.locked == 1)
414 imx31_regclr32(&SSI_SIER1, SSI_SIER_RDMAE);
415}
416
417void pcm_rec_unlock(void)
418{
419 if (--dma_rec_data.locked == 0 && dma_rec_data.state != 0)
420 {
421 int oldstatus = disable_irq_save();
422 int pending = dma_rec_data.callback_pending;
423 dma_rec_data.callback_pending = 0;
424 SSI_SIER1 |= SSI_SIER_RDMAE;
425 restore_irq(oldstatus);
426
427 /* Should an interrupt be forced instead? The upper pcm layer can
428 * call consumer's callback in thread context so technically this is
429 * acceptable. */
430 if (pending != 0)
431 rec_dma_callback();
432 }
433}
434
435void pcm_record_more(void *start, size_t size)
436{
437 start = (void *)(((unsigned long)start + 3) & ~3);
438 size &= ~3;
439
440 /* Invalidate - buffer must be coherent */
441 dump_dcache_range(start, size);
442
443 start = (void *)addr_virt_to_phys((unsigned long)start);
444
445 pcm_rec_peak_addr = start;
446 dma_rec_bd.buf_addr = start;
447 dma_rec_bd.mode.count = size;
448 dma_rec_bd.mode.command = TRANSFER_16BIT;
449 dma_rec_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
450}
451
452void pcm_rec_dma_stop(void)
453{
454 /* Stop receiving data */
455 sdma_channel_stop(DMA_REC_CH_NUM);
456
457 imx31_regclr32(&SSI_SIER1, SSI_SIER_RDMAE);
458
459 SSI_SCR1 &= ~SSI_SCR_RE; /* Disable RX */
460 SSI_SRCR1 &= ~SSI_SRCR_RFEN0; /* Disable RX FIFO */
461
462 /* Set state before pending to prevent race with interrupt */
463 /* Do not enable DMA requests on unlock */
464 dma_rec_data.state = 0;
465 dma_rec_data.callback_pending = 0;
466}
467
468void pcm_rec_dma_start(void *addr, size_t size)
469{
470 pcm_rec_dma_stop();
471
472 addr = (void *)(((unsigned long)addr + 3) & ~3);
473 size &= ~3;
474
475 if (size <= 0)
476 return;
477
478 if (!sdma_channel_reset(DMA_REC_CH_NUM))
479 return;
480
481 /* Invalidate - buffer must be coherent */
482 dump_dcache_range(addr, size);
483
484 addr = (void *)addr_virt_to_phys((unsigned long)addr);
485 pcm_rec_peak_addr = addr;
486 dma_rec_bd.buf_addr = addr;
487 dma_rec_bd.mode.count = size;
488 dma_rec_bd.mode.command = TRANSFER_16BIT;
489 dma_rec_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
490
491 dma_rec_data.state = 1;
492
493 SSI_SRCR1 |= SSI_SRCR_RFEN0; /* Enable RX FIFO */
494
495 /* Ensure clear FIFO */
496 while (SSI_SFCSR1 & SSI_SFCSR_RFCNT0)
497 SSI_SRX0_1;
498
499 /* Enable receive */
500 SSI_SCR1 |= SSI_SCR_RE;
501 sdma_channel_run(DMA_REC_CH_NUM);
502}
503
504void pcm_rec_dma_close(void)
505{
506 pcm_rec_dma_stop();
507 sdma_channel_close(DMA_REC_CH_NUM);
508}
509
510void pcm_rec_dma_init(void)
511{
512 pcm_rec_dma_stop();
513
514 /* Init channel information */
515 dma_rec_cd.bd_count = 1;
516 dma_rec_cd.callback = rec_dma_callback;
517 dma_rec_cd.shp_addr = SDMA_PER_ADDR_SSI1_RX1;
518 dma_rec_cd.wml = SDMA_SSI_RXFIFO_WML*2;
519 dma_rec_cd.per_type = SDMA_PER_SSI;
520 dma_rec_cd.tran_type = SDMA_TRAN_PER_2_EMI;
521 dma_rec_cd.event_id1 = SDMA_REQ_SSI1_RX1;
522
523 sdma_channel_init(DMA_REC_CH_NUM, &dma_rec_cd, &dma_rec_bd);
524 sdma_channel_set_priority(DMA_REC_CH_NUM, DMA_REC_CH_PRIORITY);
525}
526
527const void * pcm_rec_dma_get_peak_buffer(int *count)
528{
529 static unsigned long pda DEVBSS_ATTR;
530 unsigned long buf, addr, end, bufend;
531 int oldstatus;
532
533 /* read burst dma destination address register in channel context */
534 sdma_read_words(&pda, CHANNEL_CONTEXT_ADDR(DMA_REC_CH_NUM)+0x0a, 1);
535
536 oldstatus = disable_irq_save();
537 end = pda;
538 buf = (unsigned long)dma_rec_bd.buf_addr;
539 addr = (unsigned long)pcm_rec_peak_addr;
540 bufend = buf + dma_rec_bd.mode.count;
541 restore_irq(oldstatus);
542
543 /* Be addresses are coherent (no buffer change during read) */
544 if (addr >= buf && addr < bufend &&
545 end >= buf && end < bufend)
546 {
547 *count = (end >> 2) - (addr >> 2);
548 return (void *)(addr & ~3);
549 }
550
551 *count = 0;
552 return NULL;
553}
554
555#endif /* HAVE_RECORDING */