From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pl1-f176.google.com (mail-pl1-f176.google.com [209.85.214.176]) (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 B24A330DEA3 for ; Wed, 5 Aug 2026 03:53:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.176 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785902028; cv=none; b=I70AoP5KhyFXYVpXAIsLENCDnXbgyfmoL+anZAIGTJGZWuubSYciB7ETq6zO4sbKqCVrayXf0t9hbaKpj8tKmgs+lYPt2vVds4+CfHu6wzcsJ+Lh9IZofDJW7fD+sVYE29YBQkrlTRldkp3IvHGhtRZFVI03n7MQjLXnRaLjqqw= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785902028; c=relaxed/simple; bh=6czYU5Oa9WZyV8JW9BgZnqi7ZB9hIUW/2qAmkCw0qBM=; h=Date:From:To:Cc:Subject:Message-ID:MIME-Version:Content-Type: Content-Disposition; b=AFAdrpMrRlt3g8hL5xGYhpwkt/5Vl7SiKDN1n4eiBjexyzHN6drZWGEgckUfbDhABtE10ZmmEXPv5GXYLTwBaoo07xlgTxU323i+MCyOW/9nRHeSTP12cfOXyx3Hqyezsqhn2RfaHL5ooYbZYets5SRXdxWscVZa+AVLCUfz5mA= 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=QlTKOC/Y; arc=none smtp.client-ip=209.85.214.176 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="QlTKOC/Y" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-2cad8076b01so6559725ad.2 for ; Tue, 04 Aug 2026 20:53:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785902026; x=1786506826; darn=vger.kernel.org; h=content-disposition:content-type:mime-version:message-id:subject:cc :to:from:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=aArZVDET4swnltVql8eMgSIS5WdHN1v7z0Y93I05HeA=; b=QlTKOC/Y8usVZOm0+t7+JyFh9/S/Krh/MRuNFwgRhWdJmSONrWYqPJ3MWBPGaDrxP1 SOUnYzRiMCqwVhkrAcC7ojP7YjUAa3Yybk7ZXwbi5mN9p8G950lX3C9JAH32htDgFQFN zeBA68+2TQWtY58UBlPh5KwyQkNd9JmGoffGnImJSifP+IxyvIOOJUVOF7fDljFPrTwI J4xyF8l7CM6f95JoAuCKVV3hAyC82AJx3oufi0R3FEbwmyAIo2FTQHLoC0pTmyIbxTOb xIL65RVDvr3bOv4bN8IYbqyLk2ttX4M9GHjAZI19rN7IfOAG3JuPFuVqgVpyLji5pa1e bv5Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785902026; x=1786506826; h=content-disposition:content-type:mime-version:message-id:subject:cc :to:from:date:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=aArZVDET4swnltVql8eMgSIS5WdHN1v7z0Y93I05HeA=; b=kERMuys3V/0dPmFKphDHVpUR/Ipyt4lFVx5F3R9JCQK7vEJTj7DKEZ2kKiNsD1nsd/ RuNs08BIVXrTP5weosetixNNrmWnGWfghTCkho+mo9qbvDaS80mhG5XMXPlq2lRIsxgA 0vxs+T5T9eMEXCMQeI0O6fvs37s71A1/oyiSbLnoJTeJFnhBQ3xs5ydWcRaGRgHtxg8n y5M9bDByDHbSaxkMvgsEm+Q8JA6iRqW3dT+Ds53pNKrHpW4fF+1AWes3tfIhUd2j1qbu O7yrdwJBi3xRGwpV8gQ9SQgPVAX/piHj0EEOw8vsmJLYZZQ6jb40v0M+/GAoXqYrGcDB 3oNw== X-Forwarded-Encrypted: i=1; AHgh+RqtPZfNww2SlpylgI1oDQludjNbc8fE5SF3N906rU0X9JCD+JVF83YNMo2JB7BA59lCL4x0INoilKfTiPE=@vger.kernel.org X-Gm-Message-State: AOJu0YwgvLi//52rBs6wEwtYH90dO8v+gUlVZ534+5Rb76zbg9zh0mVY E62JdhZa476b6k0FZO6AfDTCH/sgc6guumzqaQrCfZsRGR7ltwnz9dy4 X-Gm-Gg: AR+sD11KcTBzsw/36H+STbhBtR/AAn/3PWN3wBz+woIUMBCVTyz4mdYfu9BLt41KG23 AdxUkJZpnrll4kM6Q3kdyIs82I5Fj7NrDB2g+dqcVjxuaRQcLDu4lzkTB95+FTU/T9aenFhEyQ/ WyD76SH7N70PI2WqIZf0euQPmJOpSQIjFjRx6Uy9iuXT9HSj/skUEcbtccZpyRuSiMg2d+PNBGR 2BdptSo+2KZkLssfR7UIJHFQGKhPQpv0c2D3DrMutijhe+MzgwAD1lDbxZBtA0jNTprw1ZIpq+Y eTbhmd4/NSg8L6ZCNC5ra1Tbpg6eVnDE89+/YFB1UU2EBqMb5l45i+8B2XestbYj+HmFk6DVdIW Z1pvLLed0YeNm4Qz8wOm8p4cTRCDcQk8GtHzWhhxj7jcy3m5t7Qp0VnXOjqZyHSZh9WeZZ9b0VX CiOZSlGGX24rsf/5EbuxMkVYllGM0lC3CA6xLJU8Eyjuq4hBunUSwQK8q1LZOk63Rh0lhVTD0En d//pDYk0V0pqkQfinv8cK3Erhtz6A== X-Received: by 2002:a17:902:da87:b0:2cf:4c0f:5129 with SMTP id d9443c01a7336-2d0caa03fcfmr34619265ad.17.1785902025892; Tue, 04 Aug 2026 20:53:45 -0700 (PDT) Received: from google.com ([2a00:79e0:2ebe:8:bf3a:ac93:79c4:84fd]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-315863b76cfsm10030054eec.4.2026.08.04.20.53.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 04 Aug 2026 20:53:45 -0700 (PDT) Date: Tue, 4 Aug 2026 20:53:42 -0700 From: Dmitry Torokhov To: Mark Pearson , "Derek J. Clark" Cc: Ike Panhc , Hans de Goede , Ilpo =?utf-8?B?SsOkcnZpbmVu?= , Maxim Mikityanskiy , platform-driver-x86@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH] platform/x86: ideapad-laptop: do not poke keyboard controller Message-ID: 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=us-ascii Content-Disposition: inline On Ideapad Z570 the driver tries to disable and re-enable data coming from the touchpad by poking directly into 8042 keyboard controller. This may coincide with the controller resuming and leads to spews in dmesg and potentially other instabilities. Instead of using i8042_command() to control the touchpad state create an input handler that serves as a filter and drop events coming from the touchpad when it is supposed to be off. Signed-off-by: Dmitry Torokhov --- Spring^WFall patch queue cleaning. The concept of this patch was discussed in August 2024 but I neglected to send the final version of the patch: https://lore.kernel.org/r/20240805141608.170844-1-hdegoede@redhat.com/ drivers/platform/x86/lenovo/ideapad-laptop.c | 171 ++++++++++++++++++- 1 file changed, 168 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/lenovo/ideapad-laptop.c b/drivers/platform/x86/lenovo/ideapad-laptop.c index 4fbc904f1fc3..384faa511120 100644 --- a/drivers/platform/x86/lenovo/ideapad-laptop.c +++ b/drivers/platform/x86/lenovo/ideapad-laptop.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -210,6 +209,13 @@ struct ideapad_private { struct led_classdev led; unsigned int last_brightness; } fn_lock; + struct { + bool initialized; + bool active; + struct input_handler handler; + struct input_dev *tp_dev; + spinlock_t lock; + } tp_switch; }; static bool no_bt_rfkill; @@ -1462,6 +1468,158 @@ static void ideapad_check_special_buttons(struct ideapad_private *priv) } } +struct ideapad_tpswitch_handle { + struct input_handle handle; + struct ideapad_private *priv; +}; + +#define to_tpswitch_handle(h) \ + container_of(h, struct ideapad_tpswitch_handle, handle); + +static int ideapad_tpswitch_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + struct ideapad_private *priv = + container_of(handler, struct ideapad_private, tp_switch.handler); + struct ideapad_tpswitch_handle *h; + int error; + + h = kzalloc_obj(*h); + if (!h) + return -ENOMEM; + + h->priv = priv; + h->handle.dev = dev; + h->handle.handler = handler; + h->handle.name = "ideapad-tpswitch"; + + error = input_register_handle(&h->handle); + if (error) + goto err_free_handle; + + /* + * FIXME: ideally we do not want to open the input device here + * if there are no other users. We need a notion of "observer" + * handlers in the input core. + */ + error = input_open_device(&h->handle); + if (error) + goto err_unregister_handle; + + scoped_guard(spinlock_irq, &priv->tp_switch.lock) + priv->tp_switch.tp_dev = dev; + + return 0; + +err_unregister_handle: + input_unregister_handle(&h->handle); +err_free_handle: + kfree(h); + return error; +} + +static void ideapad_tpswitch_disconnect(struct input_handle *handle) +{ + struct ideapad_tpswitch_handle *h = to_tpswitch_handle(handle); + struct ideapad_private *priv = h->priv; + + scoped_guard(spinlock_irq, &priv->tp_switch.lock) + priv->tp_switch.tp_dev = NULL; + + input_close_device(handle); + input_unregister_handle(handle); + kfree(h); +} + +static bool ideapad_tpswitch_filter(struct input_handle *handle, + unsigned int type, unsigned int code, + int value) +{ + struct ideapad_tpswitch_handle *h = to_tpswitch_handle(handle); + struct ideapad_private *priv = h->priv; + + if (!priv->tp_switch.active) + return false; + + /* Allow passing button release events, drop everything else */ + return !(type == EV_KEY && value == 0) && + !(type == EV_SYN && code == SYN_REPORT); + +} + +static const struct input_device_id ideapad_tpswitch_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .bustype = BUS_I8042, + .vendor = 0x0002, + .evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) }, + .keybit = { [BIT_WORD(BTN_TOOL_FINGER)] = + BIT_MASK(BTN_TOOL_FINGER) }, + .absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | + BIT_MASK(ABS_PRESSURE) | + BIT_MASK(ABS_TOOL_WIDTH) }, + }, + { } +}; + +static int ideapad_tpswitch_init(struct ideapad_private *priv) +{ + int error; + + if (!priv->features.ctrl_ps2_aux_port) + return 0; + + spin_lock_init(&priv->tp_switch.lock); + + priv->tp_switch.handler.name = "ideapad-tpswitch"; + priv->tp_switch.handler.id_table = ideapad_tpswitch_ids; + priv->tp_switch.handler.filter = ideapad_tpswitch_filter; + priv->tp_switch.handler.connect = ideapad_tpswitch_connect; + priv->tp_switch.handler.disconnect = ideapad_tpswitch_disconnect; + + error = input_register_handler(&priv->tp_switch.handler); + if (error) { + dev_err(&priv->platform_device->dev, + "failed to register touchpad switch handler: %d", + error); + return error; + } + + priv->tp_switch.initialized = true; + return 0; +} + +static void ideapad_tpswitch_exit(struct ideapad_private *priv) +{ + if (priv->tp_switch.initialized) { + input_unregister_handler(&priv->tp_switch.handler); + priv->tp_switch.initialized = false; + } +} + +static void ideapad_tpswitch_toggle(struct ideapad_private *priv, bool on) +{ + guard(spinlock_irq)(&priv->tp_switch.lock); + + priv->tp_switch.active = on; + if (on) { + struct input_dev *tp_dev = priv->tp_switch.tp_dev; + if (tp_dev) { + input_report_key(tp_dev, BTN_TOUCH, 0); + input_report_key(tp_dev, BTN_TOOL_FINGER, 0); + input_report_key(tp_dev, BTN_TOOL_DOUBLETAP, 0); + input_report_key(tp_dev, BTN_TOOL_TRIPLETAP, 0); + input_report_key(tp_dev, BTN_LEFT, 0); + input_report_key(tp_dev, BTN_RIGHT, 0); + input_report_key(tp_dev, BTN_MIDDLE, 0); + input_sync(tp_dev); + } + } +} + /* * backlight */ @@ -1799,7 +1957,6 @@ static void ideapad_fn_lock_led_exit(struct ideapad_private *priv) static void ideapad_sync_touchpad_state(struct ideapad_private *priv, bool send_events) { unsigned long value; - unsigned char param; int ret; /* Without reading from EC touchpad LED doesn't switch state */ @@ -1815,7 +1972,7 @@ static void ideapad_sync_touchpad_state(struct ideapad_private *priv, bool send_ * KEY_TOUCHPAD_ON to not to get out of sync with LED */ if (priv->features.ctrl_ps2_aux_port) - i8042_command(¶m, value ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE); + ideapad_tpswitch_toggle(priv, value); /* * On older models the EC controls the touchpad and toggles it on/off @@ -2402,6 +2559,10 @@ static int ideapad_acpi_add(struct platform_device *pdev) if (err) goto input_failed; + err = ideapad_tpswitch_init(priv); + if (err) + goto tpswitch_failed; + err = ideapad_kbd_bl_init(priv); if (err) { if (err != -ENODEV) @@ -2478,6 +2639,9 @@ static int ideapad_acpi_add(struct platform_device *pdev) ideapad_fn_lock_led_exit(priv); ideapad_kbd_bl_exit(priv); + ideapad_tpswitch_exit(priv); + +tpswitch_failed: ideapad_input_exit(priv); input_failed: @@ -2507,6 +2671,7 @@ static void ideapad_acpi_remove(struct platform_device *pdev) ideapad_fn_lock_led_exit(priv); ideapad_kbd_bl_exit(priv); + ideapad_tpswitch_exit(priv); ideapad_input_exit(priv); ideapad_debugfs_exit(priv); } -- 2.55.0.571.g244d577d93-goog -- Dmitry