diff options
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.c | 376 |
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 | |||
27 | static 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 | |||
49 | unsigned short * p; | ||
50 | size_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"))); | ||
57 | void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ"))); | ||
58 | void 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 | |||
97 | void 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 | |||
123 | void pcm_postinit(void) | ||
124 | { | ||
125 | audiohw_postinit(); | ||
126 | } | ||
127 | |||
128 | void 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 */ | ||
191 | void 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 | |||
217 | void pcm_play_pause_pause(void) | ||
218 | { | ||
219 | /* stop servicing refills */ | ||
220 | INTMSK |= (1<<19); | ||
221 | } | ||
222 | |||
223 | |||
224 | |||
225 | void pcm_play_pause_unpause(void) | ||
226 | { | ||
227 | /* refill buffer and keep going */ | ||
228 | INTMSK &= ~(1<<19); | ||
229 | } | ||
230 | |||
231 | void 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 | |||
280 | size_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 */ | ||
288 | void pcm_apply_settings(void) | ||
289 | { | ||
290 | } | ||
291 | |||
292 | void pcm_set_monitor(int monitor) | ||
293 | { | ||
294 | (void)monitor; | ||
295 | } | ||
296 | /** **/ | ||
297 | |||
298 | void 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) | ||
319 | void 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 | } | ||