Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  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	.owner = THIS_MODULE,
179	.dai_link = &tegra_rt5677_dai,
180	.num_links = 1,
181	.controls = tegra_rt5677_controls,
182	.num_controls = ARRAY_SIZE(tegra_rt5677_controls),
183	.dapm_widgets = tegra_rt5677_dapm_widgets,
184	.num_dapm_widgets = ARRAY_SIZE(tegra_rt5677_dapm_widgets),
185	.fully_routed = true,
186};
187
188static int tegra_rt5677_probe(struct platform_device *pdev)
189{
190	struct device_node *np = pdev->dev.of_node;
191	struct snd_soc_card *card = &snd_soc_tegra_rt5677;
192	struct tegra_rt5677 *machine;
193	int ret;
194
195	machine = devm_kzalloc(&pdev->dev,
196			sizeof(struct tegra_rt5677), GFP_KERNEL);
197	if (!machine)
198		return -ENOMEM;
199
200	card->dev = &pdev->dev;
201	snd_soc_card_set_drvdata(card, machine);
202
203	machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0);
204	if (machine->gpio_hp_det == -EPROBE_DEFER)
205		return -EPROBE_DEFER;
206
207	machine->gpio_mic_present = of_get_named_gpio(np,
208			"nvidia,mic-present-gpios", 0);
209	if (machine->gpio_mic_present == -EPROBE_DEFER)
210		return -EPROBE_DEFER;
211
212	machine->gpio_hp_en = of_get_named_gpio(np, "nvidia,hp-en-gpios", 0);
213	if (machine->gpio_hp_en == -EPROBE_DEFER)
214		return -EPROBE_DEFER;
215	if (gpio_is_valid(machine->gpio_hp_en)) {
216		ret = devm_gpio_request_one(&pdev->dev, machine->gpio_hp_en,
217				GPIOF_OUT_INIT_LOW, "hp_en");
218		if (ret) {
219			dev_err(card->dev, "cannot get hp_en gpio\n");
220			return ret;
221		}
222	}
223
224	machine->gpio_dmic_clk_en = of_get_named_gpio(np,
225		"nvidia,dmic-clk-en-gpios", 0);
226	if (machine->gpio_dmic_clk_en == -EPROBE_DEFER)
227		return -EPROBE_DEFER;
228	if (gpio_is_valid(machine->gpio_dmic_clk_en)) {
229		ret = devm_gpio_request_one(&pdev->dev,
230				machine->gpio_dmic_clk_en,
231				GPIOF_OUT_INIT_HIGH, "dmic_clk_en");
232		if (ret) {
233			dev_err(card->dev, "cannot get dmic_clk_en gpio\n");
234			return ret;
235		}
236	}
237
238	ret = snd_soc_of_parse_card_name(card, "nvidia,model");
239	if (ret)
240		goto err;
241
242	ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing");
243	if (ret)
244		goto err;
245
246	tegra_rt5677_dai.codecs->of_node = of_parse_phandle(np,
247			"nvidia,audio-codec", 0);
248	if (!tegra_rt5677_dai.codecs->of_node) {
249		dev_err(&pdev->dev,
250			"Property 'nvidia,audio-codec' missing or invalid\n");
251		ret = -EINVAL;
252		goto err;
253	}
254
255	tegra_rt5677_dai.cpus->of_node = of_parse_phandle(np,
256			"nvidia,i2s-controller", 0);
257	if (!tegra_rt5677_dai.cpus->of_node) {
258		dev_err(&pdev->dev,
259			"Property 'nvidia,i2s-controller' missing or invalid\n");
260		ret = -EINVAL;
261		goto err_put_codec_of_node;
262	}
263	tegra_rt5677_dai.platforms->of_node = tegra_rt5677_dai.cpus->of_node;
264
265	ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
266	if (ret)
267		goto err_put_cpu_of_node;
268
269	ret = snd_soc_register_card(card);
270	if (ret) {
271		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
272			ret);
273		goto err_put_cpu_of_node;
274	}
275
276	return 0;
277
278err_put_cpu_of_node:
279	of_node_put(tegra_rt5677_dai.cpus->of_node);
280	tegra_rt5677_dai.cpus->of_node = NULL;
281	tegra_rt5677_dai.platforms->of_node = NULL;
282err_put_codec_of_node:
283	of_node_put(tegra_rt5677_dai.codecs->of_node);
284	tegra_rt5677_dai.codecs->of_node = NULL;
285err:
286	return ret;
287}
288
289static int tegra_rt5677_remove(struct platform_device *pdev)
290{
291	struct snd_soc_card *card = platform_get_drvdata(pdev);
292
293	snd_soc_unregister_card(card);
294
295	tegra_rt5677_dai.platforms->of_node = NULL;
296	of_node_put(tegra_rt5677_dai.codecs->of_node);
297	tegra_rt5677_dai.codecs->of_node = NULL;
298	of_node_put(tegra_rt5677_dai.cpus->of_node);
299	tegra_rt5677_dai.cpus->of_node = NULL;
300
301	return 0;
302}
303
304static const struct of_device_id tegra_rt5677_of_match[] = {
305	{ .compatible = "nvidia,tegra-audio-rt5677", },
306	{},
307};
308
309static struct platform_driver tegra_rt5677_driver = {
310	.driver = {
311		.name = DRV_NAME,
312		.pm = &snd_soc_pm_ops,
313		.of_match_table = tegra_rt5677_of_match,
314	},
315	.probe = tegra_rt5677_probe,
316	.remove = tegra_rt5677_remove,
317};
318module_platform_driver(tegra_rt5677_driver);
319
320MODULE_AUTHOR("Anatol Pomozov <anatol@google.com>");
321MODULE_DESCRIPTION("Tegra+RT5677 machine ASoC driver");
322MODULE_LICENSE("GPL v2");
323MODULE_ALIAS("platform:" DRV_NAME);
324MODULE_DEVICE_TABLE(of, tegra_rt5677_of_match);