From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pj2-f12.google.com (mail-pj2-f12.google.com [74.125.227.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2C8862571A0 for ; Wed, 30 Sep 2026 00:22:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.227.140 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790727741; cv=none; b=fyV4Ltqvew/T6ppRjkhHsYwK1lcZjgGv3hAwxtjcceIK7PuwpswYwmOa4v2DaN/qIgWsSJRSH/ob2T+npjrjlMyFOvnsQOQJ+wv62eS6PA51ZvhP+fHk/3Ryg3SJaYLtXPEQEDY0wIdPNU+NepYtzgIHGIgTVTZvcCOBt0/pFfk= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790727741; c=relaxed/simple; bh=RS3ADzVfxL8qqGmpxz4abkEC5P9mt7MTpddiH2NP6KQ=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=pq2vB9PfeUt4YU35k0ny67zTX43j9CqA8nAZSCbHig+brxAxcHJNN9TR0gTGbdOiwjuG+ayGj+7qZXBZ990uqPZkDWiZpZVisl/0YC8vMnzQHqqaIvhgcnehtOsk9Pg1Ll9dlIHu+kQSfPjzEpL6nS0axb18T+h7UHv+M3cgpEs= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=K58U5GhN; arc=none smtp.client-ip=74.125.227.140 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="K58U5GhN" Received: by mail-pj2-f12.google.com with SMTP id 98e67ed59e1d1-396ccb1a98dso2710116a91.0 for ; Tue, 29 Sep 2026 17:22:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790727739; x=1791332539; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=CfCX2ufsEicMArdWfEJGMSdcRPfc57DekObWO7Y+3J0=; b=K58U5GhNGfnTm7QtEqNrNPJG8XBAuknpOsqxO1wc6OgktYIGPkMtt8oh4BzG+yTsBQ sIg/x0YJp7ZUzWsCz4bnKuVqxeYkXckOejjWqYZiiu6Xwjyw8KhhqQzmjjYm0enAKcgc rHxbrpFTC/AvL7mFlq5dstGWjsKIXCwgugX8JNowdgdz3JoU1LWjhDaVo3A+V+GibmAX s4ia44m1AjTXZdoJ+xTRoXb0ZAP1zuNV6/zW0bW2IErewaJsAxxo7Djaxnf1T1SLAdB7 tKSVLUby8UHXYB3a0W26teomcDe1LisR/qeI1trTAp5Ez4EIaUxRs0eryowzsEC6VTcd EPsA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790727739; x=1791332539; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=CfCX2ufsEicMArdWfEJGMSdcRPfc57DekObWO7Y+3J0=; b=sB/yQ+GTVHBwu1JKXM8pQltmawBVYUWjtVsLLoYnJUzkfHx30g+7nBGz7etXoPj3Ah gaqjyRgzZu//3EXckdX8M240SCiNSKv0dQRgBhuayWNyYzlOM6GJ4xNE6va7/Zuwft2I ny9uagilSCgU7GG9qu7zy8Hs4/eg8R+yeQvh1WwavuNuU0jOE6bZBYe1IWPD/OxtMMq9 qjNU48hlpVhl/oLGZuYluQ4ikwPcwklwX63y1E/57EUp+kMw+pBvQFHFB5vaKFb2Z2yI xfesaVMkQq12ZOVTL7xKFRPQbnE6UjIeyUGFT7J9Jw5p02VFtLmY40Sega/co7xYOmT2 GAqg== X-Forwarded-Encrypted: i=1; AKwUvBxsBXxymxf10s1yn5XGmSE0x9US8lPVqqrak4bhLlKnkuyz/36AACUnk24yu1n48C30EjLXXnBTXBmJZXI=@vger.kernel.org X-Gm-Message-State: AFq9FYICvqTpd0LZpKp92oX0t+tg8+C/cBUDZjjoK2RQV9lQhFPaFJrY r85aa+37vibWpNRdg+MiO/K597eHJCnYsaw9Y1Ra9+O0Ybe1D1E1Eya7 X-Gm-Gg: AYBFou1Zy1LCxxN46jDy06siplYbhSaTGWs3pho5kj9AiICf9O1E8cUaQXOKa8cG2Qa CdpG68ElnjGCKupzyDYgm0lQSjXaFdyvAP81HIm9Z6PZqyozqr4EdU7PcKlXULCQ86SikOWE2ph rRiDSdZ2k8j1SPW+2MOOfUny7UOQhvilJY+GBeXu6Dp1XLUZ8PrDj82wB+RK51l3IvtDWyfG3RY 4+yb/7nBpXSXJ4qGOKB/4J69DUCY5qHcag0ohIz/8D1nxaXcxOHeJcKHcapbPETLxZoBIAWBFQ3 OrCsGKIibKMSkz2UH1QYTpEjgGX1xYW6Iqc57S2mCVNlKMGSy2NIg8+lK0I8/8bRNQr5yUkHeHd SuriGSLjHSvRW3mdnA0zchBSXLkdqJxjxkoJeX/eqktJpacjicTK5Mv8vYiViTO0v4WqfpdBlu9 Px47oVx1AS6OxwsQqHVcUMrWPuO0Vc7rLQk93U51lcK2693psD3uAqa09ZWlT/R0Do X-Received: by 2002:a17:90b:4a4e:b0:39e:237c:50e0 with SMTP id 98e67ed59e1d1-3a4bfdebb0amr783292a91.13.1790727738878; Tue, 29 Sep 2026 17:22:18 -0700 (PDT) Received: from fedora ([2400:2410:5f2b:700:6bda:eb8:32f0:3372]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3a4c7632de2sm242073a91.4.2026.09.29.17.22.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 29 Sep 2026 17:22:18 -0700 (PDT) From: Vishnu Sankar To: "Derek J . Clark" , Mark Pearson , Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Vishnu Sankar , Mark Pearson Subject: [PATCH] HID: lenovo: add fn+F7 display switch support for X12 and X13 Folio Date: Wed, 30 Sep 2026 09:21:33 +0900 Message-ID: <20260930002133.10794-1-vishnuocv@gmail.com> X-Mailer: git-send-email 2.55.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Vishnu Sankar --- 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