From 8f751e925da2510ded1003976186a379853b134e Mon Sep 17 00:00:00 2001 From: Nikesh Oswal Date: Fri, 22 Jun 2012 12:54:52 +0530 Subject: asoc: tegra: Change HW disabling dequence and I2S clock parent Change HW disabling dequence and I2S clock parent in slave mode for voice call use-case Bug: 1005176 Signed-off-by: Nikesh Oswal 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 Reviewed-by: Scott Peterson --- sound/soc/tegra/tegra30_ahub.c | 37 +++++++++++ sound/soc/tegra/tegra30_ahub.h | 4 ++ sound/soc/tegra/tegra30_dam.c | 47 ++++++++++--- sound/soc/tegra/tegra30_i2s.c | 147 +++++++++++++++++++++++++++++------------ 4 files changed, 183 insertions(+), 52 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c index 30b7e481acc4..6fac8fc0177b 100644 --- a/sound/soc/tegra/tegra30_ahub.c +++ b/sound/soc/tegra/tegra30_ahub.c @@ -3,6 +3,7 @@ * * Author: Stephen Warren * 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) { diff --git a/sound/soc/tegra/tegra30_ahub.h b/sound/soc/tegra/tegra30_ahub.h index 8dc27abc5aed..b5fd2363f080 100644 --- a/sound/soc/tegra/tegra30_ahub.h +++ b/sound/soc/tegra/tegra30_ahub.h @@ -3,6 +3,7 @@ * * Author: Stephen Warren * 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); diff --git a/sound/soc/tegra/tegra30_dam.c b/sound/soc/tegra/tegra30_dam.c index d308179110c9..8460266d0d66 100644 --- a/sound/soc/tegra/tegra30_dam.c +++ b/sound/soc/tegra/tegra30_dam.c @@ -3,6 +3,7 @@ * * Author: Nikesh Oswal * 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 #include #include +#include #include #include #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) diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c index a50b853135ae..72e64470008a 100644 --- a/sound/soc/tegra/tegra30_i2s.c +++ b/sound/soc/tegra/tegra30_i2s.c @@ -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 * * 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; } -- cgit v1.2.3