| From 3e6db33aaf1d42a30339f831ec4850570d6cc7a3 Mon Sep 17 00:00:00 2001 |
| From: Xiong Zhang <xiong.y.zhang@intel.com> |
| Date: Fri, 18 Dec 2015 13:29:18 +0800 |
| Subject: ALSA: hda - Set SKL+ hda controller power at freeze() and thaw() |
| |
| From: Xiong Zhang <xiong.y.zhang@intel.com> |
| |
| commit 3e6db33aaf1d42a30339f831ec4850570d6cc7a3 upstream. |
| |
| It takes three minutes to enter into hibernation on some OEM SKL |
| machines and we see many codec spurious response after thaw() opertion. |
| This is because HDA is still in D0 state after freeze() call and |
| pci_pm_freeze/pci_pm_freeze_noirq() don't set D3 hot in pci_bus driver. |
| It seems bios still access HDA when system enter into freeze state, |
| HDA will receive codec response interrupt immediately after thaw() call. |
| Because of this unexpected interrupt, HDA enter into a abnormal |
| state and slow down the system enter into hibernation. |
| |
| In this patch, we put HDA into D3 hot state in azx_freeze_noirq() and |
| put HDA into D0 state in azx_thaw_noirq(). |
| |
| V2: Only apply this fix to SKL+ |
| Fix compile error when CONFIG_PM_SLEEP isn't defined |
| |
| [Yet another fix for CONFIG_PM_SLEEP ifdef and the additional comment |
| by tiwai] |
| |
| Signed-off-by: Xiong Zhang <xiong.y.zhang@intel.com> |
| Signed-off-by: Takashi Iwai <tiwai@suse.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| sound/pci/hda/hda_intel.c | 34 ++++++++++++++++++++++++++++++++++ |
| 1 file changed, 34 insertions(+) |
| |
| --- a/sound/pci/hda/hda_intel.c |
| +++ b/sound/pci/hda/hda_intel.c |
| @@ -927,6 +927,36 @@ static int azx_resume(struct device *dev |
| } |
| #endif /* CONFIG_PM_SLEEP || SUPPORT_VGA_SWITCHEROO */ |
| |
| +#ifdef CONFIG_PM_SLEEP |
| +/* put codec down to D3 at hibernation for Intel SKL+; |
| + * otherwise BIOS may still access the codec and screw up the driver |
| + */ |
| +#define IS_SKL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa170) |
| +#define IS_SKL_LP(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x9d70) |
| +#define IS_BXT(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x5a98) |
| +#define IS_SKL_PLUS(pci) (IS_SKL(pci) || IS_SKL_LP(pci) || IS_BXT(pci)) |
| + |
| +static int azx_freeze_noirq(struct device *dev) |
| +{ |
| + struct pci_dev *pci = to_pci_dev(dev); |
| + |
| + if (IS_SKL_PLUS(pci)) |
| + pci_set_power_state(pci, PCI_D3hot); |
| + |
| + return 0; |
| +} |
| + |
| +static int azx_thaw_noirq(struct device *dev) |
| +{ |
| + struct pci_dev *pci = to_pci_dev(dev); |
| + |
| + if (IS_SKL_PLUS(pci)) |
| + pci_set_power_state(pci, PCI_D0); |
| + |
| + return 0; |
| +} |
| +#endif /* CONFIG_PM_SLEEP */ |
| + |
| #ifdef CONFIG_PM |
| static int azx_runtime_suspend(struct device *dev) |
| { |
| @@ -1036,6 +1066,10 @@ static int azx_runtime_idle(struct devic |
| |
| static const struct dev_pm_ops azx_pm = { |
| SET_SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume) |
| +#ifdef CONFIG_PM_SLEEP |
| + .freeze_noirq = azx_freeze_noirq, |
| + .thaw_noirq = azx_thaw_noirq, |
| +#endif |
| SET_RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, azx_runtime_idle) |
| }; |
| |