diff options
author | Dave Chapman <dave@dchapman.com> | 2008-09-06 17:50:59 +0000 |
---|---|---|
committer | Dave Chapman <dave@dchapman.com> | 2008-09-06 17:50:59 +0000 |
commit | d462a64a918117991e11dade2d7fa3a28196e07a (patch) | |
tree | 0a6f5ed8777b18bb1641fbb3608f10374901706c /firmware/target/arm/pcm-telechips.c | |
parent | b87715f670f04c9adbe358c32a385c6771d99a81 (diff) | |
download | rockbox-d462a64a918117991e11dade2d7fa3a28196e07a.tar.gz rockbox-d462a64a918117991e11dade2d7fa3a28196e07a.zip |
Initial commit of iaudio 7 port by Vitja Makarov (FS#9245). Port is at quite an advanced stage, but is troubled by the lack of a reliable NAND driver (similar to the Cowon D2 port) and is not yet suitable for non-developers.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@18435 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/pcm-telechips.c')
-rw-r--r-- | firmware/target/arm/pcm-telechips.c | 434 |
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 | |||
33 | struct 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 | **/ | ||
49 | struct 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 | |||
61 | static unsigned long pcm_freq SHAREDDATA_ATTR = HW_SAMPR_DEFAULT; /* 44.1 is default */ | ||
62 | |||
63 | void 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 | |||
71 | const 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 | |||
79 | void 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 | |||
120 | void pcm_apply_settings(void) | ||
121 | { | ||
122 | pcm_curr_sampr = pcm_freq; | ||
123 | } | ||
124 | |||
125 | void pcm_set_frequency(unsigned int frequency) | ||
126 | { | ||
127 | (void) frequency; | ||
128 | pcm_freq = HW_SAMPR_DEFAULT; | ||
129 | } | ||
130 | |||
131 | static 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 | |||
154 | static void play_stop_pcm(void) | ||
155 | { | ||
156 | DAMR &= ~(1<<14); /* disable tx */ | ||
157 | dma_play_data.state = 0; | ||
158 | } | ||
159 | |||
160 | void 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 | |||
175 | void 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 | |||
184 | void 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 | |||
196 | void 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 | |||
208 | void pcm_play_dma_pause(bool pause) | ||
209 | { | ||
210 | (void) pause; | ||
211 | } | ||
212 | |||
213 | size_t pcm_get_bytes_waiting(void) | ||
214 | { | ||
215 | return dma_play_data.size & ~3; | ||
216 | } | ||
217 | |||
218 | #ifdef HAVE_RECORDING | ||
219 | /* TODO: implement */ | ||
220 | void pcm_rec_dma_init(void) | ||
221 | { | ||
222 | } | ||
223 | |||
224 | void pcm_rec_dma_close(void) | ||
225 | { | ||
226 | } | ||
227 | |||
228 | void pcm_rec_dma_start(void *addr, size_t size) | ||
229 | { | ||
230 | (void) addr; | ||
231 | (void) size; | ||
232 | } | ||
233 | |||
234 | void pcm_rec_dma_stop(void) | ||
235 | { | ||
236 | } | ||
237 | |||
238 | void pcm_rec_lock(void) | ||
239 | { | ||
240 | } | ||
241 | |||
242 | void pcm_rec_unlock(void) | ||
243 | { | ||
244 | } | ||
245 | |||
246 | const void * pcm_rec_dma_get_peak_buffer(int *count) | ||
247 | { | ||
248 | *count = 0; | ||
249 | return NULL; | ||
250 | } | ||
251 | |||
252 | void 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 */ | ||
259 | void fiq_handler(void) ICODE_ATTR __attribute__((naked)); | ||
260 | void 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 */ | ||
322 | void fiq_handler(void) ICODE_ATTR __attribute__((naked)); | ||
323 | void 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 */ | ||
371 | void 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 | |||
381 | static int frame = 0; | ||
382 | static 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 | |||
405 | void 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 | ||