/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2008 by Catalin Patulea * Copyright (C) 2008 by Maurus Cuelenaere * Copyright (C) 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 "registers.h" #include "arm.h" #include "ipc.h" /* Size of data buffer in words (16 bit) */ #define DSP_BUFFER_SIZE (0x1000) /* Put the "data" buffer in it's own .dma section so that it can * be handled in the linker.cmd. */ #pragma DATA_SECTION (data, ".dma") /* This is the "data" buffer on the DSP side used for SARAM to McBSP (IIS) */ static signed short data[DSP_BUFFER_SIZE]; /* These two describe the location of the buffer on the ARM (set in DSPHINT) */ volatile unsigned short sdem_addrh; volatile unsigned short sdem_addrl; /* This is the size of the ARM buffer (set in DSPHINT) */ volatile unsigned short sdem_dsp_size; /* These two variables keep track of the buffer level in the DSP, dsp_level, * (SARAM to McBSP) and the level on the ARM buffer (sdem_level). * sdem_level is used in the main firmware to keep track of the current * playback status. dsp_level is only used in this function. */ static unsigned short dsp_level; volatile unsigned short sdem_level; /* This is used to keep track of the last SDRAM to SARAM transfer */ static unsigned short last_size; /* This tells us which half of the DSP buffer (data) is free */ static unsigned short dma0_unlocked; /* This is used by the ARM to flag playback status and start/stop the DMA * transfers. */ volatile unsigned short dma0_stopped; /* This is used to effectively flag whether the ARM has new data ready or not */ short waiting; /* rebuffer sets up the next SDRAM to SARAM transfer and tells the ARM when DMA * needs a new buffer. * * Note: The upper limit on larger buffers is the size of a short. If larger * buffer sizes are needed the code on the ARM side needs to be changed to * update a full long. */ void rebuffer(void) { unsigned long sdem_addr; if(dma0_stopped==1 || dma0_stopped==2) /* Stop / Pause */ { /* Stop MCBSP DMA0 */ DMPREC &= 0xFFFE; /* Shut the transmitter down */ audiohw_stop(); /* Stop the HPIB transfer if it is running */ DMA_TRG = 0; /* Reset the following variables for DMA restart */ sdem_level = 0; dsp_level = 0; last_size = 0; return; } /* If the sdem_level is equal to the buffer size the ARM code gave * (sdem_dsp_size) then reset the size and ask the arm for another buffer */ if(sdem_level == sdem_dsp_size) { sdem_level=0; /* Get a new buffer (location and size) from ARM */ status.msg = MSG_REFILL; waiting=1; /* trigger DSPHINT on the ARM */ int_arm(); } if(!waiting) { /* Size is in bytes (but forced 32 bit transfers). Comparison is * against DSP_BUFFER_SIZE because it is in words and this needs to * compare against half the total size in bytes. */ if( dsp_level + sdem_dsp_size - sdem_level > DSP_BUFFER_SIZE) { last_size = DSP_BUFFER_SIZE - dsp_level; } else { last_size = sdem_dsp_size - sdem_level; } /* DSP addresses are 16 bit (word). dsp_level is in bytes so it needs to * be converted to words. */ DSP_ADDRL = (unsigned short)data + dma0_unlocked + (dsp_level >> 1); DSP_ADDRH = 0; /* SDRAM addresses are 8 bit (byte) * Warning: These addresses are forced to 32 bit alignment! */ sdem_addr = ((unsigned long)sdem_addrh << 16 | sdem_addrl) + sdem_level; SDEM_ADDRL = sdem_addr & 0xffff; SDEM_ADDRH = sdem_addr >> 16; /* Set the size of the SDRAM to SARAM transfer (demac transfer) */ DMA_SIZE = last_size; DMA_CTRL = 0; /* These are just debug signals that are not used/needed right now */ status.payload.refill._DMA_TRG = DMA_TRG; status.payload.refill._SDEM_ADDRH = SDEM_ADDRH; status.payload.refill._SDEM_ADDRL = SDEM_ADDRL; status.payload.refill._DSP_ADDRH = DSP_ADDRH; status.payload.refill._DSP_ADDRL = DSP_ADDRL; /* Start the demac transfer */ DMA_TRG = 1; } } /* This interupt handler is for the SARAM (on DSP) to McBSP IIS DMA transfer. * It interupts at 1/2 empty and empty so that we can start filling a new buffer * from SDRAM when a half is free. dsp_level should always be full when this * interupt occurs except for the initial start. */ interrupt void handle_dma0(void) { /* Byte offset to half-buffer locked by DMA0. 0 for top, DSP_BUFFER_SIZE/2 for bottom */ unsigned short dma0_locked; IFR = 1 << 6; /* DMSRC0 is the beginning of the DMA0-locked SARAM half-buffer. */ DMSA = 0x00 /* DMSRC0 (banked register, see page 133 of SPRU302B */; /* Note that these address offsets (dma0_locked and dma0_unlocked are in * words. */ dma0_locked = DMSDN & (DSP_BUFFER_SIZE>>1); dma0_unlocked = dma0_locked ^ (DSP_BUFFER_SIZE>>1); dsp_level = 0; /* Start the SDRAM to SARAM copy */ rebuffer(); } /* This interupt handler runs every time a DMA transfer is complete from SDRAM * to the SARAM buffer. It is used to update the SARAM buffer level * (dsp_level), the SDRAM buffer level (sdem_level) and to rebuffer if the dsp * buffer is not full. */ interrupt void handle_dmac(void) { IFR = 1 << 11; /* Clear interrupt */ /* dsp_level and sdem_level are in bytes */ dsp_level += last_size; sdem_level += last_size; /* compare to DSP_BUFFER_SIZE without a divide because it is in words and * we want half the total size in bytes. */ if(dsp_level < DSP_BUFFER_SIZE) { rebuffer(); } } void dma_init(void) { /* Initialize some of the global variables to known values avoiding the * .cinit section. */ dsp_level = 0; sdem_level = 0; last_size = 0; dma0_unlocked = 0; dma0_stopped = 1; waiting = 0; /* Configure SARAM to McBSP DMA */ /* Event XEVT0, 32-bit transfers, 0 frame count */ DMSFC0 = 2 << 12 | 1 << 11; /* Interrupts generated, Half and full buffer. * ABU mode, From data space with postincrement, to data space with no * change */ DMMCR0 = 1 << 14 | 1 << 13 | 1 << 12 | 1 << 8 | 1 << 6 | 1; /* Set the source (incrementing) location */ DMSRC0 = (unsigned short)&data; /* Set the destination (static) location to the McBSP IIS interface */ DMDST0 = (unsigned short)&DXR20; /* Set the size of the buffer */ DMCTR0 = sizeof(data); /* Setup DMA0 interrupts and start the transfer */ DMPREC = 2 << 6; }