[PATCH v4 4/7] ASoC: SDCA: Populate IRQ data earlier

From: Charles Keepax

Date: Tue Jul 21 2026 - 10:43:22 EST


Currently, the IRQ data (attached Entity/Control/etc) is populated
as the IRQ is requested. However, this can cause issues as
occasionally the setup process wants to access specifics of
an IRQ before the IRQ is actually enabled. To facilitate this
cache all the IRQ data during sdca_irq_populate_early() and make
sdca_irq_populate() simply request the outstanding IRQs. This
also has the advantage that sdca_irq_populate() can now just
iterate through the IRQ array which is much smaller/faster than
going through every Entity in the Function for Controls.

Signed-off-by: Charles Keepax <ckeepax@xxxxxxxxxxxxxxxxxxxxx>
---

Changes since v3:
- Updated some bits of kernel doc
- Moved before fixup_controls in the chain

include/sound/sdca_interrupts.h | 2 +
sound/soc/sdca/sdca_interrupts.c | 115 ++++++++++++-------------------
2 files changed, 47 insertions(+), 70 deletions(-)

diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h
index 8a44c19e917ce..3b30146e21db5 100644
--- a/include/sound/sdca_interrupts.h
+++ b/include/sound/sdca_interrupts.h
@@ -30,6 +30,7 @@ struct sdca_function_data;
* @function: Pointer to the Function that the interrupt is associated with.
* @entity: Pointer to the Entity that the interrupt is associated with.
* @control: Pointer to the Control that the interrupt is associated with.
+ * @handler: Handler function to be called for the IRQ.
* @priv: Pointer to private data for use by the handler.
* @free_priv: Pointer to a function that can be used to free the priv data.
* @irq: IRQ number allocated to this interrupt, also used internally to track
@@ -46,6 +47,7 @@ struct sdca_interrupt {
struct sdca_function_data *function;
struct sdca_entity *entity;
struct sdca_control *control;
+ irq_handler_t handler;

void *priv;
void (*free_priv)(struct sdca_interrupt *interrupt);
diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c
index cd2c5d49bb95b..6f0d8c0fe622d 100644
--- a/sound/soc/sdca/sdca_interrupts.c
+++ b/sound/soc/sdca/sdca_interrupts.c
@@ -421,7 +421,8 @@ static struct sdca_interrupt *get_interrupt_data(struct device *dev, int irq,
*
* This is intended to be used as part of the Function boot process. It
* can be called before the soundcard is registered (ie. doesn't depend
- * on component) and will register the FDL interrupts.
+ * on component) and will populate all the required IRQ data, as well as
+ * registering the FDL interrupts to start booting the device.
*
* Return: Zero on success, and a negative error code on failure.
*/
@@ -448,24 +449,36 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap,
else if (!interrupt)
continue;

+ ret = sdca_irq_data_populate(dev, regmap, NULL, function,
+ entity, control, interrupt);
+ if (ret)
+ return ret;
+
switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
- case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER):
- ret = sdca_irq_data_populate(dev, regmap, NULL,
- function, entity,
- control, interrupt);
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_STATUS):
+ interrupt->handler = function_status_handler;
+ break;
+ case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
+ interrupt->handler = detected_mode_handler;
+ interrupt->free_priv = sdca_jack_free_state;
+
+ ret = sdca_jack_alloc_state(interrupt);
if (ret)
return ret;
-
- interrupt->early_request = true;
+ break;
+ case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER):
+ interrupt->handler = fdl_owner_handler;
interrupt->free_priv = sdca_fdl_free_state;

ret = sdca_fdl_alloc_state(interrupt);
if (ret)
return ret;

+ interrupt->early_request = true;
+
ret = sdca_irq_request_locked(dev, info, irq,
interrupt->name,
- fdl_owner_handler,
+ interrupt->handler,
interrupt);
if (ret) {
dev_err(dev, "failed to request irq %s: %d\n",
@@ -473,7 +486,11 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap,
return ret;
}
break;
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER):
+ interrupt->handler = hid_handler;
+ break;
default:
+ interrupt->handler = base_handler;
break;
}
}
@@ -498,70 +515,26 @@ int sdca_irq_populate(struct sdca_function_data *function,
struct sdca_interrupt_info *info)
{
struct device *dev = component->dev;
- int i, j;
+ int i, ret;

guard(mutex)(&info->irq_lock);

- for (i = 0; i < function->num_entities; i++) {
- struct sdca_entity *entity = &function->entities[i];
-
- for (j = 0; j < entity->num_controls; j++) {
- struct sdca_control *control = &entity->controls[j];
- int irq = control->interrupt_position;
- struct sdca_interrupt *interrupt;
- irq_handler_t handler;
- int ret;
-
- interrupt = get_interrupt_data(dev, irq, info);
- if (IS_ERR(interrupt))
- return PTR_ERR(interrupt);
- else if (!interrupt)
- continue;
-
- ret = sdca_irq_data_populate(dev, NULL, component,
- function, entity, control,
- interrupt);
- if (ret)
- return ret;
-
- handler = base_handler;
-
- switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
- case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_STATUS):
- handler = function_status_handler;
- break;
- case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
- interrupt->free_priv = sdca_jack_free_state;
-
- ret = sdca_jack_alloc_state(interrupt);
- if (ret)
- return ret;
-
- handler = detected_mode_handler;
- break;
- case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER):
- interrupt->free_priv = sdca_fdl_free_state;
+ for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) {
+ struct sdca_interrupt *interrupt = &info->irqs[i];
+ int irq;

- ret = sdca_fdl_alloc_state(interrupt);
- if (ret)
- return ret;
+ if (interrupt->function != function || interrupt->irq)
+ continue;

- handler = fdl_owner_handler;
- break;
- case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER):
- handler = hid_handler;
- break;
- default:
- break;
- }
+ interrupt->component = component;

- ret = sdca_irq_request_locked(dev, info, irq, interrupt->name,
- handler, interrupt);
- if (ret) {
- dev_err(dev, "failed to request irq %s: %d\n",
- interrupt->name, ret);
- return ret;
- }
+ irq = interrupt->control->interrupt_position;
+ ret = sdca_irq_request_locked(dev, info, irq, interrupt->name,
+ interrupt->handler, interrupt);
+ if (ret) {
+ dev_err(dev, "failed to request irq %s: %d\n",
+ interrupt->name, ret);
+ return ret;
}
}

@@ -581,13 +554,15 @@ static void sdca_irq_cleanup_flags(struct device *dev,
for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) {
struct sdca_interrupt *interrupt = &info->irqs[i];

- if (interrupt->function != function || !interrupt->irq)
+ if (interrupt->function != function ||
+ (interrupt->early_request && !late_cleanup))
continue;

- if (interrupt->early_request && !late_cleanup)
- continue;
+ if (interrupt->irq)
+ sdca_irq_free_locked(dev, info, i, interrupt->name, interrupt);

- sdca_irq_free_locked(dev, info, i, interrupt->name, interrupt);
+ if (!late_cleanup)
+ continue;

if (interrupt->free_priv)
interrupt->free_priv(interrupt);
--
2.47.3