asoc: tegra: restore i2s/das/spdif registers after suspend for t20
Nikesh Oswal [Tue, 13 Dec 2011 09:40:28 +0000 (14:40 +0530)]
when system resumes from suspend state the i2s/das/spdif registers
have power on reset values, this change restores the registers
with their prior values from cache

Bug: 904530

Change-Id: I35c14d95d2d6bf5bc116a1a80e21f4904c8969e5
Signed-off-by: Nikesh Oswal <noswal@nvidia.com>
Reviewed-on: http://git-master/r/69715
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Sumit Bhattacharya <sumitb@nvidia.com>
Reviewed-by: Scott Peterson <speterson@nvidia.com>

sound/soc/tegra/tegra20_das.c
sound/soc/tegra/tegra20_das.h
sound/soc/tegra/tegra20_i2s.c
sound/soc/tegra/tegra20_i2s.h
sound/soc/tegra/tegra20_spdif.c
sound/soc/tegra/tegra20_spdif.h

index b647798..29ce316 100644 (file)
@@ -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);
index 2fd731b..1d7c57f 100644 (file)
 #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)
index 3f83887..c868230 100644 (file)
@@ -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,
index b2d3ef7..ded0c8d 100644 (file)
 
 #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
index e5ef598..3e747b5 100644 (file)
 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,
index 1938aa6..c1fb6ed 100644 (file)
  * 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