Last active
April 24, 2018 02:12
-
-
Save rpavlik/675dde5bd622bcb0d132f95906901c5c to your computer and use it in GitHub Desktop.
TX2 with WM8731 audio codec
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* amati-audio-machine-generic-wm8731.dtsi - Module defining the audio | |
* "card"/machine driver on Amati R1 boards. | |
* | |
* Copyright (c) 2018 Sensics, Inc. All rights reserved. | |
* | |
* This program is free software; you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation; version 2 of the License. | |
* | |
* This program is distributed in the hope that 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. | |
*/ | |
#include <dt-bindings/pinctrl/pinctrl-tegra.h> | |
/ { | |
pinmux@2430000 { | |
i2s1_active_state: i2s1_active { | |
dap1_sclk_pj0 { | |
nvidia,pins = "dap1_sclk_pj0"; | |
nvidia,function = "i2s1"; | |
nvidia,enable-input = <TEGRA_PIN_DISABLE>; | |
nvidia,tristate = <TEGRA_PIN_DISABLE>; | |
}; | |
dap1_dout_pj1 { | |
nvidia,pins = "dap1_dout_pj1"; | |
nvidia,function = "i2s1"; | |
nvidia,enable-input = <TEGRA_PIN_DISABLE>; | |
nvidia,tristate = <TEGRA_PIN_DISABLE>; | |
}; | |
dap1_din_pj2 { | |
nvidia,pins = "dap1_din_pj2"; | |
nvidia,function = "i2s1"; | |
nvidia,enable-input = <TEGRA_PIN_ENABLE>; | |
nvidia,tristate = <TEGRA_PIN_ENABLE>; | |
nvidia,pull = <TEGRA_PIN_PULL_UP>; | |
}; | |
dap1_fs_pj3 { | |
nvidia,pins = "dap1_fs_pj3"; | |
nvidia,function = "i2s1"; | |
nvidia,enable-input = <TEGRA_PIN_DISABLE>; | |
nvidia,tristate = <TEGRA_PIN_DISABLE>; | |
}; | |
}; | |
}; | |
ahub { | |
status = "okay"; | |
/*tegra_i2s1*/ | |
i2s@2901000 { | |
pinctrl-names = "dap_active", "dap_inactive"; | |
pinctrl-0 = <&i2s1_active_state>; | |
pinctrl-1 = <>; | |
/*fsync-width = <31>;*/ | |
status = "okay"; | |
}; | |
}; | |
sound { | |
compatible = "nvidia,tegra-audio-t186ref-generic"; | |
//nvidia,model = "tegra-snd-t186ref-generic"; | |
nvidia,model = "tegra-snd-t186-amati-wm8731"; | |
nvidia,num-clk = <8>; | |
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"; | |
nvidia,audio-routing = | |
"x Headphone Jack", "x LHPOUT", | |
"x Headphone Jack", "x RHPOUT", | |
//"x Headphone Jack", "x Headphone", | |
//"x Mic Bias", "x MICIN", | |
//"x Mic Jack", "x Mic Bias", | |
"x MICIN", "x Mic Bias", | |
"x Mic Jack", "x MICIN", | |
"x DAC", "x Playback"; | |
//"x Headphone Jack", "x Playback", | |
//"x LHPOUT", "x Playback", | |
//"x LHPOUT", "x Playback"; | |
//"x Mic", "x Mic Jack"; | |
nvidia,xbar = <&tegra_axbar>; | |
nvidia,num-codec-link = <1>; | |
status = "okay"; | |
nvidia,main-link-name = "wm8731-playback"; | |
/* #define WM8731_SYSCLK_MCLK 2 */ | |
/* only in 4.4.y! changes to 0 later @todo */ | |
nvidia,main-clk-id = <2>; | |
nvidia,hp-detect-gpios = <&tegra_main_gpio TEGRA_MAIN_GPIO(J, 5) GPIO_ACTIVE_LOW>; | |
/* add this if we have a microphone on the codec */ | |
/* have-headset-mic; */ | |
wm8731_dai_link: nvidia,dai-link-1 { | |
link-name = "wm8731-playback"; | |
cpu-dai = <&tegra_i2s1>; | |
codec-dai = <&wm8731_codec>; | |
cpu-dai-name = "I2S1"; | |
codec-dai-name = "wm8731-hifi"; | |
format = "dsp_a"; | |
bitclock-slave; | |
frame-slave; | |
#if 0 | |
format = "i2s"; | |
bitclock-slave; | |
frame-slave; | |
#endif | |
bitclock-noninversion; | |
frame-noninversion; | |
bit-format = "s16_le"; | |
bclk_ratio = <1>; | |
srate = <48000>; | |
num-channel = <2>; | |
name-prefix = "x"; | |
status = "okay"; | |
}; | |
}; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* amati-audio-wm8731-codec.dtsi - Module defining the audio codec on | |
* Amati R1 boards. | |
* | |
* Copyright (c) 2018 Sensics, Inc. All rights reserved. | |
* | |
* This program is free software; you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation; version 2 of the License. | |
* | |
* This program is distributed in the hope that 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. | |
*/ | |
/ { | |
fixed-regulators { | |
vdd_1v8: regulator@100 { | |
compatible = "regulator-fixed-sync"; | |
reg = <100>; | |
regulator-name = "vdd-1v8"; | |
regulator-min-microvolt = <1800000>; | |
regulator-max-microvolt = <1800000>; | |
parent-supply = <&vdd_3v3>; | |
regulator-always-on; | |
regulator-boot-on; | |
}; | |
ldo_dcvdd: regulator@101 { | |
/* TPS73101DBVR with VDD_1V8 as supply, always enabled, output of roughly 1.5V */ | |
compatible = "regulator-fixed-sync"; | |
reg = <101>; | |
regulator-name = "dcvdd-1v5-aud"; | |
regulator-min-microvolt = <1497000>; | |
regulator-max-microvolt = <1497000>; | |
parent-supply = <&vdd_1v8>; | |
regulator-always-on; | |
regulator-boot-on; | |
}; | |
}; | |
}; | |
&vdd_3v3 { | |
regulator-always-on; | |
regulator-boot-on; | |
}; | |
/* Yes, the module i2c_gp2 is actually gen7_i2c... */ | |
&gen7_i2c { | |
wm8731_codec: wm8731@1a { | |
compatible = "wlf,wm8731"; | |
reg = <0x1a>; | |
AVDD-supply = <&battery_reg>; /*VCC_3V3 VCC_3V3_AUDCOD*/ | |
HPVDD-supply = <&battery_reg>; /* same as avdd*/ | |
DCVDD-supply = <&ldo_dcvdd>; /* DCVDD */ | |
DBVDD-supply = <&vdd_1v8>; /*VDD_1V8*/ | |
clocks = <&tegra_car TEGRA186_CLK_AUD_MCLK>; | |
clock-names = "mclk"; | |
/* Only the following channels are connected: | |
* LHPOUT: Left Channel Headphone Output | |
* RHPOUT: Right Channel Headphone Output | |
* MICIN: Microphone Input | |
*/ | |
}; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* tegra_t186ref_generic.c - More generic Tegra t186ref Machine driver | |
* | |
* Copyright (c) 2018 Sensics, Inc. All rights reserved. | |
* | |
* Based on tegra_t186ref_mobile_rt565x.c | |
* Copyright (c) 2015-2018 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 <http://www.gnu.org/licenses/>. | |
*/ | |
#include <linux/module.h> | |
#include <linux/platform_device.h> | |
#include <linux/of_platform.h> | |
#include <linux/input.h> | |
#include <linux/slab.h> | |
#include <linux/gpio.h> | |
#include <linux/of_gpio.h> | |
#include <linux/i2c.h> | |
#include <linux/regulator/consumer.h> | |
#include <linux/delay.h> | |
#include <soc/tegra/pmc.h> | |
#ifdef CONFIG_SWITCH | |
#include <linux/switch.h> | |
#endif | |
#include <linux/pm_runtime.h> | |
#include <linux/version.h> | |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) | |
#include <linux/platform_data/tegra_asoc_pdata.h> | |
#else | |
#include <mach/tegra_asoc_pdata.h> | |
#endif | |
#include <sound/core.h> | |
#include <sound/jack.h> | |
#include <sound/pcm.h> | |
#include <sound/pcm_params.h> | |
#include <sound/soc.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" | |
#define DRV_NAME "tegra-snd-t186ref-generic" | |
#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 | |
struct regulator *digital_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; | |
unsigned int bclk_ratio_override; | |
const char * main_link_name; | |
int main_clk_id; | |
}; | |
static const int tegra_t186ref_srate_values[] = { | |
0, | |
8000, | |
16000, | |
44100, | |
48000, | |
11025, | |
22050, | |
24000, | |
32000, | |
88200, | |
96000, | |
176400, | |
192000, | |
}; | |
static struct snd_soc_jack tegra_t186ref_hp_jack; | |
#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; | |
struct snd_soc_card *card = jack->card; | |
struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); | |
enum headset_state state = BIT_NO_HEADSET; | |
int idx = 0; | |
if (machine->is_codec_dummy) | |
return NOTIFY_OK; | |
idx = tegra_machine_get_codec_dai_link_idx_t18x(machine->main_link_name); | |
/* check if idx has valid number */ | |
if (idx == -EINVAL) | |
return idx; | |
dev_info(card->dev, "jack status = %d", jack->status); | |
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_info(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_jack_gpio tegra_t186ref_jack_detect_gpio = { | |
.name = "nvidia,hp-detect", /* this name used to construct DT property name */ | |
.report = SND_JACK_HEADPHONE, /* may be overridden if have-headset-mic is in dt */ | |
.debounce_time = 150, | |
}; | |
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_bclk_ratio(struct tegra_t186ref *machine, | |
struct snd_soc_pcm_runtime *rtd) | |
{ | |
unsigned int bclk_ratio; | |
int err = 0; | |
if (machine->bclk_ratio_override) | |
bclk_ratio = machine->bclk_ratio_override; | |
else | |
bclk_ratio = tegra_machine_get_bclk_ratio_t18x(rtd); | |
if (bclk_ratio >= 0) | |
err = snd_soc_dai_set_bclk_ratio(rtd->cpu_dai, bclk_ratio); | |
return err; | |
} | |
static int tegra_t186ref_set_tdm_slot(struct snd_soc_pcm_runtime *rtd) | |
{ | |
unsigned int fmt, mask; | |
int err = 0; | |
fmt = rtd->dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK; | |
mask = (1 << rtd->dai_link->params->channels_min) - 1; | |
if ((fmt == SND_SOC_DAIFMT_DSP_A) || (fmt == SND_SOC_DAIFMT_DSP_B)) | |
err = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, mask, mask, 0, 0); | |
return err; | |
} | |
static int tegra_t186ref_set_params(struct snd_soc_card *card, | |
struct tegra_t186ref *machine, | |
int rate, | |
int channels, | |
u64 formats) | |
{ | |
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) { | |
dai_params->formats = formats; | |
err = tegra_t186ref_set_bclk_ratio(machine, | |
&card->rtd[idx]); | |
if (err < 0) { | |
dev_err(card->dev, | |
"Failed to set cpu dai bclk ratio for %s\n", | |
card->rtd[idx].dai_link->name); | |
} | |
err = tegra_t186ref_set_tdm_slot( | |
&card->rtd[idx]); | |
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("tegra_t186ref_dai_init: audio_clock.set_mclk = %d Hz, audio_clock.clk_out_rate = %d Hz, requested 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(machine->main_link_name); | |
/* 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; | |
if (!machine->is_codec_dummy) { | |
err = snd_soc_dai_set_sysclk(card->rtd[idx].codec_dai, | |
machine->main_clk_id, clk_out_rate, SND_SOC_CLOCK_IN); | |
if (err < 0) { | |
dev_err(card->dev, "codec_dai clock not set\n"); | |
return err; | |
} | |
} | |
} | |
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); | |
int err; | |
static const char hp_jack[] = "Headphone Jack"; | |
static const char hs_jack[] = "Headset Jack"; | |
const char *jackname = hp_jack; | |
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; | |
} | |
if (of_property_read_bool(card->dev->of_node, "have-headset-mic")) { | |
tegra_t186ref_jack_detect_gpio.report = SND_JACK_HEADSET; | |
jackname = hs_jack; | |
} | |
err = snd_soc_card_jack_new(card, jackname, | |
tegra_t186ref_jack_detect_gpio.report, | |
&tegra_t186ref_hp_jack, NULL, 0); | |
if (err) { | |
dev_err(card->dev, "%s creation failed %d\n", jackname, err); | |
return err; | |
} | |
#ifndef CONFIG_SWITCH | |
tegra_t186ref_hp_jack_pins[0].mask = tegra_t186ref_jack_detect_gpio.report; | |
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 | |
err = snd_soc_jack_add_gpiods(card->dev, &tegra_t186ref_hp_jack, 1, | |
&tegra_t186ref_jack_detect_gpio); | |
if (err) { | |
dev_warn(card->dev, "snd_soc_jack_add_gpiods failed %d\n", err); | |
/* Not fatal to not have jack-detect. */ | |
} | |
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_generic_event_hp(struct snd_soc_dapm_widget *w, | |
struct snd_kcontrol *k, int event) | |
{ | |
#if 0 | |
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)); | |
#endif | |
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[] = { | |
#if 0 | |
/** @todo actually adjust power... */ | |
SND_SOC_DAPM_HP("x Headphone Jack", tegra_generic_event_int_spk), | |
SND_SOC_DAPM_SPK("x Int Spk", tegra_generic_event_hp), | |
SND_SOC_DAPM_MIC("x Int Mic", tegra_generic_event_int_mic), | |
SND_SOC_DAPM_MIC("x Mic Jack", tegra_generic_event_ext_mic), | |
#endif | |
SND_SOC_DAPM_HP("x Headphone Jack", tegra_generic_event_hp), | |
#if 0 | |
SND_SOC_DAPM_SPK("x Int Spk", NULL), | |
SND_SOC_DAPM_MIC("x Int Mic", NULL), | |
SND_SOC_DAPM_MIC("Int Mic", NULL), | |
#endif | |
SND_SOC_DAPM_MIC("x Mic Jack", NULL), | |
#if 0 | |
SND_SOC_DAPM_HP("x Headphone", NULL), | |
SND_SOC_DAPM_MIC("x Mic", NULL), | |
SND_SOC_DAPM_HP("y Headphone", NULL), | |
SND_SOC_DAPM_MIC("y Mic", NULL), | |
SND_SOC_DAPM_HP("z Headphone", NULL), | |
SND_SOC_DAPM_MIC("z Mic", NULL), | |
SND_SOC_DAPM_HP("m Headphone", NULL), | |
SND_SOC_DAPM_MIC("m Mic", NULL), | |
SND_SOC_DAPM_HP("n Headphone", NULL), | |
SND_SOC_DAPM_MIC("n Mic", NULL), | |
SND_SOC_DAPM_HP("o Headphone", NULL), | |
SND_SOC_DAPM_MIC("o Mic", NULL), | |
SND_SOC_DAPM_MIC("a Mic", NULL), | |
SND_SOC_DAPM_MIC("b Mic", NULL), | |
SND_SOC_DAPM_MIC("c Mic", NULL), | |
SND_SOC_DAPM_MIC("d Mic", NULL), | |
SND_SOC_DAPM_HP("e Headphone", NULL), | |
SND_SOC_DAPM_MIC("e Mic", NULL), | |
SND_SOC_DAPM_SPK("d1 Headphone", NULL), | |
SND_SOC_DAPM_SPK("d2 Headphone", NULL), | |
#endif | |
}; | |
static int tegra_t186ref_suspend_pre(struct snd_soc_card *card) | |
{ | |
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); | |
} | |
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->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); | |
int ret; | |
if (machine->digital_reg) | |
ret = regulator_enable(machine->digital_reg); | |
return 0; | |
} | |
static int tegra_t186ref_remove(struct snd_soc_card *card) | |
{ | |
#if 0 | |
struct tegra_t186ref *machine = snd_soc_card_get_drvdata(card); | |
if (gpio_is_valid(machine->pdata->gpio_hp_det)) | |
snd_soc_jack_free_gpios(&tegra_t186ref_hp_jack, 1, | |
&tegra_t186ref_jack_detect_gpio); | |
#endif | |
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 int tegra_t186ref_bclk_ratio_get(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->bclk_ratio_override; | |
return 0; | |
} | |
static int tegra_t186ref_bclk_ratio_put(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); | |
machine->bclk_ratio_override = ucontrol->value.integer.value[0]; | |
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 Headphone Jack"), | |
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), | |
SOC_SINGLE_EXT("bclk ratio override", SND_SOC_NOPM, 0, INT_MAX, 0, | |
tegra_t186ref_bclk_ratio_get, tegra_t186ref_bclk_ratio_put), | |
}; | |
static struct snd_soc_card snd_soc_tegra_t186ref = { | |
.name = "tegra-t186ref-generic", | |
.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, | |
machine->main_link_name)) { | |
pr_info("*** codec link %d is main link\n", i); | |
tegra_t186ref_codec_links[i].init = tegra_t186ref_init; | |
/// Only a single clock being used for both playback and capture | |
tegra_t186ref_codec_links[i].symmetric_rates = true; | |
} | |
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: | |
tegra_machine_remove_dai_link(); | |
tegra_machine_remove_codec_conf(); | |
return; | |
} | |
static int tegra_t186ref_driver_probe(struct platform_device *pdev) | |
{ | |
struct device_node *np = pdev->dev.of_node; | |
struct snd_soc_card *card = &snd_soc_tegra_t186ref; | |
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 generic 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; | |
} | |
if (of_property_read_string_index(np, | |
"nvidia,main-link-name", 0, &machine->main_link_name) < 0) { | |
dev_err(&pdev->dev, | |
"Missing property nvidia,main-link-name\n"); | |
ret = -ENODEV; | |
goto err; | |
} | |
{ | |
u32 temp; | |
if (of_property_read_u32(np, | |
"nvidia,main-clk-id", &temp) < 0) { | |
dev_err(&pdev->dev, | |
"Missing property nvidia,main-clk-id (clock ID enum from codec)\n"); | |
ret = -ENODEV; | |
goto err; | |
} | |
machine->main_clk_id = (int)temp; | |
} | |
dai_link_setup(pdev); | |
#ifdef CONFIG_SWITCH | |
/* Add h2w switch 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_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_switch_unregister; | |
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(machine->main_link_name); | |
/* check if idx has valid number */ | |
if (idx == -EINVAL) | |
dev_warn(&pdev->dev, "main 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; | |
} | |
} | |
return 0; | |
err_fini_utils: | |
tegra_alt_asoc_utils_fini(&machine->audio_clock); | |
err_switch_unregister: | |
#ifdef CONFIG_SWITCH | |
tegra_alt_asoc_switch_unregister(&tegra_t186ref_headset_switch); | |
#endif | |
err_alloc_dai_link: | |
tegra_machine_remove_dai_link(); | |
tegra_machine_remove_codec_conf(); | |
err: | |
return ret; | |
} | |
static int tegra_t186ref_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); | |
#ifdef CONFIG_SWITCH | |
tegra_alt_asoc_switch_unregister(&tegra_t186ref_headset_switch); | |
#endif | |
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_of_match[] = { | |
{ .compatible = "nvidia,tegra-audio-t186ref-generic", }, | |
{}, | |
}; | |
static struct platform_driver tegra_t186ref_driver = { | |
.driver = { | |
.name = DRV_NAME, | |
.owner = THIS_MODULE, | |
.pm = &snd_soc_pm_ops, | |
.of_match_table = tegra_t186ref_of_match, | |
}, | |
.probe = tegra_t186ref_driver_probe, | |
.remove = tegra_t186ref_driver_remove, | |
}; | |
module_platform_driver(tegra_t186ref_driver); | |
MODULE_AUTHOR("Ryan Pavlik <ryan@sensics.com> and Mohan Kumar <mkumard@nvidia.com>"); | |
MODULE_DESCRIPTION("Generic Tegra+t186ref machine ASoC driver"); | |
MODULE_LICENSE("GPL"); | |
MODULE_ALIAS("platform:" DRV_NAME); | |
MODULE_DEVICE_TABLE(of, tegra_t186ref_of_match); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment