diff options
Diffstat (limited to 'firmware/target/arm')
-rw-r--r-- | firmware/target/arm/crt0-pp.S | 8 | ||||
-rw-r--r-- | firmware/target/arm/i2s-pp.c | 81 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c | 114 | ||||
-rw-r--r-- | firmware/target/arm/pcm-pp.c | 846 | ||||
-rw-r--r-- | firmware/target/arm/pnx0101/pcm-pnx0101.c | 69 | ||||
-rw-r--r-- | firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c | 374 | ||||
-rw-r--r-- | firmware/target/arm/system-arm.h | 6 | ||||
-rw-r--r-- | firmware/target/arm/system-pp502x.c | 1 | ||||
-rw-r--r-- | firmware/target/arm/system-target.h | 14 |
9 files changed, 673 insertions, 840 deletions
diff --git a/firmware/target/arm/crt0-pp.S b/firmware/target/arm/crt0-pp.S index 858f795aad..8fd1e31f09 100644 --- a/firmware/target/arm/crt0-pp.S +++ b/firmware/target/arm/crt0-pp.S @@ -243,10 +243,10 @@ cpu_init: msr cpsr_c, #0xd1 /* IRQ/FIQ disabled */ ldr sp, =fiq_stack /* We'll load the banked FIQ mode registers with useful values here. - These values will be used in the FIQ handler in pcm_playback.c */ - ldr r12, =IIS_CONFIG + These values will be used in the FIQ handler in pcm-pp.c */ + ldr r10, =IIS_CONFIG - ldr r11, =p + ldr r11, =dma_play_data /* Let abort and undefined modes use IRQ stack */ msr cpsr_c, #0xd7 /* IRQ/FIQ disabled */ @@ -328,7 +328,7 @@ vectors: .word data_abort_handler .word reserved_handler .word irq_handler - .word 0 /* fiq handler set in pcm driver */ + .word fiq_handler .text diff --git a/firmware/target/arm/i2s-pp.c b/firmware/target/arm/i2s-pp.c index e06ee835a6..24d901a4c1 100644 --- a/firmware/target/arm/i2s-pp.c +++ b/firmware/target/arm/i2s-pp.c @@ -47,99 +47,40 @@ void i2s_reset(void) } #else /* PP502X */ -/* All I2S formats send MSB first */ - -/* Data format on the I2S bus */ -#define FORMAT_MASK (0x3 << 10) -#define FORMAT_I2S (0x0 << 10) /* Standard I2S - leading dummy bit */ -#define FORMAT_1 (0x1 << 10) -#define FORMAT_LJUST (0x2 << 10) /* Left justified - no dummy bit */ -#define FORMAT_3 (0x3 << 10) -/* Other formats not yet known */ - -/* Data size on I2S bus */ -#define SIZE_MASK (0x3 << 8) -#define SIZE_16BIT (0x0 << 8) -/* Other sizes not yet known */ - -/* Data size/format on I2S FIFO */ -#define FIFO_FORMAT_MASK (0x7 << 4) -#define FIFO_FORMAT_0 (0x0 << 4) -/* Big-endian formats - data sent to the FIFO must be big endian. - * I forgot which is which size but did test them. */ -#define FIFO_FORMAT_1 (0x1 << 4) -#define FIFO_FORMAT_2 (0x2 << 4) - /* 32bit-MSB-little endian */ -#define FIFO_FORMAT_LE32 (0x3 << 4) - /* 16bit-MSB-little endian */ -#define FIFO_FORMAT_LE16 (0x4 << 4) - -/* FIFO formats 0x5 and above seem equivalent to 0x4 ?? */ - -/** - * PP502x - * - * IISCONFIG bits: - * | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | - * | RESET | |TXFIFOEN|RXFIFOEN| | ???? | MS | ???? | - * | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | - * | | | | | | | | | - * | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | - * | | | | | Bus Format[1:0] | Size[1:0] | - * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | - * | | Size Format[2:0] | ???? | ???? | IRQTX | IRQRX | - * - * IISFIFO_CFG bits: - * | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | - * | | Free[6:0] | - * | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | - * | | | | | | | | | - * | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | - * | | | | RXCLR | | | | TXCLR | - * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | - * | | | RX_ATN_LEVEL | | | TX_ATN_LEVEL | - */ - -/* Are we I2S Master or slave? */ -#define I2S_MASTER (1<<25) - -#define I2S_RESET (0x1 << 31) - /* * Reset the I2S BIT.FORMAT I2S, 16bit, FIFO.FORMAT 32bit */ void i2s_reset(void) { /* I2S soft reset */ - IISCONFIG |= I2S_RESET; - IISCONFIG &= ~I2S_RESET; + IISCONFIG |= IIS_RESET; + IISCONFIG &= ~IIS_RESET; /* BIT.FORMAT */ - IISCONFIG = ((IISCONFIG & ~FORMAT_MASK) | FORMAT_I2S); + IISCONFIG = ((IISCONFIG & ~IIS_FORMAT_MASK) | IIS_FORMAT_IIS); /* BIT.SIZE */ - IISCONFIG = ((IISCONFIG & ~SIZE_MASK) | SIZE_16BIT); + IISCONFIG = ((IISCONFIG & ~IIS_SIZE_MASK) | IIS_SIZE_16BIT); /* FIFO.FORMAT */ /* If BIT.SIZE < FIFO.FORMAT low bits will be 0 */ #ifdef HAVE_AS3514 /* AS3514 can only operate as I2S Slave */ - IISCONFIG |= I2S_MASTER; + IISCONFIG |= IIS_MASTER; /* Set I2S to 44.1kHz */ outl((inl(0x70002808) & ~(0x1ff)) | 33, 0x70002808); outl(7, 0x60006080); - - IISCONFIG = ((IISCONFIG & ~FIFO_FORMAT_MASK) | FIFO_FORMAT_LE16); + IISCONFIG = ((IISCONFIG & ~IIS_FIFO_FORMAT_MASK) | IIS_FIFO_FORMAT_LE16); #else - IISCONFIG = ((IISCONFIG & ~FIFO_FORMAT_MASK) | FIFO_FORMAT_LE32); + IISCONFIG = ((IISCONFIG & ~IIS_FIFO_FORMAT_MASK) | IIS_FIFO_FORMAT_LE32); #endif - /* RX_ATN_LVL=1 == when 12 slots full */ - /* TX_ATN_LVL=1 == when 12 slots empty */ - IISFIFO_CFG |= 0x33; + /* RX_ATN_LVL = when 12 slots full */ + /* TX_ATN_LVL = when 12 slots empty */ + IISFIFO_CFG |= IIS_RX_FULL_LVL_12 | IIS_TX_EMPTY_LVL_12; /* Rx.CLR = 1, TX.CLR = 1 */ - IISFIFO_CFG |= 0x1100; + IISFIFO_CFG |= IIS_RXCLR | IIS_TXCLR; } #endif /* CONFIG_CPU == */ diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c b/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c index 73bd9fef14..c29c4b2930 100644 --- a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c @@ -25,69 +25,145 @@ #include "file.h" #include "mmu-imx31.h" +static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ + void fiq_handler(void) __attribute__((naked)); +/* Implement separately on recording and playback - simply disable the + specific DMA interrupt. Disable the FIQ itself only temporarily to sync + with the DMA interrupt and restore its previous state. The pcm routines + will call the lockout first then call into these low-level routines. */ +struct dma_lock +{ + int locked; + unsigned long state; +}; + +static struct dma_play_lock = +{ + .locked = 0, + .state = 0, /* Initialize this as disabled */ +}; + +void pcm_play_lock(void) +{ + int status = set_fiq_status(FIQ_DISABLED); + if (++dma_play_lock.locked == 1) + ; /* Mask the DMA interrupt */ + set_fiq_status(status); +} + +void pcm_play_unlock(void) +{ + int status = set_fiq_status(FIQ_DISABLED); + if (--dma_play_lock.locked == 0) + ; /* Unmask the DMA interrupt if enabled */ + set_fiq_status(status); +} + static void _pcm_apply_settings(void) { + if (pcm_freq != pcm_curr_sampr) + { + pcm_curr_sampr = pcm_freq; + /* Change hardware sample rate */ + /* */ + } } void pcm_apply_settings(void) { + /* Lockout FIQ and sync the hardware to the settings */ + int oldstatus = set_fiq_status(FIQ_DISABLED); + _pcm_apply_settings(); + set_fiq_status(oldstatus); } -void pcm_init(void) +void pcm_play_dma_init(void) { + pcm_set_frequency(SAMPR_44); + +#if 0 + /* Do basic init enable output in pcm_postinit if popping is a + problem at boot to enable a lenghy delay and let the boot + process continue */ + audiohw_init(); +#endif } void pcm_postinit(void) { } -void pcm_play_dma_start(const void *addr, size_t size) +/* Connect the DMA and start filling the FIFO */ +static void play_start_pcm(void) { - (void)addr; - (void)size; +#if 0 + /* unmask DMA interrupt when unlocking */ + dma_play_lock.state = 0; /* Set to allow pcm_play_unlock to unmask interrupt */ +#endif } -static void pcm_play_dma_stop_fiq(void) +/* Disconnect the DMA and wait for the FIFO to clear */ +static void play_stop_pcm(void) { +#if 0 + /* Keep interrupt masked when unlocking */ + dma_play_lock.state = 0; /* Set to keep pcm_play_unlock from unmasking interrupt */ +#endif } -void fiq_handler(void) +void pcm_play_dma_start(const void *addr, size_t size) { + (void)addr; + (void)size; } -/* Disconnect the DMA and wait for the FIFO to clear */ void pcm_play_dma_stop(void) { + play_stop_pcm(); } -void pcm_play_pause_pause(void) +void pcm_play_dma_pause(bool pause) { + if (pause) + { + play_stop_pcm(); + } + else + { + play_start_pcm(); + } } -void pcm_play_pause_unpause(void) +/* Get more samples to play out - call pcm_play_dma_stop and + pcm_play_dma_stopped_callback if the data runs out */ +void fiq_handler(void) { +#if 0 + /* Callback missing or no more DMA to do */ + pcm_play_dma_stop(); + pcm_play_dma_stopped_callback(); +#endif } +/* Set the pcm frequency hardware will use when play is next started or + when pcm_apply_settings is called. Do not apply the setting to the + hardware here but simply cache it. */ void pcm_set_frequency(unsigned int frequency) { - (void)frequency; + pcm_freq = frequency; } +/* Return the number of bytes waiting - full L-R sample pairs only */ size_t pcm_get_bytes_waiting(void) { } -void pcm_mute(bool mute) +/* Return a pointer to the samples and the number of them in *count */ +const void * pcm_play_dma_get_peak_buffer(int *count) { + (void)count; } -/** - * Return playback peaks - Peaks ahead in the DMA buffer based upon the - * calling period to attempt to compensate for - * delay. - */ -void pcm_calculate_peaks(int *left, int *right) -{ -} +/* Any recording functionality should be implemented similarly */ diff --git a/firmware/target/arm/pcm-pp.c b/firmware/target/arm/pcm-pp.c index f38757ec6c..50e5b272aa 100644 --- a/firmware/target/arm/pcm-pp.c +++ b/firmware/target/arm/pcm-pp.c @@ -23,329 +23,286 @@ #include "audio.h" #include "sound.h" -/* peaks */ -#ifdef HAVE_RECORDING -static unsigned long *rec_peak_addr; -static int rec_peak_left, rec_peak_right; +/** DMA **/ + +#if defined(HAVE_AS3514) +/* E200 uses 16bit FIFO - all others should be able to as well - + i2s-pp.c has to set the right size as well */ +#define SAMPLE_SIZE 16 +#else +#define SAMPLE_SIZE 32 #endif -/** DMA **/ -#ifdef CPU_PP502x -#define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x3f000000) >> 24) -#elif CONFIG_CPU == PP5002 -#define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x7800000) >> 23) +struct dma_data +{ +/* NOTE: The order of size and p is important if you use assembler + optimised fiq handler, so don't change it. */ +#if SAMPLE_SIZE == 16 + uint32_t *p; +#elif SAMPLE_SIZE == 32 + uint16_t *p; #endif + size_t size; +#if NUM_CORES > 1 + unsigned core; +#endif + int locked; + int state; +} __attribute__((packed)); + +extern void *fiq_function; + +/* Dispatch to the proper handler and leave the main vector table alone */ +void fiq_handler(void) ICODE_ATTR __attribute__((naked)); +void fiq_handler(void) +{ + asm volatile ( + "ldr pc, [pc, #-4] \n" + "fiq_function: \n" + ".word 0 \n" + ); +} + +/* TODO: Get simultaneous recording and playback to work. Just needs some tweaking */ /**************************************************************************** ** Playback DMA transfer **/ -static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ +struct dma_data dma_play_data NOCACHEBSS_ATTR = +{ + /* Initialize to a locked, stopped state */ + .p = NULL, + .size = 0, +#if NUM_CORES > 1 + .core = 0x00, +#endif + .locked = 0, + .state = 0 +}; -/* NOTE: The order of these two variables is important if you use the iPod - assembler optimised fiq handler, so don't change it. */ -unsigned short* p IBSS_ATTR; -size_t p_size IBSS_ATTR; +static unsigned long pcm_freq NOCACHEDATA_ATTR = HW_SAMPR_DEFAULT; /* 44.1 is default */ + +void pcm_set_frequency(unsigned int frequency) +{ + (void)frequency; + pcm_freq = HW_SAMPR_DEFAULT; +} + +void pcm_apply_settings(void) +{ + pcm_curr_sampr = pcm_freq; +} /* ASM optimised FIQ handler. GCC fails to make use of the fact that FIQ mode has registers r8-r14 banked, and so does not need to be saved. This routine uses only these registers, and so will never touch the stack unless it actually needs to do so when calling pcm_callback_for_more. C version is - still included below for reference. + still included below for reference and testing. */ #if 1 -void fiq(void) ICODE_ATTR __attribute__((naked)); -void fiq(void) +void fiq_playback(void) ICODE_ATTR __attribute__((naked)); +void fiq_playback(void) { - /* r12 contains IISCONFIG address (set in crt0.S to minimise code in actual + /* r10 contains IISCONFIG address (set in crt0.S to minimise code in actual * FIQ handler. r11 contains address of p (also set in crt0.S). Most other * addresses we need are generated by using offsets with these two. - * r12 + 0x40 is IISFIFO_WR, and r12 + 0x0c is IISFIFO_CFG. - * r8 and r9 contains local copies of p_size and p respectively. - * r10 is a working register. + * r10 + 0x40 is IISFIFO_WR, and r10 + 0x0c is IISFIFO_CFG. + * r8 and r9 contains local copies of p and size respectively. + * r12 is a working register. */ asm volatile ( #if CONFIG_CPU == PP5002 - "ldr r10, =0xcf001040 \n\t" /* Some magic from iPodLinux */ - "ldr r10, [r10] \n\t" - "ldr r10, [r12, #0x1c]\n\t" - "bic r10, r10, #0x200 \n\t" /* clear interrupt */ - "str r10, [r12, #0x1c]\n\t" -#else - "ldr r10, [r12] \n\t" - "bic r10, r10, #0x2 \n\t" /* clear interrupt */ - "str r10, [r12] \n\t" -#endif - "ldr r8, [r11, #4] \n\t" /* r8 = p_size */ - "ldr r9, [r11] \n\t" /* r9 = p */ - ".loop: \n\t" - "cmp r8, #0 \n\t" /* is p_size 0? */ - "beq .more_data \n\t" /* if so, ask pcmbuf for more data */ - ".fifo_loop: \n\t" -#if CONFIG_CPU == PP5002 - "ldr r10, [r12, #0x1c]\n\t" /* read IISFIFO_CFG to check FIFO status */ - "and r10, r10, #0x7800000\n\t" - "cmp r10, #0x800000 \n\t" -#else - "ldr r10, [r12, #0x0c]\n\t" /* read IISFIFO_CFG to check FIFO status */ - "and r10, r10, #0x3f0000\n\t" - "cmp r10, #0x10000 \n\t" -#endif - "bls .fifo_full \n\t" /* FIFO full, exit */ - "ldr r10, [r9], #4 \n\t" /* load two samples */ -#ifdef HAVE_AS3514 - "str r10, [r12, #0x40]\n\t" /* write them */ -#else - "mov r10, r10, ror #16\n\t" /* put left sample at the top bits */ - "str r10, [r12, #0x40]\n\t" /* write top sample, lower sample ignored */ - "mov r10, r10, lsl #16\n\t" /* shift lower sample up */ - "str r10, [r12, #0x40]\n\t" /* then write it */ -#endif - "subs r8, r8, #4 \n\t" /* check if we have more samples */ - "bne .fifo_loop \n\t" /* yes, continue */ - ".more_data: \n\t" - "stmdb sp!, { r0-r3, r12, lr}\n\t" /* stack scratch regs and lr */ - "mov r0, r11 \n\t" /* r0 = &p */ - "add r1, r11, #4 \n\t" /* r1 = &p_size */ - "str r9, [r0] \n\t" /* save internal copies of variables back */ - "str r8, [r1] \n\t" - "ldr r2, =pcm_callback_for_more\n\t" - "ldr r2, [r2] \n\t" /* get callback address */ - "cmp r2, #0 \n\t" /* check for null pointer */ - "movne lr, pc \n\t" /* call pcm_callback_for_more */ - "bxne r2 \n\t" - "ldmia sp!, { r0-r3, r12, lr}\n\t" - "ldr r8, [r11, #4] \n\t" /* reload p_size and p */ - "ldr r9, [r11] \n\t" - "cmp r8, #0 \n\t" /* did we actually get more data? */ - "bne .loop \n\t" /* yes, continue to try feeding FIFO */ - ".dma_stop: \n\t" /* no more data, do dma_stop() and exit */ - "ldr r10, =pcm_playing\n\t" - "strb r8, [r10] \n\t" /* pcm_playing = false (r8=0, look above) */ - "ldr r10, =pcm_paused \n\t" - "strb r8, [r10] \n\t" /* pcm_paused = false (r8=0, look above) */ - "ldr r10, [r12] \n\t" -#if CONFIG_CPU == PP5002 - "bic r10, r10, #0x4\n\t" /* disable playback FIFO */ - "str r10, [r12] \n\t" - "ldr r10, [r12, #0x1c] \n\t" - "bic r10, r10, #0x200 \n\t" /* clear interrupt */ - "str r10, [r12, #0x1c] \n\t" -#else - "bic r10, r10, #0x20000002\n\t" /* disable playback FIFO and IRQ */ - "str r10, [r12] \n\t" + "ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux */ + "ldr r12, [r12] \n" #endif - "mrs r10, cpsr \n\t" - "orr r10, r10, #0x40 \n\t" /* disable FIQ */ - "msr cpsr_c, r10 \n\t" - ".exit: \n\t" - "str r8, [r11, #4] \n\t" - "str r9, [r11] \n\t" - "subs pc, lr, #4 \n\t" /* FIQ specific return sequence */ - ".fifo_full: \n\t" /* enable IRQ and exit */ -#if CONFIG_CPU == PP5002 - "ldr r10, [r12, #0x1c]\n\t" - "orr r10, r10, #0x200 \n\t" /* set interrupt */ - "str r10, [r12, #0x1c]\n\t" -#else - "ldr r10, [r12] \n\t" - "orr r10, r10, #0x2 \n\t" /* set interrupt */ - "str r10, [r12] \n\t" + "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */ + "cmp r9, #0 \n" /* is size 0? */ + "beq .more_data \n" /* if so, ask pcmbuf for more data */ + ".fifo_loop: \n" + "ldr r12, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */ + "ands r12, r12, %[mask] \n" + "beq .exit \n" /* FIFO full, exit */ + "ldr r12, [r8], #4 \n" /* load two samples */ +#if SAMPLE_SIZE == 16 + "str r12, [r10, %[wr]] \n" /* write them */ +#elif SAMPLE_SIZE == 32 + "mov r12, r12, ror #16 \n" /* put left sample at the top bits */ + "str r12, [r10, %[wr]] \n" /* write top sample, lower sample ignored */ + "mov r12, r12, lsl #16 \n" /* shift lower sample up */ + "str r12, [r10, %[wr]] \n" /* then write it */ #endif - "b .exit \n\t" + "subs r9, r9, #4 \n" /* check if we have more samples */ + "bne .fifo_loop \n" /* yes, continue */ + ".more_data: \n" + "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */ + "ldr r2, =pcm_callback_for_more \n" + "ldr r2, [r2] \n" /* get callback address */ + "cmp r2, #0 \n" /* check for null pointer */ + "stmneia r11, { r8-r9 } \n" /* save internal copies of variables back */ + "movne r0, r11 \n" /* r0 = &p */ + "addne r1, r11, #4 \n" /* r1 = &size */ + "movne lr, pc \n" /* call pcm_callback_for_more */ + "bxne r2 \n" + "ldmia r11, { r8-r9 } \n" /* reload p and size */ + "cmp r9, #0 \n" /* did we actually get more data? */ + "ldmnefd sp!, { r0-r3, lr } \n" + "bne .fifo_loop \n" /* yes, continue to try feeding FIFO */ + "ldr r12, =pcm_play_dma_stop \n" + "mov lr, pc \n" + "bx r12 \n" + "ldr r12, =pcm_play_dma_stopped_callback \n" + "mov lr, pc \n" + "bx r12 \n" + "ldmfd sp!, { r0-r3, lr } \n" + ".exit: \n" /* (r8=0 if stopping, look above) */ + "stmia r11, { r8-r9 } \n" /* save p and size */ + "subs pc, lr, #4 \n" /* FIQ specific return sequence */ + ".ltorg \n" + : /* These must only be integers! No regs */ + : [mask]"i"(IIS_TX_FREE_MASK & (IIS_TX_FREE_MASK-1)), + [cfg]"i"((int)&IISFIFO_CFG - (int)&IISCONFIG), + [wr]"i"((int)&IISFIFO_WR - (int)&IISCONFIG) ); } #else /* C version for reference */ -void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ"))); -void fiq(void) +void fiq_playback(void) __attribute__((interrupt ("FIQ"))) ICODE_ATTR; +/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ +void fiq_playback(void) { - /* Clear interrupt */ -#ifdef CPU_PP502x - IISCONFIG &= ~(1 << 1); -#elif CONFIG_CPU == PP5002 + register pcm_more_callback_type get_more; + +#if CONFIG_CPU == PP5002 inl(0xcf001040); - IISFIFO_CFG &= ~(1<<9); #endif do { - while (p_size) { - //if (FIFO_FREE_COUNT < 2) { - if (((IISFIFO_CFG & (0x1f << 16)) >> 16) < 2) { - /* Enable interrupt */ -#ifdef CPU_PP502x - IISCONFIG |= (1 << 1); -#elif CONFIG_CPU == PP5002 - IISFIFO_CFG |= (1<<9); -#endif + while (dma_play_data.size > 0) { + if (IIS_TX_FREE_COUNT < 2) { return; } - -#ifdef HAVE_AS3514 - IISFIFO_WR = *(int32_t *)p; - p += 2; -#else - IISFIFO_WR = (*(p++))<<16; - IISFIFO_WR = (*(p++))<<16; +#if SAMPLE_SIZE == 16 + IISFIFO_WR = *dma_play_data.p++; +#elif SAMPLE_SIZE == 32 + IISFIFO_WR = *dma_play_data.p++ << 16; + IISFIFO_WR = *dma_play_data.p++ << 16; #endif - p_size-=4; + dma_play_data.size -= 4; } /* p is empty, get some more data */ - if (pcm_callback_for_more) { - pcm_callback_for_more((unsigned char**)&p,&p_size); + get_more = pcm_callback_for_more; + if (get_more) { + get_more((unsigned char**)&dma_play_data.p, + &dma_play_data.size); } - } while (p_size); + } while (dma_play_data.size); - /* No more data, so disable the FIFO/FIQ */ + /* No more data, so disable the FIFO/interrupt */ pcm_play_dma_stop(); + pcm_play_dma_stopped_callback(); } #endif /* ASM / C selection */ -void pcm_play_dma_start(const void *addr, size_t size) +/* For the locks, FIQ must be disabled because the handler manipulates + IISCONFIG and the operation is not atomic - dual core support + will require other measures */ +void pcm_play_lock(void) { - p=(unsigned short*)addr; - p_size=size; - - pcm_playing = true; - -#ifdef CPU_PP502x - CPU_INT_PRIORITY |= I2S_MASK; /* FIQ priority for I2S */ - CPU_INT_EN = I2S_MASK; /* Enable I2S interrupt */ -#else - /* setup I2S interrupt for FIQ */ - outl(inl(0xcf00102c) | DMA_OUT_MASK, 0xcf00102c); - outl(DMA_OUT_MASK, 0xcf001024); -#endif + int status = set_fiq_status(FIQ_DISABLED); - /* Clear the FIQ disable bit in cpsr_c */ - set_fiq_handler(fiq); - enable_fiq(); + if (++dma_play_data.locked == 1) { + IIS_IRQTX_REG &= ~IIS_IRQTX; + } -#if defined(CPU_PP502x) - /* Enable playback FIFO */ - IISCONFIG |= (1 << 29); -#elif CONFIG_CPU == PP5002 - IISCONFIG |= 0x4; -#endif + set_fiq_status(status); +} - /* Fill the FIFO - we assume there are enough bytes in the pcm buffer to - fill the 32-byte FIFO. */ - while (p_size > 0) { - if (FIFO_FREE_COUNT < 2) { - /* Enable interrupt */ -#ifdef CPU_PP502x - IISCONFIG |= (1 << 1); -#elif CONFIG_CPU == PP5002 - IISFIFO_CFG |= (1<<9); -#endif - return; - } +void pcm_play_unlock(void) +{ + int status = set_fiq_status(FIQ_DISABLED); -#ifdef HAVE_AS3514 - IISFIFO_WR = *(int32_t *)p; - p += 2; -#else - IISFIFO_WR = (*(p++))<<16; - IISFIFO_WR = (*(p++))<<16; -#endif - p_size-=4; + if (--dma_play_data.locked == 0 && dma_play_data.state != 0) { + IIS_IRQTX_REG |= IIS_IRQTX; } + + set_fiq_status(status); } -/* Stops the DMA transfer and interrupt */ -void pcm_play_dma_stop(void) +static void play_start_pcm(void) { - pcm_playing = false; - if (!audio_status()) - pcm_paused = false; + fiq_function = fiq_playback; + pcm_apply_settings(); -#if CONFIG_CPU == PP5020 - /* Disable TX interrupt */ - IISCONFIG &= ~(1 << 1); -#elif defined(CPU_PP502x) - /* Disable playback FIFO and interrupt */ - IISCONFIG &= ~((1 << 29) | (1 << 1)); -#elif CONFIG_CPU == PP5002 - /* Disable playback FIFO */ - IISCONFIG &= ~0x4; - - /* Disable the interrupt */ - IISFIFO_CFG &= ~(1<<9); -#endif + IISCONFIG &= ~IIS_TXFIFOEN; /* Stop transmitting */ + dma_play_data.state = 1; + + /* Fill the FIFO or start when data is used up */ + while (1) { + if (IIS_TX_FREE_COUNT < 2 || dma_play_data.size == 0) { + IISCONFIG |= IIS_TXFIFOEN; /* Start transmitting */ + return; + } - disable_fiq(); +#if SAMPLE_SIZE == 16 + IISFIFO_WR = *dma_play_data.p++; +#elif SAMPLE_SIZE == 32 + IISFIFO_WR = *dma_play_data.p++ << 16; + IISFIFO_WR = *dma_play_data.p++ << 16; +#endif + dma_play_data.size -= 4; + } } -void pcm_play_pause_pause(void) +static void play_stop_pcm(void) { -#if CONFIG_CPU == PP5020 /* Disable TX interrupt */ - IISCONFIG &= ~(1 << 1); -#elif defined(CPU_PP502x) - /* Disable playback FIFO and interrupt */ - IISCONFIG &= ~((1 << 29) | (1 << 1)); -#elif CONFIG_CPU == PP5002 - /* Disable the interrupt */ - IISFIFO_CFG &= ~(1<<9); - /* Disable playback FIFO */ - IISCONFIG &= ~0x4; -#endif - disable_fiq(); + IIS_IRQTX_REG &= ~IIS_IRQTX; + dma_play_data.state = 0; } -void pcm_play_pause_unpause(void) +void pcm_play_dma_start(const void *addr, size_t size) { - /* Enable the FIFO and fill it */ - - set_fiq_handler(fiq); - enable_fiq(); + dma_play_data.p = (void *)addr; + dma_play_data.size = size; -#if defined(CPU_PP502x) - /* Enable playback FIFO */ - IISCONFIG |= (1 << 29); -#elif CONFIG_CPU == PP5002 - IISCONFIG |= 0x4; +#if NUM_CORES > 1 + /* This will become more important later - and different ! */ + dma_play_data.core = processor_id(); /* save initiating core */ #endif - /* Fill the FIFO - we assume there are enough bytes in the - pcm buffer to fill the 32-byte FIFO. */ - while (p_size > 0) { - if (FIFO_FREE_COUNT < 2) { - /* Enable interrupt */ -#ifdef CPU_PP502x - IISCONFIG |= (1 << 1); -#elif CONFIG_CPU == PP5002 - IISFIFO_CFG |= (1<<9); -#endif - return; - } + CPU_INT_PRIORITY |= IIS_MASK; /* FIQ priority for I2S */ + CPU_INT_EN = IIS_MASK; -#ifdef HAVE_AS3514 - IISFIFO_WR = *(int32_t *)p; - p += 2; -#else - IISFIFO_WR = (*(p++))<<16; - IISFIFO_WR = (*(p++))<<16; + play_start_pcm(); +} + +/* Stops the DMA transfer and interrupt */ +void pcm_play_dma_stop(void) +{ + play_stop_pcm(); + dma_play_data.size = 0; +#if NUM_CORES > 1 + dma_play_data.core = 0; /* no core in control */ #endif - p_size-=4; - } } -void pcm_set_frequency(unsigned int frequency) +void pcm_play_dma_pause(bool pause) { - (void)frequency; - pcm_freq = HW_SAMPR_DEFAULT; + if (pause) { + play_stop_pcm(); + } else { + play_start_pcm(); + } } size_t pcm_get_bytes_waiting(void) { - return p_size; + return dma_play_data.size & ~3; } -void pcm_init(void) +void pcm_play_dma_init(void) { - pcm_playing = false; - pcm_paused = false; - pcm_callback_for_more = NULL; + pcm_set_frequency(SAMPR_44); /* Initialize default register values. */ audiohw_init(); @@ -357,91 +314,129 @@ void pcm_init(void) audiohw_mute(false); #endif - /* Call pcm_play_dma_stop to initialize everything. */ - pcm_play_dma_stop(); - -#if CONFIG_CPU == PP5020 - /* This processor doesn't like this disabled */ - IISCONFIG |= (1 << 29); + dma_play_data.size = 0; +#if NUM_CORES > 1 + dma_play_data.core = 0; /* no core in control */ #endif + + IISCONFIG |= IIS_TXFIFOEN; } void pcm_postinit(void) { audiohw_postinit(); + pcm_apply_settings(); +} + +const void * pcm_play_dma_get_peak_buffer(int *count) +{ + unsigned long addr = (unsigned long)dma_play_data.p; + size_t cnt = dma_play_data.size; + *count = cnt >> 2; + return (void *)((addr + 2) & ~3); } /**************************************************************************** ** Recording DMA transfer **/ #ifdef HAVE_RECORDING +/* PCM recording interrupt routine lockout */ +static struct dma_data dma_rec_data NOCACHEBSS_ATTR = +{ + /* Initialize to a locked, stopped state */ + .p = NULL, + .size = 0, +#if NUM_CORES > 1 + .core = 0x00, +#endif + .locked = 0, + .state = 0 +}; + +/* For the locks, FIQ must be disabled because the handler manipulates + IISCONFIG and the operation is not atomic - dual core support + will require other measures */ +void pcm_rec_lock(void) +{ + int status = set_fiq_status(FIQ_DISABLED); -#ifdef HAVE_AS3514 -void fiq_record(void) ICODE_ATTR __attribute__((naked)); -void fiq_record(void) + if (++dma_rec_data.locked == 1) + IIS_IRQRX_REG &= ~IIS_IRQRX; + + set_fiq_status(status); +} + +void pcm_rec_unlock(void) { - register pcm_more_callback_type2 more_ready; - register int32_t value1, value2; + int status = set_fiq_status(FIQ_DISABLED); + + if (--dma_rec_data.locked == 0 && dma_rec_data.state != 0) + IIS_IRQRX_REG |= IIS_IRQRX; - asm volatile ("stmfd sp!, {r0-r7, ip, lr} \n"); /* Store context */ + set_fiq_status(status); +} + +/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ +void fiq_record(void) ICODE_ATTR __attribute__((interrupt ("FIQ"))); - IISCONFIG &= ~(1 << 0); +#if defined(SANSA_C200) || defined(SANSA_E200) +void fiq_record(void) +{ + register pcm_more_callback_type2 more_ready; + register int32_t value; if (audio_channels == 2) { /* RX is stereo */ - while (p_size > 0) { - if (FIFO_FREE_COUNT < 2) { - /* enable interrupt */ - IISCONFIG |= (1 << 0); - goto fiq_record_exit; + while (dma_rec_data.size > 0) { + if (IIS_RX_FULL_COUNT < 2) { + return; } /* Discard every other sample since ADC clock is 1/2 LRCK */ - value1 = IISFIFO_RD; - value2 = IISFIFO_RD; + value = IISFIFO_RD; + IISFIFO_RD; - *(int32_t *)p = value1; - p += 2; - p_size -= 4; + *dma_rec_data.p++ = value; + dma_rec_data.size -= 4; /* TODO: Figure out how to do IIS loopback */ if (audio_output_source != AUDIO_SRC_PLAYBACK) { - if ((IISFIFO_CFG & (0x3f << 16)) >= (16 << 16)) { + if (IIS_TX_FREE_COUNT >= 16) { /* Resync the output FIFO - it ran dry */ IISFIFO_WR = 0; IISFIFO_WR = 0; } - IISFIFO_WR = value1; - IISFIFO_WR = value1; + IISFIFO_WR = value; + IISFIFO_WR = value; } } } else { /* RX is left channel mono */ - while (p_size > 0) { - if (FIFO_FREE_COUNT < 2) { - /* enable interrupt */ - IISCONFIG |= (1 << 0); - goto fiq_record_exit; + while (dma_rec_data.size > 0) { + if (IIS_RX_FULL_COUNT < 2) { + return; } /* Discard every other sample since ADC clock is 1/2 LRCK */ - value1 = IISFIFO_RD; - value2 = IISFIFO_RD; - *p++ = value1; - *p++ = value1; - p_size -= 4; + value = IISFIFO_RD; + IISFIFO_RD; + + value = (uint16_t)value | (value << 16); + + *dma_rec_data.p++ = value; + dma_rec_data.size -= 4; if (audio_output_source != AUDIO_SRC_PLAYBACK) { - if ((IISFIFO_CFG & (0x3f << 16)) >= (16 << 16)) { + if (IIS_TX_FREE_COUNT >= 16) { /* Resync the output FIFO - it ran dry */ IISFIFO_WR = 0; IISFIFO_WR = 0; } - value1 = *((int32_t *)p - 1); - IISFIFO_WR = value1; - IISFIFO_WR = value1; + value = *((int32_t *)dma_rec_data.p - 1); + IISFIFO_WR = value; + IISFIFO_WR = value; } } } @@ -451,281 +446,112 @@ void fiq_record(void) if (more_ready == NULL || more_ready(0) < 0) { /* Finished recording */ pcm_rec_dma_stop(); + pcm_rec_dma_stopped_callback(); } - -fiq_record_exit: - asm volatile("ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */ - "subs pc, lr, #4 \n"); /* Return from FIQ */ } #else -static short peak_l, peak_r IBSS_ATTR; - -/* Temporary to stop playback crashing after record */ -void fiq_record(void) ICODE_ATTR __attribute__((naked)); void fiq_record(void) { - asm volatile ("stmfd sp!, {r0-r7, r11, ip, lr} \n"); /* Store context */ - - register short value; register pcm_more_callback_type2 more_ready; - register int status = 0; - /* Clear interrupt */ -#ifdef CPU_PP502x - IISCONFIG &= ~(1 << 0); -#elif CONFIG_CPU == PP5002 - /* TODO */ -#endif - - while (p_size > 0) { - if (FIFO_FREE_COUNT < 2) { - /* enable interrupt */ -#ifdef CPU_PP502x - IISCONFIG |= (1 << 0); -#elif CONFIG_CPU == PP5002 - /* TODO */ -#endif - goto fiq_record_exit; + while (dma_rec_data.size > 0) { + if (IIS_RX_FULL_COUNT < 2) { + return; } - value = (unsigned short)(IISFIFO_RD >> 16); - if (value > peak_l) peak_l = value; - else if (-value > peak_l) peak_l = -value; - *(p++) = value; - - value = (unsigned short)(IISFIFO_RD >> 16); - if (value > peak_r) peak_r = value; - else if (-value > peak_r) peak_r = -value; - *(p++) = value; - - p_size -= 4; - - /* If we have filled the current chunk, start a new one */ - if (p_size == 0) { - rec_peak_left = peak_l; - rec_peak_right = peak_r; - peak_l = peak_r = 0; - } +#if SAMPLE_SIZE == 16 + *dma_rec_data.p++ = IISFIFO_RD; +#elif SAMPLE_SIZE == 32 + *dma_rec_data.p++ = IISFIFO_RD >> 16; + *dma_rec_data.p++ = IISFIFO_RD >> 16; +#endif + dma_rec_data.size -= 4; } more_ready = pcm_callback_more_ready; - if (more_ready != NULL && more_ready(status) >= 0) - goto fiq_record_exit; - - /* Finished recording */ - pcm_rec_dma_stop(); - -fiq_record_exit: - asm volatile("ldmfd sp!, {r0-r7, r11, ip, lr} \n" /* Restore context */ - "subs pc, lr, #4 \n"); /* Return from FIQ */ + if (more_ready == NULL || more_ready(0) < 0) { + /* Finished recording */ + pcm_rec_dma_stop(); + pcm_rec_dma_stopped_callback(); + } } -#endif /* HAVE_AS3514 */ +#endif /* SANSA_E200 */ /* Continue transferring data in */ void pcm_record_more(void *start, size_t size) { - rec_peak_addr = start; /* Start peaking at dest */ - p = start; /* Start of RX buffer */ - p_size = size; /* Bytes to transfer */ -#ifdef CPU_PP502x - IISCONFIG |= (1 << 0); -#elif CONFIG_CPU == PP5002 - /* TODO */ -#endif + pcm_rec_peak_addr = start; /* Start peaking at dest */ + dma_rec_data.p = start; /* Start of RX buffer */ + dma_rec_data.size = size; /* Bytes to transfer */ } void pcm_rec_dma_stop(void) { - logf("pcm_rec_dma_stop"); - - disable_fiq(); + /* disable interrupt */ + IIS_IRQRX_REG &= ~IIS_IRQRX; - /* clear interrupt, disable fifo */ - IISCONFIG &= ~((1 << 28) | (1 << 0)); - - /* clear rx fifo */ - IISFIFO_CFG |= (1 << 12); + dma_rec_data.state = 0; + dma_rec_data.size = 0; +#if NUM_CORES > 1 + dma_rec_data.core = 0x00; +#endif - pcm_recording = false; + /* disable fifo */ + IISCONFIG &= ~IIS_RXFIFOEN; + IISFIFO_CFG |= IIS_RXCLR; } void pcm_rec_dma_start(void *addr, size_t size) { - logf("pcm_rec_dma_start"); - - pcm_recording = true; + pcm_rec_dma_stop(); -#ifndef HAVE_AS3514 - peak_l = peak_r = 0; + pcm_rec_peak_addr = addr; + dma_rec_data.p = addr; + dma_rec_data.size = size; +#if NUM_CORES > 1 + /* This will become more important later - and different ! */ + dma_rec_data.core = processor_id(); /* save initiating core */ #endif + /* setup FIQ handler */ + fiq_function = fiq_record; - p_size = size; - p = addr; - - /* setup FIQ */ - CPU_INT_PRIORITY |= I2S_MASK; - CPU_INT_EN = I2S_MASK; + /* interrupt on full fifo, enable record fifo interrupt */ + dma_rec_data.state = 1; - /* interrupt on full fifo, enable record fifo */ - IISCONFIG |= (1 << 28) | (1 << 0); + /* enable RX FIFO */ + IISCONFIG |= IIS_RXFIFOEN; - set_fiq_handler(fiq_record); - enable_fiq(); + /* enable IIS interrupt as FIQ */ + CPU_INT_PRIORITY |= IIS_MASK; + CPU_INT_EN = IIS_MASK; } -void pcm_close_recording(void) +void pcm_rec_dma_close(void) { - logf("pcm_close_recording"); pcm_rec_dma_stop(); } /* pcm_close_recording */ -void pcm_init_recording(void) +void pcm_rec_dma_init(void) { - logf("pcm_init_recording"); - - pcm_recording = false; - pcm_callback_more_ready = NULL; - -#ifdef CPU_PP502x #if defined(IPOD_COLOR) || defined (IPOD_4G) /* The usual magic from IPL - I'm guessing this configures the headphone socket to be input or output - in this case, input. */ GPIOI_OUTPUT_VAL &= ~0x40; GPIOA_OUTPUT_VAL &= ~0x4; #endif - /* Setup the recording FIQ handler */ - set_fiq_handler(fiq_record); -#endif pcm_rec_dma_stop(); } /* pcm_init */ -void pcm_calculate_rec_peaks(int *left, int *right) +const void * pcm_rec_dma_get_peak_buffer(int *count) { -#ifdef HAVE_AS3514 - if (pcm_recording) - { - unsigned long *start = rec_peak_addr; - unsigned long *end = (unsigned long *)p; - - if (start < end) - { - unsigned long *addr = start; - long peak_l = 0, peak_r = 0; - long peaksq_l = 0, peaksq_r = 0; - - do - { - long value = *addr; - long ch, chsq; - - ch = (int16_t)value; - chsq = ch*ch; - if (chsq > peaksq_l) - peak_l = ch, peaksq_l = chsq; - - ch = value >> 16; - chsq = ch*ch; - if (chsq > peaksq_r) - peak_r = ch, peaksq_r = chsq; - - addr += 4; - } - while (addr < end); - - if (start == rec_peak_addr) - rec_peak_addr = end; - - rec_peak_left = abs(peak_l); - rec_peak_right = abs(peak_r); - } - } - else - { - rec_peak_left = rec_peak_right = 0; - } -#endif /* HAVE_AS3514 */ - - if (left) - *left = rec_peak_left; + unsigned long addr = (unsigned long)pcm_rec_peak_addr; + unsigned long end = (unsigned long)dma_rec_data.p; + *count = (end >> 2) - (addr >> 2); + return (void *)(addr & ~3); +} /* pcm_rec_dma_get_peak_buffer */ - if (right) - *right = rec_peak_right; -} #endif /* HAVE_RECORDING */ - -/* - * This function goes directly into the DMA buffer to calculate the left and - * right peak values. To avoid missing peaks it tries to look forward two full - * peek periods (2/HZ sec, 100% overlap), although it's always possible that - * the entire period will not be visible. To reduce CPU load it only looks at - * every third sample, and this can be reduced even further if needed (even - * every tenth sample would still be pretty accurate). - */ - -/* Check for a peak every PEAK_STRIDE samples */ -#define PEAK_STRIDE 3 -/* Up to 1/50th of a second of audio for peak calculation */ -/* This should use NATIVE_FREQUENCY, or eventually an adjustable freq. value */ -#define PEAK_SAMPLES (44100/50) -void pcm_calculate_peaks(int *left, int *right) -{ - short *addr; - short *end; - { - size_t samples = p_size / 4; - addr = p; - - if (samples > PEAK_SAMPLES) - samples = PEAK_SAMPLES - (PEAK_STRIDE - 1); - else - samples -= MIN(PEAK_STRIDE - 1, samples); - - end = &addr[samples * 2]; - } - - if (left && right) { - int left_peak = 0, right_peak = 0; - - while (addr < end) { - int value; - if ((value = addr [0]) > left_peak) - left_peak = value; - else if (-value > left_peak) - left_peak = -value; - - if ((value = addr [PEAK_STRIDE | 1]) > right_peak) - right_peak = value; - else if (-value > right_peak) - right_peak = -value; - - addr = &addr[PEAK_STRIDE * 2]; - } - - *left = left_peak; - *right = right_peak; - } - else if (left || right) { - int peak_value = 0, value; - - if (right) - addr += (PEAK_STRIDE | 1); - - while (addr < end) { - if ((value = addr [0]) > peak_value) - peak_value = value; - else if (-value > peak_value) - peak_value = -value; - - addr += PEAK_STRIDE * 2; - } - - if (left) - *left = peak_value; - else - *right = peak_value; - } -} diff --git a/firmware/target/arm/pnx0101/pcm-pnx0101.c b/firmware/target/arm/pnx0101/pcm-pnx0101.c index 8b076cc918..adfc752e8e 100644 --- a/firmware/target/arm/pnx0101/pcm-pnx0101.c +++ b/firmware/target/arm/pnx0101/pcm-pnx0101.c @@ -25,9 +25,36 @@ short __attribute__((section(".dmabuf"))) dma_buf_left[DMA_BUF_SAMPLES]; short __attribute__((section(".dmabuf"))) dma_buf_right[DMA_BUF_SAMPLES]; -/* From pcm_playback.c */ -extern unsigned short* p; -extern size_t p_size; +static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ + +unsigned short* p IBSS_ATTR; +size_t p_size IBSS_ATTR; + +void pcm_play_lock(void) +{ +} + +void pcm_play_unlock(void) +{ +} + +void pcm_play_dma_start(const void *addr, size_t size) +{ + pcm_apply_settings(); + + p = (unsigned short*)addr; + p_size = size; +} + +void pcm_play_dma_stop(void) +{ +} + +void pcm_play_dma_pause(bool pause) +{ + if (!pause) + pcm_apply_settings(); +} static inline void fill_dma_buf(int offset) { @@ -85,7 +112,8 @@ static inline void fill_dma_buf(int offset) &p_size); } while (p_size); - pcm_playing = false; + + pcm_play_dma_stopped_callback(); } if (l < lend) @@ -117,9 +145,7 @@ void pcm_init(void) { int i; - pcm_playing = false; - pcm_paused = false; - pcm_callback_for_more = NULL; + pcm_set_frequency(HW_SAMPR_DEFAULT); memset(dma_buf_left, 0, sizeof(dma_buf_left)); memset(dma_buf_right, 0, sizeof(dma_buf_right)); @@ -159,3 +185,32 @@ void pcm_init(void) DMAR10(1) |= 1; } +void pcm_postinit(void) +{ + audiohw_postinit(); + pcm_apply_settings(); +} + +void pcm_set_frequency(unsigned int frequency) +{ + (void)frequency; + pcm_freq = HW_SAMPR_DEFAULT; +} + +void pcm_apply_settings(void) +{ + pcm_curr_sampr = pcm_freq; +} + +size_t pcm_get_bytes_waiting(void) +{ + return p_size & ~3; +} + +const void * pcm_play_dma_get_peak_buffer(int *count) +{ + unsigned long addr = (unsigned long)p; + size_t cnt = p_size; + *count = cnt >> 2; + return (void *)((addr + 2) & ~3); +} diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c index 57873faaff..a38b4e424e 100644 --- a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c +++ b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c @@ -25,31 +25,42 @@ #include "file.h" #include "mmu-meg-fx.h" +/* All exact rates for 16.9344MHz clock */ #define GIGABEAT_11025HZ (0x19 << 1) #define GIGABEAT_22050HZ (0x1b << 1) #define GIGABEAT_44100HZ (0x11 << 1) #define GIGABEAT_88200HZ (0x1f << 1) -static int pcm_freq = 0; /* 44.1 is default */ +/* PCM interrupt routine lockout */ +static struct +{ + int locked; + unsigned long state; +} dma_play_lock = +{ + .locked = 0, + .state = (0<<19) +}; + +/* Last samplerate set by pcm_set_frequency */ +static unsigned long pcm_freq = 0; /* 44.1 is default */ +/* Samplerate control for audio codec */ static int sr_ctrl = 0; -#define FIFO_COUNT ((IISFCON >> 6) & 0x01F) + +#define FIFO_COUNT ((IISFCON >> 6) & 0x3F) /* Setup for the DMA controller */ #define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20)) /* DMA count has hit zero - no more data */ /* Get more data from the callback and top off the FIFO */ -/* Uses explicitly coded prologue/epilogue code to get around complier bugs - in order to be able to use the stack */ -void fiq_handler(void) __attribute__((naked)); +void fiq_handler(void) __attribute__((interrupt ("FIQ"))); static void _pcm_apply_settings(void) { - static int last_freqency = 0; - - if (pcm_freq != last_freqency) + if (pcm_freq != pcm_curr_sampr) { - last_freqency = pcm_freq; + pcm_curr_sampr = pcm_freq; audiohw_set_frequency(sr_ctrl); } } @@ -61,29 +72,50 @@ void pcm_apply_settings(void) set_fiq_status(oldstatus); } -void pcm_init(void) +/* For the locks, DMA interrupt must be disabled because the handler + manipulates INTMSK and the operation is not atomic */ +void pcm_play_lock(void) +{ + int status = set_fiq_status(FIQ_DISABLED); + if (++dma_play_lock.locked == 1) + INTMSK |= (1<<19); /* Mask the DMA interrupt */ + set_fiq_status(status); +} + +void pcm_play_unlock(void) { - pcm_playing = false; - pcm_paused = false; - pcm_callback_for_more = NULL; + int status = set_fiq_status(FIQ_DISABLED); + if (--dma_play_lock.locked == 0) + INTMSK &= ~dma_play_lock.state; /* Unmask the DMA interrupt if enabled */ + set_fiq_status(status); +} +void pcm_play_dma_init(void) +{ pcm_set_frequency(SAMPR_44); /* slave */ IISMOD |= (1<<8); + /* RX,TX off,idle */ + IISCON |= (1<<3) | (1<<2); + audiohw_init(); /* init GPIO */ GPCCON = (GPCCON & ~(3<<14)) | (1<<14); - GPCDAT |= 1<<7; - GPECON |= 0x2aa; + GPCDAT |= (1<<7); + /* GPE4=I2SDO, GPE3=I2SDI, GPE2=CDCLK, GPE1=I2SSCLK, GPE0=I2SLRCK */ + GPECON = (GPECON & ~0x3ff) | 0x2aa; /* Do not service DMA requests, yet */ + /* clear any pending int and mask it */ - INTMSK |= (1<<19); /* mask the interrupt */ - SRCPND = (1<<19); /* clear any pending interrupts */ - INTMOD |= (1<<19); /* connect to FIQ */ + INTMSK |= (1<<19); + SRCPND = (1<<19); + + /* connect to FIQ */ + INTMOD |= (1<<19); } void pcm_postinit(void) @@ -92,21 +124,69 @@ void pcm_postinit(void) pcm_apply_settings(); } -void pcm_play_dma_start(const void *addr, size_t size) +/* Connect the DMA and start filling the FIFO */ +static void play_start_pcm(void) +{ + /* clear pending DMA interrupt */ + SRCPND = (1<<19); + + _pcm_apply_settings(); + + /* Flush any pending writes */ + clean_dcache_range((void*)DISRC2, (DCON2 & 0xFFFFF) * 2); + + /* unmask DMA interrupt when unlocking */ + dma_play_lock.state = (1<<19); + + /* turn on the request */ + IISCON |= (1<<5); + + /* Activate the channel */ + DMASKTRIG2 = 0x2; + + /* turn off the idle */ + IISCON &= ~(1<<3); + + /* start the IIS */ + IISCON |= (1<<0); +} + +/* Disconnect the DMA and wait for the FIFO to clear */ +static void play_stop_pcm(void) { - addr = (void *)((unsigned long)addr & ~3); /* Align data */ - size &= ~3; /* Size must be multiple of 4 */ + /* Mask DMA interrupt */ + INTMSK |= (1<<19); + + /* De-Activate the DMA channel */ + DMASKTRIG2 = 0x4; + + /* are we playing? wait for the chunk to finish */ + if (dma_play_lock.state != 0) + { + /* wait for the FIFO to empty and DMA to stop */ + while ((IISCON & (1<<7)) || (DMASKTRIG2 & 0x2)); + } + + /* Keep interrupt masked when unlocking */ + dma_play_lock.state = 0; + + /* turn off the request */ + IISCON &= ~(1<<5); - /* sanity check: bad pointer or too small file */ - if (NULL == addr || size == 0) return; + /* turn on the idle */ + IISCON |= (1<<3); - disable_fiq(); + /* stop the IIS */ + IISCON &= ~(1<<0); +} +void pcm_play_dma_start(const void *addr, size_t size) +{ /* Enable the IIS clock */ CLKCON |= (1<<17); - /* IIS interface setup and set to idle */ - IISCON = (1<<5) | (1<<3); + /* stop any DMA in progress - idle IIS */ + play_stop_pcm(); /* slave, transmit mode, 16 bit samples - MCLK 384fs - use 16.9344Mhz - BCLK 32fs */ @@ -116,155 +196,80 @@ void pcm_play_dma_start(const void *addr, size_t size) IISFCON = (1<<15) | (1<<13); /* set DMA dest */ - DIDST2 = (int)&IISFIFO; + DIDST2 = (unsigned int)&IISFIFO; /* IIS is on the APB bus, INT when TC reaches 0, fixed dest addr */ DIDSTC2 = 0x03; + /* set DMA source and options */ + DISRC2 = (unsigned int)addr + 0x30000000; /* How many transfers to make - we transfer half-word at a time = 2 bytes */ /* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */ /* no auto-reload, half-word (16bit) */ DCON2 = DMA_CONTROL_SETUP | (size / 2); - - /* set DMA source and options */ - DISRC2 = (unsigned long)addr + 0x30000000; DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */ - /* clear pending DMA interrupt */ - SRCPND = 1<<19; - - pcm_playing = true; - - _pcm_apply_settings(); - - /* unmask the DMA interrupt */ - INTMSK &= ~(1<<19); - - /* Flush any pending writes */ - clean_dcache_range(addr, size); - - /* Activate the channel */ - DMASKTRIG2 = 0x2; - - /* turn off the idle */ - IISCON &= ~(1<<3); + play_start_pcm(); +} - /* start the IIS */ - IISCON |= (1<<0); +/* Promptly stop DMA transfers and stop IIS */ +void pcm_play_dma_stop(void) +{ + play_stop_pcm(); - enable_fiq(); + /* Disconnect the IIS clock */ + CLKCON &= ~(1<<17); } -static void pcm_play_dma_stop_fiq(void) +void pcm_play_dma_pause(bool pause) { - INTMSK |= (1<<19); /* mask the DMA interrupt */ - IISCON &= ~(1<<5); /* disable fifo request */ - DMASKTRIG2 = 0x4; /* De-Activate the DMA channel */ - - /* are we playing? wait for the chunk to finish */ - if (pcm_playing) + if (pause) { - /* wait for the FIFO to empty before turning things off */ - while (IISCON & (1<<7)) ; - - pcm_playing = false; - if (!audio_status()) - pcm_paused = false; + /* pause playback on current buffer */ + play_stop_pcm(); + } + else + { + /* restart playback on current buffer */ + /* make sure we're aligned on left channel - skip any right + channel sample left waiting */ + DISRC2 = (DCSRC2 + 2) & ~0x3; + DCON2 = DMA_CONTROL_SETUP | (DSTAT2 & 0xFFFFE); + play_start_pcm(); } - - /* Disconnect the IIS clock */ - CLKCON &= ~(1<<17); } void fiq_handler(void) { - /* r0-r7 are probably not all used by GCC but there's no way to know - otherwise this whole thing must be assembly */ - asm volatile ("stmfd sp!, {r0-r7, ip, lr} \n" /* Store context */ - "sub sp, sp, #8 \n"); /* Reserve stack */ + static unsigned char *start; + static size_t size; register pcm_more_callback_type get_more; /* No stack for this */ - unsigned char *next_start; /* sp + #0 */ - size_t next_size; /* sp + #4 */ /* clear any pending interrupt */ SRCPND = (1<<19); /* Buffer empty. Try to get more. */ get_more = pcm_callback_for_more; - if (get_more == NULL) - { - /* Callback missing */ - pcm_play_dma_stop_fiq(); - goto fiq_exit; - } - - next_size = 0; - get_more(&next_start, &next_size); + size = 0; - if (next_size == 0) + if (get_more == NULL || (get_more(&start, &size), size == 0)) { - /* No more DMA to do */ - pcm_play_dma_stop_fiq(); - goto fiq_exit; + /* Callback missing or no more DMA to do */ + pcm_play_dma_stop(); + pcm_play_dma_stopped_callback(); } - - /* Flush any pending cache writes */ - clean_dcache_range(next_start, next_size); - - /* set the new DMA values */ - DCON2 = DMA_CONTROL_SETUP | (next_size >> 1); - DISRC2 = (unsigned long)next_start + 0x30000000; - - /* Re-Activate the channel */ - DMASKTRIG2 = 0x2; - -fiq_exit: - asm volatile("add sp, sp, #8 \n" /* Cleanup stack */ - "ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */ - "subs pc, lr, #4 \n"); /* Return from FIQ */ -} - -/* Disconnect the DMA and wait for the FIFO to clear */ -void pcm_play_dma_stop(void) -{ - disable_fiq(); - pcm_play_dma_stop_fiq(); -} - -void pcm_play_pause_pause(void) -{ - /* stop servicing refills */ - int oldstatus = set_fiq_status(FIQ_DISABLED); - INTMSK |= (1<<19); /* mask interrupt request */ - IISCON &= ~(1<<5); /* turn off FIFO request */ - DMASKTRIG2 = 0x4; /* stop DMA at end of atomic transfer */ - - if (pcm_playing) + else { - /* playing - wait for the FIFO to empty */ - while (IISCON & (1<<7)) ; - } + /* Flush any pending cache writes */ + clean_dcache_range(start, size); - set_fiq_status(oldstatus); -} + /* set the new DMA values */ + DCON2 = DMA_CONTROL_SETUP | (size >> 1); + DISRC2 = (unsigned int)start + 0x30000000; -void pcm_play_pause_unpause(void) -{ - /* refill buffer and keep going */ - int oldstatus = set_fiq_status(FIQ_DISABLED); - _pcm_apply_settings(); - if (pcm_playing) - { - /* make sure we're aligned on left channel - skip any right channel - sample left waiting */ - DISRC2 = (DCSRC2 + 2) & ~0x3; - DCON2 = (DSTAT2 & 0xFFFFE); - - SRCPND = (1<<19); /* clear pending DMA interrupt */ - INTMSK &= ~(1<<19); /* unmask interrupt request */ - IISCON |= (1<<5); /* enable FIFO request */ + /* Re-Activate the channel */ + DMASKTRIG2 = 0x2; } - set_fiq_status(oldstatus); } void pcm_set_frequency(unsigned int frequency) @@ -296,89 +301,10 @@ size_t pcm_get_bytes_waiting(void) return (DSTAT2 & 0xFFFFE) * 2; } -/** **/ - -void pcm_mute(bool mute) +const void * pcm_play_dma_get_peak_buffer(int *count) { - audiohw_mute(mute); - if (mute) - sleep(HZ/16); + unsigned long addr = DCSRC2; + int cnt = DSTAT2; + *count = (cnt & 0xFFFFF) >> 1; + return (void *)((addr + 2) & ~3); } - -/** - * Return playback peaks - Peaks ahead in the DMA buffer based upon the - * calling period to attempt to compensate for - * delay. - */ -void pcm_calculate_peaks(int *left, int *right) -{ - static unsigned long last_peak_tick = 0; - static unsigned long frame_period = 0; - static int peaks_l = 0, peaks_r = 0; - - /* Throttled peak ahead based on calling period */ - unsigned long period = current_tick - last_peak_tick; - - /* Keep reasonable limits on period */ - if (period < 1) - period = 1; - else if (period > HZ/5) - period = HZ/5; - - frame_period = (3*frame_period + period) >> 2; - - last_peak_tick = current_tick; - - if (pcm_playing && !pcm_paused) - { - unsigned long *addr = (unsigned long *)DCSRC2; - long samples = DSTAT2; - long samp_frames; - - addr = (unsigned long *)((unsigned long)addr & ~3); - samples &= 0xFFFFE; - samp_frames = frame_period*pcm_freq/(HZ/2); - samples = MIN(samp_frames, samples) >> 1; - - if (samples > 0) - { - long peak_l = 0, peak_r = 0; - long peaksq_l = 0, peaksq_r = 0; - - addr -= 0x30000000 >> 2; - addr = (long *)((long)addr & ~3); - - do - { - long value = *addr; - long ch, chsq; - - ch = (int16_t)value; - chsq = ch*ch; - if (chsq > peaksq_l) - peak_l = ch, peaksq_l = chsq; - - ch = value >> 16; - chsq = ch*ch; - if (chsq > peaksq_r) - peak_r = ch, peaksq_r = chsq; - - addr += 4; - } - while ((samples -= 4) > 0); - - peaks_l = abs(peak_l); - peaks_r = abs(peak_r); - } - } - else - { - peaks_l = peaks_r = 0; - } - - if (left) - *left = peaks_l; - - if (right) - *right = peaks_r; -} /* pcm_calculate_peaks */ diff --git a/firmware/target/arm/system-arm.h b/firmware/target/arm/system-arm.h index c3af652ebc..774cdbcff4 100644 --- a/firmware/target/arm/system-arm.h +++ b/firmware/target/arm/system-arm.h @@ -74,12 +74,6 @@ static inline uint32_t swap_odd_even32(uint32_t value) return value; } -static inline void set_fiq_handler(void(*fiq_handler)(void)) -{ - /* Install the FIQ handler */ - *((unsigned int*)(15*4)) = (unsigned int)fiq_handler; -} - static inline void enable_fiq(void) { /* Clear FIQ disable bit */ diff --git a/firmware/target/arm/system-pp502x.c b/firmware/target/arm/system-pp502x.c index 576459d6c1..8110cfc127 100644 --- a/firmware/target/arm/system-pp502x.c +++ b/firmware/target/arm/system-pp502x.c @@ -214,6 +214,7 @@ void system_init(void) outl(inl(0x70000024) | 0xc0, 0x70000024); DEV_RS = 0; DEV_OFF_MASK = 0; + STRAP_OPT_A = 0x80; #endif #if !defined(SANSA_E200) && !defined(SANSA_C200) diff --git a/firmware/target/arm/system-target.h b/firmware/target/arm/system-target.h index 6b35a49c97..7a1ff4f79a 100644 --- a/firmware/target/arm/system-target.h +++ b/firmware/target/arm/system-target.h @@ -70,6 +70,20 @@ static inline unsigned int current_core(void) return core; } +/* Return the actual ID instead of core index */ +static inline unsigned int processor_id(void) +{ + unsigned char id; + + asm volatile ( + "ldrb %0, [%1] \n" + : "=r"(id) + : "r"(&PROCESSOR_ID) + ); + + return id; +} + #ifdef BOOTLOADER /* All addresses within rockbox are in IRAM in the bootloader so are therefore uncached */ |