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);