The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* Re: [PATCH 1/2] input: atkbd: add softleds quirk for broken EC PS/2 emulation
  2026-06-29  1:57 ` [PATCH 1/2] input: atkbd: add softleds quirk for broken EC PS/2 emulation Rodnei Cilto
@ 2026-06-28  5:20   ` Dmitry Torokhov
  0 siblings, 0 replies; 4+ messages in thread
From: Dmitry Torokhov @ 2026-06-28  5:20 UTC (permalink / raw)
  To: Rodnei Cilto
  Cc: Ike Panhc, Mark Pearson, Derek J. Clark, Hans de Goede,
	Ilpo Järvinen, linux-input, linux-kernel,
	platform-driver-x86

Hi Rodnei,

On Sun, Jun 28, 2026 at 10:57:03PM -0300, Rodnei Cilto wrote:
> Some Lenovo IdeaPad laptops (e.g. 83RR/83SR, Wildcat Lake) implement
> PS/2 keyboard emulation via the Embedded Controller (EC) but do not
> fully support the AT protocol. Specifically, sending the SETLEDS
> command (0xED) after initialization causes the EC to return corrupted
> scancodes (reported as '**' in i8042.debug), rendering the keyboard
> non-functional.

"**" do not represent corrupted scancodes, they are safety measure to
not disclose your password on accident.

Use i8042.debug=1 i8042.unmask_kbd_data=1 to unveil real data.

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 0/2] Add keyboard LED support for Lenovo IdeaPad 83RR/83SR
@ 2026-06-29  1:57 Rodnei Cilto
  2026-06-29  1:57 ` [PATCH 1/2] input: atkbd: add softleds quirk for broken EC PS/2 emulation Rodnei Cilto
  2026-06-29  1:57 ` [PATCH 2/2] platform/x86: ideapad-laptop: add CapsLock/NumLock LED via EC Rodnei Cilto
  0 siblings, 2 replies; 4+ messages in thread
From: Rodnei Cilto @ 2026-06-29  1:57 UTC (permalink / raw)
  To: Dmitry Torokhov, Ike Panhc, Mark Pearson, Derek J. Clark,
	Hans de Goede, Ilpo Järvinen
  Cc: linux-input, linux-kernel, platform-driver-x86, Rodnei Cilto

This series adds support for the internal keyboard of Lenovo IdeaPad
laptops based on Wildcat Lake SoC (models 83RR worldwide and 83SR
Brazil regional variant).

The EC PS/2 emulation on these models does not fully support the AT
protocol. Sending the SETLEDS command (0xED) after initialization
causes the EC to return corrupted scancodes ('**' in i8042.debug),
rendering the keyboard non-functional.

Patch 1 adds a softleds DMI quirk to atkbd that suppresses 0xED
while keeping EV_LED capabilities visible to userspace.

Patch 2 adds physical CapsLock/NumLock LED control in ideapad-laptop
via direct EC register access (offset 0xA1) and ACPI _QDF method,
discovered via DSDT analysis.

Rodnei Cilto (2):
  input: atkbd: add softleds quirk for broken EC PS/2 emulation
  platform/x86: ideapad-laptop: add CapsLock/NumLock LED via EC

Signed-off-by: Rodnei Cilto <rodnei.cilto@gmail.com>
---
Rodnei Cilto (2):
      input: atkbd: add softleds quirk for broken EC PS/2 emulation
      platform/x86: ideapad-laptop: add CapsLock/NumLock LED via EC

 drivers/input/keyboard/atkbd.c               |  46 ++++++++-
 drivers/input/serio/i8042-acpipnpio.h        |   3 +
 drivers/platform/x86/lenovo/ideapad-laptop.c | 143 +++++++++++++++++++++++++++
 3 files changed, 191 insertions(+), 1 deletion(-)
---
base-commit: 03e2778d1f11de9260543f969e9e888a1c2bf830
change-id: 20260628-ideapad-kbd-leds-16f587e9ab97

Best regards,
--  
Rodnei Cilto <rodnei.cilto@gmail.com>


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/2] input: atkbd: add softleds quirk for broken EC PS/2 emulation
  2026-06-29  1:57 [PATCH 0/2] Add keyboard LED support for Lenovo IdeaPad 83RR/83SR Rodnei Cilto
@ 2026-06-29  1:57 ` Rodnei Cilto
  2026-06-28  5:20   ` Dmitry Torokhov
  2026-06-29  1:57 ` [PATCH 2/2] platform/x86: ideapad-laptop: add CapsLock/NumLock LED via EC Rodnei Cilto
  1 sibling, 1 reply; 4+ messages in thread
From: Rodnei Cilto @ 2026-06-29  1:57 UTC (permalink / raw)
  To: Dmitry Torokhov, Ike Panhc, Mark Pearson, Derek J. Clark,
	Hans de Goede, Ilpo Järvinen
  Cc: linux-input, linux-kernel, platform-driver-x86, Rodnei Cilto

Some Lenovo IdeaPad laptops (e.g. 83RR/83SR, Wildcat Lake) implement
PS/2 keyboard emulation via the Embedded Controller (EC) but do not
fully support the AT protocol. Specifically, sending the SETLEDS
command (0xED) after initialization causes the EC to return corrupted
scancodes (reported as '**' in i8042.debug), rendering the keyboard
non-functional.

The existing SERIO_QUIRK_DUMBKBD resolves scancode corruption by
zeroing serio->write, preventing AT commands. However, LED registration
in atkbd_set_device_attrs() depends on atkbd->write being set, so
dumbkbd mode loses EV_LED capabilities entirely.

Note: serio->id.extra is __u8 (8 bits only) and cannot be used to
pass new quirk flags from i8042 to atkbd. The quirk is detected
directly in atkbd via its DMI quirk table.

Introduce atkbd_softleds: a DMI-detected mode that combines dumbkbd
behaviour (serio->write = NULL, no 0xED sent) with EV_LED registration
so that CapsLock/NumLock/ScrollLock state remains visible to userspace
via the input subsystem.

Add DMI entries for Lenovo IdeaPad 83RR (Wildcat Lake) and its Brazil
regional variant 83SR.

Signed-off-by: Rodnei Cilto <rodnei.cilto@gmail.com>
---
 drivers/input/keyboard/atkbd.c        | 46 ++++++++++++++++++++++++++++++++++-
 drivers/input/serio/i8042-acpipnpio.h |  3 +++
 2 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index 8cb4dc6fb165..826a21dc016a 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -212,6 +212,7 @@ struct atkbd {
 	bool softrepeat;
 	bool softraw;
 	bool scroll;
+	bool softleds;		/* suppress 0xED, register EV_LED in software */
 	bool enabled;
 
 	/* Accessed only from interrupt */
@@ -245,6 +246,7 @@ static unsigned int (*atkbd_platform_scancode_fixup)(struct atkbd *, unsigned in
  * to many commands until full reset (ATKBD_CMD_RESET_BAT) is performed.
  */
 static bool atkbd_skip_deactivate;
+static bool atkbd_softleds;
 
 static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf,
 				      ssize_t (*handler)(struct atkbd *, char *));
@@ -600,6 +602,14 @@ static int atkbd_set_leds(struct atkbd *atkbd)
 	struct input_dev *dev = atkbd->dev;
 	u8 param[2];
 
+	/*
+	 * softleds: EC PS/2 emulation does not support AT commands
+	 * after initialization. Accept LED state from userspace but
+	 * never send SETLEDS (0xED) to avoid scancode corruption.
+	 */
+	if (atkbd->softleds)
+		return 0;
+
 	param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0)
 		 | (test_bit(LED_NUML,    dev->led) ? 2 : 0)
 		 | (test_bit(LED_CAPSL,   dev->led) ? 4 : 0);
@@ -1193,7 +1203,7 @@ static void atkbd_set_device_attrs(struct atkbd *atkbd)
 	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) |
 		BIT_MASK(EV_MSC);
 
-	if (atkbd->write) {
+	if (atkbd->write || atkbd->softleds) {
 		input_dev->evbit[0] |= BIT_MASK(EV_LED);
 		input_dev->ledbit[0] = BIT_MASK(LED_NUML) |
 			BIT_MASK(LED_CAPSL) | BIT_MASK(LED_SCROLLL);
@@ -1291,6 +1301,12 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
 	if (atkbd->softrepeat)
 		atkbd->softraw = true;
 
+	if (atkbd_softleds) {
+		serio->write = NULL;
+		atkbd->write = false;
+		atkbd->softleds = true;
+	}
+
 	serio_set_drvdata(serio, atkbd);
 
 	err = serio_open(serio, drv);
@@ -1767,6 +1783,12 @@ static int __init atkbd_deactivate_fixup(const struct dmi_system_id *id)
 	return 1;
 }
 
+static int __init atkbd_setup_softleds(const struct dmi_system_id *id)
+{
+	atkbd_softleds = true;
+	return 1;
+}
+
 /*
  * NOTE: do not add any more "force release" quirks to this table.  The
  * task of adjusting list of keys that should be "released" automatically
@@ -1938,6 +1960,28 @@ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = {
 		},
 		.callback = atkbd_deactivate_fixup,
 	},
+	{
+		/*
+		 * Lenovo IdeaPad 83RR (Wildcat Lake) - EC PS/2 emulation
+		 * returns corrupted scancodes ('**' in i8042.debug) when
+		 * receiving AT SETLEDS (0xED) after keyboard initialization.
+		 * Enable softleds mode: suppress 0xED to hardware while
+		 * keeping CapsLock/NumLock/ScrollLock visible to userspace.
+		 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "83RR"),
+		},
+		.callback = atkbd_setup_softleds,
+	},
+	{
+		/* Lenovo IdeaPad 83SR (83RR Brazil regional variant) */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "83SR"),
+		},
+		.callback = atkbd_setup_softleds,
+	},
 	{ }
 };
 
diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h
index 8ebdf4fb9030..d233544ebac9 100644
--- a/drivers/input/serio/i8042-acpipnpio.h
+++ b/drivers/input/serio/i8042-acpipnpio.h
@@ -79,6 +79,9 @@ static inline void i8042_write_command(int val)
 #define SERIO_QUIRK_DIRECT		BIT(8)
 #define SERIO_QUIRK_DUMBKBD		BIT(9)
 #define SERIO_QUIRK_NOLOOP		BIT(10)
+/* SERIO_QUIRK_DUMBKBD_LEDS handled via atkbd DMI quirk table.
+ * serio->id.extra is __u8 (8 bits only), cannot carry this flag.
+ */
 #define SERIO_QUIRK_NOTIMEOUT		BIT(11)
 #define SERIO_QUIRK_KBDRESET		BIT(12)
 #define SERIO_QUIRK_DRITEK		BIT(13)

-- 
2.51.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/2] platform/x86: ideapad-laptop: add CapsLock/NumLock LED via EC
  2026-06-29  1:57 [PATCH 0/2] Add keyboard LED support for Lenovo IdeaPad 83RR/83SR Rodnei Cilto
  2026-06-29  1:57 ` [PATCH 1/2] input: atkbd: add softleds quirk for broken EC PS/2 emulation Rodnei Cilto
@ 2026-06-29  1:57 ` Rodnei Cilto
  1 sibling, 0 replies; 4+ messages in thread
From: Rodnei Cilto @ 2026-06-29  1:57 UTC (permalink / raw)
  To: Dmitry Torokhov, Ike Panhc, Mark Pearson, Derek J. Clark,
	Hans de Goede, Ilpo Järvinen
  Cc: linux-input, linux-kernel, platform-driver-x86, Rodnei Cilto

Some Lenovo IdeaPad laptops (e.g. 83RR/83SR, Wildcat Lake) have
physical CapsLock and NumLock LEDs controlled via the EC.

The EC exposes CAPL (bit 5) and NUML (bit 4) at offset 0xA1.
Writing these bits via ec_write() and evaluating _QDF via
acpi_evaluate_object() causes the firmware to sync EC state to the
GPIO lines that drive the physical LEDs.

Discovery via DSDT analysis on Lenovo IdeaPad 83RR (Wildcat Lake):
  - CAPL/NUML at EC offset 0xA1 (bits 5 and 4)
  - _QDF (_SB.PC00.LPCB.EC0._QDF) reads CAPL/NUML -> SGOV()
  - GPIO 0x001A1087 -> CapsLock LED physical pin
  - GPIO 0x001A0485 -> NumLock LED physical pin
  - ec_read/ec_write exported via EXPORT_SYMBOL in drivers/acpi/ec.c
    and declared in <linux/acpi.h>

Add two led_classdev entries (input::capslock, input::numlock)
guarded by DMI match (features.kbd_leds) for 83RR and its Brazil
regional variant 83SR.

Signed-off-by: Rodnei Cilto <rodnei.cilto@gmail.com>
---
 drivers/platform/x86/lenovo/ideapad-laptop.c | 143 +++++++++++++++++++++++++++
 1 file changed, 143 insertions(+)

diff --git a/drivers/platform/x86/lenovo/ideapad-laptop.c b/drivers/platform/x86/lenovo/ideapad-laptop.c
index 4fbc904f1fc3..03e3f234067c 100644
--- a/drivers/platform/x86/lenovo/ideapad-laptop.c
+++ b/drivers/platform/x86/lenovo/ideapad-laptop.c
@@ -42,6 +42,17 @@
 
 #include <dt-bindings/leds/common.h>
 
+/* EC keyboard LED control (IdeaPad EC PS/2 emulation).
+ * Validated on Lenovo IdeaPad 83RR (Wildcat Lake):
+ *   EC offset 0xA1 bit4=NUML, bit5=CAPL
+ *   _QDF syncs EC state to GPIO -> physical LED
+ * ec_read/ec_write declared in <linux/acpi.h>
+ */
+#define IDEAPAD_EC_KBD_LED_OFFSET	0xA1
+#define IDEAPAD_EC_KBD_LED_NUML_BIT	BIT(4)
+#define IDEAPAD_EC_KBD_LED_CAPL_BIT	BIT(5)
+#define IDEAPAD_ACPI_EC0_QDF_PATH	"\\_SB.PC00.LPCB.EC0._QDF"
+
 #define IDEAPAD_RFKILL_DEV_NUM	3
 
 enum {
@@ -198,6 +209,7 @@ struct ideapad_private {
 		bool ctrl_ps2_aux_port    : 1;
 		bool usb_charging         : 1;
 		bool ymc_ec_trigger       : 1;
+		bool kbd_leds             : 1;
 	} features;
 	struct {
 		bool initialized;
@@ -210,6 +222,11 @@ struct ideapad_private {
 		struct led_classdev led;
 		unsigned int last_brightness;
 	} fn_lock;
+	struct {
+		bool initialized;
+		struct led_classdev capslock;
+		struct led_classdev numlock;
+	} kbd_leds;
 };
 
 static bool no_bt_rfkill;
@@ -1587,6 +1604,99 @@ static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
 /*
  * keyboard backlight
  */
+static int ideapad_kbd_led_ec_set(u8 bit, bool on)
+{
+	u8 val;
+	int err;
+
+	err = ec_read(IDEAPAD_EC_KBD_LED_OFFSET, &val);
+	if (err)
+		return err;
+	if (on)
+		val |= bit;
+	else
+		val &= ~bit;
+	err = ec_write(IDEAPAD_EC_KBD_LED_OFFSET, val);
+	if (err)
+		return err;
+	acpi_evaluate_object(NULL, IDEAPAD_ACPI_EC0_QDF_PATH, NULL, NULL);
+	return 0;
+}
+
+static void ideapad_capslock_led_set(struct led_classdev *led_cdev,
+				     enum led_brightness brightness)
+{
+	ideapad_kbd_led_ec_set(IDEAPAD_EC_KBD_LED_CAPL_BIT, brightness != LED_OFF);
+}
+
+static enum led_brightness ideapad_capslock_led_get(struct led_classdev *led_cdev)
+{
+	u8 val;
+
+	if (ec_read(IDEAPAD_EC_KBD_LED_OFFSET, &val))
+		return LED_OFF;
+	return (val & IDEAPAD_EC_KBD_LED_CAPL_BIT) ? LED_ON : LED_OFF;
+}
+
+static void ideapad_numlock_led_set(struct led_classdev *led_cdev,
+				    enum led_brightness brightness)
+{
+	ideapad_kbd_led_ec_set(IDEAPAD_EC_KBD_LED_NUML_BIT, brightness != LED_OFF);
+}
+
+static enum led_brightness ideapad_numlock_led_get(struct led_classdev *led_cdev)
+{
+	u8 val;
+
+	if (ec_read(IDEAPAD_EC_KBD_LED_OFFSET, &val))
+		return LED_OFF;
+	return (val & IDEAPAD_EC_KBD_LED_NUML_BIT) ? LED_ON : LED_OFF;
+}
+
+static int ideapad_kbd_leds_init(struct ideapad_private *priv)
+{
+	int err;
+
+	if (WARN_ON(priv->kbd_leds.initialized))
+		return -EEXIST;
+
+	priv->kbd_leds.capslock.name           = "input::capslock";
+	priv->kbd_leds.capslock.max_brightness = 1;
+	priv->kbd_leds.capslock.brightness_set = ideapad_capslock_led_set;
+	priv->kbd_leds.capslock.brightness_get = ideapad_capslock_led_get;
+	priv->kbd_leds.capslock.flags          = LED_RETAIN_AT_SHUTDOWN;
+
+	err = led_classdev_register(&priv->platform_device->dev,
+				    &priv->kbd_leds.capslock);
+	if (err)
+		return err;
+
+	priv->kbd_leds.numlock.name            = "input::numlock";
+	priv->kbd_leds.numlock.max_brightness  = 1;
+	priv->kbd_leds.numlock.brightness_set  = ideapad_numlock_led_set;
+	priv->kbd_leds.numlock.brightness_get  = ideapad_numlock_led_get;
+	priv->kbd_leds.numlock.flags           = LED_RETAIN_AT_SHUTDOWN;
+
+	err = led_classdev_register(&priv->platform_device->dev,
+				    &priv->kbd_leds.numlock);
+	if (err) {
+		led_classdev_unregister(&priv->kbd_leds.capslock);
+		return err;
+	}
+
+	priv->kbd_leds.initialized = true;
+	return 0;
+}
+
+static void ideapad_kbd_leds_exit(struct ideapad_private *priv)
+{
+	if (!priv->kbd_leds.initialized)
+		return;
+	priv->kbd_leds.initialized = false;
+	led_classdev_unregister(&priv->kbd_leds.numlock);
+	led_classdev_unregister(&priv->kbd_leds.capslock);
+}
+
 static int ideapad_kbd_bl_check_tristate(int type)
 {
 	return (type == KBD_BL_TRISTATE) || (type == KBD_BL_TRISTATE_AUTO);
@@ -1832,6 +1942,29 @@ static void ideapad_sync_touchpad_state(struct ideapad_private *priv, bool send_
 	priv->r_touchpad_val = value;
 }
 
+static const struct dmi_system_id ideapad_kbd_leds_dmi_table[] = {
+	{
+		/*
+		 * Lenovo IdeaPad 83RR (Wildcat Lake) - EC PS/2 emulation
+		 * controls CapsLock/NumLock LEDs via EC offset 0xA1 + _QDF.
+		 * CAPL=bit5 (0x20), NUML=bit4 (0x10).
+		 * _QDF drives GPIO via SGOV() to physical LED pins.
+		 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "83RR"),
+		},
+	},
+	{
+		/* Lenovo IdeaPad 83SR (83RR Brazil regional variant) */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "83SR"),
+		},
+	},
+	{ }
+};
+
 static const struct dmi_system_id ymc_ec_trigger_quirk_dmi_table[] = {
 	{
 		/* Lenovo Yoga 7 14ARB7 */
@@ -2178,6 +2311,8 @@ static int ideapad_check_features(struct ideapad_private *priv)
 	priv->features.touchpad_ctrl_via_ec = touchpad_ctrl_via_ec;
 	priv->features.ymc_ec_trigger =
 		ymc_ec_trigger || dmi_check_system(ymc_ec_trigger_quirk_dmi_table);
+	priv->features.kbd_leds =
+		dmi_check_system(ideapad_kbd_leds_dmi_table);
 
 	if (!read_ec_data(handle, VPCCMD_R_FAN, &val))
 		priv->features.fan_mode = true;
@@ -2418,6 +2553,12 @@ static int ideapad_acpi_add(struct platform_device *pdev)
 			dev_info(&pdev->dev, "FnLock control not available\n");
 	}
 
+	if (priv->features.kbd_leds) {
+		err = ideapad_kbd_leds_init(priv);
+		if (err)
+			dev_warn(&pdev->dev, "Could not set up kbd LEDs: %d\n", err);
+	}
+
 	/*
 	 * On some models without a hw-switch (the yoga 2 13 at least)
 	 * VPCCMD_W_RF must be explicitly set to 1 for the wifi to work.
@@ -2477,6 +2618,7 @@ static int ideapad_acpi_add(struct platform_device *pdev)
 		ideapad_unregister_rfkill(priv, i);
 
 	ideapad_fn_lock_led_exit(priv);
+	ideapad_kbd_leds_exit(priv);
 	ideapad_kbd_bl_exit(priv);
 	ideapad_input_exit(priv);
 
@@ -2506,6 +2648,7 @@ static void ideapad_acpi_remove(struct platform_device *pdev)
 		ideapad_unregister_rfkill(priv, i);
 
 	ideapad_fn_lock_led_exit(priv);
+	ideapad_kbd_leds_exit(priv);
 	ideapad_kbd_bl_exit(priv);
 	ideapad_input_exit(priv);
 	ideapad_debugfs_exit(priv);

-- 
2.51.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-06-28  5:20 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-29  1:57 [PATCH 0/2] Add keyboard LED support for Lenovo IdeaPad 83RR/83SR Rodnei Cilto
2026-06-29  1:57 ` [PATCH 1/2] input: atkbd: add softleds quirk for broken EC PS/2 emulation Rodnei Cilto
2026-06-28  5:20   ` Dmitry Torokhov
2026-06-29  1:57 ` [PATCH 2/2] platform/x86: ideapad-laptop: add CapsLock/NumLock LED via EC Rodnei Cilto

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