diff options
author | Arun Shamanna Lakshmi <aruns@nvidia.com> | 2014-04-02 20:50:45 -0700 |
---|---|---|
committer | Bharat Nihalani <bnihalani@nvidia.com> | 2014-04-07 21:05:46 -0700 |
commit | b9111a748b905f65dda6a1aca1b4831d9be5bcf7 (patch) | |
tree | 7f8023302753dc715d2f5453977677330cd2ed5e /sound | |
parent | ebf698ed229151ab6aec580eb758aec69b4169ac (diff) |
ASoC: tegra_alt: Fix I2S shutdown sequence
1. Soft reset APBIF and wait for tx/rx fifo to be disabled/empty
2. Soft reset I2S post the DAPM widget power down
Bug 1492370
Change-Id: Iadbfa86a6e1937e33c7bc920e0ea4847e18aa7c0
Signed-off-by: Arun Shamanna Lakshmi <aruns@nvidia.com>
Signed-off-by: Songhee Baek <sbaek@nvidia.com>
Reviewed-on: http://git-master/r/391633
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Tested-by: Sonny Jamuar <sjamuar@nvidia.com>
Reviewed-by: Gajanan Bhat <gbhat@nvidia.com>
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/tegra-alt/tegra30_apbif_alt.c | 63 | ||||
-rw-r--r-- | sound/soc/tegra-alt/tegra30_apbif_alt.h | 5 | ||||
-rw-r--r-- | sound/soc/tegra-alt/tegra30_i2s_alt.c | 97 |
3 files changed, 160 insertions, 5 deletions
diff --git a/sound/soc/tegra-alt/tegra30_apbif_alt.c b/sound/soc/tegra-alt/tegra30_apbif_alt.c index 08ce186ba10b..5413f2355fd5 100644 --- a/sound/soc/tegra-alt/tegra30_apbif_alt.c +++ b/sound/soc/tegra-alt/tegra30_apbif_alt.c @@ -34,6 +34,8 @@ #define DRV_NAME "tegra30-ahub-apbif" +struct tegra30_apbif *apbif; + #define FIFOS_IN_FIRST_REG_BLOCK 4 #define LAST_REG(name) \ @@ -178,6 +180,54 @@ static int tegra30_apbif_runtime_resume(struct device *dev) return 0; } +int tegra30_apbif_i2s_rx_fifo_is_enabled(int i2s_id) +{ + int val; + + regmap_read(apbif->regmap[0], TEGRA_AHUB_I2S_LIVE_STATUS, &val); + val &= (TEGRA_AHUB_I2S_LIVE_STATUS_I2S0_RX_FIFO_ENABLED << + (i2s_id * 2)); + + return val; +} +EXPORT_SYMBOL_GPL(tegra30_apbif_i2s_rx_fifo_is_enabled); + +int tegra30_apbif_i2s_tx_fifo_is_enabled(int i2s_id) +{ + int val; + + regmap_read(apbif->regmap[0], TEGRA_AHUB_I2S_LIVE_STATUS, &val); + val &= (TEGRA_AHUB_I2S_LIVE_STATUS_I2S0_TX_FIFO_ENABLED << + (i2s_id * 2)); + + return val; +} +EXPORT_SYMBOL_GPL(tegra30_apbif_i2s_tx_fifo_is_enabled); + +int tegra30_apbif_i2s_rx_fifo_is_empty(int i2s_id) +{ + int val; + + regmap_read(apbif->regmap[0], TEGRA_AHUB_I2S_LIVE_STATUS, &val); + val &= (TEGRA_AHUB_I2S_LIVE_STATUS_I2S0_RX_FIFO_EMPTY << + (i2s_id * 2)); + + return val; +} +EXPORT_SYMBOL_GPL(tegra30_apbif_i2s_rx_fifo_is_empty); + +int tegra30_apbif_i2s_tx_fifo_is_empty(int i2s_id) +{ + int val; + + regmap_read(apbif->regmap[0], TEGRA_AHUB_I2S_LIVE_STATUS, &val); + val &= (TEGRA_AHUB_I2S_LIVE_STATUS_I2S0_TX_FIFO_EMPTY << + (i2s_id * 2)); + + return val; +} +EXPORT_SYMBOL_GPL(tegra30_apbif_i2s_tx_fifo_is_empty); + static int tegra30_apbif_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -308,6 +358,12 @@ static void tegra30_apbif_stop_playback(struct snd_soc_dai *dai) reg = TEGRA_AHUB_CHANNEL_CTRL + ((dai->id - base_ch) * TEGRA_AHUB_CHANNEL_CTRL_STRIDE); regmap_update_bits(regmap, reg, TEGRA_AHUB_CHANNEL_CTRL_TX_EN, 0); + + /* soft reset APBIF TX */ + reg = TEGRA_AHUB_CHANNEL_CLEAR + + ((dai->id - base_ch) * TEGRA_AHUB_CHANNEL_CTRL_STRIDE); + regmap_update_bits(regmap, reg, + TEGRA_AHUB_CHANNEL_CLEAR_TX_SOFT_RESET, 1); } static void tegra30_apbif_start_capture(struct snd_soc_dai *dai) @@ -347,6 +403,12 @@ static void tegra30_apbif_stop_capture(struct snd_soc_dai *dai) reg = TEGRA_AHUB_CHANNEL_CTRL + ((dai->id - base_ch) * TEGRA_AHUB_CHANNEL_CTRL_STRIDE); regmap_update_bits(regmap, reg, TEGRA_AHUB_CHANNEL_CTRL_RX_EN, 0); + + /* soft reset APBIF RX */ + reg = TEGRA_AHUB_CHANNEL_CLEAR + + ((dai->id - base_ch) * TEGRA_AHUB_CHANNEL_CTRL_STRIDE); + regmap_update_bits(regmap, reg, + TEGRA_AHUB_CHANNEL_CLEAR_RX_SOFT_RESET, 1); } static int tegra30_apbif_trigger(struct snd_pcm_substream *substream, int cmd, @@ -527,7 +589,6 @@ static int tegra30_apbif_probe(struct platform_device *pdev) int i; struct clk *clk; int ret; - struct tegra30_apbif *apbif; void __iomem *regs; struct resource *res[2]; u32 of_dma[10][2]; diff --git a/sound/soc/tegra-alt/tegra30_apbif_alt.h b/sound/soc/tegra-alt/tegra30_apbif_alt.h index 62a835fb8ecf..c46c114d5dcc 100644 --- a/sound/soc/tegra-alt/tegra30_apbif_alt.h +++ b/sound/soc/tegra-alt/tegra30_apbif_alt.h @@ -302,6 +302,11 @@ /* TEGRA_AHUB_CIF_RX9_CTRL */ #define TEGRA_AHUB_CIF_RX9_CTRL 0xb8 +int tegra30_apbif_i2s_rx_fifo_is_enabled(int i2s_id); +int tegra30_apbif_i2s_tx_fifo_is_enabled(int i2s_id); +int tegra30_apbif_i2s_rx_fifo_is_empty(int i2s_id); +int tegra30_apbif_i2s_tx_fifo_is_empty(int i2s_id); + struct tegra30_apbif_soc_data { unsigned int num_ch; unsigned int clk_list_mask; diff --git a/sound/soc/tegra-alt/tegra30_i2s_alt.c b/sound/soc/tegra-alt/tegra30_i2s_alt.c index c1aac76599b7..dc045e5359a4 100644 --- a/sound/soc/tegra-alt/tegra30_i2s_alt.c +++ b/sound/soc/tegra-alt/tegra30_i2s_alt.c @@ -25,6 +25,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <linux/delay.h> #include <linux/clk.h> #include <linux/device.h> #include <linux/io.h> @@ -43,6 +44,7 @@ #include "tegra30_xbar_alt.h" #include "tegra30_i2s_alt.h" +#include "tegra30_apbif_alt.h" #define DRV_NAME "tegra30-i2s" @@ -355,6 +357,83 @@ 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; + unsigned int val, ctrl_val; + + regmap_read(i2s->regmap, TEGRA30_I2S_CTRL, &ctrl_val); + + regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL, + TEGRA30_I2S_CTRL_SOFT_RESET, TEGRA30_I2S_CTRL_SOFT_RESET); + + do { + udelay(100); + regmap_read(i2s->regmap, TEGRA30_I2S_CTRL, &val); + } while ((val & TEGRA30_I2S_CTRL_SOFT_RESET) && dcnt--); + + /* Restore reg_ctrl to ensure if a concurrent playback/capture + session was active it continues after SOFT_RESET */ + regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL, + TEGRA30_I2S_CTRL_SOFT_RESET, 0); + + regmap_write(i2s->regmap, TEGRA30_I2S_CTRL, ctrl_val); + + return (dcnt < 0) ? -ETIMEDOUT : 0; +} + +static int tegra30_i2s_rx_stop(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct device *dev = codec->dev; + struct tegra30_i2s *i2s = dev_get_drvdata(dev); + int dcnt = 10; + + /* wait until rx fifo is disabled */ + while (tegra30_apbif_i2s_rx_fifo_is_enabled(dev->id) && dcnt--) + udelay(100); + + dcnt = 10; + while (!tegra30_apbif_i2s_rx_fifo_is_empty(dev->id) && dcnt--) + udelay(100); + + if (dcnt < 0) { + dcnt = 10; + tegra30_i2s_soft_reset(i2s); + while (!tegra30_apbif_i2s_rx_fifo_is_empty(dev->id) && + dcnt--) + udelay(100); + } + return 0; +} + +static int tegra30_i2s_tx_stop(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct device *dev = codec->dev; + struct tegra30_i2s *i2s = dev_get_drvdata(dev); + int dcnt = 10; + + /* wait until tx fifo is disabled */ + while (tegra30_apbif_i2s_tx_fifo_is_enabled(dev->id) && dcnt--) + udelay(100); + + dcnt = 10; + while (!tegra30_apbif_i2s_tx_fifo_is_empty(dev->id) && dcnt--) + udelay(100); + + if (dcnt < 0) { + dcnt = 10; + tegra30_i2s_soft_reset(i2s); + while (!tegra30_apbif_i2s_tx_fifo_is_empty(dev->id) && + dcnt--) + udelay(100); + } + return 0; +} + static int tegra30_i2s_codec_probe(struct snd_soc_codec *codec) { struct tegra30_i2s *i2s = snd_soc_codec_get_drvdata(codec); @@ -432,10 +511,12 @@ static const struct snd_soc_dapm_widget tegra30_i2s_widgets[] = { 0, 0), SND_SOC_DAPM_AIF_OUT("CIF TX", NULL, 0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_AIF_IN("DAP RX", NULL, 0, TEGRA30_I2S_CTRL, - TEGRA30_I2S_CTRL_XFER_EN_RX_SHIFT, 0), - SND_SOC_DAPM_AIF_OUT("DAP TX", NULL, 0, TEGRA30_I2S_CTRL, - TEGRA30_I2S_CTRL_XFER_EN_TX_SHIFT, 0), + SND_SOC_DAPM_AIF_IN_E("DAP RX", NULL, 0, TEGRA30_I2S_CTRL, + TEGRA30_I2S_CTRL_XFER_EN_RX_SHIFT, 0, + tegra30_i2s_rx_stop, SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT_E("DAP TX", NULL, 0, TEGRA30_I2S_CTRL, + TEGRA30_I2S_CTRL_XFER_EN_TX_SHIFT, 0, + tegra30_i2s_tx_stop, SND_SOC_DAPM_POST_PMD), }; static const struct snd_soc_dapm_route tegra30_i2s_routes[] = { @@ -491,6 +572,7 @@ static bool tegra30_i2s_wr_rd_reg(struct device *dev, unsigned int reg) static bool tegra30_i2s_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { + case TEGRA30_I2S_CTRL: case TEGRA30_I2S_FLOW_STATUS: case TEGRA30_I2S_FLOW_TOTAL: case TEGRA30_I2S_FLOW_OVER: @@ -623,6 +705,13 @@ static int tegra30_i2s_platform_probe(struct platform_device *pdev) } regcache_cache_only(i2s->regmap, true); + if (of_property_read_u32(pdev->dev.of_node, + "nvidia,ahub-i2s-id", &pdev->dev.id) < 0) { + dev_err(&pdev->dev, "Missing property nvidia,ahub-i2s-id\n"); + ret = -ENODEV; + goto err; + } + pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { ret = tegra30_i2s_runtime_resume(&pdev->dev); |