Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: skip the endpoint that doesn't present and

Merge series from Bard Liao <yung-chuan.liao@linux.intel.com>:

A codec endpoint may not be used. We could check the present SDCA
functions to know if the endpoint is used or not. Skip the endpoint
which is not used. And load the topology dynamically for each endpoint.
With this feature, we don't need to use the quirk to determine the
existence of the optional codec DAIs.

Mark Brown c84c801d 51f04358

+429 -44
+13
include/sound/soc-acpi.h
··· 10 10 #include <linux/acpi.h> 11 11 #include <linux/mod_devicetable.h> 12 12 #include <linux/soundwire/sdw.h> 13 + #include <sound/soc.h> 13 14 14 15 struct snd_soc_acpi_package_context { 15 16 char *name; /* package name */ ··· 194 193 * is not constant since this field may be updated at run-time 195 194 * @sof_tplg_filename: Sound Open Firmware topology file name, if enabled 196 195 * @tplg_quirk_mask: quirks to select different topology files dynamically 196 + * @get_function_tplg_files: This is an optional callback, if specified then instead of 197 + * the single sof_tplg_filename the callback will return the list of function topology 198 + * files to be loaded. 199 + * Return value: The number of the files or negative ERRNO. 0 means that the single topology 200 + * file should be used, no function topology split can be used on the machine. 201 + * @card: the pointer of the card 202 + * @mach: the pointer of the machine driver 203 + * @prefix: the prefix of the topology file name. Typically, it is the path. 204 + * @tplg_files: the pointer of the array of the topology file names. 197 205 */ 198 206 /* Descriptor for SST ASoC machine driver */ 199 207 struct snd_soc_acpi_mach { ··· 222 212 struct snd_soc_acpi_mach_params mach_params; 223 213 const char *sof_tplg_filename; 224 214 const u32 tplg_quirk_mask; 215 + int (*get_function_tplg_files)(struct snd_soc_card *card, 216 + const struct snd_soc_acpi_mach *mach, 217 + const char *prefix, const char ***tplg_files); 225 218 }; 226 219 227 220 #define SND_SOC_ACPI_MAX_CODECS 3
+1 -1
sound/soc/intel/common/Makefile
··· 12 12 soc-acpi-intel-lnl-match.o \ 13 13 soc-acpi-intel-ptl-match.o \ 14 14 soc-acpi-intel-hda-match.o \ 15 - soc-acpi-intel-sdw-mockup-match.o 15 + soc-acpi-intel-sdw-mockup-match.o sof-function-topology-lib.o 16 16 17 17 snd-soc-acpi-intel-match-y += soc-acpi-intel-ssp-common.o 18 18
+9
sound/soc/intel/common/soc-acpi-intel-arl-match.c
··· 8 8 #include <sound/soc-acpi.h> 9 9 #include <sound/soc-acpi-intel-match.h> 10 10 #include <sound/soc-acpi-intel-ssp-common.h> 11 + #include "sof-function-topology-lib.h" 11 12 12 13 static const struct snd_soc_acpi_endpoint single_endpoint = { 13 14 .num = 0, ··· 437 436 .links = arl_cs42l43_l0_cs35l56_l23, 438 437 .drv_name = "sof_sdw", 439 438 .sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l23.tplg", 439 + .get_function_tplg_files = sof_sdw_get_tplg_files, 440 440 }, 441 441 { 442 442 .link_mask = BIT(0) | BIT(2) | BIT(3), 443 443 .links = arl_cs42l43_l0_cs35l56_2_l23, 444 444 .drv_name = "sof_sdw", 445 445 .sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l23.tplg", 446 + .get_function_tplg_files = sof_sdw_get_tplg_files, 446 447 }, 447 448 { 448 449 .link_mask = BIT(0) | BIT(2) | BIT(3), 449 450 .links = arl_cs42l43_l0_cs35l56_3_l23, 450 451 .drv_name = "sof_sdw", 451 452 .sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l23.tplg", 453 + .get_function_tplg_files = sof_sdw_get_tplg_files, 452 454 }, 453 455 { 454 456 .link_mask = BIT(0) | BIT(2), 455 457 .links = arl_cs42l43_l0_cs35l56_l2, 456 458 .drv_name = "sof_sdw", 457 459 .sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l2.tplg", 460 + .get_function_tplg_files = sof_sdw_get_tplg_files, 458 461 }, 459 462 { 460 463 .link_mask = BIT(0), 461 464 .links = arl_cs42l43_l0, 462 465 .drv_name = "sof_sdw", 463 466 .sof_tplg_filename = "sof-arl-cs42l43-l0.tplg", 467 + .get_function_tplg_files = sof_sdw_get_tplg_files, 464 468 }, 465 469 { 466 470 .link_mask = BIT(2), 467 471 .links = arl_cs42l43_l2, 468 472 .drv_name = "sof_sdw", 469 473 .sof_tplg_filename = "sof-arl-cs42l43-l2.tplg", 474 + .get_function_tplg_files = sof_sdw_get_tplg_files, 470 475 }, 471 476 { 472 477 .link_mask = BIT(2) | BIT(3), 473 478 .links = arl_cs42l43_l2_cs35l56_l3, 474 479 .drv_name = "sof_sdw", 475 480 .sof_tplg_filename = "sof-arl-cs42l43-l2-cs35l56-l3.tplg", 481 + .get_function_tplg_files = sof_sdw_get_tplg_files, 476 482 }, 477 483 { 478 484 .link_mask = 0x1, /* link0 required */ ··· 498 490 .links = arl_rt722_l0_rt1320_l2, 499 491 .drv_name = "sof_sdw", 500 492 .sof_tplg_filename = "sof-arl-rt722-l0_rt1320-l2.tplg", 493 + .get_function_tplg_files = sof_sdw_get_tplg_files, 501 494 }, 502 495 {}, 503 496 };
+7 -2
sound/soc/intel/common/soc-acpi-intel-lnl-match.c
··· 8 8 9 9 #include <sound/soc-acpi.h> 10 10 #include <sound/soc-acpi-intel-match.h> 11 + #include "sof-function-topology-lib.h" 11 12 #include "soc-acpi-intel-sdca-quirks.h" 12 13 #include "soc-acpi-intel-sdw-mockup-match.h" 13 14 ··· 713 712 .links = lnl_cs42l43_l0, 714 713 .drv_name = "sof_sdw", 715 714 .sof_tplg_filename = "sof-lnl-cs42l43-l0.tplg", 715 + .get_function_tplg_files = sof_sdw_get_tplg_files, 716 716 }, 717 717 { 718 718 .link_mask = BIT(0), ··· 732 730 .links = lnl_rt722_only, 733 731 .drv_name = "sof_sdw", 734 732 .sof_tplg_filename = "sof-lnl-rt722-l0.tplg", 733 + .get_function_tplg_files = sof_sdw_get_tplg_files, 735 734 }, 736 735 { 737 736 .link_mask = GENMASK(2, 0), ··· 751 748 .links = lnl_sdw_rt712_vb_l2_rt1320_l1, 752 749 .drv_name = "sof_sdw", 753 750 .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, 754 - .sof_tplg_filename = "sof-lnl-rt712-l2-rt1320-l1.tplg" 751 + .sof_tplg_filename = "sof-lnl-rt712-l2-rt1320-l1.tplg", 752 + .get_function_tplg_files = sof_sdw_get_tplg_files, 755 753 }, 756 754 { 757 755 .link_mask = BIT(1) | BIT(2) | BIT(3), 758 756 .links = lnl_sdw_rt713_vb_l2_rt1320_l13, 759 757 .drv_name = "sof_sdw", 760 758 .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, 761 - .sof_tplg_filename = "sof-lnl-rt713-l2-rt1320-l13.tplg" 759 + .sof_tplg_filename = "sof-lnl-rt713-l2-rt1320-l13.tplg", 760 + .get_function_tplg_files = sof_sdw_get_tplg_files, 762 761 }, 763 762 {}, 764 763 };
+10 -1
sound/soc/intel/common/soc-acpi-intel-mtl-match.c
··· 11 11 #include <sound/soc-acpi.h> 12 12 #include <sound/soc-acpi-intel-match.h> 13 13 #include <sound/soc-acpi-intel-ssp-common.h> 14 + #include "sof-function-topology-lib.h" 14 15 #include "soc-acpi-intel-sdca-quirks.h" 15 16 #include "soc-acpi-intel-sdw-mockup-match.h" 16 17 ··· 1084 1083 .drv_name = "sof_sdw", 1085 1084 .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, 1086 1085 .sof_tplg_filename = "sof-mtl-rt712-vb-l0.tplg", 1086 + .get_function_tplg_files = sof_sdw_get_tplg_files, 1087 1087 }, 1088 1088 { 1089 1089 .link_mask = BIT(0), 1090 1090 .links = mtl_712_l0, 1091 1091 .drv_name = "sof_sdw", 1092 1092 .sof_tplg_filename = "sof-mtl-rt712-l0.tplg", 1093 + .get_function_tplg_files = sof_sdw_get_tplg_files, 1093 1094 }, 1094 1095 { 1095 1096 .link_mask = GENMASK(2, 0), ··· 1104 1101 .links = cs42l43_link0_cs35l56_link2_link3, 1105 1102 .drv_name = "sof_sdw", 1106 1103 .sof_tplg_filename = "sof-mtl-cs42l43-l0-cs35l56-l23.tplg", 1104 + .get_function_tplg_files = sof_sdw_get_tplg_files, 1107 1105 }, 1108 1106 { 1109 1107 .link_mask = BIT(0) | BIT(1) | BIT(3), 1110 1108 .links = cs42l43_link3_cs35l56_x4_link0_link1_spkagg, 1111 1109 .drv_name = "sof_sdw", 1112 1110 .sof_tplg_filename = "sof-mtl-cs42l43-l3-cs35l56-l01-spkagg.tplg", 1111 + .get_function_tplg_files = sof_sdw_get_tplg_files, 1113 1112 }, 1114 1113 { 1115 1114 .link_mask = GENMASK(2, 0), 1116 1115 .links = mtl_cs42l43_cs35l56, 1117 1116 .drv_name = "sof_sdw", 1118 1117 .sof_tplg_filename = "sof-mtl-cs42l43-l0-cs35l56-l12.tplg", 1118 + .get_function_tplg_files = sof_sdw_get_tplg_files, 1119 1119 }, 1120 1120 { 1121 1121 .link_mask = BIT(0) | BIT(1), 1122 1122 .links = mtl_cs35l56_x8_link0_link1_fb, 1123 1123 .drv_name = "sof_sdw", 1124 - .sof_tplg_filename = "sof-mtl-cs35l56-l01-fb8.tplg" 1124 + .sof_tplg_filename = "sof-mtl-cs35l56-l01-fb8.tplg", 1125 + .get_function_tplg_files = sof_sdw_get_tplg_files, 1125 1126 }, 1126 1127 { 1127 1128 .link_mask = BIT(0), 1128 1129 .links = mtl_cs42l43_l0, 1129 1130 .drv_name = "sof_sdw", 1130 1131 .sof_tplg_filename = "sof-mtl-cs42l43-l0.tplg", 1132 + .get_function_tplg_files = sof_sdw_get_tplg_files, 1131 1133 }, 1132 1134 { 1133 1135 .link_mask = GENMASK(3, 0), ··· 1151 1143 .links = mtl_rt722_only, 1152 1144 .drv_name = "sof_sdw", 1153 1145 .sof_tplg_filename = "sof-mtl-rt722-l0.tplg", 1146 + .get_function_tplg_files = sof_sdw_get_tplg_files, 1154 1147 }, 1155 1148 { 1156 1149 .link_mask = BIT(0),
+13 -4
sound/soc/intel/common/soc-acpi-intel-ptl-match.c
··· 8 8 9 9 #include <sound/soc-acpi.h> 10 10 #include <sound/soc-acpi-intel-match.h> 11 + #include "sof-function-topology-lib.h" 11 12 #include "soc-acpi-intel-sdca-quirks.h" 12 13 #include "soc-acpi-intel-sdw-mockup-match.h" 13 14 #include <sound/soc-acpi-intel-ssp-common.h> ··· 587 586 .links = ptl_rt721_l3, 588 587 .drv_name = "sof_sdw", 589 588 .sof_tplg_filename = "sof-ptl-rt721.tplg", 589 + .get_function_tplg_files = sof_sdw_get_tplg_files, 590 590 }, 591 591 { 592 592 .link_mask = BIT(0), 593 593 .links = ptl_rt722_only, 594 594 .drv_name = "sof_sdw", 595 595 .sof_tplg_filename = "sof-ptl-rt722.tplg", 596 + .get_function_tplg_files = sof_sdw_get_tplg_files, 596 597 }, 597 598 { 598 599 .link_mask = BIT(1), 599 600 .links = ptl_rt722_l1, 600 601 .drv_name = "sof_sdw", 601 602 .sof_tplg_filename = "sof-ptl-rt722.tplg", 603 + .get_function_tplg_files = sof_sdw_get_tplg_files, 602 604 }, 603 605 { 604 606 .link_mask = BIT(3), 605 607 .links = ptl_rt722_l3, 606 608 .drv_name = "sof_sdw", 607 609 .sof_tplg_filename = "sof-ptl-rt722.tplg", 610 + .get_function_tplg_files = sof_sdw_get_tplg_files, 608 611 }, 609 612 { 610 613 .link_mask = BIT(1) | BIT(2), 611 614 .links = ptl_sdw_rt712_vb_l2_rt1320_l1, 612 615 .drv_name = "sof_sdw", 613 616 .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, 614 - .sof_tplg_filename = "sof-ptl-rt712-l2-rt1320-l1.tplg" 617 + .sof_tplg_filename = "sof-ptl-rt712-l2-rt1320-l1.tplg", 618 + .get_function_tplg_files = sof_sdw_get_tplg_files, 615 619 }, 616 620 { 617 621 .link_mask = BIT(2) | BIT(3), 618 622 .links = ptl_sdw_rt712_vb_l3_rt1320_l2, 619 623 .drv_name = "sof_sdw", 620 624 .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, 621 - .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l2.tplg" 625 + .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l2.tplg", 626 + .get_function_tplg_files = sof_sdw_get_tplg_files, 622 627 }, 623 628 { 624 629 .link_mask = BIT(1) | BIT(2) | BIT(3), 625 630 .links = ptl_sdw_rt713_vb_l2_rt1320_l13, 626 631 .drv_name = "sof_sdw", 627 632 .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, 628 - .sof_tplg_filename = "sof-ptl-rt713-l2-rt1320-l13.tplg" 633 + .sof_tplg_filename = "sof-ptl-rt713-l2-rt1320-l13.tplg", 634 + .get_function_tplg_files = sof_sdw_get_tplg_files, 629 635 }, 630 636 { 631 637 .link_mask = BIT(1) | BIT(2) | BIT(3), 632 638 .links = ptl_sdw_rt713_vb_l3_rt1320_l12, 633 639 .drv_name = "sof_sdw", 634 640 .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, 635 - .sof_tplg_filename = "sof-ptl-rt713-l3-rt1320-l12.tplg" 641 + .sof_tplg_filename = "sof-ptl-rt713-l3-rt1320-l12.tplg", 642 + .get_function_tplg_files = sof_sdw_get_tplg_files, 636 643 }, 637 644 {}, 638 645 };
+135
sound/soc/intel/common/sof-function-topology-lib.c
··· 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_MAX 23 + }; 24 + 25 + #define SDCA_DEVICE_MASK (BIT(TPLG_DEVICE_SDCA_JACK) | BIT(TPLG_DEVICE_SDCA_AMP) | \ 26 + BIT(TPLG_DEVICE_SDCA_MIC)) 27 + 28 + #define SOF_INTEL_PLATFORM_NAME_MAX 4 29 + 30 + int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach, 31 + const char *prefix, const char ***tplg_files) 32 + { 33 + struct snd_soc_acpi_mach_params mach_params = mach->mach_params; 34 + struct snd_soc_dai_link *dai_link; 35 + const struct firmware *fw; 36 + char platform[SOF_INTEL_PLATFORM_NAME_MAX]; 37 + unsigned long tplg_mask = 0; 38 + int tplg_num = 0; 39 + int tplg_dev; 40 + int ret; 41 + int i; 42 + 43 + ret = sscanf(mach->sof_tplg_filename, "sof-%3s-*.tplg", platform); 44 + if (ret != 1) { 45 + dev_err(card->dev, "Invalid platform name %s of tplg %s\n", 46 + platform, mach->sof_tplg_filename); 47 + return -EINVAL; 48 + } 49 + 50 + for_each_card_prelinks(card, i, dai_link) { 51 + char *tplg_dev_name; 52 + 53 + dev_dbg(card->dev, "dai_link %s id %d\n", dai_link->name, dai_link->id); 54 + if (strstr(dai_link->name, "SimpleJack")) { 55 + tplg_dev = TPLG_DEVICE_SDCA_JACK; 56 + tplg_dev_name = "sdca-jack"; 57 + } else if (strstr(dai_link->name, "SmartAmp")) { 58 + tplg_dev = TPLG_DEVICE_SDCA_AMP; 59 + tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL, 60 + "sdca-%damp", dai_link->num_cpus); 61 + if (!tplg_dev_name) 62 + return -ENOMEM; 63 + } else if (strstr(dai_link->name, "SmartMic")) { 64 + tplg_dev = TPLG_DEVICE_SDCA_MIC; 65 + tplg_dev_name = "sdca-mic"; 66 + } else if (strstr(dai_link->name, "dmic")) { 67 + switch (mach_params.dmic_num) { 68 + case 2: 69 + tplg_dev_name = "dmic-2ch"; 70 + break; 71 + case 4: 72 + tplg_dev_name = "dmic-4ch"; 73 + break; 74 + default: 75 + dev_warn(card->dev, 76 + "only -2ch and -4ch are supported for dmic\n"); 77 + continue; 78 + } 79 + tplg_dev = TPLG_DEVICE_INTEL_PCH_DMIC; 80 + } else if (strstr(dai_link->name, "iDisp")) { 81 + tplg_dev = TPLG_DEVICE_HDMI; 82 + tplg_dev_name = "hdmi-pcm5"; 83 + 84 + } else { 85 + /* The dai link is not supported by separated tplg yet */ 86 + dev_dbg(card->dev, 87 + "dai_link %s is not supported by separated tplg yet\n", 88 + dai_link->name); 89 + return 0; 90 + } 91 + if (tplg_mask & BIT(tplg_dev)) 92 + continue; 93 + 94 + tplg_mask |= BIT(tplg_dev); 95 + 96 + /* 97 + * The tplg file naming rule is sof-<platform>-<function>-id<BE id number>.tplg 98 + * where <platform> is only required for the DMIC function as the nhlt blob 99 + * is platform dependent. 100 + */ 101 + switch (tplg_dev) { 102 + case TPLG_DEVICE_INTEL_PCH_DMIC: 103 + (*tplg_files)[tplg_num] = devm_kasprintf(card->dev, GFP_KERNEL, 104 + "%s/sof-%s-%s-id%d.tplg", 105 + prefix, platform, 106 + tplg_dev_name, dai_link->id); 107 + break; 108 + default: 109 + (*tplg_files)[tplg_num] = devm_kasprintf(card->dev, GFP_KERNEL, 110 + "%s/sof-%s-id%d.tplg", 111 + prefix, tplg_dev_name, 112 + dai_link->id); 113 + break; 114 + } 115 + if (!(*tplg_files)[tplg_num]) 116 + return -ENOMEM; 117 + tplg_num++; 118 + } 119 + 120 + dev_dbg(card->dev, "tplg_mask %#lx tplg_num %d\n", tplg_mask, tplg_num); 121 + 122 + /* Check presence of sub-topologies */ 123 + for (i = 0; i < tplg_num; i++) { 124 + ret = firmware_request_nowarn(&fw, (*tplg_files)[i], card->dev); 125 + if (!ret) { 126 + release_firmware(fw); 127 + } else { 128 + dev_dbg(card->dev, "Failed to open topology file: %s\n", (*tplg_files)[i]); 129 + return 0; 130 + } 131 + } 132 + 133 + return tplg_num; 134 + } 135 +
+15
sound/soc/intel/common/sof-function-topology-lib.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * soc-acpi-intel-get-tplg.h - get-tplg-files ops 4 + * 5 + * Copyright (c) 2025, Intel Corporation. 6 + * 7 + */ 8 + 9 + #ifndef _SND_SOC_ACPI_INTEL_GET_TPLG_H 10 + #define _SND_SOC_ACPI_INTEL_GET_TPLG_H 11 + 12 + int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach, 13 + const char *prefix, const char ***tplg_files); 14 + 15 + #endif
+153 -15
sound/soc/sdw_utils/soc_sdw_utils.c
··· 10 10 #include <linux/module.h> 11 11 #include <linux/soundwire/sdw.h> 12 12 #include <linux/soundwire/sdw_type.h> 13 + #include <sound/sdca_function.h> 13 14 #include <sound/soc_sdw_utils.h> 14 15 15 16 static const struct snd_soc_dapm_widget generic_dmic_widgets[] = { ··· 935 934 return true; 936 935 } 937 936 938 - const char *asoc_sdw_get_codec_name(struct device *dev, 939 - const struct asoc_sdw_codec_info *codec_info, 940 - const struct snd_soc_acpi_link_adr *adr_link, 941 - int adr_index) 937 + static const char *_asoc_sdw_get_codec_name(struct device *dev, 938 + const struct asoc_sdw_codec_info *codec_info, 939 + const struct snd_soc_acpi_link_adr *adr_link, 940 + int adr_index) 942 941 { 943 942 u64 adr = adr_link->adr_d[adr_index].adr; 944 943 unsigned int sdw_version = SDW_VERSION(adr); ··· 948 947 unsigned int part_id = SDW_PART_ID(adr); 949 948 unsigned int class_id = SDW_CLASS_ID(adr); 950 949 951 - if (codec_info->codec_name) 952 - return devm_kstrdup(dev, codec_info->codec_name, GFP_KERNEL); 953 - else if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id, 954 - class_id, adr_index)) 950 + if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id, 951 + class_id, adr_index)) 955 952 return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x", 956 953 link_id, mfg_id, part_id, class_id); 957 - else 958 - return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x", 959 - link_id, mfg_id, part_id, class_id, unique_id); 960 954 961 - return NULL; 955 + return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x", 956 + link_id, mfg_id, part_id, class_id, unique_id); 957 + } 958 + 959 + const char *asoc_sdw_get_codec_name(struct device *dev, 960 + const struct asoc_sdw_codec_info *codec_info, 961 + const struct snd_soc_acpi_link_adr *adr_link, 962 + int adr_index) 963 + { 964 + if (codec_info->codec_name) 965 + return devm_kstrdup(dev, codec_info->codec_name, GFP_KERNEL); 966 + 967 + return _asoc_sdw_get_codec_name(dev, codec_info, adr_link, adr_index); 962 968 } 963 969 EXPORT_SYMBOL_NS(asoc_sdw_get_codec_name, "SND_SOC_SDW_UTILS"); 964 970 ··· 1132 1124 } 1133 1125 EXPORT_SYMBOL_NS(asoc_sdw_find_dailink, "SND_SOC_SDW_UTILS"); 1134 1126 1127 + static int asoc_sdw_get_dai_type(u32 type) 1128 + { 1129 + switch (type) { 1130 + case SDCA_FUNCTION_TYPE_SMART_AMP: 1131 + case SDCA_FUNCTION_TYPE_SIMPLE_AMP: 1132 + return SOC_SDW_DAI_TYPE_AMP; 1133 + case SDCA_FUNCTION_TYPE_SMART_MIC: 1134 + case SDCA_FUNCTION_TYPE_SIMPLE_MIC: 1135 + case SDCA_FUNCTION_TYPE_SPEAKER_MIC: 1136 + return SOC_SDW_DAI_TYPE_MIC; 1137 + case SDCA_FUNCTION_TYPE_UAJ: 1138 + case SDCA_FUNCTION_TYPE_RJ: 1139 + case SDCA_FUNCTION_TYPE_SIMPLE_JACK: 1140 + return SOC_SDW_DAI_TYPE_JACK; 1141 + default: 1142 + return -EINVAL; 1143 + } 1144 + } 1145 + 1146 + /* 1147 + * Check if the SDCA endpoint is present by the SDW peripheral 1148 + * 1149 + * @dev: Device pointer 1150 + * @codec_info: Codec info pointer 1151 + * @adr_link: ACPI link address 1152 + * @adr_index: Index of the ACPI link address 1153 + * @end_index: Index of the endpoint 1154 + * 1155 + * Return: 1 if the endpoint is present, 1156 + * 0 if the endpoint is not present, 1157 + * negative error code. 1158 + */ 1159 + 1160 + static int is_sdca_endpoint_present(struct device *dev, 1161 + struct asoc_sdw_codec_info *codec_info, 1162 + const struct snd_soc_acpi_link_adr *adr_link, 1163 + int adr_index, int end_index) 1164 + { 1165 + const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[adr_index]; 1166 + const struct snd_soc_acpi_endpoint *adr_end; 1167 + const struct asoc_sdw_dai_info *dai_info; 1168 + struct snd_soc_dai_link_component *dlc; 1169 + struct snd_soc_dai *codec_dai; 1170 + struct sdw_slave *slave; 1171 + struct device *sdw_dev; 1172 + const char *sdw_codec_name; 1173 + int i; 1174 + 1175 + dlc = kzalloc(sizeof(*dlc), GFP_KERNEL); 1176 + 1177 + adr_end = &adr_dev->endpoints[end_index]; 1178 + dai_info = &codec_info->dais[adr_end->num]; 1179 + 1180 + dlc->dai_name = dai_info->dai_name; 1181 + codec_dai = snd_soc_find_dai_with_mutex(dlc); 1182 + if (!codec_dai) { 1183 + dev_warn(dev, "codec dai %s not registered yet\n", dlc->dai_name); 1184 + kfree(dlc); 1185 + return -EPROBE_DEFER; 1186 + } 1187 + kfree(dlc); 1188 + 1189 + sdw_codec_name = _asoc_sdw_get_codec_name(dev, codec_info, 1190 + adr_link, adr_index); 1191 + if (!sdw_codec_name) 1192 + return -ENOMEM; 1193 + 1194 + sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); 1195 + if (!sdw_dev) { 1196 + dev_err(dev, "codec %s not found\n", sdw_codec_name); 1197 + return -EINVAL; 1198 + } 1199 + 1200 + slave = dev_to_sdw_dev(sdw_dev); 1201 + if (!slave) 1202 + return -EINVAL; 1203 + 1204 + /* Make sure BIOS provides SDCA properties */ 1205 + if (!slave->sdca_data.interface_revision) { 1206 + dev_warn(&slave->dev, "SDCA properties not found in the BIOS\n"); 1207 + return 1; 1208 + } 1209 + 1210 + for (i = 0; i < slave->sdca_data.num_functions; i++) { 1211 + int dai_type = asoc_sdw_get_dai_type(slave->sdca_data.function[i].type); 1212 + 1213 + if (dai_type == dai_info->dai_type) { 1214 + dev_dbg(&slave->dev, "DAI type %d sdca function %s found\n", 1215 + dai_type, slave->sdca_data.function[i].name); 1216 + return 1; 1217 + } 1218 + } 1219 + 1220 + dev_dbg(&slave->dev, 1221 + "SDCA device function for DAI type %d not supported, skip endpoint\n", 1222 + dai_info->dai_type); 1223 + 1224 + return 0; 1225 + } 1226 + 1135 1227 int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, 1136 1228 struct asoc_sdw_dailink *soc_dais, 1137 1229 struct asoc_sdw_endpoint *soc_ends, ··· 1260 1152 const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i]; 1261 1153 struct asoc_sdw_codec_info *codec_info; 1262 1154 const char *codec_name; 1155 + bool check_sdca = false; 1263 1156 1264 1157 if (!adr_dev->name_prefix) { 1265 1158 dev_err(dev, "codec 0x%llx does not have a name prefix\n", ··· 1291 1182 soc_end->include_sidecar = true; 1292 1183 } 1293 1184 1185 + if (SDW_CLASS_ID(adr_dev->adr) && adr_dev->num_endpoints > 1) 1186 + check_sdca = true; 1187 + 1294 1188 for (j = 0; j < adr_dev->num_endpoints; j++) { 1295 1189 const struct snd_soc_acpi_endpoint *adr_end; 1296 1190 const struct asoc_sdw_dai_info *dai_info; ··· 1304 1192 dai_info = &codec_info->dais[adr_end->num]; 1305 1193 soc_dai = asoc_sdw_find_dailink(soc_dais, adr_end); 1306 1194 1307 - if (dai_info->quirk && 1308 - !(dai_info->quirk_exclude ^ !!(dai_info->quirk & ctx->mc_quirk))) 1309 - continue; 1195 + /* 1196 + * quirk should have higher priority than the sdca properties 1197 + * in the BIOS. We can't always check the DAI quirk because we 1198 + * will set the mc_quirk when the BIOS doesn't provide the right 1199 + * information. The endpoint will be skipped if the dai_info-> 1200 + * quirk_exclude and mc_quirk are both not set if we always skip 1201 + * the endpoint according to the quirk information. We need to 1202 + * keep the endpoint if it is present in the BIOS. So, only 1203 + * check the DAI quirk when the mc_quirk is set or SDCA endpoint 1204 + * present check is not needed. 1205 + */ 1206 + if (dai_info->quirk & ctx->mc_quirk || !check_sdca) { 1207 + /* 1208 + * Check the endpoint if a matching quirk is set or SDCA 1209 + * endpoint check is not necessary 1210 + */ 1211 + if (dai_info->quirk && 1212 + !(dai_info->quirk_exclude ^ !!(dai_info->quirk & ctx->mc_quirk))) 1213 + continue; 1214 + } else { 1215 + /* Check SDCA codec endpoint if there is no matching quirk */ 1216 + ret = is_sdca_endpoint_present(dev, codec_info, adr_link, i, j); 1217 + if (ret < 0) 1218 + return ret; 1219 + 1220 + /* The endpoint is not present, skip */ 1221 + if (!ret) 1222 + continue; 1223 + } 1310 1224 1311 1225 dev_dbg(dev, 1312 1226 "Add dev: %d, 0x%llx end: %d, dai: %d, %c/%c to %s: %d\n",
+73 -21
sound/soc/sof/topology.c
··· 571 571 continue; 572 572 573 573 tuples[*num_copied_tuples].token = tokens[j].token; 574 - tuples[*num_copied_tuples].value.s = elem->string; 574 + tuples[*num_copied_tuples].value.s = 575 + devm_kasprintf(sdev->dev, GFP_KERNEL, 576 + "%s", elem->string); 577 + if (!tuples[*num_copied_tuples].value.s) 578 + return -ENOMEM; 575 579 } else { 576 580 struct snd_soc_tplg_vendor_value_elem *elem; 577 581 ··· 2310 2306 .link_load = sof_link_load, 2311 2307 .link_unload = sof_link_unload, 2312 2308 2313 - /* completion - called at completion of firmware loading */ 2314 - .complete = sof_complete, 2309 + /* 2310 + * No need to set the complete callback. sof_complete will be called explicitly after 2311 + * topology loading is complete. 2312 + */ 2315 2313 2316 2314 /* manifest - optional to inform component of manifest */ 2317 2315 .manifest = sof_manifest, ··· 2469 2463 int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) 2470 2464 { 2471 2465 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 2466 + struct snd_sof_pdata *sof_pdata = sdev->pdata; 2467 + const char *tplg_filename_prefix = sof_pdata->tplg_filename_prefix; 2472 2468 const struct firmware *fw; 2469 + const char **tplg_files; 2470 + int tplg_cnt = 0; 2473 2471 int ret; 2472 + int i; 2474 2473 2475 - dev_dbg(scomp->dev, "loading topology:%s\n", file); 2474 + tplg_files = kcalloc(scomp->card->num_links, sizeof(char *), GFP_KERNEL); 2475 + if (!tplg_files) 2476 + return -ENOMEM; 2476 2477 2477 - ret = request_firmware(&fw, file, scomp->dev); 2478 - if (ret < 0) { 2479 - dev_err(scomp->dev, "error: tplg request firmware %s failed err: %d\n", 2480 - file, ret); 2481 - dev_err(scomp->dev, 2482 - "you may need to download the firmware from https://github.com/thesofproject/sof-bin/\n"); 2483 - return ret; 2478 + if (sof_pdata->machine->get_function_tplg_files) { 2479 + tplg_cnt = sof_pdata->machine->get_function_tplg_files(scomp->card, 2480 + sof_pdata->machine, 2481 + tplg_filename_prefix, 2482 + &tplg_files); 2483 + if (tplg_cnt < 0) { 2484 + kfree(tplg_files); 2485 + return tplg_cnt; 2486 + } 2484 2487 } 2485 2488 2486 - if (sdev->dspless_mode_selected) 2487 - ret = snd_soc_tplg_component_load(scomp, &sof_dspless_tplg_ops, fw); 2488 - else 2489 - ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); 2490 - 2491 - if (ret < 0) { 2492 - dev_err(scomp->dev, "error: tplg component load failed %d\n", 2493 - ret); 2494 - ret = -EINVAL; 2489 + /* 2490 + * The monolithic topology will be used if there is no get_function_tplg_files 2491 + * callback or the callback returns 0. 2492 + */ 2493 + if (!tplg_cnt) { 2494 + tplg_files[0] = file; 2495 + tplg_cnt = 1; 2496 + dev_dbg(scomp->dev, "loading topology: %s\n", file); 2497 + } else { 2498 + dev_info(scomp->dev, "Using function topologies instead %s\n", file); 2495 2499 } 2496 2500 2497 - release_firmware(fw); 2501 + for (i = 0; i < tplg_cnt; i++) { 2502 + /* Only print the file names if the function topologies are used */ 2503 + if (tplg_files[0] != file) 2504 + dev_info(scomp->dev, "loading topology %d: %s\n", i, tplg_files[i]); 2498 2505 2506 + ret = request_firmware(&fw, tplg_files[i], scomp->dev); 2507 + if (ret < 0) { 2508 + /* 2509 + * snd_soc_tplg_component_remove(scomp) will be called 2510 + * if snd_soc_tplg_component_load(scomp) failed and all 2511 + * objects in the scomp will be removed. No need to call 2512 + * snd_soc_tplg_component_remove(scomp) here. 2513 + */ 2514 + dev_err(scomp->dev, "tplg request firmware %s failed err: %d\n", 2515 + tplg_files[i], ret); 2516 + goto out; 2517 + } 2518 + 2519 + if (sdev->dspless_mode_selected) 2520 + ret = snd_soc_tplg_component_load(scomp, &sof_dspless_tplg_ops, fw); 2521 + else 2522 + ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); 2523 + 2524 + release_firmware(fw); 2525 + 2526 + if (ret < 0) { 2527 + dev_err(scomp->dev, "tplg %s component load failed %d\n", 2528 + tplg_files[i], ret); 2529 + goto out; 2530 + } 2531 + } 2532 + 2533 + /* call sof_complete when topologies are loaded successfully */ 2534 + ret = sof_complete(scomp); 2535 + 2536 + out: 2499 2537 if (ret >= 0 && sdev->led_present) 2500 2538 ret = snd_ctl_led_request(); 2539 + 2540 + kfree(tplg_files); 2501 2541 2502 2542 return ret; 2503 2543 }