Use DMA for audio playback on PP502x (FS#9910 + some further mods). I can't say at this point about any change in battery life but it frees up a percent or two of CPU cycles as measured in the buffering screen. No change in recording transfers yet. Testing seemed to check out so put it out for general use and see what happens.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@20052 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Michael Sevakis 2009-02-19 20:40:03 +00:00
parent 1ad58f9757
commit 6c399b8254
6 changed files with 304 additions and 52 deletions

View file

@ -18,10 +18,9 @@
* KIND, either express or implied. * KIND, either express or implied.
* *
****************************************************************************/ ****************************************************************************/
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include "config.h" #include "config.h"
#include "system.h"
#include "debug.h" #include "debug.h"
#include "panic.h" #include "panic.h"
#include <kernel.h> #include <kernel.h>
@ -31,7 +30,6 @@
#ifndef SIMULATOR #ifndef SIMULATOR
#include "cpu.h" #include "cpu.h"
#endif #endif
#include "system.h"
#include <string.h> #include <string.h>
#include "buffer.h" #include "buffer.h"
#include "settings.h" #include "settings.h"

View file

@ -23,6 +23,9 @@
/* All info gleaned and/or copied from the iPodLinux project. */ /* All info gleaned and/or copied from the iPodLinux project. */
/* PCM addresses for obtaining buffers will be what DMA is using (physical) */
#define HAVE_PCM_DMA_ADDRESS
/* USBOTG */ /* USBOTG */
#define USB_NUM_ENDPOINTS 3 #define USB_NUM_ENDPOINTS 3
/* This needs to be 2048 byte aligned, but USB_QHARRAY_ATTR should take care /* This needs to be 2048 byte aligned, but USB_QHARRAY_ATTR should take care
@ -104,6 +107,10 @@
#define USB_IRQ 20 #define USB_IRQ 20
#define IDE_IRQ 23 #define IDE_IRQ 23
#define FIREWIRE_IRQ 25 #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 HI_IRQ 30
#define GPIO0_IRQ (32+0) /* Ports A..D */ #define GPIO0_IRQ (32+0) /* Ports A..D */
#define GPIO1_IRQ (32+1) /* Ports E..H */ #define GPIO1_IRQ (32+1) /* Ports E..H */
@ -119,6 +126,10 @@
#define IDE_MASK (1 << IDE_IRQ) #define IDE_MASK (1 << IDE_IRQ)
#define USB_MASK (1 << USB_IRQ) #define USB_MASK (1 << USB_IRQ)
#define FIREWIRE_MASK (1 << FIREWIRE_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 HI_MASK (1 << HI_IRQ)
#define GPIO0_MASK (1 << (GPIO0_IRQ-32)) #define GPIO0_MASK (1 << (GPIO0_IRQ-32))
#define GPIO1_MASK (1 << (GPIO1_IRQ-32)) #define GPIO1_MASK (1 << (GPIO1_IRQ-32))
@ -601,4 +612,88 @@
#define MMAP7_LOGICAL (*(volatile unsigned long*)(0xf000f038)) #define MMAP7_LOGICAL (*(volatile unsigned long*)(0xf000f038))
#define MMAP7_PHYSICAL (*(volatile unsigned long*)(0xf000f03c)) #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
/* DMA request IDs */
#define DMA_REQ_IIS 2
#define DMA_REQ_SDHC 13
#define DMA_MASTER_CONTROL (*(volatile unsigned long*)(0x6000a000))
#define DMA_MASTER_STATUS (*(volatile unsigned long*)(0x6000a004))
/* 1ul << DMA_REQ_xxx to set bit */
#define DMA_REQ_STATUS (*(volatile unsigned long*)(0x6000a008))
#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*)(DMA1_BASE_ADDR+0x00))
#define DMA1_STATUS (*(volatile unsigned long*)(DMA1_BASE_ADDR+0x04))
#define DMA1_RAM_ADDR (*(volatile unsigned long*)(DMA1_BASE_ADDR+0x10))
#define DMA1_FLAGS (*(volatile unsigned long*)(DMA1_BASE_ADDR+0x14))
#define DMA1_PER_ADDR (*(volatile unsigned long*)(DMA1_BASE_ADDR+0x18))
#define DMA1_INCR (*(volatile unsigned long*)(DMA1_BASE_ADDR+0x1c))
#define DMA2_CMD (*(volatile unsigned long*)(DMA2_BASE_ADDR+0x00))
#define DMA2_STATUS (*(volatile unsigned long*)(DMA2_BASE_ADDR+0x04))
#define DMA2_RAM_ADDR (*(volatile unsigned long*)(DMA2_BASE_ADDR+0x10))
#define DMA2_FLAGS (*(volatile unsigned long*)(DMA2_BASE_ADDR+0x14))
#define DMA2_PER_ADDR (*(volatile unsigned long*)(DMA2_BASE_ADDR+0x18))
#define DMA2_INCR (*(volatile unsigned long*)(DMA2_BASE_ADDR+0x1c))
#define DMA3_CMD (*(volatile unsigned long*)(DMA3_BASE_ADDR+0x00))
#define DMA3_STATUS (*(volatile unsigned long*)(DMA3_BASE_ADDR+0x04))
#define DMA3_RAM_ADDR (*(volatile unsigned long*)(DMA3_BASE_ADDR+0x10))
#define DMA3_FLAGS (*(volatile unsigned long*)(DMA3_BASE_ADDR+0x14))
#define DMA3_PER_ADDR (*(volatile unsigned long*)(DMA3_BASE_ADDR+0x18))
#define DMA3_INCR (*(volatile unsigned long*)(DMA3_BASE_ADDR+0x1c))
#define DMA_CMD_SIZE (0xffff)
#define DMA_CMD_REQ_ID (0xf << 16)
#define DMA_CMD_REQ_ID_POS 16
#define DMA_CMD_WAIT_REQ (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__ */ #endif /* __PP5020_H__ */

View file

@ -71,8 +71,8 @@ void i2s_reset(void)
IISCONFIG = ((IISCONFIG & ~IIS_FIFO_FORMAT_MASK) | IIS_FIFO_FORMAT_LE16_2); IISCONFIG = ((IISCONFIG & ~IIS_FIFO_FORMAT_MASK) | IIS_FIFO_FORMAT_LE16_2);
/* RX_ATN_LVL = when 12 slots full */ /* RX_ATN_LVL = when 12 slots full */
/* TX_ATN_LVL = when 12 slots empty */ /* TX_ATN_LVL = DMA request when 4 slots empty */
IISFIFO_CFG |= IIS_RX_FULL_LVL_12 | IIS_TX_EMPTY_LVL_12; IISFIFO_CFG |= IIS_RX_FULL_LVL_12 | IIS_TX_EMPTY_LVL_4;
/* Rx.CLR = 1, TX.CLR = 1 */ /* Rx.CLR = 1, TX.CLR = 1 */
IISFIFO_CFG |= IIS_RXCLR | IIS_TXCLR; IISFIFO_CFG |= IIS_RXCLR | IIS_TXCLR;

View file

@ -32,6 +32,18 @@
#ifdef CPU_PP502x #ifdef CPU_PP502x
/* 16-bit, L-R packed into 32 bits with left in the least significant halfword */ /* 16-bit, L-R packed into 32 bits with left in the least significant halfword */
#define SAMPLE_SIZE 16 #define SAMPLE_SIZE 16
/* DMA Requests from IIS, Memory to peripheral, single transfer,
wait for DMA request, interrupt on complete */
#define DMA_PLAY_CONFIG ((DMA_REQ_IIS << DMA_CMD_REQ_ID_POS) | \
DMA_CMD_RAM_TO_PER | DMA_CMD_SINGLE | \
DMA_CMD_WAIT_REQ | DMA_CMD_INTR)
/* DMA status cannot be viewed from outside code in control because that can
* clear the interrupt from outside the handler and prevent the handler from
* from being called. Split up transfers to a reasonable size that is good as
* a timer, obtaining a keyclick position and peaking yet still keeps the
* FIQ count low.
*/
#define MAX_DMA_CHUNK_SIZE (pcm_curr_sampr >> 6) /* ~1/256 seconds */
#else #else
/* 32-bit, one left 32-bit sample followed by one right 32-bit sample */ /* 32-bit, one left 32-bit sample followed by one right 32-bit sample */
#define SAMPLE_SIZE 32 #define SAMPLE_SIZE 32
@ -41,11 +53,12 @@ struct dma_data
{ {
/* NOTE: The order of size and p is important if you use assembler /* NOTE: The order of size and p is important if you use assembler
optimised fiq handler, so don't change it. */ optimised fiq handler, so don't change it. */
#if SAMPLE_SIZE == 16 union
uint32_t *p; {
#elif SAMPLE_SIZE == 32 unsigned long addr;
uint16_t *p; uint32_t *p16; /* For packed 16-16 stereo pairs */
#endif uint16_t *p32; /* For individual samples converted to 32-bit */
};
size_t size; size_t size;
#if NUM_CORES > 1 #if NUM_CORES > 1
unsigned core; unsigned core;
@ -67,15 +80,24 @@ void fiq_handler(void)
); );
} }
#ifdef HAVE_PCM_DMA_ADDRESS
void * pcm_dma_addr(void *addr)
{
if (addr != NULL && (unsigned long)addr < UNCACHED_BASE_ADDR)
addr = UNCACHED_ADDR(addr);
return addr;
}
#endif
/* TODO: Get simultaneous recording and playback to work. Just needs some tweaking */ /* TODO: Get simultaneous recording and playback to work. Just needs some tweaking */
/**************************************************************************** /****************************************************************************
** Playback DMA transfer ** Playback DMA transfer
**/ **/
static struct dma_data dma_play_data SHAREDBSS_ATTR = static struct dma_data dma_play_data IBSS_ATTR =
{ {
/* Initialize to a locked, stopped state */ /* Initialize to a locked, stopped state */
.p = NULL, { .addr = 0 },
.size = 0, .size = 0,
#if NUM_CORES > 1 #if NUM_CORES > 1
.core = 0x00, .core = 0x00,
@ -89,6 +111,59 @@ void pcm_dma_apply_settings(void)
audiohw_set_frequency(pcm_fsel); audiohw_set_frequency(pcm_fsel);
} }
#if defined(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;
register size_t size;
DMA0_STATUS; /* Clear any pending interrupt */
size = (DMA0_CMD & 0xffff) + 4; /* Get size of trasfer that caused this
interrupt */
dma_play_data.addr += size;
dma_play_data.size -= size;
while (1)
{
if (dma_play_data.size > 0) {
size = MAX_DMA_CHUNK_SIZE;
/* Not at least MAX_DMA_CHUNK_SIZE left or there would be less
* than a FIFO's worth of data after this transfer? */
if (size + 16*4 > dma_play_data.size)
size = dma_play_data.size;
/* Set the new DMA values and activate channel */
DMA0_RAM_ADDR = dma_play_data.addr;
DMA0_CMD = DMA_PLAY_CONFIG | (size - 4) | DMA_CMD_START;
return;
}
/* Buffer empty. Try to get more. */
get_more = pcm_callback_for_more;
if (get_more) {
get_more((unsigned char **)&dma_play_data.addr, &dma_play_data.size);
dma_play_data.addr = (dma_play_data.addr + 3) & ~3;
dma_play_data.size &= 0xfffc;
}
if (dma_play_data.size <= 0) {
break;
}
if (dma_play_data.addr < UNCACHED_BASE_ADDR) {
/* Flush any pending cache writes */
dma_play_data.addr = UNCACHED_ADDR(dma_play_data.addr);
cpucache_flush();
}
}
/* 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 /* ASM optimised FIQ handler. Checks for the minimum allowed loop cycles by
* evalutation of free IISFIFO-slots against available source buffer words. * 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 * Through this it is possible to move the check for IIS_TX_FREE_COUNT outside
@ -221,10 +296,10 @@ void fiq_playback(void)
return; return;
} }
#if SAMPLE_SIZE == 16 #if SAMPLE_SIZE == 16
IISFIFO_WR = *dma_play_data.p++; IISFIFO_WR = *dma_play_data.p16++;
#elif SAMPLE_SIZE == 32 #elif SAMPLE_SIZE == 32
IISFIFO_WR = *dma_play_data.p++ << 16; IISFIFO_WR = *dma_play_data.p32++ << 16;
IISFIFO_WR = *dma_play_data.p++ << 16; IISFIFO_WR = *dma_play_data.p32++ << 16;
#endif #endif
dma_play_data.size -= 4; dma_play_data.size -= 4;
} }
@ -232,7 +307,7 @@ void fiq_playback(void)
/* p is empty, get some more data */ /* p is empty, get some more data */
get_more = pcm_callback_for_more; get_more = pcm_callback_for_more;
if (get_more) { if (get_more) {
get_more((unsigned char**)&dma_play_data.p, get_more((unsigned char**)&dma_play_data.addr,
&dma_play_data.size); &dma_play_data.size);
} }
} while (dma_play_data.size); } while (dma_play_data.size);
@ -242,6 +317,7 @@ void fiq_playback(void)
pcm_play_dma_stopped_callback(); pcm_play_dma_stopped_callback();
} }
#endif /* ASM / C selection */ #endif /* ASM / C selection */
#endif /* CPU_PP502x */
/* For the locks, FIQ must be disabled because the handler manipulates /* For the locks, FIQ must be disabled because the handler manipulates
IISCONFIG and the operation is not atomic - dual core support IISCONFIG and the operation is not atomic - dual core support
@ -251,7 +327,11 @@ void pcm_play_lock(void)
int status = disable_fiq_save(); int status = disable_fiq_save();
if (++dma_play_data.locked == 1) { if (++dma_play_data.locked == 1) {
#ifdef CPU_PP502x
CPU_INT_DIS = DMA0_MASK;
#else
IIS_IRQTX_REG &= ~IIS_IRQTX; IIS_IRQTX_REG &= ~IIS_IRQTX;
#endif
} }
restore_fiq(status); restore_fiq(status);
@ -262,7 +342,11 @@ void pcm_play_unlock(void)
int status = disable_fiq_save(); int status = disable_fiq_save();
if (--dma_play_data.locked == 0 && dma_play_data.state != 0) { 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; IIS_IRQTX_REG |= IIS_IRQTX;
#endif
} }
restore_fiq(status); restore_fiq(status);
@ -272,30 +356,71 @@ static void play_start_pcm(void)
{ {
fiq_function = fiq_playback; fiq_function = fiq_playback;
IISCONFIG &= ~IIS_TXFIFOEN; /* Stop transmitting */ #ifdef CPU_PP502x
/* Not at least MAX_DMA_CHUNK_SIZE left or there would be less than a
* FIFO's worth of data after this transfer? */
size_t size = MAX_DMA_CHUNK_SIZE;
if (dma_play_data.size + 16*4 < size)
size = dma_play_data.size;
DMA0_RAM_ADDR = dma_play_data.addr;
DMA0_CMD = DMA_PLAY_CONFIG | (size - 4) | DMA_CMD_START;
dma_play_data.state = 1; dma_play_data.state = 1;
#else
IISCONFIG &= ~IIS_TXFIFOEN; /* Stop transmitting */
/* Fill the FIFO or start when data is used up */ /* Fill the FIFO or start when data is used up */
while (1) { while (1) {
if (IIS_TX_FREE_COUNT < 2 || dma_play_data.size == 0) { if (IIS_TX_FREE_COUNT < 2 || dma_play_data.size == 0) {
IISCONFIG |= IIS_TXFIFOEN; /* Start transmitting */ IISCONFIG |= IIS_TXFIFOEN; /* Start transmitting */
dma_play_data.state = 1;
return; return;
} }
#if SAMPLE_SIZE == 16 #if SAMPLE_SIZE == 16
IISFIFO_WR = *dma_play_data.p++; IISFIFO_WR = *dma_play_data.p16++;
#elif SAMPLE_SIZE == 32 #elif SAMPLE_SIZE == 32
IISFIFO_WR = *dma_play_data.p++ << 16; IISFIFO_WR = *dma_play_data.p32++ << 16;
IISFIFO_WR = *dma_play_data.p++ << 16; IISFIFO_WR = *dma_play_data.p32++ << 16;
#endif #endif
dma_play_data.size -= 4; dma_play_data.size -= 4;
} }
#endif
} }
static void play_stop_pcm(void) static void play_stop_pcm(void)
{ {
#ifdef CPU_PP502x
size_t status = DMA0_STATUS;
size_t size;
/* Stop transfer */
DMA0_CMD &= ~(DMA_CMD_START | DMA_CMD_INTR);
/* Wait for not busy + clear int */
while (DMA0_STATUS & (DMA_STATUS_BUSY | DMA_STATUS_INTR));
size = (status & 0xfffc) + 4;
if (status & DMA_STATUS_BUSY)
{
/* Transfer was interrupted - leave what's left */
dma_play_data.addr += dma_play_data.size - size;
dma_play_data.size = size;
}
else if (status & DMA_STATUS_INTR)
{
/* Tranfer was finished - DMA0_STATUS will have been reloaded
* automatically with size in DMA0_CMD. */
dma_play_data.addr += size;
dma_play_data.size -= size;
if (dma_play_data.size <= 0)
dma_play_data.addr = 0; /* Entire buffer has completed */
}
#else
/* Disable TX interrupt */ /* Disable TX interrupt */
IIS_IRQTX_REG &= ~IIS_IRQTX; IIS_IRQTX_REG &= ~IIS_IRQTX;
#endif
/* Wait for FIFO to empty */ /* Wait for FIFO to empty */
while (!IIS_TX_IS_EMPTY); while (!IIS_TX_IS_EMPTY);
@ -305,16 +430,32 @@ static void play_stop_pcm(void)
void pcm_play_dma_start(const void *addr, size_t size) void pcm_play_dma_start(const void *addr, size_t size)
{ {
dma_play_data.p = (void *)(((uintptr_t)addr + 2) & ~3); addr = (void *)(((long)addr + 2) & ~3);
dma_play_data.size = (size & ~3); size &= 0xfffc;
#if NUM_CORES > 1 #if NUM_CORES > 1
/* This will become more important later - and different ! */ /* This will become more important later - and different ! */
dma_play_data.core = processor_id(); /* save initiating core */ dma_play_data.core = processor_id(); /* save initiating core */
#endif #endif
CPU_INT_PRIORITY |= IIS_MASK; /* FIQ priority for I2S */ pcm_play_dma_stop();
CPU_INT_EN = IIS_MASK;
if (size <= 0)
return;
#ifdef CPU_PP502x
if ((unsigned long)addr < UNCACHED_BASE_ADDR) {
/* Flush any pending cache writes */
addr = UNCACHED_ADDR(addr);
cpucache_flush();
}
dma_play_data.addr = (unsigned long)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;
#endif
play_start_pcm(); play_start_pcm();
} }
@ -323,6 +464,7 @@ void pcm_play_dma_start(const void *addr, size_t size)
void pcm_play_dma_stop(void) void pcm_play_dma_stop(void)
{ {
play_stop_pcm(); play_stop_pcm();
dma_play_data.addr = 0;
dma_play_data.size = 0; dma_play_data.size = 0;
#if NUM_CORES > 1 #if NUM_CORES > 1
dma_play_data.core = 0; /* no core in control */ dma_play_data.core = 0; /* no core in control */
@ -345,6 +487,20 @@ size_t pcm_get_bytes_waiting(void)
void pcm_play_dma_init(void) void pcm_play_dma_init(void)
{ {
/* Initialize default register values. */
audiohw_init();
#ifdef CPU_PP502x
/* Enable DMA controller */
DMA_MASTER_CONTROL |= DMA_MASTER_CONTROL_EN;
/* FIQ priority for DMA0 */
CPU_INT_PRIORITY |= DMA0_MASK;
/* Enable request?? Not setting or clearing everything doesn't seem to
* prevent it operating. Perhaps important for reliability (how requests
* are handled). */
DMA_REQ_STATUS |= 1ul << DMA_REQ_IIS;
DMA0_STATUS;
#else
/* Set up banked registers for FIQ mode */ /* Set up banked registers for FIQ mode */
/* Use non-banked registers for scratch. */ /* Use non-banked registers for scratch. */
@ -363,12 +519,9 @@ void pcm_play_dma_init(void)
: [iiscfg]"r"(iiscfg), [dmapd]"r"(dmapd) : [iiscfg]"r"(iiscfg), [dmapd]"r"(dmapd)
: "r2"); : "r2");
/* Initialize default register values. */ /* FIQ priority for I2S */
audiohw_init(); CPU_INT_PRIORITY |= IIS_MASK;
CPU_INT_EN = IIS_MASK;
dma_play_data.size = 0;
#if NUM_CORES > 1
dma_play_data.core = 0; /* no core in control */
#endif #endif
IISCONFIG |= IIS_TXFIFOEN; IISCONFIG |= IIS_TXFIFOEN;
@ -381,9 +534,14 @@ void pcm_postinit(void)
const void * pcm_play_dma_get_peak_buffer(int *count) const void * pcm_play_dma_get_peak_buffer(int *count)
{ {
unsigned long addr = (unsigned long)dma_play_data.p; unsigned long addr, size;
size_t cnt = dma_play_data.size;
*count = cnt >> 2; int status = disable_fiq_save();
addr = dma_play_data.addr;
size = dma_play_data.size;
restore_fiq(status);
*count = size >> 2;
return (void *)((addr + 2) & ~3); return (void *)((addr + 2) & ~3);
} }
@ -392,10 +550,10 @@ const void * pcm_play_dma_get_peak_buffer(int *count)
**/ **/
#ifdef HAVE_RECORDING #ifdef HAVE_RECORDING
/* PCM recording interrupt routine lockout */ /* PCM recording interrupt routine lockout */
static struct dma_data dma_rec_data SHAREDBSS_ATTR = static struct dma_data dma_rec_data IBSS_ATTR =
{ {
/* Initialize to a locked, stopped state */ /* Initialize to a locked, stopped state */
.p = NULL, { .addr = 0 },
.size = 0, .size = 0,
#if NUM_CORES > 1 #if NUM_CORES > 1
.core = 0x00, .core = 0x00,
@ -447,7 +605,7 @@ void fiq_record(void)
value = IISFIFO_RD; value = IISFIFO_RD;
IISFIFO_RD; IISFIFO_RD;
*dma_rec_data.p++ = value; *dma_rec_data.p16++ = value;
dma_rec_data.size -= 4; dma_rec_data.size -= 4;
/* TODO: Figure out how to do IIS loopback */ /* TODO: Figure out how to do IIS loopback */
@ -475,7 +633,7 @@ void fiq_record(void)
value = (uint16_t)value | (value << 16); value = (uint16_t)value | (value << 16);
*dma_rec_data.p++ = value; *dma_rec_data.p16++ = value;
dma_rec_data.size -= 4; dma_rec_data.size -= 4;
if (audio_output_source != AUDIO_SRC_PLAYBACK) { if (audio_output_source != AUDIO_SRC_PLAYBACK) {
@ -485,7 +643,7 @@ void fiq_record(void)
IISFIFO_WR = 0; 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;
IISFIFO_WR = value; IISFIFO_WR = value;
} }
@ -512,10 +670,10 @@ void fiq_record(void)
} }
#if SAMPLE_SIZE == 16 #if SAMPLE_SIZE == 16
*dma_rec_data.p++ = IISFIFO_RD; *dma_rec_data.p16++ = IISFIFO_RD;
#elif SAMPLE_SIZE == 32 #elif SAMPLE_SIZE == 32
*dma_rec_data.p++ = IISFIFO_RD >> 16; *dma_rec_data.p32++ = IISFIFO_RD >> 16;
*dma_rec_data.p++ = IISFIFO_RD >> 16; *dma_rec_data.p32++ = IISFIFO_RD >> 16;
#endif #endif
dma_rec_data.size -= 4; dma_rec_data.size -= 4;
} }
@ -535,7 +693,7 @@ void fiq_record(void)
void pcm_record_more(void *start, size_t size) void pcm_record_more(void *start, size_t size)
{ {
pcm_rec_peak_addr = start; /* Start peaking at dest */ pcm_rec_peak_addr = start; /* Start peaking at dest */
dma_rec_data.p = start; /* Start of RX buffer */ dma_rec_data.addr = (unsigned long)start; /* Start of RX buffer */
dma_rec_data.size = size; /* Bytes to transfer */ dma_rec_data.size = size; /* Bytes to transfer */
} }
@ -560,7 +718,7 @@ void pcm_rec_dma_start(void *addr, size_t size)
pcm_rec_dma_stop(); pcm_rec_dma_stop();
pcm_rec_peak_addr = addr; pcm_rec_peak_addr = addr;
dma_rec_data.p = addr; dma_rec_data.addr = (unsigned long)addr;
dma_rec_data.size = size; dma_rec_data.size = size;
#if NUM_CORES > 1 #if NUM_CORES > 1
/* This will become more important later - and different ! */ /* This will become more important later - and different ! */
@ -592,8 +750,13 @@ void pcm_rec_dma_init(void)
const void * pcm_rec_dma_get_peak_buffer(int *count) const void * pcm_rec_dma_get_peak_buffer(int *count)
{ {
unsigned long addr = (unsigned long)pcm_rec_peak_addr; unsigned long addr, end;
unsigned long end = (unsigned long)dma_rec_data.p;
int status = disable_fiq_save();
addr = (unsigned long)pcm_rec_peak_addr;
end = dma_rec_data.addr;
restore_fiq(status);
*count = (end >> 2) - (addr >> 2); *count = (end >> 2) - (addr >> 2);
return (void *)(addr & ~3); return (void *)(addr & ~3);
} /* pcm_rec_dma_get_peak_buffer */ } /* pcm_rec_dma_get_peak_buffer */

View file

@ -169,6 +169,7 @@ void ICODE_ATTR cpucache_flush(void)
{ {
CACHE_OPERATION |= CACHE_OP_FLUSH; CACHE_OPERATION |= CACHE_OP_FLUSH;
while ((CACHE_CTL & CACHE_CTL_BUSY) != 0); while ((CACHE_CTL & CACHE_CTL_BUSY) != 0);
nop; nop; nop; nop;
} }
} }
@ -470,11 +471,6 @@ void system_init(void)
GPIOK_INT_EN = 0; GPIOK_INT_EN = 0;
GPIOL_INT_EN = 0; GPIOL_INT_EN = 0;
#if defined(SANSA_E200) || defined(SANSA_C200) || defined(PHILIPS_SA9200)
/* outl(0x00000000, 0x6000b000); */
outl(inl(0x6000a000) | 0x80000000, 0x6000a000); /* Init DMA controller? */
#endif
#ifdef HAVE_ADJUSTABLE_CPU_FREQ #ifdef HAVE_ADJUSTABLE_CPU_FREQ
#if NUM_CORES > 1 #if NUM_CORES > 1
corelock_init(&cpufreq_cl); corelock_init(&cpufreq_cl);

View file

@ -389,7 +389,7 @@ void usb_drv_reset(void)
REG_USBCMD |= USBCMD_CTRL_RESET; REG_USBCMD |= USBCMD_CTRL_RESET;
while (REG_USBCMD & USBCMD_CTRL_RESET); while (REG_USBCMD & USBCMD_CTRL_RESET);
#if CONFIG_CPU == PP5022 || CONFIG_CPU == PP5024 #if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5022 || CONFIG_CPU == PP5024
/* On a CPU which identifies as a PP5022, this /* On a CPU which identifies as a PP5022, this
initialization must be done after USB is reset. initialization must be done after USB is reset.
*/ */