Finally a click, pop and other distortion free PCM driver for that fishy bufferless S5L870x I2S controller.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23324 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Michael Sparmann 2009-10-23 23:47:38 +00:00
parent 92737a3877
commit 16a956c4f3
2 changed files with 106 additions and 51 deletions

View file

@ -28,7 +28,6 @@
#include "audiohw.h" #include "audiohw.h"
#include "pcm.h" #include "pcm.h"
#include "pcm_sampr.h" #include "pcm_sampr.h"
#include "dma-target.h"
#include "mmu-target.h" #include "mmu-target.h"
/* Driver for the IIS/PCM part of the s5l8700 using DMA /* Driver for the IIS/PCM part of the s5l8700 using DMA
@ -41,8 +40,10 @@
- recording is not implemented - recording is not implemented
*/ */
static void dma_callback(void);
static volatile int locked = 0; static volatile int locked = 0;
size_t nextsize;
int dmamode;
unsigned char* dblbuf;
/* table of recommended PLL/MCLK dividers for mode 256Fs from the datasheet */ /* table of recommended PLL/MCLK dividers for mode 256Fs from the datasheet */
static const struct div_entry { static const struct div_entry {
@ -54,10 +55,9 @@ static const struct div_entry {
[HW_FREQ_44] = { 2, 41, 3, 4}, [HW_FREQ_44] = { 2, 41, 3, 4},
[HW_FREQ_88] = { 2, 41, 2, 4}, [HW_FREQ_88] = { 2, 41, 2, 4},
#if 0 /* disabled because the codec driver does not support it (yet) */ #if 0 /* disabled because the codec driver does not support it (yet) */
[HW_FREQ_8 ] = { 2, 12, 3, 9} [HW_FREQ_8 ] = { 2, 12, 3, 9},
[HW_FREQ_16] = { 2, 12, 2, 9}, [HW_FREQ_16] = { 2, 12, 2, 9},
[HW_FREQ_32] = { 2, 12, 1, 9}, [HW_FREQ_32] = { 2, 12, 1, 9},
[HW_FREQ_12] = { 2, 12, 4, 3}, [HW_FREQ_12] = { 2, 12, 4, 3},
[HW_FREQ_24] = { 2, 12, 3, 3}, [HW_FREQ_24] = { 2, 12, 3, 3},
[HW_FREQ_48] = { 2, 12, 2, 3}, [HW_FREQ_48] = { 2, 12, 2, 3},
@ -69,10 +69,9 @@ static const struct div_entry {
[HW_FREQ_44] = { 37, 151, 1, 9}, [HW_FREQ_44] = { 37, 151, 1, 9},
[HW_FREQ_88] = { 50, 98, 1, 4}, [HW_FREQ_88] = { 50, 98, 1, 4},
#if 0 /* disabled because the codec driver does not support it (yet) */ #if 0 /* disabled because the codec driver does not support it (yet) */
[HW_FREQ_8 ] = { 28, 192, 3, 12} [HW_FREQ_8 ] = { 28, 192, 3, 12},
[HW_FREQ_16] = { 28, 192, 3, 6}, [HW_FREQ_16] = { 28, 192, 3, 6},
[HW_FREQ_32] = { 28, 192, 2, 6}, [HW_FREQ_32] = { 28, 192, 2, 6},
[HW_FREQ_12] = { 28, 192, 3, 8}, [HW_FREQ_12] = { 28, 192, 3, 8},
[HW_FREQ_24] = { 28, 192, 2, 8}, [HW_FREQ_24] = { 28, 192, 2, 8},
[HW_FREQ_48] = { 28, 192, 2, 4}, [HW_FREQ_48] = { 28, 192, 2, 4},
@ -97,50 +96,109 @@ void pcm_play_unlock(void)
} }
} }
static void play_next(void *addr, size_t size) static void* dma_callback(void) ICODE_ATTR;
static void* dma_callback(void)
{ {
/* setup DMA */ if (dmamode)
dma_setup_channel(DMA_IISOUT_CHANNEL, DMA_IISOUT_SELECT, DMA_MEM_TO_IO,
DMA_IISOUT_DSIZE, DMA_IISOUT_BLEN, addr, size / 2,
dma_callback);
/* DMA channel on */
// clean_dcache();
dma_enable_channel(DMA_IISOUT_CHANNEL);
}
static void dma_callback(void)
{ {
unsigned char *dma_start_addr; unsigned char *dma_start_addr;
size_t dma_size;
register pcm_more_callback_type get_more = pcm_callback_for_more; register pcm_more_callback_type get_more = pcm_callback_for_more;
if (get_more) { if (get_more)
get_more(&dma_start_addr, &dma_size); {
get_more(&dma_start_addr, &nextsize);
if (nextsize > 4096)
{
nextsize = nextsize - 2048;
dblbuf = dma_start_addr + nextsize;
dmamode = 0;
}
nextsize = (nextsize >> 1) - 1;
clean_dcache();
return dma_start_addr;
}
else
{
nextsize = -1;
return 0;
}
}
else
{
dmamode = 1;
nextsize = 1023;
return dblbuf;
}
}
if (dma_size == 0) { void fiq_handler(void) __attribute__((interrupt ("FIQ"), naked)) ICODE_ATTR;
pcm_play_dma_stop(); void fiq_handler(void)
pcm_play_dma_stopped_callback(); {
} asm volatile (
else { "cmn r11, #1 \n"
play_next(dma_start_addr, dma_size); "strne r10, [r8] \n" /* DMABASE0 */
} "strne r11, [r8,#0x08] \n" /* DMATCNT0 */
"strne r9, [r8,#0x14] \n" /* DMACOM0 */
"moveq r10, #5 \n" /* STOP DMA */
"streq r10, [r8,#0x14] \n" /* DMACOM0 */
"mov r10, #7 \n" /* CLEAR IRQ */
"str r10, [r8,#0x14] \n" /* DMACOM0 */
"mov r11, #0x39C00000 \n" /* SRCPND */
"mov r10, #0x00000400 \n" /* INT_DMA */
"str r10, [r11] \n" /* ACK FIQ */
"stmfd sp!, {r0-r3,lr} \n"
"ldreq r0, =pcm_play_dma_stopped_callback \n"
"ldrne r0, =dma_callback \n"
"mov lr, pc \n"
"bx r0 \n"
"mov r10, r0 \n"
"ldmfd sp!, {r0-r3,lr} \n"
"ldr r11, =nextsize \n"
"ldr r11, [r11] \n"
"subs pc, lr, #4 \n"
);
} }
void bootstrap_fiq(const void* addr, size_t tcnt) __attribute__((naked,noinline));
void bootstrap_fiq(const void* addr, size_t tcnt)
{
(void)addr;
(void)tcnt;
asm volatile (
"add r2, lr, #4 \n"
"mrs r3, cpsr \n"
"msr cpsr_c, #0xD1 \n" /* FIQ mode, IRQ/FIQ disabled */
"mov r8, #0x38400000 \n" /* DMA BASE */
"mov r9, #4 \n" /* START DMA */
"mov r10, r0 \n"
"mov r11, r1 \n"
"mov r0, #0 \n"
"ldr r12, =fiq_handler \n"
"ldr sp, =_fiqstackend \n"
"mov lr, r2 \n"
"msr spsr_all, r3 \n"
"bx r12 \n"
);
} }
void pcm_play_dma_start(const void *addr, size_t size) void pcm_play_dma_start(const void *addr, size_t size)
{ {
/* S1: DMA channel 0 set */ /* S1: DMA channel 0 set */
dma_setup_channel(DMA_IISOUT_CHANNEL, DMA_IISOUT_SELECT, DMA_MEM_TO_IO, DMACON0 = (0 << 30) | /* DEVSEL */
DMA_IISOUT_DSIZE, DMA_IISOUT_BLEN, (void *)addr, size / 2, (1 << 29) | /* DIR */
dma_callback); (0 << 24) | /* SCHCNT */
(1 << 22) | /* DSIZE */
(0 << 19) | /* BLEN */
(0 << 18) | /* RELOAD */
(0 << 17) | /* HCOMINT */
(1 << 16) | /* WCOMINT */
(0 << 0); /* OFFSET */
#ifdef IPOD_NANO2G #ifdef IPOD_NANO2G
PCON5 = (PCON5 & ~(0xFFFF0000)) | 0x77720000; PCON5 = (PCON5 & ~(0xFFFF0000)) | 0x77720000;
PCON6 = (PCON6 & ~(0x0F000000)) | 0x02000000; PCON6 = (PCON6 & ~(0x0F000000)) | 0x02000000;
I2STXCON = (1 << 20) | /* undocumented */ I2STXCON = (1 << 20) | /* undocumented */
(DMA_IISOUT_BLEN << 16) | /* burst length */ (0 << 16) | /* burst length */
(0 << 15) | /* 0 = falling edge */ (0 << 15) | /* 0 = falling edge */
(0 << 13) | /* 0 = basic I2S format */ (0 << 13) | /* 0 = basic I2S format */
(0 << 12) | /* 0 = MSB first */ (0 << 12) | /* 0 = MSB first */
@ -163,8 +221,10 @@ void pcm_play_dma_start(const void *addr, size_t size)
#endif #endif
/* S3: DMA channel 0 on */ /* S3: DMA channel 0 on */
// clean_dcache(); clean_dcache();
dma_enable_channel(DMA_IISOUT_CHANNEL); dmamode = 0;
dblbuf = (unsigned char*)addr + size - 2048;
bootstrap_fiq(addr, (size >> 1) - 1025);
/* S4: IIS Tx clock on */ /* S4: IIS Tx clock on */
I2SCLKCON = (1 << 0); /* 1 = power on */ I2SCLKCON = (1 << 0); /* 1 = power on */
@ -179,7 +239,7 @@ void pcm_play_dma_start(const void *addr, size_t size)
void pcm_play_dma_stop(void) void pcm_play_dma_stop(void)
{ {
/* DMA channel off */ /* DMA channel off */
dma_disable_channel(DMA_IISOUT_CHANNEL); DMACOM0 = 5;
/* TODO Some time wait */ /* TODO Some time wait */
/* LRCK half cycle wait */ /* LRCK half cycle wait */
@ -215,6 +275,10 @@ void pcm_play_dma_init(void)
/* enable clock to the IIS module */ /* enable clock to the IIS module */
PWRCON &= ~(1 << 6); PWRCON &= ~(1 << 6);
/* Enable the DMA FIQ */
INTMOD |= (1 << 10);
INTMSK |= (1 << 10);
audiohw_preinit(); audiohw_preinit();
} }
@ -248,20 +312,20 @@ void pcm_dma_apply_settings(void)
/* configure MCLK */ /* configure MCLK */
CLKCON = (CLKCON & ~(0xFF)) | CLKCON = (CLKCON & ~(0xFF)) |
(0 << 7) | /* MCLK_MASK */ (0 << 7) | /* MCLK_MASK */
(2 << 5) | /* MCLK_SEL = PLL2 */ (2 << 5) | /* MCLK_SEL = PLL1 */
(1 << 4) | /* MCLK_DIV_ON */ (1 << 4) | /* MCLK_DIV_ON */
(div.cdiv - 1); /* MCLK_DIV_VAL */ (div.cdiv - 1); /* MCLK_DIV_VAL */
} }
size_t pcm_get_bytes_waiting(void) size_t pcm_get_bytes_waiting(void)
{ {
return DMACTCNT0 * 2; return (DMACTCNT0 + 1) << 1;
} }
const void * pcm_play_dma_get_peak_buffer(int *count) const void * pcm_play_dma_get_peak_buffer(int *count)
{ {
*count = DMACTCNT0 >> 1; *count = DMACTCNT0 >> 1;
return (void *)((DMACADDR0 + 2) & ~3); return (void *)(((DMACADDR0 + 2) & ~3) | 0x40000000);
} }
#ifdef HAVE_PCM_DMA_ADDRESS #ifdef HAVE_PCM_DMA_ADDRESS

View file

@ -30,7 +30,6 @@
extern __attribute__((weak,alias("UIRQ"))) void name (void) extern __attribute__((weak,alias("UIRQ"))) void name (void)
void irq_handler(void) __attribute__((interrupt ("IRQ"), naked)); void irq_handler(void) __attribute__((interrupt ("IRQ"), naked));
void fiq_handler(void) __attribute__((interrupt ("FIQ"), naked));
default_interrupt(EXT0); default_interrupt(EXT0);
default_interrupt(EXT1); default_interrupt(EXT1);
@ -142,14 +141,6 @@ void irq_handler(void)
"subs pc, lr, #4 \n"); /* Return from IRQ */ "subs pc, lr, #4 \n"); /* Return from IRQ */
} }
void fiq_handler(void)
{
asm volatile (
"subs pc, lr, #4 \r\n"
);
}
void system_init(void) void system_init(void)
{ {
} }