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: vscodec.c,v $
00040  * Revision 1.4  2008/09/18 09:51:58  haraldkipp
00041  * Use the correct PORT macros.
00042  *
00043  * Revision 1.3  2008/08/11 06:59:42  haraldkipp
00044  * BSD types replaced by stdint types (feature request #1282721).
00045  *
00046  * Revision 1.2  2008/04/01 10:15:27  haraldkipp
00047  * VS10xx ioctl() returned -1 on success. Fixed.
00048  *
00049  * Revision 1.1  2008/02/15 16:45:41  haraldkipp
00050  * First release.
00051  *
00052  * Revision 1.1  2007/04/12 08:59:55  haraldkipp
00053  * VS10XX decoder support added.
00054  *
00055  */
00056 
00057 #include <cfg/arch/avr.h>
00058 #include <cfg/audio.h>
00059 
00060 #include <sys/atom.h>
00061 #include <sys/event.h>
00062 #include <sys/timer.h>
00063 #include <sys/heap.h>
00064 
00065 #include <dev/irqreg.h>
00066 #include <dev/vscodec.h>
00067 
00068 #include <sys/bankmem.h>
00069 
00070 #include <stdlib.h>
00071 #include <stddef.h>
00072 #include <string.h>
00073 
00078 
00079 #if !defined(AUDIO_VS1001K) && !defined(AUDIO_VS1011E) && !defined(AUDIO_VS1002D) && !defined(AUDIO_VS1003B) && !defined(AUDIO_VS1033C) && !defined(AUDIO_VS1053C)
00080 #define AUDIO_VS1001K
00081 #endif
00082 
00083 #ifndef VS10XX_FREQ
00084 
00085 #define VS10XX_FREQ             12288000UL
00086 #endif
00087 
00088 #ifndef VS10XX_HWRST_DURATION
00089 
00090 #define VS10XX_HWRST_DURATION   1
00091 #endif
00092 
00093 #ifndef VS10XX_HWRST_RECOVER
00094 
00095 #define VS10XX_HWRST_RECOVER    4
00096 #endif
00097 
00098 #ifndef VS10XX_SWRST_RECOVER
00099 
00100 #define VS10XX_SWRST_RECOVER    2
00101 #endif
00102 
00103 #ifndef VS10XX_SCI_MODE
00104 #define VS10XX_SCI_MODE         0
00105 #endif
00106 
00107 #ifndef VS10XX_SCI_RATE
00108 #define VS10XX_SCI_RATE         (VS10XX_FREQ / 6)
00109 #endif
00110 
00111 #ifndef VS10XX_SDI_MODE
00112 #define VS10XX_SDI_MODE         0
00113 #endif
00114 
00115 #ifndef VS10XX_SDI_RATE
00116 #define VS10XX_SDI_RATE         (VS10XX_FREQ / 6)
00117 #endif
00118 
00119 
00120 /* -------------------------------------------------
00121  * AT91 port specifications.
00122  */
00123 #if defined (MCU_AT91R40008) || defined (MCU_AT91SAM7X256) || defined (MCU_AT91SAM7SE512) || defined (MCU_AT91SAM9260)
00124 
00125 #if defined(ELEKTOR_IR1)
00126 
00127 #define VS_XRESET_BIT           31 /* PB31 */
00128 #define VS10XX_XCS_BIT          PA31_SPI0_NPCS1_A
00129 #define VS10XX_XCS_PORT       PIOA_ID
00130 #define VS10XX_XDCS_BIT         PB30_SPI0_NPCS2_A
00131 #define VS10XX_XDCS_PORT      PIOB_ID
00132 #define VS10XX_DREQ_BIT         PA30_IRQ1_A
00133 #define VS10XX_DREQ_PIO_ID      PIOA_ID
00134 #define VS10XX_SIGNAL           sig_INTERRUPT1
00135 
00136 #endif /* ELEKTOR_IR1 */
00137 
00138 #if defined(VS10XX_XCS_BIT)
00139 
00140 #if !defined(VS10XX_XCS_PORT)
00141 #define VS10XX_XCS_PE_REG        PIO_PER
00142 #define VS10XX_XCS_OE_REG        PIO_OER
00143 #define VS10XX_XCS_COD_REG       PIO_CODR
00144 #define VS10XX_XCS_SOD_REG       PIO_SODR
00145 #elif VS10XX_XCS_PORT == PIOA_ID
00146 #define VS10XX_XCS_PE_REG        PIOA_PER
00147 #define VS10XX_XCS_OE_REG        PIOA_OER
00148 #define VS10XX_XCS_COD_REG       PIOA_CODR
00149 #define VS10XX_XCS_SOD_REG       PIOA_SODR
00150 #elif VS10XX_XCS_PORT == PIOB_ID
00151 #define VS10XX_XCS_PE_REG        PIOB_PER
00152 #define VS10XX_XCS_OE_REG        PIOB_OER
00153 #define VS10XX_XCS_COD_REG       PIOB_CODR
00154 #define VS10XX_XCS_SOD_REG       PIOB_SODR
00155 #elif VS10XX_XCS_PORT == PIOC_ID
00156 #define VS10XX_XCS_PE_REG        PIOC_PER
00157 #define VS10XX_XCS_OE_REG        PIOC_OER
00158 #define VS10XX_XCS_COD_REG       PIOC_CODR
00159 #define VS10XX_XCS_SOD_REG       PIOC_SODR
00160 #endif
00161 #define VS10XX_XCS_ENA() \
00162     outr(VS10XX_XCS_PE_REG, _BV(VS10XX_XCS_BIT)); \
00163     outr(VS10XX_XCS_OE_REG, _BV(VS10XX_XCS_BIT))
00164 #define VS10XX_XCS_CLR()   outr(VS10XX_XCS_COD_REG, _BV(VS10XX_XCS_BIT))
00165 #define VS10XX_XCS_SET()   outr(VS10XX_XCS_SOD_REG, _BV(VS10XX_XCS_BIT))
00166 
00167 #else /* VS10XX_XCS_BIT */
00168 
00169 #define VS10XX_XCS_ENA()
00170 #define VS10XX_XCS_CLR()
00171 #define VS10XX_XCS_SET()
00172 
00173 #endif /* VS10XX_XCS_BIT */
00174 
00175 #if defined(VS10XX_XDCS_BIT)
00176 
00177 #if !defined(VS10XX_XDCS_PORT)
00178 #define VS10XX_XDCS_PE_REG        PIO_PER
00179 #define VS10XX_XDCS_OE_REG        PIO_OER
00180 #define VS10XX_XDCS_COD_REG       PIO_CODR
00181 #define VS10XX_XDCS_SOD_REG       PIO_SODR
00182 #elif VS10XX_XDCS_PORT == PIOA_ID
00183 #define VS10XX_XDCS_PE_REG        PIOA_PER
00184 #define VS10XX_XDCS_OE_REG        PIOA_OER
00185 #define VS10XX_XDCS_COD_REG       PIOA_CODR
00186 #define VS10XX_XDCS_SOD_REG       PIOA_SODR
00187 #elif VS10XX_XDCS_PORT == PIOB_ID
00188 #define VS10XX_XDCS_PE_REG        PIOB_PER
00189 #define VS10XX_XDCS_OE_REG        PIOB_OER
00190 #define VS10XX_XDCS_COD_REG       PIOB_CODR
00191 #define VS10XX_XDCS_SOD_REG       PIOB_SODR
00192 #elif VS10XX_XDCS_PORT == PIOC_ID
00193 #define VS10XX_XDCS_PE_REG        PIOC_PER
00194 #define VS10XX_XDCS_OE_REG        PIOC_OER
00195 #define VS10XX_XDCS_COD_REG       PIOC_CODR
00196 #define VS10XX_XDCS_SOD_REG       PIOC_SODR
00197 #endif
00198 #define VS10XX_XDCS_ENA() \
00199     outr(VS10XX_XDCS_PE_REG, _BV(VS10XX_XDCS_BIT)); \
00200     outr(VS10XX_XDCS_OE_REG, _BV(VS10XX_XDCS_BIT))
00201 #define VS10XX_XDCS_CLR()   outr(VS10XX_XDCS_COD_REG, _BV(VS10XX_XDCS_BIT))
00202 #define VS10XX_XDCS_SET()   outr(VS10XX_XDCS_SOD_REG, _BV(VS10XX_XDCS_BIT))
00203 
00204 #else /* VS10XX_XDCS_BIT */
00205 
00206 #define VS10XX_XDCS_ENA()
00207 #define VS10XX_XDCS_CLR()
00208 #define VS10XX_XDCS_SET()
00209 
00210 #endif /* VS10XX_XDCS_BIT */
00211 
00212 #if defined(VS10XX_DREQ_BIT)
00213 
00214 #if !defined(VS10XX_DREQ_PIO_ID)
00215 #define VS10XX_DREQ_PD_REG       PIO_PDR
00216 #define VS10XX_DREQ_OD_REG       PIO_ODR
00217 #define VS10XX_DREQ_PDS_REG      PIO_PDSR
00218 #elif VS10XX_DREQ_PIO_ID == PIOA_ID
00219 #define VS10XX_DREQ_PD_REG       PIOA_PDR
00220 #define VS10XX_DREQ_OD_REG       PIOA_ODR
00221 #define VS10XX_DREQ_PDS_REG      PIOA_PDSR
00222 #elif VS10XX_DREQ_PIO_ID == PIOB_ID
00223 #define VS10XX_DREQ_PD_REG       PIOB_PDR
00224 #define VS10XX_DREQ_OD_REG       PIOB_ODR
00225 #define VS10XX_DREQ_PDS_REG      PIOB_PDSR
00226 #elif VS10XX_DREQ_PIO_ID == PIOC_ID
00227 #define VS10XX_DREQ_PD_REG       PIOC_PDR
00228 #define VS10XX_DREQ_OD_REG       PIOC_ODR
00229 #define VS10XX_DREQ_PDS_REG      PIOC_PDSR
00230 #endif
00231 
00232 #define VS10XX_DREQ_ENA() \
00233     outr(VS10XX_DREQ_PD_REG, _BV(VS10XX_DREQ_BIT)); \
00234     outr(VS10XX_DREQ_OD_REG, _BV(VS10XX_DREQ_BIT))
00235 #define VS10XX_DREQ_TST()    ((inr(VS10XX_DREQ_PDS_REG) & _BV(VS10XX_DREQ_BIT)) == _BV(VS10XX_DREQ_BIT))
00236 
00237 #else /* VS10XX_DREQ_BIT */
00238 
00239 #define VS10XX_DREQ_ENA()
00240 #define VS10XX_DREQ_TST()   0
00241 
00242 #endif /* VS10XX_DREQ_BIT */
00243 
00244 /* -------------------------------------------------
00245  * End of port specifications.
00246  */
00247 #endif
00248 
00249 
00250 #define VSREQ_PLAY      0x00000001
00251 #define VSREQ_CANCEL    0x00000002
00252 #define VSREQ_BEEP      0x00000004
00253 
00254 typedef struct _VSDCB {
00255     int dcb_pbstat;     
00256     uint32_t dcb_scmd;    
00257     int dcb_crvol;      
00258     int dcb_srvol;      
00259     int dcb_clvol;      
00260     int dcb_slvol;      
00261     uint32_t dcb_pbwlo;   
00262     uint32_t dcb_pbwhi;   
00263 } VSDCB;
00264 
00265 static VSDCB dcb;
00266 static u_int vs_chip;
00267 
00268 /*
00269  * Interlink not ready yet. Provide some basic SPI routines.
00270  */
00271 
00272 static uint8_t SpiByte(uint8_t val)
00273 {
00274     /* Transmission is started by writing the transmit data. */
00275     outr(SPI0_TDR, val);
00276     /* Wait for receiver data register full. */
00277     while((inr(SPI0_SR) & SPI_RDRF) == 0);
00278     /* Read data. */
00279     val = (uint8_t)inr(SPI0_RDR);
00280 
00281     return val;
00282 }
00283 
00284 static void SciSelect(void)
00285 {
00286     outr(SPI0_CSR0, (24 << SPI_SCBR_LSB) | SPI_NCPHA);
00287     outr(SPI0_MR, SPI_MODFDIS | SPI_MSTR);
00288     outr(PIOA_CODR, _BV(VS10XX_XCS_BIT));
00289 }
00290 
00291 static void SciDeselect(void)
00292 {
00293     outr(PIOA_SODR, _BV(VS10XX_XCS_BIT));
00294 }
00295 
00302 static int VsWaitReady(void)
00303 {
00304     int tmo = 16384;
00305 
00306     do {
00307         if (VS10XX_DREQ_TST()) {
00308             return 0;
00309         }
00310     } while (tmo--);
00311 
00312     return -1;
00313 }
00314 
00315 /*
00316  * \brief Write a specified number of bytes to the VS10XX data interface.
00317  *
00318  * This function will check the DREQ line. Decoder interrupts must have 
00319  * been disabled before calling this function.
00320  */
00321 static int VsSdiWrite(CONST uint8_t * data, size_t len)
00322 {
00323     while (len--) {
00324         if (!VS10XX_DREQ_TST() && VsWaitReady()) {
00325             return -1;
00326         }
00327         SpiByte(*data);
00328         data++;
00329     }
00330     return 0;
00331 }
00332 
00333 /*
00334  * \brief Write a specified number of bytes from program space to the 
00335  *        VS10XX data interface.
00336  *
00337  * This function is similar to VsSdiWrite() except that the data is 
00338  * located in program space.
00339  */
00340 static int VsSdiWrite_P(PGM_P data, size_t len)
00341 {
00342     while (len--) {
00343         if (!VS10XX_DREQ_TST() && VsWaitReady()) {
00344             return -1;
00345         }
00346         SpiByte(PRG_RDB(data));
00347         data++;
00348     }
00349     return 0;
00350 }
00351 
00352 /*
00353  * \brief Write to a decoder register.
00354  *
00355  * Decoder interrupts must have been disabled before calling this function.
00356  */
00357 static void VsRegWrite(ureg_t reg, uint16_t data)
00358 {
00359     VsWaitReady();
00360     SciSelect();
00361     SpiByte(VS_OPCODE_WRITE);
00362     SpiByte((uint8_t) reg);
00363     SpiByte((uint8_t) (data >> 8));
00364     SpiByte((uint8_t) data);
00365     SciDeselect();
00366 }
00367 
00368 /*
00369  * \brief Read from a register.
00370  *
00371  * Decoder interrupts must have been disabled before calling this function.
00372  * 
00373  * \return Register contents.
00374  */
00375 static uint16_t VsRegRead(ureg_t reg)
00376 {
00377     uint16_t data;
00378 
00379     VsWaitReady();
00380     SciSelect();
00381     SpiByte(VS_OPCODE_READ);
00382     SpiByte((uint8_t) reg);
00383     data = (uint16_t)SpiByte(0) << 8;
00384     data |= SpiByte(0);
00385     SciDeselect();
00386 
00387     return data;
00388 }
00389 
00400 static int VsBeep(uint8_t fsin, uint8_t ms)
00401 {
00402     static prog_char on[] = { 0x53, 0xEF, 0x6E };
00403     static prog_char off[] = { 0x45, 0x78, 0x69, 0x74 };
00404     static prog_char end[] = { 0x00, 0x00, 0x00, 0x00 };
00405 
00406     VsRegWrite(VS_MODE_REG, VS_SM_TESTS | VS_SM_SDINEW);
00407 
00408     fsin = 56 + (fsin & 7) * 9;
00409     VsSdiWrite_P(on, sizeof(on));
00410     VsSdiWrite(&fsin, 1);
00411     VsSdiWrite_P(end, sizeof(end));
00412     NutDelay(ms);
00413     VsSdiWrite_P(off, sizeof(off));
00414     VsSdiWrite_P(end, sizeof(end));
00415 
00416     VsRegWrite(VS_MODE_REG, VS_SM_SDINEW);
00417 
00418     return 0;
00419 }
00420 
00421 static HANDLE vs_ready;
00422 
00423 static void VsInterrupt(void *arg)
00424 {
00425     NutEventPostFromIrq(&vs_ready);
00426 }
00427 
00428 THREAD(FeederThread, arg)
00429 {
00430     char *bp;
00431     size_t avail;
00432     int filled;
00433     uint8_t crgain;
00434     uint8_t srgain;
00435     uint8_t clgain;
00436     uint8_t slgain;
00437 
00438     NutSleep(500);
00439 
00440     dcb.dcb_slvol = dcb.dcb_clvol = -12;
00441     dcb.dcb_srvol = dcb.dcb_crvol = -12;
00442     srgain = (uint8_t)(-2 * dcb.dcb_srvol);
00443     crgain = 254;
00444     slgain = (uint8_t)(-2 * dcb.dcb_slvol);
00445     clgain = 254;
00446     VsRegWrite(VS_VOL_REG, ((uint16_t)clgain << VS_VOL_LEFT_LSB) | ((uint16_t)crgain << VS_VOL_RIGHT_LSB));
00447 
00448     /* Register the interrupt routine */
00449     while (NutRegisterIrqHandler(&VS10XX_SIGNAL, VsInterrupt, NULL)) {
00450         NutSleep(1000);
00451     }
00452 
00453     /* Rising edge will generate an interrupt. */
00454     NutIrqSetMode(&VS10XX_SIGNAL, NUT_IRQMODE_RISINGEDGE);
00455 
00456     VS10XX_DREQ_ENA();
00457     NutIrqEnable(&VS10XX_SIGNAL);
00458 
00459     for (;;) {
00460         NutEventWait(&vs_ready, 100);
00461         if (!VS10XX_DREQ_TST()) {
00462             continue;
00463         }
00464         if (dcb.dcb_scmd) {
00465             if (dcb.dcb_scmd & VSREQ_CANCEL) {
00466                 NutSegBufReset();
00467             }
00468             if (dcb.dcb_scmd & VSREQ_BEEP) {
00469                 VsBeep(2, 100);
00470             }
00471             dcb.dcb_scmd &= VSREQ_PLAY;
00472         }
00473         if (NutSegBufUsed() < dcb.dcb_pbwlo) {
00474             dcb.dcb_pbstat = CODEC_STATUS_IDLE;
00475             if (crgain != 254) {
00476                 clgain = crgain = 254;
00477                 VsRegWrite(VS_VOL_REG, ((uint16_t)clgain << VS_VOL_LEFT_LSB) | ((uint16_t)crgain << VS_VOL_RIGHT_LSB));
00478             }
00479             while (NutSegBufUsed() <  dcb.dcb_pbwhi) {
00480                 if (dcb.dcb_scmd) {
00481                     if (dcb.dcb_scmd & VSREQ_PLAY) {
00482                         dcb.dcb_pbwhi = dcb.dcb_pbwlo = NutSegBufUsed() / 2;
00483                     }
00484                     break;
00485                 }
00486                 NutSleep(100);
00487             }
00488         }
00489         dcb.dcb_scmd &= ~VSREQ_PLAY;
00490         if (dcb.dcb_scmd) {
00491             continue;
00492         }
00493 
00494         outr(SPI0_CSR0, (12 << SPI_SCBR_LSB) | SPI_NCPHA);
00495         outr(SPI0_MR, SPI_MODFDIS | SPI_MSTR);
00496         if (dcb.dcb_pbstat != CODEC_STATUS_PLAYING) {
00497             outr(PIOB_CODR, _BV(30));
00498             while (!VS10XX_DREQ_TST()) {
00499                 SpiByte(0);
00500             }
00501             outr(PIOB_SODR, _BV(30));
00502         }
00503 
00504         for (;;) {
00505             if (!VS10XX_DREQ_TST()) {
00506                 break;
00507             }
00508             bp = NutSegBufReadRequest(&avail);
00509             if (avail == 0) {
00510                 dcb.dcb_pbstat = CODEC_STATUS_IDLE;
00511                 if (crgain != 254) {
00512                     clgain = crgain = 254;
00513                     VsRegWrite(VS_VOL_REG, ((uint16_t)clgain << VS_VOL_LEFT_LSB) | ((uint16_t)crgain << VS_VOL_RIGHT_LSB));
00514                 }
00515                 break;
00516             }
00517             outr(PIOB_CODR, _BV(30));
00518             for (filled = 0; avail--; filled++, bp++) {
00519                 if (!VS10XX_DREQ_TST()) {
00520                     dcb.dcb_pbstat = CODEC_STATUS_PLAYING;
00521                     break;
00522                 }
00523                 SpiByte(*bp);
00524             }
00525             outr(PIOB_SODR, _BV(30));
00526             NutSegBufReadLast(filled);
00527         }
00528         if (dcb.dcb_clvol != dcb.dcb_slvol || dcb.dcb_crvol != dcb.dcb_srvol) {
00529             srgain = (uint8_t)(-2 * dcb.dcb_srvol);
00530             slgain = (uint8_t)(-2 * dcb.dcb_slvol);
00531 
00532             dcb.dcb_clvol = dcb.dcb_slvol;
00533             dcb.dcb_crvol = dcb.dcb_srvol;
00534         }
00535         else if (srgain != crgain || slgain != clgain) {
00536             int diff = (int)srgain - (int)crgain;
00537 
00538             if (diff > 4) {
00539                 diff = 4;
00540             }
00541             else if (diff < -4) {
00542                 diff = -4;
00543             }
00544             crgain = (uint8_t)((int)crgain + diff);
00545 
00546             diff = (int)slgain - (int)clgain;
00547             if (diff > 4) {
00548                 diff = 4;
00549             }
00550             else if (diff < -4) {
00551                 diff = -4;
00552             }
00553             clgain = (uint8_t)((int)clgain + diff);
00554             VsRegWrite(VS_VOL_REG, ((uint16_t)clgain << VS_VOL_LEFT_LSB) | ((uint16_t)crgain << VS_VOL_RIGHT_LSB));
00555         }
00556     }
00557 }
00558 
00559 static int VsPlayerFlush(void)
00560 {
00561     int tmo = 1000; /* TODO: Configurable timeout. */
00562 
00563     /* Stupid polling for now. */
00564     while(dcb.dcb_pbstat == CODEC_STATUS_PLAYING) {
00565         NutSleep(1);
00566         if (tmo-- <= 0) {
00567             return -1;
00568         }
00569     }
00570     return 0;
00571 }
00572 
00581 static int VsWrite(NUTFILE * fp, CONST void *data, int len)
00582 {
00583     char *buf;
00584     size_t rbytes;
00585 
00586     /* Flush buffer if data pointer is a NULL pointer. */
00587     if (data == NULL || len == 0) {
00588         return VsPlayerFlush();
00589     }
00590     if (len) {
00591         buf = NutSegBufWriteRequest(&rbytes);
00592         if (len > rbytes) {
00593             len = rbytes;
00594         }
00595         if (len) {
00596             memcpy(buf, data, len);
00597         }
00598         NutSegBufWriteLast(len);
00599     }
00600     return len;
00601 }
00602 
00603 #ifdef __HARVARD_ARCH__
00604 
00626 static int VsWrite_P(NUTFILE * nfp, PGM_P buffer, int len)
00627 {
00628     return -1;
00629 }
00630 #endif
00631 
00637 static NUTFILE *VsOpen(NUTDEVICE * dev, CONST char *name, int mode, int acc)
00638 {
00639     NUTFILE *nfp;
00640 
00641     VsRegWrite(VS_MODE_REG, VS_SM_RESET | VS_SM_SDINEW);
00642     NutSleep(2);
00643 
00644     nfp = malloc(sizeof(NUTFILE));
00645     nfp->nf_next = NULL;
00646     nfp->nf_dev = dev;
00647     nfp->nf_fcb = NULL;
00648 
00649     NutSegBufReset();
00650 
00651     return nfp;
00652 }
00653 
00657 static int VsClose(NUTFILE * nfp)
00658 {
00659     int rc = VsPlayerFlush();
00660 
00661     if (nfp) {
00662         free(nfp);
00663     }
00664     return rc;
00665 }
00666 
00667 static int VsPlayBufferInit(uint32_t size)
00668 {
00669     if (dcb.dcb_pbstat != CODEC_STATUS_IDLE) {
00670         return -1;
00671     }
00672     if (NutSegBufInit((size_t)size) == NULL) {
00673         return -1;
00674     }
00675     dcb.dcb_pbwlo = NutSegBufAvailable() / 3;
00676     dcb.dcb_pbwhi = dcb.dcb_pbwlo * 2;
00677 
00678     return 0;
00679 }
00680 
00700 static int VsIOCtl(NUTDEVICE * dev, int req, void *conf)
00701 {
00702     int rc = 0;
00703     uint32_t *lvp = (uint32_t *) conf;
00704     int *ivp = (int *) conf;
00705     int iv = *ivp;
00706 
00707     switch (req) {
00708     case AUDIO_PLAY:
00709         /* Immediately start playing. */
00710         if (dcb.dcb_pbstat != CODEC_STATUS_PLAYING) {
00711             dcb.dcb_scmd |= VSREQ_PLAY;
00712         }
00713         break;
00714     case AUDIO_CANCEL:
00715         /* Immediately stop playing and discard buffer. */
00716         if (dcb.dcb_pbstat == CODEC_STATUS_PLAYING) {
00717             dcb.dcb_scmd |= VSREQ_CANCEL;
00718         }
00719         break;
00720     case AUDIO_GET_STATUS:
00721         *ivp = dcb.dcb_pbstat;
00722         break;
00723     case AUDIO_GET_PLAYGAIN:
00724         if (dcb.dcb_crvol >= dcb.dcb_clvol) {
00725             *ivp = dcb.dcb_crvol;
00726         }
00727         else {
00728             *ivp = dcb.dcb_clvol;
00729         }
00730         break;
00731     case AUDIO_SET_PLAYGAIN:
00732         if (iv > AUDIO_DAC_MAX_GAIN) {
00733             iv = AUDIO_DAC_MAX_GAIN;
00734         }
00735         if (iv < AUDIO_DAC_MIN_GAIN) {
00736             iv = AUDIO_DAC_MIN_GAIN;
00737         }
00738         dcb.dcb_slvol = dcb.dcb_srvol = iv;
00739         break;
00740     case AUDIO_GET_PBSIZE:
00741         *lvp = NutSegBufAvailable() + NutSegBufUsed();
00742         break;
00743     case AUDIO_SET_PBSIZE:
00744         rc = VsPlayBufferInit(*lvp);
00745         break;
00746     case AUDIO_GET_PBLEVEL:
00747         *lvp = NutSegBufUsed();
00748         break;
00749     case AUDIO_GET_PBWLOW:
00750         *lvp = dcb.dcb_pbwlo;
00751         break;
00752     case AUDIO_SET_PBWLOW:
00753         dcb.dcb_pbwlo = *lvp;
00754         break;
00755     case AUDIO_GET_PBWHIGH:
00756         *lvp = dcb.dcb_pbwhi;
00757         break;
00758     case AUDIO_SET_PBWHIGH:
00759         dcb.dcb_pbwhi = *lvp;
00760         break;
00761     case AUDIO_BEEP:
00762         dcb.dcb_scmd |= VSREQ_BEEP;
00763         break;
00764 #if 0
00765     case AUDIO_GET_DECINFO:
00766         /* Retrieve decoder information. */
00767         break;
00768     case AUDIO_GET_DECCAPS:
00769         /* Retrieve decoder capabilities. */
00770         break;
00771     case AUDIO_GET_DECFMTS:
00772         /* Retrieve decoder formats. */
00773         break;
00774     case AUDIO_SET_DECFMTS:
00775         /* Enable or disable specific decoder formats. */
00776         break;
00777     case AUDIO_GET_CODINFO:
00778         /* Retrieve encoder information. */
00779         break;
00780     case AUDIO_GET_CODCAPS:
00781         /* Retrieve encoder capabilities. */
00782         break;
00783     case AUDIO_GET_CODFMTS:
00784         /* Retrieve encoder formats. */
00785         break;
00786     case AUDIO_SET_CODFMTS:
00787         /* Enable or disable specific encoder formats. */
00788         break;
00789     case AUDIO_GET_MIDINFO:
00790         /* Retrieve midi information. */
00791         break;
00792     case AUDIO_GET_MIDCAPS:
00793         /* Retrieve midi capabilities. */
00794         break;
00795 #endif
00796     default:
00797         rc = -1;
00798         break;
00799     }
00800     return rc;
00801 }
00802 
00803 /*
00804  * Called via dev_init pointer when the device is registered.
00805  */
00806 static int VsInit(NUTDEVICE * dev)
00807 {
00808     uint16_t mode;
00809 
00810     /* Release reset line and read the status register. */
00811     outr(PIOB_PER, _BV(VS_XRESET_BIT));
00812     outr(PIOB_SODR, _BV(VS_XRESET_BIT));
00813     outr(PIOB_OER, _BV(VS_XRESET_BIT));
00814     NutSleep(3);
00815 
00816     VsRegRead(VS_MODE_REG); /* Dummy read. TODO: Why? */
00817     mode = VsRegRead(VS_MODE_REG);
00818     if ((mode & VS_SM_SDINEW) == 0) {
00819         VsRegWrite(VS_MODE_REG, VS_SM_RESET | VS_SM_SDINEW);
00820         NutSleep(2);
00821     }
00822     vs_chip = (VsRegRead(VS_STATUS_REG) & VS_SS_VER) >> VS_SS_VER_LSB;
00823 #if VS10XX_FREQ < 20000000UL
00824     VsRegWrite(VS_CLOCKF_REG, (uint16_t)(VS_CF_DOUBLER | (VS10XX_FREQ / 2000UL)));
00825 #else
00826     VsRegWrite(VS_CLOCKF_REG, (uint16_t)(VS10XX_FREQ / 2000UL));
00827 #endif
00828     if (vs_chip == 0) {
00829         /* Force frequency change (see datasheet). */
00830         VsRegWrite(VS_INT_FCTLH_REG, 0x8008);
00831     }
00832 
00833     if (VsPlayBufferInit(0)) {
00834         return -1;
00835     }
00836     if (NutThreadCreate("vsdeco", FeederThread, NULL, 1024) == 0) {
00837         return -1;
00838     }
00839     return 0;
00840 }
00841 
00852 NUTDEVICE devVsCodec = {
00853     0,              /* Pointer to next device, dev_next. */
00854     {'a', 'u', 'd', 'i', 'o', '0', 0, 0, 0},    /* Unique device name, dev_name. */
00855     IFTYP_CHAR,     /* Type of device, dev_type. */
00856     0,              /* Base address, dev_base (not used). */
00857     0,              /* First interrupt number, dev_irq (not used). */
00858     0,              /* Interface control block, dev_icb (not used). */
00859     &dcb,           /* Driver control block, dev_dcb. */
00860     VsInit,         /* Driver initialization routine, dev_init. */
00861     VsIOCtl,        /* Driver specific control function, dev_ioctl. */
00862     NULL,           /* Read from device, dev_read. */
00863     VsWrite,        /* Write to device, dev_write. */
00864 #ifdef __HARVARD_ARCH__
00865     VsWrite_P,      /* Write data from program space to device, dev_write_P. */
00866 #endif
00867     VsOpen,         /* Open a device or file, dev_open. */
00868     VsClose,        /* Close a device or file, dev_close. */
00869     NULL            /* Request file size, dev_size. */
00870 };
00871 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/