blob: 375274438a6d53a27f7d703b408a8d05563fa289 [file] [log] [blame]
Rob Purchase47ea0302008-01-14 22:04:48 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
Rob Purchased6159ea2008-06-22 18:48:22 +000010 * Copyright (C) 2006 by Michael Sevakis
11 * Copyright (C) 2008 by Rob Purchase
Rob Purchase47ea0302008-01-14 22:04:48 +000012 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000013 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
Rob Purchase47ea0302008-01-14 22:04:48 +000017 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
Rob Purchased6159ea2008-06-22 18:48:22 +000022#include <stdlib.h>
Rob Purchase47ea0302008-01-14 22:04:48 +000023#include "system.h"
24#include "kernel.h"
25#include "logf.h"
26#include "audio.h"
27#include "sound.h"
Rob Purchased6159ea2008-06-22 18:48:22 +000028#include "pcm.h"
29
30struct dma_data
31{
32/* NOTE: The order of size and p is important if you use assembler
33 optimised fiq handler, so don't change it. */
34 uint16_t *p;
35 size_t size;
36#if NUM_CORES > 1
37 unsigned core;
38#endif
39 int locked;
40 int state;
41};
42
43/****************************************************************************
44 ** Playback DMA transfer
45 **/
46struct dma_data dma_play_data SHAREDBSS_ATTR =
47{
48 /* Initialize to a locked, stopped state */
49 .p = NULL,
50 .size = 0,
51#if NUM_CORES > 1
52 .core = 0x00,
53#endif
54 .locked = 0,
55 .state = 0
56};
57
58static unsigned long pcm_freq SHAREDDATA_ATTR = HW_SAMPR_DEFAULT; /* 44.1 is default */
Rob Purchase47ea0302008-01-14 22:04:48 +000059
60void pcm_postinit(void)
61{
Rob Purchased6159ea2008-06-22 18:48:22 +000062 /*audiohw_postinit();*/
63 pcm_apply_settings();
Rob Purchase47ea0302008-01-14 22:04:48 +000064}
65
66const void * pcm_play_dma_get_peak_buffer(int *count)
67{
Rob Purchased6159ea2008-06-22 18:48:22 +000068 unsigned long addr = (unsigned long)dma_play_data.p;
69 size_t cnt = dma_play_data.size;
70 *count = cnt >> 2;
71 return (void *)((addr + 2) & ~3);
Rob Purchase47ea0302008-01-14 22:04:48 +000072}
73
74void pcm_play_dma_init(void)
75{
Rob Purchased6159ea2008-06-22 18:48:22 +000076 /* Set DAI clock divided from PLL0 (192MHz).
77 The best approximation of 256*44.1kHz is 11.291MHz. */
78 BCLKCTR &= ~DEV_DAI;
79 PCLK_DAI = (1<<28) | 61682; /* DCO mode */
80 BCLKCTR |= DEV_DAI;
81
82 /* Enable DAI block in Master mode, 256fs->32fs, 16bit LSB */
83 DAMR = 0x3c8e80;
84 DAVC = 0x0; /* Digital Volume = max */
85
86 /* Set DAI interrupts as FIQs */
87 IRQSEL = ~(DAI_RX_IRQ_MASK | DAI_TX_IRQ_MASK);
88
89 pcm_set_frequency(SAMPR_44);
90
91 /* Initialize default register values. */
92 audiohw_init();
93
94 /* Power on */
95 audiohw_enable_output(true);
96
97 /* Unmute the master channel (DAC should be at zero point now). */
98 audiohw_mute(false);
99
100 dma_play_data.size = 0;
101#if NUM_CORES > 1
102 dma_play_data.core = 0; /* no core in control */
103#endif
Rob Purchase47ea0302008-01-14 22:04:48 +0000104}
105
106void pcm_apply_settings(void)
107{
Rob Purchased6159ea2008-06-22 18:48:22 +0000108 pcm_curr_sampr = pcm_freq;
Rob Purchase47ea0302008-01-14 22:04:48 +0000109}
110
111void pcm_set_frequency(unsigned int frequency)
112{
Rob Purchase47ea0302008-01-14 22:04:48 +0000113 (void) frequency;
Rob Purchased6159ea2008-06-22 18:48:22 +0000114 pcm_freq = HW_SAMPR_DEFAULT;
115}
116
117static void play_start_pcm(void)
118{
119 pcm_apply_settings();
120
121 DAMR &= ~(1<<14); /* disable tx */
122 dma_play_data.state = 1;
123
124 if (dma_play_data.size >= 16)
125 {
126 DADO_L(0) = *dma_play_data.p++;
127 DADO_R(0) = *dma_play_data.p++;
128 DADO_L(1) = *dma_play_data.p++;
129 DADO_R(1) = *dma_play_data.p++;
130 DADO_L(2) = *dma_play_data.p++;
131 DADO_R(2) = *dma_play_data.p++;
132 DADO_L(3) = *dma_play_data.p++;
133 DADO_R(3) = *dma_play_data.p++;
134 dma_play_data.size -= 16;
135 }
136
137 DAMR |= (1<<14); /* enable tx */
138}
139
140static void play_stop_pcm(void)
141{
142 DAMR &= ~(1<<14); /* disable tx */
143 dma_play_data.state = 0;
Rob Purchase47ea0302008-01-14 22:04:48 +0000144}
145
146void pcm_play_dma_start(const void *addr, size_t size)
147{
Rob Purchased6159ea2008-06-22 18:48:22 +0000148 dma_play_data.p = (void *)(((uintptr_t)addr + 2) & ~3);
149 dma_play_data.size = (size & ~3);
150
151#if NUM_CORES > 1
152 /* This will become more important later - and different ! */
153 dma_play_data.core = processor_id(); /* save initiating core */
154#endif
155
156 IEN |= DAI_TX_IRQ_MASK;
157
158 play_start_pcm();
Rob Purchase47ea0302008-01-14 22:04:48 +0000159}
160
161void pcm_play_dma_stop(void)
162{
Rob Purchased6159ea2008-06-22 18:48:22 +0000163 play_stop_pcm();
164 dma_play_data.size = 0;
165#if NUM_CORES > 1
166 dma_play_data.core = 0; /* no core in control */
167#endif
Rob Purchase47ea0302008-01-14 22:04:48 +0000168}
169
170void pcm_play_lock(void)
171{
Rob Purchased6159ea2008-06-22 18:48:22 +0000172 int status = disable_fiq_save();
173
174 if (++dma_play_data.locked == 1)
175 {
176 IEN &= ~DAI_TX_IRQ_MASK;
177 }
178
179 restore_fiq(status);
Rob Purchase47ea0302008-01-14 22:04:48 +0000180}
181
182void pcm_play_unlock(void)
183{
Rob Purchased6159ea2008-06-22 18:48:22 +0000184 int status = disable_fiq_save();
185
186 if (--dma_play_data.locked == 0 && dma_play_data.state != 0)
187 {
188 IEN |= DAI_TX_IRQ_MASK;
189 }
190
191 restore_fiq(status);
Rob Purchase47ea0302008-01-14 22:04:48 +0000192}
193
194void pcm_play_dma_pause(bool pause)
195{
Rob Purchase47ea0302008-01-14 22:04:48 +0000196 (void) pause;
197}
198
199size_t pcm_get_bytes_waiting(void)
200{
Rob Purchased6159ea2008-06-22 18:48:22 +0000201 return dma_play_data.size & ~3;
Rob Purchase47ea0302008-01-14 22:04:48 +0000202}
Rob Purchasefd773cb2008-04-21 20:16:18 +0000203
Rob Purchased6159ea2008-06-22 18:48:22 +0000204#if 1
205void fiq_handler(void) ICODE_ATTR __attribute__((naked));
Rob Purchasefd773cb2008-04-21 20:16:18 +0000206void fiq_handler(void)
207{
Rob Purchased6159ea2008-06-22 18:48:22 +0000208 /* r10 contains DADO_L0 base address (set in crt0.S to minimise code in the
209 * FIQ handler. r11 contains address of p (also set in crt0.S). Most other
210 * addresses we need are generated by using offsets with these two.
211 * r8 and r9 contains local copies of p and size respectively.
212 * r0-r3 and r12 is a working register.
213 */
Rob Purchase39aaa2f2008-06-27 12:39:03 +0000214 asm volatile (
215 "mov r8, #0xc000 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */
216 "ldr r9, =0xf3001004 \n" /* CREQ */
217 "str r8, [r9] \n" /* clear DAI IRQs */
Rob Purchased6159ea2008-06-22 18:48:22 +0000218
219 "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */
220 "cmp r9, #0x10 \n" /* is size <16? */
221 "blt .more_data \n" /* if so, ask pcmbuf for more data */
222
223 ".fill_fifo: \n"
224 "ldr r12, [r8], #4 \n" /* load two samples */
225 "str r12, [r10, #0x0] \n" /* write top sample to DADO_L0 */
226 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
227 "str r12, [r10, #0x4] \n" /* write low sample to DADO_R0*/
228 "ldr r12, [r8], #4 \n" /* load two samples */
229 "str r12, [r10, #0x8] \n" /* write top sample to DADO_L1 */
230 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
231 "str r12, [r10, #0xc] \n" /* write low sample to DADO_R1*/
232 "ldr r12, [r8], #4 \n" /* load two samples */
233 "str r12, [r10, #0x10] \n" /* write top sample to DADO_L2 */
234 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
235 "str r12, [r10, #0x14] \n" /* write low sample to DADO_R2*/
236 "ldr r12, [r8], #4 \n" /* load two samples */
237 "str r12, [r10, #0x18] \n" /* write top sample to DADO_L3 */
238 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
239 "str r12, [r10, #0x1c] \n" /* write low sample to DADO_R3*/
240 "sub r9, r9, #0x10 \n" /* 4 words written */
241 "stmia r11, { r8-r9 } \n" /* save p and size */
242
243 ".exit: \n"
Rob Purchased6159ea2008-06-22 18:48:22 +0000244 "subs pc, lr, #4 \n" /* FIQ specific return sequence */
245
246 ".more_data: \n"
Rob Purchase39aaa2f2008-06-27 12:39:03 +0000247 "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */
Rob Purchased6159ea2008-06-22 18:48:22 +0000248 "ldr r2, =pcm_callback_for_more \n"
249 "ldr r2, [r2] \n" /* get callback address */
250 "cmp r2, #0 \n" /* check for null pointer */
251 "movne r0, r11 \n" /* r0 = &p */
252 "addne r1, r11, #4 \n" /* r1 = &size */
253 "blxne r2 \n" /* call pcm_callback_for_more */
254 "ldmia r11, { r8-r9 } \n" /* reload p and size */
255 "cmp r9, #0x10 \n" /* did we actually get more data? */
Rob Purchase39aaa2f2008-06-27 12:39:03 +0000256 "ldmgefd sp!, { r0-r3, lr } \n"
Rob Purchased6159ea2008-06-22 18:48:22 +0000257 "bge .fill_fifo \n" /* yes: fill the fifo */
258 "ldr r12, =pcm_play_dma_stop \n"
259 "blx r12 \n" /* no: stop playback */
260 "ldr r12, =pcm_play_dma_stopped_callback \n"
261 "blx r12 \n"
Rob Purchase39aaa2f2008-06-27 12:39:03 +0000262 "ldmfd sp!, { r0-r3, lr } \n"
Rob Purchased6159ea2008-06-22 18:48:22 +0000263 "b .exit \n"
264 ".ltorg \n"
Rob Purchasefd773cb2008-04-21 20:16:18 +0000265 );
266}
Rob Purchased6159ea2008-06-22 18:48:22 +0000267#else /* C version for reference */
268void fiq_handler(void) ICODE_ATTR __attribute__((naked));
269void fiq_handler(void)
270{
271 asm volatile( "stmfd sp!, {r0-r7, ip, lr} \n" /* Store context */
272 "sub sp, sp, #8 \n"); /* Reserve stack */
273
274 register pcm_more_callback_type get_more;
275
276 if (dma_play_data.size < 16)
277 {
278 /* p is empty, get some more data */
279 get_more = pcm_callback_for_more;
280 if (get_more)
281 {
282 get_more((unsigned char**)&dma_play_data.p,
283 &dma_play_data.size);
284 }
285 }
286
287 if (dma_play_data.size >= 16)
288 {
289 DADO_L(0) = *dma_play_data.p++;
290 DADO_R(0) = *dma_play_data.p++;
291 DADO_L(1) = *dma_play_data.p++;
292 DADO_R(1) = *dma_play_data.p++;
293 DADO_L(2) = *dma_play_data.p++;
294 DADO_R(2) = *dma_play_data.p++;
295 DADO_L(3) = *dma_play_data.p++;
296 DADO_R(3) = *dma_play_data.p++;
297
298 dma_play_data.size -= 16;
299 }
300 else
301 {
302 /* No more data, so disable the FIFO/interrupt */
303 pcm_play_dma_stop();
304 pcm_play_dma_stopped_callback();
305 }
306
307 /* Clear FIQ status */
308 CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK;
309
310 asm volatile( "add sp, sp, #8 \n" /* Cleanup stack */
311 "ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */
312 "subs pc, lr, #4 \n"); /* Return from FIQ */
313}
314#endif