summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2009-02-19 20:40:03 +0000
committerMichael Sevakis <jethead71@rockbox.org>2009-02-19 20:40:03 +0000
commit6c399b82544fca6db45f0475ec558ec79b963fc7 (patch)
tree09fd7a75f8479ae159458352f3afee6f86c3ece3
parent1ad58f9757de11248e74ca52c3288e177ac2760b (diff)
downloadrockbox-6c399b82544fca6db45f0475ec558ec79b963fc7.tar.gz
rockbox-6c399b82544fca6db45f0475ec558ec79b963fc7.zip
Use DMA for audio playback on PP502x (FS#9910 + some further mods). I can't say at this point about any change in battery life but it frees up a percent or two of CPU cycles as measured in the buffering screen. No change in recording transfers yet. Testing seemed to check out so put it out for general use and see what happens.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@20052 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/pcmbuf.c4
-rw-r--r--firmware/export/pp5020.h95
-rw-r--r--firmware/target/arm/i2s-pp.c4
-rw-r--r--firmware/target/arm/pcm-pp.c245
-rw-r--r--firmware/target/arm/system-pp502x.c6
-rw-r--r--firmware/target/arm/usb-drv-arc.c2
6 files changed, 304 insertions, 52 deletions
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index 7d2c579e42..9143e32660 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -18,10 +18,9 @@
18 * KIND, either express or implied. 18 * KIND, either express or implied.
19 * 19 *
20 ****************************************************************************/ 20 ****************************************************************************/
21
22#include <stdbool.h>
23#include <stdio.h> 21#include <stdio.h>
24#include "config.h" 22#include "config.h"
23#include "system.h"
25#include "debug.h" 24#include "debug.h"
26#include "panic.h" 25#include "panic.h"
27#include <kernel.h> 26#include <kernel.h>
@@ -31,7 +30,6 @@
31#ifndef SIMULATOR 30#ifndef SIMULATOR
32#include "cpu.h" 31#include "cpu.h"
33#endif 32#endif
34#include "system.h"
35#include <string.h> 33#include <string.h>
36#include "buffer.h" 34#include "buffer.h"
37#include "settings.h" 35#include "settings.h"
diff --git a/firmware/export/pp5020.h b/firmware/export/pp5020.h
index 36c88e00df..26d5bbaa51 100644
--- a/firmware/export/pp5020.h
+++ b/firmware/export/pp5020.h
@@ -23,6 +23,9 @@
23 23
24/* All info gleaned and/or copied from the iPodLinux project. */ 24/* All info gleaned and/or copied from the iPodLinux project. */
25 25
26/* PCM addresses for obtaining buffers will be what DMA is using (physical) */
27#define HAVE_PCM_DMA_ADDRESS
28
26/* USBOTG */ 29/* USBOTG */
27#define USB_NUM_ENDPOINTS 3 30#define USB_NUM_ENDPOINTS 3
28/* This needs to be 2048 byte aligned, but USB_QHARRAY_ATTR should take care 31/* This needs to be 2048 byte aligned, but USB_QHARRAY_ATTR should take care
@@ -104,6 +107,10 @@
104#define USB_IRQ 20 107#define USB_IRQ 20
105#define IDE_IRQ 23 108#define IDE_IRQ 23
106#define FIREWIRE_IRQ 25 109#define FIREWIRE_IRQ 25
110#define DMA0_IRQ 26
111#define DMA1_IRQ 27 /* guess */
112#define DMA2_IRQ 28 /* guess */
113#define DMA3_IRQ 29 /* guess */
107#define HI_IRQ 30 114#define HI_IRQ 30
108#define GPIO0_IRQ (32+0) /* Ports A..D */ 115#define GPIO0_IRQ (32+0) /* Ports A..D */
109#define GPIO1_IRQ (32+1) /* Ports E..H */ 116#define GPIO1_IRQ (32+1) /* Ports E..H */
@@ -119,6 +126,10 @@
119#define IDE_MASK (1 << IDE_IRQ) 126#define IDE_MASK (1 << IDE_IRQ)
120#define USB_MASK (1 << USB_IRQ) 127#define USB_MASK (1 << USB_IRQ)
121#define FIREWIRE_MASK (1 << FIREWIRE_IRQ) 128#define FIREWIRE_MASK (1 << FIREWIRE_IRQ)
129#define DMA0_MASK (1 << DMA0_IRQ)
130#define DMA1_MASK (1 << DMA1_IRQ)
131#define DMA2_MASK (1 << DMA2_IRQ)
132#define DMA3_MASK (1 << DMA3_IRQ)
122#define HI_MASK (1 << HI_IRQ) 133#define HI_MASK (1 << HI_IRQ)
123#define GPIO0_MASK (1 << (GPIO0_IRQ-32)) 134#define GPIO0_MASK (1 << (GPIO0_IRQ-32))
124#define GPIO1_MASK (1 << (GPIO1_IRQ-32)) 135#define GPIO1_MASK (1 << (GPIO1_IRQ-32))
@@ -601,4 +612,88 @@
601#define MMAP7_LOGICAL (*(volatile unsigned long*)(0xf000f038)) 612#define MMAP7_LOGICAL (*(volatile unsigned long*)(0xf000f038))
602#define MMAP7_PHYSICAL (*(volatile unsigned long*)(0xf000f03c)) 613#define MMAP7_PHYSICAL (*(volatile unsigned long*)(0xf000f03c))
603 614
615/** DMA engine **/
616#define DMA0_BASE_ADDR 0x6000b000
617#define DMA1_BASE_ADDR 0x6000b020
618#define DMA2_BASE_ADDR 0x6000b040
619#define DMA3_BASE_ADDR 0x6000b060
620
621/* DMA request IDs */
622#define DMA_REQ_IIS 2
623#define DMA_REQ_SDHC 13
624
625#define DMA_MASTER_CONTROL (*(volatile unsigned long*)(0x6000a000))
626#define DMA_MASTER_STATUS (*(volatile unsigned long*)(0x6000a004))
627 /* 1ul << DMA_REQ_xxx to set bit */
628#define DMA_REQ_STATUS (*(volatile unsigned long*)(0x6000a008))
629
630#define DMA_MASTER_CONTROL_EN (1 << 31)
631
632#define DMA_MASTER_STATUS_CH0 (0x1 << 24)
633#define DMA_MASTER_STATUS_CH1 (0x1 << 25)
634#define DMA_MASTER_STATUS_CH2 (0x1 << 26)
635#define DMA_MASTER_STATUS_CH3 (0x1 << 27)
636
637#define DMA0_CMD (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x00))
638#define DMA0_STATUS (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x04))
639#define DMA0_RAM_ADDR (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x10))
640#define DMA0_FLAGS (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x14))
641#define DMA0_PER_ADDR (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x18))
642#define DMA0_INCR (*(volatile unsigned long*)(DMA0_BASE_ADDR+0x1c))
643
644#define DMA1_CMD (*(volatile unsigned long*)(DMA1_BASE_ADDR+0x00))
645#define DMA1_STATUS (*(volatile unsigned long*)(DMA1_BASE_ADDR+0x04))
646#define DMA1_RAM_ADDR (*(volatile unsigned long*)(DMA1_BASE_ADDR+0x10))
647#define DMA1_FLAGS (*(volatile unsigned long*)(DMA1_BASE_ADDR+0x14))
648#define DMA1_PER_ADDR (*(volatile unsigned long*)(DMA1_BASE_ADDR+0x18))
649#define DMA1_INCR (*(volatile unsigned long*)(DMA1_BASE_ADDR+0x1c))
650
651#define DMA2_CMD (*(volatile unsigned long*)(DMA2_BASE_ADDR+0x00))
652#define DMA2_STATUS (*(volatile unsigned long*)(DMA2_BASE_ADDR+0x04))
653#define DMA2_RAM_ADDR (*(volatile unsigned long*)(DMA2_BASE_ADDR+0x10))
654#define DMA2_FLAGS (*(volatile unsigned long*)(DMA2_BASE_ADDR+0x14))
655#define DMA2_PER_ADDR (*(volatile unsigned long*)(DMA2_BASE_ADDR+0x18))
656#define DMA2_INCR (*(volatile unsigned long*)(DMA2_BASE_ADDR+0x1c))
657
658#define DMA3_CMD (*(volatile unsigned long*)(DMA3_BASE_ADDR+0x00))
659#define DMA3_STATUS (*(volatile unsigned long*)(DMA3_BASE_ADDR+0x04))
660#define DMA3_RAM_ADDR (*(volatile unsigned long*)(DMA3_BASE_ADDR+0x10))
661#define DMA3_FLAGS (*(volatile unsigned long*)(DMA3_BASE_ADDR+0x14))
662#define DMA3_PER_ADDR (*(volatile unsigned long*)(DMA3_BASE_ADDR+0x18))
663#define DMA3_INCR (*(volatile unsigned long*)(DMA3_BASE_ADDR+0x1c))
664
665#define DMA_CMD_SIZE (0xffff)
666#define DMA_CMD_REQ_ID (0xf << 16)
667 #define DMA_CMD_REQ_ID_POS 16
668#define DMA_CMD_WAIT_REQ (0x1 << 24)
669#define DMA_CMD_UNK25 (0x1 << 25)
670#define DMA_CMD_SINGLE (0x1 << 26) /* stop on complete, no auto reload */
671#define DMA_CMD_RAM_TO_PER (0x1 << 27) /* otherwise per to ram */
672#define DMA_CMD_SLEEP_WAIT (0x1 << 28)
673#define DMA_CMD_INTR (0x1 << 30)
674#define DMA_CMD_START (0x1 << 31)
675
676#define DMA_STATUS_SIZE_REMAIN (0xffff)
677#define DMA_STATUS_INTR (0x1 << 30)
678#define DMA_STATUS_BUSY (0x1 << 31)
679
680#define DMA_FLAGS_ALIGNED (0x1 << 24)
681#define DMA_FLAGS_UNK26 (0x1 << 26)
682
683#define DMA_INCR_RANGE (0x7 << 16)
684#define DMA_INCR_RANGE_UNL (0x0 << 16)
685#define DMA_INCR_RANGE_FIXED (0x1 << 16)
686#define DMA_INCR_RANGE_ALTR (0x2 << 16)
687#define DMA_INCR_RANGE_4 (0x3 << 16)
688#define DMA_INCR_RANGE_8 (0x4 << 16)
689#define DMA_INCR_RANGE_16 (0x5 << 16)
690#define DMA_INCR_RANGE_32 (0x6 << 16)
691#define DMA_INCR_RANGE_64 (0x7 << 16)
692
693#define DMA_INCR_WIDTH (0x7 << 28)
694#define DMA_INCR_WIDTH_8BIT (0x0 << 28)
695#define DMA_INCR_WIDTH_16BIT (0x1 << 28)
696#define DMA_INCR_WIDTH_32BIT (0x2 << 28)
697/* All other values reserved? */
698
604#endif /* __PP5020_H__ */ 699#endif /* __PP5020_H__ */
diff --git a/firmware/target/arm/i2s-pp.c b/firmware/target/arm/i2s-pp.c
index 81c1fa1265..e4b9f8e1df 100644
--- a/firmware/target/arm/i2s-pp.c
+++ b/firmware/target/arm/i2s-pp.c
@@ -71,8 +71,8 @@ void i2s_reset(void)
71 IISCONFIG = ((IISCONFIG & ~IIS_FIFO_FORMAT_MASK) | IIS_FIFO_FORMAT_LE16_2); 71 IISCONFIG = ((IISCONFIG & ~IIS_FIFO_FORMAT_MASK) | IIS_FIFO_FORMAT_LE16_2);
72 72
73 /* RX_ATN_LVL = when 12 slots full */ 73 /* RX_ATN_LVL = when 12 slots full */
74 /* TX_ATN_LVL = when 12 slots empty */ 74 /* TX_ATN_LVL = DMA request when 4 slots empty */
75 IISFIFO_CFG |= IIS_RX_FULL_LVL_12 | IIS_TX_EMPTY_LVL_12; 75 IISFIFO_CFG |= IIS_RX_FULL_LVL_12 | IIS_TX_EMPTY_LVL_4;
76 76
77 /* Rx.CLR = 1, TX.CLR = 1 */ 77 /* Rx.CLR = 1, TX.CLR = 1 */
78 IISFIFO_CFG |= IIS_RXCLR | IIS_TXCLR; 78 IISFIFO_CFG |= IIS_RXCLR | IIS_TXCLR;
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 */
diff --git a/firmware/target/arm/system-pp502x.c b/firmware/target/arm/system-pp502x.c
index 10a7651f7b..2b6bd3f717 100644
--- a/firmware/target/arm/system-pp502x.c
+++ b/firmware/target/arm/system-pp502x.c
@@ -169,6 +169,7 @@ void ICODE_ATTR cpucache_flush(void)
169 { 169 {
170 CACHE_OPERATION |= CACHE_OP_FLUSH; 170 CACHE_OPERATION |= CACHE_OP_FLUSH;
171 while ((CACHE_CTL & CACHE_CTL_BUSY) != 0); 171 while ((CACHE_CTL & CACHE_CTL_BUSY) != 0);
172 nop; nop; nop; nop;
172 } 173 }
173} 174}
174 175
@@ -470,11 +471,6 @@ void system_init(void)
470 GPIOK_INT_EN = 0; 471 GPIOK_INT_EN = 0;
471 GPIOL_INT_EN = 0; 472 GPIOL_INT_EN = 0;
472 473
473#if defined(SANSA_E200) || defined(SANSA_C200) || defined(PHILIPS_SA9200)
474 /* outl(0x00000000, 0x6000b000); */
475 outl(inl(0x6000a000) | 0x80000000, 0x6000a000); /* Init DMA controller? */
476#endif
477
478#ifdef HAVE_ADJUSTABLE_CPU_FREQ 474#ifdef HAVE_ADJUSTABLE_CPU_FREQ
479#if NUM_CORES > 1 475#if NUM_CORES > 1
480 corelock_init(&cpufreq_cl); 476 corelock_init(&cpufreq_cl);
diff --git a/firmware/target/arm/usb-drv-arc.c b/firmware/target/arm/usb-drv-arc.c
index af97e3e174..6d519469a2 100644
--- a/firmware/target/arm/usb-drv-arc.c
+++ b/firmware/target/arm/usb-drv-arc.c
@@ -389,7 +389,7 @@ void usb_drv_reset(void)
389 REG_USBCMD |= USBCMD_CTRL_RESET; 389 REG_USBCMD |= USBCMD_CTRL_RESET;
390 while (REG_USBCMD & USBCMD_CTRL_RESET); 390 while (REG_USBCMD & USBCMD_CTRL_RESET);
391 391
392#if CONFIG_CPU == PP5022 || CONFIG_CPU == PP5024 392#if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5022 || CONFIG_CPU == PP5024
393 /* On a CPU which identifies as a PP5022, this 393 /* On a CPU which identifies as a PP5022, this
394 initialization must be done after USB is reset. 394 initialization must be done after USB is reset.
395 */ 395 */