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
24static struct snd_soc_jack tegra_machine_hp_jack;
25
26static 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
31static 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
39static struct snd_soc_jack tegra_machine_headset_jack;
40
41static 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
46static 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 */
53static 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
65static struct snd_soc_jack tegra_machine_mic_jack;
66
67static 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
72static 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
78static 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
107static 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
127static 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
139int 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}
219EXPORT_SYMBOL_GPL(tegra_asoc_machine_init);
220
221static unsigned int tegra_machine_mclk_rate_128(unsigned int srate)
222{
223 return 128 * srate;
224}
225
226static unsigned int tegra_machine_mclk_rate_256(unsigned int srate)
227{
228 return 256 * srate;
229}
230
231static unsigned int tegra_machine_mclk_rate_512(unsigned int srate)
232{
233 return 512 * srate;
234}
235
236static 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
264static 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
290static 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
373static const struct snd_soc_ops tegra_machine_snd_ops = {
374 .hw_params = tegra_machine_hw_params,
375};
376
377static void tegra_machine_node_release(void *of_node)
378{
379 of_node_put(node: of_node);
380}
381
382static struct device_node *
383tegra_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
401static void tegra_machine_unregister_codec(void *pdev)
402{
403 platform_device_unregister(pdev);
404}
405
406static 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
426int 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}
626EXPORT_SYMBOL_GPL(tegra_asoc_machine_probe);
627
628/* WM8753 machine */
629
630SND_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
635static 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
644static 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
651static 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
660static 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
665SND_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
670static 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
677static 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
684static 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
693SND_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
698static 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
708static 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
715static 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
728SND_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
733static 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
743static 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
750static 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
762SND_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
767static 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
776static 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
783static 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
792static 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
797static 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
805SND_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
810static 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
819static 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
831static 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
839static 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
853SND_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
858static 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
868static 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
875static 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
887SND_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
892static 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
902static 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
909static 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
920SND_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
925static 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
935static 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
942static 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
953SND_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
958static 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
968static 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
975static 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
985static 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};
999MODULE_DEVICE_TABLE(of, tegra_machine_of_match);
1000
1001static 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};
1009module_platform_driver(tegra_asoc_machine_driver);
1010
1011MODULE_AUTHOR("Anatol Pomozov <anatol@google.com>");
1012MODULE_AUTHOR("Andrey Danin <danindrey@mail.ru>");
1013MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");
1014MODULE_AUTHOR("Ion Agorria <ion@agorria.com>");
1015MODULE_AUTHOR("Leon Romanovsky <leon@leon.nu>");
1016MODULE_AUTHOR("Lucas Stach <dev@lynxeye.de>");
1017MODULE_AUTHOR("Marc Dietrich <marvin24@gmx.de>");
1018MODULE_AUTHOR("Marcel Ziswiler <marcel@ziswiler.com>");
1019MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
1020MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
1021MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
1022MODULE_DESCRIPTION("Tegra machine ASoC driver");
1023MODULE_LICENSE("GPL");
1024

source code of linux/sound/soc/tegra/tegra_asoc_machine.c