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) 2023 Intel Corporation 7 // 8 9 #include <linux/firmware.h> 10 #include <sound/sof.h> 11 #include <sound/sof/ext_manifest4.h> 12 #include "sof-priv.h" 13 14 static int sof_test_firmware_file(struct device *dev, 15 struct sof_loadable_file_profile *profile, 16 enum sof_ipc_type *ipc_type_to_adjust) 17 { 18 enum sof_ipc_type fw_ipc_type; 19 const struct firmware *fw; 20 const char *fw_filename; 21 const u32 *magic; 22 int ret; 23 24 fw_filename = kasprintf(GFP_KERNEL, "%s/%s", profile->fw_path, 25 profile->fw_name); 26 if (!fw_filename) 27 return -ENOMEM; 28 29 ret = firmware_request_nowarn(&fw, fw_filename, dev); 30 if (ret < 0) { 31 dev_dbg(dev, "Failed to open firmware file: %s\n", fw_filename); 32 kfree(fw_filename); 33 return ret; 34 } 35 36 /* firmware file exists, check the magic number */ 37 magic = (const u32 *)fw->data; 38 switch (*magic) { 39 case SOF_EXT_MAN_MAGIC_NUMBER: 40 fw_ipc_type = SOF_IPC_TYPE_3; 41 break; 42 case SOF_EXT_MAN4_MAGIC_NUMBER: 43 fw_ipc_type = SOF_IPC_TYPE_4; 44 break; 45 default: 46 dev_err(dev, "Invalid firmware magic: %#x\n", *magic); 47 ret = -EINVAL; 48 goto out; 49 } 50 51 if (ipc_type_to_adjust) { 52 *ipc_type_to_adjust = fw_ipc_type; 53 } else if (fw_ipc_type != profile->ipc_type) { 54 dev_err(dev, 55 "ipc type mismatch between %s and expected: %d vs %d\n", 56 fw_filename, fw_ipc_type, profile->ipc_type); 57 ret = -EINVAL; 58 } 59 out: 60 release_firmware(fw); 61 kfree(fw_filename); 62 63 return ret; 64 } 65 66 static int sof_test_topology_file(struct device *dev, 67 struct sof_loadable_file_profile *profile) 68 { 69 const struct firmware *fw; 70 const char *tplg_filename; 71 int ret; 72 73 if (!profile->tplg_path || !profile->tplg_name) 74 return 0; 75 76 tplg_filename = kasprintf(GFP_KERNEL, "%s/%s", profile->tplg_path, 77 profile->tplg_name); 78 if (!tplg_filename) 79 return -ENOMEM; 80 81 ret = firmware_request_nowarn(&fw, tplg_filename, dev); 82 if (!ret) 83 release_firmware(fw); 84 else 85 dev_dbg(dev, "Failed to open topology file: %s\n", tplg_filename); 86 87 kfree(tplg_filename); 88 89 return ret; 90 } 91 92 static bool sof_platform_uses_generic_loader(struct snd_sof_dev *sdev) 93 { 94 return (sdev->pdata->desc->ops->load_firmware == snd_sof_load_firmware_raw || 95 sdev->pdata->desc->ops->load_firmware == snd_sof_load_firmware_memcpy); 96 } 97 98 static int 99 sof_file_profile_for_ipc_type(struct snd_sof_dev *sdev, 100 enum sof_ipc_type ipc_type, 101 const struct sof_dev_desc *desc, 102 struct sof_loadable_file_profile *base_profile, 103 struct sof_loadable_file_profile *out_profile) 104 { 105 struct snd_sof_pdata *plat_data = sdev->pdata; 106 bool fw_lib_path_allocated = false; 107 struct device *dev = sdev->dev; 108 bool fw_path_allocated = false; 109 int ret = 0; 110 111 /* firmware path */ 112 if (base_profile->fw_path) { 113 out_profile->fw_path = base_profile->fw_path; 114 } else if (base_profile->fw_path_postfix) { 115 out_profile->fw_path = devm_kasprintf(dev, GFP_KERNEL, "%s/%s", 116 desc->default_fw_path[ipc_type], 117 base_profile->fw_path_postfix); 118 if (!out_profile->fw_path) 119 return -ENOMEM; 120 121 fw_path_allocated = true; 122 } else { 123 out_profile->fw_path = desc->default_fw_path[ipc_type]; 124 } 125 126 /* firmware filename */ 127 if (base_profile->fw_name) 128 out_profile->fw_name = base_profile->fw_name; 129 else 130 out_profile->fw_name = desc->default_fw_filename[ipc_type]; 131 132 /* 133 * Check the custom firmware path/filename and adjust the ipc_type to 134 * match with the existing file for the remaining path configuration. 135 * 136 * For default path and firmware name do a verification before 137 * continuing further. 138 */ 139 if ((base_profile->fw_path || base_profile->fw_name) && 140 sof_platform_uses_generic_loader(sdev)) { 141 ret = sof_test_firmware_file(dev, out_profile, &ipc_type); 142 if (ret) 143 return ret; 144 145 if (!(desc->ipc_supported_mask & BIT(ipc_type))) { 146 dev_err(dev, "Unsupported IPC type %d needed by %s/%s\n", 147 ipc_type, out_profile->fw_path, 148 out_profile->fw_name); 149 return -EINVAL; 150 } 151 } 152 153 /* firmware library path */ 154 if (base_profile->fw_lib_path) { 155 out_profile->fw_lib_path = base_profile->fw_lib_path; 156 } else if (desc->default_lib_path[ipc_type]) { 157 if (base_profile->fw_lib_path_postfix) { 158 out_profile->fw_lib_path = devm_kasprintf(dev, 159 GFP_KERNEL, "%s/%s", 160 desc->default_lib_path[ipc_type], 161 base_profile->fw_lib_path_postfix); 162 if (!out_profile->fw_lib_path) { 163 ret = -ENOMEM; 164 goto out; 165 } 166 167 fw_lib_path_allocated = true; 168 } else { 169 out_profile->fw_lib_path = desc->default_lib_path[ipc_type]; 170 } 171 } 172 173 if (base_profile->fw_path_postfix) 174 out_profile->fw_path_postfix = base_profile->fw_path_postfix; 175 176 if (base_profile->fw_lib_path_postfix) 177 out_profile->fw_lib_path_postfix = base_profile->fw_lib_path_postfix; 178 179 /* topology path */ 180 if (base_profile->tplg_path) 181 out_profile->tplg_path = base_profile->tplg_path; 182 else 183 out_profile->tplg_path = desc->default_tplg_path[ipc_type]; 184 185 /* topology name */ 186 out_profile->tplg_name = plat_data->tplg_filename; 187 188 out_profile->ipc_type = ipc_type; 189 190 /* Test only default firmware file */ 191 if ((!base_profile->fw_path && !base_profile->fw_name) && 192 sof_platform_uses_generic_loader(sdev)) 193 ret = sof_test_firmware_file(dev, out_profile, NULL); 194 195 if (!ret) 196 ret = sof_test_topology_file(dev, out_profile); 197 198 out: 199 if (ret) { 200 /* Free up path strings created with devm_kasprintf */ 201 if (fw_path_allocated) 202 devm_kfree(dev, out_profile->fw_path); 203 if (fw_lib_path_allocated) 204 devm_kfree(dev, out_profile->fw_lib_path); 205 206 memset(out_profile, 0, sizeof(*out_profile)); 207 } 208 209 return ret; 210 } 211 212 static void 213 sof_print_missing_firmware_info(struct snd_sof_dev *sdev, 214 enum sof_ipc_type ipc_type, 215 struct sof_loadable_file_profile *base_profile) 216 { 217 struct snd_sof_pdata *plat_data = sdev->pdata; 218 const struct sof_dev_desc *desc = plat_data->desc; 219 struct device *dev = sdev->dev; 220 int ipc_type_count, i; 221 char *marker; 222 223 dev_err(dev, "SOF firmware and/or topology file not found.\n"); 224 dev_info(dev, "Supported default profiles\n"); 225 226 if (IS_ENABLED(CONFIG_SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION)) 227 ipc_type_count = SOF_IPC_TYPE_COUNT - 1; 228 else 229 ipc_type_count = base_profile->ipc_type; 230 231 for (i = 0; i <= ipc_type_count; i++) { 232 if (!(desc->ipc_supported_mask & BIT(i))) 233 continue; 234 235 if (i == ipc_type) 236 marker = "Requested"; 237 else 238 marker = "Fallback"; 239 240 dev_info(dev, "- ipc type %d (%s):\n", i, marker); 241 if (base_profile->fw_path_postfix) 242 dev_info(dev, " Firmware file: %s/%s/%s\n", 243 desc->default_fw_path[i], 244 base_profile->fw_path_postfix, 245 desc->default_fw_filename[i]); 246 else 247 dev_info(dev, " Firmware file: %s/%s\n", 248 desc->default_fw_path[i], 249 desc->default_fw_filename[i]); 250 251 dev_info(dev, " Topology file: %s/%s\n", 252 desc->default_tplg_path[i], 253 plat_data->tplg_filename); 254 } 255 256 if (base_profile->fw_path || base_profile->fw_name || 257 base_profile->tplg_path || base_profile->tplg_name) 258 dev_info(dev, "Verify the path/name override module parameters.\n"); 259 260 dev_info(dev, "Check if you have 'sof-firmware' package installed.\n"); 261 dev_info(dev, "Optionally it can be manually downloaded from:\n"); 262 dev_info(dev, " https://github.com/thesofproject/sof-bin/\n"); 263 } 264 265 static void sof_print_profile_info(struct snd_sof_dev *sdev, 266 enum sof_ipc_type ipc_type, 267 struct sof_loadable_file_profile *profile) 268 { 269 struct device *dev = sdev->dev; 270 271 if (ipc_type != profile->ipc_type) 272 dev_info(dev, 273 "Using fallback IPC type %d (requested type was %d)\n", 274 profile->ipc_type, ipc_type); 275 276 dev_info(dev, "Firmware paths/files for ipc type %d:\n", profile->ipc_type); 277 278 /* The firmware path is only valid when generic loader is used */ 279 if (sof_platform_uses_generic_loader(sdev)) 280 dev_info(dev, " Firmware file: %s/%s\n", 281 profile->fw_path, profile->fw_name); 282 283 if (profile->fw_lib_path) 284 dev_info(dev, " Firmware lib path: %s\n", profile->fw_lib_path); 285 dev_info(dev, " Topology file: %s/%s\n", profile->tplg_path, profile->tplg_name); 286 } 287 288 int sof_create_ipc_file_profile(struct snd_sof_dev *sdev, 289 struct sof_loadable_file_profile *base_profile, 290 struct sof_loadable_file_profile *out_profile) 291 { 292 const struct sof_dev_desc *desc = sdev->pdata->desc; 293 int ipc_fallback_start, ret, i; 294 295 memset(out_profile, 0, sizeof(*out_profile)); 296 297 ret = sof_file_profile_for_ipc_type(sdev, base_profile->ipc_type, desc, 298 base_profile, out_profile); 299 if (!ret) 300 goto out; 301 302 /* 303 * No firmware file was found for the requested IPC type, as fallback 304 * if SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION is selected, check 305 * all IPC versions in a backwards direction (from newer to older) 306 * if SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION is not selected, 307 * check only older IPC versions than the selected/default version 308 */ 309 if (IS_ENABLED(CONFIG_SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION)) 310 ipc_fallback_start = SOF_IPC_TYPE_COUNT - 1; 311 else 312 ipc_fallback_start = (int)base_profile->ipc_type - 1; 313 314 for (i = ipc_fallback_start; i >= 0 ; i--) { 315 if (i == base_profile->ipc_type || 316 !(desc->ipc_supported_mask & BIT(i))) 317 continue; 318 319 ret = sof_file_profile_for_ipc_type(sdev, i, desc, base_profile, 320 out_profile); 321 if (!ret) 322 break; 323 } 324 325 out: 326 if (ret) 327 sof_print_missing_firmware_info(sdev, base_profile->ipc_type, 328 base_profile); 329 else 330 sof_print_profile_info(sdev, base_profile->ipc_type, out_profile); 331 332 return ret; 333 } 334 EXPORT_SYMBOL(sof_create_ipc_file_profile); 335
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.