summaryrefslogtreecommitdiff
path: root/firmware/target/arm/pp/pcm-pp.c
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/pp/pcm-pp.c
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/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{