asoc: tegra: wm8753: add support for voice call
Nikesh Oswal [Tue, 24 Jan 2012 06:49:32 +0000 (11:49 +0530)]
Bug: 919350

Reviewed-on: http://git-master/r/76953

Change-Id: I95611af5e6c12e03cc22fc30c8147e3ef6852615
Signed-off-by: Nikesh Oswal <noswal@nvidia.com>
Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
Reviewed-on: http://git-master/r/77731
Reviewed-by: Automatic_Commit_Validation_User

Rebase-Id: Re2081c2ba847e7e0969b34bbc7e4d9e46d0fe3c7

sound/soc/tegra/tegra_wm8753.c

index 8872fe9..2b93067 100644 (file)
@@ -65,6 +65,7 @@ struct tegra_wm8753 {
        struct tegra_wm8753_platform_data *pdata;
        struct regulator *audio_reg;
        int gpio_requested;
+       int is_call_mode;
 };
 
 static int tegra_wm8753_hw_params(struct snd_pcm_substream *substream,
@@ -274,6 +275,98 @@ static int tegra_spdif_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
+static int tegra_wm8753_voice_hw_params(struct snd_pcm_substream *substream,
+                                       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_codec *codec = rtd->codec;
+       struct snd_soc_card *card = codec->card;
+       struct tegra_wm8753 *machine = snd_soc_card_get_drvdata(card);
+       int srate, mclk, i2s_daifmt, sys_clk;
+       int err, pcmdiv, vxclkdiv;
+
+       srate = params_rate(params);
+       switch (srate) {
+       case 8000:
+       case 16000:
+       case 24000:
+       case 32000:
+       case 48000:
+       case 64000:
+       case 96000:
+               mclk = 12288000;
+               break;
+       case 11025:
+       case 22050:
+       case 44100:
+       case 88200:
+               mclk = 11289600;
+               break;
+       default:
+               mclk = 12000000;
+               break;
+       }
+
+       err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
+       if (err < 0) {
+               if (!(machine->util_data.set_mclk % mclk))
+                       mclk = machine->util_data.set_mclk;
+               else {
+                       dev_err(card->dev, "Can't configure clocks\n");
+                       return err;
+               }
+       }
+
+       tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1);
+
+       i2s_daifmt = SND_SOC_DAIFMT_NB_NF |
+                    SND_SOC_DAIFMT_CBM_CFM;
+
+       i2s_daifmt |= SND_SOC_DAIFMT_DSP_A;
+
+       err = snd_soc_dai_set_fmt(codec_dai, i2s_daifmt);
+       if (err < 0) {
+               dev_err(card->dev, "codec_dai fmt not set\n");
+               return err;
+       }
+
+       sys_clk = machine->util_data.set_mclk;
+
+       err = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, sys_clk,
+                                                       SND_SOC_CLOCK_IN);
+       if (err < 0) {
+               dev_err(card->dev, "codec_dai clock not set\n");
+               return err;
+       }
+
+       err = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,
+               sys_clk, 12288000);
+
+       if (err < 0) {
+               dev_err(card->dev, "codec_dai pll not set\n");
+               return err;
+       }
+
+       if (params_rate(params) == 8000) {
+               pcmdiv = WM8753_PCM_DIV_6;
+               /* BB expecting 2048Khz bclk */
+               vxclkdiv = WM8753_VXCLK_DIV_1;
+       } else if (params_rate(params) == 16000) {
+               pcmdiv = WM8753_PCM_DIV_3;
+               /* BB expecting 2048Khz bclk */
+               vxclkdiv = WM8753_VXCLK_DIV_2;
+       } else {
+               dev_err(card->dev, "codec_dai unsupported voice rate\n");
+               return -EINVAL;
+       }
+
+       snd_soc_dai_set_clkdiv(codec_dai, WM8753_VXCLKDIV, vxclkdiv);
+       snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
+
+       return 0;
+}
+
 static int tegra_hw_free(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -284,11 +377,94 @@ static int tegra_hw_free(struct snd_pcm_substream *substream)
        return 0;
 }
 
+static int tegra_wm8753_voice_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct tegra_wm8753 *machine = snd_soc_card_get_drvdata(rtd->card);
+
+       tegra_asoc_utils_lock_clk_rate(&machine->util_data, 0);
+
+       return 0;
+}
+
+static int tegra_call_mode_info(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+static int tegra_call_mode_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct tegra_wm8753 *machine = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = machine->is_call_mode;
+
+       return 0;
+}
+
+static int tegra_call_mode_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct tegra_wm8753 *machine = snd_kcontrol_chip(kcontrol);
+       int is_call_mode_new = ucontrol->value.integer.value[0];
+
+       if (machine->is_call_mode == is_call_mode_new)
+               return 0;
+
+       if (is_call_mode_new) {
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+               tegra20_das_set_tristate(TEGRA20_DAS_DAP_ID_2, 1);
+               tegra20_das_set_tristate(TEGRA20_DAS_DAP_ID_3, 1);
+               tegra20_das_connect_dap_to_dap(TEGRA20_DAS_DAP_ID_2,
+                       TEGRA20_DAS_DAP_SEL_DAP3, 1, 0, 0);
+               tegra20_das_connect_dap_to_dap(TEGRA20_DAS_DAP_ID_3,
+                       TEGRA20_DAS_DAP_SEL_DAP2, 0, 0, 0);
+               tegra20_das_set_tristate(TEGRA20_DAS_DAP_ID_2, 0);
+               tegra20_das_set_tristate(TEGRA20_DAS_DAP_ID_3, 0);
+#endif
+       } else {
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+               tegra20_das_set_tristate(TEGRA20_DAS_DAP_ID_2, 1);
+               tegra20_das_set_tristate(TEGRA20_DAS_DAP_ID_3, 1);
+               tegra20_das_connect_dap_to_dap(TEGRA20_DAS_DAP_ID_3,
+                       TEGRA20_DAS_DAP_SEL_DAP3, 0, 0, 0);
+               tegra20_das_connect_dap_to_dap(TEGRA20_DAS_DAP_ID_2,
+                       TEGRA20_DAS_DAP_SEL_DAP2, 0, 0, 0);
+               tegra20_das_set_tristate(TEGRA20_DAS_DAP_ID_2, 0);
+               tegra20_das_set_tristate(TEGRA20_DAS_DAP_ID_3, 0);
+#endif
+       }
+
+       machine->is_call_mode = is_call_mode_new;
+
+       return 1;
+}
+
+struct snd_kcontrol_new tegra_call_mode_control = {
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Call Mode Switch",
+       .private_value = 0xffff,
+       .info = tegra_call_mode_info,
+       .get = tegra_call_mode_get,
+       .put = tegra_call_mode_put
+};
+
 static struct snd_soc_ops tegra_wm8753_ops = {
        .hw_params = tegra_wm8753_hw_params,
        .hw_free = tegra_hw_free,
 };
 
+static struct snd_soc_ops tegra_wm8753_voice_ops = {
+       .hw_params = tegra_wm8753_voice_hw_params,
+       .hw_free = tegra_wm8753_voice_hw_free,
+};
+
 static struct snd_soc_ops tegra_bt_sco_ops = {
        .hw_params = tegra_bt_sco_hw_params,
        .hw_free = tegra_hw_free,
@@ -472,6 +648,12 @@ static int tegra_wm8753_init(struct snd_soc_pcm_runtime *rtd)
                ARRAY_SIZE(tegra_wm8753_hp_jack_pins),
                tegra_wm8753_hp_jack_pins);
 
+       /* Add call mode switch control */
+       ret = snd_ctl_add(codec->card->snd_card,
+                       snd_ctl_new1(&tegra_call_mode_control, machine));
+       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");
@@ -513,6 +695,15 @@ static struct snd_soc_dai_link tegra_wm8753_dai[] = {
                .ops = &tegra_bt_sco_ops,
        },
 #endif
+       {
+               .name = "VOICE CALL",
+               .stream_name = "VOICE CALL PCM",
+               .codec_name = "wm8753-codec.4-001a",
+               .platform_name = "tegra-pcm-audio",
+               .cpu_dai_name = "dit-hifi",
+               .codec_dai_name = "wm8753-voice",
+               .ops = &tegra_wm8753_voice_ops,
+       },
 };
 
 static struct snd_soc_card snd_soc_tegra_wm8753 = {