summaryrefslogtreecommitdiff
path: root/firmware/target/arm/gigabeat/meg-fx/pcm-meg-fx.c
diff options
context:
space:
mode:
authorMarcoen Hirschberg <marcoen@gmail.com>2006-12-29 02:49:12 +0000
committerMarcoen Hirschberg <marcoen@gmail.com>2006-12-29 02:49:12 +0000
commit295367686ec9855c4d90f68a6003e819fef8e7ab (patch)
treeb4077ffb8d2283bf199ad12a90322be77040c2fd /firmware/target/arm/gigabeat/meg-fx/pcm-meg-fx.c
parent995a804defda23233ccbdd859023f4ba3ecba0bf (diff)
downloadrockbox-295367686ec9855c4d90f68a6003e819fef8e7ab.tar.gz
rockbox-295367686ec9855c4d90f68a6003e819fef8e7ab.zip
merge a big part of the unofficial gigabeat cvs back. Includes working bootloader and rockbox with audio.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11850 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/gigabeat/meg-fx/pcm-meg-fx.c')
-rw-r--r--firmware/target/arm/gigabeat/meg-fx/pcm-meg-fx.c347
1 files changed, 347 insertions, 0 deletions
diff --git a/firmware/target/arm/gigabeat/meg-fx/pcm-meg-fx.c b/firmware/target/arm/gigabeat/meg-fx/pcm-meg-fx.c
new file mode 100644
index 0000000000..a3c09c5c64
--- /dev/null
+++ b/firmware/target/arm/gigabeat/meg-fx/pcm-meg-fx.c
@@ -0,0 +1,347 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 by Michael Sevakis
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#include "system.h"
20#include "kernel.h"
21#include "logf.h"
22#include "audio.h"
23#include "wm8975.h"
24#include "file.h"
25
26static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
27
28#define FIFO_COUNT ((IISFCON >> 6) & 0x01F)
29
30/* number of bytes in FIFO */
31#define IIS_FIFO_SIZE 64
32
33/* Setup for the DMA controller */
34#define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20))
35
36unsigned short * p;
37size_t p_size;
38
39
40
41/* DMA count has hit zero - no more data */
42/* Get more data from the callback and top off the FIFO */
43//void fiq(void) __attribute__ ((interrupt ("naked")));
44void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ")));
45void fiq(void)
46{
47 /* clear any pending interrupt */
48 SRCPND = (1<<19);
49
50 /* Buffer empty. Try to get more. */
51 if (pcm_callback_for_more)
52 {
53 pcm_callback_for_more((unsigned char**)&p, &p_size);
54 }
55 else
56 {
57 /* callback func is missing? */
58 pcm_play_dma_stop();
59 return;
60 }
61
62 if (p_size)
63 {
64 /* set the new DMA values */
65 DCON2 = DMA_CONTROL_SETUP | (p_size >> 1);
66 DISRC2 = (int)p + 0x30000000;
67
68 /* Re-Activate the channel */
69 DMASKTRIG2 = 0x2;
70 }
71 else
72 {
73 /* No more DMA to do */
74 pcm_play_dma_stop();
75 }
76
77}
78
79
80
81void pcm_init(void)
82{
83 pcm_playing = false;
84 pcm_paused = false;
85 pcm_callback_for_more = NULL;
86
87 audiohw_init();
88 audiohw_enable_output(true);
89 audiohw_mute(true);
90
91 /* cannot use the WM8975 defaults since our clock is not the same */
92 /* the input master clock is 16.9344MHz - we can divide exact for that */
93 audiohw_set_sample_rate( (0<<6) | (0x11 << 1) | (0<<0));
94
95 /* init GPIO */
96 GPCCON = (GPCCON & ~(3<<14)) | (1<<14);
97 GPCDAT |= 1<<7;
98 GPECON |= 0x2aa;
99
100 /* Do no service DMA0 requests, yet */
101 /* clear any pending int and mask it */
102 INTMSK |= (1<<19); /* mask the interrupt */
103 SRCPND = (1<<19); /* clear any pending interrupts */
104 INTMOD |= (1<<19); /* connect to FIQ */
105
106}
107
108
109
110void pcm_play_dma_start(const void *addr, size_t size)
111{
112 //FIXME
113 //return;
114
115 int i;
116
117 /* sanity check: bad pointer or too small file */
118 if ((NULL == addr) || (size & ~1) <= IIS_FIFO_SIZE) return;
119
120 p = (unsigned short *)addr;
121 p_size = size;
122
123
124 /* Enable the IIS clock */
125 CLKCON |= (1<<17);
126
127 /* IIS interface setup and set to idle */
128 IISCON = (1<<5) | (1<<3);
129
130 /* slave, transmit mode, 16 bit samples - 384fs - use 16.9344Mhz */
131 IISMOD = (1<<9) | (1<<8) | (2<<6) | (1<<3) | (1<<2);
132
133 /* connect DMA to the FIFO and enable the FIFO */
134 IISFCON = (1<<15) | (1<<13);
135
136 /* prefill the FIFO with half words */
137 for (i=0; i < IIS_FIFO_SIZE; i+=2)
138 {
139 IISFIFO = *p++;
140 p_size -= 2;
141 }
142
143 /* set DMA dest */
144 DIDST2 = (int)&IISFIFO;
145
146 /* IIS is on the APB bus, INT when TC reaches 0, fixed dest addr */
147 DIDSTC2 = 0x03;
148
149 /* How many transfers to make - we transfer half-word at a time = 2 bytes */
150 /* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */
151 /* no auto-reload, half-word (16bit) */
152 DCON2 = DMA_CONTROL_SETUP | (p_size / 2);
153
154 /* set DMA source and options */
155 DISRC2 = (int)p + 0x30000000;
156 DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */
157
158 /* clear pending DMA interrupt */
159 SRCPND = 1<<19;
160
161 enable_fiq(fiq);
162
163 /* unmask the DMA interrupt */
164 INTMSK &= ~(1<<19);
165
166 /* Activate the channel */
167 DMASKTRIG2 = 0x2;
168
169 /* turn off the idle */
170 IISCON &= ~(1<<3);
171
172 pcm_playing = true;
173
174 /* start the IIS */
175 IISCON |= (1<<0);
176
177}
178
179
180
181/* Disconnect the DMA and wait for the FIFO to clear */
182void pcm_play_dma_stop(void)
183{
184 pcm_playing = false;
185
186 /* mask the DMA interrupt */
187 INTMSK |= (1<<19);
188
189 /* De-Activate the channel */
190 DMASKTRIG2 = 0x4;
191
192 /* idle the IIS transmit */
193 IISCON |= (1<<3);
194
195 /* stop the IIS interface */
196 IISCON &= ~(1<<0);
197
198 /* Disconnect the IIS IIS clock */
199 CLKCON &= ~(1<<17);
200
201
202 disable_fiq();
203
204}
205
206
207
208void pcm_play_pause_pause(void)
209{
210 /* idle */
211 IISCON |= (1<<3);
212}
213
214
215
216void pcm_play_pause_unpause(void)
217{
218 /* no idle */
219 IISCON &= ~(1<<3);
220}
221
222
223
224void pcm_set_frequency(unsigned int frequency)
225{
226 int sr_ctrl;
227
228 switch(frequency)
229 {
230 case SAMPR_11:
231 sr_ctrl = 0x19 << 1;
232 break;
233 case SAMPR_22:
234 sr_ctrl = 0x1B << 1;
235 break;
236 default:
237 case SAMPR_44:
238 sr_ctrl = 0x11 << 1;
239 break;
240 case SAMPR_88:
241 sr_ctrl = 0x1F << 1;
242 break;
243 }
244 audiohw_set_sample_rate(sr_ctrl);
245 pcm_freq = frequency;
246}
247
248
249
250size_t pcm_get_bytes_waiting(void)
251{
252 return (DSTAT2 & 0xFFFFF) * 2;
253}
254
255
256
257/* dummy functions for those not actually supporting all this yet */
258void pcm_apply_settings(bool reset)
259{
260 (void)reset;
261}
262
263void pcm_set_monitor(int monitor)
264{
265 (void)monitor;
266}
267/** **/
268
269void pcm_mute(bool mute)
270{
271 audiohw_mute(mute);
272 if (mute)
273 sleep(HZ/16);
274}
275
276/*
277 * This function goes directly into the DMA buffer to calculate the left and
278 * right peak values. To avoid missing peaks it tries to look forward two full
279 * peek periods (2/HZ sec, 100% overlap), although it's always possible that
280 * the entire period will not be visible. To reduce CPU load it only looks at
281 * every third sample, and this can be reduced even further if needed (even
282 * every tenth sample would still be pretty accurate).
283 */
284
285/* Check for a peak every PEAK_STRIDE samples */
286#define PEAK_STRIDE 3
287/* Up to 1/50th of a second of audio for peak calculation */
288/* This should use NATIVE_FREQUENCY, or eventually an adjustable freq. value */
289#define PEAK_SAMPLES (44100/50)
290void pcm_calculate_peaks(int *left, int *right)
291{
292 short *addr;
293 short *end;
294 {
295 size_t samples = p_size / 4;
296 addr = p;
297
298 if (samples > PEAK_SAMPLES)
299 samples = PEAK_SAMPLES - (PEAK_STRIDE - 1);
300 else
301 samples -= MIN(PEAK_STRIDE - 1, samples);
302
303 end = &addr[samples * 2];
304 }
305
306 if (left && right) {
307 int left_peak = 0, right_peak = 0;
308
309 while (addr < end) {
310 int value;
311 if ((value = addr [0]) > left_peak)
312 left_peak = value;
313 else if (-value > left_peak)
314 left_peak = -value;
315
316 if ((value = addr [PEAK_STRIDE | 1]) > right_peak)
317 right_peak = value;
318 else if (-value > right_peak)
319 right_peak = -value;
320
321 addr = &addr[PEAK_STRIDE * 2];
322 }
323
324 *left = left_peak;
325 *right = right_peak;
326 }
327 else if (left || right) {
328 int peak_value = 0, value;
329
330 if (right)
331 addr += (PEAK_STRIDE | 1);
332
333 while (addr < end) {
334 if ((value = addr [0]) > peak_value)
335 peak_value = value;
336 else if (-value > peak_value)
337 peak_value = -value;
338
339 addr += PEAK_STRIDE * 2;
340 }
341
342 if (left)
343 *left = peak_value;
344 else
345 *right = peak_value;
346 }
347}