xref: /linux/sound/soc/intel/boards/sof_board_helpers.c (revision 36ec807b627b4c0a0a382f0ae48eac7187d14b2b)
1 // SPDX-License-Identifier: GPL-2.0-only
2 //
3 // Copyright(c) 2023 Intel Corporation
4 
5 #include <sound/soc.h>
6 #include "../common/soc-intel-quirks.h"
7 #include "hda_dsp_common.h"
8 #include "sof_board_helpers.h"
9 
10 /*
11  * Intel HDMI DAI Link
12  */
13 static int hdmi_init(struct snd_soc_pcm_runtime *rtd)
14 {
15 	struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
16 	struct snd_soc_dai *dai = snd_soc_rtd_to_codec(rtd, 0);
17 
18 	ctx->hdmi.hdmi_comp = dai->component;
19 
20 	return 0;
21 }
22 
23 int sof_intel_board_card_late_probe(struct snd_soc_card *card)
24 {
25 	struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
26 
27 	if (!ctx->hdmi_num)
28 		return 0;
29 
30 	if (!ctx->hdmi.idisp_codec)
31 		return 0;
32 
33 	if (!ctx->hdmi.hdmi_comp)
34 		return -EINVAL;
35 
36 	return hda_dsp_hdmi_build_controls(card, ctx->hdmi.hdmi_comp);
37 }
38 EXPORT_SYMBOL_NS(sof_intel_board_card_late_probe, SND_SOC_INTEL_SOF_BOARD_HELPERS);
39 
40 /*
41  * DMIC DAI Link
42  */
43 static const struct snd_soc_dapm_widget dmic_widgets[] = {
44 	SND_SOC_DAPM_MIC("SoC DMIC", NULL),
45 };
46 
47 static const struct snd_soc_dapm_route dmic_routes[] = {
48 	{"DMic", NULL, "SoC DMIC"},
49 };
50 
51 static int dmic_init(struct snd_soc_pcm_runtime *rtd)
52 {
53 	struct snd_soc_card *card = rtd->card;
54 	int ret;
55 
56 	ret = snd_soc_dapm_new_controls(&card->dapm, dmic_widgets,
57 					ARRAY_SIZE(dmic_widgets));
58 	if (ret) {
59 		dev_err(rtd->dev, "fail to add dmic widgets, ret %d\n", ret);
60 		return ret;
61 	}
62 
63 	ret = snd_soc_dapm_add_routes(&card->dapm, dmic_routes,
64 				      ARRAY_SIZE(dmic_routes));
65 	if (ret) {
66 		dev_err(rtd->dev, "fail to add dmic routes, ret %d\n", ret);
67 		return ret;
68 	}
69 
70 	return 0;
71 }
72 
73 /*
74  * DAI Link Helpers
75  */
76 
77 enum sof_dmic_be_type {
78 	SOF_DMIC_01,
79 	SOF_DMIC_16K,
80 };
81 
82 /* DEFAULT_LINK_ORDER: the order used in sof_rt5682 */
83 #define DEFAULT_LINK_ORDER	SOF_LINK_ORDER(SOF_LINK_CODEC, \
84 					SOF_LINK_DMIC01,       \
85 					SOF_LINK_DMIC16K,      \
86 					SOF_LINK_IDISP_HDMI,   \
87 					SOF_LINK_AMP,          \
88 					SOF_LINK_BT_OFFLOAD,   \
89 					SOF_LINK_HDMI_IN)
90 
91 static struct snd_soc_dai_link_component dmic_component[] = {
92 	{
93 		.name = "dmic-codec",
94 		.dai_name = "dmic-hifi",
95 	}
96 };
97 
98 static struct snd_soc_dai_link_component platform_component[] = {
99 	{
100 		/* name might be overridden during probe */
101 		.name = "0000:00:1f.3"
102 	}
103 };
104 
105 static int set_ssp_codec_link(struct device *dev, struct snd_soc_dai_link *link,
106 			      int be_id, enum snd_soc_acpi_intel_codec codec_type,
107 			      int ssp_codec)
108 {
109 	struct snd_soc_dai_link_component *cpus;
110 
111 	dev_dbg(dev, "link %d: ssp codec %s, ssp %d\n", be_id,
112 		snd_soc_acpi_intel_get_codec_name(codec_type), ssp_codec);
113 
114 	/* link name */
115 	link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_codec);
116 	if (!link->name)
117 		return -ENOMEM;
118 
119 	/* cpus */
120 	cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
121 			    GFP_KERNEL);
122 	if (!cpus)
123 		return -ENOMEM;
124 
125 	if (soc_intel_is_byt() || soc_intel_is_cht()) {
126 		/* backward-compatibility for BYT/CHT boards */
127 		cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "ssp%d-port",
128 						ssp_codec);
129 	} else {
130 		cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin",
131 						ssp_codec);
132 	}
133 	if (!cpus->dai_name)
134 		return -ENOMEM;
135 
136 	link->cpus = cpus;
137 	link->num_cpus = 1;
138 
139 	/* codecs - caller to handle */
140 
141 	/* platforms */
142 	link->platforms = platform_component;
143 	link->num_platforms = ARRAY_SIZE(platform_component);
144 
145 	link->id = be_id;
146 	link->no_pcm = 1;
147 	link->dpcm_capture = 1;
148 	link->dpcm_playback = 1;
149 
150 	return 0;
151 }
152 
153 static int set_dmic_link(struct device *dev, struct snd_soc_dai_link *link,
154 			 int be_id, enum sof_dmic_be_type be_type)
155 {
156 	struct snd_soc_dai_link_component *cpus;
157 
158 	/* cpus */
159 	cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
160 			    GFP_KERNEL);
161 	if (!cpus)
162 		return -ENOMEM;
163 
164 	switch (be_type) {
165 	case SOF_DMIC_01:
166 		dev_dbg(dev, "link %d: dmic01\n", be_id);
167 
168 		link->name = "dmic01";
169 		cpus->dai_name = "DMIC01 Pin";
170 		break;
171 	case SOF_DMIC_16K:
172 		dev_dbg(dev, "link %d: dmic16k\n", be_id);
173 
174 		link->name = "dmic16k";
175 		cpus->dai_name = "DMIC16k Pin";
176 		break;
177 	default:
178 		dev_err(dev, "invalid be type %d\n", be_type);
179 		return -EINVAL;
180 	}
181 
182 	link->cpus = cpus;
183 	link->num_cpus = 1;
184 
185 	/* codecs */
186 	link->codecs = dmic_component;
187 	link->num_codecs = ARRAY_SIZE(dmic_component);
188 
189 	/* platforms */
190 	link->platforms = platform_component;
191 	link->num_platforms = ARRAY_SIZE(platform_component);
192 
193 	link->id = be_id;
194 	if (be_type == SOF_DMIC_01)
195 		link->init = dmic_init;
196 	link->ignore_suspend = 1;
197 	link->no_pcm = 1;
198 	link->dpcm_capture = 1;
199 
200 	return 0;
201 }
202 
203 static int set_idisp_hdmi_link(struct device *dev, struct snd_soc_dai_link *link,
204 			       int be_id, int hdmi_id, bool idisp_codec)
205 {
206 	struct snd_soc_dai_link_component *cpus, *codecs;
207 
208 	dev_dbg(dev, "link %d: idisp hdmi %d, idisp codec %d\n", be_id, hdmi_id,
209 		idisp_codec);
210 
211 	/* link name */
212 	link->name = devm_kasprintf(dev, GFP_KERNEL, "iDisp%d", hdmi_id);
213 	if (!link->name)
214 		return -ENOMEM;
215 
216 	/* cpus */
217 	cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
218 			    GFP_KERNEL);
219 	if (!cpus)
220 		return -ENOMEM;
221 
222 	cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "iDisp%d Pin", hdmi_id);
223 	if (!cpus->dai_name)
224 		return -ENOMEM;
225 
226 	link->cpus = cpus;
227 	link->num_cpus = 1;
228 
229 	/* codecs */
230 	if (idisp_codec) {
231 		codecs = devm_kzalloc(dev,
232 				      sizeof(struct snd_soc_dai_link_component),
233 				      GFP_KERNEL);
234 		if (!codecs)
235 			return -ENOMEM;
236 
237 		codecs->name = "ehdaudio0D2";
238 		codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL,
239 						  "intel-hdmi-hifi%d", hdmi_id);
240 		if (!codecs->dai_name)
241 			return -ENOMEM;
242 
243 		link->codecs = codecs;
244 	} else {
245 		link->codecs = &snd_soc_dummy_dlc;
246 	}
247 	link->num_codecs = 1;
248 
249 	/* platforms */
250 	link->platforms = platform_component;
251 	link->num_platforms = ARRAY_SIZE(platform_component);
252 
253 	link->id = be_id;
254 	link->init = (hdmi_id == 1) ? hdmi_init : NULL;
255 	link->no_pcm = 1;
256 	link->dpcm_playback = 1;
257 
258 	return 0;
259 }
260 
261 static int set_ssp_amp_link(struct device *dev, struct snd_soc_dai_link *link,
262 			    int be_id, enum snd_soc_acpi_intel_codec amp_type,
263 			    int ssp_amp)
264 {
265 	struct snd_soc_dai_link_component *cpus;
266 
267 	dev_dbg(dev, "link %d: ssp amp %s, ssp %d\n", be_id,
268 		snd_soc_acpi_intel_get_codec_name(amp_type), ssp_amp);
269 
270 	/* link name */
271 	link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_amp);
272 	if (!link->name)
273 		return -ENOMEM;
274 
275 	/* cpus */
276 	cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
277 			    GFP_KERNEL);
278 	if (!cpus)
279 		return -ENOMEM;
280 
281 	cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_amp);
282 	if (!cpus->dai_name)
283 		return -ENOMEM;
284 
285 	link->cpus = cpus;
286 	link->num_cpus = 1;
287 
288 	/* codecs - caller to handle */
289 
290 	/* platforms */
291 	link->platforms = platform_component;
292 	link->num_platforms = ARRAY_SIZE(platform_component);
293 
294 	link->id = be_id;
295 	link->no_pcm = 1;
296 	link->dpcm_capture = 1; /* feedback stream or firmware-generated echo reference */
297 	link->dpcm_playback = 1;
298 
299 	return 0;
300 }
301 
302 static int set_bt_offload_link(struct device *dev, struct snd_soc_dai_link *link,
303 			       int be_id, int ssp_bt)
304 {
305 	struct snd_soc_dai_link_component *cpus;
306 
307 	dev_dbg(dev, "link %d: bt offload, ssp %d\n", be_id, ssp_bt);
308 
309 	/* link name */
310 	link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", ssp_bt);
311 	if (!link->name)
312 		return -ENOMEM;
313 
314 	/* cpus */
315 	cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
316 			    GFP_KERNEL);
317 	if (!cpus)
318 		return -ENOMEM;
319 
320 	cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_bt);
321 	if (!cpus->dai_name)
322 		return -ENOMEM;
323 
324 	link->cpus = cpus;
325 	link->num_cpus = 1;
326 
327 	/* codecs */
328 	link->codecs = &snd_soc_dummy_dlc;
329 	link->num_codecs = 1;
330 
331 	/* platforms */
332 	link->platforms = platform_component;
333 	link->num_platforms = ARRAY_SIZE(platform_component);
334 
335 	link->id = be_id;
336 	link->no_pcm = 1;
337 	link->dpcm_capture = 1;
338 	link->dpcm_playback = 1;
339 
340 	return 0;
341 }
342 
343 static int set_hdmi_in_link(struct device *dev, struct snd_soc_dai_link *link,
344 			    int be_id, int ssp_hdmi)
345 {
346 	struct snd_soc_dai_link_component *cpus;
347 
348 	dev_dbg(dev, "link %d: hdmi-in, ssp %d\n", be_id, ssp_hdmi);
349 
350 	/* link name */
351 	link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-HDMI", ssp_hdmi);
352 	if (!link->name)
353 		return -ENOMEM;
354 
355 	/* cpus */
356 	cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
357 			    GFP_KERNEL);
358 	if (!cpus)
359 		return -ENOMEM;
360 
361 	cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_hdmi);
362 	if (!cpus->dai_name)
363 		return -ENOMEM;
364 
365 	link->cpus = cpus;
366 	link->num_cpus = 1;
367 
368 	/* codecs */
369 	link->codecs = &snd_soc_dummy_dlc;
370 	link->num_codecs = 1;
371 
372 	/* platforms */
373 	link->platforms = platform_component;
374 	link->num_platforms = ARRAY_SIZE(platform_component);
375 
376 	link->id = be_id;
377 	link->no_pcm = 1;
378 	link->dpcm_capture = 1;
379 
380 	return 0;
381 }
382 
383 static int calculate_num_links(struct sof_card_private *ctx)
384 {
385 	int num_links = 0;
386 
387 	/* headphone codec */
388 	if (ctx->codec_type != CODEC_NONE)
389 		num_links++;
390 
391 	/* dmic01 and dmic16k */
392 	if (ctx->dmic_be_num > 0)
393 		num_links++;
394 
395 	if (ctx->dmic_be_num > 1)
396 		num_links++;
397 
398 	/* idisp HDMI */
399 	num_links += ctx->hdmi_num;
400 
401 	/* speaker amp */
402 	if (ctx->amp_type != CODEC_NONE)
403 		num_links++;
404 
405 	/* BT audio offload */
406 	if (ctx->bt_offload_present)
407 		num_links++;
408 
409 	/* HDMI-In */
410 	num_links += hweight32(ctx->ssp_mask_hdmi_in);
411 
412 	return num_links;
413 }
414 
415 int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card,
416 				 struct sof_card_private *ctx)
417 {
418 	struct snd_soc_dai_link *links;
419 	int num_links;
420 	int i;
421 	int idx = 0;
422 	int ret;
423 	int ssp_hdmi_in = 0;
424 	unsigned long link_order, link;
425 	unsigned long link_ids, be_id;
426 
427 	num_links = calculate_num_links(ctx);
428 
429 	links = devm_kcalloc(dev, num_links, sizeof(struct snd_soc_dai_link),
430 			     GFP_KERNEL);
431 	if (!links)
432 		return -ENOMEM;
433 
434 	if (ctx->link_order_overwrite)
435 		link_order = ctx->link_order_overwrite;
436 	else
437 		link_order = DEFAULT_LINK_ORDER;
438 
439 	if (ctx->link_id_overwrite)
440 		link_ids = ctx->link_id_overwrite;
441 	else
442 		link_ids = 0;
443 
444 	dev_dbg(dev, "create dai links, link_order 0x%lx, id_overwrite 0x%lx\n",
445 		link_order, link_ids);
446 
447 	while (link_order) {
448 		link = link_order & SOF_LINK_ORDER_MASK;
449 		link_order >>= SOF_LINK_ORDER_SHIFT;
450 
451 		if (ctx->link_id_overwrite) {
452 			be_id = link_ids & SOF_LINK_IDS_MASK;
453 			link_ids >>= SOF_LINK_IDS_SHIFT;
454 		} else {
455 			/* use array index as link id */
456 			be_id = idx;
457 		}
458 
459 		switch (link) {
460 		case SOF_LINK_CODEC:
461 			/* headphone codec */
462 			if (ctx->codec_type == CODEC_NONE)
463 				continue;
464 
465 			ret = set_ssp_codec_link(dev, &links[idx], be_id,
466 						 ctx->codec_type, ctx->ssp_codec);
467 			if (ret) {
468 				dev_err(dev, "fail to set codec link, ret %d\n",
469 					ret);
470 				return ret;
471 			}
472 
473 			ctx->codec_link = &links[idx];
474 			idx++;
475 			break;
476 		case SOF_LINK_DMIC01:
477 			/* dmic01 */
478 			if (ctx->dmic_be_num == 0)
479 				continue;
480 
481 			/* at least we have dmic01 */
482 			ret = set_dmic_link(dev, &links[idx], be_id, SOF_DMIC_01);
483 			if (ret) {
484 				dev_err(dev, "fail to set dmic01 link, ret %d\n",
485 					ret);
486 				return ret;
487 			}
488 
489 			idx++;
490 			break;
491 		case SOF_LINK_DMIC16K:
492 			/* dmic16k */
493 			if (ctx->dmic_be_num <= 1)
494 				continue;
495 
496 			/* set up 2 BE links at most */
497 			ret = set_dmic_link(dev, &links[idx], be_id,
498 					    SOF_DMIC_16K);
499 			if (ret) {
500 				dev_err(dev, "fail to set dmic16k link, ret %d\n",
501 					ret);
502 				return ret;
503 			}
504 
505 			idx++;
506 			break;
507 		case SOF_LINK_IDISP_HDMI:
508 			/* idisp HDMI */
509 			for (i = 1; i <= ctx->hdmi_num; i++) {
510 				ret = set_idisp_hdmi_link(dev, &links[idx],
511 							  be_id, i,
512 							  ctx->hdmi.idisp_codec);
513 				if (ret) {
514 					dev_err(dev, "fail to set hdmi link, ret %d\n",
515 						ret);
516 					return ret;
517 				}
518 
519 				idx++;
520 				be_id++;
521 			}
522 			break;
523 		case SOF_LINK_AMP:
524 			/* speaker amp */
525 			if (ctx->amp_type == CODEC_NONE)
526 				continue;
527 
528 			ret = set_ssp_amp_link(dev, &links[idx], be_id,
529 					       ctx->amp_type, ctx->ssp_amp);
530 			if (ret) {
531 				dev_err(dev, "fail to set amp link, ret %d\n",
532 					ret);
533 				return ret;
534 			}
535 
536 			ctx->amp_link = &links[idx];
537 			idx++;
538 			break;
539 		case SOF_LINK_BT_OFFLOAD:
540 			/* BT audio offload */
541 			if (!ctx->bt_offload_present)
542 				continue;
543 
544 			ret = set_bt_offload_link(dev, &links[idx], be_id,
545 						  ctx->ssp_bt);
546 			if (ret) {
547 				dev_err(dev, "fail to set bt link, ret %d\n",
548 					ret);
549 				return ret;
550 			}
551 
552 			idx++;
553 			break;
554 		case SOF_LINK_HDMI_IN:
555 			/* HDMI-In */
556 			for_each_set_bit(ssp_hdmi_in, &ctx->ssp_mask_hdmi_in, 32) {
557 				ret = set_hdmi_in_link(dev, &links[idx], be_id,
558 						       ssp_hdmi_in);
559 				if (ret) {
560 					dev_err(dev, "fail to set hdmi-in link, ret %d\n",
561 						ret);
562 					return ret;
563 				}
564 
565 				idx++;
566 				be_id++;
567 			}
568 			break;
569 		case SOF_LINK_NONE:
570 			/* caught here if it's not used as terminator in macro */
571 			fallthrough;
572 		default:
573 			dev_err(dev, "invalid link type %ld\n", link);
574 			return -EINVAL;
575 		}
576 	}
577 
578 	if (idx != num_links) {
579 		dev_err(dev, "link number mismatch, idx %d, num_links %d\n", idx,
580 			num_links);
581 		return -EINVAL;
582 	}
583 
584 	card->dai_link = links;
585 	card->num_links = num_links;
586 
587 	return 0;
588 }
589 EXPORT_SYMBOL_NS(sof_intel_board_set_dai_link, SND_SOC_INTEL_SOF_BOARD_HELPERS);
590 
591 struct sof_card_private *
592 sof_intel_board_get_ctx(struct device *dev, unsigned long board_quirk)
593 {
594 	struct sof_card_private *ctx;
595 
596 	dev_dbg(dev, "create ctx, board_quirk 0x%lx\n", board_quirk);
597 
598 	ctx = devm_kzalloc(dev, sizeof(struct sof_card_private), GFP_KERNEL);
599 	if (!ctx)
600 		return NULL;
601 
602 	ctx->codec_type = snd_soc_acpi_intel_detect_codec_type(dev);
603 	ctx->amp_type = snd_soc_acpi_intel_detect_amp_type(dev);
604 
605 	ctx->dmic_be_num = 2;
606 	ctx->hdmi_num = (board_quirk & SOF_NUM_IDISP_HDMI_MASK) >>
607 			SOF_NUM_IDISP_HDMI_SHIFT;
608 	/* default number of HDMI DAI's */
609 	if (!ctx->hdmi_num)
610 		ctx->hdmi_num = 3;
611 
612 	/* port number/mask of peripherals attached to ssp interface */
613 	if (ctx->codec_type != CODEC_NONE)
614 		ctx->ssp_codec = (board_quirk & SOF_SSP_PORT_CODEC_MASK) >>
615 				SOF_SSP_PORT_CODEC_SHIFT;
616 
617 	if (ctx->amp_type != CODEC_NONE)
618 		ctx->ssp_amp = (board_quirk & SOF_SSP_PORT_AMP_MASK) >>
619 				SOF_SSP_PORT_AMP_SHIFT;
620 
621 	if (board_quirk & SOF_BT_OFFLOAD_PRESENT) {
622 		ctx->bt_offload_present = true;
623 		ctx->ssp_bt = (board_quirk & SOF_SSP_PORT_BT_OFFLOAD_MASK) >>
624 				SOF_SSP_PORT_BT_OFFLOAD_SHIFT;
625 	}
626 
627 	ctx->ssp_mask_hdmi_in = (board_quirk & SOF_SSP_MASK_HDMI_CAPTURE_MASK) >>
628 				SOF_SSP_MASK_HDMI_CAPTURE_SHIFT;
629 
630 	return ctx;
631 }
632 EXPORT_SYMBOL_NS(sof_intel_board_get_ctx, SND_SOC_INTEL_SOF_BOARD_HELPERS);
633 
634 MODULE_DESCRIPTION("ASoC Intel SOF Machine Driver Board Helpers");
635 MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
636 MODULE_LICENSE("GPL");
637 MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
638 MODULE_IMPORT_NS(SND_SOC_ACPI_INTEL_MATCH);
639