Sophie

Sophie

distrib > Mageia > 9 > x86_64 > by-pkgid > 733549e898e68ac22275f709b9310735 > files > 23

kernel-6.5.13-6.mga9.src.rpm

From a5326aa83113152602e9e66a5b704370c8404b37 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
Date: Thu, 5 Nov 2020 13:09:45 +0100
Subject: [PATCH 17/41] hid/multitouch: Turn off Type Cover keyboard backlight
 when suspending

The Type Cover for Microsoft Surface devices supports a special usb
control request to disable or enable the built-in keyboard backlight.
On Windows, this request happens when putting the device into suspend or
resuming it, without it the backlight of the Type Cover will remain
enabled for some time even though the computer is suspended, which looks
weird to the user.

So add support for this special usb control request to hid-multitouch,
which is the driver that's handling the Type Cover.

The reason we have to use a pm_notifier for this instead of the usual
suspend/resume methods is that those won't get called in case the usb
device is already autosuspended.

Also, if the device is autosuspended, we have to briefly autoresume it
in order to send the request. Doing that should be fine, the usb-core
driver does something similar during suspend inside choose_wakeup().

To make sure we don't send that request to every device but only to
devices which support it, add a new quirk
MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER to hid-multitouch. For now this quirk
is only enabled for the usb id of the Surface Pro 2017 Type Cover, which
is where I confirmed that it's working.

Patchset: surface-typecover
---
 drivers/hid/hid-multitouch.c | 100 ++++++++++++++++++++++++++++++++++-
 1 file changed, 98 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 521b2ffb4244..c8f3d05c8866 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -34,7 +34,10 @@
 #include <linux/device.h>
 #include <linux/hid.h>
 #include <linux/module.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
+#include <linux/suspend.h>
+#include <linux/usb.h>
 #include <linux/input/mt.h>
 #include <linux/jiffies.h>
 #include <linux/string.h>
@@ -47,6 +50,7 @@ MODULE_DESCRIPTION("HID multitouch panels");
 MODULE_LICENSE("GPL");
 
 #include "hid-ids.h"
+#include "usbhid/usbhid.h"
 
 /* quirks to control the device */
 #define MT_QUIRK_NOT_SEEN_MEANS_UP	BIT(0)
@@ -72,12 +76,15 @@ MODULE_LICENSE("GPL");
 #define MT_QUIRK_FORCE_MULTI_INPUT	BIT(20)
 #define MT_QUIRK_DISABLE_WAKEUP		BIT(21)
 #define MT_QUIRK_ORIENTATION_INVERT	BIT(22)
+#define MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT	BIT(23)
 
 #define MT_INPUTMODE_TOUCHSCREEN	0x02
 #define MT_INPUTMODE_TOUCHPAD		0x03
 
 #define MT_BUTTONTYPE_CLICKPAD		0
 
+#define MS_TYPE_COVER_FEATURE_REPORT_USAGE	0xff050086
+
 enum latency_mode {
 	HID_LATENCY_NORMAL = 0,
 	HID_LATENCY_HIGH = 1,
@@ -169,6 +176,8 @@ struct mt_device {
 
 	struct list_head applications;
 	struct list_head reports;
+
+	struct notifier_block pm_notifier;
 };
 
 static void mt_post_parse_default_settings(struct mt_device *td,
@@ -213,6 +222,7 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app);
 #define MT_CLS_GOOGLE				0x0111
 #define MT_CLS_RAZER_BLADE_STEALTH		0x0112
 #define MT_CLS_SMART_TECH			0x0113
+#define MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER	0x0114
 
 #define MT_DEFAULT_MAXCONTACT	10
 #define MT_MAX_MAXCONTACT	250
@@ -397,6 +407,16 @@ static const struct mt_class mt_classes[] = {
 			MT_QUIRK_CONTACT_CNT_ACCURATE |
 			MT_QUIRK_SEPARATE_APP_REPORT,
 	},
+	{ .name = MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER,
+		.quirks = MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT |
+			MT_QUIRK_ALWAYS_VALID |
+			MT_QUIRK_IGNORE_DUPLICATES |
+			MT_QUIRK_HOVERING |
+			MT_QUIRK_CONTACT_CNT_ACCURATE |
+			MT_QUIRK_STICKY_FINGERS |
+			MT_QUIRK_WIN8_PTP_BUTTONS,
+		.export_all_inputs = true
+	},
 	{ }
 };
 
@@ -1721,6 +1741,69 @@ static void mt_expired_timeout(struct timer_list *t)
 	clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
 }
 
+static void get_type_cover_backlight_field(struct hid_device *hdev,
+					   struct hid_field **field)
+{
+	struct hid_report_enum *rep_enum;
+	struct hid_report *rep;
+	struct hid_field *cur_field;
+	int i, j;
+
+	rep_enum = &hdev->report_enum[HID_FEATURE_REPORT];
+	list_for_each_entry(rep, &rep_enum->report_list, list) {
+		for (i = 0; i < rep->maxfield; i++) {
+			cur_field = rep->field[i];
+
+			for (j = 0; j < cur_field->maxusage; j++) {
+				if (cur_field->usage[j].hid
+				    == MS_TYPE_COVER_FEATURE_REPORT_USAGE) {
+					*field = cur_field;
+					return;
+				}
+			}
+		}
+	}
+}
+
+static void update_keyboard_backlight(struct hid_device *hdev, bool enabled)
+{
+	struct usb_device *udev = hid_to_usb_dev(hdev);
+	struct hid_field *field = NULL;
+
+	/* Wake up the device in case it's already suspended */
+	pm_runtime_get_sync(&udev->dev);
+
+	get_type_cover_backlight_field(hdev, &field);
+	if (!field) {
+		hid_err(hdev, "couldn't find backlight field\n");
+		goto out;
+	}
+
+	field->value[field->index] = enabled ? 0x01ff00ff : 0x00ff00ff;
+	hid_hw_request(hdev, field->report, HID_REQ_SET_REPORT);
+
+out:
+	pm_runtime_put_sync(&udev->dev);
+}
+
+static int mt_pm_notifier(struct notifier_block *notifier,
+			  unsigned long pm_event,
+			  void *unused)
+{
+	struct mt_device *td =
+		container_of(notifier, struct mt_device, pm_notifier);
+	struct hid_device *hdev = td->hdev;
+
+	if (td->mtclass.quirks & MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT) {
+		if (pm_event == PM_SUSPEND_PREPARE)
+			update_keyboard_backlight(hdev, 0);
+		else if (pm_event == PM_POST_SUSPEND)
+			update_keyboard_backlight(hdev, 1);
+	}
+
+	return NOTIFY_DONE;
+}
+
 static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
 	int ret, i;
@@ -1744,6 +1827,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN;
 	hid_set_drvdata(hdev, td);
 
+	td->pm_notifier.notifier_call = mt_pm_notifier;
+	register_pm_notifier(&td->pm_notifier);
+
 	INIT_LIST_HEAD(&td->applications);
 	INIT_LIST_HEAD(&td->reports);
 
@@ -1782,15 +1868,19 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	timer_setup(&td->release_timer, mt_expired_timeout, 0);
 
 	ret = hid_parse(hdev);
-	if (ret != 0)
+	if (ret != 0) {
+		unregister_pm_notifier(&td->pm_notifier);
 		return ret;
+	}
 
 	if (mtclass->quirks & MT_QUIRK_FIX_CONST_CONTACT_ID)
 		mt_fix_const_fields(hdev, HID_DG_CONTACTID);
 
 	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
-	if (ret)
+	if (ret) {
+		unregister_pm_notifier(&td->pm_notifier);
 		return ret;
+	}
 
 	ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group);
 	if (ret)
@@ -1842,6 +1932,7 @@ static void mt_remove(struct hid_device *hdev)
 {
 	struct mt_device *td = hid_get_drvdata(hdev);
 
+	unregister_pm_notifier(&td->pm_notifier);
 	del_timer_sync(&td->release_timer);
 
 	sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
@@ -2219,6 +2310,11 @@ static const struct hid_device_id mt_devices[] = {
 		MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
 			USB_DEVICE_ID_XIROKU_CSR2) },
 
+	/* Microsoft Surface type cover */
+	{ .driver_data = MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER,
+		HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY,
+			USB_VENDOR_ID_MICROSOFT, 0x09c0) },
+
 	/* Google MT devices */
 	{ .driver_data = MT_CLS_GOOGLE,
 		HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_GOOGLE,
-- 
2.41.0