diff options
author | Rafaël Carré <rafael.carre@gmail.com> | 2010-05-13 05:26:12 +0000 |
---|---|---|
committer | Rafaël Carré <rafael.carre@gmail.com> | 2010-05-13 05:26:12 +0000 |
commit | d556f268209e949b92a44e710e9720b3565dac2e (patch) | |
tree | e4c9db8b2ba5d61a016ee1b06002daa4dfe8fd24 /firmware/target/arm/as3525/pcm-as3525.c | |
parent | 9abd82fc0459cc2c617f2bd521275a887ddcb991 (diff) | |
download | rockbox-d556f268209e949b92a44e710e9720b3565dac2e.tar.gz rockbox-d556f268209e949b92a44e710e9720b3565dac2e.zip |
as3525: use DMA for recording
Flyspray: FS#11257
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25980 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/as3525/pcm-as3525.c')
-rw-r--r-- | firmware/target/arm/as3525/pcm-as3525.c | 175 |
1 files changed, 69 insertions, 106 deletions
diff --git a/firmware/target/arm/as3525/pcm-as3525.c b/firmware/target/arm/as3525/pcm-as3525.c index 4d78899398..bdd6a4eca1 100644 --- a/firmware/target/arm/as3525/pcm-as3525.c +++ b/firmware/target/arm/as3525/pcm-as3525.c | |||
@@ -134,18 +134,19 @@ void pcm_postinit(void) | |||
134 | audiohw_postinit(); | 134 | audiohw_postinit(); |
135 | } | 135 | } |
136 | 136 | ||
137 | void pcm_dma_apply_settings(void) | 137 | static unsigned mclk_divider(void) |
138 | { | 138 | { |
139 | unsigned long frequency = pcm_sampr; | ||
140 | |||
141 | /* TODO : use a table ? */ | 139 | /* TODO : use a table ? */ |
142 | const int divider = ((AS3525_MCLK_FREQ/128) + (frequency/2)) / frequency; | 140 | return (((AS3525_MCLK_FREQ/128) + (pcm_sampr/2)) / pcm_sampr) - 1; |
141 | } | ||
143 | 142 | ||
143 | void pcm_dma_apply_settings(void) | ||
144 | { | ||
144 | int cgu_audio = CGU_AUDIO; /* read register */ | 145 | int cgu_audio = CGU_AUDIO; /* read register */ |
145 | cgu_audio &= ~(3 << 0); /* clear i2sout MCLK_SEL */ | 146 | cgu_audio &= ~(3 << 0); /* clear i2sout MCLK_SEL */ |
146 | cgu_audio |= (AS3525_MCLK_SEL << 0); /* set i2sout MCLK_SEL */ | 147 | cgu_audio |= (AS3525_MCLK_SEL << 0); /* set i2sout MCLK_SEL */ |
147 | cgu_audio &= ~(511 << 2); /* clear i2sout divider */ | 148 | cgu_audio &= ~(511 << 2); /* clear i2sout divider */ |
148 | cgu_audio |= (divider - 1) << 2; /* set new i2sout divider */ | 149 | cgu_audio |= mclk_divider() << 2; /* set new i2sout divider */ |
149 | CGU_AUDIO = cgu_audio; /* write back register */ | 150 | CGU_AUDIO = cgu_audio; /* write back register */ |
150 | } | 151 | } |
151 | 152 | ||
@@ -174,166 +175,128 @@ void * pcm_dma_addr(void *addr) | |||
174 | ** Recording DMA transfer | 175 | ** Recording DMA transfer |
175 | **/ | 176 | **/ |
176 | #ifdef HAVE_RECORDING | 177 | #ifdef HAVE_RECORDING |
177 | #define I2SIN_RECORDING_MASK ( I2SIN_MASK_POER | I2SIN_MASK_PUER | \ | ||
178 | I2SIN_MASK_POHF | I2SIN_MASK_POAF | I2SIN_MASK_POF ) | ||
179 | 178 | ||
180 | static int rec_locked = 0; | 179 | static int rec_locked = 0; |
181 | static unsigned int *rec_start_addr; | 180 | static unsigned char *rec_dma_start_addr; |
182 | static size_t rec_size; | 181 | static size_t rec_dma_size; |
182 | static void rec_dma_callback(void); | ||
183 | |||
183 | 184 | ||
184 | void pcm_rec_lock(void) | 185 | void pcm_rec_lock(void) |
185 | { | 186 | { |
186 | if(++rec_locked == 1) { | 187 | if(++rec_locked == 1) |
187 | int vic_state = disable_irq_save(); | 188 | VIC_INT_EN_CLEAR = INTERRUPT_DMAC; |
188 | VIC_INT_EN_CLEAR = INTERRUPT_I2SIN; | ||
189 | I2SIN_MASK = 0; | ||
190 | restore_irq( vic_state ); | ||
191 | } | ||
192 | } | 189 | } |
193 | 190 | ||
191 | |||
194 | void pcm_rec_unlock(void) | 192 | void pcm_rec_unlock(void) |
195 | { | 193 | { |
196 | if(--rec_locked == 0) { | 194 | if(--rec_locked == 0) |
197 | int vic_state = disable_irq_save(); | 195 | VIC_INT_ENABLE = INTERRUPT_DMAC; |
198 | VIC_INT_ENABLE = INTERRUPT_I2SIN; | ||
199 | I2SIN_MASK = I2SIN_RECORDING_MASK; | ||
200 | restore_irq( vic_state ); | ||
201 | } | ||
202 | } | 196 | } |
203 | 197 | ||
204 | 198 | ||
205 | void pcm_rec_dma_record_more(void *start, size_t size) | 199 | static void rec_dma_start(void) |
206 | { | 200 | { |
207 | rec_start_addr = start; | 201 | void* addr = rec_dma_start_addr; |
208 | rec_size = size; | 202 | size_t size = rec_dma_size; |
209 | } | ||
210 | 203 | ||
204 | /* We are limited to 8188 DMA transfers, and the recording core asks for | ||
205 | * 8192 bytes. Avoid splitting 8192 bytes transfers in 8188 + 4 */ | ||
206 | if(size > 4096) | ||
207 | size = 4096; | ||
211 | 208 | ||
212 | void pcm_rec_dma_stop(void) | 209 | rec_dma_size -= size; |
213 | { | 210 | rec_dma_start_addr += size; |
214 | int vic_state = disable_irq_save(); | ||
215 | VIC_INT_EN_CLEAR = INTERRUPT_I2SIN; | ||
216 | I2SIN_MASK = 0; | ||
217 | restore_irq( vic_state ); | ||
218 | 211 | ||
219 | I2SOUT_CONTROL &= ~(1<<5); /* source = i2soutif fifo */ | 212 | dma_enable_channel(1, (void*)I2SIN_DATA, addr, DMA_PERI_I2SIN, |
220 | CGU_AUDIO &= ~((1<<23)|(1<<11)); | 213 | DMAC_FLOWCTRL_DMAC_PERI_TO_MEM, false, true, size >> 2, DMA_S4, |
221 | CGU_PERI &= ~(CGU_I2SIN_APB_CLOCK_ENABLE|CGU_I2SOUT_APB_CLOCK_ENABLE); | 214 | rec_dma_callback); |
222 | } | 215 | } |
223 | 216 | ||
224 | 217 | ||
225 | void INT_I2SIN(void) | 218 | static void rec_dma_callback(void) |
226 | { | 219 | { |
227 | register int status; | 220 | if(!rec_dma_size) |
228 | register pcm_more_callback_type2 more_ready; | ||
229 | |||
230 | status = I2SIN_STATUS; | ||
231 | |||
232 | if ( status & ((1<<6)|(1<<0)) ) /* errors */ | ||
233 | panicf("i2sin error: 0x%x = %s %s", status, | ||
234 | (status & (1<<6)) ? "push" : "", | ||
235 | (status & (1<<0)) ? "pop" : "" | ||
236 | ); | ||
237 | |||
238 | /* called at half full so it's safe to pull 16 FIFO reads in one chunk */ | ||
239 | if( rec_size >= 16*4 ) | ||
240 | { | ||
241 | /* unrolled loop */ | ||
242 | *rec_start_addr++ = *I2SIN_DATA; | ||
243 | *rec_start_addr++ = *I2SIN_DATA; | ||
244 | *rec_start_addr++ = *I2SIN_DATA; | ||
245 | *rec_start_addr++ = *I2SIN_DATA; | ||
246 | |||
247 | *rec_start_addr++ = *I2SIN_DATA; | ||
248 | *rec_start_addr++ = *I2SIN_DATA; | ||
249 | *rec_start_addr++ = *I2SIN_DATA; | ||
250 | *rec_start_addr++ = *I2SIN_DATA; | ||
251 | |||
252 | *rec_start_addr++ = *I2SIN_DATA; | ||
253 | *rec_start_addr++ = *I2SIN_DATA; | ||
254 | *rec_start_addr++ = *I2SIN_DATA; | ||
255 | *rec_start_addr++ = *I2SIN_DATA; | ||
256 | |||
257 | *rec_start_addr++ = *I2SIN_DATA; | ||
258 | *rec_start_addr++ = *I2SIN_DATA; | ||
259 | *rec_start_addr++ = *I2SIN_DATA; | ||
260 | *rec_start_addr++ = *I2SIN_DATA; | ||
261 | |||
262 | rec_size -= 16*4; /* 16x4byte reads */ | ||
263 | } | ||
264 | |||
265 | /* read out any odd samples left */ | ||
266 | while (((I2SIN_RAW_STATUS & (1<<5)) == 0) && rec_size) | ||
267 | { | ||
268 | /* 14 bits per sample = 1 32 bits word */ | ||
269 | *rec_start_addr++ = *I2SIN_DATA; | ||
270 | rec_size -= 4; | ||
271 | } | ||
272 | |||
273 | I2SIN_CLEAR = status; | ||
274 | |||
275 | if(!rec_size) | ||
276 | { | 221 | { |
277 | more_ready = pcm_callback_more_ready; | 222 | register pcm_more_callback_type2 more_ready = pcm_callback_more_ready; |
278 | if(!more_ready || more_ready(0) < 0) | 223 | if (!more_ready || more_ready(0) < 0) |
279 | { | 224 | { |
280 | /* Finished recording */ | 225 | /* Finished recording */ |
281 | pcm_rec_dma_stop(); | 226 | pcm_rec_dma_stop(); |
282 | pcm_rec_dma_stopped_callback(); | 227 | pcm_rec_dma_stopped_callback(); |
228 | return; | ||
283 | } | 229 | } |
284 | } | 230 | } |
231 | |||
232 | rec_dma_start(); | ||
233 | } | ||
234 | |||
235 | |||
236 | void pcm_rec_dma_record_more(void *start, size_t size) | ||
237 | { | ||
238 | dump_dcache_range(start, size); | ||
239 | rec_dma_start_addr = start; | ||
240 | rec_dma_size = size; | ||
241 | } | ||
242 | |||
243 | |||
244 | void pcm_rec_dma_stop(void) | ||
245 | { | ||
246 | dma_disable_channel(1); | ||
247 | rec_dma_size = 0; | ||
248 | dma_release(); | ||
249 | |||
250 | I2SOUT_CONTROL &= ~(1<<5); /* source = i2soutif fifo */ | ||
251 | I2SIN_CONTROL &= ~(1<<11); /* disable dma */ | ||
252 | |||
253 | CGU_AUDIO &= ~((1<<23)|(1<<11)); | ||
254 | CGU_PERI &= ~(CGU_I2SIN_APB_CLOCK_ENABLE|CGU_I2SOUT_APB_CLOCK_ENABLE); | ||
285 | } | 255 | } |
286 | 256 | ||
287 | 257 | ||
288 | void pcm_rec_dma_start(void *addr, size_t size) | 258 | void pcm_rec_dma_start(void *addr, size_t size) |
289 | { | 259 | { |
290 | rec_start_addr = addr; | 260 | dump_dcache_range(addr, size); |
291 | rec_size = size; | 261 | rec_dma_start_addr = addr; |
262 | rec_dma_size = size; | ||
263 | |||
264 | dma_retain(); | ||
292 | 265 | ||
293 | CGU_PERI |= CGU_I2SIN_APB_CLOCK_ENABLE|CGU_I2SOUT_APB_CLOCK_ENABLE; | 266 | CGU_PERI |= CGU_I2SIN_APB_CLOCK_ENABLE|CGU_I2SOUT_APB_CLOCK_ENABLE; |
294 | CGU_AUDIO |= ((1<<23)|(1<<11)); | 267 | CGU_AUDIO |= ((1<<23)|(1<<11)); |
295 | 268 | ||
296 | I2SOUT_CONTROL |= 1<<5; /* source = loopback from i2sin fifo */ | 269 | I2SOUT_CONTROL |= 1<<5; /* source = loopback from i2sin fifo */ |
297 | 270 | ||
298 | /* 14 bits samples, i2c clk src = I2SOUTIF, sdata src = AFE, | 271 | I2SIN_CONTROL |= (1<<11)|(1<<5); /* enable dma, 14bits samples */ |
299 | * data valid at positive edge of SCLK */ | ||
300 | I2SIN_CONTROL = (1<<5) | (1<<2); | ||
301 | 272 | ||
302 | unsigned long tmp; | 273 | rec_dma_start(); |
303 | while ( ( I2SIN_RAW_STATUS & ( 1<<5 ) ) == 0 ) | ||
304 | tmp = *I2SIN_DATA; /* FLUSH FIFO */ | ||
305 | I2SIN_CLEAR = (1<<6)|(1<<0); /* push error, pop error */ | ||
306 | I2SIN_MASK = I2SIN_RECORDING_MASK; | ||
307 | |||
308 | VIC_INT_ENABLE = INTERRUPT_I2SIN; | ||
309 | } | 274 | } |
310 | 275 | ||
311 | 276 | ||
312 | void pcm_rec_dma_close(void) | 277 | void pcm_rec_dma_close(void) |
313 | { | 278 | { |
314 | pcm_rec_dma_stop(); | ||
315 | } | 279 | } |
316 | 280 | ||
317 | 281 | ||
318 | void pcm_rec_dma_init(void) | 282 | void pcm_rec_dma_init(void) |
319 | { | 283 | { |
320 | unsigned long frequency = pcm_sampr; | ||
321 | |||
322 | /* TODO : use a table ? */ | ||
323 | const int divider = ((AS3525_MCLK_FREQ/128) + (frequency/2)) / frequency; | ||
324 | |||
325 | int cgu_audio = CGU_AUDIO; /* read register */ | 284 | int cgu_audio = CGU_AUDIO; /* read register */ |
326 | cgu_audio &= ~(3 << 12); /* clear i2sin MCLK_SEL */ | 285 | cgu_audio &= ~(3 << 12); /* clear i2sin MCLK_SEL */ |
327 | cgu_audio |= (AS3525_MCLK_SEL << 12); /* set i2sin MCLK_SEL */ | 286 | cgu_audio |= (AS3525_MCLK_SEL << 12); /* set i2sin MCLK_SEL */ |
328 | cgu_audio &= ~(511 << 14); /* clear i2sin divider */ | 287 | cgu_audio &= ~(511 << 14); /* clear i2sin divider */ |
329 | cgu_audio |= (divider - 1) << 14; /* set new i2sin divider */ | 288 | cgu_audio |= mclk_divider() << 14; /* set new i2sin divider */ |
330 | CGU_AUDIO = cgu_audio; /* write back register */ | 289 | CGU_AUDIO = cgu_audio; /* write back register */ |
290 | |||
291 | /* i2c clk src = I2SOUTIF, sdata src = AFE, | ||
292 | * data valid at positive edge of SCLK */ | ||
293 | I2SIN_CONTROL = (1<<2); | ||
331 | } | 294 | } |
332 | 295 | ||
333 | 296 | ||
334 | const void * pcm_rec_dma_get_peak_buffer(void) | 297 | const void * pcm_rec_dma_get_peak_buffer(void) |
335 | { | 298 | { |
336 | return (const void*)rec_start_addr; | 299 | return rec_dma_start_addr; |
337 | } | 300 | } |
338 | 301 | ||
339 | #endif /* HAVE_RECORDING */ | 302 | #endif /* HAVE_RECORDING */ |