ALSA: usb-audio: Unify mixer resume and reset_resume procedure

USB-audio driver assumes that the normal resume would preserve the
device configuration while reset_resume wouldn't, and tries to restore
the mixer elements only at reset_resume callback.  However, this seems
too naive, and some devices do behave differently, resetting the
volume at the normal resume; this resulted in the inconsistent volume
that surprised users.

This patch changes the mixer resume code to handle both the normal and
reset resume in the same way, always restoring the original mixer
element values.  This allows us to unify the both callbacks as well as
dropping the no longer used reset_resume field, which ends up with a
good code reduction.

A slight behavior change by this patch is that now we assign
restore_mixer_value() as the default resume callback, and the function
is no longer called at reset-resume when the resume callback is
overridden by the quirk function.  That is, if needed, the quirk
resume function would have to handle similarly as
restore_mixer_value() by itself.

Reported-by: En-Shuo Hsu <enshuo@chromium.org>
Cc: Yu-Hsuan Hsu <yuhsuan@chromium.org>
Link: https://lore.kernel.org/r/CADDZ45UPsbpAAqP6=ZkTT8BE-yLii4Y7xSDnjK550G2DhQsMew@mail.gmail.com
Link: https://lore.kernel.org/r/20210910105155.12862-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2021-09-10 12:51:55 +02:00
parent 6f44578430
commit 7b9cf90366
4 changed files with 10 additions and 39 deletions

View File

@ -1054,7 +1054,7 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
return 0; return 0;
} }
static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume) static int usb_audio_resume(struct usb_interface *intf)
{ {
struct snd_usb_audio *chip = usb_get_intfdata(intf); struct snd_usb_audio *chip = usb_get_intfdata(intf);
struct snd_usb_stream *as; struct snd_usb_stream *as;
@ -1080,7 +1080,7 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume)
* we just notify and restart the mixers * we just notify and restart the mixers
*/ */
list_for_each_entry(mixer, &chip->mixer_list, list) { list_for_each_entry(mixer, &chip->mixer_list, list) {
err = snd_usb_mixer_resume(mixer, reset_resume); err = snd_usb_mixer_resume(mixer);
if (err < 0) if (err < 0)
goto err_out; goto err_out;
} }
@ -1100,20 +1100,10 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume)
atomic_dec(&chip->active); /* allow autopm after this point */ atomic_dec(&chip->active); /* allow autopm after this point */
return err; return err;
} }
static int usb_audio_resume(struct usb_interface *intf)
{
return __usb_audio_resume(intf, false);
}
static int usb_audio_reset_resume(struct usb_interface *intf)
{
return __usb_audio_resume(intf, true);
}
#else #else
#define usb_audio_suspend NULL #define usb_audio_suspend NULL
#define usb_audio_resume NULL #define usb_audio_resume NULL
#define usb_audio_reset_resume NULL #define usb_audio_resume NULL
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
static const struct usb_device_id usb_audio_ids [] = { static const struct usb_device_id usb_audio_ids [] = {
@ -1135,7 +1125,7 @@ static struct usb_driver usb_audio_driver = {
.disconnect = usb_audio_disconnect, .disconnect = usb_audio_disconnect,
.suspend = usb_audio_suspend, .suspend = usb_audio_suspend,
.resume = usb_audio_resume, .resume = usb_audio_resume,
.reset_resume = usb_audio_reset_resume, .reset_resume = usb_audio_resume,
.id_table = usb_audio_ids, .id_table = usb_audio_ids,
.supports_autosuspend = 1, .supports_autosuspend = 1,
}; };

View File

@ -3653,33 +3653,16 @@ static int restore_mixer_value(struct usb_mixer_elem_list *list)
return 0; return 0;
} }
static int default_mixer_reset_resume(struct usb_mixer_elem_list *list) int snd_usb_mixer_resume(struct usb_mixer_interface *mixer)
{
int err;
if (list->resume) {
err = list->resume(list);
if (err < 0)
return err;
}
return restore_mixer_value(list);
}
int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume)
{ {
struct usb_mixer_elem_list *list; struct usb_mixer_elem_list *list;
usb_mixer_elem_resume_func_t f;
int id, err; int id, err;
/* restore cached mixer values */ /* restore cached mixer values */
for (id = 0; id < MAX_ID_ELEMS; id++) { for (id = 0; id < MAX_ID_ELEMS; id++) {
for_each_mixer_elem(list, mixer, id) { for_each_mixer_elem(list, mixer, id) {
if (reset_resume) if (list->resume) {
f = list->reset_resume; err = list->resume(list);
else
f = list->resume;
if (f) {
err = f(list);
if (err < 0) if (err < 0)
return err; return err;
} }
@ -3700,7 +3683,6 @@ void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list,
list->id = unitid; list->id = unitid;
list->dump = snd_usb_mixer_dump_cval; list->dump = snd_usb_mixer_dump_cval;
#ifdef CONFIG_PM #ifdef CONFIG_PM
list->resume = NULL; list->resume = restore_mixer_value;
list->reset_resume = default_mixer_reset_resume;
#endif #endif
} }

View File

@ -70,7 +70,6 @@ struct usb_mixer_elem_list {
bool is_std_info; bool is_std_info;
usb_mixer_elem_dump_func_t dump; usb_mixer_elem_dump_func_t dump;
usb_mixer_elem_resume_func_t resume; usb_mixer_elem_resume_func_t resume;
usb_mixer_elem_resume_func_t reset_resume;
}; };
/* iterate over mixer element list of the given unit id */ /* iterate over mixer element list of the given unit id */
@ -121,7 +120,7 @@ int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
#ifdef CONFIG_PM #ifdef CONFIG_PM
int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer); int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer);
int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume); int snd_usb_mixer_resume(struct usb_mixer_interface *mixer);
#endif #endif
int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,

View File

@ -151,7 +151,7 @@ static int add_single_ctl_with_resume(struct usb_mixer_interface *mixer,
*listp = list; *listp = list;
list->mixer = mixer; list->mixer = mixer;
list->id = id; list->id = id;
list->reset_resume = resume; list->resume = resume;
kctl = snd_ctl_new1(knew, list); kctl = snd_ctl_new1(knew, list);
if (!kctl) { if (!kctl) {
kfree(list); kfree(list);