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