diff options
author | Marcoen Hirschberg <marcoen@gmail.com> | 2006-12-29 02:49:12 +0000 |
---|---|---|
committer | Marcoen Hirschberg <marcoen@gmail.com> | 2006-12-29 02:49:12 +0000 |
commit | 295367686ec9855c4d90f68a6003e819fef8e7ab (patch) | |
tree | b4077ffb8d2283bf199ad12a90322be77040c2fd /firmware/target/arm/gigabeat/meg-fx/pcm-meg-fx.c | |
parent | 995a804defda23233ccbdd859023f4ba3ecba0bf (diff) | |
download | rockbox-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.c | 347 |
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 | |||
26 | static 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 | |||
36 | unsigned short * p; | ||
37 | size_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"))); | ||
44 | void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ"))); | ||
45 | void 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 | |||
81 | void 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 | |||
110 | void 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 */ | ||
182 | void 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 | |||
208 | void pcm_play_pause_pause(void) | ||
209 | { | ||
210 | /* idle */ | ||
211 | IISCON |= (1<<3); | ||
212 | } | ||
213 | |||
214 | |||
215 | |||
216 | void pcm_play_pause_unpause(void) | ||
217 | { | ||
218 | /* no idle */ | ||
219 | IISCON &= ~(1<<3); | ||
220 | } | ||
221 | |||
222 | |||
223 | |||
224 | void 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 | |||
250 | size_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 */ | ||
258 | void pcm_apply_settings(bool reset) | ||
259 | { | ||
260 | (void)reset; | ||
261 | } | ||
262 | |||
263 | void pcm_set_monitor(int monitor) | ||
264 | { | ||
265 | (void)monitor; | ||
266 | } | ||
267 | /** **/ | ||
268 | |||
269 | void 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) | ||
290 | void 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 | } | ||