| // |
| // Copyright(c) by Benny Sjostrand (benny@hostmobility.com) |
| // |
| // 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 program is distributed in the hope that it will be useful, |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| // GNU General Public License for more details. |
| // |
| // You should have received a copy of the GNU General Public License |
| // along with this program; if not, write to the Free Software |
| // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| // |
| |
| |
| // |
| // This code runs inside the DSP (cs4610, cs4612, cs4624, or cs4630), |
| // to compile it you need a tool named SPASM 3.0 and DSP code owned by |
| // Cirrus Logic(R). The SPASM program will generate a object file (cwcdma.osp), |
| // the "ospparser" tool will genereate the cwcdma.h file it's included from |
| // the cs46xx_lib.c file. |
| // |
| // |
| // The purpose of this code is very simple: make it possible to tranfser |
| // the samples 'as they are' with no alteration from a PCMreader |
| // SCB (DMA from host) to any other SCB. This is useful for AC3 through SPDIF. |
| // SRC (source rate converters) task always alters the samples in somehow, |
| // however it's from 48khz -> 48khz. |
| // The alterations are not audible, but AC3 wont work. |
| // |
| // ... |
| // | |
| // +---------------+ |
| // | AsynchFGTxSCB | |
| // +---------------+ |
| // | |
| // subListPtr |
| // | |
| // +--------------+ |
| // | DMAReader | |
| // +--------------+ |
| // | |
| // subListPtr |
| // | |
| // +-------------+ |
| // | PCMReader | |
| // +-------------+ |
| // (DMA from host) |
| // |
| |
| struct dmaSCB |
| { |
| long dma_reserved1[3]; |
| |
| short dma_reserved2:dma_outBufPtr; |
| |
| short dma_unused1:dma_unused2; |
| |
| long dma_reserved3[4]; |
| |
| short dma_subListPtr:dma_nextSCB; |
| short dma_SPBptr:dma_entryPoint; |
| |
| long dma_strmRsConfig; |
| long dma_strmBufPtr; |
| |
| long dma_reserved4; |
| |
| VolumeControl s2m_volume; |
| }; |
| |
| #export DMAReader |
| void DMAReader() |
| { |
| execChild(); |
| r2 = r0->dma_subListPtr; |
| r1 = r0->nextSCB; |
| |
| rsConfig01 = r2->strmRsConfig; |
| // Load rsConfig for input buffer |
| |
| rsDMA01 = r2->basicReq.daw, , tb = Z(0 - rf); |
| // Load rsDMA in case input buffer is a DMA buffer Test to see if there is any data to transfer |
| |
| if (tb) goto execSibling_2ind1 after { |
| r5 = rf + (-1); |
| r6 = r1->dma_entryPoint; // r6 = entry point of sibling task |
| r1 = r1->dma_SPBptr, // r1 = pointer to sibling task's SPB |
| , ind = r6; // Load entry point of sibling task |
| } |
| |
| rsConfig23 = r0->dma_strmRsConfig; |
| // Load rsConfig for output buffer (never a DMA buffer) |
| |
| r4 = r0->dma_outBufPtr; |
| |
| rsa0 = r2->strmBufPtr; |
| // rsa0 = input buffer pointer |
| |
| for (i = r5; i >= 0; --i) |
| after { |
| rsa2 = r4; |
| // rsa2 = output buffer pointer |
| |
| nop; |
| nop; |
| } |
| //***************************** |
| // TODO: cycles to this point * |
| //***************************** |
| { |
| acc0 = (rsd0 = *rsa0++1); |
| // get sample |
| |
| nop; // Those "nop"'s are really uggly, but there's |
| nop; // something with DSP's pipelines which I don't |
| nop; // understand, resulting this code to fail without |
| // having those "nop"'s (Benny) |
| |
| rsa0?reqDMA = r2; |
| // Trigger DMA transfer on input stream, |
| // if needed to replenish input buffer |
| |
| nop; |
| // Yet another magic "nop" to make stuff work |
| |
| ,,r98 = acc0 $+>> 0; |
| // store sample in ALU |
| |
| nop; |
| // latency on load register. |
| // (this one is understandable) |
| |
| *rsa2++1 = r98; |
| // store sample in output buffer |
| |
| nop; // The same story |
| nop; // as above again ... |
| nop; |
| } |
| // TODO: cycles per loop iteration |
| |
| r2->strmBufPtr = rsa0,, ; |
| // Update the modified buffer pointers |
| |
| r4 = rsa2; |
| // Load output pointer position into r4 |
| |
| r2 = r0->nextSCB; |
| // Sibling task |
| |
| goto execSibling_2ind1 // takes 6 cycles |
| after { |
| r98 = r2->thisSPB:entryPoint; |
| // Load child routine entry and data address |
| |
| r1 = r9; |
| // r9 is r2->thisSPB |
| |
| r0->dma_outBufPtr = r4,, |
| // Store updated output buffer pointer |
| |
| ind = r8; |
| // r8 is r2->entryPoint |
| } |
| } |