mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-26 05:34:13 +08:00
2be79d5864
This is a second attempt to fix the bug appearing on Android with the recent kernel; the first try wasff878b408a
and reverted at commit79764ec772
. The details taken from the v1 patch: 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. *** What's new in the take#2 patch: - The regression caused by the v1 patch (bko#216500) was due to the missing check of need_setup flag at hw_params. Now the check is added, and the snd_usb_endpoint_set_params() call is skipped when the running EP is re-opened. - There was another bug in v1 where the clock reference rate wasn't updated at hw_params phase, which may lead to a lack of the proper hw constraints when an application doesn't issue the prepare but only the hw_params call. This patch fixes it as well by tracking the clock rate change in the prepare callback with a new flag "need_update" for the clock reference object, just like others. - The configure_endpoints() are simplified and folded back into snd_usb_pcm_prepare(). Fixes:bf6313a0ff
("ALSA: usb-audio: Refactor endpoint management") Fixes:ff878b408a
("ALSA: usb-audio: Split endpoint setups for hw_params and prepare") 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 Link: https://bugzilla.kernel.org/show_bug.cgi?id=216500 Link: https://lore.kernel.org/r/20220920181106.4894-1-tiwai@suse.de Signed-off-by: Takashi Iwai <tiwai@suse.de>
58 lines
2.3 KiB
C
58 lines
2.3 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef __USBAUDIO_ENDPOINT_H
|
|
#define __USBAUDIO_ENDPOINT_H
|
|
|
|
#define SND_USB_ENDPOINT_TYPE_DATA 0
|
|
#define SND_USB_ENDPOINT_TYPE_SYNC 1
|
|
|
|
struct snd_usb_endpoint *snd_usb_get_endpoint(struct snd_usb_audio *chip,
|
|
int ep_num);
|
|
|
|
int snd_usb_add_endpoint(struct snd_usb_audio *chip, int ep_num, int type);
|
|
|
|
struct snd_usb_endpoint *
|
|
snd_usb_endpoint_open(struct snd_usb_audio *chip,
|
|
const struct audioformat *fp,
|
|
const struct snd_pcm_hw_params *params,
|
|
bool is_sync_ep);
|
|
void snd_usb_endpoint_close(struct snd_usb_audio *chip,
|
|
struct snd_usb_endpoint *ep);
|
|
int snd_usb_endpoint_set_params(struct snd_usb_audio *chip,
|
|
struct snd_usb_endpoint *ep);
|
|
int snd_usb_endpoint_prepare(struct snd_usb_audio *chip,
|
|
struct snd_usb_endpoint *ep);
|
|
int snd_usb_endpoint_get_clock_rate(struct snd_usb_audio *chip, int clock);
|
|
|
|
bool snd_usb_endpoint_compatible(struct snd_usb_audio *chip,
|
|
struct snd_usb_endpoint *ep,
|
|
const struct audioformat *fp,
|
|
const struct snd_pcm_hw_params *params);
|
|
|
|
void snd_usb_endpoint_set_sync(struct snd_usb_audio *chip,
|
|
struct snd_usb_endpoint *data_ep,
|
|
struct snd_usb_endpoint *sync_ep);
|
|
void snd_usb_endpoint_set_callback(struct snd_usb_endpoint *ep,
|
|
int (*prepare)(struct snd_usb_substream *subs,
|
|
struct urb *urb,
|
|
bool in_stream_lock),
|
|
void (*retire)(struct snd_usb_substream *subs,
|
|
struct urb *urb),
|
|
struct snd_usb_substream *data_subs);
|
|
|
|
int snd_usb_endpoint_start(struct snd_usb_endpoint *ep);
|
|
void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, bool keep_pending);
|
|
void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep);
|
|
void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep);
|
|
int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
|
|
void snd_usb_endpoint_release(struct snd_usb_endpoint *ep);
|
|
void snd_usb_endpoint_free_all(struct snd_usb_audio *chip);
|
|
|
|
int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep);
|
|
int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep,
|
|
struct snd_urb_ctx *ctx, int idx,
|
|
unsigned int avail);
|
|
void snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep,
|
|
bool in_stream_lock);
|
|
|
|
#endif /* __USBAUDIO_ENDPOINT_H */
|