i.MX31: Add a debug menu to play around with DVFS/DPTC settings for fun, testing...
[kugel-rb.git] / firmware / target / arm / pcm-telechips.c
blob851ebee7de90ac9c11da65a1ef297c25d27dae15
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006 by Michael Sevakis
11 * Copyright (C) 2008 by Rob Purchase
13 * 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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
22 #include <stdlib.h>
23 #include "system.h"
24 #include "kernel.h"
25 #include "logf.h"
26 #include "audio.h"
27 #include "sound.h"
28 #include "i2s.h"
29 #include "pcm.h"
31 struct dma_data
33 /* NOTE: The order of size and p is important if you use assembler
34 optimised fiq handler, so don't change it. */
35 uint16_t *p;
36 size_t size;
37 #if NUM_CORES > 1
38 unsigned core;
39 #endif
40 int locked;
41 int state;
44 /****************************************************************************
45 ** Playback DMA transfer
46 **/
47 struct dma_data dma_play_data SHAREDBSS_ATTR =
49 /* Initialize to a locked, stopped state */
50 .p = NULL,
51 .size = 0,
52 #if NUM_CORES > 1
53 .core = 0x00,
54 #endif
55 .locked = 0,
56 .state = 0
59 void pcm_postinit(void)
61 audiohw_postinit();
64 const void * pcm_play_dma_get_peak_buffer(int *count)
66 unsigned long addr = (unsigned long)dma_play_data.p;
67 size_t cnt = dma_play_data.size;
68 *count = cnt >> 2;
69 return (void *)((addr + 2) & ~3);
72 void pcm_play_dma_init(void)
74 DAVC = 0x0; /* Digital Volume = max */
75 #ifdef COWON_D2
76 /* 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;
82 /* Enable DAI block in Master mode, 256fs->32fs, 16bit LSB */
83 DAMR = 0x3c8e80;
84 #elif defined(IAUDIO_7)
85 BCLKCTR &= ~DEV_DAI;
86 PCLK_DAI = (0x800a << 16) | (PCLK_DAI & 0xffff);
87 BCLKCTR |= DEV_DAI;
89 /* Master mode, 256->64fs, 16bit LSB*/
90 DAMR = 0x3cce20;
91 #elif defined(LOGIK_DAX)
92 /* TODO */
93 #elif defined(SANSA_M200)
94 /* TODO */
95 #elif defined(SANSA_C100)
96 /* TODO */
97 #else
98 #error "Target isn't supported"
99 #endif
100 /* Set DAI interrupts as FIQs */
101 IRQSEL = ~(DAI_RX_IRQ_MASK | DAI_TX_IRQ_MASK);
103 /* Initialize default register values. */
104 audiohw_init();
106 dma_play_data.size = 0;
107 #if NUM_CORES > 1
108 dma_play_data.core = 0; /* no core in control */
109 #endif
112 void pcm_dma_apply_settings(void)
116 static void play_start_pcm(void)
118 DAMR &= ~(1<<14); /* disable tx */
119 dma_play_data.state = 1;
121 if (dma_play_data.size >= 16)
123 DADO_L(0) = *dma_play_data.p++;
124 DADO_R(0) = *dma_play_data.p++;
125 DADO_L(1) = *dma_play_data.p++;
126 DADO_R(1) = *dma_play_data.p++;
127 DADO_L(2) = *dma_play_data.p++;
128 DADO_R(2) = *dma_play_data.p++;
129 DADO_L(3) = *dma_play_data.p++;
130 DADO_R(3) = *dma_play_data.p++;
131 dma_play_data.size -= 16;
134 DAMR |= (1<<14); /* enable tx */
137 static void play_stop_pcm(void)
139 DAMR &= ~(1<<14); /* disable tx */
140 dma_play_data.state = 0;
143 void pcm_play_dma_start(const void *addr, size_t size)
145 dma_play_data.p = (uint16_t*)addr;
146 dma_play_data.size = size;
148 #if NUM_CORES > 1
149 /* This will become more important later - and different ! */
150 dma_play_data.core = processor_id(); /* save initiating core */
151 #endif
153 IEN |= DAI_TX_IRQ_MASK;
155 play_start_pcm();
158 void pcm_play_dma_stop(void)
160 play_stop_pcm();
161 dma_play_data.size = 0;
162 #if NUM_CORES > 1
163 dma_play_data.core = 0; /* no core in control */
164 #endif
167 void pcm_play_lock(void)
169 int status = disable_fiq_save();
171 if (++dma_play_data.locked == 1)
173 IEN &= ~DAI_TX_IRQ_MASK;
176 restore_fiq(status);
179 void pcm_play_unlock(void)
181 int status = disable_fiq_save();
183 if (--dma_play_data.locked == 0 && dma_play_data.state != 0)
185 IEN |= DAI_TX_IRQ_MASK;
188 restore_fiq(status);
191 void pcm_play_dma_pause(bool pause)
193 if (pause) {
194 play_stop_pcm();
195 } else {
196 play_start_pcm();
200 size_t pcm_get_bytes_waiting(void)
202 return dma_play_data.size & ~3;
205 #ifdef HAVE_RECORDING
206 /* TODO: implement */
207 void pcm_rec_dma_init(void)
211 void pcm_rec_dma_close(void)
215 void pcm_rec_dma_start(void *addr, size_t size)
217 (void) addr;
218 (void) size;
221 void pcm_rec_dma_stop(void)
225 void pcm_rec_lock(void)
229 void pcm_rec_unlock(void)
233 const void * pcm_rec_dma_get_peak_buffer(void)
235 return NULL;
237 #endif
239 #if defined(CPU_TCC77X) || defined(CPU_TCC780X)
240 void fiq_handler(void) ICODE_ATTR __attribute__((naked));
241 void fiq_handler(void)
243 /* r10 contains DADO_L0 base address (set in crt0.S to minimise code in the
244 * FIQ handler. r11 contains address of p (also set in crt0.S). Most other
245 * addresses we need are generated by using offsets with these two.
246 * r8 and r9 contains local copies of p and size respectively.
247 * r0-r3 and r12 is a working register.
249 asm volatile (
250 #if defined(CPU_TCC780X)
251 "mov r8, #0xc000 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */
252 "ldr r9, =0xf3001004 \n" /* CREQ */
253 #elif defined(CPU_TCC77X)
254 "mov r8, #0x0030 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */
255 "ldr r9, =0x80000104 \n" /* CREQ */
256 #endif
257 "str r8, [r9] \n" /* clear DAI IRQs */
258 "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */
259 "cmp r9, #0x10 \n" /* is size <16? */
260 "blt .more_data \n" /* if so, ask pcmbuf for more data */
262 ".fill_fifo: \n"
263 "ldr r12, [r8], #4 \n" /* load two samples */
264 "str r12, [r10, #0x0] \n" /* write top sample to DADO_L0 */
265 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
266 "str r12, [r10, #0x4] \n" /* write low sample to DADO_R0*/
267 "ldr r12, [r8], #4 \n" /* load two samples */
268 "str r12, [r10, #0x8] \n" /* write top sample to DADO_L1 */
269 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
270 "str r12, [r10, #0xc] \n" /* write low sample to DADO_R1*/
271 "ldr r12, [r8], #4 \n" /* load two samples */
272 "str r12, [r10, #0x10] \n" /* write top sample to DADO_L2 */
273 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
274 "str r12, [r10, #0x14] \n" /* write low sample to DADO_R2*/
275 "ldr r12, [r8], #4 \n" /* load two samples */
276 "str r12, [r10, #0x18] \n" /* write top sample to DADO_L3 */
277 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
278 "str r12, [r10, #0x1c] \n" /* write low sample to DADO_R3*/
279 "sub r9, r9, #0x10 \n" /* 4 words written */
280 "stmia r11, { r8-r9 } \n" /* save p and size */
282 ".exit: \n"
283 "subs pc, lr, #4 \n" /* FIQ specific return sequence */
285 ".more_data: \n"
286 "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */
287 "ldr r2, =pcm_play_get_more_callback \n"
288 "mov r0, r11 \n" /* r0 = &p */
289 "add r1, r11, #4 \n" /* r1 = &size */
290 "blx r2 \n" /* call pcm_play_get_more_callback */
291 "ldmia r11, { r8-r9 } \n" /* load new p and size */
292 "cmp r9, #0x10 \n" /* did we actually get enough data? */
293 "ldmfd sp!, { r0-r3, lr } \n"
294 "bpl .fill_fifo \n" /* not stop and enough? refill */
295 "b .exit \n"
296 ".ltorg \n"
299 #else /* C version for reference */
300 void fiq_handler(void) ICODE_ATTR __attribute__((naked));
301 void fiq_handler(void)
303 asm volatile( "stmfd sp!, {r0-r7, ip, lr} \n" /* Store context */
304 "sub sp, sp, #8 \n"); /* Reserve stack */
306 if (dma_play_data.size < 16)
308 /* p is empty, get some more data */
309 pcm_play_get_more_callback((void**)&dma_play_data.p,
310 &dma_play_data.size);
313 if (dma_play_data.size >= 16)
315 DADO_L(0) = *dma_play_data.p++;
316 DADO_R(0) = *dma_play_data.p++;
317 DADO_L(1) = *dma_play_data.p++;
318 DADO_R(1) = *dma_play_data.p++;
319 DADO_L(2) = *dma_play_data.p++;
320 DADO_R(2) = *dma_play_data.p++;
321 DADO_L(3) = *dma_play_data.p++;
322 DADO_R(3) = *dma_play_data.p++;
324 dma_play_data.size -= 16;
327 /* Clear FIQ status */
328 CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK;
330 asm volatile( "add sp, sp, #8 \n" /* Cleanup stack */
331 "ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */
332 "subs pc, lr, #4 \n"); /* Return from FIQ */
334 #endif
336 /* TODO: required by wm8731 codec */
337 void i2s_reset(void)
339 /* DAMR = 0; */