]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for 4.14
authorSasha Levin <sashal@kernel.org>
Thu, 18 Jun 2020 21:13:23 +0000 (17:13 -0400)
committerSasha Levin <sashal@kernel.org>
Thu, 18 Jun 2020 21:13:23 +0000 (17:13 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
12 files changed:
queue-4.14/pci-add-acs-quirk-for-ampere-root-ports.patch [new file with mode: 0644]
queue-4.14/pci-add-acs-quirk-for-intel-root-complex-integrated-.patch [new file with mode: 0644]
queue-4.14/pci-add-acs-quirk-for-iproc-paxb.patch [new file with mode: 0644]
queue-4.14/pci-avoid-flr-for-amd-matisse-hd-audio-usb-3.0.patch [new file with mode: 0644]
queue-4.14/pci-avoid-flr-for-amd-starship-usb-3.0.patch [new file with mode: 0644]
queue-4.14/pci-disable-msi-for-freescale-layerscape-pcie-rc-mod.patch [new file with mode: 0644]
queue-4.14/pci-generalize-multi-function-power-dependency-devic.patch [new file with mode: 0644]
queue-4.14/pci-make-acs-quirk-implementations-more-uniform.patch [new file with mode: 0644]
queue-4.14/pci-unify-acs-quirk-desired-vs-provided-checking.patch [new file with mode: 0644]
queue-4.14/series
queue-4.14/vga_switcheroo-deduplicate-power-state-tracking.patch [new file with mode: 0644]
queue-4.14/vga_switcheroo-use-device-link-for-hda-controller.patch [new file with mode: 0644]

diff --git a/queue-4.14/pci-add-acs-quirk-for-ampere-root-ports.patch b/queue-4.14/pci-add-acs-quirk-for-ampere-root-ports.patch
new file mode 100644 (file)
index 0000000..40e139a
--- /dev/null
@@ -0,0 +1,61 @@
+From 2d9452b07f3030faa6effe2de76436d9e8523a92 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 20 Feb 2018 19:19:27 -0800
+Subject: PCI: Add ACS quirk for Ampere root ports
+
+From: Feng Kan <fkan@apm.com>
+
+[ Upstream commit 4ef76ad0462cf25ce948541c8724eaa8a8365e1d ]
+
+The Ampere Computing PCIe root port does not support ACS at this point.
+However, the hardware provides isolation and source validation through the
+SMMU. The stream ID generated by the PCIe ports contain both the
+bus/device/function number as well as the port ID in its 3 most significant
+bits. Turn on ACS but disable all the peer-to-peer features.
+
+APM is being rebranded to Ampere.  The Vendor and Device IDs change, but
+the functionality stays the same.
+
+Signed-off-by: Feng Kan <fkan@apm.com>
+Signed-off-by: Bjorn Helgaas <helgaas@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pci/quirks.c    | 9 +++++++++
+ include/linux/pci_ids.h | 1 +
+ 2 files changed, 10 insertions(+)
+
+diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
+index f6e88d5b1c4f..81d76e34b0db 100644
+--- a/drivers/pci/quirks.c
++++ b/drivers/pci/quirks.c
+@@ -4610,6 +4610,15 @@ static const struct pci_dev_acs_enabled {
+       { PCI_VENDOR_ID_CAVIUM, PCI_ANY_ID, pci_quirk_cavium_acs },
+       /* APM X-Gene */
+       { PCI_VENDOR_ID_AMCC, 0xE004, pci_quirk_xgene_acs },
++      /* Ampere Computing */
++      { PCI_VENDOR_ID_AMPERE, 0xE005, pci_quirk_xgene_acs },
++      { PCI_VENDOR_ID_AMPERE, 0xE006, pci_quirk_xgene_acs },
++      { PCI_VENDOR_ID_AMPERE, 0xE007, pci_quirk_xgene_acs },
++      { PCI_VENDOR_ID_AMPERE, 0xE008, pci_quirk_xgene_acs },
++      { PCI_VENDOR_ID_AMPERE, 0xE009, pci_quirk_xgene_acs },
++      { PCI_VENDOR_ID_AMPERE, 0xE00A, pci_quirk_xgene_acs },
++      { PCI_VENDOR_ID_AMPERE, 0xE00B, pci_quirk_xgene_acs },
++      { PCI_VENDOR_ID_AMPERE, 0xE00C, pci_quirk_xgene_acs },
+       { PCI_VENDOR_ID_BROADCOM, 0xD714, pci_quirk_brcm_acs },
+       { 0 }
+ };
+diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
+index 7fa3f1498b34..bd882f51fb5f 100644
+--- a/include/linux/pci_ids.h
++++ b/include/linux/pci_ids.h
+@@ -1331,6 +1331,7 @@
+ #define PCI_DEVICE_ID_IMS_TT3D                0x9135
+ #define PCI_VENDOR_ID_AMCC            0x10e8
++#define PCI_VENDOR_ID_AMPERE          0x1def
+ #define PCI_VENDOR_ID_INTERG          0x10ea
+ #define PCI_DEVICE_ID_INTERG_1682     0x1682
+-- 
+2.25.1
+
diff --git a/queue-4.14/pci-add-acs-quirk-for-intel-root-complex-integrated-.patch b/queue-4.14/pci-add-acs-quirk-for-intel-root-complex-integrated-.patch
new file mode 100644 (file)
index 0000000..3d6017d
--- /dev/null
@@ -0,0 +1,113 @@
+From 3176df575b23c89afb39c412539d2ada05dca6b4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2020 13:57:42 -0700
+Subject: PCI: Add ACS quirk for Intel Root Complex Integrated Endpoints
+
+From: Ashok Raj <ashok.raj@intel.com>
+
+[ Upstream commit 3247bd10a4502a3075ce8e1c3c7d31ef76f193ce ]
+
+All Intel platforms guarantee that all root complex implementations must
+send transactions up to IOMMU for address translations. Hence for Intel
+RCiEP devices, we can assume some ACS-type isolation even without an ACS
+capability.
+
+From the Intel VT-d spec, r3.1, sec 3.16 ("Root-Complex Peer to Peer
+Considerations"):
+
+  When DMA remapping is enabled, peer-to-peer requests through the
+  Root-Complex must be handled as follows:
+
+  - The input address in the request is translated (through first-level,
+    second-level or nested translation) to a host physical address (HPA).
+    The address decoding for peer addresses must be done only on the
+    translated HPA. Hardware implementations are free to further limit
+    peer-to-peer accesses to specific host physical address regions (or
+    to completely disallow peer-forwarding of translated requests).
+
+  - Since address translation changes the contents (address field) of
+    the PCI Express Transaction Layer Packet (TLP), for PCI Express
+    peer-to-peer requests with ECRC, the Root-Complex hardware must use
+    the new ECRC (re-computed with the translated address) if it
+    decides to forward the TLP as a peer request.
+
+  - Root-ports, and multi-function root-complex integrated endpoints, may
+    support additional peer-to-peer control features by supporting PCI
+    Express Access Control Services (ACS) capability. Refer to ACS
+    capability in PCI Express specifications for details.
+
+Since Linux didn't give special treatment to allow this exception, certain
+RCiEP MFD devices were grouped in a single IOMMU group. This doesn't permit
+a single device to be assigned to a guest for instance.
+
+In one vendor system: Device 14.x were grouped in a single IOMMU group.
+
+  /sys/kernel/iommu_groups/5/devices/0000:00:14.0
+  /sys/kernel/iommu_groups/5/devices/0000:00:14.2
+  /sys/kernel/iommu_groups/5/devices/0000:00:14.3
+
+After this patch:
+
+  /sys/kernel/iommu_groups/5/devices/0000:00:14.0
+  /sys/kernel/iommu_groups/5/devices/0000:00:14.2
+  /sys/kernel/iommu_groups/6/devices/0000:00:14.3 <<< new group
+
+14.0 and 14.2 are integrated devices, but legacy end points, whereas 14.3
+was a PCIe-compliant RCiEP.
+
+  00:14.3 Network controller: Intel Corporation Device 9df0 (rev 30)
+    Capabilities: [40] Express (v2) Root Complex Integrated Endpoint, MSI 00
+
+This permits assigning this device to a guest VM.
+
+[bhelgaas: drop "Fixes" tag since this doesn't fix a bug in that commit]
+Link: https://lore.kernel.org/r/1590699462-7131-1-git-send-email-ashok.raj@intel.com
+Tested-by: Darrel Goeddel <dgoeddel@forcepoint.com>
+Signed-off-by: Ashok Raj <ashok.raj@intel.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Reviewed-by: Alex Williamson <alex.williamson@redhat.com>
+Cc: stable@vger.kernel.org
+Cc: Lu Baolu <baolu.lu@linux.intel.com>
+Cc: Mark Scott <mscott@forcepoint.com>,
+Cc: Romil Sharma <rsharma@forcepoint.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pci/quirks.c | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
+index 3e1a0a207734..0472e69833c8 100644
+--- a/drivers/pci/quirks.c
++++ b/drivers/pci/quirks.c
+@@ -4513,6 +4513,20 @@ static int pci_quirk_mf_endpoint_acs(struct pci_dev *dev, u16 acs_flags)
+       return acs_flags ? 0 : 1;
+ }
++static int pci_quirk_rciep_acs(struct pci_dev *dev, u16 acs_flags)
++{
++      /*
++       * Intel RCiEP's are required to allow p2p only on translated
++       * addresses.  Refer to Intel VT-d specification, r3.1, sec 3.16,
++       * "Root-Complex Peer to Peer Considerations".
++       */
++      if (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_END)
++              return -ENOTTY;
++
++      return pci_acs_ctrl_enabled(acs_flags,
++              PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF);
++}
++
+ static int pci_quirk_brcm_acs(struct pci_dev *dev, u16 acs_flags)
+ {
+       /*
+@@ -4596,6 +4610,7 @@ static const struct pci_dev_acs_enabled {
+       /* I219 */
+       { PCI_VENDOR_ID_INTEL, 0x15b7, pci_quirk_mf_endpoint_acs },
+       { PCI_VENDOR_ID_INTEL, 0x15b8, pci_quirk_mf_endpoint_acs },
++      { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_rciep_acs },
+       /* QCOM QDF2xxx root ports */
+       { 0x17cb, 0x400, pci_quirk_qcom_rp_acs },
+       { 0x17cb, 0x401, pci_quirk_qcom_rp_acs },
+-- 
+2.25.1
+
diff --git a/queue-4.14/pci-add-acs-quirk-for-iproc-paxb.patch b/queue-4.14/pci-add-acs-quirk-for-iproc-paxb.patch
new file mode 100644 (file)
index 0000000..e3c6efc
--- /dev/null
@@ -0,0 +1,59 @@
+From 7076f531cfb1a89d7cff3272c045c77681c8bc4c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 20 Aug 2019 10:09:45 +0530
+Subject: PCI: Add ACS quirk for iProc PAXB
+
+From: Abhinav Ratna <abhinav.ratna@broadcom.com>
+
+[ Upstream commit 46b2c32df7a462d0e64b68c513e5c4c1b2a399a7 ]
+
+iProc PAXB Root Ports don't advertise an ACS capability, but they do not
+allow peer-to-peer transactions between Root Ports.  Add an ACS quirk so
+each Root Port can be in a separate IOMMU group.
+
+[bhelgaas: commit log, comment, use common implementation style]
+Link: https://lore.kernel.org/r/1566275985-25670-1-git-send-email-srinath.mannam@broadcom.com
+Signed-off-by: Abhinav Ratna <abhinav.ratna@broadcom.com>
+Signed-off-by: Srinath Mannam <srinath.mannam@broadcom.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Acked-by: Scott Branden <scott.branden@broadcom.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pci/quirks.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
+index 1bc7d4bcfea4..f6e88d5b1c4f 100644
+--- a/drivers/pci/quirks.c
++++ b/drivers/pci/quirks.c
+@@ -4515,6 +4515,19 @@ static int pci_quirk_mf_endpoint_acs(struct pci_dev *dev, u16 acs_flags)
+       return acs_flags ? 0 : 1;
+ }
++static int pci_quirk_brcm_acs(struct pci_dev *dev, u16 acs_flags)
++{
++      /*
++       * iProc PAXB Root Ports don't advertise an ACS capability, but
++       * they do not allow peer-to-peer transactions between Root Ports.
++       * Allow each Root Port to be in a separate IOMMU group by masking
++       * SV/RR/CR/UF bits.
++       */
++      acs_flags &= ~(PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF);
++
++      return acs_flags ? 0 : 1;
++}
++
+ static const struct pci_dev_acs_enabled {
+       u16 vendor;
+       u16 device;
+@@ -4597,6 +4610,7 @@ static const struct pci_dev_acs_enabled {
+       { PCI_VENDOR_ID_CAVIUM, PCI_ANY_ID, pci_quirk_cavium_acs },
+       /* APM X-Gene */
+       { PCI_VENDOR_ID_AMCC, 0xE004, pci_quirk_xgene_acs },
++      { PCI_VENDOR_ID_BROADCOM, 0xD714, pci_quirk_brcm_acs },
+       { 0 }
+ };
+-- 
+2.25.1
+
diff --git a/queue-4.14/pci-avoid-flr-for-amd-matisse-hd-audio-usb-3.0.patch b/queue-4.14/pci-avoid-flr-for-amd-matisse-hd-audio-usb-3.0.patch
new file mode 100644 (file)
index 0000000..7dcc781
--- /dev/null
@@ -0,0 +1,62 @@
+From 468337a4389057fd9735ac1746daf91738c98cbb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2020 18:23:30 -0500
+Subject: PCI: Avoid FLR for AMD Matisse HD Audio & USB 3.0
+
+From: Marcos Scriven <marcos@scriven.org>
+
+[ Upstream commit 0d14f06cd6657ba3446a5eb780672da487b068e7 ]
+
+The AMD Matisse HD Audio & USB 3.0 devices advertise Function Level Reset
+support, but hang when an FLR is triggered.
+
+To reproduce the problem, attach the device to a VM, then detach and try to
+attach again.
+
+Rename the existing quirk_intel_no_flr(), which was not Intel-specific, to
+quirk_no_flr(), and apply it to prevent the use of FLR on these AMD
+devices.
+
+Link: https://lore.kernel.org/r/CAAri2DpkcuQZYbT6XsALhx2e6vRqPHwtbjHYeiH7MNp4zmt1RA@mail.gmail.com
+Signed-off-by: Marcos Scriven <marcos@scriven.org>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pci/quirks.c | 18 ++++++++++++++----
+ 1 file changed, 14 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
+index c751f2f81142..8d01c6d372fe 100644
+--- a/drivers/pci/quirks.c
++++ b/drivers/pci/quirks.c
+@@ -4869,13 +4869,23 @@ static void quirk_intel_qat_vf_cap(struct pci_dev *pdev)
+ }
+ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x443, quirk_intel_qat_vf_cap);
+-/* FLR may cause some 82579 devices to hang. */
+-static void quirk_intel_no_flr(struct pci_dev *dev)
++/*
++ * FLR may cause the following to devices to hang:
++ *
++ * AMD Starship/Matisse HD Audio Controller 0x1487
++ * AMD Matisse USB 3.0 Host Controller 0x149c
++ * Intel 82579LM Gigabit Ethernet Controller 0x1502
++ * Intel 82579V Gigabit Ethernet Controller 0x1503
++ *
++ */
++static void quirk_no_flr(struct pci_dev *dev)
+ {
+       dev->dev_flags |= PCI_DEV_FLAGS_NO_FLR_RESET;
+ }
+-DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1502, quirk_intel_no_flr);
+-DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1503, quirk_intel_no_flr);
++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x1487, quirk_no_flr);
++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x149c, quirk_no_flr);
++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1502, quirk_no_flr);
++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1503, quirk_no_flr);
+ static void quirk_no_ext_tags(struct pci_dev *pdev)
+ {
+-- 
+2.25.1
+
diff --git a/queue-4.14/pci-avoid-flr-for-amd-starship-usb-3.0.patch b/queue-4.14/pci-avoid-flr-for-amd-starship-usb-3.0.patch
new file mode 100644 (file)
index 0000000..646cd83
--- /dev/null
@@ -0,0 +1,69 @@
+From 6a5f6cec3b2fdde3e3b8d5c8072921f7a256a600 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 24 May 2020 00:35:29 -0700
+Subject: PCI: Avoid FLR for AMD Starship USB 3.0
+
+From: Kevin Buettner <kevinb@redhat.com>
+
+[ Upstream commit 5727043c73fdfe04597971b5f3f4850d879c1f4f ]
+
+The AMD Starship USB 3.0 host controller advertises Function Level Reset
+support, but it apparently doesn't work.  Add a quirk to prevent use of FLR
+on this device.
+
+Without this quirk, when attempting to assign (pass through) an AMD
+Starship USB 3.0 host controller to a guest OS, the system becomes
+increasingly unresponsive over the course of several minutes, eventually
+requiring a hard reset.  Shortly after attempting to start the guest, I see
+these messages:
+
+  vfio-pci 0000:05:00.3: not ready 1023ms after FLR; waiting
+  vfio-pci 0000:05:00.3: not ready 2047ms after FLR; waiting
+  vfio-pci 0000:05:00.3: not ready 4095ms after FLR; waiting
+  vfio-pci 0000:05:00.3: not ready 8191ms after FLR; waiting
+
+And then eventually:
+
+  vfio-pci 0000:05:00.3: not ready 65535ms after FLR; giving up
+  INFO: NMI handler (perf_event_nmi_handler) took too long to run: 0.000 msecs
+  perf: interrupt took too long (642744 > 2500), lowering kernel.perf_event_max_sample_rate to 1000
+  INFO: NMI handler (perf_event_nmi_handler) took too long to run: 82.270 msecs
+  INFO: NMI handler (perf_event_nmi_handler) took too long to run: 680.608 msecs
+  INFO: NMI handler (perf_event_nmi_handler) took too long to run: 100.952 msecs
+  ...
+  watchdog: BUG: soft lockup - CPU#3 stuck for 22s! [qemu-system-x86:7487]
+
+Tested on a Micro-Star International Co., Ltd. MS-7C59/Creator TRX40
+motherboard with an AMD Ryzen Threadripper 3970X.
+
+Link: https://lore.kernel.org/r/20200524003529.598434ff@f31-4.lan
+Signed-off-by: Kevin Buettner <kevinb@redhat.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pci/quirks.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
+index 8d01c6d372fe..1bc7d4bcfea4 100644
+--- a/drivers/pci/quirks.c
++++ b/drivers/pci/quirks.c
+@@ -4873,6 +4873,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x443, quirk_intel_qat_vf_cap);
+  * FLR may cause the following to devices to hang:
+  *
+  * AMD Starship/Matisse HD Audio Controller 0x1487
++ * AMD Starship USB 3.0 Host Controller 0x148c
+  * AMD Matisse USB 3.0 Host Controller 0x149c
+  * Intel 82579LM Gigabit Ethernet Controller 0x1502
+  * Intel 82579V Gigabit Ethernet Controller 0x1503
+@@ -4883,6 +4884,7 @@ static void quirk_no_flr(struct pci_dev *dev)
+       dev->dev_flags |= PCI_DEV_FLAGS_NO_FLR_RESET;
+ }
+ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x1487, quirk_no_flr);
++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x148c, quirk_no_flr);
+ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x149c, quirk_no_flr);
+ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1502, quirk_no_flr);
+ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1503, quirk_no_flr);
+-- 
+2.25.1
+
diff --git a/queue-4.14/pci-disable-msi-for-freescale-layerscape-pcie-rc-mod.patch b/queue-4.14/pci-disable-msi-for-freescale-layerscape-pcie-rc-mod.patch
new file mode 100644 (file)
index 0000000..95c2688
--- /dev/null
@@ -0,0 +1,42 @@
+From 92ffb17260a9abdb8e6caa6c23197c0937fdfb4e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 12 Oct 2017 17:44:47 +0800
+Subject: PCI: Disable MSI for Freescale Layerscape PCIe RC mode
+
+From: Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
+
+[ Upstream commit 06dc4ee54e306eff61cbdac3593b42b09f618103 ]
+
+The Freescale PCIe controller advertises the MSI/MSI-X capability in both
+RC and Endpoint mode, but in RC mode it doesn't support MSI/MSI-X by
+itself; it can only transfer MSI/MSI-X from downstream devices.
+
+Add a quirk to prevent use of MSI/MSI-X in RC mode.
+
+Signed-off-by: Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Acked-by: Minghuan Lian <minghuan.Lian@nxp.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pci/quirks.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
+index e7ed051ec125..c751f2f81142 100644
+--- a/drivers/pci/quirks.c
++++ b/drivers/pci/quirks.c
+@@ -4912,3 +4912,11 @@ static void quirk_no_ats(struct pci_dev *pdev)
+ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x98e4, quirk_no_ats);
+ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x6900, quirk_no_ats);
+ #endif /* CONFIG_PCI_ATS */
++
++/* Freescale PCIe doesn't support MSI in RC mode */
++static void quirk_fsl_no_msi(struct pci_dev *pdev)
++{
++      if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT)
++              pdev->no_msi = 1;
++}
++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID, quirk_fsl_no_msi);
+-- 
+2.25.1
+
diff --git a/queue-4.14/pci-generalize-multi-function-power-dependency-devic.patch b/queue-4.14/pci-generalize-multi-function-power-dependency-devic.patch
new file mode 100644 (file)
index 0000000..eef8628
--- /dev/null
@@ -0,0 +1,109 @@
+From a6d309cf192cec0086d13cd004cb920546638861 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 6 Jun 2019 14:52:24 +0530
+Subject: PCI: Generalize multi-function power dependency device links
+
+From: Abhishek Sahu <abhsahu@nvidia.com>
+
+[ Upstream commit a17beb1a0882a544523dcb5d0da4801272dfd43a ]
+
+Although not allowed by the PCI specs, some multi-function devices have
+power dependencies between the functions.  For example, function 1 may not
+work unless function 0 is in the D0 power state.
+
+The existing quirk_gpu_hda() adds a device link to express this dependency
+for GPU and HDA devices, but it really is not specific to those device
+types.
+
+Generalize it and rename it to pci_create_device_link() so we can create
+dependencies between any "consumer" and "producer" functions of a
+multi-function device, where the consumer is only functional if the
+producer is in D0.  This reorganization should not affect any
+functionality.
+
+Link: https://lore.kernel.org/lkml/20190606092225.17960-2-abhsahu@nvidia.com
+Signed-off-by: Abhishek Sahu <abhsahu@nvidia.com>
+[bhelgaas: commit log, reword diagnostic]
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pci/quirks.c | 54 ++++++++++++++++++++++++++++----------------
+ 1 file changed, 34 insertions(+), 20 deletions(-)
+
+diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
+index 6af7fc0be21d..3e1a0a207734 100644
+--- a/drivers/pci/quirks.c
++++ b/drivers/pci/quirks.c
+@@ -4955,35 +4955,49 @@ static void quirk_fsl_no_msi(struct pci_dev *pdev)
+ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID, quirk_fsl_no_msi);
+ /*
+- * GPUs with integrated HDA controller for streaming audio to attached displays
+- * need a device link from the HDA controller (consumer) to the GPU (supplier)
+- * so that the GPU is powered up whenever the HDA controller is accessed.
+- * The GPU and HDA controller are functions 0 and 1 of the same PCI device.
+- * The device link stays in place until shutdown (or removal of the PCI device
+- * if it's hotplugged).  Runtime PM is allowed by default on the HDA controller
+- * to prevent it from permanently keeping the GPU awake.
++ * Although not allowed by the spec, some multi-function devices have
++ * dependencies of one function (consumer) on another (supplier).  For the
++ * consumer to work in D0, the supplier must also be in D0.  Create a
++ * device link from the consumer to the supplier to enforce this
++ * dependency.  Runtime PM is allowed by default on the consumer to prevent
++ * it from permanently keeping the supplier awake.
+  */
+-static void quirk_gpu_hda(struct pci_dev *hda)
++static void pci_create_device_link(struct pci_dev *pdev, unsigned int consumer,
++                                 unsigned int supplier, unsigned int class,
++                                 unsigned int class_shift)
+ {
+-      struct pci_dev *gpu;
++      struct pci_dev *supplier_pdev;
+-      if (PCI_FUNC(hda->devfn) != 1)
++      if (PCI_FUNC(pdev->devfn) != consumer)
+               return;
+-      gpu = pci_get_domain_bus_and_slot(pci_domain_nr(hda->bus),
+-                                        hda->bus->number,
+-                                        PCI_DEVFN(PCI_SLOT(hda->devfn), 0));
+-      if (!gpu || (gpu->class >> 16) != PCI_BASE_CLASS_DISPLAY) {
+-              pci_dev_put(gpu);
++      supplier_pdev = pci_get_domain_bus_and_slot(pci_domain_nr(pdev->bus),
++                              pdev->bus->number,
++                              PCI_DEVFN(PCI_SLOT(pdev->devfn), supplier));
++      if (!supplier_pdev || (supplier_pdev->class >> class_shift) != class) {
++              pci_dev_put(supplier_pdev);
+               return;
+       }
+-      if (!device_link_add(&hda->dev, &gpu->dev,
+-                           DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME))
+-              pci_err(hda, "cannot link HDA to GPU %s\n", pci_name(gpu));
++      if (device_link_add(&pdev->dev, &supplier_pdev->dev,
++                          DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME))
++              pci_info(pdev, "D0 power state depends on %s\n",
++                       pci_name(supplier_pdev));
++      else
++              pci_err(pdev, "Cannot enforce power dependency on %s\n",
++                      pci_name(supplier_pdev));
++
++      pm_runtime_allow(&pdev->dev);
++      pci_dev_put(supplier_pdev);
++}
+-      pm_runtime_allow(&hda->dev);
+-      pci_dev_put(gpu);
++/*
++ * Create device link for GPUs with integrated HDA controller for streaming
++ * audio to attached displays.
++ */
++static void quirk_gpu_hda(struct pci_dev *hda)
++{
++      pci_create_device_link(hda, 1, 0, PCI_BASE_CLASS_DISPLAY, 16);
+ }
+ DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_ATI, PCI_ANY_ID,
+                             PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda);
+-- 
+2.25.1
+
diff --git a/queue-4.14/pci-make-acs-quirk-implementations-more-uniform.patch b/queue-4.14/pci-make-acs-quirk-implementations-more-uniform.patch
new file mode 100644 (file)
index 0000000..94bb13c
--- /dev/null
@@ -0,0 +1,124 @@
+From 9e91e02ae51bba67384b1877263f216463da4421 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 5 Sep 2019 17:54:42 -0500
+Subject: PCI: Make ACS quirk implementations more uniform
+
+From: Bjorn Helgaas <bhelgaas@google.com>
+
+[ Upstream commit c8de8ed2dcaac82e5d76d467dc0b02e0ee79809b ]
+
+The ACS quirks differ in needless ways, which makes them look more
+different than they really are.
+
+Reorder the ACS flags in order of definitions in the spec:
+
+  PCI_ACS_SV   Source Validation
+  PCI_ACS_TB   Translation Blocking
+  PCI_ACS_RR   P2P Request Redirect
+  PCI_ACS_CR   P2P Completion Redirect
+  PCI_ACS_UF   Upstream Forwarding
+  PCI_ACS_EC   P2P Egress Control
+  PCI_ACS_DT   Direct Translated P2P
+
+(PCIe r5.0, sec 7.7.8.2) and use similar code structure in all.  No
+functional change intended.
+
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
+Reviewed-by: Alex Williamson <alex.williamson@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pci/quirks.c | 41 +++++++++++++++++++----------------------
+ 1 file changed, 19 insertions(+), 22 deletions(-)
+
+diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
+index 81d76e34b0db..44be840dac0d 100644
+--- a/drivers/pci/quirks.c
++++ b/drivers/pci/quirks.c
+@@ -4305,18 +4305,18 @@ static bool pci_quirk_cavium_acs_match(struct pci_dev *dev)
+ static int pci_quirk_cavium_acs(struct pci_dev *dev, u16 acs_flags)
+ {
++      if (!pci_quirk_cavium_acs_match(dev))
++              return -ENOTTY;
++
+       /*
+-       * Cavium root ports don't advertise an ACS capability.  However,
++       * Cavium Root Ports don't advertise an ACS capability.  However,
+        * the RTL internally implements similar protection as if ACS had
+-       * Request Redirection, Completion Redirection, Source Validation,
++       * Source Validation, Request Redirection, Completion Redirection,
+        * and Upstream Forwarding features enabled.  Assert that the
+        * hardware implements and enables equivalent ACS functionality for
+        * these flags.
+        */
+-      acs_flags &= ~(PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_SV | PCI_ACS_UF);
+-
+-      if (!pci_quirk_cavium_acs_match(dev))
+-              return -ENOTTY;
++      acs_flags &= ~(PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF);
+       return acs_flags ? 0 : 1;
+ }
+@@ -4334,7 +4334,7 @@ static int pci_quirk_xgene_acs(struct pci_dev *dev, u16 acs_flags)
+ }
+ /*
+- * Many Intel PCH root ports do provide ACS-like features to disable peer
++ * Many Intel PCH Root Ports do provide ACS-like features to disable peer
+  * transactions and validate bus numbers in requests, but do not provide an
+  * actual PCIe ACS capability.  This is the list of device IDs known to fall
+  * into that category as provided by Intel in Red Hat bugzilla 1037684.
+@@ -4382,37 +4382,34 @@ static bool pci_quirk_intel_pch_acs_match(struct pci_dev *dev)
+       return false;
+ }
+-#define INTEL_PCH_ACS_FLAGS (PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF | PCI_ACS_SV)
++#define INTEL_PCH_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF)
+ static int pci_quirk_intel_pch_acs(struct pci_dev *dev, u16 acs_flags)
+ {
+-      u16 flags = dev->dev_flags & PCI_DEV_FLAGS_ACS_ENABLED_QUIRK ?
+-                  INTEL_PCH_ACS_FLAGS : 0;
+-
+       if (!pci_quirk_intel_pch_acs_match(dev))
+               return -ENOTTY;
+-      return acs_flags & ~flags ? 0 : 1;
++      if (dev->dev_flags & PCI_DEV_FLAGS_ACS_ENABLED_QUIRK)
++              acs_flags &= ~(INTEL_PCH_ACS_FLAGS);
++
++      return acs_flags ? 0 : 1;
+ }
+ /*
+- * These QCOM root ports do provide ACS-like features to disable peer
++ * These QCOM Root Ports do provide ACS-like features to disable peer
+  * transactions and validate bus numbers in requests, but do not provide an
+  * actual PCIe ACS capability.  Hardware supports source validation but it
+  * will report the issue as Completer Abort instead of ACS Violation.
+- * Hardware doesn't support peer-to-peer and each root port is a root
+- * complex with unique segment numbers.  It is not possible for one root
+- * port to pass traffic to another root port.  All PCIe transactions are
+- * terminated inside the root port.
++ * Hardware doesn't support peer-to-peer and each Root Port is a Root
++ * Complex with unique segment numbers.  It is not possible for one Root
++ * Port to pass traffic to another Root Port.  All PCIe transactions are
++ * terminated inside the Root Port.
+  */
+ static int pci_quirk_qcom_rp_acs(struct pci_dev *dev, u16 acs_flags)
+ {
+-      u16 flags = (PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF | PCI_ACS_SV);
+-      int ret = acs_flags & ~flags ? 0 : 1;
+-
+-      dev_info(&dev->dev, "Using QCOM ACS Quirk (%d)\n", ret);
++      acs_flags &= ~(PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF);
+-      return ret;
++      return acs_flags ? 0 : 1;
+ }
+ /*
+-- 
+2.25.1
+
diff --git a/queue-4.14/pci-unify-acs-quirk-desired-vs-provided-checking.patch b/queue-4.14/pci-unify-acs-quirk-desired-vs-provided-checking.patch
new file mode 100644 (file)
index 0000000..d18f75e
--- /dev/null
@@ -0,0 +1,177 @@
+From be6b1a52e610d5e4dc15ee8d4072862465ef0bcf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 6 Sep 2019 18:36:06 -0500
+Subject: PCI: Unify ACS quirk desired vs provided checking
+
+From: Bjorn Helgaas <bhelgaas@google.com>
+
+[ Upstream commit 7cf2cba43f15c74bac46dc5f0326805d25ef514d ]
+
+Most of the ACS quirks have a similar pattern of:
+
+  acs_flags &= ~( <controls provided by this device> );
+  return acs_flags ? 0 : 1;
+
+Pull this out into a helper function to simplify the quirks slightly.  The
+helper function is also a convenient place for comments about what the list
+of ACS controls means.  No functional change intended.
+
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
+Reviewed-by: Alex Williamson <alex.williamson@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pci/quirks.c | 67 +++++++++++++++++++++++++++++---------------
+ 1 file changed, 45 insertions(+), 22 deletions(-)
+
+diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
+index 0472e69833c8..5f26c170315c 100644
+--- a/drivers/pci/quirks.c
++++ b/drivers/pci/quirks.c
+@@ -4236,6 +4236,24 @@ static void quirk_chelsio_T5_disable_root_port_attributes(struct pci_dev *pdev)
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID,
+                        quirk_chelsio_T5_disable_root_port_attributes);
++/*
++ * pci_acs_ctrl_enabled - compare desired ACS controls with those provided
++ *                      by a device
++ * @acs_ctrl_req: Bitmask of desired ACS controls
++ * @acs_ctrl_ena: Bitmask of ACS controls enabled or provided implicitly by
++ *              the hardware design
++ *
++ * Return 1 if all ACS controls in the @acs_ctrl_req bitmask are included
++ * in @acs_ctrl_ena, i.e., the device provides all the access controls the
++ * caller desires.  Return 0 otherwise.
++ */
++static int pci_acs_ctrl_enabled(u16 acs_ctrl_req, u16 acs_ctrl_ena)
++{
++      if ((acs_ctrl_req & acs_ctrl_ena) == acs_ctrl_req)
++              return 1;
++      return 0;
++}
++
+ /*
+  * AMD has indicated that the devices below do not support peer-to-peer
+  * in any system where they are found in the southbridge with an AMD
+@@ -4279,7 +4297,7 @@ static int pci_quirk_amd_sb_acs(struct pci_dev *dev, u16 acs_flags)
+       /* Filter out flags not applicable to multifunction */
+       acs_flags &= (PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC | PCI_ACS_DT);
+-      return acs_flags & ~(PCI_ACS_RR | PCI_ACS_CR) ? 0 : 1;
++      return pci_acs_ctrl_enabled(acs_flags, PCI_ACS_RR | PCI_ACS_CR);
+ #else
+       return -ENODEV;
+ #endif
+@@ -4317,9 +4335,8 @@ static int pci_quirk_cavium_acs(struct pci_dev *dev, u16 acs_flags)
+        * hardware implements and enables equivalent ACS functionality for
+        * these flags.
+        */
+-      acs_flags &= ~(PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF);
+-
+-      return acs_flags ? 0 : 1;
++      return pci_acs_ctrl_enabled(acs_flags,
++              PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF);
+ }
+ static int pci_quirk_xgene_acs(struct pci_dev *dev, u16 acs_flags)
+@@ -4329,9 +4346,8 @@ static int pci_quirk_xgene_acs(struct pci_dev *dev, u16 acs_flags)
+        * transactions with others, allowing masking out these bits as if they
+        * were unimplemented in the ACS capability.
+        */
+-      acs_flags &= ~(PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF);
+-
+-      return acs_flags ? 0 : 1;
++      return pci_acs_ctrl_enabled(acs_flags,
++              PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF);
+ }
+ /*
+@@ -4383,17 +4399,16 @@ static bool pci_quirk_intel_pch_acs_match(struct pci_dev *dev)
+       return false;
+ }
+-#define INTEL_PCH_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF)
+-
+ static int pci_quirk_intel_pch_acs(struct pci_dev *dev, u16 acs_flags)
+ {
+       if (!pci_quirk_intel_pch_acs_match(dev))
+               return -ENOTTY;
+       if (dev->dev_flags & PCI_DEV_FLAGS_ACS_ENABLED_QUIRK)
+-              acs_flags &= ~(INTEL_PCH_ACS_FLAGS);
++              return pci_acs_ctrl_enabled(acs_flags,
++                      PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF);
+-      return acs_flags ? 0 : 1;
++      return pci_acs_ctrl_enabled(acs_flags, 0);
+ }
+ /*
+@@ -4408,9 +4423,8 @@ static int pci_quirk_intel_pch_acs(struct pci_dev *dev, u16 acs_flags)
+  */
+ static int pci_quirk_qcom_rp_acs(struct pci_dev *dev, u16 acs_flags)
+ {
+-      acs_flags &= ~(PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF);
+-
+-      return acs_flags ? 0 : 1;
++      return pci_acs_ctrl_enabled(acs_flags,
++              PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF);
+ }
+ /*
+@@ -4493,7 +4507,7 @@ static int pci_quirk_intel_spt_pch_acs(struct pci_dev *dev, u16 acs_flags)
+       pci_read_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, &ctrl);
+-      return acs_flags & ~ctrl ? 0 : 1;
++      return pci_acs_ctrl_enabled(acs_flags, ctrl);
+ }
+ static int pci_quirk_mf_endpoint_acs(struct pci_dev *dev, u16 acs_flags)
+@@ -4507,10 +4521,9 @@ static int pci_quirk_mf_endpoint_acs(struct pci_dev *dev, u16 acs_flags)
+        * perform peer-to-peer with other functions, allowing us to mask out
+        * these bits as if they were unimplemented in the ACS capability.
+        */
+-      acs_flags &= ~(PCI_ACS_SV | PCI_ACS_TB | PCI_ACS_RR |
+-                     PCI_ACS_CR | PCI_ACS_UF | PCI_ACS_DT);
+-
+-      return acs_flags ? 0 : 1;
++      return pci_acs_ctrl_enabled(acs_flags,
++              PCI_ACS_SV | PCI_ACS_TB | PCI_ACS_RR |
++              PCI_ACS_CR | PCI_ACS_UF | PCI_ACS_DT);
+ }
+ static int pci_quirk_rciep_acs(struct pci_dev *dev, u16 acs_flags)
+@@ -4535,9 +4548,8 @@ static int pci_quirk_brcm_acs(struct pci_dev *dev, u16 acs_flags)
+        * Allow each Root Port to be in a separate IOMMU group by masking
+        * SV/RR/CR/UF bits.
+        */
+-      acs_flags &= ~(PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF);
+-
+-      return acs_flags ? 0 : 1;
++      return pci_acs_ctrl_enabled(acs_flags,
++              PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF);
+ }
+ static const struct pci_dev_acs_enabled {
+@@ -4636,6 +4648,17 @@ static const struct pci_dev_acs_enabled {
+       { 0 }
+ };
++/*
++ * pci_dev_specific_acs_enabled - check whether device provides ACS controls
++ * @dev:      PCI device
++ * @acs_flags:        Bitmask of desired ACS controls
++ *
++ * Returns:
++ *   -ENOTTY: No quirk applies to this device; we can't tell whether the
++ *            device provides the desired controls
++ *   0:               Device does not provide all the desired controls
++ *   >0:      Device provides all the controls in @acs_flags
++ */
+ int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags)
+ {
+       const struct pci_dev_acs_enabled *i;
+-- 
+2.25.1
+
index 518fc01589f4b8ca0e5de15e5e36d3557e480570..22f2915bde8250388884904f9a58be8dca6944ac 100644 (file)
@@ -145,3 +145,14 @@ evm-fix-possible-memory-leak-in-evm_calc_hmac_or_hash.patch
 ext4-fix-ext_max_extent-index-to-check-for-zeroed-eh_max.patch
 ext4-fix-error-pointer-dereference.patch
 ext4-fix-race-between-ext4_sync_parent-and-rename.patch
+pci-disable-msi-for-freescale-layerscape-pcie-rc-mod.patch
+pci-avoid-flr-for-amd-matisse-hd-audio-usb-3.0.patch
+pci-avoid-flr-for-amd-starship-usb-3.0.patch
+pci-add-acs-quirk-for-iproc-paxb.patch
+pci-add-acs-quirk-for-ampere-root-ports.patch
+pci-make-acs-quirk-implementations-more-uniform.patch
+vga_switcheroo-deduplicate-power-state-tracking.patch
+vga_switcheroo-use-device-link-for-hda-controller.patch
+pci-generalize-multi-function-power-dependency-devic.patch
+pci-add-acs-quirk-for-intel-root-complex-integrated-.patch
+pci-unify-acs-quirk-desired-vs-provided-checking.patch
diff --git a/queue-4.14/vga_switcheroo-deduplicate-power-state-tracking.patch b/queue-4.14/vga_switcheroo-deduplicate-power-state-tracking.patch
new file mode 100644 (file)
index 0000000..ec3560a
--- /dev/null
@@ -0,0 +1,156 @@
+From bbfbc2e26d5c1061004ab106951d78e9d05d2e52 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 3 Mar 2018 10:53:24 +0100
+Subject: vga_switcheroo: Deduplicate power state tracking
+
+From: Lukas Wunner <lukas@wunner.de>
+
+[ Upstream commit 8948ca1a12c9a039361bbc3e4627064153971d57 ]
+
+If DRM drivers use runtime PM, they currently notify vga_switcheroo
+whenever they ->runtime_suspend or ->runtime_resume to update
+vga_switcheroo's internal power state tracking.
+
+That's essentially a duplication of a functionality performed by the
+PM core as it already tracks the GPU's power state and vga_switcheroo
+can always query it.
+
+Introduce a new internal helper vga_switcheroo_pwr_state() which does
+just that if runtime PM is used, or falls back to vga_switcheroo's
+internal power state tracking if manual power control is used.
+Drop a redundant power state check in set_audio_state() while at it.
+
+This removes one of the two purposes of the notification mechanism
+implemented by vga_switcheroo_set_dynamic_switch().  The other one is
+power management of the audio device and we'll remove that next.
+
+Cc: Dave Airlie <airlied@redhat.com>
+Cc: Ben Skeggs <bskeggs@redhat.com>
+Cc: Takashi Iwai <tiwai@suse.de>
+Cc: Alex Deucher <alexander.deucher@amd.com>
+Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Reviewed-by: Peter Wu <peter@lekensteyn.nl>
+Tested-by: Kai Heng Feng <kai.heng.feng@canonical.com> # AMD PowerXpress
+Tested-by: Mike Lothian <mike@fireburn.co.uk>          # AMD PowerXpress
+Tested-by: Denis Lisov <dennis.lissov@gmail.com>       # Nvidia Optimus
+Tested-by: Peter Wu <peter@lekensteyn.nl>              # Nvidia Optimus
+Tested-by: Lukas Wunner <lukas@wunner.de>              # MacBook Pro
+Signed-off-by: Lukas Wunner <lukas@wunner.de>
+Link: https://patchwork.freedesktop.org/patch/msgid/0aa49d735b988aa04524a8dc339582ace33f0f94.1520068884.git.lukas@wunner.de
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/vga/vga_switcheroo.c | 35 +++++++++++++++++++++-----------
+ 1 file changed, 23 insertions(+), 12 deletions(-)
+
+diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
+index 3cd153c6d271..5da45325621c 100644
+--- a/drivers/gpu/vga/vga_switcheroo.c
++++ b/drivers/gpu/vga/vga_switcheroo.c
+@@ -92,7 +92,8 @@
+  * struct vga_switcheroo_client - registered client
+  * @pdev: client pci device
+  * @fb_info: framebuffer to which console is remapped on switching
+- * @pwr_state: current power state
++ * @pwr_state: current power state if manual power control is used.
++ *    For driver power control, call vga_switcheroo_pwr_state().
+  * @ops: client callbacks
+  * @id: client identifier. Determining the id requires the handler,
+  *    so gpus are initially assigned VGA_SWITCHEROO_UNKNOWN_ID
+@@ -406,6 +407,19 @@ bool vga_switcheroo_client_probe_defer(struct pci_dev *pdev)
+ }
+ EXPORT_SYMBOL(vga_switcheroo_client_probe_defer);
++static enum vga_switcheroo_state
++vga_switcheroo_pwr_state(struct vga_switcheroo_client *client)
++{
++      if (client->driver_power_control)
++              if (pm_runtime_enabled(&client->pdev->dev) &&
++                  pm_runtime_active(&client->pdev->dev))
++                      return VGA_SWITCHEROO_ON;
++              else
++                      return VGA_SWITCHEROO_OFF;
++      else
++              return client->pwr_state;
++}
++
+ /**
+  * vga_switcheroo_get_client_state() - obtain power state of a given client
+  * @pdev: client pci device
+@@ -425,7 +439,7 @@ enum vga_switcheroo_state vga_switcheroo_get_client_state(struct pci_dev *pdev)
+       if (!client)
+               ret = VGA_SWITCHEROO_NOT_FOUND;
+       else
+-              ret = client->pwr_state;
++              ret = vga_switcheroo_pwr_state(client);
+       mutex_unlock(&vgasr_mutex);
+       return ret;
+ }
+@@ -598,7 +612,7 @@ static int vga_switcheroo_show(struct seq_file *m, void *v)
+                          client_is_vga(client) ? "" : "-Audio",
+                          client->active ? '+' : ' ',
+                          client->driver_power_control ? "Dyn" : "",
+-                         client->pwr_state ? "Pwr" : "Off",
++                         vga_switcheroo_pwr_state(client) ? "Pwr" : "Off",
+                          pci_name(client->pdev));
+               i++;
+       }
+@@ -641,7 +655,7 @@ static void set_audio_state(enum vga_switcheroo_client_id id,
+       struct vga_switcheroo_client *client;
+       client = find_client_from_id(&vgasr_priv.clients, id | ID_BIT_AUDIO);
+-      if (client && client->pwr_state != state) {
++      if (client) {
+               client->ops->set_gpu_state(client->pdev, state);
+               client->pwr_state = state;
+       }
+@@ -656,7 +670,7 @@ static int vga_switchto_stage1(struct vga_switcheroo_client *new_client)
+       if (!active)
+               return 0;
+-      if (new_client->pwr_state == VGA_SWITCHEROO_OFF)
++      if (vga_switcheroo_pwr_state(new_client) == VGA_SWITCHEROO_OFF)
+               vga_switchon(new_client);
+       vga_set_default_device(new_client->pdev);
+@@ -695,7 +709,7 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client)
+       if (new_client->ops->reprobe)
+               new_client->ops->reprobe(new_client->pdev);
+-      if (active->pwr_state == VGA_SWITCHEROO_ON)
++      if (vga_switcheroo_pwr_state(active) == VGA_SWITCHEROO_ON)
+               vga_switchoff(active);
+       set_audio_state(new_client->id, VGA_SWITCHEROO_ON);
+@@ -940,8 +954,7 @@ EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch);
+  * command line disables it.
+  *
+  * When the driver decides to power up or down, it notifies vga_switcheroo
+- * thereof so that it can (a) power the audio device on the GPU up or down,
+- * and (b) update its internal power state representation for the device.
++ * thereof so that it can power the audio device on the GPU up or down.
+  * This is achieved by vga_switcheroo_set_dynamic_switch().
+  *
+  * After the GPU has been suspended, the handler needs to be called to cut
+@@ -985,9 +998,8 @@ static void vga_switcheroo_power_switch(struct pci_dev *pdev,
+  *
+  * Helper for GPUs whose power state is controlled by the driver's runtime pm.
+  * When the driver decides to power up or down, it notifies vga_switcheroo
+- * thereof using this helper so that it can (a) power the audio device on
+- * the GPU up or down, and (b) update its internal power state representation
+- * for the device.
++ * thereof using this helper so that it can power the audio device on the GPU
++ * up or down.
+  */
+ void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev,
+                                      enum vga_switcheroo_state dynamic)
+@@ -1001,7 +1013,6 @@ void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev,
+               return;
+       }
+-      client->pwr_state = dynamic;
+       set_audio_state(client->id, dynamic);
+       mutex_unlock(&vgasr_mutex);
+ }
+-- 
+2.25.1
+
diff --git a/queue-4.14/vga_switcheroo-use-device-link-for-hda-controller.patch b/queue-4.14/vga_switcheroo-use-device-link-for-hda-controller.patch
new file mode 100644 (file)
index 0000000..7887762
--- /dev/null
@@ -0,0 +1,594 @@
+From 9b8dc8fe5c3d49fb91dae57651c8c08f0dac8d42 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 3 Mar 2018 10:53:24 +0100
+Subject: vga_switcheroo: Use device link for HDA controller
+
+From: Lukas Wunner <lukas@wunner.de>
+
+[ Upstream commit 07f4f97d7b4bf325d9f558c5b58230387e4e57e0 ]
+
+Back in 2013, runtime PM for GPUs with integrated HDA controller was
+introduced with commits 0d69704ae348 ("gpu/vga_switcheroo: add driver
+control power feature. (v3)") and 246efa4a072f ("snd/hda: add runtime
+suspend/resume on optimus support (v4)").
+
+Briefly, the idea was that the HDA controller is forced on and off in
+unison with the GPU.
+
+The original code is mostly still in place even though it was never a
+100% perfect solution:  E.g. on access to the HDA controller, the GPU
+is powered up via vga_switcheroo_runtime_resume_hdmi_audio() but there
+are no provisions to keep it resumed until access to the HDA controller
+has ceased:  The GPU autosuspends after 5 seconds, rendering the HDA
+controller inaccessible.
+
+Additionally, a kludge is required when hda_intel.c probes:  It has to
+check whether the GPU is powered down (check_hdmi_disabled()) and defer
+probing if so.
+
+However in the meantime (in v4.10) the driver core has gained a feature
+called device links which promises to solve such issues in a clean way:
+It allows us to declare a dependency from the HDA controller (consumer)
+to the GPU (supplier).  The PM core then automagically ensures that the
+GPU is runtime resumed as long as the HDA controller's ->probe hook is
+executed and whenever the HDA controller is accessed.
+
+By default, the HDA controller has a dependency on its parent, a PCIe
+Root Port.  Adding a device link creates another dependency on its
+sibling:
+
+                            PCIe Root Port
+                             ^          ^
+                             |          |
+                             |          |
+                            HDA  ===>  GPU
+
+The device link is not only used for runtime PM, it also guarantees that
+on system sleep, the HDA controller suspends before the GPU and resumes
+after the GPU, and on system shutdown the HDA controller's ->shutdown
+hook is executed before the one of the GPU.  It is a complete solution.
+
+Using this functionality is as simple as calling device_link_add(),
+which results in a dmesg entry like this:
+
+        pci 0000:01:00.1: Linked as a consumer to 0000:01:00.0
+
+The code for the GPU-governed audio power management can thus be removed
+(except where it's still needed for legacy manual power control).
+
+The device link is added in a PCI quirk rather than in hda_intel.c.
+It is therefore legal for the GPU to runtime suspend to D3cold even if
+the HDA controller is not bound to a driver or if CONFIG_SND_HDA_INTEL
+is not enabled, for accesses to the HDA controller will cause the GPU to
+wake up regardless if they're occurring outside of hda_intel.c (think
+config space readout via sysfs).
+
+Contrary to the previous implementation, the HDA controller's power
+state is now self-governed, rather than GPU-governed, whereas the GPU's
+power state is no longer fully self-governed.  (The HDA controller needs
+to runtime suspend before the GPU can.)
+
+It is thus crucial that runtime PM is always activated on the HDA
+controller even if CONFIG_SND_HDA_POWER_SAVE_DEFAULT is set to 0 (which
+is the default), lest the GPU stays awake.  This is achieved by setting
+the auto_runtime_pm flag on every codec and the AZX_DCAPS_PM_RUNTIME
+flag on the HDA controller.
+
+A side effect is that power consumption might be reduced if the GPU is
+in use but the HDA controller is not, because the HDA controller is now
+allowed to go to D3hot.  Before, it was forced to stay in D0 as long as
+the GPU was in use.  (There is no reduction in power consumption on my
+Nvidia GK107, but there might be on other chips.)
+
+The code paths for legacy manual power control are adjusted such that
+runtime PM is disabled during power off, thereby preventing the PM core
+from resuming the HDA controller.
+
+Note that the device link is not only added on vga_switcheroo capable
+systems, but for *any* GPU with integrated HDA controller.  The idea is
+that the HDA controller streams audio via connectors located on the GPU,
+so the GPU needs to be on for the HDA controller to do anything useful.
+
+This commit implicitly fixes an unbalanced runtime PM ref upon unbind of
+hda_intel.c:  On ->probe, a runtime PM ref was previously released under
+the condition "azx_has_pm_runtime(chip) || hda->use_vga_switcheroo", but
+on ->remove a runtime PM ref was only acquired under the first of those
+conditions.  Thus, binding and unbinding the driver twice on a
+vga_switcheroo capable system caused the runtime PM refcount to drop
+below zero.  The issue is resolved because the AZX_DCAPS_PM_RUNTIME flag
+is now always set if use_vga_switcheroo is true.
+
+For more information on device links please refer to:
+https://www.kernel.org/doc/html/latest/driver-api/device_link.html
+Documentation/driver-api/device_link.rst
+
+Cc: Dave Airlie <airlied@redhat.com>
+Cc: Ben Skeggs <bskeggs@redhat.com>
+Cc: Alex Deucher <alexander.deucher@amd.com>
+Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Acked-by: Bjorn Helgaas <bhelgaas@google.com>
+Reviewed-by: Takashi Iwai <tiwai@suse.de>
+Reviewed-by: Peter Wu <peter@lekensteyn.nl>
+Tested-by: Kai Heng Feng <kai.heng.feng@canonical.com> # AMD PowerXpress
+Tested-by: Mike Lothian <mike@fireburn.co.uk>          # AMD PowerXpress
+Tested-by: Denis Lisov <dennis.lissov@gmail.com>       # Nvidia Optimus
+Tested-by: Peter Wu <peter@lekensteyn.nl>              # Nvidia Optimus
+Tested-by: Lukas Wunner <lukas@wunner.de>              # MacBook Pro
+Signed-off-by: Lukas Wunner <lukas@wunner.de>
+Link: https://patchwork.freedesktop.org/patch/msgid/51bd38360ff502a8c42b1ebf4405ee1d3f27118d.1520068884.git.lukas@wunner.de
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c |   2 -
+ drivers/gpu/drm/nouveau/nouveau_drm.c   |   2 -
+ drivers/gpu/drm/radeon/radeon_drv.c     |   2 -
+ drivers/gpu/vga/vga_switcheroo.c        | 115 ++----------------------
+ drivers/pci/quirks.c                    |  39 ++++++++
+ include/linux/pci_ids.h                 |   1 +
+ include/linux/vga_switcheroo.h          |   6 --
+ include/sound/hdaudio.h                 |   3 -
+ sound/pci/hda/hda_intel.c               |  35 +++++---
+ sound/pci/hda/hda_intel.h               |   3 -
+ 10 files changed, 72 insertions(+), 136 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+index 4894d8a87c04..ae23f7e0290c 100644
+--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+@@ -728,7 +728,6 @@ static int amdgpu_pmops_runtime_suspend(struct device *dev)
+       drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
+       drm_kms_helper_poll_disable(drm_dev);
+-      vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
+       ret = amdgpu_device_suspend(drm_dev, false, false);
+       pci_save_state(pdev);
+@@ -765,7 +764,6 @@ static int amdgpu_pmops_runtime_resume(struct device *dev)
+       ret = amdgpu_device_resume(drm_dev, false, false);
+       drm_kms_helper_poll_enable(drm_dev);
+-      vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
+       drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
+       return 0;
+ }
+diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
+index 70a8d0b0c4f1..d00524a5d7f0 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
++++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
+@@ -754,7 +754,6 @@ nouveau_pmops_runtime_suspend(struct device *dev)
+       }
+       drm_kms_helper_poll_disable(drm_dev);
+-      vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
+       nouveau_switcheroo_optimus_dsm();
+       ret = nouveau_do_suspend(drm_dev, true);
+       pci_save_state(pdev);
+@@ -789,7 +788,6 @@ nouveau_pmops_runtime_resume(struct device *dev)
+       /* do magic */
+       nvif_mask(&device->object, 0x088488, (1 << 25), (1 << 25));
+-      vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
+       drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
+       /* Monitors may have been connected / disconnected during suspend */
+diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
+index f4becad0a78c..f6908e2f9e55 100644
+--- a/drivers/gpu/drm/radeon/radeon_drv.c
++++ b/drivers/gpu/drm/radeon/radeon_drv.c
+@@ -424,7 +424,6 @@ static int radeon_pmops_runtime_suspend(struct device *dev)
+       drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
+       drm_kms_helper_poll_disable(drm_dev);
+-      vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
+       ret = radeon_suspend_kms(drm_dev, false, false, false);
+       pci_save_state(pdev);
+@@ -461,7 +460,6 @@ static int radeon_pmops_runtime_resume(struct device *dev)
+       ret = radeon_resume_kms(drm_dev, false, false);
+       drm_kms_helper_poll_enable(drm_dev);
+-      vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
+       drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
+       return 0;
+ }
+diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
+index 5da45325621c..f188c85b3b7a 100644
+--- a/drivers/gpu/vga/vga_switcheroo.c
++++ b/drivers/gpu/vga/vga_switcheroo.c
+@@ -105,8 +105,7 @@
+  * @list: client list
+  *
+  * Registered client. A client can be either a GPU or an audio device on a GPU.
+- * For audio clients, the @fb_info, @active and @driver_power_control members
+- * are bogus.
++ * For audio clients, the @fb_info and @active members are bogus.
+  */
+ struct vga_switcheroo_client {
+       struct pci_dev *pdev;
+@@ -332,8 +331,8 @@ EXPORT_SYMBOL(vga_switcheroo_register_client);
+  * @ops: client callbacks
+  * @id: client identifier
+  *
+- * Register audio client (audio device on a GPU). The power state of the
+- * client is assumed to be ON. Beforehand, vga_switcheroo_client_probe_defer()
++ * Register audio client (audio device on a GPU). The client is assumed
++ * to use runtime PM. Beforehand, vga_switcheroo_client_probe_defer()
+  * shall be called to ensure that all prerequisites are met.
+  *
+  * Return: 0 on success, -ENOMEM on memory allocation error.
+@@ -342,7 +341,7 @@ int vga_switcheroo_register_audio_client(struct pci_dev *pdev,
+                       const struct vga_switcheroo_client_ops *ops,
+                       enum vga_switcheroo_client_id id)
+ {
+-      return register_client(pdev, ops, id | ID_BIT_AUDIO, false, false);
++      return register_client(pdev, ops, id | ID_BIT_AUDIO, false, true);
+ }
+ EXPORT_SYMBOL(vga_switcheroo_register_audio_client);
+@@ -655,10 +654,8 @@ static void set_audio_state(enum vga_switcheroo_client_id id,
+       struct vga_switcheroo_client *client;
+       client = find_client_from_id(&vgasr_priv.clients, id | ID_BIT_AUDIO);
+-      if (client) {
++      if (client)
+               client->ops->set_gpu_state(client->pdev, state);
+-              client->pwr_state = state;
+-      }
+ }
+ /* stage one happens before delay */
+@@ -953,10 +950,6 @@ EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch);
+  * Specifying nouveau.runpm=0, radeon.runpm=0 or amdgpu.runpm=0 on the kernel
+  * command line disables it.
+  *
+- * When the driver decides to power up or down, it notifies vga_switcheroo
+- * thereof so that it can power the audio device on the GPU up or down.
+- * This is achieved by vga_switcheroo_set_dynamic_switch().
+- *
+  * After the GPU has been suspended, the handler needs to be called to cut
+  * power to the GPU. Likewise it needs to reinstate power before the GPU
+  * can resume. This is achieved by vga_switcheroo_init_domain_pm_ops(),
+@@ -964,8 +957,9 @@ EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch);
+  * calls to the handler.
+  *
+  * When the audio device resumes, the GPU needs to be woken. This is achieved
+- * by vga_switcheroo_init_domain_pm_optimus_hdmi_audio(), which augments the
+- * audio device's resume function.
++ * by a PCI quirk which calls device_link_add() to declare a dependency on the
++ * GPU. That way, the GPU is kept awake whenever and as long as the audio
++ * device is in use.
+  *
+  * On muxed machines, if the mux is initially switched to the discrete GPU,
+  * the user ends up with a black screen when the GPU powers down after boot.
+@@ -991,33 +985,6 @@ static void vga_switcheroo_power_switch(struct pci_dev *pdev,
+       vgasr_priv.handler->power_state(client->id, state);
+ }
+-/**
+- * vga_switcheroo_set_dynamic_switch() - helper for driver power control
+- * @pdev: client pci device
+- * @dynamic: new power state
+- *
+- * Helper for GPUs whose power state is controlled by the driver's runtime pm.
+- * When the driver decides to power up or down, it notifies vga_switcheroo
+- * thereof using this helper so that it can power the audio device on the GPU
+- * up or down.
+- */
+-void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev,
+-                                     enum vga_switcheroo_state dynamic)
+-{
+-      struct vga_switcheroo_client *client;
+-
+-      mutex_lock(&vgasr_mutex);
+-      client = find_client_from_pci(&vgasr_priv.clients, pdev);
+-      if (!client || !client->driver_power_control) {
+-              mutex_unlock(&vgasr_mutex);
+-              return;
+-      }
+-
+-      set_audio_state(client->id, dynamic);
+-      mutex_unlock(&vgasr_mutex);
+-}
+-EXPORT_SYMBOL(vga_switcheroo_set_dynamic_switch);
+-
+ /* switcheroo power domain */
+ static int vga_switcheroo_runtime_suspend(struct device *dev)
+ {
+@@ -1087,69 +1054,3 @@ void vga_switcheroo_fini_domain_pm_ops(struct device *dev)
+       dev_pm_domain_set(dev, NULL);
+ }
+ EXPORT_SYMBOL(vga_switcheroo_fini_domain_pm_ops);
+-
+-static int vga_switcheroo_runtime_resume_hdmi_audio(struct device *dev)
+-{
+-      struct pci_dev *pdev = to_pci_dev(dev);
+-      struct vga_switcheroo_client *client;
+-      struct device *video_dev = NULL;
+-      int ret;
+-
+-      /* we need to check if we have to switch back on the video
+-       * device so the audio device can come back
+-       */
+-      mutex_lock(&vgasr_mutex);
+-      list_for_each_entry(client, &vgasr_priv.clients, list) {
+-              if (PCI_SLOT(client->pdev->devfn) == PCI_SLOT(pdev->devfn) &&
+-                  client_is_vga(client)) {
+-                      video_dev = &client->pdev->dev;
+-                      break;
+-              }
+-      }
+-      mutex_unlock(&vgasr_mutex);
+-
+-      if (video_dev) {
+-              ret = pm_runtime_get_sync(video_dev);
+-              if (ret && ret != 1)
+-                      return ret;
+-      }
+-      ret = dev->bus->pm->runtime_resume(dev);
+-
+-      /* put the reference for the gpu */
+-      if (video_dev) {
+-              pm_runtime_mark_last_busy(video_dev);
+-              pm_runtime_put_autosuspend(video_dev);
+-      }
+-      return ret;
+-}
+-
+-/**
+- * vga_switcheroo_init_domain_pm_optimus_hdmi_audio() - helper for driver
+- *    power control
+- * @dev: audio client device
+- * @domain: power domain
+- *
+- * Helper for GPUs whose power state is controlled by the driver's runtime pm.
+- * When the audio device resumes, the GPU needs to be woken. This helper
+- * augments the audio device's resume function to do that.
+- *
+- * Return: 0 on success, -EINVAL if no power management operations are
+- * defined for this device.
+- */
+-int
+-vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev,
+-                                               struct dev_pm_domain *domain)
+-{
+-      /* copy over all the bus versions */
+-      if (dev->bus && dev->bus->pm) {
+-              domain->ops = *dev->bus->pm;
+-              domain->ops.runtime_resume =
+-                      vga_switcheroo_runtime_resume_hdmi_audio;
+-
+-              dev_pm_domain_set(dev, domain);
+-              return 0;
+-      }
+-      dev_pm_domain_set(dev, NULL);
+-      return -EINVAL;
+-}
+-EXPORT_SYMBOL(vga_switcheroo_init_domain_pm_optimus_hdmi_audio);
+diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
+index 44be840dac0d..6af7fc0be21d 100644
+--- a/drivers/pci/quirks.c
++++ b/drivers/pci/quirks.c
+@@ -27,6 +27,7 @@
+ #include <linux/ktime.h>
+ #include <linux/mm.h>
+ #include <linux/platform_data/x86/apple.h>
++#include <linux/pm_runtime.h>
+ #include <asm/dma.h>  /* isa_dma_bridge_buggy */
+ #include "pci.h"
+@@ -4952,3 +4953,41 @@ static void quirk_fsl_no_msi(struct pci_dev *pdev)
+               pdev->no_msi = 1;
+ }
+ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID, quirk_fsl_no_msi);
++
++/*
++ * GPUs with integrated HDA controller for streaming audio to attached displays
++ * need a device link from the HDA controller (consumer) to the GPU (supplier)
++ * so that the GPU is powered up whenever the HDA controller is accessed.
++ * The GPU and HDA controller are functions 0 and 1 of the same PCI device.
++ * The device link stays in place until shutdown (or removal of the PCI device
++ * if it's hotplugged).  Runtime PM is allowed by default on the HDA controller
++ * to prevent it from permanently keeping the GPU awake.
++ */
++static void quirk_gpu_hda(struct pci_dev *hda)
++{
++      struct pci_dev *gpu;
++
++      if (PCI_FUNC(hda->devfn) != 1)
++              return;
++
++      gpu = pci_get_domain_bus_and_slot(pci_domain_nr(hda->bus),
++                                        hda->bus->number,
++                                        PCI_DEVFN(PCI_SLOT(hda->devfn), 0));
++      if (!gpu || (gpu->class >> 16) != PCI_BASE_CLASS_DISPLAY) {
++              pci_dev_put(gpu);
++              return;
++      }
++
++      if (!device_link_add(&hda->dev, &gpu->dev,
++                           DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME))
++              pci_err(hda, "cannot link HDA to GPU %s\n", pci_name(gpu));
++
++      pm_runtime_allow(&hda->dev);
++      pci_dev_put(gpu);
++}
++DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_ATI, PCI_ANY_ID,
++                            PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda);
++DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_AMD, PCI_ANY_ID,
++                            PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda);
++DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
++                            PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda);
+diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
+index bd882f51fb5f..2d036930a3cd 100644
+--- a/include/linux/pci_ids.h
++++ b/include/linux/pci_ids.h
+@@ -45,6 +45,7 @@
+ #define PCI_CLASS_MULTIMEDIA_VIDEO    0x0400
+ #define PCI_CLASS_MULTIMEDIA_AUDIO    0x0401
+ #define PCI_CLASS_MULTIMEDIA_PHONE    0x0402
++#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
+ #define PCI_CLASS_MULTIMEDIA_OTHER    0x0480
+ #define PCI_BASE_CLASS_MEMORY         0x05
+diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h
+index 960bedbdec87..77f0f0af3a71 100644
+--- a/include/linux/vga_switcheroo.h
++++ b/include/linux/vga_switcheroo.h
+@@ -168,11 +168,8 @@ int vga_switcheroo_process_delayed_switch(void);
+ bool vga_switcheroo_client_probe_defer(struct pci_dev *pdev);
+ enum vga_switcheroo_state vga_switcheroo_get_client_state(struct pci_dev *dev);
+-void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum vga_switcheroo_state dynamic);
+-
+ int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *domain);
+ void vga_switcheroo_fini_domain_pm_ops(struct device *dev);
+-int vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, struct dev_pm_domain *domain);
+ #else
+ static inline void vga_switcheroo_unregister_client(struct pci_dev *dev) {}
+@@ -192,11 +189,8 @@ static inline int vga_switcheroo_process_delayed_switch(void) { return 0; }
+ static inline bool vga_switcheroo_client_probe_defer(struct pci_dev *pdev) { return false; }
+ static inline enum vga_switcheroo_state vga_switcheroo_get_client_state(struct pci_dev *dev) { return VGA_SWITCHEROO_ON; }
+-static inline void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum vga_switcheroo_state dynamic) {}
+-
+ static inline int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *domain) { return -EINVAL; }
+ static inline void vga_switcheroo_fini_domain_pm_ops(struct device *dev) {}
+-static inline int vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, struct dev_pm_domain *domain) { return -EINVAL; }
+ #endif
+ #endif /* _LINUX_VGA_SWITCHEROO_H_ */
+diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h
+index 926ea701cdc4..5d0bf1688eba 100644
+--- a/include/sound/hdaudio.h
++++ b/include/sound/hdaudio.h
+@@ -228,9 +228,6 @@ struct hdac_io_ops {
+ #define HDA_UNSOL_QUEUE_SIZE  64
+ #define HDA_MAX_CODECS                8       /* limit by controller side */
+-/* HD Audio class code */
+-#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
+-
+ /*
+  * CORB/RIRB
+  *
+diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
+index 7779f5460715..e399c5718ee6 100644
+--- a/sound/pci/hda/hda_intel.c
++++ b/sound/pci/hda/hda_intel.c
+@@ -1282,6 +1282,7 @@ static void azx_vs_set_state(struct pci_dev *pci,
+       struct snd_card *card = pci_get_drvdata(pci);
+       struct azx *chip = card->private_data;
+       struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
++      struct hda_codec *codec;
+       bool disabled;
+       wait_for_completion(&hda->probe_wait);
+@@ -1306,8 +1307,12 @@ static void azx_vs_set_state(struct pci_dev *pci,
+               dev_info(chip->card->dev, "%s via vga_switcheroo\n",
+                        disabled ? "Disabling" : "Enabling");
+               if (disabled) {
+-                      pm_runtime_put_sync_suspend(card->dev);
+-                      azx_suspend(card->dev);
++                      list_for_each_codec(codec, &chip->bus) {
++                              pm_runtime_suspend(hda_codec_dev(codec));
++                              pm_runtime_disable(hda_codec_dev(codec));
++                      }
++                      pm_runtime_suspend(card->dev);
++                      pm_runtime_disable(card->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 */
+@@ -1318,9 +1323,12 @@ static void azx_vs_set_state(struct pci_dev *pci,
+                                        "Cannot lock devices!\n");
+               } else {
+                       snd_hda_unlock_devices(&chip->bus);
+-                      pm_runtime_get_noresume(card->dev);
+                       chip->disabled = false;
+-                      azx_resume(card->dev);
++                      pm_runtime_enable(card->dev);
++                      list_for_each_codec(codec, &chip->bus) {
++                              pm_runtime_enable(hda_codec_dev(codec));
++                              pm_runtime_resume(hda_codec_dev(codec));
++                      }
+               }
+       }
+ }
+@@ -1350,6 +1358,7 @@ static void init_vga_switcheroo(struct azx *chip)
+               dev_info(chip->card->dev,
+                        "Handle vga_switcheroo audio client\n");
+               hda->use_vga_switcheroo = 1;
++              chip->driver_caps |= AZX_DCAPS_PM_RUNTIME;
+               pci_dev_put(p);
+       }
+ }
+@@ -1375,9 +1384,6 @@ static int register_vga_switcheroo(struct azx *chip)
+               return err;
+       hda->vga_switcheroo_registered = 1;
+-      /* register as an optimus hdmi audio power domain */
+-      vga_switcheroo_init_domain_pm_optimus_hdmi_audio(chip->card->dev,
+-                                                       &hda->hdmi_pm_domain);
+       return 0;
+ }
+ #else
+@@ -1406,10 +1412,8 @@ static int azx_free(struct azx *chip)
+       if (use_vga_switcheroo(hda)) {
+               if (chip->disabled && hda->probe_continued)
+                       snd_hda_unlock_devices(&chip->bus);
+-              if (hda->vga_switcheroo_registered) {
++              if (hda->vga_switcheroo_registered)
+                       vga_switcheroo_unregister_client(chip->pci);
+-                      vga_switcheroo_fini_domain_pm_ops(chip->card->dev);
+-              }
+       }
+       if (bus->chip_init) {
+@@ -2301,6 +2305,7 @@ static int azx_probe_continue(struct azx *chip)
+       struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+       struct hdac_bus *bus = azx_bus(chip);
+       struct pci_dev *pci = chip->pci;
++      struct hda_codec *codec;
+       int dev = chip->dev_index;
+       int val;
+       int err;
+@@ -2385,6 +2390,14 @@ static int azx_probe_continue(struct azx *chip)
+       chip->running = 1;
+       azx_add_card_list(chip);
++      /*
++       * The discrete GPU cannot power down unless the HDA controller runtime
++       * suspends, so activate runtime PM on codecs even if power_save == 0.
++       */
++      if (use_vga_switcheroo(hda))
++              list_for_each_codec(codec, &chip->bus)
++                      codec->auto_runtime_pm = 1;
++
+       val = power_save;
+ #ifdef CONFIG_PM
+       if (pm_blacklist) {
+@@ -2399,7 +2412,7 @@ static int azx_probe_continue(struct azx *chip)
+       }
+ #endif /* CONFIG_PM */
+       snd_hda_set_power_save(&chip->bus, val * 1000);
+-      if (azx_has_pm_runtime(chip) || hda->use_vga_switcheroo)
++      if (azx_has_pm_runtime(chip))
+               pm_runtime_put_autosuspend(&pci->dev);
+ out_free:
+diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h
+index ff0c4d617bc1..e3a3d318d2e5 100644
+--- a/sound/pci/hda/hda_intel.h
++++ b/sound/pci/hda/hda_intel.h
+@@ -40,9 +40,6 @@ struct hda_intel {
+       unsigned int vga_switcheroo_registered:1;
+       unsigned int init_failed:1; /* delayed init failed */
+-      /* secondary power domain for hdmi audio under vga device */
+-      struct dev_pm_domain hdmi_pm_domain;
+-
+       bool need_i915_power:1; /* the hda controller needs i915 power */
+ };
+-- 
+2.25.1
+