diff options
Diffstat (limited to 'firmware/target/arm/pp')
-rw-r--r-- | firmware/target/arm/pp/pcm-pp.c | 627 |
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 | |||
53 | struct dma_data | 33 | struct 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 */ |
117 | void 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 | |||
112 | static 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 | |||
125 | static 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 | |||
133 | static 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 | |||
146 | static 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 | |||
162 | static 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 | |||
193 | static inline void dma_tx_lock(void) | ||
194 | { | ||
195 | CPU_INT_DIS = DMA_MASK; | ||
196 | } | ||
197 | |||
198 | static 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 */ | ||
204 | void fiq_playback(void) ICODE_ATTR __attribute__((interrupt("FIQ"))); | ||
205 | void 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) { | 231 | static 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 | |||
256 | static inline void dma_tx_setup(void) | ||
257 | { | ||
258 | /* Nothing to do */ | ||
259 | } | ||
260 | |||
261 | static inline unsigned long dma_tx_buf_prepare(const void *addr) | ||
262 | { | ||
263 | return (unsigned long)addr; | ||
264 | } | ||
265 | |||
266 | static 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 | |||
284 | static inline void dma_tx_stop(void) | ||
285 | { | ||
286 | /* Disable TX interrupt */ | ||
287 | IIS_IRQTX_REG &= ~IIS_IRQTX; | ||
288 | } | ||
289 | |||
290 | static inline void dma_tx_lock(void) | ||
291 | { | ||
292 | IIS_IRQTX_REG &= ~IIS_IRQTX; | ||
293 | } | ||
294 | |||
295 | static 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 |
180 | void fiq_playback(void) ICODE_ATTR __attribute__((naked)); | 314 | void fiq_playback(void) ICODE_ATTR __attribute__((naked)); |
181 | void fiq_playback(void) | 315 | void 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 */ |
280 | void 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 */ |
404 | void fiq_playback(void) ICODE_ATTR __attribute__((interrupt ("FIQ"))); | ||
282 | void fiq_playback(void) | 405 | void 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 | ||
342 | void pcm_play_unlock(void) | 445 | void 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 | ||
357 | static void play_start_pcm(void) | 456 | static 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 | ||
393 | static void play_stop_pcm(void) | 463 | static 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 | ||
434 | void pcm_play_dma_start(const void *addr, size_t size) | 473 | void 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 | |||
657 | void fiq_record(void) | 655 | void 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 | ||
679 | void pcm_rec_dma_stop(void) | 680 | void pcm_rec_dma_stop(void) |
680 | { | 681 | { |