summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sound/soc/tegra/tegra20_das.c36
-rw-r--r--sound/soc/tegra/tegra20_das.h11
-rw-r--r--sound/soc/tegra/tegra20_i2s.c51
-rw-r--r--sound/soc/tegra/tegra20_i2s.h9
-rw-r--r--sound/soc/tegra/tegra20_spdif.c52
-rw-r--r--sound/soc/tegra/tegra20_spdif.h8
6 files changed, 167 insertions, 0 deletions
diff --git a/sound/soc/tegra/tegra20_das.c b/sound/soc/tegra/tegra20_das.c
index b647798bfadb..29ce3166052c 100644
--- a/sound/soc/tegra/tegra20_das.c
+++ b/sound/soc/tegra/tegra20_das.c
@@ -37,6 +37,9 @@ static struct tegra20_das *das;
static inline void tegra20_das_write(u32 reg, u32 val)
{
+#ifdef CONFIG_PM
+ das->reg_cache[reg >> 2] = val;
+#endif
__raw_writel(val, das->regs + reg);
}
@@ -45,6 +48,24 @@ static inline u32 tegra20_das_read(u32 reg)
return __raw_readl(das->regs + reg);
}
+#ifdef CONFIG_PM
+int tegra20_das_resume()
+{
+ int i, reg;
+
+ for (i = 0; i <= TEGRA20_DAS_DAP_ID_5; i++)
+ tegra20_das_write(i << 2, das->reg_cache[i]);
+
+ for (i = 0; i <= TEGRA20_DAS_DAC_ID_3; i++) {
+ reg = TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL +
+ (i * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE);
+ tegra20_das_write(reg, das->reg_cache[reg >> 2]);
+ }
+
+ return 0;
+}
+#endif
+
int tegra20_das_connect_dap_to_dac(int dap, int dac)
{
u32 addr;
@@ -168,6 +189,9 @@ static int __devinit tegra20_das_probe(struct platform_device *pdev)
{
struct resource *res, *region;
int ret = 0;
+#ifdef CONFIG_PM
+ int i, reg;
+#endif
if (das)
return -ENODEV;
@@ -202,6 +226,18 @@ static int __devinit tegra20_das_probe(struct platform_device *pdev)
goto err_release;
}
+#ifdef CONFIG_PM
+ /* populate the das reg cache with POR values*/
+ for (i = 0; i <= TEGRA20_DAS_DAP_ID_5; i++)
+ das->reg_cache[i] = tegra20_das_read(i << 2);
+
+ for (i = 0; i <= TEGRA20_DAS_DAC_ID_3; i++) {
+ reg = TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL +
+ (i * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE);
+ das->reg_cache[reg >> 2] = tegra20_das_read(reg);
+ }
+#endif
+
tegra20_das_debug_add(das);
platform_set_drvdata(pdev, das);
diff --git a/sound/soc/tegra/tegra20_das.h b/sound/soc/tegra/tegra20_das.h
index 2fd731b70849..1d7c57fd0092 100644
--- a/sound/soc/tegra/tegra20_das.h
+++ b/sound/soc/tegra/tegra20_das.h
@@ -83,12 +83,23 @@
#define TEGRA20_DAS_DAC_ID_2 1
#define TEGRA20_DAS_DAC_ID_3 2
+#ifdef CONFIG_PM
+#define TEGRA20_DAS_CACHE_SIZE ((((TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL) + (TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE*TEGRA20_DAS_DAC_ID_3))>>2) + 1)
+#endif
+
struct tegra20_das {
struct device *dev;
void __iomem *regs;
struct dentry *debug;
+#ifdef CONFIG_PM
+ u32 reg_cache[TEGRA20_DAS_CACHE_SIZE];
+#endif
};
+#ifdef CONFIG_PM
+/* Restores the das registers from cache */
+extern int tegra20_das_resume();
+#endif
/*
* Terminology:
* DAS: Digital audio switch (HW module controlled by this driver)
diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c
index 3f83887a7185..c8682302b4aa 100644
--- a/sound/soc/tegra/tegra20_i2s.c
+++ b/sound/soc/tegra/tegra20_i2s.c
@@ -49,6 +49,9 @@
static inline void tegra20_i2s_write(struct tegra20_i2s *i2s, u32 reg, u32 val)
{
+#ifdef CONFIG_PM
+ i2s->reg_cache[reg >> 2] = val;
+#endif
__raw_writel(val, i2s->regs + reg);
}
@@ -336,13 +339,59 @@ static int tegra20_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
static int tegra20_i2s_probe(struct snd_soc_dai *dai)
{
struct tegra20_i2s * i2s = snd_soc_dai_get_drvdata(dai);
+#ifdef CONFIG_PM
+ int i;
+#endif
dai->capture_dma_data = &i2s->capture_dma_data;
dai->playback_dma_data = &i2s->playback_dma_data;
+#ifdef CONFIG_PM
+ /* populate the i2s reg cache with POR values*/
+ clk_enable(i2s->clk_i2s);
+
+ for (i = 0; i < ((TEGRA20_I2S_TDM_TX_RX_CTRL >> 2) + 1); i++) {
+ if ((i == TEGRA20_I2S_CACHE_RSVD_6) ||
+ (i == TEGRA20_I2S_CACHE_RSVD_7))
+ continue;
+
+ i2s->reg_cache[i] = tegra20_i2s_read(i2s, i << 2);
+ }
+
+ clk_disable(i2s->clk_i2s);
+#endif
+
return 0;
}
+#ifdef CONFIG_PM
+int tegra20_i2s_resume(struct snd_soc_dai *cpu_dai)
+{
+ struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+ int i;
+
+ clk_enable(i2s->clk_i2s);
+
+ /*restore the i2s regs*/
+ for (i = 0; i < ((TEGRA20_I2S_TDM_TX_RX_CTRL >> 2) + 1); i++) {
+ if ((i == TEGRA20_I2S_CACHE_RSVD_6) ||
+ (i == TEGRA20_I2S_CACHE_RSVD_7))
+ continue;
+
+ tegra20_i2s_write(i2s, i << 2, i2s->reg_cache[i]);
+ }
+
+ /*restore the das regs*/
+ tegra20_das_resume();
+
+ clk_disable(i2s->clk_i2s);
+
+ return 0;
+}
+#else
+#define tegra20_i2s_resume NULL
+#endif
+
static struct snd_soc_dai_ops tegra20_i2s_dai_ops = {
.set_fmt = tegra20_i2s_set_fmt,
.hw_params = tegra20_i2s_hw_params,
@@ -353,6 +402,7 @@ struct snd_soc_dai_driver tegra20_i2s_dai[] = {
{
.name = DRV_NAME ".0",
.probe = tegra20_i2s_probe,
+ .resume = tegra20_i2s_resume,
.playback = {
.channels_min = 1,
.channels_max = 2,
@@ -371,6 +421,7 @@ struct snd_soc_dai_driver tegra20_i2s_dai[] = {
{
.name = DRV_NAME ".1",
.probe = tegra20_i2s_probe,
+ .resume = tegra20_i2s_resume,
.playback = {
.channels_min = 1,
.channels_max = 2,
diff --git a/sound/soc/tegra/tegra20_i2s.h b/sound/soc/tegra/tegra20_i2s.h
index b2d3ef710599..ded0c8dee023 100644
--- a/sound/soc/tegra/tegra20_i2s.h
+++ b/sound/soc/tegra/tegra20_i2s.h
@@ -177,6 +177,12 @@
#define TEGRA20_I2S_PCM_CTRL_RCV_MODE_EN (1 << 0)
+#ifdef CONFIG_PM
+/* unused cache locations for i2s reg cache */
+#define TEGRA20_I2S_CACHE_RSVD_6 ((TEGRA20_I2S_NW_CTRL>>2) + 1)
+#define TEGRA20_I2S_CACHE_RSVD_7 (TEGRA20_I2S_CACHE_RSVD_6 + 1)
+#endif
+
struct tegra20_i2s {
struct clk *clk_i2s;
struct tegra_pcm_dma_params capture_dma_data;
@@ -184,6 +190,9 @@ struct tegra20_i2s {
void __iomem *regs;
struct dentry *debug;
u32 reg_ctrl;
+#ifdef CONFIG_PM
+ u32 reg_cache[(TEGRA20_I2S_TDM_TX_RX_CTRL >> 2) + 1];
+#endif
};
#endif
diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c
index e5ef59841566..3e747b5e1931 100644
--- a/sound/soc/tegra/tegra20_spdif.c
+++ b/sound/soc/tegra/tegra20_spdif.c
@@ -42,6 +42,13 @@
static inline void tegra20_spdif_write(struct tegra20_spdif *spdif, u32 reg,
u32 val)
{
+#ifdef CONFIG_PM
+ if (reg < TEGRA20_SPDIF_CH_STA_TX_A)
+ spdif->reg_ctrl_cache[reg >> 2] = val;
+ else
+ spdif->reg_tx_cache[((reg - TEGRA20_SPDIF_CH_STA_TX_A) >> 2)]
+ = val;
+#endif
__raw_writel(val, spdif->regs + reg);
}
@@ -250,13 +257,57 @@ static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
static int tegra20_spdif_probe(struct snd_soc_dai *dai)
{
struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+#ifdef CONFIG_PM
+ int i, reg;
+#endif
dai->capture_dma_data = NULL;
dai->playback_dma_data = &spdif->playback_dma_data;
+#ifdef CONFIG_PM
+ clk_enable(spdif->clk_spdif_out);
+
+ /* populate the spdif reg cache with POR values*/
+ for (i = 0; i < TEGRA20_SPDIF_CTRL_CACHE_SIZE; i++)
+ spdif->reg_ctrl_cache[i] = tegra20_spdif_read(spdif, i << 2);
+
+ for (i = 0; i < TEGRA20_SPDIF_TX_CACHE_SIZE; i++) {
+ reg = (TEGRA20_SPDIF_CH_STA_TX_A) + (i << 2);
+ spdif->reg_tx_cache[i] = tegra20_spdif_read(spdif, reg);
+ }
+
+ clk_disable(spdif->clk_spdif_out);
+
+#endif
+
return 0;
}
+#ifdef CONFIG_PM
+int tegra20_spdif_resume(struct snd_soc_dai *cpu_dai)
+{
+ struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(cpu_dai);
+ int i, reg;
+
+ clk_enable(spdif->clk_spdif_out);
+
+ /*restore the spdif regs*/
+ for (i = 0; i < TEGRA20_SPDIF_CTRL_CACHE_SIZE; i++)
+ tegra20_spdif_write(spdif, i << 2, spdif->reg_ctrl_cache[i]);
+
+ for (i = 0; i < TEGRA20_SPDIF_TX_CACHE_SIZE; i++) {
+ reg = (TEGRA20_SPDIF_CH_STA_TX_A) + (i << 2);
+ tegra20_spdif_write(spdif, reg, spdif->reg_tx_cache[i]);
+ }
+
+ clk_disable(spdif->clk_spdif_out);
+
+ return 0;
+}
+#else
+#define tegra20_spdif_resume NULL
+#endif
+
static struct snd_soc_dai_ops tegra20_spdif_dai_ops = {
.hw_params = tegra20_spdif_hw_params,
.trigger = tegra20_spdif_trigger,
@@ -265,6 +316,7 @@ static struct snd_soc_dai_ops tegra20_spdif_dai_ops = {
struct snd_soc_dai_driver tegra20_spdif_dai = {
.name = DRV_NAME,
.probe = tegra20_spdif_probe,
+ .resume = tegra20_spdif_resume,
.playback = {
.channels_min = 2,
.channels_max = 2,
diff --git a/sound/soc/tegra/tegra20_spdif.h b/sound/soc/tegra/tegra20_spdif.h
index 1938aa67917f..c1fb6ed2a79f 100644
--- a/sound/soc/tegra/tegra20_spdif.h
+++ b/sound/soc/tegra/tegra20_spdif.h
@@ -535,6 +535,10 @@
* This 4-word deep FIFO transmits user FIFO field information. The order of
* transmission is from LSB to MSB bit.
*/
+#ifdef CONFIG_PM
+#define TEGRA20_SPDIF_CTRL_CACHE_SIZE ((TEGRA20_SPDIF_DATA_FIFO_CSR >> 2) + 1)
+#define TEGRA20_SPDIF_TX_CACHE_SIZE (((TEGRA20_SPDIF_CH_STA_TX_F - TEGRA20_SPDIF_CH_STA_TX_A) >> 2) + 1)
+#endif
struct tegra20_spdif {
struct clk *clk_spdif_out;
@@ -543,6 +547,10 @@ struct tegra20_spdif {
void __iomem *regs;
struct dentry *debug;
u32 reg_ctrl;
+#ifdef CONFIG_PM
+ u32 reg_ctrl_cache[TEGRA20_SPDIF_CTRL_CACHE_SIZE];
+ u32 reg_tx_cache[TEGRA20_SPDIF_TX_CACHE_SIZE];
+#endif
};
#endif