diff options
Diffstat (limited to 'firmware/target/arm/tcc780x/pcm-tcc780x.c')
-rw-r--r-- | firmware/target/arm/tcc780x/pcm-tcc780x.c | 249 |
1 files changed, 237 insertions, 12 deletions
diff --git a/firmware/target/arm/tcc780x/pcm-tcc780x.c b/firmware/target/arm/tcc780x/pcm-tcc780x.c index a9312749c8..7db775d4c7 100644 --- a/firmware/target/arm/tcc780x/pcm-tcc780x.c +++ b/firmware/target/arm/tcc780x/pcm-tcc780x.c | |||
@@ -7,7 +7,8 @@ | |||
7 | * \/ \/ \/ \/ \/ | 7 | * \/ \/ \/ \/ \/ |
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (C) 2007 by Karl Kurbjun | 10 | * Copyright (C) 2006 by Michael Sevakis |
11 | * Copyright (C) 2008 by Rob Purchase | ||
11 | * | 12 | * |
12 | * All files in this archive are subject to the GNU General Public License. | 13 | * 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 | * See the file COPYING in the source tree root for full license agreement. |
@@ -16,52 +17,176 @@ | |||
16 | * KIND, either express or implied. | 17 | * KIND, either express or implied. |
17 | * | 18 | * |
18 | ****************************************************************************/ | 19 | ****************************************************************************/ |
20 | #include <stdlib.h> | ||
19 | #include "system.h" | 21 | #include "system.h" |
20 | #include "kernel.h" | 22 | #include "kernel.h" |
21 | #include "logf.h" | 23 | #include "logf.h" |
22 | #include "audio.h" | 24 | #include "audio.h" |
23 | #include "sound.h" | 25 | #include "sound.h" |
24 | #include "file.h" | 26 | #include "pcm.h" |
27 | |||
28 | struct dma_data | ||
29 | { | ||
30 | /* NOTE: The order of size and p is important if you use assembler | ||
31 | optimised fiq handler, so don't change it. */ | ||
32 | uint16_t *p; | ||
33 | size_t size; | ||
34 | #if NUM_CORES > 1 | ||
35 | unsigned core; | ||
36 | #endif | ||
37 | int locked; | ||
38 | int state; | ||
39 | }; | ||
40 | |||
41 | /**************************************************************************** | ||
42 | ** Playback DMA transfer | ||
43 | **/ | ||
44 | struct dma_data dma_play_data SHAREDBSS_ATTR = | ||
45 | { | ||
46 | /* Initialize to a locked, stopped state */ | ||
47 | .p = NULL, | ||
48 | .size = 0, | ||
49 | #if NUM_CORES > 1 | ||
50 | .core = 0x00, | ||
51 | #endif | ||
52 | .locked = 0, | ||
53 | .state = 0 | ||
54 | }; | ||
55 | |||
56 | static unsigned long pcm_freq SHAREDDATA_ATTR = HW_SAMPR_DEFAULT; /* 44.1 is default */ | ||
25 | 57 | ||
26 | void pcm_postinit(void) | 58 | void pcm_postinit(void) |
27 | { | 59 | { |
60 | /*audiohw_postinit();*/ | ||
61 | pcm_apply_settings(); | ||
28 | } | 62 | } |
29 | 63 | ||
30 | const void * pcm_play_dma_get_peak_buffer(int *count) | 64 | const void * pcm_play_dma_get_peak_buffer(int *count) |
31 | { | 65 | { |
32 | (void) count; | 66 | unsigned long addr = (unsigned long)dma_play_data.p; |
33 | return 0; | 67 | size_t cnt = dma_play_data.size; |
68 | *count = cnt >> 2; | ||
69 | return (void *)((addr + 2) & ~3); | ||
34 | } | 70 | } |
35 | 71 | ||
36 | void pcm_play_dma_init(void) | 72 | void pcm_play_dma_init(void) |
37 | { | 73 | { |
74 | /* Set DAI clock divided from PLL0 (192MHz). | ||
75 | The best approximation of 256*44.1kHz is 11.291MHz. */ | ||
76 | BCLKCTR &= ~DEV_DAI; | ||
77 | PCLK_DAI = (1<<28) | 61682; /* DCO mode */ | ||
78 | BCLKCTR |= DEV_DAI; | ||
79 | |||
80 | /* Enable DAI block in Master mode, 256fs->32fs, 16bit LSB */ | ||
81 | DAMR = 0x3c8e80; | ||
82 | DAVC = 0x0; /* Digital Volume = max */ | ||
83 | |||
84 | /* Set DAI interrupts as FIQs */ | ||
85 | IRQSEL = ~(DAI_RX_IRQ_MASK | DAI_TX_IRQ_MASK); | ||
86 | |||
87 | pcm_set_frequency(SAMPR_44); | ||
88 | |||
89 | /* Initialize default register values. */ | ||
90 | audiohw_init(); | ||
91 | |||
92 | /* Power on */ | ||
93 | audiohw_enable_output(true); | ||
94 | |||
95 | /* Unmute the master channel (DAC should be at zero point now). */ | ||
96 | audiohw_mute(false); | ||
97 | |||
98 | dma_play_data.size = 0; | ||
99 | #if NUM_CORES > 1 | ||
100 | dma_play_data.core = 0; /* no core in control */ | ||
101 | #endif | ||
38 | } | 102 | } |
39 | 103 | ||
40 | void pcm_apply_settings(void) | 104 | void pcm_apply_settings(void) |
41 | { | 105 | { |
106 | pcm_curr_sampr = pcm_freq; | ||
42 | } | 107 | } |
43 | 108 | ||
44 | void pcm_set_frequency(unsigned int frequency) | 109 | void pcm_set_frequency(unsigned int frequency) |
45 | { | 110 | { |
46 | (void) frequency; | 111 | (void) frequency; |
112 | pcm_freq = HW_SAMPR_DEFAULT; | ||
113 | } | ||
114 | |||
115 | static void play_start_pcm(void) | ||
116 | { | ||
117 | pcm_apply_settings(); | ||
118 | |||
119 | DAMR &= ~(1<<14); /* disable tx */ | ||
120 | dma_play_data.state = 1; | ||
121 | |||
122 | if (dma_play_data.size >= 16) | ||
123 | { | ||
124 | DADO_L(0) = *dma_play_data.p++; | ||
125 | DADO_R(0) = *dma_play_data.p++; | ||
126 | DADO_L(1) = *dma_play_data.p++; | ||
127 | DADO_R(1) = *dma_play_data.p++; | ||
128 | DADO_L(2) = *dma_play_data.p++; | ||
129 | DADO_R(2) = *dma_play_data.p++; | ||
130 | DADO_L(3) = *dma_play_data.p++; | ||
131 | DADO_R(3) = *dma_play_data.p++; | ||
132 | dma_play_data.size -= 16; | ||
133 | } | ||
134 | |||
135 | DAMR |= (1<<14); /* enable tx */ | ||
136 | } | ||
137 | |||
138 | static void play_stop_pcm(void) | ||
139 | { | ||
140 | DAMR &= ~(1<<14); /* disable tx */ | ||
141 | dma_play_data.state = 0; | ||
47 | } | 142 | } |
48 | 143 | ||
49 | void pcm_play_dma_start(const void *addr, size_t size) | 144 | void pcm_play_dma_start(const void *addr, size_t size) |
50 | { | 145 | { |
51 | (void) addr; | 146 | dma_play_data.p = (void *)(((uintptr_t)addr + 2) & ~3); |
52 | (void) size; | 147 | dma_play_data.size = (size & ~3); |
148 | |||
149 | #if NUM_CORES > 1 | ||
150 | /* This will become more important later - and different ! */ | ||
151 | dma_play_data.core = processor_id(); /* save initiating core */ | ||
152 | #endif | ||
153 | |||
154 | IEN |= DAI_TX_IRQ_MASK; | ||
155 | |||
156 | play_start_pcm(); | ||
53 | } | 157 | } |
54 | 158 | ||
55 | void pcm_play_dma_stop(void) | 159 | void pcm_play_dma_stop(void) |
56 | { | 160 | { |
161 | play_stop_pcm(); | ||
162 | dma_play_data.size = 0; | ||
163 | #if NUM_CORES > 1 | ||
164 | dma_play_data.core = 0; /* no core in control */ | ||
165 | #endif | ||
57 | } | 166 | } |
58 | 167 | ||
59 | void pcm_play_lock(void) | 168 | void pcm_play_lock(void) |
60 | { | 169 | { |
170 | int status = disable_fiq_save(); | ||
171 | |||
172 | if (++dma_play_data.locked == 1) | ||
173 | { | ||
174 | IEN &= ~DAI_TX_IRQ_MASK; | ||
175 | } | ||
176 | |||
177 | restore_fiq(status); | ||
61 | } | 178 | } |
62 | 179 | ||
63 | void pcm_play_unlock(void) | 180 | void pcm_play_unlock(void) |
64 | { | 181 | { |
182 | int status = disable_fiq_save(); | ||
183 | |||
184 | if (--dma_play_data.locked == 0 && dma_play_data.state != 0) | ||
185 | { | ||
186 | IEN |= DAI_TX_IRQ_MASK; | ||
187 | } | ||
188 | |||
189 | restore_fiq(status); | ||
65 | } | 190 | } |
66 | 191 | ||
67 | void pcm_play_dma_pause(bool pause) | 192 | void pcm_play_dma_pause(bool pause) |
@@ -71,16 +196,116 @@ void pcm_play_dma_pause(bool pause) | |||
71 | 196 | ||
72 | size_t pcm_get_bytes_waiting(void) | 197 | size_t pcm_get_bytes_waiting(void) |
73 | { | 198 | { |
74 | return 0; | 199 | return dma_play_data.size & ~3; |
75 | } | 200 | } |
76 | 201 | ||
202 | #if 1 | ||
203 | void fiq_handler(void) ICODE_ATTR __attribute__((naked)); | ||
77 | void fiq_handler(void) | 204 | void fiq_handler(void) |
78 | { | 205 | { |
79 | /* Clear FIQ status */ | 206 | /* r10 contains DADO_L0 base address (set in crt0.S to minimise code in the |
80 | CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK; | 207 | * FIQ handler. r11 contains address of p (also set in crt0.S). Most other |
81 | 208 | * addresses we need are generated by using offsets with these two. | |
82 | /* Return from FIQ */ | 209 | * r8 and r9 contains local copies of p and size respectively. |
210 | * r0-r3 and r12 is a working register. | ||
211 | */ | ||
83 | asm volatile ( | 212 | asm volatile ( |
84 | "subs pc, lr, #4 \r\n" | 213 | "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */ |
214 | |||
215 | "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */ | ||
216 | "cmp r9, #0x10 \n" /* is size <16? */ | ||
217 | "blt .more_data \n" /* if so, ask pcmbuf for more data */ | ||
218 | |||
219 | ".fill_fifo: \n" | ||
220 | "ldr r12, [r8], #4 \n" /* load two samples */ | ||
221 | "str r12, [r10, #0x0] \n" /* write top sample to DADO_L0 */ | ||
222 | "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */ | ||
223 | "str r12, [r10, #0x4] \n" /* write low sample to DADO_R0*/ | ||
224 | "ldr r12, [r8], #4 \n" /* load two samples */ | ||
225 | "str r12, [r10, #0x8] \n" /* write top sample to DADO_L1 */ | ||
226 | "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */ | ||
227 | "str r12, [r10, #0xc] \n" /* write low sample to DADO_R1*/ | ||
228 | "ldr r12, [r8], #4 \n" /* load two samples */ | ||
229 | "str r12, [r10, #0x10] \n" /* write top sample to DADO_L2 */ | ||
230 | "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */ | ||
231 | "str r12, [r10, #0x14] \n" /* write low sample to DADO_R2*/ | ||
232 | "ldr r12, [r8], #4 \n" /* load two samples */ | ||
233 | "str r12, [r10, #0x18] \n" /* write top sample to DADO_L3 */ | ||
234 | "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */ | ||
235 | "str r12, [r10, #0x1c] \n" /* write low sample to DADO_R3*/ | ||
236 | "sub r9, r9, #0x10 \n" /* 4 words written */ | ||
237 | "stmia r11, { r8-r9 } \n" /* save p and size */ | ||
238 | |||
239 | ".exit: \n" | ||
240 | "mov r8, #0xc000 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */ | ||
241 | "ldr r9, =0xf3001004 \n" /* CREQ */ | ||
242 | "str r8, [r9] \n" /* clear DAI IRQs */ | ||
243 | "ldmfd sp!, { r0-r3, lr } \n" | ||
244 | "subs pc, lr, #4 \n" /* FIQ specific return sequence */ | ||
245 | |||
246 | ".more_data: \n" | ||
247 | "ldr r2, =pcm_callback_for_more \n" | ||
248 | "ldr r2, [r2] \n" /* get callback address */ | ||
249 | "cmp r2, #0 \n" /* check for null pointer */ | ||
250 | "movne r0, r11 \n" /* r0 = &p */ | ||
251 | "addne r1, r11, #4 \n" /* r1 = &size */ | ||
252 | "blxne r2 \n" /* call pcm_callback_for_more */ | ||
253 | "ldmia r11, { r8-r9 } \n" /* reload p and size */ | ||
254 | "cmp r9, #0x10 \n" /* did we actually get more data? */ | ||
255 | "bge .fill_fifo \n" /* yes: fill the fifo */ | ||
256 | "ldr r12, =pcm_play_dma_stop \n" | ||
257 | "blx r12 \n" /* no: stop playback */ | ||
258 | "ldr r12, =pcm_play_dma_stopped_callback \n" | ||
259 | "blx r12 \n" | ||
260 | "b .exit \n" | ||
261 | ".ltorg \n" | ||
85 | ); | 262 | ); |
86 | } | 263 | } |
264 | #else /* C version for reference */ | ||
265 | void fiq_handler(void) ICODE_ATTR __attribute__((naked)); | ||
266 | void fiq_handler(void) | ||
267 | { | ||
268 | asm volatile( "stmfd sp!, {r0-r7, ip, lr} \n" /* Store context */ | ||
269 | "sub sp, sp, #8 \n"); /* Reserve stack */ | ||
270 | |||
271 | register pcm_more_callback_type get_more; | ||
272 | |||
273 | if (dma_play_data.size < 16) | ||
274 | { | ||
275 | /* p is empty, get some more data */ | ||
276 | get_more = pcm_callback_for_more; | ||
277 | if (get_more) | ||
278 | { | ||
279 | get_more((unsigned char**)&dma_play_data.p, | ||
280 | &dma_play_data.size); | ||
281 | } | ||
282 | } | ||
283 | |||
284 | if (dma_play_data.size >= 16) | ||
285 | { | ||
286 | DADO_L(0) = *dma_play_data.p++; | ||
287 | DADO_R(0) = *dma_play_data.p++; | ||
288 | DADO_L(1) = *dma_play_data.p++; | ||
289 | DADO_R(1) = *dma_play_data.p++; | ||
290 | DADO_L(2) = *dma_play_data.p++; | ||
291 | DADO_R(2) = *dma_play_data.p++; | ||
292 | DADO_L(3) = *dma_play_data.p++; | ||
293 | DADO_R(3) = *dma_play_data.p++; | ||
294 | |||
295 | dma_play_data.size -= 16; | ||
296 | } | ||
297 | else | ||
298 | { | ||
299 | /* No more data, so disable the FIFO/interrupt */ | ||
300 | pcm_play_dma_stop(); | ||
301 | pcm_play_dma_stopped_callback(); | ||
302 | } | ||
303 | |||
304 | /* Clear FIQ status */ | ||
305 | CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK; | ||
306 | |||
307 | asm volatile( "add sp, sp, #8 \n" /* Cleanup stack */ | ||
308 | "ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */ | ||
309 | "subs pc, lr, #4 \n"); /* Return from FIQ */ | ||
310 | } | ||
311 | #endif | ||