/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright © 2008 Rafaël Carré * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "system.h" #include "audio.h" #include "string.h" #include "as3525.h" #include "pl081.h" #include "dma-target.h" #include "clock-target.h" #include "panic.h" #include "as3514.h" #include "audiohw.h" #include "mmu-arm.h" #define MAX_TRANSFER (4*((1<<11)-1)) /* maximum data we can transfer via DMA * i.e. 32 bits at once (size of I2SO_DATA) * and the number of 32bits words has to * fit in 11 bits of DMA register */ static unsigned char *dma_start_addr; static size_t dma_size; /* in 4*32 bits */ static void dma_callback(void); static int locked = 0; /* Mask the DMA interrupt */ void pcm_play_lock(void) { if(++locked == 1) VIC_INT_EN_CLEAR = INTERRUPT_DMAC; } /* Unmask the DMA interrupt if enabled */ void pcm_play_unlock(void) { if(--locked == 0) VIC_INT_ENABLE |= INTERRUPT_DMAC; } static void play_start_pcm(void) { const unsigned char* addr = dma_start_addr; size_t size = dma_size; if(size > MAX_TRANSFER) size = MAX_TRANSFER; if((unsigned int)dma_start_addr & 3) panicf("unaligned pointer!"); dma_size -= size; dma_start_addr += size; CGU_PERI |= CGU_I2SOUT_APB_CLOCK_ENABLE; CGU_AUDIO |= (1<<11); clean_dcache_range((void*)addr, size); /* force write back */ dma_enable_channel(1, (void*)addr, (void*)I2SOUT_DATA, DMA_PERI_I2SOUT, DMAC_FLOWCTRL_DMAC_MEM_TO_PERI, true, false, size >> 2, DMA_S1, dma_callback); } static void dma_callback(void) { if(!dma_size) { register pcm_more_callback_type get_more = pcm_callback_for_more; if(get_more) get_more(&dma_start_addr, &dma_size); } if(!dma_size) { pcm_play_dma_stop(); pcm_play_dma_stopped_callback(); } else play_start_pcm(); } void pcm_play_dma_start(const void *addr, size_t size) { dma_size = size; dma_start_addr = (unsigned char*)addr; dma_retain(); play_start_pcm(); } void pcm_play_dma_stop(void) { dma_disable_channel(1); dma_size = 0; dma_release(); CGU_PERI &= ~CGU_I2SOUT_APB_CLOCK_ENABLE; CGU_AUDIO &= ~(1<<11); } void pcm_play_dma_pause(bool pause) { if(pause) dma_disable_channel(1); else play_start_pcm(); } void pcm_play_dma_init(void) { CGU_PERI |= CGU_I2SOUT_APB_CLOCK_ENABLE; /* clock source PLLA, minimal frequency */ CGU_AUDIO |= (511<<2) | (1<<0); I2SOUT_CONTROL |= (1<<6) ; /* enable dma */ I2SOUT_CONTROL |= (1<<3) ; /* stereo */ I2SOUT_CONTROL &= ~(1<<2); /* 16 bit samples */ audiohw_preinit(); } void pcm_postinit(void) { audiohw_postinit(); } void pcm_dma_apply_settings(void) { unsigned long frequency = pcm_sampr; const int divider = (((AS3525_PLLA_FREQ/128) + (frequency/2)) / frequency) - 1; if(divider < 0 || divider > 511) panicf("unsupported frequency %ld", frequency); int cgu_audio = CGU_AUDIO; /* read register */ cgu_audio &= ~(511 << 2); /* clear i2sout divider */ cgu_audio |= divider << 2; /* set new i2sout divider */ #if 0 cgu_audio &= ~(511 << 14); /* clear i2sin divider */ cgu_audio |= divider << 14; /* set new i2sin divider */ #endif CGU_AUDIO = cgu_audio; /* write back register */ } size_t pcm_get_bytes_waiting(void) { return dma_size; } const void * pcm_play_dma_get_peak_buffer(int *count) { *count = dma_size >> 2; return (const void*)dma_start_addr; } #ifdef HAVE_PCM_DMA_ADDRESS void * pcm_dma_addr(void *addr) { if (addr != NULL) addr = UNCACHED_ADDR(addr); return addr; } #endif /**************************************************************************** ** Recording DMA transfer **/ #ifdef HAVE_RECORDING void pcm_rec_lock(void) { } void pcm_rec_unlock(void) { } void pcm_record_more(void *start, size_t size) { (void)start; (void)size; } void pcm_rec_dma_stop(void) { } void pcm_rec_dma_start(void *addr, size_t size) { (void)addr; (void)size; } void pcm_rec_dma_close(void) { } void pcm_rec_dma_init(void) { } const void * pcm_rec_dma_get_peak_buffer(int *count) { (void)count; } #endif /* HAVE_RECORDING */