summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorNikesh Oswal <noswal@nvidia.com>2011-11-18 14:59:12 +0530
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:50:23 -0800
commitab8abd6275cfe826002956d13a72d290ae344474 (patch)
treedee88307d894907f8894e97d2db1786003230f18 /sound
parent9f655d719048c0eda67e041f365325f07ac0eef5 (diff)
asoc: tegra: add power management functionality in t30 drivers
add code for handling register save/restore and clocks disable/enable during suspend/resume Bug: 862023 Change-Id: I1b709b6bf674c9a2d93c2a21c1f44bbadff64aab Signed-off-by: Nikesh Oswal <noswal@nvidia.com> Reviewed-on: http://git-master/r/65478 Reviewed-by: Sumit Bhattacharya <sumitb@nvidia.com> Reviewed-by: Scott Peterson <speterson@nvidia.com> Rebase-Id: R2383486dac0892e317dbd25044df59284031b6c4
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/tegra/tegra30_ahub.c36
-rw-r--r--sound/soc/tegra/tegra30_ahub.h16
-rw-r--r--sound/soc/tegra/tegra30_dam.c17
-rw-r--r--sound/soc/tegra/tegra30_dam.h5
-rw-r--r--sound/soc/tegra/tegra30_i2s.c27
-rw-r--r--sound/soc/tegra/tegra30_i2s.h3
6 files changed, 89 insertions, 15 deletions
diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c
index 8ee96bca2aff..866474a962b3 100644
--- a/sound/soc/tegra/tegra30_ahub.c
+++ b/sound/soc/tegra/tegra30_ahub.c
@@ -39,6 +39,9 @@ static struct tegra30_ahub *ahub;
static inline void tegra30_apbif_write(u32 reg, u32 val)
{
+#ifdef CONFIG_PM
+ ahub->apbif_reg_cache[reg >> 2] = val;
+#endif
__raw_writel(val, ahub->apbif_regs + reg);
}
@@ -49,6 +52,9 @@ static inline u32 tegra30_apbif_read(u32 reg)
static inline void tegra30_audio_write(u32 reg, u32 val)
{
+#ifdef CONFIG_PM
+ ahub->ahub_reg_cache[reg >> 2] = val;
+#endif
__raw_writel(val, ahub->audio_regs + reg);
}
@@ -57,6 +63,36 @@ static inline u32 tegra30_audio_read(u32 reg)
return __raw_readl(ahub->audio_regs + reg);
}
+#ifdef CONFIG_PM
+int tegra30_ahub_apbif_resume()
+{
+ int i = 0;
+ int cache_idx_rsvd;
+
+ tegra30_ahub_enable_clocks();
+
+ /*restore ahub regs*/
+ for (i = 0; i < TEGRA30_AHUB_AUDIO_RX_COUNT; i++)
+ tegra30_audio_write(i<<2, ahub->ahub_reg_cache[i]);
+
+ /*restore apbif regs*/
+ cache_idx_rsvd = TEGRA30_APBIF_CACHE_REG_INDEX_RSVD;
+ for (i = 0; i < TEGRA30_APBIF_CACHE_REG_COUNT; i++) {
+ if (i == cache_idx_rsvd) {
+ cache_idx_rsvd +=
+ TEGRA30_APBIF_CACHE_REG_INDEX_RSVD_STRIDE;
+ continue;
+ }
+
+ tegra30_apbif_write(i<<2, ahub->apbif_reg_cache[i]);
+ }
+
+ tegra30_ahub_disable_clocks();
+
+ return 0;
+}
+#endif
+
/*
* clk_apbif isn't required for a theoretical I2S<->I2S configuration where
* no PCM data is read from or sent to memory. However, that's an unlikely
diff --git a/sound/soc/tegra/tegra30_ahub.h b/sound/soc/tegra/tegra30_ahub.h
index 84c2cdfd69a8..0d18f9c94bcc 100644
--- a/sound/soc/tegra/tegra30_ahub.h
+++ b/sound/soc/tegra/tegra30_ahub.h
@@ -399,6 +399,14 @@
/* This register repeats once for each entry in enum tegra30_ahub_rxcif */
/* The fields in this register are 1 bit per entry in tegra30_ahub_txcif */
+/* apbif register count */
+#define TEGRA30_APBIF_CACHE_REG_COUNT_PER_CHANNEL ((TEGRA30_AHUB_CIF_RX_CTRL>>2) + 1)
+#define TEGRA30_APBIF_CACHE_REG_COUNT ((TEGRA30_APBIF_CACHE_REG_COUNT_PER_CHANNEL + 1) * TEGRA30_AHUB_CHANNEL_CTRL_COUNT)
+
+/* cache index to be skipped */
+#define TEGRA30_APBIF_CACHE_REG_INDEX_RSVD TEGRA30_APBIF_CACHE_REG_COUNT_PER_CHANNEL
+#define TEGRA30_APBIF_CACHE_REG_INDEX_RSVD_STRIDE (TEGRA30_APBIF_CACHE_REG_COUNT_PER_CHANNEL + 1)
+
/*
* Terminology:
* AHUB: Audio Hub; a cross-bar switch between the audio devices: DMA FIFOs,
@@ -481,6 +489,10 @@ extern int tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif,
enum tegra30_ahub_txcif txcif);
extern int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif);
+#ifdef CONFIG_PM
+extern int tegra30_ahub_apbif_resume();
+#endif
+
struct tegra30_ahub {
struct device *dev;
struct clk *clk_d_audio;
@@ -491,6 +503,10 @@ struct tegra30_ahub {
DECLARE_BITMAP(rx_usage, TEGRA30_AHUB_CHANNEL_CTRL_COUNT);
DECLARE_BITMAP(tx_usage, TEGRA30_AHUB_CHANNEL_CTRL_COUNT);
struct dentry *debug;
+#ifdef CONFIG_PM
+ u32 ahub_reg_cache[TEGRA30_AHUB_AUDIO_RX_COUNT];
+ u32 apbif_reg_cache[TEGRA30_APBIF_CACHE_REG_COUNT];
+#endif
};
#endif
diff --git a/sound/soc/tegra/tegra30_dam.c b/sound/soc/tegra/tegra30_dam.c
index 0828b014d9a7..fe11f2b5702e 100644
--- a/sound/soc/tegra/tegra30_dam.c
+++ b/sound/soc/tegra/tegra30_dam.c
@@ -65,7 +65,9 @@ static void tegra30_dam_ch0_set_step(struct tegra30_dam_context *dam, int step);
static inline void tegra30_dam_writel(struct tegra30_dam_context *dam,
u32 val, u32 reg)
{
- dam->ctrlreg_cache[(reg >> 2)] = val;
+#ifdef CONFIG_PM
+ dam->reg_cache[reg >> 2] = val;
+#endif
__raw_writel(val, dam->damregs + reg);
}
@@ -77,17 +79,6 @@ static inline u32 tegra30_dam_readl(struct tegra30_dam_context *dam, u32 reg)
}
#ifdef CONFIG_PM
-int tegra30_dam_suspend(int ifc)
-{
- int i = 0;
- struct tegra30_dam_context *dam;
-
- if (ifc >= TEGRA30_NR_DAM_IFC)
- return -EINVAL;
-
- return 0;
-}
-
int tegra30_dam_resume(int ifc)
{
int i = 0;
@@ -106,7 +97,7 @@ int tegra30_dam_resume(int ifc)
(i == TEGRA30_DAM_CTRL_RSVD_10))
continue;
- tegra30_dam_writel(dam, dam->ctrlreg_cache[i],
+ tegra30_dam_writel(dam, dam->reg_cache[i],
(i << 2));
}
diff --git a/sound/soc/tegra/tegra30_dam.h b/sound/soc/tegra/tegra30_dam.h
index 199730fc1563..ea76a2315655 100644
--- a/sound/soc/tegra/tegra30_dam.h
+++ b/sound/soc/tegra/tegra30_dam.h
@@ -129,7 +129,9 @@ struct tegra30_dam_context {
bool ch_alloc[TEGRA30_DAM_NUM_INPUT_CHANNELS];
int ch_enable_refcnt[TEGRA30_DAM_NUM_INPUT_CHANNELS];
int ch_insamplerate[TEGRA30_DAM_NUM_INPUT_CHANNELS];
- int ctrlreg_cache[TEGRA30_DAM_CTRL_REGINDEX + 1];
+#ifdef CONFIG_PM
+ int reg_cache[TEGRA30_DAM_CTRL_REGINDEX + 1];
+#endif
struct clk *dam_clk;
bool in_use;
void __iomem *damregs;
@@ -143,7 +145,6 @@ struct tegra30_dam_src_step_table {
};
#ifdef CONFIG_PM
-int tegra30_dam_suspend(int ifc);
int tegra30_dam_resume(int ifc);
#endif
void tegra30_dam_disable_clock(int ifc);
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
index 78e3ee042b5c..9b2a6be24d1d 100644
--- a/sound/soc/tegra/tegra30_i2s.c
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -52,6 +52,9 @@ static struct tegra30_i2s i2scont[TEGRA30_NR_I2S_IFC];
static inline void tegra30_i2s_write(struct tegra30_i2s *i2s, u32 reg, u32 val)
{
+#ifdef CONFIG_PM
+ i2s->reg_cache[reg >> 2] = val;
+#endif
__raw_writel(val, i2s->regs + reg);
}
@@ -451,6 +454,29 @@ static int tegra30_i2s_probe(struct snd_soc_dai *dai)
return 0;
}
+#ifdef CONFIG_PM
+int tegra30_i2s_resume(struct snd_soc_dai *cpu_dai)
+{
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+ int i, ret = 0;
+
+ tegra30_i2s_enable_clocks(i2s);
+
+ /*restore the i2s regs*/
+ for (i = 0; i < ((TEGRA30_I2S_CIF_TX_CTRL>>2) + 1); i++)
+ tegra30_i2s_write(i2s, i<<2, i2s->reg_cache[i]);
+
+ tegra30_i2s_disable_clocks(i2s);
+
+ if (i2s->dam_ch_refcount)
+ ret = tegra30_dam_resume(i2s->dam_ifc);
+
+ return ret;
+}
+#else
+#define tegra30_i2s_resume NULL
+#endif
+
static struct snd_soc_dai_ops tegra30_i2s_dai_ops = {
.startup = tegra30_i2s_startup,
.shutdown = tegra30_i2s_shutdown,
@@ -463,6 +489,7 @@ static struct snd_soc_dai_ops tegra30_i2s_dai_ops = {
{ \
.name = DRV_NAME "." #id, \
.probe = tegra30_i2s_probe, \
+ .resume = tegra30_i2s_resume, \
.playback = { \
.channels_min = 1, \
.channels_max = 2, \
diff --git a/sound/soc/tegra/tegra30_i2s.h b/sound/soc/tegra/tegra30_i2s.h
index 6dd09eb63bfb..26c2c87cde52 100644
--- a/sound/soc/tegra/tegra30_i2s.h
+++ b/sound/soc/tegra/tegra30_i2s.h
@@ -246,6 +246,9 @@ struct tegra30_i2s {
int dam_ch_refcount;
int playback_ref_count;
bool is_dam_used;
+#ifdef CONFIG_PM
+ u32 reg_cache[(TEGRA30_I2S_CIF_TX_CTRL >> 2) + 1];
+#endif
};
struct codec_config {