| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937 |
- /*
- * Copyright (C) 2008 by egnite GmbH. All rights reserved.
- * Copyright (C) 2001-2007 by egnite Software GmbH. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holders nor the names of
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * For additional information see http://www.ethernut.de/
- * -
- *
- * This software has been inspired by all the valuable work done by
- * Jesper Hansen and Pavel Chromy. Many thanks for all their help.
- */
- /*
- * $Log$
- * Revision 1.8 2009/02/13 14:52:05 haraldkipp
- * Include memdebug.h for heap management debugging support.
- *
- * Revision 1.7 2009/01/30 08:59:30 haraldkipp
- * Make sure, that used registers are defined.
- *
- * Revision 1.6 2009/01/17 11:26:46 haraldkipp
- * Getting rid of two remaining BSD types in favor of stdint.
- * Replaced 'u_int' by 'unsinged int' and 'uptr_t' by 'uintptr_t'.
- *
- * Revision 1.5 2008/10/23 08:54:07 haraldkipp
- * Include the correct header file.
- *
- * Revision 1.4 2008/09/18 09:51:58 haraldkipp
- * Use the correct PORT macros.
- *
- * Revision 1.3 2008/08/11 06:59:42 haraldkipp
- * BSD types replaced by stdint types (feature request #1282721).
- *
- * Revision 1.2 2008/04/01 10:15:27 haraldkipp
- * VS10xx ioctl() returned -1 on success. Fixed.
- *
- * Revision 1.1 2008/02/15 16:45:41 haraldkipp
- * First release.
- *
- * Revision 1.1 2007/04/12 08:59:55 haraldkipp
- * VS10XX decoder support added.
- *
- */
- #include <sys/atom.h>
- #include <sys/event.h>
- #include <sys/timer.h>
- #include <sys/heap.h>
- #include <cfg/arch/gpio.h>
- #include <cfg/audio.h>
- #include <dev/irqreg.h>
- #include <dev/vscodec.h>
- #include <sys/bankmem.h>
- #include <stdlib.h>
- #include <stddef.h>
- #include <string.h>
- #include <memdebug.h>
- /*!
- * \addtogroup xgVsCodec
- */
- /*@{*/
- #if !defined(AUDIO_VS1001K) && !defined(AUDIO_VS1011E) && !defined(AUDIO_VS1002D) && !defined(AUDIO_VS1003B) && !defined(AUDIO_VS1033C) && !defined(AUDIO_VS1053C)
- #define AUDIO_VS1001K
- #endif
- #ifndef VS10XX_FREQ
- /*! \brief Decoder crystal frequency. */
- #define VS10XX_FREQ 12288000UL
- #endif
- #ifndef VS10XX_HWRST_DURATION
- /*! \brief Minimum time in milliseconds to held hardware reset low. */
- #define VS10XX_HWRST_DURATION 1
- #endif
- #ifndef VS10XX_HWRST_RECOVER
- /*! \brief Milliseconds to wait after hardware reset. */
- #define VS10XX_HWRST_RECOVER 4
- #endif
- #ifndef VS10XX_SWRST_RECOVER
- /*! \brief Milliseconds to wait after software reset. */
- #define VS10XX_SWRST_RECOVER 2
- #endif
- #ifndef VS10XX_SCI_MODE
- #define VS10XX_SCI_MODE 0
- #endif
- #ifndef VS10XX_SCI_RATE
- #define VS10XX_SCI_RATE (VS10XX_FREQ / 6)
- #endif
- #ifndef VS10XX_SDI_MODE
- #define VS10XX_SDI_MODE 0
- #endif
- #ifndef VS10XX_SDI_RATE
- #define VS10XX_SDI_RATE (VS10XX_FREQ / 6)
- #endif
- /* -------------------------------------------------
- * AT91 port specifications.
- */
- #if defined (MCU_AT91R40008) || defined (MCU_AT91SAM7X) || defined (MCU_AT91SAM7SE512) || defined (MCU_AT91SAM9260)
- #if defined(ELEKTOR_IR1)
- #define VS_XRESET_BIT 31 /* PB31 */
- #define VS10XX_XCS_BIT PA31_SPI0_NPCS1_A
- #define VS10XX_XCS_PORT PIOA_ID
- #define VS10XX_XDCS_BIT PB30_SPI0_NPCS2_A
- #define VS10XX_XDCS_PORT PIOB_ID
- #define VS10XX_DREQ_BIT PA30_IRQ1_A
- #define VS10XX_DREQ_PIO_ID PIOA_ID
- #define VS10XX_SIGNAL sig_INTERRUPT1
- #endif /* ELEKTOR_IR1 */
- #if defined(VS10XX_XCS_BIT)
- #if !defined(VS10XX_XCS_PORT)
- #define VS10XX_XCS_PE_REG PIO_PER
- #define VS10XX_XCS_OE_REG PIO_OER
- #define VS10XX_XCS_COD_REG PIO_CODR
- #define VS10XX_XCS_SOD_REG PIO_SODR
- #elif VS10XX_XCS_PORT == PIOA_ID
- #define VS10XX_XCS_PE_REG PIOA_PER
- #define VS10XX_XCS_OE_REG PIOA_OER
- #define VS10XX_XCS_COD_REG PIOA_CODR
- #define VS10XX_XCS_SOD_REG PIOA_SODR
- #elif VS10XX_XCS_PORT == PIOB_ID
- #define VS10XX_XCS_PE_REG PIOB_PER
- #define VS10XX_XCS_OE_REG PIOB_OER
- #define VS10XX_XCS_COD_REG PIOB_CODR
- #define VS10XX_XCS_SOD_REG PIOB_SODR
- #elif VS10XX_XCS_PORT == PIOC_ID
- #define VS10XX_XCS_PE_REG PIOC_PER
- #define VS10XX_XCS_OE_REG PIOC_OER
- #define VS10XX_XCS_COD_REG PIOC_CODR
- #define VS10XX_XCS_SOD_REG PIOC_SODR
- #endif
- #define VS10XX_XCS_ENA() \
- outr(VS10XX_XCS_PE_REG, _BV(VS10XX_XCS_BIT)); \
- outr(VS10XX_XCS_OE_REG, _BV(VS10XX_XCS_BIT))
- #define VS10XX_XCS_CLR() outr(VS10XX_XCS_COD_REG, _BV(VS10XX_XCS_BIT))
- #define VS10XX_XCS_SET() outr(VS10XX_XCS_SOD_REG, _BV(VS10XX_XCS_BIT))
- #else /* VS10XX_XCS_BIT */
- #define VS10XX_XCS_ENA()
- #define VS10XX_XCS_CLR()
- #define VS10XX_XCS_SET()
- #endif /* VS10XX_XCS_BIT */
- #if defined(VS10XX_XDCS_BIT)
- #if !defined(VS10XX_XDCS_PORT)
- #define VS10XX_XDCS_PE_REG PIO_PER
- #define VS10XX_XDCS_OE_REG PIO_OER
- #define VS10XX_XDCS_COD_REG PIO_CODR
- #define VS10XX_XDCS_SOD_REG PIO_SODR
- #elif VS10XX_XDCS_PORT == PIOA_ID
- #define VS10XX_XDCS_PE_REG PIOA_PER
- #define VS10XX_XDCS_OE_REG PIOA_OER
- #define VS10XX_XDCS_COD_REG PIOA_CODR
- #define VS10XX_XDCS_SOD_REG PIOA_SODR
- #elif VS10XX_XDCS_PORT == PIOB_ID
- #define VS10XX_XDCS_PE_REG PIOB_PER
- #define VS10XX_XDCS_OE_REG PIOB_OER
- #define VS10XX_XDCS_COD_REG PIOB_CODR
- #define VS10XX_XDCS_SOD_REG PIOB_SODR
- #elif VS10XX_XDCS_PORT == PIOC_ID
- #define VS10XX_XDCS_PE_REG PIOC_PER
- #define VS10XX_XDCS_OE_REG PIOC_OER
- #define VS10XX_XDCS_COD_REG PIOC_CODR
- #define VS10XX_XDCS_SOD_REG PIOC_SODR
- #endif
- #define VS10XX_XDCS_ENA() \
- outr(VS10XX_XDCS_PE_REG, _BV(VS10XX_XDCS_BIT)); \
- outr(VS10XX_XDCS_OE_REG, _BV(VS10XX_XDCS_BIT))
- #define VS10XX_XDCS_CLR() outr(VS10XX_XDCS_COD_REG, _BV(VS10XX_XDCS_BIT))
- #define VS10XX_XDCS_SET() outr(VS10XX_XDCS_SOD_REG, _BV(VS10XX_XDCS_BIT))
- #else /* VS10XX_XDCS_BIT */
- #define VS10XX_XDCS_ENA()
- #define VS10XX_XDCS_CLR()
- #define VS10XX_XDCS_SET()
- #endif /* VS10XX_XDCS_BIT */
- #if defined(VS10XX_DREQ_BIT)
- #if !defined(VS10XX_DREQ_PIO_ID)
- #define VS10XX_DREQ_PD_REG PIO_PDR
- #define VS10XX_DREQ_OD_REG PIO_ODR
- #define VS10XX_DREQ_PDS_REG PIO_PDSR
- #elif VS10XX_DREQ_PIO_ID == PIOA_ID
- #define VS10XX_DREQ_PD_REG PIOA_PDR
- #define VS10XX_DREQ_OD_REG PIOA_ODR
- #define VS10XX_DREQ_PDS_REG PIOA_PDSR
- #elif VS10XX_DREQ_PIO_ID == PIOB_ID
- #define VS10XX_DREQ_PD_REG PIOB_PDR
- #define VS10XX_DREQ_OD_REG PIOB_ODR
- #define VS10XX_DREQ_PDS_REG PIOB_PDSR
- #elif VS10XX_DREQ_PIO_ID == PIOC_ID
- #define VS10XX_DREQ_PD_REG PIOC_PDR
- #define VS10XX_DREQ_OD_REG PIOC_ODR
- #define VS10XX_DREQ_PDS_REG PIOC_PDSR
- #endif
- #define VS10XX_DREQ_ENA() \
- outr(VS10XX_DREQ_PD_REG, _BV(VS10XX_DREQ_BIT)); \
- outr(VS10XX_DREQ_OD_REG, _BV(VS10XX_DREQ_BIT))
- #define VS10XX_DREQ_TST() ((inr(VS10XX_DREQ_PDS_REG) & _BV(VS10XX_DREQ_BIT)) == _BV(VS10XX_DREQ_BIT))
- #else /* VS10XX_DREQ_BIT */
- #define VS10XX_DREQ_ENA()
- #define VS10XX_DREQ_TST() 0
- #endif /* VS10XX_DREQ_BIT */
- /* -------------------------------------------------
- * End of port specifications.
- */
- #endif
- #define VSREQ_PLAY 0x00000001
- #define VSREQ_CANCEL 0x00000002
- #define VSREQ_BEEP 0x00000004
- typedef struct _VSDCB {
- int dcb_pbstat; /*!< \brief Playback status. */
- uint32_t dcb_scmd; /*!< \brief Requested command flags, see VSREQ_ flags. */
- int dcb_crvol; /*!< \brief Current volume of right channel. */
- int dcb_srvol; /*!< \brief Requested volume of right channel. */
- int dcb_clvol; /*!< \brief Current volume of left channel. */
- int dcb_slvol; /*!< \brief Requested volume of left channel. */
- int dcb_ctreb; /*!< \brief Current bass enhancement. */
- int dcb_streb; /*!< \brief Requested bass enhancement. */
- int dcb_ctfin; /*!< \brief Current bass frequency.*/
- int dcb_stfin; /*!< \brief Requested bass frequency. */
- int dcb_cbass; /*!< \brief Current treble enhancement. */
- int dcb_sbass; /*!< \brief Requested bass enhancement. */
- int dcb_cbfin; /*!< \brief Current treble frequency. */
- int dcb_sbfin; /*!< \brief Requested treble frequency. */
- uint32_t dcb_pbwlo; /*!< \brief Playback buffer low watermark. */
- uint32_t dcb_pbwhi; /*!< \brief Playback buffer high watermark. */
- } VSDCB;
- static VSDCB dcb;
- static unsigned int vs_chip;
- /*
- * Interlink not ready yet. Provide some basic SPI routines.
- */
- static uint8_t SpiByte(uint8_t val)
- {
- /* Transmission is started by writing the transmit data. */
- outr(SPI0_TDR, val);
- /* Wait for receiver data register full. */
- while((inr(SPI0_SR) & SPI_RDRF) == 0);
- /* Read data. */
- val = (uint8_t)inr(SPI0_RDR);
- return val;
- }
- static void SciSelect(void)
- {
- outr(SPI0_CSR0, (24 << SPI_SCBR_LSB) | SPI_NCPHA);
- outr(SPI0_MR, SPI_MODFDIS | SPI_MSTR);
- outr(PIOA_CODR, _BV(VS10XX_XCS_BIT));
- }
- static void SciDeselect(void)
- {
- outr(PIOA_SODR, _BV(VS10XX_XCS_BIT));
- }
- /*!
- * \brief Wait for decoder ready.
- *
- * This function will check the DREQ line. Decoder interrupts must have
- * been disabled before calling this function.
- */
- static int VsWaitReady(void)
- {
- int tmo = 16384;
- do {
- if (VS10XX_DREQ_TST()) {
- return 0;
- }
- } while (tmo--);
- return -1;
- }
- /*
- * \brief Write a specified number of bytes to the VS10XX data interface.
- *
- * This function will check the DREQ line. Decoder interrupts must have
- * been disabled before calling this function.
- */
- static int VsSdiWrite(const uint8_t * data, size_t len)
- {
- while (len--) {
- if (!VS10XX_DREQ_TST() && VsWaitReady()) {
- return -1;
- }
- SpiByte(*data);
- data++;
- }
- return 0;
- }
- /*
- * \brief Write a specified number of bytes from program space to the
- * VS10XX data interface.
- *
- * This function is similar to VsSdiWrite() except that the data is
- * located in program space.
- */
- static int VsSdiWrite_P(PGM_P data, size_t len)
- {
- while (len--) {
- if (!VS10XX_DREQ_TST() && VsWaitReady()) {
- return -1;
- }
- SpiByte(PRG_RDB(data));
- data++;
- }
- return 0;
- }
- /*
- * \brief Write to a decoder register.
- *
- * Decoder interrupts must have been disabled before calling this function.
- */
- static void VsRegWrite(ureg_t reg, uint16_t data)
- {
- VsWaitReady();
- SciSelect();
- SpiByte(VS_OPCODE_WRITE);
- SpiByte((uint8_t) reg);
- SpiByte((uint8_t) (data >> 8));
- SpiByte((uint8_t) data);
- SciDeselect();
- }
- /*
- * \brief Read from a register.
- *
- * Decoder interrupts must have been disabled before calling this function.
- *
- * \return Register contents.
- */
- static uint16_t VsRegRead(ureg_t reg)
- {
- uint16_t data;
- VsWaitReady();
- SciSelect();
- SpiByte(VS_OPCODE_READ);
- SpiByte((uint8_t) reg);
- data = (uint16_t)SpiByte(0) << 8;
- data |= SpiByte(0);
- SciDeselect();
- return data;
- }
- /*!
- * \brief Sine wave beep.
- *
- * Untested.
- *
- * \param fsin Frequency.
- * \param ms Duration.
- *
- * \return Always 0.
- */
- static int VsBeep(uint8_t fsin, uint8_t ms)
- {
- static const char on[] PROGMEM = { 0x53, 0xEF, 0x6E };
- static const char off[] PROGMEM = { 0x45, 0x78, 0x69, 0x74 };
- static const char end[] PROGMEM = { 0x00, 0x00, 0x00, 0x00 };
- VsRegWrite(VS_MODE_REG, VS_SM_TESTS | VS_SM_SDINEW);
- fsin = 56 + (fsin & 7) * 9;
- VsSdiWrite_P(on, sizeof(on));
- VsSdiWrite(&fsin, 1);
- VsSdiWrite_P(end, sizeof(end));
- NutDelay(ms);
- VsSdiWrite_P(off, sizeof(off));
- VsSdiWrite_P(end, sizeof(end));
- VsRegWrite(VS_MODE_REG, VS_SM_SDINEW);
- return 0;
- }
- static HANDLE vs_ready;
- static void VsInterrupt(void *arg)
- {
- NutEventPostFromIrq(&vs_ready);
- }
- THREAD(FeederThread, arg)
- {
- char *bp;
- size_t avail;
- int filled;
- uint8_t crgain;
- uint8_t srgain;
- uint8_t clgain;
- uint8_t slgain;
- NutSleep(500);
- dcb.dcb_slvol = dcb.dcb_clvol = -12;
- dcb.dcb_srvol = dcb.dcb_crvol = -12;
- srgain = (uint8_t)(-2 * dcb.dcb_srvol);
- crgain = 254;
- slgain = (uint8_t)(-2 * dcb.dcb_slvol);
- clgain = 254;
- VsRegWrite(VS_VOL_REG, ((uint16_t)clgain << VS_VOL_LEFT_LSB) | ((uint16_t)crgain << VS_VOL_RIGHT_LSB));
- /* Register the interrupt routine */
- while (NutRegisterIrqHandler(&VS10XX_SIGNAL, VsInterrupt, NULL)) {
- NutSleep(1000);
- }
- /* Rising edge will generate an interrupt. */
- NutIrqSetMode(&VS10XX_SIGNAL, NUT_IRQMODE_RISINGEDGE);
- VS10XX_DREQ_ENA();
- NutIrqEnable(&VS10XX_SIGNAL);
- for (;;) {
- NutEventWait(&vs_ready, 100);
- if (!VS10XX_DREQ_TST()) {
- continue;
- }
- if (dcb.dcb_scmd) {
- if (dcb.dcb_scmd & VSREQ_CANCEL) {
- NutSegBufReset();
- }
- if (dcb.dcb_scmd & VSREQ_BEEP) {
- VsBeep(2, 100);
- }
- dcb.dcb_scmd &= VSREQ_PLAY;
- }
- if (NutSegBufUsed() < dcb.dcb_pbwlo) {
- dcb.dcb_pbstat = CODEC_STATUS_IDLE;
- if (crgain != 254) {
- clgain = crgain = 254;
- VsRegWrite(VS_VOL_REG, ((uint16_t)clgain << VS_VOL_LEFT_LSB) | ((uint16_t)crgain << VS_VOL_RIGHT_LSB));
- }
- while (NutSegBufUsed() < dcb.dcb_pbwhi) {
- if (dcb.dcb_scmd) {
- if (dcb.dcb_scmd & VSREQ_PLAY) {
- dcb.dcb_pbwhi = dcb.dcb_pbwlo = NutSegBufUsed() / 2;
- }
- break;
- }
- NutSleep(100);
- }
- }
- dcb.dcb_scmd &= ~VSREQ_PLAY;
- if (dcb.dcb_scmd) {
- continue;
- }
- outr(SPI0_CSR0, (12 << SPI_SCBR_LSB) | SPI_NCPHA);
- outr(SPI0_MR, SPI_MODFDIS | SPI_MSTR);
- if (dcb.dcb_pbstat != CODEC_STATUS_PLAYING) {
- outr(PIOB_CODR, _BV(30));
- while (!VS10XX_DREQ_TST()) {
- SpiByte(0);
- }
- outr(PIOB_SODR, _BV(30));
- }
- for (;;) {
- if (!VS10XX_DREQ_TST()) {
- break;
- }
- bp = NutSegBufReadRequest(&avail);
- if (avail == 0) {
- dcb.dcb_pbstat = CODEC_STATUS_IDLE;
- if (crgain != 254) {
- clgain = crgain = 254;
- VsRegWrite(VS_VOL_REG, ((uint16_t)clgain << VS_VOL_LEFT_LSB) | ((uint16_t)crgain << VS_VOL_RIGHT_LSB));
- }
- break;
- }
- outr(PIOB_CODR, _BV(30));
- for (filled = 0; avail--; filled++, bp++) {
- if (!VS10XX_DREQ_TST()) {
- dcb.dcb_pbstat = CODEC_STATUS_PLAYING;
- break;
- }
- SpiByte(*bp);
- }
- outr(PIOB_SODR, _BV(30));
- NutSegBufReadLast(filled);
- }
- if (dcb.dcb_clvol != dcb.dcb_slvol || dcb.dcb_crvol != dcb.dcb_srvol) {
- srgain = (uint8_t)(-2 * dcb.dcb_srvol);
- slgain = (uint8_t)(-2 * dcb.dcb_slvol);
- dcb.dcb_clvol = dcb.dcb_slvol;
- dcb.dcb_crvol = dcb.dcb_srvol;
- }
- else if (srgain != crgain || slgain != clgain) {
- int diff = (int)srgain - (int)crgain;
- if (diff > 4) {
- diff = 4;
- }
- else if (diff < -4) {
- diff = -4;
- }
- crgain = (uint8_t)((int)crgain + diff);
- diff = (int)slgain - (int)clgain;
- if (diff > 4) {
- diff = 4;
- }
- else if (diff < -4) {
- diff = -4;
- }
- clgain = (uint8_t)((int)clgain + diff);
- VsRegWrite(VS_VOL_REG, ((uint16_t)clgain << VS_VOL_LEFT_LSB) | ((uint16_t)crgain << VS_VOL_RIGHT_LSB));
- }
- }
- }
- static int VsPlayerFlush(void)
- {
- int tmo = 1000; /* TODO: Configurable timeout. */
- /* Stupid polling for now. */
- while(dcb.dcb_pbstat == CODEC_STATUS_PLAYING) {
- NutSleep(1);
- if (tmo-- <= 0) {
- return -1;
- }
- }
- return 0;
- }
- /*!
- * \brief Send data to the decoder.
- *
- * A carriage return character will be automatically appended
- * to any linefeed.
- *
- * \return Number of characters sent.
- */
- static int VsWrite(NUTFILE * fp, const void *data, int len)
- {
- char *buf;
- size_t rbytes;
- /* Flush buffer if data pointer is a NULL pointer. */
- if (data == NULL || len == 0) {
- return VsPlayerFlush();
- }
- if (len) {
- buf = NutSegBufWriteRequest(&rbytes);
- if (len > rbytes) {
- len = rbytes;
- }
- if (len) {
- memcpy(buf, data, len);
- }
- NutSegBufWriteLast(len);
- }
- return len;
- }
- #ifdef __HARVARD_ARCH__
- /*!
- * \brief Write to device.
- *
- * Similar to VsWrite() except that the data is expected in program memory.
- *
- * This function is implemented for CPUs with Harvard Architecture only.
- *
- * This function is called by the low level output routines of the
- * \ref xrCrtLowio "C runtime library", using the
- * \ref _NUTDEVICE::dev_write_P entry.
- *
- * \param nfp Pointer to a \ref NUTFILE structure, obtained by a previous
- * call to PnutFileOpen().
- * \param buffer Pointer to the data in program space. If zero, then the
- * output buffer will be flushed.
- * \param len Number of bytes to write.
- *
- * \return The number of bytes written. A return value of -1 indicates an
- * error. Currently this function is not implemented and always
- * returns -1.
- *
- */
- static int VsWrite_P(NUTFILE * nfp, PGM_P buffer, int len)
- {
- return -1;
- }
- #endif
- /*!
- * \brief Open codec device.
- *
- * \return Pointer to a static NUTFILE structure.
- */
- static NUTFILE *VsOpen(NUTDEVICE * dev, const char *name, int mode, int acc)
- {
- NUTFILE *nfp;
- VsRegWrite(VS_MODE_REG, VS_SM_RESET | VS_SM_SDINEW);
- NutSleep(2);
- nfp = malloc(sizeof(NUTFILE));
- nfp->nf_dev = dev;
- nfp->nf_fcb = NULL;
- NutSegBufReset();
- return nfp;
- }
- /*!
- * \brief Close codec device.
- */
- static int VsClose(NUTFILE * nfp)
- {
- int rc = VsPlayerFlush();
- if (nfp) {
- free(nfp);
- }
- return rc;
- }
- static int VsPlayBufferInit(uint32_t size)
- {
- if (dcb.dcb_pbstat != CODEC_STATUS_IDLE) {
- return -1;
- }
- if (NutSegBufInit((size_t)size) == NULL) {
- return -1;
- }
- dcb.dcb_pbwlo = NutSegBufAvailable() / 3;
- dcb.dcb_pbwhi = dcb.dcb_pbwlo * 2;
- return 0;
- }
- /*!
- * \brief Handle I/O controls for audio codec.
- *
- * - AUDIO_PLAY Force playback start, even if the buffer level is too lower.
- * - AUDIO_CANCEL Cancel playback and discard all buffered data.
- * - AUDIO_GET_STATUS Sets an int to 1 if the player is running, 0 if idle.
- * - AUDIO_GET_PLAYGAIN Sets an int to the current playback gain, 0..-127.
- * - AUDIO_SET_PLAYGAIN Reads the requested playback gain from an int, 0..-127.
- * - AUDIO_GET_TREB Reads audio enhancement treble value as int in 1.5dB steps
- * - AUDIO_SET_TREB Sets audio enhancement treble value as int in 1.5dB steps
- * - AUDIO_GET_TFIN Reads audio enhancement treable cutoff frequency in 1000Hz steps
- * - AUDIO_SET_TFIN Sets audio enhancement treable cutoff frequency in 1000Hz steps
- * - AUDIO_GET_BASS Reads audio enhancement bass value as int in 1dB steps
- * - AUDIO_SET_BASS Sets audio enhancement bass value as int in 1dB steps
- * - AUDIO_GET_BFIN Reads audio enhancement bass cutoff frequency in 10Hz steps
- * - AUDIO_SET_BFIN Sets audio enhancement bass cutoff frequency in 10Hz steps
- * - AUDIO_GET_PBSIZE Sets an unsigned long with the size of the playback buffer.
- * - AUDIO_SET_PBSIZE Sets the size the playback buffer using an unsigned long.
- * - AUDIO_GET_PBLEVEL Sets an unsigned long with the number of bytes in the playback buffer.
- * - AUDIO_GET_PBWLOW Sets an unsigned long with the low watermark of the playback buffer.
- * - AUDIO_SET_PBWLOW Sets the low watermark (unsigned long) of the playback buffer.
- * - AUDIO_GET_PBWHIGH Sets an unsigned long with the high watermark of the playback buffer.
- * - AUDIO_SET_PBWHIGH Sets the high watermark (unsigned long) of the playback buffer.
- * - AUDIO_BEEP Plays a short sine wave beep.
- *
- * \return 0 on success, -1 otherwise.
- */
- static int VsIOCtl(NUTDEVICE * dev, int req, void *conf)
- {
- int rc = 0;
- uint32_t *lvp = (uint32_t *) conf;
- int *ivp = (int *) conf;
- int iv = *ivp;
- switch (req) {
- case AUDIO_PLAY:
- /* Immediately start playing. */
- if (dcb.dcb_pbstat != CODEC_STATUS_PLAYING) {
- dcb.dcb_scmd |= VSREQ_PLAY;
- }
- break;
- case AUDIO_CANCEL:
- /* Immediately stop playing and discard buffer. */
- if (dcb.dcb_pbstat == CODEC_STATUS_PLAYING) {
- dcb.dcb_scmd |= VSREQ_CANCEL;
- }
- break;
- case AUDIO_GET_STATUS:
- *ivp = dcb.dcb_pbstat;
- break;
- case AUDIO_GET_PLAYGAIN:
- if (dcb.dcb_crvol >= dcb.dcb_clvol) {
- *ivp = dcb.dcb_crvol;
- }
- else {
- *ivp = dcb.dcb_clvol;
- }
- break;
- case AUDIO_SET_PLAYGAIN:
- if (iv > AUDIO_DAC_MAX_GAIN) {
- iv = AUDIO_DAC_MAX_GAIN;
- }
- if (iv < AUDIO_DAC_MIN_GAIN) {
- iv = AUDIO_DAC_MIN_GAIN;
- }
- dcb.dcb_slvol = dcb.dcb_srvol = iv;
- break;
- case AUDIO_GET_TREB:
- *lvp = dcb.dcb_ctreb;
- break;
- case AUDIO_SET_TREB:
- if( iv > AUDIO_DAC_MAX_TREB) {
- iv = AUDIO_DAC_MAX_TREB;
- }
- dcb.dcb_streb = iv;
- break;
- case AUDIO_GET_TFIN:
- *lvp = dcb.dcb_ctfin;
- break;
- case AUDIO_SET_TFIN:
- if( iv > AUDIO_DAC_MAX_TFIN) {
- iv = AUDIO_DAC_MAX_TFIN;
- }
- dcb.dcb_stfin = iv;
- break;
- case AUDIO_GET_BASS:
- *lvp = dcb.dcb_cbass;
- break;
- case AUDIO_SET_BASS:
- if( iv > AUDIO_DAC_MAX_BASS) {
- iv = AUDIO_DAC_MAX_BASS;
- }
- dcb.dcb_sbass = iv;
- case AUDIO_GET_BFIN:
- *lvp = dcb.dcb_cbass;
- break;
- case AUDIO_SET_BFIN:
- if( iv > AUDIO_DAC_MAX_BFIN) {
- iv = AUDIO_DAC_MAX_BFIN;
- }
- dcb.dcb_sbass = iv;
- case AUDIO_GET_PBSIZE:
- *lvp = NutSegBufAvailable() + NutSegBufUsed();
- break;
- case AUDIO_SET_PBSIZE:
- rc = VsPlayBufferInit(*lvp);
- break;
- case AUDIO_GET_PBLEVEL:
- *lvp = NutSegBufUsed();
- break;
- case AUDIO_GET_PBWLOW:
- *lvp = dcb.dcb_pbwlo;
- break;
- case AUDIO_SET_PBWLOW:
- dcb.dcb_pbwlo = *lvp;
- break;
- case AUDIO_GET_PBWHIGH:
- *lvp = dcb.dcb_pbwhi;
- break;
- case AUDIO_SET_PBWHIGH:
- dcb.dcb_pbwhi = *lvp;
- break;
- case AUDIO_BEEP:
- dcb.dcb_scmd |= VSREQ_BEEP;
- break;
- #if 0
- case AUDIO_GET_DECINFO:
- /* Retrieve decoder information. */
- break;
- case AUDIO_GET_DECCAPS:
- /* Retrieve decoder capabilities. */
- break;
- case AUDIO_GET_DECFMTS:
- /* Retrieve decoder formats. */
- break;
- case AUDIO_SET_DECFMTS:
- /* Enable or disable specific decoder formats. */
- break;
- case AUDIO_GET_CODINFO:
- /* Retrieve encoder information. */
- break;
- case AUDIO_GET_CODCAPS:
- /* Retrieve encoder capabilities. */
- break;
- case AUDIO_GET_CODFMTS:
- /* Retrieve encoder formats. */
- break;
- case AUDIO_SET_CODFMTS:
- /* Enable or disable specific encoder formats. */
- break;
- case AUDIO_GET_MIDINFO:
- /* Retrieve midi information. */
- break;
- case AUDIO_GET_MIDCAPS:
- /* Retrieve midi capabilities. */
- break;
- #endif
- default:
- rc = -1;
- break;
- }
- return rc;
- }
- /*
- * Called via dev_init pointer when the device is registered.
- */
- static int VsInit(NUTDEVICE * dev)
- {
- uint16_t mode;
- /* Release reset line and read the status register. */
- outr(PIOB_PER, _BV(VS_XRESET_BIT));
- outr(PIOB_SODR, _BV(VS_XRESET_BIT));
- outr(PIOB_OER, _BV(VS_XRESET_BIT));
- NutSleep(3);
- VsRegRead(VS_MODE_REG); /* Dummy read. TODO: Why? */
- mode = VsRegRead(VS_MODE_REG);
- if ((mode & VS_SM_SDINEW) == 0) {
- VsRegWrite(VS_MODE_REG, VS_SM_RESET | VS_SM_SDINEW);
- NutSleep(2);
- }
- vs_chip = (VsRegRead(VS_STATUS_REG) & VS_SS_VER) >> VS_SS_VER_LSB;
- #if VS10XX_FREQ < 20000000UL && defined(VS_CF_DOUBLER)
- VsRegWrite(VS_CLOCKF_REG, (uint16_t)(VS_CF_DOUBLER | (VS10XX_FREQ / 2000UL)));
- #else
- VsRegWrite(VS_CLOCKF_REG, (uint16_t)(VS10XX_FREQ / 2000UL));
- #endif
- #if defined(VS_INT_FCTLH_REG)
- if (vs_chip == 0) {
- /* Force frequency change (see datasheet). */
- VsRegWrite(VS_INT_FCTLH_REG, 0x8008);
- }
- #endif
- if (VsPlayBufferInit(0)) {
- return -1;
- }
- if (NutThreadCreate("vsdeco", FeederThread, NULL, 1024) == 0) {
- return -1;
- }
- return 0;
- }
- /*!
- * \brief VS10XX device information structure.
- *
- * An application must pass a pointer to this structure to
- * NutRegisterDevice() before using this driver.
- *
- * The device is named \b audio0.
- *
- * \showinitializer
- */
- NUTDEVICE devVsCodec = {
- 0, /* Pointer to next device, dev_next. */
- {'a', 'u', 'd', 'i', 'o', '0', 0, 0, 0}, /* Unique device name, dev_name. */
- IFTYP_CHAR, /* Type of device, dev_type. */
- 0, /* Base address, dev_base (not used). */
- 0, /* First interrupt number, dev_irq (not used). */
- 0, /* Interface control block, dev_icb (not used). */
- &dcb, /* Driver control block, dev_dcb. */
- VsInit, /* Driver initialization routine, dev_init. */
- VsIOCtl, /* Driver specific control function, dev_ioctl. */
- NULL, /* Read from device, dev_read. */
- VsWrite, /* Write to device, dev_write. */
- #ifdef __HARVARD_ARCH__
- VsWrite_P, /* Write data from program space to device, dev_write_P. */
- #endif
- VsOpen, /* Open a device or file, dev_open. */
- VsClose, /* Close a device or file, dev_close. */
- NULL, /* Request file size, dev_size. */
- NULL, /* Select function, optional, not yet implemented */
- };
- /*@}*/
|