asoc: tegra: aic326x machine: add voice call functionalities for ap30
Nikesh Oswal [Wed, 14 Mar 2012 16:19:03 +0000 (21:19 +0530)]
add voice call, bt voice call, voice call with system sound mix,
bt voice call with system sound mix, voice call recording,
bt voice call recording  functionalities for ti aic326x on ap30

Change-Id: Ia94c1586c30d0636d8de738ff6cbb716f4f79d16
Signed-off-by: Nikesh Oswal <noswal@nvidia.com>
Reviewed-on: http://git-master/r/90123
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Scott Peterson <speterson@nvidia.com>

sound/soc/tegra/tegra_aic326x.c

index b64ab41..6d38934 100644 (file)
 
 extern int g_is_call_mode;
 
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+const char *tegra_i2s_dai_name[TEGRA30_NR_I2S_IFC] = {
+       "tegra30-i2s.0",
+       "tegra30-i2s.1",
+       "tegra30-i2s.2",
+       "tegra30-i2s.3",
+       "tegra30-i2s.4",
+};
+#endif
+
 struct tegra_aic326x {
        struct tegra_asoc_utils_data util_data;
        struct tegra_aic326x_platform_data *pdata;
@@ -81,6 +91,10 @@ struct tegra_aic326x {
        bool init_done;
        int is_call_mode;
        int is_device_bt;
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       struct codec_config codec_info[NUM_I2S_DEVICES];
+       struct snd_soc_card *pcard;
+#endif
 };
 
 static int tegra_aic326x_call_mode_info(struct snd_kcontrol *kcontrol,
@@ -110,6 +124,9 @@ static int tegra_aic326x_call_mode_put(struct snd_kcontrol *kcontrol,
        int is_call_mode_new = ucontrol->value.integer.value[0];
 #ifdef CONFIG_ARCH_TEGRA_2x_SOC
        int codec_dap_id, codec_dap_sel, bb_dap_id, bb_dap_sel;
+#else /*assumes tegra3*/
+       int codec_index;
+       unsigned int i;
 #endif
 
        if (machine->is_call_mode == is_call_mode_new)
@@ -127,6 +144,11 @@ static int tegra_aic326x_call_mode_put(struct snd_kcontrol *kcontrol,
                codec_dap_id = TEGRA20_DAS_DAP_ID_2;
                codec_dap_sel = TEGRA20_DAS_DAP_SEL_DAP2;
        }
+#else /*assumes tegra3*/
+       if (machine->is_device_bt)
+               codec_index = BT_SCO;
+       else
+               codec_index = HIFI_CODEC;
 #endif
 
        if (is_call_mode_new) {
@@ -139,6 +161,17 @@ static int tegra_aic326x_call_mode_put(struct snd_kcontrol *kcontrol,
                        codec_dap_sel, 1, 0, 0);
                tegra20_das_set_tristate(codec_dap_id, 0);
                tegra20_das_set_tristate(bb_dap_id, 0);
+#else /*assumes tegra3*/
+               if (machine->codec_info[codec_index].rate == 0 ||
+                       machine->codec_info[codec_index].channels == 0)
+                               return -EINVAL;
+
+               for (i = 0; i < machine->pcard->num_links; i++)
+                       machine->pcard->dai_link[i].ignore_suspend = 1;
+
+               tegra30_make_voice_call_connections(
+                       &machine->codec_info[codec_index],
+                       &machine->codec_info[BASEBAND]);
 #endif
        } else {
 #ifdef CONFIG_ARCH_TEGRA_2x_SOC
@@ -150,6 +183,13 @@ static int tegra_aic326x_call_mode_put(struct snd_kcontrol *kcontrol,
                        codec_dap_sel, 0, 0, 0);
                tegra20_das_set_tristate(codec_dap_id, 0);
                tegra20_das_set_tristate(bb_dap_id, 0);
+#else /*assumes tegra3*/
+               tegra30_break_voice_call_connections(
+                       &machine->codec_info[codec_index],
+                       &machine->codec_info[BASEBAND]);
+
+               for (i = 0; i < machine->pcard->num_links; i++)
+                       machine->pcard->dai_link[i].ignore_suspend = 0;
 #endif
        }
 
@@ -196,6 +236,35 @@ static int tegra_aic326x_get_mclk(int srate)
        return mclk;
 }
 
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+static int tegra_aic326x_set_dam_cif(int dam_ifc, int srate,
+                       int channels, int bit_size, int src_on, int src_srate,
+                       int src_channels, int src_bit_size)
+{
+       tegra30_dam_set_gain(dam_ifc, TEGRA30_DAM_CHIN1, 0x1000);
+       tegra30_dam_set_samplerate(dam_ifc, TEGRA30_DAM_CHOUT,
+                               srate);
+       tegra30_dam_set_samplerate(dam_ifc, TEGRA30_DAM_CHIN1,
+                               srate);
+       tegra30_dam_set_acif(dam_ifc, TEGRA30_DAM_CHIN1,
+               channels, bit_size, channels,
+                               bit_size);
+       tegra30_dam_set_acif(dam_ifc, TEGRA30_DAM_CHOUT,
+               channels, bit_size, channels,
+                               bit_size);
+
+       if (src_on) {
+               tegra30_dam_set_gain(dam_ifc, TEGRA30_DAM_CHIN0_SRC, 0x1000);
+               tegra30_dam_set_samplerate(dam_ifc, TEGRA30_DAM_CHIN0_SRC,
+                       src_srate);
+               tegra30_dam_set_acif(dam_ifc, TEGRA30_DAM_CHIN0_SRC,
+                       src_channels, src_bit_size, 1, 16);
+       }
+
+       return 0;
+}
+#endif
+
 static int tegra_aic326x_hw_params(struct snd_pcm_substream *substream,
                                        struct snd_pcm_hw_params *params)
 {
@@ -207,6 +276,9 @@ static int tegra_aic326x_hw_params(struct snd_pcm_substream *substream,
        struct tegra_aic326x *machine = snd_soc_card_get_drvdata(card);
        int srate, mclk, sample_size, daifmt;
        int err;
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+#endif
 
        switch (params_format(params)) {
        case SNDRV_PCM_FORMAT_S16_LE:
@@ -271,6 +343,10 @@ static int tegra_aic326x_hw_params(struct snd_pcm_substream *substream,
                dev_err(card->dev, "failed to set dac-dap path\n");
                return err;
        }
+#else /*assumes tegra3*/
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK  && i2s->is_dam_used)
+               tegra_aic326x_set_dam_cif(i2s->dam_ifc, srate,
+                       params_channels(params), sample_size, 0, 0, 0, 0);
 #endif
 
        return 0;
@@ -315,6 +391,9 @@ static int tegra_aic326x_bt_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_card *card = rtd->card;
        struct tegra_aic326x *machine = snd_soc_card_get_drvdata(card);
        int err, srate, mclk, min_mclk, sample_size;
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+#endif
 
        switch (params_format(params)) {
        case SNDRV_PCM_FORMAT_S16_LE:
@@ -368,11 +447,164 @@ static int tegra_aic326x_bt_hw_params(struct snd_pcm_substream *substream,
                dev_err(card->dev, "failed to set dac-dap path\n");
                return err;
        }
+#else
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && i2s->is_dam_used)
+               tegra_aic326x_set_dam_cif(i2s->dam_ifc, params_rate(params),
+                       params_channels(params), sample_size, 0, 0, 0, 0);
 #endif
 
        return 0;
 }
 
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+static int tegra_aic326x_startup(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+       struct tegra_aic326x *machine = snd_soc_card_get_drvdata(rtd->card);
+       struct codec_config *codec_info;
+       struct codec_config *bb_info;
+       int codec_index;
+
+       if (!i2s->is_dam_used)
+               return 0;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               /*dam configuration*/
+               if (!i2s->dam_ch_refcount)
+                       i2s->dam_ifc = tegra30_dam_allocate_controller();
+
+               tegra30_dam_allocate_channel(i2s->dam_ifc, TEGRA30_DAM_CHIN1);
+               i2s->dam_ch_refcount++;
+               tegra30_dam_enable_clock(i2s->dam_ifc);
+
+               tegra30_ahub_set_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX1 +
+                               (i2s->dam_ifc*2), i2s->txcif);
+
+               /*
+               *make the dam tx to i2s rx connection if this is the only client
+               *using i2s for playback
+               */
+               if (i2s->playback_ref_count == 1)
+                       tegra30_ahub_set_rx_cif_source(
+                               TEGRA30_AHUB_RXCIF_I2S0_RX0 + i2s->id,
+                               TEGRA30_AHUB_TXCIF_DAM0_TX0 + i2s->dam_ifc);
+
+               /* enable the dam*/
+               tegra30_dam_enable(i2s->dam_ifc, TEGRA30_DAM_ENABLE,
+                               TEGRA30_DAM_CHIN1);
+       } else {
+
+               i2s->is_call_mode_rec = machine->is_call_mode;
+
+               if (!i2s->is_call_mode_rec)
+                       return 0;
+
+               if (machine->is_device_bt)
+                       codec_index = BT_SCO;
+               else
+                       codec_index = HIFI_CODEC;
+
+               codec_info = &machine->codec_info[codec_index];
+               bb_info = &machine->codec_info[BASEBAND];
+
+               /* allocate a dam for voice call recording */
+
+               i2s->call_record_dam_ifc = tegra30_dam_allocate_controller();
+               tegra30_dam_allocate_channel(i2s->call_record_dam_ifc,
+                       TEGRA30_DAM_CHIN0_SRC);
+               tegra30_dam_allocate_channel(i2s->call_record_dam_ifc,
+                       TEGRA30_DAM_CHIN1);
+               tegra30_dam_enable_clock(i2s->call_record_dam_ifc);
+
+               /* configure the dam */
+               tegra_aic326x_set_dam_cif(i2s->call_record_dam_ifc,
+                       codec_info->rate, codec_info->channels,
+                       codec_info->bitsize, 1, bb_info->rate,
+                       bb_info->channels, bb_info->bitsize);
+
+               /* setup the connections for voice call record */
+
+               tegra30_ahub_unset_rx_cif_source(i2s->rxcif);
+               tegra30_ahub_set_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX0 +
+                       (i2s->call_record_dam_ifc*2),
+                       TEGRA30_AHUB_TXCIF_I2S0_TX0 + bb_info->i2s_id);
+               tegra30_ahub_set_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX1 +
+                       (i2s->call_record_dam_ifc*2),
+                       TEGRA30_AHUB_TXCIF_I2S0_TX0 + codec_info->i2s_id);
+               tegra30_ahub_set_rx_cif_source(i2s->rxcif,
+                       TEGRA30_AHUB_TXCIF_DAM0_TX0 + i2s->call_record_dam_ifc);
+
+               /* enable the dam*/
+
+               tegra30_dam_enable(i2s->call_record_dam_ifc, TEGRA30_DAM_ENABLE,
+                               TEGRA30_DAM_CHIN1);
+               tegra30_dam_enable(i2s->call_record_dam_ifc, TEGRA30_DAM_ENABLE,
+                               TEGRA30_DAM_CHIN0_SRC);
+       }
+
+       return 0;
+}
+
+static void tegra_aic326x_shutdown(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+
+       if (!i2s->is_dam_used)
+               return;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               /* disable the dam*/
+               tegra30_dam_enable(i2s->dam_ifc, TEGRA30_DAM_DISABLE,
+                               TEGRA30_DAM_CHIN1);
+
+               /* disconnect the ahub connections*/
+               tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX1 +
+                                       (i2s->dam_ifc*2));
+
+               /* disable the dam and free the controller */
+               tegra30_dam_disable_clock(i2s->dam_ifc);
+               tegra30_dam_free_channel(i2s->dam_ifc, TEGRA30_DAM_CHIN1);
+               i2s->dam_ch_refcount--;
+               if (!i2s->dam_ch_refcount)
+                       tegra30_dam_free_controller(i2s->dam_ifc);
+        } else {
+
+               if (!i2s->is_call_mode_rec)
+                       return 0;
+
+               i2s->is_call_mode_rec = 0;
+
+               /* disable the dam*/
+               tegra30_dam_enable(i2s->call_record_dam_ifc,
+                       TEGRA30_DAM_DISABLE, TEGRA30_DAM_CHIN1);
+               tegra30_dam_enable(i2s->call_record_dam_ifc,
+                       TEGRA30_DAM_DISABLE, TEGRA30_DAM_CHIN0_SRC);
+
+               /* disconnect the ahub connections*/
+               tegra30_ahub_unset_rx_cif_source(i2s->rxcif);
+               tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX0 +
+                       (i2s->call_record_dam_ifc*2));
+               tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX1 +
+                       (i2s->call_record_dam_ifc*2));
+
+               /* free the dam channels and dam controller */
+               tegra30_dam_disable_clock(i2s->call_record_dam_ifc);
+               tegra30_dam_free_channel(i2s->call_record_dam_ifc,
+                       TEGRA30_DAM_CHIN1);
+               tegra30_dam_free_channel(i2s->call_record_dam_ifc,
+                       TEGRA30_DAM_CHIN0_SRC);
+               tegra30_dam_free_controller(i2s->call_record_dam_ifc);
+        }
+
+       return;
+}
+#endif
+
+
 static int tegra_aic326x_hw_free(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -412,10 +644,18 @@ static int tegra_aic326x_voice_call_hw_params(
 
        tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1);
 
-       err = snd_soc_dai_set_fmt(codec_dai,
+       if(machine_is_tegra_enterprise()) {
+               err = snd_soc_dai_set_fmt(codec_dai,
+                                       SND_SOC_DAIFMT_I2S |
+                                       SND_SOC_DAIFMT_NB_NF |
+                                       SND_SOC_DAIFMT_CBS_CFS);
+       } else {
+               err = snd_soc_dai_set_fmt(codec_dai,
                                        SND_SOC_DAIFMT_DSP_B |
                                        SND_SOC_DAIFMT_NB_NF |
                                        SND_SOC_DAIFMT_CBS_CFS);
+       }
+
        if (err < 0) {
                dev_err(card->dev, "codec_dai fmt not set\n");
                return err;
@@ -428,23 +668,39 @@ static int tegra_aic326x_voice_call_hw_params(
                return err;
        }
 
-       if (params_rate(params) == 8000) {
-               /* Change these Settings for 8KHz*/
-               pcmdiv = 1;
-               /* BB expecting 2048Khz bclk */
-               vxclkdiv = 27;
-       } else if (params_rate(params) == 16000) {
-               pcmdiv = 1;
-               /* BB expecting 2048Khz bclk */
-               vxclkdiv = 27;
-       } else {
-               dev_err(card->dev, "codec_dai unsupported voice rate\n");
-               return -EINVAL;
+       if(!machine_is_tegra_enterprise()) {
+               if (params_rate(params) == 8000) {
+                       /* Change these Settings for 8KHz*/
+                       pcmdiv = 1;
+                       /* BB expecting 2048Khz bclk */
+                       vxclkdiv = 27;
+               } else if (params_rate(params) == 16000) {
+                       pcmdiv = 1;
+                       /* BB expecting 2048Khz bclk */
+                       vxclkdiv = 27;
+               } else {
+                       dev_err(card->dev, "codec_dai unsupported voice rate\n");
+                       return -EINVAL;
+               }
        }
 
        //snd_soc_dai_set_clkdiv(codec_dai, ASI2_BCLK_N, vxclkdiv);
        //snd_soc_dai_set_clkdiv(codec_dai, ASI2_WCLK_N, pcmdiv);
 
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       /* codec configuration */
+       machine->codec_info[HIFI_CODEC].rate = params_rate(params);
+       machine->codec_info[HIFI_CODEC].channels = params_channels(params);
+       machine->codec_info[HIFI_CODEC].bitsize = 16;
+       machine->codec_info[HIFI_CODEC].is_i2smaster = 1;
+       machine->codec_info[HIFI_CODEC].is_format_dsp = 0;
+
+       /* baseband configuration */
+       machine->codec_info[BASEBAND].bitsize = 16;
+       machine->codec_info[BASEBAND].is_i2smaster = 1;
+       machine->codec_info[BASEBAND].is_format_dsp = 1;
+#endif
+
        machine->is_device_bt = 0;
 
        return 0;
@@ -457,6 +713,11 @@ static void tegra_aic326x_voice_call_shutdown(
        struct tegra_aic326x *machine  =
                        snd_soc_card_get_drvdata(rtd->codec->card);
 
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       machine->codec_info[HIFI_CODEC].rate = 0;
+       machine->codec_info[HIFI_CODEC].channels = 0;
+#endif
+
        machine->is_device_bt = 0;
 }
 
@@ -489,6 +750,20 @@ static int tegra_aic326x_bt_voice_call_hw_params(
 
        tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1);
 
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       /* codec configuration */
+       machine->codec_info[BT_SCO].rate = params_rate(params);
+       machine->codec_info[BT_SCO].channels = params_channels(params);
+       machine->codec_info[BT_SCO].bitsize = 16;
+       machine->codec_info[BT_SCO].is_i2smaster = 1;
+       machine->codec_info[BT_SCO].is_format_dsp = 1;
+
+       /* baseband configuration */
+       machine->codec_info[BASEBAND].bitsize = 16;
+       machine->codec_info[BASEBAND].is_i2smaster = 1;
+       machine->codec_info[BASEBAND].is_format_dsp = 1;
+#endif
+
        machine->is_device_bt = 1;
 
        return 0;
@@ -501,12 +776,21 @@ static void tegra_aic326x_bt_voice_call_shutdown(
        struct tegra_aic326x *machine  =
                        snd_soc_card_get_drvdata(rtd->codec->card);
 
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       machine->codec_info[BT_SCO].rate = 0;
+       machine->codec_info[BT_SCO].channels = 0;
+#endif
+
        machine->is_device_bt = 0;
 }
 
 static struct snd_soc_ops tegra_aic326x_hifi_ops = {
        .hw_params = tegra_aic326x_hw_params,
        .hw_free = tegra_aic326x_hw_free,
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       .startup = tegra_aic326x_startup,
+       .shutdown = tegra_aic326x_shutdown,
+#endif
 };
 
 static struct snd_soc_ops tegra_aic326x_spdif_ops = {
@@ -529,6 +813,10 @@ static struct snd_soc_ops tegra_aic326x_bt_voice_call_ops = {
 static struct snd_soc_ops tegra_aic326x_bt_ops = {
        .hw_params = tegra_aic326x_bt_hw_params,
        .hw_free = tegra_aic326x_hw_free,
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       .startup = tegra_aic326x_startup,
+       .shutdown = tegra_aic326x_shutdown,
+#endif
 };
 
 static struct snd_soc_jack tegra_aic326x_hp_jack;
@@ -657,13 +945,25 @@ static int tegra_aic326x_init(struct snd_soc_pcm_runtime *rtd)
        struct snd_soc_card *card = codec->card;
        struct tegra_aic326x *machine = snd_soc_card_get_drvdata(card);
        struct tegra_aic326x_platform_data *pdata = machine->pdata;
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+#endif
        int ret;
 
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       if (machine->codec_info[BASEBAND].i2s_id != -1)
+               i2s->is_dam_used = true;
+#endif
+
        if (machine->init_done)
                return 0;
 
        machine->init_done = true;
 
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       machine->pcard = card;
+#endif
+
        if (machine_is_whistler()) {
                machine->audio_reg = regulator_get(NULL, "avddio_audio");
                if (IS_ERR(machine->audio_reg)) {
@@ -808,6 +1108,7 @@ static struct snd_soc_dai_link tegra_aic326x_dai[] = {
                .cpu_dai_name = "tegra30-i2s.3",
 #endif
                .codec_dai_name = "dit-hifi",
+               .init = tegra_aic326x_init,
                .ops = &tegra_aic326x_bt_ops,
                },
        [DAI_LINK_VOICE_CALL] = {
@@ -841,7 +1142,7 @@ static __devinit int tegra_aic326x_driver_probe(struct platform_device *pdev)
        struct snd_soc_card *card = &snd_soc_tegra_aic326x;
        struct tegra_aic326x *machine;
        struct tegra_aic326x_platform_data *pdata;
-       int ret;
+       int ret, i;
 
        pdata = pdev->dev.platform_data;
        if (!pdata) {
@@ -875,9 +1176,24 @@ static __devinit int tegra_aic326x_driver_probe(struct platform_device *pdev)
        }
 #endif
 
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       for (i = 0; i < NUM_I2S_DEVICES ; i++)
+               machine->codec_info[i].i2s_id = pdata->audio_port_id[i];
+
+       machine->codec_info[BASEBAND].rate = pdata->baseband_param.rate;
+       machine->codec_info[BASEBAND].channels = pdata->baseband_param.channels;
+
+       tegra_aic326x_dai[DAI_LINK_HIFI].cpu_dai_name =
+       tegra_i2s_dai_name[machine->codec_info[HIFI_CODEC].i2s_id];
+
+       tegra_aic326x_dai[DAI_LINK_BTSCO].cpu_dai_name =
+       tegra_i2s_dai_name[machine->codec_info[BT_SCO].i2s_id];
+#endif
+
        if(machine_is_tegra_enterprise()) {
                tegra_aic326x_dai[DAI_LINK_HIFI].codec_name = "aic3262-codec.0-0018";
                tegra_aic326x_dai[DAI_LINK_VOICE_CALL].codec_name = "aic3262-codec.0-0018";
+               tegra_aic326x_dai[DAI_LINK_VOICE_CALL].codec_dai_name = "aic3262-asi1";
        }
 
        ret = snd_soc_register_card(card);