1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * tegra_wm8903.c - Tegra machine ASoC driver for boards using WM8903 codec. 4 * 5 * Author: Stephen Warren <swarren@nvidia.com> 6 * Copyright (C) 2010-2012 - NVIDIA, Inc. 7 * 8 * Based on code copyright/by: 9 * 10 * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd. 11 * 12 * Copyright 2007 Wolfson Microelectronics PLC. 13 * Author: Graeme Gregory 14 * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com 15 */ 16 17 #include <linux/gpio/consumer.h> 18 #include <linux/of.h> 19 #include <linux/module.h> 20 #include <linux/platform_device.h> 21 22 #include <sound/core.h> 23 #include <sound/jack.h> 24 #include <sound/soc.h> 25 26 #include "../codecs/wm8903.h" 27 28 #include "tegra_asoc_machine.h" 29 30 static struct snd_soc_jack_pin tegra_wm8903_mic_jack_pins[] = { 31 { .pin = "Mic Jack", .mask = SND_JACK_MICROPHONE }, 32 }; 33 34 static unsigned int tegra_wm8903_mclk_rate(unsigned int srate) 35 { 36 unsigned int mclk; 37 38 switch (srate) { 39 case 64000: 40 case 88200: 41 case 96000: 42 mclk = 128 * srate; 43 break; 44 default: 45 mclk = 256 * srate; 46 break; 47 } 48 /* FIXME: Codec only requires >= 3MHz if OSR==0 */ 49 while (mclk < 6000000) 50 mclk *= 2; 51 52 return mclk; 53 } 54 55 static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd) 56 { 57 struct tegra_machine *machine = snd_soc_card_get_drvdata(rtd->card); 58 struct snd_soc_card *card = rtd->card; 59 int err; 60 61 /* 62 * Older version of machine driver was ignoring GPIO polarity, 63 * forcing it to active-low. This means that all older device-trees 64 * which set the polarity to active-high are wrong and we need to fix 65 * them up. 66 */ 67 if (machine->asoc->hp_jack_gpio_active_low) { 68 bool active_low = gpiod_is_active_low(machine->gpiod_hp_det); 69 70 machine->hp_jack_gpio->invert = !active_low; 71 } 72 73 err = tegra_asoc_machine_init(rtd); 74 if (err) 75 return err; 76 77 if (!machine->gpiod_mic_det && machine->asoc->add_mic_jack) { 78 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 79 struct snd_soc_component *component = codec_dai->component; 80 int shrt = 0; 81 82 err = snd_soc_card_jack_new_pins(rtd->card, "Mic Jack", 83 SND_JACK_MICROPHONE, 84 machine->mic_jack, 85 tegra_wm8903_mic_jack_pins, 86 ARRAY_SIZE(tegra_wm8903_mic_jack_pins)); 87 if (err) { 88 dev_err(rtd->dev, "Mic Jack creation failed: %d\n", err); 89 return err; 90 } 91 92 if (of_property_read_bool(card->dev->of_node, "nvidia,headset")) 93 shrt = SND_JACK_MICROPHONE; 94 95 wm8903_mic_detect(component, machine->mic_jack, 96 SND_JACK_MICROPHONE, shrt); 97 } 98 99 snd_soc_dapm_force_enable_pin(&card->dapm, "MICBIAS"); 100 101 return 0; 102 } 103 104 static int tegra_wm8903_remove(struct snd_soc_card *card) 105 { 106 struct snd_soc_dai_link *link = &card->dai_link[0]; 107 struct snd_soc_pcm_runtime *rtd = snd_soc_get_pcm_runtime(card, link); 108 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 109 struct snd_soc_component *component = codec_dai->component; 110 111 wm8903_mic_detect(component, NULL, 0, 0); 112 113 return 0; 114 } 115 116 SND_SOC_DAILINK_DEFS(hifi, 117 DAILINK_COMP_ARRAY(COMP_EMPTY()), 118 DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8903-hifi")), 119 DAILINK_COMP_ARRAY(COMP_EMPTY())); 120 121 static struct snd_soc_dai_link tegra_wm8903_dai = { 122 .name = "WM8903", 123 .stream_name = "WM8903 PCM", 124 .init = tegra_wm8903_init, 125 .dai_fmt = SND_SOC_DAIFMT_I2S | 126 SND_SOC_DAIFMT_NB_NF | 127 SND_SOC_DAIFMT_CBS_CFS, 128 SND_SOC_DAILINK_REG(hifi), 129 }; 130 131 static struct snd_soc_card snd_soc_tegra_wm8903 = { 132 .components = "codec:wm8903", 133 .owner = THIS_MODULE, 134 .dai_link = &tegra_wm8903_dai, 135 .num_links = 1, 136 .remove = tegra_wm8903_remove, 137 .fully_routed = true, 138 }; 139 140 /* older device-trees used wrong polarity for the headphones-detection GPIO */ 141 static const struct tegra_asoc_data tegra_wm8903_data_legacy = { 142 .mclk_rate = tegra_wm8903_mclk_rate, 143 .card = &snd_soc_tegra_wm8903, 144 .hp_jack_gpio_active_low = true, 145 .add_common_dapm_widgets = true, 146 .add_common_controls = true, 147 .add_common_snd_ops = true, 148 .add_mic_jack = true, 149 .add_hp_jack = true, 150 }; 151 152 static const struct tegra_asoc_data tegra_wm8903_data = { 153 .mclk_rate = tegra_wm8903_mclk_rate, 154 .card = &snd_soc_tegra_wm8903, 155 .add_common_dapm_widgets = true, 156 .add_common_controls = true, 157 .add_common_snd_ops = true, 158 .add_mic_jack = true, 159 .add_hp_jack = true, 160 }; 161 162 static const struct of_device_id tegra_wm8903_of_match[] = { 163 { .compatible = "ad,tegra-audio-plutux", .data = &tegra_wm8903_data_legacy }, 164 { .compatible = "ad,tegra-audio-wm8903-medcom-wide", .data = &tegra_wm8903_data_legacy }, 165 { .compatible = "ad,tegra-audio-wm8903-tec", .data = &tegra_wm8903_data_legacy }, 166 { .compatible = "nvidia,tegra-audio-wm8903-cardhu", .data = &tegra_wm8903_data_legacy }, 167 { .compatible = "nvidia,tegra-audio-wm8903-harmony", .data = &tegra_wm8903_data_legacy }, 168 { .compatible = "nvidia,tegra-audio-wm8903-picasso", .data = &tegra_wm8903_data_legacy }, 169 { .compatible = "nvidia,tegra-audio-wm8903-seaboard", .data = &tegra_wm8903_data_legacy }, 170 { .compatible = "nvidia,tegra-audio-wm8903-ventana", .data = &tegra_wm8903_data_legacy }, 171 { .compatible = "nvidia,tegra-audio-wm8903", .data = &tegra_wm8903_data }, 172 {}, 173 }; 174 MODULE_DEVICE_TABLE(of, tegra_wm8903_of_match); 175 176 static struct platform_driver tegra_wm8903_driver = { 177 .driver = { 178 .name = "tegra-wm8903", 179 .of_match_table = tegra_wm8903_of_match, 180 .pm = &snd_soc_pm_ops, 181 }, 182 .probe = tegra_asoc_machine_probe, 183 }; 184 module_platform_driver(tegra_wm8903_driver); 185 186 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 187 MODULE_DESCRIPTION("Tegra+WM8903 machine ASoC driver"); 188 MODULE_LICENSE("GPL"); 189