xref: /linux/sound/soc/intel/common/sof-function-topology-lib.c (revision 0d0e98603f9f2870fdd6c8df8ed851cf975082f8)
1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2 //
3 // This file is provided under a dual BSD/GPLv2 license.  When using or
4 // redistributing this file, you may do so under either license.
5 //
6 // Copyright(c) 2025 Intel Corporation.
7 //
8 
9 #include <linux/device.h>
10 #include <linux/errno.h>
11 #include <linux/firmware.h>
12 #include <sound/soc.h>
13 #include <sound/soc-acpi.h>
14 #include "sof-function-topology-lib.h"
15 
16 enum tplg_device_id {
17 	TPLG_DEVICE_SDCA_JACK,
18 	TPLG_DEVICE_SDCA_AMP,
19 	TPLG_DEVICE_SDCA_MIC,
20 	TPLG_DEVICE_INTEL_PCH_DMIC,
21 	TPLG_DEVICE_HDMI,
22 	TPLG_DEVICE_LOOPBACK_VIRTUAL,
23 	TPLG_DEVICE_MAX
24 };
25 
26 #define SDCA_DEVICE_MASK (BIT(TPLG_DEVICE_SDCA_JACK) | BIT(TPLG_DEVICE_SDCA_AMP) | \
27 			  BIT(TPLG_DEVICE_SDCA_MIC))
28 
29 #define SOF_INTEL_PLATFORM_NAME_MAX 4
30 
31 int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach,
32 			   const char *prefix, const char ***tplg_files, bool best_effort)
33 {
34 	struct snd_soc_acpi_mach_params mach_params = mach->mach_params;
35 	struct snd_soc_dai_link *dai_link;
36 	const struct firmware *fw;
37 	char platform[SOF_INTEL_PLATFORM_NAME_MAX];
38 	unsigned long tplg_mask = 0;
39 	int tplg_num = 0;
40 	int tplg_dev;
41 	int ret;
42 	int i;
43 
44 	ret = sscanf(mach->sof_tplg_filename, "sof-%3s-*.tplg", platform);
45 	if (ret != 1) {
46 		dev_err(card->dev, "Invalid platform name %s of tplg %s\n",
47 			platform, mach->sof_tplg_filename);
48 		return -EINVAL;
49 	}
50 
51 	for_each_card_prelinks(card, i, dai_link) {
52 		char *tplg_dev_name;
53 
54 		dev_dbg(card->dev, "dai_link %s id %d\n", dai_link->name, dai_link->id);
55 		if (strstr(dai_link->name, "SimpleJack")) {
56 			tplg_dev = TPLG_DEVICE_SDCA_JACK;
57 			tplg_dev_name = "sdca-jack";
58 		} else if (strstr(dai_link->name, "SmartAmp")) {
59 			tplg_dev = TPLG_DEVICE_SDCA_AMP;
60 			tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL,
61 						       "sdca-%damp", dai_link->num_cpus);
62 			if (!tplg_dev_name)
63 				return -ENOMEM;
64 		} else if (strstr(dai_link->name, "SmartMic")) {
65 			tplg_dev = TPLG_DEVICE_SDCA_MIC;
66 			tplg_dev_name = "sdca-mic";
67 		} else if (strstr(dai_link->name, "dmic")) {
68 			switch (mach_params.dmic_num) {
69 			case 2:
70 				tplg_dev_name = "dmic-2ch";
71 				break;
72 			case 4:
73 				tplg_dev_name = "dmic-4ch";
74 				break;
75 			default:
76 				dev_warn(card->dev,
77 					 "unsupported number of dmics: %d\n",
78 					 mach_params.dmic_num);
79 				continue;
80 			}
81 			tplg_dev = TPLG_DEVICE_INTEL_PCH_DMIC;
82 		} else if (strstr(dai_link->name, "iDisp")) {
83 			tplg_dev = TPLG_DEVICE_HDMI;
84 			tplg_dev_name = "hdmi-pcm5";
85 		} else if (strstr(dai_link->name, "Loopback_Virtual")) {
86 			tplg_dev = TPLG_DEVICE_LOOPBACK_VIRTUAL;
87 			/*
88 			 * Mark the LOOPBACK_VIRTUAL device but no need to create the
89 			 * LOOPBACK_VIRTUAL topology. Just to avoid the dai_link is not supported
90 			 * error.
91 			 */
92 			tplg_mask |= BIT(tplg_dev);
93 			continue;
94 		} else {
95 			/* The dai link is not supported by separated tplg yet */
96 			dev_dbg(card->dev,
97 				"dai_link %s is not supported by separated tplg yet\n",
98 				dai_link->name);
99 			if (best_effort)
100 				continue;
101 
102 			return 0;
103 		}
104 		if (tplg_mask & BIT(tplg_dev))
105 			continue;
106 
107 		tplg_mask |= BIT(tplg_dev);
108 
109 		/*
110 		 * The tplg file naming rule is sof-<platform>-<function>-id<BE id number>.tplg
111 		 * where <platform> is only required for the DMIC function as the nhlt blob
112 		 * is platform dependent.
113 		 */
114 		switch (tplg_dev) {
115 		case TPLG_DEVICE_INTEL_PCH_DMIC:
116 			(*tplg_files)[tplg_num] = devm_kasprintf(card->dev, GFP_KERNEL,
117 								 "%s/sof-%s-%s-id%d.tplg",
118 								 prefix, platform,
119 								 tplg_dev_name, dai_link->id);
120 			break;
121 		default:
122 			(*tplg_files)[tplg_num] = devm_kasprintf(card->dev, GFP_KERNEL,
123 								 "%s/sof-%s-id%d.tplg",
124 								 prefix, tplg_dev_name,
125 								 dai_link->id);
126 			break;
127 		}
128 		if (!(*tplg_files)[tplg_num])
129 			return -ENOMEM;
130 		tplg_num++;
131 	}
132 
133 	dev_dbg(card->dev, "tplg_mask %#lx tplg_num %d\n", tplg_mask, tplg_num);
134 
135 	/* Check presence of sub-topologies */
136 	for (i = 0; i < tplg_num; i++) {
137 		ret = firmware_request_nowarn(&fw, (*tplg_files)[i], card->dev);
138 		if (!ret) {
139 			release_firmware(fw);
140 		} else {
141 			dev_warn(card->dev,
142 				 "Failed to open topology file: %s, you might need to\n",
143 				 (*tplg_files)[i]);
144 			dev_warn(card->dev,
145 				 "download it from https://github.com/thesofproject/sof-bin/\n");
146 			return 0;
147 		}
148 	}
149 
150 	return tplg_num;
151 }
152 EXPORT_SYMBOL_GPL(sof_sdw_get_tplg_files);
153