summaryrefslogtreecommitdiff
path: root/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c')
-rw-r--r--firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c376
1 files changed, 376 insertions, 0 deletions
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
new file mode 100644
index 0000000000..0f22aa5c5c
--- /dev/null
+++ b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
@@ -0,0 +1,376 @@
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 "sound.h"
24#include "file.h"
25#include "mmu-meg-fx.h"
26
27static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
28
29#define GIGABEAT_8000HZ 0x4d
30#define GIGABEAT_11025HZ 0x32
31#define GIGABEAT_12000HZ 0x61
32#define GIGABEAT_16000HZ 0x55
33#define GIGABEAT_22050HZ 0x36
34#define GIGABEAT_24000HZ 0x79
35#define GIGABEAT_32000HZ 0x59
36#define GIGABEAT_44100HZ 0x22
37#define GIGABEAT_48000HZ 0x41
38#define GIGABEAT_88200HZ 0x3e
39#define GIGABEAT_96000HZ 0x5d
40
41#define FIFO_COUNT ((IISFCON >> 6) & 0x01F)
42
43/* number of bytes in FIFO */
44#define IIS_FIFO_SIZE 64
45
46/* Setup for the DMA controller */
47#define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20))
48
49unsigned short * p;
50size_t p_size;
51
52
53
54/* DMA count has hit zero - no more data */
55/* Get more data from the callback and top off the FIFO */
56//void fiq(void) __attribute__ ((interrupt ("naked")));
57void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ")));
58void fiq(void)
59{
60 /* clear any pending interrupt */
61 SRCPND = (1<<19);
62
63 /* Buffer empty. Try to get more. */
64 if (pcm_callback_for_more)
65 {
66 pcm_callback_for_more((unsigned char**)&p, &p_size);
67 }
68 else
69 {
70 /* callback func is missing? */
71 pcm_play_dma_stop();
72 return;
73 }
74
75 if (p_size)
76 {
77 /* Flush any pending cache writes */
78 clean_dcache_range(p, p_size);
79
80 /* set the new DMA values */
81 DCON2 = DMA_CONTROL_SETUP | (p_size >> 1);
82 DISRC2 = (int)p + 0x30000000;
83
84 /* Re-Activate the channel */
85 DMASKTRIG2 = 0x2;
86 }
87 else
88 {
89 /* No more DMA to do */
90 pcm_play_dma_stop();
91 }
92
93}
94
95
96
97void pcm_init(void)
98{
99 pcm_playing = false;
100 pcm_paused = false;
101 pcm_callback_for_more = NULL;
102
103 audiohw_init();
104 audiohw_enable_output(true);
105
106 /* cannot use the WM8975 defaults since our clock is not the same */
107 /* the input master clock is 16.9344MHz - we can divide exact for that */
108 pcm_set_frequency(SAMPR_44);
109
110 /* init GPIO */
111 GPCCON = (GPCCON & ~(3<<14)) | (1<<14);
112 GPCDAT |= 1<<7;
113 GPECON |= 0x2aa;
114
115 /* Do not service DMA requests, yet */
116 /* clear any pending int and mask it */
117 INTMSK |= (1<<19); /* mask the interrupt */
118 SRCPND = (1<<19); /* clear any pending interrupts */
119 INTMOD |= (1<<19); /* connect to FIQ */
120
121}
122
123void pcm_postinit(void)
124{
125 audiohw_postinit();
126}
127
128void pcm_play_dma_start(const void *addr, size_t size)
129{
130 /* sanity check: bad pointer or too small file */
131 if (NULL == addr || size <= IIS_FIFO_SIZE) return;
132
133 p = (unsigned short *)addr;
134 p_size = size;
135
136 /* Enable the IIS clock */
137 CLKCON |= (1<<17);
138
139 /* IIS interface setup and set to idle */
140 IISCON = (1<<5) | (1<<3);
141
142 /* slave, transmit mode, 16 bit samples - 384fs - use 16.9344Mhz */
143 IISMOD = (1<<9) | (1<<8) | (2<<6) | (1<<3) | (1<<2);
144
145 /* connect DMA to the FIFO and enable the FIFO */
146 IISFCON = (1<<15) | (1<<13);
147
148 /* set DMA dest */
149 DIDST2 = (int)&IISFIFO;
150
151 /* IIS is on the APB bus, INT when TC reaches 0, fixed dest addr */
152 DIDSTC2 = 0x03;
153
154 /* How many transfers to make - we transfer half-word at a time = 2 bytes */
155 /* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */
156 /* no auto-reload, half-word (16bit) */
157 DCON2 = DMA_CONTROL_SETUP | (p_size / 2);
158
159 /* set DMA source and options */
160 DISRC2 = (int)p + 0x30000000;
161 DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */
162
163 /* clear pending DMA interrupt */
164 SRCPND = 1<<19;
165
166 set_fiq_handler(fiq);
167 enable_fiq();
168
169 /* unmask the DMA interrupt */
170 INTMSK &= ~(1<<19);
171
172 /* Flush any pending writes */
173 clean_dcache_range(addr, size);
174
175 /* Activate the channel */
176 DMASKTRIG2 = 0x2;
177
178 /* turn off the idle */
179 IISCON &= ~(1<<3);
180
181 pcm_playing = true;
182
183 /* start the IIS */
184 IISCON |= (1<<0);
185
186}
187
188
189
190/* Disconnect the DMA and wait for the FIFO to clear */
191void pcm_play_dma_stop(void)
192{
193 /* mask the DMA interrupt */
194 INTMSK |= (1<<19);
195
196 /* are we playing? wait for the chunk to finish */
197 if (pcm_playing)
198 {
199 /* wait for the FIFO to empty before turning things off */
200 while (IISCON & (1<<7)) ;
201
202 pcm_playing = false;
203 }
204
205 /* De-Activate the DMA channel */
206 DMASKTRIG2 = 0x4;
207
208 /* Disconnect the IIS clock */
209 CLKCON &= ~(1<<17);
210
211 disable_fiq();
212
213}
214
215
216
217void pcm_play_pause_pause(void)
218{
219 /* stop servicing refills */
220 INTMSK |= (1<<19);
221}
222
223
224
225void pcm_play_pause_unpause(void)
226{
227 /* refill buffer and keep going */
228 INTMSK &= ~(1<<19);
229}
230
231void pcm_set_frequency(unsigned int frequency)
232{
233 int sr_ctrl;
234
235 switch(frequency)
236 {
237 case SAMPR_8:
238 sr_ctrl = GIGABEAT_8000HZ;
239 break;
240 case SAMPR_11:
241 sr_ctrl = GIGABEAT_11025HZ;
242 break;
243 case SAMPR_12:
244 sr_ctrl = GIGABEAT_12000HZ;
245 break;
246 case SAMPR_16:
247 sr_ctrl = GIGABEAT_16000HZ;
248 break;
249 case SAMPR_22:
250 sr_ctrl = GIGABEAT_22050HZ;
251 break;
252 case SAMPR_24:
253 sr_ctrl = GIGABEAT_24000HZ;
254 break;
255 case SAMPR_32:
256 sr_ctrl = GIGABEAT_32000HZ;
257 break;
258 default:
259 frequency = SAMPR_44;
260 case SAMPR_44:
261 sr_ctrl = GIGABEAT_44100HZ;
262 break;
263 case SAMPR_48:
264 sr_ctrl = GIGABEAT_48000HZ;
265 break;
266 case SAMPR_88:
267 sr_ctrl = GIGABEAT_88200HZ;
268 break;
269 case SAMPR_96:
270 sr_ctrl = GIGABEAT_96000HZ;
271 break;
272 }
273
274 audiohw_set_sample_rate(sr_ctrl);
275 pcm_freq = frequency;
276}
277
278
279
280size_t pcm_get_bytes_waiting(void)
281{
282 return (DSTAT2 & 0xFFFFF) * 2;
283}
284
285
286
287/* dummy functions for those not actually supporting all this yet */
288void pcm_apply_settings(void)
289{
290}
291
292void pcm_set_monitor(int monitor)
293{
294 (void)monitor;
295}
296/** **/
297
298void pcm_mute(bool mute)
299{
300 audiohw_mute(mute);
301 if (mute)
302 sleep(HZ/16);
303}
304
305/*
306 * This function goes directly into the DMA buffer to calculate the left and
307 * right peak values. To avoid missing peaks it tries to look forward two full
308 * peek periods (2/HZ sec, 100% overlap), although it's always possible that
309 * the entire period will not be visible. To reduce CPU load it only looks at
310 * every third sample, and this can be reduced even further if needed (even
311 * every tenth sample would still be pretty accurate).
312 */
313
314/* Check for a peak every PEAK_STRIDE samples */
315#define PEAK_STRIDE 3
316/* Up to 1/50th of a second of audio for peak calculation */
317/* This should use NATIVE_FREQUENCY, or eventually an adjustable freq. value */
318#define PEAK_SAMPLES (44100/50)
319void pcm_calculate_peaks(int *left, int *right)
320{
321 short *addr;
322 short *end;
323 {
324 size_t samples = p_size / 4;
325 addr = p;
326
327 if (samples > PEAK_SAMPLES)
328 samples = PEAK_SAMPLES - (PEAK_STRIDE - 1);
329 else
330 samples -= MIN(PEAK_STRIDE - 1, samples);
331
332 end = &addr[samples * 2];
333 }
334
335 if (left && right) {
336 int left_peak = 0, right_peak = 0;
337
338 while (addr < end) {
339 int value;
340 if ((value = addr [0]) > left_peak)
341 left_peak = value;
342 else if (-value > left_peak)
343 left_peak = -value;
344
345 if ((value = addr [PEAK_STRIDE | 1]) > right_peak)
346 right_peak = value;
347 else if (-value > right_peak)
348 right_peak = -value;
349
350 addr = &addr[PEAK_STRIDE * 2];
351 }
352
353 *left = left_peak;
354 *right = right_peak;
355 }
356 else if (left || right) {
357 int peak_value = 0, value;
358
359 if (right)
360 addr += (PEAK_STRIDE | 1);
361
362 while (addr < end) {
363 if ((value = addr [0]) > peak_value)
364 peak_value = value;
365 else if (-value > peak_value)
366 peak_value = -value;
367
368 addr += PEAK_STRIDE * 2;
369 }
370
371 if (left)
372 *left = peak_value;
373 else
374 *right = peak_value;
375 }
376}