Skip to content

Commit

Permalink
snd/hda: add runtime suspend/resume on optimus support (v4)
Browse files Browse the repository at this point in the history
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.

v2: fix incorrect runtime call suspend->resume.

v3: rework irq handler to avoid false irq when we are resuming
but haven't runtime resumed yet, don't bother trying D3cold,
it won't work, just set it manually ourselves, move runtime s/r
calls outside the main s/r hook. enable dnyamic pm properly by
dropping reference.

v4: put back irq handler check just wrap it with cap check

Acked-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Dave Airlie <airlied@redhat.com>
  • Loading branch information
airlied committed Aug 29, 2013
1 parent 0d69704 commit 246efa4
Showing 1 changed file with 32 additions and 4 deletions.
36 changes: 32 additions & 4 deletions sound/pci/hda/hda_intel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1397,8 +1400,9 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
int i, ok;

#ifdef CONFIG_PM_RUNTIME
if (chip->pci->dev.power.runtime_status != RPM_ACTIVE)
return IRQ_NONE;
if (chip->driver_caps & AZX_DCAPS_PM_RUNTIME)
if (chip->pci->dev.power.runtime_status != RPM_ACTIVE)
return IRQ_NONE;
#endif

spin_lock(&chip->reg_lock);
Expand All @@ -1409,7 +1413,7 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
}

status = azx_readl(chip, INTSTS);
if (status == 0) {
if (status == 0 || status == 0xffffffff) {
spin_unlock(&chip->reg_lock);
return IRQ_NONE;
}
Expand Down Expand Up @@ -2971,6 +2975,12 @@ 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;

if (!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME))
return 0;

azx_stop_chip(chip);
azx_enter_link_reset(chip);
azx_clear_irq_pending(chip);
Expand All @@ -2984,6 +2994,12 @@ 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_PM_RUNTIME))
return 0;

if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
hda_display_power(true);
azx_init_pci(chip);
Expand All @@ -2996,6 +3012,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;
Expand Down Expand Up @@ -3078,13 +3097,19 @@ 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) {
pm_runtime_put_sync_suspend(&pci->dev);
azx_suspend(&pci->dev);
/* 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",
pci_name(chip->pci));
} else {
snd_hda_unlock_devices(chip->bus);
pm_runtime_get_noresume(&pci->dev);
chip->disabled = false;
azx_resume(&pci->dev);
}
Expand Down Expand Up @@ -3139,6 +3164,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
Expand Down Expand Up @@ -3887,7 +3915,7 @@ static int azx_probe_continue(struct azx *chip)
power_down_all_codecs(chip);
azx_notifier_register(chip);
azx_add_card_list(chip);
if (chip->driver_caps & AZX_DCAPS_PM_RUNTIME)
if ((chip->driver_caps & AZX_DCAPS_PM_RUNTIME) || chip->use_vga_switcheroo)
pm_runtime_put_noidle(&pci->dev);

return 0;
Expand Down

0 comments on commit 246efa4

Please sign in to comment.