summaryrefslogtreecommitdiff
path: root/firmware/target/arm/s5l8702/pcm-s5l8702.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/s5l8702/pcm-s5l8702.c')
-rw-r--r--firmware/target/arm/s5l8702/pcm-s5l8702.c176
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
253static volatile int rec_locked = 0;
254static void *rec_dma_addr;
255static size_t rec_dma_size;
256static bool pcm_rec_initialized = false;
257static int completed_task;
258
259/* ahead capture buffer */
260#define PCM_AHEADBUF_SAMPLES 128
261#define AHEADBUF_SZ (PCM_AHEADBUF_SAMPLES * 4)
262static unsigned char ahead_buf[AHEADBUF_SZ] CACHEALIGN_ATTR;
263
264/* DMA configuration */
265static void dma_rec_callback(void *cb_data) ICODE_ATTR;
266
267enum { /* 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 */
274static struct dmac_tsk dma_rec_tskbuf[DMA_REC_TSKBUF_SZ];
275static struct dmac_lli volatile \
276 dma_rec_llibuf[DMA_REC_LLIBUF_SZ] CACHEALIGN_ATTR;
277
278static 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
292static 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
323static 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
330static 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
253void pcm_rec_lock(void) 359void 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
257void pcm_rec_unlock(void) 365void 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
261void pcm_rec_dma_stop(void) 371void 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
265void pcm_rec_dma_start(void *addr, size_t size) 381void 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
271void pcm_rec_dma_close(void) 399void pcm_rec_dma_close(void)
272{ 400{
401 pcm_rec_dma_stop();
273} 402}
274 403
275
276void pcm_rec_dma_init(void) 404void 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
281const void * pcm_rec_dma_get_peak_buffer(void) 424const 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 */