summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorRavindra Lokhande <rlokhande@nvidia.com>2014-06-26 20:31:22 +0530
committerEmad Mir <emir@nvidia.com>2014-06-30 16:32:48 -0700
commitfa9a15badd061956c4a75f1e492ca721b4647615 (patch)
tree3d1213e70e29f87973a2c0271c968dcaf1a5b44d /sound
parentd37acd2a442340c5a19b51d35bc469a2e23b41e0 (diff)
ASoC: Tegra: Refactor offload to handle multiple BE
- Added virtual mixer and switch to support multiple BE - One FE can be connected to multiple BE, mixer control can be used to select the path - Fix crash if no path is selected Change-Id: Ibbf03ef1e1826acd92402b8275d72a580e643e66 Signed-off-by: Ravindra Lokhande <rlokhande@nvidia.com> Reviewed-on: http://git-master/r/428950 GVS: Gerrit_Virtual_Submit Reviewed-by: Sumit Bhattacharya <sumitb@nvidia.com>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/tegra/tegra30_i2s.c60
-rw-r--r--sound/soc/tegra/tegra_max98090.c5
-rw-r--r--sound/soc/tegra/tegra_offload.c145
-rw-r--r--sound/soc/tegra/tegra_rt5639.c5
4 files changed, 184 insertions, 31 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 */
diff --git a/sound/soc/tegra/tegra_max98090.c b/sound/soc/tegra/tegra_max98090.c
index 4cf15c4098a4..d5a280cce764 100644
--- a/sound/soc/tegra/tegra_max98090.c
+++ b/sound/soc/tegra/tegra_max98090.c
@@ -1012,9 +1012,6 @@ static const struct snd_soc_dapm_route tegra_max98090_audio_map[] = {
/* AHUB BE connections */
{"HiFi Playback", NULL, "I2S1_OUT"},
-
- {"I2S1_OUT", NULL, "offload-pcm-playback"},
- {"I2S1_OUT", NULL, "offload-compr-playback"},
};
static const struct snd_kcontrol_new tegra_max98090_controls[] = {
@@ -1160,7 +1157,7 @@ static struct snd_soc_dai_link tegra_max98090_dai[] = {
.dynamic = 1,
},
[DAI_LINK_I2S_OFFLOAD_BE] = {
- .name = "offload-audio",
+ .name = "offload-audio-codec",
.stream_name = "offload-audio-pcm",
.codec_name = "max98090.0-0010",
.codec_dai_name = "HiFi",
diff --git a/sound/soc/tegra/tegra_offload.c b/sound/soc/tegra/tegra_offload.c
index fbe766e4342c..bdfe94e30add 100644
--- a/sound/soc/tegra/tegra_offload.c
+++ b/sound/soc/tegra/tegra_offload.c
@@ -59,6 +59,8 @@ static struct tegra_offload_ops offload_ops;
static int tegra_offload_init_done;
static DEFINE_MUTEX(tegra_offload_lock);
+static int codec, spk;
+
static const struct snd_pcm_hardware tegra_offload_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -247,32 +249,54 @@ static int tegra_offload_compr_set_params(struct snd_compr_stream *stream,
struct snd_soc_pcm_runtime *rtd = stream->device->private_data;
struct tegra_pcm_dma_params *dmap;
struct tegra_offload_compr_params offl_params;
+ int dir;
int ret = 0;
dev_vdbg(dev, "%s", __func__);
+ if (stream->direction == SND_COMPRESS_PLAYBACK)
+ dir = SNDRV_PCM_STREAM_PLAYBACK;
+ else
+ dir = SNDRV_PCM_STREAM_CAPTURE;
+
dmap = rtd->cpu_dai->playback_dma_data;
if (!dmap) {
struct snd_soc_dpcm *dpcm;
+ if (list_empty(&rtd->dpcm[dir].be_clients)) {
+ dev_err(dev, "No backend DAIs enabled for %s\n",
+ rtd->dai_link->name);
+ return -EINVAL;
+ }
+
list_for_each_entry(dpcm,
- &rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients,
- list_be) {
+ &rtd->dpcm[dir].be_clients, list_be) {
struct snd_soc_pcm_runtime *be = dpcm->be;
struct snd_pcm_substream *be_substream =
- snd_soc_dpcm_get_substream(be,
- SNDRV_PCM_STREAM_PLAYBACK);
+ snd_soc_dpcm_get_substream(be, dir);
+ struct snd_soc_dai_link *dai_link = be->dai_link;
dmap = snd_soc_dai_get_dma_data(be->cpu_dai,
- be_substream);
- if (!dmap) {
- dev_err(dev, "Failed to get DMA params.");
- return -ENODEV;
+ be_substream);
+
+ if (spk && strstr(dai_link->name, "speaker")) {
+ dmap = snd_soc_dai_get_dma_data(be->cpu_dai,
+ be_substream);
+ break;
+ }
+ if (codec && strstr(dai_link->name, "codec")) {
+ dmap = snd_soc_dai_get_dma_data(be->cpu_dai,
+ be_substream);
+ break;
}
/* TODO : Multiple BE to single FE not yet supported */
- break;
}
}
+ if (!dmap) {
+ dev_err(dev, "Failed to get DMA params.");
+ return -ENODEV;
+ }
+
offl_params.codec_type = params->codec.id;
offl_params.bits_per_sample = 16;
offl_params.rate = snd_pcm_rate_bit_to_rate(params->codec.sample_rate);
@@ -472,23 +496,40 @@ static int tegra_offload_pcm_hw_params(struct snd_pcm_substream *substream,
if (!dmap) {
struct snd_soc_dpcm *dpcm;
+ if (list_empty(&rtd->dpcm[substream->stream].be_clients)) {
+ dev_err(dev, "No backend DAIs enabled for %s\n",
+ rtd->dai_link->name);
+ return -EINVAL;
+ }
+
list_for_each_entry(dpcm,
&rtd->dpcm[substream->stream].be_clients, list_be) {
struct snd_soc_pcm_runtime *be = dpcm->be;
struct snd_pcm_substream *be_substream =
snd_soc_dpcm_get_substream(be,
substream->stream);
+ struct snd_soc_dai_link *dai_link = be->dai_link;
dmap = snd_soc_dai_get_dma_data(be->cpu_dai,
- be_substream);
- if (!dmap) {
- dev_err(dev, "Failed to get DMA params.");
- return -ENODEV;
+ be_substream);
+
+ if (spk && strstr(dai_link->name, "speaker")) {
+ dmap = snd_soc_dai_get_dma_data(be->cpu_dai,
+ be_substream);
+ break;
+ }
+ if (codec && strstr(dai_link->name, "codec")) {
+ dmap = snd_soc_dai_get_dma_data(be->cpu_dai,
+ be_substream);
+ break;
}
/* TODO : Multiple BE to single FE not yet supported */
- break;
}
}
+ if (!dmap) {
+ dev_err(dev, "Failed to get DMA params.");
+ return -ENODEV;
+ }
offl_params.bits_per_sample =
snd_pcm_format_width(params_format(params));
@@ -577,6 +618,59 @@ static int tegra_offload_pcm_ack(struct snd_pcm_substream *substream)
return 0;
}
+static int codec_get_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = codec;
+ return 0;
+}
+
+static int codec_put_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+ if (ucontrol->value.integer.value[0]) {
+ codec = ucontrol->value.integer.value[0];
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
+ } else {
+ codec = ucontrol->value.integer.value[0];
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
+ }
+ return 1;
+}
+
+static int spk_get_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = spk;
+ return 0;
+}
+
+static int spk_put_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+ if (ucontrol->value.integer.value[0]) {
+ spk = ucontrol->value.integer.value[0];
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
+ } else {
+ spk = ucontrol->value.integer.value[0];
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
+ }
+ return 1;
+}
+
+static const struct snd_kcontrol_new codec_control =
+ SOC_SINGLE_EXT("Codec Switch", SND_SOC_NOPM, 0, 1, 0,
+ codec_get_mixer, codec_put_switch);
+
+static const struct snd_kcontrol_new spk_control =
+ SOC_SINGLE_EXT("SPK Switch", SND_SOC_NOPM, 1, 1, 0,
+ spk_get_mixer, spk_put_switch);
static const struct snd_soc_dapm_widget tegra_offload_widgets[] = {
/* BackEnd DAIs */
@@ -591,6 +685,20 @@ static const struct snd_soc_dapm_widget tegra_offload_widgets[] = {
SND_SOC_DAPM_AIF_OUT("I2S4_OUT", "tegra30-i2s.4 Playback", 0,
0/*wreg*/, 0/*wshift*/, 0/*winvert*/),
+ SND_SOC_DAPM_MIXER("Codec VMixer", SND_SOC_NOPM, 0, 0,
+ &codec_control, 1),
+ SND_SOC_DAPM_MIXER("SPK VMixer", SND_SOC_NOPM, 0, 0,
+ &spk_control, 1),
+};
+
+static const struct snd_soc_dapm_route graph[] = {
+ {"Codec VMixer", "Codec Switch", "offload-pcm-playback"},
+ {"Codec VMixer", "Codec Switch", "offload-compr-playback"},
+ {"I2S1_OUT", NULL, "Codec VMixer"},
+
+ {"SPK VMixer", "SPK Switch", "offload-pcm-playback"},
+ {"SPK VMixer", "SPK Switch", "offload-compr-playback"},
+ {"I2S2_OUT", NULL, "SPK VMixer"},
};
static struct snd_pcm_ops tegra_pcm_ops = {
@@ -677,10 +785,6 @@ static int tegra_offload_pcm_probe(struct snd_soc_platform *platform)
{
pr_debug("%s", __func__);
- snd_soc_dapm_new_controls(&platform->dapm, tegra_offload_widgets,
- ARRAY_SIZE(tegra_offload_widgets));
-
- snd_soc_dapm_new_widgets(&platform->dapm);
platform->dapm.idle_bias_off = 1;
return 0;
}
@@ -705,6 +809,11 @@ static struct snd_soc_platform_driver tegra_offload_platform = {
.probe = tegra_offload_pcm_probe,
.read = tegra_offload_pcm_read,
.write = tegra_offload_pcm_write,
+
+ .dapm_widgets = tegra_offload_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra_offload_widgets),
+ .dapm_routes = graph,
+ .num_dapm_routes = ARRAY_SIZE(graph),
};
static struct snd_soc_dai_driver tegra_offload_dai[] = {
diff --git a/sound/soc/tegra/tegra_rt5639.c b/sound/soc/tegra/tegra_rt5639.c
index df9161c84868..467375e15770 100644
--- a/sound/soc/tegra/tegra_rt5639.c
+++ b/sound/soc/tegra/tegra_rt5639.c
@@ -929,9 +929,6 @@ static const struct snd_soc_dapm_route ardbeg_audio_map[] = {
/*{"IN1P", NULL, "micbias1"},*/
/*{"IN1N", NULL, "micbias1"},*/
/* AHUB BE connections */
- {"I2S1_OUT", NULL, "offload-pcm-playback"},
- {"I2S1_OUT", NULL, "offload-compr-playback"},
-
{"AIF1 Playback", NULL, "I2S1_OUT"},
};
@@ -1066,7 +1063,7 @@ static struct snd_soc_dai_link tegra_rt5639_dai[NUM_DAI_LINKS] = {
.dynamic = 1,
},
[DAI_LINK_I2S_OFFLOAD_BE] = {
- .name = "offload-audio",
+ .name = "offload-audio-codec",
.stream_name = "offload-audio-pcm",
.codec_name = "rt5639.0-001a",
.platform_name = "tegra30-i2s.1",