| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984 |
- /*
- * Copyright (C) 2008-2009 by egnite GmbH
- * 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/
- */
- /*
- * $Id: spi_vscodec.c 5472 2013-12-06 00:16:28Z olereinhardt $
- */
- #include <dev/vscodec.h>
- #include <sys/event.h>
- #include <sys/timer.h>
- #include <sys/nutdebug.h>
- #include <sys/bankmem.h>
- #include <stdlib.h>
- #include <string.h>
- #include <memdebug.h>
- #include <fcntl.h>
- /*!
- * \addtogroup xgVsCodec
- */
- /*@{*/
- /*! \name Internal Command Flags */
- /*@{*/
- /*! \brief Force immediate player start. */
- #define VSREQ_PLAY 0x00000001
- /*! \brief Force immediate player stop. */
- #define VSREQ_CANCEL 0x00000002
- /*! \brief Volume update. */
- #define VSREQ_VOLUPD 0x00000004
- /*! \brief Audio enhancement update. */
- #define VSREQ_AUDIOE 0x00000008
- /*! \brief Sine wave test. */
- #define VSREQ_BEEP 0x00000010
- /*! \brief Start recording. */
- #define VSREQ_RECORD 0x00000020
- /*@}*/
- /* Used to send zeros to the decoder. */
- uint8_t zero_chunk[VSCODEC_DATA_CHUNK_SIZE];
- /*!
- * \brief Wait until codec is ready.
- *
- * \param dev Sepcifies the codec device.
- * \param tmo Maximum number of milliseconds to wait.
- *
- * \return 0 on success, -1 on time out.
- */
- int VsCodecWaitReady(NUTDEVICE *dev, uint32_t tmo)
- {
- VSDCB *dcb = (VSDCB *) dev->dev_dcb;
- while (!(* dcb->dcb_isready)()) {
- if (NutEventWait(&dcb->dcb_feedme, tmo)) {
- return -1;
- }
- }
- return 0;
- }
- /*
- * \brief Read from or write to a VLSI audio codec register.
- *
- * \param dev Specifies the codec device.
- * \param op Operation, either \ref VS_OPCODE_READ or
- * \ref VS_OPCODE_WRITE.
- * \param reg The register index.
- * \param val This value will be stored in the register on a write
- * operation. It is ignored on read operations.
- *
- * \return Register contents. On write operations, the contents before
- * the modification is returned.
- */
- uint16_t VsCodecReg(NUTDEVICE *dev, uint_fast8_t op, uint_fast8_t reg, uint_fast16_t val)
- {
- uint8_t cmd[4];
- VSDCB *dcb = (VSDCB *) dev->dev_dcb;
- /* Assemble command buffer. */
- cmd[0] = (uint8_t) op;
- cmd[1] = (uint8_t) reg;
- cmd[2] = (uint8_t) (val >> 8);
- cmd[3] = (uint8_t) val;
- VsCodecWaitReady(dev, VSCODEC_CMD_TIMEOUT);
- (*dcb->dcb_sendcmd)(cmd, 4);
- val = cmd[2];
- val <<= 8;
- val |= cmd[3];
- return (uint16_t) val;
- }
- /*!
- * \brief Read and modify VLSI audio codec mode flags.
- *
- * Set both, flags and mask to zero to read the flags without change.
- *
- * \param dev Specifies the audio codec device.
- * \param flags Mode flags to set. All other flags are disabled, if they
- * appear in the mask.
- * \param mask Mode flags to update. All other flags are left unchanged.
- *
- * \return Previous mode flags.
- */
- uint16_t VsCodecMode(NUTDEVICE *dev, uint_fast16_t flags, uint_fast16_t mask)
- {
- uint16_t rc;
- /* Read the mode register. */
- rc = VsCodecReg(dev, VS_OPCODE_READ, VS_MODE_REG, 0);
- if (mask | flags) {
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_MODE_REG, (rc & ~mask) | flags);
- /* Add delay after software reset, if configured. */
- #if defined(VSCODEC_SWRST_RECOVER)
- if (flags & VS_SM_RESET) {
- NutSleep(VSCODEC_SWRST_RECOVER);
- }
- #endif
- }
- return rc;
- }
- /*!
- * \brief Set volume.
- *
- * \param dev Specifies the audio codec device.
- * \param left Left channel gain in db.
- * \param right Right channel gain in db.
- *
- * \return Always 0.
- */
- int VsDecoderSetVolume(NUTDEVICE *dev, int left, int right)
- {
- VSDCB *dcb = (VSDCB *)dev->dev_dcb;
- uint16_t l;
- uint16_t r;
- /* Honor limits. */
- left = left > AUDIO_DAC_MAX_GAIN ? AUDIO_DAC_MAX_GAIN : left;
- left = left < AUDIO_DAC_MIN_GAIN ? AUDIO_DAC_MIN_GAIN : left;
- right = right > AUDIO_DAC_MAX_GAIN ? AUDIO_DAC_MAX_GAIN : right;
- right = right < AUDIO_DAC_MIN_GAIN ? AUDIO_DAC_MIN_GAIN : right;
- /* Convert to register values. */
- l = (uint16_t)(-2 * left);
- r = (uint16_t)(-2 * right);
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_VOL_REG, (l << VS_VOL_LEFT_LSB) | (r << VS_VOL_RIGHT_LSB));
- dcb->dcb_lvol = left;
- dcb->dcb_rvol = right;
- return 0;
- }
- /*!
- * \brief Set Bass.
- *
- * \param dev Specifies the audio codec device.
- * \param treb Treble amplitude x 1.5dB.
- * \param tset Treble frequency limit x 1000Hz
- * \param bass Bass amplitude x 1dB.
- * \param bset Bass frequency limit x 10Hz
- *
- * \return Always 0.
- */
- int VsDecoderSetBass(NUTDEVICE *dev, int treb, int tfin, int bass, int bfin)
- {
- #ifdef VS_HAS_BASS_REG
- VSDCB *dcb = (VSDCB *)dev->dev_dcb;
- // uint16_t t;
- // uint16_t b;
- //printf("bass: t:%ddB tf:%dHz b:%ddB bf:%dHz\n", treb, tfin*1000, bass, bfin);
- /* Honor limits. */
- treb = treb > AUDIO_DAC_MAX_TREB ? AUDIO_DAC_MAX_TREB : treb;
- treb = treb < 0 ? 0 : treb;
- tfin = tfin > AUDIO_DAC_MAX_TFIN ? AUDIO_DAC_MAX_TFIN : tfin;
- tfin = tfin < 0 ? 0 : tfin;
- bass = bass > AUDIO_DAC_MAX_BASS ? AUDIO_DAC_MAX_BASS : bass;
- bass = bass < 0 ? 0 : bass;
- bfin = bfin > AUDIO_DAC_MAX_BFIN ? AUDIO_DAC_MAX_BFIN : bfin;
- bfin = bfin < 0 ? 0 : bfin;
- /* Convert to register values. */
- /*
- l = (uint16_t)(-2 * left);
- r = (uint16_t)(-2 * right);
- */
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_BASS_REG,
- (treb << VS_ST_AMPLITUDE_LSB) | (tfin << VS_ST_FREQLIMIT_LSB) |
- (treb << VS_SB_AMPLITUDE_LSB) | (tfin << VS_SB_FREQLIMIT_LSB)
- );
- dcb->dcb_treb = treb;
- dcb->dcb_tfin = tfin;
- dcb->dcb_bass = bass;
- dcb->dcb_bfin = bfin;
- #endif
- return 0;
- }
- /*!
- * \brief Start or stop sine wave beeper.
- *
- * Old, VS1001 compatible routine, which supports a limit number of
- * frequencies only. It will try to find the best value.
- *
- * \param dev Specifies the audio codec device.
- * \param fsin Frequency, or 0 to switch beeper off.
- *
- * \return Actual frequency set.
- */
- uint16_t VsCodecBeep(NUTDEVICE *dev, uint16_t fsin)
- {
- uint16_t rc = 0;
- static uint8_t on[] = { 0x53, 0xEF, 0x6E, 0x3F, 0x00, 0x00, 0x00, 0x00 };
- static const uint8_t off[] = { 0x45, 0x78, 0x69, 0x74, 0x00, 0x00, 0x00, 0x00 };
- static const uint16_t ftab[] = { 44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000 };
- static uint16_t mode;
- VSDCB *dcb = (VSDCB *)dev->dev_dcb;
- if (fsin) {
- uint_fast16_t s;
- uint32_t f;
- uint_fast16_t d;
- int_fast8_t i = sizeof(ftab) / sizeof(ftab[0]);
- int_fast8_t ie = 0;
- int_fast16_t dmin = fsin;
- uint32_t fs = (long)fsin * 128;
- while (--i >= ie) {
- /* Calculate skip speed. */
- f = ftab[i];
- s = (fs + f / 2) / f;
- /* Check that skip is within 5-bit range. */
- if (s && s < 32) {
- /* Calculate the absolute deviation. */
- f *= s;
- f += 64;
- f >>= 7;
- d = (uint_fast16_t)(fsin > f ? fsin - f : f - fsin);
- /* Check if this is a new minimum. */
- if (d < dmin) {
- dmin = d;
- rc = (uint16_t) f;
- /* Set new skip speed and frequency index. */
- on[3] = (i << 5) | s;
- /* We can stop earlier with this fit. */
- if (i > 1) {
- ie = i - 2;
- }
- }
- }
- }
- mode = VsCodecMode(dev, VS_SM_RESET, VS_SM_RESET);
- #ifdef VS_SM_TESTS
- VsCodecMode(dev, mode | VS_SM_TESTS, 0xffff);
- #endif
- (*dcb->dcb_senddata)(on, sizeof(on));
- } else {
- (*dcb->dcb_senddata)(off, sizeof(off));
- #ifdef VS_SM_TESTS
- VsCodecMode(dev, VS_SM_RESET, VS_SM_RESET | VS_SM_TESTS);
- #else
- VsCodecMode(dev, VS_SM_RESET, VS_SM_RESET);
- #endif
- VsCodecMode(dev, mode, 0xffff);
- }
- return rc;
- }
- /*
- * Initialize the stream output buffer with a given size.
- *
- * \param dev Specifies the audio codec device.
- */
- int VsDecoderBufferInit(NUTDEVICE *dev, uint32_t size)
- {
- VSDCB *dcb = (VSDCB *)dev->dev_dcb;
- /* Make sure that the decoder is idle. */
- if (dcb->dcb_pbstat != CODEC_STATUS_IDLE) {
- return -1;
- }
- /* Set new buffer size. */
- if (NutSegBufInit((size_t)size) == NULL) {
- return -1;
- }
- /* Set watermark defaults. */
- dcb->dcb_pbwlo = NutSegBufAvailable() / 3;
- dcb->dcb_pbwhi = dcb->dcb_pbwlo * 2;
- return 0;
- }
- /*
- * VLSI decoder feeding thread.
- */
- THREAD(FeederThread, arg)
- {
- uint8_t *bp;
- size_t avail;
- int filled;
- uint_fast8_t intest = 0;
- uint_fast16_t idlefill = 0;
- NUTDEVICE *dev = (NUTDEVICE *)arg;
- VSDCB *dcb = (VSDCB *)dev->dev_dcb;
- /* We are a high priority thread. */
- NutThreadSetPriority(7);
- for (;;) {
- /*
- ** Idle mode processing.
- */
- if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
- /* Loop while in test mode. */
- do {
- /* Wait for a request or a buffer update. */
- NutEventWait(&dcb->dcb_feedme, NUT_WAIT_INFINITE);
- /* Process beep commands. */
- if (dcb->dcb_scmd & VSREQ_BEEP) {
- if ((*dcb->dcb_isready)()) {
- dcb->dcb_scmd &= ~VSREQ_BEEP;
- intest = VsCodecBeep(dev, dcb->dcb_sinefreq) != 0;
- }
- }
- } while (intest);
- /* Check if we should change to record mode. */
- if (dcb->dcb_scmd & VSREQ_RECORD) {
- dcb->dcb_scmd &= ~(VSREQ_RECORD | VSREQ_CANCEL);
- dcb->dcb_pbstat = CODEC_STATUS_RECORDING;
- /* Activate the encoder. */
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_AIADDR_REG, 0x0034);
- }
- /* Check if we should change to play mode. This will happen
- ** if we receive a play request or if the buffer contents
- ** reaches the high watermark. */
- else if ((dcb->dcb_scmd & VSREQ_PLAY) != 0 || NutSegBufUsed() >= dcb->dcb_pbwhi) {
- dcb->dcb_scmd &= ~(VSREQ_PLAY | VSREQ_CANCEL);
- dcb->dcb_pbstat = CODEC_STATUS_PLAYING;
- VsDecoderSetVolume(dev, dcb->dcb_lvol, dcb->dcb_rvol);
- }
- }
- /*
- ** Play mode processing.
- */
- if (dcb->dcb_pbstat == CODEC_STATUS_PLAYING) {
- /* Wait while decoder is busy. */
- VsCodecWaitReady(dev, NUT_WAIT_INFINITE);
- /* Process cancel requests. */
- if (dcb->dcb_scmd & VSREQ_CANCEL) {
- dcb->dcb_scmd &= ~VSREQ_CANCEL;
- NutSegBufReset();
- #if VS_HAS_SM_CANCEL
- VsCodecMode(dev, VS_SM_CANCEL, VS_SM_CANCEL);
- #endif
- }
- /* Retrieve new data from the input buffer. */
- bp = (uint8_t *) NutSegBufReadRequest(&avail);
- if (avail) {
- /* More data available, send as much as possible to the
- ** decoder. If no data was sent, then the bus may be
- ** blocked by another device. */
- filled = (*dcb->dcb_senddata)(bp, avail);
- if (filled) {
- /* Update the buffer's read position. */
- NutSegBufReadLast(filled);
- NutEventPost(&dcb->dcb_bufque);
- idlefill = 2048;
- }
- if (dcb->dcb_scmd & VSREQ_VOLUPD) {
- VsDecoderSetVolume(dev, dcb->dcb_lvol, dcb->dcb_rvol);
- dcb->dcb_scmd &= ~VSREQ_VOLUPD;
- }
- if (dcb->dcb_scmd & VSREQ_AUDIOE) {
- VsDecoderSetBass( dev, dcb->dcb_treb, dcb->dcb_tfin, dcb->dcb_bass, dcb->dcb_bfin);
- dcb->dcb_scmd &= ~VSREQ_AUDIOE;
- }
- } else if (NutSegBufUsed() == 0) {
- /* Running out of data. */
- if (idlefill) {
- /* For some reason the HDAT0/HDAT1 registers in the
- ** VS1053 do not contain zero when the codec runs
- ** empty. I expected this when reading the datasheet.
- ** Instead we fill the buffer with zeros, to make
- ** sure that the whole buffer is decoded before we
- ** enter idle mode. */
- idlefill -= (*dcb->dcb_senddata)(NULL, idlefill);
- }
- if (idlefill == 0) {
- /* Finally we reached idle mode. */
- dcb->dcb_pbstat = CODEC_STATUS_IDLE;
- NutEventPost(&dcb->dcb_bufque);
- }
- }
- }
- /*
- ** Record mode processing.
- */
- else if (dcb->dcb_pbstat == CODEC_STATUS_RECORDING) {
- for (;;) {
- /* Process cancel requests. */
- if (dcb->dcb_scmd & VSREQ_CANCEL) {
- dcb->dcb_scmd &= ~VSREQ_CANCEL;
- dcb->dcb_pbstat = CODEC_STATUS_IDLE;
- break;
- }
- bp = (uint8_t *)NutSegBufWriteRequest(&avail);
- avail >>= 1;
- if (avail == 0) {
- /* Buffer overflow. */
- dcb->dcb_pbstat = CODEC_STATUS_IDLE;
- break;
- }
- #if defined(VS_HDAT0_REG) && defined(VS_HDAT1_REG)
- /* TODO: Missing register definitions give compiler error. */
- filled = VsCodecReg(dev, VS_OPCODE_READ, VS_HDAT1_REG, 0);
- if (filled > 255) {
- if (avail > filled) {
- avail = filled;
- } else {
- filled = avail;
- }
- (*dcb->dcb_recvdata)(bp, filled << 1);
- NutSegBufWriteLast(avail << 1);
- NutEventPost(&dcb->dcb_bufque);
- } else {
- NutSleep(200);
- }
- #endif
- }
- NutEventPost(&dcb->dcb_bufque);
- }
- }
- }
- /* TODO: This is a preview and may not work as expected. */
- static int VsCodecLoadPlugIn(NUTDEVICE *dev, VS_PLUGIN_INFO *plg)
- {
- uint_fast8_t reg;
- uint_fast16_t cnt;
- size_t i = 0;
- #ifdef VS_SM_ADPCM
- VsCodecMode(dev, 0, VS_SM_ADPCM);
- #endif
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_CLOCKF_REG, 0xC000);
- NutSleep(100);
- #if VS_HAS_BASS_REG
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_BASS_REG, 0);
- #endif
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_AIADDR_REG, 0);
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_WRAMADDR_REG, 0xC01A);
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_WRAM_REG, 0x0002);
- while (i + 3 <= plg->vsplg_size) {
- reg = (uint_fast8_t)plg->vsplg_data[i++];
- cnt = plg->vsplg_data[i++];
- if (cnt & 0x8000) {
- cnt &= 0x7FFF;
- while (cnt--) {
- VsCodecReg(dev, VS_OPCODE_WRITE, reg, plg->vsplg_data[i]);
- }
- i++;
- }
- else if (i + cnt <= plg->vsplg_size) {
- while (cnt--) {
- VsCodecReg(dev, VS_OPCODE_WRITE, reg, plg->vsplg_data[i]);
- i++;
- }
- }
- else {
- break;
- }
- }
- if (i != plg->vsplg_size) {
- return -1;
- }
- #ifdef VS_SM_ADPCM
- // set bits 12 and 13 of register SCI_MODE
- // 12 is the ADPCM bit, 13 is the MIC/LINE bit.
- VsCodecMode(dev, VS_SM_LINE_IN | VS_SM_ADPCM, VS_SM_LINE_IN | VS_SM_ADPCM);
- #endif
- #if VS_HAS_AICTRL0_REG
- // write 0 to SCI_AICTRL0 (maximum signal level, set by encoder to read later on)
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_AICTRL0_REG, 0);
- #endif
- // recording gain 0: automatic gain control on
- //CodecReg(&codec_node, VS_OPCODE_WRITE, VS_AICTRL1_REG, 0);
- #if VS_HAS_AICTRL1_REG
- // recording gain 1
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_AICTRL1_REG, 1024);
- #endif
- #if VS_HAS_AICTRL2_REG
- // maximum autogain amplification, 4096 (=4x) is recommended
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_AICTRL2_REG, 4096);
- #endif
- #if VS_HAS_AICTRL3_REG
- // setting SCI_AICTRL3 should be fine, too...
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_AICTRL3_REG, 0);
- #endif
- return 0;
- }
- static int VsCodecWriteWRam(NUTDEVICE *dev, VS_WRAM_DATA *vswd)
- {
- size_t i;
- if (vswd == NULL) {
- /*
- * Initialize data transfer.
- */
- #ifdef VS_SM_ADPCM
- VsCodecMode(dev, 0, VS_SM_ADPCM);
- #endif
- /* Set clock to 4.5x = 55.3 MHz. */
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_CLOCKF_REG, 0xC000);
- NutSleep(100);
- #if VS_HAS_BASS_REG
- /* Clear SCI bass register. */
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_BASS_REG, 0);
- #endif
- /* Disable all interrupts except SCI. */
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_AIADDR_REG, 0);
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_WRAMADDR_REG, 0xC01A);
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_WRAM_REG, 0x0002);
- }
- else if (vswd->vswd_data) {
- /*
- * Transfer data, if available.
- */
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_WRAMADDR_REG, vswd->vswd_addr);
- for (i = 0; i < vswd->vswd_size; i++) {
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_WRAM_REG, vswd->vswd_data[i]);
- }
- } else {
- /*
- * All data transfered.
- */
- #ifdef VS_SM_ADPCM
- VsCodecMode(dev, VS_SM_LINE_IN | VS_SM_ADPCM, VS_SM_LINE_IN | VS_SM_ADPCM);
- #endif
- #if VS_HAS_AICTRL0_REG
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_AICTRL0_REG, 0);
- #endif
- #if VS_HAS_AICTRL1_REG
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_AICTRL1_REG, 1024);
- #endif
- #if VS_HAS_AICTRL2_REG
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_AICTRL2_REG, 4096);
- #endif
- #if VS_HAS_AICTRL3_REG
- VsCodecReg(dev, VS_OPCODE_WRITE, VS_AICTRL3_REG, 0);
- #endif
- }
- return 0;
- }
- /*!
- * \brief Handle I/O controls for audio codec.
- *
- * \param dev Specifies the audio codec device.
- *
- * - 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_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.
- */
- int VsCodecIOCtl(NUTDEVICE * dev, int req, void *conf)
- {
- int rc = 0;
- uint32_t *lvp = (uint32_t *) conf;
- int *ivp = (int *) conf;
- VSDCB *dcb = (VSDCB *)dev->dev_dcb;
- switch (req) {
- case AUDIO_PLAY:
- if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
- /* Immediately start playing. */
- dcb->dcb_scmd |= VSREQ_PLAY;
- }
- NutEventPost(&dcb->dcb_feedme);
- break;
- case AUDIO_CANCEL:
- if (dcb->dcb_pbstat == CODEC_STATUS_PLAYING) {
- /* Immediately stop playing and discard buffer. */
- dcb->dcb_scmd |= VSREQ_CANCEL;
- }
- NutEventPost(&dcb->dcb_feedme);
- break;
- case AUDIO_GET_STATUS:
- *ivp = dcb->dcb_pbstat;
- break;
- case AUDIO_GET_PLAYGAIN:
- *ivp = dcb->dcb_rvol > dcb->dcb_lvol ? dcb->dcb_rvol : dcb->dcb_lvol;
- break;
- case AUDIO_SET_PLAYGAIN:
- dcb->dcb_lvol = *ivp;
- dcb->dcb_rvol = *ivp;
- dcb->dcb_scmd |= VSREQ_VOLUPD;
- break;
- case AUDIO_SET_TREB:
- dcb->dcb_treb = *ivp;
- dcb->dcb_scmd |= VSREQ_AUDIOE;
- break;
- case AUDIO_SET_TFIN:
- dcb->dcb_tfin = *ivp;
- dcb->dcb_scmd |= VSREQ_AUDIOE;
- break;
- case AUDIO_SET_BASS:
- dcb->dcb_bass = *ivp;
- dcb->dcb_scmd |= VSREQ_AUDIOE;
- break;
- case AUDIO_SET_BFIN:
- dcb->dcb_bfin = *ivp;
- dcb->dcb_scmd |= VSREQ_AUDIOE;
- break;
- case AUDIO_GET_PBSIZE:
- *lvp = NutSegBufAvailable() + NutSegBufUsed();
- break;
- case AUDIO_SET_PBSIZE:
- rc = VsDecoderBufferInit(dev, *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_SETWRITETIMEOUT:
- dcb->dcb_wtmo = *lvp;
- break;
- case AUDIO_GETWRITETIMEOUT:
- *lvp = dcb->dcb_wtmo;
- break;
- case AUDIO_BEEP:
- if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
- dcb->dcb_sinefreq = *((uint16_t *) conf);
- dcb->dcb_scmd |= VSREQ_BEEP;
- NutEventPost(&dcb->dcb_feedme);
- } else {
- rc = -1;
- }
- break;
- case AUDIO_GET_DECCAPS:
- /* Retrieve decoder capabilities. */
- *lvp = dcb->dcb_dec_caps;
- break;
- case AUDIO_GET_CODCAPS:
- /* Retrieve encoder capabilities. */
- *lvp = dcb->dcb_cod_caps;
- break;
- case AUDIO_GET_MIDCAPS:
- /* Retrieve midi capabilities. */
- *lvp = dcb->dcb_midi_caps;
- break;
- #if 0
- case AUDIO_GET_DECINFO:
- /* Retrieve decoder information. */
- break;
- case AUDIO_GET_CODINFO:
- /* Retrieve encoder information. */
- break;
- case AUDIO_GET_MIDINFO:
- /* Retrieve midi information. */
- break;
- #endif
- case AUDIO_PLUGIN_UPLOAD:
- rc = VsCodecLoadPlugIn(dev, (VS_PLUGIN_INFO *) conf);
- break;
- case AUDIO_WRITE_CMEM:
- VsCodecWriteWRam(dev, (VS_WRAM_DATA *) conf);
- break;
- default:
- rc = (*dcb->dcb_control)(req, conf);
- break;
- }
- return rc;
- }
- /*!
- * \brief Flush VLSI audio decoder buffer.
- *
- * Waits until all currently buffered data had been processed by the
- * decoder. This makes sure that the end of the stream will not be
- * cut off.
- *
- * \param dev Specifies the audio codec device.
- * \param tmo Timeout in milliseconds.
- *
- * \return 0 on success, -1 on timeout.
- */
- static int VsDecoderBufferFlush(NUTDEVICE *dev, uint32_t tmo)
- {
- int rc = 0;
- VSDCB *dcb = dev->dev_dcb;
- for (;;) {
- /* Keep the player running if the buffer contains data. */
- if (NutSegBufUsed()) {
- VsCodecIOCtl(dev, AUDIO_PLAY, NULL);
- }
- /* No data in buffer. If idle, then we are done. */
- else if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
- /* No data and player is idle. */
- break;
- }
- /* Wait for a new buffer update. */
- rc = NutEventWait(&dcb->dcb_bufque, tmo);
- if (rc) {
- break;
- }
- }
- return rc;
- }
- /*!
- * \brief Read from the encoder.
- *
- * \param nfp Pointer to a \ref NUTFILE structure, obtained by a previous
- * call to VsCodecOpen().
- * \param data Pointer to the data buffer. If NULL, the buffered data
- * will be flushed.
- * \param len Number of bytes to read. If 0, all buffered data will be
- * flushed.
- *
- * \return Number of characters sent. If a read timeout had been set,
- * then this may be less than the specified length.
- */
- int VsCodecRead(NUTFILE * nfp, void *data, int len)
- {
- int rc = 0;
- uint8_t *bp;
- uint8_t *dp;
- size_t rbytes;
- NUTDEVICE *dev;
- VSDCB *dcb;
- dev = nfp->nf_dev;
- dcb = dev->dev_dcb;
- /* Flush buffer if data pointer is a NULL pointer. */
- if (data == NULL || len == 0) {
- return 0;
- }
- if (dcb->dcb_pbstat == CODEC_STATUS_PLAYING) {
- /* Cannot read decoder data. */
- return -1;
- } else if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
- /* Start recording if idle. */
- if (dcb->dcb_cod_mode & AUDIO_FMT_VORBIS) {
- }
- dcb->dcb_scmd |= VSREQ_RECORD;
- NutEventPost(&dcb->dcb_feedme);
- }
- dp = data;
- while (len) {
- /* Retrieve new data from the input buffer. */
- bp = (uint8_t *) NutSegBufReadRequest(&rbytes);
- if (rbytes) {
- if (rbytes > len) {
- rbytes = len;
- }
- memcpy(dp, bp, rbytes);
- NutSegBufReadLast(rbytes);
- NutEventPost(&dcb->dcb_feedme);
- len -= rbytes;
- rc += rbytes;
- dp += rbytes;
- }
- /* Wait for data. */
- else if (NutEventWait(&dcb->dcb_bufque, dcb->dcb_rtmo)) {
- /* Read timeout. */
- break;
- }
- }
- return rc;
- }
- /*!
- * \brief Write to decoder.
- *
- * \param nfp Pointer to a \ref NUTFILE structure, obtained by a previous
- * call to VsCodecOpen().
- * \param data Pointer to the data buffer. If NULL, the buffered data
- * will be flushed.
- * \param len Number of bytes that are available in the buffer. If 0,
- * all buffered data will be flushed.
- *
- * \return Number of characters sent. If a write timeout had been set,
- * then this may be less than the specified length.
- */
- int VsCodecWrite(NUTFILE * nfp, const void *data, int len)
- {
- int rc = 0;
- uint8_t *bp;
- const uint8_t *dp;
- size_t rbytes;
- VSDCB *dcb = nfp->nf_dev->dev_dcb;
- /* Flush buffer if data pointer is a NULL pointer. */
- if (data == NULL || len == 0) {
- return VsDecoderBufferFlush(nfp->nf_dev, dcb->dcb_wtmo);
- }
- dp = data;
- while (len) {
- bp = (uint8_t *)NutSegBufWriteRequest(&rbytes);
- if (rbytes == 0) {
- /* No buffer space, wait for change. */
- if (NutEventWait(&dcb->dcb_bufque, dcb->dcb_wtmo)) {
- /* Write timeout. */
- break;
- }
- } else {
- if (rbytes > len) {
- rbytes = len;
- }
- memcpy(bp, dp, rbytes);
- NutSegBufWriteLast(rbytes);
- NutEventPost(&dcb->dcb_feedme);
- len -= rbytes;
- rc += rbytes;
- dp += rbytes;
- }
- }
- return rc;
- }
- #ifdef __HARVARD_ARCH__
- /*!
- * \brief Write program data to decoder.
- *
- * Similar to VsCodecWrite() 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 VsCodecOpen().
- * \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.
- *
- */
- int VsCodecWrite_P(NUTFILE * nfp, PGM_P buffer, int len)
- {
- return -1;
- }
- #endif
- /*
- * Open codec stream.
- */
- NUTFILE *VsCodecOpen(NUTDEVICE * dev, const char *name, int mode, int acc)
- {
- NUTFILE *nfp;
- VSDCB *dcb;
- #if defined(VS_SW_RESET_ON_OPEN)
- VsCodecMode(dev, VS_SM_RESET, VS_SM_RESET);
- #endif
- dcb = dev->dev_dcb;
- if (mode & _O_WRONLY) {
- uint32_t on = 1;
- /* Decoder. */
- if (VsCodecIOCtl(dev, AUDIO_IRQ_ENABLE, &on)) {
- return NULL;
- }
- } else {
- /* Encoder. */
- if (strcmp(name, "vorbis") == 0) {
- dcb->dcb_cod_mode = AUDIO_FMT_VORBIS;
- } else {
- dcb->dcb_cod_mode = AUDIO_FMT_WAV_IMA_ADPCM;
- }
- }
- nfp = malloc(sizeof(NUTFILE));
- if (nfp) {
- nfp->nf_dev = dev;
- nfp->nf_fcb = NULL;
- }
- NutSegBufReset();
- return nfp;
- }
- /*
- * Close codec stream.
- */
- int VsCodecClose(NUTFILE * nfp)
- {
- VSDCB *dcb = nfp->nf_dev->dev_dcb;
- int rc = VsDecoderBufferFlush(nfp->nf_dev, dcb->dcb_wtmo);
- if (nfp) {
- free(nfp);
- }
- return rc;
- }
- /*@}*/
|