ASoC: Tegra: Add support for AVP rendering
Sumit Bhattacharya [Wed, 30 May 2012 11:03:00 +0000 (16:03 +0530)]
Add alsa controls to specify which alsa device is used by AVP to render
audio. Also add support for disabling DMA interrupt when AVP renders
audio. Also add couple of alsa controls to query DMA channel ID and
DMA buffer physical address.

Bug 968814
Signed-off-by: Sumit Bhattacharya <sumitb@nvidia.com>

Change-Id: If593329db72bf00d97f7433f5c54e13500281253
Reviewed-on: http://git-master/r/97916
Reviewed-by: Rohan Somvanshi <rsomvanshi@nvidia.com>
Tested-by: Rohan Somvanshi <rsomvanshi@nvidia.com>

Conflicts:

sound/soc/tegra/tegra_wm8903.c

sound/soc/tegra/tegra_aic326x.c
sound/soc/tegra/tegra_asoc_utils.c
sound/soc/tegra/tegra_asoc_utils.h
sound/soc/tegra/tegra_max98088.c
sound/soc/tegra/tegra_max98095.c
sound/soc/tegra/tegra_pcm.c
sound/soc/tegra/tegra_pcm.h
sound/soc/tegra/tegra_rt5640.c
sound/soc/tegra/tegra_wm8753.c
sound/soc/tegra/tegra_wm8903.c

index ccf80f0..0e14fa8 100644 (file)
@@ -1062,6 +1062,10 @@ static int tegra_aic326x_init(struct snd_soc_pcm_runtime *rtd)
        if (ret < 0)
                return ret;
 
+       ret = tegra_asoc_utils_register_ctls(&machine->util_data);
+       if (ret < 0)
+               return ret;
+
        snd_soc_dapm_force_enable_pin(dapm, "MICBIAS_EXT ON");
        snd_soc_dapm_force_enable_pin(dapm,"MICBIAS_INT ON");
        snd_soc_dapm_sync(dapm);
index c211114..818cf03 100644 (file)
@@ -28,6 +28,9 @@
 
 #include <mach/clk.h>
 
+#include <sound/soc.h>
+
+#include "tegra_pcm.h"
 #include "tegra_asoc_utils.h"
 
 int g_is_call_mode;
@@ -41,6 +44,115 @@ bool tegra_is_voice_call_active(void)
 }
 EXPORT_SYMBOL_GPL(tegra_is_voice_call_active);
 
+static int tegra_get_avp_device(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct  tegra_asoc_utils_data *data = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = data->avp_device_id;
+       return 0;
+}
+
+static int tegra_set_avp_device(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct  tegra_asoc_utils_data *data = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_card *card = data->card;
+       struct snd_soc_pcm_runtime *rtd;
+       struct snd_pcm_substream *substream;
+       struct tegra_runtime_data *prtd;
+       int id, old_id = data->avp_device_id;
+
+       id = ucontrol->value.integer.value[0];
+       if ((id >= card->num_rtd) || (id < 0))
+               id = -1;
+
+       if (old_id >= 0) {
+               rtd = &card->rtd[old_id];
+               substream =
+                       rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+               if (substream && substream->runtime) {
+                       prtd = substream->runtime->private_data;
+                       if (prtd->running)
+                               return -EBUSY;
+                       if (prtd)
+                               prtd->disable_intr = false;
+               }
+       }
+
+       if (id >= 0) {
+               rtd = &card->rtd[id];
+               substream =
+                       rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+               if (substream && substream->runtime) {
+                       prtd = substream->runtime->private_data;
+                       if (prtd->running)
+                               return -EBUSY;
+                       if (prtd)
+                               prtd->disable_intr = true;
+               }
+       }
+       data->avp_device_id = id;
+       return 1;
+}
+
+static int tegra_get_dma_ch_id(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct  tegra_asoc_utils_data *data = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_card *card = data->card;
+       struct snd_soc_pcm_runtime *rtd;
+       struct snd_pcm_substream *substream;
+       struct tegra_runtime_data *prtd;
+
+       ucontrol->value.integer.value[0] = -1;
+       if (data->avp_device_id < 0)
+               return 0;
+
+       rtd = &card->rtd[data->avp_device_id];
+       substream = rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+       if (!substream || !substream->runtime)
+               return 0;
+
+       prtd = substream->runtime->private_data;
+       if (!prtd || !prtd->dma_chan)
+               return 0;
+
+       ucontrol->value.integer.value[0] =
+               tegra_dma_get_channel_id(prtd->dma_chan);
+       return 0;
+}
+
+static int tegra_get_dma_addr(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct  tegra_asoc_utils_data *data = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_card *card = data->card;
+       struct snd_soc_pcm_runtime *rtd;
+       struct snd_pcm_substream *substream;
+
+       ucontrol->value.integer.value[0] = 0;
+       if (data->avp_device_id < 0)
+               return 0;
+
+       rtd = &card->rtd[data->avp_device_id];
+       substream = rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+       if (!substream || !substream->runtime)
+               return 0;
+
+       ucontrol->value.integer.value[0] = substream->runtime->dma_addr;
+       return 0;
+}
+
+struct snd_kcontrol_new tegra_avp_controls[] = {
+       SOC_SINGLE_EXT("AVP alsa device select", 0, 0, TEGRA_ALSA_MAX_DEVICES, \
+                       0, tegra_get_avp_device, tegra_set_avp_device),
+       SOC_SINGLE_EXT("AVP DMA channel id", 0, 0, TEGRA_DMA_MAX_CHANNELS, \
+                       0, tegra_get_dma_ch_id, NULL),
+       SOC_SINGLE_EXT("AVP DMA address", 0, 0, 0xFFFFFFFF, \
+                       0, tegra_get_dma_addr, NULL),
+};
+
 int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
                              int mclk)
 {
@@ -153,6 +265,26 @@ int tegra_asoc_utils_clk_disable(struct tegra_asoc_utils_data *data)
 }
 EXPORT_SYMBOL_GPL(tegra_asoc_utils_clk_disable);
 
+int tegra_asoc_utils_register_ctls(struct tegra_asoc_utils_data *data)
+{
+       int i;
+       int ret = 0;
+
+       /* Add AVP related alsa controls */
+       data->avp_device_id = -1;
+       for (i = 0; i < ARRAY_SIZE(tegra_avp_controls); i++) {
+               ret = snd_ctl_add(data->card->snd_card,
+                               snd_ctl_new1(&tegra_avp_controls[i], data));
+               if (ret < 0) {
+                       dev_err(data->dev, "Can't add avp alsa controls");
+                       return ret;
+               }
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(tegra_asoc_utils_register_ctls);
+
 int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
                          struct device *dev, struct snd_soc_card *card)
 {
index 512df0d..0423f02 100644 (file)
@@ -25,6 +25,8 @@
 
 
 #define TEGRA30_I2S_MASTER_PLAYBACK 1
+#define TEGRA_ALSA_MAX_DEVICES 6
+#define TEGRA_DMA_MAX_CHANNELS 32
 
 struct clk;
 struct device;
@@ -41,6 +43,7 @@ struct tegra_asoc_utils_data {
        int set_baseclock;
        int set_mclk;
        int lock_count;
+       int avp_device_id;
 };
 
 int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
@@ -52,6 +55,7 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
 void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data);
 int tegra_asoc_utils_clk_enable(struct tegra_asoc_utils_data *data);
 int tegra_asoc_utils_clk_disable(struct tegra_asoc_utils_data *data);
+int tegra_asoc_utils_register_ctls(struct tegra_asoc_utils_data *data);
 
 #endif
 
index 705c497..df3e853 100644 (file)
@@ -1009,6 +1009,10 @@ static int tegra_max98088_init(struct snd_soc_pcm_runtime *rtd)
        if (ret < 0)
                return ret;
 
+       ret = tegra_asoc_utils_register_ctls(&machine->util_data);
+       if (ret < 0)
+               return ret;
+
        snd_soc_dapm_nc_pin(dapm, "INA1");
        snd_soc_dapm_nc_pin(dapm, "INA2");
        snd_soc_dapm_nc_pin(dapm, "INB1");
index f39572f..e17c212 100644 (file)
@@ -542,6 +542,10 @@ static int tegra_max98095_init(struct snd_soc_pcm_runtime *rtd)
                tegra_max98095_hp_jack_pins);
 #endif
 
+       ret = tegra_asoc_utils_register_ctls(&machine->util_data);
+       if (ret < 0)
+               return ret;
+
        /* max98095_headset_detect(codec, &tegra_max98095_hp_jack,
                SND_JACK_HEADSET); */
 
index dfcbc01..b102910 100644 (file)
@@ -52,8 +52,8 @@ static const struct snd_pcm_hardware tegra_pcm_hardware = {
        .channels_min           = 1,
        .channels_max           = 2,
        .period_bytes_min       = 128,
-       .period_bytes_max       = PAGE_SIZE,
-       .periods_min            = 2,
+       .period_bytes_max       = PAGE_SIZE * 2,
+       .periods_min            = 1,
        .periods_max            = 8,
        .buffer_bytes_max       = PAGE_SIZE * 8,
        .fifo_size              = 4,
@@ -272,6 +272,15 @@ int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
                prtd->dma_pos_end = frames_to_bytes(runtime, runtime->periods * runtime->period_size);
                prtd->period_index = 0;
                prtd->dma_req_idx = 0;
+               if (prtd->disable_intr) {
+                       prtd->dma_req_count = 1;
+                       prtd->dma_req[0].complete = NULL;
+               } else if (!prtd->dma_req[0].complete) {
+                       prtd->dma_req[0].complete = dma_complete_callback;
+                       prtd->dma_req_count =
+                               (MAX_DMA_REQ_COUNT <= runtime->periods) ?
+                               MAX_DMA_REQ_COUNT : runtime->periods;
+               }
                /* Fall-through */
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
@@ -289,8 +298,9 @@ int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
                spin_unlock_irqrestore(&prtd->lock, flags);
                tegra_dma_cancel(prtd->dma_chan);
                for (i = 0; i < prtd->dma_req_count; i++) {
-                       if (prtd->dma_req[i].status ==
-                               -TEGRA_DMA_REQ_ERROR_ABORTED)
+                       if (prtd->dma_req[i].complete &&
+                               (prtd->dma_req[i].status ==
+                                -TEGRA_DMA_REQ_ERROR_ABORTED))
                                prtd->dma_req[i].complete(&prtd->dma_req[i]);
                }
                break;
index 7fe2278..b63de32 100644 (file)
@@ -53,6 +53,7 @@ struct tegra_runtime_data {
        struct tegra_dma_req dma_req[MAX_DMA_REQ_COUNT];
        struct tegra_dma_channel *dma_chan;
        int dma_req_count;
+       int disable_intr;
 };
 
 int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd);
index 72be76f..eab71a4 100644 (file)
@@ -547,6 +547,11 @@ static int tegra_rt5640_init(struct snd_soc_pcm_runtime *rtd)
 
        snd_soc_dapm_add_routes(dapm, cardhu_audio_map,
                                ARRAY_SIZE(cardhu_audio_map));
+
+       ret = tegra_asoc_utils_register_ctls(&machine->util_data);
+       if (ret < 0)
+               return ret;
+
        /* FIXME: Calculate automatically based on DAPM routes? */
        snd_soc_dapm_nc_pin(dapm, "LOUTL");
        snd_soc_dapm_nc_pin(dapm, "LOUTR");
index 593829a..e873143 100644 (file)
@@ -782,6 +782,10 @@ static int tegra_wm8753_init(struct snd_soc_pcm_runtime *rtd)
        if (ret < 0)
                return ret;
 
+       ret = tegra_asoc_utils_register_ctls(&machine->util_data);
+       if (ret < 0)
+               return ret;
+
        snd_soc_dapm_nc_pin(dapm, "ACIN");
        snd_soc_dapm_nc_pin(dapm, "ACOP");
        snd_soc_dapm_nc_pin(dapm, "OUT3");
index a2a99e3..19f88de 100644 (file)
@@ -687,6 +687,10 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
        wm8903_mic_detect(codec, &tegra_wm8903_mic_jack, SND_JACK_MICROPHONE,
                          machine_is_cardhu() ? SND_JACK_MICROPHONE : 0);
 
+       ret = tegra_asoc_utils_register_ctls(&machine->util_data);
+       if (ret < 0)
+               return ret;
+
        snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
 
        return 0;