mirror of
https://github.com/apache/nuttx.git
synced 2026-06-01 16:59:28 +08:00
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
This commit is contained in:
+121
-69
@@ -184,6 +184,12 @@ static void wm8904_audio_output(FAR struct wm8904_dev_s *priv);
|
|||||||
#if 0 /* Not used */
|
#if 0 /* Not used */
|
||||||
static void wm8904_audio_input(FAR struct wm8904_dev_s *priv);
|
static void wm8904_audio_input(FAR struct wm8904_dev_s *priv);
|
||||||
#endif
|
#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
|
* Private Data
|
||||||
@@ -627,6 +633,7 @@ static void wm8904_setbitrate(FAR struct wm8904_dev_s *priv)
|
|||||||
unsigned int fllndx;
|
unsigned int fllndx;
|
||||||
unsigned int divndx;
|
unsigned int divndx;
|
||||||
unsigned int outdiv;
|
unsigned int outdiv;
|
||||||
|
unsigned int framelen;
|
||||||
#ifdef WM8904_USE_FFLOCK_INT
|
#ifdef WM8904_USE_FFLOCK_INT
|
||||||
bool enabled;
|
bool enabled;
|
||||||
int retries;
|
int retries;
|
||||||
@@ -634,22 +641,21 @@ static void wm8904_setbitrate(FAR struct wm8904_dev_s *priv)
|
|||||||
|
|
||||||
DEBUGASSERT(priv && priv->lower);
|
DEBUGASSERT(priv && priv->lower);
|
||||||
|
|
||||||
/* First calculate the desired bitrate (fout) */
|
/* First calculate the desired bitrate (fout). This is based on
|
||||||
|
*
|
||||||
#if 0
|
* 1. The I2S frame length (in bits)
|
||||||
/* This is the correct calculation. However, the resulting bitrate is two
|
* 2. The number of frames per second = nchannnels * samplerate
|
||||||
* times too fast???
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fout = (uint32_t)priv->samprate * (uint32_t)priv->nchannels * (uint32_t)priv->bpsamp;
|
framelen = (priv->bpsamp == 8) ? WM8904_FRAMELEN8 : WM8904_FRAMELEN16;
|
||||||
#else
|
fout = (uint32_t)priv->samprate * (uint32_t)priv->nchannels * framelen;
|
||||||
/* Ahhh.. much better */
|
|
||||||
|
|
||||||
fout = (uint32_t)priv->samprate * (uint32_t)priv->bpsamp;
|
regval = WM8904_LRCLK_DIR | WM8904_LRCLK_RATE(framelen << 1);
|
||||||
#endif
|
wm8904_writereg(priv, WM8904_AIF3, regval);
|
||||||
|
|
||||||
audvdbg("sample rate=%u nchannels=%u bpsamp=%u fout=%lu\n",
|
audvdbg("sample rate=%u nchannels=%u bpsamp=%u framelen=%d fout=%lu\n",
|
||||||
priv->samprate, priv->nchannels, priv->bpsamp, (unsigned long)fout);
|
priv->samprate, priv->nchannels, priv->bpsamp, framelen,
|
||||||
|
(unsigned long)fout);
|
||||||
|
|
||||||
/* Disable the SYSCLK.
|
/* 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
|
/* Now issue a software reset. This puts all WM8904 registers back in
|
||||||
* their default state.
|
* their default state.
|
||||||
*/
|
*/
|
||||||
/* REVISIT: But then the register configuration is lost. */
|
|
||||||
/* wm8904_writereg(priv, WM8904_SWRST, 0); */
|
|
||||||
|
|
||||||
|
wm8904_hw_reset(priv);
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2068,15 +2073,9 @@ static void *wm8904_workerthread(pthread_addr_t pvarg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Disable the WM8904 interrupt */
|
/* Reset the WM8904 hardware */
|
||||||
|
|
||||||
WM8904_DISABLE(priv->lower);
|
wm8904_hw_reset(priv);
|
||||||
|
|
||||||
/* Mute the volume and disable the FLL output */
|
|
||||||
|
|
||||||
wm8904_setvolume(priv, priv->volume, true);
|
|
||||||
wm8904_writereg(priv, WM8904_FLL_CTRL1, 0);
|
|
||||||
wm8904_writereg(priv, WM8904_DUMMY, 0x55aa);
|
|
||||||
|
|
||||||
/* Return any pending buffers in our pending queue */
|
/* 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
|
/* Audio Interface 3
|
||||||
*
|
*
|
||||||
* Set LRCLK as an output with rate = BCLK / 64. This is a constant value
|
* Set LRCLK as an output with rate = BCLK / (2*WM8904_FRAMELENn). This is
|
||||||
* used with audio. Since I2S will send a word on each edge of LRCLK (after
|
* a value that varies with bits per sample, n=8 or 16. Since I2S will send
|
||||||
* a delay), this essentially means that each audio frame is 32 bits in
|
* a word on each edge of LRCLK (after a delay), this essentially means that
|
||||||
* length;
|
* 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);
|
wm8904_writereg(priv, WM8904_AIF3, regval);
|
||||||
|
|
||||||
/* DAC Digital 1 */
|
/* 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.
|
* function then modifies the configuration to support audio input.
|
||||||
*
|
*
|
||||||
* Input Parameters:
|
* Input Parameters:
|
||||||
* prive - A reference to the driver state structure
|
* priv - A reference to the driver state structure
|
||||||
*
|
*
|
||||||
* Returned Value:
|
* Returned Value:
|
||||||
* None. No failures are detected.
|
* None. No failures are detected.
|
||||||
@@ -2336,6 +2335,98 @@ static void wm8904_audio_input(FAR struct wm8904_dev_s *priv)
|
|||||||
}
|
}
|
||||||
#endif
|
#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
|
* Public Functions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
@@ -2381,12 +2472,6 @@ FAR struct audio_lowerhalf_s *
|
|||||||
priv->lower = lower;
|
priv->lower = lower;
|
||||||
priv->i2c = i2c;
|
priv->i2c = i2c;
|
||||||
priv->i2s = i2s;
|
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);
|
sem_init(&priv->pendsem, 0, 1);
|
||||||
dq_init(&priv->pendq);
|
dq_init(&priv->pendq);
|
||||||
@@ -2414,42 +2499,9 @@ FAR struct audio_lowerhalf_s *
|
|||||||
goto errout_with_dev;
|
goto errout_with_dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Configure the WM8904 hardware as an audio input device */
|
/* Reset and reconfigure the WM8904 hardwaqre */
|
||||||
|
|
||||||
wm8904_audio_output(priv);
|
wm8904_hw_reset(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 */
|
|
||||||
|
|
||||||
wm8904_dump_registers(&priv->dev, "After configuration");
|
|
||||||
wm8904_clock_analysis(&priv->dev, "After configuration");
|
|
||||||
return &priv->dev;
|
return &priv->dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-10
@@ -1015,24 +1015,27 @@
|
|||||||
/* FLL Configuration *********************************************************/
|
/* FLL Configuration *********************************************************/
|
||||||
/* Default FLL configuration */
|
/* Default FLL configuration */
|
||||||
|
|
||||||
#define WM8904_DEFAULT_SAMPRATE 11025
|
#define WM8904_DEFAULT_SAMPRATE 11025 /* Initial sample rate */
|
||||||
#define WM8904_DEFAULT_NCHANNELS 1
|
#define WM8904_DEFAULT_NCHANNELS 1 /* Initial number of channels */
|
||||||
#define WM8904_DEFAULT_BPSAMP 16
|
#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_DIV2 1
|
||||||
#define WM8904_NFLLRATIO_DIV4 2
|
#define WM8904_NFLLRATIO_DIV4 2
|
||||||
#define WM8904_NFLLRATIO_DIV8 3
|
#define WM8904_NFLLRATIO_DIV8 3
|
||||||
#define WM8904_NFLLRATIO_DIV16 4
|
#define WM8904_NFLLRATIO_DIV16 4
|
||||||
#define WM8904_NFLLRATIO 5
|
#define WM8904_NFLLRATIO 5 /* Number of FLL_RATIO values */
|
||||||
|
|
||||||
#define WM8904_MINOUTDIV 4
|
#define WM8904_MINOUTDIV 4 /* Minimum FLL_OUTDIV divider */
|
||||||
#define WM8904_MAXOUTDIV 64
|
#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_MIN 90000000 /* Minimum value of Fvco */
|
||||||
#define WM8904_FVCO_MAX 100000000
|
#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 */
|
/* Commonly defined and redefined macros */
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user