blob: 704296d40798caf779eaaf4626ecbebeb23b5cd1 [file] [log] [blame]
Barry Wardelldf0dc222006-12-18 01:52:21 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 by Michael Sevakis
11 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
Barry Wardelldf0dc222006-12-18 01:52:21 +000016 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include <stdlib.h>
22#include "system.h"
23#include "kernel.h"
24#include "logf.h"
25#include "audio.h"
Michael Sevakisc2d21062007-03-11 06:21:43 +000026#include "sound.h"
Bertrik Sikkena5d30292008-04-28 10:22:05 +000027#include "pcm.h"
Michael Sevakise69d5672008-12-12 11:01:07 +000028#include "pcm_sampr.h"
Michael Sevakisa2b67032011-06-29 06:37:04 +000029#include "pcm-internal.h"
Robert Kukla47cfed12008-01-20 12:59:23 +000030
Michael Sevakis6077e5b2007-10-06 22:27:27 +000031/** DMA **/
32
Michael Sevakis782aae42007-10-16 04:19:18 +000033#ifdef CPU_PP502x
Michael Sevakis856ca782007-10-07 06:10:08 +000034/* 16-bit, L-R packed into 32 bits with left in the least significant halfword */
35#define SAMPLE_SIZE 16
Michael Sevakis6c399b82009-02-19 20:40:03 +000036/* 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 */
Michael Sevakis6077e5b2007-10-06 22:27:27 +000048#else
Michael Sevakis856ca782007-10-07 06:10:08 +000049/* 32-bit, one left 32-bit sample followed by one right 32-bit sample */
50#define SAMPLE_SIZE 32
Barry Wardellf2c03e82006-12-18 02:37:35 +000051#endif
Barry Wardelldf0dc222006-12-18 01:52:21 +000052
Michael Sevakis6077e5b2007-10-06 22:27:27 +000053struct dma_data
54{
55/* NOTE: The order of size and p is important if you use assembler
56 optimised fiq handler, so don't change it. */
Michael Sevakis6c399b82009-02-19 20:40:03 +000057 union
58 {
59 unsigned long addr;
60 uint32_t *p16; /* For packed 16-16 stereo pairs */
61 uint16_t *p32; /* For individual samples converted to 32-bit */
62 };
Michael Sevakis6077e5b2007-10-06 22:27:27 +000063 size_t size;
64#if NUM_CORES > 1
65 unsigned core;
66#endif
67 int locked;
68 int state;
Michael Sevakis3fd073e2007-10-09 00:09:28 +000069};
Michael Sevakis6077e5b2007-10-06 22:27:27 +000070
71extern void *fiq_function;
72
73/* Dispatch to the proper handler and leave the main vector table alone */
74void fiq_handler(void) ICODE_ATTR __attribute__((naked));
75void fiq_handler(void)
76{
77 asm volatile (
Rafaël Carré45c74982010-06-11 04:41:36 +000078 "ldr pc, [pc, #-4] \n"
Rafaël Carré45c74982010-06-11 04:41:36 +000079 "fiq_function: \n"
80 ".word 0 \n"
Michael Sevakis6077e5b2007-10-06 22:27:27 +000081 );
82}
83
Michael Sevakis6c399b82009-02-19 20:40:03 +000084#ifdef HAVE_PCM_DMA_ADDRESS
85void * pcm_dma_addr(void *addr)
86{
87 if (addr != NULL && (unsigned long)addr < UNCACHED_BASE_ADDR)
88 addr = UNCACHED_ADDR(addr);
89 return addr;
90}
91#endif
92
Michael Sevakis6077e5b2007-10-06 22:27:27 +000093/* TODO: Get simultaneous recording and playback to work. Just needs some tweaking */
Barry Wardelldf0dc222006-12-18 01:52:21 +000094
95/****************************************************************************
96 ** Playback DMA transfer
97 **/
Michael Sevakis6c399b82009-02-19 20:40:03 +000098static struct dma_data dma_play_data IBSS_ATTR =
Barry Wardelldf0dc222006-12-18 01:52:21 +000099{
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000100 /* Initialize to a locked, stopped state */
Michael Sevakis6c399b82009-02-19 20:40:03 +0000101 { .addr = 0 },
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000102 .size = 0,
103#if NUM_CORES > 1
104 .core = 0x00,
Barry Wardelldf0dc222006-12-18 01:52:21 +0000105#endif
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000106 .locked = 0,
107 .state = 0
108};
Barry Wardelldf0dc222006-12-18 01:52:21 +0000109
Michael Sevakise69d5672008-12-12 11:01:07 +0000110void pcm_dma_apply_settings(void)
Peter D'Hoyeb297eb92008-09-29 20:50:26 +0000111{
Michael Sevakise69d5672008-12-12 11:01:07 +0000112 audiohw_set_frequency(pcm_fsel);
Barry Wardelldf0dc222006-12-18 01:52:21 +0000113}
114
Michael Sevakis6c399b82009-02-19 20:40:03 +0000115#if defined(CPU_PP502x)
116/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
117void ICODE_ATTR __attribute__((interrupt("FIQ"))) fiq_playback(void)
118{
Michael Sevakisa2b67032011-06-29 06:37:04 +0000119 bool new_buffer = false;
Michael Sevakis6c399b82009-02-19 20:40:03 +0000120 register size_t size;
121
122 DMA0_STATUS; /* Clear any pending interrupt */
123
124 size = (DMA0_CMD & 0xffff) + 4; /* Get size of trasfer that caused this
125 interrupt */
126 dma_play_data.addr += size;
127 dma_play_data.size -= size;
128
129 while (1)
130 {
131 if (dma_play_data.size > 0) {
132 size = MAX_DMA_CHUNK_SIZE;
133 /* Not at least MAX_DMA_CHUNK_SIZE left or there would be less
134 * than a FIFO's worth of data after this transfer? */
135 if (size + 16*4 > dma_play_data.size)
136 size = dma_play_data.size;
137
138 /* Set the new DMA values and activate channel */
139 DMA0_RAM_ADDR = dma_play_data.addr;
140 DMA0_CMD = DMA_PLAY_CONFIG | (size - 4) | DMA_CMD_START;
Michael Sevakisa2b67032011-06-29 06:37:04 +0000141
142 if (new_buffer)
143 pcm_play_dma_started_callback();
Michael Sevakis6c399b82009-02-19 20:40:03 +0000144 return;
145 }
146
Michael Sevakisa2b67032011-06-29 06:37:04 +0000147 new_buffer = true;
148
Michael Sevakis6c399b82009-02-19 20:40:03 +0000149 /* Buffer empty. Try to get more. */
Michael Sevakisd5699982010-05-24 16:42:32 +0000150 pcm_play_get_more_callback((void **)&dma_play_data.addr,
151 &dma_play_data.size);
Michael Sevakis6c399b82009-02-19 20:40:03 +0000152
Michael Sevakis44c15ee2009-03-07 05:21:58 +0000153 if (dma_play_data.size == 0) {
Michael Sevakisd5699982010-05-24 16:42:32 +0000154 /* No more data */
155 return;
Michael Sevakis6c399b82009-02-19 20:40:03 +0000156 }
157
158 if (dma_play_data.addr < UNCACHED_BASE_ADDR) {
159 /* Flush any pending cache writes */
160 dma_play_data.addr = UNCACHED_ADDR(dma_play_data.addr);
161 cpucache_flush();
162 }
163 }
Michael Sevakis6c399b82009-02-19 20:40:03 +0000164}
165#else
Jens Arnolda73b13c2008-04-18 18:52:11 +0000166/* ASM optimised FIQ handler. Checks for the minimum allowed loop cycles by
167 * 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
169 * the loop and do some further optimization. Right after the loops (source
170 * buffer -> IISFIFO) are done we need to check whether we have to exit FIQ
171 * handler (this must be done, if all free FIFO slots were filled) or we will
172 * have to get some new source data. Important information kept from former
173 * 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.
175 * 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.
177 * C version is still included below for reference and testing.
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000178 */
179#if 1
180void fiq_playback(void) ICODE_ATTR __attribute__((naked));
181void fiq_playback(void)
Barry Wardelldf0dc222006-12-18 01:52:21 +0000182{
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000183 /* r10 contains IISCONFIG address (set in crt0.S to minimise code in actual
184 * FIQ handler. r11 contains address of p (also set in crt0.S). Most other
185 * addresses we need are generated by using offsets with these two.
186 * r10 + 0x40 is IISFIFO_WR, and r10 + 0x0c is IISFIFO_CFG.
187 * r8 and r9 contains local copies of p and size respectively.
Andree Buschmannb90a7662008-04-13 17:03:24 +0000188 * r0-r3 and r12 is a working register.
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000189 */
190 asm volatile (
Michael Sevakisa2b67032011-06-29 06:37:04 +0000191 "stmfd sp!, { r0-r4, lr } \n" /* stack scratch regs and lr */
Peter D'Hoyeb297eb92008-09-29 20:50:26 +0000192
Michael Sevakisa2b67032011-06-29 06:37:04 +0000193 "mov r4, #0 \n" /* Was the callback called? */
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000194#if CONFIG_CPU == PP5002
195 "ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux */
196 "ldr r12, [r12] \n"
197#endif
198 "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */
199 "cmp r9, #0 \n" /* is size 0? */
200 "beq .more_data \n" /* if so, ask pcmbuf for more data */
Andree Buschmannb90a7662008-04-13 17:03:24 +0000201
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000202#if SAMPLE_SIZE == 16
Andree Buschmannb90a7662008-04-13 17:03:24 +0000203 ".check_fifo: \n"
204 "ldr r0, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */
205 "and r0, r0, %[mask] \n" /* r0 = IIS_TX_FREE_COUNT << 16 (PP502x) */
Peter D'Hoyeb297eb92008-09-29 20:50:26 +0000206
207 "mov r1, r0, lsr #16 \n" /* number of free FIFO slots */
Andree Buschmannb90a7662008-04-13 17:03:24 +0000208 "cmp r1, r9, lsr #2 \n" /* number of words from source */
209 "movgt r1, r9, lsr #2 \n" /* r1 = amount of allowed loops */
210 "sub r9, r9, r1, lsl #2 \n" /* r1 words will be written in following loop */
Peter D'Hoyeb297eb92008-09-29 20:50:26 +0000211
Andree Buschmannb90a7662008-04-13 17:03:24 +0000212 "subs r1, r1, #2 \n"
213 ".fifo_loop_2: \n"
214 "ldmgeia r8!, {r2, r12} \n" /* load four samples */
215 "strge r2 , [r10, %[wr]] \n" /* write sample 0-1 to IISFIFO_WR */
216 "strge r12, [r10, %[wr]] \n" /* write sample 2-3 to IISFIFO_WR */
217 "subges r1, r1, #2 \n" /* one more loop? */
218 "bge .fifo_loop_2 \n" /* yes, continue */
Peter D'Hoyeb297eb92008-09-29 20:50:26 +0000219
Andree Buschmannb90a7662008-04-13 17:03:24 +0000220 "tst r1, #1 \n" /* two samples (one word) left? */
221 "ldrne r12, [r8], #4 \n" /* load two samples */
222 "strne r12, [r10, %[wr]] \n" /* write sample 0-1 to IISFIFO_WR */
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000223#elif SAMPLE_SIZE == 32
Peter D'Hoyeb297eb92008-09-29 20:50:26 +0000224 ".check_fifo: \n"
Andree Buschmannb90a7662008-04-13 17:03:24 +0000225 "ldr r0, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */
226 "and r0, r0, %[mask] \n" /* r0 = IIS_TX_FREE_COUNT << 23 (PP5002) */
Jens Arnolda73b13c2008-04-18 18:52:11 +0000227
228 "movs r1, r0, lsr #24 \n" /* number of free pairs of FIFO slots */
Michael Sevakisa2b67032011-06-29 06:37:04 +0000229 "beq .fifo_fill_complete \n" /* no complete pair? -> exit */
Andree Buschmannb90a7662008-04-13 17:03:24 +0000230 "cmp r1, r9, lsr #2 \n" /* number of words from source */
231 "movgt r1, r9, lsr #2 \n" /* r1 = amount of allowed loops */
232 "sub r9, r9, r1, lsl #2 \n" /* r1 words will be written in following loop */
Peter D'Hoyeb297eb92008-09-29 20:50:26 +0000233
Andree Buschmannb90a7662008-04-13 17:03:24 +0000234 ".fifo_loop: \n"
Michael Sevakis856ca782007-10-07 06:10:08 +0000235 "ldr r12, [r8], #4 \n" /* load two samples */
Andree Buschmannb90a7662008-04-13 17:03:24 +0000236 "mov r2 , r12, lsl #16 \n" /* put left sample at the top bits */
237 "str r2 , [r10, %[wr]] \n" /* write top sample to IISFIFO_WR */
238 "str r12, [r10, %[wr]] \n" /* write low sample to IISFIFO_WR*/
239 "subs r1, r1, #1 \n" /* one more loop? */
240 "bgt .fifo_loop \n" /* yes, continue */
Peter D'Hoyeb297eb92008-09-29 20:50:26 +0000241
Michael Sevakisa2b67032011-06-29 06:37:04 +0000242 ".fifo_fill_complete: \n"
243#endif
244 "cmp r4, #0 \n" /* If fill came after get_more... */
245 "beq .still_old_buffer \n"
246 "mov r4, #0 \n"
247 "ldr r2, =pcm_play_dma_started \n"
248 "ldrne r2, [r2] \n"
249 "cmp r2, #0 \n"
250 "movne lr, pc \n"
251 "bxne r2 \n"
252
253 ".still_old_buffer: \n"
Andree Buschmannb90a7662008-04-13 17:03:24 +0000254 "cmp r9, #0 \n" /* either FIFO is full or source buffer is empty */
255 "bgt .exit \n" /* if source buffer is not empty, FIFO must be full */
Peter D'Hoyeb297eb92008-09-29 20:50:26 +0000256
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000257 ".more_data: \n"
Michael Sevakisa2b67032011-06-29 06:37:04 +0000258 "mov r4, #1 \n" /* Remember we did this */
Michael Sevakisd5699982010-05-24 16:42:32 +0000259 "ldr r2, =pcm_play_get_more_callback \n"
Michael Sevakis2e8266d2008-12-11 01:48:31 +0000260 "mov r0, r11 \n" /* r0 = &p */
261 "add r1, r11, #4 \n" /* r1 = &size */
Michael Sevakisd5699982010-05-24 16:42:32 +0000262 "mov lr, pc \n" /* call pcm_play_get_more_callback */
Michael Sevakis2e8266d2008-12-11 01:48:31 +0000263 "bx r2 \n"
Michael Sevakisd5699982010-05-24 16:42:32 +0000264 "ldmia r11, { r8-r9 } \n" /* load new p and size */
265 "cmp r9, #0 \n"
266 "bne .check_fifo \n" /* size != 0? refill */
Michael Sevakis2e8266d2008-12-11 01:48:31 +0000267
Michael Sevakisd5699982010-05-24 16:42:32 +0000268 ".exit: \n" /* (r9=0 if stopping, look above) */
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000269 "stmia r11, { r8-r9 } \n" /* save p and size */
Michael Sevakisa2b67032011-06-29 06:37:04 +0000270 "ldmfd sp!, { r0-r4, lr } \n"
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000271 "subs pc, lr, #4 \n" /* FIQ specific return sequence */
272 ".ltorg \n"
273 : /* These must only be integers! No regs */
Andree Buschmannb90a7662008-04-13 17:03:24 +0000274 : [mask]"i"(IIS_TX_FREE_MASK),
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000275 [cfg]"i"((int)&IISFIFO_CFG - (int)&IISCONFIG),
276 [wr]"i"((int)&IISFIFO_WR - (int)&IISCONFIG)
277 );
278}
279#else /* C version for reference */
280void fiq_playback(void) __attribute__((interrupt ("FIQ"))) ICODE_ATTR;
281/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
282void fiq_playback(void)
283{
Michael Sevakisa2b67032011-06-29 06:37:04 +0000284 bool new_buffer = false;
285
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000286#if CONFIG_CPU == PP5002
287 inl(0xcf001040);
288#endif
289
290 do {
291 while (dma_play_data.size > 0) {
292 if (IIS_TX_FREE_COUNT < 2) {
Michael Sevakisa2b67032011-06-29 06:37:04 +0000293 if (new_buffer) {
294 new_buffer = false;
295 pcm_play_dma_started_callback();
296 }
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000297 return;
298 }
299#if SAMPLE_SIZE == 16
Michael Sevakis6c399b82009-02-19 20:40:03 +0000300 IISFIFO_WR = *dma_play_data.p16++;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000301#elif SAMPLE_SIZE == 32
Michael Sevakis6c399b82009-02-19 20:40:03 +0000302 IISFIFO_WR = *dma_play_data.p32++ << 16;
303 IISFIFO_WR = *dma_play_data.p32++ << 16;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000304#endif
305 dma_play_data.size -= 4;
306 }
307
Michael Sevakisa2b67032011-06-29 06:37:04 +0000308 if (new_buffer) {
309 new_buffer = false;
310 pcm_play_dma_started_callback();
311 }
312
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000313 /* p is empty, get some more data */
Michael Sevakisd5699982010-05-24 16:42:32 +0000314 pcm_play_get_more_callback((void **)&dma_play_data.addr,
315 &dma_play_data.size);
Michael Sevakisa2b67032011-06-29 06:37:04 +0000316 new_buffer = true;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000317 } while (dma_play_data.size);
318
Michael Sevakisd5699982010-05-24 16:42:32 +0000319 /* No more data */
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000320}
321#endif /* ASM / C selection */
Michael Sevakis6c399b82009-02-19 20:40:03 +0000322#endif /* CPU_PP502x */
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000323
324/* For the locks, FIQ must be disabled because the handler manipulates
325 IISCONFIG and the operation is not atomic - dual core support
326 will require other measures */
327void pcm_play_lock(void)
328{
Michael Sevakisaf395f42008-03-26 01:50:41 +0000329 int status = disable_fiq_save();
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000330
331 if (++dma_play_data.locked == 1) {
Michael Sevakis6c399b82009-02-19 20:40:03 +0000332#ifdef CPU_PP502x
Michael Sevakisb1dccc42009-02-23 04:33:33 +0000333 CPU_INT_DIS = DMA_MASK;
Michael Sevakis6c399b82009-02-19 20:40:03 +0000334#else
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000335 IIS_IRQTX_REG &= ~IIS_IRQTX;
Michael Sevakis6c399b82009-02-19 20:40:03 +0000336#endif
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000337 }
338
Michael Sevakisaf395f42008-03-26 01:50:41 +0000339 restore_fiq(status);
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000340}
341
342void pcm_play_unlock(void)
343{
Michael Sevakisaf395f42008-03-26 01:50:41 +0000344 int status = disable_fiq_save();
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000345
346 if (--dma_play_data.locked == 0 && dma_play_data.state != 0) {
Michael Sevakis6c399b82009-02-19 20:40:03 +0000347#ifdef CPU_PP502x
Michael Sevakisb1dccc42009-02-23 04:33:33 +0000348 CPU_INT_EN = DMA_MASK;
Michael Sevakis6c399b82009-02-19 20:40:03 +0000349#else
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000350 IIS_IRQTX_REG |= IIS_IRQTX;
Michael Sevakis6c399b82009-02-19 20:40:03 +0000351#endif
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000352 }
353
Michael Sevakisaf395f42008-03-26 01:50:41 +0000354 restore_fiq(status);
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000355}
356
357static void play_start_pcm(void)
358{
359 fiq_function = fiq_playback;
Michael Sevakis2e8266d2008-12-11 01:48:31 +0000360
Michael Sevakis6c399b82009-02-19 20:40:03 +0000361#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;
Michael Sevakis44c15ee2009-03-07 05:21:58 +0000365 if (size + 16*4 > dma_play_data.size)
Michael Sevakis6c399b82009-02-19 20:40:03 +0000366 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;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000370 dma_play_data.state = 1;
Michael Sevakis6c399b82009-02-19 20:40:03 +0000371#else
372 IISCONFIG &= ~IIS_TXFIFOEN; /* Stop transmitting */
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000373
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 */
Michael Sevakis6c399b82009-02-19 20:40:03 +0000378 dma_play_data.state = 1;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000379 return;
380 }
381
382#if SAMPLE_SIZE == 16
Michael Sevakis6c399b82009-02-19 20:40:03 +0000383 IISFIFO_WR = *dma_play_data.p16++;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000384#elif SAMPLE_SIZE == 32
Michael Sevakis6c399b82009-02-19 20:40:03 +0000385 IISFIFO_WR = *dma_play_data.p32++ << 16;
386 IISFIFO_WR = *dma_play_data.p32++ << 16;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000387#endif
388 dma_play_data.size -= 4;
389 }
Michael Sevakis6c399b82009-02-19 20:40:03 +0000390#endif
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000391}
392
393static void play_stop_pcm(void)
394{
Michael Sevakis6c399b82009-02-19 20:40:03 +0000395#ifdef CPU_PP502x
Michael Sevakis44c15ee2009-03-07 05:21:58 +0000396 unsigned long status = DMA0_STATUS; /* Snapshot- resume from this point */
397 unsigned long cmd = DMA0_CMD;
398 size_t size = 0;
Michael Sevakis6c399b82009-02-19 20:40:03 +0000399
400 /* Stop transfer */
Michael Sevakis44c15ee2009-03-07 05:21:58 +0000401 DMA0_CMD = cmd & ~(DMA_CMD_START | DMA_CMD_INTR);
Michael Sevakis6c399b82009-02-19 20:40:03 +0000402
403 /* Wait for not busy + clear int */
404 while (DMA0_STATUS & (DMA_STATUS_BUSY | DMA_STATUS_INTR));
405
Michael Sevakis44c15ee2009-03-07 05:21:58 +0000406 if (status & DMA_STATUS_BUSY) {
Michael Sevakis6c399b82009-02-19 20:40:03 +0000407 /* Transfer was interrupted - leave what's left */
Michael Sevakis44c15ee2009-03-07 05:21:58 +0000408 size = (cmd & 0xfffc) - (status & 0xfffc);
Michael Sevakis6c399b82009-02-19 20:40:03 +0000409 }
Michael Sevakis44c15ee2009-03-07 05:21:58 +0000410 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;
Michael Sevakis6c399b82009-02-19 20:40:03 +0000415 }
Michael Sevakis44c15ee2009-03-07 05:21:58 +0000416 /* 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. */
Michael Sevakis6c399b82009-02-19 20:40:03 +0000423#else
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000424 /* Disable TX interrupt */
425 IIS_IRQTX_REG &= ~IIS_IRQTX;
Michael Sevakis6c399b82009-02-19 20:40:03 +0000426#endif
Michael Sevakis2e8266d2008-12-11 01:48:31 +0000427
428 /* Wait for FIFO to empty */
Michael Sevakise69d5672008-12-12 11:01:07 +0000429 while (!IIS_TX_IS_EMPTY);
Michael Sevakis2e8266d2008-12-11 01:48:31 +0000430
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000431 dma_play_data.state = 0;
432}
433
434void pcm_play_dma_start(const void *addr, size_t size)
435{
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000436#if NUM_CORES > 1
437 /* This will become more important later - and different ! */
438 dma_play_data.core = processor_id(); /* save initiating core */
439#endif
440
Michael Sevakis6c399b82009-02-19 20:40:03 +0000441 pcm_play_dma_stop();
442
Michael Sevakis6c399b82009-02-19 20:40:03 +0000443#ifdef CPU_PP502x
444 if ((unsigned long)addr < UNCACHED_BASE_ADDR) {
445 /* Flush any pending cache writes */
446 addr = UNCACHED_ADDR(addr);
447 cpucache_flush();
448 }
449
450 dma_play_data.addr = (unsigned long)addr;
451 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;
Michael Sevakiscc7457e2010-05-12 14:31:12 +0000455#else
456 dma_play_data.addr = (unsigned long)addr;
457 dma_play_data.size = size;
Michael Sevakis6c399b82009-02-19 20:40:03 +0000458#endif
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000459
460 play_start_pcm();
461}
462
463/* Stops the DMA transfer and interrupt */
464void pcm_play_dma_stop(void)
465{
466 play_stop_pcm();
Michael Sevakis6c399b82009-02-19 20:40:03 +0000467 dma_play_data.addr = 0;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000468 dma_play_data.size = 0;
469#if NUM_CORES > 1
470 dma_play_data.core = 0; /* no core in control */
471#endif
472}
473
474void pcm_play_dma_pause(bool pause)
475{
476 if (pause) {
477 play_stop_pcm();
478 } else {
479 play_start_pcm();
480 }
481}
482
483size_t pcm_get_bytes_waiting(void)
484{
485 return dma_play_data.size & ~3;
486}
487
488void pcm_play_dma_init(void)
489{
Michael Sevakis6c399b82009-02-19 20:40:03 +0000490 /* Initialize default register values. */
491 audiohw_init();
492
493#ifdef CPU_PP502x
494 /* Enable DMA controller */
495 DMA_MASTER_CONTROL |= DMA_MASTER_CONTROL_EN;
Michael Sevakisb1dccc42009-02-23 04:33:33 +0000496 /* FIQ priority for DMA */
497 CPU_INT_PRIORITY |= DMA_MASK;
Michael Sevakis6c399b82009-02-19 20:40:03 +0000498 /* 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
Michael Sevakis2e8266d2008-12-11 01:48:31 +0000504 /* 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
Michael Sevakis6c399b82009-02-19 20:40:03 +0000522 /* FIQ priority for I2S */
523 CPU_INT_PRIORITY |= IIS_MASK;
524 CPU_INT_EN = IIS_MASK;
Michael Sevakis15b73212007-10-02 07:48:50 +0000525#endif
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000526
527 IISCONFIG |= IIS_TXFIFOEN;
Barry Wardelldf0dc222006-12-18 01:52:21 +0000528}
Michael Sevakisc2d21062007-03-11 06:21:43 +0000529
530void pcm_postinit(void)
531{
532 audiohw_postinit();
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000533}
534
535const void * pcm_play_dma_get_peak_buffer(int *count)
536{
Michael Sevakis6c399b82009-02-19 20:40:03 +0000537 unsigned long addr, size;
538
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;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000545 return (void *)((addr + 2) & ~3);
Michael Sevakisc2d21062007-03-11 06:21:43 +0000546}
Barry Wardelldf0dc222006-12-18 01:52:21 +0000547
548/****************************************************************************
549 ** Recording DMA transfer
550 **/
Barry Wardellf2c03e82006-12-18 02:37:35 +0000551#ifdef HAVE_RECORDING
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000552/* PCM recording interrupt routine lockout */
Michael Sevakis6c399b82009-02-19 20:40:03 +0000553static struct dma_data dma_rec_data IBSS_ATTR =
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000554{
555 /* Initialize to a locked, stopped state */
Michael Sevakis6c399b82009-02-19 20:40:03 +0000556 { .addr = 0 },
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000557 .size = 0,
558#if NUM_CORES > 1
559 .core = 0x00,
560#endif
561 .locked = 0,
562 .state = 0
563};
Michael Sevakisbcb8a882007-06-05 07:03:30 +0000564
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000565/* For the locks, FIQ must be disabled because the handler manipulates
566 IISCONFIG and the operation is not atomic - dual core support
567 will require other measures */
568void pcm_rec_lock(void)
569{
Michael Sevakisaf395f42008-03-26 01:50:41 +0000570 int status = disable_fiq_save();
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000571
572 if (++dma_rec_data.locked == 1)
573 IIS_IRQRX_REG &= ~IIS_IRQRX;
574
Michael Sevakisaf395f42008-03-26 01:50:41 +0000575 restore_fiq(status);
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000576}
577
578void pcm_rec_unlock(void)
579{
Michael Sevakisaf395f42008-03-26 01:50:41 +0000580 int status = disable_fiq_save();
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000581
582 if (--dma_rec_data.locked == 0 && dma_rec_data.state != 0)
583 IIS_IRQRX_REG |= IIS_IRQRX;
584
Michael Sevakisaf395f42008-03-26 01:50:41 +0000585 restore_fiq(status);
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000586}
587
588/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
589void fiq_record(void) ICODE_ATTR __attribute__((interrupt ("FIQ")));
590
591#if defined(SANSA_C200) || defined(SANSA_E200)
Michael Sevakisbcb8a882007-06-05 07:03:30 +0000592void fiq_record(void)
593{
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000594 register int32_t value;
Michael Sevakisbcb8a882007-06-05 07:03:30 +0000595
596 if (audio_channels == 2) {
597 /* RX is stereo */
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000598 while (dma_rec_data.size > 0) {
599 if (IIS_RX_FULL_COUNT < 2) {
600 return;
Michael Sevakisbcb8a882007-06-05 07:03:30 +0000601 }
602
603 /* Discard every other sample since ADC clock is 1/2 LRCK */
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000604 value = IISFIFO_RD;
605 IISFIFO_RD;
Michael Sevakisbcb8a882007-06-05 07:03:30 +0000606
Michael Sevakis6c399b82009-02-19 20:40:03 +0000607 *dma_rec_data.p16++ = value;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000608 dma_rec_data.size -= 4;
Michael Sevakisbcb8a882007-06-05 07:03:30 +0000609
610 /* TODO: Figure out how to do IIS loopback */
611 if (audio_output_source != AUDIO_SRC_PLAYBACK) {
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000612 if (IIS_TX_FREE_COUNT >= 16) {
Michael Sevakis84d28e82007-10-04 12:55:46 +0000613 /* Resync the output FIFO - it ran dry */
614 IISFIFO_WR = 0;
615 IISFIFO_WR = 0;
616 }
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000617 IISFIFO_WR = value;
618 IISFIFO_WR = value;
Michael Sevakisbcb8a882007-06-05 07:03:30 +0000619 }
620 }
621 }
622 else {
623 /* RX is left channel mono */
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000624 while (dma_rec_data.size > 0) {
625 if (IIS_RX_FULL_COUNT < 2) {
626 return;
Michael Sevakisbcb8a882007-06-05 07:03:30 +0000627 }
628
629 /* Discard every other sample since ADC clock is 1/2 LRCK */
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000630 value = IISFIFO_RD;
631 IISFIFO_RD;
632
633 value = (uint16_t)value | (value << 16);
634
Michael Sevakis6c399b82009-02-19 20:40:03 +0000635 *dma_rec_data.p16++ = value;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000636 dma_rec_data.size -= 4;
Michael Sevakisbcb8a882007-06-05 07:03:30 +0000637
638 if (audio_output_source != AUDIO_SRC_PLAYBACK) {
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000639 if (IIS_TX_FREE_COUNT >= 16) {
Michael Sevakis84d28e82007-10-04 12:55:46 +0000640 /* Resync the output FIFO - it ran dry */
641 IISFIFO_WR = 0;
642 IISFIFO_WR = 0;
643 }
644
Michael Sevakis6c399b82009-02-19 20:40:03 +0000645 value = *((int32_t *)dma_rec_data.p16 - 1);
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000646 IISFIFO_WR = value;
647 IISFIFO_WR = value;
Michael Sevakisbcb8a882007-06-05 07:03:30 +0000648 }
649 }
650 }
651
Michael Sevakisd5699982010-05-24 16:42:32 +0000652 pcm_rec_more_ready_callback(0, (void *)&dma_rec_data.addr,
653 &dma_rec_data.size);
Michael Sevakisbcb8a882007-06-05 07:03:30 +0000654}
655
656#else
Barry Wardelldf0dc222006-12-18 01:52:21 +0000657void fiq_record(void)
658{
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000659 while (dma_rec_data.size > 0) {
660 if (IIS_RX_FULL_COUNT < 2) {
661 return;
Barry Wardelldf0dc222006-12-18 01:52:21 +0000662 }
Michael Sevakisbcb8a882007-06-05 07:03:30 +0000663
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000664#if SAMPLE_SIZE == 16
Michael Sevakis6c399b82009-02-19 20:40:03 +0000665 *dma_rec_data.p16++ = IISFIFO_RD;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000666#elif SAMPLE_SIZE == 32
Michael Sevakis6c399b82009-02-19 20:40:03 +0000667 *dma_rec_data.p32++ = IISFIFO_RD >> 16;
668 *dma_rec_data.p32++ = IISFIFO_RD >> 16;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000669#endif
670 dma_rec_data.size -= 4;
Barry Wardelldf0dc222006-12-18 01:52:21 +0000671 }
Daniel Ankersdfad4062007-03-11 17:38:08 +0000672
Michael Sevakisd5699982010-05-24 16:42:32 +0000673 pcm_rec_more_ready_callback(0, (void *)&dma_rec_data.addr,
674 &dma_rec_data.size);
Barry Wardelldf0dc222006-12-18 01:52:21 +0000675}
676
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000677#endif /* SANSA_E200 */
Michael Sevakisbcb8a882007-06-05 07:03:30 +0000678
Barry Wardelldf0dc222006-12-18 01:52:21 +0000679void pcm_rec_dma_stop(void)
680{
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000681 /* disable interrupt */
682 IIS_IRQRX_REG &= ~IIS_IRQRX;
Daniel Ankersdfad4062007-03-11 17:38:08 +0000683
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000684 dma_rec_data.state = 0;
685 dma_rec_data.size = 0;
686#if NUM_CORES > 1
687 dma_rec_data.core = 0x00;
688#endif
Daniel Ankersdfad4062007-03-11 17:38:08 +0000689
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000690 /* disable fifo */
691 IISCONFIG &= ~IIS_RXFIFOEN;
692 IISFIFO_CFG |= IIS_RXCLR;
Barry Wardelldf0dc222006-12-18 01:52:21 +0000693}
694
695void pcm_rec_dma_start(void *addr, size_t size)
696{
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000697 pcm_rec_dma_stop();
Barry Wardelldf0dc222006-12-18 01:52:21 +0000698
Michael Sevakis6c399b82009-02-19 20:40:03 +0000699 dma_rec_data.addr = (unsigned long)addr;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000700 dma_rec_data.size = size;
701#if NUM_CORES > 1
702 /* This will become more important later - and different ! */
703 dma_rec_data.core = processor_id(); /* save initiating core */
Michael Sevakisbcb8a882007-06-05 07:03:30 +0000704#endif
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000705 /* setup FIQ handler */
706 fiq_function = fiq_record;
Michael Sevakisbcb8a882007-06-05 07:03:30 +0000707
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000708 /* interrupt on full fifo, enable record fifo interrupt */
709 dma_rec_data.state = 1;
Daniel Ankersdfad4062007-03-11 17:38:08 +0000710
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000711 /* enable RX FIFO */
712 IISCONFIG |= IIS_RXFIFOEN;
Barry Wardelldf0dc222006-12-18 01:52:21 +0000713
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000714 /* enable IIS interrupt as FIQ */
715 CPU_INT_PRIORITY |= IIS_MASK;
716 CPU_INT_EN = IIS_MASK;
Barry Wardelldf0dc222006-12-18 01:52:21 +0000717}
718
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000719void pcm_rec_dma_close(void)
Barry Wardelldf0dc222006-12-18 01:52:21 +0000720{
Barry Wardelldf0dc222006-12-18 01:52:21 +0000721 pcm_rec_dma_stop();
Barry Wardelldf0dc222006-12-18 01:52:21 +0000722} /* pcm_close_recording */
723
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000724void pcm_rec_dma_init(void)
Barry Wardelldf0dc222006-12-18 01:52:21 +0000725{
Barry Wardelldf0dc222006-12-18 01:52:21 +0000726 pcm_rec_dma_stop();
727} /* pcm_init */
728
Michael Sevakis4d041322010-05-12 14:05:36 +0000729const void * pcm_rec_dma_get_peak_buffer(void)
Barry Wardelldf0dc222006-12-18 01:52:21 +0000730{
Michael Sevakis4d041322010-05-12 14:05:36 +0000731 return (void *)((unsigned long)dma_rec_data.addr & ~3);
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000732} /* pcm_rec_dma_get_peak_buffer */
Michael Sevakisbcb8a882007-06-05 07:03:30 +0000733
Michael Sevakisbcb8a882007-06-05 07:03:30 +0000734#endif /* HAVE_RECORDING */