diff options
| author | Michael Sevakis <jethead71@rockbox.org> | 2011-01-07 20:40:36 +0000 |
|---|---|---|
| committer | Michael Sevakis <jethead71@rockbox.org> | 2011-01-07 20:40:36 +0000 |
| commit | 9d97ee1b5401698ede224888028ca64f399fdae1 (patch) | |
| tree | 54aa01c9027866ca81a11e756ab1c77913c459bd | |
| parent | 2093bb021f357913ff9280c7d4e21568e3fc4575 (diff) | |
| download | rockbox-9d97ee1b5401698ede224888028ca64f399fdae1.zip rockbox-9d97ee1b5401698ede224888028ca64f399fdae1.tar.gz rockbox-9d97ee1b5401698ede224888028ca64f399fdae1.tar.bz2 rockbox-9d97ee1b5401698ede224888028ca64f399fdae1.tar.xz | |
Gigabeat S/i.MX31: Take care of an interrupt priority inversion that can happen during PCM callback lockout when DVFS switches frequecies during the lockout, preventing a thread from unlocking the callback until DVFS finishes, causing an SSI FIFO underrun. Hadn't thought of an acceptable way to deal with it before.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28996 a1c6a512-1295-4272-9138-f99709370657
| -rw-r--r-- | firmware/target/arm/imx31/avic-imx31.h | 5 | ||||
| -rw-r--r-- | firmware/target/arm/imx31/dvfs_dptc-imx31.c | 16 | ||||
| -rw-r--r-- | firmware/target/arm/imx31/dvfs_dptc-imx31.h | 1 | ||||
| -rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c | 5 | ||||
| -rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c | 51 | ||||
| -rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/system-target.h | 3 |
6 files changed, 65 insertions, 16 deletions
diff --git a/firmware/target/arm/imx31/avic-imx31.h b/firmware/target/arm/imx31/avic-imx31.h index 04a2856..ca48f85 100644 --- a/firmware/target/arm/imx31/avic-imx31.h +++ b/firmware/target/arm/imx31/avic-imx31.h @@ -67,6 +67,11 @@ void avic_set_int_type(enum IMX31_INT_LIST ints, enum INT_TYPE intstype); #define AVIC_NIL_ENABLE (-1) void avic_set_ni_level(int level); +static inline void avic_mask_int(enum IMX31_INT_LIST ints) + { AVIC_INTDISNUM = ints; } + +static inline void avic_unmask_int(enum IMX31_INT_LIST ints) + { AVIC_INTENNUM = ints; } /* Call a service routine while allowing preemption by interrupts of higher * priority. Avoid using any app or other SVC stack by doing it with a mini diff --git a/firmware/target/arm/imx31/dvfs_dptc-imx31.c b/firmware/target/arm/imx31/dvfs_dptc-imx31.c index aa8d0f5..555e030 100644 --- a/firmware/target/arm/imx31/dvfs_dptc-imx31.c +++ b/firmware/target/arm/imx31/dvfs_dptc-imx31.c @@ -612,6 +612,22 @@ void dvfs_dptc_stop(void) } +/* Mask the DVFS interrupt without affecting running status */ +void dvfs_int_mask(bool mask) +{ + if (mask) + { + /* Just disable, not running = already disabled */ + avic_mask_int(INT_CCM_DVFS); + } + else if (dvfs_running) + { + /* DVFS is running; unmask it */ + avic_unmask_int(INT_CCM_DVFS); + } +} + + /* Set a signal load tracking weight */ void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value) { diff --git a/firmware/target/arm/imx31/dvfs_dptc-imx31.h b/firmware/target/arm/imx31/dvfs_dptc-imx31.h index 844fd6e..6b59bff 100644 --- a/firmware/target/arm/imx31/dvfs_dptc-imx31.h +++ b/firmware/target/arm/imx31/dvfs_dptc-imx31.h @@ -128,6 +128,7 @@ void dvfs_wfi_monitor(bool on); void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value); void dvfs_set_lt_detect(enum DVFS_LT_SIGS index, bool edge); void dvfs_set_gp_bit(enum DVFS_DVGPS dvgp, bool assert); +void dvfs_int_mask(bool mask); unsigned int dvfs_dptc_get_voltage(void); unsigned int dvfs_get_level(void); diff --git a/firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c index 79f3ecc..f040d8f 100644 --- a/firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c @@ -85,3 +85,8 @@ void tick_stop(void) ccm_module_clock_gating(CG_EPIT1, CGM_OFF); /* Turn off module clock */ } + +void kernel_audio_locking(bool locking) +{ + dvfs_int_mask(locking); +}
\ No newline at end of file diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c index 50c7da9..c8c1283 100644 --- a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c @@ -109,20 +109,33 @@ static void play_dma_callback(void) void pcm_play_lock(void) { + /* Need to prevent DVFS from causing interrupt priority inversion if audio + * is locked and a DVFS interrupt fires, blocking reenabling of audio by a + * low-priority mode for at least the duration of the lengthy DVFS routine. + * Not really an issue with state changes but lockout when playing. + * + * Keep direct use of DVFS code away from here though. This could provide + * more services in the future anyway. */ + kernel_audio_locking(true); ++dma_play_data.locked; } void pcm_play_unlock(void) { - if (--dma_play_data.locked == 0 && dma_play_data.state != 0) + if (--dma_play_data.locked == 0) { - int oldstatus = disable_irq_save(); - int pending = dma_play_data.callback_pending; - dma_play_data.callback_pending = 0; - restore_irq(oldstatus); - - if (pending != 0) - play_dma_callback(); + if (dma_play_data.state != 0) + { + int oldstatus = disable_irq_save(); + int pending = dma_play_data.callback_pending; + dma_play_data.callback_pending = 0; + restore_irq(oldstatus); + + if (pending != 0) + play_dma_callback(); + } + + kernel_audio_locking(false); } } @@ -442,20 +455,26 @@ static void rec_dma_callback(void) void pcm_rec_lock(void) { + kernel_audio_locking(true); ++dma_rec_data.locked; } void pcm_rec_unlock(void) { - if (--dma_rec_data.locked == 0 && dma_rec_data.state != 0) + if (--dma_rec_data.locked == 0) { - int oldstatus = disable_irq_save(); - int pending = dma_rec_data.callback_pending; - dma_rec_data.callback_pending = 0; - restore_irq(oldstatus); - - if (pending != 0) - rec_dma_callback(); + if (dma_rec_data.state != 0) + { + int oldstatus = disable_irq_save(); + int pending = dma_rec_data.callback_pending; + dma_rec_data.callback_pending = 0; + restore_irq(oldstatus); + + if (pending != 0) + rec_dma_callback(); + } + + kernel_audio_locking(false); } } diff --git a/firmware/target/arm/imx31/gigabeat-s/system-target.h b/firmware/target/arm/imx31/gigabeat-s/system-target.h index e970109..f3ba719 100644 --- a/firmware/target/arm/imx31/gigabeat-s/system-target.h +++ b/firmware/target/arm/imx31/gigabeat-s/system-target.h @@ -51,6 +51,9 @@ void tick_stop(void); void kernel_device_init(void); void system_halt(void); +/* Handle some audio lockout related tasks */ +void kernel_audio_locking(bool locking); + #define KDEV_INIT struct ARM_REGS { |