]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ASoC: SDCA: Populate IRQ data earlier
authorCharles Keepax <ckeepax@opensource.cirrus.com>
Tue, 21 Jul 2026 14:36:33 +0000 (15:36 +0100)
committerMark Brown <broonie@kernel.org>
Mon, 27 Jul 2026 17:47:06 +0000 (18:47 +0100)
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@opensource.cirrus.com>
Link: https://patch.msgid.link/20260721143636.361814-5-ckeepax@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie@kernel.org>
include/sound/sdca_interrupts.h
sound/soc/sdca/sdca_interrupts.c

index 8a44c19e917ce1f3bffc87e834816a56dbb775fe..3b30146e21db55a70cdcaa3dc2e86261e43e1b71 100644 (file)
@@ -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);
index cd2c5d49bb95b3b7ccdd1ed4b09aaaeb8b233c39..6f0d8c0fe622d956d72159e3501d36892e995dcb 100644 (file)
@@ -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);