[PATCH 1/2] ASoC: cs35l56: Fix potential probe() deadlock

From: Richard Fitzgerald

Date: Thu Jul 16 2026 - 09:22:12 EST


On I2C/SPI call cs35l56_init() before calling
snd_soc_register_component() to prevent the potential for a deadlock
on init_completion.

For most buses all the hardware would be ready when probe() returns,
but on SoundWire, probe() must return before the SoundWire bus driver
will enumerate the device. All access to the registers must be deferred
until the driver receives an ATTACHED notification. But anything that
could return -EPROBE_DEFER must be called during probe, and that includes
snd_soc_register_component(). Because of that, on SoundWire the ASoC
component can be created before the registers are accssible, so
cs35l56_component_probe() waits for init_completion to signal that the
registers are accessible.

On I2C/SPI this 2-stage startup isn't required so their probe()
functions simply called cs35l56_common_probe() and then cs35l56_init().
The problem with this was that snd_soc_register_component() was still
called early. If this triggered ASoC to create the card, ASoC would call
cs35l56_component_probe() which waits on init_completion - but this would
be running inside the cs35l56 driver probe() so blocking it from reaching
the code that signals init_completion, causing a deadlock.

Fixes: e496112529006 ("ASoC: cs35l56: Add driver for Cirrus Logic CS35L56")
Reported-by: Salman S. Tahir <salman.abusaad@xxxxxxxxx>
Closes: https://lore.kernel.org/linux-sound/95c21574-97d5-4311-9263-9e174d22d22c@xxxxxxxxxxxxxxxxxxxxx/T/#u
Tested-by: Salman S. Tahir <salman.abusaad@xxxxxxxxx>
Signed-off-by: Richard Fitzgerald <rf@xxxxxxxxxxxxxxxxxxxxx>
---
sound/soc/codecs/cs35l56-i2c.c | 4 +---
sound/soc/codecs/cs35l56-spi.c | 4 +---
sound/soc/codecs/cs35l56.c | 15 +++++++++++++++
3 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c
index 0f64ab628b03b..4f6ddf1c5a3f6 100644
--- a/sound/soc/codecs/cs35l56-i2c.c
+++ b/sound/soc/codecs/cs35l56-i2c.c
@@ -55,9 +55,7 @@ static int cs35l56_i2c_probe(struct i2c_client *client)
if (ret != 0)
return ret;

- ret = cs35l56_init(cs35l56);
- if (ret == 0)
- ret = cs35l56_irq_request(&cs35l56->base, client->irq);
+ ret = cs35l56_irq_request(&cs35l56->base, client->irq);
if (ret < 0)
cs35l56_remove(cs35l56);

diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c
index 9bc9b7c98390d..b1eb924a5b6cc 100644
--- a/sound/soc/codecs/cs35l56-spi.c
+++ b/sound/soc/codecs/cs35l56-spi.c
@@ -44,9 +44,7 @@ static int cs35l56_spi_probe(struct spi_device *spi)
if (ret != 0)
return ret;

- ret = cs35l56_init(cs35l56);
- if (ret == 0)
- ret = cs35l56_irq_request(&cs35l56->base, spi->irq);
+ ret = cs35l56_irq_request(&cs35l56->base, spi->irq);
if (ret < 0)
cs35l56_remove(cs35l56);

diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
index f365f76ce56c1..995e39645621e 100644
--- a/sound/soc/codecs/cs35l56.c
+++ b/sound/soc/codecs/cs35l56.c
@@ -2009,6 +2009,16 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
goto err;
}

+ /*
+ * On SoundWire the cs35l56_init() cannot be run until after the
+ * device has been enumerated by the SoundWire core.
+ */
+ if (!cs35l56->sdw_peripheral) {
+ ret = cs35l56_init(cs35l56);
+ if (ret)
+ goto err_remove_wm_adsp;
+ }
+
ret = snd_soc_register_component(cs35l56->base.dev,
&soc_component_dev_cs35l56,
cs35l56_dai, ARRAY_SIZE(cs35l56_dai));
@@ -2023,6 +2033,11 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
wm_adsp2_remove(&cs35l56->dsp);

err:
+ if (pm_runtime_enabled(cs35l56->base.dev)) {
+ pm_runtime_dont_use_autosuspend(cs35l56->base.dev);
+ pm_runtime_disable(cs35l56->base.dev);
+ }
+
gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);

--
2.47.3