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.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.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 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) |
68 | void avic_set_ni_level(int level); | 68 | void avic_set_ni_level(int level); |
69 | 69 | ||
70 | static inline void avic_mask_int(enum IMX31_INT_LIST ints) | ||
71 | { AVIC_INTDISNUM = ints; } | ||
72 | |||
73 | static 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 */ | ||
616 | void 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 */ |
616 | void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value) | 632 | void 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); | |||
128 | void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value); | 128 | void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value); |
129 | void dvfs_set_lt_detect(enum DVFS_LT_SIGS index, bool edge); | 129 | void dvfs_set_lt_detect(enum DVFS_LT_SIGS index, bool edge); |
130 | void dvfs_set_gp_bit(enum DVFS_DVGPS dvgp, bool assert); | 130 | void dvfs_set_gp_bit(enum DVFS_DVGPS dvgp, bool assert); |
131 | void dvfs_int_mask(bool mask); | ||
131 | 132 | ||
132 | unsigned int dvfs_dptc_get_voltage(void); | 133 | unsigned int dvfs_dptc_get_voltage(void); |
133 | unsigned int dvfs_get_level(void); | 134 | 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 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 | |||
89 | void 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 | ||
110 | void pcm_play_lock(void) | 110 | void 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 | ||
115 | void pcm_play_unlock(void) | 123 | void 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 | ||
443 | void pcm_rec_lock(void) | 456 | void 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 | ||
448 | void pcm_rec_unlock(void) | 462 | void 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); | |||
51 | void kernel_device_init(void); | 51 | void kernel_device_init(void); |
52 | void system_halt(void); | 52 | void system_halt(void); |
53 | 53 | ||
54 | /* Handle some audio lockout related tasks */ | ||
55 | void kernel_audio_locking(bool locking); | ||
56 | |||
54 | #define KDEV_INIT | 57 | #define KDEV_INIT |
55 | 58 | ||
56 | struct ARM_REGS { | 59 | struct ARM_REGS { |