Index: firmware/export/pp5020.h =================================================================== --- firmware/export/pp5020.h (revision 20007) +++ firmware/export/pp5020.h (working copy) @@ -104,6 +104,10 @@ #define USB_IRQ 20 #define IDE_IRQ 23 #define FIREWIRE_IRQ 25 +#define DMA0_IRQ 26 +#define DMA1_IRQ 27 /* guess */ +#define DMA2_IRQ 28 /* guess */ +#define DMA3_IRQ 29 /* guess */ #define HI_IRQ 30 #define GPIO0_IRQ (32+0) /* Ports A..D */ #define GPIO1_IRQ (32+1) /* Ports E..H */ @@ -119,6 +123,10 @@ #define IDE_MASK (1 << IDE_IRQ) #define USB_MASK (1 << USB_IRQ) #define FIREWIRE_MASK (1 << FIREWIRE_IRQ) +#define DMA0_MASK (1 << DMA0_IRQ) +#define DMA1_MASK (1 << DMA1_IRQ) +#define DMA2_MASK (1 << DMA2_IRQ) +#define DMA3_MASK (1 << DMA3_IRQ) #define HI_MASK (1 << HI_IRQ) #define GPIO0_MASK (1 << (GPIO0_IRQ-32)) #define GPIO1_MASK (1 << (GPIO1_IRQ-32)) @@ -601,4 +609,86 @@ #define MMAP7_LOGICAL (*(volatile unsigned long*)(0xf000f038)) #define MMAP7_PHYSICAL (*(volatile unsigned long*)(0xf000f03c)) +/* DMA engine */ +#define DMA0_BASE_ADDR 0x6000b000 +#define DMA1_BASE_ADDR 0x6000b020 +#define DMA2_BASE_ADDR 0x6000b040 +#define DMA3_BASE_ADDR 0x6000b060 + +#define DMA_MASTER_CONTROL (*(volatile unsigned long*)(0x6000a000)) +#define DMA_MASTER_STATUS (*(volatile unsigned long*)(0x6000a004)) +#define DMA_DRQ_STATUS (*(volatile unsigned long*)(0x6000a008)) + /* 1ul << DMA_DRQ_xxx to set bit */ + #define DMA_REQ_IIS 2 + #define DMA_REQ_SDHC 13 + +#define DMA_MASTER_CONTROL_EN (1 << 31) + +#define DMA_MASTER_STATUS_CH0 (0x1 << 24) +#define DMA_MASTER_STATUS_CH1 (0x1 << 25) +#define DMA_MASTER_STATUS_CH2 (0x1 << 26) +#define DMA_MASTER_STATUS_CH3 (0x1 << 27) + +#define DMA0_CMD (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x00)) +#define DMA0_STATUS (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x04)) +#define DMA0_RAM_ADDR (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x10)) +#define DMA0_FLAGS (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x14)) +#define DMA0_PER_ADDR (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x18)) +#define DMA0_INCR (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x1c)) + +#define DMA1_CMD (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x00)) +#define DMA1_STATUS (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x04)) +#define DMA1_RAM_ADDR (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x10)) +#define DMA1_FLAGS (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x14)) +#define DMA1_PER_ADDR (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x18)) +#define DMA1_INCR (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x1c)) + +#define DMA2_CMD (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x00)) +#define DMA2_STATUS (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x04)) +#define DMA2_RAM_ADDR (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x10)) +#define DMA2_FLAGS (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x14)) +#define DMA2_PER_ADDR (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x18)) +#define DMA2_INCR (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x1c)) + +#define DMA3_CMD (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x00)) +#define DMA3_STATUS (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x04)) +#define DMA3_RAM_ADDR (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x10)) +#define DMA3_FLAGS (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x14)) +#define DMA3_PER_ADDR (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x18)) +#define DMA3_INCR (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x1c)) + +#define DMA_CMD_SIZE (0xffff) +#define DMA_CMD_REQ_ID (0xf << 16) /* bit number in DRQ_STATUS */ + #define DMA_CMD_REQ_ID_POS 16 +#define DMA_CMD_WAIT_DRQ (0x1 << 24) +#define DMA_CMD_UNK25 (0x1 << 25) +#define DMA_CMD_SINGLE (0x1 << 26) /* stop on complete, no auto reload */ +#define DMA_CMD_RAM_TO_PER (0x1 << 27) /* otherwise per to ram */ +#define DMA_CMD_SLEEP_WAIT (0x1 << 28) +#define DMA_CMD_INTR (0x1 << 30) +#define DMA_CMD_START (0x1 << 31) + +#define DMA_STATUS_SIZE_REMAIN (0xffff) +#define DMA_STATUS_INTR (0x1 << 30) +#define DMA_STATUS_BUSY (0x1 << 31) + +#define DMA_FLAGS_ALIGNED (0x1 << 24) +#define DMA_FLAGS_UNK26 (0x1 << 26) + +#define DMA_INCR_RANGE (0x7 << 16) +#define DMA_INCR_RANGE_UNL (0x0 << 16) +#define DMA_INCR_RANGE_FIXED (0x1 << 16) +#define DMA_INCR_RANGE_ALTR (0x2 << 16) +#define DMA_INCR_RANGE_4 (0x3 << 16) +#define DMA_INCR_RANGE_8 (0x4 << 16) +#define DMA_INCR_RANGE_16 (0x5 << 16) +#define DMA_INCR_RANGE_32 (0x6 << 16) +#define DMA_INCR_RANGE_64 (0x7 << 16) + +#define DMA_INCR_WIDTH (0x7 << 28) +#define DMA_INCR_WIDTH_8BIT (0x0 << 28) +#define DMA_INCR_WIDTH_16BIT (0x1 << 28) +#define DMA_INCR_WIDTH_32BIT (0x2 << 28) +/* All other values reserved? */ + #endif /* __PP5020_H__ */ Index: firmware/target/arm/i2s-pp.c =================================================================== --- firmware/target/arm/i2s-pp.c (revision 20007) +++ firmware/target/arm/i2s-pp.c (working copy) @@ -71,8 +71,8 @@ IISCONFIG = ((IISCONFIG & ~IIS_FIFO_FORMAT_MASK) | IIS_FIFO_FORMAT_LE16_2); /* 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; + /* TX_ATN_LVL = when 8 slots empty */ + IISFIFO_CFG |= IIS_RX_FULL_LVL_12 | IIS_TX_EMPTY_LVL_8; /* Rx.CLR = 1, TX.CLR = 1 */ IISFIFO_CFG |= IIS_RXCLR | IIS_TXCLR; Index: firmware/target/arm/pcm-pp.c =================================================================== --- firmware/target/arm/pcm-pp.c (revision 20007) +++ firmware/target/arm/pcm-pp.c (working copy) @@ -32,6 +32,8 @@ #ifdef CPU_PP502x /* 16-bit, L-R packed into 32 bits with left in the least significant halfword */ #define SAMPLE_SIZE 16 +#define DMA_PLAY_CONFIG ((DMA_REQ_IIS << DMA_CMD_REQ_ID_POS) | DMA_CMD_RAM_TO_PER | \ + DMA_CMD_SINGLE | DMA_CMD_WAIT_DRQ | DMA_CMD_INTR) #else /* 32-bit, one left 32-bit sample followed by one right 32-bit sample */ #define SAMPLE_SIZE 32 @@ -41,11 +43,12 @@ { /* 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 + union + { + void *addr; + uint32_t *p16; /* For packed 16-16 stereo pairs */ + uint16_t *p32; /* For individual samples converted to 32-bit */ + }; size_t size; #if NUM_CORES > 1 unsigned core; @@ -75,7 +78,7 @@ static struct dma_data dma_play_data SHAREDBSS_ATTR = { /* Initialize to a locked, stopped state */ - .p = NULL, + { .addr = NULL }, .size = 0, #if NUM_CORES > 1 .core = 0x00, @@ -89,6 +92,47 @@ audiohw_set_frequency(pcm_fsel); } +#ifdef CPU_PP502x +/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ +void ICODE_ATTR __attribute__((interrupt("FIQ"))) fiq_playback(void) +{ + register pcm_more_callback_type get_more; /* No stack for this */ + + /* clear any pending interrupt */ + DMA0_STATUS; + INT_FORCED_CLR = DMA0_MASK; + + /* Buffer empty. Try to get more. */ + get_more = pcm_callback_for_more; + dma_play_data.size = 0; + + if (get_more) { + get_more((unsigned char **)&dma_play_data.addr, &dma_play_data.size); + + dma_play_data.addr = (void *)(((long)dma_play_data.addr + 3) & ~3); + dma_play_data.size &= 0xfffc; + } + + if (dma_play_data.size > 0) { + /* Flush any pending cache writes */ + if ((unsigned long)dma_play_data.addr < UNCACHED_BASE_ADDR) { + cpucache_flush(); + } + + /* set the new DMA values */ + DMA0_CMD = DMA_PLAY_CONFIG | (dma_play_data.size - 4); + DMA0_RAM_ADDR = (unsigned long)UNCACHED_ADDR(dma_play_data.addr); + + /* Re-Activate the channel */ + DMA0_CMD |= DMA_CMD_START; + return; + } + + /* Callback missing or no more DMA to do */ + pcm_play_dma_stop(); + pcm_play_dma_stopped_callback(); +} +#else /* ASM optimised FIQ handler. Checks for the minimum allowed loop cycles by * evalutation of free IISFIFO-slots against available source buffer words. * Through this it is possible to move the check for IIS_TX_FREE_COUNT outside @@ -221,10 +265,10 @@ return; } #if SAMPLE_SIZE == 16 - IISFIFO_WR = *dma_play_data.p++; + IISFIFO_WR = *dma_play_data.p16++; #elif SAMPLE_SIZE == 32 - IISFIFO_WR = *dma_play_data.p++ << 16; - IISFIFO_WR = *dma_play_data.p++ << 16; + IISFIFO_WR = *dma_play_data.p32++ << 16; + IISFIFO_WR = *dma_play_data.p32++ << 16; #endif dma_play_data.size -= 4; } @@ -232,7 +276,7 @@ /* p is empty, get some more data */ get_more = pcm_callback_for_more; if (get_more) { - get_more((unsigned char**)&dma_play_data.p, + get_more((unsigned char**)&dma_play_data.addr, &dma_play_data.size); } } while (dma_play_data.size); @@ -242,6 +286,7 @@ pcm_play_dma_stopped_callback(); } #endif /* ASM / C selection */ +#endif /* CPU_PP502x */ /* For the locks, FIQ must be disabled because the handler manipulates IISCONFIG and the operation is not atomic - dual core support @@ -251,7 +296,11 @@ int status = disable_fiq_save(); if (++dma_play_data.locked == 1) { +#ifdef CPU_PP502x + CPU_INT_DIS = DMA0_MASK; +#else IIS_IRQTX_REG &= ~IIS_IRQTX; +#endif } restore_fiq(status); @@ -262,7 +311,11 @@ int status = disable_fiq_save(); if (--dma_play_data.locked == 0 && dma_play_data.state != 0) { +#ifdef CPU_PP502x + CPU_INT_EN = DMA0_MASK; +#else IIS_IRQTX_REG |= IIS_IRQTX; +#endif } restore_fiq(status); @@ -275,6 +328,13 @@ IISCONFIG &= ~IIS_TXFIFOEN; /* Stop transmitting */ dma_play_data.state = 1; +#ifdef CPU_PP502x + DMA0_CMD = DMA_PLAY_CONFIG | (dma_play_data.size - 4); + DMA0_RAM_ADDR = (unsigned long)UNCACHED_ADDR(dma_play_data.addr); + IISCONFIG |= IIS_TXFIFOEN; + DMA0_CMD |= DMA_CMD_START; + dma_play_data.state = 1; +#else /* Fill the FIFO or start when data is used up */ while (1) { if (IIS_TX_FREE_COUNT < 2 || dma_play_data.size == 0) { @@ -283,19 +343,35 @@ } #if SAMPLE_SIZE == 16 - IISFIFO_WR = *dma_play_data.p++; + IISFIFO_WR = *dma_play_data.p16++; #elif SAMPLE_SIZE == 32 - IISFIFO_WR = *dma_play_data.p++ << 16; - IISFIFO_WR = *dma_play_data.p++ << 16; + IISFIFO_WR = *dma_play_data.p32++ << 16; + IISFIFO_WR = *dma_play_data.p32++ << 16; #endif dma_play_data.size -= 4; } +#endif } static void play_stop_pcm(void) { +#ifdef CPU_PP502x + size_t size = pcm_get_bytes_waiting(); + + /* Stop transfer */ + DMA0_CMD &= ~DMA_CMD_START; + + /* Wait for not busy + clear int */ + while (DMA0_STATUS & (DMA_STATUS_BUSY | DMA_STATUS_INTR)); + INT_FORCED_CLR = DMA0_MASK; + + /* Save current position to continue where it left off if unpaused */ + dma_play_data.addr += dma_play_data.size - size; + dma_play_data.size = size; +#else /* Disable TX interrupt */ IIS_IRQTX_REG &= ~IIS_IRQTX; +#endif /* Wait for FIFO to empty */ while (!IIS_TX_IS_EMPTY); @@ -305,16 +381,35 @@ void pcm_play_dma_start(const void *addr, size_t size) { - dma_play_data.p = (void *)(((uintptr_t)addr + 2) & ~3); - dma_play_data.size = (size & ~3); + addr = (void *)(((long)addr + 2) & ~3); + size &= 0xfffc; #if NUM_CORES > 1 /* This will become more important later - and different ! */ dma_play_data.core = processor_id(); /* save initiating core */ #endif + play_stop_pcm(); + + if (size <= 0) + return; + +#ifdef CPU_PP502x + CPU_INT_PRIORITY |= DMA0_MASK; /* FIQ priority for DMA0 */ + /* Interrupt enabled upon callback unlock */ + + if ((unsigned long)addr < UNCACHED_BASE_ADDR) + cpucache_flush(); + + dma_play_data.addr = (void *)addr; + dma_play_data.size = size; + DMA0_PER_ADDR = (unsigned long)&IISFIFO_WR; + DMA0_FLAGS = DMA_FLAGS_UNK26; + DMA0_INCR = DMA_INCR_RANGE_FIXED | DMA_INCR_WIDTH_32BIT; +#else CPU_INT_PRIORITY |= IIS_MASK; /* FIQ priority for I2S */ CPU_INT_EN = IIS_MASK; +#endif play_start_pcm(); } @@ -323,6 +418,7 @@ void pcm_play_dma_stop(void) { play_stop_pcm(); + DMA0_CMD = 0xfffc; /* So that ((DMA0_STATUS + 4) & 0xfffc) = 0 when stopped */ dma_play_data.size = 0; #if NUM_CORES > 1 dma_play_data.core = 0; /* no core in control */ @@ -340,7 +436,19 @@ size_t pcm_get_bytes_waiting(void) { +#ifdef CPU_PP502x + unsigned long status = DMA0_STATUS; + + if (status & DMA_STATUS_INTR) { + /* If the interrupt was "stolen"-- because reading the status clears + * it-- put it back by forcing it. */ + INT_FORCED_SET = DMA0_MASK; + } + + return (status + 4) & 0xfffc; +#else return dma_play_data.size & ~3; +#endif } void pcm_play_dma_init(void) @@ -366,7 +474,12 @@ /* Initialize default register values. */ audiohw_init(); +#ifdef CPU_PP502x + DMA_MASTER_CONTROL |= DMA_MASTER_CONTROL_EN; + DMA_DRQ_STATUS |= 1ul << DMA_REQ_IIS; +#endif dma_play_data.size = 0; + #if NUM_CORES > 1 dma_play_data.core = 0; /* no core in control */ #endif @@ -381,10 +494,37 @@ const void * pcm_play_dma_get_peak_buffer(int *count) { - unsigned long addr = (unsigned long)dma_play_data.p; +#ifdef CPU_PP502x + int oldfiq = disable_fiq_save(); + unsigned long status = DMA0_STATUS; + unsigned long addr = (unsigned long)dma_play_data.addr; + unsigned long size = dma_play_data.size; + restore_fiq(oldfiq); + + if (status & DMA_STATUS_INTR) { + /* If the interrupt was "stolen"-- because reading the status clears + * it-- put it back by forcing it. */ + INT_FORCED_SET = DMA0_MASK; + } + + if (status & DMA_STATUS_BUSY) + { + size -= (status + 6) & 0xfffc; + *count = size >> 2; + return (void *)(addr + size); + } + else + { + *count = 0; + return NULL; + } + +#else + unsigned long addr = (unsigned long)dma_play_data.addr; size_t cnt = dma_play_data.size; *count = cnt >> 2; return (void *)((addr + 2) & ~3); +#endif } /**************************************************************************** @@ -395,7 +535,7 @@ static struct dma_data dma_rec_data SHAREDBSS_ATTR = { /* Initialize to a locked, stopped state */ - .p = NULL, + { .addr = NULL }, .size = 0, #if NUM_CORES > 1 .core = 0x00, @@ -447,7 +587,7 @@ value = IISFIFO_RD; IISFIFO_RD; - *dma_rec_data.p++ = value; + *dma_rec_data.p16++ = value; dma_rec_data.size -= 4; /* TODO: Figure out how to do IIS loopback */ @@ -475,7 +615,7 @@ value = (uint16_t)value | (value << 16); - *dma_rec_data.p++ = value; + *dma_rec_data.p16++ = value; dma_rec_data.size -= 4; if (audio_output_source != AUDIO_SRC_PLAYBACK) { @@ -485,7 +625,7 @@ IISFIFO_WR = 0; } - value = *((int32_t *)dma_rec_data.p - 1); + value = *((int32_t *)dma_rec_data.p16 - 1); IISFIFO_WR = value; IISFIFO_WR = value; } @@ -512,10 +652,10 @@ } #if SAMPLE_SIZE == 16 - *dma_rec_data.p++ = IISFIFO_RD; + *dma_rec_data.p16++ = IISFIFO_RD; #elif SAMPLE_SIZE == 32 - *dma_rec_data.p++ = IISFIFO_RD >> 16; - *dma_rec_data.p++ = IISFIFO_RD >> 16; + *dma_rec_data.p32++ = IISFIFO_RD >> 16; + *dma_rec_data.p32++ = IISFIFO_RD >> 16; #endif dma_rec_data.size -= 4; } @@ -535,7 +675,7 @@ void pcm_record_more(void *start, size_t size) { pcm_rec_peak_addr = start; /* Start peaking at dest */ - dma_rec_data.p = start; /* Start of RX buffer */ + dma_rec_data.addr = start; /* Start of RX buffer */ dma_rec_data.size = size; /* Bytes to transfer */ } @@ -560,7 +700,7 @@ pcm_rec_dma_stop(); pcm_rec_peak_addr = addr; - dma_rec_data.p = addr; + dma_rec_data.addr = addr; dma_rec_data.size = size; #if NUM_CORES > 1 /* This will become more important later - and different ! */ @@ -593,7 +733,7 @@ const void * pcm_rec_dma_get_peak_buffer(int *count) { unsigned long addr = (unsigned long)pcm_rec_peak_addr; - unsigned long end = (unsigned long)dma_rec_data.p; + unsigned long end = (unsigned long)dma_rec_data.addr; *count = (end >> 2) - (addr >> 2); return (void *)(addr & ~3); } /* pcm_rec_dma_get_peak_buffer */