summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2011-01-07 20:40:36 +0000
committerMichael Sevakis <jethead71@rockbox.org>2011-01-07 20:40:36 +0000
commit9d97ee1b5401698ede224888028ca64f399fdae1 (patch)
tree54aa01c9027866ca81a11e756ab1c77913c459bd
parent2093bb021f357913ff9280c7d4e21568e3fc4575 (diff)
downloadrockbox-9d97ee1b5401698ede224888028ca64f399fdae1.tar.gz
rockbox-9d97ee1b5401698ede224888028ca64f399fdae1.zip
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.h5
-rw-r--r--firmware/target/arm/imx31/dvfs_dptc-imx31.c16
-rw-r--r--firmware/target/arm/imx31/dvfs_dptc-imx31.h1
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c5
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c51
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/system-target.h3
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 04a2856b50..ca48f85ba5 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);
67#define AVIC_NIL_ENABLE (-1) 67#define AVIC_NIL_ENABLE (-1)
68void avic_set_ni_level(int level); 68void avic_set_ni_level(int level);
69 69
70static inline void avic_mask_int(enum IMX31_INT_LIST ints)
71 { AVIC_INTDISNUM = ints; }
72
73static inline void avic_unmask_int(enum IMX31_INT_LIST ints)
74 { AVIC_INTENNUM = ints; }
70 75
71/* Call a service routine while allowing preemption by interrupts of higher 76/* Call a service routine while allowing preemption by interrupts of higher
72 * priority. Avoid using any app or other SVC stack by doing it with a mini 77 * 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 aa8d0f52fb..555e030af5 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)
612} 612}
613 613
614 614
615/* Mask the DVFS interrupt without affecting running status */
616void dvfs_int_mask(bool mask)
617{
618 if (mask)
619 {
620 /* Just disable, not running = already disabled */
621 avic_mask_int(INT_CCM_DVFS);
622 }
623 else if (dvfs_running)
624 {
625 /* DVFS is running; unmask it */
626 avic_unmask_int(INT_CCM_DVFS);
627 }
628}
629
630
615/* Set a signal load tracking weight */ 631/* Set a signal load tracking weight */
616void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value) 632void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value)
617{ 633{
diff --git a/firmware/target/arm/imx31/dvfs_dptc-imx31.h b/firmware/target/arm/imx31/dvfs_dptc-imx31.h
index 844fd6ebff..6b59bffae6 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);
128void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value); 128void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value);
129void dvfs_set_lt_detect(enum DVFS_LT_SIGS index, bool edge); 129void dvfs_set_lt_detect(enum DVFS_LT_SIGS index, bool edge);
130void dvfs_set_gp_bit(enum DVFS_DVGPS dvgp, bool assert); 130void dvfs_set_gp_bit(enum DVFS_DVGPS dvgp, bool assert);
131void dvfs_int_mask(bool mask);
131 132
132unsigned int dvfs_dptc_get_voltage(void); 133unsigned int dvfs_dptc_get_voltage(void);
133unsigned int dvfs_get_level(void); 134unsigned 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 79f3eccc6b..f040d8fcf1 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)
85 ccm_module_clock_gating(CG_EPIT1, CGM_OFF); /* Turn off module clock */ 85 ccm_module_clock_gating(CG_EPIT1, CGM_OFF); /* Turn off module clock */
86} 86}
87 87
88
89void kernel_audio_locking(bool locking)
90{
91 dvfs_int_mask(locking);
92} \ 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 50c7da943e..c8c1283d12 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)
109 109
110void pcm_play_lock(void) 110void pcm_play_lock(void)
111{ 111{
112 /* Need to prevent DVFS from causing interrupt priority inversion if audio
113 * is locked and a DVFS interrupt fires, blocking reenabling of audio by a
114 * low-priority mode for at least the duration of the lengthy DVFS routine.
115 * Not really an issue with state changes but lockout when playing.
116 *
117 * Keep direct use of DVFS code away from here though. This could provide
118 * more services in the future anyway. */
119 kernel_audio_locking(true);
112 ++dma_play_data.locked; 120 ++dma_play_data.locked;
113} 121}
114 122
115void pcm_play_unlock(void) 123void pcm_play_unlock(void)
116{ 124{
117 if (--dma_play_data.locked == 0 && dma_play_data.state != 0) 125 if (--dma_play_data.locked == 0)
118 { 126 {
119 int oldstatus = disable_irq_save(); 127 if (dma_play_data.state != 0)
120 int pending = dma_play_data.callback_pending; 128 {
121 dma_play_data.callback_pending = 0; 129 int oldstatus = disable_irq_save();
122 restore_irq(oldstatus); 130 int pending = dma_play_data.callback_pending;
123 131 dma_play_data.callback_pending = 0;
124 if (pending != 0) 132 restore_irq(oldstatus);
125 play_dma_callback(); 133
134 if (pending != 0)
135 play_dma_callback();
136 }
137
138 kernel_audio_locking(false);
126 } 139 }
127} 140}
128 141
@@ -442,20 +455,26 @@ static void rec_dma_callback(void)
442 455
443void pcm_rec_lock(void) 456void pcm_rec_lock(void)
444{ 457{
458 kernel_audio_locking(true);
445 ++dma_rec_data.locked; 459 ++dma_rec_data.locked;
446} 460}
447 461
448void pcm_rec_unlock(void) 462void pcm_rec_unlock(void)
449{ 463{
450 if (--dma_rec_data.locked == 0 && dma_rec_data.state != 0) 464 if (--dma_rec_data.locked == 0)
451 { 465 {
452 int oldstatus = disable_irq_save(); 466 if (dma_rec_data.state != 0)
453 int pending = dma_rec_data.callback_pending; 467 {
454 dma_rec_data.callback_pending = 0; 468 int oldstatus = disable_irq_save();
455 restore_irq(oldstatus); 469 int pending = dma_rec_data.callback_pending;
456 470 dma_rec_data.callback_pending = 0;
457 if (pending != 0) 471 restore_irq(oldstatus);
458 rec_dma_callback(); 472
473 if (pending != 0)
474 rec_dma_callback();
475 }
476
477 kernel_audio_locking(false);
459 } 478 }
460} 479}
461 480
diff --git a/firmware/target/arm/imx31/gigabeat-s/system-target.h b/firmware/target/arm/imx31/gigabeat-s/system-target.h
index e970109f82..f3ba719443 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);
51void kernel_device_init(void); 51void kernel_device_init(void);
52void system_halt(void); 52void system_halt(void);
53 53
54/* Handle some audio lockout related tasks */
55void kernel_audio_locking(bool locking);
56
54#define KDEV_INIT 57#define KDEV_INIT
55 58
56struct ARM_REGS { 59struct ARM_REGS {