Bug 217539 - snd_usb_audio: Device stuck in D1 state after resume
Summary: snd_usb_audio: Device stuck in D1 state after resume
Status: RESOLVED CODE_FIX
Alias: None
Product: Drivers
Classification: Unclassified
Component: Sound(ALSA) (show other bugs)
Hardware: All Linux
: P3 normal
Assignee: Jaroslav Kysela
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-06-11 09:51 UTC by Tatsuyuki Ishi
Modified: 2023-06-29 07:12 UTC (History)
1 user (show)

See Also:
Kernel Version:
Subsystem:
Regression: No
Bisected commit-id:


Attachments
Hack to confirm that PM is the root cause (871 bytes, patch)
2023-06-11 09:52 UTC, Tatsuyuki Ishi
Details | Diff

Description Tatsuyuki Ishi 2023-06-11 09:51:44 UTC
snd_usb_pcm only restores the DAC to D1 state, not D0 state, after resuming from a suspend.

Userspace sees a state of SND_PCM_STATE_SUSPENDED, which it will try to resume through snd_pcm_prepare (snd_pcm_resume is -ENOSYS). However, snd_pcm_prepare does not trigger the D0 state transition, leading to the audio device not functioning. More detailed symptom at [1].

The DAC is only kicked out of this state after a call to hw_params, which triggers a transition to D0 state.

The attached patch hacks so that the DAC enters D0 state right after resume, which I have tested and fixes the problem.

[1]: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/2707
Comment 1 Tatsuyuki Ishi 2023-06-11 09:52:12 UTC
Created attachment 304395 [details]
Hack to confirm that PM is the root cause
Comment 2 Takashi Iwai 2023-06-12 07:15:44 UTC
So, simply changing the power state at prepare fixes the issue?

--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -650,6 +650,10 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
 		goto unlock;
 	}
 
+	ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D0);
+	if (ret < 0)
+		goto unlock;
+
  again:
 	if (subs->sync_endpoint) {
 		ret = snd_usb_endpoint_prepare(chip, subs->sync_endpoint);


hw_params isn't called in a usual resume path, hence the current code failed.
Comment 3 Tatsuyuki Ishi 2023-06-12 07:22:39 UTC
Looks good to me, will test later.
Comment 4 Tatsuyuki Ishi 2023-06-12 11:11:15 UTC
Patch in comment #2 works fine, thanks. Additionally it seems we can remove this call from hw_params since all the setup URBs are flushed only once prepare is called.

diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index eec5232f9fb2..b7428a23647e 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -520,10 +520,6 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
        if (ret < 0)
                goto stop_pipeline;
 
-       ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D0);
-       if (ret < 0)
-               goto unlock;
-
        if (subs->data_endpoint) {
                if (snd_usb_endpoint_compatible(chip, subs->data_endpoint,
                                                fmt, hw_params))
Comment 5 Takashi Iwai 2023-06-12 12:52:24 UTC
Well, that's another code path, and in theory, application can issue the hw_params step before the prepare step, and that'll hit a similar problem.  So it's still safer to keep the power state change code there, too.

We may optimize the power state change by caching the value and checking it before actually executing the USB command, if it really matters, too.
Comment 6 Tatsuyuki Ishi 2023-06-12 12:59:00 UTC
> Well, that's another code path, and in theory, application can issue the
> hw_params step before the prepare step, and that'll hit a similar problem. 
> So it's still safer to keep the power state change code there, too.

A call to hw_params() put the stream in SND_PCM_STATE_SETUP state, and this needs to always be followed by a prepare() in order to proceed to starting the stream.  Hence, I don't see how we will "hit a similar problem".

> We may optimize the power state change by caching the value and checking it
> before actually executing the USB command, if it really matters, too.

Looking at debug logs it seems it already avoids redundant state changes. So this is more or less a code flow clarity thing.  I don't really mind keeping the code if you want to be absolutely safe, so up to you.
Comment 7 Takashi Iwai 2023-06-12 13:13:03 UTC
(In reply to Tatsuyuki Ishi from comment #6)
> > Well, that's another code path, and in theory, application can issue the
> > hw_params step before the prepare step, and that'll hit a similar problem. 
> > So it's still safer to keep the power state change code there, too.
> 
> A call to hw_params() put the stream in SND_PCM_STATE_SETUP state, and this
> needs to always be followed by a prepare() in order to proceed to starting
> the stream.  Hence, I don't see how we will "hit a similar problem".

In general, hw_params does set up the hardware parameters, and it implies that the power gets turned on at this stage.  Although USB-audio driver tries to avoid touching the hardware parameters at hw_parms call as much as possible, it'd be error-prone to leave in an unknown power state.

So yes, I'd like to be in the safer side and keep the power up explicitly there on purpose.
Comment 8 Takashi Iwai 2023-06-12 13:47:51 UTC
The fix submitted to the upstream:
  https://lore.kernel.org/all/20230612132818.29486-1-tiwai@suse.de/
Comment 9 Tatsuyuki Ishi 2023-06-29 07:12:56 UTC
Fix live with 6.3.9 or 6.4.

Note You need to log in before you can comment on or make changes to this bug.