ALSA: usb-audio: Resume mixer values properly
[firefly-linux-kernel-4.4.55.git] / sound / usb / mixer.c
index 44b0ba4feab3bd1b43100feb457e82e290ea26dc..aa9bc19aae688a8f2ef4480da4d142f16addc4df 100644 (file)
@@ -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