diff --git a/sources/hardware/nvidia/platform/t18x/quill/kernel-dts/tegra186-quill-p3310-1000-a00-00-base.dts b/sources/hardware/nvidia/platform/t18x/quill/kernel-dts/tegra186-quill-p3310-1000-a00-00-base.dts index 6d9b81b..ba63fc4 100644 --- a/sources/hardware/nvidia/platform/t18x/quill/kernel-dts/tegra186-quill-p3310-1000-a00-00-base.dts +++ b/sources/hardware/nvidia/platform/t18x/quill/kernel-dts/tegra186-quill-p3310-1000-a00-00-base.dts @@ -203,6 +203,15 @@ i2c@c240000 { clock-frequency = <400000>; +// added by Yen for rt5639 audio codec + b400tx2_rt5639: rt5639.1-001c@1c { + status = "okay"; + compatible = "realtek,rt5640"; + reg = <0x1c>; + realtek,ldo1-en-gpios = <&tegra_main_gpio TEGRA_MAIN_GPIO(J, 6) GPIO_ACTIVE_HIGH>; + sel_jd_source = <3>; + }; +//// }; cpus { @@ -270,6 +279,148 @@ }; //// +// added by Yen for rt5639 audio codec + tegra_sound: sound { + compatible = "nvidia,tegra-audio-t186ref-mobile-rt5639/40"; + nvidia,model = "tegra-snd-t186ref-mobile-rt5639_40"; + realtek,in1-differential = <1>; + sel_jd_source = <3>; + nvidia,num-codec-link = <5>; + nvidia,num-clk = <8>; + nvidia,hp-det-gpios = <&tegra_main_gpio TEGRA_MAIN_GPIO(J, 5) GPIO_ACTIVE_HIGH>; + nvidia,clk-rates = < 270950400 /* PLLA_x11025_RATE */ + 11289600 /* AUD_MCLK_x11025_RATE */ + 45158400 /* PLLA_OUT0_x11025_RATE */ + 45158400 /* AHUB_x11025_RATE */ + 245760000 /* PLLA_x8000_RATE */ + 12288000 /* AUD_MCLK_x8000_RATE */ + 49152000 /* PLLA_OUT0_x8000_RATE */ + 49152000 >;/* AHUB_x8000_RATE */ + clocks = <&tegra_car TEGRA186_CLK_PLLP_OUT0>, + <&tegra_car TEGRA186_CLK_PLLA>, + <&tegra_car TEGRA186_CLK_PLL_A_OUT0>, + <&tegra_car TEGRA186_CLK_AHUB>, + <&tegra_car TEGRA186_CLK_CLK_M>, + <&tegra_car TEGRA186_CLK_AUD_MCLK>; + clock-names = "pll_p_out1", "pll_a", "pll_a_out0", "ahub", + "clk_m", "extern1"; + resets = <&tegra_car TEGRA186_RESET_AUD_MCLK>; + reset-names = "extern1_rst"; + + status = "okay"; + nvidia,audio-routing = + "x Headphone Jack", "x HP L Playback", + "x Headphone Jack", "x HP R Playback", + "x Int Spk", "x SPORP", + "x Int Spk", "x SPORN", + "x Int Spk", "x SPOLP", + "x Int Spk", "x SPOLN", + "x IN2P", "x Mic Jack", + "x IN2P", "x Mic Jack", + "x DMIC L1", "x Int Mic", + "x DMIC L2", "x Int Mic", + "x DMIC R1", "x Int Mic", + "x DMIC R2", "x Int Mic", + "y Headphone", "y OUT", + "y IN", "y Mic", + "z Headphone", "z OUT", + "z IN", "z Mic"; + + nvidia,xbar = <&tegra_axbar>; + + rt5640_dai_link: nvidia,dai-link-1 { + link-name = "rt5640-playback"; + cpu-dai = <&tegra_i2s1>; + codec-dai = <&b400tx2_rt5639>; + /* codec-dai = <&spdif_dit0>; */ + cpu-dai-name = "I2S1"; + codec-dai-name = "rt5640-aif1"; + /* codec-dai-name = "dit-hifi"; */ + format = "i2s"; + bitclock-slave; + frame-slave; + bitclock-noninversion; + frame-noninversion; + bit-format = "s16_le"; + bclk_ratio = <0>; + srate = <48000>; + num-channel = <2>; + ignore_suspend; + name-prefix = "x"; + status = "okay"; + }; + + nvidia,dai-link-2 { + link-name = "spdif-dit-1"; + cpu-dai = <&tegra_i2s2>; + codec-dai = <&spdif_dit1>; + cpu-dai-name = "I2S2"; + codec-dai-name = "dit-hifi"; + format = "dsp_a"; + bitclock-slave; + frame-slave; + bitclock-inversion; + frame-inversion; + bit-format = "s16_le"; + bclk_ratio = <4>; + srate = <8000>; + num-channel = <1>; + ignore_suspend; + name-prefix = "y"; + status = "okay"; + }; + + nvidia,dai-link-3 { + link-name = "spdif-dit-2"; + cpu-dai = <&tegra_i2s3>; + codec-dai = <&spdif_dit2>; + cpu-dai-name = "I2S3"; + codec-dai-name = "dit-hifi"; + format = "i2s"; + bitclock-slave; + frame-slave; + bitclock-noninversion; + frame-noninversion; + bit-format = "s16_le"; + bclk_ratio = <1>; + srate = <48000>; + num-channel = <2>; + ignore_suspend; + name-prefix = "z"; + status = "okay"; + }; + + dspk_dai_link_1: nvidia,dai-link-4 { + link-name = "dspk-playback-r"; + cpu-dai = <&tegra_dspk1>; + codec-dai = <&spdif_dit3>; + cpu-dai-name = "DSPK1"; + codec-dai-name = "dit-hifi"; + format = "i2s"; + bit-format = "s16_le"; + srate = <48000>; + num-channel = <2>; + ignore_suspend; + name-prefix = "d1"; + status = "okay"; + }; + dspk_dai_link_2: nvidia,dai-link-5 { + link-name = "dspk-playback-l"; + cpu-dai = <&tegra_dspk2>; + codec-dai = <&spdif_dit4>; + cpu-dai-name = "DSPK2"; + codec-dai-name = "dit-hifi"; + format = "i2s"; + bit-format = "s16_le"; + srate = <48000>; + num-channel = <2>; + ignore_suspend; + name-prefix = "d2"; + status = "okay"; + }; + }; +//// + mttcan@c310000 { status = "okay"; gpio_can_stb = <&tegra_aon_gpio TEGRA_AON_GPIO(AA, 0) GPIO_ACTIVE_HIGH>; diff --git a/sources/kernel/kernel-4.4/arch/arm64/configs/tegra18_defconfig b/sources/kernel/kernel-4.4/arch/arm64/configs/tegra18_defconfig index 2c09bed..c5086dc 100644 --- a/sources/kernel/kernel-4.4/arch/arm64/configs/tegra18_defconfig +++ b/sources/kernel/kernel-4.4/arch/arm64/configs/tegra18_defconfig @@ -856,3 +856,12 @@ CONFIG_CRYPTO_GHASH_ARM64_CE=y CONFIG_CRYPTO_AES_ARM64_CE_CCM=y CONFIG_CRYPTO_AES_ARM64_CE_BLK=y CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y +# added by Yen for rt5639 audio codec +CONFIG_SND_SOC_TEGRA210_ADSP_ALT=y +CONFIG_SND_SOC_RL6231=y +CONFIG_SND_SOC_RT5640=y +CONFIG_SND_SOC_TEGRA_T186REF_MOBILE_ALT_RT5639=y +CONFIG_TEGRA_NVADSP=y +CONFIG_TEGRA_ADSP_CONSOLE=y +CONFIG_TEGRA_NVADSP_ON_SMMU=y +#### diff --git a/sources/kernel/kernel-4.4/sound/soc/codecs/rt5640.c b/sources/kernel/kernel-4.4/sound/soc/codecs/rt5640.c index dbc6a89..06193bf 100644 --- a/sources/kernel/kernel-4.4/sound/soc/codecs/rt5640.c +++ b/sources/kernel/kernel-4.4/sound/soc/codecs/rt5640.c @@ -133,8 +133,10 @@ static const struct reg_default rt5640_reg[] = { { 0x8b, 0x0600 }, { 0x8c, 0x0228 }, { 0x8d, 0xa000 }, - { 0x8e, 0x0004 }, - { 0x8f, 0x1100 }, +// { 0x8e, 0x0004 }, + { 0x8e, 0x0005 }, +// { 0x8f, 0x1100 }, + { 0x8f, 0x1140 }, { 0x90, 0x0646 }, { 0x91, 0x0c00 }, { 0x92, 0x0000 }, @@ -930,8 +932,10 @@ static void hp_amp_power_on(struct snd_soc_codec *codec) /* depop parameters */ regmap_update_bits(rt5640->regmap, RT5640_PR_BASE + RT5640_CHPUMP_INT_REG1, 0x0700, 0x0200); - regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M2, - RT5640_DEPOP_MASK, RT5640_DEPOP_MAN); +// commented out for fixing hearing microphone's sound from headphone + //regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M2, + // RT5640_DEPOP_MASK, RT5640_DEPOP_MAN); +//// regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M1, RT5640_HP_CP_MASK | RT5640_HP_SG_MASK | RT5640_HP_CB_MASK, RT5640_HP_CP_PU | RT5640_HP_SG_DIS | RT5640_HP_CB_PU); @@ -1959,6 +1963,10 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_STANDBY: if (SND_SOC_BIAS_OFF == snd_soc_codec_get_bias_level(codec)) { +//added by Yen for microphone + snd_soc_update_bits(codec, RT5640_PWR_ANLG2, + RT5640_PWR_MB1, RT5640_PWR_MB1); +//// snd_soc_update_bits(codec, RT5640_PWR_ANLG1, RT5640_PWR_VREF1 | RT5640_PWR_MB | RT5640_PWR_BG | RT5640_PWR_VREF2, @@ -1972,12 +1980,18 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec, 0x0301, 0x0301); snd_soc_update_bits(codec, RT5640_MICBIAS, 0x0030, 0x0030); +// + snd_soc_write(codec, RT5640_DEPOP_M1, 0x0005); + snd_soc_write(codec, RT5640_DEPOP_M2, 0x1140); +//// } break; case SND_SOC_BIAS_OFF: - snd_soc_write(codec, RT5640_DEPOP_M1, 0x0004); - snd_soc_write(codec, RT5640_DEPOP_M2, 0x1100); +// snd_soc_write(codec, RT5640_DEPOP_M1, 0x0004); +// snd_soc_write(codec, RT5640_DEPOP_M2, 0x1100); + snd_soc_write(codec, RT5640_DEPOP_M1, 0x0005); + snd_soc_write(codec, RT5640_DEPOP_M2, 0x1140); snd_soc_update_bits(codec, RT5640_DUMMY1, 0x1, 0); snd_soc_write(codec, RT5640_PWR_DIG1, 0x0000); snd_soc_write(codec, RT5640_PWR_DIG2, 0x0000); diff --git a/sources/kernel/t18x/sound/soc/tegra-alt/Kconfig.t18x b/sources/kernel/t18x/sound/soc/tegra-alt/Kconfig.t18x index 73fcb0d..759eadd 100644 --- a/sources/kernel/t18x/sound/soc/tegra-alt/Kconfig.t18x +++ b/sources/kernel/t18x/sound/soc/tegra-alt/Kconfig.t18x @@ -110,6 +110,36 @@ config SND_SOC_TEGRA_T186REF_MOBILE_ALT help Say Y or M here. +config SND_SOC_TEGRA_T186REF_MOBILE_ALT_RT5639 + tristate "SoC Audio support for T186Ref Mobile RT5639/40" + depends on SND_SOC_TEGRA_ALT + depends on ARCH_TEGRA_18x_SOC + select SND_SOC_TEGRA186_XBAR_ALT + select SND_SOC_TEGRA210_PCM_ALT + select SND_SOC_TEGRA210_ADMA_ALT + select SND_SOC_TEGRA210_ADMAIF_ALT + select SND_SOC_TEGRA210_I2S_ALT + select SND_SOC_TEGRA210_DMIC_ALT + select SND_SOC_TEGRA210_AMX_ALT + select SND_SOC_TEGRA210_ADX_ALT + select SND_SOC_TEGRA210_MIXER_ALT + select SND_SOC_TEGRA210_SFC_ALT + select SND_SOC_TEGRA186_AFC_ALT + select SND_SOC_TEGRA210_MVC_ALT + select SND_SOC_TEGRA210_OPE_ALT + select SND_SOC_TEGRA210_SPDIF_ALT + select SND_SOC_TEGRA210_ADSP_ALT if TEGRA_NVADSP + select SND_SOC_TEGRA186_ASRC_ALT + select SND_SOC_TEGRA186_ARAD_ALT + select SND_SOC_TEGRA186_DSPK_ALT + select SND_SOC_TEGRA_ASOC_HWDEP_ALT + select SND_SOC_SPDIF + select SND_SOC_RT5640 + select SND_SOC_COMPRESS + select SND_SOC_TEGRA_ASOC_MACHINE_T18X_ALT + help + Say Y or M here. + config SND_SOC_TEGRA_T186REF_P4573_ALT tristate "SoC Audio support for P4573 with CS53L30 and RT5658" depends on SND_SOC_TEGRA_T186REF_ALT diff --git a/sources/kernel/t18x/sound/soc/tegra-alt/Makefile.t18x b/sources/kernel/t18x/sound/soc/tegra-alt/Makefile.t18x index b54b9dc..a905f8e 100644 --- a/sources/kernel/t18x/sound/soc/tegra-alt/Makefile.t18x +++ b/sources/kernel/t18x/sound/soc/tegra-alt/Makefile.t18x @@ -25,6 +25,9 @@ obj-$(CONFIG_SND_SOC_TEGRA_ASOC_MACHINE_T18X_ALT) += snd-soc-tegra-asoc-machine- snd-soc-tegra-alt-t186ref-objs := tegra_t186ref_alt.o snd-soc-tegra-alt-t186ref-mobile-objs := tegra_t186ref_mobile_rt565x.o +# added by Yen for rt5639 audio codec +snd-soc-tegra-alt-t186ref-mobile-rt5639-objs := tegra_t186ref_mobile_alt.o +#### snd-soc-tegra-alt-t186ref-p2382-objs := \ tegra_t186ref_p2382_alt.o snd-soc-tegra-alt-t186ref-bali-objs := \ @@ -38,3 +41,6 @@ obj-$(CONFIG_SND_SOC_TEGRA_T186REF_AUTO_ALT) += snd-soc-tegra-alt-t186ref-p2382. obj-$(CONFIG_SND_SOC_TEGRA_T186REF_AUTO_ALT) += snd-soc-tegra-alt-t186ref-bali.o obj-$(CONFIG_SND_SOC_TEGRA_T186REF_P4573_ALT) += snd-soc-tegra-alt-t186ref-p4573.o obj-$(CONFIG_SND_SOC_TEGRA_T186REF_M3420_ALT) += snd-soc-tegra-alt-t186ref-m3420.o +# added by Yen for rt5639 audio codec +obj-$(CONFIG_SND_SOC_TEGRA_T186REF_MOBILE_ALT_RT5639) += snd-soc-tegra-alt-t186ref-mobile-rt5639.o +#### diff --git a/sources/kernel/t18x/sound/soc/tegra-alt/tegra_t186ref_mobile_alt.c b/sources/kernel/t18x/sound/soc/tegra-alt/tegra_t186ref_mobile_alt.c new file mode 100644 index 0000000..098739c --- /dev/null +++ b/sources/kernel/t18x/sound/soc/tegra-alt/tegra_t186ref_mobile_alt.c @@ -0,0 +1,1209 @@ +/* + * tegra_t186ref_mobile_alt.c - Tegra t186ref Machine driver + * + * Copyright (c) 2015-2016 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SWITCH +#include +#endif +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) +#include +#else +#include +#include +#endif + +#include +#include +#include +#include +#include +#include "../codecs/rt5640.h" + +#include "tegra_asoc_utils_alt.h" +#include "tegra_asoc_machine_alt.h" +#include "tegra_asoc_machine_alt_t18x.h" +#include "tegra210_xbar_alt.h" +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) +#include +#endif + +#define DRV_NAME "tegra-snd-t186ref-mobile-rt5639/40" + +#define GPIO_SPKR_EN BIT(0) +#define GPIO_HP_MUTE BIT(1) +#define GPIO_INT_MIC_EN BIT(2) +#define GPIO_EXT_MIC_EN BIT(3) +#define GPIO_HP_DET BIT(4) + +#define PARAMS(sformat, channels) \ + { \ + .formats = sformat, \ + .rate_min = 48000, \ + .rate_max = 48000, \ + .channels_min = channels, \ + .channels_max = channels, \ + } + +struct tegra_t186ref { + struct tegra_asoc_platform_data *pdata; + struct tegra_asoc_audio_clock_info audio_clock; + unsigned int num_codec_links; + int gpio_requested; +#ifdef CONFIG_SWITCH + int jack_status; +#endif + enum snd_soc_bias_level bias_level; + int clock_enabled; + struct regulator *codec_reg; + struct regulator *digital_reg; + struct regulator *analog_reg; + struct regulator *spk_reg; + struct regulator *dmic_reg; + struct snd_soc_card *pcard; + int rate_via_kcontrol; + int is_codec_dummy; + int fmt_via_kcontrol; +}; + +static const int tegra_t186ref_srate_values[] = { + 0, + 8000, + 16000, + 44100, + 48000, + 11025, + 22050, + 24000, + 32000, + 88200, + 96000, + 176000, + 192000, +}; + +static struct snd_soc_jack tegra_t186ref_hp_jack; + +static struct snd_soc_jack_gpio tegra_t186ref_hp_jack_gpio = { + .name = "headphone detect", + .report = SND_JACK_HEADPHONE, + .debounce_time = 150, + .invert = 1, +}; + +#ifdef CONFIG_SWITCH +static struct switch_dev tegra_t186ref_headset_switch = { + .name = "h2w", +}; + +static int tegra_t186ref_jack_notifier(struct notifier_block *self, + unsigned long action, void *dev) +{ + struct snd_soc_jack *jack = dev; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) + struct snd_soc_codec *codec = jack->codec; + struct snd_soc_card *card = codec->component.card; +#else + struct snd_soc_card *card = jack->card; +#endif + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); + enum headset_state state = BIT_NO_HEADSET; + int idx = 0; + static bool button_pressed = false; + // Tonie port RT5639 audio codec on 20171023 + unsigned char status_jack = 0; + struct snd_soc_codec *pcodec; + struct tegra_asoc_platform_data *pdata = machine->pdata; + /////////////////////////////// + + if (machine->is_codec_dummy) + return NOTIFY_OK; + + idx = tegra_machine_get_codec_dai_link_idx_t18x("rt5640-playback"); + /* check if idx has valid number */ + if (idx == -EINVAL) + return idx; + + dev_dbg(card->dev, "jack status = %d", jack->status); + if (jack->status & (SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3)) { + button_pressed = true; + return NOTIFY_OK; + } else if ((jack->status & SND_JACK_HEADSET) && button_pressed) { + button_pressed = false; + return NOTIFY_OK; + } + + // Tonie port RT5639 audio codec on 20171023 + pcodec = card->rtd[idx].codec; + + if (jack == &tegra_t186ref_hp_jack) { + if (action) { + /* Enable ext mic; enable signal is active-low */ + if (gpio_is_valid(pdata->gpio_ext_mic_en)) + gpio_direction_output( + pdata->gpio_ext_mic_en, 0); + + status_jack = rt5640_headset_detect(pcodec, jack, 1); + + jack->status &= ~SND_JACK_HEADPHONE; + jack->status &= ~SND_JACK_MICROPHONE; + jack->status &= ~SND_JACK_HEADSET; + + if (status_jack == RT5640_HEADPHO_DET) { + jack->status |= SND_JACK_HEADPHONE; + } else if (status_jack == RT5640_HEADSET_DET) { + jack->status |= SND_JACK_HEADPHONE; + jack->status |= SND_JACK_MICROPHONE; + } + } else { + /* Disable ext mic; enable signal is active-low*/ + if (gpio_is_valid(pdata->gpio_ext_mic_en)) + gpio_direction_output( + pdata->gpio_ext_mic_en, 1); + + rt5640_headset_detect(pcodec, jack, 0); + + jack->status &= ~SND_JACK_HEADPHONE; + jack->status &= ~SND_JACK_MICROPHONE; + } + } + /////////////////////////////////////////////////////////////// + + switch (jack->status) { + case SND_JACK_HEADPHONE: + state = BIT_HEADSET_NO_MIC; + break; + case SND_JACK_HEADSET: + state = BIT_HEADSET; + break; + case SND_JACK_MICROPHONE: + /* mic: would not report */ + default: + state = BIT_NO_HEADSET; + } + + dev_dbg(card->dev, "switch state to %x\n", state); + switch_set_state(&tegra_t186ref_headset_switch, state); + return NOTIFY_OK; +} + +static struct notifier_block tegra_t186ref_jack_detect_nb = { + .notifier_call = tegra_t186ref_jack_notifier, +}; +#else +static struct snd_soc_jack_pin tegra_t186ref_hp_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; +#endif + +static struct snd_soc_pcm_stream tegra_t186ref_asrc_link_params[] = { + PARAMS(SNDRV_PCM_FMTBIT_S32_LE, 8), + PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2), + PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2), + PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2), + PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2), + PARAMS(SNDRV_PCM_FMTBIT_S16_LE, 2), +}; + +static int tegra_t186ref_set_params(struct snd_soc_card *card, + struct tegra_t186ref *machine, + int rate, + int channels, + u64 formats) +{ + unsigned int tx_mask = (1 << channels) - 1; + unsigned int rx_mask = (1 << channels) - 1; + int idx, err = 0; + u64 format_k; + int num_of_dai_links = TEGRA186_XBAR_DAI_LINKS + + machine->num_codec_links; + + format_k = (machine->fmt_via_kcontrol == 2) ? + (1ULL << SNDRV_PCM_FORMAT_S32_LE) : formats; + + /* update dai link hw_params */ + for (idx = 0; idx < num_of_dai_links; idx++) { + if (card->rtd[idx].dai_link->params) { + struct snd_soc_pcm_stream *dai_params; + + dai_params = + (struct snd_soc_pcm_stream *) + card->rtd[idx].dai_link->params; + + dai_params->rate_min = rate; + dai_params->channels_min = channels; + dai_params->formats = format_k; + + if (idx >= TEGRA186_XBAR_DAI_LINKS) { + unsigned int fmt; + int bclk_ratio; + + err = 0; + dai_params->formats = formats; + + fmt = card->rtd[idx].dai_link->dai_fmt; + bclk_ratio = + tegra_machine_get_bclk_ratio_t18x( + &card->rtd[idx]); + + if (bclk_ratio >= 0) { + err = snd_soc_dai_set_bclk_ratio( + card->rtd[idx].cpu_dai, + bclk_ratio); + } + + if (err < 0) { + dev_err(card->dev, + "Failed to set cpu dai bclk ratio for %s\n", + card->rtd[idx].dai_link->name); + } + + /* set TDM slot mask */ + if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == + SND_SOC_DAIFMT_DSP_A) { + err = snd_soc_dai_set_tdm_slot( + card->rtd[idx].cpu_dai, + tx_mask, rx_mask, 0, 0); + if (err < 0) { + dev_err(card->dev, + "%s cpu DAI slot mask not set\n", + card->rtd[idx].cpu_dai->name); + } + } + } + } + } + return 0; +} + +static int tegra_t186ref_dai_init(struct snd_soc_pcm_runtime *rtd, + int rate, + int channels, + u64 formats, + bool is_playback) +{ + struct snd_soc_card *card = rtd->card; + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); + struct snd_soc_pcm_stream *dai_params; + unsigned int idx, clk_out_rate; + int err, codec_rate, clk_rate; + + codec_rate = tegra_t186ref_srate_values[machine->rate_via_kcontrol]; + clk_rate = (machine->rate_via_kcontrol) ? codec_rate : rate; + + err = tegra_alt_asoc_utils_set_rate(&machine->audio_clock, clk_rate, + 0, 0); + if (err < 0) { + dev_err(card->dev, "Can't configure clocks\n"); + return err; + } + + clk_out_rate = machine->audio_clock.clk_out_rate; + + pr_info("pll_a_out0 = %d Hz, aud_mclk = %d Hz, codec rate = %d Hz\n", + machine->audio_clock.set_mclk, clk_out_rate, clk_rate); + + tegra_t186ref_set_params(card, machine, rate, channels, formats); + + idx = tegra_machine_get_codec_dai_link_idx_t18x("rt5640-playback"); + /* check if idx has valid number */ + if (idx != -EINVAL) { + dai_params = + (struct snd_soc_pcm_stream *)card->rtd[idx].dai_link->params; + + /*dai_params->rate_min = clk_rate; + dai_params->formats = (machine->fmt_via_kcontrol == 2) ? + (1ULL << SNDRV_PCM_FORMAT_S32_LE) : formats;*/ + /* update link_param to update hw_param for DAPM */ + dai_params->rate_min = (machine->rate_via_kcontrol) ? + codec_rate : rate; + dai_params->channels_min = channels; + dai_params->formats = formats; + + if (!machine->is_codec_dummy) { + err = snd_soc_dai_set_sysclk(card->rtd[idx].codec_dai, + RT5640_SCLK_S_MCLK, clk_out_rate, SND_SOC_CLOCK_IN); + + if (err < 0) { + dev_err(card->dev, "rt5640-playback codec_dai clock not set\n"); + return err; + } + } + } + + idx = tegra_machine_get_codec_dai_link_idx("spdif-dit-1"); + if (idx != -EINVAL) { + dai_params = + (struct snd_soc_pcm_stream *)card->rtd[idx].dai_link->params; + + /* update link_param to update hw_param for DAPM */ + dai_params->rate_min = rate; + dai_params->channels_min = channels; + dai_params->formats = formats; + + err = snd_soc_dai_set_bclk_ratio(card->rtd[idx].cpu_dai, + tegra_machine_get_bclk_ratio(&card->rtd[idx])); + if (err < 0) { + dev_err(card->dev, "Can't set cpu dai bclk ratio\n"); + return err; + } + + err = snd_soc_dai_set_tdm_slot(card->rtd[idx].cpu_dai, + (1 << channels) - 1, (1 << channels) - 1, 0, 0); + if (err < 0) { + dev_err(card->dev, "Can't set cpu dai slot ctrl\n"); + return err; + } + } + + idx = tegra_machine_get_codec_dai_link_idx("spdif-dit-2"); + if (idx != -EINVAL) { + dai_params = + (struct snd_soc_pcm_stream *)card->rtd[idx].dai_link->params; + + /* update link_param to update hw_param for DAPM */ + dai_params->rate_min = rate; + dai_params->channels_min = channels; + dai_params->formats = formats; + } + + idx = tegra_machine_get_codec_dai_link_idx("spdif-dit-3"); + if (idx != -EINVAL) { + dai_params = + (struct snd_soc_pcm_stream *)card->rtd[idx].dai_link->params; + + /* update link_param to update hw_param for DAPM */ + dai_params->rate_min = (machine->rate_via_kcontrol) ? + codec_rate : rate; + } + + return 0; +} + +static int tegra_t186ref_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_card *card = rtd->card; + int err; + bool is_playback; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + is_playback = true; + else + is_playback = false; + + err = tegra_t186ref_dai_init(rtd, params_rate(params), + params_channels(params), + (1ULL << (params_format(params))), + is_playback); + if (err < 0) { + dev_err(card->dev, "Failed dai init\n"); + return err; + } + + return 0; +} + +static int tegra_t186ref_compr_startup(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_card *card = rtd->card; + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); + + tegra_alt_asoc_utils_clk_enable(&machine->audio_clock); + + return 0; +} + +static void tegra_t186ref_compr_shutdown(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_card *card = rtd->card; + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); + + tegra_alt_asoc_utils_clk_disable(&machine->audio_clock); + + return; +} +static int tegra_t186ref_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(rtd->card); + + tegra_alt_asoc_utils_clk_enable(&machine->audio_clock); + + return 0; +} + +static void tegra_t186ref_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(rtd->card); + + tegra_alt_asoc_utils_clk_disable(&machine->audio_clock); + + return; +} + +static int tegra_t186ref_dspk_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct snd_soc_dapm_context *dapm = &card->dapm; + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); + int err; + + err = tegra_alt_asoc_utils_set_extern_parent(&machine->audio_clock, + "pll_a_out0"); + if (err < 0) + dev_err(card->dev, "Failed to set extern clk parent\n"); + + snd_soc_dapm_sync(dapm); + return err; +} + +static int tegra_t186ref_compr_set_params(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_card *card = rtd->card; + struct snd_soc_platform *platform = rtd->platform; + struct snd_codec codec_params; + int err; + bool is_playback; + + if (platform->driver->compr_ops && + platform->driver->compr_ops->get_params) { + err = platform->driver->compr_ops->get_params(cstream, + &codec_params); + if (err < 0) { + dev_err(card->dev, "Failed to get compr params\n"); + return err; + } + } else { + dev_err(card->dev, "compr ops not set\n"); + return -EINVAL; + } + + if (cstream->direction == SND_COMPRESS_PLAYBACK) + is_playback = true; + else + is_playback = false; + + err = tegra_t186ref_dai_init(rtd, codec_params.sample_rate, + codec_params.ch_out, SNDRV_PCM_FMTBIT_S16_LE, + is_playback); + if (err < 0) { + dev_err(card->dev, "Failed dai init\n"); + return err; + } + + return 0; +} + +static int tegra_t186ref_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); + struct tegra_asoc_platform_data *pdata = machine->pdata; + int err; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_codec *codec = codec_dai->codec; +#endif + + err = tegra_alt_asoc_utils_set_extern_parent(&machine->audio_clock, + "pll_a_out0"); + if (err < 0) { + dev_err(card->dev, "Failed to set extern clk parent\n"); + return err; + } + + tegra_t186ref_hp_jack_gpio.gpio = pdata->gpio_hp_det; + tegra_t186ref_hp_jack_gpio.invert = + !pdata->gpio_hp_det_active_high; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) + snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, + &tegra_t186ref_hp_jack); + +#else + err = snd_soc_card_jack_new(card, "Headphone Jack", SND_JACK_HEADPHONE, + &tegra_t186ref_hp_jack, NULL, 0); + if (err) { + dev_err(card->dev, "Headset Jack creation failed %d\n", err); + return err; + } +#endif + +#ifndef CONFIG_SWITCH + err = snd_soc_jack_add_pins(&tegra_t186ref_hp_jack, + ARRAY_SIZE(tegra_t186ref_hp_jack_pins), + tegra_t186ref_hp_jack_pins); + if (err) { + dev_err(card->dev, "snd_soc_jack_add_pins failed %d\n", err); + return err; + } +#else + snd_soc_jack_notifier_register(&tegra_t186ref_hp_jack, + &tegra_t186ref_jack_detect_nb); +#endif + + if (gpio_is_valid(pdata->gpio_hp_det)) { + dev_dbg(card->dev, "associate the gpio to jack\n"); + snd_soc_jack_add_gpios(&tegra_t186ref_hp_jack, + 1, &tegra_t186ref_hp_jack_gpio); + } + + /* single button supporting play/pause */ + snd_jack_set_key(tegra_t186ref_hp_jack.jack, + SND_JACK_BTN_0, KEY_MEDIA); + + /* multiple buttons supporting play/pause and volume up/down */ + snd_jack_set_key(tegra_t186ref_hp_jack.jack, + SND_JACK_BTN_1, KEY_MEDIA); + snd_jack_set_key(tegra_t186ref_hp_jack.jack, + SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(tegra_t186ref_hp_jack.jack, + SND_JACK_BTN_3, KEY_VOLUMEDOWN); + + snd_soc_dapm_sync(&card->dapm); + + return 0; +} + +static int tegra_t186ref_sfc_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = rtd->codec_dai; + unsigned int in_srate, out_srate; + int err; + + in_srate = 48000; + out_srate = 8000; + + err = snd_soc_dai_set_sysclk(codec_dai, 0, out_srate, + SND_SOC_CLOCK_OUT); + err = snd_soc_dai_set_sysclk(codec_dai, 0, in_srate, + SND_SOC_CLOCK_IN); + + return err; +} + + +static int tegra_rt5639_event_int_spk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); + struct tegra_asoc_platform_data *pdata = machine->pdata; + int err; + + if (machine->spk_reg) { + if (SND_SOC_DAPM_EVENT_ON(event)) + err = regulator_enable(machine->spk_reg); + else + regulator_disable(machine->spk_reg); + } + + if (!(machine->gpio_requested & GPIO_SPKR_EN)) + return 0; + + gpio_set_value_cansleep(pdata->gpio_spkr_en, + !!SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static int tegra_rt5639_event_hp(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); + struct tegra_asoc_platform_data *pdata = machine->pdata; + + if (!(machine->gpio_requested & GPIO_HP_MUTE)) + return 0; + + gpio_set_value_cansleep(pdata->gpio_hp_mute, + !SND_SOC_DAPM_EVENT_ON(event)); + return 0; +} + +static int tegra_rt5639_event_int_mic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); + struct tegra_asoc_platform_data *pdata = machine->pdata; + int ret = 0; + + if (machine->dmic_reg) { + if (SND_SOC_DAPM_EVENT_ON(event)) + ret = regulator_enable(machine->dmic_reg); + else + regulator_disable(machine->dmic_reg); + } + + if (!(machine->gpio_requested & GPIO_INT_MIC_EN)) + return 0; + + gpio_set_value_cansleep(pdata->gpio_int_mic_en, + !!SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static int tegra_rt5639_event_ext_mic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); + struct tegra_asoc_platform_data *pdata = machine->pdata; + + if (!(machine->gpio_requested & GPIO_EXT_MIC_EN)) + return 0; + + gpio_set_value_cansleep(pdata->gpio_ext_mic_en, + !SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static struct snd_soc_ops tegra_t186ref_ops = { + .hw_params = tegra_t186ref_hw_params, + .startup = tegra_t186ref_startup, + .shutdown = tegra_t186ref_shutdown, +}; + +static struct snd_soc_compr_ops tegra_t186ref_compr_ops = { + .set_params = tegra_t186ref_compr_set_params, + .startup = tegra_t186ref_compr_startup, + .shutdown = tegra_t186ref_compr_shutdown, +}; + +static const struct snd_soc_dapm_widget tegra_t186ref_dapm_widgets[] = { + SND_SOC_DAPM_HP("x Headphone Jack", tegra_rt5639_event_hp), + SND_SOC_DAPM_SPK("x Int Spk", tegra_rt5639_event_int_spk), + SND_SOC_DAPM_HP("y Headphone", NULL), + SND_SOC_DAPM_HP("z Headphone", NULL), + SND_SOC_DAPM_HP("s Headphone", NULL), + SND_SOC_DAPM_MIC("x Int Mic", tegra_rt5639_event_int_mic), + SND_SOC_DAPM_MIC("x Mic Jack", tegra_rt5639_event_ext_mic), + SND_SOC_DAPM_MIC("y Mic", NULL), + SND_SOC_DAPM_MIC("z Mic", NULL), + SND_SOC_DAPM_MIC("s Mic", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), +}; + +static int tegra_t186ref_suspend_pre(struct snd_soc_card *card) +{ + struct snd_soc_jack_gpio *gpio = &tegra_t186ref_hp_jack_gpio; + unsigned int idx; + + /* DAPM dai link stream work for non pcm links */ + for (idx = 0; idx < card->num_rtd; idx++) { + if (card->rtd[idx].dai_link->params) + INIT_DELAYED_WORK(&card->rtd[idx].delayed_work, NULL); + } + + if (gpio_is_valid(gpio->gpio)) + disable_irq(gpio_to_irq(gpio->gpio)); + + return 0; +} + +static int tegra_t186ref_suspend_post(struct snd_soc_card *card) +{ + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); + + if (machine->clock_enabled) { + machine->clock_enabled = 0; + tegra_alt_asoc_utils_clk_disable(&machine->audio_clock); + } + + if (machine->digital_reg) + regulator_disable(machine->digital_reg); + + return 0; +} + +static int tegra_t186ref_resume_pre(struct snd_soc_card *card) +{ + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); + struct snd_soc_jack_gpio *gpio = &tegra_t186ref_hp_jack_gpio; + int ret, val; + + if (machine->digital_reg) + ret = regulator_enable(machine->digital_reg); + + if (gpio_is_valid(gpio->gpio)) { + val = gpio_get_value(gpio->gpio); + val = gpio->invert ? !val : val; + if (gpio->jack) + snd_soc_jack_report(gpio->jack, val, gpio->report); + enable_irq(gpio_to_irq(gpio->gpio)); + } + + if (!machine->clock_enabled) { + machine->clock_enabled = 1; + tegra_alt_asoc_utils_clk_enable(&machine->audio_clock); + } + return 0; +} + +static int tegra_t186ref_remove(struct snd_soc_card *card) +{ + return 0; +} + +static const char * const tegra_t186ref_srate_text[] = { + "None", + "8kHz", + "16kHz", + "44kHz", + "48kHz", + "11kHz", + "22kHz", + "24kHz", + "32kHz", + "88kHz", + "96kHz", + "176kHz", + "192kHz", +}; + +static int tegra_t186ref_codec_get_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); + + ucontrol->value.integer.value[0] = machine->rate_via_kcontrol; + + return 0; +} + +static int tegra_t186ref_codec_put_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); + + /* set the rate control flag */ + machine->rate_via_kcontrol = ucontrol->value.integer.value[0]; + + return 0; +} + +static const char * const tegra_t186ref_format_text[] = { + "None", + "16", + "32", +}; + +static int tegra_t186ref_codec_get_format(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); + + ucontrol->value.integer.value[0] = machine->fmt_via_kcontrol; + + return 0; +} + +static int tegra_t186ref_codec_put_format(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); + + /* set the format control flag */ + machine->fmt_via_kcontrol = ucontrol->value.integer.value[0]; + + return 0; +} + +static const char * const tegra_t186ref_jack_state_text[] = { + "None", + "HS", + "HP", +}; + +static int tegra_t186ref_codec_get_jack_state(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = tegra_t186ref_headset_switch.state; + return 0; +} + +static int tegra_t186ref_codec_put_jack_state(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + if (ucontrol->value.integer.value[0] == 0) + switch_set_state(&tegra_t186ref_headset_switch, BIT_NO_HEADSET); + else if (ucontrol->value.integer.value[0] == 1) + switch_set_state(&tegra_t186ref_headset_switch, BIT_HEADSET); + else if (ucontrol->value.integer.value[0] == 2) + switch_set_state(&tegra_t186ref_headset_switch, + BIT_HEADSET_NO_MIC); + return 0; +} + +static const struct soc_enum tegra_t186ref_codec_rate = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tegra_t186ref_srate_text), + tegra_t186ref_srate_text); + +static const struct soc_enum tegra_t186ref_codec_format = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tegra_t186ref_format_text), + tegra_t186ref_format_text); + +static const struct soc_enum tegra_t186ref_jack_state = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tegra_t186ref_jack_state_text), + tegra_t186ref_jack_state_text); + +static const struct snd_soc_dapm_route tegra_t186ref_audio_map[] = { +}; + +static const struct snd_kcontrol_new tegra_t186ref_controls[] = { + SOC_DAPM_PIN_SWITCH("x Int Spk"), + SOC_DAPM_PIN_SWITCH("x Headphone Jack"), + SOC_DAPM_PIN_SWITCH("x Mic Jack"), + SOC_DAPM_PIN_SWITCH("x Int Mic"), + SOC_ENUM_EXT("codec-x rate", tegra_t186ref_codec_rate, + tegra_t186ref_codec_get_rate, tegra_t186ref_codec_put_rate), + SOC_ENUM_EXT("codec-x format", tegra_t186ref_codec_format, + tegra_t186ref_codec_get_format, tegra_t186ref_codec_put_format), + SOC_ENUM_EXT("Jack-state", tegra_t186ref_jack_state, + tegra_t186ref_codec_get_jack_state, + tegra_t186ref_codec_put_jack_state), +}; + +static struct snd_soc_card snd_soc_tegra_t186ref_rt5639 = { + .name = "tegra-t186ref", + .owner = THIS_MODULE, + .remove = tegra_t186ref_remove, + .suspend_post = tegra_t186ref_suspend_post, + .suspend_pre = tegra_t186ref_suspend_pre, + .resume_pre = tegra_t186ref_resume_pre, + .controls = tegra_t186ref_controls, + .num_controls = ARRAY_SIZE(tegra_t186ref_controls), + .dapm_widgets = tegra_t186ref_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra_t186ref_dapm_widgets), + .fully_routed = true, +}; + +static void dai_link_setup(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); + struct snd_soc_codec_conf *tegra_machine_codec_conf = NULL; + struct snd_soc_codec_conf *tegra_t186ref_codec_conf = NULL; + struct snd_soc_dai_link *tegra_machine_dai_links = NULL; + struct snd_soc_dai_link *tegra_t186ref_codec_links = NULL; + int i; + + /* set new codec links and conf */ + tegra_t186ref_codec_links = tegra_machine_new_codec_links(pdev, + tegra_t186ref_codec_links, + &machine->num_codec_links); + if (!tegra_t186ref_codec_links) + goto err_alloc_dai_link; + + /* set codec init */ + for (i = 0; i < machine->num_codec_links; i++) { + if (tegra_t186ref_codec_links[i].name) { + if (strstr(tegra_t186ref_codec_links[i].name, + "rt5640-playback")) + tegra_t186ref_codec_links[i].init = tegra_t186ref_init; + else if (strstr(tegra_t186ref_codec_links[i].name, + "dspk-playback-r")) + tegra_t186ref_codec_links[i].init = tegra_t186ref_dspk_init; + else if (strstr(tegra_t186ref_codec_links[i].name, + "dspk-playback-l")) + tegra_t186ref_codec_links[i].init = tegra_t186ref_dspk_init; + } + } + + tegra_t186ref_codec_conf = tegra_machine_new_codec_conf(pdev, + tegra_t186ref_codec_conf, + &machine->num_codec_links); + if (!tegra_t186ref_codec_conf) + goto err_alloc_dai_link; + + /* get the xbar dai link/codec conf structure */ + tegra_machine_dai_links = tegra_machine_get_dai_link_t18x(); + if (!tegra_machine_dai_links) + goto err_alloc_dai_link; + + tegra_machine_codec_conf = tegra_machine_get_codec_conf_t18x(); + if (!tegra_machine_codec_conf) + goto err_alloc_dai_link; + + /* set ADMAIF dai_ops */ + for (i = TEGRA186_DAI_LINK_ADMAIF1; + i <= TEGRA186_DAI_LINK_ADMAIF20; i++) + tegra_machine_set_dai_ops(i, &tegra_t186ref_ops); + + /* set sfc dai_init */ + tegra_machine_set_dai_init(TEGRA186_DAI_LINK_SFC1_RX, + &tegra_t186ref_sfc_init); +#if defined(CONFIG_SND_SOC_TEGRA210_ADSP_ALT) + /* set ADSP PCM/COMPR */ + for (i = TEGRA186_DAI_LINK_ADSP_PCM1; + i <= TEGRA186_DAI_LINK_ADSP_PCM2; i++) { + tegra_machine_set_dai_ops(i, &tegra_t186ref_ops); + } + + /* set ADSP COMPR */ + for (i = TEGRA186_DAI_LINK_ADSP_COMPR1; + i <= TEGRA186_DAI_LINK_ADSP_COMPR2; i++) { + tegra_machine_set_dai_compr_ops(i, + &tegra_t186ref_compr_ops); + } +#endif + + /* set ASRC params. The default is 2 channels */ + for (i = 0; i < 6; i++) { + tegra_machine_set_dai_params(TEGRA186_DAI_LINK_ASRC1_TX1 + i, + (struct snd_soc_pcm_stream *) + &tegra_t186ref_asrc_link_params[i]); + tegra_machine_set_dai_params(TEGRA186_DAI_LINK_ASRC1_RX1 + i, + (struct snd_soc_pcm_stream *) + &tegra_t186ref_asrc_link_params[i]); + } + + /* append t186ref specific dai_links */ + card->num_links = + tegra_machine_append_dai_link_t18x(tegra_t186ref_codec_links, + 2 * machine->num_codec_links); + tegra_machine_dai_links = tegra_machine_get_dai_link_t18x(); + card->dai_link = tegra_machine_dai_links; + + /* append t186ref specific codec_conf */ + card->num_configs = + tegra_machine_append_codec_conf_t18x(tegra_t186ref_codec_conf, + machine->num_codec_links); + tegra_machine_codec_conf = tegra_machine_get_codec_conf_t18x(); + card->codec_conf = tegra_machine_codec_conf; + + return; + +err_alloc_dai_link: + dev_info(&pdev->dev, "Tonie's Msg : err_alloc_dai_link\n"); + tegra_machine_remove_dai_link(); + tegra_machine_remove_codec_conf(); + return; +} + +static int tegra_t186ref_rt5639_driver_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct snd_soc_card *card = &snd_soc_tegra_t186ref_rt5639; + struct tegra_t186ref *machine; + struct tegra_asoc_platform_data *pdata = NULL; + struct snd_soc_codec *codec = NULL; + int idx = 0; + int ret = 0; + const char *codec_dai_name; + + if (!np) { + dev_err(&pdev->dev, "No device tree node for t186ref driver"); + return -ENODEV; + } + + machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_t186ref), + GFP_KERNEL); + if (!machine) { + dev_err(&pdev->dev, "Can't allocate t186ref struct\n"); + ret = -ENOMEM; + goto err; + } + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, machine); + machine->is_codec_dummy = 0; + machine->audio_clock.clk_cdev1_state = 0; + machine->digital_reg = NULL; + machine->spk_reg = NULL; + machine->dmic_reg = NULL; + card->dapm.idle_bias_off = true; + + ret = snd_soc_of_parse_card_name(card, "nvidia,model"); + if (ret) + goto err; + + ret = snd_soc_of_parse_audio_routing(card, + "nvidia,audio-routing"); + if (ret) + goto err; + + if (of_property_read_u32(np, "nvidia,num-clk", + &machine->audio_clock.num_clk) < 0) { + dev_err(&pdev->dev, + "Missing property nvidia,num-clk\n"); + ret = -ENODEV; + goto err; + } + + if (of_property_read_u32_array(np, "nvidia,clk-rates", + (u32 *)&machine->audio_clock.clk_rates, + machine->audio_clock.num_clk) < 0) { + dev_err(&pdev->dev, + "Missing property nvidia,clk-rates\n"); + ret = -ENODEV; + goto err; + } + + dai_link_setup(pdev); + +#ifdef CONFIG_SWITCH + /* Addd h2w swith class support */ + ret = tegra_alt_asoc_switch_register(&tegra_t186ref_headset_switch); + if (ret < 0) + goto err_alloc_dai_link; +#endif + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct tegra_asoc_platform_data), + GFP_KERNEL); + if (!pdata) { + dev_err(&pdev->dev, + "Can't allocate tegra_asoc_platform_data struct\n"); + return -ENOMEM; + } + + pdata->gpio_hp_det = of_get_named_gpio(np, + "nvidia,hp-det-gpios", 0); + if (pdata->gpio_hp_det < 0) { + /* interrupt handled by codec */ + dev_warn(&pdev->dev, "Failed to get HP Det GPIO, should be handled by codec\n"); + } + + pdata->gpio_codec1 = pdata->gpio_codec2 = pdata->gpio_codec3 = + pdata->gpio_spkr_en = pdata->gpio_hp_mute = + pdata->gpio_int_mic_en = pdata->gpio_ext_mic_en = -1; + + machine->pdata = pdata; + machine->pcard = card; + + ret = tegra_alt_asoc_utils_init(&machine->audio_clock, + &pdev->dev, + card); + if (ret) + goto err_alloc_dai_link; + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err_fini_utils; + } + + idx = tegra_machine_get_codec_dai_link_idx_t18x("rt5640-playback"); + /* check if idx has valid number */ + if (idx == -EINVAL) + dev_warn(&pdev->dev, "codec link not defined - codec not part of sound card"); + else { + codec = card->rtd[idx].codec; + codec_dai_name = card->rtd[idx].dai_link->codec_dai_name; + + dev_info(&pdev->dev, + "codec-dai \"%s\" registered\n", codec_dai_name); + if (!strcmp("dit-hifi", codec_dai_name)) { + dev_info(&pdev->dev, "This is a dummy codec\n"); + machine->is_codec_dummy = 1; + } + else + dev_info(&pdev->dev, "This is not dummy codec\n"); + + if (!machine->is_codec_dummy) { + /* setup for jack detection only in non-dummy case */ + rt5640_irq_jd_reg_init(codec); + } + } + + return 0; + +err_fini_utils: + tegra_alt_asoc_utils_fini(&machine->audio_clock); + dev_info(&pdev->dev, "Tonie's Msg : tegra_t186ref_driver_probe err_fini_utils \n"); +err_alloc_dai_link: + tegra_machine_remove_dai_link(); + tegra_machine_remove_codec_conf(); + dev_info(&pdev->dev, "Tonie's Msg : tegra_t186ref_driver_probe err_alloc_dai_link \n"); +err: + dev_info(&pdev->dev, "Tonie's Msg : tegra_t186ref_driver_probe err \n"); + return ret; +} + +static int tegra_t186ref_rt5639_driver_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); + + snd_soc_unregister_card(card); + + tegra_machine_remove_dai_link(); + tegra_machine_remove_codec_conf(); + tegra_alt_asoc_utils_fini(&machine->audio_clock); + + return 0; +} + +static const struct of_device_id tegra_t186ref_rt5639_of_match[] = { + { .compatible = "nvidia,tegra-audio-t186ref-mobile-rt5639/40", }, + {}, +}; + +static struct platform_driver tegra_t186ref_rt5639_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = tegra_t186ref_rt5639_of_match, + }, + .probe = tegra_t186ref_rt5639_driver_probe, + .remove = tegra_t186ref_rt5639_driver_remove, +}; +module_platform_driver(tegra_t186ref_rt5639_driver); + +MODULE_AUTHOR("Tonie Huang "); +MODULE_DESCRIPTION("Tegra+t186ref machine ASoC driver for rt5639"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra_t186ref_rt5639_of_match);