forked from len0rd/rockbox
Coldfire targets: Shuffle IRQ levels around to have all interaction between low level audio function calls and DMA be atomic. Make recording and playback independently startable and stoppable so one can be running and not interfere with the other. All tests I can do at the moment check out ok (play, record, play+record, FM radio on iRivers, S/PDIF on H120 (w/running optical on/off), and on-the-fly samplerate changes). Recording tested for well over an hour run and no problems.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12658 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
2c94c1afc3
commit
633f388002
12 changed files with 148 additions and 36 deletions
|
@ -19,6 +19,8 @@ stopwatch.c
|
|||
vbrfix.c
|
||||
viewer.c
|
||||
|
||||
ringmod.c
|
||||
|
||||
/* plugins built for all targets, but not for the simulator */
|
||||
#if !defined(SIMULATOR)
|
||||
metronome.c
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
void audio_set_output_source(int source)
|
||||
{
|
||||
int level = set_irq_level(DMA_IRQ_LEVEL);
|
||||
unsigned long txsrc;
|
||||
|
||||
if ((unsigned)source >= AUDIO_NUM_SOURCES)
|
||||
|
@ -31,6 +32,7 @@ void audio_set_output_source(int source)
|
|||
txsrc = (4 << 8); /* recording, iis1RcvData */
|
||||
|
||||
IIS1CONFIG = (IIS1CONFIG & ~(7 << 8)) | txsrc;
|
||||
set_irq_level(level);
|
||||
} /* audio_set_output_source */
|
||||
|
||||
void audio_set_source(int source, unsigned flags)
|
||||
|
|
|
@ -60,7 +60,7 @@ bool ide_powered(void)
|
|||
void power_off(void)
|
||||
{
|
||||
lcd_remote_poweroff();
|
||||
set_irq_level(HIGHEST_IRQ_LEVEL);
|
||||
set_irq_level(DISABLE_INTERRUPTS);
|
||||
and_l(~0x00000008, &GPIO_OUT); /* Set KEEPACT low */
|
||||
asm("halt");
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
void audio_set_output_source(int source)
|
||||
{
|
||||
int level = set_irq_level(DMA_IRQ_LEVEL);
|
||||
unsigned long txsrc;
|
||||
|
||||
if ((unsigned)source >= AUDIO_NUM_SOURCES)
|
||||
|
@ -31,6 +32,7 @@ void audio_set_output_source(int source)
|
|||
txsrc = (4 << 8); /* recording, iis1RcvData */
|
||||
|
||||
IIS1CONFIG = (IIS1CONFIG & ~(7 << 8)) | txsrc;
|
||||
set_irq_level(level);
|
||||
} /* audio_set_output_source */
|
||||
|
||||
void audio_set_source(int source, unsigned flags)
|
||||
|
|
|
@ -60,7 +60,7 @@ bool ide_powered(void)
|
|||
void power_off(void)
|
||||
{
|
||||
lcd_remote_poweroff();
|
||||
set_irq_level(HIGHEST_IRQ_LEVEL);
|
||||
set_irq_level(DISABLE_INTERRUPTS);
|
||||
and_l(~0x00000008, &GPIO_OUT); /* Set KEEPACT low */
|
||||
asm("halt");
|
||||
}
|
||||
|
|
|
@ -34,10 +34,14 @@ void audio_set_output_source(int source)
|
|||
#endif
|
||||
};
|
||||
|
||||
int level = set_irq_level(DMA_IRQ_LEVEL);
|
||||
|
||||
if ((unsigned)source >= AUDIO_NUM_SOURCES)
|
||||
source = AUDIO_SRC_PLAYBACK;
|
||||
|
||||
IIS2CONFIG = (IIS2CONFIG & ~(7 << 8)) | (txsrc_select[source+1] << 8);
|
||||
|
||||
set_irq_level(level);
|
||||
} /* audio_set_output_source */
|
||||
|
||||
void audio_set_source(int source, unsigned flags)
|
||||
|
|
|
@ -119,7 +119,7 @@ bool ide_powered(void)
|
|||
|
||||
void power_off(void)
|
||||
{
|
||||
set_irq_level(HIGHEST_IRQ_LEVEL);
|
||||
set_irq_level(DISABLE_INTERRUPTS);
|
||||
and_l(~0x00080000, &GPIO1_OUT);
|
||||
asm("halt");
|
||||
while(1)
|
||||
|
|
|
@ -63,30 +63,42 @@ void spdif_set_output_source(int source, bool src_on)
|
|||
};
|
||||
|
||||
bool kick;
|
||||
int level;
|
||||
int iis2config = 0;
|
||||
|
||||
if ((unsigned)source >= ARRAYLEN(ebu1_config))
|
||||
source = AUDIO_SRC_PLAYBACK;
|
||||
|
||||
spdif_source = source;
|
||||
spdif_on = spdif_powered() && src_on;
|
||||
kick = spdif_on && source == AUDIO_SRC_PLAYBACK;
|
||||
|
||||
/* Keep a DMA interrupt initiated stop from changing play state */
|
||||
level = set_irq_level(DMA_IRQ_LEVEL);
|
||||
|
||||
kick = spdif_on && source == AUDIO_SRC_PLAYBACK
|
||||
&& (DCR0 & DMA_EEXT);
|
||||
|
||||
/* FIFO must be in reset condition to reprogram bits 15-12 */
|
||||
or_l(0x800, &EBU1CONFIG);
|
||||
|
||||
if (kick)
|
||||
{
|
||||
iis2config = IIS2CONFIG;
|
||||
or_l(0x800, &IIS2CONFIG); /* Have to resync IIS2 TXSRC */
|
||||
}
|
||||
|
||||
/* Tranceiver must be powered or else monitoring will be disabled.
|
||||
CLOCKSEL bits only have relevance to normal operation so just
|
||||
set them always. */
|
||||
EBU1CONFIG = (spdif_on ? ebu1_config[source + 1] : 0) | (7 << 12);
|
||||
|
||||
if (kick && (DCR0 & DMA_EEXT)) /* only if still playing */
|
||||
if (kick)
|
||||
{
|
||||
and_l(~0x800, &IIS2CONFIG);
|
||||
IIS2CONFIG = iis2config;
|
||||
PDOR3 = 0; /* A write to the FIFO kick-starts playback */
|
||||
}
|
||||
|
||||
set_irq_level(level);
|
||||
} /* spdif_set_output_source */
|
||||
|
||||
/* Return the last set S/PDIF audio source */
|
||||
|
|
|
@ -91,7 +91,7 @@ bool ide_powered(void)
|
|||
|
||||
void power_off(void)
|
||||
{
|
||||
set_irq_level(HIGHEST_IRQ_LEVEL);
|
||||
set_irq_level(DISABLE_INTERRUPTS);
|
||||
and_l(~0x00080000, &GPIO1_OUT);
|
||||
asm("halt");
|
||||
while(1)
|
||||
|
|
|
@ -55,6 +55,21 @@ static int peaks[4]; /* p-l, p-r, r-l, r-r */
|
|||
#define IIS_PLAY IIS2CONFIG
|
||||
#endif
|
||||
|
||||
static bool is_playback_monitoring(void)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
#define PLLCR_SET_AUDIO_BITS_DEFPARM \
|
||||
((freq_ent[FPARM_CLSEL] << 28) | (1 << 22))
|
||||
|
||||
|
@ -113,9 +128,10 @@ void pcm_set_frequency(unsigned int frequency)
|
|||
} /* pcm_set_frequency */
|
||||
|
||||
/* apply audio settings */
|
||||
void _pcm_apply_settings(bool clear_reset)
|
||||
bool _pcm_apply_settings(bool clear_reset)
|
||||
{
|
||||
static int last_pcm_freq = 0;
|
||||
bool did_reset = false;
|
||||
|
||||
if (pcm_freq != last_pcm_freq)
|
||||
{
|
||||
|
@ -125,6 +141,7 @@ void _pcm_apply_settings(bool clear_reset)
|
|||
or_l(IIS_FIFO_RESET, &IIS_PLAY);
|
||||
audiohw_set_frequency(freq_ent[FPARM_FSEL]);
|
||||
coldfire_set_pllcr_audio_bits(PLLCR_SET_AUDIO_BITS_DEFPARM);
|
||||
did_reset = true;
|
||||
}
|
||||
|
||||
SET_IIS_PLAY(IIS_PLAY_DEFPARM |
|
||||
|
@ -132,12 +149,25 @@ void _pcm_apply_settings(bool clear_reset)
|
|||
#if 0
|
||||
logf("IISPLAY: %08X", IIS_PLAY);
|
||||
#endif
|
||||
|
||||
return did_reset;
|
||||
} /* _pcm_apply_settings */
|
||||
|
||||
/* This clears the reset bit to enable monitoring immediately */
|
||||
/* This clears the reset bit to enable monitoring immediately if monitoring
|
||||
recording sources or always if playback is in progress - we might be
|
||||
switching samplerates on the fly */
|
||||
void pcm_apply_settings(void)
|
||||
{
|
||||
_pcm_apply_settings(true);
|
||||
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
|
||||
active and playback monitoring */
|
||||
if (_pcm_apply_settings(!pbm || kick) && kick)
|
||||
PDOR3 = 0; /* Kick FIFO out of reset by writing to it */
|
||||
|
||||
set_irq_level(level);
|
||||
} /* pcm_apply_settings */
|
||||
|
||||
/** DMA **/
|
||||
|
@ -149,11 +179,17 @@ void pcm_apply_settings(void)
|
|||
/* 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)
|
||||
{
|
||||
int level;
|
||||
|
||||
logf("pcm_play_dma_start");
|
||||
|
||||
addr = (void *)((unsigned long)addr & ~3); /* Align data */
|
||||
size &= ~3; /* Size must be multiple of 4 */
|
||||
|
||||
/* If a tranfer is going, prevent an interrupt while setting up
|
||||
a new one */
|
||||
level = set_irq_level(DMA_IRQ_LEVEL);
|
||||
|
||||
pcm_playing = true;
|
||||
|
||||
/* Set up DMA transfer */
|
||||
|
@ -162,28 +198,38 @@ void pcm_play_dma_start(const void *addr, size_t size)
|
|||
BCR0 = (unsigned long)size; /* Bytes to transfer */
|
||||
|
||||
/* Enable the FIFO and force one write to it */
|
||||
pcm_apply_settings();
|
||||
_pcm_apply_settings(is_playback_monitoring());
|
||||
|
||||
DCR0 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA |
|
||||
DMA_SINC | DMA_SSIZE(DMA_SIZE_LINE) | DMA_START;
|
||||
|
||||
set_irq_level(level);
|
||||
} /* pcm_play_dma_start */
|
||||
|
||||
/* Stops the DMA transfer and interrupt */
|
||||
void pcm_play_dma_stop(void)
|
||||
static void pcm_play_dma_stop_irq(void)
|
||||
{
|
||||
#if 0
|
||||
logf("pcm_play_dma_stop");
|
||||
#endif
|
||||
|
||||
pcm_playing = false;
|
||||
|
||||
DSR0 = 1;
|
||||
DCR0 = 0;
|
||||
|
||||
/* Place FIFO in reset condition */
|
||||
or_l(IIS_FIFO_RESET, &IIS_PLAY);
|
||||
/* 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);
|
||||
|
||||
pcm_playing = false;
|
||||
} /* pcm_play_dma_stop_irq */
|
||||
|
||||
void pcm_play_dma_stop(void)
|
||||
{
|
||||
int level = set_irq_level(DMA_IRQ_LEVEL);
|
||||
|
||||
logf("pcm_play_dma_stop");
|
||||
|
||||
pcm_play_dma_stop_irq();
|
||||
|
||||
set_irq_level(level);
|
||||
} /* pcm_play_dma_stop */
|
||||
|
||||
void pcm_init(void)
|
||||
|
@ -236,8 +282,8 @@ void pcm_init(void)
|
|||
(DAC should be at zero point now). */
|
||||
audiohw_mute(false);
|
||||
|
||||
/* Enable interrupt at level 7, priority 0 */
|
||||
ICR6 = (7 << 2);
|
||||
/* Enable interrupt at level 6, priority 0 */
|
||||
ICR6 = (6 << 2);
|
||||
and_l(~(1 << 14), &IMR); /* bit 14 is DMA0 */
|
||||
} /* pcm_init */
|
||||
|
||||
|
@ -292,7 +338,7 @@ void DMA0(void)
|
|||
#endif
|
||||
}
|
||||
|
||||
pcm_play_dma_stop();
|
||||
pcm_play_dma_stop_irq();
|
||||
} /* DMA0 */
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -300,17 +346,21 @@ void DMA0(void)
|
|||
**/
|
||||
void pcm_rec_dma_start(void *addr, size_t size)
|
||||
{
|
||||
int level;
|
||||
logf("pcm_rec_dma_start");
|
||||
|
||||
addr = (void *)((unsigned long)addr & ~3); /* Align data */
|
||||
size &= ~3; /* Size must be multiple of 4 */
|
||||
|
||||
/* No DMA1 interrupts while setting up a new transfer */
|
||||
level = set_irq_level(DMA_IRQ_LEVEL);
|
||||
|
||||
pcm_recording = true;
|
||||
|
||||
and_l(~PDIR2_FIFO_RESET, &DATAINCONTROL);
|
||||
/* Clear reset bit if the source is not set to monitor playback
|
||||
/* Clear TX FIFO reset bit if the source is not set to monitor playback
|
||||
otherwise maintain independence between playback and recording. */
|
||||
_pcm_apply_settings((IIS_PLAY & (7 << 8)) != (3 << 8));
|
||||
_pcm_apply_settings(!is_playback_monitoring());
|
||||
|
||||
/* Start the DMA transfer.. */
|
||||
#ifdef HAVE_SPDIF_IN
|
||||
|
@ -324,21 +374,35 @@ void pcm_rec_dma_start(void *addr, size_t size)
|
|||
|
||||
DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA | DMA_DINC |
|
||||
DMA_DSIZE(DMA_SIZE_LINE) /* | DMA_START */;
|
||||
|
||||
set_irq_level(level);
|
||||
} /* pcm_dma_start */
|
||||
|
||||
static void pcm_rec_dma_stop_irq(void)
|
||||
{
|
||||
DSR1 = 1; /* Clear interrupt */
|
||||
DCR1 = 0;
|
||||
pcm_recording = false;
|
||||
or_l(PDIR2_FIFO_RESET, &DATAINCONTROL);
|
||||
|
||||
iis_play_reset_if_playback(false);
|
||||
} /* pcm_rec_dma_stop_irq */
|
||||
|
||||
void pcm_rec_dma_stop(void)
|
||||
{
|
||||
int level = set_irq_level(DMA_IRQ_LEVEL);
|
||||
|
||||
logf("pcm_rec_dma_stop");
|
||||
|
||||
DSR1 = 1; /* Clear interrupt */
|
||||
DCR1 = 0;
|
||||
pcm_rec_dma_stop_irq();
|
||||
|
||||
pcm_recording = false;
|
||||
or_l(PDIR2_FIFO_RESET, &DATAINCONTROL);
|
||||
} /* pcm_dma_stop */
|
||||
set_irq_level(level);
|
||||
} /* pcm_rec_dma_stop */
|
||||
|
||||
void pcm_init_recording(void)
|
||||
{
|
||||
int level = set_irq_level(DMA_IRQ_LEVEL);
|
||||
|
||||
logf("pcm_init_recording");
|
||||
|
||||
pcm_recording = false;
|
||||
|
@ -349,21 +413,27 @@ void pcm_init_recording(void)
|
|||
and_l(0xffff00ff, &DMAROUTE);
|
||||
or_l(DMA1_REQ_AUDIO_2, &DMAROUTE);
|
||||
|
||||
pcm_rec_dma_stop();
|
||||
pcm_rec_dma_stop_irq();
|
||||
|
||||
ICR7 = (7 << 2); /* Enable interrupt at level 7, priority 0 */
|
||||
ICR7 = (6 << 2) | (1 << 0); /* Enable interrupt at level 6, priority 1 */
|
||||
and_l(~(1 << 15), &IMR); /* bit 15 is DMA1 */
|
||||
|
||||
set_irq_level(level);
|
||||
} /* pcm_init_recording */
|
||||
|
||||
void pcm_close_recording(void)
|
||||
{
|
||||
int level = set_irq_level(DMA_IRQ_LEVEL);
|
||||
|
||||
logf("pcm_close_recording");
|
||||
|
||||
pcm_rec_dma_stop();
|
||||
pcm_rec_dma_stop_irq();
|
||||
|
||||
and_l(0xffff00ff, &DMAROUTE);
|
||||
ICR7 = 0x00; /* Disable interrupt */
|
||||
or_l((1 << 15), &IMR); /* bit 15 is DMA1 */
|
||||
|
||||
set_irq_level(level);
|
||||
} /* pcm_close_recording */
|
||||
|
||||
/* DMA1 Interrupt is called when the DMA has finished transfering a chunk
|
||||
|
@ -409,10 +479,10 @@ void DMA1(void)
|
|||
logf("DMA1 done:%04x %d", res, status);
|
||||
#endif
|
||||
/* Finished recording */
|
||||
pcm_rec_dma_stop();
|
||||
pcm_rec_dma_stop_irq();
|
||||
} /* DMA1 */
|
||||
|
||||
/* Continue transferring data in */
|
||||
/* Continue transferring data in - call from interrupt callback */
|
||||
void pcm_record_more(void *start, size_t size)
|
||||
{
|
||||
rec_peak_addr = (unsigned long *)start; /* Start peaking at dest */
|
||||
|
@ -431,15 +501,23 @@ void pcm_mute(bool mute)
|
|||
void pcm_play_pause_pause(void)
|
||||
{
|
||||
/* Disable DMA peripheral request. */
|
||||
int level = set_irq_level(DMA_IRQ_LEVEL);
|
||||
|
||||
and_l(~DMA_EEXT, &DCR0);
|
||||
or_l(IIS_FIFO_RESET, &IIS_PLAY);
|
||||
iis_play_reset_if_playback(true);
|
||||
|
||||
set_irq_level(level);
|
||||
} /* pcm_play_pause_pause */
|
||||
|
||||
void pcm_play_pause_unpause(void)
|
||||
{
|
||||
int level = set_irq_level(DMA_IRQ_LEVEL);
|
||||
|
||||
/* Enable the FIFO and force one write to it */
|
||||
pcm_apply_settings();
|
||||
_pcm_apply_settings(is_playback_monitoring());
|
||||
or_l(DMA_EEXT | DMA_START, &DCR0);
|
||||
|
||||
set_irq_level(level);
|
||||
} /* pcm_play_pause_unpause */
|
||||
|
||||
/**
|
||||
|
|
|
@ -324,5 +324,8 @@ void coldfire_set_pllcr_audio_bits(long bits)
|
|||
/* Set DATAINCONTROL without disturbing FIFO reset state */
|
||||
void coldfire_set_dataincontrol(unsigned long value)
|
||||
{
|
||||
/* Have to be atomic against recording stop initiated by DMA1 */
|
||||
int level = set_irq_level(DMA_IRQ_LEVEL);
|
||||
DATAINCONTROL = (DATAINCONTROL & (1 << 9)) | value;
|
||||
set_irq_level(level);
|
||||
}
|
||||
|
|
|
@ -57,7 +57,16 @@ static inline unsigned long coldfire_get_macsr(void)
|
|||
return m;
|
||||
}
|
||||
|
||||
#define HIGHEST_IRQ_LEVEL (7<<8)
|
||||
/* ColdFire IRQ Levels/Priorities in Rockbox summary:
|
||||
* DMA0 - level 6, priority 0 (playback)
|
||||
* DMA1 - level 6, priority 1 (recording)
|
||||
* TIMER1 - level 4, priority 0 (timers)
|
||||
* TIMER0 - level 3, priority 0 (ticks)
|
||||
* GPI0 - level 3, priority 0 (pcf50606 PMU, secondary controller)
|
||||
*/
|
||||
#define HIGHEST_IRQ_LEVEL (5<<8) /* Disable all but DMA and higher */
|
||||
#define DMA_IRQ_LEVEL (6<<8) /* Disable DMA and lower */
|
||||
#define DISABLE_INTERRUPTS (7<<8) /* Disable all but NMIs */
|
||||
static inline int set_irq_level(int level)
|
||||
{
|
||||
int oldlevel;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue