diff --git a/sound/hda/core/intel-nhlt.c b/sound/hda/core/intel-nhlt.c index 6d72a871bda0b5..af4d469dbd764b 100644 --- a/sound/hda/core/intel-nhlt.c +++ b/sound/hda/core/intel-nhlt.c @@ -351,7 +351,7 @@ int intel_nhlt_ssp_device_type(struct device *dev, struct nhlt_acpi_table *nhlt, int i; if (!nhlt) { - dev_err(dev, "%s: NHLT table is missing (query for SSP%d)\n", + dev_dbg(dev, "%s: NHLT table is missing (query for SSP%d)\n", __func__, virtual_bus_id); return -EINVAL; } @@ -369,7 +369,7 @@ int intel_nhlt_ssp_device_type(struct device *dev, struct nhlt_acpi_table *nhlt, epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); } - dev_err(dev, "%s: No match for SSP%d in NHLT table\n", __func__, + dev_dbg(dev, "%s: No match for SSP%d in NHLT table\n", __func__, virtual_bus_id); dev_dbg(dev, "Available endpoints:\n"); diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c index 1055fb4838f612..28616130566202 100644 --- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c @@ -51,12 +51,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_machines[] = { .drv_name = "ptl_es83x6_c1_h02", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &ptl_lt6911_hdmi, + .get_function_tplg_files = sof_i2s_get_tplg_files, .sof_tplg_filename = "sof-ptl-es83x6-ssp1-hdmi-ssp02.tplg", }, { .comp_ids = &ptl_essx_83x6, .drv_name = "sof-essx8336", .sof_tplg_filename = "sof-ptl-es8336", /* the tplg suffix is added at run time */ + .get_function_tplg_files = sof_i2s_get_tplg_files, .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER, diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c index 0daa7d83808be6..f9d8454deead84 100644 --- a/sound/soc/intel/common/sof-function-topology-lib.c +++ b/sound/soc/intel/common/sof-function-topology-lib.c @@ -19,6 +19,10 @@ enum tplg_device_id { TPLG_DEVICE_SDCA_MIC, TPLG_DEVICE_INTEL_PCH_DMIC, TPLG_DEVICE_HDMI, + TPLG_DEVICE_SSP_JACK, + TPLG_DEVICE_SSP_AMP, + TPLG_DEVICE_SSP_BT, + TPLG_DEVICE_SSP_HDMI_IN, TPLG_DEVICE_MAX }; @@ -27,25 +31,88 @@ enum tplg_device_id { #define SOF_INTEL_PLATFORM_NAME_MAX 4 +static int get_platform_name(struct snd_soc_card *card, + const struct snd_soc_acpi_mach *mach, char *platform) +{ + int ret; + + ret = sscanf(mach->sof_tplg_filename, "sof-%3s-*.tplg", platform); + if (ret != 1) { + dev_err(card->dev, "Invalid platform name %s of tplg %s\n", + platform, mach->sof_tplg_filename); + return -EINVAL; + } + + return 0; +} + +static bool all_tplg_files_exist(struct device *dev, const char ***tplg_files, int tplg_num) +{ + const struct firmware *fw; + int ret; + int i; + + for (i = 0; i < tplg_num; i++) { + ret = firmware_request_nowarn(&fw, (*tplg_files)[i], dev); + if (!ret) { + release_firmware(fw); + } else { + dev_warn(dev, + "Failed to open topology file: %s, you might need to\n", + (*tplg_files)[i]); + dev_warn(dev, + "download it from https://github.com/thesofproject/sof-bin/\n"); + return false; + } + } + + return true; +} + +static char *get_tplg_filename(struct device *dev, const char *prefix, + const char *platform, const char *tplg_dev_name, + int dai_link_id, int tplg_dev) +{ + char *filename = NULL; + /* + * The tplg file naming rule is sof---id.tplg + * where is only required for the devices that need NHLT blob like DMIC + * as the nhlt blob is platform dependent. + */ + switch (tplg_dev) { + case TPLG_DEVICE_INTEL_PCH_DMIC: + case TPLG_DEVICE_SSP_JACK: + case TPLG_DEVICE_SSP_AMP: + case TPLG_DEVICE_SSP_BT: + case TPLG_DEVICE_SSP_HDMI_IN: + filename = devm_kasprintf(dev, GFP_KERNEL, "%s/sof-%s-%s-id%d.tplg", + prefix, platform, tplg_dev_name, dai_link_id); + break; + default: + filename = devm_kasprintf(dev, GFP_KERNEL, "%s/sof-%s-id%d.tplg", + prefix, tplg_dev_name, dai_link_id); + break; + } + + return filename; +} + int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach, const char *prefix, const char ***tplg_files, bool best_effort) { struct snd_soc_acpi_mach_params mach_params = mach->mach_params; struct snd_soc_dai_link *dai_link; - const struct firmware *fw; char platform[SOF_INTEL_PLATFORM_NAME_MAX]; unsigned long tplg_mask = 0; + u16 hdmi_in_mask = 0; int tplg_num = 0; int tplg_dev; int ret; int i; - ret = sscanf(mach->sof_tplg_filename, "sof-%3s-*.tplg", platform); - if (ret != 1) { - dev_err(card->dev, "Invalid platform name %s of tplg %s\n", - platform, mach->sof_tplg_filename); - return -EINVAL; - } + ret = get_platform_name(card, mach, platform); + if (ret < 0) + return ret; for_each_card_prelinks(card, i, dai_link) { char *tplg_dev_name; @@ -82,6 +149,45 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ tplg_dev = TPLG_DEVICE_HDMI; tplg_dev_name = "hdmi-pcm5"; + } else if (strstr(dai_link->name, "SSP")) { + unsigned int ssp_port; + + if (sscanf(dai_link->name, "SSP%d", &ssp_port) != 1) { + dev_err(card->dev, "Invalid SSP port %d\n", ssp_port); + return -EINVAL; + } + if (strstr(dai_link->name, "Codec")) { + /* + * Assume DAI link 0 is jack which is true in all existing + * machine driver + */ + if (dai_link->id == 0) { + tplg_dev = TPLG_DEVICE_SSP_JACK; + tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL, + "ssp%d-jack", ssp_port); + } else { + tplg_dev = TPLG_DEVICE_SSP_AMP; + tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL, + "ssp%d-amp", ssp_port); + } + } else if (strstr(dai_link->name, "BT")) { + tplg_dev = TPLG_DEVICE_SSP_BT; + tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL, + "ssp%d-bt", ssp_port); + } else if (strstr(dai_link->name, "HDMI")) { + hdmi_in_mask |= BIT(ssp_port); + /* The number of HDMI in dai link is always 2 right now */ + if (hweight16(hdmi_in_mask) != 2) + continue; + + tplg_dev = TPLG_DEVICE_SSP_HDMI_IN; + tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL, + "ssp%x-hdmiin", hdmi_in_mask); + } else { + dev_warn(card->dev, + "unsupported SSP link %s\n", dai_link->name); + continue; + } } else { /* The dai link is not supported by separated tplg yet */ dev_dbg(card->dev, @@ -97,25 +203,9 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ tplg_mask |= BIT(tplg_dev); - /* - * The tplg file naming rule is sof---id.tplg - * where is only required for the DMIC function as the nhlt blob - * is platform dependent. - */ - switch (tplg_dev) { - case TPLG_DEVICE_INTEL_PCH_DMIC: - (*tplg_files)[tplg_num] = devm_kasprintf(card->dev, GFP_KERNEL, - "%s/sof-%s-%s-id%d.tplg", - prefix, platform, - tplg_dev_name, dai_link->id); - break; - default: - (*tplg_files)[tplg_num] = devm_kasprintf(card->dev, GFP_KERNEL, - "%s/sof-%s-id%d.tplg", - prefix, tplg_dev_name, - dai_link->id); - break; - } + (*tplg_files)[tplg_num] = get_tplg_filename(card->dev, prefix, platform, + tplg_dev_name, dai_link->id, + tplg_dev); if (!(*tplg_files)[tplg_num]) return -ENOMEM; tplg_num++; @@ -124,20 +214,119 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ dev_dbg(card->dev, "tplg_mask %#lx tplg_num %d\n", tplg_mask, tplg_num); /* Check presence of sub-topologies */ - for (i = 0; i < tplg_num; i++) { - ret = firmware_request_nowarn(&fw, (*tplg_files)[i], card->dev); - if (!ret) { - release_firmware(fw); + if (all_tplg_files_exist(card->dev, tplg_files, tplg_num)) + return tplg_num; + + /* return 0 to use monolithic topology */ + return 0; +} +EXPORT_SYMBOL_GPL(sof_sdw_get_tplg_files); + +int sof_i2s_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach, + const char *prefix, const char ***tplg_files, bool best_effort) +{ + struct snd_soc_acpi_mach_params mach_params = mach->mach_params; + struct snd_soc_dai_link *dai_link; + char platform[SOF_INTEL_PLATFORM_NAME_MAX]; + unsigned long tplg_mask = 0; + u16 hdmi_in_mask = 0; + int tplg_num = 0; + int tplg_dev; + int ret; + int i; + + ret = get_platform_name(card, mach, platform); + if (ret < 0) + return ret; + + for_each_card_prelinks(card, i, dai_link) { + char *tplg_dev_name; + + dev_dbg(card->dev, "dai_link %s id %d\n", dai_link->name, dai_link->id); + if (strstr(dai_link->name, "SSP")) { + unsigned int ssp_port; + + if (sscanf(dai_link->name, "SSP%d", &ssp_port) != 1) { + dev_err(card->dev, "Invalid SSP port %d\n", ssp_port); + return -EINVAL; + } + if (strstr(dai_link->name, "Codec")) { + /* + * Assume DAI link 0 is jack which is true in all existing + * machine driver + */ + if (dai_link->id == 0) { + tplg_dev = TPLG_DEVICE_SSP_JACK; + tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL, + "ssp%d-jack", ssp_port); + } else { + tplg_dev = TPLG_DEVICE_SSP_AMP; + tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL, + "ssp%d-amp", ssp_port); + } + } else if (strstr(dai_link->name, "BT")) { + tplg_dev = TPLG_DEVICE_SSP_BT; + tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL, + "ssp%d-bt", ssp_port); + } else if (strstr(dai_link->name, "HDMI")) { + hdmi_in_mask |= BIT(ssp_port); + /* The number of HDMI in dai link is always 2 right now */ + if (hweight16(hdmi_in_mask) != 2) + continue; + + tplg_dev = TPLG_DEVICE_SSP_HDMI_IN; + tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL, + "ssp%x-hdmiin", hdmi_in_mask); + } else { + dev_warn(card->dev, + "unsupported SSP link %s\n", dai_link->name); + continue; + } + } else if (strstr(dai_link->name, "dmic")) { + switch (mach_params.dmic_num) { + case 2: + tplg_dev_name = "dmic-2ch"; + break; + case 4: + tplg_dev_name = "dmic-4ch"; + break; + default: + dev_warn(card->dev, + "unsupported number of dmics: %d\n", + mach_params.dmic_num); + continue; + } + tplg_dev = TPLG_DEVICE_INTEL_PCH_DMIC; + } else if (strstr(dai_link->name, "iDisp")) { + tplg_dev = TPLG_DEVICE_HDMI; + tplg_dev_name = "hdmi-pcm5"; } else { - dev_warn(card->dev, - "Failed to open topology file: %s, you might need to\n", - (*tplg_files)[i]); - dev_warn(card->dev, - "download it from https://github.com/thesofproject/sof-bin/\n"); + /* The dai link is not supported by separated tplg yet */ + dev_dbg(card->dev, + "dai_link %s is not supported by separated tplg yet\n", + dai_link->name); return 0; } + if (tplg_mask & BIT(tplg_dev)) + continue; + + tplg_mask |= BIT(tplg_dev); + + (*tplg_files)[tplg_num] = get_tplg_filename(card->dev, prefix, platform, + tplg_dev_name, dai_link->id, + tplg_dev); + if (!(*tplg_files)[tplg_num]) + return -ENOMEM; + tplg_num++; } - return tplg_num; + dev_dbg(card->dev, "tplg_mask %#lx tplg_num %d\n", tplg_mask, tplg_num); + + /* Check presence of sub-topologies */ + if (all_tplg_files_exist(card->dev, tplg_files, tplg_num)) + return tplg_num; + + /* return 0 to use monolithic topology */ + return 0; } -EXPORT_SYMBOL_GPL(sof_sdw_get_tplg_files); +EXPORT_SYMBOL_GPL(sof_i2s_get_tplg_files); diff --git a/sound/soc/intel/common/sof-function-topology-lib.h b/sound/soc/intel/common/sof-function-topology-lib.h index f358f8c52d7854..9755e97709685e 100644 --- a/sound/soc/intel/common/sof-function-topology-lib.h +++ b/sound/soc/intel/common/sof-function-topology-lib.h @@ -12,4 +12,7 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach, const char *prefix, const char ***tplg_files, bool best_effort); +int sof_i2s_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach, + const char *prefix, const char ***tplg_files, bool best_effort); + #endif diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 15faedeec16d78..2c5f82a015d7eb 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -760,8 +760,15 @@ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4 && !hda_use_tplg_nhlt) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; + struct snd_ipc4_nhlt *nhlt; - ipc4_data->nhlt = intel_nhlt_init(sdev->dev); + nhlt = devm_kzalloc(sdev->dev, sizeof(*nhlt), GFP_KERNEL); + if (!nhlt) + return; + + nhlt->nhlt = intel_nhlt_init(sdev->dev); + + list_add(&nhlt->list, &ipc4_data->nhlt_list); } } EXPORT_SYMBOL_NS(hda_set_dai_drv_ops, "SND_SOC_SOF_INTEL_HDA_COMMON"); @@ -770,9 +777,14 @@ void hda_ops_free(struct snd_sof_dev *sdev) { if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; + struct snd_ipc4_nhlt *nhlt; - if (!hda_use_tplg_nhlt) - intel_nhlt_free(ipc4_data->nhlt); + if (!hda_use_tplg_nhlt) { + nhlt = list_first_entry(&ipc4_data->nhlt_list, + struct snd_ipc4_nhlt, list); + intel_nhlt_free(nhlt->nhlt); + list_del(&nhlt->list); + } kfree(sdev->private); sdev->private = NULL; diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index e3007648d78681..f72966c2cdbc0d 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -149,6 +149,7 @@ static size_t sof_ipc4_fw_parse_basefw_ext_man(struct snd_sof_dev *sdev) ssize_t payload_offset; int ret; + INIT_LIST_HEAD(&ipc4_data->nhlt_list); if (sdev->dsp_test_mode_enabled) fw_lib = devm_kzalloc(sdev->dev, sizeof(*fw_lib), GFP_KERNEL); else diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index a8cdf9bc750b4d..c56073baa9b93f 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -58,13 +58,17 @@ struct sof_ipc4_fw_library { struct sof_ipc4_fw_module *modules; }; +struct snd_ipc4_nhlt { + struct list_head list; + void *nhlt; +}; + /** * struct sof_ipc4_fw_data - IPC4-specific data * @manifest_fw_hdr_offset: FW header offset in the manifest * @fw_lib_xa: XArray for firmware libraries, including basefw (ID = 0) * Used to store the FW libraries and to manage the unique IDs of the * libraries. - * @nhlt: NHLT table either from the BIOS or the topology manifest * @mtrace_type: mtrace type supported on the booted platform * @mtrace_log_bytes: log bytes as reported by the firmware via fw_config reply * @num_playback_streams: max number of playback DMAs, needed for CHAIN_DMA offset @@ -74,6 +78,7 @@ struct sof_ipc4_fw_library { * base firmware * @fw_context_save: Firmware supports full context save and restore * @libraries_restored: The libraries have been retained during firmware boot + * @nhlt_list: The NHLT tables from the BIOS and the topology manifest * * @load_library: Callback function for platform dependent library loading * @pipeline_state_mutex: Mutex to protect pipeline triggers, ref counts, states and deletion @@ -81,7 +86,6 @@ struct sof_ipc4_fw_library { struct sof_ipc4_fw_data { u32 manifest_fw_hdr_offset; struct xarray fw_lib_xa; - void *nhlt; enum sof_ipc4_mtrace_type mtrace_type; u32 mtrace_log_bytes; int num_playback_streams; @@ -90,6 +94,7 @@ struct sof_ipc4_fw_data { u32 max_libs_count; bool fw_context_save; bool libraries_restored; + struct list_head nhlt_list; int (*load_library)(struct snd_sof_dev *sdev, struct sof_ipc4_fw_library *fw_lib, bool reload); diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 622bffb50a1c79..46de70040a6227 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1753,6 +1753,7 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai { struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct nhlt_specific_cfg *cfg; + struct snd_ipc4_nhlt *nhlt; int sample_rate, channel_count; bool format_change = false; int bit_depth, ret; @@ -1788,10 +1789,16 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai * Query the type for the port and then pass that information back * to the blob lookup function. */ - dev_type = intel_nhlt_ssp_device_type(sdev->dev, ipc4_data->nhlt, - dai_index); - if (dev_type < 0) + list_for_each_entry(nhlt, &ipc4_data->nhlt_list, list) { + dev_type = intel_nhlt_ssp_device_type(sdev->dev, nhlt->nhlt, + dai_index); + if (dev_type >= 0) + break; + } + if (dev_type < 0) { + dev_err(sdev->dev, "%s: No match for SSP%d in NHLT table\n", __func__, dai_index); return dev_type; + } break; default: return 0; @@ -1801,9 +1808,20 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai dai_index, nhlt_type, dir, dev_type); /* find NHLT blob with matching params */ - cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt, dai_index, nhlt_type, - bit_depth, bit_depth, channel_count, sample_rate, - dir, dev_type); + if (nhlt) { + cfg = intel_nhlt_get_endpoint_blob(sdev->dev, nhlt->nhlt, dai_index, nhlt_type, + bit_depth, bit_depth, channel_count, + sample_rate, dir, dev_type); + } else { + list_for_each_entry(nhlt, &ipc4_data->nhlt_list, list) { + cfg = intel_nhlt_get_endpoint_blob(sdev->dev, nhlt->nhlt, dai_index, + nhlt_type, bit_depth, bit_depth, + channel_count, sample_rate, dir, + dev_type); + if (cfg) + break; + } + } if (!cfg) { bool get_new_blob = false; @@ -1837,13 +1855,15 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai } if (get_new_blob) { - cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt, - dai_index, nhlt_type, - bit_depth, bit_depth, - channel_count, sample_rate, - dir, dev_type); - if (cfg) - goto out; + list_for_each_entry(nhlt, &ipc4_data->nhlt_list, list) { + cfg = intel_nhlt_get_endpoint_blob(sdev->dev, nhlt->nhlt, + dai_index, nhlt_type, + bit_depth, bit_depth, + channel_count, sample_rate, + dir, dev_type); + if (cfg) + goto out; + } } dev_err(sdev->dev, @@ -3747,13 +3767,18 @@ static int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index, switch (le32_to_cpu(manifest_tlv->type)) { case SOF_MANIFEST_DATA_TYPE_NHLT: - /* no NHLT in BIOS, so use the one from topology manifest */ - if (ipc4_data->nhlt) - break; - ipc4_data->nhlt = devm_kmemdup(sdev->dev, manifest_tlv->data, - le32_to_cpu(manifest_tlv->size), GFP_KERNEL); - if (!ipc4_data->nhlt) + struct snd_ipc4_nhlt *tplg_nhlt; + + /* Get the nhlt from topology manifest*/ + tplg_nhlt = devm_kzalloc(sdev->dev, sizeof(*tplg_nhlt), GFP_KERNEL); + if (!tplg_nhlt) return -ENOMEM; + + tplg_nhlt->nhlt = devm_kmemdup(sdev->dev, manifest_tlv->data, + le32_to_cpu(manifest_tlv->size), GFP_KERNEL); + + list_add(&tplg_nhlt->list, &ipc4_data->nhlt_list); + break; default: dev_warn(scomp->dev, "Skipping unknown manifest data type %d\n",