asoc: tegra: Change HW disabling dequence and I2S clock parent
Nikesh Oswal [Fri, 22 Jun 2012 07:24:52 +0000 (12:24 +0530)]
Change HW disabling dequence and I2S clock parent in slave mode
for voice call use-case

Bug: 1005176
Signed-off-by: Nikesh Oswal <noswal@nvidia.com>
Reviewed-on: http://git-master/r/110529
(cherry picked from commit 4b138cdeb3374575bde9f49d0c644faa91ced68f)

Change-Id: Ia037ed5ef45d38972c3e1e1a78b4b7b7f39d8f72
Reviewed-on: http://git-master/r/114444
Reviewed-by: Automatic_Commit_Validation_User
Tested-by: Nikesh Oswal <noswal@nvidia.com>
Reviewed-by: Scott Peterson <speterson@nvidia.com>

sound/soc/tegra/tegra30_ahub.c
sound/soc/tegra/tegra30_ahub.h
sound/soc/tegra/tegra30_dam.c
sound/soc/tegra/tegra30_i2s.c

index 30b7e48..6fac8fc 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Author: Stephen Warren <swarren@nvidia.com>
  * Copyright (C) 2011 - NVIDIA, Inc.
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -302,6 +303,42 @@ int tegra30_ahub_tx_fifo_is_enabled(int i2s_id)
        return val;
 }
 
+int tegra30_ahub_dam_ch0_is_enabled(int dam_id)
+{
+       int val, mask;
+
+       val = tegra30_apbif_read((TEGRA30_AHUB_DAM_LIVE_STATUS) +
+                       (dam_id * TEGRA30_AHUB_DAM_LIVE_STATUS_STRIDE));
+       mask = TEGRA30_AHUB_DAM_LIVE_STATUS_RX0_ENABLED;
+       val &= mask;
+
+       return val;
+}
+
+int tegra30_ahub_dam_ch1_is_enabled(int dam_id)
+{
+       int val, mask;
+
+       val = tegra30_apbif_read((TEGRA30_AHUB_DAM_LIVE_STATUS) +
+                       (dam_id * TEGRA30_AHUB_DAM_LIVE_STATUS_STRIDE));
+       mask = TEGRA30_AHUB_DAM_LIVE_STATUS_RX1_ENABLED;
+       val &= mask;
+
+       return val;
+}
+
+int tegra30_ahub_dam_tx_is_enabled(int dam_id)
+{
+       int val, mask;
+
+       val = tegra30_apbif_read((TEGRA30_AHUB_DAM_LIVE_STATUS) +
+                       (dam_id * TEGRA30_AHUB_DAM_LIVE_STATUS_STRIDE));
+       mask = TEGRA30_AHUB_DAM_LIVE_STATUS_TX_ENABLED;
+       val &= mask;
+
+       return val;
+}
+
 int tegra30_ahub_set_rx_fifo_pack_mode(enum tegra30_ahub_rxcif rxcif,
                                                        unsigned int pack_mode)
 {
index 8dc27ab..b5fd236 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Author: Stephen Warren <swarren@nvidia.com>
  * Copyright (C) 2011 - NVIDIA, Inc.
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -502,6 +503,9 @@ extern int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif);
 
 extern int tegra30_ahub_rx_fifo_is_enabled(int i2s_id);
 extern int tegra30_ahub_tx_fifo_is_enabled(int i2s_id);
+extern int tegra30_ahub_dam_ch0_is_enabled(int dam_id);
+extern int tegra30_ahub_dam_ch1_is_enabled(int dam_id);
+extern int tegra30_ahub_dam_tx_is_enabled(int dam_id);
 
 #ifdef CONFIG_PM
 extern int tegra30_ahub_apbif_resume(void);
index d308179..8460266 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Author: Nikesh Oswal <noswal@nvidia.com>
  * Copyright (C) 2011 - NVIDIA, Inc.
+ * Copyright (C) 2012, NVIDIA CORPORATION. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -27,6 +28,7 @@
 #include <linux/platform_device.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
+#include <linux/delay.h>
 #include <linux/io.h>
 #include <sound/soc.h>
 #include "tegra30_dam.h"
@@ -455,6 +457,8 @@ int tegra30_dam_set_acif(int ifc, int chid, unsigned int audio_channels,
 void tegra30_dam_enable(int ifc, int on, int chid)
 {
        u32 old_val, val, enreg;
+       u32 old_val_dam, val_dam;
+       int dcnt = 10;
        struct tegra30_dam_context *dam = dams_cont_info[ifc];
 
        if (ifc >= TEGRA30_NR_DAM_IFC)
@@ -476,19 +480,46 @@ void tegra30_dam_enable(int ifc, int on, int chid)
                        val &= ~TEGRA30_DAM_CH0_CTRL_EN;
        }
 
-       if (val != old_val)
-               tegra30_dam_writel(dam, val, enreg);
-
-       old_val = val = tegra30_dam_readl(dam, TEGRA30_DAM_CTRL);
+       old_val_dam = val_dam = tegra30_dam_readl(dam, TEGRA30_DAM_CTRL);
 
        if (dam->ch_enable_refcnt[dam_ch_in0] ||
                dam->ch_enable_refcnt[dam_ch_in1])
-               val |= TEGRA30_DAM_CTRL_DAM_EN;
+               val_dam |= TEGRA30_DAM_CTRL_DAM_EN;
        else
-               val &= ~TEGRA30_DAM_CTRL_DAM_EN;
+               val_dam &= ~TEGRA30_DAM_CTRL_DAM_EN;
+
+       if (val != old_val) {
+               tegra30_dam_writel(dam, val, enreg);
+
+               if (!on) {
+                       if (chid == dam_ch_in0) {
+                               while (tegra30_ahub_dam_ch0_is_enabled(ifc)
+                                       && dcnt--)
+                                       udelay(100);
+
+                               dcnt = 10;
+                       }
+                       else {
+                               while (tegra30_ahub_dam_ch1_is_enabled(ifc)
+                                       && dcnt--)
+                                       udelay(100);
+
+                               dcnt = 10;
+                       }
+               }
+       }
+
+       if (old_val_dam != val_dam) {
+               tegra30_dam_writel(dam, val_dam, TEGRA30_DAM_CTRL);
+
+               if (!on) {
+                       while (tegra30_ahub_dam_tx_is_enabled(ifc) && dcnt--)
+                               udelay(100);
 
-       if (old_val != val)
-               tegra30_dam_writel(dam, val, TEGRA30_DAM_CTRL);
+                       dcnt = 10;
+               }
+
+       }
 }
 
 void tegra30_dam_ch0_set_datasync(struct tegra30_dam_context *dam, int datasync)
index a50b853..72e6447 100644 (file)
@@ -7,6 +7,7 @@
  * Based on code copyright/by:
  *
  * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
  * Scott Peterson <speterson@nvidia.com>
  *
  * Copyright (C) 2010 Google, Inc.
@@ -811,7 +812,47 @@ static int configure_baseband_i2s(struct tegra30_i2s  *i2s, int is_i2smaster,
                int is_formatdsp, int channels, int rate, int bitsize)
 {
        u32 val;
-       int i2sclock, bitcnt;
+       int i2sclock, bitcnt, ret;
+
+       i2sclock = rate * channels * bitsize * 2;
+
+       /* additional 8 for baseband */
+       if (is_formatdsp)
+               i2sclock *= 8;
+
+       if (is_i2smaster) {
+               ret = clk_set_parent(i2s->clk_i2s, i2s->clk_pll_a_out0);
+               if (ret) {
+                       pr_err("Can't set parent of I2S clock\n");
+                       return ret;
+               }
+
+               ret = clk_set_rate(i2s->clk_i2s, i2sclock);
+               if (ret) {
+                       pr_err("Can't set I2S clock rate: %d\n", ret);
+                       return ret;
+               }
+       } else {
+               ret = clk_set_rate(i2s->clk_i2s_sync, i2sclock);
+               if (ret) {
+                       pr_err("Can't set I2S sync clock rate\n");
+                       return ret;
+               }
+
+               ret = clk_set_rate(i2s->clk_audio_2x, i2sclock);
+               if (ret) {
+                       pr_err("Can't set I2S sync clock rate\n");
+                       return ret;
+               }
+
+               ret = clk_set_parent(i2s->clk_i2s, i2s->clk_audio_2x);
+               if (ret) {
+                       pr_err("Can't set parent of audio2x clock\n");
+                       return ret;
+               }
+       }
+
+       tegra30_i2s_enable_clocks(i2s);
 
        i2s->reg_ctrl &= ~(TEGRA30_I2S_CTRL_FRAME_FORMAT_MASK |
                                        TEGRA30_I2S_CTRL_LRCK_MASK |
@@ -846,14 +887,6 @@ static int configure_baseband_i2s(struct tegra30_i2s  *i2s, int is_i2smaster,
              (1 << TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT);
        tegra30_i2s_write(i2s, TEGRA30_I2S_OFFSET, val);
 
-       i2sclock = rate * channels * bitsize * 2;
-
-       /* additional 8 for baseband */
-       if (is_formatdsp)
-               i2sclock *= 8;
-
-       clk_set_rate(i2s->clk_i2s, i2sclock);
-
        if (is_formatdsp) {
                bitcnt = (i2sclock/rate) - 1;
                val = bitcnt << TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
@@ -916,8 +949,6 @@ int tegra30_make_voice_call_connections(struct codec_config *codec_info,
 
        codec_i2s = &i2scont[codec_info->i2s_id];
        bb_i2s = &i2scont[bb_info->i2s_id];
-       tegra30_i2s_enable_clocks(codec_i2s);
-       tegra30_i2s_enable_clocks(bb_i2s);
 
        /* increment the codec i2s playback ref count */
        codec_i2s->playback_ref_count++;
@@ -985,61 +1016,89 @@ int tegra30_break_voice_call_connections(struct codec_config *codec_info,
 {
        struct tegra30_i2s  *codec_i2s;
        struct tegra30_i2s  *bb_i2s;
+       int dcnt = 10;
 
        codec_i2s = &i2scont[codec_info->i2s_id];
        bb_i2s = &i2scont[bb_info->i2s_id];
 
-       /* disconnect the ahub connections */
-
-       /* if this is the only user of i2s tx then break ahub
-       i2s rx connection */
-       if (codec_i2s->playback_ref_count == 1)
-               tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_I2S0_RX0
-                       + codec_info->i2s_id);
+       /*Disable Codec I2S RX (TX to ahub)*/
+       codec_i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_RX;
+       tegra30_i2s_write(codec_i2s, TEGRA30_I2S_CTRL, codec_i2s->reg_ctrl);
 
-       tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_I2S0_RX0
-                               + bb_info->i2s_id);
-       tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX0
-                               + (codec_i2s->dam_ifc*2));
-       tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX0
-                               + (bb_i2s->dam_ifc*2));
+       while (tegra30_ahub_rx_fifo_is_enabled(codec_i2s->id) && dcnt--)
+               udelay(100);
 
-       /* disable the i2s */
+       dcnt = 10;
 
-       /* if this is the only user of i2s tx then disable it*/
-       if (codec_i2s->playback_ref_count == 1)
-                       codec_i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_TX;
+       /*Disable baseband DAM*/
+       tegra30_dam_enable(bb_i2s->dam_ifc, TEGRA30_DAM_DISABLE,
+                       TEGRA30_DAM_CHIN0_SRC);
+       tegra30_dam_free_channel(bb_i2s->dam_ifc, TEGRA30_DAM_CHIN0_SRC);
+       bb_i2s->dam_ch_refcount--;
+       if (!bb_i2s->dam_ch_refcount)
+               tegra30_dam_free_controller(bb_i2s->dam_ifc);
 
-       codec_i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_RX;
-       tegra30_i2s_write(codec_i2s, TEGRA30_I2S_CTRL, codec_i2s->reg_ctrl);
+       /*Disable baseband I2S TX (RX from ahub)*/
        bb_i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_TX;
+       tegra30_i2s_write(bb_i2s, TEGRA30_I2S_CTRL, bb_i2s->reg_ctrl);
+
+       while (tegra30_ahub_tx_fifo_is_enabled(bb_i2s->id) && dcnt--)
+               udelay(100);
+
+       dcnt = 10;
+
+       /*Disable baseband I2S RX (TX to ahub)*/
        bb_i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_RX;
        tegra30_i2s_write(bb_i2s, TEGRA30_I2S_CTRL, bb_i2s->reg_ctrl);
-       tegra30_i2s_disable_clocks(codec_i2s);
-       tegra30_i2s_disable_clocks(bb_i2s);
 
-       /* decrement the codec i2s playback ref count */
-       codec_i2s->playback_ref_count--;
-       bb_i2s->playback_ref_count--;
+       while (tegra30_ahub_rx_fifo_is_enabled(bb_i2s->id) && dcnt--)
+               udelay(100);
 
-       /* disable the codec dam */
+       dcnt = 10;
+
+       /*Disable Codec DAM*/
        tegra30_dam_enable(codec_i2s->dam_ifc,
                TEGRA30_DAM_DISABLE, TEGRA30_DAM_CHIN0_SRC);
-       tegra30_dam_disable_clock(codec_i2s->dam_ifc);
        tegra30_dam_free_channel(codec_i2s->dam_ifc,
                TEGRA30_DAM_CHIN0_SRC);
        codec_i2s->dam_ch_refcount--;
        if (!codec_i2s->dam_ch_refcount)
                tegra30_dam_free_controller(codec_i2s->dam_ifc);
 
-       /* disable the bb dam */
-       tegra30_dam_enable(bb_i2s->dam_ifc, TEGRA30_DAM_DISABLE,
-                       TEGRA30_DAM_CHIN0_SRC);
+       /*Disable Codec I2S TX (RX from ahub)*/
+       if (codec_i2s->playback_ref_count == 1)
+                       codec_i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_TX;
+
+       tegra30_i2s_write(codec_i2s, TEGRA30_I2S_CTRL, codec_i2s->reg_ctrl);
+
+       while (tegra30_ahub_tx_fifo_is_enabled(codec_i2s->id) && dcnt--)
+               udelay(100);
+
+       dcnt = 10;
+
+       /* Disconnect the ahub connections */
+       /* If this is the only user of i2s tx then break ahub
+       i2s rx connection */
+       if (codec_i2s->playback_ref_count == 1)
+               tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_I2S0_RX0
+                       + codec_info->i2s_id);
+
+       tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_I2S0_RX0
+                               + bb_info->i2s_id);
+       tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX0
+                               + (codec_i2s->dam_ifc*2));
+       tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX0
+                               + (bb_i2s->dam_ifc*2));
+
+       /* Decrement the codec and bb i2s playback ref count */
+       codec_i2s->playback_ref_count--;
+       bb_i2s->playback_ref_count--;
+
+       /* Disable the clocks */
+       tegra30_i2s_disable_clocks(codec_i2s);
+       tegra30_i2s_disable_clocks(bb_i2s);
+       tegra30_dam_disable_clock(codec_i2s->dam_ifc);
        tegra30_dam_disable_clock(bb_i2s->dam_ifc);
-       tegra30_dam_free_channel(bb_i2s->dam_ifc, TEGRA30_DAM_CHIN0_SRC);
-       bb_i2s->dam_ch_refcount--;
-       if (!bb_i2s->dam_ch_refcount)
-               tegra30_dam_free_controller(bb_i2s->dam_ifc);
 
        return 0;
 }