• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
31 struct tegra_alc5632 {
32 	struct tegra_asoc_utils_data util_data;
33 	int gpio_hp_det;
34 };
35 
tegra_alc5632_asoc_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)36 static 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 
65 static const struct snd_soc_ops tegra_alc5632_asoc_ops = {
66 	.hw_params = tegra_alc5632_asoc_hw_params,
67 };
68 
69 static struct snd_soc_jack tegra_alc5632_hs_jack;
70 
71 static 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 
82 static 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 
88 static 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 
95 static const struct snd_kcontrol_new tegra_alc5632_controls[] = {
96 	SOC_DAPM_PIN_SWITCH("Int Spk"),
97 };
98 
tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime * rtd)99 static 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 
124 SND_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 
129 static 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 
140 static 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 
tegra_alc5632_probe(struct platform_device * pdev)153 static 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 
214 err_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;
218 err_put_codec_of_node:
219 	of_node_put(tegra_alc5632_dai.codecs->of_node);
220 	tegra_alc5632_dai.codecs->of_node = NULL;
221 err:
222 	return ret;
223 }
224 
tegra_alc5632_remove(struct platform_device * pdev)225 static 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 
240 static const struct of_device_id tegra_alc5632_of_match[] = {
241 	{ .compatible = "nvidia,tegra-audio-alc5632", },
242 	{},
243 };
244 
245 static 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 };
254 module_platform_driver(tegra_alc5632_driver);
255 
256 MODULE_AUTHOR("Leon Romanovsky <leon@leon.nu>");
257 MODULE_DESCRIPTION("Tegra+ALC5632 machine ASoC driver");
258 MODULE_LICENSE("GPL");
259 MODULE_ALIAS("platform:" DRV_NAME);
260 MODULE_DEVICE_TABLE(of, tegra_alc5632_of_match);
261