浏览代码

Merge branch 'master' into 1254-package-for-fedora-39

Maximilian Luz 1 年之前
父节点
当前提交
adb9ece5f7
共有 68 个文件被更改,包括 8340 次插入185 次删除
  1. 0 0
      configs/surface-6.5.config
  2. 1 1
      patches/6.1/0001-surface3-oemb.patch
  3. 9 9
      patches/6.1/0002-mwifiex.patch
  4. 1 1
      patches/6.1/0003-ath10k.patch
  5. 7 7
      patches/6.1/0004-ipts.patch
  6. 2 2
      patches/6.1/0005-ithc.patch
  7. 14 14
      patches/6.1/0006-surface-sam.patch
  8. 2 2
      patches/6.1/0007-surface-sam-over-hid.patch
  9. 2 2
      patches/6.1/0008-surface-button.patch
  10. 16 16
      patches/6.1/0009-surface-typecover.patch
  11. 3 3
      patches/6.1/0010-surface-shutdown.patch
  12. 1 1
      patches/6.1/0011-surface-gpe.patch
  13. 15 15
      patches/6.1/0012-cameras.patch
  14. 2 2
      patches/6.1/0013-amd-gpio.patch
  15. 1 1
      patches/6.1/0014-rtc.patch
  16. 1 1
      patches/6.4/0001-surface3-oemb.patch
  17. 9 9
      patches/6.4/0002-mwifiex.patch
  18. 1 1
      patches/6.4/0003-ath10k.patch
  19. 7 7
      patches/6.4/0004-ipts.patch
  20. 2 2
      patches/6.4/0005-ithc.patch
  21. 2 2
      patches/6.4/0006-surface-sam-over-hid.patch
  22. 2 2
      patches/6.4/0007-surface-button.patch
  23. 16 16
      patches/6.4/0008-surface-typecover.patch
  24. 2 2
      patches/6.4/0009-surface-shutdown.patch
  25. 1 1
      patches/6.4/0010-surface-gpe.patch
  26. 25 25
      patches/6.4/0011-cameras.patch
  27. 2 2
      patches/6.4/0012-amd-gpio.patch
  28. 1 1
      patches/6.4/0013-rtc.patch
  29. 101 0
      patches/6.5/0001-surface3-oemb.patch
  30. 400 0
      patches/6.5/0002-mwifiex.patch
  31. 121 0
      patches/6.5/0003-ath10k.patch
  32. 3230 0
      patches/6.5/0004-ipts.patch
  33. 1433 0
      patches/6.5/0005-ithc.patch
  34. 49 0
      patches/6.5/0006-surface-sam.patch
  35. 335 0
      patches/6.5/0007-surface-sam-over-hid.patch
  36. 149 0
      patches/6.5/0008-surface-button.patch
  37. 574 0
      patches/6.5/0009-surface-typecover.patch
  38. 97 0
      patches/6.5/0010-surface-shutdown.patch
  39. 51 0
      patches/6.5/0011-surface-gpe.patch
  40. 1394 0
      patches/6.5/0012-cameras.patch
  41. 109 0
      patches/6.5/0013-amd-gpio.patch
  42. 109 0
      patches/6.5/0014-rtc.patch
  43. 1 1
      pkg/arch/kernel/0001-surface3-oemb.patch
  44. 1 1
      pkg/arch/kernel/0002-mwifiex.patch
  45. 1 1
      pkg/arch/kernel/0003-ath10k.patch
  46. 1 1
      pkg/arch/kernel/0004-ipts.patch
  47. 1 1
      pkg/arch/kernel/0005-ithc.patch
  48. 0 1
      pkg/arch/kernel/0006-surface-sam-over-hid.patch
  49. 1 0
      pkg/arch/kernel/0006-surface-sam.patch
  50. 0 1
      pkg/arch/kernel/0007-surface-button.patch
  51. 1 0
      pkg/arch/kernel/0007-surface-sam-over-hid.patch
  52. 1 0
      pkg/arch/kernel/0008-surface-button.patch
  53. 0 1
      pkg/arch/kernel/0008-surface-typecover.patch
  54. 0 1
      pkg/arch/kernel/0009-surface-shutdown.patch
  55. 1 0
      pkg/arch/kernel/0009-surface-typecover.patch
  56. 0 1
      pkg/arch/kernel/0010-surface-gpe.patch
  57. 1 0
      pkg/arch/kernel/0010-surface-shutdown.patch
  58. 0 1
      pkg/arch/kernel/0011-cameras.patch
  59. 1 0
      pkg/arch/kernel/0011-surface-gpe.patch
  60. 0 1
      pkg/arch/kernel/0012-amd-gpio.patch
  61. 1 0
      pkg/arch/kernel/0012-cameras.patch
  62. 1 0
      pkg/arch/kernel/0013-amd-gpio.patch
  63. 0 1
      pkg/arch/kernel/0013-rtc.patch
  64. 1 0
      pkg/arch/kernel/0014-rtc.patch
  65. 24 22
      pkg/arch/kernel/PKGBUILD
  66. 1 1
      pkg/arch/kernel/surface.config
  67. 1 1
      pkg/debian/kernel/version.conf
  68. 1 1
      pkg/fedora/kernel-surface/build-linux-surface.py

+ 0 - 0
configs/surface-6.3.config → configs/surface-6.5.config


+ 1 - 1
patches/6.1/0001-surface3-oemb.patch

@@ -1,4 +1,4 @@
-From 3f94424c9bf069313dba60486c3ab56589f106d5 Mon Sep 17 00:00:00 2001
+From 533b06004078d0d0583b1d5cfa3a4a1b5312460d Mon Sep 17 00:00:00 2001
 From: Tsuchiya Yuto <kitakar@gmail.com>
 From: Tsuchiya Yuto <kitakar@gmail.com>
 Date: Sun, 18 Oct 2020 16:42:44 +0900
 Date: Sun, 18 Oct 2020 16:42:44 +0900
 Subject: [PATCH] (surface3-oemb) add DMI matches for Surface 3 with broken DMI
 Subject: [PATCH] (surface3-oemb) add DMI matches for Surface 3 with broken DMI

+ 9 - 9
patches/6.1/0002-mwifiex.patch

@@ -1,4 +1,4 @@
-From 47a4a425ad34d3e874b403985f2ded51c7f8c654 Mon Sep 17 00:00:00 2001
+From 481bd70ec12dabae7973f44cb0389607989a13df Mon Sep 17 00:00:00 2001
 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
 Date: Tue, 3 Nov 2020 13:28:04 +0100
 Date: Tue, 3 Nov 2020 13:28:04 +0100
 Subject: [PATCH] mwifiex: Add quirk resetting the PCI bridge on MS Surface
 Subject: [PATCH] mwifiex: Add quirk resetting the PCI bridge on MS Surface
@@ -32,10 +32,10 @@ Patchset: mwifiex
  3 files changed, 31 insertions(+), 8 deletions(-)
  3 files changed, 31 insertions(+), 8 deletions(-)
 
 
 diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c
 diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c
-index 9a698a16a8f3..14687342bc81 100644
+index 6697132ecc97..f06b4ebc5bd8 100644
 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c
 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c
 +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
 +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
-@@ -1762,9 +1762,21 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
+@@ -1771,9 +1771,21 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
  static int mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter)
  static int mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter)
  {
  {
  	struct pcie_service_card *card = adapter->card;
  	struct pcie_service_card *card = adapter->card;
@@ -165,7 +165,7 @@ index d6ff964aec5b..5d30ae39d65e 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 93616c8371d139434e92055aa26f082b58d99f5a Mon Sep 17 00:00:00 2001
+From 1973ee74cf11f0e7a2806502a52a053f00564829 Mon Sep 17 00:00:00 2001
 From: Tsuchiya Yuto <kitakar@gmail.com>
 From: Tsuchiya Yuto <kitakar@gmail.com>
 Date: Sun, 4 Oct 2020 00:11:49 +0900
 Date: Sun, 4 Oct 2020 00:11:49 +0900
 Subject: [PATCH] mwifiex: pcie: disable bridge_d3 for Surface gen4+
 Subject: [PATCH] mwifiex: pcie: disable bridge_d3 for Surface gen4+
@@ -187,10 +187,10 @@ Patchset: mwifiex
  3 files changed, 27 insertions(+), 8 deletions(-)
  3 files changed, 27 insertions(+), 8 deletions(-)
 
 
 diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c
 diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c
-index 14687342bc81..5e1a341f63df 100644
+index f06b4ebc5bd8..07f13b52ddb9 100644
 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c
 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c
 +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
 +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
-@@ -368,6 +368,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
+@@ -370,6 +370,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
  					const struct pci_device_id *ent)
  					const struct pci_device_id *ent)
  {
  {
  	struct pcie_service_card *card;
  	struct pcie_service_card *card;
@@ -198,7 +198,7 @@ index 14687342bc81..5e1a341f63df 100644
  	int ret;
  	int ret;
  
  
  	pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n",
  	pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n",
-@@ -409,6 +410,12 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
+@@ -411,6 +412,12 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
  		return -1;
  		return -1;
  	}
  	}
  
  
@@ -320,7 +320,7 @@ index 5d30ae39d65e..c14eb56eb911 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From c9cfe1a0af912391a4c50500d2db58ad8a0e609d Mon Sep 17 00:00:00 2001
+From 6d0ff934cd4272113a2ee32189be952c006a9bd5 Mon Sep 17 00:00:00 2001
 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
 Date: Thu, 25 Mar 2021 11:33:02 +0100
 Date: Thu, 25 Mar 2021 11:33:02 +0100
 Subject: [PATCH] Bluetooth: btusb: Lower passive lescan interval on Marvell
 Subject: [PATCH] Bluetooth: btusb: Lower passive lescan interval on Marvell
@@ -356,7 +356,7 @@ Patchset: mwifiex
  1 file changed, 15 insertions(+)
  1 file changed, 15 insertions(+)
 
 
 diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
 diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
-index d6f405763c56..934246a42ef8 100644
+index f2062c2a28da..6c55e59790ae 100644
 --- a/drivers/bluetooth/btusb.c
 --- a/drivers/bluetooth/btusb.c
 +++ b/drivers/bluetooth/btusb.c
 +++ b/drivers/bluetooth/btusb.c
 @@ -64,6 +64,7 @@ static struct usb_driver btusb_driver;
 @@ -64,6 +64,7 @@ static struct usb_driver btusb_driver;

+ 1 - 1
patches/6.1/0003-ath10k.patch

@@ -1,4 +1,4 @@
-From c15d272bc62f6091e4de764136a2b6c1373916c5 Mon Sep 17 00:00:00 2001
+From 48be23752bc2354a9f43388f9798894abbd4e59e Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Sat, 27 Feb 2021 00:45:52 +0100
 Date: Sat, 27 Feb 2021 00:45:52 +0100
 Subject: [PATCH] ath10k: Add module parameters to override board files
 Subject: [PATCH] ath10k: Add module parameters to override board files

+ 7 - 7
patches/6.1/0004-ipts.patch

@@ -1,4 +1,4 @@
-From 8290af992c14704749d967be0871230dc84d7ca6 Mon Sep 17 00:00:00 2001
+From 144f46f32c68d5da59eb848461397c9bc8db87fc Mon Sep 17 00:00:00 2001
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 Date: Thu, 30 Jul 2020 13:21:53 +0200
 Date: Thu, 30 Jul 2020 13:21:53 +0200
 Subject: [PATCH] misc: mei: Add missing IPTS device IDs
 Subject: [PATCH] misc: mei: Add missing IPTS device IDs
@@ -36,7 +36,7 @@ index 5bf0d50d55a0..c13864512229 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 08c73591e888d9abeb72f2d278e076e4f1e396e2 Mon Sep 17 00:00:00 2001
+From c76106c587665117a1315b8803f92fecc85314e6 Mon Sep 17 00:00:00 2001
 From: Liban Hannan <liban.p@gmail.com>
 From: Liban Hannan <liban.p@gmail.com>
 Date: Tue, 12 Apr 2022 23:31:12 +0100
 Date: Tue, 12 Apr 2022 23:31:12 +0100
 Subject: [PATCH] iommu: ipts: use IOMMU passthrough mode for IPTS
 Subject: [PATCH] iommu: ipts: use IOMMU passthrough mode for IPTS
@@ -136,7 +136,7 @@ index d4b5d20bd6dd..3965979c7bd4 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From e7698e8624234c87edb35520b382f48d70ad3878 Mon Sep 17 00:00:00 2001
+From 6bc4e937c87f29967b4f68fff6212773398618e0 Mon Sep 17 00:00:00 2001
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 Date: Sun, 11 Dec 2022 12:00:59 +0100
 Date: Sun, 11 Dec 2022 12:00:59 +0100
 Subject: [PATCH] hid: Add support for Intel Precise Touch and Stylus
 Subject: [PATCH] hid: Add support for Intel Precise Touch and Stylus
@@ -2986,7 +2986,7 @@ index 000000000000..a314843599fc
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From e99815f09405c18ad632c16df4070cc0524dc807 Mon Sep 17 00:00:00 2001
+From d9e7fdee8f373988a6584651a67d5377338f3cf6 Mon Sep 17 00:00:00 2001
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 Date: Fri, 28 Apr 2023 15:41:12 +0200
 Date: Fri, 28 Apr 2023 15:41:12 +0200
 Subject: [PATCH] Update IPTS from module repo
 Subject: [PATCH] Update IPTS from module repo
@@ -3178,7 +3178,7 @@ index 6cbb24a8a054..e0c400f420b9 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From b1d4f0bad7ad1b8950928c4db016f27c4f4e1256 Mon Sep 17 00:00:00 2001
+From e112a61d68bff35b7760602812902ab677e6198b Mon Sep 17 00:00:00 2001
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 Date: Mon, 17 Jul 2023 18:10:43 +0200
 Date: Mon, 17 Jul 2023 18:10:43 +0200
 Subject: [PATCH] Update IPTS from module repo
 Subject: [PATCH] Update IPTS from module repo
@@ -4433,7 +4433,7 @@ index a314843599fc..1f966b8b32c4 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From d52e8b46f574bb2c570a76e907a3f11f2cab5a4a Mon Sep 17 00:00:00 2001
+From cffae5ff02f65035e21f37025073ba6096c203a7 Mon Sep 17 00:00:00 2001
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 Date: Sat, 22 Jul 2023 17:08:56 +0200
 Date: Sat, 22 Jul 2023 17:08:56 +0200
 Subject: [PATCH] Update IPTS from module repo
 Subject: [PATCH] Update IPTS from module repo
@@ -4467,7 +4467,7 @@ index e835b460aa79..7c5cbafdb279 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 04f77a5e9411e3b83d77038de8eb235a8dd94f50 Mon Sep 17 00:00:00 2001
+From 955627bcdaa6131d4f3daed1747b514787d748ad Mon Sep 17 00:00:00 2001
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 Date: Sun, 23 Jul 2023 17:55:15 +0200
 Date: Sun, 23 Jul 2023 17:55:15 +0200
 Subject: [PATCH] Update IPTS from module repo
 Subject: [PATCH] Update IPTS from module repo

+ 2 - 2
patches/6.1/0005-ithc.patch

@@ -1,4 +1,4 @@
-From e8af7e5a14c8a0b9099827d1306f44f52adf6396 Mon Sep 17 00:00:00 2001
+From e3ff629361f2fe33651d250310ef1d60ff81a467 Mon Sep 17 00:00:00 2001
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 Date: Sun, 11 Dec 2022 12:03:38 +0100
 Date: Sun, 11 Dec 2022 12:03:38 +0100
 Subject: [PATCH] iommu: intel: Disable source id verification for ITHC
 Subject: [PATCH] iommu: intel: Disable source id verification for ITHC
@@ -39,7 +39,7 @@ index 5962bb5027d0..86fa129d10d4 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 17b17c7a1f08911384fac4fffd89a8ecc08fe6aa Mon Sep 17 00:00:00 2001
+From 196c9ea58a257cefea36a7ef1e5844c8433d4058 Mon Sep 17 00:00:00 2001
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 Date: Sun, 11 Dec 2022 12:10:54 +0100
 Date: Sun, 11 Dec 2022 12:10:54 +0100
 Subject: [PATCH] hid: Add support for Intel Touch Host Controller
 Subject: [PATCH] hid: Add support for Intel Touch Host Controller

+ 14 - 14
patches/6.1/0006-surface-sam.patch

@@ -1,4 +1,4 @@
-From b1e03d23d2f1008931450828548d066a6537ee18 Mon Sep 17 00:00:00 2001
+From 99c002b9461643b0d25a9388c5c792a10a191c31 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Fri, 2 Dec 2022 23:33:20 +0100
 Date: Fri, 2 Dec 2022 23:33:20 +0100
 Subject: [PATCH] platform/surface: aggregator: Improve documentation and
 Subject: [PATCH] platform/surface: aggregator: Improve documentation and
@@ -289,7 +289,7 @@ index 45501b6e54e8..5c4ae1a26183 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 1f72a4ee3e98475a0ca1e30c8e71a0153ed5e1e2 Mon Sep 17 00:00:00 2001
+From d6a03bbff4b123f55c4eb0884ffaf84e45b9a485 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Fri, 2 Dec 2022 23:33:21 +0100
 Date: Fri, 2 Dec 2022 23:33:21 +0100
 Subject: [PATCH] platform/surface: aggregator: Add target and source IDs to
 Subject: [PATCH] platform/surface: aggregator: Add target and source IDs to
@@ -486,7 +486,7 @@ index 2a2c17771d01..55cc61bba1da 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 267faa983a46c8d4136851178857a494a98fa1b4 Mon Sep 17 00:00:00 2001
+From 83193354125ed66b8ad43397ef2b78a8d28d9213 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Fri, 2 Dec 2022 23:33:22 +0100
 Date: Fri, 2 Dec 2022 23:33:22 +0100
 Subject: [PATCH] platform/surface: aggregator_hub: Use target-ID enum instead
 Subject: [PATCH] platform/surface: aggregator_hub: Use target-ID enum instead
@@ -529,7 +529,7 @@ index 43061514be38..62f27cdb6ca8 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From da102a3aae53f4795a1d6b41c7373c04fb6517f2 Mon Sep 17 00:00:00 2001
+From 13840a13e63a50b6f0d11e7c00b72a7192926ef7 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Fri, 2 Dec 2022 23:33:23 +0100
 Date: Fri, 2 Dec 2022 23:33:23 +0100
 Subject: [PATCH] platform/surface: aggregator_tabletsw: Use target-ID enum
 Subject: [PATCH] platform/surface: aggregator_tabletsw: Use target-ID enum
@@ -581,7 +581,7 @@ index af8b547cffdc..c8ecbdbb516c 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 17d2e5ad1b23a71acb222da2c9aa4c873602d664 Mon Sep 17 00:00:00 2001
+From d75a163071e91be8b33d2c9a8a3afff8c5161d95 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Fri, 2 Dec 2022 23:33:24 +0100
 Date: Fri, 2 Dec 2022 23:33:24 +0100
 Subject: [PATCH] platform/surface: dtx: Use target-ID enum instead of
 Subject: [PATCH] platform/surface: dtx: Use target-ID enum instead of
@@ -679,7 +679,7 @@ index ed36944467f9..0de76a784a35 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 839ba652d0b4aef5ecc4a9d46bb4d7bc2074f3ae Mon Sep 17 00:00:00 2001
+From 874bb176e9cc21213d97cb74fc7a5ea386040be4 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Fri, 2 Dec 2022 23:33:25 +0100
 Date: Fri, 2 Dec 2022 23:33:25 +0100
 Subject: [PATCH] HID: surface-hid: Use target-ID enum instead of hard-coding
 Subject: [PATCH] HID: surface-hid: Use target-ID enum instead of hard-coding
@@ -713,7 +713,7 @@ index 0635341bc517..42933bf3e925 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From a4766c51764191c1eb631ec14723bf7da61a2b3d Mon Sep 17 00:00:00 2001
+From 255b897c494c8097c4789c6f177f76b9d0e365df Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Fri, 2 Dec 2022 23:33:26 +0100
 Date: Fri, 2 Dec 2022 23:33:26 +0100
 Subject: [PATCH] platform/surface: aggregator: Enforce use of target-ID enum
 Subject: [PATCH] platform/surface: aggregator: Enforce use of target-ID enum
@@ -949,7 +949,7 @@ index 46c45d1b6368..4da20b7a0ee5 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 1d54fae05c030c2d11eebdab3edfec591e22df70 Mon Sep 17 00:00:00 2001
+From 99ac252a4a33a916c53a006274c45755eb971be0 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Fri, 2 Dec 2022 23:33:27 +0100
 Date: Fri, 2 Dec 2022 23:33:27 +0100
 Subject: [PATCH] platform/surface: aggregator_registry: Fix target-ID of
 Subject: [PATCH] platform/surface: aggregator_registry: Fix target-ID of
@@ -1004,7 +1004,7 @@ index 023f126121d7..296f72d52e6a 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From be85cd44f0f6758d7d1aa2f2b395b07ce3e93a1c Mon Sep 17 00:00:00 2001
+From 7fefc5a871cb38b915d72da4012329e71d03279b Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Tue, 20 Dec 2022 18:56:08 +0100
 Date: Tue, 20 Dec 2022 18:56:08 +0100
 Subject: [PATCH] platform/surface: aggregator: Rename top-level request
 Subject: [PATCH] platform/surface: aggregator: Rename top-level request
@@ -1551,7 +1551,7 @@ index 4da20b7a0ee5..1545e5567b15 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 4145e07216b6717344a07020f6a1e339688e913e Mon Sep 17 00:00:00 2001
+From 8ea2a4dd15d39c2e0a69124c5b1e3bec75ea7fc7 Mon Sep 17 00:00:00 2001
 From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
 From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
 Date: Wed, 18 Jan 2023 11:38:23 +0200
 Date: Wed, 18 Jan 2023 11:38:23 +0200
 Subject: [PATCH] platform/surface: Switch to use acpi_evaluate_dsm_typed()
 Subject: [PATCH] platform/surface: Switch to use acpi_evaluate_dsm_typed()
@@ -1607,7 +1607,7 @@ index f004a2495201..7b6d887dccdb 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 5dd79113c76e9d8e2d5b84453c4b814de2b58154 Mon Sep 17 00:00:00 2001
+From 82d67a3479c5f06aedc41c9510fbf37368528658 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Sat, 4 Mar 2023 20:09:36 +0100
 Date: Sat, 4 Mar 2023 20:09:36 +0100
 Subject: [PATCH] platform/surface: aggregator_tabletsw: Properly handle
 Subject: [PATCH] platform/surface: aggregator_tabletsw: Properly handle
@@ -1915,7 +1915,7 @@ index a18e9fc7896b..f9a58db6afde 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 2713284df9abcf28eceb9d90b00cafd515b535be Mon Sep 17 00:00:00 2001
+From 5e4d7cd2534dccbefed85866f9fd442d4b3a514b Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Sun, 19 Feb 2023 23:33:43 +0100
 Date: Sun, 19 Feb 2023 23:33:43 +0100
 Subject: [PATCH] platform/surface: aggregator_tabletsw: Add support for
 Subject: [PATCH] platform/surface: aggregator_tabletsw: Add support for
@@ -2032,7 +2032,7 @@ index f9a58db6afde..4a029f5db20a 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 76d7a7348fbb41558bccc73192f37d7e6a6475eb Mon Sep 17 00:00:00 2001
+From f768876f014e40dd2badba934e831e988b7c0a17 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Sun, 19 Feb 2023 23:41:18 +0100
 Date: Sun, 19 Feb 2023 23:41:18 +0100
 Subject: [PATCH] platform/surface: aggregator_registry: Add support for
 Subject: [PATCH] platform/surface: aggregator_registry: Add support for
@@ -2063,7 +2063,7 @@ index 296f72d52e6a..0fe5be539652 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From a334e2e9accafcb3d71f950219625dd985c6c64a Mon Sep 17 00:00:00 2001
+From 7dc06d041ad3da2b7aa3c2e67deb6989364ea353 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Thu, 18 May 2023 22:57:17 +0200
 Date: Thu, 18 May 2023 22:57:17 +0200
 Subject: [PATCH] platform/surface: aggregator_tabletsw: Add support for book
 Subject: [PATCH] platform/surface: aggregator_tabletsw: Add support for book

+ 2 - 2
patches/6.1/0007-surface-sam-over-hid.patch

@@ -1,4 +1,4 @@
-From 4267e38d64cdd54af7b4d56b3ced05f539b67735 Mon Sep 17 00:00:00 2001
+From 43f1492ac17d385fc171319e6a7f9b6711e2bb16 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Sat, 25 Jul 2020 17:19:53 +0200
 Date: Sat, 25 Jul 2020 17:19:53 +0200
 Subject: [PATCH] i2c: acpi: Implement RawBytes read access
 Subject: [PATCH] i2c: acpi: Implement RawBytes read access
@@ -110,7 +110,7 @@ index 4dd777cc0c89..b2338618163a 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 090ac4c9697262d85323d1c8a7749cf4ba1974f1 Mon Sep 17 00:00:00 2001
+From 3de5f7c6d3bb168ee7ac4cdfe133b09193880c1b Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Sat, 13 Feb 2021 16:41:18 +0100
 Date: Sat, 13 Feb 2021 16:41:18 +0100
 Subject: [PATCH] platform/surface: Add driver for Surface Book 1 dGPU switch
 Subject: [PATCH] platform/surface: Add driver for Surface Book 1 dGPU switch

+ 2 - 2
patches/6.1/0008-surface-button.patch

@@ -1,4 +1,4 @@
-From 8c009dd1fc82739f207820a0f251266b8c6f8b49 Mon Sep 17 00:00:00 2001
+From fd45df50389cbbed25f57fff41ac5dbca5de61cd Mon Sep 17 00:00:00 2001
 From: Sachi King <nakato@nakato.io>
 From: Sachi King <nakato@nakato.io>
 Date: Tue, 5 Oct 2021 00:05:09 +1100
 Date: Tue, 5 Oct 2021 00:05:09 +1100
 Subject: [PATCH] Input: soc_button_array - support AMD variant Surface devices
 Subject: [PATCH] Input: soc_button_array - support AMD variant Surface devices
@@ -75,7 +75,7 @@ index e79f5497948b..2bddbe6e9ea4 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 1f6dbbdfdb055776ab657b3e696031417ceedb9d Mon Sep 17 00:00:00 2001
+From 1bdbf40a89e2d366c7001f9d09fad3cea99b4632 Mon Sep 17 00:00:00 2001
 From: Sachi King <nakato@nakato.io>
 From: Sachi King <nakato@nakato.io>
 Date: Tue, 5 Oct 2021 00:22:57 +1100
 Date: Tue, 5 Oct 2021 00:22:57 +1100
 Subject: [PATCH] platform/surface: surfacepro3_button: don't load on amd
 Subject: [PATCH] platform/surface: surfacepro3_button: don't load on amd

+ 16 - 16
patches/6.1/0009-surface-typecover.patch

@@ -1,4 +1,4 @@
-From ec55618600e50ba996100f1d4a1c155592a89c99 Mon Sep 17 00:00:00 2001
+From 373b5746b6a695b182f1ede748c636677d156470 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Sat, 18 Feb 2023 01:02:49 +0100
 Date: Sat, 18 Feb 2023 01:02:49 +0100
 Subject: [PATCH] USB: quirks: Add USB_QUIRK_DELAY_INIT for Surface Go 3
 Subject: [PATCH] USB: quirks: Add USB_QUIRK_DELAY_INIT for Surface Go 3
@@ -39,7 +39,7 @@ index 15e9bd180a1d..0d70461d01e1 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 655fdf62cc5fc7c684c9337c734ba8e57d1f4ded Mon Sep 17 00:00:00 2001
+From 14559e36d36056ceab943fb768ae5c3e4230769d Mon Sep 17 00:00:00 2001
 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
 Date: Thu, 5 Nov 2020 13:09:45 +0100
 Date: Thu, 5 Nov 2020 13:09:45 +0100
 Subject: [PATCH] hid/multitouch: Turn off Type Cover keyboard backlight when
 Subject: [PATCH] hid/multitouch: Turn off Type Cover keyboard backlight when
@@ -75,7 +75,7 @@ Patchset: surface-typecover
  1 file changed, 98 insertions(+), 2 deletions(-)
  1 file changed, 98 insertions(+), 2 deletions(-)
 
 
 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
-index e31be0cb8b85..63fd042aba6b 100644
+index 521b2ffb4244..c8f3d05c8866 100644
 --- a/drivers/hid/hid-multitouch.c
 --- a/drivers/hid/hid-multitouch.c
 +++ b/drivers/hid/hid-multitouch.c
 +++ b/drivers/hid/hid-multitouch.c
 @@ -34,7 +34,10 @@
 @@ -34,7 +34,10 @@
@@ -147,7 +147,7 @@ index e31be0cb8b85..63fd042aba6b 100644
  	{ }
  	{ }
  };
  };
  
  
-@@ -1728,6 +1748,69 @@ static void mt_expired_timeout(struct timer_list *t)
+@@ -1721,6 +1741,69 @@ static void mt_expired_timeout(struct timer_list *t)
  	clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
  	clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
  }
  }
  
  
@@ -217,7 +217,7 @@ index e31be0cb8b85..63fd042aba6b 100644
  static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
  static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
  {
  {
  	int ret, i;
  	int ret, i;
-@@ -1751,6 +1834,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
+@@ -1744,6 +1827,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
  	td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN;
  	td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN;
  	hid_set_drvdata(hdev, td);
  	hid_set_drvdata(hdev, td);
  
  
@@ -227,7 +227,7 @@ index e31be0cb8b85..63fd042aba6b 100644
  	INIT_LIST_HEAD(&td->applications);
  	INIT_LIST_HEAD(&td->applications);
  	INIT_LIST_HEAD(&td->reports);
  	INIT_LIST_HEAD(&td->reports);
  
  
-@@ -1789,15 +1875,19 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
+@@ -1782,15 +1868,19 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
  	timer_setup(&td->release_timer, mt_expired_timeout, 0);
  	timer_setup(&td->release_timer, mt_expired_timeout, 0);
  
  
  	ret = hid_parse(hdev);
  	ret = hid_parse(hdev);
@@ -249,7 +249,7 @@ index e31be0cb8b85..63fd042aba6b 100644
  
  
  	ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group);
  	ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group);
  	if (ret)
  	if (ret)
-@@ -1849,6 +1939,7 @@ static void mt_remove(struct hid_device *hdev)
+@@ -1842,6 +1932,7 @@ static void mt_remove(struct hid_device *hdev)
  {
  {
  	struct mt_device *td = hid_get_drvdata(hdev);
  	struct mt_device *td = hid_get_drvdata(hdev);
  
  
@@ -257,7 +257,7 @@ index e31be0cb8b85..63fd042aba6b 100644
  	del_timer_sync(&td->release_timer);
  	del_timer_sync(&td->release_timer);
  
  
  	sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
  	sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
-@@ -2226,6 +2317,11 @@ static const struct hid_device_id mt_devices[] = {
+@@ -2219,6 +2310,11 @@ static const struct hid_device_id mt_devices[] = {
  		MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
  		MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
  			USB_DEVICE_ID_XIROKU_CSR2) },
  			USB_DEVICE_ID_XIROKU_CSR2) },
  
  
@@ -272,7 +272,7 @@ index e31be0cb8b85..63fd042aba6b 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 60afd1e9ab313862c9682f84e852697744e593f4 Mon Sep 17 00:00:00 2001
+From ea49e87b10c6b0bf457c98a9d966c0e4792e3fdd Mon Sep 17 00:00:00 2001
 From: PJungkamp <p.jungkamp@gmail.com>
 From: PJungkamp <p.jungkamp@gmail.com>
 Date: Fri, 25 Feb 2022 12:04:25 +0100
 Date: Fri, 25 Feb 2022 12:04:25 +0100
 Subject: [PATCH] hid/multitouch: Add support for surface pro type cover tablet
 Subject: [PATCH] hid/multitouch: Add support for surface pro type cover tablet
@@ -301,7 +301,7 @@ Patchset: surface-typecover
  1 file changed, 122 insertions(+), 26 deletions(-)
  1 file changed, 122 insertions(+), 26 deletions(-)
 
 
 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
-index 63fd042aba6b..508a250ff4bf 100644
+index c8f3d05c8866..1c6e4d66e762 100644
 --- a/drivers/hid/hid-multitouch.c
 --- a/drivers/hid/hid-multitouch.c
 +++ b/drivers/hid/hid-multitouch.c
 +++ b/drivers/hid/hid-multitouch.c
 @@ -77,6 +77,7 @@ MODULE_LICENSE("GPL");
 @@ -77,6 +77,7 @@ MODULE_LICENSE("GPL");
@@ -454,7 +454,7 @@ index 63fd042aba6b..508a250ff4bf 100644
  static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
  static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
  {
  {
  	struct mt_device *td = hid_get_drvdata(hdev);
  	struct mt_device *td = hid_get_drvdata(hdev);
-@@ -1660,6 +1742,13 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
+@@ -1659,6 +1741,13 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
  		/* force BTN_STYLUS to allow tablet matching in udev */
  		/* force BTN_STYLUS to allow tablet matching in udev */
  		__set_bit(BTN_STYLUS, hi->input->keybit);
  		__set_bit(BTN_STYLUS, hi->input->keybit);
  		break;
  		break;
@@ -468,7 +468,7 @@ index 63fd042aba6b..508a250ff4bf 100644
  	default:
  	default:
  		suffix = "UNKNOWN";
  		suffix = "UNKNOWN";
  		break;
  		break;
-@@ -1748,30 +1837,6 @@ static void mt_expired_timeout(struct timer_list *t)
+@@ -1741,30 +1830,6 @@ static void mt_expired_timeout(struct timer_list *t)
  	clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
  	clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
  }
  }
  
  
@@ -499,7 +499,7 @@ index 63fd042aba6b..508a250ff4bf 100644
  static void update_keyboard_backlight(struct hid_device *hdev, bool enabled)
  static void update_keyboard_backlight(struct hid_device *hdev, bool enabled)
  {
  {
  	struct usb_device *udev = hid_to_usb_dev(hdev);
  	struct usb_device *udev = hid_to_usb_dev(hdev);
-@@ -1780,8 +1845,9 @@ static void update_keyboard_backlight(struct hid_device *hdev, bool enabled)
+@@ -1773,8 +1838,9 @@ static void update_keyboard_backlight(struct hid_device *hdev, bool enabled)
  	/* Wake up the device in case it's already suspended */
  	/* Wake up the device in case it's already suspended */
  	pm_runtime_get_sync(&udev->dev);
  	pm_runtime_get_sync(&udev->dev);
  
  
@@ -511,7 +511,7 @@ index 63fd042aba6b..508a250ff4bf 100644
  		hid_err(hdev, "couldn't find backlight field\n");
  		hid_err(hdev, "couldn't find backlight field\n");
  		goto out;
  		goto out;
  	}
  	}
-@@ -1916,13 +1982,24 @@ static int mt_suspend(struct hid_device *hdev, pm_message_t state)
+@@ -1909,13 +1975,24 @@ static int mt_suspend(struct hid_device *hdev, pm_message_t state)
  
  
  static int mt_reset_resume(struct hid_device *hdev)
  static int mt_reset_resume(struct hid_device *hdev)
  {
  {
@@ -536,7 +536,7 @@ index 63fd042aba6b..508a250ff4bf 100644
  	/* Some Elan legacy devices require SET_IDLE to be set on resume.
  	/* Some Elan legacy devices require SET_IDLE to be set on resume.
  	 * It should be safe to send it to other devices too.
  	 * It should be safe to send it to other devices too.
  	 * Tested on 3M, Stantum, Cypress, Zytronic, eGalax, and Elan panels. */
  	 * Tested on 3M, Stantum, Cypress, Zytronic, eGalax, and Elan panels. */
-@@ -1931,6 +2008,10 @@ static int mt_resume(struct hid_device *hdev)
+@@ -1924,6 +2001,10 @@ static int mt_resume(struct hid_device *hdev)
  
  
  	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
  	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
  
  
@@ -547,7 +547,7 @@ index 63fd042aba6b..508a250ff4bf 100644
  	return 0;
  	return 0;
  }
  }
  #endif
  #endif
-@@ -1938,6 +2019,21 @@ static int mt_resume(struct hid_device *hdev)
+@@ -1931,6 +2012,21 @@ static int mt_resume(struct hid_device *hdev)
  static void mt_remove(struct hid_device *hdev)
  static void mt_remove(struct hid_device *hdev)
  {
  {
  	struct mt_device *td = hid_get_drvdata(hdev);
  	struct mt_device *td = hid_get_drvdata(hdev);

+ 3 - 3
patches/6.1/0010-surface-shutdown.patch

@@ -1,4 +1,4 @@
-From 1feea468e51f137c2dd38c828d9ef809c9cadafa Mon Sep 17 00:00:00 2001
+From 861a3c4fa1e4c3a80f2b024126aa9057fc6043c3 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Sun, 19 Feb 2023 22:12:24 +0100
 Date: Sun, 19 Feb 2023 22:12:24 +0100
 Subject: [PATCH] PCI: Add quirk to prevent calling shutdown mehtod
 Subject: [PATCH] PCI: Add quirk to prevent calling shutdown mehtod
@@ -81,10 +81,10 @@ index 472fa2c8ebce..6e86d7753d7d 100644
 +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x466d, quirk_no_shutdown);  // Thunderbolt 4 NHI
 +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x466d, quirk_no_shutdown);  // Thunderbolt 4 NHI
 +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x46a8, quirk_no_shutdown);  // GPU
 +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x46a8, quirk_no_shutdown);  // GPU
 diff --git a/include/linux/pci.h b/include/linux/pci.h
 diff --git a/include/linux/pci.h b/include/linux/pci.h
-index 9f617ffdb863..ca7dbe7937ae 100644
+index eccaf1abea79..f3684d9a388a 100644
 --- a/include/linux/pci.h
 --- a/include/linux/pci.h
 +++ b/include/linux/pci.h
 +++ b/include/linux/pci.h
-@@ -462,6 +462,7 @@ struct pci_dev {
+@@ -463,6 +463,7 @@ struct pci_dev {
  	unsigned int	no_vf_scan:1;		/* Don't scan for VFs after IOV enablement */
  	unsigned int	no_vf_scan:1;		/* Don't scan for VFs after IOV enablement */
  	unsigned int	no_command_memory:1;	/* No PCI_COMMAND_MEMORY */
  	unsigned int	no_command_memory:1;	/* No PCI_COMMAND_MEMORY */
  	unsigned int	rom_bar_overlap:1;	/* ROM BAR disable broken */
  	unsigned int	rom_bar_overlap:1;	/* ROM BAR disable broken */

+ 1 - 1
patches/6.1/0011-surface-gpe.patch

@@ -1,4 +1,4 @@
-From a25dee6dcd5d37ae8a6cd7a416fea0004ae6c93d Mon Sep 17 00:00:00 2001
+From abce0fa855f8205272a35fd71c77e28a93623a04 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Sun, 12 Mar 2023 01:41:57 +0100
 Date: Sun, 12 Mar 2023 01:41:57 +0100
 Subject: [PATCH] platform/surface: gpe: Add support for Surface Pro 9
 Subject: [PATCH] platform/surface: gpe: Add support for Surface Pro 9

+ 15 - 15
patches/6.1/0012-cameras.patch

@@ -1,4 +1,4 @@
-From 049c7f345a2fcfb46bf8af08db776e49345c9b65 Mon Sep 17 00:00:00 2001
+From 72862f6ca4e76322742c33608ead6d862227c16f Mon Sep 17 00:00:00 2001
 From: Hans de Goede <hdegoede@redhat.com>
 From: Hans de Goede <hdegoede@redhat.com>
 Date: Sun, 10 Oct 2021 20:56:57 +0200
 Date: Sun, 10 Oct 2021 20:56:57 +0200
 Subject: [PATCH] ACPI: delay enumeration of devices with a _DEP pointing to an
 Subject: [PATCH] ACPI: delay enumeration of devices with a _DEP pointing to an
@@ -74,7 +74,7 @@ index a0e347f6f97e..167dbf4cf623 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From c05344a730e1d9bc31c80adb6fd867e726de7e33 Mon Sep 17 00:00:00 2001
+From 16330447778bbb27b6818ba50996993ef16ee687 Mon Sep 17 00:00:00 2001
 From: zouxiaoh <xiaohong.zou@intel.com>
 From: zouxiaoh <xiaohong.zou@intel.com>
 Date: Fri, 25 Jun 2021 08:52:59 +0800
 Date: Fri, 25 Jun 2021 08:52:59 +0800
 Subject: [PATCH] iommu: intel-ipu: use IOMMU passthrough mode for Intel IPUs
 Subject: [PATCH] iommu: intel-ipu: use IOMMU passthrough mode for Intel IPUs
@@ -191,7 +191,7 @@ index 3965979c7bd4..509cfb9f6695 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 143568e6ae1aba458cdb5aa2082cad5c8a8f7234 Mon Sep 17 00:00:00 2001
+From 5fd2e3debf10d7f9c04769f40ae327f2c68358f5 Mon Sep 17 00:00:00 2001
 From: Daniel Scally <djrscally@gmail.com>
 From: Daniel Scally <djrscally@gmail.com>
 Date: Sun, 10 Oct 2021 20:57:02 +0200
 Date: Sun, 10 Oct 2021 20:57:02 +0200
 Subject: [PATCH] platform/x86: int3472: Enable I2c daisy chain
 Subject: [PATCH] platform/x86: int3472: Enable I2c daisy chain
@@ -228,7 +228,7 @@ index 5b8d1a9620a5..6a0ff035cf20 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From c600cddab0484439a9c07adb88a7794fc0f35a7b Mon Sep 17 00:00:00 2001
+From b705e4fad1be1f2474cbc0f6073303795a7c137e Mon Sep 17 00:00:00 2001
 From: Daniel Scally <djrscally@gmail.com>
 From: Daniel Scally <djrscally@gmail.com>
 Date: Thu, 28 Oct 2021 21:55:16 +0100
 Date: Thu, 28 Oct 2021 21:55:16 +0100
 Subject: [PATCH] media: i2c: Add driver for DW9719 VCM
 Subject: [PATCH] media: i2c: Add driver for DW9719 VCM
@@ -247,7 +247,7 @@ Patchset: cameras
  create mode 100644 drivers/media/i2c/dw9719.c
  create mode 100644 drivers/media/i2c/dw9719.c
 
 
 diff --git a/MAINTAINERS b/MAINTAINERS
 diff --git a/MAINTAINERS b/MAINTAINERS
-index 379387e20a96..e4170a128f8b 100644
+index 07a9c274c0e2..0ecc68379a3e 100644
 --- a/MAINTAINERS
 --- a/MAINTAINERS
 +++ b/MAINTAINERS
 +++ b/MAINTAINERS
 @@ -6286,6 +6286,13 @@ T:	git git://linuxtv.org/media_tree.git
 @@ -6286,6 +6286,13 @@ T:	git git://linuxtv.org/media_tree.git
@@ -265,10 +265,10 @@ index 379387e20a96..e4170a128f8b 100644
  M:	Dongchun Zhu <dongchun.zhu@mediatek.com>
  M:	Dongchun Zhu <dongchun.zhu@mediatek.com>
  L:	linux-media@vger.kernel.org
  L:	linux-media@vger.kernel.org
 diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
 diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
-index 7806d4b81716..98d081efeef7 100644
+index a34afb5217eb..fbaa2ce7fa3e 100644
 --- a/drivers/media/i2c/Kconfig
 --- a/drivers/media/i2c/Kconfig
 +++ b/drivers/media/i2c/Kconfig
 +++ b/drivers/media/i2c/Kconfig
-@@ -821,6 +821,17 @@ config VIDEO_DW9714
+@@ -828,6 +828,17 @@ config VIDEO_DW9714
  	  capability. This is designed for linear control of
  	  capability. This is designed for linear control of
  	  voice coil motors, controlled via I2C serial interface.
  	  voice coil motors, controlled via I2C serial interface.
  
  
@@ -732,7 +732,7 @@ index 000000000000..180b04d2a6b3
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 6a2863a6196e8cd3f9cd9fb5e65c1d44859e23bb Mon Sep 17 00:00:00 2001
+From d80f85d22ca8d52c2d2e0ad9a46b08432bd2e9ac Mon Sep 17 00:00:00 2001
 From: Daniel Scally <djrscally@gmail.com>
 From: Daniel Scally <djrscally@gmail.com>
 Date: Wed, 4 May 2022 23:21:45 +0100
 Date: Wed, 4 May 2022 23:21:45 +0100
 Subject: [PATCH] media: ipu3-cio2: Move functionality from .complete() to
 Subject: [PATCH] media: ipu3-cio2: Move functionality from .complete() to
@@ -751,7 +751,7 @@ Patchset: cameras
  1 file changed, 23 insertions(+), 42 deletions(-)
  1 file changed, 23 insertions(+), 42 deletions(-)
 
 
 diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
 diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
-index 3b76a9d0383a..38f9f4da1922 100644
+index 1bbe58b24d99..159f0d6cab8f 100644
 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
 +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
 +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
 @@ -1383,7 +1383,10 @@ static int cio2_notifier_bound(struct v4l2_async_notifier *notifier,
 @@ -1383,7 +1383,10 @@ static int cio2_notifier_bound(struct v4l2_async_notifier *notifier,
@@ -847,7 +847,7 @@ index 3b76a9d0383a..38f9f4da1922 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 772e56f054dd8c66cab75cdf55df273926eaac02 Mon Sep 17 00:00:00 2001
+From a7cfb8aaa541745c0c1118e4071d60c46a72b7f3 Mon Sep 17 00:00:00 2001
 From: Daniel Scally <djrscally@gmail.com>
 From: Daniel Scally <djrscally@gmail.com>
 Date: Thu, 2 Jun 2022 22:15:56 +0100
 Date: Thu, 2 Jun 2022 22:15:56 +0100
 Subject: [PATCH] media: ipu3-cio2: Re-add .complete() to ipu3-cio2
 Subject: [PATCH] media: ipu3-cio2: Re-add .complete() to ipu3-cio2
@@ -865,7 +865,7 @@ Patchset: cameras
  1 file changed, 9 insertions(+)
  1 file changed, 9 insertions(+)
 
 
 diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
 diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
-index 38f9f4da1922..82681df7d794 100644
+index 159f0d6cab8f..0b2abfa0c724 100644
 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
 +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
 +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
 @@ -1430,9 +1430,18 @@ static void cio2_notifier_unbind(struct v4l2_async_notifier *notifier,
 @@ -1430,9 +1430,18 @@ static void cio2_notifier_unbind(struct v4l2_async_notifier *notifier,
@@ -890,7 +890,7 @@ index 38f9f4da1922..82681df7d794 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 1f22a8024e12613dbc9963830d35f2f68dbe9228 Mon Sep 17 00:00:00 2001
+From 8eb3b743196306c303a1ae19e4223eea251f1471 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Fri, 15 Jul 2022 23:48:00 +0200
 Date: Fri, 15 Jul 2022 23:48:00 +0200
 Subject: [PATCH] drivers/media/i2c: Fix DW9719 dependencies
 Subject: [PATCH] drivers/media/i2c: Fix DW9719 dependencies
@@ -904,10 +904,10 @@ Patchset: cameras
  1 file changed, 1 insertion(+), 1 deletion(-)
  1 file changed, 1 insertion(+), 1 deletion(-)
 
 
 diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
 diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
-index 98d081efeef7..c67966cafe10 100644
+index fbaa2ce7fa3e..7a46ad85dbd4 100644
 --- a/drivers/media/i2c/Kconfig
 --- a/drivers/media/i2c/Kconfig
 +++ b/drivers/media/i2c/Kconfig
 +++ b/drivers/media/i2c/Kconfig
-@@ -823,7 +823,7 @@ config VIDEO_DW9714
+@@ -830,7 +830,7 @@ config VIDEO_DW9714
  
  
  config VIDEO_DW9719
  config VIDEO_DW9719
  	tristate "DW9719 lens voice coil support"
  	tristate "DW9719 lens voice coil support"
@@ -919,7 +919,7 @@ index 98d081efeef7..c67966cafe10 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 61b32f0730f7e36a869d35eac3483dec2871bb0a Mon Sep 17 00:00:00 2001
+From 70dfff3f0ba14b3afe427c7ed72392603f567cc3 Mon Sep 17 00:00:00 2001
 From: Daniel Scally <dan.scally@ideasonboard.com>
 From: Daniel Scally <dan.scally@ideasonboard.com>
 Date: Thu, 2 Mar 2023 12:59:39 +0000
 Date: Thu, 2 Mar 2023 12:59:39 +0000
 Subject: [PATCH] platform/x86: int3472: Remap reset GPIO for INT347E
 Subject: [PATCH] platform/x86: int3472: Remap reset GPIO for INT347E

+ 2 - 2
patches/6.1/0013-amd-gpio.patch

@@ -1,4 +1,4 @@
-From 8d134ad0125086e62f08d82ff534b7240ecc3244 Mon Sep 17 00:00:00 2001
+From 371768adf76082ddcc64c24c6148a846bd9a9ecb Mon Sep 17 00:00:00 2001
 From: Sachi King <nakato@nakato.io>
 From: Sachi King <nakato@nakato.io>
 Date: Sat, 29 May 2021 17:47:38 +1000
 Date: Sat, 29 May 2021 17:47:38 +1000
 Subject: [PATCH] ACPI: Add quirk for Surface Laptop 4 AMD missing irq 7
 Subject: [PATCH] ACPI: Add quirk for Surface Laptop 4 AMD missing irq 7
@@ -65,7 +65,7 @@ index 0f762070a5e1..6362dd452233 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 90f9a6629585ead44dfaef9c9f87555bde1caffe Mon Sep 17 00:00:00 2001
+From 6b58c7ee50944840ba0aa74842cba972b68a8cc0 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Thu, 3 Jun 2021 14:04:26 +0200
 Date: Thu, 3 Jun 2021 14:04:26 +0200
 Subject: [PATCH] ACPI: Add AMD 13" Surface Laptop 4 model to irq 7 override
 Subject: [PATCH] ACPI: Add AMD 13" Surface Laptop 4 model to irq 7 override

+ 1 - 1
patches/6.1/0014-rtc.patch

@@ -1,4 +1,4 @@
-From 61618f660b94ef105dd327b5cb5c00879549970c Mon Sep 17 00:00:00 2001
+From f4398cdfef3a3b5ff9f6b6e170851682c06cd94e Mon Sep 17 00:00:00 2001
 From: "Bart Groeneveld | GPX Solutions B.V" <bart@gpxbv.nl>
 From: "Bart Groeneveld | GPX Solutions B.V" <bart@gpxbv.nl>
 Date: Mon, 5 Dec 2022 16:08:46 +0100
 Date: Mon, 5 Dec 2022 16:08:46 +0100
 Subject: [PATCH] acpi: allow usage of acpi_tad on HW-reduced platforms
 Subject: [PATCH] acpi: allow usage of acpi_tad on HW-reduced platforms

+ 1 - 1
patches/6.4/0001-surface3-oemb.patch

@@ -1,4 +1,4 @@
-From eab8b6dc66e2f5ee303fc0719cdebb1807f5d320 Mon Sep 17 00:00:00 2001
+From 16a8c7c230862cefa13cbf3d3c63c53e403b0f8d Mon Sep 17 00:00:00 2001
 From: Tsuchiya Yuto <kitakar@gmail.com>
 From: Tsuchiya Yuto <kitakar@gmail.com>
 Date: Sun, 18 Oct 2020 16:42:44 +0900
 Date: Sun, 18 Oct 2020 16:42:44 +0900
 Subject: [PATCH] (surface3-oemb) add DMI matches for Surface 3 with broken DMI
 Subject: [PATCH] (surface3-oemb) add DMI matches for Surface 3 with broken DMI

+ 9 - 9
patches/6.4/0002-mwifiex.patch

@@ -1,4 +1,4 @@
-From de173a6233f192422784aba823835c18fcd8b485 Mon Sep 17 00:00:00 2001
+From dc2e72cce140ad1dee9cbc6fdaa52226b8bb1a00 Mon Sep 17 00:00:00 2001
 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
 Date: Tue, 3 Nov 2020 13:28:04 +0100
 Date: Tue, 3 Nov 2020 13:28:04 +0100
 Subject: [PATCH] mwifiex: Add quirk resetting the PCI bridge on MS Surface
 Subject: [PATCH] mwifiex: Add quirk resetting the PCI bridge on MS Surface
@@ -32,10 +32,10 @@ Patchset: mwifiex
  3 files changed, 31 insertions(+), 8 deletions(-)
  3 files changed, 31 insertions(+), 8 deletions(-)
 
 
 diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c
 diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c
-index 9a698a16a8f3..14687342bc81 100644
+index 6697132ecc97..f06b4ebc5bd8 100644
 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c
 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c
 +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
 +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
-@@ -1762,9 +1762,21 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
+@@ -1771,9 +1771,21 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
  static int mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter)
  static int mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter)
  {
  {
  	struct pcie_service_card *card = adapter->card;
  	struct pcie_service_card *card = adapter->card;
@@ -165,7 +165,7 @@ index d6ff964aec5b..5d30ae39d65e 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 24046b6b9cb6ba34dc079bd690d9362581c8ec90 Mon Sep 17 00:00:00 2001
+From 897c0853d05db16395c72b8fba7f429d78436b3d Mon Sep 17 00:00:00 2001
 From: Tsuchiya Yuto <kitakar@gmail.com>
 From: Tsuchiya Yuto <kitakar@gmail.com>
 Date: Sun, 4 Oct 2020 00:11:49 +0900
 Date: Sun, 4 Oct 2020 00:11:49 +0900
 Subject: [PATCH] mwifiex: pcie: disable bridge_d3 for Surface gen4+
 Subject: [PATCH] mwifiex: pcie: disable bridge_d3 for Surface gen4+
@@ -187,10 +187,10 @@ Patchset: mwifiex
  3 files changed, 27 insertions(+), 8 deletions(-)
  3 files changed, 27 insertions(+), 8 deletions(-)
 
 
 diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c
 diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c
-index 14687342bc81..5e1a341f63df 100644
+index f06b4ebc5bd8..07f13b52ddb9 100644
 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c
 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c
 +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
 +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
-@@ -368,6 +368,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
+@@ -370,6 +370,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
  					const struct pci_device_id *ent)
  					const struct pci_device_id *ent)
  {
  {
  	struct pcie_service_card *card;
  	struct pcie_service_card *card;
@@ -198,7 +198,7 @@ index 14687342bc81..5e1a341f63df 100644
  	int ret;
  	int ret;
  
  
  	pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n",
  	pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n",
-@@ -409,6 +410,12 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
+@@ -411,6 +412,12 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
  		return -1;
  		return -1;
  	}
  	}
  
  
@@ -320,7 +320,7 @@ index 5d30ae39d65e..c14eb56eb911 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From f9d0cf76c6cf6015c066d70b0961bcf1ad5461b5 Mon Sep 17 00:00:00 2001
+From bbeaaa59c819fedf71d8a90302105652ec37da5b Mon Sep 17 00:00:00 2001
 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
 Date: Thu, 25 Mar 2021 11:33:02 +0100
 Date: Thu, 25 Mar 2021 11:33:02 +0100
 Subject: [PATCH] Bluetooth: btusb: Lower passive lescan interval on Marvell
 Subject: [PATCH] Bluetooth: btusb: Lower passive lescan interval on Marvell
@@ -356,7 +356,7 @@ Patchset: mwifiex
  1 file changed, 15 insertions(+)
  1 file changed, 15 insertions(+)
 
 
 diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
 diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
-index 025e803ba55c..cf3d0c36a160 100644
+index 5559d6d8ae8b..5baec547dfee 100644
 --- a/drivers/bluetooth/btusb.c
 --- a/drivers/bluetooth/btusb.c
 +++ b/drivers/bluetooth/btusb.c
 +++ b/drivers/bluetooth/btusb.c
 @@ -65,6 +65,7 @@ static struct usb_driver btusb_driver;
 @@ -65,6 +65,7 @@ static struct usb_driver btusb_driver;

+ 1 - 1
patches/6.4/0003-ath10k.patch

@@ -1,4 +1,4 @@
-From ab53d245c04af4a1284084bada8701923cd0a5c1 Mon Sep 17 00:00:00 2001
+From f9b647f7acd5a85651c864917d1be439cbfb8b84 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Sat, 27 Feb 2021 00:45:52 +0100
 Date: Sat, 27 Feb 2021 00:45:52 +0100
 Subject: [PATCH] ath10k: Add module parameters to override board files
 Subject: [PATCH] ath10k: Add module parameters to override board files

+ 7 - 7
patches/6.4/0004-ipts.patch

@@ -1,4 +1,4 @@
-From 09ae21ea0d5a3462edfbab8c07c71179e1e22824 Mon Sep 17 00:00:00 2001
+From 3c60bfb7064b153409c126bb86a0c222d01535fe Mon Sep 17 00:00:00 2001
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 Date: Thu, 30 Jul 2020 13:21:53 +0200
 Date: Thu, 30 Jul 2020 13:21:53 +0200
 Subject: [PATCH] misc: mei: Add missing IPTS device IDs
 Subject: [PATCH] misc: mei: Add missing IPTS device IDs
@@ -36,7 +36,7 @@ index 676d566f38dd..6b37dd1f8b2a 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 789cc2098d901d9092ed0205ca4c4a9101f22f0a Mon Sep 17 00:00:00 2001
+From 6459f5cfd31c3eaacb1bf31e58f3686a01a76894 Mon Sep 17 00:00:00 2001
 From: Liban Hannan <liban.p@gmail.com>
 From: Liban Hannan <liban.p@gmail.com>
 Date: Tue, 12 Apr 2022 23:31:12 +0100
 Date: Tue, 12 Apr 2022 23:31:12 +0100
 Subject: [PATCH] iommu: ipts: use IOMMU passthrough mode for IPTS
 Subject: [PATCH] iommu: ipts: use IOMMU passthrough mode for IPTS
@@ -136,7 +136,7 @@ index b871a6afd803..0d4542b7365d 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 5f92b2d1ed99b508339c1547751175fbce51bc58 Mon Sep 17 00:00:00 2001
+From 0a2abc1902c17b9449cbc9c53ae5da72fdf62933 Mon Sep 17 00:00:00 2001
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 Date: Sun, 11 Dec 2022 12:00:59 +0100
 Date: Sun, 11 Dec 2022 12:00:59 +0100
 Subject: [PATCH] hid: Add support for Intel Precise Touch and Stylus
 Subject: [PATCH] hid: Add support for Intel Precise Touch and Stylus
@@ -2986,7 +2986,7 @@ index 000000000000..a314843599fc
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 577f601a5e3dc806fbe8572d5d8ce3b1778553bb Mon Sep 17 00:00:00 2001
+From e8dee0b4dfcad77d13d78130034679509bc08e78 Mon Sep 17 00:00:00 2001
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 Date: Fri, 28 Apr 2023 15:41:12 +0200
 Date: Fri, 28 Apr 2023 15:41:12 +0200
 Subject: [PATCH] Update IPTS from module repo
 Subject: [PATCH] Update IPTS from module repo
@@ -3178,7 +3178,7 @@ index 6cbb24a8a054..e0c400f420b9 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From ab4faaa7b81363b8023e598ba68ca3cb0b313f08 Mon Sep 17 00:00:00 2001
+From 97f8ddfb5a0c033afb894f0f77edb6c3071bec5c Mon Sep 17 00:00:00 2001
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 Date: Mon, 17 Jul 2023 18:10:43 +0200
 Date: Mon, 17 Jul 2023 18:10:43 +0200
 Subject: [PATCH] Update IPTS from module repo
 Subject: [PATCH] Update IPTS from module repo
@@ -4433,7 +4433,7 @@ index a314843599fc..1f966b8b32c4 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 0f057c98109b621537051ee0ef3b6bcec39663bb Mon Sep 17 00:00:00 2001
+From caf8cc911482d8a28f18fbe04ccdadb428bf48e8 Mon Sep 17 00:00:00 2001
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 Date: Sat, 22 Jul 2023 17:08:56 +0200
 Date: Sat, 22 Jul 2023 17:08:56 +0200
 Subject: [PATCH] Update IPTS from module repo
 Subject: [PATCH] Update IPTS from module repo
@@ -4467,7 +4467,7 @@ index e835b460aa79..7c5cbafdb279 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 632ca681eb097274381a34b6f750a9e3c88bd350 Mon Sep 17 00:00:00 2001
+From 891dd6386c0a7c7a12b1dbab7170b97d0ddb8543 Mon Sep 17 00:00:00 2001
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 Date: Sun, 23 Jul 2023 17:55:15 +0200
 Date: Sun, 23 Jul 2023 17:55:15 +0200
 Subject: [PATCH] Update IPTS from module repo
 Subject: [PATCH] Update IPTS from module repo

+ 2 - 2
patches/6.4/0005-ithc.patch

@@ -1,4 +1,4 @@
-From 03de9ebd942f5ce1f62b272879cee0a50bd644b6 Mon Sep 17 00:00:00 2001
+From de8e6e5f1d2324336292b470b1c7e4a015baba23 Mon Sep 17 00:00:00 2001
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 Date: Sun, 11 Dec 2022 12:03:38 +0100
 Date: Sun, 11 Dec 2022 12:03:38 +0100
 Subject: [PATCH] iommu: intel: Disable source id verification for ITHC
 Subject: [PATCH] iommu: intel: Disable source id verification for ITHC
@@ -39,7 +39,7 @@ index a1b987335b31..970805409470 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 910cb08b0b8ac5d6a140cffa76db1dfc9cd5bd87 Mon Sep 17 00:00:00 2001
+From 0acabea1031724a9af7023b35efb4c24e60e5842 Mon Sep 17 00:00:00 2001
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 From: Dorian Stoll <dorian.stoll@tmsp.io>
 Date: Sun, 11 Dec 2022 12:10:54 +0100
 Date: Sun, 11 Dec 2022 12:10:54 +0100
 Subject: [PATCH] hid: Add support for Intel Touch Host Controller
 Subject: [PATCH] hid: Add support for Intel Touch Host Controller

+ 2 - 2
patches/6.4/0006-surface-sam-over-hid.patch

@@ -1,4 +1,4 @@
-From d7c4caa8c28450f74ce210e4d005ff30dc131e58 Mon Sep 17 00:00:00 2001
+From e3016490e738dabb8cad5ff542b97d05fff6c63e Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Sat, 25 Jul 2020 17:19:53 +0200
 Date: Sat, 25 Jul 2020 17:19:53 +0200
 Subject: [PATCH] i2c: acpi: Implement RawBytes read access
 Subject: [PATCH] i2c: acpi: Implement RawBytes read access
@@ -110,7 +110,7 @@ index d6037a328669..a290ebc77aea 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 57751f7566cfbe80fbf08f0a3fdbeb818574c6e8 Mon Sep 17 00:00:00 2001
+From eb26a92e146a575944fd75899cb860de7f6d86a7 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Sat, 13 Feb 2021 16:41:18 +0100
 Date: Sat, 13 Feb 2021 16:41:18 +0100
 Subject: [PATCH] platform/surface: Add driver for Surface Book 1 dGPU switch
 Subject: [PATCH] platform/surface: Add driver for Surface Book 1 dGPU switch

+ 2 - 2
patches/6.4/0007-surface-button.patch

@@ -1,4 +1,4 @@
-From 674be06add9dae037acfcd208dd0ff5d28a8329a Mon Sep 17 00:00:00 2001
+From 5c4b82f566f9abf7918305292626ee033395477a Mon Sep 17 00:00:00 2001
 From: Sachi King <nakato@nakato.io>
 From: Sachi King <nakato@nakato.io>
 Date: Tue, 5 Oct 2021 00:05:09 +1100
 Date: Tue, 5 Oct 2021 00:05:09 +1100
 Subject: [PATCH] Input: soc_button_array - support AMD variant Surface devices
 Subject: [PATCH] Input: soc_button_array - support AMD variant Surface devices
@@ -75,7 +75,7 @@ index e79f5497948b..2bddbe6e9ea4 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From cfb9a41882e71f09927585c52b51ea2ca843a4f8 Mon Sep 17 00:00:00 2001
+From 4567303193e8334c5017af6b36f9ad22a8875470 Mon Sep 17 00:00:00 2001
 From: Sachi King <nakato@nakato.io>
 From: Sachi King <nakato@nakato.io>
 Date: Tue, 5 Oct 2021 00:22:57 +1100
 Date: Tue, 5 Oct 2021 00:22:57 +1100
 Subject: [PATCH] platform/surface: surfacepro3_button: don't load on amd
 Subject: [PATCH] platform/surface: surfacepro3_button: don't load on amd

+ 16 - 16
patches/6.4/0008-surface-typecover.patch

@@ -1,4 +1,4 @@
-From a534c2f2d04a3d181c06870c7f92a73580427838 Mon Sep 17 00:00:00 2001
+From 93a283b21122dae68ece3fef3790b0736fe92e5d Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Sat, 18 Feb 2023 01:02:49 +0100
 Date: Sat, 18 Feb 2023 01:02:49 +0100
 Subject: [PATCH] USB: quirks: Add USB_QUIRK_DELAY_INIT for Surface Go 3
 Subject: [PATCH] USB: quirks: Add USB_QUIRK_DELAY_INIT for Surface Go 3
@@ -39,7 +39,7 @@ index 15e9bd180a1d..0d70461d01e1 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From b8da2a367e79570b0cb418fd6fb7d1ca699dbe90 Mon Sep 17 00:00:00 2001
+From 0f829b3d21f87353ddf9b03b4203e66745291cb7 Mon Sep 17 00:00:00 2001
 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
 Date: Thu, 5 Nov 2020 13:09:45 +0100
 Date: Thu, 5 Nov 2020 13:09:45 +0100
 Subject: [PATCH] hid/multitouch: Turn off Type Cover keyboard backlight when
 Subject: [PATCH] hid/multitouch: Turn off Type Cover keyboard backlight when
@@ -75,7 +75,7 @@ Patchset: surface-typecover
  1 file changed, 98 insertions(+), 2 deletions(-)
  1 file changed, 98 insertions(+), 2 deletions(-)
 
 
 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
-index e31be0cb8b85..63fd042aba6b 100644
+index 521b2ffb4244..c8f3d05c8866 100644
 --- a/drivers/hid/hid-multitouch.c
 --- a/drivers/hid/hid-multitouch.c
 +++ b/drivers/hid/hid-multitouch.c
 +++ b/drivers/hid/hid-multitouch.c
 @@ -34,7 +34,10 @@
 @@ -34,7 +34,10 @@
@@ -147,7 +147,7 @@ index e31be0cb8b85..63fd042aba6b 100644
  	{ }
  	{ }
  };
  };
  
  
-@@ -1728,6 +1748,69 @@ static void mt_expired_timeout(struct timer_list *t)
+@@ -1721,6 +1741,69 @@ static void mt_expired_timeout(struct timer_list *t)
  	clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
  	clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
  }
  }
  
  
@@ -217,7 +217,7 @@ index e31be0cb8b85..63fd042aba6b 100644
  static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
  static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
  {
  {
  	int ret, i;
  	int ret, i;
-@@ -1751,6 +1834,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
+@@ -1744,6 +1827,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
  	td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN;
  	td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN;
  	hid_set_drvdata(hdev, td);
  	hid_set_drvdata(hdev, td);
  
  
@@ -227,7 +227,7 @@ index e31be0cb8b85..63fd042aba6b 100644
  	INIT_LIST_HEAD(&td->applications);
  	INIT_LIST_HEAD(&td->applications);
  	INIT_LIST_HEAD(&td->reports);
  	INIT_LIST_HEAD(&td->reports);
  
  
-@@ -1789,15 +1875,19 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
+@@ -1782,15 +1868,19 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
  	timer_setup(&td->release_timer, mt_expired_timeout, 0);
  	timer_setup(&td->release_timer, mt_expired_timeout, 0);
  
  
  	ret = hid_parse(hdev);
  	ret = hid_parse(hdev);
@@ -249,7 +249,7 @@ index e31be0cb8b85..63fd042aba6b 100644
  
  
  	ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group);
  	ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group);
  	if (ret)
  	if (ret)
-@@ -1849,6 +1939,7 @@ static void mt_remove(struct hid_device *hdev)
+@@ -1842,6 +1932,7 @@ static void mt_remove(struct hid_device *hdev)
  {
  {
  	struct mt_device *td = hid_get_drvdata(hdev);
  	struct mt_device *td = hid_get_drvdata(hdev);
  
  
@@ -257,7 +257,7 @@ index e31be0cb8b85..63fd042aba6b 100644
  	del_timer_sync(&td->release_timer);
  	del_timer_sync(&td->release_timer);
  
  
  	sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
  	sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
-@@ -2226,6 +2317,11 @@ static const struct hid_device_id mt_devices[] = {
+@@ -2219,6 +2310,11 @@ static const struct hid_device_id mt_devices[] = {
  		MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
  		MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
  			USB_DEVICE_ID_XIROKU_CSR2) },
  			USB_DEVICE_ID_XIROKU_CSR2) },
  
  
@@ -272,7 +272,7 @@ index e31be0cb8b85..63fd042aba6b 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From d8c27dc0c2ebacdc037c1f5684ec65c92f8a6303 Mon Sep 17 00:00:00 2001
+From 91e26c96736b4c4972fe26660bfc15badecdb633 Mon Sep 17 00:00:00 2001
 From: PJungkamp <p.jungkamp@gmail.com>
 From: PJungkamp <p.jungkamp@gmail.com>
 Date: Fri, 25 Feb 2022 12:04:25 +0100
 Date: Fri, 25 Feb 2022 12:04:25 +0100
 Subject: [PATCH] hid/multitouch: Add support for surface pro type cover tablet
 Subject: [PATCH] hid/multitouch: Add support for surface pro type cover tablet
@@ -301,7 +301,7 @@ Patchset: surface-typecover
  1 file changed, 122 insertions(+), 26 deletions(-)
  1 file changed, 122 insertions(+), 26 deletions(-)
 
 
 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
-index 63fd042aba6b..508a250ff4bf 100644
+index c8f3d05c8866..1c6e4d66e762 100644
 --- a/drivers/hid/hid-multitouch.c
 --- a/drivers/hid/hid-multitouch.c
 +++ b/drivers/hid/hid-multitouch.c
 +++ b/drivers/hid/hid-multitouch.c
 @@ -77,6 +77,7 @@ MODULE_LICENSE("GPL");
 @@ -77,6 +77,7 @@ MODULE_LICENSE("GPL");
@@ -454,7 +454,7 @@ index 63fd042aba6b..508a250ff4bf 100644
  static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
  static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
  {
  {
  	struct mt_device *td = hid_get_drvdata(hdev);
  	struct mt_device *td = hid_get_drvdata(hdev);
-@@ -1660,6 +1742,13 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
+@@ -1659,6 +1741,13 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
  		/* force BTN_STYLUS to allow tablet matching in udev */
  		/* force BTN_STYLUS to allow tablet matching in udev */
  		__set_bit(BTN_STYLUS, hi->input->keybit);
  		__set_bit(BTN_STYLUS, hi->input->keybit);
  		break;
  		break;
@@ -468,7 +468,7 @@ index 63fd042aba6b..508a250ff4bf 100644
  	default:
  	default:
  		suffix = "UNKNOWN";
  		suffix = "UNKNOWN";
  		break;
  		break;
-@@ -1748,30 +1837,6 @@ static void mt_expired_timeout(struct timer_list *t)
+@@ -1741,30 +1830,6 @@ static void mt_expired_timeout(struct timer_list *t)
  	clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
  	clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
  }
  }
  
  
@@ -499,7 +499,7 @@ index 63fd042aba6b..508a250ff4bf 100644
  static void update_keyboard_backlight(struct hid_device *hdev, bool enabled)
  static void update_keyboard_backlight(struct hid_device *hdev, bool enabled)
  {
  {
  	struct usb_device *udev = hid_to_usb_dev(hdev);
  	struct usb_device *udev = hid_to_usb_dev(hdev);
-@@ -1780,8 +1845,9 @@ static void update_keyboard_backlight(struct hid_device *hdev, bool enabled)
+@@ -1773,8 +1838,9 @@ static void update_keyboard_backlight(struct hid_device *hdev, bool enabled)
  	/* Wake up the device in case it's already suspended */
  	/* Wake up the device in case it's already suspended */
  	pm_runtime_get_sync(&udev->dev);
  	pm_runtime_get_sync(&udev->dev);
  
  
@@ -511,7 +511,7 @@ index 63fd042aba6b..508a250ff4bf 100644
  		hid_err(hdev, "couldn't find backlight field\n");
  		hid_err(hdev, "couldn't find backlight field\n");
  		goto out;
  		goto out;
  	}
  	}
-@@ -1916,13 +1982,24 @@ static int mt_suspend(struct hid_device *hdev, pm_message_t state)
+@@ -1909,13 +1975,24 @@ static int mt_suspend(struct hid_device *hdev, pm_message_t state)
  
  
  static int mt_reset_resume(struct hid_device *hdev)
  static int mt_reset_resume(struct hid_device *hdev)
  {
  {
@@ -536,7 +536,7 @@ index 63fd042aba6b..508a250ff4bf 100644
  	/* Some Elan legacy devices require SET_IDLE to be set on resume.
  	/* Some Elan legacy devices require SET_IDLE to be set on resume.
  	 * It should be safe to send it to other devices too.
  	 * It should be safe to send it to other devices too.
  	 * Tested on 3M, Stantum, Cypress, Zytronic, eGalax, and Elan panels. */
  	 * Tested on 3M, Stantum, Cypress, Zytronic, eGalax, and Elan panels. */
-@@ -1931,6 +2008,10 @@ static int mt_resume(struct hid_device *hdev)
+@@ -1924,6 +2001,10 @@ static int mt_resume(struct hid_device *hdev)
  
  
  	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
  	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
  
  
@@ -547,7 +547,7 @@ index 63fd042aba6b..508a250ff4bf 100644
  	return 0;
  	return 0;
  }
  }
  #endif
  #endif
-@@ -1938,6 +2019,21 @@ static int mt_resume(struct hid_device *hdev)
+@@ -1931,6 +2012,21 @@ static int mt_resume(struct hid_device *hdev)
  static void mt_remove(struct hid_device *hdev)
  static void mt_remove(struct hid_device *hdev)
  {
  {
  	struct mt_device *td = hid_get_drvdata(hdev);
  	struct mt_device *td = hid_get_drvdata(hdev);

+ 2 - 2
patches/6.4/0009-surface-shutdown.patch

@@ -1,4 +1,4 @@
-From 0cbbacdcce263e71a3af3f51ee3261cf0cfd3784 Mon Sep 17 00:00:00 2001
+From b4be6f1e311dd7e312e1003ec65edc4068447350 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Sun, 19 Feb 2023 22:12:24 +0100
 Date: Sun, 19 Feb 2023 22:12:24 +0100
 Subject: [PATCH] PCI: Add quirk to prevent calling shutdown mehtod
 Subject: [PATCH] PCI: Add quirk to prevent calling shutdown mehtod
@@ -81,7 +81,7 @@ index b7c65193e786..f9e94faaf1b4 100644
 +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x466d, quirk_no_shutdown);  // Thunderbolt 4 NHI
 +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x466d, quirk_no_shutdown);  // Thunderbolt 4 NHI
 +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x46a8, quirk_no_shutdown);  // GPU
 +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x46a8, quirk_no_shutdown);  // GPU
 diff --git a/include/linux/pci.h b/include/linux/pci.h
 diff --git a/include/linux/pci.h b/include/linux/pci.h
-index c69a2cc1f412..a23262cfc9b7 100644
+index 7ee498cd1f37..740049a82343 100644
 --- a/include/linux/pci.h
 --- a/include/linux/pci.h
 +++ b/include/linux/pci.h
 +++ b/include/linux/pci.h
 @@ -464,6 +464,7 @@ struct pci_dev {
 @@ -464,6 +464,7 @@ struct pci_dev {

+ 1 - 1
patches/6.4/0010-surface-gpe.patch

@@ -1,4 +1,4 @@
-From d29305888f2e4550e2951ce724e881cb5cb70e71 Mon Sep 17 00:00:00 2001
+From df55e0de121fe4fc0012aea462aa4992acea3f0b Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Sun, 12 Mar 2023 01:41:57 +0100
 Date: Sun, 12 Mar 2023 01:41:57 +0100
 Subject: [PATCH] platform/surface: gpe: Add support for Surface Pro 9
 Subject: [PATCH] platform/surface: gpe: Add support for Surface Pro 9

+ 25 - 25
patches/6.4/0011-cameras.patch

@@ -1,4 +1,4 @@
-From afc382cc55de5fbbba9c71b4f6c3c2c378c809e5 Mon Sep 17 00:00:00 2001
+From ae015f45fc28c9ba7b637975d2a0c49d649e9c9f Mon Sep 17 00:00:00 2001
 From: Hans de Goede <hdegoede@redhat.com>
 From: Hans de Goede <hdegoede@redhat.com>
 Date: Sun, 10 Oct 2021 20:56:57 +0200
 Date: Sun, 10 Oct 2021 20:56:57 +0200
 Subject: [PATCH] ACPI: delay enumeration of devices with a _DEP pointing to an
 Subject: [PATCH] ACPI: delay enumeration of devices with a _DEP pointing to an
@@ -74,7 +74,7 @@ index c28c8d5ca0c8..9bdd65519ed4 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From f157439151ef154a0b1974679858746878eac77d Mon Sep 17 00:00:00 2001
+From 63e9677412a1346622d91dd69b6ddcf76d818f32 Mon Sep 17 00:00:00 2001
 From: zouxiaoh <xiaohong.zou@intel.com>
 From: zouxiaoh <xiaohong.zou@intel.com>
 Date: Fri, 25 Jun 2021 08:52:59 +0800
 Date: Fri, 25 Jun 2021 08:52:59 +0800
 Subject: [PATCH] iommu: intel-ipu: use IOMMU passthrough mode for Intel IPUs
 Subject: [PATCH] iommu: intel-ipu: use IOMMU passthrough mode for Intel IPUs
@@ -191,7 +191,7 @@ index 0d4542b7365d..c96a9baea21c 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From edadb99e2211b024733b1882832048290355d818 Mon Sep 17 00:00:00 2001
+From a515136213935f0b16ef6d39810f717f3b558ce3 Mon Sep 17 00:00:00 2001
 From: Daniel Scally <djrscally@gmail.com>
 From: Daniel Scally <djrscally@gmail.com>
 Date: Sun, 10 Oct 2021 20:57:02 +0200
 Date: Sun, 10 Oct 2021 20:57:02 +0200
 Subject: [PATCH] platform/x86: int3472: Enable I2c daisy chain
 Subject: [PATCH] platform/x86: int3472: Enable I2c daisy chain
@@ -228,7 +228,7 @@ index 5b8d1a9620a5..6a0ff035cf20 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From f04e2306f26380a836fe64dbda7279d0bdb16ba6 Mon Sep 17 00:00:00 2001
+From 62ef76ec78e2b52003c28b5f963dd6b1a825f9d5 Mon Sep 17 00:00:00 2001
 From: Daniel Scally <djrscally@gmail.com>
 From: Daniel Scally <djrscally@gmail.com>
 Date: Wed, 4 May 2022 23:21:45 +0100
 Date: Wed, 4 May 2022 23:21:45 +0100
 Subject: [PATCH] media: ipu3-cio2: Move functionality from .complete() to
 Subject: [PATCH] media: ipu3-cio2: Move functionality from .complete() to
@@ -247,10 +247,10 @@ Patchset: cameras
  1 file changed, 23 insertions(+), 42 deletions(-)
  1 file changed, 23 insertions(+), 42 deletions(-)
 
 
 diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
 diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
-index 3c84cb121632..8ba5f78baf9c 100644
+index 03a7ab4d2e69..3046faf31254 100644
 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
 +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
 +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
-@@ -1384,7 +1384,10 @@ static int cio2_notifier_bound(struct v4l2_async_notifier *notifier,
+@@ -1385,7 +1385,10 @@ static int cio2_notifier_bound(struct v4l2_async_notifier *notifier,
  {
  {
  	struct cio2_device *cio2 = to_cio2_device(notifier);
  	struct cio2_device *cio2 = to_cio2_device(notifier);
  	struct sensor_async_subdev *s_asd = to_sensor_asd(asd);
  	struct sensor_async_subdev *s_asd = to_sensor_asd(asd);
@@ -261,7 +261,7 @@ index 3c84cb121632..8ba5f78baf9c 100644
  
  
  	if (cio2->queue[s_asd->csi2.port].sensor)
  	if (cio2->queue[s_asd->csi2.port].sensor)
  		return -EBUSY;
  		return -EBUSY;
-@@ -1395,7 +1398,26 @@ static int cio2_notifier_bound(struct v4l2_async_notifier *notifier,
+@@ -1396,7 +1399,26 @@ static int cio2_notifier_bound(struct v4l2_async_notifier *notifier,
  	q->sensor = sd;
  	q->sensor = sd;
  	q->csi_rx_base = cio2->base + CIO2_REG_PIPE_BASE(q->csi2.port);
  	q->csi_rx_base = cio2->base + CIO2_REG_PIPE_BASE(q->csi2.port);
  
  
@@ -289,7 +289,7 @@ index 3c84cb121632..8ba5f78baf9c 100644
  }
  }
  
  
  /* The .unbind callback */
  /* The .unbind callback */
-@@ -1409,50 +1431,9 @@ static void cio2_notifier_unbind(struct v4l2_async_notifier *notifier,
+@@ -1410,50 +1432,9 @@ static void cio2_notifier_unbind(struct v4l2_async_notifier *notifier,
  	cio2->queue[s_asd->csi2.port].sensor = NULL;
  	cio2->queue[s_asd->csi2.port].sensor = NULL;
  }
  }
  
  
@@ -343,7 +343,7 @@ index 3c84cb121632..8ba5f78baf9c 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From e9c08ea0aaf68bd9fecb4d07f7954eda4d4f4c22 Mon Sep 17 00:00:00 2001
+From 6e975da0d6eda4e5b2a2ead8fa557d4c51c3484f Mon Sep 17 00:00:00 2001
 From: Daniel Scally <djrscally@gmail.com>
 From: Daniel Scally <djrscally@gmail.com>
 Date: Thu, 2 Jun 2022 22:15:56 +0100
 Date: Thu, 2 Jun 2022 22:15:56 +0100
 Subject: [PATCH] media: ipu3-cio2: Re-add .complete() to ipu3-cio2
 Subject: [PATCH] media: ipu3-cio2: Re-add .complete() to ipu3-cio2
@@ -361,10 +361,10 @@ Patchset: cameras
  1 file changed, 9 insertions(+)
  1 file changed, 9 insertions(+)
 
 
 diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
 diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
-index 8ba5f78baf9c..f0b77012641b 100644
+index 3046faf31254..21854c734fdd 100644
 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
 +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
 +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
-@@ -1431,9 +1431,18 @@ static void cio2_notifier_unbind(struct v4l2_async_notifier *notifier,
+@@ -1432,9 +1432,18 @@ static void cio2_notifier_unbind(struct v4l2_async_notifier *notifier,
  	cio2->queue[s_asd->csi2.port].sensor = NULL;
  	cio2->queue[s_asd->csi2.port].sensor = NULL;
  }
  }
  
  
@@ -386,7 +386,7 @@ index 8ba5f78baf9c..f0b77012641b 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 6db062559784ffd2491cca31d98fa71c0395ce65 Mon Sep 17 00:00:00 2001
+From 545955f4693dced7e1912de1b0d3c8aa9fdf03b5 Mon Sep 17 00:00:00 2001
 From: Daniel Scally <djrscally@gmail.com>
 From: Daniel Scally <djrscally@gmail.com>
 Date: Thu, 28 Oct 2021 21:55:16 +0100
 Date: Thu, 28 Oct 2021 21:55:16 +0100
 Subject: [PATCH] media: i2c: Add driver for DW9719 VCM
 Subject: [PATCH] media: i2c: Add driver for DW9719 VCM
@@ -423,10 +423,10 @@ index 35e19594640d..7e6631f0b55b 100644
  M:	Dongchun Zhu <dongchun.zhu@mediatek.com>
  M:	Dongchun Zhu <dongchun.zhu@mediatek.com>
  L:	linux-media@vger.kernel.org
  L:	linux-media@vger.kernel.org
 diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
 diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
-index 76d1ee3cc1ba..f5d036164db9 100644
+index dbcc2222ddd3..750b38d11210 100644
 --- a/drivers/media/i2c/Kconfig
 --- a/drivers/media/i2c/Kconfig
 +++ b/drivers/media/i2c/Kconfig
 +++ b/drivers/media/i2c/Kconfig
-@@ -835,6 +835,17 @@ config VIDEO_DW9714
+@@ -842,6 +842,17 @@ config VIDEO_DW9714
  	  capability. This is designed for linear control of
  	  capability. This is designed for linear control of
  	  voice coil motors, controlled via I2C serial interface.
  	  voice coil motors, controlled via I2C serial interface.
  
  
@@ -890,7 +890,7 @@ index 000000000000..180b04d2a6b3
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 62bdb6fb38e044d8a505844466a7fffe1318473c Mon Sep 17 00:00:00 2001
+From 690c13815c584cbb1be0c7526c0639af98c01a97 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Fri, 15 Jul 2022 23:48:00 +0200
 Date: Fri, 15 Jul 2022 23:48:00 +0200
 Subject: [PATCH] drivers/media/i2c: Fix DW9719 dependencies
 Subject: [PATCH] drivers/media/i2c: Fix DW9719 dependencies
@@ -904,10 +904,10 @@ Patchset: cameras
  1 file changed, 1 insertion(+), 1 deletion(-)
  1 file changed, 1 insertion(+), 1 deletion(-)
 
 
 diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
 diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
-index f5d036164db9..cf6919f73347 100644
+index 750b38d11210..99d84e60e58d 100644
 --- a/drivers/media/i2c/Kconfig
 --- a/drivers/media/i2c/Kconfig
 +++ b/drivers/media/i2c/Kconfig
 +++ b/drivers/media/i2c/Kconfig
-@@ -837,7 +837,7 @@ config VIDEO_DW9714
+@@ -844,7 +844,7 @@ config VIDEO_DW9714
  
  
  config VIDEO_DW9719
  config VIDEO_DW9719
  	tristate "DW9719 lens voice coil support"
  	tristate "DW9719 lens voice coil support"
@@ -919,7 +919,7 @@ index f5d036164db9..cf6919f73347 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From efae832a3f6b7bb206cc8ecbd0dd690fb0accb5d Mon Sep 17 00:00:00 2001
+From a3a01bf668c8b4d39bccdf7323d6a07862fb9296 Mon Sep 17 00:00:00 2001
 From: Daniel Scally <dan.scally@ideasonboard.com>
 From: Daniel Scally <dan.scally@ideasonboard.com>
 Date: Thu, 2 Mar 2023 12:59:39 +0000
 Date: Thu, 2 Mar 2023 12:59:39 +0000
 Subject: [PATCH] platform/x86: int3472: Remap reset GPIO for INT347E
 Subject: [PATCH] platform/x86: int3472: Remap reset GPIO for INT347E
@@ -975,7 +975,7 @@ index ef020e23e596..d21ef437c2f6 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 8d5a73b2eb529478fe2b57ae09f3fe21f5ba7237 Mon Sep 17 00:00:00 2001
+From abc076db092192159821a8b8cba4d6d206b1b826 Mon Sep 17 00:00:00 2001
 From: Daniel Scally <dan.scally@ideasonboard.com>
 From: Daniel Scally <dan.scally@ideasonboard.com>
 Date: Tue, 21 Mar 2023 13:45:26 +0000
 Date: Tue, 21 Mar 2023 13:45:26 +0000
 Subject: [PATCH] media: i2c: Clarify that gain is Analogue gain in OV7251
 Subject: [PATCH] media: i2c: Clarify that gain is Analogue gain in OV7251
@@ -1014,7 +1014,7 @@ index 88e987435285..ff7b2c26da83 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 1d05c07b717a305c9024f31a63c55d335999fd6c Mon Sep 17 00:00:00 2001
+From 698c1a2111fcd8edf2290663ae31c35be5645978 Mon Sep 17 00:00:00 2001
 From: Daniel Scally <dan.scally@ideasonboard.com>
 From: Daniel Scally <dan.scally@ideasonboard.com>
 Date: Wed, 22 Mar 2023 11:01:42 +0000
 Date: Wed, 22 Mar 2023 11:01:42 +0000
 Subject: [PATCH] media: v4l2-core: Acquire privacy led in
 Subject: [PATCH] media: v4l2-core: Acquire privacy led in
@@ -1048,10 +1048,10 @@ index b16b5f4cb91e..33739a979cbc 100644
  	 * No reference taken. The reference is held by the device
  	 * No reference taken. The reference is held by the device
  	 * (struct v4l2_subdev.dev), and async sub-device does not
  	 * (struct v4l2_subdev.dev), and async sub-device does not
 diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
 diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
-index 049c2f2001ea..f8c3e40b2b71 100644
+index 4fa9225aa3d9..ed4c75253cbc 100644
 --- a/drivers/media/v4l2-core/v4l2-fwnode.c
 --- a/drivers/media/v4l2-core/v4l2-fwnode.c
 +++ b/drivers/media/v4l2-core/v4l2-fwnode.c
 +++ b/drivers/media/v4l2-core/v4l2-fwnode.c
-@@ -1304,10 +1304,6 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
+@@ -1314,10 +1314,6 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
  
  
  	v4l2_async_nf_init(notifier);
  	v4l2_async_nf_init(notifier);
  
  
@@ -1065,7 +1065,7 @@ index 049c2f2001ea..f8c3e40b2b71 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From e8a7b24cb38482bedfa3970847863c79c6591aaf Mon Sep 17 00:00:00 2001
+From 3864b56bf152f15d200dccc5ae7192392cfcce2a Mon Sep 17 00:00:00 2001
 From: Kate Hsuan <hpa@redhat.com>
 From: Kate Hsuan <hpa@redhat.com>
 Date: Tue, 21 Mar 2023 23:37:16 +0800
 Date: Tue, 21 Mar 2023 23:37:16 +0800
 Subject: [PATCH] platform: x86: int3472: Add MFD cell for tps68470 LED
 Subject: [PATCH] platform: x86: int3472: Add MFD cell for tps68470 LED
@@ -1106,7 +1106,7 @@ index 6a0ff035cf20..2a7d01d3abc8 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From a4f49d63639d4b7d2d2c3e8dae2f91ba0ae1f36f Mon Sep 17 00:00:00 2001
+From 84145fddb4a55e49a6b9c3556d00d71c91b69e26 Mon Sep 17 00:00:00 2001
 From: Kate Hsuan <hpa@redhat.com>
 From: Kate Hsuan <hpa@redhat.com>
 Date: Tue, 21 Mar 2023 23:37:17 +0800
 Date: Tue, 21 Mar 2023 23:37:17 +0800
 Subject: [PATCH] include: mfd: tps68470: Add masks for LEDA and LEDB
 Subject: [PATCH] include: mfd: tps68470: Add masks for LEDA and LEDB
@@ -1147,7 +1147,7 @@ index 7807fa329db0..2d2abb25b944 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From 155a68ad65f86c4660352fa046fb781d7d74a17c Mon Sep 17 00:00:00 2001
+From 8af1b18be10e3236b9bf98323c6be2794b7c0747 Mon Sep 17 00:00:00 2001
 From: Kate Hsuan <hpa@redhat.com>
 From: Kate Hsuan <hpa@redhat.com>
 Date: Tue, 21 Mar 2023 23:37:18 +0800
 Date: Tue, 21 Mar 2023 23:37:18 +0800
 Subject: [PATCH] leds: tps68470: Add LED control for tps68470
 Subject: [PATCH] leds: tps68470: Add LED control for tps68470

+ 2 - 2
patches/6.4/0012-amd-gpio.patch

@@ -1,4 +1,4 @@
-From 5140e4db732738d5e90bc91f1b638cd870f78577 Mon Sep 17 00:00:00 2001
+From 5a48de44411bdd2aa8112a09c721af53a432791b Mon Sep 17 00:00:00 2001
 From: Sachi King <nakato@nakato.io>
 From: Sachi King <nakato@nakato.io>
 Date: Sat, 29 May 2021 17:47:38 +1000
 Date: Sat, 29 May 2021 17:47:38 +1000
 Subject: [PATCH] ACPI: Add quirk for Surface Laptop 4 AMD missing irq 7
 Subject: [PATCH] ACPI: Add quirk for Surface Laptop 4 AMD missing irq 7
@@ -65,7 +65,7 @@ index 53369c57751e..1ec1a9015178 100644
 -- 
 -- 
 2.42.0
 2.42.0
 
 
-From cbc760a04e85b356ee67b389cc90081df8c5d092 Mon Sep 17 00:00:00 2001
+From 392d7ad028302a7fe48ef1dc87ba037aa8f50c93 Mon Sep 17 00:00:00 2001
 From: Maximilian Luz <luzmaximilian@gmail.com>
 From: Maximilian Luz <luzmaximilian@gmail.com>
 Date: Thu, 3 Jun 2021 14:04:26 +0200
 Date: Thu, 3 Jun 2021 14:04:26 +0200
 Subject: [PATCH] ACPI: Add AMD 13" Surface Laptop 4 model to irq 7 override
 Subject: [PATCH] ACPI: Add AMD 13" Surface Laptop 4 model to irq 7 override

+ 1 - 1
patches/6.4/0013-rtc.patch

@@ -1,4 +1,4 @@
-From 0c9b74d64ee08c290d67ad9aa511e5a8c2cb3a75 Mon Sep 17 00:00:00 2001
+From ae845ee6a2a7b3a080d791a3fe1c52e3fc5783ec Mon Sep 17 00:00:00 2001
 From: "Bart Groeneveld | GPX Solutions B.V" <bart@gpxbv.nl>
 From: "Bart Groeneveld | GPX Solutions B.V" <bart@gpxbv.nl>
 Date: Mon, 5 Dec 2022 16:08:46 +0100
 Date: Mon, 5 Dec 2022 16:08:46 +0100
 Subject: [PATCH] acpi: allow usage of acpi_tad on HW-reduced platforms
 Subject: [PATCH] acpi: allow usage of acpi_tad on HW-reduced platforms

+ 101 - 0
patches/6.5/0001-surface3-oemb.patch

@@ -0,0 +1,101 @@
+From 12a5bbe01502724f70dd634639ae1abbb438547c Mon Sep 17 00:00:00 2001
+From: Tsuchiya Yuto <kitakar@gmail.com>
+Date: Sun, 18 Oct 2020 16:42:44 +0900
+Subject: [PATCH] (surface3-oemb) add DMI matches for Surface 3 with broken DMI
+ table
+
+On some Surface 3, the DMI table gets corrupted for unknown reasons
+and breaks existing DMI matching used for device-specific quirks.
+
+This commit adds the (broken) DMI data into dmi_system_id tables used
+for quirks so that each driver can enable quirks even on the affected
+systems.
+
+On affected systems, DMI data will look like this:
+    $ grep . /sys/devices/virtual/dmi/id/{bios_vendor,board_name,board_vendor,\
+    chassis_vendor,product_name,sys_vendor}
+    /sys/devices/virtual/dmi/id/bios_vendor:American Megatrends Inc.
+    /sys/devices/virtual/dmi/id/board_name:OEMB
+    /sys/devices/virtual/dmi/id/board_vendor:OEMB
+    /sys/devices/virtual/dmi/id/chassis_vendor:OEMB
+    /sys/devices/virtual/dmi/id/product_name:OEMB
+    /sys/devices/virtual/dmi/id/sys_vendor:OEMB
+
+Expected:
+    $ grep . /sys/devices/virtual/dmi/id/{bios_vendor,board_name,board_vendor,\
+    chassis_vendor,product_name,sys_vendor}
+    /sys/devices/virtual/dmi/id/bios_vendor:American Megatrends Inc.
+    /sys/devices/virtual/dmi/id/board_name:Surface 3
+    /sys/devices/virtual/dmi/id/board_vendor:Microsoft Corporation
+    /sys/devices/virtual/dmi/id/chassis_vendor:Microsoft Corporation
+    /sys/devices/virtual/dmi/id/product_name:Surface 3
+    /sys/devices/virtual/dmi/id/sys_vendor:Microsoft Corporation
+
+Signed-off-by: Tsuchiya Yuto <kitakar@gmail.com>
+Patchset: surface3-oemb
+---
+ drivers/platform/surface/surface3-wmi.c           | 7 +++++++
+ sound/soc/codecs/rt5645.c                         | 9 +++++++++
+ sound/soc/intel/common/soc-acpi-intel-cht-match.c | 8 ++++++++
+ 3 files changed, 24 insertions(+)
+
+diff --git a/drivers/platform/surface/surface3-wmi.c b/drivers/platform/surface/surface3-wmi.c
+index ca4602bcc7de..490b9731068a 100644
+--- a/drivers/platform/surface/surface3-wmi.c
++++ b/drivers/platform/surface/surface3-wmi.c
+@@ -37,6 +37,13 @@ static const struct dmi_system_id surface3_dmi_table[] = {
+ 			DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"),
+ 		},
+ 	},
++	{
++		.matches = {
++			DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."),
++			DMI_MATCH(DMI_SYS_VENDOR, "OEMB"),
++			DMI_MATCH(DMI_PRODUCT_NAME, "OEMB"),
++		},
++	},
+ #endif
+ 	{ }
+ };
+diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
+index a506d940a2ea..2766484b8d2c 100644
+--- a/sound/soc/codecs/rt5645.c
++++ b/sound/soc/codecs/rt5645.c
+@@ -3717,6 +3717,15 @@ static const struct dmi_system_id dmi_platform_data[] = {
+ 		},
+ 		.driver_data = (void *)&intel_braswell_platform_data,
+ 	},
++	{
++		.ident = "Microsoft Surface 3",
++		.matches = {
++			DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."),
++			DMI_MATCH(DMI_SYS_VENDOR, "OEMB"),
++			DMI_MATCH(DMI_PRODUCT_NAME, "OEMB"),
++		},
++		.driver_data = (void *)&intel_braswell_platform_data,
++	},
+ 	{
+ 		/*
+ 		 * Match for the GPDwin which unfortunately uses somewhat
+diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c
+index cdcbf04b8832..958305779b12 100644
+--- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c
++++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c
+@@ -27,6 +27,14 @@ static const struct dmi_system_id cht_table[] = {
+ 			DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"),
+ 		},
+ 	},
++	{
++		.callback = cht_surface_quirk_cb,
++		.matches = {
++			DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."),
++			DMI_MATCH(DMI_SYS_VENDOR, "OEMB"),
++			DMI_MATCH(DMI_PRODUCT_NAME, "OEMB"),
++		},
++	},
+ 	{ }
+ };
+ 
+-- 
+2.42.0
+

+ 400 - 0
patches/6.5/0002-mwifiex.patch

@@ -0,0 +1,400 @@
+From 49318ff022593bd9ee50d006ef084652a01c8df8 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
+Date: Tue, 3 Nov 2020 13:28:04 +0100
+Subject: [PATCH] mwifiex: Add quirk resetting the PCI bridge on MS Surface
+ devices
+
+The most recent firmware of the 88W8897 card reports a hardcoded LTR
+value to the system during initialization, probably as an (unsuccessful)
+attempt of the developers to fix firmware crashes. This LTR value
+prevents most of the Microsoft Surface devices from entering deep
+powersaving states (either platform C-State 10 or S0ix state), because
+the exit latency of that state would be higher than what the card can
+tolerate.
+
+Turns out the card works just the same (including the firmware crashes)
+no matter if that hardcoded LTR value is reported or not, so it's kind
+of useless and only prevents us from saving power.
+
+To get rid of those hardcoded LTR reports, it's possible to reset the
+PCI bridge device after initializing the cards firmware. I'm not exactly
+sure why that works, maybe the power management subsystem of the PCH
+resets its stored LTR values when doing a function level reset of the
+bridge device. Doing the reset once after starting the wifi firmware
+works very well, probably because the firmware only reports that LTR
+value a single time during firmware startup.
+
+Patchset: mwifiex
+---
+ drivers/net/wireless/marvell/mwifiex/pcie.c   | 12 +++++++++
+ .../wireless/marvell/mwifiex/pcie_quirks.c    | 26 +++++++++++++------
+ .../wireless/marvell/mwifiex/pcie_quirks.h    |  1 +
+ 3 files changed, 31 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c
+index 6697132ecc97..f06b4ebc5bd8 100644
+--- a/drivers/net/wireless/marvell/mwifiex/pcie.c
++++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
+@@ -1771,9 +1771,21 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
+ static int mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter)
+ {
+ 	struct pcie_service_card *card = adapter->card;
++	struct pci_dev *pdev = card->dev;
++	struct pci_dev *parent_pdev = pci_upstream_bridge(pdev);
+ 	const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+ 	int tx_wrap = card->txbd_wrptr & reg->tx_wrap_mask;
+ 
++	/* Trigger a function level reset of the PCI bridge device, this makes
++	 * the firmware of PCIe 88W8897 cards stop reporting a fixed LTR value
++	 * that prevents the system from entering package C10 and S0ix powersaving
++	 * states.
++	 * We need to do it here because it must happen after firmware
++	 * initialization and this function is called after that is done.
++	 */
++	if (card->quirks & QUIRK_DO_FLR_ON_BRIDGE)
++		pci_reset_function(parent_pdev);
++
+ 	/* Write the RX ring read pointer in to reg->rx_rdptr */
+ 	if (mwifiex_write_reg(adapter, reg->rx_rdptr, card->rxbd_rdptr |
+ 			      tx_wrap)) {
+diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c
+index dd6d21f1dbfd..f46b06f8d643 100644
+--- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c
++++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c
+@@ -13,7 +13,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = {
+ 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+ 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"),
+ 		},
+-		.driver_data = (void *)QUIRK_FW_RST_D3COLD,
++		.driver_data = (void *)(QUIRK_FW_RST_D3COLD |
++					QUIRK_DO_FLR_ON_BRIDGE),
+ 	},
+ 	{
+ 		.ident = "Surface Pro 5",
+@@ -22,7 +23,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = {
+ 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+ 			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"),
+ 		},
+-		.driver_data = (void *)QUIRK_FW_RST_D3COLD,
++		.driver_data = (void *)(QUIRK_FW_RST_D3COLD |
++					QUIRK_DO_FLR_ON_BRIDGE),
+ 	},
+ 	{
+ 		.ident = "Surface Pro 5 (LTE)",
+@@ -31,7 +33,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = {
+ 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+ 			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"),
+ 		},
+-		.driver_data = (void *)QUIRK_FW_RST_D3COLD,
++		.driver_data = (void *)(QUIRK_FW_RST_D3COLD |
++					QUIRK_DO_FLR_ON_BRIDGE),
+ 	},
+ 	{
+ 		.ident = "Surface Pro 6",
+@@ -39,7 +42,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = {
+ 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+ 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"),
+ 		},
+-		.driver_data = (void *)QUIRK_FW_RST_D3COLD,
++		.driver_data = (void *)(QUIRK_FW_RST_D3COLD |
++					QUIRK_DO_FLR_ON_BRIDGE),
+ 	},
+ 	{
+ 		.ident = "Surface Book 1",
+@@ -47,7 +51,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = {
+ 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+ 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"),
+ 		},
+-		.driver_data = (void *)QUIRK_FW_RST_D3COLD,
++		.driver_data = (void *)(QUIRK_FW_RST_D3COLD |
++					QUIRK_DO_FLR_ON_BRIDGE),
+ 	},
+ 	{
+ 		.ident = "Surface Book 2",
+@@ -55,7 +60,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = {
+ 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+ 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"),
+ 		},
+-		.driver_data = (void *)QUIRK_FW_RST_D3COLD,
++		.driver_data = (void *)(QUIRK_FW_RST_D3COLD |
++					QUIRK_DO_FLR_ON_BRIDGE),
+ 	},
+ 	{
+ 		.ident = "Surface Laptop 1",
+@@ -63,7 +69,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = {
+ 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+ 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"),
+ 		},
+-		.driver_data = (void *)QUIRK_FW_RST_D3COLD,
++		.driver_data = (void *)(QUIRK_FW_RST_D3COLD |
++					QUIRK_DO_FLR_ON_BRIDGE),
+ 	},
+ 	{
+ 		.ident = "Surface Laptop 2",
+@@ -71,7 +78,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = {
+ 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+ 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"),
+ 		},
+-		.driver_data = (void *)QUIRK_FW_RST_D3COLD,
++		.driver_data = (void *)(QUIRK_FW_RST_D3COLD |
++					QUIRK_DO_FLR_ON_BRIDGE),
+ 	},
+ 	{}
+ };
+@@ -89,6 +97,8 @@ void mwifiex_initialize_quirks(struct pcie_service_card *card)
+ 		dev_info(&pdev->dev, "no quirks enabled\n");
+ 	if (card->quirks & QUIRK_FW_RST_D3COLD)
+ 		dev_info(&pdev->dev, "quirk reset_d3cold enabled\n");
++	if (card->quirks & QUIRK_DO_FLR_ON_BRIDGE)
++		dev_info(&pdev->dev, "quirk do_flr_on_bridge enabled\n");
+ }
+ 
+ static void mwifiex_pcie_set_power_d3cold(struct pci_dev *pdev)
+diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h
+index d6ff964aec5b..5d30ae39d65e 100644
+--- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h
++++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h
+@@ -4,6 +4,7 @@
+ #include "pcie.h"
+ 
+ #define QUIRK_FW_RST_D3COLD	BIT(0)
++#define QUIRK_DO_FLR_ON_BRIDGE	BIT(1)
+ 
+ void mwifiex_initialize_quirks(struct pcie_service_card *card);
+ int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev);
+-- 
+2.42.0
+
+From 727fa45140fe1e6ce389f7698ca7b67a67e8781f Mon Sep 17 00:00:00 2001
+From: Tsuchiya Yuto <kitakar@gmail.com>
+Date: Sun, 4 Oct 2020 00:11:49 +0900
+Subject: [PATCH] mwifiex: pcie: disable bridge_d3 for Surface gen4+
+
+Currently, mwifiex fw will crash after suspend on recent kernel series.
+On Windows, it seems that the root port of wifi will never enter D3 state
+(stay on D0 state). And on Linux, disabling the D3 state for the
+bridge fixes fw crashing after suspend.
+
+This commit disables the D3 state of root port on driver initialization
+and fixes fw crashing after suspend.
+
+Signed-off-by: Tsuchiya Yuto <kitakar@gmail.com>
+Patchset: mwifiex
+---
+ drivers/net/wireless/marvell/mwifiex/pcie.c   |  7 +++++
+ .../wireless/marvell/mwifiex/pcie_quirks.c    | 27 +++++++++++++------
+ .../wireless/marvell/mwifiex/pcie_quirks.h    |  1 +
+ 3 files changed, 27 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c
+index f06b4ebc5bd8..07f13b52ddb9 100644
+--- a/drivers/net/wireless/marvell/mwifiex/pcie.c
++++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
+@@ -370,6 +370,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
+ 					const struct pci_device_id *ent)
+ {
+ 	struct pcie_service_card *card;
++	struct pci_dev *parent_pdev = pci_upstream_bridge(pdev);
+ 	int ret;
+ 
+ 	pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n",
+@@ -411,6 +412,12 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
+ 		return -1;
+ 	}
+ 
++	/* disable bridge_d3 for Surface gen4+ devices to fix fw crashing
++	 * after suspend
++	 */
++	if (card->quirks & QUIRK_NO_BRIDGE_D3)
++		parent_pdev->bridge_d3 = false;
++
+ 	return 0;
+ }
+ 
+diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c
+index f46b06f8d643..99b024ecbade 100644
+--- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c
++++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c
+@@ -14,7 +14,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = {
+ 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"),
+ 		},
+ 		.driver_data = (void *)(QUIRK_FW_RST_D3COLD |
+-					QUIRK_DO_FLR_ON_BRIDGE),
++					QUIRK_DO_FLR_ON_BRIDGE |
++					QUIRK_NO_BRIDGE_D3),
+ 	},
+ 	{
+ 		.ident = "Surface Pro 5",
+@@ -24,7 +25,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = {
+ 			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"),
+ 		},
+ 		.driver_data = (void *)(QUIRK_FW_RST_D3COLD |
+-					QUIRK_DO_FLR_ON_BRIDGE),
++					QUIRK_DO_FLR_ON_BRIDGE |
++					QUIRK_NO_BRIDGE_D3),
+ 	},
+ 	{
+ 		.ident = "Surface Pro 5 (LTE)",
+@@ -34,7 +36,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = {
+ 			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"),
+ 		},
+ 		.driver_data = (void *)(QUIRK_FW_RST_D3COLD |
+-					QUIRK_DO_FLR_ON_BRIDGE),
++					QUIRK_DO_FLR_ON_BRIDGE |
++					QUIRK_NO_BRIDGE_D3),
+ 	},
+ 	{
+ 		.ident = "Surface Pro 6",
+@@ -43,7 +46,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = {
+ 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"),
+ 		},
+ 		.driver_data = (void *)(QUIRK_FW_RST_D3COLD |
+-					QUIRK_DO_FLR_ON_BRIDGE),
++					QUIRK_DO_FLR_ON_BRIDGE |
++					QUIRK_NO_BRIDGE_D3),
+ 	},
+ 	{
+ 		.ident = "Surface Book 1",
+@@ -52,7 +56,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = {
+ 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"),
+ 		},
+ 		.driver_data = (void *)(QUIRK_FW_RST_D3COLD |
+-					QUIRK_DO_FLR_ON_BRIDGE),
++					QUIRK_DO_FLR_ON_BRIDGE |
++					QUIRK_NO_BRIDGE_D3),
+ 	},
+ 	{
+ 		.ident = "Surface Book 2",
+@@ -61,7 +66,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = {
+ 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"),
+ 		},
+ 		.driver_data = (void *)(QUIRK_FW_RST_D3COLD |
+-					QUIRK_DO_FLR_ON_BRIDGE),
++					QUIRK_DO_FLR_ON_BRIDGE |
++					QUIRK_NO_BRIDGE_D3),
+ 	},
+ 	{
+ 		.ident = "Surface Laptop 1",
+@@ -70,7 +76,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = {
+ 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"),
+ 		},
+ 		.driver_data = (void *)(QUIRK_FW_RST_D3COLD |
+-					QUIRK_DO_FLR_ON_BRIDGE),
++					QUIRK_DO_FLR_ON_BRIDGE |
++					QUIRK_NO_BRIDGE_D3),
+ 	},
+ 	{
+ 		.ident = "Surface Laptop 2",
+@@ -79,7 +86,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = {
+ 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"),
+ 		},
+ 		.driver_data = (void *)(QUIRK_FW_RST_D3COLD |
+-					QUIRK_DO_FLR_ON_BRIDGE),
++					QUIRK_DO_FLR_ON_BRIDGE |
++					QUIRK_NO_BRIDGE_D3),
+ 	},
+ 	{}
+ };
+@@ -99,6 +107,9 @@ void mwifiex_initialize_quirks(struct pcie_service_card *card)
+ 		dev_info(&pdev->dev, "quirk reset_d3cold enabled\n");
+ 	if (card->quirks & QUIRK_DO_FLR_ON_BRIDGE)
+ 		dev_info(&pdev->dev, "quirk do_flr_on_bridge enabled\n");
++	if (card->quirks & QUIRK_NO_BRIDGE_D3)
++		dev_info(&pdev->dev,
++			 "quirk no_brigde_d3 enabled\n");
+ }
+ 
+ static void mwifiex_pcie_set_power_d3cold(struct pci_dev *pdev)
+diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h
+index 5d30ae39d65e..c14eb56eb911 100644
+--- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h
++++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h
+@@ -5,6 +5,7 @@
+ 
+ #define QUIRK_FW_RST_D3COLD	BIT(0)
+ #define QUIRK_DO_FLR_ON_BRIDGE	BIT(1)
++#define QUIRK_NO_BRIDGE_D3	BIT(2)
+ 
+ void mwifiex_initialize_quirks(struct pcie_service_card *card);
+ int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev);
+-- 
+2.42.0
+
+From e265bfd909c9f17867eb68d6446f238a450d8e78 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
+Date: Thu, 25 Mar 2021 11:33:02 +0100
+Subject: [PATCH] Bluetooth: btusb: Lower passive lescan interval on Marvell
+ 88W8897
+
+The Marvell 88W8897 combined wifi and bluetooth card (pcie+usb version)
+is used in a lot of Microsoft Surface devices, and all those devices
+suffer from very low 2.4GHz wifi connection speeds while bluetooth is
+enabled. The reason for that is that the default passive scanning
+interval for Bluetooth Low Energy devices is quite high in Linux
+(interval of 60 msec and scan window of 30 msec, see hci_core.c), and
+the Marvell chip is known for its bad bt+wifi coexisting performance.
+
+So decrease that passive scan interval and make the scan window shorter
+on this particular device to allow for spending more time transmitting
+wifi signals: The new scan interval is 250 msec (0x190 * 0.625 msec) and
+the new scan window is 6.25 msec (0xa * 0,625 msec).
+
+This change has a very large impact on the 2.4GHz wifi speeds and gets
+it up to performance comparable with the Windows driver, which seems to
+apply a similar quirk.
+
+The interval and window length were tested and found to work very well
+with a lot of Bluetooth Low Energy devices, including the Surface Pen, a
+Bluetooth Speaker and two modern Bluetooth headphones. All devices were
+discovered immediately after turning them on. Even lower values were
+also tested, but they introduced longer delays until devices get
+discovered.
+
+Patchset: mwifiex
+---
+ drivers/bluetooth/btusb.c | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
+index dfdfb72d350f..44ef02efba46 100644
+--- a/drivers/bluetooth/btusb.c
++++ b/drivers/bluetooth/btusb.c
+@@ -65,6 +65,7 @@ static struct usb_driver btusb_driver;
+ #define BTUSB_INTEL_BROKEN_INITIAL_NCMD BIT(25)
+ #define BTUSB_INTEL_NO_WBS_SUPPORT	BIT(26)
+ #define BTUSB_ACTIONS_SEMI		BIT(27)
++#define BTUSB_LOWER_LESCAN_INTERVAL	BIT(28)
+ 
+ static const struct usb_device_id btusb_table[] = {
+ 	/* Generic Bluetooth USB device */
+@@ -468,6 +469,7 @@ static const struct usb_device_id blacklist_table[] = {
+ 	{ USB_DEVICE(0x1286, 0x2044), .driver_info = BTUSB_MARVELL },
+ 	{ USB_DEVICE(0x1286, 0x2046), .driver_info = BTUSB_MARVELL },
+ 	{ USB_DEVICE(0x1286, 0x204e), .driver_info = BTUSB_MARVELL },
++	{ USB_DEVICE(0x1286, 0x204c), .driver_info = BTUSB_LOWER_LESCAN_INTERVAL },
+ 
+ 	/* Intel Bluetooth devices */
+ 	{ USB_DEVICE(0x8087, 0x0025), .driver_info = BTUSB_INTEL_COMBINED },
+@@ -4317,6 +4319,19 @@ static int btusb_probe(struct usb_interface *intf,
+ 	if (id->driver_info & BTUSB_MARVELL)
+ 		hdev->set_bdaddr = btusb_set_bdaddr_marvell;
+ 
++	/* The Marvell 88W8897 combined wifi and bluetooth card is known for
++	 * very bad bt+wifi coexisting performance.
++	 *
++	 * Decrease the passive BT Low Energy scan interval a bit
++	 * (0x0190 * 0.625 msec = 250 msec) and make the scan window shorter
++	 * (0x000a * 0,625 msec = 6.25 msec). This allows for significantly
++	 * higher wifi throughput while passively scanning for BT LE devices.
++	 */
++	if (id->driver_info & BTUSB_LOWER_LESCAN_INTERVAL) {
++		hdev->le_scan_interval = 0x0190;
++		hdev->le_scan_window = 0x000a;
++	}
++
+ 	if (IS_ENABLED(CONFIG_BT_HCIBTUSB_MTK) &&
+ 	    (id->driver_info & BTUSB_MEDIATEK)) {
+ 		hdev->setup = btusb_mtk_setup;
+-- 
+2.42.0
+

+ 121 - 0
patches/6.5/0003-ath10k.patch

@@ -0,0 +1,121 @@
+From e07ae09261bc155f332dca703a12f3ce2891f35f Mon Sep 17 00:00:00 2001
+From: Maximilian Luz <luzmaximilian@gmail.com>
+Date: Sat, 27 Feb 2021 00:45:52 +0100
+Subject: [PATCH] ath10k: Add module parameters to override board files
+
+Some Surface devices, specifically the Surface Go and AMD version of the
+Surface Laptop 3 (wich both come with QCA6174 WiFi chips), work better
+with a different board file, as it seems that the firmeware included
+upstream is buggy.
+
+As it is generally not a good idea to randomly overwrite files, let
+alone doing so via packages, we add module parameters to override those
+file names in the driver. This allows us to package/deploy the override
+via a modprobe.d config.
+
+Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
+Patchset: ath10k
+---
+ drivers/net/wireless/ath/ath10k/core.c | 58 ++++++++++++++++++++++++++
+ 1 file changed, 58 insertions(+)
+
+diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
+index 6cdb225b7eac..19c036751fb1 100644
+--- a/drivers/net/wireless/ath/ath10k/core.c
++++ b/drivers/net/wireless/ath/ath10k/core.c
+@@ -38,6 +38,9 @@ static bool fw_diag_log;
+ /* frame mode values are mapped as per enum ath10k_hw_txrx_mode */
+ unsigned int ath10k_frame_mode = ATH10K_HW_TXRX_NATIVE_WIFI;
+ 
++static char *override_board = "";
++static char *override_board2 = "";
++
+ unsigned long ath10k_coredump_mask = BIT(ATH10K_FW_CRASH_DUMP_REGISTERS) |
+ 				     BIT(ATH10K_FW_CRASH_DUMP_CE_DATA);
+ 
+@@ -50,6 +53,9 @@ module_param(fw_diag_log, bool, 0644);
+ module_param_named(frame_mode, ath10k_frame_mode, uint, 0644);
+ module_param_named(coredump_mask, ath10k_coredump_mask, ulong, 0444);
+ 
++module_param(override_board, charp, 0644);
++module_param(override_board2, charp, 0644);
++
+ MODULE_PARM_DESC(debug_mask, "Debugging mask");
+ MODULE_PARM_DESC(uart_print, "Uart target debugging");
+ MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode");
+@@ -59,6 +65,9 @@ MODULE_PARM_DESC(frame_mode,
+ MODULE_PARM_DESC(coredump_mask, "Bitfield of what to include in firmware crash file");
+ MODULE_PARM_DESC(fw_diag_log, "Diag based fw log debugging");
+ 
++MODULE_PARM_DESC(override_board, "Override for board.bin file");
++MODULE_PARM_DESC(override_board2, "Override for board-2.bin file");
++
+ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
+ 	{
+ 		.id = QCA988X_HW_2_0_VERSION,
+@@ -911,6 +920,42 @@ static int ath10k_init_configure_target(struct ath10k *ar)
+ 	return 0;
+ }
+ 
++static const char *ath10k_override_board_fw_file(struct ath10k *ar,
++						 const char *file)
++{
++	if (strcmp(file, "board.bin") == 0) {
++		if (strcmp(override_board, "") == 0)
++			return file;
++
++		if (strcmp(override_board, "none") == 0) {
++			dev_info(ar->dev, "firmware override: pretending 'board.bin' does not exist\n");
++			return NULL;
++		}
++
++		dev_info(ar->dev, "firmware override: replacing 'board.bin' with '%s'\n",
++			 override_board);
++
++		return override_board;
++	}
++
++	if (strcmp(file, "board-2.bin") == 0) {
++		if (strcmp(override_board2, "") == 0)
++			return file;
++
++		if (strcmp(override_board2, "none") == 0) {
++			dev_info(ar->dev, "firmware override: pretending 'board-2.bin' does not exist\n");
++			return NULL;
++		}
++
++		dev_info(ar->dev, "firmware override: replacing 'board-2.bin' with '%s'\n",
++			 override_board2);
++
++		return override_board2;
++	}
++
++	return file;
++}
++
+ static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar,
+ 						   const char *dir,
+ 						   const char *file)
+@@ -925,6 +970,19 @@ static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar,
+ 	if (dir == NULL)
+ 		dir = ".";
+ 
++	/* HACK: Override board.bin and board-2.bin files if specified.
++	 *
++	 * Some Surface devices perform better with a different board
++	 * configuration. To this end, one would need to replace the board.bin
++	 * file with the modified config and remove the board-2.bin file.
++	 * Unfortunately, that's not a solution that we can easily package. So
++	 * we add module options to perform these overrides here.
++	 */
++
++	file = ath10k_override_board_fw_file(ar, file);
++	if (!file)
++		return ERR_PTR(-ENOENT);
++
+ 	snprintf(filename, sizeof(filename), "%s/%s", dir, file);
+ 	ret = firmware_request_nowarn(&fw, filename, ar->dev);
+ 	ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot fw request '%s': %d\n",
+-- 
+2.42.0
+

+ 3230 - 0
patches/6.5/0004-ipts.patch

@@ -0,0 +1,3230 @@
+From eb9f77870820a66f7a5e4585f6f2b33b354615ca Mon Sep 17 00:00:00 2001
+From: Dorian Stoll <dorian.stoll@tmsp.io>
+Date: Thu, 30 Jul 2020 13:21:53 +0200
+Subject: [PATCH] misc: mei: Add missing IPTS device IDs
+
+Patchset: ipts
+---
+ drivers/misc/mei/hw-me-regs.h | 1 +
+ drivers/misc/mei/pci-me.c     | 1 +
+ 2 files changed, 2 insertions(+)
+
+diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h
+index bdc65d50b945..08723c01d727 100644
+--- a/drivers/misc/mei/hw-me-regs.h
++++ b/drivers/misc/mei/hw-me-regs.h
+@@ -92,6 +92,7 @@
+ #define MEI_DEV_ID_CDF        0x18D3  /* Cedar Fork */
+ 
+ #define MEI_DEV_ID_ICP_LP     0x34E0  /* Ice Lake Point LP */
++#define MEI_DEV_ID_ICP_LP_3   0x34E4  /* Ice Lake Point LP 3 (iTouch) */
+ #define MEI_DEV_ID_ICP_N      0x38E0  /* Ice Lake Point N */
+ 
+ #define MEI_DEV_ID_JSP_N      0x4DE0  /* Jasper Lake Point N */
+diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c
+index 676d566f38dd..6b37dd1f8b2a 100644
+--- a/drivers/misc/mei/pci-me.c
++++ b/drivers/misc/mei/pci-me.c
+@@ -97,6 +97,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
+ 	{MEI_PCI_DEVICE(MEI_DEV_ID_CMP_H_3, MEI_ME_PCH8_ITOUCH_CFG)},
+ 
+ 	{MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)},
++	{MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP_3, MEI_ME_PCH12_CFG)},
+ 	{MEI_PCI_DEVICE(MEI_DEV_ID_ICP_N, MEI_ME_PCH12_CFG)},
+ 
+ 	{MEI_PCI_DEVICE(MEI_DEV_ID_TGP_LP, MEI_ME_PCH15_CFG)},
+-- 
+2.42.0
+
+From b01fede6915508acdbdec41c29fd3c8740b6f082 Mon Sep 17 00:00:00 2001
+From: Liban Hannan <liban.p@gmail.com>
+Date: Tue, 12 Apr 2022 23:31:12 +0100
+Subject: [PATCH] iommu: ipts: use IOMMU passthrough mode for IPTS
+
+Adds a quirk so that IOMMU uses passthrough mode for the IPTS device.
+Otherwise, when IOMMU is enabled, IPTS produces DMAR errors like:
+
+DMAR: [DMA Read NO_PASID] Request device [00:16.4] fault addr
+0x104ea3000 [fault reason 0x06] PTE Read access is not set
+
+This is very similar to the bug described at:
+https://bugs.launchpad.net/bugs/1958004
+
+Fixed with the following patch which this patch basically copies:
+https://launchpadlibrarian.net/586396847/43255ca.diff
+Patchset: ipts
+---
+ drivers/iommu/intel/iommu.c | 24 ++++++++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
+index 5c8c5cdc36cf..fc4799415c3c 100644
+--- a/drivers/iommu/intel/iommu.c
++++ b/drivers/iommu/intel/iommu.c
+@@ -37,6 +37,8 @@
+ #define IS_GFX_DEVICE(pdev) ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY)
+ #define IS_USB_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_SERIAL_USB)
+ #define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA)
++#define IS_IPTS(pdev) ((pdev)->vendor == PCI_VENDOR_ID_INTEL &&	\
++			    ((pdev)->device == 0x9d3e))
+ #define IS_AZALIA(pdev) ((pdev)->vendor == 0x8086 && (pdev)->device == 0x3a3e)
+ 
+ #define IOAPIC_RANGE_START	(0xfee00000)
+@@ -287,12 +289,14 @@ int intel_iommu_enabled = 0;
+ EXPORT_SYMBOL_GPL(intel_iommu_enabled);
+ 
+ static int dmar_map_gfx = 1;
++static int dmar_map_ipts = 1;
+ static int intel_iommu_superpage = 1;
+ static int iommu_identity_mapping;
+ static int iommu_skip_te_disable;
+ 
+ #define IDENTMAP_GFX		2
+ #define IDENTMAP_AZALIA		4
++#define IDENTMAP_IPTS		16
+ 
+ const struct iommu_ops intel_iommu_ops;
+ 
+@@ -2548,6 +2552,9 @@ static int device_def_domain_type(struct device *dev)
+ 
+ 		if ((iommu_identity_mapping & IDENTMAP_GFX) && IS_GFX_DEVICE(pdev))
+ 			return IOMMU_DOMAIN_IDENTITY;
++
++		if ((iommu_identity_mapping & IDENTMAP_IPTS) && IS_IPTS(pdev))
++			return IOMMU_DOMAIN_IDENTITY;
+ 	}
+ 
+ 	return 0;
+@@ -2855,6 +2862,9 @@ static int __init init_dmars(void)
+ 	if (!dmar_map_gfx)
+ 		iommu_identity_mapping |= IDENTMAP_GFX;
+ 
++	if (!dmar_map_ipts)
++		iommu_identity_mapping |= IDENTMAP_IPTS;
++
+ 	check_tylersburg_isoch();
+ 
+ 	ret = si_domain_init(hw_pass_through);
+@@ -4771,6 +4781,17 @@ static void quirk_iommu_igfx(struct pci_dev *dev)
+ 	dmar_map_gfx = 0;
+ }
+ 
++static void quirk_iommu_ipts(struct pci_dev *dev)
++{
++	if (!IS_IPTS(dev))
++		return;
++
++	if (risky_device(dev))
++		return;
++
++	pci_info(dev, "Passthrough IOMMU for IPTS\n");
++	dmar_map_ipts = 0;
++}
+ /* G4x/GM45 integrated gfx dmar support is totally busted. */
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_igfx);
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e00, quirk_iommu_igfx);
+@@ -4806,6 +4827,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx);
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163A, quirk_iommu_igfx);
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163D, quirk_iommu_igfx);
+ 
++/* disable IPTS dmar support */
++DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9D3E, quirk_iommu_ipts);
++
+ static void quirk_iommu_rwbf(struct pci_dev *dev)
+ {
+ 	if (risky_device(dev))
+-- 
+2.42.0
+
+From b9358540674348b39d248a02ec314ba8ad6016d9 Mon Sep 17 00:00:00 2001
+From: Dorian Stoll <dorian.stoll@tmsp.io>
+Date: Sun, 11 Dec 2022 12:00:59 +0100
+Subject: [PATCH] hid: Add support for Intel Precise Touch and Stylus
+
+Based on linux-surface/intel-precise-touch@8abe268
+
+Signed-off-by: Dorian Stoll <dorian.stoll@tmsp.io>
+Patchset: ipts
+---
+ drivers/hid/Kconfig            |   2 +
+ drivers/hid/Makefile           |   2 +
+ drivers/hid/ipts/Kconfig       |  14 +
+ drivers/hid/ipts/Makefile      |  16 ++
+ drivers/hid/ipts/cmd.c         |  61 +++++
+ drivers/hid/ipts/cmd.h         |  60 ++++
+ drivers/hid/ipts/context.h     |  52 ++++
+ drivers/hid/ipts/control.c     | 486 +++++++++++++++++++++++++++++++++
+ drivers/hid/ipts/control.h     | 126 +++++++++
+ drivers/hid/ipts/desc.h        |  80 ++++++
+ drivers/hid/ipts/eds1.c        | 103 +++++++
+ drivers/hid/ipts/eds1.h        |  35 +++
+ drivers/hid/ipts/eds2.c        | 144 ++++++++++
+ drivers/hid/ipts/eds2.h        |  35 +++
+ drivers/hid/ipts/hid.c         | 225 +++++++++++++++
+ drivers/hid/ipts/hid.h         |  24 ++
+ drivers/hid/ipts/main.c        | 126 +++++++++
+ drivers/hid/ipts/mei.c         | 188 +++++++++++++
+ drivers/hid/ipts/mei.h         |  66 +++++
+ drivers/hid/ipts/receiver.c    | 250 +++++++++++++++++
+ drivers/hid/ipts/receiver.h    |  16 ++
+ drivers/hid/ipts/resources.c   | 131 +++++++++
+ drivers/hid/ipts/resources.h   |  41 +++
+ drivers/hid/ipts/spec-data.h   | 100 +++++++
+ drivers/hid/ipts/spec-device.h | 290 ++++++++++++++++++++
+ drivers/hid/ipts/spec-hid.h    |  34 +++
+ drivers/hid/ipts/thread.c      |  84 ++++++
+ drivers/hid/ipts/thread.h      |  59 ++++
+ 28 files changed, 2850 insertions(+)
+ create mode 100644 drivers/hid/ipts/Kconfig
+ create mode 100644 drivers/hid/ipts/Makefile
+ create mode 100644 drivers/hid/ipts/cmd.c
+ create mode 100644 drivers/hid/ipts/cmd.h
+ create mode 100644 drivers/hid/ipts/context.h
+ create mode 100644 drivers/hid/ipts/control.c
+ create mode 100644 drivers/hid/ipts/control.h
+ create mode 100644 drivers/hid/ipts/desc.h
+ create mode 100644 drivers/hid/ipts/eds1.c
+ create mode 100644 drivers/hid/ipts/eds1.h
+ create mode 100644 drivers/hid/ipts/eds2.c
+ create mode 100644 drivers/hid/ipts/eds2.h
+ create mode 100644 drivers/hid/ipts/hid.c
+ create mode 100644 drivers/hid/ipts/hid.h
+ create mode 100644 drivers/hid/ipts/main.c
+ create mode 100644 drivers/hid/ipts/mei.c
+ create mode 100644 drivers/hid/ipts/mei.h
+ create mode 100644 drivers/hid/ipts/receiver.c
+ create mode 100644 drivers/hid/ipts/receiver.h
+ create mode 100644 drivers/hid/ipts/resources.c
+ create mode 100644 drivers/hid/ipts/resources.h
+ create mode 100644 drivers/hid/ipts/spec-data.h
+ create mode 100644 drivers/hid/ipts/spec-device.h
+ create mode 100644 drivers/hid/ipts/spec-hid.h
+ create mode 100644 drivers/hid/ipts/thread.c
+ create mode 100644 drivers/hid/ipts/thread.h
+
+diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
+index e11c1c803676..54f45bdf663e 100644
+--- a/drivers/hid/Kconfig
++++ b/drivers/hid/Kconfig
+@@ -1334,4 +1334,6 @@ source "drivers/hid/amd-sfh-hid/Kconfig"
+ 
+ source "drivers/hid/surface-hid/Kconfig"
+ 
++source "drivers/hid/ipts/Kconfig"
++
+ endif # HID_SUPPORT
+diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
+index 7a9e160158f7..f58610f27216 100644
+--- a/drivers/hid/Makefile
++++ b/drivers/hid/Makefile
+@@ -168,3 +168,5 @@ obj-$(INTEL_ISH_FIRMWARE_DOWNLOADER)	+= intel-ish-hid/
+ obj-$(CONFIG_AMD_SFH_HID)       += amd-sfh-hid/
+ 
+ obj-$(CONFIG_SURFACE_HID_CORE)  += surface-hid/
++
++obj-$(CONFIG_HID_IPTS)          += ipts/
+diff --git a/drivers/hid/ipts/Kconfig b/drivers/hid/ipts/Kconfig
+new file mode 100644
+index 000000000000..297401bd388d
+--- /dev/null
++++ b/drivers/hid/ipts/Kconfig
+@@ -0,0 +1,14 @@
++# SPDX-License-Identifier: GPL-2.0-or-later
++
++config HID_IPTS
++	tristate "Intel Precise Touch & Stylus"
++	depends on INTEL_MEI
++	depends on HID
++	help
++	  Say Y here if your system has a touchscreen using Intels
++	  Precise Touch & Stylus (IPTS) technology.
++
++	  If unsure say N.
++
++	  To compile this driver as a module, choose M here: the
++	  module will be called ipts.
+diff --git a/drivers/hid/ipts/Makefile b/drivers/hid/ipts/Makefile
+new file mode 100644
+index 000000000000..883896f68e6a
+--- /dev/null
++++ b/drivers/hid/ipts/Makefile
+@@ -0,0 +1,16 @@
++# SPDX-License-Identifier: GPL-2.0-or-later
++#
++# Makefile for the IPTS touchscreen driver
++#
++
++obj-$(CONFIG_HID_IPTS) += ipts.o
++ipts-objs := cmd.o
++ipts-objs += control.o
++ipts-objs += eds1.o
++ipts-objs += eds2.o
++ipts-objs += hid.o
++ipts-objs += main.o
++ipts-objs += mei.o
++ipts-objs += receiver.o
++ipts-objs += resources.o
++ipts-objs += thread.o
+diff --git a/drivers/hid/ipts/cmd.c b/drivers/hid/ipts/cmd.c
+new file mode 100644
+index 000000000000..63a4934bbc5f
+--- /dev/null
++++ b/drivers/hid/ipts/cmd.c
+@@ -0,0 +1,61 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (c) 2020-2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#include <linux/errno.h>
++#include <linux/types.h>
++
++#include "cmd.h"
++#include "context.h"
++#include "mei.h"
++#include "spec-device.h"
++
++int ipts_cmd_recv_timeout(struct ipts_context *ipts, enum ipts_command_code code,
++			  struct ipts_response *rsp, u64 timeout)
++{
++	int ret = 0;
++
++	if (!ipts)
++		return -EFAULT;
++
++	if (!rsp)
++		return -EFAULT;
++
++	/*
++	 * In a response, the command code will have the most significant bit flipped to 1.
++	 * If code is passed to ipts_mei_recv as is, no messages will be received.
++	 */
++	ret = ipts_mei_recv(&ipts->mei, code | IPTS_RSP_BIT, rsp, timeout);
++	if (ret < 0)
++		return ret;
++
++	dev_dbg(ipts->dev, "Received 0x%02X with status 0x%02X\n", code, rsp->status);
++
++	/*
++	 * Some devices will always return this error.
++	 * It is allowed to ignore it and to try continuing.
++	 */
++	if (rsp->status == IPTS_STATUS_COMPAT_CHECK_FAIL)
++		rsp->status = IPTS_STATUS_SUCCESS;
++
++	return 0;
++}
++
++int ipts_cmd_send(struct ipts_context *ipts, enum ipts_command_code code, void *data, size_t size)
++{
++	struct ipts_command cmd = { 0 };
++
++	if (!ipts)
++		return -EFAULT;
++
++	cmd.cmd = code;
++
++	if (data && size > 0)
++		memcpy(cmd.payload, data, size);
++
++	dev_dbg(ipts->dev, "Sending 0x%02X with %ld bytes payload\n", code, size);
++	return ipts_mei_send(&ipts->mei, &cmd, sizeof(cmd.cmd) + size);
++}
+diff --git a/drivers/hid/ipts/cmd.h b/drivers/hid/ipts/cmd.h
+new file mode 100644
+index 000000000000..2b4079075b64
+--- /dev/null
++++ b/drivers/hid/ipts/cmd.h
+@@ -0,0 +1,60 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * Copyright (c) 2020-2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#ifndef IPTS_CMD_H
++#define IPTS_CMD_H
++
++#include <linux/types.h>
++
++#include "context.h"
++#include "spec-device.h"
++
++/*
++ * The default timeout for receiving responses
++ */
++#define IPTS_CMD_DEFAULT_TIMEOUT 1000
++
++/**
++ * ipts_cmd_recv_timeout() - Receives a response to a command.
++ * @ipts: The IPTS driver context.
++ * @code: The type of the command / response.
++ * @rsp: The address that the received response will be copied to.
++ * @timeout: How many milliseconds the function will wait at most.
++ *
++ * A negative timeout means to wait forever.
++ *
++ * Returns: 0 on success, <0 on error, -EAGAIN if no response has been received.
++ */
++int ipts_cmd_recv_timeout(struct ipts_context *ipts, enum ipts_command_code code,
++			  struct ipts_response *rsp, u64 timeout);
++
++/**
++ * ipts_cmd_recv() - Receives a response to a command.
++ * @ipts: The IPTS driver context.
++ * @code: The type of the command / response.
++ * @rsp: The address that the received response will be copied to.
++ *
++ * Returns: 0 on success, <0 on error, -EAGAIN if no response has been received.
++ */
++static inline int ipts_cmd_recv(struct ipts_context *ipts, enum ipts_command_code code,
++				struct ipts_response *rsp)
++{
++	return ipts_cmd_recv_timeout(ipts, code, rsp, IPTS_CMD_DEFAULT_TIMEOUT);
++}
++
++/**
++ * ipts_cmd_send() - Executes a command on the device.
++ * @ipts: The IPTS driver context.
++ * @code: The type of the command to execute.
++ * @data: The payload containing parameters for the command.
++ * @size: The size of the payload.
++ *
++ * Returns: 0 on success, <0 on error.
++ */
++int ipts_cmd_send(struct ipts_context *ipts, enum ipts_command_code code, void *data, size_t size);
++
++#endif /* IPTS_CMD_H */
+diff --git a/drivers/hid/ipts/context.h b/drivers/hid/ipts/context.h
+new file mode 100644
+index 000000000000..ba33259f1f7c
+--- /dev/null
++++ b/drivers/hid/ipts/context.h
+@@ -0,0 +1,52 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * Copyright (c) 2020-2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#ifndef IPTS_CONTEXT_H
++#define IPTS_CONTEXT_H
++
++#include <linux/completion.h>
++#include <linux/device.h>
++#include <linux/hid.h>
++#include <linux/mei_cl_bus.h>
++#include <linux/mutex.h>
++#include <linux/sched.h>
++#include <linux/types.h>
++
++#include "mei.h"
++#include "resources.h"
++#include "spec-device.h"
++#include "thread.h"
++
++struct ipts_context {
++	struct device *dev;
++	struct ipts_mei mei;
++
++	enum ipts_mode mode;
++
++	/*
++	 * Prevents concurrent GET_FEATURE reports.
++	 */
++	struct mutex feature_lock;
++	struct completion feature_event;
++
++	/*
++	 * These are not inside of struct ipts_resources
++	 * because they don't own the memory they point to.
++	 */
++	struct ipts_buffer feature_report;
++	struct ipts_buffer descriptor;
++
++	bool hid_active;
++	struct hid_device *hid;
++
++	struct ipts_device_info info;
++	struct ipts_resources resources;
++
++	struct ipts_thread receiver_loop;
++};
++
++#endif /* IPTS_CONTEXT_H */
+diff --git a/drivers/hid/ipts/control.c b/drivers/hid/ipts/control.c
+new file mode 100644
+index 000000000000..5360842d260b
+--- /dev/null
++++ b/drivers/hid/ipts/control.c
+@@ -0,0 +1,486 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (c) 2020-2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#include <linux/delay.h>
++#include <linux/dev_printk.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/kthread.h>
++#include <linux/types.h>
++
++#include "cmd.h"
++#include "context.h"
++#include "control.h"
++#include "desc.h"
++#include "hid.h"
++#include "receiver.h"
++#include "resources.h"
++#include "spec-data.h"
++#include "spec-device.h"
++
++static int ipts_control_get_device_info(struct ipts_context *ipts, struct ipts_device_info *info)
++{
++	int ret = 0;
++	struct ipts_response rsp = { 0 };
++
++	if (!ipts)
++		return -EFAULT;
++
++	if (!info)
++		return -EFAULT;
++
++	ret = ipts_cmd_send(ipts, IPTS_CMD_GET_DEVICE_INFO, NULL, 0);
++	if (ret) {
++		dev_err(ipts->dev, "GET_DEVICE_INFO: send failed: %d\n", ret);
++		return ret;
++	}
++
++	ret = ipts_cmd_recv(ipts, IPTS_CMD_GET_DEVICE_INFO, &rsp);
++	if (ret) {
++		dev_err(ipts->dev, "GET_DEVICE_INFO: recv failed: %d\n", ret);
++		return ret;
++	}
++
++	if (rsp.status != IPTS_STATUS_SUCCESS) {
++		dev_err(ipts->dev, "GET_DEVICE_INFO: cmd failed: %d\n", rsp.status);
++		return -EBADR;
++	}
++
++	memcpy(info, rsp.payload, sizeof(*info));
++	return 0;
++}
++
++static int ipts_control_set_mode(struct ipts_context *ipts, enum ipts_mode mode)
++{
++	int ret = 0;
++	struct ipts_set_mode cmd = { 0 };
++	struct ipts_response rsp = { 0 };
++
++	if (!ipts)
++		return -EFAULT;
++
++	cmd.mode = mode;
++
++	ret = ipts_cmd_send(ipts, IPTS_CMD_SET_MODE, &cmd, sizeof(cmd));
++	if (ret) {
++		dev_err(ipts->dev, "SET_MODE: send failed: %d\n", ret);
++		return ret;
++	}
++
++	ret = ipts_cmd_recv(ipts, IPTS_CMD_SET_MODE, &rsp);
++	if (ret) {
++		dev_err(ipts->dev, "SET_MODE: recv failed: %d\n", ret);
++		return ret;
++	}
++
++	if (rsp.status != IPTS_STATUS_SUCCESS) {
++		dev_err(ipts->dev, "SET_MODE: cmd failed: %d\n", rsp.status);
++		return -EBADR;
++	}
++
++	return 0;
++}
++
++static int ipts_control_set_mem_window(struct ipts_context *ipts, struct ipts_resources *res)
++{
++	int i = 0;
++	int ret = 0;
++	struct ipts_mem_window cmd = { 0 };
++	struct ipts_response rsp = { 0 };
++
++	if (!ipts)
++		return -EFAULT;
++
++	if (!res)
++		return -EFAULT;
++
++	for (i = 0; i < IPTS_BUFFERS; i++) {
++		cmd.data_addr_lower[i] = lower_32_bits(res->data[i].dma_address);
++		cmd.data_addr_upper[i] = upper_32_bits(res->data[i].dma_address);
++		cmd.feedback_addr_lower[i] = lower_32_bits(res->feedback[i].dma_address);
++		cmd.feedback_addr_upper[i] = upper_32_bits(res->feedback[i].dma_address);
++	}
++
++	cmd.workqueue_addr_lower = lower_32_bits(res->workqueue.dma_address);
++	cmd.workqueue_addr_upper = upper_32_bits(res->workqueue.dma_address);
++
++	cmd.doorbell_addr_lower = lower_32_bits(res->doorbell.dma_address);
++	cmd.doorbell_addr_upper = upper_32_bits(res->doorbell.dma_address);
++
++	cmd.hid2me_addr_lower = lower_32_bits(res->hid2me.dma_address);
++	cmd.hid2me_addr_upper = upper_32_bits(res->hid2me.dma_address);
++
++	cmd.workqueue_size = IPTS_WORKQUEUE_SIZE;
++	cmd.workqueue_item_size = IPTS_WORKQUEUE_ITEM_SIZE;
++
++	ret = ipts_cmd_send(ipts, IPTS_CMD_SET_MEM_WINDOW, &cmd, sizeof(cmd));
++	if (ret) {
++		dev_err(ipts->dev, "SET_MEM_WINDOW: send failed: %d\n", ret);
++		return ret;
++	}
++
++	ret = ipts_cmd_recv(ipts, IPTS_CMD_SET_MEM_WINDOW, &rsp);
++	if (ret) {
++		dev_err(ipts->dev, "SET_MEM_WINDOW: recv failed: %d\n", ret);
++		return ret;
++	}
++
++	if (rsp.status != IPTS_STATUS_SUCCESS) {
++		dev_err(ipts->dev, "SET_MEM_WINDOW: cmd failed: %d\n", rsp.status);
++		return -EBADR;
++	}
++
++	return 0;
++}
++
++static int ipts_control_get_descriptor(struct ipts_context *ipts)
++{
++	int ret = 0;
++	struct ipts_data_header *header = NULL;
++	struct ipts_get_descriptor cmd = { 0 };
++	struct ipts_response rsp = { 0 };
++
++	if (!ipts)
++		return -EFAULT;
++
++	if (!ipts->resources.descriptor.address)
++		return -EFAULT;
++
++	memset(ipts->resources.descriptor.address, 0, ipts->resources.descriptor.size);
++
++	cmd.addr_lower = lower_32_bits(ipts->resources.descriptor.dma_address);
++	cmd.addr_upper = upper_32_bits(ipts->resources.descriptor.dma_address);
++	cmd.magic = 8;
++
++	ret = ipts_cmd_send(ipts, IPTS_CMD_GET_DESCRIPTOR, &cmd, sizeof(cmd));
++	if (ret) {
++		dev_err(ipts->dev, "GET_DESCRIPTOR: send failed: %d\n", ret);
++		return ret;
++	}
++
++	ret = ipts_cmd_recv(ipts, IPTS_CMD_GET_DESCRIPTOR, &rsp);
++	if (ret) {
++		dev_err(ipts->dev, "GET_DESCRIPTOR: recv failed: %d\n", ret);
++		return ret;
++	}
++
++	if (rsp.status != IPTS_STATUS_SUCCESS) {
++		dev_err(ipts->dev, "GET_DESCRIPTOR: cmd failed: %d\n", rsp.status);
++		return -EBADR;
++	}
++
++	header = (struct ipts_data_header *)ipts->resources.descriptor.address;
++
++	if (header->type == IPTS_DATA_TYPE_DESCRIPTOR) {
++		ipts->descriptor.address = &header->data[8];
++		ipts->descriptor.size = header->size - 8;
++
++		return 0;
++	}
++
++	return -ENODATA;
++}
++
++int ipts_control_request_flush(struct ipts_context *ipts)
++{
++	int ret = 0;
++	struct ipts_quiesce_io cmd = { 0 };
++
++	if (!ipts)
++		return -EFAULT;
++
++	ret = ipts_cmd_send(ipts, IPTS_CMD_QUIESCE_IO, &cmd, sizeof(cmd));
++	if (ret)
++		dev_err(ipts->dev, "QUIESCE_IO: send failed: %d\n", ret);
++
++	return ret;
++}
++
++int ipts_control_wait_flush(struct ipts_context *ipts)
++{
++	int ret = 0;
++	struct ipts_response rsp = { 0 };
++
++	if (!ipts)
++		return -EFAULT;
++
++	ret = ipts_cmd_recv(ipts, IPTS_CMD_QUIESCE_IO, &rsp);
++	if (ret) {
++		dev_err(ipts->dev, "QUIESCE_IO: recv failed: %d\n", ret);
++		return ret;
++	}
++
++	if (rsp.status == IPTS_STATUS_TIMEOUT)
++		return -EAGAIN;
++
++	if (rsp.status != IPTS_STATUS_SUCCESS) {
++		dev_err(ipts->dev, "QUIESCE_IO: cmd failed: %d\n", rsp.status);
++		return -EBADR;
++	}
++
++	return 0;
++}
++
++int ipts_control_request_data(struct ipts_context *ipts)
++{
++	int ret = 0;
++
++	if (!ipts)
++		return -EFAULT;
++
++	ret = ipts_cmd_send(ipts, IPTS_CMD_READY_FOR_DATA, NULL, 0);
++	if (ret)
++		dev_err(ipts->dev, "READY_FOR_DATA: send failed: %d\n", ret);
++
++	return ret;
++}
++
++int ipts_control_wait_data(struct ipts_context *ipts, bool shutdown)
++{
++	int ret = 0;
++	struct ipts_response rsp = { 0 };
++
++	if (!ipts)
++		return -EFAULT;
++
++	if (!shutdown)
++		ret = ipts_cmd_recv_timeout(ipts, IPTS_CMD_READY_FOR_DATA, &rsp, 0);
++	else
++		ret = ipts_cmd_recv(ipts, IPTS_CMD_READY_FOR_DATA, &rsp);
++
++	if (ret) {
++		if (ret != -EAGAIN)
++			dev_err(ipts->dev, "READY_FOR_DATA: recv failed: %d\n", ret);
++
++		return ret;
++	}
++
++	/*
++	 * During shutdown, it is possible that the sensor has already been disabled.
++	 */
++	if (rsp.status == IPTS_STATUS_SENSOR_DISABLED)
++		return 0;
++
++	if (rsp.status == IPTS_STATUS_TIMEOUT)
++		return -EAGAIN;
++
++	if (rsp.status != IPTS_STATUS_SUCCESS) {
++		dev_err(ipts->dev, "READY_FOR_DATA: cmd failed: %d\n", rsp.status);
++		return -EBADR;
++	}
++
++	return 0;
++}
++
++int ipts_control_send_feedback(struct ipts_context *ipts, u32 buffer)
++{
++	int ret = 0;
++	struct ipts_feedback cmd = { 0 };
++	struct ipts_response rsp = { 0 };
++
++	if (!ipts)
++		return -EFAULT;
++
++	cmd.buffer = buffer;
++
++	ret = ipts_cmd_send(ipts, IPTS_CMD_FEEDBACK, &cmd, sizeof(cmd));
++	if (ret) {
++		dev_err(ipts->dev, "FEEDBACK: send failed: %d\n", ret);
++		return ret;
++	}
++
++	ret = ipts_cmd_recv(ipts, IPTS_CMD_FEEDBACK, &rsp);
++	if (ret) {
++		dev_err(ipts->dev, "FEEDBACK: recv failed: %d\n", ret);
++		return ret;
++	}
++
++	/*
++	 * We don't know what feedback data looks like so we are sending zeros.
++	 * See also ipts_control_refill_buffer.
++	 */
++	if (rsp.status == IPTS_STATUS_INVALID_PARAMS)
++		return 0;
++
++	if (rsp.status != IPTS_STATUS_SUCCESS) {
++		dev_err(ipts->dev, "FEEDBACK: cmd failed: %d\n", rsp.status);
++		return -EBADR;
++	}
++
++	return 0;
++}
++
++int ipts_control_hid2me_feedback(struct ipts_context *ipts, enum ipts_feedback_cmd_type cmd,
++				 enum ipts_feedback_data_type type, void *data, size_t size)
++{
++	struct ipts_feedback_header *header = NULL;
++
++	if (!ipts)
++		return -EFAULT;
++
++	if (!ipts->resources.hid2me.address)
++		return -EFAULT;
++
++	memset(ipts->resources.hid2me.address, 0, ipts->resources.hid2me.size);
++	header = (struct ipts_feedback_header *)ipts->resources.hid2me.address;
++
++	header->cmd_type = cmd;
++	header->data_type = type;
++	header->size = size;
++	header->buffer = IPTS_HID2ME_BUFFER;
++
++	if (size + sizeof(*header) > ipts->resources.hid2me.size)
++		return -EINVAL;
++
++	if (data && size > 0)
++		memcpy(header->payload, data, size);
++
++	return ipts_control_send_feedback(ipts, IPTS_HID2ME_BUFFER);
++}
++
++int ipts_control_start(struct ipts_context *ipts)
++{
++	int ret = 0;
++	struct ipts_device_info info = { 0 };
++
++	if (!ipts)
++		return -EFAULT;
++
++	dev_info(ipts->dev, "Starting IPTS\n");
++
++	ret = ipts_control_get_device_info(ipts, &info);
++	if (ret) {
++		dev_err(ipts->dev, "Failed to get device info: %d\n", ret);
++		return ret;
++	}
++
++	ipts->info = info;
++
++	ret = ipts_resources_init(&ipts->resources, ipts->dev, info.data_size, info.feedback_size);
++	if (ret) {
++		dev_err(ipts->dev, "Failed to allocate buffers: %d", ret);
++		return ret;
++	}
++
++	dev_info(ipts->dev, "IPTS EDS Version: %d\n", info.intf_eds);
++
++	/*
++	 * Handle newer devices
++	 */
++	if (info.intf_eds > 1) {
++		/*
++		 * Fetching the descriptor will only work on newer devices.
++		 * For older devices, a fallback descriptor will be used.
++		 */
++		ret = ipts_control_get_descriptor(ipts);
++		if (ret) {
++			dev_err(ipts->dev, "Failed to fetch HID descriptor: %d\n", ret);
++			return ret;
++		}
++
++		/*
++		 * Newer devices can be directly initialized in polling mode.
++		 */
++		ipts->mode = IPTS_MODE_POLL;
++	}
++
++	ret = ipts_control_set_mode(ipts, ipts->mode);
++	if (ret) {
++		dev_err(ipts->dev, "Failed to set mode: %d\n", ret);
++		return ret;
++	}
++
++	ret = ipts_control_set_mem_window(ipts, &ipts->resources);
++	if (ret) {
++		dev_err(ipts->dev, "Failed to set memory window: %d\n", ret);
++		return ret;
++	}
++
++	ret = ipts_receiver_start(ipts);
++	if (ret) {
++		dev_err(ipts->dev, "Failed to start receiver: %d\n", ret);
++		return ret;
++	}
++
++	ret = ipts_control_request_data(ipts);
++	if (ret) {
++		dev_err(ipts->dev, "Failed to request data: %d\n", ret);
++		return ret;
++	}
++
++	ipts_hid_enable(ipts);
++
++	ret = ipts_hid_init(ipts, info);
++	if (ret) {
++		dev_err(ipts->dev, "Failed to initialize HID device: %d\n", ret);
++		return ret;
++	}
++
++	return 0;
++}
++
++static int _ipts_control_stop(struct ipts_context *ipts)
++{
++	int ret = 0;
++
++	if (!ipts)
++		return -EFAULT;
++
++	ipts_hid_disable(ipts);
++	dev_info(ipts->dev, "Stopping IPTS\n");
++
++	ret = ipts_receiver_stop(ipts);
++	if (ret) {
++		dev_err(ipts->dev, "Failed to stop receiver: %d\n", ret);
++		return ret;
++	}
++
++	ret = ipts_resources_free(&ipts->resources);
++	if (ret) {
++		dev_err(ipts->dev, "Failed to free resources: %d\n", ret);
++		return ret;
++	}
++
++	return 0;
++}
++
++int ipts_control_stop(struct ipts_context *ipts)
++{
++	int ret = 0;
++
++	ret = _ipts_control_stop(ipts);
++	if (ret)
++		return ret;
++
++	ret = ipts_hid_free(ipts);
++	if (ret) {
++		dev_err(ipts->dev, "Failed to free HID device: %d\n", ret);
++		return ret;
++	}
++
++	return 0;
++}
++
++int ipts_control_restart(struct ipts_context *ipts)
++{
++	int ret = 0;
++
++	ret = _ipts_control_stop(ipts);
++	if (ret)
++		return ret;
++
++	/*
++	 * Wait a second to give the sensor time to fully shut down.
++	 */
++	msleep(1000);
++
++	ret = ipts_control_start(ipts);
++	if (ret)
++		return ret;
++
++	return 0;
++}
+diff --git a/drivers/hid/ipts/control.h b/drivers/hid/ipts/control.h
+new file mode 100644
+index 000000000000..26629c5144ed
+--- /dev/null
++++ b/drivers/hid/ipts/control.h
+@@ -0,0 +1,126 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * Copyright (c) 2020-2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#ifndef IPTS_CONTROL_H
++#define IPTS_CONTROL_H
++
++#include <linux/types.h>
++
++#include "context.h"
++#include "spec-data.h"
++#include "spec-device.h"
++
++/**
++ * ipts_control_request_flush() - Stop the data flow.
++ * @ipts: The IPTS driver context.
++ *
++ * Runs the command to stop the data flow on the device.
++ * All outstanding data needs to be acknowledged using feedback before the command will return.
++ *
++ * Returns: 0 on success, <0 on error.
++ */
++int ipts_control_request_flush(struct ipts_context *ipts);
++
++/**
++ * ipts_control_wait_flush() - Wait until data flow has been stopped.
++ * @ipts: The IPTS driver context.
++ *
++ * Returns: 0 on success, <0 on error.
++ */
++int ipts_control_wait_flush(struct ipts_context *ipts);
++
++/**
++ * ipts_control_wait_flush() - Notify the device that the driver can receive new data.
++ * @ipts: The IPTS driver context.
++ *
++ * Returns: 0 on success, <0 on error.
++ */
++int ipts_control_request_data(struct ipts_context *ipts);
++
++/**
++ * ipts_control_wait_data() - Wait until new data is available.
++ * @ipts: The IPTS driver context.
++ * @block: Whether to block execution until data is available.
++ *
++ * In poll mode, this function will never return while the data flow is active. Instead,
++ * the poll will be incremented when new data is available.
++ *
++ * Returns: 0 on success, <0 on error, -EAGAIN if no data is available.
++ */
++int ipts_control_wait_data(struct ipts_context *ipts, bool block);
++
++/**
++ * ipts_control_send_feedback() - Submits a feedback buffer to the device.
++ * @ipts: The IPTS driver context.
++ * @buffer: The ID of the buffer containing feedback data.
++ *
++ * Returns: 0 on success, <0 on error.
++ */
++int ipts_control_send_feedback(struct ipts_context *ipts, u32 buffer);
++
++/**
++ * ipts_control_hid2me_feedback() - Sends HID2ME feedback, a special type of feedback.
++ * @ipts: The IPTS driver context.
++ * @cmd: The command that will be run on the device.
++ * @type: The type of the payload that is sent to the device.
++ * @data: The payload of the feedback command.
++ * @size: The size of the payload.
++ *
++ * HID2ME feedback is a special type of feedback, because it allows interfacing with
++ * the HID API of the device at any moment, without requiring a buffer that has to
++ * be acknowledged.
++ *
++ * Returns: 0 on success, <0 on error.
++ */
++int ipts_control_hid2me_feedback(struct ipts_context *ipts, enum ipts_feedback_cmd_type cmd,
++				 enum ipts_feedback_data_type type, void *data, size_t size);
++
++/**
++ * ipts_control_refill_buffer() - Acknowledges that data in a buffer has been processed.
++ * @ipts: The IPTS driver context.
++ * @buffer: The buffer that has been processed and can be refilled.
++ *
++ * Returns: 0 on success, <0 on error.
++ */
++static inline int ipts_control_refill_buffer(struct ipts_context *ipts, u32 buffer)
++{
++	/*
++	 * IPTS expects structured data in the feedback buffer matching the buffer that will be
++	 * refilled. We don't know what that data looks like, so we just keep the buffer empty.
++	 * This results in an INVALID_PARAMS error, but the buffer gets refilled without an issue.
++	 * Sending a minimal structure with the buffer ID fixes the error, but breaks refilling
++	 * the buffers on some devices.
++	 */
++
++	return ipts_control_send_feedback(ipts, buffer);
++}
++
++/**
++ * ipts_control_start() - Initialized the device and starts the data flow.
++ * @ipts: The IPTS driver context.
++ *
++ * Returns: 0 on success, <0 on error.
++ */
++int ipts_control_start(struct ipts_context *ipts);
++
++/**
++ * ipts_control_stop() - Stops the data flow and resets the device.
++ * @ipts: The IPTS driver context.
++ *
++ * Returns: 0 on success, <0 on error.
++ */
++int ipts_control_stop(struct ipts_context *ipts);
++
++/**
++ * ipts_control_restart() - Stops the device and starts it again.
++ * @ipts: The IPTS driver context.
++ *
++ * Returns: 0 on success, <0 on error.
++ */
++int ipts_control_restart(struct ipts_context *ipts);
++
++#endif /* IPTS_CONTROL_H */
+diff --git a/drivers/hid/ipts/desc.h b/drivers/hid/ipts/desc.h
+new file mode 100644
+index 000000000000..307438c7c80c
+--- /dev/null
++++ b/drivers/hid/ipts/desc.h
+@@ -0,0 +1,80 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * Copyright (c) 2022-2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#ifndef IPTS_DESC_H
++#define IPTS_DESC_H
++
++#include <linux/types.h>
++
++#define IPTS_HID_REPORT_SINGLETOUCH 64
++#define IPTS_HID_REPORT_DATA	    65
++#define IPTS_HID_REPORT_SET_MODE    66
++
++#define IPTS_HID_REPORT_DATA_SIZE 7485
++
++/*
++ * HID descriptor for singletouch data.
++ * This descriptor should be present on all IPTS devices.
++ */
++static const u8 ipts_singletouch_descriptor[] = {
++	0x05, 0x0D,	  /*  Usage Page (Digitizer),            */
++	0x09, 0x04,	  /*  Usage (Touchscreen),               */
++	0xA1, 0x01,	  /*  Collection (Application),          */
++	0x85, 0x40,	  /*      Report ID (64),                */
++	0x09, 0x42,	  /*      Usage (Tip Switch),            */
++	0x15, 0x00,	  /*      Logical Minimum (0),           */
++	0x25, 0x01,	  /*      Logical Maximum (1),           */
++	0x75, 0x01,	  /*      Report Size (1),               */
++	0x95, 0x01,	  /*      Report Count (1),              */
++	0x81, 0x02,	  /*      Input (Variable),              */
++	0x95, 0x07,	  /*      Report Count (7),              */
++	0x81, 0x03,	  /*      Input (Constant, Variable),    */
++	0x05, 0x01,	  /*      Usage Page (Desktop),          */
++	0x09, 0x30,	  /*      Usage (X),                     */
++	0x75, 0x10,	  /*      Report Size (16),              */
++	0x95, 0x01,	  /*      Report Count (1),              */
++	0xA4,		  /*      Push,                          */
++	0x55, 0x0E,	  /*      Unit Exponent (14),            */
++	0x65, 0x11,	  /*      Unit (Centimeter),             */
++	0x46, 0x76, 0x0B, /*      Physical Maximum (2934),       */
++	0x26, 0xFF, 0x7F, /*      Logical Maximum (32767),       */
++	0x81, 0x02,	  /*      Input (Variable),              */
++	0x09, 0x31,	  /*      Usage (Y),                     */
++	0x46, 0x74, 0x06, /*      Physical Maximum (1652),       */
++	0x26, 0xFF, 0x7F, /*      Logical Maximum (32767),       */
++	0x81, 0x02,	  /*      Input (Variable),              */
++	0xB4,		  /*      Pop,                           */
++	0xC0,		  /*  End Collection                     */
++};
++
++/*
++ * Fallback HID descriptor for older devices that do not have
++ * the ability to query their HID descriptor.
++ */
++static const u8 ipts_fallback_descriptor[] = {
++	0x05, 0x0D,	  /*  Usage Page (Digitizer),            */
++	0x09, 0x0F,	  /*  Usage (Capacitive Hm Digitizer),   */
++	0xA1, 0x01,	  /*  Collection (Application),          */
++	0x85, 0x41,	  /*      Report ID (65),                */
++	0x09, 0x56,	  /*      Usage (Scan Time),             */
++	0x95, 0x01,	  /*      Report Count (1),              */
++	0x75, 0x10,	  /*      Report Size (16),              */
++	0x81, 0x02,	  /*      Input (Variable),              */
++	0x09, 0x61,	  /*      Usage (Gesture Char Quality),  */
++	0x75, 0x08,	  /*      Report Size (8),               */
++	0x96, 0x3D, 0x1D, /*      Report Count (7485),           */
++	0x81, 0x03,	  /*      Input (Constant, Variable),    */
++	0x85, 0x42,	  /*      Report ID (66),                */
++	0x06, 0x00, 0xFF, /*      Usage Page (FF00h),            */
++	0x09, 0xC8,	  /*      Usage (C8h),                   */
++	0x75, 0x08,	  /*      Report Size (8),               */
++	0x95, 0x01,	  /*      Report Count (1),              */
++	0xB1, 0x02,	  /*      Feature (Variable),            */
++	0xC0,		  /*  End Collection,                    */
++};
++
++#endif /* IPTS_DESC_H */
+diff --git a/drivers/hid/ipts/eds1.c b/drivers/hid/ipts/eds1.c
+new file mode 100644
+index 000000000000..ecbb3a8bdaf6
+--- /dev/null
++++ b/drivers/hid/ipts/eds1.c
+@@ -0,0 +1,103 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (c) 2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#include <linux/err.h>
++#include <linux/gfp.h>
++#include <linux/hid.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++
++#include "context.h"
++#include "control.h"
++#include "desc.h"
++#include "spec-device.h"
++
++int ipts_eds1_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size)
++{
++	size_t size = 0;
++	u8 *buffer = NULL;
++
++	if (!ipts)
++		return -EFAULT;
++
++	if (!desc_buffer)
++		return -EFAULT;
++
++	if (!desc_size)
++		return -EFAULT;
++
++	size = sizeof(ipts_singletouch_descriptor) + sizeof(ipts_fallback_descriptor);
++
++	buffer = kzalloc(size, GFP_KERNEL);
++	if (!buffer)
++		return -ENOMEM;
++
++	memcpy(buffer, ipts_singletouch_descriptor, sizeof(ipts_singletouch_descriptor));
++	memcpy(&buffer[sizeof(ipts_singletouch_descriptor)], ipts_fallback_descriptor,
++	       sizeof(ipts_fallback_descriptor));
++
++	*desc_size = size;
++	*desc_buffer = buffer;
++
++	return 0;
++}
++
++static int ipts_eds1_switch_mode(struct ipts_context *ipts, enum ipts_mode mode)
++{
++	int ret = 0;
++
++	if (!ipts)
++		return -EFAULT;
++
++	if (ipts->mode == mode)
++		return 0;
++
++	ipts->mode = mode;
++
++	ret = ipts_control_restart(ipts);
++	if (ret)
++		dev_err(ipts->dev, "Failed to switch modes: %d\n", ret);
++
++	return ret;
++}
++
++int ipts_eds1_raw_request(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id,
++			  enum hid_report_type report_type, enum hid_class_request request_type)
++{
++	int ret = 0;
++
++	if (!ipts)
++		return -EFAULT;
++
++	if (!buffer)
++		return -EFAULT;
++
++	if (report_id != IPTS_HID_REPORT_SET_MODE)
++		return -EIO;
++
++	if (report_type != HID_FEATURE_REPORT)
++		return -EIO;
++
++	if (size != 2)
++		return -EINVAL;
++
++	/*
++	 * Implement mode switching report for older devices without native HID support.
++	 */
++
++	if (request_type == HID_REQ_GET_REPORT) {
++		memset(buffer, 0, size);
++		buffer[0] = report_id;
++		buffer[1] = ipts->mode;
++	} else if (request_type == HID_REQ_SET_REPORT) {
++		return ipts_eds1_switch_mode(ipts, buffer[1]);
++	} else {
++		return -EIO;
++	}
++
++	return ret;
++}
+diff --git a/drivers/hid/ipts/eds1.h b/drivers/hid/ipts/eds1.h
+new file mode 100644
+index 000000000000..eeeb6575e3e8
+--- /dev/null
++++ b/drivers/hid/ipts/eds1.h
+@@ -0,0 +1,35 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (c) 2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#include <linux/hid.h>
++#include <linux/types.h>
++
++#include "context.h"
++
++/**
++ * ipts_eds1_get_descriptor() - Assembles the HID descriptor of the device.
++ * @ipts: The IPTS driver context.
++ * @desc_buffer: A pointer to the location where the address of the allocated buffer is stored.
++ * @desc_size: A pointer to the location where the size of the allocated buffer is stored.
++ *
++ * Returns: 0 on success, <0 on error.
++ */
++int ipts_eds1_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size);
++
++/**
++ * ipts_eds1_raw_request() - Executes an output or feature report on the device.
++ * @ipts: The IPTS driver context.
++ * @buffer: The buffer containing the report.
++ * @size: The size of the buffer.
++ * @report_id: The HID report ID.
++ * @report_type: Whether this report is an output or a feature report.
++ * @request_type: Whether this report requests or sends data.
++ *
++ * Returns: 0 on success, <0 on error.
++ */
++int ipts_eds1_raw_request(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id,
++			  enum hid_report_type report_type, enum hid_class_request request_type);
+diff --git a/drivers/hid/ipts/eds2.c b/drivers/hid/ipts/eds2.c
+new file mode 100644
+index 000000000000..198dc65d7887
+--- /dev/null
++++ b/drivers/hid/ipts/eds2.c
+@@ -0,0 +1,144 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (c) 2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#include <linux/completion.h>
++#include <linux/err.h>
++#include <linux/gfp.h>
++#include <linux/mutex.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++
++#include "context.h"
++#include "control.h"
++#include "desc.h"
++#include "spec-data.h"
++
++int ipts_eds2_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size)
++{
++	size_t size = 0;
++	u8 *buffer = NULL;
++
++	if (!ipts)
++		return -EFAULT;
++
++	if (!desc_buffer)
++		return -EFAULT;
++
++	if (!desc_size)
++		return -EFAULT;
++
++	size = sizeof(ipts_singletouch_descriptor) + ipts->descriptor.size;
++
++	buffer = kzalloc(size, GFP_KERNEL);
++	if (!buffer)
++		return -ENOMEM;
++
++	memcpy(buffer, ipts_singletouch_descriptor, sizeof(ipts_singletouch_descriptor));
++	memcpy(&buffer[sizeof(ipts_singletouch_descriptor)], ipts->descriptor.address,
++	       ipts->descriptor.size);
++
++	*desc_size = size;
++	*desc_buffer = buffer;
++
++	return 0;
++}
++
++static int ipts_eds2_get_feature(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id,
++				 enum ipts_feedback_data_type type)
++{
++	int ret = 0;
++
++	if (!ipts)
++		return -EFAULT;
++
++	if (!buffer)
++		return -EFAULT;
++
++	mutex_lock(&ipts->feature_lock);
++
++	memset(buffer, 0, size);
++	buffer[0] = report_id;
++
++	memset(&ipts->feature_report, 0, sizeof(ipts->feature_report));
++	reinit_completion(&ipts->feature_event);
++
++	ret = ipts_control_hid2me_feedback(ipts, IPTS_FEEDBACK_CMD_TYPE_NONE, type, buffer, size);
++	if (ret) {
++		dev_err(ipts->dev, "Failed to send hid2me feedback: %d\n", ret);
++		goto out;
++	}
++
++	ret = wait_for_completion_timeout(&ipts->feature_event, msecs_to_jiffies(5000));
++	if (ret == 0) {
++		dev_warn(ipts->dev, "GET_FEATURES timed out!\n");
++		ret = -EIO;
++		goto out;
++	}
++
++	if (!ipts->feature_report.address) {
++		ret = -EFAULT;
++		goto out;
++	}
++
++	if (ipts->feature_report.size > size) {
++		ret = -ETOOSMALL;
++		goto out;
++	}
++
++	ret = ipts->feature_report.size;
++	memcpy(buffer, ipts->feature_report.address, ipts->feature_report.size);
++
++out:
++	mutex_unlock(&ipts->feature_lock);
++	return ret;
++}
++
++static int ipts_eds2_set_feature(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id,
++				 enum ipts_feedback_data_type type)
++{
++	int ret = 0;
++
++	if (!ipts)
++		return -EFAULT;
++
++	if (!buffer)
++		return -EFAULT;
++
++	buffer[0] = report_id;
++
++	ret = ipts_control_hid2me_feedback(ipts, IPTS_FEEDBACK_CMD_TYPE_NONE, type, buffer, size);
++	if (ret)
++		dev_err(ipts->dev, "Failed to send hid2me feedback: %d\n", ret);
++
++	return ret;
++}
++
++int ipts_eds2_raw_request(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id,
++			  enum hid_report_type report_type, enum hid_class_request request_type)
++{
++	enum ipts_feedback_data_type feedback_type = IPTS_FEEDBACK_DATA_TYPE_VENDOR;
++
++	if (!ipts)
++		return -EFAULT;
++
++	if (!buffer)
++		return -EFAULT;
++
++	if (report_type == HID_OUTPUT_REPORT && request_type == HID_REQ_SET_REPORT)
++		feedback_type = IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT;
++	else if (report_type == HID_FEATURE_REPORT && request_type == HID_REQ_GET_REPORT)
++		feedback_type = IPTS_FEEDBACK_DATA_TYPE_GET_FEATURES;
++	else if (report_type == HID_FEATURE_REPORT && request_type == HID_REQ_SET_REPORT)
++		feedback_type = IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES;
++	else
++		return -EIO;
++
++	if (request_type == HID_REQ_GET_REPORT)
++		return ipts_eds2_get_feature(ipts, buffer, size, report_id, feedback_type);
++	else
++		return ipts_eds2_set_feature(ipts, buffer, size, report_id, feedback_type);
++}
+diff --git a/drivers/hid/ipts/eds2.h b/drivers/hid/ipts/eds2.h
+new file mode 100644
+index 000000000000..064e3716907a
+--- /dev/null
++++ b/drivers/hid/ipts/eds2.h
+@@ -0,0 +1,35 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (c) 2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#include <linux/hid.h>
++#include <linux/types.h>
++
++#include "context.h"
++
++/**
++ * ipts_eds2_get_descriptor() - Assembles the HID descriptor of the device.
++ * @ipts: The IPTS driver context.
++ * @desc_buffer: A pointer to the location where the address of the allocated buffer is stored.
++ * @desc_size: A pointer to the location where the size of the allocated buffer is stored.
++ *
++ * Returns: 0 on success, <0 on error.
++ */
++int ipts_eds2_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size);
++
++/**
++ * ipts_eds2_raw_request() - Executes an output or feature report on the device.
++ * @ipts: The IPTS driver context.
++ * @buffer: The buffer containing the report.
++ * @size: The size of the buffer.
++ * @report_id: The HID report ID.
++ * @report_type: Whether this report is an output or a feature report.
++ * @request_type: Whether this report requests or sends data.
++ *
++ * Returns: 0 on success, <0 on error.
++ */
++int ipts_eds2_raw_request(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id,
++			  enum hid_report_type report_type, enum hid_class_request request_type);
+diff --git a/drivers/hid/ipts/hid.c b/drivers/hid/ipts/hid.c
+new file mode 100644
+index 000000000000..e34a1a4f9fa7
+--- /dev/null
++++ b/drivers/hid/ipts/hid.c
+@@ -0,0 +1,225 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (c) 2022-2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#include <linux/completion.h>
++#include <linux/err.h>
++#include <linux/gfp.h>
++#include <linux/hid.h>
++#include <linux/mutex.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++
++#include "context.h"
++#include "desc.h"
++#include "eds1.h"
++#include "eds2.h"
++#include "hid.h"
++#include "spec-data.h"
++#include "spec-hid.h"
++
++void ipts_hid_enable(struct ipts_context *ipts)
++{
++	WRITE_ONCE(ipts->hid_active, true);
++}
++
++void ipts_hid_disable(struct ipts_context *ipts)
++{
++	WRITE_ONCE(ipts->hid_active, false);
++}
++
++static int ipts_hid_start(struct hid_device *hid)
++{
++	return 0;
++}
++
++static void ipts_hid_stop(struct hid_device *hid)
++{
++}
++
++static int ipts_hid_parse(struct hid_device *hid)
++{
++	int ret = 0;
++	struct ipts_context *ipts = NULL;
++
++	u8 *buffer = NULL;
++	size_t size = 0;
++
++	if (!hid)
++		return -ENODEV;
++
++	ipts = hid->driver_data;
++
++	if (!ipts)
++		return -EFAULT;
++
++	if (!READ_ONCE(ipts->hid_active))
++		return -ENODEV;
++
++	if (ipts->info.intf_eds == 1)
++		ret = ipts_eds1_get_descriptor(ipts, &buffer, &size);
++	else
++		ret = ipts_eds2_get_descriptor(ipts, &buffer, &size);
++
++	if (ret) {
++		dev_err(ipts->dev, "Failed to allocate HID descriptor: %d\n", ret);
++		return ret;
++	}
++
++	ret = hid_parse_report(hid, buffer, size);
++	kfree(buffer);
++
++	if (ret) {
++		dev_err(ipts->dev, "Failed to parse HID descriptor: %d\n", ret);
++		return ret;
++	}
++
++	return 0;
++}
++
++static int ipts_hid_raw_request(struct hid_device *hid, unsigned char report_id, __u8 *buffer,
++				size_t size, unsigned char report_type, int request_type)
++{
++	struct ipts_context *ipts = NULL;
++
++	if (!hid)
++		return -ENODEV;
++
++	ipts = hid->driver_data;
++
++	if (!ipts)
++		return -EFAULT;
++
++	if (!READ_ONCE(ipts->hid_active))
++		return -ENODEV;
++
++	if (ipts->info.intf_eds == 1) {
++		return ipts_eds1_raw_request(ipts, buffer, size, report_id, report_type,
++					     request_type);
++	} else {
++		return ipts_eds2_raw_request(ipts, buffer, size, report_id, report_type,
++					     request_type);
++	}
++}
++
++static struct hid_ll_driver ipts_hid_driver = {
++	.start = ipts_hid_start,
++	.stop = ipts_hid_stop,
++	.open = ipts_hid_start,
++	.close = ipts_hid_stop,
++	.parse = ipts_hid_parse,
++	.raw_request = ipts_hid_raw_request,
++};
++
++int ipts_hid_input_data(struct ipts_context *ipts, u32 buffer)
++{
++	u8 *temp = NULL;
++	struct ipts_hid_header *frame = NULL;
++	struct ipts_data_header *header = NULL;
++
++	if (!ipts)
++		return -EFAULT;
++
++	if (!ipts->hid)
++		return -ENODEV;
++
++	if (!READ_ONCE(ipts->hid_active))
++		return -ENODEV;
++
++	header = (struct ipts_data_header *)ipts->resources.data[buffer].address;
++
++	temp = ipts->resources.report.address;
++	memset(temp, 0, ipts->resources.report.size);
++
++	if (!header)
++		return -EFAULT;
++
++	if (header->size == 0)
++		return 0;
++
++	if (header->type == IPTS_DATA_TYPE_HID)
++		return hid_input_report(ipts->hid, HID_INPUT_REPORT, header->data, header->size, 1);
++
++	if (header->type == IPTS_DATA_TYPE_GET_FEATURES) {
++		ipts->feature_report.address = header->data;
++		ipts->feature_report.size = header->size;
++
++		complete_all(&ipts->feature_event);
++		return 0;
++	}
++
++	if (header->type != IPTS_DATA_TYPE_FRAME)
++		return 0;
++
++	if (header->size + 3 + sizeof(struct ipts_hid_header) > IPTS_HID_REPORT_DATA_SIZE)
++		return -ERANGE;
++
++	/*
++	 * Synthesize a HID report matching the devices that natively send HID reports
++	 */
++	temp[0] = IPTS_HID_REPORT_DATA;
++
++	frame = (struct ipts_hid_header *)&temp[3];
++	frame->type = IPTS_HID_FRAME_TYPE_RAW;
++	frame->size = header->size + sizeof(*frame);
++
++	memcpy(frame->data, header->data, header->size);
++
++	return hid_input_report(ipts->hid, HID_INPUT_REPORT, temp, IPTS_HID_REPORT_DATA_SIZE, 1);
++}
++
++int ipts_hid_init(struct ipts_context *ipts, struct ipts_device_info info)
++{
++	int ret = 0;
++
++	if (!ipts)
++		return -EFAULT;
++
++	if (ipts->hid)
++		return 0;
++
++	ipts->hid = hid_allocate_device();
++	if (IS_ERR(ipts->hid)) {
++		int err = PTR_ERR(ipts->hid);
++
++		dev_err(ipts->dev, "Failed to allocate HID device: %d\n", err);
++		return err;
++	}
++
++	ipts->hid->driver_data = ipts;
++	ipts->hid->dev.parent = ipts->dev;
++	ipts->hid->ll_driver = &ipts_hid_driver;
++
++	ipts->hid->vendor = info.vendor;
++	ipts->hid->product = info.product;
++	ipts->hid->group = HID_GROUP_GENERIC;
++
++	snprintf(ipts->hid->name, sizeof(ipts->hid->name), "IPTS %04X:%04X", info.vendor,
++		 info.product);
++
++	ret = hid_add_device(ipts->hid);
++	if (ret) {
++		dev_err(ipts->dev, "Failed to add HID device: %d\n", ret);
++		ipts_hid_free(ipts);
++		return ret;
++	}
++
++	return 0;
++}
++
++int ipts_hid_free(struct ipts_context *ipts)
++{
++	if (!ipts)
++		return -EFAULT;
++
++	if (!ipts->hid)
++		return 0;
++
++	hid_destroy_device(ipts->hid);
++	ipts->hid = NULL;
++
++	return 0;
++}
+diff --git a/drivers/hid/ipts/hid.h b/drivers/hid/ipts/hid.h
+new file mode 100644
+index 000000000000..1ebe77447903
+--- /dev/null
++++ b/drivers/hid/ipts/hid.h
+@@ -0,0 +1,24 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * Copyright (c) 2022-2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#ifndef IPTS_HID_H
++#define IPTS_HID_H
++
++#include <linux/types.h>
++
++#include "context.h"
++#include "spec-device.h"
++
++void ipts_hid_enable(struct ipts_context *ipts);
++void ipts_hid_disable(struct ipts_context *ipts);
++
++int ipts_hid_input_data(struct ipts_context *ipts, u32 buffer);
++
++int ipts_hid_init(struct ipts_context *ipts, struct ipts_device_info info);
++int ipts_hid_free(struct ipts_context *ipts);
++
++#endif /* IPTS_HID_H */
+diff --git a/drivers/hid/ipts/main.c b/drivers/hid/ipts/main.c
+new file mode 100644
+index 000000000000..fb5b5c13ee3e
+--- /dev/null
++++ b/drivers/hid/ipts/main.c
+@@ -0,0 +1,126 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (c) 2020-2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#include <linux/completion.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/dma-mapping.h>
++#include <linux/mei_cl_bus.h>
++#include <linux/mod_devicetable.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
++#include <linux/slab.h>
++#include <linux/stddef.h>
++#include <linux/types.h>
++
++#include "context.h"
++#include "control.h"
++#include "mei.h"
++#include "receiver.h"
++#include "spec-device.h"
++
++/*
++ * The MEI client ID for IPTS functionality.
++ */
++#define IPTS_ID UUID_LE(0x3e8d0870, 0x271a, 0x4208, 0x8e, 0xb5, 0x9a, 0xcb, 0x94, 0x02, 0xae, 0x04)
++
++static int ipts_set_dma_mask(struct mei_cl_device *cldev)
++{
++	if (!cldev)
++		return -EFAULT;
++
++	if (!dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(64)))
++		return 0;
++
++	return dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(32));
++}
++
++static int ipts_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id)
++{
++	int ret = 0;
++	struct ipts_context *ipts = NULL;
++
++	if (!cldev)
++		return -EFAULT;
++
++	ret = ipts_set_dma_mask(cldev);
++	if (ret) {
++		dev_err(&cldev->dev, "Failed to set DMA mask for IPTS: %d\n", ret);
++		return ret;
++	}
++
++	ret = mei_cldev_enable(cldev);
++	if (ret) {
++		dev_err(&cldev->dev, "Failed to enable MEI device: %d\n", ret);
++		return ret;
++	}
++
++	ipts = devm_kzalloc(&cldev->dev, sizeof(*ipts), GFP_KERNEL);
++	if (!ipts) {
++		mei_cldev_disable(cldev);
++		return -ENOMEM;
++	}
++
++	ret = ipts_mei_init(&ipts->mei, cldev);
++	if (ret) {
++		dev_err(&cldev->dev, "Failed to init MEI bus logic: %d\n", ret);
++		return ret;
++	}
++
++	ipts->dev = &cldev->dev;
++	ipts->mode = IPTS_MODE_EVENT;
++
++	mutex_init(&ipts->feature_lock);
++	init_completion(&ipts->feature_event);
++
++	mei_cldev_set_drvdata(cldev, ipts);
++
++	ret = ipts_control_start(ipts);
++	if (ret) {
++		dev_err(&cldev->dev, "Failed to start IPTS: %d\n", ret);
++		return ret;
++	}
++
++	return 0;
++}
++
++static void ipts_remove(struct mei_cl_device *cldev)
++{
++	int ret = 0;
++	struct ipts_context *ipts = NULL;
++
++	if (!cldev) {
++		pr_err("MEI device is NULL!");
++		return;
++	}
++
++	ipts = mei_cldev_get_drvdata(cldev);
++
++	ret = ipts_control_stop(ipts);
++	if (ret)
++		dev_err(&cldev->dev, "Failed to stop IPTS: %d\n", ret);
++
++	mei_cldev_disable(cldev);
++}
++
++static struct mei_cl_device_id ipts_device_id_table[] = {
++	{ .uuid = IPTS_ID, .version = MEI_CL_VERSION_ANY },
++	{},
++};
++MODULE_DEVICE_TABLE(mei, ipts_device_id_table);
++
++static struct mei_cl_driver ipts_driver = {
++	.id_table = ipts_device_id_table,
++	.name = "ipts",
++	.probe = ipts_probe,
++	.remove = ipts_remove,
++};
++module_mei_cl_driver(ipts_driver);
++
++MODULE_DESCRIPTION("IPTS touchscreen driver");
++MODULE_AUTHOR("Dorian Stoll <dorian.stoll@tmsp.io>");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/hid/ipts/mei.c b/drivers/hid/ipts/mei.c
+new file mode 100644
+index 000000000000..1e0395ceae4a
+--- /dev/null
++++ b/drivers/hid/ipts/mei.c
+@@ -0,0 +1,188 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (c) 2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#include <linux/device.h>
++#include <linux/errno.h>
++#include <linux/jiffies.h>
++#include <linux/list.h>
++#include <linux/mei_cl_bus.h>
++#include <linux/printk.h>
++#include <linux/rwsem.h>
++#include <linux/types.h>
++#include <linux/wait.h>
++
++#include "context.h"
++#include "mei.h"
++
++static void locked_list_add(struct list_head *new, struct list_head *head,
++			    struct rw_semaphore *lock)
++{
++	down_write(lock);
++	list_add(new, head);
++	up_write(lock);
++}
++
++static void locked_list_del(struct list_head *entry, struct rw_semaphore *lock)
++{
++	down_write(lock);
++	list_del(entry);
++	up_write(lock);
++}
++
++static void ipts_mei_incoming(struct mei_cl_device *cldev)
++{
++	ssize_t ret = 0;
++	struct ipts_mei_message *entry = NULL;
++	struct ipts_context *ipts = NULL;
++
++	if (!cldev) {
++		pr_err("MEI device is NULL!");
++		return;
++	}
++
++	ipts = mei_cldev_get_drvdata(cldev);
++	if (!ipts) {
++		pr_err("IPTS driver context is NULL!");
++		return;
++	}
++
++	entry = devm_kzalloc(ipts->dev, sizeof(*entry), GFP_KERNEL);
++	if (!entry)
++		return;
++
++	INIT_LIST_HEAD(&entry->list);
++
++	do {
++		ret = mei_cldev_recv(cldev, (u8 *)&entry->rsp, sizeof(entry->rsp));
++	} while (ret == -EINTR);
++
++	if (ret < 0) {
++		dev_err(ipts->dev, "Error while reading response: %ld\n", ret);
++		return;
++	}
++
++	if (ret == 0) {
++		dev_err(ipts->dev, "Received empty response\n");
++		return;
++	}
++
++	locked_list_add(&entry->list, &ipts->mei.messages, &ipts->mei.message_lock);
++	wake_up_all(&ipts->mei.message_queue);
++}
++
++static int ipts_mei_search(struct ipts_mei *mei, enum ipts_command_code code,
++			   struct ipts_response *rsp)
++{
++	struct ipts_mei_message *entry = NULL;
++
++	if (!mei)
++		return -EFAULT;
++
++	if (!rsp)
++		return -EFAULT;
++
++	down_read(&mei->message_lock);
++
++	/*
++	 * Iterate over the list of received messages, and check if there is one
++	 * matching the requested command code.
++	 */
++	list_for_each_entry(entry, &mei->messages, list) {
++		if (entry->rsp.cmd == code)
++			break;
++	}
++
++	up_read(&mei->message_lock);
++
++	/*
++	 * If entry is not the list head, this means that the loop above has been stopped early,
++	 * and that we found a matching element. We drop the message from the list and return it.
++	 */
++	if (!list_entry_is_head(entry, &mei->messages, list)) {
++		locked_list_del(&entry->list, &mei->message_lock);
++
++		*rsp = entry->rsp;
++		devm_kfree(&mei->cldev->dev, entry);
++
++		return 0;
++	}
++
++	return -EAGAIN;
++}
++
++int ipts_mei_recv(struct ipts_mei *mei, enum ipts_command_code code, struct ipts_response *rsp,
++		  u64 timeout)
++{
++	int ret = 0;
++
++	if (!mei)
++		return -EFAULT;
++
++	/*
++	 * A timeout of 0 means check and return immideately.
++	 */
++	if (timeout == 0)
++		return ipts_mei_search(mei, code, rsp);
++
++	/*
++	 * A timeout of less than 0 means to wait forever.
++	 */
++	if (timeout < 0) {
++		wait_event(mei->message_queue, ipts_mei_search(mei, code, rsp) == 0);
++		return 0;
++	}
++
++	ret = wait_event_timeout(mei->message_queue, ipts_mei_search(mei, code, rsp) == 0,
++				 msecs_to_jiffies(timeout));
++
++	if (ret > 0)
++		return 0;
++
++	return -EAGAIN;
++}
++
++int ipts_mei_send(struct ipts_mei *mei, void *data, size_t length)
++{
++	int ret = 0;
++
++	if (!mei)
++		return -EFAULT;
++
++	if (!mei->cldev)
++		return -EFAULT;
++
++	if (!data)
++		return -EFAULT;
++
++	do {
++		ret = mei_cldev_send(mei->cldev, (u8 *)data, length);
++	} while (ret == -EINTR);
++
++	if (ret < 0)
++		return ret;
++
++	return 0;
++}
++
++int ipts_mei_init(struct ipts_mei *mei, struct mei_cl_device *cldev)
++{
++	if (!mei)
++		return -EFAULT;
++
++	if (!cldev)
++		return -EFAULT;
++
++	mei->cldev = cldev;
++
++	INIT_LIST_HEAD(&mei->messages);
++	init_waitqueue_head(&mei->message_queue);
++	init_rwsem(&mei->message_lock);
++
++	mei_cldev_register_rx_cb(cldev, ipts_mei_incoming);
++
++	return 0;
++}
+diff --git a/drivers/hid/ipts/mei.h b/drivers/hid/ipts/mei.h
+new file mode 100644
+index 000000000000..973bade6b0fd
+--- /dev/null
++++ b/drivers/hid/ipts/mei.h
+@@ -0,0 +1,66 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * Copyright (c) 2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#ifndef IPTS_MEI_H
++#define IPTS_MEI_H
++
++#include <linux/list.h>
++#include <linux/mei_cl_bus.h>
++#include <linux/rwsem.h>
++#include <linux/types.h>
++#include <linux/wait.h>
++
++#include "spec-device.h"
++
++struct ipts_mei_message {
++	struct list_head list;
++	struct ipts_response rsp;
++};
++
++struct ipts_mei {
++	struct mei_cl_device *cldev;
++
++	struct list_head messages;
++
++	wait_queue_head_t message_queue;
++	struct rw_semaphore message_lock;
++};
++
++/**
++ * ipts_mei_recv() - Receive data from a MEI device.
++ * @mei: The IPTS MEI device context.
++ * @code: The IPTS command code to look for.
++ * @rsp: The address that the received data will be copied to.
++ * @timeout: How many milliseconds the function will wait at most.
++ *
++ * A negative timeout means to wait forever.
++ *
++ * Returns: 0 on success, <0 on error, -EAGAIN if no response has been received.
++ */
++int ipts_mei_recv(struct ipts_mei *mei, enum ipts_command_code code, struct ipts_response *rsp,
++		  u64 timeout);
++
++/**
++ * ipts_mei_send() - Send data to a MEI device.
++ * @ipts: The IPTS MEI device context.
++ * @data: The data to send.
++ * @size: The size of the data.
++ *
++ * Returns: 0 on success, <0 on error.
++ */
++int ipts_mei_send(struct ipts_mei *mei, void *data, size_t length);
++
++/**
++ * ipts_mei_init() - Initialize the MEI device context.
++ * @mei: The MEI device context to initialize.
++ * @cldev: The MEI device the context will be bound to.
++ *
++ * Returns: 0 on success, <0 on error.
++ */
++int ipts_mei_init(struct ipts_mei *mei, struct mei_cl_device *cldev);
++
++#endif /* IPTS_MEI_H */
+diff --git a/drivers/hid/ipts/receiver.c b/drivers/hid/ipts/receiver.c
+new file mode 100644
+index 000000000000..ef66c3c9db80
+--- /dev/null
++++ b/drivers/hid/ipts/receiver.c
+@@ -0,0 +1,250 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (c) 2020-2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#include <linux/delay.h>
++#include <linux/err.h>
++#include <linux/kthread.h>
++#include <linux/time64.h>
++#include <linux/timekeeping.h>
++#include <linux/types.h>
++
++#include "cmd.h"
++#include "context.h"
++#include "control.h"
++#include "hid.h"
++#include "resources.h"
++#include "spec-device.h"
++#include "thread.h"
++
++static void ipts_receiver_next_doorbell(struct ipts_context *ipts)
++{
++	u32 *doorbell = (u32 *)ipts->resources.doorbell.address;
++	*doorbell = *doorbell + 1;
++}
++
++static u32 ipts_receiver_current_doorbell(struct ipts_context *ipts)
++{
++	u32 *doorbell = (u32 *)ipts->resources.doorbell.address;
++	return *doorbell;
++}
++
++static void ipts_receiver_backoff(time64_t last, u32 n)
++{
++	/*
++	 * If the last change was less than n seconds ago,
++	 * sleep for a shorter period so that new data can be
++	 * processed quickly. If there was no change for more than
++	 * n seconds, sleep longer to avoid wasting CPU cycles.
++	 */
++	if (last + n > ktime_get_seconds())
++		usleep_range(1 * USEC_PER_MSEC, 5 * USEC_PER_MSEC);
++	else
++		msleep(200);
++}
++
++static int ipts_receiver_event_loop(struct ipts_thread *thread)
++{
++	int ret = 0;
++	u32 buffer = 0;
++
++	struct ipts_context *ipts = NULL;
++	time64_t last = ktime_get_seconds();
++
++	if (!thread)
++		return -EFAULT;
++
++	ipts = thread->data;
++
++	if (!ipts)
++		return -EFAULT;
++
++	dev_info(ipts->dev, "IPTS running in event mode\n");
++
++	while (!ipts_thread_should_stop(thread)) {
++		int i = 0;
++
++		for (i = 0; i < IPTS_BUFFERS; i++) {
++			ret = ipts_control_wait_data(ipts, false);
++			if (ret == -EAGAIN)
++				break;
++
++			if (ret) {
++				dev_err(ipts->dev, "Failed to wait for data: %d\n", ret);
++				continue;
++			}
++
++			buffer = ipts_receiver_current_doorbell(ipts) % IPTS_BUFFERS;
++			ipts_receiver_next_doorbell(ipts);
++
++			ret = ipts_hid_input_data(ipts, buffer);
++			if (ret)
++				dev_err(ipts->dev, "Failed to process buffer: %d\n", ret);
++
++			ret = ipts_control_refill_buffer(ipts, buffer);
++			if (ret)
++				dev_err(ipts->dev, "Failed to send feedback: %d\n", ret);
++
++			ret = ipts_control_request_data(ipts);
++			if (ret)
++				dev_err(ipts->dev, "Failed to request data: %d\n", ret);
++
++			last = ktime_get_seconds();
++		}
++
++		ipts_receiver_backoff(last, 5);
++	}
++
++	ret = ipts_control_request_flush(ipts);
++	if (ret) {
++		dev_err(ipts->dev, "Failed to request flush: %d\n", ret);
++		return ret;
++	}
++
++	ret = ipts_control_wait_data(ipts, true);
++	if (ret) {
++		dev_err(ipts->dev, "Failed to wait for data: %d\n", ret);
++
++		if (ret != -EAGAIN)
++			return ret;
++		else
++			return 0;
++	}
++
++	ret = ipts_control_wait_flush(ipts);
++	if (ret) {
++		dev_err(ipts->dev, "Failed to wait for flush: %d\n", ret);
++
++		if (ret != -EAGAIN)
++			return ret;
++		else
++			return 0;
++	}
++
++	return 0;
++}
++
++static int ipts_receiver_poll_loop(struct ipts_thread *thread)
++{
++	int ret = 0;
++	u32 buffer = 0;
++
++	u32 doorbell = 0;
++	u32 lastdb = 0;
++
++	struct ipts_context *ipts = NULL;
++	time64_t last = ktime_get_seconds();
++
++	if (!thread)
++		return -EFAULT;
++
++	ipts = thread->data;
++
++	if (!ipts)
++		return -EFAULT;
++
++	dev_info(ipts->dev, "IPTS running in poll mode\n");
++
++	while (true) {
++		if (ipts_thread_should_stop(thread)) {
++			ret = ipts_control_request_flush(ipts);
++			if (ret) {
++				dev_err(ipts->dev, "Failed to request flush: %d\n", ret);
++				return ret;
++			}
++		}
++
++		doorbell = ipts_receiver_current_doorbell(ipts);
++
++		/*
++		 * After filling up one of the data buffers, IPTS will increment
++		 * the doorbell. The value of the doorbell stands for the *next*
++		 * buffer that IPTS is going to fill.
++		 */
++		while (lastdb != doorbell) {
++			buffer = lastdb % IPTS_BUFFERS;
++
++			ret = ipts_hid_input_data(ipts, buffer);
++			if (ret)
++				dev_err(ipts->dev, "Failed to process buffer: %d\n", ret);
++
++			ret = ipts_control_refill_buffer(ipts, buffer);
++			if (ret)
++				dev_err(ipts->dev, "Failed to send feedback: %d\n", ret);
++
++			last = ktime_get_seconds();
++			lastdb++;
++		}
++
++		if (ipts_thread_should_stop(thread))
++			break;
++
++		ipts_receiver_backoff(last, 5);
++	}
++
++	ret = ipts_control_wait_data(ipts, true);
++	if (ret) {
++		dev_err(ipts->dev, "Failed to wait for data: %d\n", ret);
++
++		if (ret != -EAGAIN)
++			return ret;
++		else
++			return 0;
++	}
++
++	ret = ipts_control_wait_flush(ipts);
++	if (ret) {
++		dev_err(ipts->dev, "Failed to wait for flush: %d\n", ret);
++
++		if (ret != -EAGAIN)
++			return ret;
++		else
++			return 0;
++	}
++
++	return 0;
++}
++
++int ipts_receiver_start(struct ipts_context *ipts)
++{
++	int ret = 0;
++
++	if (!ipts)
++		return -EFAULT;
++
++	if (ipts->mode == IPTS_MODE_EVENT) {
++		ret = ipts_thread_start(&ipts->receiver_loop, ipts_receiver_event_loop, ipts,
++					"ipts_event");
++	} else if (ipts->mode == IPTS_MODE_POLL) {
++		ret = ipts_thread_start(&ipts->receiver_loop, ipts_receiver_poll_loop, ipts,
++					"ipts_poll");
++	} else {
++		ret = -EINVAL;
++	}
++
++	if (ret) {
++		dev_err(ipts->dev, "Failed to start receiver loop: %d\n", ret);
++		return ret;
++	}
++
++	return 0;
++}
++
++int ipts_receiver_stop(struct ipts_context *ipts)
++{
++	int ret = 0;
++
++	if (!ipts)
++		return -EFAULT;
++
++	ret = ipts_thread_stop(&ipts->receiver_loop);
++	if (ret) {
++		dev_err(ipts->dev, "Failed to stop receiver loop: %d\n", ret);
++		return ret;
++	}
++
++	return 0;
++}
+diff --git a/drivers/hid/ipts/receiver.h b/drivers/hid/ipts/receiver.h
+new file mode 100644
+index 000000000000..3de7da62d40c
+--- /dev/null
++++ b/drivers/hid/ipts/receiver.h
+@@ -0,0 +1,16 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * Copyright (c) 2020-2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#ifndef IPTS_RECEIVER_H
++#define IPTS_RECEIVER_H
++
++#include "context.h"
++
++int ipts_receiver_start(struct ipts_context *ipts);
++int ipts_receiver_stop(struct ipts_context *ipts);
++
++#endif /* IPTS_RECEIVER_H */
+diff --git a/drivers/hid/ipts/resources.c b/drivers/hid/ipts/resources.c
+new file mode 100644
+index 000000000000..cc14653b2a9f
+--- /dev/null
++++ b/drivers/hid/ipts/resources.c
+@@ -0,0 +1,131 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (c) 2020-2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#include <linux/dma-mapping.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++
++#include "desc.h"
++#include "resources.h"
++#include "spec-device.h"
++
++static int ipts_resources_alloc_buffer(struct ipts_buffer *buffer, struct device *dev, size_t size)
++{
++	if (!buffer)
++		return -EFAULT;
++
++	if (buffer->address)
++		return 0;
++
++	buffer->address = dma_alloc_coherent(dev, size, &buffer->dma_address, GFP_KERNEL);
++
++	if (!buffer->address)
++		return -ENOMEM;
++
++	buffer->size = size;
++	buffer->device = dev;
++
++	return 0;
++}
++
++static void ipts_resources_free_buffer(struct ipts_buffer *buffer)
++{
++	if (!buffer->address)
++		return;
++
++	dma_free_coherent(buffer->device, buffer->size, buffer->address, buffer->dma_address);
++
++	buffer->address = NULL;
++	buffer->size = 0;
++
++	buffer->dma_address = 0;
++	buffer->device = NULL;
++}
++
++int ipts_resources_init(struct ipts_resources *res, struct device *dev, size_t ds, size_t fs)
++{
++	int ret = 0;
++
++	/*
++	 * Some compilers (AOSP clang) complain about a redefined
++	 * variable when this is declared inside of the for loop.
++	 */
++	int i = 0;
++
++	if (!res)
++		return -EFAULT;
++
++	for (i = 0; i < IPTS_BUFFERS; i++) {
++		ret = ipts_resources_alloc_buffer(&res->data[i], dev, ds);
++		if (ret)
++			goto err;
++	}
++
++	for (i = 0; i < IPTS_BUFFERS; i++) {
++		ret = ipts_resources_alloc_buffer(&res->feedback[i], dev, fs);
++		if (ret)
++			goto err;
++	}
++
++	ret = ipts_resources_alloc_buffer(&res->doorbell, dev, sizeof(u32));
++	if (ret)
++		goto err;
++
++	ret = ipts_resources_alloc_buffer(&res->workqueue, dev, sizeof(u32));
++	if (ret)
++		goto err;
++
++	ret = ipts_resources_alloc_buffer(&res->hid2me, dev, fs);
++	if (ret)
++		goto err;
++
++	ret = ipts_resources_alloc_buffer(&res->descriptor, dev, ds + 8);
++	if (ret)
++		goto err;
++
++	if (!res->report.address) {
++		res->report.size = IPTS_HID_REPORT_DATA_SIZE;
++		res->report.address = kzalloc(res->report.size, GFP_KERNEL);
++
++		if (!res->report.address) {
++			ret = -ENOMEM;
++			goto err;
++		}
++	}
++
++	return 0;
++
++err:
++
++	ipts_resources_free(res);
++	return ret;
++}
++
++int ipts_resources_free(struct ipts_resources *res)
++{
++	int i = 0;
++
++	if (!res)
++		return -EFAULT;
++
++	for (i = 0; i < IPTS_BUFFERS; i++)
++		ipts_resources_free_buffer(&res->data[i]);
++
++	for (i = 0; i < IPTS_BUFFERS; i++)
++		ipts_resources_free_buffer(&res->feedback[i]);
++
++	ipts_resources_free_buffer(&res->doorbell);
++	ipts_resources_free_buffer(&res->workqueue);
++	ipts_resources_free_buffer(&res->hid2me);
++	ipts_resources_free_buffer(&res->descriptor);
++
++	kfree(res->report.address);
++	res->report.address = NULL;
++	res->report.size = 0;
++
++	return 0;
++}
+diff --git a/drivers/hid/ipts/resources.h b/drivers/hid/ipts/resources.h
+new file mode 100644
+index 000000000000..2068e13285f0
+--- /dev/null
++++ b/drivers/hid/ipts/resources.h
+@@ -0,0 +1,41 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * Copyright (c) 2020-2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#ifndef IPTS_RESOURCES_H
++#define IPTS_RESOURCES_H
++
++#include <linux/device.h>
++#include <linux/types.h>
++
++#include "spec-device.h"
++
++struct ipts_buffer {
++	u8 *address;
++	size_t size;
++
++	dma_addr_t dma_address;
++	struct device *device;
++};
++
++struct ipts_resources {
++	struct ipts_buffer data[IPTS_BUFFERS];
++	struct ipts_buffer feedback[IPTS_BUFFERS];
++
++	struct ipts_buffer doorbell;
++	struct ipts_buffer workqueue;
++	struct ipts_buffer hid2me;
++
++	struct ipts_buffer descriptor;
++
++	// Buffer for synthesizing HID reports
++	struct ipts_buffer report;
++};
++
++int ipts_resources_init(struct ipts_resources *res, struct device *dev, size_t ds, size_t fs);
++int ipts_resources_free(struct ipts_resources *res);
++
++#endif /* IPTS_RESOURCES_H */
+diff --git a/drivers/hid/ipts/spec-data.h b/drivers/hid/ipts/spec-data.h
+new file mode 100644
+index 000000000000..e8dd98895a7e
+--- /dev/null
++++ b/drivers/hid/ipts/spec-data.h
+@@ -0,0 +1,100 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * Copyright (c) 2016 Intel Corporation
++ * Copyright (c) 2020-2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#ifndef IPTS_SPEC_DATA_H
++#define IPTS_SPEC_DATA_H
++
++#include <linux/build_bug.h>
++#include <linux/types.h>
++
++/**
++ * enum ipts_feedback_cmd_type - Commands that can be executed on the sensor through feedback.
++ */
++enum ipts_feedback_cmd_type {
++	IPTS_FEEDBACK_CMD_TYPE_NONE = 0,
++	IPTS_FEEDBACK_CMD_TYPE_SOFT_RESET = 1,
++	IPTS_FEEDBACK_CMD_TYPE_GOTO_ARMED = 2,
++	IPTS_FEEDBACK_CMD_TYPE_GOTO_SENSING = 3,
++	IPTS_FEEDBACK_CMD_TYPE_GOTO_SLEEP = 4,
++	IPTS_FEEDBACK_CMD_TYPE_GOTO_DOZE = 5,
++	IPTS_FEEDBACK_CMD_TYPE_HARD_RESET = 6,
++};
++
++/**
++ * enum ipts_feedback_data_type - Defines what data a feedback buffer contains.
++ * @IPTS_FEEDBACK_DATA_TYPE_VENDOR:        The buffer contains vendor specific feedback.
++ * @IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES:  The buffer contains a HID set features report.
++ * @IPTS_FEEDBACK_DATA_TYPE_GET_FEATURES:  The buffer contains a HID get features report.
++ * @IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT: The buffer contains a HID output report.
++ * @IPTS_FEEDBACK_DATA_TYPE_STORE_DATA:    The buffer contains calibration data for the sensor.
++ */
++enum ipts_feedback_data_type {
++	IPTS_FEEDBACK_DATA_TYPE_VENDOR = 0,
++	IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES = 1,
++	IPTS_FEEDBACK_DATA_TYPE_GET_FEATURES = 2,
++	IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT = 3,
++	IPTS_FEEDBACK_DATA_TYPE_STORE_DATA = 4,
++};
++
++/**
++ * struct ipts_feedback_header - Header that is prefixed to the data in a feedback buffer.
++ * @cmd_type:   A command that should be executed on the sensor.
++ * @size:       The size of the payload to be written.
++ * @buffer:     The ID of the buffer that contains this feedback data.
++ * @protocol:   The protocol version of the EDS.
++ * @data_type:  The type of data that the buffer contains.
++ * @spi_offset: The offset at which to write the payload data to the sensor.
++ * @payload:    Payload for the feedback command, or 0 if no payload is sent.
++ */
++struct ipts_feedback_header {
++	enum ipts_feedback_cmd_type cmd_type;
++	u32 size;
++	u32 buffer;
++	u32 protocol;
++	enum ipts_feedback_data_type data_type;
++	u32 spi_offset;
++	u8 reserved[40];
++	u8 payload[];
++} __packed;
++
++static_assert(sizeof(struct ipts_feedback_header) == 64);
++
++/**
++ * enum ipts_data_type - Defines what type of data a buffer contains.
++ * @IPTS_DATA_TYPE_FRAME:        Raw data frame.
++ * @IPTS_DATA_TYPE_ERROR:        Error data.
++ * @IPTS_DATA_TYPE_VENDOR:       Vendor specific data.
++ * @IPTS_DATA_TYPE_HID:          A HID report.
++ * @IPTS_DATA_TYPE_GET_FEATURES: The response to a GET_FEATURES HID2ME command.
++ */
++enum ipts_data_type {
++	IPTS_DATA_TYPE_FRAME = 0x00,
++	IPTS_DATA_TYPE_ERROR = 0x01,
++	IPTS_DATA_TYPE_VENDOR = 0x02,
++	IPTS_DATA_TYPE_HID = 0x03,
++	IPTS_DATA_TYPE_GET_FEATURES = 0x04,
++	IPTS_DATA_TYPE_DESCRIPTOR = 0x05,
++};
++
++/**
++ * struct ipts_data_header - Header that is prefixed to the data in a data buffer.
++ * @type: What data the buffer contains.
++ * @size: How much data the buffer contains.
++ * @buffer: Which buffer the data is in.
++ */
++struct ipts_data_header {
++	enum ipts_data_type type;
++	u32 size;
++	u32 buffer;
++	u8 reserved[52];
++	u8 data[];
++} __packed;
++
++static_assert(sizeof(struct ipts_data_header) == 64);
++
++#endif /* IPTS_SPEC_DATA_H */
+diff --git a/drivers/hid/ipts/spec-device.h b/drivers/hid/ipts/spec-device.h
+new file mode 100644
+index 000000000000..41845f9d9025
+--- /dev/null
++++ b/drivers/hid/ipts/spec-device.h
+@@ -0,0 +1,290 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * Copyright (c) 2016 Intel Corporation
++ * Copyright (c) 2020-2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#ifndef IPTS_SPEC_DEVICE_H
++#define IPTS_SPEC_DEVICE_H
++
++#include <linux/build_bug.h>
++#include <linux/types.h>
++
++/*
++ * The amount of buffers that IPTS can use for data transfer.
++ */
++#define IPTS_BUFFERS 16
++
++/*
++ * The buffer ID that is used for HID2ME feedback
++ */
++#define IPTS_HID2ME_BUFFER IPTS_BUFFERS
++
++/**
++ * enum ipts_command - Commands that can be sent to the IPTS hardware.
++ * @IPTS_CMD_GET_DEVICE_INFO:  Retrieves vendor information from the device.
++ * @IPTS_CMD_SET_MODE:         Changes the mode that the device will operate in.
++ * @IPTS_CMD_SET_MEM_WINDOW:   Configures memory buffers for passing data between device and driver.
++ * @IPTS_CMD_QUIESCE_IO:       Stops the data flow from the device to the driver.
++ * @IPTS_CMD_READY_FOR_DATA:   Informs the device that the driver is ready to receive data.
++ * @IPTS_CMD_FEEDBACK:         Informs the device that a buffer was processed and can be refilled.
++ * @IPTS_CMD_CLEAR_MEM_WINDOW: Stops the data flow and clears the buffer addresses on the device.
++ * @IPTS_CMD_RESET_SENSOR:     Resets the sensor to its default state.
++ * @IPTS_CMD_GET_DESCRIPTOR:   Retrieves the HID descriptor of the device.
++ */
++enum ipts_command_code {
++	IPTS_CMD_GET_DEVICE_INFO = 0x01,
++	IPTS_CMD_SET_MODE = 0x02,
++	IPTS_CMD_SET_MEM_WINDOW = 0x03,
++	IPTS_CMD_QUIESCE_IO = 0x04,
++	IPTS_CMD_READY_FOR_DATA = 0x05,
++	IPTS_CMD_FEEDBACK = 0x06,
++	IPTS_CMD_CLEAR_MEM_WINDOW = 0x07,
++	IPTS_CMD_RESET_SENSOR = 0x0B,
++	IPTS_CMD_GET_DESCRIPTOR = 0x0F,
++};
++
++/**
++ * enum ipts_status - Possible status codes returned by the IPTS device.
++ * @IPTS_STATUS_SUCCESS:                 Operation completed successfully.
++ * @IPTS_STATUS_INVALID_PARAMS:          Command contained an invalid payload.
++ * @IPTS_STATUS_ACCESS_DENIED:           ME could not validate a buffer address.
++ * @IPTS_STATUS_CMD_SIZE_ERROR:          Command contains an invalid payload.
++ * @IPTS_STATUS_NOT_READY:               Buffer addresses have not been set.
++ * @IPTS_STATUS_REQUEST_OUTSTANDING:     There is an outstanding command of the same type.
++ * @IPTS_STATUS_NO_SENSOR_FOUND:         No sensor could be found.
++ * @IPTS_STATUS_OUT_OF_MEMORY:           Not enough free memory for requested operation.
++ * @IPTS_STATUS_INTERNAL_ERROR:          An unexpected error occurred.
++ * @IPTS_STATUS_SENSOR_DISABLED:         The sensor has been disabled and must be reinitialized.
++ * @IPTS_STATUS_COMPAT_CHECK_FAIL:       Compatibility revision check between sensor and ME failed.
++ *                                       The host can ignore this error and attempt to continue.
++ * @IPTS_STATUS_SENSOR_EXPECTED_RESET:   The sensor went through a reset initiated by the driver.
++ * @IPTS_STATUS_SENSOR_UNEXPECTED_RESET: The sensor went through an unexpected reset.
++ * @IPTS_STATUS_RESET_FAILED:            Requested sensor reset failed to complete.
++ * @IPTS_STATUS_TIMEOUT:                 The operation timed out.
++ * @IPTS_STATUS_TEST_MODE_FAIL:          Test mode pattern did not match expected values.
++ * @IPTS_STATUS_SENSOR_FAIL_FATAL:       The sensor reported an error during reset sequence.
++ *                                       Further progress is not possible.
++ * @IPTS_STATUS_SENSOR_FAIL_NONFATAL:    The sensor reported an error during reset sequence.
++ *                                       The driver can attempt to continue.
++ * @IPTS_STATUS_INVALID_DEVICE_CAPS:     The device reported invalid capabilities.
++ * @IPTS_STATUS_QUIESCE_IO_IN_PROGRESS:  Command cannot be completed until Quiesce IO is done.
++ */
++enum ipts_status {
++	IPTS_STATUS_SUCCESS = 0x00,
++	IPTS_STATUS_INVALID_PARAMS = 0x01,
++	IPTS_STATUS_ACCESS_DENIED = 0x02,
++	IPTS_STATUS_CMD_SIZE_ERROR = 0x03,
++	IPTS_STATUS_NOT_READY = 0x04,
++	IPTS_STATUS_REQUEST_OUTSTANDING = 0x05,
++	IPTS_STATUS_NO_SENSOR_FOUND = 0x06,
++	IPTS_STATUS_OUT_OF_MEMORY = 0x07,
++	IPTS_STATUS_INTERNAL_ERROR = 0x08,
++	IPTS_STATUS_SENSOR_DISABLED = 0x09,
++	IPTS_STATUS_COMPAT_CHECK_FAIL = 0x0A,
++	IPTS_STATUS_SENSOR_EXPECTED_RESET = 0x0B,
++	IPTS_STATUS_SENSOR_UNEXPECTED_RESET = 0x0C,
++	IPTS_STATUS_RESET_FAILED = 0x0D,
++	IPTS_STATUS_TIMEOUT = 0x0E,
++	IPTS_STATUS_TEST_MODE_FAIL = 0x0F,
++	IPTS_STATUS_SENSOR_FAIL_FATAL = 0x10,
++	IPTS_STATUS_SENSOR_FAIL_NONFATAL = 0x11,
++	IPTS_STATUS_INVALID_DEVICE_CAPS = 0x12,
++	IPTS_STATUS_QUIESCE_IO_IN_PROGRESS = 0x13,
++};
++
++/**
++ * struct ipts_command - Message that is sent to the device for calling a command.
++ * @cmd:     The command that will be called.
++ * @payload: Payload containing parameters for the called command.
++ */
++struct ipts_command {
++	enum ipts_command_code cmd;
++	u8 payload[320];
++} __packed;
++
++static_assert(sizeof(struct ipts_command) == 324);
++
++/**
++ * enum ipts_mode - Configures what data the device produces and how its sent.
++ * @IPTS_MODE_EVENT: The device will send an event once a buffer was filled.
++ *                   Older devices will return singletouch data in this mode.
++ * @IPTS_MODE_POLL:  The device will notify the driver by incrementing the doorbell value.
++ *                   Older devices will return multitouch data in this mode.
++ */
++enum ipts_mode {
++	IPTS_MODE_EVENT = 0x00,
++	IPTS_MODE_POLL = 0x01,
++};
++
++/**
++ * struct ipts_set_mode - Payload for the SET_MODE command.
++ * @mode: Changes the mode that IPTS will operate in.
++ */
++struct ipts_set_mode {
++	enum ipts_mode mode;
++	u8 reserved[12];
++} __packed;
++
++static_assert(sizeof(struct ipts_set_mode) == 16);
++
++#define IPTS_WORKQUEUE_SIZE	 8192
++#define IPTS_WORKQUEUE_ITEM_SIZE 16
++
++/**
++ * struct ipts_mem_window - Payload for the SET_MEM_WINDOW command.
++ * @data_addr_lower:      Lower 32 bits of the data buffer addresses.
++ * @data_addr_upper:      Upper 32 bits of the data buffer addresses.
++ * @workqueue_addr_lower: Lower 32 bits of the workqueue buffer address.
++ * @workqueue_addr_upper: Upper 32 bits of the workqueue buffer address.
++ * @doorbell_addr_lower:  Lower 32 bits of the doorbell buffer address.
++ * @doorbell_addr_upper:  Upper 32 bits of the doorbell buffer address.
++ * @feedbackaddr_lower:   Lower 32 bits of the feedback buffer addresses.
++ * @feedbackaddr_upper:   Upper 32 bits of the feedback buffer addresses.
++ * @hid2me_addr_lower:    Lower 32 bits of the hid2me buffer address.
++ * @hid2me_addr_upper:    Upper 32 bits of the hid2me buffer address.
++ * @hid2me_size:          Size of the hid2me feedback buffer.
++ * @workqueue_item_size:  Magic value. Must be 16.
++ * @workqueue_size:       Magic value. Must be 8192.
++ *
++ * The workqueue related items in this struct are required for using
++ * GuC submission with binary processing firmware. Since this driver does
++ * not use GuC submission and instead exports raw data to userspace, these
++ * items are not actually used, but they need to be allocated and passed
++ * to the device, otherwise initialization will fail.
++ */
++struct ipts_mem_window {
++	u32 data_addr_lower[IPTS_BUFFERS];
++	u32 data_addr_upper[IPTS_BUFFERS];
++	u32 workqueue_addr_lower;
++	u32 workqueue_addr_upper;
++	u32 doorbell_addr_lower;
++	u32 doorbell_addr_upper;
++	u32 feedback_addr_lower[IPTS_BUFFERS];
++	u32 feedback_addr_upper[IPTS_BUFFERS];
++	u32 hid2me_addr_lower;
++	u32 hid2me_addr_upper;
++	u32 hid2me_size;
++	u8 reserved1;
++	u8 workqueue_item_size;
++	u16 workqueue_size;
++	u8 reserved[32];
++} __packed;
++
++static_assert(sizeof(struct ipts_mem_window) == 320);
++
++/**
++ * struct ipts_quiesce_io - Payload for the QUIESCE_IO command.
++ */
++struct ipts_quiesce_io {
++	u8 reserved[12];
++} __packed;
++
++static_assert(sizeof(struct ipts_quiesce_io) == 12);
++
++/**
++ * struct ipts_feedback - Payload for the FEEDBACK command.
++ * @buffer: The buffer that the device should refill.
++ */
++struct ipts_feedback {
++	u32 buffer;
++	u8 reserved[12];
++} __packed;
++
++static_assert(sizeof(struct ipts_feedback) == 16);
++
++/**
++ * enum ipts_reset_type - Possible ways of resetting the device.
++ * @IPTS_RESET_TYPE_HARD: Perform hardware reset using GPIO pin.
++ * @IPTS_RESET_TYPE_SOFT: Perform software reset using SPI command.
++ */
++enum ipts_reset_type {
++	IPTS_RESET_TYPE_HARD = 0x00,
++	IPTS_RESET_TYPE_SOFT = 0x01,
++};
++
++/**
++ * struct ipts_reset - Payload for the RESET_SENSOR command.
++ * @type: How the device should get reset.
++ */
++struct ipts_reset_sensor {
++	enum ipts_reset_type type;
++	u8 reserved[4];
++} __packed;
++
++static_assert(sizeof(struct ipts_reset_sensor) == 8);
++
++/**
++ * struct ipts_get_descriptor - Payload for the GET_DESCRIPTOR command.
++ * @addr_lower: The lower 32 bits of the descriptor buffer address.
++ * @addr_upper: The upper 32 bits of the descriptor buffer address.
++ * @magic:      A magic value. Must be 8.
++ */
++struct ipts_get_descriptor {
++	u32 addr_lower;
++	u32 addr_upper;
++	u32 magic;
++	u8 reserved[12];
++} __packed;
++
++static_assert(sizeof(struct ipts_get_descriptor) == 24);
++
++/*
++ * The type of a response is indicated by a
++ * command code, with the most significant bit flipped to 1.
++ */
++#define IPTS_RSP_BIT BIT(31)
++
++/**
++ * struct ipts_response - Data returned from the device in response to a command.
++ * @cmd:     The command that this response answers (IPTS_RSP_BIT will be 1).
++ * @status:  The return code of the command.
++ * @payload: The data that was produced by the command.
++ */
++struct ipts_response {
++	enum ipts_command_code cmd;
++	enum ipts_status status;
++	u8 payload[80];
++} __packed;
++
++static_assert(sizeof(struct ipts_response) == 88);
++
++/**
++ * struct ipts_device_info - Vendor information of the IPTS device.
++ * @vendor:         Vendor ID of this device.
++ * @product:        Product ID of this device.
++ * @hw_version:     Hardware revision of this device.
++ * @fw_version:     Firmware revision of this device.
++ * @data_size:      Requested size for a data buffer.
++ * @feedback_size:  Requested size for a feedback buffer.
++ * @mode:           Mode that the device currently operates in.
++ * @max_contacts:   Maximum amount of concurrent touches the sensor can process.
++ * @sensor_min_eds: The minimum EDS version supported by the sensor.
++ * @sensor_max_eds: The maximum EDS version supported by the sensor.
++ * @me_min_eds:     The minimum EDS version supported by the ME for communicating with the sensor.
++ * @me_max_eds:     The maximum EDS version supported by the ME for communicating with the sensor.
++ * @intf_eds:       The EDS version implemented by the interface between ME and host.
++ */
++struct ipts_device_info {
++	u16 vendor;
++	u16 product;
++	u32 hw_version;
++	u32 fw_version;
++	u32 data_size;
++	u32 feedback_size;
++	enum ipts_mode mode;
++	u8 max_contacts;
++	u8 reserved1[3];
++	u8 sensor_min_eds;
++	u8 sensor_maj_eds;
++	u8 me_min_eds;
++	u8 me_maj_eds;
++	u8 intf_eds;
++	u8 reserved2[11];
++} __packed;
++
++static_assert(sizeof(struct ipts_device_info) == 44);
++
++#endif /* IPTS_SPEC_DEVICE_H */
+diff --git a/drivers/hid/ipts/spec-hid.h b/drivers/hid/ipts/spec-hid.h
+new file mode 100644
+index 000000000000..5a58d4a0a610
+--- /dev/null
++++ b/drivers/hid/ipts/spec-hid.h
+@@ -0,0 +1,34 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * Copyright (c) 2020-2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#ifndef IPTS_SPEC_HID_H
++#define IPTS_SPEC_HID_H
++
++#include <linux/build_bug.h>
++#include <linux/types.h>
++
++/*
++ * Made-up type for passing raw IPTS data in a HID report.
++ */
++#define IPTS_HID_FRAME_TYPE_RAW 0xEE
++
++/**
++ * struct ipts_hid_frame - Header that is prefixed to raw IPTS data wrapped in a HID report.
++ * @size: Size of the data inside the report, including this header.
++ * @type: What type of data does this report contain.
++ */
++struct ipts_hid_header {
++	u32 size;
++	u8 reserved1;
++	u8 type;
++	u8 reserved2;
++	u8 data[];
++} __packed;
++
++static_assert(sizeof(struct ipts_hid_header) == 7);
++
++#endif /* IPTS_SPEC_HID_H */
+diff --git a/drivers/hid/ipts/thread.c b/drivers/hid/ipts/thread.c
+new file mode 100644
+index 000000000000..355e92bea26f
+--- /dev/null
++++ b/drivers/hid/ipts/thread.c
+@@ -0,0 +1,84 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (c) 2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#include <linux/completion.h>
++#include <linux/err.h>
++#include <linux/kthread.h>
++#include <linux/mutex.h>
++
++#include "thread.h"
++
++bool ipts_thread_should_stop(struct ipts_thread *thread)
++{
++	if (!thread)
++		return false;
++
++	return READ_ONCE(thread->should_stop);
++}
++
++static int ipts_thread_runner(void *data)
++{
++	int ret = 0;
++	struct ipts_thread *thread = data;
++
++	if (!thread)
++		return -EFAULT;
++
++	if (!thread->threadfn)
++		return -EFAULT;
++
++	ret = thread->threadfn(thread);
++	complete_all(&thread->done);
++
++	return ret;
++}
++
++int ipts_thread_start(struct ipts_thread *thread, int (*threadfn)(struct ipts_thread *thread),
++		      void *data, const char *name)
++{
++	if (!thread)
++		return -EFAULT;
++
++	if (!threadfn)
++		return -EFAULT;
++
++	init_completion(&thread->done);
++
++	thread->data = data;
++	thread->should_stop = false;
++	thread->threadfn = threadfn;
++
++	thread->thread = kthread_run(ipts_thread_runner, thread, name);
++	return PTR_ERR_OR_ZERO(thread->thread);
++}
++
++int ipts_thread_stop(struct ipts_thread *thread)
++{
++	int ret = 0;
++
++	if (!thread)
++		return -EFAULT;
++
++	if (!thread->thread)
++		return 0;
++
++	WRITE_ONCE(thread->should_stop, true);
++
++	/*
++	 * Make sure that the write has gone through before waiting.
++	 */
++	wmb();
++
++	wait_for_completion(&thread->done);
++	ret = kthread_stop(thread->thread);
++
++	thread->thread = NULL;
++	thread->data = NULL;
++	thread->threadfn = NULL;
++
++	return ret;
++}
+diff --git a/drivers/hid/ipts/thread.h b/drivers/hid/ipts/thread.h
+new file mode 100644
+index 000000000000..1f966b8b32c4
+--- /dev/null
++++ b/drivers/hid/ipts/thread.h
+@@ -0,0 +1,59 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * Copyright (c) 2023 Dorian Stoll
++ *
++ * Linux driver for Intel Precise Touch & Stylus
++ */
++
++#ifndef IPTS_THREAD_H
++#define IPTS_THREAD_H
++
++#include <linux/completion.h>
++#include <linux/mutex.h>
++#include <linux/sched.h>
++
++/*
++ * This wrapper over kthread is necessary, because calling kthread_stop makes it impossible
++ * to issue MEI commands from that thread while it shuts itself down. By using a custom
++ * boolean variable and a completion object, we can call kthread_stop only when the thread
++ * already finished all of its work and has returned.
++ */
++struct ipts_thread {
++	struct task_struct *thread;
++
++	bool should_stop;
++	struct completion done;
++
++	void *data;
++	int (*threadfn)(struct ipts_thread *thread);
++};
++
++/**
++ * ipts_thread_should_stop() - Returns true if the thread is asked to terminate.
++ * @thread: The current thread.
++ *
++ * Returns: true if the thread should stop, false if not.
++ */
++bool ipts_thread_should_stop(struct ipts_thread *thread);
++
++/**
++ * ipts_thread_start() - Starts an IPTS thread.
++ * @thread: The thread to initialize and start.
++ * @threadfn: The function to execute.
++ * @data: An argument that will be passed to threadfn.
++ * @name: The name of the new thread.
++ *
++ * Returns: 0 on success, <0 on error.
++ */
++int ipts_thread_start(struct ipts_thread *thread, int (*threadfn)(struct ipts_thread *thread),
++		      void *data, const char name[]);
++
++/**
++ * ipts_thread_stop() - Asks the thread to terminate and waits until it has finished.
++ * @thread: The thread that should stop.
++ *
++ * Returns: The return value of the thread function.
++ */
++int ipts_thread_stop(struct ipts_thread *thread);
++
++#endif /* IPTS_THREAD_H */
+-- 
+2.42.0
+

+ 1433 - 0
patches/6.5/0005-ithc.patch

@@ -0,0 +1,1433 @@
+From 2f4527f977e57fba5d278825215d51730a7623dc Mon Sep 17 00:00:00 2001
+From: Dorian Stoll <dorian.stoll@tmsp.io>
+Date: Sun, 11 Dec 2022 12:03:38 +0100
+Subject: [PATCH] iommu: intel: Disable source id verification for ITHC
+
+Signed-off-by: Dorian Stoll <dorian.stoll@tmsp.io>
+Patchset: ithc
+---
+ drivers/iommu/intel/irq_remapping.c | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+diff --git a/drivers/iommu/intel/irq_remapping.c b/drivers/iommu/intel/irq_remapping.c
+index 08f56326e2f8..75218b38995c 100644
+--- a/drivers/iommu/intel/irq_remapping.c
++++ b/drivers/iommu/intel/irq_remapping.c
+@@ -386,6 +386,22 @@ static int set_msi_sid(struct irte *irte, struct pci_dev *dev)
+ 	data.busmatch_count = 0;
+ 	pci_for_each_dma_alias(dev, set_msi_sid_cb, &data);
+ 
++	/*
++	 * The Intel Touch Host Controller is at 00:10.6, but for some reason
++	 * the MSI interrupts have request id 01:05.0.
++	 * Disable id verification to work around this.
++	 * FIXME Find proper fix or turn this into a quirk.
++	 */
++	if (dev->vendor == PCI_VENDOR_ID_INTEL && (dev->class >> 8) == PCI_CLASS_INPUT_PEN) {
++		switch(dev->device) {
++		case 0x98d0: case 0x98d1: // LKF
++		case 0xa0d0: case 0xa0d1: // TGL LP
++		case 0x43d0: case 0x43d1: // TGL H
++			set_irte_sid(irte, SVT_NO_VERIFY, SQ_ALL_16, 0);
++			return 0;
++		}
++	}
++
+ 	/*
+ 	 * DMA alias provides us with a PCI device and alias.  The only case
+ 	 * where the it will return an alias on a different bus than the
+-- 
+2.42.0
+
+From 1f0c9d3f6c22e8136358dc366f61ca4d20c0377c Mon Sep 17 00:00:00 2001
+From: Dorian Stoll <dorian.stoll@tmsp.io>
+Date: Sun, 11 Dec 2022 12:10:54 +0100
+Subject: [PATCH] hid: Add support for Intel Touch Host Controller
+
+Based on quo/ithc-linux@55803a2
+
+Signed-off-by: Dorian Stoll <dorian.stoll@tmsp.io>
+Patchset: ithc
+---
+ drivers/hid/Kconfig           |   2 +
+ drivers/hid/Makefile          |   1 +
+ drivers/hid/ithc/Kbuild       |   6 +
+ drivers/hid/ithc/Kconfig      |  12 +
+ drivers/hid/ithc/ithc-debug.c |  96 ++++++
+ drivers/hid/ithc/ithc-dma.c   | 258 ++++++++++++++++
+ drivers/hid/ithc/ithc-dma.h   |  67 +++++
+ drivers/hid/ithc/ithc-main.c  | 534 ++++++++++++++++++++++++++++++++++
+ drivers/hid/ithc/ithc-regs.c  |  64 ++++
+ drivers/hid/ithc/ithc-regs.h  | 186 ++++++++++++
+ drivers/hid/ithc/ithc.h       |  60 ++++
+ 11 files changed, 1286 insertions(+)
+ create mode 100644 drivers/hid/ithc/Kbuild
+ create mode 100644 drivers/hid/ithc/Kconfig
+ create mode 100644 drivers/hid/ithc/ithc-debug.c
+ create mode 100644 drivers/hid/ithc/ithc-dma.c
+ create mode 100644 drivers/hid/ithc/ithc-dma.h
+ create mode 100644 drivers/hid/ithc/ithc-main.c
+ create mode 100644 drivers/hid/ithc/ithc-regs.c
+ create mode 100644 drivers/hid/ithc/ithc-regs.h
+ create mode 100644 drivers/hid/ithc/ithc.h
+
+diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
+index 54f45bdf663e..0b58bd30a22b 100644
+--- a/drivers/hid/Kconfig
++++ b/drivers/hid/Kconfig
+@@ -1336,4 +1336,6 @@ source "drivers/hid/surface-hid/Kconfig"
+ 
+ source "drivers/hid/ipts/Kconfig"
+ 
++source "drivers/hid/ithc/Kconfig"
++
+ endif # HID_SUPPORT
+diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
+index f58610f27216..656a0783c709 100644
+--- a/drivers/hid/Makefile
++++ b/drivers/hid/Makefile
+@@ -170,3 +170,4 @@ obj-$(CONFIG_AMD_SFH_HID)       += amd-sfh-hid/
+ obj-$(CONFIG_SURFACE_HID_CORE)  += surface-hid/
+ 
+ obj-$(CONFIG_HID_IPTS)          += ipts/
++obj-$(CONFIG_HID_ITHC)          += ithc/
+diff --git a/drivers/hid/ithc/Kbuild b/drivers/hid/ithc/Kbuild
+new file mode 100644
+index 000000000000..aea83f2ac07b
+--- /dev/null
++++ b/drivers/hid/ithc/Kbuild
+@@ -0,0 +1,6 @@
++obj-$(CONFIG_HID_ITHC) := ithc.o
++
++ithc-objs := ithc-main.o ithc-regs.o ithc-dma.o ithc-debug.o
++
++ccflags-y := -std=gnu11 -Wno-declaration-after-statement
++
+diff --git a/drivers/hid/ithc/Kconfig b/drivers/hid/ithc/Kconfig
+new file mode 100644
+index 000000000000..ede713023609
+--- /dev/null
++++ b/drivers/hid/ithc/Kconfig
+@@ -0,0 +1,12 @@
++config HID_ITHC
++	tristate "Intel Touch Host Controller"
++	depends on PCI
++	depends on HID
++	help
++	  Say Y here if your system has a touchscreen using Intels
++	  Touch Host Controller (ITHC / IPTS) technology.
++
++	  If unsure say N.
++
++	  To compile this driver as a module, choose M here: the
++	  module will be called ithc.
+diff --git a/drivers/hid/ithc/ithc-debug.c b/drivers/hid/ithc/ithc-debug.c
+new file mode 100644
+index 000000000000..57bf125c45bd
+--- /dev/null
++++ b/drivers/hid/ithc/ithc-debug.c
+@@ -0,0 +1,96 @@
++#include "ithc.h"
++
++void ithc_log_regs(struct ithc *ithc) {
++	if (!ithc->prev_regs) return;
++	u32 __iomem *cur = (__iomem void*)ithc->regs;
++	u32 *prev = (void*)ithc->prev_regs;
++	for (int i = 1024; i < sizeof *ithc->regs / 4; i++) {
++		u32 x = readl(cur + i);
++		if (x != prev[i]) {
++			pci_info(ithc->pci, "reg %04x: %08x -> %08x\n", i * 4, prev[i], x);
++			prev[i] = x;
++		}
++	}
++}
++
++static ssize_t ithc_debugfs_cmd_write(struct file *f, const char __user *buf, size_t len, loff_t *offset) {
++	struct ithc *ithc = file_inode(f)->i_private;
++	char cmd[256];
++	if (!ithc || !ithc->pci) return -ENODEV;
++	if (!len) return -EINVAL;
++	if (len >= sizeof cmd) return -EINVAL;
++	if (copy_from_user(cmd, buf, len)) return -EFAULT;
++	cmd[len] = 0;
++	if (cmd[len-1] == '\n') cmd[len-1] = 0;
++	pci_info(ithc->pci, "debug command: %s\n", cmd);
++	u32 n = 0;
++	const char *s = cmd + 1;
++	u32 a[32];
++	while (*s && *s != '\n') {
++		if (n >= ARRAY_SIZE(a)) return -EINVAL;
++		if (*s++ != ' ') return -EINVAL;
++		char *e;
++		a[n++] = simple_strtoul(s, &e, 0);
++		if (e == s) return -EINVAL;
++		s = e;
++	}
++	ithc_log_regs(ithc);
++	switch(cmd[0]) {
++	case 'x': // reset
++		ithc_reset(ithc);
++		break;
++	case 'w': // write register: offset mask value
++		if (n != 3 || (a[0] & 3)) return -EINVAL;
++		pci_info(ithc->pci, "debug write 0x%04x = 0x%08x (mask 0x%08x)\n", a[0], a[2], a[1]);
++		bitsl(((__iomem u32 *)ithc->regs) + a[0] / 4, a[1], a[2]);
++		break;
++	case 'r': // read register: offset
++		if (n != 1 || (a[0] & 3)) return -EINVAL;
++		pci_info(ithc->pci, "debug read 0x%04x = 0x%08x\n", a[0], readl(((__iomem u32 *)ithc->regs) + a[0] / 4));
++		break;
++	case 's': // spi command: cmd offset len data...
++		// read config: s 4 0 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
++		// set touch cfg: s 6 12 4 XX
++		if (n < 3 || a[2] > (n - 3) * 4) return -EINVAL;
++		pci_info(ithc->pci, "debug spi command %u with %u bytes of data\n", a[0], a[2]);
++		if (!CHECK(ithc_spi_command, ithc, a[0], a[1], a[2], a + 3))
++			for (u32 i = 0; i < (a[2] + 3) / 4; i++) pci_info(ithc->pci, "resp %u = 0x%08x\n", i, a[3+i]);
++		break;
++	case 'd': // dma command: cmd len data...
++		// get report descriptor: d 7 8 0 0
++		// enable multitouch: d 3 2 0x0105
++		if (n < 2 || a[1] > (n - 2) * 4) return -EINVAL;
++		pci_info(ithc->pci, "debug dma command %u with %u bytes of data\n", a[0], a[1]);
++		if (ithc_dma_tx(ithc, a[0], a[1], a + 2)) pci_err(ithc->pci, "dma tx failed\n");
++		break;
++	default:
++		return -EINVAL;
++	}
++	ithc_log_regs(ithc);
++	return len;
++}
++
++static const struct file_operations ithc_debugfops_cmd = {
++	.owner = THIS_MODULE,
++	.write = ithc_debugfs_cmd_write,
++};
++
++static void ithc_debugfs_devres_release(struct device *dev, void *res) {
++	struct dentry **dbgm = res;
++	if (*dbgm) debugfs_remove_recursive(*dbgm);
++}
++
++int ithc_debug_init(struct ithc *ithc) {
++	struct dentry **dbgm = devres_alloc(ithc_debugfs_devres_release, sizeof *dbgm, GFP_KERNEL);
++	if (!dbgm) return -ENOMEM;
++	devres_add(&ithc->pci->dev, dbgm);
++	struct dentry *dbg = debugfs_create_dir(DEVNAME, NULL);
++	if (IS_ERR(dbg)) return PTR_ERR(dbg);
++	*dbgm = dbg;
++
++	struct dentry *cmd = debugfs_create_file("cmd", 0220, dbg, ithc, &ithc_debugfops_cmd);
++	if (IS_ERR(cmd)) return PTR_ERR(cmd);
++
++	return 0;
++}
++
+diff --git a/drivers/hid/ithc/ithc-dma.c b/drivers/hid/ithc/ithc-dma.c
+new file mode 100644
+index 000000000000..7e89b3496918
+--- /dev/null
++++ b/drivers/hid/ithc/ithc-dma.c
+@@ -0,0 +1,258 @@
++#include "ithc.h"
++
++static int ithc_dma_prd_alloc(struct ithc *ithc, struct ithc_dma_prd_buffer *p, unsigned num_buffers, unsigned num_pages, enum dma_data_direction dir) {
++	p->num_pages = num_pages;
++	p->dir = dir;
++	p->size = round_up(num_buffers * num_pages * sizeof(struct ithc_phys_region_desc), PAGE_SIZE);
++	p->addr = dmam_alloc_coherent(&ithc->pci->dev, p->size, &p->dma_addr, GFP_KERNEL);
++	if (!p->addr) return -ENOMEM;
++	if (p->dma_addr & (PAGE_SIZE - 1)) return -EFAULT;
++	return 0;
++}
++
++struct ithc_sg_table {
++	void *addr;
++	struct sg_table sgt;
++	enum dma_data_direction dir;
++};
++static void ithc_dma_sgtable_free(struct sg_table *sgt) {
++	struct scatterlist *sg;
++	int i;
++	for_each_sgtable_sg(sgt, sg, i) {
++		struct page *p = sg_page(sg);
++		if (p) __free_page(p);
++	}
++	sg_free_table(sgt);
++}
++static void ithc_dma_data_devres_release(struct device *dev, void *res) {
++	struct ithc_sg_table *sgt = res;
++	if (sgt->addr) vunmap(sgt->addr);
++	dma_unmap_sgtable(dev, &sgt->sgt, sgt->dir, 0);
++	ithc_dma_sgtable_free(&sgt->sgt);
++}
++
++static int ithc_dma_data_alloc(struct ithc* ithc, struct ithc_dma_prd_buffer *prds, struct ithc_dma_data_buffer *b) {
++	// We don't use dma_alloc_coherent for data buffers, because they don't have to be contiguous (we can use one PRD per page) or coherent (they are unidirectional).
++	// Instead we use an sg_table of individually allocated pages (5.13 has dma_alloc_noncontiguous for this, but we'd like to support 5.10 for now).
++	struct page *pages[16];
++	if (prds->num_pages == 0 || prds->num_pages > ARRAY_SIZE(pages)) return -EINVAL;
++	b->active_idx = -1;
++	struct ithc_sg_table *sgt = devres_alloc(ithc_dma_data_devres_release, sizeof *sgt, GFP_KERNEL);
++	if (!sgt) return -ENOMEM;
++	sgt->dir = prds->dir;
++	if (!sg_alloc_table(&sgt->sgt, prds->num_pages, GFP_KERNEL)) {
++		struct scatterlist *sg;
++		int i;
++		bool ok = true;
++		for_each_sgtable_sg(&sgt->sgt, sg, i) {
++			struct page *p = pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO); // don't need __GFP_DMA for PCI DMA
++			if (!p) { ok = false; break; }
++			sg_set_page(sg, p, PAGE_SIZE, 0);
++		}
++		if (ok && !dma_map_sgtable(&ithc->pci->dev, &sgt->sgt, prds->dir, 0)) {
++			devres_add(&ithc->pci->dev, sgt);
++			b->sgt = &sgt->sgt;
++			b->addr = sgt->addr = vmap(pages, prds->num_pages, 0, PAGE_KERNEL);
++			if (!b->addr) return -ENOMEM;
++			return 0;
++		}
++		ithc_dma_sgtable_free(&sgt->sgt);
++	}
++	devres_free(sgt);
++	return -ENOMEM;
++}
++
++static int ithc_dma_data_buffer_put(struct ithc *ithc, struct ithc_dma_prd_buffer *prds, struct ithc_dma_data_buffer *b, unsigned idx) {
++	struct ithc_phys_region_desc *prd = prds->addr;
++	prd += idx * prds->num_pages;
++	if (b->active_idx >= 0) { pci_err(ithc->pci, "buffer already active\n"); return -EINVAL; }
++	b->active_idx = idx;
++	if (prds->dir == DMA_TO_DEVICE) {
++		if (b->data_size > PAGE_SIZE) return -EINVAL;
++		prd->addr = sg_dma_address(b->sgt->sgl) >> 10;
++		prd->size = b->data_size | PRD_FLAG_END;
++		flush_kernel_vmap_range(b->addr, b->data_size);
++	} else if (prds->dir == DMA_FROM_DEVICE) {
++		struct scatterlist *sg;
++		int i;
++		for_each_sgtable_dma_sg(b->sgt, sg, i) {
++			prd->addr = sg_dma_address(sg) >> 10;
++			prd->size = sg_dma_len(sg);
++			prd++;
++		}
++		prd[-1].size |= PRD_FLAG_END;
++	}
++	dma_wmb(); // for the prds
++	dma_sync_sgtable_for_device(&ithc->pci->dev, b->sgt, prds->dir);
++	return 0;
++}
++
++static int ithc_dma_data_buffer_get(struct ithc *ithc, struct ithc_dma_prd_buffer *prds, struct ithc_dma_data_buffer *b, unsigned idx) {
++	struct ithc_phys_region_desc *prd = prds->addr;
++	prd += idx * prds->num_pages;
++	if (b->active_idx != idx) { pci_err(ithc->pci, "wrong buffer index\n"); return -EINVAL; }
++	b->active_idx = -1;
++	if (prds->dir == DMA_FROM_DEVICE) {
++		dma_rmb(); // for the prds
++		b->data_size = 0;
++		struct scatterlist *sg;
++		int i;
++		for_each_sgtable_dma_sg(b->sgt, sg, i) {
++			unsigned size = prd->size;
++			b->data_size += size & PRD_SIZE_MASK;
++			if (size & PRD_FLAG_END) break;
++			if ((size & PRD_SIZE_MASK) != sg_dma_len(sg)) { pci_err(ithc->pci, "truncated prd\n"); break; }
++			prd++;
++		}
++		invalidate_kernel_vmap_range(b->addr, b->data_size);
++	}
++	dma_sync_sgtable_for_cpu(&ithc->pci->dev, b->sgt, prds->dir);
++	return 0;
++}
++
++int ithc_dma_rx_init(struct ithc *ithc, u8 channel, const char *devname) {
++	struct ithc_dma_rx *rx = &ithc->dma_rx[channel];
++	mutex_init(&rx->mutex);
++	u32 buf_size = DEVCFG_DMA_RX_SIZE(ithc->config.dma_buf_sizes);
++	unsigned num_pages = (buf_size + PAGE_SIZE - 1) / PAGE_SIZE;
++	pci_dbg(ithc->pci, "allocating rx buffers: num = %u, size = %u, pages = %u\n", NUM_RX_BUF, buf_size, num_pages);
++	CHECK_RET(ithc_dma_prd_alloc, ithc, &rx->prds, NUM_RX_BUF, num_pages, DMA_FROM_DEVICE);
++	for (unsigned i = 0; i < NUM_RX_BUF; i++)
++		CHECK_RET(ithc_dma_data_alloc, ithc, &rx->prds, &rx->bufs[i]);
++	writeb(DMA_RX_CONTROL2_RESET, &ithc->regs->dma_rx[channel].control2);
++	lo_hi_writeq(rx->prds.dma_addr, &ithc->regs->dma_rx[channel].addr);
++	writeb(NUM_RX_BUF - 1, &ithc->regs->dma_rx[channel].num_bufs);
++	writeb(num_pages - 1, &ithc->regs->dma_rx[channel].num_prds);
++	u8 head = readb(&ithc->regs->dma_rx[channel].head);
++	if (head) { pci_err(ithc->pci, "head is nonzero (%u)\n", head); return -EIO; }
++	for (unsigned i = 0; i < NUM_RX_BUF; i++)
++		CHECK_RET(ithc_dma_data_buffer_put, ithc, &rx->prds, &rx->bufs[i], i);
++	writeb(head ^ DMA_RX_WRAP_FLAG, &ithc->regs->dma_rx[channel].tail);
++	return 0;
++}
++void ithc_dma_rx_enable(struct ithc *ithc, u8 channel) {
++	bitsb_set(&ithc->regs->dma_rx[channel].control, DMA_RX_CONTROL_ENABLE | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_DATA);
++	CHECK(waitl, ithc, &ithc->regs->dma_rx[1].status, DMA_RX_STATUS_ENABLED, DMA_RX_STATUS_ENABLED);
++}
++
++int ithc_dma_tx_init(struct ithc *ithc) {
++	struct ithc_dma_tx *tx = &ithc->dma_tx;
++	mutex_init(&tx->mutex);
++	tx->max_size = DEVCFG_DMA_TX_SIZE(ithc->config.dma_buf_sizes);
++	unsigned num_pages = (tx->max_size + PAGE_SIZE - 1) / PAGE_SIZE;
++	pci_dbg(ithc->pci, "allocating tx buffers: size = %u, pages = %u\n", tx->max_size, num_pages);
++	CHECK_RET(ithc_dma_prd_alloc, ithc, &tx->prds, 1, num_pages, DMA_TO_DEVICE);
++	CHECK_RET(ithc_dma_data_alloc, ithc, &tx->prds, &tx->buf);
++	lo_hi_writeq(tx->prds.dma_addr, &ithc->regs->dma_tx.addr);
++	writeb(num_pages - 1, &ithc->regs->dma_tx.num_prds);
++	CHECK_RET(ithc_dma_data_buffer_put, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0);
++	return 0;
++}
++
++static int ithc_dma_rx_process_buf(struct ithc *ithc, struct ithc_dma_data_buffer *data, u8 channel, u8 buf) {
++	if (buf >= NUM_RX_BUF) {
++		pci_err(ithc->pci, "invalid dma ringbuffer index\n");
++		return -EINVAL;
++	}
++	ithc_set_active(ithc);
++	u32 len = data->data_size;
++	struct ithc_dma_rx_header *hdr = data->addr;
++	u8 *hiddata = (void *)(hdr + 1);
++	if (len >= sizeof *hdr && hdr->code == DMA_RX_CODE_RESET) {
++		CHECK(ithc_reset, ithc);
++	} else if (len < sizeof *hdr || len != sizeof *hdr + hdr->data_size) {
++		if (hdr->code == DMA_RX_CODE_INPUT_REPORT) {
++			// When the CPU enters a low power state during DMA, we can get truncated messages.
++			// Typically this will be a single touch HID report that is only 1 byte, or a multitouch report that is 257 bytes.
++			// See also ithc_set_active().
++		} else {
++			pci_err(ithc->pci, "invalid dma rx data! channel %u, buffer %u, size %u, code %u, data size %u\n", channel, buf, len, hdr->code, hdr->data_size);
++			print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1, hdr, min(len, 0x400u), 0);
++		}
++	} else if (hdr->code == DMA_RX_CODE_REPORT_DESCRIPTOR && hdr->data_size > 8) {
++		CHECK(hid_parse_report, ithc->hid, hiddata + 8, hdr->data_size - 8);
++		WRITE_ONCE(ithc->hid_parse_done, true);
++		wake_up(&ithc->wait_hid_parse);
++	} else if (hdr->code == DMA_RX_CODE_INPUT_REPORT) {
++		CHECK(hid_input_report, ithc->hid, HID_INPUT_REPORT, hiddata, hdr->data_size, 1);
++	} else if (hdr->code == DMA_RX_CODE_FEATURE_REPORT) {
++		bool done = false;
++		mutex_lock(&ithc->hid_get_feature_mutex);
++		if (ithc->hid_get_feature_buf) {
++			if (hdr->data_size < ithc->hid_get_feature_size) ithc->hid_get_feature_size = hdr->data_size;
++			memcpy(ithc->hid_get_feature_buf, hiddata, ithc->hid_get_feature_size);
++			ithc->hid_get_feature_buf = NULL;
++			done = true;
++		}
++		mutex_unlock(&ithc->hid_get_feature_mutex);
++		if (done) wake_up(&ithc->wait_hid_get_feature);
++		else CHECK(hid_input_report, ithc->hid, HID_FEATURE_REPORT, hiddata, hdr->data_size, 1);
++	} else {
++		pci_dbg(ithc->pci, "unhandled dma rx data! channel %u, buffer %u, size %u, code %u\n", channel, buf, len, hdr->code);
++		print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1, hdr, min(len, 0x400u), 0);
++	}
++	return 0;
++}
++
++static int ithc_dma_rx_unlocked(struct ithc *ithc, u8 channel) {
++	struct ithc_dma_rx *rx = &ithc->dma_rx[channel];
++	unsigned n = rx->num_received;
++	u8 head_wrap = readb(&ithc->regs->dma_rx[channel].head);
++	while (1) {
++		u8 tail = n % NUM_RX_BUF;
++		u8 tail_wrap = tail | ((n / NUM_RX_BUF) & 1 ? 0 : DMA_RX_WRAP_FLAG);
++		writeb(tail_wrap, &ithc->regs->dma_rx[channel].tail);
++		// ringbuffer is full if tail_wrap == head_wrap
++		// ringbuffer is empty if tail_wrap == head_wrap ^ WRAP_FLAG
++		if (tail_wrap == (head_wrap ^ DMA_RX_WRAP_FLAG)) return 0;
++
++		// take the buffer that the device just filled
++		struct ithc_dma_data_buffer *b = &rx->bufs[n % NUM_RX_BUF];
++		CHECK_RET(ithc_dma_data_buffer_get, ithc, &rx->prds, b, tail);
++		rx->num_received = ++n;
++
++		// process data
++		CHECK(ithc_dma_rx_process_buf, ithc, b, channel, tail);
++
++		// give the buffer back to the device
++		CHECK_RET(ithc_dma_data_buffer_put, ithc, &rx->prds, b, tail);
++	}
++}
++int ithc_dma_rx(struct ithc *ithc, u8 channel) {
++	struct ithc_dma_rx *rx = &ithc->dma_rx[channel];
++	mutex_lock(&rx->mutex);
++	int ret = ithc_dma_rx_unlocked(ithc, channel);
++	mutex_unlock(&rx->mutex);
++	return ret;
++}
++
++static int ithc_dma_tx_unlocked(struct ithc *ithc, u32 cmdcode, u32 datasize, void *data) {
++	pci_dbg(ithc->pci, "dma tx command %u, size %u\n", cmdcode, datasize);
++	struct ithc_dma_tx_header *hdr;
++	u8 padding = datasize & 3 ? 4 - (datasize & 3) : 0;
++	unsigned fullsize = sizeof *hdr + datasize + padding;
++	if (fullsize > ithc->dma_tx.max_size || fullsize > PAGE_SIZE) return -EINVAL;
++	CHECK_RET(ithc_dma_data_buffer_get, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0);
++
++	ithc->dma_tx.buf.data_size = fullsize;
++	hdr = ithc->dma_tx.buf.addr;
++	hdr->code = cmdcode;
++	hdr->data_size = datasize;
++	u8 *dest = (void *)(hdr + 1);
++	memcpy(dest, data, datasize);
++	dest += datasize;
++	for (u8 p = 0; p < padding; p++) *dest++ = 0;
++	CHECK_RET(ithc_dma_data_buffer_put, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0);
++
++	bitsb_set(&ithc->regs->dma_tx.control, DMA_TX_CONTROL_SEND);
++	CHECK_RET(waitb, ithc, &ithc->regs->dma_tx.control, DMA_TX_CONTROL_SEND, 0);
++	writel(DMA_TX_STATUS_DONE, &ithc->regs->dma_tx.status);
++	return 0;
++}
++int ithc_dma_tx(struct ithc *ithc, u32 cmdcode, u32 datasize, void *data) {
++	mutex_lock(&ithc->dma_tx.mutex);
++	int ret = ithc_dma_tx_unlocked(ithc, cmdcode, datasize, data);
++	mutex_unlock(&ithc->dma_tx.mutex);
++	return ret;
++}
++
+diff --git a/drivers/hid/ithc/ithc-dma.h b/drivers/hid/ithc/ithc-dma.h
+new file mode 100644
+index 000000000000..d9f2c19a13f3
+--- /dev/null
++++ b/drivers/hid/ithc/ithc-dma.h
+@@ -0,0 +1,67 @@
++#define PRD_SIZE_MASK            0xffffff
++#define PRD_FLAG_END             0x1000000
++#define PRD_FLAG_SUCCESS         0x2000000
++#define PRD_FLAG_ERROR           0x4000000
++
++struct ithc_phys_region_desc {
++	u64 addr; // physical addr/1024
++	u32 size; // num bytes, PRD_FLAG_END marks last prd for data split over multiple prds
++	u32 unused;
++};
++
++#define DMA_RX_CODE_INPUT_REPORT          3
++#define DMA_RX_CODE_FEATURE_REPORT        4
++#define DMA_RX_CODE_REPORT_DESCRIPTOR     5
++#define DMA_RX_CODE_RESET                 7
++
++struct ithc_dma_rx_header {
++	u32 code;
++	u32 data_size;
++	u32 _unknown[14];
++};
++
++#define DMA_TX_CODE_SET_FEATURE           3
++#define DMA_TX_CODE_GET_FEATURE           4
++#define DMA_TX_CODE_OUTPUT_REPORT         5
++#define DMA_TX_CODE_GET_REPORT_DESCRIPTOR 7
++
++struct ithc_dma_tx_header {
++	u32 code;
++	u32 data_size;
++};
++
++struct ithc_dma_prd_buffer {
++	void *addr;
++	dma_addr_t dma_addr;
++	u32 size;
++	u32 num_pages; // per data buffer
++	enum dma_data_direction dir;
++};
++
++struct ithc_dma_data_buffer {
++	void *addr;
++	struct sg_table *sgt;
++	int active_idx;
++	u32 data_size;
++};
++
++struct ithc_dma_tx {
++	struct mutex mutex;
++	u32 max_size;
++	struct ithc_dma_prd_buffer prds;
++	struct ithc_dma_data_buffer buf;
++};
++
++struct ithc_dma_rx {
++	struct mutex mutex;
++	u32 num_received;
++	struct ithc_dma_prd_buffer prds;
++	struct ithc_dma_data_buffer bufs[NUM_RX_BUF];
++};
++
++int ithc_dma_rx_init(struct ithc *ithc, u8 channel, const char *devname);
++void ithc_dma_rx_enable(struct ithc *ithc, u8 channel);
++int ithc_dma_tx_init(struct ithc *ithc);
++int ithc_dma_rx(struct ithc *ithc, u8 channel);
++int ithc_dma_tx(struct ithc *ithc, u32 cmdcode, u32 datasize, void *cmddata);
++
+diff --git a/drivers/hid/ithc/ithc-main.c b/drivers/hid/ithc/ithc-main.c
+new file mode 100644
+index 000000000000..09512b9cb4d3
+--- /dev/null
++++ b/drivers/hid/ithc/ithc-main.c
+@@ -0,0 +1,534 @@
++#include "ithc.h"
++
++MODULE_DESCRIPTION("Intel Touch Host Controller driver");
++MODULE_LICENSE("Dual BSD/GPL");
++
++// Lakefield
++#define PCI_DEVICE_ID_INTEL_THC_LKF_PORT1    0x98d0
++#define PCI_DEVICE_ID_INTEL_THC_LKF_PORT2    0x98d1
++// Tiger Lake
++#define PCI_DEVICE_ID_INTEL_THC_TGL_LP_PORT1 0xa0d0
++#define PCI_DEVICE_ID_INTEL_THC_TGL_LP_PORT2 0xa0d1
++#define PCI_DEVICE_ID_INTEL_THC_TGL_H_PORT1  0x43d0
++#define PCI_DEVICE_ID_INTEL_THC_TGL_H_PORT2  0x43d1
++// Alder Lake
++#define PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT1  0x7ad8
++#define PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT2  0x7ad9
++#define PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT1  0x51d0
++#define PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT2  0x51d1
++#define PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT1  0x54d0
++#define PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT2  0x54d1
++// Raptor Lake
++#define PCI_DEVICE_ID_INTEL_THC_RPL_S_PORT1  0x7a58
++#define PCI_DEVICE_ID_INTEL_THC_RPL_S_PORT2  0x7a59
++// Meteor Lake
++#define PCI_DEVICE_ID_INTEL_THC_MTL_PORT1    0x7e48
++#define PCI_DEVICE_ID_INTEL_THC_MTL_PORT2    0x7e4a
++
++static const struct pci_device_id ithc_pci_tbl[] = {
++	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_LKF_PORT1) },
++	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_LKF_PORT2) },
++	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_TGL_LP_PORT1) },
++	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_TGL_LP_PORT2) },
++	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_TGL_H_PORT1) },
++	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_TGL_H_PORT2) },
++	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT1) },
++	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT2) },
++	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT1) },
++	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT2) },
++	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT1) },
++	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT2) },
++	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_RPL_S_PORT1) },
++	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_RPL_S_PORT2) },
++	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_MTL_PORT1) },
++	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_MTL_PORT2) },
++	{}
++};
++MODULE_DEVICE_TABLE(pci, ithc_pci_tbl);
++
++// Module parameters
++
++static bool ithc_use_polling = false;
++module_param_named(poll, ithc_use_polling, bool, 0);
++MODULE_PARM_DESC(poll, "Use polling instead of interrupts");
++
++static bool ithc_use_rx0 = false;
++module_param_named(rx0, ithc_use_rx0, bool, 0);
++MODULE_PARM_DESC(rx0, "Use DMA RX channel 0");
++
++static bool ithc_use_rx1 = true;
++module_param_named(rx1, ithc_use_rx1, bool, 0);
++MODULE_PARM_DESC(rx1, "Use DMA RX channel 1");
++
++static bool ithc_log_regs_enabled = false;
++module_param_named(logregs, ithc_log_regs_enabled, bool, 0);
++MODULE_PARM_DESC(logregs, "Log changes in register values (for debugging)");
++
++// Sysfs attributes
++
++static bool ithc_is_config_valid(struct ithc *ithc) {
++	return ithc->config.device_id == DEVCFG_DEVICE_ID_TIC;
++}
++
++static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, char *buf) {
++	struct ithc *ithc = dev_get_drvdata(dev);
++	if (!ithc || !ithc_is_config_valid(ithc)) return -ENODEV;
++	return sprintf(buf, "0x%04x", ithc->config.vendor_id);
++}
++static DEVICE_ATTR_RO(vendor);
++static ssize_t product_show(struct device *dev, struct device_attribute *attr, char *buf) {
++	struct ithc *ithc = dev_get_drvdata(dev);
++	if (!ithc || !ithc_is_config_valid(ithc)) return -ENODEV;
++	return sprintf(buf, "0x%04x", ithc->config.product_id);
++}
++static DEVICE_ATTR_RO(product);
++static ssize_t revision_show(struct device *dev, struct device_attribute *attr, char *buf) {
++	struct ithc *ithc = dev_get_drvdata(dev);
++	if (!ithc || !ithc_is_config_valid(ithc)) return -ENODEV;
++	return sprintf(buf, "%u", ithc->config.revision);
++}
++static DEVICE_ATTR_RO(revision);
++static ssize_t fw_version_show(struct device *dev, struct device_attribute *attr, char *buf) {
++	struct ithc *ithc = dev_get_drvdata(dev);
++	if (!ithc || !ithc_is_config_valid(ithc)) return -ENODEV;
++	u32 v = ithc->config.fw_version;
++	return sprintf(buf, "%i.%i.%i.%i", v >> 24, v >> 16 & 0xff, v >> 8 & 0xff, v & 0xff);
++}
++static DEVICE_ATTR_RO(fw_version);
++
++static const struct attribute_group *ithc_attribute_groups[] = {
++	&(const struct attribute_group){
++		.name = DEVNAME,
++		.attrs = (struct attribute *[]){
++			&dev_attr_vendor.attr,
++			&dev_attr_product.attr,
++			&dev_attr_revision.attr,
++			&dev_attr_fw_version.attr,
++			NULL
++		},
++	},
++	NULL
++};
++
++// HID setup
++
++static int ithc_hid_start(struct hid_device *hdev) { return 0; }
++static void ithc_hid_stop(struct hid_device *hdev) { }
++static int ithc_hid_open(struct hid_device *hdev) { return 0; }
++static void ithc_hid_close(struct hid_device *hdev) { }
++
++static int ithc_hid_parse(struct hid_device *hdev) {
++	struct ithc *ithc = hdev->driver_data;
++	u64 val = 0;
++	WRITE_ONCE(ithc->hid_parse_done, false);
++	CHECK_RET(ithc_dma_tx, ithc, DMA_TX_CODE_GET_REPORT_DESCRIPTOR, sizeof val, &val);
++	if (!wait_event_timeout(ithc->wait_hid_parse, READ_ONCE(ithc->hid_parse_done), msecs_to_jiffies(1000))) return -ETIMEDOUT;
++	return 0;
++}
++
++static int ithc_hid_raw_request(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, size_t len, unsigned char rtype, int reqtype) {
++	struct ithc *ithc = hdev->driver_data;
++	if (!buf || !len) return -EINVAL;
++	u32 code;
++	if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT) code = DMA_TX_CODE_OUTPUT_REPORT;
++	else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_SET_REPORT) code = DMA_TX_CODE_SET_FEATURE;
++	else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_GET_REPORT) code = DMA_TX_CODE_GET_FEATURE;
++	else {
++		pci_err(ithc->pci, "unhandled hid request %i %i for report id %i\n", rtype, reqtype, reportnum);
++		return -EINVAL;
++	}
++	buf[0] = reportnum;
++	if (reqtype == HID_REQ_GET_REPORT) {
++		mutex_lock(&ithc->hid_get_feature_mutex);
++		ithc->hid_get_feature_buf = buf;
++		ithc->hid_get_feature_size = len;
++		mutex_unlock(&ithc->hid_get_feature_mutex);
++		int r = CHECK(ithc_dma_tx, ithc, code, 1, buf);
++		if (!r) {
++			r = wait_event_interruptible_timeout(ithc->wait_hid_get_feature, !ithc->hid_get_feature_buf, msecs_to_jiffies(1000));
++			if (!r) r = -ETIMEDOUT;
++			else if (r < 0) r = -EINTR;
++			else r = 0;
++		}
++		mutex_lock(&ithc->hid_get_feature_mutex);
++		ithc->hid_get_feature_buf = NULL;
++		if (!r) r = ithc->hid_get_feature_size;
++		mutex_unlock(&ithc->hid_get_feature_mutex);
++		return r;
++	}
++	CHECK_RET(ithc_dma_tx, ithc, code, len, buf);
++	return 0;
++}
++
++static struct hid_ll_driver ithc_ll_driver = {
++	.start = ithc_hid_start,
++	.stop = ithc_hid_stop,
++	.open = ithc_hid_open,
++	.close = ithc_hid_close,
++	.parse = ithc_hid_parse,
++	.raw_request = ithc_hid_raw_request,
++};
++
++static void ithc_hid_devres_release(struct device *dev, void *res) {
++	struct hid_device **hidm = res;
++	if (*hidm) hid_destroy_device(*hidm);
++}
++
++static int ithc_hid_init(struct ithc *ithc) {
++	struct hid_device **hidm = devres_alloc(ithc_hid_devres_release, sizeof *hidm, GFP_KERNEL);
++	if (!hidm) return -ENOMEM;
++	devres_add(&ithc->pci->dev, hidm);
++	struct hid_device *hid = hid_allocate_device();
++	if (IS_ERR(hid)) return PTR_ERR(hid);
++	*hidm = hid;
++
++	strscpy(hid->name, DEVFULLNAME, sizeof(hid->name));
++	strscpy(hid->phys, ithc->phys, sizeof(hid->phys));
++	hid->ll_driver = &ithc_ll_driver;
++	hid->bus = BUS_PCI;
++	hid->vendor = ithc->config.vendor_id;
++	hid->product = ithc->config.product_id;
++	hid->version = 0x100;
++	hid->dev.parent = &ithc->pci->dev;
++	hid->driver_data = ithc;
++
++	ithc->hid = hid;
++	return 0;
++}
++
++// Interrupts/polling
++
++static void ithc_activity_timer_callback(struct timer_list *t) {
++	struct ithc *ithc = container_of(t, struct ithc, activity_timer);
++	cpu_latency_qos_update_request(&ithc->activity_qos, PM_QOS_DEFAULT_VALUE);
++}
++
++void ithc_set_active(struct ithc *ithc) {
++	// When CPU usage is very low, the CPU can enter various low power states (C2-C10).
++	// This disrupts DMA, causing truncated DMA messages. ERROR_FLAG_DMA_UNKNOWN_12 will be set when this happens.
++	// The amount of truncated messages can become very high, resulting in user-visible effects (laggy/stuttering cursor).
++	// To avoid this, we use a CPU latency QoS request to prevent the CPU from entering low power states during touch interactions.
++	cpu_latency_qos_update_request(&ithc->activity_qos, 0);
++	mod_timer(&ithc->activity_timer, jiffies + msecs_to_jiffies(1000));
++}
++
++static int ithc_set_device_enabled(struct ithc *ithc, bool enable) {
++	u32 x = ithc->config.touch_cfg = (ithc->config.touch_cfg & ~(u32)DEVCFG_TOUCH_MASK) | DEVCFG_TOUCH_UNKNOWN_2
++		| (enable ? DEVCFG_TOUCH_ENABLE | DEVCFG_TOUCH_UNKNOWN_3 | DEVCFG_TOUCH_UNKNOWN_4 : 0);
++	return ithc_spi_command(ithc, SPI_CMD_CODE_WRITE, offsetof(struct ithc_device_config, touch_cfg), sizeof x, &x);
++}
++
++static void ithc_disable_interrupts(struct ithc *ithc) {
++	writel(0, &ithc->regs->error_control);
++	bitsb(&ithc->regs->spi_cmd.control, SPI_CMD_CONTROL_IRQ, 0);
++	bitsb(&ithc->regs->dma_rx[0].control, DMA_RX_CONTROL_IRQ_UNKNOWN_1 | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_UNKNOWN_4 | DMA_RX_CONTROL_IRQ_DATA, 0);
++	bitsb(&ithc->regs->dma_rx[1].control, DMA_RX_CONTROL_IRQ_UNKNOWN_1 | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_UNKNOWN_4 | DMA_RX_CONTROL_IRQ_DATA, 0);
++	bitsb(&ithc->regs->dma_tx.control, DMA_TX_CONTROL_IRQ, 0);
++}
++
++static void ithc_clear_dma_rx_interrupts(struct ithc *ithc, unsigned channel) {
++	writel(DMA_RX_STATUS_ERROR | DMA_RX_STATUS_UNKNOWN_4 | DMA_RX_STATUS_HAVE_DATA, &ithc->regs->dma_rx[channel].status);
++}
++
++static void ithc_clear_interrupts(struct ithc *ithc) {
++	writel(0xffffffff, &ithc->regs->error_flags);
++	writel(ERROR_STATUS_DMA | ERROR_STATUS_SPI, &ithc->regs->error_status);
++	writel(SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR, &ithc->regs->spi_cmd.status);
++	ithc_clear_dma_rx_interrupts(ithc, 0);
++	ithc_clear_dma_rx_interrupts(ithc, 1);
++	writel(DMA_TX_STATUS_DONE | DMA_TX_STATUS_ERROR | DMA_TX_STATUS_UNKNOWN_2, &ithc->regs->dma_tx.status);
++}
++
++static void ithc_process(struct ithc *ithc) {
++	ithc_log_regs(ithc);
++
++	// read and clear error bits
++	u32 err = readl(&ithc->regs->error_flags);
++	if (err) {
++		if (err & ~ERROR_FLAG_DMA_UNKNOWN_12) pci_err(ithc->pci, "error flags: 0x%08x\n", err);
++		writel(err, &ithc->regs->error_flags);
++	}
++
++	// process DMA rx
++	if (ithc_use_rx0) {
++		ithc_clear_dma_rx_interrupts(ithc, 0);
++		ithc_dma_rx(ithc, 0);
++	}
++	if (ithc_use_rx1) {
++		ithc_clear_dma_rx_interrupts(ithc, 1);
++		ithc_dma_rx(ithc, 1);
++	}
++
++	ithc_log_regs(ithc);
++}
++
++static irqreturn_t ithc_interrupt_thread(int irq, void *arg) {
++	struct ithc *ithc = arg;
++	pci_dbg(ithc->pci, "IRQ! err=%08x/%08x/%08x, cmd=%02x/%08x, rx0=%02x/%08x, rx1=%02x/%08x, tx=%02x/%08x\n",
++		readl(&ithc->regs->error_control), readl(&ithc->regs->error_status), readl(&ithc->regs->error_flags),
++		readb(&ithc->regs->spi_cmd.control), readl(&ithc->regs->spi_cmd.status),
++		readb(&ithc->regs->dma_rx[0].control), readl(&ithc->regs->dma_rx[0].status),
++		readb(&ithc->regs->dma_rx[1].control), readl(&ithc->regs->dma_rx[1].status),
++		readb(&ithc->regs->dma_tx.control), readl(&ithc->regs->dma_tx.status));
++	ithc_process(ithc);
++	return IRQ_HANDLED;
++}
++
++static int ithc_poll_thread(void *arg) {
++	struct ithc *ithc = arg;
++	unsigned sleep = 100;
++	while (!kthread_should_stop()) {
++		u32 n = ithc->dma_rx[1].num_received;
++		ithc_process(ithc);
++		if (n != ithc->dma_rx[1].num_received) sleep = 20;
++		else sleep = min(200u, sleep + (sleep >> 4) + 1);
++		msleep_interruptible(sleep);
++	}
++	return 0;
++}
++
++// Device initialization and shutdown
++
++static void ithc_disable(struct ithc *ithc) {
++	bitsl_set(&ithc->regs->control_bits, CONTROL_QUIESCE);
++	CHECK(waitl, ithc, &ithc->regs->control_bits, CONTROL_IS_QUIESCED, CONTROL_IS_QUIESCED);
++	bitsl(&ithc->regs->control_bits, CONTROL_NRESET, 0);
++	bitsb(&ithc->regs->spi_cmd.control, SPI_CMD_CONTROL_SEND, 0);
++	bitsb(&ithc->regs->dma_tx.control, DMA_TX_CONTROL_SEND, 0);
++	bitsb(&ithc->regs->dma_rx[0].control, DMA_RX_CONTROL_ENABLE, 0);
++	bitsb(&ithc->regs->dma_rx[1].control, DMA_RX_CONTROL_ENABLE, 0);
++	ithc_disable_interrupts(ithc);
++	ithc_clear_interrupts(ithc);
++}
++
++static int ithc_init_device(struct ithc *ithc) {
++	ithc_log_regs(ithc);
++	bool was_enabled = (readl(&ithc->regs->control_bits) & CONTROL_NRESET) != 0;
++	ithc_disable(ithc);
++	CHECK_RET(waitl, ithc, &ithc->regs->control_bits, CONTROL_READY, CONTROL_READY);
++	ithc_set_spi_config(ithc, 10, 0);
++	bitsl_set(&ithc->regs->dma_rx[0].unknown_init_bits, 0x80000000); // seems to help with reading config
++
++	if (was_enabled) if (msleep_interruptible(100)) return -EINTR;
++	bitsl(&ithc->regs->control_bits, CONTROL_QUIESCE, 0);
++	CHECK_RET(waitl, ithc, &ithc->regs->control_bits, CONTROL_IS_QUIESCED, 0);
++	for (int retries = 0; ; retries++) {
++		ithc_log_regs(ithc);
++		bitsl_set(&ithc->regs->control_bits, CONTROL_NRESET);
++		if (!waitl(ithc, &ithc->regs->state, 0xf, 2)) break;
++		if (retries > 5) {
++			pci_err(ithc->pci, "too many retries, failed to reset device\n");
++			return -ETIMEDOUT;
++		}
++		pci_err(ithc->pci, "invalid state, retrying reset\n");
++		bitsl(&ithc->regs->control_bits, CONTROL_NRESET, 0);
++		if (msleep_interruptible(1000)) return -EINTR;
++	}
++	ithc_log_regs(ithc);
++
++	CHECK(waitl, ithc, &ithc->regs->dma_rx[0].status, DMA_RX_STATUS_UNKNOWN_4, DMA_RX_STATUS_UNKNOWN_4);
++
++	// read config
++	for (int retries = 0; ; retries++) {
++		ithc_log_regs(ithc);
++		memset(&ithc->config, 0, sizeof ithc->config);
++		CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, 0, sizeof ithc->config, &ithc->config);
++		u32 *p = (void *)&ithc->config;
++		pci_info(ithc->pci, "config: %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
++			p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
++		if (ithc_is_config_valid(ithc)) break;
++		if (retries > 10) {
++			pci_err(ithc->pci, "failed to read config, unknown device ID 0x%08x\n", ithc->config.device_id);
++			return -EIO;
++		}
++		pci_err(ithc->pci, "failed to read config, retrying\n");
++		if (msleep_interruptible(100)) return -EINTR;
++	}
++	ithc_log_regs(ithc);
++
++	CHECK_RET(ithc_set_spi_config, ithc, DEVCFG_SPI_MAX_FREQ(ithc->config.spi_config), DEVCFG_SPI_MODE(ithc->config.spi_config));
++	CHECK_RET(ithc_set_device_enabled, ithc, true);
++	ithc_log_regs(ithc);
++	return 0;
++}
++
++int ithc_reset(struct ithc *ithc) {
++	// FIXME This should probably do devres_release_group()+ithc_start(). But because this is called during DMA
++	// processing, that would have to be done asynchronously (schedule_work()?). And with extra locking?
++	pci_err(ithc->pci, "reset\n");
++	CHECK(ithc_init_device, ithc);
++	if (ithc_use_rx0) ithc_dma_rx_enable(ithc, 0);
++	if (ithc_use_rx1) ithc_dma_rx_enable(ithc, 1);
++	ithc_log_regs(ithc);
++	pci_dbg(ithc->pci, "reset completed\n");
++	return 0;
++}
++
++static void ithc_stop(void *res) {
++	struct ithc *ithc = res;
++	pci_dbg(ithc->pci, "stopping\n");
++	ithc_log_regs(ithc);
++	if (ithc->poll_thread) CHECK(kthread_stop, ithc->poll_thread);
++	if (ithc->irq >= 0) disable_irq(ithc->irq);
++	CHECK(ithc_set_device_enabled, ithc, false);
++	ithc_disable(ithc);
++	del_timer_sync(&ithc->activity_timer);
++	cpu_latency_qos_remove_request(&ithc->activity_qos);
++	// clear dma config
++	for(unsigned i = 0; i < 2; i++) {
++		CHECK(waitl, ithc, &ithc->regs->dma_rx[i].status, DMA_RX_STATUS_ENABLED, 0);
++		lo_hi_writeq(0, &ithc->regs->dma_rx[i].addr);
++		writeb(0, &ithc->regs->dma_rx[i].num_bufs);
++		writeb(0, &ithc->regs->dma_rx[i].num_prds);
++	}
++	lo_hi_writeq(0, &ithc->regs->dma_tx.addr);
++	writeb(0, &ithc->regs->dma_tx.num_prds);
++	ithc_log_regs(ithc);
++	pci_dbg(ithc->pci, "stopped\n");
++}
++
++static void ithc_clear_drvdata(void *res) {
++	struct pci_dev *pci = res;
++	pci_set_drvdata(pci, NULL);
++}
++
++static int ithc_start(struct pci_dev *pci) {
++	pci_dbg(pci, "starting\n");
++	if (pci_get_drvdata(pci)) {
++		pci_err(pci, "device already initialized\n");
++		return -EINVAL;
++	}
++	if (!devres_open_group(&pci->dev, ithc_start, GFP_KERNEL)) return -ENOMEM;
++
++	struct ithc *ithc = devm_kzalloc(&pci->dev, sizeof *ithc, GFP_KERNEL);
++	if (!ithc) return -ENOMEM;
++	ithc->irq = -1;
++	ithc->pci = pci;
++	snprintf(ithc->phys, sizeof ithc->phys, "pci-%s/" DEVNAME, pci_name(pci));
++	init_waitqueue_head(&ithc->wait_hid_parse);
++	init_waitqueue_head(&ithc->wait_hid_get_feature);
++	mutex_init(&ithc->hid_get_feature_mutex);
++	pci_set_drvdata(pci, ithc);
++	CHECK_RET(devm_add_action_or_reset, &pci->dev, ithc_clear_drvdata, pci);
++	if (ithc_log_regs_enabled) ithc->prev_regs = devm_kzalloc(&pci->dev, sizeof *ithc->prev_regs, GFP_KERNEL);
++
++	CHECK_RET(pcim_enable_device, pci);
++	pci_set_master(pci);
++	CHECK_RET(pcim_iomap_regions, pci, BIT(0), DEVNAME " regs");
++	CHECK_RET(dma_set_mask_and_coherent, &pci->dev, DMA_BIT_MASK(64));
++	CHECK_RET(pci_set_power_state, pci, PCI_D0);
++	ithc->regs = pcim_iomap_table(pci)[0];
++
++	if (!ithc_use_polling) {
++		CHECK_RET(pci_alloc_irq_vectors, pci, 1, 1, PCI_IRQ_MSI | PCI_IRQ_MSIX);
++		ithc->irq = CHECK(pci_irq_vector, pci, 0);
++		if (ithc->irq < 0) return ithc->irq;
++	}
++
++	CHECK_RET(ithc_init_device, ithc);
++	CHECK(devm_device_add_groups, &pci->dev, ithc_attribute_groups);
++	if (ithc_use_rx0) CHECK_RET(ithc_dma_rx_init, ithc, 0, ithc_use_rx1 ? DEVNAME "0" : DEVNAME);
++	if (ithc_use_rx1) CHECK_RET(ithc_dma_rx_init, ithc, 1, ithc_use_rx0 ? DEVNAME "1" : DEVNAME);
++	CHECK_RET(ithc_dma_tx_init, ithc);
++
++	CHECK_RET(ithc_hid_init, ithc);
++
++	cpu_latency_qos_add_request(&ithc->activity_qos, PM_QOS_DEFAULT_VALUE);
++	timer_setup(&ithc->activity_timer, ithc_activity_timer_callback, 0);
++
++	// add ithc_stop callback AFTER setting up DMA buffers, so that polling/irqs/DMA are disabled BEFORE the buffers are freed
++	CHECK_RET(devm_add_action_or_reset, &pci->dev, ithc_stop, ithc);
++
++	if (ithc_use_polling) {
++		pci_info(pci, "using polling instead of irq\n");
++		// use a thread instead of simple timer because we want to be able to sleep
++		ithc->poll_thread = kthread_run(ithc_poll_thread, ithc, DEVNAME "poll");
++		if (IS_ERR(ithc->poll_thread)) {
++			int err = PTR_ERR(ithc->poll_thread);
++			ithc->poll_thread = NULL;
++			return err;
++		}
++	} else {
++		CHECK_RET(devm_request_threaded_irq, &pci->dev, ithc->irq, NULL, ithc_interrupt_thread, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, DEVNAME, ithc);
++	}
++
++	if (ithc_use_rx0) ithc_dma_rx_enable(ithc, 0);
++	if (ithc_use_rx1) ithc_dma_rx_enable(ithc, 1);
++
++	// hid_add_device can only be called after irq/polling is started and DMA is enabled, because it calls ithc_hid_parse which reads the report descriptor via DMA
++	CHECK_RET(hid_add_device, ithc->hid);
++
++	CHECK(ithc_debug_init, ithc);
++
++	pci_dbg(pci, "started\n");
++	return 0;
++}
++
++static int ithc_probe(struct pci_dev *pci, const struct pci_device_id *id) {
++	pci_dbg(pci, "device probe\n");
++	return ithc_start(pci);
++}
++
++static void ithc_remove(struct pci_dev *pci) {
++	pci_dbg(pci, "device remove\n");
++	// all cleanup is handled by devres
++}
++
++static int ithc_suspend(struct device *dev) {
++	struct pci_dev *pci = to_pci_dev(dev);
++	pci_dbg(pci, "pm suspend\n");
++	devres_release_group(dev, ithc_start);
++	return 0;
++}
++
++static int ithc_resume(struct device *dev) {
++	struct pci_dev *pci = to_pci_dev(dev);
++	pci_dbg(pci, "pm resume\n");
++	return ithc_start(pci);
++}
++
++static int ithc_freeze(struct device *dev) {
++	struct pci_dev *pci = to_pci_dev(dev);
++	pci_dbg(pci, "pm freeze\n");
++	devres_release_group(dev, ithc_start);
++	return 0;
++}
++
++static int ithc_thaw(struct device *dev) {
++	struct pci_dev *pci = to_pci_dev(dev);
++	pci_dbg(pci, "pm thaw\n");
++	return ithc_start(pci);
++}
++
++static int ithc_restore(struct device *dev) {
++	struct pci_dev *pci = to_pci_dev(dev);
++	pci_dbg(pci, "pm restore\n");
++	return ithc_start(pci);
++}
++
++static struct pci_driver ithc_driver = {
++	.name = DEVNAME,
++	.id_table = ithc_pci_tbl,
++	.probe = ithc_probe,
++	.remove = ithc_remove,
++	.driver.pm = &(const struct dev_pm_ops) {
++		.suspend = ithc_suspend,
++		.resume = ithc_resume,
++		.freeze = ithc_freeze,
++		.thaw = ithc_thaw,
++		.restore = ithc_restore,
++	},
++	//.dev_groups = ithc_attribute_groups, // could use this (since 5.14), however the attributes won't have valid values until config has been read anyway
++};
++
++static int __init ithc_init(void) {
++	return pci_register_driver(&ithc_driver);
++}
++
++static void __exit ithc_exit(void) {
++	pci_unregister_driver(&ithc_driver);
++}
++
++module_init(ithc_init);
++module_exit(ithc_exit);
++
+diff --git a/drivers/hid/ithc/ithc-regs.c b/drivers/hid/ithc/ithc-regs.c
+new file mode 100644
+index 000000000000..85d567b05761
+--- /dev/null
++++ b/drivers/hid/ithc/ithc-regs.c
+@@ -0,0 +1,64 @@
++#include "ithc.h"
++
++#define reg_num(r) (0x1fff & (u16)(__force u64)(r))
++
++void bitsl(__iomem u32 *reg, u32 mask, u32 val) {
++	if (val & ~mask) pr_err("register 0x%x: invalid value 0x%x for bitmask 0x%x\n", reg_num(reg), val, mask);
++	writel((readl(reg) & ~mask) | (val & mask), reg);
++}
++
++void bitsb(__iomem u8 *reg, u8 mask, u8 val) {
++	if (val & ~mask) pr_err("register 0x%x: invalid value 0x%x for bitmask 0x%x\n", reg_num(reg), val, mask);
++	writeb((readb(reg) & ~mask) | (val & mask), reg);
++}
++
++int waitl(struct ithc *ithc, __iomem u32 *reg, u32 mask, u32 val) {
++	pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%08x val 0x%08x\n", reg_num(reg), mask, val);
++	u32 x;
++	if (readl_poll_timeout(reg, x, (x & mask) == val, 200, 1000*1000)) {
++		pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%08x val 0x%08x\n", reg_num(reg), mask, val);
++		return -ETIMEDOUT;
++	}
++	pci_dbg(ithc->pci, "done waiting\n");
++	return 0;
++}
++
++int waitb(struct ithc *ithc, __iomem u8 *reg, u8 mask, u8 val) {
++	pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%02x val 0x%02x\n", reg_num(reg), mask, val);
++	u8 x;
++	if (readb_poll_timeout(reg, x, (x & mask) == val, 200, 1000*1000)) {
++		pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%02x val 0x%02x\n", reg_num(reg), mask, val);
++		return -ETIMEDOUT;
++	}
++	pci_dbg(ithc->pci, "done waiting\n");
++	return 0;
++}
++
++int ithc_set_spi_config(struct ithc *ithc, u8 speed, u8 mode) {
++	pci_dbg(ithc->pci, "setting SPI speed to %i, mode %i\n", speed, mode);
++	if (mode == 3) mode = 2;
++	bitsl(&ithc->regs->spi_config,
++		SPI_CONFIG_MODE(0xff) | SPI_CONFIG_SPEED(0xff) | SPI_CONFIG_UNKNOWN_18(0xff) | SPI_CONFIG_SPEED2(0xff),
++		SPI_CONFIG_MODE(mode) | SPI_CONFIG_SPEED(speed) | SPI_CONFIG_UNKNOWN_18(0) | SPI_CONFIG_SPEED2(speed));
++	return 0;
++}
++
++int ithc_spi_command(struct ithc *ithc, u8 command, u32 offset, u32 size, void *data) {
++	pci_dbg(ithc->pci, "SPI command %u, size %u, offset %u\n", command, size, offset);
++	if (size > sizeof ithc->regs->spi_cmd.data) return -EINVAL;
++	CHECK_RET(waitl, ithc, &ithc->regs->spi_cmd.status, SPI_CMD_STATUS_BUSY, 0);
++	writel(SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR, &ithc->regs->spi_cmd.status);
++	writeb(command, &ithc->regs->spi_cmd.code);
++	writew(size, &ithc->regs->spi_cmd.size);
++	writel(offset, &ithc->regs->spi_cmd.offset);
++	u32 *p = data, n = (size + 3) / 4;
++	for (u32 i = 0; i < n; i++) writel(p[i], &ithc->regs->spi_cmd.data[i]);
++	bitsb_set(&ithc->regs->spi_cmd.control, SPI_CMD_CONTROL_SEND);
++	CHECK_RET(waitl, ithc, &ithc->regs->spi_cmd.status, SPI_CMD_STATUS_BUSY, 0);
++	if ((readl(&ithc->regs->spi_cmd.status) & (SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR)) != SPI_CMD_STATUS_DONE) return -EIO;
++	if (readw(&ithc->regs->spi_cmd.size) != size) return -EMSGSIZE;
++	for (u32 i = 0; i < n; i++) p[i] = readl(&ithc->regs->spi_cmd.data[i]);
++	writel(SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR, &ithc->regs->spi_cmd.status);
++	return 0;
++}
++
+diff --git a/drivers/hid/ithc/ithc-regs.h b/drivers/hid/ithc/ithc-regs.h
+new file mode 100644
+index 000000000000..1a96092ed7ee
+--- /dev/null
++++ b/drivers/hid/ithc/ithc-regs.h
+@@ -0,0 +1,186 @@
++#define CONTROL_QUIESCE                     BIT(1)
++#define CONTROL_IS_QUIESCED                 BIT(2)
++#define CONTROL_NRESET                      BIT(3)
++#define CONTROL_READY                       BIT(29)
++
++#define SPI_CONFIG_MODE(x)                  (((x) & 3) << 2)
++#define SPI_CONFIG_SPEED(x)                 (((x) & 7) << 4)
++#define SPI_CONFIG_UNKNOWN_18(x)            (((x) & 3) << 18)
++#define SPI_CONFIG_SPEED2(x)                (((x) & 0xf) << 20) // high bit = high speed mode?
++
++#define ERROR_CONTROL_UNKNOWN_0             BIT(0)
++#define ERROR_CONTROL_DISABLE_DMA           BIT(1) // clears DMA_RX_CONTROL_ENABLE when a DMA error occurs
++#define ERROR_CONTROL_UNKNOWN_2             BIT(2)
++#define ERROR_CONTROL_UNKNOWN_3             BIT(3)
++#define ERROR_CONTROL_IRQ_DMA_UNKNOWN_9     BIT(9)
++#define ERROR_CONTROL_IRQ_DMA_UNKNOWN_10    BIT(10)
++#define ERROR_CONTROL_IRQ_DMA_UNKNOWN_12    BIT(12)
++#define ERROR_CONTROL_IRQ_DMA_UNKNOWN_13    BIT(13)
++#define ERROR_CONTROL_UNKNOWN_16(x)         (((x) & 0xff) << 16) // spi error code irq?
++#define ERROR_CONTROL_SET_DMA_STATUS        BIT(29) // sets DMA_RX_STATUS_ERROR when a DMA error occurs
++
++#define ERROR_STATUS_DMA                    BIT(28)
++#define ERROR_STATUS_SPI                    BIT(30)
++
++#define ERROR_FLAG_DMA_UNKNOWN_9            BIT(9)
++#define ERROR_FLAG_DMA_UNKNOWN_10           BIT(10)
++#define ERROR_FLAG_DMA_UNKNOWN_12           BIT(12) // set when we receive a truncated DMA message
++#define ERROR_FLAG_DMA_UNKNOWN_13           BIT(13)
++#define ERROR_FLAG_SPI_BUS_TURNAROUND       BIT(16)
++#define ERROR_FLAG_SPI_RESPONSE_TIMEOUT     BIT(17)
++#define ERROR_FLAG_SPI_INTRA_PACKET_TIMEOUT BIT(18)
++#define ERROR_FLAG_SPI_INVALID_RESPONSE     BIT(19)
++#define ERROR_FLAG_SPI_HS_RX_TIMEOUT        BIT(20)
++#define ERROR_FLAG_SPI_TOUCH_IC_INIT        BIT(21)
++
++#define SPI_CMD_CONTROL_SEND                BIT(0) // cleared by device when sending is complete
++#define SPI_CMD_CONTROL_IRQ                 BIT(1)
++
++#define SPI_CMD_CODE_READ                   4
++#define SPI_CMD_CODE_WRITE                  6
++
++#define SPI_CMD_STATUS_DONE                 BIT(0)
++#define SPI_CMD_STATUS_ERROR                BIT(1)
++#define SPI_CMD_STATUS_BUSY                 BIT(3)
++
++#define DMA_TX_CONTROL_SEND                 BIT(0) // cleared by device when sending is complete
++#define DMA_TX_CONTROL_IRQ                  BIT(3)
++
++#define DMA_TX_STATUS_DONE                  BIT(0)
++#define DMA_TX_STATUS_ERROR                 BIT(1)
++#define DMA_TX_STATUS_UNKNOWN_2             BIT(2)
++#define DMA_TX_STATUS_UNKNOWN_3             BIT(3) // busy?
++
++#define DMA_RX_CONTROL_ENABLE               BIT(0)
++#define DMA_RX_CONTROL_IRQ_UNKNOWN_1        BIT(1) // rx1 only?
++#define DMA_RX_CONTROL_IRQ_ERROR            BIT(3) // rx1 only?
++#define DMA_RX_CONTROL_IRQ_UNKNOWN_4        BIT(4) // rx0 only?
++#define DMA_RX_CONTROL_IRQ_DATA             BIT(5)
++
++#define DMA_RX_CONTROL2_UNKNOWN_5           BIT(5) // rx0 only?
++#define DMA_RX_CONTROL2_RESET               BIT(7) // resets ringbuffer indices
++
++#define DMA_RX_WRAP_FLAG                    BIT(7)
++
++#define DMA_RX_STATUS_ERROR                 BIT(3)
++#define DMA_RX_STATUS_UNKNOWN_4             BIT(4) // set in rx0 after using CONTROL_NRESET when it becomes possible to read config (can take >100ms)
++#define DMA_RX_STATUS_HAVE_DATA             BIT(5)
++#define DMA_RX_STATUS_ENABLED               BIT(8)
++
++#define COUNTER_RESET                       BIT(31)
++
++struct ithc_registers {
++	/* 0000 */ u32 _unknown_0000[1024];
++	/* 1000 */ u32 _unknown_1000;
++	/* 1004 */ u32 _unknown_1004;
++	/* 1008 */ u32 control_bits;
++	/* 100c */ u32 _unknown_100c;
++	/* 1010 */ u32 spi_config;
++	/* 1014 */ u32 _unknown_1014[3];
++	/* 1020 */ u32 error_control;
++	/* 1024 */ u32 error_status; // write to clear
++	/* 1028 */ u32 error_flags; // write to clear
++	/* 102c */ u32 _unknown_102c[5];
++	struct {
++		/* 1040 */ u8 control;
++		/* 1041 */ u8 code;
++		/* 1042 */ u16 size;
++		/* 1044 */ u32 status; // write to clear
++		/* 1048 */ u32 offset;
++		/* 104c */ u32 data[16];
++		/* 108c */ u32 _unknown_108c;
++	} spi_cmd;
++	struct {
++		/* 1090 */ u64 addr; // cannot be written with writeq(), must use lo_hi_writeq()
++		/* 1098 */ u8 control;
++		/* 1099 */ u8 _unknown_1099;
++		/* 109a */ u8 _unknown_109a;
++		/* 109b */ u8 num_prds;
++		/* 109c */ u32 status; // write to clear
++	} dma_tx;
++	/* 10a0 */ u32 _unknown_10a0[7];
++	/* 10bc */ u32 state; // is 0xe0000402 (dev config val 0) after CONTROL_NRESET, 0xe0000461 after first touch, 0xe0000401 after DMA_RX_CODE_RESET
++	/* 10c0 */ u32 _unknown_10c0[8];
++	/* 10e0 */ u32 _unknown_10e0_counters[3];
++	/* 10ec */ u32 _unknown_10ec[5];
++	struct {
++		/* 1100/1200 */ u64 addr; // cannot be written with writeq(), must use lo_hi_writeq()
++		/* 1108/1208 */ u8 num_bufs;
++		/* 1109/1209 */ u8 num_prds;
++		/* 110a/120a */ u16 _unknown_110a;
++		/* 110c/120c */ u8 control;
++		/* 110d/120d */ u8 head;
++		/* 110e/120e */ u8 tail;
++		/* 110f/120f */ u8 control2;
++		/* 1110/1210 */ u32 status; // write to clear
++		/* 1114/1214 */ u32 _unknown_1114;
++		/* 1118/1218 */ u64 _unknown_1118_guc_addr;
++		/* 1120/1220 */ u32 _unknown_1120_guc;
++		/* 1124/1224 */ u32 _unknown_1124_guc;
++		/* 1128/1228 */ u32 unknown_init_bits; // bit 2 = guc related, bit 3 = rx1 related, bit 4 = guc related
++		/* 112c/122c */ u32 _unknown_112c;
++		/* 1130/1230 */ u64 _unknown_1130_guc_addr;
++		/* 1138/1238 */ u32 _unknown_1138_guc;
++		/* 113c/123c */ u32 _unknown_113c;
++		/* 1140/1240 */ u32 _unknown_1140_guc;
++		/* 1144/1244 */ u32 _unknown_1144[23];
++		/* 11a0/12a0 */ u32 _unknown_11a0_counters[6];
++		/* 11b8/12b8 */ u32 _unknown_11b8[18];
++	} dma_rx[2];
++};
++static_assert(sizeof(struct ithc_registers) == 0x1300);
++
++#define DEVCFG_DMA_RX_SIZE(x)          ((((x) & 0x3fff) + 1) << 6)
++#define DEVCFG_DMA_TX_SIZE(x)          (((((x) >> 14) & 0x3ff) + 1) << 6)
++
++#define DEVCFG_TOUCH_MASK              0x3f
++#define DEVCFG_TOUCH_ENABLE            BIT(0)
++#define DEVCFG_TOUCH_UNKNOWN_1         BIT(1)
++#define DEVCFG_TOUCH_UNKNOWN_2         BIT(2)
++#define DEVCFG_TOUCH_UNKNOWN_3         BIT(3)
++#define DEVCFG_TOUCH_UNKNOWN_4         BIT(4)
++#define DEVCFG_TOUCH_UNKNOWN_5         BIT(5)
++#define DEVCFG_TOUCH_UNKNOWN_6         BIT(6)
++
++#define DEVCFG_DEVICE_ID_TIC           0x43495424 // "$TIC"
++
++#define DEVCFG_SPI_MAX_FREQ(x)         (((x) >> 1) & 0xf) // high bit = use high speed mode?
++#define DEVCFG_SPI_MODE(x)             (((x) >> 6) & 3)
++#define DEVCFG_SPI_UNKNOWN_8(x)        (((x) >> 8) & 0x3f)
++#define DEVCFG_SPI_NEEDS_HEARTBEAT     BIT(20)
++#define DEVCFG_SPI_HEARTBEAT_INTERVAL  (((x) >> 21) & 7)
++#define DEVCFG_SPI_UNKNOWN_25          BIT(25)
++#define DEVCFG_SPI_UNKNOWN_26          BIT(26)
++#define DEVCFG_SPI_UNKNOWN_27          BIT(27)
++#define DEVCFG_SPI_DELAY               (((x) >> 28) & 7)
++#define DEVCFG_SPI_USE_EXT_READ_CFG    BIT(31)
++
++struct ithc_device_config {
++	u32 _unknown_00;      // 00 = 0xe0000402 (0xe0000401 after DMA_RX_CODE_RESET)
++	u32 _unknown_04;      // 04 = 0x00000000
++	u32 dma_buf_sizes;    // 08 = 0x000a00ff
++	u32 touch_cfg;        // 0c = 0x0000001c
++	u32 _unknown_10;      // 10 = 0x0000001c
++	u32 device_id;        // 14 = 0x43495424 = "$TIC"
++	u32 spi_config;       // 18 = 0xfda00a2e
++	u16 vendor_id;        // 1c = 0x045e = Microsoft Corp.
++	u16 product_id;       // 1e = 0x0c1a
++	u32 revision;         // 20 = 0x00000001
++	u32 fw_version;       // 24 = 0x05008a8b = 5.0.138.139
++	u32 _unknown_28;      // 28 = 0x00000000
++	u32 fw_mode;          // 2c = 0x00000000
++	u32 _unknown_30;      // 30 = 0x00000000
++	u32 _unknown_34;      // 34 = 0x0404035e (u8,u8,u8,u8 = version?)
++	u32 _unknown_38;      // 38 = 0x000001c0 (0x000001c1 after DMA_RX_CODE_RESET)
++	u32 _unknown_3c;      // 3c = 0x00000002
++};
++
++void bitsl(__iomem u32 *reg, u32 mask, u32 val);
++void bitsb(__iomem u8 *reg, u8 mask, u8 val);
++#define bitsl_set(reg, x) bitsl(reg, x, x)
++#define bitsb_set(reg, x) bitsb(reg, x, x)
++int waitl(struct ithc *ithc, __iomem u32 *reg, u32 mask, u32 val);
++int waitb(struct ithc *ithc, __iomem u8 *reg, u8 mask, u8 val);
++int ithc_set_spi_config(struct ithc *ithc, u8 speed, u8 mode);
++int ithc_spi_command(struct ithc *ithc, u8 command, u32 offset, u32 size, void *data);
++
+diff --git a/drivers/hid/ithc/ithc.h b/drivers/hid/ithc/ithc.h
+new file mode 100644
+index 000000000000..6a9b0d480bc1
+--- /dev/null
++++ b/drivers/hid/ithc/ithc.h
+@@ -0,0 +1,60 @@
++#include <linux/module.h>
++#include <linux/input.h>
++#include <linux/hid.h>
++#include <linux/dma-mapping.h>
++#include <linux/highmem.h>
++#include <linux/pci.h>
++#include <linux/io-64-nonatomic-lo-hi.h>
++#include <linux/iopoll.h>
++#include <linux/delay.h>
++#include <linux/kthread.h>
++#include <linux/miscdevice.h>
++#include <linux/debugfs.h>
++#include <linux/poll.h>
++#include <linux/timer.h>
++#include <linux/pm_qos.h>
++
++#define DEVNAME "ithc"
++#define DEVFULLNAME "Intel Touch Host Controller"
++
++#undef pr_fmt
++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
++
++#define CHECK(fn, ...) ({ int r = fn(__VA_ARGS__); if (r < 0) pci_err(ithc->pci, "%s: %s failed with %i\n", __func__, #fn, r); r; })
++#define CHECK_RET(...) do { int r = CHECK(__VA_ARGS__); if (r < 0) return r; } while(0)
++
++#define NUM_RX_BUF 16
++
++struct ithc;
++
++#include "ithc-regs.h"
++#include "ithc-dma.h"
++
++struct ithc {
++	char phys[32];
++	struct pci_dev *pci;
++	int irq;
++	struct task_struct *poll_thread;
++	struct pm_qos_request activity_qos;
++	struct timer_list activity_timer;
++
++	struct hid_device *hid;
++	bool hid_parse_done;
++	wait_queue_head_t wait_hid_parse;
++	wait_queue_head_t wait_hid_get_feature;
++	struct mutex hid_get_feature_mutex;
++	void *hid_get_feature_buf;
++	size_t hid_get_feature_size;
++
++	struct ithc_registers __iomem *regs;
++	struct ithc_registers *prev_regs; // for debugging
++	struct ithc_device_config config;
++	struct ithc_dma_rx dma_rx[2];
++	struct ithc_dma_tx dma_tx;
++};
++
++int ithc_reset(struct ithc *ithc);
++void ithc_set_active(struct ithc *ithc);
++int ithc_debug_init(struct ithc *ithc);
++void ithc_log_regs(struct ithc *ithc);
++
+-- 
+2.42.0
+

+ 49 - 0
patches/6.5/0006-surface-sam.patch

@@ -0,0 +1,49 @@
+From b81f708b2a9b753fbd5f785be2a30c71befec1a9 Mon Sep 17 00:00:00 2001
+From: Maximilian Luz <luzmaximilian@gmail.com>
+Date: Tue, 3 Oct 2023 02:15:04 +0200
+Subject: [PATCH] wip! serial: core: Disable runtime-suspend by default
+
+Commit 84a9582fd203 ("serial: core: Start managing serial controllers to
+enable runtime PM"), broke suspend on various Microsoft Surface devices.
+In particular, devices with the Surface Aggregator Module (SAM) EC
+connected via the Surface Serial Hub (SSH), a UART device.
+
+The core issue is that any UART communication is blocked when the UART
+port is in runtime suspend. Data sent during that time will be cached
+and transferred only after the device has been runtime-resumed.
+Unfortunately, any runtime PM transitions are blocked right before
+entering normal suspend and only unblocked after resuming. This means,
+that if we enter normal suspend when the port is runtime suspended, any
+communication will be blocked until the device resumes.
+
+Unfortunately, however, we need to tell the SAM EC when we are
+suspending and resuming to quiesce / re-enable events, turn off the
+keyboard backlight, and transition it to a lower power mode. This is
+currently done in prepare()/complete() and suspend()/resume(). With the
+commit mentioned above, these operations now time out, causing suspend
+to fail.
+
+As a temporary workaround: Disable runtime PM on the UART ports.
+
+Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
+Patchset: surface-sam
+---
+ drivers/tty/serial/serial_port.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/tty/serial/serial_port.c b/drivers/tty/serial/serial_port.c
+index 862423237007..6ceacea5e790 100644
+--- a/drivers/tty/serial/serial_port.c
++++ b/drivers/tty/serial/serial_port.c
+@@ -55,6 +55,8 @@ static int serial_port_probe(struct device *dev)
+ 	pm_runtime_set_autosuspend_delay(dev, SERIAL_PORT_AUTOSUSPEND_DELAY_MS);
+ 	pm_runtime_use_autosuspend(dev);
+ 
++	pm_runtime_forbid(dev);
++
+ 	return 0;
+ }
+ 
+-- 
+2.42.0
+

+ 335 - 0
patches/6.5/0007-surface-sam-over-hid.patch

@@ -0,0 +1,335 @@
+From 57a9790031ee4e702a633a58e7fc1e0923457975 Mon Sep 17 00:00:00 2001
+From: Maximilian Luz <luzmaximilian@gmail.com>
+Date: Sat, 25 Jul 2020 17:19:53 +0200
+Subject: [PATCH] i2c: acpi: Implement RawBytes read access
+
+Microsoft Surface Pro 4 and Book 1 devices access the MSHW0030 I2C
+device via a generic serial bus operation region and RawBytes read
+access. On the Surface Book 1, this access is required to turn on (and
+off) the discrete GPU.
+
+Multiple things are to note here:
+
+a) The RawBytes access is device/driver dependent. The ACPI
+   specification states:
+
+   > Raw accesses assume that the writer has knowledge of the bus that
+   > the access is made over and the device that is being accessed. The
+   > protocol may only ensure that the buffer is transmitted to the
+   > appropriate driver, but the driver must be able to interpret the
+   > buffer to communicate to a register.
+
+   Thus this implementation may likely not work on other devices
+   accessing I2C via the RawBytes accessor type.
+
+b) The MSHW0030 I2C device is an HID-over-I2C device which seems to
+   serve multiple functions:
+
+   1. It is the main access point for the legacy-type Surface Aggregator
+      Module (also referred to as SAM-over-HID, as opposed to the newer
+      SAM-over-SSH/UART). It has currently not been determined on how
+      support for the legacy SAM should be implemented. Likely via a
+      custom HID driver.
+
+   2. It seems to serve as the HID device for the Integrated Sensor Hub.
+      This might complicate matters with regards to implementing a
+      SAM-over-HID driver required by legacy SAM.
+
+In light of this, the simplest approach has been chosen for now.
+However, it may make more sense regarding breakage and compatibility to
+either provide functionality for replacing or enhancing the default
+operation region handler via some additional API functions, or even to
+completely blacklist MSHW0030 from the I2C core and provide a custom
+driver for it.
+
+Replacing/enhancing the default operation region handler would, however,
+either require some sort of secondary driver and access point for it,
+from which the new API functions would be called and the new handler
+(part) would be installed, or hard-coding them via some sort of
+quirk-like interface into the I2C core.
+
+Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
+Patchset: surface-sam-over-hid
+---
+ drivers/i2c/i2c-core-acpi.c | 35 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 35 insertions(+)
+
+diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c
+index d6037a328669..a290ebc77aea 100644
+--- a/drivers/i2c/i2c-core-acpi.c
++++ b/drivers/i2c/i2c-core-acpi.c
+@@ -628,6 +628,28 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client,
+ 	return (ret == 1) ? 0 : -EIO;
+ }
+ 
++static int acpi_gsb_i2c_write_raw_bytes(struct i2c_client *client,
++		u8 *data, u8 data_len)
++{
++	struct i2c_msg msgs[1];
++	int ret = AE_OK;
++
++	msgs[0].addr = client->addr;
++	msgs[0].flags = client->flags;
++	msgs[0].len = data_len + 1;
++	msgs[0].buf = data;
++
++	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
++
++	if (ret < 0) {
++		dev_err(&client->adapter->dev, "i2c write failed: %d\n", ret);
++		return ret;
++	}
++
++	/* 1 transfer must have completed successfully */
++	return (ret == 1) ? 0 : -EIO;
++}
++
+ static acpi_status
+ i2c_acpi_space_handler(u32 function, acpi_physical_address command,
+ 			u32 bits, u64 *value64,
+@@ -729,6 +751,19 @@ i2c_acpi_space_handler(u32 function, acpi_physical_address command,
+ 		}
+ 		break;
+ 
++	case ACPI_GSB_ACCESS_ATTRIB_RAW_BYTES:
++		if (action == ACPI_READ) {
++			dev_warn(&adapter->dev,
++				 "protocol 0x%02x not supported for client 0x%02x\n",
++				 accessor_type, client->addr);
++			ret = AE_BAD_PARAMETER;
++			goto err;
++		} else {
++			status = acpi_gsb_i2c_write_raw_bytes(client,
++					gsb->data, info->access_length);
++		}
++		break;
++
+ 	default:
+ 		dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n",
+ 			 accessor_type, client->addr);
+-- 
+2.42.0
+
+From 0db288ee722c357b3c786102e3d3d54bd594cec3 Mon Sep 17 00:00:00 2001
+From: Maximilian Luz <luzmaximilian@gmail.com>
+Date: Sat, 13 Feb 2021 16:41:18 +0100
+Subject: [PATCH] platform/surface: Add driver for Surface Book 1 dGPU switch
+
+Add driver exposing the discrete GPU power-switch of the  Microsoft
+Surface Book 1 to user-space.
+
+On the Surface Book 1, the dGPU power is controlled via the Surface
+System Aggregator Module (SAM). The specific SAM-over-HID command for
+this is exposed via ACPI. This module provides a simple driver exposing
+the ACPI call via a sysfs parameter to user-space, so that users can
+easily power-on/-off the dGPU.
+
+Patchset: surface-sam-over-hid
+---
+ drivers/platform/surface/Kconfig              |   7 +
+ drivers/platform/surface/Makefile             |   1 +
+ .../surface/surfacebook1_dgpu_switch.c        | 162 ++++++++++++++++++
+ 3 files changed, 170 insertions(+)
+ create mode 100644 drivers/platform/surface/surfacebook1_dgpu_switch.c
+
+diff --git a/drivers/platform/surface/Kconfig b/drivers/platform/surface/Kconfig
+index b629e82af97c..68656e8f309e 100644
+--- a/drivers/platform/surface/Kconfig
++++ b/drivers/platform/surface/Kconfig
+@@ -149,6 +149,13 @@ config SURFACE_AGGREGATOR_TABLET_SWITCH
+ 	  Select M or Y here, if you want to provide tablet-mode switch input
+ 	  events on the Surface Pro 8, Surface Pro X, and Surface Laptop Studio.
+ 
++config SURFACE_BOOK1_DGPU_SWITCH
++	tristate "Surface Book 1 dGPU Switch Driver"
++	depends on SYSFS
++	help
++	  This driver provides a sysfs switch to set the power-state of the
++	  discrete GPU found on the Microsoft Surface Book 1.
++
+ config SURFACE_DTX
+ 	tristate "Surface DTX (Detachment System) Driver"
+ 	depends on SURFACE_AGGREGATOR
+diff --git a/drivers/platform/surface/Makefile b/drivers/platform/surface/Makefile
+index 53344330939b..7efcd0cdb532 100644
+--- a/drivers/platform/surface/Makefile
++++ b/drivers/platform/surface/Makefile
+@@ -12,6 +12,7 @@ obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV)	+= surface_aggregator_cdev.o
+ obj-$(CONFIG_SURFACE_AGGREGATOR_HUB)	+= surface_aggregator_hub.o
+ obj-$(CONFIG_SURFACE_AGGREGATOR_REGISTRY) += surface_aggregator_registry.o
+ obj-$(CONFIG_SURFACE_AGGREGATOR_TABLET_SWITCH) += surface_aggregator_tabletsw.o
++obj-$(CONFIG_SURFACE_BOOK1_DGPU_SWITCH) += surfacebook1_dgpu_switch.o
+ obj-$(CONFIG_SURFACE_DTX)		+= surface_dtx.o
+ obj-$(CONFIG_SURFACE_GPE)		+= surface_gpe.o
+ obj-$(CONFIG_SURFACE_HOTPLUG)		+= surface_hotplug.o
+diff --git a/drivers/platform/surface/surfacebook1_dgpu_switch.c b/drivers/platform/surface/surfacebook1_dgpu_switch.c
+new file mode 100644
+index 000000000000..8b816ed8f35c
+--- /dev/null
++++ b/drivers/platform/surface/surfacebook1_dgpu_switch.c
+@@ -0,0 +1,162 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/acpi.h>
++#include <linux/platform_device.h>
++
++
++#ifdef pr_fmt
++#undef pr_fmt
++#endif
++#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__
++
++
++static const guid_t dgpu_sw_guid = GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4,
++	0x95, 0xed, 0xab, 0x16, 0x65, 0x49, 0x80, 0x35);
++
++#define DGPUSW_ACPI_PATH_DSM	"\\_SB_.PCI0.LPCB.EC0_.VGBI"
++#define DGPUSW_ACPI_PATH_HGON	"\\_SB_.PCI0.RP05.HGON"
++#define DGPUSW_ACPI_PATH_HGOF	"\\_SB_.PCI0.RP05.HGOF"
++
++
++static int sb1_dgpu_sw_dsmcall(void)
++{
++	union acpi_object *ret;
++	acpi_handle handle;
++	acpi_status status;
++
++	status = acpi_get_handle(NULL, DGPUSW_ACPI_PATH_DSM, &handle);
++	if (status)
++		return -EINVAL;
++
++	ret = acpi_evaluate_dsm_typed(handle, &dgpu_sw_guid, 1, 1, NULL, ACPI_TYPE_BUFFER);
++	if (!ret)
++		return -EINVAL;
++
++	ACPI_FREE(ret);
++	return 0;
++}
++
++static int sb1_dgpu_sw_hgon(void)
++{
++	struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
++	acpi_status status;
++
++	status = acpi_evaluate_object(NULL, DGPUSW_ACPI_PATH_HGON, NULL, &buf);
++	if (status) {
++		pr_err("failed to run HGON: %d\n", status);
++		return -EINVAL;
++	}
++
++	if (buf.pointer)
++		ACPI_FREE(buf.pointer);
++
++	pr_info("turned-on dGPU via HGON\n");
++	return 0;
++}
++
++static int sb1_dgpu_sw_hgof(void)
++{
++	struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
++	acpi_status status;
++
++	status = acpi_evaluate_object(NULL, DGPUSW_ACPI_PATH_HGOF, NULL, &buf);
++	if (status) {
++		pr_err("failed to run HGOF: %d\n", status);
++		return -EINVAL;
++	}
++
++	if (buf.pointer)
++		ACPI_FREE(buf.pointer);
++
++	pr_info("turned-off dGPU via HGOF\n");
++	return 0;
++}
++
++
++static ssize_t dgpu_dsmcall_store(struct device *dev, struct device_attribute *attr,
++				  const char *buf, size_t len)
++{
++	int status, value;
++
++	status = kstrtoint(buf, 0, &value);
++	if (status < 0)
++		return status;
++
++	if (value != 1)
++		return -EINVAL;
++
++	status = sb1_dgpu_sw_dsmcall();
++
++	return status < 0 ? status : len;
++}
++
++static ssize_t dgpu_power_store(struct device *dev, struct device_attribute *attr,
++				const char *buf, size_t len)
++{
++	bool power;
++	int status;
++
++	status = kstrtobool(buf, &power);
++	if (status < 0)
++		return status;
++
++	if (power)
++		status = sb1_dgpu_sw_hgon();
++	else
++		status = sb1_dgpu_sw_hgof();
++
++	return status < 0 ? status : len;
++}
++
++static DEVICE_ATTR_WO(dgpu_dsmcall);
++static DEVICE_ATTR_WO(dgpu_power);
++
++static struct attribute *sb1_dgpu_sw_attrs[] = {
++	&dev_attr_dgpu_dsmcall.attr,
++	&dev_attr_dgpu_power.attr,
++	NULL,
++};
++
++static const struct attribute_group sb1_dgpu_sw_attr_group = {
++	.attrs = sb1_dgpu_sw_attrs,
++};
++
++
++static int sb1_dgpu_sw_probe(struct platform_device *pdev)
++{
++	return sysfs_create_group(&pdev->dev.kobj, &sb1_dgpu_sw_attr_group);
++}
++
++static int sb1_dgpu_sw_remove(struct platform_device *pdev)
++{
++	sysfs_remove_group(&pdev->dev.kobj, &sb1_dgpu_sw_attr_group);
++	return 0;
++}
++
++/*
++ * The dGPU power seems to be actually handled by MSHW0040. However, that is
++ * also the power-/volume-button device with a mainline driver. So let's use
++ * MSHW0041 instead for now, which seems to be the LTCH (latch/DTX) device.
++ */
++static const struct acpi_device_id sb1_dgpu_sw_match[] = {
++	{ "MSHW0041", },
++	{ },
++};
++MODULE_DEVICE_TABLE(acpi, sb1_dgpu_sw_match);
++
++static struct platform_driver sb1_dgpu_sw = {
++	.probe = sb1_dgpu_sw_probe,
++	.remove = sb1_dgpu_sw_remove,
++	.driver = {
++		.name = "surfacebook1_dgpu_switch",
++		.acpi_match_table = sb1_dgpu_sw_match,
++		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
++	},
++};
++module_platform_driver(sb1_dgpu_sw);
++
++MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
++MODULE_DESCRIPTION("Discrete GPU Power-Switch for Surface Book 1");
++MODULE_LICENSE("GPL");
+-- 
+2.42.0
+

+ 149 - 0
patches/6.5/0008-surface-button.patch

@@ -0,0 +1,149 @@
+From c53835c377fe5311e26e6c4a43875c2fe05a9988 Mon Sep 17 00:00:00 2001
+From: Sachi King <nakato@nakato.io>
+Date: Tue, 5 Oct 2021 00:05:09 +1100
+Subject: [PATCH] Input: soc_button_array - support AMD variant Surface devices
+
+The power button on the AMD variant of the Surface Laptop uses the
+same MSHW0040 device ID as the 5th and later generation of Surface
+devices, however they report 0 for their OEM platform revision.  As the
+_DSM does not exist on the devices requiring special casing, check for
+the existance of the _DSM to determine if soc_button_array should be
+loaded.
+
+Fixes: c394159310d0 ("Input: soc_button_array - add support for newer surface devices")
+Co-developed-by: Maximilian Luz <luzmaximilian@gmail.com>
+
+Signed-off-by: Sachi King <nakato@nakato.io>
+Patchset: surface-button
+---
+ drivers/input/misc/soc_button_array.c | 33 +++++++--------------------
+ 1 file changed, 8 insertions(+), 25 deletions(-)
+
+diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c
+index e79f5497948b..2bddbe6e9ea4 100644
+--- a/drivers/input/misc/soc_button_array.c
++++ b/drivers/input/misc/soc_button_array.c
+@@ -537,8 +537,8 @@ static const struct soc_device_data soc_device_MSHW0028 = {
+  * Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned
+  * devices use MSHW0040 for power and volume buttons, however the way they
+  * have to be addressed differs. Make sure that we only load this drivers
+- * for the correct devices by checking the OEM Platform Revision provided by
+- * the _DSM method.
++ * for the correct devices by checking if the OEM Platform Revision DSM call
++ * exists.
+  */
+ #define MSHW0040_DSM_REVISION		0x01
+ #define MSHW0040_DSM_GET_OMPR		0x02	// get OEM Platform Revision
+@@ -549,31 +549,14 @@ static const guid_t MSHW0040_DSM_UUID =
+ static int soc_device_check_MSHW0040(struct device *dev)
+ {
+ 	acpi_handle handle = ACPI_HANDLE(dev);
+-	union acpi_object *result;
+-	u64 oem_platform_rev = 0;	// valid revisions are nonzero
+-
+-	// get OEM platform revision
+-	result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID,
+-					 MSHW0040_DSM_REVISION,
+-					 MSHW0040_DSM_GET_OMPR, NULL,
+-					 ACPI_TYPE_INTEGER);
+-
+-	if (result) {
+-		oem_platform_rev = result->integer.value;
+-		ACPI_FREE(result);
+-	}
+-
+-	/*
+-	 * If the revision is zero here, the _DSM evaluation has failed. This
+-	 * indicates that we have a Pro 4 or Book 1 and this driver should not
+-	 * be used.
+-	 */
+-	if (oem_platform_rev == 0)
+-		return -ENODEV;
++	bool exists;
+ 
+-	dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev);
++	// check if OEM platform revision DSM call exists
++	exists = acpi_check_dsm(handle, &MSHW0040_DSM_UUID,
++				MSHW0040_DSM_REVISION,
++				BIT(MSHW0040_DSM_GET_OMPR));
+ 
+-	return 0;
++	return exists ? 0 : -ENODEV;
+ }
+ 
+ /*
+-- 
+2.42.0
+
+From 62906484583876cc396a55712f3c9483879b72ac Mon Sep 17 00:00:00 2001
+From: Sachi King <nakato@nakato.io>
+Date: Tue, 5 Oct 2021 00:22:57 +1100
+Subject: [PATCH] platform/surface: surfacepro3_button: don't load on amd
+ variant
+
+The AMD variant of the Surface Laptop report 0 for their OEM platform
+revision.  The Surface devices that require the surfacepro3_button
+driver do not have the _DSM that gets the OEM platform revision.  If the
+method does not exist, load surfacepro3_button.
+
+Fixes: 64dd243d7356 ("platform/x86: surfacepro3_button: Fix device check")
+Co-developed-by: Maximilian Luz <luzmaximilian@gmail.com>
+
+Signed-off-by: Sachi King <nakato@nakato.io>
+Patchset: surface-button
+---
+ drivers/platform/surface/surfacepro3_button.c | 30 ++++---------------
+ 1 file changed, 6 insertions(+), 24 deletions(-)
+
+diff --git a/drivers/platform/surface/surfacepro3_button.c b/drivers/platform/surface/surfacepro3_button.c
+index 2755601f979c..4240c98ca226 100644
+--- a/drivers/platform/surface/surfacepro3_button.c
++++ b/drivers/platform/surface/surfacepro3_button.c
+@@ -149,7 +149,8 @@ static int surface_button_resume(struct device *dev)
+ /*
+  * Surface Pro 4 and Surface Book 2 / Surface Pro 2017 use the same device
+  * ID (MSHW0040) for the power/volume buttons. Make sure this is the right
+- * device by checking for the _DSM method and OEM Platform Revision.
++ * device by checking for the _DSM method and OEM Platform Revision DSM
++ * function.
+  *
+  * Returns true if the driver should bind to this device, i.e. the device is
+  * either MSWH0028 (Pro 3) or MSHW0040 on a Pro 4 or Book 1.
+@@ -157,30 +158,11 @@ static int surface_button_resume(struct device *dev)
+ static bool surface_button_check_MSHW0040(struct acpi_device *dev)
+ {
+ 	acpi_handle handle = dev->handle;
+-	union acpi_object *result;
+-	u64 oem_platform_rev = 0;	// valid revisions are nonzero
+-
+-	// get OEM platform revision
+-	result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID,
+-					 MSHW0040_DSM_REVISION,
+-					 MSHW0040_DSM_GET_OMPR,
+-					 NULL, ACPI_TYPE_INTEGER);
+-
+-	/*
+-	 * If evaluating the _DSM fails, the method is not present. This means
+-	 * that we have either MSHW0028 or MSHW0040 on Pro 4 or Book 1, so we
+-	 * should use this driver. We use revision 0 indicating it is
+-	 * unavailable.
+-	 */
+-
+-	if (result) {
+-		oem_platform_rev = result->integer.value;
+-		ACPI_FREE(result);
+-	}
+-
+-	dev_dbg(&dev->dev, "OEM Platform Revision %llu\n", oem_platform_rev);
+ 
+-	return oem_platform_rev == 0;
++	// make sure that OEM platform revision DSM call does not exist
++	return !acpi_check_dsm(handle, &MSHW0040_DSM_UUID,
++			       MSHW0040_DSM_REVISION,
++			       BIT(MSHW0040_DSM_GET_OMPR));
+ }
+ 
+ 
+-- 
+2.42.0
+

+ 574 - 0
patches/6.5/0009-surface-typecover.patch

@@ -0,0 +1,574 @@
+From ae85c1b98b374a567945912d45deaeb71f803bcf Mon Sep 17 00:00:00 2001
+From: Maximilian Luz <luzmaximilian@gmail.com>
+Date: Sat, 18 Feb 2023 01:02:49 +0100
+Subject: [PATCH] USB: quirks: Add USB_QUIRK_DELAY_INIT for Surface Go 3
+ Type-Cover
+
+The touchpad on the Type-Cover of the Surface Go 3 is sometimes not
+being initialized properly. Apply USB_QUIRK_DELAY_INIT to fix this
+issue.
+
+More specifically, the device in question is a fairly standard modern
+touchpad with pointer and touchpad input modes. During setup, the device
+needs to be switched from pointer- to touchpad-mode (which is done in
+hid-multitouch) to fully utilize it as intended. Unfortunately, however,
+this seems to occasionally fail silently, leaving the device in
+pointer-mode. Applying USB_QUIRK_DELAY_INIT seems to fix this.
+
+Link: https://github.com/linux-surface/linux-surface/issues/1059
+Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
+Patchset: surface-typecover
+---
+ drivers/usb/core/quirks.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
+index 15e9bd180a1d..0d70461d01e1 100644
+--- a/drivers/usb/core/quirks.c
++++ b/drivers/usb/core/quirks.c
+@@ -220,6 +220,9 @@ static const struct usb_device_id usb_quirk_list[] = {
+ 	/* Microsoft Surface Dock Ethernet (RTL8153 GigE) */
+ 	{ USB_DEVICE(0x045e, 0x07c6), .driver_info = USB_QUIRK_NO_LPM },
+ 
++	/* Microsoft Surface Go 3 Type-Cover */
++	{ USB_DEVICE(0x045e, 0x09b5), .driver_info = USB_QUIRK_DELAY_INIT },
++
+ 	/* Cherry Stream G230 2.0 (G85-231) and 3.0 (G85-232) */
+ 	{ USB_DEVICE(0x046a, 0x0023), .driver_info = USB_QUIRK_RESET_RESUME },
+ 
+-- 
+2.42.0
+
+From 2c8a51cd05ec2625ab8268283f6eed216a4e2e1b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
+Date: Thu, 5 Nov 2020 13:09:45 +0100
+Subject: [PATCH] hid/multitouch: Turn off Type Cover keyboard backlight when
+ suspending
+
+The Type Cover for Microsoft Surface devices supports a special usb
+control request to disable or enable the built-in keyboard backlight.
+On Windows, this request happens when putting the device into suspend or
+resuming it, without it the backlight of the Type Cover will remain
+enabled for some time even though the computer is suspended, which looks
+weird to the user.
+
+So add support for this special usb control request to hid-multitouch,
+which is the driver that's handling the Type Cover.
+
+The reason we have to use a pm_notifier for this instead of the usual
+suspend/resume methods is that those won't get called in case the usb
+device is already autosuspended.
+
+Also, if the device is autosuspended, we have to briefly autoresume it
+in order to send the request. Doing that should be fine, the usb-core
+driver does something similar during suspend inside choose_wakeup().
+
+To make sure we don't send that request to every device but only to
+devices which support it, add a new quirk
+MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER to hid-multitouch. For now this quirk
+is only enabled for the usb id of the Surface Pro 2017 Type Cover, which
+is where I confirmed that it's working.
+
+Patchset: surface-typecover
+---
+ drivers/hid/hid-multitouch.c | 100 ++++++++++++++++++++++++++++++++++-
+ 1 file changed, 98 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
+index 521b2ffb4244..c8f3d05c8866 100644
+--- a/drivers/hid/hid-multitouch.c
++++ b/drivers/hid/hid-multitouch.c
+@@ -34,7 +34,10 @@
+ #include <linux/device.h>
+ #include <linux/hid.h>
+ #include <linux/module.h>
++#include <linux/pm_runtime.h>
+ #include <linux/slab.h>
++#include <linux/suspend.h>
++#include <linux/usb.h>
+ #include <linux/input/mt.h>
+ #include <linux/jiffies.h>
+ #include <linux/string.h>
+@@ -47,6 +50,7 @@ MODULE_DESCRIPTION("HID multitouch panels");
+ MODULE_LICENSE("GPL");
+ 
+ #include "hid-ids.h"
++#include "usbhid/usbhid.h"
+ 
+ /* quirks to control the device */
+ #define MT_QUIRK_NOT_SEEN_MEANS_UP	BIT(0)
+@@ -72,12 +76,15 @@ MODULE_LICENSE("GPL");
+ #define MT_QUIRK_FORCE_MULTI_INPUT	BIT(20)
+ #define MT_QUIRK_DISABLE_WAKEUP		BIT(21)
+ #define MT_QUIRK_ORIENTATION_INVERT	BIT(22)
++#define MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT	BIT(23)
+ 
+ #define MT_INPUTMODE_TOUCHSCREEN	0x02
+ #define MT_INPUTMODE_TOUCHPAD		0x03
+ 
+ #define MT_BUTTONTYPE_CLICKPAD		0
+ 
++#define MS_TYPE_COVER_FEATURE_REPORT_USAGE	0xff050086
++
+ enum latency_mode {
+ 	HID_LATENCY_NORMAL = 0,
+ 	HID_LATENCY_HIGH = 1,
+@@ -169,6 +176,8 @@ struct mt_device {
+ 
+ 	struct list_head applications;
+ 	struct list_head reports;
++
++	struct notifier_block pm_notifier;
+ };
+ 
+ static void mt_post_parse_default_settings(struct mt_device *td,
+@@ -213,6 +222,7 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app);
+ #define MT_CLS_GOOGLE				0x0111
+ #define MT_CLS_RAZER_BLADE_STEALTH		0x0112
+ #define MT_CLS_SMART_TECH			0x0113
++#define MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER	0x0114
+ 
+ #define MT_DEFAULT_MAXCONTACT	10
+ #define MT_MAX_MAXCONTACT	250
+@@ -397,6 +407,16 @@ static const struct mt_class mt_classes[] = {
+ 			MT_QUIRK_CONTACT_CNT_ACCURATE |
+ 			MT_QUIRK_SEPARATE_APP_REPORT,
+ 	},
++	{ .name = MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER,
++		.quirks = MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT |
++			MT_QUIRK_ALWAYS_VALID |
++			MT_QUIRK_IGNORE_DUPLICATES |
++			MT_QUIRK_HOVERING |
++			MT_QUIRK_CONTACT_CNT_ACCURATE |
++			MT_QUIRK_STICKY_FINGERS |
++			MT_QUIRK_WIN8_PTP_BUTTONS,
++		.export_all_inputs = true
++	},
+ 	{ }
+ };
+ 
+@@ -1721,6 +1741,69 @@ static void mt_expired_timeout(struct timer_list *t)
+ 	clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
+ }
+ 
++static void get_type_cover_backlight_field(struct hid_device *hdev,
++					   struct hid_field **field)
++{
++	struct hid_report_enum *rep_enum;
++	struct hid_report *rep;
++	struct hid_field *cur_field;
++	int i, j;
++
++	rep_enum = &hdev->report_enum[HID_FEATURE_REPORT];
++	list_for_each_entry(rep, &rep_enum->report_list, list) {
++		for (i = 0; i < rep->maxfield; i++) {
++			cur_field = rep->field[i];
++
++			for (j = 0; j < cur_field->maxusage; j++) {
++				if (cur_field->usage[j].hid
++				    == MS_TYPE_COVER_FEATURE_REPORT_USAGE) {
++					*field = cur_field;
++					return;
++				}
++			}
++		}
++	}
++}
++
++static void update_keyboard_backlight(struct hid_device *hdev, bool enabled)
++{
++	struct usb_device *udev = hid_to_usb_dev(hdev);
++	struct hid_field *field = NULL;
++
++	/* Wake up the device in case it's already suspended */
++	pm_runtime_get_sync(&udev->dev);
++
++	get_type_cover_backlight_field(hdev, &field);
++	if (!field) {
++		hid_err(hdev, "couldn't find backlight field\n");
++		goto out;
++	}
++
++	field->value[field->index] = enabled ? 0x01ff00ff : 0x00ff00ff;
++	hid_hw_request(hdev, field->report, HID_REQ_SET_REPORT);
++
++out:
++	pm_runtime_put_sync(&udev->dev);
++}
++
++static int mt_pm_notifier(struct notifier_block *notifier,
++			  unsigned long pm_event,
++			  void *unused)
++{
++	struct mt_device *td =
++		container_of(notifier, struct mt_device, pm_notifier);
++	struct hid_device *hdev = td->hdev;
++
++	if (td->mtclass.quirks & MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT) {
++		if (pm_event == PM_SUSPEND_PREPARE)
++			update_keyboard_backlight(hdev, 0);
++		else if (pm_event == PM_POST_SUSPEND)
++			update_keyboard_backlight(hdev, 1);
++	}
++
++	return NOTIFY_DONE;
++}
++
+ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
+ {
+ 	int ret, i;
+@@ -1744,6 +1827,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
+ 	td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN;
+ 	hid_set_drvdata(hdev, td);
+ 
++	td->pm_notifier.notifier_call = mt_pm_notifier;
++	register_pm_notifier(&td->pm_notifier);
++
+ 	INIT_LIST_HEAD(&td->applications);
+ 	INIT_LIST_HEAD(&td->reports);
+ 
+@@ -1782,15 +1868,19 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
+ 	timer_setup(&td->release_timer, mt_expired_timeout, 0);
+ 
+ 	ret = hid_parse(hdev);
+-	if (ret != 0)
++	if (ret != 0) {
++		unregister_pm_notifier(&td->pm_notifier);
+ 		return ret;
++	}
+ 
+ 	if (mtclass->quirks & MT_QUIRK_FIX_CONST_CONTACT_ID)
+ 		mt_fix_const_fields(hdev, HID_DG_CONTACTID);
+ 
+ 	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+-	if (ret)
++	if (ret) {
++		unregister_pm_notifier(&td->pm_notifier);
+ 		return ret;
++	}
+ 
+ 	ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group);
+ 	if (ret)
+@@ -1842,6 +1932,7 @@ static void mt_remove(struct hid_device *hdev)
+ {
+ 	struct mt_device *td = hid_get_drvdata(hdev);
+ 
++	unregister_pm_notifier(&td->pm_notifier);
+ 	del_timer_sync(&td->release_timer);
+ 
+ 	sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
+@@ -2219,6 +2310,11 @@ static const struct hid_device_id mt_devices[] = {
+ 		MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
+ 			USB_DEVICE_ID_XIROKU_CSR2) },
+ 
++	/* Microsoft Surface type cover */
++	{ .driver_data = MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER,
++		HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY,
++			USB_VENDOR_ID_MICROSOFT, 0x09c0) },
++
+ 	/* Google MT devices */
+ 	{ .driver_data = MT_CLS_GOOGLE,
+ 		HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_GOOGLE,
+-- 
+2.42.0
+
+From 5e71abe238543f2e8931e6b0336882574c6adddb Mon Sep 17 00:00:00 2001
+From: PJungkamp <p.jungkamp@gmail.com>
+Date: Fri, 25 Feb 2022 12:04:25 +0100
+Subject: [PATCH] hid/multitouch: Add support for surface pro type cover tablet
+ switch
+
+The Surface Pro Type Cover has several non standard HID usages in it's
+hid report descriptor.
+I noticed that, upon folding the typecover back, a vendor specific range
+of 4 32 bit integer hid usages is transmitted.
+Only the first byte of the message seems to convey reliable information
+about the keyboard state.
+
+0x22 => Normal (keys enabled)
+0x33 => Folded back (keys disabled)
+0x53 => Rotated left/right side up (keys disabled)
+0x13 => Cover closed (keys disabled)
+0x43 => Folded back and Tablet upside down (keys disabled)
+This list may not be exhaustive.
+
+The tablet mode switch will be disabled for a value of 0x22 and enabled
+on any other value.
+
+Patchset: surface-typecover
+---
+ drivers/hid/hid-multitouch.c | 148 +++++++++++++++++++++++++++++------
+ 1 file changed, 122 insertions(+), 26 deletions(-)
+
+diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
+index c8f3d05c8866..1c6e4d66e762 100644
+--- a/drivers/hid/hid-multitouch.c
++++ b/drivers/hid/hid-multitouch.c
+@@ -77,6 +77,7 @@ MODULE_LICENSE("GPL");
+ #define MT_QUIRK_DISABLE_WAKEUP		BIT(21)
+ #define MT_QUIRK_ORIENTATION_INVERT	BIT(22)
+ #define MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT	BIT(23)
++#define MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH	BIT(24)
+ 
+ #define MT_INPUTMODE_TOUCHSCREEN	0x02
+ #define MT_INPUTMODE_TOUCHPAD		0x03
+@@ -84,6 +85,8 @@ MODULE_LICENSE("GPL");
+ #define MT_BUTTONTYPE_CLICKPAD		0
+ 
+ #define MS_TYPE_COVER_FEATURE_REPORT_USAGE	0xff050086
++#define MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE	0xff050072
++#define MS_TYPE_COVER_APPLICATION	0xff050050
+ 
+ enum latency_mode {
+ 	HID_LATENCY_NORMAL = 0,
+@@ -409,6 +412,7 @@ static const struct mt_class mt_classes[] = {
+ 	},
+ 	{ .name = MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER,
+ 		.quirks = MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT |
++			MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH |
+ 			MT_QUIRK_ALWAYS_VALID |
+ 			MT_QUIRK_IGNORE_DUPLICATES |
+ 			MT_QUIRK_HOVERING |
+@@ -1390,6 +1394,9 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ 	    field->application != HID_CP_CONSUMER_CONTROL &&
+ 	    field->application != HID_GD_WIRELESS_RADIO_CTLS &&
+ 	    field->application != HID_GD_SYSTEM_MULTIAXIS &&
++	    !(field->application == MS_TYPE_COVER_APPLICATION &&
++	      application->quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH &&
++	      usage->hid == MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE) &&
+ 	    !(field->application == HID_VD_ASUS_CUSTOM_MEDIA_KEYS &&
+ 	      application->quirks & MT_QUIRK_ASUS_CUSTOM_UP))
+ 		return -1;
+@@ -1417,6 +1424,21 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ 		return 1;
+ 	}
+ 
++	/*
++	 * The Microsoft Surface Pro Typecover has a non-standard HID
++	 * tablet mode switch on a vendor specific usage page with vendor
++	 * specific usage.
++	 */
++	if (field->application == MS_TYPE_COVER_APPLICATION &&
++	    application->quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH &&
++	    usage->hid == MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE) {
++		usage->type = EV_SW;
++		usage->code = SW_TABLET_MODE;
++		*max = SW_MAX;
++		*bit = hi->input->swbit;
++		return 1;
++	}
++
+ 	if (rdata->is_mt_collection)
+ 		return mt_touch_input_mapping(hdev, hi, field, usage, bit, max,
+ 					      application);
+@@ -1438,6 +1460,7 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+ {
+ 	struct mt_device *td = hid_get_drvdata(hdev);
+ 	struct mt_report_data *rdata;
++	struct input_dev *input;
+ 
+ 	rdata = mt_find_report_data(td, field->report);
+ 	if (rdata && rdata->is_mt_collection) {
+@@ -1445,6 +1468,19 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+ 		return -1;
+ 	}
+ 
++	/*
++	 * We own an input device which acts as a tablet mode switch for
++	 * the Surface Pro Typecover.
++	 */
++	if (field->application == MS_TYPE_COVER_APPLICATION &&
++	    rdata->application->quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH &&
++	    usage->hid == MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE) {
++		input = hi->input;
++		input_set_capability(input, EV_SW, SW_TABLET_MODE);
++		input_report_switch(input, SW_TABLET_MODE, 0);
++		return -1;
++	}
++
+ 	/* let hid-core decide for the others */
+ 	return 0;
+ }
+@@ -1454,11 +1490,21 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
+ {
+ 	struct mt_device *td = hid_get_drvdata(hid);
+ 	struct mt_report_data *rdata;
++	struct input_dev *input;
+ 
+ 	rdata = mt_find_report_data(td, field->report);
+ 	if (rdata && rdata->is_mt_collection)
+ 		return mt_touch_event(hid, field, usage, value);
+ 
++	if (field->application == MS_TYPE_COVER_APPLICATION &&
++	    rdata->application->quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH &&
++	    usage->hid == MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE) {
++		input = field->hidinput->input;
++		input_report_switch(input, SW_TABLET_MODE, (value & 0xFF) != 0x22);
++		input_sync(input);
++		return 1;
++	}
++
+ 	return 0;
+ }
+ 
+@@ -1611,6 +1657,42 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app)
+ 		app->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE;
+ }
+ 
++static int get_type_cover_field(struct hid_report_enum *rep_enum,
++				struct hid_field **field, int usage)
++{
++	struct hid_report *rep;
++	struct hid_field *cur_field;
++	int i, j;
++
++	list_for_each_entry(rep, &rep_enum->report_list, list) {
++		for (i = 0; i < rep->maxfield; i++) {
++			cur_field = rep->field[i];
++			if (cur_field->application != MS_TYPE_COVER_APPLICATION)
++				continue;
++			for (j = 0; j < cur_field->maxusage; j++) {
++				if (cur_field->usage[j].hid == usage) {
++					*field = cur_field;
++					return true;
++				}
++			}
++		}
++	}
++	return false;
++}
++
++static void request_type_cover_tablet_mode_switch(struct hid_device *hdev)
++{
++	struct hid_field *field;
++
++	if (get_type_cover_field(&hdev->report_enum[HID_INPUT_REPORT],
++				 &field,
++				 MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE)) {
++		hid_hw_request(hdev, field->report, HID_REQ_GET_REPORT);
++	} else {
++		hid_err(hdev, "couldn't find tablet mode field\n");
++	}
++}
++
+ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
+ {
+ 	struct mt_device *td = hid_get_drvdata(hdev);
+@@ -1659,6 +1741,13 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
+ 		/* force BTN_STYLUS to allow tablet matching in udev */
+ 		__set_bit(BTN_STYLUS, hi->input->keybit);
+ 		break;
++	case MS_TYPE_COVER_APPLICATION:
++		if (td->mtclass.quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH) {
++			suffix = "Tablet Mode Switch";
++			request_type_cover_tablet_mode_switch(hdev);
++			break;
++		}
++		fallthrough;
+ 	default:
+ 		suffix = "UNKNOWN";
+ 		break;
+@@ -1741,30 +1830,6 @@ static void mt_expired_timeout(struct timer_list *t)
+ 	clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
+ }
+ 
+-static void get_type_cover_backlight_field(struct hid_device *hdev,
+-					   struct hid_field **field)
+-{
+-	struct hid_report_enum *rep_enum;
+-	struct hid_report *rep;
+-	struct hid_field *cur_field;
+-	int i, j;
+-
+-	rep_enum = &hdev->report_enum[HID_FEATURE_REPORT];
+-	list_for_each_entry(rep, &rep_enum->report_list, list) {
+-		for (i = 0; i < rep->maxfield; i++) {
+-			cur_field = rep->field[i];
+-
+-			for (j = 0; j < cur_field->maxusage; j++) {
+-				if (cur_field->usage[j].hid
+-				    == MS_TYPE_COVER_FEATURE_REPORT_USAGE) {
+-					*field = cur_field;
+-					return;
+-				}
+-			}
+-		}
+-	}
+-}
+-
+ static void update_keyboard_backlight(struct hid_device *hdev, bool enabled)
+ {
+ 	struct usb_device *udev = hid_to_usb_dev(hdev);
+@@ -1773,8 +1838,9 @@ static void update_keyboard_backlight(struct hid_device *hdev, bool enabled)
+ 	/* Wake up the device in case it's already suspended */
+ 	pm_runtime_get_sync(&udev->dev);
+ 
+-	get_type_cover_backlight_field(hdev, &field);
+-	if (!field) {
++	if (!get_type_cover_field(&hdev->report_enum[HID_FEATURE_REPORT],
++				  &field,
++				  MS_TYPE_COVER_FEATURE_REPORT_USAGE)) {
+ 		hid_err(hdev, "couldn't find backlight field\n");
+ 		goto out;
+ 	}
+@@ -1909,13 +1975,24 @@ static int mt_suspend(struct hid_device *hdev, pm_message_t state)
+ 
+ static int mt_reset_resume(struct hid_device *hdev)
+ {
++	struct mt_device *td = hid_get_drvdata(hdev);
++
+ 	mt_release_contacts(hdev);
+ 	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
++
++	/* Request an update on the typecover folding state on resume
++	 * after reset.
++	 */
++	if (td->mtclass.quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH)
++		request_type_cover_tablet_mode_switch(hdev);
++
+ 	return 0;
+ }
+ 
+ static int mt_resume(struct hid_device *hdev)
+ {
++	struct mt_device *td = hid_get_drvdata(hdev);
++
+ 	/* Some Elan legacy devices require SET_IDLE to be set on resume.
+ 	 * It should be safe to send it to other devices too.
+ 	 * Tested on 3M, Stantum, Cypress, Zytronic, eGalax, and Elan panels. */
+@@ -1924,6 +2001,10 @@ static int mt_resume(struct hid_device *hdev)
+ 
+ 	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
+ 
++	/* Request an update on the typecover folding state on resume. */
++	if (td->mtclass.quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH)
++		request_type_cover_tablet_mode_switch(hdev);
++
+ 	return 0;
+ }
+ #endif
+@@ -1931,6 +2012,21 @@ static int mt_resume(struct hid_device *hdev)
+ static void mt_remove(struct hid_device *hdev)
+ {
+ 	struct mt_device *td = hid_get_drvdata(hdev);
++	struct hid_field *field;
++	struct input_dev *input;
++
++	/* Reset tablet mode switch on disconnect. */
++	if (td->mtclass.quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH) {
++		if (get_type_cover_field(&hdev->report_enum[HID_INPUT_REPORT],
++					 &field,
++					 MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE)) {
++			input = field->hidinput->input;
++			input_report_switch(input, SW_TABLET_MODE, 0);
++			input_sync(input);
++		} else {
++			hid_err(hdev, "couldn't find tablet mode field\n");
++		}
++	}
+ 
+ 	unregister_pm_notifier(&td->pm_notifier);
+ 	del_timer_sync(&td->release_timer);
+-- 
+2.42.0
+

+ 97 - 0
patches/6.5/0010-surface-shutdown.patch

@@ -0,0 +1,97 @@
+From 7a9ec453afe5ecc05de3f3daec2f3755fd02c2f3 Mon Sep 17 00:00:00 2001
+From: Maximilian Luz <luzmaximilian@gmail.com>
+Date: Sun, 19 Feb 2023 22:12:24 +0100
+Subject: [PATCH] PCI: Add quirk to prevent calling shutdown mehtod
+
+Work around buggy EFI firmware: On some Microsoft Surface devices
+(Surface Pro 9 and Surface Laptop 5) the EFI ResetSystem call with
+EFI_RESET_SHUTDOWN doesn't function properly. Instead of shutting the
+system down, it returns and the system stays on.
+
+It turns out that this only happens after PCI shutdown callbacks ran for
+specific devices. Excluding those devices from the shutdown process
+makes the ResetSystem call work as expected.
+
+TODO: Maybe we can find a better way or the root cause of this?
+
+Not-Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
+Patchset: surface-shutdown
+---
+ drivers/pci/pci-driver.c |  3 +++
+ drivers/pci/quirks.c     | 36 ++++++++++++++++++++++++++++++++++++
+ include/linux/pci.h      |  1 +
+ 3 files changed, 40 insertions(+)
+
+diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
+index ae9baf801681..fdfaec2312a0 100644
+--- a/drivers/pci/pci-driver.c
++++ b/drivers/pci/pci-driver.c
+@@ -507,6 +507,9 @@ static void pci_device_shutdown(struct device *dev)
+ 	struct pci_dev *pci_dev = to_pci_dev(dev);
+ 	struct pci_driver *drv = pci_dev->driver;
+ 
++	if (pci_dev->no_shutdown)
++		return;
++
+ 	pm_runtime_resume(dev);
+ 
+ 	if (drv && drv->shutdown)
+diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
+index 321156ca273d..37e06e778ce2 100644
+--- a/drivers/pci/quirks.c
++++ b/drivers/pci/quirks.c
+@@ -6138,3 +6138,39 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9a2d, dpc_log_size);
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9a2f, dpc_log_size);
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9a31, dpc_log_size);
+ #endif
++
++static const struct dmi_system_id no_shutdown_dmi_table[] = {
++	/*
++	 * Systems on which some devices should not be touched during shutdown.
++	 */
++	{
++		.ident = "Microsoft Surface Pro 9",
++		.matches = {
++			DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
++			DMI_MATCH(DMI_PRODUCT_NAME, "Surface Pro 9"),
++		},
++	},
++	{
++		.ident = "Microsoft Surface Laptop 5",
++		.matches = {
++			DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
++			DMI_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 5"),
++		},
++	},
++	{}
++};
++
++static void quirk_no_shutdown(struct pci_dev *dev)
++{
++	if (!dmi_check_system(no_shutdown_dmi_table))
++		return;
++
++	dev->no_shutdown = 1;
++	pci_info(dev, "disabling shutdown ops for [%04x:%04x]\n",
++		 dev->vendor, dev->device);
++}
++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x461e, quirk_no_shutdown);  // Thunderbolt 4 USB Controller
++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x461f, quirk_no_shutdown);  // Thunderbolt 4 PCI Express Root Port
++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x462f, quirk_no_shutdown);  // Thunderbolt 4 PCI Express Root Port
++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x466d, quirk_no_shutdown);  // Thunderbolt 4 NHI
++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x46a8, quirk_no_shutdown);  // GPU
+diff --git a/include/linux/pci.h b/include/linux/pci.h
+index 7ee498cd1f37..740049a82343 100644
+--- a/include/linux/pci.h
++++ b/include/linux/pci.h
+@@ -464,6 +464,7 @@ struct pci_dev {
+ 	unsigned int	no_vf_scan:1;		/* Don't scan for VFs after IOV enablement */
+ 	unsigned int	no_command_memory:1;	/* No PCI_COMMAND_MEMORY */
+ 	unsigned int	rom_bar_overlap:1;	/* ROM BAR disable broken */
++	unsigned int	no_shutdown:1;		/* Do not touch device on shutdown */
+ 	pci_dev_flags_t dev_flags;
+ 	atomic_t	enable_cnt;	/* pci_enable_device has been called */
+ 
+-- 
+2.42.0
+

+ 51 - 0
patches/6.5/0011-surface-gpe.patch

@@ -0,0 +1,51 @@
+From a6d5bb2a087deac6ffd82f176b5a037f2f87994f Mon Sep 17 00:00:00 2001
+From: Maximilian Luz <luzmaximilian@gmail.com>
+Date: Sun, 12 Mar 2023 01:41:57 +0100
+Subject: [PATCH] platform/surface: gpe: Add support for Surface Pro 9
+
+Add the lid GPE used by the Surface Pro 9.
+
+Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
+Patchset: surface-gpe
+---
+ drivers/platform/surface/surface_gpe.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+diff --git a/drivers/platform/surface/surface_gpe.c b/drivers/platform/surface/surface_gpe.c
+index c219b840d491..69c4352e8406 100644
+--- a/drivers/platform/surface/surface_gpe.c
++++ b/drivers/platform/surface/surface_gpe.c
+@@ -41,6 +41,11 @@ static const struct property_entry lid_device_props_l4F[] = {
+ 	{},
+ };
+ 
++static const struct property_entry lid_device_props_l52[] = {
++	PROPERTY_ENTRY_U32("gpe", 0x52),
++	{},
++};
++
+ static const struct property_entry lid_device_props_l57[] = {
+ 	PROPERTY_ENTRY_U32("gpe", 0x57),
+ 	{},
+@@ -107,6 +112,18 @@ static const struct dmi_system_id dmi_lid_device_table[] = {
+ 		},
+ 		.driver_data = (void *)lid_device_props_l4B,
+ 	},
++	{
++		/*
++		 * We match for SKU here due to product name clash with the ARM
++		 * version.
++		 */
++		.ident = "Surface Pro 9",
++		.matches = {
++			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
++			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_9_2038"),
++		},
++		.driver_data = (void *)lid_device_props_l52,
++	},
+ 	{
+ 		.ident = "Surface Book 1",
+ 		.matches = {
+-- 
+2.42.0
+

+ 1394 - 0
patches/6.5/0012-cameras.patch

@@ -0,0 +1,1394 @@
+From 507234aac475d6639b99ab195f38d8be10c476c4 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Sun, 10 Oct 2021 20:56:57 +0200
+Subject: [PATCH] ACPI: delay enumeration of devices with a _DEP pointing to an
+ INT3472 device
+
+The clk and regulator frameworks expect clk/regulator consumer-devices
+to have info about the consumed clks/regulators described in the device's
+fw_node.
+
+To work around cases where this info is not present in the firmware tables,
+which is often the case on x86/ACPI devices, both frameworks allow the
+provider-driver to attach info about consumers to the clks/regulators
+when registering these.
+
+This causes problems with the probe ordering wrt drivers for consumers
+of these clks/regulators. Since the lookups are only registered when the
+provider-driver binds, trying to get these clks/regulators before then
+results in a -ENOENT error for clks and a dummy regulator for regulators.
+
+One case where we hit this issue is camera sensors such as e.g. the OV8865
+sensor found on the Microsoft Surface Go. The sensor uses clks, regulators
+and GPIOs provided by a TPS68470 PMIC which is described in an INT3472
+ACPI device. There is special platform code handling this and setting
+platform_data with the necessary consumer info on the MFD cells
+instantiated for the PMIC under: drivers/platform/x86/intel/int3472.
+
+For this to work properly the ov8865 driver must not bind to the I2C-client
+for the OV8865 sensor until after the TPS68470 PMIC gpio, regulator and
+clk MFD cells have all been fully setup.
+
+The OV8865 on the Microsoft Surface Go is just one example, all X86
+devices using the Intel IPU3 camera block found on recent Intel SoCs
+have similar issues where there is an INT3472 HID ACPI-device, which
+describes the clks and regulators, and the driver for this INT3472 device
+must be fully initialized before the sensor driver (any sensor driver)
+binds for things to work properly.
+
+On these devices the ACPI nodes describing the sensors all have a _DEP
+dependency on the matching INT3472 ACPI device (there is one per sensor).
+
+This allows solving the probe-ordering problem by delaying the enumeration
+(instantiation of the I2C-client in the ov8865 example) of ACPI-devices
+which have a _DEP dependency on an INT3472 device.
+
+The new acpi_dev_ready_for_enumeration() helper used for this is also
+exported because for devices, which have the enumeration_by_parent flag
+set, the parent-driver will do its own scan of child ACPI devices and
+it will try to enumerate those during its probe(). Code doing this such
+as e.g. the i2c-core-acpi.c code must call this new helper to ensure
+that it too delays the enumeration until all the _DEP dependencies are
+met on devices which have the new honor_deps flag set.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Patchset: cameras
+---
+ drivers/acpi/scan.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
+index 87e385542576..1183d09c13a6 100644
+--- a/drivers/acpi/scan.c
++++ b/drivers/acpi/scan.c
+@@ -2105,6 +2105,9 @@ static acpi_status acpi_bus_check_add_2(acpi_handle handle, u32 lvl_not_used,
+ 
+ static void acpi_default_enumeration(struct acpi_device *device)
+ {
++	if (!acpi_dev_ready_for_enumeration(device))
++		return;
++
+ 	/*
+ 	 * Do not enumerate devices with enumeration_by_parent flag set as
+ 	 * they will be enumerated by their respective parents.
+-- 
+2.42.0
+
+From aa099b5087160e2159c178d167025537a23e1908 Mon Sep 17 00:00:00 2001
+From: zouxiaoh <xiaohong.zou@intel.com>
+Date: Fri, 25 Jun 2021 08:52:59 +0800
+Subject: [PATCH] iommu: intel-ipu: use IOMMU passthrough mode for Intel IPUs
+
+Intel IPU(Image Processing Unit) has its own (IO)MMU hardware,
+The IPU driver allocates its own page table that is not mapped
+via the DMA, and thus the Intel IOMMU driver blocks access giving
+this error: DMAR: DRHD: handling fault status reg 3 DMAR:
+[DMA Read] Request device [00:05.0] PASID ffffffff
+fault addr 76406000 [fault reason 06] PTE Read access is not set
+As IPU is not an external facing device which is not risky, so use
+IOMMU passthrough mode for Intel IPUs.
+
+Change-Id: I6dcccdadac308cf42e20a18e1b593381391e3e6b
+Depends-On: Iacd67578e8c6a9b9ac73285f52b4081b72fb68a6
+Tracked-On: #JIITL8-411
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+Signed-off-by: zouxiaoh <xiaohong.zou@intel.com>
+Signed-off-by: Xu Chongyang <chongyang.xu@intel.com>
+Patchset: cameras
+---
+ drivers/iommu/intel/iommu.c | 30 ++++++++++++++++++++++++++++++
+ 1 file changed, 30 insertions(+)
+
+diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
+index fc4799415c3c..e10b4f62594d 100644
+--- a/drivers/iommu/intel/iommu.c
++++ b/drivers/iommu/intel/iommu.c
+@@ -37,6 +37,12 @@
+ #define IS_GFX_DEVICE(pdev) ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY)
+ #define IS_USB_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_SERIAL_USB)
+ #define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA)
++#define IS_INTEL_IPU(pdev) ((pdev)->vendor == PCI_VENDOR_ID_INTEL &&	\
++			   ((pdev)->device == 0x9a19 ||		\
++			    (pdev)->device == 0x9a39 ||		\
++			    (pdev)->device == 0x4e19 ||		\
++			    (pdev)->device == 0x465d ||		\
++			    (pdev)->device == 0x1919))
+ #define IS_IPTS(pdev) ((pdev)->vendor == PCI_VENDOR_ID_INTEL &&	\
+ 			    ((pdev)->device == 0x9d3e))
+ #define IS_AZALIA(pdev) ((pdev)->vendor == 0x8086 && (pdev)->device == 0x3a3e)
+@@ -290,12 +296,14 @@ EXPORT_SYMBOL_GPL(intel_iommu_enabled);
+ 
+ static int dmar_map_gfx = 1;
+ static int dmar_map_ipts = 1;
++static int dmar_map_ipu = 1;
+ static int intel_iommu_superpage = 1;
+ static int iommu_identity_mapping;
+ static int iommu_skip_te_disable;
+ 
+ #define IDENTMAP_GFX		2
+ #define IDENTMAP_AZALIA		4
++#define IDENTMAP_IPU		8
+ #define IDENTMAP_IPTS		16
+ 
+ const struct iommu_ops intel_iommu_ops;
+@@ -2553,6 +2561,9 @@ static int device_def_domain_type(struct device *dev)
+ 		if ((iommu_identity_mapping & IDENTMAP_GFX) && IS_GFX_DEVICE(pdev))
+ 			return IOMMU_DOMAIN_IDENTITY;
+ 
++		if ((iommu_identity_mapping & IDENTMAP_IPU) && IS_INTEL_IPU(pdev))
++			return IOMMU_DOMAIN_IDENTITY;
++
+ 		if ((iommu_identity_mapping & IDENTMAP_IPTS) && IS_IPTS(pdev))
+ 			return IOMMU_DOMAIN_IDENTITY;
+ 	}
+@@ -2862,6 +2873,9 @@ static int __init init_dmars(void)
+ 	if (!dmar_map_gfx)
+ 		iommu_identity_mapping |= IDENTMAP_GFX;
+ 
++	if (!dmar_map_ipu)
++		iommu_identity_mapping |= IDENTMAP_IPU;
++
+ 	if (!dmar_map_ipts)
+ 		iommu_identity_mapping |= IDENTMAP_IPTS;
+ 
+@@ -4781,6 +4795,18 @@ static void quirk_iommu_igfx(struct pci_dev *dev)
+ 	dmar_map_gfx = 0;
+ }
+ 
++static void quirk_iommu_ipu(struct pci_dev *dev)
++{
++	if (!IS_INTEL_IPU(dev))
++		return;
++
++	if (risky_device(dev))
++		return;
++
++	pci_info(dev, "Passthrough IOMMU for integrated Intel IPU\n");
++	dmar_map_ipu = 0;
++}
++
+ static void quirk_iommu_ipts(struct pci_dev *dev)
+ {
+ 	if (!IS_IPTS(dev))
+@@ -4792,6 +4818,7 @@ static void quirk_iommu_ipts(struct pci_dev *dev)
+ 	pci_info(dev, "Passthrough IOMMU for IPTS\n");
+ 	dmar_map_ipts = 0;
+ }
++
+ /* G4x/GM45 integrated gfx dmar support is totally busted. */
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_igfx);
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e00, quirk_iommu_igfx);
+@@ -4827,6 +4854,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx);
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163A, quirk_iommu_igfx);
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163D, quirk_iommu_igfx);
+ 
++/* disable IPU dmar support */
++DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, quirk_iommu_ipu);
++
+ /* disable IPTS dmar support */
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9D3E, quirk_iommu_ipts);
+ 
+-- 
+2.42.0
+
+From ffa573f5cb76fa904d6f8cd66ad7dab31eccd1a3 Mon Sep 17 00:00:00 2001
+From: Daniel Scally <djrscally@gmail.com>
+Date: Sun, 10 Oct 2021 20:57:02 +0200
+Subject: [PATCH] platform/x86: int3472: Enable I2c daisy chain
+
+The TPS68470 PMIC has an I2C passthrough mode through which I2C traffic
+can be forwarded to a device connected to the PMIC as though it were
+connected directly to the system bus. Enable this mode when the chip
+is initialised.
+
+Signed-off-by: Daniel Scally <djrscally@gmail.com>
+Patchset: cameras
+---
+ drivers/platform/x86/intel/int3472/tps68470.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/drivers/platform/x86/intel/int3472/tps68470.c b/drivers/platform/x86/intel/int3472/tps68470.c
+index 1e107fd49f82..e3e1696e7f0e 100644
+--- a/drivers/platform/x86/intel/int3472/tps68470.c
++++ b/drivers/platform/x86/intel/int3472/tps68470.c
+@@ -46,6 +46,13 @@ static int tps68470_chip_init(struct device *dev, struct regmap *regmap)
+ 		return ret;
+ 	}
+ 
++	/* Enable I2C daisy chain */
++	ret = regmap_write(regmap, TPS68470_REG_S_I2C_CTL, 0x03);
++	if (ret) {
++		dev_err(dev, "Failed to enable i2c daisy chain\n");
++		return ret;
++	}
++
+ 	dev_info(dev, "TPS68470 REVID: 0x%02x\n", version);
+ 
+ 	return 0;
+-- 
+2.42.0
+
+From 213416982cbd90a719907c0066ea54b54a56577b Mon Sep 17 00:00:00 2001
+From: Daniel Scally <djrscally@gmail.com>
+Date: Wed, 4 May 2022 23:21:45 +0100
+Subject: [PATCH] media: ipu3-cio2: Move functionality from .complete() to
+ .bound()
+
+Creating links and registering subdev nodes during the .complete()
+callback has the unfortunate effect of preventing all cameras that
+connect to a notifier from working if any one of their drivers fails
+to probe. Moving the functionality from .complete() to .bound() allows
+those camera sensor drivers that did probe correctly to work regardless.
+
+Signed-off-by: Daniel Scally <djrscally@gmail.com>
+Patchset: cameras
+---
+ drivers/media/pci/intel/ipu3/ipu3-cio2-main.c | 59 +++++++------------
+ 1 file changed, 21 insertions(+), 38 deletions(-)
+
+diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
+index ca51776a961f..c027b2bfd851 100644
+--- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
++++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
+@@ -1386,7 +1386,10 @@ static int cio2_notifier_bound(struct v4l2_async_notifier *notifier,
+ {
+ 	struct cio2_device *cio2 = to_cio2_device(notifier);
+ 	struct sensor_async_subdev *s_asd = to_sensor_asd(asd);
++	struct device *dev = &cio2->pci_dev->dev;
+ 	struct cio2_queue *q;
++	unsigned int pad;
++	int ret;
+ 
+ 	if (cio2->queue[s_asd->csi2.port].sensor)
+ 		return -EBUSY;
+@@ -1397,7 +1400,24 @@ static int cio2_notifier_bound(struct v4l2_async_notifier *notifier,
+ 	q->sensor = sd;
+ 	q->csi_rx_base = cio2->base + CIO2_REG_PIPE_BASE(q->csi2.port);
+ 
+-	return 0;
++	ret = media_entity_get_fwnode_pad(&q->sensor->entity,
++					  s_asd->asd.match.fwnode,
++					  MEDIA_PAD_FL_SOURCE);
++	if (ret < 0) {
++		dev_err(dev, "no pad for endpoint %pfw (%d)\n",
++			s_asd->asd.match.fwnode, ret);
++		return ret;
++	}
++
++	ret = media_create_pad_link(&q->sensor->entity, ret, &q->subdev.entity,
++				    CIO2_PAD_SINK, 0);
++	if (ret) {
++		dev_err(dev, "failed to create link for %s\n",
++			q->sensor->name);
++		return ret;
++	}
++
++	return v4l2_device_register_subdev_nodes(&cio2->v4l2_dev);
+ }
+ 
+ /* The .unbind callback */
+@@ -1411,46 +1431,9 @@ static void cio2_notifier_unbind(struct v4l2_async_notifier *notifier,
+ 	cio2->queue[s_asd->csi2.port].sensor = NULL;
+ }
+ 
+-/* .complete() is called after all subdevices have been located */
+-static int cio2_notifier_complete(struct v4l2_async_notifier *notifier)
+-{
+-	struct cio2_device *cio2 = to_cio2_device(notifier);
+-	struct device *dev = &cio2->pci_dev->dev;
+-	struct sensor_async_subdev *s_asd;
+-	struct v4l2_async_subdev *asd;
+-	struct cio2_queue *q;
+-	int ret;
+-
+-	list_for_each_entry(asd, &cio2->notifier.asd_list, asd_list) {
+-		s_asd = to_sensor_asd(asd);
+-		q = &cio2->queue[s_asd->csi2.port];
+-
+-		ret = media_entity_get_fwnode_pad(&q->sensor->entity,
+-						  s_asd->asd.match.fwnode,
+-						  MEDIA_PAD_FL_SOURCE);
+-		if (ret < 0) {
+-			dev_err(dev, "no pad for endpoint %pfw (%d)\n",
+-				s_asd->asd.match.fwnode, ret);
+-			return ret;
+-		}
+-
+-		ret = media_create_pad_link(&q->sensor->entity, ret,
+-					    &q->subdev.entity, CIO2_PAD_SINK,
+-					    0);
+-		if (ret) {
+-			dev_err(dev, "failed to create link for %s (endpoint %pfw, error %d)\n",
+-				q->sensor->name, s_asd->asd.match.fwnode, ret);
+-			return ret;
+-		}
+-	}
+-
+-	return v4l2_device_register_subdev_nodes(&cio2->v4l2_dev);
+-}
+-
+ static const struct v4l2_async_notifier_operations cio2_async_ops = {
+ 	.bound = cio2_notifier_bound,
+ 	.unbind = cio2_notifier_unbind,
+-	.complete = cio2_notifier_complete,
+ };
+ 
+ static int cio2_parse_firmware(struct cio2_device *cio2)
+-- 
+2.42.0
+
+From 770a549c8fbf595e60adc5129aabf1f9a27627cc Mon Sep 17 00:00:00 2001
+From: Daniel Scally <djrscally@gmail.com>
+Date: Thu, 2 Jun 2022 22:15:56 +0100
+Subject: [PATCH] media: ipu3-cio2: Re-add .complete() to ipu3-cio2
+
+Removing the .complete() callback had some unintended consequences.
+Because the VCM driver is not directly linked to the ipu3-cio2
+driver .bound() never gets called for it, which means its devnode
+is never created if it probes late. Because .complete() waits for
+any sub-notifiers to also be complete it is captured in that call.
+
+Signed-off-by: Daniel Scally <djrscally@gmail.com>
+Patchset: cameras
+---
+ drivers/media/pci/intel/ipu3/ipu3-cio2-main.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
+index c027b2bfd851..031acee26553 100644
+--- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
++++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
+@@ -1431,9 +1431,18 @@ static void cio2_notifier_unbind(struct v4l2_async_notifier *notifier,
+ 	cio2->queue[s_asd->csi2.port].sensor = NULL;
+ }
+ 
++/* .complete() is called after all subdevices have been located */
++static int cio2_notifier_complete(struct v4l2_async_notifier *notifier)
++{
++	struct cio2_device *cio2 = to_cio2_device(notifier);
++
++	return v4l2_device_register_subdev_nodes(&cio2->v4l2_dev);
++}
++
+ static const struct v4l2_async_notifier_operations cio2_async_ops = {
+ 	.bound = cio2_notifier_bound,
+ 	.unbind = cio2_notifier_unbind,
++	.complete = cio2_notifier_complete,
+ };
+ 
+ static int cio2_parse_firmware(struct cio2_device *cio2)
+-- 
+2.42.0
+
+From 9e08cb6916dcf0afd1cf7e761c79b1637badc968 Mon Sep 17 00:00:00 2001
+From: Daniel Scally <djrscally@gmail.com>
+Date: Thu, 28 Oct 2021 21:55:16 +0100
+Subject: [PATCH] media: i2c: Add driver for DW9719 VCM
+
+Add a driver for the DW9719 VCM. The driver creates a v4l2 subdevice
+and registers a control to set the desired focus.
+
+Signed-off-by: Daniel Scally <djrscally@gmail.com>
+Patchset: cameras
+---
+ MAINTAINERS                |   7 +
+ drivers/media/i2c/Kconfig  |  11 +
+ drivers/media/i2c/Makefile |   1 +
+ drivers/media/i2c/dw9719.c | 425 +++++++++++++++++++++++++++++++++++++
+ 4 files changed, 444 insertions(+)
+ create mode 100644 drivers/media/i2c/dw9719.c
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 4cc6bf79fdd8..439cf523b80e 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -6251,6 +6251,13 @@ T:	git git://linuxtv.org/media_tree.git
+ F:	Documentation/devicetree/bindings/media/i2c/dongwoon,dw9714.yaml
+ F:	drivers/media/i2c/dw9714.c
+ 
++DONGWOON DW9719 LENS VOICE COIL DRIVER
++M:	Daniel Scally <djrscally@gmail.com>
++L:	linux-media@vger.kernel.org
++S:	Maintained
++T:	git git://linuxtv.org/media_tree.git
++F:	drivers/media/i2c/dw9719.c
++
+ DONGWOON DW9768 LENS VOICE COIL DRIVER
+ M:	Dongchun Zhu <dongchun.zhu@mediatek.com>
+ L:	linux-media@vger.kernel.org
+diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
+index 0669aea3eba3..6959ee1a89fb 100644
+--- a/drivers/media/i2c/Kconfig
++++ b/drivers/media/i2c/Kconfig
+@@ -855,6 +855,17 @@ config VIDEO_DW9714
+ 	  capability. This is designed for linear control of
+ 	  voice coil motors, controlled via I2C serial interface.
+ 
++config VIDEO_DW9719
++	tristate "DW9719 lens voice coil support"
++	depends on I2C && VIDEO_V4L2
++	select MEDIA_CONTROLLER
++	select VIDEO_V4L2_SUBDEV_API
++	select V4L2_ASYNC
++	help
++	  This is a driver for the DW9719 camera lens voice coil.
++	  This is designed for linear control of  voice coil motors,
++	  controlled via I2C serial interface.
++
+ config VIDEO_DW9768
+ 	tristate "DW9768 lens voice coil support"
+ 	depends on I2C && VIDEO_DEV
+diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
+index c743aeb5d1ad..db1ebf5cadfe 100644
+--- a/drivers/media/i2c/Makefile
++++ b/drivers/media/i2c/Makefile
+@@ -29,6 +29,7 @@ obj-$(CONFIG_VIDEO_CS5345) += cs5345.o
+ obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o
+ obj-$(CONFIG_VIDEO_CX25840) += cx25840/
+ obj-$(CONFIG_VIDEO_DW9714) += dw9714.o
++obj-$(CONFIG_VIDEO_DW9719) += dw9719.o
+ obj-$(CONFIG_VIDEO_DW9768) += dw9768.o
+ obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o
+ obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/
+diff --git a/drivers/media/i2c/dw9719.c b/drivers/media/i2c/dw9719.c
+new file mode 100644
+index 000000000000..180b04d2a6b3
+--- /dev/null
++++ b/drivers/media/i2c/dw9719.c
+@@ -0,0 +1,425 @@
++// SPDX-License-Identifier: GPL-2.0
++// Copyright (c) 2012 Intel Corporation
++
++/*
++ * Based on linux/modules/camera/drivers/media/i2c/imx/dw9719.c in this repo:
++ * https://github.com/ZenfoneArea/android_kernel_asus_zenfone5
++ */
++
++#include <asm/unaligned.h>
++
++#include <linux/delay.h>
++#include <linux/i2c.h>
++#include <linux/pm_runtime.h>
++#include <linux/regulator/consumer.h>
++#include <linux/types.h>
++
++#include <media/v4l2-common.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-subdev.h>
++
++#define DW9719_MAX_FOCUS_POS	1023
++#define DW9719_CTRL_STEPS	16
++#define DW9719_CTRL_DELAY_US	1000
++#define DELAY_MAX_PER_STEP_NS	(1000000 * 1023)
++
++#define DW9719_INFO			0
++#define DW9719_ID			0xF1
++#define DW9719_CONTROL			2
++#define DW9719_VCM_CURRENT		3
++
++#define DW9719_MODE			6
++#define DW9719_VCM_FREQ			7
++
++#define DW9719_MODE_SAC3		0x40
++#define DW9719_DEFAULT_VCM_FREQ		0x60
++#define DW9719_ENABLE_RINGING		0x02
++
++#define NUM_REGULATORS			2
++
++#define to_dw9719_device(x) container_of(x, struct dw9719_device, sd)
++
++struct dw9719_device {
++	struct device *dev;
++	struct i2c_client *client;
++	struct regulator_bulk_data regulators[NUM_REGULATORS];
++	struct v4l2_subdev sd;
++
++	struct dw9719_v4l2_ctrls {
++		struct v4l2_ctrl_handler handler;
++		struct v4l2_ctrl *focus;
++	} ctrls;
++};
++
++static int dw9719_i2c_rd8(struct i2c_client *client, u8 reg, u8 *val)
++{
++	struct i2c_msg msg[2];
++	u8 buf[2] = { reg };
++	int ret;
++
++	msg[0].addr = client->addr;
++	msg[0].flags = 0;
++	msg[0].len = 1;
++	msg[0].buf = buf;
++
++	msg[1].addr = client->addr;
++	msg[1].flags = I2C_M_RD;
++	msg[1].len = 1;
++	msg[1].buf = &buf[1];
++	*val = 0;
++
++	ret = i2c_transfer(client->adapter, msg, 2);
++	if (ret < 0)
++		return ret;
++
++	*val = buf[1];
++
++	return 0;
++}
++
++static int dw9719_i2c_wr8(struct i2c_client *client, u8 reg, u8 val)
++{
++	struct i2c_msg msg;
++	int ret;
++
++	u8 buf[2] = { reg, val };
++
++	msg.addr = client->addr;
++	msg.flags = 0;
++	msg.len = sizeof(buf);
++	msg.buf = buf;
++
++	ret = i2c_transfer(client->adapter, &msg, 1);
++
++	return ret < 0 ? ret : 0;
++}
++
++static int dw9719_i2c_wr16(struct i2c_client *client, u8 reg, u16 val)
++{
++	struct i2c_msg msg;
++	u8 buf[3] = { reg };
++	int ret;
++
++	put_unaligned_be16(val, buf + 1);
++
++	msg.addr = client->addr;
++	msg.flags = 0;
++	msg.len = sizeof(buf);
++	msg.buf = buf;
++
++	ret = i2c_transfer(client->adapter, &msg, 1);
++
++	return ret < 0 ? ret : 0;
++}
++
++static int dw9719_detect(struct dw9719_device *dw9719)
++{
++	int ret;
++	u8 val;
++
++	ret = dw9719_i2c_rd8(dw9719->client, DW9719_INFO, &val);
++	if (ret < 0)
++		return ret;
++
++	if (val != DW9719_ID) {
++		dev_err(dw9719->dev, "Failed to detect correct id\n");
++		ret = -ENXIO;
++	}
++
++	return 0;
++}
++
++static int dw9719_power_down(struct dw9719_device *dw9719)
++{
++	return regulator_bulk_disable(NUM_REGULATORS, dw9719->regulators);
++}
++
++static int dw9719_power_up(struct dw9719_device *dw9719)
++{
++	int ret;
++
++	ret = regulator_bulk_enable(NUM_REGULATORS, dw9719->regulators);
++	if (ret)
++		return ret;
++
++	/* Jiggle SCL pin to wake up device */
++	ret = dw9719_i2c_wr8(dw9719->client, DW9719_CONTROL, 1);
++
++	/* Need 100us to transit from SHUTDOWN to STANDBY*/
++	usleep_range(100, 1000);
++
++	ret = dw9719_i2c_wr8(dw9719->client, DW9719_CONTROL,
++			     DW9719_ENABLE_RINGING);
++	if (ret < 0)
++		goto fail_powerdown;
++
++	ret = dw9719_i2c_wr8(dw9719->client, DW9719_MODE, DW9719_MODE_SAC3);
++	if (ret < 0)
++		goto fail_powerdown;
++
++	ret = dw9719_i2c_wr8(dw9719->client, DW9719_VCM_FREQ,
++			     DW9719_DEFAULT_VCM_FREQ);
++	if (ret < 0)
++		goto fail_powerdown;
++
++	return 0;
++
++fail_powerdown:
++	dw9719_power_down(dw9719);
++	return ret;
++}
++
++static int dw9719_t_focus_abs(struct dw9719_device *dw9719, s32 value)
++{
++	int ret;
++
++	value = clamp(value, 0, DW9719_MAX_FOCUS_POS);
++	ret = dw9719_i2c_wr16(dw9719->client, DW9719_VCM_CURRENT, value);
++	if (ret < 0)
++		return ret;
++
++	return 0;
++}
++
++static int dw9719_set_ctrl(struct v4l2_ctrl *ctrl)
++{
++	struct dw9719_device *dw9719 = container_of(ctrl->handler,
++						    struct dw9719_device,
++						    ctrls.handler);
++	int ret;
++
++	/* Only apply changes to the controls if the device is powered up */
++	if (!pm_runtime_get_if_in_use(dw9719->dev))
++		return 0;
++
++	switch (ctrl->id) {
++	case V4L2_CID_FOCUS_ABSOLUTE:
++		ret = dw9719_t_focus_abs(dw9719, ctrl->val);
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	pm_runtime_put(dw9719->dev);
++
++	return ret;
++}
++
++static const struct v4l2_ctrl_ops dw9719_ctrl_ops = {
++	.s_ctrl = dw9719_set_ctrl,
++};
++
++static int __maybe_unused dw9719_suspend(struct device *dev)
++{
++	struct v4l2_subdev *sd = dev_get_drvdata(dev);
++	struct dw9719_device *dw9719 = to_dw9719_device(sd);
++	int ret;
++	int val;
++
++	for (val = dw9719->ctrls.focus->val; val >= 0;
++	     val -= DW9719_CTRL_STEPS) {
++		ret = dw9719_t_focus_abs(dw9719, val);
++		if (ret)
++			return ret;
++
++		usleep_range(DW9719_CTRL_DELAY_US, DW9719_CTRL_DELAY_US + 10);
++	}
++
++	return dw9719_power_down(dw9719);
++}
++
++static int __maybe_unused dw9719_resume(struct device *dev)
++{
++	struct v4l2_subdev *sd = dev_get_drvdata(dev);
++	struct dw9719_device *dw9719 = to_dw9719_device(sd);
++	int current_focus = dw9719->ctrls.focus->val;
++	int ret;
++	int val;
++
++	ret = dw9719_power_up(dw9719);
++	if (ret)
++		return ret;
++
++	for (val = current_focus % DW9719_CTRL_STEPS; val < current_focus;
++	     val += DW9719_CTRL_STEPS) {
++		ret = dw9719_t_focus_abs(dw9719, val);
++		if (ret)
++			goto err_power_down;
++
++		usleep_range(DW9719_CTRL_DELAY_US, DW9719_CTRL_DELAY_US + 10);
++	}
++
++	return 0;
++
++err_power_down:
++	dw9719_power_down(dw9719);
++	return ret;
++}
++
++static int dw9719_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
++{
++	return pm_runtime_resume_and_get(sd->dev);
++}
++
++static int dw9719_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
++{
++	pm_runtime_put(sd->dev);
++
++	return 0;
++}
++
++static const struct v4l2_subdev_internal_ops dw9719_internal_ops = {
++	.open = dw9719_open,
++	.close = dw9719_close,
++};
++
++static int dw9719_init_controls(struct dw9719_device *dw9719)
++{
++	const struct v4l2_ctrl_ops *ops = &dw9719_ctrl_ops;
++	int ret;
++
++	ret = v4l2_ctrl_handler_init(&dw9719->ctrls.handler, 1);
++	if (ret)
++		return ret;
++
++	dw9719->ctrls.focus = v4l2_ctrl_new_std(&dw9719->ctrls.handler, ops,
++						V4L2_CID_FOCUS_ABSOLUTE, 0,
++						DW9719_MAX_FOCUS_POS, 1, 0);
++
++	if (dw9719->ctrls.handler.error) {
++		dev_err(dw9719->dev, "Error initialising v4l2 ctrls\n");
++		ret = dw9719->ctrls.handler.error;
++		goto err_free_handler;
++	}
++
++	dw9719->sd.ctrl_handler = &dw9719->ctrls.handler;
++
++	return ret;
++
++err_free_handler:
++	v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
++	return ret;
++}
++
++static const struct v4l2_subdev_ops dw9719_ops = { };
++
++static int dw9719_probe(struct i2c_client *client)
++{
++	struct dw9719_device *dw9719;
++	int ret;
++
++	dw9719 = devm_kzalloc(&client->dev, sizeof(*dw9719), GFP_KERNEL);
++	if (!dw9719)
++		return -ENOMEM;
++
++	dw9719->client = client;
++	dw9719->dev = &client->dev;
++
++	dw9719->regulators[0].supply = "vdd";
++	/*
++	 * The DW9719 has only the 1 VDD voltage input, but some PMICs such as
++	 * the TPS68470 PMIC have I2C passthrough capability, to disconnect the
++	 * sensor's I2C pins from the I2C bus when the sensors VSIO (Sensor-IO)
++	 * is off, because some sensors then short these pins to ground;
++	 * and the DW9719 might sit behind this passthrough, this it needs to
++	 * enable VSIO as that will also enable the I2C passthrough.
++	 */
++	dw9719->regulators[1].supply = "vsio";
++
++	ret = devm_regulator_bulk_get(&client->dev, NUM_REGULATORS,
++				      dw9719->regulators);
++	if (ret)
++		return dev_err_probe(&client->dev, ret, "getting regulators\n");
++
++	v4l2_i2c_subdev_init(&dw9719->sd, client, &dw9719_ops);
++	dw9719->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
++	dw9719->sd.internal_ops = &dw9719_internal_ops;
++
++	ret = dw9719_init_controls(dw9719);
++	if (ret)
++		return ret;
++
++	ret = media_entity_pads_init(&dw9719->sd.entity, 0, NULL);
++	if (ret < 0)
++		goto err_free_ctrl_handler;
++
++	dw9719->sd.entity.function = MEDIA_ENT_F_LENS;
++
++	/*
++	 * We need the driver to work in the event that pm runtime is disable in
++	 * the kernel, so power up and verify the chip now. In the event that
++	 * runtime pm is disabled this will leave the chip on, so that the lens
++	 * will work.
++	 */
++
++	ret = dw9719_power_up(dw9719);
++	if (ret)
++		goto err_cleanup_media;
++
++	ret = dw9719_detect(dw9719);
++	if (ret)
++		goto err_powerdown;
++
++	pm_runtime_set_active(&client->dev);
++	pm_runtime_get_noresume(&client->dev);
++	pm_runtime_enable(&client->dev);
++
++	ret = v4l2_async_register_subdev(&dw9719->sd);
++	if (ret < 0)
++		goto err_pm_runtime;
++
++	pm_runtime_set_autosuspend_delay(&client->dev, 1000);
++	pm_runtime_use_autosuspend(&client->dev);
++	pm_runtime_put_autosuspend(&client->dev);
++
++	return ret;
++
++err_pm_runtime:
++	pm_runtime_disable(&client->dev);
++	pm_runtime_put_noidle(&client->dev);
++err_powerdown:
++	dw9719_power_down(dw9719);
++err_cleanup_media:
++	media_entity_cleanup(&dw9719->sd.entity);
++err_free_ctrl_handler:
++	v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
++
++	return ret;
++}
++
++static void dw9719_remove(struct i2c_client *client)
++{
++	struct v4l2_subdev *sd = i2c_get_clientdata(client);
++	struct dw9719_device *dw9719 = container_of(sd, struct dw9719_device,
++						    sd);
++
++	pm_runtime_disable(&client->dev);
++	v4l2_async_unregister_subdev(sd);
++	v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
++	media_entity_cleanup(&dw9719->sd.entity);
++}
++
++static const struct i2c_device_id dw9719_id_table[] = {
++	{ "dw9719" },
++	{ }
++};
++MODULE_DEVICE_TABLE(i2c, dw9719_id_table);
++
++static const struct dev_pm_ops dw9719_pm_ops = {
++	SET_RUNTIME_PM_OPS(dw9719_suspend, dw9719_resume, NULL)
++};
++
++static struct i2c_driver dw9719_i2c_driver = {
++	.driver = {
++		.name = "dw9719",
++		.pm = &dw9719_pm_ops,
++	},
++	.probe_new = dw9719_probe,
++	.remove = dw9719_remove,
++	.id_table = dw9719_id_table,
++};
++module_i2c_driver(dw9719_i2c_driver);
++
++MODULE_AUTHOR("Daniel Scally <djrscally@gmail.com>");
++MODULE_DESCRIPTION("DW9719 VCM Driver");
++MODULE_LICENSE("GPL");
+-- 
+2.42.0
+
+From 3978aa1e89da9e6cfda3fb0591b5d9aeb0aebf84 Mon Sep 17 00:00:00 2001
+From: Maximilian Luz <luzmaximilian@gmail.com>
+Date: Fri, 15 Jul 2022 23:48:00 +0200
+Subject: [PATCH] drivers/media/i2c: Fix DW9719 dependencies
+
+It should depend on VIDEO_DEV instead of VIDEO_V4L2.
+
+Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
+Patchset: cameras
+---
+ drivers/media/i2c/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
+index 6959ee1a89fb..1d5082fe9ce3 100644
+--- a/drivers/media/i2c/Kconfig
++++ b/drivers/media/i2c/Kconfig
+@@ -857,7 +857,7 @@ config VIDEO_DW9714
+ 
+ config VIDEO_DW9719
+ 	tristate "DW9719 lens voice coil support"
+-	depends on I2C && VIDEO_V4L2
++	depends on I2C && VIDEO_DEV
+ 	select MEDIA_CONTROLLER
+ 	select VIDEO_V4L2_SUBDEV_API
+ 	select V4L2_ASYNC
+-- 
+2.42.0
+
+From 8a6c9505ca9d7a7637d5c60f35f6b38c7f41fa66 Mon Sep 17 00:00:00 2001
+From: Daniel Scally <dan.scally@ideasonboard.com>
+Date: Thu, 2 Mar 2023 12:59:39 +0000
+Subject: [PATCH] platform/x86: int3472: Remap reset GPIO for INT347E
+
+ACPI _HID INT347E represents the OmniVision 7251 camera sensor. The
+driver for this sensor expects a single pin named "enable", but on
+some Microsoft Surface platforms the sensor is assigned a single
+GPIO who's type flag is INT3472_GPIO_TYPE_RESET.
+
+Remap the GPIO pin's function from "reset" to "enable". This is done
+outside of the existing remap table since it is a more widespread
+discrepancy than that method is designed for. Additionally swap the
+polarity of the pin to match the driver's expectation.
+
+Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
+Patchset: cameras
+---
+ drivers/platform/x86/intel/int3472/discrete.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c
+index e33c2d75975c..c0c90ae66b70 100644
+--- a/drivers/platform/x86/intel/int3472/discrete.c
++++ b/drivers/platform/x86/intel/int3472/discrete.c
+@@ -57,6 +57,9 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347
+ 					  const char *func, u32 polarity)
+ {
+ 	char *path = agpio->resource_source.string_ptr;
++	const struct acpi_device_id ov7251_ids[] = {
++		{ "INT347E" },
++	};
+ 	struct gpiod_lookup *table_entry;
+ 	struct acpi_device *adev;
+ 	acpi_handle handle;
+@@ -67,6 +70,17 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347
+ 		return -EINVAL;
+ 	}
+ 
++	/*
++	 * In addition to the function remap table we need to bulk remap the
++	 * "reset" GPIO for the OmniVision 7251 sensor, as the driver for that
++	 * expects its only GPIO pin to be called "enable" (and to have the
++	 * opposite polarity).
++	 */
++	if (!strcmp(func, "reset") && !acpi_match_device_ids(int3472->sensor, ov7251_ids)) {
++		func = "enable";
++		polarity = GPIO_ACTIVE_HIGH;
++	}
++
+ 	status = acpi_get_handle(NULL, path, &handle);
+ 	if (ACPI_FAILURE(status))
+ 		return -EINVAL;
+-- 
+2.42.0
+
+From 697a87edc7b6cac129d311e5c9539f1eb6d3a0b1 Mon Sep 17 00:00:00 2001
+From: Daniel Scally <dan.scally@ideasonboard.com>
+Date: Tue, 21 Mar 2023 13:45:26 +0000
+Subject: [PATCH] media: i2c: Clarify that gain is Analogue gain in OV7251
+
+Update the control ID for the gain control in the ov7251 driver to
+V4L2_CID_ANALOGUE_GAIN.
+
+Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
+Patchset: cameras
+---
+ drivers/media/i2c/ov7251.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c
+index 675fb37a6fea..43b30db08c9e 100644
+--- a/drivers/media/i2c/ov7251.c
++++ b/drivers/media/i2c/ov7251.c
+@@ -1051,7 +1051,7 @@ static int ov7251_s_ctrl(struct v4l2_ctrl *ctrl)
+ 	case V4L2_CID_EXPOSURE:
+ 		ret = ov7251_set_exposure(ov7251, ctrl->val);
+ 		break;
+-	case V4L2_CID_GAIN:
++	case V4L2_CID_ANALOGUE_GAIN:
+ 		ret = ov7251_set_gain(ov7251, ctrl->val);
+ 		break;
+ 	case V4L2_CID_TEST_PATTERN:
+@@ -1551,7 +1551,7 @@ static int ov7251_init_ctrls(struct ov7251 *ov7251)
+ 	ov7251->exposure = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops,
+ 					     V4L2_CID_EXPOSURE, 1, 32, 1, 32);
+ 	ov7251->gain = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops,
+-					 V4L2_CID_GAIN, 16, 1023, 1, 16);
++					 V4L2_CID_ANALOGUE_GAIN, 16, 1023, 1, 16);
+ 	v4l2_ctrl_new_std_menu_items(&ov7251->ctrls, &ov7251_ctrl_ops,
+ 				     V4L2_CID_TEST_PATTERN,
+ 				     ARRAY_SIZE(ov7251_test_pattern_menu) - 1,
+-- 
+2.42.0
+
+From 7596584c77520e6e3958345fa2f404f2fe5ba423 Mon Sep 17 00:00:00 2001
+From: Daniel Scally <dan.scally@ideasonboard.com>
+Date: Wed, 22 Mar 2023 11:01:42 +0000
+Subject: [PATCH] media: v4l2-core: Acquire privacy led in
+ v4l2_async_register_subdev()
+
+The current call to v4l2_subdev_get_privacy_led() is contained in
+v4l2_async_register_subdev_sensor(), but that function isn't used by
+all the sensor drivers. Move the acquisition of the privacy led to
+v4l2_async_register_subdev() instead.
+
+Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
+Patchset: cameras
+---
+ drivers/media/v4l2-core/v4l2-async.c  | 4 ++++
+ drivers/media/v4l2-core/v4l2-fwnode.c | 4 ----
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
+index b16b5f4cb91e..33739a979cbc 100644
+--- a/drivers/media/v4l2-core/v4l2-async.c
++++ b/drivers/media/v4l2-core/v4l2-async.c
+@@ -760,6 +760,10 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
+ 	struct v4l2_async_notifier *notifier;
+ 	int ret;
+ 
++	ret = v4l2_subdev_get_privacy_led(sd);
++	if (ret < 0)
++		return ret;
++
+ 	/*
+ 	 * No reference taken. The reference is held by the device
+ 	 * (struct v4l2_subdev.dev), and async sub-device does not
+diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
+index 4fa9225aa3d9..ed4c75253cbc 100644
+--- a/drivers/media/v4l2-core/v4l2-fwnode.c
++++ b/drivers/media/v4l2-core/v4l2-fwnode.c
+@@ -1314,10 +1314,6 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
+ 
+ 	v4l2_async_nf_init(notifier);
+ 
+-	ret = v4l2_subdev_get_privacy_led(sd);
+-	if (ret < 0)
+-		goto out_cleanup;
+-
+ 	ret = v4l2_async_nf_parse_fwnode_sensor(sd->dev, notifier);
+ 	if (ret < 0)
+ 		goto out_cleanup;
+-- 
+2.42.0
+
+From 7616189535ee7f83ef622b4f2516de8f83fd0a89 Mon Sep 17 00:00:00 2001
+From: Kate Hsuan <hpa@redhat.com>
+Date: Tue, 21 Mar 2023 23:37:16 +0800
+Subject: [PATCH] platform: x86: int3472: Add MFD cell for tps68470 LED
+
+Add MFD cell for tps68470-led.
+
+Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
+Signed-off-by: Kate Hsuan <hpa@redhat.com>
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Patchset: cameras
+---
+ drivers/platform/x86/intel/int3472/tps68470.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/platform/x86/intel/int3472/tps68470.c b/drivers/platform/x86/intel/int3472/tps68470.c
+index e3e1696e7f0e..423dc555093f 100644
+--- a/drivers/platform/x86/intel/int3472/tps68470.c
++++ b/drivers/platform/x86/intel/int3472/tps68470.c
+@@ -17,7 +17,7 @@
+ #define DESIGNED_FOR_CHROMEOS		1
+ #define DESIGNED_FOR_WINDOWS		2
+ 
+-#define TPS68470_WIN_MFD_CELL_COUNT	3
++#define TPS68470_WIN_MFD_CELL_COUNT	4
+ 
+ static const struct mfd_cell tps68470_cros[] = {
+ 	{ .name = "tps68470-gpio" },
+@@ -200,7 +200,8 @@ static int skl_int3472_tps68470_probe(struct i2c_client *client)
+ 		cells[1].name = "tps68470-regulator";
+ 		cells[1].platform_data = (void *)board_data->tps68470_regulator_pdata;
+ 		cells[1].pdata_size = sizeof(struct tps68470_regulator_platform_data);
+-		cells[2].name = "tps68470-gpio";
++		cells[2].name = "tps68470-led";
++		cells[3].name = "tps68470-gpio";
+ 
+ 		for (i = 0; i < board_data->n_gpiod_lookups; i++)
+ 			gpiod_add_lookup_table(board_data->tps68470_gpio_lookup_tables[i]);
+-- 
+2.42.0
+
+From 7de3af05f1128542fb27a72b8fe3ae219609d358 Mon Sep 17 00:00:00 2001
+From: Kate Hsuan <hpa@redhat.com>
+Date: Tue, 21 Mar 2023 23:37:17 +0800
+Subject: [PATCH] include: mfd: tps68470: Add masks for LEDA and LEDB
+
+Add flags for both LEDA(TPS68470_ILEDCTL_ENA), LEDB
+(TPS68470_ILEDCTL_ENB), and current control mask for LEDB
+(TPS68470_ILEDCTL_CTRLB)
+
+Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Kate Hsuan <hpa@redhat.com>
+Patchset: cameras
+---
+ include/linux/mfd/tps68470.h | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/include/linux/mfd/tps68470.h b/include/linux/mfd/tps68470.h
+index 7807fa329db0..2d2abb25b944 100644
+--- a/include/linux/mfd/tps68470.h
++++ b/include/linux/mfd/tps68470.h
+@@ -34,6 +34,7 @@
+ #define TPS68470_REG_SGPO		0x22
+ #define TPS68470_REG_GPDI		0x26
+ #define TPS68470_REG_GPDO		0x27
++#define TPS68470_REG_ILEDCTL		0x28
+ #define TPS68470_REG_VCMVAL		0x3C
+ #define TPS68470_REG_VAUX1VAL		0x3D
+ #define TPS68470_REG_VAUX2VAL		0x3E
+@@ -94,4 +95,8 @@
+ #define TPS68470_GPIO_MODE_OUT_CMOS	2
+ #define TPS68470_GPIO_MODE_OUT_ODRAIN	3
+ 
++#define TPS68470_ILEDCTL_ENA		BIT(2)
++#define TPS68470_ILEDCTL_ENB		BIT(6)
++#define TPS68470_ILEDCTL_CTRLB		GENMASK(5, 4)
++
+ #endif /* __LINUX_MFD_TPS68470_H */
+-- 
+2.42.0
+
+From 8f2e512696d2ead1eef0f6b4f6b00a02f3ce18b2 Mon Sep 17 00:00:00 2001
+From: Kate Hsuan <hpa@redhat.com>
+Date: Tue, 21 Mar 2023 23:37:18 +0800
+Subject: [PATCH] leds: tps68470: Add LED control for tps68470
+
+There are two LED controllers, LEDA indicator LED and LEDB flash LED for
+tps68470. LEDA can be enabled by setting TPS68470_ILEDCTL_ENA. Moreover,
+tps68470 provides four levels of power status for LEDB. If the
+properties called "ti,ledb-current" can be found, the current will be
+set according to the property values. These two LEDs can be controlled
+through the LED class of sysfs (tps68470-leda and tps68470-ledb).
+
+Signed-off-by: Kate Hsuan <hpa@redhat.com>
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Patchset: cameras
+---
+ drivers/leds/Kconfig         |  12 +++
+ drivers/leds/Makefile        |   1 +
+ drivers/leds/leds-tps68470.c | 185 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 198 insertions(+)
+ create mode 100644 drivers/leds/leds-tps68470.c
+
+diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
+index 6046dfeca16f..385c06e4f1d3 100644
+--- a/drivers/leds/Kconfig
++++ b/drivers/leds/Kconfig
+@@ -864,6 +864,18 @@ config LEDS_TPS6105X
+ 	  It is a single boost converter primarily for white LEDs and
+ 	  audio amplifiers.
+ 
++config LEDS_TPS68470
++	tristate "LED support for TI TPS68470"
++	depends on LEDS_CLASS
++	depends on INTEL_SKL_INT3472
++	help
++	  This driver supports TPS68470 PMIC with LED chip.
++	  It provides two LED controllers, with the ability to drive 2
++	  indicator LEDs and 2 flash LEDs.
++
++	  To compile this driver as a module, choose M and it will be
++	  called leds-tps68470
++
+ config LEDS_IP30
+ 	tristate "LED support for SGI Octane machines"
+ 	depends on LEDS_CLASS
+diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
+index d71f1226540c..e2002b535967 100644
+--- a/drivers/leds/Makefile
++++ b/drivers/leds/Makefile
+@@ -83,6 +83,7 @@ obj-$(CONFIG_LEDS_TCA6507)		+= leds-tca6507.o
+ obj-$(CONFIG_LEDS_TI_LMU_COMMON)	+= leds-ti-lmu-common.o
+ obj-$(CONFIG_LEDS_TLC591XX)		+= leds-tlc591xx.o
+ obj-$(CONFIG_LEDS_TPS6105X)		+= leds-tps6105x.o
++obj-$(CONFIG_LEDS_TPS68470)		+= leds-tps68470.o
+ obj-$(CONFIG_LEDS_TURRIS_OMNIA)		+= leds-turris-omnia.o
+ obj-$(CONFIG_LEDS_WM831X_STATUS)	+= leds-wm831x-status.o
+ obj-$(CONFIG_LEDS_WM8350)		+= leds-wm8350.o
+diff --git a/drivers/leds/leds-tps68470.c b/drivers/leds/leds-tps68470.c
+new file mode 100644
+index 000000000000..35aeb5db89c8
+--- /dev/null
++++ b/drivers/leds/leds-tps68470.c
+@@ -0,0 +1,185 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * LED driver for TPS68470 PMIC
++ *
++ * Copyright (C) 2023 Red Hat
++ *
++ * Authors:
++ *	Kate Hsuan <hpa@redhat.com>
++ */
++
++#include <linux/leds.h>
++#include <linux/mfd/tps68470.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/property.h>
++#include <linux/regmap.h>
++
++
++#define lcdev_to_led(led_cdev) \
++	container_of(led_cdev, struct tps68470_led, lcdev)
++
++#define led_to_tps68470(led, index) \
++	container_of(led, struct tps68470_device, leds[index])
++
++enum tps68470_led_ids {
++	TPS68470_ILED_A,
++	TPS68470_ILED_B,
++	TPS68470_NUM_LEDS
++};
++
++static const char *tps68470_led_names[] = {
++	[TPS68470_ILED_A] = "tps68470-iled_a",
++	[TPS68470_ILED_B] = "tps68470-iled_b",
++};
++
++struct tps68470_led {
++	unsigned int led_id;
++	struct led_classdev lcdev;
++};
++
++struct tps68470_device {
++	struct device *dev;
++	struct regmap *regmap;
++	struct tps68470_led leds[TPS68470_NUM_LEDS];
++};
++
++enum ctrlb_current {
++	CTRLB_2MA	= 0,
++	CTRLB_4MA	= 1,
++	CTRLB_8MA	= 2,
++	CTRLB_16MA	= 3,
++};
++
++static int tps68470_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness)
++{
++	struct tps68470_led *led = lcdev_to_led(led_cdev);
++	struct tps68470_device *tps68470 = led_to_tps68470(led, led->led_id);
++	struct regmap *regmap = tps68470->regmap;
++
++	switch (led->led_id) {
++	case TPS68470_ILED_A:
++		return regmap_update_bits(regmap, TPS68470_REG_ILEDCTL, TPS68470_ILEDCTL_ENA,
++					  brightness ? TPS68470_ILEDCTL_ENA : 0);
++	case TPS68470_ILED_B:
++		return regmap_update_bits(regmap, TPS68470_REG_ILEDCTL, TPS68470_ILEDCTL_ENB,
++					  brightness ? TPS68470_ILEDCTL_ENB : 0);
++	}
++	return -EINVAL;
++}
++
++static enum led_brightness tps68470_brightness_get(struct led_classdev *led_cdev)
++{
++	struct tps68470_led *led = lcdev_to_led(led_cdev);
++	struct tps68470_device *tps68470 = led_to_tps68470(led, led->led_id);
++	struct regmap *regmap = tps68470->regmap;
++	int ret = 0;
++	int value = 0;
++
++	ret =  regmap_read(regmap, TPS68470_REG_ILEDCTL, &value);
++	if (ret)
++		return dev_err_probe(led_cdev->dev, -EINVAL, "failed on reading register\n");
++
++	switch (led->led_id) {
++	case TPS68470_ILED_A:
++		value = value & TPS68470_ILEDCTL_ENA;
++		break;
++	case TPS68470_ILED_B:
++		value = value & TPS68470_ILEDCTL_ENB;
++		break;
++	}
++
++	return value ? LED_ON : LED_OFF;
++}
++
++
++static int tps68470_ledb_current_init(struct platform_device *pdev,
++				      struct tps68470_device *tps68470)
++{
++	int ret = 0;
++	unsigned int curr;
++
++	/* configure LEDB current if the properties can be got */
++	if (!device_property_read_u32(&pdev->dev, "ti,ledb-current", &curr)) {
++		if (curr > CTRLB_16MA) {
++			dev_err(&pdev->dev,
++				"Invalid LEDB current value: %d\n",
++				curr);
++			return -EINVAL;
++		}
++		ret = regmap_update_bits(tps68470->regmap, TPS68470_REG_ILEDCTL,
++					 TPS68470_ILEDCTL_CTRLB, curr);
++	}
++	return ret;
++}
++
++static int tps68470_leds_probe(struct platform_device *pdev)
++{
++	int i = 0;
++	int ret = 0;
++	struct tps68470_device *tps68470;
++	struct tps68470_led *led;
++	struct led_classdev *lcdev;
++
++	tps68470 = devm_kzalloc(&pdev->dev, sizeof(struct tps68470_device),
++				GFP_KERNEL);
++	if (!tps68470)
++		return -ENOMEM;
++
++	tps68470->dev = &pdev->dev;
++	tps68470->regmap = dev_get_drvdata(pdev->dev.parent);
++
++	for (i = 0; i < TPS68470_NUM_LEDS; i++) {
++		led = &tps68470->leds[i];
++		lcdev = &led->lcdev;
++
++		led->led_id = i;
++
++		lcdev->name = devm_kasprintf(tps68470->dev, GFP_KERNEL, "%s::%s",
++					     tps68470_led_names[i], LED_FUNCTION_INDICATOR);
++		if (!lcdev->name)
++			return -ENOMEM;
++
++		lcdev->max_brightness = 1;
++		lcdev->brightness = 0;
++		lcdev->brightness_set_blocking = tps68470_brightness_set;
++		lcdev->brightness_get = tps68470_brightness_get;
++		lcdev->dev = &pdev->dev;
++
++		ret = devm_led_classdev_register(tps68470->dev, lcdev);
++		if (ret) {
++			dev_err_probe(tps68470->dev, ret,
++				      "error registering led\n");
++			goto err_exit;
++		}
++
++		if (i == TPS68470_ILED_B) {
++			ret = tps68470_ledb_current_init(pdev, tps68470);
++			if (ret)
++				goto err_exit;
++		}
++	}
++
++err_exit:
++	if (ret) {
++		for (i = 0; i < TPS68470_NUM_LEDS; i++) {
++			if (tps68470->leds[i].lcdev.name)
++				devm_led_classdev_unregister(&pdev->dev,
++							     &tps68470->leds[i].lcdev);
++		}
++	}
++
++	return ret;
++}
++static struct platform_driver tps68470_led_driver = {
++	.driver = {
++		   .name = "tps68470-led",
++	},
++	.probe = tps68470_leds_probe,
++};
++
++module_platform_driver(tps68470_led_driver);
++
++MODULE_ALIAS("platform:tps68470-led");
++MODULE_DESCRIPTION("LED driver for TPS68470 PMIC");
++MODULE_LICENSE("GPL v2");
+-- 
+2.42.0
+

+ 109 - 0
patches/6.5/0013-amd-gpio.patch

@@ -0,0 +1,109 @@
+From a909512fc2501e9eca6b400a46283fa98e28548a Mon Sep 17 00:00:00 2001
+From: Sachi King <nakato@nakato.io>
+Date: Sat, 29 May 2021 17:47:38 +1000
+Subject: [PATCH] ACPI: Add quirk for Surface Laptop 4 AMD missing irq 7
+ override
+
+This patch is the work of Thomas Gleixner <tglx@linutronix.de> and is
+copied from:
+https://lore.kernel.org/lkml/87lf8ddjqx.ffs@nanos.tec.linutronix.de/
+
+This patch adds a quirk to the ACPI setup to patch in the the irq 7 pin
+setup that is missing in the laptops ACPI table.
+
+This patch was used for validation of the issue, and is not a proper
+fix, but is probably a better temporary hack than continuing to probe
+the Legacy PIC and run with the PIC in an unknown state.
+
+Patchset: amd-gpio
+---
+ arch/x86/kernel/acpi/boot.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
+index 53369c57751e..1ec1a9015178 100644
+--- a/arch/x86/kernel/acpi/boot.c
++++ b/arch/x86/kernel/acpi/boot.c
+@@ -22,6 +22,7 @@
+ #include <linux/efi-bgrt.h>
+ #include <linux/serial_core.h>
+ #include <linux/pgtable.h>
++#include <linux/dmi.h>
+ 
+ #include <asm/e820/api.h>
+ #include <asm/irqdomain.h>
+@@ -1256,6 +1257,17 @@ static void __init mp_config_acpi_legacy_irqs(void)
+ 	}
+ }
+ 
++static const struct dmi_system_id surface_quirk[] __initconst = {
++	{
++		.ident = "Microsoft Surface Laptop 4 (AMD)",
++		.matches = {
++			DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
++			DMI_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_4_1952:1953")
++		},
++	},
++	{}
++};
++
+ /*
+  * Parse IOAPIC related entries in MADT
+  * returns 0 on success, < 0 on error
+@@ -1311,6 +1323,11 @@ static int __init acpi_parse_madt_ioapic_entries(void)
+ 		acpi_sci_ioapic_setup(acpi_gbl_FADT.sci_interrupt, 0, 0,
+ 				      acpi_gbl_FADT.sci_interrupt);
+ 
++	if (dmi_check_system(surface_quirk)) {
++		pr_warn("Surface hack: Override irq 7\n");
++		mp_override_legacy_irq(7, 3, 3, 7);
++	}
++
+ 	/* Fill in identity legacy mappings where no override */
+ 	mp_config_acpi_legacy_irqs();
+ 
+-- 
+2.42.0
+
+From f1b1d63f60ef83b9c8d8db177324f3496cd14542 Mon Sep 17 00:00:00 2001
+From: Maximilian Luz <luzmaximilian@gmail.com>
+Date: Thu, 3 Jun 2021 14:04:26 +0200
+Subject: [PATCH] ACPI: Add AMD 13" Surface Laptop 4 model to irq 7 override
+ quirk
+
+The 13" version of the Surface Laptop 4 has the same problem as the 15"
+version, but uses a different SKU. Add that SKU to the quirk as well.
+
+Patchset: amd-gpio
+---
+ arch/x86/kernel/acpi/boot.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
+index 1ec1a9015178..a7d40015e46a 100644
+--- a/arch/x86/kernel/acpi/boot.c
++++ b/arch/x86/kernel/acpi/boot.c
+@@ -1259,12 +1259,19 @@ static void __init mp_config_acpi_legacy_irqs(void)
+ 
+ static const struct dmi_system_id surface_quirk[] __initconst = {
+ 	{
+-		.ident = "Microsoft Surface Laptop 4 (AMD)",
++		.ident = "Microsoft Surface Laptop 4 (AMD 15\")",
+ 		.matches = {
+ 			DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+ 			DMI_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_4_1952:1953")
+ 		},
+ 	},
++	{
++		.ident = "Microsoft Surface Laptop 4 (AMD 13\")",
++		.matches = {
++			DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
++			DMI_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_4_1958:1959")
++		},
++	},
+ 	{}
+ };
+ 
+-- 
+2.42.0
+

+ 109 - 0
patches/6.5/0014-rtc.patch

@@ -0,0 +1,109 @@
+From a7c5852afe2108a4484aece5f408edf9b5f03399 Mon Sep 17 00:00:00 2001
+From: "Bart Groeneveld | GPX Solutions B.V" <bart@gpxbv.nl>
+Date: Mon, 5 Dec 2022 16:08:46 +0100
+Subject: [PATCH] acpi: allow usage of acpi_tad on HW-reduced platforms
+
+The specification [1] allows so-called HW-reduced platforms,
+which do not implement everything, especially the wakeup related stuff.
+
+In that case, it is still usable as a RTC. This is helpful for [2]
+and [3], which is about a device with no other working RTC,
+but it does have an HW-reduced TAD, which can be used as a RTC instead.
+
+[1]: https://uefi.org/specs/ACPI/6.5/09_ACPI_Defined_Devices_and_Device_Specific_Objects.html#time-and-alarm-device
+[2]: https://bugzilla.kernel.org/show_bug.cgi?id=212313
+[3]: https://github.com/linux-surface/linux-surface/issues/415
+
+Signed-off-by: Bart Groeneveld | GPX Solutions B.V. <bart@gpxbv.nl>
+Patchset: rtc
+---
+ drivers/acpi/acpi_tad.c | 35 ++++++++++++++++++++++++-----------
+ 1 file changed, 24 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c
+index e9b8e8305e23..944276934e7e 100644
+--- a/drivers/acpi/acpi_tad.c
++++ b/drivers/acpi/acpi_tad.c
+@@ -432,6 +432,14 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
+ 
+ static DEVICE_ATTR_RO(caps);
+ 
++static struct attribute *acpi_tad_attrs[] = {
++	&dev_attr_caps.attr,
++	NULL,
++};
++static const struct attribute_group acpi_tad_attr_group = {
++	.attrs	= acpi_tad_attrs,
++};
++
+ static ssize_t ac_alarm_store(struct device *dev, struct device_attribute *attr,
+ 			      const char *buf, size_t count)
+ {
+@@ -480,15 +488,14 @@ static ssize_t ac_status_show(struct device *dev, struct device_attribute *attr,
+ 
+ static DEVICE_ATTR_RW(ac_status);
+ 
+-static struct attribute *acpi_tad_attrs[] = {
+-	&dev_attr_caps.attr,
++static struct attribute *acpi_tad_ac_attrs[] = {
+ 	&dev_attr_ac_alarm.attr,
+ 	&dev_attr_ac_policy.attr,
+ 	&dev_attr_ac_status.attr,
+ 	NULL,
+ };
+-static const struct attribute_group acpi_tad_attr_group = {
+-	.attrs	= acpi_tad_attrs,
++static const struct attribute_group acpi_tad_ac_attr_group = {
++	.attrs	= acpi_tad_ac_attrs,
+ };
+ 
+ static ssize_t dc_alarm_store(struct device *dev, struct device_attribute *attr,
+@@ -563,13 +570,18 @@ static int acpi_tad_remove(struct platform_device *pdev)
+ 
+ 	pm_runtime_get_sync(dev);
+ 
++	if (dd->capabilities & ACPI_TAD_AC_WAKE)
++		sysfs_remove_group(&dev->kobj, &acpi_tad_ac_attr_group);
++
+ 	if (dd->capabilities & ACPI_TAD_DC_WAKE)
+ 		sysfs_remove_group(&dev->kobj, &acpi_tad_dc_attr_group);
+ 
+ 	sysfs_remove_group(&dev->kobj, &acpi_tad_attr_group);
+ 
+-	acpi_tad_disable_timer(dev, ACPI_TAD_AC_TIMER);
+-	acpi_tad_clear_status(dev, ACPI_TAD_AC_TIMER);
++	if (dd->capabilities & ACPI_TAD_AC_WAKE) {
++		acpi_tad_disable_timer(dev, ACPI_TAD_AC_TIMER);
++		acpi_tad_clear_status(dev, ACPI_TAD_AC_TIMER);
++	}
+ 	if (dd->capabilities & ACPI_TAD_DC_WAKE) {
+ 		acpi_tad_disable_timer(dev, ACPI_TAD_DC_TIMER);
+ 		acpi_tad_clear_status(dev, ACPI_TAD_DC_TIMER);
+@@ -604,11 +616,6 @@ static int acpi_tad_probe(struct platform_device *pdev)
+ 		return -ENODEV;
+ 	}
+ 
+-	if (!acpi_has_method(handle, "_PRW")) {
+-		dev_info(dev, "Missing _PRW\n");
+-		return -ENODEV;
+-	}
+-
+ 	dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
+ 	if (!dd)
+ 		return -ENOMEM;
+@@ -637,6 +644,12 @@ static int acpi_tad_probe(struct platform_device *pdev)
+ 	if (ret)
+ 		goto fail;
+ 
++	if (caps & ACPI_TAD_AC_WAKE) {
++		ret = sysfs_create_group(&dev->kobj, &acpi_tad_ac_attr_group);
++		if (ret)
++			goto fail;
++	}
++
+ 	if (caps & ACPI_TAD_DC_WAKE) {
+ 		ret = sysfs_create_group(&dev->kobj, &acpi_tad_dc_attr_group);
+ 		if (ret)
+-- 
+2.42.0
+

+ 1 - 1
pkg/arch/kernel/0001-surface3-oemb.patch

@@ -1 +1 @@
-../../../patches/6.4/0001-surface3-oemb.patch
+../../../patches/6.5/0001-surface3-oemb.patch

+ 1 - 1
pkg/arch/kernel/0002-mwifiex.patch

@@ -1 +1 @@
-../../../patches/6.4/0002-mwifiex.patch
+../../../patches/6.5/0002-mwifiex.patch

+ 1 - 1
pkg/arch/kernel/0003-ath10k.patch

@@ -1 +1 @@
-../../../patches/6.4/0003-ath10k.patch
+../../../patches/6.5/0003-ath10k.patch

+ 1 - 1
pkg/arch/kernel/0004-ipts.patch

@@ -1 +1 @@
-../../../patches/6.4/0004-ipts.patch
+../../../patches/6.5/0004-ipts.patch

+ 1 - 1
pkg/arch/kernel/0005-ithc.patch

@@ -1 +1 @@
-../../../patches/6.4/0005-ithc.patch
+../../../patches/6.5/0005-ithc.patch

+ 0 - 1
pkg/arch/kernel/0006-surface-sam-over-hid.patch

@@ -1 +0,0 @@
-../../../patches/6.4/0006-surface-sam-over-hid.patch

+ 1 - 0
pkg/arch/kernel/0006-surface-sam.patch

@@ -0,0 +1 @@
+../../../patches/6.5/0006-surface-sam.patch

+ 0 - 1
pkg/arch/kernel/0007-surface-button.patch

@@ -1 +0,0 @@
-../../../patches/6.4/0007-surface-button.patch

+ 1 - 0
pkg/arch/kernel/0007-surface-sam-over-hid.patch

@@ -0,0 +1 @@
+../../../patches/6.5/0007-surface-sam-over-hid.patch

+ 1 - 0
pkg/arch/kernel/0008-surface-button.patch

@@ -0,0 +1 @@
+../../../patches/6.5/0008-surface-button.patch

+ 0 - 1
pkg/arch/kernel/0008-surface-typecover.patch

@@ -1 +0,0 @@
-../../../patches/6.4/0008-surface-typecover.patch

+ 0 - 1
pkg/arch/kernel/0009-surface-shutdown.patch

@@ -1 +0,0 @@
-../../../patches/6.4/0009-surface-shutdown.patch

+ 1 - 0
pkg/arch/kernel/0009-surface-typecover.patch

@@ -0,0 +1 @@
+../../../patches/6.5/0009-surface-typecover.patch

+ 0 - 1
pkg/arch/kernel/0010-surface-gpe.patch

@@ -1 +0,0 @@
-../../../patches/6.4/0010-surface-gpe.patch

+ 1 - 0
pkg/arch/kernel/0010-surface-shutdown.patch

@@ -0,0 +1 @@
+../../../patches/6.5/0010-surface-shutdown.patch

+ 0 - 1
pkg/arch/kernel/0011-cameras.patch

@@ -1 +0,0 @@
-../../../patches/6.4/0011-cameras.patch

+ 1 - 0
pkg/arch/kernel/0011-surface-gpe.patch

@@ -0,0 +1 @@
+../../../patches/6.5/0011-surface-gpe.patch

+ 0 - 1
pkg/arch/kernel/0012-amd-gpio.patch

@@ -1 +0,0 @@
-../../../patches/6.4/0012-amd-gpio.patch

+ 1 - 0
pkg/arch/kernel/0012-cameras.patch

@@ -0,0 +1 @@
+../../../patches/6.5/0012-cameras.patch

+ 1 - 0
pkg/arch/kernel/0013-amd-gpio.patch

@@ -0,0 +1 @@
+../../../patches/6.5/0013-amd-gpio.patch

+ 0 - 1
pkg/arch/kernel/0013-rtc.patch

@@ -1 +0,0 @@
-../../../patches/6.4/0013-rtc.patch

+ 1 - 0
pkg/arch/kernel/0014-rtc.patch

@@ -0,0 +1 @@
+../../../patches/6.5/0014-rtc.patch

+ 24 - 22
pkg/arch/kernel/PKGBUILD

@@ -3,7 +3,7 @@
 # Maintainer: Jan Alexander Steffens (heftig) <jan.steffens@gmail.com>
 # Maintainer: Jan Alexander Steffens (heftig) <jan.steffens@gmail.com>
 
 
 pkgbase=linux-surface
 pkgbase=linux-surface
-pkgver=6.4.12.arch1
+pkgver=6.5.5.arch1
 pkgrel=1
 pkgrel=1
 pkgdesc='Linux'
 pkgdesc='Linux'
 _shortver=${pkgver%.*}
 _shortver=${pkgver%.*}
@@ -41,14 +41,15 @@ source=(
   0003-ath10k.patch
   0003-ath10k.patch
   0004-ipts.patch
   0004-ipts.patch
   0005-ithc.patch
   0005-ithc.patch
-  0006-surface-sam-over-hid.patch
-  0007-surface-button.patch
-  0008-surface-typecover.patch
-  0009-surface-shutdown.patch
-  0010-surface-gpe.patch
-  0011-cameras.patch
-  0012-amd-gpio.patch
-  0013-rtc.patch
+  0006-surface-sam.patch
+  0007-surface-sam-over-hid.patch
+  0008-surface-button.patch
+  0009-surface-typecover.patch
+  0010-surface-shutdown.patch
+  0011-surface-gpe.patch
+  0012-cameras.patch
+  0013-amd-gpio.patch
+  0014-rtc.patch
 )
 )
 validpgpkeys=(
 validpgpkeys=(
   'ABAF11C65A2970B130ABE3C479BE3E4300411886'  # Linus Torvalds
   'ABAF11C65A2970B130ABE3C479BE3E4300411886'  # Linus Torvalds
@@ -59,19 +60,20 @@ sha256sums=('SKIP'
             'e8d4bbd63808eeec78680c7de4383333b76ab9d58214042bd4b01d784bbc8ec8'
             'e8d4bbd63808eeec78680c7de4383333b76ab9d58214042bd4b01d784bbc8ec8'
             'c2a412b0203b31bb3c6b64d3c00d7669d3e58c723f542ecdda940016599d342a'
             'c2a412b0203b31bb3c6b64d3c00d7669d3e58c723f542ecdda940016599d342a'
             '3cabe391cc39dbee88a79f76e9e7a68f7fbcd5518941aa2ab73a77692c87dead'
             '3cabe391cc39dbee88a79f76e9e7a68f7fbcd5518941aa2ab73a77692c87dead'
-            '4281f764a3878ae7fccb3bd6bcd9295008d07e5afb5cdb70c65efb30214250e2'
-            'a9c61675289a41904262707dfbfb456432261e789ebe7c224f0d7c381492113e'
-            '045fc86d48d0cd9d6d1fb82c04092f02488199dffa29ad55891cd985d7d2bc12'
-            'a4436d24dd6018c9f192407ea1b78ab6f3de6224e68aed14000fd5674002dcd2'
-            'cf332137123b49c6d713fface2c7965b167901d730ca5c5d6f1c0e9a3afe2009'
-            'a84de783add63dacd5cd9dcbe49bba0a16b058ab81380b938e27e7fa1d5db84c'
-            'cb452a7aa823b7fe2e04283e83d0de22264d747f503d6eeb9959e57d9cd07a48'
-            '3a7e41c44be388a81e6d4ccfb717bb968e8f84d783434db23dcccdcfd161724f'
-            '1e895a49282ad6f483c57afb81108453056b5dced14c482118d0db680d0cd60c'
-            '4d13f1b8bbd5be4d8689c2ca0603d64d443ac6ac6b5ecb69af33dd6d2dd17452'
-            '4f39e17f7f8b495a7c4a5f3ea66e8e20542afc324b0fed1bb70848fe073ccdf1'
-            '4abf6ed8919cf5ddcc837ceda58b2913d63c0803f06db10587acf00877f8030c'
-            'f38ca5439e07d3f5a35571bc12ad4ee9557036fb03f147a2d083adca3f45e53d')
+            '7def972567e6c9f88a409c5dd1c9fd3adc906d3d8a3e2f692fb6f8773f955c70'
+            'bff62acc498ab21962456b3ba11dce91b31916c6a2233be003663bea493c6ce0'
+            '62ed65fe89a7f5529d8af9782924498b38173e0a7a377843535c02a7ac074406'
+            '0bd91368c1a336c2a01f4a96f70b296b04b9cec9cdfdeabfe95bc164f9c0d6d8'
+            '1164ce0940c70c09a30f1a6ff5e6604e285498d64977a237dfe5f88aa421e62f'
+            'ef36d847e00dcccd4a29aecee0dc2b011d1b324056737ad80dc93132ad4b1889'
+            '3770cf9fcd68d0e0b05517e8b9063aa979c42ab62d1caca11b41f3d74912c08f'
+            'bd0da003b690877d7827129316db9ecaa4c27f5dbc7c55808b9e68ecc73dc4ea'
+            'a4d54968c5fe2939d4784f6a49ca263d6fb871e1ee71e07a02adc332fecb54c9'
+            '7ae7b0925047ebc89598a93179a4877acfb4dfbddca66147d8b329c6eefed99d'
+            '5199471ef9026a1a7af70bd1d080871ac2d36d4ca0c24ef8526161e07fb637d6'
+            'c2f80e76c179fce5899f81196b1a586c5c439ab040e9d306d838eaec0703fc31'
+            'f830119a3deb2dfca2d9be75e27019916b5f06e9ce1edec946e37459f65045b8'
+            'df3013454d3973ce22a603e771d461bedbaa9d3fd7d74db9e3a4dbd33a50006d')
 
 
 
 
 export KBUILD_BUILD_HOST=archlinux
 export KBUILD_BUILD_HOST=archlinux

+ 1 - 1
pkg/arch/kernel/surface.config

@@ -1 +1 @@
-../../../configs/surface-6.4.config
+../../../configs/surface-6.5.config

+ 1 - 1
pkg/debian/kernel/version.conf

@@ -1,3 +1,3 @@
-KERNEL_VERSION="6.4.12"
+KERNEL_VERSION="6.5.5"
 KERNEL_REVISION="1"
 KERNEL_REVISION="1"
 KERNEL_LOCALVERSION="-surface"
 KERNEL_LOCALVERSION="-surface"

+ 1 - 1
pkg/fedora/kernel-surface/build-linux-surface.py

@@ -18,7 +18,7 @@ PACKAGE_NAME = "surface"
 ## Fedora tags: kernel-X.Y.Z
 ## Fedora tags: kernel-X.Y.Z
 ## Upstream tags: vX.Y.Z
 ## Upstream tags: vX.Y.Z
 ##
 ##
-PACKAGE_TAG = "kernel-6.4.12-0"
+PACKAGE_TAG = "kernel-6.5.5-0"
 
 
 ##
 ##
 ## The release number of the modified kernel package.
 ## The release number of the modified kernel package.