1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
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
);
71 pcm_callback_for_more
= NULL
;
73 pcm_set_frequency(SAMPR_44
);
78 GPCCON
= (GPCCON
& ~(3<<14)) | (1<<14);
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)
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;
106 /* Enable the IIS clock */
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);
119 DIDST2
= (int)&IISFIFO
;
121 /* IIS is on the APB bus, INT when TC reaches 0, fixed dest addr */
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 */
138 _pcm_apply_settings();
140 /* unmask the DMA interrupt */
143 /* Flush any pending writes */
144 clean_dcache_range(addr
, size
);
146 /* Activate the channel */
149 /* turn off the idle */
158 static void pcm_play_dma_stop_fiq(void)
160 /* mask the DMA interrupt */
163 /* are we playing? wait for the chunk to finish */
166 /* wait for the FIFO to empty before turning things off */
167 while (IISCON
& (1<<7)) ;
173 /* De-Activate the DMA channel */
176 /* Disconnect the IIS clock */
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 */
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();
203 get_more(&next_start
, &next_size
);
207 /* No more DMA to do */
208 pcm_play_dma_stop_fiq();
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 */
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)
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
);
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();
252 set_fiq_status(oldstatus
);
255 void pcm_set_frequency(unsigned int frequency
)
260 sr_ctrl
= GIGABEAT_11025HZ
;
263 sr_ctrl
= GIGABEAT_22050HZ
;
266 frequency
= SAMPR_44
;
268 sr_ctrl
= GIGABEAT_44100HZ
;
271 sr_ctrl
= GIGABEAT_88200HZ
;
275 pcm_freq
= frequency
;
280 size_t pcm_get_bytes_waiting(void)
282 return (DSTAT2
& 0xFFFFF) * 2;
286 void pcm_set_monitor(int monitor
)
293 void pcm_mute(bool mute
)
301 * Return playback peaks - Peaks ahead in the DMA buffer based upon the
302 * calling period to attempt to compensate for
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 */
317 else if (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
;
331 samp_frames
= frame_period
*pcm_freq
/(HZ
/2);
332 samples
= MIN(samp_frames
, samples
) >> 1;
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);
350 peak_l
= ch
, peaksq_l
= chsq
;
355 peak_r
= ch
, peaksq_r
= chsq
;
359 while ((samples
-= 4) > 0);
361 peaks_l
= abs(peak_l
);
362 peaks_r
= abs(peak_r
);
367 peaks_l
= peaks_r
= 0;
375 } /* pcm_calculate_peaks */