From 36b624b992ff692f8a3a1c4c078827c576d72efb Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 25 Jun 2025 12:34:28 -0400 Subject: [PATCH 01/38] dt-bindings: input: touchscreen: convert lpc32xx-tsc.txt to yaml format Convert lpc32xx-tsc.txt to yaml format. Additional changes: - add clocks and put it into required list to match existed lpc32xx.dtsi. Signed-off-by: Frank Li Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250625163431.2543597-1-Frank.Li@nxp.com Signed-off-by: Dmitry Torokhov --- .../input/touchscreen/lpc32xx-tsc.txt | 16 ------- .../input/touchscreen/nxp,lpc3220-tsc.yaml | 43 +++++++++++++++++++ 2 files changed, 43 insertions(+), 16 deletions(-) delete mode 100644 Documentation/devicetree/bindings/input/touchscreen/lpc32xx-tsc.txt create mode 100644 Documentation/devicetree/bindings/input/touchscreen/nxp,lpc3220-tsc.yaml diff --git a/Documentation/devicetree/bindings/input/touchscreen/lpc32xx-tsc.txt b/Documentation/devicetree/bindings/input/touchscreen/lpc32xx-tsc.txt deleted file mode 100644 index 41cbf4b7a670..000000000000 --- a/Documentation/devicetree/bindings/input/touchscreen/lpc32xx-tsc.txt +++ /dev/null @@ -1,16 +0,0 @@ -* NXP LPC32xx SoC Touchscreen Controller (TSC) - -Required properties: -- compatible: must be "nxp,lpc3220-tsc" -- reg: physical base address of the controller and length of memory mapped - region. -- interrupts: The TSC/ADC interrupt - -Example: - - tsc@40048000 { - compatible = "nxp,lpc3220-tsc"; - reg = <0x40048000 0x1000>; - interrupt-parent = <&mic>; - interrupts = <39 0>; - }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/nxp,lpc3220-tsc.yaml b/Documentation/devicetree/bindings/input/touchscreen/nxp,lpc3220-tsc.yaml new file mode 100644 index 000000000000..b6feda127c7b --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/nxp,lpc3220-tsc.yaml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/touchscreen/nxp,lpc3220-tsc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC32xx SoC Touchscreen Controller (TSC) + +maintainers: + - Frank Li + +properties: + compatible: + const: nxp,lpc3220-tsc + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - interrupts + +additionalProperties: false + +examples: + - | + #include + + touchscreen@40048000 { + compatible = "nxp,lpc3220-tsc"; + reg = <0x40048000 0x1000>; + interrupt-parent = <&mic>; + interrupts = <39 0>; + clocks = <&clk LPC32XX_CLK_ADC>; + }; From 4aaadf94aab09ccc95f14f75e11e045a1530d364 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 11:39:58 +0200 Subject: [PATCH 02/38] Input: ad7879 - use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Signed-off-by: Bartosz Golaszewski Acked-by: Michael Hennerich Link: https://lore.kernel.org/r/20250610-gpiochip-set-rv-input-v1-1-5875240b48d8@linaro.org Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ad7879.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index f661e199b63c..12fc39f9e784 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -444,10 +444,11 @@ static int ad7879_gpio_get_value(struct gpio_chip *chip, unsigned gpio) return !!(val & AD7879_GPIO_DATA); } -static void ad7879_gpio_set_value(struct gpio_chip *chip, - unsigned gpio, int value) +static int ad7879_gpio_set_value(struct gpio_chip *chip, unsigned int gpio, + int value) { struct ad7879 *ts = gpiochip_get_data(chip); + int ret; mutex_lock(&ts->mutex); if (value) @@ -455,8 +456,10 @@ static void ad7879_gpio_set_value(struct gpio_chip *chip, else ts->cmd_crtl2 &= ~AD7879_GPIO_DATA; - ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); + ret = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); mutex_unlock(&ts->mutex); + + return ret; } static int ad7879_gpio_add(struct ad7879 *ts) @@ -472,7 +475,7 @@ static int ad7879_gpio_add(struct ad7879 *ts) ts->gc.direction_input = ad7879_gpio_direction_input; ts->gc.direction_output = ad7879_gpio_direction_output; ts->gc.get = ad7879_gpio_get_value; - ts->gc.set = ad7879_gpio_set_value; + ts->gc.set_rv = ad7879_gpio_set_value; ts->gc.can_sleep = 1; ts->gc.base = -1; ts->gc.ngpio = 1; From 687f0d0ee5cf3f9efd69e58da8f22e10590cd76c Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 11:39:59 +0200 Subject: [PATCH 03/38] Input: adp5588 - use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Signed-off-by: Bartosz Golaszewski Acked-by: Michael Hennerich Link: https://lore.kernel.org/r/20250610-gpiochip-set-rv-input-v1-2-5875240b48d8@linaro.org Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/adp5588-keys.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index dc734974ce06..2b2aca08423a 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -232,8 +232,8 @@ static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned int off) return !!(val & bit); } -static void adp5588_gpio_set_value(struct gpio_chip *chip, - unsigned int off, int val) +static int adp5588_gpio_set_value(struct gpio_chip *chip, unsigned int off, + int val) { struct adp5588_kpad *kpad = gpiochip_get_data(chip); unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); @@ -246,7 +246,8 @@ static void adp5588_gpio_set_value(struct gpio_chip *chip, else kpad->dat_out[bank] &= ~bit; - adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank, kpad->dat_out[bank]); + return adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank, + kpad->dat_out[bank]); } static int adp5588_gpio_set_config(struct gpio_chip *chip, unsigned int off, @@ -424,7 +425,7 @@ static int adp5588_gpio_add(struct adp5588_kpad *kpad) kpad->gc.direction_input = adp5588_gpio_direction_input; kpad->gc.direction_output = adp5588_gpio_direction_output; kpad->gc.get = adp5588_gpio_get_value; - kpad->gc.set = adp5588_gpio_set_value; + kpad->gc.set_rv = adp5588_gpio_set_value; kpad->gc.set_config = adp5588_gpio_set_config; kpad->gc.can_sleep = 1; From 43a8440f396951eaae85db478b30a53aea8cda7d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 11:40:00 +0200 Subject: [PATCH 04/38] Input: adp5589 - use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Signed-off-by: Bartosz Golaszewski Acked-by: Michael Hennerich Link: https://lore.kernel.org/r/20250610-gpiochip-set-rv-input-v1-3-5875240b48d8@linaro.org Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/adp5589-keys.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c index 81d0876ee358..986a789f1ec3 100644 --- a/drivers/input/keyboard/adp5589-keys.c +++ b/drivers/input/keyboard/adp5589-keys.c @@ -404,8 +404,8 @@ static int adp5589_gpio_get_value(struct gpio_chip *chip, unsigned off) return !!(val & bit); } -static void adp5589_gpio_set_value(struct gpio_chip *chip, - unsigned off, int val) +static int adp5589_gpio_set_value(struct gpio_chip *chip, unsigned int off, + int val) { struct adp5589_kpad *kpad = gpiochip_get_data(chip); unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); @@ -418,8 +418,9 @@ static void adp5589_gpio_set_value(struct gpio_chip *chip, else kpad->dat_out[bank] &= ~bit; - adp5589_write(kpad->client, kpad->var->reg(ADP5589_GPO_DATA_OUT_A) + - bank, kpad->dat_out[bank]); + return adp5589_write(kpad->client, + kpad->var->reg(ADP5589_GPO_DATA_OUT_A) + bank, + kpad->dat_out[bank]); } static int adp5589_gpio_direction_input(struct gpio_chip *chip, unsigned off) @@ -520,7 +521,7 @@ static int adp5589_gpio_add(struct adp5589_kpad *kpad) kpad->gc.direction_input = adp5589_gpio_direction_input; kpad->gc.direction_output = adp5589_gpio_direction_output; kpad->gc.get = adp5589_gpio_get_value; - kpad->gc.set = adp5589_gpio_set_value; + kpad->gc.set_rv = adp5589_gpio_set_value; kpad->gc.can_sleep = 1; kpad->gc.base = gpio_data->gpio_start; From 409fe0cea366ee2e239631ab14337210a5e5f7e4 Mon Sep 17 00:00:00 2001 From: Joseph Guo Date: Sun, 29 Jun 2025 17:35:39 -0700 Subject: [PATCH 05/38] Input: goodix - add support for polling mode There are designs incorporating Goodix touch controller that do not connect interrupt pin, for example Raspberry Pi. To support such systems use polling mode for the input device when I2C client does not have interrupt assigned to it. Signed-off-by: Joseph Guo Reviewed-by: Haibo Chen Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20250522020418.1963422-1-qijian.guo@nxp.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix.c | 50 ++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index a3e8a51c9144..252dcae039f8 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -44,9 +44,11 @@ #define GOODIX_HAVE_KEY BIT(4) #define GOODIX_BUFFER_STATUS_TIMEOUT 20 -#define RESOLUTION_LOC 1 -#define MAX_CONTACTS_LOC 5 -#define TRIGGER_LOC 6 +#define RESOLUTION_LOC 1 +#define MAX_CONTACTS_LOC 5 +#define TRIGGER_LOC 6 + +#define GOODIX_POLL_INTERVAL_MS 17 /* 17ms = 60fps */ /* Our special handling for GPIO accesses through ACPI is x86 specific */ #if defined CONFIG_X86 && defined CONFIG_ACPI @@ -497,6 +499,14 @@ sync: input_sync(ts->input_dev); } +static void goodix_ts_work_i2c_poll(struct input_dev *input) +{ + struct goodix_ts_data *ts = input_get_drvdata(input); + + goodix_process_events(ts); + goodix_i2c_write_u8(ts->client, GOODIX_READ_COOR_ADDR, 0); +} + /** * goodix_ts_irq_handler - The IRQ handler * @@ -513,13 +523,29 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static void goodix_enable_irq(struct goodix_ts_data *ts) +{ + if (ts->client->irq) + enable_irq(ts->client->irq); +} + +static void goodix_disable_irq(struct goodix_ts_data *ts) +{ + if (ts->client->irq) + disable_irq(ts->client->irq); +} + static void goodix_free_irq(struct goodix_ts_data *ts) { - devm_free_irq(&ts->client->dev, ts->client->irq, ts); + if (ts->client->irq) + devm_free_irq(&ts->client->dev, ts->client->irq, ts); } static int goodix_request_irq(struct goodix_ts_data *ts) { + if (!ts->client->irq) + return 0; + return devm_request_threaded_irq(&ts->client->dev, ts->client->irq, NULL, goodix_ts_irq_handler, ts->irq_flags, ts->client->name, ts); @@ -1219,6 +1245,18 @@ retry_read_config: return error; } + input_set_drvdata(ts->input_dev, ts); + + if (!ts->client->irq) { + error = input_setup_polling(ts->input_dev, goodix_ts_work_i2c_poll); + if (error) { + dev_err(&ts->client->dev, + "could not set up polling mode, %d\n", error); + return error; + } + input_set_poll_interval(ts->input_dev, GOODIX_POLL_INTERVAL_MS); + } + error = input_register_device(ts->input_dev); if (error) { dev_err(&ts->client->dev, @@ -1422,7 +1460,7 @@ static int goodix_suspend(struct device *dev) /* We need gpio pins to suspend/resume */ if (ts->irq_pin_access_method == IRQ_PIN_ACCESS_NONE) { - disable_irq(client->irq); + goodix_disable_irq(ts); return 0; } @@ -1466,7 +1504,7 @@ static int goodix_resume(struct device *dev) int error; if (ts->irq_pin_access_method == IRQ_PIN_ACCESS_NONE) { - enable_irq(client->irq); + goodix_enable_irq(ts); return 0; } From fc75e51e6977f12f76cdcc6f9658a61b04cc4b3e Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sun, 29 Jun 2025 22:28:20 +0100 Subject: [PATCH 06/38] Input: pcf50633-input - remove the driver The pcf50633 was used as part of the OpenMoko devices but the support for its main chip was recently removed in: commit 61b7f8920b17 ("ARM: s3c: remove all s3c24xx support") Remove the input driver. This was originally posted as a set of pcf50633 removals in March, and is the only major component that hasn't been picked up. https://lore.kernel.org/all/20250311014959.743322-1-linux@treblig.org/ Signed-off-by: Dr. David Alan Gilbert Link: https://lore.kernel.org/r/20250629212820.319584-1-linux@treblig.org Signed-off-by: Dmitry Torokhov --- drivers/input/misc/Kconfig | 7 -- drivers/input/misc/Makefile | 1 - drivers/input/misc/pcf50633-input.c | 113 ---------------------------- 3 files changed, 121 deletions(-) delete mode 100644 drivers/input/misc/pcf50633-input.c diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index f5496ca0c0d2..0fb21c99a5e3 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -584,13 +584,6 @@ config INPUT_PALMAS_PWRBUTTON To compile this driver as a module, choose M here. The module will be called palmas_pwrbutton. -config INPUT_PCF50633_PMU - tristate "PCF50633 PMU events" - depends on MFD_PCF50633 - help - Say Y to include support for delivering PMU events via input - layer on NXP PCF50633. - config INPUT_PCF8574 tristate "PCF8574 Keypad input device" depends on I2C diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 6d91804d0a6f..d468c8140b93 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -59,7 +59,6 @@ obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o obj-$(CONFIG_INPUT_MMA8450) += mma8450.o obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o -obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o obj-$(CONFIG_INPUT_PM8941_PWRKEY) += pm8941-pwrkey.o diff --git a/drivers/input/misc/pcf50633-input.c b/drivers/input/misc/pcf50633-input.c deleted file mode 100644 index 6d046e236ba6..000000000000 --- a/drivers/input/misc/pcf50633-input.c +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* NXP PCF50633 Input Driver - * - * (C) 2006-2008 by Openmoko, Inc. - * Author: Balaji Rao - * All rights reserved. - * - * Broken down from monstrous PCF50633 driver mainly by - * Harald Welte, Andy Green and Werner Almesberger - */ - -#include -#include -#include -#include -#include -#include - -#include - -#define PCF50633_OOCSTAT_ONKEY 0x01 -#define PCF50633_REG_OOCSTAT 0x12 -#define PCF50633_REG_OOCMODE 0x10 - -struct pcf50633_input { - struct pcf50633 *pcf; - struct input_dev *input_dev; -}; - -static void -pcf50633_input_irq(int irq, void *data) -{ - struct pcf50633_input *input; - int onkey_released; - - input = data; - - /* We report only one event depending on the key press status */ - onkey_released = pcf50633_reg_read(input->pcf, PCF50633_REG_OOCSTAT) - & PCF50633_OOCSTAT_ONKEY; - - if (irq == PCF50633_IRQ_ONKEYF && !onkey_released) - input_report_key(input->input_dev, KEY_POWER, 1); - else if (irq == PCF50633_IRQ_ONKEYR && onkey_released) - input_report_key(input->input_dev, KEY_POWER, 0); - - input_sync(input->input_dev); -} - -static int pcf50633_input_probe(struct platform_device *pdev) -{ - struct pcf50633_input *input; - struct input_dev *input_dev; - int ret; - - - input = kzalloc(sizeof(*input), GFP_KERNEL); - if (!input) - return -ENOMEM; - - input_dev = input_allocate_device(); - if (!input_dev) { - kfree(input); - return -ENOMEM; - } - - platform_set_drvdata(pdev, input); - input->pcf = dev_to_pcf50633(pdev->dev.parent); - input->input_dev = input_dev; - - input_dev->name = "PCF50633 PMU events"; - input_dev->id.bustype = BUS_I2C; - input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_PWR); - set_bit(KEY_POWER, input_dev->keybit); - - ret = input_register_device(input_dev); - if (ret) { - input_free_device(input_dev); - kfree(input); - return ret; - } - pcf50633_register_irq(input->pcf, PCF50633_IRQ_ONKEYR, - pcf50633_input_irq, input); - pcf50633_register_irq(input->pcf, PCF50633_IRQ_ONKEYF, - pcf50633_input_irq, input); - - return 0; -} - -static void pcf50633_input_remove(struct platform_device *pdev) -{ - struct pcf50633_input *input = platform_get_drvdata(pdev); - - pcf50633_free_irq(input->pcf, PCF50633_IRQ_ONKEYR); - pcf50633_free_irq(input->pcf, PCF50633_IRQ_ONKEYF); - - input_unregister_device(input->input_dev); - kfree(input); -} - -static struct platform_driver pcf50633_input_driver = { - .driver = { - .name = "pcf50633-input", - }, - .probe = pcf50633_input_probe, - .remove = pcf50633_input_remove, -}; -module_platform_driver(pcf50633_input_driver); - -MODULE_AUTHOR("Balaji Rao "); -MODULE_DESCRIPTION("PCF50633 input driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:pcf50633-input"); From 54e626d097b05af9421534d211c9f96211d07d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 30 Jun 2025 12:24:31 -0700 Subject: [PATCH 07/38] Input: max8997_haptic - optimize PWM configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both pwm_config() and pwm_enable() are wrappers around pwm_apply_might_sleep(). Instead of calling this function twice only call it once without an intermediate step. Setup the PWM in max8997_haptic_enable() only where it was enabled historically. max8997_haptic_set_duty_cycle() is renamed accordingly to make it clear this function is only about the internal setup now. pwm_config() was called earlier back then, but that call has no effect on the hardware when the PWM is disabled, so delaying this configuration doesn't make a difference. As pwm_apply_might_sleep() is used now defining the whole state of the PWM, the call to pwm_apply_args() in .probe() can be dropped now, too. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250630093718.2062359-2-u.kleine-koenig@baylibre.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/max8997_haptic.c | 98 ++++++++++++++--------------- 1 file changed, 48 insertions(+), 50 deletions(-) diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c index f97f341ee0bb..d5e051a25a74 100644 --- a/drivers/input/misc/max8997_haptic.c +++ b/drivers/input/misc/max8997_haptic.c @@ -53,40 +53,30 @@ struct max8997_haptic { unsigned int pattern_signal_period; }; -static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip) +static void max8997_haptic_set_internal_duty_cycle(struct max8997_haptic *chip) { - int ret = 0; + u8 duty_index = DIV_ROUND_UP(chip->level * 64, 100); - if (chip->mode == MAX8997_EXTERNAL_MODE) { - unsigned int duty = chip->pwm_period * chip->level / 100; - ret = pwm_config(chip->pwm, duty, chip->pwm_period); - } else { - u8 duty_index = 0; - - duty_index = DIV_ROUND_UP(chip->level * 64, 100); - - switch (chip->internal_mode_pattern) { - case 0: - max8997_write_reg(chip->client, - MAX8997_HAPTIC_REG_SIGPWMDC1, duty_index); - break; - case 1: - max8997_write_reg(chip->client, - MAX8997_HAPTIC_REG_SIGPWMDC2, duty_index); - break; - case 2: - max8997_write_reg(chip->client, - MAX8997_HAPTIC_REG_SIGPWMDC3, duty_index); - break; - case 3: - max8997_write_reg(chip->client, - MAX8997_HAPTIC_REG_SIGPWMDC4, duty_index); - break; - default: - break; - } + switch (chip->internal_mode_pattern) { + case 0: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC1, duty_index); + break; + case 1: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC2, duty_index); + break; + case 2: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC3, duty_index); + break; + case 3: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC4, duty_index); + break; + default: + break; } - return ret; } static void max8997_haptic_configure(struct max8997_haptic *chip) @@ -155,11 +145,8 @@ static void max8997_haptic_enable(struct max8997_haptic *chip) guard(mutex)(&chip->mutex); - error = max8997_haptic_set_duty_cycle(chip); - if (error) { - dev_err(chip->dev, "set_pwm_cycle failed, error: %d\n", error); - return; - } + if (chip->mode != MAX8997_EXTERNAL_MODE) + max8997_haptic_set_internal_duty_cycle(chip); if (!chip->enabled) { error = regulator_enable(chip->regulator); @@ -168,16 +155,32 @@ static void max8997_haptic_enable(struct max8997_haptic *chip) return; } max8997_haptic_configure(chip); - if (chip->mode == MAX8997_EXTERNAL_MODE) { - error = pwm_enable(chip->pwm); - if (error) { - dev_err(chip->dev, "Failed to enable PWM\n"); - regulator_disable(chip->regulator); - return; - } - } - chip->enabled = true; } + + /* + * It would be more straight forward to configure the external PWM + * earlier i.e. when the internal duty_cycle is setup in internal mode. + * But historically this is done only after the regulator was enabled + * and max8997_haptic_configure() set the enable bit in + * MAX8997_HAPTIC_REG_CONF2. So better keep it this way. + */ + if (chip->mode == MAX8997_EXTERNAL_MODE) { + struct pwm_state state; + + pwm_init_state(chip->pwm, &state); + state.period = chip->pwm_period; + state.duty_cycle = chip->pwm_period * chip->level / 100; + state.enabled = true; + + error = pwm_apply_might_sleep(chip->pwm, &state); + if (error) { + dev_err(chip->dev, "Failed to enable PWM\n"); + regulator_disable(chip->regulator); + return; + } + } + + chip->enabled = true; } static void max8997_haptic_disable(struct max8997_haptic *chip) @@ -282,11 +285,6 @@ static int max8997_haptic_probe(struct platform_device *pdev) goto err_free_mem; } - /* - * FIXME: pwm_apply_args() should be removed when switching to - * the atomic PWM API. - */ - pwm_apply_args(chip->pwm); break; default: From 45601c66b5ddbe665937659b957e71bc8efedd46 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Thu, 29 May 2025 15:32:35 -0400 Subject: [PATCH 08/38] dt-bindings: input: touchscreen: convert tsc2007.txt to yaml format Convert tsc2007.txt to yaml format. Additional changes: - add pendown-gpio property to match existed dts. Signed-off-by: Frank Li Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250529193241.793678-1-Frank.Li@nxp.com Signed-off-by: Dmitry Torokhov --- .../input/touchscreen/ti.tsc2007.yaml | 75 +++++++++++++++++++ .../bindings/input/touchscreen/tsc2007.txt | 39 ---------- 2 files changed, 75 insertions(+), 39 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/ti.tsc2007.yaml delete mode 100644 Documentation/devicetree/bindings/input/touchscreen/tsc2007.txt diff --git a/Documentation/devicetree/bindings/input/touchscreen/ti.tsc2007.yaml b/Documentation/devicetree/bindings/input/touchscreen/ti.tsc2007.yaml new file mode 100644 index 000000000000..8bb4bc7df4fa --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/ti.tsc2007.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/touchscreen/ti.tsc2007.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments tsc2007 touchscreen controller + +maintainers: + - Frank Li + +properties: + compatible: + const: ti,tsc2007 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + ti,x-plate-ohms: + description: X-plate resistance in ohms. + + gpios: true + + pendown-gpio: true + + ti,max-rt: + $ref: /schemas/types.yaml#/definitions/uint32 + description: maximum pressure. + + ti,fuzzx: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + specifies the absolute input fuzz x value. + If set, it will permit noise in the data up to +- the value given to the fuzz + parameter, that is used to filter noise from the event stream. + + ti,fuzzy: + $ref: /schemas/types.yaml#/definitions/uint32 + description: specifies the absolute input fuzz y value. + + ti,fuzzz: + $ref: /schemas/types.yaml#/definitions/uint32 + description: specifies the absolute input fuzz z value. + + ti,poll-period: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + how much time to wait (in milliseconds) before reading again the + values from the tsc2007. + +required: + - compatible + - reg + - ti,x-plate-ohms + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + touch@49 { + compatible = "ti,tsc2007"; + reg = <0x49>; + interrupt-parent = <&gpio4>; + interrupts = <0x0 0x8>; + gpios = <&gpio4 0 0>; + ti,x-plate-ohms = <180>; + }; + }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/tsc2007.txt b/Documentation/devicetree/bindings/input/touchscreen/tsc2007.txt deleted file mode 100644 index 210486a3fb11..000000000000 --- a/Documentation/devicetree/bindings/input/touchscreen/tsc2007.txt +++ /dev/null @@ -1,39 +0,0 @@ -* Texas Instruments tsc2007 touchscreen controller - -Required properties: -- compatible: must be "ti,tsc2007". -- reg: I2C address of the chip. -- ti,x-plate-ohms: X-plate resistance in ohms. - -Optional properties: -- gpios: the interrupt gpio the chip is connected to (through the penirq pin). - The penirq pin goes to low when the panel is touched. - (see GPIO binding[1] for more details). -- interrupts: (gpio) interrupt to which the chip is connected - (see interrupt binding[0]). -- ti,max-rt: maximum pressure. -- ti,fuzzx: specifies the absolute input fuzz x value. - If set, it will permit noise in the data up to +- the value given to the fuzz - parameter, that is used to filter noise from the event stream. -- ti,fuzzy: specifies the absolute input fuzz y value. -- ti,fuzzz: specifies the absolute input fuzz z value. -- ti,poll-period: how much time to wait (in milliseconds) before reading again the - values from the tsc2007. - -[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt -[1]: Documentation/devicetree/bindings/gpio/gpio.txt - -Example: - &i2c1 { - /* ... */ - tsc2007@49 { - compatible = "ti,tsc2007"; - reg = <0x49>; - interrupt-parent = <&gpio4>; - interrupts = <0x0 0x8>; - gpios = <&gpio4 0 0>; - ti,x-plate-ohms = <180>; - }; - - /* ... */ - }; From 6a71a6679fcbe96f894292d6cfc0d58eabf9ac8c Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Tue, 1 Jul 2025 11:33:18 -0700 Subject: [PATCH 09/38] Input: cs40l50 - remove redundant flush_workqueue() calls destroy_workqueue() already drains the queue before destroying it, so there is no need to flush it explicitly. Remove the redundant 'flush_workqueue()' calls. Signed-off-by: Chen Ni Link: https://lore.kernel.org/r/20250312072940.1429931-1-nichen@iscas.ac.cn Signed-off-by: Dmitry Torokhov --- drivers/input/misc/cs40l50-vibra.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/misc/cs40l50-vibra.c b/drivers/input/misc/cs40l50-vibra.c index dce3b0ec8cf3..0fc7ab032cf5 100644 --- a/drivers/input/misc/cs40l50-vibra.c +++ b/drivers/input/misc/cs40l50-vibra.c @@ -480,7 +480,6 @@ static int cs40l50_erase(struct input_dev *dev, int effect_id) static void cs40l50_remove_wq(void *data) { - flush_workqueue(data); destroy_workqueue(data); } From 68743c500c6eafcd0b16dc6067fea5bca0795eef Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 1 Jul 2025 11:40:06 -0700 Subject: [PATCH 10/38] Input: edt-ft5x06 - use per-client debugfs directory The I2C core now provides a debugfs entry for each client. Let this driver use it instead of the custom directory in debugfs root. Further improvements by this change: support of multiple instances. Signed-off-by: Wolfram Sang Link: https://lore.kernel.org/r/20250318091904.22468-1-wsa+renesas@sang-engineering.com Signed-off-by: Dmitry Torokhov --- Documentation/input/devices/edt-ft5x06.rst | 21 +++++++++++++++++++-- drivers/input/touchscreen/edt-ft5x06.c | 20 ++++++++------------ 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/Documentation/input/devices/edt-ft5x06.rst b/Documentation/input/devices/edt-ft5x06.rst index 1ccc94b192b7..e410d73d4841 100644 --- a/Documentation/input/devices/edt-ft5x06.rst +++ b/Documentation/input/devices/edt-ft5x06.rst @@ -29,8 +29,25 @@ The driver allows configuration of the touch screen via a set of sysfs files: For debugging purposes the driver provides a few files in the debug -filesystem (if available in the kernel). In /sys/kernel/debug/edt_ft5x06 -you'll find the following files: +filesystem (if available in the kernel). They are located in: + + /sys/kernel/debug/i2c/// + +If you don't know the bus and device numbers, you can look them up with this +command: + + $ ls -l /sys/bus/i2c/drivers/edt_ft5x06 + +The dereference of the symlink will contain the needed information. You will +need the last two elements of its path: + + 0-0038 -> ../../../../devices/platform/soc/fcfee800.i2c/i2c-0/0-0038 + +So in this case, the location for the debug files is: + + /sys/kernel/debug/i2c/i2c-0/0-0038/ + +There, you'll find the following files: num_x, num_y: (readonly) contains the number of sensor fields in X- and diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 0d7bf18e2508..abc5bbb5c8c9 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -120,7 +120,6 @@ struct edt_ft5x06_ts_data { struct regmap *regmap; #if defined(CONFIG_DEBUG_FS) - struct dentry *debug_dir; u8 *raw_buffer; size_t raw_bufsize; #endif @@ -815,23 +814,21 @@ static const struct file_operations debugfs_raw_data_fops = { .read = edt_ft5x06_debugfs_raw_data_read, }; -static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, - const char *debugfs_name) +static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata) { - tsdata->debug_dir = debugfs_create_dir(debugfs_name, NULL); + struct dentry *debug_dir = tsdata->client->debugfs; - debugfs_create_u16("num_x", S_IRUSR, tsdata->debug_dir, &tsdata->num_x); - debugfs_create_u16("num_y", S_IRUSR, tsdata->debug_dir, &tsdata->num_y); + debugfs_create_u16("num_x", S_IRUSR, debug_dir, &tsdata->num_x); + debugfs_create_u16("num_y", S_IRUSR, debug_dir, &tsdata->num_y); debugfs_create_file("mode", S_IRUSR | S_IWUSR, - tsdata->debug_dir, tsdata, &debugfs_mode_fops); + debug_dir, tsdata, &debugfs_mode_fops); debugfs_create_file("raw_data", S_IRUSR, - tsdata->debug_dir, tsdata, &debugfs_raw_data_fops); + debug_dir, tsdata, &debugfs_raw_data_fops); } static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) { - debugfs_remove_recursive(tsdata->debug_dir); kfree(tsdata->raw_buffer); } @@ -842,8 +839,7 @@ static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata) return -ENOSYS; } -static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, - const char *debugfs_name) +static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata) { } @@ -1349,7 +1345,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client) if (error) return error; - edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev)); + edt_ft5x06_ts_prepare_debugfs(tsdata); dev_dbg(&client->dev, "EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n", From 06226bd497dd99780968600e498f95e8d049d1c2 Mon Sep 17 00:00:00 2001 From: Jens Reidel Date: Thu, 13 Mar 2025 21:20:16 +0100 Subject: [PATCH 11/38] dt-bindings: input: touchscreen: edt-ft5x06: Document FT8716 support Document FocalTech FT8716 support by adding the compatible. Signed-off-by: Jens Reidel Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20250313202017.19621-2-adrian@mainlining.org Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/touchscreen/edt-ft5x06.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml index ab821490284a..7d3edb58f72d 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml @@ -43,6 +43,7 @@ properties: - focaltech,ft5452 - focaltech,ft6236 - focaltech,ft8201 + - focaltech,ft8716 - focaltech,ft8719 reg: From c6f908f88a55be7641d78a99053818147bde93e9 Mon Sep 17 00:00:00 2001 From: Jens Reidel Date: Thu, 13 Mar 2025 21:20:17 +0100 Subject: [PATCH 12/38] Input: edt-ft5x06 - add support for FocalTech FT8716 This driver is compatible with the FocalTech FT8716 touchscreen, which supports up to 10 concurrent touch points. Add a compatible for it. Signed-off-by: Jens Reidel Link: https://lore.kernel.org/r/20250313202017.19621-3-adrian@mainlining.org Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/edt-ft5x06.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index abc5bbb5c8c9..bf498bd4dea9 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -1491,6 +1491,10 @@ static const struct edt_i2c_chip_data edt_ft8201_data = { .max_support_points = 10, }; +static const struct edt_i2c_chip_data edt_ft8716_data = { + .max_support_points = 10, +}; + static const struct edt_i2c_chip_data edt_ft8719_data = { .max_support_points = 10, }; @@ -1503,6 +1507,7 @@ static const struct i2c_device_id edt_ft5x06_ts_id[] = { /* Note no edt- prefix for compatibility with the ft6236.c driver */ { .name = "ft6236", .driver_data = (long)&edt_ft6236_data }, { .name = "ft8201", .driver_data = (long)&edt_ft8201_data }, + { .name = "ft8716", .driver_data = (long)&edt_ft8716_data }, { .name = "ft8719", .driver_data = (long)&edt_ft8719_data }, { /* sentinel */ } }; @@ -1519,6 +1524,7 @@ static const struct of_device_id edt_ft5x06_of_match[] = { /* Note focaltech vendor prefix for compatibility with ft6236.c */ { .compatible = "focaltech,ft6236", .data = &edt_ft6236_data }, { .compatible = "focaltech,ft8201", .data = &edt_ft8201_data }, + { .compatible = "focaltech,ft8716", .data = &edt_ft8716_data }, { .compatible = "focaltech,ft8719", .data = &edt_ft8719_data }, { /* sentinel */ } }; From e65efc62ca352906c880796e9ea2f2d77299de97 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 18 Aug 2024 21:57:58 -0700 Subject: [PATCH 13/38] Input: samsung-keypad - switch to using devm_clk_get_prepared() Switch to using devm_clk_get_prepared() instead of combining devm_clk_get() with clk_prepare(), which simplifies the code and ensures that the clock is unprepared at the right time relative to releasing other managed resources. Link: https://lore.kernel.org/r/20240819045813.2154642-2-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/samsung-keypad.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index 9f1049aa3048..edd085f86df0 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -361,18 +361,12 @@ static int samsung_keypad_probe(struct platform_device *pdev) if (!keypad->base) return -EBUSY; - keypad->clk = devm_clk_get(&pdev->dev, "keypad"); + keypad->clk = devm_clk_get_prepared(&pdev->dev, "keypad"); if (IS_ERR(keypad->clk)) { dev_err(&pdev->dev, "failed to get keypad clk\n"); return PTR_ERR(keypad->clk); } - error = clk_prepare(keypad->clk); - if (error) { - dev_err(&pdev->dev, "keypad clock prepare failed\n"); - return error; - } - keypad->input_dev = input_dev; keypad->pdev = pdev; keypad->row_shift = row_shift; @@ -399,7 +393,7 @@ static int samsung_keypad_probe(struct platform_device *pdev) keypad->keycodes, input_dev); if (error) { dev_err(&pdev->dev, "failed to build keymap\n"); - goto err_unprepare_clk; + return error; } input_set_capability(input_dev, EV_MSC, MSC_SCAN); @@ -411,7 +405,7 @@ static int samsung_keypad_probe(struct platform_device *pdev) keypad->irq = platform_get_irq(pdev, 0); if (keypad->irq < 0) { error = keypad->irq; - goto err_unprepare_clk; + return error; } error = devm_request_threaded_irq(&pdev->dev, keypad->irq, NULL, @@ -419,7 +413,7 @@ static int samsung_keypad_probe(struct platform_device *pdev) dev_name(&pdev->dev), keypad); if (error) { dev_err(&pdev->dev, "failed to register keypad interrupt\n"); - goto err_unprepare_clk; + return error; } device_init_wakeup(&pdev->dev, pdata->wakeup); @@ -439,20 +433,12 @@ static int samsung_keypad_probe(struct platform_device *pdev) err_disable_runtime_pm: pm_runtime_disable(&pdev->dev); -err_unprepare_clk: - clk_unprepare(keypad->clk); return error; } static void samsung_keypad_remove(struct platform_device *pdev) { - struct samsung_keypad *keypad = platform_get_drvdata(pdev); - pm_runtime_disable(&pdev->dev); - - input_unregister_device(keypad->input_dev); - - clk_unprepare(keypad->clk); } static int samsung_keypad_runtime_suspend(struct device *dev) From 4d4d74c6c9931b3c16ec49f0fb26c547be90f998 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 18 Aug 2024 21:57:59 -0700 Subject: [PATCH 14/38] Input: samsung-keypad - do not set input device's parent explicitly The driver uses devm_input_allocate_device() to allocate instances of input device, which sets the parent appropriately. Remove extraneous assignment. Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240819045813.2154642-3-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/samsung-keypad.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index edd085f86df0..b0e22903bb1c 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -383,7 +383,6 @@ static int samsung_keypad_probe(struct platform_device *pdev) input_dev->name = pdev->name; input_dev->id.bustype = BUS_HOST; - input_dev->dev.parent = &pdev->dev; input_dev->open = samsung_keypad_open; input_dev->close = samsung_keypad_close; From 706a066328dbdd7a15ac4904905fa7f8bd29cdf0 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 18 Aug 2024 21:58:00 -0700 Subject: [PATCH 15/38] Input: samsung-keypad - do not combine memory allocation checks The driver uses devm API to allocate resources so there's no reason to do allocations first and then check and release everything at once. Check results immediately after doing allocation of each resource. Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240819045813.2154642-4-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/samsung-keypad.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index b0e22903bb1c..ada4cd151dc6 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -349,8 +349,11 @@ static int samsung_keypad_probe(struct platform_device *pdev) keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad) + keymap_size, GFP_KERNEL); + if (!keypad) + return -ENOMEM; + input_dev = devm_input_allocate_device(&pdev->dev); - if (!keypad || !input_dev) + if (!input_dev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); From 647fc2bfe21e1114b06de8ee2bc1e479072e7509 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 18 Aug 2024 21:58:01 -0700 Subject: [PATCH 16/38] Input: samsung-keypad - use struct_size() helper When allocating memory for the keypad use struct_size() helper to be protected from overflows. Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240819045813.2154642-5-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/samsung-keypad.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index ada4cd151dc6..e3a1b0db7ec8 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -318,7 +318,6 @@ static int samsung_keypad_probe(struct platform_device *pdev) struct resource *res; struct input_dev *input_dev; unsigned int row_shift; - unsigned int keymap_size; int error; pdata = dev_get_platdata(&pdev->dev); @@ -345,9 +344,10 @@ static int samsung_keypad_probe(struct platform_device *pdev) pdata->cfg_gpio(pdata->rows, pdata->cols); row_shift = get_count_order(pdata->cols); - keymap_size = (pdata->rows << row_shift) * sizeof(keypad->keycodes[0]); - keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad) + keymap_size, + keypad = devm_kzalloc(&pdev->dev, + struct_size(keypad, keycodes, + pdata->rows << row_shift), GFP_KERNEL); if (!keypad) return -ENOMEM; From 5658439a5fc71272be9457b5028bdcd68c559b7a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 24 Jul 2024 14:23:16 -0700 Subject: [PATCH 17/38] Input: samsung-keypad - use devm to disable runtime PM To make sure that runtime PM is disabled at the right time compared to all other devm-managed resources use devm_pm_runtime_enable(). Link: https://lore.kernel.org/r/20240819045813.2154642-6-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/samsung-keypad.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index e3a1b0db7ec8..d8eda10d63ee 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -420,11 +420,14 @@ static int samsung_keypad_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, pdata->wakeup); platform_set_drvdata(pdev, keypad); - pm_runtime_enable(&pdev->dev); + + error = devm_pm_runtime_enable(&pdev->dev); + if (error) + return error; error = input_register_device(keypad->input_dev); if (error) - goto err_disable_runtime_pm; + return error; if (pdev->dev.of_node) { devm_kfree(&pdev->dev, (void *)pdata->keymap_data->keymap); @@ -432,15 +435,6 @@ static int samsung_keypad_probe(struct platform_device *pdev) devm_kfree(&pdev->dev, (void *)pdata); } return 0; - -err_disable_runtime_pm: - pm_runtime_disable(&pdev->dev); - return error; -} - -static void samsung_keypad_remove(struct platform_device *pdev) -{ - pm_runtime_disable(&pdev->dev); } static int samsung_keypad_runtime_suspend(struct device *dev) @@ -575,7 +569,6 @@ MODULE_DEVICE_TABLE(platform, samsung_keypad_driver_ids); static struct platform_driver samsung_keypad_driver = { .probe = samsung_keypad_probe, - .remove = samsung_keypad_remove, .driver = { .name = "samsung-keypad", .of_match_table = of_match_ptr(samsung_keypad_dt_match), From f1e5f6827dd36dda9a3745ead9f142f66429f14a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 18 Aug 2024 21:58:03 -0700 Subject: [PATCH 18/38] Input: samsung-keypad - use guard notation to acquire mutex Guard notation is more compact and ensures that the mutex will be released when control leaves the function. Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240819045813.2154642-7-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/samsung-keypad.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index d8eda10d63ee..80a495e7e93a 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -510,15 +510,13 @@ static int samsung_keypad_suspend(struct device *dev) struct samsung_keypad *keypad = platform_get_drvdata(pdev); struct input_dev *input_dev = keypad->input_dev; - mutex_lock(&input_dev->mutex); + guard(mutex)(&input_dev->mutex); if (input_device_enabled(input_dev)) samsung_keypad_stop(keypad); samsung_keypad_toggle_wakeup(keypad, true); - mutex_unlock(&input_dev->mutex); - return 0; } @@ -528,15 +526,13 @@ static int samsung_keypad_resume(struct device *dev) struct samsung_keypad *keypad = platform_get_drvdata(pdev); struct input_dev *input_dev = keypad->input_dev; - mutex_lock(&input_dev->mutex); + guard(mutex)(&input_dev->mutex); samsung_keypad_toggle_wakeup(keypad, false); if (input_device_enabled(input_dev)) samsung_keypad_start(keypad); - mutex_unlock(&input_dev->mutex); - return 0; } From 42121e7828fde8559de76af301770ed2bf3e1527 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 18 Aug 2024 21:58:04 -0700 Subject: [PATCH 19/38] Input: samsung-keypad - use per-chip parameters Instead of doing conditional logic based on the chip type, define per-chip parameter structure, and reference it in the match data (either of_device_id or platform_device_id). Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240819045813.2154642-8-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/samsung-keypad.c | 61 +++++++++++++++---------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index 80a495e7e93a..dcb40ab939b9 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -44,8 +44,7 @@ #define S5PV210_KEYIFSTSCLR_R_INT_OFFSET 16 /* SAMSUNG_KEYIFCOL */ -#define SAMSUNG_KEYIFCOL_MASK (0xff << 0) -#define S5PV210_KEYIFCOLEN_MASK (0xff << 8) +#define SAMSUNG_KEYIFCOL_MASK 0xff /* SAMSUNG_KEYIFROW */ #define SAMSUNG_KEYIFROW_MASK (0xff << 0) @@ -54,12 +53,12 @@ /* SAMSUNG_KEYIFFC */ #define SAMSUNG_KEYIFFC_MASK (0x3ff << 0) -enum samsung_keypad_type { - KEYPAD_TYPE_SAMSUNG, - KEYPAD_TYPE_S5PV210, +struct samsung_chip_info { + unsigned int column_shift; }; struct samsung_keypad { + const struct samsung_chip_info *chip; struct input_dev *input_dev; struct platform_device *pdev; struct clk *clk; @@ -68,7 +67,6 @@ struct samsung_keypad { bool stopped; bool wake_enabled; int irq; - enum samsung_keypad_type type; unsigned int row_shift; unsigned int rows; unsigned int cols; @@ -83,13 +81,8 @@ static void samsung_keypad_scan(struct samsung_keypad *keypad, unsigned int val; for (col = 0; col < keypad->cols; col++) { - if (keypad->type == KEYPAD_TYPE_S5PV210) { - val = S5PV210_KEYIFCOLEN_MASK; - val &= ~(1 << col) << 8; - } else { - val = SAMSUNG_KEYIFCOL_MASK; - val &= ~(1 << col); - } + val = SAMSUNG_KEYIFCOL_MASK & ~(1 << col); + val <<= keypad->chip->column_shift; writel(val, keypad->base + SAMSUNG_KEYIFCOL); mdelay(1); @@ -314,6 +307,7 @@ static int samsung_keypad_probe(struct platform_device *pdev) { const struct samsung_keypad_platdata *pdata; const struct matrix_keymap_data *keymap_data; + const struct platform_device_id *id; struct samsung_keypad *keypad; struct resource *res; struct input_dev *input_dev; @@ -378,11 +372,17 @@ static int samsung_keypad_probe(struct platform_device *pdev) keypad->stopped = true; init_waitqueue_head(&keypad->wait); - if (pdev->dev.of_node) - keypad->type = of_device_is_compatible(pdev->dev.of_node, - "samsung,s5pv210-keypad"); - else - keypad->type = platform_get_device_id(pdev)->driver_data; + keypad->chip = device_get_match_data(&pdev->dev); + if (!keypad->chip) { + id = platform_get_device_id(pdev); + if (id) + keypad->chip = (const void *)id->driver_data; + } + + if (!keypad->chip) { + dev_err(&pdev->dev, "Unable to determine chip type"); + return -EINVAL; + } input_dev->name = pdev->name; input_dev->id.bustype = BUS_HOST; @@ -542,11 +542,24 @@ static const struct dev_pm_ops samsung_keypad_pm_ops = { samsung_keypad_runtime_resume, NULL) }; +static const struct samsung_chip_info samsung_s3c6410_chip_info = { + .column_shift = 0, +}; + +static const struct samsung_chip_info samsung_s5pv210_chip_info = { + .column_shift = 8, +}; + #ifdef CONFIG_OF static const struct of_device_id samsung_keypad_dt_match[] = { - { .compatible = "samsung,s3c6410-keypad" }, - { .compatible = "samsung,s5pv210-keypad" }, - {}, + { + .compatible = "samsung,s3c6410-keypad", + .data = &samsung_s3c6410_chip_info, + }, { + .compatible = "samsung,s5pv210-keypad", + .data = &samsung_s5pv210_chip_info, + }, + { } }; MODULE_DEVICE_TABLE(of, samsung_keypad_dt_match); #endif @@ -554,12 +567,12 @@ MODULE_DEVICE_TABLE(of, samsung_keypad_dt_match); static const struct platform_device_id samsung_keypad_driver_ids[] = { { .name = "samsung-keypad", - .driver_data = KEYPAD_TYPE_SAMSUNG, + .driver_data = (kernel_ulong_t)&samsung_s3c6410_chip_info, }, { .name = "s5pv210-keypad", - .driver_data = KEYPAD_TYPE_S5PV210, + .driver_data = (kernel_ulong_t)&samsung_s5pv210_chip_info, }, - { }, + { } }; MODULE_DEVICE_TABLE(platform, samsung_keypad_driver_ids); From a8353b632eb0fa95fe89086e0ac83e550d3c2c42 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 18 Aug 2024 21:58:05 -0700 Subject: [PATCH 20/38] Input: samsung-keypad - use BIT() and GENMASK() where appropriate Instead of using (1 << ) construct use BIT() helper. Convert (1 << ) - 1 to GENMASK(). Link: https://lore.kernel.org/r/20240819045813.2154642-9-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/samsung-keypad.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index dcb40ab939b9..17127269e3f0 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -7,6 +7,7 @@ * Author: Donghwa Lee */ +#include #include #include #include @@ -29,11 +30,11 @@ #define SAMSUNG_KEYIFFC 0x10 /* SAMSUNG_KEYIFCON */ -#define SAMSUNG_KEYIFCON_INT_F_EN (1 << 0) -#define SAMSUNG_KEYIFCON_INT_R_EN (1 << 1) -#define SAMSUNG_KEYIFCON_DF_EN (1 << 2) -#define SAMSUNG_KEYIFCON_FC_EN (1 << 3) -#define SAMSUNG_KEYIFCON_WAKEUPEN (1 << 4) +#define SAMSUNG_KEYIFCON_INT_F_EN BIT(0) +#define SAMSUNG_KEYIFCON_INT_R_EN BIT(1) +#define SAMSUNG_KEYIFCON_DF_EN BIT(2) +#define SAMSUNG_KEYIFCON_FC_EN BIT(3) +#define SAMSUNG_KEYIFCON_WAKEUPEN BIT(4) /* SAMSUNG_KEYIFSTSCLR */ #define SAMSUNG_KEYIFSTSCLR_P_INT_MASK (0xff << 0) @@ -81,14 +82,14 @@ static void samsung_keypad_scan(struct samsung_keypad *keypad, unsigned int val; for (col = 0; col < keypad->cols; col++) { - val = SAMSUNG_KEYIFCOL_MASK & ~(1 << col); + val = SAMSUNG_KEYIFCOL_MASK & ~BIT(col); val <<= keypad->chip->column_shift; writel(val, keypad->base + SAMSUNG_KEYIFCOL); mdelay(1); val = readl(keypad->base + SAMSUNG_KEYIFROW); - row_state[col] = ~val & ((1 << keypad->rows) - 1); + row_state[col] = ~val & GENMASK(keypad->rows - 1, 0); } /* KEYIFCOL reg clear */ @@ -112,10 +113,10 @@ static bool samsung_keypad_report(struct samsung_keypad *keypad, continue; for (row = 0; row < keypad->rows; row++) { - if (!(changed & (1 << row))) + if (!(changed & BIT(row))) continue; - pressed = row_state[col] & (1 << row); + pressed = row_state[col] & BIT(row); dev_dbg(&keypad->input_dev->dev, "key %s, row: %d, col: %d\n", From 84060ea3e0b6294abde57b85502ccf9fa65f94de Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 18 Feb 2025 15:09:21 -0800 Subject: [PATCH 21/38] Input: evdev - switch matching to EV_SYN Each input device has EV_SYN capability. This is enforced by the input core which sets this capability bit unconditionally in input_register_device(). Switch evdev matching from declaring non-zero id->driver_info to match on EV_SYN so that special handling can be removed from input_match_device() and "driver_info" field can be removed from input_device_id structure. Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index b5cbb57ee5f6..90ff6be85cf4 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -1408,8 +1408,12 @@ static void evdev_disconnect(struct input_handle *handle) } static const struct input_device_id evdev_ids[] = { - { .driver_info = 1 }, /* Matches all devices */ - { }, /* Terminating zero entry */ + { + /* Matches all devices */ + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_SYN) }, + }, + { } /* Terminating zero entry */ }; MODULE_DEVICE_TABLE(input, evdev_ids); From bf4e6e3331effa91fdf7cac6b5dd7ff4ec18c227 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 19 Feb 2025 09:14:23 -0800 Subject: [PATCH 22/38] Input: remove special handling of id->driver_info when matching evdev has switched to match on EV_SYN instead of relying on non-zero driver_info field to allow matching to all devices. Remove special handling from input core. Signed-off-by: Dmitry Torokhov --- drivers/input/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/input.c b/drivers/input/input.c index ec4346f20efd..abd272526303 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -971,7 +971,7 @@ static const struct input_device_id *input_match_device(struct input_handler *ha { const struct input_device_id *id; - for (id = handler->id_table; id->flags || id->driver_info; id++) { + for (id = handler->id_table; id->flags; id++) { if (input_match_device_id(dev, id) && (!handler->match || handler->match(handler, dev))) { return id; From bc25e6bf032e8fb17e6985c6393a6cdee6010a28 Mon Sep 17 00:00:00 2001 From: Louis-Alexis Eyraud Date: Thu, 3 Jul 2025 17:41:03 +0200 Subject: [PATCH 23/38] Input: mtk-pmic-keys - add support for MT6359 PMIC keys Add PMIC key support on MT6359 SoC. Signed-off-by: Louis-Alexis Eyraud Link: https://lore.kernel.org/r/20250703-add-mt6359-pmic-keys-support-v1-1-21a4d2774e34@collabora.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/mtk-pmic-keys.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/input/keyboard/mtk-pmic-keys.c b/drivers/input/keyboard/mtk-pmic-keys.c index 061d48350df6..50e2e792c91d 100644 --- a/drivers/input/keyboard/mtk-pmic-keys.c +++ b/drivers/input/keyboard/mtk-pmic-keys.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -117,6 +118,19 @@ static const struct mtk_pmic_regs mt6358_regs = { .rst_lprst_mask = MTK_PMIC_RST_DU_MASK, }; +static const struct mtk_pmic_regs mt6359_regs = { + .keys_regs[MTK_PMIC_PWRKEY_INDEX] = + MTK_PMIC_KEYS_REGS(MT6359_TOPSTATUS, + 0x2, MT6359_PSC_TOP_INT_CON0, 0x5, + MTK_PMIC_PWRKEY_RST), + .keys_regs[MTK_PMIC_HOMEKEY_INDEX] = + MTK_PMIC_KEYS_REGS(MT6359_TOPSTATUS, + 0x8, MT6359_PSC_TOP_INT_CON0, 0xa, + MTK_PMIC_HOMEKEY_RST), + .pmic_rst_reg = MT6359_TOP_RST_MISC, + .rst_lprst_mask = MTK_PMIC_RST_DU_MASK, +}; + struct mtk_pmic_keys_info { struct mtk_pmic_keys *keys; const struct mtk_pmic_keys_regs *regs; @@ -296,6 +310,9 @@ static const struct of_device_id of_mtk_pmic_keys_match_tbl[] = { }, { .compatible = "mediatek,mt6358-keys", .data = &mt6358_regs, + }, { + .compatible = "mediatek,mt6359-keys", + .data = &mt6359_regs, }, { /* sentinel */ } From a9c95d17dc13b8a4c5faa02a6b84ba83af058206 Mon Sep 17 00:00:00 2001 From: Marge Yang Date: Wed, 23 Jul 2025 09:16:20 -0700 Subject: [PATCH 24/38] Input: synaptics-rmi4 - add support for Forcepads (F21) Forcepad devices do not have physical buttons underneath the surface and use F21 to report "clicks" based on touch pressure. Signed-off-by: Marge Yang Link: https://lore.kernel.org/r/20250716033648.1785509-1-marge.yang@tw.synaptics.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/Kconfig | 8 ++ drivers/input/rmi4/Makefile | 1 + drivers/input/rmi4/rmi_bus.c | 3 + drivers/input/rmi4/rmi_driver.h | 1 + drivers/input/rmi4/rmi_f21.c | 179 ++++++++++++++++++++++++++++++++ 5 files changed, 192 insertions(+) create mode 100644 drivers/input/rmi4/rmi_f21.c diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index c0163b983ce6..1f91d620eadb 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -82,6 +82,14 @@ config RMI4_F12 touchpads. For sensors that support relative pointing, F12 also provides mouse input. +config RMI4_F21 + bool "RMI4 Function 21 (PRESSURE)" + help + Say Y here if you want to add support for RMI4 function 21. + + Function 21 provides buttons/pressure handling for RMI4 devices. + This includes support for buttons/pressure on PressurePad. + config RMI4_F30 bool "RMI4 Function 30 (GPIO LED)" help diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile index 02f14c846861..484b97eca025 100644 --- a/drivers/input/rmi4/Makefile +++ b/drivers/input/rmi4/Makefile @@ -8,6 +8,7 @@ rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o rmi_core-$(CONFIG_RMI4_F03) += rmi_f03.o rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o +rmi_core-$(CONFIG_RMI4_F21) += rmi_f21.o rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o rmi_core-$(CONFIG_RMI4_F34) += rmi_f34.o rmi_f34v7.o rmi_core-$(CONFIG_RMI4_F3A) += rmi_f3a.o diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index 3aee04837205..47fe7a88c92b 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -360,6 +360,9 @@ static struct rmi_function_handler *fn_handlers[] = { #ifdef CONFIG_RMI4_F12 &rmi_f12_handler, #endif +#ifdef CONFIG_RMI4_F21 + &rmi_f21_handler, +#endif #ifdef CONFIG_RMI4_F30 &rmi_f30_handler, #endif diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index 3bfe9013043e..21e1c7663561 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -133,6 +133,7 @@ extern struct rmi_function_handler rmi_f01_handler; extern struct rmi_function_handler rmi_f03_handler; extern struct rmi_function_handler rmi_f11_handler; extern struct rmi_function_handler rmi_f12_handler; +extern struct rmi_function_handler rmi_f21_handler; extern struct rmi_function_handler rmi_f30_handler; extern struct rmi_function_handler rmi_f34_handler; extern struct rmi_function_handler rmi_f3a_handler; diff --git a/drivers/input/rmi4/rmi_f21.c b/drivers/input/rmi4/rmi_f21.c new file mode 100644 index 000000000000..e3a9dfc3f0ec --- /dev/null +++ b/drivers/input/rmi4/rmi_f21.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2025 Synaptics Incorporated + */ + +#include +#include +#include +#include +#include +#include +#include "rmi_driver.h" + +#define RMI_F21_SENSOR_COUNT_MASK GENMASK(3, 0) +#define RMI_F21_FINGER_COUNT_PRESENT BIT(5) +#define RMI_F21_NEW_REPORT_FORMAT BIT(6) + +#define RMI_F21_FINGER_COUNT_MASK GENMASK(3, 0) + +#define RMI_F21_MAX_SENSORS 16 +#define RMI_F21_MAX_FINGERS 16 +#define RMI_F21_DATA_REGS_MAX_SIZE (RMI_F21_MAX_SENSORS * 2 + \ + RMI_F21_MAX_FINGERS * 2 + 1) + +#define RMI_F21_FORCE_CLICK_BIT BIT(0) + +#define RMI_F21_FORCEPAD_BUTTON_COUNT 1 + +struct f21_data { + struct input_dev *input; + u16 key_code; + + unsigned int attn_data_size; + unsigned int attn_data_button_offset; + + unsigned int data_reg_size; + unsigned int data_reg_button_offset; + u8 data_regs[RMI_F21_DATA_REGS_MAX_SIZE]; +}; + +static irqreturn_t rmi_f21_attention(int irq, void *ctx) +{ + struct rmi_function *fn = ctx; + struct f21_data *f21 = dev_get_drvdata(&fn->dev); + struct rmi_driver_data *drvdata = dev_get_drvdata(&fn->rmi_dev->dev); + u8 *pdata; + int error; + bool pressed; + + if (drvdata->attn_data.data) { + if (drvdata->attn_data.size < f21->attn_data_size) { + dev_warn(&fn->dev, "f21 interrupt, but data is missing\n"); + return IRQ_HANDLED; + } + + pdata = drvdata->attn_data.data + f21->attn_data_button_offset; + + drvdata->attn_data.data += f21->attn_data_size; + drvdata->attn_data.size -= f21->attn_data_size; + } else { + error = rmi_read_block(fn->rmi_dev, fn->fd.data_base_addr, + f21->data_regs, f21->data_reg_size); + if (error) { + dev_err(&fn->dev, "failed to read f21 data registers: %d\n", + error); + return IRQ_RETVAL(error); + } + + pdata = f21->data_regs + f21->data_reg_button_offset; + } + + pressed = *pdata & RMI_F21_FORCE_CLICK_BIT; + input_report_key(f21->input, f21->key_code, pressed); + + return IRQ_HANDLED; +} + +static int rmi_f21_config(struct rmi_function *fn) +{ + struct rmi_driver *drv = fn->rmi_dev->driver; + + drv->set_irq_bits(fn->rmi_dev, fn->irq_mask); + + return 0; +} + +static int rmi_f21_initialize(struct rmi_function *fn, struct f21_data *f21) +{ + struct input_dev *input = f21->input; + + f21->key_code = BTN_LEFT; + + input->keycode = &f21->key_code; + input->keycodesize = sizeof(f21->key_code); + input->keycodemax = RMI_F21_FORCEPAD_BUTTON_COUNT; + + input_set_capability(input, EV_KEY, f21->key_code); + __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); + + return 0; +} + +static int rmi_f21_probe(struct rmi_function *fn) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev); + struct f21_data *f21; + unsigned int sensor_count; + unsigned int max_fingers; + unsigned int query15_offset; + u8 query15_data; + int error; + + if (!drv_data->input) { + dev_info(&fn->dev, "f21: no input device found, ignoring\n"); + return -ENXIO; + } + + f21 = devm_kzalloc(&fn->dev, sizeof(*f21), GFP_KERNEL); + if (!f21) + return -ENOMEM; + + f21->input = drv_data->input; + + error = rmi_f21_initialize(fn, f21); + if (error) + return error; + + dev_set_drvdata(&fn->dev, f21); + + sensor_count = fn->fd.query_base_addr & RMI_F21_SENSOR_COUNT_MASK; + if (fn->fd.query_base_addr & RMI_F21_FINGER_COUNT_PRESENT) { + query15_offset = fn->fd.query_base_addr & RMI_F21_NEW_REPORT_FORMAT ? 2 : 1; + error = rmi_read_block(fn->rmi_dev, + fn->fd.query_base_addr + query15_offset, + &query15_data, sizeof(query15_data)); + if (error) + return dev_err_probe(&fn->dev, error, + "failed to read 'query15' data"); + + max_fingers = query15_data & RMI_F21_FINGER_COUNT_MASK; + } else { + max_fingers = 5; + } + + if (fn->fd.query_base_addr & RMI_F21_NEW_REPORT_FORMAT) { + /* Each finger uses one byte, and the button state uses one byte.*/ + f21->attn_data_size = max_fingers + 1; + f21->attn_data_button_offset = f21->attn_data_size - 1; + /* + * Each sensor uses two bytes, the button state uses one byte, + * and each finger uses two bytes. + */ + f21->data_reg_size = sensor_count * 2 + 1 + max_fingers * 2; + f21->data_reg_button_offset = sensor_count * 2; + } else { + /* + * Regardless of the transport each finger uses two bytes, + * and the button state uses one byte. + */ + f21->attn_data_size = sensor_count * 2 + 1; + f21->attn_data_button_offset = sensor_count * 2; + + f21->data_reg_size = f21->attn_data_size; + f21->data_reg_button_offset = f21->attn_data_button_offset; + } + + return 0; +} + +struct rmi_function_handler rmi_f21_handler = { + .driver = { + .name = "rmi4_f21", + }, + .func = 0x21, + .probe = rmi_f21_probe, + .config = rmi_f21_config, + .attention = rmi_f21_attention, +}; From 0da895952607c03cf0518960692cd1b3439c5d25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Apitzsch?= Date: Sun, 27 Jul 2025 00:31:32 -0700 Subject: [PATCH 25/38] dt-bindings: input: syna,rmi4: Document F1A function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In some configurations the touch controller can support touch keys. Document the linux,keycodes property that enables those keys and specifies the keycodes that should be used to report the key events. Signed-off-by: André Apitzsch Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250707-rmi4_f1a-v1-1-838d83c72e7f@apitzsch.eu Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/syna,rmi4.yaml | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Documentation/devicetree/bindings/input/syna,rmi4.yaml b/Documentation/devicetree/bindings/input/syna,rmi4.yaml index b522c8d3ce0d..f369385ffaf0 100644 --- a/Documentation/devicetree/bindings/input/syna,rmi4.yaml +++ b/Documentation/devicetree/bindings/input/syna,rmi4.yaml @@ -89,6 +89,24 @@ properties: required: - reg + rmi4-f1a@1a: + type: object + additionalProperties: false + $ref: input.yaml# + description: + RMI4 Function 1A is for capacitive keys. + + properties: + reg: + maxItems: 1 + + linux,keycodes: + minItems: 1 + maxItems: 4 + + required: + - reg + patternProperties: "^rmi4-f1[12]@1[12]$": type: object @@ -201,6 +219,7 @@ allOf: examples: - | + #include #include i2c { @@ -234,6 +253,7 @@ examples: rmi4-f1a@1a { reg = <0x1a>; + linux,keycodes = ; }; }; }; From 4619b6b97553693be7faf21af0533c24240d1d4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Apitzsch?= Date: Sun, 27 Jul 2025 00:32:14 -0700 Subject: [PATCH 26/38] Input: synaptics-rmi4 - add support for F1A MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RMI4 F1A implements capacitive keys. Add support for touch keys found in some Synaptics touch controller configurations. Signed-off-by: André Apitzsch Link: https://lore.kernel.org/r/20250707-rmi4_f1a-v1-2-838d83c72e7f@apitzsch.eu Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/Kconfig | 7 ++ drivers/input/rmi4/Makefile | 1 + drivers/input/rmi4/rmi_bus.c | 3 + drivers/input/rmi4/rmi_driver.h | 1 + drivers/input/rmi4/rmi_f1a.c | 143 ++++++++++++++++++++++++++++++++ 5 files changed, 155 insertions(+) create mode 100644 drivers/input/rmi4/rmi_f1a.c diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index 1f91d620eadb..5db58fc9e11b 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -82,6 +82,13 @@ config RMI4_F12 touchpads. For sensors that support relative pointing, F12 also provides mouse input. +config RMI4_F1A + bool "RMI4 Function 1A (0D pointing)" + help + Say Y here if you want to add support for RMI4 function 1A. + + Function 1A provides capacitive keys support for RMI4 devices. + config RMI4_F21 bool "RMI4 Function 21 (PRESSURE)" help diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile index 484b97eca025..35ae29f72497 100644 --- a/drivers/input/rmi4/Makefile +++ b/drivers/input/rmi4/Makefile @@ -8,6 +8,7 @@ rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o rmi_core-$(CONFIG_RMI4_F03) += rmi_f03.o rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o +rmi_core-$(CONFIG_RMI4_F1A) += rmi_f1a.o rmi_core-$(CONFIG_RMI4_F21) += rmi_f21.o rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o rmi_core-$(CONFIG_RMI4_F34) += rmi_f34.o rmi_f34v7.o diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index 47fe7a88c92b..5f98c3bcfd46 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -360,6 +360,9 @@ static struct rmi_function_handler *fn_handlers[] = { #ifdef CONFIG_RMI4_F12 &rmi_f12_handler, #endif +#ifdef CONFIG_RMI4_F1A + &rmi_f1a_handler, +#endif #ifdef CONFIG_RMI4_F21 &rmi_f21_handler, #endif diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index 21e1c7663561..e84495caab15 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -133,6 +133,7 @@ extern struct rmi_function_handler rmi_f01_handler; extern struct rmi_function_handler rmi_f03_handler; extern struct rmi_function_handler rmi_f11_handler; extern struct rmi_function_handler rmi_f12_handler; +extern struct rmi_function_handler rmi_f1a_handler; extern struct rmi_function_handler rmi_f21_handler; extern struct rmi_function_handler rmi_f30_handler; extern struct rmi_function_handler rmi_f34_handler; diff --git a/drivers/input/rmi4/rmi_f1a.c b/drivers/input/rmi4/rmi_f1a.c new file mode 100644 index 000000000000..765e9eae4cdc --- /dev/null +++ b/drivers/input/rmi4/rmi_f1a.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2025 André Apitzsch + */ + +#include +#include +#include "rmi_driver.h" + +struct f1a_data { + struct input_dev *input; + + u32 *keymap; + unsigned int num_keys; +}; + +static int rmi_f1a_parse_device_properties(struct rmi_function *fn, struct f1a_data *f1a) +{ + static const char buttons_property[] = "linux,keycodes"; + struct device *dev = &fn->dev; + u32 *buttonmap; + int n_keys; + int error; + + if (!device_property_present(dev, buttons_property)) + return 0; + + n_keys = device_property_count_u32(dev, buttons_property); + if (n_keys <= 0) { + error = n_keys < 0 ? n_keys : -EINVAL; + dev_err(dev, "Invalid/malformed '%s' property: %d\n", + buttons_property, error); + return error; + } + + buttonmap = devm_kmalloc_array(dev, n_keys, sizeof(*buttonmap), + GFP_KERNEL); + if (!buttonmap) + return -ENOMEM; + + error = device_property_read_u32_array(dev, buttons_property, + buttonmap, n_keys); + if (error) { + dev_err(dev, "Failed to parse '%s' property: %d\n", + buttons_property, error); + return error; + } + + f1a->keymap = buttonmap; + f1a->num_keys = n_keys; + + return 0; +} + +static irqreturn_t rmi_f1a_attention(int irq, void *ctx) +{ + struct rmi_function *fn = ctx; + struct f1a_data *f1a = dev_get_drvdata(&fn->dev); + char button_bitmask; + int key; + int error; + + error = rmi_read_block(fn->rmi_dev, fn->fd.data_base_addr, + &button_bitmask, sizeof(button_bitmask)); + if (error) { + dev_err(&fn->dev, "Failed to read object data. Code: %d.\n", + error); + return IRQ_RETVAL(error); + } + + for (key = 0; key < f1a->num_keys; key++) + input_report_key(f1a->input, f1a->keymap[key], + button_bitmask & BIT(key)); + + return IRQ_HANDLED; +} + +static int rmi_f1a_config(struct rmi_function *fn) +{ + struct f1a_data *f1a = dev_get_drvdata(&fn->dev); + struct rmi_driver *drv = fn->rmi_dev->driver; + + if (f1a->num_keys) + drv->set_irq_bits(fn->rmi_dev, fn->irq_mask); + + return 0; +} + +static int rmi_f1a_initialize(struct rmi_function *fn, struct f1a_data *f1a) +{ + int error; + int i; + + error = rmi_f1a_parse_device_properties(fn, f1a); + if (error) + return error; + + for (i = 0; i < f1a->num_keys; i++) + input_set_capability(f1a->input, EV_KEY, f1a->keymap[i]); + + f1a->input->keycode = f1a->keymap; + f1a->input->keycodemax = f1a->num_keys; + f1a->input->keycodesize = sizeof(f1a->keymap[0]); + + return 0; +} + +static int rmi_f1a_probe(struct rmi_function *fn) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev); + struct f1a_data *f1a; + int error; + + if (!drv_data->input) { + dev_info(&fn->dev, "F1A: no input device found, ignoring\n"); + return -ENXIO; + } + + f1a = devm_kzalloc(&fn->dev, sizeof(*f1a), GFP_KERNEL); + if (!f1a) + return -ENOMEM; + + f1a->input = drv_data->input; + + error = rmi_f1a_initialize(fn, f1a); + if (error) + return error; + + dev_set_drvdata(&fn->dev, f1a); + + return 0; +} + +struct rmi_function_handler rmi_f1a_handler = { + .driver = { + .name = "rmi4_f1a", + }, + .func = 0x1a, + .probe = rmi_f1a_probe, + .config = rmi_f1a_config, + .attention = rmi_f1a_attention, +}; From 3b19c9ed6f01060e9b58963b581ec41b03104c55 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 27 Jul 2025 00:49:54 -0700 Subject: [PATCH 27/38] Documentation: Fix capitalization of XBox -> Xbox This also improves the phrasing of "an example" listing two examples. Signed-off-by: Vicki Pfau Link: https://lore.kernel.org/r/20250702034500.124741-1-vi@endrift.com Signed-off-by: Dmitry Torokhov --- Documentation/input/gamepad.rst | 6 +++--- Documentation/userspace-api/media/rc/rc-protos.rst | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/input/gamepad.rst b/Documentation/input/gamepad.rst index eca17a7f5258..2bba721aa20b 100644 --- a/Documentation/input/gamepad.rst +++ b/Documentation/input/gamepad.rst @@ -192,6 +192,6 @@ Gamepads report the following events: - Profile: - Some pads provide a multi-value profile selection switch. An example is the - XBox Adaptive and the XBox Elite 2 controllers. When the active profile is - switched, its newly selected value is emitted as an ABS_PROFILE event. + Some pads provide a multi-value profile selection switch. Examples include + the Xbox Adaptive and the Xbox Elite 2 controllers. When the active profile + is switched, its newly selected value is emitted as an ABS_PROFILE event. diff --git a/Documentation/userspace-api/media/rc/rc-protos.rst b/Documentation/userspace-api/media/rc/rc-protos.rst index 2a888ff5829f..ec706290c921 100644 --- a/Documentation/userspace-api/media/rc/rc-protos.rst +++ b/Documentation/userspace-api/media/rc/rc-protos.rst @@ -449,6 +449,6 @@ the 32 bits. xbox-dvd (RC_PROTO_XBOX_DVD) ---------------------------- -This protocol is used by XBox DVD Remote, which was made for the original -XBox. There is no in-kernel decoder or encoder for this protocol. The usb +This protocol is used by Xbox DVD Remote, which was made for the original +Xbox. There is no in-kernel decoder or encoder for this protocol. The usb device decodes the protocol. There is a BPF decoder available in v4l-utils. From a43a503df996739ae34f179f6b73b0ae91000c5c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 27 Jul 2025 01:13:42 -0700 Subject: [PATCH 28/38] Input: xpad - change buttons the D-Pad gets mapped as to BTN_DPAD_* Since dance pads can have both up/down or left/right pressed at the same time, by design, they are not suitable for mapping the buttons to axes. Historically, this driver mapped the D-pad to BTN_TRIGGER_HAPPY1-4 in these cases, and before that as mouse buttons. However, BTN_DPAD_* exists for this and makes far more sense than the arbitrary mapping it was before. Signed-off-by: Vicki Pfau Link: https://lore.kernel.org/r/20250702034740.124817-1-vi@endrift.com Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 7746530da030..5e84e2be34a6 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -438,8 +438,8 @@ static const signed short xpad_btn[] = { /* used when dpad is mapped to buttons */ static const signed short xpad_btn_pad[] = { - BTN_TRIGGER_HAPPY1, BTN_TRIGGER_HAPPY2, /* d-pad left, right */ - BTN_TRIGGER_HAPPY3, BTN_TRIGGER_HAPPY4, /* d-pad up, down */ + BTN_DPAD_LEFT, BTN_DPAD_RIGHT, /* d-pad left, right */ + BTN_DPAD_UP, BTN_DPAD_DOWN, /* d-pad up, down */ -1 /* terminating entry */ }; @@ -835,10 +835,10 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d /* digital pad */ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { /* dpad as buttons (left, right, up, down) */ - input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & BIT(2)); - input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & BIT(3)); - input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & BIT(0)); - input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & BIT(1)); + input_report_key(dev, BTN_DPAD_LEFT, data[2] & BIT(2)); + input_report_key(dev, BTN_DPAD_RIGHT, data[2] & BIT(3)); + input_report_key(dev, BTN_DPAD_UP, data[2] & BIT(0)); + input_report_key(dev, BTN_DPAD_DOWN, data[2] & BIT(1)); } else { input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04)); @@ -886,10 +886,10 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev, /* digital pad */ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { /* dpad as buttons (left, right, up, down) */ - input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & BIT(2)); - input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & BIT(3)); - input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & BIT(0)); - input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & BIT(1)); + input_report_key(dev, BTN_DPAD_LEFT, data[2] & BIT(2)); + input_report_key(dev, BTN_DPAD_RIGHT, data[2] & BIT(3)); + input_report_key(dev, BTN_DPAD_UP, data[2] & BIT(0)); + input_report_key(dev, BTN_DPAD_DOWN, data[2] & BIT(1)); } /* @@ -1108,10 +1108,10 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char /* digital pad */ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { /* dpad as buttons (left, right, up, down) */ - input_report_key(dev, BTN_TRIGGER_HAPPY1, data[5] & BIT(2)); - input_report_key(dev, BTN_TRIGGER_HAPPY2, data[5] & BIT(3)); - input_report_key(dev, BTN_TRIGGER_HAPPY3, data[5] & BIT(0)); - input_report_key(dev, BTN_TRIGGER_HAPPY4, data[5] & BIT(1)); + input_report_key(dev, BTN_DPAD_LEFT, data[5] & BIT(2)); + input_report_key(dev, BTN_DPAD_RIGHT, data[5] & BIT(3)); + input_report_key(dev, BTN_DPAD_UP, data[5] & BIT(0)); + input_report_key(dev, BTN_DPAD_DOWN, data[5] & BIT(1)); } else { input_report_abs(dev, ABS_HAT0X, !!(data[5] & 0x08) - !!(data[5] & 0x04)); From 97c01e65ef4c1878532be245b2899fc4363cc453 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 27 Jul 2025 01:15:17 -0700 Subject: [PATCH 29/38] Input: Add and document BTN_GRIP* Many controllers these days have started including grip buttons. As there has been no particular assigned BTN_* constants for these, they've been haphazardly assigned to BTN_TRIGGER_HAPPY*. Unfortunately, the assignment of these has varied significantly between drivers. Add and document new constants for these grip buttons. Signed-off-by: Vicki Pfau Link: https://lore.kernel.org/r/20250702040102.125432-2-vi@endrift.com Signed-off-by: Dmitry Torokhov --- Documentation/input/gamepad.rst | 13 +++++++++++++ drivers/hid/hid-debug.c | 2 ++ include/uapi/linux/input-event-codes.h | 5 +++++ 3 files changed, 20 insertions(+) diff --git a/Documentation/input/gamepad.rst b/Documentation/input/gamepad.rst index 2bba721aa20b..0c918b6f288b 100644 --- a/Documentation/input/gamepad.rst +++ b/Documentation/input/gamepad.rst @@ -190,6 +190,19 @@ Gamepads report the following events: Rumble is advertised as FF_RUMBLE. +- Grip buttons: + + Many pads include buttons on the rear, usually referred to as either grip or + rear buttons, or paddles. These are often reprogrammable by the firmware to + appear as "normal" buttons, but are sometimes exposed to software too. Some + notable examples of this are the Steam Deck, which has R4, R5, L4, and L5 on + the back; the Xbox Elite pads, which have P1-P4; and the Switch 2 Pro + Controller, which has GL and GR. + + For these controllers, BTN_GRIPR and BTN_GRIPR2 should be used for the top + and bottom (if present) right grip button(s), and BTN_GRIPL and BTN_GRIPL2 + should be used for the top and bottom (if present) left grip button(s). + - Profile: Some pads provide a multi-value profile selection switch. Examples include diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 8433306148d5..3cd9c1150cdf 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -3291,6 +3291,8 @@ static const char *keys[KEY_MAX + 1] = { [BTN_TR2] = "BtnTR2", [BTN_SELECT] = "BtnSelect", [BTN_START] = "BtnStart", [BTN_MODE] = "BtnMode", [BTN_THUMBL] = "BtnThumbL", [BTN_THUMBR] = "BtnThumbR", + [BTN_GRIPL] = "BtnGripL", [BTN_GRIPR] = "BtnGripR", + [BTN_GRIPL2] = "BtnGripL2", [BTN_GRIPR2] = "BtnGripR2", [BTN_TOOL_PEN] = "ToolPen", [BTN_TOOL_RUBBER] = "ToolRubber", [BTN_TOOL_BRUSH] = "ToolBrush", [BTN_TOOL_PENCIL] = "ToolPencil", [BTN_TOOL_AIRBRUSH] = "ToolAirbrush", [BTN_TOOL_FINGER] = "ToolFinger", diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h index 5a199f3d4a26..5426297d93fd 100644 --- a/include/uapi/linux/input-event-codes.h +++ b/include/uapi/linux/input-event-codes.h @@ -601,6 +601,11 @@ #define BTN_DPAD_LEFT 0x222 #define BTN_DPAD_RIGHT 0x223 +#define BTN_GRIPL 0x224 +#define BTN_GRIPR 0x225 +#define BTN_GRIPL2 0x226 +#define BTN_GRIPR2 0x227 + #define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */ #define KEY_ROTATE_LOCK_TOGGLE 0x231 /* Display rotation lock */ #define KEY_REFRESH_RATE_TOGGLE 0x232 /* Display refresh rate toggle */ From e7412ba919f625438c570d8b4fbf16c5f31b583d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 27 Jul 2025 01:19:10 -0700 Subject: [PATCH 30/38] Input: xpad - use new BTN_GRIP* buttons Map paddles to the newly defined BTN_GRIP* buttons. Signed-off-by: Vicki Pfau Link: https://lore.kernel.org/r/20250702040102.125432-3-vi@endrift.com Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 36 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 5e84e2be34a6..ad759a939026 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -475,8 +475,8 @@ static const signed short xpad_abs_triggers[] = { /* used when the controller has extra paddle buttons */ static const signed short xpad_btn_paddles[] = { - BTN_TRIGGER_HAPPY5, BTN_TRIGGER_HAPPY6, /* paddle upper right, lower right */ - BTN_TRIGGER_HAPPY7, BTN_TRIGGER_HAPPY8, /* paddle upper left, lower left */ + BTN_GRIPR, BTN_GRIPR2, /* paddle upper right, lower right */ + BTN_GRIPL, BTN_GRIPL2, /* paddle upper left, lower left */ -1 /* terminating entry */ }; @@ -1070,10 +1070,10 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char data[18] = 0; /* Elite Series 2 split packet paddle bits */ - input_report_key(dev, BTN_TRIGGER_HAPPY5, data[18] & BIT(0)); - input_report_key(dev, BTN_TRIGGER_HAPPY6, data[18] & BIT(1)); - input_report_key(dev, BTN_TRIGGER_HAPPY7, data[18] & BIT(2)); - input_report_key(dev, BTN_TRIGGER_HAPPY8, data[18] & BIT(3)); + input_report_key(dev, BTN_GRIPR, data[18] & BIT(0)); + input_report_key(dev, BTN_GRIPR2, data[18] & BIT(1)); + input_report_key(dev, BTN_GRIPL, data[18] & BIT(2)); + input_report_key(dev, BTN_GRIPL2, data[18] & BIT(3)); do_sync = true; } @@ -1170,10 +1170,10 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char data[32] = 0; /* OG Elite Series Controller paddle bits */ - input_report_key(dev, BTN_TRIGGER_HAPPY5, data[32] & BIT(1)); - input_report_key(dev, BTN_TRIGGER_HAPPY6, data[32] & BIT(3)); - input_report_key(dev, BTN_TRIGGER_HAPPY7, data[32] & BIT(0)); - input_report_key(dev, BTN_TRIGGER_HAPPY8, data[32] & BIT(2)); + input_report_key(dev, BTN_GRIPR, data[32] & BIT(1)); + input_report_key(dev, BTN_GRIPR2, data[32] & BIT(3)); + input_report_key(dev, BTN_GRIPL, data[32] & BIT(0)); + input_report_key(dev, BTN_GRIPL2, data[32] & BIT(2)); } else if (xpad->packet_type == PKT_XBE2_FW_OLD) { /* Mute paddles if controller has a custom mapping applied. * Checked by comparing the current mapping @@ -1183,10 +1183,10 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char data[18] = 0; /* Elite Series 2 4.x firmware paddle bits */ - input_report_key(dev, BTN_TRIGGER_HAPPY5, data[18] & BIT(0)); - input_report_key(dev, BTN_TRIGGER_HAPPY6, data[18] & BIT(1)); - input_report_key(dev, BTN_TRIGGER_HAPPY7, data[18] & BIT(2)); - input_report_key(dev, BTN_TRIGGER_HAPPY8, data[18] & BIT(3)); + input_report_key(dev, BTN_GRIPR, data[18] & BIT(0)); + input_report_key(dev, BTN_GRIPR2, data[18] & BIT(1)); + input_report_key(dev, BTN_GRIPL, data[18] & BIT(2)); + input_report_key(dev, BTN_GRIPL2, data[18] & BIT(3)); } else if (xpad->packet_type == PKT_XBE2_FW_5_EARLY) { /* Mute paddles if controller has a custom mapping applied. * Checked by comparing the current mapping @@ -1198,10 +1198,10 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char /* Elite Series 2 5.x firmware paddle bits * (before the packet was split) */ - input_report_key(dev, BTN_TRIGGER_HAPPY5, data[22] & BIT(0)); - input_report_key(dev, BTN_TRIGGER_HAPPY6, data[22] & BIT(1)); - input_report_key(dev, BTN_TRIGGER_HAPPY7, data[22] & BIT(2)); - input_report_key(dev, BTN_TRIGGER_HAPPY8, data[22] & BIT(3)); + input_report_key(dev, BTN_GRIPR, data[22] & BIT(0)); + input_report_key(dev, BTN_GRIPR2, data[22] & BIT(1)); + input_report_key(dev, BTN_GRIPL, data[22] & BIT(2)); + input_report_key(dev, BTN_GRIPL2, data[22] & BIT(3)); } } From 17eabb792740cea3f24b236e150f9fee8cd344f3 Mon Sep 17 00:00:00 2001 From: Werner Sembach Date: Tue, 22 Jul 2025 14:04:35 +0200 Subject: [PATCH 31/38] Input: atkbd - correctly map F13 - F24 Currently only F23 is correctly mapped for PS/2 keyboards. According to this table: https://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf - F24 and Zenkaku/Hankaku share the same scancode, but since in real world Zenkaku/Hankaku keys seem to just use the tilde scancode, this patch binds the scancode to F24. Note that on userspace side the KEY_ZENKAKUHANKAKU keycode is currently not bound in xkeyboard-config, so it is (mostly*) unused anyway. * Qt on Wayland and therefore KDE on Wayland can see the keypress anyway for some reason and it is actually used in a touchpad toggle shortcut, but this is currently being fixed in both KDE and xkeyboard-config to make this less weird, so it could directly be fixed to correctly handle the F24 keypress instead. - The scancodes for F13-F22 are currently unmapped so there will probably be no harm in mapping them. This would also fix the issue that some of these keys can't be mapped as the target from userspace using the `setkeycodes` command. Reviewed-by: Hans de Goede Signed-off-by: Werner Sembach Link: https://lore.kernel.org/r/20250722120438.28011-1-wse@tuxedocomputers.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/atkbd.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 3ff2fcf05ad5..6a2af2c1dfaf 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -84,12 +84,12 @@ static const unsigned short atkbd_set2_keycode[ATKBD_KEYMAP_SIZE] = { #include "hpps2atkbd.h" /* include the keyboard scancodes */ #else - 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117, - 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0, - 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183, - 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185, - 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0, - 0, 89, 40, 0, 26, 13, 0,193, 58, 54, 28, 27, 0, 43, 0, 85, + 0, 67, 65, 63, 61, 59, 60, 88,183, 68, 66, 64, 62, 15, 41,117, + 184, 56, 42, 93, 29, 16, 2, 0,185, 0, 44, 31, 30, 17, 3, 0, + 186, 46, 45, 32, 18, 5, 4, 95,187, 57, 47, 33, 20, 19, 6,183, + 188, 49, 48, 35, 34, 21, 7,184,189, 0, 50, 36, 22, 8, 9,185, + 190, 51, 37, 23, 24, 11, 10, 0,191, 52, 53, 38, 39, 25, 12, 0, + 192, 89, 40, 0, 26, 13, 0,193, 58, 54, 28, 27, 0, 43, 0,194, 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0, 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99, From 19875ccec01653a897a53db91cd4434c52ef5465 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 16 Oct 2024 06:02:40 +0200 Subject: [PATCH 32/38] dt-bindings: touchscreen: add touch-overlay property The touch-overlay encompasses a number of touch areas that define a clipped touchscreen area and/or buttons with a specific functionality. A clipped touchscreen area avoids getting events from regions that are physically hidden by overlay frames. For touchscreens with printed overlay buttons, sub-nodes with a suitable key code can be defined to report key events instead of the original touch events. Reviewed-by: Jeff LaBundy Reviewed-by: Rob Herring Signed-off-by: Javier Carrasco Link: https://lore.kernel.org/r/20241016-feature-ts_virtobj_patch-v11-1-b292a1bbb0a1@wolfvision.net Signed-off-by: Dmitry Torokhov --- .../input/touchscreen/touchscreen.yaml | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/Documentation/devicetree/bindings/input/touchscreen/touchscreen.yaml b/Documentation/devicetree/bindings/input/touchscreen/touchscreen.yaml index 431c13335c40..3e3572aa483a 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/touchscreen.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/touchscreen.yaml @@ -87,6 +87,125 @@ properties: touchscreen-y-plate-ohms: description: Resistance of the Y-plate in Ohms + touch-overlay: + description: | + List of nodes defining segments (touch areas) on the touchscreen. + + This object can be used to describe a series of segments to restrict + the region within touch events are reported or buttons with a specific + functionality. + + This is of special interest if the touchscreen is shipped with a physical + overlay on top of it with a frame that hides some part of the original + touchscreen area. Printed buttons on that overlay are also a typical + use case. + + A new touchscreen area is defined as a sub-node without a key code. If a + key code is defined in the sub-node, it will be interpreted as a button. + + The x-origin and y-origin properties of a touchscreen area define the + offset of a new origin from where the touchscreen events are referenced. + This offset is applied to the events accordingly. The x-size and y-size + properties define the size of the touchscreen effective area. + + The following example shows a new touchscreen area with the new origin + (0',0') for the touch events generated by the device. + + Touchscreen (full area) + ┌────────────────────────────────────────┐ + │ ┌───────────────────────────────┐ │ + │ │ │ │ + │ ├ y-size │ │ + │ │ │ │ + │ │ touchscreen area │ │ + │ │ (no key code) │ │ + │ │ │ │ + │ │ x-size │ │ + │ ┌└──────────────┴────────────────┘ │ + │(0',0') │ + ┌└────────────────────────────────────────┘ + (0,0) + + where (0',0') = (0+x-origin,0+y-origin) + + Sub-nodes with key codes report the touch events on their surface as key + events instead. + + The following example shows a touchscreen with a single button on it. + + Touchscreen (full area) + ┌───────────────────────────────────┐ + │ │ + │ │ + │ ┌─────────┐ │ + │ │button 0 │ │ + │ │KEY_POWER│ │ + │ └─────────┘ │ + │ │ + │ │ + ┌└───────────────────────────────────┘ + (0,0) + + Segments defining buttons and clipped toushcreen areas can be combined + as shown in the following example. + In that case only the events within the touchscreen area are reported + as touch events. Events within the button areas report their associated + key code. Any events outside the defined areas are ignored. + + Touchscreen (full area) + ┌─────────┬──────────────────────────────┐ + │ │ │ + │ │ ┌───────────────────────┐ │ + │ button 0│ │ │ │ + │KEY_POWER│ │ │ │ + │ │ │ │ │ + ├─────────┤ │ touchscreen area │ │ + │ │ │ (no key code) │ │ + │ │ │ │ │ + │ button 1│ │ │ │ + │ KEY_INFO│ ┌└───────────────────────┘ │ + │ │(0',0') │ + ┌└─────────┴──────────────────────────────┘ + (0,0) + + type: object + + patternProperties: + '^segment-': + type: object + description: + Each segment is represented as a sub-node. + properties: + x-origin: + description: horizontal origin of the node area + $ref: /schemas/types.yaml#/definitions/uint32 + + y-origin: + description: vertical origin of the node area + $ref: /schemas/types.yaml#/definitions/uint32 + + x-size: + description: horizontal resolution of the node area + $ref: /schemas/types.yaml#/definitions/uint32 + + y-size: + description: vertical resolution of the node area + $ref: /schemas/types.yaml#/definitions/uint32 + + label: + description: descriptive name of the segment + $ref: /schemas/types.yaml#/definitions/string + + linux,code: true + + required: + - x-origin + - y-origin + - x-size + - y-size + + unevaluatedProperties: false + dependencies: touchscreen-size-x: [ touchscreen-size-y ] touchscreen-size-y: [ touchscreen-size-x ] From ea4d331050b4cd43e6a900937db88b01ef75e1f2 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 16 Oct 2024 06:02:41 +0200 Subject: [PATCH 33/38] Input: touch-overlay - add touchscreen overlay handling Some touch devices provide mechanical overlays with different objects like buttons or clipped touchscreen surfaces. In order to support these objects, add a series of helper functions to the input subsystem to transform them into overlay objects via device tree nodes. These overlay objects consume the raw touch events and report the expected input events depending on the object properties. Note that the current implementation allows for multiple definitions of touchscreen areas (regions that report touch events), but only the first one will be used for the touchscreen device that the consumers typically provide. Should the need for multiple touchscreen areas arise, additional touchscreen devices would be required at the consumer side. There is no limitation in the number of touch areas defined as buttons. Reviewed-by: Jeff LaBundy Signed-off-by: Javier Carrasco Link: https://lore.kernel.org/r/20241016-feature-ts_virtobj_patch-v11-2-b292a1bbb0a1@wolfvision.net Signed-off-by: Dmitry Torokhov --- MAINTAINERS | 7 + drivers/input/Makefile | 2 +- drivers/input/touch-overlay.c | 277 ++++++++++++++++++++++++++++ include/linux/input/touch-overlay.h | 25 +++ 4 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 drivers/input/touch-overlay.c create mode 100644 include/linux/input/touch-overlay.h diff --git a/MAINTAINERS b/MAINTAINERS index adcd58147f97..3466d0d59c5f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24609,6 +24609,13 @@ L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/toshiba-wmi.c +TOUCH OVERLAY +M: Javier Carrasco +L: linux-input@vger.kernel.org +S: Maintained +F: drivers/input/touch-overlay.c +F: include/linux/input/touch-overlay.h + TPM DEVICE DRIVER M: Peter Huewe M: Jarkko Sakkinen diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 930b64d2115e..2cd6e1c9a778 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_INPUT) += input-core.o input-core-y := input.o input-compat.o input-mt.o input-poller.o ff-core.o -input-core-y += touchscreen.o +input-core-y += touchscreen.o touch-overlay.o obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o diff --git a/drivers/input/touch-overlay.c b/drivers/input/touch-overlay.c new file mode 100644 index 000000000000..8806373f7a4a --- /dev/null +++ b/drivers/input/touch-overlay.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Helper functions for overlay objects on touchscreens + * + * Copyright (c) 2023 Javier Carrasco + */ + +#include +#include +#include +#include +#include +#include + +struct touch_overlay_segment { + struct list_head list; + u32 x_origin; + u32 y_origin; + u32 x_size; + u32 y_size; + u32 key; + bool pressed; + int slot; +}; + +static int touch_overlay_get_segment(struct fwnode_handle *segment_node, + struct touch_overlay_segment *segment, + struct input_dev *input) +{ + int error; + + error = fwnode_property_read_u32(segment_node, "x-origin", + &segment->x_origin); + if (error) + return error; + + error = fwnode_property_read_u32(segment_node, "y-origin", + &segment->y_origin); + if (error) + return error; + + error = fwnode_property_read_u32(segment_node, "x-size", + &segment->x_size); + if (error) + return error; + + error = fwnode_property_read_u32(segment_node, "y-size", + &segment->y_size); + if (error) + return error; + + error = fwnode_property_read_u32(segment_node, "linux,code", + &segment->key); + if (!error) + input_set_capability(input, EV_KEY, segment->key); + else if (error != -EINVAL) + return error; + + return 0; +} + +/** + * touch_overlay_map - map overlay objects from the device tree and set + * key capabilities if buttons are defined. + * @list: pointer to the list that will hold the segments + * @input: pointer to the already allocated input_dev + * + * Returns 0 on success and error number otherwise. + * + * If buttons are defined, key capabilities are set accordingly. + */ +int touch_overlay_map(struct list_head *list, struct input_dev *input) +{ + struct fwnode_handle *fw_segment; + struct device *dev = input->dev.parent; + struct touch_overlay_segment *segment; + int error; + + struct fwnode_handle *overlay __free(fwnode_handle) = + device_get_named_child_node(dev, "touch-overlay"); + if (!overlay) + return 0; + + fwnode_for_each_available_child_node(overlay, fw_segment) { + segment = devm_kzalloc(dev, sizeof(*segment), GFP_KERNEL); + if (!segment) { + fwnode_handle_put(fw_segment); + return -ENOMEM; + } + error = touch_overlay_get_segment(fw_segment, segment, input); + if (error) { + fwnode_handle_put(fw_segment); + return error; + } + list_add_tail(&segment->list, list); + } + + return 0; +} +EXPORT_SYMBOL(touch_overlay_map); + +/** + * touch_overlay_get_touchscreen_abs - get abs size from the touchscreen area. + * @list: pointer to the list that holds the segments + * @x: horizontal abs + * @y: vertical abs + */ +void touch_overlay_get_touchscreen_abs(struct list_head *list, u16 *x, u16 *y) +{ + struct touch_overlay_segment *segment; + struct list_head *ptr; + + list_for_each(ptr, list) { + segment = list_entry(ptr, struct touch_overlay_segment, list); + if (!segment->key) { + *x = segment->x_size - 1; + *y = segment->y_size - 1; + break; + } + } +} +EXPORT_SYMBOL(touch_overlay_get_touchscreen_abs); + +static bool touch_overlay_segment_event(struct touch_overlay_segment *seg, + struct input_mt_pos *pos) +{ + if (pos->x >= seg->x_origin && pos->x < (seg->x_origin + seg->x_size) && + pos->y >= seg->y_origin && pos->y < (seg->y_origin + seg->y_size)) + return true; + + return false; +} + +/** + * touch_overlay_mapped_touchscreen - check if a touchscreen area is mapped + * @list: pointer to the list that holds the segments + * + * Returns true if a touchscreen area is mapped or false otherwise. + */ +bool touch_overlay_mapped_touchscreen(struct list_head *list) +{ + struct touch_overlay_segment *segment; + struct list_head *ptr; + + list_for_each(ptr, list) { + segment = list_entry(ptr, struct touch_overlay_segment, list); + if (!segment->key) + return true; + } + + return false; +} +EXPORT_SYMBOL(touch_overlay_mapped_touchscreen); + +static bool touch_overlay_event_on_ts(struct list_head *list, + struct input_mt_pos *pos) +{ + struct touch_overlay_segment *segment; + struct list_head *ptr; + + list_for_each(ptr, list) { + segment = list_entry(ptr, struct touch_overlay_segment, list); + if (segment->key) + continue; + + if (touch_overlay_segment_event(segment, pos)) { + pos->x -= segment->x_origin; + pos->y -= segment->y_origin; + return true; + } + /* ignore touch events outside the defined area */ + return false; + } + + return true; +} + +static bool touch_overlay_button_event(struct input_dev *input, + struct touch_overlay_segment *segment, + struct input_mt_pos *pos, int slot) +{ + struct input_mt *mt = input->mt; + struct input_mt_slot *s = &mt->slots[slot]; + bool button_contact = touch_overlay_segment_event(segment, pos); + + if (segment->slot == slot && segment->pressed) { + /* sliding out of the button releases it */ + if (!button_contact) { + input_report_key(input, segment->key, false); + segment->pressed = false; + /* keep available for a possible touch event */ + return false; + } + /* ignore sliding on the button while pressed */ + s->frame = mt->frame; + return true; + } else if (button_contact) { + input_report_key(input, segment->key, true); + s->frame = mt->frame; + segment->slot = slot; + segment->pressed = true; + return true; + } + + return false; +} + +/** + * touch_overlay_sync_frame - update the status of the segments and report + * buttons whose tracked slot is unused. + * @list: pointer to the list that holds the segments + * @input: pointer to the input device associated to the contact + */ +void touch_overlay_sync_frame(struct list_head *list, struct input_dev *input) +{ + struct touch_overlay_segment *segment; + struct input_mt *mt = input->mt; + struct input_mt_slot *s; + struct list_head *ptr; + + list_for_each(ptr, list) { + segment = list_entry(ptr, struct touch_overlay_segment, list); + if (!segment->key) + continue; + + s = &mt->slots[segment->slot]; + if (!input_mt_is_used(mt, s) && segment->pressed) { + input_report_key(input, segment->key, false); + segment->pressed = false; + } + } +} +EXPORT_SYMBOL(touch_overlay_sync_frame); + +/** + * touch_overlay_process_contact - process contacts according to the overlay + * mapping. This function acts as a filter to release the calling driver + * from the contacts that are either related to overlay buttons or out of the + * overlay touchscreen area, if defined. + * @list: pointer to the list that holds the segments + * @input: pointer to the input device associated to the contact + * @pos: pointer to the contact position + * @slot: slot associated to the contact (0 if multitouch is not supported) + * + * Returns true if the contact was processed (reported for valid key events + * and dropped for contacts outside the overlay touchscreen area) or false + * if the contact must be processed by the caller. In that case this function + * shifts the (x,y) coordinates to the overlay touchscreen axis if required. + */ +bool touch_overlay_process_contact(struct list_head *list, + struct input_dev *input, + struct input_mt_pos *pos, int slot) +{ + struct touch_overlay_segment *segment; + struct list_head *ptr; + + /* + * buttons must be prioritized over overlay touchscreens to account for + * overlappings e.g. a button inside the touchscreen area. + */ + list_for_each(ptr, list) { + segment = list_entry(ptr, struct touch_overlay_segment, list); + if (segment->key && + touch_overlay_button_event(input, segment, pos, slot)) + return true; + } + + /* + * valid contacts on the overlay touchscreen are left for the client + * to be processed/reported according to its (possibly) unique features. + */ + return !touch_overlay_event_on_ts(list, pos); +} +EXPORT_SYMBOL(touch_overlay_process_contact); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Helper functions for overlay objects on touch devices"); diff --git a/include/linux/input/touch-overlay.h b/include/linux/input/touch-overlay.h new file mode 100644 index 000000000000..0253e554d3cd --- /dev/null +++ b/include/linux/input/touch-overlay.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2023 Javier Carrasco + */ + +#ifndef _TOUCH_OVERLAY +#define _TOUCH_OVERLAY + +#include + +struct input_dev; + +int touch_overlay_map(struct list_head *list, struct input_dev *input); + +void touch_overlay_get_touchscreen_abs(struct list_head *list, u16 *x, u16 *y); + +bool touch_overlay_mapped_touchscreen(struct list_head *list); + +bool touch_overlay_process_contact(struct list_head *list, + struct input_dev *input, + struct input_mt_pos *pos, int slot); + +void touch_overlay_sync_frame(struct list_head *list, struct input_dev *input); + +#endif From 88fb51ea6a38189bed42d302183fbbf6bb03accf Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 16 Oct 2024 06:02:42 +0200 Subject: [PATCH 34/38] dt-bindings: input: touchscreen: st1232: add touch-overlay example The touch-overlay feature adds support for segments (touch areas) on the touchscreen surface that represent overlays with clipped touchscreen areas and printed buttons. Add nodes for a clipped touchscreen and overlay buttons to the existing example. Reviewed-by: Rob Herring Signed-off-by: Javier Carrasco Link: https://lore.kernel.org/r/20241016-feature-ts_virtobj_patch-v11-3-b292a1bbb0a1@wolfvision.net Signed-off-by: Dmitry Torokhov --- .../input/touchscreen/sitronix,st1232.yaml | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Documentation/devicetree/bindings/input/touchscreen/sitronix,st1232.yaml b/Documentation/devicetree/bindings/input/touchscreen/sitronix,st1232.yaml index 1d8ca19fd37a..e7ee7a0d74c4 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/sitronix,st1232.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/sitronix,st1232.yaml @@ -37,6 +37,7 @@ unevaluatedProperties: false examples: - | + #include i2c { #address-cells = <1>; #size-cells = <0>; @@ -46,5 +47,33 @@ examples: reg = <0x55>; interrupts = <2 0>; gpios = <&gpio1 166 0>; + + touch-overlay { + segment-0 { + label = "Touchscreen"; + x-origin = <0>; + x-size = <240>; + y-origin = <40>; + y-size = <280>; + }; + + segment-1a { + label = "Camera light"; + linux,code = ; + x-origin = <40>; + x-size = <40>; + y-origin = <0>; + y-size = <40>; + }; + + segment-2a { + label = "Power"; + linux,code = ; + x-origin = <160>; + x-size = <40>; + y-origin = <0>; + y-size = <40>; + }; + }; }; }; From 1c44b818b81bf6a111a702536a560f5bc830c6d5 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 16 Oct 2024 06:02:43 +0200 Subject: [PATCH 35/38] Input: st1232 - add touch-overlay handling Use touch-overlay to support overlay objects such as buttons and a resized frame defined in the device tree. A key event will be generated if the coordinates of a touch event are within the area defined by the button properties. Reviewed-by: Jeff LaBundy Signed-off-by: Javier Carrasco Link: https://lore.kernel.org/r/20241016-feature-ts_virtobj_patch-v11-4-b292a1bbb0a1@wolfvision.net Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/st1232.c | 35 +++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 6475084aee1b..9b3901eec0a5 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -22,6 +22,7 @@ #include #include #include +#include #define ST1232_TS_NAME "st1232-ts" #define ST1633_TS_NAME "st1633-ts" @@ -57,6 +58,7 @@ struct st1232_ts_data { struct dev_pm_qos_request low_latency_req; struct gpio_desc *reset_gpio; const struct st_chip_info *chip_info; + struct list_head touch_overlay_list; int read_buf_len; u8 *read_buf; }; @@ -156,6 +158,10 @@ static int st1232_ts_parse_and_report(struct st1232_ts_data *ts) input_mt_assign_slots(input, slots, pos, n_contacts, 0); for (i = 0; i < n_contacts; i++) { + if (touch_overlay_process_contact(&ts->touch_overlay_list, + input, &pos[i], slots[i])) + continue; + input_mt_slot(input, slots[i]); input_mt_report_slot_state(input, MT_TOOL_FINGER, true); input_report_abs(input, ABS_MT_POSITION_X, pos[i].x); @@ -164,6 +170,7 @@ static int st1232_ts_parse_and_report(struct st1232_ts_data *ts) input_report_abs(input, ABS_MT_TOUCH_MAJOR, z[i]); } + touch_overlay_sync_frame(&ts->touch_overlay_list, input); input_mt_sync_frame(input); input_sync(input); @@ -292,18 +299,30 @@ static int st1232_ts_probe(struct i2c_client *client) if (error) return error; - /* Read resolution from the chip */ - error = st1232_ts_read_resolution(ts, &max_x, &max_y); - if (error) { - dev_err(&client->dev, - "Failed to read resolution: %d\n", error); - return error; - } - if (ts->chip_info->have_z) input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, ts->chip_info->max_area, 0, 0); + /* map overlay objects if defined in the device tree */ + INIT_LIST_HEAD(&ts->touch_overlay_list); + error = touch_overlay_map(&ts->touch_overlay_list, input_dev); + if (error) + return error; + + if (touch_overlay_mapped_touchscreen(&ts->touch_overlay_list)) { + /* Read resolution from the overlay touchscreen if defined */ + touch_overlay_get_touchscreen_abs(&ts->touch_overlay_list, + &max_x, &max_y); + } else { + /* Read resolution from the chip */ + error = st1232_ts_read_resolution(ts, &max_x, &max_y); + if (error) { + dev_err(&client->dev, + "Failed to read resolution: %d\n", error); + return error; + } + } + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, max_x, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_Y, From 61c6fef7c4b06b4bdbf3142f4e5cace70597e0de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 30 Jun 2025 09:27:26 -0700 Subject: [PATCH 36/38] Input: max77693 - convert to atomic pwm operation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver called pwm_config() and pwm_enable() separately. Today both are wrappers for pwm_apply_might_sleep() and it's more effective to call this function directly and only once. Also don't configure the duty_cycle and period if the next operation is to disable the PWM so configure the PWM in max77693_haptic_enable(). With the direct use of pwm_apply_might_sleep() the need to call pwm_apply_args() in .probe() is now gone, too, so drop this one. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250630103851.2069952-2-u.kleine-koenig@baylibre.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/max77693-haptic.c | 41 ++++++++++++---------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/drivers/input/misc/max77693-haptic.c b/drivers/input/misc/max77693-haptic.c index 1dfd7b95a4ce..5d45680d74a4 100644 --- a/drivers/input/misc/max77693-haptic.c +++ b/drivers/input/misc/max77693-haptic.c @@ -68,15 +68,16 @@ struct max77693_haptic { static int max77693_haptic_set_duty_cycle(struct max77693_haptic *haptic) { - struct pwm_args pargs; - int delta; + struct pwm_state state; int error; - pwm_get_args(haptic->pwm_dev, &pargs); - delta = (pargs.period + haptic->pwm_duty) / 2; - error = pwm_config(haptic->pwm_dev, delta, pargs.period); + pwm_init_state(haptic->pwm_dev, &state); + state.duty_cycle = (state.period + haptic->pwm_duty) / 2; + + error = pwm_apply_might_sleep(haptic->pwm_dev, &state); if (error) { - dev_err(haptic->dev, "failed to configure pwm: %d\n", error); + dev_err(haptic->dev, + "failed to set pwm duty cycle: %d\n", error); return error; } @@ -166,12 +167,17 @@ static int max77693_haptic_lowsys(struct max77693_haptic *haptic, bool enable) static void max77693_haptic_enable(struct max77693_haptic *haptic) { + struct pwm_state state; int error; if (haptic->enabled) return; - error = pwm_enable(haptic->pwm_dev); + pwm_init_state(haptic->pwm_dev, &state); + state.duty_cycle = (state.period + haptic->pwm_duty) / 2; + state.enabled = true; + + error = pwm_apply_might_sleep(haptic->pwm_dev, &state); if (error) { dev_err(haptic->dev, "failed to enable haptic pwm device: %d\n", error); @@ -224,18 +230,13 @@ static void max77693_haptic_play_work(struct work_struct *work) { struct max77693_haptic *haptic = container_of(work, struct max77693_haptic, work); - int error; - error = max77693_haptic_set_duty_cycle(haptic); - if (error) { - dev_err(haptic->dev, "failed to set duty cycle: %d\n", error); - return; - } - - if (haptic->magnitude) - max77693_haptic_enable(haptic); - else + if (!haptic->magnitude) max77693_haptic_disable(haptic); + else if (haptic->enabled) + max77693_haptic_set_duty_cycle(haptic); + else + max77693_haptic_enable(haptic); } static int max77693_haptic_play_effect(struct input_dev *dev, void *data, @@ -340,12 +341,6 @@ static int max77693_haptic_probe(struct platform_device *pdev) return PTR_ERR(haptic->pwm_dev); } - /* - * FIXME: pwm_apply_args() should be removed when switching to the - * atomic PWM API. - */ - pwm_apply_args(haptic->pwm_dev); - haptic->motor_reg = devm_regulator_get(&pdev->dev, "haptic"); if (IS_ERR(haptic->motor_reg)) { dev_err(&pdev->dev, "failed to get regulator\n"); From 89c52146392948f4cdda3853da9d82ec6d1dd1f4 Mon Sep 17 00:00:00 2001 From: Marcos Alano Date: Tue, 5 Aug 2025 13:44:29 -0700 Subject: [PATCH 37/38] Input: add keycode for performance mode key Alienware calls this key "Performance Boost". Dell calls it "G-Mode". The goal is to have a specific keycode to detect when this key is pressed, so userspace can act upon it and do what have to do, usually starting the power profile for performance. Signed-off-by: Marcos Alano Link: https://lore.kernel.org/r/20250509193708.2190586-1-marcoshalano@gmail.com Signed-off-by: Dmitry Torokhov --- include/uapi/linux/input-event-codes.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h index 08cb157ab593..ca5851e97fac 100644 --- a/include/uapi/linux/input-event-codes.h +++ b/include/uapi/linux/input-event-codes.h @@ -770,6 +770,9 @@ #define KEY_KBD_LCD_MENU4 0x2bb #define KEY_KBD_LCD_MENU5 0x2bc +/* Performance Boost key (Alienware)/G-Mode key (Dell) */ +#define KEY_PERFORMANCE 0x2bd + #define BTN_TRIGGER_HAPPY 0x2c0 #define BTN_TRIGGER_HAPPY1 0x2c0 #define BTN_TRIGGER_HAPPY2 0x2c1 From 4f67c41894674d351a4b4e7dd3471380b71b5bb3 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 6 Aug 2025 09:53:32 -0700 Subject: [PATCH 38/38] HID: hid-steam: Use new BTN_GRIP* buttons Make use of the newly defined BTN_GRIP* codes instead of using BTN_TRIGGER_HAPPY* and other less suited button codes. Signed-off-by: Vicki Pfau Acked-by: Jiri Kosina Link: https://lore.kernel.org/r/20250717000143.1902875-4-vi@endrift.com Signed-off-by: Dmitry Torokhov --- drivers/hid/hid-steam.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c index 949d307c66a8..197126d6e081 100644 --- a/drivers/hid/hid-steam.c +++ b/drivers/hid/hid-steam.c @@ -755,15 +755,12 @@ static int steam_input_register(struct steam_device *steam) input_set_capability(input, EV_KEY, BTN_THUMBL); input_set_capability(input, EV_KEY, BTN_THUMB); input_set_capability(input, EV_KEY, BTN_THUMB2); + input_set_capability(input, EV_KEY, BTN_GRIPL); + input_set_capability(input, EV_KEY, BTN_GRIPR); if (steam->quirks & STEAM_QUIRK_DECK) { input_set_capability(input, EV_KEY, BTN_BASE); - input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY1); - input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY2); - input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY3); - input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY4); - } else { - input_set_capability(input, EV_KEY, BTN_GEAR_DOWN); - input_set_capability(input, EV_KEY, BTN_GEAR_UP); + input_set_capability(input, EV_KEY, BTN_GRIPL2); + input_set_capability(input, EV_KEY, BTN_GRIPR2); } input_set_abs_params(input, ABS_X, -32767, 32767, 0, 0); @@ -1419,8 +1416,8 @@ static inline s16 steam_le16(u8 *data) * 9.4 | BTN_SELECT | menu left * 9.5 | BTN_MODE | steam logo * 9.6 | BTN_START | menu right - * 9.7 | BTN_GEAR_DOWN | left back lever - * 10.0 | BTN_GEAR_UP | right back lever + * 9.7 | BTN_GRIPL | left back lever + * 10.0 | BTN_GRIPR | right back lever * 10.1 | -- | left-pad clicked * 10.2 | BTN_THUMBR | right-pad clicked * 10.3 | BTN_THUMB | left-pad touched (but see explanation below) @@ -1485,8 +1482,8 @@ static void steam_do_input_event(struct steam_device *steam, input_event(input, EV_KEY, BTN_SELECT, !!(b9 & BIT(4))); input_event(input, EV_KEY, BTN_MODE, !!(b9 & BIT(5))); input_event(input, EV_KEY, BTN_START, !!(b9 & BIT(6))); - input_event(input, EV_KEY, BTN_GEAR_DOWN, !!(b9 & BIT(7))); - input_event(input, EV_KEY, BTN_GEAR_UP, !!(b10 & BIT(0))); + input_event(input, EV_KEY, BTN_GRIPL, !!(b9 & BIT(7))); + input_event(input, EV_KEY, BTN_GRIPR, !!(b10 & BIT(0))); input_event(input, EV_KEY, BTN_THUMBR, !!(b10 & BIT(2))); input_event(input, EV_KEY, BTN_THUMBL, !!(b10 & BIT(6))); input_event(input, EV_KEY, BTN_THUMB, lpad_touched || lpad_and_joy); @@ -1547,8 +1544,8 @@ static void steam_do_input_event(struct steam_device *steam, * 9.4 | BTN_SELECT | menu left * 9.5 | BTN_MODE | steam logo * 9.6 | BTN_START | menu right - * 9.7 | BTN_TRIGGER_HAPPY3 | left bottom grip button - * 10.0 | BTN_TRIGGER_HAPPY4 | right bottom grip button + * 9.7 | BTN_GRIPL2 | left bottom grip button + * 10.0 | BTN_GRIPR2 | right bottom grip button * 10.1 | BTN_THUMB | left pad pressed * 10.2 | BTN_THUMB2 | right pad pressed * 10.3 | -- | left pad touched @@ -1573,8 +1570,8 @@ static void steam_do_input_event(struct steam_device *steam, * 12.6 | -- | unknown * 12.7 | -- | unknown * 13.0 | -- | unknown - * 13.1 | BTN_TRIGGER_HAPPY1 | left top grip button - * 13.2 | BTN_TRIGGER_HAPPY2 | right top grip button + * 13.1 | BTN_GRIPL | left top grip button + * 13.2 | BTN_GRIPR | right top grip button * 13.3 | -- | unknown * 13.4 | -- | unknown * 13.5 | -- | unknown @@ -1659,8 +1656,8 @@ static void steam_do_deck_input_event(struct steam_device *steam, input_event(input, EV_KEY, BTN_SELECT, !!(b9 & BIT(4))); input_event(input, EV_KEY, BTN_MODE, !!(b9 & BIT(5))); input_event(input, EV_KEY, BTN_START, !!(b9 & BIT(6))); - input_event(input, EV_KEY, BTN_TRIGGER_HAPPY3, !!(b9 & BIT(7))); - input_event(input, EV_KEY, BTN_TRIGGER_HAPPY4, !!(b10 & BIT(0))); + input_event(input, EV_KEY, BTN_GRIPL2, !!(b9 & BIT(7))); + input_event(input, EV_KEY, BTN_GRIPR2, !!(b10 & BIT(0))); input_event(input, EV_KEY, BTN_THUMBL, !!(b10 & BIT(6))); input_event(input, EV_KEY, BTN_THUMBR, !!(b11 & BIT(2))); input_event(input, EV_KEY, BTN_DPAD_UP, !!(b9 & BIT(0))); @@ -1669,8 +1666,8 @@ static void steam_do_deck_input_event(struct steam_device *steam, input_event(input, EV_KEY, BTN_DPAD_DOWN, !!(b9 & BIT(3))); input_event(input, EV_KEY, BTN_THUMB, !!(b10 & BIT(1))); input_event(input, EV_KEY, BTN_THUMB2, !!(b10 & BIT(2))); - input_event(input, EV_KEY, BTN_TRIGGER_HAPPY1, !!(b13 & BIT(1))); - input_event(input, EV_KEY, BTN_TRIGGER_HAPPY2, !!(b13 & BIT(2))); + input_event(input, EV_KEY, BTN_GRIPL, !!(b13 & BIT(1))); + input_event(input, EV_KEY, BTN_GRIPR, !!(b13 & BIT(2))); input_event(input, EV_KEY, BTN_BASE, !!(b14 & BIT(2))); input_sync(input);