summaryrefslogtreecommitdiff
path: root/sound/soc/tegra/tegra30_i2s.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/tegra/tegra30_i2s.c')
-rw-r--r--sound/soc/tegra/tegra30_i2s.c60
1 files changed, 55 insertions, 5 deletions
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
index c54e078014a0..d81b9b4dd64e 100644
--- a/sound/soc/tegra/tegra30_i2s.c
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -50,6 +50,10 @@
#define RETRY_CNT 10
+static struct snd_soc_pcm_runtime *allocated_fe;
+static int apbif_ref_cnt;
+static DEFINE_MUTEX(apbif_mutex);
+
extern int tegra_i2sloopback_func;
static struct tegra30_i2s *i2scont[TEGRA30_NR_I2S_IFC];
#if defined(CONFIG_ARCH_TEGRA_14x_SOC)
@@ -91,17 +95,57 @@ int tegra30_i2s_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
- int ret;
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ int allocate_fifo = 1;
/* increment the playback ref count */
i2s->playback_ref_count++;
- ret = tegra30_ahub_allocate_tx_fifo(&i2s->playback_fifo_cif,
+ mutex_lock(&apbif_mutex);
+ if (dai_link->no_pcm) {
+ struct snd_soc_dpcm *dpcm;
+
+ list_for_each_entry(dpcm,
+ &rtd->dpcm[substream->stream].fe_clients,
+ list_fe) {
+ struct snd_soc_pcm_runtime *fe = dpcm->fe;
+
+ if (allocated_fe == fe) {
+ allocate_fifo = 0;
+ break;
+ }
+
+ if (allocated_fe == NULL) {
+ allocated_fe = fe;
+ snd_soc_pcm_set_drvdata(allocated_fe,
+ i2s);
+ }
+ }
+ }
+
+ if (allocate_fifo) {
+ ret = tegra30_ahub_allocate_tx_fifo(
+ &i2s->playback_fifo_cif,
&i2s->playback_dma_data.addr,
&i2s->playback_dma_data.req_sel);
- i2s->playback_dma_data.wrap = 4;
- i2s->playback_dma_data.width = 32;
+ i2s->playback_dma_data.wrap = 4;
+ i2s->playback_dma_data.width = 32;
+ } else {
+ struct tegra30_i2s *allocated_be =
+ snd_soc_pcm_get_drvdata(allocated_fe);
+ if (allocated_be) {
+ memcpy(&i2s->playback_dma_data,
+ &allocated_be->playback_dma_data,
+ sizeof(struct tegra_pcm_dma_params));
+ i2s->playback_fifo_cif =
+ allocated_be->playback_fifo_cif;
+ }
+ }
+ apbif_ref_cnt++;
+ mutex_unlock(&apbif_mutex);
if (!i2s->is_dam_used)
tegra30_ahub_set_rx_cif_source(
@@ -131,8 +175,14 @@ void tegra30_i2s_shutdown(struct snd_pcm_substream *substream,
tegra30_ahub_unset_rx_cif_source(
i2s->playback_i2s_cif);
+ mutex_lock(&apbif_mutex);
+ apbif_ref_cnt--;
/* free the apbif dma channel*/
- tegra30_ahub_free_tx_fifo(i2s->playback_fifo_cif);
+ if (!apbif_ref_cnt) {
+ tegra30_ahub_free_tx_fifo(i2s->playback_fifo_cif);
+ allocated_fe = NULL;
+ }
+ mutex_unlock(&apbif_mutex);
i2s->playback_fifo_cif = -1;
/* decrement the playback ref count */