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>
This commit is contained in:
committed by
Takashi Iwai
parent
edf7de31c2
commit
88a8516a21
@ -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(®ister_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)
|
||||
|
Reference in New Issue
Block a user