diff mbox

[4/4] snd/hda: add runtime suspend/resume on optimus support

Message ID 1375078019-25358-5-git-send-email-airlied@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Dave Airlie July 29, 2013, 6:06 a.m. UTC
Add support for HDMI audio device on VGA cards that powerdown
to D3cold using non-standard ACPI/PCI infrastructure (optimus).

This does a couple of things to make it work:

a) add a set of power ops for the hdmi domain, and enables them
via vga_switcheroo when we are a switcheroo controlled card. This
just replaces the runtime resume operation so that when the card
is in D3cold the userspace pci config space access via sysfs,
the vga switcheroon runtime resume gets called first and it calls
the GPU resume callback before calling the sound card runtime
resume.

b) standard ACPI/PCI stacks won't put a device into D3cold without
an ACPI handle, but since the hdmi audio devices on gpus don't have
an ACPI handle, we need to manually force the device into D3cold
after suspend from the switcheroo path only.

c) don't try and do runtime s/r when the GPU is off.

d) call runtime suspend/resume during switcheroo suspend/resume
this is to make sure the runtime stack knows to try and resume
the hdmi audio device for pci config space access.

Signed-off-by: Dave Airlie <airlied@redhat.com>
---
 sound/pci/hda/hda_intel.c | 40 +++++++++++++++++++++++++++++++++++++---
 1 file changed, 37 insertions(+), 3 deletions(-)

Comments

Takashi Iwai July 29, 2013, 12:46 p.m. UTC | #1
At Mon, 29 Jul 2013 16:06:59 +1000,
Dave Airlie wrote:
> 
> Add support for HDMI audio device on VGA cards that powerdown
> to D3cold using non-standard ACPI/PCI infrastructure (optimus).
> 
> This does a couple of things to make it work:
> 
> a) add a set of power ops for the hdmi domain, and enables them
> via vga_switcheroo when we are a switcheroo controlled card. This
> just replaces the runtime resume operation so that when the card
> is in D3cold the userspace pci config space access via sysfs,
> the vga switcheroon runtime resume gets called first and it calls
> the GPU resume callback before calling the sound card runtime
> resume.
> 
> b) standard ACPI/PCI stacks won't put a device into D3cold without
> an ACPI handle, but since the hdmi audio devices on gpus don't have
> an ACPI handle, we need to manually force the device into D3cold
> after suspend from the switcheroo path only.
> 
> c) don't try and do runtime s/r when the GPU is off.
> 
> d) call runtime suspend/resume during switcheroo suspend/resume
> this is to make sure the runtime stack knows to try and resume
> the hdmi audio device for pci config space access.
> 
> Signed-off-by: Dave Airlie <airlied@redhat.com>
> ---
>  sound/pci/hda/hda_intel.c | 40 +++++++++++++++++++++++++++++++++++++---
>  1 file changed, 37 insertions(+), 3 deletions(-)
> 
> diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
> index 8860dd5..4b4d05b 100644
> --- a/sound/pci/hda/hda_intel.c
> +++ b/sound/pci/hda/hda_intel.c
> @@ -555,6 +555,9 @@ struct azx {
>  #ifdef CONFIG_SND_HDA_DSP_LOADER
>  	struct azx_dev saved_azx_dev;
>  #endif
> +
> +	/* secondary power domain for hdmi audio under vga device */
> +	struct dev_pm_domain hdmi_pm_domain;
>  };
>  
>  #define CREATE_TRACE_POINTS
> @@ -2898,7 +2901,7 @@ static int param_set_xint(const char *val, const struct kernel_param *kp)
>  /*
>   * power management
>   */
> -static int azx_suspend(struct device *dev)
> +static int azx_do_suspend(struct device *dev, pci_power_t state)
>  {
>  	struct pci_dev *pci = to_pci_dev(dev);
>  	struct snd_card *card = dev_get_drvdata(dev);
> @@ -2920,16 +2923,30 @@ static int azx_suspend(struct device *dev)
>  		free_irq(chip->irq, chip);
>  		chip->irq = -1;
>  	}
> +
> +	/*
> +	 * for vga switcheroo suspend we need to
> +	 * force runtime suspend so lspci works.
> +	 */
> +	if (state == PCI_D3cold)
> +		pm_runtime_suspend(&pci->dev);
> +
>  	if (chip->msi)
>  		pci_disable_msi(chip->pci);
>  	pci_disable_device(pci);
>  	pci_save_state(pci);
> -	pci_set_power_state(pci, PCI_D3hot);
> +
> +	pci_set_power_state(pci, state);
>  	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
>  		hda_display_power(false);
>  	return 0;
>  }
>  
> +static int azx_suspend(struct device *dev)
> +{
> +	return azx_do_suspend(dev, PCI_D3hot);
> +}
> +
>  static int azx_resume(struct device *dev)
>  {
>  	struct pci_dev *pci = to_pci_dev(dev);
> @@ -2971,6 +2988,9 @@ static int azx_runtime_suspend(struct device *dev)
>  	struct snd_card *card = dev_get_drvdata(dev);
>  	struct azx *chip = card->private_data;
>  
> +	if (chip->disabled)
> +		return 0;
> +
>  	azx_stop_chip(chip);
>  	azx_enter_link_reset(chip);
>  	azx_clear_irq_pending(chip);
> @@ -2984,6 +3004,9 @@ static int azx_runtime_resume(struct device *dev)
>  	struct snd_card *card = dev_get_drvdata(dev);
>  	struct azx *chip = card->private_data;
>  
> +	if (chip->disabled)
> +		return 0;
> +
>  	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
>  		hda_display_power(true);
>  	azx_init_pci(chip);
> @@ -2996,6 +3019,9 @@ static int azx_runtime_idle(struct device *dev)
>  	struct snd_card *card = dev_get_drvdata(dev);
>  	struct azx *chip = card->private_data;
>  
> +	if (chip->disabled)
> +		return 0;
> +
>  	if (!power_save_controller ||
>  	    !(chip->driver_caps & AZX_DCAPS_PM_RUNTIME))
>  		return -EBUSY;
> @@ -3078,7 +3104,11 @@ static void azx_vs_set_state(struct pci_dev *pci,
>  			   "%s: %s via VGA-switcheroo\n", pci_name(chip->pci),
>  			   disabled ? "Disabling" : "Enabling");
>  		if (disabled) {
> -			azx_suspend(&pci->dev);
> +			azx_do_suspend(&pci->dev, PCI_D3cold);
> +			/* when we get suspended by vga switcheroo we end up in D3cold,
> +			 * however we have no ACPI handle, so pci/acpi can't put us there,
> +			 * put ourselves there */
> +			pci->current_state = PCI_D3cold;
>  			chip->disabled = true;
>  			if (snd_hda_lock_devices(chip->bus))
>  				snd_printk(KERN_WARNING SFX "%s: Cannot lock devices!\n",
> @@ -3087,6 +3117,7 @@ static void azx_vs_set_state(struct pci_dev *pci,
>  			snd_hda_unlock_devices(chip->bus);
>  			chip->disabled = false;
>  			azx_resume(&pci->dev);
> +			pm_runtime_suspend(&pci->dev);

Is this meant as pm_runtime_resume()?


thanks,

Takashi

>  		}
>  	}
>  }
> @@ -3139,6 +3170,9 @@ static int register_vga_switcheroo(struct azx *chip)
>  	if (err < 0)
>  		return err;
>  	chip->vga_switcheroo_registered = 1;
> +
> +	/* register as an optimus hdmi audio power domain */
> +	vga_switcheroo_init_domain_pm_optimus_hdmi_audio(&chip->pci->dev, &chip->hdmi_pm_domain);
>  	return 0;
>  }
>  #else
> -- 
> 1.8.2.1
> 
>
Dave Airlie July 30, 2013, 6:46 a.m. UTC | #2
On Mon, Jul 29, 2013 at 10:46 PM, Takashi Iwai <tiwai@suse.de> wrote:
> At Mon, 29 Jul 2013 16:06:59 +1000,
> Dave Airlie wrote:
>>
>> Add support for HDMI audio device on VGA cards that powerdown
>> to D3cold using non-standard ACPI/PCI infrastructure (optimus).
>>
>> This does a couple of things to make it work:
>>
>> a) add a set of power ops for the hdmi domain, and enables them
>> via vga_switcheroo when we are a switcheroo controlled card. This
>> just replaces the runtime resume operation so that when the card
>> is in D3cold the userspace pci config space access via sysfs,
>> the vga switcheroon runtime resume gets called first and it calls
>> the GPU resume callback before calling the sound card runtime
>> resume.
>>
>> b) standard ACPI/PCI stacks won't put a device into D3cold without
>> an ACPI handle, but since the hdmi audio devices on gpus don't have
>> an ACPI handle, we need to manually force the device into D3cold
>> after suspend from the switcheroo path only.
>>
>> c) don't try and do runtime s/r when the GPU is off.
>>
>> d) call runtime suspend/resume during switcheroo suspend/resume
>> this is to make sure the runtime stack knows to try and resume
>> the hdmi audio device for pci config space access.
>>
>> Signed-off-by: Dave Airlie <airlied@redhat.com>
>> ---
>>  sound/pci/hda/hda_intel.c | 40 +++++++++++++++++++++++++++++++++++++---
>>  1 file changed, 37 insertions(+), 3 deletions(-)
>>
>> diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
>> index 8860dd5..4b4d05b 100644
>> --- a/sound/pci/hda/hda_intel.c
>> +++ b/sound/pci/hda/hda_intel.c
>> @@ -555,6 +555,9 @@ struct azx {
>>  #ifdef CONFIG_SND_HDA_DSP_LOADER
>>       struct azx_dev saved_azx_dev;
>>  #endif
>> +
>> +     /* secondary power domain for hdmi audio under vga device */
>> +     struct dev_pm_domain hdmi_pm_domain;
>>  };
>>
>>  #define CREATE_TRACE_POINTS
>> @@ -2898,7 +2901,7 @@ static int param_set_xint(const char *val, const struct kernel_param *kp)
>>  /*
>>   * power management
>>   */
>> -static int azx_suspend(struct device *dev)
>> +static int azx_do_suspend(struct device *dev, pci_power_t state)
>>  {
>>       struct pci_dev *pci = to_pci_dev(dev);
>>       struct snd_card *card = dev_get_drvdata(dev);
>> @@ -2920,16 +2923,30 @@ static int azx_suspend(struct device *dev)
>>               free_irq(chip->irq, chip);
>>               chip->irq = -1;
>>       }
>> +
>> +     /*
>> +      * for vga switcheroo suspend we need to
>> +      * force runtime suspend so lspci works.
>> +      */
>> +     if (state == PCI_D3cold)
>> +             pm_runtime_suspend(&pci->dev);
>> +
>>       if (chip->msi)
>>               pci_disable_msi(chip->pci);
>>       pci_disable_device(pci);
>>       pci_save_state(pci);
>> -     pci_set_power_state(pci, PCI_D3hot);
>> +
>> +     pci_set_power_state(pci, state);
>>       if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
>>               hda_display_power(false);
>>       return 0;
>>  }
>>
>> +static int azx_suspend(struct device *dev)
>> +{
>> +     return azx_do_suspend(dev, PCI_D3hot);
>> +}
>> +
>>  static int azx_resume(struct device *dev)
>>  {
>>       struct pci_dev *pci = to_pci_dev(dev);
>> @@ -2971,6 +2988,9 @@ static int azx_runtime_suspend(struct device *dev)
>>       struct snd_card *card = dev_get_drvdata(dev);
>>       struct azx *chip = card->private_data;
>>
>> +     if (chip->disabled)
>> +             return 0;
>> +
>>       azx_stop_chip(chip);
>>       azx_enter_link_reset(chip);
>>       azx_clear_irq_pending(chip);
>> @@ -2984,6 +3004,9 @@ static int azx_runtime_resume(struct device *dev)
>>       struct snd_card *card = dev_get_drvdata(dev);
>>       struct azx *chip = card->private_data;
>>
>> +     if (chip->disabled)
>> +             return 0;
>> +
>>       if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
>>               hda_display_power(true);
>>       azx_init_pci(chip);
>> @@ -2996,6 +3019,9 @@ static int azx_runtime_idle(struct device *dev)
>>       struct snd_card *card = dev_get_drvdata(dev);
>>       struct azx *chip = card->private_data;
>>
>> +     if (chip->disabled)
>> +             return 0;
>> +
>>       if (!power_save_controller ||
>>           !(chip->driver_caps & AZX_DCAPS_PM_RUNTIME))
>>               return -EBUSY;
>> @@ -3078,7 +3104,11 @@ static void azx_vs_set_state(struct pci_dev *pci,
>>                          "%s: %s via VGA-switcheroo\n", pci_name(chip->pci),
>>                          disabled ? "Disabling" : "Enabling");
>>               if (disabled) {
>> -                     azx_suspend(&pci->dev);
>> +                     azx_do_suspend(&pci->dev, PCI_D3cold);
>> +                     /* when we get suspended by vga switcheroo we end up in D3cold,
>> +                      * however we have no ACPI handle, so pci/acpi can't put us there,
>> +                      * put ourselves there */
>> +                     pci->current_state = PCI_D3cold;
>>                       chip->disabled = true;
>>                       if (snd_hda_lock_devices(chip->bus))
>>                               snd_printk(KERN_WARNING SFX "%s: Cannot lock devices!\n",
>> @@ -3087,6 +3117,7 @@ static void azx_vs_set_state(struct pci_dev *pci,
>>                       snd_hda_unlock_devices(chip->bus);
>>                       chip->disabled = false;
>>                       azx_resume(&pci->dev);
>> +                     pm_runtime_suspend(&pci->dev);
>
> Is this meant as pm_runtime_resume()?
>

Oh probably that seems likely alright!

Dave.
diff mbox

Patch

diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 8860dd5..4b4d05b 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -555,6 +555,9 @@  struct azx {
 #ifdef CONFIG_SND_HDA_DSP_LOADER
 	struct azx_dev saved_azx_dev;
 #endif
+
+	/* secondary power domain for hdmi audio under vga device */
+	struct dev_pm_domain hdmi_pm_domain;
 };
 
 #define CREATE_TRACE_POINTS
@@ -2898,7 +2901,7 @@  static int param_set_xint(const char *val, const struct kernel_param *kp)
 /*
  * power management
  */
-static int azx_suspend(struct device *dev)
+static int azx_do_suspend(struct device *dev, pci_power_t state)
 {
 	struct pci_dev *pci = to_pci_dev(dev);
 	struct snd_card *card = dev_get_drvdata(dev);
@@ -2920,16 +2923,30 @@  static int azx_suspend(struct device *dev)
 		free_irq(chip->irq, chip);
 		chip->irq = -1;
 	}
+
+	/*
+	 * for vga switcheroo suspend we need to
+	 * force runtime suspend so lspci works.
+	 */
+	if (state == PCI_D3cold)
+		pm_runtime_suspend(&pci->dev);
+
 	if (chip->msi)
 		pci_disable_msi(chip->pci);
 	pci_disable_device(pci);
 	pci_save_state(pci);
-	pci_set_power_state(pci, PCI_D3hot);
+
+	pci_set_power_state(pci, state);
 	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
 		hda_display_power(false);
 	return 0;
 }
 
+static int azx_suspend(struct device *dev)
+{
+	return azx_do_suspend(dev, PCI_D3hot);
+}
+
 static int azx_resume(struct device *dev)
 {
 	struct pci_dev *pci = to_pci_dev(dev);
@@ -2971,6 +2988,9 @@  static int azx_runtime_suspend(struct device *dev)
 	struct snd_card *card = dev_get_drvdata(dev);
 	struct azx *chip = card->private_data;
 
+	if (chip->disabled)
+		return 0;
+
 	azx_stop_chip(chip);
 	azx_enter_link_reset(chip);
 	azx_clear_irq_pending(chip);
@@ -2984,6 +3004,9 @@  static int azx_runtime_resume(struct device *dev)
 	struct snd_card *card = dev_get_drvdata(dev);
 	struct azx *chip = card->private_data;
 
+	if (chip->disabled)
+		return 0;
+
 	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
 		hda_display_power(true);
 	azx_init_pci(chip);
@@ -2996,6 +3019,9 @@  static int azx_runtime_idle(struct device *dev)
 	struct snd_card *card = dev_get_drvdata(dev);
 	struct azx *chip = card->private_data;
 
+	if (chip->disabled)
+		return 0;
+
 	if (!power_save_controller ||
 	    !(chip->driver_caps & AZX_DCAPS_PM_RUNTIME))
 		return -EBUSY;
@@ -3078,7 +3104,11 @@  static void azx_vs_set_state(struct pci_dev *pci,
 			   "%s: %s via VGA-switcheroo\n", pci_name(chip->pci),
 			   disabled ? "Disabling" : "Enabling");
 		if (disabled) {
-			azx_suspend(&pci->dev);
+			azx_do_suspend(&pci->dev, PCI_D3cold);
+			/* when we get suspended by vga switcheroo we end up in D3cold,
+			 * however we have no ACPI handle, so pci/acpi can't put us there,
+			 * put ourselves there */
+			pci->current_state = PCI_D3cold;
 			chip->disabled = true;
 			if (snd_hda_lock_devices(chip->bus))
 				snd_printk(KERN_WARNING SFX "%s: Cannot lock devices!\n",
@@ -3087,6 +3117,7 @@  static void azx_vs_set_state(struct pci_dev *pci,
 			snd_hda_unlock_devices(chip->bus);
 			chip->disabled = false;
 			azx_resume(&pci->dev);
+			pm_runtime_suspend(&pci->dev);
 		}
 	}
 }
@@ -3139,6 +3170,9 @@  static int register_vga_switcheroo(struct azx *chip)
 	if (err < 0)
 		return err;
 	chip->vga_switcheroo_registered = 1;
+
+	/* register as an optimus hdmi audio power domain */
+	vga_switcheroo_init_domain_pm_optimus_hdmi_audio(&chip->pci->dev, &chip->hdmi_pm_domain);
 	return 0;
 }
 #else