[RFC] ASoC: clarify deferred component resume error handling
From: Pengpeng Hou
Date: Tue Jul 21 2026 - 01:58:35 EST
Hi,
While auditing codec register-cache restore paths, I found a mismatch
between the component resume callback API and its first consumer.
struct snd_soc_component_driver::resume returns int, and many callbacks
perform fallible work such as regcache_sync(). However, the first caller
discards that return value and always clears the suspended flag:
void snd_soc_component_resume(struct snd_soc_component *component)
{
if (component->driver->resume)
component->driver->resume(component);
component->suspended = 0;
}
The caller is soc_resume_deferred(), which then continues with DAPM
stream resume, digital unmute, the card resume-post callback and the
transition to SNDRV_CTL_POWER_D0. The work runs after snd_soc_resume()
has scheduled it and returned 0 to the PM core.
This is not a recent helper regression. Commit 6ed2597883b1 ("ALSA:
ASoC: Don't block system resume") deliberately moved slow codec resume
I/O to a workqueue. Commit 9a840cbac77a ("ASoC: soc-component: add
snd_soc_component_resume()") preserved the ignored-return behavior when
the helper was introduced. snd_soc_component_suspend() has the same
return-discarding shape, but the concrete sites below are resume paths.
This makes several intuitive per-codec changes incomplete:
- returning a regcache_sync() error does not change caller-visible state;
- returning early can suppress unsafe driver-local IRQ or jack work, but
the core still publishes the component as resumed;
- powering the codec back down on failure conflicts with the core marking
it resumed, and no retry is arranged;
- checking only regcache_sync() can miss earlier fallible private-register
or model-specific restore operations.
I found 33 current component resume implementations with one of these
source-level patterns. This is a count of statically reviewed call sites,
not a claim of 33 independently reproduced user-visible failures.
Return value has no effective consumer (17):
adau17x1, ak4642, es8316, es8323, nau8540, rt1011, rt1016,
rt1305, rt1308, rt1318, rt5616, rt5659, rt5660, rt5665,
rt5668, rt5670 and tlv320aic23.
An early return would only suppress dependent local work (7):
nau8821, nau8824, nau8825, rt5663, rt5682, rt5682s and wm8978.
Local resource rollback would conflict with resumed core state (3):
nau8822, rt5640 and rt5677.
The local restore transaction also has earlier unchecked operations (6):
es8326, es8375, es8389, rt274, rt286 and rt298.
For example, an early return in nau8821 can avoid enabling its IRQ after
a failed cache replay, but the component is still marked resumed. In
nau8822, disabling supplies after replay failure would leave hardware off
while the core continues resume. The RT274/RT286/RT298 paths also restore
private indexed registers through void helpers before the cache replay,
so checking only the final sync would not cover the complete transaction.
Before preparing per-driver patches, what is the intended contract for
component resume failures?
1. Resume is best effort. Drivers should diagnose and recover locally,
and the callback return type should not imply PM propagation.
2. The core should collect and report component failures while retaining
best-effort resume behavior.
3. A failed component should remain unavailable until recovery, which
would require an explicit retry and card-publication design around the
deferred work.
I am not proposing to make snd_soc_resume() return a component error: it
has already returned before the deferred work runs, and changing that
would undo the historical non-blocking design. I have held the per-codec
changes rather than posting a mechanical series whose returned errors
have no consumer.
If best-effort local handling is intended, I will drop the pure
propagation changes and keep only independently useful state or ordering
fixes. If core reporting or failed-component recovery is preferred, I
can prepare one representative RFC patch before touching the individual
drivers.
Thanks,
Pengpeng