From nobody Wed Jun 10 22:47:11 2026 Received: from mail-qt1-f169.google.com (mail-qt1-f169.google.com [209.85.160.169]) (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 DA9493B19C1 for ; Tue, 9 Jun 2026 21:26:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781040400; cv=none; b=lE7MVuiitoxpDY3nse99EJmOyoUp+9Bo1faOT4JBQ0OVQNGxeUaWpN11UM7QtPRSRFQkEG+u+aUytK22Kb1fXFQwwEJOqGUDwxddez9TO/q/hKQ+VwxTuW/sZ96VqcrQg2ffvJP3g/QN9xIbdVTQega+CKOmJ8L+kZryjFGOKSY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781040400; c=relaxed/simple; bh=RjcmqHddOPJFPlXAo7SY94la3yf/PIGdxvDHfx5w/Vo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=q3FqzCHB+uR9AXeDj0QZejT7VhNI5JWTvYOH38ngaiNNShFLuiP+ZBkweW/+QpROtSUluSupVOIef2da79uIo+AI9knePAo7DIn2h7zBwPS/v9Suyw/PWfIJ7rqSg+6FfEnKTklpijtveft+vBBK2Pw6nj5shu9EpoCnc1BZ4Gc= 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=ZaBhmIRp; arc=none smtp.client-ip=209.85.160.169 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="ZaBhmIRp" Received: by mail-qt1-f169.google.com with SMTP id d75a77b69052e-5176bbb9384so60778741cf.1 for ; Tue, 09 Jun 2026 14:26:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1781040398; x=1781645198; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=5q5PI7NNO6j/SihoDeyAbVWZeOHgS2VkuRW1R2tonY8=; b=ZaBhmIRpH1mNM3B7H2hsntKtF/ioI/t+KeEY3PrA/mabHu5QBCNuCAbdKwMPAWct3n 5nGTHlu78eR0tahcLNvgqs2Itv+MzH3zOMZjye+QmQppyf8Wwq2rI60C+j1h5TyDp3SG 2HzcX3x3E+dD9zQJfoylrc8/x2du+Oj7XzOvHZJjOgzfZ49ecBVsj5t3nlhcrdW7mM0L CNXdtqsKag9EtYBEN7fuKg8PzT/fFO6XfXnflpUYeOt3Bx31Z5PE47ddIeddELpMMQLQ lkhiewndxoCKOtHg73z5rnak2Z0m9iaWAwmyg0PjIXWSWO9gOSF5YpwVYc48jOJxet1X mgmg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781040398; x=1781645198; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=5q5PI7NNO6j/SihoDeyAbVWZeOHgS2VkuRW1R2tonY8=; b=EUDxRD+mRPgvDaqBEB7v/S0f1and1WiboyfHPYnNYzxNfnIrpopogyqWUba8aulHwR h9XtxA5m0zGS5mAPOUUV3nIIRyOU2Ozcl32trTVv3vLurhcuzluH8Yc135W7o1C9YKhq aoUVKOBZoNn+q69i5QGziApEN4DvYy31ueGqgGnfJZGbrdkqLvj4pt8gMXDitzHN6raG LF41h9Jn9OKeQAhXWPBAf/M2pQjwIvkREaNLuNCfRBHqIR1VshK8nnt1enPZDLLFiZUJ iwVQQCZfgw6HoiMrRwrwG6t7SbFvheCVUBs4/KlynSlpLAcjg1XXowEkweRLz8XHB1Mp 8o0A== X-Forwarded-Encrypted: i=1; AFNElJ84+2j7d29QPRfcBAO/+3hUeFWPBKB5i43CPNuuBScInFsgJcEupYJg3a2NQewIlMggysrmrvrfvCsNl48=@vger.kernel.org X-Gm-Message-State: AOJu0YwL/yDYzLT+2KKs06SIFXqc9phwf9TgSI3E57yMcBEbTczK0UO5 2snRyW2o+O8CaX+IApapQzBzJHM6FbSldhOG2ZSKITaslKRRrFzkopAv X-Gm-Gg: Acq92OFjihZkFilVLYKNK+2XBygiDpLdkS2qE6qeKejd59tfVJuorWVmjCmmONqO77j LuXQsw8LhRuVMQnxJ1CdquFsoY8XXVEB5Ihp97wwLKcktd5oaXg1CcTg4hYvGpFzVs4BRd5LYDG tWY0NVewArD33Fi0F4C9311L6WtLKgMeczQizLotbHPH1LvF99ymcDkfjmdUfu9u8CQpbEzj71v ijqKExzuLpPpkaXf+pdx3v+Es6Rv+CLlk+jEo/b1IvAzlea8ScMyOF179kUqXxTxDu8cn3Ma/hW Uwe1b9QBdAM05aMkCmC1G0pbrYGkGDa34eD1Bf7iyfxuRVAaosZdraZTR7d97Lxbf5i9MJm5DOM gx4oUxLvQrPgy5XXHW/9BdPz+bdt9TK1RKBMqxSuEDu1BSyA3d9ssf2GCrCHaS9L6EiPm+DcCEI Ph4UFhNByYGi+QFbfwPbSq6z3dUrIEIeDD0/1bj/0Iazbzz2UjScPBCE1PkQSTcWW0M8d6XgzzP PriFmM= X-Received: by 2002:a05:622a:1f9a:b0:517:8541:8b70 with SMTP id d75a77b69052e-51795c614d6mr320278931cf.45.1781040397949; Tue, 09 Jun 2026 14:26:37 -0700 (PDT) Received: from fedora (pool-100-11-178-145.phlapa.fios.verizon.net. [100.11.178.145]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8ceccdcb3c5sm226713036d6.21.2026.06.09.14.26.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jun 2026 14:26:37 -0700 (PDT) From: Dave Carey To: platform-driver-x86@vger.kernel.org Cc: johannes.goede@oss.qualcomm.com, mpearson-lenovo@squebb.ca, ilpo.jarvinen@linux.intel.com, armin.wolf@outlook.de, linux-kernel@vger.kernel.org, Dave Carey Subject: [PATCH v7 1/2] platform/x86/lenovo: lenovo-ymc: Suppress probe on Yoga Book 9 14IAH10 Date: Tue, 9 Jun 2026 17:26:30 -0400 Message-ID: <20260609212631.925943-2-carvsdriver@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260609212631.925943-1-carvsdriver@gmail.com> References: <20260608215205.678182-1-carvsdriver@gmail.com> <20260609212631.925943-1-carvsdriver@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The Yoga Book 9 14IAH10 (DMI product name "83KJ") has a dedicated yb9-kbdock WMI driver that registers an input device reporting SW_TABLET_MODE to track the detachable Bluetooth keyboard. lenovo-ymc also loads on this machine and creates an input node with the SW_TABLET_MODE capability bit set. For input switches, the presence of the capability bit has semantic meaning: userspace (e.g. GNOME) reads the switch state at startup from every node advertising the capability and does not expect more than one such node. Add a DMI match for the Yoga Book 9 14IAH10 to probe() so that lenovo-ymc returns -ENODEV on this hardware, leaving yb9-kbdock as the sole SW_TABLET_MODE source. The ymc_ec_trigger EC write, the only other action taken in response to a YMC event, is guarded by a separate DMI table that excludes this machine; no other functionality is affected. Signed-off-by: Dave Carey Reviewed-by: Hans de Goede --- drivers/platform/x86/lenovo/ymc.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/platform/x86/lenovo/ymc.c b/drivers/platform/x86/lenov= o/ymc.c --- a/drivers/platform/x86/lenovo/ymc.c +++ b/drivers/platform/x86/lenovo/ymc.c @@ -23,7 +23,23 @@ module_param(force, bool, 0444); static bool force; module_param(force, bool, 0444); MODULE_PARM_DESC(force, "Force loading on boards without a convertible DMI= chassis-type"); +static const struct dmi_system_id lenovo_ymc_nosupport_dmi_table[] =3D { + { + /* + * Yoga Book 9 14IAH10: SW_TABLET_MODE is reported by the + * yb9-kbdock driver. Suppress lenovo-ymc on this machine to + * avoid userspace seeing two input nodes that both advertise + * the SW_TABLET_MODE capability. + */ + .matches =3D { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83KJ"), + }, + }, + { } +}; + static const struct dmi_system_id allowed_chasis_types_dmi_table[] =3D { { .matches =3D { @@ -100,7 +116,10 @@ static int lenovo_ymc_probe(struct wmi_device *wdev, c= onst void *ctx) struct lenovo_ymc_private *priv; struct input_dev *input_dev; int err; + if (dmi_check_system(lenovo_ymc_nosupport_dmi_table)) + return -ENODEV; + if (!dmi_check_system(allowed_chasis_types_dmi_table)) { if (force) dev_info(&wdev->dev, "Force loading Lenovo YMC support\n"); -- 2.54.0 From nobody Wed Jun 10 22:47:11 2026 Received: from mail-qv1-f53.google.com (mail-qv1-f53.google.com [209.85.219.53]) (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 C8BFE37268F for ; Tue, 9 Jun 2026 21:26:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781040401; cv=none; b=qrPnZcQworEBAfTTJHNwCQRFFHIXqq/ld+HJ9WFxcbnzpU1DtgUUt3yyYa5j7Gu4OZbVOlecyKojupstKxpJ1lAbJWC3UHfKk5lbsopAokAGQt4SdVeOru4RYWJ/15H9PNChT+LuTjKck6Gjc6JakQHHIJA96Fs2Q3o+geOvcSQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781040401; c=relaxed/simple; bh=hfQOUiuGmzpUZo+gSeNocXoChKbS7listFAm1bAC6lI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=nv5hu6IV7pKRPpp3jAmL4J3pWodSsglYQNbGISCNXORr+gVy+2kBj8UDRvuki2DnSPBoI2ndlIYNAc/aMqWUO01gcihQH1T1eAt9bCUXAY4gxCUx2IKTfDyp+SOXiX7iBtCnlrn2ZfBqQIvv1k20wvz6g/4dOAWIVrKXsdgAWRg= 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=jAhM+zzi; arc=none smtp.client-ip=209.85.219.53 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="jAhM+zzi" Received: by mail-qv1-f53.google.com with SMTP id 6a1803df08f44-8ccf0fa0aacso83720136d6.2 for ; Tue, 09 Jun 2026 14:26:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1781040399; x=1781645199; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Jtsq6ar/hYL2f3XMwD3BwuiAC477bjb3FJHRJIXJs1Q=; b=jAhM+zzi6VFpyQLQdluFcJO8s9ajnPGk6yklQie3NzIUV+yZVs1YjzOwJ0pHyMvhB2 J25e5pp8D0rxzf/jdWdZhMEGyB4WVdjVuBs7a4jjxhzd2NF8GdQwEHjbGKvxets3ttZi gC0kr6obAx3gcWXEjymlJ7OUFJtPWzvhrjZFkSlAB4E0BN2U016Ep7rQEA6nt3CijGcO SMIpRD+H1cBoD5F2iXnejU2m5lCAOp4XkfjrN89zLFBrbr53z7+8hVJAjaGwf4rddjOB XFbl5CbI/t1rn7oPiFpLXCRgQ7JXdaNgW24g+O8GtclvT1hYva7l1Rco4UCHcH/ywfKh rIMA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781040399; x=1781645199; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=Jtsq6ar/hYL2f3XMwD3BwuiAC477bjb3FJHRJIXJs1Q=; b=Y+I6Rg6b+UYpIc39aMIge6aVukwy/mci61qWpfyH5/SZRjWOkYppaPjA2288aRuHFF alPFA0zg0WQ8+zVJwXjhG7+qTwh9aahb+7i2/4lnJ5cdnrAR8NT6mJvk5kp9VEsgSSk/ nAReEy83SQPmJD2dmczkHCPAl4/miMk8KaWemZfN9WTVUtSue3+hi0+dlmGWcqg42Jy6 a4QzEdYxkPYqIDNPlT/EbHGWjiX1qqJa1MoVu/J6tVRozQoA3LZt9WO+bCUAQbzPQC/S HHHJmB262hxcdaxEW3UTj2Iw8Y30ytfUDPs0P31ORgjrWQHsozVTumP+gESEdju17UpC ymwg== X-Forwarded-Encrypted: i=1; AFNElJ9alWcMr5iGsoH5VtxpMAxRpcszsVgLAXkHp7ieHJEA2WHFsHb0z92/zLZzUjyVKk5b/XwNg5uZ9b0tyDE=@vger.kernel.org X-Gm-Message-State: AOJu0YwJGKYzryUZHTx2MBh0vcfhWFKouz7OqiLIYhnQkHzzdk/5WeNS LK7zKb4m5OuI7eoJP2Se3IzoeHFqo0fqFyFc/M3av9Fmm38nrcVkV+Ua X-Gm-Gg: Acq92OEQikrsJ+l+iGYTXemX948Hg5buLS/ZbDSrqEQ9f9v3SodFx7AKLtTtBEbOJV+ 99MJGDUfa/cok+VgQ7pCk6DqY40Rfcvwd/luV8CJqyph2siOIdhDNCzxpX4Y2Se2SoDFZTYacmu 4K01EVjMG7ll4+0HNJzGO1V0hecbTvjFQj4GViLbhimiFm6GRIhtLW82k43cPq/MjrLpS8k8Ai1 l2EhfKdxrWjXuGHSXuf7jDMknFmFWQ8/11KfMBlHym1C6jon8u+iv6JmSuAHtBu0aHSPlsqzGkt Se/OghamKqWqzTmwMsVhsHvngDAOm9Qv7oErd0urq97z3WNV+kRU88hnmM7Ug+WbcuHU7Ekpjen gugZyO19ctxkskI1P+rcknKa3XCv9kOgEdfjDtGcZp/L28aY8FTYra4fY8xFnMjPT6dJxc/vsX5 /GPHnnbHsAk5bqkczJDd620KrTPpiPj0HoDrO04fNk56GSz23nfOOdcBaPaHAI+qfG4h72Pb98a 4mbXqk= X-Received: by 2002:a05:6214:10ec:b0:8cc:d165:7ee0 with SMTP id 6a1803df08f44-8cee5fb6e29mr298831276d6.11.1781040398635; Tue, 09 Jun 2026 14:26:38 -0700 (PDT) Received: from fedora (pool-100-11-178-145.phlapa.fios.verizon.net. [100.11.178.145]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8ceccdcb3c5sm226713036d6.21.2026.06.09.14.26.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jun 2026 14:26:38 -0700 (PDT) From: Dave Carey To: platform-driver-x86@vger.kernel.org Cc: johannes.goede@oss.qualcomm.com, mpearson-lenovo@squebb.ca, ilpo.jarvinen@linux.intel.com, armin.wolf@outlook.de, linux-kernel@vger.kernel.org, Dave Carey Subject: [PATCH v7 2/2] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver Date: Tue, 9 Jun 2026 17:26:31 -0400 Message-ID: <20260609212631.925943-3-carvsdriver@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260609212631.925943-1-carvsdriver@gmail.com> References: <20260608215205.678182-1-carvsdriver@gmail.com> <20260609212631.925943-1-carvsdriver@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable The Lenovo Yoga Book 9 14IAH10 ships with a detachable Bluetooth keyboard that magnetically attaches to the bottom (secondary) screen in one of two positions. The Embedded Controller tracks the attachment state in a 2-bit field called BKBD and signals changes via WMI event GUID 806BD2A2-177B-481D-BFB5-3BA0BB4A2285 (notify ID 0xEB on the WM10 ACPI device, _UID "GMZN"). The device contains embedded BMOF data (WQDD, 20705 bytes) documenting both WMI interfaces used by this driver: LENOVO_BTKBD_EVENT (event GUID): WmiDataId(1) uint32 Status. The ACPI _WED(0xEB) method returns EC.BKBD directly as an integer, so the notify callback receives BKBD without a separate query. LENOVO_FEATURE_STATUS_DATA (block GUID, WQAF method): returns an 8-byte buffer {uint32 IDs=3D0x00060000, uint32 Status=3DBKBD}. Used for the initial state read on probe and after resume. BKBD encoding: 0 =3D keyboard detached 1 =3D keyboard docked on top half of bottom screen 2 =3D keyboard docked on bottom half of bottom screen 3 =3D reserved (not observed in practice) This driver: - Registers two WMI drivers: one on the event GUID (LENOVO_BTKBD_EVENT) and one on the block GUID (LENOVO_FEATURE_STATUS_DATA). - On probe, reads initial BKBD state via wmidev_query_block() on the block device; whichever driver probes last triggers the initial read. - On WMI notification, reads BKBD directly from the event data integer (ACPI _WED(0xEB) returns EC.BKBD) without a redundant WQAF call. - Reports SW_TABLET_MODE=3D1 when detached, SW_TABLET_MODE=3D0 when docked in either position (a physical keyboard is present in both cases). - Exposes the raw BKBD value via read-only sysfs attribute "keyboard_position". - Re-reads BKBD state on resume from suspend or hibernation. Tested on: Lenovo Yoga Book 9 14IAH10 (model 83KJ), kernel 7.0. Signed-off-by: Dave Carey Acked-by: Hans de Goede --- v7: - Use wmidev_query_block() in place of deprecated wmidev_block_query(). Use __free(kfree) to manage the output buffer; simplify the parsing to a single length check now that the WMI core handles type conversion. - Define BKBD_FIELD with GENMASK() and extract the field with FIELD_GET() instead of open-coded masking. - Use guard(mutex) for all lock/unlock pairs. - Add yb9_kbdock_sync_locked() helper to consolidate the duplicated initial-state read pattern in event_probe, block_probe, and resume. - Add linux/bitfield.h, linux/cleanup.h, linux/slab.h includes. - Fix comment on struct mutex field; use Context:/Returns: sections in the kerneldoc comment for yb9_kbdock_query_locked(). v6: - Submitted as 2/2; patch 1/2 adds a DMI early-exit to lenovo-ymc to prevent duplicate SW_TABLET_MODE input nodes on the YB9. v5: - Rewrote as two WMI drivers (event + block) to avoid the deprecated wmi_query_block() API: event driver owns the GUID that delivers notifications, block driver owns the GUID used for querying state. Either can probe first; both probe callbacks check whether the other has already probed and fire the initial read when both are ready. - Use wmidev_block_query() on the block wmi_device directly (replaces the deprecated wmi_query_block()). - In the notify callback, read BKBD directly from the event data integer passed by the WMI core (_WED(0xEB) returns EC.BKBD; confirmed by decoding the embedded BMOF from WQDD). Eliminates the redundant WQAF call that v4 made on every notification. - Documented the two BMOF classes (LENOVO_BTKBD_EVENT and LENOVO_FEATURE_STATUS_DATA) with field layouts in the commit message and file header. - sysfs keyboard_position: output bare integer, drop the parenthetical position-name suffix that a couple of reviewers found non-standard. - Set .no_singleton =3D true on both WMI drivers (was missing from block driver in v4). - Add PM resume callback (yb9_kbdock_resume) to re-read BKBD state after suspend/hibernation. v4: - Dropped module_wmi_driver(); registered two WMI drivers manually to allow sharing state. module_init/exit pair registers/unregisters both. - Added ATTRIBUTE_GROUPS() and .dev_groups for the sysfs attribute. - sysfs show: performed a live query instead of returning a cached value. v3: - Switched to devm_input_allocate_device(). - Added DMI guard (dmi_check_system) to reject non-YB9 machines. - Removed redundant MODULE_DEVICE_TABLE on block driver. v2: - Added .no_singleton =3D true on the event WMI driver. - Added PM suspend/resume skeleton. - Fixed MODULE_LICENSE to "GPL" (was "GPL v2"). .../testing/sysfs-driver-lenovo-yb9-kbdock | 19 + MAINTAINERS | 7 + drivers/platform/x86/lenovo/Kconfig | 14 + drivers/platform/x86/lenovo/Makefile | 1 + drivers/platform/x86/lenovo/yb9-kbdock.c | 317 ++++++++++++++++++ 5 files changed, 358 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock create mode 100644 drivers/platform/x86/lenovo/yb9-kbdock.c diff --git a/Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock b/Doc= umentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock new file mode 100644 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock @@ -0,0 +1,19 @@ +What: /sys/bus/wmi/drivers/lenovo-yb9-kbdock//keyboard_position +Date: April 2026 +KernelVersion: 6.10 +Contact: Dave Carey +Description: + Read-only attribute reporting the current keyboard dock position + as reported by the Embedded Controller on the Lenovo Yoga Book 9 + 14IAH10. + + Possible values: + + =3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + 0 keyboard is not docked to any screen (detached) + 1 keyboard docked on the top half of the bottom screen + 2 keyboard docked on the bottom half of the bottom screen + =3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + + SW_TABLET_MODE input events are also emitted: 0 when the keyboard + is docked (either position), 1 when detached. diff --git a/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14479,6 +14479,13 @@ L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/lenovo/wmi-hotkey-utilities.c =20 +LENOVO YOGA BOOK 9 KEYBOARD DOCK DRIVER +M: Dave Carey +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock +F: drivers/platform/x86/lenovo/yb9-kbdock.c + LETSKETCH HID TABLET DRIVER M: Hans de Goede L: linux-input@vger.kernel.org diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/len= ovo/Kconfig --- a/drivers/platform/x86/lenovo/Kconfig +++ b/drivers/platform/x86/lenovo/Kconfig @@ -43,6 +43,20 @@ config LENOVO_WMI_CAMERA To compile this driver as a module, choose M here: the module will be called lenovo-wmi-camera. =20 +config LENOVO_YB9_KBDOCK + tristate "Lenovo Yoga Book 9 keyboard dock detection" + depends on ACPI_WMI + depends on DMI + depends on INPUT + help + Say Y here to enable keyboard dock detection on the Lenovo Yoga Book 9 + 14IAH10. The detachable Bluetooth keyboard magnetically attaches to + either screen; this driver reports SW_TABLET_MODE input events based + on the attachment state and exposes the raw position in sysfs. + + To compile this driver as a module, choose M here: the module will be + called lenovo-yb9-kbdock. + config LENOVO_YMC tristate "Lenovo Yoga Tablet Mode Control" depends on ACPI_WMI diff --git a/drivers/platform/x86/lenovo/Makefile b/drivers/platform/x86/le= novo/Makefile --- a/drivers/platform/x86/lenovo/Makefile +++ b/drivers/platform/x86/lenovo/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_THINKPAD_LMI) +=3D think-lmi.o obj-$(CONFIG_THINKPAD_ACPI) +=3D thinkpad_acpi.o =20 lenovo-target-$(CONFIG_LENOVO_WMI_HOTKEY_UTILITIES) +=3D wmi-hotkey-utilit= ies.o +lenovo-target-$(CONFIG_LENOVO_YB9_KBDOCK) +=3D yb9-kbdock.o lenovo-target-$(CONFIG_LENOVO_YMC) +=3D ymc.o lenovo-target-$(CONFIG_YOGABOOK) +=3D yogabook.o lenovo-target-$(CONFIG_YT2_1380) +=3D yoga-tab2-pro-1380-fastcharger.o diff --git a/drivers/platform/x86/lenovo/yb9-kbdock.c b/drivers/platform/x8= 6/lenovo/yb9-kbdock.c new file mode 100644 --- /dev/null +++ b/drivers/platform/x86/lenovo/yb9-kbdock.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Lenovo Yoga Book 9 keyboard-dock detection + * + * The Yoga Book 9 ships with a detachable Bluetooth keyboard that magneti= cally + * attaches to the bottom screen in one of two positions. The EC tracks + * attachment state in a 2-bit field called BKBD and signals changes via W= MI + * event 0xEB on the WM10 ACPI device (_UID "GMZN"). + * + * BKBD values: + * 0 =3D keyboard detached + * 1 =3D keyboard docked on the top half of the bottom screen + * 2 =3D keyboard docked on the bottom half of the bottom screen + * 3 =3D reserved / not observed + * + * Two WMI interfaces are used (documented in embedded BMOF, WQDD, 20705 b= ytes): + * + * LENOVO_BTKBD_EVENT (event GUID, 806BD2A2-...) + * WmiDataId(1) uint32 Status =E2=80=94 _WED(0xEB) returns EC.BKBD dir= ectly. + * The notify callback receives BKBD as an integer; no separate query = needed. + * + * LENOVO_FEATURE_STATUS_DATA (block GUID, E7F300FA-...) + * WmiDataId(1) uint32 IDs =3D 0x00060000 (feature selector) + * WmiDataId(2) uint32 Status =3D BKBD value + * Used on probe and resume to read initial state. + * + * SW_TABLET_MODE=3D1 is reported when the keyboard is detached; + * SW_TABLET_MODE=3D0 when docked in either position (keyboard present). + * The raw BKBD value is exposed via the sysfs attribute "keyboard_positio= n". + * + * Copyright (C) 2026 Dave Carey + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define YB9_KBDOCK_EVENT_GUID "806BD2A2-177B-481D-BFB5-3BA0BB4A2285" +#define YB9_KBDOCK_QUERY_GUID "E7F300FA-21CD-4003-ADAC-2696135982E6" + +/* 2-bit EC field encoding the keyboard dock position */ +#define BKBD_FIELD GENMASK(1, 0) + +/* BKBD encoding */ +#define BKBD_DETACHED 0 +#define BKBD_TOP_HALF 1 +#define BKBD_BOTTOM_HALF 2 + +static const struct dmi_system_id yb9_kbdock_dmi_table[] =3D { + { + /* Lenovo Yoga Book 9 14IAH10 */ + .matches =3D { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83KJ"), + }, + }, + { } +}; + +/* + * Shared state between the event and block WMI drivers. Protected by loc= k. + * The lock may be held across wmidev_query_block() calls (which can sleep= ); + * block_remove() acquires the lock before clearing block_wdev, ensuring + * block_wdev remains valid for the duration of any in-progress query. + */ +static struct { + struct mutex lock; /* protects input_dev and block_wdev */ + struct input_dev *input_dev; /* set by event probe */ + struct wmi_device *block_wdev; /* set by block probe */ +} yb9; + +/** + * yb9_kbdock_query_locked() - Read BKBD from the block device. + * @log_dev: Device for logging. + * + * Context: Caller must hold yb9.lock. + * Returns: 0-3 on success, -errno on failure. + */ +static int yb9_kbdock_query_locked(struct device *log_dev) +{ + struct wmi_buffer out =3D {}; + void *data __free(kfree) =3D NULL; + u32 bkbd; + int ret; + + if (!yb9.block_wdev) + return -ENODEV; + + ret =3D wmidev_query_block(yb9.block_wdev, 0, &out); + if (ret) { + dev_warn(log_dev, "WQAF failed: %d\n", ret); + return ret; + } + + data =3D out.data; + + /* + * LENOVO_FEATURE_STATUS_DATA: 8-byte buffer {IDs=3D0x00060000, Status=3D= BKBD}. + * BKBD is at bytes 4-7. + */ + if (out.length < 8) { + dev_warn(log_dev, "WQAF: buffer too short (%zu)\n", out.length); + return -EIO; + } + + memcpy(&bkbd, (u8 *)data + 4, sizeof(bkbd)); + return (int)FIELD_GET(BKBD_FIELD, bkbd); +} + +/* Report SW_TABLET_MODE from BKBD. Caller must hold yb9.lock. */ +static void yb9_kbdock_report_locked(int bkbd, struct device *log_dev) +{ + int tablet =3D (bkbd =3D=3D BKBD_DETACHED) ? 1 : 0; + + input_report_switch(yb9.input_dev, SW_TABLET_MODE, tablet); + input_sync(yb9.input_dev); + dev_dbg(log_dev, "BKBD=3D%d SW_TABLET_MODE=3D%d\n", bkbd, tablet); +} + +/* Read BKBD and report if both WMI devices are ready. Caller must hold y= b9.lock. */ +static void yb9_kbdock_sync_locked(struct device *log_dev) +{ + int bkbd; + + if (!yb9.input_dev || !yb9.block_wdev) + return; + + bkbd =3D yb9_kbdock_query_locked(log_dev); + if (bkbd >=3D 0) + yb9_kbdock_report_locked(bkbd, log_dev); +} + +/* ------------------------------------------------------------------ + * sysfs + * ------------------------------------------------------------------ */ + +static ssize_t keyboard_position_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int bkbd; + + guard(mutex)(&yb9.lock); + bkbd =3D yb9_kbdock_query_locked(dev); + + if (bkbd < 0) + return bkbd; + return sysfs_emit(buf, "%u\n", (unsigned int)bkbd); +} +static DEVICE_ATTR_RO(keyboard_position); + +static struct attribute *yb9_kbdock_attrs[] =3D { + &dev_attr_keyboard_position.attr, + NULL, +}; +ATTRIBUTE_GROUPS(yb9_kbdock); + +/* ------------------------------------------------------------------ + * Event WMI driver =E2=80=94 LENOVO_BTKBD_EVENT + * ------------------------------------------------------------------ */ + +static void yb9_kbdock_notify(struct wmi_device *wdev, union acpi_object *= data) +{ + u32 bkbd; + + /* + * _WED(0xEB) returns EC.BKBD directly as an integer + * (LENOVO_BTKBD_EVENT WmiDataId(1) uint32 Status). + */ + if (!data || data->type !=3D ACPI_TYPE_INTEGER) { + dev_warn(&wdev->dev, "unexpected event data type %d\n", + data ? data->type : -1); + return; + } + bkbd =3D FIELD_GET(BKBD_FIELD, data->integer.value); + + guard(mutex)(&yb9.lock); + if (yb9.input_dev) + yb9_kbdock_report_locked(bkbd, &wdev->dev); +} + +static int yb9_kbdock_event_probe(struct wmi_device *wdev, const void *ctx) +{ + struct input_dev *input_dev; + int err; + + if (!dmi_check_system(yb9_kbdock_dmi_table)) + return -ENODEV; + + input_dev =3D devm_input_allocate_device(&wdev->dev); + if (!input_dev) + return -ENOMEM; + + input_dev->name =3D "Lenovo Yoga Book 9 keyboard dock switch"; + input_dev->phys =3D YB9_KBDOCK_EVENT_GUID "/input0"; + input_dev->id.bustype =3D BUS_HOST; + input_set_capability(input_dev, EV_SW, SW_TABLET_MODE); + + err =3D input_register_device(input_dev); + if (err) + return err; + + guard(mutex)(&yb9.lock); + yb9.input_dev =3D input_dev; + yb9_kbdock_sync_locked(&wdev->dev); + return 0; +} + +static void yb9_kbdock_event_remove(struct wmi_device *wdev) +{ + guard(mutex)(&yb9.lock); + yb9.input_dev =3D NULL; +} + +static int yb9_kbdock_resume(struct device *dev) +{ + guard(mutex)(&yb9.lock); + yb9_kbdock_sync_locked(dev); + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(yb9_kbdock_pm_ops, NULL, yb9_kbdock_resume= ); + +static const struct wmi_device_id yb9_kbdock_event_id_table[] =3D { + { .guid_string =3D YB9_KBDOCK_EVENT_GUID }, + { } +}; +MODULE_DEVICE_TABLE(wmi, yb9_kbdock_event_id_table); + +static struct wmi_driver yb9_kbdock_event_driver =3D { + .driver =3D { + .name =3D "lenovo-yb9-kbdock", + .dev_groups =3D yb9_kbdock_groups, + .pm =3D pm_sleep_ptr(&yb9_kbdock_pm_ops), + }, + .id_table =3D yb9_kbdock_event_id_table, + .no_singleton =3D true, + .probe =3D yb9_kbdock_event_probe, + .remove =3D yb9_kbdock_event_remove, + .notify =3D yb9_kbdock_notify, +}; + +/* ------------------------------------------------------------------ + * Block WMI driver =E2=80=94 LENOVO_FEATURE_STATUS_DATA + * ------------------------------------------------------------------ */ + +static int yb9_kbdock_block_probe(struct wmi_device *wdev, const void *ctx) +{ + if (!dmi_check_system(yb9_kbdock_dmi_table)) + return -ENODEV; + + guard(mutex)(&yb9.lock); + yb9.block_wdev =3D wdev; + yb9_kbdock_sync_locked(&wdev->dev); + return 0; +} + +static void yb9_kbdock_block_remove(struct wmi_device *wdev) +{ + guard(mutex)(&yb9.lock); + yb9.block_wdev =3D NULL; +} + +static const struct wmi_device_id yb9_kbdock_block_id_table[] =3D { + { .guid_string =3D YB9_KBDOCK_QUERY_GUID }, + { } +}; + +static struct wmi_driver yb9_kbdock_block_driver =3D { + .driver =3D { + .name =3D "lenovo-yb9-kbdock-block", + }, + .id_table =3D yb9_kbdock_block_id_table, + .no_singleton =3D true, + .probe =3D yb9_kbdock_block_probe, + .remove =3D yb9_kbdock_block_remove, +}; + +/* ------------------------------------------------------------------ + * Module init / exit + * ------------------------------------------------------------------ */ + +static int __init yb9_kbdock_init(void) +{ + int ret; + + mutex_init(&yb9.lock); + + ret =3D wmi_driver_register(&yb9_kbdock_event_driver); + if (ret) + return ret; + + ret =3D wmi_driver_register(&yb9_kbdock_block_driver); + if (ret) { + wmi_driver_unregister(&yb9_kbdock_event_driver); + return ret; + } + return 0; +} +module_init(yb9_kbdock_init); + +static void __exit yb9_kbdock_exit(void) +{ + wmi_driver_unregister(&yb9_kbdock_block_driver); + wmi_driver_unregister(&yb9_kbdock_event_driver); +} +module_exit(yb9_kbdock_exit); + +MODULE_AUTHOR("Dave Carey "); +MODULE_DESCRIPTION("Lenovo Yoga Book 9 keyboard dock detection"); +MODULE_LICENSE("GPL"); -- 2.54.0