diff options
author | Takashi Iwai <tiwai@suse.de> | 2022-09-01 14:41:36 +0200 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2022-09-01 15:06:56 +0200 |
commit | ff878b408a03bef5d610b7e2302702e16a53636e (patch) | |
tree | a4f4fb8233b11435af3dac36232d9dd0c158b4d2 /sound/usb/pcm.c | |
parent | ALSA: usb-audio: Register card again for iface over delayed_register option (diff) | |
download | linux-ff878b408a03bef5d610b7e2302702e16a53636e.tar.xz linux-ff878b408a03bef5d610b7e2302702e16a53636e.zip |
ALSA: usb-audio: Split endpoint setups for hw_params and prepare
One of the former changes for the endpoint management was the more
consistent setup of endpoints at hw_params.
snd_usb_endpoint_configure() is a single function that does the full
setup, and it's called from both PCM hw_params and prepare callbacks.
Although the EP setup at the prepare phase is usually skipped (by
checking need_setup flag), it may be still effective in some cases
like suspend/resume that requires the interface setup again.
As it's a full and single setup, the invocation of
snd_usb_endpoint_configure() includes not only the USB interface setup
but also the buffer release and allocation. OTOH, doing the buffer
release and re-allocation at PCM prepare phase is rather superfluous,
and better to be done only in the hw_params phase.
For those optimizations, this patch splits the endpoint setup to two
phases: snd_usb_endpoint_set_params() and snd_usb_endpoint_prepare(),
to be called from hw_params and from prepare, respectively.
Note that this patch changes the driver operation slightly,
effectively moving the USB interface setup again to PCM prepare stage
instead of hw_params stage, while the buffer allocation and such
initializations are still done at hw_params stage.
And, the change of the USB interface setup timing (moving to prepare)
gave an interesting "fix", too: it was reported that the recent
kernels caused silent output at the beginning on playbacks on some
devices on Android, and this change casually fixed the regression.
It seems that those devices are picky about the sample rate change (or
the interface change?), and don't follow the too immediate rate
changes.
Meanwhile, Android operates the PCM in the following order:
- open, then hw_params with the possibly highest sample rate
- close without prepare
- re-open, hw_params with the normal sample rate
- prepare, and start streaming
This procedure ended up the hw_params twice with different rates, and
because the recent kernel did set up the sample rate twice one and
after, it screwed up the device. OTOH, the earlier kernels didn't set
up the USB interface at hw_params, hence this problem didn't appear.
Now, with this patch, the USB interface setup is again back to the
prepare phase, and it works around the problem automagically.
Although we should address the sample rate problem in a more solid
way in future, let's keep things working as before for now.
Fixes: bf6313a0ff76 ("ALSA: usb-audio: Refactor endpoint management")
Cc: <stable@vger.kernel.org>
Reported-by: chihhao chen <chihhao.chen@mediatek.com>
Link: https://lore.kernel.org/r/87e6d6ae69d68dc588ac9acc8c0f24d6188375c3.camel@mediatek.com
Link: https://lore.kernel.org/r/20220901124136.4984-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/pcm.c')
-rw-r--r-- | sound/usb/pcm.c | 14 |
1 files changed, 10 insertions, 4 deletions
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index d45d1d7e6664..b604f7e95e82 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -443,17 +443,17 @@ static int configure_endpoints(struct snd_usb_audio *chip, if (stop_endpoints(subs, false)) sync_pending_stops(subs); if (subs->sync_endpoint) { - err = snd_usb_endpoint_configure(chip, subs->sync_endpoint); + err = snd_usb_endpoint_prepare(chip, subs->sync_endpoint); if (err < 0) return err; } - err = snd_usb_endpoint_configure(chip, subs->data_endpoint); + err = snd_usb_endpoint_prepare(chip, subs->data_endpoint); if (err < 0) return err; snd_usb_set_format_quirk(subs, subs->cur_audiofmt); } else { if (subs->sync_endpoint) { - err = snd_usb_endpoint_configure(chip, subs->sync_endpoint); + err = snd_usb_endpoint_prepare(chip, subs->sync_endpoint); if (err < 0) return err; } @@ -551,7 +551,13 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, subs->cur_audiofmt = fmt; mutex_unlock(&chip->mutex); - ret = configure_endpoints(chip, subs); + if (subs->sync_endpoint) { + ret = snd_usb_endpoint_set_params(chip, subs->sync_endpoint); + if (ret < 0) + goto unlock; + } + + ret = snd_usb_endpoint_set_params(chip, subs->data_endpoint); unlock: if (ret < 0) |