diff options
Diffstat (limited to 'sound/soc/intel/skylake/skl.c')
-rw-r--r-- | sound/soc/intel/skylake/skl.c | 67 |
1 files changed, 62 insertions, 5 deletions
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 2989c164dafe..da5db5098274 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -26,6 +26,7 @@ #include <linux/pm_runtime.h> #include <linux/platform_device.h> #include <linux/firmware.h> +#include <linux/delay.h> #include <sound/pcm.h> #include "../common/sst-acpi.h" #include <sound/hda_register.h> @@ -109,6 +110,52 @@ static int skl_init_chip(struct hdac_bus *bus, bool full_reset) return ret; } +void skl_update_d0i3c(struct device *dev, bool enable) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct hdac_ext_bus *ebus = pci_get_drvdata(pci); + struct hdac_bus *bus = ebus_to_hbus(ebus); + u8 reg; + int timeout = 50; + + reg = snd_hdac_chip_readb(bus, VS_D0I3C); + /* Do not write to D0I3C until command in progress bit is cleared */ + while ((reg & AZX_REG_VS_D0I3C_CIP) && --timeout) { + udelay(10); + reg = snd_hdac_chip_readb(bus, VS_D0I3C); + } + + /* Highly unlikely. But if it happens, flag error explicitly */ + if (!timeout) { + dev_err(bus->dev, "Before D0I3C update: D0I3C CIP timeout\n"); + return; + } + + if (enable) + reg = reg | AZX_REG_VS_D0I3C_I3; + else + reg = reg & (~AZX_REG_VS_D0I3C_I3); + + snd_hdac_chip_writeb(bus, VS_D0I3C, reg); + + timeout = 50; + /* Wait for cmd in progress to be cleared before exiting the function */ + reg = snd_hdac_chip_readb(bus, VS_D0I3C); + while ((reg & AZX_REG_VS_D0I3C_CIP) && --timeout) { + udelay(10); + reg = snd_hdac_chip_readb(bus, VS_D0I3C); + } + + /* Highly unlikely. But if it happens, flag error explicitly */ + if (!timeout) { + dev_err(bus->dev, "After D0I3C update: D0I3C CIP timeout\n"); + return; + } + + dev_dbg(bus->dev, "D0I3C register = 0x%x\n", + snd_hdac_chip_readb(bus, VS_D0I3C)); +} + /* called from IRQ */ static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr) { @@ -181,6 +228,15 @@ static int skl_acquire_irq(struct hdac_ext_bus *ebus, int do_disconnect) return 0; } +static int skl_suspend_late(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct hdac_ext_bus *ebus = pci_get_drvdata(pci); + struct skl *skl = ebus_to_skl(ebus); + + return skl_suspend_late_dsp(skl); +} + #ifdef CONFIG_PM static int _skl_suspend(struct hdac_ext_bus *ebus) { @@ -243,7 +299,6 @@ static int skl_suspend(struct device *dev) enable_irq_wake(bus->irq); pci_save_state(pci); - pci_disable_device(pci); } else { ret = _skl_suspend(ebus); if (ret < 0) @@ -286,7 +341,6 @@ static int skl_resume(struct device *dev) */ if (skl->supend_active) { pci_restore_state(pci); - ret = pci_enable_device(pci); snd_hdac_ext_bus_link_power_up_all(ebus); disable_irq_wake(bus->irq); /* @@ -345,6 +399,7 @@ static int skl_runtime_resume(struct device *dev) static const struct dev_pm_ops skl_pm = { SET_SYSTEM_SLEEP_PM_OPS(skl_suspend, skl_resume) SET_RUNTIME_PM_OPS(skl_runtime_suspend, skl_runtime_resume, NULL) + .suspend_late = skl_suspend_late, }; /* @@ -674,7 +729,7 @@ static int skl_probe(struct pci_dev *pci, if (skl->nhlt == NULL) { err = -ENODEV; - goto out_free; + goto out_display_power_off; } skl_nhlt_update_topology_bin(skl); @@ -746,6 +801,9 @@ out_mach_free: skl_machine_device_unregister(skl); out_nhlt_free: skl_nhlt_free(skl->nhlt); +out_display_power_off: + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) + snd_hdac_display_power(bus, false); out_free: skl->init_failed = 1; skl_free(ebus); @@ -785,8 +843,7 @@ static void skl_remove(struct pci_dev *pci) release_firmware(skl->tplg); - if (pci_dev_run_wake(pci)) - pm_runtime_get_noresume(&pci->dev); + pm_runtime_get_noresume(&pci->dev); /* codec removal, invoke bus_device_remove */ snd_hdac_ext_bus_device_remove(ebus); |