| /*************************************************************************** |
| * __________ __ ___. |
| * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| * \/ \/ \/ \/ \/ |
| * $Id$ |
| * |
| * Copyright (C) 2007 and 2009 by Karl Kurbjun |
| * |
| * 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 <stdlib.h> |
| #include "system.h" |
| #include "kernel.h" |
| #include "logf.h" |
| #include "audio.h" |
| #include "sound.h" |
| #include "file.h" |
| #include "dsp-target.h" |
| #include "dsp/ipc.h" |
| #include "mmu-arm.h" |
| #include "pcm-internal.h" |
| |
| /* This is global to save some latency when pcm_play_dma_get_peak_buffer is |
| * called. |
| */ |
| static void *start; |
| |
| void pcm_postinit(void) |
| { |
| /* Configure clock divider */ |
| tsc2100_writereg(CONTROL_PAGE2, TSPP1_ADDRESS, 0x1120); |
| tsc2100_writereg(CONTROL_PAGE2, TSAC3_ADDRESS, 0x0800); |
| tsc2100_writereg(CONTROL_PAGE2, TSCPC_ADDRESS, 0x3B00); |
| tsc2100_writereg(CONTROL_PAGE2, TSAC1_ADDRESS, 0x0300); |
| tsc2100_writereg(CONTROL_PAGE2, TSCSC_ADDRESS, 0xC580); |
| audiohw_postinit(); |
| } |
| |
| /* Return the current location in the SDRAM to SARAM transfer along with the |
| * number of bytes read in the current buffer (count). There is latency with |
| * this method equivalent to ~ the size of the SARAM buffer since there is |
| * another buffer between your ears and this calculation, but this works for |
| * key clicks and an approximate peak meter. |
| */ |
| const void * pcm_play_dma_get_peak_buffer(int *count) |
| { |
| int cnt = DSP_(_sdem_level); |
| |
| unsigned long addr = (unsigned long) start + cnt; |
| |
| *count = (cnt & 0xFFFFF) >> 1; |
| return (void *)((addr + 2) & ~3); |
| } |
| |
| void pcm_play_dma_init(void) |
| { |
| IO_INTC_IRQ0 = INTR_IRQ0_IMGBUF; |
| bitset16(&IO_INTC_EINT0, INTR_EINT0_IMGBUF); |
| |
| /* Set this as a FIQ */ |
| bitset16(&IO_INTC_FISEL0, INTR_EINT0_IMGBUF); |
| |
| /* Enable the HPIB clock */ |
| bitset16(&IO_CLK_MOD0, (CLK_MOD0_HPIB | CLK_MOD0_DSP)); |
| |
| IO_SDRAM_SDDMASEL = 0x24; |
| |
| IO_DSPC_HPIB_CONTROL = 1 << 10 | 1 << 9 | 1 << 8 | 1 << 7 | 1 << 3 | 1 << 0; |
| |
| dsp_reset(); |
| dsp_load(dsp_image); |
| |
| DSP_(_dma0_stopped)=1; |
| dsp_wake(); |
| } |
| |
| void pcm_dma_apply_settings(void) |
| { |
| audiohw_set_frequency(pcm_fsel); |
| } |
| |
| /* Note that size is actually limited to the size of a short right now due to |
| * the implementation on the DSP side (and the way that we access it) |
| */ |
| void pcm_play_dma_start(const void *addr, size_t size) |
| { |
| unsigned long sdem_addr=(unsigned long)addr - CONFIG_SDRAM_START; |
| /* Initialize codec. */ |
| DSP_(_sdem_addrl) = sdem_addr & 0xffff; |
| DSP_(_sdem_addrh) = sdem_addr >> 16; |
| DSP_(_sdem_dsp_size) = size; |
| DSP_(_dma0_stopped)=0; |
| |
| dsp_wake(); |
| } |
| |
| void pcm_play_dma_stop(void) |
| { |
| DSP_(_dma0_stopped)=1; |
| dsp_wake(); |
| } |
| |
| void pcm_play_lock(void) |
| { |
| |
| } |
| |
| void pcm_play_unlock(void) |
| { |
| |
| } |
| |
| void pcm_play_dma_pause(bool pause) |
| { |
| if (pause) |
| { |
| DSP_(_dma0_stopped)=2; |
| dsp_wake(); |
| } |
| else |
| { |
| DSP_(_dma0_stopped)=0; |
| dsp_wake(); |
| } |
| } |
| |
| size_t pcm_get_bytes_waiting(void) |
| { |
| return DSP_(_sdem_dsp_size)-DSP_(_sdem_level); |
| } |
| |
| /* Only used when debugging */ |
| static char buffer[80]; |
| |
| void DSPHINT(void) __attribute__ ((section(".icode"))); |
| void DSPHINT(void) |
| { |
| unsigned int i; |
| size_t size; |
| |
| IO_INTC_FIQ0 = INTR_IRQ0_IMGBUF; |
| |
| switch (dsp_message.msg) |
| { |
| case MSG_DEBUGF: |
| /* DSP stores one character per word. */ |
| for (i = 0; i < sizeof(buffer); i++) |
| { |
| buffer[i] = dsp_message.payload.debugf.buffer[i]; |
| } |
| |
| DEBUGF("DSP: %s", buffer); |
| break; |
| |
| case MSG_REFILL: |
| /* Buffer empty. Try to get more. */ |
| pcm_play_get_more_callback(&start, &size); |
| |
| if (size != 0) |
| { |
| unsigned long sdem_addr=(unsigned long)start - CONFIG_SDRAM_START; |
| /* Flush any pending cache writes */ |
| clean_dcache_range(start, size); |
| |
| /* set the new DMA values */ |
| DSP_(_sdem_addrl) = sdem_addr & 0xffff; |
| DSP_(_sdem_addrh) = sdem_addr >> 16; |
| DSP_(_sdem_dsp_size) = size; |
| |
| DEBUGF("pcm_sdram at 0x%08lx, sdem_addr 0x%08lx", |
| (unsigned long)start, (unsigned long)sdem_addr); |
| |
| pcm_play_dma_started_callback(); |
| } |
| |
| break; |
| default: |
| DEBUGF("DSP: unknown msg 0x%04x", dsp_message.msg); |
| break; |
| } |
| |
| /* Re-Activate the channel */ |
| dsp_wake(); |
| |
| DEBUGF("DSP: %s", buffer); |
| } |
| |