diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2014-08-04 19:26:43 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2014-08-04 19:26:43 -0600 |
commit | 76fe1ce3468022a914bc7f5dc69a44dea5edc398 (patch) | |
tree | acc3f87f686a23290f6fec8d9d94632fec062855 | |
parent | fd6f4cdf8fd57d006fba669fc62d7f7eaa420493 (diff) | |
download | px4-nuttx-76fe1ce3468022a914bc7f5dc69a44dea5edc398.tar.gz px4-nuttx-76fe1ce3468022a914bc7f5dc69a44dea5edc398.tar.bz2 px4-nuttx-76fe1ce3468022a914bc7f5dc69a44dea5edc398.zip |
WM8904: Add reset logic to put the part back in its initial state after playing each WAV file. Base samles per second on frame length, not bits-per-sample. Use a different frame length for 8-bit and 16-bit data
-rw-r--r-- | nuttx/drivers/audio/wm8904.c | 190 | ||||
-rw-r--r-- | nuttx/drivers/audio/wm8904.h | 23 |
2 files changed, 134 insertions, 79 deletions
diff --git a/nuttx/drivers/audio/wm8904.c b/nuttx/drivers/audio/wm8904.c index 3b7c21da5..c74a9d641 100644 --- a/nuttx/drivers/audio/wm8904.c +++ b/nuttx/drivers/audio/wm8904.c @@ -184,6 +184,12 @@ static void wm8904_audio_output(FAR struct wm8904_dev_s *priv); #if 0 /* Not used */ static void wm8904_audio_input(FAR struct wm8904_dev_s *priv); #endif +#ifdef WM8904_USE_FFLOCK_INT +static void wm8904_configure_ints(FAR struct wm8904_dev_s *priv); +#else +# define wm8904_configure_ints(p) +#endif +static void wm8904_hw_reset(FAR struct wm8904_dev_s *priv); /**************************************************************************** * Private Data @@ -627,6 +633,7 @@ static void wm8904_setbitrate(FAR struct wm8904_dev_s *priv) unsigned int fllndx; unsigned int divndx; unsigned int outdiv; + unsigned int framelen; #ifdef WM8904_USE_FFLOCK_INT bool enabled; int retries; @@ -634,22 +641,21 @@ static void wm8904_setbitrate(FAR struct wm8904_dev_s *priv) DEBUGASSERT(priv && priv->lower); - /* First calculate the desired bitrate (fout) */ - -#if 0 - /* This is the correct calculation. However, the resulting bitrate is two - * times too fast??? + /* First calculate the desired bitrate (fout). This is based on + * + * 1. The I2S frame length (in bits) + * 2. The number of frames per second = nchannnels * samplerate */ - fout = (uint32_t)priv->samprate * (uint32_t)priv->nchannels * (uint32_t)priv->bpsamp; -#else - /* Ahhh.. much better */ + framelen = (priv->bpsamp == 8) ? WM8904_FRAMELEN8 : WM8904_FRAMELEN16; + fout = (uint32_t)priv->samprate * (uint32_t)priv->nchannels * framelen; - fout = (uint32_t)priv->samprate * (uint32_t)priv->bpsamp; -#endif + regval = WM8904_LRCLK_DIR | WM8904_LRCLK_RATE(framelen << 1); + wm8904_writereg(priv, WM8904_AIF3, regval); - audvdbg("sample rate=%u nchannels=%u bpsamp=%u fout=%lu\n", - priv->samprate, priv->nchannels, priv->bpsamp, (unsigned long)fout); + audvdbg("sample rate=%u nchannels=%u bpsamp=%u framelen=%d fout=%lu\n", + priv->samprate, priv->nchannels, priv->bpsamp, framelen, + (unsigned long)fout); /* Disable the SYSCLK. * @@ -1273,9 +1279,8 @@ static int wm8904_shutdown(FAR struct audio_lowerhalf_s *dev) /* Now issue a software reset. This puts all WM8904 registers back in * their default state. */ - /* REVISIT: But then the register configuration is lost. */ - /* wm8904_writereg(priv, WM8904_SWRST, 0); */ + wm8904_hw_reset(priv); return OK; } @@ -2068,15 +2073,9 @@ static void *wm8904_workerthread(pthread_addr_t pvarg) } } - /* Disable the WM8904 interrupt */ - - WM8904_DISABLE(priv->lower); - - /* Mute the volume and disable the FLL output */ + /* Reset the WM8904 hardware */ - wm8904_setvolume(priv, priv->volume, true); - wm8904_writereg(priv, WM8904_FLL_CTRL1, 0); - wm8904_writereg(priv, WM8904_DUMMY, 0x55aa); + wm8904_hw_reset(priv); /* Return any pending buffers in our pending queue */ @@ -2248,13 +2247,13 @@ static void wm8904_audio_output(FAR struct wm8904_dev_s *priv) /* Audio Interface 3 * - * Set LRCLK as an output with rate = BCLK / 64. This is a constant value - * used with audio. Since I2S will send a word on each edge of LRCLK (after - * a delay), this essentially means that each audio frame is 32 bits in - * length; + * Set LRCLK as an output with rate = BCLK / (2*WM8904_FRAMELENn). This is + * a value that varies with bits per sample, n=8 or 16. Since I2S will send + * a word on each edge of LRCLK (after a delay), this essentially means that + * each audio frame is WM8904_FRAMELENn bits in length. */ - regval = WM8904_LRCLK_DIR | WM8904_LRCLK_RATE(64); + regval = WM8904_LRCLK_DIR | WM8904_LRCLK_RATE(2*WM8904_FRAMELEN16); wm8904_writereg(priv, WM8904_AIF3, regval); /* DAC Digital 1 */ @@ -2308,7 +2307,7 @@ static void wm8904_audio_output(FAR struct wm8904_dev_s *priv) * function then modifies the configuration to support audio input. * * Input Parameters: - * prive - A reference to the driver state structure + * priv - A reference to the driver state structure * * Returned Value: * None. No failures are detected. @@ -2337,6 +2336,98 @@ static void wm8904_audio_input(FAR struct wm8904_dev_s *priv) #endif /**************************************************************************** + * Name: wm8904_configure_ints + * + * Description: + * Configure the GPIO/IRQ interrupt + * + * Input Parameters: + * priv - A reference to the driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef WM8904_USE_FFLOCK_INT +static void wm8904_configure_ints(FAR struct wm8904_dev_s *priv) +{ + uint16_t regval; + + /* Configure GPIO1 as an IRQ + * + * WM8904_GPIO1_PU=0 : No pull-up + * WM8904_GPIO1_PD=1 : Pulled-down + * WM8904_GPIO1_SEL_IRQ : Configured as IRQ + */ + + regval = (WM8904_GPIO1_SEL_IRQ | WM8904_GPIO1_PD); + wm8904_writereg(priv, WM8904_GPIO_CTRL1, regval); + + /* Attach our handler to the GPIO1/IRQ interrupt */ + + WM8904_ATTACH(lower, wm8904_interrupt, priv); + + /* Configure interrupts. wm8904_setbitrate() depends on FLL interrupts. */ + + wm8904_writereg(priv, WM8904_INT_STATUS, WM8904_ALL_INTS); + wm8904_writereg(priv, WM8904_INT_MASK, WM8904_ALL_INTS); + wm8904_writereg(priv, WM8904_INT_POL, 0); + wm8904_writereg(priv, WM8904_INT_DEBOUNCE, WM8904_ALL_INTS); +} +#endif + +/**************************************************************************** + * Name: wm8904_hw_reset + * + * Description: + * Reset and re-initialize the WM8904 + * + * Input Parameters: + * priv - A reference to the driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void wm8904_hw_reset(FAR struct wm8904_dev_s *priv) +{ + /* Put audio output back to its initial configuration */ + + priv->samprate = WM8904_DEFAULT_SAMPRATE; + priv->nchannels = WM8904_DEFAULT_NCHANNELS; + priv->bpsamp = WM8904_DEFAULT_BPSAMP; +#if !defined(CONFIG_AUDIO_EXCLUDE_VOLUME) && !defined(CONFIG_AUDIO_EXCLUDE_BALANCE) + priv->balance = b16HALF; /* Center balance */ +#endif + + /* Software reset. This puts all WM8904 registers back in their + * default state. + */ + + wm8904_writereg(priv, WM8904_SWRST, 0); + + /* Configure the WM8904 hardware as an audio input device */ + + wm8904_audio_output(priv); + + /* Configure interrupts */ + + wm8904_configure_ints(priv); + + /* Configure the FLL and the LRCLK */ + + wm8904_setbitrate(priv); + wm8904_writereg(priv, WM8904_DUMMY, 0x55aa); + + /* Dump some information and return the device instance */ + + wm8904_dump_registers(&priv->dev, "After configuration"); + wm8904_clock_analysis(&priv->dev, "After configuration"); +} + +/**************************************************************************** * Public Functions ****************************************************************************/ @@ -2381,12 +2472,6 @@ FAR struct audio_lowerhalf_s * priv->lower = lower; priv->i2c = i2c; priv->i2s = i2s; - priv->samprate = WM8904_DEFAULT_SAMPRATE; - priv->nchannels = WM8904_DEFAULT_NCHANNELS; - priv->bpsamp = WM8904_DEFAULT_BPSAMP; -#if !defined(CONFIG_AUDIO_EXCLUDE_VOLUME) && !defined(CONFIG_AUDIO_EXCLUDE_BALANCE) - priv->balance = b16HALF; /* Center balance */ -#endif sem_init(&priv->pendsem, 0, 1); dq_init(&priv->pendq); @@ -2414,42 +2499,9 @@ FAR struct audio_lowerhalf_s * goto errout_with_dev; } - /* Configure the WM8904 hardware as an audio input device */ - - wm8904_audio_output(priv); - -#ifdef WM8904_USE_FFLOCK_INT - /* Configure GPIO1 as an IRQ - * - * WM8904_GPIO1_PU=0 : No pull-up - * WM8904_GPIO1_PD=1 : Pulled-down - * WM8904_GPIO1_SEL_IRQ : Configured as IRQ - */ - - regval = (WM8904_GPIO1_SEL_IRQ | WM8904_GPIO1_PD); - wm8904_writereg(priv, WM8904_GPIO_CTRL1, regval); - - /* Attach our handler to the GPIO1/IRQ interrupt */ - - WM8904_ATTACH(lower, wm8904_interrupt, priv); - - /* Configure interrupts. wm8904_setbitrate() depends on FLL interrupts. */ - - wm8904_writereg(priv, WM8904_INT_STATUS, WM8904_ALL_INTS); - wm8904_writereg(priv, WM8904_INT_MASK, WM8904_ALL_INTS); - wm8904_writereg(priv, WM8904_INT_POL, 0); - wm8904_writereg(priv, WM8904_INT_DEBOUNCE, WM8904_ALL_INTS); -#endif - - /* Configure the FLL and the LRCLK */ - - wm8904_setbitrate(priv); - wm8904_writereg(priv, WM8904_DUMMY, 0x55aa); - - /* Dump some information and return the device instance */ + /* Reset and reconfigure the WM8904 hardwaqre */ - wm8904_dump_registers(&priv->dev, "After configuration"); - wm8904_clock_analysis(&priv->dev, "After configuration"); + wm8904_hw_reset(priv); return &priv->dev; } diff --git a/nuttx/drivers/audio/wm8904.h b/nuttx/drivers/audio/wm8904.h index 47544d306..fd3a83b10 100644 --- a/nuttx/drivers/audio/wm8904.h +++ b/nuttx/drivers/audio/wm8904.h @@ -1015,24 +1015,27 @@ /* FLL Configuration *********************************************************/ /* Default FLL configuration */ -#define WM8904_DEFAULT_SAMPRATE 11025 -#define WM8904_DEFAULT_NCHANNELS 1 -#define WM8904_DEFAULT_BPSAMP 16 +#define WM8904_DEFAULT_SAMPRATE 11025 /* Initial sample rate */ +#define WM8904_DEFAULT_NCHANNELS 1 /* Initial number of channels */ +#define WM8904_DEFAULT_BPSAMP 16 /* Initial bits per sample */ -#define WM8904_NFLLRATIO_DIV1 0 +#define WM8904_NFLLRATIO_DIV1 0 /* Values of the FLL_RATIO field */ #define WM8904_NFLLRATIO_DIV2 1 #define WM8904_NFLLRATIO_DIV4 2 #define WM8904_NFLLRATIO_DIV8 3 #define WM8904_NFLLRATIO_DIV16 4 -#define WM8904_NFLLRATIO 5 +#define WM8904_NFLLRATIO 5 /* Number of FLL_RATIO values */ -#define WM8904_MINOUTDIV 4 -#define WM8904_MAXOUTDIV 64 +#define WM8904_MINOUTDIV 4 /* Minimum FLL_OUTDIV divider */ +#define WM8904_MAXOUTDIV 64 /* Maximum FLL_OUTDIV divider */ -#define WM8904_BCLK_MAXDIV 20 +#define WM8904_BCLK_MAXDIV 20 /* Maximum BCLK divider */ -#define WM8904_FVCO_MIN 90000000 -#define WM8904_FVCO_MAX 100000000 +#define WM8904_FVCO_MIN 90000000 /* Minimum value of Fvco */ +#define WM8904_FVCO_MAX 100000000 /* Maximum value of Fvco */ + +#define WM8904_FRAMELEN8 14 /* Bits per frame for 8-bit data */ +#define WM8904_FRAMELEN16 32 /* Bits per frame for 16-bit data */ /* Commonly defined and redefined macros */ |