summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorNikesh Oswal <noswal@nvidia.com>2011-11-08 14:08:31 +0530
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:50:08 -0800
commit64f161ec92b9715c08ca58a36a3e772ba9e412b5 (patch)
tree69541b9a9ff0e63935786e61b4ca308ffadcea4c /sound
parent5c68d67b98772244fdedb056e85c1a9cdf381325 (diff)
ASoC: tegra: t30 add support for voice call
Bug: 862023 Change-Id: If22a815f3c941e07b4be91c485353087e45b7503 Signed-off-by: Nikesh Oswal <noswal@nvidia.com> Reviewed-on: http://git-master/r/62894 Reviewed-by: Rohan Somvanshi <rsomvanshi@nvidia.com> Rebase-Id: R3cceb258be9da3e8bb45741705aabd65c6065c3c
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/tegra/tegra30_i2s.c240
-rw-r--r--sound/soc/tegra/tegra30_i2s.h15
2 files changed, 255 insertions, 0 deletions
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
index 3e002b69dbb3..78e3ee042b5c 100644
--- a/sound/soc/tegra/tegra30_i2s.c
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -487,6 +487,246 @@ struct snd_soc_dai_driver tegra30_i2s_dai[] = {
TEGRA30_I2S_DAI(4),
};
+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;
+
+ i2s->reg_ctrl &= ~(TEGRA30_I2S_CTRL_FRAME_FORMAT_MASK |
+ TEGRA30_I2S_CTRL_LRCK_MASK |
+ TEGRA30_I2S_CTRL_MASTER_ENABLE);
+ i2s->reg_ch_ctrl &= ~TEGRA30_I2S_CH_CTRL_EGDE_CTRL_MASK;
+
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_BIT_SIZE_16;
+
+ if (is_i2smaster)
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_MASTER_ENABLE;
+
+ if (is_formatdsp) {
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC;
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_R_LOW;
+ i2s->reg_ch_ctrl |= TEGRA30_I2S_CH_CTRL_EGDE_CTRL_NEG_EDGE;
+ } else {
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK;
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_L_LOW;
+ i2s->reg_ch_ctrl |= TEGRA30_I2S_CH_CTRL_EGDE_CTRL_POS_EDGE;
+ }
+
+ tegra30_i2s_write(i2s, TEGRA30_I2S_CH_CTRL, i2s->reg_ch_ctrl);
+
+ val = tegra30_i2s_read(i2s, TEGRA30_I2S_SLOT_CTRL);
+ val &= ~(TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_MASK |
+ TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_MASK);
+ val |= (1 << TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_SHIFT |
+ 1 << TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT);
+ tegra30_i2s_write(i2s, TEGRA30_I2S_SLOT_CTRL, val);
+
+ val = (1 << TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT) |
+ (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;
+ if (i2sclock % (rate))
+ val |= TEGRA30_I2S_TIMING_NON_SYM_ENABLE;
+ } else {
+ bitcnt = (i2sclock/(2*rate)) - 1;
+ val = bitcnt << TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
+ if (i2sclock % (2*rate))
+ val |= TEGRA30_I2S_TIMING_NON_SYM_ENABLE;
+ }
+
+ tegra30_i2s_write(i2s, TEGRA30_I2S_TIMING, val);
+
+ /* configure the i2s cif*/
+ val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) |
+ ((channels - 1) << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
+ ((channels - 1) << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) |
+ TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 |
+ TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16;
+ val |= TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX;
+ tegra30_i2s_write(i2s, TEGRA30_I2S_CIF_RX_CTRL, val);
+
+ val &= ~TEGRA30_AUDIOCIF_CTRL_DIRECTION_MASK;
+ val |= TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX;
+ tegra30_i2s_write(i2s, TEGRA30_I2S_CIF_TX_CTRL, val);
+
+ return 0;
+}
+
+static int configure_dam(struct tegra30_i2s *i2s, int out_channel,
+ int out_rate, int out_bitsize, int in_channels,
+ int in_rate, int in_bitsize)
+{
+
+ if (!i2s->dam_ch_refcount)
+ i2s->dam_ifc = tegra30_dam_allocate_controller();
+
+ tegra30_dam_allocate_channel(i2s->dam_ifc, TEGRA30_DAM_CHIN0_SRC);
+ i2s->dam_ch_refcount++;
+ tegra30_dam_enable_clock(i2s->dam_ifc);
+ tegra30_dam_set_samplerate(i2s->dam_ifc, TEGRA30_DAM_CHOUT, out_rate);
+ tegra30_dam_set_samplerate(i2s->dam_ifc, TEGRA30_DAM_CHIN0_SRC,
+ in_rate);
+ tegra30_dam_set_gain(i2s->dam_ifc, TEGRA30_DAM_CHIN0_SRC, 0x1000);
+ tegra30_dam_set_acif(i2s->dam_ifc, TEGRA30_DAM_CHIN0_SRC,
+ in_channels, in_bitsize, 1, 16);
+ tegra30_dam_set_acif(i2s->dam_ifc, TEGRA30_DAM_CHOUT,
+ out_channel, out_bitsize, out_channel, out_bitsize);
+
+ return 0;
+}
+
+
+int tegra30_make_voice_call_connections(struct codec_config *codec_info,
+ struct codec_config *bb_info)
+{
+ struct tegra30_i2s *codec_i2s;
+ struct tegra30_i2s *bb_i2s;
+
+ 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++;
+ bb_i2s->playback_ref_count++;
+
+ /*Configure codec i2s*/
+ configure_baseband_i2s(codec_i2s, codec_info->is_i2smaster,
+ codec_info->is_format_dsp, codec_info->channels,
+ codec_info->rate, codec_info->bitsize);
+
+ /*Configure bb i2s*/
+ configure_baseband_i2s(bb_i2s, bb_info->is_i2smaster,
+ bb_info->is_format_dsp, bb_info->channels,
+ bb_info->rate, bb_info->bitsize);
+
+ /*configure codec dam*/
+ configure_dam(codec_i2s, codec_info->channels,
+ codec_info->rate, codec_info->bitsize, bb_info->channels,
+ bb_info->rate, bb_info->bitsize);
+
+ /*configure bb dam*/
+ configure_dam(bb_i2s, bb_info->channels,
+ bb_info->rate, bb_info->bitsize, codec_info->channels,
+ codec_info->rate, codec_info->bitsize);
+
+ /*make ahub connections*/
+
+ /* if this is the only user of i2s tx then make ahub i2s rx connection*/
+ if (codec_i2s->playback_ref_count == 1) {
+ 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_DAM0_TX0 +
+ bb_i2s->dam_ifc);
+ tegra30_ahub_set_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX0 +
+ (codec_i2s->dam_ifc*2), TEGRA30_AHUB_TXCIF_I2S0_TX0 +
+ bb_info->i2s_id);
+ tegra30_ahub_set_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX0 +
+ (bb_i2s->dam_ifc*2), TEGRA30_AHUB_TXCIF_I2S0_TX0 +
+ codec_info->i2s_id);
+
+ /*enable dam and i2s*/
+ tegra30_dam_enable(codec_i2s->dam_ifc, TEGRA30_DAM_ENABLE,
+ TEGRA30_DAM_CHIN0_SRC);
+ tegra30_dam_enable(bb_i2s->dam_ifc, TEGRA30_DAM_ENABLE,
+ TEGRA30_DAM_CHIN0_SRC);
+
+ /* if this is the only user of i2s tx then enable it*/
+ if (codec_i2s->playback_ref_count == 1)
+ 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);
+ 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)
+{
+ struct tegra30_i2s *codec_i2s;
+ struct tegra30_i2s *bb_i2s;
+
+ 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);
+
+ 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));
+
+ /* disable the i2s */
+
+ /* 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;
+
+ codec_i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_RX;
+ tegra30_i2s_write(codec_i2s, TEGRA30_I2S_CTRL, codec_i2s->reg_ctrl);
+ 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);
+ 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--;
+
+ /* disable the 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);
+ 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;
+}
+
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 16af6bc75491..6dd09eb63bfb 100644
--- a/sound/soc/tegra/tegra30_i2s.h
+++ b/sound/soc/tegra/tegra30_i2s.h
@@ -248,4 +248,19 @@ struct tegra30_i2s {
bool is_dam_used;
};
+struct codec_config {
+ int i2s_id;
+ int rate;
+ int channels;
+ int bitsize;
+ int is_i2smaster;
+ int is_format_dsp;
+};
+
+int tegra30_make_voice_call_connections(struct codec_config *codec_info,
+ struct codec_config *bb_info);
+
+int tegra30_break_voice_call_connections(struct codec_config *codec_info,
+ struct codec_config *bb_info);
+
#endif