]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
Input: synaptics-rmi4 - add support for Forcepads (F21)
authorMarge Yang <Marge.Yang@tw.synaptics.com>
Wed, 23 Jul 2025 16:16:20 +0000 (09:16 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Sun, 27 Jul 2025 08:24:21 +0000 (01:24 -0700)
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 <Marge.Yang@tw.synaptics.com>
Link: https://lore.kernel.org/r/20250716033648.1785509-1-marge.yang@tw.synaptics.com
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
drivers/input/rmi4/Kconfig
drivers/input/rmi4/Makefile
drivers/input/rmi4/rmi_bus.c
drivers/input/rmi4/rmi_driver.h
drivers/input/rmi4/rmi_f21.c [new file with mode: 0644]

index c0163b983ce698ff241eeb6303d0e29989e0fcb8..1f91d620eadb28ac38accd6bc9697ef574037acb 100644 (file)
@@ -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
index 02f14c84686189b45d6adbbc06e266f64efaa036..484b97eca025014d81bdf73f5b9b581ae401211d 100644 (file)
@@ -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
index 3aee0483720533050c60014d843418740cf04a2d..47fe7a88c92ba631f5acf8d72404e78daec31345 100644 (file)
@@ -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
index 3bfe9013043ef3dff46249095a5b3116c8f7d9a6..21e1c766356119ffbbb3a74a7093e3b41af044d5 100644 (file)
@@ -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 (file)
index 0000000..e3a9dfc
--- /dev/null
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012-2025 Synaptics Incorporated
+ */
+
+#include <linux/bits.h>
+#include <linux/dev_printk.h>
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#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,
+};