From: Maciej Strozek Date: Mon, 20 Oct 2025 15:55:06 +0000 (+0100) Subject: ASoC: SDCA: Add SDCA FDL data parsing X-Git-Tag: v6.19-rc1~156^2~3^2~82^2~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c4d096c3ca425562192a3626c30e82651d0f2c1c;p=thirdparty%2Fkernel%2Flinux.git ASoC: SDCA: Add SDCA FDL data parsing Add parsing of ACPI DisCo information specific to FDL (File DownLoad). DisCo contains a list of File Sets which can be requested by the device and within each of those a list of individual files to be downloaded to the device. Optionally the contents of the files may also be present in a special ACPI table, called SWFT (SoundWire File Table). Reviewed-by: Bard Liao Signed-off-by: Maciej Strozek Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20251020155512.353774-14-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- diff --git a/include/sound/sdca.h b/include/sound/sdca.h index 9c6a351c9d474..d38cdbfeb35f5 100644 --- a/include/sound/sdca.h +++ b/include/sound/sdca.h @@ -12,6 +12,7 @@ #include #include +struct acpi_table_swft; struct sdw_slave; #define SDCA_MAX_FUNCTION_COUNT 8 @@ -37,11 +38,13 @@ struct sdca_function_desc { * @num_functions: Total number of supported SDCA functions. Invalid/unsupported * functions will be skipped. * @function: Array of function descriptors. + * @swft: Pointer to the SWFT table, if available. */ struct sdca_device_data { u32 interface_revision; int num_functions; struct sdca_function_desc function[SDCA_MAX_FUNCTION_COUNT]; + struct acpi_table_swft *swft; }; enum sdca_quirk { @@ -52,12 +55,14 @@ enum sdca_quirk { #if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_SOC_SDCA) void sdca_lookup_functions(struct sdw_slave *slave); +void sdca_lookup_swft(struct sdw_slave *slave); void sdca_lookup_interface_revision(struct sdw_slave *slave); bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk); #else static inline void sdca_lookup_functions(struct sdw_slave *slave) {} +static inline void sdca_lookup_swft(struct sdw_slave *slave) {} static inline void sdca_lookup_interface_revision(struct sdw_slave *slave) {} static inline bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk) { diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index 6dd44a7a8a359..f557206cec83d 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -13,6 +13,7 @@ #include #include +struct acpi_table_swft; struct device; struct sdca_entity; struct sdca_function_desc; @@ -1338,6 +1339,42 @@ enum sdca_cluster_range { SDCA_CLUSTER_NCOLS = 2, }; +/** + * struct sdca_fdl_file - information about a file from a fileset used in FDL + * @vendor_id: Vendor ID of the file. + * @file_id: File ID of the file. + * @fdl_offset: Offset information for FDL. + */ +struct sdca_fdl_file { + u16 vendor_id; + u32 file_id; + u32 fdl_offset; +}; + +/** + * struct sdca_fdl_set - information about a set of files used in FDL + * @files: Array of files in this FDL set. + * @num_files: Number of files in this FDL set. + * @id: ID of the FDL set. + */ +struct sdca_fdl_set { + struct sdca_fdl_file *files; + int num_files; + u32 id; +}; + +/** + * struct sdca_fdl_data - information about a function's FDL data + * @swft: Pointer to the SoundWire File Table. + * @sets: Array of FDL sets used by this function. + * @num_sets: Number of FDL sets used by this function. + */ +struct sdca_fdl_data { + struct acpi_table_swft *swft; + struct sdca_fdl_set *sets; + int num_sets; +}; + /** * struct sdca_function_data - top-level information for one SDCA function * @desc: Pointer to short descriptor from initial parsing. @@ -1351,6 +1388,7 @@ enum sdca_cluster_range { * error should be reported. * @reset_max_delay: Maximum Function reset delay in microseconds, before an * error should be reported. + * @fdl_data: FDL data for this Function, if available. */ struct sdca_function_data { struct sdca_function_desc *desc; @@ -1364,6 +1402,8 @@ struct sdca_function_data { unsigned int busy_max_delay; unsigned int reset_max_delay; + + struct sdca_fdl_data fdl_data; }; static inline u32 sdca_range(struct sdca_control_range *range, diff --git a/sound/soc/sdca/sdca_device.c b/sound/soc/sdca/sdca_device.c index 4798ce2c8f0b4..405e80b979de8 100644 --- a/sound/soc/sdca/sdca_device.c +++ b/sound/soc/sdca/sdca_device.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -27,6 +28,25 @@ void sdca_lookup_interface_revision(struct sdw_slave *slave) } EXPORT_SYMBOL_NS(sdca_lookup_interface_revision, "SND_SOC_SDCA"); +static void devm_acpi_table_put(void *ptr) +{ + acpi_put_table((struct acpi_table_header *)ptr); +} + +void sdca_lookup_swft(struct sdw_slave *slave) +{ + acpi_status status; + + status = acpi_get_table(ACPI_SIG_SWFT, 0, + (struct acpi_table_header **)&slave->sdca_data.swft); + if (ACPI_FAILURE(status)) + dev_info(&slave->dev, "SWFT not available\n"); + else + devm_add_action_or_reset(&slave->dev, devm_acpi_table_put, + &slave->sdca_data.swft); +} +EXPORT_SYMBOL_NS(sdca_lookup_swft, "SND_SOC_SDCA"); + static bool sdca_device_quirk_rt712_vb(struct sdw_slave *slave) { struct sdw_slave_id *id = &slave->id; diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 6602727c73f73..b2e3fab9bd959 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -2010,6 +2010,95 @@ static int find_sdca_clusters(struct device *dev, return 0; } +static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, + struct fwnode_handle *function_node, + struct sdca_function_data *function) +{ + static const int mult_fileset = 3; + char fileset_name[SDCA_PROPERTY_LENGTH]; + u32 *filesets_list __free(kfree) = NULL; + struct sdca_fdl_set *sets; + int num_sets; + int i, j; + + num_sets = fwnode_property_count_u32(function_node, + "mipi-sdca-file-set-id-list"); + if (num_sets == 0 || num_sets == -EINVAL) { + return 0; + } else if (num_sets < 0) { + dev_err(dev, "%pfwP: failed to read file set list: %d\n", + function_node, num_sets); + return num_sets; + } + + filesets_list = kcalloc(num_sets, sizeof(u32), GFP_KERNEL); + if (!filesets_list) + return -ENOMEM; + + fwnode_property_read_u32_array(function_node, "mipi-sdca-file-set-id-list", + filesets_list, num_sets); + + sets = devm_kcalloc(dev, num_sets, sizeof(struct sdca_fdl_set), GFP_KERNEL); + if (!sets) + return -ENOMEM; + + for (i = 0; i < num_sets; i++) { + u32 *fileset_entries __free(kfree) = NULL; + struct sdca_fdl_set *set = &sets[i]; + struct sdca_fdl_file *files; + int num_files, num_entries; + + snprintf(fileset_name, sizeof(fileset_name), + "mipi-sdca-file-set-id-0x%X", filesets_list[i]); + + num_entries = fwnode_property_count_u32(function_node, fileset_name); + if (num_entries <= 0) { + dev_err(dev, "%pfwP: file set %d missing entries: %d\n", + function_node, filesets_list[i], num_entries); + return -EINVAL; + } else if (num_entries % mult_fileset != 0) { + dev_err(dev, "%pfwP: file set %d files not multiple of %d\n", + function_node, filesets_list[i], mult_fileset); + return -EINVAL; + } + + dev_info(dev, "fileset: %#x\n", filesets_list[i]); + + files = devm_kcalloc(dev, num_entries / mult_fileset, + sizeof(struct sdca_fdl_file), GFP_KERNEL); + if (!files) + return -ENOMEM; + + fileset_entries = kcalloc(num_entries, sizeof(u32), GFP_KERNEL); + if (!fileset_entries) + return -ENOMEM; + + fwnode_property_read_u32_array(function_node, fileset_name, + fileset_entries, num_entries); + + for (j = 0, num_files = 0; j < num_entries; num_files++) { + struct sdca_fdl_file *file = &files[num_files]; + + file->vendor_id = fileset_entries[j++]; + file->file_id = fileset_entries[j++]; + file->fdl_offset = fileset_entries[j++]; + + dev_info(dev, "file: %#x, vendor: %#x, offset: %#x\n", + file->file_id, file->vendor_id, file->fdl_offset); + } + + set->id = filesets_list[i]; + set->num_files = num_files; + set->files = files; + } + + function->fdl_data.swft = sdw->sdca_data.swft; + function->fdl_data.num_sets = num_sets; + function->fdl_data.sets = sets; + + return 0; +} + /** * sdca_parse_function - parse ACPI DisCo for a Function * @dev: Pointer to device against which function data will be allocated. @@ -2058,6 +2147,10 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, if (ret < 0) return ret; + ret = find_sdca_filesets(dev, sdw, function_desc->node, function); + if (ret) + return ret; + return 0; } EXPORT_SYMBOL_NS(sdca_parse_function, "SND_SOC_SDCA");