summaryrefslogtreecommitdiff
path: root/firmware/target/arm
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm')
-rw-r--r--firmware/target/arm/audio-pp.c84
-rw-r--r--firmware/target/arm/pcm-pp.c578
2 files changed, 662 insertions, 0 deletions
diff --git a/firmware/target/arm/audio-pp.c b/firmware/target/arm/audio-pp.c
new file mode 100644
index 0000000000..c08db8a88a
--- /dev/null
+++ b/firmware/target/arm/audio-pp.c
@@ -0,0 +1,84 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 by Michael Sevakis
11 *
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.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#include "system.h"
20#include "cpu.h"
21#include "audio.h"
22#include "sound.h"
23
24void audio_set_output_source(int source)
25{
26 if ((unsigned)source >= AUDIO_NUM_SOURCES)
27 source = AUDIO_SRC_PLAYBACK;
28} /* audio_set_output_source */
29
30void audio_set_source(int source, unsigned flags)
31{
32 /* Prevent pops from unneeded switching */
33 static int last_source = AUDIO_SRC_PLAYBACK;
34 bool recording = flags & SRCF_RECORDING;
35 static bool last_recording = false;
36
37 switch (source)
38 {
39 default: /* playback - no recording */
40 source = AUDIO_SRC_PLAYBACK;
41 case AUDIO_SRC_PLAYBACK:
42 if (source != last_source)
43 {
44 audiohw_disable_recording();
45 audiohw_set_monitor(false);
46 }
47 break;
48
49 case AUDIO_SRC_MIC: /* recording only */
50 if (source != last_source)
51 {
52 audiohw_enable_recording(true); /* source mic */
53 audiohw_set_monitor(false);
54 }
55 break;
56
57 case AUDIO_SRC_LINEIN: /* recording only */
58 if (source != last_source)
59 {
60 audiohw_enable_recording(false); /* source line */
61 audiohw_set_monitor(false);
62 }
63 break;
64#ifdef CONFIG_TUNER
65 case AUDIO_SRC_FMRADIO: /* recording and playback */
66 if (!recording)
67 audiohw_set_recvol(0, 0, AUDIO_GAIN_LINEIN);
68
69 if (source == last_source && recording == last_recording)
70 break;
71
72 last_recording = recording;
73
74 /* I2S recording and playback */
75 audiohw_enable_recording(false); /* source line */
76 audiohw_set_monitor(!recording);
77 break;
78#endif
79 } /* end switch */
80
81 last_source = source;
82} /* audio_set_source */
83
84
diff --git a/firmware/target/arm/pcm-pp.c b/firmware/target/arm/pcm-pp.c
new file mode 100644
index 0000000000..d9a3b6d7d7
--- /dev/null
+++ b/firmware/target/arm/pcm-pp.c
@@ -0,0 +1,578 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 by Michael Sevakis
11 *
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.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#include <stdlib.h>
20#include "system.h"
21#include "kernel.h"
22#include "logf.h"
23#include "audio.h"
24#if defined(HAVE_WM8975)
25#include "wm8975.h"
26#elif defined(HAVE_WM8758)
27#include "wm8758.h"
28#elif defined(HAVE_WM8731)
29#include "wm8731l.h"
30#endif
31
32
33
34/* peaks */
35static int play_peak_left, play_peak_right;
36static unsigned long *rec_peak_addr;
37static int rec_peak_left, rec_peak_right;
38
39/** DMA **/
40#if CONFIG_CPU == PP5020
41#define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x3f000000) >> 24)
42#elif CONFIG_CPU == PP5002
43#define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x7800000) >> 23)
44#elif CONFIG_CPU == PP5024
45#define FIFO_FREE_COUNT 4 /* TODO: make this sensible */
46#endif
47
48/****************************************************************************
49 ** Playback DMA transfer
50 **/
51static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
52
53/* NOTE: The order of these two variables is important if you use the iPod
54 assembler optimised fiq handler, so don't change it. */
55unsigned short* p IBSS_ATTR;
56size_t p_size IBSS_ATTR;
57
58/* ASM optimised FIQ handler. GCC fails to make use of the fact that FIQ mode
59 has registers r8-r14 banked, and so does not need to be saved. This routine
60 uses only these registers, and so will never touch the stack unless it
61 actually needs to do so when calling pcm_callback_for_more. C version is
62 still included below for reference.
63 */
64#if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002
65void fiq(void) ICODE_ATTR __attribute__((naked));
66void fiq(void)
67{
68 /* r12 contains IISCONFIG address (set in crt0.S to minimise code in actual
69 * FIQ handler. r11 contains address of p (also set in crt0.S). Most other
70 * addresses we need are generated by using offsets with these two.
71 * r12 + 0x40 is IISFIFO_WR, and r12 + 0x0c is IISFIFO_CFG.
72 * r8 and r9 contains local copies of p_size and p respectively.
73 * r10 is a working register.
74 */
75 asm volatile (
76#if CONFIG_CPU == PP5002
77 "ldr r10, =0xcf001040 \n\t" /* Some magic from iPodLinux */
78 "ldr r10, [r10] \n\t"
79 "ldr r10, [r12, #0x1c]\n\t"
80 "bic r10, r10, #0x200 \n\t" /* clear interrupt */
81 "str r10, [r12, #0x1c]\n\t"
82#else
83 "ldr r10, [r12] \n\t"
84 "bic r10, r10, #0x2 \n\t" /* clear interrupt */
85 "str r10, [r12] \n\t"
86#endif
87 "ldr r8, [r11, #4] \n\t" /* r8 = p_size */
88 "ldr r9, [r11] \n\t" /* r9 = p */
89 ".loop: \n\t"
90 "cmp r8, #0 \n\t" /* is p_size 0? */
91 "beq .more_data \n\t" /* if so, ask pcmbuf for more data */
92 ".fifo_loop: \n\t"
93#if CONFIG_CPU == PP5002
94 "ldr r10, [r12, #0x1c]\n\t" /* read IISFIFO_CFG to check FIFO status */
95 "and r10, r10, #0x7800000\n\t"
96 "cmp r10, #0x800000 \n\t"
97#else
98 "ldr r10, [r12, #0x0c]\n\t" /* read IISFIFO_CFG to check FIFO status */
99 "and r10, r10, #0x3f0000\n\t"
100 "cmp r10, #0x10000 \n\t"
101#endif
102 "bls .fifo_full \n\t" /* FIFO full, exit */
103 "ldr r10, [r9], #4 \n\t" /* load two samples */
104 "mov r10, r10, ror #16\n\t" /* put left sample at the top bits */
105 "str r10, [r12, #0x40]\n\t" /* write top sample, lower sample ignored */
106 "mov r10, r10, lsl #16\n\t" /* shift lower sample up */
107 "str r10, [r12, #0x40]\n\t" /* then write it */
108 "subs r8, r8, #4 \n\t" /* check if we have more samples */
109 "bne .fifo_loop \n\t" /* yes, continue */
110 ".more_data: \n\t"
111 "stmdb sp!, { r0-r3, r12, lr}\n\t" /* stack scratch regs and lr */
112 "mov r0, r11 \n\t" /* r0 = &p */
113 "add r1, r11, #4 \n\t" /* r1 = &p_size */
114 "str r9, [r0] \n\t" /* save internal copies of variables back */
115 "str r8, [r1] \n\t"
116 "ldr r2, =pcm_callback_for_more\n\t"
117 "ldr r2, [r2] \n\t" /* get callback address */
118 "cmp r2, #0 \n\t" /* check for null pointer */
119 "movne lr, pc \n\t" /* call pcm_callback_for_more */
120 "bxne r2 \n\t"
121 "ldmia sp!, { r0-r3, r12, lr}\n\t"
122 "ldr r8, [r11, #4] \n\t" /* reload p_size and p */
123 "ldr r9, [r11] \n\t"
124 "cmp r8, #0 \n\t" /* did we actually get more data? */
125 "bne .loop \n\t" /* yes, continue to try feeding FIFO */
126 ".dma_stop: \n\t" /* no more data, do dma_stop() and exit */
127 "ldr r10, =pcm_playing\n\t"
128 "strb r8, [r10] \n\t" /* pcm_playing = false (r8=0, look above) */
129 "ldr r10, [r12] \n\t"
130#if CONFIG_CPU == PP5002
131 "bic r10, r10, #0x4\n\t" /* disable playback FIFO */
132 "str r10, [r12] \n\t"
133 "ldr r10, [r12, #0x1c] \n\t"
134 "bic r10, r10, #0x200 \n\t" /* clear interrupt */
135 "str r10, [r12, #0x1c] \n\t"
136#else
137 "bic r10, r10, #0x20000002\n\t" /* disable playback FIFO and IRQ */
138 "str r10, [r12] \n\t"
139#endif
140 "mrs r10, cpsr \n\t"
141 "orr r10, r10, #0x40 \n\t" /* disable FIQ */
142 "msr cpsr_c, r10 \n\t"
143 ".exit: \n\t"
144 "str r8, [r11, #4] \n\t"
145 "str r9, [r11] \n\t"
146 "subs pc, lr, #4 \n\t" /* FIQ specific return sequence */
147 ".fifo_full: \n\t" /* enable IRQ and exit */
148#if CONFIG_CPU == PP5002
149 "ldr r10, [r12, #0x1c]\n\t"
150 "orr r10, r10, #0x200 \n\t" /* set interrupt */
151 "str r10, [r12, #0x1c]\n\t"
152#else
153 "ldr r10, [r12] \n\t"
154 "orr r10, r10, #0x2 \n\t" /* set interrupt */
155 "str r10, [r12] \n\t"
156#endif
157 "b .exit \n\t"
158 );
159}
160#else /* !(CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002) */
161void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ")));
162void fiq(void)
163{
164 /* Clear interrupt */
165#if CONFIG_CPU == PP5020
166 IISCONFIG &= ~0x2;
167#elif CONFIG_CPU == PP5002
168 inl(0xcf001040);
169 IISFIFO_CFG &= ~(1<<9);
170#endif
171
172 do {
173 while (p_size) {
174 if (FIFO_FREE_COUNT < 2) {
175 /* Enable interrupt */
176#if CONFIG_CPU == PP5020
177 IISCONFIG |= 0x2;
178#elif CONFIG_CPU == PP5002
179 IISFIFO_CFG |= (1<<9);
180#endif
181 return;
182 }
183
184 IISFIFO_WR = (*(p++))<<16;
185 IISFIFO_WR = (*(p++))<<16;
186 p_size-=4;
187 }
188
189 /* p is empty, get some more data */
190 if (pcm_callback_for_more) {
191 pcm_callback_for_more((unsigned char**)&p,&p_size);
192 }
193 } while (p_size);
194
195 /* No more data, so disable the FIFO/FIQ */
196 pcm_play_dma_stop();
197}
198#endif /* CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002 */
199
200void pcm_play_dma_start(const void *addr, size_t size)
201{
202 p=(unsigned short*)addr;
203 p_size=size;
204
205 pcm_playing = true;
206
207#if CONFIG_CPU == PP5020
208 /* setup I2S interrupt for FIQ */
209 outl(inl(0x6000402c) | I2S_MASK, 0x6000402c);
210 outl(I2S_MASK, 0x60004024);
211#elif CONFIG_CPU == PP5024
212#else
213 /* setup I2S interrupt for FIQ */
214 outl(inl(0xcf00102c) | DMA_OUT_MASK, 0xcf00102c);
215 outl(DMA_OUT_MASK, 0xcf001024);
216#endif
217
218 /* Clear the FIQ disable bit in cpsr_c */
219 enable_fiq(fiq);
220
221 /* Enable playback FIFO */
222#if CONFIG_CPU == PP5020
223 IISCONFIG |= 0x20000000;
224#elif CONFIG_CPU == PP5002
225 IISCONFIG |= 0x4;
226#endif
227
228 /* Fill the FIFO - we assume there are enough bytes in the pcm buffer to
229 fill the 32-byte FIFO. */
230 while (p_size > 0) {
231 if (FIFO_FREE_COUNT < 2) {
232 /* Enable interrupt */
233#if CONFIG_CPU == PP5020
234 IISCONFIG |= 0x2;
235#elif CONFIG_CPU == PP5002
236 IISFIFO_CFG |= (1<<9);
237#endif
238 return;
239 }
240
241 IISFIFO_WR = (*(p++))<<16;
242 IISFIFO_WR = (*(p++))<<16;
243 p_size-=4;
244 }
245}
246
247/* Stops the DMA transfer and interrupt */
248void pcm_play_dma_stop(void)
249{
250 pcm_playing = false;
251
252#if CONFIG_CPU == PP5020
253
254 /* Disable playback FIFO */
255 IISCONFIG &= ~0x20000000;
256
257 /* Disable the interrupt */
258 IISCONFIG &= ~0x2;
259
260#elif CONFIG_CPU == PP5002
261
262 /* Disable playback FIFO */
263 IISCONFIG &= ~0x4;
264
265 /* Disable the interrupt */
266 IISFIFO_CFG &= ~(1<<9);
267#endif
268
269 disable_fiq();
270}
271
272void pcm_play_pause_pause(void)
273{
274#if CONFIG_CPU == PP5020
275 /* Disable the interrupt */
276 IISCONFIG &= ~0x2;
277 /* Disable playback FIFO */
278 IISCONFIG &= ~0x20000000;
279#elif CONFIG_CPU == PP5002
280 /* Disable the interrupt */
281 IISFIFO_CFG &= ~(1<<9);
282 /* Disable playback FIFO */
283 IISCONFIG &= ~0x4;
284#endif
285 disable_fiq();
286}
287
288void pcm_play_pause_unpause(void)
289{
290 /* Enable the FIFO and fill it */
291
292 enable_fiq(fiq);
293
294 /* Enable playback FIFO */
295#if CONFIG_CPU == PP5020
296 IISCONFIG |= 0x20000000;
297#elif CONFIG_CPU == PP5002
298 IISCONFIG |= 0x4;
299#endif
300
301 /* Fill the FIFO - we assume there are enough bytes in the
302 pcm buffer to fill the 32-byte FIFO. */
303 while (p_size > 0) {
304 if (FIFO_FREE_COUNT < 2) {
305 /* Enable interrupt */
306#if CONFIG_CPU == PP5020
307 IISCONFIG |= 0x2;
308#elif CONFIG_CPU == PP5002
309 IISFIFO_CFG |= (1<<9);
310#endif
311 return;
312 }
313
314 IISFIFO_WR = (*(p++))<<16;
315 IISFIFO_WR = (*(p++))<<16;
316 p_size-=4;
317 }
318}
319
320void pcm_set_frequency(unsigned int frequency)
321{
322 (void)frequency;
323 pcm_freq = HW_SAMPR_DEFAULT;
324}
325
326size_t pcm_get_bytes_waiting(void)
327{
328 return p_size;
329}
330
331#ifdef HAVE_PP5024_CODEC
332void pcm_init(void)
333{
334}
335#else
336void pcm_init(void)
337{
338 pcm_playing = false;
339 pcm_paused = false;
340 pcm_callback_for_more = NULL;
341
342 /* Initialize default register values. */
343 audiohw_init();
344
345 /* Power on */
346 audiohw_enable_output(true);
347
348 /* Unmute the master channel (DAC should be at zero point now). */
349 audiohw_mute(false);
350
351 /* Call pcm_play_dma_stop to initialize everything. */
352 pcm_play_dma_stop();
353}
354#endif /* HAVE_PP5024_CODEC */
355
356
357/****************************************************************************
358 ** Recording DMA transfer
359 **/
360static short peak_l, peak_r IBSS_ATTR;
361
362void fiq_record(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ")));
363void fiq_record(void)
364{
365 short value;
366 pcm_more_callback_type2 more_ready;
367 int status = 0;
368
369 /* Clear interrupt */
370#if CONFIG_CPU == PP5020
371 IISCONFIG &= ~0x01;
372#elif CONFIG_CPU == PP5002
373 /* TODO */
374#endif
375
376 while (p_size > 0) {
377 if (FIFO_FREE_COUNT < 2) {
378 /* enable interrupt */
379#if CONFIG_CPU == PP5020
380 IISCONFIG |= 0x01;
381#elif CONFIG_CPU == PP5002
382 /* TODO */
383#endif
384 return;
385 }
386 value = (unsigned short)(IISFIFO_RD >> 16);
387 if (value > peak_l) peak_l = value;
388 else if (-value > peak_l) peak_l = -value;
389 *(p++) = value;
390
391 value = (unsigned short)(IISFIFO_RD >> 16);
392 if (value > peak_r) peak_r = value;
393 else if (-value > peak_r) peak_r = -value;
394 *(p++) = value;
395
396 p_size -= 4;
397
398 /* If we have filled the current chunk, start a new one */
399 if (p_size == 0) {
400 rec_peak_left = peak_l;
401 rec_peak_right = peak_r;
402 peak_l = peak_r = 0;
403 }
404 }
405
406 more_ready = pcm_callback_more_ready;
407
408 if (more_ready != NULL && more_ready(status) >= 0)
409 return;
410
411 /* Finished recording */
412 pcm_rec_dma_stop();
413}
414
415/* Continue transferring data in */
416void pcm_record_more(void *start, size_t size)
417{
418 rec_peak_addr = (unsigned long *)start; /* Start peaking at dest */
419 p = start;
420 p_size = size; /* Bytes to transfer */
421#if CONFIG_CPU == PP5020
422 IISCONFIG |= 0x01;
423#elif CONFIG_CPU == PP5002
424 /* TODO */
425#endif
426}
427
428void pcm_rec_dma_stop(void)
429{
430 logf("pcm_rec_dma_stop");
431
432 /* disable fifo */
433 IISCONFIG &= ~0x10000000;
434
435 disable_fiq();
436
437 pcm_recording = false;
438}
439
440void pcm_rec_dma_start(void *addr, size_t size)
441{
442 logf("pcm_rec_dma_start");
443
444 pcm_recording = true;
445
446 peak_l = peak_r = 0;
447 p_size = size;
448 p = addr;
449
450 /* setup FIQ */
451 outl(inl(0x6000402c) | I2S_MASK, 0x6000402c);
452 outl(I2S_MASK, 0x60004024);
453
454 /* interrupt on full fifo */
455 outl(inl(0x70002800) | 0x1, 0x70002800);
456
457 /* enable record fifo */
458 outl(inl(0x70002800) | 0x10000000, 0x70002800);
459
460 enable_fiq(fiq_record);
461}
462
463void pcm_close_recording(void)
464{
465 logf("pcm_close_recording");
466
467 pcm_rec_dma_stop();
468
469#if (CONFIG_CPU == PP5020)
470 disable_fiq();
471
472 /* disable fifo */
473 IISCONFIG &= ~0x10000000;
474
475 /* Clear interrupt */
476 IISCONFIG &= ~0x01;
477#endif
478} /* pcm_close_recording */
479
480void pcm_init_recording(void)
481{
482 logf("pcm_init_recording");
483
484 pcm_recording = false;
485 pcm_callback_more_ready = NULL;
486
487#if (CONFIG_CPU == PP5020)
488#if defined(IPOD_COLOR) || defined (IPOD_4G)
489 /* The usual magic from IPL - I'm guessing this configures the headphone
490 socket to be input or output - in this case, input. */
491 GPIOI_OUTPUT_VAL &= ~0x40;
492 GPIOA_OUTPUT_VAL &= ~0x4;
493#endif
494 /* Setup the recording FIQ handler */
495 *((unsigned int*)(15*4)) = (unsigned int)&fiq_record;
496#endif
497
498 pcm_rec_dma_stop();
499} /* pcm_init */
500
501void pcm_calculate_rec_peaks(int *left, int *right)
502{
503 *left = rec_peak_left;
504 *right = rec_peak_right;
505}
506
507/*
508 * This function goes directly into the DMA buffer to calculate the left and
509 * right peak values. To avoid missing peaks it tries to look forward two full
510 * peek periods (2/HZ sec, 100% overlap), although it's always possible that
511 * the entire period will not be visible. To reduce CPU load it only looks at
512 * every third sample, and this can be reduced even further if needed (even
513 * every tenth sample would still be pretty accurate).
514 */
515
516/* Check for a peak every PEAK_STRIDE samples */
517#define PEAK_STRIDE 3
518/* Up to 1/50th of a second of audio for peak calculation */
519/* This should use NATIVE_FREQUENCY, or eventually an adjustable freq. value */
520#define PEAK_SAMPLES (44100/50)
521void pcm_calculate_peaks(int *left, int *right)
522{
523 short *addr;
524 short *end;
525 {
526 size_t samples = p_size / 4;
527 addr = p;
528
529 if (samples > PEAK_SAMPLES)
530 samples = PEAK_SAMPLES - (PEAK_STRIDE - 1);
531 else
532 samples -= MIN(PEAK_STRIDE - 1, samples);
533
534 end = &addr[samples * 2];
535 }
536
537 if (left && right) {
538 int left_peak = 0, right_peak = 0;
539
540 while (addr < end) {
541 int value;
542 if ((value = addr [0]) > left_peak)
543 left_peak = value;
544 else if (-value > left_peak)
545 left_peak = -value;
546
547 if ((value = addr [PEAK_STRIDE | 1]) > right_peak)
548 right_peak = value;
549 else if (-value > right_peak)
550 right_peak = -value;
551
552 addr = &addr[PEAK_STRIDE * 2];
553 }
554
555 *left = left_peak;
556 *right = right_peak;
557 }
558 else if (left || right) {
559 int peak_value = 0, value;
560
561 if (right)
562 addr += (PEAK_STRIDE | 1);
563
564 while (addr < end) {
565 if ((value = addr [0]) > peak_value)
566 peak_value = value;
567 else if (-value > peak_value)
568 peak_value = -value;
569
570 addr += PEAK_STRIDE * 2;
571 }
572
573 if (left)
574 *left = peak_value;
575 else
576 *right = peak_value;
577 }
578}