mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] HID: lenovo: add fn+F7 display switch support for X12 and X13 Folio
@ 2026-09-30  0:21 Vishnu Sankar
  0 siblings, 0 replies; only message in thread
From: Vishnu Sankar @ 2026-09-30  0:21 UTC (permalink / raw)
  To: Derek J . Clark, Mark Pearson, Jiri Kosina, Benjamin Tissoires
  Cc: linux-input, linux-kernel, Vishnu Sankar, Mark Pearson

On ThinkPad X12 and X13 detachable keyboards FN+F7 reports the Super+P
key chord, which behaves differently from FN+F7 on other Thinkpads.
In particular giving a shorter on screen display time making it harder
for users to select their preferred display mode.

Newer keyboard FW is now available, allowing the keyboard to be
configured by the OS to provide the standard FN+F7 KEY_SWITCHVIDEOMODE
code as used on other Thinkpads.

Detect if the capability is present and enables the mode if available
on initial probe and resume.

Suggested-by: Mark Pearson <mpearson@squebb.ca>
Signed-off-by: Vishnu Sankar <vishnuocv@gmail.com>
---
 drivers/hid/hid-lenovo.c | 71 +++++++++++++++++++++++++++++++++++++---
 1 file changed, 66 insertions(+), 5 deletions(-)

diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c
index 3976d7b53b14..6b4b689c8834 100644
--- a/drivers/hid/hid-lenovo.c
+++ b/drivers/hid/hid-lenovo.c
@@ -44,6 +44,7 @@
 #define TP_X12_RAW_HOTKEY_FN_F10	0x00000803
 #define TP_X12_RAW_HOTKEY_FN_F12	0x00000403
 #define TP_X12_RAW_HOTKEY_FN_SPACE	0x18001003
+#define TP_X12_RAW_HOTKEY_FN_F7		0x00000815
 
 struct lenovo_drvdata {
 	u8 led_report[3]; /* Must be first for proper alignment */
@@ -557,6 +558,34 @@ static int lenovo_send_cmd_cptkbd(struct hid_device *hdev,
 	return ret < 0 ? ret : 0; /* BT returns 0, USB returns sizeof(buf) */
 }
 
+/* X12/X13 folio Fn+F7 reporting mode: feature report 0xa4, [id, mode]. */
+#define TP_X12_FEATURE_HOTKEY_MODE	0xa4
+#define TP_X12_MODE_DEDICATED_HOTKEY	0x01
+
+/*
+ * Select how the X12/X13 detachable folio reports Fn+F7 (display switch):
+ * a dedicated vendor hotkey (report ID 0x15) instead of the default
+ * Super+P key chords.
+ */
+static int lenovo_send_hotkey_mode_tp10ubkbd(struct hid_device *hdev, u8 mode)
+{
+	unsigned char *buf;
+	int ret;
+
+	buf = kzalloc(2, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	buf[0] = TP_X12_FEATURE_HOTKEY_MODE;
+	buf[1] = mode;
+
+	ret = hid_hw_raw_request(hdev, TP_X12_FEATURE_HOTKEY_MODE, buf, 2,
+				 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+
+	kfree(buf);
+	return ret < 0 ? ret : 0;
+}
+
 static void lenovo_features_set_cptkbd(struct hid_device *hdev)
 {
 	int ret;
@@ -739,6 +768,10 @@ static int lenovo_raw_event_TP_X12_tab(struct hid_device *hdev, u32 raw_data)
 		case TP_X12_RAW_HOTKEY_FN_F4:
 			report_key_event(input, LENOVO_KEY_MICMUTE);
 			return 1;
+		case TP_X12_RAW_HOTKEY_FN_F7:
+			/* Fn-F7: switch/extend external display */
+			report_key_event(input, KEY_SWITCHVIDEOMODE);
+			return 1;
 		/* Power-mode or Airplane mode will be called based on the device*/
 		case TP_X12_RAW_HOTKEY_FN_F8:
 			/*
@@ -794,11 +827,22 @@ static int lenovo_raw_event(struct hid_device *hdev,
 	 * Lenovo TP X12 Tab KBD's Fn+XX is HID raw data defined. Report ID is 0x03
 	 * e.g.: Raw data received for MIC mute is 0x00020003.
 	 */
-	if (unlikely((hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB ||
-		      hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB2 ||
-		      hdev->product == USB_DEVICE_ID_LENOVO_X13_TAB) &&
-		      size >= 4 && report->id == 0x03))
-		return lenovo_raw_event_TP_X12_tab(hdev, get_unaligned_le32(data));
+	if (unlikely(hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB ||
+		     hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB2 ||
+		     hdev->product == USB_DEVICE_ID_LENOVO_X13_TAB)) {
+		/* Existing 0x03 vendor hotkeys (8-byte). */
+		if (size >= 4 && report->id == 0x03)
+			return lenovo_raw_event_TP_X12_tab(hdev,
+				get_unaligned_le32(data));
+
+		/* Dedicated-mode display-switch (F7): 3-byte 0x15 report. */
+		if (report->id == 0x15 && size >= 3) {
+			u8 pad[4] = { data[0], data[1], data[2], 0 };
+
+			return lenovo_raw_event_TP_X12_tab(hdev,
+					get_unaligned_le32(pad));
+		}
+	}
 
 	return 0;
 }
@@ -1366,6 +1410,12 @@ static int lenovo_probe_tp10ubkbd(struct hid_device *hdev)
 
 	lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, data->fn_lock);
 
+	if (hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB ||
+	    hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB2 ||
+	    hdev->product == USB_DEVICE_ID_LENOVO_X13_TAB)
+		lenovo_send_hotkey_mode_tp10ubkbd(hdev,
+						  TP_X12_MODE_DEDICATED_HOTKEY);
+
 	ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd);
 	if (ret)
 		return ret;
@@ -1439,6 +1489,13 @@ static int lenovo_reset_resume(struct hid_device *hdev)
 			lenovo_features_set_cptkbd(hdev);
 
 		break;
+	case USB_DEVICE_ID_LENOVO_X12_TAB:
+	case USB_DEVICE_ID_LENOVO_X12_TAB2:
+	case USB_DEVICE_ID_LENOVO_X13_TAB:
+		lenovo_send_hotkey_mode_tp10ubkbd(hdev,
+						  TP_X12_MODE_DEDICATED_HOTKEY);
+		break;
+
 	default:
 		break;
 	}
@@ -1526,9 +1583,13 @@ static int lenovo_input_configured(struct hid_device *hdev,
 						hi->input->propbit);
 			}
 			break;
+		case USB_DEVICE_ID_LENOVO_X12_TAB:
+			input_set_capability(hi->input, EV_KEY, KEY_SWITCHVIDEOMODE);
+			break;
 		case USB_DEVICE_ID_LENOVO_X12_TAB2:
 		case USB_DEVICE_ID_LENOVO_X13_TAB:
 			input_set_capability(hi->input, EV_KEY, KEY_PERFORMANCE);
+			input_set_capability(hi->input, EV_KEY, KEY_SWITCHVIDEOMODE);
 			break;
 	}
 
-- 
2.55.0


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-09-30  0:22 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-30  0:21 [PATCH] HID: lenovo: add fn+F7 display switch support for X12 and X13 Folio Vishnu Sankar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®