* [PATCH v4 4/5] HID: asus: add support for xgm led
From: Denis Benato @ 2026-06-15 16:50 UTC (permalink / raw)
To: linux-kernel
Cc: linux-input, Benjamin Tissoires, Jiri Kosina, Luke D . Jones,
Mateusz Schyboll, Denis Benato, Denis Benato, Antheas Kapenekakis,
Connor Belli
In-Reply-To: <20260615165058.3845-1-denis.benato@linux.dev>
XG mobile stations have very bright leds behind the fan that can be
turned either ON or OFF: add a cled interface to allow controlling the
brightness of those red leds.
Signed-off-by: Denis Benato <denis.benato@linux.dev>
---
drivers/hid/hid-asus.c | 91 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 91 insertions(+)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 6896730efafc..e68a6d93369d 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -51,6 +51,8 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define FEATURE_KBD_LED_REPORT_ID1 0x5d
#define FEATURE_KBD_LED_REPORT_ID2 0x5e
+#define ROG_XGM_REPORT_SIZE 300
+
#define ROG_ALLY_REPORT_SIZE 64
#define ROG_ALLY_X_MIN_MCU 313
#define ROG_ALLY_MIN_MCU 319
@@ -143,6 +145,11 @@ struct asus_worker {
bool removed;
};
+struct asus_xgm_led {
+ struct led_classdev cdev;
+ struct hid_device *hdev;
+};
+
struct asus_touchpad_info {
int max_x;
int max_y;
@@ -169,6 +176,7 @@ struct asus_drvdata {
unsigned long battery_next_query;
struct asus_hid_listener listener;
bool fn_lock;
+ struct asus_xgm_led *xgm_led;
};
static int asus_report_battery(struct asus_drvdata *, u8 *, int);
@@ -1119,6 +1127,26 @@ static int asus_battery_probe(struct hid_device *hdev)
return ret;
}
+static int asus_xgm_led_set(struct led_classdev *led_cdev, enum led_brightness value)
+{
+ const u8 buf[ROG_XGM_REPORT_SIZE] = {
+ FEATURE_KBD_LED_REPORT_ID2, 0xC5, (value) ? 0x50 : 0x00
+ };
+ struct asus_xgm_led *xgm = container_of(led_cdev, struct asus_xgm_led, cdev);
+ int ret;
+
+ ret = asus_kbd_set_report(xgm->hdev, buf, ROG_XGM_REPORT_SIZE);
+ if (ret < 0) {
+ hid_err(xgm->hdev, "Unable to set XG mobile led state: %d\n", ret);
+ return ret;
+ } else if (ret != ROG_XGM_REPORT_SIZE) {
+ hid_err(xgm->hdev, "Unexpected partial transfer to XG mobile: %d\n", ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+
static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
{
struct input_dev *input = hi->input;
@@ -1343,9 +1371,52 @@ static int asus_start_multitouch(struct hid_device *hdev)
return 0;
}
+static int asus_xgm_init(struct hid_device *hdev, struct asus_drvdata *drvdata)
+{
+ const char *name;
+ int ret;
+
+ drvdata->xgm_led = devm_kzalloc(&hdev->dev, sizeof(*drvdata->xgm_led), GFP_KERNEL);
+ if (drvdata->xgm_led == NULL)
+ return -ENOMEM;
+
+ name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "asus:xgm-%s:led",
+ strlen(hdev->uniq) ? hdev->uniq : dev_name(&hdev->dev));
+
+ if (name == NULL) {
+ ret = -ENOMEM;
+ goto asus_xgm_init_err;
+ }
+
+ drvdata->xgm_led->hdev = hdev;
+ drvdata->xgm_led->cdev.name = name;
+ drvdata->xgm_led->cdev.brightness = 1;
+ drvdata->xgm_led->cdev.max_brightness = 1;
+ drvdata->xgm_led->cdev.brightness_set_blocking = asus_xgm_led_set;
+
+ /* LED state is arbitrary on boot, set a default */
+ ret = asus_xgm_led_set(&drvdata->xgm_led->cdev, drvdata->xgm_led->cdev.brightness);
+ if (ret) {
+ hid_err(hdev, "Asus failed to set xgm led: %d\n", ret);
+ goto asus_xgm_init_err;
+ }
+
+ ret = devm_led_classdev_register(&hdev->dev, &drvdata->xgm_led->cdev);
+ if (ret) {
+ hid_err(hdev, "Asus failed to register xgm led: %d\n", ret);
+ goto asus_xgm_init_err;
+ }
+
+ return 0;
+asus_xgm_init_err:
+ drvdata->xgm_led = NULL;
+ return ret;
+}
+
static int __maybe_unused asus_resume(struct hid_device *hdev)
{
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
+ int ret;
/*
* If we have a backlight listener registered, restore the previous state,
@@ -1355,7 +1426,17 @@ static int __maybe_unused asus_resume(struct hid_device *hdev)
if (drvdata->listener.brightness_set)
asus_kbd_backlight_set(&drvdata->listener, drvdata->kbd_backlight_brightness);
+ if (drvdata->xgm_led) {
+ ret = asus_xgm_led_set(&drvdata->xgm_led->cdev, drvdata->xgm_led->cdev.brightness);
+ if (ret) {
+ hid_err(hdev, "Asus failed to restore xgm brightness: %d\n", ret);
+ goto asus_resume_err;
+ }
+ }
+
return 0;
+asus_resume_err:
+ return ret;
}
static int __maybe_unused asus_reset_resume(struct hid_device *hdev)
@@ -1484,6 +1565,16 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
}
+ if (asus_has_report_id(hdev, FEATURE_KBD_REPORT_ID) &&
+ ((hdev->product == USB_DEVICE_ID_ASUSTEK_XGM_2022) ||
+ (hdev->product == USB_DEVICE_ID_ASUSTEK_XGM_2023))) {
+ ret = asus_xgm_init(hdev, drvdata);
+ if (ret) {
+ hid_err(hdev, "Failed to initialize xg mobile: %d\n", ret);
+ goto err_stop_hw;
+ }
+ }
+
/* Laptops keyboard backlight is always at 0x5a */
if (is_vendor && (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT) &&
(asus_has_report_id(hdev, FEATURE_KBD_REPORT_ID)) &&
--
2.47.3
^ permalink raw reply related
* [PATCH v4 3/5] HID: asus: fix a off-by-one issue making a check stricter that it needs to be
From: Denis Benato @ 2026-06-15 16:50 UTC (permalink / raw)
To: linux-kernel
Cc: linux-input, Benjamin Tissoires, Jiri Kosina, Luke D . Jones,
Mateusz Schyboll, Denis Benato, Denis Benato, Antheas Kapenekakis,
Connor Belli
In-Reply-To: <20260615165058.3845-1-denis.benato@linux.dev>
In mcu_parse_version_string() a size validation for response is stricter
that it needs to be: relax the check by one byte.
Fixes: ("hid-asus: check ROG Ally MCU version and warn")
Signed-off-by: Denis Benato <denis.benato@linux.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 bbf964b12c16..6896730efafc 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -835,7 +835,7 @@ static int mcu_parse_version_string(const u8 *response, size_t response_size)
dots++;
}
- if (dots != 2 || p >= end || (p + 3) >= end)
+ if (dots != 2 || end - p < 3)
return -EINVAL;
memcpy(buf, p, 3);
--
2.47.3
^ permalink raw reply related
* [PATCH v4 2/5] HID: asus: remove unnecessary OOM message
From: Denis Benato @ 2026-06-15 16:50 UTC (permalink / raw)
To: linux-kernel
Cc: linux-input, Benjamin Tissoires, Jiri Kosina, Luke D . Jones,
Mateusz Schyboll, Denis Benato, Denis Benato, Antheas Kapenekakis,
Connor Belli
In-Reply-To: <20260615165058.3845-1-denis.benato@linux.dev>
If devm_kzalloc fails an allocation error is already being reported:
there is no need for the driver to repeat it, moreover if this were
new code checkpatch.pl would throw a warning.
Signed-off-by: Denis Benato <denis.benato@linux.dev>
---
drivers/hid/hid-asus.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 05ca6665e0a4..bbf964b12c16 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -1377,10 +1377,8 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
int ret;
drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
- if (drvdata == NULL) {
- hid_err(hdev, "Can't alloc Asus descriptor\n");
+ if (drvdata == NULL)
return -ENOMEM;
- }
hid_set_drvdata(hdev, drvdata);
--
2.47.3
^ permalink raw reply related
* [PATCH v4 1/5] HID: asus: refactor the two workqueues and init sequence
From: Denis Benato @ 2026-06-15 16:50 UTC (permalink / raw)
To: linux-kernel
Cc: linux-input, Benjamin Tissoires, Jiri Kosina, Luke D . Jones,
Mateusz Schyboll, Denis Benato, Denis Benato, Antheas Kapenekakis,
Connor Belli, sahiko-bot
In-Reply-To: <20260615165058.3845-1-denis.benato@linux.dev>
Multiple issues have been found within the hid-asus driver:
- unchecked size in asus_raw_event()
- unclean teardown of asus_probe on failure
- possible use-after-free in asus_probe
- multiple workqueue used for jobs where one was enough
- sleeping calls in atomic context
- packets of incorrect size being sent to the keyboard controller
Join the two workqueues into one reusing the stopping mechanism
of the brightness workqueue, use the joined workqueue to also
move the asus_wmi_send_event() sleeping call away from atomic
context and add a size check in asus_raw_event().
Fixes: f631011e36b8 ("HID: hid-asus: Implement fn lock for Asus ProArt P16")
Fixes: 1489a34e97ef ("HID: asus: Implement Fn+F5 fan control key handler")
Fixes: b34b5945a769 ("HID: asus: listen to the asus-wmi brightness device instead of creating one")
Reported-by: sahiko-bot@kernel.org
Closes: https://lore.kernel.org/all/20260613154732.60A4B1F000E9@smtp.kernel.org/
Signed-off-by: Denis Benato <denis.benato@linux.dev>
---
drivers/hid/hid-asus.c | 393 +++++++++++++++++++++++++++++------------
1 file changed, 282 insertions(+), 111 deletions(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index d34d74df3dc0..05ca6665e0a4 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -109,11 +109,36 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define TRKID_SGN ((TRKID_MAX + 1) >> 1)
-struct asus_kbd_leds {
- struct asus_hid_listener listener;
+enum asus_work_action_type {
+ FN_LOCK_SYNC,
+ BRIGHTNESS_SET,
+ WMI_FAN,
+};
+
+struct hid_raw_event_data {
+ u8 report_data[FEATURE_KBD_REPORT_SIZE];
+ size_t report_size;
+};
+
+struct asus_work_action {
+ struct list_head node;
+ enum asus_work_action_type type;
+ union {
+ /* Data for BRIGHTNESS_SET */
+ unsigned int brightness;
+
+ /* Data for FN_LOCK_SYNC */
+ bool fn_lock;
+
+ /* Data for WMI_FAN */
+ struct hid_raw_event_data fan_hid_data;
+ } data;
+};
+
+struct asus_worker {
struct hid_device *hdev;
struct work_struct work;
- unsigned int brightness;
+ struct list_head actions;
spinlock_t lock;
bool removed;
};
@@ -133,7 +158,8 @@ struct asus_drvdata {
struct hid_device *hdev;
struct input_dev *input;
struct input_dev *tp_kbd_input;
- struct asus_kbd_leds *kbd_backlight;
+ struct asus_worker *worker;
+ unsigned int kbd_backlight_brightness;
const struct asus_touchpad_info *tp;
struct power_supply *battery;
struct power_supply_desc battery_desc;
@@ -141,7 +167,7 @@ struct asus_drvdata {
int battery_stat;
bool battery_in_query;
unsigned long battery_next_query;
- struct work_struct fn_lock_sync_work;
+ struct asus_hid_listener listener;
bool fn_lock;
};
@@ -211,6 +237,29 @@ static const u8 asus_report_id_init[] = {
FEATURE_KBD_LED_REPORT_ID2
};
+/*
+ * Send events to asus-wmi driver for handling special keys
+ */
+static int asus_wmi_send_event(struct asus_drvdata *drvdata, u8 code)
+{
+ int err;
+ u32 retval;
+
+ err = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS,
+ ASUS_WMI_METHODID_NOTIF, code, &retval);
+ if (err) {
+ pr_warn("Failed to notify asus-wmi: %d\n", err);
+ return err;
+ }
+
+ if (retval != 0) {
+ pr_warn("Failed to notify asus-wmi (retval): 0x%x\n", retval);
+ return -EIO;
+ }
+
+ return 0;
+}
+
static void asus_report_contact_down(struct asus_drvdata *drvdat,
int toolType, u8 *data)
{
@@ -331,25 +380,71 @@ static int asus_e1239t_event(struct asus_drvdata *drvdat, u8 *data, int size)
}
/*
- * Send events to asus-wmi driver for handling special keys
+ * Used in atomic contexts to schedule work involving sleeps operations or
+ * asus-wmi interactions.
+ *
+ * Caller is responsible to store relevant data in the structure to carry out
+ * the required action.
+ *
+ * This function must be called while the spin lock protecting the workqueue
+ * is already being held.
*/
-static int asus_wmi_send_event(struct asus_drvdata *drvdata, u8 code)
+static void asus_worker_schedule(struct asus_worker *worker, struct asus_work_action *action)
{
- int err;
- u32 retval;
-
- err = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS,
- ASUS_WMI_METHODID_NOTIF, code, &retval);
- if (err) {
- pr_warn("Failed to notify asus-wmi: %d\n", err);
- return err;
+ if (worker->removed) {
+ kfree(action);
+ return;
}
- if (retval != 0) {
- pr_warn("Failed to notify asus-wmi (retval): 0x%x\n", retval);
- return -EIO;
+ list_add_tail(&action->node, &worker->actions);
+ schedule_work(&worker->work);
+}
+
+static int asus_kbd_fn_lock_set(struct asus_drvdata *drvdata, bool enabled)
+{
+ struct asus_work_action *action;
+ unsigned long flags;
+
+ action = kzalloc_obj(struct asus_work_action);
+ if (!action)
+ return -ENOMEM;
+
+ drvdata->fn_lock = enabled;
+ action->type = FN_LOCK_SYNC;
+ action->data.fn_lock = drvdata->fn_lock;
+ INIT_LIST_HEAD(&action->node);
+
+ spin_lock_irqsave(&drvdata->worker->lock, flags);
+ asus_worker_schedule(drvdata->worker, action);
+ spin_unlock_irqrestore(&drvdata->worker->lock, flags);
+
+ return 0;
+}
+
+static int asus_kbd_wmi_fan_send(struct asus_drvdata *drvdata, u8 *report_data,
+ size_t report_size)
+{
+ struct asus_work_action *action;
+ unsigned long flags;
+
+ if (report_size > FEATURE_KBD_REPORT_SIZE) {
+ hid_err(drvdata->hdev, "Invalid report size for fan event: %zu\n", report_size);
+ return -EINVAL;
}
+ action = kzalloc_obj(struct asus_work_action);
+ if (!action)
+ return -ENOMEM;
+
+ action->type = WMI_FAN;
+ action->data.fan_hid_data.report_size = report_size;
+ memcpy(action->data.fan_hid_data.report_data, report_data, report_size);
+ INIT_LIST_HEAD(&action->node);
+
+ spin_lock_irqsave(&drvdata->worker->lock, flags);
+ asus_worker_schedule(drvdata->worker, action);
+ spin_unlock_irqrestore(&drvdata->worker->lock, flags);
+
return 0;
}
@@ -357,6 +452,7 @@ static int asus_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
+ int ret;
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_ASUSVENDOR &&
(usage->hid & HID_USAGE) != 0x00 &&
@@ -375,8 +471,11 @@ static int asus_event(struct hid_device *hdev, struct hid_field *field,
return !asus_hid_event(ASUS_EV_BRTTOGGLE);
case KEY_FN_ESC:
if (drvdata->quirks & QUIRK_HID_FN_LOCK) {
- drvdata->fn_lock = !drvdata->fn_lock;
- schedule_work(&drvdata->fn_lock_sync_work);
+ ret = asus_kbd_fn_lock_set(drvdata, !drvdata->fn_lock);
+ if (ret) {
+ hid_err(hdev, "Error while toggling FN lock: %d\n", ret);
+ return ret;
+ }
}
break;
}
@@ -389,6 +488,12 @@ static int asus_raw_event(struct hid_device *hdev,
struct hid_report *report, u8 *data, int size)
{
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
+ int ret;
+
+ if (size < 2) {
+ hid_dbg(hdev, "Unexpected keyboard report size %d\n", size);
+ return 0;
+ }
if (drvdata->battery && data[0] == BATTERY_REPORT_ID)
return asus_report_battery(drvdata, data, size);
@@ -414,19 +519,13 @@ static int asus_raw_event(struct hid_device *hdev,
* pass to userspace so it can implement its own fan control.
*/
if (data[1] == ASUS_FAN_CTRL_KEY_CODE) {
- int ret = asus_wmi_send_event(drvdata, ASUS_FAN_CTRL_KEY_CODE);
+ ret = asus_kbd_wmi_fan_send(drvdata, data, size);
- if (ret == 0) {
- /* Successfully handled by asus-wmi, block event */
+ /* if execution deferred successfully block event */
+ if (ret == 0)
return -1;
- }
- /*
- * Warn if asus-wmi failed (but not if it's unavailable).
- * Let the event reach userspace in all failure cases.
- */
- if (ret != -ENODEV)
- hid_warn(hdev, "Failed to notify asus-wmi: %d\n", ret);
+ return ret;
}
/*
@@ -569,59 +668,151 @@ static int asus_kbd_disable_oobe(struct hid_device *hdev)
return 0;
}
-static int asus_kbd_set_fn_lock(struct hid_device *hdev, bool enabled)
+static void asus_kbd_set_fn_lock(struct hid_device *hdev, bool enabled)
{
- u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xd0, 0x4e, !!enabled };
+ const u8 buf[FEATURE_KBD_REPORT_SIZE] = { FEATURE_KBD_REPORT_ID, 0xd0, 0x4e, !!enabled };
+ int ret;
- return asus_kbd_set_report(hdev, buf, sizeof(buf));
+ ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
+ if (ret < 0)
+ hid_err(hdev, "Asus failed to set fn lock: %d\n", ret);
}
-static void asus_sync_fn_lock(struct work_struct *work)
+static void asus_kbd_set_brightness(struct hid_device *hdev, u8 brightness)
{
- struct asus_drvdata *drvdata =
- container_of(work, struct asus_drvdata, fn_lock_sync_work);
+ const u8 buf[FEATURE_KBD_REPORT_SIZE] = {
+ FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4, brightness
+ };
+ int ret;
- asus_kbd_set_fn_lock(drvdata->hdev, drvdata->fn_lock);
+ ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
+ if (ret < 0)
+ hid_err(hdev, "Asus failed to set keyboard backlight: %d\n", ret);
}
-static void asus_schedule_work(struct asus_kbd_leds *led)
+static void asus_kbd_wmi_fan(struct hid_device *hdev, struct hid_raw_event_data *data)
{
+ struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
+ int ret;
+
+ ret = asus_wmi_send_event(drvdata, ASUS_FAN_CTRL_KEY_CODE);
+
+ /*
+ * Warn if asus-wmi failed (but not if it's unavailable).
+ * Let the event reach userspace in all failure cases.
+ */
+ switch (ret) {
+ case -ENODEV:
+ break;
+ case 0:
+ return;
+ default:
+ hid_warn(hdev, "Failed to notify asus-wmi: %d\n", ret);
+ break;
+ }
+
+ /* Fallback: pass the raw event to the HID core */
+ hid_report_raw_event(hdev, HID_INPUT_REPORT,
+ data->report_data, data->report_size,
+ data->report_size, 1);
+}
+
+static void asus_kbd_backlight_set(struct asus_hid_listener *listener, int brightness)
+{
+ struct asus_drvdata *drvdata = container_of(listener, struct asus_drvdata, listener);
+ struct asus_worker *worker = drvdata->worker;
+ struct asus_work_action *action;
unsigned long flags;
- spin_lock_irqsave(&led->lock, flags);
- if (!led->removed)
- schedule_work(&led->work);
- spin_unlock_irqrestore(&led->lock, flags);
+ drvdata->kbd_backlight_brightness = brightness;
+
+ action = kzalloc_obj(struct asus_work_action);
+ if (!action) {
+ hid_warn(drvdata->hdev, "Failed to allocate memory for backlight action\n");
+ return;
+ }
+
+ action->type = BRIGHTNESS_SET;
+ action->data.brightness = brightness;
+ INIT_LIST_HEAD(&action->node);
+
+ spin_lock_irqsave(&worker->lock, flags);
+ asus_worker_schedule(worker, action);
+ spin_unlock_irqrestore(&worker->lock, flags);
}
-static void asus_kbd_backlight_set(struct asus_hid_listener *listener,
- int brightness)
+static void asus_work(struct work_struct *work)
{
- struct asus_kbd_leds *led = container_of(listener, struct asus_kbd_leds,
- listener);
+ struct asus_worker *worker = container_of(work, struct asus_worker, work);
+ struct asus_work_action *action = NULL;
unsigned long flags;
- spin_lock_irqsave(&led->lock, flags);
- led->brightness = brightness;
- spin_unlock_irqrestore(&led->lock, flags);
+ /* Save the action to be performed and clear the flag */
+ spin_lock_irqsave(&worker->lock, flags);
+ if (!list_empty(&worker->actions)) {
+ action = list_first_entry(&worker->actions,
+ struct asus_work_action, node);
+ list_del(&action->node);
+ }
+ spin_unlock_irqrestore(&worker->lock, flags);
+
+ if (!action)
+ return;
+
+ switch (action->type) {
+ case BRIGHTNESS_SET:
+ asus_kbd_set_brightness(worker->hdev, action->data.brightness);
+ break;
+ case FN_LOCK_SYNC:
+ asus_kbd_set_fn_lock(worker->hdev, action->data.fn_lock);
+ break;
+ case WMI_FAN:
+ asus_kbd_wmi_fan(worker->hdev, &action->data.fan_hid_data);
+ break;
+ default:
+ hid_err(worker->hdev, "Invalid action type: %d\n", action->type);
+ break;
+ }
+
+ kfree(action);
- asus_schedule_work(led);
+ /* Re-schedule if there are more pending actions */
+ spin_lock_irqsave(&worker->lock, flags);
+ if (!list_empty(&worker->actions))
+ schedule_work(&worker->work);
+ spin_unlock_irqrestore(&worker->lock, flags);
}
-static void asus_kbd_backlight_work(struct work_struct *work)
+static int asus_worker_create(struct hid_device *hdev, struct asus_drvdata *drvdata)
{
- struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);
- u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4, 0x00 };
- int ret;
+ drvdata->worker = devm_kzalloc(&hdev->dev, sizeof(struct asus_worker), GFP_KERNEL);
+ if (!drvdata->worker)
+ return -ENOMEM;
+
+ drvdata->worker->removed = false;
+ drvdata->worker->hdev = hdev;
+ INIT_LIST_HEAD(&drvdata->worker->actions);
+
+ INIT_WORK(&drvdata->worker->work, asus_work);
+ spin_lock_init(&drvdata->worker->lock);
+
+ return 0;
+}
+
+static void asus_worker_stop(struct asus_worker *worker)
+{
+ struct asus_work_action *action, *tmp;
unsigned long flags;
- spin_lock_irqsave(&led->lock, flags);
- buf[4] = led->brightness;
- spin_unlock_irqrestore(&led->lock, flags);
+ spin_lock_irqsave(&worker->lock, flags);
+ worker->removed = true;
+ list_for_each_entry_safe(action, tmp, &worker->actions, node) {
+ list_del(&action->node);
+ kfree(action);
+ }
+ spin_unlock_irqrestore(&worker->lock, flags);
- ret = asus_kbd_set_report(led->hdev, buf, sizeof(buf));
- if (ret < 0)
- hid_err(led->hdev, "Asus failed to set keyboard backlight: %d\n", ret);
+ cancel_work_sync(&worker->work);
}
/*
@@ -760,23 +951,11 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
le16_to_cpu(udev->descriptor.idProduct));
}
- drvdata->kbd_backlight = devm_kzalloc(&hdev->dev,
- sizeof(struct asus_kbd_leds),
- GFP_KERNEL);
- if (!drvdata->kbd_backlight)
- return -ENOMEM;
-
- drvdata->kbd_backlight->removed = false;
- drvdata->kbd_backlight->brightness = 0;
- drvdata->kbd_backlight->hdev = hdev;
- 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 = asus_hid_register_listener(&drvdata->kbd_backlight->listener);
+ drvdata->listener.brightness_set = asus_kbd_backlight_set;
+ ret = asus_hid_register_listener(&drvdata->listener);
if (ret < 0) {
- /* No need to have this still around */
- devm_kfree(&hdev->dev, drvdata->kbd_backlight);
+ hid_err(hdev, "Unable to register kbd brightness listener: %d\n", ret);
+ drvdata->listener.brightness_set = NULL;
}
return ret;
@@ -965,11 +1144,9 @@ static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
}
}
- if (drvdata->quirks & QUIRK_HID_FN_LOCK) {
- drvdata->fn_lock = true;
- INIT_WORK(&drvdata->fn_lock_sync_work, asus_sync_fn_lock);
- asus_kbd_set_fn_lock(hdev, true);
- }
+ if ((drvdata->quirks & QUIRK_HID_FN_LOCK) &&
+ (asus_kbd_fn_lock_set(drvdata, true)))
+ hid_warn(hdev, "Error while setting FN lock to ON\n");
if (drvdata->tp) {
int ret;
@@ -1004,11 +1181,9 @@ static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
drvdata->input = input;
- if (drvdata->quirks & QUIRK_HID_FN_LOCK) {
- drvdata->fn_lock = true;
- INIT_WORK(&drvdata->fn_lock_sync_work, asus_sync_fn_lock);
- asus_kbd_set_fn_lock(hdev, true);
- }
+ if ((drvdata->quirks & QUIRK_HID_FN_LOCK) &&
+ (asus_kbd_fn_lock_set(drvdata, true)))
+ hid_warn(hdev, "Error while setting FN lock to ON\n");
return 0;
}
@@ -1171,20 +1346,16 @@ static int asus_start_multitouch(struct hid_device *hdev)
static int __maybe_unused asus_resume(struct hid_device *hdev)
{
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
- int ret = 0;
- if (drvdata->kbd_backlight) {
- const u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4,
- 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);
- goto asus_resume_err;
- }
- }
+ /*
+ * If we have a backlight listener registered, restore the previous state,
+ * in case of error do not fail: most models restore the backlight
+ * automatically, and the error is non-fatal.
+ */
+ if (drvdata->listener.brightness_set)
+ asus_kbd_backlight_set(&drvdata->listener, drvdata->kbd_backlight_brightness);
-asus_resume_err:
- return ret;
+ return 0;
}
static int __maybe_unused asus_reset_resume(struct hid_device *hdev)
@@ -1294,6 +1465,12 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
is_vendor = true;
}
+ ret = asus_worker_create(hdev, drvdata);
+ if (ret) {
+ hid_warn(hdev, "Failed to initialize worker: %d\n", ret);
+ return ret;
+ }
+
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret) {
hid_err(hdev, "Asus hw start failed: %d\n", ret);
@@ -1343,6 +1520,10 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
return 0;
err_stop_hw:
+ if (drvdata->listener.brightness_set)
+ asus_hid_unregister_listener(&drvdata->listener);
+
+ asus_worker_stop(drvdata->worker);
hid_hw_stop(hdev);
return ret;
}
@@ -1350,21 +1531,11 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
static void asus_remove(struct hid_device *hdev)
{
struct asus_drvdata *drvdata = hid_get_drvdata(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);
-
- cancel_work_sync(&drvdata->kbd_backlight->work);
- }
- if (drvdata->quirks & QUIRK_HID_FN_LOCK)
- cancel_work_sync(&drvdata->fn_lock_sync_work);
+ if (drvdata->listener.brightness_set)
+ asus_hid_unregister_listener(&drvdata->listener);
+ asus_worker_stop(drvdata->worker);
hid_hw_stop(hdev);
}
--
2.47.3
^ permalink raw reply related
* Re: [PATCH v6 1/7] dt-bindings: mfd: mt6397: Add MT6392 PMIC
From: Conor Dooley @ 2026-06-15 16:50 UTC (permalink / raw)
To: Luca Leonardo Scorcia
Cc: linux-mediatek, Fabien Parent, Val Packett, Dmitry Torokhov,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sen Chu,
Sean Wang, Macpaul Lin, Lee Jones, Matthias Brugger,
AngeloGioacchino Del Regno, Linus Walleij, Julien Massot,
Louis-Alexis Eyraud, Akari Tsuyukusa, Chen Zhong, linux-input,
devicetree, linux-kernel, linux-pm, linux-arm-kernel, linux-gpio
In-Reply-To: <20260612200717.361018-2-l.scorcia@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1612 bytes --]
On Fri, Jun 12, 2026 at 10:04:06PM +0200, Luca Leonardo Scorcia wrote:
> From: Fabien Parent <parent.f@gmail.com>
>
> Add the initial bindings for the MT6392 PMIC and its RTC device.
>
> Signed-off-by: Fabien Parent <parent.f@gmail.com>
> Signed-off-by: Val Packett <val@packett.cool>
> Signed-off-by: Luca Leonardo Scorcia <l.scorcia@gmail.com>
Sashiko complaint about missing regulators looks valid.
Is it?
Cheers,
Conor.
> ---
> .../devicetree/bindings/mfd/mediatek,mt6397.yaml | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml
> index 3cbc0dc12c31..e39e81aa9924 100644
> --- a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml
> +++ b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml
> @@ -40,6 +40,10 @@ properties:
> - mediatek,mt6358
> - mediatek,mt6359
> - mediatek,mt6397
> + - items:
> + - enum:
> + - mediatek,mt6392
> + - const: mediatek,mt6323
> - items:
> - enum:
> - mediatek,mt6366
> @@ -72,6 +76,10 @@ properties:
> - mediatek,mt6331-rtc
> - mediatek,mt6358-rtc
> - mediatek,mt6397-rtc
> + - items:
> + - enum:
> + - mediatek,mt6392-rtc
> + - const: mediatek,mt6323-rtc
> - items:
> - enum:
> - mediatek,mt6359-rtc
> --
> 2.43.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v3 03/10] dt-bindings: input: microchip,cap11xx: Update datasheet URL and LED reg range
From: Conor Dooley @ 2026-06-15 16:27 UTC (permalink / raw)
To: Jun Yan
Cc: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-input, devicetree, linux-kernel
In-Reply-To: <20260615142103.352163-4-jerrysteve1101@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2881 bytes --]
On Mon, Jun 15, 2026 at 10:20:29PM +0800, Jun Yan wrote:
> - Add datasheet links for all supported CAP11xx variants.
> - Update LED node regex and replace enum constraints with minimum/maximum
> for LED reg ranges in preparation for CAP1114 support.
>
> CAP1114 has 11 LED channels. minimum/maximum constraints are easier to
> maintain than long enum lists when expanding channel count later.
>
> Signed-off-by: Jun Yan <jerrysteve1101@gmail.com>
> ---
> .../bindings/input/microchip,cap11xx.yaml | 17 ++++++++++++++---
> 1 file changed, 14 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml b/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml
> index 7ade03f1b32b..9578c7c206a2 100644
> --- a/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml
> +++ b/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml
> @@ -10,6 +10,15 @@ description: |
> The Microchip CAP1xxx Family of RightTouchTM multiple-channel capacitive
> touch controllers and LED drivers. The device communication via I2C only.
>
> + For more product information please see the links below:
> + CAP1106: https://ww1.microchip.com/downloads/en/DeviceDoc/00001624B.pdf
> + CAP1126: https://ww1.microchip.com/downloads/en/DeviceDoc/00001623B.pdf
> + CAP1188: https://ww1.microchip.com/downloads/en/DeviceDoc/00001620C.pdf
> + CAP1203: https://ww1.microchip.com/downloads/en/DeviceDoc/00001572B.pdf
> + CAP1206: https://ww1.microchip.com/downloads/en/DeviceDoc/00001567B.pdf
> + CAP1293: https://ww1.microchip.com/downloads/en/DeviceDoc/00001566B.pdf
> + CAP1298: https://ww1.microchip.com/downloads/en/DeviceDoc/00001571B.pdf
> +
> maintainers:
> - Rob Herring <robh@kernel.org>
>
> @@ -124,14 +133,16 @@ properties:
> The number of entries must correspond to the number of channels.
>
> patternProperties:
> - "^led@[0-7]$":
> + "^led@[0-9a-f]$":
This should be done alongside the cap1114 change, not here I think. The
constraint relaxation doesn't make sense because the user is arriving in
a later patch.
With it moved,
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Although, should it not be led@[0-9a-b] if the max is 11?
pw-bot: changes-requested
Cheers,
Conor.
> type: object
> description: CAP11xx LEDs
> $ref: /schemas/leds/common.yaml#
>
> properties:
> reg:
> - enum: [0, 1, 2, 3, 4, 5, 6, 7]
> + description: LED channel number
> + minimum: 0
> + maximum: 7
>
> label: true
>
> @@ -158,7 +169,7 @@ allOf:
> - microchip,cap1298
> then:
> patternProperties:
> - "^led@[0-7]$": false
> + "^led@": false
>
> - if:
> properties:
> --
> 2.54.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v3 04/10] dt-bindings: input: microchip,cap11xx: Add microchip,cap1126 LED reg constraints
From: Conor Dooley @ 2026-06-15 16:24 UTC (permalink / raw)
To: Jun Yan
Cc: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-input, devicetree, linux-kernel
In-Reply-To: <20260615142103.352163-5-jerrysteve1101@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 75 bytes --]
Acked-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v3 09/10] dt-bindings: input: microchip,cap11xx: Add CAP1114 support
From: Conor Dooley @ 2026-06-15 16:24 UTC (permalink / raw)
To: Jun Yan
Cc: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-input, devicetree, linux-kernel
In-Reply-To: <20260615142103.352163-10-jerrysteve1101@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 78 bytes --]
Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v3 3/4] Input: snvs_pwrkey - use local device pointer to simple code
From: Frank Li @ 2026-06-15 14:43 UTC (permalink / raw)
To: joy.zou
Cc: Dmitry Torokhov, Frank Li, Bough Chen, Peng Fan, Jacky Bai, Ye Li,
imx, linux-input, linux-kernel, Joy Zou
In-Reply-To: <20260615-b4-pwrkey-v3-3-9510b1173f6e@oss.nxp.com>
On Mon, Jun 15, 2026 at 03:52:16PM +0800, joy.zou@oss.nxp.com wrote:
> [You don't often get email from joy.zou@oss.nxp.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> From: Joy Zou <joy.zou@nxp.com>
>
> Use local struct device pointer to avoid reference the platform_device
> pointer every time.
>
> No functional change.
>
> Signed-off-by: Joy Zou <joy.zou@nxp.com>
> ---
> Changes for v2:
> 1. Use dev instead of &pdev->dev for devm_input_allocate_device(),
> which was missed in patch v1 per AI review comments.
> 2. Modify commit message.
> ---
> drivers/input/keyboard/snvs_pwrkey.c | 41 ++++++++++++++++++------------------
> 1 file changed, 21 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c
> index a291812e6d22..4a1d04898482 100644
> --- a/drivers/input/keyboard/snvs_pwrkey.c
> +++ b/drivers/input/keyboard/snvs_pwrkey.c
> @@ -112,6 +112,7 @@ static void imx_snvs_pwrkey_act(void *pdata)
>
> static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
> {
> + struct device *dev = &pdev->dev;
> struct pwrkey_drv_data *pdata;
> struct input_dev *input;
> struct device_node *np;
> @@ -122,26 +123,26 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
> u32 vid;
>
> /* Get SNVS register Page */
> - np = pdev->dev.of_node;
> + np = dev->of_node;
> if (!np)
> - return dev_err_probe(&pdev->dev, -ENODEV, "Device tree node not found\n");
> + return dev_err_probe(dev, -ENODEV, "Device tree node not found\n");
>
> - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
> + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
> if (!pdata)
> return -ENOMEM;
>
> pdata->snvs = syscon_regmap_lookup_by_phandle(np, "regmap");
> if (IS_ERR(pdata->snvs))
> - return dev_err_probe(&pdev->dev, PTR_ERR(pdata->snvs), "Can't get snvs syscon\n");
> + return dev_err_probe(dev, PTR_ERR(pdata->snvs), "Can't get snvs syscon\n");
>
> if (of_property_read_u32(np, "linux,keycode", &pdata->keycode)) {
> pdata->keycode = KEY_POWER;
> - dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n");
> + dev_warn(dev, "KEY_POWER without setting in dts\n");
> }
>
> - clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
> + clk = devm_clk_get_optional_enabled(dev, NULL);
> if (IS_ERR(clk))
> - return dev_err_probe(&pdev->dev, PTR_ERR(clk),
> + return dev_err_probe(dev, PTR_ERR(clk),
> "Failed to get snvs clock (%pe)\n", clk);
>
> pdata->wakeup = of_property_read_bool(np, "wakeup-source");
> @@ -162,7 +163,7 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
> bpt = (val / 5) - 1;
> break;
> default:
> - return dev_err_probe(&pdev->dev, -EINVAL,
> + return dev_err_probe(dev, -EINVAL,
> "power-off-time-sec %d out of range\n", val);
> }
>
> @@ -180,9 +181,9 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
>
> timer_setup(&pdata->check_timer, imx_imx_snvs_check_for_events, 0);
>
> - input = devm_input_allocate_device(&pdev->dev);
> + input = devm_input_allocate_device(dev);
> if (!input) {
> - dev_err(&pdev->dev, "failed to allocate the input device\n");
> + dev_err(dev, "failed to allocate the input device\n");
> return -ENOMEM;
look like you missed this one changeing to dev_err_probe() at patch 1.
Frank
> }
>
> @@ -193,27 +194,27 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
> input_set_capability(input, EV_KEY, pdata->keycode);
>
> /* input customer action to cancel release timer */
> - error = devm_add_action(&pdev->dev, imx_snvs_pwrkey_act, pdata);
> + error = devm_add_action(dev, imx_snvs_pwrkey_act, pdata);
> if (error)
> - return dev_err_probe(&pdev->dev, error, "failed to register remove action\n");
> + return dev_err_probe(dev, error, "failed to register remove action\n");
>
> pdata->input = input;
> platform_set_drvdata(pdev, pdata);
>
> - error = devm_request_irq(&pdev->dev, pdata->irq,
> - imx_snvs_pwrkey_interrupt,
> - 0, pdev->name, pdev);
> + error = devm_request_irq(dev, pdata->irq,
> + imx_snvs_pwrkey_interrupt,
> + 0, pdev->name, pdev);
> if (error)
> - return dev_err_probe(&pdev->dev, error, "interrupt not available.\n");
> + return dev_err_probe(dev, error, "interrupt not available.\n");
>
> error = input_register_device(input);
> if (error < 0)
> - return dev_err_probe(&pdev->dev, error, "failed to register input device\n");
> + return dev_err_probe(dev, error, "failed to register input device\n");
>
> - device_init_wakeup(&pdev->dev, pdata->wakeup);
> - error = dev_pm_set_wake_irq(&pdev->dev, pdata->irq);
> + device_init_wakeup(dev, pdata->wakeup);
> + error = dev_pm_set_wake_irq(dev, pdata->irq);
> if (error)
> - dev_err(&pdev->dev, "irq wake enable failed.\n");
> + dev_err(dev, "irq wake enable failed.\n");
>
> return 0;
> }
>
> --
> 2.34.1
>
>
^ permalink raw reply
* Re: [PATCH v3 3/4] Input: snvs_pwrkey - use local device pointer to simple code
From: Frank Li @ 2026-06-15 14:40 UTC (permalink / raw)
To: joy.zou
Cc: Dmitry Torokhov, Frank Li, Bough Chen, Peng Fan, Jacky Bai, Ye Li,
imx, linux-input, linux-kernel, Joy Zou
In-Reply-To: <20260615-b4-pwrkey-v3-3-9510b1173f6e@oss.nxp.com>
On Mon, Jun 15, 2026 at 03:52:16PM +0800, joy.zou@oss.nxp.com wrote:
>
> Use local struct device pointer to avoid reference the platform_device
> pointer every time.
>
> No functional change.
>
> Signed-off-by: Joy Zou <joy.zou@nxp.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> Changes for v2:
> 1. Use dev instead of &pdev->dev for devm_input_allocate_device(),
> which was missed in patch v1 per AI review comments.
> 2. Modify commit message.
> ---
> drivers/input/keyboard/snvs_pwrkey.c | 41 ++++++++++++++++++------------------
> 1 file changed, 21 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c
> index a291812e6d22..4a1d04898482 100644
> --- a/drivers/input/keyboard/snvs_pwrkey.c
> +++ b/drivers/input/keyboard/snvs_pwrkey.c
> @@ -112,6 +112,7 @@ static void imx_snvs_pwrkey_act(void *pdata)
>
> static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
> {
> + struct device *dev = &pdev->dev;
> struct pwrkey_drv_data *pdata;
> struct input_dev *input;
> struct device_node *np;
> @@ -122,26 +123,26 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
> u32 vid;
>
> /* Get SNVS register Page */
> - np = pdev->dev.of_node;
> + np = dev->of_node;
> if (!np)
> - return dev_err_probe(&pdev->dev, -ENODEV, "Device tree node not found\n");
> + return dev_err_probe(dev, -ENODEV, "Device tree node not found\n");
>
> - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
> + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
> if (!pdata)
> return -ENOMEM;
>
> pdata->snvs = syscon_regmap_lookup_by_phandle(np, "regmap");
> if (IS_ERR(pdata->snvs))
> - return dev_err_probe(&pdev->dev, PTR_ERR(pdata->snvs), "Can't get snvs syscon\n");
> + return dev_err_probe(dev, PTR_ERR(pdata->snvs), "Can't get snvs syscon\n");
>
> if (of_property_read_u32(np, "linux,keycode", &pdata->keycode)) {
> pdata->keycode = KEY_POWER;
> - dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n");
> + dev_warn(dev, "KEY_POWER without setting in dts\n");
> }
>
> - clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
> + clk = devm_clk_get_optional_enabled(dev, NULL);
> if (IS_ERR(clk))
> - return dev_err_probe(&pdev->dev, PTR_ERR(clk),
> + return dev_err_probe(dev, PTR_ERR(clk),
> "Failed to get snvs clock (%pe)\n", clk);
>
> pdata->wakeup = of_property_read_bool(np, "wakeup-source");
> @@ -162,7 +163,7 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
> bpt = (val / 5) - 1;
> break;
> default:
> - return dev_err_probe(&pdev->dev, -EINVAL,
> + return dev_err_probe(dev, -EINVAL,
> "power-off-time-sec %d out of range\n", val);
> }
>
> @@ -180,9 +181,9 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
>
> timer_setup(&pdata->check_timer, imx_imx_snvs_check_for_events, 0);
>
> - input = devm_input_allocate_device(&pdev->dev);
> + input = devm_input_allocate_device(dev);
> if (!input) {
> - dev_err(&pdev->dev, "failed to allocate the input device\n");
> + dev_err(dev, "failed to allocate the input device\n");
> return -ENOMEM;
> }
>
> @@ -193,27 +194,27 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
> input_set_capability(input, EV_KEY, pdata->keycode);
>
> /* input customer action to cancel release timer */
> - error = devm_add_action(&pdev->dev, imx_snvs_pwrkey_act, pdata);
> + error = devm_add_action(dev, imx_snvs_pwrkey_act, pdata);
> if (error)
> - return dev_err_probe(&pdev->dev, error, "failed to register remove action\n");
> + return dev_err_probe(dev, error, "failed to register remove action\n");
>
> pdata->input = input;
> platform_set_drvdata(pdev, pdata);
>
> - error = devm_request_irq(&pdev->dev, pdata->irq,
> - imx_snvs_pwrkey_interrupt,
> - 0, pdev->name, pdev);
> + error = devm_request_irq(dev, pdata->irq,
> + imx_snvs_pwrkey_interrupt,
> + 0, pdev->name, pdev);
> if (error)
> - return dev_err_probe(&pdev->dev, error, "interrupt not available.\n");
> + return dev_err_probe(dev, error, "interrupt not available.\n");
>
> error = input_register_device(input);
> if (error < 0)
> - return dev_err_probe(&pdev->dev, error, "failed to register input device\n");
> + return dev_err_probe(dev, error, "failed to register input device\n");
>
> - device_init_wakeup(&pdev->dev, pdata->wakeup);
> - error = dev_pm_set_wake_irq(&pdev->dev, pdata->irq);
> + device_init_wakeup(dev, pdata->wakeup);
> + error = dev_pm_set_wake_irq(dev, pdata->irq);
> if (error)
> - dev_err(&pdev->dev, "irq wake enable failed.\n");
> + dev_err(dev, "irq wake enable failed.\n");
>
> return 0;
> }
>
> --
> 2.34.1
>
>
^ permalink raw reply
* Re: [PATCH v3 2/4] Input: snvs_pwrkey - replace hardcoding -EINVAL with original error code
From: Frank Li @ 2026-06-15 14:38 UTC (permalink / raw)
To: joy.zou
Cc: Dmitry Torokhov, Frank Li, Bough Chen, Peng Fan, Jacky Bai, Ye Li,
imx, linux-input, linux-kernel, Joy Zou
In-Reply-To: <20260615-b4-pwrkey-v3-2-9510b1173f6e@oss.nxp.com>
On Mon, Jun 15, 2026 at 03:52:15PM +0800, joy.zou@oss.nxp.com wrote:
Nit: subject
Input: snvs_pwrkey: propagate error code of platform_get_irq()
>
> Hardcoding -EINVAL discards the actual error code, which breaks probe
> deferral (-EPROBE_DEFER) and loses critical diagnostic information
> needed for proper kernel error handling.
>
> Signed-off-by: Joy Zou <joy.zou@nxp.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/input/keyboard/snvs_pwrkey.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c
> index 748196fcce75..a291812e6d22 100644
> --- a/drivers/input/keyboard/snvs_pwrkey.c
> +++ b/drivers/input/keyboard/snvs_pwrkey.c
> @@ -148,7 +148,7 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
>
> pdata->irq = platform_get_irq(pdev, 0);
> if (pdata->irq < 0)
> - return -EINVAL;
> + return pdata->irq;
>
> error = of_property_read_u32(np, "power-off-time-sec", &val);
> if (!error) {
>
> --
> 2.34.1
>
>
^ permalink raw reply
* Re: [PATCH v3 02/10] Input: cap11xx - remove unused register macros
From: sashiko-bot @ 2026-06-15 14:34 UTC (permalink / raw)
To: Jun Yan; +Cc: conor+dt, dmitry.torokhov, robh, devicetree, linux-input
In-Reply-To: <20260615142103.352163-3-jerrysteve1101@gmail.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Medium] Removing sensor delta registers from the volatile register list causes regmap to incorrectly cache their values, breaking debugfs observability.
Pre-existing issues:
- [High] The driver requests its IRQ after registering the input device, creating a race condition where an unhandled interrupt can permanently disable the device.
--
commit 72bbdd7bb3155f054afabfd004f5e097d568001b
Author: Jun Yan <jerrysteve1101@gmail.com>
Input: cap11xx - remove unused register macros
Remove unused register address macros and unused definitions in
the cap11xx_reg_defaults array and cap11xx_volatile_reg. This cleanup
reduces code clutter and makes the driver easier to maintain.
> diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c
> index 485d8ba977235..686174722204c 100644
> --- a/drivers/input/keyboard/cap11xx.c
> +++ b/drivers/input/keyboard/cap11xx.c
[ ... ]
> @@ -149,12 +97,6 @@ static bool cap11xx_volatile_reg(struct device *dev, unsigned int reg)
> switch (reg) {
> case CAP11XX_REG_MAIN_CONTROL:
> case CAP11XX_REG_SENSOR_INPUT:
> - case CAP11XX_REG_SENOR_DELTA(0):
> - case CAP11XX_REG_SENOR_DELTA(1):
> - case CAP11XX_REG_SENOR_DELTA(2):
> - case CAP11XX_REG_SENOR_DELTA(3):
> - case CAP11XX_REG_SENOR_DELTA(4):
> - case CAP11XX_REG_SENOR_DELTA(5):
[Severity: Medium]
Does removing the sensor delta registers from the volatile list cause a
regression for observability via the regmap debugfs interface?
These registers contain real-time capacitance delta hardware values that are
continuously updated by the device. Treating them as non-volatile causes
regmap to cache them on the first read, resulting in stale values for any
subsequent reads by tuning tools or developers.
> return true;
> }
>
[Severity: High]
This isn't a bug introduced by this patch, but I noticed a pre-existing probe
ordering flaw in cap11xx_i2c_probe():
error = input_register_device(priv->idev);
if (error)
return error;
error = devm_request_threaded_irq(dev, i2c_client->irq,
NULL, cap11xx_thread_func,
IRQF_ONESHOT, dev_name(dev), priv);
Could this sequence lead to the interrupt being permanently disabled?
Since input_register_device() exposes the device to userspace, a daemon
can immediately open the device, waking the sensor from deep sleep.
If the sensor generates a hardware interrupt before
devm_request_threaded_irq() completes, the generic IRQ core will receive an
interrupt with no registered handler.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260615142103.352163-1-jerrysteve1101@gmail.com?part=2
^ permalink raw reply
* Re: [PATCH v3 06/10] Input: cap11xx - add reset gpio support
From: sashiko-bot @ 2026-06-15 14:29 UTC (permalink / raw)
To: Jun Yan; +Cc: linux-input, robh, dmitry.torokhov, conor+dt, devicetree
In-Reply-To: <20260615142103.352163-7-jerrysteve1101@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Coding style violations: redundant 'else' after 'return' and inconsistent brace usage.
--
commit 2a7317102372f10d4bd76864cfeaec2d7955c415
Author: Jun Yan <jerrysteve1101@gmail.com>
Input: cap11xx - add reset gpio support
This commit adds support for an optional hardware reset pin on CAP11xx
devices to improve reliability and ensure proper initialization during probe.
> diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c
> --- a/drivers/input/keyboard/cap11xx.c
> +++ b/drivers/input/keyboard/cap11xx.c
[ ... ]
> @@ -452,6 +457,16 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client)
> if (IS_ERR(priv->regmap))
> return PTR_ERR(priv->regmap);
>
> + priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
> + if (IS_ERR(priv->reset_gpio))
> + return dev_err_probe(dev, PTR_ERR(priv->reset_gpio),
> + "Failed to get 'reset' GPIO\n");
> + else if (priv->reset_gpio) {
[Severity: Low]
This isn't a bug, but does this follow the standard kernel coding style?
Since the preceding if statement ends with a return, the else keyword here
is redundant. Additionally, kernel style guidelines generally prefer that if
one branch of a conditional requires braces, all branches should use them.
Could this be simplified by dropping the else and just using a separate if
statement for checking priv->reset_gpio?
> + usleep_range(CAP11XX_T_RST_FILT_MIN_US, CAP11XX_T_RST_FILT_MIN_US * 2);
> + gpiod_set_value_cansleep(priv->reset_gpio, 0);
> + msleep(CAP11XX_T_RST_ON_MIN_MS);
> + }
> +
> error = regmap_read(priv->regmap, CAP11XX_REG_PRODUCT_ID, &val);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260615142103.352163-1-jerrysteve1101@gmail.com?part=6
^ permalink raw reply
* [PATCH v3 10/10] Input: cap11xx - add support for CAP1114
From: Jun Yan @ 2026-06-15 14:20 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Jun Yan, linux-input, devicetree, linux-kernel
In-Reply-To: <20260615142103.352163-1-jerrysteve1101@gmail.com>
CAP1114 is a 14-channel capacitive touch sensor with 11 LED outputs
and hardware reset support.
The CAP1114 uses two control registers for LED output management and
requires two button status registers for touch input state reporting.
By default, channels CS8~CS14 operate as a single grouped block.
Set the corresponding register enable bit to enable these channels as
independent touch inputs. Note these channels share the input threshold
of the eighth entry, causing num_sensor_thresholds to differ from
num_channels.
Signed-off-by: Jun Yan <jerrysteve1101@gmail.com>
---
drivers/input/keyboard/cap11xx.c | 73 ++++++++++++++++++++++++++++++--
1 file changed, 69 insertions(+), 4 deletions(-)
diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c
index c48ee8520a27..187a212dfa8f 100644
--- a/drivers/input/keyboard/cap11xx.c
+++ b/drivers/input/keyboard/cap11xx.c
@@ -18,6 +18,12 @@
#include <linux/gpio/consumer.h>
#include <linux/bitfield.h>
+#define CAP1114_REG_BUTTON_STATUS1 0x03
+#define CAP1114_REG_BUTTON_STATUS2 0x04
+#define CAP1114_REG_CONFIG2 0x40
+#define CAP1114_REG_CONFIG2_VOL_UP_DOWN BIT(1)
+#define CAP1114_REG_LED_OUTPUT_CONTROL1 0x73
+
#define CAP11XX_REG_MAIN_CONTROL 0x00
#define CAP11XX_REG_MAIN_CONTROL_GAIN_SHIFT (6)
#define CAP11XX_REG_MAIN_CONTROL_GAIN_MASK (0xc0)
@@ -82,6 +88,7 @@ struct cap11xx_hw_model {
unsigned int num_leds;
unsigned int num_sensor_thresholds;
bool has_gain;
+ bool has_grouped_sensors;
bool has_irq_config;
bool has_repeat_en;
bool has_sensitivity_control;
@@ -98,6 +105,8 @@ static const struct reg_default cap11xx_reg_defaults[] = {
{ CAP11XX_REG_SENSOR_THRESH(3), 0x40 },
{ CAP11XX_REG_SENSOR_THRESH(4), 0x40 },
{ CAP11XX_REG_SENSOR_THRESH(5), 0x40 },
+ { CAP11XX_REG_SENSOR_THRESH(6), 0x40 },
+ { CAP11XX_REG_SENSOR_THRESH(7), 0x40 },
{ CAP11XX_REG_CONFIG2, 0x40 },
};
@@ -106,6 +115,12 @@ static bool cap11xx_volatile_reg(struct device *dev, unsigned int reg)
switch (reg) {
case CAP11XX_REG_MAIN_CONTROL:
case CAP11XX_REG_SENSOR_INPUT:
+ /*
+ * CAP1114_REG_BUTTON_STATUS1 (CAP11XX_REG_SENSOR_INPUT) and
+ * CAP1114_REG_BUTTON_STATUS2 is volatile for the CAP1114,
+ * which supports more than 8 touch channels.
+ */
+ case CAP1114_REG_BUTTON_STATUS2:
return true;
}
@@ -285,6 +300,17 @@ static int cap11xx_init_keys(struct cap11xx_priv *priv)
of_property_read_u32_array(node, "linux,keycodes",
priv->keycodes, priv->model->num_channels);
+ /*
+ * CAP1114 needs dedicated configuration to split
+ * grouped sensors into independent inputs.
+ */
+ if (priv->model->has_grouped_sensors) {
+ error = regmap_set_bits(priv->regmap, CAP1114_REG_CONFIG2,
+ CAP1114_REG_CONFIG2_VOL_UP_DOWN);
+ if (error)
+ return error;
+ }
+
if (priv->model->has_repeat_en) {
/* Disable autorepeat. The Linux input system has its own handling. */
error = regmap_write(priv->regmap, CAP11XX_REG_REPEAT_RATE, 0);
@@ -313,6 +339,21 @@ static irqreturn_t cap11xx_thread_func(int irq_num, void *data)
if (ret < 0)
goto out;
+ if (priv->model->num_channels > 8) {
+ unsigned int status2;
+
+ ret = regmap_read(priv->regmap, priv->model->sensor_input_reg_base + 1, &status2);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * CAP1114 STATUS1 register only contains data for the first 6 channels.
+ * the remaining channels is stored in STATUS2.
+ */
+ status &= GENMASK(5, 0);
+ status |= FIELD_PREP(GENMASK(13, 6), status2);
+ }
+
for (i = 0; i < priv->idev->keycodemax; i++)
input_report_key(priv->idev, priv->keycodes[i],
status & (1 << i));
@@ -362,10 +403,16 @@ static int cap11xx_led_set(struct led_classdev *cdev,
* limitation. Brightness levels per LED are either
* 0 (OFF) and 1 (ON).
*/
- return regmap_update_bits(priv->regmap,
- priv->model->led_output_control_reg_base,
- BIT(led->reg),
- value ? BIT(led->reg) : 0);
+ if (led->reg >= 8)
+ return regmap_update_bits(priv->regmap,
+ priv->model->led_output_control_reg_base + 1,
+ BIT(led->reg - 8),
+ value ? BIT(led->reg - 8) : 0);
+ else
+ return regmap_update_bits(priv->regmap,
+ priv->model->led_output_control_reg_base,
+ BIT(led->reg),
+ value ? BIT(led->reg) : 0);
}
static int cap11xx_init_leds(struct device *dev,
@@ -396,6 +443,14 @@ static int cap11xx_init_leds(struct device *dev,
if (error)
return error;
+ if (num_leds > 8) {
+ error = regmap_update_bits(priv->regmap,
+ priv->model->led_output_control_reg_base + 1,
+ GENMASK(num_leds - 8 - 1, 0), 0);
+ if (error)
+ return error;
+ }
+
duty_val = FIELD_PREP(CAP11XX_REG_LED_DUTY_MAX_MASK,
CAP11XX_REG_LED_DUTY_MAX_VALUE);
@@ -573,6 +628,14 @@ static const struct cap11xx_hw_model cap1106_model = {
.has_repeat_en = true,
};
+static const struct cap11xx_hw_model cap1114_model = {
+ .product_id = 0x3a,
+ .num_channels = 14, .num_leds = 11, .num_sensor_thresholds = 8,
+ .led_output_control_reg_base = CAP1114_REG_LED_OUTPUT_CONTROL1,
+ .sensor_input_reg_base = CAP1114_REG_BUTTON_STATUS1,
+ .has_grouped_sensors = true,
+};
+
static const struct cap11xx_hw_model cap1126_model = {
.product_id = 0x53,
.num_channels = 6, .num_leds = 2, .num_sensor_thresholds = 6,
@@ -629,6 +692,7 @@ static const struct cap11xx_hw_model cap1298_model = {
static const struct of_device_id cap11xx_dt_ids[] = {
{ .compatible = "microchip,cap1106", .data = &cap1106_model },
+ { .compatible = "microchip,cap1114", .data = &cap1114_model },
{ .compatible = "microchip,cap1126", .data = &cap1126_model },
{ .compatible = "microchip,cap1188", .data = &cap1188_model },
{ .compatible = "microchip,cap1203", .data = &cap1203_model },
@@ -641,6 +705,7 @@ MODULE_DEVICE_TABLE(of, cap11xx_dt_ids);
static const struct i2c_device_id cap11xx_i2c_ids[] = {
{ .name = "cap1106", .driver_data = (kernel_ulong_t)&cap1106_model },
+ { .name = "cap1114", .driver_data = (kernel_ulong_t)&cap1114_model },
{ .name = "cap1126", .driver_data = (kernel_ulong_t)&cap1126_model },
{ .name = "cap1188", .driver_data = (kernel_ulong_t)&cap1188_model },
{ .name = "cap1203", .driver_data = (kernel_ulong_t)&cap1203_model },
--
2.54.0
^ permalink raw reply related
* [PATCH v3 09/10] dt-bindings: input: microchip,cap11xx: Add CAP1114 support
From: Jun Yan @ 2026-06-15 14:20 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Jun Yan, linux-input, devicetree, linux-kernel
In-Reply-To: <20260615142103.352163-1-jerrysteve1101@gmail.com>
CAP1114 is a 14-channel capacitive touch sensor with 11 LED outputs
and hardware reset support.
Add the compatible string for CAP1114, add its datasheet URL,
update the maximum of LED channel reg, and add constraint for
linux,keycodes.
Previously, the LED reg property had a default maximum of 7 for CAP1188.
With the addition of CAP1114, the default maximum is now 11.
An if-then constraint is added to limit the LED count for CAP1188.
Update description for microchip,input-threshold: CAP1114 only provides
eight threshold entries, which does not match its total channel count.
CAP1114 does not support microchip,signal-guard and
microchip,calib-sensitivity.
Add CAP1114 to the unsupported enum list.
Signed-off-by: Jun Yan <jerrysteve1101@gmail.com>
---
.../bindings/input/microchip,cap11xx.yaml | 37 ++++++++++++++++++-
1 file changed, 35 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml b/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml
index 778ec6d659a8..153099d59d1a 100644
--- a/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml
+++ b/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml
@@ -12,6 +12,7 @@ description: |
For more product information please see the links below:
CAP1106: https://ww1.microchip.com/downloads/en/DeviceDoc/00001624B.pdf
+ CAP1114: https://ww1.microchip.com/downloads/en/DeviceDoc/00002444A.pdf
CAP1126: https://ww1.microchip.com/downloads/en/DeviceDoc/00001623B.pdf
CAP1188: https://ww1.microchip.com/downloads/en/DeviceDoc/00001620C.pdf
CAP1203: https://ww1.microchip.com/downloads/en/DeviceDoc/00001572B.pdf
@@ -26,6 +27,7 @@ properties:
compatible:
enum:
- microchip,cap1106
+ - microchip,cap1114
- microchip,cap1126
- microchip,cap1188
- microchip,cap1203
@@ -62,7 +64,7 @@ properties:
linux,keycodes:
minItems: 3
- maxItems: 8
+ maxItems: 14
description: |
Specifies an array of numeric keycode values to
be used for the channels. If this property is
@@ -122,6 +124,8 @@ properties:
is required for a touch to be registered, making the touch sensor less
sensitive.
The number of entries must correspond to the number of channels.
+ CAP1114 is an exception where channels 8~14 reuse the eighth entry's
+ threshold, so counts differ.
microchip,calib-sensitivity:
$ref: /schemas/types.yaml#/definitions/uint32-array
@@ -149,7 +153,7 @@ patternProperties:
reg:
description: LED channel number
minimum: 0
- maximum: 7
+ maximum: 10
label: true
@@ -178,6 +182,21 @@ allOf:
properties:
reset-gpios: false
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - microchip,cap1114
+ then:
+ properties:
+ linux,keycodes:
+ minItems: 14
+ else:
+ properties:
+ linux,keycodes:
+ maxItems: 8
+
- if:
properties:
compatible:
@@ -205,12 +224,26 @@ allOf:
reg:
maximum: 1
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - microchip,cap1188
+ then:
+ patternProperties:
+ "^led@":
+ properties:
+ reg:
+ maximum: 7
+
- if:
properties:
compatible:
contains:
enum:
- microchip,cap1106
+ - microchip,cap1114
- microchip,cap1126
- microchip,cap1188
- microchip,cap1203
--
2.54.0
^ permalink raw reply related
* [PATCH v3 08/10] Input: cap11xx - guard unsupported DT properties before parsing
From: Jun Yan @ 2026-06-15 14:20 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Jun Yan, linux-input, devicetree, linux-kernel
In-Reply-To: <20260615142103.352163-1-jerrysteve1101@gmail.com>
Check of_property_present() before parsing microchip,calib-sensitivity
and microchip,signal-guard, so that models which do not support these
properties (e.g. CAP1114) skip the parsing entirely.
This prevents a potential buffer overflow in calib_sensitivities[8] and
signal_guard_inputs_mask when a model with more than 8 channels
(CAP1114 has 14) would otherwise call of_property_read_u32_array()
with num_channels as the element count.
Signed-off-by: Jun Yan <jerrysteve1101@gmail.com>
---
drivers/input/keyboard/cap11xx.c | 52 +++++++++++++++++---------------
1 file changed, 27 insertions(+), 25 deletions(-)
diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c
index 2e9382a721e9..c48ee8520a27 100644
--- a/drivers/input/keyboard/cap11xx.c
+++ b/drivers/input/keyboard/cap11xx.c
@@ -224,10 +224,13 @@ static int cap11xx_init_keys(struct cap11xx_priv *priv)
}
}
- if (!of_property_read_u32_array(node, "microchip,calib-sensitivity",
- priv->calib_sensitivities,
- priv->model->num_channels)) {
- if (priv->model->has_sensitivity_control) {
+ if (of_property_present(node, "microchip,calib-sensitivity")) {
+ if (!priv->model->has_sensitivity_control) {
+ dev_warn(dev,
+ "This model doesn't support 'calib-sensitivity'\n");
+ } else if (!of_property_read_u32_array(node, "microchip,calib-sensitivity",
+ priv->calib_sensitivities,
+ priv->model->num_channels)) {
for (i = 0; i < priv->model->num_channels; i++) {
if (!is_power_of_2(priv->calib_sensitivities[i]) ||
priv->calib_sensitivities[i] > 4) {
@@ -247,32 +250,31 @@ static int cap11xx_init_keys(struct cap11xx_priv *priv)
if (error)
return error;
}
- } else {
- dev_warn(dev,
- "This model doesn't support 'calib-sensitivity'\n");
}
}
- for (i = 0; i < priv->model->num_channels; i++) {
- if (!of_property_read_u32_index(node, "microchip,signal-guard",
- i, &u32_val)) {
- if (u32_val > 1)
- return -EINVAL;
- if (u32_val)
- priv->signal_guard_inputs_mask |= 0x01 << i;
- }
- }
-
- if (priv->signal_guard_inputs_mask) {
- if (priv->model->has_signal_guard) {
- error = regmap_write(priv->regmap,
- CAP11XX_REG_SIGNAL_GUARD_ENABLE,
- priv->signal_guard_inputs_mask);
- if (error)
- return error;
- } else {
+ if (of_property_present(node, "microchip,signal-guard")) {
+ if (!priv->model->has_signal_guard) {
dev_warn(dev,
"This model doesn't support 'signal-guard'\n");
+ } else {
+ for (i = 0; i < priv->model->num_channels; i++) {
+ if (!of_property_read_u32_index(node, "microchip,signal-guard",
+ i, &u32_val)) {
+ if (u32_val > 1)
+ return -EINVAL;
+ if (u32_val)
+ priv->signal_guard_inputs_mask |= 0x01 << i;
+ }
+ }
+
+ if (priv->signal_guard_inputs_mask) {
+ error = regmap_write(priv->regmap,
+ CAP11XX_REG_SIGNAL_GUARD_ENABLE,
+ priv->signal_guard_inputs_mask);
+ if (error)
+ return error;
+ }
}
}
--
2.54.0
^ permalink raw reply related
* [PATCH v3 07/10] Input: cap11xx - refactor code for better CAP1114 support.
From: Jun Yan @ 2026-06-15 14:20 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Jun Yan, linux-input, devicetree, linux-kernel
In-Reply-To: <20260615142103.352163-1-jerrysteve1101@gmail.com>
Extend cap11xx_hw_model structure to support CAP1114 with
different register offsets and hardware characteristics:
- led_output_control_reg_base: different address on CAP1114
- sensor_input_reg_base: different address on CAP1114
- num_sensor_thresholds: separate value from num_channels for CAP1114
- has_repeat_en: repeat enable support, disabled by default on CAP1114
Include linux/bits.h, update the register operations related to LEDs.
Signed-off-by: Jun Yan <jerrysteve1101@gmail.com>
---
drivers/input/keyboard/cap11xx.c | 73 +++++++++++++++++++++++---------
1 file changed, 53 insertions(+), 20 deletions(-)
diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c
index 3d75c0f90752..2e9382a721e9 100644
--- a/drivers/input/keyboard/cap11xx.c
+++ b/drivers/input/keyboard/cap11xx.c
@@ -5,6 +5,7 @@
* (c) 2014 Daniel Mack <linux@zonque.org>
*/
+#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -35,7 +36,6 @@
#define CAP11XX_REG_LED_DUTY_CYCLE_4 0x93
#define CAP11XX_REG_LED_DUTY_MAX_MASK (0xf0)
-#define CAP11XX_REG_LED_DUTY_MAX_MASK_SHIFT (4)
#define CAP11XX_REG_LED_DUTY_MAX_VALUE (15)
#define CAP11XX_REG_PRODUCT_ID 0xfd
@@ -76,10 +76,14 @@ struct cap11xx_priv {
struct cap11xx_hw_model {
u8 product_id;
+ u8 led_output_control_reg_base;
+ u8 sensor_input_reg_base;
unsigned int num_channels;
unsigned int num_leds;
+ unsigned int num_sensor_thresholds;
bool has_gain;
bool has_irq_config;
+ bool has_repeat_en;
bool has_sensitivity_control;
bool has_signal_guard;
};
@@ -204,8 +208,8 @@ static int cap11xx_init_keys(struct cap11xx_priv *priv)
}
if (!of_property_read_u32_array(node, "microchip,input-threshold",
- priv->thresholds, priv->model->num_channels)) {
- for (i = 0; i < priv->model->num_channels; i++) {
+ priv->thresholds, priv->model->num_sensor_thresholds)) {
+ for (i = 0; i < priv->model->num_sensor_thresholds; i++) {
if (priv->thresholds[i] > 127) {
dev_err(dev, "Invalid input-threshold value %u\n",
priv->thresholds[i]);
@@ -279,10 +283,12 @@ static int cap11xx_init_keys(struct cap11xx_priv *priv)
of_property_read_u32_array(node, "linux,keycodes",
priv->keycodes, priv->model->num_channels);
- /* Disable autorepeat. The Linux input system has its own handling. */
- error = regmap_write(priv->regmap, CAP11XX_REG_REPEAT_RATE, 0);
- if (error)
- return error;
+ if (priv->model->has_repeat_en) {
+ /* Disable autorepeat. The Linux input system has its own handling. */
+ error = regmap_write(priv->regmap, CAP11XX_REG_REPEAT_RATE, 0);
+ if (error)
+ return error;
+ }
return 0;
}
@@ -301,7 +307,7 @@ static irqreturn_t cap11xx_thread_func(int irq_num, void *data)
if (ret < 0)
goto out;
- ret = regmap_read(priv->regmap, CAP11XX_REG_SENSOR_INPUT, &status);
+ ret = regmap_read(priv->regmap, priv->model->sensor_input_reg_base, &status);
if (ret < 0)
goto out;
@@ -355,7 +361,7 @@ static int cap11xx_led_set(struct led_classdev *cdev,
* 0 (OFF) and 1 (ON).
*/
return regmap_update_bits(priv->regmap,
- CAP11XX_REG_LED_OUTPUT_CONTROL,
+ priv->model->led_output_control_reg_base,
BIT(led->reg),
value ? BIT(led->reg) : 0);
}
@@ -367,6 +373,7 @@ static int cap11xx_init_leds(struct device *dev,
struct cap11xx_led *led;
int cnt = of_get_child_count(node);
int error;
+ u32 duty_val;
if (!num_leds || !cnt)
return 0;
@@ -380,15 +387,18 @@ static int cap11xx_init_leds(struct device *dev,
priv->leds = led;
+ /* Set all LEDs to off */
error = regmap_update_bits(priv->regmap,
- CAP11XX_REG_LED_OUTPUT_CONTROL, 0xff, 0);
+ priv->model->led_output_control_reg_base,
+ GENMASK(min(num_leds, 8) - 1, 0), 0);
if (error)
return error;
+ duty_val = FIELD_PREP(CAP11XX_REG_LED_DUTY_MAX_MASK,
+ CAP11XX_REG_LED_DUTY_MAX_VALUE);
+
error = regmap_update_bits(priv->regmap, CAP11XX_REG_LED_DUTY_CYCLE_4,
- CAP11XX_REG_LED_DUTY_MAX_MASK,
- CAP11XX_REG_LED_DUTY_MAX_VALUE <<
- CAP11XX_REG_LED_DUTY_MAX_MASK_SHIFT);
+ CAP11XX_REG_LED_DUTY_MAX_MASK, duty_val);
if (error)
return error;
@@ -553,41 +563,64 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client)
}
static const struct cap11xx_hw_model cap1106_model = {
- .product_id = 0x55, .num_channels = 6, .num_leds = 0,
+ .product_id = 0x55,
+ .num_channels = 6, .num_leds = 0, .num_sensor_thresholds = 6,
+ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT,
.has_gain = true,
.has_irq_config = true,
+ .has_repeat_en = true,
};
static const struct cap11xx_hw_model cap1126_model = {
- .product_id = 0x53, .num_channels = 6, .num_leds = 2,
+ .product_id = 0x53,
+ .num_channels = 6, .num_leds = 2, .num_sensor_thresholds = 6,
+ .led_output_control_reg_base = CAP11XX_REG_LED_OUTPUT_CONTROL,
+ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT,
.has_gain = true,
.has_irq_config = true,
+ .has_repeat_en = true,
};
static const struct cap11xx_hw_model cap1188_model = {
- .product_id = 0x50, .num_channels = 8, .num_leds = 8,
+ .product_id = 0x50,
+ .num_channels = 8, .num_leds = 8, .num_sensor_thresholds = 8,
+ .led_output_control_reg_base = CAP11XX_REG_LED_OUTPUT_CONTROL,
+ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT,
.has_gain = true,
.has_irq_config = true,
+ .has_repeat_en = true,
};
static const struct cap11xx_hw_model cap1203_model = {
- .product_id = 0x6d, .num_channels = 3, .num_leds = 0,
+ .product_id = 0x6d,
+ .num_channels = 3, .num_leds = 0, .num_sensor_thresholds = 3,
+ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT,
+ .has_repeat_en = true,
};
static const struct cap11xx_hw_model cap1206_model = {
- .product_id = 0x67, .num_channels = 6, .num_leds = 0,
+ .product_id = 0x67,
+ .num_channels = 6, .num_leds = 0, .num_sensor_thresholds = 6,
+ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT,
+ .has_repeat_en = true,
};
static const struct cap11xx_hw_model cap1293_model = {
- .product_id = 0x6f, .num_channels = 3, .num_leds = 0,
+ .product_id = 0x6f,
+ .num_channels = 3, .num_leds = 0, .num_sensor_thresholds = 3,
+ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT,
.has_gain = true,
+ .has_repeat_en = true,
.has_sensitivity_control = true,
.has_signal_guard = true,
};
static const struct cap11xx_hw_model cap1298_model = {
- .product_id = 0x71, .num_channels = 8, .num_leds = 0,
+ .product_id = 0x71,
+ .num_channels = 8, .num_leds = 0, .num_sensor_thresholds = 8,
+ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT,
.has_gain = true,
+ .has_repeat_en = true,
.has_sensitivity_control = true,
.has_signal_guard = true,
};
--
2.54.0
^ permalink raw reply related
* [PATCH v3 06/10] Input: cap11xx - add reset gpio support
From: Jun Yan @ 2026-06-15 14:20 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Jun Yan, linux-input, devicetree, linux-kernel
In-Reply-To: <20260615142103.352163-1-jerrysteve1101@gmail.com>
Some CAP11xx devices (CAP1126/CAP1188) have a dedicated RESET pin.
Add hardware reset operation to improve device reliability and
ensure proper initialization on probe.
Signed-off-by: Jun Yan <jerrysteve1101@gmail.com>
---
drivers/input/keyboard/cap11xx.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c
index 686174722204..3d75c0f90752 100644
--- a/drivers/input/keyboard/cap11xx.c
+++ b/drivers/input/keyboard/cap11xx.c
@@ -5,6 +5,7 @@
* (c) 2014 Daniel Mack <linux@zonque.org>
*/
+#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
@@ -43,6 +44,9 @@
#define CAP11XX_MANUFACTURER_ID 0x5d
+#define CAP11XX_T_RST_FILT_MIN_US 10000
+#define CAP11XX_T_RST_ON_MIN_MS 400
+
#ifdef CONFIG_LEDS_CLASS
struct cap11xx_led {
struct cap11xx_priv *priv;
@@ -55,6 +59,7 @@ struct cap11xx_priv {
struct regmap *regmap;
struct device *dev;
struct input_dev *idev;
+ struct gpio_desc *reset_gpio;
const struct cap11xx_hw_model *model;
struct cap11xx_led *leds;
@@ -452,6 +457,16 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client)
if (IS_ERR(priv->regmap))
return PTR_ERR(priv->regmap);
+ priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(priv->reset_gpio),
+ "Failed to get 'reset' GPIO\n");
+ else if (priv->reset_gpio) {
+ usleep_range(CAP11XX_T_RST_FILT_MIN_US, CAP11XX_T_RST_FILT_MIN_US * 2);
+ gpiod_set_value_cansleep(priv->reset_gpio, 0);
+ msleep(CAP11XX_T_RST_ON_MIN_MS);
+ }
+
error = regmap_read(priv->regmap, CAP11XX_REG_PRODUCT_ID, &val);
if (error)
return dev_err_probe(dev, error, "Failed to read product ID\n");
--
2.54.0
^ permalink raw reply related
* [PATCH v3 05/10] dt-bindings: input: microchip,cap11xx: Add reset-gpios property
From: Jun Yan @ 2026-06-15 14:20 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Jun Yan, Conor Dooley, linux-input, devicetree, linux-kernel
In-Reply-To: <20260615142103.352163-1-jerrysteve1101@gmail.com>
Add support for the optional reset-gpios property to describe
the active-high reset pin for CAP1126/CAP1188 devices.
Driving the GPIO high asserts reset and deep sleep, while driving
it low releases reset for normal operation.
Restrict this property to be available only on CAP1126 and CAP1188
chips, as other CAP11xx variants do not have a hardware reset pin.
Signed-off-by: Jun Yan <jerrysteve1101@gmail.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
---
.../bindings/input/microchip,cap11xx.yaml | 25 +++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml b/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml
index 22a292d4a880..778ec6d659a8 100644
--- a/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml
+++ b/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml
@@ -49,6 +49,13 @@ properties:
device's ALERT#/CM_IRQ# pin is connected to.
The device only has one interrupt source.
+ reset-gpios:
+ description: |
+ GPIO connected to the active-high RESET pin of the chip;
+ driving it high asserts reset and deep sleep, while driving
+ it low releases reset for normal operation.
+ maxItems: 1
+
autorepeat:
description: |
Enables the Linux input system's autorepeat feature on the input device.
@@ -157,6 +164,20 @@ patternProperties:
allOf:
- $ref: input.yaml
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - microchip,cap1106
+ - microchip,cap1203
+ - microchip,cap1206
+ - microchip,cap1293
+ - microchip,cap1298
+ then:
+ properties:
+ reset-gpios: false
+
- if:
properties:
compatible:
@@ -207,6 +228,8 @@ additionalProperties: false
examples:
- |
+ #include <dt-bindings/gpio/gpio.h>
+
i2c {
#address-cells = <1>;
#size-cells = <0>;
@@ -228,6 +251,8 @@ examples:
<109>, /* KEY_PAGEDOWN */
<104>; /* KEY_PAGEUP */
+ reset-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>;
+
#address-cells = <1>;
#size-cells = <0>;
--
2.54.0
^ permalink raw reply related
* [PATCH v3 04/10] dt-bindings: input: microchip,cap11xx: Add microchip,cap1126 LED reg constraints
From: Jun Yan @ 2026-06-15 14:20 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Jun Yan, linux-input, devicetree, linux-kernel
In-Reply-To: <20260615142103.352163-1-jerrysteve1101@gmail.com>
Apply per-chip LED channel limits:
- CAP1126: max 2 channels (0-1)
- CAP1188: max 8 channels (0-7)
- CAP1106, CAP12xx: no LED support
Signed-off-by: Jun Yan <jerrysteve1101@gmail.com>
---
.../bindings/input/microchip,cap11xx.yaml | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml b/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml
index 9578c7c206a2..22a292d4a880 100644
--- a/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml
+++ b/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml
@@ -171,6 +171,19 @@ allOf:
patternProperties:
"^led@": false
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - microchip,cap1126
+ then:
+ patternProperties:
+ "^led@":
+ properties:
+ reg:
+ maximum: 1
+
- if:
properties:
compatible:
--
2.54.0
^ permalink raw reply related
* [PATCH v3 03/10] dt-bindings: input: microchip,cap11xx: Update datasheet URL and LED reg range
From: Jun Yan @ 2026-06-15 14:20 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Jun Yan, linux-input, devicetree, linux-kernel
In-Reply-To: <20260615142103.352163-1-jerrysteve1101@gmail.com>
- Add datasheet links for all supported CAP11xx variants.
- Update LED node regex and replace enum constraints with minimum/maximum
for LED reg ranges in preparation for CAP1114 support.
CAP1114 has 11 LED channels. minimum/maximum constraints are easier to
maintain than long enum lists when expanding channel count later.
Signed-off-by: Jun Yan <jerrysteve1101@gmail.com>
---
.../bindings/input/microchip,cap11xx.yaml | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml b/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml
index 7ade03f1b32b..9578c7c206a2 100644
--- a/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml
+++ b/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml
@@ -10,6 +10,15 @@ description: |
The Microchip CAP1xxx Family of RightTouchTM multiple-channel capacitive
touch controllers and LED drivers. The device communication via I2C only.
+ For more product information please see the links below:
+ CAP1106: https://ww1.microchip.com/downloads/en/DeviceDoc/00001624B.pdf
+ CAP1126: https://ww1.microchip.com/downloads/en/DeviceDoc/00001623B.pdf
+ CAP1188: https://ww1.microchip.com/downloads/en/DeviceDoc/00001620C.pdf
+ CAP1203: https://ww1.microchip.com/downloads/en/DeviceDoc/00001572B.pdf
+ CAP1206: https://ww1.microchip.com/downloads/en/DeviceDoc/00001567B.pdf
+ CAP1293: https://ww1.microchip.com/downloads/en/DeviceDoc/00001566B.pdf
+ CAP1298: https://ww1.microchip.com/downloads/en/DeviceDoc/00001571B.pdf
+
maintainers:
- Rob Herring <robh@kernel.org>
@@ -124,14 +133,16 @@ properties:
The number of entries must correspond to the number of channels.
patternProperties:
- "^led@[0-7]$":
+ "^led@[0-9a-f]$":
type: object
description: CAP11xx LEDs
$ref: /schemas/leds/common.yaml#
properties:
reg:
- enum: [0, 1, 2, 3, 4, 5, 6, 7]
+ description: LED channel number
+ minimum: 0
+ maximum: 7
label: true
@@ -158,7 +169,7 @@ allOf:
- microchip,cap1298
then:
patternProperties:
- "^led@[0-7]$": false
+ "^led@": false
- if:
properties:
--
2.54.0
^ permalink raw reply related
* [PATCH v3 02/10] Input: cap11xx - remove unused register macros
From: Jun Yan @ 2026-06-15 14:20 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Jun Yan, linux-input, devicetree, linux-kernel
In-Reply-To: <20260615142103.352163-1-jerrysteve1101@gmail.com>
Remove unused register address macros and unused definitions in
the cap11xx_reg_defaults array and cap11xx_volatile_reg.
This cleanup reduces code clutter and makes the driver easier to
maintain without affecting functionality.
Signed-off-by: Jun Yan <jerrysteve1101@gmail.com>
---
drivers/input/keyboard/cap11xx.c | 58 --------------------------------
1 file changed, 58 deletions(-)
diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c
index 485d8ba97723..686174722204 100644
--- a/drivers/input/keyboard/cap11xx.c
+++ b/drivers/input/keyboard/cap11xx.c
@@ -20,53 +20,23 @@
#define CAP11XX_REG_MAIN_CONTROL_GAIN_SHIFT (6)
#define CAP11XX_REG_MAIN_CONTROL_GAIN_MASK (0xc0)
#define CAP11XX_REG_MAIN_CONTROL_DLSEEP BIT(4)
-#define CAP11XX_REG_GENERAL_STATUS 0x02
#define CAP11XX_REG_SENSOR_INPUT 0x03
-#define CAP11XX_REG_NOISE_FLAG_STATUS 0x0a
-#define CAP11XX_REG_SENOR_DELTA(X) (0x10 + (X))
#define CAP11XX_REG_SENSITIVITY_CONTROL 0x1f
#define CAP11XX_REG_SENSITIVITY_CONTROL_DELTA_SENSE_MASK 0x70
-#define CAP11XX_REG_CONFIG 0x20
-#define CAP11XX_REG_SENSOR_ENABLE 0x21
-#define CAP11XX_REG_SENSOR_CONFIG 0x22
-#define CAP11XX_REG_SENSOR_CONFIG2 0x23
-#define CAP11XX_REG_SAMPLING_CONFIG 0x24
-#define CAP11XX_REG_CALIBRATION 0x26
-#define CAP11XX_REG_INT_ENABLE 0x27
#define CAP11XX_REG_REPEAT_RATE 0x28
#define CAP11XX_REG_SIGNAL_GUARD_ENABLE 0x29
-#define CAP11XX_REG_MT_CONFIG 0x2a
-#define CAP11XX_REG_MT_PATTERN_CONFIG 0x2b
-#define CAP11XX_REG_MT_PATTERN 0x2d
-#define CAP11XX_REG_RECALIB_CONFIG 0x2f
#define CAP11XX_REG_SENSOR_THRESH(X) (0x30 + (X))
-#define CAP11XX_REG_SENSOR_NOISE_THRESH 0x38
-#define CAP11XX_REG_STANDBY_CHANNEL 0x40
-#define CAP11XX_REG_STANDBY_CONFIG 0x41
-#define CAP11XX_REG_STANDBY_SENSITIVITY 0x42
-#define CAP11XX_REG_STANDBY_THRESH 0x43
#define CAP11XX_REG_CONFIG2 0x44
#define CAP11XX_REG_CONFIG2_ALT_POL BIT(6)
-#define CAP11XX_REG_SENSOR_BASE_CNT(X) (0x50 + (X))
-#define CAP11XX_REG_LED_POLARITY 0x73
#define CAP11XX_REG_LED_OUTPUT_CONTROL 0x74
#define CAP11XX_REG_CALIB_SENSITIVITY_CONFIG 0x80
#define CAP11XX_REG_CALIB_SENSITIVITY_CONFIG2 0x81
-
-#define CAP11XX_REG_LED_DUTY_CYCLE_1 0x90
-#define CAP11XX_REG_LED_DUTY_CYCLE_2 0x91
-#define CAP11XX_REG_LED_DUTY_CYCLE_3 0x92
#define CAP11XX_REG_LED_DUTY_CYCLE_4 0x93
-#define CAP11XX_REG_LED_DUTY_MIN_MASK (0x0f)
-#define CAP11XX_REG_LED_DUTY_MIN_MASK_SHIFT (0)
#define CAP11XX_REG_LED_DUTY_MAX_MASK (0xf0)
#define CAP11XX_REG_LED_DUTY_MAX_MASK_SHIFT (4)
#define CAP11XX_REG_LED_DUTY_MAX_VALUE (15)
-#define CAP11XX_REG_SENSOR_CALIB (0xb1 + (X))
-#define CAP11XX_REG_SENSOR_CALIB_LSB1 0xb9
-#define CAP11XX_REG_SENSOR_CALIB_LSB2 0xba
#define CAP11XX_REG_PRODUCT_ID 0xfd
#define CAP11XX_REG_MANUFACTURER_ID 0xfe
#define CAP11XX_REG_REVISION 0xff
@@ -111,37 +81,15 @@ struct cap11xx_hw_model {
static const struct reg_default cap11xx_reg_defaults[] = {
{ CAP11XX_REG_MAIN_CONTROL, 0x00 },
- { CAP11XX_REG_GENERAL_STATUS, 0x00 },
- { CAP11XX_REG_SENSOR_INPUT, 0x00 },
- { CAP11XX_REG_NOISE_FLAG_STATUS, 0x00 },
{ CAP11XX_REG_SENSITIVITY_CONTROL, 0x2f },
- { CAP11XX_REG_CONFIG, 0x20 },
- { CAP11XX_REG_SENSOR_ENABLE, 0x3f },
- { CAP11XX_REG_SENSOR_CONFIG, 0xa4 },
- { CAP11XX_REG_SENSOR_CONFIG2, 0x07 },
- { CAP11XX_REG_SAMPLING_CONFIG, 0x39 },
- { CAP11XX_REG_CALIBRATION, 0x00 },
- { CAP11XX_REG_INT_ENABLE, 0x3f },
{ CAP11XX_REG_REPEAT_RATE, 0x3f },
- { CAP11XX_REG_MT_CONFIG, 0x80 },
- { CAP11XX_REG_MT_PATTERN_CONFIG, 0x00 },
- { CAP11XX_REG_MT_PATTERN, 0x3f },
- { CAP11XX_REG_RECALIB_CONFIG, 0x8a },
{ CAP11XX_REG_SENSOR_THRESH(0), 0x40 },
{ CAP11XX_REG_SENSOR_THRESH(1), 0x40 },
{ CAP11XX_REG_SENSOR_THRESH(2), 0x40 },
{ CAP11XX_REG_SENSOR_THRESH(3), 0x40 },
{ CAP11XX_REG_SENSOR_THRESH(4), 0x40 },
{ CAP11XX_REG_SENSOR_THRESH(5), 0x40 },
- { CAP11XX_REG_SENSOR_NOISE_THRESH, 0x01 },
- { CAP11XX_REG_STANDBY_CHANNEL, 0x00 },
- { CAP11XX_REG_STANDBY_CONFIG, 0x39 },
- { CAP11XX_REG_STANDBY_SENSITIVITY, 0x02 },
- { CAP11XX_REG_STANDBY_THRESH, 0x40 },
{ CAP11XX_REG_CONFIG2, 0x40 },
- { CAP11XX_REG_LED_POLARITY, 0x00 },
- { CAP11XX_REG_SENSOR_CALIB_LSB1, 0x00 },
- { CAP11XX_REG_SENSOR_CALIB_LSB2, 0x00 },
};
static bool cap11xx_volatile_reg(struct device *dev, unsigned int reg)
@@ -149,12 +97,6 @@ static bool cap11xx_volatile_reg(struct device *dev, unsigned int reg)
switch (reg) {
case CAP11XX_REG_MAIN_CONTROL:
case CAP11XX_REG_SENSOR_INPUT:
- case CAP11XX_REG_SENOR_DELTA(0):
- case CAP11XX_REG_SENOR_DELTA(1):
- case CAP11XX_REG_SENOR_DELTA(2):
- case CAP11XX_REG_SENOR_DELTA(3):
- case CAP11XX_REG_SENOR_DELTA(4):
- case CAP11XX_REG_SENOR_DELTA(5):
return true;
}
--
2.54.0
^ permalink raw reply related
* [PATCH v3 01/10] Input: cap11xx - clean up duplicate log and add probe error logs
From: Jun Yan @ 2026-06-15 14:20 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Jun Yan, linux-input, devicetree, linux-kernel
In-Reply-To: <20260615142103.352163-1-jerrysteve1101@gmail.com>
Duplicated device detection log exists at line 537 and line 542,
which brings redundant kernel print messages. Drop one redundant
log entry to clean up dmesg output.
Meanwhile add missing error logs when I2C communication fails
during driver probe(), helping debug.
Signed-off-by: Jun Yan <jerrysteve1101@gmail.com>
---
drivers/input/keyboard/cap11xx.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c
index 2447c1ae2166..485d8ba97723 100644
--- a/drivers/input/keyboard/cap11xx.c
+++ b/drivers/input/keyboard/cap11xx.c
@@ -512,7 +512,7 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client)
error = regmap_read(priv->regmap, CAP11XX_REG_PRODUCT_ID, &val);
if (error)
- return error;
+ return dev_err_probe(dev, error, "Failed to read product ID\n");
if (val != cap->product_id) {
dev_err(dev, "Product ID: Got 0x%02x, expected 0x%02x\n",
@@ -522,7 +522,7 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client)
error = regmap_read(priv->regmap, CAP11XX_REG_MANUFACTURER_ID, &val);
if (error)
- return error;
+ return dev_err_probe(dev, error, "Failed to read manufacturer ID\n");
if (val != CAP11XX_MANUFACTURER_ID) {
dev_err(dev, "Manufacturer ID: Got 0x%02x, expected 0x%02x\n",
@@ -531,11 +531,8 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client)
}
error = regmap_read(priv->regmap, CAP11XX_REG_REVISION, &rev);
- if (error < 0)
- return error;
-
- dev_info(dev, "CAP11XX detected, model %s, revision 0x%02x\n",
- id->name, rev);
+ if (error)
+ return dev_err_probe(dev, error, "Failed to read revision\n");
priv->model = cap;
--
2.54.0
^ permalink raw reply related
* [PATCH v3 00/10] Input: cap11xx - Add support for CAP1114
From: Jun Yan @ 2026-06-15 14:20 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Jun Yan, linux-input, devicetree, linux-kernel
CAP1114 is a 14-channel capacitive touch sensor with 11 LED outputs
and hardware reset support.
Patches 1-4 perform driver cleanup and DT binding tweaks.
Patches 5-6 add reset-gpios support for CAP11xx.
Patches 7-10 add support for CAP1114.
Changes in v3:
- Simplified the logic of the reset pin operation.
- Adjust linux,keycodes configuration for CAP11xx.
- Drop unnecessary CAP11XX_REG_SENSOR_THRESH(8).
- Checks for the presence of microchip,calib-sensitivity and
microchip,signal-guard properties before processing them.
- Link to v2:
https://lore.kernel.org/all/20260612072237.1177304-1-jerrysteve1101@gmail.com/
Changes in v2:
- Drop LED property tweaks, keep only reg changes and node regex
update in DT bindings.
- Split microchip,cap1126 LED reg constraints into a separate patch.
- Replace usleep_range() with msleep() for 500 ms delay during
reset pin handling.
- Add missing <linux/delay.h> for usleep_range() and msleep().
- Add CAP1114 to unsupported enum for microchip,signal-guard and
microchip,calib-sensitivity
- Add constraint for linux,keycodes to support CAP1114.
- When reading CAP1114 button status, mask STATUS1 to bits 0-5
and OR with STATUS2.
- Adjust code style.
- Link to v1:
https://lore.kernel.org/all/20260606150458.250606-1-jerrysteve1101@gmail.com
Jun Yan (10):
Input: cap11xx - clean up duplicate log and add probe error logs
Input: cap11xx - remove unused register macros
dt-bindings: input: microchip,cap11xx: Update datasheet URL and LED
reg range
dt-bindings: input: microchip,cap11xx: Add microchip,cap1126 LED reg
constraints
dt-bindings: input: microchip,cap11xx: Add reset-gpios property
Input: cap11xx - add reset gpio support
Input: cap11xx - refactor code for better CAP1114 support.
Input: cap11xx - guard unsupported DT properties before parsing
dt-bindings: input: microchip,cap11xx: Add CAP1114 support
Input: cap11xx - add support for CAP1114
.../bindings/input/microchip,cap11xx.yaml | 90 +++++-
drivers/input/keyboard/cap11xx.c | 280 +++++++++++-------
2 files changed, 253 insertions(+), 117 deletions(-)
--
2.54.0
^ permalink raw reply
* Re: [PATCH 2/9] iio: orientation: hid-sensor-incl-3d: Fix race between callback registration and device exposure
From: Pandruvada, Srinivas @ 2026-06-15 13:36 UTC (permalink / raw)
To: jic23@kernel.org
Cc: dlechner@baylibre.com, archana.patni@linux.intel.com,
hongyan.song@intel.com, linux-iio@vger.kernel.org,
nuno.sa@analog.com, linux-kernel@vger.kernel.org,
jikos@kernel.org, andy@kernel.org, sanjayembeddedse@gmail.com,
linux-input@vger.kernel.org
In-Reply-To: <20260614192442.6eaa1a54@jic23-huawei>
On Sun, 2026-06-14 at 19:24 +0100, Jonathan Cameron wrote:
> On Mon, 8 Jun 2026 15:34:05 +0000
> "Pandruvada, Srinivas" <srinivas.pandruvada@intel.com> wrote:
>
> > On Sat, 2026-06-06 at 17:07 +0530, Sanjay Chitroda wrote:
> > > From: Sanjay Chitroda <sanjayembeddedse@gmail.com>
> > >
> > > The driver registers the IIO device before completing sensor hub
> > > callback registration and unregisters callbacks while the IIO
> > > device
> > > is still exposed during teardown.
> > >
> > > This creates race windows in both probe and remove paths, which
> > > can
> > > lead to NULL pointer dereferences or use-after-free.
> >
> > Reordering is fine, but can you show how this use after free is
> > possible?
> Agreed - I'm not seeing a definite issue so more info needed.
> For now I'm going to mark this changes-requested in patchwork.
>
> It might be a touch slow if someone manages to get buffered capture
> up before the callbacks are available, but I think that just means
> dropping a few samples?
Correct.
Thanks,
Srinivas
>
> Jonathan
>
> >
> > Thanks,
> > Srinivas
>
^ 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