/* * tegra_t210ref_mobile.c - Tegra T210 Machine driver for mobile * * Copyright (c) 2013-2017 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 #include #include #include #include #include #include #include #include #include "../codecs/rt5640.h" #include "tegra_asoc_utils_alt.h" #include "tegra_asoc_machine_alt.h" #include "tegra210_xbar_alt.h" #define DRV_NAME "tegra-snd-t210ref-mobile-rt5640" #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) struct tegra_t210ref { struct tegra_asoc_platform_data *pdata; struct tegra_asoc_audio_clock_info audio_clock; unsigned int num_codec_links; int gpio_requested; 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 is_codec_dummy; int rate_via_kcontrol; int fmt_via_kcontrol; }; static const int tegra_t210ref_srate_values[] = { 0, 8000, 16000, 44100, 48000, 11025, 22050, 24000, 32000, 88200, 96000, 176400, 192000, }; static struct snd_soc_jack tegra_t210ref_hp_jack; static struct snd_soc_jack_gpio tegra_t210ref_hp_jack_gpio = { .name = "headphone detect", .report = SND_JACK_HEADPHONE, .debounce_time = 150, .invert = 1, }; static int tegra_t210ref_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct tegra_machine *machine = snd_soc_card_get_drvdata(rtd->card); tegra_alt_asoc_utils_clk_enable(&machine->audio_clock); return 0; } static void tegra_t210ref_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct tegra_machine *machine = snd_soc_card_get_drvdata(rtd->card); tegra_alt_asoc_utils_clk_disable(&machine->audio_clock); } static int tegra_t210ref_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 snd_soc_codec *codec; struct tegra_t210ref *machine = snd_soc_card_get_drvdata(card); struct tegra_asoc_platform_data *pdata = machine->pdata; struct snd_soc_pcm_runtime *rtd; unsigned long focus_action = 0; unsigned char status_jack = 0; int idx = 0; if (machine->is_codec_dummy) return NOTIFY_OK; idx = tegra_machine_get_codec_dai_link_idx("rt5640-playback"); /* check if idx has valid number */ if (idx == -EINVAL) return idx; rtd = snd_soc_get_pcm_runtime(card, card->dai_link[idx].name); codec = rtd->codec; if (jack == &tegra_t210ref_hp_jack) { if (focus_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(codec, 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(codec, jack, 0); jack->status &= ~SND_JACK_HEADPHONE; jack->status &= ~SND_JACK_MICROPHONE; } } return NOTIFY_OK; } static struct notifier_block tegra_t210ref_jack_detect_nb = { .notifier_call = tegra_t210ref_jack_notifier, }; extern int tegra210_xbar_set_clock(unsigned long rate); static int tegra_t210ref_dai_init(struct snd_soc_pcm_runtime *rtd, int rate, int channels, u64 formats) { struct snd_soc_card *card = rtd->card; struct tegra_t210ref *machine = snd_soc_card_get_drvdata(card); struct snd_soc_pcm_stream *dai_params; unsigned int idx, mclk, clk_out_rate; int err, codec_rate, clk_rate; struct snd_soc_pcm_runtime *rtdp; unsigned int ratio = 1; codec_rate = tegra_t210ref_srate_values[machine->rate_via_kcontrol]; clk_rate = (machine->rate_via_kcontrol) ? codec_rate : rate; /* aud_mclk, 256 times the sample rate */ clk_out_rate = clk_rate << 8; switch (clk_rate) { case 11025: mclk = 22579200; break; case 22050: case 44100: case 88200: case 176400: mclk = 45158400; break; case 8000: mclk = 24576000; break; case 16000: case 32000: case 48000: case 64000: case 96000: case 192000: default: mclk = 49152000; break; } err = tegra_alt_asoc_utils_set_rate(&machine->audio_clock, clk_rate, mclk, clk_out_rate); if (err < 0) { dev_err(card->dev, "Can't configure clocks clk_rate %dHz pll_a %dHz clk_out %dHz\n", clk_rate, mclk, clk_out_rate); return err; } err = tegra210_xbar_set_clock(mclk); if (err < 0) { dev_err(card->dev, "Can't configure xbar clock = %d Hz\n", mclk); return err; } /* update dai link hw_params for non pcm links */ for (idx = 0; idx < TEGRA210_XBAR_DAI_LINKS; idx++) { rtdp = snd_soc_get_pcm_runtime(card, card->dai_link[idx].name); if (rtdp->dai_link->params) { dai_params = (struct snd_soc_pcm_stream *)rtdp->dai_link->params; dai_params->rate_min = rate; dai_params->channels_min = channels; dai_params->formats = 1ULL << ((machine->fmt_via_kcontrol == 2) ? SNDRV_PCM_FORMAT_S32_LE : SNDRV_PCM_FORMAT_S16_LE); } } idx = tegra_machine_get_codec_dai_link_idx("rt5640-playback"); //printk(KERN_ERR "tegra_t210ref_rt5640 %s rt5640-playback id = %d\n",__FUNCTION__,idx); /* check if idx has valid number */ if (idx != -EINVAL) { rtdp = snd_soc_get_pcm_runtime(card, card->dai_link[idx].name); dai_params =(struct snd_soc_pcm_stream *)rtdp->dai_link->params; if (!machine->is_codec_dummy) { err = snd_soc_dai_set_sysclk(rtdp->codec_dai, RT5640_SCLK_S_MCLK, clk_out_rate, SND_SOC_CLOCK_IN); if (err < 0) { dev_err(card->dev, "codec_dai clock not set\n"); return err; } } dai_params->formats = 1ULL << ((machine->fmt_via_kcontrol == 2) ? SNDRV_PCM_FORMAT_S32_LE : SNDRV_PCM_FORMAT_S16_LE); dai_params->rate_min = (machine->rate_via_kcontrol) ? codec_rate : rate; dai_params->channels_min = channels; err = snd_soc_dai_set_bclk_ratio(rtdp->cpu_dai,ratio); if (err < 0) { dev_err(card->dev, "Can't set cpu dai bclk ratio\n"); return err; } } return 0; } static int tegra_t210ref_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; err = tegra_t210ref_dai_init(rtd, params_rate(params), params_channels(params), (1ULL << (params_format(params)))); if (err < 0) { dev_err(card->dev, "Failed dai init\n"); return err; } return 0; } static int tegra_t210ref_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct tegra_t210ref *machine = snd_soc_card_get_drvdata(card); struct tegra_asoc_platform_data *pdata = machine->pdata; struct snd_soc_pcm_stream *dai_params = (struct snd_soc_pcm_stream *)rtd->dai_link->params; unsigned int srate; int err; srate = dai_params->rate_min; /* 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_t210ref_hp_jack_gpio.gpio = pdata->gpio_hp_det; tegra_t210ref_hp_jack_gpio.invert =!pdata->gpio_hp_det_active_high; err = snd_soc_card_jack_new(card, "Headphone Jack", SND_JACK_HEADPHONE, &tegra_t210ref_hp_jack, NULL, 0); if (err) { dev_err(card->dev, "Headset Jack creation failed %d\n", err); return err; } snd_soc_jack_notifier_register(&tegra_t210ref_hp_jack,&tegra_t210ref_jack_detect_nb); if (gpio_is_valid(pdata->gpio_hp_det)) { dev_dbg(card->dev, "associate the gpio to jack\n"); snd_soc_jack_add_gpios(&tegra_t210ref_hp_jack, 1, &tegra_t210ref_hp_jack_gpio); } snd_jack_set_key(tegra_t210ref_hp_jack.jack,SND_JACK_BTN_1, KEY_MEDIA); /* FIXME: map other button events too */ snd_soc_dapm_sync(&card->dapm); return 0; } static int tegra_t210ref_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_rt5640_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_t210ref *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_rt5640_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_t210ref *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_rt5640_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_t210ref *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_rt5640_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_t210ref *machine = snd_soc_card_get_drvdata(card); struct tegra_asoc_platform_data *pdata = machine->pdata; printk(KERN_ERR "tegra_t210ref_rt5640 %s\n",__FUNCTION__); 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_t210ref_ops = { .hw_params = tegra_t210ref_hw_params, .startup = tegra_t210ref_startup, .shutdown = tegra_t210ref_shutdown, }; static const struct snd_soc_dapm_widget tegra_t210ref_dapm_widgets[] = { SND_SOC_DAPM_HP("x Headphone Jack", tegra_rt5640_event_hp), SND_SOC_DAPM_SPK("x Int Spk", tegra_rt5640_event_int_spk), SND_SOC_DAPM_HP("x Headphone", NULL), 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_rt5640_event_int_mic), SND_SOC_DAPM_MIC("x Mic Jack", tegra_rt5640_event_ext_mic), SND_SOC_DAPM_MIC("x Mic", NULL), 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), SND_SOC_DAPM_LINE("x LineOut", NULL), }; static int tegra_t210ref_suspend_pre(struct snd_soc_card *card) { unsigned int idx; struct snd_soc_pcm_runtime *rtd; struct snd_soc_jack_gpio *gpio = &tegra_t210ref_hp_jack_gpio; /* DAPM dai link stream work for non pcm links */ for (idx = 0; idx < card->num_rtd; idx++) { rtd = snd_soc_get_pcm_runtime(card, card->dai_link[idx].name); if(rtd->dai_link->params) INIT_DELAYED_WORK(&rtd->delayed_work, NULL); } if (gpio_is_valid(gpio->gpio)) disable_irq(gpio_to_irq(gpio->gpio)); return 0; } static int tegra_t210ref_suspend_post(struct snd_soc_card *card) { struct tegra_t210ref *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_t210ref_resume_pre(struct snd_soc_card *card) { struct tegra_t210ref *machine = snd_soc_card_get_drvdata(card); struct snd_soc_jack_gpio *gpio = &tegra_t210ref_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 const char * const tegra_t210ref_srate_text[] = { "None", "8kHz", "16kHz", "44kHz", "48kHz", "11kHz", "22kHz", "24kHz", "32kHz", "88kHz", "96kHz", "176kHz", "192kHz", }; static int tegra_t210ref_codec_get_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); struct tegra_t210ref *machine = snd_soc_card_get_drvdata(card); ucontrol->value.integer.value[0] = machine->rate_via_kcontrol; return 0; } static int tegra_t210ref_codec_put_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); struct tegra_t210ref *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_t210ref_format_text[] = { "None", "16", "32", }; static int tegra_t210ref_codec_get_format(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); struct tegra_t210ref *machine = snd_soc_card_get_drvdata(card); ucontrol->value.integer.value[0] = machine->fmt_via_kcontrol; return 0; } static int tegra_t210ref_codec_put_format(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); struct tegra_t210ref *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 struct soc_enum tegra_t210ref_codec_rate = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tegra_t210ref_srate_text), tegra_t210ref_srate_text); static const struct soc_enum tegra_t210ref_codec_format = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tegra_t210ref_format_text), tegra_t210ref_format_text); static const struct snd_kcontrol_new tegra_t210ref_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_DAPM_PIN_SWITCH("x LineOut"), SOC_ENUM_EXT("codec-x rate", tegra_t210ref_codec_rate, tegra_t210ref_codec_get_rate, tegra_t210ref_codec_put_rate), SOC_ENUM_EXT("codec-x format", tegra_t210ref_codec_format, tegra_t210ref_codec_get_format, tegra_t210ref_codec_put_format), }; static int tegra_t210ref_remove(struct snd_soc_card *card) { return 0; } static struct snd_soc_card snd_soc_tegra_t210ref = { .name = "tegra-t210ref", .owner = THIS_MODULE, .remove = tegra_t210ref_remove, .suspend_post = tegra_t210ref_suspend_post, .suspend_pre = tegra_t210ref_suspend_pre, .resume_pre = tegra_t210ref_resume_pre, .controls = tegra_t210ref_controls, .num_controls = ARRAY_SIZE(tegra_t210ref_controls), .dapm_widgets = tegra_t210ref_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(tegra_t210ref_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_t210ref *machine = snd_soc_card_get_drvdata(card); struct snd_soc_codec_conf *tegra_machine_codec_conf = NULL; struct snd_soc_codec_conf *tegra_t210ref_codec_conf = NULL; struct snd_soc_dai_link *tegra_machine_dai_links = NULL; struct snd_soc_dai_link *tegra_t210ref_codec_links = NULL; int i; /* set new codec links and conf */ tegra_t210ref_codec_links = tegra_machine_new_codec_links(pdev,tegra_t210ref_codec_links,&machine->num_codec_links); if (!tegra_t210ref_codec_links) goto err_alloc_dai_link; /* set codec init */ for (i = 0; i < machine->num_codec_links; i++) { if (tegra_t210ref_codec_links[i].name) { if (strstr(tegra_t210ref_codec_links[i].name,"rt5640-playback")) { tegra_t210ref_codec_links[i].init = tegra_t210ref_init; } } } tegra_t210ref_codec_conf = tegra_machine_new_codec_conf(pdev,tegra_t210ref_codec_conf,&machine->num_codec_links); if (!tegra_t210ref_codec_conf) goto err_alloc_dai_link; /* get the xbar dai link/codec conf structure */ tegra_machine_dai_links = tegra_machine_get_dai_link(); if (!tegra_machine_dai_links) goto err_alloc_dai_link; tegra_machine_codec_conf = tegra_machine_get_codec_conf(); if (!tegra_machine_codec_conf) goto err_alloc_dai_link; /* set ADMAIF dai_ops */ for (i = TEGRA210_DAI_LINK_ADMAIF1; i <= TEGRA210_DAI_LINK_ADMAIF10; i++) tegra_machine_set_dai_ops(i, &tegra_t210ref_ops); //printk(KERN_ERR "tegra_t210ref_rt5640 %s TEGRA210_DAI_LINK_ADMAIF10 = %d\n",__FUNCTION__,i); /* set sfc dai_init */ tegra_machine_set_dai_init(TEGRA210_DAI_LINK_SFC1_RX,&tegra_t210ref_sfc_init); //i = TEGRA210_DAI_LINK_SFC1_RX; //printk(KERN_ERR "tegra_t210ref_rt5640 %s TEGRA210_DAI_LINK_SFC1_RX = %d\n",__FUNCTION__,i); /* append t210ref specific dai_links */ card->num_links = tegra_machine_append_dai_link(tegra_t210ref_codec_links, 2 * machine->num_codec_links); tegra_machine_dai_links = tegra_machine_get_dai_link(); card->dai_link = tegra_machine_dai_links; /* append t210ref specific codec_conf */ card->num_configs = tegra_machine_append_codec_conf(tegra_t210ref_codec_conf, machine->num_codec_links); tegra_machine_codec_conf = tegra_machine_get_codec_conf(); card->codec_conf = tegra_machine_codec_conf; return; err_alloc_dai_link: tegra_machine_remove_dai_link(); tegra_machine_remove_codec_conf(); } static int tegra_t210ref_driver_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct snd_soc_card *card = &snd_soc_tegra_t210ref; struct tegra_t210ref *machine; struct tegra_asoc_platform_data *pdata = NULL; struct snd_soc_codec *codec = NULL; int ret = 0, idx; const char *codec_dai_name; if (!np) { dev_err(&pdev->dev, "No device tree node for t210ref driver"); return -ENODEV; } machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_t210ref), GFP_KERNEL); if (!machine) { 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; 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; dai_link_setup(pdev); 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_info(&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; /* *codec_reg - its a GPIO (in the form of a fixed regulator) that enables *the basic(I2C) power for the codec and must be ON always */ if (!gpio_is_valid(pdata->gpio_ldo1_en)) { machine->codec_reg = regulator_get(&pdev->dev, "ldoen"); if (IS_ERR(machine->codec_reg)) { machine->codec_reg = NULL; } else ret = regulator_enable(machine->codec_reg); } /* *digital_reg - provided the digital power for the codec and must be *ON always */ machine->digital_reg = regulator_get(&pdev->dev, "dbvdd"); if (IS_ERR(machine->digital_reg)) { machine->digital_reg = NULL; } else { ret = regulator_enable(machine->digital_reg); } /* *analog_reg - provided the analog power for the codec and must be *ON always */ machine->analog_reg = regulator_get(&pdev->dev, "avdd"); if (IS_ERR(machine->analog_reg)) { machine->analog_reg = NULL; } else { ret = regulator_enable(machine->analog_reg); } /* *spk_reg - provided the speaker power and can be turned ON *on need basis, when required */ machine->spk_reg = regulator_get(&pdev->dev, "spkvdd"); if (IS_ERR(machine->spk_reg)) { machine->spk_reg = NULL; } else ret = regulator_enable(machine->spk_reg); machine->dmic_reg = regulator_get(&pdev->dev, "dmicvdd"); if (IS_ERR(machine->dmic_reg)) { machine->dmic_reg = NULL; } else ret = regulator_enable(machine->dmic_reg); 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_alloc_dai_link; } idx = tegra_machine_get_codec_dai_link_idx("rt5640-playback"); if (idx == -EINVAL) dev_warn(&pdev->dev, "codec link not defined - codec not part of sound card"); else { struct snd_soc_pcm_runtime *rtd; rtd = snd_soc_get_pcm_runtime(card, card->dai_link[idx].name); codec = rtd->codec; codec_dai_name = rtd->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_err(&pdev->dev, "This is a dummy codec\n"); machine->is_codec_dummy = 1; } } if (!machine->is_codec_dummy) { /* setup for jack detection only in non-dummy case */ rt5640_irq_jd_reg_init(codec); } return 0; err_alloc_dai_link: tegra_machine_remove_dai_link(); tegra_machine_remove_codec_conf(); err: return ret; } static int tegra_t210ref_driver_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); struct tegra_t210ref *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); tegra_alt_asoc_utils_clk_disable(&machine->audio_clock); return 0; } static const struct of_device_id tegra_t210ref_of_match[] = { { .compatible = "nvidia,tegra-audio-t210ref-mobile-rt5640", }, {}, }; static struct platform_driver tegra_t210ref_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, .pm = &snd_soc_pm_ops, .of_match_table = tegra_t210ref_of_match, }, .probe = tegra_t210ref_driver_probe, .remove = tegra_t210ref_driver_remove, }; module_platform_driver(tegra_t210ref_driver); MODULE_AUTHOR("Dara Ramesh "); MODULE_DESCRIPTION("Tegra t210ref machine ASoC driver for rt5640"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DRV_NAME); MODULE_DEVICE_TABLE(of, tegra_t210ref_of_match);