summaryrefslogtreecommitdiff
path: root/firmware/target/arm/pcm-pp.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/pcm-pp.c')
-rw-r--r--firmware/target/arm/pcm-pp.c846
1 files changed, 336 insertions, 510 deletions
diff --git a/firmware/target/arm/pcm-pp.c b/firmware/target/arm/pcm-pp.c
index f38757ec6c..50e5b272aa 100644
--- a/firmware/target/arm/pcm-pp.c
+++ b/firmware/target/arm/pcm-pp.c
@@ -23,329 +23,286 @@
23#include "audio.h" 23#include "audio.h"
24#include "sound.h" 24#include "sound.h"
25 25
26/* peaks */ 26/** DMA **/
27#ifdef HAVE_RECORDING 27
28static unsigned long *rec_peak_addr; 28#if defined(HAVE_AS3514)
29static int rec_peak_left, rec_peak_right; 29/* E200 uses 16bit FIFO - all others should be able to as well -
30 i2s-pp.c has to set the right size as well */
31#define SAMPLE_SIZE 16
32#else
33#define SAMPLE_SIZE 32
30#endif 34#endif
31 35
32/** DMA **/ 36struct dma_data
33#ifdef CPU_PP502x 37{
34#define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x3f000000) >> 24) 38/* NOTE: The order of size and p is important if you use assembler
35#elif CONFIG_CPU == PP5002 39 optimised fiq handler, so don't change it. */
36#define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x7800000) >> 23) 40#if SAMPLE_SIZE == 16
41 uint32_t *p;
42#elif SAMPLE_SIZE == 32
43 uint16_t *p;
37#endif 44#endif
45 size_t size;
46#if NUM_CORES > 1
47 unsigned core;
48#endif
49 int locked;
50 int state;
51} __attribute__((packed));
52
53extern void *fiq_function;
54
55/* Dispatch to the proper handler and leave the main vector table alone */
56void fiq_handler(void) ICODE_ATTR __attribute__((naked));
57void fiq_handler(void)
58{
59 asm volatile (
60 "ldr pc, [pc, #-4] \n"
61 "fiq_function: \n"
62 ".word 0 \n"
63 );
64}
65
66/* TODO: Get simultaneous recording and playback to work. Just needs some tweaking */
38 67
39/**************************************************************************** 68/****************************************************************************
40 ** Playback DMA transfer 69 ** Playback DMA transfer
41 **/ 70 **/
42static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ 71struct dma_data dma_play_data NOCACHEBSS_ATTR =
72{
73 /* Initialize to a locked, stopped state */
74 .p = NULL,
75 .size = 0,
76#if NUM_CORES > 1
77 .core = 0x00,
78#endif
79 .locked = 0,
80 .state = 0
81};
43 82
44/* NOTE: The order of these two variables is important if you use the iPod 83static unsigned long pcm_freq NOCACHEDATA_ATTR = HW_SAMPR_DEFAULT; /* 44.1 is default */
45 assembler optimised fiq handler, so don't change it. */ 84
46unsigned short* p IBSS_ATTR; 85void pcm_set_frequency(unsigned int frequency)
47size_t p_size IBSS_ATTR; 86{
87 (void)frequency;
88 pcm_freq = HW_SAMPR_DEFAULT;
89}
90
91void pcm_apply_settings(void)
92{
93 pcm_curr_sampr = pcm_freq;
94}
48 95
49/* ASM optimised FIQ handler. GCC fails to make use of the fact that FIQ mode 96/* ASM optimised FIQ handler. GCC fails to make use of the fact that FIQ mode
50 has registers r8-r14 banked, and so does not need to be saved. This routine 97 has registers r8-r14 banked, and so does not need to be saved. This routine
51 uses only these registers, and so will never touch the stack unless it 98 uses only these registers, and so will never touch the stack unless it
52 actually needs to do so when calling pcm_callback_for_more. C version is 99 actually needs to do so when calling pcm_callback_for_more. C version is
53 still included below for reference. 100 still included below for reference and testing.
54 */ 101 */
55#if 1 102#if 1
56void fiq(void) ICODE_ATTR __attribute__((naked)); 103void fiq_playback(void) ICODE_ATTR __attribute__((naked));
57void fiq(void) 104void fiq_playback(void)
58{ 105{
59 /* r12 contains IISCONFIG address (set in crt0.S to minimise code in actual 106 /* r10 contains IISCONFIG address (set in crt0.S to minimise code in actual
60 * FIQ handler. r11 contains address of p (also set in crt0.S). Most other 107 * FIQ handler. r11 contains address of p (also set in crt0.S). Most other
61 * addresses we need are generated by using offsets with these two. 108 * addresses we need are generated by using offsets with these two.
62 * r12 + 0x40 is IISFIFO_WR, and r12 + 0x0c is IISFIFO_CFG. 109 * r10 + 0x40 is IISFIFO_WR, and r10 + 0x0c is IISFIFO_CFG.
63 * r8 and r9 contains local copies of p_size and p respectively. 110 * r8 and r9 contains local copies of p and size respectively.
64 * r10 is a working register. 111 * r12 is a working register.
65 */ 112 */
66 asm volatile ( 113 asm volatile (
67#if CONFIG_CPU == PP5002 114#if CONFIG_CPU == PP5002
68 "ldr r10, =0xcf001040 \n\t" /* Some magic from iPodLinux */ 115 "ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux */
69 "ldr r10, [r10] \n\t" 116 "ldr r12, [r12] \n"
70 "ldr r10, [r12, #0x1c]\n\t"
71 "bic r10, r10, #0x200 \n\t" /* clear interrupt */
72 "str r10, [r12, #0x1c]\n\t"
73#else
74 "ldr r10, [r12] \n\t"
75 "bic r10, r10, #0x2 \n\t" /* clear interrupt */
76 "str r10, [r12] \n\t"
77#endif
78 "ldr r8, [r11, #4] \n\t" /* r8 = p_size */
79 "ldr r9, [r11] \n\t" /* r9 = p */
80 ".loop: \n\t"
81 "cmp r8, #0 \n\t" /* is p_size 0? */
82 "beq .more_data \n\t" /* if so, ask pcmbuf for more data */
83 ".fifo_loop: \n\t"
84#if CONFIG_CPU == PP5002
85 "ldr r10, [r12, #0x1c]\n\t" /* read IISFIFO_CFG to check FIFO status */
86 "and r10, r10, #0x7800000\n\t"
87 "cmp r10, #0x800000 \n\t"
88#else
89 "ldr r10, [r12, #0x0c]\n\t" /* read IISFIFO_CFG to check FIFO status */
90 "and r10, r10, #0x3f0000\n\t"
91 "cmp r10, #0x10000 \n\t"
92#endif
93 "bls .fifo_full \n\t" /* FIFO full, exit */
94 "ldr r10, [r9], #4 \n\t" /* load two samples */
95#ifdef HAVE_AS3514
96 "str r10, [r12, #0x40]\n\t" /* write them */
97#else
98 "mov r10, r10, ror #16\n\t" /* put left sample at the top bits */
99 "str r10, [r12, #0x40]\n\t" /* write top sample, lower sample ignored */
100 "mov r10, r10, lsl #16\n\t" /* shift lower sample up */
101 "str r10, [r12, #0x40]\n\t" /* then write it */
102#endif
103 "subs r8, r8, #4 \n\t" /* check if we have more samples */
104 "bne .fifo_loop \n\t" /* yes, continue */
105 ".more_data: \n\t"
106 "stmdb sp!, { r0-r3, r12, lr}\n\t" /* stack scratch regs and lr */
107 "mov r0, r11 \n\t" /* r0 = &p */
108 "add r1, r11, #4 \n\t" /* r1 = &p_size */
109 "str r9, [r0] \n\t" /* save internal copies of variables back */
110 "str r8, [r1] \n\t"
111 "ldr r2, =pcm_callback_for_more\n\t"
112 "ldr r2, [r2] \n\t" /* get callback address */
113 "cmp r2, #0 \n\t" /* check for null pointer */
114 "movne lr, pc \n\t" /* call pcm_callback_for_more */
115 "bxne r2 \n\t"
116 "ldmia sp!, { r0-r3, r12, lr}\n\t"
117 "ldr r8, [r11, #4] \n\t" /* reload p_size and p */
118 "ldr r9, [r11] \n\t"
119 "cmp r8, #0 \n\t" /* did we actually get more data? */
120 "bne .loop \n\t" /* yes, continue to try feeding FIFO */
121 ".dma_stop: \n\t" /* no more data, do dma_stop() and exit */
122 "ldr r10, =pcm_playing\n\t"
123 "strb r8, [r10] \n\t" /* pcm_playing = false (r8=0, look above) */
124 "ldr r10, =pcm_paused \n\t"
125 "strb r8, [r10] \n\t" /* pcm_paused = false (r8=0, look above) */
126 "ldr r10, [r12] \n\t"
127#if CONFIG_CPU == PP5002
128 "bic r10, r10, #0x4\n\t" /* disable playback FIFO */
129 "str r10, [r12] \n\t"
130 "ldr r10, [r12, #0x1c] \n\t"
131 "bic r10, r10, #0x200 \n\t" /* clear interrupt */
132 "str r10, [r12, #0x1c] \n\t"
133#else
134 "bic r10, r10, #0x20000002\n\t" /* disable playback FIFO and IRQ */
135 "str r10, [r12] \n\t"
136#endif 117#endif
137 "mrs r10, cpsr \n\t" 118 "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */
138 "orr r10, r10, #0x40 \n\t" /* disable FIQ */ 119 "cmp r9, #0 \n" /* is size 0? */
139 "msr cpsr_c, r10 \n\t" 120 "beq .more_data \n" /* if so, ask pcmbuf for more data */
140 ".exit: \n\t" 121 ".fifo_loop: \n"
141 "str r8, [r11, #4] \n\t" 122 "ldr r12, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */
142 "str r9, [r11] \n\t" 123 "ands r12, r12, %[mask] \n"
143 "subs pc, lr, #4 \n\t" /* FIQ specific return sequence */ 124 "beq .exit \n" /* FIFO full, exit */
144 ".fifo_full: \n\t" /* enable IRQ and exit */ 125 "ldr r12, [r8], #4 \n" /* load two samples */
145#if CONFIG_CPU == PP5002 126#if SAMPLE_SIZE == 16
146 "ldr r10, [r12, #0x1c]\n\t" 127 "str r12, [r10, %[wr]] \n" /* write them */
147 "orr r10, r10, #0x200 \n\t" /* set interrupt */ 128#elif SAMPLE_SIZE == 32
148 "str r10, [r12, #0x1c]\n\t" 129 "mov r12, r12, ror #16 \n" /* put left sample at the top bits */
149#else 130 "str r12, [r10, %[wr]] \n" /* write top sample, lower sample ignored */
150 "ldr r10, [r12] \n\t" 131 "mov r12, r12, lsl #16 \n" /* shift lower sample up */
151 "orr r10, r10, #0x2 \n\t" /* set interrupt */ 132 "str r12, [r10, %[wr]] \n" /* then write it */
152 "str r10, [r12] \n\t"
153#endif 133#endif
154 "b .exit \n\t" 134 "subs r9, r9, #4 \n" /* check if we have more samples */
135 "bne .fifo_loop \n" /* yes, continue */
136 ".more_data: \n"
137 "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */
138 "ldr r2, =pcm_callback_for_more \n"
139 "ldr r2, [r2] \n" /* get callback address */
140 "cmp r2, #0 \n" /* check for null pointer */
141 "stmneia r11, { r8-r9 } \n" /* save internal copies of variables back */
142 "movne r0, r11 \n" /* r0 = &p */
143 "addne r1, r11, #4 \n" /* r1 = &size */
144 "movne lr, pc \n" /* call pcm_callback_for_more */
145 "bxne r2 \n"
146 "ldmia r11, { r8-r9 } \n" /* reload p and size */
147 "cmp r9, #0 \n" /* did we actually get more data? */
148 "ldmnefd sp!, { r0-r3, lr } \n"
149 "bne .fifo_loop \n" /* yes, continue to try feeding FIFO */
150 "ldr r12, =pcm_play_dma_stop \n"
151 "mov lr, pc \n"
152 "bx r12 \n"
153 "ldr r12, =pcm_play_dma_stopped_callback \n"
154 "mov lr, pc \n"
155 "bx r12 \n"
156 "ldmfd sp!, { r0-r3, lr } \n"
157 ".exit: \n" /* (r8=0 if stopping, look above) */
158 "stmia r11, { r8-r9 } \n" /* save p and size */
159 "subs pc, lr, #4 \n" /* FIQ specific return sequence */
160 ".ltorg \n"
161 : /* These must only be integers! No regs */
162 : [mask]"i"(IIS_TX_FREE_MASK & (IIS_TX_FREE_MASK-1)),
163 [cfg]"i"((int)&IISFIFO_CFG - (int)&IISCONFIG),
164 [wr]"i"((int)&IISFIFO_WR - (int)&IISCONFIG)
155 ); 165 );
156} 166}
157#else /* C version for reference */ 167#else /* C version for reference */
158void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ"))); 168void fiq_playback(void) __attribute__((interrupt ("FIQ"))) ICODE_ATTR;
159void fiq(void) 169/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
170void fiq_playback(void)
160{ 171{
161 /* Clear interrupt */ 172 register pcm_more_callback_type get_more;
162#ifdef CPU_PP502x 173
163 IISCONFIG &= ~(1 << 1); 174#if CONFIG_CPU == PP5002
164#elif CONFIG_CPU == PP5002
165 inl(0xcf001040); 175 inl(0xcf001040);
166 IISFIFO_CFG &= ~(1<<9);
167#endif 176#endif
168 177
169 do { 178 do {
170 while (p_size) { 179 while (dma_play_data.size > 0) {
171 //if (FIFO_FREE_COUNT < 2) { 180 if (IIS_TX_FREE_COUNT < 2) {
172 if (((IISFIFO_CFG & (0x1f << 16)) >> 16) < 2) {
173 /* Enable interrupt */
174#ifdef CPU_PP502x
175 IISCONFIG |= (1 << 1);
176#elif CONFIG_CPU == PP5002
177 IISFIFO_CFG |= (1<<9);
178#endif
179 return; 181 return;
180 } 182 }
181 183#if SAMPLE_SIZE == 16
182#ifdef HAVE_AS3514 184 IISFIFO_WR = *dma_play_data.p++;
183 IISFIFO_WR = *(int32_t *)p; 185#elif SAMPLE_SIZE == 32
184 p += 2; 186 IISFIFO_WR = *dma_play_data.p++ << 16;
185#else 187 IISFIFO_WR = *dma_play_data.p++ << 16;
186 IISFIFO_WR = (*(p++))<<16;
187 IISFIFO_WR = (*(p++))<<16;
188#endif 188#endif
189 p_size-=4; 189 dma_play_data.size -= 4;
190 } 190 }
191 191
192 /* p is empty, get some more data */ 192 /* p is empty, get some more data */
193 if (pcm_callback_for_more) { 193 get_more = pcm_callback_for_more;
194 pcm_callback_for_more((unsigned char**)&p,&p_size); 194 if (get_more) {
195 get_more((unsigned char**)&dma_play_data.p,
196 &dma_play_data.size);
195 } 197 }
196 } while (p_size); 198 } while (dma_play_data.size);
197 199
198 /* No more data, so disable the FIFO/FIQ */ 200 /* No more data, so disable the FIFO/interrupt */
199 pcm_play_dma_stop(); 201 pcm_play_dma_stop();
202 pcm_play_dma_stopped_callback();
200} 203}
201#endif /* ASM / C selection */ 204#endif /* ASM / C selection */
202 205
203void pcm_play_dma_start(const void *addr, size_t size) 206/* For the locks, FIQ must be disabled because the handler manipulates
207 IISCONFIG and the operation is not atomic - dual core support
208 will require other measures */
209void pcm_play_lock(void)
204{ 210{
205 p=(unsigned short*)addr; 211 int status = set_fiq_status(FIQ_DISABLED);
206 p_size=size;
207
208 pcm_playing = true;
209
210#ifdef CPU_PP502x
211 CPU_INT_PRIORITY |= I2S_MASK; /* FIQ priority for I2S */
212 CPU_INT_EN = I2S_MASK; /* Enable I2S interrupt */
213#else
214 /* setup I2S interrupt for FIQ */
215 outl(inl(0xcf00102c) | DMA_OUT_MASK, 0xcf00102c);
216 outl(DMA_OUT_MASK, 0xcf001024);
217#endif
218 212
219 /* Clear the FIQ disable bit in cpsr_c */ 213 if (++dma_play_data.locked == 1) {
220 set_fiq_handler(fiq); 214 IIS_IRQTX_REG &= ~IIS_IRQTX;
221 enable_fiq(); 215 }
222 216
223#if defined(CPU_PP502x) 217 set_fiq_status(status);
224 /* Enable playback FIFO */ 218}
225 IISCONFIG |= (1 << 29);
226#elif CONFIG_CPU == PP5002
227 IISCONFIG |= 0x4;
228#endif
229 219
230 /* Fill the FIFO - we assume there are enough bytes in the pcm buffer to 220void pcm_play_unlock(void)
231 fill the 32-byte FIFO. */ 221{
232 while (p_size > 0) { 222 int status = set_fiq_status(FIQ_DISABLED);
233 if (FIFO_FREE_COUNT < 2) {
234 /* Enable interrupt */
235#ifdef CPU_PP502x
236 IISCONFIG |= (1 << 1);
237#elif CONFIG_CPU == PP5002
238 IISFIFO_CFG |= (1<<9);
239#endif
240 return;
241 }
242 223
243#ifdef HAVE_AS3514 224 if (--dma_play_data.locked == 0 && dma_play_data.state != 0) {
244 IISFIFO_WR = *(int32_t *)p; 225 IIS_IRQTX_REG |= IIS_IRQTX;
245 p += 2;
246#else
247 IISFIFO_WR = (*(p++))<<16;
248 IISFIFO_WR = (*(p++))<<16;
249#endif
250 p_size-=4;
251 } 226 }
227
228 set_fiq_status(status);
252} 229}
253 230
254/* Stops the DMA transfer and interrupt */ 231static void play_start_pcm(void)
255void pcm_play_dma_stop(void)
256{ 232{
257 pcm_playing = false; 233 fiq_function = fiq_playback;
258 if (!audio_status()) 234 pcm_apply_settings();
259 pcm_paused = false;
260 235
261#if CONFIG_CPU == PP5020 236 IISCONFIG &= ~IIS_TXFIFOEN; /* Stop transmitting */
262 /* Disable TX interrupt */ 237 dma_play_data.state = 1;
263 IISCONFIG &= ~(1 << 1); 238
264#elif defined(CPU_PP502x) 239 /* Fill the FIFO or start when data is used up */
265 /* Disable playback FIFO and interrupt */ 240 while (1) {
266 IISCONFIG &= ~((1 << 29) | (1 << 1)); 241 if (IIS_TX_FREE_COUNT < 2 || dma_play_data.size == 0) {
267#elif CONFIG_CPU == PP5002 242 IISCONFIG |= IIS_TXFIFOEN; /* Start transmitting */
268 /* Disable playback FIFO */ 243 return;
269 IISCONFIG &= ~0x4; 244 }
270
271 /* Disable the interrupt */
272 IISFIFO_CFG &= ~(1<<9);
273#endif
274 245
275 disable_fiq(); 246#if SAMPLE_SIZE == 16
247 IISFIFO_WR = *dma_play_data.p++;
248#elif SAMPLE_SIZE == 32
249 IISFIFO_WR = *dma_play_data.p++ << 16;
250 IISFIFO_WR = *dma_play_data.p++ << 16;
251#endif
252 dma_play_data.size -= 4;
253 }
276} 254}
277 255
278void pcm_play_pause_pause(void) 256static void play_stop_pcm(void)
279{ 257{
280#if CONFIG_CPU == PP5020
281 /* Disable TX interrupt */ 258 /* Disable TX interrupt */
282 IISCONFIG &= ~(1 << 1); 259 IIS_IRQTX_REG &= ~IIS_IRQTX;
283#elif defined(CPU_PP502x) 260 dma_play_data.state = 0;
284 /* Disable playback FIFO and interrupt */
285 IISCONFIG &= ~((1 << 29) | (1 << 1));
286#elif CONFIG_CPU == PP5002
287 /* Disable the interrupt */
288 IISFIFO_CFG &= ~(1<<9);
289 /* Disable playback FIFO */
290 IISCONFIG &= ~0x4;
291#endif
292 disable_fiq();
293} 261}
294 262
295void pcm_play_pause_unpause(void) 263void pcm_play_dma_start(const void *addr, size_t size)
296{ 264{
297 /* Enable the FIFO and fill it */ 265 dma_play_data.p = (void *)addr;
298 266 dma_play_data.size = size;
299 set_fiq_handler(fiq);
300 enable_fiq();
301 267
302#if defined(CPU_PP502x) 268#if NUM_CORES > 1
303 /* Enable playback FIFO */ 269 /* This will become more important later - and different ! */
304 IISCONFIG |= (1 << 29); 270 dma_play_data.core = processor_id(); /* save initiating core */
305#elif CONFIG_CPU == PP5002
306 IISCONFIG |= 0x4;
307#endif 271#endif
308 272
309 /* Fill the FIFO - we assume there are enough bytes in the 273 CPU_INT_PRIORITY |= IIS_MASK; /* FIQ priority for I2S */
310 pcm buffer to fill the 32-byte FIFO. */ 274 CPU_INT_EN = IIS_MASK;
311 while (p_size > 0) {
312 if (FIFO_FREE_COUNT < 2) {
313 /* Enable interrupt */
314#ifdef CPU_PP502x
315 IISCONFIG |= (1 << 1);
316#elif CONFIG_CPU == PP5002
317 IISFIFO_CFG |= (1<<9);
318#endif
319 return;
320 }
321 275
322#ifdef HAVE_AS3514 276 play_start_pcm();
323 IISFIFO_WR = *(int32_t *)p; 277}
324 p += 2; 278
325#else 279/* Stops the DMA transfer and interrupt */
326 IISFIFO_WR = (*(p++))<<16; 280void pcm_play_dma_stop(void)
327 IISFIFO_WR = (*(p++))<<16; 281{
282 play_stop_pcm();
283 dma_play_data.size = 0;
284#if NUM_CORES > 1
285 dma_play_data.core = 0; /* no core in control */
328#endif 286#endif
329 p_size-=4;
330 }
331} 287}
332 288
333void pcm_set_frequency(unsigned int frequency) 289void pcm_play_dma_pause(bool pause)
334{ 290{
335 (void)frequency; 291 if (pause) {
336 pcm_freq = HW_SAMPR_DEFAULT; 292 play_stop_pcm();
293 } else {
294 play_start_pcm();
295 }
337} 296}
338 297
339size_t pcm_get_bytes_waiting(void) 298size_t pcm_get_bytes_waiting(void)
340{ 299{
341 return p_size; 300 return dma_play_data.size & ~3;
342} 301}
343 302
344void pcm_init(void) 303void pcm_play_dma_init(void)
345{ 304{
346 pcm_playing = false; 305 pcm_set_frequency(SAMPR_44);
347 pcm_paused = false;
348 pcm_callback_for_more = NULL;
349 306
350 /* Initialize default register values. */ 307 /* Initialize default register values. */
351 audiohw_init(); 308 audiohw_init();
@@ -357,91 +314,129 @@ void pcm_init(void)
357 audiohw_mute(false); 314 audiohw_mute(false);
358#endif 315#endif
359 316
360 /* Call pcm_play_dma_stop to initialize everything. */ 317 dma_play_data.size = 0;
361 pcm_play_dma_stop(); 318#if NUM_CORES > 1
362 319 dma_play_data.core = 0; /* no core in control */
363#if CONFIG_CPU == PP5020
364 /* This processor doesn't like this disabled */
365 IISCONFIG |= (1 << 29);
366#endif 320#endif
321
322 IISCONFIG |= IIS_TXFIFOEN;
367} 323}
368 324
369void pcm_postinit(void) 325void pcm_postinit(void)
370{ 326{
371 audiohw_postinit(); 327 audiohw_postinit();
328 pcm_apply_settings();
329}
330
331const void * pcm_play_dma_get_peak_buffer(int *count)
332{
333 unsigned long addr = (unsigned long)dma_play_data.p;
334 size_t cnt = dma_play_data.size;
335 *count = cnt >> 2;
336 return (void *)((addr + 2) & ~3);
372} 337}
373 338
374/**************************************************************************** 339/****************************************************************************
375 ** Recording DMA transfer 340 ** Recording DMA transfer
376 **/ 341 **/
377#ifdef HAVE_RECORDING 342#ifdef HAVE_RECORDING
343/* PCM recording interrupt routine lockout */
344static struct dma_data dma_rec_data NOCACHEBSS_ATTR =
345{
346 /* Initialize to a locked, stopped state */
347 .p = NULL,
348 .size = 0,
349#if NUM_CORES > 1
350 .core = 0x00,
351#endif
352 .locked = 0,
353 .state = 0
354};
355
356/* For the locks, FIQ must be disabled because the handler manipulates
357 IISCONFIG and the operation is not atomic - dual core support
358 will require other measures */
359void pcm_rec_lock(void)
360{
361 int status = set_fiq_status(FIQ_DISABLED);
378 362
379#ifdef HAVE_AS3514 363 if (++dma_rec_data.locked == 1)
380void fiq_record(void) ICODE_ATTR __attribute__((naked)); 364 IIS_IRQRX_REG &= ~IIS_IRQRX;
381void fiq_record(void) 365
366 set_fiq_status(status);
367}
368
369void pcm_rec_unlock(void)
382{ 370{
383 register pcm_more_callback_type2 more_ready; 371 int status = set_fiq_status(FIQ_DISABLED);
384 register int32_t value1, value2; 372
373 if (--dma_rec_data.locked == 0 && dma_rec_data.state != 0)
374 IIS_IRQRX_REG |= IIS_IRQRX;
385 375
386 asm volatile ("stmfd sp!, {r0-r7, ip, lr} \n"); /* Store context */ 376 set_fiq_status(status);
377}
378
379/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
380void fiq_record(void) ICODE_ATTR __attribute__((interrupt ("FIQ")));
387 381
388 IISCONFIG &= ~(1 << 0); 382#if defined(SANSA_C200) || defined(SANSA_E200)
383void fiq_record(void)
384{
385 register pcm_more_callback_type2 more_ready;
386 register int32_t value;
389 387
390 if (audio_channels == 2) { 388 if (audio_channels == 2) {
391 /* RX is stereo */ 389 /* RX is stereo */
392 while (p_size > 0) { 390 while (dma_rec_data.size > 0) {
393 if (FIFO_FREE_COUNT < 2) { 391 if (IIS_RX_FULL_COUNT < 2) {
394 /* enable interrupt */ 392 return;
395 IISCONFIG |= (1 << 0);
396 goto fiq_record_exit;
397 } 393 }
398 394
399 /* Discard every other sample since ADC clock is 1/2 LRCK */ 395 /* Discard every other sample since ADC clock is 1/2 LRCK */
400 value1 = IISFIFO_RD; 396 value = IISFIFO_RD;
401 value2 = IISFIFO_RD; 397 IISFIFO_RD;
402 398
403 *(int32_t *)p = value1; 399 *dma_rec_data.p++ = value;
404 p += 2; 400 dma_rec_data.size -= 4;
405 p_size -= 4;
406 401
407 /* TODO: Figure out how to do IIS loopback */ 402 /* TODO: Figure out how to do IIS loopback */
408 if (audio_output_source != AUDIO_SRC_PLAYBACK) { 403 if (audio_output_source != AUDIO_SRC_PLAYBACK) {
409 if ((IISFIFO_CFG & (0x3f << 16)) >= (16 << 16)) { 404 if (IIS_TX_FREE_COUNT >= 16) {
410 /* Resync the output FIFO - it ran dry */ 405 /* Resync the output FIFO - it ran dry */
411 IISFIFO_WR = 0; 406 IISFIFO_WR = 0;
412 IISFIFO_WR = 0; 407 IISFIFO_WR = 0;
413 } 408 }
414 IISFIFO_WR = value1; 409 IISFIFO_WR = value;
415 IISFIFO_WR = value1; 410 IISFIFO_WR = value;
416 } 411 }
417 } 412 }
418 } 413 }
419 else { 414 else {
420 /* RX is left channel mono */ 415 /* RX is left channel mono */
421 while (p_size > 0) { 416 while (dma_rec_data.size > 0) {
422 if (FIFO_FREE_COUNT < 2) { 417 if (IIS_RX_FULL_COUNT < 2) {
423 /* enable interrupt */ 418 return;
424 IISCONFIG |= (1 << 0);
425 goto fiq_record_exit;
426 } 419 }
427 420
428 /* Discard every other sample since ADC clock is 1/2 LRCK */ 421 /* Discard every other sample since ADC clock is 1/2 LRCK */
429 value1 = IISFIFO_RD; 422 value = IISFIFO_RD;
430 value2 = IISFIFO_RD; 423 IISFIFO_RD;
431 *p++ = value1; 424
432 *p++ = value1; 425 value = (uint16_t)value | (value << 16);
433 p_size -= 4; 426
427 *dma_rec_data.p++ = value;
428 dma_rec_data.size -= 4;
434 429
435 if (audio_output_source != AUDIO_SRC_PLAYBACK) { 430 if (audio_output_source != AUDIO_SRC_PLAYBACK) {
436 if ((IISFIFO_CFG & (0x3f << 16)) >= (16 << 16)) { 431 if (IIS_TX_FREE_COUNT >= 16) {
437 /* Resync the output FIFO - it ran dry */ 432 /* Resync the output FIFO - it ran dry */
438 IISFIFO_WR = 0; 433 IISFIFO_WR = 0;
439 IISFIFO_WR = 0; 434 IISFIFO_WR = 0;
440 } 435 }
441 436
442 value1 = *((int32_t *)p - 1); 437 value = *((int32_t *)dma_rec_data.p - 1);
443 IISFIFO_WR = value1; 438 IISFIFO_WR = value;
444 IISFIFO_WR = value1; 439 IISFIFO_WR = value;
445 } 440 }
446 } 441 }
447 } 442 }
@@ -451,281 +446,112 @@ void fiq_record(void)
451 if (more_ready == NULL || more_ready(0) < 0) { 446 if (more_ready == NULL || more_ready(0) < 0) {
452 /* Finished recording */ 447 /* Finished recording */
453 pcm_rec_dma_stop(); 448 pcm_rec_dma_stop();
449 pcm_rec_dma_stopped_callback();
454 } 450 }
455
456fiq_record_exit:
457 asm volatile("ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */
458 "subs pc, lr, #4 \n"); /* Return from FIQ */
459} 451}
460 452
461#else 453#else
462static short peak_l, peak_r IBSS_ATTR;
463
464/* Temporary to stop playback crashing after record */
465void fiq_record(void) ICODE_ATTR __attribute__((naked));
466void fiq_record(void) 454void fiq_record(void)
467{ 455{
468 asm volatile ("stmfd sp!, {r0-r7, r11, ip, lr} \n"); /* Store context */
469
470 register short value;
471 register pcm_more_callback_type2 more_ready; 456 register pcm_more_callback_type2 more_ready;
472 register int status = 0;
473 457
474 /* Clear interrupt */ 458 while (dma_rec_data.size > 0) {
475#ifdef CPU_PP502x 459 if (IIS_RX_FULL_COUNT < 2) {
476 IISCONFIG &= ~(1 << 0); 460 return;
477#elif CONFIG_CPU == PP5002
478 /* TODO */
479#endif
480
481 while (p_size > 0) {
482 if (FIFO_FREE_COUNT < 2) {
483 /* enable interrupt */
484#ifdef CPU_PP502x
485 IISCONFIG |= (1 << 0);
486#elif CONFIG_CPU == PP5002
487 /* TODO */
488#endif
489 goto fiq_record_exit;
490 } 461 }
491 462
492 value = (unsigned short)(IISFIFO_RD >> 16); 463#if SAMPLE_SIZE == 16
493 if (value > peak_l) peak_l = value; 464 *dma_rec_data.p++ = IISFIFO_RD;
494 else if (-value > peak_l) peak_l = -value; 465#elif SAMPLE_SIZE == 32
495 *(p++) = value; 466 *dma_rec_data.p++ = IISFIFO_RD >> 16;
496 467 *dma_rec_data.p++ = IISFIFO_RD >> 16;
497 value = (unsigned short)(IISFIFO_RD >> 16); 468#endif
498 if (value > peak_r) peak_r = value; 469 dma_rec_data.size -= 4;
499 else if (-value > peak_r) peak_r = -value;
500 *(p++) = value;
501
502 p_size -= 4;
503
504 /* If we have filled the current chunk, start a new one */
505 if (p_size == 0) {
506 rec_peak_left = peak_l;
507 rec_peak_right = peak_r;
508 peak_l = peak_r = 0;
509 }
510 } 470 }
511 471
512 more_ready = pcm_callback_more_ready; 472 more_ready = pcm_callback_more_ready;
513 473
514 if (more_ready != NULL && more_ready(status) >= 0) 474 if (more_ready == NULL || more_ready(0) < 0) {
515 goto fiq_record_exit; 475 /* Finished recording */
516 476 pcm_rec_dma_stop();
517 /* Finished recording */ 477 pcm_rec_dma_stopped_callback();
518 pcm_rec_dma_stop(); 478 }
519
520fiq_record_exit:
521 asm volatile("ldmfd sp!, {r0-r7, r11, ip, lr} \n" /* Restore context */
522 "subs pc, lr, #4 \n"); /* Return from FIQ */
523} 479}
524 480
525#endif /* HAVE_AS3514 */ 481#endif /* SANSA_E200 */
526 482
527/* Continue transferring data in */ 483/* Continue transferring data in */
528void pcm_record_more(void *start, size_t size) 484void pcm_record_more(void *start, size_t size)
529{ 485{
530 rec_peak_addr = start; /* Start peaking at dest */ 486 pcm_rec_peak_addr = start; /* Start peaking at dest */
531 p = start; /* Start of RX buffer */ 487 dma_rec_data.p = start; /* Start of RX buffer */
532 p_size = size; /* Bytes to transfer */ 488 dma_rec_data.size = size; /* Bytes to transfer */
533#ifdef CPU_PP502x
534 IISCONFIG |= (1 << 0);
535#elif CONFIG_CPU == PP5002
536 /* TODO */
537#endif
538} 489}
539 490
540void pcm_rec_dma_stop(void) 491void pcm_rec_dma_stop(void)
541{ 492{
542 logf("pcm_rec_dma_stop"); 493 /* disable interrupt */
543 494 IIS_IRQRX_REG &= ~IIS_IRQRX;
544 disable_fiq();
545 495
546 /* clear interrupt, disable fifo */ 496 dma_rec_data.state = 0;
547 IISCONFIG &= ~((1 << 28) | (1 << 0)); 497 dma_rec_data.size = 0;
548 498#if NUM_CORES > 1
549 /* clear rx fifo */ 499 dma_rec_data.core = 0x00;
550 IISFIFO_CFG |= (1 << 12); 500#endif
551 501
552 pcm_recording = false; 502 /* disable fifo */
503 IISCONFIG &= ~IIS_RXFIFOEN;
504 IISFIFO_CFG |= IIS_RXCLR;
553} 505}
554 506
555void pcm_rec_dma_start(void *addr, size_t size) 507void pcm_rec_dma_start(void *addr, size_t size)
556{ 508{
557 logf("pcm_rec_dma_start"); 509 pcm_rec_dma_stop();
558
559 pcm_recording = true;
560 510
561#ifndef HAVE_AS3514 511 pcm_rec_peak_addr = addr;
562 peak_l = peak_r = 0; 512 dma_rec_data.p = addr;
513 dma_rec_data.size = size;
514#if NUM_CORES > 1
515 /* This will become more important later - and different ! */
516 dma_rec_data.core = processor_id(); /* save initiating core */
563#endif 517#endif
518 /* setup FIQ handler */
519 fiq_function = fiq_record;
564 520
565 p_size = size; 521 /* interrupt on full fifo, enable record fifo interrupt */
566 p = addr; 522 dma_rec_data.state = 1;
567
568 /* setup FIQ */
569 CPU_INT_PRIORITY |= I2S_MASK;
570 CPU_INT_EN = I2S_MASK;
571 523
572 /* interrupt on full fifo, enable record fifo */ 524 /* enable RX FIFO */
573 IISCONFIG |= (1 << 28) | (1 << 0); 525 IISCONFIG |= IIS_RXFIFOEN;
574 526
575 set_fiq_handler(fiq_record); 527 /* enable IIS interrupt as FIQ */
576 enable_fiq(); 528 CPU_INT_PRIORITY |= IIS_MASK;
529 CPU_INT_EN = IIS_MASK;
577} 530}
578 531
579void pcm_close_recording(void) 532void pcm_rec_dma_close(void)
580{ 533{
581 logf("pcm_close_recording");
582 pcm_rec_dma_stop(); 534 pcm_rec_dma_stop();
583} /* pcm_close_recording */ 535} /* pcm_close_recording */
584 536
585void pcm_init_recording(void) 537void pcm_rec_dma_init(void)
586{ 538{
587 logf("pcm_init_recording");
588
589 pcm_recording = false;
590 pcm_callback_more_ready = NULL;
591
592#ifdef CPU_PP502x
593#if defined(IPOD_COLOR) || defined (IPOD_4G) 539#if defined(IPOD_COLOR) || defined (IPOD_4G)
594 /* The usual magic from IPL - I'm guessing this configures the headphone 540 /* The usual magic from IPL - I'm guessing this configures the headphone
595 socket to be input or output - in this case, input. */ 541 socket to be input or output - in this case, input. */
596 GPIOI_OUTPUT_VAL &= ~0x40; 542 GPIOI_OUTPUT_VAL &= ~0x40;
597 GPIOA_OUTPUT_VAL &= ~0x4; 543 GPIOA_OUTPUT_VAL &= ~0x4;
598#endif 544#endif
599 /* Setup the recording FIQ handler */
600 set_fiq_handler(fiq_record);
601#endif
602 545
603 pcm_rec_dma_stop(); 546 pcm_rec_dma_stop();
604} /* pcm_init */ 547} /* pcm_init */
605 548
606void pcm_calculate_rec_peaks(int *left, int *right) 549const void * pcm_rec_dma_get_peak_buffer(int *count)
607{ 550{
608#ifdef HAVE_AS3514 551 unsigned long addr = (unsigned long)pcm_rec_peak_addr;
609 if (pcm_recording) 552 unsigned long end = (unsigned long)dma_rec_data.p;
610 { 553 *count = (end >> 2) - (addr >> 2);
611 unsigned long *start = rec_peak_addr; 554 return (void *)(addr & ~3);
612 unsigned long *end = (unsigned long *)p; 555} /* pcm_rec_dma_get_peak_buffer */
613
614 if (start < end)
615 {
616 unsigned long *addr = start;
617 long peak_l = 0, peak_r = 0;
618 long peaksq_l = 0, peaksq_r = 0;
619
620 do
621 {
622 long value = *addr;
623 long ch, chsq;
624
625 ch = (int16_t)value;
626 chsq = ch*ch;
627 if (chsq > peaksq_l)
628 peak_l = ch, peaksq_l = chsq;
629
630 ch = value >> 16;
631 chsq = ch*ch;
632 if (chsq > peaksq_r)
633 peak_r = ch, peaksq_r = chsq;
634
635 addr += 4;
636 }
637 while (addr < end);
638
639 if (start == rec_peak_addr)
640 rec_peak_addr = end;
641
642 rec_peak_left = abs(peak_l);
643 rec_peak_right = abs(peak_r);
644 }
645 }
646 else
647 {
648 rec_peak_left = rec_peak_right = 0;
649 }
650#endif /* HAVE_AS3514 */
651
652 if (left)
653 *left = rec_peak_left;
654 556
655 if (right)
656 *right = rec_peak_right;
657}
658#endif /* HAVE_RECORDING */ 557#endif /* HAVE_RECORDING */
659
660/*
661 * This function goes directly into the DMA buffer to calculate the left and
662 * right peak values. To avoid missing peaks it tries to look forward two full
663 * peek periods (2/HZ sec, 100% overlap), although it's always possible that
664 * the entire period will not be visible. To reduce CPU load it only looks at
665 * every third sample, and this can be reduced even further if needed (even
666 * every tenth sample would still be pretty accurate).
667 */
668
669/* Check for a peak every PEAK_STRIDE samples */
670#define PEAK_STRIDE 3
671/* Up to 1/50th of a second of audio for peak calculation */
672/* This should use NATIVE_FREQUENCY, or eventually an adjustable freq. value */
673#define PEAK_SAMPLES (44100/50)
674void pcm_calculate_peaks(int *left, int *right)
675{
676 short *addr;
677 short *end;
678 {
679 size_t samples = p_size / 4;
680 addr = p;
681
682 if (samples > PEAK_SAMPLES)
683 samples = PEAK_SAMPLES - (PEAK_STRIDE - 1);
684 else
685 samples -= MIN(PEAK_STRIDE - 1, samples);
686
687 end = &addr[samples * 2];
688 }
689
690 if (left && right) {
691 int left_peak = 0, right_peak = 0;
692
693 while (addr < end) {
694 int value;
695 if ((value = addr [0]) > left_peak)
696 left_peak = value;
697 else if (-value > left_peak)
698 left_peak = -value;
699
700 if ((value = addr [PEAK_STRIDE | 1]) > right_peak)
701 right_peak = value;
702 else if (-value > right_peak)
703 right_peak = -value;
704
705 addr = &addr[PEAK_STRIDE * 2];
706 }
707
708 *left = left_peak;
709 *right = right_peak;
710 }
711 else if (left || right) {
712 int peak_value = 0, value;
713
714 if (right)
715 addr += (PEAK_STRIDE | 1);
716
717 while (addr < end) {
718 if ((value = addr [0]) > peak_value)
719 peak_value = value;
720 else if (-value > peak_value)
721 peak_value = -value;
722
723 addr += PEAK_STRIDE * 2;
724 }
725
726 if (left)
727 *left = peak_value;
728 else
729 *right = peak_value;
730 }
731}