Linux Input/HID development
 help / color / mirror / Atom feed
* [PATCH v2] input: zforce_ts: Remove error print for devm_add_action_or_reset()
From: Waqar Hameed @ 2025-08-05  9:33 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: kernel, linux-input, linux-kernel

When `devm_add_action_or_reset()` fails, it is due to a failed memory
allocation and will thus return `-ENOMEM`. `dev_err_probe()` doesn't do
anything when error is `-ENOMEM`. Therefore, remove the useless call to
`dev_err_probe()` when `devm_add_action_or_reset()` fails, and just
return the value instead.

Signed-off-by: Waqar Hameed <waqar.hameed@axis.com>
---
Changes in v2:

* Split the patch to one seperate patch for each sub-system.

Link to v1: https://lore.kernel.org/all/pnd7c0s6ji2.fsf@axis.com/

 drivers/input/touchscreen/zforce_ts.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c
index df42fdf36ae3..4d000b5b3ae6 100644
--- a/drivers/input/touchscreen/zforce_ts.c
+++ b/drivers/input/touchscreen/zforce_ts.c
@@ -739,8 +739,7 @@ static int zforce_probe(struct i2c_client *client)
 
 	error = devm_add_action_or_reset(&client->dev, zforce_reset, ts);
 	if (error)
-		return dev_err_probe(&client->dev, error,
-				     "failed to register reset action\n");
+		return error;
 
 	snprintf(ts->phys, sizeof(ts->phys),
 		 "%s/input0", dev_name(&client->dev));

base-commit: 260f6f4fda93c8485c8037865c941b42b9cba5d2
-- 
2.39.5


^ permalink raw reply related

* Re: 答复: 答复: [External Mail][PATCH 2/2] HID: input: report battery status changes immediately
From: José Expósito @ 2025-08-05 12:38 UTC (permalink / raw)
  To: 卢国宏
  Cc: jikos@kernel.org, bentiss@kernel.org, linux-input@vger.kernel.org,
	linux-kernel@vger.kernel.org, Fei1 Jiang 蒋飞
In-Reply-To: <b5aa18342f42420093db90ee2ead88ba@xiaomi.com>

Hi,

On Tue, Aug 05, 2025 at 12:25:17PM +0000, 卢国宏 wrote:
> ________________________________
> 发件人: José Expósito <jose.exposito89@gmail.com>
> 发送时间: 2025年8月5日 16:42
> 收件人: 卢国宏
> 抄送: jikos@kernel.org; bentiss@kernel.org; linux-input@vger.kernel.org; linux-kernel@vger.kernel.org; Fei1 Jiang 蒋飞
> 主题: Re: 答复: [External Mail][PATCH 2/2] HID: input: report battery status changes immediately
> 
> [外部邮件] 此邮件来源于小米公司外部,请谨慎处理。若对邮件安全性存疑,请将邮件转发给misec@xiaomi.com进行反馈
> 
> Hi!
> 
> On Tue, Aug 05, 2025 at 01:43:30AM +0000, 卢国宏 wrote:
> > ________________________________
> > 发件人: José Expósito <jose.exposito89@gmail.com>
> > 发送时间: 2025年8月4日 17:11
> > 收件人: jikos@kernel.org
> > 抄送: bentiss@kernel.org; 卢国宏; linux-input@vger.kernel.org; linux-kernel@vger.kernel.org; José Expósito
> > 主题: [External Mail][PATCH 2/2] HID: input: report battery status changes immediately
> >
> > [外部邮件] 此邮件来源于小米公司外部,请谨慎处理。若对邮件安全性存疑,请将邮件转发给misec@xiaomi.com进行反馈
> >
> > When the battery status changes, report the change immediately to user
> > space.
> >
> > Fixes: a608dc1c0639 ("HID: input: map battery system charging")
> > Reported-by: 卢国宏 <luguohong@xiaomi.com>
> > Closes: https://lore.kernel.org/linux-input/aI49Im0sGb6fpgc8@fedora/T/
> > Signed-off-by: José Expósito <jose.exposito89@gmail.com>
> > ---
> >  drivers/hid/hid-input.c | 17 ++++++++++-------
> >  1 file changed, 10 insertions(+), 7 deletions(-)
> >
> > diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
> > index 262787e6eb20..277538a17b57 100644
> > --- a/drivers/hid/hid-input.c
> > +++ b/drivers/hid/hid-input.c
> > @@ -609,13 +609,19 @@ static bool hidinput_update_battery_charge_status(struct hid_device *dev,
> >         return false;
> >  }
> >
> > -static void hidinput_update_battery(struct hid_device *dev, int value)
> > +static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
> > +                                   int value)
> >  {
> >         int capacity;
> >
> >         if (!dev->battery)
> >                 return;
> >
> > +       if (hidinput_update_battery_charge_status(dev, usage, value)) {
> > +               power_supply_changed(dev->battery);
> > +               return;
> > +       }
> > +
> >
> > > Hi, José. Shouldn't the return statement in this code be removed?
> > > Otherwise, if both the battery level and the charging status change
> > > simultaneously, the code following the if statement that updates the
> > > battery level won't run, and the battery level won't be updated.
> > > Thanks!
> 
> At least on the hardware I have access to, changes are reported independently.
> 
> I added a log at the beginning of this function to illustrate it.
> This is the output when the battery of my device goes from 99% to 95%:
> 
>     New EV_PWR report:
>         usage = 8716389
>         value = 99
>     New EV_PWR report:
>         usage = 8716356 (HID_BAT_CHARGING)
>         value = 0       (POWER_SUPPLY_STATUS_DISCHARGING)
>     [...]
>     New EV_PWR report:
>         usage = 8716389
>         value = 95
>     New EV_PWR report:
>         usage = 8716356 (HID_BAT_CHARGING)
>         value = 0       (POWER_SUPPLY_STATUS_DISCHARGING)
> 
> If we remove that return, then "value" (0 or 1) would be used as battery level,
> reporting wrong battery levels to user-space.
> 
> Isn't your device reporting its battery information in a similar way?
> 
> > --->>>
> > Hello, Jose!
> > You're right, this return can't be removed. I had previously only
> > theoretically assumed they would be reported simultaneously, but
> > that turned out to be incorrect. It seems your solution should be fine.
> > Finally, when will your patch be merged into the Linux kernel?
> > We're looking forward to using this feature. Thank you very much!

Would you be able to test that the patches work on your device?

I assume there won't be surprises, but better to double check if
you have access to the affected device.

Once you test it, I'll send a second version of the code fixing a
warning and adding you as tester of the patch. Then it is up to
the maintainers to merge it.

Looking forward for your testing,
Jose
 
> 
> Jose
> 
> PS - I'll fix the warning in v2 and add a Tested-by: 卢国宏 tag once
>      this is confirmed to work on the affected hardware.
> 
> >
> >
> >         if (value == 0 || value < dev->battery_min || value > dev->battery_max)
> >                 return;
> >
> > @@ -648,7 +654,8 @@ static bool hidinput_update_battery_charge_status(struct hid_device *dev,
> >         return false;
> >  }
> >
> > -static void hidinput_update_battery(struct hid_device *dev, int value)
> > +static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
> > +                                   int value)
> >  {
> >  }
> >  #endif /* CONFIG_HID_BATTERY_STRENGTH */
> > @@ -1515,11 +1522,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
> >                 return;
> >
> >         if (usage->type == EV_PWR) {
> > -               bool handled = hidinput_update_battery_charge_status(hid, usage->hid, value);
> > -
> > -               if (!handled)
> > -                       hidinput_update_battery(hid, value);
> > -
> > +               hidinput_update_battery(hid, usage->hid, value);
> >                 return;
> >         }
> >
> > --
> > 2.50.1
> >
> > #/******本邮件及其附件含有小米公司的保密信息,仅限于发送给上面地址中列出的个人或群组。禁止任何其他人以任何形式使用(包括但不限于全部或部分地泄露、复制、或散发)本邮件中的信息。如果您错收了本邮件,请您立即电话或邮件通知发件人并删除本邮件! This e-mail and its attachments contain confidential information from XIAOMI, which is intended only for the person or entity whose address is listed above. Any use of the information contained herein in any way (including, but not limited to, total or partial disclosure, reproduction, or dissemination) by persons other than the intended recipient(s) is prohibited. If you receive this e-mail in error, please notify the sender by phone or email immediately and delete it!******/#
> #/******本邮件及其附件含有小米公司的保密信息,仅限于发送给上面地址中列出的个人或群组。禁止任何其他人以任何形式使用(包括但不限于全部或部分地泄露、复制、或散发)本邮件中的信息。如果您错收了本邮件,请您立即电话或邮件通知发件人并删除本邮件! This e-mail and its attachments contain confidential information from XIAOMI, which is intended only for the person or entity whose address is listed above. Any use of the information contained herein in any way (including, but not limited to, total or partial disclosure, reproduction, or dissemination) by persons other than the intended recipient(s) is prohibited. If you receive this e-mail in error, please notify the sender by phone or email immediately and delete it!******/#

^ permalink raw reply

* [PATCH AUTOSEL 6.16-6.15] HID: rate-limit hid_warn to prevent log flooding
From: Sasha Levin @ 2025-08-05 13:09 UTC (permalink / raw)
  To: patches, stable
  Cc: Li Chen, Jiri Kosina, Sasha Levin, jikos, bentiss, linux-input
In-Reply-To: <20250805130945.471732-1-sashal@kernel.org>

From: Li Chen <chenl311@chinatelecom.cn>

[ Upstream commit 4051ead99888f101be92c7ce90d2de09aac6fd1c ]

Syzkaller can create many uhid devices that trigger
repeated warnings like:

  "hid-generic xxxx: unknown main item tag 0x0"

These messages can flood the system log, especially if a crash occurs
(e.g., with a slow UART console, leading to soft lockups). To mitigate
this, convert `hid_warn()` to use `dev_warn_ratelimited()`.

This helps reduce log noise and improves system stability under fuzzing
or faulty device scenarios.

Signed-off-by: Li Chen <chenl311@chinatelecom.cn>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---

LLM Generated explanations, may be completely bogus:

**Backport Status: YES**

This commit should be backported to stable kernel trees for the
following reasons:

## Bug Fix for System Stability Issue

This commit addresses a **real system stability problem** that can lead
to soft lockups under specific conditions. The code changes show that
when malformed HID devices (either intentionally via fuzzing tools like
syzkaller or faulty hardware) send invalid data, the kernel can generate
excessive warning messages that flood the system log.

## Specific Code Analysis:

1. **Location of the fix** (drivers/hid/hid-core.c:661-663): The
   warnings are generated in the HID parser's main item processing
   function when encountering unknown or reserved tags:
  ```c
  hid_warn(parser->device, "reserved main item tag 0x%x\n", item->tag);
  hid_warn(parser->device, "unknown main item tag 0x%x\n", item->tag);
  ```

2. **Simple and contained change**: The fix only:
   - Adds a new `hid_warn_ratelimited` macro in include/linux/hid.h
   - Replaces two `hid_warn` calls with `hid_warn_ratelimited` calls
   - Total change is just 6 lines (4 insertions, 2 modifications)

3. **No architectural changes**: This is a straightforward substitution
   that uses the existing kernel infrastructure (`dev_warn_ratelimited`)
   which is already widely used throughout the kernel.

## Meets Stable Kernel Criteria:

1. **Fixes a real bug**: The commit explicitly mentions that excessive
   logging can cause "soft lockups" especially with slow UART consoles -
   this is a serious stability issue that affects real users.

2. **Security implications**: The commit mentions syzkaller (kernel
   fuzzer) can trigger this, and uhid devices can be created by
   unprivileged users in many configurations, making this a potential
   DoS vector.

3. **Minimal risk**: Rate-limiting warnings has virtually no risk of
   regression - it doesn't change any functional behavior, only reduces
   log output frequency.

4. **Already proven pattern**: Similar rate-limiting fixes have been
   applied in the HID subsystem before (e.g., commit e93363f716a2 for
   Nintendo controller) and throughout the kernel for log flooding
   issues.

5. **Clear reproducibility**: The issue is triggered by malformed HID
   devices sending invalid item tags, which can happen with faulty
   hardware or intentional fuzzing.

The commit follows the stable kernel rules perfectly: it's a small,
obvious fix for a real problem that improves system stability without
introducing new features or making risky changes.

 drivers/hid/hid-core.c | 4 ++--
 include/linux/hid.h    | 2 ++
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index b31b8a2fd540..a110d1965447 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -659,9 +659,9 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
 	default:
 		if (item->tag >= HID_MAIN_ITEM_TAG_RESERVED_MIN &&
 			item->tag <= HID_MAIN_ITEM_TAG_RESERVED_MAX)
-			hid_warn(parser->device, "reserved main item tag 0x%x\n", item->tag);
+			hid_warn_ratelimited(parser->device, "reserved main item tag 0x%x\n", item->tag);
 		else
-			hid_warn(parser->device, "unknown main item tag 0x%x\n", item->tag);
+			hid_warn_ratelimited(parser->device, "unknown main item tag 0x%x\n", item->tag);
 		ret = 0;
 	}
 
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 568a9d8c749b..7f260e0e2049 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -1239,6 +1239,8 @@ void hid_quirks_exit(__u16 bus);
 	dev_notice(&(hid)->dev, fmt, ##__VA_ARGS__)
 #define hid_warn(hid, fmt, ...)				\
 	dev_warn(&(hid)->dev, fmt, ##__VA_ARGS__)
+#define hid_warn_ratelimited(hid, fmt, ...)				\
+	dev_warn_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
 #define hid_info(hid, fmt, ...)				\
 	dev_info(&(hid)->dev, fmt, ##__VA_ARGS__)
 #define hid_dbg(hid, fmt, ...)				\
-- 
2.39.5


^ permalink raw reply related

* [PATCH 0/3] xpad: Add support for Thrustmaster, Inc. Ferrari 458 Spider Racing Wheel
From: Pär Eriksson @ 2025-08-05 19:20 UTC (permalink / raw)
  To: Dmitry Torokhov, Vicki Pfau, Pavel Rojtberg, Nilton Perim Neto,
	Antheas Kapenekakis, Pär Eriksson, Mario Limonciello,
	linux-input, linux-kernel

Hello,

This patch series adds support for the Thrustmaster, Inc. Ferrari 458 Spider Racing Wheel (USB ID 044f:b671) to the xpad driver. The implementation is inspired by the work in https://github.com/Capure/xpad.

The wheel reports steering and pedal data at non-standard offsets, so custom axis mapping is introduced via a new mapping flag.
I have tested the changes with the actual hardware on Fedora 42 (kernel 6.15.7-200.fc42.x86_64), and confirmed correct input reporting.

If you prefer a different name for the mapping flag, please let me know.

Please consider this for upstream inclusion.

Pär Eriksson (3):
  Input: xpad - Add MAP_FERRARI_458_CUSTOM_AXES mapping flag
  Input: xpad - Add Thrustmaster Ferrari 458 Spider (044f:b671) device
    entry
  Input: xpad - Implement custom axis mapping for Ferrari 458 Spider

 drivers/input/joystick/xpad.c | 35 ++++++++++++++++++++++++-----------
 1 file changed, 24 insertions(+), 11 deletions(-)

-- 
2.50.1


^ permalink raw reply

* [PATCH 1/3] Input: xpad - Add MAP_FERRARI_458_CUSTOM_AXES mapping flag
From: Pär Eriksson @ 2025-08-05 19:20 UTC (permalink / raw)
  To: Dmitry Torokhov, Vicki Pfau, Pavel Rojtberg, Nilton Perim Neto,
	Antheas Kapenekakis, Pär Eriksson, Mario Limonciello,
	linux-input, linux-kernel
In-Reply-To: <20250805192036.53918-1-parherman@gmail.com>

Introduce MAP_FERRARI_458_CUSTOM_AXES to support custom axis mapping for
the Thrustmaster Ferrari 458 Spider wheel and pedals.

Signed-off-by: Pär Eriksson <parherman@gmail.com>
---
 drivers/input/joystick/xpad.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 1d8c579b5433..51e285efac79 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -84,6 +84,7 @@
 #define MAP_PADDLES			BIT(4)
 #define MAP_PROFILE_BUTTON		BIT(5)
 #define MAP_SHARE_OFFSET		BIT(6)
+#define MAP_FERRARI_458_CUSTOM_AXES		BIT(7)
 
 #define DANCEPAD_MAP_CONFIG	(MAP_DPAD_TO_BUTTONS |			\
 				MAP_TRIGGERS_TO_BUTTONS | MAP_STICKS_TO_NULL)
-- 
2.50.1


^ permalink raw reply related

* [PATCH 2/3] Input: xpad - Add Thrustmaster Ferrari 458 Spider (044f:b671) device entry
From: Pär Eriksson @ 2025-08-05 19:20 UTC (permalink / raw)
  To: Dmitry Torokhov, Vicki Pfau, Pavel Rojtberg, Nilton Perim Neto,
	Antheas Kapenekakis, Pär Eriksson, Mario Limonciello,
	linux-input, linux-kernel
In-Reply-To: <20250805192036.53918-1-parherman@gmail.com>

Add the USB ID for the Thrustmaster Ferrari 458 Spider (044f:b671) to the
device table, using the MAP_FERRARI_458_CUSTOM_AXES mapping flag.

Signed-off-by: Pär Eriksson <parherman@gmail.com>
---
 drivers/input/joystick/xpad.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 51e285efac79..fdd70f256be7 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -147,6 +147,7 @@ static const struct xpad_device {
 	{ 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX },
 	{ 0x044f, 0x0f10, "Thrustmaster Modena GT Wheel", 0, XTYPE_XBOX },
 	{ 0x044f, 0xb326, "Thrustmaster Gamepad GP XID", 0, XTYPE_XBOX360 },
+	{ 0x044f, 0xb671, "Thrustmaster, Inc. Ferrari 458 Spider Racing Wheel", MAP_FERRARI_458_CUSTOM_AXES, XTYPE_XBOXONE },
 	{ 0x044f, 0xd01e, "ThrustMaster, Inc. ESWAP X 2 ELDEN RING EDITION", 0, XTYPE_XBOXONE },
 	{ 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", 0, XTYPE_XBOX },
 	{ 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", 0, XTYPE_XBOX },
-- 
2.50.1


^ permalink raw reply related

* [PATCH 3/3] Input: xpad - Implement custom axis mapping for Ferrari 458 Spider
From: Pär Eriksson @ 2025-08-05 19:20 UTC (permalink / raw)
  To: Dmitry Torokhov, Vicki Pfau, Pavel Rojtberg, Nilton Perim Neto,
	Pär Eriksson, Antheas Kapenekakis, Pierre-Loup A. Griffais,
	linux-input, linux-kernel
In-Reply-To: <20250805192036.53918-1-parherman@gmail.com>

Update xpadone_process_packet to handle steering and pedal data at
non-standard offsets for devices with MAP_FERRARI_458_CUSTOM_AXES.

Signed-off-by: Pär Eriksson <parherman@gmail.com>
---
 drivers/input/joystick/xpad.c | 33 ++++++++++++++++++++++-----------
 1 file changed, 22 insertions(+), 11 deletions(-)

diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index fdd70f256be7..252290424bbd 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -1135,17 +1135,23 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char
 		input_report_key(dev, BTN_THUMBR, data[5] & BIT(7));
 
 		if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
-			/* left stick */
-			input_report_abs(dev, ABS_X,
-					(__s16) le16_to_cpup((__le16 *)(data + 10)));
-			input_report_abs(dev, ABS_Y,
-					~(__s16) le16_to_cpup((__le16 *)(data + 12)));
-
-			/* right stick */
-			input_report_abs(dev, ABS_RX,
-					(__s16) le16_to_cpup((__le16 *)(data + 14)));
-			input_report_abs(dev, ABS_RY,
-					~(__s16) le16_to_cpup((__le16 *)(data + 16)));
+			if (xpad->mapping & MAP_FERRARI_458_CUSTOM_AXES) {
+				/* steering wheel */
+				input_report_abs(dev, ABS_X,
+				((__u16) le16_to_cpup((__le16 *)(data + 6))) - S16_MAX);
+			} else {
+				/* left stick */
+				input_report_abs(dev, ABS_X,
+						(__s16) le16_to_cpup((__le16 *)(data + 10)));
+				input_report_abs(dev, ABS_Y,
+						~(__s16) le16_to_cpup((__le16 *)(data + 12)));
+
+				/* right stick */
+				input_report_abs(dev, ABS_RX,
+						(__s16) le16_to_cpup((__le16 *)(data + 14)));
+				input_report_abs(dev, ABS_RY,
+						~(__s16) le16_to_cpup((__le16 *)(data + 16)));
+			}
 		}
 
 		/* triggers left/right */
@@ -1154,6 +1160,11 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char
 					(__u16) le16_to_cpup((__le16 *)(data + 6)));
 			input_report_key(dev, BTN_TR2,
 					(__u16) le16_to_cpup((__le16 *)(data + 8)));
+		} else if (xpad->mapping & MAP_FERRARI_458_CUSTOM_AXES) {
+			input_report_abs(dev, ABS_Z,
+					(__u16) le16_to_cpup((__le16 *)(data + 10)));
+			input_report_abs(dev, ABS_RZ,
+					(__u16) le16_to_cpup((__le16 *)(data + 8)));
 		} else {
 			input_report_abs(dev, ABS_Z,
 					(__u16) le16_to_cpup((__le16 *)(data + 6)));
-- 
2.50.1


^ permalink raw reply related

* Re: [PATCH v2] platform/x86: Add WMI driver for Redmibook keyboard.
From: Dmitry Torokhov @ 2025-08-05 19:42 UTC (permalink / raw)
  To: Gladyshev Ilya
  Cc: w_armin, linux-input, nikita.nikita.krasnov, Hans de Goede,
	Ilpo Järvinen, linux-kernel, platform-driver-x86
In-Reply-To: <20250729190528.8446-1-foxido@foxido.dev>

On Tue, Jul 29, 2025 at 10:05:21PM +0300, Gladyshev Ilya wrote:
> +static void redmi_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
> +{
> +	struct redmi_wmi *data = dev_get_drvdata(&wdev->dev);
> +	int value = 1;
> +	bool autorelease = true;
> +
> +	if (obj->type != ACPI_TYPE_BUFFER) {
> +		dev_err(&wdev->dev, "Bad response type %u\n", obj->type);
> +		return;
> +	}
> +
> +	if (obj->buffer.length != 32) {
> +		dev_err(&wdev->dev, "Invalid buffer length %u\n", obj->buffer.length);
> +		return;
> +	}
> +
> +	/* For linearizability */
> +	guard(mutex)(&data->key_lock);

What is the exact purpose of this mutex? What does it protect? 

> +
> +	u32 payload = get_unaligned_le32(obj->buffer.pointer);
> +	struct key_entry *entry = sparse_keymap_entry_from_scancode(data->input_dev, payload);
> +
> +	if (!entry) {
> +		dev_dbg(&wdev->dev, "Unknown WMI event with payload %u", payload);
> +		return;
> +	}
> +
> +	/* AI key quirk */
> +	if (entry->keycode == KEY_ASSISTANT) {
> +		value = !(payload & AI_KEY_VALUE_MASK);
> +		autorelease = false;
> +	}
> +
> +	sparse_keymap_report_entry(data->input_dev, entry, value, autorelease);
> +}


Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH 0/3] xpad: Add support for Thrustmaster, Inc. Ferrari 458 Spider Racing Wheel
From: Vicki Pfau @ 2025-08-05 20:23 UTC (permalink / raw)
  To: Pär Eriksson, Dmitry Torokhov, Pavel Rojtberg,
	Nilton Perim Neto, Antheas Kapenekakis, Mario Limonciello,
	linux-input, linux-kernel
In-Reply-To: <20250805192036.53918-1-parherman@gmail.com>

Hi Pär,

On 8/5/25 12:20 PM, Pär Eriksson wrote:
> Hello,
> 
> This patch series adds support for the Thrustmaster, Inc. Ferrari 458 Spider Racing Wheel (USB ID 044f:b671) to the xpad driver. The implementation is inspired by the work in https://github.com/Capure/xpad.
> 
> The wheel reports steering and pedal data at non-standard offsets, so custom axis mapping is introduced via a new mapping flag.
> I have tested the changes with the actual hardware on Fedora 42 (kernel 6.15.7-200.fc42.x86_64), and confirmed correct input reporting.
> 
> If you prefer a different name for the mapping flag, please let me know.
> 
> Please consider this for upstream inclusion.
> 
> Pär Eriksson (3):
>   Input: xpad - Add MAP_FERRARI_458_CUSTOM_AXES mapping flag
>   Input: xpad - Add Thrustmaster Ferrari 458 Spider (044f:b671) device
>     entry
>   Input: xpad - Implement custom axis mapping for Ferrari 458 Spider
> 
>  drivers/input/joystick/xpad.c | 35 ++++++++++++++++++++++++-----------
>  1 file changed, 24 insertions(+), 11 deletions(-)
> 

So I have several concerns with this series in general. First of all, it should be just one patch, but more importantly, the device *does* have standard offsets...just not standard *gamepad* offsets. GIP, the Xbox One controller protocol, uses GUIDs to specify the type of device in question. Most devices you're familiar with use the gamepad GUID, but the wheels use a different one which have a different set of mappings. So ideally, the name of the flag would be MAP_WHEEL.

However, the problem runs deeper. This is all easily detectable from the metadata. xpad *in general* does not support the metadata parsing, so we can't autodetect it without parsing it. Unfortunately, the metadata message is fragmented. xpad *in general* does not support fragmented messages. xpad is based on a very rudimentary understanding of the protocol, which is very obsolete at this point.

I have a new driver I've been working on for a few months that supports all of this stuff properly, including wheels, that I am hoping to support for the 6.17 merge window. I may be too late, but if I submit it soon you can at least pull it downstream while waiting for the 6.18 merge window. I'll see if I can get it ready before the end of the week.

Vicki

^ permalink raw reply

* Re: [PATCH 0/3] xpad: Add support for Thrustmaster, Inc. Ferrari 458 Spider Racing Wheel
From: Dmitry Torokhov @ 2025-08-05 20:57 UTC (permalink / raw)
  To: Vicki Pfau
  Cc: Pär Eriksson, Pavel Rojtberg, Nilton Perim Neto,
	Antheas Kapenekakis, Mario Limonciello, linux-input, linux-kernel
In-Reply-To: <1607cb00-1146-4a64-92cc-65eb91062706@endrift.com>

Hi Vicki,

On Tue, Aug 05, 2025 at 01:23:39PM -0700, Vicki Pfau wrote:

> I have a new driver I've been working on for a few months that
> supports all of this stuff properly, including wheels, that I am
> hoping to support for the 6.17 merge window.

6.17 merge window is closing this weekend so no new drivers can go into
it anymore, only what was in maintainer trees/linux-next.

6.18 is the earliest option.

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH] hid: fix I2C read buffer overflow in raw_event() for mcp2221
From: Arnaud Lecomte @ 2025-08-05 21:07 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Jiri Kosina, linux-i2c, linux-input, linux-kernel,
	syzbot+52c1a7d3e5b361ccd346
In-Reply-To: <20250726220931.7126-1-contact@arnaud-lcm.com>

Hi Benjamin,
I’m forwarding this patch to you since I noticed that Rishi’s last 
activity on the Linux mailing list was
over three years ago, he may no longer be actively involved.
Please let me know if this is the right approach or if there’s a more 
appropriate way to proceed.
I’d hate to cause any inconvenience.

Cheers,
Arnaud

On 26/07/2025 23:09, Arnaud Lecomte wrote:
> As reported by syzbot, mcp2221_raw_event lacked
> validation of incoming I2C read data sizes, risking buffer
> overflows in mcp->rxbuf during multi-part transfers.
> As highlighted in the DS20005565B spec, p44, we have:
> "The number of read-back data bytes to follow in this packet:
> from 0 to a maximum of 60 bytes of read-back bytes."
> This patch enforces we don't exceed this limit.
>
> Reported-by: syzbot+52c1a7d3e5b361ccd346@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=52c1a7d3e5b361ccd346
> Tested-by: syzbot+52c1a7d3e5b361ccd346@syzkaller.appspotmail.com
> Signed-off-by: Arnaud Lecomte <contact@arnaud-lcm.com>
> ---
>   drivers/hid/hid-mcp2221.c | 4 ++++
>   1 file changed, 4 insertions(+)
>
> diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c
> index 0f93c22a479f..83941b916cd6 100644
> --- a/drivers/hid/hid-mcp2221.c
> +++ b/drivers/hid/hid-mcp2221.c
> @@ -814,6 +814,10 @@ static int mcp2221_raw_event(struct hid_device *hdev,
>   			}
>   			if (data[2] == MCP2221_I2C_READ_COMPL ||
>   			    data[2] == MCP2221_I2C_READ_PARTIAL) {
> +				if (!mcp->rxbuf || mcp->rxbuf_idx < 0 || data[3] > 60) {
> +					mcp->status = -EINVAL;
> +					break;
> +				}
>   				buf = mcp->rxbuf;
>   				memcpy(&buf[mcp->rxbuf_idx], &data[4], data[3]);
>   				mcp->rxbuf_idx = mcp->rxbuf_idx + data[3];

^ permalink raw reply

* Re: Re [PATCH v11 0/4] Firmware Support for USB-HID Devices and CP2112
From: Andy Shevchenko @ 2025-08-05 21:32 UTC (permalink / raw)
  To: Danny Kaehn
  Cc: Willie Thai, bartosz.golaszewski, bentiss, devicetree,
	dmitry.torokhov, ethan.twardy, jikos, krzk+dt, linux-input, robh,
	tingkaic, rastekar, dkodihalli, mhn, arundp
In-Reply-To: <20250729174951.GB4111945@LNDCL34533.neenah.na.plexus.com>

On Tue, Jul 29, 2025 at 12:49:51PM -0500, Danny Kaehn wrote:
> On Tue, Jul 29, 2025 at 02:53:50PM +0000, Willie Thai wrote:
> > Hi Danny,
> > 
> > I hope this message finds you well.
> > Thank you for the patch set — it’s exactly what we need for the I2C-over-USB feature in our new products.
> > Could you please let us know when we can expect the next version of the patch set?
> > If you've paused work on it, we're happy to take over and continue from where you left off.
> > 
> > Thanks!
> 
> Thanks for reaching out!
> 
> Apologies, I haven't been working on this in a while, and have only been able
> to intermittently return to attempt to bring it forward.
> 
> Feel free to take over and move this forward! I'm not sure what the protocol
> is for that, as far as changelogs and versions and whatnot. If your product's
> timeline for needing this mainlined is not urgent; however, I can prioritize
> coming back to this and having a v12 submitted, likely by the end of next
> week, to remove the overhead needed for you to assume ownership of the
> patchset.
> 
> The last several versions of this patchset have all revolved around trying
> to get this change working for ACPI as well as DeviceTree in such a way which
> make the ACPI and DeviceTree interface/binding acceptable to their respective
> maintainers. With this latest version, it seemed that there was not going to
> be any consensus between the two firmware languages, so it seemed an entirely
> different binding/interface and corresponding logic in the device driver
> would be needed. This seems unfortunate, as it seemed the whole purpose of
> the fwnode / device_*() functions was to unify the driver interface to the
> firmware language used... but this is presumably a special case, being almost
> exclusively a device composed of different generic device functions...
> 
> Let me know if you plan to take this over and if there's any
> documentation/context/test procedures you would need from me; else I would be
> happy to start moving this forward again now that there is someone waiting
> on it.

Right and I'm, for instance, lost the context long time ago, so please Cc me on
a new version to have a fresh look at it.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH 0/3] xpad: Add support for Thrustmaster, Inc. Ferrari 458 Spider Racing Wheel
From: Vicki Pfau @ 2025-08-06  0:16 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Pär Eriksson, Pavel Rojtberg, Nilton Perim Neto,
	Antheas Kapenekakis, Mario Limonciello, linux-input, linux-kernel
In-Reply-To: <qnu5h6nyigaxdv3m3uxfztrxm6bad6hv6jfnvgbkr5ewhjgplz@dleqh5zzljbd>

Hi Dmitry

On 8/5/25 1:57 PM, Dmitry Torokhov wrote:
> Hi Vicki,
> 
> On Tue, Aug 05, 2025 at 01:23:39PM -0700, Vicki Pfau wrote:
> 
>> I have a new driver I've been working on for a few months that
>> supports all of this stuff properly, including wheels, that I am
>> hoping to support for the 6.17 merge window.
> 
> 6.17 merge window is closing this weekend so no new drivers can go into
> it anymore, only what was in maintainer trees/linux-next.

I see. I haven't been paying attention to how merge window timing works until now, so that's on me for not cleaning it up sooner for this cycle. My other concerns for this patchset still stand, though.

> 
> 6.18 is the earliest option.

That gives me more time to clean it up and generally get it working better, so I guess it's a trade-off here. It's still missing some features I'd like to get in so I don't have to do significant refactoring later.

> 
> Thanks.
> 

Thanks,
Vicki

^ permalink raw reply

* [PATCH] Hid: Intel-thc-hid: Intel-quicki2c: Enhance driver re-install flow
From: Even Xu @ 2025-08-06  0:23 UTC (permalink / raw)
  To: jikos, bentiss
  Cc: srinivas.pandruvada, linux-input, linux-kernel, Even Xu,
	Rui Zhang

After driver module is removed and during re-install stage, if there
is continueous user touching on the screen, it is a risk impacting
THC hardware initialization which causes driver installation failure.

This patch enhances this flow by quiescing the external touch
interrupt after driver is removed which keeps THC hardware
ignore external interrupt during this remove and re-install stage.

Signed-off-by: Even Xu <even.xu@intel.com>
Tested-by: Rui Zhang <rui1.zhang@intel.com>
Fixes: 66b59bfce6d9 ("HID: intel-thc-hid: intel-quicki2c: Complete THC QuickI2C driver")
---
 drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
index e944a6ccb776..854926b3cfd4 100644
--- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
@@ -419,6 +419,7 @@ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __io
  */
 static void quicki2c_dev_deinit(struct quicki2c_device *qcdev)
 {
+	thc_interrupt_quiesce(qcdev->thc_hw, true);
 	thc_interrupt_enable(qcdev->thc_hw, false);
 	thc_ltr_unconfig(qcdev->thc_hw);
 	thc_wot_unconfig(qcdev->thc_hw);
-- 
2.40.1


^ permalink raw reply related

* [PATCH v2 1/2] HID: input: rename hidinput_set_battery_charge_status()
From: José Expósito @ 2025-08-06  7:39 UTC (permalink / raw)
  To: jikos
  Cc: bentiss, luguohong, linux-input, linux-kernel,
	José Expósito

In preparation for a patch fixing a bug affecting
hidinput_set_battery_charge_status(), rename the function to
hidinput_update_battery_charge_status() and move it up so it can be used
by hidinput_update_battery().

Refactor, no functional changes.

Tested-by: 卢国宏 <luguohong@xiaomi.com>
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
---
 drivers/hid/hid-input-test.c | 10 +++++-----
 drivers/hid/hid-input.c      | 38 ++++++++++++++++++------------------
 2 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/drivers/hid/hid-input-test.c b/drivers/hid/hid-input-test.c
index 77c2d45ac62a..6f5c71660d82 100644
--- a/drivers/hid/hid-input-test.c
+++ b/drivers/hid/hid-input-test.c
@@ -7,7 +7,7 @@
 
 #include <kunit/test.h>
 
-static void hid_test_input_set_battery_charge_status(struct kunit *test)
+static void hid_test_input_update_battery_charge_status(struct kunit *test)
 {
 	struct hid_device *dev;
 	bool handled;
@@ -15,15 +15,15 @@ static void hid_test_input_set_battery_charge_status(struct kunit *test)
 	dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL);
 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
 
-	handled = hidinput_set_battery_charge_status(dev, HID_DG_HEIGHT, 0);
+	handled = hidinput_update_battery_charge_status(dev, HID_DG_HEIGHT, 0);
 	KUNIT_EXPECT_FALSE(test, handled);
 	KUNIT_EXPECT_EQ(test, dev->battery_charge_status, POWER_SUPPLY_STATUS_UNKNOWN);
 
-	handled = hidinput_set_battery_charge_status(dev, HID_BAT_CHARGING, 0);
+	handled = hidinput_update_battery_charge_status(dev, HID_BAT_CHARGING, 0);
 	KUNIT_EXPECT_TRUE(test, handled);
 	KUNIT_EXPECT_EQ(test, dev->battery_charge_status, POWER_SUPPLY_STATUS_DISCHARGING);
 
-	handled = hidinput_set_battery_charge_status(dev, HID_BAT_CHARGING, 1);
+	handled = hidinput_update_battery_charge_status(dev, HID_BAT_CHARGING, 1);
 	KUNIT_EXPECT_TRUE(test, handled);
 	KUNIT_EXPECT_EQ(test, dev->battery_charge_status, POWER_SUPPLY_STATUS_CHARGING);
 }
@@ -63,7 +63,7 @@ static void hid_test_input_get_battery_property(struct kunit *test)
 }
 
 static struct kunit_case hid_input_tests[] = {
-	KUNIT_CASE(hid_test_input_set_battery_charge_status),
+	KUNIT_CASE(hid_test_input_update_battery_charge_status),
 	KUNIT_CASE(hid_test_input_get_battery_property),
 	{ }
 };
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index ff1784b5c2a4..262787e6eb20 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -595,6 +595,20 @@ static void hidinput_cleanup_battery(struct hid_device *dev)
 	dev->battery = NULL;
 }
 
+static bool hidinput_update_battery_charge_status(struct hid_device *dev,
+						  unsigned int usage, int value)
+{
+	switch (usage) {
+	case HID_BAT_CHARGING:
+		dev->battery_charge_status = value ?
+					     POWER_SUPPLY_STATUS_CHARGING :
+					     POWER_SUPPLY_STATUS_DISCHARGING;
+		return true;
+	}
+
+	return false;
+}
+
 static void hidinput_update_battery(struct hid_device *dev, int value)
 {
 	int capacity;
@@ -617,20 +631,6 @@ static void hidinput_update_battery(struct hid_device *dev, int value)
 		power_supply_changed(dev->battery);
 	}
 }
-
-static bool hidinput_set_battery_charge_status(struct hid_device *dev,
-					       unsigned int usage, int value)
-{
-	switch (usage) {
-	case HID_BAT_CHARGING:
-		dev->battery_charge_status = value ?
-					     POWER_SUPPLY_STATUS_CHARGING :
-					     POWER_SUPPLY_STATUS_DISCHARGING;
-		return true;
-	}
-
-	return false;
-}
 #else  /* !CONFIG_HID_BATTERY_STRENGTH */
 static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
 				  struct hid_field *field, bool is_percentage)
@@ -642,14 +642,14 @@ static void hidinput_cleanup_battery(struct hid_device *dev)
 {
 }
 
-static void hidinput_update_battery(struct hid_device *dev, int value)
+static bool hidinput_update_battery_charge_status(struct hid_device *dev,
+						  unsigned int usage, int value)
 {
+	return false;
 }
 
-static bool hidinput_set_battery_charge_status(struct hid_device *dev,
-					       unsigned int usage, int value)
+static void hidinput_update_battery(struct hid_device *dev, int value)
 {
-	return false;
 }
 #endif	/* CONFIG_HID_BATTERY_STRENGTH */
 
@@ -1515,7 +1515,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
 		return;
 
 	if (usage->type == EV_PWR) {
-		bool handled = hidinput_set_battery_charge_status(hid, usage->hid, value);
+		bool handled = hidinput_update_battery_charge_status(hid, usage->hid, value);
 
 		if (!handled)
 			hidinput_update_battery(hid, value);
-- 
2.50.1


^ permalink raw reply related

* [PATCH v2 2/2] HID: input: report battery status changes immediately
From: José Expósito @ 2025-08-06  7:39 UTC (permalink / raw)
  To: jikos
  Cc: bentiss, luguohong, linux-input, linux-kernel,
	José Expósito
In-Reply-To: <20250806073944.5310-1-jose.exposito89@gmail.com>

When the battery status changes, report the change immediately to user
space.

Fixes: a608dc1c0639 ("HID: input: map battery system charging")
Reported-by: 卢国宏 <luguohong@xiaomi.com>
Closes: https://lore.kernel.org/linux-input/aI49Im0sGb6fpgc8@fedora/T/
Tested-by: 卢国宏 <luguohong@xiaomi.com>
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
---
 drivers/hid/hid-input.c | 23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)

diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 262787e6eb20..f45f856a127f 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -609,13 +609,19 @@ static bool hidinput_update_battery_charge_status(struct hid_device *dev,
 	return false;
 }
 
-static void hidinput_update_battery(struct hid_device *dev, int value)
+static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
+				    int value)
 {
 	int capacity;
 
 	if (!dev->battery)
 		return;
 
+	if (hidinput_update_battery_charge_status(dev, usage, value)) {
+		power_supply_changed(dev->battery);
+		return;
+	}
+
 	if (value == 0 || value < dev->battery_min || value > dev->battery_max)
 		return;
 
@@ -642,13 +648,8 @@ static void hidinput_cleanup_battery(struct hid_device *dev)
 {
 }
 
-static bool hidinput_update_battery_charge_status(struct hid_device *dev,
-						  unsigned int usage, int value)
-{
-	return false;
-}
-
-static void hidinput_update_battery(struct hid_device *dev, int value)
+static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
+				    int value)
 {
 }
 #endif	/* CONFIG_HID_BATTERY_STRENGTH */
@@ -1515,11 +1516,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
 		return;
 
 	if (usage->type == EV_PWR) {
-		bool handled = hidinput_update_battery_charge_status(hid, usage->hid, value);
-
-		if (!handled)
-			hidinput_update_battery(hid, value);
-
+		hidinput_update_battery(hid, usage->hid, value);
 		return;
 	}
 
-- 
2.50.1


^ permalink raw reply related

* Re: 答复: 答复: 答复: [External Mail][PATCH 2/2] HID: input: report battery status changes immediately
From: José Expósito @ 2025-08-06  7:42 UTC (permalink / raw)
  To: 卢国宏
  Cc: jikos@kernel.org, bentiss@kernel.org, linux-input@vger.kernel.org,
	linux-kernel@vger.kernel.org, Fei1 Jiang 蒋飞
In-Reply-To: <9dbdee5aa8fa4b899bfbb8be3b4cd6c5@xiaomi.com>

Hi 卢国宏,

On Wed, Aug 06, 2025 at 07:03:05AM +0000, 卢国宏 wrote:
> 
> Hello, Jose!
> I've completed testing on our devices. Your patch works fine for charging status reporting.
> Below is the switching log between partially charged state and uncharged state:
> 
> ...
> Xiaomi handler POWER_SUPPLY_STATUS=Charging
> ...
> Xiaomi handler POWER_SUPPLY_STATUS=Discharging
> ...
> Xiaomi handler POWER_SUPPLY_STATUS=Charging
> 
> In addition, the handle battery level can also be reported normally.
> Now you should be able to submit your patch to the Linux kernel. After submitting,
> please send me a copy of the official patch so I can contact Google to merge it into
> their GKI and enable this feature in our products first. Thank you very much!

Thanks a lot for testing it. Here is the link to the patch:
https://lore.kernel.org/linux-input/20250806073944.5310-1-jose.exposito89@gmail.com/T/

Best wishes,
Jose

> 
> luguohong
> 
> ________________________________
> 发件人: 卢国宏
> 发送时间: 2025年8月6日 9:41
> 收件人: José Expósito
> 抄送: jikos@kernel.org; bentiss@kernel.org; linux-input@vger.kernel.org; linux-kernel@vger.kernel.org; Fei1 Jiang 蒋飞; 卢国宏
> 主题: 答复: 答复: 答复: [External Mail][PATCH 2/2] HID: input: report battery status changes immediately
> 
> 
> 
> 
> OK! I'll use your patch and finish testing it soon and let you know the results, thanks!
> 
> 
> ________________________________
> 发件人: José Expósito <jose.exposito89@gmail.com>
> 发送时间: 2025年8月5日 20:38
> 收件人: 卢国宏
> 抄送: jikos@kernel.org; bentiss@kernel.org; linux-input@vger.kernel.org; linux-kernel@vger.kernel.org; Fei1 Jiang 蒋飞
> 主题: Re: 答复: 答复: [External Mail][PATCH 2/2] HID: input: report battery status changes immediately
> 
> [外部邮件] 此邮件来源于小米公司外部,请谨慎处理。若对邮件安全性存疑,请将邮件转发给misec@xiaomi.com进行反馈
> 
> Hi,
> 
> On Tue, Aug 05, 2025 at 12:25:17PM +0000, 卢国宏 wrote:
> > ________________________________
> > 发件人: José Expósito <jose.exposito89@gmail.com>
> > 发送时间: 2025年8月5日 16:42
> > 收件人: 卢国宏
> > 抄送: jikos@kernel.org; bentiss@kernel.org; linux-input@vger.kernel.org; linux-kernel@vger.kernel.org; Fei1 Jiang 蒋飞
> > 主题: Re: 答复: [External Mail][PATCH 2/2] HID: input: report battery status changes immediately
> >
> > [外部邮件] 此邮件来源于小米公司外部,请谨慎处理。若对邮件安全性存疑,请将邮件转发给misec@xiaomi.com进行反馈
> >
> > Hi!
> >
> > On Tue, Aug 05, 2025 at 01:43:30AM +0000, 卢国宏 wrote:
> > > ________________________________
> > > 发件人: José Expósito <jose.exposito89@gmail.com>
> > > 发送时间: 2025年8月4日 17:11
> > > 收件人: jikos@kernel.org
> > > 抄送: bentiss@kernel.org; 卢国宏; linux-input@vger.kernel.org; linux-kernel@vger.kernel.org; José Expósito
> > > 主题: [External Mail][PATCH 2/2] HID: input: report battery status changes immediately
> > >
> > > [外部邮件] 此邮件来源于小米公司外部,请谨慎处理。若对邮件安全性存疑,请将邮件转发给misec@xiaomi.com进行反馈
> > >
> > > When the battery status changes, report the change immediately to user
> > > space.
> > >
> > > Fixes: a608dc1c0639 ("HID: input: map battery system charging")
> > > Reported-by: 卢国宏 <luguohong@xiaomi.com>
> > > Closes: https://lore.kernel.org/linux-input/aI49Im0sGb6fpgc8@fedora/T/
> > > Signed-off-by: José Expósito <jose.exposito89@gmail.com>
> > > ---
> > >  drivers/hid/hid-input.c | 17 ++++++++++-------
> > >  1 file changed, 10 insertions(+), 7 deletions(-)
> > >
> > > diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
> > > index 262787e6eb20..277538a17b57 100644
> > > --- a/drivers/hid/hid-input.c
> > > +++ b/drivers/hid/hid-input.c
> > > @@ -609,13 +609,19 @@ static bool hidinput_update_battery_charge_status(struct hid_device *dev,
> > >         return false;
> > >  }
> > >
> > > -static void hidinput_update_battery(struct hid_device *dev, int value)
> > > +static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
> > > +                                   int value)
> > >  {
> > >         int capacity;
> > >
> > >         if (!dev->battery)
> > >                 return;
> > >
> > > +       if (hidinput_update_battery_charge_status(dev, usage, value)) {
> > > +               power_supply_changed(dev->battery);
> > > +               return;
> > > +       }
> > > +
> > >
> > > > Hi, José. Shouldn't the return statement in this code be removed?
> > > > Otherwise, if both the battery level and the charging status change
> > > > simultaneously, the code following the if statement that updates the
> > > > battery level won't run, and the battery level won't be updated.
> > > > Thanks!
> >
> > At least on the hardware I have access to, changes are reported independently.
> >
> > I added a log at the beginning of this function to illustrate it.
> > This is the output when the battery of my device goes from 99% to 95%:
> >
> >     New EV_PWR report:
> >         usage = 8716389
> >         value = 99
> >     New EV_PWR report:
> >         usage = 8716356 (HID_BAT_CHARGING)
> >         value = 0       (POWER_SUPPLY_STATUS_DISCHARGING)
> >     [...]
> >     New EV_PWR report:
> >         usage = 8716389
> >         value = 95
> >     New EV_PWR report:
> >         usage = 8716356 (HID_BAT_CHARGING)
> >         value = 0       (POWER_SUPPLY_STATUS_DISCHARGING)
> >
> > If we remove that return, then "value" (0 or 1) would be used as battery level,
> > reporting wrong battery levels to user-space.
> >
> > Isn't your device reporting its battery information in a similar way?
> >
> > > --->>>
> > > Hello, Jose!
> > > You're right, this return can't be removed. I had previously only
> > > theoretically assumed they would be reported simultaneously, but
> > > that turned out to be incorrect. It seems your solution should be fine.
> > > Finally, when will your patch be merged into the Linux kernel?
> > > We're looking forward to using this feature. Thank you very much!
> 
> Would you be able to test that the patches work on your device?
> 
> I assume there won't be surprises, but better to double check if
> you have access to the affected device.
> 
> Once you test it, I'll send a second version of the code fixing a
> warning and adding you as tester of the patch. Then it is up to
> the maintainers to merge it.
> 
> Looking forward for your testing,
> Jose
> 
> >
> > Jose
> >
> > PS - I'll fix the warning in v2 and add a Tested-by: 卢国宏 tag once
> >      this is confirmed to work on the affected hardware.
> >
> > >
> > >
> > >         if (value == 0 || value < dev->battery_min || value > dev->battery_max)
> > >                 return;
> > >
> > > @@ -648,7 +654,8 @@ static bool hidinput_update_battery_charge_status(struct hid_device *dev,
> > >         return false;
> > >  }
> > >
> > > -static void hidinput_update_battery(struct hid_device *dev, int value)
> > > +static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
> > > +                                   int value)
> > >  {
> > >  }
> > >  #endif /* CONFIG_HID_BATTERY_STRENGTH */
> > > @@ -1515,11 +1522,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
> > >                 return;
> > >
> > >         if (usage->type == EV_PWR) {
> > > -               bool handled = hidinput_update_battery_charge_status(hid, usage->hid, value);
> > > -
> > > -               if (!handled)
> > > -                       hidinput_update_battery(hid, value);
> > > -
> > > +               hidinput_update_battery(hid, usage->hid, value);
> > >                 return;
> > >         }
> > >
> > > --
> > > 2.50.1
> > >
> > > #/******本邮件及其附件含有小米公司的保密信息,仅限于发送给上面地址中列出的个人或群组。禁止任何其他人以任何形式使用(包括但不限于全部或部分地泄露、复制、或散发)本邮件中的信息。如果您错收了本邮件,请您立即电话或邮件通知发件人并删除本邮件! This e-mail and its attachments contain confidential information from XIAOMI, which is intended only for the person or entity whose address is listed above. Any use of the information contained herein in any way (including, but not limited to, total or partial disclosure, reproduction, or dissemination) by persons other than the intended recipient(s) is prohibited. If you receive this e-mail in error, please notify the sender by phone or email immediately and delete it!******/#
> > #/******本邮件及其附件含有小米公司的保密信息,仅限于发送给上面地址中列出的个人或群组。禁止任何其他人以任何形式使用(包括但不限于全部或部分地泄露、复制、或散发)本邮件中的信息。如果您错收了本邮件,请您立即电话或邮件通知发件人并删除本邮件! This e-mail and its attachments contain confidential information from XIAOMI, which is intended only for the person or entity whose address is listed above. Any use of the information contained herein in any way (including, but not limited to, total or partial disclosure, reproduction, or dissemination) by persons other than the intended recipient(s) is prohibited. If you receive this e-mail in error, please notify the sender by phone or email immediately and delete it!******/#
> #/******本邮件及其附件含有小米公司的保密信息,仅限于发送给上面地址中列出的个人或群组。禁止任何其他人以任何形式使用(包括但不限于全部或部分地泄露、复制、或散发)本邮件中的信息。如果您错收了本邮件,请您立即电话或邮件通知发件人并删除本邮件! This e-mail and its attachments contain confidential information from XIAOMI, which is intended only for the person or entity whose address is listed above. Any use of the information contained herein in any way (including, but not limited to, total or partial disclosure, reproduction, or dissemination) by persons other than the intended recipient(s) is prohibited. If you receive this e-mail in error, please notify the sender by phone or email immediately and delete it!******/#

^ permalink raw reply

* Re: Re [PATCH v11 0/4] Firmware Support for USB-HID Devices and CP2112
From: Willie Thai @ 2025-08-06 11:07 UTC (permalink / raw)
  To: danny.kaehn
  Cc: andriy.shevchenko, arundp, bartosz.golaszewski, bentiss,
	devicetree, dkodihalli, dmitry.torokhov, ethan.twardy, jikos,
	krzk+dt, linux-input, mhn, rastekar, robh, tingkaic, wthai
In-Reply-To: <20250729174951.GB4111945@LNDCL34533.neenah.na.plexus.com>

>> Hi Danny,
>> 
>> I hope this message finds you well.
>> Thank you for the patch set — it’s exactly what we need for the I2C-over-USB feature in our new products.
>> Could you please let us know when we can expect the next version of the patch set?
>> If you've paused work on it, we're happy to take over and continue from where you left off.
>> 
>> Thanks!
>
> Thanks for reaching out!
>
> Apologies, I haven't been working on this in a while, and have only been able
> to intermittently return to attempt to bring it forward.
>
> Feel free to take over and move this forward! I'm not sure what the protocol
> is for that, as far as changelogs and versions and whatnot. If your product's
> timeline for needing this mainlined is not urgent; however, I can prioritize
> coming back to this and having a v12 submitted, likely by the end of next
> week, to remove the overhead needed for you to assume ownership of the
> patchset.
>
> The last several versions of this patchset have all revolved around trying
> to get this change working for ACPI as well as DeviceTree in such a way which
> make the ACPI and DeviceTree interface/binding acceptable to their respective
> maintainers. With this latest version, it seemed that there was not going to
> be any consensus between the two firmware languages, so it seemed an entirely
> different binding/interface and corresponding logic in the device driver
> would be needed. This seems unfortunate, as it seemed the whole purpose of
> the fwnode / device_*() functions was to unify the driver interface to the
> firmware language used... but this is presumably a special case, being almost
> exclusively a device composed of different generic device functions...
>
> Let me know if you plan to take this over and if there's any
> documentation/context/test procedures you would need from me; else I would be
> happy to start moving this forward again now that there is someone waiting
> on it.
>
> Thanks
> 
> Danny Kaehn

Hi Danny,

Thanks for your response !
Currently, your patch is working fine with our feature without ACPI.
We can use it downstream first.
Please let me know if you need any support !

Thanks !

^ permalink raw reply

* Re: [PATCH v12 04/10] pwm: max7360: Add MAX7360 PWM support
From: Mathieu Dubois-Briand @ 2025-08-06 12:07 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
	Dmitry Torokhov, Michael Walle, Mark Brown, Greg Kroah-Hartman,
	Rafael J. Wysocki, Danilo Krummrich, devicetree, linux-kernel,
	linux-gpio, linux-input, linux-pwm, andriy.shevchenko,
	Grégory Clement, Thomas Petazzoni, Andy Shevchenko
In-Reply-To: <2msg7e7q42ocjewv35rytdtxwrfqrndpm2y5ustqeaeodencsd@nfdufgtevxte>

On Fri Aug 1, 2025 at 12:11 PM CEST, Uwe Kleine-König wrote:
> On Tue, Jul 22, 2025 at 06:23:48PM +0200, Mathieu Dubois-Briand wrote:
>> +static int max7360_pwm_round_waveform_tohw(struct pwm_chip *chip,
>> +					   struct pwm_device *pwm,
>> +					   const struct pwm_waveform *wf,
>> +					   void *_wfhw)
>> +{
>> +	struct max7360_pwm_waveform *wfhw = _wfhw;
>> +	u64 duty_steps;
>> +
>> +	/*
>> +	 * Ignore user provided values for period_length_ns and duty_offset_ns:
>> +	 * we only support fixed period of MAX7360_PWM_PERIOD_NS and offset of 0.
>> +	 * Values from 0 to 254 as duty_steps will provide duty cycles of 0/256
>> +	 * to 254/256, while value 255 will provide a duty cycle of 100%.
>> +	 */
>> +	if (wf->duty_length_ns >= MAX7360_PWM_PERIOD_NS) {
>> +		duty_steps = MAX7360_PWM_MAX;
>> +	} else {
>> +		duty_steps = (u32)wf->duty_length_ns * MAX7360_PWM_STEPS / MAX7360_PWM_PERIOD_NS;
>> +		if (duty_steps == MAX7360_PWM_MAX)
>> +			duty_steps = MAX7360_PWM_MAX - 1;
>> +	}
>> +
>> +	wfhw->duty_steps = min(MAX7360_PWM_MAX, duty_steps);
>> +	wfhw->enabled = !!wf->period_length_ns;
>> +
>> +	return 0;
>
> The unconditional return 0 is wrong and testing with PWM_DEBUG enabled
> should tell you that.
>

When you say should, does that mean the current version of PWM core will
tell me that with PWM_DEBUG enabled, or does that mean we should modify
the code so it does show a warning? As I did not see any warning when
specifying a wf->period_length_ns > MAX7360_PWM_PERIOD_NS, even with
PWM_DEBUG enabled.

On the other hand, if I specify a wf->period_length_ns value below
MAX7360_PWM_PERIOD_NS, I indeed get an error:
pwm pwmchip0: Wrong rounding: requested 1000000/1000000 [+0], result 1000000/2000000 [+0]

> I think the right thing to do here is:
>
> 	if (wf->period_length_ns > MAX7360_PWM_PERIOD_NS)
> 		return 1;
> 	else
> 		return 0;

I can definitely do that, but now I'm a bit confused by the meaning of
this return value: is it 0 on success, 1 if some rounding was made,
-errno on error? So I believe I should only return 0 if
wf->period_length_ns == MAX7360_PWM_PERIOD_NS, no?

Or reading this comment on pwm_round_waveform_might_sleep(), maybe we
only have to return 1 if some value is rounded UP. So I believe the test
should be (wf->period_length_ns < MAX7360_PWM_PERIOD_NS).

>  * Returns: 0 on success, 1 if at least one value had to be rounded up or a
>  * negative errno.

This is kinda confirmed by this other comment, in the code checking the
above returned value in __pwm_apply(), even its just typical examples:

> if (err > 0)
> 	/*
> 	 * This signals an invalid request, typically
> 	 * the requested period (or duty_offset) is
> 	 * smaller than possible with the hardware.
> 	 */
> 	return -EINVAL;

So, yeah, sorry, but I'm really confused about what is the correct
return value here.

>
> Otherwise looks fine.
>
> Best regards
> Uwe

Thanks again for your time.

Best regards,
Mathieu

-- 
Mathieu Dubois-Briand, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


^ permalink raw reply

* Re: [PATCH v12 04/10] pwm: max7360: Add MAX7360 PWM support
From: Uwe Kleine-König @ 2025-08-06 14:02 UTC (permalink / raw)
  To: Mathieu Dubois-Briand
  Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
	Dmitry Torokhov, Michael Walle, Mark Brown, Greg Kroah-Hartman,
	Rafael J. Wysocki, Danilo Krummrich, devicetree, linux-kernel,
	linux-gpio, linux-input, linux-pwm, andriy.shevchenko,
	Grégory Clement, Thomas Petazzoni, Andy Shevchenko
In-Reply-To: <DBVBZ48R7DNR.850O5X7MLMEF@bootlin.com>

[-- Attachment #1: Type: text/plain, Size: 3327 bytes --]

On Wed, Aug 06, 2025 at 02:07:15PM +0200, Mathieu Dubois-Briand wrote:
> On Fri Aug 1, 2025 at 12:11 PM CEST, Uwe Kleine-König wrote:
> > On Tue, Jul 22, 2025 at 06:23:48PM +0200, Mathieu Dubois-Briand wrote:
> >> +static int max7360_pwm_round_waveform_tohw(struct pwm_chip *chip,
> >> +					   struct pwm_device *pwm,
> >> +					   const struct pwm_waveform *wf,
> >> +					   void *_wfhw)
> >> +{
> >> +	struct max7360_pwm_waveform *wfhw = _wfhw;
> >> +	u64 duty_steps;
> >> +
> >> +	/*
> >> +	 * Ignore user provided values for period_length_ns and duty_offset_ns:
> >> +	 * we only support fixed period of MAX7360_PWM_PERIOD_NS and offset of 0.
> >> +	 * Values from 0 to 254 as duty_steps will provide duty cycles of 0/256
> >> +	 * to 254/256, while value 255 will provide a duty cycle of 100%.
> >> +	 */
> >> +	if (wf->duty_length_ns >= MAX7360_PWM_PERIOD_NS) {
> >> +		duty_steps = MAX7360_PWM_MAX;
> >> +	} else {
> >> +		duty_steps = (u32)wf->duty_length_ns * MAX7360_PWM_STEPS / MAX7360_PWM_PERIOD_NS;
> >> +		if (duty_steps == MAX7360_PWM_MAX)
> >> +			duty_steps = MAX7360_PWM_MAX - 1;
> >> +	}
> >> +
> >> +	wfhw->duty_steps = min(MAX7360_PWM_MAX, duty_steps);
> >> +	wfhw->enabled = !!wf->period_length_ns;
> >> +
> >> +	return 0;
> >
> > The unconditional return 0 is wrong and testing with PWM_DEBUG enabled
> > should tell you that.
> >
> 
> When you say should, does that mean the current version of PWM core will
> tell me that with PWM_DEBUG enabled, or does that mean we should modify
> the code so it does show a warning? As I did not see any warning when
> specifying a wf->period_length_ns > MAX7360_PWM_PERIOD_NS, even with
> PWM_DEBUG enabled.
> 
> On the other hand, if I specify a wf->period_length_ns value below
> MAX7360_PWM_PERIOD_NS, I indeed get an error:
> pwm pwmchip0: Wrong rounding: requested 1000000/1000000 [+0], result 1000000/2000000 [+0]

Yes, that's how I expect it.

> > I think the right thing to do here is:
> >
> > 	if (wf->period_length_ns > MAX7360_PWM_PERIOD_NS)
> > 		return 1;
> > 	else
> > 		return 0;
> 
> I can definitely do that, but now I'm a bit confused by the meaning of
> this return value: is it 0 on success, 1 if some rounding was made,
> -errno on error? So I believe I should only return 0 if
> wf->period_length_ns == MAX7360_PWM_PERIOD_NS, no?
> 
> Or reading this comment on pwm_round_waveform_might_sleep(), maybe we
> only have to return 1 if some value is rounded UP. So I believe the test
> should be (wf->period_length_ns < MAX7360_PWM_PERIOD_NS).

Right,

	if (wf->period_length_ns < MAX7360_PWM_PERIOD_NS)
		return 1;
	else
		return 0;

So 0 = request could be matched by only rounding down, 1 = request could
be matched but rounding up was needed, negative value = error.

> >  * Returns: 0 on success, 1 if at least one value had to be rounded up or a
> >  * negative errno.
> 
> This is kinda confirmed by this other comment, in the code checking the
> above returned value in __pwm_apply(), even its just typical examples:

pwm_apply() has different rules. (.apply() fails when .period is too
small. This has the downside that finding a valid period is hard. For
that reason the waveform callbacks round up and signal that by returning
1.)

Best regards
Uwe

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply

* Re: [PATCH v12 04/10] pwm: max7360: Add MAX7360 PWM support
From: Mathieu Dubois-Briand @ 2025-08-06 14:36 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Kamel Bouhara, Linus Walleij, Bartosz Golaszewski,
	Dmitry Torokhov, Michael Walle, Mark Brown, Greg Kroah-Hartman,
	Rafael J. Wysocki, Danilo Krummrich, devicetree, linux-kernel,
	linux-gpio, linux-input, linux-pwm, andriy.shevchenko,
	Grégory Clement, Thomas Petazzoni, Andy Shevchenko
In-Reply-To: <praujgmc3c63j6brecp5kwn7tbdd7rcxmrxn67kxhxcr7rpyhw@pfbsgycx4aop>

On Wed Aug 6, 2025 at 4:02 PM CEST, Uwe Kleine-König wrote:
> On Wed, Aug 06, 2025 at 02:07:15PM +0200, Mathieu Dubois-Briand wrote:
>> > I think the right thing to do here is:
>> >
>> > 	if (wf->period_length_ns > MAX7360_PWM_PERIOD_NS)
>> > 		return 1;
>> > 	else
>> > 		return 0;
>> 
>> I can definitely do that, but now I'm a bit confused by the meaning of
>> this return value: is it 0 on success, 1 if some rounding was made,
>> -errno on error? So I believe I should only return 0 if
>> wf->period_length_ns == MAX7360_PWM_PERIOD_NS, no?
>> 
>> Or reading this comment on pwm_round_waveform_might_sleep(), maybe we
>> only have to return 1 if some value is rounded UP. So I believe the test
>> should be (wf->period_length_ns < MAX7360_PWM_PERIOD_NS).
>
> Right,
>
> 	if (wf->period_length_ns < MAX7360_PWM_PERIOD_NS)
> 		return 1;
> 	else
> 		return 0;
>
> So 0 = request could be matched by only rounding down, 1 = request could
> be matched but rounding up was needed, negative value = error.
>

Ok, thanks for the explanation.

I will fix the return value, and a new version should come soon.

Best regards,
Mathieu


-- 
Mathieu Dubois-Briand, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


^ permalink raw reply

* [PATCH 0/3] Add support for Awinic AW86927 haptic driver
From: Griffin Kroah-Hartman @ 2025-08-06 15:10 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Luca Weiss
  Cc: linux-input, devicetree, linux-kernel, linux-arm-msm,
	Griffin Kroah-Hartman

Add devicetree bindings and a driver for the AW86927 haptic driver, and
add it to the devicetree for the Fairphone 5 smartphone.

This driver does not enable all capabilities of the AW86927, features
such as f0 detection, rtp mode, and cont mode are not included.

Note: This is my first driver I have ever worked on so if there is
anything I can do to improve it please let me know!

Signed-off-by: Griffin Kroah-Hartman <griffin.kroah@fairphone.com>
---
Griffin Kroah-Hartman (3):
      dt-bindings: input: Add bindings for Awinic AW86927
      Input: aw86927 - add driver for Awinic AW86927
      arm64: dts: qcom: qcm6490-fairphone-fp5: Add vibrator support

 .../devicetree/bindings/input/awinic,aw86927.yaml  |  48 ++
 arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts |  18 +-
 drivers/input/misc/Kconfig                         |  11 +
 drivers/input/misc/Makefile                        |   1 +
 drivers/input/misc/aw86927.c                       | 841 +++++++++++++++++++++
 5 files changed, 918 insertions(+), 1 deletion(-)
---
base-commit: 3624e9a34b36d64a7037946eda28ae9599363a3b
change-id: 20250804-aw86927-9dddc32fcaec

Best regards,
-- 
Griffin Kroah-Hartman <griffin.kroah@fairphone.com>


^ permalink raw reply

* [PATCH 1/3] dt-bindings: input: Add bindings for Awinic AW86927
From: Griffin Kroah-Hartman @ 2025-08-06 15:10 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Luca Weiss
  Cc: linux-input, devicetree, linux-kernel, linux-arm-msm,
	Griffin Kroah-Hartman
In-Reply-To: <20250806-aw86927-v1-0-23d8a6d0f2b2@fairphone.com>

Add bindings for the Awinic AW86927 haptic chip which can be found in
smartphones.

Signed-off-by: Griffin Kroah-Hartman <griffin.kroah@fairphone.com>
---
 .../devicetree/bindings/input/awinic,aw86927.yaml  | 48 ++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/Documentation/devicetree/bindings/input/awinic,aw86927.yaml b/Documentation/devicetree/bindings/input/awinic,aw86927.yaml
new file mode 100644
index 000000000000..43012f28d9aa
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/awinic,aw86927.yaml
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: (GPL-2.0-only)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/awinic,aw86927.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Awinic AW86927 LRA Haptics driver
+
+maintainers:
+  - Griffin Kroah-Hartman <griffin.kroah@fairphone.com>
+
+properties:
+  compatible:
+    const: awinic,aw86927
+
+  reg:
+    maxItems: 1
+
+  reset-gpios:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - reset-gpios
+  - interrupts
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        vibrator@5a {
+            compatible = "awinic,aw86927";
+            reg = <0x5a>;
+            interrupts-extended = <&tlmm 101 IRQ_TYPE_EDGE_FALLING>;
+            reset-gpios = <&tlmm 100 GPIO_ACTIVE_LOW>;
+        };
+    };

-- 
2.43.0


^ permalink raw reply related

* [PATCH 3/3] arm64: dts: qcom: qcm6490-fairphone-fp5: Add vibrator support
From: Griffin Kroah-Hartman @ 2025-08-06 15:10 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Luca Weiss
  Cc: linux-input, devicetree, linux-kernel, linux-arm-msm,
	Griffin Kroah-Hartman
In-Reply-To: <20250806-aw86927-v1-0-23d8a6d0f2b2@fairphone.com>

Add the required node for haptic playback (Awinic AW86927).

Signed-off-by: Griffin Kroah-Hartman <griffin.kroah@fairphone.com>
---
 arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts b/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts
index 4c6cb4a644e2..9576efdf1e8d 100644
--- a/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts
+++ b/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts
@@ -866,7 +866,16 @@ ocp96011_sbu_mux: endpoint {
 		};
 	};
 
-	/* AW86927FCR haptics @ 5a */
+	vibrator@5a {
+		compatible = "awinic,aw86927";
+		reg = <0x5a>;
+
+		interrupts-extended = <&tlmm 101 IRQ_TYPE_EDGE_FALLING>;
+		reset-gpios = <&tlmm 100 GPIO_ACTIVE_LOW>;
+
+		pinctrl-0 = <&aw86927_int_default>;
+		pinctrl-names = "default";
+	};
 };
 
 &i2c2 {
@@ -1415,6 +1424,13 @@ usb_redrive_1v8_en_default: usb-redrive-1v8-en-default-state {
 		bias-disable;
 		output-high;
 	};
+
+	aw86927_int_default: aw86927-int-default-state {
+		pins = "gpio101";
+		function = "gpio";
+		drive-strength = <2>;
+		bias-pull-up;
+	};
 };
 
 &uart5 {

-- 
2.43.0


^ permalink raw reply related

* [PATCH 2/3] Input: aw86927 - add driver for Awinic AW86927
From: Griffin Kroah-Hartman @ 2025-08-06 15:10 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Luca Weiss
  Cc: linux-input, devicetree, linux-kernel, linux-arm-msm,
	Griffin Kroah-Hartman
In-Reply-To: <20250806-aw86927-v1-0-23d8a6d0f2b2@fairphone.com>

Add support for the I2C-connected Awinic AW86927 LRA haptic driver.

This driver includes a hardcoded sine waveform to be uploaded to the
AW86927's SRAM for haptic playback.
This driver does not currently support all the capabilities of the
AW86927, such as F0 calibration, RTP mode, and CONT mode.

Signed-off-by: Griffin Kroah-Hartman <griffin.kroah@fairphone.com>
---
 drivers/input/misc/Kconfig   |  11 +
 drivers/input/misc/Makefile  |   1 +
 drivers/input/misc/aw86927.c | 841 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 853 insertions(+)

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index f5496ca0c0d2..20a5f552d9f4 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -126,6 +126,17 @@ config INPUT_ATMEL_CAPTOUCH
 	  To compile this driver as a module, choose M here: the
 	  module will be called atmel_captouch.
 
+config INPUT_AW86927
+	tristate "Awinic AW86927 Haptic Driver Support"
+	depends on I2C && INPUT
+	select INPUT_FF_MEMLESS
+	select REGMAP_I2C
+	help
+	  Say Y here if you have an Awinic AW86927 haptic chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called aw86927.
+
 config INPUT_BBNSM_PWRKEY
 	tristate "NXP BBNSM Power Key Driver"
 	depends on ARCH_MXC || COMPILE_TEST
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 6d91804d0a6f..a311a84d1b70 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_INPUT_ATC260X_ONKEY)	+= atc260x-onkey.o
 obj-$(CONFIG_INPUT_ATI_REMOTE2)		+= ati_remote2.o
 obj-$(CONFIG_INPUT_ATLAS_BTNS)		+= atlas_btns.o
 obj-$(CONFIG_INPUT_ATMEL_CAPTOUCH)	+= atmel_captouch.o
+obj-$(CONFIG_INPUT_AW86927)		+= aw86927.o
 obj-$(CONFIG_INPUT_BBNSM_PWRKEY)	+= nxp-bbnsm-pwrkey.o
 obj-$(CONFIG_INPUT_BMA150)		+= bma150.o
 obj-$(CONFIG_INPUT_CM109)		+= cm109.o
diff --git a/drivers/input/misc/aw86927.c b/drivers/input/misc/aw86927.c
new file mode 100644
index 000000000000..d08fafbde124
--- /dev/null
+++ b/drivers/input/misc/aw86927.c
@@ -0,0 +1,841 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Griffin Kroah-Hartman <griffin.kroah@fairphone.com>
+ *
+ * Partially based on vendor driver:
+ *	Copyright (c) 2021 AWINIC Technology CO., LTD
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#define AW86927_RSTCFG				(0x00)
+#define AW86927_RSTCFG_SOFTRST			(0xaa)
+
+#define AW86927_SYSINT				(0x02)
+#define AW86927_SYSINT_BST_SCPI			BIT(7)
+#define AW86927_SYSINT_BST_OVPI			BIT(6)
+#define AW86927_SYSINT_UVLI			BIT(5)
+#define AW86927_SYSINT_FF_AEI			BIT(4)
+#define AW86927_SYSINT_FF_AFI			BIT(3)
+#define AW86927_SYSINT_OCDI			BIT(2)
+#define AW86927_SYSINT_OTI			BIT(1)
+#define AW86927_SYSINT_DONEI			BIT(0)
+
+#define AW86927_SYSINTM				(0x03)
+#define AW86927_SYSINTM_BST_OVPM		BIT(6)
+#define AW86927_SYSINTM_FF_AEM			BIT(4)
+#define AW86927_SYSINTM_FF_AFM			BIT(3)
+#define AW86927_SYSINTM_DONEM			BIT(0)
+
+#define AW86927_PLAYCFG1			(0x06)
+#define AW86927_PLAYCFG1_BST_MODE_MASK		GENMASK(7, 7)
+#define AW86927_PLAYCFG1_BST_MODE_BYPASS	(0)
+#define AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK	GENMASK(6, 0)
+#define AW86927_PLAYCFG1_BST_8500MV		(0x50)
+
+#define AW86927_PLAYCFG2			(0x07)
+
+#define AW86927_PLAYCFG3			(0x08)
+#define AW86927_PLAYCFG3_AUTO_BST_MASK		GENMASK(4, 4)
+#define AW86927_PLAYCFG3_AUTO_BST_ENABLE	(1)
+#define AW86927_PLAYCFG3_AUTO_BST_DISABLE	(0)
+#define AW86927_PLAYCFG3_PLAY_MODE_MASK		GENMASK(1, 0)
+#define AW86927_PLAYCFG3_PLAY_MODE_RAM		(0)
+
+#define AW86927_PLAYCFG4			(0x09)
+#define AW86927_PLAYCFG4_STOP			BIT(1)
+#define AW86927_PLAYCFG4_GO			BIT(0)
+
+#define AW86927_WAVCFG1				(0x0a)
+#define AW86927_WAVCFG1_WAVSEQ1_MASK		GENMASK(6, 0)
+
+#define AW86927_WAVCFG2				(0x0b)
+#define AW86927_WAVCFG2_WAVSEQ2_MASK		GENMASK(6, 0)
+
+#define AW86927_WAVCFG9				(0x12)
+#define AW86927_WAVCFG9_SEQ1LOOP_MASK		GENMASK(7, 4)
+#define AW86927_WAVCFG9_SEQ1LOOP_INFINITELY	(0x0f)
+
+#define AW86927_CONTCFG1			(0x18)
+#define AW86927_CONTCFG1_BRK_BST_MD_MASK	GENMASK(6, 6)
+
+#define AW86927_CONTCFG5			(0x1c)
+#define AW86927_CONTCFG5_BST_BRK_GAIN_MASK	GENMASK(7, 4)
+#define AW86927_CONTCFG5_BRK_GAIN_MASK		GENMASK(3, 0)
+
+#define AW86927_CONTCFG10			(0x21)
+#define AW86927_CONTCFG10_BRK_TIME_MASK		GENMASK(7, 0)
+#define AW86927_CONTCFG10_BRK_TIME_DEFAULT	(8)
+
+#define AW86927_CONTCFG13			(0x24)
+#define AW86927_CONTCFG13_TSET_MASK		GENMASK(7, 4)
+#define AW86927_CONTCFG13_BEME_SET_MASK		GENMASK(3, 0)
+
+#define AW86927_BASEADDRH			(0x2d)
+#define AW86927_BASEADDRL			(0x2e)
+
+#define AW86927_GLBRD5				(0x3f)
+#define AW86927_GLBRD5_STATE_MASK		GENMASK(3, 0)
+#define AW86927_GLBRD5_STATE_STANDBY		(0)
+
+#define AW86927_RAMADDRH			(0x40)
+#define AW86927_RAMADDRL			(0x41)
+#define AW86927_RAMDATA				(0x42)
+
+#define AW86927_SYSCTRL3			(0x45)
+#define AW86927_SYSCTRL3_STANDBY_MASK           GENMASK(5, 5)
+#define AW86927_SYSCTRL3_STANDBY_ON             (1)
+#define AW86927_SYSCTRL3_STANDBY_OFF            (0)
+#define AW86927_SYSCTRL3_EN_RAMINIT_MASK        GENMASK(2, 2)
+#define AW86927_SYSCTRL3_EN_RAMINIT_ON          (1)
+#define AW86927_SYSCTRL3_EN_RAMINIT_OFF         (0)
+
+#define AW86927_SYSCTRL4			(0x46)
+#define AW86927_SYSCTRL4_WAVDAT_MODE_MASK	GENMASK(6, 5)
+#define AW86927_SYSCTRL4_WAVDAT_24K		(0)
+#define AW86927_SYSCTRL4_INT_EDGE_MODE_MASK	GENMASK(4, 4)
+#define AW86927_SYSCTRL4_INT_EDGE_MODE_POS	(0)
+#define AW86927_SYSCTRL4_INT_MODE_MASK		GENMASK(3, 3)
+#define AW86927_SYSCTRL4_INT_MODE_EDGE		(1)
+#define AW86927_SYSCTRL4_GAIN_BYPASS_MASK	GENMASK(0, 0)
+
+#define AW86927_PWMCFG1				(0x48)
+#define AW86927_PWMCFG1_PRC_EN_MASK		GENMASK(7, 7)
+#define AW86927_PWMCFG1_PRC_DISABLE		(0)
+
+#define AW86927_PWMCFG3				(0x4a)
+#define AW86927_PWMCFG3_PR_EN_MASK		GENMASK(7, 7)
+#define AW86927_PWMCFG3_PRCTIME_MASK		GENMASK(6, 0)
+
+#define AW86927_PWMCFG4				(0x4b)
+#define AW86927_PWMCFG4_PRTIME_MASK		GENMASK(7, 0)
+
+#define AW86927_VBATCTRL			(0x4c)
+#define AW86927_VBATCTRL_VBAT_MODE_MASK		GENMASK(6, 6)
+#define AW86927_VBATCTRL_VBAT_MODE_SW		(0)
+
+#define AW86927_DETCFG1				(0x4d)
+#define AW86927_DETCFG1_DET_GO_MASK		GENMASK(1, 0)
+#define AW86927_DETCFG1_DET_GO_DET_SEQ0		(1)
+#define AW86927_DETCFG1_DET_GO_NA		(0)
+
+#define AW86927_DETCFG2				(0x4e)
+#define AW86927_DETCFG2_DET_SEQ0_MASK		GENMASK(6, 3)
+#define AW86927_DETCFG2_DET_SEQ0_VBAT		(0)
+#define AW86927_DETCFG2_D2S_GAIN_MASK		GENMASK(2, 0)
+#define AW86927_DETCFG2_D2S_GAIN_10		(4)
+
+#define AW86927_CHIPIDH				(0x57)
+#define AW86927_CHIPIDL				(0x58)
+#define AW86927_CHIPID				(0x9270)
+
+#define AW86927_TMCFG				(0x5b)
+#define AW86927_TMCFG_UNLOCK			(0x7d)
+#define AW86927_TMCFG_LOCK			(0x00)
+
+#define AW86927_ANACFG11			(0x70)
+#define AW86927_ANACFG12			(0x71)
+#define AW86927_ANACFG12_BST_SKIP_MASK		GENMASK(7, 7)
+#define AW86927_ANACFG12_BST_SKIP_SHUTDOWN	(1)
+
+#define AW86927_ANACFG13			(0x72)
+#define AW86927_ANACFG13_BST_PC_MASK		GENMASK(7, 4)
+#define AW86927_ANACFG13_BST_PEAKCUR_3P45A	(6)
+
+#define AW86927_ANACFG15			(0x74)
+#define AW86927_ANACFG15_BST_PEAK_MODE_MASK	GENMASK(7, 7)
+#define AW86927_ANACFG15_BST_PEAK_BACK		(1)
+
+#define AW86927_ANACFG16			(0x75)
+#define AW86927_ANACFG16_BST_SRC_MASK		GENMASK(4, 4)
+#define AW86927_ANACFG16_BST_SRC_3NS		(0)
+
+/* default value of base addr */
+#define AW86927_RAM_BASE_ADDR			(0x800)
+#define AW86927_BASEADDRH_VAL			(0x08)
+#define AW86927_BASEADDRL_VAL			(0x00)
+
+enum aw86927_work_mode {
+	AW86927_STANDBY_MODE,
+	AW86927_RAM_MODE,
+};
+
+struct aw86927_data {
+	struct work_struct play_work;
+	struct device *dev;
+	struct input_dev *input_dev;
+	struct i2c_client *client;
+	struct regmap *regmap;
+	struct gpio_desc *reset_gpio;
+	bool running;
+};
+
+static const struct regmap_config aw86927_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.cache_type = REGCACHE_NONE,
+	.max_register = 0x80,
+};
+
+/*
+ * Sine wave representing the magnitude of the drive to be used.
+ * Data is encoded in two's complement.
+ *   round(84 * sin(x / 16.25))
+ */
+static const uint8_t aw86927_waveform[] = {
+	0x00, 0x05, 0x0a, 0x0f, 0x14, 0x1a, 0x1f, 0x23, 0x28, 0x2d, 0x31, 0x35,
+	0x39, 0x3d, 0x41, 0x44, 0x47, 0x4a, 0x4c, 0x4f, 0x51, 0x52, 0x53, 0x54,
+	0x55, 0x55, 0x55, 0x55, 0x55, 0x54, 0x52, 0x51, 0x4f, 0x4d, 0x4a, 0x47,
+	0x44, 0x41, 0x3d, 0x3a, 0x36, 0x31, 0x2d, 0x28, 0x24, 0x1f, 0x1a, 0x15,
+	0x10, 0x0a, 0x05, 0x00, 0xfc, 0xf6, 0xf1, 0xec, 0xe7, 0xe2, 0xdd, 0xd8,
+	0xd4, 0xcf, 0xcb, 0xc7, 0xc3, 0xbf, 0xbc, 0xb9, 0xb6, 0xb4, 0xb1, 0xb0,
+	0xae, 0xad, 0xac, 0xab, 0xab, 0xab, 0xab, 0xab, 0xac, 0xae, 0xaf, 0xb1,
+	0xb3, 0xb6, 0xb8, 0xbc, 0xbf, 0xc2, 0xc6, 0xca, 0xce, 0xd3, 0xd7, 0xdc,
+	0xe1, 0xe6, 0xeb, 0xf0, 0xf5, 0xfb
+};
+
+struct aw86927_sram_waveform_header {
+	uint8_t version;
+	struct {
+		__be16 start_address;
+		__be16 end_address;
+	} __packed waveform_address[1];
+} __packed;
+
+static const struct aw86927_sram_waveform_header sram_waveform_header = {
+	.version = 0x01,
+	.waveform_address = {
+		/* Simple sine wave defined above */
+		{
+			.start_address = cpu_to_be16(AW86927_RAM_BASE_ADDR +
+				sizeof(struct aw86927_sram_waveform_header)),
+			.end_address = cpu_to_be16(AW86927_RAM_BASE_ADDR +
+				sizeof(struct aw86927_sram_waveform_header) +
+				ARRAY_SIZE(aw86927_waveform) - 1),
+		}
+	}
+};
+
+static int aw86927_wait_enter_standby(struct aw86927_data *haptics)
+{
+	unsigned int reg_val;
+	int err;
+
+	err = regmap_read_poll_timeout(haptics->regmap,
+			AW86927_GLBRD5, reg_val,
+			(FIELD_GET(AW86927_GLBRD5_STATE_MASK, reg_val) == AW86927_GLBRD5_STATE_STANDBY),
+			2500, 2500 * 100);
+
+	if (err)
+		dev_err(haptics->dev, "did not enter standby: %d\n", err);
+	return err;
+}
+
+static int aw86927_play_mode(struct aw86927_data *haptics, uint8_t play_mode)
+{
+	int err;
+
+	switch (play_mode) {
+	case AW86927_STANDBY_MODE:
+		/* Briefly toggle standby, then toggle back to standby off */
+		err = regmap_update_bits(haptics->regmap,
+				AW86927_SYSCTRL3,
+				AW86927_SYSCTRL3_STANDBY_MASK,
+				FIELD_PREP(AW86927_SYSCTRL3_STANDBY_MASK,
+					   AW86927_SYSCTRL3_STANDBY_ON));
+		if (err)
+			return err;
+
+		err = regmap_update_bits(haptics->regmap,
+				AW86927_SYSCTRL3,
+				AW86927_SYSCTRL3_STANDBY_MASK,
+				FIELD_PREP(AW86927_SYSCTRL3_STANDBY_MASK,
+					   AW86927_SYSCTRL3_STANDBY_OFF));
+		if (err)
+			return err;
+		break;
+	case AW86927_RAM_MODE:
+		err = regmap_update_bits(haptics->regmap,
+				AW86927_PLAYCFG3,
+				AW86927_PLAYCFG3_PLAY_MODE_MASK,
+				FIELD_PREP(AW86927_PLAYCFG3_PLAY_MODE_MASK,
+					   AW86927_PLAYCFG3_PLAY_MODE_RAM));
+		if (err)
+			return err;
+
+		err = regmap_update_bits(haptics->regmap,
+				AW86927_PLAYCFG1,
+				AW86927_PLAYCFG1_BST_MODE_MASK,
+				FIELD_PREP(AW86927_PLAYCFG1_BST_MODE_MASK,
+					   AW86927_PLAYCFG1_BST_MODE_BYPASS));
+		if (err)
+			return err;
+
+		err = regmap_update_bits(haptics->regmap,
+				AW86927_VBATCTRL,
+				AW86927_VBATCTRL_VBAT_MODE_MASK,
+				FIELD_PREP(AW86927_VBATCTRL_VBAT_MODE_MASK,
+					   AW86927_VBATCTRL_VBAT_MODE_SW));
+		if (err)
+			return err;
+		break;
+	}
+	return 0;
+}
+
+static int aw86927_stop(struct aw86927_data *haptics)
+{
+	int err;
+
+	err = regmap_write(haptics->regmap, AW86927_PLAYCFG4, AW86927_PLAYCFG4_STOP);
+	if (err) {
+		dev_err(haptics->dev, "Failed to stop playback: %d\n", err);
+		return err;
+	}
+
+	err = aw86927_wait_enter_standby(haptics);
+	if (err) {
+		dev_err(haptics->dev, "Failed to enter standby, trying to force it\n");
+		err = aw86927_play_mode(haptics, AW86927_STANDBY_MODE);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
+static int aw86927_haptics_play(struct input_dev *dev, void *data, struct ff_effect *effect)
+{
+	struct aw86927_data *haptics = input_get_drvdata(dev);
+	int level;
+
+	level = effect->u.rumble.strong_magnitude;
+	if (!level)
+		level = effect->u.rumble.weak_magnitude;
+
+	/* If already running, don't restart playback */
+	if (haptics->running && level)
+		return 0;
+
+	haptics->running = level;
+	schedule_work(&haptics->play_work);
+
+	return 0;
+}
+
+static int aw86927_play_sine(struct aw86927_data *haptics)
+{
+	int err;
+
+	err = aw86927_stop(haptics);
+	if (err)
+		return err;
+
+	err = aw86927_play_mode(haptics, AW86927_RAM_MODE);
+	if (err)
+		return err;
+
+	err = regmap_update_bits(haptics->regmap, AW86927_PLAYCFG3,
+			AW86927_PLAYCFG3_AUTO_BST_MASK,
+			FIELD_PREP(AW86927_PLAYCFG3_AUTO_BST_MASK,
+				   AW86927_PLAYCFG3_AUTO_BST_ENABLE));
+	if (err)
+		return err;
+
+	/* Set waveseq 1 to the first wave */
+	err = regmap_update_bits(haptics->regmap, AW86927_WAVCFG1,
+			AW86927_WAVCFG1_WAVSEQ1_MASK,
+			FIELD_PREP(AW86927_WAVCFG1_WAVSEQ1_MASK,
+				   1));
+	if (err)
+		return err;
+
+	/* set wavseq 2 to zero */
+	err = regmap_update_bits(haptics->regmap, AW86927_WAVCFG2,
+			AW86927_WAVCFG2_WAVSEQ2_MASK,
+			FIELD_PREP(AW86927_WAVCFG2_WAVSEQ2_MASK,
+				   0));
+	if (err)
+		return err;
+
+	err = regmap_update_bits(haptics->regmap,
+			AW86927_WAVCFG9,
+			AW86927_WAVCFG9_SEQ1LOOP_MASK,
+			FIELD_PREP(AW86927_WAVCFG9_SEQ1LOOP_MASK,
+				   AW86927_WAVCFG9_SEQ1LOOP_INFINITELY));
+	if (err)
+		return err;
+
+	/* set gain to value lower than 0x80 to avoid distorted playback */
+	err = regmap_write(haptics->regmap, AW86927_PLAYCFG2, 0x7c);
+	if (err)
+		return err;
+
+	/* Start playback */
+	return regmap_write(haptics->regmap, AW86927_PLAYCFG4, AW86927_PLAYCFG4_GO);
+}
+
+static void aw86927_close(struct input_dev *input)
+{
+	struct aw86927_data *haptics = input_get_drvdata(input);
+	struct device *dev = &haptics->client->dev;
+	int err;
+
+	cancel_work_sync(&haptics->play_work);
+
+	err = aw86927_stop(haptics);
+	if (err)
+		dev_err(dev, "Failed to close the Driver: %d\n", err);
+}
+
+static void aw86927_haptics_play_work(struct work_struct *work)
+{
+	struct aw86927_data *haptics =
+		container_of(work, struct aw86927_data, play_work);
+	struct device *dev = &haptics->client->dev;
+	int err;
+
+	if (haptics->running)
+		err = aw86927_play_sine(haptics);
+	else
+		err = aw86927_stop(haptics);
+
+	if (err)
+		dev_err(dev, "Failed to execute work command: %d\n", err);
+}
+
+static void aw86927_hw_reset(struct aw86927_data *haptics)
+{
+	/* Assert reset */
+	gpiod_set_value_cansleep(haptics->reset_gpio, 1);
+	/* Wait ~1ms */
+	usleep_range(1000, 2000);
+	/* Deassert reset */
+	gpiod_set_value_cansleep(haptics->reset_gpio, 0);
+	/* Wait ~8ms until I2C is accessible */
+	usleep_range(8000, 8500);
+}
+
+static int aw86927_haptic_init(struct aw86927_data *haptics)
+{
+	int err;
+
+	err = regmap_update_bits(haptics->regmap,
+			AW86927_SYSCTRL4,
+			AW86927_SYSCTRL4_WAVDAT_MODE_MASK,
+			FIELD_PREP(AW86927_SYSCTRL4_WAVDAT_MODE_MASK,
+				   AW86927_SYSCTRL4_WAVDAT_24K));
+	if (err)
+		return err;
+
+	/* enable gain bypass */
+	err = regmap_update_bits(haptics->regmap,
+			AW86927_SYSCTRL4,
+			AW86927_SYSCTRL4_GAIN_BYPASS_MASK,
+			FIELD_PREP(AW86927_SYSCTRL4_GAIN_BYPASS_MASK,
+				   0x01));
+	if (err)
+		return err;
+
+	err = regmap_write(haptics->regmap,
+			AW86927_TMCFG,
+			AW86927_TMCFG_UNLOCK);
+	if (err)
+		return err;
+
+	err = regmap_write(haptics->regmap,
+			AW86927_ANACFG11,
+			0x0f);
+	if (err)
+		return err;
+
+	err = regmap_update_bits(haptics->regmap,
+			AW86927_ANACFG12,
+			AW86927_ANACFG12_BST_SKIP_MASK,
+			FIELD_PREP(AW86927_ANACFG12_BST_SKIP_MASK,
+				   AW86927_ANACFG12_BST_SKIP_SHUTDOWN));
+	if (err)
+		return err;
+
+	err = regmap_update_bits(haptics->regmap,
+			AW86927_ANACFG15,
+			AW86927_ANACFG15_BST_PEAK_MODE_MASK,
+			FIELD_PREP(AW86927_ANACFG15_BST_PEAK_MODE_MASK,
+				   AW86927_ANACFG15_BST_PEAK_BACK));
+	if (err)
+		return err;
+
+	err = regmap_update_bits(haptics->regmap,
+			AW86927_ANACFG16,
+			AW86927_ANACFG16_BST_SRC_MASK,
+			FIELD_PREP(AW86927_ANACFG16_BST_SRC_MASK,
+				   AW86927_ANACFG16_BST_SRC_3NS));
+	if (err)
+		return err;
+
+	err = regmap_write(haptics->regmap,
+			AW86927_TMCFG,
+			AW86927_TMCFG_LOCK);
+	if (err)
+		return err;
+
+	err = regmap_update_bits(haptics->regmap,
+			AW86927_CONTCFG1,
+			AW86927_CONTCFG1_BRK_BST_MD_MASK,
+			FIELD_PREP(AW86927_CONTCFG1_BRK_BST_MD_MASK,
+				   0x00));
+	if (err)
+		return err;
+
+	err = regmap_write(haptics->regmap,
+			AW86927_CONTCFG5,
+			FIELD_PREP(AW86927_CONTCFG5_BST_BRK_GAIN_MASK, 0x05) |
+			FIELD_PREP(AW86927_CONTCFG5_BRK_GAIN_MASK, 0x08));
+	if (err)
+		return err;
+
+	err = regmap_update_bits(haptics->regmap, AW86927_CONTCFG10,
+			AW86927_CONTCFG10_BRK_TIME_MASK,
+			FIELD_PREP(AW86927_CONTCFG10_BRK_TIME_MASK,
+				   AW86927_CONTCFG10_BRK_TIME_DEFAULT));
+	if (err)
+		return err;
+
+	err = regmap_write(haptics->regmap,
+			AW86927_CONTCFG13,
+			FIELD_PREP(AW86927_CONTCFG13_TSET_MASK, 0x06) |
+			FIELD_PREP(AW86927_CONTCFG13_BEME_SET_MASK, 0x02));
+	if (err)
+		return err;
+
+	err = regmap_update_bits(haptics->regmap,
+			AW86927_DETCFG2,
+			AW86927_DETCFG2_D2S_GAIN_MASK,
+			FIELD_PREP(AW86927_DETCFG2_D2S_GAIN_MASK,
+				   AW86927_DETCFG2_D2S_GAIN_10));
+	if (err)
+		return err;
+
+	err = regmap_update_bits(haptics->regmap,
+			AW86927_PWMCFG1,
+			AW86927_PWMCFG1_PRC_EN_MASK,
+			FIELD_PREP(AW86927_PWMCFG1_PRC_EN_MASK,
+				   AW86927_PWMCFG1_PRC_DISABLE));
+	if (err)
+		return err;
+
+	err = regmap_write(haptics->regmap,
+			AW86927_PWMCFG3,
+			FIELD_PREP(AW86927_PWMCFG3_PR_EN_MASK, 0x01) |
+			FIELD_PREP(AW86927_PWMCFG3_PRCTIME_MASK, 0x3f));
+	if (err)
+		return err;
+
+	err = regmap_update_bits(haptics->regmap,
+			AW86927_PWMCFG4,
+			AW86927_PWMCFG4_PRTIME_MASK,
+			FIELD_PREP(AW86927_PWMCFG4_PRTIME_MASK,
+				   0x32));
+	if (err)
+		return err;
+
+	err = regmap_write(haptics->regmap,
+			AW86927_TMCFG,
+			AW86927_TMCFG_UNLOCK);
+	if (err)
+		return err;
+
+	err = regmap_update_bits(haptics->regmap,
+			AW86927_ANACFG13,
+			AW86927_ANACFG13_BST_PC_MASK,
+			FIELD_PREP(AW86927_ANACFG13_BST_PC_MASK,
+				   AW86927_ANACFG13_BST_PEAKCUR_3P45A));
+	if (err)
+		return err;
+
+	err = regmap_write(haptics->regmap,
+			AW86927_TMCFG,
+			AW86927_TMCFG_LOCK);
+	if (err)
+		return err;
+
+	err = regmap_update_bits(haptics->regmap,
+			AW86927_PLAYCFG1,
+			AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK,
+			FIELD_PREP(AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK,
+				   AW86927_PLAYCFG1_BST_8500MV));
+	if (err)
+		return err;
+
+	return regmap_update_bits(haptics->regmap,
+			AW86927_PLAYCFG3,
+			AW86927_PLAYCFG3_AUTO_BST_MASK,
+			FIELD_PREP(AW86927_PLAYCFG3_AUTO_BST_MASK,
+				   AW86927_PLAYCFG3_AUTO_BST_DISABLE));
+}
+
+static int aw86927_ram_init(struct aw86927_data *haptics)
+{
+	int err;
+
+	err = aw86927_wait_enter_standby(haptics);
+	if (err)
+		return err;
+
+	/* Enable SRAM init */
+	err = regmap_update_bits(haptics->regmap,
+			AW86927_SYSCTRL3,
+			AW86927_SYSCTRL3_EN_RAMINIT_MASK,
+			FIELD_PREP(AW86927_SYSCTRL3_EN_RAMINIT_MASK,
+				   AW86927_SYSCTRL3_EN_RAMINIT_ON));
+
+	/* Set base address for the start of the SRAM waveforms */
+	err = regmap_write(haptics->regmap,
+			   AW86927_BASEADDRH,
+			   AW86927_BASEADDRH_VAL);
+	if (err)
+		return err;
+
+	err = regmap_write(haptics->regmap,
+			   AW86927_BASEADDRL,
+			   AW86927_BASEADDRL_VAL);
+	if (err)
+		return err;
+
+	/* Set start of SRAM, before the data is written it will be the same as the base */
+	err = regmap_write(haptics->regmap,
+			   AW86927_RAMADDRH,
+			   AW86927_BASEADDRH_VAL);
+	if (err)
+		return err;
+
+	err = regmap_write(haptics->regmap,
+			   AW86927_RAMADDRL,
+			   AW86927_BASEADDRL_VAL);
+	if (err)
+		return err;
+
+	/* Write waveform header to SRAM */
+	err = regmap_noinc_write(haptics->regmap, AW86927_RAMDATA,
+				 &sram_waveform_header, sizeof(sram_waveform_header));
+
+	/* Write waveform to SRAM */
+	err = regmap_noinc_write(haptics->regmap, AW86927_RAMDATA,
+				 aw86927_waveform, ARRAY_SIZE(aw86927_waveform));
+	if (err)
+		return err;
+
+	err = regmap_update_bits(haptics->regmap,
+			AW86927_DETCFG2,
+			AW86927_DETCFG2_DET_SEQ0_MASK,
+			FIELD_PREP(AW86927_DETCFG2_DET_SEQ0_MASK,
+				   AW86927_DETCFG2_DET_SEQ0_VBAT));
+	if (err)
+		return err;
+
+	err = regmap_update_bits(haptics->regmap,
+			AW86927_DETCFG1,
+			AW86927_DETCFG1_DET_GO_MASK,
+			FIELD_PREP(AW86927_DETCFG1_DET_GO_MASK,
+				   AW86927_DETCFG1_DET_GO_DET_SEQ0));
+	if (err)
+		return err;
+
+	usleep_range(3000, 3500);
+
+	err = regmap_update_bits(haptics->regmap,
+			AW86927_DETCFG1,
+			AW86927_DETCFG1_DET_GO_MASK,
+			FIELD_PREP(AW86927_DETCFG1_DET_GO_MASK,
+				   AW86927_DETCFG1_DET_GO_NA));
+	if (err)
+		return err;
+
+	/* Disable SRAM init */
+	return regmap_update_bits(haptics->regmap,
+			AW86927_SYSCTRL3,
+			AW86927_SYSCTRL3_EN_RAMINIT_MASK,
+			FIELD_PREP(AW86927_SYSCTRL3_EN_RAMINIT_MASK,
+				   AW86927_SYSCTRL3_EN_RAMINIT_OFF));
+}
+
+static irqreturn_t aw86927_irq(int irq, void *data)
+{
+	struct aw86927_data *haptics = data;
+	struct device *dev = &haptics->client->dev;
+	unsigned int reg_val;
+	int err;
+
+	err = regmap_read(haptics->regmap, AW86927_SYSINT, &reg_val);
+	if (err) {
+		dev_err(dev, "Failed to read SYSINT register: %d\n", err);
+		return IRQ_NONE;
+	}
+
+	if (reg_val & AW86927_SYSINT_BST_SCPI)
+		dev_err(dev, "Received a Short Circuit Protection interrupt\n");
+	if (reg_val & AW86927_SYSINT_BST_OVPI)
+		dev_err(dev, "Received an Over Voltage Protection interrupt\n");
+	if (reg_val & AW86927_SYSINT_UVLI)
+		dev_err(dev, "Received an Under Voltage Lock Out interrupt\n");
+	if (reg_val & AW86927_SYSINT_OCDI)
+		dev_err(dev, "Received an Over Current interrupt\n");
+	if (reg_val & AW86927_SYSINT_OTI)
+		dev_err(dev, "Received an Over Temperature interrupt\n");
+
+	if (reg_val & AW86927_SYSINT_DONEI)
+		dev_dbg(dev, "Chip playback done!\n");
+	if (reg_val & AW86927_SYSINT_FF_AFI)
+		dev_dbg(dev, "The RTP mode FIFO is almost full!\n");
+	if (reg_val & AW86927_SYSINT_FF_AEI)
+		dev_dbg(dev, "The RTP mode FIFO is almost empty!\n");
+
+	return IRQ_HANDLED;
+}
+
+static int aw86927_detect(struct aw86927_data *haptics)
+{
+	__be16 read_buf;
+	u16 chip_id;
+	int err;
+
+	err = regmap_bulk_read(haptics->regmap, AW86927_CHIPIDH, &read_buf, 2);
+	if (err)
+		return dev_err_probe(haptics->dev, err, "Failed to read CHIPID registers\n");
+
+	chip_id = be16_to_cpu(read_buf);
+
+	if (chip_id != AW86927_CHIPID) {
+		dev_err(haptics->dev, "Unexpected CHIPID value 0x%x\n", chip_id);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int aw86927_probe(struct i2c_client *client)
+{
+	struct aw86927_data *haptics;
+	unsigned int read_buf;
+	int err;
+
+	haptics = devm_kzalloc(&client->dev, sizeof(struct aw86927_data), GFP_KERNEL);
+	if (!haptics)
+		return -ENOMEM;
+
+	haptics->dev = &client->dev;
+	haptics->client = client;
+
+	i2c_set_clientdata(client, haptics);
+	dev_set_drvdata(&client->dev, haptics);
+
+	haptics->regmap = devm_regmap_init_i2c(client, &aw86927_regmap_config);
+	if (IS_ERR(haptics->regmap))
+		return dev_err_probe(haptics->dev, PTR_ERR(haptics->regmap),
+					"Failed to allocate register map\n");
+
+	haptics->input_dev = devm_input_allocate_device(haptics->dev);
+	if (!haptics->input_dev)
+		return -ENOMEM;
+
+	haptics->reset_gpio = devm_gpiod_get(haptics->dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(haptics->reset_gpio))
+		return dev_err_probe(haptics->dev, PTR_ERR(haptics->reset_gpio),
+				     "Failed to get reset gpio\n");
+
+	/* Hardware reset */
+	aw86927_hw_reset(haptics);
+
+	/* Software reset */
+	err = regmap_write(haptics->regmap, AW86927_RSTCFG, AW86927_RSTCFG_SOFTRST);
+	if (err)
+		return dev_err_probe(haptics->dev, PTR_ERR(haptics->regmap),
+					"Failed Software reset\n");
+
+	/* Wait ~3s until I2C is accessible */
+	usleep_range(3000, 3500);
+
+	err = aw86927_detect(haptics);
+	if (err)
+		return dev_err_probe(haptics->dev, err, "Failed to find chip\n");
+
+	/* IRQ config */
+	err = regmap_write(haptics->regmap, AW86927_SYSCTRL4,
+			FIELD_PREP(AW86927_SYSCTRL4_INT_MODE_MASK,
+				   AW86927_SYSCTRL4_INT_MODE_EDGE) |
+			FIELD_PREP(AW86927_SYSCTRL4_INT_EDGE_MODE_MASK,
+				   AW86927_SYSCTRL4_INT_EDGE_MODE_POS));
+	if (err)
+		return err;
+
+	err = regmap_write(haptics->regmap, AW86927_SYSINTM,
+			AW86927_SYSINTM_BST_OVPM |
+			AW86927_SYSINTM_FF_AEM |
+			AW86927_SYSINTM_FF_AFM |
+			AW86927_SYSINTM_DONEM);
+	if (err)
+		return err;
+
+	err = devm_request_threaded_irq(haptics->dev, client->irq, NULL,
+					aw86927_irq, IRQF_ONESHOT, NULL, haptics);
+
+	INIT_WORK(&haptics->play_work, aw86927_haptics_play_work);
+
+	haptics->input_dev->name = "aw86927-haptics";
+	haptics->input_dev->close = aw86927_close;
+
+	input_set_drvdata(haptics->input_dev, haptics);
+	input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
+
+	err = input_ff_create_memless(haptics->input_dev, NULL,
+			aw86927_haptics_play);
+	if (err)
+		return dev_err_probe(haptics->dev, err, "Failed to create FF dev\n");
+
+	/* Set up registers */
+	err = aw86927_play_mode(haptics, AW86927_STANDBY_MODE);
+	if (err)
+		return err;
+
+	err = aw86927_haptic_init(haptics);
+	if (err)
+		return dev_err_probe(haptics->dev, err, "Haptic init failed\n");
+
+	/* RAM init, upload the waveform for playback */
+	err = aw86927_ram_init(haptics);
+	if (err)
+		return dev_err_probe(haptics->dev, err, "Failed to init aw86927 sram\n");
+
+	err = input_register_device(haptics->input_dev);
+	if (err)
+		return dev_err_probe(haptics->dev, err, "Failed to register input device\n");
+
+	return 0;
+}
+
+static const struct of_device_id aw86927_of_id[] = {
+	{ .compatible = "awinic,aw86927" },
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, aw86927_of_id);
+
+static struct i2c_driver aw86927_driver = {
+	.driver = {
+		.name = "aw86927-haptics",
+		.of_match_table = aw86927_of_id,
+	},
+	.probe = aw86927_probe,
+};
+
+module_i2c_driver(aw86927_driver);
+
+MODULE_AUTHOR("Griffin Kroah-Hartman <griffin.kroah@fairphone.com>");
+MODULE_DESCRIPTION("AWINIC AW86927 LRA Haptic Driver");
+MODULE_LICENSE("GPL");

-- 
2.43.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox