| //  | 
 | //  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 | 
 |     } | 
 | } |