diff options
author | Nikesh Oswal <noswal@nvidia.com> | 2011-11-18 14:59:12 +0530 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:50:23 -0800 |
commit | ab8abd6275cfe826002956d13a72d290ae344474 (patch) | |
tree | dee88307d894907f8894e97d2db1786003230f18 /sound | |
parent | 9f655d719048c0eda67e041f365325f07ac0eef5 (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.c | 36 | ||||
-rw-r--r-- | sound/soc/tegra/tegra30_ahub.h | 16 | ||||
-rw-r--r-- | sound/soc/tegra/tegra30_dam.c | 17 | ||||
-rw-r--r-- | sound/soc/tegra/tegra30_dam.h | 5 | ||||
-rw-r--r-- | sound/soc/tegra/tegra30_i2s.c | 27 | ||||
-rw-r--r-- | sound/soc/tegra/tegra30_i2s.h | 3 |
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 { |