1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * tegra_asoc_machine.c - Universal ASoC machine driver for NVIDIA Tegra boards. |
4 | */ |
5 | |
6 | #include <linux/clk.h> |
7 | #include <linux/export.h> |
8 | #include <linux/gpio/consumer.h> |
9 | #include <linux/module.h> |
10 | #include <linux/of.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/slab.h> |
13 | |
14 | #include <sound/core.h> |
15 | #include <sound/jack.h> |
16 | #include <sound/pcm.h> |
17 | #include <sound/pcm_params.h> |
18 | #include <sound/soc.h> |
19 | |
20 | #include "tegra_asoc_machine.h" |
21 | |
22 | /* Headphones Jack */ |
23 | |
24 | static struct snd_soc_jack tegra_machine_hp_jack; |
25 | |
26 | static struct snd_soc_jack_pin tegra_machine_hp_jack_pins[] = { |
27 | { .pin = "Headphone" , .mask = SND_JACK_HEADPHONE }, |
28 | { .pin = "Headphones" , .mask = SND_JACK_HEADPHONE }, |
29 | }; |
30 | |
31 | static struct snd_soc_jack_gpio tegra_machine_hp_jack_gpio = { |
32 | .name = "Headphones detection" , |
33 | .report = SND_JACK_HEADPHONE, |
34 | .debounce_time = 150, |
35 | }; |
36 | |
37 | /* Headset Jack */ |
38 | |
39 | static struct snd_soc_jack tegra_machine_headset_jack; |
40 | |
41 | static struct snd_soc_jack_pin tegra_machine_headset_jack_pins[] = { |
42 | { .pin = "Headset Mic" , .mask = SND_JACK_MICROPHONE }, |
43 | { .pin = "Headset Stereophone" , .mask = SND_JACK_HEADPHONE }, |
44 | }; |
45 | |
46 | static struct snd_soc_jack_gpio tegra_machine_headset_jack_gpio = { |
47 | .name = "Headset detection" , |
48 | .report = SND_JACK_HEADSET, |
49 | .debounce_time = 150, |
50 | }; |
51 | |
52 | /* Mic Jack */ |
53 | static int coupled_mic_hp_check(void *data) |
54 | { |
55 | struct tegra_machine *machine = (struct tegra_machine *)data; |
56 | |
57 | /* Detect mic insertion only if 3.5 jack is in */ |
58 | if (gpiod_get_value_cansleep(desc: machine->gpiod_hp_det) && |
59 | gpiod_get_value_cansleep(desc: machine->gpiod_mic_det)) |
60 | return SND_JACK_MICROPHONE; |
61 | |
62 | return 0; |
63 | } |
64 | |
65 | static struct snd_soc_jack tegra_machine_mic_jack; |
66 | |
67 | static struct snd_soc_jack_pin tegra_machine_mic_jack_pins[] = { |
68 | { .pin = "Mic Jack" , .mask = SND_JACK_MICROPHONE }, |
69 | { .pin = "Headset Mic" , .mask = SND_JACK_MICROPHONE }, |
70 | }; |
71 | |
72 | static struct snd_soc_jack_gpio tegra_machine_mic_jack_gpio = { |
73 | .name = "Mic detection" , |
74 | .report = SND_JACK_MICROPHONE, |
75 | .debounce_time = 150, |
76 | }; |
77 | |
78 | static int tegra_machine_event(struct snd_soc_dapm_widget *w, |
79 | struct snd_kcontrol *k, int event) |
80 | { |
81 | struct snd_soc_dapm_context *dapm = w->dapm; |
82 | struct tegra_machine *machine = snd_soc_card_get_drvdata(card: dapm->card); |
83 | |
84 | if (!snd_soc_dapm_widget_name_cmp(widget: w, s: "Int Spk" ) || |
85 | !snd_soc_dapm_widget_name_cmp(widget: w, s: "Speakers" )) |
86 | gpiod_set_value_cansleep(desc: machine->gpiod_spkr_en, |
87 | SND_SOC_DAPM_EVENT_ON(event)); |
88 | |
89 | if (!snd_soc_dapm_widget_name_cmp(widget: w, s: "Mic Jack" ) || |
90 | !snd_soc_dapm_widget_name_cmp(widget: w, s: "Headset Mic" )) |
91 | gpiod_set_value_cansleep(desc: machine->gpiod_ext_mic_en, |
92 | SND_SOC_DAPM_EVENT_ON(event)); |
93 | |
94 | if (!snd_soc_dapm_widget_name_cmp(widget: w, s: "Int Mic" ) || |
95 | !snd_soc_dapm_widget_name_cmp(widget: w, s: "Internal Mic 2" )) |
96 | gpiod_set_value_cansleep(desc: machine->gpiod_int_mic_en, |
97 | SND_SOC_DAPM_EVENT_ON(event)); |
98 | |
99 | if (!snd_soc_dapm_widget_name_cmp(widget: w, s: "Headphone" ) || |
100 | !snd_soc_dapm_widget_name_cmp(widget: w, s: "Headphone Jack" )) |
101 | gpiod_set_value_cansleep(desc: machine->gpiod_hp_mute, |
102 | value: !SND_SOC_DAPM_EVENT_ON(event)); |
103 | |
104 | return 0; |
105 | } |
106 | |
107 | static const struct snd_soc_dapm_widget tegra_machine_dapm_widgets[] = { |
108 | SND_SOC_DAPM_HP("Headphone Jack" , tegra_machine_event), |
109 | SND_SOC_DAPM_HP("Headphone" , tegra_machine_event), |
110 | SND_SOC_DAPM_HP("Headset Stereophone" , NULL), |
111 | SND_SOC_DAPM_HP("Headphones" , NULL), |
112 | SND_SOC_DAPM_SPK("Speakers" , tegra_machine_event), |
113 | SND_SOC_DAPM_SPK("Int Spk" , tegra_machine_event), |
114 | SND_SOC_DAPM_SPK("Earpiece" , NULL), |
115 | SND_SOC_DAPM_MIC("Int Mic" , tegra_machine_event), |
116 | SND_SOC_DAPM_MIC("Mic Jack" , tegra_machine_event), |
117 | SND_SOC_DAPM_MIC("Internal Mic 1" , NULL), |
118 | SND_SOC_DAPM_MIC("Internal Mic 2" , tegra_machine_event), |
119 | SND_SOC_DAPM_MIC("Headset Mic" , tegra_machine_event), |
120 | SND_SOC_DAPM_MIC("Digital Mic" , NULL), |
121 | SND_SOC_DAPM_MIC("Mic" , NULL), |
122 | SND_SOC_DAPM_LINE("Line In Jack" , NULL), |
123 | SND_SOC_DAPM_LINE("Line In" , NULL), |
124 | SND_SOC_DAPM_LINE("LineIn" , NULL), |
125 | }; |
126 | |
127 | static const struct snd_kcontrol_new tegra_machine_controls[] = { |
128 | SOC_DAPM_PIN_SWITCH("Speakers" ), |
129 | SOC_DAPM_PIN_SWITCH("Int Spk" ), |
130 | SOC_DAPM_PIN_SWITCH("Earpiece" ), |
131 | SOC_DAPM_PIN_SWITCH("Int Mic" ), |
132 | SOC_DAPM_PIN_SWITCH("Headset Mic" ), |
133 | SOC_DAPM_PIN_SWITCH("Internal Mic 1" ), |
134 | SOC_DAPM_PIN_SWITCH("Internal Mic 2" ), |
135 | SOC_DAPM_PIN_SWITCH("Headphones" ), |
136 | SOC_DAPM_PIN_SWITCH("Mic Jack" ), |
137 | }; |
138 | |
139 | int tegra_asoc_machine_init(struct snd_soc_pcm_runtime *rtd) |
140 | { |
141 | struct snd_soc_card *card = rtd->card; |
142 | struct tegra_machine *machine = snd_soc_card_get_drvdata(card); |
143 | const char *jack_name; |
144 | int err; |
145 | |
146 | if (machine->gpiod_hp_det && machine->asoc->add_hp_jack) { |
147 | if (machine->asoc->hp_jack_name) |
148 | jack_name = machine->asoc->hp_jack_name; |
149 | else |
150 | jack_name = "Headphones Jack" ; |
151 | |
152 | err = snd_soc_card_jack_new_pins(card, id: jack_name, |
153 | type: SND_JACK_HEADPHONE, |
154 | jack: &tegra_machine_hp_jack, |
155 | pins: tegra_machine_hp_jack_pins, |
156 | ARRAY_SIZE(tegra_machine_hp_jack_pins)); |
157 | if (err) { |
158 | dev_err(rtd->dev, |
159 | "Headphones Jack creation failed: %d\n" , err); |
160 | return err; |
161 | } |
162 | |
163 | tegra_machine_hp_jack_gpio.desc = machine->gpiod_hp_det; |
164 | |
165 | err = snd_soc_jack_add_gpios(jack: &tegra_machine_hp_jack, count: 1, |
166 | gpios: &tegra_machine_hp_jack_gpio); |
167 | if (err) |
168 | dev_err(rtd->dev, "HP GPIOs not added: %d\n" , err); |
169 | } |
170 | |
171 | if (machine->gpiod_hp_det && machine->asoc->add_headset_jack) { |
172 | err = snd_soc_card_jack_new_pins(card, id: "Headset Jack" , |
173 | type: SND_JACK_HEADSET, |
174 | jack: &tegra_machine_headset_jack, |
175 | pins: tegra_machine_headset_jack_pins, |
176 | ARRAY_SIZE(tegra_machine_headset_jack_pins)); |
177 | if (err) { |
178 | dev_err(rtd->dev, |
179 | "Headset Jack creation failed: %d\n" , err); |
180 | return err; |
181 | } |
182 | |
183 | tegra_machine_headset_jack_gpio.desc = machine->gpiod_hp_det; |
184 | |
185 | err = snd_soc_jack_add_gpios(jack: &tegra_machine_headset_jack, count: 1, |
186 | gpios: &tegra_machine_headset_jack_gpio); |
187 | if (err) |
188 | dev_err(rtd->dev, "Headset GPIOs not added: %d\n" , err); |
189 | } |
190 | |
191 | if (machine->gpiod_mic_det && machine->asoc->add_mic_jack) { |
192 | err = snd_soc_card_jack_new_pins(card: rtd->card, id: "Mic Jack" , |
193 | type: SND_JACK_MICROPHONE, |
194 | jack: &tegra_machine_mic_jack, |
195 | pins: tegra_machine_mic_jack_pins, |
196 | ARRAY_SIZE(tegra_machine_mic_jack_pins)); |
197 | if (err) { |
198 | dev_err(rtd->dev, "Mic Jack creation failed: %d\n" , err); |
199 | return err; |
200 | } |
201 | |
202 | tegra_machine_mic_jack_gpio.data = machine; |
203 | tegra_machine_mic_jack_gpio.desc = machine->gpiod_mic_det; |
204 | |
205 | if (of_property_read_bool(np: card->dev->of_node, |
206 | propname: "nvidia,coupled-mic-hp-det" )) { |
207 | tegra_machine_mic_jack_gpio.desc = machine->gpiod_hp_det; |
208 | tegra_machine_mic_jack_gpio.jack_status_check = coupled_mic_hp_check; |
209 | } |
210 | |
211 | err = snd_soc_jack_add_gpios(jack: &tegra_machine_mic_jack, count: 1, |
212 | gpios: &tegra_machine_mic_jack_gpio); |
213 | if (err) |
214 | dev_err(rtd->dev, "Mic GPIOs not added: %d\n" , err); |
215 | } |
216 | |
217 | return 0; |
218 | } |
219 | EXPORT_SYMBOL_GPL(tegra_asoc_machine_init); |
220 | |
221 | static unsigned int tegra_machine_mclk_rate_128(unsigned int srate) |
222 | { |
223 | return 128 * srate; |
224 | } |
225 | |
226 | static unsigned int tegra_machine_mclk_rate_256(unsigned int srate) |
227 | { |
228 | return 256 * srate; |
229 | } |
230 | |
231 | static unsigned int tegra_machine_mclk_rate_512(unsigned int srate) |
232 | { |
233 | return 512 * srate; |
234 | } |
235 | |
236 | static unsigned int tegra_machine_mclk_rate_12mhz(unsigned int srate) |
237 | { |
238 | unsigned int mclk; |
239 | |
240 | switch (srate) { |
241 | case 8000: |
242 | case 16000: |
243 | case 24000: |
244 | case 32000: |
245 | case 48000: |
246 | case 64000: |
247 | case 96000: |
248 | mclk = 12288000; |
249 | break; |
250 | case 11025: |
251 | case 22050: |
252 | case 44100: |
253 | case 88200: |
254 | mclk = 11289600; |
255 | break; |
256 | default: |
257 | mclk = 12000000; |
258 | break; |
259 | } |
260 | |
261 | return mclk; |
262 | } |
263 | |
264 | static unsigned int tegra_machine_mclk_rate_6mhz(unsigned int srate) |
265 | { |
266 | unsigned int mclk; |
267 | |
268 | switch (srate) { |
269 | case 8000: |
270 | case 16000: |
271 | case 64000: |
272 | mclk = 8192000; |
273 | break; |
274 | case 11025: |
275 | case 22050: |
276 | case 88200: |
277 | mclk = 11289600; |
278 | break; |
279 | case 96000: |
280 | mclk = 12288000; |
281 | break; |
282 | default: |
283 | mclk = 256 * srate; |
284 | break; |
285 | } |
286 | |
287 | return mclk; |
288 | } |
289 | |
290 | static int tegra_machine_hw_params(struct snd_pcm_substream *substream, |
291 | struct snd_pcm_hw_params *params) |
292 | { |
293 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
294 | struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); |
295 | struct snd_soc_card *card = rtd->card; |
296 | struct tegra_machine *machine = snd_soc_card_get_drvdata(card); |
297 | unsigned int srate = params_rate(p: params); |
298 | unsigned int mclk = machine->asoc->mclk_rate(srate); |
299 | unsigned int clk_id = machine->asoc->mclk_id; |
300 | unsigned int new_baseclock; |
301 | int err; |
302 | |
303 | switch (srate) { |
304 | case 11025: |
305 | case 22050: |
306 | case 44100: |
307 | case 88200: |
308 | if (of_machine_is_compatible(compat: "nvidia,tegra20" )) |
309 | new_baseclock = 56448000; |
310 | else if (of_machine_is_compatible(compat: "nvidia,tegra30" )) |
311 | new_baseclock = 564480000; |
312 | else |
313 | new_baseclock = 282240000; |
314 | break; |
315 | case 8000: |
316 | case 16000: |
317 | case 32000: |
318 | case 48000: |
319 | case 64000: |
320 | case 96000: |
321 | if (of_machine_is_compatible(compat: "nvidia,tegra20" )) |
322 | new_baseclock = 73728000; |
323 | else if (of_machine_is_compatible(compat: "nvidia,tegra30" )) |
324 | new_baseclock = 552960000; |
325 | else |
326 | new_baseclock = 368640000; |
327 | break; |
328 | default: |
329 | dev_err(card->dev, "Invalid sound rate: %u\n" , srate); |
330 | return -EINVAL; |
331 | } |
332 | |
333 | if (new_baseclock != machine->set_baseclock || |
334 | mclk != machine->set_mclk) { |
335 | machine->set_baseclock = 0; |
336 | machine->set_mclk = 0; |
337 | |
338 | clk_disable_unprepare(clk: machine->clk_cdev1); |
339 | |
340 | err = clk_set_rate(clk: machine->clk_pll_a, rate: new_baseclock); |
341 | if (err) { |
342 | dev_err(card->dev, "Can't set pll_a rate: %d\n" , err); |
343 | return err; |
344 | } |
345 | |
346 | err = clk_set_rate(clk: machine->clk_pll_a_out0, rate: mclk); |
347 | if (err) { |
348 | dev_err(card->dev, "Can't set pll_a_out0 rate: %d\n" , err); |
349 | return err; |
350 | } |
351 | |
352 | /* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */ |
353 | |
354 | err = clk_prepare_enable(clk: machine->clk_cdev1); |
355 | if (err) { |
356 | dev_err(card->dev, "Can't enable cdev1: %d\n" , err); |
357 | return err; |
358 | } |
359 | |
360 | machine->set_baseclock = new_baseclock; |
361 | machine->set_mclk = mclk; |
362 | } |
363 | |
364 | err = snd_soc_dai_set_sysclk(dai: codec_dai, clk_id, freq: mclk, SND_SOC_CLOCK_IN); |
365 | if (err < 0) { |
366 | dev_err(card->dev, "codec_dai clock not set: %d\n" , err); |
367 | return err; |
368 | } |
369 | |
370 | return 0; |
371 | } |
372 | |
373 | static const struct snd_soc_ops tegra_machine_snd_ops = { |
374 | .hw_params = tegra_machine_hw_params, |
375 | }; |
376 | |
377 | static void tegra_machine_node_release(void *of_node) |
378 | { |
379 | of_node_put(node: of_node); |
380 | } |
381 | |
382 | static struct device_node * |
383 | tegra_machine_parse_phandle(struct device *dev, const char *name) |
384 | { |
385 | struct device_node *np; |
386 | int err; |
387 | |
388 | np = of_parse_phandle(np: dev->of_node, phandle_name: name, index: 0); |
389 | if (!np) { |
390 | dev_err(dev, "Property '%s' missing or invalid\n" , name); |
391 | return ERR_PTR(error: -EINVAL); |
392 | } |
393 | |
394 | err = devm_add_action_or_reset(dev, tegra_machine_node_release, np); |
395 | if (err) |
396 | return ERR_PTR(error: err); |
397 | |
398 | return np; |
399 | } |
400 | |
401 | static void tegra_machine_unregister_codec(void *pdev) |
402 | { |
403 | platform_device_unregister(pdev); |
404 | } |
405 | |
406 | static int tegra_machine_register_codec(struct device *dev, const char *name) |
407 | { |
408 | struct platform_device *pdev; |
409 | int err; |
410 | |
411 | if (!name) |
412 | return 0; |
413 | |
414 | pdev = platform_device_register_simple(name, id: -1, NULL, num: 0); |
415 | if (IS_ERR(ptr: pdev)) |
416 | return PTR_ERR(ptr: pdev); |
417 | |
418 | err = devm_add_action_or_reset(dev, tegra_machine_unregister_codec, |
419 | pdev); |
420 | if (err) |
421 | return err; |
422 | |
423 | return 0; |
424 | } |
425 | |
426 | int tegra_asoc_machine_probe(struct platform_device *pdev) |
427 | { |
428 | struct device_node *np_codec, *np_i2s, *np_ac97; |
429 | const struct tegra_asoc_data *asoc; |
430 | struct device *dev = &pdev->dev; |
431 | struct tegra_machine *machine; |
432 | struct snd_soc_card *card; |
433 | struct gpio_desc *gpiod; |
434 | int err; |
435 | |
436 | machine = devm_kzalloc(dev, size: sizeof(*machine), GFP_KERNEL); |
437 | if (!machine) |
438 | return -ENOMEM; |
439 | |
440 | asoc = of_device_get_match_data(dev); |
441 | card = asoc->card; |
442 | card->dev = dev; |
443 | |
444 | machine->asoc = asoc; |
445 | machine->mic_jack = &tegra_machine_mic_jack; |
446 | machine->hp_jack_gpio = &tegra_machine_hp_jack_gpio; |
447 | snd_soc_card_set_drvdata(card, data: machine); |
448 | |
449 | gpiod = devm_gpiod_get_optional(dev, con_id: "nvidia,hp-mute" , flags: GPIOD_OUT_HIGH); |
450 | machine->gpiod_hp_mute = gpiod; |
451 | if (IS_ERR(ptr: gpiod)) |
452 | return PTR_ERR(ptr: gpiod); |
453 | |
454 | gpiod = devm_gpiod_get_optional(dev, con_id: "nvidia,hp-det" , flags: GPIOD_IN); |
455 | machine->gpiod_hp_det = gpiod; |
456 | if (IS_ERR(ptr: gpiod)) |
457 | return PTR_ERR(ptr: gpiod); |
458 | |
459 | gpiod = devm_gpiod_get_optional(dev, con_id: "nvidia,mic-det" , flags: GPIOD_IN); |
460 | machine->gpiod_mic_det = gpiod; |
461 | if (IS_ERR(ptr: gpiod)) |
462 | return PTR_ERR(ptr: gpiod); |
463 | |
464 | gpiod = devm_gpiod_get_optional(dev, con_id: "nvidia,spkr-en" , flags: GPIOD_OUT_LOW); |
465 | machine->gpiod_spkr_en = gpiod; |
466 | if (IS_ERR(ptr: gpiod)) |
467 | return PTR_ERR(ptr: gpiod); |
468 | |
469 | gpiod = devm_gpiod_get_optional(dev, con_id: "nvidia,int-mic-en" , flags: GPIOD_OUT_LOW); |
470 | machine->gpiod_int_mic_en = gpiod; |
471 | if (IS_ERR(ptr: gpiod)) |
472 | return PTR_ERR(ptr: gpiod); |
473 | |
474 | gpiod = devm_gpiod_get_optional(dev, con_id: "nvidia,ext-mic-en" , flags: GPIOD_OUT_LOW); |
475 | machine->gpiod_ext_mic_en = gpiod; |
476 | if (IS_ERR(ptr: gpiod)) |
477 | return PTR_ERR(ptr: gpiod); |
478 | |
479 | err = snd_soc_of_parse_card_name(card, propname: "nvidia,model" ); |
480 | if (err) |
481 | return err; |
482 | |
483 | if (!card->dapm_routes) { |
484 | err = snd_soc_of_parse_audio_routing(card, propname: "nvidia,audio-routing" ); |
485 | if (err) |
486 | return err; |
487 | } |
488 | |
489 | if (asoc->set_ac97) { |
490 | err = tegra_machine_register_codec(dev, name: asoc->codec_dev_name); |
491 | if (err) |
492 | return err; |
493 | |
494 | np_ac97 = tegra_machine_parse_phandle(dev, name: "nvidia,ac97-controller" ); |
495 | if (IS_ERR(ptr: np_ac97)) |
496 | return PTR_ERR(ptr: np_ac97); |
497 | |
498 | card->dai_link->cpus->of_node = np_ac97; |
499 | card->dai_link->platforms->of_node = np_ac97; |
500 | } else { |
501 | np_codec = tegra_machine_parse_phandle(dev, name: "nvidia,audio-codec" ); |
502 | if (IS_ERR(ptr: np_codec)) |
503 | return PTR_ERR(ptr: np_codec); |
504 | |
505 | np_i2s = tegra_machine_parse_phandle(dev, name: "nvidia,i2s-controller" ); |
506 | if (IS_ERR(ptr: np_i2s)) |
507 | return PTR_ERR(ptr: np_i2s); |
508 | |
509 | card->dai_link->cpus->of_node = np_i2s; |
510 | card->dai_link->codecs->of_node = np_codec; |
511 | card->dai_link->platforms->of_node = np_i2s; |
512 | } |
513 | |
514 | if (asoc->add_common_controls) { |
515 | card->controls = tegra_machine_controls; |
516 | card->num_controls = ARRAY_SIZE(tegra_machine_controls); |
517 | } |
518 | |
519 | if (asoc->add_common_dapm_widgets) { |
520 | card->dapm_widgets = tegra_machine_dapm_widgets; |
521 | card->num_dapm_widgets = ARRAY_SIZE(tegra_machine_dapm_widgets); |
522 | } |
523 | |
524 | if (asoc->add_common_snd_ops) |
525 | card->dai_link->ops = &tegra_machine_snd_ops; |
526 | |
527 | if (!card->owner) |
528 | card->owner = THIS_MODULE; |
529 | if (!card->driver_name) |
530 | card->driver_name = "tegra" ; |
531 | |
532 | machine->clk_pll_a = devm_clk_get(dev, id: "pll_a" ); |
533 | if (IS_ERR(ptr: machine->clk_pll_a)) { |
534 | dev_err(dev, "Can't retrieve clk pll_a\n" ); |
535 | return PTR_ERR(ptr: machine->clk_pll_a); |
536 | } |
537 | |
538 | machine->clk_pll_a_out0 = devm_clk_get(dev, id: "pll_a_out0" ); |
539 | if (IS_ERR(ptr: machine->clk_pll_a_out0)) { |
540 | dev_err(dev, "Can't retrieve clk pll_a_out0\n" ); |
541 | return PTR_ERR(ptr: machine->clk_pll_a_out0); |
542 | } |
543 | |
544 | machine->clk_cdev1 = devm_clk_get(dev, id: "mclk" ); |
545 | if (IS_ERR(ptr: machine->clk_cdev1)) { |
546 | dev_err(dev, "Can't retrieve clk cdev1\n" ); |
547 | return PTR_ERR(ptr: machine->clk_cdev1); |
548 | } |
549 | |
550 | /* |
551 | * If clock parents are not set in DT, configure here to use clk_out_1 |
552 | * as mclk and extern1 as parent for Tegra30 and higher. |
553 | */ |
554 | if (!of_property_present(np: dev->of_node, propname: "assigned-clock-parents" ) && |
555 | !of_machine_is_compatible(compat: "nvidia,tegra20" )) { |
556 | struct clk *clk_out_1, *clk_extern1; |
557 | |
558 | dev_warn(dev, "Configuring clocks for a legacy device-tree\n" ); |
559 | dev_warn(dev, "Please update DT to use assigned-clock-parents\n" ); |
560 | |
561 | clk_extern1 = devm_clk_get(dev, id: "extern1" ); |
562 | if (IS_ERR(ptr: clk_extern1)) { |
563 | dev_err(dev, "Can't retrieve clk extern1\n" ); |
564 | return PTR_ERR(ptr: clk_extern1); |
565 | } |
566 | |
567 | err = clk_set_parent(clk: clk_extern1, parent: machine->clk_pll_a_out0); |
568 | if (err < 0) { |
569 | dev_err(dev, "Set parent failed for clk extern1\n" ); |
570 | return err; |
571 | } |
572 | |
573 | clk_out_1 = devm_clk_get(dev, id: "pmc_clk_out_1" ); |
574 | if (IS_ERR(ptr: clk_out_1)) { |
575 | dev_err(dev, "Can't retrieve pmc_clk_out_1\n" ); |
576 | return PTR_ERR(ptr: clk_out_1); |
577 | } |
578 | |
579 | err = clk_set_parent(clk: clk_out_1, parent: clk_extern1); |
580 | if (err < 0) { |
581 | dev_err(dev, "Set parent failed for pmc_clk_out_1\n" ); |
582 | return err; |
583 | } |
584 | |
585 | machine->clk_cdev1 = clk_out_1; |
586 | } |
587 | |
588 | if (asoc->set_ac97) { |
589 | /* |
590 | * AC97 rate is fixed at 24.576MHz and is used for both the |
591 | * host controller and the external codec |
592 | */ |
593 | err = clk_set_rate(clk: machine->clk_pll_a, rate: 73728000); |
594 | if (err) { |
595 | dev_err(dev, "Can't set pll_a rate: %d\n" , err); |
596 | return err; |
597 | } |
598 | |
599 | err = clk_set_rate(clk: machine->clk_pll_a_out0, rate: 24576000); |
600 | if (err) { |
601 | dev_err(dev, "Can't set pll_a_out0 rate: %d\n" , err); |
602 | return err; |
603 | } |
604 | |
605 | machine->set_baseclock = 73728000; |
606 | machine->set_mclk = 24576000; |
607 | } |
608 | |
609 | /* |
610 | * FIXME: There is some unknown dependency between audio MCLK disable |
611 | * and suspend-resume functionality on Tegra30, although audio MCLK is |
612 | * only needed for audio. |
613 | */ |
614 | err = clk_prepare_enable(clk: machine->clk_cdev1); |
615 | if (err) { |
616 | dev_err(dev, "Can't enable cdev1: %d\n" , err); |
617 | return err; |
618 | } |
619 | |
620 | err = devm_snd_soc_register_card(dev, card); |
621 | if (err) |
622 | return err; |
623 | |
624 | return 0; |
625 | } |
626 | EXPORT_SYMBOL_GPL(tegra_asoc_machine_probe); |
627 | |
628 | /* WM8753 machine */ |
629 | |
630 | SND_SOC_DAILINK_DEFS(wm8753_hifi, |
631 | DAILINK_COMP_ARRAY(COMP_EMPTY()), |
632 | DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8753-hifi" )), |
633 | DAILINK_COMP_ARRAY(COMP_EMPTY())); |
634 | |
635 | static struct snd_soc_dai_link tegra_wm8753_dai = { |
636 | .name = "WM8753" , |
637 | .stream_name = "WM8753 PCM" , |
638 | .dai_fmt = SND_SOC_DAIFMT_I2S | |
639 | SND_SOC_DAIFMT_NB_NF | |
640 | SND_SOC_DAIFMT_CBS_CFS, |
641 | SND_SOC_DAILINK_REG(wm8753_hifi), |
642 | }; |
643 | |
644 | static struct snd_soc_card snd_soc_tegra_wm8753 = { |
645 | .components = "codec:wm8753" , |
646 | .dai_link = &tegra_wm8753_dai, |
647 | .num_links = 1, |
648 | .fully_routed = true, |
649 | }; |
650 | |
651 | static const struct tegra_asoc_data tegra_wm8753_data = { |
652 | .mclk_rate = tegra_machine_mclk_rate_12mhz, |
653 | .card = &snd_soc_tegra_wm8753, |
654 | .add_common_dapm_widgets = true, |
655 | .add_common_snd_ops = true, |
656 | }; |
657 | |
658 | /* WM9712 machine */ |
659 | |
660 | static int tegra_wm9712_init(struct snd_soc_pcm_runtime *rtd) |
661 | { |
662 | return snd_soc_dapm_force_enable_pin(dapm: &rtd->card->dapm, pin: "Mic Bias" ); |
663 | } |
664 | |
665 | SND_SOC_DAILINK_DEFS(wm9712_hifi, |
666 | DAILINK_COMP_ARRAY(COMP_EMPTY()), |
667 | DAILINK_COMP_ARRAY(COMP_CODEC("wm9712-codec" , "wm9712-hifi" )), |
668 | DAILINK_COMP_ARRAY(COMP_EMPTY())); |
669 | |
670 | static struct snd_soc_dai_link tegra_wm9712_dai = { |
671 | .name = "AC97 HiFi" , |
672 | .stream_name = "AC97 HiFi" , |
673 | .init = tegra_wm9712_init, |
674 | SND_SOC_DAILINK_REG(wm9712_hifi), |
675 | }; |
676 | |
677 | static struct snd_soc_card snd_soc_tegra_wm9712 = { |
678 | .components = "codec:wm9712" , |
679 | .dai_link = &tegra_wm9712_dai, |
680 | .num_links = 1, |
681 | .fully_routed = true, |
682 | }; |
683 | |
684 | static const struct tegra_asoc_data tegra_wm9712_data = { |
685 | .card = &snd_soc_tegra_wm9712, |
686 | .add_common_dapm_widgets = true, |
687 | .codec_dev_name = "wm9712-codec" , |
688 | .set_ac97 = true, |
689 | }; |
690 | |
691 | /* MAX98090 machine */ |
692 | |
693 | SND_SOC_DAILINK_DEFS(max98090_hifi, |
694 | DAILINK_COMP_ARRAY(COMP_EMPTY()), |
695 | DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi" )), |
696 | DAILINK_COMP_ARRAY(COMP_EMPTY())); |
697 | |
698 | static struct snd_soc_dai_link tegra_max98090_dai = { |
699 | .name = "max98090" , |
700 | .stream_name = "max98090 PCM" , |
701 | .init = tegra_asoc_machine_init, |
702 | .dai_fmt = SND_SOC_DAIFMT_I2S | |
703 | SND_SOC_DAIFMT_NB_NF | |
704 | SND_SOC_DAIFMT_CBS_CFS, |
705 | SND_SOC_DAILINK_REG(max98090_hifi), |
706 | }; |
707 | |
708 | static struct snd_soc_card snd_soc_tegra_max98090 = { |
709 | .components = "codec:max98090" , |
710 | .dai_link = &tegra_max98090_dai, |
711 | .num_links = 1, |
712 | .fully_routed = true, |
713 | }; |
714 | |
715 | static const struct tegra_asoc_data tegra_max98090_data = { |
716 | .mclk_rate = tegra_machine_mclk_rate_12mhz, |
717 | .card = &snd_soc_tegra_max98090, |
718 | .hp_jack_name = "Headphones" , |
719 | .add_common_dapm_widgets = true, |
720 | .add_common_controls = true, |
721 | .add_common_snd_ops = true, |
722 | .add_mic_jack = true, |
723 | .add_hp_jack = true, |
724 | }; |
725 | |
726 | /* MAX98088 machine */ |
727 | |
728 | SND_SOC_DAILINK_DEFS(max98088_hifi, |
729 | DAILINK_COMP_ARRAY(COMP_EMPTY()), |
730 | DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi" )), |
731 | DAILINK_COMP_ARRAY(COMP_EMPTY())); |
732 | |
733 | static struct snd_soc_dai_link tegra_max98088_dai = { |
734 | .name = "MAX98088" , |
735 | .stream_name = "MAX98088 PCM" , |
736 | .init = tegra_asoc_machine_init, |
737 | .dai_fmt = SND_SOC_DAIFMT_I2S | |
738 | SND_SOC_DAIFMT_NB_NF | |
739 | SND_SOC_DAIFMT_CBS_CFS, |
740 | SND_SOC_DAILINK_REG(max98088_hifi), |
741 | }; |
742 | |
743 | static struct snd_soc_card snd_soc_tegra_max98088 = { |
744 | .components = "codec:max98088" , |
745 | .dai_link = &tegra_max98088_dai, |
746 | .num_links = 1, |
747 | .fully_routed = true, |
748 | }; |
749 | |
750 | static const struct tegra_asoc_data tegra_max98088_data = { |
751 | .mclk_rate = tegra_machine_mclk_rate_12mhz, |
752 | .card = &snd_soc_tegra_max98088, |
753 | .add_common_dapm_widgets = true, |
754 | .add_common_controls = true, |
755 | .add_common_snd_ops = true, |
756 | .add_mic_jack = true, |
757 | .add_hp_jack = true, |
758 | }; |
759 | |
760 | /* SGTL5000 machine */ |
761 | |
762 | SND_SOC_DAILINK_DEFS(sgtl5000_hifi, |
763 | DAILINK_COMP_ARRAY(COMP_EMPTY()), |
764 | DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "sgtl5000" )), |
765 | DAILINK_COMP_ARRAY(COMP_EMPTY())); |
766 | |
767 | static struct snd_soc_dai_link tegra_sgtl5000_dai = { |
768 | .name = "sgtl5000" , |
769 | .stream_name = "HiFi" , |
770 | .dai_fmt = SND_SOC_DAIFMT_I2S | |
771 | SND_SOC_DAIFMT_NB_NF | |
772 | SND_SOC_DAIFMT_CBS_CFS, |
773 | SND_SOC_DAILINK_REG(sgtl5000_hifi), |
774 | }; |
775 | |
776 | static struct snd_soc_card snd_soc_tegra_sgtl5000 = { |
777 | .components = "codec:sgtl5000" , |
778 | .dai_link = &tegra_sgtl5000_dai, |
779 | .num_links = 1, |
780 | .fully_routed = true, |
781 | }; |
782 | |
783 | static const struct tegra_asoc_data tegra_sgtl5000_data = { |
784 | .mclk_rate = tegra_machine_mclk_rate_12mhz, |
785 | .card = &snd_soc_tegra_sgtl5000, |
786 | .add_common_dapm_widgets = true, |
787 | .add_common_snd_ops = true, |
788 | }; |
789 | |
790 | /* TLV320AIC23 machine */ |
791 | |
792 | static const struct snd_soc_dapm_widget trimslice_dapm_widgets[] = { |
793 | SND_SOC_DAPM_HP("Line Out" , NULL), |
794 | SND_SOC_DAPM_LINE("Line In" , NULL), |
795 | }; |
796 | |
797 | static const struct snd_soc_dapm_route trimslice_audio_map[] = { |
798 | {"Line Out" , NULL, "LOUT" }, |
799 | {"Line Out" , NULL, "ROUT" }, |
800 | |
801 | {"LLINEIN" , NULL, "Line In" }, |
802 | {"RLINEIN" , NULL, "Line In" }, |
803 | }; |
804 | |
805 | SND_SOC_DAILINK_DEFS(tlv320aic23_hifi, |
806 | DAILINK_COMP_ARRAY(COMP_EMPTY()), |
807 | DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "tlv320aic23-hifi" )), |
808 | DAILINK_COMP_ARRAY(COMP_EMPTY())); |
809 | |
810 | static struct snd_soc_dai_link tegra_tlv320aic23_dai = { |
811 | .name = "TLV320AIC23" , |
812 | .stream_name = "AIC23" , |
813 | .dai_fmt = SND_SOC_DAIFMT_I2S | |
814 | SND_SOC_DAIFMT_NB_NF | |
815 | SND_SOC_DAIFMT_CBS_CFS, |
816 | SND_SOC_DAILINK_REG(tlv320aic23_hifi), |
817 | }; |
818 | |
819 | static struct snd_soc_card snd_soc_tegra_trimslice = { |
820 | .name = "tegra-trimslice" , |
821 | .components = "codec:tlv320aic23" , |
822 | .dai_link = &tegra_tlv320aic23_dai, |
823 | .num_links = 1, |
824 | .dapm_widgets = trimslice_dapm_widgets, |
825 | .num_dapm_widgets = ARRAY_SIZE(trimslice_dapm_widgets), |
826 | .dapm_routes = trimslice_audio_map, |
827 | .num_dapm_routes = ARRAY_SIZE(trimslice_audio_map), |
828 | .fully_routed = true, |
829 | }; |
830 | |
831 | static const struct tegra_asoc_data tegra_trimslice_data = { |
832 | .mclk_rate = tegra_machine_mclk_rate_128, |
833 | .card = &snd_soc_tegra_trimslice, |
834 | .add_common_snd_ops = true, |
835 | }; |
836 | |
837 | /* RT5677 machine */ |
838 | |
839 | static int tegra_rt5677_init(struct snd_soc_pcm_runtime *rtd) |
840 | { |
841 | struct snd_soc_card *card = rtd->card; |
842 | int err; |
843 | |
844 | err = tegra_asoc_machine_init(rtd); |
845 | if (err) |
846 | return err; |
847 | |
848 | snd_soc_dapm_force_enable_pin(dapm: &card->dapm, pin: "MICBIAS1" ); |
849 | |
850 | return 0; |
851 | } |
852 | |
853 | SND_SOC_DAILINK_DEFS(rt5677_aif1, |
854 | DAILINK_COMP_ARRAY(COMP_EMPTY()), |
855 | DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5677-aif1" )), |
856 | DAILINK_COMP_ARRAY(COMP_EMPTY())); |
857 | |
858 | static struct snd_soc_dai_link tegra_rt5677_dai = { |
859 | .name = "RT5677" , |
860 | .stream_name = "RT5677 PCM" , |
861 | .init = tegra_rt5677_init, |
862 | .dai_fmt = SND_SOC_DAIFMT_I2S | |
863 | SND_SOC_DAIFMT_NB_NF | |
864 | SND_SOC_DAIFMT_CBS_CFS, |
865 | SND_SOC_DAILINK_REG(rt5677_aif1), |
866 | }; |
867 | |
868 | static struct snd_soc_card snd_soc_tegra_rt5677 = { |
869 | .components = "codec:rt5677" , |
870 | .dai_link = &tegra_rt5677_dai, |
871 | .num_links = 1, |
872 | .fully_routed = true, |
873 | }; |
874 | |
875 | static const struct tegra_asoc_data tegra_rt5677_data = { |
876 | .mclk_rate = tegra_machine_mclk_rate_256, |
877 | .card = &snd_soc_tegra_rt5677, |
878 | .add_common_dapm_widgets = true, |
879 | .add_common_controls = true, |
880 | .add_common_snd_ops = true, |
881 | .add_mic_jack = true, |
882 | .add_hp_jack = true, |
883 | }; |
884 | |
885 | /* RT5640 machine */ |
886 | |
887 | SND_SOC_DAILINK_DEFS(rt5640_aif1, |
888 | DAILINK_COMP_ARRAY(COMP_EMPTY()), |
889 | DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5640-aif1" )), |
890 | DAILINK_COMP_ARRAY(COMP_EMPTY())); |
891 | |
892 | static struct snd_soc_dai_link tegra_rt5640_dai = { |
893 | .name = "RT5640" , |
894 | .stream_name = "RT5640 PCM" , |
895 | .init = tegra_asoc_machine_init, |
896 | .dai_fmt = SND_SOC_DAIFMT_I2S | |
897 | SND_SOC_DAIFMT_NB_NF | |
898 | SND_SOC_DAIFMT_CBS_CFS, |
899 | SND_SOC_DAILINK_REG(rt5640_aif1), |
900 | }; |
901 | |
902 | static struct snd_soc_card snd_soc_tegra_rt5640 = { |
903 | .components = "codec:rt5640" , |
904 | .dai_link = &tegra_rt5640_dai, |
905 | .num_links = 1, |
906 | .fully_routed = true, |
907 | }; |
908 | |
909 | static const struct tegra_asoc_data tegra_rt5640_data = { |
910 | .mclk_rate = tegra_machine_mclk_rate_256, |
911 | .card = &snd_soc_tegra_rt5640, |
912 | .add_common_dapm_widgets = true, |
913 | .add_common_controls = true, |
914 | .add_common_snd_ops = true, |
915 | .add_hp_jack = true, |
916 | }; |
917 | |
918 | /* RT5632 machine */ |
919 | |
920 | SND_SOC_DAILINK_DEFS(rt5632_hifi, |
921 | DAILINK_COMP_ARRAY(COMP_EMPTY()), |
922 | DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "alc5632-hifi" )), |
923 | DAILINK_COMP_ARRAY(COMP_EMPTY())); |
924 | |
925 | static struct snd_soc_dai_link tegra_rt5632_dai = { |
926 | .name = "ALC5632" , |
927 | .stream_name = "ALC5632 PCM" , |
928 | .init = tegra_rt5677_init, |
929 | .dai_fmt = SND_SOC_DAIFMT_I2S | |
930 | SND_SOC_DAIFMT_NB_NF | |
931 | SND_SOC_DAIFMT_CBS_CFS, |
932 | SND_SOC_DAILINK_REG(rt5632_hifi), |
933 | }; |
934 | |
935 | static struct snd_soc_card snd_soc_tegra_rt5632 = { |
936 | .components = "codec:rt5632" , |
937 | .dai_link = &tegra_rt5632_dai, |
938 | .num_links = 1, |
939 | .fully_routed = true, |
940 | }; |
941 | |
942 | static const struct tegra_asoc_data tegra_rt5632_data = { |
943 | .mclk_rate = tegra_machine_mclk_rate_512, |
944 | .card = &snd_soc_tegra_rt5632, |
945 | .add_common_dapm_widgets = true, |
946 | .add_common_controls = true, |
947 | .add_common_snd_ops = true, |
948 | .add_headset_jack = true, |
949 | }; |
950 | |
951 | /* RT5631 machine */ |
952 | |
953 | SND_SOC_DAILINK_DEFS(rt5631_hifi, |
954 | DAILINK_COMP_ARRAY(COMP_EMPTY()), |
955 | DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5631-hifi" )), |
956 | DAILINK_COMP_ARRAY(COMP_EMPTY())); |
957 | |
958 | static struct snd_soc_dai_link tegra_rt5631_dai = { |
959 | .name = "RT5631" , |
960 | .stream_name = "RT5631 PCM" , |
961 | .init = tegra_asoc_machine_init, |
962 | .dai_fmt = SND_SOC_DAIFMT_I2S | |
963 | SND_SOC_DAIFMT_NB_NF | |
964 | SND_SOC_DAIFMT_CBS_CFS, |
965 | SND_SOC_DAILINK_REG(rt5631_hifi), |
966 | }; |
967 | |
968 | static struct snd_soc_card snd_soc_tegra_rt5631 = { |
969 | .components = "codec:rt5631" , |
970 | .dai_link = &tegra_rt5631_dai, |
971 | .num_links = 1, |
972 | .fully_routed = true, |
973 | }; |
974 | |
975 | static const struct tegra_asoc_data tegra_rt5631_data = { |
976 | .mclk_rate = tegra_machine_mclk_rate_6mhz, |
977 | .card = &snd_soc_tegra_rt5631, |
978 | .add_common_dapm_widgets = true, |
979 | .add_common_controls = true, |
980 | .add_common_snd_ops = true, |
981 | .add_mic_jack = true, |
982 | .add_hp_jack = true, |
983 | }; |
984 | |
985 | static const struct of_device_id tegra_machine_of_match[] = { |
986 | { .compatible = "nvidia,tegra-audio-trimslice" , .data = &tegra_trimslice_data }, |
987 | { .compatible = "nvidia,tegra-audio-max98090" , .data = &tegra_max98090_data }, |
988 | { .compatible = "nvidia,tegra-audio-max98088" , .data = &tegra_max98088_data }, |
989 | { .compatible = "nvidia,tegra-audio-max98089" , .data = &tegra_max98088_data }, |
990 | { .compatible = "nvidia,tegra-audio-sgtl5000" , .data = &tegra_sgtl5000_data }, |
991 | { .compatible = "nvidia,tegra-audio-wm9712" , .data = &tegra_wm9712_data }, |
992 | { .compatible = "nvidia,tegra-audio-wm8753" , .data = &tegra_wm8753_data }, |
993 | { .compatible = "nvidia,tegra-audio-rt5677" , .data = &tegra_rt5677_data }, |
994 | { .compatible = "nvidia,tegra-audio-rt5640" , .data = &tegra_rt5640_data }, |
995 | { .compatible = "nvidia,tegra-audio-alc5632" , .data = &tegra_rt5632_data }, |
996 | { .compatible = "nvidia,tegra-audio-rt5631" , .data = &tegra_rt5631_data }, |
997 | {}, |
998 | }; |
999 | MODULE_DEVICE_TABLE(of, tegra_machine_of_match); |
1000 | |
1001 | static struct platform_driver tegra_asoc_machine_driver = { |
1002 | .driver = { |
1003 | .name = "tegra-audio" , |
1004 | .of_match_table = tegra_machine_of_match, |
1005 | .pm = &snd_soc_pm_ops, |
1006 | }, |
1007 | .probe = tegra_asoc_machine_probe, |
1008 | }; |
1009 | module_platform_driver(tegra_asoc_machine_driver); |
1010 | |
1011 | MODULE_AUTHOR("Anatol Pomozov <anatol@google.com>" ); |
1012 | MODULE_AUTHOR("Andrey Danin <danindrey@mail.ru>" ); |
1013 | MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>" ); |
1014 | MODULE_AUTHOR("Ion Agorria <ion@agorria.com>" ); |
1015 | MODULE_AUTHOR("Leon Romanovsky <leon@leon.nu>" ); |
1016 | MODULE_AUTHOR("Lucas Stach <dev@lynxeye.de>" ); |
1017 | MODULE_AUTHOR("Marc Dietrich <marvin24@gmx.de>" ); |
1018 | MODULE_AUTHOR("Marcel Ziswiler <marcel@ziswiler.com>" ); |
1019 | MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>" ); |
1020 | MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>" ); |
1021 | MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>" ); |
1022 | MODULE_DESCRIPTION("Tegra machine ASoC driver" ); |
1023 | MODULE_LICENSE("GPL" ); |
1024 | |