summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sound/soc/tegra/tegra30_ahub.c172
-rw-r--r--sound/soc/tegra/tegra30_ahub.h16
-rw-r--r--sound/soc/tegra/tegra30_i2s.c238
-rw-r--r--sound/soc/tegra/tegra30_i2s.h14
4 files changed, 431 insertions, 9 deletions
diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c
index 710d9465b4b0..ea772f40464b 100644
--- a/sound/soc/tegra/tegra30_ahub.c
+++ b/sound/soc/tegra/tegra30_ahub.c
@@ -116,6 +116,15 @@ void tegra30_ahub_disable_clocks(void)
clk_disable(ahub->clk_d_audio);
}
+/*
+ * for TDM mode, ahub has to run faster than I2S controller. This will avoid
+ * FIFO overflow/underflow, the causes of slot-hopping symptoms
+ */
+void tegra30_ahub_clock_set_rate(int rate)
+{
+ clk_set_rate(ahub->clk_d_audio, rate);
+}
+
#ifdef CONFIG_DEBUG_FS
static inline u32 tegra30_ahub_read(u32 space, u32 reg)
{
@@ -272,6 +281,116 @@ int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif,
return 0;
}
+int tegra30_ahub_rx_fifo_is_busy(enum tegra30_ahub_rxcif rxcif)
+{
+ int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
+ int reg, val;
+
+ reg = TEGRA30_AHUB_CHANNEL_STATUS +
+ (channel * TEGRA30_AHUB_CHANNEL_STATUS_STRIDE);
+
+ val = tegra30_apbif_read(reg);
+ val &= TEGRA30_AHUB_CHANNEL_STATUS_RX_TRIG;
+
+ return val;
+}
+
+int tegra30_ahub_tx_fifo_is_busy(enum tegra30_ahub_txcif txcif)
+{
+ int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0;
+ int reg, val;
+
+ reg = TEGRA30_AHUB_CHANNEL_STATUS +
+ (channel * TEGRA30_AHUB_CHANNEL_STATUS_STRIDE);
+
+ val = tegra30_apbif_read(reg);
+ val &= TEGRA30_AHUB_CHANNEL_STATUS_TX_TRIG;
+
+ return val;
+}
+
+int tegra30_ahub_rx_fifo_clear(enum tegra30_ahub_rxcif rxcif)
+{
+ int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
+ int reg, val;
+
+ reg = TEGRA30_AHUB_CHANNEL_CLEAR +
+ (channel * TEGRA30_AHUB_CHANNEL_CLEAR_STRIDE);
+
+ val = tegra30_apbif_read(reg);
+ val |= TEGRA30_AHUB_CHANNEL_CLEAR_RX_SOFT_RESET;
+ tegra30_apbif_write(reg, val);
+
+ tegra30_ahub_disable_clocks();
+
+ return 0;
+}
+
+int tegra30_ahub_tx_fifo_clear(enum tegra30_ahub_txcif txcif)
+{
+ int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0;
+ int reg, val;
+
+ reg = TEGRA30_AHUB_CHANNEL_CLEAR +
+ (channel * TEGRA30_AHUB_CHANNEL_CLEAR_STRIDE);
+
+ val = tegra30_apbif_read(reg);
+ val |= TEGRA30_AHUB_CHANNEL_CLEAR_TX_SOFT_RESET;
+ tegra30_apbif_write(reg, val);
+
+ tegra30_ahub_disable_clocks();
+
+ return 0;
+}
+
+int tegra30_ahub_set_rx_fifo_pack_mode(enum tegra30_ahub_rxcif rxcif,
+ unsigned int pack_mode)
+{
+ int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
+ int reg, val;
+
+ tegra30_ahub_enable_clocks();
+ reg = TEGRA30_AHUB_CHANNEL_CTRL +
+ (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
+ val = tegra30_apbif_read(reg);
+
+ val &= ~TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK;
+ val &= ~TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_EN;
+
+ if ((pack_mode == TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_16) ||
+ (pack_mode == TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_8_4))
+ val |= (TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_EN |
+ pack_mode);
+ tegra30_apbif_write(reg, val);
+ tegra30_ahub_disable_clocks();
+
+ return 0;
+}
+
+int tegra30_ahub_set_tx_fifo_pack_mode(enum tegra30_ahub_txcif txcif,
+ unsigned int pack_mode)
+{
+ int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0;
+ int reg, val;
+
+ tegra30_ahub_enable_clocks();
+ reg = TEGRA30_AHUB_CHANNEL_CTRL +
+ (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
+ val = tegra30_apbif_read(reg);
+
+ val &= ~TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK;
+ val &= ~TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_EN;
+
+ if ((pack_mode == TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_16) ||
+ (pack_mode == TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_8_4))
+ val |= (TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_EN |
+ pack_mode);
+ tegra30_apbif_write(reg, val);
+ tegra30_ahub_disable_clocks();
+
+ return 0;
+}
+
int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif)
{
int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
@@ -299,8 +418,6 @@ int tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif)
val &= ~TEGRA30_AHUB_CHANNEL_CTRL_RX_EN;
tegra30_apbif_write(reg, val);
- tegra30_ahub_disable_clocks();
-
return 0;
}
@@ -386,8 +503,6 @@ int tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif)
val &= ~TEGRA30_AHUB_CHANNEL_CTRL_TX_EN;
tegra30_apbif_write(reg, val);
- tegra30_ahub_disable_clocks();
-
return 0;
}
@@ -480,6 +595,54 @@ int tegra30_ahub_set_tx_cif_channels(enum tegra30_ahub_txcif txcif,
return 0;
}
+int tegra30_ahub_set_rx_cif_bits(enum tegra30_ahub_rxcif rxcif,
+ unsigned int audio_bits,
+ unsigned int client_bits)
+{
+ int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
+ unsigned int reg, val;
+
+ tegra30_ahub_enable_clocks();
+
+ reg = TEGRA30_AHUB_CIF_RX_CTRL +
+ (channel * TEGRA30_AHUB_CIF_RX_CTRL_STRIDE);
+ val = tegra30_apbif_read(reg);
+ val &= ~(TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_MASK |
+ TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_MASK);
+ val |= ((audio_bits) << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) |
+ ((client_bits) << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT);
+ tegra30_apbif_write(reg, val);
+
+ tegra30_ahub_disable_clocks();
+
+ return 0;
+}
+
+int tegra30_ahub_set_tx_cif_bits(enum tegra30_ahub_txcif txcif,
+ unsigned int audio_bits,
+ unsigned int client_bits)
+{
+ int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0;
+ unsigned int reg, val;
+
+ tegra30_ahub_enable_clocks();
+
+ reg = TEGRA30_AHUB_CIF_TX_CTRL +
+ (channel * TEGRA30_AHUB_CIF_TX_CTRL_STRIDE);
+ val = tegra30_apbif_read(reg);
+ val &= ~(TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_MASK |
+ TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_MASK);
+ val |= ((audio_bits) << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) |
+ ((client_bits) << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT);
+
+ tegra30_apbif_write(reg, val);
+
+ tegra30_ahub_disable_clocks();
+
+ return 0;
+}
+
+
static int __devinit tegra30_ahub_probe(struct platform_device *pdev)
{
struct resource *res0, *res1, *region;
@@ -507,6 +670,7 @@ static int __devinit tegra30_ahub_probe(struct platform_device *pdev)
goto err_free;
}
clkm_rate = clk_get_rate(clk_get_parent(ahub->clk_d_audio));
+
while (clkm_rate > 12000000)
clkm_rate >>= 1;
diff --git a/sound/soc/tegra/tegra30_ahub.h b/sound/soc/tegra/tegra30_ahub.h
index 7de1b7c86c7f..2c0f5aab4db1 100644
--- a/sound/soc/tegra/tegra30_ahub.h
+++ b/sound/soc/tegra/tegra30_ahub.h
@@ -464,6 +464,7 @@ enum tegra30_ahub_rxcif {
extern void tegra30_ahub_enable_clocks(void);
extern void tegra30_ahub_disable_clocks(void);
+extern void tegra30_ahub_clock_set_rate(int rate);
extern int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif,
unsigned long *fiforeg,
@@ -471,8 +472,13 @@ extern int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif,
extern int tegra30_ahub_set_rx_cif_channels(enum tegra30_ahub_rxcif rxcif,
unsigned int audio_ch,
unsigned int client_ch);
+extern int tegra30_ahub_set_rx_cif_bits(enum tegra30_ahub_rxcif rxcif,
+ unsigned int audio_bits,
+ unsigned int client_bits);
extern int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif);
extern int tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif);
+extern int tegra30_ahub_set_rx_fifo_pack_mode(enum tegra30_ahub_rxcif rxcif,
+ unsigned int pack_mode);
extern int tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif);
extern int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif,
@@ -481,14 +487,24 @@ extern int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif,
extern int tegra30_ahub_set_tx_cif_channels(enum tegra30_ahub_txcif txcif,
unsigned int audio_ch,
unsigned int client_ch);
+extern int tegra30_ahub_set_tx_cif_bits(enum tegra30_ahub_txcif txcif,
+ unsigned int audio_bits,
+ unsigned int client_bits);
extern int tegra30_ahub_enable_tx_fifo(enum tegra30_ahub_txcif txcif);
extern int tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif);
+extern int tegra30_ahub_set_tx_fifo_pack_mode(enum tegra30_ahub_txcif txcif,
+ unsigned int pack_mode);
extern int tegra30_ahub_free_tx_fifo(enum tegra30_ahub_txcif txcif);
extern int tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif,
enum tegra30_ahub_txcif txcif);
extern int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif);
+extern int tegra30_ahub_rx_fifo_is_busy(enum tegra30_ahub_rxcif rxcif);
+extern int tegra30_ahub_tx_fifo_is_busy(enum tegra30_ahub_txcif txcif);
+extern int tegra30_ahub_rx_fifo_clear(enum tegra30_ahub_rxcif rxcif);
+extern int tegra30_ahub_tx_fifo_clear(enum tegra30_ahub_txcif txcif);
+
#ifdef CONFIG_PM
extern int tegra30_ahub_apbif_resume(void);
#endif
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
index 02d1038ea36e..c2750bfbef64 100644
--- a/sound/soc/tegra/tegra30_i2s.c
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -36,6 +36,7 @@
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/io.h>
+#include <linux/delay.h>
#include <mach/iomap.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -280,6 +281,194 @@ static int tegra30_i2s_set_fmt(struct snd_soc_dai *dai,
return 0;
}
+static void tegra30_i2s_set_channel_bit_count(struct tegra30_i2s *i2s,
+ int i2sclock, int srate)
+{
+ int sym_bitclk, bitcnt;
+ u32 val;
+
+ bitcnt = (i2sclock / (2 * srate)) - 1;
+ sym_bitclk = !(i2sclock % (2 * srate));
+
+ val = bitcnt << TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
+
+ if (!sym_bitclk)
+ val |= TEGRA30_I2S_TIMING_NON_SYM_ENABLE;
+
+ tegra30_i2s_write(i2s, TEGRA30_I2S_TIMING, val);
+}
+
+static void tegra30_i2s_set_data_offset(struct tegra30_i2s *i2s)
+{
+ u32 val;
+ int rx_data_offset = i2s->dsp_config.rx_data_offset;
+ int tx_data_offset = i2s->dsp_config.tx_data_offset;
+
+ val = (rx_data_offset <<
+ TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT) |
+ (tx_data_offset <<
+ TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT);
+
+ tegra30_i2s_write(i2s, TEGRA30_I2S_OFFSET, val);
+}
+
+static void tegra30_i2s_set_slot_control(struct tegra30_i2s *i2s, int stream)
+{
+ u32 val;
+ int tx_mask = i2s->dsp_config.tx_mask;
+ int rx_mask = i2s->dsp_config.rx_mask;
+
+ val = tegra30_i2s_read(i2s, TEGRA30_I2S_SLOT_CTRL);
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ val &= ~TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_MASK;
+ val |= (tx_mask << TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_SHIFT);
+ } else {
+ val &= ~TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_MASK;
+ val |= (rx_mask << TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT);
+ }
+
+ val &= ~TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK;
+ val |= (i2s->dsp_config.num_slots - 1)
+ << TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT;
+
+ tegra30_i2s_write(i2s, TEGRA30_I2S_SLOT_CTRL, val);
+}
+
+static int tegra30_i2s_tdm_setup_clocks(struct device *dev,
+ struct tegra30_i2s *i2s, int *i2sclock)
+{
+ int ret;
+
+ if (i2s->reg_ctrl & TEGRA30_I2S_CTRL_MASTER_ENABLE) {
+
+ ret = clk_set_parent(i2s->clk_i2s, i2s->clk_pll_a_out0);
+ if (ret) {
+ dev_err(dev, "Can't set parent of I2S clock\n");
+ return ret;
+ }
+ ret = clk_set_rate(i2s->clk_i2s, *i2sclock);
+ if (ret) {
+ dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
+ return ret;
+ }
+ } else {
+
+ ret = clk_set_rate(i2s->clk_i2s_sync, *i2sclock);
+ if (ret) {
+ dev_err(dev, "Can't set I2S sync clock rate\n");
+ return ret;
+ }
+
+ ret = clk_set_rate(i2s->clk_audio_2x, *i2sclock);
+ if (ret) {
+ dev_err(dev, "Can't set audio2x clock rate\n");
+ return ret;
+ }
+
+ ret = clk_set_parent(i2s->clk_i2s, i2s->clk_audio_2x);
+ if (ret) {
+ dev_err(dev, "Can't set parent of audio2x clock\n");
+ return ret;
+ }
+ }
+ return ret;
+}
+
+
+static int tegra30_i2s_tdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = substream->pcm->card->dev;
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ u32 val;
+ int i2s_client_ch, i2s_audio_ch, i2s_audio_bits, i2s_client_bits;
+ int i2sclock, srate;
+ int ret;
+
+ srate = params_rate(params);
+
+ i2sclock = srate *
+ i2s->dsp_config.num_slots *
+ i2s->dsp_config.slot_width;
+
+ ret = tegra30_i2s_tdm_setup_clocks(dev, i2s, &i2sclock);
+ if (ret)
+ return -EINVAL;
+
+ /* Run ahub clock greater than i2sclock */
+ tegra30_ahub_clock_set_rate(i2sclock*2);
+
+ tegra30_i2s_enable_clocks(i2s);
+
+ tegra30_i2s_set_channel_bit_count(i2s, i2sclock*2, srate);
+
+ i2s_client_ch = i2s->dsp_config.num_slots;
+ i2s_audio_ch = i2s->dsp_config.num_slots;
+
+ i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_BIT_SIZE_MASK;
+ switch (i2s->dsp_config.slot_width) {
+ case 16:
+ i2s_audio_bits = TEGRA30_AUDIOCIF_BITS_16;
+ i2s_client_bits = TEGRA30_AUDIOCIF_BITS_16;
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_BIT_SIZE_16;
+ break;
+ case 32:
+ i2s_audio_bits = TEGRA30_AUDIOCIF_BITS_32;
+ i2s_client_bits = TEGRA30_AUDIOCIF_BITS_32;
+ i2s->reg_ctrl |= TEGRA30_I2S_CTRL_BIT_SIZE_32;
+ break;
+ }
+
+ val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) |
+ ((i2s_audio_ch - 1) <<
+ TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
+ ((i2s_client_ch - 1) <<
+ TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) |
+ (i2s_audio_bits <<
+ TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) |
+ (i2s_client_bits <<
+ TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ val |= TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX;
+ tegra30_i2s_write(i2s, TEGRA30_I2S_CIF_RX_CTRL, val);
+
+ tegra30_ahub_set_tx_cif_channels(i2s->txcif,
+ i2s_audio_ch,
+ i2s_client_ch);
+ tegra30_ahub_set_tx_cif_bits(i2s->txcif,
+ i2s_audio_bits,
+ i2s_client_bits);
+ tegra30_ahub_set_tx_fifo_pack_mode(i2s->txcif, 0);
+
+ } else {
+ val |= TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX;
+ tegra30_i2s_write(i2s, TEGRA30_I2S_CIF_TX_CTRL, val);
+
+ tegra30_ahub_set_rx_cif_channels(i2s->rxcif,
+ i2s_audio_ch,
+ i2s_client_ch);
+ tegra30_ahub_set_rx_cif_bits(i2s->rxcif,
+ i2s_audio_bits,
+ i2s_client_bits);
+ tegra30_ahub_set_rx_fifo_pack_mode(i2s->rxcif, 0);
+ }
+
+ tegra30_i2s_set_slot_control(i2s, substream->stream);
+
+ tegra30_i2s_set_data_offset(i2s);
+
+ i2s->reg_ch_ctrl &= ~TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK;
+ i2s->reg_ch_ctrl |= (i2s->dsp_config.slot_width - 1) <<
+ TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_SHIFT;
+ tegra30_i2s_write(i2s, TEGRA30_I2S_CH_CTRL, i2s->reg_ch_ctrl);
+
+ tegra30_i2s_disable_clocks(i2s);
+
+ return 0;
+}
+
static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -300,6 +489,12 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
+ /* TDM mode */
+ if ((i2s->reg_ctrl & TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC) &&
+ (i2s->dsp_config.slot_width > 2))
+ return tegra30_i2s_tdm_hw_params(substream, params, dai);
+
+
srate = params_rate(params);
if (i2s->reg_ctrl & TEGRA30_I2S_CTRL_MASTER_ENABLE) {
@@ -423,12 +618,16 @@ static void tegra30_i2s_start_playback(struct tegra30_i2s *i2s)
static void tegra30_i2s_stop_playback(struct tegra30_i2s *i2s)
{
- tegra30_ahub_disable_tx_fifo(i2s->txcif);
+ int dcnt = 10;
/* if this is the only user of i2s tx then disable it*/
+ tegra30_ahub_disable_tx_fifo(i2s->txcif);
if (i2s->playback_ref_count == 1) {
i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_TX;
tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl);
}
+ while (tegra30_ahub_tx_fifo_is_busy(i2s->txcif) && dcnt--)
+ udelay(100);
+ tegra30_ahub_tx_fifo_clear(i2s->txcif);
}
static void tegra30_i2s_start_capture(struct tegra30_i2s *i2s)
@@ -442,11 +641,15 @@ static void tegra30_i2s_start_capture(struct tegra30_i2s *i2s)
static void tegra30_i2s_stop_capture(struct tegra30_i2s *i2s)
{
+ int dcnt = 10;
tegra30_ahub_disable_rx_fifo(i2s->rxcif);
if (!i2s->is_call_mode_rec) {
i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_RX;
tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl);
}
+ while (tegra30_ahub_rx_fifo_is_busy(i2s->rxcif) && dcnt--)
+ udelay(100);
+ tegra30_ahub_rx_fifo_clear(i2s->rxcif);
}
static int tegra30_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
@@ -500,6 +703,33 @@ static int tegra30_i2s_probe(struct snd_soc_dai *dai)
tegra30_i2s_disable_clocks(i2s);
#endif
+ /* Default values for DSP mode */
+ i2s->dsp_config.num_slots = 1;
+ i2s->dsp_config.slot_width = 2;
+ i2s->dsp_config.tx_mask = 1;
+ i2s->dsp_config.rx_mask = 1;
+ i2s->dsp_config.rx_data_offset = 1;
+ i2s->dsp_config.tx_data_offset = 1;
+
+
+ return 0;
+}
+
+int tegra30_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots,
+ int slot_width)
+{
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+
+ i2s->dsp_config.num_slots = slots;
+ i2s->dsp_config.slot_width = slot_width;
+ i2s->dsp_config.tx_mask = tx_mask;
+ i2s->dsp_config.rx_mask = rx_mask;
+ i2s->dsp_config.rx_data_offset = 0;
+ i2s->dsp_config.tx_data_offset = 0;
+
return 0;
}
@@ -534,6 +764,7 @@ static struct snd_soc_dai_ops tegra30_i2s_dai_ops = {
.set_fmt = tegra30_i2s_set_fmt,
.hw_params = tegra30_i2s_hw_params,
.trigger = tegra30_i2s_trigger,
+ .set_tdm_slot = tegra30_i2s_set_tdm_slot,
};
#define TEGRA30_I2S_DAI(id) \
@@ -543,13 +774,13 @@ static struct snd_soc_dai_ops tegra30_i2s_dai_ops = {
.resume = tegra30_i2s_resume, \
.playback = { \
.channels_min = 1, \
- .channels_max = 2, \
+ .channels_max = 16, \
.rates = SNDRV_PCM_RATE_8000_96000, \
.formats = SNDRV_PCM_FMTBIT_S16_LE, \
}, \
.capture = { \
.channels_min = 1, \
- .channels_max = 2, \
+ .channels_max = 16, \
.rates = SNDRV_PCM_RATE_8000_96000, \
.formats = SNDRV_PCM_FMTBIT_S16_LE, \
}, \
@@ -827,7 +1058,6 @@ static __devinit int tegra30_i2s_platform_probe(struct platform_device *pdev)
ret = PTR_ERR(i2s->clk_i2s);
goto exit;
}
-
i2s->clk_i2s_sync = clk_get(&pdev->dev, "ext_audio_sync");
if (IS_ERR(i2s->clk_i2s_sync)) {
dev_err(&pdev->dev, "Can't retrieve i2s_sync clock\n");
diff --git a/sound/soc/tegra/tegra30_i2s.h b/sound/soc/tegra/tegra30_i2s.h
index b9baddd5db8e..0992bf0d5b17 100644
--- a/sound/soc/tegra/tegra30_i2s.h
+++ b/sound/soc/tegra/tegra30_i2s.h
@@ -176,7 +176,7 @@
/* Number of slots in frame, minus 1 */
#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT 16
#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK_US 7
-#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK (TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOT_MASK_US << TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOT_SHIFT)
+#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK (TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK_US << TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT)
/* TDM mode slot enable bitmask */
#define TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT 8
@@ -231,6 +231,16 @@
/* Number of i2s controllers*/
#define TEGRA30_NR_I2S_IFC 5
+struct dsp_config_t {
+ int num_slots;
+ int rx_mask;
+ int tx_mask;
+ int slot_width;
+ int rx_data_offset;
+ int tx_data_offset;
+};
+
+
struct tegra30_i2s {
int id;
struct clk *clk_i2s;
@@ -254,6 +264,8 @@ struct tegra30_i2s {
#endif
int call_record_dam_ifc;
int is_call_mode_rec;
+
+ struct dsp_config_t dsp_config;
};
struct codec_config {