1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900 |
- /****************************************************************************
- * drivers/audio/vs1053.c
- *
- * Audio device driver for VLSI Solutions VS1053 Audio codec.
- *
- * Copyright (C) 2013 Ken Pettit. All rights reserved.
- * Author: Ken Pettit <pettitkd@gmail.com>
- *
- * 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 NuttX nor the names of its 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.
- *
- ****************************************************************************/
- /****************************************************************************
- * Included Files
- ****************************************************************************/
- #include <nuttx/config.h>
- #include <sys/types.h>
- #include <sys/ioctl.h>
- #include <stdint.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <fcntl.h>
- #include <string.h>
- #include <debug.h>
- #include <errno.h>
- #include <queue.h>
- #include <nuttx/kmalloc.h>
- #include <nuttx/signal.h>
- #include <nuttx/mqueue.h>
- #include <nuttx/fs/fs.h>
- #include <nuttx/fs/ioctl.h>
- #include <nuttx/audio/audio.h>
- #include <nuttx/audio/vs1053.h>
- #include <nuttx/lib/math.h>
- #include "vs1053.h"
- /****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
- #ifndef CONFIG_VS1053_SPIMODE
- # define CONFIG_VS1053_SPIMODE SPIDEV_MODE0
- #endif
- #ifndef CONFIG_VS1053_XTALI
- # define CONFIG_VS1053_XTALI 12288000
- #endif
- #ifndef CONFIG_VS1053_MP3_DECODE_FREQ
- # define CONFIG_VS1053_MP3_DECODE_FREQ 43000000
- #endif
- #ifndef CONFIG_VS1053_MSG_PRIO
- # define CONFIG_VS1053_MSG_PRIO 1
- #endif
- #ifndef CONFIG_VS1053_BUFFER_SIZE
- # define CONFIG_VS1053_BUFFER_SIZE 8192
- #endif
- #ifndef CONFIG_VS1053_NUM_BUFFERS
- # define CONFIG_VS1053_NUM_BUFFERS 2
- #endif
- #ifndef CONFIG_VS1053_WORKER_STACKSIZE
- # define CONFIG_VS1053_WORKER_STACKSIZE 768
- #endif
- #define VS1053_DUMMY 0xFF
- #define VS1053_DEFAULT_XTALI 12288000
- #define VS1053_DATA_FREQ 20000000
- #define VS1053_RST_USECS 2000
- /****************************************************************************
- * Private Types
- ****************************************************************************/
- struct vs1053_struct_s
- {
- /* We are an audio lower half driver */
- struct audio_lowerhalf_s lower;
- /* Our specific driver data goes here */
- const FAR struct vs1053_lower_s *hw_lower; /* Pointer to the hardware lower functions */
- FAR struct spi_dev_s *spi; /* Pointer to the SPI bus */
- FAR struct ap_buffer_s *apb; /* Pointer to the buffer we are processing */
- struct dq_queue_s apbq; /* Our queue for enqueued buffers */
- unsigned long spi_freq; /* Frequency to run the SPI bus at. */
- unsigned long chip_freq; /* Current chip frequency */
- mqd_t mq; /* Message queue for receiving messages */
- char mqname[16]; /* Our message queue name */
- pthread_t threadid; /* ID of our thread */
- sem_t apbq_sem; /* Audio Pipeline Buffer Queue sem access */
- #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
- int16_t volume; /* Current volume level */
- #ifndef CONFIG_AUDIO_EXCLUDE_BALANCE
- int16_t balance; /* Current balance level */
- #endif /* CONFIG_AUDIO_EXCLUDE_BALANCE */
- #endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
- #ifndef CONFIG_AUDIO_EXCLUDE_TONE
- uint8_t bass; /* Bass level */
- uint8_t treble; /* Bass level */
- #endif
- uint16_t endfillbytes;
- uint8_t endfillchar; /* Fill char to send when no more data */
- bool running;
- bool paused;
- bool endmode;
- #ifndef CONFIG_AUDIO_EXCLUDE_STOP
- bool cancelmode;
- #endif
- bool busy; /* Set true when device reserved */
- };
- /****************************************************************************
- * Private Function Prototypes
- ****************************************************************************/
- static int vs1053_getcaps(FAR struct audio_lowerhalf_s *lower, int type,
- FAR struct audio_caps_s *pCaps);
- static int vs1053_shutdown(FAR struct audio_lowerhalf_s *lower);
- #ifdef CONFIG_AUDIO_MULTI_SESSION
- static int vs1053_configure(FAR struct audio_lowerhalf_s *lower,
- FAR void *session, FAR const struct audio_caps_s *pCaps);
- static int vs1053_start(FAR struct audio_lowerhalf_s *lower,
- FAR void *session);
- #ifndef CONFIG_AUDIO_EXCLUDE_STOP
- static int vs1053_stop(FAR struct audio_lowerhalf_s *lower,
- FAR void *session);
- #endif
- #ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
- static int vs1053_pause(FAR struct audio_lowerhalf_s *lower,
- FAR void *session);
- static int vs1053_resume(FAR struct audio_lowerhalf_s *lower,
- FAR void *session);
- #endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */
- static int vs1053_reserve(FAR struct audio_lowerhalf_s *lower,
- FAR void** ppContext);
- static int vs1053_release(FAR struct audio_lowerhalf_s *lower,
- FAR void *pContext);
- #else
- static int vs1053_configure(FAR struct audio_lowerhalf_s *lower,
- FAR const struct audio_caps_s *pCaps);
- static int vs1053_start(FAR struct audio_lowerhalf_s *lower);
- #ifndef CONFIG_AUDIO_EXCLUDE_STOP
- static int vs1053_stop(FAR struct audio_lowerhalf_s *lower);
- #endif
- #ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
- static int vs1053_pause(FAR struct audio_lowerhalf_s *lower);
- static int vs1053_resume(FAR struct audio_lowerhalf_s *lower);
- #endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */
- static int vs1053_reserve(FAR struct audio_lowerhalf_s *lower);
- static int vs1053_release(FAR struct audio_lowerhalf_s *lower);
- #endif /* CONFIG_AUDIO_MULTI_SESION */
- static int vs1053_enqueuebuffer(FAR struct audio_lowerhalf_s *lower,
- FAR struct ap_buffer_s *apb);
- static int vs1053_cancelbuffer(FAR struct audio_lowerhalf_s *lower,
- FAR struct ap_buffer_s *apb);
- static int vs1053_ioctl(FAR struct audio_lowerhalf_s *lower, int cmd,
- unsigned long arg);
- /****************************************************************************
- * Private Data
- ****************************************************************************/
- static const struct audio_ops_s g_audioops =
- {
- vs1053_getcaps, /* getcaps */
- vs1053_configure, /* configure */
- vs1053_shutdown, /* shutdown */
- vs1053_start, /* start */
- #ifndef CONFIG_AUDIO_EXCLUDE_STOP
- vs1053_stop, /* stop */
- #endif
- #ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
- vs1053_pause, /* pause */
- vs1053_resume, /* resume */
- #endif
- NULL, /* alloc_buffer */
- NULL, /* free_buffer */
- vs1053_enqueuebuffer, /* enqueue_buffer */
- vs1053_cancelbuffer, /* cancel_buffer */
- vs1053_ioctl, /* ioctl */
- NULL, /* read */
- NULL, /* write */
- vs1053_reserve, /* reserve */
- vs1053_release /* release */
- };
- /* Volume control log table. This table is in increments of 2% of
- * requested volume level and is the register value that should be
- * programmed to the VS1053 to achieve that volume pecentage.
- */
- #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
- static const uint8_t g_logtable [] =
- {
- 254, 170, 140, 122, 110, /* 0 - 8 */
- 100, 92, 85, 80, 74, /* 10 - 18 */
- 70, 66, 62, 59, 55, /* 20 - 28 */
- 52, 49, 47, 44, 42, /* 30 - 38 */
- 40, 38, 36, 34, 32, /* 40 - 48 */
- 30, 28, 27, 25, 24, /* 50 - 58 */
- 22, 21, 19, 18, 17, /* 60 - 68 */
- 15, 14, 13, 12, 11, /* 70 - 78 */
- 10, 9, 8, 7, 6, /* 80 - 88 */
- 5, 4, 3, 2, 1, /* 90 - 98 */
- 0 /* 100 */
- };
- #endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
- /****************************************************************************
- * Private Functions
- ****************************************************************************/
- /************************************************************************************
- * Name: vs1053_spi_lock
- ************************************************************************************/
- static void vs1053_spi_lock(FAR struct spi_dev_s *dev, unsigned long freq_mhz)
- {
- /* On SPI busses where there are multiple devices, it will be necessary to
- * lock SPI to have exclusive access to the busses for a sequence of
- * transfers. The bus should be locked before the chip is selected.
- *
- * This is a blocking call and will not return until we have exclusiv access to
- * the SPI buss. We will retain that exclusive access until the bus is unlocked.
- */
- (void)SPI_LOCK(dev, true);
- /* After locking the SPI bus, the we also need call the setfrequency, setbits, and
- * setmode methods to make sure that the SPI is properly configured for the device.
- * If the SPI buss is being shared, then it may have been left in an incompatible
- * state.
- */
- SPI_SETMODE(dev, CONFIG_VS1053_SPIMODE);
- SPI_SETBITS(dev, 8);
- (void)SPI_HWFEATURES(dev, 0);
- (void)SPI_SETFREQUENCY(dev, freq_mhz);
- }
- /************************************************************************************
- * Name: vs1053_spi_unlock
- ************************************************************************************/
- static inline void vs1053_spi_unlock(FAR struct spi_dev_s *dev)
- {
- (void)SPI_LOCK(dev, false);
- }
- /************************************************************************************
- * Name: vs1053_readreg - Read the specified 16-bit register from the
- * VS1053 device. Caller must hold the SPI lock.
- ************************************************************************************/
- static uint16_t vs1053_readreg(FAR struct vs1053_struct_s *dev, uint8_t reg)
- {
- uint16_t ret;
- FAR struct spi_dev_s *spi = dev->spi;
- /* Select the AUDIO_CTRL device on the SPI bus */
- SPI_SELECT(spi, SPIDEV_AUDIO_CTRL(0), true);
- /* Send the WRITE command followed by the address */
- SPI_SEND(spi, VS1053_OPCODE_READ);
- SPI_SEND(spi, reg);
- /* Now read the 16-bit value */
- ret = SPI_SEND(spi, VS1053_DUMMY) << 8;
- ret |= SPI_SEND(spi, VS1053_DUMMY);
- /* Deselect the CODEC */
- SPI_SELECT(spi, SPIDEV_AUDIO_CTRL(0), false);
- return ret;
- }
- /************************************************************************************
- * Name: vs1053_writereg - Write the specified 16-bit register to the
- * VS1053 device. Caller must hold the SPI lock.
- ************************************************************************************/
- static void vs1053_writereg(FAR struct vs1053_struct_s *dev, uint8_t reg, uint16_t val)
- {
- FAR struct spi_dev_s *spi = dev->spi;
- /* Select the AUDIO_CTRL device on the SPI bus */
- audinfo("Write Reg %d = 0x%0X\n", reg, val);
- SPI_SELECT(spi, SPIDEV_AUDIO_CTRL(0), true);
- /* Send the WRITE command followed by the address */
- SPI_SEND(spi, VS1053_OPCODE_WRITE);
- SPI_SEND(spi, reg);
- /* Now read the 16-bit value */
- SPI_SEND(spi, val >> 8);
- SPI_SEND(spi, val & 0xFF);
- /* Deselect the CODEC */
- SPI_SELECT(spi, SPIDEV_AUDIO_CTRL(0), false);
- /* Short delay after a write for VS1053 processing time */
- nxsig_usleep(10);
- }
- /****************************************************************************
- * Name: vs1053_setfrequency
- *
- * Description: Get the audio device capabilities
- *
- ****************************************************************************/
- static int vs1053_setfrequency(FAR struct vs1053_struct_s *dev, uint32_t freq)
- {
- double factor;
- uint16_t reg;
- uint8_t timeout;
- audinfo("Entry\n");
- /* Calculate the clock divisor based on the input frequency */
- factor = (double) freq / (double) CONFIG_VS1053_XTALI * 10.0 + 0.5;
- /* Check the input frequency against bounds */
- if (factor > 50.0)
- {
- audinfo("Frequency too high! Limiting to XTALI * 5\n");
- factor = 50.0;
- return -EINVAL;
- }
- if (factor < 10.0)
- {
- factor = 10.0;
- }
- /* Calculate the clock mulit register based on the factor */
- if ((int) factor == 10)
- {
- reg = 0;
- }
- else
- {
- reg = (((int) factor - 15) / 5) << VS1053_SC_MULT_SHIFT;
- }
- /* Set the MULT_ADD factor to the max to allow the chip to dynamically
- * increase the frequency the maximum amount as needed
- */
- reg |= (VS1053_SC_ADD_XTALIx20 << VS1053_SC_ADD_SHIFT);
- /* If we aren't running with a 12.228Mhz input crystal, then we
- * must tell the chip what the frequency is
- */
- if (CONFIG_VS1053_XTALI != VS1053_DEFAULT_XTALI)
- {
- /* Calculate register value based on equation: (XTALI - 8000000) / 4000
- * per the datasheet.
- */
- reg |= (CONFIG_VS1053_XTALI - 8000000) / 4000;
- }
- /* Now set the new clock multiplier register */
- vs1053_writereg(dev, VS1053_SCI_CLOCKF, reg);
- /* Wait for DREQ to go active */
- timeout = 200;
- while (!dev->hw_lower->read_dreq(dev->hw_lower) && timeout)
- {
- nxsig_usleep(1000);
- timeout--;
- }
- /* Update our internal variables */
- dev->chip_freq = freq;
- dev->spi_freq = freq / 7;
- return OK;
- }
- /************************************************************************************
- * Name: vs1053_logapprox - Approximate the register value in .5 dB increments
- * level based on the percentage using a log table since
- * math libraries aren't available.
- ************************************************************************************/
- #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
- uint8_t vs1053_logapprox(int percent)
- {
- /* Check percentage for bounds */
- if (percent >= 100)
- {
- return 0;
- }
- return (g_logtable[percent >> 1] + g_logtable[(percent+1) >> 1]) >> 1;
- }
- #endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
- /************************************************************************************
- * Name: vs1053_setvolume - Set the right and left volume values in the VS1053
- * device based on the current volume and balance settings.
- ************************************************************************************/
- #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
- static void vs1053_setvolume(FAR struct vs1053_struct_s *dev)
- {
- FAR struct spi_dev_s *spi = dev->spi;
- uint32_t leftlevel;
- uint32_t rightlevel;
- uint8_t leftreg, rightreg;
- /* Constrain balance */
- #ifndef CONFIG_AUDIO_EXCLUDE_BALANCE
- if (dev->balance > 1000)
- {
- dev->balance = 1000;
- }
- /* Calculate the left channel volume level */
- if (dev->balance <= 500)
- {
- leftlevel = dev->volume;
- }
- else if (dev->balance == 1000)
- {
- leftlevel = 0;
- }
- else
- {
- leftlevel = dev->volume * (1000 - dev->balance) / 500;
- }
- /* Calculate the right channel volume level */
- if (dev->balance >= 500)
- {
- rightlevel = dev->volume;
- }
- else if (dev->balance == 0)
- {
- rightlevel = 0;
- }
- else
- {
- rightlevel = dev->volume * dev->balance / 500;
- }
- #else
- leftlevel = rightlevel = dev->volume;
- #endif
- /* Calculate the left and right register values */
- /* The register sets the volume in dB which is a logrithmic scale,
- * so we must use log() to calculate the register value.
- */
- leftreg = vs1053_logapprox(leftlevel / 10);
- rightreg = vs1053_logapprox(rightlevel / 10);
- /* Lock the SPI bus to get exclusive access to the chip. */
- vs1053_spi_lock(spi, dev->spi_freq);
- vs1053_writereg(dev, VS1053_SCI_VOL, (leftreg << 8) | rightreg);
- vs1053_spi_unlock(spi);
- }
- #endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
- /************************************************************************************
- * Name: vs1053_setbass - Set the bass and treble level as specified in the
- * context's bass and treble variables..
- *
- * The level and range are in whole percentage levels (0-100).
- *
- ************************************************************************************/
- #ifndef CONFIG_AUDIO_EXCLUDE_TONE
- static void vs1053_setbass(FAR struct vs1053_struct_s *dev)
- {
- FAR struct spi_dev_s *spi = dev->spi;
- int bass_range, bass_boost;
- int treble_range, treble_boost;
- /* Calculate range and boost based on level */
- bass_boost = 15 * dev->bass / 100;
- bass_range = 15;
- treble_boost = 15 * dev->treble / 100;
- treble_range = 15;
- /* Lock the SPI bus to get exclsive access to the chip. */
- vs1053_spi_lock(spi, dev->spi_freq);
- vs1053_writereg(dev, VS1053_SCI_BASS, (treble_boost << 12) | (treble_range << 8) |
- (bass_boost << 4) | bass_range);
- vs1053_spi_unlock(spi);
- }
- #endif /* CONFIG_AUDIO_EXCLUDE_TONE */
- /****************************************************************************
- * Name: vs1053_getcaps
- *
- * Description: Get the audio device capabilities
- *
- ****************************************************************************/
- static int vs1053_getcaps(FAR struct audio_lowerhalf_s *lower, int type,
- FAR struct audio_caps_s *pCaps)
- {
- audinfo("Entry\n");
- /* Validate the structure */
- DEBUGASSERT(pCaps->ac_len >= sizeof(struct audio_caps_s));
- /* Fill in the caller's structure based on requested info */
- pCaps->ac_format.hw = 0;
- pCaps->ac_controls.w = 0;
- switch (pCaps->ac_type)
- {
- /* Caller is querying for the types of units we support */
- case AUDIO_TYPE_QUERY:
- /* Provide our overall capabilities. The interfacing software
- * must then call us back for specific info for each capability.
- */
- pCaps->ac_channels = 2; /* Stereo output */
- switch (pCaps->ac_subtype)
- {
- case AUDIO_TYPE_QUERY:
- /* The input formats we can decode / accept */
- pCaps->ac_format.hw = 0
- #ifdef CONFIG_AUDIO_FORMAT_AC3
- | (1 << (AUDIO_FMT_AC3 - 1))
- #endif
- #ifdef CONFIG_AUDIO_FORMAT_MP3
- | (1 << (AUDIO_FMT_MP3 - 1))
- #endif
- #ifdef CONFIG_AUDIO_FORMAT_WMA
- | (1 << (AUDIO_FMT_WMA - 1))
- #endif
- #ifdef CONFIG_AUDIO_FORMAT_MIDI
- | (1 << (AUDIO_FMT_MIDI - 1))
- #endif
- #ifdef CONFIG_AUDIO_FORMAT_PCM
- | (1 << (AUDIO_FMT_PCM - 1))
- #endif
- #ifdef CONFIG_AUDIO_FORMAT_OGG_VORBIS
- | (1 << (AUDIO_FMT_OGG_VORBIS - 1))
- #endif
- ;
- /* The types of audio units we implement */
- pCaps->ac_controls.b[0] = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_FEATURE |
- AUDIO_TYPE_PROCESSING;
- break;
- /* Report sub-formats for MIDI if requested */
- #ifdef CONFIG_AUDIO_FORMAT_MIDI
- case AUDIO_FMT_MIDI:
- /* We only support Format 0 */
- pCaps->ac_controls.b[0] = AUDIO_SUBFMT_MIDI_0;
- pCaps->ac_controls.b[1] = AUDIO_SUBFMT_END;
- break;
- #endif
- default:
- pCaps->ac_controls.b[0] = AUDIO_SUBFMT_END;
- break;
- }
- break;
- /* Provide capabilities of our OUTPUT unit */
- case AUDIO_TYPE_OUTPUT:
- pCaps->ac_channels = 2;
- switch (pCaps->ac_subtype)
- {
- case AUDIO_TYPE_QUERY:
- /* Report the Sample rates we support */
- pCaps->ac_controls.b[0] = AUDIO_SAMP_RATE_8K | AUDIO_SAMP_RATE_11K |
- AUDIO_SAMP_RATE_16K | AUDIO_SAMP_RATE_22K |
- AUDIO_SAMP_RATE_32K | AUDIO_SAMP_RATE_44K |
- AUDIO_SAMP_RATE_48K;
- break;
- case AUDIO_FMT_MP3:
- case AUDIO_FMT_WMA:
- case AUDIO_FMT_PCM:
- /* Report the Bit rates we support. The bit rate support is actually a
- * complex function of the format and selected sample rate, and the datasheet
- * has multiple tables to indicate the supported bit rate vs sample rate vs
- * format. The selected sample rate should be provided in the ac_format
- * field of the query, and only a single sample rate should be given.
- */
- /* TODO: Create a table or set of tables to report this! */
- break;
- default:
- break;
- }
- break;
- /* Provide capabilities of our FEATURE units */
- case AUDIO_TYPE_FEATURE:
- /* If the sub-type is UNDEF, then report the Feature Units we support */
- if (pCaps->ac_subtype == AUDIO_FU_UNDEF)
- {
- /* Fill in the ac_controls section with the Feature Units we have */
- pCaps->ac_controls.b[0] = AUDIO_FU_VOLUME | AUDIO_FU_BASS | AUDIO_FU_TREBLE;
- pCaps->ac_controls.b[1] = AUDIO_FU_BALANCE >> 8;
- }
- else
- {
- /* TODO: Do we need to provide specific info for the Feature Units,
- * such as volume setting ranges, etc.?
- */
- }
- break;
- /* Provide capabilities of our PROCESSING unit */
- case AUDIO_TYPE_PROCESSING:
- switch (pCaps->ac_subtype)
- {
- case AUDIO_PU_UNDEF:
- /* Provide the type of Processing Units we support */
- pCaps->ac_controls.b[0] = AUDIO_PU_STEREO_EXTENDER;
- break;
- case AUDIO_PU_STEREO_EXTENDER:
- /* Proivde capabilities of our Stereo Extender */
- pCaps->ac_controls.b[0] = AUDIO_STEXT_ENABLE | AUDIO_STEXT_WIDTH;
- break;
- default:
- /* Other types of processing uint we don't support */
- break;
- }
- break;
- /* All others we don't support */
- default:
- /* Zero out the fields to indicate no support */
- pCaps->ac_subtype = 0;
- pCaps->ac_channels = 0;
- break;
- }
- /* Return the length of the audio_caps_s struct for validation of
- * proper Audio device type.
- */
- return pCaps->ac_len;
- }
- /****************************************************************************
- * Name: vs1053_configure
- *
- * Description: Configure the audio device for the specified mode of
- * operation.
- *
- ****************************************************************************/
- #ifdef CONFIG_AUDIO_MULTI_SESSION
- static int vs1053_configure(FAR struct audio_lowerhalf_s *lower,
- FAR void *session, FAR const struct audio_caps_s *pCaps)
- #else
- static int vs1053_configure(FAR struct audio_lowerhalf_s *lower,
- FAR const struct audio_caps_s *pCaps)
- #endif
- {
- int ret = OK;
- #if !defined(CONFIG_AUDIO_EXCLUDE_VOLUME) || !defined(CONFIG_AUDIO_EXCLUDE_TONE)
- FAR struct vs1053_struct_s *dev = (struct vs1053_struct_s *) lower;
- #endif
- audinfo("Entry\n");
- /* Process the configure operation */
- switch (pCaps->ac_type)
- {
- case AUDIO_TYPE_FEATURE:
- /* Process based on Feature Unit */
- switch (pCaps->ac_format.hw)
- {
- #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
- case AUDIO_FU_VOLUME:
- /* Set the volume */
- dev->volume = pCaps->ac_controls.hw[0];
- vs1053_setvolume(dev);
- break;
- #endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
- #if !defined(CONFIG_AUDIO_EXCLUDE_TONE) && !defined(CONFIG_AUDIO_EXCLUDE_VOLUME)
- case AUDIO_FU_BALANCE:
- /* Set the volume */
- dev->balance = pCaps->ac_controls.hw[0];
- vs1053_setvolume(dev);
- break;
- #endif
- #ifndef CONFIG_AUDIO_EXCLUDE_TONE
- case AUDIO_FU_BASS:
- /* Set the bass. The percentage level (0-100) is in the
- * ac_controls[0] parameter.
- */
- dev->bass = pCaps->ac_controls.b[0];
- if (dev->bass > 100)
- dev->bass = 100;
- vs1053_setbass(dev);
- break;
- case AUDIO_FU_TREBLE:
- /* Set the treble. The percentage level (0-100) is in the
- * ac_controls.b[0] parameter.
- */
- dev->treble = pCaps->ac_controls.b[0];
- if (dev->treble > 100)
- dev->treble = 100;
- vs1053_setbass(dev);
- break;
- #endif /* CONFIG_AUDIO_EXCLUDE_TONE */
- default:
- /* Others we don't support */
- break;
- }
- break;
- case AUDIO_TYPE_OUTPUT:
- break;
- case AUDIO_TYPE_PROCESSING:
- /* We only support STEREO_EXTENDER */
- if (pCaps->ac_format.hw == AUDIO_PU_STEREO_EXTENDER)
- {
- }
- break;
- }
- return ret;
- }
- /****************************************************************************
- * Name: vs1053_softreset
- *
- * Description: Performs a soft reset on the VS1053 chip by setting the
- * RESET bit of the MODE register.
- *
- ****************************************************************************/
- static int vs1053_softreset(FAR struct vs1053_struct_s *dev)
- {
- uint16_t reg;
- uint16_t timeout;
- /* First disable interrupts, lower the frequency and lock the SPI bus */
- dev->hw_lower->disable(dev->hw_lower); /* Disable the DREQ interrupt */
- vs1053_spi_lock(dev->spi, VS1053_DEFAULT_XTALI / 7);
- /* Now issue a reset command */
- reg = vs1053_readreg(dev, VS1053_SCI_MODE);
- vs1053_writereg(dev, VS1053_SCI_MODE, reg | VS1053_SM_RESET);
- /* Now wait for the SM_RESET to go inactive */
- timeout = 1000;
- while (vs1053_readreg(dev, VS1053_SCI_MODE) & VS1053_SM_RESET && timeout)
- {
- timeout--;
- }
- /* Switch to low frequency, Unlock the SPI bus and exit */
- vs1053_setfrequency(dev, CONFIG_VS1053_XTALI);
- vs1053_spi_unlock(dev->spi);
- return OK;
- }
- /****************************************************************************
- * Name: vs1053_hardreset
- *
- * Description: Performs a hardware reset on the VS1053 chip by toggling
- * the RST line, disabling IRQ, and setting the default
- * XTALI frequency.
- *
- ****************************************************************************/
- static int vs1053_hardreset(FAR struct vs1053_struct_s *dev)
- {
- dev->hw_lower->disable(dev->hw_lower); /* Disable the DREQ interrupt */
- dev->hw_lower->reset(dev->hw_lower, false);
- nxsig_usleep(10);
- dev->hw_lower->reset(dev->hw_lower, true);
- nxsig_usleep(VS1053_RST_USECS);
- vs1053_setfrequency(dev, CONFIG_VS1053_XTALI); /* Slow speed at first */
- return OK;
- }
- /****************************************************************************
- * Name: vs1053_shutdown
- *
- * Description: Shutdown the VS1053 chip and put it in the lowest power
- * state possible.
- *
- ****************************************************************************/
- static int vs1053_shutdown(FAR struct audio_lowerhalf_s *lower)
- {
- FAR struct vs1053_struct_s *dev = (struct vs1053_struct_s *) lower;
- FAR struct spi_dev_s *spi = dev->spi;
- audinfo("Entry\n");
- vs1053_spi_lock(spi, dev->spi_freq); /* Lock the device */
- vs1053_setfrequency(dev, CONFIG_VS1053_XTALI); /* Reduce speed to minimum */
- vs1053_writereg(dev, VS1053_SCI_VOL, 0xFEFE); /* Power down the DAC outputs */
- vs1053_spi_unlock(spi); /* Unlock the device */
- return OK;
- }
- /****************************************************************************
- * Name: vs1053_feeddata
- *
- * Description: Feeds more data to the vs1053 chip from the enqueued
- * buffers. It will continue feeding data until the DREQ
- * line indicates it can't accept any more data.
- *
- ****************************************************************************/
- static void vs1053_feeddata(FAR struct vs1053_struct_s *dev)
- {
- int bytecount;
- int ret;
- uint8_t *pSamp = NULL;
- uint16_t reg;
- struct ap_buffer_s *apb;
- FAR struct spi_dev_s *spi = dev->spi;
- /* Check for false interrupt caused by an SCI transaction */
- if (!dev->hw_lower->read_dreq(dev->hw_lower) || dev->paused)
- {
- return;
- }
- /* Grab the SPI bus. We can run at 20Mhz because we increased the
- * chip frequency above 40Mhz for the decode operation.
- */
- vs1053_spi_lock(spi, VS1053_DATA_FREQ); /* Lock the SPI bus */
- SPI_SELECT(spi, SPIDEV_AUDIO_DATA(0), true); /* Select the VS1053 data bus */
- /* Local stack copy of our active buffer */
- apb = dev->apb;
- //audinfo("Entry apb=%p, Bytes left=%d\n", apb, apb->nbytes - apb->curbyte);
- /* Setup pointer to the next sample in the buffer */
- if (apb)
- {
- pSamp = &apb->samp[apb->curbyte];
- }
- else if (!dev->endmode)
- {
- SPI_SELECT(spi, SPIDEV_AUDIO_DATA(0), false);
- vs1053_spi_unlock(spi);
- return;
- }
- /* Loop until the FIFO is full */
- while (dev->hw_lower->read_dreq(dev->hw_lower))
- {
- /* If endmode, then send fill characters */
- if (dev->endmode)
- {
- bytecount = 32;
- while (bytecount)
- {
- SPI_SEND(spi, dev->endfillchar);
- bytecount--;
- }
- /* For the VS1053, after the file has been played, we must
- * send 2052 bytes of endfillchar per the datasheet.
- */
- dev->endfillbytes += 32;
- /* Process end mode logic. We send 2080 bytes of endfillchar as
- * directed by the datasheet, then set SM_CANCEL. Then we wait
- * until the chip clears SM_CANCEL while sending endfillchar
- * 32 bytes at a time.
- */
- if (dev->endfillbytes == 32*65)
- {
- /* After at least 2052 bytes, we send an SM_CANCEL */
- dev->hw_lower->disable(dev->hw_lower); /* Disable the DREQ interrupt */
- (void)SPI_SETFREQUENCY(dev->spi, dev->spi_freq);
- reg = vs1053_readreg(dev, VS1053_SCI_MODE);
- vs1053_writereg(dev, VS1053_SCI_MODE, reg | VS1053_SM_CANCEL);
- dev->hw_lower->enable(dev->hw_lower); /* Enable the DREQ interrupt */
- }
- else if (dev->endfillbytes >= 32*130)
- {
- /* Do a hard reset and terminate */
- vs1053_hardreset(dev);
- dev->running = false;
- dev->endmode = false;
- break;
- }
- else if (dev->endfillbytes > 32*65)
- {
- /* After each 32 byte of endfillchar, check the status
- * register to see if SM_CANCEL has been cleared. If
- * it has been cleared, then we're done.
- */
- if (!(vs1053_readreg(dev, VS1053_SCI_STATUS) & VS1053_SM_CANCEL))
- {
- (void)SPI_SETFREQUENCY(dev->spi, dev->spi_freq);
- dev->hw_lower->disable(dev->hw_lower); /* Disable the DREQ interrupt */
- audinfo("HDAT1: 0x%0X HDAT0: 0x%0X\n",
- vs1053_readreg(dev, VS1053_SCI_HDAT1),
- vs1053_readreg(dev, VS1053_SCI_HDAT0));
- vs1053_writereg(dev, VS1053_SCI_WRAMADDR, VS1053_END_FILL_BYTE);
- dev->endfillchar = vs1053_readreg(dev, VS1053_SCI_WRAM) >> 8;
- audinfo("EndFillChar: 0x%0X\n", dev->endfillchar);
- reg = vs1053_readreg(dev, VS1053_SCI_MODE);
- vs1053_writereg(dev, VS1053_SCI_MODE, reg | VS1053_SM_RESET);
- dev->running = false;
- dev->endmode = false;
- break;
- }
- }
- }
- else
- {
- /* Send 32 more bytes. We only send 32 at a time because this is
- * the meaning of DREQ active from the chip ... that it can
- * accept at least 32 more bytes. After each 32 byte block, we
- * will recheck the DREQ line again.
- */
- bytecount = apb->nbytes - apb->curbyte;
- if (bytecount > 32)
- {
- bytecount = 32;
- }
- #if 1
- SPI_SNDBLOCK(spi, pSamp, bytecount);
- pSamp += bytecount;
- #else
- bytecount = bytecount;
- while (bytecount--)
- {
- /* Send next byte from the buffer */
- SPI_SEND(spi, *pSamp);
- pSamp++;
- }
- #endif
- apb->curbyte += bytecount;
- /* Test if we are in cancel mode. If we are, then we need
- * to continue sending file data and check for the SM_CANCEL
- * bit going inactive.
- */
- #ifndef CONFIG_AUDIO_EXCLUDE_STOP
- if (dev->cancelmode)
- {
- /* Read the VS1053 MODE register */
- dev->hw_lower->disable(dev->hw_lower); /* Disable the DREQ interrupt */
- (void)SPI_SETFREQUENCY(dev->spi, dev->spi_freq);
- reg = vs1053_readreg(dev, VS1053_SCI_MODE);
- (void)SPI_SETFREQUENCY(dev->spi, VS1053_DATA_FREQ);
- dev->hw_lower->enable(dev->hw_lower); /* Enable the DREQ interrupt */
- /* Check the SM_CANCEL bit */
- if (!(reg & VS1053_SM_CANCEL))
- {
- /* Cancel has begun. Switch to endmode */
- apb->nbytes = 0;
- apb->curbyte = 0;
- }
- }
- #endif /* CONFIG_AUDIO_EXCLUDE_STOP */
- /* Test if we are at the end of the buffer */
- if (apb->curbyte >= apb->nbytes)
- {
- /* Check if this was the final buffer in stream */
- if ((apb->flags & AUDIO_APB_FINAL) != 0)
- {
- /* This is the final buffer. Get the VS1053 endfillchar */
- dev->hw_lower->disable(dev->hw_lower); /* Disable the DREQ interrupt */
- (void)SPI_SETFREQUENCY(dev->spi, dev->spi_freq);
- vs1053_writereg(dev, VS1053_SCI_WRAMADDR, VS1053_END_FILL_BYTE);
- dev->endfillchar = vs1053_readreg(dev, VS1053_SCI_WRAM) >> 8;
- (void)SPI_SETFREQUENCY(dev->spi, VS1053_DATA_FREQ);
- dev->hw_lower->enable(dev->hw_lower); /* Enable the DREQ interrupt */
- /* Mark the device as endmode */
- dev->endmode = true;
- #ifndef CONFIG_AUDIO_EXCLUDE_STOP
- if (dev->cancelmode)
- {
- /* If we are in cancel mode, then we don't dequeue the buffer
- * or need to send another SM_CANCEL, so jump into the middle
- * of the stop sequence.
- */
- dev->endfillbytes = 32*65+1;
- continue;
- }
- else
- #endif /* CONFIG_AUDIO_EXCLUDE_STOP */
- {
- dev->endfillbytes = 0;
- }
- }
- /* We referenced the buffer so we must free it */
- apb_free(apb);
- #ifdef CONFIG_AUDIO_MULTI_SESSION
- dev->lower.upper(dev->lower.priv, AUDIO_CALLBACK_DEQUEUE,
- apb, OK, NULL);
- #else
- dev->lower.upper(dev->lower.priv, AUDIO_CALLBACK_DEQUEUE,
- apb, OK);
- #endif
- /* Lock the buffer queue to pop the next buffer */
- if ((ret = nxsem_wait(&dev->apbq_sem)) < 0)
- {
- #ifdef CONFIG_AUDIO_MULTI_SESSION
- dev->lower.upper(dev->lower.priv,
- AUDIO_CALLBACK_IOERR, NULL, ret, NULL);
- #else
- dev->lower.upper(dev->lower.priv,
- AUDIO_CALLBACK_IOERR, NULL, ret);
- #endif
- auderr("ERROR: I/O error!\n");
- goto err_out;
- }
- /* Pop the next entry */
- apb = (struct ap_buffer_s *) dq_remfirst(&dev->apbq);
- dev->apb = apb;
- //audinfo("Next Buffer = %p, bytes = %d\n", apb, apb ? apb->nbytes : 0);
- if (apb == NULL)
- {
- nxsem_post(&dev->apbq_sem);
- break;
- }
- pSamp = &apb->samp[apb->curbyte];
- apb_reference(apb); /* Add our buffer reference */
- nxsem_post(&dev->apbq_sem);
- }
- }
- }
- /* Deselect the SPI bus and unlock it */
- err_out:
- SPI_SELECT(spi, SPIDEV_AUDIO_DATA(0), false);
- vs1053_spi_unlock(spi);
- }
- /****************************************************************************
- * Name: vs1053_dreq_isr
- *
- * This is the ISR that services the DREQ pin from the VS1053, which
- * indicates the chip is ready to receive additional data. We use it to
- * send a message to our workertherad message queue so it knows to wake
- * up and send more data.
- *
- ****************************************************************************/
- static int vs1053_dreq_isr(int irq, FAR void *context, FAR void *arg)
- {
- struct vs1053_struct_s *dev = (struct vs1053_struct_s *)arg;
- struct audio_msg_s msg;
- DEBUGASSERT(dev != NULL);
- /* Now create a message and send it to the workerthread */
- if (dev->running)
- {
- msg.msgId = AUDIO_MSG_DATA_REQUEST;
- (void)nxmq_send(dev->mq, (FAR const char *)&msg, sizeof(msg),
- CONFIG_VS1053_MSG_PRIO);
- }
- else
- {
- msg.msgId = AUDIO_MSG_DATA_REQUEST;
- }
- return 0;
- }
- /****************************************************************************
- * Name: vs1053_workerthread
- *
- * This is the thread that feeds data to the chip and keeps the audio
- * stream going.
- *
- ****************************************************************************/
- static void *vs1053_workerthread(pthread_addr_t pvarg)
- {
- FAR struct vs1053_struct_s *dev = (struct vs1053_struct_s *) pvarg;
- struct audio_msg_s msg;
- FAR struct ap_buffer_s *apb;
- int size;
- unsigned int prio;
- #ifndef CONFIG_AUDIO_EXCLUDE_STOP
- uint16_t reg;
- #endif
- uint8_t timeout;
- audinfo("Entry\n");
- #ifndef CONFIG_AUDIO_EXCLUDE_STOP
- dev->cancelmode = false;
- #endif
- dev->endmode = false;
- dev->endfillbytes = 0;
- /* Fill the VS1053 FIFO with initial data. */
- vs1053_feeddata(dev); /* Fill the VS1053 FIFO */
- /* Wait for DREQ to go active so we can issue a READ command */
- timeout = 200;
- while (!dev->hw_lower->read_dreq(dev->hw_lower) && timeout)
- {
- nxsig_usleep(100);
- timeout--;
- }
- /* Loop as long as we are supposed to be running */
- dev->running = true;
- dev->hw_lower->enable(dev->hw_lower); /* Enable the DREQ interrupt */
- while (dev->running || dev->endmode)
- {
- if (dev->hw_lower->read_dreq(dev->hw_lower))
- {
- vs1053_feeddata(dev); /* Feed more data to the VS1053 FIFO */
- }
- /* Wait for messages from our message queue */
- size = nxmq_receive(dev->mq, (FAR char *)&msg, sizeof(msg), &prio);
- /* Handle the case when we return with no message */
- if (size == 0)
- {
- /* Should we just stop running? */
- dev->running = false;
- break;
- }
- /* Process the message */
- switch (msg.msgId)
- {
- /* The ISR has requested more data */
- case AUDIO_MSG_DATA_REQUEST:
- nxsig_usleep(500);
- vs1053_feeddata(dev); /* Feed more data to the VS1053 FIFO */
- break;
- /* Stop the playback */
- #ifndef CONFIG_AUDIO_EXCLUDE_STOP
- case AUDIO_MSG_STOP:
- if (!dev->hw_lower->read_dreq(dev->hw_lower))
- {
- nxsig_usleep(300);
- }
- /* Send CANCEL message to VS1053 */
- dev->hw_lower->disable(dev->hw_lower);
- vs1053_spi_lock(dev->spi, dev->spi_freq);
- reg = vs1053_readreg(dev, VS1053_SCI_MODE);
- vs1053_writereg(dev, VS1053_SCI_MODE, reg | VS1053_SM_CANCEL);
- vs1053_spi_unlock(dev->spi);
- dev->hw_lower->enable(dev->hw_lower);
- /* Set cancelmode */
- dev->cancelmode = true;
- break;
- #endif
- /* We will wake up when a new buffer enqueued just in case */
- case AUDIO_MSG_ENQUEUE:
- break;
- default:
- break;
- }
- }
- /* Disable the DREQ interrupt */
- dev->hw_lower->disable(dev->hw_lower);
- /* Cancel any leftover buffer in our queue */
- if (nxsem_wait(&dev->apbq_sem) == OK)
- {
- /* Get the next buffer from the queue */
- while ((apb = (FAR struct ap_buffer_s *) dq_remfirst(&dev->apbq)) != NULL)
- ;
- }
- nxsem_post(&dev->apbq_sem);
- /* Free the active buffer */
- if (dev->apb != NULL)
- {
- apb_free(dev->apb);
- dev->apb = NULL;
- }
- /* Close the message queue */
- mq_close(dev->mq);
- mq_unlink(dev->mqname);
- dev->mq = NULL;
- /* Send an AUDIO_MSG_COMPLETE message to the client */
- #ifdef CONFIG_AUDIO_MULTI_SESSION
- dev->lower.upper(dev->lower.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK, NULL);
- #else
- dev->lower.upper(dev->lower.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK);
- #endif
- audinfo("Exit\n");
- return NULL;
- }
- /****************************************************************************
- * Name: vs1053_start
- *
- * Description: Start the configured operation (audio streaming, volume
- * enabled, etc.).
- *
- ****************************************************************************/
- #ifdef CONFIG_AUDIO_MULTI_SESSION
- static int vs1053_start(FAR struct audio_lowerhalf_s *lower, FAR void *session)
- #else
- static int vs1053_start(FAR struct audio_lowerhalf_s *lower)
- #endif
- {
- FAR struct vs1053_struct_s *dev = (struct vs1053_struct_s *) lower;
- struct mq_attr attr;
- struct sched_param sparam;
- pthread_attr_t tattr;
- int ret;
- void *value;
- audinfo("Entry\n");
- vs1053_spi_lock(dev->spi, dev->spi_freq); /* Lock the device */
- audinfo("Entry HDAT1=0x%0X HDAT0=0x%0X\n",
- vs1053_readreg(dev, VS1053_SCI_HDAT1),
- vs1053_readreg(dev, VS1053_SCI_HDAT0));
- vs1053_spi_unlock(dev->spi);
- /* Do a soft reset, just in case */
- vs1053_softreset(dev);
- /* Increase the frequency of the part during processing */
- vs1053_spi_lock(dev->spi, dev->spi_freq); /* Lock the device */
- vs1053_setfrequency(dev, CONFIG_VS1053_MP3_DECODE_FREQ);
- audinfo("Reset HDAT1=0x%0X HDAT0=0x%0X\n",
- vs1053_readreg(dev, VS1053_SCI_HDAT1),
- vs1053_readreg(dev, VS1053_SCI_HDAT0));
- vs1053_spi_unlock(dev->spi);
- /* Create a message queue for the worker thread */
- snprintf(dev->mqname, sizeof(dev->mqname), "/tmp/%X", dev);
- attr.mq_maxmsg = 16;
- attr.mq_msgsize = sizeof(struct audio_msg_s);
- attr.mq_curmsgs = 0;
- attr.mq_flags = 0;
- dev->mq = mq_open(dev->mqname, O_RDWR | O_CREAT, 0644, &attr);
- if (dev->mq == NULL)
- {
- /* Error creating message queue! */
- auderr("ERROR: Couldn't allocate message queue\n");
- return -ENOMEM;
- }
- /* Pop the first enqueued buffer */
- if ((ret = nxsem_wait(&dev->apbq_sem)) == OK)
- {
- dev->apb = (FAR struct ap_buffer_s *) dq_remfirst(&dev->apbq);
- apb_reference(dev->apb); /* Add our buffer reference */
- nxsem_post(&dev->apbq_sem);
- }
- else
- {
- auderr("ERROR: Error getting APB Queue sem\n");
- return ret;
- }
- /* Join any old worker thread we had created to prevent a memory leak */
- if (dev->threadid != 0)
- {
- audinfo("Joining old thread\n");
- pthread_join(dev->threadid, &value);
- }
- /* Start our thread for sending data to the device */
- pthread_attr_init(&tattr);
- sparam.sched_priority = sched_get_priority_max(SCHED_FIFO) - 3;
- (void)pthread_attr_setschedparam(&tattr, &sparam);
- (void)pthread_attr_setstacksize(&tattr, CONFIG_VS1053_WORKER_STACKSIZE);
- audinfo("Starting workerthread\n");
- ret = pthread_create(&dev->threadid, &tattr, vs1053_workerthread,
- (pthread_addr_t) dev);
- if (ret != OK)
- {
- auderr("ERROR: Can't create worker thread, errno=%d\n", errno);
- }
- else
- {
- pthread_setname_np(dev->threadid, "vs1053");
- audinfo("Created worker thread\n");
- }
- return ret;
- }
- /****************************************************************************
- * Name: vs1053_stop
- *
- * Description: Stop the configured operation (audio streaming, volume
- * disabled, etc.).
- *
- ****************************************************************************/
- #ifndef CONFIG_AUDIO_EXCLUDE_STOP
- #ifdef CONFIG_AUDIO_MULTI_SESSION
- static int vs1053_stop(FAR struct audio_lowerhalf_s *lower, FAR void *session)
- #else
- static int vs1053_stop(FAR struct audio_lowerhalf_s *lower)
- #endif
- {
- FAR struct vs1053_struct_s *dev = (struct vs1053_struct_s *) lower;
- struct audio_msg_s term_msg;
- FAR void *value;
- /* Send a message to stop all audio streaming */
- term_msg.msgId = AUDIO_MSG_STOP;
- term_msg.u.data = 0;
- (void)nxmq_send(dev->mq, (FAR const char *)&term_msg, sizeof(term_msg),
- CONFIG_VS1053_MSG_PRIO);
- /* Join the worker thread */
- pthread_join(dev->threadid, &value);
- dev->threadid = 0;
- /* Reduce the decoder's operating frequency to save power */
- vs1053_spi_lock(dev->spi, dev->spi_freq); /* Lock the device */
- vs1053_setfrequency(dev, CONFIG_VS1053_XTALI);
- vs1053_spi_unlock(dev->spi);
- /* Wait for a bit */
- up_mdelay(40);
- return OK;
- }
- #endif
- /****************************************************************************
- * Name: vs1053_pause
- *
- * Description: Pauses the playback.
- *
- ****************************************************************************/
- #ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
- #ifdef CONFIG_AUDIO_MULTI_SESSION
- static int vs1053_pause(FAR struct audio_lowerhalf_s *lower, FAR void *session)
- #else
- static int vs1053_pause(FAR struct audio_lowerhalf_s *lower)
- #endif
- {
- FAR struct vs1053_struct_s *dev = (struct vs1053_struct_s *) lower;
- if (!dev->running)
- {
- return OK;
- }
- /* Disable interrupts to prevent us from supplying any more data */
- dev->paused = true;
- dev->hw_lower->disable(dev->hw_lower); /* Disable the DREQ interrupt */
- return OK;
- }
- #endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */
- /****************************************************************************
- * Name: vs1053_resume
- *
- * Description: Resuems the playback.
- *
- ****************************************************************************/
- #ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
- #ifdef CONFIG_AUDIO_MULTI_SESSION
- static int vs1053_resume(FAR struct audio_lowerhalf_s *lower, FAR void *session)
- #else
- static int vs1053_resume(FAR struct audio_lowerhalf_s *lower)
- #endif
- {
- FAR struct vs1053_struct_s *dev = (struct vs1053_struct_s *) lower;
- if (!dev->running)
- {
- return OK;
- }
- /* Enable interrupts to allow suppling data */
- dev->paused = false;
- vs1053_feeddata(dev);
- dev->hw_lower->enable(dev->hw_lower); /* Enable the DREQ interrupt */
- return OK;
- }
- #endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */
- /****************************************************************************
- * Name: vs1053_enqueuebuffer
- *
- * Description: Enqueue an Audio Pipeline Buffer for playback/ processing.
- *
- ****************************************************************************/
- static int vs1053_enqueuebuffer(FAR struct audio_lowerhalf_s *lower,
- FAR struct ap_buffer_s *apb)
- {
- FAR struct vs1053_struct_s *dev = (struct vs1053_struct_s *)lower;
- struct audio_msg_s term_msg;
- int ret;
- audinfo("Entry\n");
- /* Lock access to the apbq */
- if ((ret = nxsem_wait(&dev->apbq_sem)) == OK)
- {
- /* We can now safely add the buffer to the queue */
- apb->curbyte = 0;
- apb->flags |= AUDIO_APB_OUTPUT_ENQUEUED;
- dq_addlast(&apb->dq_entry, &dev->apbq);
- nxsem_post(&dev->apbq_sem);
- /* Send a message indicating a new buffer enqueued */
- if (dev->mq != NULL)
- {
- term_msg.msgId = AUDIO_MSG_ENQUEUE;
- term_msg.u.data = 0;
- (void)nxmq_send(dev->mq, (FAR const char *)&term_msg,
- sizeof(term_msg), CONFIG_VS1053_MSG_PRIO);
- }
- }
- return ret;
- }
- /****************************************************************************
- * Name: vs1053_cancelbuffer
- *
- * Description: Called when an enqueued buffer is being cancelled.
- *
- ****************************************************************************/
- static int vs1053_cancelbuffer(FAR struct audio_lowerhalf_s *lower,
- FAR struct ap_buffer_s *apb)
- {
- return OK;
- }
- /****************************************************************************
- * Name: vs1053_ioctl
- *
- * Description: Perform a device ioctl
- *
- ****************************************************************************/
- static int vs1053_ioctl(FAR struct audio_lowerhalf_s *lower, int cmd,
- unsigned long arg)
- {
- #ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
- FAR struct ap_buffer_info_s *pBufInfo;
- #endif
- /* Deal with ioctls passed from the upper-half driver */
- switch (cmd)
- {
- /* Check for AUDIOIOC_HWRESET ioctl. This ioctl is passed straight
- * through from the upper-half audio driver.
- */
- case AUDIOIOC_HWRESET:
- vs1053_hardreset((FAR struct vs1053_struct_s *) lower);
- break;
- /* Report our preferred buffer size and quantity */
- #ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
- case AUDIOIOC_GETBUFFERINFO:
- pBufInfo = (FAR struct ap_buffer_info_s *) arg;
- pBufInfo->buffer_size = CONFIG_VS1053_BUFFER_SIZE;
- pBufInfo->nbuffers = CONFIG_VS1053_NUM_BUFFERS;
- break;
- #endif
- default:
- break;
- }
- return OK;
- }
- /****************************************************************************
- * Name: vs1053_reserve
- *
- * Description: Reserves a session (the only one we have).
- *
- ****************************************************************************/
- #ifdef CONFIG_AUDIO_MULTI_SESSION
- static int vs1053_reserve(FAR struct audio_lowerhalf_s *lower,
- FAR void **psession)
- #else
- static int vs1053_reserve(FAR struct audio_lowerhalf_s *lower)
- #endif
- {
- FAR struct vs1053_struct_s *dev = (struct vs1053_struct_s *) lower;
- int ret;
- /* Borrow the APBQ semaphore for thread sync */
- ret = nxsem_wait(&dev->apbq_sem);
- if (ret < 0)
- {
- return ret;
- }
- if (dev->busy)
- {
- ret = -EBUSY;
- }
- else
- {
- /* Initialize the session context. We don't really use it. */
- #ifdef CONFIG_AUDIO_MULTI_SESSION
- *psession = NULL;
- #endif
- dev->busy = true;
- dev->running = false;
- dev->paused = false;
- }
- nxsem_post(&dev->apbq_sem);
- return ret;
- }
- /****************************************************************************
- * Name: vs1053_release
- *
- * Description: Releases the session (the only one we have).
- *
- ****************************************************************************/
- #ifdef CONFIG_AUDIO_MULTI_SESSION
- static int vs1053_release(FAR struct audio_lowerhalf_s *lower,
- FAR void *psession)
- #else
- static int vs1053_release(FAR struct audio_lowerhalf_s *lower)
- #endif
- {
- FAR struct vs1053_struct_s *dev = (struct vs1053_struct_s *) lower;
- void *value;
- int ret;
- /* Join any old worker thread we had created to prevent a memory leak */
- if (dev->threadid != 0)
- {
- pthread_join(dev->threadid, &value);
- dev->threadid = 0;
- }
- /* Borrow the APBQ semaphore for thread sync */
- ret = nxsem_wait(&dev->apbq_sem);
- if (ret < 0)
- {
- return ret;
- }
- /* Really we should free any queued buffers here */
- dev->busy = false;
- nxsem_post(&dev->apbq_sem);
- return OK;
- }
- /****************************************************************************
- * Public Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: vs1053_initialize
- *
- * Description:
- * Initialize the VS1053 device
- *
- * Input Parameters:
- * spidevice - This is a placeholder argument until the Audio interface
- * has been flushed out a bit.
- *
- ****************************************************************************/
- struct audio_lowerhalf_s *vs1053_initialize(FAR struct spi_dev_s *spi,
- FAR const struct vs1053_lower_s *lower,
- unsigned int devno)
- {
- FAR struct vs1053_struct_s *dev;
- uint16_t status;
- uint8_t id;
- uint8_t retry;
- /* Sanity check */
- DEBUGASSERT(spi != NULL);
- DEBUGASSERT(lower != NULL);
- DEBUGASSERT(lower->reset != NULL);
- /* Allocate a VS1053 device structure */
- dev = (struct vs1053_struct_s *)kmm_malloc(sizeof(struct vs1053_struct_s));
- if (dev)
- {
- /* Initialize the VS1053 device structure */
- dev->lower.ops = &g_audioops;
- dev->lower.upper = NULL;
- dev->lower.priv = NULL;
- dev->hw_lower = lower;
- dev->spi_freq = CONFIG_VS1053_XTALI / 7;
- dev->spi = spi;
- dev->mq = NULL;
- dev->busy = false;
- dev->threadid = 0;
- dev->running = false;
- #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
- dev->volume = 250; /* 25% volume as default */
- #ifndef CONFIG_AUDIO_EXCLUDE_BALANCE
- dev->balance = 500; /* Center balance */
- #endif
- #endif
- #ifndef CONFIG_AUDIO_EXCLUDE_TONE
- dev->bass = 0;
- dev->treble = 0;
- #endif
- nxsem_init(&dev->apbq_sem, 0, 1);
- dq_init(&dev->apbq);
- /* Reset the VS1053 chip */
- lower->reset(lower, false);
- up_udelay(10);
- lower->reset(lower, true);
- up_udelay(VS1053_RST_USECS);
- #if CONFIG_VS1053_XTALI == VS1053_DEFAULT_XTALI
- /* If we have a standard crystal, then wait extra time
- * for the DREQ to be active indicating the device is ready
- */
- retry = 200;
- while (!lower->read_dreq(lower) && retry)
- {
- up_udelay(10);
- retry--;
- }
- #endif
- /* Do device detection to validate the chip is there.
- * We have to hold the SPI lock during reads / writes.
- */
- vs1053_spi_lock(spi, dev->spi_freq);
- status = vs1053_readreg(dev, VS1053_SCI_STATUS);
- vs1053_spi_unlock(spi);
- /* Validate the device ID read from the chip */
- id = (status & VS1053_SS_VER) >> VS1053_VER_SHIFT;
- if (id != VS1053_VER_VS1053)
- {
- auderr("ERROR: Unexpected VER bits: 0x%0X\n", id);
- kmm_free(dev);
- return NULL;
- }
- else
- {
- audinfo("VS1053 Detected!\n");
- }
- /* Attach our ISR to this device */
- dev->hw_lower->attach(dev->hw_lower, vs1053_dreq_isr, dev);
- /* Do some initialization of the codec */
- vs1053_shutdown(&dev->lower); /* Go to shutdown state */
- }
- return &dev->lower;
- }
|