always reset the pcm_paused flag when stopping playback. fixes FS #7187
[Rockbox.git] / firmware / target / arm / s3c2440 / gigabeat-fx / pcm-meg-fx.c
blob592842668d52d28cdf701cf1fed80f5a3562f7a6
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006 by Michael Sevakis
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.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include <stdlib.h>
20 #include "system.h"
21 #include "kernel.h"
22 #include "logf.h"
23 #include "audio.h"
24 #include "sound.h"
25 #include "file.h"
26 #include "mmu-meg-fx.h"
28 #define GIGABEAT_11025HZ (0x19 << 1)
29 #define GIGABEAT_22050HZ (0x1b << 1)
30 #define GIGABEAT_44100HZ (0x11 << 1)
31 #define GIGABEAT_88200HZ (0x1f << 1)
33 static int pcm_freq = 0; /* 44.1 is default */
34 static int sr_ctrl = 0;
35 #define FIFO_COUNT ((IISFCON >> 6) & 0x01F)
37 /* number of bytes in FIFO */
38 #define IIS_FIFO_SIZE 64
40 /* Setup for the DMA controller */
41 #define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20))
43 /* DMA count has hit zero - no more data */
44 /* Get more data from the callback and top off the FIFO */
45 /* Uses explicitly coded prologue/epilogue code to get around complier bugs
46 in order to be able to use the stack */
47 void fiq_handler(void) __attribute__((naked));
49 static void _pcm_apply_settings(void)
51 static int last_freqency = 0;
53 if (pcm_freq != last_freqency)
55 last_freqency = pcm_freq;
56 audiohw_set_frequency(sr_ctrl);
60 void pcm_apply_settings(void)
62 int oldstatus = set_fiq_status(FIQ_DISABLED);
63 _pcm_apply_settings();
64 set_fiq_status(oldstatus);
67 void pcm_init(void)
69 pcm_playing = false;
70 pcm_paused = false;
71 pcm_callback_for_more = NULL;
73 pcm_set_frequency(SAMPR_44);
75 audiohw_init();
77 /* init GPIO */
78 GPCCON = (GPCCON & ~(3<<14)) | (1<<14);
79 GPCDAT |= 1<<7;
80 GPECON |= 0x2aa;
82 /* Do not service DMA requests, yet */
83 /* clear any pending int and mask it */
84 INTMSK |= (1<<19); /* mask the interrupt */
85 SRCPND = (1<<19); /* clear any pending interrupts */
86 INTMOD |= (1<<19); /* connect to FIQ */
90 void pcm_postinit(void)
92 audiohw_postinit();
93 pcm_apply_settings();
96 void pcm_play_dma_start(const void *addr, size_t size)
98 addr = (void *)((unsigned long)addr & ~3); /* Align data */
99 size &= ~3; /* Size must be multiple of 4 */
101 /* sanity check: bad pointer or too small file */
102 if (NULL == addr || size <= IIS_FIFO_SIZE) return;
104 disable_fiq();
106 /* Enable the IIS clock */
107 CLKCON |= (1<<17);
109 /* IIS interface setup and set to idle */
110 IISCON = (1<<5) | (1<<3);
112 /* slave, transmit mode, 16 bit samples - 384fs - use 16.9344Mhz */
113 IISMOD = (1<<9) | (1<<8) | (2<<6) | (1<<3) | (1<<2);
115 /* connect DMA to the FIFO and enable the FIFO */
116 IISFCON = (1<<15) | (1<<13);
118 /* set DMA dest */
119 DIDST2 = (int)&IISFIFO;
121 /* IIS is on the APB bus, INT when TC reaches 0, fixed dest addr */
122 DIDSTC2 = 0x03;
124 /* How many transfers to make - we transfer half-word at a time = 2 bytes */
125 /* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */
126 /* no auto-reload, half-word (16bit) */
127 DCON2 = DMA_CONTROL_SETUP | (size / 2);
129 /* set DMA source and options */
130 DISRC2 = (unsigned long)addr + 0x30000000;
131 DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */
133 /* clear pending DMA interrupt */
134 SRCPND = 1<<19;
136 pcm_playing = true;
138 _pcm_apply_settings();
140 /* unmask the DMA interrupt */
141 INTMSK &= ~(1<<19);
143 /* Flush any pending writes */
144 clean_dcache_range(addr, size);
146 /* Activate the channel */
147 DMASKTRIG2 = 0x2;
149 /* turn off the idle */
150 IISCON &= ~(1<<3);
152 /* start the IIS */
153 IISCON |= (1<<0);
155 enable_fiq();
158 static void pcm_play_dma_stop_fiq(void)
160 /* mask the DMA interrupt */
161 INTMSK |= (1<<19);
163 /* are we playing? wait for the chunk to finish */
164 if (pcm_playing)
166 /* wait for the FIFO to empty before turning things off */
167 while (IISCON & (1<<7)) ;
169 pcm_playing = false;
170 pcm_paused = false;
173 /* De-Activate the DMA channel */
174 DMASKTRIG2 = 0x4;
176 /* Disconnect the IIS clock */
177 CLKCON &= ~(1<<17);
180 void fiq_handler(void)
182 /* r0-r7 are probably not all used by GCC but there's no way to know
183 otherwise this whole thing must be assembly */
184 asm volatile ("stmfd sp!, {r0-r7, ip, lr} \n" /* Store context */
185 "sub sp, sp, #8 \n"); /* Reserve stack */
186 register pcm_more_callback_type get_more; /* No stack for this */
187 unsigned char *next_start; /* sp + #0 */
188 size_t next_size; /* sp + #4 */
190 /* clear any pending interrupt */
191 SRCPND = (1<<19);
193 /* Buffer empty. Try to get more. */
194 get_more = pcm_callback_for_more;
195 if (get_more == NULL)
197 /* Callback missing */
198 pcm_play_dma_stop_fiq();
199 goto fiq_exit;
202 next_size = 0;
203 get_more(&next_start, &next_size);
205 if (next_size == 0)
207 /* No more DMA to do */
208 pcm_play_dma_stop_fiq();
209 goto fiq_exit;
212 /* Flush any pending cache writes */
213 clean_dcache_range(next_start, next_size);
215 /* set the new DMA values */
216 DCON2 = DMA_CONTROL_SETUP | (next_size >> 1);
217 DISRC2 = (unsigned long)next_start + 0x30000000;
219 /* Re-Activate the channel */
220 DMASKTRIG2 = 0x2;
222 fiq_exit:
223 asm volatile("add sp, sp, #8 \n" /* Cleanup stack */
224 "ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */
225 "subs pc, lr, #4 \n"); /* Return from FIQ */
228 /* Disconnect the DMA and wait for the FIFO to clear */
229 void pcm_play_dma_stop(void)
231 disable_fiq();
232 pcm_play_dma_stop_fiq();
236 void pcm_play_pause_pause(void)
238 /* stop servicing refills */
239 int oldstatus = set_fiq_status(FIQ_DISABLED);
240 INTMSK |= (1<<19);
241 set_fiq_status(oldstatus);
246 void pcm_play_pause_unpause(void)
248 /* refill buffer and keep going */
249 int oldstatus = set_fiq_status(FIQ_DISABLED);
250 _pcm_apply_settings();
251 INTMSK &= ~(1<<19);
252 set_fiq_status(oldstatus);
255 void pcm_set_frequency(unsigned int frequency)
257 switch(frequency)
259 case SAMPR_11:
260 sr_ctrl = GIGABEAT_11025HZ;
261 break;
262 case SAMPR_22:
263 sr_ctrl = GIGABEAT_22050HZ;
264 break;
265 default:
266 frequency = SAMPR_44;
267 case SAMPR_44:
268 sr_ctrl = GIGABEAT_44100HZ;
269 break;
270 case SAMPR_88:
271 sr_ctrl = GIGABEAT_88200HZ;
272 break;
275 pcm_freq = frequency;
280 size_t pcm_get_bytes_waiting(void)
282 return (DSTAT2 & 0xFFFFF) * 2;
285 #if 0
286 void pcm_set_monitor(int monitor)
288 (void)monitor;
290 #endif
291 /** **/
293 void pcm_mute(bool mute)
295 audiohw_mute(mute);
296 if (mute)
297 sleep(HZ/16);
301 * Return playback peaks - Peaks ahead in the DMA buffer based upon the
302 * calling period to attempt to compensate for
303 * delay.
305 void pcm_calculate_peaks(int *left, int *right)
307 static unsigned long last_peak_tick = 0;
308 static unsigned long frame_period = 0;
309 static int peaks_l = 0, peaks_r = 0;
311 /* Throttled peak ahead based on calling period */
312 unsigned long period = current_tick - last_peak_tick;
314 /* Keep reasonable limits on period */
315 if (period < 1)
316 period = 1;
317 else if (period > HZ/5)
318 period = HZ/5;
320 frame_period = (3*frame_period + period) >> 2;
322 last_peak_tick = current_tick;
324 if (pcm_playing && !pcm_paused)
326 unsigned long *addr = (unsigned long *)DCSRC2;
327 long samples = DSTAT2;
328 long samp_frames;
330 samples &= 0xFFFFE;
331 samp_frames = frame_period*pcm_freq/(HZ/2);
332 samples = MIN(samp_frames, samples) >> 1;
334 if (samples > 0)
336 long peak_l = 0, peak_r = 0;
337 long peaksq_l = 0, peaksq_r = 0;
339 addr -= 0x30000000 >> 2;
340 addr = (long *)((long)addr & ~3);
344 long value = *addr;
345 long ch, chsq;
347 ch = (int16_t)value;
348 chsq = ch*ch;
349 if (chsq > peaksq_l)
350 peak_l = ch, peaksq_l = chsq;
352 ch = value >> 16;
353 chsq = ch*ch;
354 if (chsq > peaksq_r)
355 peak_r = ch, peaksq_r = chsq;
357 addr += 4;
359 while ((samples -= 4) > 0);
361 peaks_l = abs(peak_l);
362 peaks_r = abs(peak_r);
365 else
367 peaks_l = peaks_r = 0;
370 if (left)
371 *left = peaks_l;
373 if (right)
374 *right = peaks_r;
375 } /* pcm_calculate_peaks */