diff --git a/patches/5.16/0001-surface3-oemb.patch b/patches/5.16/0001-surface3-oemb.patch index cfed1a1e1..35fc45728 100644 --- a/patches/5.16/0001-surface3-oemb.patch +++ b/patches/5.16/0001-surface3-oemb.patch @@ -1,4 +1,4 @@ -From 4d3a1d1b7982a5606a3487d88001873f27ccde33 Mon Sep 17 00:00:00 2001 +From b281caed8c64e4995e940b6392e343f9de430a78 Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Sun, 18 Oct 2020 16:42:44 +0900 Subject: [PATCH] (surface3-oemb) add DMI matches for Surface 3 with broken DMI diff --git a/patches/5.16/0002-mwifiex.patch b/patches/5.16/0002-mwifiex.patch index 5df8886dd..957f6d857 100644 --- a/patches/5.16/0002-mwifiex.patch +++ b/patches/5.16/0002-mwifiex.patch @@ -1,4 +1,4 @@ -From 87e58ba4d9bd4ede8a89369e084d4410bae0deee Mon Sep 17 00:00:00 2001 +From 6a027f5c058e37000185b232ebbf80e8d69d1140 Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Tue, 29 Sep 2020 17:32:22 +0900 Subject: [PATCH] mwifiex: pcie: add reset_wsid quirk for Surface 3 @@ -183,7 +183,7 @@ index 8ec4176d698f..25370c5a4f59 100644 -- 2.35.1 -From 919100911e86f781718dcc54c758e1558d7bae2c Mon Sep 17 00:00:00 2001 +From 8f7a59af7ea9a70bc220b4456dad4697db24a8a4 Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Wed, 30 Sep 2020 18:08:24 +0900 Subject: [PATCH] mwifiex: pcie: (OEMB) add quirk for Surface 3 with broken DMI @@ -245,7 +245,7 @@ index 563dd0d5ac79..32e2f000e57b 100644 -- 2.35.1 -From 72a6c0303d0f23b455112260ae840aa7c0311542 Mon Sep 17 00:00:00 2001 +From 9d264868c3bf477498608c96af9d37b03d361e92 Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Sun, 4 Oct 2020 00:11:49 +0900 Subject: [PATCH] mwifiex: pcie: disable bridge_d3 for Surface gen4+ @@ -400,7 +400,7 @@ index 25370c5a4f59..a1de111ad1db 100644 -- 2.35.1 -From af845656c459d99ce0302ad6d3ccc30aeda320e0 Mon Sep 17 00:00:00 2001 +From c6c76184cea6e3dfd665006a46f6ebca65f094a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Tue, 3 Nov 2020 13:28:04 +0100 Subject: [PATCH] mwifiex: Add quirk resetting the PCI bridge on MS Surface @@ -567,7 +567,7 @@ index a1de111ad1db..0e429779bb04 100644 -- 2.35.1 -From 2889a255ae31c329478f26149af1109eda939f57 Mon Sep 17 00:00:00 2001 +From 187231fc2e26b2321f904dd43f83fff761b37356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Thu, 25 Mar 2021 11:33:02 +0100 Subject: [PATCH] Bluetooth: btusb: Lower passive lescan interval on Marvell @@ -645,7 +645,7 @@ index ea72afb7abea..dc0d1d8e0daa 100644 -- 2.35.1 -From 86cccb3c0c5b940424774241c4785e24a0ba31fa Mon Sep 17 00:00:00 2001 +From 65e0f3a3dfa02dffa631e5bdeed93e993aa6130b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Tue, 10 Nov 2020 12:49:56 +0100 Subject: [PATCH] mwifiex: Use non-posted PCI register writes @@ -702,7 +702,7 @@ index 586c79dc0a98..f87bc9bdfba7 100644 -- 2.35.1 -From 35515228f4da328d99d22e46d94a734edfee1a40 Mon Sep 17 00:00:00 2001 +From 64651de263b769378a255cdab1e18468c0b03ad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Tue, 13 Apr 2021 14:23:05 +0200 Subject: [PATCH] mwifiex: Add quirk to disable deep sleep with certain @@ -805,7 +805,7 @@ index 6b5d35d9e69f..8e49ebca1847 100644 -- 2.35.1 -From de45e6e95c16c2c14e7c9acebcfd66f5dc74a563 Mon Sep 17 00:00:00 2001 +From bea9eeec098b4cd62d2fc9ab6612d97fce3ace56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Tue, 13 Apr 2021 12:57:41 +0200 Subject: [PATCH] mwifiex: Ignore BTCOEX events from the firmware diff --git a/patches/5.16/0002-surface3-oemb.patch b/patches/5.16/0002-surface3-oemb.patch deleted file mode 100644 index 35fc45728..000000000 --- a/patches/5.16/0002-surface3-oemb.patch +++ /dev/null @@ -1,101 +0,0 @@ -From b281caed8c64e4995e940b6392e343f9de430a78 Mon Sep 17 00:00:00 2001 -From: Tsuchiya Yuto -Date: Sun, 18 Oct 2020 16:42:44 +0900 -Subject: [PATCH] (surface3-oemb) add DMI matches for Surface 3 with broken DMI - table - -On some Surface 3, the DMI table gets corrupted for unknown reasons -and breaks existing DMI matching used for device-specific quirks. - -This commit adds the (broken) DMI data into dmi_system_id tables used -for quirks so that each driver can enable quirks even on the affected -systems. - -On affected systems, DMI data will look like this: - $ grep . /sys/devices/virtual/dmi/id/{bios_vendor,board_name,board_vendor,\ - chassis_vendor,product_name,sys_vendor} - /sys/devices/virtual/dmi/id/bios_vendor:American Megatrends Inc. - /sys/devices/virtual/dmi/id/board_name:OEMB - /sys/devices/virtual/dmi/id/board_vendor:OEMB - /sys/devices/virtual/dmi/id/chassis_vendor:OEMB - /sys/devices/virtual/dmi/id/product_name:OEMB - /sys/devices/virtual/dmi/id/sys_vendor:OEMB - -Expected: - $ grep . /sys/devices/virtual/dmi/id/{bios_vendor,board_name,board_vendor,\ - chassis_vendor,product_name,sys_vendor} - /sys/devices/virtual/dmi/id/bios_vendor:American Megatrends Inc. - /sys/devices/virtual/dmi/id/board_name:Surface 3 - /sys/devices/virtual/dmi/id/board_vendor:Microsoft Corporation - /sys/devices/virtual/dmi/id/chassis_vendor:Microsoft Corporation - /sys/devices/virtual/dmi/id/product_name:Surface 3 - /sys/devices/virtual/dmi/id/sys_vendor:Microsoft Corporation - -Signed-off-by: Tsuchiya Yuto -Patchset: surface3-oemb ---- - drivers/platform/surface/surface3-wmi.c | 7 +++++++ - sound/soc/codecs/rt5645.c | 9 +++++++++ - sound/soc/intel/common/soc-acpi-intel-cht-match.c | 8 ++++++++ - 3 files changed, 24 insertions(+) - -diff --git a/drivers/platform/surface/surface3-wmi.c b/drivers/platform/surface/surface3-wmi.c -index 09ac9cfc40d8..c626109cf445 100644 ---- a/drivers/platform/surface/surface3-wmi.c -+++ b/drivers/platform/surface/surface3-wmi.c -@@ -37,6 +37,13 @@ static const struct dmi_system_id surface3_dmi_table[] = { - DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"), - }, - }, -+ { -+ .matches = { -+ DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."), -+ DMI_MATCH(DMI_SYS_VENDOR, "OEMB"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "OEMB"), -+ }, -+ }, - #endif - { } - }; -diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c -index 197c56047947..9893e9c3cdf7 100644 ---- a/sound/soc/codecs/rt5645.c -+++ b/sound/soc/codecs/rt5645.c -@@ -3718,6 +3718,15 @@ static const struct dmi_system_id dmi_platform_data[] = { - }, - .driver_data = (void *)&intel_braswell_platform_data, - }, -+ { -+ .ident = "Microsoft Surface 3", -+ .matches = { -+ DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."), -+ DMI_MATCH(DMI_SYS_VENDOR, "OEMB"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "OEMB"), -+ }, -+ .driver_data = (void *)&intel_braswell_platform_data, -+ }, - { - /* - * Match for the GPDwin which unfortunately uses somewhat -diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c -index c60a5e8e7bc9..e947133a2c36 100644 ---- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c -+++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c -@@ -27,6 +27,14 @@ static const struct dmi_system_id cht_table[] = { - DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"), - }, - }, -+ { -+ .callback = cht_surface_quirk_cb, -+ .matches = { -+ DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."), -+ DMI_MATCH(DMI_SYS_VENDOR, "OEMB"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "OEMB"), -+ }, -+ }, - { } - }; - --- -2.35.1 - diff --git a/patches/5.16/0003-ath10k.patch b/patches/5.16/0003-ath10k.patch index a6a367831..5bcb625c7 100644 --- a/patches/5.16/0003-ath10k.patch +++ b/patches/5.16/0003-ath10k.patch @@ -1,4 +1,4 @@ -From 311a7f0739022bf7d4252b61135ab106b28e31b9 Mon Sep 17 00:00:00 2001 +From ddd1f30f0a41ae8229d875d6f3d1ef8fb8eb59f2 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 27 Feb 2021 00:45:52 +0100 Subject: [PATCH] ath10k: Add module parameters to override board files diff --git a/patches/5.16/0003-mwifiex.patch b/patches/5.16/0003-mwifiex.patch deleted file mode 100644 index 957f6d857..000000000 --- a/patches/5.16/0003-mwifiex.patch +++ /dev/null @@ -1,845 +0,0 @@ -From 6a027f5c058e37000185b232ebbf80e8d69d1140 Mon Sep 17 00:00:00 2001 -From: Tsuchiya Yuto -Date: Tue, 29 Sep 2020 17:32:22 +0900 -Subject: [PATCH] mwifiex: pcie: add reset_wsid quirk for Surface 3 - -This commit adds reset_wsid quirk and uses this quirk for Surface 3 on -card reset. - -To reset mwifiex on Surface 3, it seems that calling the _DSM method -exists in \_SB.WSID [1] device is required. - -On Surface 3, calling the _DSM method removes/re-probes the card by -itself. So, need to place the reset function before performing FLR and -skip performing any other reset-related works. - -Note that Surface Pro 3 also has the WSID device [2], but it seems to need -more work. This commit only supports Surface 3 yet. - -[1] https://github.com/linux-surface/acpidumps/blob/05cba925f3a515f222acb5b3551a032ddde958fe/surface_3/dsdt.dsl#L11947-L12011 -[2] https://github.com/linux-surface/acpidumps/blob/05cba925f3a515f222acb5b3551a032ddde958fe/surface_pro_3/dsdt.dsl#L12164-L12216 - -Signed-off-by: Tsuchiya Yuto -Patchset: mwifiex ---- - drivers/net/wireless/marvell/mwifiex/pcie.c | 10 +++ - .../wireless/marvell/mwifiex/pcie_quirks.c | 83 +++++++++++++++++++ - .../wireless/marvell/mwifiex/pcie_quirks.h | 6 ++ - 3 files changed, 99 insertions(+) - -diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c -index c3f5583ea70d..3f5138008594 100644 ---- a/drivers/net/wireless/marvell/mwifiex/pcie.c -+++ b/drivers/net/wireless/marvell/mwifiex/pcie.c -@@ -2993,6 +2993,16 @@ static void mwifiex_pcie_card_reset_work(struct mwifiex_adapter *adapter) - { - struct pcie_service_card *card = adapter->card; - -+ /* On Surface 3, reset_wsid method removes then re-probes card by -+ * itself. So, need to place it here and skip performing any other -+ * reset-related works. -+ */ -+ if (card->quirks & QUIRK_FW_RST_WSID_S3) { -+ mwifiex_pcie_reset_wsid_quirk(card->dev); -+ /* skip performing any other reset-related works */ -+ return; -+ } -+ - /* We can't afford to wait here; remove() might be waiting on us. If we - * can't grab the device lock, maybe we'll get another chance later. - */ -diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c -index 0234cf3c2974..563dd0d5ac79 100644 ---- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c -+++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c -@@ -15,10 +15,21 @@ - * this warranty disclaimer. - */ - -+#include - #include - - #include "pcie_quirks.h" - -+/* For reset_wsid quirk */ -+#define ACPI_WSID_PATH "\\_SB.WSID" -+#define WSID_REV 0x0 -+#define WSID_FUNC_WIFI_PWR_OFF 0x1 -+#define WSID_FUNC_WIFI_PWR_ON 0x2 -+/* WSID _DSM UUID: "534ea3bf-fcc2-4e7a-908f-a13978f0c7ef" */ -+static const guid_t wsid_dsm_guid = -+ GUID_INIT(0x534ea3bf, 0xfcc2, 0x4e7a, -+ 0x90, 0x8f, 0xa1, 0x39, 0x78, 0xf0, 0xc7, 0xef); -+ - /* quirk table based on DMI matching */ - static const struct dmi_system_id mwifiex_quirk_table[] = { - { -@@ -87,6 +98,14 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { - }, - .driver_data = (void *)QUIRK_FW_RST_D3COLD, - }, -+ { -+ .ident = "Surface 3", -+ .matches = { -+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), -+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface 3"), -+ }, -+ .driver_data = (void *)QUIRK_FW_RST_WSID_S3, -+ }, - {} - }; - -@@ -103,6 +122,9 @@ void mwifiex_initialize_quirks(struct pcie_service_card *card) - dev_info(&pdev->dev, "no quirks enabled\n"); - if (card->quirks & QUIRK_FW_RST_D3COLD) - dev_info(&pdev->dev, "quirk reset_d3cold enabled\n"); -+ if (card->quirks & QUIRK_FW_RST_WSID_S3) -+ dev_info(&pdev->dev, -+ "quirk reset_wsid for Surface 3 enabled\n"); - } - - static void mwifiex_pcie_set_power_d3cold(struct pci_dev *pdev) -@@ -159,3 +181,64 @@ int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev) - - return 0; - } -+ -+int mwifiex_pcie_reset_wsid_quirk(struct pci_dev *pdev) -+{ -+ acpi_handle handle; -+ union acpi_object *obj; -+ acpi_status status; -+ -+ dev_info(&pdev->dev, "Using reset_wsid quirk to perform FW reset\n"); -+ -+ status = acpi_get_handle(NULL, ACPI_WSID_PATH, &handle); -+ if (ACPI_FAILURE(status)) { -+ dev_err(&pdev->dev, "No ACPI handle for path %s\n", -+ ACPI_WSID_PATH); -+ return -ENODEV; -+ } -+ -+ if (!acpi_has_method(handle, "_DSM")) { -+ dev_err(&pdev->dev, "_DSM method not found\n"); -+ return -ENODEV; -+ } -+ -+ if (!acpi_check_dsm(handle, &wsid_dsm_guid, -+ WSID_REV, WSID_FUNC_WIFI_PWR_OFF)) { -+ dev_err(&pdev->dev, -+ "_DSM method doesn't support wifi power off func\n"); -+ return -ENODEV; -+ } -+ -+ if (!acpi_check_dsm(handle, &wsid_dsm_guid, -+ WSID_REV, WSID_FUNC_WIFI_PWR_ON)) { -+ dev_err(&pdev->dev, -+ "_DSM method doesn't support wifi power on func\n"); -+ return -ENODEV; -+ } -+ -+ /* card will be removed immediately after this call on Surface 3 */ -+ dev_info(&pdev->dev, "turning wifi off...\n"); -+ obj = acpi_evaluate_dsm(handle, &wsid_dsm_guid, -+ WSID_REV, WSID_FUNC_WIFI_PWR_OFF, -+ NULL); -+ if (!obj) { -+ dev_err(&pdev->dev, -+ "device _DSM execution failed for turning wifi off\n"); -+ return -EIO; -+ } -+ ACPI_FREE(obj); -+ -+ /* card will be re-probed immediately after this call on Surface 3 */ -+ dev_info(&pdev->dev, "turning wifi on...\n"); -+ obj = acpi_evaluate_dsm(handle, &wsid_dsm_guid, -+ WSID_REV, WSID_FUNC_WIFI_PWR_ON, -+ NULL); -+ if (!obj) { -+ dev_err(&pdev->dev, -+ "device _DSM execution failed for turning wifi on\n"); -+ return -EIO; -+ } -+ ACPI_FREE(obj); -+ -+ return 0; -+} -diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h -index 8ec4176d698f..25370c5a4f59 100644 ---- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h -+++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h -@@ -19,5 +19,11 @@ - - #define QUIRK_FW_RST_D3COLD BIT(0) - -+/* Surface 3 and Surface Pro 3 have the same _DSM method but need to -+ * be handled differently. Currently, only S3 is supported. -+ */ -+#define QUIRK_FW_RST_WSID_S3 BIT(1) -+ - void mwifiex_initialize_quirks(struct pcie_service_card *card); - int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev); -+int mwifiex_pcie_reset_wsid_quirk(struct pci_dev *pdev); --- -2.35.1 - -From 8f7a59af7ea9a70bc220b4456dad4697db24a8a4 Mon Sep 17 00:00:00 2001 -From: Tsuchiya Yuto -Date: Wed, 30 Sep 2020 18:08:24 +0900 -Subject: [PATCH] mwifiex: pcie: (OEMB) add quirk for Surface 3 with broken DMI - table - -(made referring to http://git.osdn.net/view?p=android-x86/kernel.git;a=commitdiff;h=18e2e857c57633b25b3b4120f212224a108cd883) - -On some Surface 3, the DMI table gets corrupted for unknown reasons -and breaks existing DMI matching used for device-specific quirks. - -This commit adds the (broken) DMI info for the affected Surface 3. - -On affected systems, DMI info will look like this: - $ grep . /sys/devices/virtual/dmi/id/{bios_vendor,board_name,board_vendor,\ - chassis_vendor,product_name,sys_vendor} - /sys/devices/virtual/dmi/id/bios_vendor:American Megatrends Inc. - /sys/devices/virtual/dmi/id/board_name:OEMB - /sys/devices/virtual/dmi/id/board_vendor:OEMB - /sys/devices/virtual/dmi/id/chassis_vendor:OEMB - /sys/devices/virtual/dmi/id/product_name:OEMB - /sys/devices/virtual/dmi/id/sys_vendor:OEMB - -Expected: - $ grep . /sys/devices/virtual/dmi/id/{bios_vendor,board_name,board_vendor,\ - chassis_vendor,product_name,sys_vendor} - /sys/devices/virtual/dmi/id/bios_vendor:American Megatrends Inc. - /sys/devices/virtual/dmi/id/board_name:Surface 3 - /sys/devices/virtual/dmi/id/board_vendor:Microsoft Corporation - /sys/devices/virtual/dmi/id/chassis_vendor:Microsoft Corporation - /sys/devices/virtual/dmi/id/product_name:Surface 3 - /sys/devices/virtual/dmi/id/sys_vendor:Microsoft Corporation - -Signed-off-by: Tsuchiya Yuto -Patchset: mwifiex ---- - drivers/net/wireless/marvell/mwifiex/pcie_quirks.c | 9 +++++++++ - 1 file changed, 9 insertions(+) - -diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c -index 563dd0d5ac79..32e2f000e57b 100644 ---- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c -+++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c -@@ -106,6 +106,15 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { - }, - .driver_data = (void *)QUIRK_FW_RST_WSID_S3, - }, -+ { -+ .ident = "Surface 3", -+ .matches = { -+ DMI_EXACT_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."), -+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "OEMB"), -+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "OEMB"), -+ }, -+ .driver_data = (void *)QUIRK_FW_RST_WSID_S3, -+ }, - {} - }; - --- -2.35.1 - -From 9d264868c3bf477498608c96af9d37b03d361e92 Mon Sep 17 00:00:00 2001 -From: Tsuchiya Yuto -Date: Sun, 4 Oct 2020 00:11:49 +0900 -Subject: [PATCH] mwifiex: pcie: disable bridge_d3 for Surface gen4+ - -Currently, mwifiex fw will crash after suspend on recent kernel series. -On Windows, it seems that the root port of wifi will never enter D3 state -(stay on D0 state). And on Linux, disabling the D3 state for the -bridge fixes fw crashing after suspend. - -This commit disables the D3 state of root port on driver initialization -and fixes fw crashing after suspend. - -Signed-off-by: Tsuchiya Yuto -Patchset: mwifiex ---- - drivers/net/wireless/marvell/mwifiex/pcie.c | 7 +++++ - .../wireless/marvell/mwifiex/pcie_quirks.c | 27 +++++++++++++------ - .../wireless/marvell/mwifiex/pcie_quirks.h | 1 + - 3 files changed, 27 insertions(+), 8 deletions(-) - -diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c -index 3f5138008594..372dde99725c 100644 ---- a/drivers/net/wireless/marvell/mwifiex/pcie.c -+++ b/drivers/net/wireless/marvell/mwifiex/pcie.c -@@ -380,6 +380,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) - { - struct pcie_service_card *card; -+ struct pci_dev *parent_pdev = pci_upstream_bridge(pdev); - int ret; - - pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n", -@@ -421,6 +422,12 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, - return -1; - } - -+ /* disable bridge_d3 for Surface gen4+ devices to fix fw crashing -+ * after suspend -+ */ -+ if (card->quirks & QUIRK_NO_BRIDGE_D3) -+ parent_pdev->bridge_d3 = false; -+ - return 0; - } - -diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c -index 32e2f000e57b..356401bab59c 100644 ---- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c -+++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c -@@ -38,7 +38,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"), - }, -- .driver_data = (void *)QUIRK_FW_RST_D3COLD, -+ .driver_data = (void *)(QUIRK_FW_RST_D3COLD | -+ QUIRK_NO_BRIDGE_D3), - }, - { - .ident = "Surface Pro 5", -@@ -47,7 +48,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"), - }, -- .driver_data = (void *)QUIRK_FW_RST_D3COLD, -+ .driver_data = (void *)(QUIRK_FW_RST_D3COLD | -+ QUIRK_NO_BRIDGE_D3), - }, - { - .ident = "Surface Pro 5 (LTE)", -@@ -56,7 +58,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"), - }, -- .driver_data = (void *)QUIRK_FW_RST_D3COLD, -+ .driver_data = (void *)(QUIRK_FW_RST_D3COLD | -+ QUIRK_NO_BRIDGE_D3), - }, - { - .ident = "Surface Pro 6", -@@ -64,7 +67,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"), - }, -- .driver_data = (void *)QUIRK_FW_RST_D3COLD, -+ .driver_data = (void *)(QUIRK_FW_RST_D3COLD | -+ QUIRK_NO_BRIDGE_D3), - }, - { - .ident = "Surface Book 1", -@@ -72,7 +76,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"), - }, -- .driver_data = (void *)QUIRK_FW_RST_D3COLD, -+ .driver_data = (void *)(QUIRK_FW_RST_D3COLD | -+ QUIRK_NO_BRIDGE_D3), - }, - { - .ident = "Surface Book 2", -@@ -80,7 +85,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"), - }, -- .driver_data = (void *)QUIRK_FW_RST_D3COLD, -+ .driver_data = (void *)(QUIRK_FW_RST_D3COLD | -+ QUIRK_NO_BRIDGE_D3), - }, - { - .ident = "Surface Laptop 1", -@@ -88,7 +94,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"), - }, -- .driver_data = (void *)QUIRK_FW_RST_D3COLD, -+ .driver_data = (void *)(QUIRK_FW_RST_D3COLD | -+ QUIRK_NO_BRIDGE_D3), - }, - { - .ident = "Surface Laptop 2", -@@ -96,7 +103,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"), - }, -- .driver_data = (void *)QUIRK_FW_RST_D3COLD, -+ .driver_data = (void *)(QUIRK_FW_RST_D3COLD | -+ QUIRK_NO_BRIDGE_D3), - }, - { - .ident = "Surface 3", -@@ -134,6 +142,9 @@ void mwifiex_initialize_quirks(struct pcie_service_card *card) - if (card->quirks & QUIRK_FW_RST_WSID_S3) - dev_info(&pdev->dev, - "quirk reset_wsid for Surface 3 enabled\n"); -+ if (card->quirks & QUIRK_NO_BRIDGE_D3) -+ dev_info(&pdev->dev, -+ "quirk no_brigde_d3 enabled\n"); - } - - static void mwifiex_pcie_set_power_d3cold(struct pci_dev *pdev) -diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h -index 25370c5a4f59..a1de111ad1db 100644 ---- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h -+++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h -@@ -23,6 +23,7 @@ - * be handled differently. Currently, only S3 is supported. - */ - #define QUIRK_FW_RST_WSID_S3 BIT(1) -+#define QUIRK_NO_BRIDGE_D3 BIT(2) - - void mwifiex_initialize_quirks(struct pcie_service_card *card); - int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev); --- -2.35.1 - -From c6c76184cea6e3dfd665006a46f6ebca65f094a5 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= -Date: Tue, 3 Nov 2020 13:28:04 +0100 -Subject: [PATCH] mwifiex: Add quirk resetting the PCI bridge on MS Surface - devices - -The most recent firmware of the 88W8897 card reports a hardcoded LTR -value to the system during initialization, probably as an (unsuccessful) -attempt of the developers to fix firmware crashes. This LTR value -prevents most of the Microsoft Surface devices from entering deep -powersaving states (either platform C-State 10 or S0ix state), because -the exit latency of that state would be higher than what the card can -tolerate. - -Turns out the card works just the same (including the firmware crashes) -no matter if that hardcoded LTR value is reported or not, so it's kind -of useless and only prevents us from saving power. - -To get rid of those hardcoded LTR reports, it's possible to reset the -PCI bridge device after initializing the cards firmware. I'm not exactly -sure why that works, maybe the power management subsystem of the PCH -resets its stored LTR values when doing a function level reset of the -bridge device. Doing the reset once after starting the wifi firmware -works very well, probably because the firmware only reports that LTR -value a single time during firmware startup. - -Patchset: mwifiex ---- - drivers/net/wireless/marvell/mwifiex/pcie.c | 12 +++++++++ - .../wireless/marvell/mwifiex/pcie_quirks.c | 26 +++++++++++++------ - .../wireless/marvell/mwifiex/pcie_quirks.h | 1 + - 3 files changed, 31 insertions(+), 8 deletions(-) - -diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c -index 372dde99725c..586c79dc0a98 100644 ---- a/drivers/net/wireless/marvell/mwifiex/pcie.c -+++ b/drivers/net/wireless/marvell/mwifiex/pcie.c -@@ -1781,9 +1781,21 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) - static int mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter) - { - struct pcie_service_card *card = adapter->card; -+ struct pci_dev *pdev = card->dev; -+ struct pci_dev *parent_pdev = pci_upstream_bridge(pdev); - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - int tx_wrap = card->txbd_wrptr & reg->tx_wrap_mask; - -+ /* Trigger a function level reset of the PCI bridge device, this makes -+ * the firmware of PCIe 88W8897 cards stop reporting a fixed LTR value -+ * that prevents the system from entering package C10 and S0ix powersaving -+ * states. -+ * We need to do it here because it must happen after firmware -+ * initialization and this function is called after that is done. -+ */ -+ if (card->quirks & QUIRK_DO_FLR_ON_BRIDGE) -+ pci_reset_function(parent_pdev); -+ - /* Write the RX ring read pointer in to reg->rx_rdptr */ - if (mwifiex_write_reg(adapter, reg->rx_rdptr, card->rxbd_rdptr | - tx_wrap)) { -diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c -index 356401bab59c..6437f067d07a 100644 ---- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c -+++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c -@@ -39,7 +39,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"), - }, - .driver_data = (void *)(QUIRK_FW_RST_D3COLD | -- QUIRK_NO_BRIDGE_D3), -+ QUIRK_NO_BRIDGE_D3 | -+ QUIRK_DO_FLR_ON_BRIDGE), - }, - { - .ident = "Surface Pro 5", -@@ -49,7 +50,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"), - }, - .driver_data = (void *)(QUIRK_FW_RST_D3COLD | -- QUIRK_NO_BRIDGE_D3), -+ QUIRK_NO_BRIDGE_D3 | -+ QUIRK_DO_FLR_ON_BRIDGE), - }, - { - .ident = "Surface Pro 5 (LTE)", -@@ -59,7 +61,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"), - }, - .driver_data = (void *)(QUIRK_FW_RST_D3COLD | -- QUIRK_NO_BRIDGE_D3), -+ QUIRK_NO_BRIDGE_D3 | -+ QUIRK_DO_FLR_ON_BRIDGE), - }, - { - .ident = "Surface Pro 6", -@@ -68,7 +71,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"), - }, - .driver_data = (void *)(QUIRK_FW_RST_D3COLD | -- QUIRK_NO_BRIDGE_D3), -+ QUIRK_NO_BRIDGE_D3 | -+ QUIRK_DO_FLR_ON_BRIDGE), - }, - { - .ident = "Surface Book 1", -@@ -77,7 +81,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"), - }, - .driver_data = (void *)(QUIRK_FW_RST_D3COLD | -- QUIRK_NO_BRIDGE_D3), -+ QUIRK_NO_BRIDGE_D3 | -+ QUIRK_DO_FLR_ON_BRIDGE), - }, - { - .ident = "Surface Book 2", -@@ -86,7 +91,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"), - }, - .driver_data = (void *)(QUIRK_FW_RST_D3COLD | -- QUIRK_NO_BRIDGE_D3), -+ QUIRK_NO_BRIDGE_D3 | -+ QUIRK_DO_FLR_ON_BRIDGE), - }, - { - .ident = "Surface Laptop 1", -@@ -95,7 +101,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"), - }, - .driver_data = (void *)(QUIRK_FW_RST_D3COLD | -- QUIRK_NO_BRIDGE_D3), -+ QUIRK_NO_BRIDGE_D3 | -+ QUIRK_DO_FLR_ON_BRIDGE), - }, - { - .ident = "Surface Laptop 2", -@@ -104,7 +111,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"), - }, - .driver_data = (void *)(QUIRK_FW_RST_D3COLD | -- QUIRK_NO_BRIDGE_D3), -+ QUIRK_NO_BRIDGE_D3 | -+ QUIRK_DO_FLR_ON_BRIDGE), - }, - { - .ident = "Surface 3", -@@ -145,6 +153,8 @@ void mwifiex_initialize_quirks(struct pcie_service_card *card) - if (card->quirks & QUIRK_NO_BRIDGE_D3) - dev_info(&pdev->dev, - "quirk no_brigde_d3 enabled\n"); -+ if (card->quirks & QUIRK_DO_FLR_ON_BRIDGE) -+ dev_info(&pdev->dev, "quirk do_flr_on_bridge enabled\n"); - } - - static void mwifiex_pcie_set_power_d3cold(struct pci_dev *pdev) -diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h -index a1de111ad1db..0e429779bb04 100644 ---- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h -+++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h -@@ -24,6 +24,7 @@ - */ - #define QUIRK_FW_RST_WSID_S3 BIT(1) - #define QUIRK_NO_BRIDGE_D3 BIT(2) -+#define QUIRK_DO_FLR_ON_BRIDGE BIT(3) - - void mwifiex_initialize_quirks(struct pcie_service_card *card); - int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev); --- -2.35.1 - -From 187231fc2e26b2321f904dd43f83fff761b37356 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= -Date: Thu, 25 Mar 2021 11:33:02 +0100 -Subject: [PATCH] Bluetooth: btusb: Lower passive lescan interval on Marvell - 88W8897 - -The Marvell 88W8897 combined wifi and bluetooth card (pcie+usb version) -is used in a lot of Microsoft Surface devices, and all those devices -suffer from very low 2.4GHz wifi connection speeds while bluetooth is -enabled. The reason for that is that the default passive scanning -interval for Bluetooth Low Energy devices is quite high in Linux -(interval of 60 msec and scan window of 30 msec, see hci_core.c), and -the Marvell chip is known for its bad bt+wifi coexisting performance. - -So decrease that passive scan interval and make the scan window shorter -on this particular device to allow for spending more time transmitting -wifi signals: The new scan interval is 250 msec (0x190 * 0.625 msec) and -the new scan window is 6.25 msec (0xa * 0,625 msec). - -This change has a very large impact on the 2.4GHz wifi speeds and gets -it up to performance comparable with the Windows driver, which seems to -apply a similar quirk. - -The interval and window length were tested and found to work very well -with a lot of Bluetooth Low Energy devices, including the Surface Pen, a -Bluetooth Speaker and two modern Bluetooth headphones. All devices were -discovered immediately after turning them on. Even lower values were -also tested, but they introduced longer delays until devices get -discovered. - -Patchset: mwifiex ---- - drivers/bluetooth/btusb.c | 15 +++++++++++++++ - 1 file changed, 15 insertions(+) - -diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c -index ea72afb7abea..dc0d1d8e0daa 100644 ---- a/drivers/bluetooth/btusb.c -+++ b/drivers/bluetooth/btusb.c -@@ -61,6 +61,7 @@ static struct usb_driver btusb_driver; - #define BTUSB_QCA_WCN6855 0x1000000 - #define BTUSB_INTEL_BROKEN_SHUTDOWN_LED 0x2000000 - #define BTUSB_INTEL_BROKEN_INITIAL_NCMD 0x4000000 -+#define BTUSB_LOWER_LESCAN_INTERVAL 0x8000000 - - static const struct usb_device_id btusb_table[] = { - /* Generic Bluetooth USB device */ -@@ -375,6 +376,7 @@ static const struct usb_device_id blacklist_table[] = { - { USB_DEVICE(0x1286, 0x2044), .driver_info = BTUSB_MARVELL }, - { USB_DEVICE(0x1286, 0x2046), .driver_info = BTUSB_MARVELL }, - { USB_DEVICE(0x1286, 0x204e), .driver_info = BTUSB_MARVELL }, -+ { USB_DEVICE(0x1286, 0x204c), .driver_info = BTUSB_LOWER_LESCAN_INTERVAL }, - - /* Intel Bluetooth devices */ - { USB_DEVICE(0x8087, 0x0025), .driver_info = BTUSB_INTEL_COMBINED }, -@@ -3908,6 +3910,19 @@ static int btusb_probe(struct usb_interface *intf, - if (id->driver_info & BTUSB_MARVELL) - hdev->set_bdaddr = btusb_set_bdaddr_marvell; - -+ /* The Marvell 88W8897 combined wifi and bluetooth card is known for -+ * very bad bt+wifi coexisting performance. -+ * -+ * Decrease the passive BT Low Energy scan interval a bit -+ * (0x0190 * 0.625 msec = 250 msec) and make the scan window shorter -+ * (0x000a * 0,625 msec = 6.25 msec). This allows for significantly -+ * higher wifi throughput while passively scanning for BT LE devices. -+ */ -+ if (id->driver_info & BTUSB_LOWER_LESCAN_INTERVAL) { -+ hdev->le_scan_interval = 0x0190; -+ hdev->le_scan_window = 0x000a; -+ } -+ - if (IS_ENABLED(CONFIG_BT_HCIBTUSB_MTK) && - (id->driver_info & BTUSB_MEDIATEK)) { - hdev->setup = btusb_mtk_setup; --- -2.35.1 - -From 65e0f3a3dfa02dffa631e5bdeed93e993aa6130b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= -Date: Tue, 10 Nov 2020 12:49:56 +0100 -Subject: [PATCH] mwifiex: Use non-posted PCI register writes - -On the 88W8897 card it's very important the TX ring write pointer is -updated correctly to its new value before setting the TX ready -interrupt, otherwise the firmware appears to crash (probably because -it's trying to DMA-read from the wrong place). - -Since PCI uses "posted writes" when writing to a register, it's not -guaranteed that a write will happen immediately. That means the pointer -might be outdated when setting the TX ready interrupt, leading to -firmware crashes especially when ASPM L1 and L1 substates are enabled -(because of the higher link latency, the write will probably take -longer). - -So fix those firmware crashes by always forcing non-posted writes. We do -that by simply reading back the register after writing it, just as a lot -of other drivers do. - -There are two reproducers that are fixed with this patch: - -1) During rx/tx traffic and with ASPM L1 substates enabled (the enabled -substates are platform dependent), the firmware crashes and eventually a -command timeout appears in the logs. That crash is fixed by using a -non-posted write in mwifiex_pcie_send_data(). - -2) When sending lots of commands to the card, waking it up from sleep in -very quick intervals, the firmware eventually crashes. That crash -appears to be fixed by some other non-posted write included here. - -Patchset: mwifiex ---- - drivers/net/wireless/marvell/mwifiex/pcie.c | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c -index 586c79dc0a98..f87bc9bdfba7 100644 ---- a/drivers/net/wireless/marvell/mwifiex/pcie.c -+++ b/drivers/net/wireless/marvell/mwifiex/pcie.c -@@ -238,6 +238,12 @@ static int mwifiex_write_reg(struct mwifiex_adapter *adapter, int reg, u32 data) - - iowrite32(data, card->pci_mmap1 + reg); - -+ /* Do a read-back, which makes the write non-posted, ensuring the -+ * completion before returning. -+ * The firmware of the 88W8897 card is buggy and this avoids crashes. -+ */ -+ ioread32(card->pci_mmap1 + reg); -+ - return 0; - } - --- -2.35.1 - -From 64651de263b769378a255cdab1e18468c0b03ad0 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= -Date: Tue, 13 Apr 2021 14:23:05 +0200 -Subject: [PATCH] mwifiex: Add quirk to disable deep sleep with certain - hardware revision - -The 88W8897 pcie card with the hardware revision 20 apparently has a -hardware issue where the card wakes up from deep sleep randomly and very -often, somewhat depending on the card activity, maybe the hardware has a -floating wakeup pin or something. - -Those continuous wakeups prevent the card from entering host sleep when -the computer suspends. And because the host won't answer to events from -the card anymore while it's suspended, the firmwares internal -powersaving state machine seems to get confused and the card can't sleep -anymore at all after that. - -Since we can't work around that hardware bug in the firmware, let's -get the hardware revision string from the firmware and match it with -known bad revisions. Then disable auto deep sleep for those revisions, -which makes sure we no longer get those spurious wakeups. - -Patchset: mwifiex ---- - drivers/net/wireless/marvell/mwifiex/main.c | 14 ++++++++++++++ - drivers/net/wireless/marvell/mwifiex/main.h | 1 + - .../net/wireless/marvell/mwifiex/sta_cmdresp.c | 16 ++++++++++++++++ - 3 files changed, 31 insertions(+) - -diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c -index 19b996c6a260..5ab2ad4c7006 100644 ---- a/drivers/net/wireless/marvell/mwifiex/main.c -+++ b/drivers/net/wireless/marvell/mwifiex/main.c -@@ -226,6 +226,19 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter) - return 0; - } - -+static void maybe_quirk_fw_disable_ds(struct mwifiex_adapter *adapter) -+{ -+ struct mwifiex_private *priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); -+ struct mwifiex_ver_ext ver_ext; -+ -+ set_bit(MWIFIEX_IS_REQUESTING_FW_VEREXT, &adapter->work_flags); -+ -+ memset(&ver_ext, 0, sizeof(ver_ext)); -+ ver_ext.version_str_sel = 1; -+ mwifiex_send_cmd(priv, HostCmd_CMD_VERSION_EXT, -+ HostCmd_ACT_GEN_GET, 0, &ver_ext, false); -+} -+ - /* - * The main process. - * -@@ -356,6 +369,7 @@ int mwifiex_main_process(struct mwifiex_adapter *adapter) - if (adapter->hw_status == MWIFIEX_HW_STATUS_INIT_DONE) { - adapter->hw_status = MWIFIEX_HW_STATUS_READY; - mwifiex_init_fw_complete(adapter); -+ maybe_quirk_fw_disable_ds(adapter); - } - } - -diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h -index 90012cbcfd15..1e829d84b1f6 100644 ---- a/drivers/net/wireless/marvell/mwifiex/main.h -+++ b/drivers/net/wireless/marvell/mwifiex/main.h -@@ -524,6 +524,7 @@ enum mwifiex_adapter_work_flags { - MWIFIEX_IS_SUSPENDED, - MWIFIEX_IS_HS_CONFIGURED, - MWIFIEX_IS_HS_ENABLING, -+ MWIFIEX_IS_REQUESTING_FW_VEREXT, - }; - - struct mwifiex_band_config { -diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c -index 6b5d35d9e69f..8e49ebca1847 100644 ---- a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c -+++ b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c -@@ -708,6 +708,22 @@ static int mwifiex_ret_ver_ext(struct mwifiex_private *priv, - { - struct host_cmd_ds_version_ext *ver_ext = &resp->params.verext; - -+ if (test_and_clear_bit(MWIFIEX_IS_REQUESTING_FW_VEREXT, &priv->adapter->work_flags)) { -+ if (strncmp(ver_ext->version_str, "ChipRev:20, BB:9b(10.00), RF:40(21)", 128) == 0) { -+ struct mwifiex_ds_auto_ds auto_ds = { -+ .auto_ds = DEEP_SLEEP_OFF, -+ }; -+ -+ mwifiex_dbg(priv->adapter, MSG, -+ "Bad HW revision detected, disabling deep sleep\n"); -+ -+ mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, -+ DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, false); -+ } -+ -+ return 0; -+ } -+ - if (version_ext) { - version_ext->version_str_sel = ver_ext->version_str_sel; - memcpy(version_ext->version_str, ver_ext->version_str, --- -2.35.1 - -From bea9eeec098b4cd62d2fc9ab6612d97fce3ace56 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= -Date: Tue, 13 Apr 2021 12:57:41 +0200 -Subject: [PATCH] mwifiex: Ignore BTCOEX events from the firmware - -The firmware of the pcie 88W8897 chip sends those events very -unreliably, which means we sometimes end up actually capping the window -size while bluetooth is disabled, artifically limiting wifi speeds even -though it's not needed. - -Since we can't fix the firmware, let's just ignore those events, it -seems that the Windows driver also doesn't change the rx/tx block ack -buffer sizes when bluetooth gets enabled or disabled, so this is -consistent with the Windows driver. - -Patchset: mwifiex ---- - drivers/net/wireless/marvell/mwifiex/sta_event.c | 4 +--- - 1 file changed, 1 insertion(+), 3 deletions(-) - -diff --git a/drivers/net/wireless/marvell/mwifiex/sta_event.c b/drivers/net/wireless/marvell/mwifiex/sta_event.c -index 2b2e6e0166e1..e9e2d4b86acb 100644 ---- a/drivers/net/wireless/marvell/mwifiex/sta_event.c -+++ b/drivers/net/wireless/marvell/mwifiex/sta_event.c -@@ -1061,9 +1061,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) - adapter->event_skb); - break; - case EVENT_BT_COEX_WLAN_PARA_CHANGE: -- dev_dbg(adapter->dev, "EVENT: BT coex wlan param update\n"); -- mwifiex_bt_coex_wlan_param_update_event(priv, -- adapter->event_skb); -+ dev_dbg(adapter->dev, "EVENT: ignoring BT coex wlan param update\n"); - break; - case EVENT_RXBA_SYNC: - dev_dbg(adapter->dev, "EVENT: RXBA_SYNC\n"); --- -2.35.1 - diff --git a/patches/5.16/0004-ath10k.patch b/patches/5.16/0004-ath10k.patch deleted file mode 100644 index 5bcb625c7..000000000 --- a/patches/5.16/0004-ath10k.patch +++ /dev/null @@ -1,121 +0,0 @@ -From ddd1f30f0a41ae8229d875d6f3d1ef8fb8eb59f2 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Sat, 27 Feb 2021 00:45:52 +0100 -Subject: [PATCH] ath10k: Add module parameters to override board files - -Some Surface devices, specifically the Surface Go and AMD version of the -Surface Laptop 3 (wich both come with QCA6174 WiFi chips), work better -with a different board file, as it seems that the firmeware included -upstream is buggy. - -As it is generally not a good idea to randomly overwrite files, let -alone doing so via packages, we add module parameters to override those -file names in the driver. This allows us to package/deploy the override -via a modprobe.d config. - -Signed-off-by: Maximilian Luz -Patchset: ath10k ---- - drivers/net/wireless/ath/ath10k/core.c | 58 ++++++++++++++++++++++++++ - 1 file changed, 58 insertions(+) - -diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c -index 5e3b4d10c1a9..2475eeab9c98 100644 ---- a/drivers/net/wireless/ath/ath10k/core.c -+++ b/drivers/net/wireless/ath/ath10k/core.c -@@ -35,6 +35,9 @@ static bool skip_otp; - static bool rawmode; - static bool fw_diag_log; - -+static char *override_board = ""; -+static char *override_board2 = ""; -+ - unsigned long ath10k_coredump_mask = BIT(ATH10K_FW_CRASH_DUMP_REGISTERS) | - BIT(ATH10K_FW_CRASH_DUMP_CE_DATA); - -@@ -47,6 +50,9 @@ module_param(rawmode, bool, 0644); - module_param(fw_diag_log, bool, 0644); - module_param_named(coredump_mask, ath10k_coredump_mask, ulong, 0444); - -+module_param(override_board, charp, 0644); -+module_param(override_board2, charp, 0644); -+ - MODULE_PARM_DESC(debug_mask, "Debugging mask"); - MODULE_PARM_DESC(uart_print, "Uart target debugging"); - MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode"); -@@ -55,6 +61,9 @@ MODULE_PARM_DESC(rawmode, "Use raw 802.11 frame datapath"); - MODULE_PARM_DESC(coredump_mask, "Bitfield of what to include in firmware crash file"); - MODULE_PARM_DESC(fw_diag_log, "Diag based fw log debugging"); - -+MODULE_PARM_DESC(override_board, "Override for board.bin file"); -+MODULE_PARM_DESC(override_board2, "Override for board-2.bin file"); -+ - static const struct ath10k_hw_params ath10k_hw_params_list[] = { - { - .id = QCA988X_HW_2_0_VERSION, -@@ -843,6 +852,42 @@ static int ath10k_init_configure_target(struct ath10k *ar) - return 0; - } - -+static const char *ath10k_override_board_fw_file(struct ath10k *ar, -+ const char *file) -+{ -+ if (strcmp(file, "board.bin") == 0) { -+ if (strcmp(override_board, "") == 0) -+ return file; -+ -+ if (strcmp(override_board, "none") == 0) { -+ dev_info(ar->dev, "firmware override: pretending 'board.bin' does not exist\n"); -+ return NULL; -+ } -+ -+ dev_info(ar->dev, "firmware override: replacing 'board.bin' with '%s'\n", -+ override_board); -+ -+ return override_board; -+ } -+ -+ if (strcmp(file, "board-2.bin") == 0) { -+ if (strcmp(override_board2, "") == 0) -+ return file; -+ -+ if (strcmp(override_board2, "none") == 0) { -+ dev_info(ar->dev, "firmware override: pretending 'board-2.bin' does not exist\n"); -+ return NULL; -+ } -+ -+ dev_info(ar->dev, "firmware override: replacing 'board-2.bin' with '%s'\n", -+ override_board2); -+ -+ return override_board2; -+ } -+ -+ return file; -+} -+ - static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar, - const char *dir, - const char *file) -@@ -857,6 +902,19 @@ static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar, - if (dir == NULL) - dir = "."; - -+ /* HACK: Override board.bin and board-2.bin files if specified. -+ * -+ * Some Surface devices perform better with a different board -+ * configuration. To this end, one would need to replace the board.bin -+ * file with the modified config and remove the board-2.bin file. -+ * Unfortunately, that's not a solution that we can easily package. So -+ * we add module options to perform these overrides here. -+ */ -+ -+ file = ath10k_override_board_fw_file(ar, file); -+ if (!file) -+ return ERR_PTR(-ENOENT); -+ - snprintf(filename, sizeof(filename), "%s/%s", dir, file); - ret = firmware_request_nowarn(&fw, filename, ar->dev); - ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot fw request '%s': %d\n", --- -2.35.1 - diff --git a/patches/5.16/0004-ipts.patch b/patches/5.16/0004-ipts.patch index e4b4c6de4..77339c5e5 100644 --- a/patches/5.16/0004-ipts.patch +++ b/patches/5.16/0004-ipts.patch @@ -1,4 +1,4 @@ -From fb09511fbb0c22e5704b5b835a55d1d5568c7f8d Mon Sep 17 00:00:00 2001 +From ac17ec1460ca95f496502ddd9acfeef9cee9928e Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Thu, 30 Jul 2020 13:21:53 +0200 Subject: [PATCH] misc: mei: Add missing IPTS device IDs @@ -36,7 +36,7 @@ index 3a45aaf002ac..55b8ee30a03c 100644 -- 2.35.1 -From 9aa5f4213859c7f8d60006246dd01e3ae45aab21 Mon Sep 17 00:00:00 2001 +From d4747f244ed7907f667efeb00f4f83d8ad9dc72e Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Thu, 6 Aug 2020 11:20:41 +0200 Subject: [PATCH] misc: Add support for Intel Precise Touch & Stylus diff --git a/patches/5.16/0005-ipts.patch b/patches/5.16/0005-ipts.patch deleted file mode 100644 index 77339c5e5..000000000 --- a/patches/5.16/0005-ipts.patch +++ /dev/null @@ -1,1503 +0,0 @@ -From ac17ec1460ca95f496502ddd9acfeef9cee9928e Mon Sep 17 00:00:00 2001 -From: Dorian Stoll -Date: Thu, 30 Jul 2020 13:21:53 +0200 -Subject: [PATCH] misc: mei: Add missing IPTS device IDs - -Patchset: ipts ---- - drivers/misc/mei/hw-me-regs.h | 1 + - drivers/misc/mei/pci-me.c | 1 + - 2 files changed, 2 insertions(+) - -diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h -index 67bb6a25fd0a..d1cb94d3452e 100644 ---- a/drivers/misc/mei/hw-me-regs.h -+++ b/drivers/misc/mei/hw-me-regs.h -@@ -92,6 +92,7 @@ - #define MEI_DEV_ID_CDF 0x18D3 /* Cedar Fork */ - - #define MEI_DEV_ID_ICP_LP 0x34E0 /* Ice Lake Point LP */ -+#define MEI_DEV_ID_ICP_LP_3 0x34E4 /* Ice Lake Point LP 3 (iTouch) */ - #define MEI_DEV_ID_ICP_N 0x38E0 /* Ice Lake Point N */ - - #define MEI_DEV_ID_JSP_N 0x4DE0 /* Jasper Lake Point N */ -diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c -index 3a45aaf002ac..55b8ee30a03c 100644 ---- a/drivers/misc/mei/pci-me.c -+++ b/drivers/misc/mei/pci-me.c -@@ -96,6 +96,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = { - {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_H_3, MEI_ME_PCH8_ITOUCH_CFG)}, - - {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)}, -+ {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP_3, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_N, MEI_ME_PCH12_CFG)}, - - {MEI_PCI_DEVICE(MEI_DEV_ID_TGP_LP, MEI_ME_PCH15_CFG)}, --- -2.35.1 - -From d4747f244ed7907f667efeb00f4f83d8ad9dc72e Mon Sep 17 00:00:00 2001 -From: Dorian Stoll -Date: Thu, 6 Aug 2020 11:20:41 +0200 -Subject: [PATCH] misc: Add support for Intel Precise Touch & Stylus - -Based on linux-surface/intel-precise-touch@3f362c - -Signed-off-by: Dorian Stoll -Patchset: ipts ---- - drivers/misc/Kconfig | 1 + - drivers/misc/Makefile | 1 + - drivers/misc/ipts/Kconfig | 17 ++ - drivers/misc/ipts/Makefile | 12 ++ - drivers/misc/ipts/context.h | 47 +++++ - drivers/misc/ipts/control.c | 113 +++++++++++ - drivers/misc/ipts/control.h | 24 +++ - drivers/misc/ipts/mei.c | 125 ++++++++++++ - drivers/misc/ipts/protocol.h | 347 ++++++++++++++++++++++++++++++++++ - drivers/misc/ipts/receiver.c | 224 ++++++++++++++++++++++ - drivers/misc/ipts/receiver.h | 16 ++ - drivers/misc/ipts/resources.c | 128 +++++++++++++ - drivers/misc/ipts/resources.h | 17 ++ - drivers/misc/ipts/uapi.c | 208 ++++++++++++++++++++ - drivers/misc/ipts/uapi.h | 47 +++++ - 15 files changed, 1327 insertions(+) - create mode 100644 drivers/misc/ipts/Kconfig - create mode 100644 drivers/misc/ipts/Makefile - create mode 100644 drivers/misc/ipts/context.h - create mode 100644 drivers/misc/ipts/control.c - create mode 100644 drivers/misc/ipts/control.h - create mode 100644 drivers/misc/ipts/mei.c - create mode 100644 drivers/misc/ipts/protocol.h - create mode 100644 drivers/misc/ipts/receiver.c - create mode 100644 drivers/misc/ipts/receiver.h - create mode 100644 drivers/misc/ipts/resources.c - create mode 100644 drivers/misc/ipts/resources.h - create mode 100644 drivers/misc/ipts/uapi.c - create mode 100644 drivers/misc/ipts/uapi.h - -diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig -index 0f5a49fc7c9e..12b081bc875a 100644 ---- a/drivers/misc/Kconfig -+++ b/drivers/misc/Kconfig -@@ -487,4 +487,5 @@ source "drivers/misc/cardreader/Kconfig" - source "drivers/misc/habanalabs/Kconfig" - source "drivers/misc/uacce/Kconfig" - source "drivers/misc/pvpanic/Kconfig" -+source "drivers/misc/ipts/Kconfig" - endmenu -diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile -index a086197af544..972cae33ba36 100644 ---- a/drivers/misc/Makefile -+++ b/drivers/misc/Makefile -@@ -59,3 +59,4 @@ obj-$(CONFIG_UACCE) += uacce/ - obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o - obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o - obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o -+obj-$(CONFIG_MISC_IPTS) += ipts/ -diff --git a/drivers/misc/ipts/Kconfig b/drivers/misc/ipts/Kconfig -new file mode 100644 -index 000000000000..83e2a930c396 ---- /dev/null -+++ b/drivers/misc/ipts/Kconfig -@@ -0,0 +1,17 @@ -+# SPDX-License-Identifier: GPL-2.0-or-later -+ -+config MISC_IPTS -+ tristate "Intel Precise Touch & Stylus" -+ depends on INTEL_MEI -+ help -+ Say Y here if your system has a touchscreen using Intels -+ Precise Touch & Stylus (IPTS) technology. -+ -+ If unsure say N. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called ipts. -+ -+ Building this driver alone will not give you a working touchscreen. -+ It only exposed a userspace API that can be used by a daemon to -+ receive and process data from the touchscreen hardware. -diff --git a/drivers/misc/ipts/Makefile b/drivers/misc/ipts/Makefile -new file mode 100644 -index 000000000000..8f58b9adbc94 ---- /dev/null -+++ b/drivers/misc/ipts/Makefile -@@ -0,0 +1,12 @@ -+# SPDX-License-Identifier: GPL-2.0-or-later -+# -+# Makefile for the IPTS touchscreen driver -+# -+ -+obj-$(CONFIG_MISC_IPTS) += ipts.o -+ipts-objs := control.o -+ipts-objs += mei.o -+ipts-objs += receiver.o -+ipts-objs += resources.o -+ipts-objs += uapi.o -+ -diff --git a/drivers/misc/ipts/context.h b/drivers/misc/ipts/context.h -new file mode 100644 -index 000000000000..f4b06a2d3f72 ---- /dev/null -+++ b/drivers/misc/ipts/context.h -@@ -0,0 +1,47 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+/* -+ * Copyright (c) 2016 Intel Corporation -+ * Copyright (c) 2020 Dorian Stoll -+ * -+ * Linux driver for Intel Precise Touch & Stylus -+ */ -+ -+#ifndef _IPTS_CONTEXT_H_ -+#define _IPTS_CONTEXT_H_ -+ -+#include -+#include -+#include -+#include -+ -+#include "protocol.h" -+ -+enum ipts_host_status { -+ IPTS_HOST_STATUS_STARTING, -+ IPTS_HOST_STATUS_STARTED, -+ IPTS_HOST_STATUS_STOPPING, -+ IPTS_HOST_STATUS_STOPPED, -+}; -+ -+struct ipts_buffer_info { -+ u8 *address; -+ dma_addr_t dma_address; -+}; -+ -+struct ipts_context { -+ struct mei_cl_device *cldev; -+ struct device *dev; -+ -+ bool restart; -+ enum ipts_host_status status; -+ struct ipts_get_device_info_rsp device_info; -+ -+ struct ipts_buffer_info data[IPTS_BUFFERS]; -+ struct ipts_buffer_info doorbell; -+ -+ struct ipts_buffer_info feedback[IPTS_BUFFERS]; -+ struct ipts_buffer_info workqueue; -+ struct ipts_buffer_info host2me; -+}; -+ -+#endif /* _IPTS_CONTEXT_H_ */ -diff --git a/drivers/misc/ipts/control.c b/drivers/misc/ipts/control.c -new file mode 100644 -index 000000000000..a1d1f97a13d7 ---- /dev/null -+++ b/drivers/misc/ipts/control.c -@@ -0,0 +1,113 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2016 Intel Corporation -+ * Copyright (c) 2020 Dorian Stoll -+ * -+ * Linux driver for Intel Precise Touch & Stylus -+ */ -+ -+#include -+ -+#include "context.h" -+#include "protocol.h" -+#include "resources.h" -+#include "uapi.h" -+ -+int ipts_control_send(struct ipts_context *ipts, u32 code, void *payload, -+ size_t size) -+{ -+ int ret; -+ struct ipts_command cmd; -+ -+ memset(&cmd, 0, sizeof(struct ipts_command)); -+ cmd.code = code; -+ -+ if (payload && size > 0) -+ memcpy(&cmd.payload, payload, size); -+ -+ ret = mei_cldev_send(ipts->cldev, (u8 *)&cmd, sizeof(cmd.code) + size); -+ if (ret >= 0) -+ return 0; -+ -+ /* -+ * During shutdown the device might get pulled away from below our feet. -+ * Dont log an error in this case, because it will confuse people. -+ */ -+ if (ret != -ENODEV || ipts->status != IPTS_HOST_STATUS_STOPPING) -+ dev_err(ipts->dev, "Error while sending: 0x%X:%d\n", code, ret); -+ -+ return ret; -+} -+ -+int ipts_control_send_feedback(struct ipts_context *ipts, u32 buffer) -+{ -+ struct ipts_feedback_cmd cmd; -+ -+ memset(&cmd, 0, sizeof(struct ipts_feedback_cmd)); -+ cmd.buffer = buffer; -+ -+ return ipts_control_send(ipts, IPTS_CMD_FEEDBACK, &cmd, -+ sizeof(struct ipts_feedback_cmd)); -+} -+ -+int ipts_control_set_feature(struct ipts_context *ipts, u8 report, u8 value) -+{ -+ struct ipts_feedback_buffer *feedback; -+ -+ memset(ipts->host2me.address, 0, ipts->device_info.feedback_size); -+ feedback = (struct ipts_feedback_buffer *)ipts->host2me.address; -+ -+ feedback->cmd_type = IPTS_FEEDBACK_CMD_TYPE_NONE; -+ feedback->data_type = IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES; -+ feedback->buffer = IPTS_HOST2ME_BUFFER; -+ feedback->size = 2; -+ feedback->payload[0] = report; -+ feedback->payload[1] = value; -+ -+ return ipts_control_send_feedback(ipts, IPTS_HOST2ME_BUFFER); -+} -+ -+int ipts_control_start(struct ipts_context *ipts) -+{ -+ if (ipts->status != IPTS_HOST_STATUS_STOPPED) -+ return -EBUSY; -+ -+ dev_info(ipts->dev, "Starting IPTS\n"); -+ ipts->status = IPTS_HOST_STATUS_STARTING; -+ ipts->restart = false; -+ -+ ipts_uapi_link(ipts); -+ return ipts_control_send(ipts, IPTS_CMD_GET_DEVICE_INFO, NULL, 0); -+} -+ -+int ipts_control_stop(struct ipts_context *ipts) -+{ -+ int ret; -+ -+ if (ipts->status == IPTS_HOST_STATUS_STOPPING) -+ return -EBUSY; -+ -+ if (ipts->status == IPTS_HOST_STATUS_STOPPED) -+ return -EBUSY; -+ -+ dev_info(ipts->dev, "Stopping IPTS\n"); -+ ipts->status = IPTS_HOST_STATUS_STOPPING; -+ -+ ipts_uapi_unlink(); -+ ipts_resources_free(ipts); -+ -+ ret = ipts_control_send_feedback(ipts, 0); -+ if (ret == -ENODEV) -+ ipts->status = IPTS_HOST_STATUS_STOPPED; -+ -+ return ret; -+} -+ -+int ipts_control_restart(struct ipts_context *ipts) -+{ -+ if (ipts->restart) -+ return -EBUSY; -+ -+ ipts->restart = true; -+ return ipts_control_stop(ipts); -+} -diff --git a/drivers/misc/ipts/control.h b/drivers/misc/ipts/control.h -new file mode 100644 -index 000000000000..2c44e9e0e99f ---- /dev/null -+++ b/drivers/misc/ipts/control.h -@@ -0,0 +1,24 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+/* -+ * Copyright (c) 2016 Intel Corporation -+ * Copyright (c) 2020 Dorian Stoll -+ * -+ * Linux driver for Intel Precise Touch & Stylus -+ */ -+ -+#ifndef _IPTS_CONTROL_H_ -+#define _IPTS_CONTROL_H_ -+ -+#include -+ -+#include "context.h" -+ -+int ipts_control_send(struct ipts_context *ipts, u32 cmd, void *payload, -+ size_t size); -+int ipts_control_send_feedback(struct ipts_context *ipts, u32 buffer); -+int ipts_control_set_feature(struct ipts_context *ipts, u8 report, u8 value); -+int ipts_control_start(struct ipts_context *ipts); -+int ipts_control_restart(struct ipts_context *ipts); -+int ipts_control_stop(struct ipts_context *ipts); -+ -+#endif /* _IPTS_CONTROL_H_ */ -diff --git a/drivers/misc/ipts/mei.c b/drivers/misc/ipts/mei.c -new file mode 100644 -index 000000000000..59ecf13e00d2 ---- /dev/null -+++ b/drivers/misc/ipts/mei.c -@@ -0,0 +1,125 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2016 Intel Corporation -+ * Copyright (c) 2020 Dorian Stoll -+ * -+ * Linux driver for Intel Precise Touch & Stylus -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "context.h" -+#include "control.h" -+#include "protocol.h" -+#include "receiver.h" -+#include "uapi.h" -+ -+static int ipts_mei_set_dma_mask(struct mei_cl_device *cldev) -+{ -+ int ret; -+ -+ ret = dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(64)); -+ if (!ret) -+ return 0; -+ -+ return dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(32)); -+} -+ -+static int ipts_mei_probe(struct mei_cl_device *cldev, -+ const struct mei_cl_device_id *id) -+{ -+ int ret; -+ struct ipts_context *ipts; -+ -+ if (ipts_mei_set_dma_mask(cldev)) { -+ dev_err(&cldev->dev, "Failed to set DMA mask for IPTS\n"); -+ return -EFAULT; -+ } -+ -+ ret = mei_cldev_enable(cldev); -+ if (ret) { -+ dev_err(&cldev->dev, "Failed to enable MEI device: %d\n", ret); -+ return ret; -+ } -+ -+ ipts = kzalloc(sizeof(*ipts), GFP_KERNEL); -+ if (!ipts) { -+ mei_cldev_disable(cldev); -+ return -ENOMEM; -+ } -+ -+ ipts->cldev = cldev; -+ ipts->dev = &cldev->dev; -+ ipts->status = IPTS_HOST_STATUS_STOPPED; -+ -+ mei_cldev_set_drvdata(cldev, ipts); -+ mei_cldev_register_rx_cb(cldev, ipts_receiver_callback); -+ -+ return ipts_control_start(ipts); -+} -+ -+static void ipts_mei_remove(struct mei_cl_device *cldev) -+{ -+ int i; -+ struct ipts_context *ipts = mei_cldev_get_drvdata(cldev); -+ -+ ipts_control_stop(ipts); -+ -+ for (i = 0; i < 20; i++) { -+ if (ipts->status == IPTS_HOST_STATUS_STOPPED) -+ break; -+ -+ msleep(25); -+ } -+ -+ mei_cldev_disable(cldev); -+ kfree(ipts); -+} -+ -+static struct mei_cl_device_id ipts_mei_device_id_table[] = { -+ { "", IPTS_MEI_UUID, MEI_CL_VERSION_ANY }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(mei, ipts_mei_device_id_table); -+ -+static struct mei_cl_driver ipts_mei_driver = { -+ .id_table = ipts_mei_device_id_table, -+ .name = "ipts", -+ .probe = ipts_mei_probe, -+ .remove = ipts_mei_remove, -+}; -+ -+static int __init ipts_mei_init(void) -+{ -+ int ret; -+ -+ ret = ipts_uapi_init(); -+ if (ret) -+ return ret; -+ -+ ret = mei_cldev_driver_register(&ipts_mei_driver); -+ if (ret) { -+ ipts_uapi_free(); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static void __exit ipts_mei_exit(void) -+{ -+ mei_cldev_driver_unregister(&ipts_mei_driver); -+ ipts_uapi_free(); -+} -+ -+MODULE_DESCRIPTION("IPTS touchscreen driver"); -+MODULE_AUTHOR("Dorian Stoll "); -+MODULE_LICENSE("GPL"); -+ -+module_init(ipts_mei_init); -+module_exit(ipts_mei_exit); -diff --git a/drivers/misc/ipts/protocol.h b/drivers/misc/ipts/protocol.h -new file mode 100644 -index 000000000000..c3458904a94d ---- /dev/null -+++ b/drivers/misc/ipts/protocol.h -@@ -0,0 +1,347 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+/* -+ * Copyright (c) 2016 Intel Corporation -+ * Copyright (c) 2020 Dorian Stoll -+ * -+ * Linux driver for Intel Precise Touch & Stylus -+ */ -+ -+#ifndef _IPTS_PROTOCOL_H_ -+#define _IPTS_PROTOCOL_H_ -+ -+#include -+ -+/* -+ * The MEI client ID for IPTS functionality. -+ */ -+#define IPTS_MEI_UUID \ -+ UUID_LE(0x3e8d0870, 0x271a, 0x4208, 0x8e, 0xb5, 0x9a, 0xcb, 0x94, \ -+ 0x02, 0xae, 0x04) -+ -+/* -+ * Queries the device for vendor specific information. -+ * -+ * The command must not contain any payload. -+ * The response will contain struct ipts_get_device_info_rsp as payload. -+ */ -+#define IPTS_CMD_GET_DEVICE_INFO 0x00000001 -+#define IPTS_RSP_GET_DEVICE_INFO 0x80000001 -+ -+/* -+ * Sets the mode that IPTS will operate in. -+ * -+ * The command must contain struct ipts_set_mode_cmd as payload. -+ * The response will not contain any payload. -+ */ -+#define IPTS_CMD_SET_MODE 0x00000002 -+#define IPTS_RSP_SET_MODE 0x80000002 -+ -+/* -+ * Configures the memory buffers that the ME will use -+ * for passing data to the host. -+ * -+ * The command must contain struct ipts_set_mem_window_cmd as payload. -+ * The response will not contain any payload. -+ */ -+#define IPTS_CMD_SET_MEM_WINDOW 0x00000003 -+#define IPTS_RSP_SET_MEM_WINDOW 0x80000003 -+ -+/* -+ * Signals that the host is ready to receive data to the ME. -+ * -+ * The command must not contain any payload. -+ * The response will not contain any payload. -+ */ -+#define IPTS_CMD_READY_FOR_DATA 0x00000005 -+#define IPTS_RSP_READY_FOR_DATA 0x80000005 -+ -+/* -+ * Signals that a buffer can be refilled to the ME. -+ * -+ * The command must contain struct ipts_feedback_cmd as payload. -+ * The response will not contain any payload. -+ */ -+#define IPTS_CMD_FEEDBACK 0x00000006 -+#define IPTS_RSP_FEEDBACK 0x80000006 -+ -+/* -+ * Resets the data flow from the ME to the hosts and -+ * clears the buffers that were set with SET_MEM_WINDOW. -+ * -+ * The command must not contain any payload. -+ * The response will not contain any payload. -+ */ -+#define IPTS_CMD_CLEAR_MEM_WINDOW 0x00000007 -+#define IPTS_RSP_CLEAR_MEM_WINDOW 0x80000007 -+ -+/* -+ * Instructs the ME to reset the touch sensor. -+ * -+ * The command must contain struct ipts_reset_sensor_cmd as payload. -+ * The response will not contain any payload. -+ */ -+#define IPTS_CMD_RESET_SENSOR 0x0000000B -+#define IPTS_RSP_RESET_SENSOR 0x8000000B -+ -+/** -+ * enum ipts_status - Possible status codes returned by IPTS commands. -+ * @IPTS_STATUS_SUCCESS: Operation completed successfully. -+ * @IPTS_STATUS_INVALID_PARAMS: Command contained a payload with invalid parameters. -+ * @IPTS_STATUS_ACCESS_DENIED: ME could not validate buffer addresses supplied by host. -+ * @IPTS_STATUS_CMD_SIZE_ERROR: Command contains an invalid payload. -+ * @IPTS_STATUS_NOT_READY: Buffer addresses have not been set. -+ * @IPTS_STATUS_REQUEST_OUTSTANDING: There is an outstanding command of the same type. -+ * The host must wait for a response before sending another -+ * command of the same type. -+ * @IPTS_STATUS_NO_SENSOR_FOUND: No sensor could be found. Either no sensor is connected, it -+ * has not been initialized yet, or the system is improperly -+ * configured. -+ * @IPTS_STATUS_OUT_OF_MEMORY: Not enough free memory for requested operation. -+ * @IPTS_STATUS_INTERNAL_ERROR: An unexpected error occurred. -+ * @IPTS_STATUS_SENSOR_DISABLED: The sensor has been disabled and must be reinitialized. -+ * @IPTS_STATUS_COMPAT_CHECK_FAIL: Compatibility revision check between sensor and ME failed. -+ * The host can ignore this error and attempt to continue. -+ * @IPTS_STATUS_SENSOR_EXPECTED_RESET: The sensor went through a reset initiated by ME or host. -+ * @IPTS_STATUS_SENSOR_UNEXPECTED_RESET: The sensor went through an unexpected reset. -+ * @IPTS_STATUS_RESET_FAILED: Requested sensor reset failed to complete. -+ * @IPTS_STATUS_TIMEOUT: The operation timed out. -+ * @IPTS_STATUS_TEST_MODE_FAIL: Test mode pattern did not match expected values. -+ * @IPTS_STATUS_SENSOR_FAIL_FATAL: The sensor reported a fatal error during reset sequence. -+ * Further progress is not possible. -+ * @IPTS_STATUS_SENSOR_FAIL_NONFATAL: The sensor reported a fatal error during reset sequence. -+ * The host can attempt to continue. -+ * @IPTS_STATUS_INVALID_DEVICE_CAPS: The device reported invalid capabilities. -+ * @IPTS_STATUS_QUIESCE_IO_IN_PROGRESS: Command cannot be completed until Quiesce IO is done. -+ */ -+enum ipts_status { -+ IPTS_STATUS_SUCCESS = 0, -+ IPTS_STATUS_INVALID_PARAMS = 1, -+ IPTS_STATUS_ACCESS_DENIED = 2, -+ IPTS_STATUS_CMD_SIZE_ERROR = 3, -+ IPTS_STATUS_NOT_READY = 4, -+ IPTS_STATUS_REQUEST_OUTSTANDING = 5, -+ IPTS_STATUS_NO_SENSOR_FOUND = 6, -+ IPTS_STATUS_OUT_OF_MEMORY = 7, -+ IPTS_STATUS_INTERNAL_ERROR = 8, -+ IPTS_STATUS_SENSOR_DISABLED = 9, -+ IPTS_STATUS_COMPAT_CHECK_FAIL = 10, -+ IPTS_STATUS_SENSOR_EXPECTED_RESET = 11, -+ IPTS_STATUS_SENSOR_UNEXPECTED_RESET = 12, -+ IPTS_STATUS_RESET_FAILED = 13, -+ IPTS_STATUS_TIMEOUT = 14, -+ IPTS_STATUS_TEST_MODE_FAIL = 15, -+ IPTS_STATUS_SENSOR_FAIL_FATAL = 16, -+ IPTS_STATUS_SENSOR_FAIL_NONFATAL = 17, -+ IPTS_STATUS_INVALID_DEVICE_CAPS = 18, -+ IPTS_STATUS_QUIESCE_IO_IN_PROGRESS = 19, -+}; -+ -+/* -+ * The amount of buffers that is used for IPTS -+ */ -+#define IPTS_BUFFERS 16 -+ -+/* -+ * The special buffer ID that is used for direct host2me feedback. -+ */ -+#define IPTS_HOST2ME_BUFFER IPTS_BUFFERS -+ -+/** -+ * enum ipts_mode - Operation mode for IPTS hardware -+ * @IPTS_MODE_SINGLETOUCH: Fallback that supports only one finger and no stylus. -+ * The data is received as a HID report with ID 64. -+ * @IPTS_MODE_MULTITOUCH: The "proper" operation mode for IPTS. It will return -+ * stylus data as well as capacitive heatmap touch data. -+ * This data needs to be processed in userspace. -+ */ -+enum ipts_mode { -+ IPTS_MODE_SINGLETOUCH = 0, -+ IPTS_MODE_MULTITOUCH = 1, -+}; -+ -+/** -+ * struct ipts_set_mode_cmd - Payload for the SET_MODE command. -+ * @mode: The mode that IPTS should operate in. -+ */ -+struct ipts_set_mode_cmd { -+ enum ipts_mode mode; -+ u8 reserved[12]; -+} __packed; -+ -+#define IPTS_WORKQUEUE_SIZE 8192 -+#define IPTS_WORKQUEUE_ITEM_SIZE 16 -+ -+/** -+ * struct ipts_set_mem_window_cmd - Payload for the SET_MEM_WINDOW command. -+ * @data_buffer_addr_lower: Lower 32 bits of the data buffer addresses. -+ * @data_buffer_addr_upper: Upper 32 bits of the data buffer addresses. -+ * @workqueue_addr_lower: Lower 32 bits of the workqueue buffer address. -+ * @workqueue_addr_upper: Upper 32 bits of the workqueue buffer address. -+ * @doorbell_addr_lower: Lower 32 bits of the doorbell buffer address. -+ * @doorbell_addr_upper: Upper 32 bits of the doorbell buffer address. -+ * @feedback_buffer_addr_lower: Lower 32 bits of the feedback buffer addresses. -+ * @feedback_buffer_addr_upper: Upper 32 bits of the feedback buffer addresses. -+ * @host2me_addr_lower: Lower 32 bits of the host2me buffer address. -+ * @host2me_addr_upper: Upper 32 bits of the host2me buffer address. -+ * @workqueue_item_size: Magic value. (IPTS_WORKQUEUE_ITEM_SIZE) -+ * @workqueue_size: Magic value. (IPTS_WORKQUEUE_SIZE) -+ * -+ * The data buffers are buffers that get filled with touch data by the ME. -+ * The doorbell buffer is a u32 that gets incremented by the ME once a data -+ * buffer has been filled with new data. -+ * -+ * The other buffers are required for using GuC submission with binary -+ * firmware. Since support for GuC submission has been dropped from i915, -+ * they are not used anymore, but they need to be allocated and passed, -+ * otherwise the hardware will refuse to start. -+ */ -+struct ipts_set_mem_window_cmd { -+ u32 data_buffer_addr_lower[IPTS_BUFFERS]; -+ u32 data_buffer_addr_upper[IPTS_BUFFERS]; -+ u32 workqueue_addr_lower; -+ u32 workqueue_addr_upper; -+ u32 doorbell_addr_lower; -+ u32 doorbell_addr_upper; -+ u32 feedback_buffer_addr_lower[IPTS_BUFFERS]; -+ u32 feedback_buffer_addr_upper[IPTS_BUFFERS]; -+ u32 host2me_addr_lower; -+ u32 host2me_addr_upper; -+ u32 host2me_size; -+ u8 reserved1; -+ u8 workqueue_item_size; -+ u16 workqueue_size; -+ u8 reserved[32]; -+} __packed; -+ -+/** -+ * struct ipts_feedback_cmd - Payload for the FEEDBACK command. -+ * @buffer: The buffer that the ME should refill. -+ */ -+struct ipts_feedback_cmd { -+ u32 buffer; -+ u8 reserved[12]; -+} __packed; -+ -+/** -+ * enum ipts_feedback_cmd_type - Commands that can be executed on the sensor through feedback. -+ */ -+enum ipts_feedback_cmd_type { -+ IPTS_FEEDBACK_CMD_TYPE_NONE = 0, -+ IPTS_FEEDBACK_CMD_TYPE_SOFT_RESET = 1, -+ IPTS_FEEDBACK_CMD_TYPE_GOTO_ARMED = 2, -+ IPTS_FEEDBACK_CMD_TYPE_GOTO_SENSING = 3, -+ IPTS_FEEDBACK_CMD_TYPE_GOTO_SLEEP = 4, -+ IPTS_FEEDBACK_CMD_TYPE_GOTO_DOZE = 5, -+ IPTS_FEEDBACK_CMD_TYPE_HARD_RESET = 6, -+}; -+ -+/** -+ * enum ipts_feedback_data_type - Describes the data that a feedback buffer contains. -+ * @IPTS_FEEDBACK_DATA_TYPE_VENDOR: The buffer contains vendor specific feedback. -+ * @IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES: The buffer contains a HID set features command. -+ * @IPTS_FEEDBACK_DATA_TYPE_GET_FEATURES: The buffer contains a HID get features command. -+ * @IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT: The buffer contains a HID output report. -+ * @IPTS_FEEDBACK_DATA_TYPE_STORE_DATA: The buffer contains calibration data for the sensor. -+ */ -+enum ipts_feedback_data_type { -+ IPTS_FEEDBACK_DATA_TYPE_VENDOR = 0, -+ IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES = 1, -+ IPTS_FEEDBACK_DATA_TYPE_GET_FEATURES = 2, -+ IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT = 3, -+ IPTS_FEEDBACK_DATA_TYPE_STORE_DATA = 4, -+}; -+ -+/** -+ * struct ipts_feedback_buffer - The contents of an IPTS feedback buffer. -+ * @cmd_type: A command that should be executed on the sensor. -+ * @size: The size of the payload to be written. -+ * @buffer: The ID of the buffer that contains this feedback data. -+ * @protocol: The protocol version of the EDS. -+ * @data_type: The type of payload that the buffer contains. -+ * @spi_offset: The offset at which to write the payload data. -+ * @payload: Payload for the feedback command, or 0 if no payload is sent. -+ */ -+struct ipts_feedback_buffer { -+ enum ipts_feedback_cmd_type cmd_type; -+ u32 size; -+ u32 buffer; -+ u32 protocol; -+ enum ipts_feedback_data_type data_type; -+ u32 spi_offset; -+ u8 reserved[40]; -+ u8 payload[]; -+} __packed; -+ -+/** -+ * enum ipts_reset_type - Possible ways of resetting the touch sensor -+ * @IPTS_RESET_TYPE_HARD: Perform hardware reset using GPIO pin. -+ * @IPTS_RESET_TYPE_SOFT: Perform software reset using SPI interface. -+ */ -+enum ipts_reset_type { -+ IPTS_RESET_TYPE_HARD = 0, -+ IPTS_RESET_TYPE_SOFT = 1, -+}; -+ -+/** -+ * struct ipts_reset_sensor_cmd - Payload for the RESET_SENSOR command. -+ * @type: What type of reset should be performed. -+ */ -+struct ipts_reset_sensor_cmd { -+ enum ipts_reset_type type; -+ u8 reserved[4]; -+} __packed; -+ -+/** -+ * struct ipts_command - A message sent from the host to the ME. -+ * @code: The message code describing the command. (see IPTS_CMD_*) -+ * @payload: Payload for the command, or 0 if no payload is required. -+ */ -+struct ipts_command { -+ u32 code; -+ u8 payload[320]; -+} __packed; -+ -+/** -+ * struct ipts_device_info - Payload for the GET_DEVICE_INFO response. -+ * @vendor_id: Vendor ID of the touch sensor. -+ * @device_id: Device ID of the touch sensor. -+ * @hw_rev: Hardware revision of the touch sensor. -+ * @fw_rev: Firmware revision of the touch sensor. -+ * @data_size: Required size of one data buffer. -+ * @feedback_size: Required size of one feedback buffer. -+ * @mode: Current operation mode of IPTS. -+ * @max_contacts: The amount of concurrent touches supported by the sensor. -+ */ -+struct ipts_get_device_info_rsp { -+ u16 vendor_id; -+ u16 device_id; -+ u32 hw_rev; -+ u32 fw_rev; -+ u32 data_size; -+ u32 feedback_size; -+ enum ipts_mode mode; -+ u8 max_contacts; -+ u8 reserved[19]; -+} __packed; -+ -+/** -+ * struct ipts_feedback_rsp - Payload for the FEEDBACK response. -+ * @buffer: The buffer that has received feedback. -+ */ -+struct ipts_feedback_rsp { -+ u32 buffer; -+} __packed; -+ -+/** -+ * struct ipts_response - A message sent from the ME to the host. -+ * @code: The message code describing the response. (see IPTS_RSP_*) -+ * @status: The status code returned by the command. -+ * @payload: Payload returned by the command. -+ */ -+struct ipts_response { -+ u32 code; -+ enum ipts_status status; -+ u8 payload[80]; -+} __packed; -+ -+#endif /* _IPTS_PROTOCOL_H_ */ -diff --git a/drivers/misc/ipts/receiver.c b/drivers/misc/ipts/receiver.c -new file mode 100644 -index 000000000000..23dca13c2139 ---- /dev/null -+++ b/drivers/misc/ipts/receiver.c -@@ -0,0 +1,224 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2016 Intel Corporation -+ * Copyright (c) 2020 Dorian Stoll -+ * -+ * Linux driver for Intel Precise Touch & Stylus -+ */ -+ -+#include -+#include -+#include -+ -+#include "context.h" -+#include "control.h" -+#include "protocol.h" -+#include "resources.h" -+ -+/* -+ * Temporary parameter to guard gen7 multitouch mode. -+ * Remove once gen7 has stable iptsd support. -+ */ -+static bool gen7mt; -+module_param(gen7mt, bool, 0644); -+ -+static int ipts_receiver_handle_get_device_info(struct ipts_context *ipts, -+ struct ipts_response *rsp) -+{ -+ struct ipts_set_mode_cmd cmd; -+ -+ memcpy(&ipts->device_info, rsp->payload, -+ sizeof(struct ipts_get_device_info_rsp)); -+ -+ memset(&cmd, 0, sizeof(struct ipts_set_mode_cmd)); -+ cmd.mode = IPTS_MODE_MULTITOUCH; -+ -+ return ipts_control_send(ipts, IPTS_CMD_SET_MODE, &cmd, -+ sizeof(struct ipts_set_mode_cmd)); -+} -+ -+static int ipts_receiver_handle_set_mode(struct ipts_context *ipts) -+{ -+ int i, ret; -+ struct ipts_set_mem_window_cmd cmd; -+ -+ ret = ipts_resources_alloc(ipts); -+ if (ret) { -+ dev_err(ipts->dev, "Failed to allocate resources\n"); -+ return ret; -+ } -+ -+ memset(&cmd, 0, sizeof(struct ipts_set_mem_window_cmd)); -+ -+ for (i = 0; i < IPTS_BUFFERS; i++) { -+ cmd.data_buffer_addr_lower[i] = -+ lower_32_bits(ipts->data[i].dma_address); -+ -+ cmd.data_buffer_addr_upper[i] = -+ upper_32_bits(ipts->data[i].dma_address); -+ -+ cmd.feedback_buffer_addr_lower[i] = -+ lower_32_bits(ipts->feedback[i].dma_address); -+ -+ cmd.feedback_buffer_addr_upper[i] = -+ upper_32_bits(ipts->feedback[i].dma_address); -+ } -+ -+ cmd.workqueue_addr_lower = lower_32_bits(ipts->workqueue.dma_address); -+ cmd.workqueue_addr_upper = upper_32_bits(ipts->workqueue.dma_address); -+ -+ cmd.doorbell_addr_lower = lower_32_bits(ipts->doorbell.dma_address); -+ cmd.doorbell_addr_upper = upper_32_bits(ipts->doorbell.dma_address); -+ -+ cmd.host2me_addr_lower = lower_32_bits(ipts->host2me.dma_address); -+ cmd.host2me_addr_upper = upper_32_bits(ipts->host2me.dma_address); -+ -+ cmd.workqueue_size = IPTS_WORKQUEUE_SIZE; -+ cmd.workqueue_item_size = IPTS_WORKQUEUE_ITEM_SIZE; -+ -+ return ipts_control_send(ipts, IPTS_CMD_SET_MEM_WINDOW, &cmd, -+ sizeof(struct ipts_set_mem_window_cmd)); -+} -+ -+static int ipts_receiver_handle_set_mem_window(struct ipts_context *ipts) -+{ -+ int ret; -+ -+ dev_info(ipts->dev, "Device %04hX:%04hX ready\n", -+ ipts->device_info.vendor_id, ipts->device_info.device_id); -+ ipts->status = IPTS_HOST_STATUS_STARTED; -+ -+ ret = ipts_control_send(ipts, IPTS_CMD_READY_FOR_DATA, NULL, 0); -+ if (ret) -+ return ret; -+ -+ if (!gen7mt) -+ return 0; -+ -+ return ipts_control_set_feature(ipts, 0x5, 0x1); -+} -+ -+static int ipts_receiver_handle_feedback(struct ipts_context *ipts, -+ struct ipts_response *rsp) -+{ -+ struct ipts_feedback_rsp feedback; -+ -+ if (ipts->status != IPTS_HOST_STATUS_STOPPING) -+ return 0; -+ -+ memcpy(&feedback, rsp->payload, sizeof(feedback)); -+ -+ if (feedback.buffer < IPTS_BUFFERS - 1) -+ return ipts_control_send_feedback(ipts, feedback.buffer + 1); -+ -+ return ipts_control_send(ipts, IPTS_CMD_CLEAR_MEM_WINDOW, NULL, 0); -+} -+ -+static int ipts_receiver_handle_clear_mem_window(struct ipts_context *ipts) -+{ -+ ipts->status = IPTS_HOST_STATUS_STOPPED; -+ -+ if (ipts->restart) -+ return ipts_control_start(ipts); -+ -+ return 0; -+} -+ -+static bool ipts_receiver_sensor_was_reset(u32 status) -+{ -+ return status == IPTS_STATUS_SENSOR_EXPECTED_RESET || -+ status == IPTS_STATUS_SENSOR_UNEXPECTED_RESET; -+} -+ -+static bool ipts_receiver_handle_error(struct ipts_context *ipts, -+ struct ipts_response *rsp) -+{ -+ bool error; -+ -+ switch (rsp->status) { -+ case IPTS_STATUS_SUCCESS: -+ case IPTS_STATUS_COMPAT_CHECK_FAIL: -+ error = false; -+ break; -+ case IPTS_STATUS_INVALID_PARAMS: -+ error = rsp->code != IPTS_RSP_FEEDBACK; -+ break; -+ case IPTS_STATUS_SENSOR_DISABLED: -+ error = ipts->status != IPTS_HOST_STATUS_STOPPING; -+ break; -+ default: -+ error = true; -+ break; -+ } -+ -+ if (!error) -+ return false; -+ -+ dev_err(ipts->dev, "Command 0x%08x failed: %d\n", rsp->code, -+ rsp->status); -+ -+ if (ipts_receiver_sensor_was_reset(rsp->status)) { -+ dev_err(ipts->dev, "Sensor was reset\n"); -+ -+ if (ipts_control_restart(ipts)) -+ dev_err(ipts->dev, "Failed to restart IPTS\n"); -+ } -+ -+ return true; -+} -+ -+static void ipts_receiver_handle_response(struct ipts_context *ipts, -+ struct ipts_response *rsp) -+{ -+ int ret; -+ -+ if (ipts_receiver_handle_error(ipts, rsp)) -+ return; -+ -+ switch (rsp->code) { -+ case IPTS_RSP_GET_DEVICE_INFO: -+ ret = ipts_receiver_handle_get_device_info(ipts, rsp); -+ break; -+ case IPTS_RSP_SET_MODE: -+ ret = ipts_receiver_handle_set_mode(ipts); -+ break; -+ case IPTS_RSP_SET_MEM_WINDOW: -+ ret = ipts_receiver_handle_set_mem_window(ipts); -+ break; -+ case IPTS_RSP_FEEDBACK: -+ ret = ipts_receiver_handle_feedback(ipts, rsp); -+ break; -+ case IPTS_RSP_CLEAR_MEM_WINDOW: -+ ret = ipts_receiver_handle_clear_mem_window(ipts); -+ break; -+ default: -+ ret = 0; -+ break; -+ } -+ -+ if (!ret) -+ return; -+ -+ dev_err(ipts->dev, "Error while handling response 0x%08x: %d\n", -+ rsp->code, ret); -+ -+ if (ipts_control_stop(ipts)) -+ dev_err(ipts->dev, "Failed to stop IPTS\n"); -+} -+ -+void ipts_receiver_callback(struct mei_cl_device *cldev) -+{ -+ int ret; -+ struct ipts_response rsp; -+ struct ipts_context *ipts; -+ -+ ipts = mei_cldev_get_drvdata(cldev); -+ -+ ret = mei_cldev_recv(cldev, (u8 *)&rsp, sizeof(struct ipts_response)); -+ if (ret <= 0) { -+ dev_err(ipts->dev, "Error while reading response: %d\n", ret); -+ return; -+ } -+ -+ ipts_receiver_handle_response(ipts, &rsp); -+} -diff --git a/drivers/misc/ipts/receiver.h b/drivers/misc/ipts/receiver.h -new file mode 100644 -index 000000000000..7f075afa7ef8 ---- /dev/null -+++ b/drivers/misc/ipts/receiver.h -@@ -0,0 +1,16 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+/* -+ * Copyright (c) 2016 Intel Corporation -+ * Copyright (c) 2020 Dorian Stoll -+ * -+ * Linux driver for Intel Precise Touch & Stylus -+ */ -+ -+#ifndef _IPTS_RECEIVER_H_ -+#define _IPTS_RECEIVER_H_ -+ -+#include -+ -+void ipts_receiver_callback(struct mei_cl_device *cldev); -+ -+#endif /* _IPTS_RECEIVER_H_ */ -diff --git a/drivers/misc/ipts/resources.c b/drivers/misc/ipts/resources.c -new file mode 100644 -index 000000000000..8e3a2409e438 ---- /dev/null -+++ b/drivers/misc/ipts/resources.c -@@ -0,0 +1,128 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2016 Intel Corporation -+ * Copyright (c) 2020 Dorian Stoll -+ * -+ * Linux driver for Intel Precise Touch & Stylus -+ */ -+ -+#include -+ -+#include "context.h" -+ -+void ipts_resources_free(struct ipts_context *ipts) -+{ -+ int i; -+ struct ipts_buffer_info *buffers; -+ -+ u32 data_buffer_size = ipts->device_info.data_size; -+ u32 feedback_buffer_size = ipts->device_info.feedback_size; -+ -+ buffers = ipts->data; -+ for (i = 0; i < IPTS_BUFFERS; i++) { -+ if (!buffers[i].address) -+ continue; -+ -+ dma_free_coherent(ipts->dev, data_buffer_size, -+ buffers[i].address, buffers[i].dma_address); -+ -+ buffers[i].address = NULL; -+ buffers[i].dma_address = 0; -+ } -+ -+ buffers = ipts->feedback; -+ for (i = 0; i < IPTS_BUFFERS; i++) { -+ if (!buffers[i].address) -+ continue; -+ -+ dma_free_coherent(ipts->dev, feedback_buffer_size, -+ buffers[i].address, buffers[i].dma_address); -+ -+ buffers[i].address = NULL; -+ buffers[i].dma_address = 0; -+ } -+ -+ if (ipts->doorbell.address) { -+ dma_free_coherent(ipts->dev, sizeof(u32), -+ ipts->doorbell.address, -+ ipts->doorbell.dma_address); -+ -+ ipts->doorbell.address = NULL; -+ ipts->doorbell.dma_address = 0; -+ } -+ -+ if (ipts->workqueue.address) { -+ dma_free_coherent(ipts->dev, sizeof(u32), -+ ipts->workqueue.address, -+ ipts->workqueue.dma_address); -+ -+ ipts->workqueue.address = NULL; -+ ipts->workqueue.dma_address = 0; -+ } -+ -+ if (ipts->host2me.address) { -+ dma_free_coherent(ipts->dev, feedback_buffer_size, -+ ipts->host2me.address, -+ ipts->host2me.dma_address); -+ -+ ipts->host2me.address = NULL; -+ ipts->host2me.dma_address = 0; -+ } -+} -+ -+int ipts_resources_alloc(struct ipts_context *ipts) -+{ -+ int i; -+ struct ipts_buffer_info *buffers; -+ -+ u32 data_buffer_size = ipts->device_info.data_size; -+ u32 feedback_buffer_size = ipts->device_info.feedback_size; -+ -+ buffers = ipts->data; -+ for (i = 0; i < IPTS_BUFFERS; i++) { -+ buffers[i].address = -+ dma_alloc_coherent(ipts->dev, data_buffer_size, -+ &buffers[i].dma_address, GFP_KERNEL); -+ -+ if (!buffers[i].address) -+ goto release_resources; -+ } -+ -+ buffers = ipts->feedback; -+ for (i = 0; i < IPTS_BUFFERS; i++) { -+ buffers[i].address = -+ dma_alloc_coherent(ipts->dev, feedback_buffer_size, -+ &buffers[i].dma_address, GFP_KERNEL); -+ -+ if (!buffers[i].address) -+ goto release_resources; -+ } -+ -+ ipts->doorbell.address = -+ dma_alloc_coherent(ipts->dev, sizeof(u32), -+ &ipts->doorbell.dma_address, GFP_KERNEL); -+ -+ if (!ipts->doorbell.address) -+ goto release_resources; -+ -+ ipts->workqueue.address = -+ dma_alloc_coherent(ipts->dev, sizeof(u32), -+ &ipts->workqueue.dma_address, GFP_KERNEL); -+ -+ if (!ipts->workqueue.address) -+ goto release_resources; -+ -+ ipts->host2me.address = -+ dma_alloc_coherent(ipts->dev, feedback_buffer_size, -+ &ipts->host2me.dma_address, GFP_KERNEL); -+ -+ if (!ipts->workqueue.address) -+ goto release_resources; -+ -+ return 0; -+ -+release_resources: -+ -+ ipts_resources_free(ipts); -+ return -ENOMEM; -+} -diff --git a/drivers/misc/ipts/resources.h b/drivers/misc/ipts/resources.h -new file mode 100644 -index 000000000000..fdac0eee9156 ---- /dev/null -+++ b/drivers/misc/ipts/resources.h -@@ -0,0 +1,17 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+/* -+ * Copyright (c) 2016 Intel Corporation -+ * Copyright (c) 2020 Dorian Stoll -+ * -+ * Linux driver for Intel Precise Touch & Stylus -+ */ -+ -+#ifndef _IPTS_RESOURCES_H_ -+#define _IPTS_RESOURCES_H_ -+ -+#include "context.h" -+ -+int ipts_resources_alloc(struct ipts_context *ipts); -+void ipts_resources_free(struct ipts_context *ipts); -+ -+#endif /* _IPTS_RESOURCES_H_ */ -diff --git a/drivers/misc/ipts/uapi.c b/drivers/misc/ipts/uapi.c -new file mode 100644 -index 000000000000..598f0710ad64 ---- /dev/null -+++ b/drivers/misc/ipts/uapi.c -@@ -0,0 +1,208 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2016 Intel Corporation -+ * Copyright (c) 2020 Dorian Stoll -+ * -+ * Linux driver for Intel Precise Touch & Stylus -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "context.h" -+#include "control.h" -+#include "protocol.h" -+#include "uapi.h" -+ -+struct ipts_uapi uapi; -+ -+static ssize_t ipts_uapi_read(struct file *file, char __user *buf, size_t count, -+ loff_t *offset) -+{ -+ int buffer; -+ int maxbytes; -+ struct ipts_context *ipts = uapi.ipts; -+ -+ buffer = MINOR(file->f_path.dentry->d_inode->i_rdev); -+ -+ if (!ipts || ipts->status != IPTS_HOST_STATUS_STARTED) -+ return -ENODEV; -+ -+ maxbytes = ipts->device_info.data_size - *offset; -+ if (maxbytes <= 0 || count > maxbytes) -+ return -EINVAL; -+ -+ if (copy_to_user(buf, ipts->data[buffer].address + *offset, count)) -+ return -EFAULT; -+ -+ return count; -+} -+ -+static long ipts_uapi_ioctl_get_device_ready(struct ipts_context *ipts, -+ unsigned long arg) -+{ -+ void __user *buffer = (void __user *)arg; -+ u8 ready = 0; -+ -+ if (ipts) -+ ready = ipts->status == IPTS_HOST_STATUS_STARTED; -+ -+ if (copy_to_user(buffer, &ready, sizeof(u8))) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+static long ipts_uapi_ioctl_get_device_info(struct ipts_context *ipts, -+ unsigned long arg) -+{ -+ struct ipts_device_info info; -+ void __user *buffer = (void __user *)arg; -+ -+ if (!ipts || ipts->status != IPTS_HOST_STATUS_STARTED) -+ return -ENODEV; -+ -+ info.vendor = ipts->device_info.vendor_id; -+ info.product = ipts->device_info.device_id; -+ info.version = ipts->device_info.fw_rev; -+ info.buffer_size = ipts->device_info.data_size; -+ info.max_contacts = ipts->device_info.max_contacts; -+ -+ if (copy_to_user(buffer, &info, sizeof(struct ipts_device_info))) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+static long ipts_uapi_ioctl_get_doorbell(struct ipts_context *ipts, -+ unsigned long arg) -+{ -+ void __user *buffer = (void __user *)arg; -+ -+ if (!ipts || ipts->status != IPTS_HOST_STATUS_STARTED) -+ return -ENODEV; -+ -+ if (copy_to_user(buffer, ipts->doorbell.address, sizeof(u32))) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+static long ipts_uapi_ioctl_send_feedback(struct ipts_context *ipts, -+ struct file *file) -+{ -+ int ret; -+ u32 buffer; -+ -+ if (!ipts || ipts->status != IPTS_HOST_STATUS_STARTED) -+ return -ENODEV; -+ -+ buffer = MINOR(file->f_path.dentry->d_inode->i_rdev); -+ -+ ret = ipts_control_send_feedback(ipts, buffer); -+ if (ret) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+static long ipts_uapi_ioctl_send_reset(struct ipts_context *ipts) -+{ -+ int ret; -+ struct ipts_reset_sensor_cmd cmd; -+ -+ if (!ipts || ipts->status != IPTS_HOST_STATUS_STARTED) -+ return -ENODEV; -+ -+ memset(&cmd, 0, sizeof(struct ipts_reset_sensor_cmd)); -+ cmd.type = IPTS_RESET_TYPE_SOFT; -+ -+ ret = ipts_control_send(ipts, IPTS_CMD_RESET_SENSOR, &cmd, -+ sizeof(struct ipts_reset_sensor_cmd)); -+ -+ if (ret) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+static long ipts_uapi_ioctl(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ struct ipts_context *ipts = uapi.ipts; -+ -+ switch (cmd) { -+ case IPTS_IOCTL_GET_DEVICE_READY: -+ return ipts_uapi_ioctl_get_device_ready(ipts, arg); -+ case IPTS_IOCTL_GET_DEVICE_INFO: -+ return ipts_uapi_ioctl_get_device_info(ipts, arg); -+ case IPTS_IOCTL_GET_DOORBELL: -+ return ipts_uapi_ioctl_get_doorbell(ipts, arg); -+ case IPTS_IOCTL_SEND_FEEDBACK: -+ return ipts_uapi_ioctl_send_feedback(ipts, file); -+ case IPTS_IOCTL_SEND_RESET: -+ return ipts_uapi_ioctl_send_reset(ipts); -+ default: -+ return -ENOTTY; -+ } -+} -+ -+static const struct file_operations ipts_uapi_fops = { -+ .owner = THIS_MODULE, -+ .read = ipts_uapi_read, -+ .unlocked_ioctl = ipts_uapi_ioctl, -+#ifdef CONFIG_COMPAT -+ .compat_ioctl = ipts_uapi_ioctl, -+#endif -+}; -+ -+void ipts_uapi_link(struct ipts_context *ipts) -+{ -+ uapi.ipts = ipts; -+} -+ -+void ipts_uapi_unlink(void) -+{ -+ uapi.ipts = NULL; -+} -+ -+int ipts_uapi_init(void) -+{ -+ int i, major; -+ -+ alloc_chrdev_region(&uapi.dev, 0, IPTS_BUFFERS, "ipts"); -+ uapi.class = class_create(THIS_MODULE, "ipts"); -+ -+ major = MAJOR(uapi.dev); -+ -+ cdev_init(&uapi.cdev, &ipts_uapi_fops); -+ uapi.cdev.owner = THIS_MODULE; -+ cdev_add(&uapi.cdev, MKDEV(major, 0), IPTS_BUFFERS); -+ -+ for (i = 0; i < IPTS_BUFFERS; i++) { -+ device_create(uapi.class, NULL, MKDEV(major, i), NULL, -+ "ipts/%d", i); -+ } -+ -+ return 0; -+} -+ -+void ipts_uapi_free(void) -+{ -+ int i; -+ int major; -+ -+ major = MAJOR(uapi.dev); -+ -+ for (i = 0; i < IPTS_BUFFERS; i++) -+ device_destroy(uapi.class, MKDEV(major, i)); -+ -+ cdev_del(&uapi.cdev); -+ -+ unregister_chrdev_region(MKDEV(major, 0), MINORMASK); -+ class_destroy(uapi.class); -+} -diff --git a/drivers/misc/ipts/uapi.h b/drivers/misc/ipts/uapi.h -new file mode 100644 -index 000000000000..53fb86a88f97 ---- /dev/null -+++ b/drivers/misc/ipts/uapi.h -@@ -0,0 +1,47 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+/* -+ * Copyright (c) 2016 Intel Corporation -+ * Copyright (c) 2020 Dorian Stoll -+ * -+ * Linux driver for Intel Precise Touch & Stylus -+ */ -+ -+#ifndef _IPTS_UAPI_H_ -+#define _IPTS_UAPI_H_ -+ -+#include -+ -+#include "context.h" -+ -+struct ipts_uapi { -+ dev_t dev; -+ struct class *class; -+ struct cdev cdev; -+ -+ struct ipts_context *ipts; -+}; -+ -+struct ipts_device_info { -+ __u16 vendor; -+ __u16 product; -+ __u32 version; -+ __u32 buffer_size; -+ __u8 max_contacts; -+ -+ /* For future expansion */ -+ __u8 reserved[19]; -+}; -+ -+#define IPTS_IOCTL_GET_DEVICE_READY _IOR(0x86, 0x01, __u8) -+#define IPTS_IOCTL_GET_DEVICE_INFO _IOR(0x86, 0x02, struct ipts_device_info) -+#define IPTS_IOCTL_GET_DOORBELL _IOR(0x86, 0x03, __u32) -+#define IPTS_IOCTL_SEND_FEEDBACK _IO(0x86, 0x04) -+#define IPTS_IOCTL_SEND_RESET _IO(0x86, 0x05) -+ -+void ipts_uapi_link(struct ipts_context *ipts); -+void ipts_uapi_unlink(void); -+ -+int ipts_uapi_init(void); -+void ipts_uapi_free(void); -+ -+#endif /* _IPTS_UAPI_H_ */ --- -2.35.1 - diff --git a/patches/5.16/0005-surface-sam.patch b/patches/5.16/0005-surface-sam.patch index 46e4b05bd..e42b590d0 100644 --- a/patches/5.16/0005-surface-sam.patch +++ b/patches/5.16/0005-surface-sam.patch @@ -1,4 +1,4 @@ -From 55886a7a57015fa88846249f9d8ea43b15236f06 Mon Sep 17 00:00:00 2001 +From 4944516f841b7ad107622276a66cdefcc018d74e Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Wed, 2 Jun 2021 03:34:06 +0200 Subject: [PATCH] platform/surface: aggregator: Make client device removal more @@ -123,7 +123,7 @@ index f636c5310321..cc257097eb05 100644 -- 2.35.1 -From 6764f12ee5cb5794330e4d45f46620feaa0cd2d6 Mon Sep 17 00:00:00 2001 +From 40bb25852c66032b2e4f150dbb7bad51138a98cb Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Wed, 27 Oct 2021 02:06:38 +0200 Subject: [PATCH] platform/surface: aggregator_registry: Use generic client @@ -211,7 +211,7 @@ index e70f4c63554e..f6c639342b9d 100644 -- 2.35.1 -From 8424f19395fd48761c79bf4a19d659a48bcf9fdb Mon Sep 17 00:00:00 2001 +From 754d1c2cf93560e57abbd00bc178957732917e42 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Wed, 27 Oct 2021 02:07:33 +0200 Subject: [PATCH] platform/surface: aggregator_registry: Rename device @@ -262,7 +262,7 @@ index f6c639342b9d..ce2bd88feeaa 100644 -- 2.35.1 -From 91b8e468fa870dc55438241368f9cc9cb1ab400d Mon Sep 17 00:00:00 2001 +From b9f37f2da29ac9ec35f95b4fe3dbab6f6fd0e67f Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Tue, 8 Jun 2021 00:24:47 +0200 Subject: [PATCH] platform/surface: aggregator: Allow devices to be marked as @@ -383,7 +383,7 @@ index cc257097eb05..491aa7e9f4bc 100644 -- 2.35.1 -From 91cea39ccc50fc7e77970770b7d74f36f39977bd Mon Sep 17 00:00:00 2001 +From 2b35e342b2a48321e2ca7eb9976eefeb0ad01f91 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Tue, 8 Jun 2021 00:48:22 +0200 Subject: [PATCH] platform/surface: aggregator: Allow notifiers to avoid @@ -670,7 +670,7 @@ index 491aa7e9f4bc..16816c34da3e 100644 -- 2.35.1 -From 641ec58894e5bdd26b5d75253cf3f1888c508335 Mon Sep 17 00:00:00 2001 +From 07940e6739b837bcedc00f070913cfd0e468a52b Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Tue, 8 Jun 2021 01:20:49 +0200 Subject: [PATCH] platform/surface: aggregator_registry: Use client device @@ -719,7 +719,7 @@ index ce2bd88feeaa..9f630e890ff7 100644 -- 2.35.1 -From bb533fa18811ae83676130069f25b9db7ae15dee Mon Sep 17 00:00:00 2001 +From 4a9a391f435e91808ef66ddd80ae93d1fc7c3faa Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Thu, 28 Oct 2021 03:37:06 +0200 Subject: [PATCH] power/supply: surface_charger: Use client device wrappers for @@ -759,7 +759,7 @@ index a060c36c7766..59182d55742d 100644 -- 2.35.1 -From 8f5c8174aa684cfee91b8d406ef5f1a390629c05 Mon Sep 17 00:00:00 2001 +From c9711270b40bc6ca568a04ba9be49d1a213b8696 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Thu, 28 Oct 2021 03:38:09 +0200 Subject: [PATCH] power/supply: surface_battery: Use client device wrappers for @@ -799,7 +799,7 @@ index 5ec2e6bb2465..540707882bb0 100644 -- 2.35.1 -From de6436d421fe15f3d3fc8501b4eb2ead6dab2a2a Mon Sep 17 00:00:00 2001 +From a90b4e28e22b94ff362ba5efbcfca41ea3f5b64a Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Tue, 8 Jun 2021 01:33:02 +0200 Subject: [PATCH] HID: surface-hid: Add support for hot-removal @@ -910,7 +910,7 @@ index 5571e74abe91..d2e695e942b6 100644 -- 2.35.1 -From 64bfad8b3771f1d130931353dad2b37c06ca4dee Mon Sep 17 00:00:00 2001 +From 11166e327e1806dad6f54583b78c9b24fed14bf8 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 31 Oct 2021 12:34:08 +0100 Subject: [PATCH] platform/surface: aggregator: Add comment for KIP subsystem @@ -948,7 +948,7 @@ index c3de43edcffa..d1efac85caf1 100644 -- 2.35.1 -From 3d3b7e6e5d99f3791b38b587d75bd11bb9de6f94 Mon Sep 17 00:00:00 2001 +From 73c08c3066e6c6f4bfe08575e64f8ca5477de72d Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 10 Oct 2021 23:56:23 +0200 Subject: [PATCH] platform/surface: aggregator_registry: Add KIP device hub @@ -1262,7 +1262,7 @@ index 9f630e890ff7..4838ce6519a6 100644 -- 2.35.1 -From ac494dd4dd7f80ce38024799232c358c7f32bd60 Mon Sep 17 00:00:00 2001 +From 8cddf47c6b5f66bbe9fab52cdfc5d9ddaa922ef2 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Wed, 27 Oct 2021 22:33:03 +0200 Subject: [PATCH] platform/surface: aggregator_registry: Add support for @@ -1360,7 +1360,7 @@ index 4838ce6519a6..c0e29c0514df 100644 -- 2.35.1 -From 73b7cd09613ec6c6bfe865b904d13cb935f5c1d8 Mon Sep 17 00:00:00 2001 +From 8b16468073e469878a5af177d47ea980239cf685 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Tue, 8 Jun 2021 03:19:20 +0200 Subject: [PATCH] platform/surface: Add KIP tablet-mode switch @@ -1704,7 +1704,7 @@ index 000000000000..458470067579 -- 2.35.1 -From 11882ec40194fb1d1cf205bd9e3d5d70d3e680f9 Mon Sep 17 00:00:00 2001 +From a3a81804d853465aba844be04a4d755efe919a44 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Wed, 27 Oct 2021 22:33:03 +0200 Subject: [PATCH] platform/surface: aggregator_registry: Add support for tablet diff --git a/patches/5.16/0006-surface-sam-over-hid.patch b/patches/5.16/0006-surface-sam-over-hid.patch index 0df69d6c8..8384deaa3 100644 --- a/patches/5.16/0006-surface-sam-over-hid.patch +++ b/patches/5.16/0006-surface-sam-over-hid.patch @@ -1,4 +1,4 @@ -From 37c38cb638f59b0715c71ad0386794abd7ec3734 Mon Sep 17 00:00:00 2001 +From a586f227220ffc968942b304242ffb85a4ad738a Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 25 Jul 2020 17:19:53 +0200 Subject: [PATCH] i2c: acpi: Implement RawBytes read access @@ -110,7 +110,7 @@ index 92c1cc07ed46..3b688cea8e00 100644 -- 2.35.1 -From 8e8c20d113c57a74499e89ea8ca5c606717053af Mon Sep 17 00:00:00 2001 +From 21496a214799137c6188bf65c90869af0d90ca21 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 13 Feb 2021 16:41:18 +0100 Subject: [PATCH] platform/surface: Add driver for Surface Book 1 dGPU switch diff --git a/patches/5.16/0006-surface-sam.patch b/patches/5.16/0006-surface-sam.patch deleted file mode 100644 index e42b590d0..000000000 --- a/patches/5.16/0006-surface-sam.patch +++ /dev/null @@ -1,1756 +0,0 @@ -From 4944516f841b7ad107622276a66cdefcc018d74e Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Wed, 2 Jun 2021 03:34:06 +0200 -Subject: [PATCH] platform/surface: aggregator: Make client device removal more - generic - -Currently, there are similar functions defined in the Aggregator -Registry and the controller core. - -Make client device removal more generic and export it. We can then use -this function later on to remove client devices from device hubs as well -as the controller and avoid re-defining similar things. - -Signed-off-by: Maximilian Luz -Patchset: surface-sam ---- - drivers/platform/surface/aggregator/bus.c | 24 ++++++++-------------- - drivers/platform/surface/aggregator/bus.h | 3 --- - drivers/platform/surface/aggregator/core.c | 3 ++- - include/linux/surface_aggregator/device.h | 9 ++++++++ - 4 files changed, 19 insertions(+), 20 deletions(-) - -diff --git a/drivers/platform/surface/aggregator/bus.c b/drivers/platform/surface/aggregator/bus.c -index 0a40dd9c94ed..abbbb5b08b07 100644 ---- a/drivers/platform/surface/aggregator/bus.c -+++ b/drivers/platform/surface/aggregator/bus.c -@@ -374,27 +374,19 @@ static int ssam_remove_device(struct device *dev, void *_data) - } - - /** -- * ssam_controller_remove_clients() - Remove SSAM client devices registered as -- * direct children under the given controller. -- * @ctrl: The controller to remove all direct clients for. -+ * ssam_remove_clients() - Remove SSAM client devices registered as direct -+ * children under the given parent device. -+ * @dev: The (parent) device to remove all direct clients for. - * -- * Remove all SSAM client devices registered as direct children under the -- * given controller. Note that this only accounts for direct children of the -- * controller device. This does not take care of any client devices where the -- * parent device has been manually set before calling ssam_device_add. Refer -- * to ssam_device_add()/ssam_device_remove() for more details on those cases. -- * -- * To avoid new devices being added in parallel to this call, the main -- * controller lock (not statelock) must be held during this (and if -- * necessary, any subsequent deinitialization) call. -+ * Remove all SSAM client devices registered as direct children under the given -+ * device. Note that this only accounts for direct children of the device. -+ * Refer to ssam_device_add()/ssam_device_remove() for more details. - */ --void ssam_controller_remove_clients(struct ssam_controller *ctrl) -+void ssam_remove_clients(struct device *dev) - { -- struct device *dev; -- -- dev = ssam_controller_device(ctrl); - device_for_each_child_reverse(dev, NULL, ssam_remove_device); - } -+EXPORT_SYMBOL_GPL(ssam_remove_clients); - - /** - * ssam_bus_register() - Register and set-up the SSAM client device bus. -diff --git a/drivers/platform/surface/aggregator/bus.h b/drivers/platform/surface/aggregator/bus.h -index ed032c2cbdb2..6964ee84e79c 100644 ---- a/drivers/platform/surface/aggregator/bus.h -+++ b/drivers/platform/surface/aggregator/bus.h -@@ -12,14 +12,11 @@ - - #ifdef CONFIG_SURFACE_AGGREGATOR_BUS - --void ssam_controller_remove_clients(struct ssam_controller *ctrl); -- - int ssam_bus_register(void); - void ssam_bus_unregister(void); - - #else /* CONFIG_SURFACE_AGGREGATOR_BUS */ - --static inline void ssam_controller_remove_clients(struct ssam_controller *ctrl) {} - static inline int ssam_bus_register(void) { return 0; } - static inline void ssam_bus_unregister(void) {} - -diff --git a/drivers/platform/surface/aggregator/core.c b/drivers/platform/surface/aggregator/core.c -index c61bbeeec2df..d384d36098c2 100644 ---- a/drivers/platform/surface/aggregator/core.c -+++ b/drivers/platform/surface/aggregator/core.c -@@ -22,6 +22,7 @@ - #include - - #include -+#include - - #include "bus.h" - #include "controller.h" -@@ -735,7 +736,7 @@ static void ssam_serial_hub_remove(struct serdev_device *serdev) - ssam_controller_lock(ctrl); - - /* Remove all client devices. */ -- ssam_controller_remove_clients(ctrl); -+ ssam_remove_clients(&serdev->dev); - - /* Act as if suspending to silence events. */ - status = ssam_ctrl_notif_display_off(ctrl); -diff --git a/include/linux/surface_aggregator/device.h b/include/linux/surface_aggregator/device.h -index f636c5310321..cc257097eb05 100644 ---- a/include/linux/surface_aggregator/device.h -+++ b/include/linux/surface_aggregator/device.h -@@ -319,6 +319,15 @@ void ssam_device_driver_unregister(struct ssam_device_driver *d); - ssam_device_driver_unregister) - - -+/* -- Helpers for controller and hub devices. ------------------------------- */ -+ -+#ifdef CONFIG_SURFACE_AGGREGATOR_BUS -+void ssam_remove_clients(struct device *dev); -+#else /* CONFIG_SURFACE_AGGREGATOR_BUS */ -+static inline void ssam_remove_clients(struct device *dev) {} -+#endif /* CONFIG_SURFACE_AGGREGATOR_BUS */ -+ -+ - /* -- Helpers for client-device requests. ----------------------------------- */ - - /** --- -2.35.1 - -From 40bb25852c66032b2e4f150dbb7bad51138a98cb Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Wed, 27 Oct 2021 02:06:38 +0200 -Subject: [PATCH] platform/surface: aggregator_registry: Use generic client - removal function - -Use generic client removal function introduced in the previous commit -instead of defining our own one. - -Signed-off-by: Maximilian Luz -Patchset: surface-sam ---- - .../surface/surface_aggregator_registry.c | 24 ++++--------------- - 1 file changed, 5 insertions(+), 19 deletions(-) - -diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c -index e70f4c63554e..f6c639342b9d 100644 ---- a/drivers/platform/surface/surface_aggregator_registry.c -+++ b/drivers/platform/surface/surface_aggregator_registry.c -@@ -258,20 +258,6 @@ static int ssam_uid_from_string(const char *str, struct ssam_device_uid *uid) - return 0; - } - --static int ssam_hub_remove_devices_fn(struct device *dev, void *data) --{ -- if (!is_ssam_device(dev)) -- return 0; -- -- ssam_device_remove(to_ssam_device(dev)); -- return 0; --} -- --static void ssam_hub_remove_devices(struct device *parent) --{ -- device_for_each_child_reverse(parent, NULL, ssam_hub_remove_devices_fn); --} -- - static int ssam_hub_add_device(struct device *parent, struct ssam_controller *ctrl, - struct fwnode_handle *node) - { -@@ -317,7 +303,7 @@ static int ssam_hub_add_devices(struct device *parent, struct ssam_controller *c - - return 0; - err: -- ssam_hub_remove_devices(parent); -+ ssam_remove_clients(parent); - return status; - } - -@@ -414,7 +400,7 @@ static void ssam_base_hub_update_workfn(struct work_struct *work) - if (hub->state == SSAM_BASE_HUB_CONNECTED) - status = ssam_hub_add_devices(&hub->sdev->dev, hub->sdev->ctrl, node); - else -- ssam_hub_remove_devices(&hub->sdev->dev); -+ ssam_remove_clients(&hub->sdev->dev); - - if (status) - dev_err(&hub->sdev->dev, "failed to update base-hub devices: %d\n", status); -@@ -496,7 +482,7 @@ static int ssam_base_hub_probe(struct ssam_device *sdev) - err: - ssam_notifier_unregister(sdev->ctrl, &hub->notif); - cancel_delayed_work_sync(&hub->update_work); -- ssam_hub_remove_devices(&sdev->dev); -+ ssam_remove_clients(&sdev->dev); - return status; - } - -@@ -508,7 +494,7 @@ static void ssam_base_hub_remove(struct ssam_device *sdev) - - ssam_notifier_unregister(sdev->ctrl, &hub->notif); - cancel_delayed_work_sync(&hub->update_work); -- ssam_hub_remove_devices(&sdev->dev); -+ ssam_remove_clients(&sdev->dev); - } - - static const struct ssam_device_id ssam_base_hub_match[] = { -@@ -625,7 +611,7 @@ static int ssam_platform_hub_remove(struct platform_device *pdev) - { - const struct software_node **nodes = platform_get_drvdata(pdev); - -- ssam_hub_remove_devices(&pdev->dev); -+ ssam_remove_clients(&pdev->dev); - set_secondary_fwnode(&pdev->dev, NULL); - software_node_unregister_node_group(nodes); - return 0; --- -2.35.1 - -From 754d1c2cf93560e57abbd00bc178957732917e42 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Wed, 27 Oct 2021 02:07:33 +0200 -Subject: [PATCH] platform/surface: aggregator_registry: Rename device - registration function - -Rename the device registration function to better align names with the -newly introduced device removal function. - -Signed-off-by: Maximilian Luz -Patchset: surface-sam ---- - drivers/platform/surface/surface_aggregator_registry.c | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c -index f6c639342b9d..ce2bd88feeaa 100644 ---- a/drivers/platform/surface/surface_aggregator_registry.c -+++ b/drivers/platform/surface/surface_aggregator_registry.c -@@ -283,8 +283,8 @@ static int ssam_hub_add_device(struct device *parent, struct ssam_controller *ct - return status; - } - --static int ssam_hub_add_devices(struct device *parent, struct ssam_controller *ctrl, -- struct fwnode_handle *node) -+static int ssam_hub_register_clients(struct device *parent, struct ssam_controller *ctrl, -+ struct fwnode_handle *node) - { - struct fwnode_handle *child; - int status; -@@ -398,7 +398,7 @@ static void ssam_base_hub_update_workfn(struct work_struct *work) - hub->state = state; - - if (hub->state == SSAM_BASE_HUB_CONNECTED) -- status = ssam_hub_add_devices(&hub->sdev->dev, hub->sdev->ctrl, node); -+ status = ssam_hub_register_clients(&hub->sdev->dev, hub->sdev->ctrl, node); - else - ssam_remove_clients(&hub->sdev->dev); - -@@ -597,7 +597,7 @@ static int ssam_platform_hub_probe(struct platform_device *pdev) - - set_secondary_fwnode(&pdev->dev, root); - -- status = ssam_hub_add_devices(&pdev->dev, ctrl, root); -+ status = ssam_hub_register_clients(&pdev->dev, ctrl, root); - if (status) { - set_secondary_fwnode(&pdev->dev, NULL); - software_node_unregister_node_group(nodes); --- -2.35.1 - -From b9f37f2da29ac9ec35f95b4fe3dbab6f6fd0e67f Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Tue, 8 Jun 2021 00:24:47 +0200 -Subject: [PATCH] platform/surface: aggregator: Allow devices to be marked as - hot-removed - -Some SSAM devices, notably the keyboard cover (keyboard and touchpad) on -the Surface Pro 8, can be hot-removed. When this occurs, communication -with the device may fail and time out. This timeout can unnecessarily -block and slow down device removal and even cause issues when the -devices are detached and re-attached quickly. Thus, communication should -generally be avoided once hot-removal is detected. - -While we already remove a device as soon as we detect its (hot-)removal, -the corresponding device driver may still attempt to communicate with -the device during teardown. This is especially critical as communication -failure may also extend to disabling of events, which is typically done -at that stage. - -Add a flag to allow marking devices as hot-removed. This can then be -used during client driver teardown to check if any communication -attempts should be avoided. - -Signed-off-by: Maximilian Luz -Patchset: surface-sam ---- - drivers/platform/surface/aggregator/bus.c | 3 ++ - include/linux/surface_aggregator/device.h | 48 +++++++++++++++++++++-- - 2 files changed, 48 insertions(+), 3 deletions(-) - -diff --git a/drivers/platform/surface/aggregator/bus.c b/drivers/platform/surface/aggregator/bus.c -index abbbb5b08b07..2b003dcbfc4b 100644 ---- a/drivers/platform/surface/aggregator/bus.c -+++ b/drivers/platform/surface/aggregator/bus.c -@@ -388,6 +388,9 @@ void ssam_remove_clients(struct device *dev) - } - EXPORT_SYMBOL_GPL(ssam_remove_clients); - -+ -+/* -- Bus registration. ----------------------------------------------------- */ -+ - /** - * ssam_bus_register() - Register and set-up the SSAM client device bus. - */ -diff --git a/include/linux/surface_aggregator/device.h b/include/linux/surface_aggregator/device.h -index cc257097eb05..491aa7e9f4bc 100644 ---- a/include/linux/surface_aggregator/device.h -+++ b/include/linux/surface_aggregator/device.h -@@ -148,17 +148,30 @@ struct ssam_device_uid { - #define SSAM_SDEV(cat, tid, iid, fun) \ - SSAM_DEVICE(SSAM_DOMAIN_SERIALHUB, SSAM_SSH_TC_##cat, tid, iid, fun) - -+/* -+ * enum ssam_device_flags - Flags for SSAM client devices. -+ * @SSAM_DEVICE_HOT_REMOVED_BIT: -+ * The device has been hot-removed. Further communication with it may time -+ * out and should be avoided. -+ */ -+enum ssam_device_flags { -+ SSAM_DEVICE_HOT_REMOVED_BIT = 0, -+}; -+ - /** - * struct ssam_device - SSAM client device. -- * @dev: Driver model representation of the device. -- * @ctrl: SSAM controller managing this device. -- * @uid: UID identifying the device. -+ * @dev: Driver model representation of the device. -+ * @ctrl: SSAM controller managing this device. -+ * @uid: UID identifying the device. -+ * @flags: Device state flags, see &enum ssam_device_flags. - */ - struct ssam_device { - struct device dev; - struct ssam_controller *ctrl; - - struct ssam_device_uid uid; -+ -+ unsigned long flags; - }; - - /** -@@ -240,6 +253,35 @@ struct ssam_device *ssam_device_alloc(struct ssam_controller *ctrl, - int ssam_device_add(struct ssam_device *sdev); - void ssam_device_remove(struct ssam_device *sdev); - -+/** -+ * ssam_device_mark_hot_removed() - Mark the given device as hot-removed. -+ * @sdev: The device to mark as hot-removed. -+ * -+ * Mark the device as having been hot-removed. This signals drivers using the -+ * device that communication with the device should be avoided and may lead to -+ * timeouts. -+ */ -+static inline void ssam_device_mark_hot_removed(struct ssam_device *sdev) -+{ -+ dev_dbg(&sdev->dev, "marking device as hot-removed\n"); -+ set_bit(SSAM_DEVICE_HOT_REMOVED_BIT, &sdev->flags); -+} -+ -+/** -+ * ssam_device_is_hot_removed() - Check if the given device has been -+ * hot-removed. -+ * @sdev: The device to check. -+ * -+ * Checks if the given device has been marked as hot-removed. See -+ * ssam_device_mark_hot_removed() for more details. -+ * -+ * Return: Returns ``true`` if the device has been marked as hot-removed. -+ */ -+static inline bool ssam_device_is_hot_removed(struct ssam_device *sdev) -+{ -+ return test_bit(SSAM_DEVICE_HOT_REMOVED_BIT, &sdev->flags); -+} -+ - /** - * ssam_device_get() - Increment reference count of SSAM client device. - * @sdev: The device to increment the reference count of. --- -2.35.1 - -From 2b35e342b2a48321e2ca7eb9976eefeb0ad01f91 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Tue, 8 Jun 2021 00:48:22 +0200 -Subject: [PATCH] platform/surface: aggregator: Allow notifiers to avoid - communication on unregistering - -When SSAM client devices have been (physically) hot-removed, -communication attempts with those devices may fail and time out. This -can even extend to event notifiers, due to which timeouts may occur -during device removal, slowing down that process. - -Add a flag to the notifier unregister function that allows skipping -communication with the EC to prevent this. Furthermore, add wrappers for -registering and unregistering notifiers belonging to SSAM client devices -that automatically check if the device has been marked as hot-removed -and communication should be avoided. - -Note that non-SSAM client devices can generally not be hot-removed, so -also add a convenience wrapper for those, defaulting to allow -communication. - -Signed-off-by: Maximilian Luz -Patchset: surface-sam ---- - .../driver-api/surface_aggregator/client.rst | 6 +- - .../platform/surface/aggregator/controller.c | 53 ++++++++++------ - include/linux/surface_aggregator/controller.h | 24 +++++++- - include/linux/surface_aggregator/device.h | 60 +++++++++++++++++++ - 4 files changed, 122 insertions(+), 21 deletions(-) - -diff --git a/Documentation/driver-api/surface_aggregator/client.rst b/Documentation/driver-api/surface_aggregator/client.rst -index e519d374c378..27f95abdbe99 100644 ---- a/Documentation/driver-api/surface_aggregator/client.rst -+++ b/Documentation/driver-api/surface_aggregator/client.rst -@@ -17,6 +17,8 @@ - .. |SSAM_DEVICE| replace:: :c:func:`SSAM_DEVICE` - .. |ssam_notifier_register| replace:: :c:func:`ssam_notifier_register` - .. |ssam_notifier_unregister| replace:: :c:func:`ssam_notifier_unregister` -+.. |ssam_device_notifier_register| replace:: :c:func:`ssam_device_notifier_register` -+.. |ssam_device_notifier_unregister| replace:: :c:func:`ssam_device_notifier_unregister` - .. |ssam_request_sync| replace:: :c:func:`ssam_request_sync` - .. |ssam_event_mask| replace:: :c:type:`enum ssam_event_mask ` - -@@ -312,7 +314,9 @@ Handling Events - To receive events from the SAM EC, an event notifier must be registered for - the desired event via |ssam_notifier_register|. The notifier must be - unregistered via |ssam_notifier_unregister| once it is not required any --more. -+more. For |ssam_device| type clients, the |ssam_device_notifier_register| and -+|ssam_device_notifier_unregister| wrappers should be preferred as they properly -+handle hot-removal of client devices. - - Event notifiers are registered by providing (at minimum) a callback to call - in case an event has been received, the registry specifying how the event -diff --git a/drivers/platform/surface/aggregator/controller.c b/drivers/platform/surface/aggregator/controller.c -index b8c377b3f932..6de834b52b63 100644 ---- a/drivers/platform/surface/aggregator/controller.c -+++ b/drivers/platform/surface/aggregator/controller.c -@@ -2199,16 +2199,26 @@ static int ssam_nf_refcount_enable(struct ssam_controller *ctrl, - } - - /** -- * ssam_nf_refcount_disable_free() - Disable event for reference count entry if it is -- * no longer in use and free the corresponding entry. -+ * ssam_nf_refcount_disable_free() - Disable event for reference count entry if -+ * it is no longer in use and free the corresponding entry. - * @ctrl: The controller to disable the event on. - * @entry: The reference count entry for the event to be disabled. - * @flags: The flags used for enabling the event on the EC. -+ * @ec: Flag specifying if the event should actually be disabled on the EC. - * -- * If the reference count equals zero, i.e. the event is no longer requested by -- * any client, the event will be disabled and the corresponding reference count -- * entry freed. The reference count entry must not be used any more after a -- * call to this function. -+ * If ``ec`` equals ``true`` and the reference count equals zero (i.e. the -+ * event is no longer requested by any client), the specified event will be -+ * disabled on the EC via the corresponding request. -+ * -+ * If ``ec`` equals ``false``, no request will be sent to the EC and the event -+ * can be considered in a detached state (i.e. no longer used but still -+ * enabled). Disabling an event via this method may be required for -+ * hot-removable devices, where event disable requests may time out after the -+ * device has been physically removed. -+ * -+ * In both cases, if the reference count equals zero, the corresponding -+ * reference count entry will be freed. The reference count entry must not be -+ * used any more after a call to this function. - * - * Also checks if the flags used for disabling the event match the flags used - * for enabling the event and warns if they do not (regardless of reference -@@ -2223,7 +2233,7 @@ static int ssam_nf_refcount_enable(struct ssam_controller *ctrl, - * returns the status of the event-enable EC command. - */ - static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl, -- struct ssam_nf_refcount_entry *entry, u8 flags) -+ struct ssam_nf_refcount_entry *entry, u8 flags, bool ec) - { - const struct ssam_event_registry reg = entry->key.reg; - const struct ssam_event_id id = entry->key.id; -@@ -2232,8 +2242,9 @@ static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl, - - lockdep_assert_held(&nf->lock); - -- ssam_dbg(ctrl, "disabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n", -- reg.target_category, id.target_category, id.instance, entry->refcount); -+ ssam_dbg(ctrl, "%s event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n", -+ ec ? "disabling" : "detaching", reg.target_category, id.target_category, -+ id.instance, entry->refcount); - - if (entry->flags != flags) { - ssam_warn(ctrl, -@@ -2242,7 +2253,7 @@ static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl, - id.instance); - } - -- if (entry->refcount == 0) { -+ if (ec && entry->refcount == 0) { - status = ssam_ssh_event_disable(ctrl, reg, id, flags); - kfree(entry); - } -@@ -2322,20 +2333,26 @@ int ssam_notifier_register(struct ssam_controller *ctrl, struct ssam_event_notif - EXPORT_SYMBOL_GPL(ssam_notifier_register); - - /** -- * ssam_notifier_unregister() - Unregister an event notifier. -- * @ctrl: The controller the notifier has been registered on. -- * @n: The event notifier to unregister. -+ * __ssam_notifier_unregister() - Unregister an event notifier. -+ * @ctrl: The controller the notifier has been registered on. -+ * @n: The event notifier to unregister. -+ * @disable: Whether to disable the corresponding event on the EC. - * - * Unregister an event notifier. Decrement the usage counter of the associated - * SAM event if the notifier is not marked as an observer. If the usage counter -- * reaches zero, the event will be disabled. -+ * reaches zero and ``disable`` equals ``true``, the event will be disabled. -+ * -+ * Useful for hot-removable devices, where communication may fail once the -+ * device has been physically removed. In that case, specifying ``disable`` as -+ * ``false`` avoids communication with the EC. - * - * Return: Returns zero on success, %-ENOENT if the given notifier block has - * not been registered on the controller. If the given notifier block was the - * last one associated with its specific event, returns the status of the - * event-disable EC-command. - */ --int ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_notifier *n) -+int __ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_notifier *n, -+ bool disable) - { - u16 rqid = ssh_tc_to_rqid(n->event.id.target_category); - struct ssam_nf_refcount_entry *entry; -@@ -2373,7 +2390,7 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_not - goto remove; - } - -- status = ssam_nf_refcount_disable_free(ctrl, entry, n->event.flags); -+ status = ssam_nf_refcount_disable_free(ctrl, entry, n->event.flags, disable); - } - - remove: -@@ -2383,7 +2400,7 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_not - - return status; - } --EXPORT_SYMBOL_GPL(ssam_notifier_unregister); -+EXPORT_SYMBOL_GPL(__ssam_notifier_unregister); - - /** - * ssam_controller_event_enable() - Enable the specified event. -@@ -2477,7 +2494,7 @@ int ssam_controller_event_disable(struct ssam_controller *ctrl, - return -ENOENT; - } - -- status = ssam_nf_refcount_disable_free(ctrl, entry, flags); -+ status = ssam_nf_refcount_disable_free(ctrl, entry, flags, true); - - mutex_unlock(&nf->lock); - return status; -diff --git a/include/linux/surface_aggregator/controller.h b/include/linux/surface_aggregator/controller.h -index 74bfdffaf7b0..50a2b4926c06 100644 ---- a/include/linux/surface_aggregator/controller.h -+++ b/include/linux/surface_aggregator/controller.h -@@ -835,8 +835,28 @@ struct ssam_event_notifier { - int ssam_notifier_register(struct ssam_controller *ctrl, - struct ssam_event_notifier *n); - --int ssam_notifier_unregister(struct ssam_controller *ctrl, -- struct ssam_event_notifier *n); -+int __ssam_notifier_unregister(struct ssam_controller *ctrl, -+ struct ssam_event_notifier *n, bool disable); -+ -+/** -+ * ssam_notifier_unregister() - Unregister an event notifier. -+ * @ctrl: The controller the notifier has been registered on. -+ * @n: The event notifier to unregister. -+ * -+ * Unregister an event notifier. Decrement the usage counter of the associated -+ * SAM event if the notifier is not marked as an observer. If the usage counter -+ * reaches zero, the event will be disabled. -+ * -+ * Return: Returns zero on success, %-ENOENT if the given notifier block has -+ * not been registered on the controller. If the given notifier block was the -+ * last one associated with its specific event, returns the status of the -+ * event-disable EC-command. -+ */ -+static inline int ssam_notifier_unregister(struct ssam_controller *ctrl, -+ struct ssam_event_notifier *n) -+{ -+ return __ssam_notifier_unregister(ctrl, n, true); -+} - - int ssam_controller_event_enable(struct ssam_controller *ctrl, - struct ssam_event_registry reg, -diff --git a/include/linux/surface_aggregator/device.h b/include/linux/surface_aggregator/device.h -index 491aa7e9f4bc..16816c34da3e 100644 ---- a/include/linux/surface_aggregator/device.h -+++ b/include/linux/surface_aggregator/device.h -@@ -472,4 +472,64 @@ static inline void ssam_remove_clients(struct device *dev) {} - sdev->uid.instance, ret); \ - } - -+ -+/* -- Helpers for client-device notifiers. ---------------------------------- */ -+ -+/** -+ * ssam_device_notifier_register() - Register an event notifier for the -+ * specified client device. -+ * @sdev: The device the notifier should be registered on. -+ * @n: The event notifier to register. -+ * -+ * Register an event notifier. Increment the usage counter of the associated -+ * SAM event if the notifier is not marked as an observer. If the event is not -+ * marked as an observer and is currently not enabled, it will be enabled -+ * during this call. If the notifier is marked as an observer, no attempt will -+ * be made at enabling any event and no reference count will be modified. -+ * -+ * Notifiers marked as observers do not need to be associated with one specific -+ * event, i.e. as long as no event matching is performed, only the event target -+ * category needs to be set. -+ * -+ * Return: Returns zero on success, %-ENOSPC if there have already been -+ * %INT_MAX notifiers for the event ID/type associated with the notifier block -+ * registered, %-ENOMEM if the corresponding event entry could not be -+ * allocated, %-ENODEV if the device is marked as hot-removed. If this is the -+ * first time that a notifier block is registered for the specific associated -+ * event, returns the status of the event-enable EC-command. -+ */ -+static inline int ssam_device_notifier_register(struct ssam_device *sdev, -+ struct ssam_event_notifier *n) -+{ -+ if (ssam_device_is_hot_removed(sdev)) -+ return -ENODEV; -+ -+ return ssam_notifier_register(sdev->ctrl, n); -+} -+ -+/** -+ * ssam_device_notifier_unregister() - Unregister an event notifier for the -+ * specified client device. -+ * @sdev: The device the notifier has been registered on. -+ * @n: The event notifier to unregister. -+ * -+ * Unregister an event notifier. Decrement the usage counter of the associated -+ * SAM event if the notifier is not marked as an observer. If the usage counter -+ * reaches zero, the event will be disabled. -+ * -+ * In case the device has been marked as hot-removed, the event will not be -+ * disabled on the EC, as in those cases any attempt at doing so may time out. -+ * -+ * Return: Returns zero on success, %-ENOENT if the given notifier block has -+ * not been registered on the controller. If the given notifier block was the -+ * last one associated with its specific event, returns the status of the -+ * event-disable EC-command. -+ */ -+static inline int ssam_device_notifier_unregister(struct ssam_device *sdev, -+ struct ssam_event_notifier *n) -+{ -+ return __ssam_notifier_unregister(sdev->ctrl, n, -+ !ssam_device_is_hot_removed(sdev)); -+} -+ - #endif /* _LINUX_SURFACE_AGGREGATOR_DEVICE_H */ --- -2.35.1 - -From 07940e6739b837bcedc00f070913cfd0e468a52b Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Tue, 8 Jun 2021 01:20:49 +0200 -Subject: [PATCH] platform/surface: aggregator_registry: Use client device - wrappers for notifier registration - -Use newly introduced client device wrapper functions for notifier -registration and unregistration. - -Signed-off-by: Maximilian Luz -Patchset: surface-sam ---- - drivers/platform/surface/surface_aggregator_registry.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c -index ce2bd88feeaa..9f630e890ff7 100644 ---- a/drivers/platform/surface/surface_aggregator_registry.c -+++ b/drivers/platform/surface/surface_aggregator_registry.c -@@ -468,7 +468,7 @@ static int ssam_base_hub_probe(struct ssam_device *sdev) - - ssam_device_set_drvdata(sdev, hub); - -- status = ssam_notifier_register(sdev->ctrl, &hub->notif); -+ status = ssam_device_notifier_register(sdev, &hub->notif); - if (status) - return status; - -@@ -480,7 +480,7 @@ static int ssam_base_hub_probe(struct ssam_device *sdev) - return 0; - - err: -- ssam_notifier_unregister(sdev->ctrl, &hub->notif); -+ ssam_device_notifier_unregister(sdev, &hub->notif); - cancel_delayed_work_sync(&hub->update_work); - ssam_remove_clients(&sdev->dev); - return status; -@@ -492,7 +492,7 @@ static void ssam_base_hub_remove(struct ssam_device *sdev) - - sysfs_remove_group(&sdev->dev.kobj, &ssam_base_hub_group); - -- ssam_notifier_unregister(sdev->ctrl, &hub->notif); -+ ssam_device_notifier_unregister(sdev, &hub->notif); - cancel_delayed_work_sync(&hub->update_work); - ssam_remove_clients(&sdev->dev); - } --- -2.35.1 - -From 4a9a391f435e91808ef66ddd80ae93d1fc7c3faa Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Thu, 28 Oct 2021 03:37:06 +0200 -Subject: [PATCH] power/supply: surface_charger: Use client device wrappers for - notifier registration - -Use newly introduced client device wrapper functions for notifier -registration and unregistration. - -Signed-off-by: Maximilian Luz -Patchset: surface-sam ---- - drivers/power/supply/surface_charger.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/drivers/power/supply/surface_charger.c b/drivers/power/supply/surface_charger.c -index a060c36c7766..59182d55742d 100644 ---- a/drivers/power/supply/surface_charger.c -+++ b/drivers/power/supply/surface_charger.c -@@ -216,7 +216,7 @@ static int spwr_ac_register(struct spwr_ac_device *ac) - if (IS_ERR(ac->psy)) - return PTR_ERR(ac->psy); - -- return ssam_notifier_register(ac->sdev->ctrl, &ac->notif); -+ return ssam_device_notifier_register(ac->sdev, &ac->notif); - } - - -@@ -251,7 +251,7 @@ static void surface_ac_remove(struct ssam_device *sdev) - { - struct spwr_ac_device *ac = ssam_device_get_drvdata(sdev); - -- ssam_notifier_unregister(sdev->ctrl, &ac->notif); -+ ssam_device_notifier_unregister(sdev, &ac->notif); - } - - static const struct spwr_psy_properties spwr_psy_props_adp1 = { --- -2.35.1 - -From c9711270b40bc6ca568a04ba9be49d1a213b8696 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Thu, 28 Oct 2021 03:38:09 +0200 -Subject: [PATCH] power/supply: surface_battery: Use client device wrappers for - notifier registration - -Use newly introduced client device wrapper functions for notifier -registration and unregistration. - -Signed-off-by: Maximilian Luz -Patchset: surface-sam ---- - drivers/power/supply/surface_battery.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/drivers/power/supply/surface_battery.c b/drivers/power/supply/surface_battery.c -index 5ec2e6bb2465..540707882bb0 100644 ---- a/drivers/power/supply/surface_battery.c -+++ b/drivers/power/supply/surface_battery.c -@@ -802,7 +802,7 @@ static int spwr_battery_register(struct spwr_battery_device *bat) - if (IS_ERR(bat->psy)) - return PTR_ERR(bat->psy); - -- return ssam_notifier_register(bat->sdev->ctrl, &bat->notif); -+ return ssam_device_notifier_register(bat->sdev, &bat->notif); - } - - -@@ -837,7 +837,7 @@ static void surface_battery_remove(struct ssam_device *sdev) - { - struct spwr_battery_device *bat = ssam_device_get_drvdata(sdev); - -- ssam_notifier_unregister(sdev->ctrl, &bat->notif); -+ ssam_device_notifier_unregister(sdev, &bat->notif); - cancel_delayed_work_sync(&bat->update_work); - } - --- -2.35.1 - -From a90b4e28e22b94ff362ba5efbcfca41ea3f5b64a Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Tue, 8 Jun 2021 01:33:02 +0200 -Subject: [PATCH] HID: surface-hid: Add support for hot-removal - -Add support for hot-removal of SSAM HID client devices. - -Once a device has been hot-removed, further communication with it should -be avoided as it may fail and time out. While the device will be removed -as soon as we detect hot-removal, communication may still occur during -teardown, especially when unregistering notifiers. - -While hot-removal is a surprise event that can happen any time, try to -avoid communication as much as possible once it has been detected to -prevent timeouts that can slow down device removal and cause issues, -e.g. when quickly re-attaching the device. - -Signed-off-by: Maximilian Luz -Patchset: surface-sam ---- - drivers/hid/surface-hid/surface_hid_core.c | 38 +++++++++++++++++++++- - 1 file changed, 37 insertions(+), 1 deletion(-) - -diff --git a/drivers/hid/surface-hid/surface_hid_core.c b/drivers/hid/surface-hid/surface_hid_core.c -index 5571e74abe91..d2e695e942b6 100644 ---- a/drivers/hid/surface-hid/surface_hid_core.c -+++ b/drivers/hid/surface-hid/surface_hid_core.c -@@ -19,12 +19,30 @@ - #include "surface_hid_core.h" - - -+/* -- Utility functions. ---------------------------------------------------- */ -+ -+static bool surface_hid_is_hot_removed(struct surface_hid_device *shid) -+{ -+ /* -+ * Non-ssam client devices, i.e. platform client devices, cannot be -+ * hot-removed. -+ */ -+ if (!is_ssam_device(shid->dev)) -+ return false; -+ -+ return ssam_device_is_hot_removed(to_ssam_device(shid->dev)); -+} -+ -+ - /* -- Device descriptor access. --------------------------------------------- */ - - static int surface_hid_load_hid_descriptor(struct surface_hid_device *shid) - { - int status; - -+ if (surface_hid_is_hot_removed(shid)) -+ return -ENODEV; -+ - status = shid->ops.get_descriptor(shid, SURFACE_HID_DESC_HID, - (u8 *)&shid->hid_desc, sizeof(shid->hid_desc)); - if (status) -@@ -61,6 +79,9 @@ static int surface_hid_load_device_attributes(struct surface_hid_device *shid) - { - int status; - -+ if (surface_hid_is_hot_removed(shid)) -+ return -ENODEV; -+ - status = shid->ops.get_descriptor(shid, SURFACE_HID_DESC_ATTRS, - (u8 *)&shid->attrs, sizeof(shid->attrs)); - if (status) -@@ -88,9 +109,18 @@ static int surface_hid_start(struct hid_device *hid) - static void surface_hid_stop(struct hid_device *hid) - { - struct surface_hid_device *shid = hid->driver_data; -+ bool hot_removed; -+ -+ /* -+ * Communication may fail for devices that have been hot-removed. This -+ * also includes unregistration of HID events, so we need to check this -+ * here. Only if the device has not been marked as hot-removed, we can -+ * safely disable events. -+ */ -+ hot_removed = surface_hid_is_hot_removed(shid); - - /* Note: This call will log errors for us, so ignore them here. */ -- ssam_notifier_unregister(shid->ctrl, &shid->notif); -+ __ssam_notifier_unregister(shid->ctrl, &shid->notif, !hot_removed); - } - - static int surface_hid_open(struct hid_device *hid) -@@ -109,6 +139,9 @@ static int surface_hid_parse(struct hid_device *hid) - u8 *buf; - int status; - -+ if (surface_hid_is_hot_removed(shid)) -+ return -ENODEV; -+ - buf = kzalloc(len, GFP_KERNEL); - if (!buf) - return -ENOMEM; -@@ -126,6 +159,9 @@ static int surface_hid_raw_request(struct hid_device *hid, unsigned char reportn - { - struct surface_hid_device *shid = hid->driver_data; - -+ if (surface_hid_is_hot_removed(shid)) -+ return -ENODEV; -+ - if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT) - return shid->ops.output_report(shid, reportnum, buf, len); - --- -2.35.1 - -From 11166e327e1806dad6f54583b78c9b24fed14bf8 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Sun, 31 Oct 2021 12:34:08 +0100 -Subject: [PATCH] platform/surface: aggregator: Add comment for KIP subsystem - category - -The KIP subsystem (full name unknown, abbreviation has been obtained -through reverse engineering) handles detachable peripherals such as the -keyboard cover on the Surface Pro X and Surface Pro 8. - -It is currently not entirely clear what this subsystem entails, but at -the very least it provides event notifications for when the keyboard -cover on the Surface Pro X and Surface Pro 8 have been detached or -re-attached, as well as the state that the keyboard cover is currently -in (e.g. folded-back, folded laptop-like, closed, etc.). - -Signed-off-by: Maximilian Luz -Patchset: surface-sam ---- - include/linux/surface_aggregator/serial_hub.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/include/linux/surface_aggregator/serial_hub.h b/include/linux/surface_aggregator/serial_hub.h -index c3de43edcffa..d1efac85caf1 100644 ---- a/include/linux/surface_aggregator/serial_hub.h -+++ b/include/linux/surface_aggregator/serial_hub.h -@@ -306,7 +306,7 @@ enum ssam_ssh_tc { - SSAM_SSH_TC_LPC = 0x0b, - SSAM_SSH_TC_TCL = 0x0c, - SSAM_SSH_TC_SFL = 0x0d, -- SSAM_SSH_TC_KIP = 0x0e, -+ SSAM_SSH_TC_KIP = 0x0e, /* Manages detachable peripherals (Pro X/8 keyboard cover) */ - SSAM_SSH_TC_EXT = 0x0f, - SSAM_SSH_TC_BLD = 0x10, - SSAM_SSH_TC_BAS = 0x11, /* Detachment system (Surface Book 2/3). */ --- -2.35.1 - -From 73c08c3066e6c6f4bfe08575e64f8ca5477de72d Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Sun, 10 Oct 2021 23:56:23 +0200 -Subject: [PATCH] platform/surface: aggregator_registry: Add KIP device hub - -Add a Surface System Aggregator Module (SSAM) client device hub for -hot-removable devices managed via the KIP subsystem. - -The KIP subsystem (full name unknown, abbreviation has been obtained -through reverse engineering) is a subsystem that manages hot-removable -SSAM client devices. Specifically, it manages HID input devices -contained in the detachable keyboard cover of the Surface Pro 8 and -Surface Pro X. - -To properly handle detachable devices, we need to remove their kernel -representation when the physical device has been detached and (re-)add -and (re-)initialize said representation when the physical device has -been (re-)attached. Note that we need to hot-remove those devices, as -communication (especially during event notifier unregistration) may time -out when the physical device is no longer present, which would lead to -an unnecessary delay. This delay might become problematic when devices -are detached and re-attached quickly. - -The KIP subsystem handles a single group of devices (e.g. all devices -contained in the keyboard cover) and cannot handle devices individually. -Thus we model it as a client device hub, which removes all devices -contained under it once removal of the hub (e.g. keyboard cover) has -been detected and (re-)adds all devices once the physical hub device has -been (re-)attached. - -Signed-off-by: Maximilian Luz -Patchset: surface-sam ---- - .../surface/surface_aggregator_registry.c | 247 +++++++++++++++++- - 1 file changed, 245 insertions(+), 2 deletions(-) - -diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c -index 9f630e890ff7..4838ce6519a6 100644 ---- a/drivers/platform/surface/surface_aggregator_registry.c -+++ b/drivers/platform/surface/surface_aggregator_registry.c -@@ -514,6 +514,237 @@ static struct ssam_device_driver ssam_base_hub_driver = { - }; - - -+/* -- SSAM KIP-subsystem hub driver. ---------------------------------------- */ -+ -+/* -+ * Some devices may need a bit of time to be fully usable after being -+ * (re-)connected. This delay has been determined via experimentation. -+ */ -+#define SSAM_KIP_UPDATE_CONNECT_DELAY msecs_to_jiffies(250) -+ -+#define SSAM_EVENT_KIP_CID_CONNECTION 0x2c -+ -+enum ssam_kip_hub_state { -+ SSAM_KIP_HUB_UNINITIALIZED, -+ SSAM_KIP_HUB_CONNECTED, -+ SSAM_KIP_HUB_DISCONNECTED, -+}; -+ -+struct ssam_kip_hub { -+ struct ssam_device *sdev; -+ -+ enum ssam_kip_hub_state state; -+ struct delayed_work update_work; -+ -+ struct ssam_event_notifier notif; -+}; -+ -+SSAM_DEFINE_SYNC_REQUEST_R(__ssam_kip_get_connection_state, u8, { -+ .target_category = SSAM_SSH_TC_KIP, -+ .target_id = 0x01, -+ .command_id = 0x2c, -+ .instance_id = 0x00, -+}); -+ -+static int ssam_kip_get_connection_state(struct ssam_kip_hub *hub, enum ssam_kip_hub_state *state) -+{ -+ int status; -+ u8 connected; -+ -+ status = ssam_retry(__ssam_kip_get_connection_state, hub->sdev->ctrl, &connected); -+ if (status < 0) { -+ dev_err(&hub->sdev->dev, "failed to query KIP connection state: %d\n", status); -+ return status; -+ } -+ -+ *state = connected ? SSAM_KIP_HUB_CONNECTED : SSAM_KIP_HUB_DISCONNECTED; -+ return 0; -+} -+ -+static ssize_t ssam_kip_hub_state_show(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct ssam_kip_hub *hub = dev_get_drvdata(dev); -+ const char *state; -+ -+ switch (hub->state) { -+ case SSAM_KIP_HUB_UNINITIALIZED: -+ state = "uninitialized"; -+ break; -+ -+ case SSAM_KIP_HUB_CONNECTED: -+ state = "connected"; -+ break; -+ -+ case SSAM_KIP_HUB_DISCONNECTED: -+ state = "disconnected"; -+ break; -+ -+ default: -+ /* -+ * Any value not handled in the above cases is invalid and -+ * should never have been set. Thus this case should be -+ * impossible to reach. -+ */ -+ WARN(1, "invalid KIP hub state: %d\n", hub->state); -+ state = ""; -+ break; -+ } -+ -+ return sysfs_emit(buf, "%s\n", state); -+} -+ -+static struct device_attribute ssam_kip_hub_attr_state = -+ __ATTR(state, 0444, ssam_kip_hub_state_show, NULL); -+ -+static struct attribute *ssam_kip_hub_attrs[] = { -+ &ssam_kip_hub_attr_state.attr, -+ NULL, -+}; -+ -+static const struct attribute_group ssam_kip_hub_group = { -+ .attrs = ssam_kip_hub_attrs, -+}; -+ -+static int ssam_kip_hub_mark_hot_removed(struct device *dev, void *_data) -+{ -+ struct ssam_device *sdev = to_ssam_device(dev); -+ -+ if (is_ssam_device(dev)) -+ ssam_device_mark_hot_removed(sdev); -+ -+ return 0; -+} -+ -+static void ssam_kip_hub_update_workfn(struct work_struct *work) -+{ -+ struct ssam_kip_hub *hub = container_of(work, struct ssam_kip_hub, update_work.work); -+ struct fwnode_handle *node = dev_fwnode(&hub->sdev->dev); -+ enum ssam_kip_hub_state state; -+ int status = 0; -+ -+ status = ssam_kip_get_connection_state(hub, &state); -+ if (status) -+ return; -+ -+ if (hub->state == state) -+ return; -+ hub->state = state; -+ -+ if (hub->state == SSAM_KIP_HUB_CONNECTED) -+ status = ssam_hub_register_clients(&hub->sdev->dev, hub->sdev->ctrl, node); -+ else -+ ssam_remove_clients(&hub->sdev->dev); -+ -+ if (status) -+ dev_err(&hub->sdev->dev, "failed to update KIP-hub devices: %d\n", status); -+} -+ -+static u32 ssam_kip_hub_notif(struct ssam_event_notifier *nf, const struct ssam_event *event) -+{ -+ struct ssam_kip_hub *hub = container_of(nf, struct ssam_kip_hub, notif); -+ unsigned long delay; -+ -+ if (event->command_id != SSAM_EVENT_KIP_CID_CONNECTION) -+ return 0; /* Return "unhandled". */ -+ -+ if (event->length < 1) { -+ dev_err(&hub->sdev->dev, "unexpected payload size: %u\n", event->length); -+ return 0; -+ } -+ -+ /* Mark devices as hot-removed before we remove any */ -+ if (!event->data[0]) -+ device_for_each_child_reverse(&hub->sdev->dev, NULL, ssam_kip_hub_mark_hot_removed); -+ -+ /* -+ * Delay update when KIP devices are being connected to give devices/EC -+ * some time to set up. -+ */ -+ delay = event->data[0] ? SSAM_KIP_UPDATE_CONNECT_DELAY : 0; -+ -+ schedule_delayed_work(&hub->update_work, delay); -+ return SSAM_NOTIF_HANDLED; -+} -+ -+static int __maybe_unused ssam_kip_hub_resume(struct device *dev) -+{ -+ struct ssam_kip_hub *hub = dev_get_drvdata(dev); -+ -+ schedule_delayed_work(&hub->update_work, 0); -+ return 0; -+} -+static SIMPLE_DEV_PM_OPS(ssam_kip_hub_pm_ops, NULL, ssam_kip_hub_resume); -+ -+static int ssam_kip_hub_probe(struct ssam_device *sdev) -+{ -+ struct ssam_kip_hub *hub; -+ int status; -+ -+ hub = devm_kzalloc(&sdev->dev, sizeof(*hub), GFP_KERNEL); -+ if (!hub) -+ return -ENOMEM; -+ -+ hub->sdev = sdev; -+ hub->state = SSAM_KIP_HUB_UNINITIALIZED; -+ -+ hub->notif.base.priority = INT_MAX; /* This notifier should run first. */ -+ hub->notif.base.fn = ssam_kip_hub_notif; -+ hub->notif.event.reg = SSAM_EVENT_REGISTRY_SAM; -+ hub->notif.event.id.target_category = SSAM_SSH_TC_KIP, -+ hub->notif.event.id.instance = 0, -+ hub->notif.event.mask = SSAM_EVENT_MASK_TARGET; -+ hub->notif.event.flags = SSAM_EVENT_SEQUENCED; -+ -+ INIT_DELAYED_WORK(&hub->update_work, ssam_kip_hub_update_workfn); -+ -+ ssam_device_set_drvdata(sdev, hub); -+ -+ status = ssam_device_notifier_register(sdev, &hub->notif); -+ if (status) -+ return status; -+ -+ status = sysfs_create_group(&sdev->dev.kobj, &ssam_kip_hub_group); -+ if (status) -+ goto err; -+ -+ schedule_delayed_work(&hub->update_work, 0); -+ return 0; -+ -+err: -+ ssam_device_notifier_unregister(sdev, &hub->notif); -+ cancel_delayed_work_sync(&hub->update_work); -+ ssam_remove_clients(&sdev->dev); -+ return status; -+} -+ -+static void ssam_kip_hub_remove(struct ssam_device *sdev) -+{ -+ struct ssam_kip_hub *hub = ssam_device_get_drvdata(sdev); -+ -+ sysfs_remove_group(&sdev->dev.kobj, &ssam_kip_hub_group); -+ -+ ssam_device_notifier_unregister(sdev, &hub->notif); -+ cancel_delayed_work_sync(&hub->update_work); -+ ssam_remove_clients(&sdev->dev); -+} -+ -+static const struct ssam_device_id ssam_kip_hub_match[] = { -+ { SSAM_SDEV(KIP, 0x01, 0x00, 0x00) }, -+ { }, -+}; -+ -+static struct ssam_device_driver ssam_kip_hub_driver = { -+ .probe = ssam_kip_hub_probe, -+ .remove = ssam_kip_hub_remove, -+ .match_table = ssam_kip_hub_match, -+ .driver = { -+ .name = "surface_kip_hub", -+ .probe_type = PROBE_PREFER_ASYNCHRONOUS, -+ .pm = &ssam_kip_hub_pm_ops, -+ }, -+}; -+ -+ - /* -- SSAM platform/meta-hub driver. ---------------------------------------- */ - - static const struct acpi_device_id ssam_platform_hub_match[] = { -@@ -636,18 +867,30 @@ static int __init ssam_device_hub_init(void) - - status = platform_driver_register(&ssam_platform_hub_driver); - if (status) -- return status; -+ goto err_platform; - - status = ssam_device_driver_register(&ssam_base_hub_driver); - if (status) -- platform_driver_unregister(&ssam_platform_hub_driver); -+ goto err_base; -+ -+ status = ssam_device_driver_register(&ssam_kip_hub_driver); -+ if (status) -+ goto err_kip; - -+ return 0; -+ -+err_kip: -+ ssam_device_driver_unregister(&ssam_base_hub_driver); -+err_base: -+ platform_driver_unregister(&ssam_platform_hub_driver); -+err_platform: - return status; - } - module_init(ssam_device_hub_init); - - static void __exit ssam_device_hub_exit(void) - { -+ ssam_device_driver_unregister(&ssam_kip_hub_driver); - ssam_device_driver_unregister(&ssam_base_hub_driver); - platform_driver_unregister(&ssam_platform_hub_driver); - } --- -2.35.1 - -From 8cddf47c6b5f66bbe9fab52cdfc5d9ddaa922ef2 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Wed, 27 Oct 2021 22:33:03 +0200 -Subject: [PATCH] platform/surface: aggregator_registry: Add support for - keyboard cover on Surface Pro 8 - -Add support for the detachable keyboard cover on the Surface Pro 8. - -The keyboard cover on the Surface Pro 8 is, unlike the keyboard covers -of earlier Surface Pro generations, handled via the Surface System -Aggregator Module (SSAM). The keyboard and touchpad (as well as other -HID input devices) of this cover are standard SSAM HID client devices -(just like keyboard and touchpad on e.g. the Surface Laptop 3 and 4), -however, some care needs to be taken as they can be physically detached -(similarly to the Surface Book 3). Specifically, the respective SSAM -client devices need to be removed when the keyboard cover has been -detached and (re-)initialized when the keyboard cover has been -(re-)attached. - -On the Surface Pro 8, detachment of the keyboard cover (and by extension -its devices) is managed via the KIP subsystem. Therefore, said devices -need to be registered under the KIP device hub, which in turn will -remove and re-create/re-initialize those devices as needed. - -Signed-off-by: Maximilian Luz -Patchset: surface-sam ---- - .../surface/surface_aggregator_registry.c | 37 ++++++++++++++++++- - 1 file changed, 36 insertions(+), 1 deletion(-) - -diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c -index 4838ce6519a6..c0e29c0514df 100644 ---- a/drivers/platform/surface/surface_aggregator_registry.c -+++ b/drivers/platform/surface/surface_aggregator_registry.c -@@ -47,6 +47,12 @@ static const struct software_node ssam_node_hub_base = { - .parent = &ssam_node_root, - }; - -+/* KIP device hub (connects keyboard cover devices on Surface Pro 8). */ -+static const struct software_node ssam_node_hub_kip = { -+ .name = "ssam:01:0e:01:00:00", -+ .parent = &ssam_node_root, -+}; -+ - /* AC adapter. */ - static const struct software_node ssam_node_bat_ac = { - .name = "ssam:01:02:01:01:01", -@@ -155,6 +161,30 @@ static const struct software_node ssam_node_hid_base_iid6 = { - .parent = &ssam_node_hub_base, - }; - -+/* HID keyboard (KIP hub). */ -+static const struct software_node ssam_node_hid_kip_keyboard = { -+ .name = "ssam:01:15:02:01:00", -+ .parent = &ssam_node_hub_kip, -+}; -+ -+/* HID pen stash (KIP hub; pen taken / stashed away evens). */ -+static const struct software_node ssam_node_hid_kip_penstash = { -+ .name = "ssam:01:15:02:02:00", -+ .parent = &ssam_node_hub_kip, -+}; -+ -+/* HID touchpad (KIP hub). */ -+static const struct software_node ssam_node_hid_kip_touchpad = { -+ .name = "ssam:01:15:02:03:00", -+ .parent = &ssam_node_hub_kip, -+}; -+ -+/* HID device instance 5 (KIP hub, unknown HID device). */ -+static const struct software_node ssam_node_hid_kip_iid5 = { -+ .name = "ssam:01:15:02:05:00", -+ .parent = &ssam_node_hub_kip, -+}; -+ - /* - * Devices for 5th- and 6th-generations models: - * - Surface Book 2, -@@ -230,10 +260,15 @@ static const struct software_node *ssam_node_group_sp7[] = { - - static const struct software_node *ssam_node_group_sp8[] = { - &ssam_node_root, -+ &ssam_node_hub_kip, - &ssam_node_bat_ac, - &ssam_node_bat_main, - &ssam_node_tmp_pprof, -- /* TODO: Add support for keyboard cover. */ -+ &ssam_node_hid_kip_keyboard, -+ &ssam_node_hid_kip_penstash, -+ &ssam_node_hid_kip_touchpad, -+ &ssam_node_hid_kip_iid5, -+ /* TODO: Add support for tablet mode switch. */ - NULL, - }; - --- -2.35.1 - -From 8b16468073e469878a5af177d47ea980239cf685 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Tue, 8 Jun 2021 03:19:20 +0200 -Subject: [PATCH] platform/surface: Add KIP tablet-mode switch - -Add a driver providing a tablet-mode switch input device for Surface -models using the KIP subsystem to manage detachable peripherals. - -The Surface Pro 8 has a detachable keyboard cover. Unlike the keyboard -covers of previous generation Surface Pro models, this cover is fully -handled by the Surface System Aggregator Module (SSAM). The SSAM KIP -subsystem (full name unknown, abbreviation found through reverse -engineering) provides notifications for mode changes of the cover. -Specifically, it allows us to know when the cover has been folded back, -detached, or whether it is in laptop mode. - -The driver introduced with this change captures these events and -translates them to standard SW_TABLET_MODE input events. - -Signed-off-by: Maximilian Luz -Patchset: surface-sam ---- - MAINTAINERS | 6 + - drivers/platform/surface/Kconfig | 22 ++ - drivers/platform/surface/Makefile | 1 + - .../surface/surface_kip_tablet_switch.c | 245 ++++++++++++++++++ - 4 files changed, 274 insertions(+) - create mode 100644 drivers/platform/surface/surface_kip_tablet_switch.c - -diff --git a/MAINTAINERS b/MAINTAINERS -index dd36acc87ce6..d69fdf5aadec 100644 ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -12678,6 +12678,12 @@ L: platform-driver-x86@vger.kernel.org - S: Maintained - F: drivers/platform/surface/surface_hotplug.c - -+MICROSOFT SURFACE KIP TABLET-MODE SWITCH -+M: Maximilian Luz -+L: platform-driver-x86@vger.kernel.org -+S: Maintained -+F: drivers/platform/surface/surface_kip_tablet_switch.c -+ - MICROSOFT SURFACE PLATFORM PROFILE DRIVER - M: Maximilian Luz - L: platform-driver-x86@vger.kernel.org -diff --git a/drivers/platform/surface/Kconfig b/drivers/platform/surface/Kconfig -index 3105f651614f..3c0ee0cdaef5 100644 ---- a/drivers/platform/surface/Kconfig -+++ b/drivers/platform/surface/Kconfig -@@ -152,6 +152,28 @@ config SURFACE_HOTPLUG - Select M or Y here, if you want to (fully) support hot-plugging of - dGPU devices on the Surface Book 2 and/or 3 during D3cold. - -+config SURFACE_KIP_TABLET_SWITCH -+ tristate "Surface KIP Tablet-Mode Switch Driver" -+ depends on SURFACE_AGGREGATOR -+ depends on SURFACE_AGGREGATOR_BUS -+ depends on INPUT -+ help -+ Provides a tablet-mode switch input device on Microsoft Surface models -+ using the KIP subsystem for detachable keyboards (e.g. keyboard -+ covers). -+ -+ The KIP subsystem is used on newer Surface generations to handle -+ detachable input peripherals, specifically the keyboard cover -+ (containing keyboard and touchpad) on the Surface Pro 8. This module -+ provides a driver to let user-space know when the device should be -+ considered in tablet-mode due to the keyboard cover being detached or -+ folded back (essentially signaling when the keyboard is not available -+ for input). It does so by creating a tablet-mode switch input device, -+ sending the standard SW_TABLET_MODE event on mode change. -+ -+ Select M or Y here, if you want to provide tablet-mode switch input -+ events on the Surface Pro 8. -+ - config SURFACE_PLATFORM_PROFILE - tristate "Surface Platform Profile Driver" - depends on SURFACE_AGGREGATOR_REGISTRY -diff --git a/drivers/platform/surface/Makefile b/drivers/platform/surface/Makefile -index 32889482de55..6d9291c993c4 100644 ---- a/drivers/platform/surface/Makefile -+++ b/drivers/platform/surface/Makefile -@@ -14,5 +14,6 @@ obj-$(CONFIG_SURFACE_AGGREGATOR_REGISTRY) += surface_aggregator_registry.o - obj-$(CONFIG_SURFACE_DTX) += surface_dtx.o - obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o - obj-$(CONFIG_SURFACE_HOTPLUG) += surface_hotplug.o -+obj-$(CONFIG_SURFACE_KIP_TABLET_SWITCH) += surface_kip_tablet_switch.o - obj-$(CONFIG_SURFACE_PLATFORM_PROFILE) += surface_platform_profile.o - obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o -diff --git a/drivers/platform/surface/surface_kip_tablet_switch.c b/drivers/platform/surface/surface_kip_tablet_switch.c -new file mode 100644 -index 000000000000..458470067579 ---- /dev/null -+++ b/drivers/platform/surface/surface_kip_tablet_switch.c -@@ -0,0 +1,245 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* -+ * Surface System Aggregator Module (SSAM) tablet mode switch via KIP -+ * subsystem. -+ * -+ * Copyright (C) 2021 Maximilian Luz -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#define SSAM_EVENT_KIP_CID_LID_STATE 0x1d -+ -+enum ssam_kip_lid_state { -+ SSAM_KIP_LID_STATE_DISCONNECTED = 0x01, -+ SSAM_KIP_LID_STATE_CLOSED = 0x02, -+ SSAM_KIP_LID_STATE_LAPTOP = 0x03, -+ SSAM_KIP_LID_STATE_FOLDED_CANVAS = 0x04, -+ SSAM_KIP_LID_STATE_FOLDED_BACK = 0x05, -+}; -+ -+struct ssam_kip_sw { -+ struct ssam_device *sdev; -+ -+ enum ssam_kip_lid_state state; -+ struct work_struct update_work; -+ struct input_dev *mode_switch; -+ -+ struct ssam_event_notifier notif; -+}; -+ -+SSAM_DEFINE_SYNC_REQUEST_R(__ssam_kip_get_lid_state, u8, { -+ .target_category = SSAM_SSH_TC_KIP, -+ .target_id = 0x01, -+ .command_id = 0x1d, -+ .instance_id = 0x00, -+}); -+ -+static int ssam_kip_get_lid_state(struct ssam_kip_sw *sw, enum ssam_kip_lid_state *state) -+{ -+ int status; -+ u8 raw; -+ -+ status = ssam_retry(__ssam_kip_get_lid_state, sw->sdev->ctrl, &raw); -+ if (status < 0) { -+ dev_err(&sw->sdev->dev, "failed to query KIP lid state: %d\n", status); -+ return status; -+ } -+ -+ *state = raw; -+ return 0; -+} -+ -+static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct ssam_kip_sw *sw = dev_get_drvdata(dev); -+ const char *state; -+ -+ switch (sw->state) { -+ case SSAM_KIP_LID_STATE_DISCONNECTED: -+ state = "disconnected"; -+ break; -+ -+ case SSAM_KIP_LID_STATE_CLOSED: -+ state = "closed"; -+ break; -+ -+ case SSAM_KIP_LID_STATE_LAPTOP: -+ state = "laptop"; -+ break; -+ -+ case SSAM_KIP_LID_STATE_FOLDED_CANVAS: -+ state = "folded-canvas"; -+ break; -+ -+ case SSAM_KIP_LID_STATE_FOLDED_BACK: -+ state = "folded-back"; -+ break; -+ -+ default: -+ state = ""; -+ dev_warn(dev, "unknown KIP lid state: %d\n", sw->state); -+ break; -+ } -+ -+ return sysfs_emit(buf, "%s\n", state); -+} -+static DEVICE_ATTR_RO(state); -+ -+static struct attribute *ssam_kip_sw_attrs[] = { -+ &dev_attr_state.attr, -+ NULL, -+}; -+ -+static const struct attribute_group ssam_kip_sw_group = { -+ .attrs = ssam_kip_sw_attrs, -+}; -+ -+static void ssam_kip_sw_update_workfn(struct work_struct *work) -+{ -+ struct ssam_kip_sw *sw = container_of(work, struct ssam_kip_sw, update_work); -+ enum ssam_kip_lid_state state; -+ int tablet, status; -+ -+ status = ssam_kip_get_lid_state(sw, &state); -+ if (status) -+ return; -+ -+ if (sw->state == state) -+ return; -+ sw->state = state; -+ -+ /* Send SW_TABLET_MODE event. */ -+ tablet = state != SSAM_KIP_LID_STATE_LAPTOP; -+ input_report_switch(sw->mode_switch, SW_TABLET_MODE, tablet); -+ input_sync(sw->mode_switch); -+} -+ -+static u32 ssam_kip_sw_notif(struct ssam_event_notifier *nf, const struct ssam_event *event) -+{ -+ struct ssam_kip_sw *sw = container_of(nf, struct ssam_kip_sw, notif); -+ -+ if (event->command_id != SSAM_EVENT_KIP_CID_LID_STATE) -+ return 0; /* Return "unhandled". */ -+ -+ if (event->length < 1) { -+ dev_err(&sw->sdev->dev, "unexpected payload size: %u\n", event->length); -+ return 0; -+ } -+ -+ schedule_work(&sw->update_work); -+ return SSAM_NOTIF_HANDLED; -+} -+ -+static int __maybe_unused ssam_kip_sw_resume(struct device *dev) -+{ -+ struct ssam_kip_sw *sw = dev_get_drvdata(dev); -+ -+ schedule_work(&sw->update_work); -+ return 0; -+} -+static SIMPLE_DEV_PM_OPS(ssam_kip_sw_pm_ops, NULL, ssam_kip_sw_resume); -+ -+static int ssam_kip_sw_probe(struct ssam_device *sdev) -+{ -+ struct ssam_kip_sw *sw; -+ int tablet, status; -+ -+ sw = devm_kzalloc(&sdev->dev, sizeof(*sw), GFP_KERNEL); -+ if (!sw) -+ return -ENOMEM; -+ -+ sw->sdev = sdev; -+ INIT_WORK(&sw->update_work, ssam_kip_sw_update_workfn); -+ -+ ssam_device_set_drvdata(sdev, sw); -+ -+ /* Get initial state. */ -+ status = ssam_kip_get_lid_state(sw, &sw->state); -+ if (status) -+ return status; -+ -+ /* Set up tablet mode switch. */ -+ sw->mode_switch = devm_input_allocate_device(&sdev->dev); -+ if (!sw->mode_switch) -+ return -ENOMEM; -+ -+ sw->mode_switch->name = "Microsoft Surface KIP Tablet Mode Switch"; -+ sw->mode_switch->phys = "ssam/01:0e:01:00:01/input0"; -+ sw->mode_switch->id.bustype = BUS_HOST; -+ sw->mode_switch->dev.parent = &sdev->dev; -+ -+ tablet = sw->state != SSAM_KIP_LID_STATE_LAPTOP; -+ input_set_capability(sw->mode_switch, EV_SW, SW_TABLET_MODE); -+ input_report_switch(sw->mode_switch, SW_TABLET_MODE, tablet); -+ -+ status = input_register_device(sw->mode_switch); -+ if (status) -+ return status; -+ -+ /* Set up notifier. */ -+ sw->notif.base.priority = 0; -+ sw->notif.base.fn = ssam_kip_sw_notif; -+ sw->notif.event.reg = SSAM_EVENT_REGISTRY_SAM; -+ sw->notif.event.id.target_category = SSAM_SSH_TC_KIP, -+ sw->notif.event.id.instance = 0, -+ sw->notif.event.mask = SSAM_EVENT_MASK_TARGET; -+ sw->notif.event.flags = SSAM_EVENT_SEQUENCED; -+ -+ status = ssam_device_notifier_register(sdev, &sw->notif); -+ if (status) -+ return status; -+ -+ status = sysfs_create_group(&sdev->dev.kobj, &ssam_kip_sw_group); -+ if (status) -+ goto err; -+ -+ /* We might have missed events during setup, so check again. */ -+ schedule_work(&sw->update_work); -+ return 0; -+ -+err: -+ ssam_device_notifier_unregister(sdev, &sw->notif); -+ cancel_work_sync(&sw->update_work); -+ return status; -+} -+ -+static void ssam_kip_sw_remove(struct ssam_device *sdev) -+{ -+ struct ssam_kip_sw *sw = ssam_device_get_drvdata(sdev); -+ -+ sysfs_remove_group(&sdev->dev.kobj, &ssam_kip_sw_group); -+ -+ ssam_device_notifier_unregister(sdev, &sw->notif); -+ cancel_work_sync(&sw->update_work); -+} -+ -+static const struct ssam_device_id ssam_kip_sw_match[] = { -+ { SSAM_SDEV(KIP, 0x01, 0x00, 0x01) }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(ssam, ssam_kip_sw_match); -+ -+static struct ssam_device_driver ssam_kip_sw_driver = { -+ .probe = ssam_kip_sw_probe, -+ .remove = ssam_kip_sw_remove, -+ .match_table = ssam_kip_sw_match, -+ .driver = { -+ .name = "surface_kip_tablet_mode_switch", -+ .probe_type = PROBE_PREFER_ASYNCHRONOUS, -+ .pm = &ssam_kip_sw_pm_ops, -+ }, -+}; -+module_ssam_device_driver(ssam_kip_sw_driver); -+ -+MODULE_AUTHOR("Maximilian Luz "); -+MODULE_DESCRIPTION("Tablet mode switch driver for Surface devices using KIP subsystem"); -+MODULE_LICENSE("GPL"); --- -2.35.1 - -From a3a81804d853465aba844be04a4d755efe919a44 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Wed, 27 Oct 2021 22:33:03 +0200 -Subject: [PATCH] platform/surface: aggregator_registry: Add support for tablet - mode switch on Surface Pro 8 - -Add a KIP subsystem tablet-mode switch device for the Surface Pro 8. -The respective driver for this device provides SW_TABLET_MODE input -events for user-space based on the state of the keyboard cover (e.g. -detached, folded-back, normal/laptop mode). - -Signed-off-by: Maximilian Luz -Patchset: surface-sam ---- - drivers/platform/surface/surface_aggregator_registry.c | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - -diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c -index c0e29c0514df..eaf0054627a5 100644 ---- a/drivers/platform/surface/surface_aggregator_registry.c -+++ b/drivers/platform/surface/surface_aggregator_registry.c -@@ -77,6 +77,12 @@ static const struct software_node ssam_node_tmp_pprof = { - .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", -+ .parent = &ssam_node_root, -+}; -+ - /* DTX / detachment-system device (Surface Book 3). */ - static const struct software_node ssam_node_bas_dtx = { - .name = "ssam:01:11:01:00:00", -@@ -264,11 +270,11 @@ static const struct software_node *ssam_node_group_sp8[] = { - &ssam_node_bat_ac, - &ssam_node_bat_main, - &ssam_node_tmp_pprof, -+ &ssam_node_kip_tablet_switch, - &ssam_node_hid_kip_keyboard, - &ssam_node_hid_kip_penstash, - &ssam_node_hid_kip_touchpad, - &ssam_node_hid_kip_iid5, -- /* TODO: Add support for tablet mode switch. */ - NULL, - }; - --- -2.35.1 - diff --git a/patches/5.16/0007-surface-gpe.patch b/patches/5.16/0007-surface-gpe.patch index 68c92628e..94c0310b1 100644 --- a/patches/5.16/0007-surface-gpe.patch +++ b/patches/5.16/0007-surface-gpe.patch @@ -1,4 +1,4 @@ -From 3bd8819271f8893096f0fffae9dc1375e1c1ebf3 Mon Sep 17 00:00:00 2001 +From e7a76b51f9f78f4887e07ba09c949e50db7afdb6 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Wed, 27 Oct 2021 00:56:11 +0200 Subject: [PATCH] platform/surface: gpe: Add support for Surface Pro 8 diff --git a/patches/5.16/0007-surface-sam-over-hid.patch b/patches/5.16/0007-surface-sam-over-hid.patch deleted file mode 100644 index 8384deaa3..000000000 --- a/patches/5.16/0007-surface-sam-over-hid.patch +++ /dev/null @@ -1,335 +0,0 @@ -From a586f227220ffc968942b304242ffb85a4ad738a Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Sat, 25 Jul 2020 17:19:53 +0200 -Subject: [PATCH] i2c: acpi: Implement RawBytes read access - -Microsoft Surface Pro 4 and Book 1 devices access the MSHW0030 I2C -device via a generic serial bus operation region and RawBytes read -access. On the Surface Book 1, this access is required to turn on (and -off) the discrete GPU. - -Multiple things are to note here: - -a) The RawBytes access is device/driver dependent. The ACPI - specification states: - - > Raw accesses assume that the writer has knowledge of the bus that - > the access is made over and the device that is being accessed. The - > protocol may only ensure that the buffer is transmitted to the - > appropriate driver, but the driver must be able to interpret the - > buffer to communicate to a register. - - Thus this implementation may likely not work on other devices - accessing I2C via the RawBytes accessor type. - -b) The MSHW0030 I2C device is an HID-over-I2C device which seems to - serve multiple functions: - - 1. It is the main access point for the legacy-type Surface Aggregator - Module (also referred to as SAM-over-HID, as opposed to the newer - SAM-over-SSH/UART). It has currently not been determined on how - support for the legacy SAM should be implemented. Likely via a - custom HID driver. - - 2. It seems to serve as the HID device for the Integrated Sensor Hub. - This might complicate matters with regards to implementing a - SAM-over-HID driver required by legacy SAM. - -In light of this, the simplest approach has been chosen for now. -However, it may make more sense regarding breakage and compatibility to -either provide functionality for replacing or enhancing the default -operation region handler via some additional API functions, or even to -completely blacklist MSHW0030 from the I2C core and provide a custom -driver for it. - -Replacing/enhancing the default operation region handler would, however, -either require some sort of secondary driver and access point for it, -from which the new API functions would be called and the new handler -(part) would be installed, or hard-coding them via some sort of -quirk-like interface into the I2C core. - -Signed-off-by: Maximilian Luz -Patchset: surface-sam-over-hid ---- - drivers/i2c/i2c-core-acpi.c | 35 +++++++++++++++++++++++++++++++++++ - 1 file changed, 35 insertions(+) - -diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c -index 92c1cc07ed46..3b688cea8e00 100644 ---- a/drivers/i2c/i2c-core-acpi.c -+++ b/drivers/i2c/i2c-core-acpi.c -@@ -609,6 +609,28 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client, - return (ret == 1) ? 0 : -EIO; - } - -+static int acpi_gsb_i2c_write_raw_bytes(struct i2c_client *client, -+ u8 *data, u8 data_len) -+{ -+ struct i2c_msg msgs[1]; -+ int ret = AE_OK; -+ -+ msgs[0].addr = client->addr; -+ msgs[0].flags = client->flags; -+ msgs[0].len = data_len + 1; -+ msgs[0].buf = data; -+ -+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); -+ -+ if (ret < 0) { -+ dev_err(&client->adapter->dev, "i2c write failed: %d\n", ret); -+ return ret; -+ } -+ -+ /* 1 transfer must have completed successfully */ -+ return (ret == 1) ? 0 : -EIO; -+} -+ - static acpi_status - i2c_acpi_space_handler(u32 function, acpi_physical_address command, - u32 bits, u64 *value64, -@@ -710,6 +732,19 @@ i2c_acpi_space_handler(u32 function, acpi_physical_address command, - } - break; - -+ case ACPI_GSB_ACCESS_ATTRIB_RAW_BYTES: -+ if (action == ACPI_READ) { -+ dev_warn(&adapter->dev, -+ "protocol 0x%02x not supported for client 0x%02x\n", -+ accessor_type, client->addr); -+ ret = AE_BAD_PARAMETER; -+ goto err; -+ } else { -+ status = acpi_gsb_i2c_write_raw_bytes(client, -+ gsb->data, info->access_length); -+ } -+ break; -+ - default: - dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n", - accessor_type, client->addr); --- -2.35.1 - -From 21496a214799137c6188bf65c90869af0d90ca21 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Sat, 13 Feb 2021 16:41:18 +0100 -Subject: [PATCH] platform/surface: Add driver for Surface Book 1 dGPU switch - -Add driver exposing the discrete GPU power-switch of the Microsoft -Surface Book 1 to user-space. - -On the Surface Book 1, the dGPU power is controlled via the Surface -System Aggregator Module (SAM). The specific SAM-over-HID command for -this is exposed via ACPI. This module provides a simple driver exposing -the ACPI call via a sysfs parameter to user-space, so that users can -easily power-on/-off the dGPU. - -Patchset: surface-sam-over-hid ---- - drivers/platform/surface/Kconfig | 7 + - drivers/platform/surface/Makefile | 1 + - .../surface/surfacebook1_dgpu_switch.c | 162 ++++++++++++++++++ - 3 files changed, 170 insertions(+) - create mode 100644 drivers/platform/surface/surfacebook1_dgpu_switch.c - -diff --git a/drivers/platform/surface/Kconfig b/drivers/platform/surface/Kconfig -index 3c0ee0cdaef5..e5eedb85d471 100644 ---- a/drivers/platform/surface/Kconfig -+++ b/drivers/platform/surface/Kconfig -@@ -104,6 +104,13 @@ config SURFACE_AGGREGATOR_REGISTRY - the respective client devices. Drivers for these devices still need to - be selected via the other options. - -+config SURFACE_BOOK1_DGPU_SWITCH -+ tristate "Surface Book 1 dGPU Switch Driver" -+ depends on SYSFS -+ help -+ This driver provides a sysfs switch to set the power-state of the -+ discrete GPU found on the Microsoft Surface Book 1. -+ - config SURFACE_DTX - tristate "Surface DTX (Detachment System) Driver" - depends on SURFACE_AGGREGATOR -diff --git a/drivers/platform/surface/Makefile b/drivers/platform/surface/Makefile -index 6d9291c993c4..9eb3a7e6382c 100644 ---- a/drivers/platform/surface/Makefile -+++ b/drivers/platform/surface/Makefile -@@ -11,6 +11,7 @@ obj-$(CONFIG_SURFACE_ACPI_NOTIFY) += surface_acpi_notify.o - obj-$(CONFIG_SURFACE_AGGREGATOR) += aggregator/ - obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV) += surface_aggregator_cdev.o - obj-$(CONFIG_SURFACE_AGGREGATOR_REGISTRY) += surface_aggregator_registry.o -+obj-$(CONFIG_SURFACE_BOOK1_DGPU_SWITCH) += surfacebook1_dgpu_switch.o - obj-$(CONFIG_SURFACE_DTX) += surface_dtx.o - obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o - obj-$(CONFIG_SURFACE_HOTPLUG) += surface_hotplug.o -diff --git a/drivers/platform/surface/surfacebook1_dgpu_switch.c b/drivers/platform/surface/surfacebook1_dgpu_switch.c -new file mode 100644 -index 000000000000..8b816ed8f35c ---- /dev/null -+++ b/drivers/platform/surface/surfacebook1_dgpu_switch.c -@@ -0,0 +1,162 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+ -+#include -+#include -+#include -+#include -+ -+ -+#ifdef pr_fmt -+#undef pr_fmt -+#endif -+#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ -+ -+ -+static const guid_t dgpu_sw_guid = GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, -+ 0x95, 0xed, 0xab, 0x16, 0x65, 0x49, 0x80, 0x35); -+ -+#define DGPUSW_ACPI_PATH_DSM "\\_SB_.PCI0.LPCB.EC0_.VGBI" -+#define DGPUSW_ACPI_PATH_HGON "\\_SB_.PCI0.RP05.HGON" -+#define DGPUSW_ACPI_PATH_HGOF "\\_SB_.PCI0.RP05.HGOF" -+ -+ -+static int sb1_dgpu_sw_dsmcall(void) -+{ -+ union acpi_object *ret; -+ acpi_handle handle; -+ acpi_status status; -+ -+ status = acpi_get_handle(NULL, DGPUSW_ACPI_PATH_DSM, &handle); -+ if (status) -+ return -EINVAL; -+ -+ ret = acpi_evaluate_dsm_typed(handle, &dgpu_sw_guid, 1, 1, NULL, ACPI_TYPE_BUFFER); -+ if (!ret) -+ return -EINVAL; -+ -+ ACPI_FREE(ret); -+ return 0; -+} -+ -+static int sb1_dgpu_sw_hgon(void) -+{ -+ struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; -+ acpi_status status; -+ -+ status = acpi_evaluate_object(NULL, DGPUSW_ACPI_PATH_HGON, NULL, &buf); -+ if (status) { -+ pr_err("failed to run HGON: %d\n", status); -+ return -EINVAL; -+ } -+ -+ if (buf.pointer) -+ ACPI_FREE(buf.pointer); -+ -+ pr_info("turned-on dGPU via HGON\n"); -+ return 0; -+} -+ -+static int sb1_dgpu_sw_hgof(void) -+{ -+ struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; -+ acpi_status status; -+ -+ status = acpi_evaluate_object(NULL, DGPUSW_ACPI_PATH_HGOF, NULL, &buf); -+ if (status) { -+ pr_err("failed to run HGOF: %d\n", status); -+ return -EINVAL; -+ } -+ -+ if (buf.pointer) -+ ACPI_FREE(buf.pointer); -+ -+ pr_info("turned-off dGPU via HGOF\n"); -+ return 0; -+} -+ -+ -+static ssize_t dgpu_dsmcall_store(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t len) -+{ -+ int status, value; -+ -+ status = kstrtoint(buf, 0, &value); -+ if (status < 0) -+ return status; -+ -+ if (value != 1) -+ return -EINVAL; -+ -+ status = sb1_dgpu_sw_dsmcall(); -+ -+ return status < 0 ? status : len; -+} -+ -+static ssize_t dgpu_power_store(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t len) -+{ -+ bool power; -+ int status; -+ -+ status = kstrtobool(buf, &power); -+ if (status < 0) -+ return status; -+ -+ if (power) -+ status = sb1_dgpu_sw_hgon(); -+ else -+ status = sb1_dgpu_sw_hgof(); -+ -+ return status < 0 ? status : len; -+} -+ -+static DEVICE_ATTR_WO(dgpu_dsmcall); -+static DEVICE_ATTR_WO(dgpu_power); -+ -+static struct attribute *sb1_dgpu_sw_attrs[] = { -+ &dev_attr_dgpu_dsmcall.attr, -+ &dev_attr_dgpu_power.attr, -+ NULL, -+}; -+ -+static const struct attribute_group sb1_dgpu_sw_attr_group = { -+ .attrs = sb1_dgpu_sw_attrs, -+}; -+ -+ -+static int sb1_dgpu_sw_probe(struct platform_device *pdev) -+{ -+ return sysfs_create_group(&pdev->dev.kobj, &sb1_dgpu_sw_attr_group); -+} -+ -+static int sb1_dgpu_sw_remove(struct platform_device *pdev) -+{ -+ sysfs_remove_group(&pdev->dev.kobj, &sb1_dgpu_sw_attr_group); -+ return 0; -+} -+ -+/* -+ * The dGPU power seems to be actually handled by MSHW0040. However, that is -+ * also the power-/volume-button device with a mainline driver. So let's use -+ * MSHW0041 instead for now, which seems to be the LTCH (latch/DTX) device. -+ */ -+static const struct acpi_device_id sb1_dgpu_sw_match[] = { -+ { "MSHW0041", }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(acpi, sb1_dgpu_sw_match); -+ -+static struct platform_driver sb1_dgpu_sw = { -+ .probe = sb1_dgpu_sw_probe, -+ .remove = sb1_dgpu_sw_remove, -+ .driver = { -+ .name = "surfacebook1_dgpu_switch", -+ .acpi_match_table = sb1_dgpu_sw_match, -+ .probe_type = PROBE_PREFER_ASYNCHRONOUS, -+ }, -+}; -+module_platform_driver(sb1_dgpu_sw); -+ -+MODULE_AUTHOR("Maximilian Luz "); -+MODULE_DESCRIPTION("Discrete GPU Power-Switch for Surface Book 1"); -+MODULE_LICENSE("GPL"); --- -2.35.1 - diff --git a/patches/5.16/0008-surface-button.patch b/patches/5.16/0008-surface-button.patch index 06ca12ad9..aea8b5086 100644 --- a/patches/5.16/0008-surface-button.patch +++ b/patches/5.16/0008-surface-button.patch @@ -1,4 +1,4 @@ -From 47805f5c80c7efd8a464066f89b01000e4276060 Mon Sep 17 00:00:00 2001 +From b66d19a4dcc7a4173c82640c9a1be2532c819ea0 Mon Sep 17 00:00:00 2001 From: Sachi King Date: Tue, 5 Oct 2021 00:05:09 +1100 Subject: [PATCH] Input: soc_button_array - support AMD variant Surface devices @@ -75,7 +75,7 @@ index cb6ec59a045d..4e8944f59def 100644 -- 2.35.1 -From 332a74a321754eeb8aa4c2edd7c450608e7c1513 Mon Sep 17 00:00:00 2001 +From bc80832636dd4fb85653d81cc51bb836aacfabc4 Mon Sep 17 00:00:00 2001 From: Sachi King Date: Tue, 5 Oct 2021 00:22:57 +1100 Subject: [PATCH] platform/surface: surfacepro3_button: don't load on amd @@ -147,7 +147,7 @@ index 242fb690dcaf..30eea54dbb47 100644 -- 2.35.1 -From af4c7d2c0e364b9d58a2106d09ba54cf812e44ba Mon Sep 17 00:00:00 2001 +From 19e6f67c7b640286a1a8d451c9234b41bc7d118c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 24 Feb 2022 12:02:40 +0100 Subject: [PATCH] Input: soc_button_array - add support for Microsoft Surface 3 @@ -250,7 +250,7 @@ index 4e8944f59def..f044c731c6a9 100644 -- 2.35.1 -From 912018cdd132146de2b832cdbb3992071801e3c0 Mon Sep 17 00:00:00 2001 +From 0e894d02855d203d67b600a1c23eceee939f43f7 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 24 Feb 2022 12:02:41 +0100 Subject: [PATCH] platform/surface: Remove Surface 3 Button driver diff --git a/patches/5.16/0008-surface-gpe.patch b/patches/5.16/0008-surface-gpe.patch deleted file mode 100644 index 94c0310b1..000000000 --- a/patches/5.16/0008-surface-gpe.patch +++ /dev/null @@ -1,37 +0,0 @@ -From e7a76b51f9f78f4887e07ba09c949e50db7afdb6 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Wed, 27 Oct 2021 00:56:11 +0200 -Subject: [PATCH] platform/surface: gpe: Add support for Surface Pro 8 - -The new Surface Pro 8 uses GPEs for lid events as well. Add an entry for -that so that the lid can be used to wake the device. Note that this is a -device with a keyboard type cover, where this acts as the "lid". - -Signed-off-by: Maximilian Luz -Patchset: surface-gpe ---- - drivers/platform/surface/surface_gpe.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/drivers/platform/surface/surface_gpe.c b/drivers/platform/surface/surface_gpe.c -index c1775db29efb..ec66fde28e75 100644 ---- a/drivers/platform/surface/surface_gpe.c -+++ b/drivers/platform/surface/surface_gpe.c -@@ -99,6 +99,14 @@ static const struct dmi_system_id dmi_lid_device_table[] = { - }, - .driver_data = (void *)lid_device_props_l4D, - }, -+ { -+ .ident = "Surface Pro 8", -+ .matches = { -+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), -+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 8"), -+ }, -+ .driver_data = (void *)lid_device_props_l4B, -+ }, - { - .ident = "Surface Book 1", - .matches = { --- -2.35.1 - diff --git a/patches/5.16/0009-surface-button.patch b/patches/5.16/0009-surface-button.patch deleted file mode 100644 index aea8b5086..000000000 --- a/patches/5.16/0009-surface-button.patch +++ /dev/null @@ -1,557 +0,0 @@ -From b66d19a4dcc7a4173c82640c9a1be2532c819ea0 Mon Sep 17 00:00:00 2001 -From: Sachi King -Date: Tue, 5 Oct 2021 00:05:09 +1100 -Subject: [PATCH] Input: soc_button_array - support AMD variant Surface devices - -The power button on the AMD variant of the Surface Laptop uses the -same MSHW0040 device ID as the 5th and later generation of Surface -devices, however they report 0 for their OEM platform revision. As the -_DSM does not exist on the devices requiring special casing, check for -the existance of the _DSM to determine if soc_button_array should be -loaded. - -Fixes: c394159310d0 ("Input: soc_button_array - add support for newer surface devices") -Co-developed-by: Maximilian Luz - -Signed-off-by: Sachi King -Patchset: surface-button ---- - drivers/input/misc/soc_button_array.c | 33 +++++++-------------------- - 1 file changed, 8 insertions(+), 25 deletions(-) - -diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c -index cb6ec59a045d..4e8944f59def 100644 ---- a/drivers/input/misc/soc_button_array.c -+++ b/drivers/input/misc/soc_button_array.c -@@ -474,8 +474,8 @@ static const struct soc_device_data soc_device_INT33D3 = { - * Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned - * devices use MSHW0040 for power and volume buttons, however the way they - * have to be addressed differs. Make sure that we only load this drivers -- * for the correct devices by checking the OEM Platform Revision provided by -- * the _DSM method. -+ * for the correct devices by checking if the OEM Platform Revision DSM call -+ * exists. - */ - #define MSHW0040_DSM_REVISION 0x01 - #define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision -@@ -486,31 +486,14 @@ static const guid_t MSHW0040_DSM_UUID = - static int soc_device_check_MSHW0040(struct device *dev) - { - acpi_handle handle = ACPI_HANDLE(dev); -- union acpi_object *result; -- u64 oem_platform_rev = 0; // valid revisions are nonzero -- -- // get OEM platform revision -- result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, -- MSHW0040_DSM_REVISION, -- MSHW0040_DSM_GET_OMPR, NULL, -- ACPI_TYPE_INTEGER); -- -- if (result) { -- oem_platform_rev = result->integer.value; -- ACPI_FREE(result); -- } -- -- /* -- * If the revision is zero here, the _DSM evaluation has failed. This -- * indicates that we have a Pro 4 or Book 1 and this driver should not -- * be used. -- */ -- if (oem_platform_rev == 0) -- return -ENODEV; -+ bool exists; - -- dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev); -+ // check if OEM platform revision DSM call exists -+ exists = acpi_check_dsm(handle, &MSHW0040_DSM_UUID, -+ MSHW0040_DSM_REVISION, -+ BIT(MSHW0040_DSM_GET_OMPR)); - -- return 0; -+ return exists ? 0 : -ENODEV; - } - - /* --- -2.35.1 - -From bc80832636dd4fb85653d81cc51bb836aacfabc4 Mon Sep 17 00:00:00 2001 -From: Sachi King -Date: Tue, 5 Oct 2021 00:22:57 +1100 -Subject: [PATCH] platform/surface: surfacepro3_button: don't load on amd - variant - -The AMD variant of the Surface Laptop report 0 for their OEM platform -revision. The Surface devices that require the surfacepro3_button -driver do not have the _DSM that gets the OEM platform revision. If the -method does not exist, load surfacepro3_button. - -Fixes: 64dd243d7356 ("platform/x86: surfacepro3_button: Fix device check") -Co-developed-by: Maximilian Luz - -Signed-off-by: Sachi King -Patchset: surface-button ---- - drivers/platform/surface/surfacepro3_button.c | 30 ++++--------------- - 1 file changed, 6 insertions(+), 24 deletions(-) - -diff --git a/drivers/platform/surface/surfacepro3_button.c b/drivers/platform/surface/surfacepro3_button.c -index 242fb690dcaf..30eea54dbb47 100644 ---- a/drivers/platform/surface/surfacepro3_button.c -+++ b/drivers/platform/surface/surfacepro3_button.c -@@ -149,7 +149,8 @@ static int surface_button_resume(struct device *dev) - /* - * Surface Pro 4 and Surface Book 2 / Surface Pro 2017 use the same device - * ID (MSHW0040) for the power/volume buttons. Make sure this is the right -- * device by checking for the _DSM method and OEM Platform Revision. -+ * device by checking for the _DSM method and OEM Platform Revision DSM -+ * function. - * - * Returns true if the driver should bind to this device, i.e. the device is - * either MSWH0028 (Pro 3) or MSHW0040 on a Pro 4 or Book 1. -@@ -157,30 +158,11 @@ static int surface_button_resume(struct device *dev) - static bool surface_button_check_MSHW0040(struct acpi_device *dev) - { - acpi_handle handle = dev->handle; -- union acpi_object *result; -- u64 oem_platform_rev = 0; // valid revisions are nonzero -- -- // get OEM platform revision -- result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, -- MSHW0040_DSM_REVISION, -- MSHW0040_DSM_GET_OMPR, -- NULL, ACPI_TYPE_INTEGER); -- -- /* -- * If evaluating the _DSM fails, the method is not present. This means -- * that we have either MSHW0028 or MSHW0040 on Pro 4 or Book 1, so we -- * should use this driver. We use revision 0 indicating it is -- * unavailable. -- */ -- -- if (result) { -- oem_platform_rev = result->integer.value; -- ACPI_FREE(result); -- } -- -- dev_dbg(&dev->dev, "OEM Platform Revision %llu\n", oem_platform_rev); - -- return oem_platform_rev == 0; -+ // make sure that OEM platform revision DSM call does not exist -+ return !acpi_check_dsm(handle, &MSHW0040_DSM_UUID, -+ MSHW0040_DSM_REVISION, -+ BIT(MSHW0040_DSM_GET_OMPR)); - } - - --- -2.35.1 - -From 19e6f67c7b640286a1a8d451c9234b41bc7d118c Mon Sep 17 00:00:00 2001 -From: Hans de Goede -Date: Thu, 24 Feb 2022 12:02:40 +0100 -Subject: [PATCH] Input: soc_button_array - add support for Microsoft Surface 3 - (MSHW0028) buttons - -The drivers/platform/surface/surface3_button.c code is alsmost a 1:1 copy -of the soc_button_array code. - -The only big difference is that it binds to an i2c_client rather then to -a platform_device. The cause of this is the ACPI resources for the MSHW0028 -device containing a bogus I2cSerialBusV2 resource which causes the kernel -to instantiate an i2c_client for it instead of a platform_device. - -Add "MSHW0028" to the ignore_serial_bus_ids[] list in drivers/apci/scan.c, -so that a platform_device will be instantiated and add support for -the MSHW0028 HID to soc_button_array. - -This fully replaces surface3_button, which will be removed in a separate -commit (since it binds to the now no longer created i2c_client it no -longer does anyyhing after this commit). - -Note the MSHW0028 id is used by Microsoft to describe the tablet buttons on -both the Surface 3 and the Surface 3 Pro and the actual API/implementation -for the Surface 3 Pro is quite different. The changes in this commit should -not impact the separate surfacepro3_button driver: - -1. Because of the bogus I2cSerialBusV2 resource problem that driver binds - to the acpi_device itself, so instantiating a platform_device instead of - an i2c_client does not matter. - -2. The soc_button_array driver will not bind to the MSHW0028 device on - the Surface 3 Pro, because it has no GPIO resources. - -Signed-off-by: Hans de Goede -Reviewed-by: Maximilian Luz -Patchset: surface-button ---- - drivers/acpi/scan.c | 5 +++++ - drivers/input/misc/soc_button_array.c | 24 +++++++++++++++++++++++- - 2 files changed, 28 insertions(+), 1 deletion(-) - -diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c -index 25d9f04f1995..608e83f07b48 100644 ---- a/drivers/acpi/scan.c -+++ b/drivers/acpi/scan.c -@@ -1708,6 +1708,11 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) - {"BSG2150", }, - {"INT33FE", }, - {"INT3515", }, -+ /* -+ * Some ACPI devs contain SerialBus resources even though they are not -+ * attached to a serial bus at all. -+ */ -+ {"MSHW0028", }, - /* - * HIDs of device with an UartSerialBusV2 resource for which userspace - * expects a regular tty cdev to be created (instead of the in kernel -diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c -index 4e8944f59def..f044c731c6a9 100644 ---- a/drivers/input/misc/soc_button_array.c -+++ b/drivers/input/misc/soc_button_array.c -@@ -469,6 +469,27 @@ static const struct soc_device_data soc_device_INT33D3 = { - .button_info = soc_button_INT33D3, - }; - -+/* -+ * Button info for Microsoft Surface 3 (non pro), this is indentical to -+ * the PNP0C40 info except that the home button is active-high. -+ * -+ * The Surface 3 Pro also has a MSHW0028 ACPI device, but that uses a custom -+ * version of the drivers/platform/x86/intel/hid.c 5 button array ACPI API -+ * instead. A check() callback is not necessary though as the Surface 3 Pro -+ * MSHW0028 ACPI device's resource table does not contain any GPIOs. -+ */ -+static const struct soc_button_info soc_button_MSHW0028[] = { -+ { "power", 0, EV_KEY, KEY_POWER, false, true, true }, -+ { "home", 1, EV_KEY, KEY_LEFTMETA, false, true, false }, -+ { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false, true }, -+ { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false, true }, -+ { } -+}; -+ -+static const struct soc_device_data soc_device_MSHW0028 = { -+ .button_info = soc_button_MSHW0028, -+}; -+ - /* - * Special device check for Surface Book 2 and Surface Pro (2017). - * Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned -@@ -518,7 +539,8 @@ static const struct acpi_device_id soc_button_acpi_match[] = { - { "ID9001", (unsigned long)&soc_device_INT33D3 }, - { "ACPI0011", 0 }, - -- /* Microsoft Surface Devices (5th and 6th generation) */ -+ /* Microsoft Surface Devices (3th, 5th and 6th generation) */ -+ { "MSHW0028", (unsigned long)&soc_device_MSHW0028 }, - { "MSHW0040", (unsigned long)&soc_device_MSHW0040 }, - - { } --- -2.35.1 - -From 0e894d02855d203d67b600a1c23eceee939f43f7 Mon Sep 17 00:00:00 2001 -From: Hans de Goede -Date: Thu, 24 Feb 2022 12:02:41 +0100 -Subject: [PATCH] platform/surface: Remove Surface 3 Button driver - -The Surface 3 buttons are now handled by the generic soc_button_array -driver. As part of adding support to soc_button_array the ACPI code -now instantiates a platform_device rather then an i2c_client so there -no longer is an i2c_client for this driver to bind to. - -Signed-off-by: Hans de Goede -Reviewed-by: Maximilian Luz -Patchset: surface-button ---- - drivers/platform/surface/Kconfig | 6 - - drivers/platform/surface/Makefile | 1 - - drivers/platform/surface/surface3_button.c | 247 --------------------- - 3 files changed, 254 deletions(-) - delete mode 100644 drivers/platform/surface/surface3_button.c - -diff --git a/drivers/platform/surface/Kconfig b/drivers/platform/surface/Kconfig -index e5eedb85d471..d13cf8d63cc3 100644 ---- a/drivers/platform/surface/Kconfig -+++ b/drivers/platform/surface/Kconfig -@@ -28,12 +28,6 @@ config SURFACE3_WMI - To compile this driver as a module, choose M here: the module will - be called surface3-wmi. - --config SURFACE_3_BUTTON -- tristate "Power/home/volume buttons driver for Microsoft Surface 3 tablet" -- depends on KEYBOARD_GPIO && I2C -- help -- This driver handles the power/home/volume buttons on the Microsoft Surface 3 tablet. -- - config SURFACE_3_POWER_OPREGION - tristate "Surface 3 battery platform operation region support" - depends on I2C -diff --git a/drivers/platform/surface/Makefile b/drivers/platform/surface/Makefile -index 9eb3a7e6382c..e4791b47f561 100644 ---- a/drivers/platform/surface/Makefile -+++ b/drivers/platform/surface/Makefile -@@ -5,7 +5,6 @@ - # - - obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o --obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o - obj-$(CONFIG_SURFACE_3_POWER_OPREGION) += surface3_power.o - obj-$(CONFIG_SURFACE_ACPI_NOTIFY) += surface_acpi_notify.o - obj-$(CONFIG_SURFACE_AGGREGATOR) += aggregator/ -diff --git a/drivers/platform/surface/surface3_button.c b/drivers/platform/surface/surface3_button.c -deleted file mode 100644 -index 48d77e7aae76..000000000000 ---- a/drivers/platform/surface/surface3_button.c -+++ /dev/null -@@ -1,247 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Supports for the button array on the Surface tablets. -- * -- * (C) Copyright 2016 Red Hat, Inc -- * -- * Based on soc_button_array.c: -- * -- * {C} Copyright 2014 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- -- --#define SURFACE_BUTTON_OBJ_NAME "TEV2" --#define MAX_NBUTTONS 4 -- --/* -- * Some of the buttons like volume up/down are auto repeat, while others -- * are not. To support both, we register two platform devices, and put -- * buttons into them based on whether the key should be auto repeat. -- */ --#define BUTTON_TYPES 2 -- --/* -- * Power button, Home button, Volume buttons support is supposed to -- * be covered by drivers/input/misc/soc_button_array.c, which is implemented -- * according to "Windows ACPI Design Guide for SoC Platforms". -- * However surface 3 seems not to obey the specs, instead it uses -- * device TEV2(MSHW0028) for declaring the GPIOs. The gpios are also slightly -- * different in which the Home button is active high. -- * Compared to surfacepro3_button.c which also handles MSHW0028, the Surface 3 -- * is a reduce platform and thus uses GPIOs, not ACPI events. -- * We choose an I2C driver here because we need to access the resources -- * declared under the device node, while surfacepro3_button.c only needs -- * the ACPI companion node. -- */ --static const struct acpi_device_id surface3_acpi_match[] = { -- { "MSHW0028", 0 }, -- { } --}; --MODULE_DEVICE_TABLE(acpi, surface3_acpi_match); -- --struct surface3_button_info { -- const char *name; -- int acpi_index; -- unsigned int event_type; -- unsigned int event_code; -- bool autorepeat; -- bool wakeup; -- bool active_low; --}; -- --struct surface3_button_data { -- struct platform_device *children[BUTTON_TYPES]; --}; -- --/* -- * Get the Nth GPIO number from the ACPI object. -- */ --static int surface3_button_lookup_gpio(struct device *dev, int acpi_index) --{ -- struct gpio_desc *desc; -- int gpio; -- -- desc = gpiod_get_index(dev, NULL, acpi_index, GPIOD_ASIS); -- if (IS_ERR(desc)) -- return PTR_ERR(desc); -- -- gpio = desc_to_gpio(desc); -- -- gpiod_put(desc); -- -- return gpio; --} -- --static struct platform_device * --surface3_button_device_create(struct i2c_client *client, -- const struct surface3_button_info *button_info, -- bool autorepeat) --{ -- const struct surface3_button_info *info; -- struct platform_device *pd; -- struct gpio_keys_button *gpio_keys; -- struct gpio_keys_platform_data *gpio_keys_pdata; -- int n_buttons = 0; -- int gpio; -- int error; -- -- gpio_keys_pdata = devm_kzalloc(&client->dev, -- sizeof(*gpio_keys_pdata) + -- sizeof(*gpio_keys) * MAX_NBUTTONS, -- GFP_KERNEL); -- if (!gpio_keys_pdata) -- return ERR_PTR(-ENOMEM); -- -- gpio_keys = (void *)(gpio_keys_pdata + 1); -- -- for (info = button_info; info->name; info++) { -- if (info->autorepeat != autorepeat) -- continue; -- -- gpio = surface3_button_lookup_gpio(&client->dev, -- info->acpi_index); -- if (!gpio_is_valid(gpio)) -- continue; -- -- gpio_keys[n_buttons].type = info->event_type; -- gpio_keys[n_buttons].code = info->event_code; -- gpio_keys[n_buttons].gpio = gpio; -- gpio_keys[n_buttons].active_low = info->active_low; -- gpio_keys[n_buttons].desc = info->name; -- gpio_keys[n_buttons].wakeup = info->wakeup; -- n_buttons++; -- } -- -- if (n_buttons == 0) { -- error = -ENODEV; -- goto err_free_mem; -- } -- -- gpio_keys_pdata->buttons = gpio_keys; -- gpio_keys_pdata->nbuttons = n_buttons; -- gpio_keys_pdata->rep = autorepeat; -- -- pd = platform_device_alloc("gpio-keys", PLATFORM_DEVID_AUTO); -- if (!pd) { -- error = -ENOMEM; -- goto err_free_mem; -- } -- -- error = platform_device_add_data(pd, gpio_keys_pdata, -- sizeof(*gpio_keys_pdata)); -- if (error) -- goto err_free_pdev; -- -- error = platform_device_add(pd); -- if (error) -- goto err_free_pdev; -- -- return pd; -- --err_free_pdev: -- platform_device_put(pd); --err_free_mem: -- devm_kfree(&client->dev, gpio_keys_pdata); -- return ERR_PTR(error); --} -- --static int surface3_button_remove(struct i2c_client *client) --{ -- struct surface3_button_data *priv = i2c_get_clientdata(client); -- -- int i; -- -- for (i = 0; i < BUTTON_TYPES; i++) -- if (priv->children[i]) -- platform_device_unregister(priv->children[i]); -- -- return 0; --} -- --static struct surface3_button_info surface3_button_surface3[] = { -- { "power", 0, EV_KEY, KEY_POWER, false, true, true }, -- { "home", 1, EV_KEY, KEY_LEFTMETA, false, true, false }, -- { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false, true }, -- { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false, true }, -- { } --}; -- --static int surface3_button_probe(struct i2c_client *client, -- const struct i2c_device_id *id) --{ -- struct device *dev = &client->dev; -- struct surface3_button_data *priv; -- struct platform_device *pd; -- int i; -- int error; -- -- if (strncmp(acpi_device_bid(ACPI_COMPANION(&client->dev)), -- SURFACE_BUTTON_OBJ_NAME, -- strlen(SURFACE_BUTTON_OBJ_NAME))) -- return -ENODEV; -- -- error = gpiod_count(dev, NULL); -- if (error < 0) { -- dev_dbg(dev, "no GPIO attached, ignoring...\n"); -- return error; -- } -- -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- -- i2c_set_clientdata(client, priv); -- -- for (i = 0; i < BUTTON_TYPES; i++) { -- pd = surface3_button_device_create(client, -- surface3_button_surface3, -- i == 0); -- if (IS_ERR(pd)) { -- error = PTR_ERR(pd); -- if (error != -ENODEV) { -- surface3_button_remove(client); -- return error; -- } -- continue; -- } -- -- priv->children[i] = pd; -- } -- -- if (!priv->children[0] && !priv->children[1]) -- return -ENODEV; -- -- return 0; --} -- --static const struct i2c_device_id surface3_id[] = { -- { } --}; --MODULE_DEVICE_TABLE(i2c, surface3_id); -- --static struct i2c_driver surface3_driver = { -- .probe = surface3_button_probe, -- .remove = surface3_button_remove, -- .id_table = surface3_id, -- .driver = { -- .name = "surface3", -- .acpi_match_table = ACPI_PTR(surface3_acpi_match), -- }, --}; --module_i2c_driver(surface3_driver); -- --MODULE_AUTHOR("Benjamin Tissoires "); --MODULE_DESCRIPTION("surface3 button array driver"); --MODULE_LICENSE("GPL v2"); --- -2.35.1 - diff --git a/patches/5.16/0009-surface-typecover.patch b/patches/5.16/0009-surface-typecover.patch index b09a907fb..8c6840114 100644 --- a/patches/5.16/0009-surface-typecover.patch +++ b/patches/5.16/0009-surface-typecover.patch @@ -1,4 +1,4 @@ -From 925b2b083ff92e4daeca8409d485dd90d339441c Mon Sep 17 00:00:00 2001 +From 6810c61094611fdb7fabfdc62ac51f207a923de9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Thu, 5 Nov 2020 13:09:45 +0100 Subject: [PATCH] hid/multitouch: Turn off Type Cover keyboard backlight when diff --git a/patches/5.16/0010-cameras.patch b/patches/5.16/0010-cameras.patch index 290192887..98c39cb72 100644 --- a/patches/5.16/0010-cameras.patch +++ b/patches/5.16/0010-cameras.patch @@ -1,4 +1,4 @@ -From 9fda64c569dc223a8814adb6db26706f365d0e6a Mon Sep 17 00:00:00 2001 +From 2d814309698aee8e7be3f75982e01c20f8e0910e Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Mon, 5 Apr 2021 23:56:53 +0100 Subject: [PATCH] media: ipu3-cio2: Toggle sensor streaming in pm runtime ops @@ -58,7 +58,7 @@ index 356ea966cf8d..76fd4e6e8e46 100644 -- 2.35.1 -From 7ebe579479c936f06369a39b90e4a733fb69cfc0 Mon Sep 17 00:00:00 2001 +From fd895042d767f90f5eb5883f216cadb93c782771 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Mon, 5 Apr 2021 23:56:54 +0100 Subject: [PATCH] media: i2c: Add support for ov5693 sensor @@ -1701,7 +1701,7 @@ index 000000000000..9499ee10f56c -- 2.35.1 -From acb977eddae163c3d7ade1a88275f48b471f1200 Mon Sep 17 00:00:00 2001 +From 2dfb5edab4b1eb1266634e5c936fcbc45b82118a Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 20 May 2021 23:31:04 +0100 Subject: [PATCH] media: i2c: Fix vertical flip in ov5693 @@ -1736,7 +1736,7 @@ index 9499ee10f56c..c558f9b48c83 100644 -- 2.35.1 -From 263137c04cc7e96eb73e622f707933b47e5f299d Mon Sep 17 00:00:00 2001 +From bcd317183156badc42bdb64701c35217cde13a79 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Fri, 9 Jul 2021 16:39:18 +0100 Subject: [PATCH] media: i2c: Add ACPI support to ov8865 @@ -1787,7 +1787,7 @@ index 92f6c3a940cf..15325df45c2b 100644 -- 2.35.1 -From 67ed10d2f2767ad475f51713e600750eece90956 Mon Sep 17 00:00:00 2001 +From 6e0c4749bc35cfe6519d2343c900ccccaea803dd Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Sat, 10 Jul 2021 21:20:17 +0100 Subject: [PATCH] media: i2c: Fix incorrect value in comment @@ -1818,7 +1818,7 @@ index 15325df45c2b..8dcdf29be25e 100644 -- 2.35.1 -From 3cfa646a7dbc0c5e4da59c61c0b56b801a7ec07a Mon Sep 17 00:00:00 2001 +From 223d62642bfdc447a0551d3959c98912263d4dca Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Sat, 10 Jul 2021 22:21:52 +0100 Subject: [PATCH] media: i2c: Defer probe if not endpoint found @@ -1855,7 +1855,7 @@ index 8dcdf29be25e..ceb9a93b043a 100644 -- 2.35.1 -From 7a0c0f9adb345d73e05e88b86460641632e0d96c Mon Sep 17 00:00:00 2001 +From e4f13264f7e7c91ef0baf87d3be44a244858e7be Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Sat, 10 Jul 2021 22:00:25 +0100 Subject: [PATCH] media: i2c: Support 19.2MHz input clock in ov8865 @@ -2214,7 +2214,7 @@ index ceb9a93b043a..9bac32efa7fa 100644 -- 2.35.1 -From 3c415ec8efc3261008a5d13c493f9a2c4956f9d5 Mon Sep 17 00:00:00 2001 +From ec4fb28d4b75ed9cde1a7a13671d807797ed3b29 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Sat, 10 Jul 2021 22:19:10 +0100 Subject: [PATCH] media: i2c: Add .get_selection() support to ov8865 @@ -2319,7 +2319,7 @@ index 9bac32efa7fa..d41ce6b5af55 100644 -- 2.35.1 -From 528d16e6658ec372a928cf555ca44601e072a8d2 Mon Sep 17 00:00:00 2001 +From 62c2d0b223798ec53f80a7311ffabaa3564491bf Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Sat, 10 Jul 2021 22:34:43 +0100 Subject: [PATCH] media: i2c: Switch control to V4L2_CID_ANALOGUE_GAIN @@ -2372,7 +2372,7 @@ index d41ce6b5af55..07f34f3ae5ec 100644 -- 2.35.1 -From a0527359a1643412a6e52ac3dd7da68512d004b1 Mon Sep 17 00:00:00 2001 +From e677005e1d45907454dfdd12b2ec2fa8160f92ab Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Mon, 12 Jul 2021 22:54:56 +0100 Subject: [PATCH] media: i2c: Add vblank control to ov8865 @@ -2482,7 +2482,7 @@ index 07f34f3ae5ec..95c1b97eb89a 100644 -- 2.35.1 -From 9dd5617c04fceb8e3a5e80fbb82319ff192fa427 Mon Sep 17 00:00:00 2001 +From e03db43d2992c707235179319ba3adfe3da4441e Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Tue, 13 Jul 2021 23:40:33 +0100 Subject: [PATCH] media: i2c: Add hblank control to ov8865 @@ -2552,7 +2552,7 @@ index 95c1b97eb89a..85a76aea67a5 100644 -- 2.35.1 -From 88e5ad92669570378e12c16381c0cff6dc6cc099 Mon Sep 17 00:00:00 2001 +From b103821c396fd02ddbec7f58ca6af55a65725883 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Wed, 20 Oct 2021 22:43:54 +0100 Subject: [PATCH] media: i2c: Update HTS values in ov8865 @@ -2728,7 +2728,7 @@ index 85a76aea67a5..7f5b0c48eac4 100644 -- 2.35.1 -From 1bb106d52f3ee86f28ada81146309f571cc1cb81 Mon Sep 17 00:00:00 2001 +From cafb393c61f5ce95318c4c87e042f80b25f0d21d Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Tue, 13 Jul 2021 23:43:17 +0100 Subject: [PATCH] media: i2c: cap exposure at height + vblank in ov8865 @@ -2811,7 +2811,7 @@ index 7f5b0c48eac4..d867676bf77e 100644 -- 2.35.1 -From 384f0675a022c81cd1fc1bdf3123b3d42198c041 Mon Sep 17 00:00:00 2001 +From 5039c9f43f0c57244e178fe8ba81db898c7e3640 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Fri, 16 Jul 2021 22:56:15 +0100 Subject: [PATCH] media: i2c: Add controls from fwnode to ov8865 @@ -2856,7 +2856,7 @@ index d867676bf77e..d0303016e7b4 100644 -- 2.35.1 -From cb171e4ec467539921fe6f39f0bf8e556f23d2eb Mon Sep 17 00:00:00 2001 +From 1c96c757338cfa78250989826e32ba622aeb3848 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Fri, 16 Jul 2021 00:00:54 +0100 Subject: [PATCH] media: i2c: Switch exposure control unit to lines @@ -2903,7 +2903,7 @@ index d0303016e7b4..a638e53bb069 100644 -- 2.35.1 -From e52641c9df9a53209060750d900e25fff9908ae5 Mon Sep 17 00:00:00 2001 +From c8f335a4e68e5d2197f3aee325fea582dc3d39b4 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Tue, 24 Aug 2021 23:17:39 +0100 Subject: [PATCH] media: i2c: Use dev_err_probe() in ov8865 @@ -2986,7 +2986,7 @@ index a638e53bb069..5ef9c407362a 100644 -- 2.35.1 -From 4d108ed0252bedba3d5191795f71fc6e6dbb36fc Mon Sep 17 00:00:00 2001 +From d6ac836da9c5181634c9d7f317d199cd81ccc085 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Wed, 14 Jul 2021 00:05:04 +0100 Subject: [PATCH] media: ipu3-cio2: Add INT347A to cio2-bridge @@ -3019,7 +3019,7 @@ index 0b586b4e537e..4550be801311 100644 -- 2.35.1 -From 12a19e2ca3b6b7b051aef0df0705868e5f8ba880 Mon Sep 17 00:00:00 2001 +From dd52bc8c51c21d44afdd51239e6fba04bae259be Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 10 Oct 2021 20:56:57 +0200 Subject: [PATCH] ACPI: delay enumeration of devices with a _DEP pointing to an @@ -3202,7 +3202,7 @@ index d6fe27b695c3..5895f6c7f6db 100644 -- 2.35.1 -From c7cf26d31ba9c379ab52b76a4c0b99f378149282 Mon Sep 17 00:00:00 2001 +From 75b7492855a6bde63125f8d01804672d34e2257d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 10 Oct 2021 20:56:58 +0200 Subject: [PATCH] i2c: acpi: Use acpi_dev_ready_for_enumeration() helper @@ -3254,7 +3254,7 @@ index 3b688cea8e00..0542d8aba902 100644 -- 2.35.1 -From b79236b933854bb2237a505137ad3cd50b957861 Mon Sep 17 00:00:00 2001 +From 7337d1dae4ab8cf07f72960ab69f4cc0baa877f4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 10 Oct 2021 20:56:59 +0200 Subject: [PATCH] platform_data: Add linux/platform_data/tps68470.h file @@ -3327,7 +3327,7 @@ index 000000000000..126d082c3f2e -- 2.35.1 -From 15789a7cdd7cb42fc0e468a388739bca4bc6f261 Mon Sep 17 00:00:00 2001 +From afe75b849e2d0bdd23fc9b0cabd1ce0a46dc3d5e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 10 Oct 2021 20:57:00 +0200 Subject: [PATCH] regulator: Introduce tps68470-regulator driver @@ -3589,7 +3589,7 @@ index 000000000000..3129fa13a122 -- 2.35.1 -From 1a387763822f2fa36a65b5711a8f5eed2e8a316a Mon Sep 17 00:00:00 2001 +From f2f07e994f692af710805ef8a53590b371288b8b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 10 Oct 2021 20:57:01 +0200 Subject: [PATCH] clk: Introduce clk-tps68470 driver @@ -3934,7 +3934,7 @@ index ffe81127d91c..7807fa329db0 100644 -- 2.35.1 -From 0439a9d0261fcb40aaae89cff9c9bc15833a9356 Mon Sep 17 00:00:00 2001 +From bf27d7b319215a0e17269ccdc42464a2be1afd7a Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Sun, 10 Oct 2021 20:57:02 +0200 Subject: [PATCH] platform/x86: int3472: Enable I2c daisy chain @@ -3971,7 +3971,7 @@ index c05b4cf502fe..42e688f4cad4 100644 -- 2.35.1 -From e8d35a1f3f79d8accbd5b59a8d1af9c2fcb9d7bf Mon Sep 17 00:00:00 2001 +From 6ea9961efd3c29d366a7608f27c84bbd4b072a67 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 10 Oct 2021 20:57:03 +0200 Subject: [PATCH] platform/x86: int3472: Split into 2 drivers @@ -4230,7 +4230,7 @@ index 42e688f4cad4..b94cf66ab61f 100644 -- 2.35.1 -From 75d9f3eb4507722d65bc650a5a91e239c2f5c855 Mon Sep 17 00:00:00 2001 +From b242c3b30351ab16ff6bead5718c046fa4543d71 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 10 Oct 2021 20:57:04 +0200 Subject: [PATCH] platform/x86: int3472: Add get_sensor_adev_and_name() helper @@ -4343,7 +4343,7 @@ index a19a1f5dbdd7..efd31a0c7a88 100644 -- 2.35.1 -From a5581c696485f4f173be9441467683071e253992 Mon Sep 17 00:00:00 2001 +From ce6d00f1afc889c5cd0b93e8d7c4c26568ed5ade Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 10 Oct 2021 20:57:05 +0200 Subject: [PATCH] platform/x86: int3472: Pass tps68470_clk_platform_data to the @@ -4431,7 +4431,7 @@ index b94cf66ab61f..78e34e7b6969 100644 -- 2.35.1 -From 330d40c102dbae6c69d5a2c8fe43c82ad64ab2ea Mon Sep 17 00:00:00 2001 +From 16cd889346121483b60ecde95f20bc2b1fe1eb19 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 10 Oct 2021 20:57:06 +0200 Subject: [PATCH] platform/x86: int3472: Pass tps68470_regulator_platform_data @@ -4725,7 +4725,7 @@ index 000000000000..96954a789bb8 -- 2.35.1 -From 8819eb3821383ca94ed437482b4a0a8948059aac Mon Sep 17 00:00:00 2001 +From ebdde93fb0053e945e6fe053a14b150d299c2500 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 10 Oct 2021 20:57:07 +0200 Subject: [PATCH] platform/x86: int3472: Deal with probe ordering issues @@ -4809,7 +4809,7 @@ index aae24d228770..21c6c1a6edfc 100644 -- 2.35.1 -From f350fb49c2c161b84749de1616bc5f9920498fab Mon Sep 17 00:00:00 2001 +From 12c17f7f09169af045cef3b15728c98403336c6c Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 4 Nov 2021 21:46:27 +0000 Subject: [PATCH] media: i2c: Add integration time margin to ov8865 @@ -4851,7 +4851,7 @@ index 5ef9c407362a..ed038efbc084 100644 -- 2.35.1 -From c4f4399853e59f5a45a966a5050d3e0b7fb3ca59 Mon Sep 17 00:00:00 2001 +From c7b51fa424ff2bf8398c412154017066e63fd909 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 4 Nov 2021 21:48:38 +0000 Subject: [PATCH] media: i2c: Fix max gain in ov8865 @@ -4883,7 +4883,7 @@ index ed038efbc084..5bedcddafe36 100644 -- 2.35.1 -From 6cdb87654ac0799d48fb898c8cb3c2b0588eb7db Mon Sep 17 00:00:00 2001 +From 6812a707a23ec35749f529e15eae3ff5a32bd0af Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 3 Dec 2021 12:51:08 +0100 Subject: [PATCH] mfd: intel-lpss: Fix I2C4 not being available on the @@ -4988,7 +4988,7 @@ index 22dbc4aed793..062ce95b68b9 100644 -- 2.35.1 -From 04c3a5157515014827e20d1afa69919a7323f9fd Mon Sep 17 00:00:00 2001 +From ffbd58376161c9353d29e8af0438114e37bd9e57 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 6 Jan 2022 22:12:38 +0000 Subject: [PATCH] platform/x86: int3472: Add board data for Surface Go 3 diff --git a/patches/5.16/0010-surface-typecover.patch b/patches/5.16/0010-surface-typecover.patch deleted file mode 100644 index 8c6840114..000000000 --- a/patches/5.16/0010-surface-typecover.patch +++ /dev/null @@ -1,233 +0,0 @@ -From 6810c61094611fdb7fabfdc62ac51f207a923de9 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= -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 082376a6cb3d..cfc2e684a22c 100644 ---- a/drivers/hid/hid-multitouch.c -+++ b/drivers/hid/hid-multitouch.c -@@ -34,7 +34,10 @@ - #include - #include - #include -+#include - #include -+#include -+#include - #include - #include - #include -@@ -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) -@@ -71,12 +75,15 @@ MODULE_LICENSE("GPL"); - #define MT_QUIRK_SEPARATE_APP_REPORT BIT(19) - #define MT_QUIRK_FORCE_MULTI_INPUT BIT(20) - #define MT_QUIRK_DISABLE_WAKEUP BIT(21) -+#define MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT BIT(22) - - #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, -@@ -168,6 +175,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, -@@ -211,6 +220,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 -@@ -386,6 +396,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 -+ }, - { } - }; - -@@ -1698,6 +1718,69 @@ static void mt_expired_timeout(struct timer_list *t) - clear_bit(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; -@@ -1721,6 +1804,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); - -@@ -1750,15 +1836,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) -@@ -1810,6 +1900,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); -@@ -2177,6 +2268,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.35.1 - diff --git a/patches/5.16/0011-amd-gpio.patch b/patches/5.16/0011-amd-gpio.patch index 2a810068f..d731ae742 100644 --- a/patches/5.16/0011-amd-gpio.patch +++ b/patches/5.16/0011-amd-gpio.patch @@ -1,4 +1,4 @@ -From dd56f00c6ba9515f9923a1af83792d1c1cfac309 Mon Sep 17 00:00:00 2001 +From 5f73c0ef09d059062b1596eb22b9f8ba6625f3c5 Mon Sep 17 00:00:00 2001 From: Sachi King Date: Sat, 29 May 2021 17:47:38 +1000 Subject: [PATCH] ACPI: Add quirk for Surface Laptop 4 AMD missing irq 7 @@ -65,7 +65,7 @@ index 5b6d1a95776f..0a05e196419a 100644 -- 2.35.1 -From 608ab2165b79e0c02fc30f4d691da8cd4da8ad8c Mon Sep 17 00:00:00 2001 +From 1d96852a8adf322ce139bbf7e850a8f076d88dd6 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Thu, 3 Jun 2021 14:04:26 +0200 Subject: [PATCH] ACPI: Add AMD 13" Surface Laptop 4 model to irq 7 override diff --git a/patches/5.16/0011-cameras.patch b/patches/5.16/0011-cameras.patch deleted file mode 100644 index 98c39cb72..000000000 --- a/patches/5.16/0011-cameras.patch +++ /dev/null @@ -1,5039 +0,0 @@ -From 2d814309698aee8e7be3f75982e01c20f8e0910e Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Mon, 5 Apr 2021 23:56:53 +0100 -Subject: [PATCH] media: ipu3-cio2: Toggle sensor streaming in pm runtime ops - -The .suspend() and .resume() runtime_pm operations for the ipu3-cio2 -driver currently do not handle the sensor's stream. Setting .s_stream() on -or off for the sensor subdev means that sensors will pause and resume the -stream at the appropriate time even if their drivers don't implement those -operations. - -Signed-off-by: Daniel Scally -Patchset: cameras ---- - drivers/media/pci/intel/ipu3/ipu3-cio2-main.c | 15 ++++++++++++++- - 1 file changed, 14 insertions(+), 1 deletion(-) - -diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c -index 356ea966cf8d..76fd4e6e8e46 100644 ---- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c -+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c -@@ -1966,12 +1966,19 @@ static int __maybe_unused cio2_suspend(struct device *dev) - struct pci_dev *pci_dev = to_pci_dev(dev); - struct cio2_device *cio2 = pci_get_drvdata(pci_dev); - struct cio2_queue *q = cio2->cur_queue; -+ int r; - - dev_dbg(dev, "cio2 suspend\n"); - if (!cio2->streaming) - return 0; - - /* Stop stream */ -+ r = v4l2_subdev_call(q->sensor, video, s_stream, 0); -+ if (r) { -+ dev_err(dev, "failed to stop sensor streaming\n"); -+ return r; -+ } -+ - cio2_hw_exit(cio2, q); - synchronize_irq(pci_dev->irq); - -@@ -2005,8 +2012,14 @@ static int __maybe_unused cio2_resume(struct device *dev) - } - - r = cio2_hw_init(cio2, q); -- if (r) -+ if (r) { - dev_err(dev, "fail to init cio2 hw\n"); -+ return r; -+ } -+ -+ r = v4l2_subdev_call(q->sensor, video, s_stream, 1); -+ if (r) -+ dev_err(dev, "fail to start sensor streaming\n"); - - return r; - } --- -2.35.1 - -From fd895042d767f90f5eb5883f216cadb93c782771 Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Mon, 5 Apr 2021 23:56:54 +0100 -Subject: [PATCH] media: i2c: Add support for ov5693 sensor - -The OV5693 is a 5 Mpx CMOS image sensor, connected via MIPI CSI-2. The -chip is capable of a single lane configuration, but currently only two -lanes are supported. - -Most of the sensor's features are supported, with the main exception -being the lens correction algorithm. - -The driver provides all mandatory, optional and recommended V4L2 controls -for maximum compatibility with libcamera. - -Signed-off-by: Daniel Scally -Patchset: cameras ---- - MAINTAINERS | 7 + - drivers/media/i2c/Kconfig | 11 + - drivers/media/i2c/Makefile | 1 + - drivers/media/i2c/ov5693.c | 1557 ++++++++++++++++++++++++++++++++++++ - 4 files changed, 1576 insertions(+) - create mode 100644 drivers/media/i2c/ov5693.c - -diff --git a/MAINTAINERS b/MAINTAINERS -index d69fdf5aadec..2eeffdd634c2 100644 ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -14117,6 +14117,13 @@ S: Maintained - T: git git://linuxtv.org/media_tree.git - F: drivers/media/i2c/ov5675.c - -+OMNIVISION OV5693 SENSOR DRIVER -+M: Daniel Scally -+L: linux-media@vger.kernel.org -+S: Maintained -+T: git git://linuxtv.org/media_tree.git -+F: drivers/media/i2c/ov5693.c -+ - OMNIVISION OV5695 SENSOR DRIVER - M: Shunqian Zheng - L: linux-media@vger.kernel.org -diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig -index d6a5d4ca439a..8761a90a7a86 100644 ---- a/drivers/media/i2c/Kconfig -+++ b/drivers/media/i2c/Kconfig -@@ -1058,6 +1058,17 @@ config VIDEO_OV5675 - To compile this driver as a module, choose M here: the - module will be called ov5675. - -+config VIDEO_OV5693 -+ tristate "OmniVision OV5693 sensor support" -+ depends on I2C && VIDEO_V4L2 -+ select V4L2_FWNODE -+ help -+ This is a Video4Linux2 sensor driver for the OmniVision -+ OV5693 camera. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called ov5693. -+ - config VIDEO_OV5695 - tristate "OmniVision OV5695 sensor support" - depends on I2C && VIDEO_V4L2 -diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile -index 4d4fe08d7a6a..b01f6cd05ee8 100644 ---- a/drivers/media/i2c/Makefile -+++ b/drivers/media/i2c/Makefile -@@ -75,6 +75,7 @@ obj-$(CONFIG_VIDEO_OV5647) += ov5647.o - obj-$(CONFIG_VIDEO_OV5648) += ov5648.o - obj-$(CONFIG_VIDEO_OV5670) += ov5670.o - obj-$(CONFIG_VIDEO_OV5675) += ov5675.o -+obj-$(CONFIG_VIDEO_OV5693) += ov5693.o - obj-$(CONFIG_VIDEO_OV5695) += ov5695.o - obj-$(CONFIG_VIDEO_OV6650) += ov6650.o - obj-$(CONFIG_VIDEO_OV7251) += ov7251.o -diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c -new file mode 100644 -index 000000000000..9499ee10f56c ---- /dev/null -+++ b/drivers/media/i2c/ov5693.c -@@ -0,0 +1,1557 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (c) 2013 Intel Corporation. All Rights Reserved. -+ * -+ * Adapted from the atomisp-ov5693 driver, with contributions from: -+ * -+ * Daniel Scally -+ * Jean-Michel Hautbois -+ * Fabian Wuthrich -+ * Tsuchiya Yuto -+ * Jordan Hand -+ * Jake Day -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* System Control */ -+#define OV5693_SW_RESET_REG 0x0103 -+#define OV5693_SW_STREAM_REG 0x0100 -+#define OV5693_START_STREAMING 0x01 -+#define OV5693_STOP_STREAMING 0x00 -+#define OV5693_SW_RESET 0x01 -+ -+#define OV5693_REG_CHIP_ID_H 0x300a -+#define OV5693_REG_CHIP_ID_L 0x300b -+/* Yes, this is right. The datasheet for the OV5693 gives its ID as 0x5690 */ -+#define OV5693_CHIP_ID 0x5690 -+ -+/* Exposure */ -+#define OV5693_EXPOSURE_L_CTRL_HH_REG 0x3500 -+#define OV5693_EXPOSURE_L_CTRL_H_REG 0x3501 -+#define OV5693_EXPOSURE_L_CTRL_L_REG 0x3502 -+#define OV5693_EXPOSURE_CTRL_HH(v) (((v) & GENMASK(14, 12)) >> 12) -+#define OV5693_EXPOSURE_CTRL_H(v) (((v) & GENMASK(11, 4)) >> 4) -+#define OV5693_EXPOSURE_CTRL_L(v) (((v) & GENMASK(3, 0)) << 4) -+#define OV5693_INTEGRATION_TIME_MARGIN 8 -+#define OV5693_EXPOSURE_MIN 1 -+#define OV5693_EXPOSURE_STEP 1 -+ -+/* Analogue Gain */ -+#define OV5693_GAIN_CTRL_H_REG 0x350a -+#define OV5693_GAIN_CTRL_H(v) (((v) >> 4) & GENMASK(2, 0)) -+#define OV5693_GAIN_CTRL_L_REG 0x350b -+#define OV5693_GAIN_CTRL_L(v) (((v) << 4) & GENMASK(7, 4)) -+#define OV5693_GAIN_MIN 1 -+#define OV5693_GAIN_MAX 127 -+#define OV5693_GAIN_DEF 8 -+#define OV5693_GAIN_STEP 1 -+ -+/* Digital Gain */ -+#define OV5693_MWB_RED_GAIN_H_REG 0x3400 -+#define OV5693_MWB_RED_GAIN_L_REG 0x3401 -+#define OV5693_MWB_GREEN_GAIN_H_REG 0x3402 -+#define OV5693_MWB_GREEN_GAIN_L_REG 0x3403 -+#define OV5693_MWB_BLUE_GAIN_H_REG 0x3404 -+#define OV5693_MWB_BLUE_GAIN_L_REG 0x3405 -+#define OV5693_MWB_GAIN_H_CTRL(v) (((v) >> 8) & GENMASK(3, 0)) -+#define OV5693_MWB_GAIN_L_CTRL(v) ((v) & GENMASK(7, 0)) -+#define OV5693_MWB_GAIN_MAX 0x0fff -+#define OV5693_DIGITAL_GAIN_MIN 1 -+#define OV5693_DIGITAL_GAIN_MAX 4095 -+#define OV5693_DIGITAL_GAIN_DEF 1024 -+#define OV5693_DIGITAL_GAIN_STEP 1 -+ -+/* Timing and Format */ -+#define OV5693_CROP_START_X_H_REG 0x3800 -+#define OV5693_CROP_START_X_H(v) (((v) & GENMASK(12, 8)) >> 8) -+#define OV5693_CROP_START_X_L_REG 0x3801 -+#define OV5693_CROP_START_X_L(v) ((v) & GENMASK(7, 0)) -+ -+#define OV5693_CROP_START_Y_H_REG 0x3802 -+#define OV5693_CROP_START_Y_H(v) (((v) & GENMASK(11, 8)) >> 8) -+#define OV5693_CROP_START_Y_L_REG 0x3803 -+#define OV5693_CROP_START_Y_L(v) ((v) & GENMASK(7, 0)) -+ -+#define OV5693_CROP_END_X_H_REG 0x3804 -+#define OV5693_CROP_END_X_H(v) (((v) & GENMASK(12, 8)) >> 8) -+#define OV5693_CROP_END_X_L_REG 0x3805 -+#define OV5693_CROP_END_X_L(v) ((v) & GENMASK(7, 0)) -+ -+#define OV5693_CROP_END_Y_H_REG 0x3806 -+#define OV5693_CROP_END_Y_H(v) (((v) & GENMASK(11, 8)) >> 8) -+#define OV5693_CROP_END_Y_L_REG 0x3807 -+#define OV5693_CROP_END_Y_L(v) ((v) & GENMASK(7, 0)) -+ -+#define OV5693_OUTPUT_SIZE_X_H_REG 0x3808 -+#define OV5693_OUTPUT_SIZE_X_H(v) (((v) & GENMASK(15, 8)) >> 8) -+#define OV5693_OUTPUT_SIZE_X_L_REG 0x3809 -+#define OV5693_OUTPUT_SIZE_X_L(v) ((v) & GENMASK(7, 0)) -+ -+#define OV5693_OUTPUT_SIZE_Y_H_REG 0x380a -+#define OV5693_OUTPUT_SIZE_Y_H(v) (((v) & GENMASK(15, 8)) >> 8) -+#define OV5693_OUTPUT_SIZE_Y_L_REG 0x380b -+#define OV5693_OUTPUT_SIZE_Y_L(v) ((v) & GENMASK(7, 0)) -+ -+#define OV5693_TIMING_HTS_H_REG 0x380c -+#define OV5693_TIMING_HTS_H(v) (((v) & GENMASK(15, 8)) >> 8) -+#define OV5693_TIMING_HTS_L_REG 0x380d -+#define OV5693_TIMING_HTS_L(v) ((v) & GENMASK(7, 0)) -+#define OV5693_FIXED_PPL 2688U -+ -+#define OV5693_TIMING_VTS_H_REG 0x380e -+#define OV5693_TIMING_VTS_H(v) (((v) & GENMASK(15, 8)) >> 8) -+#define OV5693_TIMING_VTS_L_REG 0x380f -+#define OV5693_TIMING_VTS_L(v) ((v) & GENMASK(7, 0)) -+#define OV5693_TIMING_MAX_VTS 0xffff -+#define OV5693_TIMING_MIN_VTS 0x04 -+ -+#define OV5693_OFFSET_START_X_H_REG 0x3810 -+#define OV5693_OFFSET_START_X_H(v) (((v) & GENMASK(15, 8)) >> 8) -+#define OV5693_OFFSET_START_X_L_REG 0x3811 -+#define OV5693_OFFSET_START_X_L(v) ((v) & GENMASK(7, 0)) -+ -+#define OV5693_OFFSET_START_Y_H_REG 0x3812 -+#define OV5693_OFFSET_START_Y_H(v) (((v) & GENMASK(15, 8)) >> 8) -+#define OV5693_OFFSET_START_Y_L_REG 0x3813 -+#define OV5693_OFFSET_START_Y_L(v) ((v) & GENMASK(7, 0)) -+ -+#define OV5693_SUB_INC_X_REG 0x3814 -+#define OV5693_SUB_INC_Y_REG 0x3815 -+ -+#define OV5693_FORMAT1_REG 0x3820 -+#define OV5693_FORMAT1_FLIP_VERT_ISP_EN BIT(2) -+#define OV5693_FORMAT1_FLIP_VERT_SENSOR_EN BIT(1) -+#define OV5693_FORMAT1_VBIN_EN BIT(0) -+#define OV5693_FORMAT2_REG 0x3821 -+#define OV5693_FORMAT2_HDR_EN BIT(7) -+#define OV5693_FORMAT2_FLIP_HORZ_ISP_EN BIT(2) -+#define OV5693_FORMAT2_FLIP_HORZ_SENSOR_EN BIT(1) -+#define OV5693_FORMAT2_HBIN_EN BIT(0) -+ -+#define OV5693_ISP_CTRL2_REG 0x5002 -+#define OV5693_ISP_SCALE_ENABLE BIT(7) -+ -+/* Pixel Array */ -+#define OV5693_NATIVE_WIDTH 2624 -+#define OV5693_NATIVE_HEIGHT 1956 -+#define OV5693_NATIVE_START_LEFT 0 -+#define OV5693_NATIVE_START_TOP 0 -+#define OV5693_ACTIVE_WIDTH 2592 -+#define OV5693_ACTIVE_HEIGHT 1944 -+#define OV5693_ACTIVE_START_LEFT 16 -+#define OV5693_ACTIVE_START_TOP 6 -+#define OV5693_MIN_CROP_WIDTH 2 -+#define OV5693_MIN_CROP_HEIGHT 2 -+ -+/* Test Pattern */ -+#define OV5693_TEST_PATTERN_REG 0x5e00 -+#define OV5693_TEST_PATTERN_ENABLE BIT(7) -+#define OV5693_TEST_PATTERN_ROLLING BIT(6) -+#define OV5693_TEST_PATTERN_RANDOM 0x01 -+#define OV5693_TEST_PATTERN_BARS 0x00 -+ -+/* System Frequencies */ -+#define OV5693_XVCLK_FREQ 19200000 -+#define OV5693_LINK_FREQ_400MHZ 400000000 -+#define OV5693_PIXEL_RATE 160000000 -+ -+/* Miscellaneous */ -+#define OV5693_NUM_SUPPLIES 2 -+ -+#define to_ov5693_sensor(x) container_of(x, struct ov5693_device, sd) -+ -+struct ov5693_reg { -+ u16 reg; -+ u8 val; -+}; -+ -+struct ov5693_reg_list { -+ u32 num_regs; -+ const struct ov5693_reg *regs; -+}; -+ -+struct ov5693_device { -+ struct i2c_client *client; -+ struct device *dev; -+ -+ /* Protect against concurrent changes to controls */ -+ struct mutex lock; -+ -+ struct gpio_desc *reset; -+ struct gpio_desc *powerdown; -+ struct regulator_bulk_data supplies[OV5693_NUM_SUPPLIES]; -+ struct clk *clk; -+ -+ struct ov5693_mode { -+ struct v4l2_rect crop; -+ struct v4l2_mbus_framefmt format; -+ bool binning_x; -+ bool binning_y; -+ unsigned int inc_x_odd; -+ unsigned int inc_y_odd; -+ unsigned int vts; -+ } mode; -+ bool streaming; -+ -+ struct v4l2_subdev sd; -+ struct media_pad pad; -+ -+ struct ov5693_v4l2_ctrls { -+ struct v4l2_ctrl_handler handler; -+ struct v4l2_ctrl *link_freq; -+ struct v4l2_ctrl *pixel_rate; -+ struct v4l2_ctrl *exposure; -+ struct v4l2_ctrl *analogue_gain; -+ struct v4l2_ctrl *digital_gain; -+ struct v4l2_ctrl *hflip; -+ struct v4l2_ctrl *vflip; -+ struct v4l2_ctrl *hblank; -+ struct v4l2_ctrl *vblank; -+ struct v4l2_ctrl *test_pattern; -+ } ctrls; -+}; -+ -+static const struct ov5693_reg ov5693_global_regs[] = { -+ {0x3016, 0xf0}, -+ {0x3017, 0xf0}, -+ {0x3018, 0xf0}, -+ {0x3022, 0x01}, -+ {0x3028, 0x44}, -+ {0x3098, 0x02}, -+ {0x3099, 0x19}, -+ {0x309a, 0x02}, -+ {0x309b, 0x01}, -+ {0x309c, 0x00}, -+ {0x30a0, 0xd2}, -+ {0x30a2, 0x01}, -+ {0x30b2, 0x00}, -+ {0x30b3, 0x7d}, -+ {0x30b4, 0x03}, -+ {0x30b5, 0x04}, -+ {0x30b6, 0x01}, -+ {0x3104, 0x21}, -+ {0x3106, 0x00}, -+ {0x3406, 0x01}, -+ {0x3503, 0x07}, -+ {0x350b, 0x40}, -+ {0x3601, 0x0a}, -+ {0x3602, 0x38}, -+ {0x3612, 0x80}, -+ {0x3620, 0x54}, -+ {0x3621, 0xc7}, -+ {0x3622, 0x0f}, -+ {0x3625, 0x10}, -+ {0x3630, 0x55}, -+ {0x3631, 0xf4}, -+ {0x3632, 0x00}, -+ {0x3633, 0x34}, -+ {0x3634, 0x02}, -+ {0x364d, 0x0d}, -+ {0x364f, 0xdd}, -+ {0x3660, 0x04}, -+ {0x3662, 0x10}, -+ {0x3663, 0xf1}, -+ {0x3665, 0x00}, -+ {0x3666, 0x20}, -+ {0x3667, 0x00}, -+ {0x366a, 0x80}, -+ {0x3680, 0xe0}, -+ {0x3681, 0x00}, -+ {0x3700, 0x42}, -+ {0x3701, 0x14}, -+ {0x3702, 0xa0}, -+ {0x3703, 0xd8}, -+ {0x3704, 0x78}, -+ {0x3705, 0x02}, -+ {0x370a, 0x00}, -+ {0x370b, 0x20}, -+ {0x370c, 0x0c}, -+ {0x370d, 0x11}, -+ {0x370e, 0x00}, -+ {0x370f, 0x40}, -+ {0x3710, 0x00}, -+ {0x371a, 0x1c}, -+ {0x371b, 0x05}, -+ {0x371c, 0x01}, -+ {0x371e, 0xa1}, -+ {0x371f, 0x0c}, -+ {0x3721, 0x00}, -+ {0x3724, 0x10}, -+ {0x3726, 0x00}, -+ {0x372a, 0x01}, -+ {0x3730, 0x10}, -+ {0x3738, 0x22}, -+ {0x3739, 0xe5}, -+ {0x373a, 0x50}, -+ {0x373b, 0x02}, -+ {0x373c, 0x41}, -+ {0x373f, 0x02}, -+ {0x3740, 0x42}, -+ {0x3741, 0x02}, -+ {0x3742, 0x18}, -+ {0x3743, 0x01}, -+ {0x3744, 0x02}, -+ {0x3747, 0x10}, -+ {0x374c, 0x04}, -+ {0x3751, 0xf0}, -+ {0x3752, 0x00}, -+ {0x3753, 0x00}, -+ {0x3754, 0xc0}, -+ {0x3755, 0x00}, -+ {0x3756, 0x1a}, -+ {0x3758, 0x00}, -+ {0x3759, 0x0f}, -+ {0x376b, 0x44}, -+ {0x375c, 0x04}, -+ {0x3774, 0x10}, -+ {0x3776, 0x00}, -+ {0x377f, 0x08}, -+ {0x3780, 0x22}, -+ {0x3781, 0x0c}, -+ {0x3784, 0x2c}, -+ {0x3785, 0x1e}, -+ {0x378f, 0xf5}, -+ {0x3791, 0xb0}, -+ {0x3795, 0x00}, -+ {0x3796, 0x64}, -+ {0x3797, 0x11}, -+ {0x3798, 0x30}, -+ {0x3799, 0x41}, -+ {0x379a, 0x07}, -+ {0x379b, 0xb0}, -+ {0x379c, 0x0c}, -+ {0x3a04, 0x06}, -+ {0x3a05, 0x14}, -+ {0x3e07, 0x20}, -+ {0x4000, 0x08}, -+ {0x4001, 0x04}, -+ {0x4004, 0x08}, -+ {0x4006, 0x20}, -+ {0x4008, 0x24}, -+ {0x4009, 0x10}, -+ {0x4058, 0x00}, -+ {0x4101, 0xb2}, -+ {0x4307, 0x31}, -+ {0x4511, 0x05}, -+ {0x4512, 0x01}, -+ {0x481f, 0x30}, -+ {0x4826, 0x2c}, -+ {0x4d02, 0xfd}, -+ {0x4d03, 0xf5}, -+ {0x4d04, 0x0c}, -+ {0x4d05, 0xcc}, -+ {0x4837, 0x0a}, -+ {0x5003, 0x20}, -+ {0x5013, 0x00}, -+ {0x5842, 0x01}, -+ {0x5843, 0x2b}, -+ {0x5844, 0x01}, -+ {0x5845, 0x92}, -+ {0x5846, 0x01}, -+ {0x5847, 0x8f}, -+ {0x5848, 0x01}, -+ {0x5849, 0x0c}, -+ {0x5e10, 0x0c}, -+ {0x3820, 0x00}, -+ {0x3821, 0x1e}, -+ {0x5041, 0x14} -+}; -+ -+static const struct ov5693_reg_list ov5693_global_setting = { -+ .num_regs = ARRAY_SIZE(ov5693_global_regs), -+ .regs = ov5693_global_regs, -+}; -+ -+static const struct v4l2_rect ov5693_default_crop = { -+ .left = OV5693_ACTIVE_START_LEFT, -+ .top = OV5693_ACTIVE_START_TOP, -+ .width = OV5693_ACTIVE_WIDTH, -+ .height = OV5693_ACTIVE_HEIGHT, -+}; -+ -+static const struct v4l2_mbus_framefmt ov5693_default_fmt = { -+ .width = OV5693_ACTIVE_WIDTH, -+ .height = OV5693_ACTIVE_HEIGHT, -+ .code = MEDIA_BUS_FMT_SBGGR10_1X10, -+}; -+ -+static const s64 link_freq_menu_items[] = { -+ OV5693_LINK_FREQ_400MHZ -+}; -+ -+static const char * const ov5693_supply_names[] = { -+ "avdd", -+ "dovdd", -+}; -+ -+static const char * const ov5693_test_pattern_menu[] = { -+ "Disabled", -+ "Random Data", -+ "Colour Bars", -+ "Colour Bars with Rolling Bar" -+}; -+ -+static const u8 ov5693_test_pattern_bits[] = { -+ 0, -+ OV5693_TEST_PATTERN_ENABLE | OV5693_TEST_PATTERN_RANDOM, -+ OV5693_TEST_PATTERN_ENABLE | OV5693_TEST_PATTERN_BARS, -+ OV5693_TEST_PATTERN_ENABLE | OV5693_TEST_PATTERN_BARS | -+ OV5693_TEST_PATTERN_ROLLING, -+}; -+ -+/* I2C I/O Operations */ -+ -+static int ov5693_read_reg(struct ov5693_device *ov5693, u16 addr, u8 *value) -+{ -+ struct i2c_client *client = ov5693->client; -+ struct i2c_msg msgs[2]; -+ u8 addr_buf[2]; -+ u8 data_buf; -+ int ret; -+ -+ put_unaligned_be16(addr, addr_buf); -+ -+ /* Write register address */ -+ msgs[0].addr = client->addr; -+ msgs[0].flags = 0; -+ msgs[0].len = ARRAY_SIZE(addr_buf); -+ msgs[0].buf = addr_buf; -+ -+ /* Read register value */ -+ msgs[1].addr = client->addr; -+ msgs[1].flags = I2C_M_RD; -+ msgs[1].len = 1; -+ msgs[1].buf = &data_buf; -+ -+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); -+ if (ret != ARRAY_SIZE(msgs)) -+ return -EIO; -+ -+ *value = data_buf; -+ -+ return 0; -+} -+ -+static void ov5693_write_reg(struct ov5693_device *ov5693, u16 addr, u8 value, -+ int *error) -+{ -+ unsigned char data[3] = { addr >> 8, addr & 0xff, value }; -+ int ret; -+ -+ if (*error < 0) -+ return; -+ -+ ret = i2c_master_send(ov5693->client, data, sizeof(data)); -+ if (ret < 0) { -+ dev_dbg(ov5693->dev, "i2c send error at address 0x%04x: %d\n", -+ addr, ret); -+ *error = ret; -+ } -+} -+ -+static int ov5693_write_reg_array(struct ov5693_device *ov5693, -+ const struct ov5693_reg_list *reglist) -+{ -+ unsigned int i; -+ int ret = 0; -+ -+ for (i = 0; i < reglist->num_regs; i++) -+ ov5693_write_reg(ov5693, reglist->regs[i].reg, -+ reglist->regs[i].val, &ret); -+ -+ return ret; -+} -+ -+static int ov5693_update_bits(struct ov5693_device *ov5693, u16 address, -+ u16 mask, u16 bits) -+{ -+ u8 value = 0; -+ int ret; -+ -+ ret = ov5693_read_reg(ov5693, address, &value); -+ if (ret) -+ return ret; -+ -+ value &= ~mask; -+ value |= bits; -+ -+ ov5693_write_reg(ov5693, address, value, &ret); -+ -+ return ret; -+} -+ -+/* V4L2 Controls Functions */ -+ -+static int ov5693_flip_vert_configure(struct ov5693_device *ov5693, bool enable) -+{ -+ u8 bits = OV5693_FORMAT1_FLIP_VERT_ISP_EN | -+ OV5693_FORMAT1_FLIP_VERT_SENSOR_EN; -+ int ret; -+ -+ ret = ov5693_update_bits(ov5693, OV5693_FORMAT1_REG, bits, -+ enable ? bits : 0); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int ov5693_flip_horz_configure(struct ov5693_device *ov5693, bool enable) -+{ -+ u8 bits = OV5693_FORMAT2_FLIP_HORZ_ISP_EN | -+ OV5693_FORMAT2_FLIP_HORZ_SENSOR_EN; -+ int ret; -+ -+ ret = ov5693_update_bits(ov5693, OV5693_FORMAT2_REG, bits, -+ enable ? bits : 0); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int ov5693_get_exposure(struct ov5693_device *ov5693, s32 *value) -+{ -+ u8 exposure_hh = 0, exposure_h = 0, exposure_l = 0; -+ int ret; -+ -+ ret = ov5693_read_reg(ov5693, OV5693_EXPOSURE_L_CTRL_HH_REG, &exposure_hh); -+ if (ret) -+ return ret; -+ -+ ret = ov5693_read_reg(ov5693, OV5693_EXPOSURE_L_CTRL_H_REG, &exposure_h); -+ if (ret) -+ return ret; -+ -+ ret = ov5693_read_reg(ov5693, OV5693_EXPOSURE_L_CTRL_L_REG, &exposure_l); -+ if (ret) -+ return ret; -+ -+ /* The lowest 4 bits are unsupported fractional bits */ -+ *value = ((exposure_hh << 16) | (exposure_h << 8) | exposure_l) >> 4; -+ -+ return 0; -+} -+ -+static int ov5693_exposure_configure(struct ov5693_device *ov5693, u32 exposure) -+{ -+ int ret = 0; -+ -+ ov5693_write_reg(ov5693, OV5693_EXPOSURE_L_CTRL_HH_REG, -+ OV5693_EXPOSURE_CTRL_HH(exposure), &ret); -+ ov5693_write_reg(ov5693, OV5693_EXPOSURE_L_CTRL_H_REG, -+ OV5693_EXPOSURE_CTRL_H(exposure), &ret); -+ ov5693_write_reg(ov5693, OV5693_EXPOSURE_L_CTRL_L_REG, -+ OV5693_EXPOSURE_CTRL_L(exposure), &ret); -+ -+ return ret; -+} -+ -+static int ov5693_get_gain(struct ov5693_device *ov5693, u32 *gain) -+{ -+ u8 gain_l = 0, gain_h = 0; -+ int ret; -+ -+ ret = ov5693_read_reg(ov5693, OV5693_GAIN_CTRL_H_REG, &gain_h); -+ if (ret) -+ return ret; -+ -+ ret = ov5693_read_reg(ov5693, OV5693_GAIN_CTRL_L_REG, &gain_l); -+ if (ret) -+ return ret; -+ -+ /* As with exposure, the lowest 4 bits are fractional bits. */ -+ *gain = ((gain_h << 8) | gain_l) >> 4; -+ -+ return ret; -+} -+ -+static int ov5693_digital_gain_configure(struct ov5693_device *ov5693, u32 gain) -+{ -+ int ret = 0; -+ -+ ov5693_write_reg(ov5693, OV5693_MWB_RED_GAIN_H_REG, -+ OV5693_MWB_GAIN_H_CTRL(gain), &ret); -+ ov5693_write_reg(ov5693, OV5693_MWB_RED_GAIN_L_REG, -+ OV5693_MWB_GAIN_L_CTRL(gain), &ret); -+ ov5693_write_reg(ov5693, OV5693_MWB_GREEN_GAIN_H_REG, -+ OV5693_MWB_GAIN_H_CTRL(gain), &ret); -+ ov5693_write_reg(ov5693, OV5693_MWB_GREEN_GAIN_L_REG, -+ OV5693_MWB_GAIN_L_CTRL(gain), &ret); -+ ov5693_write_reg(ov5693, OV5693_MWB_BLUE_GAIN_H_REG, -+ OV5693_MWB_GAIN_H_CTRL(gain), &ret); -+ ov5693_write_reg(ov5693, OV5693_MWB_BLUE_GAIN_L_REG, -+ OV5693_MWB_GAIN_L_CTRL(gain), &ret); -+ -+ return ret; -+} -+ -+static int ov5693_analog_gain_configure(struct ov5693_device *ov5693, u32 gain) -+{ -+ int ret = 0; -+ -+ ov5693_write_reg(ov5693, OV5693_GAIN_CTRL_L_REG, -+ OV5693_GAIN_CTRL_L(gain), &ret); -+ ov5693_write_reg(ov5693, OV5693_GAIN_CTRL_H_REG, -+ OV5693_GAIN_CTRL_H(gain), &ret); -+ -+ return ret; -+} -+ -+static int ov5693_vts_configure(struct ov5693_device *ov5693, u32 vblank) -+{ -+ u16 vts = ov5693->mode.format.height + vblank; -+ int ret = 0; -+ -+ ov5693_write_reg(ov5693, OV5693_TIMING_VTS_H_REG, -+ OV5693_TIMING_VTS_H(vts), &ret); -+ ov5693_write_reg(ov5693, OV5693_TIMING_VTS_L_REG, -+ OV5693_TIMING_VTS_L(vts), &ret); -+ -+ return ret; -+} -+ -+static int ov5693_test_pattern_configure(struct ov5693_device *ov5693, u32 idx) -+{ -+ int ret = 0; -+ -+ ov5693_write_reg(ov5693, OV5693_TEST_PATTERN_REG, -+ ov5693_test_pattern_bits[idx], &ret); -+ -+ return ret; -+} -+ -+static int ov5693_s_ctrl(struct v4l2_ctrl *ctrl) -+{ -+ struct ov5693_device *ov5693 = -+ container_of(ctrl->handler, struct ov5693_device, ctrls.handler); -+ int ret = 0; -+ -+ /* If VBLANK is altered we need to update exposure to compensate */ -+ if (ctrl->id == V4L2_CID_VBLANK) { -+ int exposure_max; -+ -+ exposure_max = ov5693->mode.format.height + ctrl->val - -+ OV5693_INTEGRATION_TIME_MARGIN; -+ __v4l2_ctrl_modify_range(ov5693->ctrls.exposure, -+ ov5693->ctrls.exposure->minimum, -+ exposure_max, -+ ov5693->ctrls.exposure->step, -+ min(ov5693->ctrls.exposure->val, exposure_max)); -+ } -+ -+ /* Only apply changes to the controls if the device is powered up */ -+ if (!pm_runtime_get_if_in_use(ov5693->dev)) -+ return 0; -+ -+ switch (ctrl->id) { -+ case V4L2_CID_EXPOSURE: -+ ret = ov5693_exposure_configure(ov5693, ctrl->val); -+ break; -+ case V4L2_CID_ANALOGUE_GAIN: -+ ret = ov5693_analog_gain_configure(ov5693, ctrl->val); -+ break; -+ case V4L2_CID_DIGITAL_GAIN: -+ ret = ov5693_digital_gain_configure(ov5693, ctrl->val); -+ break; -+ case V4L2_CID_HFLIP: -+ ret = ov5693_flip_horz_configure(ov5693, !!ctrl->val); -+ break; -+ case V4L2_CID_VFLIP: -+ ret = ov5693_flip_vert_configure(ov5693, !!ctrl->val); -+ break; -+ case V4L2_CID_VBLANK: -+ ret = ov5693_vts_configure(ov5693, ctrl->val); -+ break; -+ case V4L2_CID_TEST_PATTERN: -+ ret = ov5693_test_pattern_configure(ov5693, ctrl->val); -+ break; -+ default: -+ ret = -EINVAL; -+ } -+ -+ pm_runtime_put(ov5693->dev); -+ -+ return ret; -+} -+ -+static int ov5693_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -+{ -+ struct ov5693_device *ov5693 = -+ container_of(ctrl->handler, struct ov5693_device, ctrls.handler); -+ -+ switch (ctrl->id) { -+ case V4L2_CID_EXPOSURE_ABSOLUTE: -+ return ov5693_get_exposure(ov5693, &ctrl->val); -+ case V4L2_CID_AUTOGAIN: -+ return ov5693_get_gain(ov5693, &ctrl->val); -+ default: -+ return -EINVAL; -+ } -+} -+ -+static const struct v4l2_ctrl_ops ov5693_ctrl_ops = { -+ .s_ctrl = ov5693_s_ctrl, -+ .g_volatile_ctrl = ov5693_g_volatile_ctrl -+}; -+ -+/* System Control Functions */ -+ -+static int ov5693_mode_configure(struct ov5693_device *ov5693) -+{ -+ const struct ov5693_mode *mode = &ov5693->mode; -+ int ret = 0; -+ -+ /* Crop Start X */ -+ ov5693_write_reg(ov5693, OV5693_CROP_START_X_H_REG, -+ OV5693_CROP_START_X_H(mode->crop.left), &ret); -+ ov5693_write_reg(ov5693, OV5693_CROP_START_X_L_REG, -+ OV5693_CROP_START_X_L(mode->crop.left), &ret); -+ -+ /* Offset X */ -+ ov5693_write_reg(ov5693, OV5693_OFFSET_START_X_H_REG, -+ OV5693_OFFSET_START_X_H(0), &ret); -+ ov5693_write_reg(ov5693, OV5693_OFFSET_START_X_L_REG, -+ OV5693_OFFSET_START_X_L(0), &ret); -+ -+ /* Output Size X */ -+ ov5693_write_reg(ov5693, OV5693_OUTPUT_SIZE_X_H_REG, -+ OV5693_OUTPUT_SIZE_X_H(mode->format.width), &ret); -+ ov5693_write_reg(ov5693, OV5693_OUTPUT_SIZE_X_L_REG, -+ OV5693_OUTPUT_SIZE_X_L(mode->format.width), &ret); -+ -+ /* Crop End X */ -+ ov5693_write_reg(ov5693, OV5693_CROP_END_X_H_REG, -+ OV5693_CROP_END_X_H(mode->crop.left + mode->crop.width), -+ &ret); -+ ov5693_write_reg(ov5693, OV5693_CROP_END_X_L_REG, -+ OV5693_CROP_END_X_L(mode->crop.left + mode->crop.width), -+ &ret); -+ -+ /* Horizontal Total Size */ -+ ov5693_write_reg(ov5693, OV5693_TIMING_HTS_H_REG, -+ OV5693_TIMING_HTS_H(OV5693_FIXED_PPL), &ret); -+ ov5693_write_reg(ov5693, OV5693_TIMING_HTS_L_REG, -+ OV5693_TIMING_HTS_L(OV5693_FIXED_PPL), &ret); -+ -+ /* Crop Start Y */ -+ ov5693_write_reg(ov5693, OV5693_CROP_START_Y_H_REG, -+ OV5693_CROP_START_Y_H(mode->crop.top), &ret); -+ ov5693_write_reg(ov5693, OV5693_CROP_START_Y_L_REG, -+ OV5693_CROP_START_Y_L(mode->crop.top), &ret); -+ -+ /* Offset Y */ -+ ov5693_write_reg(ov5693, OV5693_OFFSET_START_Y_H_REG, -+ OV5693_OFFSET_START_Y_H(0), &ret); -+ ov5693_write_reg(ov5693, OV5693_OFFSET_START_Y_L_REG, -+ OV5693_OFFSET_START_Y_L(0), &ret); -+ -+ /* Output Size Y */ -+ ov5693_write_reg(ov5693, OV5693_OUTPUT_SIZE_Y_H_REG, -+ OV5693_OUTPUT_SIZE_Y_H(mode->format.height), &ret); -+ ov5693_write_reg(ov5693, OV5693_OUTPUT_SIZE_Y_L_REG, -+ OV5693_OUTPUT_SIZE_Y_L(mode->format.height), &ret); -+ -+ /* Crop End Y */ -+ ov5693_write_reg(ov5693, OV5693_CROP_END_Y_H_REG, -+ OV5693_CROP_END_Y_H(mode->crop.top + mode->crop.height), -+ &ret); -+ ov5693_write_reg(ov5693, OV5693_CROP_END_Y_L_REG, -+ OV5693_CROP_END_Y_L(mode->crop.top + mode->crop.height), -+ &ret); -+ -+ /* Vertical Total Size */ -+ ov5693_write_reg(ov5693, OV5693_TIMING_VTS_H_REG, -+ OV5693_TIMING_VTS_H(mode->vts), &ret); -+ ov5693_write_reg(ov5693, OV5693_TIMING_VTS_L_REG, -+ OV5693_TIMING_VTS_L(mode->vts), &ret); -+ -+ /* Subsample X increase */ -+ ov5693_write_reg(ov5693, OV5693_SUB_INC_X_REG, -+ ((mode->inc_x_odd << 4) & 0xf0) | 0x01, &ret); -+ /* Subsample Y increase */ -+ ov5693_write_reg(ov5693, OV5693_SUB_INC_Y_REG, -+ ((mode->inc_y_odd << 4) & 0xf0) | 0x01, &ret); -+ -+ /* Binning */ -+ ret = ov5693_update_bits(ov5693, OV5693_FORMAT1_REG, -+ OV5693_FORMAT1_VBIN_EN, -+ mode->binning_y ? OV5693_FORMAT1_VBIN_EN : 0); -+ if (ret) -+ return ret; -+ -+ ret = ov5693_update_bits(ov5693, OV5693_FORMAT2_REG, -+ OV5693_FORMAT2_HBIN_EN, -+ mode->binning_x ? OV5693_FORMAT2_HBIN_EN : 0); -+ -+ return ret; -+} -+ -+static int ov5693_sw_standby(struct ov5693_device *ov5693, bool standby) -+{ -+ int ret = 0; -+ -+ ov5693_write_reg(ov5693, OV5693_SW_STREAM_REG, -+ standby ? OV5693_STOP_STREAMING : OV5693_START_STREAMING, -+ &ret); -+ -+ return ret; -+} -+ -+static int ov5693_sw_reset(struct ov5693_device *ov5693) -+{ -+ int ret = 0; -+ -+ ov5693_write_reg(ov5693, OV5693_SW_RESET_REG, OV5693_SW_RESET, &ret); -+ -+ return ret; -+} -+ -+static int ov5693_sensor_init(struct ov5693_device *ov5693) -+{ -+ int ret = 0; -+ -+ ret = ov5693_sw_reset(ov5693); -+ if (ret) { -+ dev_err(ov5693->dev, "%s software reset error\n", __func__); -+ return ret; -+ } -+ -+ ret = ov5693_write_reg_array(ov5693, &ov5693_global_setting); -+ if (ret) { -+ dev_err(ov5693->dev, "%s global settings error\n", __func__); -+ return ret; -+ } -+ -+ ret = ov5693_mode_configure(ov5693); -+ if (ret) { -+ dev_err(ov5693->dev, "%s mode configure error\n", __func__); -+ return ret; -+ } -+ -+ ret = ov5693_sw_standby(ov5693, true); -+ if (ret) -+ dev_err(ov5693->dev, "%s software standby error\n", __func__); -+ -+ return ret; -+} -+ -+static void ov5693_sensor_powerdown(struct ov5693_device *ov5693) -+{ -+ gpiod_set_value_cansleep(ov5693->reset, 1); -+ gpiod_set_value_cansleep(ov5693->powerdown, 1); -+ -+ regulator_bulk_disable(OV5693_NUM_SUPPLIES, ov5693->supplies); -+ -+ clk_disable_unprepare(ov5693->clk); -+} -+ -+static int ov5693_sensor_powerup(struct ov5693_device *ov5693) -+{ -+ int ret; -+ -+ gpiod_set_value_cansleep(ov5693->reset, 1); -+ gpiod_set_value_cansleep(ov5693->powerdown, 1); -+ -+ ret = clk_prepare_enable(ov5693->clk); -+ if (ret) { -+ dev_err(ov5693->dev, "Failed to enable clk\n"); -+ goto fail_power; -+ } -+ -+ ret = regulator_bulk_enable(OV5693_NUM_SUPPLIES, ov5693->supplies); -+ if (ret) { -+ dev_err(ov5693->dev, "Failed to enable regulators\n"); -+ goto fail_power; -+ } -+ -+ gpiod_set_value_cansleep(ov5693->powerdown, 0); -+ gpiod_set_value_cansleep(ov5693->reset, 0); -+ -+ usleep_range(5000, 7500); -+ -+ return 0; -+ -+fail_power: -+ ov5693_sensor_powerdown(ov5693); -+ return ret; -+} -+ -+static int __maybe_unused ov5693_sensor_suspend(struct device *dev) -+{ -+ struct v4l2_subdev *sd = dev_get_drvdata(dev); -+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd); -+ -+ ov5693_sensor_powerdown(ov5693); -+ -+ return 0; -+} -+ -+static int __maybe_unused ov5693_sensor_resume(struct device *dev) -+{ -+ struct v4l2_subdev *sd = dev_get_drvdata(dev); -+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd); -+ int ret; -+ -+ mutex_lock(&ov5693->lock); -+ -+ ret = ov5693_sensor_powerup(ov5693); -+ if (ret) -+ goto out_unlock; -+ -+ ret = ov5693_sensor_init(ov5693); -+ if (ret) { -+ dev_err(dev, "ov5693 sensor init failure\n"); -+ goto err_power; -+ } -+ -+ goto out_unlock; -+ -+err_power: -+ ov5693_sensor_powerdown(ov5693); -+out_unlock: -+ mutex_unlock(&ov5693->lock); -+ return ret; -+} -+ -+static int ov5693_detect(struct ov5693_device *ov5693) -+{ -+ u8 id_l = 0, id_h = 0; -+ u16 id = 0; -+ int ret; -+ -+ ret = ov5693_read_reg(ov5693, OV5693_REG_CHIP_ID_H, &id_h); -+ if (ret) -+ return ret; -+ -+ ret = ov5693_read_reg(ov5693, OV5693_REG_CHIP_ID_L, &id_l); -+ if (ret) -+ return ret; -+ -+ id = (id_h << 8) | id_l; -+ -+ if (id != OV5693_CHIP_ID) { -+ dev_err(ov5693->dev, "sensor ID mismatch. Found 0x%04x\n", id); -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+/* V4L2 Framework callbacks */ -+ -+static unsigned int __ov5693_calc_vts(u32 height) -+{ -+ /* -+ * We need to set a sensible default VTS for whatever format height we -+ * happen to be given from set_fmt(). This function just targets -+ * an even multiple of 30fps. -+ */ -+ -+ unsigned int tgt_fps; -+ -+ tgt_fps = rounddown(OV5693_PIXEL_RATE / OV5693_FIXED_PPL / height, 30); -+ -+ return ALIGN_DOWN(OV5693_PIXEL_RATE / OV5693_FIXED_PPL / tgt_fps, 2); -+} -+ -+static struct v4l2_mbus_framefmt * -+__ov5693_get_pad_format(struct ov5693_device *ov5693, -+ struct v4l2_subdev_state *state, -+ unsigned int pad, enum v4l2_subdev_format_whence which) -+{ -+ switch (which) { -+ case V4L2_SUBDEV_FORMAT_TRY: -+ return v4l2_subdev_get_try_format(&ov5693->sd, state, pad); -+ case V4L2_SUBDEV_FORMAT_ACTIVE: -+ return &ov5693->mode.format; -+ default: -+ return NULL; -+ } -+} -+ -+static struct v4l2_rect * -+__ov5693_get_pad_crop(struct ov5693_device *ov5693, -+ struct v4l2_subdev_state *state, -+ unsigned int pad, enum v4l2_subdev_format_whence which) -+{ -+ switch (which) { -+ case V4L2_SUBDEV_FORMAT_TRY: -+ return v4l2_subdev_get_try_crop(&ov5693->sd, state, pad); -+ case V4L2_SUBDEV_FORMAT_ACTIVE: -+ return &ov5693->mode.crop; -+ } -+ -+ return NULL; -+} -+ -+static int ov5693_get_fmt(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_format *format) -+{ -+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd); -+ -+ format->format = ov5693->mode.format; -+ -+ return 0; -+} -+ -+static int ov5693_set_fmt(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_format *format) -+{ -+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd); -+ const struct v4l2_rect *crop; -+ struct v4l2_mbus_framefmt *fmt; -+ unsigned int hratio, vratio; -+ unsigned int width, height; -+ unsigned int hblank; -+ int exposure_max; -+ int ret = 0; -+ -+ crop = __ov5693_get_pad_crop(ov5693, state, format->pad, format->which); -+ -+ /* -+ * Align to two to simplify the binning calculations below, and clamp -+ * the requested format at the crop rectangle -+ */ -+ width = clamp_t(unsigned int, ALIGN(format->format.width, 2), -+ OV5693_MIN_CROP_WIDTH, crop->width); -+ height = clamp_t(unsigned int, ALIGN(format->format.height, 2), -+ OV5693_MIN_CROP_HEIGHT, crop->height); -+ -+ /* -+ * We can only support setting either the dimensions of the crop rect -+ * or those dimensions binned (separately) by a factor of two. -+ */ -+ hratio = clamp_t(unsigned int, DIV_ROUND_CLOSEST(crop->width, width), 1, 2); -+ vratio = clamp_t(unsigned int, DIV_ROUND_CLOSEST(crop->height, height), 1, 2); -+ -+ fmt = __ov5693_get_pad_format(ov5693, state, format->pad, format->which); -+ -+ fmt->width = crop->width / hratio; -+ fmt->height = crop->height / vratio; -+ fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; -+ -+ format->format = *fmt; -+ -+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) -+ return ret; -+ -+ mutex_lock(&ov5693->lock); -+ -+ ov5693->mode.binning_x = hratio > 1 ? true : false; -+ ov5693->mode.inc_x_odd = hratio > 1 ? 3 : 1; -+ ov5693->mode.binning_y = vratio > 1 ? true : false; -+ ov5693->mode.inc_y_odd = vratio > 1 ? 3 : 1; -+ -+ ov5693->mode.vts = __ov5693_calc_vts(fmt->height); -+ -+ __v4l2_ctrl_modify_range(ov5693->ctrls.vblank, -+ OV5693_TIMING_MIN_VTS, -+ OV5693_TIMING_MAX_VTS - fmt->height, -+ 1, ov5693->mode.vts - fmt->height); -+ __v4l2_ctrl_s_ctrl(ov5693->ctrls.vblank, -+ ov5693->mode.vts - fmt->height); -+ -+ hblank = OV5693_FIXED_PPL - fmt->width; -+ __v4l2_ctrl_modify_range(ov5693->ctrls.hblank, hblank, hblank, 1, -+ hblank); -+ -+ exposure_max = ov5693->mode.vts - OV5693_INTEGRATION_TIME_MARGIN; -+ __v4l2_ctrl_modify_range(ov5693->ctrls.exposure, -+ ov5693->ctrls.exposure->minimum, exposure_max, -+ ov5693->ctrls.exposure->step, -+ min(ov5693->ctrls.exposure->val, exposure_max)); -+ -+ mutex_unlock(&ov5693->lock); -+ return ret; -+} -+ -+static int ov5693_get_selection(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_selection *sel) -+{ -+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd); -+ -+ switch (sel->target) { -+ case V4L2_SEL_TGT_CROP: -+ mutex_lock(&ov5693->lock); -+ sel->r = *__ov5693_get_pad_crop(ov5693, state, sel->pad, -+ sel->which); -+ mutex_unlock(&ov5693->lock); -+ break; -+ case V4L2_SEL_TGT_NATIVE_SIZE: -+ sel->r.top = 0; -+ sel->r.left = 0; -+ sel->r.width = OV5693_NATIVE_WIDTH; -+ sel->r.height = OV5693_NATIVE_HEIGHT; -+ break; -+ case V4L2_SEL_TGT_CROP_BOUNDS: -+ case V4L2_SEL_TGT_CROP_DEFAULT: -+ sel->r.top = OV5693_ACTIVE_START_TOP; -+ sel->r.left = OV5693_ACTIVE_START_LEFT; -+ sel->r.width = OV5693_ACTIVE_WIDTH; -+ sel->r.height = OV5693_ACTIVE_HEIGHT; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int ov5693_set_selection(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_selection *sel) -+{ -+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd); -+ struct v4l2_mbus_framefmt *format; -+ struct v4l2_rect *__crop; -+ struct v4l2_rect rect; -+ -+ if (sel->target != V4L2_SEL_TGT_CROP) -+ return -EINVAL; -+ -+ /* -+ * Clamp the boundaries of the crop rectangle to the size of the sensor -+ * pixel array. Align to multiples of 2 to ensure Bayer pattern isn't -+ * disrupted. -+ */ -+ rect.left = clamp(ALIGN(sel->r.left, 2), OV5693_NATIVE_START_LEFT, -+ OV5693_NATIVE_WIDTH); -+ rect.top = clamp(ALIGN(sel->r.top, 2), OV5693_NATIVE_START_TOP, -+ OV5693_NATIVE_HEIGHT); -+ rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), -+ OV5693_MIN_CROP_WIDTH, OV5693_NATIVE_WIDTH); -+ rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), -+ OV5693_MIN_CROP_HEIGHT, OV5693_NATIVE_HEIGHT); -+ -+ /* Make sure the crop rectangle isn't outside the bounds of the array */ -+ rect.width = min_t(unsigned int, rect.width, -+ OV5693_NATIVE_WIDTH - rect.left); -+ rect.height = min_t(unsigned int, rect.height, -+ OV5693_NATIVE_HEIGHT - rect.top); -+ -+ __crop = __ov5693_get_pad_crop(ov5693, state, sel->pad, sel->which); -+ -+ if (rect.width != __crop->width || rect.height != __crop->height) { -+ /* -+ * Reset the output image size if the crop rectangle size has -+ * been modified. -+ */ -+ format = __ov5693_get_pad_format(ov5693, state, sel->pad, sel->which); -+ format->width = rect.width; -+ format->height = rect.height; -+ } -+ -+ *__crop = rect; -+ sel->r = rect; -+ -+ return 0; -+} -+ -+static int ov5693_s_stream(struct v4l2_subdev *sd, int enable) -+{ -+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd); -+ int ret; -+ -+ if (enable) { -+ ret = pm_runtime_get_sync(ov5693->dev); -+ if (ret < 0) -+ goto err_power_down; -+ -+ ret = __v4l2_ctrl_handler_setup(&ov5693->ctrls.handler); -+ if (ret) -+ goto err_power_down; -+ } -+ -+ mutex_lock(&ov5693->lock); -+ ret = ov5693_sw_standby(ov5693, !enable); -+ mutex_unlock(&ov5693->lock); -+ -+ if (ret) -+ goto err_power_down; -+ ov5693->streaming = !!enable; -+ -+ if (!enable) -+ pm_runtime_put(ov5693->dev); -+ -+ return 0; -+err_power_down: -+ pm_runtime_put_noidle(ov5693->dev); -+ return ret; -+} -+ -+static int ov5693_g_frame_interval(struct v4l2_subdev *sd, -+ struct v4l2_subdev_frame_interval *interval) -+{ -+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd); -+ unsigned int framesize = OV5693_FIXED_PPL * (ov5693->mode.format.height + -+ ov5693->ctrls.vblank->val); -+ unsigned int fps = DIV_ROUND_CLOSEST(OV5693_PIXEL_RATE, framesize); -+ -+ interval->interval.numerator = 1; -+ interval->interval.denominator = fps; -+ -+ return 0; -+} -+ -+static int ov5693_enum_mbus_code(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_mbus_code_enum *code) -+{ -+ /* Only a single mbus format is supported */ -+ if (code->index > 0) -+ return -EINVAL; -+ -+ code->code = MEDIA_BUS_FMT_SBGGR10_1X10; -+ return 0; -+} -+ -+static int ov5693_enum_frame_size(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_frame_size_enum *fse) -+{ -+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd); -+ struct v4l2_rect *__crop; -+ -+ if (fse->index > 1 || fse->code != MEDIA_BUS_FMT_SBGGR10_1X10) -+ return -EINVAL; -+ -+ __crop = __ov5693_get_pad_crop(ov5693, state, fse->pad, fse->which); -+ if (!__crop) -+ return -EINVAL; -+ -+ fse->min_width = __crop->width / (fse->index + 1); -+ fse->min_height = __crop->height / (fse->index + 1); -+ fse->max_width = fse->min_width; -+ fse->max_height = fse->min_height; -+ -+ return 0; -+} -+ -+static const struct v4l2_subdev_video_ops ov5693_video_ops = { -+ .s_stream = ov5693_s_stream, -+ .g_frame_interval = ov5693_g_frame_interval, -+}; -+ -+static const struct v4l2_subdev_pad_ops ov5693_pad_ops = { -+ .enum_mbus_code = ov5693_enum_mbus_code, -+ .enum_frame_size = ov5693_enum_frame_size, -+ .get_fmt = ov5693_get_fmt, -+ .set_fmt = ov5693_set_fmt, -+ .get_selection = ov5693_get_selection, -+ .set_selection = ov5693_set_selection, -+}; -+ -+static const struct v4l2_subdev_ops ov5693_ops = { -+ .video = &ov5693_video_ops, -+ .pad = &ov5693_pad_ops, -+}; -+ -+/* Sensor and Driver Configuration Functions */ -+ -+static int ov5693_init_controls(struct ov5693_device *ov5693) -+{ -+ const struct v4l2_ctrl_ops *ops = &ov5693_ctrl_ops; -+ struct v4l2_fwnode_device_properties props; -+ int vblank_max, vblank_def; -+ int exposure_max; -+ int hblank; -+ int ret; -+ -+ ret = v4l2_ctrl_handler_init(&ov5693->ctrls.handler, 12); -+ if (ret) -+ return ret; -+ -+ /* link freq */ -+ ov5693->ctrls.link_freq = v4l2_ctrl_new_int_menu(&ov5693->ctrls.handler, -+ NULL, V4L2_CID_LINK_FREQ, -+ 0, 0, link_freq_menu_items); -+ if (ov5693->ctrls.link_freq) -+ ov5693->ctrls.link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; -+ -+ /* pixel rate */ -+ ov5693->ctrls.pixel_rate = v4l2_ctrl_new_std(&ov5693->ctrls.handler, NULL, -+ V4L2_CID_PIXEL_RATE, 0, -+ OV5693_PIXEL_RATE, 1, -+ OV5693_PIXEL_RATE); -+ -+ /* Exposure */ -+ exposure_max = ov5693->mode.vts - OV5693_INTEGRATION_TIME_MARGIN; -+ ov5693->ctrls.exposure = v4l2_ctrl_new_std(&ov5693->ctrls.handler, ops, -+ V4L2_CID_EXPOSURE, -+ OV5693_EXPOSURE_MIN, -+ exposure_max, -+ OV5693_EXPOSURE_STEP, -+ exposure_max); -+ -+ /* Gain */ -+ ov5693->ctrls.analogue_gain = v4l2_ctrl_new_std(&ov5693->ctrls.handler, -+ ops, V4L2_CID_ANALOGUE_GAIN, -+ OV5693_GAIN_MIN, -+ OV5693_GAIN_MAX, -+ OV5693_GAIN_STEP, -+ OV5693_GAIN_DEF); -+ -+ ov5693->ctrls.digital_gain = v4l2_ctrl_new_std(&ov5693->ctrls.handler, ops, -+ V4L2_CID_DIGITAL_GAIN, -+ OV5693_DIGITAL_GAIN_MIN, -+ OV5693_DIGITAL_GAIN_MAX, -+ OV5693_DIGITAL_GAIN_STEP, -+ OV5693_DIGITAL_GAIN_DEF); -+ -+ /* Flip */ -+ ov5693->ctrls.hflip = v4l2_ctrl_new_std(&ov5693->ctrls.handler, ops, -+ V4L2_CID_HFLIP, 0, 1, 1, 0); -+ -+ ov5693->ctrls.vflip = v4l2_ctrl_new_std(&ov5693->ctrls.handler, ops, -+ V4L2_CID_VFLIP, 0, 1, 1, 0); -+ -+ hblank = OV5693_FIXED_PPL - ov5693->mode.format.width; -+ ov5693->ctrls.hblank = v4l2_ctrl_new_std(&ov5693->ctrls.handler, ops, -+ V4L2_CID_HBLANK, hblank, -+ hblank, 1, hblank); -+ -+ if (ov5693->ctrls.hblank) -+ ov5693->ctrls.hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; -+ -+ vblank_max = OV5693_TIMING_MAX_VTS - ov5693->mode.format.height; -+ vblank_def = ov5693->mode.vts - ov5693->mode.format.height; -+ ov5693->ctrls.vblank = v4l2_ctrl_new_std(&ov5693->ctrls.handler, ops, -+ V4L2_CID_VBLANK, -+ OV5693_TIMING_MIN_VTS, -+ vblank_max, 1, vblank_def); -+ -+ ov5693->ctrls.test_pattern = v4l2_ctrl_new_std_menu_items( -+ &ov5693->ctrls.handler, ops, -+ V4L2_CID_TEST_PATTERN, -+ ARRAY_SIZE(ov5693_test_pattern_menu) - 1, -+ 0, 0, ov5693_test_pattern_menu); -+ -+ if (ov5693->ctrls.handler.error) { -+ dev_err(ov5693->dev, "Error initialising v4l2 ctrls\n"); -+ ret = ov5693->ctrls.handler.error; -+ goto err_free_handler; -+ } -+ -+ /* set properties from fwnode (e.g. rotation, orientation) */ -+ ret = v4l2_fwnode_device_parse(ov5693->dev, &props); -+ if (ret) -+ goto err_free_handler; -+ -+ ret = v4l2_ctrl_new_fwnode_properties(&ov5693->ctrls.handler, ops, -+ &props); -+ if (ret) -+ goto err_free_handler; -+ -+ /* Use same lock for controls as for everything else. */ -+ ov5693->ctrls.handler.lock = &ov5693->lock; -+ ov5693->sd.ctrl_handler = &ov5693->ctrls.handler; -+ -+ return 0; -+ -+err_free_handler: -+ v4l2_ctrl_handler_free(&ov5693->ctrls.handler); -+ return ret; -+} -+ -+static int ov5693_configure_gpios(struct ov5693_device *ov5693) -+{ -+ ov5693->reset = devm_gpiod_get_optional(ov5693->dev, "reset", -+ GPIOD_OUT_HIGH); -+ if (IS_ERR(ov5693->reset)) { -+ dev_err(ov5693->dev, "Error fetching reset GPIO\n"); -+ return PTR_ERR(ov5693->reset); -+ } -+ -+ ov5693->powerdown = devm_gpiod_get_optional(ov5693->dev, "powerdown", -+ GPIOD_OUT_HIGH); -+ if (IS_ERR(ov5693->powerdown)) { -+ dev_err(ov5693->dev, "Error fetching powerdown GPIO\n"); -+ return PTR_ERR(ov5693->powerdown); -+ } -+ -+ return 0; -+} -+ -+static int ov5693_get_regulators(struct ov5693_device *ov5693) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < OV5693_NUM_SUPPLIES; i++) -+ ov5693->supplies[i].supply = ov5693_supply_names[i]; -+ -+ return devm_regulator_bulk_get(ov5693->dev, OV5693_NUM_SUPPLIES, -+ ov5693->supplies); -+} -+ -+static int ov5693_probe(struct i2c_client *client) -+{ -+ struct fwnode_handle *fwnode = dev_fwnode(&client->dev); -+ struct fwnode_handle *endpoint; -+ struct ov5693_device *ov5693; -+ u32 clk_rate; -+ int ret = 0; -+ -+ endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL); -+ if (!endpoint && !IS_ERR_OR_NULL(fwnode->secondary)) -+ endpoint = fwnode_graph_get_next_endpoint(fwnode->secondary, NULL); -+ if (!endpoint) -+ return -EPROBE_DEFER; -+ -+ ov5693 = devm_kzalloc(&client->dev, sizeof(*ov5693), GFP_KERNEL); -+ if (!ov5693) -+ return -ENOMEM; -+ -+ ov5693->client = client; -+ ov5693->dev = &client->dev; -+ -+ mutex_init(&ov5693->lock); -+ -+ v4l2_i2c_subdev_init(&ov5693->sd, client, &ov5693_ops); -+ -+ ov5693->clk = devm_clk_get(&client->dev, "xvclk"); -+ if (IS_ERR(ov5693->clk)) { -+ dev_err(&client->dev, "Error getting clock\n"); -+ return PTR_ERR(ov5693->clk); -+ } -+ -+ clk_rate = clk_get_rate(ov5693->clk); -+ if (clk_rate != OV5693_XVCLK_FREQ) { -+ dev_err(&client->dev, "Unsupported clk freq %u, expected %u\n", -+ clk_rate, OV5693_XVCLK_FREQ); -+ return -EINVAL; -+ } -+ -+ ret = ov5693_configure_gpios(ov5693); -+ if (ret) -+ return ret; -+ -+ ret = ov5693_get_regulators(ov5693); -+ if (ret) { -+ dev_err(&client->dev, "Error fetching regulators\n"); -+ return ret; -+ } -+ -+ ov5693->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; -+ ov5693->pad.flags = MEDIA_PAD_FL_SOURCE; -+ ov5693->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; -+ -+ ov5693->mode.crop = ov5693_default_crop; -+ ov5693->mode.format = ov5693_default_fmt; -+ ov5693->mode.vts = __ov5693_calc_vts(ov5693->mode.format.height); -+ -+ ret = ov5693_init_controls(ov5693); -+ if (ret) -+ return ret; -+ -+ ret = media_entity_pads_init(&ov5693->sd.entity, 1, &ov5693->pad); -+ if (ret) -+ goto err_ctrl_handler_free; -+ -+ /* -+ * We need the driver to work in the event that pm runtime is disable in -+ * the kernel, so power up and verify the chip now. In the event that -+ * runtime pm is disabled this will leave the chip on, so that streaming -+ * will work. -+ */ -+ -+ ret = ov5693_sensor_powerup(ov5693); -+ if (ret) -+ goto err_media_entity_cleanup; -+ -+ ret = ov5693_detect(ov5693); -+ if (ret) -+ goto err_powerdown; -+ -+ pm_runtime_set_active(&client->dev); -+ pm_runtime_get_noresume(&client->dev); -+ pm_runtime_enable(&client->dev); -+ -+ ret = v4l2_async_register_subdev_sensor(&ov5693->sd); -+ if (ret) { -+ dev_err(&client->dev, "failed to register V4L2 subdev: %d", -+ ret); -+ goto err_pm_runtime; -+ } -+ -+ pm_runtime_set_autosuspend_delay(&client->dev, 1000); -+ pm_runtime_use_autosuspend(&client->dev); -+ pm_runtime_put_autosuspend(&client->dev); -+ -+ return ret; -+ -+err_pm_runtime: -+ pm_runtime_disable(&client->dev); -+ pm_runtime_put_noidle(&client->dev); -+err_powerdown: -+ ov5693_sensor_powerdown(ov5693); -+err_media_entity_cleanup: -+ media_entity_cleanup(&ov5693->sd.entity); -+err_ctrl_handler_free: -+ v4l2_ctrl_handler_free(&ov5693->ctrls.handler); -+ -+ return ret; -+} -+ -+static int ov5693_remove(struct i2c_client *client) -+{ -+ struct v4l2_subdev *sd = i2c_get_clientdata(client); -+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd); -+ -+ v4l2_async_unregister_subdev(sd); -+ media_entity_cleanup(&ov5693->sd.entity); -+ v4l2_ctrl_handler_free(&ov5693->ctrls.handler); -+ mutex_destroy(&ov5693->lock); -+ -+ /* -+ * Disable runtime PM. In case runtime PM is disabled in the kernel, -+ * make sure to turn power off manually. -+ */ -+ pm_runtime_disable(&client->dev); -+ if (!pm_runtime_status_suspended(&client->dev)) -+ ov5693_sensor_powerdown(ov5693); -+ pm_runtime_set_suspended(&client->dev); -+ -+ return 0; -+} -+ -+static const struct dev_pm_ops ov5693_pm_ops = { -+ SET_RUNTIME_PM_OPS(ov5693_sensor_suspend, ov5693_sensor_resume, NULL) -+}; -+ -+static const struct acpi_device_id ov5693_acpi_match[] = { -+ {"INT33BE"}, -+ {}, -+}; -+MODULE_DEVICE_TABLE(acpi, ov5693_acpi_match); -+ -+static struct i2c_driver ov5693_driver = { -+ .driver = { -+ .name = "ov5693", -+ .acpi_match_table = ov5693_acpi_match, -+ .pm = &ov5693_pm_ops, -+ }, -+ .probe_new = ov5693_probe, -+ .remove = ov5693_remove, -+}; -+module_i2c_driver(ov5693_driver); -+ -+MODULE_DESCRIPTION("A low-level driver for OmniVision 5693 sensors"); -+MODULE_LICENSE("GPL"); --- -2.35.1 - -From 2dfb5edab4b1eb1266634e5c936fcbc45b82118a Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Thu, 20 May 2021 23:31:04 +0100 -Subject: [PATCH] media: i2c: Fix vertical flip in ov5693 - -The pinkness experienced by users with rotated sensors in their laptops -was due to an incorrect setting for the vertical flip function; the -datasheet for the sensor gives the settings as bits 1&2 in one place and -bits 1&6 in another. - -Switch to flipping bit 6 instead of bit 2 for 0x3820 in the vertical -flip function to fix the pink hue. - -Signed-off-by: Daniel Scally -Patchset: cameras ---- - drivers/media/i2c/ov5693.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c -index 9499ee10f56c..c558f9b48c83 100644 ---- a/drivers/media/i2c/ov5693.c -+++ b/drivers/media/i2c/ov5693.c -@@ -133,7 +133,7 @@ - #define OV5693_SUB_INC_Y_REG 0x3815 - - #define OV5693_FORMAT1_REG 0x3820 --#define OV5693_FORMAT1_FLIP_VERT_ISP_EN BIT(2) -+#define OV5693_FORMAT1_FLIP_VERT_ISP_EN BIT(6) - #define OV5693_FORMAT1_FLIP_VERT_SENSOR_EN BIT(1) - #define OV5693_FORMAT1_VBIN_EN BIT(0) - #define OV5693_FORMAT2_REG 0x3821 --- -2.35.1 - -From bcd317183156badc42bdb64701c35217cde13a79 Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Fri, 9 Jul 2021 16:39:18 +0100 -Subject: [PATCH] media: i2c: Add ACPI support to ov8865 - -The ov8865 sensor is sometimes found on x86 platforms enumerated via ACPI. -Add an ACPI match table to the driver so that it's probed on those -platforms. - -Signed-off-by: Daniel Scally -Patchset: cameras ---- - drivers/media/i2c/ov8865.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c -index 92f6c3a940cf..15325df45c2b 100644 ---- a/drivers/media/i2c/ov8865.c -+++ b/drivers/media/i2c/ov8865.c -@@ -9,6 +9,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -2950,6 +2951,12 @@ static const struct dev_pm_ops ov8865_pm_ops = { - SET_RUNTIME_PM_OPS(ov8865_suspend, ov8865_resume, NULL) - }; - -+static const struct acpi_device_id ov8865_acpi_match[] = { -+ {"INT347A"}, -+ { } -+}; -+MODULE_DEVICE_TABLE(acpi, ov8865_acpi_match); -+ - static const struct of_device_id ov8865_of_match[] = { - { .compatible = "ovti,ov8865" }, - { } -@@ -2960,6 +2967,7 @@ static struct i2c_driver ov8865_driver = { - .driver = { - .name = "ov8865", - .of_match_table = ov8865_of_match, -+ .acpi_match_table = ov8865_acpi_match, - .pm = &ov8865_pm_ops, - }, - .probe_new = ov8865_probe, --- -2.35.1 - -From 6e0c4749bc35cfe6519d2343c900ccccaea803dd Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Sat, 10 Jul 2021 21:20:17 +0100 -Subject: [PATCH] media: i2c: Fix incorrect value in comment - -The PLL configuration defined here sets 72MHz (which is correct), not -80MHz. Correct the comment. - -Reviewed-by: Paul Kocialkowski -Signed-off-by: Daniel Scally -Patchset: cameras ---- - drivers/media/i2c/ov8865.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c -index 15325df45c2b..8dcdf29be25e 100644 ---- a/drivers/media/i2c/ov8865.c -+++ b/drivers/media/i2c/ov8865.c -@@ -713,7 +713,7 @@ static const struct ov8865_pll2_config ov8865_pll2_config_native = { - /* - * EXTCLK = 24 MHz - * DAC_CLK = 360 MHz -- * SCLK = 80 MHz -+ * SCLK = 72 MHz - */ - - static const struct ov8865_pll2_config ov8865_pll2_config_binning = { --- -2.35.1 - -From 223d62642bfdc447a0551d3959c98912263d4dca Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Sat, 10 Jul 2021 22:21:52 +0100 -Subject: [PATCH] media: i2c: Defer probe if not endpoint found - -The ov8865 driver is one of those that can be connected to a CIO2 -device by the cio2-bridge code. This means that the absence of an -endpoint for this device is not necessarily fatal, as one might be -built by the cio2-bridge when it probes. Return -EPROBE_DEFER if no -endpoint is found rather than a fatal error. - -Signed-off-by: Daniel Scally -Patchset: cameras ---- - drivers/media/i2c/ov8865.c | 6 ++---- - 1 file changed, 2 insertions(+), 4 deletions(-) - -diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c -index 8dcdf29be25e..ceb9a93b043a 100644 ---- a/drivers/media/i2c/ov8865.c -+++ b/drivers/media/i2c/ov8865.c -@@ -2798,10 +2798,8 @@ static int ov8865_probe(struct i2c_client *client) - /* Graph Endpoint */ - - handle = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); -- if (!handle) { -- dev_err(dev, "unable to find endpoint node\n"); -- return -EINVAL; -- } -+ if (!handle) -+ return -EPROBE_DEFER; - - sensor->endpoint.bus_type = V4L2_MBUS_CSI2_DPHY; - --- -2.35.1 - -From e4f13264f7e7c91ef0baf87d3be44a244858e7be Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Sat, 10 Jul 2021 22:00:25 +0100 -Subject: [PATCH] media: i2c: Support 19.2MHz input clock in ov8865 - -The ov8865 driver as written expects a 24MHz input clock, but the sensor -is sometimes found on x86 platforms with a 19.2MHz input clock supplied. -Add a set of PLL configurations to the driver to support that rate too. -As ACPI doesn't auto-configure the clock rate, check for a clock-frequency -during probe and set that rate if one is found. - -Signed-off-by: Daniel Scally -Patchset: cameras ---- - drivers/media/i2c/ov8865.c | 186 +++++++++++++++++++++++++++---------- - 1 file changed, 135 insertions(+), 51 deletions(-) - -diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c -index ceb9a93b043a..9bac32efa7fa 100644 ---- a/drivers/media/i2c/ov8865.c -+++ b/drivers/media/i2c/ov8865.c -@@ -21,10 +21,6 @@ - #include - #include - --/* Clock rate */ -- --#define OV8865_EXTCLK_RATE 24000000 -- - /* Register definitions */ - - /* System */ -@@ -567,6 +563,25 @@ struct ov8865_sclk_config { - unsigned int sclk_div; - }; - -+struct ov8865_pll_configs { -+ const struct ov8865_pll1_config *pll1_config; -+ const struct ov8865_pll2_config *pll2_config_native; -+ const struct ov8865_pll2_config *pll2_config_binning; -+}; -+ -+/* Clock rate */ -+ -+enum extclk_rate { -+ OV8865_19_2_MHZ, -+ OV8865_24_MHZ, -+ OV8865_NUM_SUPPORTED_RATES -+}; -+ -+static const unsigned long supported_extclk_rates[] = { -+ [OV8865_19_2_MHZ] = 19200000, -+ [OV8865_24_MHZ] = 24000000, -+}; -+ - /* - * General formulas for (array-centered) mode calculation: - * - photo_array_width = 3296 -@@ -635,9 +650,7 @@ struct ov8865_mode { - - struct v4l2_fract frame_interval; - -- const struct ov8865_pll1_config *pll1_config; -- const struct ov8865_pll2_config *pll2_config; -- const struct ov8865_sclk_config *sclk_config; -+ bool pll2_binning; - - const struct ov8865_register_value *register_values; - unsigned int register_values_count; -@@ -665,6 +678,9 @@ struct ov8865_sensor { - struct regulator *avdd; - struct regulator *dvdd; - struct regulator *dovdd; -+ -+ unsigned long extclk_rate; -+ const struct ov8865_pll_configs *pll_configs; - struct clk *extclk; - - struct v4l2_fwnode_endpoint endpoint; -@@ -680,43 +696,70 @@ struct ov8865_sensor { - /* Static definitions */ - - /* -- * EXTCLK = 24 MHz - * PHY_SCLK = 720 MHz - * MIPI_PCLK = 90 MHz - */ --static const struct ov8865_pll1_config ov8865_pll1_config_native = { -- .pll_pre_div_half = 1, -- .pll_pre_div = 0, -- .pll_mul = 30, -- .m_div = 1, -- .mipi_div = 3, -- .pclk_div = 1, -- .sys_pre_div = 1, -- .sys_div = 2, -+ -+static const struct ov8865_pll1_config ov8865_pll1_config_native_19_2mhz = { -+ .pll_pre_div_half = 1, -+ .pll_pre_div = 2, -+ .pll_mul = 75, -+ .m_div = 1, -+ .mipi_div = 3, -+ .pclk_div = 1, -+ .sys_pre_div = 1, -+ .sys_div = 2, -+}; -+ -+static const struct ov8865_pll1_config ov8865_pll1_config_native_24mhz = { -+ .pll_pre_div_half = 1, -+ .pll_pre_div = 0, -+ .pll_mul = 30, -+ .m_div = 1, -+ .mipi_div = 3, -+ .pclk_div = 1, -+ .sys_pre_div = 1, -+ .sys_div = 2, - }; - - /* -- * EXTCLK = 24 MHz - * DAC_CLK = 360 MHz - * SCLK = 144 MHz - */ - --static const struct ov8865_pll2_config ov8865_pll2_config_native = { -- .pll_pre_div_half = 1, -- .pll_pre_div = 0, -- .pll_mul = 30, -- .dac_div = 2, -- .sys_pre_div = 5, -- .sys_div = 0, -+static const struct ov8865_pll2_config ov8865_pll2_config_native_19_2mhz = { -+ .pll_pre_div_half = 1, -+ .pll_pre_div = 5, -+ .pll_mul = 75, -+ .dac_div = 1, -+ .sys_pre_div = 1, -+ .sys_div = 3, -+}; -+ -+static const struct ov8865_pll2_config ov8865_pll2_config_native_24mhz = { -+ .pll_pre_div_half = 1, -+ .pll_pre_div = 0, -+ .pll_mul = 30, -+ .dac_div = 2, -+ .sys_pre_div = 5, -+ .sys_div = 0, - }; - - /* -- * EXTCLK = 24 MHz - * DAC_CLK = 360 MHz - * SCLK = 72 MHz - */ - --static const struct ov8865_pll2_config ov8865_pll2_config_binning = { -+static const struct ov8865_pll2_config ov8865_pll2_config_binning_19_2mhz = { -+ .pll_pre_div_half = 1, -+ .pll_pre_div = 2, -+ .pll_mul = 75, -+ .dac_div = 2, -+ .sys_pre_div = 10, -+ .sys_div = 0, -+}; -+ -+static const struct ov8865_pll2_config ov8865_pll2_config_binning_24mhz = { - .pll_pre_div_half = 1, - .pll_pre_div = 0, - .pll_mul = 30, -@@ -725,6 +768,23 @@ static const struct ov8865_pll2_config ov8865_pll2_config_binning = { - .sys_div = 0, - }; - -+static struct ov8865_pll_configs ov8865_pll_configs_19_2mhz = { -+ .pll1_config = &ov8865_pll1_config_native_19_2mhz, -+ .pll2_config_native = &ov8865_pll2_config_native_19_2mhz, -+ .pll2_config_binning = &ov8865_pll2_config_binning_19_2mhz, -+}; -+ -+static struct ov8865_pll_configs ov8865_pll_configs_24mhz = { -+ .pll1_config = &ov8865_pll1_config_native_24mhz, -+ .pll2_config_native = &ov8865_pll2_config_native_24mhz, -+ .pll2_config_binning = &ov8865_pll2_config_binning_24mhz, -+}; -+ -+static const struct ov8865_pll_configs *ov8865_pll_configs[] = { -+ &ov8865_pll_configs_19_2mhz, -+ &ov8865_pll_configs_24mhz, -+}; -+ - static const struct ov8865_sclk_config ov8865_sclk_config_native = { - .sys_sel = 1, - .sclk_sel = 0, -@@ -934,9 +994,7 @@ static const struct ov8865_mode ov8865_modes[] = { - .frame_interval = { 1, 30 }, - - /* PLL */ -- .pll1_config = &ov8865_pll1_config_native, -- .pll2_config = &ov8865_pll2_config_native, -- .sclk_config = &ov8865_sclk_config_native, -+ .pll2_binning = false, - - /* Registers */ - .register_values = ov8865_register_values_native, -@@ -990,9 +1048,7 @@ static const struct ov8865_mode ov8865_modes[] = { - .frame_interval = { 1, 30 }, - - /* PLL */ -- .pll1_config = &ov8865_pll1_config_native, -- .pll2_config = &ov8865_pll2_config_native, -- .sclk_config = &ov8865_sclk_config_native, -+ .pll2_binning = false, - - /* Registers */ - .register_values = ov8865_register_values_native, -@@ -1050,9 +1106,7 @@ static const struct ov8865_mode ov8865_modes[] = { - .frame_interval = { 1, 30 }, - - /* PLL */ -- .pll1_config = &ov8865_pll1_config_native, -- .pll2_config = &ov8865_pll2_config_binning, -- .sclk_config = &ov8865_sclk_config_native, -+ .pll2_binning = true, - - /* Registers */ - .register_values = ov8865_register_values_binning, -@@ -1116,9 +1170,7 @@ static const struct ov8865_mode ov8865_modes[] = { - .frame_interval = { 1, 90 }, - - /* PLL */ -- .pll1_config = &ov8865_pll1_config_native, -- .pll2_config = &ov8865_pll2_config_binning, -- .sclk_config = &ov8865_sclk_config_native, -+ .pll2_binning = true, - - /* Registers */ - .register_values = ov8865_register_values_binning, -@@ -1513,12 +1565,11 @@ static int ov8865_isp_configure(struct ov8865_sensor *sensor) - static unsigned long ov8865_mode_pll1_rate(struct ov8865_sensor *sensor, - const struct ov8865_mode *mode) - { -- const struct ov8865_pll1_config *config = mode->pll1_config; -- unsigned long extclk_rate; -+ const struct ov8865_pll1_config *config; - unsigned long pll1_rate; - -- extclk_rate = clk_get_rate(sensor->extclk); -- pll1_rate = extclk_rate * config->pll_mul / config->pll_pre_div_half; -+ config = sensor->pll_configs->pll1_config; -+ pll1_rate = sensor->extclk_rate * config->pll_mul / config->pll_pre_div_half; - - switch (config->pll_pre_div) { - case 0: -@@ -1552,10 +1603,12 @@ static int ov8865_mode_pll1_configure(struct ov8865_sensor *sensor, - const struct ov8865_mode *mode, - u32 mbus_code) - { -- const struct ov8865_pll1_config *config = mode->pll1_config; -+ const struct ov8865_pll1_config *config; - u8 value; - int ret; - -+ config = sensor->pll_configs->pll1_config; -+ - switch (mbus_code) { - case MEDIA_BUS_FMT_SBGGR10_1X10: - value = OV8865_MIPI_BIT_SEL(10); -@@ -1622,9 +1675,12 @@ static int ov8865_mode_pll1_configure(struct ov8865_sensor *sensor, - static int ov8865_mode_pll2_configure(struct ov8865_sensor *sensor, - const struct ov8865_mode *mode) - { -- const struct ov8865_pll2_config *config = mode->pll2_config; -+ const struct ov8865_pll2_config *config; - int ret; - -+ config = mode->pll2_binning ? sensor->pll_configs->pll2_config_binning : -+ sensor->pll_configs->pll2_config_native; -+ - ret = ov8865_write(sensor, OV8865_PLL_CTRL12_REG, - OV8865_PLL_CTRL12_PRE_DIV_HALF(config->pll_pre_div_half) | - OV8865_PLL_CTRL12_DAC_DIV(config->dac_div)); -@@ -1658,7 +1714,7 @@ static int ov8865_mode_pll2_configure(struct ov8865_sensor *sensor, - static int ov8865_mode_sclk_configure(struct ov8865_sensor *sensor, - const struct ov8865_mode *mode) - { -- const struct ov8865_sclk_config *config = mode->sclk_config; -+ const struct ov8865_sclk_config *config = &ov8865_sclk_config_native; - int ret; - - ret = ov8865_write(sensor, OV8865_CLK_SEL0_REG, -@@ -2053,9 +2109,11 @@ static int ov8865_mode_configure(struct ov8865_sensor *sensor, - static unsigned long ov8865_mode_mipi_clk_rate(struct ov8865_sensor *sensor, - const struct ov8865_mode *mode) - { -- const struct ov8865_pll1_config *config = mode->pll1_config; -+ const struct ov8865_pll1_config *config; - unsigned long pll1_rate; - -+ config = sensor->pll_configs->pll1_config; -+ - pll1_rate = ov8865_mode_pll1_rate(sensor, mode); - - return pll1_rate / config->m_div / 2; -@@ -2785,7 +2843,8 @@ static int ov8865_probe(struct i2c_client *client) - struct ov8865_sensor *sensor; - struct v4l2_subdev *subdev; - struct media_pad *pad; -- unsigned long rate; -+ unsigned int rate; -+ unsigned int i; - int ret; - - sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); -@@ -2860,13 +2919,38 @@ static int ov8865_probe(struct i2c_client *client) - goto error_endpoint; - } - -- rate = clk_get_rate(sensor->extclk); -- if (rate != OV8865_EXTCLK_RATE) { -- dev_err(dev, "clock rate %lu Hz is unsupported\n", rate); -+ /* -+ * We could have either a 24MHz or 19.2MHz clock rate. Check for a -+ * clock-frequency property and if found, set that rate. This should -+ * cover the ACPI case. If the system uses devicetree then the -+ * configured rate should already be set, so we'll have to check it. -+ */ -+ ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", -+ &rate); -+ if (!ret) { -+ ret = clk_set_rate(sensor->extclk, rate); -+ if (ret) { -+ dev_err(dev, "failed to set clock rate\n"); -+ return ret; -+ } -+ } -+ -+ sensor->extclk_rate = clk_get_rate(sensor->extclk); -+ -+ for (i = 0; i < ARRAY_SIZE(supported_extclk_rates); i++) { -+ if (sensor->extclk_rate == supported_extclk_rates[i]) -+ break; -+ } -+ -+ if (i == ARRAY_SIZE(supported_extclk_rates)) { -+ dev_err(dev, "clock rate %lu Hz is unsupported\n", -+ sensor->extclk_rate); - ret = -EINVAL; - goto error_endpoint; - } - -+ sensor->pll_configs = ov8865_pll_configs[i]; -+ - /* Subdev, entity and pad */ - - subdev = &sensor->subdev; --- -2.35.1 - -From ec4fb28d4b75ed9cde1a7a13671d807797ed3b29 Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Sat, 10 Jul 2021 22:19:10 +0100 -Subject: [PATCH] media: i2c: Add .get_selection() support to ov8865 - -The ov8865 driver's v4l2_subdev_pad_ops currently does not include -.get_selection() - add support for that callback. - -Signed-off-by: Daniel Scally -Patchset: cameras ---- - drivers/media/i2c/ov8865.c | 64 ++++++++++++++++++++++++++++++++++++++ - 1 file changed, 64 insertions(+) - -diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c -index 9bac32efa7fa..d41ce6b5af55 100644 ---- a/drivers/media/i2c/ov8865.c -+++ b/drivers/media/i2c/ov8865.c -@@ -450,6 +450,15 @@ - #define OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES 2 - #define OV8865_PRE_CTRL0_PATTERN_BLACK 3 - -+/* Pixel Array */ -+ -+#define OV8865_NATIVE_WIDTH 3296 -+#define OV8865_NATIVE_HEIGHT 2528 -+#define OV8865_ACTIVE_START_TOP 32 -+#define OV8865_ACTIVE_START_LEFT 80 -+#define OV8865_ACTIVE_WIDTH 3264 -+#define OV8865_ACTIVE_HEIGHT 2448 -+ - /* Macros */ - - #define ov8865_subdev_sensor(s) \ -@@ -2758,12 +2767,67 @@ static int ov8865_enum_frame_interval(struct v4l2_subdev *subdev, - return 0; - } - -+static void -+__ov8865_get_pad_crop(struct ov8865_sensor *sensor, -+ struct v4l2_subdev_state *state, unsigned int pad, -+ enum v4l2_subdev_format_whence which, struct v4l2_rect *r) -+{ -+ const struct ov8865_mode *mode = sensor->state.mode; -+ -+ switch (which) { -+ case V4L2_SUBDEV_FORMAT_TRY: -+ *r = *v4l2_subdev_get_try_crop(&sensor->subdev, state, pad); -+ break; -+ case V4L2_SUBDEV_FORMAT_ACTIVE: -+ r->height = mode->output_size_y; -+ r->width = mode->output_size_x; -+ r->top = (OV8865_NATIVE_HEIGHT - mode->output_size_y) / 2; -+ r->left = (OV8865_NATIVE_WIDTH - mode->output_size_x) / 2; -+ break; -+ } -+} -+ -+static int ov8865_get_selection(struct v4l2_subdev *subdev, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_selection *sel) -+{ -+ struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); -+ -+ switch (sel->target) { -+ case V4L2_SEL_TGT_CROP: -+ mutex_lock(&sensor->mutex); -+ __ov8865_get_pad_crop(sensor, state, sel->pad, -+ sel->which, &sel->r); -+ mutex_unlock(&sensor->mutex); -+ break; -+ case V4L2_SEL_TGT_NATIVE_SIZE: -+ sel->r.top = 0; -+ sel->r.left = 0; -+ sel->r.width = OV8865_NATIVE_WIDTH; -+ sel->r.height = OV8865_NATIVE_HEIGHT; -+ break; -+ case V4L2_SEL_TGT_CROP_BOUNDS: -+ case V4L2_SEL_TGT_CROP_DEFAULT: -+ sel->r.top = OV8865_ACTIVE_START_TOP; -+ sel->r.left = OV8865_ACTIVE_START_LEFT; -+ sel->r.width = OV8865_ACTIVE_WIDTH; -+ sel->r.height = OV8865_ACTIVE_HEIGHT; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ - static const struct v4l2_subdev_pad_ops ov8865_subdev_pad_ops = { - .enum_mbus_code = ov8865_enum_mbus_code, - .get_fmt = ov8865_get_fmt, - .set_fmt = ov8865_set_fmt, - .enum_frame_size = ov8865_enum_frame_size, - .enum_frame_interval = ov8865_enum_frame_interval, -+ .get_selection = ov8865_get_selection, -+ .set_selection = ov8865_get_selection, - }; - - static const struct v4l2_subdev_ops ov8865_subdev_ops = { --- -2.35.1 - -From 62c2d0b223798ec53f80a7311ffabaa3564491bf Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Sat, 10 Jul 2021 22:34:43 +0100 -Subject: [PATCH] media: i2c: Switch control to V4L2_CID_ANALOGUE_GAIN - -The V4L2_CID_GAIN control for this driver configures registers that -the datasheet specifies as analogue gain. Switch the control's ID -to V4L2_CID_ANALOGUE_GAIN. - -Reviewed-by: Paul Kocialkowski -Signed-off-by: Daniel Scally -Patchset: cameras ---- - drivers/media/i2c/ov8865.c | 9 +++++---- - 1 file changed, 5 insertions(+), 4 deletions(-) - -diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c -index d41ce6b5af55..07f34f3ae5ec 100644 ---- a/drivers/media/i2c/ov8865.c -+++ b/drivers/media/i2c/ov8865.c -@@ -2150,7 +2150,7 @@ static int ov8865_exposure_configure(struct ov8865_sensor *sensor, u32 exposure) - - /* Gain */ - --static int ov8865_gain_configure(struct ov8865_sensor *sensor, u32 gain) -+static int ov8865_analog_gain_configure(struct ov8865_sensor *sensor, u32 gain) - { - int ret; - -@@ -2462,8 +2462,8 @@ static int ov8865_s_ctrl(struct v4l2_ctrl *ctrl) - if (ret) - return ret; - break; -- case V4L2_CID_GAIN: -- ret = ov8865_gain_configure(sensor, ctrl->val); -+ case V4L2_CID_ANALOGUE_GAIN: -+ ret = ov8865_analog_gain_configure(sensor, ctrl->val); - if (ret) - return ret; - break; -@@ -2508,7 +2508,8 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor) - - /* Gain */ - -- v4l2_ctrl_new_std(handler, ops, V4L2_CID_GAIN, 128, 8191, 128, 128); -+ v4l2_ctrl_new_std(handler, ops, V4L2_CID_ANALOGUE_GAIN, 128, 8191, 128, -+ 128); - - /* White Balance */ - --- -2.35.1 - -From e677005e1d45907454dfdd12b2ec2fa8160f92ab Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Mon, 12 Jul 2021 22:54:56 +0100 -Subject: [PATCH] media: i2c: Add vblank control to ov8865 - -Add a V4L2_CID_VBLANK control to the ov8865 driver. - -Signed-off-by: Daniel Scally -Patchset: cameras ---- - drivers/media/i2c/ov8865.c | 34 ++++++++++++++++++++++++++++++++++ - 1 file changed, 34 insertions(+) - -diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c -index 07f34f3ae5ec..95c1b97eb89a 100644 ---- a/drivers/media/i2c/ov8865.c -+++ b/drivers/media/i2c/ov8865.c -@@ -183,6 +183,8 @@ - #define OV8865_VTS_H(v) (((v) & GENMASK(11, 8)) >> 8) - #define OV8865_VTS_L_REG 0x380f - #define OV8865_VTS_L(v) ((v) & GENMASK(7, 0)) -+#define OV8865_TIMING_MAX_VTS 0xffff -+#define OV8865_TIMING_MIN_VTS 0x04 - #define OV8865_OFFSET_X_H_REG 0x3810 - #define OV8865_OFFSET_X_H(v) (((v) & GENMASK(15, 8)) >> 8) - #define OV8865_OFFSET_X_L_REG 0x3811 -@@ -675,6 +677,7 @@ struct ov8865_state { - struct ov8865_ctrls { - struct v4l2_ctrl *link_freq; - struct v4l2_ctrl *pixel_rate; -+ struct v4l2_ctrl *vblank; - - struct v4l2_ctrl_handler handler; - }; -@@ -2225,6 +2228,20 @@ static int ov8865_test_pattern_configure(struct ov8865_sensor *sensor, - ov8865_test_pattern_bits[index]); - } - -+/* Blanking */ -+ -+static int ov8865_vts_configure(struct ov8865_sensor *sensor, u32 vblank) -+{ -+ u16 vts = sensor->state.mode->output_size_y + vblank; -+ int ret; -+ -+ ret = ov8865_write(sensor, OV8865_VTS_H_REG, OV8865_VTS_H(vts)); -+ if (ret) -+ return ret; -+ -+ return ov8865_write(sensor, OV8865_VTS_L_REG, OV8865_VTS_L(vts)); -+} -+ - /* State */ - - static int ov8865_state_mipi_configure(struct ov8865_sensor *sensor, -@@ -2478,6 +2495,8 @@ static int ov8865_s_ctrl(struct v4l2_ctrl *ctrl) - case V4L2_CID_TEST_PATTERN: - index = (unsigned int)ctrl->val; - return ov8865_test_pattern_configure(sensor, index); -+ case V4L2_CID_VBLANK: -+ return ov8865_vts_configure(sensor, ctrl->val); - default: - return -EINVAL; - } -@@ -2494,6 +2513,8 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor) - struct ov8865_ctrls *ctrls = &sensor->ctrls; - struct v4l2_ctrl_handler *handler = &ctrls->handler; - const struct v4l2_ctrl_ops *ops = &ov8865_ctrl_ops; -+ const struct ov8865_mode *mode = sensor->state.mode; -+ unsigned int vblank_max, vblank_def; - int ret; - - v4l2_ctrl_handler_init(handler, 32); -@@ -2530,6 +2551,13 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor) - ARRAY_SIZE(ov8865_test_pattern_menu) - 1, - 0, 0, ov8865_test_pattern_menu); - -+ /* Blanking */ -+ vblank_max = OV8865_TIMING_MAX_VTS - mode->output_size_y; -+ vblank_def = mode->vts - mode->output_size_y; -+ ctrls->vblank = v4l2_ctrl_new_std(handler, ops, V4L2_CID_VBLANK, -+ OV8865_TIMING_MIN_VTS, vblank_max, 1, -+ vblank_def); -+ - /* MIPI CSI-2 */ - - ctrls->link_freq = -@@ -2710,6 +2738,10 @@ static int ov8865_set_fmt(struct v4l2_subdev *subdev, - sensor->state.mbus_code != mbus_code) - ret = ov8865_state_configure(sensor, mode, mbus_code); - -+ __v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV8865_TIMING_MIN_VTS, -+ OV8865_TIMING_MAX_VTS - mode->output_size_y, -+ 1, mode->vts - mode->output_size_y); -+ - complete: - mutex_unlock(&sensor->mutex); - -@@ -3037,6 +3069,8 @@ static int ov8865_probe(struct i2c_client *client) - - /* Sensor */ - -+ sensor->state.mode = &ov8865_modes[0]; -+ - ret = ov8865_ctrls_init(sensor); - if (ret) - goto error_mutex; --- -2.35.1 - -From e03db43d2992c707235179319ba3adfe3da4441e Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Tue, 13 Jul 2021 23:40:33 +0100 -Subject: [PATCH] media: i2c: Add hblank control to ov8865 - -Add a V4L2_CID_HBLANK control to the ov8865 driver. This is read only -with timing control intended to be done via vblanking alone. - -Signed-off-by: Daniel Scally -Patchset: cameras ---- - drivers/media/i2c/ov8865.c | 14 ++++++++++++++ - 1 file changed, 14 insertions(+) - -diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c -index 95c1b97eb89a..85a76aea67a5 100644 ---- a/drivers/media/i2c/ov8865.c -+++ b/drivers/media/i2c/ov8865.c -@@ -677,6 +677,7 @@ struct ov8865_state { - struct ov8865_ctrls { - struct v4l2_ctrl *link_freq; - struct v4l2_ctrl *pixel_rate; -+ struct v4l2_ctrl *hblank; - struct v4l2_ctrl *vblank; - - struct v4l2_ctrl_handler handler; -@@ -2515,6 +2516,7 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor) - const struct v4l2_ctrl_ops *ops = &ov8865_ctrl_ops; - const struct ov8865_mode *mode = sensor->state.mode; - unsigned int vblank_max, vblank_def; -+ unsigned int hblank; - int ret; - - v4l2_ctrl_handler_init(handler, 32); -@@ -2552,6 +2554,13 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor) - 0, 0, ov8865_test_pattern_menu); - - /* Blanking */ -+ hblank = mode->hts - mode->output_size_x; -+ ctrls->hblank = v4l2_ctrl_new_std(handler, ops, V4L2_CID_HBLANK, hblank, -+ hblank, 1, hblank); -+ -+ if (ctrls->hblank) -+ ctrls->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; -+ - vblank_max = OV8865_TIMING_MAX_VTS - mode->output_size_y; - vblank_def = mode->vts - mode->output_size_y; - ctrls->vblank = v4l2_ctrl_new_std(handler, ops, V4L2_CID_VBLANK, -@@ -2698,6 +2707,7 @@ static int ov8865_set_fmt(struct v4l2_subdev *subdev, - struct v4l2_mbus_framefmt *mbus_format = &format->format; - const struct ov8865_mode *mode; - u32 mbus_code = 0; -+ unsigned int hblank; - unsigned int index; - int ret = 0; - -@@ -2742,6 +2752,10 @@ static int ov8865_set_fmt(struct v4l2_subdev *subdev, - OV8865_TIMING_MAX_VTS - mode->output_size_y, - 1, mode->vts - mode->output_size_y); - -+ hblank = mode->hts - mode->output_size_x; -+ __v4l2_ctrl_modify_range(sensor->ctrls.hblank, hblank, hblank, 1, -+ hblank); -+ - complete: - mutex_unlock(&sensor->mutex); - --- -2.35.1 - -From b103821c396fd02ddbec7f58ca6af55a65725883 Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Wed, 20 Oct 2021 22:43:54 +0100 -Subject: [PATCH] media: i2c: Update HTS values in ov8865 - -The HTS values for some of the modes in the ov8865 driver are a bit -unusual, coming in lower than the output_size_x is set to. It seems -like they might be calculated to fit the desired framerate into a -configuration with just two data lanes. To bring this more in line -with expected behaviour, raise the HTS values above the output_size_x. - -The corollary of that change is that the hardcoded frame intervals -against the modes no longer make sense, so remove those entirely. -Update the .g/s_frame_interval() callbacks to calculate the frame -interval based on the current mode and the vblank and hblank settings -plus the number of data lanes detected from firmware. - -The implementation of the .enum_frame_interval() callback is no longer -suitable since the possible frame rate is now a continuous range depending -on the vblank control setting, so remove that callback entirely. - -Signed-off-by: Daniel Scally -Patchset: cameras ---- - drivers/media/i2c/ov8865.c | 65 +++++++------------------------------- - 1 file changed, 11 insertions(+), 54 deletions(-) - -diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c -index 85a76aea67a5..7f5b0c48eac4 100644 ---- a/drivers/media/i2c/ov8865.c -+++ b/drivers/media/i2c/ov8865.c -@@ -659,8 +659,6 @@ struct ov8865_mode { - unsigned int blc_anchor_right_start; - unsigned int blc_anchor_right_end; - -- struct v4l2_fract frame_interval; -- - bool pll2_binning; - - const struct ov8865_register_value *register_values; -@@ -964,7 +962,7 @@ static const struct ov8865_mode ov8865_modes[] = { - { - /* Horizontal */ - .output_size_x = 3264, -- .hts = 1944, -+ .hts = 3888, - - /* Vertical */ - .output_size_y = 2448, -@@ -1003,9 +1001,6 @@ static const struct ov8865_mode ov8865_modes[] = { - .blc_anchor_right_start = 1984, - .blc_anchor_right_end = 2239, - -- /* Frame Interval */ -- .frame_interval = { 1, 30 }, -- - /* PLL */ - .pll2_binning = false, - -@@ -1018,11 +1013,11 @@ static const struct ov8865_mode ov8865_modes[] = { - { - /* Horizontal */ - .output_size_x = 3264, -- .hts = 2582, -+ .hts = 3888, - - /* Vertical */ - .output_size_y = 1836, -- .vts = 2002, -+ .vts = 2470, - - .size_auto = true, - .size_auto_boundary_x = 8, -@@ -1057,9 +1052,6 @@ static const struct ov8865_mode ov8865_modes[] = { - .blc_anchor_right_start = 1984, - .blc_anchor_right_end = 2239, - -- /* Frame Interval */ -- .frame_interval = { 1, 30 }, -- - /* PLL */ - .pll2_binning = false, - -@@ -1115,9 +1107,6 @@ static const struct ov8865_mode ov8865_modes[] = { - .blc_anchor_right_start = 992, - .blc_anchor_right_end = 1119, - -- /* Frame Interval */ -- .frame_interval = { 1, 30 }, -- - /* PLL */ - .pll2_binning = true, - -@@ -1179,9 +1168,6 @@ static const struct ov8865_mode ov8865_modes[] = { - .blc_anchor_right_start = 992, - .blc_anchor_right_end = 1119, - -- /* Frame Interval */ -- .frame_interval = { 1, 90 }, -- - /* PLL */ - .pll2_binning = true, - -@@ -2630,11 +2616,18 @@ static int ov8865_g_frame_interval(struct v4l2_subdev *subdev, - { - struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); - const struct ov8865_mode *mode; -+ unsigned int framesize; -+ unsigned int fps; - - mutex_lock(&sensor->mutex); - - mode = sensor->state.mode; -- interval->interval = mode->frame_interval; -+ framesize = mode->hts * (mode->output_size_y + -+ sensor->ctrls.vblank->val); -+ fps = DIV_ROUND_CLOSEST(sensor->ctrls.pixel_rate->val, framesize); -+ -+ interval->interval.numerator = 1; -+ interval->interval.denominator = fps; - - mutex_unlock(&sensor->mutex); - -@@ -2779,41 +2772,6 @@ static int ov8865_enum_frame_size(struct v4l2_subdev *subdev, - return 0; - } - --static int ov8865_enum_frame_interval(struct v4l2_subdev *subdev, -- struct v4l2_subdev_state *sd_state, -- struct v4l2_subdev_frame_interval_enum *interval_enum) --{ -- const struct ov8865_mode *mode = NULL; -- unsigned int mode_index; -- unsigned int interval_index; -- -- if (interval_enum->index > 0) -- return -EINVAL; -- /* -- * Multiple modes with the same dimensions may have different frame -- * intervals, so look up each relevant mode. -- */ -- for (mode_index = 0, interval_index = 0; -- mode_index < ARRAY_SIZE(ov8865_modes); mode_index++) { -- mode = &ov8865_modes[mode_index]; -- -- if (mode->output_size_x == interval_enum->width && -- mode->output_size_y == interval_enum->height) { -- if (interval_index == interval_enum->index) -- break; -- -- interval_index++; -- } -- } -- -- if (mode_index == ARRAY_SIZE(ov8865_modes)) -- return -EINVAL; -- -- interval_enum->interval = mode->frame_interval; -- -- return 0; --} -- - static void - __ov8865_get_pad_crop(struct ov8865_sensor *sensor, - struct v4l2_subdev_state *state, unsigned int pad, -@@ -2872,7 +2830,6 @@ static const struct v4l2_subdev_pad_ops ov8865_subdev_pad_ops = { - .get_fmt = ov8865_get_fmt, - .set_fmt = ov8865_set_fmt, - .enum_frame_size = ov8865_enum_frame_size, -- .enum_frame_interval = ov8865_enum_frame_interval, - .get_selection = ov8865_get_selection, - .set_selection = ov8865_get_selection, - }; --- -2.35.1 - -From cafb393c61f5ce95318c4c87e042f80b25f0d21d Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Tue, 13 Jul 2021 23:43:17 +0100 -Subject: [PATCH] media: i2c: cap exposure at height + vblank in ov8865 - -Exposure limits depend on the total height; when vblank is altered (and -thus the total height is altered), change the exposure limits to reflect -the new cap. - -Signed-off-by: Daniel Scally -Patchset: cameras ---- - drivers/media/i2c/ov8865.c | 26 ++++++++++++++++++++++++-- - 1 file changed, 24 insertions(+), 2 deletions(-) - -diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c -index 7f5b0c48eac4..d867676bf77e 100644 ---- a/drivers/media/i2c/ov8865.c -+++ b/drivers/media/i2c/ov8865.c -@@ -677,6 +677,7 @@ struct ov8865_ctrls { - struct v4l2_ctrl *pixel_rate; - struct v4l2_ctrl *hblank; - struct v4l2_ctrl *vblank; -+ struct v4l2_ctrl *exposure; - - struct v4l2_ctrl_handler handler; - }; -@@ -2456,6 +2457,19 @@ static int ov8865_s_ctrl(struct v4l2_ctrl *ctrl) - unsigned int index; - int ret; - -+ /* If VBLANK is altered we need to update exposure to compensate */ -+ if (ctrl->id == V4L2_CID_VBLANK) { -+ int exposure_max; -+ -+ exposure_max = sensor->state.mode->output_size_y + ctrl->val; -+ __v4l2_ctrl_modify_range(sensor->ctrls.exposure, -+ sensor->ctrls.exposure->minimum, -+ exposure_max, -+ sensor->ctrls.exposure->step, -+ min(sensor->ctrls.exposure->val, -+ exposure_max)); -+ } -+ - /* Wait for the sensor to be on before setting controls. */ - if (pm_runtime_suspended(sensor->dev)) - return 0; -@@ -2512,8 +2526,8 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor) - - /* Exposure */ - -- v4l2_ctrl_new_std(handler, ops, V4L2_CID_EXPOSURE, 16, 1048575, 16, -- 512); -+ ctrls->exposure = v4l2_ctrl_new_std(handler, ops, V4L2_CID_EXPOSURE, 16, -+ 1048575, 16, 512); - - /* Gain */ - -@@ -2702,6 +2716,7 @@ static int ov8865_set_fmt(struct v4l2_subdev *subdev, - u32 mbus_code = 0; - unsigned int hblank; - unsigned int index; -+ int exposure_max; - int ret = 0; - - mutex_lock(&sensor->mutex); -@@ -2749,6 +2764,13 @@ static int ov8865_set_fmt(struct v4l2_subdev *subdev, - __v4l2_ctrl_modify_range(sensor->ctrls.hblank, hblank, hblank, 1, - hblank); - -+ exposure_max = mode->vts; -+ __v4l2_ctrl_modify_range(sensor->ctrls.exposure, -+ sensor->ctrls.exposure->minimum, exposure_max, -+ sensor->ctrls.exposure->step, -+ min(sensor->ctrls.exposure->val, -+ exposure_max)); -+ - complete: - mutex_unlock(&sensor->mutex); - --- -2.35.1 - -From 5039c9f43f0c57244e178fe8ba81db898c7e3640 Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Fri, 16 Jul 2021 22:56:15 +0100 -Subject: [PATCH] media: i2c: Add controls from fwnode to ov8865 - -Add V4L2_CID_CAMERA_ORIENTATION and V4L2_CID_CAMERA_SENSOR_ROTATION -controls to the ov8865 driver by attempting to parse them from firmware. - -Signed-off-by: Daniel Scally -Patchset: cameras ---- - drivers/media/i2c/ov8865.c | 10 ++++++++++ - 1 file changed, 10 insertions(+) - -diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c -index d867676bf77e..d0303016e7b4 100644 ---- a/drivers/media/i2c/ov8865.c -+++ b/drivers/media/i2c/ov8865.c -@@ -2515,6 +2515,7 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor) - struct v4l2_ctrl_handler *handler = &ctrls->handler; - const struct v4l2_ctrl_ops *ops = &ov8865_ctrl_ops; - const struct ov8865_mode *mode = sensor->state.mode; -+ struct v4l2_fwnode_device_properties props; - unsigned int vblank_max, vblank_def; - unsigned int hblank; - int ret; -@@ -2578,6 +2579,15 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor) - v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 1, - INT_MAX, 1, 1); - -+ /* set properties from fwnode (e.g. rotation, orientation) */ -+ ret = v4l2_fwnode_device_parse(sensor->dev, &props); -+ if (ret) -+ goto error_ctrls; -+ -+ ret = v4l2_ctrl_new_fwnode_properties(handler, ops, &props); -+ if (ret) -+ goto error_ctrls; -+ - if (handler->error) { - ret = handler->error; - goto error_ctrls; --- -2.35.1 - -From 1c96c757338cfa78250989826e32ba622aeb3848 Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Fri, 16 Jul 2021 00:00:54 +0100 -Subject: [PATCH] media: i2c: Switch exposure control unit to lines - -The ov8865 driver currently has the unit of the V4L2_CID_EXPOSURE control -as 1/16th of a line. This is what the sensor expects, but isn't very -intuitive. Switch the control to be in units of a line and simply do the -16x multiplication before passing the value to the sensor. - -The datasheet for this sensor gives minimum exposure as 2 lines, so take -the opportunity to correct the lower bounds of the control. - -Signed-off-by: Daniel Scally -Patchset: cameras ---- - drivers/media/i2c/ov8865.c | 7 +++++-- - 1 file changed, 5 insertions(+), 2 deletions(-) - -diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c -index d0303016e7b4..a638e53bb069 100644 ---- a/drivers/media/i2c/ov8865.c -+++ b/drivers/media/i2c/ov8865.c -@@ -2125,6 +2125,9 @@ static int ov8865_exposure_configure(struct ov8865_sensor *sensor, u32 exposure) - { - int ret; - -+ /* The sensor stores exposure in units of 1/16th of a line */ -+ exposure *= 16; -+ - ret = ov8865_write(sensor, OV8865_EXPOSURE_CTRL_HH_REG, - OV8865_EXPOSURE_CTRL_HH(exposure)); - if (ret) -@@ -2527,8 +2530,8 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor) - - /* Exposure */ - -- ctrls->exposure = v4l2_ctrl_new_std(handler, ops, V4L2_CID_EXPOSURE, 16, -- 1048575, 16, 512); -+ ctrls->exposure = v4l2_ctrl_new_std(handler, ops, V4L2_CID_EXPOSURE, 2, -+ 65535, 1, 32); - - /* Gain */ - --- -2.35.1 - -From c8f335a4e68e5d2197f3aee325fea582dc3d39b4 Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Tue, 24 Aug 2021 23:17:39 +0100 -Subject: [PATCH] media: i2c: Use dev_err_probe() in ov8865 - -There is a chance that regulator_get() returns -EPROBE_DEFER, in which -case printing an error message is undesirable. To avoid spurious messages -in dmesg in the event that -EPROBE_DEFER is returned, use dev_err_probe() -on error paths for regulator_get(). - -Signed-off-by: Daniel Scally -Patchset: cameras ---- - drivers/media/i2c/ov8865.c | 46 +++++++++++++++++--------------------- - 1 file changed, 20 insertions(+), 26 deletions(-) - -diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c -index a638e53bb069..5ef9c407362a 100644 ---- a/drivers/media/i2c/ov8865.c -+++ b/drivers/media/i2c/ov8865.c -@@ -2957,6 +2957,26 @@ static int ov8865_probe(struct i2c_client *client) - sensor->dev = dev; - sensor->i2c_client = client; - -+ /* Regulators */ -+ -+ /* DVDD: digital core */ -+ sensor->dvdd = devm_regulator_get(dev, "dvdd"); -+ if (IS_ERR(sensor->dvdd)) -+ return dev_err_probe(dev, PTR_ERR(sensor->dvdd), -+ "cannot get DVDD regulator\n"); -+ -+ /* DOVDD: digital I/O */ -+ sensor->dovdd = devm_regulator_get(dev, "dovdd"); -+ if (IS_ERR(sensor->dovdd)) -+ return dev_err_probe(dev, PTR_ERR(sensor->dovdd), -+ "cannot get DOVDD regulator\n"); -+ -+ /* AVDD: analog */ -+ sensor->avdd = devm_regulator_get(dev, "avdd"); -+ if (IS_ERR(sensor->avdd)) -+ return dev_err_probe(dev, PTR_ERR(sensor->avdd), -+ "cannot get AVDD regulator\n"); -+ - /* Graph Endpoint */ - - handle = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); -@@ -2987,32 +3007,6 @@ static int ov8865_probe(struct i2c_client *client) - goto error_endpoint; - } - -- /* Regulators */ -- -- /* DVDD: digital core */ -- sensor->dvdd = devm_regulator_get(dev, "dvdd"); -- if (IS_ERR(sensor->dvdd)) { -- dev_err(dev, "cannot get DVDD (digital core) regulator\n"); -- ret = PTR_ERR(sensor->dvdd); -- goto error_endpoint; -- } -- -- /* DOVDD: digital I/O */ -- sensor->dovdd = devm_regulator_get(dev, "dovdd"); -- if (IS_ERR(sensor->dovdd)) { -- dev_err(dev, "cannot get DOVDD (digital I/O) regulator\n"); -- ret = PTR_ERR(sensor->dovdd); -- goto error_endpoint; -- } -- -- /* AVDD: analog */ -- sensor->avdd = devm_regulator_get(dev, "avdd"); -- if (IS_ERR(sensor->avdd)) { -- dev_err(dev, "cannot get AVDD (analog) regulator\n"); -- ret = PTR_ERR(sensor->avdd); -- goto error_endpoint; -- } -- - /* External Clock */ - - sensor->extclk = devm_clk_get(dev, NULL); --- -2.35.1 - -From d6ac836da9c5181634c9d7f317d199cd81ccc085 Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Wed, 14 Jul 2021 00:05:04 +0100 -Subject: [PATCH] media: ipu3-cio2: Add INT347A to cio2-bridge - -ACPI _HID INT347A represents the OV8865 sensor, the driver for which can -support the platforms that the cio2-bridge serves. Add it to the array -of supported sensors so the bridge will connect the sensor to the CIO2 -device. - -Reviewed-by: Andy Shevchenko -Signed-off-by: Daniel Scally -Patchset: cameras ---- - drivers/media/pci/intel/ipu3/cio2-bridge.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.c b/drivers/media/pci/intel/ipu3/cio2-bridge.c -index 0b586b4e537e..4550be801311 100644 ---- a/drivers/media/pci/intel/ipu3/cio2-bridge.c -+++ b/drivers/media/pci/intel/ipu3/cio2-bridge.c -@@ -22,6 +22,8 @@ - static const struct cio2_sensor_config cio2_supported_sensors[] = { - /* Omnivision OV5693 */ - CIO2_SENSOR_CONFIG("INT33BE", 0), -+ /* Omnivision OV8865 */ -+ CIO2_SENSOR_CONFIG("INT347A", 1, 360000000), - /* Omnivision OV2680 */ - CIO2_SENSOR_CONFIG("OVTI2680", 0), - }; --- -2.35.1 - -From dd52bc8c51c21d44afdd51239e6fba04bae259be Mon Sep 17 00:00:00 2001 -From: Hans de Goede -Date: Sun, 10 Oct 2021 20:56:57 +0200 -Subject: [PATCH] ACPI: delay enumeration of devices with a _DEP pointing to an - INT3472 device - -The clk and regulator frameworks expect clk/regulator consumer-devices -to have info about the consumed clks/regulators described in the device's -fw_node. - -To work around cases where this info is not present in the firmware tables, -which is often the case on x86/ACPI devices, both frameworks allow the -provider-driver to attach info about consumers to the clks/regulators -when registering these. - -This causes problems with the probe ordering wrt drivers for consumers -of these clks/regulators. Since the lookups are only registered when the -provider-driver binds, trying to get these clks/regulators before then -results in a -ENOENT error for clks and a dummy regulator for regulators. - -One case where we hit this issue is camera sensors such as e.g. the OV8865 -sensor found on the Microsoft Surface Go. The sensor uses clks, regulators -and GPIOs provided by a TPS68470 PMIC which is described in an INT3472 -ACPI device. There is special platform code handling this and setting -platform_data with the necessary consumer info on the MFD cells -instantiated for the PMIC under: drivers/platform/x86/intel/int3472. - -For this to work properly the ov8865 driver must not bind to the I2C-client -for the OV8865 sensor until after the TPS68470 PMIC gpio, regulator and -clk MFD cells have all been fully setup. - -The OV8865 on the Microsoft Surface Go is just one example, all X86 -devices using the Intel IPU3 camera block found on recent Intel SoCs -have similar issues where there is an INT3472 HID ACPI-device, which -describes the clks and regulators, and the driver for this INT3472 device -must be fully initialized before the sensor driver (any sensor driver) -binds for things to work properly. - -On these devices the ACPI nodes describing the sensors all have a _DEP -dependency on the matching INT3472 ACPI device (there is one per sensor). - -This allows solving the probe-ordering problem by delaying the enumeration -(instantiation of the I2C-client in the ov8865 example) of ACPI-devices -which have a _DEP dependency on an INT3472 device. - -The new acpi_dev_ready_for_enumeration() helper used for this is also -exported because for devices, which have the enumeration_by_parent flag -set, the parent-driver will do its own scan of child ACPI devices and -it will try to enumerate those during its probe(). Code doing this such -as e.g. the i2c-core-acpi.c code must call this new helper to ensure -that it too delays the enumeration until all the _DEP dependencies are -met on devices which have the new honor_deps flag set. - -Signed-off-by: Hans de Goede -Patchset: cameras ---- - drivers/acpi/scan.c | 36 ++++++++++++++++++++++++++++++++++-- - include/acpi/acpi_bus.h | 5 ++++- - 2 files changed, 38 insertions(+), 3 deletions(-) - -diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c -index 608e83f07b48..ca33437def41 100644 ---- a/drivers/acpi/scan.c -+++ b/drivers/acpi/scan.c -@@ -797,6 +797,12 @@ static const char * const acpi_ignore_dep_ids[] = { - NULL - }; - -+/* List of HIDs for which we honor deps of matching ACPI devs, when checking _DEP lists. */ -+static const char * const acpi_honor_dep_ids[] = { -+ "INT3472", /* Camera sensor PMIC / clk and regulator info */ -+ NULL -+}; -+ - static struct acpi_device *acpi_bus_get_parent(acpi_handle handle) - { - struct acpi_device *device = NULL; -@@ -1774,8 +1780,12 @@ static void acpi_scan_dep_init(struct acpi_device *adev) - struct acpi_dep_data *dep; - - list_for_each_entry(dep, &acpi_dep_list, node) { -- if (dep->consumer == adev->handle) -+ if (dep->consumer == adev->handle) { -+ if (dep->honor_dep) -+ adev->flags.honor_deps = 1; -+ - adev->dep_unmet++; -+ } - } - } - -@@ -1979,7 +1989,7 @@ static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep) - for (count = 0, i = 0; i < dep_devices.count; i++) { - struct acpi_device_info *info; - struct acpi_dep_data *dep; -- bool skip; -+ bool skip, honor_dep; - - status = acpi_get_object_info(dep_devices.handles[i], &info); - if (ACPI_FAILURE(status)) { -@@ -1988,6 +1998,7 @@ static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep) - } - - skip = acpi_info_matches_ids(info, acpi_ignore_dep_ids); -+ honor_dep = acpi_info_matches_ids(info, acpi_honor_dep_ids); - kfree(info); - - if (skip) -@@ -2001,6 +2012,7 @@ static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep) - - dep->supplier = dep_devices.handles[i]; - dep->consumer = handle; -+ dep->honor_dep = honor_dep; - - mutex_lock(&acpi_dep_list_lock); - list_add_tail(&dep->node , &acpi_dep_list); -@@ -2088,6 +2100,9 @@ static acpi_status acpi_bus_check_add_2(acpi_handle handle, u32 lvl_not_used, - - static void acpi_default_enumeration(struct acpi_device *device) - { -+ if (!acpi_dev_ready_for_enumeration(device)) -+ return; -+ - /* - * Do not enumerate devices with enumeration_by_parent flag set as - * they will be enumerated by their respective parents. -@@ -2330,6 +2345,23 @@ void acpi_dev_clear_dependencies(struct acpi_device *supplier) - } - EXPORT_SYMBOL_GPL(acpi_dev_clear_dependencies); - -+/** -+ * acpi_dev_ready_for_enumeration - Check if the ACPI device is ready for enumeration -+ * @device: Pointer to the &struct acpi_device to check -+ * -+ * Check if the device is present and has no unmet dependencies. -+ * -+ * Return true if the device is ready for enumeratino. Otherwise, return false. -+ */ -+bool acpi_dev_ready_for_enumeration(const struct acpi_device *device) -+{ -+ if (device->flags.honor_deps && device->dep_unmet) -+ return false; -+ -+ return acpi_device_is_present(device); -+} -+EXPORT_SYMBOL_GPL(acpi_dev_ready_for_enumeration); -+ - /** - * acpi_dev_get_first_consumer_dev - Return ACPI device dependent on @supplier - * @supplier: Pointer to the dependee device -diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h -index d6fe27b695c3..5895f6c7f6db 100644 ---- a/include/acpi/acpi_bus.h -+++ b/include/acpi/acpi_bus.h -@@ -202,7 +202,8 @@ struct acpi_device_flags { - u32 coherent_dma:1; - u32 cca_seen:1; - u32 enumeration_by_parent:1; -- u32 reserved:19; -+ u32 honor_deps:1; -+ u32 reserved:18; - }; - - /* File System */ -@@ -285,6 +286,7 @@ struct acpi_dep_data { - struct list_head node; - acpi_handle supplier; - acpi_handle consumer; -+ bool honor_dep; - }; - - /* Performance Management */ -@@ -694,6 +696,7 @@ static inline bool acpi_device_can_poweroff(struct acpi_device *adev) - bool acpi_dev_hid_uid_match(struct acpi_device *adev, const char *hid2, const char *uid2); - - void acpi_dev_clear_dependencies(struct acpi_device *supplier); -+bool acpi_dev_ready_for_enumeration(const struct acpi_device *device); - struct acpi_device *acpi_dev_get_first_consumer_dev(struct acpi_device *supplier); - struct acpi_device * - acpi_dev_get_next_match_dev(struct acpi_device *adev, const char *hid, const char *uid, s64 hrv); --- -2.35.1 - -From 75b7492855a6bde63125f8d01804672d34e2257d Mon Sep 17 00:00:00 2001 -From: Hans de Goede -Date: Sun, 10 Oct 2021 20:56:58 +0200 -Subject: [PATCH] i2c: acpi: Use acpi_dev_ready_for_enumeration() helper - -The clk and regulator frameworks expect clk/regulator consumer-devices -to have info about the consumed clks/regulators described in the device's -fw_node. - -To work around cases where this info is not present in the firmware tables, -which is often the case on x86/ACPI devices, both frameworks allow the -provider-driver to attach info about consumers to the clks/regulators -when registering these. - -This causes problems with the probe ordering wrt drivers for consumers -of these clks/regulators. Since the lookups are only registered when the -provider-driver binds, trying to get these clks/regulators before then -results in a -ENOENT error for clks and a dummy regulator for regulators. - -To ensure the correct probe-ordering the ACPI core has code to defer the -enumeration of consumers affected by this until the providers are ready. - -Call the new acpi_dev_ready_for_enumeration() helper to avoid -enumerating / instantiating i2c-clients too early. - -Signed-off-by: Hans de Goede -Patchset: cameras ---- - drivers/i2c/i2c-core-acpi.c | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c -index 3b688cea8e00..0542d8aba902 100644 ---- a/drivers/i2c/i2c-core-acpi.c -+++ b/drivers/i2c/i2c-core-acpi.c -@@ -144,9 +144,12 @@ static int i2c_acpi_do_lookup(struct acpi_device *adev, - struct list_head resource_list; - int ret; - -- if (acpi_bus_get_status(adev) || !adev->status.present) -+ if (acpi_bus_get_status(adev)) - return -EINVAL; - -+ if (!acpi_dev_ready_for_enumeration(adev)) -+ return -ENODEV; -+ - if (acpi_match_device_ids(adev, i2c_acpi_ignored_device_ids) == 0) - return -ENODEV; - --- -2.35.1 - -From 7337d1dae4ab8cf07f72960ab69f4cc0baa877f4 Mon Sep 17 00:00:00 2001 -From: Hans de Goede -Date: Sun, 10 Oct 2021 20:56:59 +0200 -Subject: [PATCH] platform_data: Add linux/platform_data/tps68470.h file - -The clk and regulator frameworks expect clk/regulator consumer-devices -to have info about the consumed clks/regulators described in the device's -fw_node. - -To work around cases where this info is not present in the firmware tables, -which is often the case on x86/ACPI devices, both frameworks allow the -provider-driver to attach info about consumers to the provider-device -during probe/registration of the provider device. - -The TI TPS68470 PMIC is used x86/ACPI devices with the consumer-info -missing from the ACPI tables. Thus the tps68470-clk and tps68470-regulator -drivers must provide the consumer-info at probe time. - -Define tps68470_clk_platform_data and tps68470_regulator_platform_data -structs to allow the x86 platform code to pass the necessary consumer info -to these drivers. - -Signed-off-by: Hans de Goede -Patchset: cameras ---- - include/linux/platform_data/tps68470.h | 35 ++++++++++++++++++++++++++ - 1 file changed, 35 insertions(+) - create mode 100644 include/linux/platform_data/tps68470.h - -diff --git a/include/linux/platform_data/tps68470.h b/include/linux/platform_data/tps68470.h -new file mode 100644 -index 000000000000..126d082c3f2e ---- /dev/null -+++ b/include/linux/platform_data/tps68470.h -@@ -0,0 +1,35 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+/* -+ * TI TPS68470 PMIC platform data definition. -+ * -+ * Copyright (c) 2021 Red Hat Inc. -+ * -+ * Red Hat authors: -+ * Hans de Goede -+ */ -+#ifndef __PDATA_TPS68470_H -+#define __PDATA_TPS68470_H -+ -+enum tps68470_regulators { -+ TPS68470_CORE, -+ TPS68470_ANA, -+ TPS68470_VCM, -+ TPS68470_VIO, -+ TPS68470_VSIO, -+ TPS68470_AUX1, -+ TPS68470_AUX2, -+ TPS68470_NUM_REGULATORS -+}; -+ -+struct regulator_init_data; -+ -+struct tps68470_regulator_platform_data { -+ const struct regulator_init_data *reg_init_data[TPS68470_NUM_REGULATORS]; -+}; -+ -+struct tps68470_clk_platform_data { -+ const char *consumer_dev_name; -+ const char *consumer_con_id; -+}; -+ -+#endif --- -2.35.1 - -From afe75b849e2d0bdd23fc9b0cabd1ce0a46dc3d5e Mon Sep 17 00:00:00 2001 -From: Hans de Goede -Date: Sun, 10 Oct 2021 20:57:00 +0200 -Subject: [PATCH] regulator: Introduce tps68470-regulator driver - -The TPS68470 PMIC provides Clocks, GPIOs and Regulators. At present in -the kernel the Regulators and Clocks are controlled by an OpRegion -driver designed to work with power control methods defined in ACPI, but -some platforms lack those methods, meaning drivers need to be able to -consume the resources of these chips through the usual frameworks. - -This commit adds a driver for the regulators provided by the tps68470, -and is designed to bind to the platform_device registered by the -intel_skl_int3472 module. - -This is based on this out of tree driver written by Intel: -https://github.com/intel/linux-intel-lts/blob/4.14/base/drivers/regulator/tps68470-regulator.c -with various cleanups added. - -Signed-off-by: Hans de Goede -Patchset: cameras ---- - drivers/regulator/Kconfig | 9 ++ - drivers/regulator/Makefile | 1 + - drivers/regulator/tps68470-regulator.c | 193 +++++++++++++++++++++++++ - 3 files changed, 203 insertions(+) - create mode 100644 drivers/regulator/tps68470-regulator.c - -diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig -index 6be9b1c8a615..25e3acb378e3 100644 ---- a/drivers/regulator/Kconfig -+++ b/drivers/regulator/Kconfig -@@ -1339,6 +1339,15 @@ config REGULATOR_TPS65912 - help - This driver supports TPS65912 voltage regulator chip. - -+config REGULATOR_TPS68470 -+ tristate "TI TPS68370 PMIC Regulators Driver" -+ depends on INTEL_SKL_INT3472 -+ help -+ This driver adds support for the TPS68470 PMIC to register -+ regulators against the usual framework. -+ -+ The module will be called "tps68470-regulator" -+ - config REGULATOR_TWL4030 - tristate "TI TWL4030/TWL5030/TWL6030/TPS659x0 PMIC" - depends on TWL4030_CORE -diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile -index b07d2a22df0b..0ea7e6adc267 100644 ---- a/drivers/regulator/Makefile -+++ b/drivers/regulator/Makefile -@@ -158,6 +158,7 @@ obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o - obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o - obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o - obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o -+obj-$(CONFIG_REGULATOR_TPS68470) += tps68470-regulator.o - obj-$(CONFIG_REGULATOR_TPS65132) += tps65132-regulator.o - obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o twl6030-regulator.o - obj-$(CONFIG_REGULATOR_UNIPHIER) += uniphier-regulator.o -diff --git a/drivers/regulator/tps68470-regulator.c b/drivers/regulator/tps68470-regulator.c -new file mode 100644 -index 000000000000..3129fa13a122 ---- /dev/null -+++ b/drivers/regulator/tps68470-regulator.c -@@ -0,0 +1,193 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Regulator driver for TPS68470 PMIC -+ * -+ * Copyright (C) 2018 Intel Corporation -+ * -+ * Authors: -+ * Zaikuo Wang -+ * Tianshu Qiu -+ * Jian Xu Zheng -+ * Yuning Pu -+ * Rajmohan Mani -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define TPS68470_REGULATOR(_name, _id, _ops, _n, _vr, \ -+ _vm, _er, _em, _t, _lr, _nlr) \ -+ [TPS68470_ ## _name] = { \ -+ .name = # _name, \ -+ .id = _id, \ -+ .ops = &_ops, \ -+ .n_voltages = _n, \ -+ .type = REGULATOR_VOLTAGE, \ -+ .owner = THIS_MODULE, \ -+ .vsel_reg = _vr, \ -+ .vsel_mask = _vm, \ -+ .enable_reg = _er, \ -+ .enable_mask = _em, \ -+ .volt_table = _t, \ -+ .linear_ranges = _lr, \ -+ .n_linear_ranges = _nlr, \ -+ } -+ -+static const struct linear_range tps68470_ldo_ranges[] = { -+ REGULATOR_LINEAR_RANGE(875000, 0, 125, 17800), -+}; -+ -+static const struct linear_range tps68470_core_ranges[] = { -+ REGULATOR_LINEAR_RANGE(900000, 0, 42, 25000), -+}; -+ -+/* Operations permitted on DCDCx, LDO2, LDO3 and LDO4 */ -+static const struct regulator_ops tps68470_regulator_ops = { -+ .is_enabled = regulator_is_enabled_regmap, -+ .enable = regulator_enable_regmap, -+ .disable = regulator_disable_regmap, -+ .get_voltage_sel = regulator_get_voltage_sel_regmap, -+ .set_voltage_sel = regulator_set_voltage_sel_regmap, -+ .list_voltage = regulator_list_voltage_linear_range, -+ .map_voltage = regulator_map_voltage_linear_range, -+}; -+ -+static const struct regulator_desc regulators[] = { -+ TPS68470_REGULATOR(CORE, TPS68470_CORE, -+ tps68470_regulator_ops, 43, TPS68470_REG_VDVAL, -+ TPS68470_VDVAL_DVOLT_MASK, TPS68470_REG_VDCTL, -+ TPS68470_VDCTL_EN_MASK, -+ NULL, tps68470_core_ranges, -+ ARRAY_SIZE(tps68470_core_ranges)), -+ TPS68470_REGULATOR(ANA, TPS68470_ANA, -+ tps68470_regulator_ops, 126, TPS68470_REG_VAVAL, -+ TPS68470_VAVAL_AVOLT_MASK, TPS68470_REG_VACTL, -+ TPS68470_VACTL_EN_MASK, -+ NULL, tps68470_ldo_ranges, -+ ARRAY_SIZE(tps68470_ldo_ranges)), -+ TPS68470_REGULATOR(VCM, TPS68470_VCM, -+ tps68470_regulator_ops, 126, TPS68470_REG_VCMVAL, -+ TPS68470_VCMVAL_VCVOLT_MASK, TPS68470_REG_VCMCTL, -+ TPS68470_VCMCTL_EN_MASK, -+ NULL, tps68470_ldo_ranges, -+ ARRAY_SIZE(tps68470_ldo_ranges)), -+ TPS68470_REGULATOR(VIO, TPS68470_VIO, -+ tps68470_regulator_ops, 126, TPS68470_REG_VIOVAL, -+ TPS68470_VIOVAL_IOVOLT_MASK, TPS68470_REG_S_I2C_CTL, -+ TPS68470_S_I2C_CTL_EN_MASK, -+ NULL, tps68470_ldo_ranges, -+ ARRAY_SIZE(tps68470_ldo_ranges)), -+ -+/* -+ * (1) This register must have same setting as VIOVAL if S_IO LDO is used to -+ * power daisy chained IOs in the receive side. -+ * (2) If there is no I2C daisy chain it can be set freely. -+ * -+ */ -+ TPS68470_REGULATOR(VSIO, TPS68470_VSIO, -+ tps68470_regulator_ops, 126, TPS68470_REG_VSIOVAL, -+ TPS68470_VSIOVAL_IOVOLT_MASK, TPS68470_REG_S_I2C_CTL, -+ TPS68470_S_I2C_CTL_EN_MASK, -+ NULL, tps68470_ldo_ranges, -+ ARRAY_SIZE(tps68470_ldo_ranges)), -+ TPS68470_REGULATOR(AUX1, TPS68470_AUX1, -+ tps68470_regulator_ops, 126, TPS68470_REG_VAUX1VAL, -+ TPS68470_VAUX1VAL_AUX1VOLT_MASK, -+ TPS68470_REG_VAUX1CTL, -+ TPS68470_VAUX1CTL_EN_MASK, -+ NULL, tps68470_ldo_ranges, -+ ARRAY_SIZE(tps68470_ldo_ranges)), -+ TPS68470_REGULATOR(AUX2, TPS68470_AUX2, -+ tps68470_regulator_ops, 126, TPS68470_REG_VAUX2VAL, -+ TPS68470_VAUX2VAL_AUX2VOLT_MASK, -+ TPS68470_REG_VAUX2CTL, -+ TPS68470_VAUX2CTL_EN_MASK, -+ NULL, tps68470_ldo_ranges, -+ ARRAY_SIZE(tps68470_ldo_ranges)), -+}; -+ -+#define TPS68470_REG_INIT_DATA(_name, _min_uV, _max_uV) \ -+ [TPS68470_ ## _name] = { \ -+ .constraints = { \ -+ .name = # _name, \ -+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | \ -+ REGULATOR_CHANGE_STATUS, \ -+ .min_uV = _min_uV, \ -+ .max_uV = _max_uV, \ -+ }, \ -+ } -+ -+struct regulator_init_data tps68470_init[] = { -+ TPS68470_REG_INIT_DATA(CORE, 900000, 1950000), -+ TPS68470_REG_INIT_DATA(ANA, 875000, 3100000), -+ TPS68470_REG_INIT_DATA(VCM, 875000, 3100000), -+ TPS68470_REG_INIT_DATA(VIO, 875000, 3100000), -+ TPS68470_REG_INIT_DATA(VSIO, 875000, 3100000), -+ TPS68470_REG_INIT_DATA(AUX1, 875000, 3100000), -+ TPS68470_REG_INIT_DATA(AUX2, 875000, 3100000), -+}; -+ -+static int tps68470_regulator_probe(struct platform_device *pdev) -+{ -+ struct tps68470_regulator_platform_data *pdata = pdev->dev.platform_data; -+ struct regulator_config config = { }; -+ struct regmap *tps68470_regmap; -+ struct regulator_dev *rdev; -+ int i; -+ -+ tps68470_regmap = dev_get_drvdata(pdev->dev.parent); -+ -+ for (i = 0; i < TPS68470_NUM_REGULATORS; i++) { -+ config.dev = pdev->dev.parent; -+ config.regmap = tps68470_regmap; -+ if (pdata && pdata->reg_init_data[i]) -+ config.init_data = pdata->reg_init_data[i]; -+ else -+ config.init_data = &tps68470_init[i]; -+ -+ rdev = devm_regulator_register(&pdev->dev, ®ulators[i], &config); -+ if (IS_ERR(rdev)) { -+ dev_err(&pdev->dev, "failed to register %s regulator\n", -+ regulators[i].name); -+ return PTR_ERR(rdev); -+ } -+ } -+ -+ return 0; -+} -+ -+static struct platform_driver tps68470_regulator_driver = { -+ .driver = { -+ .name = "tps68470-regulator", -+ }, -+ .probe = tps68470_regulator_probe, -+}; -+ -+/* -+ * The ACPI tps68470 probe-ordering depends on the clk/gpio/regulator drivers -+ * registering before the drivers for the camera-sensors which use them bind. -+ * subsys_initcall() ensures this when the drivers are builtin. -+ */ -+static int __init tps68470_regulator_init(void) -+{ -+ return platform_driver_register(&tps68470_regulator_driver); -+} -+subsys_initcall(tps68470_regulator_init); -+ -+static void __exit tps68470_regulator_exit(void) -+{ -+ platform_driver_unregister(&tps68470_regulator_driver); -+} -+module_exit(tps68470_regulator_exit); -+ -+MODULE_ALIAS("platform:tps68470-regulator"); -+MODULE_DESCRIPTION("TPS68470 voltage regulator driver"); -+MODULE_LICENSE("GPL v2"); --- -2.35.1 - -From f2f07e994f692af710805ef8a53590b371288b8b Mon Sep 17 00:00:00 2001 -From: Hans de Goede -Date: Sun, 10 Oct 2021 20:57:01 +0200 -Subject: [PATCH] clk: Introduce clk-tps68470 driver - -The TPS68470 PMIC provides Clocks, GPIOs and Regulators. At present in -the kernel the Regulators and Clocks are controlled by an OpRegion -driver designed to work with power control methods defined in ACPI, but -some platforms lack those methods, meaning drivers need to be able to -consume the resources of these chips through the usual frameworks. - -This commit adds a driver for the clocks provided by the tps68470, -and is designed to bind to the platform_device registered by the -intel_skl_int3472 module. - -This is based on this out of tree driver written by Intel: -https://github.com/intel/linux-intel-lts/blob/4.14/base/drivers/clk/clk-tps68470.c -with various cleanups added. - -Signed-off-by: Hans de Goede -Patchset: cameras ---- - drivers/clk/Kconfig | 6 + - drivers/clk/Makefile | 1 + - drivers/clk/clk-tps68470.c | 256 +++++++++++++++++++++++++++++++++++ - include/linux/mfd/tps68470.h | 11 ++ - 4 files changed, 274 insertions(+) - create mode 100644 drivers/clk/clk-tps68470.c - -diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig -index c5b3dc97396a..7dffecac83d1 100644 ---- a/drivers/clk/Kconfig -+++ b/drivers/clk/Kconfig -@@ -169,6 +169,12 @@ config COMMON_CLK_CDCE706 - help - This driver supports TI CDCE706 programmable 3-PLL clock synthesizer. - -+config COMMON_CLK_TPS68470 -+ tristate "Clock Driver for TI TPS68470 PMIC" -+ depends on I2C && REGMAP_I2C && INTEL_SKL_INT3472 -+ help -+ This driver supports the clocks provided by TPS68470 -+ - config COMMON_CLK_CDCE925 - tristate "Clock driver for TI CDCE913/925/937/949 devices" - depends on I2C -diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile -index e42312121e51..6b6a88ae1425 100644 ---- a/drivers/clk/Makefile -+++ b/drivers/clk/Makefile -@@ -63,6 +63,7 @@ obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o - obj-$(CONFIG_COMMON_CLK_STM32F) += clk-stm32f4.o - obj-$(CONFIG_COMMON_CLK_STM32H7) += clk-stm32h7.o - obj-$(CONFIG_COMMON_CLK_STM32MP157) += clk-stm32mp1.o -+obj-$(CONFIG_COMMON_CLK_TPS68470) += clk-tps68470.o - obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o - obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o - obj-$(CONFIG_COMMON_CLK_VC5) += clk-versaclock5.o -diff --git a/drivers/clk/clk-tps68470.c b/drivers/clk/clk-tps68470.c -new file mode 100644 -index 000000000000..27e8cbd0f60e ---- /dev/null -+++ b/drivers/clk/clk-tps68470.c -@@ -0,0 +1,256 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Clock driver for TPS68470 PMIC -+ * -+ * Copyright (C) 2018 Intel Corporation -+ * -+ * Authors: -+ * Zaikuo Wang -+ * Tianshu Qiu -+ * Jian Xu Zheng -+ * Yuning Pu -+ * Antti Laakso -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define TPS68470_CLK_NAME "tps68470-clk" -+ -+#define to_tps68470_clkdata(clkd) \ -+ container_of(clkd, struct tps68470_clkdata, clkout_hw) -+ -+struct tps68470_clkout_freqs { -+ unsigned long freq; -+ unsigned int xtaldiv; -+ unsigned int plldiv; -+ unsigned int postdiv; -+ unsigned int buckdiv; -+ unsigned int boostdiv; -+} clk_freqs[] = { -+/* -+ * The PLL is used to multiply the crystal oscillator -+ * frequency range of 3 MHz to 27 MHz by a programmable -+ * factor of F = (M/N)*(1/P) such that the output -+ * available at the HCLK_A or HCLK_B pins are in the range -+ * of 4 MHz to 64 MHz in increments of 0.1 MHz -+ * -+ * hclk_# = osc_in * (((plldiv*2)+320) / (xtaldiv+30)) * (1 / 2^postdiv) -+ * -+ * PLL_REF_CLK should be as close as possible to 100kHz -+ * PLL_REF_CLK = input clk / XTALDIV[7:0] + 30) -+ * -+ * PLL_VCO_CLK = (PLL_REF_CLK * (plldiv*2 + 320)) -+ * -+ * BOOST should be as close as possible to 2Mhz -+ * BOOST = PLL_VCO_CLK / (BOOSTDIV[4:0] + 16) * -+ * -+ * BUCK should be as close as possible to 5.2Mhz -+ * BUCK = PLL_VCO_CLK / (BUCKDIV[3:0] + 5) -+ * -+ * osc_in xtaldiv plldiv postdiv hclk_# -+ * 20Mhz 170 32 1 19.2Mhz -+ * 20Mhz 170 40 1 20Mhz -+ * 20Mhz 170 80 1 24Mhz -+ * -+ */ -+ { 19200000, 170, 32, 1, 2, 3 }, -+ { 20000000, 170, 40, 1, 3, 4 }, -+ { 24000000, 170, 80, 1, 4, 8 }, -+}; -+ -+struct tps68470_clkdata { -+ struct clk_hw clkout_hw; -+ struct regmap *regmap; -+ struct clk *clk; -+ int clk_cfg_idx; -+}; -+ -+static int tps68470_clk_is_prepared(struct clk_hw *hw) -+{ -+ struct tps68470_clkdata *clkdata = to_tps68470_clkdata(hw); -+ int val; -+ -+ if (regmap_read(clkdata->regmap, TPS68470_REG_PLLCTL, &val)) -+ return 0; -+ -+ return val & TPS68470_PLL_EN_MASK; -+} -+ -+static int tps68470_clk_prepare(struct clk_hw *hw) -+{ -+ struct tps68470_clkdata *clkdata = to_tps68470_clkdata(hw); -+ int idx = clkdata->clk_cfg_idx; -+ -+ regmap_write(clkdata->regmap, TPS68470_REG_BOOSTDIV, clk_freqs[idx].boostdiv); -+ regmap_write(clkdata->regmap, TPS68470_REG_BUCKDIV, clk_freqs[idx].buckdiv); -+ regmap_write(clkdata->regmap, TPS68470_REG_PLLSWR, TPS68470_PLLSWR_DEFAULT); -+ regmap_write(clkdata->regmap, TPS68470_REG_XTALDIV, clk_freqs[idx].xtaldiv); -+ regmap_write(clkdata->regmap, TPS68470_REG_PLLDIV, clk_freqs[idx].plldiv); -+ regmap_write(clkdata->regmap, TPS68470_REG_POSTDIV, clk_freqs[idx].postdiv); -+ regmap_write(clkdata->regmap, TPS68470_REG_POSTDIV2, clk_freqs[idx].postdiv); -+ regmap_write(clkdata->regmap, TPS68470_REG_CLKCFG2, TPS68470_CLKCFG2_DRV_STR_2MA); -+ -+ regmap_write(clkdata->regmap, TPS68470_REG_PLLCTL, -+ TPS68470_OSC_EXT_CAP_DEFAULT << TPS68470_OSC_EXT_CAP_SHIFT | -+ TPS68470_CLK_SRC_XTAL << TPS68470_CLK_SRC_SHIFT); -+ -+ regmap_write(clkdata->regmap, TPS68470_REG_CLKCFG1, -+ (TPS68470_PLL_OUTPUT_ENABLE << -+ TPS68470_OUTPUT_A_SHIFT) | -+ (TPS68470_PLL_OUTPUT_ENABLE << -+ TPS68470_OUTPUT_B_SHIFT)); -+ -+ regmap_update_bits(clkdata->regmap, TPS68470_REG_PLLCTL, -+ TPS68470_PLL_EN_MASK, TPS68470_PLL_EN_MASK); -+ -+ return 0; -+} -+ -+static void tps68470_clk_unprepare(struct clk_hw *hw) -+{ -+ struct tps68470_clkdata *clkdata = to_tps68470_clkdata(hw); -+ -+ /* disable clock first*/ -+ regmap_update_bits(clkdata->regmap, TPS68470_REG_PLLCTL, TPS68470_PLL_EN_MASK, 0); -+ -+ /* write hw defaults */ -+ regmap_write(clkdata->regmap, TPS68470_REG_BOOSTDIV, 0); -+ regmap_write(clkdata->regmap, TPS68470_REG_BUCKDIV, 0); -+ regmap_write(clkdata->regmap, TPS68470_REG_PLLSWR, 0); -+ regmap_write(clkdata->regmap, TPS68470_REG_XTALDIV, 0); -+ regmap_write(clkdata->regmap, TPS68470_REG_PLLDIV, 0); -+ regmap_write(clkdata->regmap, TPS68470_REG_POSTDIV, 0); -+ regmap_write(clkdata->regmap, TPS68470_REG_CLKCFG2, 0); -+ regmap_write(clkdata->regmap, TPS68470_REG_CLKCFG1, 0); -+} -+ -+static unsigned long tps68470_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) -+{ -+ struct tps68470_clkdata *clkdata = to_tps68470_clkdata(hw); -+ -+ return clk_freqs[clkdata->clk_cfg_idx].freq; -+} -+ -+static int tps68470_clk_cfg_lookup(unsigned long rate) -+{ -+ long diff, best_diff = LONG_MAX; -+ int i, best_idx = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(clk_freqs); i++) { -+ diff = clk_freqs[i].freq - rate; -+ if (diff == 0) -+ return i; -+ -+ diff = abs(diff); -+ if (diff < best_diff) { -+ best_diff = diff; -+ best_idx = i; -+ } -+ } -+ -+ return best_idx; -+} -+ -+static long tps68470_clk_round_rate(struct clk_hw *hw, unsigned long rate, -+ unsigned long *parent_rate) -+{ -+ int idx = tps68470_clk_cfg_lookup(rate); -+ -+ return clk_freqs[idx].freq; -+} -+ -+static int tps68470_clk_set_rate(struct clk_hw *hw, unsigned long rate, -+ unsigned long parent_rate) -+{ -+ struct tps68470_clkdata *clkdata = to_tps68470_clkdata(hw); -+ int idx = tps68470_clk_cfg_lookup(rate); -+ -+ if (rate != clk_freqs[idx].freq) -+ return -EINVAL; -+ -+ clkdata->clk_cfg_idx = idx; -+ return 0; -+} -+ -+static const struct clk_ops tps68470_clk_ops = { -+ .is_prepared = tps68470_clk_is_prepared, -+ .prepare = tps68470_clk_prepare, -+ .unprepare = tps68470_clk_unprepare, -+ .recalc_rate = tps68470_clk_recalc_rate, -+ .round_rate = tps68470_clk_round_rate, -+ .set_rate = tps68470_clk_set_rate, -+}; -+ -+static struct clk_init_data tps68470_clk_initdata = { -+ .name = TPS68470_CLK_NAME, -+ .ops = &tps68470_clk_ops, -+}; -+ -+static int tps68470_clk_probe(struct platform_device *pdev) -+{ -+ struct tps68470_clk_platform_data *pdata = pdev->dev.platform_data; -+ struct tps68470_clkdata *tps68470_clkdata; -+ int ret; -+ -+ tps68470_clkdata = devm_kzalloc(&pdev->dev, sizeof(*tps68470_clkdata), -+ GFP_KERNEL); -+ if (!tps68470_clkdata) -+ return -ENOMEM; -+ -+ tps68470_clkdata->regmap = dev_get_drvdata(pdev->dev.parent); -+ tps68470_clkdata->clkout_hw.init = &tps68470_clk_initdata; -+ tps68470_clkdata->clk = devm_clk_register(&pdev->dev, &tps68470_clkdata->clkout_hw); -+ if (IS_ERR(tps68470_clkdata->clk)) -+ return PTR_ERR(tps68470_clkdata->clk); -+ -+ ret = devm_clk_hw_register_clkdev(&pdev->dev, &tps68470_clkdata->clkout_hw, -+ TPS68470_CLK_NAME, NULL); -+ if (ret) -+ return ret; -+ -+ if (pdata) { -+ ret = devm_clk_hw_register_clkdev(&pdev->dev, -+ &tps68470_clkdata->clkout_hw, -+ pdata->consumer_con_id, -+ pdata->consumer_dev_name); -+ if (ret) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static struct platform_driver tps68470_clk_driver = { -+ .driver = { -+ .name = TPS68470_CLK_NAME, -+ }, -+ .probe = tps68470_clk_probe, -+}; -+ -+/* -+ * The ACPI tps68470 probe-ordering depends on the clk/gpio/regulator drivers -+ * registering before the drivers for the camera-sensors which use them bind. -+ * subsys_initcall() ensures this when the drivers are builtin. -+ */ -+static int __init tps68470_clk_init(void) -+{ -+ return platform_driver_register(&tps68470_clk_driver); -+} -+subsys_initcall(tps68470_clk_init); -+ -+static void __exit tps68470_clk_exit(void) -+{ -+ platform_driver_unregister(&tps68470_clk_driver); -+} -+module_exit(tps68470_clk_exit); -+ -+MODULE_ALIAS("platform:tps68470-clk"); -+MODULE_DESCRIPTION("clock driver for TPS68470 pmic"); -+MODULE_LICENSE("GPL"); -diff --git a/include/linux/mfd/tps68470.h b/include/linux/mfd/tps68470.h -index ffe81127d91c..7807fa329db0 100644 ---- a/include/linux/mfd/tps68470.h -+++ b/include/linux/mfd/tps68470.h -@@ -75,6 +75,17 @@ - #define TPS68470_CLKCFG1_MODE_A_MASK GENMASK(1, 0) - #define TPS68470_CLKCFG1_MODE_B_MASK GENMASK(3, 2) - -+#define TPS68470_CLKCFG2_DRV_STR_2MA 0x05 -+#define TPS68470_PLL_OUTPUT_ENABLE 0x02 -+#define TPS68470_CLK_SRC_XTAL BIT(0) -+#define TPS68470_PLLSWR_DEFAULT GENMASK(1, 0) -+#define TPS68470_OSC_EXT_CAP_DEFAULT 0x05 -+ -+#define TPS68470_OUTPUT_A_SHIFT 0x00 -+#define TPS68470_OUTPUT_B_SHIFT 0x02 -+#define TPS68470_CLK_SRC_SHIFT GENMASK(2, 0) -+#define TPS68470_OSC_EXT_CAP_SHIFT BIT(2) -+ - #define TPS68470_GPIO_CTL_REG_A(x) (TPS68470_REG_GPCTL0A + (x) * 2) - #define TPS68470_GPIO_CTL_REG_B(x) (TPS68470_REG_GPCTL0B + (x) * 2) - #define TPS68470_GPIO_MODE_MASK GENMASK(1, 0) --- -2.35.1 - -From bf27d7b319215a0e17269ccdc42464a2be1afd7a Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Sun, 10 Oct 2021 20:57:02 +0200 -Subject: [PATCH] platform/x86: int3472: Enable I2c daisy chain - -The TPS68470 PMIC has an I2C passthrough mode through which I2C traffic -can be forwarded to a device connected to the PMIC as though it were -connected directly to the system bus. Enable this mode when the chip -is initialised. - -Signed-off-by: Daniel Scally -Patchset: cameras ---- - .../x86/intel/int3472/intel_skl_int3472_tps68470.c | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/drivers/platform/x86/intel/int3472/intel_skl_int3472_tps68470.c b/drivers/platform/x86/intel/int3472/intel_skl_int3472_tps68470.c -index c05b4cf502fe..42e688f4cad4 100644 ---- a/drivers/platform/x86/intel/int3472/intel_skl_int3472_tps68470.c -+++ b/drivers/platform/x86/intel/int3472/intel_skl_int3472_tps68470.c -@@ -45,6 +45,13 @@ static int tps68470_chip_init(struct device *dev, struct regmap *regmap) - return ret; - } - -+ /* Enable I2C daisy chain */ -+ ret = regmap_write(regmap, TPS68470_REG_S_I2C_CTL, 0x03); -+ if (ret) { -+ dev_err(dev, "Failed to enable i2c daisy chain\n"); -+ return ret; -+ } -+ - dev_info(dev, "TPS68470 REVID: 0x%02x\n", version); - - return 0; --- -2.35.1 - -From 6ea9961efd3c29d366a7608f27c84bbd4b072a67 Mon Sep 17 00:00:00 2001 -From: Hans de Goede -Date: Sun, 10 Oct 2021 20:57:03 +0200 -Subject: [PATCH] platform/x86: int3472: Split into 2 drivers - -The intel_skl_int3472.ko module contains 2 separate drivers, -the int3472_discrete platform driver and the int3472_tps68470 -I2C-driver. - -These 2 drivers contain very little shared code, only -skl_int3472_get_acpi_buffer() and skl_int3472_fill_cldb() are -shared. - -Split the module into 2 drivers, linking the little shared code -directly into both. - -This will allow us to add soft-module dependencies for the -tps68470 clk, gpio and regulator drivers to the new -intel_skl_int3472_tps68470.ko to help with probe ordering issues -without causing these modules to get loaded on boards which only -use the int3472_discrete platform driver. - -While at it also rename the .c and .h files to remove the -cumbersome intel_skl_int3472_ prefix. - -Signed-off-by: Hans de Goede -Patchset: cameras ---- - drivers/platform/x86/intel/int3472/Makefile | 9 ++-- - ...lk_and_regulator.c => clk_and_regulator.c} | 2 +- - drivers/platform/x86/intel/int3472/common.c | 54 +++++++++++++++++++ - .../{intel_skl_int3472_common.h => common.h} | 3 -- - ...ntel_skl_int3472_discrete.c => discrete.c} | 28 ++++++++-- - ...ntel_skl_int3472_tps68470.c => tps68470.c} | 23 +++++++- - 6 files changed, 105 insertions(+), 14 deletions(-) - rename drivers/platform/x86/intel/int3472/{intel_skl_int3472_clk_and_regulator.c => clk_and_regulator.c} (99%) - create mode 100644 drivers/platform/x86/intel/int3472/common.c - rename drivers/platform/x86/intel/int3472/{intel_skl_int3472_common.h => common.h} (94%) - rename drivers/platform/x86/intel/int3472/{intel_skl_int3472_discrete.c => discrete.c} (93%) - rename drivers/platform/x86/intel/int3472/{intel_skl_int3472_tps68470.c => tps68470.c} (85%) - -diff --git a/drivers/platform/x86/intel/int3472/Makefile b/drivers/platform/x86/intel/int3472/Makefile -index 2362e04db18d..4a4b2518ea16 100644 ---- a/drivers/platform/x86/intel/int3472/Makefile -+++ b/drivers/platform/x86/intel/int3472/Makefile -@@ -1,5 +1,4 @@ --obj-$(CONFIG_INTEL_SKL_INT3472) += intel_skl_int3472.o --intel_skl_int3472-y := intel_skl_int3472_common.o \ -- intel_skl_int3472_discrete.o \ -- intel_skl_int3472_tps68470.o \ -- intel_skl_int3472_clk_and_regulator.o -+obj-$(CONFIG_INTEL_SKL_INT3472) += intel_skl_int3472_discrete.o \ -+ intel_skl_int3472_tps68470.o -+intel_skl_int3472_discrete-y := discrete.o clk_and_regulator.o common.o -+intel_skl_int3472_tps68470-y := tps68470.o common.o -diff --git a/drivers/platform/x86/intel/int3472/intel_skl_int3472_clk_and_regulator.c b/drivers/platform/x86/intel/int3472/clk_and_regulator.c -similarity index 99% -rename from drivers/platform/x86/intel/int3472/intel_skl_int3472_clk_and_regulator.c -rename to drivers/platform/x86/intel/int3472/clk_and_regulator.c -index 1700e7557a82..1cf958983e86 100644 ---- a/drivers/platform/x86/intel/int3472/intel_skl_int3472_clk_and_regulator.c -+++ b/drivers/platform/x86/intel/int3472/clk_and_regulator.c -@@ -9,7 +9,7 @@ - #include - #include - --#include "intel_skl_int3472_common.h" -+#include "common.h" - - /* - * The regulators have to have .ops to be valid, but the only ops we actually -diff --git a/drivers/platform/x86/intel/int3472/common.c b/drivers/platform/x86/intel/int3472/common.c -new file mode 100644 -index 000000000000..350655a9515b ---- /dev/null -+++ b/drivers/platform/x86/intel/int3472/common.c -@@ -0,0 +1,54 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* Author: Dan Scally */ -+ -+#include -+#include -+ -+#include "common.h" -+ -+union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev, char *id) -+{ -+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; -+ acpi_handle handle = adev->handle; -+ union acpi_object *obj; -+ acpi_status status; -+ -+ status = acpi_evaluate_object(handle, id, NULL, &buffer); -+ if (ACPI_FAILURE(status)) -+ return ERR_PTR(-ENODEV); -+ -+ obj = buffer.pointer; -+ if (!obj) -+ return ERR_PTR(-ENODEV); -+ -+ if (obj->type != ACPI_TYPE_BUFFER) { -+ acpi_handle_err(handle, "%s object is not an ACPI buffer\n", id); -+ kfree(obj); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ return obj; -+} -+ -+int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb) -+{ -+ union acpi_object *obj; -+ int ret; -+ -+ obj = skl_int3472_get_acpi_buffer(adev, "CLDB"); -+ if (IS_ERR(obj)) -+ return PTR_ERR(obj); -+ -+ if (obj->buffer.length > sizeof(*cldb)) { -+ acpi_handle_err(adev->handle, "The CLDB buffer is too large\n"); -+ ret = -EINVAL; -+ goto out_free_obj; -+ } -+ -+ memcpy(cldb, obj->buffer.pointer, obj->buffer.length); -+ ret = 0; -+ -+out_free_obj: -+ kfree(obj); -+ return ret; -+} -diff --git a/drivers/platform/x86/intel/int3472/intel_skl_int3472_common.h b/drivers/platform/x86/intel/int3472/common.h -similarity index 94% -rename from drivers/platform/x86/intel/int3472/intel_skl_int3472_common.h -rename to drivers/platform/x86/intel/int3472/common.h -index 714fde73b524..d14944ee8586 100644 ---- a/drivers/platform/x86/intel/int3472/intel_skl_int3472_common.h -+++ b/drivers/platform/x86/intel/int3472/common.h -@@ -105,9 +105,6 @@ struct int3472_discrete_device { - struct gpiod_lookup_table gpios; - }; - --int skl_int3472_discrete_probe(struct platform_device *pdev); --int skl_int3472_discrete_remove(struct platform_device *pdev); --int skl_int3472_tps68470_probe(struct i2c_client *client); - union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev, - char *id); - int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb); -diff --git a/drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c b/drivers/platform/x86/intel/int3472/discrete.c -similarity index 93% -rename from drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c -rename to drivers/platform/x86/intel/int3472/discrete.c -index e59d79c7e82f..a19a1f5dbdd7 100644 ---- a/drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c -+++ b/drivers/platform/x86/intel/int3472/discrete.c -@@ -14,7 +14,7 @@ - #include - #include - --#include "intel_skl_int3472_common.h" -+#include "common.h" - - /* - * 79234640-9e10-4fea-a5c1-b5aa8b19756f -@@ -332,7 +332,9 @@ static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472) - return 0; - } - --int skl_int3472_discrete_probe(struct platform_device *pdev) -+static int skl_int3472_discrete_remove(struct platform_device *pdev); -+ -+static int skl_int3472_discrete_probe(struct platform_device *pdev) - { - struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); - struct int3472_discrete_device *int3472; -@@ -395,7 +397,7 @@ int skl_int3472_discrete_probe(struct platform_device *pdev) - return ret; - } - --int skl_int3472_discrete_remove(struct platform_device *pdev) -+static int skl_int3472_discrete_remove(struct platform_device *pdev) - { - struct int3472_discrete_device *int3472 = platform_get_drvdata(pdev); - -@@ -411,3 +413,23 @@ int skl_int3472_discrete_remove(struct platform_device *pdev) - - return 0; - } -+ -+static const struct acpi_device_id int3472_device_id[] = { -+ { "INT3472", 0 }, -+ { } -+}; -+MODULE_DEVICE_TABLE(acpi, int3472_device_id); -+ -+static struct platform_driver int3472_discrete = { -+ .driver = { -+ .name = "int3472-discrete", -+ .acpi_match_table = int3472_device_id, -+ }, -+ .probe = skl_int3472_discrete_probe, -+ .remove = skl_int3472_discrete_remove, -+}; -+module_platform_driver(int3472_discrete); -+ -+MODULE_DESCRIPTION("Intel SkyLake INT3472 ACPI Discrete Device Driver"); -+MODULE_AUTHOR("Daniel Scally "); -+MODULE_LICENSE("GPL v2"); -diff --git a/drivers/platform/x86/intel/int3472/intel_skl_int3472_tps68470.c b/drivers/platform/x86/intel/int3472/tps68470.c -similarity index 85% -rename from drivers/platform/x86/intel/int3472/intel_skl_int3472_tps68470.c -rename to drivers/platform/x86/intel/int3472/tps68470.c -index 42e688f4cad4..b94cf66ab61f 100644 ---- a/drivers/platform/x86/intel/int3472/intel_skl_int3472_tps68470.c -+++ b/drivers/platform/x86/intel/int3472/tps68470.c -@@ -7,7 +7,7 @@ - #include - #include - --#include "intel_skl_int3472_common.h" -+#include "common.h" - - #define DESIGNED_FOR_CHROMEOS 1 - #define DESIGNED_FOR_WINDOWS 2 -@@ -102,7 +102,7 @@ static int skl_int3472_tps68470_calc_type(struct acpi_device *adev) - return DESIGNED_FOR_WINDOWS; - } - --int skl_int3472_tps68470_probe(struct i2c_client *client) -+static int skl_int3472_tps68470_probe(struct i2c_client *client) - { - struct acpi_device *adev = ACPI_COMPANION(&client->dev); - struct regmap *regmap; -@@ -142,3 +142,22 @@ int skl_int3472_tps68470_probe(struct i2c_client *client) - - return ret; - } -+ -+static const struct acpi_device_id int3472_device_id[] = { -+ { "INT3472", 0 }, -+ { } -+}; -+MODULE_DEVICE_TABLE(acpi, int3472_device_id); -+ -+static struct i2c_driver int3472_tps68470 = { -+ .driver = { -+ .name = "int3472-tps68470", -+ .acpi_match_table = int3472_device_id, -+ }, -+ .probe_new = skl_int3472_tps68470_probe, -+}; -+module_i2c_driver(int3472_tps68470); -+ -+MODULE_DESCRIPTION("Intel SkyLake INT3472 ACPI TPS68470 Device Driver"); -+MODULE_AUTHOR("Daniel Scally "); -+MODULE_LICENSE("GPL v2"); --- -2.35.1 - -From b242c3b30351ab16ff6bead5718c046fa4543d71 Mon Sep 17 00:00:00 2001 -From: Hans de Goede -Date: Sun, 10 Oct 2021 20:57:04 +0200 -Subject: [PATCH] platform/x86: int3472: Add get_sensor_adev_and_name() helper - -The discrete.c code is not the only code which needs to lookup the -acpi_device and device-name for the sensor for which the INT3472 -ACPI-device is a GPIO/clk/regulator provider. - -The tps68470.c code also needs this functionality, so factor this -out into a new get_sensor_adev_and_name() helper. - -Signed-off-by: Hans de Goede -Patchset: cameras ---- - drivers/platform/x86/intel/int3472/common.c | 28 +++++++++++++++++++ - drivers/platform/x86/intel/int3472/common.h | 3 ++ - drivers/platform/x86/intel/int3472/discrete.c | 22 +++------------ - 3 files changed, 35 insertions(+), 18 deletions(-) - -diff --git a/drivers/platform/x86/intel/int3472/common.c b/drivers/platform/x86/intel/int3472/common.c -index 350655a9515b..77cf058e4168 100644 ---- a/drivers/platform/x86/intel/int3472/common.c -+++ b/drivers/platform/x86/intel/int3472/common.c -@@ -52,3 +52,31 @@ int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb) - kfree(obj); - return ret; - } -+ -+/* sensor_adev_ret may be NULL, name_ret must not be NULL */ -+int skl_int3472_get_sensor_adev_and_name(struct device *dev, -+ struct acpi_device **sensor_adev_ret, -+ const char **name_ret) -+{ -+ struct acpi_device *adev = ACPI_COMPANION(dev); -+ struct acpi_device *sensor; -+ int ret = 0; -+ -+ sensor = acpi_dev_get_first_consumer_dev(adev); -+ if (!sensor) { -+ dev_err(dev, "INT3472 seems to have no dependents.\n"); -+ return -ENODEV; -+ } -+ -+ *name_ret = devm_kasprintf(dev, GFP_KERNEL, I2C_DEV_NAME_FORMAT, -+ acpi_dev_name(sensor)); -+ if (!*name_ret) -+ ret = -ENOMEM; -+ -+ if (ret == 0 && sensor_adev_ret) -+ *sensor_adev_ret = sensor; -+ else -+ acpi_dev_put(sensor); -+ -+ return ret; -+} -diff --git a/drivers/platform/x86/intel/int3472/common.h b/drivers/platform/x86/intel/int3472/common.h -index d14944ee8586..53270d19c73a 100644 ---- a/drivers/platform/x86/intel/int3472/common.h -+++ b/drivers/platform/x86/intel/int3472/common.h -@@ -108,6 +108,9 @@ struct int3472_discrete_device { - union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev, - char *id); - int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb); -+int skl_int3472_get_sensor_adev_and_name(struct device *dev, -+ struct acpi_device **sensor_adev_ret, -+ const char **name_ret); - - int skl_int3472_register_clock(struct int3472_discrete_device *int3472); - void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472); -diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c -index a19a1f5dbdd7..efd31a0c7a88 100644 ---- a/drivers/platform/x86/intel/int3472/discrete.c -+++ b/drivers/platform/x86/intel/int3472/discrete.c -@@ -363,19 +363,10 @@ static int skl_int3472_discrete_probe(struct platform_device *pdev) - int3472->dev = &pdev->dev; - platform_set_drvdata(pdev, int3472); - -- int3472->sensor = acpi_dev_get_first_consumer_dev(adev); -- if (!int3472->sensor) { -- dev_err(&pdev->dev, "INT3472 seems to have no dependents.\n"); -- return -ENODEV; -- } -- -- int3472->sensor_name = devm_kasprintf(int3472->dev, GFP_KERNEL, -- I2C_DEV_NAME_FORMAT, -- acpi_dev_name(int3472->sensor)); -- if (!int3472->sensor_name) { -- ret = -ENOMEM; -- goto err_put_sensor; -- } -+ ret = skl_int3472_get_sensor_adev_and_name(&pdev->dev, &int3472->sensor, -+ &int3472->sensor_name); -+ if (ret) -+ return ret; - - /* - * Initialising this list means we can call gpiod_remove_lookup_table() -@@ -390,11 +381,6 @@ static int skl_int3472_discrete_probe(struct platform_device *pdev) - } - - return 0; -- --err_put_sensor: -- acpi_dev_put(int3472->sensor); -- -- return ret; - } - - static int skl_int3472_discrete_remove(struct platform_device *pdev) --- -2.35.1 - -From ce6d00f1afc889c5cd0b93e8d7c4c26568ed5ade Mon Sep 17 00:00:00 2001 -From: Hans de Goede -Date: Sun, 10 Oct 2021 20:57:05 +0200 -Subject: [PATCH] platform/x86: int3472: Pass tps68470_clk_platform_data to the - tps68470-regulator MFD-cell - -Pass tps68470_clk_platform_data to the tps68470-clk MFD-cell, -so that sensors which use the TPS68470 can find their clock. - -Signed-off-by: Hans de Goede -Patchset: cameras ---- - drivers/platform/x86/intel/int3472/tps68470.c | 33 ++++++++++++++----- - 1 file changed, 25 insertions(+), 8 deletions(-) - -diff --git a/drivers/platform/x86/intel/int3472/tps68470.c b/drivers/platform/x86/intel/int3472/tps68470.c -index b94cf66ab61f..78e34e7b6969 100644 ---- a/drivers/platform/x86/intel/int3472/tps68470.c -+++ b/drivers/platform/x86/intel/int3472/tps68470.c -@@ -5,6 +5,7 @@ - #include - #include - #include -+#include - #include - - #include "common.h" -@@ -17,12 +18,6 @@ static const struct mfd_cell tps68470_cros[] = { - { .name = "tps68470_pmic_opregion" }, - }; - --static const struct mfd_cell tps68470_win[] = { -- { .name = "tps68470-gpio" }, -- { .name = "tps68470-clk" }, -- { .name = "tps68470-regulator" }, --}; -- - static const struct regmap_config tps68470_regmap_config = { - .reg_bits = 8, - .val_bits = 8, -@@ -105,10 +100,17 @@ static int skl_int3472_tps68470_calc_type(struct acpi_device *adev) - static int skl_int3472_tps68470_probe(struct i2c_client *client) - { - struct acpi_device *adev = ACPI_COMPANION(&client->dev); -+ struct tps68470_clk_platform_data clk_pdata = {}; -+ struct mfd_cell *cells; - struct regmap *regmap; - int device_type; - int ret; - -+ ret = skl_int3472_get_sensor_adev_and_name(&client->dev, NULL, -+ &clk_pdata.consumer_dev_name); -+ if (ret) -+ return ret; -+ - regmap = devm_regmap_init_i2c(client, &tps68470_regmap_config); - if (IS_ERR(regmap)) { - dev_err(&client->dev, "Failed to create regmap: %ld\n", PTR_ERR(regmap)); -@@ -126,9 +128,24 @@ static int skl_int3472_tps68470_probe(struct i2c_client *client) - device_type = skl_int3472_tps68470_calc_type(adev); - switch (device_type) { - case DESIGNED_FOR_WINDOWS: -- ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE, -- tps68470_win, ARRAY_SIZE(tps68470_win), -+ cells = kcalloc(3, sizeof(*cells), GFP_KERNEL); -+ if (!cells) -+ return -ENOMEM; -+ -+ cells[0].name = "tps68470-clk"; -+ cells[0].platform_data = &clk_pdata; -+ cells[0].pdata_size = sizeof(clk_pdata); -+ cells[1].name = "tps68470-regulator"; -+ /* -+ * The GPIO cell must be last because acpi_gpiochip_add() calls -+ * acpi_dev_clear_dependencies() and the clk + regulators must -+ * be ready when this happens. -+ */ -+ cells[2].name = "tps68470-gpio"; -+ -+ ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE, cells, 3, - NULL, 0, NULL); -+ kfree(cells); - break; - case DESIGNED_FOR_CHROMEOS: - ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE, --- -2.35.1 - -From 16cd889346121483b60ecde95f20bc2b1fe1eb19 Mon Sep 17 00:00:00 2001 -From: Hans de Goede -Date: Sun, 10 Oct 2021 20:57:06 +0200 -Subject: [PATCH] platform/x86: int3472: Pass tps68470_regulator_platform_data - to the tps68470-regulator MFD-cell - -Pass tps68470_regulator_platform_data to the tps68470-regulator -MFD-cell, specifying the voltages of the various regulators and -tying the regulators to the sensor supplies so that sensors which use -the TPS68470 can find their regulators. - -Since the voltages and supply connections are board-specific, this -introduces a DMI matches int3472_tps68470_board_data struct which -contains the necessary per-board info. - -This per-board info also includes GPIO lookup information for the -sensor GPIOs which may be connected to the tps68470 gpios. - -Signed-off-by: Hans de Goede -Patchset: cameras ---- - drivers/platform/x86/intel/int3472/Makefile | 2 +- - drivers/platform/x86/intel/int3472/tps68470.c | 43 +++++-- - drivers/platform/x86/intel/int3472/tps68470.h | 25 ++++ - .../x86/intel/int3472/tps68470_board_data.c | 118 ++++++++++++++++++ - 4 files changed, 180 insertions(+), 8 deletions(-) - create mode 100644 drivers/platform/x86/intel/int3472/tps68470.h - create mode 100644 drivers/platform/x86/intel/int3472/tps68470_board_data.c - -diff --git a/drivers/platform/x86/intel/int3472/Makefile b/drivers/platform/x86/intel/int3472/Makefile -index 4a4b2518ea16..ca56e7eea781 100644 ---- a/drivers/platform/x86/intel/int3472/Makefile -+++ b/drivers/platform/x86/intel/int3472/Makefile -@@ -1,4 +1,4 @@ - obj-$(CONFIG_INTEL_SKL_INT3472) += intel_skl_int3472_discrete.o \ - intel_skl_int3472_tps68470.o - intel_skl_int3472_discrete-y := discrete.o clk_and_regulator.o common.o --intel_skl_int3472_tps68470-y := tps68470.o common.o -+intel_skl_int3472_tps68470-y := tps68470.o tps68470_board_data.o common.o -diff --git a/drivers/platform/x86/intel/int3472/tps68470.c b/drivers/platform/x86/intel/int3472/tps68470.c -index 78e34e7b6969..aae24d228770 100644 ---- a/drivers/platform/x86/intel/int3472/tps68470.c -+++ b/drivers/platform/x86/intel/int3472/tps68470.c -@@ -9,6 +9,7 @@ - #include - - #include "common.h" -+#include "tps68470.h" - - #define DESIGNED_FOR_CHROMEOS 1 - #define DESIGNED_FOR_WINDOWS 2 -@@ -100,6 +101,7 @@ static int skl_int3472_tps68470_calc_type(struct acpi_device *adev) - static int skl_int3472_tps68470_probe(struct i2c_client *client) - { - struct acpi_device *adev = ACPI_COMPANION(&client->dev); -+ const struct int3472_tps68470_board_data *board_data; - struct tps68470_clk_platform_data clk_pdata = {}; - struct mfd_cell *cells; - struct regmap *regmap; -@@ -128,6 +130,12 @@ static int skl_int3472_tps68470_probe(struct i2c_client *client) - device_type = skl_int3472_tps68470_calc_type(adev); - switch (device_type) { - case DESIGNED_FOR_WINDOWS: -+ board_data = int3472_tps68470_get_board_data(dev_name(&client->dev)); -+ if (!board_data) { -+ dev_err(&client->dev, "No board-data found for this laptop/tablet model\n"); -+ return -ENODEV; -+ } -+ - cells = kcalloc(3, sizeof(*cells), GFP_KERNEL); - if (!cells) - return -ENOMEM; -@@ -136,6 +144,8 @@ static int skl_int3472_tps68470_probe(struct i2c_client *client) - cells[0].platform_data = &clk_pdata; - cells[0].pdata_size = sizeof(clk_pdata); - cells[1].name = "tps68470-regulator"; -+ cells[1].platform_data = (void *)board_data->tps68470_regulator_pdata; -+ cells[1].pdata_size = sizeof(struct tps68470_regulator_platform_data); - /* - * The GPIO cell must be last because acpi_gpiochip_add() calls - * acpi_dev_clear_dependencies() and the clk + regulators must -@@ -143,9 +153,15 @@ static int skl_int3472_tps68470_probe(struct i2c_client *client) - */ - cells[2].name = "tps68470-gpio"; - -+ gpiod_add_lookup_table(board_data->tps68470_gpio_lookup_table); -+ - ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE, cells, 3, - NULL, 0, NULL); - kfree(cells); -+ -+ if (ret) -+ gpiod_remove_lookup_table(board_data->tps68470_gpio_lookup_table); -+ - break; - case DESIGNED_FOR_CHROMEOS: - ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE, -@@ -160,18 +176,31 @@ static int skl_int3472_tps68470_probe(struct i2c_client *client) - return ret; - } - -+static int skl_int3472_tps68470_remove(struct i2c_client *client) -+{ -+ const struct int3472_tps68470_board_data *board_data; -+ -+ board_data = int3472_tps68470_get_board_data(dev_name(&client->dev)); -+ if (board_data) -+ gpiod_remove_lookup_table(board_data->tps68470_gpio_lookup_table); -+ -+ return 0; -+} -+ -+ - static const struct acpi_device_id int3472_device_id[] = { -- { "INT3472", 0 }, -- { } -+ { "INT3472", 0 }, -+ { } - }; - MODULE_DEVICE_TABLE(acpi, int3472_device_id); - - static struct i2c_driver int3472_tps68470 = { -- .driver = { -- .name = "int3472-tps68470", -- .acpi_match_table = int3472_device_id, -- }, -- .probe_new = skl_int3472_tps68470_probe, -+ .driver = { -+ .name = "int3472-tps68470", -+ .acpi_match_table = int3472_device_id, -+ }, -+ .probe_new = skl_int3472_tps68470_probe, -+ .remove = skl_int3472_tps68470_remove, - }; - module_i2c_driver(int3472_tps68470); - -diff --git a/drivers/platform/x86/intel/int3472/tps68470.h b/drivers/platform/x86/intel/int3472/tps68470.h -new file mode 100644 -index 000000000000..cfd33eb62740 ---- /dev/null -+++ b/drivers/platform/x86/intel/int3472/tps68470.h -@@ -0,0 +1,25 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * TI TPS68470 PMIC platform data definition. -+ * -+ * Copyright (c) 2021 Red Hat Inc. -+ * -+ * Red Hat authors: -+ * Hans de Goede -+ */ -+ -+#ifndef _INTEL_SKL_INT3472_TPS68470_H -+#define _INTEL_SKL_INT3472_TPS68470_H -+ -+struct gpiod_lookup_table; -+struct tps68470_regulator_platform_data; -+ -+struct int3472_tps68470_board_data { -+ const char *dev_name; -+ struct gpiod_lookup_table *tps68470_gpio_lookup_table; -+ const struct tps68470_regulator_platform_data *tps68470_regulator_pdata; -+}; -+ -+const struct int3472_tps68470_board_data *int3472_tps68470_get_board_data(const char *dev_name); -+ -+#endif -diff --git a/drivers/platform/x86/intel/int3472/tps68470_board_data.c b/drivers/platform/x86/intel/int3472/tps68470_board_data.c -new file mode 100644 -index 000000000000..96954a789bb8 ---- /dev/null -+++ b/drivers/platform/x86/intel/int3472/tps68470_board_data.c -@@ -0,0 +1,118 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * TI TPS68470 PMIC platform data definition. -+ * -+ * Copyright (c) 2021 Dan Scally -+ * Copyright (c) 2021 Red Hat Inc. -+ * -+ * Red Hat authors: -+ * Hans de Goede -+ */ -+ -+#include -+#include -+#include -+#include -+#include "tps68470.h" -+ -+static struct regulator_consumer_supply int347a_core_consumer_supplies[] = { -+ REGULATOR_SUPPLY("dvdd", "i2c-INT347A:00"), -+}; -+ -+static struct regulator_consumer_supply int347a_ana_consumer_supplies[] = { -+ REGULATOR_SUPPLY("avdd", "i2c-INT347A:00"), -+}; -+ -+static struct regulator_consumer_supply int347a_vsio_consumer_supplies[] = { -+ REGULATOR_SUPPLY("dovdd", "i2c-INT347A:00"), -+}; -+ -+static const struct regulator_init_data surface_go_tps68470_core_reg_init_data = { -+ .constraints = { -+ .min_uV = 1200000, -+ .max_uV = 1200000, -+ .apply_uV = 1, -+ .valid_ops_mask = REGULATOR_CHANGE_STATUS, -+ }, -+ .num_consumer_supplies = ARRAY_SIZE(int347a_core_consumer_supplies), -+ .consumer_supplies = int347a_core_consumer_supplies, -+}; -+ -+static const struct regulator_init_data surface_go_tps68470_ana_reg_init_data = { -+ .constraints = { -+ .min_uV = 2815200, -+ .max_uV = 2815200, -+ .apply_uV = 1, -+ .valid_ops_mask = REGULATOR_CHANGE_STATUS, -+ }, -+ .num_consumer_supplies = ARRAY_SIZE(int347a_ana_consumer_supplies), -+ .consumer_supplies = int347a_ana_consumer_supplies, -+}; -+ -+static const struct regulator_init_data surface_go_tps68470_vsio_reg_init_data = { -+ .constraints = { -+ .min_uV = 1800600, -+ .max_uV = 1800600, -+ .apply_uV = 1, -+ .valid_ops_mask = REGULATOR_CHANGE_STATUS, -+ }, -+ .num_consumer_supplies = ARRAY_SIZE(int347a_vsio_consumer_supplies), -+ .consumer_supplies = int347a_vsio_consumer_supplies, -+}; -+ -+static const struct tps68470_regulator_platform_data surface_go_tps68470_pdata = { -+ .reg_init_data = { -+ [TPS68470_CORE] = &surface_go_tps68470_core_reg_init_data, -+ [TPS68470_ANA] = &surface_go_tps68470_ana_reg_init_data, -+ [TPS68470_VSIO] = &surface_go_tps68470_vsio_reg_init_data, -+ }, -+}; -+ -+static struct gpiod_lookup_table surface_go_tps68470_gpios = { -+ .dev_id = "i2c-INT347A:00", -+ .table = { -+ GPIO_LOOKUP("tps68470-gpio", 9, "reset", GPIO_ACTIVE_LOW), -+ GPIO_LOOKUP("tps68470-gpio", 7, "powerdown", GPIO_ACTIVE_LOW) -+ } -+}; -+ -+static const struct int3472_tps68470_board_data surface_go_tps68470_board_data = { -+ .dev_name = "i2c-INT3472:05", -+ .tps68470_gpio_lookup_table = &surface_go_tps68470_gpios, -+ .tps68470_regulator_pdata = &surface_go_tps68470_pdata, -+}; -+ -+static const struct dmi_system_id int3472_tps68470_board_data_table[] = { -+ { -+ .matches = { -+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), -+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Go"), -+ }, -+ .driver_data = (void *)&surface_go_tps68470_board_data, -+ }, -+ { -+ .matches = { -+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), -+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Go 2"), -+ }, -+ .driver_data = (void *)&surface_go_tps68470_board_data, -+ }, -+ { } -+}; -+ -+const struct int3472_tps68470_board_data *int3472_tps68470_get_board_data(const char *dev_name) -+{ -+ const struct int3472_tps68470_board_data *board_data; -+ const struct dmi_system_id *match; -+ -+ match = dmi_first_match(int3472_tps68470_board_data_table); -+ while (match) { -+ board_data = match->driver_data; -+ if (strcmp(board_data->dev_name, dev_name) == 0) -+ return board_data; -+ -+ dmi_first_match(++match); -+ } -+ -+ return NULL; -+} --- -2.35.1 - -From ebdde93fb0053e945e6fe053a14b150d299c2500 Mon Sep 17 00:00:00 2001 -From: Hans de Goede -Date: Sun, 10 Oct 2021 20:57:07 +0200 -Subject: [PATCH] platform/x86: int3472: Deal with probe ordering issues - -The clk and regulator frameworks expect clk/regulator consumer-devices -to have info about the consumed clks/regulators described in the device's -fw_node. - -To work around this info missing from the ACPI tables on devices where -the int3472 driver is used, the int3472 MFD-cell drivers attach info about -consumers to the clks/regulators when registering these. - -This causes problems with the probe ordering wrt drivers for consumers -of these clks/regulators. Since the lookups are only registered when the -provider-driver binds, trying to get these clks/regulators before then -results in a -ENOENT error for clks and a dummy regulator for regulators. - -All the sensor ACPI fw-nodes have a _DEP dependency on the INT3472 ACPI -fw-node, so to work around these probe ordering issues the ACPI core / -i2c-code does not instantiate the I2C-clients for any ACPI devices -which have a _DEP dependency on an INT3472 ACPI device until all -_DEP-s are met. - -This relies on acpi_dev_clear_dependencies() getting called by the driver -for the _DEP-s when they are ready, add a acpi_dev_clear_dependencies() -call to the discrete.c probe code. - -In the tps68470 case calling acpi_dev_clear_dependencies() is already done -by the acpi_gpiochip_add() call done by the driver for the GPIO MFD cell -(The GPIO cell is deliberately the last cell created to make sure the -clk + regulator cells are already instantiated when this happens). - -However for proper probe ordering, the clk/regulator cells must not just -be instantiated the must be fully ready (the clks + regulators must be -registered with their subsystems). - -Add MODULE_SOFTDEP dependencies for the clk and regulator drivers for -the instantiated MFD-cells so that these are loaded before us and so -that they bind immediately when the platform-devs are instantiated. - -Signed-off-by: Hans de Goede -Patchset: cameras ---- - drivers/platform/x86/intel/int3472/discrete.c | 1 + - drivers/platform/x86/intel/int3472/tps68470.c | 6 ++++++ - 2 files changed, 7 insertions(+) - -diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c -index efd31a0c7a88..18e6d51acc96 100644 ---- a/drivers/platform/x86/intel/int3472/discrete.c -+++ b/drivers/platform/x86/intel/int3472/discrete.c -@@ -380,6 +380,7 @@ static int skl_int3472_discrete_probe(struct platform_device *pdev) - return ret; - } - -+ acpi_dev_clear_dependencies(adev); - return 0; - } - -diff --git a/drivers/platform/x86/intel/int3472/tps68470.c b/drivers/platform/x86/intel/int3472/tps68470.c -index aae24d228770..21c6c1a6edfc 100644 ---- a/drivers/platform/x86/intel/int3472/tps68470.c -+++ b/drivers/platform/x86/intel/int3472/tps68470.c -@@ -173,6 +173,11 @@ static int skl_int3472_tps68470_probe(struct i2c_client *client) - return device_type; - } - -+ /* -+ * No acpi_dev_clear_dependencies() here, since the acpi_gpiochip_add() -+ * for the GPIO cell already does this. -+ */ -+ - return ret; - } - -@@ -207,3 +212,4 @@ module_i2c_driver(int3472_tps68470); - MODULE_DESCRIPTION("Intel SkyLake INT3472 ACPI TPS68470 Device Driver"); - MODULE_AUTHOR("Daniel Scally "); - MODULE_LICENSE("GPL v2"); -+MODULE_SOFTDEP("pre: clk-tps68470 tps68470-regulator"); --- -2.35.1 - -From 12c17f7f09169af045cef3b15728c98403336c6c Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Thu, 4 Nov 2021 21:46:27 +0000 -Subject: [PATCH] media: i2c: Add integration time margin to ov8865 - -Without this integration margin to reduce the max exposure, it seems -that we trip over a limit that results in the image being entirely -black when max exposure is set. Add the margin to prevent this issue. - -With thanks to jhautbois for spotting and reporting. - -Signed-off-by: Daniel Scally -Patchset: cameras ---- - drivers/media/i2c/ov8865.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c -index 5ef9c407362a..ed038efbc084 100644 ---- a/drivers/media/i2c/ov8865.c -+++ b/drivers/media/i2c/ov8865.c -@@ -143,6 +143,7 @@ - #define OV8865_EXPOSURE_CTRL_L_REG 0x3502 - #define OV8865_EXPOSURE_CTRL_L(v) ((v) & GENMASK(7, 0)) - #define OV8865_EXPOSURE_GAIN_MANUAL_REG 0x3503 -+#define OV8865_INTEGRATION_TIME_MARGIN 8 - - #define OV8865_GAIN_CTRL_H_REG 0x3508 - #define OV8865_GAIN_CTRL_H(v) (((v) & GENMASK(12, 8)) >> 8) -@@ -2464,7 +2465,8 @@ static int ov8865_s_ctrl(struct v4l2_ctrl *ctrl) - if (ctrl->id == V4L2_CID_VBLANK) { - int exposure_max; - -- exposure_max = sensor->state.mode->output_size_y + ctrl->val; -+ exposure_max = sensor->state.mode->output_size_y + ctrl->val - -+ OV8865_INTEGRATION_TIME_MARGIN; - __v4l2_ctrl_modify_range(sensor->ctrls.exposure, - sensor->ctrls.exposure->minimum, - exposure_max, --- -2.35.1 - -From c7b51fa424ff2bf8398c412154017066e63fd909 Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Thu, 4 Nov 2021 21:48:38 +0000 -Subject: [PATCH] media: i2c: Fix max gain in ov8865 - -The maximum gain figure in the v4l2 ctrl is wrong. The field is 12 bits -wide, which is where the 8191 figure comes from, but the datasheet is -specific that maximum gain is 16x (the low seven bits are fractional, so -16x gain is 2048) - -Signed-off-by: Daniel Scally -Patchset: cameras ---- - drivers/media/i2c/ov8865.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c -index ed038efbc084..5bedcddafe36 100644 ---- a/drivers/media/i2c/ov8865.c -+++ b/drivers/media/i2c/ov8865.c -@@ -2537,7 +2537,7 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor) - - /* Gain */ - -- v4l2_ctrl_new_std(handler, ops, V4L2_CID_ANALOGUE_GAIN, 128, 8191, 128, -+ v4l2_ctrl_new_std(handler, ops, V4L2_CID_ANALOGUE_GAIN, 128, 2048, 128, - 128); - - /* White Balance */ --- -2.35.1 - -From 6812a707a23ec35749f529e15eae3ff5a32bd0af Mon Sep 17 00:00:00 2001 -From: Hans de Goede -Date: Fri, 3 Dec 2021 12:51:08 +0100 -Subject: [PATCH] mfd: intel-lpss: Fix I2C4 not being available on the - Microsoft Surface Go & Go 2 - -Many DSDTs for Kaby Lake and Kaby Lake Refresh models contain a -_SB.PCI0.GEXP ACPI Device node describing an I2C attached PCA953x -GPIO expander. - -This seems to be something which is copy and pasted from the DSDT -from some reference design since this ACPI Device is present even on -models where no such GPIO expander is used at all, such as on the -Microsoft Surface Go & Go 2. - -This ACPI Device is a problem because it contains a SystemMemory -OperationRegion which covers the MMIO for the I2C4 I2C controller. This -causes the MFD cell for the I2C4 controller to not be instantiated due -to a resource conflict, requiring the use of acpi_enforce_resources=lax -to work around this. - -I have done an extensive analysis of all the ACPI tables on the -Microsoft Surface Go and the _SB.PCI0.GEXP ACPI Device's methods are -not used by any code in the ACPI tables, neither are any of them -directly called by any Linux kernel code. This is unsurprising since -running i2cdetect on the I2C4 bus shows that there is no GPIO -expander chip present on these devices at all. - -This commit adds a PCI subsystem vendor:device table listing PCI devices -where it is known to be safe to ignore resource conflicts with ACPI -declared SystemMemory regions. - -This makes the I2C4 bus work out of the box on the Microsoft Surface -Go & Go 2, which is necessary for the cameras on these devices to work. - -Cc: Dan Scally -Cc: Kate Hsuan -Cc: Maximilian Luz -Reviewed-by: Laurent Pinchart -Reviewed-by: Andy Shevchenko -Signed-off-by: Hans de Goede -Patchset: cameras ---- - drivers/mfd/intel-lpss-pci.c | 12 ++++++++++++ - drivers/mfd/intel-lpss.c | 1 + - drivers/mfd/intel-lpss.h | 1 + - 3 files changed, 14 insertions(+) - -diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c -index f70464ce8e3d..22eefd71f8c4 100644 ---- a/drivers/mfd/intel-lpss-pci.c -+++ b/drivers/mfd/intel-lpss-pci.c -@@ -17,6 +17,15 @@ - - #include "intel-lpss.h" - -+/* Some DSDTs have an unused GEXP ACPI device conflicting with I2C4 resources */ -+static const struct pci_device_id ignore_resource_conflicts_ids[] = { -+ /* Microsoft Surface Go (version 1) I2C4 */ -+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_INTEL, 0x9d64, 0x152d, 0x1182), }, -+ /* Microsoft Surface Go 2 I2C4 */ -+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_INTEL, 0x9d64, 0x152d, 0x1237), }, -+ { } -+}; -+ - static int intel_lpss_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *id) - { -@@ -35,6 +44,9 @@ static int intel_lpss_pci_probe(struct pci_dev *pdev, - info->mem = &pdev->resource[0]; - info->irq = pdev->irq; - -+ if (pci_match_id(ignore_resource_conflicts_ids, pdev)) -+ info->ignore_resource_conflicts = true; -+ - pdev->d3cold_delay = 0; - - /* Probably it is enough to set this for iDMA capable devices only */ -diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c -index 0e15afc39f54..cfbee2cfba6b 100644 ---- a/drivers/mfd/intel-lpss.c -+++ b/drivers/mfd/intel-lpss.c -@@ -401,6 +401,7 @@ int intel_lpss_probe(struct device *dev, - return ret; - - lpss->cell->swnode = info->swnode; -+ lpss->cell->ignore_resource_conflicts = info->ignore_resource_conflicts; - - intel_lpss_init_dev(lpss); - -diff --git a/drivers/mfd/intel-lpss.h b/drivers/mfd/intel-lpss.h -index 22dbc4aed793..062ce95b68b9 100644 ---- a/drivers/mfd/intel-lpss.h -+++ b/drivers/mfd/intel-lpss.h -@@ -19,6 +19,7 @@ struct software_node; - - struct intel_lpss_platform_info { - struct resource *mem; -+ bool ignore_resource_conflicts; - int irq; - unsigned long clk_rate; - const char *clk_con_id; --- -2.35.1 - -From ffbd58376161c9353d29e8af0438114e37bd9e57 Mon Sep 17 00:00:00 2001 -From: Daniel Scally -Date: Thu, 6 Jan 2022 22:12:38 +0000 -Subject: [PATCH] platform/x86: int3472: Add board data for Surface Go 3 - -The Surface Go 3 needs some board data in order to configure the -TPS68470 PMIC - add entries to the tables in tps68470_board_data.c -that define the configuration that's needed. - -Signed-off-by: Daniel Scally -Patchset: cameras ---- - .../x86/intel/int3472/tps68470_board_data.c | 13 +++++++++++++ - 1 file changed, 13 insertions(+) - -diff --git a/drivers/platform/x86/intel/int3472/tps68470_board_data.c b/drivers/platform/x86/intel/int3472/tps68470_board_data.c -index 96954a789bb8..2dcadfa62196 100644 ---- a/drivers/platform/x86/intel/int3472/tps68470_board_data.c -+++ b/drivers/platform/x86/intel/int3472/tps68470_board_data.c -@@ -82,6 +82,12 @@ static const struct int3472_tps68470_board_data surface_go_tps68470_board_data = - .tps68470_regulator_pdata = &surface_go_tps68470_pdata, - }; - -+static const struct int3472_tps68470_board_data surface_go3_tps68470_board_data = { -+ .dev_name = "i2c-INT3472:01", -+ .tps68470_gpio_lookup_table = &surface_go_tps68470_gpios, -+ .tps68470_regulator_pdata = &surface_go_tps68470_pdata, -+}; -+ - static const struct dmi_system_id int3472_tps68470_board_data_table[] = { - { - .matches = { -@@ -97,6 +103,13 @@ static const struct dmi_system_id int3472_tps68470_board_data_table[] = { - }, - .driver_data = (void *)&surface_go_tps68470_board_data, - }, -+ { -+ .matches = { -+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), -+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Go 3"), -+ }, -+ .driver_data = (void *)&surface_go3_tps68470_board_data, -+ }, - { } - }; - --- -2.35.1 - diff --git a/patches/5.16/0012-amd-gpio.patch b/patches/5.16/0012-amd-gpio.patch deleted file mode 100644 index d731ae742..000000000 --- a/patches/5.16/0012-amd-gpio.patch +++ /dev/null @@ -1,109 +0,0 @@ -From 5f73c0ef09d059062b1596eb22b9f8ba6625f3c5 Mon Sep 17 00:00:00 2001 -From: Sachi King -Date: Sat, 29 May 2021 17:47:38 +1000 -Subject: [PATCH] ACPI: Add quirk for Surface Laptop 4 AMD missing irq 7 - override - -This patch is the work of Thomas Gleixner and is -copied from: -https://lore.kernel.org/lkml/87lf8ddjqx.ffs@nanos.tec.linutronix.de/ - -This patch adds a quirk to the ACPI setup to patch in the the irq 7 pin -setup that is missing in the laptops ACPI table. - -This patch was used for validation of the issue, and is not a proper -fix, but is probably a better temporary hack than continuing to probe -the Legacy PIC and run with the PIC in an unknown state. - -Patchset: amd-gpio ---- - arch/x86/kernel/acpi/boot.c | 17 +++++++++++++++++ - 1 file changed, 17 insertions(+) - -diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c -index 5b6d1a95776f..0a05e196419a 100644 ---- a/arch/x86/kernel/acpi/boot.c -+++ b/arch/x86/kernel/acpi/boot.c -@@ -22,6 +22,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -1152,6 +1153,17 @@ static void __init mp_config_acpi_legacy_irqs(void) - } - } - -+static const struct dmi_system_id surface_quirk[] __initconst = { -+ { -+ .ident = "Microsoft Surface Laptop 4 (AMD)", -+ .matches = { -+ DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), -+ DMI_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_4_1952:1953") -+ }, -+ }, -+ {} -+}; -+ - /* - * Parse IOAPIC related entries in MADT - * returns 0 on success, < 0 on error -@@ -1207,6 +1219,11 @@ static int __init acpi_parse_madt_ioapic_entries(void) - acpi_sci_ioapic_setup(acpi_gbl_FADT.sci_interrupt, 0, 0, - acpi_gbl_FADT.sci_interrupt); - -+ if (dmi_check_system(surface_quirk)) { -+ pr_warn("Surface hack: Override irq 7\n"); -+ mp_override_legacy_irq(7, 3, 3, 7); -+ } -+ - /* Fill in identity legacy mappings where no override */ - mp_config_acpi_legacy_irqs(); - --- -2.35.1 - -From 1d96852a8adf322ce139bbf7e850a8f076d88dd6 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Thu, 3 Jun 2021 14:04:26 +0200 -Subject: [PATCH] ACPI: Add AMD 13" Surface Laptop 4 model to irq 7 override - quirk - -The 13" version of the Surface Laptop 4 has the same problem as the 15" -version, but uses a different SKU. Add that SKU to the quirk as well. - -Patchset: amd-gpio ---- - arch/x86/kernel/acpi/boot.c | 9 ++++++++- - 1 file changed, 8 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c -index 0a05e196419a..35de5613980a 100644 ---- a/arch/x86/kernel/acpi/boot.c -+++ b/arch/x86/kernel/acpi/boot.c -@@ -1155,12 +1155,19 @@ static void __init mp_config_acpi_legacy_irqs(void) - - static const struct dmi_system_id surface_quirk[] __initconst = { - { -- .ident = "Microsoft Surface Laptop 4 (AMD)", -+ .ident = "Microsoft Surface Laptop 4 (AMD 15\")", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - DMI_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_4_1952:1953") - }, - }, -+ { -+ .ident = "Microsoft Surface Laptop 4 (AMD 13\")", -+ .matches = { -+ DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), -+ DMI_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_4_1958:1959") -+ }, -+ }, - {} - }; - --- -2.35.1 - diff --git a/patches/5.16/0012-misc-fixes.patch b/patches/5.16/0012-misc-fixes.patch index a3f58f9b0..24376ba9d 100644 --- a/patches/5.16/0012-misc-fixes.patch +++ b/patches/5.16/0012-misc-fixes.patch @@ -1,4 +1,4 @@ -From 26338be871ace1f30bdc15b6664327e9ccdcb66b Mon Sep 17 00:00:00 2001 +From 0169488e6792b0939ec350c59b608e744faf3fc4 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Wed, 8 Dec 2021 16:22:50 +0100 Subject: [PATCH] acpi/battery: Add device HID and quirk for Microsoft Surface @@ -51,7 +51,7 @@ index ead0114f27c9..56db7b4da514 100644 -- 2.35.1 -From 8478d4f8fac3741896c52d2091b8ac81a719d17c Mon Sep 17 00:00:00 2001 +From ed246dbe6d9a05f195f78fb1b325b088add2321a Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Tue, 8 Feb 2022 01:29:48 +0100 Subject: [PATCH] ACPI: battery: Add "Not Charging" quirk for Microsoft Surface diff --git a/patches/5.16/0013-misc-fixes.patch b/patches/5.16/0013-misc-fixes.patch deleted file mode 100644 index 24376ba9d..000000000 --- a/patches/5.16/0013-misc-fixes.patch +++ /dev/null @@ -1,111 +0,0 @@ -From 0169488e6792b0939ec350c59b608e744faf3fc4 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Wed, 8 Dec 2021 16:22:50 +0100 -Subject: [PATCH] acpi/battery: Add device HID and quirk for Microsoft Surface - Go 3 - -For some reason, the Microsoft Surface Go 3 uses the standard ACPI -interface for battery information, but does not use the standard PNP0C0A -HID. Instead it uses MSHW0146 as identifier. Add that ID to the driver -as this seems to work well. - -Additionally, the power state is not updated immediately after the AC -has been (un-)plugged, so add the respective quirk for that. - -Signed-off-by: Maximilian Luz -Patchset: misc-fixes ---- - drivers/acpi/battery.c | 12 ++++++++++++ - 1 file changed, 12 insertions(+) - -diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c -index ead0114f27c9..56db7b4da514 100644 ---- a/drivers/acpi/battery.c -+++ b/drivers/acpi/battery.c -@@ -60,6 +60,10 @@ MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); - - static const struct acpi_device_id battery_device_ids[] = { - {"PNP0C0A", 0}, -+ -+ /* Microsoft Surface Go 3 */ -+ {"MSHW0146", 0}, -+ - {"", 0}, - }; - -@@ -1177,6 +1181,14 @@ static const struct dmi_system_id bat_dmi_table[] __initconst = { - DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad"), - }, - }, -+ { -+ /* Microsoft Surface Go 3 */ -+ .callback = battery_notification_delay_quirk, -+ .matches = { -+ DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "Surface Go 3"), -+ }, -+ }, - {}, - }; - --- -2.35.1 - -From ed246dbe6d9a05f195f78fb1b325b088add2321a Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Tue, 8 Feb 2022 01:29:48 +0100 -Subject: [PATCH] ACPI: battery: Add "Not Charging" quirk for Microsoft Surface - devices - -Microsoft Surface devices have a limiter that sets a fixed maximum -charge capacity for the battery. When that maximum capacity has been -reached, charging stops. In that case, _BST returns a battery state -field with both "charging" and "discharging" bits cleared. The battery -driver, however, returns "unknown" as status. - -This seems to be the same behavior as observed on the ThinkPads, so -let's use the same quirk to handle that as well. - -Signed-off-by: Maximilian Luz -Patchset: misc-fixes - ---- -For what it's worth, I don't think the ACPI spec explicitly states that -any of the status bits need to be set, or that there are only the -"charging" and "discharging" states. As far as I can tell, ACPI only -states: - - Notice that the Charging bit and the Discharging bit are mutually - exclusive and must not both be set at the same time. Even in - critical state, hardware should report the corresponding - charging/discharging state. - -But that does not exclude the case that no bit is set. So, strictly -going by spec, I don't think it's necessary to put all of this behind a -quirk. ---- - drivers/acpi/battery.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c -index 56db7b4da514..8edaa3020af3 100644 ---- a/drivers/acpi/battery.c -+++ b/drivers/acpi/battery.c -@@ -1181,6 +1181,14 @@ static const struct dmi_system_id bat_dmi_table[] __initconst = { - DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad"), - }, - }, -+ { -+ .callback = battery_quirk_not_charging, -+ .ident = "Microsoft Surface", -+ .matches = { -+ DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "Surface"), -+ }, -+ }, - { - /* Microsoft Surface Go 3 */ - .callback = battery_notification_delay_quirk, --- -2.35.1 -