/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2006 by Michael Sevakis * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include #include "system.h" #include "kernel.h" #include "logf.h" #include "audio.h" #include "sound.h" #include "file.h" #include "mmu-meg-fx.h" /* All exact rates for 16.9344MHz clock */ #define GIGABEAT_11025HZ (0x19 << 1) #define GIGABEAT_22050HZ (0x1b << 1) #define GIGABEAT_44100HZ (0x11 << 1) #define GIGABEAT_88200HZ (0x1f << 1) /* PCM interrupt routine lockout */ static struct { int locked; unsigned long state; } dma_play_lock = { .locked = 0, .state = (0<<19) }; /* Last samplerate set by pcm_set_frequency */ static unsigned long pcm_freq = 0; /* 44.1 is default */ /* Samplerate control for audio codec */ static int sr_ctrl = 0; #define FIFO_COUNT ((IISFCON >> 6) & 0x3F) /* Setup for the DMA controller */ #define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20)) /* DMA count has hit zero - no more data */ /* Get more data from the callback and top off the FIFO */ void fiq_handler(void) __attribute__((interrupt ("FIQ"))); static void _pcm_apply_settings(void) { if (pcm_freq != pcm_curr_sampr) { pcm_curr_sampr = pcm_freq; audiohw_set_frequency(sr_ctrl); } } void pcm_apply_settings(void) { int oldstatus = set_fiq_status(FIQ_DISABLED); _pcm_apply_settings(); set_fiq_status(oldstatus); } /* For the locks, DMA interrupt must be disabled because the handler manipulates INTMSK and the operation is not atomic */ void pcm_play_lock(void) { int status = set_fiq_status(FIQ_DISABLED); if (++dma_play_lock.locked == 1) INTMSK |= (1<<19); /* Mask the DMA interrupt */ set_fiq_status(status); } void pcm_play_unlock(void) { int status = set_fiq_status(FIQ_DISABLED); if (--dma_play_lock.locked == 0) INTMSK &= ~dma_play_lock.state; /* Unmask the DMA interrupt if enabled */ set_fiq_status(status); } void pcm_play_dma_init(void) { pcm_set_frequency(SAMPR_44); /* slave */ IISMOD |= (1<<8); /* RX,TX off,idle */ IISCON |= (1<<3) | (1<<2); audiohw_init(); /* init GPIO */ GPCCON = (GPCCON & ~(3<<14)) | (1<<14); GPCDAT |= (1<<7); /* GPE4=I2SDO, GPE3=I2SDI, GPE2=CDCLK, GPE1=I2SSCLK, GPE0=I2SLRCK */ GPECON = (GPECON & ~0x3ff) | 0x2aa; /* Do not service DMA requests, yet */ /* clear any pending int and mask it */ INTMSK |= (1<<19); SRCPND = (1<<19); /* connect to FIQ */ INTMOD |= (1<<19); } void pcm_postinit(void) { audiohw_postinit(); pcm_apply_settings(); } /* Connect the DMA and start filling the FIFO */ static void play_start_pcm(void) { /* clear pending DMA interrupt */ SRCPND = (1<<19); _pcm_apply_settings(); /* Flush any pending writes */ clean_dcache_range((void*)DISRC2, (DCON2 & 0xFFFFF) * 2); /* unmask DMA interrupt when unlocking */ dma_play_lock.state = (1<<19); /* turn on the request */ IISCON |= (1<<5); /* Activate the channel */ DMASKTRIG2 = 0x2; /* turn off the idle */ IISCON &= ~(1<<3); /* start the IIS */ IISCON |= (1<<0); } /* Disconnect the DMA and wait for the FIFO to clear */ static void play_stop_pcm(void) { /* Mask DMA interrupt */ INTMSK |= (1<<19); /* De-Activate the DMA channel */ DMASKTRIG2 = 0x4; /* are we playing? wait for the chunk to finish */ if (dma_play_lock.state != 0) { /* wait for the FIFO to empty and DMA to stop */ while ((IISCON & (1<<7)) || (DMASKTRIG2 & 0x2)); } /* Keep interrupt masked when unlocking */ dma_play_lock.state = 0; /* turn off the request */ IISCON &= ~(1<<5); /* turn on the idle */ IISCON |= (1<<3); /* stop the IIS */ IISCON &= ~(1<<0); } void pcm_play_dma_start(const void *addr, size_t size) { /* Enable the IIS clock */ CLKCON |= (1<<17); /* stop any DMA in progress - idle IIS */ play_stop_pcm(); /* slave, transmit mode, 16 bit samples - MCLK 384fs - use 16.9344Mhz - BCLK 32fs */ IISMOD = (1<<9) | (1<<8) | (2<<6) | (1<<3) | (1<<2) | (1<<0); /* connect DMA to the FIFO and enable the FIFO */ IISFCON = (1<<15) | (1<<13); /* set DMA dest */ DIDST2 = (unsigned int)&IISFIFO; /* IIS is on the APB bus, INT when TC reaches 0, fixed dest addr */ DIDSTC2 = 0x03; /* set DMA source and options */ DISRC2 = (unsigned int)addr + 0x30000000; /* How many transfers to make - we transfer half-word at a time = 2 bytes */ /* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */ /* no auto-reload, half-word (16bit) */ DCON2 = DMA_CONTROL_SETUP | (size / 2); DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */ play_start_pcm(); } /* Promptly stop DMA transfers and stop IIS */ void pcm_play_dma_stop(void) { play_stop_pcm(); /* Disconnect the IIS clock */ CLKCON &= ~(1<<17); } void pcm_play_dma_pause(bool pause) { if (pause) { /* pause playback on current buffer */ play_stop_pcm(); } else { /* restart playback on current buffer */ /* make sure we're aligned on left channel - skip any right channel sample left waiting */ DISRC2 = (DCSRC2 + 2) & ~0x3; DCON2 = DMA_CONTROL_SETUP | (DSTAT2 & 0xFFFFE); play_start_pcm(); } } void fiq_handler(void) { static unsigned char *start; static size_t size; register pcm_more_callback_type get_more; /* No stack for this */ /* clear any pending interrupt */ SRCPND = (1<<19); /* Buffer empty. Try to get more. */ get_more = pcm_callback_for_more; size = 0; if (get_more == NULL || (get_more(&start, &size), size == 0)) { /* Callback missing or no more DMA to do */ pcm_play_dma_stop(); pcm_play_dma_stopped_callback(); } else { /* Flush any pending cache writes */ clean_dcache_range(start, size); /* set the new DMA values */ DCON2 = DMA_CONTROL_SETUP | (size >> 1); DISRC2 = (unsigned int)start + 0x30000000; /* Re-Activate the channel */ DMASKTRIG2 = 0x2; } } void pcm_set_frequency(unsigned int frequency) { switch(frequency) { case SAMPR_11: sr_ctrl = GIGABEAT_11025HZ; break; case SAMPR_22: sr_ctrl = GIGABEAT_22050HZ; break; default: frequency = SAMPR_44; case SAMPR_44: sr_ctrl = GIGABEAT_44100HZ; break; case SAMPR_88: sr_ctrl = GIGABEAT_88200HZ; break; } pcm_freq = frequency; } size_t pcm_get_bytes_waiting(void) { /* lie a little and only return full pairs */ return (DSTAT2 & 0xFFFFE) * 2; } const void * pcm_play_dma_get_peak_buffer(int *count) { unsigned long addr = DCSRC2; int cnt = DSTAT2; *count = (cnt & 0xFFFFF) >> 1; return (void *)((addr + 2) & ~3); }