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 */
13static 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
23int 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}
38EXPORT_SYMBOL_NS(sof_intel_board_card_late_probe, SND_SOC_INTEL_SOF_BOARD_HELPERS);
39
40/*
41 * DMIC DAI Link
42 */
43static const struct snd_soc_dapm_widget dmic_widgets[] = {
44	SND_SOC_DAPM_MIC("SoC DMIC", NULL),
45};
46
47static const struct snd_soc_dapm_route dmic_routes[] = {
48	{"DMic", NULL, "SoC DMIC"},
49};
50
51static 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
77enum 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
91static struct snd_soc_dai_link_component dmic_component[] = {
92	{
93		.name = "dmic-codec",
94		.dai_name = "dmic-hifi",
95	}
96};
97
98static 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
105static 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
153static 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
203static 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
261static 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
302static 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
343static 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
383static 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
415int 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}
589EXPORT_SYMBOL_NS(sof_intel_board_set_dai_link, SND_SOC_INTEL_SOF_BOARD_HELPERS);
590
591struct sof_card_private *
592sof_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}
632EXPORT_SYMBOL_NS(sof_intel_board_get_ctx, SND_SOC_INTEL_SOF_BOARD_HELPERS);
633
634MODULE_DESCRIPTION("ASoC Intel SOF Machine Driver Board Helpers");
635MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
636MODULE_LICENSE("GPL");
637MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
638MODULE_IMPORT_NS(SND_SOC_ACPI_INTEL_MATCH);
639