diff options
author | Takashi Iwai <tiwai@suse.de> | 2014-01-20 16:51:16 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2014-02-03 09:51:34 +0100 |
commit | 400362f1d8dcfda3562e80e88cfc2a92cffaf9bf (patch) | |
tree | 05328d792a4a0e7a928da1b3c04f6ba4b2123658 | |
parent | 63f4b3a475e84c5aa77f720f0f66989c686a14fb (diff) |
ALSA: usb-audio: Resume mixer values properly
Implement reset_resume callback so that the mixer values are properly
restored. Still no boot quirks are called, so it might not work well
on some devices.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r-- | sound/usb/card.c | 18 | ||||
-rw-r--r-- | sound/usb/mixer.c | 99 | ||||
-rw-r--r-- | sound/usb/mixer.h | 7 |
3 files changed, 99 insertions, 25 deletions
diff --git a/sound/usb/card.c b/sound/usb/card.c index d979050e6a6..02522413612 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -691,12 +691,12 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) } list_for_each_entry(mixer, &chip->mixer_list, list) - snd_usb_mixer_inactivate(mixer); + snd_usb_mixer_suspend(mixer); return 0; } -static int usb_audio_resume(struct usb_interface *intf) +static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume) { struct snd_usb_audio *chip = usb_get_intfdata(intf); struct usb_mixer_interface *mixer; @@ -711,7 +711,7 @@ static int usb_audio_resume(struct usb_interface *intf) * we just notify and restart the mixers */ list_for_each_entry(mixer, &chip->mixer_list, list) { - err = snd_usb_mixer_activate(mixer); + err = snd_usb_mixer_resume(mixer, reset_resume); if (err < 0) goto err_out; } @@ -723,9 +723,20 @@ static int usb_audio_resume(struct usb_interface *intf) err_out: 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 #define usb_audio_suspend NULL #define usb_audio_resume NULL +#define usb_audio_reset_resume NULL #endif /* CONFIG_PM */ static struct usb_device_id usb_audio_ids [] = { @@ -747,6 +758,7 @@ static struct usb_driver usb_audio_driver = { .disconnect = usb_audio_disconnect, .suspend = usb_audio_suspend, .resume = usb_audio_resume, + .reset_resume = usb_audio_reset_resume, .id_table = usb_audio_ids, .supports_autosuspend = 1, }; diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 44b0ba4feab..aa9bc19aae6 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -2299,26 +2299,6 @@ requeue: } } -/* stop any bus activity of a mixer */ -void snd_usb_mixer_inactivate(struct usb_mixer_interface *mixer) -{ - usb_kill_urb(mixer->urb); - usb_kill_urb(mixer->rc_urb); -} - -int snd_usb_mixer_activate(struct usb_mixer_interface *mixer) -{ - int err; - - if (mixer->urb) { - err = usb_submit_urb(mixer->urb, GFP_NOIO); - if (err < 0) - return err; - } - - return 0; -} - /* create the handler for the optional status interrupt endpoint */ static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer) { @@ -2417,3 +2397,82 @@ void snd_usb_mixer_disconnect(struct list_head *p) usb_kill_urb(mixer->urb); usb_kill_urb(mixer->rc_urb); } + +#ifdef CONFIG_PM +/* stop any bus activity of a mixer */ +static void snd_usb_mixer_inactivate(struct usb_mixer_interface *mixer) +{ + usb_kill_urb(mixer->urb); + usb_kill_urb(mixer->rc_urb); +} + +static int snd_usb_mixer_activate(struct usb_mixer_interface *mixer) +{ + int err; + + if (mixer->urb) { + err = usb_submit_urb(mixer->urb, GFP_NOIO); + if (err < 0) + return err; + } + + return 0; +} + +int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer) +{ + snd_usb_mixer_inactivate(mixer); + return 0; +} + +static int restore_mixer_value(struct usb_mixer_elem_info *cval) +{ + int c, err, idx; + + if (cval->cmask) { + idx = 0; + for (c = 0; c < MAX_CHANNELS; c++) { + if (!(cval->cmask & (1 << c))) + continue; + if (cval->cached & (1 << c)) { + err = set_cur_mix_value(cval, c + 1, idx, + cval->cache_val[idx]); + if (err < 0) + return err; + } + idx++; + } + } else { + /* master */ + if (cval->cached) { + err = set_cur_mix_value(cval, 0, 0, *cval->cache_val); + if (err < 0) + return err; + } + } + + return 0; +} + +int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume) +{ + struct usb_mixer_elem_info *cval; + int id, err; + + /* FIXME: any mixer quirks? */ + + if (reset_resume) { + /* restore cached mixer values */ + for (id = 0; id < MAX_ID_ELEMS; id++) { + for (cval = mixer->id_elems[id]; cval; + cval = cval->next_id_elem) { + err = restore_mixer_value(cval); + if (err < 0) + return err; + } + } + } + + return snd_usb_mixer_activate(mixer); +} +#endif diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index aab80df201b..73b1f649447 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -63,8 +63,6 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid); int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int value_set); -void snd_usb_mixer_inactivate(struct usb_mixer_interface *mixer); -int snd_usb_mixer_activate(struct usb_mixer_interface *mixer); int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer, struct snd_kcontrol *kctl); @@ -72,4 +70,9 @@ int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer, int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *_tlv); +#ifdef CONFIG_PM +int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer); +int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume); +#endif + #endif /* __USBMIXER_H */ |