Nut/OS  4.10.3
API Reference
vscodec.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2008 by egnite GmbH. All rights reserved.
00003  * Copyright (C) 2001-2007 by egnite Software GmbH. All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * 1. Redistributions of source code must retain the above copyright
00010  *    notice, this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above copyright
00012  *    notice, this list of conditions and the following disclaimer in the
00013  *    documentation and/or other materials provided with the distribution.
00014  * 3. Neither the name of the copyright holders nor the names of
00015  *    contributors may be used to endorse or promote products derived
00016  *    from this software without specific prior written permission.
00017  *
00018  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00019  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00020  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00021  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00022  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00023  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00024  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00025  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00026  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00027  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00028  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00029  * SUCH DAMAGE.
00030  *
00031  * For additional information see http://www.ethernut.de/
00032  * -
00033  *
00034  * This software has been inspired by all the valuable work done by
00035  * Jesper Hansen and Pavel Chromy. Many thanks for all their help.
00036  */
00037 
00038 /*
00039  * $Log$
00040  * Revision 1.8  2009/02/13 14:52:05  haraldkipp
00041  * Include memdebug.h for heap management debugging support.
00042  *
00043  * Revision 1.7  2009/01/30 08:59:30  haraldkipp
00044  * Make sure, that used registers are defined.
00045  *
00046  * Revision 1.6  2009/01/17 11:26:46  haraldkipp
00047  * Getting rid of two remaining BSD types in favor of stdint.
00048  * Replaced 'u_int' by 'unsinged int' and 'uptr_t' by 'uintptr_t'.
00049  *
00050  * Revision 1.5  2008/10/23 08:54:07  haraldkipp
00051  * Include the correct header file.
00052  *
00053  * Revision 1.4  2008/09/18 09:51:58  haraldkipp
00054  * Use the correct PORT macros.
00055  *
00056  * Revision 1.3  2008/08/11 06:59:42  haraldkipp
00057  * BSD types replaced by stdint types (feature request #1282721).
00058  *
00059  * Revision 1.2  2008/04/01 10:15:27  haraldkipp
00060  * VS10xx ioctl() returned -1 on success. Fixed.
00061  *
00062  * Revision 1.1  2008/02/15 16:45:41  haraldkipp
00063  * First release.
00064  *
00065  * Revision 1.1  2007/04/12 08:59:55  haraldkipp
00066  * VS10XX decoder support added.
00067  *
00068  */
00069 
00070 #include <sys/atom.h>
00071 #include <sys/event.h>
00072 #include <sys/timer.h>
00073 #include <sys/heap.h>
00074 
00075 #include <cfg/arch/gpio.h>
00076 #include <cfg/audio.h>
00077 #include <dev/irqreg.h>
00078 #include <dev/vscodec.h>
00079 
00080 #include <sys/bankmem.h>
00081 
00082 #include <stdlib.h>
00083 #include <stddef.h>
00084 #include <string.h>
00085 #include <memdebug.h>
00086 
00091 
00092 #if !defined(AUDIO_VS1001K) && !defined(AUDIO_VS1011E) && !defined(AUDIO_VS1002D) && !defined(AUDIO_VS1003B) && !defined(AUDIO_VS1033C) && !defined(AUDIO_VS1053C)
00093 #define AUDIO_VS1001K
00094 #endif
00095 
00096 #ifndef VS10XX_FREQ
00097 
00098 #define VS10XX_FREQ             12288000UL
00099 #endif
00100 
00101 #ifndef VS10XX_HWRST_DURATION
00102 
00103 #define VS10XX_HWRST_DURATION   1
00104 #endif
00105 
00106 #ifndef VS10XX_HWRST_RECOVER
00107 
00108 #define VS10XX_HWRST_RECOVER    4
00109 #endif
00110 
00111 #ifndef VS10XX_SWRST_RECOVER
00112 
00113 #define VS10XX_SWRST_RECOVER    2
00114 #endif
00115 
00116 #ifndef VS10XX_SCI_MODE
00117 #define VS10XX_SCI_MODE         0
00118 #endif
00119 
00120 #ifndef VS10XX_SCI_RATE
00121 #define VS10XX_SCI_RATE         (VS10XX_FREQ / 6)
00122 #endif
00123 
00124 #ifndef VS10XX_SDI_MODE
00125 #define VS10XX_SDI_MODE         0
00126 #endif
00127 
00128 #ifndef VS10XX_SDI_RATE
00129 #define VS10XX_SDI_RATE         (VS10XX_FREQ / 6)
00130 #endif
00131 
00132 
00133 /* -------------------------------------------------
00134  * AT91 port specifications.
00135  */
00136 #if defined (MCU_AT91R40008) || defined (MCU_AT91SAM7X) || defined (MCU_AT91SAM7SE512) || defined (MCU_AT91SAM9260)
00137 
00138 #if defined(ELEKTOR_IR1)
00139 
00140 #define VS_XRESET_BIT           31 /* PB31 */
00141 #define VS10XX_XCS_BIT          PA31_SPI0_NPCS1_A
00142 #define VS10XX_XCS_PORT       PIOA_ID
00143 #define VS10XX_XDCS_BIT         PB30_SPI0_NPCS2_A
00144 #define VS10XX_XDCS_PORT      PIOB_ID
00145 #define VS10XX_DREQ_BIT         PA30_IRQ1_A
00146 #define VS10XX_DREQ_PIO_ID      PIOA_ID
00147 #define VS10XX_SIGNAL           sig_INTERRUPT1
00148 
00149 #endif /* ELEKTOR_IR1 */
00150 
00151 #if defined(VS10XX_XCS_BIT)
00152 
00153 #if !defined(VS10XX_XCS_PORT)
00154 #define VS10XX_XCS_PE_REG        PIO_PER
00155 #define VS10XX_XCS_OE_REG        PIO_OER
00156 #define VS10XX_XCS_COD_REG       PIO_CODR
00157 #define VS10XX_XCS_SOD_REG       PIO_SODR
00158 #elif VS10XX_XCS_PORT == PIOA_ID
00159 #define VS10XX_XCS_PE_REG        PIOA_PER
00160 #define VS10XX_XCS_OE_REG        PIOA_OER
00161 #define VS10XX_XCS_COD_REG       PIOA_CODR
00162 #define VS10XX_XCS_SOD_REG       PIOA_SODR
00163 #elif VS10XX_XCS_PORT == PIOB_ID
00164 #define VS10XX_XCS_PE_REG        PIOB_PER
00165 #define VS10XX_XCS_OE_REG        PIOB_OER
00166 #define VS10XX_XCS_COD_REG       PIOB_CODR
00167 #define VS10XX_XCS_SOD_REG       PIOB_SODR
00168 #elif VS10XX_XCS_PORT == PIOC_ID
00169 #define VS10XX_XCS_PE_REG        PIOC_PER
00170 #define VS10XX_XCS_OE_REG        PIOC_OER
00171 #define VS10XX_XCS_COD_REG       PIOC_CODR
00172 #define VS10XX_XCS_SOD_REG       PIOC_SODR
00173 #endif
00174 #define VS10XX_XCS_ENA() \
00175     outr(VS10XX_XCS_PE_REG, _BV(VS10XX_XCS_BIT)); \
00176     outr(VS10XX_XCS_OE_REG, _BV(VS10XX_XCS_BIT))
00177 #define VS10XX_XCS_CLR()   outr(VS10XX_XCS_COD_REG, _BV(VS10XX_XCS_BIT))
00178 #define VS10XX_XCS_SET()   outr(VS10XX_XCS_SOD_REG, _BV(VS10XX_XCS_BIT))
00179 
00180 #else /* VS10XX_XCS_BIT */
00181 
00182 #define VS10XX_XCS_ENA()
00183 #define VS10XX_XCS_CLR()
00184 #define VS10XX_XCS_SET()
00185 
00186 #endif /* VS10XX_XCS_BIT */
00187 
00188 #if defined(VS10XX_XDCS_BIT)
00189 
00190 #if !defined(VS10XX_XDCS_PORT)
00191 #define VS10XX_XDCS_PE_REG        PIO_PER
00192 #define VS10XX_XDCS_OE_REG        PIO_OER
00193 #define VS10XX_XDCS_COD_REG       PIO_CODR
00194 #define VS10XX_XDCS_SOD_REG       PIO_SODR
00195 #elif VS10XX_XDCS_PORT == PIOA_ID
00196 #define VS10XX_XDCS_PE_REG        PIOA_PER
00197 #define VS10XX_XDCS_OE_REG        PIOA_OER
00198 #define VS10XX_XDCS_COD_REG       PIOA_CODR
00199 #define VS10XX_XDCS_SOD_REG       PIOA_SODR
00200 #elif VS10XX_XDCS_PORT == PIOB_ID
00201 #define VS10XX_XDCS_PE_REG        PIOB_PER
00202 #define VS10XX_XDCS_OE_REG        PIOB_OER
00203 #define VS10XX_XDCS_COD_REG       PIOB_CODR
00204 #define VS10XX_XDCS_SOD_REG       PIOB_SODR
00205 #elif VS10XX_XDCS_PORT == PIOC_ID
00206 #define VS10XX_XDCS_PE_REG        PIOC_PER
00207 #define VS10XX_XDCS_OE_REG        PIOC_OER
00208 #define VS10XX_XDCS_COD_REG       PIOC_CODR
00209 #define VS10XX_XDCS_SOD_REG       PIOC_SODR
00210 #endif
00211 #define VS10XX_XDCS_ENA() \
00212     outr(VS10XX_XDCS_PE_REG, _BV(VS10XX_XDCS_BIT)); \
00213     outr(VS10XX_XDCS_OE_REG, _BV(VS10XX_XDCS_BIT))
00214 #define VS10XX_XDCS_CLR()   outr(VS10XX_XDCS_COD_REG, _BV(VS10XX_XDCS_BIT))
00215 #define VS10XX_XDCS_SET()   outr(VS10XX_XDCS_SOD_REG, _BV(VS10XX_XDCS_BIT))
00216 
00217 #else /* VS10XX_XDCS_BIT */
00218 
00219 #define VS10XX_XDCS_ENA()
00220 #define VS10XX_XDCS_CLR()
00221 #define VS10XX_XDCS_SET()
00222 
00223 #endif /* VS10XX_XDCS_BIT */
00224 
00225 #if defined(VS10XX_DREQ_BIT)
00226 
00227 #if !defined(VS10XX_DREQ_PIO_ID)
00228 #define VS10XX_DREQ_PD_REG       PIO_PDR
00229 #define VS10XX_DREQ_OD_REG       PIO_ODR
00230 #define VS10XX_DREQ_PDS_REG      PIO_PDSR
00231 #elif VS10XX_DREQ_PIO_ID == PIOA_ID
00232 #define VS10XX_DREQ_PD_REG       PIOA_PDR
00233 #define VS10XX_DREQ_OD_REG       PIOA_ODR
00234 #define VS10XX_DREQ_PDS_REG      PIOA_PDSR
00235 #elif VS10XX_DREQ_PIO_ID == PIOB_ID
00236 #define VS10XX_DREQ_PD_REG       PIOB_PDR
00237 #define VS10XX_DREQ_OD_REG       PIOB_ODR
00238 #define VS10XX_DREQ_PDS_REG      PIOB_PDSR
00239 #elif VS10XX_DREQ_PIO_ID == PIOC_ID
00240 #define VS10XX_DREQ_PD_REG       PIOC_PDR
00241 #define VS10XX_DREQ_OD_REG       PIOC_ODR
00242 #define VS10XX_DREQ_PDS_REG      PIOC_PDSR
00243 #endif
00244 
00245 #define VS10XX_DREQ_ENA() \
00246     outr(VS10XX_DREQ_PD_REG, _BV(VS10XX_DREQ_BIT)); \
00247     outr(VS10XX_DREQ_OD_REG, _BV(VS10XX_DREQ_BIT))
00248 #define VS10XX_DREQ_TST()    ((inr(VS10XX_DREQ_PDS_REG) & _BV(VS10XX_DREQ_BIT)) == _BV(VS10XX_DREQ_BIT))
00249 
00250 #else /* VS10XX_DREQ_BIT */
00251 
00252 #define VS10XX_DREQ_ENA()
00253 #define VS10XX_DREQ_TST()   0
00254 
00255 #endif /* VS10XX_DREQ_BIT */
00256 
00257 /* -------------------------------------------------
00258  * End of port specifications.
00259  */
00260 #endif
00261 
00262 
00263 #define VSREQ_PLAY      0x00000001
00264 #define VSREQ_CANCEL    0x00000002
00265 #define VSREQ_BEEP      0x00000004
00266 
00267 typedef struct _VSDCB {
00268     int dcb_pbstat;     
00269     uint32_t dcb_scmd;  
00270     int dcb_crvol;      
00271     int dcb_srvol;      
00272     int dcb_clvol;      
00273     int dcb_slvol;      
00274     int dcb_ctreb;      
00275     int dcb_streb;      
00276     int dcb_ctfin;      
00277     int dcb_stfin;      
00278     int dcb_cbass;      
00279     int dcb_sbass;      
00280     int dcb_cbfin;      
00281     int dcb_sbfin;      
00282     uint32_t dcb_pbwlo; 
00283     uint32_t dcb_pbwhi; 
00284 } VSDCB;
00285 
00286 static VSDCB dcb;
00287 static unsigned int vs_chip;
00288 
00289 /*
00290  * Interlink not ready yet. Provide some basic SPI routines.
00291  */
00292 
00293 static uint8_t SpiByte(uint8_t val)
00294 {
00295     /* Transmission is started by writing the transmit data. */
00296     outr(SPI0_TDR, val);
00297     /* Wait for receiver data register full. */
00298     while((inr(SPI0_SR) & SPI_RDRF) == 0);
00299     /* Read data. */
00300     val = (uint8_t)inr(SPI0_RDR);
00301 
00302     return val;
00303 }
00304 
00305 static void SciSelect(void)
00306 {
00307     outr(SPI0_CSR0, (24 << SPI_SCBR_LSB) | SPI_NCPHA);
00308     outr(SPI0_MR, SPI_MODFDIS | SPI_MSTR);
00309     outr(PIOA_CODR, _BV(VS10XX_XCS_BIT));
00310 }
00311 
00312 static void SciDeselect(void)
00313 {
00314     outr(PIOA_SODR, _BV(VS10XX_XCS_BIT));
00315 }
00316 
00323 static int VsWaitReady(void)
00324 {
00325     int tmo = 16384;
00326 
00327     do {
00328         if (VS10XX_DREQ_TST()) {
00329             return 0;
00330         }
00331     } while (tmo--);
00332 
00333     return -1;
00334 }
00335 
00336 /*
00337  * \brief Write a specified number of bytes to the VS10XX data interface.
00338  *
00339  * This function will check the DREQ line. Decoder interrupts must have
00340  * been disabled before calling this function.
00341  */
00342 static int VsSdiWrite(CONST uint8_t * data, size_t len)
00343 {
00344     while (len--) {
00345         if (!VS10XX_DREQ_TST() && VsWaitReady()) {
00346             return -1;
00347         }
00348         SpiByte(*data);
00349         data++;
00350     }
00351     return 0;
00352 }
00353 
00354 /*
00355  * \brief Write a specified number of bytes from program space to the
00356  *        VS10XX data interface.
00357  *
00358  * This function is similar to VsSdiWrite() except that the data is
00359  * located in program space.
00360  */
00361 static int VsSdiWrite_P(PGM_P data, size_t len)
00362 {
00363     while (len--) {
00364         if (!VS10XX_DREQ_TST() && VsWaitReady()) {
00365             return -1;
00366         }
00367         SpiByte(PRG_RDB(data));
00368         data++;
00369     }
00370     return 0;
00371 }
00372 
00373 /*
00374  * \brief Write to a decoder register.
00375  *
00376  * Decoder interrupts must have been disabled before calling this function.
00377  */
00378 static void VsRegWrite(ureg_t reg, uint16_t data)
00379 {
00380     VsWaitReady();
00381     SciSelect();
00382     SpiByte(VS_OPCODE_WRITE);
00383     SpiByte((uint8_t) reg);
00384     SpiByte((uint8_t) (data >> 8));
00385     SpiByte((uint8_t) data);
00386     SciDeselect();
00387 }
00388 
00389 /*
00390  * \brief Read from a register.
00391  *
00392  * Decoder interrupts must have been disabled before calling this function.
00393  *
00394  * \return Register contents.
00395  */
00396 static uint16_t VsRegRead(ureg_t reg)
00397 {
00398     uint16_t data;
00399 
00400     VsWaitReady();
00401     SciSelect();
00402     SpiByte(VS_OPCODE_READ);
00403     SpiByte((uint8_t) reg);
00404     data = (uint16_t)SpiByte(0) << 8;
00405     data |= SpiByte(0);
00406     SciDeselect();
00407 
00408     return data;
00409 }
00410 
00421 static int VsBeep(uint8_t fsin, uint8_t ms)
00422 {
00423     static prog_char on[] = { 0x53, 0xEF, 0x6E };
00424     static prog_char off[] = { 0x45, 0x78, 0x69, 0x74 };
00425     static prog_char end[] = { 0x00, 0x00, 0x00, 0x00 };
00426 
00427     VsRegWrite(VS_MODE_REG, VS_SM_TESTS | VS_SM_SDINEW);
00428 
00429     fsin = 56 + (fsin & 7) * 9;
00430     VsSdiWrite_P(on, sizeof(on));
00431     VsSdiWrite(&fsin, 1);
00432     VsSdiWrite_P(end, sizeof(end));
00433     NutDelay(ms);
00434     VsSdiWrite_P(off, sizeof(off));
00435     VsSdiWrite_P(end, sizeof(end));
00436 
00437     VsRegWrite(VS_MODE_REG, VS_SM_SDINEW);
00438 
00439     return 0;
00440 }
00441 
00442 static HANDLE vs_ready;
00443 
00444 static void VsInterrupt(void *arg)
00445 {
00446     NutEventPostFromIrq(&vs_ready);
00447 }
00448 
00449 THREAD(FeederThread, arg)
00450 {
00451     char *bp;
00452     size_t avail;
00453     int filled;
00454     uint8_t crgain;
00455     uint8_t srgain;
00456     uint8_t clgain;
00457     uint8_t slgain;
00458 
00459     NutSleep(500);
00460 
00461     dcb.dcb_slvol = dcb.dcb_clvol = -12;
00462     dcb.dcb_srvol = dcb.dcb_crvol = -12;
00463     srgain = (uint8_t)(-2 * dcb.dcb_srvol);
00464     crgain = 254;
00465     slgain = (uint8_t)(-2 * dcb.dcb_slvol);
00466     clgain = 254;
00467     VsRegWrite(VS_VOL_REG, ((uint16_t)clgain << VS_VOL_LEFT_LSB) | ((uint16_t)crgain << VS_VOL_RIGHT_LSB));
00468 
00469     /* Register the interrupt routine */
00470     while (NutRegisterIrqHandler(&VS10XX_SIGNAL, VsInterrupt, NULL)) {
00471         NutSleep(1000);
00472     }
00473 
00474     /* Rising edge will generate an interrupt. */
00475     NutIrqSetMode(&VS10XX_SIGNAL, NUT_IRQMODE_RISINGEDGE);
00476 
00477     VS10XX_DREQ_ENA();
00478     NutIrqEnable(&VS10XX_SIGNAL);
00479 
00480     for (;;) {
00481         NutEventWait(&vs_ready, 100);
00482         if (!VS10XX_DREQ_TST()) {
00483             continue;
00484         }
00485         if (dcb.dcb_scmd) {
00486             if (dcb.dcb_scmd & VSREQ_CANCEL) {
00487                 NutSegBufReset();
00488             }
00489             if (dcb.dcb_scmd & VSREQ_BEEP) {
00490                 VsBeep(2, 100);
00491             }
00492             dcb.dcb_scmd &= VSREQ_PLAY;
00493         }
00494         if (NutSegBufUsed() < dcb.dcb_pbwlo) {
00495             dcb.dcb_pbstat = CODEC_STATUS_IDLE;
00496             if (crgain != 254) {
00497                 clgain = crgain = 254;
00498                 VsRegWrite(VS_VOL_REG, ((uint16_t)clgain << VS_VOL_LEFT_LSB) | ((uint16_t)crgain << VS_VOL_RIGHT_LSB));
00499             }
00500             while (NutSegBufUsed() <  dcb.dcb_pbwhi) {
00501                 if (dcb.dcb_scmd) {
00502                     if (dcb.dcb_scmd & VSREQ_PLAY) {
00503                         dcb.dcb_pbwhi = dcb.dcb_pbwlo = NutSegBufUsed() / 2;
00504                     }
00505                     break;
00506                 }
00507                 NutSleep(100);
00508             }
00509         }
00510         dcb.dcb_scmd &= ~VSREQ_PLAY;
00511         if (dcb.dcb_scmd) {
00512             continue;
00513         }
00514 
00515         outr(SPI0_CSR0, (12 << SPI_SCBR_LSB) | SPI_NCPHA);
00516         outr(SPI0_MR, SPI_MODFDIS | SPI_MSTR);
00517         if (dcb.dcb_pbstat != CODEC_STATUS_PLAYING) {
00518             outr(PIOB_CODR, _BV(30));
00519             while (!VS10XX_DREQ_TST()) {
00520                 SpiByte(0);
00521             }
00522             outr(PIOB_SODR, _BV(30));
00523         }
00524 
00525         for (;;) {
00526             if (!VS10XX_DREQ_TST()) {
00527                 break;
00528             }
00529             bp = NutSegBufReadRequest(&avail);
00530             if (avail == 0) {
00531                 dcb.dcb_pbstat = CODEC_STATUS_IDLE;
00532                 if (crgain != 254) {
00533                     clgain = crgain = 254;
00534                     VsRegWrite(VS_VOL_REG, ((uint16_t)clgain << VS_VOL_LEFT_LSB) | ((uint16_t)crgain << VS_VOL_RIGHT_LSB));
00535                 }
00536                 break;
00537             }
00538             outr(PIOB_CODR, _BV(30));
00539             for (filled = 0; avail--; filled++, bp++) {
00540                 if (!VS10XX_DREQ_TST()) {
00541                     dcb.dcb_pbstat = CODEC_STATUS_PLAYING;
00542                     break;
00543                 }
00544                 SpiByte(*bp);
00545             }
00546             outr(PIOB_SODR, _BV(30));
00547             NutSegBufReadLast(filled);
00548         }
00549         if (dcb.dcb_clvol != dcb.dcb_slvol || dcb.dcb_crvol != dcb.dcb_srvol) {
00550             srgain = (uint8_t)(-2 * dcb.dcb_srvol);
00551             slgain = (uint8_t)(-2 * dcb.dcb_slvol);
00552 
00553             dcb.dcb_clvol = dcb.dcb_slvol;
00554             dcb.dcb_crvol = dcb.dcb_srvol;
00555         }
00556         else if (srgain != crgain || slgain != clgain) {
00557             int diff = (int)srgain - (int)crgain;
00558 
00559             if (diff > 4) {
00560                 diff = 4;
00561             }
00562             else if (diff < -4) {
00563                 diff = -4;
00564             }
00565             crgain = (uint8_t)((int)crgain + diff);
00566 
00567             diff = (int)slgain - (int)clgain;
00568             if (diff > 4) {
00569                 diff = 4;
00570             }
00571             else if (diff < -4) {
00572                 diff = -4;
00573             }
00574             clgain = (uint8_t)((int)clgain + diff);
00575             VsRegWrite(VS_VOL_REG, ((uint16_t)clgain << VS_VOL_LEFT_LSB) | ((uint16_t)crgain << VS_VOL_RIGHT_LSB));
00576         }
00577     }
00578 }
00579 
00580 static int VsPlayerFlush(void)
00581 {
00582     int tmo = 1000; /* TODO: Configurable timeout. */
00583 
00584     /* Stupid polling for now. */
00585     while(dcb.dcb_pbstat == CODEC_STATUS_PLAYING) {
00586         NutSleep(1);
00587         if (tmo-- <= 0) {
00588             return -1;
00589         }
00590     }
00591     return 0;
00592 }
00593 
00602 static int VsWrite(NUTFILE * fp, CONST void *data, int len)
00603 {
00604     char *buf;
00605     size_t rbytes;
00606 
00607     /* Flush buffer if data pointer is a NULL pointer. */
00608     if (data == NULL || len == 0) {
00609         return VsPlayerFlush();
00610     }
00611     if (len) {
00612         buf = NutSegBufWriteRequest(&rbytes);
00613         if (len > rbytes) {
00614             len = rbytes;
00615         }
00616         if (len) {
00617             memcpy(buf, data, len);
00618         }
00619         NutSegBufWriteLast(len);
00620     }
00621     return len;
00622 }
00623 
00624 #ifdef __HARVARD_ARCH__
00625 
00647 static int VsWrite_P(NUTFILE * nfp, PGM_P buffer, int len)
00648 {
00649     return -1;
00650 }
00651 #endif
00652 
00658 static NUTFILE *VsOpen(NUTDEVICE * dev, CONST char *name, int mode, int acc)
00659 {
00660     NUTFILE *nfp;
00661 
00662     VsRegWrite(VS_MODE_REG, VS_SM_RESET | VS_SM_SDINEW);
00663     NutSleep(2);
00664 
00665     nfp = malloc(sizeof(NUTFILE));
00666     nfp->nf_next = NULL;
00667     nfp->nf_dev = dev;
00668     nfp->nf_fcb = NULL;
00669 
00670     NutSegBufReset();
00671 
00672     return nfp;
00673 }
00674 
00678 static int VsClose(NUTFILE * nfp)
00679 {
00680     int rc = VsPlayerFlush();
00681 
00682     if (nfp) {
00683         free(nfp);
00684     }
00685     return rc;
00686 }
00687 
00688 static int VsPlayBufferInit(uint32_t size)
00689 {
00690     if (dcb.dcb_pbstat != CODEC_STATUS_IDLE) {
00691         return -1;
00692     }
00693     if (NutSegBufInit((size_t)size) == NULL) {
00694         return -1;
00695     }
00696     dcb.dcb_pbwlo = NutSegBufAvailable() / 3;
00697     dcb.dcb_pbwhi = dcb.dcb_pbwlo * 2;
00698 
00699     return 0;
00700 }
00701 
00729 static int VsIOCtl(NUTDEVICE * dev, int req, void *conf)
00730 {
00731     int rc = 0;
00732     uint32_t *lvp = (uint32_t *) conf;
00733     int *ivp = (int *) conf;
00734     int iv = *ivp;
00735 
00736     switch (req) {
00737     case AUDIO_PLAY:
00738         /* Immediately start playing. */
00739         if (dcb.dcb_pbstat != CODEC_STATUS_PLAYING) {
00740             dcb.dcb_scmd |= VSREQ_PLAY;
00741         }
00742         break;
00743     case AUDIO_CANCEL:
00744         /* Immediately stop playing and discard buffer. */
00745         if (dcb.dcb_pbstat == CODEC_STATUS_PLAYING) {
00746             dcb.dcb_scmd |= VSREQ_CANCEL;
00747         }
00748         break;
00749     case AUDIO_GET_STATUS:
00750         *ivp = dcb.dcb_pbstat;
00751         break;
00752     case AUDIO_GET_PLAYGAIN:
00753         if (dcb.dcb_crvol >= dcb.dcb_clvol) {
00754             *ivp = dcb.dcb_crvol;
00755         }
00756         else {
00757             *ivp = dcb.dcb_clvol;
00758         }
00759         break;
00760     case AUDIO_SET_PLAYGAIN:
00761         if (iv > AUDIO_DAC_MAX_GAIN) {
00762             iv = AUDIO_DAC_MAX_GAIN;
00763         }
00764         if (iv < AUDIO_DAC_MIN_GAIN) {
00765             iv = AUDIO_DAC_MIN_GAIN;
00766         }
00767         dcb.dcb_slvol = dcb.dcb_srvol = iv;
00768         break;
00769     case AUDIO_GET_TREB:
00770         *lvp = dcb.dcb_ctreb;
00771         break;
00772     case AUDIO_SET_TREB:
00773         if( iv > AUDIO_DAC_MAX_TREB) {
00774             iv = AUDIO_DAC_MAX_TREB;
00775         }
00776         dcb.dcb_streb = iv;
00777         break;
00778     case AUDIO_GET_TFIN:
00779         *lvp = dcb.dcb_ctfin;
00780         break;
00781     case AUDIO_SET_TFIN:
00782         if( iv > AUDIO_DAC_MAX_TFIN) {
00783             iv = AUDIO_DAC_MAX_TFIN;
00784         }
00785         dcb.dcb_stfin = iv;
00786         break;
00787     case AUDIO_GET_BASS:
00788         *lvp = dcb.dcb_cbass;
00789         break;
00790     case AUDIO_SET_BASS:
00791         if( iv > AUDIO_DAC_MAX_BASS) {
00792             iv = AUDIO_DAC_MAX_BASS;
00793         }
00794         dcb.dcb_sbass = iv;
00795     case AUDIO_GET_BFIN:
00796         *lvp = dcb.dcb_cbass;
00797         break;
00798     case AUDIO_SET_BFIN:
00799         if( iv > AUDIO_DAC_MAX_BFIN) {
00800             iv = AUDIO_DAC_MAX_BFIN;
00801         }
00802         dcb.dcb_sbass = iv;
00803     case AUDIO_GET_PBSIZE:
00804         *lvp = NutSegBufAvailable() + NutSegBufUsed();
00805         break;
00806     case AUDIO_SET_PBSIZE:
00807         rc = VsPlayBufferInit(*lvp);
00808         break;
00809     case AUDIO_GET_PBLEVEL:
00810         *lvp = NutSegBufUsed();
00811         break;
00812     case AUDIO_GET_PBWLOW:
00813         *lvp = dcb.dcb_pbwlo;
00814         break;
00815     case AUDIO_SET_PBWLOW:
00816         dcb.dcb_pbwlo = *lvp;
00817         break;
00818     case AUDIO_GET_PBWHIGH:
00819         *lvp = dcb.dcb_pbwhi;
00820         break;
00821     case AUDIO_SET_PBWHIGH:
00822         dcb.dcb_pbwhi = *lvp;
00823         break;
00824     case AUDIO_BEEP:
00825         dcb.dcb_scmd |= VSREQ_BEEP;
00826         break;
00827 #if 0
00828     case AUDIO_GET_DECINFO:
00829         /* Retrieve decoder information. */
00830         break;
00831     case AUDIO_GET_DECCAPS:
00832         /* Retrieve decoder capabilities. */
00833         break;
00834     case AUDIO_GET_DECFMTS:
00835         /* Retrieve decoder formats. */
00836         break;
00837     case AUDIO_SET_DECFMTS:
00838         /* Enable or disable specific decoder formats. */
00839         break;
00840     case AUDIO_GET_CODINFO:
00841         /* Retrieve encoder information. */
00842         break;
00843     case AUDIO_GET_CODCAPS:
00844         /* Retrieve encoder capabilities. */
00845         break;
00846     case AUDIO_GET_CODFMTS:
00847         /* Retrieve encoder formats. */
00848         break;
00849     case AUDIO_SET_CODFMTS:
00850         /* Enable or disable specific encoder formats. */
00851         break;
00852     case AUDIO_GET_MIDINFO:
00853         /* Retrieve midi information. */
00854         break;
00855     case AUDIO_GET_MIDCAPS:
00856         /* Retrieve midi capabilities. */
00857         break;
00858 #endif
00859     default:
00860         rc = -1;
00861         break;
00862     }
00863     return rc;
00864 }
00865 
00866 /*
00867  * Called via dev_init pointer when the device is registered.
00868  */
00869 static int VsInit(NUTDEVICE * dev)
00870 {
00871     uint16_t mode;
00872 
00873     /* Release reset line and read the status register. */
00874     outr(PIOB_PER, _BV(VS_XRESET_BIT));
00875     outr(PIOB_SODR, _BV(VS_XRESET_BIT));
00876     outr(PIOB_OER, _BV(VS_XRESET_BIT));
00877     NutSleep(3);
00878 
00879     VsRegRead(VS_MODE_REG); /* Dummy read. TODO: Why? */
00880     mode = VsRegRead(VS_MODE_REG);
00881     if ((mode & VS_SM_SDINEW) == 0) {
00882         VsRegWrite(VS_MODE_REG, VS_SM_RESET | VS_SM_SDINEW);
00883         NutSleep(2);
00884     }
00885     vs_chip = (VsRegRead(VS_STATUS_REG) & VS_SS_VER) >> VS_SS_VER_LSB;
00886 #if VS10XX_FREQ < 20000000UL && defined(VS_CF_DOUBLER)
00887     VsRegWrite(VS_CLOCKF_REG, (uint16_t)(VS_CF_DOUBLER | (VS10XX_FREQ / 2000UL)));
00888 #else
00889     VsRegWrite(VS_CLOCKF_REG, (uint16_t)(VS10XX_FREQ / 2000UL));
00890 #endif
00891 #if defined(VS_INT_FCTLH_REG)
00892     if (vs_chip == 0) {
00893         /* Force frequency change (see datasheet). */
00894         VsRegWrite(VS_INT_FCTLH_REG, 0x8008);
00895     }
00896 #endif
00897 
00898     if (VsPlayBufferInit(0)) {
00899         return -1;
00900     }
00901     if (NutThreadCreate("vsdeco", FeederThread, NULL, 1024) == 0) {
00902         return -1;
00903     }
00904     return 0;
00905 }
00906 
00917 NUTDEVICE devVsCodec = {
00918     0,              /* Pointer to next device, dev_next. */
00919     {'a', 'u', 'd', 'i', 'o', '0', 0, 0, 0},    /* Unique device name, dev_name. */
00920     IFTYP_CHAR,     /* Type of device, dev_type. */
00921     0,              /* Base address, dev_base (not used). */
00922     0,              /* First interrupt number, dev_irq (not used). */
00923     0,              /* Interface control block, dev_icb (not used). */
00924     &dcb,           /* Driver control block, dev_dcb. */
00925     VsInit,         /* Driver initialization routine, dev_init. */
00926     VsIOCtl,        /* Driver specific control function, dev_ioctl. */
00927     NULL,           /* Read from device, dev_read. */
00928     VsWrite,        /* Write to device, dev_write. */
00929 #ifdef __HARVARD_ARCH__
00930     VsWrite_P,      /* Write data from program space to device, dev_write_P. */
00931 #endif
00932     VsOpen,         /* Open a device or file, dev_open. */
00933     VsClose,        /* Close a device or file, dev_close. */
00934     NULL            /* Request file size, dev_size. */
00935 };
00936