mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/3] AW86224 haptic driver
@ 2026-10-27 22:01 Gergo Koteles
  2026-10-27 22:01 ` [PATCH 1/3] dt-bindings: input: Add compatibility for Awinic AW86224 Gergo Koteles
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Gergo Koteles @ 2026-10-27 22:01 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Arnd Bergmann, Gergo Koteles,
	Griffin Kroah-Hartman, Enrico Cic
  Cc: linux-input, devicetree, linux-kernel, linux-arm-msm,
	phone-devel, ~postmarketos/upstreaming

Hi,

This series enables the haptics of the Motorola Edge 30.

The first patch adds the binding documentation for the AW86224, which is 
compatible with the AW86927 haptic driver.

The second one contains the driver. The AW86224 differs from the AW86927 
in many respects, so writing a new driver seemed the simpler approach.

The third one enables it in the sm7325-motorola-dubai-common DTS.

Gergo Koteles (3):
  dt-bindings: input: Add compatibility for Awinic AW86224
  Input: aw86224 - add driver for Awinic AW86224
  arm64: dts: qcom: sm7325-motorola-dubai: Add vibrator support

 .../bindings/input/awinic,aw86927.yaml        |   6 +-
 .../qcom/sm7325-motorola-dubai-common.dtsi    |  11 +
 drivers/input/misc/Kconfig                    |  12 +
 drivers/input/misc/Makefile                   |   1 +
 drivers/input/misc/aw86224.c                  | 582 ++++++++++++++++++
 5 files changed, 608 insertions(+), 4 deletions(-)
 create mode 100644 drivers/input/misc/aw86224.c

-- 
2.56.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/3] dt-bindings: input: Add compatibility for Awinic AW86224
  2026-10-27 22:01 [PATCH 0/3] AW86224 haptic driver Gergo Koteles
@ 2026-10-27 22:01 ` Gergo Koteles
  2026-10-27 22:01 ` [PATCH 2/3] Input: aw86224 - add driver " Gergo Koteles
  2026-10-27 22:01 ` [PATCH 3/3] arm64: dts: qcom: sm7325-motorola-dubai: Add vibrator support Gergo Koteles
  2 siblings, 0 replies; 4+ messages in thread
From: Gergo Koteles @ 2026-10-27 22:01 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Arnd Bergmann, Gergo Koteles,
	Griffin Kroah-Hartman, Enrico Cic
  Cc: linux-input, devicetree, linux-kernel, linux-arm-msm,
	phone-devel, ~postmarketos/upstreaming

Add the AW86224 haptic driver to the binding documentation for the
AW86927 and AW86938, as this binding needs the same properties.

They are not compatible though, because the chip identification works
differently compared to the 86927/86938

Signed-off-by: Gergo Koteles <soyer@irl.hu>
---
 Documentation/devicetree/bindings/input/awinic,aw86927.yaml | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/Documentation/devicetree/bindings/input/awinic,aw86927.yaml b/Documentation/devicetree/bindings/input/awinic,aw86927.yaml
index bd74b81488f6..655633527c01 100644
--- a/Documentation/devicetree/bindings/input/awinic,aw86927.yaml
+++ b/Documentation/devicetree/bindings/input/awinic,aw86927.yaml
@@ -12,11 +12,9 @@ maintainers:
 properties:
   compatible:
     oneOf:
+      - const: awinic,aw86224
       - const: awinic,aw86927
-      - items:
-          - enum:
-              - awinic,aw86938
-          - const: awinic,aw86927
+      - const: awinic,aw86938
 
   reg:
     maxItems: 1
-- 
2.56.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 2/3] Input: aw86224 - add driver for Awinic AW86224
  2026-10-27 22:01 [PATCH 0/3] AW86224 haptic driver Gergo Koteles
  2026-10-27 22:01 ` [PATCH 1/3] dt-bindings: input: Add compatibility for Awinic AW86224 Gergo Koteles
@ 2026-10-27 22:01 ` Gergo Koteles
  2026-10-27 22:01 ` [PATCH 3/3] arm64: dts: qcom: sm7325-motorola-dubai: Add vibrator support Gergo Koteles
  2 siblings, 0 replies; 4+ messages in thread
From: Gergo Koteles @ 2026-10-27 22:01 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Arnd Bergmann, Gergo Koteles,
	Griffin Kroah-Hartman, Enrico Cic
  Cc: linux-input, devicetree, linux-kernel, linux-arm-msm,
	phone-devel, ~postmarketos/upstreaming

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

The chip supports multiple modes of which only RAM mode is implemented.

Tested-by: Enrico Cic <enrico@privacyrequired.com>
Signed-off-by: Gergo Koteles <soyer@irl.hu>
---
 drivers/input/misc/Kconfig   |  12 +
 drivers/input/misc/Makefile  |   1 +
 drivers/input/misc/aw86224.c | 582 +++++++++++++++++++++++++++++++++++
 3 files changed, 595 insertions(+)
 create mode 100644 drivers/input/misc/aw86224.c

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 1f6c57dba030..aa14417154ff 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -126,6 +126,18 @@ config INPUT_ATMEL_CAPTOUCH
 	  To compile this driver as a module, choose M here: the
 	  module will be called atmel_captouch.
 
+config INPUT_AW86224
+	tristate "Awinic AW86224 Haptic Driver Support"
+	depends on I2C && INPUT
+	select INPUT_FF_MEMLESS
+	select REGMAP_I2C
+	help
+	  Say Y here if you have an Awinic AW86224 haptic chip.
+	  One should find "awinic,aw86224" node in the board specific DTS.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called aw86224.
+
 config INPUT_AW86927
 	tristate "Awinic AW86927 Haptic Driver Support"
 	depends on I2C && INPUT
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 2281d6803fce..f0cd6a40790d 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_AW86224)		+= aw86224.o
 obj-$(CONFIG_INPUT_AW86927)		+= aw86927.o
 obj-$(CONFIG_INPUT_BBNSM_PWRKEY)	+= nxp-bbnsm-pwrkey.o
 obj-$(CONFIG_INPUT_BMA150)		+= bma150.o
diff --git a/drivers/input/misc/aw86224.c b/drivers/input/misc/aw86224.c
new file mode 100644
index 000000000000..b93ee727ad7e
--- /dev/null
+++ b/drivers/input/misc/aw86224.c
@@ -0,0 +1,582 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Gergo Koteles <soyer@irl.hu>
+ *
+ * Partially based on aw86927 driver
+ *	Copyright (C) 2025 Griffin Kroah-Hartman <griffin.kroah@fairphone.com>
+ *
+ * Partially based on vendor driver:
+ *	Copyright (c) 2021 AWINIC Technology CO., LTD
+ *
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#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/types.h>
+
+#define AW86224_RSTCFG_REG			0x00
+#define AW86224_RSTCFG_SOFTRST			0xaa
+
+#define AW86224_SYSINT_REG			0x02
+#define AW86224_SYSINT_UVLI			BIT(5)
+#define AW86224_SYSINT_OCDI			BIT(2)
+#define AW86224_SYSINT_OTI			BIT(1)
+
+#define AW86224_SYSINTM_REG			0x03
+#define AW86224_SYSINTM_FF_AEM			BIT(4)
+#define AW86224_SYSINTM_FF_AFM			BIT(3)
+#define AW86224_SYSINTM_DONEM			BIT(0)
+
+#define AW86224_PLAYCFG2_REG			0x07
+#define AW86224_PLAYCFG2_GAIN_MAX		0x80
+
+#define AW86224_PLAYCFG3_REG			0x08
+#define AW86224_PLAYCFG3_PLAY_MODE		GENMASK(1, 0)
+#define AW86224_PLAYCFG3_PLAY_MODE_RAM		0
+
+#define AW86224_PLAYCFG4_REG			0x09
+#define AW86224_PLAYCFG4_STOP			BIT(1)
+#define AW86224_PLAYCFG4_GO			BIT(0)
+
+#define AW86224_WAVCFG9_REG			0x12
+#define AW86224_WAVCFG9_SEQ1LOOP		GENMASK(7, 4)
+#define AW86224_WAVCFG9_SEQ1LOOP_INFINITELY	0x0f
+
+#define AW86224_GLBRD5_REG			0x3f
+#define AW86224_GLBRD5_STATE			GENMASK(3, 0)
+#define AW86224_GLBRD5_STATE_STANDBY		0
+
+#define AW86224_RAMADDRH_REG			0x40
+#define AW86224_RAMADDRH_MASK			GENMASK(3, 0)
+
+#define AW86224_RAMADDRL_REG			0x41
+
+#define AW86224_RAMDATA_REG			0x42
+
+#define AW86224_SYSCTRL1_REG			0x43
+#define AW86224_SYSCTRL1_EN_RAMINIT		BIT(3)
+
+#define AW86224_SYSCTRL2_REG			0x44
+#define AW86224_SYSCTRL2_STANDBY		BIT(6)
+
+#define AW86224_SYSCTRL7_REG			0x49
+#define AW86224_SYSCTRL7_GAIN_BYPASS		BIT(6)
+
+#define AW86224_CHIPID_REG			0x64
+#define AW86224_CHIPID_H			BIT(6)
+#define AW86224_CHIPID_H_AW8622X		0
+
+/* default value of base addr */
+#define AW86224_RAM_BASE_ADDR			0x800
+#define AW86224_BASEADDRH_VAL			0x08
+#define AW86224_BASEADDRL_VAL			0x00
+
+enum aw86224_work_mode {
+	AW86224_STANDBY_MODE,
+	AW86224_RAM_MODE,
+};
+
+struct aw86224_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;
+	u16 level;
+	bool running;
+	bool suspend;
+};
+
+static const struct regmap_range aw86224_volatile_registers[] = {
+	regmap_reg_range(AW86224_SYSINT_REG, AW86224_SYSINT_REG),
+	regmap_reg_range(AW86224_GLBRD5_REG, AW86224_GLBRD5_REG),
+	regmap_reg_range(AW86224_RAMDATA_REG, AW86224_RAMDATA_REG),
+};
+
+static const struct regmap_access_table aw86224_volatile_table = {
+	.yes_ranges = aw86224_volatile_registers,
+	.n_yes_ranges = ARRAY_SIZE(aw86224_volatile_registers),
+};
+
+static const struct reg_default aw86224_regmap_defs[] = {
+	{ AW86224_PLAYCFG2_REG,		0x80 },
+	{ AW86224_PLAYCFG3_REG,		0x04 },
+	{ AW86224_PLAYCFG4_REG,		0x00 },
+	{ AW86224_WAVCFG9_REG,		0x00 },
+	{ AW86224_RAMADDRH_REG,		0x00 },
+	{ AW86224_RAMADDRL_REG,		0x00 },
+	{ AW86224_SYSCTRL1_REG,		0x04 },
+	{ AW86224_SYSCTRL2_REG,		0x28 },
+	{ AW86224_SYSCTRL7_REG,		0x14 },
+};
+
+static const struct regmap_config aw86224_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0x80,
+	.cache_type = REGCACHE_MAPLE,
+	.reg_defaults = aw86224_regmap_defs,
+	.num_reg_defaults = ARRAY_SIZE(aw86224_regmap_defs),
+	.volatile_table = &aw86224_volatile_table,
+};
+
+/*
+ * 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 u8 aw86224_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 aw86224_sram_waveform_header {
+	u8 version;
+	__be16 start_address;
+	__be16 end_address;
+} __packed;
+
+static const struct aw86224_sram_waveform_header sram_waveform_header = {
+	.version = 0x01,
+	.start_address = cpu_to_be16(AW86224_RAM_BASE_ADDR +
+			sizeof(struct aw86224_sram_waveform_header)),
+	.end_address = cpu_to_be16(AW86224_RAM_BASE_ADDR +
+			sizeof(struct aw86224_sram_waveform_header) +
+			ARRAY_SIZE(aw86224_waveform) - 1),
+};
+
+static int aw86224_wait_enter_standby(struct aw86224_data *haptics)
+{
+	unsigned int reg_val;
+	int err;
+
+	err = regmap_read_poll_timeout(haptics->regmap, AW86224_GLBRD5_REG, reg_val,
+				       (FIELD_GET(AW86224_GLBRD5_STATE, reg_val) ==
+				       AW86224_GLBRD5_STATE_STANDBY),
+				       2500, 2500 * 100);
+	if (err) {
+		dev_err(haptics->dev, "did not enter standby: %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static int aw86224_play_mode(struct aw86224_data *haptics, u8 play_mode)
+{
+	int err;
+
+	switch (play_mode) {
+	case AW86224_STANDBY_MODE:
+		/* Briefly toggle standby, then toggle back to standby off */
+		err = regmap_set_bits(haptics->regmap,
+				      AW86224_SYSCTRL2_REG,
+				      AW86224_SYSCTRL2_STANDBY);
+		if (err)
+			return err;
+
+		err = regmap_clear_bits(haptics->regmap,
+					AW86224_SYSCTRL2_REG,
+					AW86224_SYSCTRL2_STANDBY);
+		if (err)
+			return err;
+
+		break;
+
+	case AW86224_RAM_MODE:
+		err = regmap_update_bits(haptics->regmap,
+					 AW86224_PLAYCFG3_REG,
+					 AW86224_PLAYCFG3_PLAY_MODE,
+					 FIELD_PREP(AW86224_PLAYCFG3_PLAY_MODE,
+						    AW86224_PLAYCFG3_PLAY_MODE_RAM));
+		if (err)
+			return err;
+
+		break;
+	}
+
+	return 0;
+}
+
+static int aw86224_stop(struct aw86224_data *haptics)
+{
+	int err;
+
+	err = regmap_write(haptics->regmap, AW86224_PLAYCFG4_REG, AW86224_PLAYCFG4_STOP);
+	if (err) {
+		dev_err(haptics->dev, "Failed to stop playback: %d\n", err);
+		return err;
+	}
+
+	err = aw86224_wait_enter_standby(haptics);
+	if (err) {
+		dev_err(haptics->dev, "Failed to enter standby, trying to force it\n");
+		err = aw86224_play_mode(haptics, AW86224_STANDBY_MODE);
+		if (err)
+			return err;
+	}
+
+	haptics->running = false;
+
+	return 0;
+}
+
+static int aw86224_haptics_play(struct input_dev *dev, void *data, struct ff_effect *effect)
+{
+	struct aw86224_data *haptics = input_get_drvdata(dev);
+	int level;
+
+	level = effect->u.rumble.strong_magnitude;
+	if (!level)
+		level = effect->u.rumble.weak_magnitude;
+
+	haptics->level = level;
+
+	schedule_work(&haptics->play_work);
+
+	return 0;
+}
+
+static int aw86224_update_gain(struct aw86224_data *haptics)
+{
+	return regmap_write(haptics->regmap,
+			    AW86224_PLAYCFG2_REG,
+			    haptics->level * AW86224_PLAYCFG2_GAIN_MAX / USHRT_MAX);
+}
+
+static int aw86224_play_sine(struct aw86224_data *haptics)
+{
+	int err;
+
+	err = aw86224_play_mode(haptics, AW86224_RAM_MODE);
+	if (err)
+		return err;
+
+	err = regmap_update_bits(haptics->regmap,
+				 AW86224_WAVCFG9_REG,
+				 AW86224_WAVCFG9_SEQ1LOOP,
+				 FIELD_PREP(AW86224_WAVCFG9_SEQ1LOOP,
+					    AW86224_WAVCFG9_SEQ1LOOP_INFINITELY));
+	if (err)
+		return err;
+
+	err = aw86224_update_gain(haptics);
+	if (err)
+		return err;
+
+	err = regmap_write(haptics->regmap, AW86224_PLAYCFG4_REG, AW86224_PLAYCFG4_GO);
+	if (err)
+		return err;
+
+	haptics->running = true;
+
+	return 0;
+}
+
+static void aw86224_close(struct input_dev *input)
+{
+	struct aw86224_data *haptics = input_get_drvdata(input);
+	struct device *dev = &haptics->client->dev;
+	int err;
+
+	cancel_work_sync(&haptics->play_work);
+
+	err = aw86224_stop(haptics);
+	if (err)
+		dev_err(dev, "Failed to close the Driver: %d\n", err);
+}
+
+static void aw86224_haptics_play_work(struct work_struct *work)
+{
+	struct aw86224_data *haptics =
+		container_of(work, struct aw86224_data, play_work);
+	struct device *dev = &haptics->client->dev;
+	int err;
+
+	if (haptics->level)
+		if (!haptics->running)
+			err = aw86224_play_sine(haptics);
+		else
+			err = aw86224_update_gain(haptics);
+	else
+		err = aw86224_stop(haptics);
+
+	if (err)
+		dev_err(dev, "Failed to execute work command: %d\n", err);
+}
+
+static void aw86224_hw_reset(struct aw86224_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 aw86224_haptic_init(struct aw86224_data *haptics)
+{
+	int err;
+
+	err = regmap_set_bits(haptics->regmap,
+			      AW86224_SYSCTRL7_REG,
+			      AW86224_SYSCTRL7_GAIN_BYPASS);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int aw86224_ram_init(struct aw86224_data *haptics)
+{
+	int err;
+
+	err = aw86224_wait_enter_standby(haptics);
+	if (err)
+		return err;
+
+	err = regmap_set_bits(haptics->regmap,
+			      AW86224_SYSCTRL1_REG,
+			      AW86224_SYSCTRL1_EN_RAMINIT);
+	if (err)
+		return err;
+
+	/* AW86224 wants a 1ms delay here */
+	usleep_range(1000, 1500);
+
+	/* Set start of SRAM, before the data is written it will be the same as the base */
+	err = regmap_update_bits(haptics->regmap,
+				 AW86224_RAMADDRH_REG,
+				 AW86224_RAMADDRH_MASK,
+				 FIELD_PREP(AW86224_RAMADDRH_MASK,
+					    AW86224_BASEADDRH_VAL));
+	if (err)
+		return err;
+
+	err = regmap_write(haptics->regmap,
+			   AW86224_RAMADDRL_REG, AW86224_BASEADDRL_VAL);
+	if (err)
+		return err;
+
+	/* Write waveform header to SRAM */
+	err = regmap_noinc_write(haptics->regmap, AW86224_RAMDATA_REG,
+				 &sram_waveform_header, sizeof(sram_waveform_header));
+	if (err)
+		return err;
+
+	/* Write waveform to SRAM */
+	err = regmap_noinc_write(haptics->regmap, AW86224_RAMDATA_REG,
+				 aw86224_waveform, ARRAY_SIZE(aw86224_waveform));
+	if (err)
+		return err;
+
+	/* Disable SRAM init */
+	err = regmap_clear_bits(haptics->regmap,
+				AW86224_SYSCTRL1_REG,
+				AW86224_SYSCTRL1_EN_RAMINIT);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static irqreturn_t aw86224_irq(int irq, void *data)
+{
+	struct aw86224_data *haptics = data;
+	struct device *dev = &haptics->client->dev;
+	unsigned int reg_val;
+	int err;
+
+	err = regmap_read(haptics->regmap, AW86224_SYSINT_REG, &reg_val);
+	if (err) {
+		dev_err(dev, "Failed to read SYSINT register: %d\n", err);
+		return IRQ_NONE;
+	}
+
+	if (reg_val & AW86224_SYSINT_UVLI)
+		dev_err(dev, "Received an Under Voltage Lock Out interrupt\n");
+	if (reg_val & AW86224_SYSINT_OCDI)
+		dev_err(dev, "Received an Over Current interrupt\n");
+	if (reg_val & AW86224_SYSINT_OTI)
+		dev_err(dev, "Received an Over Temperature interrupt\n");
+
+	return IRQ_HANDLED;
+}
+
+static int aw86224_detect(struct aw86224_data *haptics)
+{
+	unsigned int chip_id;
+	int err;
+
+	err = regmap_read(haptics->regmap, AW86224_CHIPID_REG, &chip_id);
+	if (err)
+		return dev_err_probe(haptics->dev, err, "Failed to read CHIPID register\n");
+
+	if ((chip_id & AW86224_CHIPID_H) != AW86224_CHIPID_H_AW8622X) {
+		dev_err(haptics->dev, "Unexpected CHIPID value: 0x%x\n", chip_id);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int aw86224_probe(struct i2c_client *client)
+{
+	struct aw86224_data *haptics;
+	int err;
+
+	haptics = devm_kzalloc(&client->dev, sizeof(struct aw86224_data), GFP_KERNEL);
+	if (!haptics)
+		return -ENOMEM;
+
+	haptics->dev = &client->dev;
+	haptics->client = client;
+
+	i2c_set_clientdata(client, haptics);
+
+	haptics->regmap = devm_regmap_init_i2c(client, &aw86224_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 */
+	aw86224_hw_reset(haptics);
+
+	/* Software reset */
+	err = regmap_write(haptics->regmap, AW86224_RSTCFG_REG, AW86224_RSTCFG_SOFTRST);
+	if (err)
+		return dev_err_probe(haptics->dev, err,	"Failed Software reset\n");
+
+	/* Wait ~3ms until I2C is accessible */
+	usleep_range(3000, 3500);
+
+	err = aw86224_detect(haptics);
+	if (err)
+		return dev_err_probe(haptics->dev, err, "Failed to find chip\n");
+
+	/* Disable FF_AE, FF_AF, DONE interrupts */
+	err = regmap_write(haptics->regmap,
+			   AW86224_SYSINTM_REG,
+			   AW86224_SYSINTM_FF_AEM |
+			   AW86224_SYSINTM_FF_AFM |
+			   AW86224_SYSINTM_DONEM);
+	if (err)
+		return dev_err_probe(haptics->dev, err, "Failed to configure interrupt masks\n");
+
+	err = devm_request_threaded_irq(haptics->dev, client->irq, NULL,
+					aw86224_irq, IRQF_ONESHOT, NULL, haptics);
+	if (err)
+		return dev_err_probe(haptics->dev, err, "Failed to request threaded irq\n");
+
+	INIT_WORK(&haptics->play_work, aw86224_haptics_play_work);
+
+	haptics->input_dev->name = "aw86224-haptics";
+	haptics->input_dev->close = aw86224_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, aw86224_haptics_play);
+	if (err)
+		return dev_err_probe(haptics->dev, err, "Failed to create FF dev\n");
+
+	/* Set up registers */
+	err = aw86224_play_mode(haptics, AW86224_STANDBY_MODE);
+	if (err)
+		return dev_err_probe(haptics->dev, err,
+				     "Failed to enter standby for Haptic init\n");
+
+	err = aw86224_haptic_init(haptics);
+	if (err)
+		return dev_err_probe(haptics->dev, err, "Haptic init failed\n");
+
+	/* RAM init, upload the waveform for playback */
+	err = aw86224_ram_init(haptics);
+	if (err)
+		return dev_err_probe(haptics->dev, err, "Failed to init 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 int aw86224_suspend(struct device *dev)
+{
+	struct aw86224_data *haptics = dev_get_drvdata(dev);
+
+	if (haptics->suspend)
+		return 0;
+
+	disable_work_sync(&haptics->play_work);
+	aw86224_stop(haptics);
+	haptics->suspend = true;
+
+	return 0;
+}
+
+static int aw86224_resume(struct device *dev)
+{
+	struct aw86224_data *haptics = dev_get_drvdata(dev);
+
+	if (!haptics->suspend)
+		return 0;
+
+	haptics->suspend = false;
+	enable_work(&haptics->play_work);
+
+	return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(aw86224_pm_ops,
+				aw86224_suspend,
+				aw86224_resume);
+
+static const struct of_device_id aw86224_of_id[] = {
+	{ .compatible = "awinic,aw86224" },
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, aw86224_of_id);
+
+static struct i2c_driver aw86224_driver = {
+	.driver = {
+		.name = "aw86224-haptics",
+		.pm   = pm_sleep_ptr(&aw86224_pm_ops),
+		.of_match_table = aw86224_of_id,
+	},
+	.probe = aw86224_probe,
+};
+
+module_i2c_driver(aw86224_driver);
+
+MODULE_AUTHOR("Gergo Koteles <soyer@irl.hu>");
+MODULE_DESCRIPTION("AWINIC AW86224 LRA Haptic Driver");
+MODULE_LICENSE("GPL");
-- 
2.56.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 3/3] arm64: dts: qcom: sm7325-motorola-dubai: Add vibrator support
  2026-10-27 22:01 [PATCH 0/3] AW86224 haptic driver Gergo Koteles
  2026-10-27 22:01 ` [PATCH 1/3] dt-bindings: input: Add compatibility for Awinic AW86224 Gergo Koteles
  2026-10-27 22:01 ` [PATCH 2/3] Input: aw86224 - add driver " Gergo Koteles
@ 2026-10-27 22:01 ` Gergo Koteles
  2 siblings, 0 replies; 4+ messages in thread
From: Gergo Koteles @ 2026-10-27 22:01 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Arnd Bergmann, Gergo Koteles,
	Griffin Kroah-Hartman, Enrico Cic
  Cc: linux-input, devicetree, linux-kernel, linux-arm-msm,
	phone-devel, ~postmarketos/upstreaming

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

Signed-off-by: Gergo Koteles <soyer@irl.hu>
---
 .../boot/dts/qcom/sm7325-motorola-dubai-common.dtsi   | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/sm7325-motorola-dubai-common.dtsi b/arch/arm64/boot/dts/qcom/sm7325-motorola-dubai-common.dtsi
index ef91986a1fb9..434846be51d9 100644
--- a/arch/arm64/boot/dts/qcom/sm7325-motorola-dubai-common.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm7325-motorola-dubai-common.dtsi
@@ -924,6 +924,17 @@ &i2c4 {
 
 	status = "okay";
 
+	vibrator@58 {
+		compatible = "awinic,aw86224";
+		reg = <0x58>;
+
+		interrupts-extended = <&tlmm 47 IRQ_TYPE_EDGE_FALLING>;
+		reset-gpios = <&tlmm 46 GPIO_ACTIVE_LOW>;
+
+		pinctrl-0 = <&aw86224_int_default>;
+		pinctrl-names = "default";
+	};
+
 	wl2864c@29 {
 		compatible = "willsemi,wl2864c";
 		reg = <0x29>;
-- 
2.56.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-10-27 22:01 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-27 22:01 [PATCH 0/3] AW86224 haptic driver Gergo Koteles
2026-10-27 22:01 ` [PATCH 1/3] dt-bindings: input: Add compatibility for Awinic AW86224 Gergo Koteles
2026-10-27 22:01 ` [PATCH 2/3] Input: aw86224 - add driver " Gergo Koteles
2026-10-27 22:01 ` [PATCH 3/3] arm64: dts: qcom: sm7325-motorola-dubai: Add vibrator support Gergo Koteles

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

all inboxes | Powered by JetHome®