summaryrefslogtreecommitdiff
path: root/firmware/target/arm/pcm-pp.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/pcm-pp.c')
-rw-r--r--firmware/target/arm/pcm-pp.c245
1 files changed, 204 insertions, 41 deletions
diff --git a/firmware/target/arm/pcm-pp.c b/firmware/target/arm/pcm-pp.c
index 0f87a74d1c..f441bb82ce 100644
--- a/firmware/target/arm/pcm-pp.c
+++ b/firmware/target/arm/pcm-pp.c
@@ -32,6 +32,18 @@
32#ifdef CPU_PP502x 32#ifdef CPU_PP502x
33/* 16-bit, L-R packed into 32 bits with left in the least significant halfword */ 33/* 16-bit, L-R packed into 32 bits with left in the least significant halfword */
34#define SAMPLE_SIZE 16 34#define SAMPLE_SIZE 16
35/* DMA Requests from IIS, Memory to peripheral, single transfer,
36 wait for DMA request, interrupt on complete */
37#define DMA_PLAY_CONFIG ((DMA_REQ_IIS << DMA_CMD_REQ_ID_POS) | \
38 DMA_CMD_RAM_TO_PER | DMA_CMD_SINGLE | \
39 DMA_CMD_WAIT_REQ | DMA_CMD_INTR)
40/* DMA status cannot be viewed from outside code in control because that can
41 * clear the interrupt from outside the handler and prevent the handler from
42 * from being called. Split up transfers to a reasonable size that is good as
43 * a timer, obtaining a keyclick position and peaking yet still keeps the
44 * FIQ count low.
45 */
46#define MAX_DMA_CHUNK_SIZE (pcm_curr_sampr >> 6) /* ~1/256 seconds */
35#else 47#else
36/* 32-bit, one left 32-bit sample followed by one right 32-bit sample */ 48/* 32-bit, one left 32-bit sample followed by one right 32-bit sample */
37#define SAMPLE_SIZE 32 49#define SAMPLE_SIZE 32
@@ -41,11 +53,12 @@ struct dma_data
41{ 53{
42/* NOTE: The order of size and p is important if you use assembler 54/* NOTE: The order of size and p is important if you use assembler
43 optimised fiq handler, so don't change it. */ 55 optimised fiq handler, so don't change it. */
44#if SAMPLE_SIZE == 16 56 union
45 uint32_t *p; 57 {
46#elif SAMPLE_SIZE == 32 58 unsigned long addr;
47 uint16_t *p; 59 uint32_t *p16; /* For packed 16-16 stereo pairs */
48#endif 60 uint16_t *p32; /* For individual samples converted to 32-bit */
61 };
49 size_t size; 62 size_t size;
50#if NUM_CORES > 1 63#if NUM_CORES > 1
51 unsigned core; 64 unsigned core;
@@ -67,15 +80,24 @@ void fiq_handler(void)
67 ); 80 );
68} 81}
69 82
83#ifdef HAVE_PCM_DMA_ADDRESS
84void * pcm_dma_addr(void *addr)
85{
86 if (addr != NULL && (unsigned long)addr < UNCACHED_BASE_ADDR)
87 addr = UNCACHED_ADDR(addr);
88 return addr;
89}
90#endif
91
70/* TODO: Get simultaneous recording and playback to work. Just needs some tweaking */ 92/* TODO: Get simultaneous recording and playback to work. Just needs some tweaking */
71 93
72/**************************************************************************** 94/****************************************************************************
73 ** Playback DMA transfer 95 ** Playback DMA transfer
74 **/ 96 **/
75static struct dma_data dma_play_data SHAREDBSS_ATTR = 97static struct dma_data dma_play_data IBSS_ATTR =
76{ 98{
77 /* Initialize to a locked, stopped state */ 99 /* Initialize to a locked, stopped state */
78 .p = NULL, 100 { .addr = 0 },
79 .size = 0, 101 .size = 0,
80#if NUM_CORES > 1 102#if NUM_CORES > 1
81 .core = 0x00, 103 .core = 0x00,
@@ -89,6 +111,59 @@ void pcm_dma_apply_settings(void)
89 audiohw_set_frequency(pcm_fsel); 111 audiohw_set_frequency(pcm_fsel);
90} 112}
91 113
114#if defined(CPU_PP502x)
115/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
116void ICODE_ATTR __attribute__((interrupt("FIQ"))) fiq_playback(void)
117{
118 register pcm_more_callback_type get_more;
119 register size_t size;
120
121 DMA0_STATUS; /* Clear any pending interrupt */
122
123 size = (DMA0_CMD & 0xffff) + 4; /* Get size of trasfer that caused this
124 interrupt */
125 dma_play_data.addr += size;
126 dma_play_data.size -= size;
127
128 while (1)
129 {
130 if (dma_play_data.size > 0) {
131 size = MAX_DMA_CHUNK_SIZE;
132 /* Not at least MAX_DMA_CHUNK_SIZE left or there would be less
133 * than a FIFO's worth of data after this transfer? */
134 if (size + 16*4 > dma_play_data.size)
135 size = dma_play_data.size;
136
137 /* Set the new DMA values and activate channel */
138 DMA0_RAM_ADDR = dma_play_data.addr;
139 DMA0_CMD = DMA_PLAY_CONFIG | (size - 4) | DMA_CMD_START;
140 return;
141 }
142
143 /* Buffer empty. Try to get more. */
144 get_more = pcm_callback_for_more;
145 if (get_more) {
146 get_more((unsigned char **)&dma_play_data.addr, &dma_play_data.size);
147 dma_play_data.addr = (dma_play_data.addr + 3) & ~3;
148 dma_play_data.size &= 0xfffc;
149 }
150
151 if (dma_play_data.size <= 0) {
152 break;
153 }
154
155 if (dma_play_data.addr < UNCACHED_BASE_ADDR) {
156 /* Flush any pending cache writes */
157 dma_play_data.addr = UNCACHED_ADDR(dma_play_data.addr);
158 cpucache_flush();
159 }
160 }
161
162 /* Callback missing or no more DMA to do */
163 pcm_play_dma_stop();
164 pcm_play_dma_stopped_callback();
165}
166#else
92/* ASM optimised FIQ handler. Checks for the minimum allowed loop cycles by 167/* ASM optimised FIQ handler. Checks for the minimum allowed loop cycles by
93 * evalutation of free IISFIFO-slots against available source buffer words. 168 * evalutation of free IISFIFO-slots against available source buffer words.
94 * Through this it is possible to move the check for IIS_TX_FREE_COUNT outside 169 * Through this it is possible to move the check for IIS_TX_FREE_COUNT outside
@@ -221,10 +296,10 @@ void fiq_playback(void)
221 return; 296 return;
222 } 297 }
223#if SAMPLE_SIZE == 16 298#if SAMPLE_SIZE == 16
224 IISFIFO_WR = *dma_play_data.p++; 299 IISFIFO_WR = *dma_play_data.p16++;
225#elif SAMPLE_SIZE == 32 300#elif SAMPLE_SIZE == 32
226 IISFIFO_WR = *dma_play_data.p++ << 16; 301 IISFIFO_WR = *dma_play_data.p32++ << 16;
227 IISFIFO_WR = *dma_play_data.p++ << 16; 302 IISFIFO_WR = *dma_play_data.p32++ << 16;
228#endif 303#endif
229 dma_play_data.size -= 4; 304 dma_play_data.size -= 4;
230 } 305 }
@@ -232,7 +307,7 @@ void fiq_playback(void)
232 /* p is empty, get some more data */ 307 /* p is empty, get some more data */
233 get_more = pcm_callback_for_more; 308 get_more = pcm_callback_for_more;
234 if (get_more) { 309 if (get_more) {
235 get_more((unsigned char**)&dma_play_data.p, 310 get_more((unsigned char**)&dma_play_data.addr,
236 &dma_play_data.size); 311 &dma_play_data.size);
237 } 312 }
238 } while (dma_play_data.size); 313 } while (dma_play_data.size);
@@ -242,6 +317,7 @@ void fiq_playback(void)
242 pcm_play_dma_stopped_callback(); 317 pcm_play_dma_stopped_callback();
243} 318}
244#endif /* ASM / C selection */ 319#endif /* ASM / C selection */
320#endif /* CPU_PP502x */
245 321
246/* For the locks, FIQ must be disabled because the handler manipulates 322/* For the locks, FIQ must be disabled because the handler manipulates
247 IISCONFIG and the operation is not atomic - dual core support 323 IISCONFIG and the operation is not atomic - dual core support
@@ -251,7 +327,11 @@ void pcm_play_lock(void)
251 int status = disable_fiq_save(); 327 int status = disable_fiq_save();
252 328
253 if (++dma_play_data.locked == 1) { 329 if (++dma_play_data.locked == 1) {
330#ifdef CPU_PP502x
331 CPU_INT_DIS = DMA0_MASK;
332#else
254 IIS_IRQTX_REG &= ~IIS_IRQTX; 333 IIS_IRQTX_REG &= ~IIS_IRQTX;
334#endif
255 } 335 }
256 336
257 restore_fiq(status); 337 restore_fiq(status);
@@ -262,7 +342,11 @@ void pcm_play_unlock(void)
262 int status = disable_fiq_save(); 342 int status = disable_fiq_save();
263 343
264 if (--dma_play_data.locked == 0 && dma_play_data.state != 0) { 344 if (--dma_play_data.locked == 0 && dma_play_data.state != 0) {
345#ifdef CPU_PP502x
346 CPU_INT_EN = DMA0_MASK;
347#else
265 IIS_IRQTX_REG |= IIS_IRQTX; 348 IIS_IRQTX_REG |= IIS_IRQTX;
349#endif
266 } 350 }
267 351
268 restore_fiq(status); 352 restore_fiq(status);
@@ -272,30 +356,71 @@ static void play_start_pcm(void)
272{ 356{
273 fiq_function = fiq_playback; 357 fiq_function = fiq_playback;
274 358
275 IISCONFIG &= ~IIS_TXFIFOEN; /* Stop transmitting */ 359#ifdef CPU_PP502x
360 /* Not at least MAX_DMA_CHUNK_SIZE left or there would be less than a
361 * FIFO's worth of data after this transfer? */
362 size_t size = MAX_DMA_CHUNK_SIZE;
363 if (dma_play_data.size + 16*4 < size)
364 size = dma_play_data.size;
365
366 DMA0_RAM_ADDR = dma_play_data.addr;
367 DMA0_CMD = DMA_PLAY_CONFIG | (size - 4) | DMA_CMD_START;
276 dma_play_data.state = 1; 368 dma_play_data.state = 1;
369#else
370 IISCONFIG &= ~IIS_TXFIFOEN; /* Stop transmitting */
277 371
278 /* Fill the FIFO or start when data is used up */ 372 /* Fill the FIFO or start when data is used up */
279 while (1) { 373 while (1) {
280 if (IIS_TX_FREE_COUNT < 2 || dma_play_data.size == 0) { 374 if (IIS_TX_FREE_COUNT < 2 || dma_play_data.size == 0) {
281 IISCONFIG |= IIS_TXFIFOEN; /* Start transmitting */ 375 IISCONFIG |= IIS_TXFIFOEN; /* Start transmitting */
376 dma_play_data.state = 1;
282 return; 377 return;
283 } 378 }
284 379
285#if SAMPLE_SIZE == 16 380#if SAMPLE_SIZE == 16
286 IISFIFO_WR = *dma_play_data.p++; 381 IISFIFO_WR = *dma_play_data.p16++;
287#elif SAMPLE_SIZE == 32 382#elif SAMPLE_SIZE == 32
288 IISFIFO_WR = *dma_play_data.p++ << 16; 383 IISFIFO_WR = *dma_play_data.p32++ << 16;
289 IISFIFO_WR = *dma_play_data.p++ << 16; 384 IISFIFO_WR = *dma_play_data.p32++ << 16;
290#endif 385#endif
291 dma_play_data.size -= 4; 386 dma_play_data.size -= 4;
292 } 387 }
388#endif
293} 389}
294 390
295static void play_stop_pcm(void) 391static void play_stop_pcm(void)
296{ 392{
393#ifdef CPU_PP502x
394 size_t status = DMA0_STATUS;
395 size_t size;
396
397 /* Stop transfer */
398 DMA0_CMD &= ~(DMA_CMD_START | DMA_CMD_INTR);
399
400 /* Wait for not busy + clear int */
401 while (DMA0_STATUS & (DMA_STATUS_BUSY | DMA_STATUS_INTR));
402
403 size = (status & 0xfffc) + 4;
404
405 if (status & DMA_STATUS_BUSY)
406 {
407 /* Transfer was interrupted - leave what's left */
408 dma_play_data.addr += dma_play_data.size - size;
409 dma_play_data.size = size;
410 }
411 else if (status & DMA_STATUS_INTR)
412 {
413 /* Tranfer was finished - DMA0_STATUS will have been reloaded
414 * automatically with size in DMA0_CMD. */
415 dma_play_data.addr += size;
416 dma_play_data.size -= size;
417 if (dma_play_data.size <= 0)
418 dma_play_data.addr = 0; /* Entire buffer has completed */
419 }
420#else
297 /* Disable TX interrupt */ 421 /* Disable TX interrupt */
298 IIS_IRQTX_REG &= ~IIS_IRQTX; 422 IIS_IRQTX_REG &= ~IIS_IRQTX;
423#endif
299 424
300 /* Wait for FIFO to empty */ 425 /* Wait for FIFO to empty */
301 while (!IIS_TX_IS_EMPTY); 426 while (!IIS_TX_IS_EMPTY);
@@ -305,16 +430,32 @@ static void play_stop_pcm(void)
305 430
306void pcm_play_dma_start(const void *addr, size_t size) 431void pcm_play_dma_start(const void *addr, size_t size)
307{ 432{
308 dma_play_data.p = (void *)(((uintptr_t)addr + 2) & ~3); 433 addr = (void *)(((long)addr + 2) & ~3);
309 dma_play_data.size = (size & ~3); 434 size &= 0xfffc;
310 435
311#if NUM_CORES > 1 436#if NUM_CORES > 1
312 /* This will become more important later - and different ! */ 437 /* This will become more important later - and different ! */
313 dma_play_data.core = processor_id(); /* save initiating core */ 438 dma_play_data.core = processor_id(); /* save initiating core */
314#endif 439#endif
315 440
316 CPU_INT_PRIORITY |= IIS_MASK; /* FIQ priority for I2S */ 441 pcm_play_dma_stop();
317 CPU_INT_EN = IIS_MASK; 442
443 if (size <= 0)
444 return;
445
446#ifdef CPU_PP502x
447 if ((unsigned long)addr < UNCACHED_BASE_ADDR) {
448 /* Flush any pending cache writes */
449 addr = UNCACHED_ADDR(addr);
450 cpucache_flush();
451 }
452
453 dma_play_data.addr = (unsigned long)addr;
454 dma_play_data.size = size;
455 DMA0_PER_ADDR = (unsigned long)&IISFIFO_WR;
456 DMA0_FLAGS = DMA_FLAGS_UNK26;
457 DMA0_INCR = DMA_INCR_RANGE_FIXED | DMA_INCR_WIDTH_32BIT;
458#endif
318 459
319 play_start_pcm(); 460 play_start_pcm();
320} 461}
@@ -323,6 +464,7 @@ void pcm_play_dma_start(const void *addr, size_t size)
323void pcm_play_dma_stop(void) 464void pcm_play_dma_stop(void)
324{ 465{
325 play_stop_pcm(); 466 play_stop_pcm();
467 dma_play_data.addr = 0;
326 dma_play_data.size = 0; 468 dma_play_data.size = 0;
327#if NUM_CORES > 1 469#if NUM_CORES > 1
328 dma_play_data.core = 0; /* no core in control */ 470 dma_play_data.core = 0; /* no core in control */
@@ -345,6 +487,20 @@ size_t pcm_get_bytes_waiting(void)
345 487
346void pcm_play_dma_init(void) 488void pcm_play_dma_init(void)
347{ 489{
490 /* Initialize default register values. */
491 audiohw_init();
492
493#ifdef CPU_PP502x
494 /* Enable DMA controller */
495 DMA_MASTER_CONTROL |= DMA_MASTER_CONTROL_EN;
496 /* FIQ priority for DMA0 */
497 CPU_INT_PRIORITY |= DMA0_MASK;
498 /* Enable request?? Not setting or clearing everything doesn't seem to
499 * prevent it operating. Perhaps important for reliability (how requests
500 * are handled). */
501 DMA_REQ_STATUS |= 1ul << DMA_REQ_IIS;
502 DMA0_STATUS;
503#else
348 /* Set up banked registers for FIQ mode */ 504 /* Set up banked registers for FIQ mode */
349 505
350 /* Use non-banked registers for scratch. */ 506 /* Use non-banked registers for scratch. */
@@ -363,12 +519,9 @@ void pcm_play_dma_init(void)
363 : [iiscfg]"r"(iiscfg), [dmapd]"r"(dmapd) 519 : [iiscfg]"r"(iiscfg), [dmapd]"r"(dmapd)
364 : "r2"); 520 : "r2");
365 521
366 /* Initialize default register values. */ 522 /* FIQ priority for I2S */
367 audiohw_init(); 523 CPU_INT_PRIORITY |= IIS_MASK;
368 524 CPU_INT_EN = IIS_MASK;
369 dma_play_data.size = 0;
370#if NUM_CORES > 1
371 dma_play_data.core = 0; /* no core in control */
372#endif 525#endif
373 526
374 IISCONFIG |= IIS_TXFIFOEN; 527 IISCONFIG |= IIS_TXFIFOEN;
@@ -381,9 +534,14 @@ void pcm_postinit(void)
381 534
382const void * pcm_play_dma_get_peak_buffer(int *count) 535const void * pcm_play_dma_get_peak_buffer(int *count)
383{ 536{
384 unsigned long addr = (unsigned long)dma_play_data.p; 537 unsigned long addr, size;
385 size_t cnt = dma_play_data.size; 538
386 *count = cnt >> 2; 539 int status = disable_fiq_save();
540 addr = dma_play_data.addr;
541 size = dma_play_data.size;
542 restore_fiq(status);
543
544 *count = size >> 2;
387 return (void *)((addr + 2) & ~3); 545 return (void *)((addr + 2) & ~3);
388} 546}
389 547
@@ -392,10 +550,10 @@ const void * pcm_play_dma_get_peak_buffer(int *count)
392 **/ 550 **/
393#ifdef HAVE_RECORDING 551#ifdef HAVE_RECORDING
394/* PCM recording interrupt routine lockout */ 552/* PCM recording interrupt routine lockout */
395static struct dma_data dma_rec_data SHAREDBSS_ATTR = 553static struct dma_data dma_rec_data IBSS_ATTR =
396{ 554{
397 /* Initialize to a locked, stopped state */ 555 /* Initialize to a locked, stopped state */
398 .p = NULL, 556 { .addr = 0 },
399 .size = 0, 557 .size = 0,
400#if NUM_CORES > 1 558#if NUM_CORES > 1
401 .core = 0x00, 559 .core = 0x00,
@@ -447,7 +605,7 @@ void fiq_record(void)
447 value = IISFIFO_RD; 605 value = IISFIFO_RD;
448 IISFIFO_RD; 606 IISFIFO_RD;
449 607
450 *dma_rec_data.p++ = value; 608 *dma_rec_data.p16++ = value;
451 dma_rec_data.size -= 4; 609 dma_rec_data.size -= 4;
452 610
453 /* TODO: Figure out how to do IIS loopback */ 611 /* TODO: Figure out how to do IIS loopback */
@@ -475,7 +633,7 @@ void fiq_record(void)
475 633
476 value = (uint16_t)value | (value << 16); 634 value = (uint16_t)value | (value << 16);
477 635
478 *dma_rec_data.p++ = value; 636 *dma_rec_data.p16++ = value;
479 dma_rec_data.size -= 4; 637 dma_rec_data.size -= 4;
480 638
481 if (audio_output_source != AUDIO_SRC_PLAYBACK) { 639 if (audio_output_source != AUDIO_SRC_PLAYBACK) {
@@ -485,7 +643,7 @@ void fiq_record(void)
485 IISFIFO_WR = 0; 643 IISFIFO_WR = 0;
486 } 644 }
487 645
488 value = *((int32_t *)dma_rec_data.p - 1); 646 value = *((int32_t *)dma_rec_data.p16 - 1);
489 IISFIFO_WR = value; 647 IISFIFO_WR = value;
490 IISFIFO_WR = value; 648 IISFIFO_WR = value;
491 } 649 }
@@ -512,10 +670,10 @@ void fiq_record(void)
512 } 670 }
513 671
514#if SAMPLE_SIZE == 16 672#if SAMPLE_SIZE == 16
515 *dma_rec_data.p++ = IISFIFO_RD; 673 *dma_rec_data.p16++ = IISFIFO_RD;
516#elif SAMPLE_SIZE == 32 674#elif SAMPLE_SIZE == 32
517 *dma_rec_data.p++ = IISFIFO_RD >> 16; 675 *dma_rec_data.p32++ = IISFIFO_RD >> 16;
518 *dma_rec_data.p++ = IISFIFO_RD >> 16; 676 *dma_rec_data.p32++ = IISFIFO_RD >> 16;
519#endif 677#endif
520 dma_rec_data.size -= 4; 678 dma_rec_data.size -= 4;
521 } 679 }
@@ -535,8 +693,8 @@ void fiq_record(void)
535void pcm_record_more(void *start, size_t size) 693void pcm_record_more(void *start, size_t size)
536{ 694{
537 pcm_rec_peak_addr = start; /* Start peaking at dest */ 695 pcm_rec_peak_addr = start; /* Start peaking at dest */
538 dma_rec_data.p = start; /* Start of RX buffer */ 696 dma_rec_data.addr = (unsigned long)start; /* Start of RX buffer */
539 dma_rec_data.size = size; /* Bytes to transfer */ 697 dma_rec_data.size = size; /* Bytes to transfer */
540} 698}
541 699
542void pcm_rec_dma_stop(void) 700void pcm_rec_dma_stop(void)
@@ -560,7 +718,7 @@ void pcm_rec_dma_start(void *addr, size_t size)
560 pcm_rec_dma_stop(); 718 pcm_rec_dma_stop();
561 719
562 pcm_rec_peak_addr = addr; 720 pcm_rec_peak_addr = addr;
563 dma_rec_data.p = addr; 721 dma_rec_data.addr = (unsigned long)addr;
564 dma_rec_data.size = size; 722 dma_rec_data.size = size;
565#if NUM_CORES > 1 723#if NUM_CORES > 1
566 /* This will become more important later - and different ! */ 724 /* This will become more important later - and different ! */
@@ -592,8 +750,13 @@ void pcm_rec_dma_init(void)
592 750
593const void * pcm_rec_dma_get_peak_buffer(int *count) 751const void * pcm_rec_dma_get_peak_buffer(int *count)
594{ 752{
595 unsigned long addr = (unsigned long)pcm_rec_peak_addr; 753 unsigned long addr, end;
596 unsigned long end = (unsigned long)dma_rec_data.p; 754
755 int status = disable_fiq_save();
756 addr = (unsigned long)pcm_rec_peak_addr;
757 end = dma_rec_data.addr;
758 restore_fiq(status);
759
597 *count = (end >> 2) - (addr >> 2); 760 *count = (end >> 2) - (addr >> 2);
598 return (void *)(addr & ~3); 761 return (void *)(addr & ~3);
599} /* pcm_rec_dma_get_peak_buffer */ 762} /* pcm_rec_dma_get_peak_buffer */