summaryrefslogtreecommitdiff
path: root/sound/usb/card.c
diff options
context:
space:
mode:
authorOliver Neukum <oneukum@suse.de>2011-03-11 14:51:12 +0100
committerTakashi Iwai <tiwai@suse.de>2011-03-11 14:59:29 +0100
commit88a8516a2128a6d078a106ead48092240e8a138f (patch)
treedc839c85a9b2fc2e17759c5a42368cb8300e42bc /sound/usb/card.c
parentedf7de31c25ce72f163bf7d1fc0d2711869d073c (diff)
ALSA: usbaudio: implement USB autosuspend
Devices are autosuspended if no pcm nor midi channel is open Mixer devices may be opened. This way they are active when in use to play or record sound, but can be suspended while users have a mixer application running. [Small clean-ups using static inline by tiwai] Signed-off-by: Oliver Neukum <oneukum@suse.de> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/card.c')
-rw-r--r--sound/usb/card.c66
1 files changed, 52 insertions, 14 deletions
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 7fa53d91e73..40722f8711a 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -65,6 +65,7 @@
#include "pcm.h"
#include "urb.h"
#include "format.h"
+#include "power.h"
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("USB Audio");
@@ -330,6 +331,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
chip->setup = device_setup[idx];
chip->nrpacks = nrpacks;
chip->async_unlink = async_unlink;
+ chip->probing = 1;
chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
@@ -451,6 +453,7 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
goto __error;
}
chip = usb_chip[i];
+ chip->probing = 1;
break;
}
}
@@ -466,6 +469,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) {
@@ -505,6 +509,7 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
usb_chip[chip->index] = chip;
chip->num_interfaces++;
+ chip->probing = 0;
mutex_unlock(&register_mutex);
return chip;
@@ -581,6 +586,23 @@ static void usb_audio_disconnect(struct usb_interface *intf)
}
#ifdef CONFIG_PM
+
+int snd_usb_autoresume(struct snd_usb_audio *chip)
+{
+ int err = -ENODEV;
+
+ if (!chip->shutdown && !chip->probing)
+ err = usb_autopm_get_interface(chip->pm_intf);
+
+ return err;
+}
+
+void snd_usb_autosuspend(struct snd_usb_audio *chip)
+{
+ if (!chip->shutdown && !chip->probing)
+ usb_autopm_put_interface(chip->pm_intf);
+}
+
static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
{
struct snd_usb_audio *chip = usb_get_intfdata(intf);
@@ -591,18 +613,26 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
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);
- }
-
- list_for_each_entry(mixer, &chip->mixer_list, list) {
- snd_usb_mixer_inactivate(mixer);
- }
+ if (!(message.event & PM_EVENT_AUTO)) {
+ 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
+ */
+ if (!chip->num_suspended_intf++)
+ chip->autosuspended = 1;
}
+ list_for_each_entry(mixer, &chip->mixer_list, list)
+ snd_usb_mixer_inactivate(mixer);
+
return 0;
}
@@ -610,6 +640,7 @@ static int usb_audio_resume(struct usb_interface *intf)
{
struct snd_usb_audio *chip = usb_get_intfdata(intf);
struct usb_mixer_interface *mixer;
+ int err = 0;
if (chip == (void *)-1L)
return 0;
@@ -619,12 +650,18 @@ static int usb_audio_resume(struct usb_interface *intf)
* ALSA leaves material resumption to user space
* we just notify and restart the mixers
*/
- list_for_each_entry(mixer, &chip->mixer_list, list)
- snd_usb_mixer_activate(mixer);
+ list_for_each_entry(mixer, &chip->mixer_list, list) {
+ err = snd_usb_mixer_activate(mixer);
+ if (err < 0)
+ goto err_out;
+ }
- snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
+ if (!chip->autosuspended)
+ snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
+ chip->autosuspended = 0;
- return 0;
+err_out:
+ return err;
}
#else
#define usb_audio_suspend NULL
@@ -652,6 +689,7 @@ static struct usb_driver usb_audio_driver = {
.suspend = usb_audio_suspend,
.resume = usb_audio_resume,
.id_table = usb_audio_ids,
+ .supports_autosuspend = 1,
};
static int __init snd_usb_audio_init(void)