1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2006 by Michael Sevakis
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
28 #include "pcm_sampr.h"
33 /* 16-bit, L-R packed into 32 bits with left in the least significant halfword */
34 #define SAMPLE_SIZE 16
35 /* DMA Requests from IIS, Memory to peripheral, single transfer,
36 wait for DMA request, interrupt on complete */
37 #define DMA_PLAY_CONFIG ((DMA_REQ_IIS << DMA_CMD_REQ_ID_POS) | \
38 DMA_CMD_RAM_TO_PER | DMA_CMD_SINGLE | \
39 DMA_CMD_WAIT_REQ | DMA_CMD_INTR)
40 /* DMA status cannot be viewed from outside code in control because that can
41 * clear the interrupt from outside the handler and prevent the handler from
42 * from being called. Split up transfers to a reasonable size that is good as
43 * a timer, obtaining a keyclick position and peaking yet still keeps the
46 #define MAX_DMA_CHUNK_SIZE (pcm_curr_sampr >> 6) /* ~1/256 seconds */
48 /* 32-bit, one left 32-bit sample followed by one right 32-bit sample */
49 #define SAMPLE_SIZE 32
54 /* NOTE: The order of size and p is important if you use assembler
55 optimised fiq handler, so don't change it. */
59 uint32_t *p16
; /* For packed 16-16 stereo pairs */
60 uint16_t *p32
; /* For individual samples converted to 32-bit */
70 extern void *fiq_function
;
72 /* Dispatch to the proper handler and leave the main vector table alone */
73 void fiq_handler(void) ICODE_ATTR
__attribute__((naked
));
74 void fiq_handler(void)
77 "ldr pc, [pc, #-4] \n"
83 #ifdef HAVE_PCM_DMA_ADDRESS
84 void * pcm_dma_addr(void *addr
)
86 if (addr
!= NULL
&& (unsigned long)addr
< UNCACHED_BASE_ADDR
)
87 addr
= UNCACHED_ADDR(addr
);
92 /* TODO: Get simultaneous recording and playback to work. Just needs some tweaking */
94 /****************************************************************************
95 ** Playback DMA transfer
97 static struct dma_data dma_play_data IBSS_ATTR
=
99 /* Initialize to a locked, stopped state */
109 void pcm_dma_apply_settings(void)
111 audiohw_set_frequency(pcm_fsel
);
114 #if defined(CPU_PP502x)
115 /* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
116 void ICODE_ATTR
__attribute__((interrupt("FIQ"))) fiq_playback(void)
118 register size_t size
;
120 DMA0_STATUS
; /* Clear any pending interrupt */
122 size
= (DMA0_CMD
& 0xffff) + 4; /* Get size of trasfer that caused this
124 dma_play_data
.addr
+= size
;
125 dma_play_data
.size
-= size
;
129 if (dma_play_data
.size
> 0) {
130 size
= MAX_DMA_CHUNK_SIZE
;
131 /* Not at least MAX_DMA_CHUNK_SIZE left or there would be less
132 * than a FIFO's worth of data after this transfer? */
133 if (size
+ 16*4 > dma_play_data
.size
)
134 size
= dma_play_data
.size
;
136 /* Set the new DMA values and activate channel */
137 DMA0_RAM_ADDR
= dma_play_data
.addr
;
138 DMA0_CMD
= DMA_PLAY_CONFIG
| (size
- 4) | DMA_CMD_START
;
142 /* Buffer empty. Try to get more. */
143 pcm_play_get_more_callback((void **)&dma_play_data
.addr
,
144 &dma_play_data
.size
);
146 if (dma_play_data
.size
== 0) {
151 if (dma_play_data
.addr
< UNCACHED_BASE_ADDR
) {
152 /* Flush any pending cache writes */
153 dma_play_data
.addr
= UNCACHED_ADDR(dma_play_data
.addr
);
159 /* ASM optimised FIQ handler. Checks for the minimum allowed loop cycles by
160 * evalutation of free IISFIFO-slots against available source buffer words.
161 * Through this it is possible to move the check for IIS_TX_FREE_COUNT outside
162 * the loop and do some further optimization. Right after the loops (source
163 * buffer -> IISFIFO) are done we need to check whether we have to exit FIQ
164 * handler (this must be done, if all free FIFO slots were filled) or we will
165 * have to get some new source data. Important information kept from former
166 * ASM implementation (not used anymore): GCC fails to make use of the fact
167 * that FIQ mode has registers r8-r14 banked, and so does not need to be saved.
168 * This routine uses only these registers, and so will never touch the stack
169 * unless it actually needs to do so when calling pcm_callback_for_more.
170 * C version is still included below for reference and testing.
173 void fiq_playback(void) ICODE_ATTR
__attribute__((naked
));
174 void fiq_playback(void)
176 /* r10 contains IISCONFIG address (set in crt0.S to minimise code in actual
177 * FIQ handler. r11 contains address of p (also set in crt0.S). Most other
178 * addresses we need are generated by using offsets with these two.
179 * r10 + 0x40 is IISFIFO_WR, and r10 + 0x0c is IISFIFO_CFG.
180 * r8 and r9 contains local copies of p and size respectively.
181 * r0-r3 and r12 is a working register.
184 "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */
186 #if CONFIG_CPU == PP5002
187 "ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux */
190 "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */
191 "cmp r9, #0 \n" /* is size 0? */
192 "beq .more_data \n" /* if so, ask pcmbuf for more data */
194 #if SAMPLE_SIZE == 16
196 "ldr r0, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */
197 "and r0, r0, %[mask] \n" /* r0 = IIS_TX_FREE_COUNT << 16 (PP502x) */
199 "mov r1, r0, lsr #16 \n" /* number of free FIFO slots */
200 "cmp r1, r9, lsr #2 \n" /* number of words from source */
201 "movgt r1, r9, lsr #2 \n" /* r1 = amount of allowed loops */
202 "sub r9, r9, r1, lsl #2 \n" /* r1 words will be written in following loop */
206 "ldmgeia r8!, {r2, r12} \n" /* load four samples */
207 "strge r2 , [r10, %[wr]] \n" /* write sample 0-1 to IISFIFO_WR */
208 "strge r12, [r10, %[wr]] \n" /* write sample 2-3 to IISFIFO_WR */
209 "subges r1, r1, #2 \n" /* one more loop? */
210 "bge .fifo_loop_2 \n" /* yes, continue */
212 "tst r1, #1 \n" /* two samples (one word) left? */
213 "ldrne r12, [r8], #4 \n" /* load two samples */
214 "strne r12, [r10, %[wr]] \n" /* write sample 0-1 to IISFIFO_WR */
216 "cmp r9, #0 \n" /* either FIFO is full or source buffer is empty */
217 "bgt .exit \n" /* if source buffer is not empty, FIFO must be full */
218 #elif SAMPLE_SIZE == 32
220 "ldr r0, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */
221 "and r0, r0, %[mask] \n" /* r0 = IIS_TX_FREE_COUNT << 23 (PP5002) */
223 "movs r1, r0, lsr #24 \n" /* number of free pairs of FIFO slots */
224 "beq .exit \n" /* no complete pair? -> exit */
225 "cmp r1, r9, lsr #2 \n" /* number of words from source */
226 "movgt r1, r9, lsr #2 \n" /* r1 = amount of allowed loops */
227 "sub r9, r9, r1, lsl #2 \n" /* r1 words will be written in following loop */
230 "ldr r12, [r8], #4 \n" /* load two samples */
231 "mov r2 , r12, lsl #16 \n" /* put left sample at the top bits */
232 "str r2 , [r10, %[wr]] \n" /* write top sample to IISFIFO_WR */
233 "str r12, [r10, %[wr]] \n" /* write low sample to IISFIFO_WR*/
234 "subs r1, r1, #1 \n" /* one more loop? */
235 "bgt .fifo_loop \n" /* yes, continue */
237 "cmp r9, #0 \n" /* either FIFO is full or source buffer is empty */
238 "bgt .exit \n" /* if source buffer is not empty, FIFO must be full */
242 "ldr r2, =pcm_play_get_more_callback \n"
243 "mov r0, r11 \n" /* r0 = &p */
244 "add r1, r11, #4 \n" /* r1 = &size */
245 "mov lr, pc \n" /* call pcm_play_get_more_callback */
247 "ldmia r11, { r8-r9 } \n" /* load new p and size */
249 "bne .check_fifo \n" /* size != 0? refill */
251 ".exit: \n" /* (r9=0 if stopping, look above) */
252 "stmia r11, { r8-r9 } \n" /* save p and size */
253 "ldmfd sp!, { r0-r3, lr } \n"
254 "subs pc, lr, #4 \n" /* FIQ specific return sequence */
256 : /* These must only be integers! No regs */
257 : [mask
]"i"(IIS_TX_FREE_MASK
),
258 [cfg
]"i"((int)&IISFIFO_CFG
- (int)&IISCONFIG
),
259 [wr
]"i"((int)&IISFIFO_WR
- (int)&IISCONFIG
)
262 #else /* C version for reference */
263 void fiq_playback(void) __attribute__((interrupt ("FIQ"))) ICODE_ATTR
;
264 /* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
265 void fiq_playback(void)
267 #if CONFIG_CPU == PP5002
272 while (dma_play_data
.size
> 0) {
273 if (IIS_TX_FREE_COUNT
< 2) {
276 #if SAMPLE_SIZE == 16
277 IISFIFO_WR
= *dma_play_data
.p16
++;
278 #elif SAMPLE_SIZE == 32
279 IISFIFO_WR
= *dma_play_data
.p32
++ << 16;
280 IISFIFO_WR
= *dma_play_data
.p32
++ << 16;
282 dma_play_data
.size
-= 4;
285 /* p is empty, get some more data */
286 pcm_play_get_more_callback((void **)&dma_play_data
.addr
,
287 &dma_play_data
.size
);
288 } while (dma_play_data
.size
);
292 #endif /* ASM / C selection */
293 #endif /* CPU_PP502x */
295 /* For the locks, FIQ must be disabled because the handler manipulates
296 IISCONFIG and the operation is not atomic - dual core support
297 will require other measures */
298 void pcm_play_lock(void)
300 int status
= disable_fiq_save();
302 if (++dma_play_data
.locked
== 1) {
304 CPU_INT_DIS
= DMA_MASK
;
306 IIS_IRQTX_REG
&= ~IIS_IRQTX
;
313 void pcm_play_unlock(void)
315 int status
= disable_fiq_save();
317 if (--dma_play_data
.locked
== 0 && dma_play_data
.state
!= 0) {
319 CPU_INT_EN
= DMA_MASK
;
321 IIS_IRQTX_REG
|= IIS_IRQTX
;
328 static void play_start_pcm(void)
330 fiq_function
= fiq_playback
;
333 /* Not at least MAX_DMA_CHUNK_SIZE left or there would be less than a
334 * FIFO's worth of data after this transfer? */
335 size_t size
= MAX_DMA_CHUNK_SIZE
;
336 if (size
+ 16*4 > dma_play_data
.size
)
337 size
= dma_play_data
.size
;
339 DMA0_RAM_ADDR
= dma_play_data
.addr
;
340 DMA0_CMD
= DMA_PLAY_CONFIG
| (size
- 4) | DMA_CMD_START
;
341 dma_play_data
.state
= 1;
343 IISCONFIG
&= ~IIS_TXFIFOEN
; /* Stop transmitting */
345 /* Fill the FIFO or start when data is used up */
347 if (IIS_TX_FREE_COUNT
< 2 || dma_play_data
.size
== 0) {
348 IISCONFIG
|= IIS_TXFIFOEN
; /* Start transmitting */
349 dma_play_data
.state
= 1;
353 #if SAMPLE_SIZE == 16
354 IISFIFO_WR
= *dma_play_data
.p16
++;
355 #elif SAMPLE_SIZE == 32
356 IISFIFO_WR
= *dma_play_data
.p32
++ << 16;
357 IISFIFO_WR
= *dma_play_data
.p32
++ << 16;
359 dma_play_data
.size
-= 4;
364 static void play_stop_pcm(void)
367 unsigned long status
= DMA0_STATUS
; /* Snapshot- resume from this point */
368 unsigned long cmd
= DMA0_CMD
;
372 DMA0_CMD
= cmd
& ~(DMA_CMD_START
| DMA_CMD_INTR
);
374 /* Wait for not busy + clear int */
375 while (DMA0_STATUS
& (DMA_STATUS_BUSY
| DMA_STATUS_INTR
));
377 if (status
& DMA_STATUS_BUSY
) {
378 /* Transfer was interrupted - leave what's left */
379 size
= (cmd
& 0xfffc) - (status
& 0xfffc);
381 else if (status
& DMA_STATUS_INTR
) {
382 /* Transfer was finished - DMA0_STATUS will have been reloaded
383 * automatically with size in DMA0_CMD. Setup to restart on next
385 size
= (cmd
& 0xfffc) + 4;
387 /* else not an active state - size = 0 */
389 dma_play_data
.addr
+= size
;
390 dma_play_data
.size
-= size
;
392 if (dma_play_data
.size
== 0)
393 dma_play_data
.addr
= 0; /* Entire buffer has completed. */
395 /* Disable TX interrupt */
396 IIS_IRQTX_REG
&= ~IIS_IRQTX
;
399 /* Wait for FIFO to empty */
400 while (!IIS_TX_IS_EMPTY
);
402 dma_play_data
.state
= 0;
405 void pcm_play_dma_start(const void *addr
, size_t size
)
408 /* This will become more important later - and different ! */
409 dma_play_data
.core
= processor_id(); /* save initiating core */
415 if ((unsigned long)addr
< UNCACHED_BASE_ADDR
) {
416 /* Flush any pending cache writes */
417 addr
= UNCACHED_ADDR(addr
);
421 dma_play_data
.addr
= (unsigned long)addr
;
422 dma_play_data
.size
= size
;
423 DMA0_PER_ADDR
= (unsigned long)&IISFIFO_WR
;
424 DMA0_FLAGS
= DMA_FLAGS_UNK26
;
425 DMA0_INCR
= DMA_INCR_RANGE_FIXED
| DMA_INCR_WIDTH_32BIT
;
427 dma_play_data
.addr
= (unsigned long)addr
;
428 dma_play_data
.size
= size
;
434 /* Stops the DMA transfer and interrupt */
435 void pcm_play_dma_stop(void)
438 dma_play_data
.addr
= 0;
439 dma_play_data
.size
= 0;
441 dma_play_data
.core
= 0; /* no core in control */
445 void pcm_play_dma_pause(bool pause
)
454 size_t pcm_get_bytes_waiting(void)
456 return dma_play_data
.size
& ~3;
459 void pcm_play_dma_init(void)
461 /* Initialize default register values. */
465 /* Enable DMA controller */
466 DMA_MASTER_CONTROL
|= DMA_MASTER_CONTROL_EN
;
467 /* FIQ priority for DMA */
468 CPU_INT_PRIORITY
|= DMA_MASK
;
469 /* Enable request?? Not setting or clearing everything doesn't seem to
470 * prevent it operating. Perhaps important for reliability (how requests
472 DMA_REQ_STATUS
|= 1ul << DMA_REQ_IIS
;
475 /* Set up banked registers for FIQ mode */
477 /* Use non-banked registers for scratch. */
478 register volatile void *iiscfg
asm("r0") = &IISCONFIG
;
479 register volatile void *dmapd
asm("r1") = &dma_play_data
;
482 "mrs r2, cpsr \n" /* Save mode and interrupt status */
483 "msr cpsr_c, #0xd1 \n" /* Switch to FIQ mode */
486 "mov r10, %[iiscfg] \n"
487 "mov r11, %[dmapd] \n"
490 : [iiscfg
]"r"(iiscfg
), [dmapd
]"r"(dmapd
)
493 /* FIQ priority for I2S */
494 CPU_INT_PRIORITY
|= IIS_MASK
;
495 CPU_INT_EN
= IIS_MASK
;
498 IISCONFIG
|= IIS_TXFIFOEN
;
501 void pcm_postinit(void)
506 const void * pcm_play_dma_get_peak_buffer(int *count
)
508 unsigned long addr
, size
;
510 int status
= disable_fiq_save();
511 addr
= dma_play_data
.addr
;
512 size
= dma_play_data
.size
;
516 return (void *)((addr
+ 2) & ~3);
519 /****************************************************************************
520 ** Recording DMA transfer
522 #ifdef HAVE_RECORDING
523 /* PCM recording interrupt routine lockout */
524 static struct dma_data dma_rec_data IBSS_ATTR
=
526 /* Initialize to a locked, stopped state */
536 /* For the locks, FIQ must be disabled because the handler manipulates
537 IISCONFIG and the operation is not atomic - dual core support
538 will require other measures */
539 void pcm_rec_lock(void)
541 int status
= disable_fiq_save();
543 if (++dma_rec_data
.locked
== 1)
544 IIS_IRQRX_REG
&= ~IIS_IRQRX
;
549 void pcm_rec_unlock(void)
551 int status
= disable_fiq_save();
553 if (--dma_rec_data
.locked
== 0 && dma_rec_data
.state
!= 0)
554 IIS_IRQRX_REG
|= IIS_IRQRX
;
559 /* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
560 void fiq_record(void) ICODE_ATTR
__attribute__((interrupt ("FIQ")));
562 #if defined(SANSA_C200) || defined(SANSA_E200)
563 void fiq_record(void)
565 register int32_t value
;
567 if (audio_channels
== 2) {
569 while (dma_rec_data
.size
> 0) {
570 if (IIS_RX_FULL_COUNT
< 2) {
574 /* Discard every other sample since ADC clock is 1/2 LRCK */
578 *dma_rec_data
.p16
++ = value
;
579 dma_rec_data
.size
-= 4;
581 /* TODO: Figure out how to do IIS loopback */
582 if (audio_output_source
!= AUDIO_SRC_PLAYBACK
) {
583 if (IIS_TX_FREE_COUNT
>= 16) {
584 /* Resync the output FIFO - it ran dry */
594 /* RX is left channel mono */
595 while (dma_rec_data
.size
> 0) {
596 if (IIS_RX_FULL_COUNT
< 2) {
600 /* Discard every other sample since ADC clock is 1/2 LRCK */
604 value
= (uint16_t)value
| (value
<< 16);
606 *dma_rec_data
.p16
++ = value
;
607 dma_rec_data
.size
-= 4;
609 if (audio_output_source
!= AUDIO_SRC_PLAYBACK
) {
610 if (IIS_TX_FREE_COUNT
>= 16) {
611 /* Resync the output FIFO - it ran dry */
616 value
= *((int32_t *)dma_rec_data
.p16
- 1);
623 pcm_rec_more_ready_callback(0, (void *)&dma_rec_data
.addr
,
628 void fiq_record(void)
630 while (dma_rec_data
.size
> 0) {
631 if (IIS_RX_FULL_COUNT
< 2) {
635 #if SAMPLE_SIZE == 16
636 *dma_rec_data
.p16
++ = IISFIFO_RD
;
637 #elif SAMPLE_SIZE == 32
638 *dma_rec_data
.p32
++ = IISFIFO_RD
>> 16;
639 *dma_rec_data
.p32
++ = IISFIFO_RD
>> 16;
641 dma_rec_data
.size
-= 4;
644 pcm_rec_more_ready_callback(0, (void *)&dma_rec_data
.addr
,
648 #endif /* SANSA_E200 */
650 void pcm_rec_dma_stop(void)
652 /* disable interrupt */
653 IIS_IRQRX_REG
&= ~IIS_IRQRX
;
655 dma_rec_data
.state
= 0;
656 dma_rec_data
.size
= 0;
658 dma_rec_data
.core
= 0x00;
662 IISCONFIG
&= ~IIS_RXFIFOEN
;
663 IISFIFO_CFG
|= IIS_RXCLR
;
666 void pcm_rec_dma_start(void *addr
, size_t size
)
670 dma_rec_data
.addr
= (unsigned long)addr
;
671 dma_rec_data
.size
= size
;
673 /* This will become more important later - and different ! */
674 dma_rec_data
.core
= processor_id(); /* save initiating core */
676 /* setup FIQ handler */
677 fiq_function
= fiq_record
;
679 /* interrupt on full fifo, enable record fifo interrupt */
680 dma_rec_data
.state
= 1;
683 IISCONFIG
|= IIS_RXFIFOEN
;
685 /* enable IIS interrupt as FIQ */
686 CPU_INT_PRIORITY
|= IIS_MASK
;
687 CPU_INT_EN
= IIS_MASK
;
690 void pcm_rec_dma_close(void)
693 } /* pcm_close_recording */
695 void pcm_rec_dma_init(void)
700 const void * pcm_rec_dma_get_peak_buffer(void)
702 return (void *)((unsigned long)dma_rec_data
.addr
& ~3);
703 } /* pcm_rec_dma_get_peak_buffer */
705 #endif /* HAVE_RECORDING */