1// SPDX-License-Identifier: GPL-2.0-only 2/* 3* tegra_alc5632.c -- Toshiba AC100(PAZ00) machine ASoC driver 4 * 5 * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net> 6 * Copyright (C) 2012 - NVIDIA, Inc. 7 * 8 * Authors: Leon Romanovsky <leon@leon.nu> 9 * Andrey Danin <danindrey@mail.ru> 10 * Marc Dietrich <marvin24@gmx.de> 11 */ 12 13#include <linux/module.h> 14#include <linux/platform_device.h> 15#include <linux/slab.h> 16#include <linux/gpio.h> 17#include <linux/of_gpio.h> 18 19#include <sound/core.h> 20#include <sound/jack.h> 21#include <sound/pcm.h> 22#include <sound/pcm_params.h> 23#include <sound/soc.h> 24 25#include "../codecs/alc5632.h" 26 27#include "tegra_asoc_utils.h" 28 29#define DRV_NAME "tegra-alc5632" 30 31struct tegra_alc5632 { 32 struct tegra_asoc_utils_data util_data; 33 int gpio_hp_det; 34}; 35 36static int tegra_alc5632_asoc_hw_params(struct snd_pcm_substream *substream, 37 struct snd_pcm_hw_params *params) 38{ 39 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 40 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 41 struct snd_soc_card *card = rtd->card; 42 struct tegra_alc5632 *alc5632 = snd_soc_card_get_drvdata(card); 43 int srate, mclk; 44 int err; 45 46 srate = params_rate(params); 47 mclk = 512 * srate; 48 49 err = tegra_asoc_utils_set_rate(&alc5632->util_data, srate, mclk); 50 if (err < 0) { 51 dev_err(card->dev, "Can't configure clocks\n"); 52 return err; 53 } 54 55 err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, 56 SND_SOC_CLOCK_IN); 57 if (err < 0) { 58 dev_err(card->dev, "codec_dai clock not set\n"); 59 return err; 60 } 61 62 return 0; 63} 64 65static const struct snd_soc_ops tegra_alc5632_asoc_ops = { 66 .hw_params = tegra_alc5632_asoc_hw_params, 67}; 68 69static struct snd_soc_jack tegra_alc5632_hs_jack; 70 71static struct snd_soc_jack_pin tegra_alc5632_hs_jack_pins[] = { 72 { 73 .pin = "Headset Mic", 74 .mask = SND_JACK_MICROPHONE, 75 }, 76 { 77 .pin = "Headset Stereophone", 78 .mask = SND_JACK_HEADPHONE, 79 }, 80}; 81 82static struct snd_soc_jack_gpio tegra_alc5632_hp_jack_gpio = { 83 .name = "Headset detection", 84 .report = SND_JACK_HEADSET, 85 .debounce_time = 150, 86}; 87 88static const struct snd_soc_dapm_widget tegra_alc5632_dapm_widgets[] = { 89 SND_SOC_DAPM_SPK("Int Spk", NULL), 90 SND_SOC_DAPM_HP("Headset Stereophone", NULL), 91 SND_SOC_DAPM_MIC("Headset Mic", NULL), 92 SND_SOC_DAPM_MIC("Digital Mic", NULL), 93}; 94 95static const struct snd_kcontrol_new tegra_alc5632_controls[] = { 96 SOC_DAPM_PIN_SWITCH("Int Spk"), 97}; 98 99static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd) 100{ 101 int ret; 102 struct tegra_alc5632 *machine = snd_soc_card_get_drvdata(rtd->card); 103 104 ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", 105 SND_JACK_HEADSET, 106 &tegra_alc5632_hs_jack, 107 tegra_alc5632_hs_jack_pins, 108 ARRAY_SIZE(tegra_alc5632_hs_jack_pins)); 109 if (ret) 110 return ret; 111 112 if (gpio_is_valid(machine->gpio_hp_det)) { 113 tegra_alc5632_hp_jack_gpio.gpio = machine->gpio_hp_det; 114 snd_soc_jack_add_gpios(&tegra_alc5632_hs_jack, 115 1, 116 &tegra_alc5632_hp_jack_gpio); 117 } 118 119 snd_soc_dapm_force_enable_pin(&rtd->card->dapm, "MICBIAS1"); 120 121 return 0; 122} 123 124SND_SOC_DAILINK_DEFS(pcm, 125 DAILINK_COMP_ARRAY(COMP_EMPTY()), 126 DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "alc5632-hifi")), 127 DAILINK_COMP_ARRAY(COMP_EMPTY())); 128 129static struct snd_soc_dai_link tegra_alc5632_dai = { 130 .name = "ALC5632", 131 .stream_name = "ALC5632 PCM", 132 .init = tegra_alc5632_asoc_init, 133 .ops = &tegra_alc5632_asoc_ops, 134 .dai_fmt = SND_SOC_DAIFMT_I2S 135 | SND_SOC_DAIFMT_NB_NF 136 | SND_SOC_DAIFMT_CBS_CFS, 137 SND_SOC_DAILINK_REG(pcm), 138}; 139 140static struct snd_soc_card snd_soc_tegra_alc5632 = { 141 .name = "tegra-alc5632", 142 .driver_name = "tegra", 143 .owner = THIS_MODULE, 144 .dai_link = &tegra_alc5632_dai, 145 .num_links = 1, 146 .controls = tegra_alc5632_controls, 147 .num_controls = ARRAY_SIZE(tegra_alc5632_controls), 148 .dapm_widgets = tegra_alc5632_dapm_widgets, 149 .num_dapm_widgets = ARRAY_SIZE(tegra_alc5632_dapm_widgets), 150 .fully_routed = true, 151}; 152 153static int tegra_alc5632_probe(struct platform_device *pdev) 154{ 155 struct device_node *np = pdev->dev.of_node; 156 struct snd_soc_card *card = &snd_soc_tegra_alc5632; 157 struct tegra_alc5632 *alc5632; 158 int ret; 159 160 alc5632 = devm_kzalloc(&pdev->dev, 161 sizeof(struct tegra_alc5632), GFP_KERNEL); 162 if (!alc5632) 163 return -ENOMEM; 164 165 card->dev = &pdev->dev; 166 snd_soc_card_set_drvdata(card, alc5632); 167 168 alc5632->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0); 169 if (alc5632->gpio_hp_det == -EPROBE_DEFER) 170 return -EPROBE_DEFER; 171 172 ret = snd_soc_of_parse_card_name(card, "nvidia,model"); 173 if (ret) 174 goto err; 175 176 ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing"); 177 if (ret) 178 goto err; 179 180 tegra_alc5632_dai.codecs->of_node = of_parse_phandle( 181 pdev->dev.of_node, "nvidia,audio-codec", 0); 182 183 if (!tegra_alc5632_dai.codecs->of_node) { 184 dev_err(&pdev->dev, 185 "Property 'nvidia,audio-codec' missing or invalid\n"); 186 ret = -EINVAL; 187 goto err; 188 } 189 190 tegra_alc5632_dai.cpus->of_node = of_parse_phandle(np, 191 "nvidia,i2s-controller", 0); 192 if (!tegra_alc5632_dai.cpus->of_node) { 193 dev_err(&pdev->dev, 194 "Property 'nvidia,i2s-controller' missing or invalid\n"); 195 ret = -EINVAL; 196 goto err_put_codec_of_node; 197 } 198 199 tegra_alc5632_dai.platforms->of_node = tegra_alc5632_dai.cpus->of_node; 200 201 ret = tegra_asoc_utils_init(&alc5632->util_data, &pdev->dev); 202 if (ret) 203 goto err_put_cpu_of_node; 204 205 ret = snd_soc_register_card(card); 206 if (ret) { 207 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", 208 ret); 209 goto err_put_cpu_of_node; 210 } 211 212 return 0; 213 214err_put_cpu_of_node: 215 of_node_put(tegra_alc5632_dai.cpus->of_node); 216 tegra_alc5632_dai.cpus->of_node = NULL; 217 tegra_alc5632_dai.platforms->of_node = NULL; 218err_put_codec_of_node: 219 of_node_put(tegra_alc5632_dai.codecs->of_node); 220 tegra_alc5632_dai.codecs->of_node = NULL; 221err: 222 return ret; 223} 224 225static int tegra_alc5632_remove(struct platform_device *pdev) 226{ 227 struct snd_soc_card *card = platform_get_drvdata(pdev); 228 229 snd_soc_unregister_card(card); 230 231 of_node_put(tegra_alc5632_dai.cpus->of_node); 232 tegra_alc5632_dai.cpus->of_node = NULL; 233 tegra_alc5632_dai.platforms->of_node = NULL; 234 of_node_put(tegra_alc5632_dai.codecs->of_node); 235 tegra_alc5632_dai.codecs->of_node = NULL; 236 237 return 0; 238} 239 240static const struct of_device_id tegra_alc5632_of_match[] = { 241 { .compatible = "nvidia,tegra-audio-alc5632", }, 242 {}, 243}; 244 245static struct platform_driver tegra_alc5632_driver = { 246 .driver = { 247 .name = DRV_NAME, 248 .pm = &snd_soc_pm_ops, 249 .of_match_table = tegra_alc5632_of_match, 250 }, 251 .probe = tegra_alc5632_probe, 252 .remove = tegra_alc5632_remove, 253}; 254module_platform_driver(tegra_alc5632_driver); 255 256MODULE_AUTHOR("Leon Romanovsky <leon@leon.nu>"); 257MODULE_DESCRIPTION("Tegra+ALC5632 machine ASoC driver"); 258MODULE_LICENSE("GPL"); 259MODULE_ALIAS("platform:" DRV_NAME); 260MODULE_DEVICE_TABLE(of, tegra_alc5632_of_match); 261