1// SPDX-License-Identifier: GPL-2.0-only 2/* 3* tegra_rt5677.c - Tegra machine ASoC driver for boards using RT5677 codec. 4 * 5 * Copyright (c) 2014, The Chromium OS Authors. All rights reserved. 6 * 7 * Based on code copyright/by: 8 * 9 * Copyright (C) 2010-2012 - NVIDIA, Inc. 10 * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net> 11 * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd. 12 * Copyright 2007 Wolfson Microelectronics PLC. 13 */ 14 15#include <linux/module.h> 16#include <linux/platform_device.h> 17#include <linux/slab.h> 18#include <linux/gpio.h> 19#include <linux/of_gpio.h> 20 21#include <sound/core.h> 22#include <sound/jack.h> 23#include <sound/pcm.h> 24#include <sound/pcm_params.h> 25#include <sound/soc.h> 26 27#include "../codecs/rt5677.h" 28 29#include "tegra_asoc_utils.h" 30 31#define DRV_NAME "tegra-snd-rt5677" 32 33struct tegra_rt5677 { 34 struct tegra_asoc_utils_data util_data; 35 int gpio_hp_det; 36 int gpio_hp_en; 37 int gpio_mic_present; 38 int gpio_dmic_clk_en; 39}; 40 41static int tegra_rt5677_asoc_hw_params(struct snd_pcm_substream *substream, 42 struct snd_pcm_hw_params *params) 43{ 44 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 45 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 46 struct snd_soc_card *card = rtd->card; 47 struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card); 48 int srate, mclk, err; 49 50 srate = params_rate(params); 51 mclk = 256 * srate; 52 53 err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); 54 if (err < 0) { 55 dev_err(card->dev, "Can't configure clocks\n"); 56 return err; 57 } 58 59 err = snd_soc_dai_set_sysclk(codec_dai, RT5677_SCLK_S_MCLK, mclk, 60 SND_SOC_CLOCK_IN); 61 if (err < 0) { 62 dev_err(card->dev, "codec_dai clock not set\n"); 63 return err; 64 } 65 66 return 0; 67} 68 69static int tegra_rt5677_event_hp(struct snd_soc_dapm_widget *w, 70 struct snd_kcontrol *k, int event) 71{ 72 struct snd_soc_dapm_context *dapm = w->dapm; 73 struct snd_soc_card *card = dapm->card; 74 struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card); 75 76 if (!gpio_is_valid(machine->gpio_hp_en)) 77 return 0; 78 79 gpio_set_value_cansleep(machine->gpio_hp_en, 80 SND_SOC_DAPM_EVENT_ON(event)); 81 82 return 0; 83} 84 85static const struct snd_soc_ops tegra_rt5677_ops = { 86 .hw_params = tegra_rt5677_asoc_hw_params, 87}; 88 89static struct snd_soc_jack tegra_rt5677_hp_jack; 90 91static struct snd_soc_jack_pin tegra_rt5677_hp_jack_pins = { 92 .pin = "Headphone", 93 .mask = SND_JACK_HEADPHONE, 94}; 95static struct snd_soc_jack_gpio tegra_rt5677_hp_jack_gpio = { 96 .name = "Headphone detection", 97 .report = SND_JACK_HEADPHONE, 98 .debounce_time = 150, 99}; 100 101static struct snd_soc_jack tegra_rt5677_mic_jack; 102 103static struct snd_soc_jack_pin tegra_rt5677_mic_jack_pins = { 104 .pin = "Headset Mic", 105 .mask = SND_JACK_MICROPHONE, 106}; 107 108static struct snd_soc_jack_gpio tegra_rt5677_mic_jack_gpio = { 109 .name = "Headset Mic detection", 110 .report = SND_JACK_MICROPHONE, 111 .debounce_time = 150, 112 .invert = 1 113}; 114 115static const struct snd_soc_dapm_widget tegra_rt5677_dapm_widgets[] = { 116 SND_SOC_DAPM_SPK("Speaker", NULL), 117 SND_SOC_DAPM_HP("Headphone", tegra_rt5677_event_hp), 118 SND_SOC_DAPM_MIC("Headset Mic", NULL), 119 SND_SOC_DAPM_MIC("Internal Mic 1", NULL), 120 SND_SOC_DAPM_MIC("Internal Mic 2", NULL), 121}; 122 123static const struct snd_kcontrol_new tegra_rt5677_controls[] = { 124 SOC_DAPM_PIN_SWITCH("Speaker"), 125 SOC_DAPM_PIN_SWITCH("Headphone"), 126 SOC_DAPM_PIN_SWITCH("Headset Mic"), 127 SOC_DAPM_PIN_SWITCH("Internal Mic 1"), 128 SOC_DAPM_PIN_SWITCH("Internal Mic 2"), 129}; 130 131static int tegra_rt5677_asoc_init(struct snd_soc_pcm_runtime *rtd) 132{ 133 struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(rtd->card); 134 135 snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE, 136 &tegra_rt5677_hp_jack, 137 &tegra_rt5677_hp_jack_pins, 1); 138 139 if (gpio_is_valid(machine->gpio_hp_det)) { 140 tegra_rt5677_hp_jack_gpio.gpio = machine->gpio_hp_det; 141 snd_soc_jack_add_gpios(&tegra_rt5677_hp_jack, 1, 142 &tegra_rt5677_hp_jack_gpio); 143 } 144 145 146 snd_soc_card_jack_new(rtd->card, "Mic Jack", SND_JACK_MICROPHONE, 147 &tegra_rt5677_mic_jack, 148 &tegra_rt5677_mic_jack_pins, 1); 149 150 if (gpio_is_valid(machine->gpio_mic_present)) { 151 tegra_rt5677_mic_jack_gpio.gpio = machine->gpio_mic_present; 152 snd_soc_jack_add_gpios(&tegra_rt5677_mic_jack, 1, 153 &tegra_rt5677_mic_jack_gpio); 154 } 155 156 snd_soc_dapm_force_enable_pin(&rtd->card->dapm, "MICBIAS1"); 157 158 return 0; 159} 160 161SND_SOC_DAILINK_DEFS(pcm, 162 DAILINK_COMP_ARRAY(COMP_EMPTY()), 163 DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5677-aif1")), 164 DAILINK_COMP_ARRAY(COMP_EMPTY())); 165 166static struct snd_soc_dai_link tegra_rt5677_dai = { 167 .name = "RT5677", 168 .stream_name = "RT5677 PCM", 169 .init = tegra_rt5677_asoc_init, 170 .ops = &tegra_rt5677_ops, 171 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 172 SND_SOC_DAIFMT_CBS_CFS, 173 SND_SOC_DAILINK_REG(pcm), 174}; 175 176static struct snd_soc_card snd_soc_tegra_rt5677 = { 177 .name = "tegra-rt5677", 178 .driver_name = "tegra", 179 .owner = THIS_MODULE, 180 .dai_link = &tegra_rt5677_dai, 181 .num_links = 1, 182 .controls = tegra_rt5677_controls, 183 .num_controls = ARRAY_SIZE(tegra_rt5677_controls), 184 .dapm_widgets = tegra_rt5677_dapm_widgets, 185 .num_dapm_widgets = ARRAY_SIZE(tegra_rt5677_dapm_widgets), 186 .fully_routed = true, 187}; 188 189static int tegra_rt5677_probe(struct platform_device *pdev) 190{ 191 struct device_node *np = pdev->dev.of_node; 192 struct snd_soc_card *card = &snd_soc_tegra_rt5677; 193 struct tegra_rt5677 *machine; 194 int ret; 195 196 machine = devm_kzalloc(&pdev->dev, 197 sizeof(struct tegra_rt5677), GFP_KERNEL); 198 if (!machine) 199 return -ENOMEM; 200 201 card->dev = &pdev->dev; 202 snd_soc_card_set_drvdata(card, machine); 203 204 machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0); 205 if (machine->gpio_hp_det == -EPROBE_DEFER) 206 return -EPROBE_DEFER; 207 208 machine->gpio_mic_present = of_get_named_gpio(np, 209 "nvidia,mic-present-gpios", 0); 210 if (machine->gpio_mic_present == -EPROBE_DEFER) 211 return -EPROBE_DEFER; 212 213 machine->gpio_hp_en = of_get_named_gpio(np, "nvidia,hp-en-gpios", 0); 214 if (machine->gpio_hp_en == -EPROBE_DEFER) 215 return -EPROBE_DEFER; 216 if (gpio_is_valid(machine->gpio_hp_en)) { 217 ret = devm_gpio_request_one(&pdev->dev, machine->gpio_hp_en, 218 GPIOF_OUT_INIT_LOW, "hp_en"); 219 if (ret) { 220 dev_err(card->dev, "cannot get hp_en gpio\n"); 221 return ret; 222 } 223 } 224 225 machine->gpio_dmic_clk_en = of_get_named_gpio(np, 226 "nvidia,dmic-clk-en-gpios", 0); 227 if (machine->gpio_dmic_clk_en == -EPROBE_DEFER) 228 return -EPROBE_DEFER; 229 if (gpio_is_valid(machine->gpio_dmic_clk_en)) { 230 ret = devm_gpio_request_one(&pdev->dev, 231 machine->gpio_dmic_clk_en, 232 GPIOF_OUT_INIT_HIGH, "dmic_clk_en"); 233 if (ret) { 234 dev_err(card->dev, "cannot get dmic_clk_en gpio\n"); 235 return ret; 236 } 237 } 238 239 ret = snd_soc_of_parse_card_name(card, "nvidia,model"); 240 if (ret) 241 goto err; 242 243 ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing"); 244 if (ret) 245 goto err; 246 247 tegra_rt5677_dai.codecs->of_node = of_parse_phandle(np, 248 "nvidia,audio-codec", 0); 249 if (!tegra_rt5677_dai.codecs->of_node) { 250 dev_err(&pdev->dev, 251 "Property 'nvidia,audio-codec' missing or invalid\n"); 252 ret = -EINVAL; 253 goto err; 254 } 255 256 tegra_rt5677_dai.cpus->of_node = of_parse_phandle(np, 257 "nvidia,i2s-controller", 0); 258 if (!tegra_rt5677_dai.cpus->of_node) { 259 dev_err(&pdev->dev, 260 "Property 'nvidia,i2s-controller' missing or invalid\n"); 261 ret = -EINVAL; 262 goto err_put_codec_of_node; 263 } 264 tegra_rt5677_dai.platforms->of_node = tegra_rt5677_dai.cpus->of_node; 265 266 ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); 267 if (ret) 268 goto err_put_cpu_of_node; 269 270 ret = snd_soc_register_card(card); 271 if (ret) { 272 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", 273 ret); 274 goto err_put_cpu_of_node; 275 } 276 277 return 0; 278 279err_put_cpu_of_node: 280 of_node_put(tegra_rt5677_dai.cpus->of_node); 281 tegra_rt5677_dai.cpus->of_node = NULL; 282 tegra_rt5677_dai.platforms->of_node = NULL; 283err_put_codec_of_node: 284 of_node_put(tegra_rt5677_dai.codecs->of_node); 285 tegra_rt5677_dai.codecs->of_node = NULL; 286err: 287 return ret; 288} 289 290static int tegra_rt5677_remove(struct platform_device *pdev) 291{ 292 struct snd_soc_card *card = platform_get_drvdata(pdev); 293 294 snd_soc_unregister_card(card); 295 296 tegra_rt5677_dai.platforms->of_node = NULL; 297 of_node_put(tegra_rt5677_dai.codecs->of_node); 298 tegra_rt5677_dai.codecs->of_node = NULL; 299 of_node_put(tegra_rt5677_dai.cpus->of_node); 300 tegra_rt5677_dai.cpus->of_node = NULL; 301 302 return 0; 303} 304 305static const struct of_device_id tegra_rt5677_of_match[] = { 306 { .compatible = "nvidia,tegra-audio-rt5677", }, 307 {}, 308}; 309 310static struct platform_driver tegra_rt5677_driver = { 311 .driver = { 312 .name = DRV_NAME, 313 .pm = &snd_soc_pm_ops, 314 .of_match_table = tegra_rt5677_of_match, 315 }, 316 .probe = tegra_rt5677_probe, 317 .remove = tegra_rt5677_remove, 318}; 319module_platform_driver(tegra_rt5677_driver); 320 321MODULE_AUTHOR("Anatol Pomozov <anatol@google.com>"); 322MODULE_DESCRIPTION("Tegra+RT5677 machine ASoC driver"); 323MODULE_LICENSE("GPL v2"); 324MODULE_ALIAS("platform:" DRV_NAME); 325MODULE_DEVICE_TABLE(of, tegra_rt5677_of_match); 326