* [PATCH v4 1/7] ASoC: SDCA: Rename sdca_irq_allocate() to include devm
2026-07-21 14:36 [PATCH v4 0/7] Fix races on creation of SDCA jack detection Charles Keepax
@ 2026-07-21 14:36 ` Charles Keepax
2026-07-21 14:36 ` [PATCH v4 2/7] ASoC: SDCA: Add sdca_irq_cleanup_late() Charles Keepax
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Charles Keepax @ 2026-07-21 14:36 UTC (permalink / raw)
To: broonie
Cc: lgirdwood, yung-chuan.liao, pierre-louis.bossart, shumingf,
linux-sound, linux-kernel, patches
Make it more clear sdca_irq_allocate() uses devm allocations by adding
it into the name.
Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
---
New since v3.
include/sound/sdca_interrupts.h | 5 +++--
sound/soc/sdca/sdca_class.c | 4 ++--
sound/soc/sdca/sdca_interrupts.c | 8 ++++----
3 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h
index a515cc3df0971..28fd44eb93343 100644
--- a/include/sound/sdca_interrupts.h
+++ b/include/sound/sdca_interrupts.h
@@ -86,8 +86,9 @@ int sdca_irq_populate(struct sdca_function_data *function,
void sdca_irq_cleanup(struct device *dev,
struct sdca_function_data *function,
struct sdca_interrupt_info *info);
-struct sdca_interrupt_info *sdca_irq_allocate(struct device *dev,
- struct regmap *regmap, int irq);
+
+struct sdca_interrupt_info *devm_sdca_irq_allocate(struct device *dev,
+ struct regmap *regmap, int irq);
void sdca_irq_enable_early(struct sdca_function_data *function,
struct sdca_interrupt_info *info);
diff --git a/sound/soc/sdca/sdca_class.c b/sound/soc/sdca/sdca_class.c
index 8d7b007a068fd..d7444f442c71c 100644
--- a/sound/soc/sdca/sdca_class.c
+++ b/sound/soc/sdca/sdca_class.c
@@ -111,8 +111,8 @@ static void class_boot_work(struct work_struct *work)
regcache_cache_only(drv->dev_regmap, false);
- drv->irq_info = sdca_irq_allocate(drv->dev, drv->dev_regmap,
- drv->sdw->irq);
+ drv->irq_info = devm_sdca_irq_allocate(drv->dev, drv->dev_regmap,
+ drv->sdw->irq);
if (IS_ERR(drv->irq_info))
goto err;
diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c
index 4539a52a8e32b..1e4efc0609d93 100644
--- a/sound/soc/sdca/sdca_interrupts.c
+++ b/sound/soc/sdca/sdca_interrupts.c
@@ -592,7 +592,7 @@ void sdca_irq_cleanup(struct device *dev,
EXPORT_SYMBOL_NS_GPL(sdca_irq_cleanup, "SND_SOC_SDCA");
/**
- * sdca_irq_allocate - allocate an SDCA interrupt structure for a device
+ * devm_sdca_irq_allocate - allocate an SDCA interrupt structure for a device
* @sdev: Device pointer against which things should be allocated.
* @regmap: regmap to be used for accessing the SDCA IRQ registers.
* @irq: The interrupt number.
@@ -604,8 +604,8 @@ EXPORT_SYMBOL_NS_GPL(sdca_irq_cleanup, "SND_SOC_SDCA");
* Return: A pointer to the allocated sdca_interrupt_info struct, or an
* error code.
*/
-struct sdca_interrupt_info *sdca_irq_allocate(struct device *sdev,
- struct regmap *regmap, int irq)
+struct sdca_interrupt_info *devm_sdca_irq_allocate(struct device *sdev,
+ struct regmap *regmap, int irq)
{
struct sdca_interrupt_info *info;
int ret, i;
@@ -634,7 +634,7 @@ struct sdca_interrupt_info *sdca_irq_allocate(struct device *sdev,
return info;
}
-EXPORT_SYMBOL_NS_GPL(sdca_irq_allocate, "SND_SOC_SDCA");
+EXPORT_SYMBOL_NS_GPL(devm_sdca_irq_allocate, "SND_SOC_SDCA");
static void irq_enable_flags(struct sdca_function_data *function,
struct sdca_interrupt_info *info, bool early)
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v4 2/7] ASoC: SDCA: Add sdca_irq_cleanup_late()
2026-07-21 14:36 [PATCH v4 0/7] Fix races on creation of SDCA jack detection Charles Keepax
2026-07-21 14:36 ` [PATCH v4 1/7] ASoC: SDCA: Rename sdca_irq_allocate() to include devm Charles Keepax
@ 2026-07-21 14:36 ` Charles Keepax
2026-07-21 14:36 ` [PATCH v4 3/7] ASoC: SDCA: Remove devm from primary IRQ cleanup Charles Keepax
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Charles Keepax @ 2026-07-21 14:36 UTC (permalink / raw)
To: broonie
Cc: lgirdwood, yung-chuan.liao, pierre-louis.bossart, shumingf,
linux-sound, linux-kernel, patches
The SDCA IRQs are split into two groups, those registered at bus probe
time (basically just FDL) and those registered at component time.
There currently exists only a single cleanup function, if the FDL IRQ
is freed at component time, then nothing would re-register it if the
component is probed again. But the IRQs depending on a component need
to be freed if the card is destroyed so they can't use stale
components.
Split the clean up into two functions one for the component level and
one for the bus level.
Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
---
Changes since v3:
- Switched to using an early request flag rather than a big switch.
- Renamed the cleanup flag to be a little clear.
include/sound/sdca_interrupts.h | 5 +++
sound/soc/sdca/sdca_class_function.c | 2 +-
sound/soc/sdca/sdca_interrupts.c | 53 ++++++++++++++++++++++------
3 files changed, 48 insertions(+), 12 deletions(-)
diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h
index 28fd44eb93343..38c6c58c2cc78 100644
--- a/include/sound/sdca_interrupts.h
+++ b/include/sound/sdca_interrupts.h
@@ -33,6 +33,7 @@ struct sdca_function_data;
* @priv: Pointer to private data for use by the handler.
* @irq: IRQ number allocated to this interrupt, also used internally to track
* the IRQ being assigned.
+ * @early_request: Flag to indicate this IRQ was requested at bus probe time.
*/
struct sdca_interrupt {
const char *name;
@@ -48,6 +49,7 @@ struct sdca_interrupt {
void *priv;
int irq;
+ bool early_request;
};
/**
@@ -86,6 +88,9 @@ int sdca_irq_populate(struct sdca_function_data *function,
void sdca_irq_cleanup(struct device *dev,
struct sdca_function_data *function,
struct sdca_interrupt_info *info);
+void sdca_irq_cleanup_late(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_interrupt_info *info);
struct sdca_interrupt_info *devm_sdca_irq_allocate(struct device *dev,
struct regmap *regmap, int irq);
diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c
index 1496a15f7d2ac..5ae6c727c796d 100644
--- a/sound/soc/sdca/sdca_class_function.c
+++ b/sound/soc/sdca/sdca_class_function.c
@@ -408,7 +408,7 @@ static void class_function_remove(struct auxiliary_device *auxdev)
{
struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
- sdca_irq_cleanup(drv->dev, drv->function, drv->core->irq_info);
+ sdca_irq_cleanup_late(drv->dev, drv->function, drv->core->irq_info);
}
static int class_function_runtime_suspend(struct device *dev)
diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c
index 1e4efc0609d93..d86884a89c405 100644
--- a/sound/soc/sdca/sdca_interrupts.c
+++ b/sound/soc/sdca/sdca_interrupts.c
@@ -456,6 +456,8 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap,
if (ret)
return ret;
+ interrupt->early_request = true;
+
ret = sdca_fdl_alloc_state(interrupt);
if (ret)
return ret;
@@ -562,17 +564,10 @@ int sdca_irq_populate(struct sdca_function_data *function,
}
EXPORT_SYMBOL_NS_GPL(sdca_irq_populate, "SND_SOC_SDCA");
-/**
- * sdca_irq_cleanup - Free all the individual IRQs for an SDCA Function
- * @dev: Device pointer against which the sdca_interrupt_info was allocated.
- * @function: Pointer to the SDCA Function.
- * @info: Pointer to the SDCA interrupt info for this device.
- *
- * Typically this would be called from the driver for a single SDCA Function.
- */
-void sdca_irq_cleanup(struct device *dev,
- struct sdca_function_data *function,
- struct sdca_interrupt_info *info)
+static void sdca_irq_cleanup_flags(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_interrupt_info *info,
+ bool late_cleanup)
{
int i;
@@ -584,13 +579,49 @@ void sdca_irq_cleanup(struct device *dev,
if (interrupt->function != function || !interrupt->irq)
continue;
+ if (interrupt->early_request && !late_cleanup)
+ continue;
+
sdca_irq_free_locked(dev, info, i, interrupt->name, interrupt);
kfree(interrupt->name);
}
}
+
+/**
+ * sdca_irq_cleanup - Free the regular IRQs for an SDCA Function
+ * @dev: Device pointer against which the sdca_interrupt_info was allocated.
+ * @function: Pointer to the SDCA Function.
+ * @info: Pointer to the SDCA interrupt info for this device.
+ *
+ * Typically this would be called from the driver for a single SDCA Function
+ * from component remove.
+ */
+void sdca_irq_cleanup(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_interrupt_info *info)
+{
+ sdca_irq_cleanup_flags(dev, function, info, false);
+}
EXPORT_SYMBOL_NS_GPL(sdca_irq_cleanup, "SND_SOC_SDCA");
+/**
+ * sdca_irq_cleanup_late - Free the early IRQs for an SDCA Function
+ * @dev: Device pointer against which the sdca_interrupt_info was allocated.
+ * @function: Pointer to the SDCA Function.
+ * @info: Pointer to the SDCA interrupt info for this device.
+ *
+ * Typically this would be called from the driver for a single SDCA Function
+ * from bus remove.
+ */
+void sdca_irq_cleanup_late(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_interrupt_info *info)
+{
+ sdca_irq_cleanup_flags(dev, function, info, true);
+}
+EXPORT_SYMBOL_NS_GPL(sdca_irq_cleanup_late, "SND_SOC_SDCA");
+
/**
* devm_sdca_irq_allocate - allocate an SDCA interrupt structure for a device
* @sdev: Device pointer against which things should be allocated.
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v4 3/7] ASoC: SDCA: Remove devm from primary IRQ cleanup
2026-07-21 14:36 [PATCH v4 0/7] Fix races on creation of SDCA jack detection Charles Keepax
2026-07-21 14:36 ` [PATCH v4 1/7] ASoC: SDCA: Rename sdca_irq_allocate() to include devm Charles Keepax
2026-07-21 14:36 ` [PATCH v4 2/7] ASoC: SDCA: Add sdca_irq_cleanup_late() Charles Keepax
@ 2026-07-21 14:36 ` Charles Keepax
2026-07-21 14:36 ` [PATCH v4 4/7] ASoC: SDCA: Populate IRQ data earlier Charles Keepax
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Charles Keepax @ 2026-07-21 14:36 UTC (permalink / raw)
To: broonie
Cc: lgirdwood, yung-chuan.liao, pierre-louis.bossart, shumingf,
linux-sound, linux-kernel, patches
To provide greater flexibility on when the IRQs are requested for
client drivers don't use devm for the primary IRQ request/cleanup
helper functions.
Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
---
New since v3.
include/sound/sdca_fdl.h | 2 ++
include/sound/sdca_interrupts.h | 2 ++
include/sound/sdca_jack.h | 2 ++
sound/soc/sdca/sdca_fdl.c | 13 +++++++++++--
sound/soc/sdca/sdca_interrupts.c | 8 ++++++++
sound/soc/sdca/sdca_jack.c | 13 +++++++++++--
6 files changed, 36 insertions(+), 4 deletions(-)
diff --git a/include/sound/sdca_fdl.h b/include/sound/sdca_fdl.h
index fbaf4b384c8af..dc33927b82bde 100644
--- a/include/sound/sdca_fdl.h
+++ b/include/sound/sdca_fdl.h
@@ -67,6 +67,8 @@ struct fdl_state {
#if IS_ENABLED(CONFIG_SND_SOC_SDCA_FDL)
int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt);
+void sdca_fdl_free_state(struct sdca_interrupt *interrupt);
+
int sdca_fdl_process(struct sdca_interrupt *interrupt);
int sdca_fdl_sync(struct device *dev, struct sdca_function_data *function,
struct sdca_interrupt_info *info);
diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h
index 38c6c58c2cc78..8a44c19e917ce 100644
--- a/include/sound/sdca_interrupts.h
+++ b/include/sound/sdca_interrupts.h
@@ -31,6 +31,7 @@ struct sdca_function_data;
* @entity: Pointer to the Entity that the interrupt is associated with.
* @control: Pointer to the Control that the interrupt is associated with.
* @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
* the IRQ being assigned.
* @early_request: Flag to indicate this IRQ was requested at bus probe time.
@@ -47,6 +48,7 @@ struct sdca_interrupt {
struct sdca_control *control;
void *priv;
+ void (*free_priv)(struct sdca_interrupt *interrupt);
int irq;
bool early_request;
diff --git a/include/sound/sdca_jack.h b/include/sound/sdca_jack.h
index 181541f0f4d8c..59de40b7d7d01 100644
--- a/include/sound/sdca_jack.h
+++ b/include/sound/sdca_jack.h
@@ -28,6 +28,8 @@ struct jack_state {
};
int sdca_jack_alloc_state(struct sdca_interrupt *interrupt);
+void sdca_jack_free_state(struct sdca_interrupt *interrupt);
+
int sdca_jack_process(struct sdca_interrupt *interrupt);
int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *jack);
int sdca_jack_report(struct sdca_interrupt *interrupt);
diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c
index 994821a6df617..82e09d960c127 100644
--- a/sound/soc/sdca/sdca_fdl.c
+++ b/sound/soc/sdca/sdca_fdl.c
@@ -481,10 +481,9 @@ EXPORT_SYMBOL_NS_GPL(sdca_fdl_process, "SND_SOC_SDCA");
*/
int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt)
{
- struct device *dev = interrupt->dev;
struct fdl_state *fdl_state;
- fdl_state = devm_kzalloc(dev, sizeof(*fdl_state), GFP_KERNEL);
+ fdl_state = kzalloc_obj(*fdl_state);
if (!fdl_state)
return -ENOMEM;
@@ -499,3 +498,13 @@ int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt)
return 0;
}
EXPORT_SYMBOL_NS_GPL(sdca_fdl_alloc_state, "SND_SOC_SDCA");
+
+/**
+ * sdca_fdl_free_state - free state for an FDL interrupt
+ * @interrupt: SDCA interrupt structure.
+ */
+void sdca_fdl_free_state(struct sdca_interrupt *interrupt)
+{
+ kfree(interrupt->priv);
+}
+EXPORT_SYMBOL_NS_GPL(sdca_fdl_free_state, "SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c
index d86884a89c405..cd2c5d49bb95b 100644
--- a/sound/soc/sdca/sdca_interrupts.c
+++ b/sound/soc/sdca/sdca_interrupts.c
@@ -457,6 +457,7 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap,
return ret;
interrupt->early_request = true;
+ interrupt->free_priv = sdca_fdl_free_state;
ret = sdca_fdl_alloc_state(interrupt);
if (ret)
@@ -530,6 +531,8 @@ int sdca_irq_populate(struct sdca_function_data *function,
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;
@@ -537,6 +540,8 @@ int sdca_irq_populate(struct sdca_function_data *function,
handler = detected_mode_handler;
break;
case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER):
+ interrupt->free_priv = sdca_fdl_free_state;
+
ret = sdca_fdl_alloc_state(interrupt);
if (ret)
return ret;
@@ -584,6 +589,9 @@ static void sdca_irq_cleanup_flags(struct device *dev,
sdca_irq_free_locked(dev, info, i, interrupt->name, interrupt);
+ if (interrupt->free_priv)
+ interrupt->free_priv(interrupt);
+
kfree(interrupt->name);
}
}
diff --git a/sound/soc/sdca/sdca_jack.c b/sound/soc/sdca/sdca_jack.c
index ae9636622a840..ffa8709649248 100644
--- a/sound/soc/sdca/sdca_jack.c
+++ b/sound/soc/sdca/sdca_jack.c
@@ -132,10 +132,9 @@ EXPORT_SYMBOL_NS_GPL(sdca_jack_process, "SND_SOC_SDCA");
*/
int sdca_jack_alloc_state(struct sdca_interrupt *interrupt)
{
- struct device *dev = interrupt->dev;
struct jack_state *jack_state;
- jack_state = devm_kzalloc(dev, sizeof(*jack_state), GFP_KERNEL);
+ jack_state = kzalloc_obj(*jack_state);
if (!jack_state)
return -ENOMEM;
@@ -145,6 +144,16 @@ int sdca_jack_alloc_state(struct sdca_interrupt *interrupt)
}
EXPORT_SYMBOL_NS_GPL(sdca_jack_alloc_state, "SND_SOC_SDCA");
+/**
+ * sdca_jack_free_state - free state for a jack interrupt
+ * @interrupt: SDCA interrupt structure.
+ */
+void sdca_jack_free_state(struct sdca_interrupt *interrupt)
+{
+ kfree(interrupt->priv);
+}
+EXPORT_SYMBOL_NS_GPL(sdca_jack_free_state, "SND_SOC_SDCA");
+
static int type_get_mask(enum sdca_terminal_type type)
{
switch (type) {
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v4 4/7] ASoC: SDCA: Populate IRQ data earlier
2026-07-21 14:36 [PATCH v4 0/7] Fix races on creation of SDCA jack detection Charles Keepax
` (2 preceding siblings ...)
2026-07-21 14:36 ` [PATCH v4 3/7] ASoC: SDCA: Remove devm from primary IRQ cleanup Charles Keepax
@ 2026-07-21 14:36 ` Charles Keepax
2026-07-21 14:36 ` [PATCH v4 5/7] ASoC: Add a component fixup_controls callback Charles Keepax
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Charles Keepax @ 2026-07-21 14:36 UTC (permalink / raw)
To: broonie
Cc: lgirdwood, yung-chuan.liao, pierre-louis.bossart, shumingf,
linux-sound, linux-kernel, patches
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>
---
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
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v4 5/7] ASoC: Add a component fixup_controls callback
2026-07-21 14:36 [PATCH v4 0/7] Fix races on creation of SDCA jack detection Charles Keepax
` (3 preceding siblings ...)
2026-07-21 14:36 ` [PATCH v4 4/7] ASoC: SDCA: Populate IRQ data earlier Charles Keepax
@ 2026-07-21 14:36 ` Charles Keepax
2026-07-21 14:36 ` [PATCH v4 6/7] ASoC: SDCA: Switch to fixup_controls callback for IRQ registration Charles Keepax
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Charles Keepax @ 2026-07-21 14:36 UTC (permalink / raw)
To: broonie
Cc: lgirdwood, yung-chuan.liao, pierre-louis.bossart, shumingf,
linux-sound, linux-kernel, patches
A card level fixup_controls callback was added in:
commit df4d27b19b89 ("ASoC: Introduce 'fixup_controls' card method")
This allowed the machine driver to take actions after all the
card controls have been added. However, there are times when a
codec driver would also want to do things like obtain references
to controls for later use, which require all the controls to be
present. Add a component level fixup_controls callback, echoing
the card level option.
Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
---
No changes since v3.
include/sound/soc-component.h | 2 ++
sound/soc/soc-component.c | 10 ++++++++++
sound/soc/soc-core.c | 5 +++++
3 files changed, 17 insertions(+)
diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h
index aa423865dbe7c..4b7d7954953db 100644
--- a/include/sound/soc-component.h
+++ b/include/sound/soc-component.h
@@ -78,6 +78,7 @@ struct snd_soc_component_driver {
unsigned int num_dapm_routes;
int (*probe)(struct snd_soc_component *component);
+ int (*fixup_controls)(struct snd_soc_component *component);
void (*remove)(struct snd_soc_component *component);
int (*suspend)(struct snd_soc_component *component);
int (*resume)(struct snd_soc_component *component);
@@ -380,6 +381,7 @@ void snd_soc_component_suspend(struct snd_soc_component *component);
void snd_soc_component_resume(struct snd_soc_component *component);
int snd_soc_component_is_suspended(struct snd_soc_component *component);
int snd_soc_component_probe(struct snd_soc_component *component);
+int snd_soc_component_fixup_controls(struct snd_soc_component *component);
void snd_soc_component_remove(struct snd_soc_component *component);
int snd_soc_component_of_xlate_dai_id(struct snd_soc_component *component,
struct device_node *ep);
diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c
index 21492d15833f7..2ce24513fac5d 100644
--- a/sound/soc/soc-component.c
+++ b/sound/soc/soc-component.c
@@ -310,6 +310,16 @@ int snd_soc_component_probe(struct snd_soc_component *component)
return soc_component_ret(component, ret);
}
+int snd_soc_component_fixup_controls(struct snd_soc_component *component)
+{
+ int ret = 0;
+
+ if (component->driver->fixup_controls)
+ ret = component->driver->fixup_controls(component);
+
+ return soc_component_ret(component, ret);
+}
+
void snd_soc_component_remove(struct snd_soc_component *component)
{
if (component->driver->remove)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 7817beea5b3bc..44f9bb4473f55 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -2162,6 +2162,11 @@ static int snd_soc_bind_card(struct snd_soc_card *card)
goto probe_end;
snd_soc_dapm_new_widgets(card);
+ for_each_card_components(card, component) {
+ ret = snd_soc_component_fixup_controls(component);
+ if (ret < 0)
+ goto probe_end;
+ }
snd_soc_card_fixup_controls(card);
ret = snd_card_register(card->snd_card);
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v4 6/7] ASoC: SDCA: Switch to fixup_controls callback for IRQ registration
2026-07-21 14:36 [PATCH v4 0/7] Fix races on creation of SDCA jack detection Charles Keepax
` (4 preceding siblings ...)
2026-07-21 14:36 ` [PATCH v4 5/7] ASoC: Add a component fixup_controls callback Charles Keepax
@ 2026-07-21 14:36 ` Charles Keepax
2026-07-21 14:36 ` [PATCH v4 7/7] ASoC: SDCA: Move kcontrol search out of IRQ Charles Keepax
2026-07-27 17:47 ` [PATCH v4 0/7] Fix races on creation of SDCA jack detection Mark Brown
7 siblings, 0 replies; 9+ messages in thread
From: Charles Keepax @ 2026-07-21 14:36 UTC (permalink / raw)
To: broonie
Cc: lgirdwood, yung-chuan.liao, pierre-louis.bossart, shumingf,
linux-sound, linux-kernel, patches
Currently there are some race conditions around the boot of SDCA
jack detection. The core creates DAPM widgets/routes quite a
long time before it creates the associated ALSA control, and
the jack detection IRQ is currently registered in component
probe. At the time of component probe, the DAPM widgets exist,
shortly after this the DAPM routes are added. At the time the DAPM
routes are added the register value for the control is checked
and the appropriate path is connected. The existing handling
in the SDCA jack IRQ handles the case the control doesn't exist
and updates the registers directly, which works until the DAPM
routes are added. After the routes are added the DAPM graph has
already set connected on a particular DAPM path, which will not
be updated until an IRQ is received when the control is present.
Thus those updates are usually not reflected in the resulting
DAPM graph which can lead to the audio path being erroneously
powered on/off.
Switch to the new fixup_controls callback to register the
IRQs, this is guaranteed to run after all the controls have
been created. Which means we can avoid the aforementioned race
condition and as a bonus no longer need to concern ourselves
with a case where the IRQ handler runs and the ALSA control
is unavailable.
Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
---
Changes since v3:
- Switch to sdca_irq_cleanup_late was done in the patch that
added it.
sound/soc/sdca/sdca_class_function.c | 4 +--
sound/soc/sdca/sdca_jack.c | 44 ++++++++++++----------------
2 files changed, 21 insertions(+), 27 deletions(-)
diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c
index 5ae6c727c796d..2fb2b043c979c 100644
--- a/sound/soc/sdca/sdca_class_function.c
+++ b/sound/soc/sdca/sdca_class_function.c
@@ -191,7 +191,7 @@ static const struct snd_soc_dai_ops class_function_sdw_ops = {
.hw_free = class_function_sdw_remove_peripheral,
};
-static int class_function_component_probe(struct snd_soc_component *component)
+static int class_function_component_fixup_controls(struct snd_soc_component *component)
{
struct class_function_drv *drv = snd_soc_component_get_drvdata(component);
struct sdca_class_drv *core = drv->core;
@@ -217,7 +217,7 @@ static int class_function_set_jack(struct snd_soc_component *component,
}
static const struct snd_soc_component_driver class_function_component_drv = {
- .probe = class_function_component_probe,
+ .fixup_controls = class_function_component_fixup_controls,
.remove = class_function_component_remove,
.endianness = 1,
};
diff --git a/sound/soc/sdca/sdca_jack.c b/sound/soc/sdca/sdca_jack.c
index ffa8709649248..3c84d17244a28 100644
--- a/sound/soc/sdca/sdca_jack.c
+++ b/sound/soc/sdca/sdca_jack.c
@@ -41,6 +41,7 @@ int sdca_jack_process(struct sdca_interrupt *interrupt)
struct jack_state *state = interrupt->priv;
struct snd_kcontrol *kctl = state->kctl;
struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL;
+ struct soc_enum *soc_enum;
unsigned int reg, val;
int ret;
@@ -55,10 +56,12 @@ int sdca_jack_process(struct sdca_interrupt *interrupt)
return -ENOMEM;
kctl = snd_soc_component_get_kcontrol(component, name);
- if (!kctl)
- dev_dbg(dev, "control not found: %s\n", name);
- else
- state->kctl = kctl;
+ if (!kctl) {
+ dev_err(dev, "control not found: %s\n", name);
+ return -ENODEV;
+ }
+
+ state->kctl = kctl;
}
reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
@@ -96,30 +99,21 @@ int sdca_jack_process(struct sdca_interrupt *interrupt)
dev_dbg(dev, "%s: %#x\n", interrupt->name, val);
- if (kctl) {
- struct soc_enum *soc_enum = (struct soc_enum *)kctl->private_value;
-
- ucontrol = kzalloc_obj(*ucontrol);
- if (!ucontrol)
- return -ENOMEM;
-
- ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val);
+ ucontrol = kzalloc_obj(*ucontrol);
+ if (!ucontrol)
+ return -ENOMEM;
- ret = snd_soc_dapm_put_enum_double(kctl, ucontrol);
- if (ret < 0) {
- dev_err(dev, "failed to update selected mode: %d\n", ret);
- return ret;
- }
+ soc_enum = (struct soc_enum *)kctl->private_value;
+ ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val);
- snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
- } else {
- ret = regmap_write(interrupt->function_regmap, reg, val);
- if (ret) {
- dev_err(dev, "failed to write selected mode: %d\n", ret);
- return ret;
- }
+ ret = snd_soc_dapm_put_enum_double(kctl, ucontrol);
+ if (ret < 0) {
+ dev_err(dev, "failed to update selected mode: %d\n", ret);
+ return ret;
}
+ snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+
return sdca_jack_report(interrupt);
}
EXPORT_SYMBOL_NS_GPL(sdca_jack_process, "SND_SOC_SDCA");
@@ -201,7 +195,7 @@ int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *ja
struct sdca_control_range *range;
struct jack_state *jack_state;
- if (!interrupt->irq)
+ if (!interrupt->dev)
continue;
switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v4 7/7] ASoC: SDCA: Move kcontrol search out of IRQ
2026-07-21 14:36 [PATCH v4 0/7] Fix races on creation of SDCA jack detection Charles Keepax
` (5 preceding siblings ...)
2026-07-21 14:36 ` [PATCH v4 6/7] ASoC: SDCA: Switch to fixup_controls callback for IRQ registration Charles Keepax
@ 2026-07-21 14:36 ` Charles Keepax
2026-07-27 17:47 ` [PATCH v4 0/7] Fix races on creation of SDCA jack detection Mark Brown
7 siblings, 0 replies; 9+ messages in thread
From: Charles Keepax @ 2026-07-21 14:36 UTC (permalink / raw)
To: broonie
Cc: lgirdwood, yung-chuan.liao, pierre-louis.bossart, shumingf,
linux-sound, linux-kernel, patches
Now that the IRQs are always registered after all the ALSA
controls are created it is possible to search for the control
at the point the IRQ is requested. Move the control search out
of the IRQ handler and do it at IRQ request time.
This also fixes a potential issue when the card was torn down
and reprobed without destroying the codec device, the kctl
pointer stored by the IRQ handler would not be updated to the
new control on the second probe.
Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
---
New since v3.
include/sound/sdca_jack.h | 1 +
sound/soc/sdca/sdca_interrupts.c | 12 +++++++++
sound/soc/sdca/sdca_jack.c | 43 +++++++++++++++++++-------------
3 files changed, 39 insertions(+), 17 deletions(-)
diff --git a/include/sound/sdca_jack.h b/include/sound/sdca_jack.h
index 59de40b7d7d01..871ba2d8146a3 100644
--- a/include/sound/sdca_jack.h
+++ b/include/sound/sdca_jack.h
@@ -28,6 +28,7 @@ struct jack_state {
};
int sdca_jack_alloc_state(struct sdca_interrupt *interrupt);
+int sdca_jack_init_state(struct sdca_interrupt *interrupt);
void sdca_jack_free_state(struct sdca_interrupt *interrupt);
int sdca_jack_process(struct sdca_interrupt *interrupt);
diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c
index 6f0d8c0fe622d..42fbd3af8a754 100644
--- a/sound/soc/sdca/sdca_interrupts.c
+++ b/sound/soc/sdca/sdca_interrupts.c
@@ -521,6 +521,8 @@ int sdca_irq_populate(struct sdca_function_data *function,
for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) {
struct sdca_interrupt *interrupt = &info->irqs[i];
+ struct sdca_control *control = interrupt->control;
+ struct sdca_entity *entity = interrupt->entity;
int irq;
if (interrupt->function != function || interrupt->irq)
@@ -528,6 +530,16 @@ int sdca_irq_populate(struct sdca_function_data *function,
interrupt->component = component;
+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+ case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
+ ret = sdca_jack_init_state(interrupt);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
irq = interrupt->control->interrupt_position;
ret = sdca_irq_request_locked(dev, info, irq, interrupt->name,
interrupt->handler, interrupt);
diff --git a/sound/soc/sdca/sdca_jack.c b/sound/soc/sdca/sdca_jack.c
index 3c84d17244a28..73f8067906a90 100644
--- a/sound/soc/sdca/sdca_jack.c
+++ b/sound/soc/sdca/sdca_jack.c
@@ -47,23 +47,6 @@ int sdca_jack_process(struct sdca_interrupt *interrupt)
guard(rwsem_write)(rwsem);
- if (!kctl) {
- const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s",
- interrupt->entity->label,
- SDCA_CTL_SELECTED_MODE_NAME);
-
- if (!name)
- return -ENOMEM;
-
- kctl = snd_soc_component_get_kcontrol(component, name);
- if (!kctl) {
- dev_err(dev, "control not found: %s\n", name);
- return -ENODEV;
- }
-
- state->kctl = kctl;
- }
-
reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
interrupt->control->sel, 0);
@@ -148,6 +131,32 @@ void sdca_jack_free_state(struct sdca_interrupt *interrupt)
}
EXPORT_SYMBOL_NS_GPL(sdca_jack_free_state, "SND_SOC_SDCA");
+/**
+ * sdca_jack_init_state - Initialise transient state for a jack interrupt
+ * @interrupt: SDCA interrupt structure.
+ *
+ * Return: Zero on success or a negative error code.
+ */
+int sdca_jack_init_state(struct sdca_interrupt *interrupt)
+{
+ struct jack_state *jack_state = interrupt->priv;
+ const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s",
+ interrupt->entity->label,
+ SDCA_CTL_SELECTED_MODE_NAME);
+
+ if (!name)
+ return -ENOMEM;
+
+ jack_state->kctl = snd_soc_component_get_kcontrol(interrupt->component, name);
+ if (!jack_state->kctl) {
+ dev_err(interrupt->dev, "control not found: %s\n", name);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_jack_init_state, "SND_SOC_SDCA");
+
static int type_get_mask(enum sdca_terminal_type type)
{
switch (type) {
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v4 0/7] Fix races on creation of SDCA jack detection
2026-07-21 14:36 [PATCH v4 0/7] Fix races on creation of SDCA jack detection Charles Keepax
` (6 preceding siblings ...)
2026-07-21 14:36 ` [PATCH v4 7/7] ASoC: SDCA: Move kcontrol search out of IRQ Charles Keepax
@ 2026-07-27 17:47 ` Mark Brown
7 siblings, 0 replies; 9+ messages in thread
From: Mark Brown @ 2026-07-27 17:47 UTC (permalink / raw)
To: Charles Keepax
Cc: lgirdwood, yung-chuan.liao, pierre-louis.bossart, shumingf,
linux-sound, linux-kernel, patches
On Tue, 21 Jul 2026 15:36:29 +0100, Charles Keepax wrote:
> Fix races on creation of SDCA jack detection
>
> Currently there exists a couple races that can result in the DAPM graph
> coming up in a state that doesn't match the hardware with respect to
> SDCA jack detection. This series fixes these up by adding a component
> level fixup_controls helper into the asoc core and shuffling around the
> IRQ requests from the SDCA side.
>
> [...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-7.2
Thanks!
[1/7] ASoC: SDCA: Rename sdca_irq_allocate() to include devm
https://git.kernel.org/broonie/sound/c/4ecef577d1ce
[2/7] ASoC: SDCA: Add sdca_irq_cleanup_late()
https://git.kernel.org/broonie/sound/c/f18e97fa7f12
[3/7] ASoC: SDCA: Remove devm from primary IRQ cleanup
https://git.kernel.org/broonie/sound/c/0880082c27b6
[4/7] ASoC: SDCA: Populate IRQ data earlier
https://git.kernel.org/broonie/sound/c/050406cbd676
[5/7] ASoC: Add a component fixup_controls callback
https://git.kernel.org/broonie/sound/c/3e81e2fb2163
[6/7] ASoC: SDCA: Switch to fixup_controls callback for IRQ registration
https://git.kernel.org/broonie/sound/c/b8f71f16134f
[7/7] ASoC: SDCA: Move kcontrol search out of IRQ
https://git.kernel.org/broonie/sound/c/bf1b7821f853
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
^ permalink raw reply [flat|nested] 9+ messages in thread