From c8b140cfc9db43d43e9513df55e6d6813d0aaead Mon Sep 17 00:00:00 2001 From: Nikesh Oswal Date: Tue, 24 Jan 2012 12:19:32 +0530 Subject: asoc: tegra: wm8753: add support for voice call Bug: 919350 Reviewed-on: http://git-master/r/76953 Change-Id: I95611af5e6c12e03cc22fc30c8147e3ef6852615 Signed-off-by: Nikesh Oswal Signed-off-by: Varun Wadekar Reviewed-on: http://git-master/r/77731 Reviewed-by: Automatic_Commit_Validation_User --- sound/soc/tegra/tegra_wm8753.c | 191 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) (limited to 'sound/soc/tegra') diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c index 0b2a431d500e..1388a8d664bb 100644 --- a/sound/soc/tegra/tegra_wm8753.c +++ b/sound/soc/tegra/tegra_wm8753.c @@ -68,6 +68,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, @@ -277,6 +278,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; @@ -287,11 +380,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, @@ -514,6 +690,12 @@ static int tegra_wm8753_init(struct snd_soc_pcm_runtime *rtd) tegra_wm8753_hp_jack_pins); #endif + /* 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"); @@ -555,6 +737,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 = { -- cgit v1.2.3