summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-08-04 19:26:43 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-08-04 19:26:43 -0600
commit76fe1ce3468022a914bc7f5dc69a44dea5edc398 (patch)
treeacc3f87f686a23290f6fec8d9d94632fec062855
parentfd6f4cdf8fd57d006fba669fc62d7f7eaa420493 (diff)
downloadnuttx-76fe1ce3468022a914bc7f5dc69a44dea5edc398.tar.gz
nuttx-76fe1ce3468022a914bc7f5dc69a44dea5edc398.tar.bz2
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.c190
-rw-r--r--nuttx/drivers/audio/wm8904.h23
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 */