summaryrefslogtreecommitdiff
path: root/firmware/target/arm/pp/pcm-pp.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/pp/pcm-pp.c')
-rw-r--r--firmware/target/arm/pp/pcm-pp.c627
1 files changed, 314 insertions, 313 deletions
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{