aboutsummaryrefslogtreecommitdiff
path: root/SOURCES/linux-surface.patch
diff options
context:
space:
mode:
Diffstat (limited to 'SOURCES/linux-surface.patch')
-rw-r--r--SOURCES/linux-surface.patch4622
1 files changed, 3165 insertions, 1457 deletions
diff --git a/SOURCES/linux-surface.patch b/SOURCES/linux-surface.patch
index ac7daa5..925163b 100644
--- a/SOURCES/linux-surface.patch
+++ b/SOURCES/linux-surface.patch
@@ -1,4 +1,41 @@
-From 45a9e7f97fc36942e3d70a78fe5313fa78733933 Mon Sep 17 00:00:00 2001
+From 24686c656a230f642f8ed6c09c184660c08cf46c Mon Sep 17 00:00:00 2001
+From: Maximilian Luz <luzmaximilian@gmail.com>
+Date: Sun, 9 Jun 2024 19:48:58 +0200
+Subject: [PATCH] Revert "efi/x86: Set the PE/COFF header's NX compat flag
+ unconditionally"
+
+This reverts commit 891f8890a4a3663da7056542757022870b499bc1.
+
+Revert because of compatibility issues of MS Surface devices and GRUB
+with NX. In short, these devices get stuck on boot with NX advertised.
+So to not advertise it, add the respective option back in.
+
+Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
+Patchset: secureboot
+---
+ arch/x86/boot/header.S | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S
+index b5c79f43359bc..a1bbedd989e42 100644
+--- a/arch/x86/boot/header.S
++++ b/arch/x86/boot/header.S
+@@ -111,7 +111,11 @@ extra_header_fields:
+ .long salign # SizeOfHeaders
+ .long 0 # CheckSum
+ .word IMAGE_SUBSYSTEM_EFI_APPLICATION # Subsystem (EFI application)
++#ifdef CONFIG_EFI_DXE_MEM_ATTRIBUTES
+ .word IMAGE_DLL_CHARACTERISTICS_NX_COMPAT # DllCharacteristics
++#else
++ .word 0 # DllCharacteristics
++#endif
+ #ifdef CONFIG_X86_32
+ .long 0 # SizeOfStackReserve
+ .long 0 # SizeOfStackCommit
+--
+2.45.1
+
+From a494cdb84ee162accff966a0012992e36e4b0c0a 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
@@ -40,7 +77,7 @@ Patchset: surface3-oemb
3 files changed, 24 insertions(+)
diff --git a/drivers/platform/surface/surface3-wmi.c b/drivers/platform/surface/surface3-wmi.c
-index c15ed7a12784..1ec8edb5aafa 100644
+index c15ed7a12784a..1ec8edb5aafaf 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[] = {
@@ -58,10 +95,10 @@ index c15ed7a12784..1ec8edb5aafa 100644
{ }
};
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
-index 20191a4473c2..5ba599b5aba6 100644
+index d0d24a53df746..43e06166a5d95 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
-@@ -3768,6 +3768,15 @@ static const struct dmi_system_id dmi_platform_data[] = {
+@@ -3777,6 +3777,15 @@ static const struct dmi_system_id dmi_platform_data[] = {
},
.driver_data = (void *)&intel_braswell_platform_data,
},
@@ -78,7 +115,7 @@ index 20191a4473c2..5ba599b5aba6 100644
/*
* 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 5e2ec60e2954..207868c699f2 100644
+index 5e2ec60e2954b..207868c699f29 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[] = {
@@ -97,9 +134,9 @@ index 5e2ec60e2954..207868c699f2 100644
};
--
-2.44.0
+2.45.1
-From 2c7ff35a85341dcd8fa2ea575088881df9dea874 Mon Sep 17 00:00:00 2001
+From 1abf1feb3b521abe9f9c9e8d68d2014e90ecb20d 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
@@ -133,7 +170,7 @@ Patchset: mwifiex
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 5f997becdbaa..9a9929424513 100644
+index 5f997becdbaa2..9a9929424513a 100644
--- a/drivers/net/wireless/marvell/mwifiex/pcie.c
+++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
@@ -1702,9 +1702,21 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
@@ -159,7 +196,7 @@ index 5f997becdbaa..9a9929424513 100644
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
+index dd6d21f1dbfd7..f46b06f8d6435 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[] = {
@@ -252,7 +289,7 @@ index dd6d21f1dbfd..f46b06f8d643 100644
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
+index d6ff964aec5bf..5d30ae39d65ec 100644
--- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h
+++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h
@@ -4,6 +4,7 @@
@@ -264,9 +301,9 @@ index d6ff964aec5b..5d30ae39d65e 100644
void mwifiex_initialize_quirks(struct pcie_service_card *card);
int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev);
--
-2.44.0
+2.45.1
-From 4a326d9e87d1dc4945903560d3d22fbd69a8962c Mon Sep 17 00:00:00 2001
+From 0574bff98f5f5af132783f8f72e8ef22e3f36097 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+
@@ -288,7 +325,7 @@ Patchset: mwifiex
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 9a9929424513..2273e3029776 100644
+index 9a9929424513a..2273e30297766 100644
--- a/drivers/net/wireless/marvell/mwifiex/pcie.c
+++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
@@ -377,6 +377,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
@@ -313,7 +350,7 @@ index 9a9929424513..2273e3029776 100644
}
diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c
-index f46b06f8d643..99b024ecbade 100644
+index f46b06f8d6435..99b024ecbadea 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[] = {
@@ -407,7 +444,7 @@ index f46b06f8d643..99b024ecbade 100644
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
+index 5d30ae39d65ec..c14eb56eb9118 100644
--- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h
+++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h
@@ -5,6 +5,7 @@
@@ -419,9 +456,9 @@ index 5d30ae39d65e..c14eb56eb911 100644
void mwifiex_initialize_quirks(struct pcie_service_card *card);
int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev);
--
-2.44.0
+2.45.1
-From dddda6f9c25716dea1265f71b8286936afa192b5 Mon Sep 17 00:00:00 2001
+From 8a4ee131ced8068371a8fa09da17d82414e6d835 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
@@ -457,7 +494,7 @@ Patchset: mwifiex
1 file changed, 15 insertions(+)
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
-index d31edad7a056..fc08e0c51c87 100644
+index fb716849b60f3..1e7b3798108f7 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -65,6 +65,7 @@ static struct usb_driver btusb_driver;
@@ -476,7 +513,7 @@ index d31edad7a056..fc08e0c51c87 100644
/* Intel Bluetooth devices */
{ USB_DEVICE(0x8087, 0x0025), .driver_info = BTUSB_INTEL_COMBINED },
-@@ -4401,6 +4403,19 @@ static int btusb_probe(struct usb_interface *intf,
+@@ -4417,6 +4419,19 @@ static int btusb_probe(struct usb_interface *intf,
if (id->driver_info & BTUSB_MARVELL)
hdev->set_bdaddr = btusb_set_bdaddr_marvell;
@@ -497,9 +534,9 @@ index d31edad7a056..fc08e0c51c87 100644
(id->driver_info & BTUSB_MEDIATEK)) {
hdev->setup = btusb_mtk_setup;
--
-2.44.0
+2.45.1
-From 7b414f11dfa0be3204b0a43b82a75744f8218d57 Mon Sep 17 00:00:00 2001
+From fc56de38d725edc7c3856c2a2d369e1f170f202f 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
@@ -521,7 +558,7 @@ Patchset: ath10k
1 file changed, 58 insertions(+)
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
-index 0032f8aa892f..17717b53316b 100644
+index fa5e2e6518313..8921b0ebf36b7 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -39,6 +39,9 @@ static bool fw_diag_log;
@@ -554,7 +591,7 @@ index 0032f8aa892f..17717b53316b 100644
static const struct ath10k_hw_params ath10k_hw_params_list[] = {
{
.id = QCA988X_HW_2_0_VERSION,
-@@ -928,6 +937,42 @@ static int ath10k_init_configure_target(struct ath10k *ar)
+@@ -931,6 +940,42 @@ static int ath10k_init_configure_target(struct ath10k *ar)
return 0;
}
@@ -597,7 +634,7 @@ index 0032f8aa892f..17717b53316b 100644
static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar,
const char *dir,
const char *file)
-@@ -942,6 +987,19 @@ static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar,
+@@ -945,6 +990,19 @@ static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar,
if (dir == NULL)
dir = ".";
@@ -618,9 +655,9 @@ index 0032f8aa892f..17717b53316b 100644
ret = firmware_request_nowarn(&fw, filename, ar->dev);
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot fw request '%s': %d\n",
--
-2.44.0
+2.45.1
-From 4a1fdfebd3e84fe581b512f73bbc551ec9e2d0e8 Mon Sep 17 00:00:00 2001
+From ee272e8a81f073b5475a3bb2c3085b55f181bc24 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] mei: me: Add Icelake device ID for iTouch
@@ -633,7 +670,7 @@ Patchset: ipts
2 files changed, 2 insertions(+)
diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h
-index aac36750d2c5..2ba8db8a9583 100644
+index c3a6657dcd4a2..82eef2f4eb0a8 100644
--- a/drivers/misc/mei/hw-me-regs.h
+++ b/drivers/misc/mei/hw-me-regs.h
@@ -92,6 +92,7 @@
@@ -645,7 +682,7 @@ index aac36750d2c5..2ba8db8a9583 100644
#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 8cf636c54032..078d3e773cda 100644
+index 7f59dd38c32f5..a56ad5b3f7790 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[] = {
@@ -657,9 +694,9 @@ index 8cf636c54032..078d3e773cda 100644
{MEI_PCI_DEVICE(MEI_DEV_ID_TGP_LP, MEI_ME_PCH15_CFG)},
--
-2.44.0
+2.45.1
-From b1e22125c8f241f49cc3a6b7eaa0b6430bb1f1ce Mon Sep 17 00:00:00 2001
+From 8536601cfc6d7671f4c11cab4dca57674f59b349 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: Use IOMMU passthrough mode for IPTS
@@ -683,10 +720,10 @@ Patchset: ipts
1 file changed, 29 insertions(+)
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
-index 11652e0bcab3..6c01b1aebf27 100644
+index e4a03588a8a0f..61bc54299a591 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
-@@ -40,6 +40,11 @@
+@@ -39,6 +39,11 @@
#define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA)
#define IS_AZALIA(pdev) ((pdev)->vendor == 0x8086 && (pdev)->device == 0x3a3e)
@@ -698,24 +735,24 @@ index 11652e0bcab3..6c01b1aebf27 100644
#define IOAPIC_RANGE_START (0xfee00000)
#define IOAPIC_RANGE_END (0xfeefffff)
#define IOVA_START_ADDR (0x1000)
-@@ -148,12 +153,14 @@ int intel_iommu_enabled = 0;
+@@ -221,12 +226,14 @@ int intel_iommu_sm = IS_ENABLED(CONFIG_INTEL_IOMMU_SCALABLE_MODE_DEFAULT_ON);
+ 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;
+ static int disable_igfx_iommu;
- #define IDENTMAP_GFX 2
#define IDENTMAP_AZALIA 4
+#define IDENTMAP_IPTS 16
const struct iommu_ops intel_iommu_ops;
static const struct iommu_dirty_ops intel_dirty_ops;
-@@ -2412,6 +2419,9 @@ static int device_def_domain_type(struct device *dev)
+@@ -2401,6 +2408,9 @@ static int device_def_domain_type(struct device *dev)
- if ((iommu_identity_mapping & IDENTMAP_GFX) && IS_GFX_DEVICE(pdev))
+ if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(pdev))
return IOMMU_DOMAIN_IDENTITY;
+
+ if ((iommu_identity_mapping & IDENTMAP_IPTS) && IS_IPTS(pdev))
@@ -723,9 +760,9 @@ index 11652e0bcab3..6c01b1aebf27 100644
}
return 0;
-@@ -2719,6 +2729,9 @@ static int __init init_dmars(void)
- if (!dmar_map_gfx)
- iommu_identity_mapping |= IDENTMAP_GFX;
+@@ -2701,6 +2711,9 @@ static int __init init_dmars(void)
+ iommu_set_root_entry(iommu);
+ }
+ if (!dmar_map_ipts)
+ iommu_identity_mapping |= IDENTMAP_IPTS;
@@ -733,8 +770,8 @@ index 11652e0bcab3..6c01b1aebf27 100644
check_tylersburg_isoch();
ret = si_domain_init(hw_pass_through);
-@@ -4896,6 +4909,18 @@ static void quirk_iommu_igfx(struct pci_dev *dev)
- dmar_map_gfx = 0;
+@@ -4871,6 +4884,18 @@ static void quirk_iommu_igfx(struct pci_dev *dev)
+ disable_igfx_iommu = 1;
}
+static void quirk_iommu_ipts(struct pci_dev *dev)
@@ -752,7 +789,7 @@ index 11652e0bcab3..6c01b1aebf27 100644
/* 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);
-@@ -4931,6 +4956,10 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx);
+@@ -4906,6 +4931,10 @@ 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);
@@ -764,9 +801,9 @@ index 11652e0bcab3..6c01b1aebf27 100644
{
if (risky_device(dev))
--
-2.44.0
+2.45.1
-From fa7796bc06659b87f47d8921d0441314612870b9 Mon Sep 17 00:00:00 2001
+From 664128ab9984f6c774d4064548d9b247041d2520 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
@@ -833,7 +870,7 @@ Patchset: ipts
create mode 100644 drivers/hid/ipts/thread.h
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
-index 4c682c650704..a263e49b2ae2 100644
+index 4c682c6507040..a263e49b2ae29 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -1351,4 +1351,6 @@ source "drivers/hid/amd-sfh-hid/Kconfig"
@@ -844,7 +881,7 @@ index 4c682c650704..a263e49b2ae2 100644
+
endif # HID_SUPPORT
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
-index 082a728eac60..f4bad1b8d813 100644
+index 082a728eac600..f4bad1b8d813f 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -170,3 +170,5 @@ obj-$(INTEL_ISH_FIRMWARE_DOWNLOADER) += intel-ish-hid/
@@ -855,7 +892,7 @@ index 082a728eac60..f4bad1b8d813 100644
+obj-$(CONFIG_HID_IPTS) += ipts/
diff --git a/drivers/hid/ipts/Kconfig b/drivers/hid/ipts/Kconfig
new file mode 100644
-index 000000000000..297401bd388d
+index 0000000000000..297401bd388dd
--- /dev/null
+++ b/drivers/hid/ipts/Kconfig
@@ -0,0 +1,14 @@
@@ -875,7 +912,7 @@ index 000000000000..297401bd388d
+ module will be called ipts.
diff --git a/drivers/hid/ipts/Makefile b/drivers/hid/ipts/Makefile
new file mode 100644
-index 000000000000..883896f68e6a
+index 0000000000000..883896f68e6ad
--- /dev/null
+++ b/drivers/hid/ipts/Makefile
@@ -0,0 +1,16 @@
@@ -897,7 +934,7 @@ index 000000000000..883896f68e6a
+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
+index 0000000000000..63a4934bbc5fa
--- /dev/null
+++ b/drivers/hid/ipts/cmd.c
@@ -0,0 +1,61 @@
@@ -964,7 +1001,7 @@ index 000000000000..63a4934bbc5f
+}
diff --git a/drivers/hid/ipts/cmd.h b/drivers/hid/ipts/cmd.h
new file mode 100644
-index 000000000000..2b4079075b64
+index 0000000000000..2b4079075b642
--- /dev/null
+++ b/drivers/hid/ipts/cmd.h
@@ -0,0 +1,60 @@
@@ -1030,7 +1067,7 @@ index 000000000000..2b4079075b64
+#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
+index 0000000000000..ba33259f1f7c5
--- /dev/null
+++ b/drivers/hid/ipts/context.h
@@ -0,0 +1,52 @@
@@ -1088,7 +1125,7 @@ index 000000000000..ba33259f1f7c
+#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
+index 0000000000000..5360842d260ba
--- /dev/null
+++ b/drivers/hid/ipts/control.c
@@ -0,0 +1,486 @@
@@ -1580,7 +1617,7 @@ index 000000000000..5360842d260b
+}
diff --git a/drivers/hid/ipts/control.h b/drivers/hid/ipts/control.h
new file mode 100644
-index 000000000000..26629c5144ed
+index 0000000000000..26629c5144edb
--- /dev/null
+++ b/drivers/hid/ipts/control.h
@@ -0,0 +1,126 @@
@@ -1712,7 +1749,7 @@ index 000000000000..26629c5144ed
+#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
+index 0000000000000..307438c7c80cd
--- /dev/null
+++ b/drivers/hid/ipts/desc.h
@@ -0,0 +1,80 @@
@@ -1798,7 +1835,7 @@ index 000000000000..307438c7c80c
+#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
+index 0000000000000..ecbb3a8bdaf60
--- /dev/null
+++ b/drivers/hid/ipts/eds1.c
@@ -0,0 +1,103 @@
@@ -1907,7 +1944,7 @@ index 000000000000..ecbb3a8bdaf6
+}
diff --git a/drivers/hid/ipts/eds1.h b/drivers/hid/ipts/eds1.h
new file mode 100644
-index 000000000000..eeeb6575e3e8
+index 0000000000000..eeeb6575e3e89
--- /dev/null
+++ b/drivers/hid/ipts/eds1.h
@@ -0,0 +1,35 @@
@@ -1948,7 +1985,7 @@ index 000000000000..eeeb6575e3e8
+ 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
+index 0000000000000..198dc65d78876
--- /dev/null
+++ b/drivers/hid/ipts/eds2.c
@@ -0,0 +1,144 @@
@@ -2098,7 +2135,7 @@ index 000000000000..198dc65d7887
+}
diff --git a/drivers/hid/ipts/eds2.h b/drivers/hid/ipts/eds2.h
new file mode 100644
-index 000000000000..064e3716907a
+index 0000000000000..064e3716907ab
--- /dev/null
+++ b/drivers/hid/ipts/eds2.h
@@ -0,0 +1,35 @@
@@ -2139,7 +2176,7 @@ index 000000000000..064e3716907a
+ 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
+index 0000000000000..e34a1a4f9fa77
--- /dev/null
+++ b/drivers/hid/ipts/hid.c
@@ -0,0 +1,225 @@
@@ -2370,7 +2407,7 @@ index 000000000000..e34a1a4f9fa7
+}
diff --git a/drivers/hid/ipts/hid.h b/drivers/hid/ipts/hid.h
new file mode 100644
-index 000000000000..1ebe77447903
+index 0000000000000..1ebe77447903a
--- /dev/null
+++ b/drivers/hid/ipts/hid.h
@@ -0,0 +1,24 @@
@@ -2400,7 +2437,7 @@ index 000000000000..1ebe77447903
+#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
+index 0000000000000..fb5b5c13ee3ea
--- /dev/null
+++ b/drivers/hid/ipts/main.c
@@ -0,0 +1,126 @@
@@ -2532,7 +2569,7 @@ index 000000000000..fb5b5c13ee3e
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/ipts/mei.c b/drivers/hid/ipts/mei.c
new file mode 100644
-index 000000000000..1e0395ceae4a
+index 0000000000000..1e0395ceae4a4
--- /dev/null
+++ b/drivers/hid/ipts/mei.c
@@ -0,0 +1,188 @@
@@ -2726,7 +2763,7 @@ index 000000000000..1e0395ceae4a
+}
diff --git a/drivers/hid/ipts/mei.h b/drivers/hid/ipts/mei.h
new file mode 100644
-index 000000000000..973bade6b0fd
+index 0000000000000..973bade6b0fdd
--- /dev/null
+++ b/drivers/hid/ipts/mei.h
@@ -0,0 +1,66 @@
@@ -2798,7 +2835,7 @@ index 000000000000..973bade6b0fd
+#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
+index 0000000000000..ef66c3c9db807
--- /dev/null
+++ b/drivers/hid/ipts/receiver.c
@@ -0,0 +1,250 @@
@@ -3054,7 +3091,7 @@ index 000000000000..ef66c3c9db80
+}
diff --git a/drivers/hid/ipts/receiver.h b/drivers/hid/ipts/receiver.h
new file mode 100644
-index 000000000000..3de7da62d40c
+index 0000000000000..3de7da62d40c1
--- /dev/null
+++ b/drivers/hid/ipts/receiver.h
@@ -0,0 +1,16 @@
@@ -3076,7 +3113,7 @@ index 000000000000..3de7da62d40c
+#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
+index 0000000000000..cc14653b2a9f5
--- /dev/null
+++ b/drivers/hid/ipts/resources.c
@@ -0,0 +1,131 @@
@@ -3213,7 +3250,7 @@ index 000000000000..cc14653b2a9f
+}
diff --git a/drivers/hid/ipts/resources.h b/drivers/hid/ipts/resources.h
new file mode 100644
-index 000000000000..2068e13285f0
+index 0000000000000..2068e13285f0e
--- /dev/null
+++ b/drivers/hid/ipts/resources.h
@@ -0,0 +1,41 @@
@@ -3260,7 +3297,7 @@ index 000000000000..2068e13285f0
+#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
+index 0000000000000..e8dd98895a7ee
--- /dev/null
+++ b/drivers/hid/ipts/spec-data.h
@@ -0,0 +1,100 @@
@@ -3366,7 +3403,7 @@ index 000000000000..e8dd98895a7e
+#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
+index 0000000000000..41845f9d90257
--- /dev/null
+++ b/drivers/hid/ipts/spec-device.h
@@ -0,0 +1,290 @@
@@ -3662,7 +3699,7 @@ index 000000000000..41845f9d9025
+#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
+index 0000000000000..5a58d4a0a610f
--- /dev/null
+++ b/drivers/hid/ipts/spec-hid.h
@@ -0,0 +1,34 @@
@@ -3702,7 +3739,7 @@ index 000000000000..5a58d4a0a610
+#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
+index 0000000000000..355e92bea26f8
--- /dev/null
+++ b/drivers/hid/ipts/thread.c
@@ -0,0 +1,84 @@
@@ -3792,7 +3829,7 @@ index 000000000000..355e92bea26f
+}
diff --git a/drivers/hid/ipts/thread.h b/drivers/hid/ipts/thread.h
new file mode 100644
-index 000000000000..1f966b8b32c4
+index 0000000000000..1f966b8b32c45
--- /dev/null
+++ b/drivers/hid/ipts/thread.h
@@ -0,0 +1,59 @@
@@ -3856,9 +3893,62 @@ index 000000000000..1f966b8b32c4
+
+#endif /* IPTS_THREAD_H */
--
-2.44.0
+2.45.1
+
+From ee8823ff409dc4507cc2f7c8f8c474735b005938 Mon Sep 17 00:00:00 2001
+From: Jasmin Huber <jasmin@jasisonee.ch>
+Date: Mon, 15 Apr 2024 10:22:55 +0200
+Subject: [PATCH] Inlude headers to avoid compiler warnings 6.8 kernels compile
+ with -Wmissing-prototypes.
+
+Signed-off-by: Dorian Stoll <dorian.stoll@tmsp.io>
+Patchset: ipts
+---
+ drivers/hid/ipts/eds1.c | 1 +
+ drivers/hid/ipts/eds2.c | 1 +
+ drivers/hid/ipts/receiver.c | 1 +
+ 3 files changed, 3 insertions(+)
+
+diff --git a/drivers/hid/ipts/eds1.c b/drivers/hid/ipts/eds1.c
+index ecbb3a8bdaf60..7b9f54388a9f6 100644
+--- a/drivers/hid/ipts/eds1.c
++++ b/drivers/hid/ipts/eds1.c
+@@ -14,6 +14,7 @@
+ #include "context.h"
+ #include "control.h"
+ #include "desc.h"
++#include "eds1.h"
+ #include "spec-device.h"
+
+ int ipts_eds1_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size)
+diff --git a/drivers/hid/ipts/eds2.c b/drivers/hid/ipts/eds2.c
+index 198dc65d78876..639940794615d 100644
+--- a/drivers/hid/ipts/eds2.c
++++ b/drivers/hid/ipts/eds2.c
+@@ -15,6 +15,7 @@
+ #include "context.h"
+ #include "control.h"
+ #include "desc.h"
++#include "eds2.h"
+ #include "spec-data.h"
+
+ int ipts_eds2_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size)
+diff --git a/drivers/hid/ipts/receiver.c b/drivers/hid/ipts/receiver.c
+index ef66c3c9db807..977724c728c3e 100644
+--- a/drivers/hid/ipts/receiver.c
++++ b/drivers/hid/ipts/receiver.c
+@@ -16,6 +16,7 @@
+ #include "context.h"
+ #include "control.h"
+ #include "hid.h"
++#include "receiver.h"
+ #include "resources.h"
+ #include "spec-device.h"
+ #include "thread.h"
+--
+2.45.1
-From 619b488e58367467f52d636b5182ff2134df68c0 Mon Sep 17 00:00:00 2001
+From 4b17942da35790b0e703a87267545f5a9f08e1cf 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
@@ -3870,7 +3960,7 @@ Patchset: ithc
1 file changed, 16 insertions(+)
diff --git a/drivers/iommu/intel/irq_remapping.c b/drivers/iommu/intel/irq_remapping.c
-index 566297bc87dd..a8cd8f12d593 100644
+index 566297bc87ddb..a8cd8f12d5937 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)
@@ -3897,9 +3987,9 @@ index 566297bc87dd..a8cd8f12d593 100644
* 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.44.0
+2.45.1
-From ee5106b4069dd150a323dce8bce7879d2b27ed0b Mon Sep 17 00:00:00 2001
+From 14baff1c79b6499868b48e6fb4ada851db35c941 Mon Sep 17 00:00:00 2001
From: quo <tuple@list.ru>
Date: Sun, 11 Dec 2022 12:10:54 +0100
Subject: [PATCH] hid: Add support for Intel Touch Host Controller
@@ -3932,7 +4022,7 @@ Patchset: ithc
create mode 100644 drivers/hid/ithc/ithc.h
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
-index a263e49b2ae2..03f0f5af289a 100644
+index a263e49b2ae29..03f0f5af289a4 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -1353,4 +1353,6 @@ source "drivers/hid/surface-hid/Kconfig"
@@ -3943,7 +4033,7 @@ index a263e49b2ae2..03f0f5af289a 100644
+
endif # HID_SUPPORT
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
-index f4bad1b8d813..d32c194400ae 100644
+index f4bad1b8d813f..d32c194400aea 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -172,3 +172,4 @@ obj-$(CONFIG_AMD_SFH_HID) += amd-sfh-hid/
@@ -3953,7 +4043,7 @@ index f4bad1b8d813..d32c194400ae 100644
+obj-$(CONFIG_HID_ITHC) += ithc/
diff --git a/drivers/hid/ithc/Kbuild b/drivers/hid/ithc/Kbuild
new file mode 100644
-index 000000000000..aea83f2ac07b
+index 0000000000000..aea83f2ac07b4
--- /dev/null
+++ b/drivers/hid/ithc/Kbuild
@@ -0,0 +1,6 @@
@@ -3965,7 +4055,7 @@ index 000000000000..aea83f2ac07b
+
diff --git a/drivers/hid/ithc/Kconfig b/drivers/hid/ithc/Kconfig
new file mode 100644
-index 000000000000..ede713023609
+index 0000000000000..ede7130236096
--- /dev/null
+++ b/drivers/hid/ithc/Kconfig
@@ -0,0 +1,12 @@
@@ -3983,7 +4073,7 @@ index 000000000000..ede713023609
+ 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..1f1f1e33f2e5
+index 0000000000000..1f1f1e33f2e5a
--- /dev/null
+++ b/drivers/hid/ithc/ithc-debug.c
@@ -0,0 +1,130 @@
@@ -4119,7 +4209,7 @@ index 000000000000..1f1f1e33f2e5
+
diff --git a/drivers/hid/ithc/ithc-dma.c b/drivers/hid/ithc/ithc-dma.c
new file mode 100644
-index 000000000000..ffb8689b8a78
+index 0000000000000..ffb8689b8a780
--- /dev/null
+++ b/drivers/hid/ithc/ithc-dma.c
@@ -0,0 +1,373 @@
@@ -4498,7 +4588,7 @@ index 000000000000..ffb8689b8a78
+
diff --git a/drivers/hid/ithc/ithc-dma.h b/drivers/hid/ithc/ithc-dma.h
new file mode 100644
-index 000000000000..93652e4476bf
+index 0000000000000..93652e4476bf8
--- /dev/null
+++ b/drivers/hid/ithc/ithc-dma.h
@@ -0,0 +1,69 @@
@@ -4573,7 +4663,7 @@ index 000000000000..93652e4476bf
+
diff --git a/drivers/hid/ithc/ithc-main.c b/drivers/hid/ithc/ithc-main.c
new file mode 100644
-index 000000000000..87ed4aa70fda
+index 0000000000000..87ed4aa70fda0
--- /dev/null
+++ b/drivers/hid/ithc/ithc-main.c
@@ -0,0 +1,728 @@
@@ -5307,7 +5397,7 @@ index 000000000000..87ed4aa70fda
+
diff --git a/drivers/hid/ithc/ithc-regs.c b/drivers/hid/ithc/ithc-regs.c
new file mode 100644
-index 000000000000..e058721886e3
+index 0000000000000..e058721886e37
--- /dev/null
+++ b/drivers/hid/ithc/ithc-regs.c
@@ -0,0 +1,96 @@
@@ -5409,7 +5499,7 @@ index 000000000000..e058721886e3
+
diff --git a/drivers/hid/ithc/ithc-regs.h b/drivers/hid/ithc/ithc-regs.h
new file mode 100644
-index 000000000000..d4007d9e2bac
+index 0000000000000..d4007d9e2bacc
--- /dev/null
+++ b/drivers/hid/ithc/ithc-regs.h
@@ -0,0 +1,189 @@
@@ -5604,7 +5694,7 @@ index 000000000000..d4007d9e2bac
+
diff --git a/drivers/hid/ithc/ithc.h b/drivers/hid/ithc/ithc.h
new file mode 100644
-index 000000000000..028e55a4ec53
+index 0000000000000..028e55a4ec53e
--- /dev/null
+++ b/drivers/hid/ithc/ithc.h
@@ -0,0 +1,67 @@
@@ -5676,9 +5766,2801 @@ index 000000000000..028e55a4ec53
+void ithc_log_regs(struct ithc *ithc);
+
--
-2.44.0
+2.45.1
+
+From 2537922b2f0c1d0002a2afe6d0fc79695add8e60 Mon Sep 17 00:00:00 2001
+From: quo <tuple@list.ru>
+Date: Fri, 19 Apr 2024 22:11:09 +0200
+Subject: [PATCH] hid: ithc: Update from quo/ithc-linux
+
+ - Added QuickSPI support for Surface Laptop Studio 2
+ - Use Latency Tolerance Reporting instead of manual CPU latency adjustments
+
+Based on: https://github.com/quo/ithc-linux/commit/18afc6ffacd70b49fdee2eb1ab0a8acd159edb31
+
+Signed-off-by: Dorian Stoll <dorian.stoll@tmsp.io>
+Patchset: ithc
+---
+ drivers/hid/ithc/Kbuild | 2 +-
+ drivers/hid/ithc/ithc-debug.c | 33 +-
+ drivers/hid/ithc/ithc-debug.h | 7 +
+ drivers/hid/ithc/ithc-dma.c | 125 ++-----
+ drivers/hid/ithc/ithc-dma.h | 24 +-
+ drivers/hid/ithc/ithc-hid.c | 207 +++++++++++
+ drivers/hid/ithc/ithc-hid.h | 32 ++
+ drivers/hid/ithc/ithc-legacy.c | 252 ++++++++++++++
+ drivers/hid/ithc/ithc-legacy.h | 8 +
+ drivers/hid/ithc/ithc-main.c | 386 ++++-----------------
+ drivers/hid/ithc/ithc-quickspi.c | 578 +++++++++++++++++++++++++++++++
+ drivers/hid/ithc/ithc-quickspi.h | 39 +++
+ drivers/hid/ithc/ithc-regs.c | 72 +++-
+ drivers/hid/ithc/ithc-regs.h | 143 ++++----
+ drivers/hid/ithc/ithc.h | 71 ++--
+ 15 files changed, 1441 insertions(+), 538 deletions(-)
+ create mode 100644 drivers/hid/ithc/ithc-debug.h
+ create mode 100644 drivers/hid/ithc/ithc-hid.c
+ create mode 100644 drivers/hid/ithc/ithc-hid.h
+ create mode 100644 drivers/hid/ithc/ithc-legacy.c
+ create mode 100644 drivers/hid/ithc/ithc-legacy.h
+ create mode 100644 drivers/hid/ithc/ithc-quickspi.c
+ create mode 100644 drivers/hid/ithc/ithc-quickspi.h
-From 973bf943acca4abdd932b5fdff032de0af07f96e Mon Sep 17 00:00:00 2001
+diff --git a/drivers/hid/ithc/Kbuild b/drivers/hid/ithc/Kbuild
+index aea83f2ac07b4..4937ba1312973 100644
+--- a/drivers/hid/ithc/Kbuild
++++ b/drivers/hid/ithc/Kbuild
+@@ -1,6 +1,6 @@
+ obj-$(CONFIG_HID_ITHC) := ithc.o
+
+-ithc-objs := ithc-main.o ithc-regs.o ithc-dma.o ithc-debug.o
++ithc-objs := ithc-main.o ithc-regs.o ithc-dma.o ithc-hid.o ithc-legacy.o ithc-quickspi.o ithc-debug.o
+
+ ccflags-y := -std=gnu11 -Wno-declaration-after-statement
+
+diff --git a/drivers/hid/ithc/ithc-debug.c b/drivers/hid/ithc/ithc-debug.c
+index 1f1f1e33f2e5a..2d8c6afe99663 100644
+--- a/drivers/hid/ithc/ithc-debug.c
++++ b/drivers/hid/ithc/ithc-debug.c
+@@ -85,10 +85,11 @@ static ssize_t ithc_debugfs_cmd_write(struct file *f, const char __user *buf, si
+ 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)
++ if (n < 1)
+ 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_info(ithc->pci, "debug dma command with %u bytes of data\n", n * 4);
++ struct ithc_data data = { .type = ITHC_DATA_RAW, .size = n * 4, .data = a };
++ if (ithc_dma_tx(ithc, &data))
+ pci_err(ithc->pci, "dma tx failed\n");
+ break;
+ default:
+@@ -98,6 +99,23 @@ static ssize_t ithc_debugfs_cmd_write(struct file *f, const char __user *buf, si
+ return len;
+ }
+
++static struct dentry *dbg_dir;
++
++void __init ithc_debug_init_module(void)
++{
++ struct dentry *d = debugfs_create_dir(DEVNAME, NULL);
++ if (IS_ERR(d))
++ pr_warn("failed to create debugfs dir (%li)\n", PTR_ERR(d));
++ else
++ dbg_dir = d;
++}
++
++void __exit ithc_debug_exit_module(void)
++{
++ debugfs_remove_recursive(dbg_dir);
++ dbg_dir = NULL;
++}
++
+ static const struct file_operations ithc_debugfops_cmd = {
+ .owner = THIS_MODULE,
+ .write = ithc_debugfs_cmd_write,
+@@ -106,17 +124,18 @@ static const struct file_operations ithc_debugfops_cmd = {
+ static void ithc_debugfs_devres_release(struct device *dev, void *res)
+ {
+ struct dentry **dbgm = res;
+- if (*dbgm)
+- debugfs_remove_recursive(*dbgm);
++ debugfs_remove_recursive(*dbgm);
+ }
+
+-int ithc_debug_init(struct ithc *ithc)
++int ithc_debug_init_device(struct ithc *ithc)
+ {
++ if (!dbg_dir)
++ return -ENOENT;
+ 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);
++ struct dentry *dbg = debugfs_create_dir(pci_name(ithc->pci), dbg_dir);
+ if (IS_ERR(dbg))
+ return PTR_ERR(dbg);
+ *dbgm = dbg;
+diff --git a/drivers/hid/ithc/ithc-debug.h b/drivers/hid/ithc/ithc-debug.h
+new file mode 100644
+index 0000000000000..38c53d916bdb5
+--- /dev/null
++++ b/drivers/hid/ithc/ithc-debug.h
+@@ -0,0 +1,7 @@
++/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
++
++void ithc_debug_init_module(void);
++void ithc_debug_exit_module(void);
++int ithc_debug_init_device(struct ithc *ithc);
++void ithc_log_regs(struct ithc *ithc);
++
+diff --git a/drivers/hid/ithc/ithc-dma.c b/drivers/hid/ithc/ithc-dma.c
+index ffb8689b8a780..bf4eab33062b0 100644
+--- a/drivers/hid/ithc/ithc-dma.c
++++ b/drivers/hid/ithc/ithc-dma.c
+@@ -173,10 +173,9 @@ int ithc_dma_rx_init(struct ithc *ithc, u8 channel)
+ mutex_init(&rx->mutex);
+
+ // Allocate buffers.
+- u32 buf_size = DEVCFG_DMA_RX_SIZE(ithc->config.dma_buf_sizes);
+- unsigned int num_pages = (buf_size + PAGE_SIZE - 1) / PAGE_SIZE;
++ unsigned int num_pages = (ithc->max_rx_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);
++ NUM_RX_BUF, ithc->max_rx_size, num_pages);
+ CHECK_RET(ithc_dma_prd_alloc, ithc, &rx->prds, NUM_RX_BUF, num_pages, DMA_FROM_DEVICE);
+ for (unsigned int i = 0; i < NUM_RX_BUF; i++)
+ CHECK_RET(ithc_dma_data_alloc, ithc, &rx->prds, &rx->bufs[i]);
+@@ -214,10 +213,9 @@ int ithc_dma_tx_init(struct ithc *ithc)
+ mutex_init(&tx->mutex);
+
+ // Allocate buffers.
+- tx->max_size = DEVCFG_DMA_TX_SIZE(ithc->config.dma_buf_sizes);
+- unsigned int num_pages = (tx->max_size + PAGE_SIZE - 1) / PAGE_SIZE;
++ unsigned int num_pages = (ithc->max_tx_size + PAGE_SIZE - 1) / PAGE_SIZE;
+ pci_dbg(ithc->pci, "allocating tx buffers: size = %u, pages = %u\n",
+- tx->max_size, num_pages);
++ ithc->max_tx_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);
+
+@@ -230,71 +228,6 @@ int ithc_dma_tx_init(struct ithc *ithc)
+ 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;
+- }
+- 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) {
+- // The THC sends a reset request when we need to reinitialize the device.
+- // This usually only happens if we send an invalid command or put the device
+- // in a bad state.
+- 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. For Surface devices, this will typically be a single touch
+- // 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) {
+- // Response to a 'get report descriptor' request.
+- // The actual descriptor is preceded by 8 nul bytes.
+- 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) {
+- // Standard HID input report containing touch data.
+- CHECK(hid_input_report, ithc->hid, HID_INPUT_REPORT, hiddata, hdr->data_size, 1);
+- } else if (hdr->code == DMA_RX_CODE_FEATURE_REPORT) {
+- // Response to a 'get feature' request.
+- 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 {
+- // Received data without a matching request, or the request already
+- // timed out. (XXX What's the correct thing to do here?)
+- 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)
+ {
+ // Process all filled RX buffers from the ringbuffer.
+@@ -316,7 +249,16 @@ static int ithc_dma_rx_unlocked(struct ithc *ithc, u8 channel)
+ rx->num_received = ++n;
+
+ // process data
+- CHECK(ithc_dma_rx_process_buf, ithc, b, channel, tail);
++ struct ithc_data d;
++ if ((ithc->use_quickspi ? ithc_quickspi_decode_rx : ithc_legacy_decode_rx)
++ (ithc, b->addr, b->data_size, &d) < 0) {
++ pci_err(ithc->pci, "invalid dma rx data! channel %u, buffer %u, size %u: %*ph\n",
++ channel, tail, b->data_size, min((int)b->data_size, 64), b->addr);
++ print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1,
++ b->addr, min(b->data_size, 0x400u), 0);
++ } else {
++ ithc_hid_process_data(ithc, &d);
++ }
+
+ // give the buffer back to the device
+ CHECK_RET(ithc_dma_data_buffer_put, ithc, &rx->prds, b, tail);
+@@ -331,31 +273,28 @@ int ithc_dma_rx(struct ithc *ithc, u8 channel)
+ return ret;
+ }
+
+-static int ithc_dma_tx_unlocked(struct ithc *ithc, u32 cmdcode, u32 datasize, void *data)
++static int ithc_dma_tx_unlocked(struct ithc *ithc, const struct ithc_data *data)
+ {
+- ithc_set_active(ithc, 100 * USEC_PER_MSEC);
+-
+ // Send a single TX buffer to the THC.
+- pci_dbg(ithc->pci, "dma tx command %u, size %u\n", cmdcode, datasize);
+- struct ithc_dma_tx_header *hdr;
+- // Data must be padded to next 4-byte boundary.
+- u8 padding = datasize & 3 ? 4 - (datasize & 3) : 0;
+- unsigned int fullsize = sizeof(*hdr) + datasize + padding;
+- if (fullsize > ithc->dma_tx.max_size || fullsize > PAGE_SIZE)
+- return -EINVAL;
++ pci_dbg(ithc->pci, "dma tx data type %u, size %u\n", data->type, data->size);
+ CHECK_RET(ithc_dma_data_buffer_get, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0);
+
+ // Fill the TX buffer with header and data.
+- 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;
++ ssize_t sz;
++ if (data->type == ITHC_DATA_RAW) {
++ sz = min(data->size, ithc->max_tx_size);
++ memcpy(ithc->dma_tx.buf.addr, data->data, sz);
++ } else {
++ sz = (ithc->use_quickspi ? ithc_quickspi_encode_tx : ithc_legacy_encode_tx)
++ (ithc, data, ithc->dma_tx.buf.addr, ithc->max_tx_size);
++ }
++ ithc->dma_tx.buf.data_size = sz < 0 ? 0 : sz;
+ CHECK_RET(ithc_dma_data_buffer_put, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0);
++ if (sz < 0) {
++ pci_err(ithc->pci, "failed to encode tx data type %i, size %u, error %i\n",
++ data->type, data->size, (int)sz);
++ return -EINVAL;
++ }
+
+ // Let the THC process the buffer.
+ bitsb_set(&ithc->regs->dma_tx.control, DMA_TX_CONTROL_SEND);
+@@ -363,10 +302,10 @@ static int ithc_dma_tx_unlocked(struct ithc *ithc, u32 cmdcode, u32 datasize, vo
+ 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)
++int ithc_dma_tx(struct ithc *ithc, const struct ithc_data *data)
+ {
+ mutex_lock(&ithc->dma_tx.mutex);
+- int ret = ithc_dma_tx_unlocked(ithc, cmdcode, datasize, data);
++ int ret = ithc_dma_tx_unlocked(ithc, 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
+index 93652e4476bf8..1749a5819b3e7 100644
+--- a/drivers/hid/ithc/ithc-dma.h
++++ b/drivers/hid/ithc/ithc-dma.h
+@@ -11,27 +11,6 @@ struct ithc_phys_region_desc {
+ 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;
+@@ -49,7 +28,6 @@ struct ithc_dma_data_buffer {
+
+ struct ithc_dma_tx {
+ struct mutex mutex;
+- u32 max_size;
+ struct ithc_dma_prd_buffer prds;
+ struct ithc_dma_data_buffer buf;
+ };
+@@ -65,5 +43,5 @@ int ithc_dma_rx_init(struct ithc *ithc, u8 channel);
+ 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);
++int ithc_dma_tx(struct ithc *ithc, const struct ithc_data *data);
+
+diff --git a/drivers/hid/ithc/ithc-hid.c b/drivers/hid/ithc/ithc-hid.c
+new file mode 100644
+index 0000000000000..065646ab499ef
+--- /dev/null
++++ b/drivers/hid/ithc/ithc-hid.c
+@@ -0,0 +1,207 @@
++// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
++
++#include "ithc.h"
++
++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;
++ const struct ithc_data get_report_desc = { .type = ITHC_DATA_REPORT_DESCRIPTOR };
++ WRITE_ONCE(ithc->hid.parse_done, false);
++ for (int retries = 0; ; retries++) {
++ ithc_log_regs(ithc);
++ CHECK_RET(ithc_dma_tx, ithc, &get_report_desc);
++ if (wait_event_timeout(ithc->hid.wait_parse, READ_ONCE(ithc->hid.parse_done),
++ msecs_to_jiffies(200))) {
++ ithc_log_regs(ithc);
++ return 0;
++ }
++ if (retries > 5) {
++ ithc_log_regs(ithc);
++ pci_err(ithc->pci, "failed to read report descriptor\n");
++ return -ETIMEDOUT;
++ }
++ pci_warn(ithc->pci, "failed to read report descriptor, retrying\n");
++ }
++}
++
++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;
++
++ struct ithc_data d = { .size = len, .data = buf };
++ buf[0] = reportnum;
++
++ if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT) {
++ d.type = ITHC_DATA_OUTPUT_REPORT;
++ CHECK_RET(ithc_dma_tx, ithc, &d);
++ return 0;
++ }
++
++ if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_SET_REPORT) {
++ d.type = ITHC_DATA_SET_FEATURE;
++ CHECK_RET(ithc_dma_tx, ithc, &d);
++ return 0;
++ }
++
++ if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_GET_REPORT) {
++ d.type = ITHC_DATA_GET_FEATURE;
++ d.data = &reportnum;
++ d.size = 1;
++
++ // Prepare for response.
++ 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);
++
++ // Transmit 'get feature' request.
++ int r = CHECK(ithc_dma_tx, ithc, &d);
++ if (!r) {
++ r = wait_event_interruptible_timeout(ithc->hid.wait_get_feature,
++ !ithc->hid.get_feature_buf, msecs_to_jiffies(1000));
++ if (!r)
++ r = -ETIMEDOUT;
++ else if (r < 0)
++ r = -EINTR;
++ else
++ r = 0;
++ }
++
++ // If everything went ok, the buffer has been filled with the response data.
++ // Return the response size.
++ 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;
++ }
++
++ pci_err(ithc->pci, "unhandled hid request %i %i for report id %i\n",
++ rtype, reqtype, reportnum);
++ return -EINVAL;
++}
++
++// FIXME hid_input_report()/hid_parse_report() currently don't take const buffers, so we have to
++// cast away the const to avoid a compiler warning...
++#define NOCONST(x) ((void *)x)
++
++void ithc_hid_process_data(struct ithc *ithc, struct ithc_data *d)
++{
++ WARN_ON(!ithc->hid.dev);
++ if (!ithc->hid.dev)
++ return;
++
++ switch (d->type) {
++
++ case ITHC_DATA_IGNORE:
++ return;
++
++ case ITHC_DATA_ERROR:
++ CHECK(ithc_reset, ithc);
++ return;
++
++ case ITHC_DATA_REPORT_DESCRIPTOR:
++ // Response to the report descriptor request sent by ithc_hid_parse().
++ CHECK(hid_parse_report, ithc->hid.dev, NOCONST(d->data), d->size);
++ WRITE_ONCE(ithc->hid.parse_done, true);
++ wake_up(&ithc->hid.wait_parse);
++ return;
++
++ case ITHC_DATA_INPUT_REPORT:
++ {
++ // Standard HID input report.
++ int r = hid_input_report(ithc->hid.dev, HID_INPUT_REPORT, NOCONST(d->data), d->size, 1);
++ if (r < 0) {
++ pci_warn(ithc->pci, "hid_input_report failed with %i (size %u, report ID 0x%02x)\n",
++ r, d->size, d->size ? *(u8 *)d->data : 0);
++ print_hex_dump_debug(DEVNAME " report: ", DUMP_PREFIX_OFFSET, 32, 1,
++ d->data, min(d->size, 0x400u), 0);
++ }
++ return;
++ }
++
++ case ITHC_DATA_GET_FEATURE:
++ {
++ // Response to a 'get feature' request sent by ithc_hid_raw_request().
++ bool done = false;
++ mutex_lock(&ithc->hid.get_feature_mutex);
++ if (ithc->hid.get_feature_buf) {
++ if (d->size < ithc->hid.get_feature_size)
++ ithc->hid.get_feature_size = d->size;
++ memcpy(ithc->hid.get_feature_buf, d->data, 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->hid.wait_get_feature);
++ } else {
++ // Received data without a matching request, or the request already
++ // timed out. (XXX What's the correct thing to do here?)
++ CHECK(hid_input_report, ithc->hid.dev, HID_FEATURE_REPORT,
++ NOCONST(d->data), d->size, 1);
++ }
++ return;
++ }
++
++ default:
++ pci_err(ithc->pci, "unhandled data type %i\n", d->type);
++ return;
++ }
++}
++
++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);
++}
++
++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->vendor_id;
++ hid->product = ithc->product_id;
++ hid->version = 0x100;
++ hid->dev.parent = &ithc->pci->dev;
++ hid->driver_data = ithc;
++
++ ithc->hid.dev = hid;
++
++ init_waitqueue_head(&ithc->hid.wait_parse);
++ init_waitqueue_head(&ithc->hid.wait_get_feature);
++ mutex_init(&ithc->hid.get_feature_mutex);
++
++ return 0;
++}
++
+diff --git a/drivers/hid/ithc/ithc-hid.h b/drivers/hid/ithc/ithc-hid.h
+new file mode 100644
+index 0000000000000..599eb912c8c84
+--- /dev/null
++++ b/drivers/hid/ithc/ithc-hid.h
+@@ -0,0 +1,32 @@
++/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
++
++enum ithc_data_type {
++ ITHC_DATA_IGNORE,
++ ITHC_DATA_RAW,
++ ITHC_DATA_ERROR,
++ ITHC_DATA_REPORT_DESCRIPTOR,
++ ITHC_DATA_INPUT_REPORT,
++ ITHC_DATA_OUTPUT_REPORT,
++ ITHC_DATA_GET_FEATURE,
++ ITHC_DATA_SET_FEATURE,
++};
++
++struct ithc_data {
++ enum ithc_data_type type;
++ u32 size;
++ const void *data;
++};
++
++struct ithc_hid {
++ struct hid_device *dev;
++ bool parse_done;
++ wait_queue_head_t wait_parse;
++ wait_queue_head_t wait_get_feature;
++ struct mutex get_feature_mutex;
++ void *get_feature_buf;
++ size_t get_feature_size;
++};
++
++int ithc_hid_init(struct ithc *ithc);
++void ithc_hid_process_data(struct ithc *ithc, struct ithc_data *d);
++
+diff --git a/drivers/hid/ithc/ithc-legacy.c b/drivers/hid/ithc/ithc-legacy.c
+new file mode 100644
+index 0000000000000..5c1da11e3f1d2
+--- /dev/null
++++ b/drivers/hid/ithc/ithc-legacy.c
+@@ -0,0 +1,252 @@
++// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
++
++#include "ithc.h"
++
++#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_PROP_DATA_ENABLE BIT(1)
++#define DEVCFG_TOUCH_HID_REPORT_ENABLE BIT(2)
++#define DEVCFG_TOUCH_POWER_STATE(x) (((x) & 7) << 3)
++#define DEVCFG_TOUCH_UNKNOWN_6 BIT(6)
++
++#define DEVCFG_DEVICE_ID_TIC 0x43495424 // "$TIC"
++
++#define DEVCFG_SPI_CLKDIV(x) (((x) >> 1) & 7)
++#define DEVCFG_SPI_CLKDIV_8 BIT(4)
++#define DEVCFG_SPI_SUPPORTS_SINGLE BIT(5)
++#define DEVCFG_SPI_SUPPORTS_DUAL BIT(6)
++#define DEVCFG_SPI_SUPPORTS_QUAD BIT(7)
++#define DEVCFG_SPI_MAX_TOUCH_POINTS(x) (((x) >> 8) & 0x3f)
++#define DEVCFG_SPI_MIN_RESET_TIME(x) (((x) >> 16) & 0xf)
++#define DEVCFG_SPI_NEEDS_HEARTBEAT BIT(20) // TODO implement heartbeat
++#define DEVCFG_SPI_HEARTBEAT_INTERVAL(x) (((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) (((x) >> 28) & 7) // TODO use this
++#define DEVCFG_SPI_USE_EXT_READ_CFG BIT(31) // TODO use this?
++
++struct ithc_device_config { // (Example values are from an SP7+.)
++ u32 irq_cause; // 00 = 0xe0000402 (0xe0000401 after DMA_RX_CODE_RESET)
++ u32 error; // 04 = 0x00000000
++ u32 dma_buf_sizes; // 08 = 0x000a00ff
++ u32 touch_cfg; // 0c = 0x0000001c
++ u32 touch_state; // 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 (this value looks more random on newer devices)
++ u32 command; // 28 = 0x00000000
++ u32 fw_mode; // 2c = 0x00000000 (for fw update?)
++ u32 _unknown_30; // 30 = 0x00000000
++ u8 eds_minor_ver; // 34 = 0x5e
++ u8 eds_major_ver; // 35 = 0x03
++ u8 interface_rev; // 36 = 0x04
++ u8 eu_kernel_ver; // 37 = 0x04
++ u32 _unknown_38; // 38 = 0x000001c0 (0x000001c1 after DMA_RX_CODE_RESET)
++ u32 _unknown_3c; // 3c = 0x00000002
++};
++static_assert(sizeof(struct ithc_device_config) == 64);
++
++#define RX_CODE_INPUT_REPORT 3
++#define RX_CODE_FEATURE_REPORT 4
++#define RX_CODE_REPORT_DESCRIPTOR 5
++#define RX_CODE_RESET 7
++
++#define TX_CODE_SET_FEATURE 3
++#define TX_CODE_GET_FEATURE 4
++#define TX_CODE_OUTPUT_REPORT 5
++#define TX_CODE_GET_REPORT_DESCRIPTOR 7
++
++static int ithc_set_device_enabled(struct ithc *ithc, bool enable)
++{
++ u32 x = ithc->legacy_touch_cfg =
++ (ithc->legacy_touch_cfg & ~(u32)DEVCFG_TOUCH_MASK) |
++ DEVCFG_TOUCH_HID_REPORT_ENABLE |
++ (enable ? DEVCFG_TOUCH_ENABLE | DEVCFG_TOUCH_POWER_STATE(3) : 0);
++ return ithc_spi_command(ithc, SPI_CMD_CODE_WRITE,
++ offsetof(struct ithc_device_config, touch_cfg), sizeof(x), &x);
++}
++
++int ithc_legacy_init(struct ithc *ithc)
++{
++ // Since we don't yet know which SPI config the device wants, use default speed and mode
++ // initially for reading config data.
++ CHECK(ithc_set_spi_config, ithc, 2, true, SPI_MODE_SINGLE, SPI_MODE_SINGLE);
++
++ // Setting the following bit seems to make reading the config more reliable.
++ bitsl_set(&ithc->regs->dma_rx[0].init_unknown, INIT_UNKNOWN_31);
++
++ // Setting this bit may be necessary on some ADL devices.
++ switch (ithc->pci->device) {
++ case PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT1:
++ case PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT2:
++ case PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT1:
++ case PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT2:
++ bitsl_set(&ithc->regs->dma_rx[0].init_unknown, INIT_UNKNOWN_5);
++ break;
++ }
++
++ // Take the touch device out of reset.
++ 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->irq_cause, 0xf, 2))
++ break;
++ if (retries > 5) {
++ pci_err(ithc->pci, "failed to reset device, irq_cause = 0x%08x\n",
++ readl(&ithc->regs->irq_cause));
++ return -ETIMEDOUT;
++ }
++ pci_warn(ithc->pci, "invalid irq_cause, 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_READY, DMA_RX_STATUS_READY);
++
++ // Read configuration data.
++ u32 spi_cfg;
++ for (int retries = 0; ; retries++) {
++ ithc_log_regs(ithc);
++ struct ithc_device_config config = { 0 };
++ CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, 0, sizeof(config), &config);
++ u32 *p = (void *)&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 (config.device_id == DEVCFG_DEVICE_ID_TIC) {
++ spi_cfg = config.spi_config;
++ ithc->vendor_id = config.vendor_id;
++ ithc->product_id = config.product_id;
++ ithc->product_rev = config.revision;
++ ithc->max_rx_size = DEVCFG_DMA_RX_SIZE(config.dma_buf_sizes);
++ ithc->max_tx_size = DEVCFG_DMA_TX_SIZE(config.dma_buf_sizes);
++ ithc->legacy_touch_cfg = config.touch_cfg;
++ ithc->have_config = true;
++ break;
++ }
++ if (retries > 10) {
++ pci_err(ithc->pci, "failed to read config, unknown device ID 0x%08x\n",
++ config.device_id);
++ return -EIO;
++ }
++ pci_warn(ithc->pci, "failed to read config, retrying\n");
++ if (msleep_interruptible(100))
++ return -EINTR;
++ }
++ ithc_log_regs(ithc);
++
++ // Apply SPI config and enable touch device.
++ CHECK_RET(ithc_set_spi_config, ithc,
++ DEVCFG_SPI_CLKDIV(spi_cfg), (spi_cfg & DEVCFG_SPI_CLKDIV_8) != 0,
++ spi_cfg & DEVCFG_SPI_SUPPORTS_QUAD ? SPI_MODE_QUAD :
++ spi_cfg & DEVCFG_SPI_SUPPORTS_DUAL ? SPI_MODE_DUAL :
++ SPI_MODE_SINGLE,
++ SPI_MODE_SINGLE);
++ CHECK_RET(ithc_set_device_enabled, ithc, true);
++ ithc_log_regs(ithc);
++ return 0;
++}
++
++void ithc_legacy_exit(struct ithc *ithc)
++{
++ CHECK(ithc_set_device_enabled, ithc, false);
++}
++
++int ithc_legacy_decode_rx(struct ithc *ithc, const void *src, size_t len, struct ithc_data *dest)
++{
++ const struct {
++ u32 code;
++ u32 data_size;
++ u32 _unknown[14];
++ } *hdr = src;
++
++ if (len < sizeof(*hdr))
++ return -ENODATA;
++ // Note: RX data is not padded, even though TX data must be padded.
++ if (len != sizeof(*hdr) + hdr->data_size)
++ return -EMSGSIZE;
++
++ dest->data = hdr + 1;
++ dest->size = hdr->data_size;
++
++ switch (hdr->code) {
++ case RX_CODE_RESET:
++ // The THC sends a reset request when we need to reinitialize the device.
++ // This usually only happens if we send an invalid command or put the device
++ // in a bad state.
++ dest->type = ITHC_DATA_ERROR;
++ return 0;
++ case RX_CODE_REPORT_DESCRIPTOR:
++ // The descriptor is preceded by 8 nul bytes.
++ if (hdr->data_size < 8)
++ return -ENODATA;
++ dest->type = ITHC_DATA_REPORT_DESCRIPTOR;
++ dest->data = (char *)(hdr + 1) + 8;
++ dest->size = hdr->data_size - 8;
++ return 0;
++ case RX_CODE_INPUT_REPORT:
++ dest->type = ITHC_DATA_INPUT_REPORT;
++ return 0;
++ case RX_CODE_FEATURE_REPORT:
++ dest->type = ITHC_DATA_GET_FEATURE;
++ return 0;
++ default:
++ return -EINVAL;
++ }
++}
++
++ssize_t ithc_legacy_encode_tx(struct ithc *ithc, const struct ithc_data *src, void *dest,
++ size_t maxlen)
++{
++ struct {
++ u32 code;
++ u32 data_size;
++ } *hdr = dest;
++
++ size_t src_size = src->size;
++ const void *src_data = src->data;
++ const u64 get_report_desc_data = 0;
++ u32 code;
++
++ switch (src->type) {
++ case ITHC_DATA_SET_FEATURE:
++ code = TX_CODE_SET_FEATURE;
++ break;
++ case ITHC_DATA_GET_FEATURE:
++ code = TX_CODE_GET_FEATURE;
++ break;
++ case ITHC_DATA_OUTPUT_REPORT:
++ code = TX_CODE_OUTPUT_REPORT;
++ break;
++ case ITHC_DATA_REPORT_DESCRIPTOR:
++ code = TX_CODE_GET_REPORT_DESCRIPTOR;
++ src_size = sizeof(get_report_desc_data);
++ src_data = &get_report_desc_data;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ // Data must be padded to next 4-byte boundary.
++ size_t padded = round_up(src_size, 4);
++ if (sizeof(*hdr) + padded > maxlen)
++ return -EOVERFLOW;
++
++ // Fill the TX buffer with header and data.
++ hdr->code = code;
++ hdr->data_size = src_size;
++ memcpy_and_pad(hdr + 1, padded, src_data, src_size, 0);
++
++ return sizeof(*hdr) + padded;
++}
++
+diff --git a/drivers/hid/ithc/ithc-legacy.h b/drivers/hid/ithc/ithc-legacy.h
+new file mode 100644
+index 0000000000000..28d6924620722
+--- /dev/null
++++ b/drivers/hid/ithc/ithc-legacy.h
+@@ -0,0 +1,8 @@
++/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
++
++int ithc_legacy_init(struct ithc *ithc);
++void ithc_legacy_exit(struct ithc *ithc);
++int ithc_legacy_decode_rx(struct ithc *ithc, const void *src, size_t len, struct ithc_data *dest);
++ssize_t ithc_legacy_encode_tx(struct ithc *ithc, const struct ithc_data *src, void *dest,
++ size_t maxlen);
++
+diff --git a/drivers/hid/ithc/ithc-main.c b/drivers/hid/ithc/ithc-main.c
+index 87ed4aa70fda0..2acf02e41d40f 100644
+--- a/drivers/hid/ithc/ithc-main.c
++++ b/drivers/hid/ithc/ithc-main.c
+@@ -5,28 +5,6 @@
+ 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) },
+@@ -66,15 +44,13 @@ static bool ithc_use_rx1 = true;
+ module_param_named(rx1, ithc_use_rx1, bool, 0);
+ MODULE_PARM_DESC(rx1, "Use DMA RX channel 1");
+
+-// Values below 250 seem to work well on the SP7+. If this is set too high, you may observe cursor stuttering.
+-static int ithc_dma_latency_us = 200;
+-module_param_named(dma_latency_us, ithc_dma_latency_us, int, 0);
+-MODULE_PARM_DESC(dma_latency_us, "Determines the CPU latency QoS value for DMA transfers (in microseconds), -1 to disable latency QoS");
++static int ithc_active_ltr_us = -1;
++module_param_named(activeltr, ithc_active_ltr_us, int, 0);
++MODULE_PARM_DESC(activeltr, "Active LTR value override (in microseconds)");
+
+-// Values above 1700 seem to work well on the SP7+. If this is set too low, you may observe cursor stuttering.
+-static unsigned int ithc_dma_early_us = 2000;
+-module_param_named(dma_early_us, ithc_dma_early_us, uint, 0);
+-MODULE_PARM_DESC(dma_early_us, "Determines how early the CPU latency QoS value is applied before the next expected IRQ (in microseconds)");
++static int ithc_idle_ltr_us = -1;
++module_param_named(idleltr, ithc_idle_ltr_us, int, 0);
++MODULE_PARM_DESC(idleltr, "Idle LTR value override (in microseconds)");
+
+ static bool ithc_log_regs_enabled = false;
+ module_param_named(logregs, ithc_log_regs_enabled, bool, 0);
+@@ -82,44 +58,30 @@ 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))
++ if (!ithc || !ithc->have_config)
+ return -ENODEV;
+- return sprintf(buf, "0x%04x", ithc->config.vendor_id);
++ return sprintf(buf, "0x%04x", ithc->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))
++ if (!ithc || !ithc->have_config)
+ return -ENODEV;
+- return sprintf(buf, "0x%04x", ithc->config.product_id);
++ return sprintf(buf, "0x%04x", ithc->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))
++ if (!ithc || !ithc->have_config)
+ return -ENODEV;
+- return sprintf(buf, "%u", ithc->config.revision);
++ return sprintf(buf, "%u", ithc->product_rev);
+ }
+ 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){
+@@ -128,185 +90,26 @@ static const struct attribute_group *ithc_attribute_groups[] = {
+ &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);
+- for (int retries = 0; ; retries++) {
+- 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(200)))
+- return 0;
+- if (retries > 5) {
+- pci_err(ithc->pci, "failed to read report descriptor\n");
+- return -ETIMEDOUT;
+- }
+- pci_warn(ithc->pci, "failed to read report descriptor, retrying\n");
+- }
+-}
+-
+-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) {
+- // Prepare for response.
+- 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);
+-
+- // Transmit 'get feature' request.
+- 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;
+- }
+-
+- // If everything went ok, the buffer has been filled with the response data.
+- // Return the response size.
+- 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;
+- }
+-
+- // 'Set feature', or 'output report'. These don't have a response.
+- 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 enum hrtimer_restart ithc_activity_start_timer_callback(struct hrtimer *t)
+-{
+- struct ithc *ithc = container_of(t, struct ithc, activity_start_timer);
+- ithc_set_active(ithc, ithc_dma_early_us * 2 + USEC_PER_MSEC);
+- return HRTIMER_NORESTART;
+-}
+-
+-static enum hrtimer_restart ithc_activity_end_timer_callback(struct hrtimer *t)
+-{
+- struct ithc *ithc = container_of(t, struct ithc, activity_end_timer);
+- cpu_latency_qos_update_request(&ithc->activity_qos, PM_QOS_DEFAULT_VALUE);
+- return HRTIMER_NORESTART;
+-}
+-
+-void ithc_set_active(struct ithc *ithc, unsigned int duration_us)
+-{
+- if (ithc_dma_latency_us < 0)
+- return;
+- // 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_RX_TIMEOUT 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, ithc_dma_latency_us);
+- hrtimer_start_range_ns(&ithc->activity_end_timer,
+- ns_to_ktime(duration_us * NSEC_PER_USEC), duration_us * NSEC_PER_USEC, HRTIMER_MODE_REL);
+-}
+-
+-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_rx[0].control, DMA_RX_CONTROL_IRQ_UNKNOWN_1 | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_READY | 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_READY | 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 int channel)
+ {
+- writel(DMA_RX_STATUS_ERROR | DMA_RX_STATUS_UNKNOWN_4 | DMA_RX_STATUS_HAVE_DATA,
++ writel(DMA_RX_STATUS_ERROR | DMA_RX_STATUS_READY | DMA_RX_STATUS_HAVE_DATA,
+ &ithc->regs->dma_rx[channel].status);
+ }
+
+@@ -325,39 +128,22 @@ static void ithc_process(struct ithc *ithc)
+ {
+ ithc_log_regs(ithc);
+
++ // The THC automatically transitions from LTR idle to active at the start of a DMA transfer.
++ // It does not appear to automatically go back to idle, so we switch it back here, since
++ // the DMA transfer should be complete.
++ ithc_set_ltr_idle(ithc);
++
+ bool rx0 = ithc_use_rx0 && (readl(&ithc->regs->dma_rx[0].status) & (DMA_RX_STATUS_ERROR | DMA_RX_STATUS_HAVE_DATA)) != 0;
+ bool rx1 = ithc_use_rx1 && (readl(&ithc->regs->dma_rx[1].status) & (DMA_RX_STATUS_ERROR | DMA_RX_STATUS_HAVE_DATA)) != 0;
+
+- // Track time between DMA rx transfers, so we can try to predict when we need to enable CPU latency QoS for the next transfer
+- ktime_t t = ktime_get();
+- ktime_t dt = ktime_sub(t, ithc->last_rx_time);
+- if (rx0 || rx1) {
+- ithc->last_rx_time = t;
+- if (dt > ms_to_ktime(100)) {
+- ithc->cur_rx_seq_count = 0;
+- ithc->cur_rx_seq_errors = 0;
+- }
+- ithc->cur_rx_seq_count++;
+- if (!ithc_use_polling && ithc_dma_latency_us >= 0) {
+- // Disable QoS, since the DMA transfer has completed (we re-enable it after a delay below)
+- cpu_latency_qos_update_request(&ithc->activity_qos, PM_QOS_DEFAULT_VALUE);
+- hrtimer_try_to_cancel(&ithc->activity_end_timer);
+- }
+- }
+-
+ // Read and clear error bits
+ u32 err = readl(&ithc->regs->error_flags);
+ if (err) {
+ writel(err, &ithc->regs->error_flags);
+ if (err & ~ERROR_FLAG_DMA_RX_TIMEOUT)
+ pci_err(ithc->pci, "error flags: 0x%08x\n", err);
+- if (err & ERROR_FLAG_DMA_RX_TIMEOUT) {
+- // Only log an error if we see a significant number of these errors.
+- ithc->cur_rx_seq_errors++;
+- if (ithc->cur_rx_seq_errors && ithc->cur_rx_seq_errors % 50 == 0 && ithc->cur_rx_seq_errors > ithc->cur_rx_seq_count / 10)
+- pci_err(ithc->pci, "High number of DMA RX timeouts/errors (%u/%u, dt=%lldus). Try adjusting dma_early_us and/or dma_latency_us.\n",
+- ithc->cur_rx_seq_errors, ithc->cur_rx_seq_count, ktime_to_us(dt));
+- }
++ if (err & ERROR_FLAG_DMA_RX_TIMEOUT)
++ pci_err(ithc->pci, "DMA RX timeout/error (try decreasing activeltr/idleltr if this happens frequently)\n");
+ }
+
+ // Process DMA rx
+@@ -372,12 +158,6 @@ static void ithc_process(struct ithc *ithc)
+ ithc_dma_rx(ithc, 1);
+ }
+
+- // Start timer to re-enable QoS for next rx, but only if we've seen an ERROR_FLAG_DMA_RX_TIMEOUT
+- if ((rx0 || rx1) && !ithc_use_polling && ithc_dma_latency_us >= 0 && ithc->cur_rx_seq_errors > 0) {
+- ktime_t expires = ktime_add(t, ktime_sub_us(dt, ithc_dma_early_us));
+- hrtimer_start_range_ns(&ithc->activity_start_timer, expires, 10 * NSEC_PER_USEC, HRTIMER_MODE_ABS);
+- }
+-
+ ithc_log_regs(ithc);
+ }
+
+@@ -403,12 +183,8 @@ static int ithc_poll_thread(void *arg)
+ ithc_process(ithc);
+ // Decrease polling interval to 20ms if we received data, otherwise slowly
+ // increase it up to 200ms.
+- if (n != ithc->dma_rx[1].num_received) {
+- ithc_set_active(ithc, 100 * USEC_PER_MSEC);
+- sleep = 20;
+- } else {
+- sleep = min(200u, sleep + (sleep >> 4) + 1);
+- }
++ sleep = n != ithc->dma_rx[1].num_received ? 20
++ : min(200u, sleep + (sleep >> 4) + 1);
+ msleep_interruptible(sleep);
+ }
+ return 0;
+@@ -431,73 +207,44 @@ static void ithc_disable(struct ithc *ithc)
+
+ static int ithc_init_device(struct ithc *ithc)
+ {
++ // Read ACPI config for QuickSPI mode
++ struct ithc_acpi_config cfg = { 0 };
++ CHECK_RET(ithc_read_acpi_config, ithc, &cfg);
++ if (!cfg.has_config)
++ pci_info(ithc->pci, "no ACPI config, using legacy mode\n");
++ else
++ ithc_print_acpi_config(ithc, &cfg);
++ ithc->use_quickspi = cfg.has_config;
++
++ // Shut down device
+ 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);
+-
+- // Since we don't yet know which SPI config the device wants, use default speed and mode
+- // initially for reading config data.
+- ithc_set_spi_config(ithc, 10, 0);
+-
+- // Setting the following bit seems to make reading the config more reliable.
+- bitsl_set(&ithc->regs->dma_rx[0].unknown_init_bits, 0x80000000);
++ ithc_log_regs(ithc);
+
+ // If the device was previously enabled, wait a bit to make sure it's fully shut down.
+ if (was_enabled)
+ if (msleep_interruptible(100))
+ return -EINTR;
+
+- // Take the touch device out of reset.
+- 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, "failed to reset device, state = 0x%08x\n", readl(&ithc->regs->state));
+- return -ETIMEDOUT;
+- }
+- pci_warn(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);
++ // Set Latency Tolerance Reporting config. The device will automatically
++ // apply these values depending on whether it is active or idle.
++ // If active value is too high, DMA buffer data can become truncated.
++ // By default, we set the active LTR value to 100us, and idle to 100ms.
++ u64 active_ltr_ns = ithc_active_ltr_us >= 0 ? (u64)ithc_active_ltr_us * 1000
++ : cfg.has_config && cfg.has_active_ltr ? (u64)cfg.active_ltr << 10
++ : 100 * 1000;
++ u64 idle_ltr_ns = ithc_idle_ltr_us >= 0 ? (u64)ithc_idle_ltr_us * 1000
++ : cfg.has_config && cfg.has_idle_ltr ? (u64)cfg.idle_ltr << 10
++ : 100 * 1000 * 1000;
++ ithc_set_ltr_config(ithc, active_ltr_ns, idle_ltr_ns);
++
++ if (ithc->use_quickspi)
++ CHECK_RET(ithc_quickspi_init, ithc, &cfg);
++ else
++ CHECK_RET(ithc_legacy_init, ithc);
+
+- // Waiting for the following status bit makes reading config much more reliable,
+- // however the official driver does not seem to do this...
+- CHECK(waitl, ithc, &ithc->regs->dma_rx[0].status, DMA_RX_STATUS_UNKNOWN_4, DMA_RX_STATUS_UNKNOWN_4);
+-
+- // Read configuration data.
+- 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_warn(ithc->pci, "failed to read config, retrying\n");
+- if (msleep_interruptible(100))
+- return -EINTR;
+- }
+- ithc_log_regs(ithc);
+-
+- // Apply SPI config and enable touch device.
+- 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;
+ }
+
+@@ -527,11 +274,11 @@ static void ithc_stop(void *res)
+ CHECK(kthread_stop, ithc->poll_thread);
+ if (ithc->irq >= 0)
+ disable_irq(ithc->irq);
+- CHECK(ithc_set_device_enabled, ithc, false);
++ if (ithc->use_quickspi)
++ ithc_quickspi_exit(ithc);
++ else
++ ithc_legacy_exit(ithc);
+ ithc_disable(ithc);
+- hrtimer_cancel(&ithc->activity_start_timer);
+- hrtimer_cancel(&ithc->activity_end_timer);
+- cpu_latency_qos_remove_request(&ithc->activity_qos);
+
+ // Clear DMA config.
+ for (unsigned int i = 0; i < 2; i++) {
+@@ -570,9 +317,6 @@ static int ithc_start(struct pci_dev *pci)
+ 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)
+@@ -596,6 +340,9 @@ static int ithc_start(struct pci_dev *pci)
+
+ // Initialize THC and touch device.
+ CHECK_RET(ithc_init_device, ithc);
++
++ // Initialize HID and DMA.
++ CHECK_RET(ithc_hid_init, ithc);
+ CHECK(devm_device_add_groups, &pci->dev, ithc_attribute_groups);
+ if (ithc_use_rx0)
+ CHECK_RET(ithc_dma_rx_init, ithc, 0);
+@@ -603,18 +350,10 @@ static int ithc_start(struct pci_dev *pci)
+ CHECK_RET(ithc_dma_rx_init, ithc, 1);
+ CHECK_RET(ithc_dma_tx_init, ithc);
+
+- cpu_latency_qos_add_request(&ithc->activity_qos, PM_QOS_DEFAULT_VALUE);
+- hrtimer_init(&ithc->activity_start_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+- ithc->activity_start_timer.function = ithc_activity_start_timer_callback;
+- hrtimer_init(&ithc->activity_end_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+- ithc->activity_end_timer.function = ithc_activity_end_timer_callback;
+-
+ // 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);
+
+- CHECK_RET(ithc_hid_init, ithc);
+-
+ // Start polling/IRQ.
+ if (ithc_use_polling) {
+ pci_info(pci, "using polling instead of irq\n");
+@@ -637,9 +376,11 @@ static int ithc_start(struct pci_dev *pci)
+
+ // 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_RET(hid_add_device, ithc->hid.dev);
++
++ CHECK(ithc_debug_init_device, ithc);
+
+- CHECK(ithc_debug_init, ithc);
++ ithc_set_ltr_idle(ithc);
+
+ pci_dbg(pci, "started\n");
+ return 0;
+@@ -710,17 +451,20 @@ static struct pci_driver ithc_driver = {
+ .thaw = ithc_thaw,
+ .restore = ithc_restore,
+ },
++ .driver.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ //.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)
+ {
++ ithc_debug_init_module();
+ return pci_register_driver(&ithc_driver);
+ }
+
+ static void __exit ithc_exit(void)
+ {
+ pci_unregister_driver(&ithc_driver);
++ ithc_debug_exit_module();
+ }
+
+ module_init(ithc_init);
+diff --git a/drivers/hid/ithc/ithc-quickspi.c b/drivers/hid/ithc/ithc-quickspi.c
+new file mode 100644
+index 0000000000000..760e55ead0788
+--- /dev/null
++++ b/drivers/hid/ithc/ithc-quickspi.c
+@@ -0,0 +1,578 @@
++// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
++
++// Some public THC/QuickSPI documentation can be found in:
++// - Intel Firmware Support Package repo: https://github.com/intel/FSP
++// - HID over SPI (HIDSPI) spec: https://www.microsoft.com/en-us/download/details.aspx?id=103325
++
++#include "ithc.h"
++
++static const guid_t guid_hidspi =
++ GUID_INIT(0x6e2ac436, 0x0fcf, 0x41af, 0xa2, 0x65, 0xb3, 0x2a, 0x22, 0x0d, 0xcf, 0xab);
++static const guid_t guid_thc_quickspi =
++ GUID_INIT(0x300d35b7, 0xac20, 0x413e, 0x8e, 0x9c, 0x92, 0xe4, 0xda, 0xfd, 0x0a, 0xfe);
++static const guid_t guid_thc_ltr =
++ GUID_INIT(0x84005682, 0x5b71, 0x41a4, 0x8d, 0x66, 0x81, 0x30, 0xf7, 0x87, 0xa1, 0x38);
++
++// TODO The HIDSPI spec says revision should be 3. Should we try both?
++#define DSM_REV 2
++
++struct hidspi_header {
++ u8 type;
++ u16 len;
++ u8 id;
++} __packed;
++static_assert(sizeof(struct hidspi_header) == 4);
++
++#define HIDSPI_INPUT_TYPE_DATA 1
++#define HIDSPI_INPUT_TYPE_RESET_RESPONSE 3
++#define HIDSPI_INPUT_TYPE_COMMAND_RESPONSE 4
++#define HIDSPI_INPUT_TYPE_GET_FEATURE_RESPONSE 5
++#define HIDSPI_INPUT_TYPE_DEVICE_DESCRIPTOR 7
++#define HIDSPI_INPUT_TYPE_REPORT_DESCRIPTOR 8
++#define HIDSPI_INPUT_TYPE_SET_FEATURE_RESPONSE 9
++#define HIDSPI_INPUT_TYPE_OUTPUT_REPORT_RESPONSE 10
++#define HIDSPI_INPUT_TYPE_GET_INPUT_REPORT_RESPONSE 11
++
++#define HIDSPI_OUTPUT_TYPE_DEVICE_DESCRIPTOR_REQUEST 1
++#define HIDSPI_OUTPUT_TYPE_REPORT_DESCRIPTOR_REQUEST 2
++#define HIDSPI_OUTPUT_TYPE_SET_FEATURE 3
++#define HIDSPI_OUTPUT_TYPE_GET_FEATURE 4
++#define HIDSPI_OUTPUT_TYPE_OUTPUT_REPORT 5
++#define HIDSPI_OUTPUT_TYPE_INPUT_REPORT_REQUEST 6
++#define HIDSPI_OUTPUT_TYPE_COMMAND 7
++
++struct hidspi_device_descriptor {
++ u16 wDeviceDescLength;
++ u16 bcdVersion;
++ u16 wReportDescLength;
++ u16 wMaxInputLength;
++ u16 wMaxOutputLength;
++ u16 wMaxFragmentLength;
++ u16 wVendorID;
++ u16 wProductID;
++ u16 wVersionID;
++ u16 wFlags;
++ u32 dwReserved;
++};
++static_assert(sizeof(struct hidspi_device_descriptor) == 24);
++
++static int read_acpi_u32(struct ithc *ithc, const guid_t *guid, u32 func, u32 *dest)
++{
++ acpi_handle handle = ACPI_HANDLE(&ithc->pci->dev);
++ union acpi_object *o = acpi_evaluate_dsm(handle, guid, DSM_REV, func, NULL);
++ if (!o)
++ return 0;
++ if (o->type != ACPI_TYPE_INTEGER) {
++ pci_err(ithc->pci, "DSM %pUl %u returned type %i instead of integer\n",
++ guid, func, o->type);
++ ACPI_FREE(o);
++ return -1;
++ }
++ pci_dbg(ithc->pci, "DSM %pUl %u = 0x%08x\n", guid, func, (u32)o->integer.value);
++ *dest = (u32)o->integer.value;
++ ACPI_FREE(o);
++ return 1;
++}
++
++static int read_acpi_buf(struct ithc *ithc, const guid_t *guid, u32 func, size_t len, u8 *dest)
++{
++ acpi_handle handle = ACPI_HANDLE(&ithc->pci->dev);
++ union acpi_object *o = acpi_evaluate_dsm(handle, guid, DSM_REV, func, NULL);
++ if (!o)
++ return 0;
++ if (o->type != ACPI_TYPE_BUFFER) {
++ pci_err(ithc->pci, "DSM %pUl %u returned type %i instead of buffer\n",
++ guid, func, o->type);
++ ACPI_FREE(o);
++ return -1;
++ }
++ if (o->buffer.length != len) {
++ pci_err(ithc->pci, "DSM %pUl %u returned len %u instead of %zu\n",
++ guid, func, o->buffer.length, len);
++ ACPI_FREE(o);
++ return -1;
++ }
++ memcpy(dest, o->buffer.pointer, len);
++ pci_dbg(ithc->pci, "DSM %pUl %u = 0x%02x\n", guid, func, dest[0]);
++ ACPI_FREE(o);
++ return 1;
++}
++
++int ithc_read_acpi_config(struct ithc *ithc, struct ithc_acpi_config *cfg)
++{
++ int r;
++ acpi_handle handle = ACPI_HANDLE(&ithc->pci->dev);
++
++ cfg->has_config = acpi_check_dsm(handle, &guid_hidspi, DSM_REV, BIT(0));
++ if (!cfg->has_config)
++ return 0;
++
++ // HIDSPI settings
++
++ r = read_acpi_u32(ithc, &guid_hidspi, 1, &cfg->input_report_header_address);
++ if (r < 0)
++ return r;
++ cfg->has_input_report_header_address = r > 0;
++ if (r > 0 && cfg->input_report_header_address > 0xffffff) {
++ pci_err(ithc->pci, "Invalid input report header address 0x%x\n",
++ cfg->input_report_header_address);
++ return -1;
++ }
++
++ r = read_acpi_u32(ithc, &guid_hidspi, 2, &cfg->input_report_body_address);
++ if (r < 0)
++ return r;
++ cfg->has_input_report_body_address = r > 0;
++ if (r > 0 && cfg->input_report_body_address > 0xffffff) {
++ pci_err(ithc->pci, "Invalid input report body address 0x%x\n",
++ cfg->input_report_body_address);
++ return -1;
++ }
++
++ r = read_acpi_u32(ithc, &guid_hidspi, 3, &cfg->output_report_body_address);
++ if (r < 0)
++ return r;
++ cfg->has_output_report_body_address = r > 0;
++ if (r > 0 && cfg->output_report_body_address > 0xffffff) {
++ pci_err(ithc->pci, "Invalid output report body address 0x%x\n",
++ cfg->output_report_body_address);
++ return -1;
++ }
++
++ r = read_acpi_buf(ithc, &guid_hidspi, 4, sizeof(cfg->read_opcode), &cfg->read_opcode);
++ if (r < 0)
++ return r;
++ cfg->has_read_opcode = r > 0;
++
++ r = read_acpi_buf(ithc, &guid_hidspi, 5, sizeof(cfg->write_opcode), &cfg->write_opcode);
++ if (r < 0)
++ return r;
++ cfg->has_write_opcode = r > 0;
++
++ u32 flags;
++ r = read_acpi_u32(ithc, &guid_hidspi, 6, &flags);
++ if (r < 0)
++ return r;
++ cfg->has_read_mode = cfg->has_write_mode = r > 0;
++ if (r > 0) {
++ cfg->read_mode = (flags >> 14) & 3;
++ cfg->write_mode = flags & BIT(13) ? cfg->read_mode : SPI_MODE_SINGLE;
++ }
++
++ // Quick SPI settings
++
++ r = read_acpi_u32(ithc, &guid_thc_quickspi, 1, &cfg->spi_frequency);
++ if (r < 0)
++ return r;
++ cfg->has_spi_frequency = r > 0;
++
++ r = read_acpi_u32(ithc, &guid_thc_quickspi, 2, &cfg->limit_packet_size);
++ if (r < 0)
++ return r;
++ cfg->has_limit_packet_size = r > 0;
++
++ r = read_acpi_u32(ithc, &guid_thc_quickspi, 3, &cfg->tx_delay);
++ if (r < 0)
++ return r;
++ cfg->has_tx_delay = r > 0;
++ if (r > 0)
++ cfg->tx_delay &= 0xffff;
++
++ // LTR settings
++
++ r = read_acpi_u32(ithc, &guid_thc_ltr, 1, &cfg->active_ltr);
++ if (r < 0)
++ return r;
++ cfg->has_active_ltr = r > 0;
++ if (r > 0 && (!cfg->active_ltr || cfg->active_ltr > 0x3ff)) {
++ if (cfg->active_ltr != 0xffffffff)
++ pci_warn(ithc->pci, "Ignoring invalid active LTR value 0x%x\n",
++ cfg->active_ltr);
++ cfg->active_ltr = 500;
++ }
++
++ r = read_acpi_u32(ithc, &guid_thc_ltr, 2, &cfg->idle_ltr);
++ if (r < 0)
++ return r;
++ cfg->has_idle_ltr = r > 0;
++ if (r > 0 && (!cfg->idle_ltr || cfg->idle_ltr > 0x3ff)) {
++ if (cfg->idle_ltr != 0xffffffff)
++ pci_warn(ithc->pci, "Ignoring invalid idle LTR value 0x%x\n",
++ cfg->idle_ltr);
++ cfg->idle_ltr = 500;
++ if (cfg->has_active_ltr && cfg->active_ltr > cfg->idle_ltr)
++ cfg->idle_ltr = cfg->active_ltr;
++ }
++
++ return 0;
++}
++
++void ithc_print_acpi_config(struct ithc *ithc, const struct ithc_acpi_config *cfg)
++{
++ if (!cfg->has_config) {
++ pci_info(ithc->pci, "No ACPI config");
++ return;
++ }
++
++ char input_report_header_address[16] = "-";
++ if (cfg->has_input_report_header_address)
++ sprintf(input_report_header_address, "0x%x", cfg->input_report_header_address);
++ char input_report_body_address[16] = "-";
++ if (cfg->has_input_report_body_address)
++ sprintf(input_report_body_address, "0x%x", cfg->input_report_body_address);
++ char output_report_body_address[16] = "-";
++ if (cfg->has_output_report_body_address)
++ sprintf(output_report_body_address, "0x%x", cfg->output_report_body_address);
++ char read_opcode[16] = "-";
++ if (cfg->has_read_opcode)
++ sprintf(read_opcode, "0x%02x", cfg->read_opcode);
++ char write_opcode[16] = "-";
++ if (cfg->has_write_opcode)
++ sprintf(write_opcode, "0x%02x", cfg->write_opcode);
++ char read_mode[16] = "-";
++ if (cfg->has_read_mode)
++ sprintf(read_mode, "%i", cfg->read_mode);
++ char write_mode[16] = "-";
++ if (cfg->has_write_mode)
++ sprintf(write_mode, "%i", cfg->write_mode);
++ char spi_frequency[16] = "-";
++ if (cfg->has_spi_frequency)
++ sprintf(spi_frequency, "%u", cfg->spi_frequency);
++ char limit_packet_size[16] = "-";
++ if (cfg->has_limit_packet_size)
++ sprintf(limit_packet_size, "%u", cfg->limit_packet_size);
++ char tx_delay[16] = "-";
++ if (cfg->has_tx_delay)
++ sprintf(tx_delay, "%u", cfg->tx_delay);
++ char active_ltr[16] = "-";
++ if (cfg->has_active_ltr)
++ sprintf(active_ltr, "%u", cfg->active_ltr);
++ char idle_ltr[16] = "-";
++ if (cfg->has_idle_ltr)
++ sprintf(idle_ltr, "%u", cfg->idle_ltr);
++
++ pci_info(ithc->pci, "ACPI config: InputHeaderAddr=%s InputBodyAddr=%s OutputBodyAddr=%s ReadOpcode=%s WriteOpcode=%s ReadMode=%s WriteMode=%s Frequency=%s LimitPacketSize=%s TxDelay=%s ActiveLTR=%s IdleLTR=%s\n",
++ input_report_header_address, input_report_body_address, output_report_body_address,
++ read_opcode, write_opcode, read_mode, write_mode,
++ spi_frequency, limit_packet_size, tx_delay, active_ltr, idle_ltr);
++}
++
++static int ithc_quickspi_init_regs(struct ithc *ithc, const struct ithc_acpi_config *cfg)
++{
++ pci_dbg(ithc->pci, "initializing QuickSPI registers\n");
++
++ // SPI frequency and mode
++ if (!cfg->has_spi_frequency || !cfg->spi_frequency) {
++ pci_err(ithc->pci, "Missing SPI frequency in configuration\n");
++ return -EINVAL;
++ }
++ unsigned int clkdiv = DIV_ROUND_UP(SPI_CLK_FREQ_BASE, cfg->spi_frequency);
++ bool clkdiv8 = clkdiv > 7;
++ if (clkdiv8)
++ clkdiv = min(7u, DIV_ROUND_UP(clkdiv, 8u));
++ if (!clkdiv)
++ clkdiv = 1;
++ CHECK_RET(ithc_set_spi_config, ithc, clkdiv, clkdiv8,
++ cfg->has_read_mode ? cfg->read_mode : SPI_MODE_SINGLE,
++ cfg->has_write_mode ? cfg->write_mode : SPI_MODE_SINGLE);
++
++ // SPI addresses and opcodes
++ if (cfg->has_input_report_header_address)
++ writel(cfg->input_report_header_address, &ithc->regs->spi_header_addr);
++ if (cfg->has_input_report_body_address)
++ writel(cfg->input_report_body_address, &ithc->regs->dma_rx[0].spi_addr);
++ if (cfg->has_output_report_body_address)
++ writel(cfg->output_report_body_address, &ithc->regs->dma_tx.spi_addr);
++
++ if (cfg->has_read_opcode) {
++ writeb(cfg->read_opcode, &ithc->regs->read_opcode);
++ writeb(cfg->read_opcode, &ithc->regs->read_opcode_single);
++ writeb(cfg->read_opcode, &ithc->regs->read_opcode_dual);
++ writeb(cfg->read_opcode, &ithc->regs->read_opcode_quad);
++ }
++ if (cfg->has_write_opcode) {
++ writeb(cfg->write_opcode, &ithc->regs->write_opcode);
++ writeb(cfg->write_opcode, &ithc->regs->write_opcode_single);
++ writeb(cfg->write_opcode, &ithc->regs->write_opcode_dual);
++ writeb(cfg->write_opcode, &ithc->regs->write_opcode_quad);
++ }
++ ithc_log_regs(ithc);
++
++ // The rest...
++ bitsl(&ithc->regs->quickspi_config1,
++ QUICKSPI_CONFIG1_UNKNOWN_0(0xff) | QUICKSPI_CONFIG1_UNKNOWN_5(0xff) |
++ QUICKSPI_CONFIG1_UNKNOWN_10(0xff) | QUICKSPI_CONFIG1_UNKNOWN_16(0xffff),
++ QUICKSPI_CONFIG1_UNKNOWN_0(4) | QUICKSPI_CONFIG1_UNKNOWN_5(4) |
++ QUICKSPI_CONFIG1_UNKNOWN_10(22) | QUICKSPI_CONFIG1_UNKNOWN_16(2));
++
++ bitsl(&ithc->regs->quickspi_config2,
++ QUICKSPI_CONFIG2_UNKNOWN_0(0xff) | QUICKSPI_CONFIG2_UNKNOWN_5(0xff) |
++ QUICKSPI_CONFIG2_UNKNOWN_12(0xff),
++ QUICKSPI_CONFIG2_UNKNOWN_0(8) | QUICKSPI_CONFIG2_UNKNOWN_5(14) |
++ QUICKSPI_CONFIG2_UNKNOWN_12(2));
++
++ u32 pktsize = cfg->has_limit_packet_size && cfg->limit_packet_size == 1 ? 4 : 0x80;
++ bitsl(&ithc->regs->spi_config,
++ SPI_CONFIG_READ_PACKET_SIZE(0xfff) | SPI_CONFIG_WRITE_PACKET_SIZE(0xfff),
++ SPI_CONFIG_READ_PACKET_SIZE(pktsize) | SPI_CONFIG_WRITE_PACKET_SIZE(pktsize));
++
++ bitsl_set(&ithc->regs->quickspi_config2,
++ QUICKSPI_CONFIG2_UNKNOWN_16 | QUICKSPI_CONFIG2_UNKNOWN_17);
++ bitsl(&ithc->regs->quickspi_config2,
++ QUICKSPI_CONFIG2_DISABLE_READ_ADDRESS_INCREMENT |
++ QUICKSPI_CONFIG2_DISABLE_WRITE_ADDRESS_INCREMENT |
++ QUICKSPI_CONFIG2_ENABLE_WRITE_STREAMING_MODE, 0);
++
++ return 0;
++}
++
++static int wait_for_report(struct ithc *ithc)
++{
++ CHECK_RET(waitl, ithc, &ithc->regs->dma_rx[0].status,
++ DMA_RX_STATUS_READY, DMA_RX_STATUS_READY);
++ writel(DMA_RX_STATUS_READY, &ithc->regs->dma_rx[0].status);
++
++ u32 h = readl(&ithc->regs->input_header);
++ ithc_log_regs(ithc);
++ if (INPUT_HEADER_SYNC(h) != INPUT_HEADER_SYNC_VALUE
++ || INPUT_HEADER_VERSION(h) != INPUT_HEADER_VERSION_VALUE) {
++ pci_err(ithc->pci, "invalid input report frame header 0x%08x\n", h);
++ return -ENODATA;
++ }
++ return INPUT_HEADER_REPORT_LENGTH(h) * 4;
++}
++
++static int ithc_quickspi_init_hidspi(struct ithc *ithc, const struct ithc_acpi_config *cfg)
++{
++ pci_dbg(ithc->pci, "initializing HIDSPI\n");
++
++ // HIDSPI initialization sequence:
++ // "1. The host shall invoke the ACPI reset method to clear the device state."
++ acpi_status s = acpi_evaluate_object(ACPI_HANDLE(&ithc->pci->dev), "_RST", NULL, NULL);
++ if (ACPI_FAILURE(s)) {
++ pci_err(ithc->pci, "ACPI reset failed\n");
++ return -EIO;
++ }
++
++ bitsl(&ithc->regs->control_bits, CONTROL_QUIESCE, 0);
++
++ // "2. Within 1 second, the device shall signal an interrupt and make available to the host
++ // an input report containing a device reset response."
++ int size = wait_for_report(ithc);
++ if (size < 0)
++ return size;
++ if (size < sizeof(struct hidspi_header)) {
++ pci_err(ithc->pci, "SPI data size too small for reset response (%u)\n", size);
++ return -EMSGSIZE;
++ }
++
++ // "3. The host shall read the reset response from the device at the Input Report addresses
++ // specified in ACPI."
++ u32 in_addr = cfg->has_input_report_body_address ? cfg->input_report_body_address : 0x1000;
++ struct {
++ struct hidspi_header header;
++ union {
++ struct hidspi_device_descriptor device_desc;
++ u32 data[16];
++ };
++ } resp = { 0 };
++ if (size > sizeof(resp)) {
++ pci_err(ithc->pci, "SPI data size for reset response too big (%u)\n", size);
++ return -EMSGSIZE;
++ }
++ CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, in_addr, size, &resp);
++ if (resp.header.type != HIDSPI_INPUT_TYPE_RESET_RESPONSE) {
++ pci_err(ithc->pci, "received type %i instead of reset response\n", resp.header.type);
++ return -ENOMSG;
++ }
++
++ // "4. The host shall then write an Output Report to the device at the Output Report Address
++ // specified in ACPI, requesting the Device Descriptor from the device."
++ u32 out_addr = cfg->has_output_report_body_address ? cfg->output_report_body_address : 0x1000;
++ struct hidspi_header req = { .type = HIDSPI_OUTPUT_TYPE_DEVICE_DESCRIPTOR_REQUEST };
++ CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_WRITE, out_addr, sizeof(req), &req);
++
++ // "5. Within 1 second, the device shall signal an interrupt and make available to the host
++ // an input report containing the Device Descriptor."
++ size = wait_for_report(ithc);
++ if (size < 0)
++ return size;
++ if (size < sizeof(resp.header) + sizeof(resp.device_desc)) {
++ pci_err(ithc->pci, "SPI data size too small for device descriptor (%u)\n", size);
++ return -EMSGSIZE;
++ }
++
++ // "6. The host shall read the Device Descriptor from the Input Report addresses specified
++ // in ACPI."
++ if (size > sizeof(resp)) {
++ pci_err(ithc->pci, "SPI data size for device descriptor too big (%u)\n", size);
++ return -EMSGSIZE;
++ }
++ memset(&resp, 0, sizeof(resp));
++ CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, in_addr, size, &resp);
++ if (resp.header.type != HIDSPI_INPUT_TYPE_DEVICE_DESCRIPTOR) {
++ pci_err(ithc->pci, "received type %i instead of device descriptor\n",
++ resp.header.type);
++ return -ENOMSG;
++ }
++ struct hidspi_device_descriptor *d = &resp.device_desc;
++ if (resp.header.len < sizeof(*d)) {
++ pci_err(ithc->pci, "response too small for device descriptor (%u)\n",
++ resp.header.len);
++ return -EMSGSIZE;
++ }
++ if (d->wDeviceDescLength != sizeof(*d)) {
++ pci_err(ithc->pci, "invalid device descriptor length (%u)\n",
++ d->wDeviceDescLength);
++ return -EMSGSIZE;
++ }
++
++ pci_info(ithc->pci, "Device descriptor: bcdVersion=0x%04x wReportDescLength=%u wMaxInputLength=%u wMaxOutputLength=%u wMaxFragmentLength=%u wVendorID=0x%04x wProductID=0x%04x wVersionID=0x%04x wFlags=0x%04x dwReserved=0x%08x\n",
++ d->bcdVersion, d->wReportDescLength,
++ d->wMaxInputLength, d->wMaxOutputLength, d->wMaxFragmentLength,
++ d->wVendorID, d->wProductID, d->wVersionID,
++ d->wFlags, d->dwReserved);
++
++ ithc->vendor_id = d->wVendorID;
++ ithc->product_id = d->wProductID;
++ ithc->product_rev = d->wVersionID;
++ ithc->max_rx_size = max_t(u32, d->wMaxInputLength,
++ d->wReportDescLength + sizeof(struct hidspi_header));
++ ithc->max_tx_size = d->wMaxOutputLength;
++ ithc->have_config = true;
++
++ // "7. The device and host shall then enter their "Ready" states - where the device may
++ // begin sending Input Reports, and the device shall be prepared for Output Reports from
++ // the host."
++
++ return 0;
++}
++
++int ithc_quickspi_init(struct ithc *ithc, const struct ithc_acpi_config *cfg)
++{
++ bitsl_set(&ithc->regs->control_bits, CONTROL_QUIESCE);
++ CHECK_RET(waitl, ithc, &ithc->regs->control_bits, CONTROL_IS_QUIESCED, CONTROL_IS_QUIESCED);
++
++ ithc_log_regs(ithc);
++ CHECK_RET(ithc_quickspi_init_regs, ithc, cfg);
++ ithc_log_regs(ithc);
++ CHECK_RET(ithc_quickspi_init_hidspi, ithc, cfg);
++ ithc_log_regs(ithc);
++
++ // This value is set to 2 in ithc_quickspi_init_regs(). It needs to be set to 1 here,
++ // otherwise DMA will not work. Maybe selects between DMA and PIO mode?
++ bitsl(&ithc->regs->quickspi_config1,
++ QUICKSPI_CONFIG1_UNKNOWN_16(0xffff), QUICKSPI_CONFIG1_UNKNOWN_16(1));
++
++ // TODO Do we need to set any of the following bits here?
++ //bitsb_set(&ithc->regs->dma_rx[1].control2, DMA_RX_CONTROL2_UNKNOWN_4);
++ //bitsb_set(&ithc->regs->dma_rx[0].control2, DMA_RX_CONTROL2_UNKNOWN_5);
++ //bitsb_set(&ithc->regs->dma_rx[1].control2, DMA_RX_CONTROL2_UNKNOWN_5);
++ //bitsl_set(&ithc->regs->dma_rx[0].init_unknown, INIT_UNKNOWN_3);
++ //bitsl_set(&ithc->regs->dma_rx[0].init_unknown, INIT_UNKNOWN_31);
++
++ ithc_log_regs(ithc);
++
++ return 0;
++}
++
++void ithc_quickspi_exit(struct ithc *ithc)
++{
++ // TODO Should we send HIDSPI 'power off' command?
++ //struct hidspi_header h = { .type = HIDSPI_OUTPUT_TYPE_COMMAND, .id = 3, };
++ //struct ithc_data d = { .type = ITHC_DATA_RAW, .data = &h, .size = sizeof(h) };
++ //CHECK(ithc_dma_tx, ithc, &d); // or ithc_spi_command()
++}
++
++int ithc_quickspi_decode_rx(struct ithc *ithc, const void *src, size_t len, struct ithc_data *dest)
++{
++ const struct hidspi_header *hdr = src;
++
++ if (len < sizeof(*hdr))
++ return -ENODATA;
++ // TODO Do we need to handle HIDSPI packet fragmentation?
++ if (len < sizeof(*hdr) + hdr->len)
++ return -EMSGSIZE;
++ if (len > round_up(sizeof(*hdr) + hdr->len, 4))
++ return -EMSGSIZE;
++
++ switch (hdr->type) {
++ case HIDSPI_INPUT_TYPE_RESET_RESPONSE:
++ // TODO "When the device detects an error condition, it may interrupt and make
++ // available to the host an Input Report containing an unsolicited Reset Response.
++ // After receiving an unsolicited Reset Response, the host shall initiate the
++ // request procedure from step (4) in the [HIDSPI initialization] process."
++ dest->type = ITHC_DATA_ERROR;
++ return 0;
++ case HIDSPI_INPUT_TYPE_REPORT_DESCRIPTOR:
++ dest->type = ITHC_DATA_REPORT_DESCRIPTOR;
++ dest->data = hdr + 1;
++ dest->size = hdr->len;
++ return 0;
++ case HIDSPI_INPUT_TYPE_DATA:
++ case HIDSPI_INPUT_TYPE_GET_INPUT_REPORT_RESPONSE:
++ dest->type = ITHC_DATA_INPUT_REPORT;
++ dest->data = &hdr->id;
++ dest->size = hdr->len + 1;
++ return 0;
++ case HIDSPI_INPUT_TYPE_GET_FEATURE_RESPONSE:
++ dest->type = ITHC_DATA_GET_FEATURE;
++ dest->data = &hdr->id;
++ dest->size = hdr->len + 1;
++ return 0;
++ case HIDSPI_INPUT_TYPE_SET_FEATURE_RESPONSE:
++ case HIDSPI_INPUT_TYPE_OUTPUT_REPORT_RESPONSE:
++ dest->type = ITHC_DATA_IGNORE;
++ return 0;
++ default:
++ return -EINVAL;
++ }
++}
++
++ssize_t ithc_quickspi_encode_tx(struct ithc *ithc, const struct ithc_data *src, void *dest,
++ size_t maxlen)
++{
++ struct hidspi_header *hdr = dest;
++
++ size_t src_size = src->size;
++ const u8 *src_data = src->data;
++ u8 type;
++
++ switch (src->type) {
++ case ITHC_DATA_SET_FEATURE:
++ type = HIDSPI_OUTPUT_TYPE_SET_FEATURE;
++ break;
++ case ITHC_DATA_GET_FEATURE:
++ type = HIDSPI_OUTPUT_TYPE_GET_FEATURE;
++ break;
++ case ITHC_DATA_OUTPUT_REPORT:
++ type = HIDSPI_OUTPUT_TYPE_OUTPUT_REPORT;
++ break;
++ case ITHC_DATA_REPORT_DESCRIPTOR:
++ type = HIDSPI_OUTPUT_TYPE_REPORT_DESCRIPTOR_REQUEST;
++ src_size = 0;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ u8 id = 0;
++ if (src_size) {
++ id = *src_data++;
++ src_size--;
++ }
++
++ // Data must be padded to next 4-byte boundary.
++ size_t padded = round_up(src_size, 4);
++ if (sizeof(*hdr) + padded > maxlen)
++ return -EOVERFLOW;
++
++ // Fill the TX buffer with header and data.
++ hdr->type = type;
++ hdr->len = (u16)src_size;
++ hdr->id = id;
++ memcpy_and_pad(hdr + 1, padded, src_data, src_size, 0);
++
++ return sizeof(*hdr) + padded;
++}
++
+diff --git a/drivers/hid/ithc/ithc-quickspi.h b/drivers/hid/ithc/ithc-quickspi.h
+new file mode 100644
+index 0000000000000..74d882f6b2f0a
+--- /dev/null
++++ b/drivers/hid/ithc/ithc-quickspi.h
+@@ -0,0 +1,39 @@
++/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
++
++struct ithc_acpi_config {
++ bool has_config: 1;
++ bool has_input_report_header_address: 1;
++ bool has_input_report_body_address: 1;
++ bool has_output_report_body_address: 1;
++ bool has_read_opcode: 1;
++ bool has_write_opcode: 1;
++ bool has_read_mode: 1;
++ bool has_write_mode: 1;
++ bool has_spi_frequency: 1;
++ bool has_limit_packet_size: 1;
++ bool has_tx_delay: 1;
++ bool has_active_ltr: 1;
++ bool has_idle_ltr: 1;
++ u32 input_report_header_address;
++ u32 input_report_body_address;
++ u32 output_report_body_address;
++ u8 read_opcode;
++ u8 write_opcode;
++ u8 read_mode;
++ u8 write_mode;
++ u32 spi_frequency;
++ u32 limit_packet_size;
++ u32 tx_delay; // us/10 // TODO use?
++ u32 active_ltr; // ns/1024
++ u32 idle_ltr; // ns/1024
++};
++
++int ithc_read_acpi_config(struct ithc *ithc, struct ithc_acpi_config *cfg);
++void ithc_print_acpi_config(struct ithc *ithc, const struct ithc_acpi_config *cfg);
++
++int ithc_quickspi_init(struct ithc *ithc, const struct ithc_acpi_config *cfg);
++void ithc_quickspi_exit(struct ithc *ithc);
++int ithc_quickspi_decode_rx(struct ithc *ithc, const void *src, size_t len, struct ithc_data *dest);
++ssize_t ithc_quickspi_encode_tx(struct ithc *ithc, const struct ithc_data *src, void *dest,
++ size_t maxlen);
++
+diff --git a/drivers/hid/ithc/ithc-regs.c b/drivers/hid/ithc/ithc-regs.c
+index e058721886e37..c0f13506af205 100644
+--- a/drivers/hid/ithc/ithc-regs.c
++++ b/drivers/hid/ithc/ithc-regs.c
+@@ -22,46 +22,104 @@ void bitsb(__iomem u8 *reg, u8 mask, u8 val)
+
+ int waitl(struct ithc *ithc, __iomem u32 *reg, u32 mask, u32 val)
+ {
++ ithc_log_regs(ithc);
+ 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)) {
++ ithc_log_regs(ithc);
+ 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;
+ }
++ ithc_log_regs(ithc);
+ pci_dbg(ithc->pci, "done waiting\n");
+ return 0;
+ }
+
+ int waitb(struct ithc *ithc, __iomem u8 *reg, u8 mask, u8 val)
+ {
++ ithc_log_regs(ithc);
+ 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)) {
++ ithc_log_regs(ithc);
+ 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;
+ }
++ ithc_log_regs(ithc);
+ pci_dbg(ithc->pci, "done waiting\n");
+ return 0;
+ }
+
+-int ithc_set_spi_config(struct ithc *ithc, u8 speed, u8 mode)
++static void calc_ltr(u64 *ns, unsigned int *val, unsigned int *scale)
+ {
+- pci_dbg(ithc->pci, "setting SPI speed to %i, mode %i\n", speed, mode);
+- if (mode == 3)
+- mode = 2;
++ unsigned int s = 0;
++ u64 v = *ns;
++ while (v > 0x3ff) {
++ s++;
++ v >>= 5;
++ }
++ if (s > 5) {
++ s = 5;
++ v = 0x3ff;
++ }
++ *val = v;
++ *scale = s;
++ *ns = v << (5 * s);
++}
++
++void ithc_set_ltr_config(struct ithc *ithc, u64 active_ltr_ns, u64 idle_ltr_ns)
++{
++ unsigned int active_val, active_scale, idle_val, idle_scale;
++ calc_ltr(&active_ltr_ns, &active_val, &active_scale);
++ calc_ltr(&idle_ltr_ns, &idle_val, &idle_scale);
++ pci_dbg(ithc->pci, "setting active LTR value to %llu ns, idle LTR value to %llu ns\n",
++ active_ltr_ns, idle_ltr_ns);
++ writel(LTR_CONFIG_ENABLE_ACTIVE | LTR_CONFIG_ENABLE_IDLE | LTR_CONFIG_APPLY |
++ LTR_CONFIG_ACTIVE_LTR_SCALE(active_scale) | LTR_CONFIG_ACTIVE_LTR_VALUE(active_val) |
++ LTR_CONFIG_IDLE_LTR_SCALE(idle_scale) | LTR_CONFIG_IDLE_LTR_VALUE(idle_val),
++ &ithc->regs->ltr_config);
++}
++
++void ithc_set_ltr_idle(struct ithc *ithc)
++{
++ u32 ltr = readl(&ithc->regs->ltr_config);
++ switch (ltr & (LTR_CONFIG_STATUS_ACTIVE | LTR_CONFIG_STATUS_IDLE)) {
++ case LTR_CONFIG_STATUS_IDLE:
++ break;
++ case LTR_CONFIG_STATUS_ACTIVE:
++ writel(ltr | LTR_CONFIG_TOGGLE | LTR_CONFIG_APPLY, &ithc->regs->ltr_config);
++ break;
++ default:
++ pci_err(ithc->pci, "invalid LTR state 0x%08x\n", ltr);
++ break;
++ }
++}
++
++int ithc_set_spi_config(struct ithc *ithc, u8 clkdiv, bool clkdiv8, u8 read_mode, u8 write_mode)
++{
++ if (clkdiv == 0 || clkdiv > 7 || read_mode > SPI_MODE_QUAD || write_mode > SPI_MODE_QUAD)
++ return -EINVAL;
++ static const char * const modes[] = { "single", "dual", "quad" };
++ pci_dbg(ithc->pci, "setting SPI frequency to %i Hz, %s read, %s write\n",
++ SPI_CLK_FREQ_BASE / (clkdiv * (clkdiv8 ? 8 : 1)),
++ modes[read_mode], modes[write_mode]);
+ 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));
++ SPI_CONFIG_READ_MODE(0xff) | SPI_CONFIG_READ_CLKDIV(0xff) |
++ SPI_CONFIG_WRITE_MODE(0xff) | SPI_CONFIG_WRITE_CLKDIV(0xff) |
++ SPI_CONFIG_CLKDIV_8,
++ SPI_CONFIG_READ_MODE(read_mode) | SPI_CONFIG_READ_CLKDIV(clkdiv) |
++ SPI_CONFIG_WRITE_MODE(write_mode) | SPI_CONFIG_WRITE_CLKDIV(clkdiv) |
++ (clkdiv8 ? SPI_CONFIG_CLKDIV_8 : 0));
+ 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);
++ pci_dbg(ithc->pci, "SPI command %u, size %u, offset 0x%x\n", command, size, offset);
+ if (size > sizeof(ithc->regs->spi_cmd.data))
+ return -EINVAL;
+
+diff --git a/drivers/hid/ithc/ithc-regs.h b/drivers/hid/ithc/ithc-regs.h
+index d4007d9e2bacc..a9d2364546442 100644
+--- a/drivers/hid/ithc/ithc-regs.h
++++ b/drivers/hid/ithc/ithc-regs.h
+@@ -1,14 +1,34 @@
+ /* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+
++#define LTR_CONFIG_ENABLE_ACTIVE BIT(0)
++#define LTR_CONFIG_TOGGLE BIT(1)
++#define LTR_CONFIG_ENABLE_IDLE BIT(2)
++#define LTR_CONFIG_APPLY BIT(3)
++#define LTR_CONFIG_IDLE_LTR_SCALE(x) (((x) & 7) << 4)
++#define LTR_CONFIG_IDLE_LTR_VALUE(x) (((x) & 0x3ff) << 7)
++#define LTR_CONFIG_ACTIVE_LTR_SCALE(x) (((x) & 7) << 17)
++#define LTR_CONFIG_ACTIVE_LTR_VALUE(x) (((x) & 0x3ff) << 20)
++#define LTR_CONFIG_STATUS_ACTIVE BIT(30)
++#define LTR_CONFIG_STATUS_IDLE BIT(31)
++
+ #define CONTROL_QUIESCE BIT(1)
+ #define CONTROL_IS_QUIESCED BIT(2)
+ #define CONTROL_NRESET BIT(3)
++#define CONTROL_UNKNOWN_24(x) (((x) & 3) << 24)
+ #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 SPI_CONFIG_READ_MODE(x) (((x) & 3) << 2)
++#define SPI_CONFIG_READ_CLKDIV(x) (((x) & 7) << 4)
++#define SPI_CONFIG_READ_PACKET_SIZE(x) (((x) & 0x1ff) << 7)
++#define SPI_CONFIG_WRITE_MODE(x) (((x) & 3) << 18)
++#define SPI_CONFIG_WRITE_CLKDIV(x) (((x) & 7) << 20)
++#define SPI_CONFIG_CLKDIV_8 BIT(23) // additionally divide clk by 8, for both read and write
++#define SPI_CONFIG_WRITE_PACKET_SIZE(x) (((x) & 0xff) << 24)
++
++#define SPI_CLK_FREQ_BASE 125000000
++#define SPI_MODE_SINGLE 0
++#define SPI_MODE_DUAL 1
++#define SPI_MODE_QUAD 2
+
+ #define ERROR_CONTROL_UNKNOWN_0 BIT(0)
+ #define ERROR_CONTROL_DISABLE_DMA BIT(1) // clears DMA_RX_CONTROL_ENABLE when a DMA error occurs
+@@ -53,33 +73,71 @@
+ #define DMA_TX_STATUS_UNKNOWN_2 BIT(2)
+ #define DMA_TX_STATUS_UNKNOWN_3 BIT(3) // busy?
+
++#define INPUT_HEADER_VERSION(x) ((x) & 0xf)
++#define INPUT_HEADER_REPORT_LENGTH(x) (((x) >> 8) & 0x3fff)
++#define INPUT_HEADER_SYNC(x) ((x) >> 24)
++#define INPUT_HEADER_VERSION_VALUE 3
++#define INPUT_HEADER_SYNC_VALUE 0x5a
++
++#define QUICKSPI_CONFIG1_UNKNOWN_0(x) (((x) & 0x1f) << 0)
++#define QUICKSPI_CONFIG1_UNKNOWN_5(x) (((x) & 0x1f) << 5)
++#define QUICKSPI_CONFIG1_UNKNOWN_10(x) (((x) & 0x1f) << 10)
++#define QUICKSPI_CONFIG1_UNKNOWN_16(x) (((x) & 0xffff) << 16)
++
++#define QUICKSPI_CONFIG2_UNKNOWN_0(x) (((x) & 0x1f) << 0)
++#define QUICKSPI_CONFIG2_UNKNOWN_5(x) (((x) & 0x1f) << 5)
++#define QUICKSPI_CONFIG2_UNKNOWN_12(x) (((x) & 0xf) << 12)
++#define QUICKSPI_CONFIG2_UNKNOWN_16 BIT(16)
++#define QUICKSPI_CONFIG2_UNKNOWN_17 BIT(17)
++#define QUICKSPI_CONFIG2_DISABLE_READ_ADDRESS_INCREMENT BIT(24)
++#define QUICKSPI_CONFIG2_DISABLE_WRITE_ADDRESS_INCREMENT BIT(25)
++#define QUICKSPI_CONFIG2_ENABLE_WRITE_STREAMING_MODE BIT(27)
++#define QUICKSPI_CONFIG2_IRQ_POLARITY BIT(28)
++
+ #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_READY BIT(4) // rx0 only
+ #define DMA_RX_CONTROL_IRQ_DATA BIT(5)
+
++#define DMA_RX_CONTROL2_UNKNOWN_4 BIT(4) // rx1 only?
+ #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_READY 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 INIT_UNKNOWN_GUC_2 BIT(2)
++#define INIT_UNKNOWN_3 BIT(3)
++#define INIT_UNKNOWN_GUC_4 BIT(4)
++#define INIT_UNKNOWN_5 BIT(5)
++#define INIT_UNKNOWN_31 BIT(31)
++
+ // COUNTER_RESET can be written to counter registers to reset them to zero. However, in some cases this can mess up the THC.
+ #define COUNTER_RESET BIT(31)
+
+ struct ithc_registers {
+- /* 0000 */ u32 _unknown_0000[1024];
++ /* 0000 */ u32 _unknown_0000[5];
++ /* 0014 */ u32 ltr_config;
++ /* 0018 */ u32 _unknown_0018[1018];
+ /* 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];
++ /* 1014 */ u8 read_opcode; // maybe for header?
++ /* 1015 */ u8 read_opcode_quad;
++ /* 1016 */ u8 read_opcode_dual;
++ /* 1017 */ u8 read_opcode_single;
++ /* 1018 */ u8 write_opcode; // not used?
++ /* 1019 */ u8 write_opcode_quad;
++ /* 101a */ u8 write_opcode_dual;
++ /* 101b */ u8 write_opcode_single;
++ /* 101c */ u32 _unknown_101c;
+ /* 1020 */ u32 error_control;
+ /* 1024 */ u32 error_status; // write to clear
+ /* 1028 */ u32 error_flags; // write to clear
+@@ -100,12 +158,19 @@ struct ithc_registers {
+ /* 109a */ u8 _unknown_109a;
+ /* 109b */ u8 num_prds;
+ /* 109c */ u32 status; // write to clear
++ /* 10a0 */ u32 _unknown_10a0[5];
++ /* 10b4 */ u32 spi_addr;
+ } 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
++ /* 10b8 */ u32 spi_header_addr;
++ union {
++ /* 10bc */ u32 irq_cause; // in legacy THC mode
++ /* 10bc */ u32 input_header; // in QuickSPI mode (see HIDSPI spec)
++ };
+ /* 10c0 */ u32 _unknown_10c0[8];
+ /* 10e0 */ u32 _unknown_10e0_counters[3];
+- /* 10ec */ u32 _unknown_10ec[5];
++ /* 10ec */ u32 quickspi_config1;
++ /* 10f0 */ u32 quickspi_config2;
++ /* 10f4 */ u32 _unknown_10f4[3];
+ struct {
+ /* 1100/1200 */ u64 addr; // cannot be written with writeq(), must use lo_hi_writeq()
+ /* 1108/1208 */ u8 num_bufs;
+@@ -120,70 +185,30 @@ struct ithc_registers {
+ /* 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
++ /* 1128/1228 */ u32 init_unknown;
+ /* 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];
++ /* 1144/1244 */ u32 _unknown_1144[11];
++ /* 1170/1270 */ u32 spi_addr;
++ /* 1174/1274 */ u32 _unknown_1174[11];
+ /* 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) // TODO implement heartbeat
+-#define DEVCFG_SPI_HEARTBEAT_INTERVAL(x) (((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) (((x) >> 28) & 7) // TODO use this
+-#define DEVCFG_SPI_USE_EXT_READ_CFG BIT(31) // TODO use this?
+-
+-struct ithc_device_config { // (Example values are from an SP7+.)
+- 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 (this value looks more random on newer devices)
+- u32 _unknown_28; // 28 = 0x00000000
+- u32 fw_mode; // 2c = 0x00000000 (for fw update?)
+- 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);
++
++void ithc_set_ltr_config(struct ithc *ithc, u64 active_ltr_ns, u64 idle_ltr_ns);
++void ithc_set_ltr_idle(struct ithc *ithc);
++int ithc_set_spi_config(struct ithc *ithc, u8 clkdiv, bool clkdiv8, u8 read_mode, u8 write_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
+index 028e55a4ec53e..e90c380444325 100644
+--- a/drivers/hid/ithc/ithc.h
++++ b/drivers/hid/ithc/ithc.h
+@@ -1,20 +1,19 @@
+ /* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+
+-#include <linux/module.h>
+-#include <linux/input.h>
+-#include <linux/hid.h>
++#include <linux/acpi.h>
++#include <linux/debugfs.h>
++#include <linux/delay.h>
+ #include <linux/dma-mapping.h>
++#include <linux/hid.h>
+ #include <linux/highmem.h>
+-#include <linux/pci.h>
++#include <linux/input.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/module.h>
++#include <linux/pci.h>
+ #include <linux/poll.h>
+-#include <linux/timer.h>
+-#include <linux/pm_qos.h>
+
+ #define DEVNAME "ithc"
+ #define DEVFULLNAME "Intel Touch Host Controller"
+@@ -27,10 +26,37 @@
+
+ #define NUM_RX_BUF 16
+
++// PCI device IDs:
++// 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
++
+ struct ithc;
+
+ #include "ithc-regs.h"
++#include "ithc-hid.h"
+ #include "ithc-dma.h"
++#include "ithc-legacy.h"
++#include "ithc-quickspi.h"
++#include "ithc-debug.h"
+
+ struct ithc {
+ char phys[32];
+@@ -38,30 +64,21 @@ struct ithc {
+ int irq;
+ struct task_struct *poll_thread;
+
+- struct pm_qos_request activity_qos;
+- struct hrtimer activity_start_timer;
+- struct hrtimer activity_end_timer;
+- ktime_t last_rx_time;
+- unsigned int cur_rx_seq_count;
+- unsigned int cur_rx_seq_errors;
+-
+- 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;
++ struct ithc_hid hid;
++
++ bool use_quickspi;
++ bool have_config;
++ u16 vendor_id;
++ u16 product_id;
++ u32 product_rev;
++ u32 max_rx_size;
++ u32 max_tx_size;
++ u32 legacy_touch_cfg;
+ };
+
+ int ithc_reset(struct ithc *ithc);
+-void ithc_set_active(struct ithc *ithc, unsigned int duration_us);
+-int ithc_debug_init(struct ithc *ithc);
+-void ithc_log_regs(struct ithc *ithc);
+
+--
+2.45.1
+
+From b4563a0da733d5759de36d9e555ce81324dca286 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Thu, 9 May 2024 16:15:49 +0200
+Subject: [PATCH] serial: Clear UPF_DEAD before calling
+ tty_port_register_device_attr_serdev()
+
+If a serdev_device_driver is already loaded for a serdev_tty_port when it
+gets registered by tty_port_register_device_attr_serdev() then that
+driver's probe() method will be called immediately.
+
+The serdev_device_driver's probe() method should then be able to call
+serdev_device_open() successfully, but because UPF_DEAD is still dead
+serdev_device_open() will fail with -ENXIO in this scenario:
+
+ serdev_device_open()
+ ctrl->ops->open() /* this callback being ttyport_open() */
+ tty->ops->open() /* this callback being uart_open() */
+ tty_port_open()
+ port->ops->activate() /* this callback being uart_port_activate() */
+ Find bit UPF_DEAD is set in uport->flags and fail with errno -ENXIO.
+
+Fix this be clearing UPF_DEAD before tty_port_register_device_attr_serdev()
+note this only moves up the UPD_DEAD clearing a small bit, before:
+
+ tty_port_register_device_attr_serdev();
+ mutex_unlock(&tty_port.mutex);
+ uart_port.flags &= ~UPF_DEAD;
+ mutex_unlock(&port_mutex);
+
+after:
+
+ uart_port.flags &= ~UPF_DEAD;
+ tty_port_register_device_attr_serdev();
+ mutex_unlock(&tty_port.mutex);
+ mutex_unlock(&port_mutex);
+
+Reported-by: Weifeng Liu <weifeng.liu.z@gmail.com>
+Closes: https://lore.kernel.org/platform-driver-x86/20240505130800.2546640-1-weifeng.liu.z@gmail.com/
+Tested-by: Weifeng Liu <weifeng.liu.z@gmail.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://lore.kernel.org/r/20240509141549.63704-1-hdegoede@redhat.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Patchset: surface-sam
+---
+ drivers/tty/serial/serial_core.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
+index c476d884356db..b47a277978a0b 100644
+--- a/drivers/tty/serial/serial_core.c
++++ b/drivers/tty/serial/serial_core.c
+@@ -3211,6 +3211,9 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u
+ if (uport->attr_group)
+ uport->tty_groups[1] = uport->attr_group;
+
++ /* Ensure serdev drivers can call serdev_device_open() right away */
++ uport->flags &= ~UPF_DEAD;
++
+ /*
+ * Register the port whether it's detected or not. This allows
+ * setserial to be used to alter this port's parameters.
+@@ -3221,6 +3224,7 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u
+ if (!IS_ERR(tty_dev)) {
+ device_set_wakeup_capable(tty_dev, 1);
+ } else {
++ uport->flags |= UPF_DEAD;
+ dev_err(uport->dev, "Cannot register tty device on line %d\n",
+ uport->line);
+ }
+@@ -3426,8 +3430,6 @@ int serial_core_register_port(struct uart_driver *drv, struct uart_port *port)
+ if (ret)
+ goto err_unregister_port_dev;
+
+- port->flags &= ~UPF_DEAD;
+-
+ mutex_unlock(&port_mutex);
+
+ return 0;
+--
+2.45.1
+
+From 0864ded554efe90dd9603b61e82c604481ee5125 Mon Sep 17 00:00:00 2001
+From: Weifeng Liu <weifeng.liu.z@gmail.com>
+Date: Sun, 5 May 2024 21:07:50 +0800
+Subject: [PATCH] platform/surface: aggregator: Log critical errors during SAM
+ probing
+
+Emits messages upon errors during probing of SAM. Hopefully this could
+provide useful context to user for the purpose of diagnosis when
+something miserable happen.
+
+Reviewed-by: Maximilian Luz <luzmaximilian@gmail.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Signed-off-by: Weifeng Liu <weifeng.liu.z@gmail.com>
+Link: https://lore.kernel.org/r/20240505130800.2546640-3-weifeng.liu.z@gmail.com
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Patchset: surface-sam
+---
+ drivers/platform/surface/aggregator/core.c | 42 ++++++++++++++--------
+ 1 file changed, 28 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/platform/surface/aggregator/core.c b/drivers/platform/surface/aggregator/core.c
+index ba550eaa06fcf..797d0645bd77f 100644
+--- a/drivers/platform/surface/aggregator/core.c
++++ b/drivers/platform/surface/aggregator/core.c
+@@ -618,15 +618,17 @@ static const struct acpi_gpio_mapping ssam_acpi_gpios[] = {
+
+ static int ssam_serial_hub_probe(struct serdev_device *serdev)
+ {
+- struct acpi_device *ssh = ACPI_COMPANION(&serdev->dev);
++ struct device *dev = &serdev->dev;
++ struct acpi_device *ssh = ACPI_COMPANION(dev);
+ struct ssam_controller *ctrl;
+ acpi_status astatus;
+ int status;
+
+- if (gpiod_count(&serdev->dev, NULL) < 0)
+- return -ENODEV;
++ status = gpiod_count(dev, NULL);
++ if (status < 0)
++ return dev_err_probe(dev, status, "no GPIO found\n");
+
+- status = devm_acpi_dev_add_driver_gpios(&serdev->dev, ssam_acpi_gpios);
++ status = devm_acpi_dev_add_driver_gpios(dev, ssam_acpi_gpios);
+ if (status)
+ return status;
+
+@@ -637,8 +639,10 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
+
+ /* Initialize controller. */
+ status = ssam_controller_init(ctrl, serdev);
+- if (status)
++ if (status) {
++ dev_err_probe(dev, status, "failed to initialize ssam controller\n");
+ goto err_ctrl_init;
++ }
+
+ ssam_controller_lock(ctrl);
+
+@@ -646,12 +650,14 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
+ serdev_device_set_drvdata(serdev, ctrl);
+ serdev_device_set_client_ops(serdev, &ssam_serdev_ops);
+ status = serdev_device_open(serdev);
+- if (status)
++ if (status) {
++ dev_err_probe(dev, status, "failed to open serdev device\n");
+ goto err_devopen;
++ }
+
+ astatus = ssam_serdev_setup_via_acpi(ssh->handle, serdev);
+ if (ACPI_FAILURE(astatus)) {
+- status = -ENXIO;
++ status = dev_err_probe(dev, -ENXIO, "failed to setup serdev\n");
+ goto err_devinit;
+ }
+
+@@ -667,25 +673,33 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
+ * states.
+ */
+ status = ssam_log_firmware_version(ctrl);
+- if (status)
++ if (status) {
++ dev_err_probe(dev, status, "failed to get firmware version\n");
+ goto err_initrq;
++ }
+
+ status = ssam_ctrl_notif_d0_entry(ctrl);
+- if (status)
++ if (status) {
++ dev_err_probe(dev, status, "D0-entry notification failed\n");
+ goto err_initrq;
++ }
+
+ status = ssam_ctrl_notif_display_on(ctrl);
+- if (status)
++ if (status) {
++ dev_err_probe(dev, status, "display-on notification failed\n");
+ goto err_initrq;
++ }
+
+- status = sysfs_create_group(&serdev->dev.kobj, &ssam_sam_group);
++ status = sysfs_create_group(&dev->kobj, &ssam_sam_group);
+ if (status)
+ goto err_initrq;
+
+ /* Set up IRQ. */
+ status = ssam_irq_setup(ctrl);
+- if (status)
++ if (status) {
++ dev_err_probe(dev, status, "failed to setup IRQ\n");
+ goto err_irq;
++ }
+
+ /* Finally, set main controller reference. */
+ status = ssam_try_set_controller(ctrl);
+@@ -702,7 +716,7 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
+ * resumed. In short, this causes some spurious unwanted wake-ups.
+ * For now let's thus default power/wakeup to false.
+ */
+- device_set_wakeup_capable(&serdev->dev, true);
++ device_set_wakeup_capable(dev, true);
+ acpi_dev_clear_dependencies(ssh);
+
+ return 0;
+@@ -710,7 +724,7 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
+ err_mainref:
+ ssam_irq_free(ctrl);
+ err_irq:
+- sysfs_remove_group(&serdev->dev.kobj, &ssam_sam_group);
++ sysfs_remove_group(&dev->kobj, &ssam_sam_group);
+ err_initrq:
+ ssam_controller_lock(ctrl);
+ ssam_controller_shutdown(ctrl);
+--
+2.45.1
+
+From d44985653441ba783a830bd5efde2fcf3a3ea271 Mon Sep 17 00:00:00 2001
+From: Maximilian Luz <luzmaximilian@gmail.com>
+Date: Fri, 19 Apr 2024 20:41:47 +0200
+Subject: [PATCH] platform/surface: aggregator: Fix warning when controller is
+ destroyed in probe
+
+There is a small window in ssam_serial_hub_probe() where the controller
+is initialized but has not been started yet. Specifically, between
+ssam_controller_init() and ssam_controller_start(). Any failure in this
+window, for example caused by a failure of serdev_device_open(),
+currently results in an incorrect warning being emitted.
+
+In particular, any failure in this window results in the controller
+being destroyed via ssam_controller_destroy(). This function checks the
+state of the controller and, in an attempt to validate that the
+controller has been cleanly shut down before we try and deallocate any
+resources, emits a warning if that state is not SSAM_CONTROLLER_STOPPED.
+
+However, since we have only just initialized the controller and have not
+yet started it, its state is SSAM_CONTROLLER_INITIALIZED. Note that this
+is the only point at which the controller has this state, as it will
+change after we start the controller with ssam_controller_start() and
+never revert back. Further, at this point no communication has taken
+place and the sender and receiver threads have not been started yet (and
+we may not even have an open serdev device either).
+
+Therefore, it is perfectly safe to call ssam_controller_destroy() with a
+state of SSAM_CONTROLLER_INITIALIZED. This, however, means that the
+warning currently being emitted is incorrect. Fix it by extending the
+check.
+
+Fixes: c167b9c7e3d6 ("platform/surface: Add Surface Aggregator subsystem")
+Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
+Patchset: surface-sam
+---
+ drivers/platform/surface/aggregator/controller.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/platform/surface/aggregator/controller.c b/drivers/platform/surface/aggregator/controller.c
+index 7fc602e01487d..7e89f547999b2 100644
+--- a/drivers/platform/surface/aggregator/controller.c
++++ b/drivers/platform/surface/aggregator/controller.c
+@@ -1354,7 +1354,8 @@ void ssam_controller_destroy(struct ssam_controller *ctrl)
+ if (ctrl->state == SSAM_CONTROLLER_UNINITIALIZED)
+ return;
+
+- WARN_ON(ctrl->state != SSAM_CONTROLLER_STOPPED);
++ WARN_ON(ctrl->state != SSAM_CONTROLLER_STOPPED &&
++ ctrl->state != SSAM_CONTROLLER_INITIALIZED);
+
+ /*
+ * Note: New events could still have been received after the previous
+--
+2.45.1
+
+From 6b6f860bbef0ba3f10f8dc151ac4e27d0a34c223 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Sun, 22 Oct 2023 14:57:11 +0200
Subject: [PATCH] platform/surface: aggregator_registry: Add support for
@@ -5695,10 +8577,10 @@ Patchset: surface-sam
1 file changed, 3 insertions(+)
diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c
-index aeb3feae40ff..2bc4977037fc 100644
+index 035d6b4105cd6..74688a2ed4b2e 100644
--- a/drivers/platform/surface/surface_aggregator_registry.c
+++ b/drivers/platform/surface/surface_aggregator_registry.c
-@@ -367,6 +367,9 @@ static const struct acpi_device_id ssam_platform_hub_match[] = {
+@@ -374,6 +374,9 @@ static const struct acpi_device_id ssam_platform_hub_match[] = {
/* Surface Laptop Go 2 */
{ "MSHW0290", (unsigned long)ssam_node_group_slg1 },
@@ -5709,9 +8591,9 @@ index aeb3feae40ff..2bc4977037fc 100644
{ "MSHW0123", (unsigned long)ssam_node_group_sls },
--
-2.44.0
+2.45.1
-From 7dedc84364f9c1fb73d271c859dce19b4a9644d6 Mon Sep 17 00:00:00 2001
+From 31b312c25822404e52a81de2525da5c7bae12864 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Mon, 20 Nov 2023 19:47:00 +0100
Subject: [PATCH] platform/surface: aggregator_registry: Add support for
@@ -5729,10 +8611,10 @@ Patchset: surface-sam
1 file changed, 21 insertions(+), 4 deletions(-)
diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c
-index 2bc4977037fc..26cb6229ad16 100644
+index 74688a2ed4b2e..f02a933160ff2 100644
--- a/drivers/platform/surface/surface_aggregator_registry.c
+++ b/drivers/platform/surface/surface_aggregator_registry.c
-@@ -247,8 +247,8 @@ static const struct software_node *ssam_node_group_sl5[] = {
+@@ -253,8 +253,8 @@ static const struct software_node *ssam_node_group_sl5[] = {
NULL,
};
@@ -5743,7 +8625,7 @@ index 2bc4977037fc..26cb6229ad16 100644
&ssam_node_root,
&ssam_node_bat_ac,
&ssam_node_bat_main,
-@@ -263,6 +263,20 @@ static const struct software_node *ssam_node_group_sls[] = {
+@@ -269,6 +269,20 @@ static const struct software_node *ssam_node_group_sls[] = {
NULL,
};
@@ -5764,7 +8646,7 @@ index 2bc4977037fc..26cb6229ad16 100644
/* Devices for Surface Laptop Go. */
static const struct software_node *ssam_node_group_slg1[] = {
&ssam_node_root,
-@@ -370,8 +384,11 @@ static const struct acpi_device_id ssam_platform_hub_match[] = {
+@@ -377,8 +391,11 @@ static const struct acpi_device_id ssam_platform_hub_match[] = {
/* Surface Laptop Go 3 */
{ "MSHW0440", (unsigned long)ssam_node_group_slg1 },
@@ -5779,277 +8661,66 @@ index 2bc4977037fc..26cb6229ad16 100644
{ },
};
--
-2.44.0
+2.45.1
-From 96a7b3dd527f3e1b97e2d089a727c16cd5045aa5 Mon Sep 17 00:00:00 2001
-From: Ivor Wanders <ivor@iwanders.net>
-Date: Mon, 18 Dec 2023 19:21:32 -0500
-Subject: [PATCH] platform/surface: aggregator_registry: add entry for fan
- speed
+From 42f6d14bda5e69c2b5a8d27cfcbd063a5922f876 Mon Sep 17 00:00:00 2001
+From: Maximilian Luz <luzmaximilian@gmail.com>
+Date: Sun, 9 Jun 2024 20:05:57 +0200
+Subject: [PATCH] platform/surface: aggregator_registry: Add support for
+ Surface Laptop 6
-Add an entry for the fan speed function.
-Add this new entry to the Surface Pro 9 group.
+Add SAM client device nodes for the Surface Laptop Studio 6 (SL6). The
+SL6 is similar to the SL5, with the typical battery/AC, platform
+profile, and HID nodes. It also has support for the newly supported fan
+interface.
-Signed-off-by: Ivor Wanders <ivor@iwanders.net>
-Link: https://github.com/linux-surface/kernel/pull/144
-Reviewed-by: Maximilian Luz <luzmaximilian@gmail.com>
+Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
Patchset: surface-sam
---
- drivers/platform/surface/surface_aggregator_registry.c | 7 +++++++
- 1 file changed, 7 insertions(+)
+ .../surface/surface_aggregator_registry.c | 19 +++++++++++++++++++
+ 1 file changed, 19 insertions(+)
diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c
-index 26cb6229ad16..f02a933160ff 100644
+index f02a933160ff2..34df1bdad83bd 100644
--- a/drivers/platform/surface/surface_aggregator_registry.c
+++ b/drivers/platform/surface/surface_aggregator_registry.c
-@@ -74,6 +74,12 @@ static const struct software_node ssam_node_tmp_pprof = {
- .parent = &ssam_node_root,
+@@ -253,6 +253,22 @@ static const struct software_node *ssam_node_group_sl5[] = {
+ NULL,
};
-+/* Fan speed function. */
-+static const struct software_node ssam_node_fan_speed = {
-+ .name = "ssam:01:05:01:01:01",
-+ .parent = &ssam_node_root,
-+};
-+
- /* Tablet-mode switch via KIP subsystem. */
- static const struct software_node ssam_node_kip_tablet_switch = {
- .name = "ssam:01:0e:01:00:01",
-@@ -319,6 +325,7 @@ static const struct software_node *ssam_node_group_sp9[] = {
- &ssam_node_bat_ac,
- &ssam_node_bat_main,
- &ssam_node_tmp_pprof,
++/* Devices for Surface Laptop 6. */
++static const struct software_node *ssam_node_group_sl6[] = {
++ &ssam_node_root,
++ &ssam_node_bat_ac,
++ &ssam_node_bat_main,
++ &ssam_node_tmp_perf_profile_with_fan,
++ &ssam_node_tmp_sensors,
+ &ssam_node_fan_speed,
- &ssam_node_pos_tablet_switch,
- &ssam_node_hid_kip_keyboard,
- &ssam_node_hid_kip_penstash,
---
-2.44.0
-
-From b4eb65349df9859f65aa9990c24c5036d35382da Mon Sep 17 00:00:00 2001
-From: Ivor Wanders <ivor@iwanders.net>
-Date: Thu, 30 Nov 2023 20:20:24 -0500
-Subject: [PATCH] hwmon: add fan speed monitoring driver for Surface devices
-
-Adds a driver that provides read only access to the fan speed for Microsoft
-Surface Pro devices. The fan speed is always regulated by the EC and cannot
-be influenced directly.
-
-Signed-off-by: Ivor Wanders <ivor@iwanders.net>
-Link: https://github.com/linux-surface/kernel/pull/144
-Patchset: surface-sam
----
- Documentation/hwmon/index.rst | 1 +
- Documentation/hwmon/surface_fan.rst | 25 ++++++++
- MAINTAINERS | 8 +++
- drivers/hwmon/Kconfig | 13 ++++
- drivers/hwmon/Makefile | 1 +
- drivers/hwmon/surface_fan.c | 93 +++++++++++++++++++++++++++++
- 6 files changed, 141 insertions(+)
- create mode 100644 Documentation/hwmon/surface_fan.rst
- create mode 100644 drivers/hwmon/surface_fan.c
-
-diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
-index c7ed1f73ac06..58be92e94a8d 100644
---- a/Documentation/hwmon/index.rst
-+++ b/Documentation/hwmon/index.rst
-@@ -208,6 +208,7 @@ Hardware Monitoring Kernel Drivers
- smsc47m1
- sparx5-temp
- stpddc60
-+ surface_fan
- sy7636a-hwmon
- tc654
- tc74
-diff --git a/Documentation/hwmon/surface_fan.rst b/Documentation/hwmon/surface_fan.rst
-new file mode 100644
-index 000000000000..07942574c4f0
---- /dev/null
-+++ b/Documentation/hwmon/surface_fan.rst
-@@ -0,0 +1,25 @@
-+.. SPDX-License-Identifier: GPL-2.0-or-later
-+
-+Kernel driver surface_fan
-+=========================
-+
-+Supported Devices:
-+
-+ * Microsoft Surface Pro 9
-+
-+Author: Ivor Wanders <ivor@iwanders.net>
-+
-+Description
-+-----------
-+
-+This provides monitoring of the fan found in some Microsoft Surface Pro devices,
-+like the Surface Pro 9. The fan is always controlled by the onboard controller.
-+
-+Sysfs interface
-+---------------
-+
-+======================= ======= =========================================
-+Name Perm Description
-+======================= ======= =========================================
-+``fan1_input`` RO Current fan speed in RPM.
-+======================= ======= =========================================
-diff --git a/MAINTAINERS b/MAINTAINERS
-index 1aabf1c15bb3..b6416cf3f022 100644
---- a/MAINTAINERS
-+++ b/MAINTAINERS
-@@ -14576,6 +14576,14 @@ F: Documentation/driver-api/surface_aggregator/clients/dtx.rst
- F: drivers/platform/surface/surface_dtx.c
- F: include/uapi/linux/surface_aggregator/dtx.h
-
-+MICROSOFT SURFACE SENSOR FAN DRIVER
-+M: Maximilian Luz <luzmaximilian@gmail.com>
-+M: Ivor Wanders <ivor@iwanders.net>
-+L: linux-hwmon@vger.kernel.org
-+S: Maintained
-+F: Documentation/hwmon/surface_fan.rst
-+F: drivers/hwmon/surface_fan.c
-+
- MICROSOFT SURFACE GPE LID SUPPORT DRIVER
- M: Maximilian Luz <luzmaximilian@gmail.com>
- L: platform-driver-x86@vger.kernel.org
-diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
-index a608264da87d..e762f6138970 100644
---- a/drivers/hwmon/Kconfig
-+++ b/drivers/hwmon/Kconfig
-@@ -1994,6 +1994,19 @@ config SENSORS_SFCTEMP
- This driver can also be built as a module. If so, the module
- will be called sfctemp.
-
-+config SENSORS_SURFACE_FAN
-+ tristate "Surface Fan Driver"
-+ depends on SURFACE_AGGREGATOR
-+ help
-+ Driver that provides monitoring of the fan on Surface Pro devices that
-+ have a fan, like the Surface Pro 9.
-+
-+ This makes the fan's current speed accessible through the hwmon
-+ system. It does not provide control over the fan, the firmware is
-+ responsible for that, this driver merely provides monitoring.
-+
-+ Select M or Y here, if you want to be able to read the fan's speed.
-+
- config SENSORS_ADC128D818
- tristate "Texas Instruments ADC128D818"
- depends on I2C
-diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
-index 47be39af5c03..30cc90f40844 100644
---- a/drivers/hwmon/Makefile
-+++ b/drivers/hwmon/Makefile
-@@ -201,6 +201,7 @@ obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
- obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
- obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o
- obj-$(CONFIG_SENSORS_STTS751) += stts751.o
-+obj-$(CONFIG_SENSORS_SURFACE_FAN)+= surface_fan.o
- obj-$(CONFIG_SENSORS_SY7636A) += sy7636a-hwmon.o
- obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o
- obj-$(CONFIG_SENSORS_TC74) += tc74.o
-diff --git a/drivers/hwmon/surface_fan.c b/drivers/hwmon/surface_fan.c
-new file mode 100644
-index 000000000000..7c2e3ae3eb40
---- /dev/null
-+++ b/drivers/hwmon/surface_fan.c
-@@ -0,0 +1,93 @@
-+// SPDX-License-Identifier: GPL-2.0+
-+/*
-+ * Surface Fan driver for Surface System Aggregator Module. It provides access
-+ * to the fan's rpm through the hwmon system.
-+ *
-+ * Copyright (C) 2023 Ivor Wanders <ivor@iwanders.net>
-+ */
-+
-+#include <linux/hwmon.h>
-+#include <linux/kernel.h>
-+#include <linux/module.h>
-+#include <linux/surface_aggregator/device.h>
-+#include <linux/types.h>
-+
-+// SSAM
-+SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_fan_rpm_get, __le16, {
-+ .target_category = SSAM_SSH_TC_FAN,
-+ .command_id = 0x01,
-+});
-+
-+// hwmon
-+umode_t surface_fan_hwmon_is_visible(const void *drvdata,
-+ enum hwmon_sensor_types type, u32 attr,
-+ int channel)
-+{
-+ return 0444;
-+}
-+
-+static int surface_fan_hwmon_read(struct device *dev,
-+ enum hwmon_sensor_types type, u32 attr,
-+ int channel, long *val)
-+{
-+ struct ssam_device *sdev = dev_get_drvdata(dev);
-+ int ret;
-+ __le16 value;
-+
-+ ret = __ssam_fan_rpm_get(sdev, &value);
-+ if (ret)
-+ return ret;
-+
-+ *val = le16_to_cpu(value);
-+
-+ return ret;
-+}
-+
-+static const struct hwmon_channel_info *const surface_fan_info[] = {
-+ HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
-+ NULL
-+};
-+
-+static const struct hwmon_ops surface_fan_hwmon_ops = {
-+ .is_visible = surface_fan_hwmon_is_visible,
-+ .read = surface_fan_hwmon_read,
-+};
-+
-+static const struct hwmon_chip_info surface_fan_chip_info = {
-+ .ops = &surface_fan_hwmon_ops,
-+ .info = surface_fan_info,
-+};
-+
-+static int surface_fan_probe(struct ssam_device *sdev)
-+{
-+ struct device *hdev;
-+
-+ hdev = devm_hwmon_device_register_with_info(&sdev->dev,
-+ "surface_fan", sdev,
-+ &surface_fan_chip_info,
-+ NULL);
-+ if (IS_ERR(hdev))
-+ return PTR_ERR(hdev);
-+
-+ return 0;
-+}
-+
-+static const struct ssam_device_id ssam_fan_match[] = {
-+ { SSAM_SDEV(FAN, SAM, 0x01, 0x01) },
-+ {},
++ &ssam_node_hid_main_keyboard,
++ &ssam_node_hid_main_touchpad,
++ &ssam_node_hid_main_iid5,
++ &ssam_node_hid_sam_sensors,
++ &ssam_node_hid_sam_ucm_ucsi,
++ NULL,
+};
-+MODULE_DEVICE_TABLE(ssam, ssam_fan_match);
+
-+static struct ssam_device_driver surface_fan = {
-+ .probe = surface_fan_probe,
-+ .match_table = ssam_fan_match,
-+ .driver = {
-+ .name = "surface_fan",
-+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
-+ },
-+};
-+module_ssam_device_driver(surface_fan);
+ /* Devices for Surface Laptop Studio 1. */
+ static const struct software_node *ssam_node_group_sls1[] = {
+ &ssam_node_root,
+@@ -382,6 +398,9 @@ static const struct acpi_device_id ssam_platform_hub_match[] = {
+ /* Surface Laptop 5 */
+ { "MSHW0350", (unsigned long)ssam_node_group_sl5 },
+
++ /* Surface Laptop 6 */
++ { "MSHW0530", (unsigned long)ssam_node_group_sl5 },
+
-+MODULE_AUTHOR("Ivor Wanders <ivor@iwanders.net>");
-+MODULE_DESCRIPTION("Fan Driver for Surface System Aggregator Module");
-+MODULE_LICENSE("GPL");
+ /* Surface Laptop Go 1 */
+ { "MSHW0118", (unsigned long)ssam_node_group_slg1 },
+
--
-2.44.0
+2.45.1
-From 5c18bed9c7ad073b61e3c3686dc4bc1858f958dc Mon Sep 17 00:00:00 2001
+From 88fda328aea3bb7077cd39f854148276dcffcea3 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Sat, 30 Dec 2023 18:07:54 +0100
Subject: [PATCH] hwmon: Add thermal sensor driver for Surface Aggregator
@@ -6071,10 +8742,10 @@ Patchset: surface-sam
create mode 100644 drivers/hwmon/surface_temp.c
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
-index e762f6138970..41261b49f8be 100644
+index 83945397b6eb1..338ef73c96a3a 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
-@@ -2007,6 +2007,16 @@ config SENSORS_SURFACE_FAN
+@@ -2070,6 +2070,16 @@ config SENSORS_SURFACE_FAN
Select M or Y here, if you want to be able to read the fan's speed.
@@ -6092,10 +8763,10 @@ index e762f6138970..41261b49f8be 100644
tristate "Texas Instruments ADC128D818"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
-index 30cc90f40844..6644fd4598a4 100644
+index 5c31808f6378d..de8bc99719e63 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
-@@ -202,6 +202,7 @@ obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
+@@ -208,6 +208,7 @@ obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o
obj-$(CONFIG_SENSORS_STTS751) += stts751.o
obj-$(CONFIG_SENSORS_SURFACE_FAN)+= surface_fan.o
@@ -6105,7 +8776,7 @@ index 30cc90f40844..6644fd4598a4 100644
obj-$(CONFIG_SENSORS_TC74) += tc74.o
diff --git a/drivers/hwmon/surface_temp.c b/drivers/hwmon/surface_temp.c
new file mode 100644
-index 000000000000..48c3e826713f
+index 0000000000000..48c3e826713f6
--- /dev/null
+++ b/drivers/hwmon/surface_temp.c
@@ -0,0 +1,165 @@
@@ -6275,9 +8946,9 @@ index 000000000000..48c3e826713f
+MODULE_DESCRIPTION("Thermal sensor subsystem driver for Surface System Aggregator Module");
+MODULE_LICENSE("GPL");
--
-2.44.0
+2.45.1
-From 8b1e37ad9423a6fdf8a8b3bfb1e15aadefe136de Mon Sep 17 00:00:00 2001
+From 17f0ec6ef1bc95e152af3a9f2b05ea669c75d24a Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Sat, 30 Dec 2023 18:12:23 +0100
Subject: [PATCH] hwmon: surface_temp: Add support for sensor names
@@ -6295,7 +8966,7 @@ Patchset: surface-sam
1 file changed, 96 insertions(+), 17 deletions(-)
diff --git a/drivers/hwmon/surface_temp.c b/drivers/hwmon/surface_temp.c
-index 48c3e826713f..4c08926139db 100644
+index 48c3e826713f6..4c08926139dbf 100644
--- a/drivers/hwmon/surface_temp.c
+++ b/drivers/hwmon/surface_temp.c
@@ -17,6 +17,27 @@
@@ -6470,9 +9141,9 @@ index 48c3e826713f..4c08926139db 100644
"surface_thermal", ssam_temp, &ssam_temp_hwmon_chip_info,
NULL);
--
-2.44.0
+2.45.1
-From 9b490a17f59518e59cf3c77c103bf5ddc52041a3 Mon Sep 17 00:00:00 2001
+From 54bfa02fe865b9f22d79b112a5244ce81e4961f1 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Sat, 30 Dec 2023 18:21:12 +0100
Subject: [PATCH] platform/surface: aggregator_registry: Add support for
@@ -6488,7 +9159,7 @@ Patchset: surface-sam
1 file changed, 7 insertions(+)
diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c
-index f02a933160ff..67686042e009 100644
+index 34df1bdad83bd..c0bf0cadcd258 100644
--- a/drivers/platform/surface/surface_aggregator_registry.c
+++ b/drivers/platform/surface/surface_aggregator_registry.c
@@ -74,6 +74,12 @@ static const struct software_node ssam_node_tmp_pprof = {
@@ -6504,7 +9175,7 @@ index f02a933160ff..67686042e009 100644
/* Fan speed function. */
static const struct software_node ssam_node_fan_speed = {
.name = "ssam:01:05:01:01:01",
-@@ -325,6 +331,7 @@ static const struct software_node *ssam_node_group_sp9[] = {
+@@ -341,6 +347,7 @@ static const struct software_node *ssam_node_group_sp9[] = {
&ssam_node_bat_ac,
&ssam_node_bat_main,
&ssam_node_tmp_pprof,
@@ -6513,9 +9184,9 @@ index f02a933160ff..67686042e009 100644
&ssam_node_pos_tablet_switch,
&ssam_node_hid_kip_keyboard,
--
-2.44.0
+2.45.1
-From ec0b680c0ce36ef6bffd682e10589cf5d8d467ae Mon Sep 17 00:00:00 2001
+From 06c4b5ac6b6357227e45c53643729140d794b48d Mon Sep 17 00:00:00 2001
From: Ivor Wanders <ivor@iwanders.net>
Date: Sat, 16 Dec 2023 15:56:39 -0500
Subject: [PATCH] platform/surface: platform_profile: add fan profile switching
@@ -6532,7 +9203,7 @@ Patchset: surface-sam
2 files changed, 100 insertions(+), 24 deletions(-)
diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c
-index 67686042e009..058b6654a91a 100644
+index c0bf0cadcd258..07a4c4e1120d3 100644
--- a/drivers/platform/surface/surface_aggregator_registry.c
+++ b/drivers/platform/surface/surface_aggregator_registry.c
@@ -68,8 +68,8 @@ static const struct software_node ssam_node_bat_sb3base = {
@@ -6603,7 +9274,7 @@ index 67686042e009..058b6654a91a 100644
&ssam_node_hid_main_keyboard,
&ssam_node_hid_main_touchpad,
&ssam_node_hid_main_iid5,
-@@ -264,7 +278,7 @@ static const struct software_node *ssam_node_group_sls1[] = {
+@@ -280,7 +294,7 @@ static const struct software_node *ssam_node_group_sls1[] = {
&ssam_node_root,
&ssam_node_bat_ac,
&ssam_node_bat_main,
@@ -6612,7 +9283,7 @@ index 67686042e009..058b6654a91a 100644
&ssam_node_pos_tablet_switch,
&ssam_node_hid_sam_keyboard,
&ssam_node_hid_sam_penstash,
-@@ -280,7 +294,7 @@ static const struct software_node *ssam_node_group_sls2[] = {
+@@ -296,7 +310,7 @@ static const struct software_node *ssam_node_group_sls2[] = {
&ssam_node_root,
&ssam_node_bat_ac,
&ssam_node_bat_main,
@@ -6621,7 +9292,7 @@ index 67686042e009..058b6654a91a 100644
&ssam_node_pos_tablet_switch,
&ssam_node_hid_sam_keyboard,
&ssam_node_hid_sam_penstash,
-@@ -294,7 +308,7 @@ static const struct software_node *ssam_node_group_slg1[] = {
+@@ -310,7 +324,7 @@ static const struct software_node *ssam_node_group_slg1[] = {
&ssam_node_root,
&ssam_node_bat_ac,
&ssam_node_bat_main,
@@ -6630,7 +9301,7 @@ index 67686042e009..058b6654a91a 100644
NULL,
};
-@@ -303,7 +317,7 @@ static const struct software_node *ssam_node_group_sp7[] = {
+@@ -319,7 +333,7 @@ static const struct software_node *ssam_node_group_sp7[] = {
&ssam_node_root,
&ssam_node_bat_ac,
&ssam_node_bat_main,
@@ -6639,7 +9310,7 @@ index 67686042e009..058b6654a91a 100644
NULL,
};
-@@ -313,7 +327,7 @@ static const struct software_node *ssam_node_group_sp8[] = {
+@@ -329,7 +343,7 @@ static const struct software_node *ssam_node_group_sp8[] = {
&ssam_node_hub_kip,
&ssam_node_bat_ac,
&ssam_node_bat_main,
@@ -6648,7 +9319,7 @@ index 67686042e009..058b6654a91a 100644
&ssam_node_kip_tablet_switch,
&ssam_node_hid_kip_keyboard,
&ssam_node_hid_kip_penstash,
-@@ -330,7 +344,7 @@ static const struct software_node *ssam_node_group_sp9[] = {
+@@ -346,7 +360,7 @@ static const struct software_node *ssam_node_group_sp9[] = {
&ssam_node_hub_kip,
&ssam_node_bat_ac,
&ssam_node_bat_main,
@@ -6658,7 +9329,7 @@ index 67686042e009..058b6654a91a 100644
&ssam_node_fan_speed,
&ssam_node_pos_tablet_switch,
diff --git a/drivers/platform/surface/surface_platform_profile.c b/drivers/platform/surface/surface_platform_profile.c
-index a5a3941b3f43..e54d0a8f7daa 100644
+index a5a3941b3f43a..e54d0a8f7daa5 100644
--- a/drivers/platform/surface/surface_platform_profile.c
+++ b/drivers/platform/surface/surface_platform_profile.c
@@ -1,7 +1,7 @@
@@ -6840,9 +9511,9 @@ index a5a3941b3f43..e54d0a8f7daa 100644
set_bit(PLATFORM_PROFILE_BALANCED, tpd->handler.choices);
set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, tpd->handler.choices);
--
-2.44.0
+2.45.1
-From 95b66fb97652988a7b4be5bb1deaa625e1bb3c3f Mon Sep 17 00:00:00 2001
+From bf55987e82f9ae913b51a6a269fc1a397930f049 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
@@ -6899,7 +9570,7 @@ Patchset: surface-sam-over-hid
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
+index d6037a3286690..a290ebc77aea2 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,
@@ -6952,9 +9623,9 @@ index d6037a328669..a290ebc77aea 100644
dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n",
accessor_type, client->addr);
--
-2.44.0
+2.45.1
-From 2b86ac312b956799265cdd1411d305cd2dcaf6db Mon Sep 17 00:00:00 2001
+From ebfda7ad73dd90971be10e9d6f59c51c781accbb 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
@@ -6977,7 +9648,7 @@ Patchset: surface-sam-over-hid
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
+index b629e82af97c0..68656e8f309ed 100644
--- a/drivers/platform/surface/Kconfig
+++ b/drivers/platform/surface/Kconfig
@@ -149,6 +149,13 @@ config SURFACE_AGGREGATOR_TABLET_SWITCH
@@ -6995,7 +9666,7 @@ index b629e82af97c..68656e8f309e 100644
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
+index 53344330939bf..7efcd0cdb5329 100644
--- a/drivers/platform/surface/Makefile
+++ b/drivers/platform/surface/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV) += surface_aggregator_cdev.o
@@ -7008,7 +9679,7 @@ index 53344330939b..7efcd0cdb532 100644
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
+index 0000000000000..8b816ed8f35c6
--- /dev/null
+++ b/drivers/platform/surface/surfacebook1_dgpu_switch.c
@@ -0,0 +1,162 @@
@@ -7175,9 +9846,9 @@ index 000000000000..8b816ed8f35c
+MODULE_DESCRIPTION("Discrete GPU Power-Switch for Surface Book 1");
+MODULE_LICENSE("GPL");
--
-2.44.0
+2.45.1
-From 3584a6c1791dc9c9b9c3ee846621571cbfabe37e Mon Sep 17 00:00:00 2001
+From 1e315f586b0b2bc375b96bb538a3be4c0b09d1ea 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
@@ -7199,7 +9870,7 @@ Patchset: surface-button
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 f6d060377d18..b8603f74eb28 100644
+index f6d060377d189..b8603f74eb286 100644
--- a/drivers/input/misc/soc_button_array.c
+++ b/drivers/input/misc/soc_button_array.c
@@ -540,8 +540,8 @@ static const struct soc_device_data soc_device_MSHW0028 = {
@@ -7252,9 +9923,9 @@ index f6d060377d18..b8603f74eb28 100644
/*
--
-2.44.0
+2.45.1
-From c26bb1d0af0fe40be270d203d6aaeab28dd04a10 Mon Sep 17 00:00:00 2001
+From ac551644781bce2145c901b16779114b273c4d49 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
@@ -7275,7 +9946,7 @@ Patchset: surface-button
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
+index 2755601f979cd..4240c98ca2265 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)
@@ -7324,582 +9995,9 @@ index 2755601f979c..4240c98ca226 100644
--
-2.44.0
-
-From b3fac417611f5bb4ae2a9bc9e828dacfe4418fbf 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 b4783574b8e6..360970620589 100644
---- a/drivers/usb/core/quirks.c
-+++ b/drivers/usb/core/quirks.c
-@@ -223,6 +223,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.44.0
-
-From bf5167d418b660e321368222505a97d9f1ed68b4 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 3e91e4d6ba6f..45b7884c97f0 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)
-@@ -1840,6 +1930,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);
-@@ -2230,6 +2321,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.44.0
-
-From dda4ca6e0248ae5acf3aaf78ee00e613f4e04bad 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 45b7884c97f0..f8978b405aca 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;
- }
-@@ -1908,13 +1974,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. */
-@@ -1923,12 +2000,31 @@ 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;
- }
-
- 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.44.0
+2.45.1
-From aa49dc59b192cc038ca789ac70215d7492f043cb Mon Sep 17 00:00:00 2001
+From 9e0b83c9668c3d0e8e5ce9c254697056940a205d 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
@@ -7924,10 +10022,10 @@ Patchset: surface-shutdown
3 files changed, 40 insertions(+)
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
-index 51ec9e7e784f..40554890d721 100644
+index af2996d0d17ff..3ce0fb61257dc 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)
+@@ -505,6 +505,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;
@@ -7938,10 +10036,10 @@ index 51ec9e7e784f..40554890d721 100644
if (drv && drv->shutdown)
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
-index d797df6e5f3e..c674ee496a0b 100644
+index eff7f5df08e27..d1cb4ff3ebc57 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
-@@ -6250,3 +6250,39 @@ static void pci_fixup_d3cold_delay_1sec(struct pci_dev *pdev)
+@@ -6253,3 +6253,39 @@ static void pci_fixup_d3cold_delay_1sec(struct pci_dev *pdev)
pdev->d3cold_delay = 1000;
}
DECLARE_PCI_FIXUP_FINAL(0x5555, 0x0004, pci_fixup_d3cold_delay_1sec);
@@ -7982,7 +10080,7 @@ index d797df6e5f3e..c674ee496a0b 100644
+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 7ab0d13672da..8d8d9225e0db 100644
+index 16493426a04ff..0eb821624056d 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -465,6 +465,7 @@ struct pci_dev {
@@ -7994,9 +10092,9 @@ index 7ab0d13672da..8d8d9225e0db 100644
atomic_t enable_cnt; /* pci_enable_device has been called */
--
-2.44.0
+2.45.1
-From 36cf5399fd0f16f10f97c21131d29ebda13607f5 Mon Sep 17 00:00:00 2001
+From 739ab84095d8ea8ec8fe05447706c6eb2ffa3f35 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
@@ -8010,7 +10108,7 @@ Patchset: surface-gpe
1 file changed, 17 insertions(+)
diff --git a/drivers/platform/surface/surface_gpe.c b/drivers/platform/surface/surface_gpe.c
-index 62fd4004db31..103fc4468262 100644
+index 62fd4004db31a..103fc4468262a 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[] = {
@@ -8045,9 +10143,9 @@ index 62fd4004db31..103fc4468262 100644
.ident = "Surface Book 1",
.matches = {
--
-2.44.0
+2.45.1
-From 526bea529e4befa282fcfd01bbadbed7325faf01 Mon Sep 17 00:00:00 2001
+From 44ed44fe421e484bcf2de223d7e8077302635772 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
@@ -8107,10 +10205,10 @@ Patchset: cameras
1 file changed, 3 insertions(+)
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
-index e6ed1ba91e5c..b367890b7438 100644
+index d1464324de951..5d865a34dd9db 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
-@@ -2138,6 +2138,9 @@ static acpi_status acpi_bus_check_add_2(acpi_handle handle, u32 lvl_not_used,
+@@ -2181,6 +2181,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)
{
@@ -8121,9 +10219,9 @@ index e6ed1ba91e5c..b367890b7438 100644
* Do not enumerate devices with enumeration_by_parent flag set as
* they will be enumerated by their respective parents.
--
-2.44.0
+2.45.1
-From d41ef92974135b1c22c2f46cbaba926701e0d4af Mon Sep 17 00:00:00 2001
+From d9d5afcea9880a957d6a8d975a122b4f54ec28c2 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
@@ -8149,10 +10247,10 @@ Patchset: cameras
1 file changed, 30 insertions(+)
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
-index 6c01b1aebf27..ceed043464b1 100644
+index 61bc54299a591..a61af0f4e9fce 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
-@@ -45,6 +45,13 @@
+@@ -44,6 +44,13 @@
((pdev)->vendor == PCI_VENDOR_ID_INTEL && (pdev)->device == 0x34E4) \
)
@@ -8166,23 +10264,23 @@ index 6c01b1aebf27..ceed043464b1 100644
#define IOAPIC_RANGE_START (0xfee00000)
#define IOAPIC_RANGE_END (0xfeefffff)
#define IOVA_START_ADDR (0x1000)
-@@ -154,12 +161,14 @@ EXPORT_SYMBOL_GPL(intel_iommu_enabled);
+@@ -227,12 +234,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 dmar_map_ipu = 1;
static int intel_iommu_superpage = 1;
static int iommu_identity_mapping;
static int iommu_skip_te_disable;
+ static int disable_igfx_iommu;
- #define IDENTMAP_GFX 2
#define IDENTMAP_AZALIA 4
+#define IDENTMAP_IPU 8
#define IDENTMAP_IPTS 16
const struct iommu_ops intel_iommu_ops;
-@@ -2420,6 +2429,9 @@ static int device_def_domain_type(struct device *dev)
- if ((iommu_identity_mapping & IDENTMAP_GFX) && IS_GFX_DEVICE(pdev))
+@@ -2409,6 +2418,9 @@ static int device_def_domain_type(struct device *dev)
+ if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(pdev))
return IOMMU_DOMAIN_IDENTITY;
+ if ((iommu_identity_mapping & IDENTMAP_IPU) && IS_INTEL_IPU(pdev))
@@ -8191,9 +10289,9 @@ index 6c01b1aebf27..ceed043464b1 100644
if ((iommu_identity_mapping & IDENTMAP_IPTS) && IS_IPTS(pdev))
return IOMMU_DOMAIN_IDENTITY;
}
-@@ -2729,6 +2741,9 @@ static int __init init_dmars(void)
- if (!dmar_map_gfx)
- iommu_identity_mapping |= IDENTMAP_GFX;
+@@ -2711,6 +2723,9 @@ static int __init init_dmars(void)
+ iommu_set_root_entry(iommu);
+ }
+ if (!dmar_map_ipu)
+ iommu_identity_mapping |= IDENTMAP_IPU;
@@ -8201,8 +10299,8 @@ index 6c01b1aebf27..ceed043464b1 100644
if (!dmar_map_ipts)
iommu_identity_mapping |= IDENTMAP_IPTS;
-@@ -4909,6 +4924,18 @@ static void quirk_iommu_igfx(struct pci_dev *dev)
- dmar_map_gfx = 0;
+@@ -4884,6 +4899,18 @@ static void quirk_iommu_igfx(struct pci_dev *dev)
+ disable_igfx_iommu = 1;
}
+static void quirk_iommu_ipu(struct pci_dev *dev)
@@ -8220,7 +10318,7 @@ index 6c01b1aebf27..ceed043464b1 100644
static void quirk_iommu_ipts(struct pci_dev *dev)
{
if (!IS_IPTS(dev))
-@@ -4956,6 +4983,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx);
+@@ -4931,6 +4958,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);
@@ -8231,9 +10329,9 @@ index 6c01b1aebf27..ceed043464b1 100644
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9D3E, quirk_iommu_ipts);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x34E4, quirk_iommu_ipts);
--
-2.44.0
+2.45.1
-From 214600c4c0c3039ba0d0a5e522a2eb162da3857b Mon Sep 17 00:00:00 2001
+From bbf2fb14fae6c6bda12e156ff9d027913ab7860f 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
@@ -8250,7 +10348,7 @@ Patchset: cameras
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
+index 1e107fd49f828..e3e1696e7f0ee 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)
@@ -8268,9 +10366,9 @@ index 1e107fd49f82..e3e1696e7f0e 100644
return 0;
--
-2.44.0
+2.45.1
-From f5c4f5e1de99e04416ddffca65246a7769a202e3 Mon Sep 17 00:00:00 2001
+From bc2732708fc2a71f7fe3808aa84cc6eabbdd1285 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
@@ -8292,7 +10390,7 @@ Patchset: cameras
1 file changed, 14 insertions(+)
diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c
-index 07b302e09340..1d3097bc7e48 100644
+index 07b302e093407..1d3097bc7e487 100644
--- a/drivers/platform/x86/intel/int3472/discrete.c
+++ b/drivers/platform/x86/intel/int3472/discrete.c
@@ -83,12 +83,26 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347
@@ -8323,9 +10421,9 @@ index 07b302e09340..1d3097bc7e48 100644
agpio, func, polarity);
if (ret)
--
-2.44.0
+2.45.1
-From 216df183e0ad29051b42fcb856d0818a6094f16d Mon Sep 17 00:00:00 2001
+From 0180b848e145642574d2a91175f92050dcec1ec7 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
@@ -8340,7 +10438,7 @@ Patchset: cameras
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c
-index 30f61e04ecaf..9c1292ca8552 100644
+index 30f61e04ecaf5..9c1292ca85522 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)
@@ -8362,9 +10460,9 @@ index 30f61e04ecaf..9c1292ca8552 100644
V4L2_CID_TEST_PATTERN,
ARRAY_SIZE(ov7251_test_pattern_menu) - 1,
--
-2.44.0
+2.45.1
-From 0573bb8c22ed0f0476a2ca6c5df2a7f09c6a1b66 Mon Sep 17 00:00:00 2001
+From 67553f37af4ea73f824108a08666b537c68972e5 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
@@ -8383,7 +10481,7 @@ Patchset: cameras
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 3ec323bd528b..b55570a0142c 100644
+index 3ec323bd528b1..b55570a0142cb 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -796,6 +796,10 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
@@ -8398,7 +10496,7 @@ index 3ec323bd528b..b55570a0142c 100644
* No reference taken. The reference is held by the device (struct
* v4l2_subdev.dev), and async sub-device does not exist independently
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
-index 89c7192148df..44eca113e772 100644
+index 89c7192148dfb..44eca113e7727 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -1219,10 +1219,6 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
@@ -8413,9 +10511,9 @@ index 89c7192148df..44eca113e772 100644
if (ret < 0)
goto out_cleanup;
--
-2.44.0
+2.45.1
-From 84d70102a7892f720a11a0b3d313f3932c859798 Mon Sep 17 00:00:00 2001
+From 82dff76b0fc0e6d8fec20339edcb52fcb3eb1fac 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
@@ -8431,7 +10529,7 @@ Patchset: cameras
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
+index e3e1696e7f0ee..423dc555093f7 100644
--- a/drivers/platform/x86/intel/int3472/tps68470.c
+++ b/drivers/platform/x86/intel/int3472/tps68470.c
@@ -17,7 +17,7 @@
@@ -8454,9 +10552,9 @@ index e3e1696e7f0e..423dc555093f 100644
for (i = 0; i < board_data->n_gpiod_lookups; i++)
gpiod_add_lookup_table(board_data->tps68470_gpio_lookup_tables[i]);
--
-2.44.0
+2.45.1
-From 183f7e4da9cacc2a0f9cb3549adad9a3c95f1b94 Mon Sep 17 00:00:00 2001
+From ac5b449b3e7da53dfff6ab59dc32d9714eb2f106 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
@@ -8474,7 +10572,7 @@ Patchset: cameras
1 file changed, 5 insertions(+)
diff --git a/include/linux/mfd/tps68470.h b/include/linux/mfd/tps68470.h
-index 7807fa329db0..2d2abb25b944 100644
+index 7807fa329db00..2d2abb25b944f 100644
--- a/include/linux/mfd/tps68470.h
+++ b/include/linux/mfd/tps68470.h
@@ -34,6 +34,7 @@
@@ -8495,9 +10593,9 @@ index 7807fa329db0..2d2abb25b944 100644
+
#endif /* __LINUX_MFD_TPS68470_H */
--
-2.44.0
+2.45.1
-From 0f3811853f436eed853d7d226eb811f65137d03a Mon Sep 17 00:00:00 2001
+From 3bdaa0c3e0b7dfa3b441e7d19d3f08ff3708e354 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
@@ -8520,10 +10618,10 @@ Patchset: cameras
create mode 100644 drivers/leds/leds-tps68470.c
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
-index d721b254e1e4..1717f94d1491 100644
+index 05e6af88b88cd..c120eb0b5aa8e 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
-@@ -899,6 +899,18 @@ config LEDS_TPS6105X
+@@ -909,6 +909,18 @@ config LEDS_TPS6105X
It is a single boost converter primarily for white LEDs and
audio amplifiers.
@@ -8543,7 +10641,7 @@ index d721b254e1e4..1717f94d1491 100644
tristate "LED support for SGI Octane machines"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
-index ce07dc295ff0..0ebf6a9f9f7f 100644
+index effdfc6f1e951..6ce609b2cdac6 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -86,6 +86,7 @@ obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o
@@ -8556,7 +10654,7 @@ index ce07dc295ff0..0ebf6a9f9f7f 100644
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
+index 0000000000000..35aeb5db89c8f
--- /dev/null
+++ b/drivers/leds/leds-tps68470.c
@@ -0,0 +1,185 @@
@@ -8746,431 +10844,9 @@ index 000000000000..35aeb5db89c8
+MODULE_DESCRIPTION("LED driver for TPS68470 PMIC");
+MODULE_LICENSE("GPL v2");
--
-2.44.0
-
-From 87ebc160cb35a068acfaf59847c84656cb52b1b7 Mon Sep 17 00:00:00 2001
-From: Sakari Ailus <sakari.ailus@linux.intel.com>
-Date: Thu, 25 May 2023 14:12:04 +0300
-Subject: [PATCH] media: ipu3-cio2: Further clean up async subdev link creation
-
-Use v4l2_create_fwnode_links_to_pad() to create links from async
-sub-devices to the CSI-2 receiver subdevs.
-
-Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
-Reviewed-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
-Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
-Patchset: cameras
----
- drivers/media/pci/intel/ipu3/ipu3-cio2.c | 22 +++++-----------------
- 1 file changed, 5 insertions(+), 17 deletions(-)
-
-diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
-index ed08bf4178f0..83e29c56fe33 100644
---- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
-+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
-@@ -28,6 +28,7 @@
- #include <media/v4l2-device.h>
- #include <media/v4l2-event.h>
- #include <media/v4l2-fwnode.h>
-+#include <media/v4l2-mc.h>
- #include <media/v4l2-ioctl.h>
- #include <media/videobuf2-dma-sg.h>
-
-@@ -1407,7 +1408,6 @@ static void cio2_notifier_unbind(struct v4l2_async_notifier *notifier,
- 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_connection *asd;
- struct cio2_queue *q;
-@@ -1417,23 +1417,10 @@ static int cio2_notifier_complete(struct v4l2_async_notifier *notifier)
- 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);
-+ ret = v4l2_create_fwnode_links_to_pad(asd->sd,
-+ &q->subdev_pads[CIO2_PAD_SINK], 0);
-+ if (ret)
- return ret;
-- }
- }
-
- return v4l2_device_register_subdev_nodes(&cio2->v4l2_dev);
-@@ -1572,6 +1559,7 @@ static int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q)
- v4l2_subdev_init(subdev, &cio2_subdev_ops);
- subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
- subdev->owner = THIS_MODULE;
-+ subdev->dev = dev;
- snprintf(subdev->name, sizeof(subdev->name),
- CIO2_ENTITY_NAME " %td", q - cio2->queue);
- subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
---
-2.44.0
-
-From a9681c29588d67321733877e11f9588ed9e54861 Mon Sep 17 00:00:00 2001
-From: mojyack <mojyack@gmail.com>
-Date: Sat, 3 Feb 2024 12:53:33 +0900
-Subject: [PATCH] media: i2c: Revert DW9719 driver
-
-Patchset: cameras
----
- drivers/media/i2c/dw9719.c | 199 +++++++++++++++++++++++++------------
- 1 file changed, 137 insertions(+), 62 deletions(-)
-
-diff --git a/drivers/media/i2c/dw9719.c b/drivers/media/i2c/dw9719.c
-index c626ed845928..d5f585dabb60 100644
---- a/drivers/media/i2c/dw9719.c
-+++ b/drivers/media/i2c/dw9719.c
-@@ -6,13 +6,14 @@
- * 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-cci.h>
- #include <media/v4l2-common.h>
- #include <media/v4l2-ctrls.h>
- #include <media/v4l2-subdev.h>
-@@ -20,31 +21,29 @@
- #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 CCI_REG8(0)
-+#define DW9719_INFO 0
- #define DW9719_ID 0xF1
-+#define DW9719_CONTROL 2
-+#define DW9719_VCM_CURRENT 3
-
--#define DW9719_CONTROL CCI_REG8(2)
--#define DW9719_ENABLE_RINGING 0x02
--
--#define DW9719_VCM_CURRENT CCI_REG16(3)
--
--#define DW9719_MODE CCI_REG8(6)
--#define DW9719_MODE_SAC_SHIFT 4
--#define DW9719_MODE_SAC3 4
-+#define DW9719_MODE 6
-+#define DW9719_VCM_FREQ 7
-
--#define DW9719_VCM_FREQ CCI_REG8(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 v4l2_subdev sd;
- struct device *dev;
-- struct regmap *regmap;
-- struct regulator *regulator;
-- u32 sac_mode;
-- u32 vcm_freq;
-+ struct i2c_client *client;
-+ struct regulator_bulk_data regulators[NUM_REGULATORS];
-+ struct v4l2_subdev sd;
-
- struct dw9719_v4l2_ctrls {
- struct v4l2_ctrl_handler handler;
-@@ -52,18 +51,79 @@ struct dw9719_device {
- } 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;
-- u64 val;
-+ u8 val;
-
-- ret = cci_read(dw9719->regmap, DW9719_INFO, &val, NULL);
-+ 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");
-- return -ENXIO;
-+ ret = -ENXIO;
- }
-
- return 0;
-@@ -71,37 +131,54 @@ static int dw9719_detect(struct dw9719_device *dw9719)
-
- static int dw9719_power_down(struct dw9719_device *dw9719)
- {
-- return regulator_disable(dw9719->regulator);
-+ return regulator_bulk_disable(NUM_REGULATORS, dw9719->regulators);
- }
-
- static int dw9719_power_up(struct dw9719_device *dw9719)
- {
- int ret;
-
-- ret = regulator_enable(dw9719->regulator);
-+ ret = regulator_bulk_enable(NUM_REGULATORS, dw9719->regulators);
- if (ret)
- return ret;
-
- /* Jiggle SCL pin to wake up device */
-- cci_write(dw9719->regmap, DW9719_CONTROL, 1, &ret);
-+ ret = dw9719_i2c_wr8(dw9719->client, DW9719_CONTROL, 1);
-
-- /* Need 100us to transit from SHUTDOWN to STANDBY */
-- fsleep(100);
-+ /* Need 100us to transit from SHUTDOWN to STANDBY*/
-+ usleep_range(100, 1000);
-
-- cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_ENABLE_RINGING, &ret);
-- cci_write(dw9719->regmap, DW9719_MODE,
-- dw9719->sac_mode << DW9719_MODE_SAC_SHIFT, &ret);
-- cci_write(dw9719->regmap, DW9719_VCM_FREQ, dw9719->vcm_freq, &ret);
-+ ret = dw9719_i2c_wr8(dw9719->client, DW9719_CONTROL,
-+ DW9719_ENABLE_RINGING);
-+ if (ret < 0)
-+ goto fail_powerdown;
-
-- if (ret)
-- dw9719_power_down(dw9719);
-+ 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)
- {
-- return cci_write(dw9719->regmap, DW9719_VCM_CURRENT, value, NULL);
-+ 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)
-@@ -132,7 +209,7 @@ static const struct v4l2_ctrl_ops dw9719_ctrl_ops = {
- .s_ctrl = dw9719_set_ctrl,
- };
-
--static int dw9719_suspend(struct device *dev)
-+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);
-@@ -151,7 +228,7 @@ static int dw9719_suspend(struct device *dev)
- return dw9719_power_down(dw9719);
- }
-
--static int dw9719_resume(struct device *dev)
-+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);
-@@ -201,7 +278,9 @@ static int dw9719_init_controls(struct dw9719_device *dw9719)
- const struct v4l2_ctrl_ops *ops = &dw9719_ctrl_ops;
- int ret;
-
-- v4l2_ctrl_handler_init(&dw9719->ctrls.handler, 1);
-+ 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,
-@@ -214,7 +293,8 @@ static int dw9719_init_controls(struct dw9719_device *dw9719)
- }
-
- dw9719->sd.ctrl_handler = &dw9719->ctrls.handler;
-- return 0;
-+
-+ return ret;
-
- err_free_handler:
- v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
-@@ -232,26 +312,24 @@ static int dw9719_probe(struct i2c_client *client)
- if (!dw9719)
- return -ENOMEM;
-
-- dw9719->regmap = devm_cci_regmap_init_i2c(client, 8);
-- if (IS_ERR(dw9719->regmap))
-- return PTR_ERR(dw9719->regmap);
--
-+ dw9719->client = client;
- dw9719->dev = &client->dev;
-- dw9719->sac_mode = DW9719_MODE_SAC3;
-- dw9719->vcm_freq = DW9719_DEFAULT_VCM_FREQ;
-
-- /* Optional indication of SAC mode select */
-- device_property_read_u32(&client->dev, "dongwoon,sac-mode",
-- &dw9719->sac_mode);
--
-- /* Optional indication of VCM frequency */
-- device_property_read_u32(&client->dev, "dongwoon,vcm-freq",
-- &dw9719->vcm_freq);
-+ 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";
-
-- dw9719->regulator = devm_regulator_get(&client->dev, "vdd");
-- if (IS_ERR(dw9719->regulator))
-- return dev_err_probe(&client->dev, PTR_ERR(dw9719->regulator),
-- "getting regulator\n");
-+ 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;
-@@ -312,17 +390,13 @@ static int dw9719_probe(struct i2c_client *client)
- 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);
-+ 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);
--
-- pm_runtime_disable(&client->dev);
-- if (!pm_runtime_status_suspended(&client->dev))
-- dw9719_power_down(dw9719);
-- pm_runtime_set_suspended(&client->dev);
- }
-
- static const struct i2c_device_id dw9719_id_table[] = {
-@@ -331,13 +405,14 @@ static const struct i2c_device_id dw9719_id_table[] = {
- };
- MODULE_DEVICE_TABLE(i2c, dw9719_id_table);
-
--static DEFINE_RUNTIME_DEV_PM_OPS(dw9719_pm_ops, dw9719_suspend, dw9719_resume,
-- NULL);
-+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 = pm_sleep_ptr(&dw9719_pm_ops),
-+ .pm = &dw9719_pm_ops,
- },
- .probe = dw9719_probe,
- .remove = dw9719_remove,
---
-2.44.0
+2.45.1
-From c5d6c95fd5cefbd4ba9779fc965bce0a36bdbe5e Mon Sep 17 00:00:00 2001
+From 05501de0dab9bc531918dcf2b8aa7e679760fd7b Mon Sep 17 00:00:00 2001
From: mojyack <mojyack@gmail.com>
Date: Sat, 3 Feb 2024 12:59:53 +0900
Subject: [PATCH] media: staging: ipu3-imgu: Fix multiple calls of s_stream on
@@ -9184,7 +10860,7 @@ Patchset: cameras
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c
-index 3df58eb3e882..81aff2d5d898 100644
+index 3df58eb3e8822..81aff2d5d8988 100644
--- a/drivers/staging/media/ipu3/ipu3-v4l2.c
+++ b/drivers/staging/media/ipu3/ipu3-v4l2.c
@@ -538,18 +538,18 @@ static void imgu_vb2_stop_streaming(struct vb2_queue *vq)
@@ -9215,9 +10891,41 @@ index 3df58eb3e882..81aff2d5d898 100644
r = imgu_s_stream(imgu, false);
if (!r)
--
-2.44.0
+2.45.1
+
+From c85328d7df3de31a574e42f66192c6a944f48bde Mon Sep 17 00:00:00 2001
+From: mojyack <mojyack@gmail.com>
+Date: Tue, 26 Mar 2024 05:55:44 +0900
+Subject: [PATCH] media: i2c: dw9719: fix probe error on surface go 2
+
+On surface go 2, sometimes probing dw9719 fails with "dw9719: probe of i2c-INT347A:00-VCM failed with error -121".
+The -121(-EREMOTEIO) is came from drivers/i2c/busses/i2c-designware-common.c:575, and indicates the initialize occurs too early.
+So just add some delay.
+There is no exact reason for this 10000us, but 100us failed.
+
+Patchset: cameras
+---
+ drivers/media/i2c/dw9719.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/media/i2c/dw9719.c b/drivers/media/i2c/dw9719.c
+index c626ed845928c..0094cfda57ea8 100644
+--- a/drivers/media/i2c/dw9719.c
++++ b/drivers/media/i2c/dw9719.c
+@@ -82,6 +82,9 @@ static int dw9719_power_up(struct dw9719_device *dw9719)
+ if (ret)
+ return ret;
+
++ /* Wait for device to be acknowledged */
++ fsleep(10000);
++
+ /* Jiggle SCL pin to wake up device */
+ cci_write(dw9719->regmap, DW9719_CONTROL, 1, &ret);
+
+--
+2.45.1
-From 302d8dc26283bc10ba22bc549c41292d00125e60 Mon Sep 17 00:00:00 2001
+From e0b584d3054d7c69d2e1b4f2ca54e42b79ee5446 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
@@ -9240,7 +10948,7 @@ Patchset: amd-gpio
1 file changed, 17 insertions(+)
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
-index 85a3ce2a3666..2c0e04a3a697 100644
+index 4bf82dbd2a6b5..7a8cb090c6568 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -22,6 +22,7 @@
@@ -9251,7 +10959,7 @@ index 85a3ce2a3666..2c0e04a3a697 100644
#include <asm/e820/api.h>
#include <asm/irqdomain.h>
-@@ -1251,6 +1252,17 @@ static void __init mp_config_acpi_legacy_irqs(void)
+@@ -1216,6 +1217,17 @@ static void __init mp_config_acpi_legacy_irqs(void)
}
}
@@ -9269,7 +10977,7 @@ index 85a3ce2a3666..2c0e04a3a697 100644
/*
* Parse IOAPIC related entries in MADT
* returns 0 on success, < 0 on error
-@@ -1306,6 +1318,11 @@ static int __init acpi_parse_madt_ioapic_entries(void)
+@@ -1271,6 +1283,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);
@@ -9282,9 +10990,9 @@ index 85a3ce2a3666..2c0e04a3a697 100644
mp_config_acpi_legacy_irqs();
--
-2.44.0
+2.45.1
-From d2a793e4fd47cd1cba2847915e7078671d9e9ea5 Mon Sep 17 00:00:00 2001
+From dafa7b45eecb1e02dc857158566d2fb3087fa71f 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
@@ -9299,10 +11007,10 @@ Patchset: amd-gpio
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
-index 2c0e04a3a697..b0e1dab3d2ec 100644
+index 7a8cb090c6568..0faafc323e673 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
-@@ -1254,12 +1254,19 @@ static void __init mp_config_acpi_legacy_irqs(void)
+@@ -1219,12 +1219,19 @@ static void __init mp_config_acpi_legacy_irqs(void)
static const struct dmi_system_id surface_quirk[] __initconst = {
{
@@ -9324,9 +11032,9 @@ index 2c0e04a3a697..b0e1dab3d2ec 100644
};
--
-2.44.0
+2.45.1
-From 34ad5b493b00c944ed68d2436cad96785fe37a33 Mon Sep 17 00:00:00 2001
+From e3e62473f885786f1b57421f5fed86ac5ba402ac 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
@@ -9349,7 +11057,7 @@ Patchset: rtc
1 file changed, 24 insertions(+), 12 deletions(-)
diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c
-index 33c3b16af556..900445d06623 100644
+index 1d670dbe4d1dd..71c9e375ca1ca 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,
@@ -9386,7 +11094,7 @@ index 33c3b16af556..900445d06623 100644
};
static ssize_t dc_alarm_store(struct device *dev, struct device_attribute *attr,
-@@ -564,13 +571,18 @@ static int acpi_tad_remove(struct platform_device *pdev)
+@@ -564,13 +571,18 @@ static void acpi_tad_remove(struct platform_device *pdev)
pm_runtime_get_sync(dev);
@@ -9407,7 +11115,7 @@ index 33c3b16af556..900445d06623 100644
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);
-@@ -613,12 +625,6 @@ static int acpi_tad_probe(struct platform_device *pdev)
+@@ -612,12 +624,6 @@ static int acpi_tad_probe(struct platform_device *pdev)
goto remove_handler;
}
@@ -9420,7 +11128,7 @@ index 33c3b16af556..900445d06623 100644
dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
if (!dd) {
ret = -ENOMEM;
-@@ -649,6 +655,12 @@ static int acpi_tad_probe(struct platform_device *pdev)
+@@ -648,6 +654,12 @@ static int acpi_tad_probe(struct platform_device *pdev)
if (ret)
goto fail;
@@ -9434,5 +11142,5 @@ index 33c3b16af556..900445d06623 100644
ret = sysfs_create_group(&dev->kobj, &acpi_tad_dc_attr_group);
if (ret)
--
-2.44.0
+2.45.1