From: Vishnu Sankar <vishnuocv@gmail.com>
To: "Derek J . Clark" <derekjohn.clark@gmail.com>,
Mark Pearson <mpearson-lenovo@squebb.ca>,
Jiri Kosina <jikos@kernel.org>,
Benjamin Tissoires <bentiss@kernel.org>
Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
Vishnu Sankar <vishnuocv@gmail.com>,
Mark Pearson <mpearson@squebb.ca>
Subject: [PATCH] HID: lenovo: add fn+F7 display switch support for X12 and X13 Folio
Date: Wed, 30 Sep 2026 09:21:33 +0900 [thread overview]
Message-ID: <20260930002133.10794-1-vishnuocv@gmail.com> (raw)
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
reply other threads:[~2026-09-30 0:22 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260930002133.10794-1-vishnuocv@gmail.com \
--to=vishnuocv@gmail.com \
--cc=bentiss@kernel.org \
--cc=derekjohn.clark@gmail.com \
--cc=jikos@kernel.org \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mpearson-lenovo@squebb.ca \
--cc=mpearson@squebb.ca \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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®