summaryrefslogtreecommitdiff
path: root/firmware/target/arm/tcc780x/pcm-tcc780x.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/tcc780x/pcm-tcc780x.c')
-rw-r--r--firmware/target/arm/tcc780x/pcm-tcc780x.c249
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
28struct 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 **/
44struct 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
56static unsigned long pcm_freq SHAREDDATA_ATTR = HW_SAMPR_DEFAULT; /* 44.1 is default */
25 57
26void pcm_postinit(void) 58void pcm_postinit(void)
27{ 59{
60 /*audiohw_postinit();*/
61 pcm_apply_settings();
28} 62}
29 63
30const void * pcm_play_dma_get_peak_buffer(int *count) 64const 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
36void pcm_play_dma_init(void) 72void 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
40void pcm_apply_settings(void) 104void pcm_apply_settings(void)
41{ 105{
106 pcm_curr_sampr = pcm_freq;
42} 107}
43 108
44void pcm_set_frequency(unsigned int frequency) 109void pcm_set_frequency(unsigned int frequency)
45{ 110{
46 (void) frequency; 111 (void) frequency;
112 pcm_freq = HW_SAMPR_DEFAULT;
113}
114
115static 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
138static void play_stop_pcm(void)
139{
140 DAMR &= ~(1<<14); /* disable tx */
141 dma_play_data.state = 0;
47} 142}
48 143
49void pcm_play_dma_start(const void *addr, size_t size) 144void 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
55void pcm_play_dma_stop(void) 159void 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
59void pcm_play_lock(void) 168void 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
63void pcm_play_unlock(void) 180void 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
67void pcm_play_dma_pause(bool pause) 192void pcm_play_dma_pause(bool pause)
@@ -71,16 +196,116 @@ void pcm_play_dma_pause(bool pause)
71 196
72size_t pcm_get_bytes_waiting(void) 197size_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
203void fiq_handler(void) ICODE_ATTR __attribute__((naked));
77void fiq_handler(void) 204void 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 */
265void fiq_handler(void) ICODE_ATTR __attribute__((naked));
266void 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