diff options
Diffstat (limited to 'firmware/target/arm/s5l8702/pcm-s5l8702.c')
-rw-r--r-- | firmware/target/arm/s5l8702/pcm-s5l8702.c | 176 |
1 files changed, 170 insertions, 6 deletions
diff --git a/firmware/target/arm/s5l8702/pcm-s5l8702.c b/firmware/target/arm/s5l8702/pcm-s5l8702.c index a4ea56edce..446be24008 100644 --- a/firmware/target/arm/s5l8702/pcm-s5l8702.c +++ b/firmware/target/arm/s5l8702/pcm-s5l8702.c | |||
@@ -250,37 +250,201 @@ void * pcm_dma_addr(void *addr) | |||
250 | ** Recording DMA transfer | 250 | ** Recording DMA transfer |
251 | **/ | 251 | **/ |
252 | #ifdef HAVE_RECORDING | 252 | #ifdef HAVE_RECORDING |
253 | static volatile int rec_locked = 0; | ||
254 | static void *rec_dma_addr; | ||
255 | static size_t rec_dma_size; | ||
256 | static bool pcm_rec_initialized = false; | ||
257 | static int completed_task; | ||
258 | |||
259 | /* ahead capture buffer */ | ||
260 | #define PCM_AHEADBUF_SAMPLES 128 | ||
261 | #define AHEADBUF_SZ (PCM_AHEADBUF_SAMPLES * 4) | ||
262 | static unsigned char ahead_buf[AHEADBUF_SZ] CACHEALIGN_ATTR; | ||
263 | |||
264 | /* DMA configuration */ | ||
265 | static void dma_rec_callback(void *cb_data) ICODE_ATTR; | ||
266 | |||
267 | enum { /* cb_data */ | ||
268 | TASK_AHEADBUF, | ||
269 | TASK_RECBUF | ||
270 | }; | ||
271 | |||
272 | #define DMA_REC_TSKBUF_SZ 2 /* N tasks, MUST be pow2 */ | ||
273 | #define DMA_REC_LLIBUF_SZ 8 /* N LLIs, MUST be pow2 */ | ||
274 | static struct dmac_tsk dma_rec_tskbuf[DMA_REC_TSKBUF_SZ]; | ||
275 | static struct dmac_lli volatile \ | ||
276 | dma_rec_llibuf[DMA_REC_LLIBUF_SZ] CACHEALIGN_ATTR; | ||
277 | |||
278 | static struct dmac_ch dma_rec_ch = { | ||
279 | .dmac = &s5l8702_dmac0, | ||
280 | .prio = DMAC_CH_PRIO(1), | ||
281 | .cb_fn = dma_rec_callback, | ||
282 | |||
283 | .llibuf = dma_rec_llibuf, | ||
284 | .llibuf_mask = DMA_REC_LLIBUF_SZ - 1, | ||
285 | .llibuf_bus = DMAC_MASTER_AHB1, | ||
286 | |||
287 | .tskbuf = dma_rec_tskbuf, | ||
288 | .tskbuf_mask = DMA_REC_TSKBUF_SZ - 1, | ||
289 | .queue_mode = QUEUE_LINK, | ||
290 | }; | ||
291 | |||
292 | static struct dmac_ch_cfg dma_rec_ch_cfg = { | ||
293 | .srcperi = S5L8702_DMAC0_PERI_IIS0_RX, | ||
294 | .dstperi = S5L8702_DMAC0_PERI_MEM, | ||
295 | .sbsize = DMACCxCONTROL_BSIZE_4, | ||
296 | .dbsize = DMACCxCONTROL_BSIZE_4, | ||
297 | .swidth = DMACCxCONTROL_WIDTH_16, | ||
298 | .dwidth = DMACCxCONTROL_WIDTH_16, | ||
299 | .sbus = DMAC_MASTER_AHB1, | ||
300 | .dbus = DMAC_MASTER_AHB1, | ||
301 | .sinc = DMACCxCONTROL_INC_DISABLE, | ||
302 | .dinc = DMACCxCONTROL_INC_ENABLE, | ||
303 | .prot = DMAC_PROT_CACH | DMAC_PROT_BUFF | DMAC_PROT_PRIV, | ||
304 | /* align LLI transfers to L-R pairs (samples) */ | ||
305 | .lli_xfer_max_count = DMAC_LLI_MAX_COUNT & ~1, | ||
306 | }; | ||
307 | |||
308 | /* maximum and minimum supported block sizes in bytes */ | ||
309 | #define MIN_SIZE ((size_t) (AHEADBUF_SZ * 2)) | ||
310 | #define MAX_SIZE ((size_t) (AHEADBUF_SZ + ((DMA_REC_LLIBUF_SZ - 1) * \ | ||
311 | (dma_rec_ch_cfg.lli_xfer_max_count << dma_rec_ch_cfg.swidth)))) | ||
312 | |||
313 | #if 0 | ||
314 | #define SIZE_PANIC(sz) { \ | ||
315 | if (((sz) < MIN_SIZE) || ((sz) > MAX_SIZE)) \ | ||
316 | panicf("pcm record: unsupported size: %d", (sz)); \ | ||
317 | } | ||
318 | #else | ||
319 | #define SIZE_PANIC(sz) {} | ||
320 | #endif | ||
321 | |||
322 | |||
323 | static void rec_dmac_ch_queue(void *addr, size_t size, int cb_data) | ||
324 | { | ||
325 | discard_dcache_range(addr, size); | ||
326 | dmac_ch_queue(&dma_rec_ch, (void*)S5L8702_DADDR_PERI_IIS0_RX, | ||
327 | addr, size, (void *)cb_data); | ||
328 | } | ||
329 | |||
330 | static void dma_rec_callback(void *cb_data) | ||
331 | { | ||
332 | completed_task = (int)cb_data; | ||
333 | |||
334 | if (completed_task == TASK_AHEADBUF) | ||
335 | { | ||
336 | /* safety check */ | ||
337 | if (rec_dma_addr == NULL) | ||
338 | return; /* capture finished */ | ||
339 | |||
340 | /* move ahead buffer to record buffer and queue | ||
341 | next capture-ahead task */ | ||
342 | memcpy(rec_dma_addr, ahead_buf, AHEADBUF_SZ); | ||
343 | rec_dmac_ch_queue(ahead_buf, AHEADBUF_SZ, TASK_AHEADBUF); | ||
344 | } | ||
345 | else /* TASK_RECBUF */ | ||
346 | { | ||
347 | /* Inform middle layer */ | ||
348 | if (pcm_rec_dma_complete_callback( | ||
349 | PCM_DMAST_OK, &rec_dma_addr, &rec_dma_size)) | ||
350 | { | ||
351 | SIZE_PANIC(rec_dma_size); | ||
352 | rec_dmac_ch_queue(rec_dma_addr + AHEADBUF_SZ, | ||
353 | rec_dma_size - AHEADBUF_SZ, TASK_RECBUF); | ||
354 | pcm_rec_dma_status_callback(PCM_DMAST_STARTED); | ||
355 | } | ||
356 | } | ||
357 | } | ||
358 | |||
253 | void pcm_rec_lock(void) | 359 | void pcm_rec_lock(void) |
254 | { | 360 | { |
361 | if ((rec_locked++ == 0) && pcm_rec_initialized) | ||
362 | dmac_ch_lock_int(&dma_rec_ch); | ||
255 | } | 363 | } |
256 | 364 | ||
257 | void pcm_rec_unlock(void) | 365 | void pcm_rec_unlock(void) |
258 | { | 366 | { |
367 | if ((--rec_locked == 0) && pcm_rec_initialized) | ||
368 | dmac_ch_unlock_int(&dma_rec_ch); | ||
259 | } | 369 | } |
260 | 370 | ||
261 | void pcm_rec_dma_stop(void) | 371 | void pcm_rec_dma_stop(void) |
262 | { | 372 | { |
373 | if (!pcm_rec_initialized) | ||
374 | return; | ||
375 | |||
376 | dmac_ch_stop(&dma_rec_ch); | ||
377 | |||
378 | I2SRXCOM = 0x2; /* stop Rx I2S */ | ||
263 | } | 379 | } |
264 | 380 | ||
265 | void pcm_rec_dma_start(void *addr, size_t size) | 381 | void pcm_rec_dma_start(void *addr, size_t size) |
266 | { | 382 | { |
267 | (void)addr; | 383 | SIZE_PANIC(size); |
268 | (void)size; | 384 | |
385 | pcm_rec_dma_stop(); | ||
386 | |||
387 | rec_dma_addr = addr; | ||
388 | rec_dma_size = size; | ||
389 | completed_task = -1; | ||
390 | |||
391 | /* launch first DMA transfer to capture into ahead buffer, | ||
392 | link the second task to capture into record buffer */ | ||
393 | rec_dmac_ch_queue(ahead_buf, AHEADBUF_SZ, TASK_AHEADBUF); | ||
394 | rec_dmac_ch_queue(addr + AHEADBUF_SZ, size - AHEADBUF_SZ, TASK_RECBUF); | ||
395 | |||
396 | I2SRXCOM = 0x6; /* start Rx I2S */ | ||
269 | } | 397 | } |
270 | 398 | ||
271 | void pcm_rec_dma_close(void) | 399 | void pcm_rec_dma_close(void) |
272 | { | 400 | { |
401 | pcm_rec_dma_stop(); | ||
273 | } | 402 | } |
274 | 403 | ||
275 | |||
276 | void pcm_rec_dma_init(void) | 404 | void pcm_rec_dma_init(void) |
277 | { | 405 | { |
278 | } | 406 | if (pcm_rec_initialized) |
407 | return; | ||
279 | 408 | ||
409 | PWRCON(0) &= ~(1 << 4); | ||
410 | PWRCON(0) &= ~(1 << 7); | ||
411 | |||
412 | dmac_ch_init(&dma_rec_ch, &dma_rec_ch_cfg); | ||
413 | |||
414 | /* synchronize lock status */ | ||
415 | if (rec_locked) | ||
416 | dmac_ch_lock_int(&dma_rec_ch); | ||
417 | |||
418 | I2SRXCON = 0x1000; | ||
419 | I2SCLKCON = 1; | ||
420 | |||
421 | pcm_rec_initialized = true; | ||
422 | } | ||
280 | 423 | ||
281 | const void * pcm_rec_dma_get_peak_buffer(void) | 424 | const void * pcm_rec_dma_get_peak_buffer(void) |
282 | { | 425 | { |
283 | return NULL; | 426 | void *dstaddr; |
284 | } | 427 | |
428 | pcm_rec_lock(); | ||
285 | 429 | ||
430 | if (completed_task == TASK_AHEADBUF) { | ||
431 | dstaddr = dmac_ch_get_info(&dma_rec_ch, NULL, NULL); | ||
432 | |||
433 | if ((dstaddr < rec_dma_addr) || | ||
434 | (dstaddr > rec_dma_addr + rec_dma_size)) | ||
435 | /* At this moment, interrupt for TASK_RECBUF is waiting to | ||
436 | be handled. TASK_RECBUF is already finished and HW is | ||
437 | transfering next TASK_AHEADBUF. Return whole block. */ | ||
438 | dstaddr = rec_dma_addr + rec_dma_size; | ||
439 | } | ||
440 | else { | ||
441 | /* Ahead buffer not yet captured _and_ moved to | ||
442 | record buffer. Return nothing. */ | ||
443 | dstaddr = rec_dma_addr; | ||
444 | } | ||
445 | |||
446 | pcm_rec_unlock(); | ||
447 | |||
448 | return CACHEALIGN_DOWN(dstaddr); | ||
449 | } | ||
286 | #endif /* HAVE_RECORDING */ | 450 | #endif /* HAVE_RECORDING */ |