diff options
Diffstat (limited to 'sound/soc/tegra/tegra30_i2s.c')
-rw-r--r-- | sound/soc/tegra/tegra30_i2s.c | 60 |
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 */ |