* Re: [PATCH v4 3/3] arm64: dts: qcom: sm6125-xiaomi-laurel-sprout: Add Focaltech FT3518 touchscreen
From: Konrad Dybcio @ 2026-01-22 13:52 UTC (permalink / raw)
To: yedaya.ka, Kamil Gołda, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, Konrad Dybcio
Cc: linux-input, devicetree, linux-kernel, linux-arm-msm
In-Reply-To: <20260120-touchscreen-patches-v4-3-30145da9d6d3@gmail.com>
On 1/20/26 10:40 PM, Yedaya Katsman via B4 Relay wrote:
> From: Yedaya Katsman <yedaya.ka@gmail.com>
>
> Add device tree node for the Focaltech FT3518 touchscreen on
> Xiaomi Mi A3 (laurel-sprout).
>
> Note that gpio pin 83 for the regulator isn't documented downstream
> except in the touchscreen node so it's not defined in the tlmm.
>
> Enable qupv3_id_0 and i2c2 bus that the touchscreen is on.
>
> Downstream references:
> Link: https://github.com/MiCode/Xiaomi_Kernel_OpenSource/blob/laurel-r-oss/arch/arm64/boot/dts/qcom/trinket-pinctrl.dtsi
> Link: https://github.com/MiCode/Xiaomi_Kernel_OpenSource/blob/laurel-r-oss/arch/arm64/boot/dts/qcom/laurel_sprout-qrd.dtsi
>
> Co-developed-by: Kamil Gołda <kamil.golda@protonmail.com>
> Signed-off-by: Kamil Gołda <kamil.golda@protonmail.com>
> Signed-off-by: Yedaya Katsman <yedaya.ka@gmail.com>
> ---
> .../boot/dts/qcom/sm6125-xiaomi-laurel-sprout.dts | 65 ++++++++++++++++++++++
> 1 file changed, 65 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/qcom/sm6125-xiaomi-laurel-sprout.dts b/arch/arm64/boot/dts/qcom/sm6125-xiaomi-laurel-sprout.dts
> index 994fb0412fcbdf5466f87a325c48b697a37b514b..ea9dbc6927a14ca66da818b81aeb0c95ec3c2e7a 100644
> --- a/arch/arm64/boot/dts/qcom/sm6125-xiaomi-laurel-sprout.dts
> +++ b/arch/arm64/boot/dts/qcom/sm6125-xiaomi-laurel-sprout.dts
> @@ -82,6 +82,18 @@ key-volume-up {
> };
> };
>
> + ts_vdd_supply: regulator-ts-vdd {
> + compatible = "regulator-fixed";
> + regulator-name = "ts_vdd_supply";
> + regulator-min-microvolt = <3300000>;
> + regulator-max-microvolt = <3300000>;
> +
> + gpio = <&tlmm 83 GPIO_ACTIVE_HIGH>;
> + enable-active-high;
Ideally you'd add a pin config for gpio83 too
[...]
> + touchscreen@38 {
> + compatible = "focaltech,ft3518";
> + reg = <0x38>;
> + interrupts-extended = <&tlmm 88 IRQ_TYPE_EDGE_FALLING>;
> +
> + vcc-supply = <&ts_vdd_supply>;
> +
> + pinctrl-names = "default","sleep";
> + pinctrl-0 = <&ts_int_active &ts_reset_active>;
> + pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
property-n
property-names
in this order specifically, please
lgtm otherwise
Konrad
^ permalink raw reply
* Re: [PATCH v1] Input: pf1550 - Remove "defined but unused" warning
From: Samuel Kayode @ 2026-01-22 13:09 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: Vaibhav Gupta, imx, linux-input, linux-kernel
In-Reply-To: <wfdhjzhkggp26mwqgvxuanuzjq6v2g4pmc44jz6kx4xwkewwhr@movikefnuvcz>
On Sat, Dec 13, 2025 at 01:38:57AM -0800, Dmitry Torokhov wrote:
> On Wed, Dec 10, 2025 at 09:11:41PM +0000, Vaibhav Gupta wrote:
> > If 'CONFIG_PM_SLEEP' is not set, compiler throws warning for *suspend() and
> > *resume() function for this driver. Using new 'DEFINE_SIMPLE_DEV_PM_OPS'
> > fixes it.
> >
> > Signed-off-by: Vaibhav Gupta <vaibhavgupta40@gmail.com>
>
> Applied, thank you.
>
I think this patch was dropped.
Thanks,
Sam
^ permalink raw reply
* [PATCH 0/1] Input: Add TouchNetix aXiom touchscreen driver
From: AThomas63 @ 2026-01-22 12:48 UTC (permalink / raw)
To: dmitry.torokhov
Cc: linux-input, linux-kernel, mark.satterthwaite, andrew.thomas,
m.felsch, kamel.bouhara
A Summary of the added features:
- Add input driver support for TouchNetix aXiom touchscreen controller
using either I2C or SPI.
- Support ABS_MT touch reports in axiom_process_u41_report().
- Support both polling and interrupt mode (trigger low).
- Add basic documentation and provide example device tree bindings.
- Provide the basic structure to add firmware and config download in
the future via both I2C and SPI.
I understand the process is already under way to add a driver to support
aXiom by Marco at Pengutronix.
I think it would be most useful for us to attempt to combine this patch
in order to support both his changes for config/firmware download and
the patch here for SPI support along with I2C. I would be happy to help
with this process.
This would allow TouchNetix to support a broader range of users of the
aXiom device family.
I also have a device tree binding however I have not added it sincec
from my understanding this should go in a different patch:
Documentation/devicetree/bindings/input/touchscreen/tnx,axiom.yaml
My apologies if there are any obvious mistakes in this patch, this is the
first patch that I have submitted.
Many thanks for your time,
Andrew Thomas
AThomas63 (1):
Adding support for aXiom touchscreen controller
drivers/input/touchscreen/Kconfig | 30 ++
drivers/input/touchscreen/Makefile | 3 +
drivers/input/touchscreen/axiom_core.c | 482 +++++++++++++++++++++++++
drivers/input/touchscreen/axiom_core.h | 128 +++++++
drivers/input/touchscreen/axiom_i2c.c | 152 ++++++++
drivers/input/touchscreen/axiom_spi.c | 159 ++++++++
6 files changed, 954 insertions(+)
create mode 100644 drivers/input/touchscreen/axiom_core.c
create mode 100644 drivers/input/touchscreen/axiom_core.h
create mode 100644 drivers/input/touchscreen/axiom_i2c.c
create mode 100644 drivers/input/touchscreen/axiom_spi.c
--
2.43.0
^ permalink raw reply
* [PATCH 1/1] Adding support for aXiom touchscreen controller
From: AThomas63 @ 2026-01-22 12:48 UTC (permalink / raw)
To: dmitry.torokhov
Cc: linux-input, linux-kernel, mark.satterthwaite, andrew.thomas,
m.felsch, kamel.bouhara
In-Reply-To: <20260122124819.273188-1-andrew.thomas@touchnetix.com>
Signed-off-by: AThomas63 <andrew.thomas@touchnetix.com>
---
drivers/input/touchscreen/Kconfig | 30 ++
drivers/input/touchscreen/Makefile | 3 +
drivers/input/touchscreen/axiom_core.c | 482 +++++++++++++++++++++++++
drivers/input/touchscreen/axiom_core.h | 128 +++++++
drivers/input/touchscreen/axiom_i2c.c | 152 ++++++++
drivers/input/touchscreen/axiom_spi.c | 159 ++++++++
6 files changed, 954 insertions(+)
create mode 100644 drivers/input/touchscreen/axiom_core.c
create mode 100644 drivers/input/touchscreen/axiom_core.h
create mode 100644 drivers/input/touchscreen/axiom_i2c.c
create mode 100644 drivers/input/touchscreen/axiom_spi.c
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 7d5b72ee07fa..f2b4fb317bdd 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -162,6 +162,36 @@ config TOUCHSCREEN_AUO_PIXCIR
To compile this driver as a module, choose M here: the
module will be called auo-pixcir-ts.
+config TOUCHSCREEN_AXIOM_CORE
+ tristate "TouchNetix Axiom touchscreen"
+ help
+ Say Y here if you have an Axiom touchscreen connected
+ to your system. You will also need to select appropriate
+ bus connection below.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called axiom_core.
+
+config TOUCHSCREEN_AXIOM_I2C
+ tristate "support I2C bus connection"
+ depends on TOUCHSCREEN_AXIOM_CORE && I2C
+ help
+ Say Y here if the touchscreen is connected via I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called axiom_spi.
+
+config TOUCHSCREEN_AXIOM_SPI
+ tristate "support SPI bus connection"
+ depends on TOUCHSCREEN_AXIOM_CORE && SPI
+ help
+ Say Y here if the touchscreen is connected via SPI bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called axiom_spi.
+
config TOUCHSCREEN_BU21013
tristate "BU21013 based touch panel controllers"
depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index ab9abd151078..9b7d572c4589 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -19,6 +19,9 @@ obj-$(CONFIG_TOUCHSCREEN_APPLE_Z2) += apple_z2.o
obj-$(CONFIG_TOUCHSCREEN_AR1021_I2C) += ar1021_i2c.o
obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o
obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o
+obj-$(CONFIG_TOUCHSCREEN_AXIOM_CORE) += axiom_core.o
+obj-$(CONFIG_TOUCHSCREEN_AXIOM_I2C) += axiom_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_AXIOM_SPI) += axiom_spi.o
obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
obj-$(CONFIG_TOUCHSCREEN_BU21029) += bu21029_ts.o
obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o
diff --git a/drivers/input/touchscreen/axiom_core.c b/drivers/input/touchscreen/axiom_core.c
new file mode 100644
index 000000000000..7983effe40f7
--- /dev/null
+++ b/drivers/input/touchscreen/axiom_core.c
@@ -0,0 +1,482 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TouchNetix aXiom Touchscreen Driver
+ *
+ * Copyright (C) 2020-2026 TouchNetix Ltd.
+ *
+ * Author(s): Mark Satterthwaite <mark.satterthwaite@touchnetix.com>
+ * Pedro Torruella <pedro.torruella@touchnetix.com>
+ * Bart Prescott <bartp@baasheep.co.uk>
+ * Hannah Rossiter <hannah.rossiter@touchnetix.com>
+ * Andrew Thomas <andrew.thomas@touchnetix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+// #define DEBUG // Enable debug messages
+
+#include <linux/device.h>
+#include <linux/input/mt.h>
+#include <linux/crc16.h>
+#include <linux/property.h>
+#include <linux/interrupt.h>
+#include <linux/unaligned.h>
+#include "axiom_core.h"
+
+/* u31 device info masks */
+#define AX_DEV_ID_MASK GENMASK(14, 0)
+#define AX_MODE BIT(15)
+#define AX_FW_REV_MINOR_MASK GENMASK(7, 0)
+#define AX_FW_REV_MAJOR_MASK GENMASK(15, 8)
+#define AX_VARIANT_MASK GENMASK(5, 0)
+#define AX_FW_STATUS BIT(7)
+#define AX_TCP_REV_MASK GENMASK(15, 8)
+#define AX_BOOT_REV_MINOR_MASK GENMASK(7, 0)
+#define AX_BOOT_REV_MAJOR_MASK GENMASK(15, 8)
+#define AX_NUM_USAGES_MASK GENMASK(7, 0)
+#define AX_SILICON_REV_MASK GENMASK(11, 8)
+#define AX_RUNTIME_FW_PATCH_MASK GENMASK(15, 12)
+
+/* u31 usage table entry masks */
+#define AX_U31_USAGE_NUM_MASK GENMASK(7, 0)
+#define AX_U31_START_PAGE_MASK GENMASK(15, 8)
+#define AX_U31_NUM_PAGES_MASK GENMASK(7, 0)
+#define AX_U31_MAX_OFFSET_MASK GENMASK(14, 8)
+#define AX_U31_OFFSET_TYPE_BIT BIT(15)
+#define AX_U31_UIF_REV_MASK GENMASK(7, 0)
+#define AX_U31_USAGE_TYPE_MASK GENMASK(15, 8)
+
+/* u34 report masks */
+#define AX_U34_LEN_MASK GENMASK(6, 0)
+#define AX_U34_OVERFLOW BIT(7)
+#define AX_U34_USAGE_MASK GENMASK(15, 8)
+#define AX_U34_PAYLOAD_BUFFER 2
+
+/* u41 report masks */
+#define AX_U41_PRESENT_MASK GENMASK(9, 0)
+#define U41_X_Y_OFFSET (2)
+#define U41_COORD_SIZE (4)
+#define U41_Z_OFFSET (42)
+
+static const char *const fw_variants[] = { "3D", "2D", "FORCE",
+ "0D", "XL", "TOUCHPAD" };
+
+static int axiom_set_capabilities(struct input_dev *input_dev)
+{
+ input_dev->name = "TouchNetix aXiom Touchscreen";
+ input_dev->phys = "input/axiom_ts";
+
+ // Single Touch
+ input_set_abs_params(input_dev, ABS_X, 0, 65535, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, 65535, 0, 0);
+
+ // Multi Touch
+ // Min, Max, Fuzz (expected noise in px, try 4?) and Flat
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 65535, 0, 0);
+ // Min, Max, Fuzz (expected noise in px, try 4?) and Flat
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, 65535, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_DISTANCE, 0, 127, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 127, 0, 0);
+
+ input_mt_init_slots(input_dev, U41_MAX_TARGETS, INPUT_MT_DIRECT);
+
+ return 0;
+}
+
+static struct u31_usage_entry *usage_find_entry(struct axiom *ax, u16 usage)
+{
+ u16 i;
+
+ for (i = 0; i < ax->dev_info.num_usages; i++) {
+ if (ax->usage_table[i].usage_num == usage)
+ return &ax->usage_table[i];
+ }
+
+ pr_err("aXiom-core: Usage u%02x not found in usage table\n", usage);
+ return ERR_PTR(-EINVAL);
+}
+
+static void axiom_unpack_device_info(const u8 *buf,
+ struct axiom_device_info *info)
+{
+ u16 w;
+
+ w = get_unaligned_le16(buf);
+ info->device_id = FIELD_GET(AX_DEV_ID_MASK, w);
+ info->mode = !!(w & AX_MODE);
+
+ w = get_unaligned_le16(buf + 2);
+ info->runtime_fw_rev_minor = FIELD_GET(AX_FW_REV_MINOR_MASK, w);
+ info->runtime_fw_rev_major = FIELD_GET(AX_FW_REV_MAJOR_MASK, w);
+
+ w = get_unaligned_le16(buf + 4);
+ info->device_build_variant = FIELD_GET(AX_VARIANT_MASK, w);
+ info->runtime_fw_status = !!(w & AX_FW_STATUS);
+ info->tcp_revision = FIELD_GET(AX_TCP_REV_MASK, w);
+
+ w = get_unaligned_le16(buf + 6);
+ info->bootloader_fw_rev_minor = FIELD_GET(AX_BOOT_REV_MINOR_MASK, w);
+ info->bootloader_fw_rev_major = FIELD_GET(AX_BOOT_REV_MAJOR_MASK, w);
+
+ info->jedec_id = get_unaligned_le16(buf + 8);
+
+ w = get_unaligned_le16(buf + 10);
+ info->num_usages = FIELD_GET(AX_NUM_USAGES_MASK, w);
+ info->silicon_revision = FIELD_GET(AX_SILICON_REV_MASK, w);
+ info->runtime_fw_rev_patch = FIELD_GET(AX_RUNTIME_FW_PATCH_MASK, w);
+}
+
+static void axiom_unpack_usage_table(u8 *buf, struct axiom *ax)
+{
+ u8 *ptr;
+ struct u31_usage_entry *entry;
+ int i;
+ u16 w;
+ u16 report_len;
+
+ for (i = 0; i < ax->dev_info.num_usages && i < U31_MAX_USAGES; i++) {
+ entry = &ax->usage_table[i];
+ /* Calculate offset for this specific entry */
+ ptr = buf + (i * SIZE_U31_USAGE_ENTRY);
+
+ w = get_unaligned_le16(ptr);
+ entry->usage_num = FIELD_GET(AX_U31_USAGE_NUM_MASK, w);
+ entry->start_page = FIELD_GET(AX_U31_START_PAGE_MASK, w);
+
+ w = get_unaligned_le16(ptr + 2);
+ entry->num_pages = FIELD_GET(AX_U31_NUM_PAGES_MASK, w);
+ entry->max_offset = FIELD_GET(AX_U31_MAX_OFFSET_MASK, w);
+ entry->offset_type = !!(w & AX_U31_OFFSET_TYPE_BIT);
+
+ w = get_unaligned_le16(ptr + 4);
+ entry->uifrevision = FIELD_GET(AX_U31_UIF_REV_MASK, w);
+ entry->usage_type = FIELD_GET(AX_U31_USAGE_TYPE_MASK, w);
+
+ // Convert words to bytes
+ report_len = (entry->max_offset + 1) * 2;
+ if ((entry->usage_type == REPORT) &&
+ (report_len > ax->max_report_len)) {
+ ax->max_report_len = report_len;
+ }
+ }
+}
+
+static int axiom_init_dev_info(struct axiom *ax)
+{
+ int i;
+ struct u31_usage_entry *u;
+ int err;
+ const char *variant_str;
+
+ /* Read page 0 of u31 */
+ err = ax->bus_ops->read(ax->dev, 0x0, SIZE_U31_DEVICE_INFO,
+ ax->read_buf);
+ if (err)
+ return -EIO;
+
+ axiom_unpack_device_info(ax->read_buf, &ax->dev_info);
+
+ if (ax->dev_info.device_build_variant < ARRAY_SIZE(fw_variants)) {
+ variant_str = fw_variants[ax->dev_info.device_build_variant];
+ } else {
+ variant_str = "UNKNOWN";
+ }
+ char silicon_rev = (char)(0x41 + ax->dev_info.silicon_revision);
+
+ dev_info(ax->dev, "Firmware Info:\n");
+ dev_info(ax->dev, " BL Mode : %u\n", ax->dev_info.mode);
+ dev_info(ax->dev, " Device ID : %04x\n", ax->dev_info.device_id);
+ dev_info(ax->dev, " FW Revision : %u.%u.%u-%s %s\n",
+ ax->dev_info.runtime_fw_rev_major,
+ ax->dev_info.runtime_fw_rev_minor,
+ ax->dev_info.runtime_fw_rev_patch,
+ (ax->dev_info.runtime_fw_status == 0) ? "eng" : "prod",
+ variant_str);
+ dev_info(ax->dev, " BL Revision : %02x.%02x\n",
+ ax->dev_info.bootloader_fw_rev_major,
+ ax->dev_info.bootloader_fw_rev_minor);
+ dev_info(ax->dev, " Silicon : 0x%04X (Rev %c)\n",
+ ax->dev_info.jedec_id, silicon_rev);
+ dev_info(ax->dev, " Num Usages : %u\n", ax->dev_info.num_usages);
+
+ if (ax->dev_info.num_usages > U31_MAX_USAGES) {
+ dev_err(ax->dev,
+ "Num usages (%u) exceeds maximum supported (%u)\n",
+ ax->dev_info.num_usages, U31_MAX_USAGES);
+ return -EINVAL;
+ }
+
+ /* Read the second page of u31 to get the usage table */
+ err = ax->bus_ops->read(ax->dev, 0x100,
+ sizeof(ax->usage_table[0]) *
+ ax->dev_info.num_usages,
+ ax->read_buf);
+ if (err)
+ return -EIO;
+
+ axiom_unpack_usage_table(ax->read_buf, ax);
+
+ dev_info(ax->dev, "Usage Table:\n");
+ for (i = 0; i < ax->dev_info.num_usages; i++) {
+ u = &ax->usage_table[i];
+
+ dev_info(
+ ax->dev,
+ " Usage: u%02x Rev: %3u Page: 0x%02x00 Num Pages: %3u\n",
+ u->usage_num, u->uifrevision, u->start_page,
+ u->num_pages);
+ }
+ dev_info(ax->dev, "Max Report Length: %u\n", ax->max_report_len);
+
+ if (ax->max_report_len > AXIOM_MAX_READ_SIZE) {
+ dev_err(ax->dev,
+ "aXiom maximum report length (%u) greater than allocated buffer size (%u).",
+ ax->max_report_len, AXIOM_MAX_READ_SIZE);
+ return -EINVAL;
+ }
+
+ /* Set u34 address to allow direct access to report reading address */
+ u = usage_find_entry(ax, 0x34);
+ if (IS_ERR(u))
+ return PTR_ERR(u);
+ ax->u34_address = u->start_page << 8;
+
+ return 0;
+}
+
+static int axiom_process_u41_report(struct axiom *ax, u8 *report)
+{
+ int i;
+ u16 target_present;
+ bool active;
+ u8 offset;
+ enum u41_target_state_e state;
+ u16 x;
+ u16 y;
+ s8 z;
+
+ target_present =
+ FIELD_GET(AX_U41_PRESENT_MASK, get_unaligned_le16(&report[0]));
+
+ for (i = 0; i < U41_MAX_TARGETS; i++) {
+ active = !!((target_present >> i) & 1);
+
+ offset = U41_X_Y_OFFSET + (i * U41_COORD_SIZE);
+ x = get_unaligned_le16(&report[offset]);
+ y = get_unaligned_le16(&report[offset + 2]);
+ z = report[U41_Z_OFFSET + i];
+
+ if (!active)
+ state = Target_State_Not_Present;
+ else if (z >= 0)
+ state = Target_State_Touching;
+ else if ((z > U41_PROX_LEVEL) && (z < 0))
+ state = Target_State_Hover;
+ else if (z == U41_PROX_LEVEL)
+ state = Target_State_Prox;
+ else
+ state = Target_State_Not_Present;
+
+ dev_dbg(ax->dev, "Target %d: x=%u y=%u z=%d present=%d\n", i, x,
+ y, z, active);
+
+ switch (state) {
+ case Target_State_Not_Present:
+ case Target_State_Prox:
+
+ input_mt_slot(ax->input, i);
+ input_mt_report_slot_inactive(ax->input);
+ break;
+
+ case Target_State_Hover:
+ case Target_State_Touching:
+
+ input_mt_slot(ax->input, i);
+ input_report_abs(ax->input, ABS_MT_TRACKING_ID, i);
+ input_report_abs(ax->input, ABS_MT_POSITION_X, x);
+ input_report_abs(ax->input, ABS_MT_POSITION_Y, y);
+
+ if (state == Target_State_Touching) {
+ input_report_abs(ax->input, ABS_MT_DISTANCE, 0);
+ input_report_abs(ax->input, ABS_MT_PRESSURE, z);
+ } else { /* Hover */
+ input_report_abs(ax->input, ABS_MT_DISTANCE, -z);
+ input_report_abs(ax->input, ABS_MT_PRESSURE, 0);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ input_mt_sync_frame(ax->input);
+ input_sync(ax->input);
+
+ return 0;
+}
+
+static int axiom_process_report(struct axiom *ax, u8 *report)
+{
+ int err;
+ struct u34_report_header hdr;
+ u16 crc_calc;
+ u16 crc_report;
+ u8 len;
+ u16 hdr_buf = get_unaligned_le16(&report[0]);
+
+ dev_dbg(ax->dev, "Payload Data %*ph\n", ax->max_report_len, report);
+
+ hdr.report_length = FIELD_GET(AX_U34_LEN_MASK, hdr_buf);
+ hdr.overflow = !!(hdr_buf & AX_U34_OVERFLOW);
+ hdr.report_usage = FIELD_GET(AX_U34_USAGE_MASK, hdr_buf);
+
+ len = hdr.report_length << 1;
+ if (hdr.report_length == 0) {
+ dev_err(ax->dev, "Zero length report discarded.\n");
+ return -EIO;
+ }
+
+ // Length is 16 bit words and remove the size of the CRC16 itself
+ crc_report = (report[len - 1] << 8) | (report[len - 2]);
+ crc_calc = crc16(0, report, (len - 2));
+
+ if (crc_calc != crc_report) {
+ dev_err(ax->dev,
+ "CRC mismatch! Expected: %04X, Calculated CRC: %04X. Report discarded.\n",
+ crc_report, crc_calc);
+ return -EIO;
+ }
+
+ switch (hdr.report_usage) {
+ case AX_2DCTS_REPORT_ID:
+ err = axiom_process_u41_report(ax,
+ &report[AX_U34_PAYLOAD_BUFFER]);
+ break;
+
+ default:
+ break;
+ }
+
+ return err;
+}
+
+static void axiom_poll(struct input_dev *input_dev)
+{
+ struct axiom *ax = input_get_drvdata(input_dev);
+ int err;
+
+ /* Read touch reports from u34 */
+ err = ax->bus_ops->read(ax->dev, ax->u34_address, ax->max_report_len,
+ ax->read_buf);
+ if (err)
+ return;
+
+ err = axiom_process_report(ax, ax->read_buf);
+ if (err)
+ dev_err(ax->dev, "Failed to process report: %d\n", err);
+}
+
+static irqreturn_t axiom_irq(int irq, void *handle)
+{
+ struct axiom *ax = handle;
+ int err;
+
+ /* Read touch reports from u34 */
+ err = ax->bus_ops->read(ax->dev, ax->u34_address, ax->max_report_len,
+ ax->read_buf);
+ if (err)
+ goto out;
+
+ err = axiom_process_report(ax, ax->read_buf);
+ if (err)
+ dev_err(ax->dev, "Failed to process report: %d\n", err);
+
+out:
+ return IRQ_HANDLED;
+}
+
+struct axiom *axiom_probe(const struct axiom_bus_ops *bus_ops,
+ struct device *dev, int irq)
+{
+ struct axiom *ax;
+ struct input_dev *input_dev;
+ int err;
+ bool poll_enable = false;
+ u8 poll_period = 0;
+
+ ax = devm_kzalloc(dev, sizeof(*ax), GFP_KERNEL);
+ if (!ax)
+ return ERR_PTR(-ENOMEM);
+
+ input_dev = devm_input_allocate_device(dev);
+ if (!input_dev) {
+ pr_err("ERROR: aXiom-core: Failed to allocate memory for input device!\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ poll_enable = device_property_read_bool(dev, "axiom,poll-enable");
+
+ device_property_read_u8(dev, "axiom,poll-period", &poll_period);
+ if (!poll_period)
+ poll_period = AX_POLLING_PERIOD_MS;
+
+ ax->dev = dev;
+ ax->input = input_dev;
+ ax->bus_ops = bus_ops;
+ ax->irq = irq;
+
+ dev_info(dev, "aXiom Probe\n");
+ if (poll_enable)
+ dev_info(dev, "Polling Period : %u\n", poll_period);
+ else
+ dev_info(dev, "Device IRQ : %u\n", ax->irq);
+
+ axiom_set_capabilities(input_dev);
+
+ err = axiom_init_dev_info(ax);
+ if (err) {
+ dev_err(ax->dev, "Failed to read device info, err: %d\n", err);
+ return ERR_PTR(err);
+ }
+
+ if (poll_enable) {
+ err = input_setup_polling(input_dev, axiom_poll);
+ if (err) {
+ dev_err(ax->dev, "could not set up polling mode, %d\n",
+ err);
+ return ERR_PTR(err);
+ }
+
+ input_set_poll_interval(input_dev, poll_period);
+ } else {
+ err = devm_request_threaded_irq(ax->dev, ax->irq, NULL,
+ axiom_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "axiom_irq", ax);
+ if (err)
+ return ERR_PTR(err);
+ }
+
+ err = input_register_device(input_dev);
+ if (err) {
+ dev_err(ax->dev, "Failed to register input device: %d\n", err);
+ return ERR_PTR(err);
+ }
+
+ input_set_drvdata(input_dev, ax);
+
+ return ax;
+}
+EXPORT_SYMBOL_GPL(axiom_probe);
+
+MODULE_AUTHOR("TouchNetix <support@touchnetix.com>");
+MODULE_DESCRIPTION("aXiom touchscreen core logic");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("axiom");
+MODULE_VERSION("1.0.0");
diff --git a/drivers/input/touchscreen/axiom_core.h b/drivers/input/touchscreen/axiom_core.h
new file mode 100644
index 000000000000..ca77f9038cb1
--- /dev/null
+++ b/drivers/input/touchscreen/axiom_core.h
@@ -0,0 +1,128 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * TouchNetix aXiom Touchscreen Driver
+ *
+ * Copyright (C) 2020-2026 TouchNetix Ltd.
+ *
+ * Author(s): Mark Satterthwaite <mark.satterthwaite@touchnetix.com>
+ * Pedro Torruella <pedro.torruella@touchnetix.com>
+ * Bart Prescott <bartp@baasheep.co.uk>
+ * Hannah Rossiter <hannah.rossiter@touchnetix.com>
+ * Andrew Thomas <andrew.thomas@touchnetix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __AXIOM_CORE_H
+#define __AXIOM_CORE_H
+
+#include <linux/input.h>
+
+#define AX_POLLING_PERIOD_MS (10)
+
+#define AXIOM_USE_TOUCHSCREEN_INTERFACE // registers the axiom device as a touch screen instead of as a mouse pointer
+#define U46_ENABLE_RAW_FORCE_DATA // enables the raw data for up to 4 force channels to be sent to the input subsystem
+
+#define AXIOM_PAGE_SIZE (256)
+// u31 has 2 pages for usage table entries. (2 * PAGE_SIZE) / U31_BYTES_PER_USAGE = 85
+#define AXIOM_MAX_READ_SIZE (2 * AXIOM_PAGE_SIZE)
+#define SIZE_U31_DEVICE_INFO (12)
+#define SIZE_U31_USAGE_ENTRY (6)
+#define U31_MAX_USAGES (85U)
+#define U41_MAX_TARGETS (10U)
+#define U41_PROX_LEVEL (-128)
+#define AXIOM_HOLDOFF_DELAY_US (40)
+
+enum ax_comms_op_e { AX_WR_OP = 0, AX_RD_OP = 1 };
+
+enum report_ids_e {
+ AX_2DCTS_REPORT_ID = 0x41,
+};
+
+enum axiom_mode_e {
+ AX_RUNTIME_STATE = 0,
+ AX_BOOTLOADER_STATE = 1,
+};
+
+enum usage_type_e {
+ UNKNOWN = 0,
+ OTHER = 1,
+ REPORT = 2,
+ REGISTER = 3,
+ REGISTER_READ_ONLY_ = 4,
+ CDU = 5,
+ CDU_READ_ONLY_ = 6,
+};
+
+struct axiom_device_info {
+ u16 device_id;
+ u8 mode;
+ u8 runtime_fw_rev_minor;
+ u8 runtime_fw_rev_major;
+ u8 device_build_variant;
+ u8 runtime_fw_status;
+ u8 tcp_revision;
+ u8 bootloader_fw_rev_minor;
+ u8 bootloader_fw_rev_major;
+ u8 jedec_id;
+ u8 num_usages;
+ u8 silicon_revision;
+ u8 runtime_fw_rev_patch;
+};
+
+struct u31_usage_entry {
+ u8 usage_num;
+ u8 start_page;
+ u8 num_pages;
+ u8 max_offset;
+ u8 offset_type;
+ u8 uifrevision;
+ u8 usage_type;
+};
+
+struct axiom_cmd_header {
+ u16 target_address;
+ u16 length : 15;
+ u16 rd_wr : 1;
+};
+
+struct axiom_bus_ops {
+ u16 bustype;
+ int (*write)(struct device *dev, u16 addr, u16 length, void *values);
+ int (*read)(struct device *dev, u16 addr, u16 length, void *values);
+};
+
+enum u41_target_state_e {
+ Target_State_Not_Present = 0,
+ Target_State_Prox = 1,
+ Target_State_Hover = 2,
+ Target_State_Touching = 3,
+};
+
+struct axiom {
+ struct device *dev;
+ int irq;
+ struct input_dev *input;
+ const struct axiom_bus_ops *bus_ops;
+ struct axiom_device_info dev_info;
+ struct u31_usage_entry usage_table[U31_MAX_USAGES];
+ u16 max_report_len;
+ u16 u34_address;
+
+ u8 read_buf[AXIOM_MAX_READ_SIZE];
+};
+
+struct u34_report_header {
+ u8 report_length;
+ u8 overflow;
+ u8 report_usage;
+};
+
+struct axiom *axiom_probe(const struct axiom_bus_ops *bus_ops,
+ struct device *dev, int irq);
+
+#endif /* __AXIOM_CORE_H */
diff --git a/drivers/input/touchscreen/axiom_i2c.c b/drivers/input/touchscreen/axiom_i2c.c
new file mode 100644
index 000000000000..66071cc0f7b3
--- /dev/null
+++ b/drivers/input/touchscreen/axiom_i2c.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TouchNetix aXiom Touchscreen Driver
+ *
+ * Copyright (C) 2020-2026 TouchNetix Ltd.
+ *
+ * Author(s): Bart Prescott <bartp@baasheep.co.uk>
+ * Pedro Torruella <pedro.torruella@touchnetix.com>
+ * Mark Satterthwaite <mark.satterthwaite@touchnetix.com>
+ * Hannah Rossiter <hannah.rossiter@touchnetix.com>
+ * Andrew Thomas <andrew.thomas@touchnetix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+// #define DEBUG // Enable debug messages
+
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include "axiom_core.h"
+
+static int axiom_i2c_read_block_data(struct device *dev, u16 addr, u16 length,
+ void *values)
+{
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct axiom_cmd_header cmd_header = { .target_address = addr,
+ .length = length,
+ .rd_wr = AX_RD_OP };
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = sizeof(cmd_header),
+ .buf = (u8 *)&cmd_header,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = values,
+ },
+ };
+
+ error = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (error < 0) {
+ dev_err(dev, "I2C transfer error: %d\n", error);
+ return error;
+ }
+
+ udelay(AXIOM_HOLDOFF_DELAY_US);
+
+ return error != ARRAY_SIZE(msgs) ? -EIO : 0;
+}
+
+static int axiom_i2c_write_block_data(struct device *dev, u16 addr, u16 length,
+ void *values)
+{
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct axiom_cmd_header cmd_header = { .target_address = addr,
+ .length = length,
+ .rd_wr = AX_WR_OP };
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = sizeof(cmd_header),
+ .buf = (u8 *)&cmd_header,
+ },
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = length,
+ .buf = values,
+ },
+ };
+
+ error = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (error < 0) {
+ dev_err(dev, "I2C transfer error: %d\n", error);
+ return error;
+ }
+
+ udelay(AXIOM_HOLDOFF_DELAY_US);
+
+ return error != ARRAY_SIZE(msgs) ? -EIO : 0;
+}
+
+static const struct axiom_bus_ops axiom_i2c_bus_ops = {
+ .bustype = BUS_I2C,
+ .write = axiom_i2c_write_block_data,
+ .read = axiom_i2c_read_block_data,
+};
+
+static int axiom_i2c_probe(struct i2c_client *client)
+{
+ struct axiom *axiom;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "I2C functionality not Supported\n");
+ return -EIO;
+ }
+
+ axiom = axiom_probe(&axiom_i2c_bus_ops, &client->dev, client->irq);
+ if (IS_ERR(axiom))
+ return dev_err_probe(&client->dev, PTR_ERR(axiom),
+ "failed to register input device\n");
+
+ return 0;
+}
+
+static const struct i2c_device_id axiom_i2c_id_table[] = {
+ { "axiom-i2c" },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, axiom_i2c_id_table);
+
+static const struct of_device_id axiom_i2c_dt_ids[] = {
+ {
+ .compatible = "tnx,axiom-i2c",
+ .data = "axiom",
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, axiom_i2c_dt_ids);
+
+static struct i2c_driver axiom_i2c_driver = {
+ .driver = {
+ .name = "axiom_i2c",
+ .of_match_table = of_match_ptr(axiom_i2c_dt_ids),
+ },
+ .id_table = axiom_i2c_id_table,
+ .probe = axiom_i2c_probe,
+};
+
+module_i2c_driver(axiom_i2c_driver);
+
+MODULE_AUTHOR("TouchNetix <support@touchnetix.com>");
+MODULE_DESCRIPTION("aXiom touchscreen I2C bus driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("axiom");
+MODULE_VERSION("1.0.0");
diff --git a/drivers/input/touchscreen/axiom_spi.c b/drivers/input/touchscreen/axiom_spi.c
new file mode 100644
index 000000000000..a67ad3645689
--- /dev/null
+++ b/drivers/input/touchscreen/axiom_spi.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TouchNetix aXiom Touchscreen Driver
+ *
+ * Copyright (C) 2018-2023 TouchNetix Ltd.
+ *
+ * Author(s): Mark Satterthwaite <mark.satterthwaite@touchnetix.com>
+ * Bart Prescott <bartp@baasheep.co.uk>
+ * Hannah Rossiter <hannah.rossiter@touchnetix.com>
+ * Andrew Thomas <andrew.thomas@touchnetix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+// #define DEBUG // Enable debug messages
+
+#include <linux/of.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/input.h>
+#include "axiom_core.h"
+
+#define SPI_PADDING_LEN 32
+
+static int axiom_spi_transfer(struct device *dev, enum ax_comms_op_e op,
+ u16 addr, u16 length, void *values)
+{
+ int ret;
+ struct spi_device *spi = to_spi_device(dev);
+ struct spi_transfer xfr_header;
+ struct spi_transfer xfr_padding;
+ struct spi_transfer xfr_payload;
+ struct spi_message msg;
+ struct axiom_cmd_header cmd_header = { .target_address = addr,
+ .length = length,
+ .rd_wr = op };
+ u8 pad_buf[SPI_PADDING_LEN] = { 0 };
+
+ memset(&xfr_header, 0, sizeof(xfr_header));
+ memset(&xfr_padding, 0, sizeof(xfr_padding));
+ memset(&xfr_payload, 0, sizeof(xfr_payload));
+
+ /* Setup the SPI transfer operations */
+ xfr_header.tx_buf = &cmd_header;
+ xfr_header.len = sizeof(cmd_header);
+
+ xfr_padding.tx_buf = pad_buf;
+ xfr_padding.len = sizeof(pad_buf);
+
+ switch (op) {
+ case AX_WR_OP:
+ xfr_payload.tx_buf = values;
+ break;
+ case AX_RD_OP:
+ xfr_payload.rx_buf = values;
+ break;
+ default:
+ dev_err(dev, "%s: invalid operation: %d\n", __func__, op);
+ return -EINVAL;
+ }
+ xfr_payload.len = length;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfr_header, &msg);
+ spi_message_add_tail(&xfr_padding, &msg);
+ spi_message_add_tail(&xfr_payload, &msg);
+
+ ret = spi_sync(spi, &msg);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to SPI transfer, error: %d\n", ret);
+ return ret;
+ }
+
+ udelay(AXIOM_HOLDOFF_DELAY_US);
+
+ return 0;
+}
+
+static int axiom_spi_read_block_data(struct device *dev, u16 addr, u16 length,
+ void *values)
+{
+ return axiom_spi_transfer(dev, AX_RD_OP, addr, length, values);
+}
+
+static int axiom_spi_write_block_data(struct device *dev, u16 addr, u16 length,
+ void *values)
+{
+ return axiom_spi_transfer(dev, AX_WR_OP, addr, length, values);
+}
+
+static const struct axiom_bus_ops axiom_spi_bus_ops = {
+ .bustype = BUS_SPI,
+ .write = axiom_spi_write_block_data,
+ .read = axiom_spi_read_block_data,
+};
+
+static int axiom_spi_probe(struct spi_device *spi)
+{
+ struct axiom *axiom;
+ int error;
+
+ /* Set up SPI */
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_0;
+ spi->max_speed_hz = 4000000;
+
+ if (spi->irq == 0)
+ dev_err(&spi->dev, "No IRQ specified!\n");
+
+ error = spi_setup(spi);
+ if (error < 0) {
+ dev_err(&spi->dev, "%s: SPI setup error %d\n", __func__, error);
+ return error;
+ }
+ axiom = axiom_probe(&axiom_spi_bus_ops, &spi->dev, spi->irq);
+ if (IS_ERR(axiom))
+ return dev_err_probe(&spi->dev, PTR_ERR(axiom),
+ "failed to register input device\n");
+
+ return 0;
+}
+
+static const struct spi_device_id axiom_spi_id_table[] = {
+ { "axiom-spi" },
+ {},
+};
+MODULE_DEVICE_TABLE(spi, axiom_spi_id_table);
+
+static const struct of_device_id axiom_spi_dt_ids[] = {
+ {
+ .compatible = "tnx,axiom-spi",
+ .data = "axiom",
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, axiom_spi_dt_ids);
+
+static struct spi_driver axiom_spi_driver = {
+ .id_table = axiom_spi_id_table,
+ .driver = {
+ .name = "axiom_spi",
+ .of_match_table = of_match_ptr(axiom_spi_dt_ids),
+ },
+ .probe = axiom_spi_probe,
+};
+
+module_spi_driver(axiom_spi_driver);
+
+MODULE_AUTHOR("TouchNetix <support@touchnetix.com>");
+MODULE_DESCRIPTION("aXiom touchscreen SPI bus driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("axiom");
+MODULE_VERSION("1.0.0");
--
2.43.0
^ permalink raw reply related
* [PATCH 2/4] Input: i8042: Add quirks for MECHREVO Wujie 15X Pro
From: gongqi @ 2026-01-22 12:06 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-input, linux-kernel, gongqi
From: gongqi <550230171@qq.com>
The MECHREVO Wujie 15X Pro requires several i8042 quirks to function correctly. Specifically, NOMUX, RESET_ALWAYS, NOLOOP, and NOPNP are needed to ensure the keyboard and touchpad work reliably.
---
drivers/input/serio/i8042-acpipnpio.h | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h
index 654771275ce87..4cd09560c5bfb 100644
--- a/drivers/input/serio/i8042-acpipnpio.h
+++ b/drivers/input/serio/i8042-acpipnpio.h
@@ -1176,6 +1176,13 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = {
.driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
},
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "WUJIE Series-X5SP4NAG"),
+ },
+ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
+ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
+ },
/*
* A lot of modern Clevo barebones have touchpad and/or keyboard issues
* after suspend fixable with the forcenorestore quirk.
--
2.43.0
^ permalink raw reply related
* Re: [PATCH] arm64: dts: qcom: milos-fairphone-fp6: Add Hall Effect sensor
From: Konrad Dybcio @ 2026-01-22 8:32 UTC (permalink / raw)
To: Dmitry Torokhov, Luca Weiss
Cc: Dmitry Baryshkov, Bjorn Andersson, Konrad Dybcio, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, ~postmarketos/upstreaming,
phone-devel, linux-arm-msm, devicetree, linux-kernel, linux-input
In-Reply-To: <t4yjnuty7tmvs4vqzm4vdid7oowicy7hcnmqmu6nqtit3yru55@zpo6kprnarth>
On 1/21/26 9:06 PM, Dmitry Torokhov wrote:
> On Wed, Jan 21, 2026 at 09:07:44AM +0100, Luca Weiss wrote:
>> On Wed Jan 21, 2026 at 12:05 AM CET, Dmitry Baryshkov wrote:
>>> On Mon, Jan 19, 2026 at 04:52:23PM +0100, Luca Weiss wrote:
>>>> On Mon Jan 19, 2026 at 3:41 PM CET, Konrad Dybcio wrote:
>>>>> On 1/16/26 3:22 PM, Luca Weiss wrote:
>>>>>> Add a node for the Hall Effect sensor, used to detect whether the Flip
>>>>>> Cover is closed or not.
>>>>>>
>>>>>> The sensor is powered through vreg_l10b, so let's put a
>>>>>> regulator-always-on on that to make sure the sensor gets power.
>>>>>
>>>>> Is there anything else on L10B? Can we turn it off if the hall sensor
>>>>> is e.g. user-disabled?
>>>>
>>>> It's the voltage source for pull-up of sensor I2C bus (so
>>>> ADSP-managed?), DVDD for amplifiers and VDD for a most sensors like
>>>> the gyro.
>>>>
>>>> So realistically, it'll probably be (nearly) always on anyways. And I
>>>> don't want to shave another yak by adding vdd support to gpio-keys...
>>>
>>> Why? If it is exactly what happens on the board: the device producing
>>> GPIO events _is_ powered via a vdd. Added Input maintainer / list to cc.
>>
>> Yes, the hall sensor which is connected to the GPIO on the SoC, has an
>> extra VDD input which needs to be on in order for the Hall-effect sensor
>> to be on.
>>
>> See page 133 "HALL" in the center of the page
>> https://www.fairphone.com/wp-content/uploads/2025/08/Fairphone-Gen.-6_-Information-on-how-to-repair-dispose-of-and-recycle-EN-NL-FR-DE.pdf
>>
>> The IC is OCH166AEV4AD where VDD is (as expected) "Power Supply Input":
>> https://www.orient-chip.com/Public/Uploads/uploadfile/files/20231014/1OCH166Adatasheet202203221.pdf
>
> If we add regulator support to gpio-keys do we expect it to be
> per-gpio/per-key? Or we'd recommend splitting into several instances of
> gpio-keys so that there is on set of supplies per gpio-keys device?
gpio-keys {} is very broad and generic, I would imagine it would only
make sense to keep it per-entry, like Luca suggested
Konrad
^ permalink raw reply
* [PATCH v12 08/11] HID: asus: listen to the asus-wmi brightness device instead of creating one
From: Antheas Kapenekakis @ 2026-01-22 7:50 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis, Denis Benato
In-Reply-To: <20260122075044.5070-1-lkml@antheas.dev>
Some ROG laptops expose multiple interfaces for controlling the
keyboard/RGB brightness. This creates a name conflict under
asus::kbd_brightness, where the second device ends up being
named asus::kbd_brightness_1 and they are both broken.
Therefore, register a listener to the asus-wmi brightness device
instead of creating a new one.
Reviewed-by: Luke D. Jones <luke@ljones.dev>
Reviewed-by: Benjamin Tissoires <bentiss@kernel.org>
Reviewed-by: Denis Benato <benato.denis96@gmail.com>
Acked-by: Benjamin Tissoires <bentiss@kernel.org>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/hid/hid-asus.c | 65 +++++++-----------------------------------
1 file changed, 10 insertions(+), 55 deletions(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 6084ec66a134..415629ebee15 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -27,7 +27,6 @@
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/platform_data/x86/asus-wmi.h>
-#include <linux/platform_data/x86/asus-wmi-leds-ids.h>
#include <linux/input/mt.h>
#include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */
#include <linux/power_supply.h>
@@ -103,7 +102,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define TRKID_SGN ((TRKID_MAX + 1) >> 1)
struct asus_kbd_leds {
- struct led_classdev cdev;
+ struct asus_hid_listener listener;
struct hid_device *hdev;
struct work_struct work;
unsigned int brightness;
@@ -495,11 +494,11 @@ static void asus_schedule_work(struct asus_kbd_leds *led)
spin_unlock_irqrestore(&led->lock, flags);
}
-static void asus_kbd_backlight_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
+static void asus_kbd_backlight_set(struct asus_hid_listener *listener,
+ int brightness)
{
- struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds,
- cdev);
+ struct asus_kbd_leds *led = container_of(listener, struct asus_kbd_leds,
+ listener);
unsigned long flags;
spin_lock_irqsave(&led->lock, flags);
@@ -509,20 +508,6 @@ static void asus_kbd_backlight_set(struct led_classdev *led_cdev,
asus_schedule_work(led);
}
-static enum led_brightness asus_kbd_backlight_get(struct led_classdev *led_cdev)
-{
- struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds,
- cdev);
- enum led_brightness brightness;
- unsigned long flags;
-
- spin_lock_irqsave(&led->lock, flags);
- brightness = led->brightness;
- spin_unlock_irqrestore(&led->lock, flags);
-
- return brightness;
-}
-
static void asus_kbd_backlight_work(struct work_struct *work)
{
struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);
@@ -539,34 +524,6 @@ static void asus_kbd_backlight_work(struct work_struct *work)
hid_err(led->hdev, "Asus failed to set keyboard backlight: %d\n", ret);
}
-/* WMI-based keyboard backlight LED control (via asus-wmi driver) takes
- * precedence. We only activate HID-based backlight control when the
- * WMI control is not available.
- */
-static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev)
-{
- struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
- u32 value;
- int ret;
-
- if (!IS_ENABLED(CONFIG_ASUS_WMI))
- return false;
-
- if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD &&
- dmi_check_system(asus_use_hid_led_dmi_ids)) {
- hid_info(hdev, "using HID for asus::kbd_backlight\n");
- return false;
- }
-
- ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS,
- ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value);
- hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value);
- if (ret)
- return false;
-
- return !!(value & ASUS_WMI_DSTS_PRESENCE_BIT);
-}
-
/*
* We don't care about any other part of the string except the version section.
* Example strings: FGA80100.RC72LA.312_T01, FGA80100.RC71LS.318_T01
@@ -706,14 +663,11 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
drvdata->kbd_backlight->removed = false;
drvdata->kbd_backlight->brightness = 0;
drvdata->kbd_backlight->hdev = hdev;
- drvdata->kbd_backlight->cdev.name = "asus::kbd_backlight";
- drvdata->kbd_backlight->cdev.max_brightness = 3;
- drvdata->kbd_backlight->cdev.brightness_set = asus_kbd_backlight_set;
- drvdata->kbd_backlight->cdev.brightness_get = asus_kbd_backlight_get;
+ drvdata->kbd_backlight->listener.brightness_set = asus_kbd_backlight_set;
INIT_WORK(&drvdata->kbd_backlight->work, asus_kbd_backlight_work);
spin_lock_init(&drvdata->kbd_backlight->lock);
- ret = devm_led_classdev_register(&hdev->dev, &drvdata->kbd_backlight->cdev);
+ ret = asus_hid_register_listener(&drvdata->kbd_backlight->listener);
if (ret < 0) {
/* No need to have this still around */
devm_kfree(&hdev->dev, drvdata->kbd_backlight);
@@ -1102,7 +1056,7 @@ static int __maybe_unused asus_resume(struct hid_device *hdev) {
if (drvdata->kbd_backlight) {
const u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4,
- drvdata->kbd_backlight->cdev.brightness };
+ drvdata->kbd_backlight->brightness };
ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
if (ret < 0) {
hid_err(hdev, "Asus failed to set keyboard backlight: %d\n", ret);
@@ -1228,7 +1182,6 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
if (is_vendor && (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT) &&
- !asus_kbd_wmi_led_control_present(hdev) &&
asus_kbd_register_leds(hdev))
hid_warn(hdev, "Failed to initialize backlight.\n");
@@ -1275,6 +1228,8 @@ static void asus_remove(struct hid_device *hdev)
unsigned long flags;
if (drvdata->kbd_backlight) {
+ asus_hid_unregister_listener(&drvdata->kbd_backlight->listener);
+
spin_lock_irqsave(&drvdata->kbd_backlight->lock, flags);
drvdata->kbd_backlight->removed = true;
spin_unlock_irqrestore(&drvdata->kbd_backlight->lock, flags);
--
2.52.0
^ permalink raw reply related
* [PATCH v12 11/11] HID: asus: add support for the asus-wmi brightness handler
From: Antheas Kapenekakis @ 2026-01-22 7:50 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis, Denis Benato
In-Reply-To: <20260122075044.5070-1-lkml@antheas.dev>
If the asus-wmi brightness handler is available, send the
keyboard brightness events to it instead of passing them
to userspace. If it is not, fall back to sending them to it.
Reviewed-by: Luke D. Jones <luke@ljones.dev>
Tested-by: Luke D. Jones <luke@ljones.dev>
Reviewed-by: Denis Benato <benato.denis96@gmail.com>
Acked-by: Benjamin Tissoires <bentiss@kernel.org>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/hid/hid-asus.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 415629ebee15..f5c8df20b88b 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -324,6 +324,17 @@ static int asus_event(struct hid_device *hdev, struct hid_field *field,
usage->hid & HID_USAGE);
}
+ if (usage->type == EV_KEY && value) {
+ switch (usage->code) {
+ case KEY_KBDILLUMUP:
+ return !asus_hid_event(ASUS_EV_BRTUP);
+ case KEY_KBDILLUMDOWN:
+ return !asus_hid_event(ASUS_EV_BRTDOWN);
+ case KEY_KBDILLUMTOGGLE:
+ return !asus_hid_event(ASUS_EV_BRTTOGGLE);
+ }
+ }
+
return 0;
}
--
2.52.0
^ permalink raw reply related
* [PATCH v12 09/11] platform/x86: asus-wmi: remove unused keyboard backlight quirk
From: Antheas Kapenekakis @ 2026-01-22 7:50 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis, Denis Benato
In-Reply-To: <20260122075044.5070-1-lkml@antheas.dev>
The quirk for selecting whether keyboard backlight should be controlled
by HID or WMI is not needed anymore, so remove the file containing it.
Reviewed-by: Denis Benato <benato.denis96@gmail.com>
Acked-by: Benjamin Tissoires <bentiss@kernel.org>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
.../platform_data/x86/asus-wmi-leds-ids.h | 50 -------------------
1 file changed, 50 deletions(-)
delete mode 100644 include/linux/platform_data/x86/asus-wmi-leds-ids.h
diff --git a/include/linux/platform_data/x86/asus-wmi-leds-ids.h b/include/linux/platform_data/x86/asus-wmi-leds-ids.h
deleted file mode 100644
index 034a039c4e37..000000000000
--- a/include/linux/platform_data/x86/asus-wmi-leds-ids.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H
-#define __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H
-
-#include <linux/dmi.h>
-#include <linux/types.h>
-
-/* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */
-#if IS_REACHABLE(CONFIG_ASUS_WMI) || IS_REACHABLE(CONFIG_HID_ASUS)
-static const struct dmi_system_id asus_use_hid_led_dmi_ids[] = {
- {
- .matches = {
- DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Zephyrus"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Strix"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Flow"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_PRODUCT_FAMILY, "ProArt P16"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_NAME, "GA403U"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_NAME, "GU605M"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_NAME, "RC71L"),
- },
- },
- { },
-};
-#endif
-
-#endif /* __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H */
--
2.52.0
^ permalink raw reply related
* [PATCH v12 10/11] platform/x86: asus-wmi: add keyboard brightness event handler
From: Antheas Kapenekakis @ 2026-01-22 7:50 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis, Denis Benato
In-Reply-To: <20260122075044.5070-1-lkml@antheas.dev>
The keyboard brightness control of Asus WMI keyboards is handled in
kernel, which leads to the shortcut going from brightness 0, to 1,
to 2, and 3.
However, for HID keyboards it is exposed as a key and handled by the
user's desktop environment. For the toggle button, this means that
brightness control becomes on/off. In addition, in the absence of a
DE, the keyboard brightness does not work.
Therefore, expose an event handler for the keyboard brightness control
which can then be used by hid-asus. Since this handler is called from
an interrupt context, defer the actual work to a workqueue.
In the process, introduce ASUS_EV_MAX_BRIGHTNESS to hold the constant
for maximum brightness since it is shared between hid-asus/asus-wmi.
Reviewed-by: Luke D. Jones <luke@ljones.dev>
Tested-by: Luke D. Jones <luke@ljones.dev>
Acked-by: Benjamin Tissoires <bentiss@kernel.org>
Reviewed-by: Denis Benato <benato.denis96@gmail.com>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/platform/x86/asus-wmi.c | 46 +++++++++++++++++++---
include/linux/platform_data/x86/asus-wmi.h | 13 ++++++
2 files changed, 54 insertions(+), 5 deletions(-)
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index c45846be3f99..4cbd7b9806dc 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -1719,6 +1719,44 @@ static void kbd_led_update_all(struct work_struct *work)
}
}
+/*
+ * This function is called from hid-asus to inform asus-wmi of brightness
+ * changes initiated by the keyboard backlight keys.
+ */
+int asus_hid_event(enum asus_hid_event event)
+{
+ struct asus_wmi *asus;
+ int brightness;
+
+ guard(spinlock_irqsave)(&asus_ref.lock);
+ asus = asus_ref.asus;
+ if (!asus || !asus->kbd_led_registered)
+ return -EBUSY;
+
+ brightness = asus->kbd_led_wk;
+
+ switch (event) {
+ case ASUS_EV_BRTUP:
+ brightness += 1;
+ break;
+ case ASUS_EV_BRTDOWN:
+ brightness -= 1;
+ break;
+ case ASUS_EV_BRTTOGGLE:
+ if (brightness >= ASUS_EV_MAX_BRIGHTNESS)
+ brightness = 0;
+ else
+ brightness += 1;
+ break;
+ }
+
+ asus->kbd_led_wk = clamp_val(brightness, 0, ASUS_EV_MAX_BRIGHTNESS);
+ asus->kbd_led_notify = true;
+ queue_work(asus->led_workqueue, &asus->kbd_led_work);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(asus_hid_event);
+
/*
* These functions actually update the LED's, and are called from a
* workqueue. By doing this as separate work rather than when the LED
@@ -1801,13 +1839,11 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value)
{
struct asus_hid_listener *listener;
struct asus_wmi *asus;
- int max_level;
asus = container_of(led_cdev, struct asus_wmi, kbd_led);
- max_level = asus->kbd_led.max_brightness;
scoped_guard(spinlock_irqsave, &asus_ref.lock)
- asus->kbd_led_wk = clamp_val(value, 0, max_level);
+ asus->kbd_led_wk = clamp_val(value, 0, ASUS_EV_MAX_BRIGHTNESS);
if (asus->kbd_led_avail)
kbd_led_update(asus);
@@ -2011,7 +2047,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
asus->kbd_led.brightness_set_blocking = kbd_led_set;
asus->kbd_led.brightness_get = kbd_led_get;
- asus->kbd_led.max_brightness = 3;
+ asus->kbd_led.max_brightness = ASUS_EV_MAX_BRIGHTNESS;
asus->kbd_led_avail = !kbd_led_read(asus, &led_val, NULL);
INIT_WORK(&asus->kbd_led_work, kbd_led_update_all);
@@ -4530,7 +4566,7 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
return;
}
if (code == NOTIFY_KBD_BRTTOGGLE) {
- if (led_value >= asus->kbd_led.max_brightness)
+ if (led_value >= ASUS_EV_MAX_BRIGHTNESS)
kbd_led_set_by_kbd(asus, 0);
else
kbd_led_set_by_kbd(asus, led_value + 1);
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
index d347cffd05d5..7b872b5d0960 100644
--- a/include/linux/platform_data/x86/asus-wmi.h
+++ b/include/linux/platform_data/x86/asus-wmi.h
@@ -178,6 +178,14 @@ struct asus_hid_listener {
void (*brightness_set)(struct asus_hid_listener *listener, int brightness);
};
+enum asus_hid_event {
+ ASUS_EV_BRTUP,
+ ASUS_EV_BRTDOWN,
+ ASUS_EV_BRTTOGGLE,
+};
+
+#define ASUS_EV_MAX_BRIGHTNESS 3
+
#if IS_REACHABLE(CONFIG_ASUS_WMI)
void set_ally_mcu_hack(enum asus_ally_mcu_hack status);
void set_ally_mcu_powersave(bool enabled);
@@ -186,6 +194,7 @@ int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval);
int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval);
int asus_hid_register_listener(struct asus_hid_listener *cdev);
void asus_hid_unregister_listener(struct asus_hid_listener *cdev);
+int asus_hid_event(enum asus_hid_event event);
#else
static inline void set_ally_mcu_hack(enum asus_ally_mcu_hack status)
{
@@ -213,6 +222,10 @@ static inline int asus_hid_register_listener(struct asus_hid_listener *bdev)
static inline void asus_hid_unregister_listener(struct asus_hid_listener *bdev)
{
}
+static inline int asus_hid_event(enum asus_hid_event event)
+{
+ return -ENODEV;
+}
#endif
#endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */
--
2.52.0
^ permalink raw reply related
* [PATCH v12 06/11] HID: asus: early return for ROG devices
From: Antheas Kapenekakis @ 2026-01-22 7:50 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis, Denis Benato
In-Reply-To: <20260122075044.5070-1-lkml@antheas.dev>
Some ROG devices have a new dynamic backlight interface for control by
Windows. This interface does not create an ->input device, causing the
kernel to print an error message and to eject it. In addition, ROG
devices have proper HID names in their descriptors so renaming them is
not necessary.
Therefore, if a device is identified as ROG, early return from probe to
skip renaming and ->input checks.
Acked-by: Benjamin Tissoires <bentiss@kernel.org>
Reviewed-by: Denis Benato <benato.denis96@gmail.com>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/hid/hid-asus.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index dc8795b59cc2..6084ec66a134 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -1232,6 +1232,13 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
asus_kbd_register_leds(hdev))
hid_warn(hdev, "Failed to initialize backlight.\n");
+ /*
+ * For ROG keyboards, skip rename for consistency and ->input check as
+ * some devices do not have inputs.
+ */
+ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD)
+ return 0;
+
/*
* Check that input registration succeeded. Checking that
* HID_CLAIMED_INPUT is set prevents a UAF when all input devices
--
2.52.0
^ permalink raw reply related
* [PATCH v12 05/11] HID: asus: move vendor initialization to probe
From: Antheas Kapenekakis @ 2026-01-22 7:50 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis, Denis Benato
In-Reply-To: <20260122075044.5070-1-lkml@antheas.dev>
ROG NKEY devices have multiple HID endpoints, around 3-4. One of those
endpoints has a usage page of 0xff31, and is the one that emits keyboard
shortcuts and controls RGB/backlight. Currently, this driver places
the usage page check under asus_input_mapping and then inits backlight
in asus_input_configured which is unnecessarily complicated and prevents
probe from performing customizations on the vendor endpoint.
Simplify the logic by introducing an is_vendor variable into probe that
checks for usage page 0xff31. Then, use this variable to move backlight
initialization into probe instead of asus_input_configured, and remove
the backlight check from asus_input_mapping.
Acked-by: Benjamin Tissoires <bentiss@kernel.org>
Reviewed-by: Denis Benato <benato.denis96@gmail.com>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/hid/hid-asus.c | 35 ++++++++++++++++++-----------------
1 file changed, 18 insertions(+), 17 deletions(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index cbf5bab9113b..dc8795b59cc2 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -48,6 +48,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define T100CHI_MOUSE_REPORT_ID 0x06
#define FEATURE_REPORT_ID 0x0d
#define INPUT_REPORT_ID 0x5d
+#define HID_USAGE_PAGE_VENDOR 0xff310000
#define FEATURE_KBD_REPORT_ID 0x5a
#define FEATURE_KBD_REPORT_SIZE 64
#define FEATURE_KBD_LED_REPORT_ID1 0x5d
@@ -127,7 +128,6 @@ struct asus_drvdata {
struct input_dev *tp_kbd_input;
struct asus_kbd_leds *kbd_backlight;
const struct asus_touchpad_info *tp;
- bool enable_backlight;
struct power_supply *battery;
struct power_supply_desc battery_desc;
int battery_capacity;
@@ -318,7 +318,7 @@ static int asus_e1239t_event(struct asus_drvdata *drvdat, u8 *data, int size)
static int asus_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
- if ((usage->hid & HID_USAGE_PAGE) == 0xff310000 &&
+ if ((usage->hid & HID_USAGE_PAGE) == HID_USAGE_PAGE_VENDOR &&
(usage->hid & HID_USAGE) != 0x00 &&
(usage->hid & HID_USAGE) != 0xff && !usage->type) {
hid_warn(hdev, "Unmapped Asus vendor usagepage code 0x%02x\n",
@@ -938,11 +938,6 @@ static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
drvdata->input = input;
- if (drvdata->enable_backlight &&
- !asus_kbd_wmi_led_control_present(hdev) &&
- asus_kbd_register_leds(hdev))
- hid_warn(hdev, "Failed to initialize backlight.\n");
-
return 0;
}
@@ -1015,15 +1010,6 @@ static int asus_input_mapping(struct hid_device *hdev,
return -1;
}
- /*
- * Check and enable backlight only on devices with UsagePage ==
- * 0xff31 to avoid initializing the keyboard firmware multiple
- * times on devices with multiple HID descriptors but same
- * PID/VID.
- */
- if (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT)
- drvdata->enable_backlight = true;
-
set_bit(EV_REP, hi->input->evbit);
return 1;
}
@@ -1140,8 +1126,11 @@ static int __maybe_unused asus_reset_resume(struct hid_device *hdev)
static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
- int ret;
+ struct hid_report_enum *rep_enum;
struct asus_drvdata *drvdata;
+ struct hid_report *rep;
+ bool is_vendor = false;
+ int ret;
drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
if (drvdata == NULL) {
@@ -1225,12 +1214,24 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
return ret;
}
+ /* Check for vendor for RGB init and handle generic devices properly. */
+ rep_enum = &hdev->report_enum[HID_INPUT_REPORT];
+ list_for_each_entry(rep, &rep_enum->report_list, list) {
+ if ((rep->application & HID_USAGE_PAGE) == HID_USAGE_PAGE_VENDOR)
+ is_vendor = true;
+ }
+
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret) {
hid_err(hdev, "Asus hw start failed: %d\n", ret);
return ret;
}
+ if (is_vendor && (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT) &&
+ !asus_kbd_wmi_led_control_present(hdev) &&
+ asus_kbd_register_leds(hdev))
+ hid_warn(hdev, "Failed to initialize backlight.\n");
+
/*
* Check that input registration succeeded. Checking that
* HID_CLAIMED_INPUT is set prevents a UAF when all input devices
--
2.52.0
^ permalink raw reply related
* [PATCH v12 04/11] HID: asus: fortify keyboard handshake
From: Antheas Kapenekakis @ 2026-01-22 7:50 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis, Denis Benato
In-Reply-To: <20260122075044.5070-1-lkml@antheas.dev>
Handshaking with an Asus device involves sending it a feature report
with the string "ASUS Tech.Inc." and then reading it back to verify the
handshake was successful, under the feature ID the interaction will
take place.
Currently, the driver only does the first part. Add the readback to
verify the handshake was successful. As this could cause breakages,
allow the verification to fail with a dmesg error until we verify
all devices work with it (they seem to).
Since the response is more than 16 bytes, increase the buffer size
to 64 as well to avoid overflow errors. In addition, add the report
ID to prints, to help identify failed handshakes.
Reviewed-by: Benjamin Tissoires <bentiss@kernel.org>
Reviewed-by: Denis Benato <benato.denis96@gmail.com>
Acked-by: Benjamin Tissoires <bentiss@kernel.org>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/hid/hid-asus.c | 34 ++++++++++++++++++++++++++++++----
1 file changed, 30 insertions(+), 4 deletions(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 2ff2170dc30a..cbf5bab9113b 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -49,7 +49,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define FEATURE_REPORT_ID 0x0d
#define INPUT_REPORT_ID 0x5d
#define FEATURE_KBD_REPORT_ID 0x5a
-#define FEATURE_KBD_REPORT_SIZE 16
+#define FEATURE_KBD_REPORT_SIZE 64
#define FEATURE_KBD_LED_REPORT_ID1 0x5d
#define FEATURE_KBD_LED_REPORT_ID2 0x5e
@@ -395,15 +395,41 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu
static int asus_kbd_init(struct hid_device *hdev, u8 report_id)
{
+ /*
+ * The handshake is first sent as a set_report, then retrieved
+ * from a get_report. They should be equal.
+ */
const u8 buf[] = { report_id, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54,
0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
int ret;
ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
- if (ret < 0)
- hid_err(hdev, "Asus failed to send init command: %d\n", ret);
+ if (ret < 0) {
+ hid_err(hdev, "Asus handshake %02x failed to send: %d\n",
+ report_id, ret);
+ return ret;
+ }
- return ret;
+ u8 *readbuf __free(kfree) = kzalloc(FEATURE_KBD_REPORT_SIZE, GFP_KERNEL);
+ if (!readbuf)
+ return -ENOMEM;
+
+ ret = hid_hw_raw_request(hdev, report_id, readbuf,
+ FEATURE_KBD_REPORT_SIZE, HID_FEATURE_REPORT,
+ HID_REQ_GET_REPORT);
+ if (ret < 0) {
+ hid_warn(hdev, "Asus handshake %02x failed to receive ack: %d\n",
+ report_id, ret);
+ } else if (memcmp(readbuf, buf, sizeof(buf)) != 0) {
+ hid_warn(hdev, "Asus handshake %02x returned invalid response: %*ph\n",
+ report_id, FEATURE_KBD_REPORT_SIZE, readbuf);
+ }
+
+ /*
+ * Do not return error if handshake is wrong until this is
+ * verified to work for all devices.
+ */
+ return 0;
}
static int asus_kbd_get_functions(struct hid_device *hdev,
--
2.52.0
^ permalink raw reply related
* [PATCH v12 07/11] platform/x86: asus-wmi: Add support for multiple kbd led handlers
From: Antheas Kapenekakis @ 2026-01-22 7:50 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis
In-Reply-To: <20260122075044.5070-1-lkml@antheas.dev>
Some devices, such as the Z13 have multiple Aura devices connected
to them by USB. In addition, they might have a WMI interface for
RGB. In Windows, Armoury Crate exposes a unified brightness slider
for all of them, with 3 brightness levels.
Therefore, to be synergistic in Linux, and support existing tooling
such as UPower, allow adding listeners to the RGB device of the WMI
interface. If WMI does not exist, lazy initialize the interface.
Since hid-asus and asus-wmi can both interact with the led objects
including from an atomic context, protect the brightness access with a
spinlock and update the values from a workqueue. Use this workqueue to
also process WMI keyboard events, so they are handled asynchronously.
Acked-by: Benjamin Tissoires <bentiss@kernel.org>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/platform/x86/asus-wmi.c | 183 ++++++++++++++++++---
include/linux/platform_data/x86/asus-wmi.h | 15 ++
2 files changed, 173 insertions(+), 25 deletions(-)
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 4aec7ec69250..c45846be3f99 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -31,13 +31,13 @@
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include <linux/platform_data/x86/asus-wmi.h>
-#include <linux/platform_data/x86/asus-wmi-leds-ids.h>
#include <linux/platform_device.h>
#include <linux/platform_profile.h>
#include <linux/power_supply.h>
#include <linux/rfkill.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/units.h>
@@ -256,6 +256,9 @@ struct asus_wmi {
int tpd_led_wk;
struct led_classdev kbd_led;
int kbd_led_wk;
+ bool kbd_led_notify;
+ bool kbd_led_avail;
+ bool kbd_led_registered;
struct led_classdev lightbar_led;
int lightbar_led_wk;
struct led_classdev micmute_led;
@@ -264,6 +267,7 @@ struct asus_wmi {
struct work_struct tpd_led_work;
struct work_struct wlan_led_work;
struct work_struct lightbar_led_work;
+ struct work_struct kbd_led_work;
struct asus_rfkill wlan;
struct asus_rfkill bluetooth;
@@ -1615,6 +1619,106 @@ static void asus_wmi_battery_exit(struct asus_wmi *asus)
/* LEDs ***********************************************************************/
+struct asus_hid_ref {
+ struct list_head listeners;
+ struct asus_wmi *asus;
+ /* Protects concurrent access from hid-asus and asus-wmi to leds */
+ spinlock_t lock;
+};
+
+static struct asus_hid_ref asus_ref = {
+ .listeners = LIST_HEAD_INIT(asus_ref.listeners),
+ .asus = NULL,
+ /*
+ * Protects .asus, .asus.kbd_led_{wk,notify}, and .listener refs. Other
+ * asus variables are read-only after .asus is set.
+ *
+ * The led cdev device is not protected because it calls backlight_get
+ * during initialization, which would result in a nested lock attempt.
+ *
+ * The led cdev is safe to access without a lock because if
+ * kbd_led_avail is true it is initialized before .asus is set and never
+ * changed until .asus is dropped. If kbd_led_avail is false, the led
+ * cdev is registered by the workqueue, which is single-threaded and
+ * cancelled before asus-wmi would access the led cdev to unregister it.
+ *
+ * A spinlock is used, because the protected variables can be accessed
+ * from an IRQ context from asus-hid.
+ */
+ .lock = __SPIN_LOCK_UNLOCKED(asus_ref.lock),
+};
+
+/*
+ * Allows registering hid-asus listeners that want to be notified of
+ * keyboard backlight changes.
+ */
+int asus_hid_register_listener(struct asus_hid_listener *bdev)
+{
+ struct asus_wmi *asus;
+
+ guard(spinlock_irqsave)(&asus_ref.lock);
+ list_add_tail(&bdev->list, &asus_ref.listeners);
+ asus = asus_ref.asus;
+ if (asus)
+ queue_work(asus->led_workqueue, &asus->kbd_led_work);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(asus_hid_register_listener);
+
+/*
+ * Allows unregistering hid-asus listeners that were added with
+ * asus_hid_register_listener().
+ */
+void asus_hid_unregister_listener(struct asus_hid_listener *bdev)
+{
+ guard(spinlock_irqsave)(&asus_ref.lock);
+ list_del(&bdev->list);
+}
+EXPORT_SYMBOL_GPL(asus_hid_unregister_listener);
+
+static void do_kbd_led_set(struct led_classdev *led_cdev, int value);
+
+static void kbd_led_update_all(struct work_struct *work)
+{
+ struct asus_wmi *asus;
+ bool registered, notify;
+ int ret, value;
+
+ asus = container_of(work, struct asus_wmi, kbd_led_work);
+
+ scoped_guard(spinlock_irqsave, &asus_ref.lock) {
+ registered = asus->kbd_led_registered;
+ value = asus->kbd_led_wk;
+ notify = asus->kbd_led_notify;
+ }
+
+ if (!registered) {
+ /*
+ * This workqueue runs under asus-wmi, which means probe has
+ * completed and asus-wmi will keep running until it finishes.
+ * Therefore, we can safely register the LED without holding
+ * a spinlock.
+ */
+ ret = devm_led_classdev_register(&asus->platform_device->dev,
+ &asus->kbd_led);
+ if (!ret) {
+ scoped_guard(spinlock_irqsave, &asus_ref.lock)
+ asus->kbd_led_registered = true;
+ } else {
+ pr_warn("Failed to register keyboard backlight LED: %d\n", ret);
+ return;
+ }
+ }
+
+ if (value >= 0)
+ do_kbd_led_set(&asus->kbd_led, value);
+ if (notify) {
+ scoped_guard(spinlock_irqsave, &asus_ref.lock)
+ asus->kbd_led_notify = false;
+ led_classdev_notify_brightness_hw_changed(&asus->kbd_led, value);
+ }
+}
+
/*
* These functions actually update the LED's, and are called from a
* workqueue. By doing this as separate work rather than when the LED
@@ -1661,7 +1765,8 @@ static void kbd_led_update(struct asus_wmi *asus)
{
int ctrl_param = 0;
- ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);
+ scoped_guard(spinlock_irqsave, &asus_ref.lock)
+ ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);
asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL);
}
@@ -1694,14 +1799,23 @@ static int kbd_led_read(struct asus_wmi *asus, int *level, int *env)
static void do_kbd_led_set(struct led_classdev *led_cdev, int value)
{
+ struct asus_hid_listener *listener;
struct asus_wmi *asus;
int max_level;
asus = container_of(led_cdev, struct asus_wmi, kbd_led);
max_level = asus->kbd_led.max_brightness;
- asus->kbd_led_wk = clamp_val(value, 0, max_level);
- kbd_led_update(asus);
+ scoped_guard(spinlock_irqsave, &asus_ref.lock)
+ asus->kbd_led_wk = clamp_val(value, 0, max_level);
+
+ if (asus->kbd_led_avail)
+ kbd_led_update(asus);
+
+ scoped_guard(spinlock_irqsave, &asus_ref.lock) {
+ list_for_each_entry(listener, &asus_ref.listeners, list)
+ listener->brightness_set(listener, asus->kbd_led_wk);
+ }
}
static int kbd_led_set(struct led_classdev *led_cdev, enum led_brightness value)
@@ -1716,10 +1830,11 @@ static int kbd_led_set(struct led_classdev *led_cdev, enum led_brightness value)
static void kbd_led_set_by_kbd(struct asus_wmi *asus, enum led_brightness value)
{
- struct led_classdev *led_cdev = &asus->kbd_led;
-
- do_kbd_led_set(led_cdev, value);
- led_classdev_notify_brightness_hw_changed(led_cdev, asus->kbd_led_wk);
+ scoped_guard(spinlock_irqsave, &asus_ref.lock) {
+ asus->kbd_led_wk = value;
+ asus->kbd_led_notify = true;
+ }
+ queue_work(asus->led_workqueue, &asus->kbd_led_work);
}
static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
@@ -1729,10 +1844,18 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
asus = container_of(led_cdev, struct asus_wmi, kbd_led);
+ scoped_guard(spinlock_irqsave, &asus_ref.lock) {
+ if (!asus->kbd_led_avail)
+ return asus->kbd_led_wk;
+ }
+
retval = kbd_led_read(asus, &value, NULL);
if (retval < 0)
return retval;
+ scoped_guard(spinlock_irqsave, &asus_ref.lock)
+ asus->kbd_led_wk = value;
+
return value;
}
@@ -1844,7 +1967,9 @@ static int camera_led_set(struct led_classdev *led_cdev,
static void asus_wmi_led_exit(struct asus_wmi *asus)
{
- led_classdev_unregister(&asus->kbd_led);
+ scoped_guard(spinlock_irqsave, &asus_ref.lock)
+ asus_ref.asus = NULL;
+
led_classdev_unregister(&asus->tpd_led);
led_classdev_unregister(&asus->wlan_led);
led_classdev_unregister(&asus->lightbar_led);
@@ -1882,22 +2007,26 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
goto error;
}
- if (!kbd_led_read(asus, &led_val, NULL) && !dmi_check_system(asus_use_hid_led_dmi_ids)) {
- pr_info("using asus-wmi for asus::kbd_backlight\n");
- asus->kbd_led_wk = led_val;
- asus->kbd_led.name = "asus::kbd_backlight";
- asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
- asus->kbd_led.brightness_set_blocking = kbd_led_set;
- asus->kbd_led.brightness_get = kbd_led_get;
- asus->kbd_led.max_brightness = 3;
+ asus->kbd_led.name = "asus::kbd_backlight";
+ asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
+ asus->kbd_led.brightness_set_blocking = kbd_led_set;
+ asus->kbd_led.brightness_get = kbd_led_get;
+ asus->kbd_led.max_brightness = 3;
+ asus->kbd_led_avail = !kbd_led_read(asus, &led_val, NULL);
+ INIT_WORK(&asus->kbd_led_work, kbd_led_update_all);
+ if (asus->kbd_led_avail) {
+ asus->kbd_led_wk = led_val;
if (num_rgb_groups != 0)
asus->kbd_led.groups = kbd_rgb_mode_groups;
+ } else {
+ asus->kbd_led_wk = -1;
+ }
- rv = led_classdev_register(&asus->platform_device->dev,
- &asus->kbd_led);
- if (rv)
- goto error;
+ scoped_guard(spinlock_irqsave, &asus_ref.lock) {
+ asus_ref.asus = asus;
+ if (asus->kbd_led_avail || !list_empty(&asus_ref.listeners))
+ queue_work(asus->led_workqueue, &asus->kbd_led_work);
}
if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_WIRELESS_LED)
@@ -4372,6 +4501,7 @@ static int asus_wmi_get_event_code(union acpi_object *obj)
static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
{
+ enum led_brightness led_value;
unsigned int key_value = 1;
bool autorelease = 1;
@@ -4388,19 +4518,22 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
return;
}
+ scoped_guard(spinlock_irqsave, &asus_ref.lock)
+ led_value = asus->kbd_led_wk;
+
if (code == NOTIFY_KBD_BRTUP) {
- kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
+ kbd_led_set_by_kbd(asus, led_value + 1);
return;
}
if (code == NOTIFY_KBD_BRTDWN) {
- kbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1);
+ kbd_led_set_by_kbd(asus, led_value - 1);
return;
}
if (code == NOTIFY_KBD_BRTTOGGLE) {
- if (asus->kbd_led_wk == asus->kbd_led.max_brightness)
+ if (led_value >= asus->kbd_led.max_brightness)
kbd_led_set_by_kbd(asus, 0);
else
- kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
+ kbd_led_set_by_kbd(asus, led_value + 1);
return;
}
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
index 419491d4abca..d347cffd05d5 100644
--- a/include/linux/platform_data/x86/asus-wmi.h
+++ b/include/linux/platform_data/x86/asus-wmi.h
@@ -172,12 +172,20 @@ enum asus_ally_mcu_hack {
ASUS_WMI_ALLY_MCU_HACK_DISABLED,
};
+/* Used to notify hid-asus when asus-wmi changes keyboard backlight */
+struct asus_hid_listener {
+ struct list_head list;
+ void (*brightness_set)(struct asus_hid_listener *listener, int brightness);
+};
+
#if IS_REACHABLE(CONFIG_ASUS_WMI)
void set_ally_mcu_hack(enum asus_ally_mcu_hack status);
void set_ally_mcu_powersave(bool enabled);
int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval);
int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval);
int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval);
+int asus_hid_register_listener(struct asus_hid_listener *cdev);
+void asus_hid_unregister_listener(struct asus_hid_listener *cdev);
#else
static inline void set_ally_mcu_hack(enum asus_ally_mcu_hack status)
{
@@ -198,6 +206,13 @@ static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
{
return -ENODEV;
}
+static inline int asus_hid_register_listener(struct asus_hid_listener *bdev)
+{
+ return -ENODEV;
+}
+static inline void asus_hid_unregister_listener(struct asus_hid_listener *bdev)
+{
+}
#endif
#endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */
--
2.52.0
^ permalink raw reply related
* [PATCH v12 03/11] HID: asus: use same report_id in response
From: Antheas Kapenekakis @ 2026-01-22 7:50 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis, Denis Benato
In-Reply-To: <20260122075044.5070-1-lkml@antheas.dev>
Currently, asus_kbd_get_functions prods the device using feature
report report_id, but then is hardcoded to check the response through
FEATURE_KBD_REPORT_ID. This only works if report_id is that value
(currently true). So, use report_id in the response as well to
maintain functionality if that value changes in the future.
Reviewed-by: Denis Benato <benato.denis96@gmail.com>
Acked-by: Benjamin Tissoires <bentiss@kernel.org>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/hid/hid-asus.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 92904b5a700c..2ff2170dc30a 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -424,7 +424,7 @@ static int asus_kbd_get_functions(struct hid_device *hdev,
if (!readbuf)
return -ENOMEM;
- ret = hid_hw_raw_request(hdev, FEATURE_KBD_REPORT_ID, readbuf,
+ ret = hid_hw_raw_request(hdev, report_id, readbuf,
FEATURE_KBD_REPORT_SIZE, HID_FEATURE_REPORT,
HID_REQ_GET_REPORT);
if (ret < 0) {
--
2.52.0
^ permalink raw reply related
* [PATCH v12 00/11] HID: asus: Fix ASUS ROG Laptop's Keyboard backlight handling
From: Antheas Kapenekakis @ 2026-01-22 7:50 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis
This is a two part series which does the following:
- Clean-up init sequence
- Unify backlight handling to happen under asus-wmi so that all Aura
devices have synced brightness controls and the backlight button works
properly when it is on a USB laptop keyboard instead of one w/ WMI.
For more context, see cover letter of V1. Since V5, I removed some patches
to make this easier to merge.
---
V11: https://lore.kernel.org/lkml/20260116133150.5606-1-lkml@antheas.dev/
V10: https://lore.kernel.org/lkml/20251122110032.4274-1-lkml@antheas.dev/
V9: https://lore.kernel.org/all/20251120094617.11672-1-lkml@antheas.dev/
V8: https://lore.kernel.org/all/20251101104712.8011-1-lkml@antheas.dev/
V7: https://lore.kernel.org/all/20251018101759.4089-1-lkml@antheas.dev/
V6: https://lore.kernel.org/all/20251013201535.6737-1-lkml@antheas.dev/
V5: https://lore.kernel.org/all/20250325184601.10990-1-lkml@antheas.dev/
V4: https://lore.kernel.org/lkml/20250324210151.6042-1-lkml@antheas.dev/
V3: https://lore.kernel.org/lkml/20250322102804.418000-1-lkml@antheas.dev/
V2: https://lore.kernel.org/all/20250320220924.5023-1-lkml@antheas.dev/
V1: https://lore.kernel.org/all/20250319191320.10092-1-lkml@antheas.dev/
Changes since V11:
- Rebase to review-ilpo-next
- Legacy to ID1/ID2 and reword "HID: asus: initialize additional endpoints
only for certain devices"
- in "platform/x86: asus-wmi: Add support for multiple kbd led handlers":
== -> >=
Changes since V10:
- Fix conflict with ccb61a328321 ("platform/x86: asus-wmi: use
brightness_set_blocking() for kbd led")
- We are on v6.19-rc5 mainline again
- In "HID: asus: initialize additional endpoints only for legacy devices"
additionally remove the if checks to avoid bailing if the device does not
support the init. Those inits are purely done as a precaution, we do not
care if they fail. Dismissed Denis' review as the patch diverged
- In "HID: asus: fortify keyboard handshake" do not bail if set report fails,
as noted by Benjamin, but only print a warning. Also, start printing the
report id in errors to identify them and use hid_warn for both non-fatal
errors. The changes are minor so the reviews are kept.
Changes since V9:
- No functional changes
- Rebase to review-ilpo-next
- Fix armoury series conflict by removing the file asus-wmi-leds-ids on
"remove unused keyboard backlight quirk" + imports
Dismiss Luke's review as this patch diverged
- Reword paragraph in "Add support for multiple kbd led handlers" to be
more verbose
- Use kfree in fortify patch
- Fix minor style quirks from --nonstict checkpatch run
Changes since V8:
- No functional changes
- Move legacy init patch to second, modify first patch so that their
diff is minimized
- Split "prevent binding to all HID devices on ROG" into two patches:
- moving backlight initialization into probe
- early exit to skip ->init check and rename
- Remove skipping vendor fixups for non-vendor devices. It is not possible
to read usages before the report fixups are applied, so it did not work
- In that patch, reword a comment to be single line and make is_vendor a bool
- Dismiss Luke's tags from "Add support for multiple kbd led handlers" as it
has drifted too far since he reviewed/tested it.
Changes since V7:
- Readd legacy init quirk for Dennis
- Remove HID_QUIRK_INPUT_PER_APP as a courtesy to asusctl
- Fix warning due to enum_backlight receiving negative values
Changes since V6:
- Split initialization refactor into three patches, update commit text
to be clearer in what it does
- Replace spinlock accesses with guard and scoped guard in all patches
- Add missing includes mentioned by Ilpo
- Reflow, tweak comment in prevent binding to all HID devices on ROG
- Replace asus_ref.asus with local reference in all patches
- Add missing kernel doc comments
- Other minor nits from Ilpo
- User reported warning due to scheduling work while holding a spinlock.
Restructure patch for multiple handlers to limit when spinlock is held to
variable access only. In parallel, setup a workqueue to handle registration
of led device and setting brightness. This is required as registering the
led device triggers kbd_led_get which needs to hold the spinlock to
protect the led_wk value. The workqueue is also required for the hid
event passthrough to avoid scheduling work while holding the spinlock.
Apply the workqueue to wmi brightness buttons as well, as that was
omitted before this series and WMI access was performed.
- On "HID: asus: prevent binding to all HID devices on ROG", rename
quirk HANDLE_GENERIC to SKIP_REPORT_FIXUP and only skip report fixup.
This allows other quirks to apply (applies quirk that fixes keyboard
being named as a pointer device).
Changes since V5:
- It's been a long time
- Remove addition of RGB as that had some comments I need to work on
- Remove folio patch (already merged)
- Remove legacy fix patch 11 from V4. There is a small chance that
without this patch, some old NKEY keyboards might not respond to
RGB commands according to Luke, but the kernel driver does not do
RGB currently. The 0x5d init is done by Armoury crate software in
Windows. If an issue is found, we can re-add it or just remove patches
1/2 before merging. However, init could use the cleanup.
Changes since V4:
- Fix KConfig (reported by kernel robot)
- Fix Ilpo's nits, if I missed anything lmk
Changes since V3:
- Add initializer for 0x5d for old NKEY keyboards until it is verified
that it is not needed for their media keys to function.
- Cover init in asus-wmi with spinlock as per Hans
- If asus-wmi registers WMI handler with brightness, init the brightness
in USB Asus keyboards, per Hans.
- Change hid handler name to asus-UNIQ:rgb:peripheral to match led class
- Fix oops when unregistering asus-wmi by moving unregister outside of
the spin lock (but after the asus reference is set to null)
Changes since V2:
- Check lazy init succeds in asus-wmi before setting register variable
- make explicit check in asus_hid_register_listener for listener existing
to avoid re-init
- rename asus_brt to asus_hid in most places and harmonize everything
- switch to a spinlock instead of a mutex to avoid kernel ooops
- fixup hid device quirks to avoid multiple RGB devices while still exposing
all input vendor devices. This includes moving rgb init to probe
instead of the input_configured callbacks.
- Remove fan key (during retest it appears to be 0xae that is already
supported by hid-asus)
- Never unregister asus::kbd_backlight while asus-wmi is active, as that
- removes fds from userspace and breaks backlight functionality. All
- current mainline drivers do not support backlight hotplugging, so most
userspace software (e.g., KDE, UPower) is built with that assumption.
For the Ally, since it disconnects its controller during sleep, this
caused the backlight slider to not work in KDE.
Changes since V1:
- Add basic RGB support on hid-asus, (Z13/Ally) tested in KDE/Z13
- Fix ifdef else having an invalid signature (reported by kernel robot)
- Restore input arguments to init and keyboard function so they can
be re-used for RGB controls.
- Remove Z13 delay (it did not work to fix the touchpad) and replace it
with a HID_GROUP_GENERIC quirk to allow hid-multitouch to load. Squash
keyboard rename into it.
- Unregister brightness listener before removing work queue to avoid
a race condition causing corruption
- Remove spurious mutex unlock in asus_brt_event
- Place mutex lock in kbd_led_set after LED_UNREGISTERING check to avoid
relocking the mutex and causing a deadlock when unregistering leds
- Add extra check during unregistering to avoid calling unregister when
no led device is registered.
- Temporarily HID_QUIRK_INPUT_PER_APP from the ROG endpoint as it causes
the driver to create 4 RGB handlers per device. I also suspect some
extra events sneak through (KDE had the @@@@@@).
Antheas Kapenekakis (11):
HID: asus: simplify RGB init sequence
HID: asus: initialize additional endpoints only for certain devices
HID: asus: use same report_id in response
HID: asus: fortify keyboard handshake
HID: asus: move vendor initialization to probe
HID: asus: early return for ROG devices
platform/x86: asus-wmi: Add support for multiple kbd led handlers
HID: asus: listen to the asus-wmi brightness device instead of
creating one
platform/x86: asus-wmi: remove unused keyboard backlight quirk
platform/x86: asus-wmi: add keyboard brightness event handler
HID: asus: add support for the asus-wmi brightness handler
drivers/hid/hid-asus.c | 214 ++++++++---------
drivers/platform/x86/asus-wmi.c | 223 +++++++++++++++---
.../platform_data/x86/asus-wmi-leds-ids.h | 50 ----
include/linux/platform_data/x86/asus-wmi.h | 28 +++
4 files changed, 325 insertions(+), 190 deletions(-)
delete mode 100644 include/linux/platform_data/x86/asus-wmi-leds-ids.h
base-commit: 2ccbdb612d0d95f25c38189b83666ff0fb2bfb47
--
2.52.0
^ permalink raw reply
* [PATCH v12 01/11] HID: asus: simplify RGB init sequence
From: Antheas Kapenekakis @ 2026-01-22 7:50 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis
In-Reply-To: <20260122075044.5070-1-lkml@antheas.dev>
Currently, RGB initialization forks depending on whether a device is
NKEY. However, in reality both initialization forks are the same, other
than the NKEY initialization initializing the LED_REPORT_ID1,
LED_REPORT_ID2 endpoints, and the non-NKEY initialization having a
functionality check which is skipped for the NKEY path.
Therefore, merge the if blocks, gate the ID1/ID2 initializations
behind the NKEY quirk instead, and introduce the functionality check
for NKEY devices (it is supported by them).
There should be no functional change with this patch.
Acked-by: Benjamin Tissoires <bentiss@kernel.org>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/hid/hid-asus.c | 52 ++++++++++++++++++------------------------
1 file changed, 22 insertions(+), 30 deletions(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 472bca54642b..323e6302bac5 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -639,13 +639,20 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
unsigned char kbd_func;
int ret;
- if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
- /* Initialize keyboard */
- ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID);
- if (ret < 0)
- return ret;
+ ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID);
+ if (ret < 0)
+ return ret;
- /* The LED endpoint is initialised in two HID */
+ /* Get keyboard functions */
+ ret = asus_kbd_get_functions(hdev, &kbd_func, FEATURE_KBD_REPORT_ID);
+ if (ret < 0)
+ return ret;
+
+ /* Check for backlight support */
+ if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
+ return -ENODEV;
+
+ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
ret = asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID1);
if (ret < 0)
return ret;
@@ -653,34 +660,19 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
ret = asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID2);
if (ret < 0)
return ret;
+ }
- if (dmi_match(DMI_PRODUCT_FAMILY, "ProArt P16")) {
- ret = asus_kbd_disable_oobe(hdev);
- if (ret < 0)
- return ret;
- }
-
- if (drvdata->quirks & QUIRK_ROG_ALLY_XPAD) {
- intf = to_usb_interface(hdev->dev.parent);
- udev = interface_to_usbdev(intf);
- validate_mcu_fw_version(hdev,
- le16_to_cpu(udev->descriptor.idProduct));
- }
-
- } else {
- /* Initialize keyboard */
- ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID);
- if (ret < 0)
- return ret;
-
- /* Get keyboard functions */
- ret = asus_kbd_get_functions(hdev, &kbd_func, FEATURE_KBD_REPORT_ID);
+ if (dmi_match(DMI_PRODUCT_FAMILY, "ProArt P16")) {
+ ret = asus_kbd_disable_oobe(hdev);
if (ret < 0)
return ret;
+ }
- /* Check for backlight support */
- if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
- return -ENODEV;
+ if (drvdata->quirks & QUIRK_ROG_ALLY_XPAD) {
+ intf = to_usb_interface(hdev->dev.parent);
+ udev = interface_to_usbdev(intf);
+ validate_mcu_fw_version(hdev,
+ le16_to_cpu(udev->descriptor.idProduct));
}
drvdata->kbd_backlight = devm_kzalloc(&hdev->dev,
--
2.52.0
^ permalink raw reply related
* [PATCH v12 02/11] HID: asus: initialize additional endpoints only for certain devices
From: Antheas Kapenekakis @ 2026-01-22 7:50 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis
In-Reply-To: <20260122075044.5070-1-lkml@antheas.dev>
Currently, ID1/ID2 initializations are performed for all NKEY devices.
However, ID1 initializations are only required for RGB control and are
only supported for RGB capable devices. ID2 initializations are only
required for initializing the Anime display endpoint which is only
supported on devices with an Anime display. Both of these
initializations are for functionality that is not present on this driver
and are performed for devices which might not support them.
At the same time, there are older NKEY devices that have only been
tested with these initializations in the kernel and it is not possible
to recheck them. There is a possibility that especially with the ID1
initialization, certain laptop models might have their shortcuts stop
working (currently unproven).
To avoid sending unnecessary commands, change to only initialize ID1/ID2
for those NKEY devices suspected to be problematic without them by
introducing a quirk for them and replacing the NKEY quirk in the block
that performs the inits with that. Therefore, new devices that do not
need (and some do not support) these initializations will not have
them performed.
In addition, as these initializations might not be supported by the
affected devices, change the function to not bail if they fail.
Acked-by: Benjamin Tissoires <bentiss@kernel.org>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/hid/hid-asus.c | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 323e6302bac5..92904b5a700c 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -90,6 +90,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define QUIRK_ROG_NKEY_KEYBOARD BIT(11)
#define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12)
#define QUIRK_ROG_ALLY_XPAD BIT(13)
+#define QUIRK_ROG_NKEY_ID1ID2_INIT BIT(14)
#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
QUIRK_NO_INIT_REPORTS | \
@@ -652,14 +653,9 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
return -ENODEV;
- if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
- ret = asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID1);
- if (ret < 0)
- return ret;
-
- ret = asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID2);
- if (ret < 0)
- return ret;
+ if (drvdata->quirks & QUIRK_ROG_NKEY_ID1ID2_INIT) {
+ asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID1);
+ asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID2);
}
if (dmi_match(DMI_PRODUCT_FAMILY, "ProArt P16")) {
@@ -1376,10 +1372,10 @@ static const struct hid_device_id asus_devices[] = {
QUIRK_USE_KBD_BACKLIGHT },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD),
- QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
+ QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_ID1ID2_INIT },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2),
- QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
+ QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_ID1ID2_INIT },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR),
QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
--
2.52.0
^ permalink raw reply related
* Re: [PATCH] arm64: dts: qcom: milos-fairphone-fp6: Add Hall Effect sensor
From: Luca Weiss @ 2026-01-22 7:48 UTC (permalink / raw)
To: Dmitry Torokhov, Luca Weiss
Cc: Dmitry Baryshkov, Konrad Dybcio, Bjorn Andersson, Konrad Dybcio,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
~postmarketos/upstreaming, phone-devel, linux-arm-msm, devicetree,
linux-kernel, linux-input
In-Reply-To: <t4yjnuty7tmvs4vqzm4vdid7oowicy7hcnmqmu6nqtit3yru55@zpo6kprnarth>
On Wed Jan 21, 2026 at 9:06 PM CET, Dmitry Torokhov wrote:
> On Wed, Jan 21, 2026 at 09:07:44AM +0100, Luca Weiss wrote:
>> On Wed Jan 21, 2026 at 12:05 AM CET, Dmitry Baryshkov wrote:
>> > On Mon, Jan 19, 2026 at 04:52:23PM +0100, Luca Weiss wrote:
>> >> On Mon Jan 19, 2026 at 3:41 PM CET, Konrad Dybcio wrote:
>> >> > On 1/16/26 3:22 PM, Luca Weiss wrote:
>> >> >> Add a node for the Hall Effect sensor, used to detect whether the Flip
>> >> >> Cover is closed or not.
>> >> >>
>> >> >> The sensor is powered through vreg_l10b, so let's put a
>> >> >> regulator-always-on on that to make sure the sensor gets power.
>> >> >
>> >> > Is there anything else on L10B? Can we turn it off if the hall sensor
>> >> > is e.g. user-disabled?
>> >>
>> >> It's the voltage source for pull-up of sensor I2C bus (so
>> >> ADSP-managed?), DVDD for amplifiers and VDD for a most sensors like
>> >> the gyro.
>> >>
>> >> So realistically, it'll probably be (nearly) always on anyways. And I
>> >> don't want to shave another yak by adding vdd support to gpio-keys...
>> >
>> > Why? If it is exactly what happens on the board: the device producing
>> > GPIO events _is_ powered via a vdd. Added Input maintainer / list to cc.
>>
>> Yes, the hall sensor which is connected to the GPIO on the SoC, has an
>> extra VDD input which needs to be on in order for the Hall-effect sensor
>> to be on.
>>
>> See page 133 "HALL" in the center of the page
>> https://www.fairphone.com/wp-content/uploads/2025/08/Fairphone-Gen.-6_-Information-on-how-to-repair-dispose-of-and-recycle-EN-NL-FR-DE.pdf
>>
>> The IC is OCH166AEV4AD where VDD is (as expected) "Power Supply Input":
>> https://www.orient-chip.com/Public/Uploads/uploadfile/files/20231014/1OCH166Adatasheet202203221.pdf
>
> If we add regulator support to gpio-keys do we expect it to be
> per-gpio/per-key? Or we'd recommend splitting into several instances of
> gpio-keys so that there is on set of supplies per gpio-keys device?
In my opinion it would fit in this case in the event-hall-sensor node,
that next to gpios = <&tlmm 70 GPIO_ACTIVE_LOW> we get a
vdd-supply = <&vreg_l10b>.
Regards
Luca
>
> Thanks.
^ permalink raw reply
* [PATCH] Input: i8042 - add quirk for ASUS Zenbook UX425QA_UM425QA
From: feng @ 2026-01-22 1:39 UTC (permalink / raw)
To: dtor; +Cc: linux-input, linux-kernel, feng
The ASUS Zenbook UX425QA_UM425QA fails to initialize the keyboard after a
cold boot.
A quirk already exists for "ZenBook UX425", but some Zenbooks report "Zenbook" with a lowercase 'b'.
Since DMI matching is case-sensitive, the existing quirk is not applied to these "extra special" Zenbooks.
Testing confirms that this model needs the same quirks as the ZenBook UX425 variants.
Signed-off-by: feng <alec.jiang@gmail.com>
---
drivers/input/serio/i8042-acpipnpio.h | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h
index 654771275..b34d922a9 100644
--- a/drivers/input/serio/i8042-acpipnpio.h
+++ b/drivers/input/serio/i8042-acpipnpio.h
@@ -115,6 +115,16 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = {
},
.driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_NEVER)
},
+ /* ASUS Zenbook UX425QA_UM425QA
+ * Some Zenbooks report "Zenbook" with a lowercase b.
+ */
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Zenbook UX425QA_UM425QA"),
+ },
+ .driver_data = (void *)(SERIO_QUIRK_PROBE_DEFER | SERIO_QUIRK_RESET_NEVER)
+ },
{
/* ASUS ZenBook UX425UA/QA */
.matches = {
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v5 1/2] dt-bindings: touchscreen: trivial-touch: Drop 'interrupts' requirement for old Ilitek
From: Marek Vasut @ 2026-01-22 0:22 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: linux-input, Frank Li, Krzysztof Kozlowski, Conor Dooley,
Job Noorman, Krzysztof Kozlowski, Rob Herring, devicetree,
linux-kernel, linux-renesas-soc
In-Reply-To: <kzt3myrtfigzvtuw62eqts5tjfwvlueuzifk7dsm5jvawztsle@5wyzecmltldb>
On 1/22/26 1:19 AM, Dmitry Torokhov wrote:
> On Thu, Jan 22, 2026 at 12:06:46AM +0100, Marek Vasut wrote:
>> The old Ilitek touch controllers V3 and V6 can operate without
>> interrupt line, in polling mode. Drop the 'interrupts' property
>> requirement for those four controllers. To avoid overloading the
>> trivial-touch, fork the old Ilitek V3/V6 touch controller binding
>> into separate document.
>>
>> Reviewed-by: Frank Li <Frank.Li@nxp.com>
>> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
>> Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
>
> Applied the lot, thank you.
Likewise, thank you.
^ permalink raw reply
* Re: [PATCH v5 1/2] dt-bindings: touchscreen: trivial-touch: Drop 'interrupts' requirement for old Ilitek
From: Dmitry Torokhov @ 2026-01-22 0:19 UTC (permalink / raw)
To: Marek Vasut
Cc: linux-input, Frank Li, Krzysztof Kozlowski, Conor Dooley,
Job Noorman, Krzysztof Kozlowski, Rob Herring, devicetree,
linux-kernel, linux-renesas-soc
In-Reply-To: <20260121230736.114623-1-marek.vasut+renesas@mailbox.org>
On Thu, Jan 22, 2026 at 12:06:46AM +0100, Marek Vasut wrote:
> The old Ilitek touch controllers V3 and V6 can operate without
> interrupt line, in polling mode. Drop the 'interrupts' property
> requirement for those four controllers. To avoid overloading the
> trivial-touch, fork the old Ilitek V3/V6 touch controller binding
> into separate document.
>
> Reviewed-by: Frank Li <Frank.Li@nxp.com>
> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
> Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Applied the lot, thank you.
--
Dmitry
^ permalink raw reply
* Re: [PATCH v3] Input: novatek-nvt-ts - drop wake_type check
From: Dmitry Torokhov @ 2026-01-22 0:14 UTC (permalink / raw)
To: Gianluca Boiano; +Cc: linux-input, hdegoede
In-Reply-To: <20260122001040.76869-1-morf3089@gmail.com>
On Thu, Jan 22, 2026 at 01:10:40AM +0100, Gianluca Boiano wrote:
> The wake_type parameter from touchscreen registers is not used for
> anything functional - the driver only validates that it matches a
> hardcoded expected value per chip variant. This causes probe to fail
> on touchscreens that report a different wake_type despite being
> otherwise compatible.
>
> Drop the wake_type check and the associated chip data member to allow
> the existing compatibles to work with more touchscreen variants.
>
> Signed-off-by: Gianluca Boiano <morf3089@gmail.com>
I think this deserves "Suggested-by:" tag. I'll add it unless there are
objections...
> ---
> Changes in v3:
> - Completely reworked per Hans de Goede's suggestion: instead of adding
> a new compatible for the e7t variant, simply drop the wake_type check
> entirely since it serves no functional purpose
> - Dropped dt-bindings patch (no longer needed)
>
> Changes in v2:
> - Removed Closes: tag referencing downstream repository
>
> drivers/input/touchscreen/novatek-nvt-ts.c | 5 -----
> 1 file changed, 5 deletions(-)
>
> diff --git a/drivers/input/touchscreen/novatek-nvt-ts.c b/drivers/input/touchscreen/novatek-nvt-ts.c
> index 44b58e0dc1ad..3e6e2ee0ba8f 100644
> --- a/drivers/input/touchscreen/novatek-nvt-ts.c
> +++ b/drivers/input/touchscreen/novatek-nvt-ts.c
> @@ -27,7 +27,6 @@
> #define NVT_TS_PARAMS_MAX_TOUCH 0x09
> #define NVT_TS_PARAMS_MAX_BUTTONS 0x0a
> #define NVT_TS_PARAMS_IRQ_TYPE 0x0b
> -#define NVT_TS_PARAMS_WAKE_TYPE 0x0c
> #define NVT_TS_PARAMS_CHIP_ID 0x0e
> #define NVT_TS_PARAMS_SIZE 0x0f
>
> @@ -49,7 +48,6 @@ static const int nvt_ts_irq_type[4] = {
> };
>
> struct nvt_ts_i2c_chip_data {
> - u8 wake_type;
> u8 chip_id;
> };
>
> @@ -261,7 +259,6 @@ static int nvt_ts_probe(struct i2c_client *client)
> if (width > NVT_TS_MAX_SIZE || height >= NVT_TS_MAX_SIZE ||
> data->max_touches > NVT_TS_MAX_TOUCHES ||
> irq_type >= ARRAY_SIZE(nvt_ts_irq_type) ||
> - data->buf[NVT_TS_PARAMS_WAKE_TYPE] != chip->wake_type ||
> data->buf[NVT_TS_PARAMS_CHIP_ID] != chip->chip_id) {
> dev_err(dev, "Unsupported touchscreen parameters: %*ph\n",
> NVT_TS_PARAMS_SIZE, data->buf);
> @@ -314,12 +311,10 @@ static int nvt_ts_probe(struct i2c_client *client)
> }
>
> static const struct nvt_ts_i2c_chip_data nvt_nt11205_ts_data = {
> - .wake_type = 0x05,
> .chip_id = 0x05,
> };
>
> static const struct nvt_ts_i2c_chip_data nvt_nt36672a_ts_data = {
> - .wake_type = 0x01,
> .chip_id = 0x08,
> };
>
Thanks.
--
Dmitry
^ permalink raw reply
* [PATCH v3] Input: novatek-nvt-ts - drop wake_type check
From: Gianluca Boiano @ 2026-01-22 0:10 UTC (permalink / raw)
To: linux-input; +Cc: hdegoede, dmitry.torokhov, Gianluca Boiano
In-Reply-To: <20260121-fervent-wren-1730d5@quoll>
The wake_type parameter from touchscreen registers is not used for
anything functional - the driver only validates that it matches a
hardcoded expected value per chip variant. This causes probe to fail
on touchscreens that report a different wake_type despite being
otherwise compatible.
Drop the wake_type check and the associated chip data member to allow
the existing compatibles to work with more touchscreen variants.
Signed-off-by: Gianluca Boiano <morf3089@gmail.com>
---
Changes in v3:
- Completely reworked per Hans de Goede's suggestion: instead of adding
a new compatible for the e7t variant, simply drop the wake_type check
entirely since it serves no functional purpose
- Dropped dt-bindings patch (no longer needed)
Changes in v2:
- Removed Closes: tag referencing downstream repository
drivers/input/touchscreen/novatek-nvt-ts.c | 5 -----
1 file changed, 5 deletions(-)
diff --git a/drivers/input/touchscreen/novatek-nvt-ts.c b/drivers/input/touchscreen/novatek-nvt-ts.c
index 44b58e0dc1ad..3e6e2ee0ba8f 100644
--- a/drivers/input/touchscreen/novatek-nvt-ts.c
+++ b/drivers/input/touchscreen/novatek-nvt-ts.c
@@ -27,7 +27,6 @@
#define NVT_TS_PARAMS_MAX_TOUCH 0x09
#define NVT_TS_PARAMS_MAX_BUTTONS 0x0a
#define NVT_TS_PARAMS_IRQ_TYPE 0x0b
-#define NVT_TS_PARAMS_WAKE_TYPE 0x0c
#define NVT_TS_PARAMS_CHIP_ID 0x0e
#define NVT_TS_PARAMS_SIZE 0x0f
@@ -49,7 +48,6 @@ static const int nvt_ts_irq_type[4] = {
};
struct nvt_ts_i2c_chip_data {
- u8 wake_type;
u8 chip_id;
};
@@ -261,7 +259,6 @@ static int nvt_ts_probe(struct i2c_client *client)
if (width > NVT_TS_MAX_SIZE || height >= NVT_TS_MAX_SIZE ||
data->max_touches > NVT_TS_MAX_TOUCHES ||
irq_type >= ARRAY_SIZE(nvt_ts_irq_type) ||
- data->buf[NVT_TS_PARAMS_WAKE_TYPE] != chip->wake_type ||
data->buf[NVT_TS_PARAMS_CHIP_ID] != chip->chip_id) {
dev_err(dev, "Unsupported touchscreen parameters: %*ph\n",
NVT_TS_PARAMS_SIZE, data->buf);
@@ -314,12 +311,10 @@ static int nvt_ts_probe(struct i2c_client *client)
}
static const struct nvt_ts_i2c_chip_data nvt_nt11205_ts_data = {
- .wake_type = 0x05,
.chip_id = 0x05,
};
static const struct nvt_ts_i2c_chip_data nvt_nt36672a_ts_data = {
- .wake_type = 0x01,
.chip_id = 0x08,
};
--
2.52.0
^ permalink raw reply related
* Re: [PATCH 2/2] input: touchscreen: novatek-nvt-ts: Add support for NT36672A e7t variant
From: Gianluca Boiano @ 2026-01-22 0:09 UTC (permalink / raw)
To: hdegoede; +Cc: linux-input, dmitry.torokhov
In-Reply-To: <b4113f8c-0fbd-4a96-9cf6-9675f837ac91@kernel.org>
Hi Hans,
Thanks for the suggestion. You're right that removing code is better than
adding more variants.
I've sent v3 which drops the wake_type check entirely, as you suggested.
Gianluca
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox