summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorLei Fan <leif@nvidia.com>2014-01-07 14:31:23 +0800
committerMartin Chi <mchi@nvidia.com>2014-01-12 21:19:22 -0800
commit6cc9dbf67be7869734630e8f29e94b972d14453e (patch)
tree2783a99d74c57b73a65f64aee16e6dcab25c70af /sound
parente2f1676b53b198bfb568443b7fb935c905100ab3 (diff)
ASoC:Tegra:dual mic support
In order to use dual mic to support nvoice, DAMs should be removed form UL and playback should use channle0 of DAM in DL to make SRC not channel1 again. Bug 1431667 Change-Id: I88d53d315a2d0a6aa74e414861af54061763d2ac Signed-off-by: Lei Fan <leif@nvidia.com> Reviewed-on: http://git-master/r/352587 Reviewed-by: Martin Chi <mchi@nvidia.com> Tested-by: Martin Chi <mchi@nvidia.com>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/tegra/tegra30_dam.c12
-rw-r--r--sound/soc/tegra/tegra30_dam.h3
-rw-r--r--sound/soc/tegra/tegra30_i2s.c330
-rw-r--r--sound/soc/tegra/tegra30_i2s.h11
-rw-r--r--sound/soc/tegra/tegra_rt5640.c252
5 files changed, 580 insertions, 28 deletions
diff --git a/sound/soc/tegra/tegra30_dam.c b/sound/soc/tegra/tegra30_dam.c
index 4dfcd117171f..91ea8967d62a 100644
--- a/sound/soc/tegra/tegra30_dam.c
+++ b/sound/soc/tegra/tegra30_dam.c
@@ -3,7 +3,7 @@
*
* Author: Nikesh Oswal <noswal@nvidia.com>
* Copyright (C) 2011 - NVIDIA, Inc.
- * Copyright (C) 2012-2013, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2012-2014, 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
@@ -777,7 +777,7 @@ void tegra30_dam_write_coeff_ram(int ifc, int fsin, int fsout)
TEGRA30_DAM_AUDIORAMCTL_DAM_CTRL_0);
for (i = 0; i < 64; i++) {
- val = coefRam[i];
+ val = coefRam[i];
tegra30_dam_writel(dams_cont_info[ifc], val,
TEGRA30_DAM_AUDIORAMCTL_DAM_DATA_0);
}
@@ -990,9 +990,11 @@ void tegra30_dam_enable(int ifc, int on, int chid)
}
}
-void tegra30_dam_ch0_set_datasync(struct tegra30_dam_context *dam, int datasync)
+void tegra30_dam_ch0_set_datasync(int ifc, int datasync)
{
u32 val;
+ struct tegra30_dam_context *dam;
+ dam = dams_cont_info[ifc];
val = tegra30_dam_readl(dam, TEGRA30_DAM_CH0_CTRL);
val &= ~TEGRA30_DAM_CH0_CTRL_DATA_SYNC_MASK;
@@ -1000,9 +1002,11 @@ void tegra30_dam_ch0_set_datasync(struct tegra30_dam_context *dam, int datasync)
tegra30_dam_writel(dam, val, TEGRA30_DAM_CH0_CTRL);
}
-void tegra30_dam_ch1_set_datasync(struct tegra30_dam_context *dam, int datasync)
+void tegra30_dam_ch1_set_datasync(int ifc, int datasync)
{
u32 val;
+ struct tegra30_dam_context *dam;
+ dam = dams_cont_info[ifc];
val = tegra30_dam_readl(dam, TEGRA30_DAM_CH1_CTRL);
val &= ~TEGRA30_DAM_CH1_CTRL_DATA_SYNC_MASK;
diff --git a/sound/soc/tegra/tegra30_dam.h b/sound/soc/tegra/tegra30_dam.h
index 4ded54b7b390..8d56a3c95bd5 100644
--- a/sound/soc/tegra/tegra30_dam.h
+++ b/sound/soc/tegra/tegra30_dam.h
@@ -3,6 +3,7 @@
*
* Author: Nikesh Oswal <noswal@nvidia.com>
* Copyright (C) 2011 - NVIDIA, Inc.
+ * Copyright (C) 2012-2014, 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
@@ -192,6 +193,8 @@ void tegra30_dam_set_biquad_fixed_coef(int ifc);
void tegra30_dam_enable_coeff_ram(int ifc);
void tegra30_dam_set_filter_stages(int ifc, int fsin, int fsout);
void tegra30_dam_enable_stereo_mixing(int ifc);
+void tegra30_dam_ch0_set_datasync(int ifc, int datasync);
+void tegra30_dam_ch1_set_datasync(int ifc, int datasync);
#endif
#endif
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
index b7ec64f31390..ae9383ec8eb0 100644
--- a/sound/soc/tegra/tegra30_i2s.c
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -1445,7 +1445,7 @@ static int configure_dam(struct tegra30_i2s *i2s, int out_channel,
}
-int tegra30_make_voice_call_connections(struct codec_config *codec_info,
+int tegra30_make_bt_voice_call_connections(struct codec_config *codec_info,
struct codec_config *bb_info,
int uses_voice_codec)
{
@@ -1646,7 +1646,7 @@ int tegra30_make_voice_call_connections(struct codec_config *codec_info,
return 0;
}
-int tegra30_break_voice_call_connections(struct codec_config *codec_info,
+int tegra30_break_bt_voice_call_connections(struct codec_config *codec_info,
struct codec_config *bb_info,
int uses_voice_codec)
{
@@ -1772,6 +1772,332 @@ int tegra30_break_voice_call_connections(struct codec_config *codec_info,
return 0;
}
+int tegra30_make_voice_call_connections(struct codec_config *codec_info,
+ struct codec_config *bb_info,
+ int uses_voice_codec)
+{
+ struct tegra30_i2s *codec_i2s;
+ struct tegra30_i2s *bb_i2s;
+ int reg, ret;
+#ifndef CONFIG_ARCH_TEGRA_3x_SOC
+ int val;
+#endif
+ int bb_i2sclock, bb_is_formatdsp, codec_i2sclock, codec_is_formatdsp;
+
+ codec_i2s = &i2scont[codec_info->i2s_id];
+ bb_i2s = &i2scont[bb_info->i2s_id];
+
+ /* increment the codec i2s playback ref count */
+ codec_i2s->playback_ref_count++;
+ bb_i2s->playback_ref_count++;
+ codec_i2s->capture_ref_count++;
+ bb_i2s->capture_ref_count++;
+
+ /* Make sure i2s is disabled during the configiration */
+ /* Soft reset to make sure DL and UL be not lost*/
+ tegra30_i2s_enable_clocks(codec_i2s);
+ reg = codec_i2s->reg_ctrl;
+ reg &= ~TEGRA30_I2S_CTRL_TX_FLOWCTL_EN;
+ reg &= ~TEGRA30_I2S_CTRL_XFER_EN_TX;
+ reg &= ~TEGRA30_I2S_CTRL_XFER_EN_RX;
+ tegra30_i2s_write(codec_i2s, TEGRA30_I2S_CTRL,
+ reg | TEGRA30_I2S_CTRL_SOFT_RESET);
+ tegra30_i2s_disable_clocks(codec_i2s);
+
+ tegra30_i2s_enable_clocks(bb_i2s);
+ reg = bb_i2s->reg_ctrl;
+ reg &= ~TEGRA30_I2S_CTRL_TX_FLOWCTL_EN;
+ reg &= ~TEGRA30_I2S_CTRL_XFER_EN_TX;
+ reg &= ~TEGRA30_I2S_CTRL_XFER_EN_RX;
+ tegra30_i2s_write(bb_i2s, TEGRA30_I2S_CTRL,
+ reg | TEGRA30_I2S_CTRL_SOFT_RESET);
+ tegra30_i2s_disable_clocks(bb_i2s);
+
+ msleep(20);
+
+ /* get bitclock of modem */
+ codec_is_formatdsp = (codec_info->i2s_mode == TEGRA_DAIFMT_DSP_A) ||
+ (codec_info->i2s_mode == TEGRA_DAIFMT_DSP_B);
+
+ if (codec_info->bit_clk) {
+ codec_i2sclock = codec_info->bit_clk;
+ } else {
+ codec_i2sclock = codec_info->rate * codec_info->channels *
+ codec_info->bitsize * 2;
+ /* additional 8 for baseband */
+ if (codec_is_formatdsp)
+ codec_i2sclock *= 8;
+ }
+
+ /* get bitclock of codec */
+ bb_is_formatdsp = (bb_info->i2s_mode == TEGRA_DAIFMT_DSP_A) ||
+ (bb_info->i2s_mode == TEGRA_DAIFMT_DSP_B);
+
+ if (bb_info->bit_clk) {
+ bb_i2sclock = bb_info->bit_clk;
+ } else {
+ bb_i2sclock = bb_info->rate * bb_info->channels *
+ bb_info->bitsize * 2;
+ /* additional 8 for baseband */
+ if (bb_is_formatdsp)
+ bb_i2sclock *= 8;
+ }
+ /* If we have two modems and one is master device and the other
+ * is slave.Audio will be inaduible with the slave modem after
+ * using the master modem*/
+ configure_voice_call_clocks(codec_info, codec_i2sclock,
+ bb_info, bb_i2sclock);
+
+ /* Configure codec i2s */
+ configure_baseband_i2s(codec_i2s, codec_info,
+ codec_i2sclock, codec_is_formatdsp);
+
+ /* Configure bb i2s */
+ configure_baseband_i2s(bb_i2s, bb_info,
+ bb_i2sclock, bb_is_formatdsp);
+
+ if (uses_voice_codec) {
+ tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_APBIF_RX0 +
+ codec_info->i2s_id);
+ tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_APBIF_RX0 +
+ bb_info->i2s_id);
+
+ tegra30_ahub_set_rx_cif_source(TEGRA30_AHUB_RXCIF_I2S0_RX0 +
+ bb_info->i2s_id, TEGRA30_AHUB_TXCIF_I2S0_TX0 +
+ codec_info->i2s_id);
+ tegra30_ahub_set_rx_cif_source(TEGRA30_AHUB_RXCIF_I2S0_RX0 +
+ codec_info->i2s_id, TEGRA30_AHUB_TXCIF_I2S0_TX0 +
+ bb_info->i2s_id);
+ } else {
+
+ /*configure codec dam*/
+ ret = configure_dam(codec_i2s,
+ codec_info->channels,
+ codec_info->rate,
+ codec_info->bitsize,
+ bb_info->channels,
+ 48000,
+ bb_info->bitsize);
+ if (ret != 0) {
+ pr_err("Error: Failed configure_dam\n");
+ return ret;
+ }
+
+ ret = tegra30_dam_allocate_channel(codec_i2s->dam_ifc,
+ TEGRA30_DAM_CHIN1);
+ if (ret)
+ pr_info("%s:Failed to allocate dam\n", __func__);
+
+ tegra30_dam_set_gain(codec_i2s->dam_ifc,
+ TEGRA30_DAM_CHIN1, 0x1000);
+ tegra30_dam_set_acif(codec_i2s->dam_ifc, TEGRA30_DAM_CHIN1, 1,
+ bb_info->bitsize, 1, 32);
+ tegra30_dam_set_acif(codec_i2s->dam_ifc, TEGRA30_DAM_CHOUT,
+ 2, codec_info->bitsize, 1, 32);
+ tegra30_dam_ch0_set_datasync(codec_i2s->dam_ifc, 2);
+ tegra30_dam_ch1_set_datasync(codec_i2s->dam_ifc, 0);
+
+ /* do routing in ahub */
+ tegra30_ahub_set_rx_cif_source(
+ TEGRA30_AHUB_RXCIF_DAM0_RX1 + (codec_i2s->dam_ifc*2),
+ TEGRA30_AHUB_RXCIF_I2S0_RX0 + bb_info->i2s_id);
+
+ tegra30_ahub_set_rx_cif_source(
+ TEGRA30_AHUB_RXCIF_DAM0_RX0 + (codec_i2s->dam_ifc*2),
+ codec_i2s->txcif);
+
+ tegra30_ahub_set_rx_cif_source(
+ TEGRA30_AHUB_RXCIF_I2S0_RX0 + codec_info->i2s_id,
+ TEGRA30_AHUB_TXCIF_DAM0_TX0 + codec_i2s->dam_ifc);
+
+ tegra30_ahub_set_rx_cif_source(
+ TEGRA30_AHUB_RXCIF_I2S0_RX0 + bb_info->i2s_id,
+ TEGRA30_AHUB_TXCIF_I2S0_TX0 + codec_info->i2s_id);
+
+ /* enable the dam*/
+ tegra30_dam_enable(codec_i2s->dam_ifc, TEGRA30_DAM_ENABLE,
+ TEGRA30_DAM_CHIN0_SRC);
+
+ tegra30_dam_enable(codec_i2s->dam_ifc, TEGRA30_DAM_ENABLE,
+ TEGRA30_DAM_CHIN1);
+
+ }
+#ifndef CONFIG_ARCH_TEGRA_3x_SOC
+ tegra30_i2s_write(codec_i2s, TEGRA30_I2S_FLOWCTL, 0);
+ tegra30_i2s_write(bb_i2s, TEGRA30_I2S_FLOWCTL, 0);
+ tegra30_i2s_write(codec_i2s, TEGRA30_I2S_TX_STEP, 0);
+ tegra30_i2s_write(bb_i2s, TEGRA30_I2S_TX_STEP, 0);
+ bb_i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_TX_FLOWCTL_EN;
+ codec_i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_TX_FLOWCTL_EN;
+
+ if (!bb_info->is_i2smaster && codec_info->is_i2smaster) {
+ tegra30_i2s_write(codec_i2s, TEGRA30_I2S_FLOWCTL,
+ TEGRA30_I2S_FLOWCTL_FILTER_QUAD |
+ 4 << TEGRA30_I2S_FLOWCTL_START_SHIFT |
+ 4 << TEGRA30_I2S_FLOWCTL_HIGH_SHIFT |
+ 4 << TEGRA30_I2S_FLOWCTL_LOW_SHIFT);
+ tegra30_i2s_write(bb_i2s, TEGRA30_I2S_FLOWCTL,
+ TEGRA30_I2S_FLOWCTL_FILTER_QUAD |
+ 4 << TEGRA30_I2S_FLOWCTL_START_SHIFT |
+ 4 << TEGRA30_I2S_FLOWCTL_HIGH_SHIFT |
+ 4 << TEGRA30_I2S_FLOWCTL_LOW_SHIFT);
+ tegra30_i2s_write(codec_i2s, TEGRA30_I2S_TX_STEP, 4);
+ tegra30_i2s_write(bb_i2s, TEGRA30_I2S_TX_STEP, 4);
+ codec_i2s->reg_ctrl |= TEGRA30_I2S_CTRL_TX_FLOWCTL_EN;
+ bb_i2s->reg_ctrl |= TEGRA30_I2S_CTRL_TX_FLOWCTL_EN;
+
+ val = tegra30_i2s_read(codec_i2s, TEGRA30_I2S_CIF_RX_CTRL);
+ val &= ~(0xf << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT);
+ val |= (4 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT);
+ tegra30_i2s_write(codec_i2s, TEGRA30_I2S_CIF_RX_CTRL, val);
+ val = tegra30_i2s_read(bb_i2s, TEGRA30_I2S_CIF_RX_CTRL);
+ val &= ~(0xf << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT);
+ val |= (4 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT);
+ tegra30_i2s_write(bb_i2s, TEGRA30_I2S_CIF_RX_CTRL, val);
+ }
+#endif
+
+ msleep(20);
+
+ codec_i2s->reg_ctrl |= TEGRA30_I2S_CTRL_XFER_EN_TX;
+ codec_i2s->reg_ctrl |= TEGRA30_I2S_CTRL_XFER_EN_RX;
+ tegra30_i2s_write(codec_i2s, TEGRA30_I2S_CTRL,
+ codec_i2s->reg_ctrl);
+
+ msleep(20);
+
+ bb_i2s->reg_ctrl |= TEGRA30_I2S_CTRL_XFER_EN_TX;
+ bb_i2s->reg_ctrl |= TEGRA30_I2S_CTRL_XFER_EN_RX;
+ tegra30_i2s_write(bb_i2s, TEGRA30_I2S_CTRL,
+ bb_i2s->reg_ctrl);
+
+ return 0;
+}
+
+int tegra30_break_voice_call_connections(struct codec_config *codec_info,
+ struct codec_config *bb_info,
+ int uses_voice_codec)
+{
+ 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];
+
+ /*Disable Codec I2S RX (TX to ahub)*/
+ if (codec_i2s->capture_ref_count == 1)
+ codec_i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_RX;
+
+ tegra30_i2s_write(codec_i2s, TEGRA30_I2S_CTRL, codec_i2s->reg_ctrl);
+
+ while (!tegra30_ahub_rx_fifo_is_empty(codec_i2s->id) && dcnt--)
+ udelay(100);
+
+ dcnt = 10;
+
+ /*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_empty(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);
+
+ while (!tegra30_ahub_rx_fifo_is_empty(bb_i2s->id) && dcnt--)
+ udelay(100);
+
+ dcnt = 10;
+
+ /*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_empty(codec_i2s->id) && dcnt--)
+ udelay(100);
+
+ dcnt = 10;
+
+ if (uses_voice_codec) {
+ tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_I2S0_RX0 +
+ bb_info->i2s_id);
+ tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_I2S0_RX0 +
+ codec_info->i2s_id);
+ } else {
+
+ /* Disable DAM in DL path */
+ tegra30_dam_enable(codec_i2s->dam_ifc,
+ TEGRA30_DAM_DISABLE, TEGRA30_DAM_CHIN0_SRC);
+ tegra30_dam_free_channel(codec_i2s->dam_ifc,
+ TEGRA30_DAM_CHIN0_SRC);
+
+ tegra30_dam_enable(codec_i2s->dam_ifc,
+ TEGRA30_DAM_DISABLE, TEGRA30_DAM_CHIN1);
+ tegra30_dam_free_channel(codec_i2s->dam_ifc,
+ TEGRA30_DAM_CHIN1);
+
+ codec_i2s->dam_ch_refcount--;
+ if (!codec_i2s->dam_ch_refcount)
+ tegra30_dam_free_controller(codec_i2s->dam_ifc);
+
+ /* Disconnect the ahub connections */
+ 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_RX1 +
+ (codec_i2s->dam_ifc*2));
+
+ 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_I2S0_RX0 +
+ codec_info->i2s_id);
+
+ tegra30_dam_ch0_set_datasync(codec_i2s->dam_ifc, 0);
+ tegra30_dam_ch1_set_datasync(codec_i2s->dam_ifc, 0);
+
+ tegra30_dam_disable_clock(codec_i2s->dam_ifc);
+ }
+
+ /* Decrement the codec and bb i2s playback ref count */
+ codec_i2s->playback_ref_count--;
+ bb_i2s->playback_ref_count--;
+ codec_i2s->capture_ref_count--;
+ bb_i2s->capture_ref_count--;
+
+ /* Soft reset */
+ tegra30_i2s_write(codec_i2s, TEGRA30_I2S_CTRL,
+ codec_i2s->reg_ctrl | TEGRA30_I2S_CTRL_SOFT_RESET);
+ tegra30_i2s_write(bb_i2s, TEGRA30_I2S_CTRL,
+ bb_i2s->reg_ctrl | TEGRA30_I2S_CTRL_SOFT_RESET);
+
+ codec_i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_TX_FLOWCTL_EN;
+ bb_i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_TX_FLOWCTL_EN;
+ codec_i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_SOFT_RESET;
+ bb_i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_SOFT_RESET;
+
+ while ((tegra30_i2s_read(codec_i2s, TEGRA30_I2S_CTRL) &
+ TEGRA30_I2S_CTRL_SOFT_RESET) && dcnt--)
+ udelay(100);
+ dcnt = 10;
+ while ((tegra30_i2s_read(bb_i2s, TEGRA30_I2S_CTRL) &
+ TEGRA30_I2S_CTRL_SOFT_RESET) && dcnt--)
+ udelay(100);
+
+ /* Disable the clocks */
+ tegra30_i2s_disable_clocks(codec_i2s);
+ tegra30_i2s_disable_clocks(bb_i2s);
+
+ return 0;
+}
+
static __devinit int tegra30_i2s_platform_probe(struct platform_device *pdev)
{
struct tegra30_i2s *i2s;
diff --git a/sound/soc/tegra/tegra30_i2s.h b/sound/soc/tegra/tegra30_i2s.h
index d6c71aec9a94..6bc319ae8a40 100644
--- a/sound/soc/tegra/tegra30_i2s.h
+++ b/sound/soc/tegra/tegra30_i2s.h
@@ -1,8 +1,8 @@
/*
* tegra30_i2s.h - Definitions for Tegra 30 I2S driver
*
- * Copyright (c) 2010-2013, NVIDIA Corporation.
- * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2010-2012, NVIDIA Corporation.
+ * Copyright (c) 2012-2014, 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
* version 2 as published by the Free Software Foundation.
@@ -312,4 +312,11 @@ int tegra30_break_voice_call_connections(struct codec_config *codec_info,
struct codec_config *bb_info,
int uses_voice_codec);
+int tegra30_make_bt_voice_call_connections(struct codec_config *codec_info,
+ struct codec_config *bb_info,
+ int uses_voice_codec);
+int tegra30_break_bt_voice_call_connections(struct codec_config *codec_info,
+ struct codec_config *bb_info,
+ int uses_voice_codec);
+
#endif
diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c
index a44deeb5cdfd..8d50991c2133 100644
--- a/sound/soc/tegra/tegra_rt5640.c
+++ b/sound/soc/tegra/tegra_rt5640.c
@@ -154,13 +154,28 @@ static int tegra_call_mode_put(struct snd_kcontrol *kcontrol,
for (i = 0; i < machine->pcard->num_links; i++)
machine->pcard->dai_link[i].ignore_suspend = 1;
- tegra30_make_voice_call_connections(
- &machine->codec_info[codec_index],
- &machine->codec_info[BASEBAND], uses_voice_codec);
+ if (machine->is_device_bt)
+ tegra30_make_bt_voice_call_connections(
+ &machine->codec_info[codec_index],
+ &machine->codec_info[BASEBAND],
+ uses_voice_codec);
+ else
+ tegra30_make_voice_call_connections(
+ &machine->codec_info[codec_index],
+ &machine->codec_info[BASEBAND],
+ uses_voice_codec);
+
} else {
- tegra30_break_voice_call_connections(
- &machine->codec_info[codec_index],
- &machine->codec_info[BASEBAND], uses_voice_codec);
+ if (machine->is_device_bt)
+ tegra30_break_bt_voice_call_connections(
+ &machine->codec_info[codec_index],
+ &machine->codec_info[BASEBAND],
+ uses_voice_codec);
+ else
+ tegra30_break_voice_call_connections(
+ &machine->codec_info[codec_index],
+ &machine->codec_info[BASEBAND],
+ uses_voice_codec);
for (i = 0; i < machine->pcard->num_links; i++)
machine->pcard->dai_link[i].ignore_suspend = 0;
@@ -183,7 +198,34 @@ struct snd_kcontrol_new tegra_rt5640_call_mode_control = {
};
#ifndef CONFIG_ARCH_TEGRA_2x_SOC
-static int tegra_rt5640_set_dam_cif(int dam_ifc, int srate,
+static int tegra_rt5640_set_dam_cif(int dam_ifc,
+ int out_rate, int out_channels, int out_bit_size,
+ int ch0_rate, int ch0_channels, int ch0_bit_size)
+{
+ tegra30_dam_set_samplerate(dam_ifc, TEGRA30_DAM_CHOUT, out_rate);
+ tegra30_dam_set_samplerate(dam_ifc, TEGRA30_DAM_CHIN0_SRC,
+ ch0_rate);
+ tegra30_dam_set_gain(dam_ifc, TEGRA30_DAM_CHIN0_SRC, 0x1000);
+
+ tegra30_dam_set_acif(dam_ifc, TEGRA30_DAM_CHIN0_SRC,
+ ch0_channels, ch0_bit_size, 1, 32);
+ tegra30_dam_set_acif(dam_ifc, TEGRA30_DAM_CHOUT,
+ out_channels, out_bit_size, out_channels, 32);
+
+ if (ch0_rate != out_rate) {
+ tegra30_dam_write_coeff_ram(dam_ifc, ch0_rate, out_rate);
+ tegra30_dam_set_farrow_param(dam_ifc, ch0_rate, out_rate);
+ tegra30_dam_set_biquad_fixed_coef(dam_ifc);
+ tegra30_dam_enable_coeff_ram(dam_ifc);
+ tegra30_dam_set_filter_stages(dam_ifc, ch0_rate, out_rate);
+ } else {
+ tegra30_dam_enable_stereo_mixing(dam_ifc);
+ }
+
+ return 0;
+}
+
+static int tegra_bt_set_dam_cif(int dam_ifc, int srate,
int channels, int bit_size, int src_on, int src_srate,
int src_channels, int src_bit_size)
{
@@ -331,8 +373,9 @@ static int tegra_rt5640_hw_params(struct snd_pcm_substream *substream,
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && i2s->is_dam_used)
- tegra_rt5640_set_dam_cif(i2s->dam_ifc, srate,
- params_channels(params), sample_size, 0, 0, 0, 0);
+ tegra_rt5640_set_dam_cif(i2s->dam_ifc,
+ srate, params_channels(params), sample_size,
+ srate, params_channels(params), sample_size);
if(machine_is_roth()) {
if(initTfa == 1) {
@@ -503,18 +546,27 @@ static int tegra_rt5640_startup(struct snd_pcm_substream *substream)
i2s->dam_ch_refcount++;
tegra30_dam_enable_clock(i2s->dam_ifc);
- tegra30_ahub_set_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX1 +
+ if (machine->is_call_mode) {
+ tegra30_ahub_set_rx_cif_source(
+ TEGRA30_AHUB_RXCIF_DAM0_RX0 +
(i2s->dam_ifc*2), i2s->txcif);
- /*
- *make the dam tx to i2s rx connection if this is the only client
- *using i2s for playback
- */
- if (i2s->playback_ref_count == 1)
+ /*
+ * make the dam tx to i2s rx connection
+ * if this is the only client
+ * using i2s for playback
+ */
+ if (i2s->playback_ref_count == 1)
+ tegra30_ahub_set_rx_cif_source(
+ TEGRA30_AHUB_RXCIF_I2S0_RX0 +
+ i2s->id,
+ TEGRA30_AHUB_TXCIF_DAM0_TX0 +
+ i2s->dam_ifc);
+ } else {
tegra30_ahub_set_rx_cif_source(
TEGRA30_AHUB_RXCIF_I2S0_RX0 + i2s->id,
- TEGRA30_AHUB_TXCIF_DAM0_TX0 + i2s->dam_ifc);
-
+ i2s->txcif);
+ }
/* enable the dam*/
tegra30_dam_enable(i2s->dam_ifc, TEGRA30_DAM_ENABLE,
TEGRA30_DAM_CHIN1);
@@ -549,7 +601,7 @@ static int tegra_rt5640_startup(struct snd_pcm_substream *substream)
/* configure the dam */
tegra_rt5640_set_dam_cif(i2s->call_record_dam_ifc,
codec_info->rate, codec_info->channels,
- codec_info->bitsize, 1, bb_info->rate,
+ codec_info->bitsize, bb_info->rate,
bb_info->channels, bb_info->bitsize);
/* setup the connections for voice call record */
@@ -589,7 +641,166 @@ static void tegra_rt5640_shutdown(struct snd_pcm_substream *substream)
TEGRA30_DAM_CHIN1);
/* disconnect the ahub connections*/
+ tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX0 +
+ (i2s->dam_ifc*2));
+
+ /* disable the dam and free the controller */
+ tegra30_dam_disable_clock(i2s->dam_ifc);
+ tegra30_dam_free_channel(i2s->dam_ifc, TEGRA30_DAM_CHIN1);
+ i2s->dam_ch_refcount--;
+ if (!i2s->dam_ch_refcount)
+ tegra30_dam_free_controller(i2s->dam_ifc);
+ } else {
+ if (!i2s->is_call_mode_rec)
+ return;
+
+ i2s->is_call_mode_rec = 0;
+
+ /* disable the dam*/
+ tegra30_dam_enable(i2s->call_record_dam_ifc,
+ TEGRA30_DAM_DISABLE, TEGRA30_DAM_CHIN1);
+ tegra30_dam_enable(i2s->call_record_dam_ifc,
+ TEGRA30_DAM_DISABLE, TEGRA30_DAM_CHIN0_SRC);
+
+ /* disconnect the ahub connections*/
+ tegra30_ahub_unset_rx_cif_source(i2s->rxcif);
+ tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX0 +
+ (i2s->call_record_dam_ifc*2));
tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX1 +
+ (i2s->call_record_dam_ifc*2));
+
+ /* free the dam channels and dam controller */
+ tegra30_dam_disable_clock(i2s->call_record_dam_ifc);
+ tegra30_dam_free_channel(i2s->call_record_dam_ifc,
+ TEGRA30_DAM_CHIN1);
+ tegra30_dam_free_channel(i2s->call_record_dam_ifc,
+ TEGRA30_DAM_CHIN0_SRC);
+ tegra30_dam_free_controller(i2s->call_record_dam_ifc);
+ }
+
+ return;
+}
+
+static int tegra_bt_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+ struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(rtd->card);
+ struct codec_config *codec_info;
+ struct codec_config *bb_info;
+ struct codec_config *hifi_info;
+ int codec_index;
+
+ if (!i2s->is_dam_used)
+ return 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /*dam configuration*/
+ if (!i2s->dam_ch_refcount)
+ i2s->dam_ifc = tegra30_dam_allocate_controller();
+ if (i2s->dam_ifc < 0)
+ return i2s->dam_ifc;
+ tegra30_dam_allocate_channel(i2s->dam_ifc, TEGRA30_DAM_CHIN1);
+ i2s->dam_ch_refcount++;
+ tegra30_dam_enable_clock(i2s->dam_ifc);
+
+ if (machine->is_call_mode) {
+ tegra30_ahub_set_rx_cif_source(
+ TEGRA30_AHUB_RXCIF_DAM0_RX0 +
+ (i2s->dam_ifc*2), i2s->txcif);
+
+ /*
+ * make the dam tx to i2s rx connection
+ * if this is the only client
+ * using i2s for playback
+ */
+ if (i2s->playback_ref_count == 1)
+ tegra30_ahub_set_rx_cif_source(
+ TEGRA30_AHUB_RXCIF_I2S0_RX0 +
+ i2s->id,
+ TEGRA30_AHUB_TXCIF_DAM0_TX0 +
+ i2s->dam_ifc);
+ } else {
+ tegra30_ahub_set_rx_cif_source(
+ TEGRA30_AHUB_RXCIF_I2S0_RX0 + i2s->id,
+ i2s->txcif);
+ }
+ /* enable the dam*/
+ tegra30_dam_enable(i2s->dam_ifc, TEGRA30_DAM_ENABLE,
+ TEGRA30_DAM_CHIN1);
+ } else {
+ i2s->is_call_mode_rec = machine->is_call_mode;
+
+ if (!i2s->is_call_mode_rec)
+ return 0;
+
+ if (machine->is_device_bt)
+ codec_index = BT_SCO;
+ else
+ codec_index = VOICE_CODEC;
+
+ codec_info = &machine->codec_info[codec_index];
+ bb_info = &machine->codec_info[BASEBAND];
+ hifi_info = &machine->codec_info[HIFI_CODEC];
+
+ /* allocate a dam for voice call recording */
+
+ i2s->call_record_dam_ifc = tegra30_dam_allocate_controller();
+
+ if (i2s->call_record_dam_ifc < 0)
+ return i2s->call_record_dam_ifc;
+
+ tegra30_dam_allocate_channel(i2s->call_record_dam_ifc,
+ TEGRA30_DAM_CHIN0_SRC);
+ tegra30_dam_allocate_channel(i2s->call_record_dam_ifc,
+ TEGRA30_DAM_CHIN1);
+ tegra30_dam_enable_clock(i2s->call_record_dam_ifc);
+
+ /* configure the dam */
+ tegra_bt_set_dam_cif(i2s->call_record_dam_ifc,
+ codec_info->rate, codec_info->channels,
+ codec_info->bitsize, 1, bb_info->rate,
+ bb_info->channels, bb_info->bitsize);
+
+ /* setup the connections for voice call record */
+ tegra30_ahub_unset_rx_cif_source(i2s->rxcif);
+ tegra30_ahub_set_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX0 +
+ (i2s->call_record_dam_ifc*2),
+ TEGRA30_AHUB_TXCIF_I2S0_TX0 + bb_info->i2s_id);
+ tegra30_ahub_set_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX1 +
+ (i2s->call_record_dam_ifc*2),
+ TEGRA30_AHUB_TXCIF_I2S0_TX0 + codec_info->i2s_id);
+ tegra30_ahub_set_rx_cif_source(i2s->rxcif,
+ TEGRA30_AHUB_TXCIF_DAM0_TX0 + i2s->call_record_dam_ifc);
+
+ /* enable the dam*/
+
+ tegra30_dam_enable(i2s->call_record_dam_ifc, TEGRA30_DAM_ENABLE,
+ TEGRA30_DAM_CHIN1);
+ tegra30_dam_enable(i2s->call_record_dam_ifc, TEGRA30_DAM_ENABLE,
+ TEGRA30_DAM_CHIN0_SRC);
+ }
+
+ return 0;
+}
+
+static void tegra_bt_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+
+ if (!i2s->is_dam_used)
+ return;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* disable the dam*/
+ tegra30_dam_enable(i2s->dam_ifc, TEGRA30_DAM_DISABLE,
+ TEGRA30_DAM_CHIN1);
+
+ /* disconnect the ahub connections*/
+ tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX0 +
(i2s->dam_ifc*2));
/* disable the dam and free the controller */
@@ -628,6 +839,7 @@ static void tegra_rt5640_shutdown(struct snd_pcm_substream *substream)
return;
}
+
#endif
static int tegra_voice_call_hw_params(struct snd_pcm_substream *substream,
@@ -824,8 +1036,8 @@ static struct snd_soc_ops tegra_rt5640_bt_sco_ops = {
.hw_params = tegra_bt_sco_hw_params,
.hw_free = tegra_hw_free,
#ifndef CONFIG_ARCH_TEGRA_2x_SOC
- .startup = tegra_rt5640_startup,
- .shutdown = tegra_rt5640_shutdown,
+ .startup = tegra_bt_startup,
+ .shutdown = tegra_bt_shutdown,
#endif
};