From 3668399ce9e7bba243728c3c02b2785565ea7b0b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 22 Jun 2026 22:35:34 -0700 Subject: Input: mms114 - prefer GPL over GPL v2 for module license As explained in commit bf7fbeeae6db ("module: Cure the MODULE_LICENSE "GPL" vs. "GPL v2" bogosity"), "GPL" and "GPL v2" have identical semantics in the module loader, but "GPL" is preferred to avoid unnecessary confusion and maintain consistency across the kernel. Change MODULE_LICENSE("GPL v2") to MODULE_LICENSE("GPL"). Assisted-by: Antigravity:gemini-3.5-flash Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260616050912.1531241-2-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/mms114.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index 53ad35d61d47..db23b51f4630 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -722,4 +722,4 @@ module_i2c_driver(mms114_driver); /* Module information */ MODULE_AUTHOR("Joonyoung Shim "); MODULE_DESCRIPTION("MELFAS mms114 Touchscreen driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From ce414fb127d9a0bf566502023a8030af564f66bb Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 22 Jun 2026 22:35:44 -0700 Subject: Input: mms114 - use appropriate register argument types The MMS114 I2C touch controller uses 8-bit register addresses (0x01 to 0xF2) and 8-bit single-register data values. The helper functions previously declared reg and val as 32-bit unsigned int, requiring explicit bitwise masking (& 0xff) to narrow the values down to u8 before populating the I2C transfer buffers. Update reg and val parameters to u8 across mms114_read_reg(), mms114_write_reg(), and __mms114_read_reg() to accurately reflect the hardware specification and eliminate the redundant & 0xff masking. Additionally, update the val buffer pointer in __mms114_read_reg() from u8 * to void * to allow callers to pass data structures directly without requiring explicit casting. Assisted-by: Antigravity:gemini-3.5-flash Link: https://patch.msgid.link/20260616050912.1531241-3-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/mms114.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index db23b51f4630..c2e006ac1196 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -87,12 +87,12 @@ struct mms114_touch { u8 reserved[2]; } __packed; -static int __mms114_read_reg(struct mms114_data *data, unsigned int reg, - unsigned int len, u8 *val) +static int __mms114_read_reg(struct mms114_data *data, u8 reg, + unsigned int len, void *val) { struct i2c_client *client = data->client; struct i2c_msg xfer[2]; - u8 buf = reg & 0xff; + u8 buf = reg; int error; if (reg <= MMS114_MODE_CONTROL && reg + len > MMS114_MODE_CONTROL) @@ -121,7 +121,7 @@ static int __mms114_read_reg(struct mms114_data *data, unsigned int reg, return 0; } -static int mms114_read_reg(struct mms114_data *data, unsigned int reg) +static int mms114_read_reg(struct mms114_data *data, u8 reg) { u8 val; int error; @@ -133,15 +133,14 @@ static int mms114_read_reg(struct mms114_data *data, unsigned int reg) return error < 0 ? error : val; } -static int mms114_write_reg(struct mms114_data *data, unsigned int reg, - unsigned int val) +static int mms114_write_reg(struct mms114_data *data, u8 reg, u8 val) { struct i2c_client *client = data->client; u8 buf[2]; int error; - buf[0] = reg & 0xff; - buf[1] = val & 0xff; + buf[0] = reg; + buf[1] = val; error = i2c_master_send(client, buf, 2); if (error != 2) { @@ -242,9 +241,8 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) touch_size = packet_size / event_size; - error = __mms114_read_reg(data, MMS114_INFORMATION, packet_size, - (u8 *)touch); - if (error < 0) + error = __mms114_read_reg(data, MMS114_INFORMATION, packet_size, touch); + if (error) goto out; for (index = 0; index < touch_size; index++) { -- cgit v1.2.3 From 144337eeefbec6bbc7e9b51a6d58551baad03d40 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 22 Jun 2026 22:36:01 -0700 Subject: Input: mms114 - replace udelay with usleep_range The driver currently uses udelay(MMS114_I2C_DELAY) (50us) to ensure a mandatory delay between I2C transfers in __mms114_read_reg() and mms114_write_reg(). Both functions invoke underlying I2C core operations (i2c_transfer, i2c_master_send) which acquire mutexes and sleep. Furthermore, the interrupt handler mms114_interrupt() is registered as a threaded IRQ handler. Since the entire execution path is fully sleepable, busy-waiting with udelay() for 50us unnecessarily wastes CPU cycles. Replace udelay() with usleep_range() to allow the CPU to enter low-power states or execute other tasks during the delay. Assisted-by: Antigravity:gemini-3.5-flash Link: https://patch.msgid.link/20260616050912.1531241-4-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/mms114.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index c2e006ac1196..c59aec8f2feb 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -116,7 +116,7 @@ static int __mms114_read_reg(struct mms114_data *data, u8 reg, "%s: i2c transfer failed (%d)\n", __func__, error); return error < 0 ? error : -EIO; } - udelay(MMS114_I2C_DELAY); + usleep_range(MMS114_I2C_DELAY, MMS114_I2C_DELAY + 50); return 0; } @@ -148,7 +148,7 @@ static int mms114_write_reg(struct mms114_data *data, u8 reg, u8 val) "%s: i2c send failed (%d)\n", __func__, error); return error < 0 ? error : -EIO; } - udelay(MMS114_I2C_DELAY); + usleep_range(MMS114_I2C_DELAY, MMS114_I2C_DELAY + 50); if (reg == MMS114_MODE_CONTROL) data->cache_mode_control = val; -- cgit v1.2.3 From 55b109de8ec5eb9840a1ea5ee163e3fee9aa3ba6 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 22 Jun 2026 22:36:13 -0700 Subject: Input: mms114 - replace BUG() and fix alignment Avoid taking the machine down with BUG() if a caller ever requests a read spanning the write-only MODE_CONTROL register; warn and return -EINVAL so the driver can recover. Additionally, fix parameter alignment to match the open parenthesis in several functions to conform to the kernel coding style. Assisted-by: Antigravity:gemini-3.5-flash Link: https://patch.msgid.link/20260616050912.1531241-5-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/mms114.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index c59aec8f2feb..bf01eee0560a 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -95,8 +95,8 @@ static int __mms114_read_reg(struct mms114_data *data, u8 reg, u8 buf = reg; int error; - if (reg <= MMS114_MODE_CONTROL && reg + len > MMS114_MODE_CONTROL) - BUG(); + if (WARN_ON(reg <= MMS114_MODE_CONTROL && reg + len > MMS114_MODE_CONTROL)) + return -EINVAL; /* Write register */ xfer[0].addr = client->addr; @@ -310,8 +310,7 @@ static int mms114_get_version(struct mms114_data *data) if (error) return error; - group = i2c_smbus_read_byte_data(data->client, - MMS152_COMPAT_GROUP); + group = i2c_smbus_read_byte_data(data->client, MMS152_COMPAT_GROUP); if (group < 0) return group; @@ -371,14 +370,14 @@ static int mms114_setup_regs(struct mms114_data *data) if (data->contact_threshold) { error = mms114_write_reg(data, MMS114_CONTACT_THRESHOLD, - data->contact_threshold); + data->contact_threshold); if (error < 0) return error; } if (data->moving_threshold) { error = mms114_write_reg(data, MMS114_MOVING_THRESHOLD, - data->moving_threshold); + data->moving_threshold); if (error < 0) return error; } @@ -464,9 +463,9 @@ static int mms114_parse_legacy_bindings(struct mms114_data *data) } device_property_read_u32(dev, "contact-threshold", - &data->contact_threshold); + &data->contact_threshold); device_property_read_u32(dev, "moving-threshold", - &data->moving_threshold); + &data->moving_threshold); if (device_property_read_bool(dev, "x-invert")) props->invert_x = true; @@ -519,7 +518,7 @@ static int mms114_probe(struct i2c_client *client) return data->num_keycodes; } else if (data->num_keycodes > MMS114_MAX_TOUCHKEYS) { dev_warn(&client->dev, - "Found %d linux,keycodes but max is %d, ignoring the rest\n", + "Found %d linux,keycodes but max is %d, ignoring the rest\n", data->num_keycodes, MMS114_MAX_TOUCHKEYS); data->num_keycodes = MMS114_MAX_TOUCHKEYS; } -- cgit v1.2.3 From 2718c726a0682e89e9fc1a11db928fad8781e268 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 22 Jun 2026 22:38:01 -0700 Subject: Input: mms114 - refactor chip variant handling using descriptors Instead of using an enum and conditional switch/if statements throughout the driver to handle differences between chip variants (MMS114, MMS134S, MMS136, MMS152, MMS345L), introduce a variant-specific descriptor structure that encapsulates variant-specific properties (name, event size, presence of configuration registers) and callbacks (such as get_version). Define descriptors for each supported chip and associate them with the matching entries in the OF and I2C device ID tables. This eliminates the need for variant checks in the driver logic, making it easier to support new chip variants in the future. Note that there is slight change in device names: MMS134S: "MELFAS MMS134 Touchscreen" -> "MELFAS MMS134S Touchscreen" MMS345L: "MELFAS MMS345 Touchscreen" -> "MELFAS MMS345L Touchscreen" Assisted-by: Antigravity:gemini-3.5-flash Link: https://patch.msgid.link/20260616050912.1531241-6-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/mms114.c | 204 ++++++++++++++++++++----------------- 1 file changed, 112 insertions(+), 92 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index bf01eee0560a..006dded17eb8 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -52,21 +52,13 @@ #define MMS114_TYPE_TOUCHSCREEN 1 #define MMS114_TYPE_TOUCHKEY 2 -enum mms_type { - TYPE_MMS114 = 114, - TYPE_MMS134S = 134, - TYPE_MMS136 = 136, - TYPE_MMS152 = 152, - TYPE_MMS345L = 345, -}; - struct mms114_data { + const struct mms_chip *chip; struct i2c_client *client; struct input_dev *input_dev; struct regulator *core_reg; struct regulator *io_reg; struct touchscreen_properties props; - enum mms_type type; unsigned int contact_threshold; unsigned int moving_threshold; @@ -77,6 +69,13 @@ struct mms114_data { u8 cache_mode_control; }; +struct mms_chip { + const char *name; + int event_size; + bool has_config_regs; + int (*get_version)(struct mms114_data *data); +}; + struct mms114_touch { u8 id:4, reserved_bit4:1, type:2, pressed:1; u8 x_hi:4, y_hi:4; @@ -156,6 +155,91 @@ static int mms114_write_reg(struct mms114_data *data, u8 reg, u8 val) return 0; } +static int mms114_get_version(struct mms114_data *data) +{ + struct device *dev = &data->client->dev; + u8 buf[6]; + int error; + + error = __mms114_read_reg(data, MMS114_TSP_REV, 6, buf); + if (error) + return error; + + dev_info(dev, "TSP Rev: 0x%x, HW Rev: 0x%x, Firmware Ver: 0x%x\n", + buf[0], buf[1], buf[3]); + return 0; +} + +static int mms152_get_version(struct mms114_data *data) +{ + struct device *dev = &data->client->dev; + u8 buf[3]; + int group; + int error; + + error = __mms114_read_reg(data, MMS152_FW_REV, 3, buf); + if (error) + return error; + + group = i2c_smbus_read_byte_data(data->client, MMS152_COMPAT_GROUP); + if (group < 0) + return group; + + dev_info(dev, "TSP FW Rev: bootloader 0x%x / core 0x%x / config 0x%x, Compat group: %c\n", + buf[0], buf[1], buf[2], group); + return 0; +} + +static int mms345l_get_version(struct mms114_data *data) +{ + struct device *dev = &data->client->dev; + u8 buf[3]; + int error; + + error = __mms114_read_reg(data, MMS152_FW_REV, 3, buf); + if (error) + return error; + + dev_info(dev, "TSP FW Rev: bootloader 0x%x / core 0x%x / config 0x%x\n", + buf[0], buf[1], buf[2]); + return 0; +} + +static const struct mms_chip mms114_descriptor = { + .name = "MMS114", + .event_size = MMS114_EVENT_SIZE, + .has_config_regs = true, + .get_version = mms114_get_version, +}; + +static const struct mms_chip mms134s_descriptor = { + .name = "MMS134S", + .event_size = MMS136_EVENT_SIZE, + .has_config_regs = true, + .get_version = mms114_get_version, +}; + +static const struct mms_chip mms136_descriptor = { + .name = "MMS136", + .event_size = MMS136_EVENT_SIZE, + .has_config_regs = true, + .get_version = mms114_get_version, +}; + +static const struct mms_chip mms152_descriptor = { + .name = "MMS152", + .event_size = MMS114_EVENT_SIZE, + .has_config_regs = false, + .get_version = mms152_get_version, +}; + +static const struct mms_chip mms345l_descriptor = { + .name = "MMS345L", + .event_size = MMS114_EVENT_SIZE, + .has_config_regs = false, + .get_version = mms345l_get_version, +}; + static void mms114_process_mt(struct mms114_data *data, struct mms114_touch *touch) { struct i2c_client *client = data->client; @@ -217,8 +301,8 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) struct i2c_client *client = data->client; struct mms114_touch touch[MMS114_MAX_TOUCH]; struct mms114_touch *t; + int event_size = data->chip->event_size; int packet_size; - int event_size; int touch_size; int index; int error; @@ -233,12 +317,6 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) goto out; } - /* MMS136 has slightly different event size */ - if (data->type == TYPE_MMS134S || data->type == TYPE_MMS136) - event_size = MMS136_EVENT_SIZE; - else - event_size = MMS114_EVENT_SIZE; - touch_size = packet_size / event_size; error = __mms114_read_reg(data, MMS114_INFORMATION, packet_size, touch); @@ -288,64 +366,17 @@ static int mms114_set_active(struct mms114_data *data, bool active) return mms114_write_reg(data, MMS114_MODE_CONTROL, val); } -static int mms114_get_version(struct mms114_data *data) -{ - struct device *dev = &data->client->dev; - u8 buf[6]; - int group; - int error; - - switch (data->type) { - case TYPE_MMS345L: - error = __mms114_read_reg(data, MMS152_FW_REV, 3, buf); - if (error) - return error; - - dev_info(dev, "TSP FW Rev: bootloader 0x%x / core 0x%x / config 0x%x\n", - buf[0], buf[1], buf[2]); - break; - - case TYPE_MMS152: - error = __mms114_read_reg(data, MMS152_FW_REV, 3, buf); - if (error) - return error; - - group = i2c_smbus_read_byte_data(data->client, MMS152_COMPAT_GROUP); - if (group < 0) - return group; - - dev_info(dev, "TSP FW Rev: bootloader 0x%x / core 0x%x / config 0x%x, Compat group: %c\n", - buf[0], buf[1], buf[2], group); - break; - - case TYPE_MMS114: - case TYPE_MMS134S: - case TYPE_MMS136: - error = __mms114_read_reg(data, MMS114_TSP_REV, 6, buf); - if (error) - return error; - - dev_info(dev, "TSP Rev: 0x%x, HW Rev: 0x%x, Firmware Ver: 0x%x\n", - buf[0], buf[1], buf[3]); - break; - } - - return 0; -} - static int mms114_setup_regs(struct mms114_data *data) { const struct touchscreen_properties *props = &data->props; int val; int error; - error = mms114_get_version(data); - if (error < 0) + error = data->chip->get_version(data); + if (error) return error; - /* MMS114, MMS134S and MMS136 have configuration and power on registers */ - if (data->type != TYPE_MMS114 && data->type != TYPE_MMS134S && - data->type != TYPE_MMS136) + if (!data->chip->has_config_regs) return 0; error = mms114_set_active(data, true); @@ -481,7 +512,6 @@ static int mms114_probe(struct i2c_client *client) { struct mms114_data *data; struct input_dev *input_dev; - const void *match_data; int error; int i; @@ -501,12 +531,10 @@ static int mms114_probe(struct i2c_client *client) data->client = client; data->input_dev = input_dev; - match_data = device_get_match_data(&client->dev); - if (!match_data) + data->chip = i2c_get_match_data(client); + if (!data->chip) return -EINVAL; - data->type = (enum mms_type)match_data; - data->num_keycodes = device_property_count_u32(&client->dev, "linux,keycodes"); if (data->num_keycodes == -EINVAL) { @@ -563,8 +591,7 @@ static int mms114_probe(struct i2c_client *client) 0, data->props.max_y, 0, 0); } - if (data->type == TYPE_MMS114 || data->type == TYPE_MMS134S || - data->type == TYPE_MMS136) { + if (data->chip->has_config_regs) { /* * The firmware handles movement and pressure fuzz, so * don't duplicate that in software. @@ -579,8 +606,8 @@ static int mms114_probe(struct i2c_client *client) } input_dev->name = devm_kasprintf(&client->dev, GFP_KERNEL, - "MELFAS MMS%d Touchscreen", - data->type); + "MELFAS %s Touchscreen", + data->chip->name); if (!input_dev->name) return -ENOMEM; @@ -676,29 +703,22 @@ static int mms114_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(mms114_pm_ops, mms114_suspend, mms114_resume); static const struct i2c_device_id mms114_id[] = { - { .name = "mms114" }, + { .name = "mms114", .driver_data = (kernel_ulong_t)&mms114_descriptor }, + { .name = "mms134s", .driver_data = (kernel_ulong_t)&mms134s_descriptor }, + { .name = "mms136", .driver_data = (kernel_ulong_t)&mms136_descriptor }, + { .name = "mms152", .driver_data = (kernel_ulong_t)&mms152_descriptor }, + { .name = "mms345l", .driver_data = (kernel_ulong_t)&mms345l_descriptor }, { } }; MODULE_DEVICE_TABLE(i2c, mms114_id); #ifdef CONFIG_OF static const struct of_device_id mms114_dt_match[] = { - { - .compatible = "melfas,mms114", - .data = (void *)TYPE_MMS114, - }, { - .compatible = "melfas,mms134s", - .data = (void *)TYPE_MMS134S, - }, { - .compatible = "melfas,mms136", - .data = (void *)TYPE_MMS136, - }, { - .compatible = "melfas,mms152", - .data = (void *)TYPE_MMS152, - }, { - .compatible = "melfas,mms345l", - .data = (void *)TYPE_MMS345L, - }, + { .compatible = "melfas,mms114", .data = &mms114_descriptor }, + { .compatible = "melfas,mms134s", .data = &mms134s_descriptor }, + { .compatible = "melfas,mms136", .data = &mms136_descriptor }, + { .compatible = "melfas,mms152", .data = &mms152_descriptor }, + { .compatible = "melfas,mms345l", .data = &mms345l_descriptor }, { } }; MODULE_DEVICE_TABLE(of, mms114_dt_match); -- cgit v1.2.3 From d3b78c9e1f79bc57d81ccac6b26a4bd6141a62b2 Mon Sep 17 00:00:00 2001 From: Jun Yan Date: Wed, 17 Jun 2026 23:02:40 +0800 Subject: Input: cap11xx - clean up duplicate log and add probe error logs 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 Link: https://patch.msgid.link/20260617150318.753148-2-jerrysteve1101@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/cap11xx.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers') 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; -- cgit v1.2.3 From 32964d017e56e123e1faf8702c7f93255db8acda Mon Sep 17 00:00:00 2001 From: Jun Yan Date: Wed, 17 Jun 2026 23:02:41 +0800 Subject: Input: cap11xx - remove unused register macros Remove unused register address macros and their corresponding definitions in the cap11xx_reg_defaults array. This cleanup reduces code clutter and makes the driver easier to maintain without affecting functionality. Signed-off-by: Jun Yan Link: https://patch.msgid.link/20260617150318.753148-3-jerrysteve1101@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/cap11xx.c | 51 ---------------------------------------- 1 file changed, 51 deletions(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c index 485d8ba97723..fae26f035186 100644 --- a/drivers/input/keyboard/cap11xx.c +++ b/drivers/input/keyboard/cap11xx.c @@ -20,53 +20,24 @@ #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 +82,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) -- cgit v1.2.3 From e40bdf042d417c876ce2623f77215a7eb51a4caa Mon Sep 17 00:00:00 2001 From: Jun Yan Date: Wed, 17 Jun 2026 23:02:45 +0800 Subject: Input: cap11xx - add reset gpio support 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 Link: https://patch.msgid.link/20260617150318.753148-7-jerrysteve1101@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/cap11xx.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers') diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c index fae26f035186..1db4a9090705 100644 --- a/drivers/input/keyboard/cap11xx.c +++ b/drivers/input/keyboard/cap11xx.c @@ -5,6 +5,7 @@ * (c) 2014 Daniel Mack */ +#include #include #include #include @@ -44,6 +45,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; @@ -56,6 +60,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; @@ -459,6 +464,17 @@ 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"); + + 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"); -- cgit v1.2.3 From 6ddb3d0c90d9423ed84b4cd3213d73d54ad386ba Mon Sep 17 00:00:00 2001 From: Jun Yan Date: Mon, 22 Jun 2026 22:07:35 -0700 Subject: Input: cap11xx - refactor code for better CAP1114 support. 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 Link: https://patch.msgid.link/20260617150318.753148-8-jerrysteve1101@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/cap11xx.c | 95 ++++++++++++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c index 1db4a9090705..16edabcf6f2d 100644 --- a/drivers/input/keyboard/cap11xx.c +++ b/drivers/input/keyboard/cap11xx.c @@ -5,6 +5,7 @@ * (c) 2014 Daniel Mack */ +#include #include #include #include @@ -22,6 +23,7 @@ #define CAP11XX_REG_MAIN_CONTROL_GAIN_MASK (0xc0) #define CAP11XX_REG_MAIN_CONTROL_DLSEEP BIT(4) #define CAP11XX_REG_SENSOR_INPUT 0x03 +#define CAP1114_REG_BUTTON_STATUS2 0x04 #define CAP11XX_REG_SENOR_DELTA(X) (0x10 + (X)) #define CAP11XX_REG_SENSITIVITY_CONTROL 0x1f #define CAP11XX_REG_SENSITIVITY_CONTROL_DELTA_SENSE_MASK 0x70 @@ -36,7 +38,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 @@ -77,10 +78,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; }; @@ -103,6 +108,7 @@ static bool cap11xx_volatile_reg(struct device *dev, unsigned int reg) switch (reg) { case CAP11XX_REG_MAIN_CONTROL: case CAP11XX_REG_SENSOR_INPUT: + case CAP1114_REG_BUTTON_STATUS2: case CAP11XX_REG_SENOR_DELTA(0): case CAP11XX_REG_SENOR_DELTA(1): case CAP11XX_REG_SENOR_DELTA(2): @@ -211,8 +217,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]); @@ -286,10 +292,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; } @@ -308,7 +316,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; @@ -361,10 +369,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, - CAP11XX_REG_LED_OUTPUT_CONTROL, - 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, @@ -374,6 +388,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; @@ -387,15 +402,26 @@ 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; + 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); + 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; @@ -561,41 +587,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, }; -- cgit v1.2.3 From a9a7baeb3ab7a09eca0696c575b7a3f1058868d6 Mon Sep 17 00:00:00 2001 From: Jun Yan Date: Wed, 17 Jun 2026 23:02:47 +0800 Subject: Input: cap11xx - guard unsupported DT properties before parsing 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 Link: https://patch.msgid.link/20260617150318.753148-9-jerrysteve1101@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/cap11xx.c | 52 +++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c index 16edabcf6f2d..5ee01edff581 100644 --- a/drivers/input/keyboard/cap11xx.c +++ b/drivers/input/keyboard/cap11xx.c @@ -233,10 +233,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) { @@ -256,32 +259,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; + } } } -- cgit v1.2.3 From 0c9245c455e809c8111cb60284328c79064df050 Mon Sep 17 00:00:00 2001 From: Jun Yan Date: Wed, 17 Jun 2026 23:02:49 +0800 Subject: Input: cap11xx - add support for CAP1114 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 Link: https://patch.msgid.link/20260617150318.753148-11-jerrysteve1101@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/cap11xx.c | 50 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) (limited to 'drivers') diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c index 5ee01edff581..25b6f7bd97ee 100644 --- a/drivers/input/keyboard/cap11xx.c +++ b/drivers/input/keyboard/cap11xx.c @@ -18,6 +18,12 @@ #include #include +#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) @@ -84,6 +90,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; @@ -100,6 +107,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 }, }; @@ -108,6 +117,11 @@ 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: case CAP11XX_REG_SENOR_DELTA(0): case CAP11XX_REG_SENOR_DELTA(1): @@ -294,6 +308,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); @@ -322,6 +347,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)); @@ -597,6 +637,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, @@ -653,6 +701,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 }, @@ -665,6 +714,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 }, -- cgit v1.2.3 From 7890fd28fd12b321ccd9e4fdeadbdf9c5ea1be7a Mon Sep 17 00:00:00 2001 From: Shashwat Agrawal Date: Fri, 26 Jun 2026 18:30:51 +0530 Subject: Input: synaptics - enable InterTouch on Dell Inspiron 3521 The Synaptics touchpad on Dell Inspiron 3521 (PNP ID DLL0597) advertises InterTouch / SMBus support, but is not on the SMBus passlist, so the driver falls back to PS/2 and logs a hint to try psmouse.synaptics_intertouch=1. Add DLL0597 to smbus_pnp_ids so InterTouch is enabled automatically on this model (and other Dells that reuse the same PNP ID). Hardware: Dell Inc. Inspiron 3521 (board 06RYX8, BIOS A07), Synaptics fw 8.1 / board id 2382, firmware_id "PNP: DLL0597 PNP0f13". Signed-off-by: Shashwat Agrawal Link: https://patch.msgid.link/20260626130051.2574-1-shashwatagrawal473@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index c70502e24031..2170bbe4c589 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -164,6 +164,7 @@ static const char * const topbuttonpad_pnp_ids[] = { #ifdef CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS static const char * const smbus_pnp_ids[] = { /* all of the topbuttonpad_pnp_ids are valid, we just add some extras */ + "DLL0597", /* Dell Inspiron 3521 */ "DLL060d", /* Dell Precision M3800 */ "LEN0048", /* X1 Carbon 3 */ "LEN0046", /* X250 */ -- cgit v1.2.3 From 8fc62e1d7429e39ac0420dd457021b5fe809e90d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 17 Jun 2026 10:05:27 +0300 Subject: Input: isa1200 - new driver for Imagis ISA1200 The ISA1200 is a haptic feedback unit from Imagis Technology using two motors for haptic feedback in mobile phones. Used in many mobile devices c. 2012 including Samsung Galxy S Advance GT-I9070 (Janice), Samsung Beam GT-I8350 (Gavini), LG Optimus 4X P880 and LG Optimus Vu P895. The exact datasheet for the ISA1200 is not available; all data was modeled based on available downstream kernel sources for various devices and fragments of information scattered across the internet. Tested-by: Linus Walleij # GT-I9070 Janice Signed-off-by: Linus Walleij Co-developed-by: Svyatoslav Ryhel Signed-off-by: Svyatoslav Ryhel Link: https://patch.msgid.link/20260617070528.35006-3-clamor95@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/Kconfig | 12 + drivers/input/misc/Makefile | 1 + drivers/input/misc/isa1200.c | 533 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 546 insertions(+) create mode 100644 drivers/input/misc/isa1200.c (limited to 'drivers') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 1f6c57dba030..7154eaf5a60b 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -842,6 +842,18 @@ config INPUT_IQS7222 To compile this driver as a module, choose M here: the module will be called iqs7222. +config INPUT_ISA1200_HAPTIC + tristate "Imagis ISA1200 haptic feedback unit" + depends on I2C + select INPUT_FF_MEMLESS + select REGMAP_I2C + help + Say Y to enable support for the Imagis ISA1200 haptic + feedback unit. + + To compile this driver as a module, choose M here: the + module will be called isa1200. + config INPUT_CMA3000 tristate "VTI CMA3000 Tri-axis accelerometer" help diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 2281d6803fce..e9f85ca20c33 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o obj-$(CONFIG_INPUT_IQS269A) += iqs269a.o obj-$(CONFIG_INPUT_IQS626A) += iqs626a.o obj-$(CONFIG_INPUT_IQS7222) += iqs7222.o +obj-$(CONFIG_INPUT_ISA1200_HAPTIC) += isa1200.o obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o diff --git a/drivers/input/misc/isa1200.c b/drivers/input/misc/isa1200.c new file mode 100644 index 000000000000..926cffcd38d6 --- /dev/null +++ b/drivers/input/misc/isa1200.c @@ -0,0 +1,533 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * System control (LDO regulator) + * + * LDO voltage to register mapping is linear, but it is split in two parts: + * 2.3V - 3.0V map to 0x08 - 0x0f; 3.1V - 3.8V map to 0x00 - 0x7 + */ + +#define ISA1200_SCTRL 0x00 +#define ISA1200_LDO_VOLTAGE_BASE 0x08 +#define ISA1200_LDO_VOLTAGE_STEP 100000 +#define ISA1200_LDO_VOLTAGE_2V3 23 +#define ISA1200_LDO_VOLTAGE_3V1 31 +#define ISA1200_LDO_VOLTAGE_MIN 2300000 +#define ISA1200_LDO_VOLTAGE_MAX 3800000 + +/* + * The output frequency is calculated with this formula: + * + * base clock frequency + * fout = ----------------------------------------- + * (128 - PWM_FREQ) * 2 * PLLDIV * PWM_PERIOD + * + * The base clock frequency is the clock frequency provided on the + * clock input to the chip, divided by the value in HCTRL0 + * + * PWM_FREQ is configured in register HCTRL4, it is common to set this + * to 0 to get only two variables to calculate. + * + * PLLDIV is configured in register HCTRL3 (bits 7..4, so 0..15) + * PWM_PERIOD is configured in register HCTRL6 + * Further the duty cycle can be configured in HCTRL5 + */ + +/* + * HCTRL0 configures clock or PWM input and selects the divider for + * the clock input. + */ +#define ISA1200_HCTRL0 0x30 +#define ISA1200_HCTRL0_HAP_ENABLE BIT(7) +#define ISA1200_HCTRL0_PWM_GEN_MODE BIT(4) +#define ISA1200_HCTRL0_PWM_INPUT_MODE BIT(3) +#define ISA1200_HCTRL0_CLKDIV_128 128 + +/* + * HCTRL1 configures the motor type and clock sourse + */ +#define ISA1200_HCTRL1 0x31 +#define ISA1200_HCTRL1_EXT_CLOCK BIT(7) +#define ISA1200_HCTRL1_DAC_INVERT BIT(6) +#define ISA1200_HCTRL1_MODE(n) (((n) & 1) << 5) + +/* HCTRL2 controls software reset of the chip */ +#define ISA1200_HCTRL2 0x32 +#define ISA1200_HCTRL2_SW_RESET BIT(0) + +/* + * HCTRL3 controls the PLL divisor + * + * Bits [0,1] are always set to 1 (we don't know what they are + * used for) and bit 4 and upward control the PLL divisor. + */ +#define ISA1200_HCTRL3 0x33 +#define ISA1200_HCTRL3_DEFAULT 0x03 +#define ISA1200_HCTRL3_PLLDIV(n) (((n) & 0xf) << 4) + +/* HCTRL4 controls the PWM frequency of external channel */ +#define ISA1200_HCTRL4 0x34 + +/* HCTRL5 controls the PWM high duty cycle of internal channel */ +#define ISA1200_HCTRL5 0x35 + +/* HCTRL6 controls the PWM period of internal channel */ +#define ISA1200_HCTRL6 0x36 +#define ISA1200_HCTRL6_PERIOD_SCALE 100 + +/* The use for these registers is unknown but they exist */ +#define ISA1200_HCTRL7 0x37 +#define ISA1200_HCTRL8 0x38 +#define ISA1200_HCTRL9 0x39 +#define ISA1200_HCTRLA 0x3a +#define ISA1200_HCTRLB 0x3b +#define ISA1200_HCTRLC 0x3c +#define ISA1200_HCTRLD 0x3d + +#define ISA1200_EN_PINS_MAX 2 + +static const struct regulator_bulk_data isa1200_supplies[] = { + { .supply = "vdd" }, { .supply = "vddp" }, +}; + +struct isa1200_config { + u32 ldo_voltage; + u32 mode; + u32 clkdiv; + u32 plldiv; + u32 freq; + u32 period; + u32 duty; +}; + +struct isa1200 { + struct input_dev *input; + struct regmap *map; + + struct clk *clk; + struct pwm_device *pwm; + struct gpio_descs *enable_gpios; + struct regulator_bulk_data *supplies; + + struct work_struct play_work; + struct isa1200_config config; + + int level; + bool suspended; + bool active; +}; + +static const struct regmap_config isa1200_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ISA1200_HCTRLD, +}; + +static void isa1200_start(struct isa1200 *isa) +{ + struct isa1200_config *config = &isa->config; + struct device *dev = &isa->input->dev; + struct pwm_state state; + u8 hctrl0 = 0, hctrl1 = 0; + DECLARE_BITMAP(values, ISA1200_EN_PINS_MAX); + int err; + + if (!isa->active) { + err = regulator_bulk_enable(ARRAY_SIZE(isa1200_supplies), + isa->supplies); + if (err) { + dev_err(dev, "failed to enable supplies (%d)\n", err); + return; + } + + err = clk_prepare_enable(isa->clk); + if (err) { + dev_err(dev, "failed to enable clock (%d)\n", err); + regulator_bulk_disable(ARRAY_SIZE(isa1200_supplies), + isa->supplies); + return; + } + + bitmap_fill(values, ISA1200_EN_PINS_MAX); + gpiod_multi_set_value_cansleep(isa->enable_gpios, values); + + usleep_range(200, 300); + } + + regmap_write(isa->map, ISA1200_SCTRL, config->ldo_voltage); + + if (isa->clk) { + hctrl0 = ISA1200_HCTRL0_PWM_GEN_MODE; + hctrl1 = ISA1200_HCTRL1_EXT_CLOCK; + } + + if (isa->pwm) { + hctrl0 = ISA1200_HCTRL0_PWM_INPUT_MODE; + hctrl1 = 0; + } + + hctrl0 |= __ffs(config->clkdiv / ISA1200_HCTRL0_CLKDIV_128); + hctrl1 |= ISA1200_HCTRL1_DAC_INVERT; + hctrl1 |= ISA1200_HCTRL1_MODE(config->mode); + + regmap_write(isa->map, ISA1200_HCTRL0, hctrl0); + regmap_write(isa->map, ISA1200_HCTRL1, hctrl1); + + /* Make sure to de-assert software reset */ + regmap_write(isa->map, ISA1200_HCTRL2, 0x00); + + /* PLL divisor */ + regmap_write(isa->map, ISA1200_HCTRL3, + ISA1200_HCTRL3_PLLDIV(config->plldiv) | + ISA1200_HCTRL3_DEFAULT); + + /* Frequency */ + regmap_write(isa->map, ISA1200_HCTRL4, config->freq); + /* Duty cycle */ + regmap_write(isa->map, ISA1200_HCTRL5, config->period >> 1); + /* Period */ + regmap_write(isa->map, ISA1200_HCTRL6, config->period); + + hctrl0 |= ISA1200_HCTRL0_HAP_ENABLE; + regmap_write(isa->map, ISA1200_HCTRL0, hctrl0); + + if (isa->clk) + regmap_write(isa->map, ISA1200_HCTRL5, config->duty); + + if (isa->pwm) { + pwm_get_state(isa->pwm, &state); + state.duty_cycle = config->duty; + state.enabled = true; + pwm_apply_might_sleep(isa->pwm, &state); + } + + isa->active = true; +} + +static void isa1200_stop(struct isa1200 *isa) +{ + struct pwm_state state; + DECLARE_BITMAP(values, ISA1200_EN_PINS_MAX); + + if (!isa->active) + return; + + if (isa->pwm) { + pwm_get_state(isa->pwm, &state); + state.duty_cycle = 0; + state.enabled = false; + pwm_apply_might_sleep(isa->pwm, &state); + } + + regmap_write(isa->map, ISA1200_HCTRL0, 0x00); + + bitmap_zero(values, ISA1200_EN_PINS_MAX); + gpiod_multi_set_value_cansleep(isa->enable_gpios, values); + + clk_disable_unprepare(isa->clk); + regulator_bulk_disable(ARRAY_SIZE(isa1200_supplies), + isa->supplies); + + isa->active = false; +} + +static void isa1200_play_work(struct work_struct *work) +{ + struct isa1200 *isa = container_of(work, struct isa1200, play_work); + + if (!READ_ONCE(isa->suspended)) { + if (isa->level) + isa1200_start(isa); + else + isa1200_stop(isa); + } +} + +static int isa1200_vibrator_play_effect(struct input_dev *input, void *data, + struct ff_effect *effect) +{ + struct isa1200 *isa = input_get_drvdata(input); + int level; + + /* + * TODO: we currently only support rumble. + * The ISA1200 can control two motors and some devices + * also have two motors mounted. + */ + level = effect->u.rumble.strong_magnitude; + if (!level) + level = effect->u.rumble.weak_magnitude; + + dev_dbg(&input->dev, "FF effect type %d level %d\n", + effect->type, level); + + if (isa->level != level) { + isa->level = level; + if (!READ_ONCE(isa->suspended)) + schedule_work(&isa->play_work); + } + + return 0; +} + +static void isa1200_vibrator_close(struct input_dev *input) +{ + struct isa1200 *isa = input_get_drvdata(input); + + cancel_work_sync(&isa->play_work); + isa1200_stop(isa); + isa->level = 0; +} + +static int isa1200_of_probe(struct i2c_client *client) +{ + struct isa1200 *isa = i2c_get_clientdata(client); + struct isa1200_config *config = &isa->config; + struct device *dev = &client->dev; + struct fwnode_handle *ldo_node; + int err; + + isa->clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(isa->clk)) + return dev_err_probe(dev, PTR_ERR(isa->clk), + "failed to get clock\n"); + + isa->pwm = devm_pwm_get(dev, NULL); + if (IS_ERR(isa->pwm)) { + err = PTR_ERR(isa->pwm); + if (err == -ENODEV || err == -EINVAL) + isa->pwm = NULL; + else + return dev_err_probe(dev, err, "getting PWM\n"); + } + + if (!isa->clk && !isa->pwm) + return dev_err_probe(dev, -EINVAL, + "clock or PWM are required, none were provided\n"); + + err = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(isa1200_supplies), + isa1200_supplies, &isa->supplies); + if (err) + return dev_err_probe(dev, err, "failed to get supplies\n"); + + isa->enable_gpios = devm_gpiod_get_array_optional(dev, "control", + GPIOD_OUT_LOW); + if (IS_ERR(isa->enable_gpios)) + return dev_err_probe(dev, PTR_ERR(isa->enable_gpios), + "failed to get enable gpios\n"); + + if (isa->enable_gpios && isa->enable_gpios->ndescs > ISA1200_EN_PINS_MAX) + return dev_err_probe(dev, -EINVAL, "too many enable gpios\n"); + + ldo_node = device_get_named_child_node(dev, "ldo"); + if (!ldo_node) + return dev_err_probe(dev, -ENODEV, + "failed to get embedded LDO node\n"); + + err = fwnode_property_read_u32(ldo_node, "regulator-min-microvolt", + &config->ldo_voltage); + fwnode_handle_put(ldo_node); + if (err) + return dev_err_probe(dev, err, + "failed to get ldo voltage\n"); + + config->ldo_voltage = clamp(config->ldo_voltage, + ISA1200_LDO_VOLTAGE_MIN, + ISA1200_LDO_VOLTAGE_MAX); + + config->ldo_voltage /= ISA1200_LDO_VOLTAGE_STEP; + if (config->ldo_voltage < ISA1200_LDO_VOLTAGE_3V1) + config->ldo_voltage = config->ldo_voltage - + ISA1200_LDO_VOLTAGE_2V3 + + ISA1200_LDO_VOLTAGE_BASE; + else + config->ldo_voltage -= ISA1200_LDO_VOLTAGE_3V1; + + config->mode = 0; /* LRA_MODE */ + device_property_read_u32(dev, "imagis,mode", &config->mode); + + config->clkdiv = ISA1200_HCTRL0_CLKDIV_128; + device_property_read_u32(dev, "imagis,clk-div", &config->clkdiv); + if (!config->clkdiv) + return dev_err_probe(dev, -EINVAL, "clk-div cannot be zero\n"); + + config->clkdiv = clamp(config->clkdiv, ISA1200_HCTRL0_CLKDIV_128, + ISA1200_HCTRL0_CLKDIV_128 << 3); + + err = device_property_read_u32(dev, "imagis,pll-div", &config->plldiv); + if (err || !config->plldiv) + config->plldiv = 1; + + config->period = 0; + config->freq = 0; + config->duty = 0; + + if (isa->clk) { + err = device_property_read_u32(dev, "imagis,period-ns", + &config->period); + if (err) + return dev_err_probe(dev, err, + "failed to get period\n"); + + /* + * TODO: The scale value is arbitrary, but it fits observations + * quite well, and the exact conversion method is unknown. + * The period property value returned above is the HCTRL6 + * register value set by the vendor code, multiplied by 100. + */ + config->period /= ISA1200_HCTRL6_PERIOD_SCALE; + config->duty = config->period >> 1; + } + + if (isa->pwm) { + struct pwm_state state; + + pwm_init_state(isa->pwm, &state); + + if (!state.period) + return dev_err_probe(dev, -EINVAL, + "PWM period cannot be zero\n"); + + config->freq = div64_u64(NANO, state.period * config->clkdiv); + config->duty = state.period >> 1; + + err = pwm_apply_might_sleep(isa->pwm, &state); + if (err) + return dev_err_probe(dev, err, + "failed to apply initial PWM state\n"); + } + + /* + * TODO: If device is using a clock, this property should return the + * value written to the HCTRL5 register by downstrem code. It likely + * needs to be converted into a meaningful duty cycle value, though + * unfortunately the exact conversion mechanism is unknown. If the + * device uses PWM, this property will return the correct duty cycle + * in nanoseconds. + */ + device_property_read_u32(dev, "imagis,duty-cycle-ns", &config->duty); + + return 0; +} + +static int isa1200_probe(struct i2c_client *client) +{ + struct isa1200 *isa; + struct device *dev = &client->dev; + int err; + + isa = devm_kzalloc(dev, sizeof(*isa), GFP_KERNEL); + if (!isa) + return -ENOMEM; + + isa->input = devm_input_allocate_device(dev); + if (!isa->input) + return -ENOMEM; + + i2c_set_clientdata(client, isa); + + err = isa1200_of_probe(client); + if (err) + return err; + + isa->map = devm_regmap_init_i2c(client, &isa1200_regmap_config); + if (IS_ERR(isa->map)) + return dev_err_probe(dev, PTR_ERR(isa->map), + "failed to initialize register map\n"); + + INIT_WORK(&isa->play_work, isa1200_play_work); + + isa->input->name = "isa1200-haptic"; + isa->input->id.bustype = BUS_I2C; + isa->input->close = isa1200_vibrator_close; + + isa->active = false; + + input_set_drvdata(isa->input, isa); + + /* TODO: this hardware can likely support more than rumble */ + input_set_capability(isa->input, EV_FF, FF_RUMBLE); + + err = input_ff_create_memless(isa->input, NULL, + isa1200_vibrator_play_effect); + if (err) + return dev_err_probe(dev, err, "failed to create FF dev\n"); + + err = input_register_device(isa->input); + if (err) + return dev_err_probe(dev, err, "failed to register input dev\n"); + + return 0; +} + +static int isa1200_suspend(struct device *dev) +{ + struct isa1200 *isa = dev_get_drvdata(dev); + + guard(mutex)(&isa->input->mutex); + + if (input_device_enabled(isa->input)) { + WRITE_ONCE(isa->suspended, true); + cancel_work_sync(&isa->play_work); + isa1200_stop(isa); + } + + return 0; +} + +static int isa1200_resume(struct device *dev) +{ + struct isa1200 *isa = dev_get_drvdata(dev); + + guard(mutex)(&isa->input->mutex); + + if (input_device_enabled(isa->input)) { + WRITE_ONCE(isa->suspended, false); + if (isa->level) + schedule_work(&isa->play_work); + } + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(isa1200_pm_ops, isa1200_suspend, isa1200_resume); + +static const struct of_device_id isa1200_of_match[] = { + { .compatible = "imagis,isa1200" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, isa1200_of_match); + +static struct i2c_driver isa1200_i2c_driver = { + .driver = { + .name = "isa1200", + .of_match_table = isa1200_of_match, + .pm = pm_sleep_ptr(&isa1200_pm_ops), + }, + .probe = isa1200_probe, +}; +module_i2c_driver(isa1200_i2c_driver); + +MODULE_AUTHOR("Linus Walleij "); +MODULE_AUTHOR("Svyatoslav Ryhel "); +MODULE_DESCRIPTION("Imagis ISA1200 haptic feedback unit"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From c8f174900926d3b58cd048ac33b4cbb3de419bfe Mon Sep 17 00:00:00 2001 From: Oliver Date: Mon, 15 Jun 2026 00:08:47 +0100 Subject: Input: sur40 - fix MAX_CONTACTS value based on PixelSense specification The Samsung SUR40 with Microsoft PixelSense is offically specified to support 52 simultaneuous touch contacts, not 64. The value of 64 was an unverified guess as noted by the FIXME comment. Update MAX_CONTACTS to match the documented hardware specification and remove the FIXME. Signed-off-by: Oliver Link: https://patch.msgid.link/20260614230847.4938-1-oliverburns.kernel@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/sur40.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index fe63d53d56db..77ec2c94b91f 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -128,8 +128,8 @@ struct sur40_image_header { /* polling interval (ms) */ #define POLL_INTERVAL 1 -/* maximum number of contacts FIXME: this is a guess? */ -#define MAX_CONTACTS 64 +/* maximum number of contacts */ +#define MAX_CONTACTS 52 /* control commands */ #define SUR40_GET_VERSION 0xb0 /* 12 bytes string */ -- cgit v1.2.3