Hi,

this patch implements autosuspend for USB audio devices.

Signed-off-by: Oliver Neukum <[EMAIL PROTECTED]>

It's against Linus's git tree plus
git-pull git://git.kernel.org/pub/scm/linux/kernel/git/perex/alsa.git mm
as this contains suspend/resume support for USB audio. Please test.

        Regards
                Oliver

----

commit 6ee21841b993d915dccba2a9c5e91e9fd2940743
Author: Oliver Neukum <[EMAIL PROTECTED]>
Date:   Fri Feb 1 12:25:26 2008 +0100

    autosuspend for USB audio

diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index 8fa9356..cdd9c36 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -1879,6 +1879,10 @@ static int setup_hw_info(struct snd_pcm_runtime 
*runtime, struct snd_usb_substre
                }
        }
 
+       err = usb_autopm_get_interface(subs->stream->chip->pm_intf);
+       if (err < 0)
+               return err;
+
        /* set the period time minimum 1ms */
        /* FIXME: high-speed mode allows 125us minimum period, but many parts
         * in the current code assume the 1ms period.
@@ -1894,23 +1898,27 @@ static int setup_hw_info(struct snd_pcm_runtime 
*runtime, struct snd_usb_substre
                                               SNDRV_PCM_HW_PARAM_FORMAT,
                                               SNDRV_PCM_HW_PARAM_CHANNELS,
                                               -1)) < 0)
-                       return err;
+                       goto rep_err;
                if ((err = snd_pcm_hw_rule_add(runtime, 0, 
SNDRV_PCM_HW_PARAM_CHANNELS,
                                               hw_rule_channels, subs,
                                               SNDRV_PCM_HW_PARAM_FORMAT,
                                               SNDRV_PCM_HW_PARAM_RATE,
                                               -1)) < 0)
-                       return err;
+                       goto rep_err;
                if ((err = snd_pcm_hw_rule_add(runtime, 0, 
SNDRV_PCM_HW_PARAM_FORMAT,
                                               hw_rule_format, subs,
                                               SNDRV_PCM_HW_PARAM_RATE,
                                               SNDRV_PCM_HW_PARAM_CHANNELS,
                                               -1)) < 0)
-                       return err;
+                       goto rep_err;
                if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0)
-                       return err;
+                       goto rep_err;
        }
        return 0;
+
+rep_err:
+       usb_autopm_put_interface(subs->stream->chip->pm_intf);
+       return err;
 }
 
 static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
@@ -1937,6 +1945,7 @@ static int snd_usb_pcm_close(struct snd_pcm_substream 
*substream, int direction)
                subs->interface = -1;
        }
        subs->pcm_substream = NULL;
+       usb_autopm_put_interface(subs->stream->chip->pm_intf);
        return 0;
 }
 
@@ -2102,6 +2111,7 @@ static struct usb_driver usb_audio_driver = {
        .suspend =      usb_audio_suspend,
        .resume =       usb_audio_resume,
        .id_table =     usb_audio_ids,
+       .supports_autosuspend = 1,
 };
 
 
@@ -3562,6 +3572,7 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
                                        goto __error;
                                }
                                snd_card_set_dev(chip->card, &intf->dev);
+                               chip->pm_intf = intf;
                                break;
                        }
                if (! chip) {
@@ -3667,26 +3678,37 @@ static void usb_audio_disconnect(struct usb_interface 
*intf)
 static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
 {
        struct snd_usb_audio *chip = dev_get_drvdata(&intf->dev);
+       struct usb_device *udev = interface_to_usbdev(intf);
        struct list_head *p;
        struct snd_usb_stream *as;
 
        if (chip == (void *)-1L)
                return 0;
 
-       snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
-       if (!chip->num_suspended_intf++) {
-               list_for_each(p, &chip->pcm_list) {
-                       as = list_entry(p, struct snd_usb_stream, list);
-                       snd_pcm_suspend_all(as->pcm);
+       if (!udev->auto_pm) {
+               snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
+               if (!chip->num_suspended_intf++) {
+                       list_for_each(p, &chip->pcm_list) {
+                               as = list_entry(p, struct snd_usb_stream, list);
+                               snd_pcm_suspend_all(as->pcm);
+                       }
                }
+       } else {
+               /*
+                * otherwise we keep the rest of the system in the dark
+                * to keep this transparent
+                */
+               chip->num_suspended_intf++;
        }
 
+
        return 0;
 }
 
 static int usb_audio_resume(struct usb_interface *intf)
 {
        struct snd_usb_audio *chip = dev_get_drvdata(&intf->dev);
+       struct usb_device *udev = interface_to_usbdev(intf);
 
        if (chip == (void *)-1L)
                return 0;
@@ -3694,10 +3716,12 @@ static int usb_audio_resume(struct usb_interface *intf)
                return 0;
        /*
         * ALSA leaves material resumption to user space
-        * we just notify
+        * we just notify - if the whole system resumes
+        * if we autoresume we keep quiet to keep this
+        * transition transparent
         */
-
-       snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
+       if (!udev->auto_pm)
+               snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
 
        return 0;
 }
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 7cf18c3..fbd6bdf 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -123,6 +123,7 @@ struct snd_usb_audio {
        int index;
        struct usb_device *dev;
        struct snd_card *card;
+       struct usb_interface *pm_intf;
        u32 usb_id;
        int shutdown;
        int num_interfaces;
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c
index 89c63d0..cbc612b 100644
--- a/sound/usb/usbmixer.c
+++ b/sound/usb/usbmixer.c
@@ -352,7 +352,11 @@ static int get_ctl_value(struct usb_mixer_elem_info *cval, 
int request, int vali
        unsigned char buf[2];
        int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
        int timeout = 10;
+       int err;
 
+       err = usb_autopm_get_interface(cval->mixer->chip->pm_intf);
+       if (err < 0)
+               return -EIO;
        while (timeout-- > 0) {
                if (snd_usb_ctl_msg(cval->mixer->chip->dev,
                                    usb_rcvctrlpipe(cval->mixer->chip->dev, 0),
@@ -361,9 +365,11 @@ static int get_ctl_value(struct usb_mixer_elem_info *cval, 
int request, int vali
                                    validx, cval->mixer->ctrlif | (cval->id << 
8),
                                    buf, val_len, 100) >= val_len) {
                        *value_ret = convert_signed_value(cval, 
snd_usb_combine_bytes(buf, val_len));
+                       usb_autopm_put_interface(cval->mixer->chip->pm_intf);
                        return 0;
                }
        }
+       usb_autopm_put_interface(cval->mixer->chip->pm_intf);
        snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, 
wIndex = %#x, type = %d\n",
                    request, validx, cval->mixer->ctrlif | (cval->id << 8), 
cval->val_type);
        return -EINVAL;
@@ -389,18 +395,27 @@ static int set_ctl_value(struct usb_mixer_elem_info 
*cval, int request, int vali
        unsigned char buf[2];
        int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
        int timeout = 10;
+       int err;
 
        value_set = convert_bytes_value(cval, value_set);
        buf[0] = value_set & 0xff;
        buf[1] = (value_set >> 8) & 0xff;
-       while (timeout -- > 0)
+       err = usb_autopm_get_interface(cval->mixer->chip->pm_intf);
+       if (err < 0)
+               return -EIO;
+
+       while (timeout -- > 0) {
                if (snd_usb_ctl_msg(cval->mixer->chip->dev,
                                    usb_sndctrlpipe(cval->mixer->chip->dev, 0),
                                    request,
                                    USB_RECIP_INTERFACE | USB_TYPE_CLASS | 
USB_DIR_OUT,
                                    validx, cval->mixer->ctrlif | (cval->id << 
8),
-                                   buf, val_len, 100) >= 0)
+                                   buf, val_len, 100) >= 0) {
+                       usb_autopm_put_interface(cval->mixer->chip->pm_intf);
                        return 0;
+                       }
+       }
+       usb_autopm_put_interface(cval->mixer->chip->pm_intf);
        snd_printdd(KERN_ERR "cannot set ctl value: req = %#x, wValue = %#x, 
wIndex = %#x, type = %d, data = %#x/%#x\n",
                    request, validx, cval->mixer->ctrlif | (cval->id << 8), 
cval->val_type, buf[0], buf[1]);
        return -EINVAL;
@@ -1910,16 +1925,28 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol 
*kcontrol, struct snd_ctl_e
        int value = ucontrol->value.integer.value[0];
        int err, changed;
 
-       if (value > 1)
-               return -EINVAL;
+       if (value > 1) {
+               changed = -EINVAL;
+               goto err_out;
+       }
        changed = value != mixer->audigy2nx_leds[index];
+       err = usb_autopm_get_interface(mixer->chip->pm_intf);
+       if (err < 0) {
+               changed = -EIO;
+               goto err_out;
+       }
        err = snd_usb_ctl_msg(mixer->chip->dev,
                              usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
                              USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
                              value, index + 2, NULL, 0, 100);
-       if (err < 0)
-               return err;
+       if (err < 0) {
+               changed = err;
+               goto err;
+       }
        mixer->audigy2nx_leds[index] = value;
+err:
+       usb_autopm_put_interface(mixer->chip->pm_intf);
+err_out:
        return changed;
 }
 
@@ -1998,6 +2025,10 @@ static void snd_audigy2nx_proc_read(struct 
snd_info_entry *entry,
        else
                return;
 
+       err = usb_autopm_get_interface(mixer->chip->pm_intf);
+       if (err < 0)
+               return;
+
        for (i = 0; jacks[i].name; ++i) {
                snd_iprintf(buffer, "%s: ", jacks[i].name);
                err = snd_usb_ctl_msg(mixer->chip->dev,
@@ -2010,6 +2041,7 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry 
*entry,
                else
                        snd_iprintf(buffer, "?\n");
        }
+       usb_autopm_put_interface(mixer->chip->pm_intf);
 }
 
 int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif)

-
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to