WM8904: Can't reset the WM8904 unless we also reinitiailize all registers. Try to get a saner timeout based on sample rate, bits-per-sample, and buffer size.

This commit is contained in:
Gregory Nutt 2014-07-31 19:14:41 -06:00
parent 0b4090df0d
commit 3e1e15b04a

View File

@ -108,12 +108,15 @@ struct wm8904_dev_s
char mqname[16]; /* Our message queue name */
pthread_t threadid; /* ID of our thread */
sem_t pendsem; /* Protect pendq */
uint16_t samprate; /* Configured samprate (sampeles/sec) */
#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
#ifndef CONFIG_AUDIO_EXCLUDE_BALANCE
uint16_t balance; /* Current balance level (b16) */
#endif /* CONFIG_AUDIO_EXCLUDE_BALANCE */
uint8_t volume; /* Current volume level {0..63} */
#endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
uint8_t nchannels; /* Number of channels (1 or 2) */
uint8_t bpshift; /* Log2 of bits per sample (3 or 4) */
volatile uint8_t inflight; /* Number of audio buffers in-flight */
bool running; /* True: Worker thread is running */
bool paused; /* True: Playing is paused */
@ -154,7 +157,6 @@ static int wm8904_configure(FAR struct audio_lowerhalf_s *dev,
static int wm8904_configure(FAR struct audio_lowerhalf_s *dev,
FAR const struct audio_caps_s *caps);
#endif
static int wm8904_reset(FAR struct wm8904_dev_s *priv);
static int wm8904_shutdown(FAR struct audio_lowerhalf_s *dev);
static void wm8904_senddone(FAR struct i2s_dev_s *i2s,
FAR struct ap_buffer_s *apb, FAR void *arg, int result);
@ -815,11 +817,48 @@ static int wm8904_configure(FAR struct audio_lowerhalf_s *dev,
break;
case AUDIO_TYPE_OUTPUT:
audvdbg(" AUDIO_TYPE_OUTPUT:\n");
audvdbg(" Number of channels: %u\n", caps->ac_channels);
audvdbg(" Sample rate: %u\n", caps->ac_controls.hw[0]);
audvdbg(" Sample width: %u\n", caps->ac_controls.b[2]);
{
uint8_t bpshift;
audvdbg(" AUDIO_TYPE_OUTPUT:\n");
audvdbg(" Number of channels: %u\n", caps->ac_channels);
audvdbg(" Sample rate: %u\n", caps->ac_controls.hw[0]);
audvdbg(" Sample width: %u\n", caps->ac_controls.b[2]);
/* Verify that all of the requested values are supported */
ret = -ERANGE;
if (caps->ac_channels != 1 && caps->ac_channels != 2)
{
break;
}
if (caps->ac_controls.b[2] == 8)
{
bpshift = 3;
}
else if (caps->ac_controls.b[2] == 16)
{
bpshift = 4;
}
else
{
break;
}
/* Configure the hardware to accept an audio stream with these
* properties
*/
#warning Missing logic
/* Save the current stream configuration */
priv->nchannels = (uint8_t)caps->ac_channels;
priv->samprate = caps->ac_controls.hw[0];
priv->bpshift = bpshift;
ret = OK;
}
break;
case AUDIO_TYPE_PROCESSING:
@ -829,27 +868,6 @@ static int wm8904_configure(FAR struct audio_lowerhalf_s *dev,
return ret;
}
/****************************************************************************
* Name: wm8904_reset
*
* Description:
* Performs a soft reset on the WM8904 chip by writing to the SWRST
* register.
*
****************************************************************************/
static int wm8904_reset(FAR struct wm8904_dev_s *priv)
{
/* First disable interrupts */
WM8904_DISABLE(priv->lower);
/* Now issue a reset command */
wm8904_writereg(priv, WM8904_SWRST, 0);
return OK;
}
/****************************************************************************
* Name: wm8904_shutdown
*
@ -863,7 +881,17 @@ static int wm8904_shutdown(FAR struct audio_lowerhalf_s *dev)
FAR struct wm8904_dev_s *priv = (FAR struct wm8904_dev_s *)dev;
DEBUGASSERT(priv);
wm8904_reset(priv);
/* First disable interrupts */
WM8904_DISABLE(priv->lower);
/* 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); */
return OK;
}
@ -886,7 +914,7 @@ static void wm8904_senddone(FAR struct i2s_dev_s *i2s,
int ret;
DEBUGASSERT(i2s && priv && priv->running && apb);
audvdbg("apb=%p inflight=%d\n", apb, priv->inflight);
audvdbg("apb=%p inflight=%d result=%d\n", apb, priv->inflight, result);
/* We do not place any restriction on the context in which this function
* is called. It may be called from an interrupt handler. Therefore, the
@ -1007,6 +1035,7 @@ static int wm8904_sendbuffer(FAR struct wm8904_dev_s *priv)
{
FAR struct ap_buffer_s *apb;
irqstate_t flags;
uint32_t timeout;
int ret = OK;
/* Loop while there are audio buffers to be sent and we have few than
@ -1039,9 +1068,25 @@ static int wm8904_sendbuffer(FAR struct wm8904_dev_s *priv)
priv->inflight++;
irqrestore(flags);
/* Send the entire audio buffer via I2S */
/* Send the entire audio buffer via I2S. What is a reasonable timeout
* to use? This would depend on the bit rate and size of the buffer.
*
* Samples in the buffer:
* = buffer_size * 8 / bpsamp samples
* Expected transfer time:
* = samples / samprate seconds
* = (samples * 1000) / (samprate * msec_per_tick) ticks
* = (buffer_size * 8000) /(samprate * bpsamp * msec_per_tick)
*
* We will set the timeout about twice that. Here is a reasonable
* approximation that saves a multiply:
* = (buffer_size * 16384) /(samprate * bpsamp * msec_per_tick)
*/
ret = I2S_SEND(priv->i2s, apb, wm8904_senddone, priv, 500 / MSEC_PER_TICK);
timeout = (((uint32_t)(apb->nbytes - apb->curbyte) << 14) /
((uint32_t)(priv->samprate * MSEC_PER_TICK) << priv->bpshift));
ret = I2S_SEND(priv->i2s, apb, wm8904_senddone, priv, timeout);
if (ret < 0)
{
auddbg("ERROR: I2S_SEND failed: %d\n", ret);
@ -1268,10 +1313,6 @@ static int wm8904_start(FAR struct audio_lowerhalf_s *dev)
audvdbg("Entry\n");
/* Do a soft reset, just in case */
wm8904_reset(priv);
/* Exit reduced power modes of operation */
/* REVISIT */
@ -1511,8 +1552,12 @@ static int wm8904_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd,
case AUDIOIOC_HWRESET:
{
/* REVISIT: Should we completely re-initialize the chip? We
* can't just issue a software reset; that would puts all WM8904
* registers back in their default state.
*/
audvdbg("AUDIOIOC_HWRESET:\n");
wm8904_reset((FAR struct wm8904_dev_s *)dev);
}
break;
@ -1847,6 +1892,9 @@ FAR struct audio_lowerhalf_s *
priv->lower = lower;
priv->i2c = i2c;
priv->i2s = i2s;
priv->nchannels = 2; /* REVISIT: Must match hardware config */
priv->samprate = 40000; /* ditto */
priv->bpshift = 4; /* ditto */
#if !defined(CONFIG_AUDIO_EXCLUDE_VOLUME) && !defined(CONFIG_AUDIO_EXCLUDE_BALANCE)
priv->balance = b16HALF; /* Center balance */
#endif
@ -1861,7 +1909,9 @@ FAR struct audio_lowerhalf_s *
I2C_SETFREQUENCY(i2c, lower->frequency);
I2C_SETADDRESS(i2c, lower->address, 7);
/* Software reset */
/* Software reset. This puts all WM8904 registers back in their
* default state.
*/
wm8904_writereg(priv, WM8904_SWRST, 0);
@ -1881,10 +1931,6 @@ FAR struct audio_lowerhalf_s *
/* Attach our ISR to this device */
WM8904_ATTACH(lower, wm8904_interrupt, priv);
/* Put the driver in the 'shutdown' state */
wm8904_shutdown(&priv->dev);
return &priv->dev;
}