1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * tegra_wm8962.c - Tegra machine ASoC driver for boards using WM8962 codec. 4 * 5 * Copyright (C) 2021-2024 Jonas Schwöbel <jonasschwoebel@yahoo.de> 6 * Svyatoslav Ryhel <clamor95@gmail.com> 7 * 8 * Based on tegra_wm8903 code copyright/by: 9 * 10 * Author: Stephen Warren <swarren@nvidia.com> 11 * Copyright (C) 2010-2012 - NVIDIA, Inc. 12 * 13 * Based on code copyright/by: 14 * 15 * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd. 16 * 17 * Copyright 2007 Wolfson Microelectronics PLC. 18 * Author: Graeme Gregory 19 * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com 20 */ 21 22 #include <linux/gpio/consumer.h> 23 #include <linux/of.h> 24 #include <linux/module.h> 25 #include <linux/platform_device.h> 26 27 #include <sound/core.h> 28 #include <sound/jack.h> 29 #include <sound/soc.h> 30 31 #include "../codecs/wm8962.h" 32 33 #include "tegra_asoc_machine.h" 34 35 static struct snd_soc_jack_pin tegra_wm8962_mic_jack_pins[] = { 36 { .pin = "Mic Jack", .mask = SND_JACK_MICROPHONE }, 37 }; 38 39 static unsigned int tegra_wm8962_mclk_rate(unsigned int srate) 40 { 41 unsigned int mclk; 42 43 switch (srate) { 44 case 8000: 45 case 16000: 46 case 24000: 47 case 32000: 48 case 48000: 49 case 64000: 50 case 96000: 51 mclk = 12288000; 52 break; 53 case 11025: 54 case 22050: 55 case 44100: 56 case 88200: 57 mclk = 11289600; 58 break; 59 default: 60 mclk = 12000000; 61 break; 62 } 63 64 return mclk; 65 } 66 67 static int tegra_wm8962_init(struct snd_soc_pcm_runtime *rtd) 68 { 69 struct tegra_machine *machine = snd_soc_card_get_drvdata(rtd->card); 70 struct snd_soc_card *card = rtd->card; 71 struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); 72 int err; 73 74 err = tegra_asoc_machine_init(rtd); 75 if (err) 76 return err; 77 78 if (!machine->gpiod_mic_det && machine->asoc->add_mic_jack) { 79 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 80 struct snd_soc_component *component = codec_dai->component; 81 82 err = snd_soc_card_jack_new_pins(rtd->card, "Mic Jack", 83 SND_JACK_MICROPHONE, 84 machine->mic_jack, 85 tegra_wm8962_mic_jack_pins, 86 ARRAY_SIZE(tegra_wm8962_mic_jack_pins)); 87 if (err) { 88 dev_err(rtd->dev, "Mic Jack creation failed: %d\n", err); 89 return err; 90 } 91 92 wm8962_mic_detect(component, machine->mic_jack); 93 } 94 95 snd_soc_dapm_force_enable_pin(dapm, "MICBIAS"); 96 97 return 0; 98 } 99 100 static int tegra_wm8962_remove(struct snd_soc_card *card) 101 { 102 struct snd_soc_dai_link *link = &card->dai_link[0]; 103 struct snd_soc_pcm_runtime *rtd = snd_soc_get_pcm_runtime(card, link); 104 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 105 struct snd_soc_component *component = codec_dai->component; 106 107 wm8962_mic_detect(component, NULL); 108 109 return 0; 110 } 111 112 SND_SOC_DAILINK_DEFS(wm8962_hifi, 113 DAILINK_COMP_ARRAY(COMP_EMPTY()), 114 DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8962")), 115 DAILINK_COMP_ARRAY(COMP_EMPTY())); 116 117 static struct snd_soc_dai_link tegra_wm8962_dai = { 118 .name = "WM8962", 119 .stream_name = "WM8962 PCM", 120 .init = tegra_wm8962_init, 121 .dai_fmt = SND_SOC_DAIFMT_I2S | 122 SND_SOC_DAIFMT_NB_NF | 123 SND_SOC_DAIFMT_CBC_CFC, 124 SND_SOC_DAILINK_REG(wm8962_hifi), 125 }; 126 127 static struct snd_soc_card snd_soc_tegra_wm8962 = { 128 .components = "codec:wm8962", 129 .owner = THIS_MODULE, 130 .dai_link = &tegra_wm8962_dai, 131 .num_links = 1, 132 .remove = tegra_wm8962_remove, 133 .fully_routed = true, 134 }; 135 136 static const struct tegra_asoc_data tegra_wm8962_data = { 137 .mclk_rate = tegra_wm8962_mclk_rate, 138 .card = &snd_soc_tegra_wm8962, 139 .add_common_dapm_widgets = true, 140 .add_common_controls = true, 141 .add_common_snd_ops = true, 142 .add_mic_jack = true, 143 .add_hp_jack = true, 144 }; 145 146 static const struct of_device_id tegra_wm8962_of_match[] = { 147 { .compatible = "nvidia,tegra-audio-wm8962", .data = &tegra_wm8962_data }, 148 {}, 149 }; 150 MODULE_DEVICE_TABLE(of, tegra_wm8962_of_match); 151 152 static struct platform_driver tegra_wm8962_driver = { 153 .driver = { 154 .name = "tegra-wm8962", 155 .of_match_table = tegra_wm8962_of_match, 156 .pm = &snd_soc_pm_ops, 157 }, 158 .probe = tegra_asoc_machine_probe, 159 }; 160 module_platform_driver(tegra_wm8962_driver); 161 162 MODULE_AUTHOR("Jonas Schwöbel <jonasschwoebel@yahoo.de>"); 163 MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>"); 164 MODULE_DESCRIPTION("Tegra+WM8962 machine ASoC driver"); 165 MODULE_LICENSE("GPL"); 166