diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2007-03-07 06:23:02 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2007-03-07 06:23:02 +0000 |
commit | 633f3880024a2f83a21dd96368aa397940bc9ad6 (patch) | |
tree | 7c4319b1715c5c86252fd816764fdf53435163fb | |
parent | 2c94c1afc377c6605772008e6c27b06d579cecf4 (diff) |
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
-rw-r--r-- | apps/plugins/SOURCES | 2 | ||||
-rw-r--r-- | firmware/target/coldfire/iaudio/m5/audio-m5.c | 2 | ||||
-rw-r--r-- | firmware/target/coldfire/iaudio/m5/power-m5.c | 2 | ||||
-rw-r--r-- | firmware/target/coldfire/iaudio/x5/audio-x5.c | 2 | ||||
-rw-r--r-- | firmware/target/coldfire/iaudio/x5/power-x5.c | 2 | ||||
-rw-r--r-- | firmware/target/coldfire/iriver/audio-iriver.c | 4 | ||||
-rw-r--r-- | firmware/target/coldfire/iriver/h100/power-h100.c | 2 | ||||
-rw-r--r-- | firmware/target/coldfire/iriver/h100/spdif-h100.c | 18 | ||||
-rw-r--r-- | firmware/target/coldfire/iriver/h300/power-h300.c | 2 | ||||
-rw-r--r-- | firmware/target/coldfire/pcm-coldfire.c | 134 | ||||
-rw-r--r-- | firmware/target/coldfire/system-coldfire.c | 3 | ||||
-rw-r--r-- | firmware/target/coldfire/system-target.h | 11 |
12 files changed, 148 insertions, 36 deletions
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index 56f845a6dd..49510f5fdf 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES @@ -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 diff --git a/firmware/target/coldfire/iaudio/m5/audio-m5.c b/firmware/target/coldfire/iaudio/m5/audio-m5.c index 9531f932ad..2442351fed 100644 --- a/firmware/target/coldfire/iaudio/m5/audio-m5.c +++ b/firmware/target/coldfire/iaudio/m5/audio-m5.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) diff --git a/firmware/target/coldfire/iaudio/m5/power-m5.c b/firmware/target/coldfire/iaudio/m5/power-m5.c index c689488bfe..f3e7cdd496 100644 --- a/firmware/target/coldfire/iaudio/m5/power-m5.c +++ b/firmware/target/coldfire/iaudio/m5/power-m5.c @@ -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"); } diff --git a/firmware/target/coldfire/iaudio/x5/audio-x5.c b/firmware/target/coldfire/iaudio/x5/audio-x5.c index e673d4ad47..91100ed751 100644 --- a/firmware/target/coldfire/iaudio/x5/audio-x5.c +++ b/firmware/target/coldfire/iaudio/x5/audio-x5.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) diff --git a/firmware/target/coldfire/iaudio/x5/power-x5.c b/firmware/target/coldfire/iaudio/x5/power-x5.c index 4d44500fd5..c646570821 100644 --- a/firmware/target/coldfire/iaudio/x5/power-x5.c +++ b/firmware/target/coldfire/iaudio/x5/power-x5.c @@ -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"); } diff --git a/firmware/target/coldfire/iriver/audio-iriver.c b/firmware/target/coldfire/iriver/audio-iriver.c index 2df7a216a9..bd071430b2 100644 --- a/firmware/target/coldfire/iriver/audio-iriver.c +++ b/firmware/target/coldfire/iriver/audio-iriver.c @@ -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) diff --git a/firmware/target/coldfire/iriver/h100/power-h100.c b/firmware/target/coldfire/iriver/h100/power-h100.c index 75c8bea9dd..ce1c350802 100644 --- a/firmware/target/coldfire/iriver/h100/power-h100.c +++ b/firmware/target/coldfire/iriver/h100/power-h100.c @@ -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) diff --git a/firmware/target/coldfire/iriver/h100/spdif-h100.c b/firmware/target/coldfire/iriver/h100/spdif-h100.c index 35508d10e8..ee4a9402ea 100644 --- a/firmware/target/coldfire/iriver/h100/spdif-h100.c +++ b/firmware/target/coldfire/iriver/h100/spdif-h100.c @@ -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 */ diff --git a/firmware/target/coldfire/iriver/h300/power-h300.c b/firmware/target/coldfire/iriver/h300/power-h300.c index 2875fa2a47..5e57326c36 100644 --- a/firmware/target/coldfire/iriver/h300/power-h300.c +++ b/firmware/target/coldfire/iriver/h300/power-h300.c @@ -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) diff --git a/firmware/target/coldfire/pcm-coldfire.c b/firmware/target/coldfire/pcm-coldfire.c index 2addcb9da8..ef847004ce 100644 --- a/firmware/target/coldfire/pcm-coldfire.c +++ b/firmware/target/coldfire/pcm-coldfire.c @@ -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 */ -void pcm_rec_dma_stop(void) +static void pcm_rec_dma_stop_irq(void) { - logf("pcm_rec_dma_stop"); - DSR1 = 1; /* Clear interrupt */ DCR1 = 0; - pcm_recording = false; or_l(PDIR2_FIFO_RESET, &DATAINCONTROL); -} /* pcm_dma_stop */ + + 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"); + + pcm_rec_dma_stop_irq(); + + 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 */ /** diff --git a/firmware/target/coldfire/system-coldfire.c b/firmware/target/coldfire/system-coldfire.c index 251c97fb73..f2ac075e14 100644 --- a/firmware/target/coldfire/system-coldfire.c +++ b/firmware/target/coldfire/system-coldfire.c @@ -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); } diff --git a/firmware/target/coldfire/system-target.h b/firmware/target/coldfire/system-target.h index 31947a2883..97d096f0e8 100644 --- a/firmware/target/coldfire/system-target.h +++ b/firmware/target/coldfire/system-target.h @@ -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; |