summaryrefslogtreecommitdiff
path: root/firmware/target/arm/s5l8702/pcm-s5l8702.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/s5l8702/pcm-s5l8702.c')
-rw-r--r--firmware/target/arm/s5l8702/pcm-s5l8702.c219
1 files changed, 219 insertions, 0 deletions
diff --git a/firmware/target/arm/s5l8702/pcm-s5l8702.c b/firmware/target/arm/s5l8702/pcm-s5l8702.c
new file mode 100644
index 0000000000..7966eaddbd
--- /dev/null
+++ b/firmware/target/arm/s5l8702/pcm-s5l8702.c
@@ -0,0 +1,219 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: pcm-s5l8700.c 28600 2010-11-14 19:49:20Z Buschel $
9 *
10 * Copyright © 2011 Michael Sparmann
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include <string.h>
22
23#include "config.h"
24#include "system.h"
25#include "audio.h"
26#include "s5l8702.h"
27#include "panic.h"
28#include "audiohw.h"
29#include "pcm.h"
30#include "pcm_sampr.h"
31#include "mmu-arm.h"
32
33/* S5L8702 PCM driver tunables: */
34#define LLIMAX (2047) /* Maximum number of samples per LLI */
35#define CHUNKSIZE (8700) /* Maximum number of samples to handle with one IRQ */
36 /* (bigger chunks will be segmented internally) */
37#define WATERMARK (512) /* Number of remaining samples to schedule IRQ at */
38
39static volatile int locked = 0;
40static const int zerosample = 0;
41static unsigned char dblbuf[WATERMARK * 4] IBSS_ATTR;
42struct dma_lli lli[(CHUNKSIZE - WATERMARK + LLIMAX - 1) / LLIMAX + 1]
43 __attribute__((aligned(16)));
44static const unsigned char* dataptr;
45static size_t remaining;
46
47/* Mask the DMA interrupt */
48void pcm_play_lock(void)
49{
50 if (locked++ == 0) {
51 //TODO: Urgh, I don't like that at all...
52 VIC0INTENCLEAR = 1 << IRQ_DMAC0;
53 }
54}
55
56/* Unmask the DMA interrupt if enabled */
57void pcm_play_unlock(void)
58{
59 if (--locked == 0) {
60 VIC0INTENABLE = 1 << IRQ_DMAC0;
61 }
62}
63
64void INT_DMAC0C0(void) ICODE_ATTR;
65void INT_DMAC0C0(void)
66{
67 DMAC0INTTCCLR = 1;
68 if (!remaining) pcm_play_get_more_callback((void**)&dataptr, &remaining);
69 if (!remaining)
70 {
71 lli->nextlli = NULL;
72 lli->control = 0x75249000;
73 clean_dcache();
74 return;
75 }
76 uint32_t lastsize = MIN(WATERMARK * 4, remaining);
77 remaining -= lastsize;
78 struct dma_lli* lastlli;
79 if (remaining) lastlli = &lli[ARRAYLEN(lli) - 1];
80 else lastlli = lli;
81 uint32_t chunksize = MIN(CHUNKSIZE * 4 - lastsize, remaining);
82 if (remaining > chunksize && chunksize > remaining - WATERMARK * 4)
83 chunksize = remaining - WATERMARK * 4;
84 remaining -= chunksize;
85 bool last = !chunksize;
86 int i = 0;
87 while (chunksize)
88 {
89 uint32_t thislli = MIN(LLIMAX * 4, chunksize);
90 chunksize -= thislli;
91 lli[i].srcaddr = (void*)dataptr;
92 lli[i].dstaddr = (void*)((int)&I2STXDB0);
93 lli[i].nextlli = chunksize ? &lli[i + 1] : lastlli;
94 lli[i].control = (chunksize ? 0x75249000 : 0xf5249000) | (thislli / 2);
95 dataptr += thislli;
96 i++;
97 }
98 if (!remaining) memcpy(dblbuf, dataptr, lastsize);
99 lastlli->srcaddr = remaining ? dataptr : dblbuf;
100 lastlli->dstaddr = (void*)((int)&I2STXDB0);
101 lastlli->nextlli = last ? NULL : lli;
102 lastlli->control = (last ? 0xf5249000 : 0x75249000) | (lastsize / 2);
103 dataptr += lastsize;
104 clean_dcache();
105 if (!(DMAC0C0CONFIG & 1) && (lli[0].control & 0xfff))
106 {
107 DMAC0C0LLI = lli[0];
108 DMAC0C0CONFIG = 0x8a81;
109 }
110 else DMAC0C0NEXTLLI = lli;
111}
112
113void pcm_play_dma_start(const void* addr, size_t size)
114{
115 dataptr = (const unsigned char*)addr;
116 remaining = size;
117 I2STXCOM = 0xe;
118 DMAC0CONFIG |= 4;
119 INT_DMAC0C0();
120}
121
122void pcm_play_dma_stop(void)
123{
124 DMAC0C0CONFIG = 0x8a80;
125 I2STXCOM = 0xa;
126}
127
128/* pause playback by disabling LRCK */
129void pcm_play_dma_pause(bool pause)
130{
131 if (pause) I2STXCOM |= 1;
132 else I2STXCOM &= ~1;
133}
134
135void pcm_play_dma_init(void)
136{
137 PWRCON(0) &= ~(1 << 4);
138 PWRCON(1) &= ~(1 << 7);
139 I2S40 = 0x110;
140 I2STXCON = 0xb100059;
141 I2SCLKCON = 1;
142 VIC0INTENABLE = 1 << IRQ_DMAC0;
143
144 audiohw_preinit();
145}
146
147void pcm_postinit(void)
148{
149 audiohw_postinit();
150}
151
152void pcm_dma_apply_settings(void)
153{
154}
155
156size_t pcm_get_bytes_waiting(void)
157{
158 int bytes = remaining + (DMAC0C0LLI.control & 0xfff) * 2;
159 const struct dma_lli* lli = DMAC0C0LLI.nextlli;
160 while (lli)
161 {
162 bytes += (lli->control & 0xfff) * 2;
163 lli = lli->nextlli;
164 }
165 return bytes;
166}
167
168const void* pcm_play_dma_get_peak_buffer(int *count)
169{
170 *count = (DMAC0C0LLI.control & 0xfff) * 2;
171 return (void*)(((uint32_t)DMAC0C0LLI.srcaddr) & ~3);
172}
173
174#ifdef HAVE_PCM_DMA_ADDRESS
175void * pcm_dma_addr(void *addr)
176{
177 return addr;
178}
179#endif
180
181
182/****************************************************************************
183 ** Recording DMA transfer
184 **/
185#ifdef HAVE_RECORDING
186void pcm_rec_lock(void)
187{
188}
189
190void pcm_rec_unlock(void)
191{
192}
193
194void pcm_rec_dma_stop(void)
195{
196}
197
198void pcm_rec_dma_start(void *addr, size_t size)
199{
200 (void)addr;
201 (void)size;
202}
203
204void pcm_rec_dma_close(void)
205{
206}
207
208
209void pcm_rec_dma_init(void)
210{
211}
212
213
214const void * pcm_rec_dma_get_peak_buffer(void)
215{
216 return NULL;
217}
218
219#endif /* HAVE_RECORDING */