summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorArun Shamanna Lakshmi <aruns@nvidia.com>2014-04-02 20:50:45 -0700
committerBharat Nihalani <bnihalani@nvidia.com>2014-04-07 21:05:46 -0700
commitb9111a748b905f65dda6a1aca1b4831d9be5bcf7 (patch)
tree7f8023302753dc715d2f5453977677330cd2ed5e /sound
parentebf698ed229151ab6aec580eb758aec69b4169ac (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.c63
-rw-r--r--sound/soc/tegra-alt/tegra30_apbif_alt.h5
-rw-r--r--sound/soc/tegra-alt/tegra30_i2s_alt.c97
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);