From 19f6748abbab8523a7b32a5e371e39d4d8d4aba5 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 5 Feb 2025 11:37:54 +0000 Subject: [PATCH] ASoC: SDCA: Parse initialization write table Each SDCA Function may contain a table of register writes that should be written out before the Function is used. Add code to parse this table from the DisCo tables in ACPI. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20250205113801.3699902-4-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_function.h | 20 +++++++++++ sound/soc/sdca/sdca_functions.c | 61 +++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index 3f4031d285d63..47fc1da8e4f33 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -10,6 +10,7 @@ #define __SDCA_FUNCTION_H__ #include +#include struct device; struct sdca_function_desc; @@ -20,6 +21,11 @@ struct sdca_function_desc; */ #define SDCA_MAX_ENTITY_COUNT 128 +/* + * Sanity check on number of initialization writes, can be expanded if needed. + */ +#define SDCA_MAX_INIT_COUNT 2048 + /** * enum sdca_function_type - SDCA Function Type codes * @SDCA_FUNCTION_TYPE_SMART_AMP: Amplifier with protection features. @@ -65,6 +71,16 @@ enum sdca_function_type { #define SDCA_FUNCTION_TYPE_HID_NAME "HID" #define SDCA_FUNCTION_TYPE_IMP_DEF_NAME "ImplementationDefined" +/** + * struct sdca_init_write - a single initialization write + * @addr: Register address to be written + * @val: Single byte value to be written + */ +struct sdca_init_write { + u32 addr; + u8 val; +}; + /** * enum sdca_entity0_controls - SDCA Controls for Entity 0 * @@ -167,7 +183,9 @@ struct sdca_entity { /** * struct sdca_function_data - top-level information for one SDCA function * @desc: Pointer to short descriptor from initial parsing. + * @init_table: Pointer to a table of initialization writes. * @entities: Dynamically allocated array of Entities. + * @num_init_table: Number of initialization writes. * @num_entities: Number of Entities reported in this Function. * @busy_max_delay: Maximum Function busy delay in microseconds, before an * error should be reported. @@ -175,7 +193,9 @@ struct sdca_entity { struct sdca_function_data { struct sdca_function_desc *desc; + struct sdca_init_write *init_table; struct sdca_entity *entities; + int num_init_table; int num_entities; unsigned int busy_max_delay; diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 72b82280918d7..f914ec3f86c9a 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -9,6 +9,7 @@ #define dev_fmt(fmt) "%s: " fmt, __func__ #include +#include #include #include #include @@ -185,6 +186,62 @@ void sdca_lookup_functions(struct sdw_slave *slave) } EXPORT_SYMBOL_NS(sdca_lookup_functions, "SND_SOC_SDCA"); +static int find_sdca_init_table(struct device *dev, + struct fwnode_handle *function_node, + struct sdca_function_data *function) +{ + struct sdca_init_write *init_write; + int write_size = sizeof(init_write->addr) + sizeof(init_write->val); + u8 *init_list, *init_iter; + int num_init_writes; + + num_init_writes = fwnode_property_count_u8(function_node, + "mipi-sdca-function-initialization-table"); + if (!num_init_writes || num_init_writes == -EINVAL) { + return 0; + } else if (num_init_writes < 0) { + dev_err(dev, "%pfwP: failed to read initialization table: %d\n", + function_node, num_init_writes); + return num_init_writes; + } else if (num_init_writes % write_size != 0) { + dev_err(dev, "%pfwP: init table size invalid\n", function_node); + return -EINVAL; + } else if (num_init_writes > SDCA_MAX_INIT_COUNT) { + dev_err(dev, "%pfwP: maximum init table size exceeded\n", function_node); + return -EINVAL; + } + + init_write = devm_kcalloc(dev, num_init_writes / write_size, + sizeof(*init_write), GFP_KERNEL); + if (!init_write) + return -ENOMEM; + + init_list = kcalloc(num_init_writes, sizeof(*init_list), GFP_KERNEL); + if (!init_list) + return -ENOMEM; + + fwnode_property_read_u8_array(function_node, + "mipi-sdca-function-initialization-table", + init_list, num_init_writes); + + function->num_init_table = num_init_writes; + function->init_table = init_write; + + for (init_iter = init_list; init_iter < init_list + num_init_writes;) { + u32 *addr = (u32 *)init_iter; + + init_write->addr = le32_to_cpu(*addr); + init_iter += sizeof(init_write->addr); + + init_write->val = *init_iter; + init_iter += sizeof(init_write->val); + } + + kfree(init_list); + + return 0; +} + static int find_sdca_entity(struct device *dev, struct fwnode_handle *function_node, struct fwnode_handle *entity_node, @@ -435,6 +492,10 @@ int sdca_parse_function(struct device *dev, dev_info(dev, "%pfwP: name %s delay %dus\n", function->desc->node, function->desc->name, function->busy_max_delay); + ret = find_sdca_init_table(dev, function_desc->node, function); + if (ret) + return ret; + ret = find_sdca_entities(dev, function_desc->node, function); if (ret) return ret; -- 2.39.5