* Re: [PATCH 5/8] pwm: dt-bindings: ti,twl-pwm: convert to DT schema
From: Uwe Kleine-König @ 2025-08-19 6:08 UTC (permalink / raw)
To: Jihed Chaibi
Cc: linux-kernel, peter.ujfalusi, dmitry.torokhov, robh, krzk+dt,
lgirdwood, tiwai, conor+dt, lee, broonie, gregkh, linux-input,
devicetree, linux-pwm, linux-sound, linux-usb, shuah
In-Reply-To: <20250811224739.53869-6-jihed.chaibi.dev@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 3240 bytes --]
On Tue, Aug 12, 2025 at 12:47:36AM +0200, Jihed Chaibi wrote:
> Convert the legacy TXT binding for the TWL-family PWM controller
> to the modern YAML DT schema format. This adds formal validation
> and improves documentation by inheriting from the base pwm schema.
>
> Signed-off-by: Jihed Chaibi <jihed.chaibi.dev@gmail.com>
> ---
> .../devicetree/bindings/pwm/ti,twl-pwm.txt | 17 -------
> .../devicetree/bindings/pwm/ti,twl-pwm.yaml | 46 +++++++++++++++++++
> 2 files changed, 46 insertions(+), 17 deletions(-)
> delete mode 100644 Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt
> create mode 100644 Documentation/devicetree/bindings/pwm/ti,twl-pwm.yaml
>
> diff --git a/Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt b/Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt
> deleted file mode 100644
> index d97ca1964..000000000
> --- a/Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt
> +++ /dev/null
> @@ -1,17 +0,0 @@
> -Texas Instruments TWL series PWM drivers
> -
> -Supported PWMs:
> -On TWL4030 series: PWM1 and PWM2
> -On TWL6030 series: PWM0 and PWM1
> -
> -Required properties:
> -- compatible: "ti,twl4030-pwm" or "ti,twl6030-pwm"
> -- #pwm-cells: should be 2. See pwm.yaml in this directory for a description of
> - the cells format.
> -
> -Example:
> -
> -twl_pwm: pwm {
> - compatible = "ti,twl6030-pwm";
> - #pwm-cells = <2>;
> -};
> diff --git a/Documentation/devicetree/bindings/pwm/ti,twl-pwm.yaml b/Documentation/devicetree/bindings/pwm/ti,twl-pwm.yaml
> new file mode 100644
> index 000000000..5bbbdc13a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pwm/ti,twl-pwm.yaml
> @@ -0,0 +1,46 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/pwm/ti,twl-pwm.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Texas Instruments TWL4030/TWL6030 family PWM controller
> +
> +maintainers:
> + - Peter Ujfalusi <peter.ujfalusi@gmail.com>
> +
> +description: |
> + Bindings for the following PWM controllers :
> + TWL4030 series: PWMA and PWMB (connected to LEDA and LEDB terminals)
> + TWL6030 series: LED PWM (mainly used as charging indicator LED)
> +
> +allOf:
> + - $ref: /schemas/pwm/pwm.yaml#
> +
> +properties:
> + compatible:
> + enum:
> + - ti,twl4030-pwm
> + - ti,twl6030-pwm
> +
> + '#pwm-cells':
> + const: 2
> +
> +required:
> + - compatible
> + - '#pwm-cells'
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + pwm {
> + compatible = "ti,twl4030-pwm";
> + #pwm-cells = <2>;
> + };
> +
> + - |
> + pwm {
> + compatible = "ti,twl6030-pwm";
> + #pwm-cells = <2>;
> + };
Without having asked dt_binding_check it looks ok formally. I guess you
added Peter Ujfalusi as maintainer from the driver file. I'd like to see
a confirmation from him this is fine.
The 2nd example isn't very helpful. Unless I'm missing some detail that
makes it have a value I suggest to drop it. (But no need to resend just
for that, I don't feel that strong and can keep it or fixup when
applying.)
Best regards
Uwe
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH v3 03/11] Input: add INPUT_PROP_HAPTIC_TOUCHPAD
From: Randy Dunlap @ 2025-08-19 4:19 UTC (permalink / raw)
To: Jonathan Denose, Jiri Kosina, Benjamin Tissoires, Dmitry Torokhov,
Jonathan Corbet, Henrik Rydberg
Cc: linux-input, linux-kernel, linux-doc, Angela Czubak,
Sean O'Brien
In-Reply-To: <20250818-support-forcepads-v3-3-e4f9ab0add84@google.com>
On 8/18/25 4:08 PM, Jonathan Denose wrote:
> From: Angela Czubak <aczubak@google.com>
>
> INPUT_PROP_HAPTIC_TOUCHPAD property is to be set for a device with simple
> haptic capabilities.
>
> Signed-off-by: Angela Czubak <aczubak@google.com>
> Co-developed-by: Jonathan Denose <jdenose@google.com>
> Signed-off-by: Jonathan Denose <jdenose@google.com>
Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
Thanks.
> ---
> Documentation/input/event-codes.rst | 14 ++++++++++++++
> include/uapi/linux/input-event-codes.h | 1 +
> 2 files changed, 15 insertions(+)
--
~Randy
^ permalink raw reply
* [PATCH v1 2/2] HID: i2c-hid: elan: Add parade-tc3408 timing
From: Langyan Ye @ 2025-08-19 3:48 UTC (permalink / raw)
To: dmitry.torokhov, robh, krzk+dt, conor+dt, jikos, bentiss,
dianders
Cc: linux-input, devicetree, linux-kernel, Langyan Ye
In-Reply-To: <20250819034852.1230264-1-yelangyan@huaqin.corp-partner.google.com>
Parade-tc3408 requires reset to pull down time greater than 10ms,
so the configuration post_power_delay_ms is 10, and the chipset
initial time is required to be greater than 300ms,
so the post_gpio_reset_on_delay_ms is set to 300.
Signed-off-by: Langyan Ye <yelangyan@huaqin.corp-partner.google.com>
---
drivers/hid/i2c-hid/i2c-hid-of-elan.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/hid/i2c-hid/i2c-hid-of-elan.c b/drivers/hid/i2c-hid/i2c-hid-of-elan.c
index 3fcff6daa0d3..23826cb808b7 100644
--- a/drivers/hid/i2c-hid/i2c-hid-of-elan.c
+++ b/drivers/hid/i2c-hid/i2c-hid-of-elan.c
@@ -179,11 +179,19 @@ static const struct elan_i2c_hid_chip_data ilitek_ili2901_chip_data = {
.main_supply_name = "vcc33",
};
+static const struct elan_i2c_hid_chip_data parade_tc3408_chip_data = {
+ .post_power_delay_ms = 10,
+ .post_gpio_reset_on_delay_ms = 100,
+ .hid_descriptor_address = 0x0001,
+ .main_supply_name = "vcc33",
+};
+
static const struct of_device_id elan_i2c_hid_of_match[] = {
{ .compatible = "elan,ekth6915", .data = &elan_ekth6915_chip_data },
{ .compatible = "elan,ekth6a12nay", .data = &elan_ekth6a12nay_chip_data },
{ .compatible = "ilitek,ili9882t", .data = &ilitek_ili9882t_chip_data },
{ .compatible = "ilitek,ili2901", .data = &ilitek_ili2901_chip_data },
+ { .compatible = "parade,tc3408", .data = ¶de_tc3408_chip_data },
{ }
};
MODULE_DEVICE_TABLE(of, elan_i2c_hid_of_match);
--
2.34.1
^ permalink raw reply related
* [PATCH v1 1/2] dt-bindings: HID: i2c-hid: parade: Introduce bindings for Parade tc3408
From: Langyan Ye @ 2025-08-19 3:48 UTC (permalink / raw)
To: dmitry.torokhov, robh, krzk+dt, conor+dt, jikos, bentiss,
dianders
Cc: linux-input, devicetree, linux-kernel, Langyan Ye
In-Reply-To: <20250819034852.1230264-1-yelangyan@huaqin.corp-partner.google.com>
The tc3408 touch screen chip same as Elan eKTH6915 controller
has a reset gpio. The difference is that they have different
post_power_delay_ms.
Signed-off-by: Langyan Ye <yelangyan@huaqin.corp-partner.google.com>
---
.../bindings/input/parade,tc3408.yaml | 72 +++++++++++++++++++
1 file changed, 72 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/parade,tc3408.yaml
diff --git a/Documentation/devicetree/bindings/input/parade,tc3408.yaml b/Documentation/devicetree/bindings/input/parade,tc3408.yaml
new file mode 100644
index 000000000000..25db85e90b18
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/parade,tc3408.yaml
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/parade,tc3408.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Parade tc3408 touchscreen controller
+
+maintainers:
+ - Langyan Ye <yelangyan@huaqin.corp-partner.google.com>
+
+description:
+ Supports the Parade tc3408 touchscreen controller.
+ This touchscreen controller uses the i2c-hid protocol with a reset GPIO.
+
+allOf:
+ - $ref: /schemas/input/touchscreen/touchscreen.yaml#
+
+properties:
+ compatible:
+ const: parade,tc3408
+
+ reg:
+ const: 0x24
+
+ interrupts:
+ maxItems: 1
+
+ panel: true
+
+ reset-gpios:
+ maxItems: 1
+ description: Reset GPIO.
+
+ vcc33-supply:
+ description: The 3.3V supply to the touchscreen.
+
+ vccio-supply:
+ description: The 1.8V supply to the touchscreen.
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - panel
+ - vcc33-supply
+ - vccio-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ touchscreen: touchscreen@24 {
+ compatible = "parade,tc3408";
+ reg = <0x24>;
+
+ interrupt-parent = <&pio>;
+ interrupts = <15 IRQ_TYPE_LEVEL_LOW>;
+
+ panel = <&panel>;
+ reset-gpios = <&pio 126 GPIO_ACTIVE_LOW>;
+ vcc33-supply = <&pp3300_tchscr_x>;
+ vccio-supply = <&pp1800_tchscr_report_disable>;
+ };
+ };
--
2.34.1
^ permalink raw reply related
* [PATCH v1 0/2] Add tc3408 bindings and timing
From: Langyan Ye @ 2025-08-19 3:48 UTC (permalink / raw)
To: dmitry.torokhov, robh, krzk+dt, conor+dt, jikos, bentiss,
dianders
Cc: linux-input, devicetree, linux-kernel, Langyan Ye
The tc3408 touch screen chip same as Elan eKTH6915 controller
has a reset gpio. The difference is that they have different
post_power_delay_ms.
According to the Parade TC3408 datasheet, the reset pin requires a
pull-down duration longer than 10 ms, therefore post_power_delay_ms
is set to 10. In addition, the chipset requires an initialization
time greater than 300 ms after reset, so post_gpio_reset_on_delay_ms
is configured as 300.
Langyan Ye (2):
dt-bindings: HID: i2c-hid: parade: Introduce bindings for Parade
tc3408
HID: i2c-hid: elan: Add parade-tc3408 timing
.../bindings/input/parade,tc3408.yaml | 72 +++++++++++++++++++
drivers/hid/i2c-hid/i2c-hid-of-elan.c | 8 +++
2 files changed, 80 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/parade,tc3408.yaml
--
2.34.1
^ permalink raw reply
* [PATCH v3 11/11] HID: multitouch: add haptic multitouch support
From: Jonathan Denose @ 2025-08-18 23:08 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, Dmitry Torokhov, Jonathan Corbet,
Henrik Rydberg
Cc: linux-input, linux-kernel, linux-doc, Angela Czubak,
Sean O'Brien, Jonathan Denose
In-Reply-To: <20250818-support-forcepads-v3-0-e4f9ab0add84@google.com>
From: Angela Czubak <aczubak@google.com>
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>
---
drivers/hid/hid-haptic.h | 16 ++++++++++-----
drivers/hid/hid-multitouch.c | 47 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 58 insertions(+), 5 deletions(-)
diff --git a/drivers/hid/hid-haptic.h b/drivers/hid/hid-haptic.h
index abdd7d710c0b832ad0be8fe63ebfa7692c8ea5ca..c6539ac04c1dbd36fb25eed9370e6fe747a28f10 100644
--- a/drivers/hid/hid-haptic.h
+++ b/drivers/hid/hid-haptic.h
@@ -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
-
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index b41001e02da7e02d492bd85743b359ed7ec16e7f..94dd0033290d6ae37d82f0992b94e24ebbc1f301 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -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)
@@ -167,11 +169,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;
@@ -525,6 +529,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,
@@ -856,6 +862,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:
@@ -980,6 +989,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);
@@ -1137,6 +1148,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;
@@ -1324,6 +1338,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;
@@ -1359,6 +1376,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) {
@@ -1421,6 +1439,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;
}
@@ -1635,6 +1658,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) {
@@ -1777,6 +1808,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;
@@ -1840,6 +1876,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;
}
--
2.51.0.rc1.193.gad69d77794-goog
^ permalink raw reply related
* [PATCH v3 02/11] Input: add FF_HAPTIC effect type
From: Jonathan Denose @ 2025-08-18 23:08 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, Dmitry Torokhov, Jonathan Corbet,
Henrik Rydberg
Cc: linux-input, linux-kernel, linux-doc, Angela Czubak,
Sean O'Brien, Jonathan Denose
In-Reply-To: <20250818-support-forcepads-v3-0-e4f9ab0add84@google.com>
From: Angela Czubak <aczubak@google.com>
FF_HAPTIC effect type can be used to trigger haptic feedback with HID
simple haptic usages.
Signed-off-by: Angela Czubak <aczubak@google.com>
Co-developed-by: Jonathan Denose <jdenose@google.com>
Signed-off-by: Jonathan Denose <jdenose@google.com>
---
include/uapi/linux/input.h | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index 2557eb7b056178b2b8be98d9cea855eba1bd5aaf..a440fafaa018947f4d9ca0a15af2b4c6bd0ae6a0 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -428,6 +428,24 @@ struct ff_rumble_effect {
__u16 weak_magnitude;
};
+/**
+ * struct ff_haptic_effect
+ * @hid_usage: hid_usage according to Haptics page (WAVEFORM_CLICK, etc.)
+ * @vendor_id: the waveform vendor ID if hid_usage is in the vendor-defined range
+ * @vendor_waveform_page: the vendor waveform page if hid_usage is in the vendor-defined range
+ * @intensity: strength of the effect as percentage
+ * @repeat_count: number of times to retrigger effect
+ * @retrigger_period: time before effect is retriggered (in ms)
+ */
+struct ff_haptic_effect {
+ __u16 hid_usage;
+ __u16 vendor_id;
+ __u8 vendor_waveform_page;
+ __u16 intensity;
+ __u16 repeat_count;
+ __u16 retrigger_period;
+};
+
/**
* struct ff_effect - defines force feedback effect
* @type: type of the effect (FF_CONSTANT, FF_PERIODIC, FF_RAMP, FF_SPRING,
@@ -464,6 +482,7 @@ struct ff_effect {
struct ff_periodic_effect periodic;
struct ff_condition_effect condition[2]; /* One for each axis */
struct ff_rumble_effect rumble;
+ struct ff_haptic_effect haptic;
} u;
};
@@ -471,6 +490,7 @@ struct ff_effect {
* Force feedback effect types
*/
+#define FF_HAPTIC 0x4f
#define FF_RUMBLE 0x50
#define FF_PERIODIC 0x51
#define FF_CONSTANT 0x52
@@ -480,7 +500,7 @@ struct ff_effect {
#define FF_INERTIA 0x56
#define FF_RAMP 0x57
-#define FF_EFFECT_MIN FF_RUMBLE
+#define FF_EFFECT_MIN FF_HAPTIC
#define FF_EFFECT_MAX FF_RAMP
/*
--
2.51.0.rc1.193.gad69d77794-goog
^ permalink raw reply related
* [PATCH v3 10/11] HID: haptic: add hid_haptic_switch_mode
From: Jonathan Denose @ 2025-08-18 23:08 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, Dmitry Torokhov, Jonathan Corbet,
Henrik Rydberg
Cc: linux-input, linux-kernel, linux-doc, Angela Czubak,
Sean O'Brien, Jonathan Denose
In-Reply-To: <20250818-support-forcepads-v3-0-e4f9ab0add84@google.com>
From: Angela Czubak <aczubak@google.com>
Function hid_haptic_switch_mode() can be used to switch between
device-controlled mode and host-controlled mode. Uploading a
WAVEFORMPRESS or WAVEFORMRELEASE effect triggers host-controlled mode if
the device is in device-controlled mode.
Signed-off-by: Angela Czubak <aczubak@google.com>
Co-developed-by: Jonathan Denose <jdenose@google.com>
Signed-off-by: Jonathan Denose <jdenose@google.com>
---
drivers/hid/hid-haptic.c | 66 +++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 59 insertions(+), 7 deletions(-)
diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c
index c02af820051c22d1c899db84496c5a44b868fe49..aa090684c1f23b61a1ac4e9e7e523b31a8166a21 100644
--- a/drivers/hid/hid-haptic.c
+++ b/drivers/hid/hid-haptic.c
@@ -5,6 +5,7 @@
* Copyright (c) 2021 Angela Czubak <acz@semihalf.com>
*/
+#include <linux/input/mt.h>
#include <linux/module.h>
#include "hid-haptic.h"
@@ -197,12 +198,46 @@ static void fill_effect_buf(struct hid_haptic_device *haptic,
mutex_unlock(&haptic->manual_trigger_mutex);
}
+static void switch_mode(struct hid_device *hdev, struct hid_haptic_device *haptic,
+ int mode)
+{
+ struct hid_report *rep = haptic->auto_trigger_report;
+ struct hid_field *field;
+ s32 value;
+ int i, j;
+
+ if (mode == HID_HAPTIC_MODE_HOST)
+ value = HID_HAPTIC_ORDINAL_WAVEFORMSTOP;
+ else
+ value = haptic->default_auto_trigger;
+
+ mutex_lock(&haptic->auto_trigger_mutex);
+ for (i = 0; i < rep->maxfield; i++) {
+ field = rep->field[i];
+ /* Ignore if report count is out of bounds. */
+ if (field->report_count < 1)
+ continue;
+
+ for (j = 0; j < field->maxusage; j++) {
+ if (field->usage[j].hid == HID_HP_AUTOTRIGGER)
+ field->value[j] = value;
+ }
+ }
+
+ /* send the report */
+ hid_hw_request(hdev, rep, HID_REQ_SET_REPORT);
+ mutex_unlock(&haptic->auto_trigger_mutex);
+ haptic->mode = mode;
+}
+
static int hid_haptic_upload_effect(struct input_dev *dev, struct ff_effect *effect,
struct ff_effect *old)
{
+ struct hid_device *hdev = input_get_drvdata(dev);
struct ff_device *ff = dev->ff;
struct hid_haptic_device *haptic = ff->private;
int i, ordinal = 0;
+ bool switch_modes = false;
/* If vendor range, check vendor id and page */
if (effect->u.haptic.hid_usage >= (HID_HP_VENDORWAVEFORMMIN & HID_USAGE) &&
@@ -225,6 +260,16 @@ static int hid_haptic_upload_effect(struct input_dev *dev, struct ff_effect *eff
fill_effect_buf(haptic, &effect->u.haptic, &haptic->effect[effect->id],
ordinal);
+ if (effect->u.haptic.hid_usage == (HID_HP_WAVEFORMPRESS & HID_USAGE) ||
+ effect->u.haptic.hid_usage == (HID_HP_WAVEFORMRELEASE & HID_USAGE))
+ switch_modes = true;
+
+ /* If device is in autonomous mode, and the uploaded effect signals userspace
+ * wants control of the device, change modes
+ */
+ if (switch_modes && haptic->mode == HID_HAPTIC_MODE_DEVICE)
+ switch_mode(hdev, haptic, HID_HAPTIC_MODE_HOST);
+
return 0;
}
@@ -290,6 +335,7 @@ static void effect_set_default(struct ff_effect *effect)
static int hid_haptic_erase(struct input_dev *dev, int effect_id)
{
struct hid_haptic_device *haptic = dev->ff->private;
+ struct hid_device *hdev = input_get_drvdata(dev);
struct ff_effect effect;
int ordinal;
@@ -297,20 +343,25 @@ static int hid_haptic_erase(struct input_dev *dev, int effect_id)
if (effect.u.haptic.hid_usage == (HID_HP_WAVEFORMRELEASE & HID_USAGE)) {
ordinal = haptic->release_ordinal;
- if (!ordinal)
+ if (!ordinal) {
ordinal = HID_HAPTIC_ORDINAL_WAVEFORMNONE;
- else
- effect.u.haptic.hid_usage = HID_HP_WAVEFORMRELEASE &
- HID_USAGE;
+ if (haptic->mode == HID_HAPTIC_MODE_HOST)
+ switch_mode(hdev, haptic, HID_HAPTIC_MODE_DEVICE);
+ } else
+ effect.u.haptic.hid_usage = HID_HP_WAVEFORMRELEASE & HID_USAGE;
+
fill_effect_buf(haptic, &effect.u.haptic, &haptic->effect[effect_id],
ordinal);
} else if (effect.u.haptic.hid_usage == (HID_HP_WAVEFORMPRESS & HID_USAGE)) {
ordinal = haptic->press_ordinal;
- if (!ordinal)
+ if (!ordinal) {
ordinal = HID_HAPTIC_ORDINAL_WAVEFORMNONE;
+ if (haptic->mode == HID_HAPTIC_MODE_HOST)
+ switch_mode(hdev, haptic, HID_HAPTIC_MODE_DEVICE);
+ }
else
- effect.u.haptic.hid_usage = HID_HP_WAVEFORMPRESS &
- HID_USAGE;
+ effect.u.haptic.hid_usage = HID_HP_WAVEFORMPRESS & HID_USAGE;
+
fill_effect_buf(haptic, &effect.u.haptic, &haptic->effect[effect_id],
ordinal);
}
@@ -392,6 +443,7 @@ int hid_haptic_init(struct hid_device *hdev,
haptic->hid_usage_map[HID_HAPTIC_ORDINAL_WAVEFORMSTOP] =
HID_HP_WAVEFORMSTOP & HID_USAGE;
+ mutex_init(&haptic->auto_trigger_mutex);
for (r = 0; r < haptic->auto_trigger_report->maxfield; r++)
parse_auto_trigger_field(haptic, haptic->auto_trigger_report->field[r]);
--
2.51.0.rc1.193.gad69d77794-goog
^ permalink raw reply related
* [PATCH v3 03/11] Input: add INPUT_PROP_HAPTIC_TOUCHPAD
From: Jonathan Denose @ 2025-08-18 23:08 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, Dmitry Torokhov, Jonathan Corbet,
Henrik Rydberg
Cc: linux-input, linux-kernel, linux-doc, Angela Czubak,
Sean O'Brien, Jonathan Denose
In-Reply-To: <20250818-support-forcepads-v3-0-e4f9ab0add84@google.com>
From: Angela Czubak <aczubak@google.com>
INPUT_PROP_HAPTIC_TOUCHPAD property is to be set for a device with simple
haptic capabilities.
Signed-off-by: Angela Czubak <aczubak@google.com>
Co-developed-by: Jonathan Denose <jdenose@google.com>
Signed-off-by: Jonathan Denose <jdenose@google.com>
---
Documentation/input/event-codes.rst | 14 ++++++++++++++
include/uapi/linux/input-event-codes.h | 1 +
2 files changed, 15 insertions(+)
diff --git a/Documentation/input/event-codes.rst b/Documentation/input/event-codes.rst
index b4557462edd7b3fef9e9cd6c2c3cb2d05bb531ab..1ead9bb8d9c6451a81426665baab643f9e50216a 100644
--- a/Documentation/input/event-codes.rst
+++ b/Documentation/input/event-codes.rst
@@ -400,6 +400,20 @@ can report through the rotational axes (absolute and/or relative rx, ry, rz).
All other axes retain their meaning. A device must not mix
regular directional axes and accelerometer axes on the same event node.
+INPUT_PROP_HAPTIC_TOUCHPAD
+--------------------------
+
+The INPUT_PROP_HAPTIC_TOUCHPAD property indicates that device:
+- supports simple haptic auto and manual triggering
+- can differentiate between at least 5 fingers
+- uses correct resolution for the X/Y (units and value)
+- reports correct force per touch, and correct units for them (newtons or grams)
+- follows the MT protocol type B
+
+Summing up, such devices follow the MS spec for input devices in
+Win8 and Win8.1, and in addition support the Simple haptic controller HID table,
+and report correct units for the pressure.
+
Guidelines
==========
diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h
index 3b2524e4b667d1e7cc02ff5cb674e7c2ac069a66..efe8c36d4ee9a938ffb1b0dd0ddd0ec6a3fcb8fe 100644
--- a/include/uapi/linux/input-event-codes.h
+++ b/include/uapi/linux/input-event-codes.h
@@ -27,6 +27,7 @@
#define INPUT_PROP_TOPBUTTONPAD 0x04 /* softbuttons at top of pad */
#define INPUT_PROP_POINTING_STICK 0x05 /* is a pointing stick */
#define INPUT_PROP_ACCELEROMETER 0x06 /* has accelerometer */
+#define INPUT_PROP_HAPTIC_TOUCHPAD 0x07 /* is a haptic touchpad */
#define INPUT_PROP_MAX 0x1f
#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1)
--
2.51.0.rc1.193.gad69d77794-goog
^ permalink raw reply related
* [PATCH v3 08/11] HID: haptic: add functions handling events
From: Jonathan Denose @ 2025-08-18 23:08 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, Dmitry Torokhov, Jonathan Corbet,
Henrik Rydberg
Cc: linux-input, linux-kernel, linux-doc, Angela Czubak,
Sean O'Brien, Jonathan Denose
In-Reply-To: <20250818-support-forcepads-v3-0-e4f9ab0add84@google.com>
From: Angela Czubak <aczubak@google.com>
Implement hid_haptic_handle_press_release() which generates haptic feedback
as well as saves the pressed state of the haptic device.
Add functions to increase and reset the state of the pressure detected by
the device.
Signed-off-by: Angela Czubak <aczubak@google.com>
Co-developed-by: Jonathan Denose <jdenose@google.com>
Signed-off-by: Jonathan Denose <jdenose@google.com>
---
drivers/hid/hid-haptic.c | 20 +++++++++++++++++++-
drivers/hid/hid-haptic.h | 15 +++++++++++++++
2 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c
index ef09b4039f33f15d7220e69fbed10bd8b0362bb4..c02af820051c22d1c899db84496c5a44b868fe49 100644
--- a/drivers/hid/hid-haptic.c
+++ b/drivers/hid/hid-haptic.c
@@ -50,8 +50,13 @@ EXPORT_SYMBOL_GPL(hid_haptic_feature_mapping);
bool hid_haptic_check_pressure_unit(struct hid_haptic_device *haptic,
struct hid_input *hi, struct hid_field *field)
{
- if (field->unit == HID_UNIT_GRAM || field->unit == HID_UNIT_NEWTON)
+ if (field->unit == HID_UNIT_GRAM || field->unit == HID_UNIT_NEWTON) {
+ haptic->force_logical_minimum = field->logical_minimum;
+ haptic->force_physical_minimum = field->physical_minimum;
+ haptic->force_resolution = input_abs_get_res(hi->input,
+ ABS_MT_PRESSURE);
return true;
+ }
return false;
}
EXPORT_SYMBOL_GPL(hid_haptic_check_pressure_unit);
@@ -508,3 +513,16 @@ int hid_haptic_init(struct hid_device *hdev,
return ret;
}
EXPORT_SYMBOL_GPL(hid_haptic_init);
+
+void hid_haptic_pressure_reset(struct hid_haptic_device *haptic)
+{
+ haptic->pressure_sum = 0;
+}
+EXPORT_SYMBOL_GPL(hid_haptic_pressure_reset);
+
+void hid_haptic_pressure_increase(struct hid_haptic_device *haptic,
+ __s32 pressure)
+{
+ haptic->pressure_sum += pressure;
+}
+EXPORT_SYMBOL_GPL(hid_haptic_pressure_increase);
diff --git a/drivers/hid/hid-haptic.h b/drivers/hid/hid-haptic.h
index 25e0e2cef9399199796d8679d66209381b6a59f2..abdd7d710c0b832ad0be8fe63ebfa7692c8ea5ca 100644
--- a/drivers/hid/hid-haptic.h
+++ b/drivers/hid/hid-haptic.h
@@ -70,6 +70,10 @@ int hid_haptic_input_configured(struct hid_device *hdev,
struct hid_haptic_device *haptic,
struct hid_input *hi);
int hid_haptic_init(struct hid_device *hdev, struct hid_haptic_device **haptic_ptr);
+void hid_haptic_handle_press_release(struct hid_haptic_device *haptic);
+void hid_haptic_pressure_reset(struct hid_haptic_device *haptic);
+void hid_haptic_pressure_increase(struct hid_haptic_device *haptic,
+ __s32 pressure);
#else
static inline
void hid_haptic_feature_mapping(struct hid_device *hdev,
@@ -103,4 +107,15 @@ int hid_haptic_init(struct hid_device *hdev, struct hid_haptic_device **haptic_p
{
return 0;
}
+static inline
+void hid_haptic_handle_press_release(struct hid_haptic_device *haptic)
+{}
+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
+
--
2.51.0.rc1.193.gad69d77794-goog
^ permalink raw reply related
* [PATCH v3 09/11] Input: MT - add INPUT_MT_TOTAL_FORCE flags
From: Jonathan Denose @ 2025-08-18 23:08 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, Dmitry Torokhov, Jonathan Corbet,
Henrik Rydberg
Cc: linux-input, linux-kernel, linux-doc, Angela Czubak,
Sean O'Brien, Jonathan Denose
In-Reply-To: <20250818-support-forcepads-v3-0-e4f9ab0add84@google.com>
From: Angela Czubak <aczubak@google.com>
Add a flag to generate ABS_PRESSURE as sum of ABS_MT_PRESSURE across
all slots.
This flag should be set if one knows a device reports true force and would
like to report total force to the userspace.
Signed-off-by: Angela Czubak <aczubak@google.com>
Co-developed-by: Jonathan Denose <jdenose@google.com>
Signed-off-by: Jonathan Denose <jdenose@google.com>
---
drivers/input/input-mt.c | 14 ++++++++++----
include/linux/input/mt.h | 1 +
2 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c
index 337006dd9dcf72ef2eeb8580e4dd83babf8100be..09f518897d4a71a4a7625367dc2c652ee6035d98 100644
--- a/drivers/input/input-mt.c
+++ b/drivers/input/input-mt.c
@@ -198,6 +198,7 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
struct input_mt *mt = dev->mt;
struct input_mt_slot *oldest;
int oldid, count, i;
+ int p, reported_p = 0;
if (!mt)
return;
@@ -216,6 +217,13 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
oldest = ps;
oldid = id;
}
+ if (test_bit(ABS_MT_PRESSURE, dev->absbit)) {
+ p = input_mt_get_value(ps, ABS_MT_PRESSURE);
+ if (mt->flags & INPUT_MT_TOTAL_FORCE)
+ reported_p += p;
+ else if (oldid == id)
+ reported_p = p;
+ }
count++;
}
@@ -245,10 +253,8 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
input_event(dev, EV_ABS, ABS_X, x);
input_event(dev, EV_ABS, ABS_Y, y);
- if (test_bit(ABS_MT_PRESSURE, dev->absbit)) {
- int p = input_mt_get_value(oldest, ABS_MT_PRESSURE);
- input_event(dev, EV_ABS, ABS_PRESSURE, p);
- }
+ if (test_bit(ABS_MT_PRESSURE, dev->absbit))
+ input_event(dev, EV_ABS, ABS_PRESSURE, reported_p);
} else {
if (test_bit(ABS_MT_PRESSURE, dev->absbit))
input_event(dev, EV_ABS, ABS_PRESSURE, 0);
diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h
index 2cf89a538b18bbc7c99c8705c2d22bdc95065238..d30286298a00a356bc9db954ae362f034cdd359b 100644
--- a/include/linux/input/mt.h
+++ b/include/linux/input/mt.h
@@ -17,6 +17,7 @@
#define INPUT_MT_DROP_UNUSED 0x0004 /* drop contacts not seen in frame */
#define INPUT_MT_TRACK 0x0008 /* use in-kernel tracking */
#define INPUT_MT_SEMI_MT 0x0010 /* semi-mt device, finger count handled manually */
+#define INPUT_MT_TOTAL_FORCE 0x0020 /* calculate total force from slots pressure */
/**
* struct input_mt_slot - represents the state of an input MT slot
--
2.51.0.rc1.193.gad69d77794-goog
^ permalink raw reply related
* [PATCH v3 07/11] HID: input: calculate resolution for pressure
From: Jonathan Denose @ 2025-08-18 23:08 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, Dmitry Torokhov, Jonathan Corbet,
Henrik Rydberg
Cc: linux-input, linux-kernel, linux-doc, Angela Czubak,
Sean O'Brien, Jonathan Denose
In-Reply-To: <20250818-support-forcepads-v3-0-e4f9ab0add84@google.com>
From: Angela Czubak <aczubak@google.com>
Assume that if the pressure is given in newtons it should be normalized
to grams. If the pressure has no unit do not calculate resolution.
Signed-off-by: Angela Czubak <aczubak@google.com>
Co-developed-by: Jonathan Denose <jdenose@google.com>
Signed-off-by: Jonathan Denose <jdenose@google.com>
---
drivers/hid/hid-input.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index d42c1fbd20a1cc01c04f93cf10f1d1c18043929c..1d59787bd0c0e251698e2a2944dae1c4a96adefe 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -303,6 +303,19 @@ __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
}
break;
+ case ABS_PRESSURE:
+ case ABS_MT_PRESSURE:
+ if (field->unit == HID_UNIT_NEWTON) {
+ /* Convert to grams, 1 newton is 101.97 grams */
+ prev = physical_extents;
+ physical_extents *= 10197;
+ if (physical_extents < prev)
+ return 0;
+ unit_exponent -= 2;
+ } else if (field->unit != HID_UNIT_GRAM) {
+ return 0;
+ }
+ break;
default:
return 0;
}
--
2.51.0.rc1.193.gad69d77794-goog
^ permalink raw reply related
* [PATCH v3 06/11] HID: haptic: initialize haptic device
From: Jonathan Denose @ 2025-08-18 23:08 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, Dmitry Torokhov, Jonathan Corbet,
Henrik Rydberg
Cc: linux-input, linux-kernel, linux-doc, Angela Czubak,
Sean O'Brien, Jonathan Denose
In-Reply-To: <20250818-support-forcepads-v3-0-e4f9ab0add84@google.com>
From: Angela Czubak <aczubak@google.com>
Add hid_haptic_init(). Parse autotrigger report to retrieve ordinals for
press and release waveforms.
Implement force feedback functions.
Signed-off-by: Angela Czubak <aczubak@google.com>
Co-developed-by: Jonathan Denose <jdenose@google.com>
Signed-off-by: Jonathan Denose <jdenose@google.com>
---
drivers/hid/hid-haptic.c | 438 +++++++++++++++++++++++++++++++++++++++++++++++
drivers/hid/hid-haptic.h | 5 +
2 files changed, 443 insertions(+)
diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c
index d659a430c1a6b06ded31d49efe4bded909671cb6..ef09b4039f33f15d7220e69fbed10bd8b0362bb4 100644
--- a/drivers/hid/hid-haptic.c
+++ b/drivers/hid/hid-haptic.c
@@ -5,12 +5,16 @@
* Copyright (c) 2021 Angela Czubak <acz@semihalf.com>
*/
+#include <linux/module.h>
+
#include "hid-haptic.h"
void hid_haptic_feature_mapping(struct hid_device *hdev,
struct hid_haptic_device *haptic,
struct hid_field *field, struct hid_usage *usage)
{
+ u16 usage_hid;
+
if (usage->hid == HID_HP_AUTOTRIGGER) {
if (usage->usage_index >= field->report_count) {
dev_err(&hdev->dev,
@@ -25,6 +29,20 @@ void hid_haptic_feature_mapping(struct hid_device *hdev,
haptic->default_auto_trigger =
field->value[usage->usage_index];
haptic->auto_trigger_report = field->report;
+ } else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_ORDINAL) {
+ usage_hid = usage->hid & HID_USAGE;
+ switch (field->logical) {
+ case HID_HP_WAVEFORMLIST:
+ if (usage_hid > haptic->max_waveform_id)
+ haptic->max_waveform_id = usage_hid;
+ break;
+ case HID_HP_DURATIONLIST:
+ if (usage_hid > haptic->max_duration_id)
+ haptic->max_duration_id = usage_hid;
+ break;
+ default:
+ break;
+ }
}
}
EXPORT_SYMBOL_GPL(hid_haptic_feature_mapping);
@@ -70,3 +88,423 @@ int hid_haptic_input_configured(struct hid_device *hdev,
return -1;
}
EXPORT_SYMBOL_GPL(hid_haptic_input_configured);
+
+static void parse_auto_trigger_field(struct hid_haptic_device *haptic,
+ struct hid_field *field)
+{
+ int count = field->report_count;
+ int n;
+ u16 usage_hid;
+
+ for (n = 0; n < count; n++) {
+ switch (field->usage[n].hid & HID_USAGE_PAGE) {
+ case HID_UP_ORDINAL:
+ usage_hid = field->usage[n].hid & HID_USAGE;
+ switch (field->logical) {
+ case HID_HP_WAVEFORMLIST:
+ haptic->hid_usage_map[usage_hid] = field->value[n];
+ if (field->value[n] ==
+ (HID_HP_WAVEFORMPRESS & HID_USAGE)) {
+ haptic->press_ordinal = usage_hid;
+ } else if (field->value[n] ==
+ (HID_HP_WAVEFORMRELEASE & HID_USAGE)) {
+ haptic->release_ordinal = usage_hid;
+ }
+ break;
+ case HID_HP_DURATIONLIST:
+ haptic->duration_map[usage_hid] =
+ field->value[n];
+ break;
+ default:
+ break;
+ }
+ break;
+ case HID_UP_HAPTIC:
+ switch (field->usage[n].hid) {
+ case HID_HP_WAVEFORMVENDORID:
+ haptic->vendor_id = field->value[n];
+ break;
+ case HID_HP_WAVEFORMVENDORPAGE:
+ haptic->vendor_page = field->value[n];
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ /* Should not really happen */
+ break;
+ }
+ }
+}
+
+static void fill_effect_buf(struct hid_haptic_device *haptic,
+ struct ff_haptic_effect *effect,
+ struct hid_haptic_effect *haptic_effect,
+ int waveform_ordinal)
+{
+ struct hid_report *rep = haptic->manual_trigger_report;
+ struct hid_usage *usage;
+ struct hid_field *field;
+ s32 value;
+ int i, j;
+ u8 *buf = haptic_effect->report_buf;
+
+ mutex_lock(&haptic->manual_trigger_mutex);
+ for (i = 0; i < rep->maxfield; i++) {
+ field = rep->field[i];
+ /* Ignore if report count is out of bounds. */
+ if (field->report_count < 1)
+ continue;
+
+ for (j = 0; j < field->maxusage; j++) {
+ usage = &field->usage[j];
+
+ switch (usage->hid) {
+ case HID_HP_INTENSITY:
+ if (effect->intensity > 100) {
+ value = field->logical_maximum;
+ } else {
+ value = field->logical_minimum +
+ effect->intensity *
+ (field->logical_maximum -
+ field->logical_minimum) / 100;
+ }
+ break;
+ case HID_HP_REPEATCOUNT:
+ value = effect->repeat_count;
+ break;
+ case HID_HP_RETRIGGERPERIOD:
+ value = effect->retrigger_period;
+ break;
+ case HID_HP_MANUALTRIGGER:
+ value = waveform_ordinal;
+ break;
+ default:
+ break;
+ }
+
+ field->value[j] = value;
+ }
+ }
+
+ hid_output_report(rep, buf);
+ mutex_unlock(&haptic->manual_trigger_mutex);
+}
+
+static int hid_haptic_upload_effect(struct input_dev *dev, struct ff_effect *effect,
+ struct ff_effect *old)
+{
+ struct ff_device *ff = dev->ff;
+ struct hid_haptic_device *haptic = ff->private;
+ int i, ordinal = 0;
+
+ /* If vendor range, check vendor id and page */
+ if (effect->u.haptic.hid_usage >= (HID_HP_VENDORWAVEFORMMIN & HID_USAGE) &&
+ effect->u.haptic.hid_usage <= (HID_HP_VENDORWAVEFORMMAX & HID_USAGE) &&
+ (effect->u.haptic.vendor_id != haptic->vendor_id ||
+ effect->u.haptic.vendor_waveform_page != haptic->vendor_page))
+ return -EINVAL;
+
+ /* Check hid_usage */
+ for (i = 1; i <= haptic->max_waveform_id; i++) {
+ if (haptic->hid_usage_map[i] == effect->u.haptic.hid_usage) {
+ ordinal = i;
+ break;
+ }
+ }
+ if (ordinal < 1)
+ return -EINVAL;
+
+ /* Fill the buffer for the effect id */
+ fill_effect_buf(haptic, &effect->u.haptic, &haptic->effect[effect->id],
+ ordinal);
+
+ return 0;
+}
+
+static int play_effect(struct hid_device *hdev, struct hid_haptic_device *haptic,
+ struct hid_haptic_effect *effect)
+{
+ int ret;
+
+ ret = hid_hw_output_report(hdev, effect->report_buf,
+ haptic->manual_trigger_report_len);
+ if (ret < 0) {
+ ret = hid_hw_raw_request(hdev,
+ haptic->manual_trigger_report->id,
+ effect->report_buf,
+ haptic->manual_trigger_report_len,
+ HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
+ }
+
+ return ret;
+}
+
+static void haptic_work_handler(struct work_struct *work)
+{
+
+ struct hid_haptic_effect *effect = container_of(work,
+ struct hid_haptic_effect,
+ work);
+ struct input_dev *dev = effect->input_dev;
+ struct hid_device *hdev = input_get_drvdata(dev);
+ struct hid_haptic_device *haptic = dev->ff->private;
+
+ mutex_lock(&haptic->manual_trigger_mutex);
+ if (effect != &haptic->stop_effect)
+ play_effect(hdev, haptic, &haptic->stop_effect);
+
+ play_effect(hdev, haptic, effect);
+ mutex_unlock(&haptic->manual_trigger_mutex);
+
+}
+
+static int hid_haptic_playback(struct input_dev *dev, int effect_id, int value)
+{
+ struct hid_haptic_device *haptic = dev->ff->private;
+
+ if (value)
+ queue_work(haptic->wq, &haptic->effect[effect_id].work);
+ else
+ queue_work(haptic->wq, &haptic->stop_effect.work);
+
+ return 0;
+}
+
+static void effect_set_default(struct ff_effect *effect)
+{
+ effect->type = FF_HAPTIC;
+ effect->id = -1;
+ effect->u.haptic.hid_usage = HID_HP_WAVEFORMNONE & HID_USAGE;
+ effect->u.haptic.intensity = 100;
+ effect->u.haptic.retrigger_period = 0;
+ effect->u.haptic.repeat_count = 0;
+}
+
+static int hid_haptic_erase(struct input_dev *dev, int effect_id)
+{
+ struct hid_haptic_device *haptic = dev->ff->private;
+ struct ff_effect effect;
+ int ordinal;
+
+ effect_set_default(&effect);
+
+ if (effect.u.haptic.hid_usage == (HID_HP_WAVEFORMRELEASE & HID_USAGE)) {
+ ordinal = haptic->release_ordinal;
+ if (!ordinal)
+ ordinal = HID_HAPTIC_ORDINAL_WAVEFORMNONE;
+ else
+ effect.u.haptic.hid_usage = HID_HP_WAVEFORMRELEASE &
+ HID_USAGE;
+ fill_effect_buf(haptic, &effect.u.haptic, &haptic->effect[effect_id],
+ ordinal);
+ } else if (effect.u.haptic.hid_usage == (HID_HP_WAVEFORMPRESS & HID_USAGE)) {
+ ordinal = haptic->press_ordinal;
+ if (!ordinal)
+ ordinal = HID_HAPTIC_ORDINAL_WAVEFORMNONE;
+ else
+ effect.u.haptic.hid_usage = HID_HP_WAVEFORMPRESS &
+ HID_USAGE;
+ fill_effect_buf(haptic, &effect.u.haptic, &haptic->effect[effect_id],
+ ordinal);
+ }
+
+ return 0;
+}
+
+static void hid_haptic_destroy(struct ff_device *ff)
+{
+ struct hid_haptic_device *haptic = ff->private;
+ struct hid_device *hdev = haptic->hdev;
+ int r;
+
+ if (hdev)
+ put_device(&hdev->dev);
+
+ kfree(haptic->stop_effect.report_buf);
+ haptic->stop_effect.report_buf = NULL;
+
+ if (haptic->effect) {
+ for (r = 0; r < ff->max_effects; r++)
+ kfree(haptic->effect[r].report_buf);
+ kfree(haptic->effect);
+ }
+ haptic->effect = NULL;
+
+ destroy_workqueue(haptic->wq);
+ haptic->wq = NULL;
+
+ kfree(haptic->duration_map);
+ haptic->duration_map = NULL;
+
+ kfree(haptic->hid_usage_map);
+ haptic->hid_usage_map = NULL;
+
+ module_put(THIS_MODULE);
+}
+
+int hid_haptic_init(struct hid_device *hdev,
+ struct hid_haptic_device **haptic_ptr)
+{
+ struct hid_haptic_device *haptic = *haptic_ptr;
+ struct input_dev *dev = NULL;
+ struct hid_input *hidinput;
+ struct ff_device *ff;
+ int ret = 0, r;
+ struct ff_haptic_effect stop_effect = {
+ .hid_usage = HID_HP_WAVEFORMSTOP & HID_USAGE,
+ };
+ const char *prefix = "hid-haptic";
+ char *name;
+ int (*flush)(struct input_dev *dev, struct file *file);
+ int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
+
+ haptic->hdev = hdev;
+ haptic->max_waveform_id = max(2u, haptic->max_waveform_id);
+ haptic->max_duration_id = max(2u, haptic->max_duration_id);
+
+ haptic->hid_usage_map = kcalloc(haptic->max_waveform_id + 1,
+ sizeof(u16), GFP_KERNEL);
+ if (!haptic->hid_usage_map) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ haptic->duration_map = kcalloc(haptic->max_duration_id + 1,
+ sizeof(u32), GFP_KERNEL);
+ if (!haptic->duration_map) {
+ ret = -ENOMEM;
+ goto usage_map;
+ }
+
+ if (haptic->max_waveform_id != haptic->max_duration_id)
+ dev_warn(&hdev->dev,
+ "Haptic duration and waveform lists have different max id (%u and %u).\n",
+ haptic->max_duration_id, haptic->max_waveform_id);
+
+ haptic->hid_usage_map[HID_HAPTIC_ORDINAL_WAVEFORMNONE] =
+ HID_HP_WAVEFORMNONE & HID_USAGE;
+ haptic->hid_usage_map[HID_HAPTIC_ORDINAL_WAVEFORMSTOP] =
+ HID_HP_WAVEFORMSTOP & HID_USAGE;
+
+ for (r = 0; r < haptic->auto_trigger_report->maxfield; r++)
+ parse_auto_trigger_field(haptic, haptic->auto_trigger_report->field[r]);
+
+ list_for_each_entry(hidinput, &hdev->inputs, list) {
+ if (hidinput->application == HID_DG_TOUCHPAD) {
+ dev = hidinput->input;
+ break;
+ }
+ }
+
+ if (!dev) {
+ dev_err(&hdev->dev, "Failed to find the input device\n");
+ ret = -ENODEV;
+ goto duration_map;
+ }
+
+ haptic->input_dev = dev;
+ haptic->manual_trigger_report_len =
+ hid_report_len(haptic->manual_trigger_report);
+ mutex_init(&haptic->manual_trigger_mutex);
+ name = kmalloc(strlen(prefix) + strlen(hdev->name) + 2, GFP_KERNEL);
+ if (name) {
+ sprintf(name, "%s %s", prefix, hdev->name);
+ haptic->wq = create_singlethread_workqueue(name);
+ kfree(name);
+ }
+ if (!haptic->wq) {
+ ret = -ENOMEM;
+ goto duration_map;
+ }
+ haptic->effect = kcalloc(FF_MAX_EFFECTS,
+ sizeof(struct hid_haptic_effect), GFP_KERNEL);
+ if (!haptic->effect) {
+ ret = -ENOMEM;
+ goto output_queue;
+ }
+ for (r = 0; r < FF_MAX_EFFECTS; r++) {
+ haptic->effect[r].report_buf =
+ hid_alloc_report_buf(haptic->manual_trigger_report,
+ GFP_KERNEL);
+ if (!haptic->effect[r].report_buf) {
+ dev_err(&hdev->dev,
+ "Failed to allocate a buffer for an effect.\n");
+ ret = -ENOMEM;
+ goto buffer_free;
+ }
+ haptic->effect[r].input_dev = dev;
+ INIT_WORK(&haptic->effect[r].work, haptic_work_handler);
+ }
+ haptic->stop_effect.report_buf =
+ hid_alloc_report_buf(haptic->manual_trigger_report,
+ GFP_KERNEL);
+ if (!haptic->stop_effect.report_buf) {
+ dev_err(&hdev->dev,
+ "Failed to allocate a buffer for stop effect.\n");
+ ret = -ENOMEM;
+ goto buffer_free;
+ }
+ haptic->stop_effect.input_dev = dev;
+ INIT_WORK(&haptic->stop_effect.work, haptic_work_handler);
+ fill_effect_buf(haptic, &stop_effect, &haptic->stop_effect,
+ HID_HAPTIC_ORDINAL_WAVEFORMSTOP);
+
+ input_set_capability(dev, EV_FF, FF_HAPTIC);
+
+ flush = dev->flush;
+ event = dev->event;
+ ret = input_ff_create(dev, FF_MAX_EFFECTS);
+ if (ret) {
+ dev_err(&hdev->dev, "Failed to create ff device.\n");
+ goto stop_buffer_free;
+ }
+
+ ff = dev->ff;
+ ff->private = haptic;
+ ff->upload = hid_haptic_upload_effect;
+ ff->playback = hid_haptic_playback;
+ ff->erase = hid_haptic_erase;
+ ff->destroy = hid_haptic_destroy;
+ if (!try_module_get(THIS_MODULE)) {
+ dev_err(&hdev->dev, "Failed to increase module count.\n");
+ goto input_free;
+ }
+ if (!get_device(&hdev->dev)) {
+ dev_err(&hdev->dev, "Failed to get hdev device.\n");
+ module_put(THIS_MODULE);
+ goto input_free;
+ }
+ return 0;
+
+input_free:
+ input_ff_destroy(dev);
+ /* Do not let double free happen, input_ff_destroy will call
+ * hid_haptic_destroy.
+ */
+ *haptic_ptr = NULL;
+ /* Restore dev flush and event */
+ dev->flush = flush;
+ dev->event = event;
+ return ret;
+stop_buffer_free:
+ kfree(haptic->stop_effect.report_buf);
+ haptic->stop_effect.report_buf = NULL;
+buffer_free:
+ while (--r >= 0)
+ kfree(haptic->effect[r].report_buf);
+ kfree(haptic->effect);
+ haptic->effect = NULL;
+output_queue:
+ destroy_workqueue(haptic->wq);
+ haptic->wq = NULL;
+duration_map:
+ kfree(haptic->duration_map);
+ haptic->duration_map = NULL;
+usage_map:
+ kfree(haptic->hid_usage_map);
+ haptic->hid_usage_map = NULL;
+exit:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(hid_haptic_init);
diff --git a/drivers/hid/hid-haptic.h b/drivers/hid/hid-haptic.h
index 2e89addf5ec280d5b9a59d06088cc08bd4f445c1..25e0e2cef9399199796d8679d66209381b6a59f2 100644
--- a/drivers/hid/hid-haptic.h
+++ b/drivers/hid/hid-haptic.h
@@ -69,6 +69,7 @@ int hid_haptic_input_mapping(struct hid_device *hdev,
int hid_haptic_input_configured(struct hid_device *hdev,
struct hid_haptic_device *haptic,
struct hid_input *hi);
+int hid_haptic_init(struct hid_device *hdev, struct hid_haptic_device **haptic_ptr);
#else
static inline
void hid_haptic_feature_mapping(struct hid_device *hdev,
@@ -98,4 +99,8 @@ int hid_haptic_input_configured(struct hid_device *hdev,
{
return 0;
}
+int hid_haptic_init(struct hid_device *hdev, struct hid_haptic_device **haptic_ptr)
+{
+ return 0;
+}
#endif
--
2.51.0.rc1.193.gad69d77794-goog
^ permalink raw reply related
* [PATCH v3 05/11] HID: input: allow mapping of haptic output
From: Jonathan Denose @ 2025-08-18 23:08 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, Dmitry Torokhov, Jonathan Corbet,
Henrik Rydberg
Cc: linux-input, linux-kernel, linux-doc, Angela Czubak,
Sean O'Brien, Jonathan Denose
In-Reply-To: <20250818-support-forcepads-v3-0-e4f9ab0add84@google.com>
From: Angela Czubak <aczubak@google.com>
This change makes it possible to parse output reports by input mapping
functions by HID drivers.
Signed-off-by: Angela Czubak <aczubak@google.com>
Co-developed-by: Jonathan Denose <jdenose@google.com>
Signed-off-by: Jonathan Denose <jdenose@google.com>
---
drivers/hid/hid-input.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 9d80635a91ebd8d8bdafaac07b5f85693b179cb4..d42c1fbd20a1cc01c04f93cf10f1d1c18043929c 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -682,9 +682,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
if (field->report_count < 1)
goto ignore;
- /* only LED usages are supported in output fields */
+ /* only LED and HAPTIC usages are supported in output fields */
if (field->report_type == HID_OUTPUT_REPORT &&
- (usage->hid & HID_USAGE_PAGE) != HID_UP_LED) {
+ (usage->hid & HID_USAGE_PAGE) != HID_UP_LED &&
+ (usage->hid & HID_USAGE_PAGE) != HID_UP_HAPTIC) {
goto ignore;
}
--
2.51.0.rc1.193.gad69d77794-goog
^ permalink raw reply related
* [PATCH v3 04/11] HID: haptic: introduce hid_haptic_device
From: Jonathan Denose @ 2025-08-18 23:08 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, Dmitry Torokhov, Jonathan Corbet,
Henrik Rydberg
Cc: linux-input, linux-kernel, linux-doc, Angela Czubak,
Sean O'Brien, Jonathan Denose
In-Reply-To: <20250818-support-forcepads-v3-0-e4f9ab0add84@google.com>
From: Angela Czubak <aczubak@google.com>
Define a new structure that contains simple haptic device configuration
as well as current state.
Add functions that recognize auto trigger and manual trigger reports
as well as save their addresses.
Verify that the pressure unit is either grams or newtons.
Mark the input device as a haptic touchpad if the unit is correct and
the reports are found.
Signed-off-by: Angela Czubak <aczubak@google.com>
Co-developed-by: Jonathan Denose <jdenose@google.com>
Signed-off-by: Jonathan Denose <jdenose@google.com>
---
drivers/hid/Kconfig | 11 ++++++
drivers/hid/Makefile | 1 +
drivers/hid/hid-haptic.c | 72 +++++++++++++++++++++++++++++++++
drivers/hid/hid-haptic.h | 101 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 185 insertions(+)
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 43859fc757470caf6ad43bd5f72f119e9c36aea7..fa0a53287c0ed6b5853d0e80641df341fb728332 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -92,6 +92,17 @@ config HID_GENERIC
If unsure, say Y.
+config HID_HAPTIC
+ tristate "Haptic touchpad support"
+ default n
+ help
+ Support for touchpads with force sensors and haptic actuators instead of a
+ traditional button.
+ Adds extra parsing and FF device for the hid multitouch driver.
+ It can be used for Elan 2703 haptic touchpad.
+
+ If unsure, say N.
+
menu "Special HID drivers"
config HID_A4TECH
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 10ae5dedbd84708d988ea1f594d409ccebd85ebb..361a7daedeb85454114def8afb5f58caeab58a00 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -4,6 +4,7 @@
#
hid-y := hid-core.o hid-input.o hid-quirks.o
hid-$(CONFIG_DEBUG_FS) += hid-debug.o
+hid-$(CONFIG_HID_HAPTIC) += hid-haptic.o
obj-$(CONFIG_HID_BPF) += bpf/
diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c
new file mode 100644
index 0000000000000000000000000000000000000000..d659a430c1a6b06ded31d49efe4bded909671cb6
--- /dev/null
+++ b/drivers/hid/hid-haptic.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HID Haptic support for Linux
+ *
+ * Copyright (c) 2021 Angela Czubak <acz@semihalf.com>
+ */
+
+#include "hid-haptic.h"
+
+void hid_haptic_feature_mapping(struct hid_device *hdev,
+ struct hid_haptic_device *haptic,
+ struct hid_field *field, struct hid_usage *usage)
+{
+ if (usage->hid == HID_HP_AUTOTRIGGER) {
+ if (usage->usage_index >= field->report_count) {
+ dev_err(&hdev->dev,
+ "HID_HP_AUTOTRIGGER out of range\n");
+ return;
+ }
+
+ hid_device_io_start(hdev);
+ hid_hw_request(hdev, field->report, HID_REQ_GET_REPORT);
+ hid_hw_wait(hdev);
+ hid_device_io_stop(hdev);
+ haptic->default_auto_trigger =
+ field->value[usage->usage_index];
+ haptic->auto_trigger_report = field->report;
+ }
+}
+EXPORT_SYMBOL_GPL(hid_haptic_feature_mapping);
+
+bool hid_haptic_check_pressure_unit(struct hid_haptic_device *haptic,
+ struct hid_input *hi, struct hid_field *field)
+{
+ if (field->unit == HID_UNIT_GRAM || field->unit == HID_UNIT_NEWTON)
+ return true;
+ return false;
+}
+EXPORT_SYMBOL_GPL(hid_haptic_check_pressure_unit);
+
+int hid_haptic_input_mapping(struct hid_device *hdev,
+ struct hid_haptic_device *haptic,
+ struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ if (usage->hid == HID_HP_MANUALTRIGGER) {
+ haptic->manual_trigger_report = field->report;
+ /* we don't really want to map these fields */
+ return -1;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hid_haptic_input_mapping);
+
+int hid_haptic_input_configured(struct hid_device *hdev,
+ struct hid_haptic_device *haptic,
+ struct hid_input *hi)
+{
+
+ if (hi->application == HID_DG_TOUCHPAD) {
+ if (haptic->auto_trigger_report &&
+ haptic->manual_trigger_report) {
+ __set_bit(INPUT_PROP_HAPTIC_TOUCHPAD, hi->input->propbit);
+ return 1;
+ }
+ return 0;
+ }
+ return -1;
+}
+EXPORT_SYMBOL_GPL(hid_haptic_input_configured);
diff --git a/drivers/hid/hid-haptic.h b/drivers/hid/hid-haptic.h
new file mode 100644
index 0000000000000000000000000000000000000000..2e89addf5ec280d5b9a59d06088cc08bd4f445c1
--- /dev/null
+++ b/drivers/hid/hid-haptic.h
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * HID Haptic support for Linux
+ *
+ * Copyright (c) 2021 Angela Czubak <acz@semihalf.com>
+ */
+
+#include <linux/hid.h>
+
+#define HID_HAPTIC_ORDINAL_WAVEFORMNONE 1
+#define HID_HAPTIC_ORDINAL_WAVEFORMSTOP 2
+
+#define HID_HAPTIC_MODE_DEVICE 0
+#define HID_HAPTIC_MODE_HOST 1
+
+struct hid_haptic_effect {
+ u8 *report_buf;
+ struct input_dev *input_dev;
+ struct work_struct work;
+ struct list_head control;
+ struct mutex control_mutex;
+};
+
+struct hid_haptic_effect_node {
+ struct list_head node;
+ struct file *file;
+};
+
+struct hid_haptic_device {
+ struct input_dev *input_dev;
+ struct hid_device *hdev;
+ struct hid_report *auto_trigger_report;
+ struct mutex auto_trigger_mutex;
+ struct workqueue_struct *wq;
+ struct hid_report *manual_trigger_report;
+ struct mutex manual_trigger_mutex;
+ size_t manual_trigger_report_len;
+ int pressed_state;
+ s32 pressure_sum;
+ s32 force_logical_minimum;
+ s32 force_physical_minimum;
+ s32 force_resolution;
+ u32 mode;
+ u32 default_auto_trigger;
+ u32 vendor_page;
+ u32 vendor_id;
+ u32 max_waveform_id;
+ u32 max_duration_id;
+ u16 *hid_usage_map;
+ u32 *duration_map;
+ u16 press_ordinal;
+ u16 release_ordinal;
+ struct hid_haptic_effect *effect;
+ struct hid_haptic_effect stop_effect;
+};
+
+#if IS_ENABLED(CONFIG_HID_HAPTIC)
+void hid_haptic_feature_mapping(struct hid_device *hdev,
+ struct hid_haptic_device *haptic,
+ struct hid_field *field, struct hid_usage
+ *usage);
+bool hid_haptic_check_pressure_unit(struct hid_haptic_device *haptic,
+ struct hid_input *hi, struct hid_field *field);
+int hid_haptic_input_mapping(struct hid_device *hdev,
+ struct hid_haptic_device *haptic,
+ struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max);
+int hid_haptic_input_configured(struct hid_device *hdev,
+ struct hid_haptic_device *haptic,
+ struct hid_input *hi);
+#else
+static inline
+void hid_haptic_feature_mapping(struct hid_device *hdev,
+ struct hid_haptic_device *haptic,
+ struct hid_field *field, struct hid_usage
+ *usage)
+{}
+static inline
+bool hid_haptic_check_pressure_unit(struct hid_haptic_device *haptic,
+ struct hid_input *hi, struct hid_field *field)
+{
+ return false;
+}
+static inline
+int hid_haptic_input_mapping(struct hid_device *hdev,
+ struct hid_haptic_device *haptic,
+ struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ return 0;
+}
+static inline
+int hid_haptic_input_configured(struct hid_device *hdev,
+ struct hid_haptic_device *haptic,
+ struct hid_input *hi)
+{
+ return 0;
+}
+#endif
--
2.51.0.rc1.193.gad69d77794-goog
^ permalink raw reply related
* [PATCH v3 01/11] HID: add haptics page defines
From: Jonathan Denose @ 2025-08-18 23:08 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, Dmitry Torokhov, Jonathan Corbet,
Henrik Rydberg
Cc: linux-input, linux-kernel, linux-doc, Angela Czubak,
Sean O'Brien, Jonathan Denose
In-Reply-To: <20250818-support-forcepads-v3-0-e4f9ab0add84@google.com>
From: Angela Czubak <aczubak@google.com>
Introduce haptic usages as defined in HID Usage Tables specification.
Add HID units for newton and gram.
Signed-off-by: Angela Czubak <aczubak@google.com>
Co-developed-by: Jonathan Denose <jdenose@google.com>
Signed-off-by: Jonathan Denose <jdenose@google.com>
---
include/linux/hid.h | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 568a9d8c749bc5547ff78d5abe6db7bce2f62d2b..344ab0e40f29afe55575e5a7544496b7bb48a266 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -156,6 +156,7 @@ struct hid_item {
#define HID_UP_TELEPHONY 0x000b0000
#define HID_UP_CONSUMER 0x000c0000
#define HID_UP_DIGITIZER 0x000d0000
+#define HID_UP_HAPTIC 0x000e0000
#define HID_UP_PID 0x000f0000
#define HID_UP_BATTERY 0x00850000
#define HID_UP_CAMERA 0x00900000
@@ -316,6 +317,28 @@ struct hid_item {
#define HID_DG_TOOLSERIALNUMBER 0x000d005b
#define HID_DG_LATENCYMODE 0x000d0060
+#define HID_HP_SIMPLECONTROLLER 0x000e0001
+#define HID_HP_WAVEFORMLIST 0x000e0010
+#define HID_HP_DURATIONLIST 0x000e0011
+#define HID_HP_AUTOTRIGGER 0x000e0020
+#define HID_HP_MANUALTRIGGER 0x000e0021
+#define HID_HP_AUTOTRIGGERASSOCIATEDCONTROL 0x000e0022
+#define HID_HP_INTENSITY 0x000e0023
+#define HID_HP_REPEATCOUNT 0x000e0024
+#define HID_HP_RETRIGGERPERIOD 0x000e0025
+#define HID_HP_WAVEFORMVENDORPAGE 0x000e0026
+#define HID_HP_WAVEFORMVENDORID 0x000e0027
+#define HID_HP_WAVEFORMCUTOFFTIME 0x000e0028
+#define HID_HP_WAVEFORMNONE 0x000e1001
+#define HID_HP_WAVEFORMSTOP 0x000e1002
+#define HID_HP_WAVEFORMCLICK 0x000e1003
+#define HID_HP_WAVEFORMBUZZCONTINUOUS 0x000e1004
+#define HID_HP_WAVEFORMRUMBLECONTINUOUS 0x000e1005
+#define HID_HP_WAVEFORMPRESS 0x000e1006
+#define HID_HP_WAVEFORMRELEASE 0x000e1007
+#define HID_HP_VENDORWAVEFORMMIN 0x000e2001
+#define HID_HP_VENDORWAVEFORMMAX 0x000e2fff
+
#define HID_BAT_ABSOLUTESTATEOFCHARGE 0x00850065
#define HID_BAT_CHARGING 0x00850044
@@ -423,6 +446,12 @@ struct hid_item {
#define HID_REPORT_PROTOCOL 1
#define HID_BOOT_PROTOCOL 0
+/*
+ * HID units
+ */
+#define HID_UNIT_GRAM 0x0101
+#define HID_UNIT_NEWTON 0xe111
+
/*
* This is the global environment of the parser. This information is
* persistent for main-items. The global environment can be saved and
--
2.51.0.rc1.193.gad69d77794-goog
^ permalink raw reply related
* [PATCH v3 00/11] HID: Implement haptic touchpad support
From: Jonathan Denose @ 2025-08-18 23:08 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, Dmitry Torokhov, Jonathan Corbet,
Henrik Rydberg
Cc: linux-input, linux-kernel, linux-doc, Angela Czubak,
Sean O'Brien, Jonathan Denose
Hello,
This is an updated implementation of the interface for controlling haptic
touchpads.
Below is an updated design proposal for the userspace and HID interfaces,
modified from what one of my colleagues submitted in 2019 [0].
We would appreciate any feedback you might have.
Thank you,
Jonathan Denose
Chromium OS Team
Background
==========
There are multiple independent projects to develop a touchpad with force sensors
and haptic actuators, instead of a traditional button. These haptic touchpads
have several advantages and potential uses; they allow clicking across the
entire touchpad surface, adjusting the force requirement for clicks, haptic
feedback initiated by UI, etc. Supporting these features will potentially
require two new communication channels at the kernel level:
* Control of haptic motor by the host
* Force sensor data from device to host
This document includes two related proposals:
1. HID design proposal, that hardware makers would need to implement
2. Kernel design proposal
Objective
==========
Develop a standard protocol to allow userspace applications to communicate with
haptic touchpads, and minimize duplicated code and effort.
Requirements:
1. Support UI-initiated haptic feedback.
2. Allow userspace to control when button press and button release haptic
effects are triggered. (Useful when detecting a false click, changing force
thresholds, or sending context-dependent effects).
3. Reveal force sensor readings to userspace applications.
4. Only allow OS-controlled haptic feedback for those systems which support it.
Proposal
========
In order to minimize duplicated effort, we propose standardized haptic touchpad
support in the linux kernel.
HID API
-------
Modes
.....
The haptic touchpad should be able to operate under two different modes.
1. Device-controlled mode
The haptic touchpad should start up in "device-controlled mode"
(HID_HAPTIC_MODE_DEVICE), meaning it acts as a normal touchpad. This means it
should perform the press and release haptic feedback autonomously at predefined
force thresholds, and send the appropriate BTN_* events.
2. Host-controlled mode
Once the touchpad has been confirmed as supporting haptics (described in more
detail in the the "Click and release control" section below), the device should
enter "host-controlled mode" (HID_HAPTIC_MODE_HOST). In this mode userspace
should take control. From here, userspace will take control over
press/release haptic feedback, relying on the effects sent by the kernel.
Multitouch
..........
The HID API for multitouch reports should follow the Microsoft precision
touchpad spec [1], with the following changes:
* A tip pressure field [2] should be used to report the force. The physical unit
Type (Newtons or grams), exponent, and limits should be reported in the
report descriptor for the force field.
* The device will always report the button state according to its predefined
force thresholds, even when not in device-controlled mode.
* The device must expose a "simple haptic controller" logical collection
alongside the touchpad collection.
Haptic control
..............
The HID protocol described in HUTRR63[3] must be used.
The following waveforms should be supported:
| WAVEFORMNONE | Implicit waveforms required by protocol |
| WAVEFORMSTOP | |
| ------------------------ | ------------------------------------------------- |
| WAVEFORMPRESS | To be used to simulate button press. In device- |
| | controlled mode, it will also be used to simulate |
| | button release. |
| ------------------------ | ------------------------------------------------- |
| WAVEFORMRELEASE | To be used to simulate button release. |
All waveforms will have an associated duration; continuous waveforms will be
ignored by the kernel.
Triggers & Mode switching
.........................
The “auto trigger waveform” should be set to WAVEFORM_PRESS by default, and the
button from the touchpad collection should be set as the “auto trigger
associated control”.
The kernel can trigger the different modes in the following ways:
* Device-controlled mode can be enabled by setting the “auto trigger waveform” to
WAVEFORM_PRESS.
* Host-controlled mode can be enabled by setting the "auto trigger waveform" to
WAVEFORM_STOP.
The device must also support manual triggering. If intensity modification for
waveforms is supported by the device, the intensity control should be included
in the manual trigger output report. This allows modification of the intensity
on a per-waveform basis. Retriggering does not need to be supported by the
device.
Userspace API
-------------
Multitouch protocol
...................
ABS_MT_PRESSURE will be used to report force. The resolution of ABS_MT_PRESSURE
should also be defined and reported in force units of grams or Newtons.
ABS_PRESSURE should be reported as the total force applied to the touchpad.
When the kernel is in host-controlled mode, it should always forward the button
press and release events to userspace.
Use Force Feedback protocol to request pre-defined effects
..........................................................
The force feedback protocol [4] should be used to control predefined effects.
Typical use of the force feedback protocol requires loading effects to the
driver by describing the output waveform, and then requesting those effects
using an ID provided by the driver. However, for haptic touchpads we do not want
to describe the output waveform explicitly, but use a set of predefined effects,
which are identified by HID usage.
The force feedback protocol will need to be extended to allow requests for HID
haptic effects. This requires a new feedback effect type:
/**
* struct ff_haptic_effect
* @hid_usage: hid_usage according to Haptics page (WAVEFORM_CLICK, etc.)
* @vendor_id: the waveform vendor ID if hid_usage is in the vendor-defined
* range
* @vendor_id: the vendor waveform page if hid_usage is in the vendor-defined
* range
* @intensity: strength of the effect
* @repeat_count: number of times to retrigger effect
* @retrigger_period: time before effect is retriggered (in ms)
*/
struct ff_haptic_effect {
__u16 hid_usage;
__u16 vendor_id;
__u8 vendor_waveform_page;
__s16 intensity;
__u16 repeat_count;
__u16 retrigger_period;
}
Since the standard waveform id namespace does not overlap with the vendor
waveform id namespace, the vendor id and page can be ignored for standard
waveforms.
Click and release control
.........................
Haptic functionality shall be gated behind the HID_MULTITOUCH_HAPTIC kernel
configuration option, and this kernel configuration option should only be
enabled if userspace will support haptic capabilities. Haptic functionality will
only be initialized and used if HID_MULTITOUCH_HAPTIC is enabled, and if the
following conditions have been met:
* ABS_MT_PRESSURE is defined and reporting force units of Newtons or grams.
* The device supports haptic effects according to the hid protocol defined in
HUTRR63 [3].
These checks will happen when the driver probes and initializes the multitouch
device.
In the case when the kernel configuration option has been set and the device
reports pressure and haptic effects as defined above, the kernel will initialize
the haptic device and configure the haptic driver to signal that the touchpad is
haptic-compatible. To signal to userspace that the touchpad is haptic-compatible
the kernel will mark INPUT_PROP_HAPTIC_TOUCHPAD.
With userspace willing and able to take control, the kernel will signal to the
device to exit device-controlled mode once a WAVEFORMPRESS or WAVEFORMRELEASE
event is uploaded. From here, userspace will take control over press/release
haptic feedback, relying on the effects sent by the kernel.
In all other cases, the driver will take no action to enable haptic
functionality.
Summary of normal use-case
1. The kernel waits for userspace to upload WAVEFORMPRESS or
WAVEFORMRELEASE.
2. Userspace determines when a click has been performed based on its own
criteria and tells the touchpad to perform a haptic effect.
3. When userspace erases the WAVEFORMPRESS or WAVEFORMRELEASE effect, signal the
device to return to device-controlled mode.
[0]: https://www.spinics.net/lists/linux-input/msg60938.html
[1]: https://learn.microsoft.com/en-us/windows-hardware/design/component-guidelines/touchpad-devices
[2]: Usage ID 0x30 of HID usage table 0x0D. See chapter 16:
https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf
[3]: https://www.usb.org/sites/default/files/hutrr63b_-_haptics_page_redline_0.pdf
[4]: https://www.kernel.org/doc/html/v4.20/input/ff.html
Signed-off-by: Jonathan Denose <jdenose@google.com>
---
Changes in v3:
- Change CONFIG_HID_HAPTIC from bool to tristate
- Fix devm_kzalloc calls in hid-multitouch.c to use correct device
- Minor comment cleanup + grammar fix
- Link to v2: https://lore.kernel.org/r/20250818-support-forcepads-v2-0-ca2546e319d5@google.com
Changes in v2:
- Rename FF_HID and ff_hid_effect to FF_HAPTIC and ff_haptic_effect
- Add more detail to CONFIG_HID_HAPTIC config option description
- Remove CONFIG_MULTITOUCH_HAPTIC config option
- Utilize devm api in hid-multitouch haptic functions
- Link to v1: https://lore.kernel.org/all/20250714-support-forcepads-v1-0-71c7c05748c9@google.com
---
Angela Czubak (11):
HID: add haptics page defines
Input: add FF_HAPTIC effect type
Input: add INPUT_PROP_HAPTIC_TOUCHPAD
HID: haptic: introduce hid_haptic_device
HID: input: allow mapping of haptic output
HID: haptic: initialize haptic device
HID: input: calculate resolution for pressure
HID: haptic: add functions handling events
Input: MT - add INPUT_MT_TOTAL_FORCE flags
HID: haptic: add hid_haptic_switch_mode
HID: multitouch: add haptic multitouch support
Documentation/input/event-codes.rst | 14 +
drivers/hid/Kconfig | 11 +
drivers/hid/Makefile | 1 +
drivers/hid/hid-haptic.c | 580 +++++++++++++++++++++++++++++++++
drivers/hid/hid-haptic.h | 127 ++++++++
drivers/hid/hid-input.c | 18 +-
drivers/hid/hid-multitouch.c | 47 +++
drivers/input/input-mt.c | 14 +-
include/linux/hid.h | 29 ++
include/linux/input/mt.h | 1 +
include/uapi/linux/input-event-codes.h | 1 +
include/uapi/linux/input.h | 22 +-
12 files changed, 858 insertions(+), 7 deletions(-)
---
base-commit: 86731a2a651e58953fc949573895f2fa6d456841
change-id: 20250625-support-forcepads-0b4f74fd3d0a
Best regards,
--
Jonathan Denose <jdenose@google.com>
^ permalink raw reply
* Re: [PATCH v2 04/11] HID: haptic: introduce hid_haptic_device
From: Jonathan Denose @ 2025-08-18 22:17 UTC (permalink / raw)
To: Randy Dunlap
Cc: Jiri Kosina, Benjamin Tissoires, Dmitry Torokhov, Jonathan Corbet,
Henrik Rydberg, linux-input, linux-kernel, linux-doc,
Angela Czubak, Sean O'Brien
In-Reply-To: <67156238-7915-4725-a030-d0b422a2aedc@infradead.org>
On Mon, Aug 18, 2025 at 11:36 AM Randy Dunlap <rdunlap@infradead.org> wrote:
>
> Hi,
>
> On 8/18/25 7:28 AM, Jonathan Denose wrote:
> > From: Angela Czubak <aczubak@google.com>
> >
> > Define a new structure that contains simple haptic device configuration
> > as well as current state.
> > Add functions that recognize auto trigger and manual trigger reports
> > as well as save their addresses.
> > Verify that the pressure unit is either grams or newtons.
> > Mark the input device as a haptic touchpad if the unit is correct and
> > the reports are found.
> >
> > Signed-off-by: Angela Czubak <aczubak@google.com>
> > Co-developed-by: Jonathan Denose <jdenose@google.com>
> > Signed-off-by: Jonathan Denose <jdenose@google.com>
> > ---
> > drivers/hid/Kconfig | 11 +++++
> > drivers/hid/Makefile | 1 +
> > drivers/hid/hid-haptic.c | 72 ++++++++++++++++++++++++++++++++
> > drivers/hid/hid-haptic.h | 105 +++++++++++++++++++++++++++++++++++++++++++++++
> > 4 files changed, 189 insertions(+)
> >
> > diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> > index 43859fc757470caf6ad43bd5f72f119e9c36aea7..cbbe82a0a2ba257e45f77ca014fb5f08b71fc62f 100644
> > --- a/drivers/hid/Kconfig
> > +++ b/drivers/hid/Kconfig
> > @@ -92,6 +92,17 @@ config HID_GENERIC
> >
> > If unsure, say Y.
> >
> > +config HID_HAPTIC
> > + bool "Haptic touchpad support"
>
> Why bool instead of tristate?
No particular reason, I can change it to tristate.
>
> > + default n
> > + help
> > + Support for touchpads with force sensors and haptic actuators instead of a
> > + traditional button.
> > + Adds extra parsing and FF device for the hid multitouch driver.
> > + It can be used for Elan 2703 haptic touchpad.
> > +
> > + If unsure, say N.
> > +
>
> I do wish that hid/Kconfig indentation didn't vary so much from coding-style.rst.
>
> > menu "Special HID drivers"
> >
> > config HID_A4TECH
>
>
>
> > diff --git a/drivers/hid/hid-haptic.h b/drivers/hid/hid-haptic.h
> > new file mode 100644
> > index 0000000000000000000000000000000000000000..b729f8245aa60c3d06b79b11846dccf6fcc0c08b
> > --- /dev/null
> > +++ b/drivers/hid/hid-haptic.h
> > @@ -0,0 +1,105 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
> > +/*
> > + * HID Haptic support for Linux
> > + *
> > + * Copyright (c) 2021 Angela Czubak <acz@semihalf.com>
> > + */
> > +
> > +/*
> > + */
> eh?
I can clean this up as well.
>
> > +
> > +
> > +#include <linux/hid.h>
> ?
>
> --
> ~Randy
>
I'll also fix the grammatical error you noted on the patch before this
one and resubmit. Thanks for your review!
--
Jonathan
^ permalink raw reply
* Re: [PATCH v3 2/2] HID: Make elan touch controllers power on after panel is enabled
From: Doug Anderson @ 2025-08-18 20:14 UTC (permalink / raw)
To: Pin-yen Lin
Cc: Neil Armstrong, Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jiri Kosina,
Benjamin Tissoires, linux-kernel, linux-input, Chen-Yu Tsai,
dri-devel
In-Reply-To: <20250818115015.2909525-2-treapking@chromium.org>
Hi,
On Mon, Aug 18, 2025 at 4:50 AM Pin-yen Lin <treapking@chromium.org> wrote:
>
> Introduce a new HID quirk to indicate that this device has to be enabled
> after the panel's backlight is enabled, and update the driver data for
> the elan devices to enable this quirk. This cannot be a I2C HID quirk
> because the kernel needs to acknowledge this before powering up the
> device and read the VID/PID. When this quirk is enabled, register
> .panel_enabled()/.panel_disabling() instead for the panel follower.
>
> Also rename the *panel_prepare* functions into *panel_follower* because
> they could be called in other situations now.
>
> Fixes: bd3cba00dcc63 ("HID: i2c-hid: elan: Add support for Elan eKTH6915 i2c-hid touchscreens")
> Fixes: d06651bebf99e ("HID: i2c-hid: elan: Add elan-ekth6a12nay timing")
>
> Reviewed-by: Douglas Anderson <dianders@chromium.org>
> Signed-off-by: Pin-yen Lin <treapking@chromium.org>
>
> ---
>
> Changes in v3:
> - Collect review tag
> - Add fixes tags
>
> Changes in v2:
> - Rename *panel_prepare* functions to *panel_follower*
> - Replace after_panel_enabled flag with enabled/disabling callbacks
>
> drivers/hid/i2c-hid/i2c-hid-core.c | 46 ++++++++++++++++-----------
> drivers/hid/i2c-hid/i2c-hid-of-elan.c | 11 ++++++-
> include/linux/hid.h | 2 ++
> 3 files changed, 40 insertions(+), 19 deletions(-)
Re-iterating my response from v2 [1] so it's still seen even if people
only look at the latest version. :-) If HID folks don't mind us
landing this through drm-misc, feel free to Ack this patch. If HID
folks would rather not land through drm-misc, the default plan would
be to just wait until patch #1 makes its way to mainline before
landing patch #2.
Thanks!
[1] https://lore.kernel.org/r/CAD=FV=UV8_XGmxC=7Z18PEnj6wKz+yZQuV_4h+LJh_MNCqszvg@mail.gmail.com/
^ permalink raw reply
* Re: [PATCH v3 1/2] drm/panel: Allow powering on panel follower after panel is enabled
From: Doug Anderson @ 2025-08-18 20:11 UTC (permalink / raw)
To: Pin-yen Lin
Cc: Neil Armstrong, Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jiri Kosina,
Benjamin Tissoires, linux-kernel, linux-input, Chen-Yu Tsai,
dri-devel
In-Reply-To: <20250818115015.2909525-1-treapking@chromium.org>
Hi,
On Mon, Aug 18, 2025 at 4:50 AM Pin-yen Lin <treapking@chromium.org> wrote:
>
> Some touch controllers have to be powered on after the panel's backlight
> is enabled. To support these controllers, introduce .panel_enabled() and
> .panel_disabling() to panel_follower_funcs and use them to power on the
> device after the panel and its backlight are enabled.
>
> Signed-off-by: Pin-yen Lin <treapking@chromium.org>
>
> ---
>
> Changes in v3:
> - Update kernel-docs of drm_panel_add_follower() and drm_panel_remove_follower()
> - Fix the order of calling .panel_disabling() and .panel_unpreparing()
> - Add a blank line before the goto label
>
> Changes in v2:
> - Replace after_panel_enabled flag with enabled/disabling callbacks
>
> drivers/gpu/drm/drm_panel.c | 73 +++++++++++++++++++++++++++++++------
> include/drm/drm_panel.h | 14 +++++++
> 2 files changed, 76 insertions(+), 11 deletions(-)
Looks good to me now.
Reviewed-by: Douglas Anderson <dianders@chromium.org>
If there are no objections, I'll plan to apply patch #1 next week to
give people a little time to speak up. As per discussion in v2 [1],
unless we hear back an "Ack" from HID maintainers then patch #2 will
just need to wait a while before it can land in the HID tree.
Question for Jessica / Neil: what do you think about landing
${SUBJECT} patch in drm-misc-fixes instead of drm-misc-next? This is a
dependency for the next patch which is marked as a "Fix". It'll mean
that the patch can make it into mainline faster so the HID patch could
land faster. The patch is also pretty low risk...
[1] https://lore.kernel.org/r/CAD=FV=UV8_XGmxC=7Z18PEnj6wKz+yZQuV_4h+LJh_MNCqszvg@mail.gmail.com/
-Doug
^ permalink raw reply
* Re: [RESEND v2 1/1] dt-bindings: input: convert lpc32xx-key.txt to yaml format
From: Vladimir Zapolskiy @ 2025-08-18 20:02 UTC (permalink / raw)
To: Frank Li, Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Piotr Wojtaszczyk,
open list:INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN)...,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
moderated list:ARM/LPC32XX SOC SUPPORT, open list
Cc: imx
In-Reply-To: <20250818183814.3603308-1-Frank.Li@nxp.com>
On 8/18/25 21:38, Frank Li wrote:
> Convert lpc32xx-key.txt to yaml format.
>
> Additional changes:
> - set maximum of key-row(column) to 4.
> - add ref to matrix-keymap.yaml.
>
> Reviewed-by: Vladimir Zapolskiy <vz@mleia.com>
> Signed-off-by: Frank Li <Frank.Li@nxp.com>
FWIW I'm fine, if this change enters over linux-input, otherwise I'll send it
in a PR to linux-arm-kernel/for-next.
--
Best wishes,
Vladimir
^ permalink raw reply
* [RESEND v2 1/1] dt-bindings: input: convert lpc32xx-key.txt to yaml format
From: Frank Li @ 2025-08-18 18:38 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Vladimir Zapolskiy, Piotr Wojtaszczyk,
open list:INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN)...,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
moderated list:ARM/LPC32XX SOC SUPPORT, open list
Cc: imx
Convert lpc32xx-key.txt to yaml format.
Additional changes:
- set maximum of key-row(column) to 4.
- add ref to matrix-keymap.yaml.
Reviewed-by: Vladimir Zapolskiy <vz@mleia.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v2
- add ref to matrix-keymap.yaml.
- remove properties, which already defined in matrix-keymap.yaml.
- Add Vladimir Zapolskiy review tag
---
.../devicetree/bindings/input/lpc32xx-key.txt | 34 -----------
.../bindings/input/nxp,lpc3220-key.yaml | 61 +++++++++++++++++++
2 files changed, 61 insertions(+), 34 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/input/lpc32xx-key.txt
create mode 100644 Documentation/devicetree/bindings/input/nxp,lpc3220-key.yaml
diff --git a/Documentation/devicetree/bindings/input/lpc32xx-key.txt b/Documentation/devicetree/bindings/input/lpc32xx-key.txt
deleted file mode 100644
index 2b075a080d303..0000000000000
--- a/Documentation/devicetree/bindings/input/lpc32xx-key.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-NXP LPC32xx Key Scan Interface
-
-This binding is based on the matrix-keymap binding with the following
-changes:
-
-Required Properties:
-- compatible: Should be "nxp,lpc3220-key"
-- reg: Physical base address of the controller and length of memory mapped
- region.
-- interrupts: The interrupt number to the cpu.
-- clocks: phandle to clock controller plus clock-specifier pair
-- nxp,debounce-delay-ms: Debounce delay in ms
-- nxp,scan-delay-ms: Repeated scan period in ms
-- linux,keymap: the key-code to be reported when the key is pressed
- and released, see also
- Documentation/devicetree/bindings/input/matrix-keymap.txt
-
-Note: keypad,num-rows and keypad,num-columns are required, and must be equal
-since LPC32xx only supports square matrices
-
-Example:
-
- key@40050000 {
- compatible = "nxp,lpc3220-key";
- reg = <0x40050000 0x1000>;
- clocks = <&clk LPC32XX_CLK_KEY>;
- interrupt-parent = <&sic1>;
- interrupts = <22 IRQ_TYPE_LEVEL_HIGH>;
- keypad,num-rows = <1>;
- keypad,num-columns = <1>;
- nxp,debounce-delay-ms = <3>;
- nxp,scan-delay-ms = <34>;
- linux,keymap = <0x00000002>;
- };
diff --git a/Documentation/devicetree/bindings/input/nxp,lpc3220-key.yaml b/Documentation/devicetree/bindings/input/nxp,lpc3220-key.yaml
new file mode 100644
index 0000000000000..9e0d977bdf5cd
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/nxp,lpc3220-key.yaml
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/nxp,lpc3220-key.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP LPC32xx Key Scan Interface
+
+maintainers:
+ - Frank Li <Frank.Li@nxp.com>
+
+properties:
+ compatible:
+ const: nxp,lpc3220-key
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ nxp,debounce-delay-ms:
+ description: Debounce delay in ms
+
+ nxp,scan-delay-ms:
+ description: Repeated scan period in ms
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - nxp,debounce-delay-ms
+ - nxp,scan-delay-ms
+ - linux,keymap
+
+allOf:
+ - $ref: matrix-keymap.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/clock/lpc32xx-clock.h>
+
+ key@40050000 {
+ compatible = "nxp,lpc3220-key";
+ reg = <0x40050000 0x1000>;
+ clocks = <&clk LPC32XX_CLK_KEY>;
+ interrupt-parent = <&sic1>;
+ interrupts = <22 IRQ_TYPE_LEVEL_HIGH>;
+ keypad,num-rows = <1>;
+ keypad,num-columns = <1>;
+ nxp,debounce-delay-ms = <3>;
+ nxp,scan-delay-ms = <34>;
+ linux,keymap = <0x00000002>;
+ };
--
2.34.1
^ permalink raw reply related
* Re: [PATCH 7/8] sound: dt-bindings: ti,omap-twl4030: convert to DT schema
From: Rob Herring @ 2025-08-18 18:03 UTC (permalink / raw)
To: Jihed Chaibi
Cc: linux-kernel, peter.ujfalusi, dmitry.torokhov, krzk+dt, lgirdwood,
tiwai, conor+dt, lee, ukleinek, broonie, gregkh, linux-input,
devicetree, linux-pwm, linux-sound, linux-usb, shuah
In-Reply-To: <20250811224739.53869-8-jihed.chaibi.dev@gmail.com>
On Tue, Aug 12, 2025 at 12:47:38AM +0200, Jihed Chaibi wrote:
> Convert the legacy TXT binding for the OMAP TWL4030 sound card
> to the modern YAML DT schema format. This adds formal validation
> and improves documentation.
>
> Signed-off-by: Jihed Chaibi <jihed.chaibi.dev@gmail.com>
> ---
> .../bindings/sound/omap-twl4030.txt | 62 -----------
> .../bindings/sound/ti,omap-twl4030.yaml | 101 ++++++++++++++++++
> 2 files changed, 101 insertions(+), 62 deletions(-)
> delete mode 100644 Documentation/devicetree/bindings/sound/omap-twl4030.txt
> create mode 100644 Documentation/devicetree/bindings/sound/ti,omap-twl4030.yaml
>
> diff --git a/Documentation/devicetree/bindings/sound/omap-twl4030.txt b/Documentation/devicetree/bindings/sound/omap-twl4030.txt
> deleted file mode 100644
> index f6a715e4e..000000000
> --- a/Documentation/devicetree/bindings/sound/omap-twl4030.txt
> +++ /dev/null
> @@ -1,62 +0,0 @@
> -* Texas Instruments SoC with twl4030 based audio setups
> -
> -Required properties:
> -- compatible: "ti,omap-twl4030"
> -- ti,model: Name of the sound card (for example "omap3beagle")
> -- ti,mcbsp: phandle for the McBSP node
> -
> -Optional properties:
> -- ti,codec: phandle for the twl4030 audio node
> -- ti,mcbsp-voice: phandle for the McBSP node connected to the voice port of twl
> -- ti, jack-det-gpio: Jack detect GPIO
> -- ti,audio-routing: List of connections between audio components.
> - Each entry is a pair of strings, the first being the connection's sink,
> - the second being the connection's source.
> - If the routing is not provided all possible connection will be available
> -
> -Available audio endpoints for the audio-routing table:
> -
> -Board connectors:
> - * Headset Stereophone
> - * Earpiece Spk
> - * Handsfree Spk
> - * Ext Spk
> - * Main Mic
> - * Sub Mic
> - * Headset Mic
> - * Carkit Mic
> - * Digital0 Mic
> - * Digital1 Mic
> - * Line In
> -
> -twl4030 pins:
> - * HSOL
> - * HSOR
> - * EARPIECE
> - * HFL
> - * HFR
> - * PREDRIVEL
> - * PREDRIVER
> - * CARKITL
> - * CARKITR
> - * MAINMIC
> - * SUBMIC
> - * HSMIC
> - * DIGIMIC0
> - * DIGIMIC1
> - * CARKITMIC
> - * AUXL
> - * AUXR
> -
> - * Headset Mic Bias
> - * Mic Bias 1 /* Used for Main Mic or Digimic0 */
> - * Mic Bias 2 /* Used for Sub Mic or Digimic1 */
> -
> -Example:
> -
> -sound {
> - compatible = "ti,omap-twl4030";
> - ti,model = "omap3beagle";
> -
> - ti,mcbsp = <&mcbsp2>;
> -};
> diff --git a/Documentation/devicetree/bindings/sound/ti,omap-twl4030.yaml b/Documentation/devicetree/bindings/sound/ti,omap-twl4030.yaml
> new file mode 100644
> index 000000000..3124320d6
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/ti,omap-twl4030.yaml
> @@ -0,0 +1,101 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/sound/ti,omap-twl4030.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Texas Instruments SoC with twl4030 based audio setups
> +
> +maintainers:
> + - Peter Ujfalusi <peter.ujfalusi@gmail.com>
> +
> +description:
> + Bindings for audio setups on TI OMAP SoCs using TWL4030-family audio codec connected via a McBSP port.
Wrap lines at 80. Drop 'Bindings for'.
> +
> +properties:
> + compatible:
> + const: ti,omap-twl4030
> +
> + ti,model:
> + $ref: /schemas/types.yaml#/definitions/string
> + description: Name of the sound card (for example "omap3beagle").
> +
> + ti,mcbsp:
> + $ref: /schemas/types.yaml#/definitions/phandle
> + description: phandle for the McBSP node.
> +
> + ti,codec:
> + $ref: /schemas/types.yaml#/definitions/phandle
> + description: phandle for the twl4030 audio node.
> +
> + ti,mcbsp-voice:
> + $ref: /schemas/types.yaml#/definitions/phandle
> + description: phandle to the McBSP node connected to the voice port.
> +
> + ti,jack-det-gpio:
> + description: GPIO specifier for jack detection.
> + maxItems: 1
> +
> + ti,audio-routing:
> + description: |
> + A list of audio routing connections. Each entry is a pair of strings,
> + with the first being the connection's sink and the second being the
> + source. If not provided, all possible connections are available.
> +
> + Available TWL4030 Pins: "HFL", "HFR",
> + "PREDRIVEL", "PREDRIVER", "CARKITL", "CARKITR", "MAINMIC", "SUBMIC",
> + "HSMIC", "DIGIMIC0", "DIGIMIC1", "CARKITMIC", "AUXL", "AUXR",
> + "Headset Mic Bias", "Mic Bias 1", "Mic Bias 2"
Is this list supposed to match the below 'CODEC Pins'? If so, then don't
duplicate it here. If not, then I'm confused.
> + $ref: /schemas/types.yaml#/definitions/non-unique-string-array
> + items:
> + enum:
> + # Board Connectors
> + - Headset Stereophone
> + - Earpiece Spk
> + - Handsfree Spk
> + - Ext Spk
> + - Main Mic
> + - Sub Mic
> + - Headset Mic
> + - Carkit Mic
> + - Digital0 Mic
> + - Digital1 Mic
> + - Line In
> +
> + # CODEC Pins
> + - HSOL
> + - HSOR
> + - EARPIECE
> + - HFL
> + - HFR
> + - PREDRIVEL
> + - PREDRIVER
> + - CARKITL
> + - CARKITR
> + - MAINMIC
> + - SUBMIC
> + - HSMIC
> + - DIGIMIC0
> + - DIGIMIC1
> + - CARKITMIC
> + - AUXL
> + - AUXR
> +
> + # Headset Mic Bias
> + - Mic Bias 1 # Used for Main Mic or Digimic0
> + - Mic Bias 2 # Used for Sub Mic or Digimic1
> +
> +required:
> + - compatible
> + - ti,model
> + - ti,mcbsp
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + sound {
> + compatible = "ti,omap-twl4030";
> + ti,model = "omap3beagle";
> + ti,mcbsp = <&mcbsp2>;
> + };
> --
> 2.39.5
>
^ permalink raw reply
* Re: [PATCH] HID: axff: add cleanup allocated struct axff_device heap
From: Dmitry Torokhov @ 2025-08-18 17:20 UTC (permalink / raw)
To: Jeongjun Park; +Cc: jikos, bentiss, x0r, linux-input, linux-kernel
In-Reply-To: <20250818154302.811718-1-aha310510@gmail.com>
Hi Jeongjun,
On Tue, Aug 19, 2025 at 12:43:02AM +0900, Jeongjun Park wrote:
> Currently, acrux hid driver allocates heap memory equal to
> sizeof(struct axff_device) to support force feedback, but there's no code
> to free this memory except when initialization fails. This causes the
> allocated memory to not be freed even if the driver is detached.
>
> Therefore, to properly clean up and safely manage the allocated heap,
> must be modified to use devm_kzalloc().
You have not tested this, have you? The private data that is passed to
input_ff_create_memless() is freed by ml_ff_destroy() which is invoked
when input core calls input_ff_destroy() as part of input device
teardown. Your change introduces double-free.
>
> Fixes: c0dbcc33c652 ("HID: add ACRUX game controller force feedback support")
> Signed-off-by: Jeongjun Park <aha310510@gmail.com>
> ---
> drivers/hid/hid-axff.c | 8 ++------
> 1 file changed, 2 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/hid/hid-axff.c b/drivers/hid/hid-axff.c
> index fbe4e16ab029..b8202737f4c8 100644
> --- a/drivers/hid/hid-axff.c
> +++ b/drivers/hid/hid-axff.c
> @@ -96,7 +96,7 @@ static int axff_init(struct hid_device *hid)
> return -ENODEV;
> }
>
> - axff = kzalloc(sizeof(struct axff_device), GFP_KERNEL);
> + axff = devm_kzalloc(&hid->dev, sizeof(struct axff_device), GFP_KERNEL);
> if (!axff)
> return -ENOMEM;
>
> @@ -104,7 +104,7 @@ static int axff_init(struct hid_device *hid)
>
> error = input_ff_create_memless(dev, axff, axff_play);
> if (error)
> - goto err_free_mem;
> + return error;
>
> axff->report = report;
> hid_hw_request(hid, axff->report, HID_REQ_SET_REPORT);
> @@ -112,10 +112,6 @@ static int axff_init(struct hid_device *hid)
> hid_info(hid, "Force Feedback for ACRUX game controllers by Sergei Kolzun <x0r@dv-life.ru>\n");
>
> return 0;
> -
> -err_free_mem:
> - kfree(axff);
> - return error;
> }
> #else
> static inline int axff_init(struct hid_device *hid)
> --
>
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH 2/3] mfd: rohm-bd71828: Use software nodes for gpio-keys
From: Dmitry Torokhov @ 2025-08-18 17:11 UTC (permalink / raw)
To: Matti Vaittinen
Cc: Lee Jones, Andy Shevchenko, Arnd Bergmann, Bartosz Golaszewski,
Linus Walleij, linux-input, linux-kernel
In-Reply-To: <a3fb7466-6774-4ae6-9e67-d35247ffa765@gmail.com>
On Mon, Aug 18, 2025 at 09:56:07AM +0300, Matti Vaittinen wrote:
> On 18/08/2025 09:54, Matti Vaittinen wrote:
> > On 18/08/2025 01:47, Dmitry Torokhov wrote:
> > > Refactor the rohm-bd71828 MFD driver to use software nodes for
> > > instantiating the gpio-keys child device, replacing the old
> > > platform_data mechanism.
> >
> > Thanks for doing this Dmitry! I believe I didn't understand how
> > providing the IRQs via swnode works... :)
> >
> > If I visit the ROHM office this week, then I will try to test this using
> > the PMIC HW. (Next week I'll be in ELCE, and after it I have probably
> > already forgotten this...)
> >
> > > The power key's properties are now defined using software nodes and
> > > property entries. The IRQ is passed as a resource attached to the
> > > platform device.
> > >
> > > This will allow dropping support for using platform data for configuring
> > > gpio-keys in the future.
> > >
> > > Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> > > ---
> > > drivers/mfd/rohm-bd71828.c | 81 +++++++++++++++++++++++++++-----------
> > > 1 file changed, 58 insertions(+), 23 deletions(-)
> > >
> > > diff --git a/drivers/mfd/rohm-bd71828.c b/drivers/mfd/rohm-bd71828.c
> > > index a14b7aa69c3c..c29dde9996b7 100644
> > > --- a/drivers/mfd/rohm-bd71828.c
> > > +++ b/drivers/mfd/rohm-bd71828.c
> > > @@ -4,7 +4,6 @@
> >
> > // ...snip
> >
> > > +static int bd71828_reg_cnt;
> > > +
> > > +static int bd71828_i2c_register_swnodes(void)
> > > +{
> > > + int error;
> > > +
> > > + if (bd71828_reg_cnt == 0) {
> >
> > Isn't this check racy...
> >
> > > + error = software_node_register_node_group(bd71828_swnodes);
> > > + if (error)
> > > + return error;
> > > + }
> > > +
> > > + bd71828_reg_cnt++;
> >
> > ... with this...
> >
> > > + return 0;
> > > +}
> > > +
> > > +static void bd71828_i2c_unregister_swnodes(void *dummy)
> > > +{
> > > + if (bd71828_reg_cnt != 0) {
> >
> > ...this...
> >
> > > + software_node_unregister_node_group(bd71828_swnodes);
> > > + bd71828_reg_cnt--;
> >
> > ...and this? Perhaps add a mutex or use atomics?
> >
> > Also, shouldn't the software_node_unregister_node_group() be only called
> > for the last instance to exit (Eg, "if (bd71828_reg_cnt == 0)" instead
> > of the "if (bd71828_reg_cnt != 0) {")?
>
> Oh. Probably "if (bd71828_reg_cnt == 1)".
You are right, I am not sure what I was thinking when I wrote this.
I actually doubt that sharing of nodes between devices would work well.
But I believe these devices are singletons, it should not be possible to
have several of them in a single system, right? So maybe the best way is
to simply instantiate them in probe and bail out if they are already
registered.
Thanks.
--
Dmitry
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox