1
0
Fork 0
forked from len0rd/rockbox

Give pcm-coldfire.c a much needed shakedown. Fixes FS#9378 - metronome freezes (works for me now on X5 and H120 after running it at 400bpm for an hour or so). The reason seems to have been DMA_INT in DCR should also be cleared when forcing a channel stop. Also fixes a dodgey keyclick without music playing that I was getting on H120.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19991 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Michael Sevakis 2009-02-12 21:32:14 +00:00
parent b411f5eee5
commit ca5ca5b6ef

View file

@ -8,6 +8,7 @@
* $Id$ * $Id$
* *
* Copyright (C) 2006 by Michael Sevakis * Copyright (C) 2006 by Michael Sevakis
* Copyright (C) 2005 by Linus Nielsen Feltzing
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -28,45 +29,30 @@
#include "spdif.h" #include "spdif.h"
#endif #endif
#define IIS_PLAY_DEFPARM ( (freq_ent[FPARM_CLOCKSEL] << 12) | \ #define IIS_PLAY_DEFPARM ( (freq_ent[FPARM_CLOCKSEL] << 12) | \
(IIS_PLAY & (7 << 8)) | \ (IIS_PLAY & (7 << 8)) | \
(4 << 2) ) /* 64 bit clocks / word clock */ (4 << 2) ) /* 64 bit clocks / word clock */
#define IIS_FIFO_RESET (1 << 11) #define IIS_FIFO_RESET (1 << 11)
#define PDIR2_FIFO_RESET (1 << 9) #define PDIR2_FIFO_RESET (1 << 9)
#if defined(IAUDIO_X5) || defined(IAUDIO_M5) || defined(IAUDIO_M3) #if defined(IAUDIO_X5) || defined(IAUDIO_M5) || defined(IAUDIO_M3)
#define SET_IIS_PLAY(x) IIS1CONFIG = (x) #define IIS_PLAY IIS1CONFIG
#define IIS_PLAY IIS1CONFIG
#else #else
#define SET_IIS_PLAY(x) IIS2CONFIG = (x) #define IIS_PLAY IIS2CONFIG
#define IIS_PLAY IIS2CONFIG
#endif #endif
struct dma_lock #ifdef HAVE_SPDIF_OUT
{ /* EBU TX auto sync, PDIR2 fifo auto sync, IIS1 fifo auto sync */
int locked; #define AUDIOGLOB_DEFPARM ((1 << 10) | (1 << 8) | (1 << 7))
unsigned long state; #else
}; /* PDIR2 fifo auto sync, IIS1 fifo auto sync */
#define AUDIOGLOB_DEFPARM ((1 << 8) | (1 << 7))
static bool is_playback_monitoring(void) #endif
{
return (IIS_PLAY & (7 << 8)) == (3 << 8);
}
static void iis_play_reset_if_playback(bool if_playback)
{
bool is_playback = is_playback_monitoring();
if (is_playback != if_playback)
return;
or_l(IIS_FIFO_RESET, &IIS_PLAY);
}
/** Sample rates **/
#define PLLCR_SET_AUDIO_BITS_DEFPARM \ #define PLLCR_SET_AUDIO_BITS_DEFPARM \
((freq_ent[FPARM_CLSEL] << 28) | (1 << 22)) ((freq_ent[FPARM_CLSEL] << 28) | (1 << 22))
/** Sample rates **/
#define FPARM_CLOCKSEL 0 #define FPARM_CLOCKSEL 0
#define FPARM_CLSEL 1 #define FPARM_CLSEL 1
#if CONFIG_CPU == MCF5249 && defined(HAVE_UDA1380) #if CONFIG_CPU == MCF5249 && defined(HAVE_UDA1380)
@ -91,62 +77,62 @@ static const unsigned char pcm_freq_parms[HW_NUM_FREQ][2] =
static const unsigned char *freq_ent; static const unsigned char *freq_ent;
/* apply audio settings */ /* Lock status struct for playback and recording */
static bool _pcm_dma_apply_settings(bool clear_reset) struct dma_lock
{ {
bool did_reset = false; int locked;
unsigned long iis_play_defparm; unsigned long state;
};
static void iis_play_reset(void)
{
or_l(IIS_FIFO_RESET, &IIS_PLAY);
and_l(~IIS_FIFO_RESET, &IIS_PLAY);
PDOR3 = 0;
}
static bool is_playback_monitoring(void)
{
return (IIS_PLAY & (7 << 8)) == (3 << 8);
}
static void iis_play_reset_if_playback(bool if_playback)
{
int level = set_irq_level(DMA_IRQ_LEVEL); int level = set_irq_level(DMA_IRQ_LEVEL);
if (is_playback_monitoring() == if_playback)
/* remember table entry */ iis_play_reset();
freq_ent = pcm_freq_parms[pcm_fsel];
iis_play_defparm = IIS_PLAY_DEFPARM;
if (pcm_sampr != pcm_curr_sampr)
{
/* Reprogramming bits 15-12 requires FIFO to be in a reset
condition - Users Manual 17-8, Note 11 */
or_l(IIS_FIFO_RESET, &IIS_PLAY);
/* Important for TLV320 - this must happen in the correct order
or starting recording will sound absolutely awful once in
awhile - audiohw_set_frequency then coldfire_set_pllcr_audio_bits
*/
SET_IIS_PLAY(IIS_PLAY_DEFPARM | IIS_FIFO_RESET);
audiohw_set_frequency(pcm_fsel);
coldfire_set_pllcr_audio_bits(PLLCR_SET_AUDIO_BITS_DEFPARM);
did_reset = true;
}
/* If a reset was done because of a sample rate change, IIS_PLAY will have
been set already, so only needs to be set again if the reset flag must
be cleared. If the frequency didn't change, it was never altered and
the reset flag can just be removed or no action taken. */
if (clear_reset)
SET_IIS_PLAY(IIS_PLAY_DEFPARM & ~IIS_FIFO_RESET);
restore_irq(level); restore_irq(level);
}
#if 0 /* apply audio settings */
logf("IISPLAY: %08X", IIS_PLAY);
#endif
return did_reset;
} /* _pcm_dma_apply_settings */
/* This clears the reset bit to enable monitoring immediately if monitoring /* This clears the reset bit to enable monitoring immediately if monitoring
recording sources or always if playback is in progress - we might be recording sources or always if playback is in progress - we might be
switching samplerates on the fly */ switching samplerates on the fly */
void pcm_dma_apply_settings(void) void pcm_dma_apply_settings(void)
{ {
int level = set_irq_level(DMA_IRQ_LEVEL); int level = set_irq_level(DMA_IRQ_LEVEL);
bool pbm = is_playback_monitoring();
bool kick = (DCR0 & DMA_EEXT) != 0 && pbm;
/* Clear reset if not playback monitoring or peripheral request is /* remember table entry */
active and playback monitoring */ freq_ent = pcm_freq_parms[pcm_fsel];
if (_pcm_dma_apply_settings(!pbm || kick) && kick)
/* Reprogramming bits 15-12 requires FIFO to be in a reset
condition - Users Manual 17-8, Note 11 */
or_l(IIS_FIFO_RESET, &IIS_PLAY);
/* Important for TLV320 - this must happen in the correct order
or starting recording will sound absolutely awful once in
awhile - audiohw_set_frequency then coldfire_set_pllcr_audio_bits
*/
IIS_PLAY = IIS_PLAY_DEFPARM | IIS_FIFO_RESET;
restore_irq(level);
audiohw_set_frequency(pcm_fsel);
coldfire_set_pllcr_audio_bits(PLLCR_SET_AUDIO_BITS_DEFPARM);
level = set_irq_level(DMA_IRQ_LEVEL);
IIS_PLAY = IIS_PLAY_DEFPARM;
if ((DCR0 & DMA_EEXT) != 0 && is_playback_monitoring())
PDOR3 = 0; /* Kick FIFO out of reset by writing to it */ PDOR3 = 0; /* Kick FIFO out of reset by writing to it */
restore_irq(level); restore_irq(level);
@ -156,24 +142,18 @@ void pcm_play_dma_init(void)
{ {
freq_ent = pcm_freq_parms[pcm_fsel]; freq_ent = pcm_freq_parms[pcm_fsel];
AUDIOGLOB = (1 << 8) /* IIS1 fifo auto sync */ AUDIOGLOB = AUDIOGLOB_DEFPARM;
| (1 << 7) /* PDIR2 fifo auto sync */ DIVR0 = 54; /* DMA0 is mapped into vector 54 in system.c */
#ifdef HAVE_SPDIF_OUT
| (1 << 10) /* EBU TX auto sync */
#endif
;
DIVR0 = 54; /* DMA0 is mapped into vector 54 in system.c */
and_l(0xffffff00, &DMAROUTE); and_l(0xffffff00, &DMAROUTE);
or_l(DMA0_REQ_AUDIO_1, &DMAROUTE); or_l(DMA0_REQ_AUDIO_1, &DMAROUTE);
DMACONFIG = 1; /* DMA0Req = PDOR3, DMA1Req = PDIR2 */ DMACONFIG = 1; /* DMA0Req = PDOR3, DMA1Req = PDIR2 */
BCR0 = 0; /* No bytes waiting */
/* Call pcm_play_dma_stop to initialize everything. */ ICR6 = (6 << 2); /* Enable interrupt at level 6, priority 0 */
pcm_play_dma_stop();
/* Setup Coldfire I2S before initializing hardware or changing /* Setup Coldfire I2S before initializing hardware or changing
other settings. */ other settings. */
or_l(IIS_FIFO_RESET, &IIS_PLAY); or_l(IIS_FIFO_RESET, &IIS_PLAY);
SET_IIS_PLAY(IIS_PLAY_DEFPARM | IIS_FIFO_RESET); IIS_PLAY = IIS_PLAY_DEFPARM | IIS_FIFO_RESET;
audio_set_output_source(AUDIO_SRC_PLAYBACK); audio_set_output_source(AUDIO_SRC_PLAYBACK);
/* Initialize default register values. */ /* Initialize default register values. */
@ -187,13 +167,12 @@ void pcm_play_dma_init(void)
#if defined(HAVE_SPDIF_REC) || defined(HAVE_SPDIF_OUT) #if defined(HAVE_SPDIF_REC) || defined(HAVE_SPDIF_OUT)
spdif_init(); spdif_init();
#endif #endif
/* Enable interrupt at level 6, priority 0 */
ICR6 = (6 << 2);
} /* pcm_play_dma_init */ } /* pcm_play_dma_init */
void pcm_postinit(void) void pcm_postinit(void)
{ {
audiohw_postinit(); audiohw_postinit();
iis_play_reset();
} }
/** DMA **/ /** DMA **/
@ -225,22 +204,22 @@ void pcm_play_unlock(void)
/* Set up the DMA transfer that kicks in when the audio FIFO gets empty */ /* Set up the DMA transfer that kicks in when the audio FIFO gets empty */
void pcm_play_dma_start(const void *addr, size_t size) void pcm_play_dma_start(const void *addr, size_t size)
{ {
logf("pcm_play_dma_start"); /* Stop any DMA in progress */
pcm_play_dma_stop();
/* stop any DMA in progress */ addr = (void *)(((long)addr + 3) & ~3);
DSR0 = 1; size &= ~3;
DCR0 = 0;
if (size <= 0)
return;
/* Set up DMA transfer */ /* Set up DMA transfer */
SAR0 = (unsigned long)addr; /* Source address */ SAR0 = (unsigned long)addr; /* Source address */
DAR0 = (unsigned long)&PDOR3; /* Destination address */ DAR0 = (unsigned long)&PDOR3; /* Destination address */
BCR0 = (unsigned long)size; /* Bytes to transfer */ BCR0 = (unsigned long)size; /* Bytes to transfer */
/* Enable the FIFO and force one write to it */ DCR0 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA | DMA_SINC |
_pcm_dma_apply_settings(is_playback_monitoring()); DMA_SSIZE(DMA_SIZE_LINE) | DMA_START;
DCR0 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA |
DMA_SINC | DMA_SSIZE(DMA_SIZE_LINE) | DMA_START;
dma_play_lock.state = (1 << 14); dma_play_lock.state = (1 << 14);
} /* pcm_play_dma_start */ } /* pcm_play_dma_start */
@ -248,12 +227,10 @@ void pcm_play_dma_start(const void *addr, size_t size)
/* Stops the DMA transfer and interrupt */ /* Stops the DMA transfer and interrupt */
void pcm_play_dma_stop(void) void pcm_play_dma_stop(void)
{ {
DSR0 = 1; and_l(~(DMA_EEXT | DMA_INT), &DCR0); /* per request and int OFF */
DCR0 = 0; BCR0 = 0; /* No bytes remaining */
BCR0 = 0; DSR0 = 1; /* Clear interrupt, errors, stop transfer */
/* Place TX FIFO in reset condition if playback monitoring is on.
Recording monitoring something else should not be stopped. */
iis_play_reset_if_playback(true); iis_play_reset_if_playback(true);
dma_play_lock.state = (0 << 14); dma_play_lock.state = (0 << 14);
@ -264,16 +241,16 @@ void pcm_play_dma_pause(bool pause)
if (pause) if (pause)
{ {
/* pause playback on current buffer */ /* pause playback on current buffer */
and_l(~DMA_EEXT, &DCR0); and_l(~(DMA_EEXT | DMA_INT), &DCR0); /* per request and int OFF */
DSR0 = 1; /* stop channel */
iis_play_reset_if_playback(true); iis_play_reset_if_playback(true);
dma_play_lock.state = (0 << 14); dma_play_lock.state = (0 << 14);
} }
else else
{ {
/* restart playback on current buffer */ /* restart playback on current buffer */
/* Enable the FIFO and force one write to it */ iis_play_reset_if_playback(true);
_pcm_dma_apply_settings(is_playback_monitoring()); or_l(DMA_INT | DMA_EEXT | DMA_START, &DCR0); /* everything ON */
or_l(DMA_EEXT | DMA_START, &DCR0);
dma_play_lock.state = (1 << 14); dma_play_lock.state = (1 << 14);
} }
} /* pcm_play_dma_pause */ } /* pcm_play_dma_pause */
@ -288,38 +265,14 @@ size_t pcm_get_bytes_waiting(void)
void DMA0(void) __attribute__ ((interrupt_handler, section(".icode"))); void DMA0(void) __attribute__ ((interrupt_handler, section(".icode")));
void DMA0(void) void DMA0(void)
{ {
int res = DSR0; unsigned long res = DSR0;
DSR0 = 1; /* Clear interrupt */ and_l(~(DMA_EEXT | DMA_INT), &DCR0); /* per request and int OFF */
and_l(~DMA_EEXT, &DCR0); /* Disable peripheral request */ DSR0 = 1; /* Clear interrupt and errors */
/* Stop on error */
if ((res & 0x70) == 0) if (res & 0x70)
{
pcm_more_callback_type get_more = pcm_callback_for_more;
unsigned char *next_start;
size_t next_size = 0;
if (get_more)
get_more(&next_start, &next_size);
if (next_size > 0)
{
SAR0 = (unsigned long)next_start; /* Source address */
BCR0 = next_size; /* Bytes to transfer */
or_l(DMA_EEXT, &DCR0); /* Enable peripheral request */
return;
}
else
{
/* Finished playing */
#if 0
/* int. logfs can trash the display */
logf("DMA0 No Data:0x%04x", res);
#endif
}
}
else
{ {
/* Stop on error */
logf("DMA0 err: %02x", res); logf("DMA0 err: %02x", res);
#if 0 #if 0
logf(" SAR0: %08x", SAR0); logf(" SAR0: %08x", SAR0);
@ -328,6 +281,27 @@ void DMA0(void)
logf(" DCR0: %08x", DCR0); logf(" DCR0: %08x", DCR0);
#endif #endif
} }
else
{
pcm_more_callback_type get_more = pcm_callback_for_more;
unsigned char *start;
size_t size = 0;
if (get_more)
get_more(&start, &size);
start = (unsigned char *)(((long)start + 3) & ~3);
size &= ~3;
if (size > 0)
{
SAR0 = (unsigned long)start; /* Source address */
BCR0 = size; /* Bytes to transfer */
or_l(DMA_EEXT | DMA_INT, &DCR0); /* per request and int ON */
return;
}
/* Finished playing */
}
/* Stop interrupt and futher transfers */ /* Stop interrupt and futher transfers */
pcm_play_dma_stop(); pcm_play_dma_stop();
@ -337,8 +311,15 @@ void DMA0(void)
const void * pcm_play_dma_get_peak_buffer(int *count) const void * pcm_play_dma_get_peak_buffer(int *count)
{ {
unsigned long addr = SAR0; unsigned long addr, cnt;
int cnt = BCR0;
/* Make sure interrupt doesn't change the second value after we read the
* first value. */
int level = set_irq_level(DMA_IRQ_LEVEL);
addr = SAR0;
cnt = BCR0;
restore_irq(level);
*count = (cnt & 0xffffff) >> 2; *count = (cnt & 0xffffff) >> 2;
return (void *)((addr + 2) & ~3); return (void *)((addr + 2) & ~3);
} /* pcm_play_dma_get_peak_buffer */ } /* pcm_play_dma_get_peak_buffer */
@ -370,13 +351,15 @@ void pcm_rec_unlock(void)
void pcm_rec_dma_start(void *addr, size_t size) void pcm_rec_dma_start(void *addr, size_t size)
{ {
/* stop any DMA in progress */ /* stop any DMA in progress */
and_l(~DMA_EEXT, &DCR1); pcm_rec_dma_stop();
DSR1 = 1;
addr = (void *)(((long)addr + 3) & ~3);
size &= ~3;
if (size <= 0)
return;
and_l(~PDIR2_FIFO_RESET, &DATAINCONTROL); and_l(~PDIR2_FIFO_RESET, &DATAINCONTROL);
/* Clear TX FIFO reset bit if the source is not set to monitor playback
otherwise maintain independence between playback and recording. */
_pcm_dma_apply_settings(!is_playback_monitoring());
/* Start the DMA transfer.. */ /* Start the DMA transfer.. */
#ifdef HAVE_SPDIF_REC #ifdef HAVE_SPDIF_REC
@ -384,10 +367,10 @@ void pcm_rec_dma_start(void *addr, size_t size)
INTERRUPTCLEAR = (1 << 25) | (1 << 24) | (1 << 23) | (1 << 22); INTERRUPTCLEAR = (1 << 25) | (1 << 24) | (1 << 23) | (1 << 22);
#endif #endif
SAR1 = (unsigned long)&PDIR2; /* Source address */
pcm_rec_peak_addr = (unsigned long *)addr; /* Start peaking at dest */ pcm_rec_peak_addr = (unsigned long *)addr; /* Start peaking at dest */
DAR1 = (unsigned long)addr; /* Destination address */ SAR1 = (unsigned long)&PDIR2; /* Source address */
BCR1 = (unsigned long)size; /* Bytes to transfer */ DAR1 = (unsigned long)addr; /* Destination address */
BCR1 = (unsigned long)size; /* Bytes to transfer */
DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA | DMA_DINC | DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA | DMA_DINC |
DMA_DSIZE(DMA_SIZE_LINE) | DMA_START; DMA_DSIZE(DMA_SIZE_LINE) | DMA_START;
@ -397,9 +380,10 @@ void pcm_rec_dma_start(void *addr, size_t size)
void pcm_rec_dma_stop(void) void pcm_rec_dma_stop(void)
{ {
DSR1 = 1; /* Clear interrupt */ and_l(~(DMA_EEXT | DMA_INT), &DCR1); /* per request and int OFF */
DCR1 = 0; DSR1 = 1; /* Clear interrupt, errors, stop transfer */
BCR1 = 0; BCR1 = 0; /* No bytes received */
or_l(PDIR2_FIFO_RESET, &DATAINCONTROL); or_l(PDIR2_FIFO_RESET, &DATAINCONTROL);
iis_play_reset_if_playback(false); iis_play_reset_if_playback(false);
@ -422,6 +406,8 @@ void pcm_rec_dma_init(void)
void pcm_rec_dma_close(void) void pcm_rec_dma_close(void)
{ {
pcm_rec_dma_stop();
and_l(0xffff00ff, &DMAROUTE); and_l(0xffff00ff, &DMAROUTE);
ICR7 = 0x00; /* Disable interrupt */ ICR7 = 0x00; /* Disable interrupt */
dma_rec_lock.state = (0 << 15); dma_rec_lock.state = (0 << 15);
@ -432,12 +418,12 @@ void pcm_rec_dma_close(void)
void DMA1(void) __attribute__ ((interrupt_handler, section(".icode"))); void DMA1(void) __attribute__ ((interrupt_handler, section(".icode")));
void DMA1(void) void DMA1(void)
{ {
int res = DSR1; unsigned long res = DSR1;
int status = 0; int status = 0;
pcm_more_callback_type2 more_ready; pcm_more_callback_type2 more_ready;
DSR1 = 1; /* Clear interrupt */ and_l(~(DMA_EEXT | DMA_INT), &DCR1); /* per request and int OFF */
and_l(~DMA_EEXT, &DCR1); /* Disable peripheral request */ DSR1 = 1; /* Clear interrupt and errors */
if (res & 0x70) if (res & 0x70)
{ {
@ -468,10 +454,6 @@ void DMA1(void)
if (more_ready != NULL && more_ready(status) >= 0) if (more_ready != NULL && more_ready(status) >= 0)
return; return;
#if 0
/* int. logfs can trash the display */
logf("DMA1 done:%04x %d", res, status);
#endif
/* Finished recording */ /* Finished recording */
pcm_rec_dma_stop(); pcm_rec_dma_stop();
/* Inform PCM that we're done */ /* Inform PCM that we're done */
@ -481,16 +463,32 @@ void DMA1(void)
/* Continue transferring data in - call from interrupt callback */ /* Continue transferring data in - call from interrupt callback */
void pcm_record_more(void *start, size_t size) void pcm_record_more(void *start, size_t size)
{ {
start = (void *)(((long)start + 3) & ~3);
size &= ~3;
if (size <= 0)
{
pcm_rec_dma_stop();
return;
}
pcm_rec_peak_addr = (unsigned long *)start; /* Start peaking at dest */ pcm_rec_peak_addr = (unsigned long *)start; /* Start peaking at dest */
DAR1 = (unsigned long)start; /* Destination address */ DAR1 = (unsigned long)start; /* Destination address */
BCR1 = (unsigned long)size; /* Bytes to transfer */ BCR1 = (unsigned long)size; /* Bytes to transfer */
or_l(DMA_EEXT, &DCR1); /* Enable peripheral request */ or_l(DMA_EEXT | DMA_INT, &DCR1); /* per request and int ON */
} /* pcm_record_more */ } /* pcm_record_more */
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 = DAR1;
/* Make sure interrupt doesn't change the second value after we read the
* first value. */
int level = set_irq_level(DMA_IRQ_LEVEL);
addr = (unsigned long)pcm_rec_peak_addr;
end = DAR1;
restore_irq(level);
addr >>= 2; addr >>= 2;
*count = (end >> 2) - addr; *count = (end >> 2) - addr;
return (void *)(addr << 2); return (void *)(addr << 2);