summaryrefslogtreecommitdiff
path: root/firmware/target/arm/pcm-telechips.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/pcm-telechips.c')
-rw-r--r--firmware/target/arm/pcm-telechips.c434
1 files changed, 434 insertions, 0 deletions
diff --git a/firmware/target/arm/pcm-telechips.c b/firmware/target/arm/pcm-telechips.c
new file mode 100644
index 0000000000..a0ad00eb22
--- /dev/null
+++ b/firmware/target/arm/pcm-telechips.c
@@ -0,0 +1,434 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 by Michael Sevakis
11 * Copyright (C) 2008 by Rob Purchase
12 *
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.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
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 "pcm.h"
29
30/* Just for tests enable it to play simple tone */
31//#define PCM_TELECHIPS_TEST
32
33struct dma_data
34{
35/* NOTE: The order of size and p is important if you use assembler
36 optimised fiq handler, so don't change it. */
37 uint16_t *p;
38 size_t size;
39#if NUM_CORES > 1
40 unsigned core;
41#endif
42 int locked;
43 int state;
44};
45
46/****************************************************************************
47 ** Playback DMA transfer
48 **/
49struct dma_data dma_play_data SHAREDBSS_ATTR =
50{
51 /* Initialize to a locked, stopped state */
52 .p = NULL,
53 .size = 0,
54#if NUM_CORES > 1
55 .core = 0x00,
56#endif
57 .locked = 0,
58 .state = 0
59};
60
61static unsigned long pcm_freq SHAREDDATA_ATTR = HW_SAMPR_DEFAULT; /* 44.1 is default */
62
63void pcm_postinit(void)
64{
65#if defined(IAUDIO_7)
66 audiohw_postinit(); /* implemented not for all codecs */
67#endif
68 pcm_apply_settings();
69}
70
71const void * pcm_play_dma_get_peak_buffer(int *count)
72{
73 unsigned long addr = (unsigned long)dma_play_data.p;
74 size_t cnt = dma_play_data.size;
75 *count = cnt >> 2;
76 return (void *)((addr + 2) & ~3);
77}
78
79void pcm_play_dma_init(void)
80{
81 DAVC = 0x0; /* Digital Volume = max */
82#ifdef COWON_D2
83 /* Set DAI clock divided from PLL0 (192MHz).
84 The best approximation of 256*44.1kHz is 11.291MHz. */
85 BCLKCTR &= ~DEV_DAI;
86 PCLK_DAI = (1<<28) | 61682; /* DCO mode */
87 BCLKCTR |= DEV_DAI;
88
89 /* Enable DAI block in Master mode, 256fs->32fs, 16bit LSB */
90 DAMR = 0x3c8e80;
91#elif defined(IAUDIO_7)
92 BCLKCTR &= ~DEV_DAI;
93 PCLK_DAI = (0x800b << 16) | (PCLK_DAI & 0xffff);
94 BCLKCTR |= DEV_DAI;
95 /* Master mode, 256->64fs, 16bit LSB*/
96 DAMR = 0x3cce20;
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);
102
103 pcm_set_frequency(SAMPR_44);
104
105 /* Initialize default register values. */
106 audiohw_init();
107
108 /* Power on */
109 audiohw_enable_output(true);
110
111 /* Unmute the master channel (DAC should be at zero point now). */
112 audiohw_mute(false);
113
114 dma_play_data.size = 0;
115#if NUM_CORES > 1
116 dma_play_data.core = 0; /* no core in control */
117#endif
118}
119
120void pcm_apply_settings(void)
121{
122 pcm_curr_sampr = pcm_freq;
123}
124
125void pcm_set_frequency(unsigned int frequency)
126{
127 (void) frequency;
128 pcm_freq = HW_SAMPR_DEFAULT;
129}
130
131static void play_start_pcm(void)
132{
133 pcm_apply_settings();
134
135 DAMR &= ~(1<<14); /* disable tx */
136 dma_play_data.state = 1;
137
138 if (dma_play_data.size >= 16)
139 {
140 DADO_L(0) = *dma_play_data.p++;
141 DADO_R(0) = *dma_play_data.p++;
142 DADO_L(1) = *dma_play_data.p++;
143 DADO_R(1) = *dma_play_data.p++;
144 DADO_L(2) = *dma_play_data.p++;
145 DADO_R(2) = *dma_play_data.p++;
146 DADO_L(3) = *dma_play_data.p++;
147 DADO_R(3) = *dma_play_data.p++;
148 dma_play_data.size -= 16;
149 }
150
151 DAMR |= (1<<14); /* enable tx */
152}
153
154static void play_stop_pcm(void)
155{
156 DAMR &= ~(1<<14); /* disable tx */
157 dma_play_data.state = 0;
158}
159
160void pcm_play_dma_start(const void *addr, size_t size)
161{
162 dma_play_data.p = (void *)(((uintptr_t)addr + 2) & ~3);
163 dma_play_data.size = (size & ~3);
164
165#if NUM_CORES > 1
166 /* This will become more important later - and different ! */
167 dma_play_data.core = processor_id(); /* save initiating core */
168#endif
169
170 IEN |= DAI_TX_IRQ_MASK;
171
172 play_start_pcm();
173}
174
175void pcm_play_dma_stop(void)
176{
177 play_stop_pcm();
178 dma_play_data.size = 0;
179#if NUM_CORES > 1
180 dma_play_data.core = 0; /* no core in control */
181#endif
182}
183
184void pcm_play_lock(void)
185{
186 int status = disable_fiq_save();
187
188 if (++dma_play_data.locked == 1)
189 {
190 IEN &= ~DAI_TX_IRQ_MASK;
191 }
192
193 restore_fiq(status);
194}
195
196void pcm_play_unlock(void)
197{
198 int status = disable_fiq_save();
199
200 if (--dma_play_data.locked == 0 && dma_play_data.state != 0)
201 {
202 IEN |= DAI_TX_IRQ_MASK;
203 }
204
205 restore_fiq(status);
206}
207
208void pcm_play_dma_pause(bool pause)
209{
210 (void) pause;
211}
212
213size_t pcm_get_bytes_waiting(void)
214{
215 return dma_play_data.size & ~3;
216}
217
218#ifdef HAVE_RECORDING
219/* TODO: implement */
220void pcm_rec_dma_init(void)
221{
222}
223
224void pcm_rec_dma_close(void)
225{
226}
227
228void pcm_rec_dma_start(void *addr, size_t size)
229{
230 (void) addr;
231 (void) size;
232}
233
234void pcm_rec_dma_stop(void)
235{
236}
237
238void pcm_rec_lock(void)
239{
240}
241
242void pcm_rec_unlock(void)
243{
244}
245
246const void * pcm_rec_dma_get_peak_buffer(int *count)
247{
248 *count = 0;
249 return NULL;
250}
251
252void pcm_record_more(void *start, size_t size)
253{
254}
255#endif
256
257#if defined(COWON_D2)
258/* TODO: hardcoded hex values differs for tcc7xx and tcc8xx */
259void fiq_handler(void) ICODE_ATTR __attribute__((naked));
260void fiq_handler(void)
261{
262 /* r10 contains DADO_L0 base address (set in crt0.S to minimise code in the
263 * FIQ handler. r11 contains address of p (also set in crt0.S). Most other
264 * addresses we need are generated by using offsets with these two.
265 * r8 and r9 contains local copies of p and size respectively.
266 * r0-r3 and r12 is a working register.
267 */
268 asm volatile (
269 "mov r8, #0xc000 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */
270 "ldr r9, =0xf3001004 \n" /* CREQ */
271 "str r8, [r9] \n" /* clear DAI IRQs */
272
273 "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */
274 "cmp r9, #0x10 \n" /* is size <16? */
275 "blt .more_data \n" /* if so, ask pcmbuf for more data */
276
277 ".fill_fifo: \n"
278 "ldr r12, [r8], #4 \n" /* load two samples */
279 "str r12, [r10, #0x0] \n" /* write top sample to DADO_L0 */
280 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
281 "str r12, [r10, #0x4] \n" /* write low sample to DADO_R0*/
282 "ldr r12, [r8], #4 \n" /* load two samples */
283 "str r12, [r10, #0x8] \n" /* write top sample to DADO_L1 */
284 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
285 "str r12, [r10, #0xc] \n" /* write low sample to DADO_R1*/
286 "ldr r12, [r8], #4 \n" /* load two samples */
287 "str r12, [r10, #0x10] \n" /* write top sample to DADO_L2 */
288 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
289 "str r12, [r10, #0x14] \n" /* write low sample to DADO_R2*/
290 "ldr r12, [r8], #4 \n" /* load two samples */
291 "str r12, [r10, #0x18] \n" /* write top sample to DADO_L3 */
292 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
293 "str r12, [r10, #0x1c] \n" /* write low sample to DADO_R3*/
294 "sub r9, r9, #0x10 \n" /* 4 words written */
295 "stmia r11, { r8-r9 } \n" /* save p and size */
296
297 ".exit: \n"
298 "subs pc, lr, #4 \n" /* FIQ specific return sequence */
299
300 ".more_data: \n"
301 "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */
302 "ldr r2, =pcm_callback_for_more \n"
303 "ldr r2, [r2] \n" /* get callback address */
304 "cmp r2, #0 \n" /* check for null pointer */
305 "movne r0, r11 \n" /* r0 = &p */
306 "addne r1, r11, #4 \n" /* r1 = &size */
307 "blxne r2 \n" /* call pcm_callback_for_more */
308 "ldmia r11, { r8-r9 } \n" /* reload p and size */
309 "cmp r9, #0x10 \n" /* did we actually get more data? */
310 "ldmgefd sp!, { r0-r3, lr } \n"
311 "bge .fill_fifo \n" /* yes: fill the fifo */
312 "ldr r12, =pcm_play_dma_stop \n"
313 "blx r12 \n" /* no: stop playback */
314 "ldr r12, =pcm_play_dma_stopped_callback \n"
315 "blx r12 \n"
316 "ldmfd sp!, { r0-r3, lr } \n"
317 "b .exit \n"
318 ".ltorg \n"
319 );
320}
321#else /* C version for reference */
322void fiq_handler(void) ICODE_ATTR __attribute__((naked));
323void fiq_handler(void)
324{
325 asm volatile( "stmfd sp!, {r0-r7, ip, lr} \n" /* Store context */
326 "sub sp, sp, #8 \n"); /* Reserve stack */
327
328 register pcm_more_callback_type get_more;
329
330 if (dma_play_data.size < 16)
331 {
332 /* p is empty, get some more data */
333 get_more = pcm_callback_for_more;
334 if (get_more)
335 {
336 get_more((unsigned char**)&dma_play_data.p,
337 &dma_play_data.size);
338 }
339 }
340
341 if (dma_play_data.size >= 16)
342 {
343 DADO_L(0) = *dma_play_data.p++;
344 DADO_R(0) = *dma_play_data.p++;
345 DADO_L(1) = *dma_play_data.p++;
346 DADO_R(1) = *dma_play_data.p++;
347 DADO_L(2) = *dma_play_data.p++;
348 DADO_R(2) = *dma_play_data.p++;
349 DADO_L(3) = *dma_play_data.p++;
350 DADO_R(3) = *dma_play_data.p++;
351
352 dma_play_data.size -= 16;
353 }
354 else
355 {
356 /* No more data, so disable the FIFO/interrupt */
357 pcm_play_dma_stop();
358 pcm_play_dma_stopped_callback();
359 }
360
361 /* Clear FIQ status */
362 CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK;
363
364 asm volatile( "add sp, sp, #8 \n" /* Cleanup stack */
365 "ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */
366 "subs pc, lr, #4 \n"); /* Return from FIQ */
367}
368#endif
369
370/* TODO: required by wm8531 codec, why not to implement */
371void i2s_reset(void)
372{
373/* DAMR = 0; */
374}
375
376#ifdef PCM_TELECHIPS_TEST
377#include "lcd.h"
378#include "sprintf.h"
379#include "backlight-target.h"
380
381static int frame = 0;
382static void test_callback_for_more(unsigned char **start, size_t *size)
383{
384 static unsigned short data[8];
385 static int cntr = 0;
386 int i;
387
388 for (i = 0; i < 8; i ++) {
389 unsigned short val;
390
391 if (0x100 == (cntr & 0x100))
392 val = 0x0fff;
393 else
394 val = 0x0000;
395 data[i] = val;
396 cntr++;
397 }
398
399 *start = data;
400 *size = sizeof(data);
401
402 frame++;
403}
404
405void pcm_telechips_test(void)
406{
407 static char buf[100];
408 unsigned char *data;
409 size_t size;
410
411 _backlight_on();
412
413 audiohw_preinit();
414 pcm_play_dma_init();
415 pcm_postinit();
416
417 audiohw_mute(false);
418 audiohw_set_master_vol(0x7f, 0x7f);
419
420 pcm_callback_for_more = test_callback_for_more;
421 test_callback_for_more(&data, &size);
422 pcm_play_dma_start(data, size);
423
424 while (1) {
425 int line = 0;
426 lcd_clear_display();
427 lcd_puts(0, line++, __func__);
428 snprintf(buf, sizeof(buf), "frame: %d", frame);
429 lcd_puts(0, line++, buf);
430 lcd_update();
431 sleep(1);
432 }
433}
434#endif