From 6a236928b43c54541c428085996e66aa94f3fcfe Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Aug 2026 13:41:58 +0100 Subject: [PATCH 1/8] ASoC: SDCA: Tidy up error message Bring the entity_pde_event() error message slightly more in line with the other SDCA error messages. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260805124205.4152543-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_asoc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index 9a6c0036b7be..03486d1c1b2f 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -458,7 +458,7 @@ static int entity_pde_event(struct snd_soc_dapm_widget *widget, entity->pde.max_delay, entity->pde.num_max_delay); if (ret) - dev_err(component->dev, "%s: PDE transition %x -> %x failed, err=%d\n", + dev_err(component->dev, "%s: pde transition %x -> %x failed: %d\n", entity->label, from, to, ret); return ret; From 3dcc74d51b223e07b54e0ad83700601598cae994 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Aug 2026 13:41:59 +0100 Subject: [PATCH 2/8] ASoC: SDCA: Remove unused dev pointer argument Remove the now unused device pointer from sdca_asoc_pde_poll_actual_ps(). Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260805124205.4152543-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_asoc.h | 8 ++++---- sound/soc/codecs/rt766-sdca.c | 2 +- sound/soc/codecs/tac5xx2-sdw.c | 4 ++-- sound/soc/sdca/sdca_asoc.c | 11 +++++------ 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/include/sound/sdca_asoc.h b/include/sound/sdca_asoc.h index d3024c3b38b9..599a72f33515 100644 --- a/include/sound/sdca_asoc.h +++ b/include/sound/sdca_asoc.h @@ -107,9 +107,9 @@ int sdca_asoc_q78_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int sdca_asoc_q78_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -int sdca_asoc_pde_poll_actual_ps(struct device *dev, struct regmap *regmap, +int sdca_asoc_pde_poll_actual_ps(struct regmap *regmap, int function_id, int entity_id, - int from_ps, int to_ps, - const struct sdca_pde_delay *pde_delays, - int num_delays); + int from_ps, int to_ps, + const struct sdca_pde_delay *pde_delays, + int num_delays); #endif // __SDCA_ASOC_H__ diff --git a/sound/soc/codecs/rt766-sdca.c b/sound/soc/codecs/rt766-sdca.c index 49ee9cef5c54..4c5acd950d70 100644 --- a/sound/soc/codecs/rt766-sdca.c +++ b/sound/soc/codecs/rt766-sdca.c @@ -616,7 +616,7 @@ static int rt766_sdca_pde_event(struct snd_soc_dapm_widget *w, return -EINVAL; } - ret = sdca_asoc_pde_poll_actual_ps(component->dev, rt766->regmap, + ret = sdca_asoc_pde_poll_actual_ps(rt766->regmap, func_num, pde_num, from_ps, to_ps, diff --git a/sound/soc/codecs/tac5xx2-sdw.c b/sound/soc/codecs/tac5xx2-sdw.c index ace06f5ab58c..ea0408b71713 100644 --- a/sound/soc/codecs/tac5xx2-sdw.c +++ b/sound/soc/codecs/tac5xx2-sdw.c @@ -800,7 +800,7 @@ static int tac_sdw_hw_params(struct snd_pcm_substream *substream, return ret; } - ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id, pde_entity, + ret = sdca_asoc_pde_poll_actual_ps(tac_dev->regmap, function_id, pde_entity, SDCA_PDE_PS3, SDCA_PDE_PS0, NULL, 0); if (ret) dev_err(tac_dev->dev, "failed to transition func %d, pde %d from PS3 -> PS0, err=%d\n", @@ -847,7 +847,7 @@ static int tac_sdw_pcm_hw_free(struct snd_pcm_substream *substream, return ret; } - ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id, + ret = sdca_asoc_pde_poll_actual_ps(tac_dev->regmap, function_id, pde_entity, SDCA_PDE_PS0, SDCA_PDE_PS3, NULL, 0); if (ret) diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index 03486d1c1b2f..ce2e7c270765 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -364,7 +364,6 @@ static int entity_parse_ot(struct device *dev, /** * sdca_asoc_pde_poll_actual_ps - Verify PDE power state reached target state - * @dev: Pointer to the device for error logging. * @regmap: Register map for reading ACTUAL_PS register. * @function_id: SDCA function identifier. * @entity_id: SDCA entity identifier for the power domain. @@ -389,11 +388,11 @@ static int entity_parse_ot(struct device *dev, * polling times out before reaching the target state, or a negative error code if * a register read fails. */ -int sdca_asoc_pde_poll_actual_ps(struct device *dev, struct regmap *regmap, +int sdca_asoc_pde_poll_actual_ps(struct regmap *regmap, int function_id, int entity_id, - int from_ps, int to_ps, - const struct sdca_pde_delay *pde_delays, - int num_delays) + int from_ps, int to_ps, + const struct sdca_pde_delay *pde_delays, + int num_delays) { static const int polls = 100; static const int default_poll_us = 1000; @@ -451,7 +450,7 @@ static int entity_pde_event(struct snd_soc_dapm_widget *widget, return 0; } - ret = sdca_asoc_pde_poll_actual_ps(component->dev, component->regmap, + ret = sdca_asoc_pde_poll_actual_ps(component->regmap, SDW_SDCA_CTL_FUNC(widget->reg), SDW_SDCA_CTL_ENT(widget->reg), from, to, From c03f0b9a7d9966b1846f561e4f49ed481c524a0a Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Aug 2026 13:42:00 +0100 Subject: [PATCH 3/8] ASoC: SDCA: Move HID registration to IRQ time Currently, the SDCA code registers the HID device whilst parsing the DisCo information. This necessitates storing the HID device in the DisCo structs, which are intended to only store the parsed DisCo. Having the HID device registered so early in the process also causes some issues with cleaning up. Update the code to register the HID device as the IRQs are handled, this alleviates the previous concerns and brings the support inline with the other SDCA event handling. As part of this move the naming for the SDCA HID is also updated, it saves some complexity around the passing of the SoundWire device to include this in this patch. Update to using the dev_name for the phys, which is more consistent with other HID users, and use the actual function name/address for the HID name itself. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260805124205.4152543-4-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_function.h | 2 -- include/sound/sdca_hid.h | 6 ++---- sound/soc/codecs/rt766-sdca.c | 22 +++++++++++++++++++--- sound/soc/codecs/rt766-sdca.h | 2 ++ sound/soc/sdca/sdca_functions.c | 24 ++++++------------------ sound/soc/sdca/sdca_hid.c | 21 ++++++++++----------- sound/soc/sdca/sdca_interrupts.c | 4 ++++ 7 files changed, 43 insertions(+), 38 deletions(-) diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index fb931ae735a2..35799a977145 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -1116,7 +1116,6 @@ struct sdca_entity_ge { /** * struct sdca_entity_hide - information specific to HIDE Entities - * @hid: HID device structure * @num_hidtx_ids: number of HIDTx Report ID * @num_hidrx_ids: number of HIDRx Report ID * @hidtx_ids: HIDTx Report ID @@ -1131,7 +1130,6 @@ struct sdca_entity_ge { * @hid_desc: HID descriptor for the HIDE Entity */ struct sdca_entity_hide { - struct hid_device *hid; unsigned int *hidtx_ids; unsigned int *hidrx_ids; int num_hidtx_ids; diff --git a/include/sound/sdca_hid.h b/include/sound/sdca_hid.h index 18bebbe428c9..83d1c7768133 100644 --- a/include/sound/sdca_hid.h +++ b/include/sound/sdca_hid.h @@ -16,14 +16,12 @@ struct sdca_interrupt; #if IS_ENABLED(CONFIG_SND_SOC_SDCA_HID) -int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, - struct sdca_entity *entity); +int sdca_add_hid_device(struct sdca_interrupt *interrupt); int sdca_hid_process_report(struct sdca_interrupt *interrupt); #else -static inline int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, - struct sdca_entity *entity) +static inline int sdca_add_hid_device(struct sdca_interrupt *interrupt) { return 0; } diff --git a/sound/soc/codecs/rt766-sdca.c b/sound/soc/codecs/rt766-sdca.c index 4c5acd950d70..54ed0c42fba2 100644 --- a/sound/soc/codecs/rt766-sdca.c +++ b/sound/soc/codecs/rt766-sdca.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -33,7 +34,6 @@ static int rt766_sdca_btn_detect(struct sdca_interrupt *interrupt) { struct rt766_sdca_priv *rt766 = interrupt->priv; - struct sdca_entity *ent_hid = interrupt->entity; unsigned char *buf = NULL; unsigned int offset, owner, length; unsigned int det_mode, idx, val; @@ -85,8 +85,8 @@ static int rt766_sdca_btn_detect(struct sdca_interrupt *interrupt) buf[idx] = val & 0xff; } - if (ent_hid) - hid_input_report(ent_hid->hide.hid, HID_INPUT_REPORT, + if (rt766->hid) + hid_input_report(rt766->hid, HID_INPUT_REPORT, buf, length, 1); } @@ -191,6 +191,13 @@ static irqreturn_t rt766_sdca_irq_jd_handler(int irq, void *data) return IRQ_HANDLED; } +static void rt766_sdca_destroy_hid_device(struct sdca_interrupt *interrupt) +{ + struct rt766_sdca_priv *rt766 = interrupt->priv; + + hid_destroy_device(rt766->hid); +} + static int rt766_sdca_irq_ctl(struct rt766_sdca_priv *rt766, struct sdca_function_data *function, struct snd_soc_component *component, @@ -231,6 +238,15 @@ static int rt766_sdca_irq_ctl(struct rt766_sdca_priv *rt766, if (ret) return ret; + if (handler == rt766_sdca_irq_btn_handler) { + ret = sdca_add_hid_device(interrupt); + if (ret) + return ret; + + interrupt->free_priv = rt766_sdca_destroy_hid_device; + rt766->hid = interrupt->priv; + } + interrupt->priv = rt766; ret = sdca_irq_request(dev, info, irq, interrupt->name, handler, interrupt); diff --git a/sound/soc/codecs/rt766-sdca.h b/sound/soc/codecs/rt766-sdca.h index 5acdb83a42fb..de4064007eb9 100644 --- a/sound/soc/codecs/rt766-sdca.h +++ b/sound/soc/codecs/rt766-sdca.h @@ -8,6 +8,7 @@ #ifndef __RT766_H__ #define __RT766_H__ +#include #include #include #include @@ -41,6 +42,7 @@ struct rt766_sdca_priv { struct sdca_function_data *sa_func_data; struct sdca_function_data *hid_func_data; struct sdca_interrupt_info *irq_info; + struct hid_device *hid; }; /* vendor registers */ diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index cdf1e68d60ac..e9b449b67033 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -18,7 +18,6 @@ #include #include #include -#include /* * Should be long enough to encompass all the MIPI DisCo properties. @@ -1366,8 +1365,7 @@ static int find_sdca_entity_ge(struct device *dev, } static int -find_sdca_entity_hide(struct device *dev, struct sdw_slave *sdw, - struct fwnode_handle *function_node, +find_sdca_entity_hide(struct device *dev, struct fwnode_handle *function_node, struct fwnode_handle *entity_node, struct sdca_entity *entity) { struct sdca_entity_hide *hide = &entity->hide; @@ -1440,13 +1438,6 @@ find_sdca_entity_hide(struct device *dev, struct sdw_slave *sdw, hide->hid_report_desc = report_desc; fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor", report_desc, nval); - - /* add HID device */ - ret = sdca_add_hid_device(dev, sdw, entity); - if (ret) { - dev_err(dev, "%pfwP: failed to add HID device: %d\n", entity_node, ret); - return ret; - } } } @@ -1475,8 +1466,7 @@ static int find_sdca_entity_xu(struct device *dev, return 0; } -static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, - struct sdca_function_data *function, +static int find_sdca_entity(struct device *dev, struct sdca_function_data *function, struct fwnode_handle *function_node, struct fwnode_handle *entity_node, struct sdca_entity *entity) @@ -1528,8 +1518,7 @@ static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, ret = find_sdca_entity_ge(dev, entity_node, entity); break; case SDCA_ENTITY_TYPE_HIDE: - ret = find_sdca_entity_hide(dev, sdw, function_node, - entity_node, entity); + ret = find_sdca_entity_hide(dev, function_node, entity_node, entity); break; default: break; @@ -1544,8 +1533,7 @@ static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, return 0; } -static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, - struct fwnode_handle *function_node, +static int find_sdca_entities(struct device *dev, struct fwnode_handle *function_node, struct sdca_function_data *function) { struct sdca_entity *entities; @@ -1596,7 +1584,7 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, return -EINVAL; } - ret = find_sdca_entity(dev, sdw, function, function_node, + ret = find_sdca_entity(dev, function, function_node, entity_node, &entities[i]); fwnode_handle_put(entity_node); if (ret) @@ -2214,7 +2202,7 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, if (ret) return ret; - ret = find_sdca_entities(dev, sdw, node, function); + ret = find_sdca_entities(dev, node, function); if (ret) return ret; diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c index abbd56a3d297..ea511c6f9798 100644 --- a/sound/soc/sdca/sdca_hid.c +++ b/sound/soc/sdca/sdca_hid.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -85,10 +86,11 @@ static const struct hid_ll_driver sdw_hid_driver = { .raw_request = sdwhid_raw_request, }; -int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, - struct sdca_entity *entity) +int sdca_add_hid_device(struct sdca_interrupt *interrupt) { - struct sdw_bus *bus = sdw->bus; + struct device *dev = interrupt->dev; + struct sdca_function_data *function = interrupt->function; + struct sdca_entity *entity = interrupt->entity; struct hid_device *hid; int ret; @@ -102,12 +104,9 @@ int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, hid->bus = BUS_SDW; hid->version = le16_to_cpu(entity->hide.hid_desc.bcdHID); - snprintf(hid->name, sizeof(hid->name), - "HID sdw:%01x:%01x:%04x:%04x:%02x", - bus->controller_id, bus->link_id, sdw->id.mfg_id, - sdw->id.part_id, sdw->id.class_id); - - snprintf(hid->phys, sizeof(hid->phys), "%s", dev->bus->name); + strscpy(hid->phys, dev_name(dev)); + snprintf(hid->name, sizeof(hid->name), "SDCA %s:%02x", + function->desc->name, function->desc->adr); hid->driver_data = entity; @@ -118,7 +117,7 @@ int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, return ret; } - entity->hide.hid = hid; + interrupt->priv = hid; return 0; } @@ -133,7 +132,7 @@ EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA"); int sdca_hid_process_report(struct sdca_interrupt *interrupt) { struct device *dev = interrupt->dev; - struct hid_device *hid = interrupt->entity->hide.hid; + struct hid_device *hid = interrupt->priv; void *val __free(kfree) = NULL; int len, ret; diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 42fbd3af8a75..71037189a057 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -487,6 +487,10 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap, } break; case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): + ret = sdca_add_hid_device(interrupt); + if (ret) + return ret; + interrupt->handler = hid_handler; break; default: From f3243b79026e31da037d1cbbbbda44317e3cbcb2 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Aug 2026 13:42:01 +0100 Subject: [PATCH 4/8] ASoC: SDCA: Move HID descriptors to function The HID descriptors are defined at the function level in DisCo and as such it makes more sense to parse and store them at that level in the SDCA code. This shouldn't really make much practical difference but is conceptually better and avoids passing the function node down to the entity parsing code. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260805124205.4152543-5-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_function.h | 19 ++++++++-- sound/soc/sdca/sdca_functions.c | 65 ++++++++++++++++++++++----------- sound/soc/sdca/sdca_hid.c | 11 +++--- 3 files changed, 63 insertions(+), 32 deletions(-) diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index 35799a977145..72441ed5eba4 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -1126,8 +1126,6 @@ struct sdca_entity_ge { * within this Device * @max_delay: the maximum time in microseconds allowed for the Device * to change the ownership from Device to Host - * @hid_report_desc: HID Report Descriptor for the HIDE Entity - * @hid_desc: HID descriptor for the HIDE Entity */ struct sdca_entity_hide { unsigned int *hidtx_ids; @@ -1137,8 +1135,6 @@ struct sdca_entity_hide { unsigned int af_number_list[SDCA_MAX_FUNCTION_COUNT]; unsigned int hide_reside_function_num; unsigned int max_delay; - unsigned char *hid_report_desc; - struct hid_descriptor hid_desc; }; /** @@ -1399,6 +1395,16 @@ struct sdca_fdl_data { int num_sets; }; +/** + * struct sdca_function_hid - information about a function's HID descriptors + * @report_desc: HID Report Descriptor for the HID Function + * @desc: HID descriptor for the HID Function + */ +struct sdca_function_hid { + unsigned char *report_desc; + struct hid_descriptor desc; +}; + /** * struct sdca_function_data - top-level information for one SDCA function * @desc: Pointer to short descriptor from initial parsing. @@ -1413,6 +1419,7 @@ struct sdca_fdl_data { * @reset_max_delay: Maximum Function reset delay in microseconds, before an * error should be reported. * @fdl_data: FDL data for this Function, if available. + * @hid: HID data for this Function, if available. */ struct sdca_function_data { struct sdca_function_desc *desc; @@ -1428,6 +1435,10 @@ struct sdca_function_data { unsigned int reset_max_delay; struct sdca_fdl_data fdl_data; + + union { + struct sdca_function_hid hid; + }; }; static inline u32 sdca_range(struct sdca_control_range *range, diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index e9b449b67033..e49acfe49e28 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -1364,14 +1364,13 @@ static int find_sdca_entity_ge(struct device *dev, return -EINVAL; } -static int -find_sdca_entity_hide(struct device *dev, struct fwnode_handle *function_node, - struct fwnode_handle *entity_node, struct sdca_entity *entity) +static int find_sdca_entity_hide(struct device *dev, + struct fwnode_handle *entity_node, + struct sdca_entity *entity) { struct sdca_entity_hide *hide = &entity->hide; unsigned int delay, *af_list = hide->af_number_list; int nval, ret; - unsigned char *report_desc = NULL; ret = fwnode_property_read_u32(entity_node, "mipi-sdca-RxUMP-ownership-transition-max-delay", &delay); @@ -1424,23 +1423,6 @@ find_sdca_entity_hide(struct device *dev, struct fwnode_handle *function_node, fwnode_property_read_u32_array(entity_node, "mipi-sdca-hide-related-audio-function-list", af_list, nval); - nval = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); - if (nval) - fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", - (u8 *)&hide->hid_desc, nval); - - if (hide->hid_desc.bNumDescriptors) { - nval = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); - if (nval) { - report_desc = devm_kzalloc(dev, nval, GFP_KERNEL); - if (!report_desc) - return -ENOMEM; - hide->hid_report_desc = report_desc; - fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor", - report_desc, nval); - } - } - return 0; } @@ -1518,7 +1500,7 @@ static int find_sdca_entity(struct device *dev, struct sdca_function_data *funct ret = find_sdca_entity_ge(dev, entity_node, entity); break; case SDCA_ENTITY_TYPE_HIDE: - ret = find_sdca_entity_hide(dev, function_node, entity_node, entity); + ret = find_sdca_entity_hide(dev, entity_node, entity); break; default: break; @@ -2167,6 +2149,35 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, return 0; } +static int find_sdca_hid(struct device *dev, struct fwnode_handle *function_node, + struct sdca_function_data *function) +{ + int nval; + + nval = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); + if (nval) + fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", + (u8 *)&function->hid.desc, nval); + + if (function->hid.desc.bNumDescriptors) { + nval = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); + if (nval) { + unsigned char *report_desc; + + report_desc = devm_kzalloc(dev, nval, GFP_KERNEL); + if (!report_desc) + return -ENOMEM; + + function->hid.report_desc = report_desc; + fwnode_property_read_u8_array(function_node, + "mipi-sdca-report-descriptor", + report_desc, nval); + } + } + + return 0; +} + /** * sdca_parse_function - parse ACPI DisCo for a Function * @dev: Pointer to device against which function data will be allocated. @@ -2218,6 +2229,16 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, if (ret) return ret; + switch (function->desc->type) { + case SDCA_FUNCTION_TYPE_HID: + ret = find_sdca_hid(dev, node, function); + if (ret) + return ret; + break; + default: + break; + } + return 0; } EXPORT_SYMBOL_NS(sdca_parse_function, "SND_SOC_SDCA"); diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c index ea511c6f9798..5000d73657b4 100644 --- a/sound/soc/sdca/sdca_hid.c +++ b/sound/soc/sdca/sdca_hid.c @@ -24,18 +24,18 @@ static int sdwhid_parse(struct hid_device *hid) { - struct sdca_entity *entity = hid->driver_data; + struct sdca_function_data *function = hid->driver_data; unsigned int rsize; int ret; - rsize = le16_to_cpu(entity->hide.hid_desc.rpt_desc.wDescriptorLength); + rsize = le16_to_cpu(function->hid.desc.rpt_desc.wDescriptorLength); if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { dev_err(&hid->dev, "invalid size of report descriptor (%u)\n", rsize); return -EINVAL; } - ret = hid_parse_report(hid, entity->hide.hid_report_desc, rsize); + ret = hid_parse_report(hid, function->hid.report_desc, rsize); if (!ret) return 0; @@ -90,7 +90,6 @@ int sdca_add_hid_device(struct sdca_interrupt *interrupt) { struct device *dev = interrupt->dev; struct sdca_function_data *function = interrupt->function; - struct sdca_entity *entity = interrupt->entity; struct hid_device *hid; int ret; @@ -102,13 +101,13 @@ int sdca_add_hid_device(struct sdca_interrupt *interrupt) hid->dev.parent = dev; hid->bus = BUS_SDW; - hid->version = le16_to_cpu(entity->hide.hid_desc.bcdHID); + hid->version = le16_to_cpu(function->hid.desc.bcdHID); strscpy(hid->phys, dev_name(dev)); snprintf(hid->name, sizeof(hid->name), "SDCA %s:%02x", function->desc->name, function->desc->adr); - hid->driver_data = entity; + hid->driver_data = function; ret = hid_add_device(hid); if (ret && ret != -ENODEV) { From 01dba3e93486d333c2a192b5250c1ed4dbb3093b Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Aug 2026 13:42:02 +0100 Subject: [PATCH 5/8] ASoC: SDCA: Update HID DisCo parsing Add more error checking on the parsing of the HID DisCo and bring the code more inline with the rest of the DisCo parsing. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260805124205.4152543-6-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_functions.c | 125 ++++++++++++++++++++------------ 1 file changed, 77 insertions(+), 48 deletions(-) diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index e49acfe49e28..1196cc09389a 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -1369,59 +1370,71 @@ static int find_sdca_entity_hide(struct device *dev, struct sdca_entity *entity) { struct sdca_entity_hide *hide = &entity->hide; - unsigned int delay, *af_list = hide->af_number_list; - int nval, ret; + int num_reports, ret; + unsigned int delay; ret = fwnode_property_read_u32(entity_node, - "mipi-sdca-RxUMP-ownership-transition-max-delay", &delay); + "mipi-sdca-RxUMP-ownership-transition-max-delay", + &delay); if (!ret) hide->max_delay = delay; - nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDTx-supported-report-ids"); - if (nval > 0) { - hide->num_hidtx_ids = nval; + num_reports = fwnode_property_count_u32(entity_node, + "mipi-sdca-HIDTx-supported-report-ids"); + if (num_reports < 0 && num_reports != -EINVAL) { + dev_err(dev, "%pfwP: failed to read hid tx ids: %d\n", + entity_node, num_reports); + return num_reports; + } else if (num_reports > 0) { + hide->num_hidtx_ids = num_reports; hide->hidtx_ids = devm_kcalloc(dev, hide->num_hidtx_ids, sizeof(*hide->hidtx_ids), GFP_KERNEL); if (!hide->hidtx_ids) return -ENOMEM; - ret = fwnode_property_read_u32_array(entity_node, - "mipi-sdca-HIDTx-supported-report-ids", - hide->hidtx_ids, - hide->num_hidtx_ids); - if (ret < 0) - return ret; + fwnode_property_read_u32_array(entity_node, + "mipi-sdca-HIDTx-supported-report-ids", + hide->hidtx_ids, hide->num_hidtx_ids); } - nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDRx-supported-report-ids"); - if (nval > 0) { - hide->num_hidrx_ids = nval; + num_reports = fwnode_property_count_u32(entity_node, + "mipi-sdca-HIDRx-supported-report-ids"); + if (num_reports < 0 && num_reports != -EINVAL) { + dev_err(dev, "%pfwP: failed to read hid rx ids: %d\n", + entity_node, num_reports); + return num_reports; + } else if (num_reports > 0) { + hide->num_hidrx_ids = num_reports; hide->hidrx_ids = devm_kcalloc(dev, hide->num_hidrx_ids, sizeof(*hide->hidrx_ids), GFP_KERNEL); if (!hide->hidrx_ids) return -ENOMEM; - ret = fwnode_property_read_u32_array(entity_node, - "mipi-sdca-HIDRx-supported-report-ids", - hide->hidrx_ids, - hide->num_hidrx_ids); - if (ret < 0) - return ret; + fwnode_property_read_u32_array(entity_node, + "mipi-sdca-HIDRx-supported-report-ids", + hide->hidrx_ids, hide->num_hidrx_ids); } - nval = fwnode_property_count_u32(entity_node, "mipi-sdca-hide-related-audio-function-list"); - if (nval <= 0) { + /* + * FIXME: This should probably link to the actual sdca_function_data pointer, + * but updating to do so should probably wait until we have a user. + */ + num_reports = fwnode_property_count_u32(entity_node, + "mipi-sdca-hide-related-audio-function-list"); + if (num_reports <= 0) { dev_err(dev, "%pfwP: audio function numbers list missing: %d\n", - entity_node, nval); + entity_node, num_reports); return -EINVAL; - } else if (nval > SDCA_MAX_FUNCTION_COUNT) { - dev_err(dev, "%pfwP: maximum number of audio function exceeded\n", entity_node); + } else if (num_reports > ARRAY_SIZE(hide->af_number_list)) { + dev_err(dev, "%pfwP: maximum number of audio function exceeded\n", + entity_node); return -EINVAL; } - hide->hide_reside_function_num = nval; + hide->hide_reside_function_num = num_reports; fwnode_property_read_u32_array(entity_node, - "mipi-sdca-hide-related-audio-function-list", af_list, nval); + "mipi-sdca-hide-related-audio-function-list", + hide->af_number_list, num_reports); return 0; } @@ -2152,29 +2165,45 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, static int find_sdca_hid(struct device *dev, struct fwnode_handle *function_node, struct sdca_function_data *function) { - int nval; + int num_desc; - nval = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); - if (nval) - fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", - (u8 *)&function->hid.desc, nval); - - if (function->hid.desc.bNumDescriptors) { - nval = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); - if (nval) { - unsigned char *report_desc; - - report_desc = devm_kzalloc(dev, nval, GFP_KERNEL); - if (!report_desc) - return -ENOMEM; - - function->hid.report_desc = report_desc; - fwnode_property_read_u8_array(function_node, - "mipi-sdca-report-descriptor", - report_desc, nval); - } + num_desc = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); + if (!num_desc) { + return 0; + } else if (num_desc < 0) { + dev_err(dev, "%pfwP: failed to read hid descriptor: %d\n", + function_node, num_desc); + return num_desc; + } else if (num_desc > sizeof(function->hid.desc)) { + dev_err(dev, "%pfwP: hid descriptor too large: %d\n", + function_node, num_desc); + return -EINVAL; } + fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", + (u8 *)&function->hid.desc, num_desc); + + if (!function->hid.desc.bNumDescriptors) + return 0; + + num_desc = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); + if (num_desc <= 0) { + dev_err(dev, "%pfwP: failed to read report descriptor: %d\n", + function_node, num_desc); + + if (!num_desc) + return -EINVAL; + + return num_desc; + } + + function->hid.report_desc = devm_kzalloc(dev, num_desc, GFP_KERNEL); + if (!function->hid.report_desc) + return -ENOMEM; + + fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor", + function->hid.report_desc, num_desc); + return 0; } From df1fd5d8ab403c264b364abe187ce94b2fa7b96f Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Aug 2026 13:42:03 +0100 Subject: [PATCH 6/8] ASoC: SDCA: Add missing destroy for HID device The SDCA code is currently missing a cleanup for HID devices when the drivers are unbound. Add the missing HID cleanup as part of the IRQ cleanup to mirror when the HID device is created. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260805124205.4152543-7-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_hid.h | 5 +++++ sound/soc/sdca/sdca_hid.c | 12 ++++++++++++ sound/soc/sdca/sdca_interrupts.c | 2 ++ 3 files changed, 19 insertions(+) diff --git a/include/sound/sdca_hid.h b/include/sound/sdca_hid.h index 83d1c7768133..848081df3e4e 100644 --- a/include/sound/sdca_hid.h +++ b/include/sound/sdca_hid.h @@ -17,6 +17,7 @@ struct sdca_interrupt; #if IS_ENABLED(CONFIG_SND_SOC_SDCA_HID) int sdca_add_hid_device(struct sdca_interrupt *interrupt); +void sdca_destroy_hid_device(struct sdca_interrupt *interrupt); int sdca_hid_process_report(struct sdca_interrupt *interrupt); #else @@ -26,6 +27,10 @@ static inline int sdca_add_hid_device(struct sdca_interrupt *interrupt) return 0; } +static inline void sdca_destroy_hid_device(struct sdca_interrupt *interrupt) +{ +} + static inline int sdca_hid_process_report(struct sdca_interrupt *interrupt) { return 0; diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c index 5000d73657b4..514f895bd90d 100644 --- a/sound/soc/sdca/sdca_hid.c +++ b/sound/soc/sdca/sdca_hid.c @@ -122,6 +122,18 @@ int sdca_add_hid_device(struct sdca_interrupt *interrupt) } EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA"); +/** + * sdca_destroy_hid_device - destroy the HID device + * @interrupt: Pointer to the SDCA interrupt information structure. + */ +void sdca_destroy_hid_device(struct sdca_interrupt *interrupt) +{ + struct hid_device *hid = interrupt->priv; + + hid_destroy_device(hid); +} +EXPORT_SYMBOL_NS(sdca_destroy_hid_device, "SND_SOC_SDCA"); + /** * sdca_hid_process_report - read a HID event from the device and report * @interrupt: Pointer to the SDCA interrupt information structure. diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 71037189a057..7aebc721a847 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -487,6 +487,8 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap, } break; case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): + interrupt->free_priv = sdca_destroy_hid_device; + ret = sdca_add_hid_device(interrupt); if (ret) return ret; From 3ede9e98ca1c85c176b1ee3de664495790e84ea5 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Aug 2026 13:42:04 +0100 Subject: [PATCH 7/8] ASoC: SDCA: Add missing HID kernel doc Add missing kernel doc for the function sdca_add_hid_device() Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260805124205.4152543-8-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_hid.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c index 514f895bd90d..bee1b83c05f2 100644 --- a/sound/soc/sdca/sdca_hid.c +++ b/sound/soc/sdca/sdca_hid.c @@ -86,6 +86,12 @@ static const struct hid_ll_driver sdw_hid_driver = { .raw_request = sdwhid_raw_request, }; +/** + * sdca_add_hid_device - create a new SDCA HID device + * @interrupt: Pointer to the SDCA interrupt information structure. + * + * Return: Zero on success, and a negative error code on failure. + */ int sdca_add_hid_device(struct sdca_interrupt *interrupt) { struct device *dev = interrupt->dev; From a50e530e05fad766003f84c0e206058c6efd70c8 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Aug 2026 13:42:05 +0100 Subject: [PATCH 8/8] ASoC: SDCA: Pass swft table through sdca_dev_register() Rather than passing the SoundWire slave into find_sdca_filesets(), stash the swift table whilst processing sdca_dev_register(). This allows us to completely remove the passing of the sdw_slave into the ACPI parsing code. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260805124205.4152543-9-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_function.h | 3 +-- sound/soc/codecs/rt766-sdca.c | 2 +- sound/soc/codecs/tac5xx2-sdw.c | 2 +- sound/soc/codecs/tas2783-sdw.c | 2 +- sound/soc/sdca/sdca_class_function.c | 2 +- sound/soc/sdca/sdca_function_device.c | 7 +++++-- sound/soc/sdca/sdca_functions.c | 10 +++------- 7 files changed, 13 insertions(+), 15 deletions(-) diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index 72441ed5eba4..f65a1d6784e8 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -1460,8 +1460,7 @@ static inline u32 sdca_range_search(struct sdca_control_range *range, return 0; } -int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, - struct sdca_function_data *function); +int sdca_parse_function(struct device *dev, struct sdca_function_data *function); const char *sdca_find_terminal_name(enum sdca_terminal_type type); diff --git a/sound/soc/codecs/rt766-sdca.c b/sound/soc/codecs/rt766-sdca.c index 54ed0c42fba2..64d763b96a06 100644 --- a/sound/soc/codecs/rt766-sdca.c +++ b/sound/soc/codecs/rt766-sdca.c @@ -1201,7 +1201,7 @@ int rt766_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave } func_data_ptr->desc = &slave->sdca_data.function[i]; - ret = sdca_parse_function(dev, slave, func_data_ptr); + ret = sdca_parse_function(dev, func_data_ptr); if (ret) { devm_kfree(dev, func_data_ptr); goto _free_dai_drv_; diff --git a/sound/soc/codecs/tac5xx2-sdw.c b/sound/soc/codecs/tac5xx2-sdw.c index ea0408b71713..fdb6213d1360 100644 --- a/sound/soc/codecs/tac5xx2-sdw.c +++ b/sound/soc/codecs/tac5xx2-sdw.c @@ -1940,7 +1940,7 @@ static s32 tac_sdw_probe(struct sdw_slave *peripheral, "failed to allocate %s function data", func_name); function_data->desc = &peripheral->sdca_data.function[i]; - ret = sdca_parse_function(dev, peripheral, function_data); + ret = sdca_parse_function(dev, function_data); if (!ret) *func_ptr = function_data; else diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index 34f17e063fa3..94f11e3b0c20 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -1348,7 +1348,7 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral, function_data->desc = &peripheral->sdca_data.function[i]; /* Parse the function */ - ret = sdca_parse_function(dev, peripheral, function_data); + ret = sdca_parse_function(dev, function_data); if (!ret) tas_dev->sa_func_data = function_data; else diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c index 2fb2b043c979..cc7045dc26e6 100644 --- a/sound/soc/sdca/sdca_class_function.c +++ b/sound/soc/sdca/sdca_class_function.c @@ -329,7 +329,7 @@ static int class_function_probe(struct auxiliary_device *auxdev, drv->core = core; drv->function = &sdev->function; - ret = sdca_parse_function(dev, core->sdw, drv->function); + ret = sdca_parse_function(dev, drv->function); if (ret) return ret; diff --git a/sound/soc/sdca/sdca_function_device.c b/sound/soc/sdca/sdca_function_device.c index b5ca98283a88..54604872ae0d 100644 --- a/sound/soc/sdca/sdca_function_device.c +++ b/sound/soc/sdca/sdca_function_device.c @@ -32,7 +32,8 @@ static void sdca_dev_release(struct device *dev) /* alloc, init and add link devices */ static struct sdca_dev *sdca_dev_register(struct device *parent, - struct sdca_function_desc *function_desc) + struct sdca_function_desc *function_desc, + struct acpi_table_swft *swft) { struct sdca_dev *sdev; struct auxiliary_device *auxdev; @@ -50,6 +51,7 @@ static struct sdca_dev *sdca_dev_register(struct device *parent, auxdev->dev.release = sdca_dev_release; sdev->function.desc = function_desc; + sdev->function.fdl_data.swft = swft; rc = ida_alloc(&sdca_function_ida, GFP_KERNEL); if (rc < 0) { @@ -99,7 +101,8 @@ int sdca_dev_register_functions(struct sdw_slave *slave) struct sdca_dev *func_dev; func_dev = sdca_dev_register(&slave->dev, - &sdca_data->function[i]); + &sdca_data->function[i], + sdca_data->swft); if (IS_ERR(func_dev)) { ret = PTR_ERR(func_dev); /* diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 1196cc09389a..e01d91eb3cc8 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -2072,8 +2072,7 @@ 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, +static int find_sdca_filesets(struct device *dev, struct fwnode_handle *function_node, struct sdca_function_data *function) { static const int mult_fileset = 3; @@ -2155,7 +2154,6 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, set->files = files; } - function->fdl_data.swft = sdw->sdca_data.swft; function->fdl_data.num_sets = num_sets; function->fdl_data.sets = sets; @@ -2210,13 +2208,11 @@ static int find_sdca_hid(struct device *dev, struct fwnode_handle *function_node /** * sdca_parse_function - parse ACPI DisCo for a Function * @dev: Pointer to device against which function data will be allocated. - * @sdw: SoundWire slave device to be processed. * @function: Pointer to the Function information, to be populated. * * Return: Returns 0 for success. */ -int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, - struct sdca_function_data *function) +int sdca_parse_function(struct device *dev, struct sdca_function_data *function) { struct fwnode_handle *node = function->desc->node; u32 tmp; @@ -2254,7 +2250,7 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, if (ret < 0) return ret; - ret = find_sdca_filesets(dev, sdw, node, function); + ret = find_sdca_filesets(dev, node, function); if (ret) return ret;