ALSA: usb-audio: Manage auto-pm of all bundled interfaces
authorTakashi Iwai <tiwai@suse.de>
Fri, 5 Jun 2020 06:41:17 +0000 (08:41 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 8 Jun 2020 17:34:49 +0000 (19:34 +0200)
Currently USB-audio driver manages the auto-pm of the primary
interface although a card may consist of multiple interfaces.
This may leave the secondary and other interfaces left running
unnecessarily after the auto-suspend.

This patch allows the driver managing the auto-pm of all bundled
interfaces per card.  The chip->pm_intf field is extended as
chip->intf[] to contain the array of assigned interfaces, and the
runtime-PM is performed to all those interfaces.

Tested-by: Macpaul Lin <macpaul.lin@mediatek.com>
Link: https://lore.kernel.org/r/20200605064117.28504-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/usb/card.c
sound/usb/usbaudio.h

index 359f7a0..162bdd6 100644 (file)
@@ -634,7 +634,6 @@ static int usb_audio_probe(struct usb_interface *intf,
                                                                   id, &chip);
                                        if (err < 0)
                                                goto __error;
-                                       chip->pm_intf = intf;
                                        break;
                                } else if (vid[i] != -1 || pid[i] != -1) {
                                        dev_info(&dev->dev,
@@ -651,6 +650,13 @@ static int usb_audio_probe(struct usb_interface *intf,
                        goto __error;
                }
        }
+
+       if (chip->num_interfaces >= MAX_CARD_INTERFACES) {
+               dev_info(&dev->dev, "Too many interfaces assigned to the single USB-audio card\n");
+               err = -EINVAL;
+               goto __error;
+       }
+
        dev_set_drvdata(&dev->dev, chip);
 
        /*
@@ -703,6 +709,7 @@ static int usb_audio_probe(struct usb_interface *intf,
        }
 
        usb_chip[chip->index] = chip;
+       chip->intf[chip->num_interfaces] = intf;
        chip->num_interfaces++;
        usb_set_intfdata(intf, chip);
        atomic_dec(&chip->active);
@@ -818,19 +825,37 @@ void snd_usb_unlock_shutdown(struct snd_usb_audio *chip)
 
 int snd_usb_autoresume(struct snd_usb_audio *chip)
 {
+       int i, err;
+
        if (atomic_read(&chip->shutdown))
                return -EIO;
-       if (atomic_inc_return(&chip->active) == 1)
-               return usb_autopm_get_interface(chip->pm_intf);
+       if (atomic_inc_return(&chip->active) != 1)
+               return 0;
+
+       for (i = 0; i < chip->num_interfaces; i++) {
+               err = usb_autopm_get_interface(chip->intf[i]);
+               if (err < 0) {
+                       /* rollback */
+                       while (--i >= 0)
+                               usb_autopm_put_interface(chip->intf[i]);
+                       atomic_dec(&chip->active);
+                       return err;
+               }
+       }
        return 0;
 }
 
 void snd_usb_autosuspend(struct snd_usb_audio *chip)
 {
+       int i;
+
        if (atomic_read(&chip->shutdown))
                return;
-       if (atomic_dec_and_test(&chip->active))
-               usb_autopm_put_interface(chip->pm_intf);
+       if (!atomic_dec_and_test(&chip->active))
+               return;
+
+       for (i = 0; i < chip->num_interfaces; i++)
+               usb_autopm_put_interface(chip->intf[i]);
 }
 
 static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
index e0ebfb2..b91c4c0 100644 (file)
 struct media_device;
 struct media_intf_devnode;
 
+#define MAX_CARD_INTERFACES    16
+
 struct snd_usb_audio {
        int index;
        struct usb_device *dev;
        struct snd_card *card;
-       struct usb_interface *pm_intf;
+       struct usb_interface *intf[MAX_CARD_INTERFACES];
        u32 usb_id;
        struct mutex mutex;
        unsigned int system_suspend;