xref: /linux/sound/soc/intel/boards/sof_board_helpers.c (revision ef9226cd56b718c79184a3466d32984a51cb449c)
1 // SPDX-License-Identifier: GPL-2.0-only
2 //
3 // Copyright(c) 2023 Intel Corporation. All rights reserved.
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 /* DEFAULT_LINK_ORDER: the order used in sof_rt5682 */
78 #define DEFAULT_LINK_ORDER	SOF_LINK_ORDER(SOF_LINK_CODEC, \
79 					SOF_LINK_DMIC01,       \
80 					SOF_LINK_DMIC16K,      \
81 					SOF_LINK_IDISP_HDMI,   \
82 					SOF_LINK_AMP,          \
83 					SOF_LINK_BT_OFFLOAD,   \
84 					SOF_LINK_HDMI_IN)
85 
86 static struct snd_soc_dai_link_component dmic_component[] = {
87 	{
88 		.name = "dmic-codec",
89 		.dai_name = "dmic-hifi",
90 	}
91 };
92 
93 static struct snd_soc_dai_link_component platform_component[] = {
94 	{
95 		/* name might be overridden during probe */
96 		.name = "0000:00:1f.3"
97 	}
98 };
99 
100 int sof_intel_board_set_codec_link(struct device *dev,
101 				   struct snd_soc_dai_link *link, int be_id,
102 				   enum sof_ssp_codec codec_type, int ssp_codec)
103 {
104 	struct snd_soc_dai_link_component *cpus;
105 
106 	dev_dbg(dev, "link %d: codec %s, ssp %d\n", be_id,
107 		sof_ssp_get_codec_name(codec_type), ssp_codec);
108 
109 	/* link name */
110 	link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_codec);
111 	if (!link->name)
112 		return -ENOMEM;
113 
114 	/* cpus */
115 	cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
116 			    GFP_KERNEL);
117 	if (!cpus)
118 		return -ENOMEM;
119 
120 	if (soc_intel_is_byt() || soc_intel_is_cht()) {
121 		/* backward-compatibility for BYT/CHT boards */
122 		cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "ssp%d-port",
123 						ssp_codec);
124 	} else {
125 		cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin",
126 						ssp_codec);
127 	}
128 	if (!cpus->dai_name)
129 		return -ENOMEM;
130 
131 	link->cpus = cpus;
132 	link->num_cpus = 1;
133 
134 	/* codecs - caller to handle */
135 
136 	/* platforms */
137 	link->platforms = platform_component;
138 	link->num_platforms = ARRAY_SIZE(platform_component);
139 
140 	link->id = be_id;
141 	link->no_pcm = 1;
142 	link->dpcm_capture = 1;
143 	link->dpcm_playback = 1;
144 
145 	return 0;
146 }
147 EXPORT_SYMBOL_NS(sof_intel_board_set_codec_link, SND_SOC_INTEL_SOF_BOARD_HELPERS);
148 
149 int sof_intel_board_set_dmic_link(struct device *dev,
150 				  struct snd_soc_dai_link *link, int be_id,
151 				  enum sof_dmic_be_type be_type)
152 {
153 	struct snd_soc_dai_link_component *cpus;
154 
155 	/* cpus */
156 	cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
157 			    GFP_KERNEL);
158 	if (!cpus)
159 		return -ENOMEM;
160 
161 	switch (be_type) {
162 	case SOF_DMIC_01:
163 		dev_dbg(dev, "link %d: dmic01\n", be_id);
164 
165 		link->name = "dmic01";
166 		cpus->dai_name = "DMIC01 Pin";
167 		break;
168 	case SOF_DMIC_16K:
169 		dev_dbg(dev, "link %d: dmic16k\n", be_id);
170 
171 		link->name = "dmic16k";
172 		cpus->dai_name = "DMIC16k Pin";
173 		break;
174 	default:
175 		dev_err(dev, "invalid be type %d\n", be_type);
176 		return -EINVAL;
177 	}
178 
179 	link->cpus = cpus;
180 	link->num_cpus = 1;
181 
182 	/* codecs */
183 	link->codecs = dmic_component;
184 	link->num_codecs = ARRAY_SIZE(dmic_component);
185 
186 	/* platforms */
187 	link->platforms = platform_component;
188 	link->num_platforms = ARRAY_SIZE(platform_component);
189 
190 	link->id = be_id;
191 	if (be_type == SOF_DMIC_01)
192 		link->init = dmic_init;
193 	link->ignore_suspend = 1;
194 	link->no_pcm = 1;
195 	link->dpcm_capture = 1;
196 
197 	return 0;
198 }
199 EXPORT_SYMBOL_NS(sof_intel_board_set_dmic_link, SND_SOC_INTEL_SOF_BOARD_HELPERS);
200 
201 int sof_intel_board_set_intel_hdmi_link(struct device *dev,
202 					struct snd_soc_dai_link *link, int be_id,
203 					int hdmi_id, bool idisp_codec)
204 {
205 	struct snd_soc_dai_link_component *cpus, *codecs;
206 
207 	dev_dbg(dev, "link %d: intel hdmi, hdmi id %d, idisp codec %d\n",
208 		be_id, hdmi_id, idisp_codec);
209 
210 	/* link name */
211 	link->name = devm_kasprintf(dev, GFP_KERNEL, "iDisp%d", hdmi_id);
212 	if (!link->name)
213 		return -ENOMEM;
214 
215 	/* cpus */
216 	cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
217 			    GFP_KERNEL);
218 	if (!cpus)
219 		return -ENOMEM;
220 
221 	cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "iDisp%d Pin", hdmi_id);
222 	if (!cpus->dai_name)
223 		return -ENOMEM;
224 
225 	link->cpus = cpus;
226 	link->num_cpus = 1;
227 
228 	/* codecs */
229 	if (idisp_codec) {
230 		codecs = devm_kzalloc(dev,
231 				      sizeof(struct snd_soc_dai_link_component),
232 				      GFP_KERNEL);
233 		if (!codecs)
234 			return -ENOMEM;
235 
236 		codecs->name = "ehdaudio0D2";
237 		codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL,
238 						  "intel-hdmi-hifi%d", hdmi_id);
239 		if (!codecs->dai_name)
240 			return -ENOMEM;
241 
242 		link->codecs = codecs;
243 	} else {
244 		link->codecs = &snd_soc_dummy_dlc;
245 	}
246 	link->num_codecs = 1;
247 
248 	/* platforms */
249 	link->platforms = platform_component;
250 	link->num_platforms = ARRAY_SIZE(platform_component);
251 
252 	link->id = be_id;
253 	link->init = (hdmi_id == 1) ? hdmi_init : NULL;
254 	link->no_pcm = 1;
255 	link->dpcm_playback = 1;
256 
257 	return 0;
258 }
259 EXPORT_SYMBOL_NS(sof_intel_board_set_intel_hdmi_link, SND_SOC_INTEL_SOF_BOARD_HELPERS);
260 
261 int sof_intel_board_set_ssp_amp_link(struct device *dev,
262 				     struct snd_soc_dai_link *link, int be_id,
263 				     enum sof_ssp_codec amp_type, 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 		sof_ssp_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 EXPORT_SYMBOL_NS(sof_intel_board_set_ssp_amp_link, SND_SOC_INTEL_SOF_BOARD_HELPERS);
302 
303 int sof_intel_board_set_bt_link(struct device *dev,
304 				struct snd_soc_dai_link *link, int be_id,
305 				int ssp_bt)
306 {
307 	struct snd_soc_dai_link_component *cpus;
308 
309 	dev_dbg(dev, "link %d: bt offload, ssp %d\n", be_id, ssp_bt);
310 
311 	/* link name */
312 	link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", ssp_bt);
313 	if (!link->name)
314 		return -ENOMEM;
315 
316 	/* cpus */
317 	cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
318 			    GFP_KERNEL);
319 	if (!cpus)
320 		return -ENOMEM;
321 
322 	cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_bt);
323 	if (!cpus->dai_name)
324 		return -ENOMEM;
325 
326 	link->cpus = cpus;
327 	link->num_cpus = 1;
328 
329 	/* codecs */
330 	link->codecs = &snd_soc_dummy_dlc;
331 	link->num_codecs = 1;
332 
333 	/* platforms */
334 	link->platforms = platform_component;
335 	link->num_platforms = ARRAY_SIZE(platform_component);
336 
337 	link->id = be_id;
338 	link->no_pcm = 1;
339 	link->dpcm_capture = 1;
340 	link->dpcm_playback = 1;
341 
342 	return 0;
343 }
344 EXPORT_SYMBOL_NS(sof_intel_board_set_bt_link, SND_SOC_INTEL_SOF_BOARD_HELPERS);
345 
346 int sof_intel_board_set_hdmi_in_link(struct device *dev,
347 				     struct snd_soc_dai_link *link, int be_id,
348 				     int ssp_hdmi)
349 {
350 	struct snd_soc_dai_link_component *cpus;
351 
352 	dev_dbg(dev, "link %d: hdmi-in, ssp %d\n", be_id, ssp_hdmi);
353 
354 	/* link name */
355 	link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-HDMI", ssp_hdmi);
356 	if (!link->name)
357 		return -ENOMEM;
358 
359 	/* cpus */
360 	cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
361 			    GFP_KERNEL);
362 	if (!cpus)
363 		return -ENOMEM;
364 
365 	cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_hdmi);
366 	if (!cpus->dai_name)
367 		return -ENOMEM;
368 
369 	link->cpus = cpus;
370 	link->num_cpus = 1;
371 
372 	/* codecs */
373 	link->codecs = &snd_soc_dummy_dlc;
374 	link->num_codecs = 1;
375 
376 	/* platforms */
377 	link->platforms = platform_component;
378 	link->num_platforms = ARRAY_SIZE(platform_component);
379 
380 	link->id = be_id;
381 	link->no_pcm = 1;
382 	link->dpcm_capture = 1;
383 
384 	return 0;
385 }
386 EXPORT_SYMBOL_NS(sof_intel_board_set_hdmi_in_link, SND_SOC_INTEL_SOF_BOARD_HELPERS);
387 
388 static int calculate_num_links(struct sof_card_private *ctx)
389 {
390 	int num_links = 0;
391 
392 	/* headphone codec */
393 	if (ctx->codec_type != CODEC_NONE)
394 		num_links++;
395 
396 	/* dmic01 and dmic16k */
397 	if (ctx->dmic_be_num > 0)
398 		num_links++;
399 
400 	if (ctx->dmic_be_num > 1)
401 		num_links++;
402 
403 	/* idisp HDMI */
404 	num_links += ctx->hdmi_num;
405 
406 	/* speaker amp */
407 	if (ctx->amp_type != CODEC_NONE)
408 		num_links++;
409 
410 	/* BT audio offload */
411 	if (ctx->bt_offload_present)
412 		num_links++;
413 
414 	/* HDMI-In */
415 	num_links += hweight32(ctx->ssp_mask_hdmi_in);
416 
417 	return num_links;
418 }
419 
420 int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card,
421 				 struct sof_card_private *ctx)
422 {
423 	struct snd_soc_dai_link *links;
424 	int num_links;
425 	int i;
426 	int idx = 0;
427 	int ret;
428 	int ssp_hdmi_in = 0;
429 	unsigned long link_order, link;
430 
431 	num_links = calculate_num_links(ctx);
432 
433 	links = devm_kcalloc(dev, num_links, sizeof(struct snd_soc_dai_link),
434 			     GFP_KERNEL);
435 	if (!links)
436 		return -ENOMEM;
437 
438 	if (ctx->link_order_overwrite)
439 		link_order = ctx->link_order_overwrite;
440 	else
441 		link_order = DEFAULT_LINK_ORDER;
442 
443 	dev_dbg(dev, "create dai links, link_order 0x%lx\n", link_order);
444 
445 	while (link_order) {
446 		link = link_order & SOF_LINK_ORDER_MASK;
447 		link_order >>= SOF_LINK_ORDER_SHIFT;
448 
449 		switch (link) {
450 		case SOF_LINK_CODEC:
451 			/* headphone codec */
452 			if (ctx->codec_type == CODEC_NONE)
453 				continue;
454 
455 			ret = sof_intel_board_set_codec_link(dev, &links[idx],
456 							     idx,
457 							     ctx->codec_type,
458 							     ctx->ssp_codec);
459 			if (ret) {
460 				dev_err(dev, "fail to set codec link, ret %d\n",
461 					ret);
462 				return ret;
463 			}
464 
465 			ctx->codec_link = &links[idx];
466 			idx++;
467 			break;
468 		case SOF_LINK_DMIC01:
469 			/* dmic01 */
470 			if (ctx->dmic_be_num == 0)
471 				continue;
472 
473 			/* at least we have dmic01 */
474 			ret = sof_intel_board_set_dmic_link(dev, &links[idx],
475 							    idx, SOF_DMIC_01);
476 			if (ret) {
477 				dev_err(dev, "fail to set dmic01 link, ret %d\n",
478 					ret);
479 				return ret;
480 			}
481 
482 			idx++;
483 			break;
484 		case SOF_LINK_DMIC16K:
485 			/* dmic16k */
486 			if (ctx->dmic_be_num <= 1)
487 				continue;
488 
489 			/* set up 2 BE links at most */
490 			ret = sof_intel_board_set_dmic_link(dev, &links[idx],
491 							    idx, SOF_DMIC_16K);
492 			if (ret) {
493 				dev_err(dev, "fail to set dmic16k link, ret %d\n",
494 					ret);
495 				return ret;
496 			}
497 
498 			idx++;
499 			break;
500 		case SOF_LINK_IDISP_HDMI:
501 			/* idisp HDMI */
502 			for (i = 1; i <= ctx->hdmi_num; i++) {
503 				ret = sof_intel_board_set_intel_hdmi_link(dev,
504 									  &links[idx],
505 									  idx, i,
506 									  ctx->hdmi.idisp_codec);
507 				if (ret) {
508 					dev_err(dev, "fail to set hdmi link, ret %d\n",
509 						ret);
510 					return ret;
511 				}
512 
513 				idx++;
514 			}
515 			break;
516 		case SOF_LINK_AMP:
517 			/* speaker amp */
518 			if (ctx->amp_type == CODEC_NONE)
519 				continue;
520 
521 			ret = sof_intel_board_set_ssp_amp_link(dev, &links[idx],
522 							       idx,
523 							       ctx->amp_type,
524 							       ctx->ssp_amp);
525 			if (ret) {
526 				dev_err(dev, "fail to set amp link, ret %d\n",
527 					ret);
528 				return ret;
529 			}
530 
531 			ctx->amp_link = &links[idx];
532 			idx++;
533 			break;
534 		case SOF_LINK_BT_OFFLOAD:
535 			/* BT audio offload */
536 			if (!ctx->bt_offload_present)
537 				continue;
538 
539 			ret = sof_intel_board_set_bt_link(dev, &links[idx], idx,
540 							  ctx->ssp_bt);
541 			if (ret) {
542 				dev_err(dev, "fail to set bt link, ret %d\n",
543 					ret);
544 				return ret;
545 			}
546 
547 			idx++;
548 			break;
549 		case SOF_LINK_HDMI_IN:
550 			/* HDMI-In */
551 			for_each_set_bit(ssp_hdmi_in, &ctx->ssp_mask_hdmi_in, 32) {
552 				ret = sof_intel_board_set_hdmi_in_link(dev,
553 								       &links[idx],
554 								       idx,
555 								       ssp_hdmi_in);
556 				if (ret) {
557 					dev_err(dev, "fail to set hdmi-in link, ret %d\n",
558 						ret);
559 					return ret;
560 				}
561 
562 				idx++;
563 			}
564 			break;
565 		case SOF_LINK_NONE:
566 			/* caught here if it's not used as terminator in macro */
567 			fallthrough;
568 		default:
569 			dev_err(dev, "invalid link type %ld\n", link);
570 			return -EINVAL;
571 		}
572 	}
573 
574 	if (idx != num_links) {
575 		dev_err(dev, "link number mismatch, idx %d, num_links %d\n", idx,
576 			num_links);
577 		return -EINVAL;
578 	}
579 
580 	card->dai_link = links;
581 	card->num_links = num_links;
582 
583 	return 0;
584 }
585 EXPORT_SYMBOL_NS(sof_intel_board_set_dai_link, SND_SOC_INTEL_SOF_BOARD_HELPERS);
586 
587 struct snd_soc_dai *get_codec_dai_by_name(struct snd_soc_pcm_runtime *rtd,
588 					  const char * const dai_name[], int num_dais)
589 {
590 	struct snd_soc_dai *dai;
591 	int index;
592 	int i;
593 
594 	for (index = 0; index < num_dais; index++)
595 		for_each_rtd_codec_dais(rtd, i, dai)
596 			if (strstr(dai->name, dai_name[index])) {
597 				dev_dbg(rtd->card->dev, "get dai %s\n", dai->name);
598 				return dai;
599 			}
600 
601 	return NULL;
602 }
603 EXPORT_SYMBOL_NS(get_codec_dai_by_name, SND_SOC_INTEL_SOF_BOARD_HELPERS);
604 
605 MODULE_DESCRIPTION("ASoC Intel SOF Machine Driver Board Helpers");
606 MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
607 MODULE_LICENSE("GPL");
608 MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
609 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_SSP_COMMON);
610