]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
HID: multitouch: add haptic multitouch support
authorAngela Czubak <aczubak@google.com>
Mon, 18 Aug 2025 23:08:52 +0000 (23:08 +0000)
committerBenjamin Tissoires <bentiss@kernel.org>
Mon, 15 Sep 2025 12:32:55 +0000 (14:32 +0200)
If CONFIG_HID_HAPTIC is enabled, and the device is recognized to have
simple haptic capabilities, try initializing the haptic device, check
input frames for pressure and handle it using hid_haptic_* API.

Signed-off-by: Angela Czubak <aczubak@google.com>
Co-developed-by: Jonathan Denose <jdenose@google.com>
Signed-off-by: Jonathan Denose <jdenose@google.com>
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
drivers/hid/hid-haptic.h
drivers/hid/hid-multitouch.c

index abdd7d710c0b832ad0be8fe63ebfa7692c8ea5ca..c6539ac04c1dbd36fb25eed9370e6fe747a28f10 100644 (file)
@@ -103,19 +103,25 @@ int hid_haptic_input_configured(struct hid_device *hdev,
 {
        return 0;
 }
+static inline
+void hid_haptic_reset(struct hid_device *hdev, struct hid_haptic_device *haptic)
+{}
+static inline
 int hid_haptic_init(struct hid_device *hdev, struct hid_haptic_device **haptic_ptr)
 {
        return 0;
 }
 static inline
-void hid_haptic_handle_press_release(struct hid_haptic_device *haptic)
-{}
+void hid_haptic_handle_press_release(struct hid_haptic_device *haptic) {}
 static inline
-void hid_haptic_pressure_reset(struct hid_haptic_device *haptic)
-{}
+bool hid_haptic_handle_input(struct hid_haptic_device *haptic)
+{
+       return false;
+}
+static inline
+void hid_haptic_pressure_reset(struct hid_haptic_device *haptic) {}
 static inline
 void hid_haptic_pressure_increase(struct hid_haptic_device *haptic,
                                  __s32 pressure)
 {}
 #endif
-
index 22c6314a88436b9fcf3c1058478447e8c5d086fb..2879e65cf303b1456311ac06115adda5a78a2600 100644 (file)
@@ -49,6 +49,8 @@ MODULE_LICENSE("GPL");
 
 #include "hid-ids.h"
 
+#include "hid-haptic.h"
+
 /* quirks to control the device */
 #define MT_QUIRK_NOT_SEEN_MEANS_UP     BIT(0)
 #define MT_QUIRK_SLOT_IS_CONTACTID     BIT(1)
@@ -168,11 +170,13 @@ struct mt_report_data {
 struct mt_device {
        struct mt_class mtclass;        /* our mt device class */
        struct timer_list release_timer;        /* to release sticky fingers */
+       struct hid_haptic_device *haptic;       /* haptic related configuration */
        struct hid_device *hdev;        /* hid_device we're attached to */
        unsigned long mt_io_flags;      /* mt flags (MT_IO_FLAGS_*) */
        __u8 inputmode_value;   /* InputMode HID feature value */
        __u8 maxcontacts;
        bool is_buttonpad;      /* is this device a button pad? */
+       bool is_haptic_touchpad;        /* is this device a haptic touchpad? */
        bool serial_maybe;      /* need to check for serial protocol */
 
        struct list_head applications;
@@ -533,6 +537,8 @@ static void mt_feature_mapping(struct hid_device *hdev,
                        mt_get_feature(hdev, field->report);
                break;
        }
+
+       hid_haptic_feature_mapping(hdev, td->haptic, field, usage);
 }
 
 static void set_abs(struct input_dev *input, unsigned int code,
@@ -888,6 +894,9 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                case HID_DG_TIPPRESSURE:
                        set_abs(hi->input, ABS_MT_PRESSURE, field,
                                cls->sn_pressure);
+                       td->is_haptic_touchpad =
+                               hid_haptic_check_pressure_unit(td->haptic,
+                                                              hi, field);
                        MT_STORE_FIELD(p);
                        return 1;
                case HID_DG_SCANTIME:
@@ -1008,6 +1017,8 @@ static void mt_sync_frame(struct mt_device *td, struct mt_application *app,
 
        app->num_received = 0;
        app->left_button_state = 0;
+       if (td->is_haptic_touchpad)
+               hid_haptic_pressure_reset(td->haptic);
 
        if (test_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags))
                set_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags);
@@ -1165,6 +1176,9 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input,
                        minor = minor >> 1;
                }
 
+               if (td->is_haptic_touchpad)
+                       hid_haptic_pressure_increase(td->haptic, *slot->p);
+
                x = hdev->quirks & HID_QUIRK_X_INVERT ?
                        input_abs_get_max(input, ABS_MT_POSITION_X) - *slot->x :
                        *slot->x;
@@ -1366,6 +1380,9 @@ static int mt_touch_input_configured(struct hid_device *hdev,
        if (cls->is_indirect)
                app->mt_flags |= INPUT_MT_POINTER;
 
+       if (td->is_haptic_touchpad)
+               app->mt_flags |= INPUT_MT_TOTAL_FORCE;
+
        if (app->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP)
                app->mt_flags |= INPUT_MT_DROP_UNUSED;
 
@@ -1401,6 +1418,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
        struct mt_device *td = hid_get_drvdata(hdev);
        struct mt_application *application;
        struct mt_report_data *rdata;
+       int ret;
 
        rdata = mt_find_report_data(td, field->report);
        if (!rdata) {
@@ -1463,6 +1481,11 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
        if (field->physical == HID_DG_STYLUS)
                hi->application = HID_DG_STYLUS;
 
+       ret = hid_haptic_input_mapping(hdev, td->haptic, hi, field, usage, bit,
+                                      max);
+       if (ret != 0)
+               return ret;
+
        /* let hid-core decide for the others */
        return 0;
 }
@@ -1685,6 +1708,14 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
        struct hid_report *report;
        int ret;
 
+       if (td->is_haptic_touchpad && (td->mtclass.name == MT_CLS_WIN_8 ||
+           td->mtclass.name == MT_CLS_WIN_8_FORCE_MULTI_INPUT)) {
+               if (hid_haptic_input_configured(hdev, td->haptic, hi) == 0)
+                       td->is_haptic_touchpad = false;
+       } else {
+               td->is_haptic_touchpad = false;
+       }
+
        list_for_each_entry(report, &hi->reports, hidinput_list) {
                rdata = mt_find_report_data(td, report);
                if (!rdata) {
@@ -1827,6 +1858,11 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
                dev_err(&hdev->dev, "cannot allocate multitouch data\n");
                return -ENOMEM;
        }
+       td->haptic = devm_kzalloc(&hdev->dev, sizeof(*(td->haptic)), GFP_KERNEL);
+       if (!td->haptic)
+               return -ENOMEM;
+
+       td->haptic->hdev = hdev;
        td->hdev = hdev;
        td->mtclass = *mtclass;
        td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN;
@@ -1895,6 +1931,17 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
 
        mt_set_modes(hdev, HID_LATENCY_NORMAL, TOUCHPAD_REPORT_ALL);
 
+       if (td->is_haptic_touchpad) {
+               if (hid_haptic_init(hdev, &td->haptic)) {
+                       dev_warn(&hdev->dev, "Cannot allocate haptic for %s\n",
+                                hdev->name);
+                       td->is_haptic_touchpad = false;
+                       devm_kfree(&hdev->dev, td->haptic);
+               }
+       } else {
+               devm_kfree(&hdev->dev, td->haptic);
+       }
+
        return 0;
 }