summaryrefslogtreecommitdiff
path: root/firmware/target/arm
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm')
-rw-r--r--firmware/target/arm/crt0-pp.S8
-rw-r--r--firmware/target/arm/i2s-pp.c81
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c114
-rw-r--r--firmware/target/arm/pcm-pp.c846
-rw-r--r--firmware/target/arm/pnx0101/pcm-pnx0101.c69
-rw-r--r--firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c374
-rw-r--r--firmware/target/arm/system-arm.h6
-rw-r--r--firmware/target/arm/system-pp502x.c1
-rw-r--r--firmware/target/arm/system-target.h14
9 files changed, 673 insertions, 840 deletions
diff --git a/firmware/target/arm/crt0-pp.S b/firmware/target/arm/crt0-pp.S
index 858f795aad..8fd1e31f09 100644
--- a/firmware/target/arm/crt0-pp.S
+++ b/firmware/target/arm/crt0-pp.S
@@ -243,10 +243,10 @@ cpu_init:
243 msr cpsr_c, #0xd1 /* IRQ/FIQ disabled */ 243 msr cpsr_c, #0xd1 /* IRQ/FIQ disabled */
244 ldr sp, =fiq_stack 244 ldr sp, =fiq_stack
245 /* We'll load the banked FIQ mode registers with useful values here. 245 /* We'll load the banked FIQ mode registers with useful values here.
246 These values will be used in the FIQ handler in pcm_playback.c */ 246 These values will be used in the FIQ handler in pcm-pp.c */
247 ldr r12, =IIS_CONFIG 247 ldr r10, =IIS_CONFIG
248 248
249 ldr r11, =p 249 ldr r11, =dma_play_data
250 250
251 /* Let abort and undefined modes use IRQ stack */ 251 /* Let abort and undefined modes use IRQ stack */
252 msr cpsr_c, #0xd7 /* IRQ/FIQ disabled */ 252 msr cpsr_c, #0xd7 /* IRQ/FIQ disabled */
@@ -328,7 +328,7 @@ vectors:
328 .word data_abort_handler 328 .word data_abort_handler
329 .word reserved_handler 329 .word reserved_handler
330 .word irq_handler 330 .word irq_handler
331 .word 0 /* fiq handler set in pcm driver */ 331 .word fiq_handler
332 332
333 .text 333 .text
334 334
diff --git a/firmware/target/arm/i2s-pp.c b/firmware/target/arm/i2s-pp.c
index e06ee835a6..24d901a4c1 100644
--- a/firmware/target/arm/i2s-pp.c
+++ b/firmware/target/arm/i2s-pp.c
@@ -47,99 +47,40 @@ void i2s_reset(void)
47} 47}
48#else /* PP502X */ 48#else /* PP502X */
49 49
50/* All I2S formats send MSB first */
51
52/* Data format on the I2S bus */
53#define FORMAT_MASK (0x3 << 10)
54#define FORMAT_I2S (0x0 << 10) /* Standard I2S - leading dummy bit */
55#define FORMAT_1 (0x1 << 10)
56#define FORMAT_LJUST (0x2 << 10) /* Left justified - no dummy bit */
57#define FORMAT_3 (0x3 << 10)
58/* Other formats not yet known */
59
60/* Data size on I2S bus */
61#define SIZE_MASK (0x3 << 8)
62#define SIZE_16BIT (0x0 << 8)
63/* Other sizes not yet known */
64
65/* Data size/format on I2S FIFO */
66#define FIFO_FORMAT_MASK (0x7 << 4)
67#define FIFO_FORMAT_0 (0x0 << 4)
68/* Big-endian formats - data sent to the FIFO must be big endian.
69 * I forgot which is which size but did test them. */
70#define FIFO_FORMAT_1 (0x1 << 4)
71#define FIFO_FORMAT_2 (0x2 << 4)
72 /* 32bit-MSB-little endian */
73#define FIFO_FORMAT_LE32 (0x3 << 4)
74 /* 16bit-MSB-little endian */
75#define FIFO_FORMAT_LE16 (0x4 << 4)
76
77/* FIFO formats 0x5 and above seem equivalent to 0x4 ?? */
78
79/**
80 * PP502x
81 *
82 * IISCONFIG bits:
83 * | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 |
84 * | RESET | |TXFIFOEN|RXFIFOEN| | ???? | MS | ???? |
85 * | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
86 * | | | | | | | | |
87 * | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
88 * | | | | | Bus Format[1:0] | Size[1:0] |
89 * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
90 * | | Size Format[2:0] | ???? | ???? | IRQTX | IRQRX |
91 *
92 * IISFIFO_CFG bits:
93 * | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 |
94 * | | Free[6:0] |
95 * | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
96 * | | | | | | | | |
97 * | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
98 * | | | | RXCLR | | | | TXCLR |
99 * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
100 * | | | RX_ATN_LEVEL | | | TX_ATN_LEVEL |
101 */
102
103/* Are we I2S Master or slave? */
104#define I2S_MASTER (1<<25)
105
106#define I2S_RESET (0x1 << 31)
107
108/* 50/*
109 * Reset the I2S BIT.FORMAT I2S, 16bit, FIFO.FORMAT 32bit 51 * Reset the I2S BIT.FORMAT I2S, 16bit, FIFO.FORMAT 32bit
110 */ 52 */
111void i2s_reset(void) 53void i2s_reset(void)
112{ 54{
113 /* I2S soft reset */ 55 /* I2S soft reset */
114 IISCONFIG |= I2S_RESET; 56 IISCONFIG |= IIS_RESET;
115 IISCONFIG &= ~I2S_RESET; 57 IISCONFIG &= ~IIS_RESET;
116 58
117 /* BIT.FORMAT */ 59 /* BIT.FORMAT */
118 IISCONFIG = ((IISCONFIG & ~FORMAT_MASK) | FORMAT_I2S); 60 IISCONFIG = ((IISCONFIG & ~IIS_FORMAT_MASK) | IIS_FORMAT_IIS);
119 61
120 /* BIT.SIZE */ 62 /* BIT.SIZE */
121 IISCONFIG = ((IISCONFIG & ~SIZE_MASK) | SIZE_16BIT); 63 IISCONFIG = ((IISCONFIG & ~IIS_SIZE_MASK) | IIS_SIZE_16BIT);
122 64
123 /* FIFO.FORMAT */ 65 /* FIFO.FORMAT */
124 /* If BIT.SIZE < FIFO.FORMAT low bits will be 0 */ 66 /* If BIT.SIZE < FIFO.FORMAT low bits will be 0 */
125#ifdef HAVE_AS3514 67#ifdef HAVE_AS3514
126 /* AS3514 can only operate as I2S Slave */ 68 /* AS3514 can only operate as I2S Slave */
127 IISCONFIG |= I2S_MASTER; 69 IISCONFIG |= IIS_MASTER;
128 /* Set I2S to 44.1kHz */ 70 /* Set I2S to 44.1kHz */
129 outl((inl(0x70002808) & ~(0x1ff)) | 33, 0x70002808); 71 outl((inl(0x70002808) & ~(0x1ff)) | 33, 0x70002808);
130 outl(7, 0x60006080); 72 outl(7, 0x60006080);
131 73 IISCONFIG = ((IISCONFIG & ~IIS_FIFO_FORMAT_MASK) | IIS_FIFO_FORMAT_LE16);
132 IISCONFIG = ((IISCONFIG & ~FIFO_FORMAT_MASK) | FIFO_FORMAT_LE16);
133#else 74#else
134 IISCONFIG = ((IISCONFIG & ~FIFO_FORMAT_MASK) | FIFO_FORMAT_LE32); 75 IISCONFIG = ((IISCONFIG & ~IIS_FIFO_FORMAT_MASK) | IIS_FIFO_FORMAT_LE32);
135#endif 76#endif
136 77
137 /* RX_ATN_LVL=1 == when 12 slots full */ 78 /* RX_ATN_LVL = when 12 slots full */
138 /* TX_ATN_LVL=1 == when 12 slots empty */ 79 /* TX_ATN_LVL = when 12 slots empty */
139 IISFIFO_CFG |= 0x33; 80 IISFIFO_CFG |= IIS_RX_FULL_LVL_12 | IIS_TX_EMPTY_LVL_12;
140 81
141 /* Rx.CLR = 1, TX.CLR = 1 */ 82 /* Rx.CLR = 1, TX.CLR = 1 */
142 IISFIFO_CFG |= 0x1100; 83 IISFIFO_CFG |= IIS_RXCLR | IIS_TXCLR;
143} 84}
144 85
145#endif /* CONFIG_CPU == */ 86#endif /* CONFIG_CPU == */
diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c b/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c
index 73bd9fef14..c29c4b2930 100644
--- a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c
+++ b/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c
@@ -25,69 +25,145 @@
25#include "file.h" 25#include "file.h"
26#include "mmu-imx31.h" 26#include "mmu-imx31.h"
27 27
28static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
29
28void fiq_handler(void) __attribute__((naked)); 30void fiq_handler(void) __attribute__((naked));
29 31
32/* Implement separately on recording and playback - simply disable the
33 specific DMA interrupt. Disable the FIQ itself only temporarily to sync
34 with the DMA interrupt and restore its previous state. The pcm routines
35 will call the lockout first then call into these low-level routines. */
36struct dma_lock
37{
38 int locked;
39 unsigned long state;
40};
41
42static struct dma_play_lock =
43{
44 .locked = 0,
45 .state = 0, /* Initialize this as disabled */
46};
47
48void pcm_play_lock(void)
49{
50 int status = set_fiq_status(FIQ_DISABLED);
51 if (++dma_play_lock.locked == 1)
52 ; /* Mask the DMA interrupt */
53 set_fiq_status(status);
54}
55
56void pcm_play_unlock(void)
57{
58 int status = set_fiq_status(FIQ_DISABLED);
59 if (--dma_play_lock.locked == 0)
60 ; /* Unmask the DMA interrupt if enabled */
61 set_fiq_status(status);
62}
63
30static void _pcm_apply_settings(void) 64static void _pcm_apply_settings(void)
31{ 65{
66 if (pcm_freq != pcm_curr_sampr)
67 {
68 pcm_curr_sampr = pcm_freq;
69 /* Change hardware sample rate */
70 /* */
71 }
32} 72}
33 73
34void pcm_apply_settings(void) 74void pcm_apply_settings(void)
35{ 75{
76 /* Lockout FIQ and sync the hardware to the settings */
77 int oldstatus = set_fiq_status(FIQ_DISABLED);
78 _pcm_apply_settings();
79 set_fiq_status(oldstatus);
36} 80}
37 81
38void pcm_init(void) 82void pcm_play_dma_init(void)
39{ 83{
84 pcm_set_frequency(SAMPR_44);
85
86#if 0
87 /* Do basic init enable output in pcm_postinit if popping is a
88 problem at boot to enable a lenghy delay and let the boot
89 process continue */
90 audiohw_init();
91#endif
40} 92}
41 93
42void pcm_postinit(void) 94void pcm_postinit(void)
43{ 95{
44} 96}
45 97
46void pcm_play_dma_start(const void *addr, size_t size) 98/* Connect the DMA and start filling the FIFO */
99static void play_start_pcm(void)
47{ 100{
48 (void)addr; 101#if 0
49 (void)size; 102 /* unmask DMA interrupt when unlocking */
103 dma_play_lock.state = 0; /* Set to allow pcm_play_unlock to unmask interrupt */
104#endif
50} 105}
51 106
52static void pcm_play_dma_stop_fiq(void) 107/* Disconnect the DMA and wait for the FIFO to clear */
108static void play_stop_pcm(void)
53{ 109{
110#if 0
111 /* Keep interrupt masked when unlocking */
112 dma_play_lock.state = 0; /* Set to keep pcm_play_unlock from unmasking interrupt */
113#endif
54} 114}
55 115
56void fiq_handler(void) 116void pcm_play_dma_start(const void *addr, size_t size)
57{ 117{
118 (void)addr;
119 (void)size;
58} 120}
59 121
60/* Disconnect the DMA and wait for the FIFO to clear */
61void pcm_play_dma_stop(void) 122void pcm_play_dma_stop(void)
62{ 123{
124 play_stop_pcm();
63} 125}
64 126
65void pcm_play_pause_pause(void) 127void pcm_play_dma_pause(bool pause)
66{ 128{
129 if (pause)
130 {
131 play_stop_pcm();
132 }
133 else
134 {
135 play_start_pcm();
136 }
67} 137}
68 138
69void pcm_play_pause_unpause(void) 139/* Get more samples to play out - call pcm_play_dma_stop and
140 pcm_play_dma_stopped_callback if the data runs out */
141void fiq_handler(void)
70{ 142{
143#if 0
144 /* Callback missing or no more DMA to do */
145 pcm_play_dma_stop();
146 pcm_play_dma_stopped_callback();
147#endif
71} 148}
72 149
150/* Set the pcm frequency hardware will use when play is next started or
151 when pcm_apply_settings is called. Do not apply the setting to the
152 hardware here but simply cache it. */
73void pcm_set_frequency(unsigned int frequency) 153void pcm_set_frequency(unsigned int frequency)
74{ 154{
75 (void)frequency; 155 pcm_freq = frequency;
76} 156}
77 157
158/* Return the number of bytes waiting - full L-R sample pairs only */
78size_t pcm_get_bytes_waiting(void) 159size_t pcm_get_bytes_waiting(void)
79{ 160{
80} 161}
81 162
82void pcm_mute(bool mute) 163/* Return a pointer to the samples and the number of them in *count */
164const void * pcm_play_dma_get_peak_buffer(int *count)
83{ 165{
166 (void)count;
84} 167}
85 168
86/** 169/* Any recording functionality should be implemented similarly */
87 * Return playback peaks - Peaks ahead in the DMA buffer based upon the
88 * calling period to attempt to compensate for
89 * delay.
90 */
91void pcm_calculate_peaks(int *left, int *right)
92{
93}
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}
diff --git a/firmware/target/arm/pnx0101/pcm-pnx0101.c b/firmware/target/arm/pnx0101/pcm-pnx0101.c
index 8b076cc918..adfc752e8e 100644
--- a/firmware/target/arm/pnx0101/pcm-pnx0101.c
+++ b/firmware/target/arm/pnx0101/pcm-pnx0101.c
@@ -25,9 +25,36 @@
25short __attribute__((section(".dmabuf"))) dma_buf_left[DMA_BUF_SAMPLES]; 25short __attribute__((section(".dmabuf"))) dma_buf_left[DMA_BUF_SAMPLES];
26short __attribute__((section(".dmabuf"))) dma_buf_right[DMA_BUF_SAMPLES]; 26short __attribute__((section(".dmabuf"))) dma_buf_right[DMA_BUF_SAMPLES];
27 27
28/* From pcm_playback.c */ 28static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
29extern unsigned short* p; 29
30extern size_t p_size; 30unsigned short* p IBSS_ATTR;
31size_t p_size IBSS_ATTR;
32
33void pcm_play_lock(void)
34{
35}
36
37void pcm_play_unlock(void)
38{
39}
40
41void pcm_play_dma_start(const void *addr, size_t size)
42{
43 pcm_apply_settings();
44
45 p = (unsigned short*)addr;
46 p_size = size;
47}
48
49void pcm_play_dma_stop(void)
50{
51}
52
53void pcm_play_dma_pause(bool pause)
54{
55 if (!pause)
56 pcm_apply_settings();
57}
31 58
32static inline void fill_dma_buf(int offset) 59static inline void fill_dma_buf(int offset)
33{ 60{
@@ -85,7 +112,8 @@ static inline void fill_dma_buf(int offset)
85 &p_size); 112 &p_size);
86 } 113 }
87 while (p_size); 114 while (p_size);
88 pcm_playing = false; 115
116 pcm_play_dma_stopped_callback();
89 } 117 }
90 118
91 if (l < lend) 119 if (l < lend)
@@ -117,9 +145,7 @@ void pcm_init(void)
117{ 145{
118 int i; 146 int i;
119 147
120 pcm_playing = false; 148 pcm_set_frequency(HW_SAMPR_DEFAULT);
121 pcm_paused = false;
122 pcm_callback_for_more = NULL;
123 149
124 memset(dma_buf_left, 0, sizeof(dma_buf_left)); 150 memset(dma_buf_left, 0, sizeof(dma_buf_left));
125 memset(dma_buf_right, 0, sizeof(dma_buf_right)); 151 memset(dma_buf_right, 0, sizeof(dma_buf_right));
@@ -159,3 +185,32 @@ void pcm_init(void)
159 DMAR10(1) |= 1; 185 DMAR10(1) |= 1;
160} 186}
161 187
188void pcm_postinit(void)
189{
190 audiohw_postinit();
191 pcm_apply_settings();
192}
193
194void pcm_set_frequency(unsigned int frequency)
195{
196 (void)frequency;
197 pcm_freq = HW_SAMPR_DEFAULT;
198}
199
200void pcm_apply_settings(void)
201{
202 pcm_curr_sampr = pcm_freq;
203}
204
205size_t pcm_get_bytes_waiting(void)
206{
207 return p_size & ~3;
208}
209
210const void * pcm_play_dma_get_peak_buffer(int *count)
211{
212 unsigned long addr = (unsigned long)p;
213 size_t cnt = p_size;
214 *count = cnt >> 2;
215 return (void *)((addr + 2) & ~3);
216}
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
index 57873faaff..a38b4e424e 100644
--- a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
+++ b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
@@ -25,31 +25,42 @@
25#include "file.h" 25#include "file.h"
26#include "mmu-meg-fx.h" 26#include "mmu-meg-fx.h"
27 27
28/* All exact rates for 16.9344MHz clock */
28#define GIGABEAT_11025HZ (0x19 << 1) 29#define GIGABEAT_11025HZ (0x19 << 1)
29#define GIGABEAT_22050HZ (0x1b << 1) 30#define GIGABEAT_22050HZ (0x1b << 1)
30#define GIGABEAT_44100HZ (0x11 << 1) 31#define GIGABEAT_44100HZ (0x11 << 1)
31#define GIGABEAT_88200HZ (0x1f << 1) 32#define GIGABEAT_88200HZ (0x1f << 1)
32 33
33static int pcm_freq = 0; /* 44.1 is default */ 34/* PCM interrupt routine lockout */
35static struct
36{
37 int locked;
38 unsigned long state;
39} dma_play_lock =
40{
41 .locked = 0,
42 .state = (0<<19)
43};
44
45/* Last samplerate set by pcm_set_frequency */
46static unsigned long pcm_freq = 0; /* 44.1 is default */
47/* Samplerate control for audio codec */
34static int sr_ctrl = 0; 48static int sr_ctrl = 0;
35#define FIFO_COUNT ((IISFCON >> 6) & 0x01F) 49
50#define FIFO_COUNT ((IISFCON >> 6) & 0x3F)
36 51
37/* Setup for the DMA controller */ 52/* Setup for the DMA controller */
38#define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20)) 53#define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20))
39 54
40/* DMA count has hit zero - no more data */ 55/* DMA count has hit zero - no more data */
41/* Get more data from the callback and top off the FIFO */ 56/* Get more data from the callback and top off the FIFO */
42/* Uses explicitly coded prologue/epilogue code to get around complier bugs 57void fiq_handler(void) __attribute__((interrupt ("FIQ")));
43 in order to be able to use the stack */
44void fiq_handler(void) __attribute__((naked));
45 58
46static void _pcm_apply_settings(void) 59static void _pcm_apply_settings(void)
47{ 60{
48 static int last_freqency = 0; 61 if (pcm_freq != pcm_curr_sampr)
49
50 if (pcm_freq != last_freqency)
51 { 62 {
52 last_freqency = pcm_freq; 63 pcm_curr_sampr = pcm_freq;
53 audiohw_set_frequency(sr_ctrl); 64 audiohw_set_frequency(sr_ctrl);
54 } 65 }
55} 66}
@@ -61,29 +72,50 @@ void pcm_apply_settings(void)
61 set_fiq_status(oldstatus); 72 set_fiq_status(oldstatus);
62} 73}
63 74
64void pcm_init(void) 75/* For the locks, DMA interrupt must be disabled because the handler
76 manipulates INTMSK and the operation is not atomic */
77void pcm_play_lock(void)
78{
79 int status = set_fiq_status(FIQ_DISABLED);
80 if (++dma_play_lock.locked == 1)
81 INTMSK |= (1<<19); /* Mask the DMA interrupt */
82 set_fiq_status(status);
83}
84
85void pcm_play_unlock(void)
65{ 86{
66 pcm_playing = false; 87 int status = set_fiq_status(FIQ_DISABLED);
67 pcm_paused = false; 88 if (--dma_play_lock.locked == 0)
68 pcm_callback_for_more = NULL; 89 INTMSK &= ~dma_play_lock.state; /* Unmask the DMA interrupt if enabled */
90 set_fiq_status(status);
91}
69 92
93void pcm_play_dma_init(void)
94{
70 pcm_set_frequency(SAMPR_44); 95 pcm_set_frequency(SAMPR_44);
71 96
72 /* slave */ 97 /* slave */
73 IISMOD |= (1<<8); 98 IISMOD |= (1<<8);
74 99
100 /* RX,TX off,idle */
101 IISCON |= (1<<3) | (1<<2);
102
75 audiohw_init(); 103 audiohw_init();
76 104
77 /* init GPIO */ 105 /* init GPIO */
78 GPCCON = (GPCCON & ~(3<<14)) | (1<<14); 106 GPCCON = (GPCCON & ~(3<<14)) | (1<<14);
79 GPCDAT |= 1<<7; 107 GPCDAT |= (1<<7);
80 GPECON |= 0x2aa; 108 /* GPE4=I2SDO, GPE3=I2SDI, GPE2=CDCLK, GPE1=I2SSCLK, GPE0=I2SLRCK */
109 GPECON = (GPECON & ~0x3ff) | 0x2aa;
81 110
82 /* Do not service DMA requests, yet */ 111 /* Do not service DMA requests, yet */
112
83 /* clear any pending int and mask it */ 113 /* clear any pending int and mask it */
84 INTMSK |= (1<<19); /* mask the interrupt */ 114 INTMSK |= (1<<19);
85 SRCPND = (1<<19); /* clear any pending interrupts */ 115 SRCPND = (1<<19);
86 INTMOD |= (1<<19); /* connect to FIQ */ 116
117 /* connect to FIQ */
118 INTMOD |= (1<<19);
87} 119}
88 120
89void pcm_postinit(void) 121void pcm_postinit(void)
@@ -92,21 +124,69 @@ void pcm_postinit(void)
92 pcm_apply_settings(); 124 pcm_apply_settings();
93} 125}
94 126
95void pcm_play_dma_start(const void *addr, size_t size) 127/* Connect the DMA and start filling the FIFO */
128static void play_start_pcm(void)
129{
130 /* clear pending DMA interrupt */
131 SRCPND = (1<<19);
132
133 _pcm_apply_settings();
134
135 /* Flush any pending writes */
136 clean_dcache_range((void*)DISRC2, (DCON2 & 0xFFFFF) * 2);
137
138 /* unmask DMA interrupt when unlocking */
139 dma_play_lock.state = (1<<19);
140
141 /* turn on the request */
142 IISCON |= (1<<5);
143
144 /* Activate the channel */
145 DMASKTRIG2 = 0x2;
146
147 /* turn off the idle */
148 IISCON &= ~(1<<3);
149
150 /* start the IIS */
151 IISCON |= (1<<0);
152}
153
154/* Disconnect the DMA and wait for the FIFO to clear */
155static void play_stop_pcm(void)
96{ 156{
97 addr = (void *)((unsigned long)addr & ~3); /* Align data */ 157 /* Mask DMA interrupt */
98 size &= ~3; /* Size must be multiple of 4 */ 158 INTMSK |= (1<<19);
159
160 /* De-Activate the DMA channel */
161 DMASKTRIG2 = 0x4;
162
163 /* are we playing? wait for the chunk to finish */
164 if (dma_play_lock.state != 0)
165 {
166 /* wait for the FIFO to empty and DMA to stop */
167 while ((IISCON & (1<<7)) || (DMASKTRIG2 & 0x2));
168 }
169
170 /* Keep interrupt masked when unlocking */
171 dma_play_lock.state = 0;
172
173 /* turn off the request */
174 IISCON &= ~(1<<5);
99 175
100 /* sanity check: bad pointer or too small file */ 176 /* turn on the idle */
101 if (NULL == addr || size == 0) return; 177 IISCON |= (1<<3);
102 178
103 disable_fiq(); 179 /* stop the IIS */
180 IISCON &= ~(1<<0);
181}
104 182
183void pcm_play_dma_start(const void *addr, size_t size)
184{
105 /* Enable the IIS clock */ 185 /* Enable the IIS clock */
106 CLKCON |= (1<<17); 186 CLKCON |= (1<<17);
107 187
108 /* IIS interface setup and set to idle */ 188 /* stop any DMA in progress - idle IIS */
109 IISCON = (1<<5) | (1<<3); 189 play_stop_pcm();
110 190
111 /* slave, transmit mode, 16 bit samples - MCLK 384fs - use 16.9344Mhz - 191 /* slave, transmit mode, 16 bit samples - MCLK 384fs - use 16.9344Mhz -
112 BCLK 32fs */ 192 BCLK 32fs */
@@ -116,155 +196,80 @@ void pcm_play_dma_start(const void *addr, size_t size)
116 IISFCON = (1<<15) | (1<<13); 196 IISFCON = (1<<15) | (1<<13);
117 197
118 /* set DMA dest */ 198 /* set DMA dest */
119 DIDST2 = (int)&IISFIFO; 199 DIDST2 = (unsigned int)&IISFIFO;
120 200
121 /* IIS is on the APB bus, INT when TC reaches 0, fixed dest addr */ 201 /* IIS is on the APB bus, INT when TC reaches 0, fixed dest addr */
122 DIDSTC2 = 0x03; 202 DIDSTC2 = 0x03;
123 203
204 /* set DMA source and options */
205 DISRC2 = (unsigned int)addr + 0x30000000;
124 /* How many transfers to make - we transfer half-word at a time = 2 bytes */ 206 /* How many transfers to make - we transfer half-word at a time = 2 bytes */
125 /* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */ 207 /* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */
126 /* no auto-reload, half-word (16bit) */ 208 /* no auto-reload, half-word (16bit) */
127 DCON2 = DMA_CONTROL_SETUP | (size / 2); 209 DCON2 = DMA_CONTROL_SETUP | (size / 2);
128
129 /* set DMA source and options */
130 DISRC2 = (unsigned long)addr + 0x30000000;
131 DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */ 210 DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */
132 211
133 /* clear pending DMA interrupt */ 212 play_start_pcm();
134 SRCPND = 1<<19; 213}
135
136 pcm_playing = true;
137
138 _pcm_apply_settings();
139
140 /* unmask the DMA interrupt */
141 INTMSK &= ~(1<<19);
142
143 /* Flush any pending writes */
144 clean_dcache_range(addr, size);
145
146 /* Activate the channel */
147 DMASKTRIG2 = 0x2;
148
149 /* turn off the idle */
150 IISCON &= ~(1<<3);
151 214
152 /* start the IIS */ 215/* Promptly stop DMA transfers and stop IIS */
153 IISCON |= (1<<0); 216void pcm_play_dma_stop(void)
217{
218 play_stop_pcm();
154 219
155 enable_fiq(); 220 /* Disconnect the IIS clock */
221 CLKCON &= ~(1<<17);
156} 222}
157 223
158static void pcm_play_dma_stop_fiq(void) 224void pcm_play_dma_pause(bool pause)
159{ 225{
160 INTMSK |= (1<<19); /* mask the DMA interrupt */ 226 if (pause)
161 IISCON &= ~(1<<5); /* disable fifo request */
162 DMASKTRIG2 = 0x4; /* De-Activate the DMA channel */
163
164 /* are we playing? wait for the chunk to finish */
165 if (pcm_playing)
166 { 227 {
167 /* wait for the FIFO to empty before turning things off */ 228 /* pause playback on current buffer */
168 while (IISCON & (1<<7)) ; 229 play_stop_pcm();
169 230 }
170 pcm_playing = false; 231 else
171 if (!audio_status()) 232 {
172 pcm_paused = false; 233 /* restart playback on current buffer */
234 /* make sure we're aligned on left channel - skip any right
235 channel sample left waiting */
236 DISRC2 = (DCSRC2 + 2) & ~0x3;
237 DCON2 = DMA_CONTROL_SETUP | (DSTAT2 & 0xFFFFE);
238 play_start_pcm();
173 } 239 }
174
175 /* Disconnect the IIS clock */
176 CLKCON &= ~(1<<17);
177} 240}
178 241
179void fiq_handler(void) 242void fiq_handler(void)
180{ 243{
181 /* r0-r7 are probably not all used by GCC but there's no way to know 244 static unsigned char *start;
182 otherwise this whole thing must be assembly */ 245 static size_t size;
183 asm volatile ("stmfd sp!, {r0-r7, ip, lr} \n" /* Store context */
184 "sub sp, sp, #8 \n"); /* Reserve stack */
185 register pcm_more_callback_type get_more; /* No stack for this */ 246 register pcm_more_callback_type get_more; /* No stack for this */
186 unsigned char *next_start; /* sp + #0 */
187 size_t next_size; /* sp + #4 */
188 247
189 /* clear any pending interrupt */ 248 /* clear any pending interrupt */
190 SRCPND = (1<<19); 249 SRCPND = (1<<19);
191 250
192 /* Buffer empty. Try to get more. */ 251 /* Buffer empty. Try to get more. */
193 get_more = pcm_callback_for_more; 252 get_more = pcm_callback_for_more;
194 if (get_more == NULL) 253 size = 0;
195 {
196 /* Callback missing */
197 pcm_play_dma_stop_fiq();
198 goto fiq_exit;
199 }
200
201 next_size = 0;
202 get_more(&next_start, &next_size);
203 254
204 if (next_size == 0) 255 if (get_more == NULL || (get_more(&start, &size), size == 0))
205 { 256 {
206 /* No more DMA to do */ 257 /* Callback missing or no more DMA to do */
207 pcm_play_dma_stop_fiq(); 258 pcm_play_dma_stop();
208 goto fiq_exit; 259 pcm_play_dma_stopped_callback();
209 } 260 }
210 261 else
211 /* Flush any pending cache writes */
212 clean_dcache_range(next_start, next_size);
213
214 /* set the new DMA values */
215 DCON2 = DMA_CONTROL_SETUP | (next_size >> 1);
216 DISRC2 = (unsigned long)next_start + 0x30000000;
217
218 /* Re-Activate the channel */
219 DMASKTRIG2 = 0x2;
220
221fiq_exit:
222 asm volatile("add sp, sp, #8 \n" /* Cleanup stack */
223 "ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */
224 "subs pc, lr, #4 \n"); /* Return from FIQ */
225}
226
227/* Disconnect the DMA and wait for the FIFO to clear */
228void pcm_play_dma_stop(void)
229{
230 disable_fiq();
231 pcm_play_dma_stop_fiq();
232}
233
234void pcm_play_pause_pause(void)
235{
236 /* stop servicing refills */
237 int oldstatus = set_fiq_status(FIQ_DISABLED);
238 INTMSK |= (1<<19); /* mask interrupt request */
239 IISCON &= ~(1<<5); /* turn off FIFO request */
240 DMASKTRIG2 = 0x4; /* stop DMA at end of atomic transfer */
241
242 if (pcm_playing)
243 { 262 {
244 /* playing - wait for the FIFO to empty */ 263 /* Flush any pending cache writes */
245 while (IISCON & (1<<7)) ; 264 clean_dcache_range(start, size);
246 }
247 265
248 set_fiq_status(oldstatus); 266 /* set the new DMA values */
249} 267 DCON2 = DMA_CONTROL_SETUP | (size >> 1);
268 DISRC2 = (unsigned int)start + 0x30000000;
250 269
251void pcm_play_pause_unpause(void) 270 /* Re-Activate the channel */
252{ 271 DMASKTRIG2 = 0x2;
253 /* refill buffer and keep going */
254 int oldstatus = set_fiq_status(FIQ_DISABLED);
255 _pcm_apply_settings();
256 if (pcm_playing)
257 {
258 /* make sure we're aligned on left channel - skip any right channel
259 sample left waiting */
260 DISRC2 = (DCSRC2 + 2) & ~0x3;
261 DCON2 = (DSTAT2 & 0xFFFFE);
262
263 SRCPND = (1<<19); /* clear pending DMA interrupt */
264 INTMSK &= ~(1<<19); /* unmask interrupt request */
265 IISCON |= (1<<5); /* enable FIFO request */
266 } 272 }
267 set_fiq_status(oldstatus);
268} 273}
269 274
270void pcm_set_frequency(unsigned int frequency) 275void pcm_set_frequency(unsigned int frequency)
@@ -296,89 +301,10 @@ size_t pcm_get_bytes_waiting(void)
296 return (DSTAT2 & 0xFFFFE) * 2; 301 return (DSTAT2 & 0xFFFFE) * 2;
297} 302}
298 303
299/** **/ 304const void * pcm_play_dma_get_peak_buffer(int *count)
300
301void pcm_mute(bool mute)
302{ 305{
303 audiohw_mute(mute); 306 unsigned long addr = DCSRC2;
304 if (mute) 307 int cnt = DSTAT2;
305 sleep(HZ/16); 308 *count = (cnt & 0xFFFFF) >> 1;
309 return (void *)((addr + 2) & ~3);
306} 310}
307
308/**
309 * Return playback peaks - Peaks ahead in the DMA buffer based upon the
310 * calling period to attempt to compensate for
311 * delay.
312 */
313void pcm_calculate_peaks(int *left, int *right)
314{
315 static unsigned long last_peak_tick = 0;
316 static unsigned long frame_period = 0;
317 static int peaks_l = 0, peaks_r = 0;
318
319 /* Throttled peak ahead based on calling period */
320 unsigned long period = current_tick - last_peak_tick;
321
322 /* Keep reasonable limits on period */
323 if (period < 1)
324 period = 1;
325 else if (period > HZ/5)
326 period = HZ/5;
327
328 frame_period = (3*frame_period + period) >> 2;
329
330 last_peak_tick = current_tick;
331
332 if (pcm_playing && !pcm_paused)
333 {
334 unsigned long *addr = (unsigned long *)DCSRC2;
335 long samples = DSTAT2;
336 long samp_frames;
337
338 addr = (unsigned long *)((unsigned long)addr & ~3);
339 samples &= 0xFFFFE;
340 samp_frames = frame_period*pcm_freq/(HZ/2);
341 samples = MIN(samp_frames, samples) >> 1;
342
343 if (samples > 0)
344 {
345 long peak_l = 0, peak_r = 0;
346 long peaksq_l = 0, peaksq_r = 0;
347
348 addr -= 0x30000000 >> 2;
349 addr = (long *)((long)addr & ~3);
350
351 do
352 {
353 long value = *addr;
354 long ch, chsq;
355
356 ch = (int16_t)value;
357 chsq = ch*ch;
358 if (chsq > peaksq_l)
359 peak_l = ch, peaksq_l = chsq;
360
361 ch = value >> 16;
362 chsq = ch*ch;
363 if (chsq > peaksq_r)
364 peak_r = ch, peaksq_r = chsq;
365
366 addr += 4;
367 }
368 while ((samples -= 4) > 0);
369
370 peaks_l = abs(peak_l);
371 peaks_r = abs(peak_r);
372 }
373 }
374 else
375 {
376 peaks_l = peaks_r = 0;
377 }
378
379 if (left)
380 *left = peaks_l;
381
382 if (right)
383 *right = peaks_r;
384} /* pcm_calculate_peaks */
diff --git a/firmware/target/arm/system-arm.h b/firmware/target/arm/system-arm.h
index c3af652ebc..774cdbcff4 100644
--- a/firmware/target/arm/system-arm.h
+++ b/firmware/target/arm/system-arm.h
@@ -74,12 +74,6 @@ static inline uint32_t swap_odd_even32(uint32_t value)
74 return value; 74 return value;
75} 75}
76 76
77static inline void set_fiq_handler(void(*fiq_handler)(void))
78{
79 /* Install the FIQ handler */
80 *((unsigned int*)(15*4)) = (unsigned int)fiq_handler;
81}
82
83static inline void enable_fiq(void) 77static inline void enable_fiq(void)
84{ 78{
85 /* Clear FIQ disable bit */ 79 /* Clear FIQ disable bit */
diff --git a/firmware/target/arm/system-pp502x.c b/firmware/target/arm/system-pp502x.c
index 576459d6c1..8110cfc127 100644
--- a/firmware/target/arm/system-pp502x.c
+++ b/firmware/target/arm/system-pp502x.c
@@ -214,6 +214,7 @@ void system_init(void)
214 outl(inl(0x70000024) | 0xc0, 0x70000024); 214 outl(inl(0x70000024) | 0xc0, 0x70000024);
215 DEV_RS = 0; 215 DEV_RS = 0;
216 DEV_OFF_MASK = 0; 216 DEV_OFF_MASK = 0;
217 STRAP_OPT_A = 0x80;
217#endif 218#endif
218 219
219#if !defined(SANSA_E200) && !defined(SANSA_C200) 220#if !defined(SANSA_E200) && !defined(SANSA_C200)
diff --git a/firmware/target/arm/system-target.h b/firmware/target/arm/system-target.h
index 6b35a49c97..7a1ff4f79a 100644
--- a/firmware/target/arm/system-target.h
+++ b/firmware/target/arm/system-target.h
@@ -70,6 +70,20 @@ static inline unsigned int current_core(void)
70 return core; 70 return core;
71} 71}
72 72
73/* Return the actual ID instead of core index */
74static inline unsigned int processor_id(void)
75{
76 unsigned char id;
77
78 asm volatile (
79 "ldrb %0, [%1] \n"
80 : "=r"(id)
81 : "r"(&PROCESSOR_ID)
82 );
83
84 return id;
85}
86
73#ifdef BOOTLOADER 87#ifdef BOOTLOADER
74/* All addresses within rockbox are in IRAM in the bootloader so 88/* All addresses within rockbox are in IRAM in the bootloader so
75 are therefore uncached */ 89 are therefore uncached */