Do core interrupt masking in a less general fashion and save some instructions to...
[kugel-rb.git] / firmware / target / arm / pcm-pp.c
blob433e6e1e4f3ec33a5c5c9e8bd6318447313e5383
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006 by Michael Sevakis
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include <stdlib.h>
20 #include "system.h"
21 #include "kernel.h"
22 #include "logf.h"
23 #include "audio.h"
24 #include "sound.h"
26 #ifdef HAVE_WM8751
27 #define MROBE100_44100HZ (0x40|(0x11 << 1)|1)
28 #endif
30 /** DMA **/
32 #ifdef CPU_PP502x
33 /* 16-bit, L-R packed into 32 bits with left in the least significant halfword */
34 #define SAMPLE_SIZE 16
35 #else
36 /* 32-bit, one left 32-bit sample followed by one right 32-bit sample */
37 #define SAMPLE_SIZE 32
38 #endif
40 struct dma_data
42 /* NOTE: The order of size and p is important if you use assembler
43 optimised fiq handler, so don't change it. */
44 #if SAMPLE_SIZE == 16
45 uint32_t *p;
46 #elif SAMPLE_SIZE == 32
47 uint16_t *p;
48 #endif
49 size_t size;
50 #if NUM_CORES > 1
51 unsigned core;
52 #endif
53 int locked;
54 int state;
57 extern void *fiq_function;
59 /* Dispatch to the proper handler and leave the main vector table alone */
60 void fiq_handler(void) ICODE_ATTR __attribute__((naked));
61 void fiq_handler(void)
63 asm volatile (
64 "ldr pc, [pc, #-4] \n"
65 "fiq_function: \n"
66 ".word 0 \n"
70 /* TODO: Get simultaneous recording and playback to work. Just needs some tweaking */
72 /****************************************************************************
73 ** Playback DMA transfer
74 **/
75 struct dma_data dma_play_data NOCACHEBSS_ATTR =
77 /* Initialize to a locked, stopped state */
78 .p = NULL,
79 .size = 0,
80 #if NUM_CORES > 1
81 .core = 0x00,
82 #endif
83 .locked = 0,
84 .state = 0
87 static unsigned long pcm_freq NOCACHEDATA_ATTR = HW_SAMPR_DEFAULT; /* 44.1 is default */
88 #ifdef HAVE_WM8751
89 /* Samplerate control for audio codec */
90 static int sr_ctrl = MROBE100_44100HZ;
91 #endif
93 void pcm_set_frequency(unsigned int frequency)
95 (void)frequency;
96 pcm_freq = HW_SAMPR_DEFAULT;
97 #ifdef HAVE_WM8751
98 sr_ctrl = MROBE100_44100HZ;
99 #endif
102 void pcm_apply_settings(void)
104 #ifdef HAVE_WM8751
105 audiohw_set_frequency(sr_ctrl);
106 #endif
107 pcm_curr_sampr = pcm_freq;
110 /* ASM optimised FIQ handler. GCC fails to make use of the fact that FIQ mode
111 has registers r8-r14 banked, and so does not need to be saved. This routine
112 uses only these registers, and so will never touch the stack unless it
113 actually needs to do so when calling pcm_callback_for_more. C version is
114 still included below for reference and testing.
116 #if 1
117 void fiq_playback(void) ICODE_ATTR __attribute__((naked));
118 void fiq_playback(void)
120 /* r10 contains IISCONFIG address (set in crt0.S to minimise code in actual
121 * FIQ handler. r11 contains address of p (also set in crt0.S). Most other
122 * addresses we need are generated by using offsets with these two.
123 * r10 + 0x40 is IISFIFO_WR, and r10 + 0x0c is IISFIFO_CFG.
124 * r8 and r9 contains local copies of p and size respectively.
125 * r12 is a working register.
127 asm volatile (
128 #if CONFIG_CPU == PP5002
129 "ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux */
130 "ldr r12, [r12] \n"
131 #endif
132 "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */
133 "cmp r9, #0 \n" /* is size 0? */
134 "beq .more_data \n" /* if so, ask pcmbuf for more data */
135 ".fifo_loop: \n"
136 "ldr r12, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */
137 "ands r12, r12, %[mask] \n"
138 "beq .exit \n" /* FIFO full, exit */
139 #if SAMPLE_SIZE == 16
140 "ldr r12, [r8], #4 \n" /* load two samples */
141 "str r12, [r10, %[wr]] \n" /* write them */
142 #elif SAMPLE_SIZE == 32
143 "ldr r12, [r8], #4 \n" /* load two samples */
144 "mov r12, r12, ror #16 \n" /* put left sample at the top bits */
145 "str r12, [r10, %[wr]] \n" /* write top sample, lower sample ignored */
146 "mov r12, r12, lsl #16 \n" /* shift lower sample up */
147 "str r12, [r10, %[wr]] \n" /* then write it */
148 #endif
149 "subs r9, r9, #4 \n" /* check if we have more samples */
150 "bne .fifo_loop \n" /* yes, continue */
151 ".more_data: \n"
152 "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */
153 "ldr r2, =pcm_callback_for_more \n"
154 "ldr r2, [r2] \n" /* get callback address */
155 "cmp r2, #0 \n" /* check for null pointer */
156 "stmneia r11, { r8-r9 } \n" /* save internal copies of variables back */
157 "movne r0, r11 \n" /* r0 = &p */
158 "addne r1, r11, #4 \n" /* r1 = &size */
159 "movne lr, pc \n" /* call pcm_callback_for_more */
160 "bxne r2 \n"
161 "ldmia r11, { r8-r9 } \n" /* reload p and size */
162 "cmp r9, #0 \n" /* did we actually get more data? */
163 "ldmnefd sp!, { r0-r3, lr } \n"
164 "bne .fifo_loop \n" /* yes, continue to try feeding FIFO */
165 "ldr r12, =pcm_play_dma_stop \n"
166 "mov lr, pc \n"
167 "bx r12 \n"
168 "ldr r12, =pcm_play_dma_stopped_callback \n"
169 "mov lr, pc \n"
170 "bx r12 \n"
171 "ldmfd sp!, { r0-r3, lr } \n"
172 ".exit: \n" /* (r8=0 if stopping, look above) */
173 "stmia r11, { r8-r9 } \n" /* save p and size */
174 "subs pc, lr, #4 \n" /* FIQ specific return sequence */
175 ".ltorg \n"
176 : /* These must only be integers! No regs */
177 : [mask]"i"(IIS_TX_FREE_MASK & (IIS_TX_FREE_MASK-1)),
178 [cfg]"i"((int)&IISFIFO_CFG - (int)&IISCONFIG),
179 [wr]"i"((int)&IISFIFO_WR - (int)&IISCONFIG)
182 #else /* C version for reference */
183 void fiq_playback(void) __attribute__((interrupt ("FIQ"))) ICODE_ATTR;
184 /* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
185 void fiq_playback(void)
187 register pcm_more_callback_type get_more;
189 #if CONFIG_CPU == PP5002
190 inl(0xcf001040);
191 #endif
193 do {
194 while (dma_play_data.size > 0) {
195 if (IIS_TX_FREE_COUNT < 2) {
196 return;
198 #if SAMPLE_SIZE == 16
199 IISFIFO_WR = *dma_play_data.p++;
200 #elif SAMPLE_SIZE == 32
201 IISFIFO_WR = *dma_play_data.p++ << 16;
202 IISFIFO_WR = *dma_play_data.p++ << 16;
203 #endif
204 dma_play_data.size -= 4;
207 /* p is empty, get some more data */
208 get_more = pcm_callback_for_more;
209 if (get_more) {
210 get_more((unsigned char**)&dma_play_data.p,
211 &dma_play_data.size);
213 } while (dma_play_data.size);
215 /* No more data, so disable the FIFO/interrupt */
216 pcm_play_dma_stop();
217 pcm_play_dma_stopped_callback();
219 #endif /* ASM / C selection */
221 /* For the locks, FIQ must be disabled because the handler manipulates
222 IISCONFIG and the operation is not atomic - dual core support
223 will require other measures */
224 void pcm_play_lock(void)
226 int status = disable_fiq_save();
228 if (++dma_play_data.locked == 1) {
229 IIS_IRQTX_REG &= ~IIS_IRQTX;
232 restore_fiq(status);
235 void pcm_play_unlock(void)
237 int status = disable_fiq_save();
239 if (--dma_play_data.locked == 0 && dma_play_data.state != 0) {
240 IIS_IRQTX_REG |= IIS_IRQTX;
243 restore_fiq(status);
246 static void play_start_pcm(void)
248 fiq_function = fiq_playback;
249 pcm_apply_settings();
251 IISCONFIG &= ~IIS_TXFIFOEN; /* Stop transmitting */
252 dma_play_data.state = 1;
254 /* Fill the FIFO or start when data is used up */
255 while (1) {
256 if (IIS_TX_FREE_COUNT < 2 || dma_play_data.size == 0) {
257 IISCONFIG |= IIS_TXFIFOEN; /* Start transmitting */
258 return;
261 #if SAMPLE_SIZE == 16
262 IISFIFO_WR = *dma_play_data.p++;
263 #elif SAMPLE_SIZE == 32
264 IISFIFO_WR = *dma_play_data.p++ << 16;
265 IISFIFO_WR = *dma_play_data.p++ << 16;
266 #endif
267 dma_play_data.size -= 4;
271 static void play_stop_pcm(void)
273 /* Disable TX interrupt */
274 IIS_IRQTX_REG &= ~IIS_IRQTX;
275 dma_play_data.state = 0;
278 void pcm_play_dma_start(const void *addr, size_t size)
280 dma_play_data.p = (void *)(((uintptr_t)addr + 2) & ~3);
281 dma_play_data.size = (size & ~3);
283 #if NUM_CORES > 1
284 /* This will become more important later - and different ! */
285 dma_play_data.core = processor_id(); /* save initiating core */
286 #endif
288 CPU_INT_PRIORITY |= IIS_MASK; /* FIQ priority for I2S */
289 CPU_INT_EN = IIS_MASK;
291 play_start_pcm();
294 /* Stops the DMA transfer and interrupt */
295 void pcm_play_dma_stop(void)
297 play_stop_pcm();
298 dma_play_data.size = 0;
299 #if NUM_CORES > 1
300 dma_play_data.core = 0; /* no core in control */
301 #endif
304 void pcm_play_dma_pause(bool pause)
306 if (pause) {
307 play_stop_pcm();
308 } else {
309 play_start_pcm();
313 size_t pcm_get_bytes_waiting(void)
315 return dma_play_data.size & ~3;
318 void pcm_play_dma_init(void)
320 pcm_set_frequency(SAMPR_44);
322 /* Initialize default register values. */
323 audiohw_init();
325 #if !defined(HAVE_WM8731) && !defined(HAVE_WM8751)
326 /* Power on */
327 audiohw_enable_output(true);
328 /* Unmute the master channel (DAC should be at zero point now). */
329 audiohw_mute(false);
330 #endif
332 dma_play_data.size = 0;
333 #if NUM_CORES > 1
334 dma_play_data.core = 0; /* no core in control */
335 #endif
337 IISCONFIG |= IIS_TXFIFOEN;
340 void pcm_postinit(void)
342 audiohw_postinit();
343 pcm_apply_settings();
346 const void * pcm_play_dma_get_peak_buffer(int *count)
348 unsigned long addr = (unsigned long)dma_play_data.p;
349 size_t cnt = dma_play_data.size;
350 *count = cnt >> 2;
351 return (void *)((addr + 2) & ~3);
354 /****************************************************************************
355 ** Recording DMA transfer
357 #ifdef HAVE_RECORDING
358 /* PCM recording interrupt routine lockout */
359 static struct dma_data dma_rec_data NOCACHEBSS_ATTR =
361 /* Initialize to a locked, stopped state */
362 .p = NULL,
363 .size = 0,
364 #if NUM_CORES > 1
365 .core = 0x00,
366 #endif
367 .locked = 0,
368 .state = 0
371 /* For the locks, FIQ must be disabled because the handler manipulates
372 IISCONFIG and the operation is not atomic - dual core support
373 will require other measures */
374 void pcm_rec_lock(void)
376 int status = disable_fiq_save();
378 if (++dma_rec_data.locked == 1)
379 IIS_IRQRX_REG &= ~IIS_IRQRX;
381 restore_fiq(status);
384 void pcm_rec_unlock(void)
386 int status = disable_fiq_save();
388 if (--dma_rec_data.locked == 0 && dma_rec_data.state != 0)
389 IIS_IRQRX_REG |= IIS_IRQRX;
391 restore_fiq(status);
394 /* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
395 void fiq_record(void) ICODE_ATTR __attribute__((interrupt ("FIQ")));
397 #if defined(SANSA_C200) || defined(SANSA_E200)
398 void fiq_record(void)
400 register pcm_more_callback_type2 more_ready;
401 register int32_t value;
403 if (audio_channels == 2) {
404 /* RX is stereo */
405 while (dma_rec_data.size > 0) {
406 if (IIS_RX_FULL_COUNT < 2) {
407 return;
410 /* Discard every other sample since ADC clock is 1/2 LRCK */
411 value = IISFIFO_RD;
412 IISFIFO_RD;
414 *dma_rec_data.p++ = value;
415 dma_rec_data.size -= 4;
417 /* TODO: Figure out how to do IIS loopback */
418 if (audio_output_source != AUDIO_SRC_PLAYBACK) {
419 if (IIS_TX_FREE_COUNT >= 16) {
420 /* Resync the output FIFO - it ran dry */
421 IISFIFO_WR = 0;
422 IISFIFO_WR = 0;
424 IISFIFO_WR = value;
425 IISFIFO_WR = value;
429 else {
430 /* RX is left channel mono */
431 while (dma_rec_data.size > 0) {
432 if (IIS_RX_FULL_COUNT < 2) {
433 return;
436 /* Discard every other sample since ADC clock is 1/2 LRCK */
437 value = IISFIFO_RD;
438 IISFIFO_RD;
440 value = (uint16_t)value | (value << 16);
442 *dma_rec_data.p++ = value;
443 dma_rec_data.size -= 4;
445 if (audio_output_source != AUDIO_SRC_PLAYBACK) {
446 if (IIS_TX_FREE_COUNT >= 16) {
447 /* Resync the output FIFO - it ran dry */
448 IISFIFO_WR = 0;
449 IISFIFO_WR = 0;
452 value = *((int32_t *)dma_rec_data.p - 1);
453 IISFIFO_WR = value;
454 IISFIFO_WR = value;
459 more_ready = pcm_callback_more_ready;
461 if (more_ready == NULL || more_ready(0) < 0) {
462 /* Finished recording */
463 pcm_rec_dma_stop();
464 pcm_rec_dma_stopped_callback();
468 #else
469 void fiq_record(void)
471 register pcm_more_callback_type2 more_ready;
473 while (dma_rec_data.size > 0) {
474 if (IIS_RX_FULL_COUNT < 2) {
475 return;
478 #if SAMPLE_SIZE == 16
479 *dma_rec_data.p++ = IISFIFO_RD;
480 #elif SAMPLE_SIZE == 32
481 *dma_rec_data.p++ = IISFIFO_RD >> 16;
482 *dma_rec_data.p++ = IISFIFO_RD >> 16;
483 #endif
484 dma_rec_data.size -= 4;
487 more_ready = pcm_callback_more_ready;
489 if (more_ready == NULL || more_ready(0) < 0) {
490 /* Finished recording */
491 pcm_rec_dma_stop();
492 pcm_rec_dma_stopped_callback();
496 #endif /* SANSA_E200 */
498 /* Continue transferring data in */
499 void pcm_record_more(void *start, size_t size)
501 pcm_rec_peak_addr = start; /* Start peaking at dest */
502 dma_rec_data.p = start; /* Start of RX buffer */
503 dma_rec_data.size = size; /* Bytes to transfer */
506 void pcm_rec_dma_stop(void)
508 /* disable interrupt */
509 IIS_IRQRX_REG &= ~IIS_IRQRX;
511 dma_rec_data.state = 0;
512 dma_rec_data.size = 0;
513 #if NUM_CORES > 1
514 dma_rec_data.core = 0x00;
515 #endif
517 /* disable fifo */
518 IISCONFIG &= ~IIS_RXFIFOEN;
519 IISFIFO_CFG |= IIS_RXCLR;
522 void pcm_rec_dma_start(void *addr, size_t size)
524 pcm_rec_dma_stop();
526 pcm_rec_peak_addr = addr;
527 dma_rec_data.p = addr;
528 dma_rec_data.size = size;
529 #if NUM_CORES > 1
530 /* This will become more important later - and different ! */
531 dma_rec_data.core = processor_id(); /* save initiating core */
532 #endif
533 /* setup FIQ handler */
534 fiq_function = fiq_record;
536 /* interrupt on full fifo, enable record fifo interrupt */
537 dma_rec_data.state = 1;
539 /* enable RX FIFO */
540 IISCONFIG |= IIS_RXFIFOEN;
542 /* enable IIS interrupt as FIQ */
543 CPU_INT_PRIORITY |= IIS_MASK;
544 CPU_INT_EN = IIS_MASK;
547 void pcm_rec_dma_close(void)
549 pcm_rec_dma_stop();
550 } /* pcm_close_recording */
552 void pcm_rec_dma_init(void)
554 #if defined(IPOD_COLOR) || defined (IPOD_4G)
555 /* The usual magic from IPL - I'm guessing this configures the headphone
556 socket to be input or output - in this case, input. */
557 GPIOI_OUTPUT_VAL &= ~0x40;
558 GPIOA_OUTPUT_VAL &= ~0x4;
559 #endif
561 pcm_rec_dma_stop();
562 } /* pcm_init */
564 const void * pcm_rec_dma_get_peak_buffer(int *count)
566 unsigned long addr = (unsigned long)pcm_rec_peak_addr;
567 unsigned long end = (unsigned long)dma_rec_data.p;
568 *count = (end >> 2) - (addr >> 2);
569 return (void *)(addr & ~3);
570 } /* pcm_rec_dma_get_peak_buffer */
572 #endif /* HAVE_RECORDING */