Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions sound/soc/bcm/bcm2835-i2s.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ static void bcm2835_i2s_stop_clock(struct bcm2835_i2s_dev *dev)
dev->clk_prepared = false;
}

static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
bool tx, bool rx)
static int bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
bool tx, bool rx)
{
int timeout = 1000;
uint32_t syncval;
Expand Down Expand Up @@ -187,8 +187,9 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
/* Wait for 2 PCM clock cycles */

/*
* Toggle the SYNC flag. After 2 PCM clock cycles it can be read back
* FIXME: This does not seem to work for slave mode!
* Toggle the SYNC flag. After 2 PCM clock cycles it can be read back.
* The reset will timeout in clock consumer mode without an external
* clock.
*/
regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &syncval);
syncval &= BCM2835_I2S_SYNC;
Expand All @@ -204,7 +205,8 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
}

if (!timeout)
dev_err(dev->dev, "I2S SYNC error!\n");
dev_err(dev->dev,
"FIFO clear timed out: no PCM clock (clock consumer with the provider not running?)\n");

/* Stop clock if it was not running before */
if (!clk_was_prepared)
Expand All @@ -213,6 +215,14 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
/* Restore I2S state */
regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG,
BCM2835_I2S_RXON | BCM2835_I2S_TXON, i2s_active_state);

/*
* Without the SYNC round trip the FIFOs were not actually cleared:
* starting DMA on top of a stale FIFO makes RX free-run (DREQ stuck
* high, the same dead word re-read at bus speed) and the state then
* survives further stream cycles. Fail the stream instead.
*/
return timeout ? 0 : -EIO;
}

static int bcm2835_i2s_set_dai_fmt(struct snd_soc_dai *dai,
Expand Down Expand Up @@ -588,6 +598,11 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
| BCM2835_I2S_RX(0x20), 0xffffffff);

/* Clear FIFOs */
/*
* May time out benignly here: on clock-consumer links the provider's
* clocks are often not running yet at hw_params time; the clear that
* must succeed is the one in .prepare.
*/
bcm2835_i2s_clear_fifos(dev, true, true);

dev_dbg(dev->dev,
Expand Down Expand Up @@ -636,7 +651,13 @@ static int bcm2835_i2s_prepare(struct snd_pcm_substream *substream,
bcm2835_i2s_clear_fifos(dev, true, false);
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE
&& (cs_reg & BCM2835_I2S_RXD))
bcm2835_i2s_clear_fifos(dev, false, true);
/*
* Stale RX data with no working clock cannot be cleared: DMA
* would free-run over the dead FIFO (DREQ stuck high) and the
* poisoned state survives stream cycles. Fail this capture
* open instead of recording garbage.
*/
return bcm2835_i2s_clear_fifos(dev, false, true);

return 0;
}
Expand Down
Loading