spi_vscodec.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2008-2009 by egnite GmbH
00003  * Copyright (C) 2001-2007 by egnite Software GmbH
00004  *
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted provided that the following conditions
00009  * are met:
00010  *
00011  * 1. Redistributions of source code must retain the above copyright
00012  *    notice, this list of conditions and the following disclaimer.
00013  * 2. Redistributions in binary form must reproduce the above copyright
00014  *    notice, this list of conditions and the following disclaimer in the
00015  *    documentation and/or other materials provided with the distribution.
00016  * 3. Neither the name of the copyright holders nor the names of
00017  *    contributors may be used to endorse or promote products derived
00018  *    from this software without specific prior written permission.
00019  *
00020  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00027  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00028  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00029  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00030  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00031  * SUCH DAMAGE.
00032  *
00033  * For additional information see http://www.ethernut.de/
00034  */
00035 
00036 /*
00037  * $Id: spi_vscodec.c 2721 2009-09-29 17:44:58Z haraldkipp $
00038  */
00039 
00040 #include <dev/vscodec.h>
00041 
00042 #include <sys/event.h>
00043 #include <sys/timer.h>
00044 #include <sys/nutdebug.h>
00045 #include <sys/bankmem.h>
00046 
00047 #include <stdlib.h>
00048 #include <string.h>
00049 #include <memdebug.h>
00050 
00055 
00059 #define VSREQ_PLAY      0x00000001
00060 
00061 #define VSREQ_CANCEL    0x00000002
00062 
00063 #define VSREQ_VOLUPD    0x00000004
00064 
00065 #define VSREQ_BEEP      0x00000008
00066 
00069 /* Used to send zeros to the decoder. */
00070 uint8_t zero_chunk[VSCODEC_DATA_CHUNK_SIZE];
00071 
00080 int VsCodecWaitReady(NUTDEVICE *dev, uint32_t tmo)
00081 {
00082     VSDCB *dcb = (VSDCB *) dev->dev_dcb;
00083 
00084     while (!(* dcb->dcb_isready)()) {
00085         if (NutEventWait(&dcb->dcb_feedme, tmo)) {
00086             return -1;
00087         }
00088     }
00089     return 0;
00090 }
00091 
00092 /*
00093  * \brief Read from or write to a VLSI audio codec register.
00094  *
00095  * \param dev  Specifies the codec device.
00096  * \param op   Operation, either \ref VS_OPCODE_READ or
00097  *             \ref VS_OPCODE_WRITE.
00098  * \param reg  The register index.
00099  * \param val  This value will be stored in the register on a write
00100  *             operation. It is ignored on read operations.
00101  *
00102  * \return Register contents. On write operations, the contents before
00103  *         the modification is returned.
00104  */
00105 uint16_t VsCodecReg(NUTDEVICE *dev, uint_fast8_t op, uint_fast8_t reg, uint_fast16_t val)
00106 {
00107     uint8_t cmd[4];
00108     VSDCB *dcb = (VSDCB *) dev->dev_dcb;
00109 
00110     /* Assemble command buffer. */
00111     cmd[0] = (uint8_t) op;
00112     cmd[1] = (uint8_t) reg;
00113     cmd[2] = (uint8_t) (val >> 8);
00114     cmd[3] = (uint8_t) val;
00115 
00116     VsCodecWaitReady(dev, VSCODEC_CMD_TIMEOUT);
00117     (*dcb->dcb_sendcmd)(cmd, 4);
00118     val = cmd[2];
00119     val <<= 8;
00120     val |= cmd[3];
00121     return (uint16_t) val;
00122 }
00123 
00136 uint16_t VsCodecMode(NUTDEVICE *dev, uint_fast16_t flags, uint_fast16_t mask)
00137 {
00138     uint16_t rc;
00139 
00140     /* Read the mode register. */
00141     rc = VsCodecReg(dev, VS_OPCODE_READ, VS_MODE_REG, 0);
00142     if (mask | flags) {
00143         VsCodecReg(dev, VS_OPCODE_WRITE, VS_MODE_REG, (rc & ~mask) | flags);
00144         /* Add delay after software reset, if configured. */
00145 #if defined(VSCODEC_SWRST_RECOVER)
00146         if (flags & VS_SM_RESET) {
00147             NutSleep(VSCODEC_SWRST_RECOVER);
00148         }
00149 #endif
00150     }
00151     return rc;
00152 }
00153 
00163 int VsDecoderSetVolume(NUTDEVICE *dev, int left, int right)
00164 {
00165     VSDCB *dcb = (VSDCB *)dev->dev_dcb;
00166     uint16_t l;
00167     uint16_t r;
00168 
00169     /* Honor limits. */
00170     left = left > AUDIO_DAC_MAX_GAIN ? AUDIO_DAC_MAX_GAIN : left;
00171     left = left < AUDIO_DAC_MIN_GAIN ? AUDIO_DAC_MIN_GAIN : left;
00172     right = right > AUDIO_DAC_MAX_GAIN ? AUDIO_DAC_MAX_GAIN : right;
00173     right = right < AUDIO_DAC_MIN_GAIN ? AUDIO_DAC_MIN_GAIN : right;
00174 
00175     /* Convert to register values. */
00176     l = (uint16_t)(-2 * left);
00177     r = (uint16_t)(-2 * right);
00178 
00179     VsCodecReg(dev, VS_OPCODE_WRITE, VS_VOL_REG, (l << VS_VOL_LEFT_LSB) | (r << VS_VOL_RIGHT_LSB));
00180     dcb->dcb_lvol = left;
00181     dcb->dcb_rvol = right;
00182 
00183     return 0;
00184 }
00185 
00197 uint16_t VsCodecBeep(NUTDEVICE *dev, uint16_t fsin)
00198 {
00199     uint16_t rc = 0;
00200     static uint8_t on[] = { 0x53, 0xEF, 0x6E, 0x3F, 0x00, 0x00, 0x00, 0x00 };
00201     static CONST uint8_t off[] = { 0x45, 0x78, 0x69, 0x74, 0x00, 0x00, 0x00, 0x00 };
00202     static CONST uint16_t ftab[] = { 44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000 };
00203     static uint16_t mode;
00204     VSDCB *dcb = (VSDCB *)dev->dev_dcb;
00205 
00206     if (fsin) {
00207         uint_fast16_t s;
00208         uint32_t f;
00209         uint_fast16_t d;
00210         int_fast8_t i = sizeof(ftab) / sizeof(ftab[0]);
00211         int_fast8_t ie = 0;
00212         int_fast16_t dmin = fsin;
00213         uint32_t fs = (long)fsin * 128;
00214 
00215         while (--i >= ie) {
00216             /* Calculate skip speed. */
00217             f = ftab[i];
00218             s = (fs + f / 2) / f;
00219             /* Check that skip is within 5-bit range. */
00220             if (s && s < 32) {
00221                 /* Calculate the absolute deviation. */
00222                 f *= s;
00223                 f += 64;
00224                 f >>= 7;
00225                 d = (uint_fast16_t)(fsin > f ? fsin - f : f - fsin);
00226                 /* Check if this is a new minimum. */
00227                 if (d < dmin) {
00228                     dmin = d;
00229                     rc = (uint16_t) f;
00230                     /* Set new skip speed and frequency index. */
00231                     on[3] = (i << 5) | s;
00232                     /* We can stop earlier with this fit. */
00233                     if (i > 1) {
00234                         ie = i - 2;
00235                     }
00236                 }
00237             }
00238         }
00239         mode = VsCodecMode(dev, VS_SM_RESET, VS_SM_RESET);
00240 #ifdef VS_SM_TESTS
00241         VsCodecMode(dev, mode | VS_SM_TESTS, 0xffff);
00242 #endif
00243         (*dcb->dcb_senddata)(on, sizeof(on));
00244     } else {
00245         (*dcb->dcb_senddata)(off, sizeof(off));
00246 #ifdef VS_SM_TESTS
00247         VsCodecMode(dev, VS_SM_RESET, VS_SM_RESET | VS_SM_TESTS);
00248 #else
00249         VsCodecMode(dev, VS_SM_RESET, VS_SM_RESET);
00250 #endif
00251         VsCodecMode(dev, mode, 0xffff);
00252     }
00253     return rc;
00254 }
00255 
00256 /*
00257  * Initialize the stream output buffer with a given size.
00258  *
00259  * \param dev Specifies the audio codec device.
00260  */
00261 int VsDecoderBufferInit(NUTDEVICE *dev, uint32_t size)
00262 {
00263     VSDCB *dcb = (VSDCB *)dev->dev_dcb;
00264 
00265     /* Make sure that the decoder is idle. */
00266     if (dcb->dcb_pbstat != CODEC_STATUS_IDLE) {
00267         return -1;
00268     }
00269     /* Set new buffer size. */
00270     if (NutSegBufInit((size_t)size) == NULL) {
00271         return -1;
00272     }
00273     /* Set watermark defaults. */
00274     dcb->dcb_pbwlo = NutSegBufAvailable() / 3;
00275     dcb->dcb_pbwhi = dcb->dcb_pbwlo * 2;
00276 
00277     return 0;
00278 }
00279 
00280 /*
00281  * VLSI decoder feeding thread.
00282  */
00283 THREAD(FeederThread, arg)
00284 {
00285     uint8_t *bp;
00286     size_t avail;
00287     int filled;
00288     uint_fast8_t intest = 0;
00289     uint_fast16_t idlefill = 0;
00290     NUTDEVICE *dev = (NUTDEVICE *)arg;
00291     VSDCB *dcb = (VSDCB *)dev->dev_dcb;
00292 
00293     /* We are a high priority thread. */
00294     NutThreadSetPriority(7);
00295     for (;;) {
00296         /* 
00297         ** Idle mode processing. 
00298         */
00299         if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
00300             /* Loop while in test mode. */
00301             do {
00302                 /* Wait for a request or a buffer update. */
00303                 NutEventWait(&dcb->dcb_feedme, NUT_WAIT_INFINITE);
00304                 /* Process beep commands. */
00305                 if (dcb->dcb_scmd & VSREQ_BEEP) {
00306                     if ((*dcb->dcb_isready)()) {
00307                         dcb->dcb_scmd &= ~VSREQ_BEEP;
00308                         intest = VsCodecBeep(dev, dcb->dcb_sinefreq) != 0;
00309                     }
00310                 }
00311             } while (intest);
00312             /* Check if we should change to play mode. This will happen
00313             ** if we receive a play request or if the buffer contents
00314             ** reaches the high watermark. */
00315             if ((dcb->dcb_scmd & VSREQ_PLAY) != 0 || NutSegBufUsed() >=  dcb->dcb_pbwhi) {
00316                 dcb->dcb_scmd &= ~(VSREQ_PLAY | VSREQ_CANCEL);
00317                 dcb->dcb_pbstat = CODEC_STATUS_PLAYING;
00318                 VsDecoderSetVolume(dev, dcb->dcb_lvol, dcb->dcb_rvol);
00319             }
00320         }
00321 
00322         /* 
00323         ** Play mode processing. 
00324         */
00325         if (dcb->dcb_pbstat == CODEC_STATUS_PLAYING) {
00326             /* Wait while decoder is busy. */
00327             VsCodecWaitReady(dev, NUT_WAIT_INFINITE);
00328             /* Process cancel requests. */
00329             if (dcb->dcb_scmd & VSREQ_CANCEL) {
00330                 dcb->dcb_scmd &= ~VSREQ_CANCEL;
00331                 NutSegBufReset();
00332 #if VS_HAS_SM_CANCEL
00333                 VsCodecMode(dev, VS_SM_CANCEL, VS_SM_CANCEL);
00334 #endif
00335             }
00336             /* Retrieve new data from the input buffer. */
00337             bp = (uint8_t *) NutSegBufReadRequest(&avail);
00338             if (avail) {
00339                 /* More data available, send as much as possible to the
00340                 ** decoder. If no data was sent, then the bus may be
00341                 ** blocked by another device. */
00342                 filled = (*dcb->dcb_senddata)(bp, avail);
00343                 if (filled) {
00344                     /* Update the buffer's read position. */
00345                     NutSegBufReadLast(filled);
00346                     NutEventPost(&dcb->dcb_bufque);
00347                     idlefill = 2048;
00348                 }
00349                 if (dcb->dcb_scmd & VSREQ_VOLUPD) {
00350                     VsDecoderSetVolume(dev, dcb->dcb_lvol, dcb->dcb_rvol);
00351                     dcb->dcb_scmd &= ~VSREQ_VOLUPD;
00352                 }
00353             } else if (NutSegBufUsed() == 0) {
00354                 /* Running out of data. */
00355                 if (idlefill) {
00356                     /* For some reason the HDAT0/HDAT1 registers in the
00357                     ** VS1053 do not contain zero when the codec runs
00358                     ** empty. I expected this when reading the datasheet.
00359                     ** Instead we fill the buffer with zeros, to make 
00360                     ** sure that the whole buffer is decoded before we 
00361                     ** enter idle mode. */
00362                     idlefill -= (*dcb->dcb_senddata)(NULL, idlefill);
00363                 }
00364                 if (idlefill == 0) {
00365                     /* Finally we reached idle mode. */
00366                     dcb->dcb_pbstat = CODEC_STATUS_IDLE;
00367                     NutEventPost(&dcb->dcb_bufque);
00368                 }
00369             }
00370         }
00371     }
00372 }
00373 
00395 int VsCodecIOCtl(NUTDEVICE * dev, int req, void *conf)
00396 {
00397     int rc = 0;
00398     uint32_t *lvp = (uint32_t *) conf;
00399     int *ivp = (int *) conf;
00400     VSDCB *dcb = (VSDCB *)dev->dev_dcb;
00401 
00402     switch (req) {
00403     case AUDIO_PLAY:
00404         if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
00405             /* Immediately start playing. */
00406             dcb->dcb_scmd |= VSREQ_PLAY;
00407         }
00408         NutEventPost(&dcb->dcb_feedme);
00409         break;
00410     case AUDIO_CANCEL:
00411         if (dcb->dcb_pbstat == CODEC_STATUS_PLAYING) {
00412             /* Immediately stop playing and discard buffer. */
00413             dcb->dcb_scmd |= VSREQ_CANCEL;
00414         }
00415         NutEventPost(&dcb->dcb_feedme);
00416         break;
00417     case AUDIO_GET_STATUS:
00418         *ivp = dcb->dcb_pbstat;
00419         break;
00420     case AUDIO_GET_PLAYGAIN:
00421         *ivp = dcb->dcb_rvol > dcb->dcb_lvol ? dcb->dcb_rvol : dcb->dcb_lvol;
00422         break;
00423     case AUDIO_SET_PLAYGAIN:
00424         dcb->dcb_lvol = *ivp;
00425         dcb->dcb_rvol = *ivp;
00426         dcb->dcb_scmd |= VSREQ_VOLUPD;
00427         break;
00428     case AUDIO_GET_PBSIZE:
00429         *lvp = NutSegBufAvailable() + NutSegBufUsed();
00430         break;
00431     case AUDIO_SET_PBSIZE:
00432         rc = VsDecoderBufferInit(dev, *lvp);
00433         break;
00434     case AUDIO_GET_PBLEVEL:
00435         *lvp = NutSegBufUsed();
00436         break;
00437     case AUDIO_GET_PBWLOW:
00438         *lvp = dcb->dcb_pbwlo;
00439         break;
00440     case AUDIO_SET_PBWLOW:
00441         dcb->dcb_pbwlo = *lvp;
00442         break;
00443     case AUDIO_GET_PBWHIGH:
00444         *lvp = dcb->dcb_pbwhi;
00445         break;
00446     case AUDIO_SET_PBWHIGH:
00447         dcb->dcb_pbwhi = *lvp;
00448         break;
00449     case AUDIO_SETWRITETIMEOUT:
00450         dcb->dcb_wtmo = *lvp;
00451         break;
00452     case AUDIO_GETWRITETIMEOUT:
00453         *lvp = dcb->dcb_wtmo;
00454         break;
00455     case AUDIO_BEEP:
00456         if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
00457             dcb->dcb_sinefreq = *((uint16_t *) conf);
00458             dcb->dcb_scmd |= VSREQ_BEEP;
00459             NutEventPost(&dcb->dcb_feedme);
00460         } else {
00461             rc = -1;
00462         }
00463         break;
00464 #if 0
00465     case AUDIO_GET_DECINFO:
00466         /* Retrieve decoder information. */
00467         break;
00468     case AUDIO_GET_DECCAPS:
00469         /* Retrieve decoder capabilities. */
00470         break;
00471     case AUDIO_GET_DECFMTS:
00472         /* Retrieve decoder formats. */
00473         break;
00474     case AUDIO_SET_DECFMTS:
00475         /* Enable or disable specific decoder formats. */
00476         break;
00477     case AUDIO_GET_CODINFO:
00478         /* Retrieve encoder information. */
00479         break;
00480     case AUDIO_GET_CODCAPS:
00481         /* Retrieve encoder capabilities. */
00482         break;
00483     case AUDIO_GET_CODFMTS:
00484         /* Retrieve encoder formats. */
00485         break;
00486     case AUDIO_SET_CODFMTS:
00487         /* Enable or disable specific encoder formats. */
00488         break;
00489     case AUDIO_GET_MIDINFO:
00490         /* Retrieve midi information. */
00491         break;
00492     case AUDIO_GET_MIDCAPS:
00493         /* Retrieve midi capabilities. */
00494         break;
00495 #endif
00496     default:
00497         rc = -1;
00498         break;
00499     }
00500     return rc;
00501 }
00502 
00515 static int VsDecoderBufferFlush(NUTDEVICE *dev, uint32_t tmo)
00516 {
00517     int rc = 0;
00518     VSDCB *dcb = dev->dev_dcb;
00519 
00520     for (;;) {
00521         /* Keep the player running if the buffer contains data. */
00522         if (NutSegBufUsed()) {
00523             VsCodecIOCtl(dev, AUDIO_PLAY, NULL);
00524         }
00525         /* No data in buffer. If idle, then we are done. */
00526         else if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
00527             /* No data and player is idle. */
00528             break;
00529         }
00530         /* Wait for a new buffer update. */
00531         rc = NutEventWait(&dcb->dcb_bufque, tmo);
00532         if (rc) {
00533             break;
00534         }
00535     }
00536     return rc;
00537 }
00538 
00552 int VsCodecWrite(NUTFILE * nfp, CONST void *data, int len)
00553 {
00554     int rc = 0;
00555     uint8_t *bp;
00556     CONST uint8_t *dp;
00557     size_t rbytes;
00558     VSDCB *dcb = nfp->nf_dev->dev_dcb;
00559 
00560     /* Flush buffer if data pointer is a NULL pointer. */
00561     if (data == NULL || len == 0) {
00562         return VsDecoderBufferFlush(nfp->nf_dev, dcb->dcb_wtmo);
00563     }
00564     dp = data;
00565     while (len) {
00566         bp = (uint8_t *)NutSegBufWriteRequest(&rbytes);
00567         if (rbytes == 0) {
00568             /* No buffer space, wait for change. */
00569             if (NutEventWait(&dcb->dcb_bufque, dcb->dcb_wtmo)) {
00570                 /* Write timeout. */
00571                 break;
00572             }
00573         } else {
00574             if (rbytes > len) {
00575                 rbytes = len;
00576             }
00577             memcpy(bp, dp, rbytes);
00578             NutSegBufWriteLast(rbytes);
00579             NutEventPost(&dcb->dcb_feedme);
00580             len -= rbytes;
00581             rc += rbytes;
00582             dp += rbytes;
00583         }
00584     }
00585     return rc;
00586 }
00587 
00588 #ifdef __HARVARD_ARCH__
00589 
00611 int VsCodecWrite_P(NUTFILE * nfp, PGM_P buffer, int len)
00612 {
00613     return -1;
00614 }
00615 #endif
00616 
00617 /*
00618  * Open codec stream.
00619  */
00620 NUTFILE *VsCodecOpen(NUTDEVICE * dev, CONST char *name, int mode, int acc)
00621 {
00622     NUTFILE *nfp;
00623 
00624 #if defined(VS_SW_RESET_ON_OPEN)
00625     VsCodecMode(dev, VS_SM_RESET, VS_SM_RESET);
00626 #endif
00627 
00628     nfp = malloc(sizeof(NUTFILE));
00629     if (nfp) {
00630         nfp->nf_next = NULL;
00631         nfp->nf_dev = dev;
00632         nfp->nf_fcb = NULL;
00633     }
00634 
00635     NutSegBufReset();
00636 
00637     return nfp;
00638 }
00639 
00640 /* 
00641  * Close codec stream.
00642  */
00643 int VsCodecClose(NUTFILE * nfp)
00644 {
00645     VSDCB *dcb = nfp->nf_dev->dev_dcb;
00646 
00647     int rc = VsDecoderBufferFlush(nfp->nf_dev, dcb->dcb_wtmo);
00648 
00649     if (nfp) {
00650         free(nfp);
00651     }
00652     return rc;
00653 }
00654 

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