summaryrefslogtreecommitdiff
path: root/firmware/target/arm
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2012-02-23 08:14:46 -0500
committerMichael Sevakis <jethead71@rockbox.org>2012-03-03 07:23:38 +0100
commit286a4c5caa1945c8d1cb365a3d90fb09d5700cb2 (patch)
tree4835f46d16ec78d035ec9f49333079fe618384c1 /firmware/target/arm
parent3f82f3aca14eb954e55f761721ffdd2684f0e812 (diff)
downloadrockbox-286a4c5caa1945c8d1cb365a3d90fb09d5700cb2.tar.gz
rockbox-286a4c5caa1945c8d1cb365a3d90fb09d5700cb2.zip
Revise the PCM callback system after adding multichannel audio.
Additional status callback is added to pcm_play/rec_data instead of using a special function to set it. Status includes DMA error reporting to the status callback. Playback and recording callback become more alike except playback uses "const void **addr" (because the data should not be altered) and recording uses "void **addr". "const" is put in place throughout where appropriate. Most changes are fairly trivial. One that should be checked in particular because it isn't so much is telechips, if anyone cares to bother. PP5002 is not so trivial either but that tested as working. Change-Id: I4928d69b3b3be7fb93e259f81635232df9bd1df2 Reviewed-on: http://gerrit.rockbox.org/166 Reviewed-by: Michael Sevakis <jethead71@rockbox.org> Tested-by: Michael Sevakis <jethead71@rockbox.org>
Diffstat (limited to 'firmware/target/arm')
-rw-r--r--firmware/target/arm/as3525/pcm-as3525.c20
-rw-r--r--firmware/target/arm/imx233/pcm-imx233.c9
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c119
-rw-r--r--firmware/target/arm/pcm-telechips.c62
-rw-r--r--firmware/target/arm/pnx0101/pcm-pnx0101.c14
-rw-r--r--firmware/target/arm/pp/pcm-pp.c627
-rw-r--r--firmware/target/arm/rk27xx/pcm-rk27xx.c8
-rw-r--r--firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c8
-rw-r--r--firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c8
-rw-r--r--firmware/target/arm/s5l8700/pcm-s5l8700.c9
-rw-r--r--firmware/target/arm/s5l8702/pcm-s5l8702.c4
-rw-r--r--firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c8
-rw-r--r--firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c8
13 files changed, 448 insertions, 456 deletions
diff --git a/firmware/target/arm/as3525/pcm-as3525.c b/firmware/target/arm/as3525/pcm-as3525.c
index 0ecc63d018..eb22fc2016 100644
--- a/firmware/target/arm/as3525/pcm-as3525.c
+++ b/firmware/target/arm/as3525/pcm-as3525.c
@@ -36,9 +36,9 @@
36 * and the number of 32bits words has to 36 * and the number of 32bits words has to
37 * fit in 11 bits of DMA register */ 37 * fit in 11 bits of DMA register */
38 38
39static void *dma_start_addr; /* Pointer to callback buffer */ 39static const void *dma_start_addr; /* Pointer to callback buffer */
40static size_t dma_start_size; /* Size of callback buffer */ 40static size_t dma_start_size; /* Size of callback buffer */
41static void *dma_sub_addr; /* Pointer to sub buffer */ 41static const void *dma_sub_addr; /* Pointer to sub buffer */
42static size_t dma_rem_size; /* Remaining size - in 4*32 bits */ 42static size_t dma_rem_size; /* Remaining size - in 4*32 bits */
43static size_t play_sub_size; /* size of current subtransfer */ 43static size_t play_sub_size; /* size of current subtransfer */
44static void dma_callback(void); 44static void dma_callback(void);
@@ -100,9 +100,8 @@ static void dma_callback(void)
100 100
101 if(!dma_rem_size) 101 if(!dma_rem_size)
102 { 102 {
103 pcm_play_get_more_callback(&dma_start_addr, &dma_start_size); 103 if(!pcm_play_dma_complete_callback(PCM_DMAST_OK, &dma_start_addr,
104 104 &dma_start_size))
105 if (!dma_start_size)
106 return; 105 return;
107 106
108 dma_sub_addr = dma_start_addr; 107 dma_sub_addr = dma_start_addr;
@@ -111,7 +110,7 @@ static void dma_callback(void)
111 /* force writeback */ 110 /* force writeback */
112 commit_dcache_range(dma_start_addr, dma_start_size); 111 commit_dcache_range(dma_start_addr, dma_start_size);
113 play_start_pcm(); 112 play_start_pcm();
114 pcm_play_dma_started_callback(); 113 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
115 } 114 }
116 else 115 else
117 { 116 {
@@ -123,7 +122,7 @@ void pcm_play_dma_start(const void *addr, size_t size)
123{ 122{
124 is_playing = true; 123 is_playing = true;
125 124
126 dma_start_addr = (void*)addr; 125 dma_start_addr = addr;
127 dma_start_size = size; 126 dma_start_size = size;
128 dma_sub_addr = dma_start_addr; 127 dma_sub_addr = dma_start_addr;
129 dma_rem_size = size; 128 dma_rem_size = size;
@@ -368,7 +367,12 @@ void INT_I2SIN(void)
368 } 367 }
369 } 368 }
370 369
371 pcm_rec_more_ready_callback(0, (void *)&rec_dma_addr, &rec_dma_size); 370 /* Inform middle layer */
371 if (pcm_rec_dma_complete_callback(PCM_DMAST_OK, (void **)&rec_dma_addr,
372 &rec_dma_size))
373 {
374 pcm_rec_dma_status_callback(PCM_DMAST_STARTED);
375 }
372} 376}
373 377
374 378
diff --git a/firmware/target/arm/imx233/pcm-imx233.c b/firmware/target/arm/imx233/pcm-imx233.c
index c8b79b3875..c4c512eed6 100644
--- a/firmware/target/arm/imx233/pcm-imx233.c
+++ b/firmware/target/arm/imx233/pcm-imx233.c
@@ -49,15 +49,13 @@ static void play(const void *addr, size_t size)
49 49
50void INT_DAC_DMA(void) 50void INT_DAC_DMA(void)
51{ 51{
52 void *start; 52 const void *start;
53 size_t size; 53 size_t size;
54 54
55 pcm_play_get_more_callback(&start, &size); 55 if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size))
56
57 if(size != 0)
58 { 56 {
59 play(start, size); 57 play(start, size);
60 pcm_play_dma_started_callback(); 58 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
61 } 59 }
62 60
63 imx233_dma_clear_channel_interrupt(APB_AUDIO_DAC); 61 imx233_dma_clear_channel_interrupt(APB_AUDIO_DAC);
@@ -65,6 +63,7 @@ void INT_DAC_DMA(void)
65 63
66void INT_DAC_ERROR(void) 64void INT_DAC_ERROR(void)
67{ 65{
66 /* TODO: Inform of error through pcm_play_dma_complete_callback */
68} 67}
69 68
70void pcm_play_lock(void) 69void pcm_play_lock(void)
diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c
index e106cf78e3..c26349b72e 100644
--- a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c
@@ -78,12 +78,20 @@ static struct dma_data dma_play_data =
78 .state = 0 78 .state = 0
79}; 79};
80 80
81static void play_dma_callback(void) 81static void play_start_dma(const void *addr, size_t size)
82{ 82{
83 void *start; 83 commit_dcache_range(addr, size);
84 size_t size; 84
85 bool rror; 85 dma_play_bd.buf_addr = (void *)addr_virt_to_phys((unsigned long)addr);
86 dma_play_bd.mode.count = size;
87 dma_play_bd.mode.command = TRANSFER_16BIT;
88 dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
89
90 sdma_channel_run(DMA_PLAY_CH_NUM);
91}
86 92
93static void play_dma_callback(void)
94{
87 if (dma_play_data.locked != 0) 95 if (dma_play_data.locked != 0)
88 { 96 {
89 /* Callback is locked out */ 97 /* Callback is locked out */
@@ -91,22 +99,17 @@ static void play_dma_callback(void)
91 return; 99 return;
92 } 100 }
93 101
94 rror = dma_play_bd.mode.status & BD_RROR; 102 /* Inform of status and get new buffer */
95 103 enum pcm_dma_status status = (dma_play_bd.mode.status & BD_RROR) ?
96 pcm_play_get_more_callback(rror ? NULL : &start, &size); 104 PCM_DMAST_ERR_DMA : PCM_DMAST_OK;
97 105 const void *addr;
98 if (size == 0) 106 size_t size;
99 return; 107
100 108 if (pcm_play_dma_complete_callback(status, &addr, &size))
101 /* Flush any pending cache writes */ 109 {
102 commit_dcache_range(start, size); 110 play_start_dma(addr, size);
103 dma_play_bd.buf_addr = (void *)addr_virt_to_phys((unsigned long)start); 111 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
104 dma_play_bd.mode.count = size; 112 }
105 dma_play_bd.mode.command = TRANSFER_16BIT;
106 dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
107 sdma_channel_run(DMA_PLAY_CH_NUM);
108
109 pcm_play_dma_started_callback();
110} 113}
111 114
112void pcm_play_lock(void) 115void pcm_play_lock(void)
@@ -221,15 +224,11 @@ void pcm_play_dma_start(const void *addr, size_t size)
221 if (!sdma_channel_reset(DMA_PLAY_CH_NUM)) 224 if (!sdma_channel_reset(DMA_PLAY_CH_NUM))
222 return; 225 return;
223 226
224 commit_dcache_range(addr, size); 227 /* Begin I2S transmission */
225 dma_play_bd.buf_addr =
226 (void *)addr_virt_to_phys((unsigned long)(void *)addr);
227 dma_play_bd.mode.count = size;
228 dma_play_bd.mode.command = TRANSFER_16BIT;
229 dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
230
231 play_start_pcm(); 228 play_start_pcm();
232 sdma_channel_run(DMA_PLAY_CH_NUM); 229
230 /* Begin DMA transfer */
231 play_start_dma(addr, size);
233} 232}
234 233
235void pcm_play_dma_stop(void) 234void pcm_play_dma_stop(void)
@@ -332,37 +331,39 @@ static struct dma_data dma_rec_data =
332 .state = 0 331 .state = 0
333}; 332};
334 333
335static void rec_dma_callback(void) 334static void rec_start_dma(void *addr, size_t size)
336{ 335{
337 int status = 0; 336 discard_dcache_range(addr, size);
338 void *start; 337
339 size_t size; 338 addr = (void *)addr_virt_to_phys((unsigned long)addr);
340 339
340 dma_rec_bd.buf_addr = addr;
341 dma_rec_bd.mode.count = size;
342 dma_rec_bd.mode.command = TRANSFER_16BIT;
343 dma_rec_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
344
345 sdma_channel_run(DMA_REC_CH_NUM);
346}
347
348static void rec_dma_callback(void)
349{
341 if (dma_rec_data.locked != 0) 350 if (dma_rec_data.locked != 0)
342 { 351 {
343 dma_rec_data.callback_pending = dma_rec_data.state; 352 dma_rec_data.callback_pending = dma_rec_data.state;
344 return; /* Callback is locked out */ 353 return; /* Callback is locked out */
345 } 354 }
346 355
347 if (dma_rec_bd.mode.status & BD_RROR) 356 /* Inform middle layer */
348 status = DMA_REC_ERROR_DMA; 357 enum pcm_dma_status status = (dma_rec_bd.mode.status & BD_RROR) ?
349 358 PCM_DMAST_ERR_DMA : PCM_DMAST_OK;
350 pcm_rec_more_ready_callback(status, &start, &size); 359 void *addr;
351 360 size_t size;
352 if (size == 0)
353 return;
354
355 /* Invalidate - buffer must be coherent */
356 discard_dcache_range(start, size);
357
358 start = (void *)addr_virt_to_phys((unsigned long)start);
359
360 dma_rec_bd.buf_addr = start;
361 dma_rec_bd.mode.count = size;
362 dma_rec_bd.mode.command = TRANSFER_16BIT;
363 dma_rec_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
364 361
365 sdma_channel_run(DMA_REC_CH_NUM); 362 if (pcm_rec_dma_complete_callback(status, &addr, &size))
363 {
364 rec_start_dma(addr, size);
365 pcm_rec_dma_status_callback(PCM_DMAST_STARTED);
366 }
366} 367}
367 368
368void pcm_rec_lock(void) 369void pcm_rec_lock(void)
@@ -426,29 +427,21 @@ void pcm_rec_dma_start(void *addr, size_t size)
426 427
427 if (!sdma_channel_reset(DMA_REC_CH_NUM)) 428 if (!sdma_channel_reset(DMA_REC_CH_NUM))
428 return; 429 return;
429
430 /* Invalidate - buffer must be coherent */
431 discard_dcache_range(addr, size);
432 430
433 addr = (void *)addr_virt_to_phys((unsigned long)addr); 431 /* Ensure clear FIFO */
434 dma_rec_bd.buf_addr = addr; 432 while (SSI_SFCSR1 & SSI_SFCSR_RFCNT0)
435 dma_rec_bd.mode.count = size; 433 SSI_SRX0_1;
436 dma_rec_bd.mode.command = TRANSFER_16BIT;
437 dma_rec_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR;
438 434
439 dma_rec_data.state = 1; /* Check callback on unlock */ 435 dma_rec_data.state = 1; /* Check callback on unlock */
440 436
441 SSI_SRCR1 |= SSI_SRCR_RFEN0; /* Enable RX FIFO */ 437 SSI_SRCR1 |= SSI_SRCR_RFEN0; /* Enable RX FIFO */
442 438
443 /* Ensure clear FIFO */
444 while (SSI_SFCSR1 & SSI_SFCSR_RFCNT0)
445 SSI_SRX0_1;
446
447 /* Enable receive */ 439 /* Enable receive */
448 SSI_SCR1 |= SSI_SCR_RE; 440 SSI_SCR1 |= SSI_SCR_RE;
449 SSI_SIER1 |= SSI_SIER_RDMAE; /* Enable DMA req. */ 441 SSI_SIER1 |= SSI_SIER_RDMAE; /* Enable DMA req. */
450 442
451 sdma_channel_run(DMA_REC_CH_NUM); 443 /* Begin DMA transfer */
444 rec_start_dma(addr, size);
452} 445}
453 446
454void pcm_rec_dma_close(void) 447void pcm_rec_dma_close(void)
diff --git a/firmware/target/arm/pcm-telechips.c b/firmware/target/arm/pcm-telechips.c
index ae4aa5ef38..3d62fcd1a9 100644
--- a/firmware/target/arm/pcm-telechips.c
+++ b/firmware/target/arm/pcm-telechips.c
@@ -33,7 +33,12 @@ struct dma_data
33{ 33{
34/* NOTE: The order of size and p is important if you use assembler 34/* NOTE: The order of size and p is important if you use assembler
35 optimised fiq handler, so don't change it. */ 35 optimised fiq handler, so don't change it. */
36 uint16_t *p; 36 union
37 {
38 uint16_t *p;
39 const void *p_r;
40 void *p_w;
41 };
37 size_t size; 42 size_t size;
38#if NUM_CORES > 1 43#if NUM_CORES > 1
39 unsigned core; 44 unsigned core;
@@ -143,7 +148,7 @@ static void play_stop_pcm(void)
143 148
144void pcm_play_dma_start(const void *addr, size_t size) 149void pcm_play_dma_start(const void *addr, size_t size)
145{ 150{
146 dma_play_data.p = (uint16_t*)addr; 151 dma_play_data.p_r = addr;
147 dma_play_data.size = size; 152 dma_play_data.size = size;
148 153
149#if NUM_CORES > 1 154#if NUM_CORES > 1
@@ -248,8 +253,9 @@ void fiq_handler(void)
248 * r0-r3 and r12 is a working register. 253 * r0-r3 and r12 is a working register.
249 */ 254 */
250 asm volatile ( 255 asm volatile (
251 "stmfd sp!, { r0-r4, lr } \n" /* stack scratch regs and lr */ 256 "sub lr, lr, #4 \n"
252 "mov r4, #0 \n" /* Was the callback called? */ 257 "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */
258 "mov r14, #0 \n" /* Was the callback called? */
253#if defined(CPU_TCC780X) 259#if defined(CPU_TCC780X)
254 "mov r8, #0xc000 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */ 260 "mov r8, #0xc000 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */
255 "ldr r9, =0xf3001004 \n" /* CREQ */ 261 "ldr r9, =0xf3001004 \n" /* CREQ */
@@ -260,7 +266,7 @@ void fiq_handler(void)
260 "str r8, [r9] \n" /* clear DAI IRQs */ 266 "str r8, [r9] \n" /* clear DAI IRQs */
261 "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */ 267 "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */
262 "cmp r9, #0x10 \n" /* is size <16? */ 268 "cmp r9, #0x10 \n" /* is size <16? */
263 "blt .more_data \n" /* if so, ask pcmbuf for more data */ 269 "blo .more_data \n" /* if so, ask pcmbuf for more data */
264 270
265 ".fill_fifo: \n" 271 ".fill_fifo: \n"
266 "ldr r12, [r8], #4 \n" /* load two samples */ 272 "ldr r12, [r8], #4 \n" /* load two samples */
@@ -282,29 +288,30 @@ void fiq_handler(void)
282 "sub r9, r9, #0x10 \n" /* 4 words written */ 288 "sub r9, r9, #0x10 \n" /* 4 words written */
283 "stmia r11, { r8-r9 } \n" /* save p and size */ 289 "stmia r11, { r8-r9 } \n" /* save p and size */
284 290
285 "cmp r4, #0 \n" /* Callback called? */ 291 "cmp r14, #0 \n" /* Callback called? */
286 "beq .exit \n" 292 "ldmeqfd sp!, { r0-r3, pc }^ \n" /* no? -> exit */
287 /* "mov r4, #0 \n" If get_more could be called multiple times! */
288 "ldr r2, =pcm_play_dma_started\n"
289 "ldr r2, [r2] \n"
290 "cmp r2, #0 \n"
291 "blxne r2 \n"
292 293
293 ".exit: \n" 294 "ldr r1, =pcm_play_status_callback \n"
294 "ldmfd sp!, { r0-r4, lr } \n" 295 "ldr r1, [r1] \n"
295 "subs pc, lr, #4 \n" /* FIQ specific return sequence */ 296 "cmp r1, #0 \n"
297 "movne r0, %1 \n"
298 "blxne r1 \n"
299 "ldmfd sp!, { r0-r3, pc }^ \n" /* exit */
296 300
297 ".more_data: \n" 301 ".more_data: \n"
298 "mov r4, #1 \n" /* Remember we got more data in this FIQ */ 302 "mov r14, #1 \n" /* Remember we got more data in this FIQ */
299 "ldr r2, =pcm_play_get_more_callback \n" 303 "mov r0, %0 \n" /* r0 = status */
300 "mov r0, r11 \n" /* r0 = &p */ 304 "mov r1, r11 \n" /* r1 = &dma_play_data.p_r */
301 "add r1, r11, #4 \n" /* r1 = &size */ 305 "add r2, r11, #4 \n" /* r2 = &dma_play_data.size */
302 "blx r2 \n" /* call pcm_play_get_more_callback */ 306 "mov lr, pc \n"
303 "ldmia r11, { r8-r9 } \n" /* load new p and size */ 307 "ldr pc, =pcm_play_dma_complete_callback \n"
304 "cmp r9, #0x10 \n" /* did we actually get enough data? */ 308 "cmp r0, #0 \n" /* any more to play? */
305 "bpl .fill_fifo \n" /* not stop and enough? refill */ 309 "ldmneia r11, { r8-r9 } \n" /* load new p and size */
306 "b .exit \n" 310 "cmpne r9, #0x0f \n" /* did we actually get enough data? */
311 "bhi .fill_fifo \n" /* not stop and enough? refill */
312 "ldmfd sp!, { r0-r3, pc }^ \n" /* exit */
307 ".ltorg \n" 313 ".ltorg \n"
314 : : "i"(PCM_DMAST_OK), "i"(PCM_DMAST_STARTED)
308 ); 315 );
309} 316}
310#else /* C version for reference */ 317#else /* C version for reference */
@@ -316,9 +323,8 @@ void fiq_handler(void)
316 if (dma_play_data.size < 16) 323 if (dma_play_data.size < 16)
317 { 324 {
318 /* p is empty, get some more data */ 325 /* p is empty, get some more data */
319 new_buffer = true; 326 new_buffer = pcm_play_dma_complete_callback(&dma_play_data.p_r,
320 pcm_play_get_more_callback((void**)&dma_play_data.p, 327 &dma_play_data.size);
321 &dma_play_data.size);
322 } 328 }
323 329
324 if (dma_play_data.size >= 16) 330 if (dma_play_data.size >= 16)
@@ -339,7 +345,7 @@ void fiq_handler(void)
339 CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK; 345 CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK;
340 346
341 if (new_buffer) 347 if (new_buffer)
342 pcm_play_dma_started_callback(); 348 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
343} 349}
344#endif 350#endif
345 351
diff --git a/firmware/target/arm/pnx0101/pcm-pnx0101.c b/firmware/target/arm/pnx0101/pcm-pnx0101.c
index 89d56af374..bb11ad32fe 100644
--- a/firmware/target/arm/pnx0101/pcm-pnx0101.c
+++ b/firmware/target/arm/pnx0101/pcm-pnx0101.c
@@ -28,7 +28,7 @@
28short __attribute__((section(".dmabuf"))) dma_buf_left[DMA_BUF_SAMPLES]; 28short __attribute__((section(".dmabuf"))) dma_buf_left[DMA_BUF_SAMPLES];
29short __attribute__((section(".dmabuf"))) dma_buf_right[DMA_BUF_SAMPLES]; 29short __attribute__((section(".dmabuf"))) dma_buf_right[DMA_BUF_SAMPLES];
30 30
31unsigned short* p IBSS_ATTR; 31const int16_t* p IBSS_ATTR;
32size_t p_size IBSS_ATTR; 32size_t p_size IBSS_ATTR;
33 33
34void pcm_play_lock(void) 34void pcm_play_lock(void)
@@ -41,7 +41,7 @@ void pcm_play_unlock(void)
41 41
42void pcm_play_dma_start(const void *addr, size_t size) 42void pcm_play_dma_start(const void *addr, size_t size)
43{ 43{
44 p = (unsigned short*)addr; 44 p = addr;
45 p_size = size; 45 p_size = size;
46} 46}
47 47
@@ -69,7 +69,7 @@ static inline void fill_dma_buf(int offset)
69 do 69 do
70 { 70 {
71 int count; 71 int count;
72 unsigned short *tmp_p; 72 const int16_t *tmp_p;
73 count = MIN(p_size / 4, (size_t)(lend - l)); 73 count = MIN(p_size / 4, (size_t)(lend - l));
74 tmp_p = p; 74 tmp_p = p;
75 p_size -= count * 4; 75 p_size -= count * 4;
@@ -109,16 +109,14 @@ static inline void fill_dma_buf(int offset)
109 if (new_buffer) 109 if (new_buffer)
110 { 110 {
111 new_buffer = false; 111 new_buffer = false;
112 pcm_play_dma_started_callback(); 112 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
113 } 113 }
114 114
115 if (l >= lend) 115 if (l >= lend)
116 return; 116 return;
117 117
118 pcm_play_get_more_callback((void**)&p, &p_size); 118 new_buffer = pcm_play_dma_complete_callback(PCM_DMAST_OK,
119 119 &p, &p_size);
120 if (p_size)
121 new_buffer = true;
122 } 120 }
123 while (p_size); 121 while (p_size);
124 } 122 }
diff --git a/firmware/target/arm/pp/pcm-pp.c b/firmware/target/arm/pp/pcm-pp.c
index 1b38994f7b..99d46a6096 100644
--- a/firmware/target/arm/pp/pcm-pp.c
+++ b/firmware/target/arm/pp/pcm-pp.c
@@ -30,26 +30,6 @@
30 30
31/** DMA **/ 31/** DMA **/
32 32
33#ifdef CPU_PP502x
34/* 16-bit, L-R packed into 32 bits with left in the least significant halfword */
35#define SAMPLE_SIZE 16
36/* DMA Requests from IIS, Memory to peripheral, single transfer,
37 wait for DMA request, interrupt on complete */
38#define DMA_PLAY_CONFIG ((DMA_REQ_IIS << DMA_CMD_REQ_ID_POS) | \
39 DMA_CMD_RAM_TO_PER | DMA_CMD_SINGLE | \
40 DMA_CMD_WAIT_REQ | DMA_CMD_INTR)
41/* DMA status cannot be viewed from outside code in control because that can
42 * clear the interrupt from outside the handler and prevent the handler from
43 * from being called. Split up transfers to a reasonable size that is good as
44 * a timer, obtaining a keyclick position and peaking yet still keeps the
45 * FIQ count low.
46 */
47#define MAX_DMA_CHUNK_SIZE (pcm_curr_sampr >> 6) /* ~1/256 seconds */
48#else
49/* 32-bit, one left 32-bit sample followed by one right 32-bit sample */
50#define SAMPLE_SIZE 32
51#endif
52
53struct dma_data 33struct dma_data
54{ 34{
55/* NOTE: The order of size and p is important if you use assembler 35/* NOTE: The order of size and p is important if you use assembler
@@ -57,6 +37,8 @@ struct dma_data
57 union 37 union
58 { 38 {
59 unsigned long addr; 39 unsigned long addr;
40 const void *p_r;
41 void *p_w;
60 uint32_t *p16; /* For packed 16-16 stereo pairs */ 42 uint32_t *p16; /* For packed 16-16 stereo pairs */
61 uint16_t *p32; /* For individual samples converted to 32-bit */ 43 uint16_t *p32; /* For individual samples converted to 32-bit */
62 }; 44 };
@@ -113,56 +95,208 @@ void pcm_dma_apply_settings(void)
113} 95}
114 96
115#if defined(CPU_PP502x) 97#if defined(CPU_PP502x)
116/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ 98/* 16-bit, L-R packed into 32 bits with left in the least significant halfword */
117void ICODE_ATTR __attribute__((interrupt("FIQ"))) fiq_playback(void) 99#define SAMPLE_SIZE 16
100/* DMA Requests from IIS, Memory to peripheral, single transfer,
101 wait for DMA request, interrupt on complete */
102#define DMA_PLAY_CONFIG ((DMA_REQ_IIS << DMA_CMD_REQ_ID_POS) | \
103 DMA_CMD_RAM_TO_PER | DMA_CMD_SINGLE | \
104 DMA_CMD_WAIT_REQ | DMA_CMD_INTR)
105/* DMA status cannot be viewed from outside code in control because that can
106 * clear the interrupt from outside the handler and prevent the handler from
107 * from being called. Split up transfers to a reasonable size that is good as
108 * a timer and peaking yet still keeps the FIQ count low.
109 */
110#define MAX_DMA_CHUNK_SIZE (pcm_curr_sampr >> 6) /* ~1/256 seconds */
111
112static inline void dma_tx_init(void)
113{
114 /* Enable DMA controller */
115 DMA_MASTER_CONTROL |= DMA_MASTER_CONTROL_EN;
116 /* FIQ priority for DMA */
117 CPU_INT_PRIORITY |= DMA_MASK;
118 /* Enable request?? Not setting or clearing everything doesn't seem to
119 * prevent it operating. Perhaps important for reliability (how requests
120 * are handled). */
121 DMA_REQ_STATUS |= 1ul << DMA_REQ_IIS;
122 DMA0_STATUS;
123}
124
125static inline void dma_tx_setup(void)
126{
127 /* Setup DMA controller */
128 DMA0_PER_ADDR = (unsigned long)&IISFIFO_WR;
129 DMA0_FLAGS = DMA_FLAGS_UNK26;
130 DMA0_INCR = DMA_INCR_RANGE_FIXED | DMA_INCR_WIDTH_32BIT;
131}
132
133static inline unsigned long dma_tx_buf_prepare(const void *addr)
134{
135 unsigned long a = (unsigned long)addr;
136
137 if (a < UNCACHED_BASE_ADDR) {
138 /* VA in DRAM - writeback all data and get PA */
139 a = UNCACHED_ADDR(a);
140 commit_dcache();
141 }
142
143 return a;
144}
145
146static inline void dma_tx_start(bool begin)
147{
148 size_t size = MAX_DMA_CHUNK_SIZE;
149
150 /* Not at least MAX_DMA_CHUNK_SIZE left or there would be less
151 * than a FIFO's worth of data after this transfer? */
152 if (size + 16*4 > dma_play_data.size)
153 size = dma_play_data.size;
154
155 /* Set the new DMA values and activate channel */
156 DMA0_RAM_ADDR = dma_play_data.addr;
157 DMA0_CMD = DMA_PLAY_CONFIG | (size - 4) | DMA_CMD_START;
158
159 (void)begin;
160}
161
162static void dma_tx_stop(void)
163{
164 unsigned long status = DMA0_STATUS; /* Snapshot- resume from this point */
165 unsigned long cmd = DMA0_CMD;
166 size_t size = 0;
167
168 /* Stop transfer */
169 DMA0_CMD = cmd & ~(DMA_CMD_START | DMA_CMD_INTR);
170
171 /* Wait for not busy + clear int */
172 while (DMA0_STATUS & (DMA_STATUS_BUSY | DMA_STATUS_INTR));
173
174 if (status & DMA_STATUS_BUSY) {
175 /* Transfer was interrupted - leave what's left */
176 size = (cmd & 0xfffc) - (status & 0xfffc);
177 }
178 else if (status & DMA_STATUS_INTR) {
179 /* Transfer was finished - DMA0_STATUS will have been reloaded
180 * automatically with size in DMA0_CMD. Setup to restart on next
181 * segment. */
182 size = (cmd & 0xfffc) + 4;
183 }
184 /* else not an active state - size = 0 */
185
186 dma_play_data.addr += size;
187 dma_play_data.size -= size;
188
189 if (dma_play_data.size == 0)
190 dma_play_data.addr = 0; /* Entire buffer has completed. */
191}
192
193static inline void dma_tx_lock(void)
194{
195 CPU_INT_DIS = DMA_MASK;
196}
197
198static inline void dma_tx_unlock(void)
118{ 199{
119 bool new_buffer = false; 200 CPU_INT_EN = DMA_MASK;
120 register size_t size; 201}
121 202
203/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
204void fiq_playback(void) ICODE_ATTR __attribute__((interrupt("FIQ")));
205void fiq_playback(void)
206{
122 DMA0_STATUS; /* Clear any pending interrupt */ 207 DMA0_STATUS; /* Clear any pending interrupt */
123 208
124 size = (DMA0_CMD & 0xffff) + 4; /* Get size of trasfer that caused this 209 size_t size = (DMA0_CMD & 0xffff) + 4; /* Get size of trasfer that caused
125 interrupt */ 210 this interrupt */
126 dma_play_data.addr += size; 211 dma_play_data.addr += size;
127 dma_play_data.size -= size; 212 dma_play_data.size -= size;
128 213
129 while (1) 214 if (LIKELY(dma_play_data.size != 0)) {
130 { 215 /* Begin next segment */
131 if (dma_play_data.size > 0) { 216 dma_tx_start(false);
132 size = MAX_DMA_CHUNK_SIZE; 217 }
133 /* Not at least MAX_DMA_CHUNK_SIZE left or there would be less 218 else if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &dma_play_data.p_r,
134 * than a FIFO's worth of data after this transfer? */ 219 &dma_play_data.size)) {
135 if (size + 16*4 > dma_play_data.size) 220 dma_play_data.addr = dma_tx_buf_prepare(dma_play_data.p_r);
136 size = dma_play_data.size; 221 dma_tx_start(false);
137 222 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
138 /* Set the new DMA values and activate channel */ 223 }
139 DMA0_RAM_ADDR = dma_play_data.addr; 224}
140 DMA0_CMD = DMA_PLAY_CONFIG | (size - 4) | DMA_CMD_START;
141
142 if (new_buffer)
143 pcm_play_dma_started_callback();
144 return;
145 }
146 225
147 new_buffer = true; 226#else /* !defined (CPU_PP502x) */
148 227
149 /* Buffer empty. Try to get more. */ 228/* 32-bit, one left 32-bit sample followed by one right 32-bit sample */
150 pcm_play_get_more_callback((void **)&dma_play_data.addr, 229#define SAMPLE_SIZE 32
151 &dma_play_data.size);
152 230
153 if (dma_play_data.size == 0) { 231static void dma_tx_init(void)
154 /* No more data */ 232{
155 return; 233 /* Set up banked registers for FIQ mode */
156 }
157 234
158 if (dma_play_data.addr < UNCACHED_BASE_ADDR) { 235 /* Use non-banked registers for scratch. */
159 /* Flush any pending cache writes */ 236 register volatile void *iiscfg asm("r0") = &IISCONFIG;
160 dma_play_data.addr = UNCACHED_ADDR(dma_play_data.addr); 237 register volatile void *dmapd asm("r1") = &dma_play_data;
161 commit_dcache(); 238
162 } 239 asm volatile (
240 "mrs r2, cpsr \n" /* Save mode and interrupt status */
241 "msr cpsr_c, #0xd1 \n" /* Switch to FIQ mode */
242 "mov r8, #0 \n"
243 "mov r9, #0 \n"
244 "mov r10, %[iiscfg] \n"
245 "mov r11, %[dmapd] \n"
246 "msr cpsr_c, r2 \n"
247 :
248 : [iiscfg]"r"(iiscfg), [dmapd]"r"(dmapd)
249 : "r2");
250
251 /* FIQ priority for I2S */
252 CPU_INT_PRIORITY |= IIS_MASK;
253 CPU_INT_EN = IIS_MASK;
254}
255
256static inline void dma_tx_setup(void)
257{
258 /* Nothing to do */
259}
260
261static inline unsigned long dma_tx_buf_prepare(const void *addr)
262{
263 return (unsigned long)addr;
264}
265
266static inline void dma_tx_start(bool begin)
267{
268 if (begin) {
269 IISCONFIG &= ~IIS_TXFIFOEN; /* Stop transmitting */
163 } 270 }
271
272 /* Fill the FIFO or start when data is used up */
273 while (IIS_TX_FREE_COUNT >= 2 && dma_play_data.size != 0) {
274 IISFIFO_WRH = *dma_play_data.p32++;
275 IISFIFO_WRH = *dma_play_data.p32++;
276 dma_play_data.size -= 4;
277 }
278
279 if (begin) {
280 IISCONFIG |= IIS_TXFIFOEN; /* Start transmitting */
281 }
282}
283
284static inline void dma_tx_stop(void)
285{
286 /* Disable TX interrupt */
287 IIS_IRQTX_REG &= ~IIS_IRQTX;
288}
289
290static inline void dma_tx_lock(void)
291{
292 IIS_IRQTX_REG &= ~IIS_IRQTX;
293}
294
295static inline void dma_tx_unlock(void)
296{
297 IIS_IRQTX_REG |= IIS_IRQTX;
164} 298}
165#else 299
166/* ASM optimised FIQ handler. Checks for the minimum allowed loop cycles by 300/* ASM optimised FIQ handler. Checks for the minimum allowed loop cycles by
167 * evalutation of free IISFIFO-slots against available source buffer words. 301 * evalutation of free IISFIFO-slots against available source buffer words.
168 * Through this it is possible to move the check for IIS_TX_FREE_COUNT outside 302 * Through this it is possible to move the check for IIS_TX_FREE_COUNT outside
@@ -173,150 +307,123 @@ void ICODE_ATTR __attribute__((interrupt("FIQ"))) fiq_playback(void)
173 * ASM implementation (not used anymore): GCC fails to make use of the fact 307 * ASM implementation (not used anymore): GCC fails to make use of the fact
174 * that FIQ mode has registers r8-r14 banked, and so does not need to be saved. 308 * that FIQ mode has registers r8-r14 banked, and so does not need to be saved.
175 * This routine uses only these registers, and so will never touch the stack 309 * This routine uses only these registers, and so will never touch the stack
176 * unless it actually needs to do so when calling pcm_callback_for_more. 310 * unless it actually needs to do so when calling pcm_play_dma_complete_callback.
177 * C version is still included below for reference and testing. 311 * C version is still included below for reference and testing.
178 */ 312 */
179#if 1 313#if 1
180void fiq_playback(void) ICODE_ATTR __attribute__((naked)); 314void fiq_playback(void) ICODE_ATTR __attribute__((naked));
181void fiq_playback(void) 315void fiq_playback(void)
182{ 316{
183 /* r10 contains IISCONFIG address (set in crt0.S to minimise code in actual 317 /* r8 and r9 contains local copies of p and size respectively.
184 * FIQ handler. r11 contains address of p (also set in crt0.S). Most other 318 * r10 contains IISCONFIG address (set during PCM init to minimize code in
185 * addresses we need are generated by using offsets with these two. 319 * FIQ handler.Most other addresses we need are generated by using offsets
186 * r10 + 0x40 is IISFIFO_WR, and r10 + 0x0c is IISFIFO_CFG. 320 * from this.
187 * r8 and r9 contains local copies of p and size respectively. 321 * r10 + 0x40 is IISFIFO_WR, and r10 + 0x1c is IISFIFO_CFG.
188 * r0-r3 and r12 is a working register. 322 * r11 contains address of dma_play_data
323 * r12 and r14 are working registers.
324 *
325 * Divided into two blocks: one where no external calls are needed and
326 * one where external callbacks are made
189 */ 327 */
190 asm volatile ( 328 asm volatile (
191 "stmfd sp!, { r0-r4, lr } \n" /* stack scratch regs and lr */ 329 /* No external calls */
192 330 "sub lr, lr, #4 \n" /* Prepare return address */
193 "mov r4, #0 \n" /* Was the callback called? */ 331 "stmfd sp!, { lr } \n" /* stack lr so we can use it */
194#if CONFIG_CPU == PP5002 332 "ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux ... */
195 "ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux */ 333 "ldr r12, [r12] \n" /* ... actually a DMA INT ack? */
196 "ldr r12, [r12] \n" 334 "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */
197#endif 335 "cmp r9, #0 \n" /* is size 0? */
198 "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */ 336 "beq 1f \n" /* if so, ask PCM for more data */
199 "cmp r9, #0 \n" /* is size 0? */ 337
200 "beq .more_data \n" /* if so, ask pcmbuf for more data */ 338 "ldr r14, [r10, #0x1c] \n" /* read IISFIFO_CFG to check FIFO status */
201 339 "and r14, r14, #(0xe<<23) \n" /* r14 = (IIS_TX_FREE_COUNT & ~1) << 23 */
202#if SAMPLE_SIZE == 16 340 "cmp r9, r14, lsr #22 \n" /* number of words from source */
203 ".check_fifo: \n" 341 "movlo r14, r9, lsl #22 \n" /* r14 = amount of allowed loops */
204 "ldr r0, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */ 342 "sub r9, r9, r14, lsr #22 \n" /* r14 words will be written in loop */
205 "and r0, r0, %[mask] \n" /* r0 = IIS_TX_FREE_COUNT << 16 (PP502x) */ 343 "0: \n"
206 344 "ldr r12, [r8], #4 \n" /* load left-right pair */
207 "mov r1, r0, lsr #16 \n" /* number of free FIFO slots */ 345 "subs r14, r14, #(0x2<<23) \n" /* one more loop? ... */
208 "cmp r1, r9, lsr #2 \n" /* number of words from source */ 346 "strh r12, [r10, #0x40] \n" /* left sample to IISFIFO_WR */
209 "movgt r1, r9, lsr #2 \n" /* r1 = amount of allowed loops */ 347 "mov r12, r12, lsr #16 \n" /* put right sample in bottom 16 bits */
210 "sub r9, r9, r1, lsl #2 \n" /* r1 words will be written in following loop */ 348 "strh r12, [r10, #0x40] \n" /* right sample to IISFIFO_WR */
211 349 "bhi 0b \n" /* ... yes, continue */
212 "subs r1, r1, #2 \n" 350
213 ".fifo_loop_2: \n" 351 "cmp r9, #0 \n" /* either FIFO full or size empty? */
214 "ldmgeia r8!, {r2, r12} \n" /* load four samples */ 352 "stmneia r11, { r8-r9 } \n" /* save p and size, if not empty */
215 "strge r2 , [r10, %[wr]] \n" /* write sample 0-1 to IISFIFO_WR */ 353 "ldmnefd sp!, { pc }^ \n" /* RFE if not empty */
216 "strge r12, [r10, %[wr]] \n" /* write sample 2-3 to IISFIFO_WR */ 354
217 "subges r1, r1, #2 \n" /* one more loop? */ 355 /* Making external calls */
218 "bge .fifo_loop_2 \n" /* yes, continue */ 356 "1: \n"
219 357 "stmfd sp!, { r0-r3 } \n" /* Must save volatiles */
220 "tst r1, #1 \n" /* two samples (one word) left? */ 358 "2: \n"
221 "ldrne r12, [r8], #4 \n" /* load two samples */ 359 "mov r0, %0 \n" /* r0 = status */
222 "strne r12, [r10, %[wr]] \n" /* write sample 0-1 to IISFIFO_WR */ 360 "mov r1, r11 \n" /* r1 = &dma_play_data.p_r */
223#elif SAMPLE_SIZE == 32 361 "add r2, r11, #4 \n" /* r2 = &dma_play_data.size */
224 ".check_fifo: \n" 362 "ldr r3, =pcm_play_dma_complete_callback \n"
225 "ldr r0, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */ 363 "mov lr, pc \n" /* long call (not in same section) */
226 "and r0, r0, %[mask] \n" /* r0 = IIS_TX_FREE_COUNT << 23 (PP5002) */ 364 "bx r3 \n"
227 365 "cmp r0, #0 \n" /* more data? */
228 "movs r1, r0, lsr #24 \n" /* number of free pairs of FIFO slots */ 366 "ldmeqfd sp!, { r0-r3, pc }^ \n" /* no? -> exit */
229 "beq .fifo_fill_complete \n" /* no complete pair? -> exit */ 367
230 "cmp r1, r9, lsr #2 \n" /* number of words from source */ 368 "ldr r14, [r10, #0x1c] \n" /* read IISFIFO_CFG to check FIFO status */
231 "movgt r1, r9, lsr #2 \n" /* r1 = amount of allowed loops */ 369 "ands r14, r14, #(0xe<<23) \n" /* r14 = (IIS_TX_FREE_COUNT & ~1) << 23 */
232 "sub r9, r9, r1, lsl #2 \n" /* r1 words will be written in following loop */ 370 "bne 4f \n"
233 371 "3: \n" /* inform of started status if registered */
234 ".fifo_loop: \n" 372 "ldr r1, =pcm_play_status_callback \n"
235 "ldr r12, [r8], #4 \n" /* load two samples */ 373 "ldr r1, [r1] \n"
236 "mov r2 , r12, lsl #16 \n" /* put left sample at the top bits */ 374 "cmp r1, #0 \n"
237 "str r2 , [r10, %[wr]] \n" /* write top sample to IISFIFO_WR */ 375 "movne r0, %1 \n"
238 "str r12, [r10, %[wr]] \n" /* write low sample to IISFIFO_WR*/ 376 "movne lr, pc \n"
239 "subs r1, r1, #1 \n" /* one more loop? */ 377 "bxne r1 \n"
240 "bgt .fifo_loop \n" /* yes, continue */ 378 "ldmfd sp!, { r0-r3, pc }^ \n" /* exit */
241 379 "4: \n"
242 ".fifo_fill_complete: \n" 380 "ldmia r11, { r8-r9 } \n" /* load new p and size */
243#endif 381 "cmp r9, r14, lsr #22 \n" /* number of words from source */
244 "cmp r4, #0 \n" /* If fill came after get_more... */ 382 "movlo r14, r9, lsl #22 \n" /* r14 = amount of allowed loops */
245 "beq .still_old_buffer \n" 383 "sub r9, r9, r14, lsr #22 \n" /* r14 words will be written in loop */
246 "mov r4, #0 \n" 384 "0: \n"
247 "ldr r2, =pcm_play_dma_started \n" 385 "ldr r12, [r8], #4 \n" /* load left-right pair */
248 "ldrne r2, [r2] \n" 386 "subs r14, r14, #(0x2<<23) \n" /* one more loop? ... */
249 "cmp r2, #0 \n" 387 "strh r12, [r10, #0x40] \n" /* left sample to IISFIFO_WR */
250 "movne lr, pc \n" 388 "mov r12, r12, lsr #16 \n" /* put right sample in bottom 16 bits */
251 "bxne r2 \n" 389 "strh r12, [r10, #0x40] \n" /* right sample to IISFIFO_WR */
252 390 "bhi 0b \n" /* ... yes, continue */
253 ".still_old_buffer: \n" 391 "stmia r11, { r8-r9 } \n" /* save p and size */
254 "cmp r9, #0 \n" /* either FIFO is full or source buffer is empty */ 392
255 "bgt .exit \n" /* if source buffer is not empty, FIFO must be full */ 393 "cmp r9, #0 \n" /* used up data in FIFO fill? */
256 394 "bne 3b \n" /* no? -> go return */
257 ".more_data: \n" 395 "b 2b \n" /* yes -> get even more */
258 "mov r4, #1 \n" /* Remember we did this */ 396 ".ltorg \n"
259 "ldr r2, =pcm_play_get_more_callback \n"
260 "mov r0, r11 \n" /* r0 = &p */
261 "add r1, r11, #4 \n" /* r1 = &size */
262 "mov lr, pc \n" /* call pcm_play_get_more_callback */
263 "bx r2 \n"
264 "ldmia r11, { r8-r9 } \n" /* load new p and size */
265 "cmp r9, #0 \n"
266 "bne .check_fifo \n" /* size != 0? refill */
267
268 ".exit: \n" /* (r9=0 if stopping, look above) */
269 "stmia r11, { r8-r9 } \n" /* save p and size */
270 "ldmfd sp!, { r0-r4, lr } \n"
271 "subs pc, lr, #4 \n" /* FIQ specific return sequence */
272 ".ltorg \n"
273 : /* These must only be integers! No regs */ 397 : /* These must only be integers! No regs */
274 : [mask]"i"(IIS_TX_FREE_MASK), 398 : "i"(PCM_DMAST_OK), "i"(PCM_DMAST_STARTED));
275 [cfg]"i"((int)&IISFIFO_CFG - (int)&IISCONFIG),
276 [wr]"i"((int)&IISFIFO_WR - (int)&IISCONFIG)
277 );
278} 399}
400
279#else /* C version for reference */ 401#else /* C version for reference */
280void fiq_playback(void) __attribute__((interrupt ("FIQ"))) ICODE_ATTR; 402
281/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ 403/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
404void fiq_playback(void) ICODE_ATTR __attribute__((interrupt ("FIQ")));
282void fiq_playback(void) 405void fiq_playback(void)
283{ 406{
284 bool new_buffer = false;
285
286#if CONFIG_CPU == PP5002
287 inl(0xcf001040); 407 inl(0xcf001040);
288#endif
289 408
290 do { 409 if (LIKELY(dma_play_data.size != 0)) {
291 while (dma_play_data.size > 0) { 410 dma_tx_start(false);
292 if (IIS_TX_FREE_COUNT < 2) {
293 if (new_buffer) {
294 new_buffer = false;
295 pcm_play_dma_started_callback();
296 }
297 return;
298 }
299#if SAMPLE_SIZE == 16
300 IISFIFO_WR = *dma_play_data.p16++;
301#elif SAMPLE_SIZE == 32
302 IISFIFO_WR = *dma_play_data.p32++ << 16;
303 IISFIFO_WR = *dma_play_data.p32++ << 16;
304#endif
305 dma_play_data.size -= 4;
306 }
307 411
308 if (new_buffer) { 412 if (dma_play_data.size != 0) {
309 new_buffer = false; 413 /* Still more data */
310 pcm_play_dma_started_callback(); 414 return;
311 } 415 }
416 }
312 417
313 /* p is empty, get some more data */ 418 while (pcm_play_dma_complete_callback(PCM_DMAST_OK, &dma_play_data.p_r,
314 pcm_play_get_more_callback((void **)&dma_play_data.addr, 419 &dma_play_data.size)) {
315 &dma_play_data.size); 420 dma_tx_start(false);
316 new_buffer = true; 421 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
317 } while (dma_play_data.size);
318 422
319 /* No more data */ 423 if (dma_play_data.size != 0) {
424 return;
425 }
426 }
320} 427}
321#endif /* ASM / C selection */ 428#endif /* ASM / C selection */
322#endif /* CPU_PP502x */ 429#endif /* CPU_PP502x */
@@ -329,11 +436,7 @@ void pcm_play_lock(void)
329 int status = disable_fiq_save(); 436 int status = disable_fiq_save();
330 437
331 if (++dma_play_data.locked == 1) { 438 if (++dma_play_data.locked == 1) {
332#ifdef CPU_PP502x 439 dma_tx_lock();
333 CPU_INT_DIS = DMA_MASK;
334#else
335 IIS_IRQTX_REG &= ~IIS_IRQTX;
336#endif
337 } 440 }
338 441
339 restore_fiq(status); 442 restore_fiq(status);
@@ -341,89 +444,25 @@ void pcm_play_lock(void)
341 444
342void pcm_play_unlock(void) 445void pcm_play_unlock(void)
343{ 446{
344 int status = disable_fiq_save(); 447 int status = disable_fiq_save();
345 448
346 if (--dma_play_data.locked == 0 && dma_play_data.state != 0) { 449 if (--dma_play_data.locked == 0 && dma_play_data.state != 0) {
347#ifdef CPU_PP502x 450 dma_tx_unlock();
348 CPU_INT_EN = DMA_MASK;
349#else
350 IIS_IRQTX_REG |= IIS_IRQTX;
351#endif
352 } 451 }
353 452
354 restore_fiq(status); 453 restore_fiq(status);
355} 454}
356 455
357static void play_start_pcm(void) 456static void play_start_pcm(void)
358{ 457{
359 fiq_function = fiq_playback; 458 fiq_function = fiq_playback;
360
361#ifdef CPU_PP502x
362 /* Not at least MAX_DMA_CHUNK_SIZE left or there would be less than a
363 * FIFO's worth of data after this transfer? */
364 size_t size = MAX_DMA_CHUNK_SIZE;
365 if (size + 16*4 > dma_play_data.size)
366 size = dma_play_data.size;
367
368 DMA0_RAM_ADDR = dma_play_data.addr;
369 DMA0_CMD = DMA_PLAY_CONFIG | (size - 4) | DMA_CMD_START;
370 dma_play_data.state = 1; 459 dma_play_data.state = 1;
371#else 460 dma_tx_start(true);
372 IISCONFIG &= ~IIS_TXFIFOEN; /* Stop transmitting */
373
374 /* Fill the FIFO or start when data is used up */
375 while (1) {
376 if (IIS_TX_FREE_COUNT < 2 || dma_play_data.size == 0) {
377 IISCONFIG |= IIS_TXFIFOEN; /* Start transmitting */
378 dma_play_data.state = 1;
379 return;
380 }
381
382#if SAMPLE_SIZE == 16
383 IISFIFO_WR = *dma_play_data.p16++;
384#elif SAMPLE_SIZE == 32
385 IISFIFO_WR = *dma_play_data.p32++ << 16;
386 IISFIFO_WR = *dma_play_data.p32++ << 16;
387#endif
388 dma_play_data.size -= 4;
389 }
390#endif
391} 461}
392 462
393static void play_stop_pcm(void) 463static void play_stop_pcm(void)
394{ 464{
395#ifdef CPU_PP502x 465 dma_tx_stop();
396 unsigned long status = DMA0_STATUS; /* Snapshot- resume from this point */
397 unsigned long cmd = DMA0_CMD;
398 size_t size = 0;
399
400 /* Stop transfer */
401 DMA0_CMD = cmd & ~(DMA_CMD_START | DMA_CMD_INTR);
402
403 /* Wait for not busy + clear int */
404 while (DMA0_STATUS & (DMA_STATUS_BUSY | DMA_STATUS_INTR));
405
406 if (status & DMA_STATUS_BUSY) {
407 /* Transfer was interrupted - leave what's left */
408 size = (cmd & 0xfffc) - (status & 0xfffc);
409 }
410 else if (status & DMA_STATUS_INTR) {
411 /* Transfer was finished - DMA0_STATUS will have been reloaded
412 * automatically with size in DMA0_CMD. Setup to restart on next
413 * segment. */
414 size = (cmd & 0xfffc) + 4;
415 }
416 /* else not an active state - size = 0 */
417
418 dma_play_data.addr += size;
419 dma_play_data.size -= size;
420
421 if (dma_play_data.size == 0)
422 dma_play_data.addr = 0; /* Entire buffer has completed. */
423#else
424 /* Disable TX interrupt */
425 IIS_IRQTX_REG &= ~IIS_IRQTX;
426#endif
427 466
428 /* Wait for FIFO to empty */ 467 /* Wait for FIFO to empty */
429 while (!IIS_TX_IS_EMPTY); 468 while (!IIS_TX_IS_EMPTY);
@@ -433,30 +472,17 @@ static void play_stop_pcm(void)
433 472
434void pcm_play_dma_start(const void *addr, size_t size) 473void pcm_play_dma_start(const void *addr, size_t size)
435{ 474{
475 pcm_play_dma_stop();
476
436#if NUM_CORES > 1 477#if NUM_CORES > 1
437 /* This will become more important later - and different ! */ 478 /* This will become more important later - and different ! */
438 dma_play_data.core = processor_id(); /* save initiating core */ 479 dma_play_data.core = processor_id(); /* save initiating core */
439#endif 480#endif
440 481
441 pcm_play_dma_stop(); 482 dma_tx_setup();
442
443#ifdef CPU_PP502x
444 if ((unsigned long)addr < UNCACHED_BASE_ADDR) {
445 /* Flush any pending cache writes */
446 addr = UNCACHED_ADDR(addr);
447 commit_dcache();
448 }
449 483
450 dma_play_data.addr = (unsigned long)addr; 484 dma_play_data.addr = dma_tx_buf_prepare(addr);
451 dma_play_data.size = size; 485 dma_play_data.size = size;
452 DMA0_PER_ADDR = (unsigned long)&IISFIFO_WR;
453 DMA0_FLAGS = DMA_FLAGS_UNK26;
454 DMA0_INCR = DMA_INCR_RANGE_FIXED | DMA_INCR_WIDTH_32BIT;
455#else
456 dma_play_data.addr = (unsigned long)addr;
457 dma_play_data.size = size;
458#endif
459
460 play_start_pcm(); 486 play_start_pcm();
461} 487}
462 488
@@ -490,39 +516,7 @@ void pcm_play_dma_init(void)
490 /* Initialize default register values. */ 516 /* Initialize default register values. */
491 audiohw_init(); 517 audiohw_init();
492 518
493#ifdef CPU_PP502x 519 dma_tx_init();
494 /* Enable DMA controller */
495 DMA_MASTER_CONTROL |= DMA_MASTER_CONTROL_EN;
496 /* FIQ priority for DMA */
497 CPU_INT_PRIORITY |= DMA_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
504 /* Set up banked registers for FIQ mode */
505
506 /* Use non-banked registers for scratch. */
507 register volatile void *iiscfg asm("r0") = &IISCONFIG;
508 register volatile void *dmapd asm("r1") = &dma_play_data;
509
510 asm volatile (
511 "mrs r2, cpsr \n" /* Save mode and interrupt status */
512 "msr cpsr_c, #0xd1 \n" /* Switch to FIQ mode */
513 "mov r8, #0 \n"
514 "mov r9, #0 \n"
515 "mov r10, %[iiscfg] \n"
516 "mov r11, %[dmapd] \n"
517 "msr cpsr_c, r2 \n"
518 :
519 : [iiscfg]"r"(iiscfg), [dmapd]"r"(dmapd)
520 : "r2");
521
522 /* FIQ priority for I2S */
523 CPU_INT_PRIORITY |= IIS_MASK;
524 CPU_INT_EN = IIS_MASK;
525#endif
526 520
527 IISCONFIG |= IIS_TXFIFOEN; 521 IISCONFIG |= IIS_TXFIFOEN;
528} 522}
@@ -649,11 +643,15 @@ void fiq_record(void)
649 } 643 }
650 } 644 }
651 645
652 pcm_rec_more_ready_callback(0, (void *)&dma_rec_data.addr, 646 if (pcm_rec_dma_complete_callback(PCM_DMAST_OK, &dma_rec_data.p_w,
653 &dma_rec_data.size); 647 &dma_rec_data.size))
648 {
649 pcm_rec_dma_status_callback(PCM_DMAST_STARTED);
650 }
654} 651}
655 652
656#else 653#else /* !(SANSA_C200 || SANSA_E200) */
654
657void fiq_record(void) 655void fiq_record(void)
658{ 656{
659 while (dma_rec_data.size > 0) { 657 while (dma_rec_data.size > 0) {
@@ -664,17 +662,20 @@ void fiq_record(void)
664#if SAMPLE_SIZE == 16 662#if SAMPLE_SIZE == 16
665 *dma_rec_data.p16++ = IISFIFO_RD; 663 *dma_rec_data.p16++ = IISFIFO_RD;
666#elif SAMPLE_SIZE == 32 664#elif SAMPLE_SIZE == 32
667 *dma_rec_data.p32++ = IISFIFO_RD >> 16; 665 *dma_rec_data.p32++ = IISFIFO_RDH;
668 *dma_rec_data.p32++ = IISFIFO_RD >> 16; 666 *dma_rec_data.p32++ = IISFIFO_RDH;
669#endif 667#endif
670 dma_rec_data.size -= 4; 668 dma_rec_data.size -= 4;
671 } 669 }
672 670
673 pcm_rec_more_ready_callback(0, (void *)&dma_rec_data.addr, 671 if (pcm_rec_dma_complete_callback(PCM_DMAST_OK, &dma_rec_data.p_w,
674 &dma_rec_data.size); 672 &dma_rec_data.size))
673 {
674 pcm_rec_dma_status_callback(PCM_DMAST_STARTED);
675 }
675} 676}
676 677
677#endif /* SANSA_E200 */ 678#endif /* SANSA_C200 || SANSA_E200 */
678 679
679void pcm_rec_dma_stop(void) 680void pcm_rec_dma_stop(void)
680{ 681{
diff --git a/firmware/target/arm/rk27xx/pcm-rk27xx.c b/firmware/target/arm/rk27xx/pcm-rk27xx.c
index 80a8d462ea..e4318de408 100644
--- a/firmware/target/arm/rk27xx/pcm-rk27xx.c
+++ b/firmware/target/arm/rk27xx/pcm-rk27xx.c
@@ -273,15 +273,13 @@ size_t pcm_get_bytes_waiting(void)
273/* audio DMA ISR called when chunk from callers buffer has been transfered */ 273/* audio DMA ISR called when chunk from callers buffer has been transfered */
274void INT_HDMA(void) 274void INT_HDMA(void)
275{ 275{
276 void *start; 276 const void *start;
277 size_t size; 277 size_t size;
278 278
279 pcm_play_get_more_callback(&start, &size); 279 if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size))
280
281 if (size != 0)
282 { 280 {
283 hdma_i2s_transfer(start, size); 281 hdma_i2s_transfer(start, size);
284 pcm_play_dma_started_callback(); 282 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
285 } 283 }
286} 284}
287 285
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 35905645dd..a1c854a0df 100644
--- a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
+++ b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
@@ -215,16 +215,14 @@ void pcm_play_dma_pause(bool pause)
215 215
216void fiq_handler(void) 216void fiq_handler(void)
217{ 217{
218 static void *start; 218 static const void *start;
219 static size_t size; 219 static size_t size;
220 220
221 /* clear any pending interrupt */ 221 /* clear any pending interrupt */
222 SRCPND = DMA2_MASK; 222 SRCPND = DMA2_MASK;
223 223
224 /* Buffer empty. Try to get more. */ 224 /* Buffer empty. Try to get more. */
225 pcm_play_get_more_callback(&start, &size); 225 if (!pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size))
226
227 if (size == 0)
228 return; 226 return;
229 227
230 /* Flush any pending cache writes */ 228 /* Flush any pending cache writes */
@@ -237,7 +235,7 @@ void fiq_handler(void)
237 /* Re-Activate the channel */ 235 /* Re-Activate the channel */
238 DMASKTRIG2 = 0x2; 236 DMASKTRIG2 = 0x2;
239 237
240 pcm_play_dma_started_callback(); 238 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
241} 239}
242 240
243size_t pcm_get_bytes_waiting(void) 241size_t pcm_get_bytes_waiting(void)
diff --git a/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c b/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c
index a4f58a8e06..943cbb2ade 100644
--- a/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c
+++ b/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c
@@ -255,16 +255,14 @@ void pcm_play_dma_pause(bool pause)
255 255
256void fiq_handler(void) 256void fiq_handler(void)
257{ 257{
258 static void *start; 258 static const void *start;
259 static size_t size; 259 static size_t size;
260 260
261 /* clear any pending interrupt */ 261 /* clear any pending interrupt */
262 SRCPND = DMA2_MASK; 262 SRCPND = DMA2_MASK;
263 263
264 /* Buffer empty. Try to get more. */ 264 /* Buffer empty. Try to get more. */
265 pcm_play_get_more_callback(&start, &size); 265 if (!pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size))
266
267 if (size == 0)
268 return; 266 return;
269 267
270 /* Flush any pending cache writes */ 268 /* Flush any pending cache writes */
@@ -277,7 +275,7 @@ void fiq_handler(void)
277 /* Re-Activate the channel */ 275 /* Re-Activate the channel */
278 DMASKTRIG2 = 0x2; 276 DMASKTRIG2 = 0x2;
279 277
280 pcm_play_dma_started_callback(); 278 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
281} 279}
282 280
283size_t pcm_get_bytes_waiting(void) 281size_t pcm_get_bytes_waiting(void)
diff --git a/firmware/target/arm/s5l8700/pcm-s5l8700.c b/firmware/target/arm/s5l8700/pcm-s5l8700.c
index 7b4258fa68..c5a5bcf74f 100644
--- a/firmware/target/arm/s5l8700/pcm-s5l8700.c
+++ b/firmware/target/arm/s5l8700/pcm-s5l8700.c
@@ -116,9 +116,10 @@ void INT_DMA(void)
116 { 116 {
117 if (!nextsize) 117 if (!nextsize)
118 { 118 {
119 pcm_play_get_more_callback((void**)&nextbuf, &nextsize); 119 new_buffer = pcm_play_dma_complete_callback(PCM_DMAST_OK,
120 if (!nextsize) break; 120 (const void**)&nextbuf, &nextsize);
121 new_buffer = true; 121 if (!new_buffer)
122 break;
122 } 123 }
123 queuedsize = MIN(sizeof(dblbuf), nextsize / 2); 124 queuedsize = MIN(sizeof(dblbuf), nextsize / 2);
124 nextsize -= queuedsize; 125 nextsize -= queuedsize;
@@ -133,7 +134,7 @@ void INT_DMA(void)
133 134
134 if (new_buffer) 135 if (new_buffer)
135 { 136 {
136 pcm_play_dma_started_callback(); 137 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
137 new_buffer = false; 138 new_buffer = false;
138 } 139 }
139 } 140 }
diff --git a/firmware/target/arm/s5l8702/pcm-s5l8702.c b/firmware/target/arm/s5l8702/pcm-s5l8702.c
index b58ef0f4d3..1442afa420 100644
--- a/firmware/target/arm/s5l8702/pcm-s5l8702.c
+++ b/firmware/target/arm/s5l8702/pcm-s5l8702.c
@@ -65,7 +65,7 @@ void INT_DMAC0C0(void)
65 DMAC0INTTCCLR = 1; 65 DMAC0INTTCCLR = 1;
66 if (!pcm_remaining) 66 if (!pcm_remaining)
67 { 67 {
68 pcm_play_get_more_callback((void**)&dataptr, &pcm_remaining); 68 pcm_play_dma_complete_callback((const void**)&dataptr, &pcm_remaining);
69 pcm_chunksize = pcm_remaining; 69 pcm_chunksize = pcm_remaining;
70 } 70 }
71 if (!pcm_remaining) 71 if (!pcm_remaining)
@@ -115,7 +115,7 @@ void INT_DMAC0C0(void)
115 } 115 }
116 else DMAC0C0NEXTLLI = pcm_lli; 116 else DMAC0C0NEXTLLI = pcm_lli;
117 117
118 pcm_play_dma_started_callback(); 118 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
119} 119}
120 120
121void pcm_play_dma_start(const void* addr, size_t size) 121void pcm_play_dma_start(const void* addr, size_t size)
diff --git a/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c b/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c
index 91d6b66130..d23c93de39 100644
--- a/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c
+++ b/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c
@@ -33,7 +33,7 @@
33/* This is global to save some latency when pcm_play_dma_get_peak_buffer is 33/* This is global to save some latency when pcm_play_dma_get_peak_buffer is
34 * called. 34 * called.
35 */ 35 */
36static void *start; 36static const void *start;
37 37
38void pcm_play_dma_postinit(void) 38void pcm_play_dma_postinit(void)
39{ 39{
@@ -164,9 +164,7 @@ void DSPHINT(void)
164 164
165 case MSG_REFILL: 165 case MSG_REFILL:
166 /* Buffer empty. Try to get more. */ 166 /* Buffer empty. Try to get more. */
167 pcm_play_get_more_callback(&start, &size); 167 if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size))
168
169 if (size != 0)
170 { 168 {
171 unsigned long sdem_addr=(unsigned long)start - CONFIG_SDRAM_START; 169 unsigned long sdem_addr=(unsigned long)start - CONFIG_SDRAM_START;
172 /* Flush any pending cache writes */ 170 /* Flush any pending cache writes */
@@ -180,7 +178,7 @@ void DSPHINT(void)
180 DEBUGF("pcm_sdram at 0x%08lx, sdem_addr 0x%08lx", 178 DEBUGF("pcm_sdram at 0x%08lx, sdem_addr 0x%08lx",
181 (unsigned long)start, (unsigned long)sdem_addr); 179 (unsigned long)start, (unsigned long)sdem_addr);
182 180
183 pcm_play_dma_started_callback(); 181 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
184 } 182 }
185 183
186 break; 184 break;
diff --git a/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c
index 8b1fbf95e4..6e640bdf12 100644
--- a/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c
+++ b/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c
@@ -34,7 +34,7 @@
34/* This is global to save some latency when pcm_play_dma_get_peak_buffer is 34/* This is global to save some latency when pcm_play_dma_get_peak_buffer is
35 * called. 35 * called.
36 */ 36 */
37static void *start; 37static const void *start;
38static int dma_channel; 38static int dma_channel;
39 39
40void pcm_play_dma_postinit(void) 40void pcm_play_dma_postinit(void)
@@ -171,9 +171,7 @@ void DSPHINT(void)
171 171
172 case MSG_REFILL: 172 case MSG_REFILL:
173 /* Buffer empty. Try to get more. */ 173 /* Buffer empty. Try to get more. */
174 pcm_play_get_more_callback(&start, &size); 174 if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size))
175
176 if (size != 0)
177 { 175 {
178 unsigned long sdem_addr=(unsigned long)start - CONFIG_SDRAM_START; 176 unsigned long sdem_addr=(unsigned long)start - CONFIG_SDRAM_START;
179 /* Flush any pending cache writes */ 177 /* Flush any pending cache writes */
@@ -187,7 +185,7 @@ void DSPHINT(void)
187 DEBUGF("pcm_sdram at 0x%08lx, sdem_addr 0x%08lx", 185 DEBUGF("pcm_sdram at 0x%08lx, sdem_addr 0x%08lx",
188 (unsigned long)start, (unsigned long)sdem_addr); 186 (unsigned long)start, (unsigned long)sdem_addr);
189 187
190 pcm_play_dma_started_callback(); 188 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
191 } 189 }
192 190
193 break; 191 break;