summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorSumit Bhattacharya <sumitb@nvidia.com>2013-04-17 18:13:04 +0530
committerMrutyunjay Sawant <msawant@nvidia.com>2013-04-29 02:47:28 -0700
commitaf3d0654d5048906417cfb71853ec02e6e82a310 (patch)
tree3d086f994fabb6fceeb70e01e3314f5980e9c4c9 /sound
parentf6126d396e3fd6ea0cff0a341d0e8a303946f9b4 (diff)
ASoC: Tegra: Improve capture stopping logic
During stopping capture session when I2s RX port is disabled I2S FIFO may contain 2 bytes of data. In case of stereo capture done in I2S mode I2S CIF will be configured for stereo and it will not transmit residual 2 byte I2S FIFO data. As a result when next capture session starts audio channel will get reversed due to residual 2 bytes of data. To solve this issue do a SOFT_RESET of I2S channel if after disabling of I2S RX port I2S FIFO does not get empty. Also disable APBIF FIFO after I2S RX port is disabled to follow source to destination disabling sequence. As a precaution similar check is also added form playback stop path. Bug 1255915 Change-Id: I8ab74c96ca00e2a1fda0abfeb73244a83b847005 Signed-off-by: Sumit Bhattacharya <sumitb@nvidia.com> Reviewed-on: http://git-master/r/220203 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Scott Peterson <speterson@nvidia.com> GVS: Gerrit_Virtual_Submit
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/tegra/tegra30_i2s.c58
1 files changed, 52 insertions, 6 deletions
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
index 20faef840358..da189842f0f8 100644
--- a/sound/soc/tegra/tegra30_i2s.c
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -790,6 +790,25 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static int tegra30_i2s_soft_reset(struct tegra30_i2s *i2s)
+{
+ int dcnt = 10;
+
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_SOFT_RESET;
+ tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl);
+
+ while ((tegra30_i2s_read(i2s, TEGRA30_I2S_CTRL) &
+ TEGRA30_I2S_CTRL_SOFT_RESET) && dcnt--)
+ udelay(100);
+
+ /* Restore reg_ctrl to ensure if a concurrent playback/capture
+ session was active it continues after SOFT_RESET */
+ i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_SOFT_RESET;
+ tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl);
+
+ return (dcnt < 0) ? -ETIMEDOUT : 0;
+}
+
static void tegra30_i2s_start_playback(struct tegra30_i2s *i2s)
{
tegra30_ahub_enable_tx_fifo(i2s->txcif);
@@ -808,9 +827,24 @@ static void tegra30_i2s_stop_playback(struct tegra30_i2s *i2s)
if (i2s->playback_ref_count == 1) {
i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_TX;
tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl);
+ while (tegra30_ahub_tx_fifo_is_enabled(i2s->id) && dcnt--)
+ udelay(100);
+
+ while (!tegra30_ahub_tx_fifo_is_empty(i2s->id) && dcnt--)
+ udelay(100);
+
+ /* In case I2S FIFO does not get empty do a soft reset of the
+ I2S channel to prevent channel reversal in next session */
+ if (dcnt < 0) {
+ tegra30_i2s_soft_reset(i2s);
+
+ dcnt = 10;
+ while (!tegra30_ahub_tx_fifo_is_empty(i2s->id) &&
+ dcnt--)
+ udelay(100);
+ }
}
- while (!tegra30_ahub_tx_fifo_is_empty(i2s->id) && dcnt--)
- udelay(100);
+
}
static void tegra30_i2s_start_capture(struct tegra30_i2s *i2s)
@@ -826,15 +860,27 @@ static void tegra30_i2s_stop_capture(struct tegra30_i2s *i2s)
{
int dcnt = 10;
if (!i2s->is_call_mode_rec && (i2s->capture_ref_count == 1)) {
- tegra30_ahub_disable_rx_fifo(i2s->rxcif);
i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_RX;
tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl);
while (tegra30_ahub_rx_fifo_is_enabled(i2s->id) && dcnt--)
udelay(100);
- }
- while (!tegra30_ahub_rx_fifo_is_empty(i2s->id) && dcnt--)
- udelay(100);
+ while (!tegra30_ahub_rx_fifo_is_empty(i2s->id) && dcnt--)
+ udelay(100);
+
+ /* In case I2S FIFO does not get empty do a soft reset of
+ the I2S channel to prevent channel reversal in next capture
+ session */
+ if (dcnt < 0) {
+ tegra30_i2s_soft_reset(i2s);
+
+ dcnt = 10;
+ while (!tegra30_ahub_rx_fifo_is_empty(i2s->id) &&
+ dcnt--)
+ udelay(100);
+ }
+ tegra30_ahub_disable_rx_fifo(i2s->rxcif);
+ }
}
static int tegra30_i2s_trigger(struct snd_pcm_substream *substream, int cmd,