diff options
Diffstat (limited to 'firmware/target/arm/s3c2440')
-rw-r--r-- | firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c | 71 |
1 files changed, 39 insertions, 32 deletions
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c index 2b4842f880..45558767d3 100644 --- a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c +++ b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c | |||
@@ -41,13 +41,11 @@ static int sr_ctrl = GIGABEAT_44100HZ; | |||
41 | /* Setup for the DMA controller */ | 41 | /* Setup for the DMA controller */ |
42 | #define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20)) | 42 | #define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20)) |
43 | 43 | ||
44 | unsigned short * p; | ||
45 | size_t p_size; | ||
46 | |||
47 | /* DMA count has hit zero - no more data */ | 44 | /* DMA count has hit zero - no more data */ |
48 | /* Get more data from the callback and top off the FIFO */ | 45 | /* Get more data from the callback and top off the FIFO */ |
49 | //void fiq(void) __attribute__ ((interrupt ("naked"))); | 46 | /* Uses explicitly coded prologue/epilogue code to get around complier bugs |
50 | void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ"))); | 47 | in order to be able to use the stack */ |
48 | void fiq_handler(void) __attribute__((naked)); | ||
51 | 49 | ||
52 | static void _pcm_apply_settings(void) | 50 | static void _pcm_apply_settings(void) |
53 | { | 51 | { |
@@ -98,14 +96,14 @@ void pcm_postinit(void) | |||
98 | 96 | ||
99 | void pcm_play_dma_start(const void *addr, size_t size) | 97 | void pcm_play_dma_start(const void *addr, size_t size) |
100 | { | 98 | { |
99 | addr = (void *)((unsigned long)addr & ~3); /* Align data */ | ||
100 | size &= ~3; /* Size must be multiple of 4 */ | ||
101 | |||
101 | /* sanity check: bad pointer or too small file */ | 102 | /* sanity check: bad pointer or too small file */ |
102 | if (NULL == addr || size <= IIS_FIFO_SIZE) return; | 103 | if (NULL == addr || size <= IIS_FIFO_SIZE) return; |
103 | 104 | ||
104 | disable_fiq(); | 105 | disable_fiq(); |
105 | 106 | ||
106 | p = (unsigned short *)addr; | ||
107 | p_size = size; | ||
108 | |||
109 | /* Enable the IIS clock */ | 107 | /* Enable the IIS clock */ |
110 | CLKCON |= (1<<17); | 108 | CLKCON |= (1<<17); |
111 | 109 | ||
@@ -127,10 +125,10 @@ void pcm_play_dma_start(const void *addr, size_t size) | |||
127 | /* How many transfers to make - we transfer half-word at a time = 2 bytes */ | 125 | /* How many transfers to make - we transfer half-word at a time = 2 bytes */ |
128 | /* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */ | 126 | /* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */ |
129 | /* no auto-reload, half-word (16bit) */ | 127 | /* no auto-reload, half-word (16bit) */ |
130 | DCON2 = DMA_CONTROL_SETUP | (p_size / 2); | 128 | DCON2 = DMA_CONTROL_SETUP | (size / 2); |
131 | 129 | ||
132 | /* set DMA source and options */ | 130 | /* set DMA source and options */ |
133 | DISRC2 = (int)p + 0x30000000; | 131 | DISRC2 = (unsigned long)addr + 0x30000000; |
134 | DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */ | 132 | DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */ |
135 | 133 | ||
136 | /* clear pending DMA interrupt */ | 134 | /* clear pending DMA interrupt */ |
@@ -140,8 +138,6 @@ void pcm_play_dma_start(const void *addr, size_t size) | |||
140 | 138 | ||
141 | _pcm_apply_settings(); | 139 | _pcm_apply_settings(); |
142 | 140 | ||
143 | set_fiq_handler(fiq); | ||
144 | |||
145 | /* unmask the DMA interrupt */ | 141 | /* unmask the DMA interrupt */ |
146 | INTMSK &= ~(1<<19); | 142 | INTMSK &= ~(1<<19); |
147 | 143 | ||
@@ -181,41 +177,52 @@ static void pcm_play_dma_stop_fiq(void) | |||
181 | CLKCON &= ~(1<<17); | 177 | CLKCON &= ~(1<<17); |
182 | } | 178 | } |
183 | 179 | ||
184 | void fiq(void) | 180 | void fiq_handler(void) |
185 | { | 181 | { |
182 | /* r0-r7 are probably not all used by GCC but there's no way to know | ||
183 | otherwise this whole thing must be assembly */ | ||
184 | asm volatile ("stmfd sp!, {r0-r7, ip, lr} \n" /* Store context */ | ||
185 | "sub sp, sp, #8 \n"); /* Reserve stack */ | ||
186 | register pcm_more_callback_type get_more; /* No stack for this */ | ||
187 | unsigned char *next_start; /* sp + #0 */ | ||
188 | size_t next_size; /* sp + #4 */ | ||
189 | |||
186 | /* clear any pending interrupt */ | 190 | /* clear any pending interrupt */ |
187 | SRCPND = (1<<19); | 191 | SRCPND = (1<<19); |
188 | 192 | ||
189 | /* Buffer empty. Try to get more. */ | 193 | /* Buffer empty. Try to get more. */ |
190 | if (pcm_callback_for_more) | 194 | get_more = pcm_callback_for_more; |
191 | { | 195 | if (get_more == NULL) |
192 | pcm_callback_for_more((unsigned char**)&p, &p_size); | ||
193 | } | ||
194 | else | ||
195 | { | 196 | { |
196 | /* callback func is missing? */ | 197 | /* Callback missing */ |
197 | pcm_play_dma_stop_fiq(); | 198 | pcm_play_dma_stop_fiq(); |
198 | return; | 199 | goto fiq_exit; |
199 | } | 200 | } |
200 | 201 | ||
201 | if (p_size) | 202 | next_size = 0; |
202 | { | 203 | get_more(&next_start, &next_size); |
203 | /* Flush any pending cache writes */ | ||
204 | clean_dcache_range(p, p_size); | ||
205 | |||
206 | /* set the new DMA values */ | ||
207 | DCON2 = DMA_CONTROL_SETUP | (p_size >> 1); | ||
208 | DISRC2 = (int)p + 0x30000000; | ||
209 | 204 | ||
210 | /* Re-Activate the channel */ | 205 | if (next_size == 0) |
211 | DMASKTRIG2 = 0x2; | ||
212 | } | ||
213 | else | ||
214 | { | 206 | { |
215 | /* No more DMA to do */ | 207 | /* No more DMA to do */ |
216 | pcm_play_dma_stop_fiq(); | 208 | pcm_play_dma_stop_fiq(); |
209 | goto fiq_exit; | ||
217 | } | 210 | } |
218 | 211 | ||
212 | /* Flush any pending cache writes */ | ||
213 | clean_dcache_range(next_start, next_size); | ||
214 | |||
215 | /* set the new DMA values */ | ||
216 | DCON2 = DMA_CONTROL_SETUP | (next_size >> 1); | ||
217 | DISRC2 = (unsigned long)next_start + 0x30000000; | ||
218 | |||
219 | /* Re-Activate the channel */ | ||
220 | DMASKTRIG2 = 0x2; | ||
221 | |||
222 | fiq_exit: | ||
223 | asm volatile("add sp, sp, #8 \n" /* Cleanup stack */ | ||
224 | "ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */ | ||
225 | "subs pc, lr, #4 \n"); /* Return from FIQ */ | ||
219 | } | 226 | } |
220 | 227 | ||
221 | /* Disconnect the DMA and wait for the FIFO to clear */ | 228 | /* Disconnect the DMA and wait for the FIFO to clear */ |