static int bcm2835_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); uint32_t cs_reg; bcm2835_i2s_start_clock(dev); /* * Clear both FIFOs if the one that should be started * is not empty at the moment. This should only happen * after overrun. Otherwise, hw_params would have cleared * the FIFO. */ regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &cs_reg); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !(cs_reg & BCM2835_I2S_TXE)) 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); return 0; }
static int bcm2835_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); uint32_t mask; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: bcm2835_i2s_start_clock(dev); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) mask = BCM2835_I2S_RXON; else mask = BCM2835_I2S_TXON; regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, mask, mask); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: bcm2835_i2s_stop(dev, substream, dai); break; default: return -EINVAL; } return 0; }
static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev, bool tx, bool rx) { int timeout = 1000; uint32_t syncval; uint32_t csreg; uint32_t i2s_active_state; bool clk_was_prepared; uint32_t off; uint32_t clr; off = tx ? BCM2835_I2S_TXON : 0; off |= rx ? BCM2835_I2S_RXON : 0; clr = tx ? BCM2835_I2S_TXCLR : 0; clr |= rx ? BCM2835_I2S_RXCLR : 0; /* Backup the current state */ regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg); i2s_active_state = csreg & (BCM2835_I2S_RXON | BCM2835_I2S_TXON); /* Start clock if not running */ clk_was_prepared = dev->clk_prepared; if (!clk_was_prepared) bcm2835_i2s_start_clock(dev); /* Stop I2S module */ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, off, 0); /* * Clear the FIFOs * Requires at least 2 PCM clock cycles to take effect */ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, clr, clr); /* 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! */ regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &syncval); syncval &= BCM2835_I2S_SYNC; regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, BCM2835_I2S_SYNC, ~syncval); /* Wait for the SYNC flag changing it's state */ while (--timeout) { regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg); if ((csreg & BCM2835_I2S_SYNC) != syncval) break; } if (!timeout) dev_err(dev->dev, "I2S SYNC error!\n"); /* Stop clock if it was not running before */ if (!clk_was_prepared) bcm2835_i2s_stop_clock(dev); /* Restore I2S state */ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, BCM2835_I2S_RXON | BCM2835_I2S_TXON, i2s_active_state); }