blob: aff43171f6117657389cf39a621debf70a99290f [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"
Bertrik Sikken9b6d8952010-08-01 09:33:29 +000028#include "i2s.h"
Rob Purchased6159ea2008-06-22 18:48:22 +000029#include "pcm.h"
Michael Sevakisa2b67032011-06-29 06:37:04 +000030#include "pcm-internal.h"
Rob Purchased6159ea2008-06-22 18:48:22 +000031
32struct dma_data
33{
34/* NOTE: The order of size and p is important if you use assembler
35 optimised fiq handler, so don't change it. */
36 uint16_t *p;
37 size_t size;
38#if NUM_CORES > 1
39 unsigned core;
40#endif
41 int locked;
42 int state;
43};
44
45/****************************************************************************
46 ** Playback DMA transfer
47 **/
48struct dma_data dma_play_data SHAREDBSS_ATTR =
49{
50 /* Initialize to a locked, stopped state */
51 .p = NULL,
52 .size = 0,
53#if NUM_CORES > 1
54 .core = 0x00,
55#endif
56 .locked = 0,
57 .state = 0
58};
59
Rob Purchase47ea0302008-01-14 22:04:48 +000060void pcm_postinit(void)
61{
Michael Sevakis888c3472008-11-26 14:52:31 +000062 audiohw_postinit();
Rob Purchase47ea0302008-01-14 22:04:48 +000063}
64
65const void * pcm_play_dma_get_peak_buffer(int *count)
66{
Rob Purchased6159ea2008-06-22 18:48:22 +000067 unsigned long addr = (unsigned long)dma_play_data.p;
68 size_t cnt = dma_play_data.size;
69 *count = cnt >> 2;
70 return (void *)((addr + 2) & ~3);
Rob Purchase47ea0302008-01-14 22:04:48 +000071}
72
73void pcm_play_dma_init(void)
74{
Dave Chapmand462a642008-09-06 17:50:59 +000075 DAVC = 0x0; /* Digital Volume = max */
76#ifdef COWON_D2
Rob Purchased6159ea2008-06-22 18:48:22 +000077 /* Set DAI clock divided from PLL0 (192MHz).
78 The best approximation of 256*44.1kHz is 11.291MHz. */
79 BCLKCTR &= ~DEV_DAI;
80 PCLK_DAI = (1<<28) | 61682; /* DCO mode */
81 BCLKCTR |= DEV_DAI;
Vitja Makarov14ac3742009-09-20 19:53:15 +000082
Rob Purchased6159ea2008-06-22 18:48:22 +000083 /* Enable DAI block in Master mode, 256fs->32fs, 16bit LSB */
84 DAMR = 0x3c8e80;
Dave Chapmand462a642008-09-06 17:50:59 +000085#elif defined(IAUDIO_7)
86 BCLKCTR &= ~DEV_DAI;
Vitja Makarov14ac3742009-09-20 19:53:15 +000087 PCLK_DAI = (0x800a << 16) | (PCLK_DAI & 0xffff);
Dave Chapmand462a642008-09-06 17:50:59 +000088 BCLKCTR |= DEV_DAI;
Vitja Makarov14ac3742009-09-20 19:53:15 +000089
Dave Chapmand462a642008-09-06 17:50:59 +000090 /* Master mode, 256->64fs, 16bit LSB*/
91 DAMR = 0x3cce20;
Dave Chapman85807cd2008-09-22 19:15:18 +000092#elif defined(LOGIK_DAX)
93 /* TODO */
94#elif defined(SANSA_M200)
95 /* TODO */
Marc Guay86ce4072009-06-01 12:37:25 +000096#elif defined(SANSA_C100)
Vitja Makarov14ac3742009-09-20 19:53:15 +000097 /* TODO */
Dave Chapmand462a642008-09-06 17:50:59 +000098#else
99#error "Target isn't supported"
100#endif
Rob Purchased6159ea2008-06-22 18:48:22 +0000101 /* Set DAI interrupts as FIQs */
102 IRQSEL = ~(DAI_RX_IRQ_MASK | DAI_TX_IRQ_MASK);
Vitja Makarov14ac3742009-09-20 19:53:15 +0000103
Rob Purchased6159ea2008-06-22 18:48:22 +0000104 /* Initialize default register values. */
105 audiohw_init();
Vitja Makarov14ac3742009-09-20 19:53:15 +0000106
Rob Purchased6159ea2008-06-22 18:48:22 +0000107 dma_play_data.size = 0;
108#if NUM_CORES > 1
109 dma_play_data.core = 0; /* no core in control */
110#endif
Rob Purchase47ea0302008-01-14 22:04:48 +0000111}
112
Michael Sevakise69d5672008-12-12 11:01:07 +0000113void pcm_dma_apply_settings(void)
Rob Purchase47ea0302008-01-14 22:04:48 +0000114{
Rob Purchased6159ea2008-06-22 18:48:22 +0000115}
116
117static void play_start_pcm(void)
118{
Rob Purchased6159ea2008-06-22 18:48:22 +0000119 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;
Rob Purchase47ea0302008-01-14 22:04:48 +0000142}
143
144void pcm_play_dma_start(const void *addr, size_t size)
145{
Michael Sevakis59063c62010-05-12 14:35:47 +0000146 dma_play_data.p = (uint16_t*)addr;
Michael Sevakis4d041322010-05-12 14:05:36 +0000147 dma_play_data.size = size;
Rob Purchased6159ea2008-06-22 18:48:22 +0000148
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();
Rob Purchase47ea0302008-01-14 22:04:48 +0000157}
158
159void pcm_play_dma_stop(void)
160{
Rob Purchased6159ea2008-06-22 18:48:22 +0000161 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
Rob Purchase47ea0302008-01-14 22:04:48 +0000166}
167
168void pcm_play_lock(void)
169{
Rob Purchased6159ea2008-06-22 18:48:22 +0000170 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);
Rob Purchase47ea0302008-01-14 22:04:48 +0000178}
179
180void pcm_play_unlock(void)
181{
Rob Purchased6159ea2008-06-22 18:48:22 +0000182 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);
Rob Purchase47ea0302008-01-14 22:04:48 +0000190}
191
192void pcm_play_dma_pause(bool pause)
193{
Rob Purchase1a08f462008-09-16 08:09:44 +0000194 if (pause) {
195 play_stop_pcm();
196 } else {
197 play_start_pcm();
198 }
Rob Purchase47ea0302008-01-14 22:04:48 +0000199}
200
201size_t pcm_get_bytes_waiting(void)
202{
Rob Purchased6159ea2008-06-22 18:48:22 +0000203 return dma_play_data.size & ~3;
Rob Purchase47ea0302008-01-14 22:04:48 +0000204}
Rob Purchasefd773cb2008-04-21 20:16:18 +0000205
Dave Chapmand462a642008-09-06 17:50:59 +0000206#ifdef HAVE_RECORDING
207/* TODO: implement */
208void pcm_rec_dma_init(void)
209{
210}
211
212void pcm_rec_dma_close(void)
213{
214}
215
216void pcm_rec_dma_start(void *addr, size_t size)
217{
218 (void) addr;
219 (void) size;
220}
221
222void pcm_rec_dma_stop(void)
223{
224}
225
226void pcm_rec_lock(void)
227{
228}
229
230void pcm_rec_unlock(void)
231{
232}
233
Michael Sevakis4d041322010-05-12 14:05:36 +0000234const void * pcm_rec_dma_get_peak_buffer(void)
Dave Chapmand462a642008-09-06 17:50:59 +0000235{
Dave Chapmand462a642008-09-06 17:50:59 +0000236 return NULL;
237}
Dave Chapmand462a642008-09-06 17:50:59 +0000238#endif
239
Rob Purchase1a08f462008-09-16 08:09:44 +0000240#if defined(CPU_TCC77X) || defined(CPU_TCC780X)
Rob Purchased6159ea2008-06-22 18:48:22 +0000241void fiq_handler(void) ICODE_ATTR __attribute__((naked));
Rob Purchasefd773cb2008-04-21 20:16:18 +0000242void fiq_handler(void)
243{
Rob Purchased6159ea2008-06-22 18:48:22 +0000244 /* r10 contains DADO_L0 base address (set in crt0.S to minimise code in the
245 * FIQ handler. r11 contains address of p (also set in crt0.S). Most other
246 * addresses we need are generated by using offsets with these two.
247 * r8 and r9 contains local copies of p and size respectively.
248 * r0-r3 and r12 is a working register.
249 */
Vitja Makarov14ac3742009-09-20 19:53:15 +0000250 asm volatile (
Michael Sevakisa2b67032011-06-29 06:37:04 +0000251 "stmfd sp!, { r0-r4, lr } \n" /* stack scratch regs and lr */
252 "mov r4, #0 \n" /* Was the callback called? */
Rob Purchase1a08f462008-09-16 08:09:44 +0000253#if defined(CPU_TCC780X)
Rob Purchase39aaa2f2008-06-27 12:39:03 +0000254 "mov r8, #0xc000 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */
255 "ldr r9, =0xf3001004 \n" /* CREQ */
Rob Purchase1a08f462008-09-16 08:09:44 +0000256#elif defined(CPU_TCC77X)
257 "mov r8, #0x0030 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */
258 "ldr r9, =0x80000104 \n" /* CREQ */
259#endif
Rob Purchase39aaa2f2008-06-27 12:39:03 +0000260 "str r8, [r9] \n" /* clear DAI IRQs */
Rob Purchased6159ea2008-06-22 18:48:22 +0000261 "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */
262 "cmp r9, #0x10 \n" /* is size <16? */
263 "blt .more_data \n" /* if so, ask pcmbuf for more data */
264
265 ".fill_fifo: \n"
266 "ldr r12, [r8], #4 \n" /* load two samples */
267 "str r12, [r10, #0x0] \n" /* write top sample to DADO_L0 */
268 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
269 "str r12, [r10, #0x4] \n" /* write low sample to DADO_R0*/
270 "ldr r12, [r8], #4 \n" /* load two samples */
271 "str r12, [r10, #0x8] \n" /* write top sample to DADO_L1 */
272 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
273 "str r12, [r10, #0xc] \n" /* write low sample to DADO_R1*/
274 "ldr r12, [r8], #4 \n" /* load two samples */
275 "str r12, [r10, #0x10] \n" /* write top sample to DADO_L2 */
276 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
277 "str r12, [r10, #0x14] \n" /* write low sample to DADO_R2*/
278 "ldr r12, [r8], #4 \n" /* load two samples */
279 "str r12, [r10, #0x18] \n" /* write top sample to DADO_L3 */
280 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
281 "str r12, [r10, #0x1c] \n" /* write low sample to DADO_R3*/
282 "sub r9, r9, #0x10 \n" /* 4 words written */
283 "stmia r11, { r8-r9 } \n" /* save p and size */
284
Michael Sevakisa2b67032011-06-29 06:37:04 +0000285 "cmp r4, #0 \n" /* Callback called? */
286 "beq .exit \n"
287 /* "mov r4, #0 \n" If get_more could be called multiple times! */
288 "ldr r2, =pcm_play_dma_started\n"
289 "ldr r2, [r2] \n"
290 "cmp r2, #0 \n"
291 "blxne r2 \n"
292
Rob Purchased6159ea2008-06-22 18:48:22 +0000293 ".exit: \n"
Michael Sevakisa2b67032011-06-29 06:37:04 +0000294 "ldmfd sp!, { r0-r4, lr } \n"
Rob Purchased6159ea2008-06-22 18:48:22 +0000295 "subs pc, lr, #4 \n" /* FIQ specific return sequence */
296
297 ".more_data: \n"
Michael Sevakisa2b67032011-06-29 06:37:04 +0000298 "mov r4, #1 \n" /* Remember we got more data in this FIQ */
Michael Sevakisd5699982010-05-24 16:42:32 +0000299 "ldr r2, =pcm_play_get_more_callback \n"
300 "mov r0, r11 \n" /* r0 = &p */
301 "add r1, r11, #4 \n" /* r1 = &size */
302 "blx r2 \n" /* call pcm_play_get_more_callback */
303 "ldmia r11, { r8-r9 } \n" /* load new p and size */
304 "cmp r9, #0x10 \n" /* did we actually get enough data? */
Michael Sevakisd5699982010-05-24 16:42:32 +0000305 "bpl .fill_fifo \n" /* not stop and enough? refill */
Rob Purchased6159ea2008-06-22 18:48:22 +0000306 "b .exit \n"
307 ".ltorg \n"
Rob Purchasefd773cb2008-04-21 20:16:18 +0000308 );
309}
Rob Purchased6159ea2008-06-22 18:48:22 +0000310#else /* C version for reference */
Michael Sevakisa2b67032011-06-29 06:37:04 +0000311void fiq_handler(void) ICODE_ATTR;
Rob Purchased6159ea2008-06-22 18:48:22 +0000312void fiq_handler(void)
313{
Michael Sevakisa2b67032011-06-29 06:37:04 +0000314 register bool new_buffer = false;
Vitja Makarov14ac3742009-09-20 19:53:15 +0000315
Rob Purchased6159ea2008-06-22 18:48:22 +0000316 if (dma_play_data.size < 16)
317 {
318 /* p is empty, get some more data */
Michael Sevakisa2b67032011-06-29 06:37:04 +0000319 new_buffer = true;
Michael Sevakisd5699982010-05-24 16:42:32 +0000320 pcm_play_get_more_callback((void**)&dma_play_data.p,
321 &dma_play_data.size);
Rob Purchased6159ea2008-06-22 18:48:22 +0000322 }
323
324 if (dma_play_data.size >= 16)
325 {
326 DADO_L(0) = *dma_play_data.p++;
327 DADO_R(0) = *dma_play_data.p++;
328 DADO_L(1) = *dma_play_data.p++;
329 DADO_R(1) = *dma_play_data.p++;
330 DADO_L(2) = *dma_play_data.p++;
331 DADO_R(2) = *dma_play_data.p++;
332 DADO_L(3) = *dma_play_data.p++;
333 DADO_R(3) = *dma_play_data.p++;
334
335 dma_play_data.size -= 16;
336 }
Vitja Makarov14ac3742009-09-20 19:53:15 +0000337
Rob Purchased6159ea2008-06-22 18:48:22 +0000338 /* Clear FIQ status */
339 CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK;
Vitja Makarov14ac3742009-09-20 19:53:15 +0000340
Michael Sevakisa2b67032011-06-29 06:37:04 +0000341 if (new_buffer)
342 pcm_play_dma_started_callback();
Rob Purchased6159ea2008-06-22 18:48:22 +0000343}
344#endif
Dave Chapmand462a642008-09-06 17:50:59 +0000345
Vitja Makarov14ac3742009-09-20 19:53:15 +0000346/* TODO: required by wm8731 codec */
Dave Chapmand462a642008-09-06 17:50:59 +0000347void i2s_reset(void)
348{
Vitja Makarov14ac3742009-09-20 19:53:15 +0000349 /* DAMR = 0; */
Dave Chapmand462a642008-09-06 17:50:59 +0000350}