Linux Input/HID development
 help / color / mirror / Atom feed
* Re: [PATCH 1/2] dt-bindings: input: cirrus,cs40l50: Support for CS40L50
From: Krzysztof Kozlowski @ 2023-08-09  6:43 UTC (permalink / raw)
  To: James Ogletree, dmitry.torokhov
  Cc: Fred Treven, Ben Bright, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Lee Jones, Wolfram Sang, Jeff LaBundy, Peng Fan,
	Eddie James, Jean Delvare, Arnd Bergmann,
	Markus Schneider-Pargmann, Jacky Bai, ChiYuan Huang, Randy Dunlap,
	patches, linux-input, devicetree, linux-kernel
In-Reply-To: <20230808172511.665787-1-james.ogletree@cirrus.com>

On 08/08/2023 19:25, James Ogletree wrote:
> Introduce device tree bindings for the initial commit
> of the CS40L50 driver.
> 
> Change-Id: I95369831f947cb32a0a80e81923fd28ace888481

Please run scripts/checkpatch.pl and fix reported warnings. Some
warnings can be ignored, but the code here looks like it needs a fix.
Feel free to get in touch if the warning is not clear.

> Signed-off-by: James Ogletree <james.ogletree@cirrus.com>
> ---
>  .../bindings/input/cirrus,cs40l50.yaml        | 77 +++++++++++++++++++
>  MAINTAINERS                                   |  9 +++
>  2 files changed, 86 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/cirrus,cs40l50.yaml
> 
> diff --git a/Documentation/devicetree/bindings/input/cirrus,cs40l50.yaml b/Documentation/devicetree/bindings/input/cirrus,cs40l50.yaml
> new file mode 100644
> index 000000000000..5437c6beb1b3
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/cirrus,cs40l50.yaml
> @@ -0,0 +1,77 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/input/cirrus,cs40l50.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Cirrus Logic CS40L50 Advanced Haptic Driver

Driver as "motor"? If yes, then ok. If "Linux Driver" then not.

> +
> +maintainers:
> +  - James Ogletree <james.ogletree@cirrus.com>
> +
> +description:
> +  CS40L50 is a Haptic Driver with Waveform Memory DSP
> +  and Closed-Loop Algorithms
> +
> +properties:
> +  compatible:
> +    enum:
> +      - cirrus,cs40l50
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  reset-gpios:
> +    maxItems: 1
> +
> +  VA-supply:
> +    description: Regulator for analog voltage

Keep lowercase, so "va-supply".

> +
> +  VP-supply:
> +    description: Regulator for peak voltage

ditto

> +
> +  VIO-supply:
> +    description: Regulator for digital IO voltage

ditto

> +
> +  cirrus,external-boost:
> +    description:
> +      Bypass the CS40L50 boost converter.
> +
> +      The amplifier on CS40L50 can be powered internally through the boost
> +      converter, or else can be powered using an external supply. If an
> +      external VA supply is used, the boost converter must be bypassed.
> +    type: boolean

Isn't this property then conflicting with va-supply?

> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - reset-gpios
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/gpio/gpio.h>
> +    #include <dt-bindings/input/input.h>
> +    #include <dt-bindings/interrupt-controller/irq.h>
> +
> +    i2c {
> +      #address-cells = <1>;
> +      #size-cells = <0>;
> +
> +      cs40l50@34 {

Node names should be generic. See also an explanation and list of
examples (not exhaustive) in DT specification:
https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#generic-names-recommendation

> +        compatible = "cirrus,cs40l50";
> +        reg = <0x34>;
> +        interrupt-parent = <&gpio>;
> +        interrupts = <113 IRQ_TYPE_LEVEL_LOW>;
> +        reset-gpios = <&gpio 112 GPIO_ACTIVE_LOW>;
> +        VA-supply = <&dummy_vreg>;

s/dummy_//

> +        VP-supply = <&dummy_vreg>;

s/dummy_//

> +        VIO-supply = <&dummy_vreg>;

s/dummy_//

> +        cirrus,external-boost;
> +      };
> +    };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 54ef320bb689..016e5ff3b831 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2081,6 +2081,15 @@ F:	arch/arm/boot/compressed/misc-ep93xx.h
>  F:	arch/arm/mach-ep93xx/
>  F:	drivers/iio/adc/ep93xx_adc.c
>  
> +CIRRUS LOGIC HAPTICS DRIVER
> +M:	James Ogletree <james.ogletree@cirrus.com>
> +M:	Fred Treven <fred.treven@cirrus.com>
> +M:	Ben Bright <ben.bright@cirrus.com>
> +L:	patches@Qopensource.cirrus.com

No typo in email? Qopensource?

> +S:	Supported
> +T:	git https://github.com/CirrusLogic/linux-drivers.git

We do not have real maintenance entries for individual drivers, thus no
git trees. Who is going to pull from your tree? Is your GPG key in
kernel.org keyring? I propose to drop the tree reference.

> +F:	Documentation/devicetree/bindings/input/cirrus,cs40l50.yaml
> +
>  ARM/CLKDEV SUPPORT
>  M:	Russell King <linux@armlinux.org.uk>
>  L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)

Best regards,
Krzysztof


^ permalink raw reply

* [PATCH v10 2/2] Input: Add Novatek NT36xxx touchscreen driver
From: Konrad Dybcio @ 2023-08-08 23:38 UTC (permalink / raw)
  To: AngeloGioacchino Del Regno, Dmitry Torokhov, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Del Regno, Henrik Rydberg
  Cc: Marijn Suijten, linux-input, devicetree, linux-kernel, Dang Huynh,
	Amit Pundir, Konrad Dybcio
In-Reply-To: <20230808-topic-nt36xxx-v10-0-dd135dfa0b5e@linaro.org>

From: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>

This is a driver for the Novatek in-cell touch controller and
supports various chips from the NT36xxx family, currently
including NT36525, NT36672A, NT36676F, NT36772 and NT36870.

Functionality like wake gestures and firmware flashing is not
included: I am not aware of any of these DrIC+Touch combo
chips not including a non-volatile memory and it should be
highly unlikely to find one, since the touch firmware is
embedded into the DriverIC one, which is obviously necessary
to drive the display unit.

However, the necessary address for the firmware update
procedure was included into the address table in this driver
so, in the event that someone finds the need to implement it
for a reason or another, it will be pretty straightforward to.

This driver is lightly based on the downstream implementation [1].
[1] https://github.com/Rasenkai/caf-tsoft-Novatek-nt36xxx

Tested-by: Dang Huynh <danct12@riseup.net>
Tested-by: Amit Pundir <amit.pundir@linaro.org>
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Konrad Dybcio <konrad.dybcio@linaro.org>
---
 drivers/input/touchscreen/Kconfig   |  12 +
 drivers/input/touchscreen/Makefile  |   1 +
 drivers/input/touchscreen/nt36xxx.c | 847 ++++++++++++++++++++++++++++++++++++
 drivers/input/touchscreen/nt36xxx.h |  89 ++++
 4 files changed, 949 insertions(+)

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index e3e2324547b9..33182358b6de 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -664,6 +664,18 @@ config TOUCHSCREEN_NOVATEK_NVT_TS
 	  To compile this driver as a module, choose M here: the
 	  module will be called novatek-nvt-ts.
 
+config TOUCHSCREEN_NT36XXX
+	tristate "Novatek NT36XXX In-Cell I2C touchscreen controller"
+	depends on I2C
+	help
+	  Say Y here if you have a Novatek NT36xxx series In-Cell
+	  touchscreen connected to your system over I2C.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called nt36xxx.
+
 config TOUCHSCREEN_IMAGIS
 	tristate "Imagis touchscreen support"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 62bd24f3ac8e..8f5dee2df20b 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_TOUCHSCREEN_MSG2638)	+= msg2638.o
 obj-$(CONFIG_TOUCHSCREEN_MTOUCH)	+= mtouch.o
 obj-$(CONFIG_TOUCHSCREEN_MK712)		+= mk712.o
 obj-$(CONFIG_TOUCHSCREEN_NOVATEK_NVT_TS)	+= novatek-nvt-ts.o
+obj-$(CONFIG_TOUCHSCREEN_NT36XXX)	+= nt36xxx.o
 obj-$(CONFIG_TOUCHSCREEN_HP600)		+= hp680_ts_input.o
 obj-$(CONFIG_TOUCHSCREEN_HP7XX)		+= jornada720_ts.o
 obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO)	+= ipaq-micro-ts.o
diff --git a/drivers/input/touchscreen/nt36xxx.c b/drivers/input/touchscreen/nt36xxx.c
new file mode 100644
index 000000000000..07d8902f5932
--- /dev/null
+++ b/drivers/input/touchscreen/nt36xxx.c
@@ -0,0 +1,847 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Novatek NT36xxx series touchscreens
+ *
+ * Copyright (C) 2010 - 2017 Novatek, Inc.
+ * Copyright (C) 2020 AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <linux/interrupt.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+
+#include "nt36xxx.h"
+
+/* FW Param address */
+#define NT36XXX_FW_ADDR			0x01
+
+/* Misc */
+#define NT36XXX_NUM_SUPPLIES		2
+#define NT36XXX_MAX_RETRIES		5
+#define NT36XXX_MAX_FW_RST_RETRY	50
+
+/* Touch info */
+#define TOUCH_DEFAULT_MAX_WIDTH 	1080
+#define TOUCH_DEFAULT_MAX_HEIGHT	2246
+#define TOUCH_MAX_FINGER_NUM		10
+#define TOUCH_MAX_PRESSURE		1000
+
+/* Point data length */
+#define POINT_DATA_LEN			65
+
+/* Global pages */
+#define NT36XXX_PAGE_CHIP_INFO		0x0001f64e
+#define NT36XXX_PAGE_CRC		0x0003f135
+
+struct nt36xxx_i2c {
+	struct i2c_client *hw_client;
+	struct i2c_client *fw_client;
+	struct regmap *regmap;
+	struct regmap *fw_regmap;
+	struct input_dev *input;
+	struct regulator_bulk_data *supplies;
+	struct gpio_desc *reset_gpio;
+
+	struct mutex lock;
+
+	struct touchscreen_properties prop;
+	struct nt36xxx_fw_info fw_info;
+	struct nt36xxx_abs_object abs_obj;
+
+	const struct nt36xxx_mem_map *mmap;
+};
+
+static const struct nt36xxx_mem_map nt36xxx_memory_maps[] = {
+	[NT36525_IC]  = {
+		.evtbuf_addr = 0x11a00,
+		.pipe0_addr = 0x10000,
+		.pipe1_addr = 0x12000,
+		.flash_csum_addr = 0x14000,
+		.flash_data_addr = 0x14002,
+	},
+	[NT36672A_IC] = {
+		.evtbuf_addr = 0x21c00,
+		.pipe0_addr = 0x20000,
+		.pipe1_addr = 0x23000,
+		.flash_csum_addr = 0x24000,
+		.flash_data_addr = 0x24002,
+	},
+	[NT36676F_IC] = {
+		.evtbuf_addr = 0x11a00,
+		.pipe0_addr = 0x10000,
+		.pipe1_addr = 0x12000,
+		.flash_csum_addr = 0x14000,
+		.flash_data_addr = 0x14002,
+	},
+	[NT36772_IC]  = {
+		.evtbuf_addr = 0x11e00,
+		.pipe0_addr = 0x10000,
+		.pipe1_addr = 0x12000,
+		.flash_csum_addr = 0x14000,
+		.flash_data_addr = 0x14002,
+	},
+	[NT36870_IC]  = {
+		.evtbuf_addr = 0x25000,
+		.pipe0_addr = 0x20000,
+		.pipe1_addr = 0x23000,
+		.flash_csum_addr = 0x24000,
+		.flash_data_addr = 0x24002,
+	},
+};
+
+static const struct nt36xxx_trim_table trim_id_table[] = {
+	{
+		.id = { 0x0a, 0xff, 0xff, 0x72, 0x66, 0x03 },
+		.mask = { 1, 0, 0, 1, 1, 1 },
+		.mapid = NT36672A_IC,
+	},
+	{
+		.id = { 0x55, 0x00, 0xff, 0x00, 0x00, 0x00 },
+		.mask = { 1, 1, 0, 1, 1, 1 },
+		.mapid = NT36772_IC,
+	},
+	{
+		.id = { 0x55, 0x72, 0xff, 0x00, 0x00, 0x00 },
+		.mask = { 1, 1, 0, 1, 1, 1 },
+		.mapid = NT36772_IC,
+	},
+	{
+		.id = { 0xaa, 0x00, 0xff, 0x00, 0x00, 0x00 },
+		.mask = { 1, 1, 0, 1, 1, 1 },
+		.mapid = NT36772_IC,
+	},
+	{
+		.id = { 0xaa, 0x72, 0xff, 0x00, 0x00, 0x00 },
+		.mask = { 1, 1, 0, 1, 1, 1 },
+		.mapid = NT36772_IC,
+	},
+	{
+		.id = { 0xff, 0xff, 0xff, 0x72, 0x67, 0x03 },
+		.mask = { 0, 0, 0, 1, 1, 1 },
+		.mapid = NT36772_IC,
+	},
+	{
+		.id = { 0xff, 0xff, 0xff, 0x70, 0x66, 0x03 },
+		.mask = { 0, 0, 0, 1, 1, 1 },
+		.mapid = NT36772_IC,
+	},
+	{
+		.id = { 0xff, 0xff, 0xff, 0x70, 0x67, 0x03 },
+		.mask = { 0, 0, 0, 1, 1, 1 },
+		.mapid = NT36772_IC,
+	},
+	{
+		.id = { 0xff, 0xff, 0xff, 0x72, 0x66, 0x03 },
+		.mask = { 0, 0, 0, 1, 1, 1 },
+		.mapid = NT36772_IC,
+	},
+	{
+		.id = { 0xff, 0xff, 0xff, 0x25, 0x65, 0x03 },
+		.mask = { 0, 0, 0, 1, 1, 1 },
+		.mapid = NT36772_IC,
+	},
+	{
+		.id = { 0xff, 0xff, 0xff, 0x70, 0x68, 0x03 },
+		.mask = { 0, 0, 0, 1, 1, 1 },
+		.mapid = NT36772_IC,
+	},
+	{
+		.id = { 0xff, 0xff, 0xff, 0x76, 0x66, 0x03 },
+		.mask = { 0, 0, 0, 1, 1, 1 },
+		.mapid = NT36676F_IC,
+	},
+};
+
+/**
+ * nt36xxx_set_page - Set page number for read/write
+ * @ts: Main driver structure
+ *
+ * Return: Always zero for success, negative number for error
+ */
+static int nt36xxx_set_page(struct nt36xxx_i2c *ts, u32 pageaddr)
+{
+	u32 data = cpu_to_be32(pageaddr) >> 8;
+	int ret;
+
+	ret = regmap_noinc_write(ts->fw_regmap, NT36XXX_CMD_SET_PAGE,
+				 &data, sizeof(data));
+	if (ret)
+		return ret;
+
+	usleep_range(100, 200);
+	return ret;
+}
+
+/**
+ * nt36xxx_sw_reset_idle - Warm restart the firmware
+ * @ts: Main driver structure
+ *
+ * This function restarts the running firmware without rebooting to
+ * the bootloader (warm restart)
+ *
+ * Return: Always zero for success, negative number for error
+ */
+static int nt36xxx_sw_reset_idle(struct nt36xxx_i2c *ts)
+{
+	int ret;
+
+	ret = regmap_write(ts->regmap, ts->hw_client->addr,
+			   NT36XXX_CMD_SW_RESET);
+	if (ret)
+		return ret;
+
+	/* Wait until the MCU resets the fw state */
+	usleep_range(15000, 16000);
+	return ret;
+}
+
+/**
+ * nt36xxx_bootloader_reset - Reset MCU to bootloader
+ * @ts: Main driver structure
+ *
+ * Return: Always zero for success, negative number for error
+ */
+static int nt36xxx_bootloader_reset(struct nt36xxx_i2c *ts)
+{
+	int ret;
+
+	ret = regmap_write(ts->regmap, ts->hw_client->addr,
+			   NT36XXX_CMD_BOOTLOADER_RESET);
+	if (ret)
+		return ret;
+
+	/* MCU has to reboot from bootloader - this is the typical boot time */
+	msleep(35);
+	return ret;
+}
+
+/**
+ * nt36xxx_check_reset_state - Check the boot state during reset
+ * @ts: Main driver structure
+ * @fw_state: Enumeration containing firmware states
+ *
+ * Return: Always zero for success, negative number for error
+ */
+static int nt36xxx_check_reset_state(struct nt36xxx_i2c *ts,
+				     enum nt36xxx_fw_state fw_state)
+{
+	u8 buf[2] = { 0 };
+	int ret, retry = NT36XXX_MAX_FW_RST_RETRY;
+
+	do {
+		ret = regmap_noinc_read(ts->fw_regmap,
+					NT36XXX_EVT_RESET_COMPLETE,
+					buf, sizeof(buf));
+		if (likely(ret == 0) &&
+		    (buf[0] >= fw_state) &&
+		    (buf[0] <= NT36XXX_STATE_MAX)) {
+			ret = 0;
+			break;
+		}
+		usleep_range(10000, 11000);
+	} while (--retry);
+
+	if (!retry) {
+		dev_err(&ts->hw_client->dev, "Firmware reset failed.\n");
+		ret = -EBUSY;
+	}
+
+	return ret;
+}
+
+/**
+ * nt36xxx_read_pid - Read Novatek Project ID
+ * @ts: Main driver structure
+ *
+ * Return: Always zero for success, negative number for error
+ */
+static int nt36xxx_read_pid(struct nt36xxx_i2c *ts)
+{
+	__be16 pid;
+	int ret;
+
+	ret = nt36xxx_set_page(ts, ts->mmap->evtbuf_addr);
+	if (ret)
+		return ret;
+
+	ret = regmap_noinc_read(ts->fw_regmap, NT36XXX_EVT_PROJECTID,
+				&pid, sizeof(pid));
+	if (ret < 0)
+		return ret;
+
+	ts->fw_info.nvt_pid = be16_to_cpu(pid);
+	return 0;
+}
+
+/**
+ * __nt36xxx_get_fw_info - Get working params from firmware
+ * @ts: Main driver structure
+ *
+ * Return: Always zero for success, negative number for error
+ */
+static int __nt36xxx_get_fw_info(struct nt36xxx_i2c *ts)
+{
+	struct nt36xxx_fw_info *fwi = &ts->fw_info;
+	u8 buf[11] = { 0 };
+	int ret = 0;
+
+	ret = nt36xxx_set_page(ts, ts->mmap->evtbuf_addr);
+	if (ret)
+		return ret;
+
+	ret = regmap_noinc_read(ts->fw_regmap, NT36XXX_EVT_FWINFO,
+				buf, sizeof(buf));
+	if (ret)
+		return ret;
+
+	fwi->fw_ver = buf[0];
+	fwi->x_num = buf[2];
+	fwi->y_num = buf[3];
+	fwi->abs_x_max = get_unaligned_be16(&buf[4]);
+	fwi->abs_y_max = get_unaligned_be16(&buf[6]);
+	fwi->max_buttons = buf[10];
+
+	/* Check fw info integrity and clear x_num, y_num if broken */
+	if ((buf[0] + buf[1]) != 0xFF) {
+		dev_err(&ts->hw_client->dev,
+			"FW info is broken! fw_ver=0x%02X, ~fw_ver=0x%02X\n",
+			buf[0], buf[1]);
+		fwi->fw_ver = 0;
+		fwi->x_num = 18;
+		fwi->y_num = 32;
+		fwi->abs_x_max = TOUCH_DEFAULT_MAX_WIDTH;
+		fwi->abs_y_max = TOUCH_DEFAULT_MAX_HEIGHT;
+		fwi->max_buttons = 0;
+		return -EINVAL;
+	}
+
+	/* Get Novatek ProjectID */
+	return nt36xxx_read_pid(ts);
+}
+
+static int nt36xxx_get_fw_info(struct nt36xxx_i2c *ts)
+{
+	struct nt36xxx_fw_info *fwi = &ts->fw_info;
+	int i, ret = 0;
+
+	for (i = 0; i < NT36XXX_MAX_RETRIES; i++) {
+		ret = __nt36xxx_get_fw_info(ts);
+		if (ret == 0)
+			break;
+	}
+
+	dev_dbg(&ts->hw_client->dev,
+		"FW Info: PID=0x%x, ver=0x%x res=%ux%u max=%ux%u buttons=%u",
+		fwi->nvt_pid, fwi->fw_ver, fwi->x_num, fwi->y_num,
+		fwi->abs_x_max, fwi->abs_y_max, fwi->max_buttons);
+
+	return ret;
+}
+
+/**
+ * nt36xxx_report - Report touch events
+ * @ts: Main driver structure
+ *
+ * Return: Always zero for success, negative number for error
+ */
+static void nt36xxx_report(struct nt36xxx_i2c *ts)
+{
+	struct nt36xxx_abs_object *obj = &ts->abs_obj;
+	struct input_dev *input = ts->input;
+	u8 input_id = 0;
+	u8 point[POINT_DATA_LEN + 1] = { 0 };
+	unsigned int ppos = 0;
+	int i, ret, finger_cnt = 0;
+
+	mutex_lock(&ts->lock);
+
+	ret = regmap_noinc_read(ts->fw_regmap, NT36XXX_EVT_REPORT,
+				point, sizeof(point));
+	if (ret < 0) {
+		dev_err(&ts->hw_client->dev,
+			"Cannot read touch point data: %d\n", ret);
+		goto xfer_error;
+	}
+
+	for (i = 0; i < TOUCH_MAX_FINGER_NUM; i++) {
+		ppos = 6 * i;
+		input_id = point[ppos + 0] >> 3;
+		if ((input_id == 0) || (input_id > TOUCH_MAX_FINGER_NUM))
+			continue;
+
+		if (((point[ppos] & 0x07) == 0x01) ||
+		    ((point[ppos] & 0x07) == 0x02)) {
+			obj->x = (point[ppos + 1] << 4) +
+				 (point[ppos + 3] >> 4);
+			obj->y = (point[ppos + 2] << 4) +
+				 (point[ppos + 3] & 0xf);
+			if ((obj->x > ts->prop.max_x) ||
+			    (obj->y > ts->prop.max_y))
+				continue;
+
+			obj->tm = point[ppos + 4];
+			if (obj->tm == 0)
+				obj->tm = 1;
+
+			obj->z = point[ppos + 5];
+			if (i < 2) {
+				obj->z += point[i + 63] << 8;
+				if (obj->z > TOUCH_MAX_PRESSURE)
+					obj->z = TOUCH_MAX_PRESSURE;
+			}
+
+			if (obj->z == 0)
+				obj->z = 1;
+
+			input_mt_slot(input, input_id - 1);
+			input_mt_report_slot_state(input,
+						   MT_TOOL_FINGER, true);
+			touchscreen_report_pos(input, &ts->prop, obj->x,
+					       obj->y, true);
+
+			input_report_abs(input, ABS_MT_TOUCH_MAJOR, obj->tm);
+			input_report_abs(input, ABS_MT_PRESSURE, obj->z);
+
+			finger_cnt++;
+		}
+	}
+	input_mt_sync_frame(input);
+	input_sync(input);
+
+xfer_error:
+	enable_irq(ts->hw_client->irq);
+
+	mutex_unlock(&ts->lock);
+}
+
+static irqreturn_t nt36xxx_i2c_irq_handler(int irq, void *dev_id)
+{
+	struct nt36xxx_i2c *ts = dev_id;
+
+	disable_irq_nosync(ts->hw_client->irq);
+	nt36xxx_report(ts);
+
+	return IRQ_HANDLED;
+}
+
+static bool nt36xxx_in_crc_reboot_loop(u8 *buf)
+{
+	return ((buf[0] == 0xfc) && (buf[1] == 0xfc) && (buf[2] == 0xfc)) ||
+	       ((buf[0] == 0xff) && (buf[1] == 0xff) && (buf[2] == 0xff));
+}
+
+/**
+ * nt36xxx_stop_crc_reboot - Stop CRC reboot loop and warm-reboot the firmware
+ * @ts: Main driver structure
+ *
+ * Return: Always zero for success, negative number for error
+ */
+static int nt36xxx_stop_crc_reboot(struct nt36xxx_i2c *ts)
+{
+	u8 buf[3] = { 0 };
+	u8 val;
+	int ret, retry = NT36XXX_MAX_RETRIES;
+
+	/* Read dummy buffer to check CRC fail reboot is happening or not */
+
+	/* Change I2C index to prevent getting 0xFF, but not 0xFC */
+	ret = nt36xxx_set_page(ts, NT36XXX_PAGE_CHIP_INFO);
+	if (ret) {
+		dev_dbg(&ts->hw_client->dev,
+			"CRC reset failed: Cannot select page.\n");
+		return ret;
+	}
+
+	/* If ChipID command returns 0xFC or 0xFF, the MCU is in CRC reboot */
+	ret = regmap_noinc_read(ts->fw_regmap, NT36XXX_EVT_CHIPID,
+				buf, sizeof(buf));
+	if (ret)
+		return ret;
+
+	if (!nt36xxx_in_crc_reboot_loop(buf))
+		return 0;
+
+	/* IC is in CRC fail reboot loop, needs to be stopped! */
+	do {
+		/* Special reset-idle sequence for CRC failure */
+		ret = regmap_write(ts->regmap, ts->hw_client->addr,
+				   NT36XXX_CMD_SW_RESET);
+		if (ret)
+			dev_dbg(&ts->hw_client->dev,
+				"SW Reset 1 failed: may not recover\n");
+
+		ret = regmap_write(ts->regmap, ts->hw_client->addr,
+				   NT36XXX_CMD_SW_RESET);
+		if (ret)
+			dev_dbg(&ts->hw_client->dev,
+				"SW Reset 2 failed: may not recover\n");
+		usleep_range(1000, 1100);
+
+		/* Clear CRC_ERR_FLAG */
+		ret = nt36xxx_set_page(ts, NT36XXX_PAGE_CRC);
+		if (ret)
+			continue;
+
+		val = 0xa5;
+		ret = regmap_raw_write(ts->fw_regmap, NT36XXX_EVT_CRC,
+				       &val, sizeof(val));
+		if (ret)
+			continue;
+
+		/* Check CRC_ERR_FLAG */
+		ret = nt36xxx_set_page(ts, NT36XXX_PAGE_CRC);
+		if (ret)
+			continue;
+
+		ret = regmap_noinc_read(ts->fw_regmap, NT36XXX_EVT_CRC,
+					&buf, sizeof(buf));
+		if (ret)
+			continue;
+
+		if (buf[0] == 0xa5)
+			break;
+	} while (--retry);
+
+	if (retry == 0) {
+		dev_err(&ts->hw_client->dev,
+			"CRC reset failed: buf=0x%2ph\n", buf);
+	}
+
+	return ret;
+}
+
+/**
+ * nt36xxx_i2c_chip_version_init - Detect Novatek NT36xxx family IC
+ * @ts: Main driver structure
+ *
+ * This function reads the ChipID from the IC and sets the right
+ * memory map for the detected chip.
+ *
+ * Return: Always zero for success, negative number for error
+ */
+static int nt36xxx_i2c_chip_version_init(struct nt36xxx_i2c *ts)
+{
+	u8 buf[7] = { 0 };
+	int retry = NT36XXX_MAX_RETRIES;
+	int sz = sizeof(trim_id_table) / sizeof(struct nt36xxx_trim_table);
+	int i, list, mapid, ret;
+
+	ret = nt36xxx_bootloader_reset(ts);
+	if (ret) {
+		dev_err(&ts->hw_client->dev, "Can't reset the nvt IC\n");
+		return ret;
+	}
+
+	do {
+		ret = nt36xxx_sw_reset_idle(ts);
+		if (ret)
+			continue;
+
+		ret = regmap_write(ts->regmap, ts->hw_client->addr, NT36XXX_CMD_UNLOCK);
+		if (ret)
+			continue;
+		usleep_range(10000, 11000);
+
+		ret = nt36xxx_set_page(ts, NT36XXX_PAGE_CHIP_INFO);
+		if (ret)
+			continue;
+
+		memset(buf, 0, ARRAY_SIZE(buf));
+		ret = regmap_noinc_read(ts->fw_regmap, NT36XXX_EVT_CHIPID,
+					buf, sizeof(buf));
+		if (ret)
+			continue;
+
+		/* Compare read chip id with trim list */
+		for (list = 0; list < sz; list++) {
+			/* Compare each not masked byte */
+			for (i = 0; i < NT36XXX_ID_LEN_MAX; i++) {
+				if (trim_id_table[list].mask[i] &&
+				    buf[i] != trim_id_table[list].id[i])
+					break;
+			}
+
+			if (i == NT36XXX_ID_LEN_MAX) {
+				mapid = trim_id_table[list].mapid;
+				ts->mmap = &nt36xxx_memory_maps[mapid];
+				return 0;
+			}
+
+			ts->mmap = NULL;
+			ret = -ENOENT;
+		}
+
+		/* Stop CRC check to prevent IC auto reboot */
+		if (nt36xxx_in_crc_reboot_loop(buf)) {
+			ret = nt36xxx_stop_crc_reboot(ts);
+			if (ret)
+				continue;
+		}
+
+		usleep_range(10000, 11000);
+	} while (--retry);
+
+	return ret;
+}
+
+static const struct regmap_config nt36xxx_i2c_regmap_hw_config = {
+	.name = "nt36xxx_i2c_hw",
+	.reg_bits = 8,
+	.val_bits = 8,
+	.cache_type = REGCACHE_NONE,
+};
+
+static const struct regmap_config nt36xxx_i2c_regmap_fw_config = {
+	.name = "nt36xxx_i2c_fw",
+	.reg_bits = 8,
+	.val_bits = 8,
+	.cache_type = REGCACHE_NONE,
+};
+
+static void nt36xxx_disable_regulators(void *data)
+{
+	struct nt36xxx_i2c *ts = data;
+
+	regulator_bulk_disable(NT36XXX_NUM_SUPPLIES, ts->supplies);
+}
+
+static int nt36xxx_i2c_probe(struct i2c_client *hw_client)
+{
+	struct nt36xxx_i2c *ts;
+	struct input_dev *input;
+	int ret;
+
+	if (!i2c_check_functionality(hw_client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&hw_client->dev, "i2c_check_functionality error\n");
+		return -EIO;
+	}
+
+	if (!hw_client->irq) {
+		dev_err(&hw_client->dev, "No irq specified\n");
+		return -EINVAL;
+	}
+
+	ts = devm_kzalloc(&hw_client->dev, sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	ts->supplies = devm_kcalloc(&hw_client->dev,
+				    NT36XXX_NUM_SUPPLIES,
+				    sizeof(*ts->supplies),
+				    GFP_KERNEL);
+	if (!ts->supplies)
+		return -ENOMEM;
+
+	input = devm_input_allocate_device(&hw_client->dev);
+	if (!input)
+		return -ENOMEM;
+
+	ts->fw_client = i2c_new_dummy_device(hw_client->adapter,
+					     NT36XXX_FW_ADDR);
+	if (IS_ERR(ts->fw_client)) {
+		dev_err(&hw_client->dev, "Cannot add FW I2C device\n");
+		return PTR_ERR(ts->fw_client);
+	}
+
+	ts->hw_client = hw_client;
+	ts->input = input;
+	i2c_set_clientdata(ts->hw_client, ts);
+	i2c_set_clientdata(ts->fw_client, ts);
+
+	ts->reset_gpio = devm_gpiod_get_optional(&hw_client->dev, "reset",
+						 GPIOD_OUT_HIGH);
+	if (IS_ERR(ts->reset_gpio))
+		return PTR_ERR(ts->reset_gpio);
+	gpiod_set_consumer_name(ts->reset_gpio, "nt36xxx reset");
+
+	/* These supplies are optional */
+	ts->supplies[0].supply = "vdd";
+	ts->supplies[1].supply = "vio";
+	ret = devm_regulator_bulk_get(&hw_client->dev,
+				      NT36XXX_NUM_SUPPLIES,
+				      ts->supplies);
+	if (ret)
+		return dev_err_probe(&hw_client->dev, ret,
+				     "Cannot get supplies: %d\n", ret);
+
+	ts->regmap = devm_regmap_init_i2c(ts->hw_client,
+					  &nt36xxx_i2c_regmap_hw_config);
+	if (IS_ERR(ts->regmap)) {
+		dev_err(&hw_client->dev, "regmap (hw-addr) init failed\n");
+		return PTR_ERR(ts->regmap);
+	}
+
+	ts->fw_regmap = devm_regmap_init_i2c(ts->fw_client,
+					     &nt36xxx_i2c_regmap_fw_config);
+	if (IS_ERR(ts->fw_regmap)) {
+		dev_err(&hw_client->dev, "regmap (fw-addr) init failed\n");
+		return PTR_ERR(ts->fw_regmap);
+	}
+
+	ret = regulator_bulk_enable(NT36XXX_NUM_SUPPLIES, ts->supplies);
+	if (ret)
+		return ret;
+
+	usleep_range(10000, 11000);
+
+	ret = devm_add_action_or_reset(&hw_client->dev,
+				       nt36xxx_disable_regulators, ts);
+	if (ret)
+		return ret;
+
+	mutex_init(&ts->lock);
+
+	/* Set memory maps for the specific chip version */
+	ret = nt36xxx_i2c_chip_version_init(ts);
+	if (ret) {
+		dev_err(&hw_client->dev, "Failed to check chip version\n");
+		return ret;
+	}
+
+	/* Reset the MCU */
+	ret = nt36xxx_bootloader_reset(ts);
+	if (ret < 0)
+		return ret;
+
+	/* Check and eventually wait until the MCU goes in reset state */
+	ret = nt36xxx_check_reset_state(ts, NT36XXX_STATE_INIT);
+	if (ret < 0)
+		return ret;
+
+	/* Get information from the TS firmware */
+	ret = nt36xxx_get_fw_info(ts);
+	if (ret < 0)
+		return ret;
+
+	input->phys = devm_kasprintf(&hw_client->dev, GFP_KERNEL,
+				     "%s/input0", dev_name(&hw_client->dev));
+	if (!input->phys)
+		return -ENOMEM;
+
+	input->name = "Novatek NT36XXX Touchscreen";
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = &hw_client->dev;
+
+	__set_bit(EV_KEY, input->evbit);
+	__set_bit(EV_ABS, input->evbit);
+	input_set_capability(input, EV_KEY, BTN_TOUCH);
+
+	ret = input_mt_init_slots(input, TOUCH_MAX_FINGER_NUM,
+				  INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (ret) {
+		dev_err(&hw_client->dev, "Cannot init MT slots (%d)\n", ret);
+		return ret;
+	}
+
+	input_set_abs_params(input, ABS_MT_PRESSURE, 0,
+			     TOUCH_MAX_PRESSURE, 0, 0);
+	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+
+	input_set_abs_params(input, ABS_MT_POSITION_X, 0,
+			     ts->fw_info.abs_x_max - 1, 0, 0);
+	input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
+			     ts->fw_info.abs_y_max - 1, 0, 0);
+
+	/* Override the firmware defaults, if needed */
+	touchscreen_parse_properties(input, true, &ts->prop);
+
+	input_set_drvdata(input, ts);
+
+	ret = input_register_device(ts->input);
+	if (ret) {
+		dev_err(&hw_client->dev, "Failed to register input device: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = devm_request_threaded_irq(&hw_client->dev, hw_client->irq, NULL,
+					nt36xxx_i2c_irq_handler, IRQF_ONESHOT,
+					hw_client->name, ts);
+	if (ret) {
+		dev_err(&hw_client->dev, "request irq failed: %d\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int __maybe_unused nt36xxx_i2c_suspend(struct device *dev)
+{
+	struct nt36xxx_i2c *ts = i2c_get_clientdata(to_i2c_client(dev));
+	int ret;
+
+	disable_irq(ts->hw_client->irq);
+
+	ret = regmap_write(ts->fw_regmap, NT36XXX_EVT_HOST_CMD,
+			   NT36XXX_CMD_ENTER_SLEEP);
+	if (ret) {
+		dev_err(&ts->hw_client->dev, "Cannot enter suspend!!\n");
+		return ret;
+	}
+
+	gpiod_set_value(ts->reset_gpio, 1);
+
+	return 0;
+}
+
+static int __maybe_unused nt36xxx_i2c_resume(struct device *dev)
+{
+	struct nt36xxx_i2c *ts = i2c_get_clientdata(to_i2c_client(dev));
+	int ret;
+
+	mutex_lock(&ts->lock);
+
+	gpiod_set_value(ts->reset_gpio, 0);
+
+	/* Reboot the MCU (also recalibrates the TS) */
+	ret = nt36xxx_bootloader_reset(ts);
+	if (ret < 0)
+		goto end;
+
+	ret = nt36xxx_check_reset_state(ts, NT36XXX_STATE_REK);
+	if (ret < 0)
+		goto end;
+
+	enable_irq(ts->hw_client->irq);
+end:
+	mutex_unlock(&ts->lock);
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(nt36xxx_i2c_pm,
+			 nt36xxx_i2c_suspend, nt36xxx_i2c_resume);
+
+static const struct of_device_id nt36xxx_of_match[] = {
+	{ .compatible = "novatek,nt36525-i2c" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, nt36xxx_of_match);
+
+static const struct i2c_device_id nt36xxx_i2c_ts_id[] = {
+	{ "NVT-ts", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, nt36xxx_i2c_ts_id);
+
+static struct i2c_driver nt36xxx_i2c_ts_driver = {
+	.driver = {
+		.name	= "nt36xxx_ts",
+		.pm	= pm_ptr(&nt36xxx_i2c_pm),
+		.of_match_table = nt36xxx_of_match,
+	},
+	.id_table	= nt36xxx_i2c_ts_id,
+	.probe		= nt36xxx_i2c_probe,
+};
+module_i2c_driver(nt36xxx_i2c_ts_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Novatek NT36XXX Touchscreen Driver");
+MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
diff --git a/drivers/input/touchscreen/nt36xxx.h b/drivers/input/touchscreen/nt36xxx.h
new file mode 100644
index 000000000000..66ac9b2886ac
--- /dev/null
+++ b/drivers/input/touchscreen/nt36xxx.h
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2010 - 2017 Novatek, Inc.
+ * Copyright (C) 2020 AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#ifndef NT36XXX_H
+#define NT36XXX_H
+
+/* Number of bytes for chip identification */
+#define NT36XXX_ID_LEN_MAX		6
+
+struct nt36xxx_abs_object {
+	u16 x;
+	u16 y;
+	u16 z;
+	u8 tm;
+};
+
+struct nt36xxx_fw_info {
+	u8 fw_ver;
+	u8 x_num;
+	u8 y_num;
+	u8 max_buttons;
+	u16 abs_x_max;
+	u16 abs_y_max;
+	u16 nvt_pid;
+};
+
+struct nt36xxx_mem_map {
+	u32 evtbuf_addr;
+	u32 pipe0_addr;
+	u32 pipe1_addr;
+	u32 flash_csum_addr;
+	u32 flash_data_addr;
+};
+
+enum nt36xxx_chips {
+	NT36525_IC = 0,
+	NT36672A_IC,
+	NT36676F_IC,
+	NT36772_IC,
+	NT36870_IC,
+	NTMAX_IC,
+};
+
+struct nt36xxx_trim_table {
+	u8 id[NT36XXX_ID_LEN_MAX];
+	u8 mask[NT36XXX_ID_LEN_MAX];
+	enum nt36xxx_chips mapid;
+};
+
+/**
+ * enum nt36xxx_fw_state - Firmware state
+ * @NT36XXX_STATE_INIT: IC Reset
+ * @NT36XXX_STATE_REK: ReK baseline
+ * @NT36XXX_STATE_REK_FINISH: Baseline is ready
+ * @NT36XXX_STATE_NORMAL_RUN: Firmware is running
+ */
+enum nt36xxx_cmds {
+	NT36XXX_CMD_ENTER_SLEEP = 0x11,
+	NT36XXX_CMD_ENTER_WKUP_GESTURE = 0x13,
+	NT36XXX_CMD_UNLOCK = 0x35,
+	NT36XXX_CMD_BOOTLOADER_RESET = 0x69,
+	NT36XXX_CMD_SW_RESET = 0xa5,
+	NT36XXX_CMD_SET_PAGE = 0xff,
+};
+
+enum nt36xxx_fw_state {
+	NT36XXX_STATE_INIT = 0xa0,	/* IC reset */
+	NT36XXX_STATE_REK,		/* ReK baseline */
+	NT36XXX_STATE_REK_FINISH,	/* Baseline is ready */
+	NT36XXX_STATE_NORMAL_RUN,	/* Normal run */
+	NT36XXX_STATE_MAX = 0xaf
+};
+
+enum nt36xxx_events {
+	NT36XXX_EVT_REPORT = 0x00,
+	NT36XXX_EVT_CRC = 0x35,
+	NT36XXX_EVT_CHIPID = 0x4e,
+	NT36XXX_EVT_HOST_CMD = 0x50,
+	NT36XXX_EVT_HS_OR_SUBCMD = 0x51,	/* Handshake or subcommand byte */
+	NT36XXX_EVT_RESET_COMPLETE = 0x60,
+	NT36XXX_EVT_FWINFO = 0x78,
+	NT36XXX_EVT_PROJECTID = 0x9a,
+
+};
+
+#endif

-- 
2.41.0


^ permalink raw reply related

* [PATCH v10 1/2] dt-bindings: touchscreen: Add binding for Novatek NT36xxx ICs
From: Konrad Dybcio @ 2023-08-08 23:38 UTC (permalink / raw)
  To: AngeloGioacchino Del Regno, Dmitry Torokhov, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Del Regno, Henrik Rydberg
  Cc: Marijn Suijten, linux-input, devicetree, linux-kernel,
	Krzysztof Kozlowski, Rob Herring, Konrad Dybcio
In-Reply-To: <20230808-topic-nt36xxx-v10-0-dd135dfa0b5e@linaro.org>

From: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>

Add binding for the Novatek NT36xxx series touchscreen ICs.

Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Konrad Dybcio <konrad.dybcio@linaro.org>
---
 .../bindings/input/touchscreen/nt36xxx.yaml        | 59 ++++++++++++++++++++++
 1 file changed, 59 insertions(+)

diff --git a/Documentation/devicetree/bindings/input/touchscreen/nt36xxx.yaml b/Documentation/devicetree/bindings/input/touchscreen/nt36xxx.yaml
new file mode 100644
index 000000000000..d88b149602ef
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/nt36xxx.yaml
@@ -0,0 +1,59 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/touchscreen/nt36xxx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Novatek NT36xxx series touchscreen controller
+
+maintainers:
+  - AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+
+allOf:
+  - $ref: touchscreen.yaml#
+
+properties:
+  compatible:
+    const: novatek,nt36525-i2c
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  reset-gpios:
+    maxItems: 1
+
+  vdd-supply:
+    description: Power supply regulator for VDD pin
+
+  vio-supply:
+    description: Power supply regulator on VDD-IO pin
+
+unevaluatedProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupts
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/gpio/gpio.h>
+
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      touchscreen@62 {
+        compatible = "novatek,nt36525-i2c";
+        reg = <0x62>;
+        interrupt-parent = <&tlmm>;
+        interrupts = <45 IRQ_TYPE_EDGE_RISING>;
+        reset-gpios = <&tlmm 102 GPIO_ACTIVE_HIGH>;
+      };
+    };
+
+...

-- 
2.41.0


^ permalink raw reply related

* [PATCH v10 0/2] Add Novatek NT36xxx touchscreen driver
From: Konrad Dybcio @ 2023-08-08 23:38 UTC (permalink / raw)
  To: AngeloGioacchino Del Regno, Dmitry Torokhov, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Del Regno, Henrik Rydberg
  Cc: Marijn Suijten, linux-input, devicetree, linux-kernel,
	Krzysztof Kozlowski, Rob Herring, Konrad Dybcio, Dang Huynh,
	Amit Pundir

I am taking over this series from Angelo, as he's moved on to other
things, but there are still devices out there (and being made)
utilizing this class of ICs.

This patch series has been tested against the following devices:
 - Sony Xperia 10        (SDM630 Ganges Kirin)
 - Sony Xperia 10 Plus   (SDM636 Ganges Mermaid)

Changes in v2:
- Fixed sparse warnings from lkp kernel test robot

Changes in v3 (as requested by Dmitry Torokhov):
- Using shorthand u16/u32 (sorry for the overlook!)
- Now using more input and touchscreen APIs
- Fixed useless workqueue involvements
- Removed useless locking
- Switched reads and writes to use regmap
- Moved header contents to nt36xxx.c
- Fixed reset gpio handling
- Other cleanups
- P.S.: Thanks, Dmitry!

Changes in v4:
- Fixed regmap read length for CRC_ERR_FLAG final check
- Fixed YAML binding, as requested by Krzysztof Kozlowski

Changes in v5:
- Replaced subsystem maintainer's name with .. mine,
  usage of additionalProperties to unevaluatedProperties
  and a typo fix for reset-gpios as per Rob Herring's review
- Changed compatible string as per Krzysztof K. request
- Renamed the novatek,nt36xxx.yaml file to just nt36xxx.yaml
  in order to now reflect the driver name instead of the DT
  compatible
- Fixed blank line at EOF

Changes in v6:
- Removed include of_gpio.h, added mod_devicetable.h and
  gpio/consumer.h
- Added kerneldoc to relevant functions/enum
- Used traditional patterns for error checking where possible
- Documented calls to usleep/msleep
- Using be16_to_cpu / get_unaligned_be16 where possible
- Added helper for CRC error check on retrieved buffer
- Decreased indentation in the CRC reboot recovery function
- Removed instances of error code sum
- Dropped all likely/unlikely optimization as per request
- Removed redundant reset_gpio checks
- Dropped of_match_ptr and ifdefs for CONFIG_OF

Changes in v7:
- Fixed typo in nt36xxx.c

Changes in v8:
- Fixed typo reset-gpio -> reset-gpios in dt-bindings

Changes in v9:
- Includes are now sorted
- Used proposed sizeof variable instead of sizeof type
- Fixed a return value check for common pattern
- Added NULL check to devm_kasprintf call
- Returning ret on probe function to be consistent

Changes in v10:
- Take over this series from Angelo
- Update Angelo's e-mail address
- Rebase over linux-next
- Pick up tags
- Drop patch adding novatek vendor prefix (it's already there)
- Reword the bindings title and commit title (bindings describe the
  hardware yada yada)
- Solve trivial checkpatch warnings
- Adapt to i2c_probe arguments changing since 2020
- Open-code nt36xxx_mem_map assignments
- Make inline hex lowercase
- Indent nt36xxx_trim_table with tabs
- Add "-i2c" suffix to the compatible, as these chips also can be
  connected over SPI, keep bindings reviews in good faith
- Don't duplicate enum/struct definitions in .c and .h (how did that go
  unnoticed)
- Fix a couple typos
- Use pm_ptr()
- Use consistent whitespacing for defines
- Remove unused defines
- Include nt36xxx.h from nt36xxx.c (........)
- Re-tested on a Sony Xperia 10
Link to v9: https://lore.kernel.org/linux-input/20201028221302.66583-1-kholk11@gmail.com/

--
2.28.0

---
AngeloGioacchino Del Regno (2):
      dt-bindings: touchscreen: Add binding for Novatek NT36xxx ICs
      Input: Add Novatek NT36xxx touchscreen driver

 .../bindings/input/touchscreen/nt36xxx.yaml        |  59 ++
 drivers/input/touchscreen/Kconfig                  |  12 +
 drivers/input/touchscreen/Makefile                 |   1 +
 drivers/input/touchscreen/nt36xxx.c                | 847 +++++++++++++++++++++
 drivers/input/touchscreen/nt36xxx.h                |  89 +++
 5 files changed, 1008 insertions(+)
---
base-commit: 71cd4fc492ec41e4acd85e98bbf7a13753fc1e03
change-id: 20230808-topic-nt36xxx-a41d0c64d5ae

Best regards,
-- 
Konrad Dybcio <konrad.dybcio@linaro.org>


^ permalink raw reply

* Re: [PATCH] Revert "Input: synaptics - enable InterTouch for the ThinkPad P1 G3"
From: Lyude Paul @ 2023-08-08 22:08 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Sebastian Wick, Aman Dhoot, Mark Pearson, Raul Rangel,
	Wolfram Sang, Andrew Duggan, linux-input, linux-kernel
In-Reply-To: <ZNK8aG/y4ol7MXVp@google.com>

On Tue, 2023-08-08 at 15:06 -0700, Dmitry Torokhov wrote:
> On Tue, Aug 08, 2023 at 05:49:23PM -0400, Lyude Paul wrote:
> > Hm, This looks fine to me (if not as a final fix, certainly as a workaround)
> > 
> > Reviewed-by: Lyude Paul <lyude@redhat.com>
> > 
> > However, this is definitely something we probably should try to fix at some
> > point - as I imagine this must be happening because something regarding how
> > clicks are reported must have changed again. Andrew, do you have any idea?
> 
> I would like to understand more about this. Is this with newer/older
> BIOS, or another hardware revision of the product, or something else?


oops, somehow when typing that response I totally forgot I was responding to a
revert ._., which changes everything

so yes - we should definitely look into this a bit more, I still have access
to this machine so I'll try to take a look tomorrow and see what's up
> 
> Lyude, do you still have access to the unit you originally developed
> the original change for? Is the behavior broken there as well?
> 
> Thanks.
> 

-- 
Cheers,
 Lyude Paul (she/her)
 Software Engineer at Red Hat


^ permalink raw reply

* Re: [PATCH] Revert "Input: synaptics - enable InterTouch for the ThinkPad P1 G3"
From: Dmitry Torokhov @ 2023-08-08 22:06 UTC (permalink / raw)
  To: Lyude Paul
  Cc: Sebastian Wick, Aman Dhoot, Mark Pearson, Raul Rangel,
	Wolfram Sang, Andrew Duggan, linux-input, linux-kernel
In-Reply-To: <f13f4ff7d46ecf887ca652b664860c4e07ec8b73.camel@redhat.com>

On Tue, Aug 08, 2023 at 05:49:23PM -0400, Lyude Paul wrote:
> Hm, This looks fine to me (if not as a final fix, certainly as a workaround)
> 
> Reviewed-by: Lyude Paul <lyude@redhat.com>
> 
> However, this is definitely something we probably should try to fix at some
> point - as I imagine this must be happening because something regarding how
> clicks are reported must have changed again. Andrew, do you have any idea?

I would like to understand more about this. Is this with newer/older
BIOS, or another hardware revision of the product, or something else?

Lyude, do you still have access to the unit you originally developed
the original change for? Is the behavior broken there as well?

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH] Revert "Input: synaptics - enable InterTouch for the ThinkPad P1 G3"
From: Lyude Paul @ 2023-08-08 21:49 UTC (permalink / raw)
  To: Sebastian Wick, Dmitry Torokhov, Aman Dhoot, Mark Pearson,
	Raul Rangel, Wolfram Sang, Andrew Duggan
  Cc: linux-input, linux-kernel
In-Reply-To: <20230808152817.304836-1-sebastian.wick@redhat.com>

Hm, This looks fine to me (if not as a final fix, certainly as a workaround)

Reviewed-by: Lyude Paul <lyude@redhat.com>

However, this is definitely something we probably should try to fix at some
point - as I imagine this must be happening because something regarding how
clicks are reported must have changed again. Andrew, do you have any idea?

On Tue, 2023-08-08 at 17:28 +0200, Sebastian Wick wrote:
> This reverts commit 7984b43542070f5888546d95b48003c4a8af7c0f to make the
> touchpad usable again.
> 
> Tapping does not generate any events for user space and moving the
> cursor is janky. Disabling InterTouch fixes those issues.
> 
> Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
> ---
>  drivers/input/mouse/synaptics.c | 1 -
>  1 file changed, 1 deletion(-)
> 
> diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
> index ada299ec5bba..0e01df88cf69 100644
> --- a/drivers/input/mouse/synaptics.c
> +++ b/drivers/input/mouse/synaptics.c
> @@ -182,7 +182,6 @@ static const char * const smbus_pnp_ids[] = {
>  	"LEN0099", /* X1 Extreme Gen 1 / P1 Gen 1 */
>  	"LEN009b", /* T580 */
>  	"LEN0402", /* X1 Extreme Gen 2 / P1 Gen 2 */
> -	"LEN040f", /* P1 Gen 3 */
>  	"LEN200f", /* T450s */
>  	"LEN2044", /* L470  */
>  	"LEN2054", /* E480 */

-- 
Cheers,
 Lyude Paul (she/her)
 Software Engineer at Red Hat


^ permalink raw reply

* Re: Proposal: Add a means to disable kernel driver logic when associated hidraw is opened
From: Vicki Pfau @ 2023-08-08 21:34 UTC (permalink / raw)
  To: Benjamin Tissoires; +Cc: open list:HID CORE LAYER, Pierre-Loup Griffais
In-Reply-To: <CAO-hwJ+4Hctv6QVSyZxKMCbDt-kFJpwU0iL6+yK60NRvwQihbQ@mail.gmail.com>

Hi Benjamin,

On 4/25/23 07:52, Benjamin Tissoires wrote:
> On Mon, Apr 24, 2023 at 3:07 PM Benjamin Tissoires
> <benjamin.tissoires@redhat.com> wrote:
>>
>> Hi Vicki,
>>
>> On Fri, Apr 21, 2023 at 4:36 AM Vicki Pfau <vi@endrift.com> wrote:
>>>
>>> Hello,
>>>
>>> Following up on a conversation from last year (cf. https://lore.kernel.org/linux-input/CAO-hwJLfY+D0NyCUCncrjcXETKwOBqj1CuHtB_mtGhYfKV0Bww@mail.gmail.com/) about how to approach differing opinions about how drivers should work between userspace and kernel, we're looking for a way to effectively stop the kernel from doing anything "smart" with a a HID device, e.g. a hid-sony device, when the associated hidraw is opened. At the moment, we have a specialized daemon that will find the mouse evdev associated with the controller when it's created and inhibit it, but this is anything but ideal and only handles the mouse itself, not the kernel logic in general. You can also see the hackish way this is implemented in the hid-steam driver, which uses an intermediary hid ll_driver to intercept the open and close commands and stop passing data if the hidraw gets opened. I consider this implementation to be very much a hack and an anti-pattern, and I think other HID implementers would agree, but I'm not sure there's a cleaner way to detect this in the kernel at the moment.
>>>
>>> I see a bit of a difference of opinion as to what should be happening here between developers on our end and the kernel end. Our position is "we have a userspace driver that does everything we want, we'd like the kernel to stop trying to be smart when our driver is active", and I expect the kernel developer opinion is "why should you have a userspace driver at all when our kernel driver is fine, and if it's not you can upstream patches?", so there's probably some need to find a middle ground if we want things to work well for users in the end.
>>
>> The main problem we have here, is that if userspace is actively using
>> hidraw to configure the device, the kernel is not aware, and thus its
>> state will be wrong. So my initial reaction is "we need to have ad-hoc
>> and opt-in functionality for these, because the states the kernel
>> needs to monitor will differ from one device to one other". But this
>> opt-in could (should) be controlled from userspace through eBPF.
>>
>> OTOH, maybe the simplest solution would be to entirely unbind the
>> device from the kernel driver when steam opens and rebinds it when it
>> closes. This way the kernel will reinitialize the device itself
>> properly and keep its state coherent. We can think of multiple
>> solutions for disabling the driver and still keeping hidraw open, and
>> guess what, eBPF is one of them :)
>>
>> Note though that disabling the kernel module might require some
>> changes in the kernel driver itself, so maybe that's not the best path
>> too. (eBPF can not change any quirks on a given device, but maybe that
>> is something we can think of).
>>
>>>
>>> We'd previously discussed an ioctl for the hidraw, or perhaps an eBPF approach, though I think at the time of that discussion, eBPF wasn't mature for the HID subsystem. I don't know the current state of that, or if it's possible to do this with just write access to the hidraw device (the ideal case for how we want to handle this--root is pretty much out of the question for an "ideal case"). Since we still don't have a good answer for this, as far as I'm aware, I'd like to try to reach an approach that's amicable for both sides.
>>
>> Good timing. Kernel 6.3 was out just yesterday, and HID-BPF is in :)
>> (well, not all use cases I envisioned are implemented, but at least
>> the bulk is there).
>>
>>>
>>> I know that the ioctl approach meant having to introduce logic for programs in userspace, but in this specific case, and possibly others, that's actually what we're looking for. Further, the reason root is out is because Steam isn't the OS (in most cases), and we want this to only happen when Steam is running. Having to run a daemon as root underneath Steam would require something like a setuid binary or custom sudo/polkit rules, which of course need root to set up anyway. So while eBPF may fit some use cases, e.g. full control over the system via DE or systemd, etc, it's not what we're looking for here.
>>
>> I think you are dismissing eBPF too quickly :)
>>
>> Basically, eBPF doesn't require you to have a daemon loaded: you can
>> load the BPF program, attach it to the device, pin it in the kernel
>> and close your eBPF loader. So all you need is a new udev rule that
>> calls for a program to load the eBPF program just at plugging time.
>> Eventually, for such  "no-daemon" programs, the kernel itself will
>> load those programs, and userspace can just override them if they are
>> not up to date.
>>
>> Which means that when you load the eBPF program, you just "patched"
>> the kernel with your current API.
>>
>> [Side note: my push for eBPF is because this is exactly the kind of
>> use case I envisioned for it: an app (or set of apps) wants to tweak
>> the kernel behavior only when it is started, and wants to be in
>> control. If this app wants to change some behaviour, fixing the eBPF
>> program is way simpler and can be done in sync with the app update
>> rather than updating the API introduced in the kernel]
>>
>> I think the following (in pseudo-ish-code) would work for
> [...]
> 
> I got a working WIP at
> https://gitlab.freedesktop.org/bentiss/udev-hid-bpf/-/tree/hidraw-close
> 
> I need a couple of kernel changes to allow for the introspection of
> the hidraw device from the struct hid_device and to let
> hid-playstation go with the events.
> 
> We probably still want the opt-in capability, so we'd also need the
> hid_hw_request() BPF hook set up, but this should give you an idea
> that BPF could be a simple and elegant solution.
> 
> Cheers,
> Benjamin
> 

Thanks for preparing this proof of concept. Does it require kernel 6.3 to work? For the time being I'm trying to get something going on 6.1, and am relying on a user space daemon to manage the `inhibited` node directly. I was hoping to switch over to a more crash-resistant method of doing this, but if it requires 6.2 or 6.3, I will probably have to stick with this for the time being. I'll switch over to an approach like this when possible, but I'm not sure when that will be.

Thanks,
Vicki

^ permalink raw reply

* Re: atkbd input regression
From: José Ramón Muñoz Pekkarinen @ 2023-08-08  5:53 UTC (permalink / raw)
  To: Linux regressions mailing list; +Cc: linux-input, dmitry.torokhov, gregkh
In-Reply-To: <a65bd290-52d5-475c-0234-85759c5892a2@leemhuis.info>

On Mon, 7 Aug 2023 at 10:53, Linux regression tracking (Thorsten
Leemhuis) <regressions@leemhuis.info> wrote:
>
> [CCing the regression list, as it should be in the loop for regressions:
> https://docs.kernel.org/admin-guide/reporting-regressions.html]
>
> Hi, Thorsten here, the Linux kernel's regression tracker.
>
> On 07.08.23 07:19, José Ramón Muñoz Pekkarinen wrote:
> >     Sorry again, now in plaintext mode.
> >
> >     Hi,
> >
> >     I'd like to seek help or advise in a possible
> > regression that I detected on my system(a
> > Thinkpad L14 Gen 1) that concerns the atkbd
> > module. My OS is a gentoo, with self built
> > kernels, and a plasma desktop environment
> > using wayland. Up to the kernel 5.16.x my
> > builtin keyboard worked(and works still)
> > fine both in tty and the plasma session, but
> > from that kernel on, the keyboard works only
> > in tty, and no longer in the plasma session.
>
> Could you try bisecting this? A few howtos on the net describe what to
> do. Be sure to use the configuration for your working 5.16 kernel for
> any builds of newer versions (ideally put it <somewhere> and run "cp
> <somewhere> .config; make oldconfig" every time before building a newer
> kernel.

    I'm afraid I run into unexpected results, since rebuilding the
same version of the kernel that works resulted in a non working
keyboard for my kernel 5.16 also, and I needed to recover the
former build from some backup. This make me think the problem
could be somewhere else.

    Thanks!

    José

^ permalink raw reply

* [PATCH 2/2] Input: cs40l50 - Initial support for Cirrus Logic CS40L50
From: James Ogletree @ 2023-08-08 17:25 UTC (permalink / raw)
  To: dmitry.torokhov
  Cc: James Ogletree, Fred Treven, Ben Bright, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Lee Jones, Peng Fan,
	Jean Delvare, Jeff LaBundy, Randy Dunlap, Arnd Bergmann,
	Eddie James, Jacky Bai, Markus Schneider-Pargmann, ChiYuan Huang,
	Jerome Neanne, patches, linux-input, devicetree, linux-kernel
In-Reply-To: <20230808172511.665787-1-james.ogletree@cirrus.com>

Introduce support for Cirrus Logic Device CS40L50: a
haptics driver with waveform memory DSP and closed-loop
algorithms.

Change-Id: I468e8f726e81072ebea19b2dfbe23ca775fedc97
Signed-off-by: James Ogletree <james.ogletree@cirrus.com>
---
 MAINTAINERS                      |    2 +
 drivers/input/misc/Kconfig       |   33 +
 drivers/input/misc/Makefile      |    3 +
 drivers/input/misc/cs40l50-i2c.c |   67 ++
 drivers/input/misc/cs40l50-spi.c |   67 ++
 drivers/input/misc/cs40l50.c     | 1013 ++++++++++++++++++++++++++++++
 include/linux/input/cs40l50.h    |  321 ++++++++++
 7 files changed, 1506 insertions(+)
 create mode 100644 drivers/input/misc/cs40l50-i2c.c
 create mode 100644 drivers/input/misc/cs40l50-spi.c
 create mode 100644 drivers/input/misc/cs40l50.c
 create mode 100644 include/linux/input/cs40l50.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 016e5ff3b831..f1d913166166 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2089,6 +2089,8 @@ L:	patches@Qopensource.cirrus.com
 S:	Supported
 T:	git https://github.com/CirrusLogic/linux-drivers.git
 F:	Documentation/devicetree/bindings/input/cirrus,cs40l50.yaml
+F:	drivers/input/misc/cs40l*
+F:	include/linux/input/cs40l*
 
 ARM/CLKDEV SUPPORT
 M:	Russell King <linux@armlinux.org.uk>
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index c47eecc6f83b..fa0bdd23f87d 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -140,6 +140,39 @@ config INPUT_BMA150
 	  To compile this driver as a module, choose M here: the
 	  module will be called bma150.
 
+config INPUT_CS40L50
+	tristate "Cirrus Logic CS40L50 Haptic Driver support"
+	select CS_DSP
+	select REGMAP_IRQ
+	help
+	  Say Y here to enable support for CS40L50 boosted haptic
+	  driver.
+
+	  To compile the driver as a module choose M here: the module
+	  will be called cs40l50_core.
+
+config INPUT_CS40L50_I2C
+	tristate "Support I2C bus connection"
+	depends on INPUT_CS40L50 && I2C
+	select REGMAP_I2C
+	help
+	  Say Y here if you have Cirrus Logic's CS40L50 connected
+	  to an I2C bus.
+
+	  To compile the driver as a module choose M here: the
+	  module will be called cs40l50_i2c
+
+config INPUT_CS40L50_SPI
+	tristate "Support SPI bus connection"
+	depends on INPUT_CS40L50 && SPI
+	select REGMAP_SPI
+	help
+	  Say Y here if you have Cirrus Logic's CS40L50 connected
+	  to a SPI bus.
+
+	  To compile the driver as a module choose M here: the
+	  module will be called cs40l50_spi.
+
 config INPUT_E3X0_BUTTON
 	tristate "NI Ettus Research USRP E3xx Button support."
 	default n
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 04296a4abe8e..77f34a33c364 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -28,6 +28,9 @@ obj-$(CONFIG_INPUT_CMA3000)		+= cma3000_d0x.o
 obj-$(CONFIG_INPUT_CMA3000_I2C)		+= cma3000_d0x_i2c.o
 obj-$(CONFIG_INPUT_COBALT_BTNS)		+= cobalt_btns.o
 obj-$(CONFIG_INPUT_CPCAP_PWRBUTTON)	+= cpcap-pwrbutton.o
+obj-$(CONFIG_INPUT_CS40L50)		+= cs40l50.o
+obj-$(CONFIG_INPUT_CS40L50_I2C)		+= cs40l50-i2c.o
+obj-$(CONFIG_INPUT_CS40L50_SPI)		+= cs40l50-spi.o
 obj-$(CONFIG_INPUT_DA7280_HAPTICS)	+= da7280.o
 obj-$(CONFIG_INPUT_DA9052_ONKEY)	+= da9052_onkey.o
 obj-$(CONFIG_INPUT_DA9055_ONKEY)	+= da9055_onkey.o
diff --git a/drivers/input/misc/cs40l50-i2c.c b/drivers/input/misc/cs40l50-i2c.c
new file mode 100644
index 000000000000..bab07debdaa9
--- /dev/null
+++ b/drivers/input/misc/cs40l50-i2c.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * CS40L50 Advanced Haptic Driver with Waveform Memory DSP
+ * and Closed-Loop Algorithms
+ *
+ * Copyright 2023 Cirrus Logic, Inc.
+ *
+ */
+#include <linux/i2c.h>
+#include <linux/input/cs40l50.h>
+
+static const struct i2c_device_id cs40l50_id_i2c[] = {
+	{"cs40l50", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, cs40l50_id_i2c);
+
+static const struct of_device_id cs40l50_of_match[] = {
+	{ .compatible = "cirrus,cs40l50" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, cs40l50_of_match);
+
+static int cs40l50_i2c_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct cs40l50_private *cs40l50;
+
+	cs40l50 = devm_kzalloc(dev, sizeof(struct cs40l50_private), GFP_KERNEL);
+	if (!cs40l50)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, cs40l50);
+
+	cs40l50->regmap = devm_regmap_init_i2c(client, &cs40l50_regmap);
+	if (IS_ERR(cs40l50->regmap))
+		return dev_err_probe(cs40l50->dev, PTR_ERR(cs40l50->regmap),
+				"Failed to initialize register map\n");
+
+	cs40l50->dev = dev;
+	cs40l50->irq = client->irq;
+
+	return cs40l50_probe(cs40l50);
+}
+
+static void cs40l50_i2c_remove(struct i2c_client *client)
+{
+	struct cs40l50_private *cs40l50 = i2c_get_clientdata(client);
+
+	cs40l50_remove(cs40l50);
+}
+
+static struct i2c_driver cs40l50_i2c_driver = {
+	.driver = {
+		.name = "cs40l50",
+		.of_match_table = cs40l50_of_match,
+	},
+	.id_table = cs40l50_id_i2c,
+	.probe_new = cs40l50_i2c_probe,
+	.remove = cs40l50_i2c_remove,
+};
+
+module_i2c_driver(cs40l50_i2c_driver);
+
+MODULE_DESCRIPTION("CS40L50 I2C Driver");
+MODULE_AUTHOR("James Ogletree, Cirrus Logic Inc. <james.ogletree@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/cs40l50-spi.c b/drivers/input/misc/cs40l50-spi.c
new file mode 100644
index 000000000000..dbaa185b6bbd
--- /dev/null
+++ b/drivers/input/misc/cs40l50-spi.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * CS40L50 Advanced Haptic Driver with Waveform Memory DSP
+ * and Closed-Loop Algorithms
+ *
+ * Copyright 2023 Cirrus Logic, Inc.
+ *
+ */
+#include <linux/spi/spi.h>
+#include <linux/input/cs40l50.h>
+
+static const struct spi_device_id cs40l50_id_spi[] = {
+	{"cs40l50", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(spi, cs40l50_id_spi);
+
+static const struct of_device_id cs40l50_of_match[] = {
+	{ .compatible = "cirrus,cs40l50" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, cs40l50_of_match);
+
+static int cs40l50_spi_probe(struct spi_device *spi)
+{
+	struct cs40l50_private *cs40l50;
+	struct device *dev = &spi->dev;
+
+	cs40l50 = devm_kzalloc(dev, sizeof(struct cs40l50_private), GFP_KERNEL);
+	if (!cs40l50)
+		return -ENOMEM;
+
+	spi_set_drvdata(spi, cs40l50);
+
+	cs40l50->regmap = devm_regmap_init_spi(spi, &cs40l50_regmap);
+	if (IS_ERR(cs40l50->regmap))
+		return dev_err_probe(cs40l50->dev, PTR_ERR(cs40l50->regmap),
+					"Failed to initialize register map\n");
+
+	cs40l50->dev = dev;
+	cs40l50->irq = spi->irq;
+
+	return cs40l50_probe(cs40l50);
+}
+
+static void cs40l50_spi_remove(struct spi_device *spi)
+{
+	struct cs40l50_private *cs40l50 = spi_get_drvdata(spi);
+
+	cs40l50_remove(cs40l50);
+}
+
+static struct spi_driver cs40l50_spi_driver = {
+	.driver = {
+		.name = "cs40l50",
+		.of_match_table = cs40l50_of_match,
+	},
+	.id_table = cs40l50_id_spi,
+	.probe = cs40l50_spi_probe,
+	.remove = cs40l50_spi_remove,
+};
+
+module_spi_driver(cs40l50_spi_driver);
+
+MODULE_DESCRIPTION("CS40L50 SPI Driver");
+MODULE_AUTHOR("James Ogletree, Cirrus Logic Inc. <james.ogletree@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/cs40l50.c b/drivers/input/misc/cs40l50.c
new file mode 100644
index 000000000000..17c8d756b432
--- /dev/null
+++ b/drivers/input/misc/cs40l50.c
@@ -0,0 +1,1013 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * CS40L50 Advanced Haptic Driver with Waveform Memory DSP
+ * and Closed-Loop Algorithms
+ *
+ * Copyright 2023 Cirrus Logic, Inc.
+ *
+ */
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/bitfield.h>
+#include <linux/input/cs40l50.h>
+
+const struct regmap_config cs40l50_regmap = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.reg_format_endian = REGMAP_ENDIAN_BIG,
+	.val_format_endian = REGMAP_ENDIAN_BIG,
+};
+EXPORT_SYMBOL_GPL(cs40l50_regmap);
+
+static struct regulator_bulk_data cs40l50_supplies[] = {
+	{ .supply = "VP" },
+	{ .supply = "VIO" },
+	{ .supply = "VA" },
+};
+
+static int cs40l50_dsp_write(struct cs40l50_private *cs40l50, u32 write_reg, u32 write_val)
+{
+	int error = 0, i;
+
+	for (i = 0; i < CS40L50_DSP_TIMEOUT_COUNT; i++) {
+		error = regmap_write(cs40l50->regmap, write_reg, write_val);
+		if (!error)
+			break;
+
+		usleep_range(CS40L50_DSP_POLL_US, CS40L50_DSP_POLL_US + 100);
+	}
+
+	if (i == CS40L50_DSP_TIMEOUT_COUNT) {
+		dev_err(cs40l50->dev, "Error writing %#X to %#X: %d", write_val, write_reg, error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int cs40l50_dsp_read(struct cs40l50_private *cs40l50, u32 read_reg, u32 *read_val)
+{
+	int error = 0, i;
+
+	for (i = 0; i < CS40L50_DSP_TIMEOUT_COUNT; i++) {
+		error = regmap_read(cs40l50->regmap, read_reg, read_val);
+		if (!error)
+			break;
+
+		usleep_range(CS40L50_DSP_POLL_US, CS40L50_DSP_POLL_US + 100);
+	}
+
+	if (i == CS40L50_DSP_TIMEOUT_COUNT) {
+		dev_err(cs40l50->dev, "Error reading %#X: %d", read_reg, error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int cs40l50_pseq_write(struct cs40l50_private *cs40l50, u32 addr, u32 data)
+{
+	u8 operation = 0;
+	struct cs40l50_pseq_op *op_new, *op_end;
+	int error;
+
+	op_new = devm_kzalloc(cs40l50->dev, sizeof(struct cs40l50_pseq_op), GFP_KERNEL);
+	if (!op_new)
+		return -ENOMEM;
+
+	op_new->words[0] = (addr >> CS40L50_PSEQ_WRITE_FULL_UPPER_ADDR_SHIFT);
+	op_new->words[1] = ((addr & CS40L50_PSEQ_WRITE_FULL_LOWER_ADDR_MASK) <<
+			CS40L50_PSEQ_WRITE_FULL_LOWER_ADDR_SHIFT) |
+			(data >> CS40L50_PSEQ_WRITE_FULL_UPPER_DATA_SHIFT);
+	op_new->words[2] = data & CS40L50_PSEQ_WRITE_FULL_LOWER_DATA_MASK;
+
+	list_for_each_entry(op_end, &cs40l50->pseq_op_head, list) {
+		operation = op_end->operation;
+		if (operation == CS40L50_PSEQ_OP_END)
+			break;
+	}
+
+	if (operation != CS40L50_PSEQ_OP_END) {
+		error = -ENOENT;
+		goto op_new_free;
+	}
+
+	if ((CS40L50_PSEQ_MAX_WORDS - op_end->offset) < ARRAY_SIZE(op_new->words)) {
+		error = -ENOSPC;
+		goto op_new_free;
+	}
+
+	op_new->offset = op_end->offset;
+	op_end->offset += (ARRAY_SIZE(op_new->words) * sizeof(u32));
+
+	error = regmap_bulk_write(cs40l50->regmap, CS40L50_DSP1_POWER_ON_SEQ + op_new->offset,
+			op_new->words, ARRAY_SIZE(op_new->words));
+	if (error)
+		goto op_new_free;
+
+	error = cs40l50_dsp_write(cs40l50, CS40L50_DSP1_POWER_ON_SEQ + op_end->offset,
+			op_end->words[0]);
+	if (error)
+		goto op_new_free;
+
+	list_add(&op_new->list, &cs40l50->pseq_op_head);
+
+	return 0;
+
+op_new_free:
+	devm_kfree(cs40l50->dev, op_new);
+
+	return error;
+}
+
+static struct cs40l50_effect *cs40l50_find_effect(struct cs40l50_private *cs40l50, int id)
+{
+	struct cs40l50_effect *effect;
+
+	if (!list_empty(&cs40l50->effect_head)) {
+		list_for_each_entry(effect, &cs40l50->effect_head, list) {
+			if (effect->id == id)
+				return effect;
+		}
+	}
+
+	return NULL;
+}
+
+static int cs40l50_owt_upload(struct cs40l50_private *cs40l50, s16 *in_data, u32 in_data_nibbles)
+{
+	size_t in_data_bytes = 2 * in_data_nibbles;
+	struct cs40l50_owt_header header;
+	u32 wt_offset, wt_size_words;
+	int error;
+	u8 *data;
+
+	error = cs40l50_dsp_read(cs40l50, CS40L50_DSP1_OWT_NEXT, &wt_offset);
+	if (error)
+		return error;
+
+	error = cs40l50_dsp_read(cs40l50, CS40L50_DSP1_OWT_SIZE, &wt_size_words);
+	if (error)
+		return error;
+
+	if ((wt_size_words * sizeof(u32)) < CS40L50_WT_HEADER_PWLE_SIZE + in_data_bytes)
+		return -ENOSPC;
+
+	data = kcalloc(CS40L50_WT_HEADER_PWLE_SIZE + in_data_bytes, sizeof(u8), GFP_KERNEL);
+	if (IS_ERR_OR_NULL(data))
+		return -ENOMEM;
+
+	header.wvfrm_type = in_data[0] == CS40L50_PCM_ID ? CS40L50_WT_TYPE_PCM :
+			CS40L50_WT_TYPE_PWLE;
+	header.offset = CS40L50_WT_HEADER_OFFSET;
+	header.data_size = in_data_bytes / sizeof(u32);
+
+	memcpy(data, &header, sizeof(struct cs40l50_owt_header));
+	memcpy(data + (CS40L50_WT_HEADER_OFFSET * sizeof(u32)), in_data, in_data_bytes);
+
+	error = regmap_bulk_write(cs40l50->regmap, CS40L50_DSP1_OWT_BASE +
+			(wt_offset * sizeof(u32)), data,
+			CS40L50_WT_HEADER_PWLE_SIZE + in_data_bytes);
+	if (error)
+		goto err_free;
+
+	error = cs40l50_dsp_write(cs40l50, CS40L50_DSP_VIRTUAL1_MBOX_1, CS40L50_MBOX_CMD_OWT_PUSH);
+err_free:
+	kfree(data);
+
+	return error;
+}
+
+static int cs40l50_add_effect(struct input_dev *dev, struct ff_effect *effect,
+		struct ff_effect *old)
+{
+	struct cs40l50_private *cs40l50 = input_get_drvdata(dev);
+	bool is_new = false;
+	struct cs40l50_effect *new_effect;
+	u32 nwaves, base_index, max_index;
+	s16 *raw_custom_data;
+	int error;
+
+	if (effect->type != FF_PERIODIC || effect->u.periodic.waveform != FF_CUSTOM) {
+		dev_err(cs40l50->dev, "Type (%#X) or waveform (%#X) unsupported\n",
+				effect->type, effect->u.periodic.waveform);
+		return -EINVAL;
+	}
+
+	raw_custom_data = kcalloc(effect->u.periodic.custom_len, sizeof(s16), GFP_KERNEL);
+	if (IS_ERR_OR_NULL(raw_custom_data))
+		return -ENOMEM;
+
+	error = copy_from_user(raw_custom_data, effect->u.periodic.custom_data,
+			sizeof(s16) * effect->u.periodic.custom_len);
+	if (error)
+		goto out_free;
+
+	mutex_lock(&cs40l50->lock);
+
+	new_effect = cs40l50_find_effect(cs40l50, effect->id);
+	if (!new_effect) {
+		is_new = true;
+		new_effect = kzalloc(sizeof(struct cs40l50_effect), GFP_KERNEL);
+		if (IS_ERR_OR_NULL(new_effect)) {
+			error = -ENOMEM;
+			goto err_mutex;
+		}
+	}
+
+	if (effect->u.periodic.custom_len > CS40L50_CUSTOM_DATA_SIZE) {
+		error = cs40l50_dsp_read(cs40l50, CS40L50_NUM_OF_OWT_WAVES,
+				&new_effect->trigger_index);
+		if (error)
+			goto err_free;
+
+		new_effect->wvfrm_bank = CS40L50_WVFRM_BANK_OWT;
+		base_index = CS40L50_OWT_INDEX_START;
+		max_index = CS40L50_OWT_INDEX_END;
+	} else {
+		new_effect->trigger_index = (raw_custom_data[1] & 0xffffu);
+		new_effect->wvfrm_bank = (raw_custom_data[0] & 0xffffu);
+
+		if (new_effect->wvfrm_bank == CS40L50_WVFRM_BANK_ROM) {
+			base_index = CS40L50_ROM_BANK_INDEX_START;
+			max_index = CS40L50_ROM_BANK_INDEX_END;
+		} else if (new_effect->wvfrm_bank == CS40L50_WVFRM_BANK_RAM) {
+			error = cs40l50_dsp_read(cs40l50, CS40L50_NUM_OF_WAVES, &nwaves);
+			if (error)
+				goto err_free;
+
+			base_index = CS40L50_RAM_BANK_INDEX_START;
+			max_index = base_index + nwaves - 1;
+		} else {
+			dev_err(cs40l50->dev, "Invalid waveform bank\n");
+			error = -EINVAL;
+			goto err_free;
+		}
+	}
+
+	new_effect->trigger_index += base_index;
+
+	if (new_effect->trigger_index > max_index) {
+		dev_err(cs40l50->dev, "Index %#X out of bounds\n", new_effect->trigger_index);
+		error = -EINVAL;
+		goto err_free;
+	}
+
+	if (effect->trigger.button) {
+		new_effect->mapping = CS40L50_DSP1_GPIO_BASE + sizeof(u32) *
+				FIELD_GET(CS40L50_BTN_NUM_MASK, effect->trigger.button) * 2 -
+				FIELD_GET(CS40L50_BTN_EDGE_MASK, effect->trigger.button);
+
+		error = cs40l50_dsp_write(cs40l50, new_effect->mapping, effect->trigger.button);
+		if (error)
+			goto err_free;
+	}
+
+	if (new_effect->wvfrm_bank == CS40L50_WVFRM_BANK_OWT) {
+		error = cs40l50_owt_upload(cs40l50, raw_custom_data,
+				effect->u.periodic.custom_len);
+		if (error)
+			goto err_free;
+	}
+
+err_free:
+	if (is_new)
+		error ? kfree(new_effect) : list_add(&new_effect->list, &cs40l50->effect_head);
+err_mutex:
+	mutex_unlock(&cs40l50->lock);
+out_free:
+	kfree(raw_custom_data);
+
+	return error ? -EFAULT : 0;
+}
+
+
+static void cs40l50_vibe_start_worker(struct work_struct *work)
+{
+	struct cs40l50_private *cs40l50 =
+			container_of(work, struct cs40l50_private, vibe_start_work);
+	struct cs40l50_effect *effect;
+
+	mutex_lock(&cs40l50->lock);
+
+	effect = cs40l50_find_effect(cs40l50, cs40l50->trigger_effect->id);
+	if (effect)
+		cs40l50_dsp_write(cs40l50, CS40L50_DSP_VIRTUAL1_MBOX_1, effect->trigger_index);
+
+	mutex_unlock(&cs40l50->lock);
+}
+
+static void cs40l50_vibe_stop_worker(struct work_struct *work)
+{
+	struct cs40l50_private *cs40l50 =
+			container_of(work, struct cs40l50_private, vibe_stop_work);
+
+	mutex_lock(&cs40l50->lock);
+
+	cs40l50_dsp_write(cs40l50, CS40L50_DSP_VIRTUAL1_MBOX_1, CS40L50_MBOX_CMD_STOP_PLAYBACK);
+
+	mutex_unlock(&cs40l50->lock);
+}
+
+static int cs40l50_playback_effect(struct input_dev *dev, int effect_id, int val)
+{
+	struct cs40l50_private *cs40l50 = input_get_drvdata(dev);
+
+	cs40l50->trigger_effect = &dev->ff->effects[effect_id];
+
+	if (val > 0)
+		queue_work(cs40l50->vibe_workqueue, &cs40l50->vibe_start_work);
+	else
+		queue_work(cs40l50->vibe_workqueue, &cs40l50->vibe_stop_work);
+
+	return 0;
+}
+
+static int cs40l50_erase_effect(struct input_dev *dev, int effect_id)
+{
+	int error = 0;
+	struct cs40l50_private *cs40l50 = input_get_drvdata(dev);
+	struct cs40l50_effect *effect, *effect_owt;
+
+	mutex_lock(&cs40l50->lock);
+
+	effect = cs40l50_find_effect(cs40l50, dev->ff->effects[effect_id].id);
+	if (!effect) {
+		error = -EINVAL;
+		goto err_mutex;
+	}
+
+	if (effect->mapping != CS40L50_GPIO_MAPPING_INVALID) {
+		error = cs40l50_dsp_write(cs40l50, effect->mapping, CS40L50_GPI_DISABLE);
+		if (error)
+			goto err_mutex;
+	}
+
+	if (effect->wvfrm_bank == CS40L50_WVFRM_BANK_OWT) {
+		error = cs40l50_dsp_write(cs40l50, CS40L50_DSP_VIRTUAL1_MBOX_1,
+				CS40L50_MBOX_CMD_OWT_DELETE | effect->trigger_index);
+		if (error)
+			goto err_mutex;
+
+		list_for_each_entry(effect_owt, &cs40l50->effect_head, list) {
+			if (effect_owt->wvfrm_bank == CS40L50_WVFRM_BANK_OWT &&
+					effect_owt->trigger_index > effect->trigger_index)
+				effect_owt->trigger_index--;
+		}
+	}
+
+	list_del(&effect->list);
+	kfree(effect);
+err_mutex:
+	mutex_unlock(&cs40l50->lock);
+
+	return error;
+}
+
+static int cs40l50_mailbox_read_next(struct cs40l50_private *cs40l50, u32 *val)
+{
+	u32 wt_ptr, rd_ptr;
+	int error = 0;
+
+	error = cs40l50_dsp_read(cs40l50, CS40L50_DSP1_MBOX_QUEUE_WT, &wt_ptr);
+	if (error)
+		return error;
+
+	error = cs40l50_dsp_read(cs40l50, CS40L50_DSP1_MBOX_QUEUE_RD, &rd_ptr);
+	if (error)
+		return error;
+
+	if (wt_ptr == rd_ptr) {
+		*val = 0;
+		return 0;
+	}
+
+	error = cs40l50_dsp_read(cs40l50, rd_ptr, val);
+	if (error)
+		return error;
+
+	rd_ptr += sizeof(u32);
+	if (rd_ptr > CS40L50_MBOX_QUEUE_END)
+		rd_ptr = CS40L50_MBOX_QUEUE_BASE;
+
+	return cs40l50_dsp_write(cs40l50, CS40L50_DSP1_MBOX_QUEUE_RD, rd_ptr);
+}
+
+static int cs40l50_handle_f0_est_done(struct cs40l50_private *cs40l50)
+{
+	int error;
+	u32 f_zero;
+
+	error = cs40l50_dsp_read(cs40l50, CS40L50_DSP1_F0_ESTIMATION, &f_zero);
+	if (error)
+		return error;
+
+	return cs40l50_dsp_write(cs40l50, CS40L50_DSP1_F0_STORED, f_zero);
+}
+
+static int cs40l50_handle_redc_est_done(struct cs40l50_private *cs40l50)
+{
+	int integer, fractional, error;
+	u32 redc;
+
+	error = cs40l50_dsp_read(cs40l50, CS40L50_DSP1_RE_EST_STATUS, &redc);
+	if (error)
+		return error;
+
+	error = cs40l50_dsp_write(cs40l50, CS40L50_DSP1_REDC_ESTIMATION, redc);
+	if (error)
+		return error;
+
+	/* Convert from Q8.15 to (Q7.17 * 29/240) */
+	integer = (redc >> 15) & 0xFF;
+	fractional = (redc & 0x7FFF) * 4;
+
+	return cs40l50_dsp_write(cs40l50, CS40L50_DSP1_REDC_STORED,
+		(((integer << 17) | fractional) * 29 / 240) & GENMASK(23, 0));
+}
+
+static int cs40l50_error_release(struct cs40l50_private *cs40l50)
+{
+	int error;
+
+	error = cs40l50_dsp_write(cs40l50, CS40L50_ERROR_RELEASE, CS40L50_ERROR_RELEASE_MASK);
+	if (error)
+		return error;
+
+	return cs40l50_dsp_write(cs40l50, CS40L50_ERROR_RELEASE, 0);
+}
+
+static irqreturn_t cs40l50_handle_mbox_buffer(int irq, void *data)
+{
+	struct cs40l50_private *cs40l50 = data;
+	int error = 0;
+	u32 val;
+
+	mutex_lock(&cs40l50->lock);
+
+	while (!cs40l50_mailbox_read_next(cs40l50, &val)) {
+		switch (val) {
+		case 0:
+			goto out_irq;
+		case CS40L50_DSP_MBOX_HAPTIC_TRIGGER_GPIO:
+			dev_dbg(cs40l50->dev, "Mailbox: TRIGGER_GPIO\n");
+			break;
+		case CS40L50_DSP_MBOX_HAPTIC_TRIGGER_MBOX:
+			dev_dbg(cs40l50->dev, "Mailbox: TRIGGER_MBOX\n");
+			break;
+		case CS40L50_DSP_MBOX_HAPTIC_TRIGGER_I2S:
+			dev_dbg(cs40l50->dev, "Mailbox: TRIGGER_I2S\n");
+			break;
+		case CS40L50_DSP_MBOX_HAPTIC_COMPLETE_MBOX:
+			dev_dbg(cs40l50->dev, "Mailbox: COMPLETE_MBOX\n");
+			break;
+		case CS40L50_DSP_MBOX_HAPTIC_COMPLETE_GPIO:
+			dev_dbg(cs40l50->dev, "Mailbox: COMPLETE_GPIO\n");
+			break;
+		case CS40L50_DSP_MBOX_HAPTIC_COMPLETE_I2S:
+			dev_dbg(cs40l50->dev, "Mailbox: COMPLETE_I2S\n");
+			break;
+		case CS40L50_DSP_MBOX_F0_EST_START:
+			dev_dbg(cs40l50->dev, "Mailbox: F0_EST_START\n");
+			break;
+		case CS40L50_DSP_MBOX_F0_EST_DONE:
+			dev_dbg(cs40l50->dev, "Mailbox: F0_EST_DONE\n");
+			error = cs40l50_handle_f0_est_done(cs40l50);
+			if (error)
+				goto out_irq;
+			break;
+		case CS40L50_DSP_MBOX_REDC_EST_START:
+			dev_dbg(cs40l50->dev, "Mailbox: REDC_EST_START\n");
+			break;
+		case CS40L50_DSP_MBOX_REDC_EST_DONE:
+			dev_dbg(cs40l50->dev, "Mailbox: REDC_EST_DONE\n");
+			error = cs40l50_handle_redc_est_done(cs40l50);
+			if (error)
+				goto out_irq;
+			break;
+		case CS40L50_DSP_MBOX_LE_EST_START:
+			dev_dbg(cs40l50->dev, "Mailbox: LE_EST_START\n");
+			break;
+		case CS40L50_DSP_MBOX_LE_EST_DONE:
+			dev_dbg(cs40l50->dev, "Mailbox: LE_EST_DONE\n");
+			break;
+		case CS40L50_DSP_MBOX_AWAKE:
+			dev_dbg(cs40l50->dev, "Mailbox: AWAKE\n");
+			break;
+		case CS40L50_DSP_MBOX_INIT:
+			dev_dbg(cs40l50->dev, "Mailbox: INIT\n");
+			break;
+		case CS40L50_DSP_MBOX_ACK:
+			dev_dbg(cs40l50->dev, "Mailbox: ACK\n");
+			break;
+		case CS40L50_DSP_MBOX_ERR_EVENT_UNMAPPED:
+			dev_err(cs40l50->dev, "Unmapped event\n");
+			break;
+		case CS40L50_DSP_MBOX_ERR_EVENT_MODIFY:
+			dev_err(cs40l50->dev, "Attempt to modify event index failed\n");
+			break;
+		case CS40L50_DSP_MBOX_ERR_NULLPTR:
+			dev_err(cs40l50->dev, "Null pointer\n");
+			break;
+		case CS40L50_DSP_MBOX_ERR_BRAKING:
+			dev_err(cs40l50->dev, "Braking not in progress\n");
+			break;
+		case CS40L50_DSP_MBOX_ERR_INVAL_SRC:
+			dev_err(cs40l50->dev, "Suspend/resume invalid source\n");
+			break;
+		case CS40L50_DSP_MBOX_ERR_ENABLE_RANGE:
+			dev_err(cs40l50->dev, "GPIO enable out of range\n");
+			break;
+		case CS40L50_DSP_MBOX_ERR_GPIO_UNMAPPED:
+			dev_err(cs40l50->dev, "GPIO not mapped\n");
+			break;
+		case CS40L50_DSP_MBOX_ERR_ISR_RANGE:
+			dev_err(cs40l50->dev, "GPIO ISR out of range\n");
+			break;
+		case CS40L50_DSP_MBOX_PERMANENT_SHORT:
+			dev_crit(cs40l50->dev, "Permanent short detected\n");
+			break;
+		case CS40L50_DSP_MBOX_RUNTIME_SHORT:
+			dev_err(cs40l50->dev, "Runtime short detected\n");
+			error = cs40l50_error_release(cs40l50);
+			if (error)
+				goto out_irq;
+			break;
+		default:
+			dev_err(cs40l50->dev, "Mailbox value %#X not recognized\n", val);
+			error = -EINVAL;
+			goto out_irq;
+		}
+	}
+
+	error = -EIO;
+
+out_irq:
+	mutex_unlock(&cs40l50->lock);
+
+	return IRQ_RETVAL(!error);
+}
+
+
+static irqreturn_t cs40l50_vddb_uv(int irq, void *data)
+{
+	struct cs40l50_private *cs40l50 = data;
+
+	dev_err(cs40l50->dev, "VDD_B undervoltage error\n");
+
+	return IRQ_RETVAL(!cs40l50_error_release(cs40l50));
+}
+
+static irqreturn_t cs40l50_boost_current_limit(int irq, void *data)
+{
+	struct cs40l50_private *cs40l50 = data;
+
+	dev_err(cs40l50->dev, "Boost current limit engagement\n");
+
+	return IRQ_RETVAL(!cs40l50_error_release(cs40l50));
+}
+
+static irqreturn_t cs40l50_boost_short(int irq, void *data)
+{
+	struct cs40l50_private *cs40l50 = data;
+
+	dev_err(cs40l50->dev, "Boost inductor short error\n");
+
+	return IRQ_RETVAL(!cs40l50_error_release(cs40l50));
+}
+
+static irqreturn_t cs40l50_boost_uv(int irq, void *data)
+{
+	struct cs40l50_private *cs40l50 = data;
+
+	dev_err(cs40l50->dev, "Boost undervoltage error\n");
+
+	return IRQ_RETVAL(!cs40l50_error_release(cs40l50));
+}
+
+static irqreturn_t cs40l50_overtemperature(int irq, void *data)
+{
+	struct cs40l50_private *cs40l50 = data;
+
+	dev_err(cs40l50->dev, "Overtemperature error\n");
+
+	return IRQ_RETVAL(!cs40l50_error_release(cs40l50));
+}
+
+static irqreturn_t cs40l50_amp_short(int irq, void *data)
+{
+	struct cs40l50_private *cs40l50 = data;
+
+	dev_err(cs40l50->dev, "Amp short error\n");
+
+	return IRQ_RETVAL(!cs40l50_error_release(cs40l50));
+}
+
+static irqreturn_t cs40l50_global_error(int irq, void *data)
+{
+	struct cs40l50_private *cs40l50 = data;
+
+	dev_err(cs40l50->dev, "MSM chip error\n");
+
+	return IRQ_RETVAL(!cs40l50_error_release(cs40l50));
+}
+
+static const struct cs40l50_irq cs40l50_irqs[] = {
+	CS40L50_IRQ(CS40L50_AMP_SHORT_IRQ, "Amp short", cs40l50_amp_short),
+	CS40L50_IRQ(CS40L50_VIRT2_MBOX_IRQ, "Firmware interrupt", cs40l50_handle_mbox_buffer),
+	CS40L50_IRQ(CS40L50_TEMP_ERR_IRQ, "Overtemperature", cs40l50_overtemperature),
+	CS40L50_IRQ(CS40L50_BST_UVP_IRQ, "Boost undervoltage", cs40l50_boost_uv),
+	CS40L50_IRQ(CS40L50_BST_SHORT_IRQ, "Boost short", cs40l50_boost_short),
+	CS40L50_IRQ(CS40L50_BST_ILIMIT_IRQ, "Boost current limit", cs40l50_boost_current_limit),
+	CS40L50_IRQ(CS40L50_UVLO_VDDBATT_IRQ, "VDD_B undervoltage", cs40l50_vddb_uv),
+	CS40L50_IRQ(CS40L50_GLOBAL_ERROR_IRQ, "Global error", cs40l50_global_error),
+};
+
+static const struct regmap_irq cs40l50_reg_irqs[] = {
+	CS40L50_REG_IRQ(IRQ1_INT_1, AMP_SHORT),
+	CS40L50_REG_IRQ(IRQ1_INT_2, VIRT2_MBOX),
+	CS40L50_REG_IRQ(IRQ1_INT_8, TEMP_ERR),
+	CS40L50_REG_IRQ(IRQ1_INT_9, BST_UVP),
+	CS40L50_REG_IRQ(IRQ1_INT_9, BST_SHORT),
+	CS40L50_REG_IRQ(IRQ1_INT_9, BST_ILIMIT),
+	CS40L50_REG_IRQ(IRQ1_INT_10, UVLO_VDDBATT),
+	CS40L50_REG_IRQ(IRQ1_INT_18, GLOBAL_ERROR),
+};
+
+static const struct regmap_irq_chip cs40l50_regmap_irq_chip = {
+	.name = "cs40l50 IRQ1 Controller",
+	.status_base = CS40L50_IRQ1_INT_1,
+	.mask_base = CS40L50_IRQ1_MASK_1,
+	.ack_base = CS40L50_IRQ1_INT_1,
+	.num_regs = 19,
+	.irqs = cs40l50_reg_irqs,
+	.num_irqs = ARRAY_SIZE(cs40l50_reg_irqs),
+	.runtime_pm = true,
+};
+
+static const struct reg_sequence cs40l50_ext_bst_seq[] = {
+	{ CS40L50_BLOCK_ENABLES, CS40L50_MON_ENABLE },
+	{ CS40L50_MON_VALUE_CTRL, CS40L50_VBST_VDD_AMP },
+	{ CS40L50_TEST_KEY, CS40L50_KEY_SET_1 },
+	{ CS40L50_TEST_KEY, CS40L50_KEY_SET_2 },
+	{ CS40L50_TEST_VIS_SPARE, CS40L50_TEMP_SENSOR_3 },
+	{ CS40L50_MON_ERROR_OVERIDE, CS40L50_TEMP_ERR_THLD },
+	{ CS40L50_STATUS, CS40L50_TEMP_WARN_THLD },
+	{ CS40L50_TEST_CONFIG, CS40L50_TEMP_FILT_DEBOUNCE_OFF },
+	{ CS40L50_TEMP_WARN_CONFIG, CS40L50_TEMP_WARN_ATK_VOL },
+};
+
+static int cs40l50_config_bst(struct cs40l50_private *cs40l50)
+{
+	int error, i;
+
+	if (device_property_present(cs40l50->dev, "cirrus,external-boost")) {
+		error = regmap_multi_reg_write(cs40l50->regmap, cs40l50_ext_bst_seq,
+				ARRAY_SIZE(cs40l50_ext_bst_seq));
+		if (error)
+			return error;
+
+		for (i = 0; i < ARRAY_SIZE(cs40l50_ext_bst_seq); i++) {
+			error = cs40l50_pseq_write(cs40l50, cs40l50_ext_bst_seq[i].reg,
+					cs40l50_ext_bst_seq[i].def);
+			if (error)
+				return error;
+		}
+
+		return 0;
+	}
+
+	/* Internal boost config */
+	return cs40l50_dsp_write(cs40l50, CS40L50_BST_LPMODE_SEL, CS40L50_DCM_LOW_POWER);
+}
+
+static int cs40l50_patch_firmware(struct cs40l50_private *cs40l50)
+{
+	const struct firmware *bin = NULL, *wmfw = NULL;
+	int error = 0;
+
+	if (request_firmware(&bin, "cs40l50.bin", cs40l50->dev))
+		dev_info(cs40l50->dev, "Could not request wavetable file: %d\n", error);
+
+	if (request_firmware(&wmfw, "cs40l50.wmfw", cs40l50->dev))
+		dev_info(cs40l50->dev, "Could not request firmware patch file: %d\n", error);
+
+	if (wmfw) {
+		error = cs40l50_dsp_write(cs40l50, CS40L50_CCM_CORE_CONTROL,
+				CS40L50_DSP1_CLOCK_DISABLE);
+		if (error)
+			goto err_fw;
+
+		error = cs40l50_dsp_write(cs40l50, CS40L50_RAM_INIT,
+				CS40L50_DSP1_RAM_INIT_FLAG);
+		if (error)
+			goto err_fw;
+
+		error = cs40l50_dsp_write(cs40l50, CS40L50_PWRMGT_CTL,
+				CS40L50_DSP1_MEM_RDY_HW);
+		if (error)
+			goto err_fw;
+	}
+
+	mutex_lock(&cs40l50->lock);
+
+	cs_dsp_power_up(&cs40l50->dsp, wmfw, "cs40l50.wmfw", bin, "cs40l50.bin", "cs40l50");
+
+	mutex_unlock(&cs40l50->lock);
+
+	if (wmfw)
+		error = cs40l50_dsp_write(cs40l50, CS40L50_CCM_CORE_CONTROL,
+				CS40L50_DSP1_CLOCK_ENABLE);
+
+err_fw:
+	release_firmware(wmfw);
+	release_firmware(bin);
+
+	return error;
+}
+
+static int cs40l50_pseq_init(struct cs40l50_private *cs40l50)
+{
+	struct cs40l50_pseq_op *pseq_op;
+	int error, i, num_words;
+	u8 operation;
+	u32 *words;
+
+	INIT_LIST_HEAD(&cs40l50->pseq_op_head);
+
+	words = kcalloc(CS40L50_PSEQ_MAX_WORDS, sizeof(u32), GFP_KERNEL);
+	if (IS_ERR_OR_NULL(words))
+		return -ENOMEM;
+
+	error = regmap_bulk_read(cs40l50->regmap, CS40L50_DSP1_POWER_ON_SEQ, words,
+			CS40L50_PSEQ_MAX_WORDS);
+	if (error)
+		goto err_free;
+
+	for (i = 0; i < CS40L50_PSEQ_MAX_WORDS; i++)
+		words[i] = be32_to_cpu(words[i]);
+
+	for (i = 0; i < CS40L50_PSEQ_MAX_WORDS; i += num_words) {
+		operation = (words[i] & CS40L50_PSEQ_OP_MASK) >> CS40L50_PSEQ_OP_SHIFT;
+
+		switch (operation) {
+		case CS40L50_PSEQ_OP_END:
+			num_words = CS40L50_PSEQ_OP_END_WORDS;
+			break;
+		case CS40L50_PSEQ_OP_WRITE_ADDR8:
+		case CS40L50_PSEQ_OP_WRITE_H16:
+		case CS40L50_PSEQ_OP_WRITE_L16:
+			num_words = CS40L50_PSEQ_OP_WRITE_X16_WORDS;
+			break;
+		case CS40L50_PSEQ_OP_WRITE_FULL:
+			num_words = CS40L50_PSEQ_OP_WRITE_FULL_WORDS;
+			break;
+		default:
+			dev_err(cs40l50->dev, "Invalid OP code 0x%02X\n", operation);
+			error = -EINVAL;
+			goto err_free;
+		}
+
+		pseq_op = devm_kzalloc(cs40l50->dev, sizeof(struct cs40l50_pseq_op), GFP_KERNEL);
+		if (!pseq_op) {
+			error = -ENOMEM;
+			goto err_free;
+		}
+
+		memcpy(pseq_op->words, &words[i], num_words * sizeof(u32));
+		pseq_op->size = num_words;
+		pseq_op->offset = i * sizeof(u32);
+		pseq_op->operation = operation;
+		list_add(&pseq_op->list, &cs40l50->pseq_op_head);
+
+		if (operation == CS40L50_PSEQ_OP_END)
+			break;
+	}
+
+	if (operation != CS40L50_PSEQ_OP_END) {
+		dev_err(cs40l50->dev, "PSEQ_END_OF_SCRIPT not found\n");
+		error = -ENOENT;
+	}
+
+err_free:
+	kfree(words);
+
+	return error;
+}
+
+static int cs40l50_input_init(struct cs40l50_private *cs40l50)
+{
+	int error;
+	u32 val;
+
+	error = cs40l50_dsp_read(cs40l50, CS40L50_DEVID, &val);
+	if (error)
+		return error;
+
+	if (val != CS40L50_DEVID_A) {
+		dev_err(cs40l50->dev, "Invalid device ID: %#010X\n", val);
+		return -EINVAL;
+	}
+
+	cs40l50->input->id.product = val & 0xFFFF;
+
+	error = cs40l50_dsp_read(cs40l50, CS40L50_REVID, &val);
+	if (error)
+		return error;
+
+	if (val != CS40L50_REVID_B0) {
+		dev_err(cs40l50->dev, "Invalid device revision: %#04X\n", val);
+		return -EINVAL;
+	}
+
+	cs40l50->input->id.version = val;
+
+	cs40l50->input = devm_input_allocate_device(cs40l50->dev);
+	if (IS_ERR_OR_NULL(cs40l50->input))
+		return cs40l50->input ? PTR_ERR(cs40l50->input) : -ENOMEM;
+
+	cs40l50->input->name = "cs40l50_input";
+	input_set_drvdata(cs40l50->input, cs40l50);
+	input_set_capability(cs40l50->input, EV_FF, FF_PERIODIC);
+	input_set_capability(cs40l50->input, EV_FF, FF_CUSTOM);
+
+	error = input_ff_create(cs40l50->input, FF_MAX_EFFECTS);
+	if (error)
+		return error;
+
+	clear_bit(FF_RUMBLE, cs40l50->input->ffbit);
+
+	cs40l50->input->ff->upload = cs40l50_add_effect;
+	cs40l50->input->ff->playback = cs40l50_playback_effect;
+	cs40l50->input->ff->erase = cs40l50_erase_effect;
+
+	INIT_LIST_HEAD(&cs40l50->effect_head);
+
+	return input_register_device(cs40l50->input);
+}
+
+static const struct cs_dsp_client_ops cs40l50_cs_dsp_client_ops;
+
+static const struct cs_dsp_region cs40l50_dsp_regions[] = {
+	{ .type = WMFW_HALO_PM_PACKED,		.base = CS40L50_DSP1_PMEM_0 },
+	{ .type = WMFW_HALO_XM_PACKED,		.base = CS40L50_DSP1_XMEM_PACKED_0 },
+	{ .type = WMFW_HALO_YM_PACKED,		.base = CS40L50_DSP1_YMEM_PACKED_0 },
+	{ .type = WMFW_ADSP2_XM,		.base = CS40L50_DSP1_XMEM_UNPACKED24_0 },
+	{ .type = WMFW_ADSP2_YM,		.base = CS40L50_DSP1_YMEM_UNPACKED24_0 },
+};
+
+static int cs40l50_cs_dsp_init(struct cs40l50_private *cs40l50)
+{
+	cs40l50->dsp.num = 1;
+	cs40l50->dsp.type = WMFW_HALO;
+	cs40l50->dsp.dev = cs40l50->dev;
+	cs40l50->dsp.regmap = cs40l50->regmap;
+	cs40l50->dsp.base = CS40L50_DSP1_CORE_BASE;
+	cs40l50->dsp.base_sysinfo = CS40L50_DSP1_SYS_INFO_ID;
+	cs40l50->dsp.mem = cs40l50_dsp_regions;
+	cs40l50->dsp.num_mems = ARRAY_SIZE(cs40l50_dsp_regions);
+	cs40l50->dsp.lock_regions = 0xFFFFFFFF;
+	cs40l50->dsp.no_core_startstop = true;
+	cs40l50->dsp.client_ops = &cs40l50_cs_dsp_client_ops;
+
+	return cs_dsp_halo_init(&cs40l50->dsp);
+}
+
+int cs40l50_probe(struct cs40l50_private *cs40l50)
+{
+	int error, i, irq;
+	u32 val;
+
+	mutex_init(&cs40l50->lock);
+
+	error = devm_regulator_bulk_get(cs40l50->dev, ARRAY_SIZE(cs40l50_supplies),
+			cs40l50_supplies);
+	if (error) {
+		dev_err(cs40l50->dev, "Failed to request supplies\n");
+		goto err;
+	}
+
+	error = regulator_bulk_enable(ARRAY_SIZE(cs40l50_supplies), cs40l50_supplies);
+	if (error) {
+		dev_err(cs40l50->dev, "Failed to enable supplies\n");
+		goto err;
+	}
+
+	cs40l50->reset_gpio = devm_gpiod_get_optional(cs40l50->dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(cs40l50->reset_gpio)) {
+		error = PTR_ERR(cs40l50->reset_gpio);
+		goto err;
+	}
+
+	usleep_range(CS40L50_MIN_RESET_PULSE_WIDTH_US, CS40L50_MIN_RESET_PULSE_WIDTH_US + 100);
+
+	gpiod_set_value_cansleep(cs40l50->reset_gpio, 0);
+
+	usleep_range(CS40L50_CP_READY_DELAY_US, CS40L50_CP_READY_DELAY_US + 1000);
+
+	for (i = 0; i < CS40L50_DSP_TIMEOUT_COUNT; i++) {
+		error = cs40l50_dsp_read(cs40l50, CS40L50_DSP1_HALO_STATE, &val);
+		if (!error && val < 0xFFFF && val >= CS40L50_HALO_STATE_BOOT_DONE)
+			break;
+
+		usleep_range(CS40L50_DSP_POLL_US, CS40L50_DSP_POLL_US + 100);
+	}
+
+	if (i == CS40L50_DSP_TIMEOUT_COUNT) {
+		dev_err(cs40l50->dev, "Firmware boot failed: %d, halo state = %#x\n", error, val);
+		goto err;
+	}
+
+	cs40l50->vibe_workqueue = alloc_ordered_workqueue("cs40l50_workqueue", WQ_HIGHPRI);
+	if (!cs40l50->vibe_workqueue) {
+		error = -ENOMEM;
+		goto err;
+	}
+
+	INIT_WORK(&cs40l50->vibe_start_work, cs40l50_vibe_start_worker);
+	INIT_WORK(&cs40l50->vibe_stop_work, cs40l50_vibe_stop_worker);
+
+	error = cs40l50_cs_dsp_init(cs40l50);
+	if (error)
+		goto err;
+
+	error = cs40l50_input_init(cs40l50);
+	if (error)
+		goto err;
+
+	error = cs40l50_patch_firmware(cs40l50);
+	if (error)
+		goto err;
+
+	error = cs40l50_pseq_init(cs40l50);
+	if (error)
+		goto err;
+
+	error = cs40l50_config_bst(cs40l50);
+	if (error)
+		goto err;
+
+	error = devm_regmap_add_irq_chip(cs40l50->dev, cs40l50->regmap, cs40l50->irq,
+			IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, 0, &cs40l50_regmap_irq_chip,
+			&cs40l50->irq_data);
+	if (error) {
+		dev_err(cs40l50->dev, "Failed to register IRQ chip: %d\n", error);
+		goto err;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(cs40l50_irqs); i++) {
+		irq = regmap_irq_get_virq(cs40l50->irq_data, cs40l50_irqs[i].irq);
+		if (irq < 0) {
+			dev_err(cs40l50->dev, "Failed to get %s\n", cs40l50_irqs[i].name);
+			error = irq;
+			goto err;
+		}
+
+		error = devm_request_threaded_irq(cs40l50->dev, irq, NULL, cs40l50_irqs[i].handler,
+				IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW,
+						cs40l50_irqs[i].name, cs40l50);
+		if (error) {
+			dev_err(cs40l50->dev, "Failed to request IRQ %s: %d\n",
+					cs40l50_irqs[i].name, error);
+			goto err;
+		}
+	}
+
+	return 0;
+
+err:
+	cs40l50_remove(cs40l50);
+
+	return error;
+}
+EXPORT_SYMBOL_GPL(cs40l50_probe);
+
+int cs40l50_remove(struct cs40l50_private *cs40l50)
+{
+	disable_irq(cs40l50->irq);
+
+	if (cs40l50->dsp.booted)
+		cs_dsp_power_down(&cs40l50->dsp);
+	if (&cs40l50->dsp)
+		cs_dsp_remove(&cs40l50->dsp);
+
+	if (cs40l50->vibe_workqueue) {
+		flush_workqueue(cs40l50->vibe_workqueue);
+		destroy_workqueue(cs40l50->vibe_workqueue);
+	}
+
+	gpiod_set_value_cansleep(cs40l50->reset_gpio, 1);
+	regulator_bulk_disable(ARRAY_SIZE(cs40l50_supplies), cs40l50_supplies);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cs40l50_remove);
+
+MODULE_DESCRIPTION("CS40L50 Advanced Haptic Driver");
+MODULE_AUTHOR("James Ogletree, Cirrus Logic Inc. <james.ogletree@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/input/cs40l50.h b/include/linux/input/cs40l50.h
new file mode 100644
index 000000000000..f2c8a101fa00
--- /dev/null
+++ b/include/linux/input/cs40l50.h
@@ -0,0 +1,321 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * CS40L50 Advanced Haptic Driver with Waveform Memory DSP
+ * and Closed-Loop Algorithms
+ *
+ * Copyright 2023 Cirrus Logic, Inc.
+ *
+ */
+#ifndef __CS40L50_H__
+#define __CS40L50_H__
+
+#include <linux/bitops.h>
+#include <linux/gpio/consumer.h>
+#include <linux/input.h>
+#include <linux/regmap.h>
+#include <linux/interrupt.h>
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/firmware/cirrus/wmfw.h>
+
+/* Boost */
+#define CS40L50_TEST_KEY			0x40
+#define CS40L50_BLOCK_ENABLES			0x2018
+#define CS40L50_ERROR_RELEASE			0x2034
+#define CS40L50_PWRMGT_CTL			0x2900
+#define CS40L50_BST_LPMODE_SEL			0x3810
+#define CS40L50_STATUS				0x4200
+#define CS40L50_MON_ERROR_OVERIDE		0x4220
+#define CS40L50_TEST_CONFIG			0x4240
+#define CS40L50_MON_VALUE_CTRL			0x4404
+#define CS40L50_TEST_VIS_SPARE			0x5C00
+#define CS40L50_TEMP_WARN_CONFIG		0x6024
+#define CS40L50_KEY_SET_1		0x55
+#define CS40L50_KEY_SET_2		0xAA
+#define CS40L50_TEMP_SENSOR_3		0x400
+#define CS40L50_DCM_LOW_POWER		0x1
+#define CS40L50_MON_ENABLE		0x3201
+#define CS40L50_VBST_VDD_AMP		0x1000000
+#define CS40L50_TEMP_ERR_THLD		0x8000007D
+#define CS40L50_TEMP_WARN_THLD		0x8
+#define CS40L50_TEMP_FILT_DEBOUNCE_OFF	0x510002B5
+#define CS40L50_TEMP_WARN_ATK_VOL	0x522303
+
+/* Interrupts */
+#define CS40L50_IRQ1_INT_1			0xE010
+#define CS40L50_IRQ1_INT_2			0xE014
+#define CS40L50_IRQ1_INT_3			0xE018
+#define CS40L50_IRQ1_INT_4			0xE01C
+#define CS40L50_IRQ1_INT_8			0xE02C
+#define CS40L50_IRQ1_INT_9			0xE030
+#define CS40L50_IRQ1_INT_10			0xE034
+#define CS40L50_IRQ1_INT_18			0xE054
+#define CS40L50_IRQ1_INT_19			0xE058
+#define CS40L50_IRQ1_MASK_1			0xE090
+#define CS40L50_IRQ1_MASK_2			0xE094
+#define CS40L50_IRQ1_MASK_3			0xE098
+#define CS40L50_IRQ1_MASK_8			0xE0AC
+#define CS40L50_IRQ1_MASK_9			0xE0B0
+#define CS40L50_IRQ1_MASK_10			0xE0B4
+#define CS40L50_IRQ1_MASK_18			0xE0D4
+#define CS40L50_IRQ1_MASK_19			0xE0D8
+#define CS40L50_IRQ1_STS_1			0xE210
+#define CS40L50_IRQ1_STS_2			0xE214
+#define CS40L50_IRQ1_STS_3			0xE218
+#define CS40L50_IRQ1_STS_8			0xE22C
+#define CS40L50_IRQ1_STS_9			0xE230
+#define CS40L50_IRQ1_STS_10			0xE234
+#define CS40L50_IRQ1_STS_18			0xE254
+#define CS40L50_IRQ1_STS_19			0xE258
+#define CS40L50_AMP_SHORT_MASK		BIT(31)
+#define CS40L50_VIRT2_MBOX_MASK		BIT(21)
+#define CS40L50_TEMP_ERR_MASK		BIT(31)
+#define CS40L50_BST_UVP_MASK		BIT(6)
+#define CS40L50_BST_SHORT_MASK		BIT(7)
+#define CS40L50_BST_ILIMIT_MASK		BIT(8)
+#define CS40L50_UVLO_VDDBATT_MASK	BIT(16)
+#define CS40L50_GLOBAL_ERROR_MASK	BIT(15)
+#define CS40L50_ERROR_RELEASE_MASK	BIT(11)
+#define CS40L50_BOOT_DONE_MASK		BIT(1)
+#define CS40L50_IRQ(_irq, _name, _hand)	\
+	{				\
+		.irq = _irq,		\
+		.name = _name,		\
+		.handler = _hand,	\
+	}
+#define CS40L50_REG_IRQ(_reg, _irq)					\
+	[CS40L50_ ## _irq ## _IRQ] = {					\
+		.reg_offset = (CS40L50_ ## _reg) - CS40L50_IRQ1_INT_1,	\
+		.mask = CS40L50_ ## _irq ## _MASK			\
+	}
+
+/* Mailbox Inbound Commands */
+#define CS40L50_RAM_BANK_INDEX_START		0x1000000
+#define CS40L50_RAM_BANK_INDEX_END		0x100007F
+#define CS40L50_OWT_INDEX_START			0x1400000
+#define CS40L50_OWT_INDEX_END			0x140007F
+#define CS40L50_ROM_BANK_INDEX_START		0x1800000
+#define CS40L50_ROM_BANK_INDEX_END		0x180001A
+#define CS40L50_OTP_BUZZ			0x1800080
+#define CS40L50_RAM_BUZZ_INDEX_START		0x1800081
+#define CS40L50_RAM_BUZZ_INDEX_END		0x1800085
+#define CS40L50_MBOX_CMD_HIBERNATE		0x2000001
+#define CS40L50_MBOX_CMD_WAKEUP			0x2000002
+#define CS40L50_MBOX_CMD_PREVENT_HIBERNATE	0x2000003
+#define CS40L50_MBOX_CMD_ALLOW_HIBERNATION	0x2000004
+#define CS40L50_MBOX_CMD_SHUTDOWN		0x2000005
+#define CS40L50_MBOX_CMD_DURATION_REPORT	0x3000001
+#define CS40L50_MBOX_CMD_START_I2S		0x3000002
+#define CS40L50_MBOX_CMD_STOP_I2S		0x3000003
+#define CS40L50_MBOX_CMD_HAPTICS_LOGGER_RESET	0x3000004
+#define CS40L50_MBOX_CMD_OWT_PUSH		0x3000008
+#define CS40L50_MBOX_CMD_OWT_RESET		0x3000009
+#define CS40L50_MBOX_CMD_DVL_REINIT		0x300000A
+#define CS40L50_MBOX_CMD_STOP_PLAYBACK		0x5000000
+#define CS40L50_MBOX_CMD_F0_EST			0x7000001
+#define CS40L50_MBOX_CMD_REDC_EST		0x7000002
+#define CS40L50_MBOX_CMD_LE_EST			0x7000004
+#define CS40L50_MBOX_CMD_OWT_DELETE		0xD000000
+
+/* Mailbox Outbound Commands */
+#define CS40L50_MBOX_QUEUE_BASE				0x11004
+#define CS40L50_MBOX_QUEUE_END				0x1101C
+#define CS40L50_DSP_VIRTUAL1_MBOX_1			0x11020
+#define CS40L50_DSP1_MBOX_QUEUE_WT			0x28042C8
+#define CS40L50_DSP1_MBOX_QUEUE_RD			0x28042CC
+#define CS40L50_DSP_MBOX_HAPTIC_COMPLETE_MBOX	0x1000000
+#define CS40L50_DSP_MBOX_HAPTIC_COMPLETE_GPIO	0x1000001
+#define CS40L50_DSP_MBOX_HAPTIC_COMPLETE_I2S	0x1000002
+#define CS40L50_DSP_MBOX_HAPTIC_TRIGGER_MBOX	0x1000010
+#define CS40L50_DSP_MBOX_HAPTIC_TRIGGER_GPIO	0x1000011
+#define CS40L50_DSP_MBOX_HAPTIC_TRIGGER_I2S	0x1000012
+#define CS40L50_DSP_MBOX_INIT			0x2000000
+#define CS40L50_DSP_MBOX_AWAKE			0x2000002
+#define CS40L50_DSP_MBOX_F0_EST_START		0x7000011
+#define CS40L50_DSP_MBOX_F0_EST_DONE		0x7000021
+#define CS40L50_DSP_MBOX_REDC_EST_START		0x7000012
+#define CS40L50_DSP_MBOX_REDC_EST_DONE		0x7000022
+#define CS40L50_DSP_MBOX_LE_EST_START		0x7000014
+#define CS40L50_DSP_MBOX_LE_EST_DONE		0x7000024
+#define CS40L50_DSP_MBOX_ACK			0xA000000
+#define CS40L50_DSP_MBOX_PANIC			0xC000000
+#define CS40L50_DSP_MBOX_WATERMARK		0xD000000
+#define CS40L50_DSP_MBOX_ERR_EVENT_UNMAPPED	0xC0004B3
+#define CS40L50_DSP_MBOX_ERR_EVENT_MODIFY	0xC0004B4
+#define CS40L50_DSP_MBOX_ERR_NULLPTR		0xC0006A5
+#define CS40L50_DSP_MBOX_ERR_BRAKING		0xC0006A8
+#define CS40L50_DSP_MBOX_ERR_INVAL_SRC		0xC0006AC
+#define CS40L50_DSP_MBOX_ERR_ENABLE_RANGE	0xC000836
+#define CS40L50_DSP_MBOX_ERR_GPIO_UNMAPPED	0xC000837
+#define CS40L50_DSP_MBOX_ERR_ISR_RANGE		0xC000838
+#define CS40L50_DSP_MBOX_PERMANENT_SHORT	0xC000C1C
+#define CS40L50_DSP_MBOX_RUNTIME_SHORT		0xC000C1D
+
+/* DSP */
+#define CS40L50_DSP1_XMEM_PACKED_0		0x2000000
+#define CS40L50_DSP1_XMEM_UNPACKED32_0		0x2400000
+#define CS40L50_DSP1_SYS_INFO_ID		0x25E0000
+#define CS40L50_DSP1_XMEM_UNPACKED24_0		0x2800000
+#define CS40L50_NUM_OF_WAVES			0x280CB4C
+#define CS40L50_DSP1_CORE_BASE			0x2B80000
+#define CS40L50_DSP1_SCRATCH1			0x2B805C0
+#define CS40L50_DSP1_SCRATCH2			0x2B805C8
+#define CS40L50_DSP1_SCRATCH3			0x2B805D0
+#define CS40L50_DSP1_SCRATCH4			0x2B805D8
+#define CS40L50_CCM_CORE_CONTROL		0x2BC1000
+#define CS40L50_RAM_INIT			0x28021DC
+#define CS40L50_DSP1_YMEM_PACKED_0		0x2C00000
+#define CS40L50_DSP1_YMEM_UNPACKED32_0		0x3000000
+#define CS40L50_DSP1_YMEM_UNPACKED24_0		0x3400000
+#define CS40L50_DSP1_PMEM_0			0x3800000
+#define CS40L50_DSP1_PMEM_5114			0x3804FE8
+#define CS40L50_DSP1_MEM_RDY_HW		0x2
+#define CS40L50_DSP1_RAM_INIT_FLAG	0x1
+#define CS40L50_DSP1_CLOCK_DISABLE	0x80
+#define CS40L50_DSP1_CLOCK_ENABLE	0x281
+#define CS40L50_DSP_POLL_US		1000
+#define CS40L50_DSP_TIMEOUT_COUNT	100
+#define CS40L50_MBOX_POLL_US		5000
+
+/* Calibration */
+#define CS40L50_DSP1_REDC_ESTIMATION		0x2802F7C
+#define CS40L50_DSP1_F0_ESTIMATION		0x2802F84
+#define CS40L50_DSP1_DYNAMIC_F0_ENABLE		0x2802F8C
+#define CS40L50_DSP1_DYNAMIC_F0_THRESHOLD	0x2802F90
+#define CS40L50_DSP1_F0_STORED			0x2805C00
+#define CS40L50_DSP1_REDC_STORED		0x2805C04
+#define CS40L50_DSP1_RE_EST_STATUS		0x3401B40
+#define CS40L50_REDC_EST_DELAY_US	30000
+#define CS40L50_F0_EST_DELAY_US		10000
+
+/* OWT */
+#define CS40L50_DSP1_OWT_SIZE			0x2805C38
+#define CS40L50_DSP1_OWT_NEXT			0x2805C3C
+#define CS40L50_NUM_OF_OWT_WAVES		0x2805C40
+#define CS40L50_DSP1_OWT_BASE			0x2805C34
+#define CS40L50_MAX_PWLE_SECTIONS	126
+#define CS40L50_CUSTOM_DATA_SIZE	2
+#define CS40L50_WT_HEADER_PWLE_SIZE	12
+#define CS40L50_WT_HEADER_DEFAULT_FLAGS	0x0000
+#define CS40L50_WT_HEADER_OFFSET	3
+#define CS40L50_WT_TYPE_PCM		8
+#define CS40L50_WT_TYPE_PWLE		12
+#define CS40L50_PCM_ID			0x0000
+
+/* GPIO */
+#define CS40L50_DSP1_GPIO_BASE			0x2804140
+#define CS40L50_GPIO_MAPPING_INVALID	0
+#define CS40L50_GPI_DISABLE		0x1FF
+#define CS40L50_BTN_NUM_MASK		GENMASK(14, 12)
+#define CS40L50_BTN_EDGE_MASK		BIT(15)
+
+/* State */
+#define CS40L50_DSP1_HALO_STATE				0x28021E0
+#define CS40L50_HALO_STATE_BOOT_DONE		2
+#define CS40L50_MIN_RESET_PULSE_WIDTH_US	1100
+#define CS40L50_CP_READY_DELAY_US		2200
+
+/* Device */
+#define CS40L50_DEVID			0x0
+#define CS40L50_REVID			0x4
+#define CS40L50_DEVID_A		0x40A50
+#define CS40L50_REVID_B0	0xB0
+
+/* Write sequencer */
+#define CS40L50_DSP1_POWER_ON_SEQ				0x280C320
+#define CS40L50_PSEQ_MAX_WORDS				200
+#define CS40L50_PSEQ_OP_MASK				GENMASK(23, 16)
+#define CS40L50_PSEQ_OP_SHIFT				16
+#define CS40L50_PSEQ_OP_WRITE_ADDR8			0x02
+#define CS40L50_PSEQ_OP_WRITE_L16			0x04
+#define CS40L50_PSEQ_OP_WRITE_H16			0x05
+#define CS40L50_PSEQ_OP_WRITE_FULL			0x00
+#define CS40L50_PSEQ_OP_WRITE_FULL_WORDS		3
+#define CS40L50_PSEQ_OP_WRITE_X16_WORDS			2
+#define CS40L50_PSEQ_OP_END				0xFF
+#define CS40L50_PSEQ_OP_END_WORDS			1
+#define CS40L50_PSEQ_WRITE_FULL_LOWER_ADDR_SHIFT	8
+#define CS40L50_PSEQ_WRITE_FULL_UPPER_ADDR_SHIFT	16
+#define CS40L50_PSEQ_WRITE_FULL_LOWER_ADDR_MASK		GENMASK(15, 0)
+#define CS40L50_PSEQ_WRITE_FULL_UPPER_DATA_SHIFT	24
+#define CS40L50_PSEQ_WRITE_FULL_LOWER_DATA_MASK		GENMASK(23, 0)
+
+enum cs40l50_wvfrm_bank {
+	CS40L50_WVFRM_BANK_RAM,
+	CS40L50_WVFRM_BANK_ROM,
+	CS40L50_WVFRM_BANK_OWT,
+	CS40L50_WVFRM_BANK_BUZ,
+	CS40L50_WVFRM_BANK_NUM,
+};
+
+enum cs40l50_irq_list {
+	CS40L50_GLOBAL_ERROR_IRQ,
+	CS40L50_UVLO_VDDBATT_IRQ,
+	CS40L50_BST_ILIMIT_IRQ,
+	CS40L50_BST_SHORT_IRQ,
+	CS40L50_BST_UVP_IRQ,
+	CS40L50_TEMP_ERR_IRQ,
+	CS40L50_VIRT2_MBOX_IRQ,
+	CS40L50_AMP_SHORT_IRQ,
+	CS40L50_NUM_IRQ
+};
+
+union cs40l50_buzzgen {
+	u32 words[3];
+	struct {
+		u32 freq;
+		u32 level;
+		u32 duration;
+	} params;
+};
+
+struct cs40l50_owt_header {
+	u32 wvfrm_type;
+	u32 offset;
+	u32 data_size;
+};
+
+struct cs40l50_irq {
+	int irq;
+	const char *name;
+	irqreturn_t (*handler)(int irq, void *data);
+};
+
+struct cs40l50_effect {
+	int id;
+	u32 trigger_index;
+	enum cs40l50_wvfrm_bank wvfrm_bank;
+	u32 mapping;
+	struct list_head list;
+};
+
+struct cs40l50_pseq_op {
+	u16 offset;
+	u8 operation;
+	u32 words[3];
+	u32 size;
+	struct list_head list;
+};
+
+struct cs40l50_private {
+	struct device *dev;
+	struct regmap *regmap;
+	struct cs_dsp dsp;
+	struct mutex lock;
+	struct gpio_desc *reset_gpio;
+	struct input_dev *input;
+	struct list_head effect_head;
+	struct ff_effect *trigger_effect;
+	struct workqueue_struct *vibe_workqueue;
+	struct work_struct vibe_start_work;
+	struct work_struct vibe_stop_work;
+	struct regmap_irq_chip_data *irq_data;
+	struct list_head pseq_op_head;
+	int irq;
+};
+
+int cs40l50_probe(struct cs40l50_private *cs40l50);
+int cs40l50_remove(struct cs40l50_private *cs40l50);
+
+extern const struct regmap_config cs40l50_regmap;
+
+#endif /* __CS40L50_H__ */
-- 
2.25.1


^ permalink raw reply related

* [PATCH 1/2] dt-bindings: input: cirrus,cs40l50: Support for CS40L50
From: James Ogletree @ 2023-08-08 17:25 UTC (permalink / raw)
  To: dmitry.torokhov
  Cc: James Ogletree, Fred Treven, Ben Bright, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Lee Jones, Wolfram Sang,
	Jeff LaBundy, Peng Fan, Eddie James, Jean Delvare, Arnd Bergmann,
	Markus Schneider-Pargmann, Jacky Bai, ChiYuan Huang, Randy Dunlap,
	patches, linux-input, devicetree, linux-kernel

Introduce device tree bindings for the initial commit
of the CS40L50 driver.

Change-Id: I95369831f947cb32a0a80e81923fd28ace888481
Signed-off-by: James Ogletree <james.ogletree@cirrus.com>
---
 .../bindings/input/cirrus,cs40l50.yaml        | 77 +++++++++++++++++++
 MAINTAINERS                                   |  9 +++
 2 files changed, 86 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/cirrus,cs40l50.yaml

diff --git a/Documentation/devicetree/bindings/input/cirrus,cs40l50.yaml b/Documentation/devicetree/bindings/input/cirrus,cs40l50.yaml
new file mode 100644
index 000000000000..5437c6beb1b3
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/cirrus,cs40l50.yaml
@@ -0,0 +1,77 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/cirrus,cs40l50.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Cirrus Logic CS40L50 Advanced Haptic Driver
+
+maintainers:
+  - James Ogletree <james.ogletree@cirrus.com>
+
+description:
+  CS40L50 is a Haptic Driver with Waveform Memory DSP
+  and Closed-Loop Algorithms
+
+properties:
+  compatible:
+    enum:
+      - cirrus,cs40l50
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  reset-gpios:
+    maxItems: 1
+
+  VA-supply:
+    description: Regulator for analog voltage
+
+  VP-supply:
+    description: Regulator for peak voltage
+
+  VIO-supply:
+    description: Regulator for digital IO voltage
+
+  cirrus,external-boost:
+    description:
+      Bypass the CS40L50 boost converter.
+
+      The amplifier on CS40L50 can be powered internally through the boost
+      converter, or else can be powered using an external supply. If an
+      external VA supply is used, the boost converter must be bypassed.
+    type: boolean
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - reset-gpios
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/input/input.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      cs40l50@34 {
+        compatible = "cirrus,cs40l50";
+        reg = <0x34>;
+        interrupt-parent = <&gpio>;
+        interrupts = <113 IRQ_TYPE_LEVEL_LOW>;
+        reset-gpios = <&gpio 112 GPIO_ACTIVE_LOW>;
+        VA-supply = <&dummy_vreg>;
+        VP-supply = <&dummy_vreg>;
+        VIO-supply = <&dummy_vreg>;
+        cirrus,external-boost;
+      };
+    };
diff --git a/MAINTAINERS b/MAINTAINERS
index 54ef320bb689..016e5ff3b831 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2081,6 +2081,15 @@ F:	arch/arm/boot/compressed/misc-ep93xx.h
 F:	arch/arm/mach-ep93xx/
 F:	drivers/iio/adc/ep93xx_adc.c
 
+CIRRUS LOGIC HAPTICS DRIVER
+M:	James Ogletree <james.ogletree@cirrus.com>
+M:	Fred Treven <fred.treven@cirrus.com>
+M:	Ben Bright <ben.bright@cirrus.com>
+L:	patches@Qopensource.cirrus.com
+S:	Supported
+T:	git https://github.com/CirrusLogic/linux-drivers.git
+F:	Documentation/devicetree/bindings/input/cirrus,cs40l50.yaml
+
 ARM/CLKDEV SUPPORT
 M:	Russell King <linux@armlinux.org.uk>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
-- 
2.25.1


^ permalink raw reply related

* [PATCH] Revert "Input: synaptics - enable InterTouch for the ThinkPad P1 G3"
From: Sebastian Wick @ 2023-08-08 15:28 UTC (permalink / raw)
  To: Dmitry Torokhov, Aman Dhoot, Sebastian Wick, Mark Pearson,
	Raul Rangel, Lyude Paul, Wolfram Sang
  Cc: linux-input, linux-kernel

This reverts commit 7984b43542070f5888546d95b48003c4a8af7c0f to make the
touchpad usable again.

Tapping does not generate any events for user space and moving the
cursor is janky. Disabling InterTouch fixes those issues.

Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
---
 drivers/input/mouse/synaptics.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index ada299ec5bba..0e01df88cf69 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -182,7 +182,6 @@ static const char * const smbus_pnp_ids[] = {
 	"LEN0099", /* X1 Extreme Gen 1 / P1 Gen 1 */
 	"LEN009b", /* T580 */
 	"LEN0402", /* X1 Extreme Gen 2 / P1 Gen 2 */
-	"LEN040f", /* P1 Gen 3 */
 	"LEN200f", /* T450s */
 	"LEN2044", /* L470  */
 	"LEN2054", /* E480 */
-- 
2.41.0


^ permalink raw reply related

* Re: atkbd input regression
From: Bagas Sanjaya @ 2023-08-08  1:41 UTC (permalink / raw)
  To: José Ramón Muñoz Pekkarinen, linux-input,
	Raul Rangel
  Cc: dmitry.torokhov, gregkh, Linux Regressions
In-Reply-To: <CANWZPgK2+9SCavsbSZv1DfJyhy0XUpJZ6+ebatM9ZGJPpAN1yA@mail.gmail.com>

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

On Mon, Aug 07, 2023 at 08:19:00AM +0300, José Ramón Muñoz Pekkarinen wrote:
>     Sorry again, now in plaintext mode.
> 
>     Hi,
> 
>     I'd like to seek help or advise in a possible
> regression that I detected on my system(a
> Thinkpad L14 Gen 1) that concerns the atkbd
> module. My OS is a gentoo, with self built
> kernels, and a plasma desktop environment
> using wayland. Up to the kernel 5.16.x my
> builtin keyboard worked(and works still)
> fine both in tty and the plasma session, but
> from that kernel on, the keyboard works only
> in tty, and no longer in the plasma session.
> 
>     The input config section of my 5.16.17 kernel:
> 
> #
> # Input device support
> #
> CONFIG_INPUT=y
> CONFIG_INPUT_LEDS=m
> # CONFIG_INPUT_FF_MEMLESS is not set
> # CONFIG_INPUT_SPARSEKMAP is not set
> # CONFIG_INPUT_MATRIXKMAP is not set
> 
> #
> # Userland interfaces
> #
> CONFIG_INPUT_MOUSEDEV=m
> # CONFIG_INPUT_MOUSEDEV_PSAUX is not set
> CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
> CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
> CONFIG_INPUT_JOYDEV=m
> CONFIG_INPUT_EVDEV=m
> # CONFIG_INPUT_EVBUG is not set
> 
> #
> # Input Device Drivers
> #
> CONFIG_INPUT_KEYBOARD=y
> # CONFIG_KEYBOARD_ADP5588 is not set
> # CONFIG_KEYBOARD_ADP5589 is not set
> # CONFIG_KEYBOARD_APPLESPI is not set
> CONFIG_KEYBOARD_ATKBD=y
> # CONFIG_KEYBOARD_QT1050 is not set
> # CONFIG_KEYBOARD_QT1070 is not set
> # CONFIG_KEYBOARD_QT2160 is not set
> # CONFIG_KEYBOARD_DLINK_DIR685 is not set
> # CONFIG_KEYBOARD_LKKBD is not set
> # CONFIG_KEYBOARD_GPIO is not set
> # CONFIG_KEYBOARD_GPIO_POLLED is not set
> # CONFIG_KEYBOARD_TCA6416 is not set
> # CONFIG_KEYBOARD_TCA8418 is not set
> # CONFIG_KEYBOARD_MATRIX is not set
> # CONFIG_KEYBOARD_LM8323 is not set
> # CONFIG_KEYBOARD_LM8333 is not set
> # CONFIG_KEYBOARD_MAX7359 is not set
> # CONFIG_KEYBOARD_MCS is not set
> # CONFIG_KEYBOARD_MPR121 is not set
> # CONFIG_KEYBOARD_NEWTON is not set
> # CONFIG_KEYBOARD_OPENCORES is not set
> # CONFIG_KEYBOARD_SAMSUNG is not set
> # CONFIG_KEYBOARD_STOWAWAY is not set
> # CONFIG_KEYBOARD_SUNKBD is not set
> # CONFIG_KEYBOARD_TM2_TOUCHKEY is not set
> # CONFIG_KEYBOARD_XTKBD is not set
> # CONFIG_KEYBOARD_CYPRESS_SF is not set
> 
>     And the same for 6.4.7:
> 
> #
> # Input device support
> #
> CONFIG_INPUT=y
> CONFIG_INPUT_LEDS=m
> # CONFIG_INPUT_FF_MEMLESS is not set
> # CONFIG_INPUT_SPARSEKMAP is not set
> # CONFIG_INPUT_MATRIXKMAP is not set
> CONFIG_INPUT_VIVALDIFMAP=y
> 
> #
> # Userland interfaces
> #
> CONFIG_INPUT_MOUSEDEV=m
> # CONFIG_INPUT_MOUSEDEV_PSAUX is not set
> CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
> CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
> CONFIG_INPUT_JOYDEV=m
> CONFIG_INPUT_EVDEV=m
> # CONFIG_INPUT_EVBUG is not set
> 
> #
> # Input Device Drivers
> #
> CONFIG_INPUT_KEYBOARD=y
> # CONFIG_KEYBOARD_ADP5588 is not set
> # CONFIG_KEYBOARD_ADP5589 is not set
> # CONFIG_KEYBOARD_APPLESPI is not set
> CONFIG_KEYBOARD_ATKBD=y
> # CONFIG_KEYBOARD_QT1050 is not set
> # CONFIG_KEYBOARD_QT1070 is not set
> # CONFIG_KEYBOARD_QT2160 is not set
> # CONFIG_KEYBOARD_DLINK_DIR685 is not set
> # CONFIG_KEYBOARD_LKKBD is not set
> # CONFIG_KEYBOARD_GPIO is not set
> # CONFIG_KEYBOARD_GPIO_POLLED is not set
> # CONFIG_KEYBOARD_TCA6416 is not set
> # CONFIG_KEYBOARD_TCA8418 is not set
> # CONFIG_KEYBOARD_MATRIX is not set
> # CONFIG_KEYBOARD_LM8323 is not set
> # CONFIG_KEYBOARD_LM8333 is not set
> # CONFIG_KEYBOARD_MAX7359 is not set
> # CONFIG_KEYBOARD_MCS is not set
> # CONFIG_KEYBOARD_MPR121 is not set
> # CONFIG_KEYBOARD_NEWTON is not set
> # CONFIG_KEYBOARD_OPENCORES is not set
> # CONFIG_KEYBOARD_PINEPHONE is not set
> # CONFIG_KEYBOARD_SAMSUNG is not set
> # CONFIG_KEYBOARD_STOWAWAY is not set
> # CONFIG_KEYBOARD_SUNKBD is not set
> # CONFIG_KEYBOARD_TM2_TOUCHKEY is not set
> # CONFIG_KEYBOARD_XTKBD is not set
> # CONFIG_KEYBOARD_CYPRESS_SF is not set
> 
>     If I run evtest, the keyboard is detected, and I can
> retrieve its events. From my kernel 6.4, this is an
> example of it's output.
> 
> # evtest
> No device specified, trying to scan all of /dev/input/event*
> Available devices:
> /dev/input/event0:      AT Translated Set 2 keyboard
> /dev/input/event1:      Power Button
> /dev/input/event10:     SEM HCT Keyboard Consumer Control
> /dev/input/event11:     SEM HCT Keyboard System Control
> /dev/input/event12:     Logitech USB Optical Mouse
> /dev/input/event2:      Lid Switch
> /dev/input/event3:      Sleep Button
> /dev/input/event4:      Power Button
> /dev/input/event5:      Video Bus
> /dev/input/event6:      ThinkPad Extra Buttons
> /dev/input/event7:      SynPS/2 Synaptics TouchPad
> /dev/input/event8:      TPPS/2 Elan TrackPoint
> /dev/input/event9:      SEM HCT Keyboard
> Select the device event number [0-12]: 0
> Input driver version is 1.0.1
> Input device ID: bus 0x11 vendor 0x1 product 0x1 version 0xab54
> Input device name: "AT Translated Set 2 keyboard"
> Supported events:
>  Event type 0 (EV_SYN)
>  Event type 1 (EV_KEY)
>    Event code 1 (KEY_ESC)
>    Event code 2 (KEY_1)
>    Event code 3 (KEY_2)
>    Event code 4 (KEY_3)
>    Event code 5 (KEY_4)
>    Event code 6 (KEY_5)
>    Event code 7 (KEY_6)
>    Event code 8 (KEY_7)
>    Event code 9 (KEY_8)
>    Event code 10 (KEY_9)
>    Event code 11 (KEY_0)
>    Event code 12 (KEY_MINUS)
>    Event code 13 (KEY_EQUAL)
>    Event code 14 (KEY_BACKSPACE)
>    Event code 15 (KEY_TAB)
>    Event code 16 (KEY_Q)
>    Event code 17 (KEY_W)
>    Event code 18 (KEY_E)
>    Event code 19 (KEY_R)
>    Event code 20 (KEY_T)
>    Event code 21 (KEY_Y)
>    Event code 22 (KEY_U)
>    Event code 23 (KEY_I)
>    Event code 24 (KEY_O)
>    Event code 25 (KEY_P)
>    Event code 26 (KEY_LEFTBRACE)
>    Event code 27 (KEY_RIGHTBRACE)
>    Event code 28 (KEY_ENTER)
>    Event code 29 (KEY_LEFTCTRL)
>    Event code 30 (KEY_A)
>    Event code 31 (KEY_S)
>    Event code 32 (KEY_D)
>    Event code 33 (KEY_F)
>    Event code 34 (KEY_G)
>    Event code 35 (KEY_H)
>    Event code 36 (KEY_J)
>    Event code 37 (KEY_K)
>    Event code 38 (KEY_L)
>    Event code 39 (KEY_SEMICOLON)
>    Event code 40 (KEY_APOSTROPHE)
>    Event code 41 (KEY_GRAVE)
>    Event code 42 (KEY_LEFTSHIFT)
>    Event code 43 (KEY_BACKSLASH)
>    Event code 44 (KEY_Z)
>    Event code 45 (KEY_X)
>    Event code 46 (KEY_C)
>    Event code 47 (KEY_V)
>    Event code 48 (KEY_B)
>    Event code 49 (KEY_N)
>    Event code 50 (KEY_M)
>    Event code 51 (KEY_COMMA)
>    Event code 52 (KEY_DOT)
>    Event code 53 (KEY_SLASH)
>    Event code 54 (KEY_RIGHTSHIFT)
>    Event code 55 (KEY_KPASTERISK)
>    Event code 56 (KEY_LEFTALT)
>    Event code 57 (KEY_SPACE)
>    Event code 58 (KEY_CAPSLOCK)
>    Event code 59 (KEY_F1)
>    Event code 60 (KEY_F2)
>    Event code 61 (KEY_F3)
>    Event code 62 (KEY_F4)
>    Event code 63 (KEY_F5)
>    Event code 64 (KEY_F6)
>    Event code 65 (KEY_F7)
>    Event code 66 (KEY_F8)
>    Event code 67 (KEY_F9)
>    Event code 68 (KEY_F10)
>    Event code 69 (KEY_NUMLOCK)
>    Event code 70 (KEY_SCROLLLOCK)
>    Event code 71 (KEY_KP7)
>    Event code 72 (KEY_KP8)
>    Event code 73 (KEY_KP9)
>    Event code 74 (KEY_KPMINUS)
>    Event code 75 (KEY_KP4)
>    Event code 76 (KEY_KP5)
>    Event code 77 (KEY_KP6)
>    Event code 78 (KEY_KPPLUS)
>    Event code 79 (KEY_KP1)
>    Event code 80 (KEY_KP2)
>    Event code 81 (KEY_KP3)
>    Event code 82 (KEY_KP0)
>    Event code 83 (KEY_KPDOT)
>    Event code 85 (KEY_ZENKAKUHANKAKU)
>    Event code 86 (KEY_102ND)
>    Event code 87 (KEY_F11)
>    Event code 88 (KEY_F12)
>    Event code 89 (KEY_RO)
>    Event code 90 (KEY_KATAKANA)
>    Event code 91 (KEY_HIRAGANA)
>    Event code 92 (KEY_HENKAN)
>    Event code 93 (KEY_KATAKANAHIRAGANA)
>    Event code 94 (KEY_MUHENKAN)
>    Event code 95 (KEY_KPJPCOMMA)
>    Event code 96 (KEY_KPENTER)
>    Event code 97 (KEY_RIGHTCTRL)
>    Event code 98 (KEY_KPSLASH)
>    Event code 99 (KEY_SYSRQ)
>    Event code 100 (KEY_RIGHTALT)
>    Event code 102 (KEY_HOME)
>    Event code 103 (KEY_UP)
>    Event code 104 (KEY_PAGEUP)
>    Event code 105 (KEY_LEFT)
>    Event code 106 (KEY_RIGHT)
>    Event code 107 (KEY_END)
>    Event code 108 (KEY_DOWN)
>    Event code 109 (KEY_PAGEDOWN)
>    Event code 110 (KEY_INSERT)
>    Event code 111 (KEY_DELETE)
>    Event code 112 (KEY_MACRO)
>    Event code 113 (KEY_MUTE)
>    Event code 114 (KEY_VOLUMEDOWN)
>    Event code 115 (KEY_VOLUMEUP)
>    Event code 116 (KEY_POWER)
>    Event code 117 (KEY_KPEQUAL)
>    Event code 118 (KEY_KPPLUSMINUS)
>    Event code 119 (KEY_PAUSE)
>    Event code 121 (KEY_KPCOMMA)
>    Event code 122 (KEY_HANGUEL)
>    Event code 123 (KEY_HANJA)
>    Event code 124 (KEY_YEN)
>    Event code 125 (KEY_LEFTMETA)
>    Event code 126 (KEY_RIGHTMETA)
>    Event code 127 (KEY_COMPOSE)
>    Event code 128 (KEY_STOP)
>    Event code 140 (KEY_CALC)
>    Event code 142 (KEY_SLEEP)
>    Event code 143 (KEY_WAKEUP)
>    Event code 155 (KEY_MAIL)
>    Event code 156 (KEY_BOOKMARKS)
>    Event code 157 (KEY_COMPUTER)
>    Event code 158 (KEY_BACK)
>    Event code 159 (KEY_FORWARD)
>    Event code 163 (KEY_NEXTSONG)
>    Event code 164 (KEY_PLAYPAUSE)
>    Event code 165 (KEY_PREVIOUSSONG)
>    Event code 166 (KEY_STOPCD)
>    Event code 172 (KEY_HOMEPAGE)
>    Event code 173 (KEY_REFRESH)
>    Event code 183 (KEY_F13)
>    Event code 184 (KEY_F14)
>    Event code 185 (KEY_F15)
>    Event code 217 (KEY_SEARCH)
>    Event code 226 (KEY_MEDIA)
>  Event type 4 (EV_MSC)
>    Event code 4 (MSC_SCAN)
>  Event type 17 (EV_LED)
>    Event code 0 (LED_NUML) state 0
>    Event code 1 (LED_CAPSL) state 0
>    Event code 2 (LED_SCROLLL) state 0
> Key repeat handling:
>  Repeat type 20 (EV_REP)
>    Repeat code 0 (REP_DELAY)
>      Value    250
>    Repeat code 1 (REP_PERIOD)
>      Value     33
> Properties:
> Testing ... (interrupt to exit)
> Event: time 1691384520.078772, type 4 (EV_MSC), code 4 (MSC_SCAN), value 10
> Event: time 1691384520.078772, type 1 (EV_KEY), code 16 (KEY_Q), value 1
> Event: time 1691384520.078772, -------------- SYN_REPORT ------------
> Event: time 1691384520.157298, type 4 (EV_MSC), code 4 (MSC_SCAN), value 10
> Event: time 1691384520.157298, type 1 (EV_KEY), code 16 (KEY_Q), value 0
> Event: time 1691384520.157298, -------------- SYN_REPORT ------------
> Event: time 1691384520.849991, type 4 (EV_MSC), code 4 (MSC_SCAN), value 11
> Event: time 1691384520.849991, type 1 (EV_KEY), code 17 (KEY_W), value 1
> Event: time 1691384520.849991, -------------- SYN_REPORT ------------
> Event: time 1691384520.919630, type 4 (EV_MSC), code 4 (MSC_SCAN), value 11
> Event: time 1691384520.919630, type 1 (EV_KEY), code 17 (KEY_W), value 0
> Event: time 1691384520.919630, -------------- SYN_REPORT ------------
> Event: time 1691384521.311986, type 4 (EV_MSC), code 4 (MSC_SCAN), value 12
> Event: time 1691384521.311986, type 1 (EV_KEY), code 18 (KEY_E), value 1
> Event: time 1691384521.311986, -------------- SYN_REPORT ------------
> Event: time 1691384521.417578, type 4 (EV_MSC), code 4 (MSC_SCAN), value 12
> Event: time 1691384521.417578, type 1 (EV_KEY), code 18 (KEY_E), value 0
> Event: time 1691384521.417578, -------------- SYN_REPORT ------------
> Event: time 1691384521.639505, type 4 (EV_MSC), code 4 (MSC_SCAN), value 13
> Event: time 1691384521.639505, type 1 (EV_KEY), code 19 (KEY_R), value 1
> Event: time 1691384521.639505, -------------- SYN_REPORT ------------
> Event: time 1691384521.709170, type 4 (EV_MSC), code 4 (MSC_SCAN), value 13
> Event: time 1691384521.709170, type 1 (EV_KEY), code 19 (KEY_R), value 0
> Event: time 1691384521.709170, -------------- SYN_REPORT ------------
> Event: time 1691384521.879657, type 4 (EV_MSC), code 4 (MSC_SCAN), value 14
> Event: time 1691384521.879657, type 1 (EV_KEY), code 20 (KEY_T), value 1
> Event: time 1691384521.879657, -------------- SYN_REPORT ------------
> Event: time 1691384521.958384, type 4 (EV_MSC), code 4 (MSC_SCAN), value 14
> Event: time 1691384521.958384, type 1 (EV_KEY), code 20 (KEY_T), value 0
> Event: time 1691384521.958384, -------------- SYN_REPORT ------------
> Event: time 1691384522.188556, type 4 (EV_MSC), code 4 (MSC_SCAN), value 15
> Event: time 1691384522.188556, type 1 (EV_KEY), code 21 (KEY_Y), value 1
> 
>     I have reported this time ago in libinput, and they
> replied it might be a kernel problem, since booting
> my kernel 5.16 makes it work correctly again anytime.
> 
>    Could anyone please shed some light on this little
> problem?

You also missed dmesg output (from both v5.16.y and v6.4.y).

Thorsten also asked you to bisect. Since you're on Gentoo, there is
a wiki article on this [1]. To narrow the bisection range, let me ask this
question: Does v6.1 kernel have this regression? What about v5.17? In any case,
the kernel documentation also have howto on building custom kernel with trimmed
config [2].

Anyway, I'm adding this regression to be tracked by regzbot:

#regzbot ^introduced: v5.16..v6.4
#regzbot title: Thinkpad L14 Gen 1 keyboard not working on Plasma session

Thanks.

[1]: https://wiki.gentoo.org/wiki/Kernel_git-bisect
[2]: https://www.kernel.org/doc/html/latest/admin-guide/quickly-build-trimmed-linux.html

-- 
An old man doll... just what I always wanted! - Clara

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

^ permalink raw reply

* Re: [PATCH] drivers/input/touchscreen/goodix.c: Add support for ACPI ID GDX9110
From: Hans de Goede @ 2023-08-07 17:08 UTC (permalink / raw)
  To: Felix Engelhardt
  Cc: Bastien Nocera, Dmitry Torokhov, linux-input, linux-kernel
In-Reply-To: <20230807124723.382899-1-felix.engelhardt@eidu.com>

Hi,

On 8/7/23 14:47, Felix Engelhardt wrote:
> The Goodix touchscreen controller with ACPI ID GDX9110 was not recognized
> by the goodix driver. This patch adds this ID to the list of supported IDs,
> allowing the driver to be used with this device. The change will allow
> Linux to be used on ~1 million tablet devices used in Kenyan primary
> schools.
> 
> Signed-off-by: Felix Engelhardt <felix.engelhardt@eidu.com>
> ---
>  drivers/input/touchscreen/goodix.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
> index 4f53d3c57e69..9cdc01eb00c9 100644
> --- a/drivers/input/touchscreen/goodix.c
> +++ b/drivers/input/touchscreen/goodix.c
> @@ -1379,6 +1379,7 @@ MODULE_DEVICE_TABLE(i2c, goodix_ts_id);
>  static const struct acpi_device_id goodix_acpi_match[] = {
>  	{ "GDIX1001", 0 },
>  	{ "GDIX1002", 0 },
> +	{ "GDX9110", 0 },
>  	{ }
>  };
>  MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);

Hmm, GDX without the 'I' does look like an official ACPI vendor prefix. But if this is used in the wild, then I guess we'll just need to live with it:

Reviewed-by: Hans de Goede <hdegoede@redhat.com>

Regards,

Hans




^ permalink raw reply

* [PATCH 3/3] HID: nvidia-shield: Update Thunderstrike LED instance name to use id
From: Rahul Rameshbabu @ 2023-08-07 16:36 UTC (permalink / raw)
  To: Jiri Kosina, Benjamin Tissoires
  Cc: linux-input, linux-kernel, Rahul Rameshbabu
In-Reply-To: <20230807163620.16855-1-rrameshbabu@nvidia.com>

Previously would let led_classdev handle renaming when name collision
occurred. Now that an ID allocator is used to uniquely identify multiple
Thunderstrike controllers, generate unique led device names.

Signed-off-by: Rahul Rameshbabu <rrameshbabu@nvidia.com>
---
 drivers/hid/hid-nvidia-shield.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/hid-nvidia-shield.c b/drivers/hid/hid-nvidia-shield.c
index 1612de3ef4c5..43784bb57d3f 100644
--- a/drivers/hid/hid-nvidia-shield.c
+++ b/drivers/hid/hid-nvidia-shield.c
@@ -798,7 +798,8 @@ static inline int thunderstrike_led_create(struct thunderstrike *ts)
 {
 	struct led_classdev *led = &ts->led_dev;
 
-	led->name = "thunderstrike:blue:led";
+	led->name = devm_kasprintf(&ts->base.hdev->dev, GFP_KERNEL,
+				   "thunderstrike%d:blue:led", ts->id);
 	led->max_brightness = 1;
 	led->flags = LED_CORE_SUSPENDRESUME;
 	led->brightness_get = &thunderstrike_led_get_brightness;
-- 
2.40.1


^ permalink raw reply related

* [PATCH 2/3] HID: nvidia-shield: Add battery support for Thunderstrike
From: Rahul Rameshbabu @ 2023-08-07 16:36 UTC (permalink / raw)
  To: Jiri Kosina, Benjamin Tissoires
  Cc: linux-input, linux-kernel, Rahul Rameshbabu
In-Reply-To: <20230807163620.16855-1-rrameshbabu@nvidia.com>

Use power supply API to expose battery information about connected
Thunderstrike controllers to the system. Provide information on battery
capacity, charge status, charger type, voltage, and temperature.

Signed-off-by: Rahul Rameshbabu <rrameshbabu@nvidia.com>
---
 drivers/hid/hid-nvidia-shield.c | 418 ++++++++++++++++++++++++++++++--
 1 file changed, 401 insertions(+), 17 deletions(-)

diff --git a/drivers/hid/hid-nvidia-shield.c b/drivers/hid/hid-nvidia-shield.c
index 4e183650c447..1612de3ef4c5 100644
--- a/drivers/hid/hid-nvidia-shield.c
+++ b/drivers/hid/hid-nvidia-shield.c
@@ -6,11 +6,15 @@
  */
 
 #include <linux/hid.h>
+#include <linux/idr.h>
 #include <linux/input-event-codes.h>
 #include <linux/input.h>
+#include <linux/jiffies.h>
 #include <linux/leds.h>
 #include <linux/module.h>
+#include <linux/power_supply.h>
 #include <linux/spinlock.h>
+#include <linux/timer.h>
 #include <linux/workqueue.h>
 
 #include "hid-ids.h"
@@ -30,6 +34,8 @@ enum {
 enum {
 	SHIELD_FW_VERSION_INITIALIZED = 0,
 	SHIELD_BOARD_INFO_INITIALIZED,
+	SHIELD_BATTERY_STATS_INITIALIZED,
+	SHIELD_CHARGER_STATE_INITIALIZED,
 };
 
 enum {
@@ -37,6 +43,7 @@ enum {
 	THUNDERSTRIKE_BOARD_INFO_UPDATE,
 	THUNDERSTRIKE_HAPTICS_UPDATE,
 	THUNDERSTRIKE_LED_UPDATE,
+	THUNDERSTRIKE_POWER_SUPPLY_STATS_UPDATE,
 };
 
 enum {
@@ -48,10 +55,46 @@ enum {
 enum {
 	THUNDERSTRIKE_HOSTCMD_ID_FW_VERSION = 1,
 	THUNDERSTRIKE_HOSTCMD_ID_LED = 6,
+	THUNDERSTRIKE_HOSTCMD_ID_BATTERY,
 	THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO = 16,
 	THUNDERSTRIKE_HOSTCMD_ID_USB_INIT = 53,
 	THUNDERSTRIKE_HOSTCMD_ID_HAPTICS = 57,
-	THUNDERSTRIKE_HOSTCMD_ID_BLUETOOTH_INIT = 58,
+	THUNDERSTRIKE_HOSTCMD_ID_CHARGER,
+};
+
+struct power_supply_dev {
+	struct power_supply *psy;
+	struct power_supply_desc desc;
+};
+
+struct thunderstrike_psy_prop_values {
+	int voltage_min;
+	int voltage_now;
+	int voltage_avg;
+	int voltage_boot;
+	int capacity;
+	int status;
+	int charge_type;
+	int temp;
+};
+
+static const enum power_supply_property thunderstrike_battery_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_VOLTAGE_AVG,
+	POWER_SUPPLY_PROP_VOLTAGE_BOOT,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_SCOPE,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_TEMP_MIN,
+	POWER_SUPPLY_PROP_TEMP_MAX,
+	POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
+	POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
 };
 
 enum thunderstrike_led_state {
@@ -60,6 +103,38 @@ enum thunderstrike_led_state {
 } __packed;
 static_assert(sizeof(enum thunderstrike_led_state) == 1);
 
+struct thunderstrike_hostcmd_battery {
+	__le16 voltage_avg;
+	u8 reserved_at_10;
+	__le16 thermistor;
+	__le16 voltage_min;
+	__le16 voltage_boot;
+	__le16 voltage_now;
+	u8 capacity;
+} __packed;
+
+enum thunderstrike_charger_type {
+	THUNDERSTRIKE_CHARGER_TYPE_NONE = 0,
+	THUNDERSTRIKE_CHARGER_TYPE_TRICKLE,
+	THUNDERSTRIKE_CHARGER_TYPE_NORMAL,
+} __packed;
+static_assert(sizeof(enum thunderstrike_charger_type) == 1);
+
+enum thunderstrike_charger_state {
+	THUNDERSTRIKE_CHARGER_STATE_UNKNOWN = 0,
+	THUNDERSTRIKE_CHARGER_STATE_DISABLED,
+	THUNDERSTRIKE_CHARGER_STATE_CHARGING,
+	THUNDERSTRIKE_CHARGER_STATE_FULL,
+	THUNDERSTRIKE_CHARGER_STATE_FAILED = 8,
+} __packed;
+static_assert(sizeof(enum thunderstrike_charger_state) == 1);
+
+struct thunderstrike_hostcmd_charger {
+	u8 connected;
+	enum thunderstrike_charger_type type;
+	enum thunderstrike_charger_state state;
+} __packed;
+
 struct thunderstrike_hostcmd_board_info {
 	__le16 revision;
 	__le16 serial[7];
@@ -80,6 +155,8 @@ struct thunderstrike_hostcmd_resp_report {
 		struct thunderstrike_hostcmd_haptics motors;
 		__le16 fw_version;
 		enum thunderstrike_led_state led_state;
+		struct thunderstrike_hostcmd_battery battery;
+		struct thunderstrike_hostcmd_charger charger;
 		u8 payload[30];
 	} __packed;
 } __packed;
@@ -109,6 +186,7 @@ static_assert(sizeof(struct thunderstrike_hostcmd_req_report) ==
 /* Common struct for shield accessories. */
 struct shield_device {
 	struct hid_device *hdev;
+	struct power_supply_dev battery_dev;
 
 	unsigned long initialized_flags;
 	const char *codename;
@@ -119,9 +197,17 @@ struct shield_device {
 	} board_info;
 };
 
+/*
+ * Non-trivial to uniquely identify Thunderstrike controllers at initialization
+ * time. Use an ID allocator to help with this.
+ */
+static DEFINE_IDA(thunderstrike_ida);
+
 struct thunderstrike {
 	struct shield_device base;
 
+	int id;
+
 	/* Sub-devices */
 	struct input_dev *haptics_dev;
 	struct led_classdev led_dev;
@@ -133,6 +219,9 @@ struct thunderstrike {
 	spinlock_t haptics_update_lock;
 	u8 led_state : 1;
 	enum thunderstrike_led_state led_value;
+	struct thunderstrike_psy_prop_values psy_stats;
+	spinlock_t psy_stats_lock;
+	struct timer_list psy_stats_timer;
 	struct work_struct hostcmd_req_work;
 };
 
@@ -247,6 +336,16 @@ static void thunderstrike_hostcmd_req_work_handler(struct work_struct *work)
 		thunderstrike_send_hostcmd_request(ts);
 	}
 
+	if (test_and_clear_bit(THUNDERSTRIKE_POWER_SUPPLY_STATS_UPDATE, &ts->update_flags)) {
+		thunderstrike_hostcmd_req_report_init(
+			report, THUNDERSTRIKE_HOSTCMD_ID_BATTERY);
+		thunderstrike_send_hostcmd_request(ts);
+
+		thunderstrike_hostcmd_req_report_init(
+			report, THUNDERSTRIKE_HOSTCMD_ID_CHARGER);
+		thunderstrike_send_hostcmd_request(ts);
+	}
+
 	if (test_and_clear_bit(THUNDERSTRIKE_BOARD_INFO_UPDATE, &ts->update_flags)) {
 		thunderstrike_hostcmd_req_report_init(
 			report, THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO);
@@ -352,6 +451,93 @@ static void thunderstrike_led_set_brightness(struct led_classdev *led,
 	schedule_work(&ts->hostcmd_req_work);
 }
 
+static int thunderstrike_battery_get_property(struct power_supply *psy,
+					      enum power_supply_property psp,
+					      union power_supply_propval *val)
+{
+	struct shield_device *shield_dev = power_supply_get_drvdata(psy);
+	struct thunderstrike_psy_prop_values prop_values;
+	struct thunderstrike *ts;
+	int ret = 0;
+
+	ts = container_of(shield_dev, struct thunderstrike, base);
+	spin_lock(&ts->psy_stats_lock);
+	prop_values = ts->psy_stats;
+	spin_unlock(&ts->psy_stats_lock);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = prop_values.status;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		val->intval = prop_values.charge_type;
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = 1;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+		val->intval = prop_values.voltage_min;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+		val->intval = 2900000; /* 2.9 V */
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+		val->intval = 2200000; /* 2.2 V */
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = prop_values.voltage_now;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+		val->intval = prop_values.voltage_avg;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_BOOT:
+		val->intval = prop_values.voltage_boot;
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = prop_values.capacity;
+		break;
+	case POWER_SUPPLY_PROP_SCOPE:
+		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+		break;
+	case POWER_SUPPLY_PROP_TEMP:
+		val->intval = prop_values.temp;
+		break;
+	case POWER_SUPPLY_PROP_TEMP_MIN:
+		val->intval = 0; /* 0 C */
+		break;
+	case POWER_SUPPLY_PROP_TEMP_MAX:
+		val->intval = 400; /* 40 C */
+		break;
+	case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+		val->intval = 15; /* 1.5 C */
+		break;
+	case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+		val->intval = 380; /* 38 C */
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static inline void thunderstrike_request_psy_stats(struct thunderstrike *ts)
+{
+	set_bit(THUNDERSTRIKE_POWER_SUPPLY_STATS_UPDATE, &ts->update_flags);
+	schedule_work(&ts->hostcmd_req_work);
+}
+
+static void thunderstrike_psy_stats_timer_handler(struct timer_list *timer)
+{
+	struct thunderstrike *ts =
+		container_of(timer, struct thunderstrike, psy_stats_timer);
+
+	thunderstrike_request_psy_stats(ts);
+	/* Query battery statistics from device every five minutes */
+	mod_timer(timer, jiffies + 300 * HZ);
+}
+
 static void
 thunderstrike_parse_fw_version_payload(struct shield_device *shield_dev,
 				       __le16 fw_version)
@@ -416,13 +602,138 @@ thunderstrike_parse_led_payload(struct shield_device *shield_dev,
 	hid_dbg(shield_dev->hdev, "Thunderstrike led HOSTCMD response, 0x%02X\n", led_state);
 }
 
+static void thunderstrike_parse_battery_payload(
+	struct shield_device *shield_dev,
+	struct thunderstrike_hostcmd_battery *battery)
+{
+	struct thunderstrike *ts = container_of(shield_dev, struct thunderstrike, base);
+	u16 hostcmd_voltage_boot = le16_to_cpu(battery->voltage_boot);
+	u16 hostcmd_voltage_avg = le16_to_cpu(battery->voltage_avg);
+	u16 hostcmd_voltage_min = le16_to_cpu(battery->voltage_min);
+	u16 hostcmd_voltage_now = le16_to_cpu(battery->voltage_now);
+	u16 hostcmd_thermistor = le16_to_cpu(battery->thermistor);
+	int voltage_boot, voltage_avg, voltage_min, voltage_now;
+	struct hid_device *hdev = shield_dev->hdev;
+	u8 capacity = battery->capacity;
+	int temp;
+
+	/* Convert thunderstrike device values to µV and tenths of degree Celsius */
+	voltage_boot = hostcmd_voltage_boot * 1000;
+	voltage_avg = hostcmd_voltage_avg * 1000;
+	voltage_min = hostcmd_voltage_min * 1000;
+	voltage_now = hostcmd_voltage_now * 1000;
+	temp = (1378 - (int)hostcmd_thermistor) * 10 / 19;
+
+	/* Copy converted values */
+	spin_lock(&ts->psy_stats_lock);
+	ts->psy_stats.voltage_boot = voltage_boot;
+	ts->psy_stats.voltage_avg = voltage_avg;
+	ts->psy_stats.voltage_min = voltage_min;
+	ts->psy_stats.voltage_now = voltage_now;
+	ts->psy_stats.capacity = capacity;
+	ts->psy_stats.temp = temp;
+	spin_unlock(&ts->psy_stats_lock);
+
+	set_bit(SHIELD_BATTERY_STATS_INITIALIZED, &shield_dev->initialized_flags);
+
+	hid_dbg(hdev,
+		"Thunderstrike battery HOSTCMD response, voltage_avg: %u voltage_now: %u\n",
+		hostcmd_voltage_avg, hostcmd_voltage_now);
+	hid_dbg(hdev,
+		"Thunderstrike battery HOSTCMD response, voltage_boot: %u voltage_min: %u\n",
+		hostcmd_voltage_boot, hostcmd_voltage_min);
+	hid_dbg(hdev,
+		"Thunderstrike battery HOSTCMD response, thermistor: %u\n",
+		hostcmd_thermistor);
+	hid_dbg(hdev,
+		"Thunderstrike battery HOSTCMD response, capacity: %u%%\n",
+		capacity);
+}
+
+static void thunderstrike_parse_charger_payload(
+	struct shield_device *shield_dev,
+	struct thunderstrike_hostcmd_charger *charger)
+{
+	struct thunderstrike *ts = container_of(shield_dev, struct thunderstrike, base);
+	int charge_type = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+	struct hid_device *hdev = shield_dev->hdev;
+	int status = POWER_SUPPLY_STATUS_UNKNOWN;
+
+	switch (charger->type) {
+	case THUNDERSTRIKE_CHARGER_TYPE_NONE:
+		charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+		break;
+	case THUNDERSTRIKE_CHARGER_TYPE_TRICKLE:
+		charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+		break;
+	case THUNDERSTRIKE_CHARGER_TYPE_NORMAL:
+		charge_type = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
+		break;
+	default:
+		hid_warn(hdev, "Unhandled Thunderstrike charger HOSTCMD type, %u\n",
+			 charger->type);
+		break;
+	}
+
+	switch (charger->state) {
+	case THUNDERSTRIKE_CHARGER_STATE_UNKNOWN:
+		status = POWER_SUPPLY_STATUS_UNKNOWN;
+		break;
+	case THUNDERSTRIKE_CHARGER_STATE_DISABLED:
+		/* Indicates charger is disconnected */
+		break;
+	case THUNDERSTRIKE_CHARGER_STATE_CHARGING:
+		status = POWER_SUPPLY_STATUS_CHARGING;
+		break;
+	case THUNDERSTRIKE_CHARGER_STATE_FULL:
+		status = POWER_SUPPLY_STATUS_FULL;
+		break;
+	case THUNDERSTRIKE_CHARGER_STATE_FAILED:
+		status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+		hid_err(hdev, "Thunderstrike device failed to charge\n");
+		break;
+	default:
+		hid_warn(hdev, "Unhandled Thunderstrike charger HOSTCMD state, %u\n",
+			 charger->state);
+		break;
+	}
+
+	if (!charger->connected)
+		status = POWER_SUPPLY_STATUS_DISCHARGING;
+
+	spin_lock(&ts->psy_stats_lock);
+	ts->psy_stats.charge_type = charge_type;
+	ts->psy_stats.status = status;
+	spin_unlock(&ts->psy_stats_lock);
+
+	set_bit(SHIELD_CHARGER_STATE_INITIALIZED, &shield_dev->initialized_flags);
+
+	hid_dbg(hdev,
+		"Thunderstrike charger HOSTCMD response, connected: %u, type: %u, state: %u\n",
+		charger->connected, charger->type, charger->state);
+}
+
+static inline void thunderstrike_device_init_info(struct shield_device *shield_dev)
+{
+	struct thunderstrike *ts =
+		container_of(shield_dev, struct thunderstrike, base);
+
+	if (!test_bit(SHIELD_FW_VERSION_INITIALIZED, &shield_dev->initialized_flags))
+		thunderstrike_request_firmware_version(ts);
+
+	if (!test_bit(SHIELD_BOARD_INFO_INITIALIZED, &shield_dev->initialized_flags))
+		thunderstrike_request_board_info(ts);
+
+	if (!test_bit(SHIELD_BATTERY_STATS_INITIALIZED, &shield_dev->initialized_flags) ||
+	    !test_bit(SHIELD_CHARGER_STATE_INITIALIZED, &shield_dev->initialized_flags))
+		thunderstrike_psy_stats_timer_handler(&ts->psy_stats_timer);
+}
+
 static int thunderstrike_parse_report(struct shield_device *shield_dev,
 				      struct hid_report *report, u8 *data,
 				      int size)
 {
 	struct thunderstrike_hostcmd_resp_report *hostcmd_resp_report;
-	struct thunderstrike *ts =
-		container_of(shield_dev, struct thunderstrike, base);
 	struct hid_device *hdev = shield_dev->hdev;
 
 	switch (report->id) {
@@ -445,6 +756,10 @@ static int thunderstrike_parse_report(struct shield_device *shield_dev,
 		case THUNDERSTRIKE_HOSTCMD_ID_LED:
 			thunderstrike_parse_led_payload(shield_dev, hostcmd_resp_report->led_state);
 			break;
+		case THUNDERSTRIKE_HOSTCMD_ID_BATTERY:
+			thunderstrike_parse_battery_payload(shield_dev,
+							    &hostcmd_resp_report->battery);
+			break;
 		case THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO:
 			thunderstrike_parse_board_info_payload(
 				shield_dev, &hostcmd_resp_report->board_info);
@@ -453,14 +768,17 @@ static int thunderstrike_parse_report(struct shield_device *shield_dev,
 			thunderstrike_parse_haptics_payload(
 				shield_dev, &hostcmd_resp_report->motors);
 			break;
-
 		case THUNDERSTRIKE_HOSTCMD_ID_USB_INIT:
-		case THUNDERSTRIKE_HOSTCMD_ID_BLUETOOTH_INIT:
 			/* May block HOSTCMD requests till received initially */
-			thunderstrike_request_firmware_version(ts);
-			thunderstrike_request_board_info(ts);
-			/* Only HOSTCMD that can be triggered without a request */
-			return 0;
+			thunderstrike_device_init_info(shield_dev);
+			break;
+		case THUNDERSTRIKE_HOSTCMD_ID_CHARGER:
+			/* May block HOSTCMD requests till received initially */
+			thunderstrike_device_init_info(shield_dev);
+
+			thunderstrike_parse_charger_payload(
+				shield_dev, &hostcmd_resp_report->charger);
+			break;
 		default:
 			hid_warn(hdev,
 				 "Unhandled Thunderstrike HOSTCMD id %d\n",
@@ -489,6 +807,50 @@ static inline int thunderstrike_led_create(struct thunderstrike *ts)
 	return led_classdev_register(&ts->base.hdev->dev, led);
 }
 
+static inline int thunderstrike_psy_create(struct shield_device *shield_dev)
+{
+	struct thunderstrike *ts = container_of(shield_dev, struct thunderstrike, base);
+	struct power_supply_config psy_cfg = { .drv_data = shield_dev, };
+	struct hid_device *hdev = shield_dev->hdev;
+	int ret;
+
+	/*
+	 * Set an initial capacity and temperature value to avoid prematurely
+	 * triggering alerts. Will be replaced by values queried from initial
+	 * HOSTCMD requests.
+	 */
+	ts->psy_stats.capacity = 100;
+	ts->psy_stats.temp = 182;
+
+	shield_dev->battery_dev.desc.properties = thunderstrike_battery_props;
+	shield_dev->battery_dev.desc.num_properties =
+		ARRAY_SIZE(thunderstrike_battery_props);
+	shield_dev->battery_dev.desc.get_property = thunderstrike_battery_get_property;
+	shield_dev->battery_dev.desc.type = POWER_SUPPLY_TYPE_BATTERY;
+	shield_dev->battery_dev.desc.name =
+		devm_kasprintf(&ts->base.hdev->dev, GFP_KERNEL,
+			       "thunderstrike_%d", ts->id);
+
+	shield_dev->battery_dev.psy = power_supply_register(
+		&hdev->dev, &shield_dev->battery_dev.desc, &psy_cfg);
+	if (IS_ERR(shield_dev->battery_dev.psy)) {
+		hid_err(hdev, "Failed to register Thunderstrike battery device\n");
+		return PTR_ERR(shield_dev->battery_dev.psy);
+	}
+
+	ret = power_supply_powers(shield_dev->battery_dev.psy, &hdev->dev);
+	if (ret) {
+		hid_err(hdev, "Failed to associate battery device to Thunderstrike\n");
+		goto err;
+	}
+
+	return 0;
+
+err:
+	power_supply_unregister(shield_dev->battery_dev.psy);
+	return ret;
+}
+
 static struct shield_device *thunderstrike_create(struct hid_device *hdev)
 {
 	struct shield_device *shield_dev;
@@ -509,27 +871,47 @@ static struct shield_device *thunderstrike_create(struct hid_device *hdev)
 	shield_dev->codename = "Thunderstrike";
 
 	spin_lock_init(&ts->haptics_update_lock);
+	spin_lock_init(&ts->psy_stats_lock);
 	INIT_WORK(&ts->hostcmd_req_work, thunderstrike_hostcmd_req_work_handler);
 
 	hid_set_drvdata(hdev, shield_dev);
 
+	ts->id = ida_alloc(&thunderstrike_ida, GFP_KERNEL);
+	if (ts->id < 0)
+		return ERR_PTR(ts->id);
+
 	ts->haptics_dev = shield_haptics_create(shield_dev, thunderstrike_play_effect);
-	if (IS_ERR(ts->haptics_dev))
-		return ERR_CAST(ts->haptics_dev);
+	if (IS_ERR(ts->haptics_dev)) {
+		hid_err(hdev, "Failed to create Thunderstrike haptics instance\n");
+		ret = PTR_ERR(ts->haptics_dev);
+		goto err_id;
+	}
+
+	ret = thunderstrike_psy_create(shield_dev);
+	if (ret) {
+		hid_err(hdev, "Failed to create Thunderstrike power supply instance\n");
+		goto err_haptics;
+	}
 
 	ret = thunderstrike_led_create(ts);
 	if (ret) {
 		hid_err(hdev, "Failed to create Thunderstrike LED instance\n");
-		goto err;
+		goto err_psy;
 	}
 
+	timer_setup(&ts->psy_stats_timer, thunderstrike_psy_stats_timer_handler, 0);
+
 	hid_info(hdev, "Registered Thunderstrike controller\n");
 	return shield_dev;
 
-err:
+err_psy:
+	power_supply_unregister(shield_dev->battery_dev.psy);
+err_haptics:
 	if (ts->haptics_dev)
 		input_unregister_device(ts->haptics_dev);
-	return ERR_CAST(ts->haptics_dev);
+err_id:
+	ida_free(&thunderstrike_ida, ts->id);
+	return ERR_PTR(ret);
 }
 
 static int android_input_mapping(struct hid_device *hdev, struct hid_input *hi,
@@ -684,8 +1066,7 @@ static int shield_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		goto err_stop;
 	}
 
-	thunderstrike_request_firmware_version(ts);
-	thunderstrike_request_board_info(ts);
+	thunderstrike_device_init_info(shield_dev);
 
 	return ret;
 
@@ -705,9 +1086,12 @@ static void shield_remove(struct hid_device *hdev)
 	ts = container_of(dev, struct thunderstrike, base);
 
 	hid_hw_close(hdev);
-	led_classdev_unregister(&ts->led_dev);
+	power_supply_unregister(dev->battery_dev.psy);
 	if (ts->haptics_dev)
 		input_unregister_device(ts->haptics_dev);
+	led_classdev_unregister(&ts->led_dev);
+	ida_free(&thunderstrike_ida, ts->id);
+	del_timer_sync(&ts->psy_stats_timer);
 	cancel_work_sync(&ts->hostcmd_req_work);
 	hid_hw_stop(hdev);
 }
-- 
2.40.1


^ permalink raw reply related

* [PATCH 1/3] HID: nvidia-shield: Remove led_classdev_unregister in thunderstrike_create
From: Rahul Rameshbabu @ 2023-08-07 16:36 UTC (permalink / raw)
  To: Jiri Kosina, Benjamin Tissoires
  Cc: linux-input, linux-kernel, Rahul Rameshbabu

Avoid calling thunderstrike_led_set_brightness from thunderstrike_create
when led_classdev_unregister is called. led_classdev_unregister was called
from thunderstrike_create in the error path. Calling
thunderstrike_led_set_brightness in this situation is unsafe.

Fixes: f88af60e74a5 ("HID: nvidia-shield: Support LED functionality for Thunderstrike")
Signed-off-by: Rahul Rameshbabu <rrameshbabu@nvidia.com>
---

Notes:
    Discussion:
    
    An alternative approach that could be used here is setting the
    LED_RETAIN_AT_SHUTDOWN flag. To me, this felt like a less appropriate
    solution since in other contexts in the driver, calling
    led_classdev_unregister where it then tries to set the led to the LED_OFF
    state is safe.
    
    Example backtrace of problem when led_classdev_unregister is called from
    thunderstrike_create.
    
        [  +0.000061] thermal_sys: Thermal zone name (thunderstrike_0_battery) too long, should be under 20 chars
        [  +0.000096] shield 0005:0955:7214.001B: Failed to register Thunderstrike battery device
        [  +0.000001] shield 0005:0955:7214.001B: Failed to create Thunderstrike power supply instance
        [  +0.000024] shield 0005:0955:7214.001B: Failed to create SHIELD device
        [  +0.000003] shield: probe of 0005:0955:7214.001B failed with error -22
        [  +0.121671] BUG: unable to handle page fault for address: 000000046474e550
        [  +0.000009] #PF: supervisor read access in kernel mode
        [  +0.000003] #PF: error_code(0x0000) - not-present page
        [  +0.000003] PGD 0 P4D 0
        [  +0.000005] Oops: 0000 [#1] PREEMPT SMP NOPTI
        [  +0.000004] CPU: 14 PID: 36436 Comm: kworker/14:3 Tainted: P           O       6.4.7 #1-NixOS
        [  +0.000005] Hardware name: Dell Inc. Precision 5760/0WP4FK, BIOS 1.16.1 11/22/2022
        [  +0.000002] Workqueue: events thunderstrike_hostcmd_req_work_handler [hid_nvidia_shield]
        [  +0.000017] RIP: 0010:thunderstrike_hostcmd_req_work_handler+0x1b3/0x390 [hid_nvidia_shield]
        [  +0.000010] Code: 09 00 00 00 41 b8 01 00 00 00 48 c7 45 08 00 00 00 00 48 c7 45 17 00 00 00 00 66 41 89 04 24 48 8b 53 98 48 8b bb 90 fd ff ff <0f> b6 32 e8 b5 5e 99 fa 85 c0 0f 88 9d 01 00 00 0f b7 05 cc 05 02
        [  +0.000003] RSP: 0018:ffffa43e8f66fe78 EFLAGS: 00010207
        [  +0.000004] RAX: 0000000000000704 RBX: ffff93edf8498a98 RCX: 0000000000000021
        [  +0.000003] RDX: 000000046474e550 RSI: 0000000000000206 RDI: 00000000000002d8
        [  +0.000003] RBP: ffff93eca5485e6a R08: 0000000000000001 R09: 0000000000000009
        [  +0.000002] R10: 0000000000000001 R11: 0000000000000000 R12: ffff93eca5485e68
        [  +0.000002] R13: 0000000000000000 R14: ffff93eca8c34540 R15: ffff93edf8498aa0
        [  +0.000003] FS:  0000000000000000(0000) GS:ffff93f3ef780000(0000) knlGS:0000000000000000
        [  +0.000002] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
        [  +0.000003] CR2: 000000046474e550 CR3: 000000027d820003 CR4: 0000000000f70ee0
        [  +0.000003] PKRU: 55555554
        [  +0.000001] Call Trace:
        [  +0.000004]  <TASK>
        [  +0.000005]  ? __die+0x23/0x70
        [  +0.000009]  ? page_fault_oops+0x17d/0x4b0
        [  +0.000005]  ? lock_timer_base+0x61/0x80
        [  +0.000005]  ? exc_page_fault+0x6d/0x150
        [  +0.000008]  ? asm_exc_page_fault+0x26/0x30
        [  +0.000012]  ? thunderstrike_hostcmd_req_work_handler+0x1b3/0x390 [hid_nvidia_shield]
        [  +0.000008]  ? thunderstrike_hostcmd_req_work_handler+0x13b/0x390 [hid_nvidia_shield]
        [  +0.000009]  process_one_work+0x1c5/0x3c0
        [  +0.000005]  worker_thread+0x51/0x390
        [  +0.000004]  ? __pfx_worker_thread+0x10/0x10
        [  +0.000003]  kthread+0xe5/0x120
        [  +0.000005]  ? __pfx_kthread+0x10/0x10
        [  +0.000004]  ret_from_fork+0x29/0x50
        [  +0.000008]  </TASK>
        [  +0.000002] Modules linked in: hid_nvidia_shield(O) hidp rfcomm snd_seq_dummy snd_hrtimer snd_seq nf_conntrack_netlink xfrm_user xfrm_algo xt_addrtype ccm cmac algif_hash algif_skcipher af_alg xt_CHECKSUM xt_MASQUERADE af_packet ipt_REJECT nf_reject_ipv4 nft_chain_nat bnep msr xt_conntrack ip6t_rpfilter ipt_rpfilter xt_pkttype xt_LOG nf_log_syslog xt_tcpudp nft_compat nf_tables nfnetlink sch_fq_codel hid_sensor_custom_intel_hinge hid_sensor_als hid_sensor_trigger industrialio_triggered_buffer kfifo_buf hid_sensor_iio_common industrialio hid_sensor_custom hid_sensor_hub intel_ishtp_hid snd_ctl_led snd_soc_sof_sdw hid_multitouch snd_soc_intel_hda_dsp_common snd_sof_probes snd_soc_intel_sof_maxim_common snd_soc_rt711 snd_soc_rt715 snd_soc_rt1308_sdw regmap_sdw snd_soc_dmic snd_sof_pci_intel_tgl snd_sof_intel_hda_common snd_soc_hdac_hda soundwire_intel soundwire_cadence snd_sof_intel_hda_mlink snd_sof_intel_hda snd_sof_pci snd_sof_xtensa_dsp snd_sof snd_sof_utils snd_hda_ext_core snd_soc_acpi_intel_match snd_soc_acpi
        [  +0.000074]  soundwire_generic_allocation soundwire_bus snd_soc_core snd_hda_codec_hdmi snd_compress ac97_bus snd_pcm_dmaengine iwlmvm mac80211 dell_rbtn ptp pps_core libarc4 i2c_designware_platform i2c_designware_core cmdlinepart x86_pkg_temp_thermal intel_powerclamp ee1004 spi_nor iTCO_wdt mei_hdcp mei_wdt intel_pmc_bxt mei_pxp watchdog mtd r8153_ecm nls_iso8859_1 snd_hda_intel cdc_ether nls_cp437 coretemp pmt_telemetry snd_intel_dspcfg usbnet vfat intel_rapl_msr dell_laptop snd_intel_sdw_acpi crc32_pclmul pmt_class ledtrig_audio fat polyval_clmulni iwlwifi snd_usb_audio btusb dell_wmi polyval_generic snd_hda_codec btrtl gf128mul uvcvideo ghash_clmulni_intel hci_uart btbcm processor_thermal_device_pci_legacy snd_usbmidi_lib nvidia_drm(PO) snd_hda_core btintel videobuf2_vmalloc processor_thermal_device uvc btqca dell_smbios snd_rawmidi intel_lpss_pci ucsi_acpi btmtk processor_thermal_rfim rapl videobuf2_memops videobuf2_v4l2 dell_wmi_sysman typec_ucsi dcdbas snd_hwdep intel_cstate nvidia_uvm(PO) snd_seq_device
        [  +0.000085]  cfg80211 bluetooth psmouse intel_uncore videodev dell_wmi_ddv firmware_attributes_class dell_wmi_descriptor nvidia_modeset(PO) intel_ish_ipc intel_lpss wmi_bmof processor_thermal_mbox snd_pcm idma64 typec r8152 i2c_i801 intel_ishtp tpm_crb videobuf2_common processor_thermal_rapl mei_me spi_intel_pci tiny_power_button spi_intel intel_rapl_common i2c_smbus ecdh_generic mc mei snd_timer 8250_pci int3403_thermal mii virt_dma rfkill intel_vsec snd ecc int3400_thermal i2c_hid_acpi intel_hid mousedev evdev tpm_tis soundcore intel_soc_dts_iosf tpm_tis_core roles battery button i2c_hid crc16 joydev int340x_thermal_zone intel_pmc_core acpi_thermal_rel pinctrl_tigerlake mac_hid acpi_pad sparse_keymap serio_raw acpi_tad ac nvidia(PO) ctr loop cpufreq_powersave xt_nat nf_nat nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 br_netfilter veth tun tap macvlan bridge stp llc kvm_intel kvm fuse irqbypass deflate efi_pstore configfs efivarfs dmi_sysfs ip_tables x_tables autofs4 dm_crypt cbc encrypted_keys trusted asn1_encoder tee
        [  +0.000100]  tpm rng_core hid_generic usbhid hid xhci_pci rtsx_pci_sdmmc xhci_pci_renesas xhci_hcd mmc_core input_leds led_class nvme thunderbolt usbcore atkbd libps2 nvme_core vivaldi_fmap sha512_ssse3 sha512_generic aesni_intel rtsx_pci libaes t10_pi crypto_simd cryptd crc64_rocksoft crc64 crc_t10dif crct10dif_generic mfd_core usb_common crct10dif_pclmul crct10dif_common i8042 rtc_cmos serio btrfs blake2b_generic xor libcrc32c crc32c_generic crc32c_intel raid6_pq i915 i2c_algo_bit drm_buddy cec intel_gtt video wmi drm_display_helper drm_kms_helper syscopyarea sysfillrect sysimgblt ttm agpgart drm i2c_core backlight dm_snapshot dm_bufio dm_mod dax [last unloaded: hid_nvidia_shield(O)]
        [  +0.000075] CR2: 000000046474e550
        [  +0.000004] ---[ end trace 0000000000000000 ]---

 drivers/hid/hid-nvidia-shield.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/drivers/hid/hid-nvidia-shield.c b/drivers/hid/hid-nvidia-shield.c
index a928ad2be62d..4e183650c447 100644
--- a/drivers/hid/hid-nvidia-shield.c
+++ b/drivers/hid/hid-nvidia-shield.c
@@ -513,21 +513,22 @@ static struct shield_device *thunderstrike_create(struct hid_device *hdev)
 
 	hid_set_drvdata(hdev, shield_dev);
 
+	ts->haptics_dev = shield_haptics_create(shield_dev, thunderstrike_play_effect);
+	if (IS_ERR(ts->haptics_dev))
+		return ERR_CAST(ts->haptics_dev);
+
 	ret = thunderstrike_led_create(ts);
 	if (ret) {
 		hid_err(hdev, "Failed to create Thunderstrike LED instance\n");
-		return ERR_PTR(ret);
-	}
-
-	ts->haptics_dev = shield_haptics_create(shield_dev, thunderstrike_play_effect);
-	if (IS_ERR(ts->haptics_dev))
 		goto err;
+	}
 
 	hid_info(hdev, "Registered Thunderstrike controller\n");
 	return shield_dev;
 
 err:
-	led_classdev_unregister(&ts->led_dev);
+	if (ts->haptics_dev)
+		input_unregister_device(ts->haptics_dev);
 	return ERR_CAST(ts->haptics_dev);
 }
 
-- 
2.40.1


^ permalink raw reply related

* Re: [PATCH V2] Input: xpad - Add HyperX Clutch Gladiate Support
From: Rahul Rameshbabu @ 2023-08-07 16:24 UTC (permalink / raw)
  To: HP Dev
  Cc: carl.ng, chris.toledanes, dmitry.torokhov, linux-input,
	maxwell.nguyen
In-Reply-To: <20230804201926.10723-1-hphyperxdev@gmail.com>

On Fri, 04 Aug, 2023 13:19:26 -0700 HP Dev <hphyperxdev@gmail.com> wrote:
> Hi Rahul,
> Is there anything else we need to provide?

From what I can tell, this patch looks good now to me.

> When can we expect the patch to be rolled in?

This is up to the subsystem maintainers to manage. In the case of the
input subsystem, that would be Dmitry. Keep in mind the subsystem
maintainers are busy and persevere to help look at patches. I am sure he
will get to it in the future when he has a chance.

  It's also ok to resend the patch or the patch series after a couple of
  weeks with the word "RESEND" added to the subject line:

  https://docs.kernel.org/process/submitting-patches.html#don-t-get-discouraged-or-impatient

If you need to do a RESEND for this patch after a couple weeks, feel
free to add my Reviewed-by to the git trailer in your commit message.

--
Thanks,

Rahul Rameshbabu

^ permalink raw reply

* Re: [PATCH v1 00/12] HID: cp2112: Cleanups and refactorings
From: Andy Shevchenko @ 2023-08-07 14:30 UTC (permalink / raw)
  To: Jiri Kosina; +Cc: linux-input, linux-kernel, Benjamin Tissoires
In-Reply-To: <nycvar.YFH.7.76.2308071319140.14207@cbobk.fhfr.pm>

On Mon, Aug 07, 2023 at 01:19:54PM +0200, Jiri Kosina wrote:
> On Fri, 4 Aug 2023, Andy Shevchenko wrote:
> > On Thu, Jul 27, 2023 at 09:43:29PM +0300, Andy Shevchenko wrote:
> > > On Mon, Jul 03, 2023 at 09:52:10PM +0300, Andy Shevchenko wrote:
> > > > After I updated GPIO library for the case Benjamin has with CP2112,
> > > > I have a brief look into the CP2112 driver itself.
> > > > 
> > > > From GPIO perspective it has two main (maitenance) issues:
> > > > - usage of ->to_irq() with IRQ chip present;
> > > > - having IRQ chip not immutable.
> > > > 
> > > > Besides that there are plenty small cleanups here and there.
> > > > Hence this series.
> > > 
> > > Any comments on this?
> > 
> > Gentle ping^2 for this...
> > 
> > Anything should I do to improve it or is it okay to go as is?
> 
> I have been off pretty much the whole July. I am now back and slowly 
> making my way through everything that accumulated, I will eventually get 
> to this.
> 
> Thanks for the patience,

Ah, okay, no worries and take your time!

I was thinking more on Benjamin's answer as last time he had a hw setup
to test... Not sure what the status of that now and if he has a chance
to test this or busy enough with something else.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* [PATCH] drivers/input/touchscreen/goodix.c: Add support for ACPI ID GDX9110
From: Felix Engelhardt @ 2023-08-07 12:47 UTC (permalink / raw)
  Cc: Felix Engelhardt, Bastien Nocera, Hans de Goede, Dmitry Torokhov,
	linux-input, linux-kernel

The Goodix touchscreen controller with ACPI ID GDX9110 was not recognized
by the goodix driver. This patch adds this ID to the list of supported IDs,
allowing the driver to be used with this device. The change will allow
Linux to be used on ~1 million tablet devices used in Kenyan primary
schools.

Signed-off-by: Felix Engelhardt <felix.engelhardt@eidu.com>
---
 drivers/input/touchscreen/goodix.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 4f53d3c57e69..9cdc01eb00c9 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -1379,6 +1379,7 @@ MODULE_DEVICE_TABLE(i2c, goodix_ts_id);
 static const struct acpi_device_id goodix_acpi_match[] = {
 	{ "GDIX1001", 0 },
 	{ "GDIX1002", 0 },
+	{ "GDX9110", 0 },
 	{ }
 };
 MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
-- 
2.39.2


^ permalink raw reply related

* Re: [PATCH v1 00/12] HID: cp2112: Cleanups and refactorings
From: Jiri Kosina @ 2023-08-07 11:19 UTC (permalink / raw)
  To: Andy Shevchenko; +Cc: linux-input, linux-kernel, Benjamin Tissoires
In-Reply-To: <ZMydcGv8Dvu3Hje1@smile.fi.intel.com>

On Fri, 4 Aug 2023, Andy Shevchenko wrote:

> On Thu, Jul 27, 2023 at 09:43:29PM +0300, Andy Shevchenko wrote:
> > On Mon, Jul 03, 2023 at 09:52:10PM +0300, Andy Shevchenko wrote:
> > > After I updated GPIO library for the case Benjamin has with CP2112,
> > > I have a brief look into the CP2112 driver itself.
> > > 
> > > From GPIO perspective it has two main (maitenance) issues:
> > > - usage of ->to_irq() with IRQ chip present;
> > > - having IRQ chip not immutable.
> > > 
> > > Besides that there are plenty small cleanups here and there.
> > > Hence this series.
> > 
> > Any comments on this?
> 
> Gentle ping^2 for this...
> 
> Anything should I do to improve it or is it okay to go as is?

I have been off pretty much the whole July. I am now back and slowly 
making my way through everything that accumulated, I will eventually get 
to this.

Thanks for the patience,

-- 
Jiri Kosina
SUSE Labs


^ permalink raw reply

* Re: [PATCH v3] HID: Add introduction about HID for non-kernel programmers
From: Jiri Kosina @ 2023-08-07 11:25 UTC (permalink / raw)
  To: Jonathan Corbet
  Cc: Marco Morandini, Peter Hutterer, Benjamin Tissoires, linux-input,
	linux-doc
In-Reply-To: <87lef9c8jz.fsf@meer.lwn.net>

On Fri, 21 Jul 2023, Jonathan Corbet wrote:

> > Add an introduction about HID meant for the casual programmer
> > that is trying either to fix his device or to understand
> > what is going wrong.
> >
> > Signed-off-by: Marco Morandini <marco.morandini@polimi.it>
> > Co-authored-by: Peter Hutterer <peter.hutterer@who-t.net>
> > ---
> > v2: https://lore.kernel.org/linux-input/70fdef05-d3b8-e24b-77be-901bd5be369e@polimi.it/
> >
> > changes: 
> >   - corrections suggested in https://lore.kernel.org/linux-input/20230627060437.GA726439@quokka/
> >   - corrections suggested in https://lore.kernel.org/linux-input/f53e756f-7c81-1c79-23ea-b9009fdd2ef4@infradead.org/
> >   - corrections suggested in https://lore.kernel.org/linux-input/20230710021034.GA600582@quokka/
> >   - corrections suggested in https://lore.kernel.org/linux-input/20230717001544.GA129954@quokka/
> >   - some rewording of Documentation/hid/hidreport-parsing.rst
> >
> >  Documentation/hid/hidintro.rst          | 524 ++++++++++++++++++++++++
> >  Documentation/hid/hidreport-parsing.rst |  49 +++
> >  Documentation/hid/index.rst             |   1 +
> >  include/linux/hid.h                     |  23 ++
> >  4 files changed, 597 insertions(+)
> >  create mode 100644 Documentation/hid/hidintro.rst
> >  create mode 100644 Documentation/hid/hidreport-parsing.rst
> 
> Is there a plan for this patch?  Please let me know if I should take it
> through the docs tree.

I have now queued this in hid.git#for-6.5/doc. Sorry for the delay, I was 
off for pretty much whole July, and it takes time to go through all the 
things that accumulated.

Thanks,

-- 
Jiri Kosina
SUSE Labs


^ permalink raw reply

* Re: atkbd input regression
From: Linux regression tracking (Thorsten Leemhuis) @ 2023-08-07  7:53 UTC (permalink / raw)
  To: José Ramón Muñoz Pekkarinen, linux-input
  Cc: dmitry.torokhov, gregkh, Linux kernel regressions list
In-Reply-To: <CANWZPgK2+9SCavsbSZv1DfJyhy0XUpJZ6+ebatM9ZGJPpAN1yA@mail.gmail.com>

[CCing the regression list, as it should be in the loop for regressions:
https://docs.kernel.org/admin-guide/reporting-regressions.html]

Hi, Thorsten here, the Linux kernel's regression tracker.

On 07.08.23 07:19, José Ramón Muñoz Pekkarinen wrote:
>     Sorry again, now in plaintext mode.
> 
>     Hi,
> 
>     I'd like to seek help or advise in a possible
> regression that I detected on my system(a
> Thinkpad L14 Gen 1) that concerns the atkbd
> module. My OS is a gentoo, with self built
> kernels, and a plasma desktop environment
> using wayland. Up to the kernel 5.16.x my
> builtin keyboard worked(and works still)
> fine both in tty and the plasma session, but
> from that kernel on, the keyboard works only
> in tty, and no longer in the plasma session.

Could you try bisecting this? A few howtos on the net describe what to
do. Be sure to use the configuration for your working 5.16 kernel for
any builds of newer versions (ideally put it <somewhere> and run "cp
<somewhere> .config; make oldconfig" every time before building a newer
kernel.

Ciao, Thorsten

>     The input config section of my 5.16.17 kernel:
> 
> #
> # Input device support
> #
> CONFIG_INPUT=y
> CONFIG_INPUT_LEDS=m
> # CONFIG_INPUT_FF_MEMLESS is not set
> # CONFIG_INPUT_SPARSEKMAP is not set
> # CONFIG_INPUT_MATRIXKMAP is not set
> 
> #
> # Userland interfaces
> #
> CONFIG_INPUT_MOUSEDEV=m
> # CONFIG_INPUT_MOUSEDEV_PSAUX is not set
> CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
> CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
> CONFIG_INPUT_JOYDEV=m
> CONFIG_INPUT_EVDEV=m
> # CONFIG_INPUT_EVBUG is not set
> 
> #
> # Input Device Drivers
> #
> CONFIG_INPUT_KEYBOARD=y
> # CONFIG_KEYBOARD_ADP5588 is not set
> # CONFIG_KEYBOARD_ADP5589 is not set
> # CONFIG_KEYBOARD_APPLESPI is not set
> CONFIG_KEYBOARD_ATKBD=y
> # CONFIG_KEYBOARD_QT1050 is not set
> # CONFIG_KEYBOARD_QT1070 is not set
> # CONFIG_KEYBOARD_QT2160 is not set
> # CONFIG_KEYBOARD_DLINK_DIR685 is not set
> # CONFIG_KEYBOARD_LKKBD is not set
> # CONFIG_KEYBOARD_GPIO is not set
> # CONFIG_KEYBOARD_GPIO_POLLED is not set
> # CONFIG_KEYBOARD_TCA6416 is not set
> # CONFIG_KEYBOARD_TCA8418 is not set
> # CONFIG_KEYBOARD_MATRIX is not set
> # CONFIG_KEYBOARD_LM8323 is not set
> # CONFIG_KEYBOARD_LM8333 is not set
> # CONFIG_KEYBOARD_MAX7359 is not set
> # CONFIG_KEYBOARD_MCS is not set
> # CONFIG_KEYBOARD_MPR121 is not set
> # CONFIG_KEYBOARD_NEWTON is not set
> # CONFIG_KEYBOARD_OPENCORES is not set
> # CONFIG_KEYBOARD_SAMSUNG is not set
> # CONFIG_KEYBOARD_STOWAWAY is not set
> # CONFIG_KEYBOARD_SUNKBD is not set
> # CONFIG_KEYBOARD_TM2_TOUCHKEY is not set
> # CONFIG_KEYBOARD_XTKBD is not set
> # CONFIG_KEYBOARD_CYPRESS_SF is not set
> 
>     And the same for 6.4.7:
> 
> #
> # Input device support
> #
> CONFIG_INPUT=y
> CONFIG_INPUT_LEDS=m
> # CONFIG_INPUT_FF_MEMLESS is not set
> # CONFIG_INPUT_SPARSEKMAP is not set
> # CONFIG_INPUT_MATRIXKMAP is not set
> CONFIG_INPUT_VIVALDIFMAP=y
> 
> #
> # Userland interfaces
> #
> CONFIG_INPUT_MOUSEDEV=m
> # CONFIG_INPUT_MOUSEDEV_PSAUX is not set
> CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
> CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
> CONFIG_INPUT_JOYDEV=m
> CONFIG_INPUT_EVDEV=m
> # CONFIG_INPUT_EVBUG is not set
> 
> #
> # Input Device Drivers
> #
> CONFIG_INPUT_KEYBOARD=y
> # CONFIG_KEYBOARD_ADP5588 is not set
> # CONFIG_KEYBOARD_ADP5589 is not set
> # CONFIG_KEYBOARD_APPLESPI is not set
> CONFIG_KEYBOARD_ATKBD=y
> # CONFIG_KEYBOARD_QT1050 is not set
> # CONFIG_KEYBOARD_QT1070 is not set
> # CONFIG_KEYBOARD_QT2160 is not set
> # CONFIG_KEYBOARD_DLINK_DIR685 is not set
> # CONFIG_KEYBOARD_LKKBD is not set
> # CONFIG_KEYBOARD_GPIO is not set
> # CONFIG_KEYBOARD_GPIO_POLLED is not set
> # CONFIG_KEYBOARD_TCA6416 is not set
> # CONFIG_KEYBOARD_TCA8418 is not set
> # CONFIG_KEYBOARD_MATRIX is not set
> # CONFIG_KEYBOARD_LM8323 is not set
> # CONFIG_KEYBOARD_LM8333 is not set
> # CONFIG_KEYBOARD_MAX7359 is not set
> # CONFIG_KEYBOARD_MCS is not set
> # CONFIG_KEYBOARD_MPR121 is not set
> # CONFIG_KEYBOARD_NEWTON is not set
> # CONFIG_KEYBOARD_OPENCORES is not set
> # CONFIG_KEYBOARD_PINEPHONE is not set
> # CONFIG_KEYBOARD_SAMSUNG is not set
> # CONFIG_KEYBOARD_STOWAWAY is not set
> # CONFIG_KEYBOARD_SUNKBD is not set
> # CONFIG_KEYBOARD_TM2_TOUCHKEY is not set
> # CONFIG_KEYBOARD_XTKBD is not set
> # CONFIG_KEYBOARD_CYPRESS_SF is not set
> 
>     If I run evtest, the keyboard is detected, and I can
> retrieve its events. From my kernel 6.4, this is an
> example of it's output.
> 
> # evtest
> No device specified, trying to scan all of /dev/input/event*
> Available devices:
> /dev/input/event0:      AT Translated Set 2 keyboard
> /dev/input/event1:      Power Button
> /dev/input/event10:     SEM HCT Keyboard Consumer Control
> /dev/input/event11:     SEM HCT Keyboard System Control
> /dev/input/event12:     Logitech USB Optical Mouse
> /dev/input/event2:      Lid Switch
> /dev/input/event3:      Sleep Button
> /dev/input/event4:      Power Button
> /dev/input/event5:      Video Bus
> /dev/input/event6:      ThinkPad Extra Buttons
> /dev/input/event7:      SynPS/2 Synaptics TouchPad
> /dev/input/event8:      TPPS/2 Elan TrackPoint
> /dev/input/event9:      SEM HCT Keyboard
> Select the device event number [0-12]: 0
> Input driver version is 1.0.1
> Input device ID: bus 0x11 vendor 0x1 product 0x1 version 0xab54
> Input device name: "AT Translated Set 2 keyboard"
> Supported events:
>  Event type 0 (EV_SYN)
>  Event type 1 (EV_KEY)
>    Event code 1 (KEY_ESC)
>    Event code 2 (KEY_1)
>    Event code 3 (KEY_2)
>    Event code 4 (KEY_3)
>    Event code 5 (KEY_4)
>    Event code 6 (KEY_5)
>    Event code 7 (KEY_6)
>    Event code 8 (KEY_7)
>    Event code 9 (KEY_8)
>    Event code 10 (KEY_9)
>    Event code 11 (KEY_0)
>    Event code 12 (KEY_MINUS)
>    Event code 13 (KEY_EQUAL)
>    Event code 14 (KEY_BACKSPACE)
>    Event code 15 (KEY_TAB)
>    Event code 16 (KEY_Q)
>    Event code 17 (KEY_W)
>    Event code 18 (KEY_E)
>    Event code 19 (KEY_R)
>    Event code 20 (KEY_T)
>    Event code 21 (KEY_Y)
>    Event code 22 (KEY_U)
>    Event code 23 (KEY_I)
>    Event code 24 (KEY_O)
>    Event code 25 (KEY_P)
>    Event code 26 (KEY_LEFTBRACE)
>    Event code 27 (KEY_RIGHTBRACE)
>    Event code 28 (KEY_ENTER)
>    Event code 29 (KEY_LEFTCTRL)
>    Event code 30 (KEY_A)
>    Event code 31 (KEY_S)
>    Event code 32 (KEY_D)
>    Event code 33 (KEY_F)
>    Event code 34 (KEY_G)
>    Event code 35 (KEY_H)
>    Event code 36 (KEY_J)
>    Event code 37 (KEY_K)
>    Event code 38 (KEY_L)
>    Event code 39 (KEY_SEMICOLON)
>    Event code 40 (KEY_APOSTROPHE)
>    Event code 41 (KEY_GRAVE)
>    Event code 42 (KEY_LEFTSHIFT)
>    Event code 43 (KEY_BACKSLASH)
>    Event code 44 (KEY_Z)
>    Event code 45 (KEY_X)
>    Event code 46 (KEY_C)
>    Event code 47 (KEY_V)
>    Event code 48 (KEY_B)
>    Event code 49 (KEY_N)
>    Event code 50 (KEY_M)
>    Event code 51 (KEY_COMMA)
>    Event code 52 (KEY_DOT)
>    Event code 53 (KEY_SLASH)
>    Event code 54 (KEY_RIGHTSHIFT)
>    Event code 55 (KEY_KPASTERISK)
>    Event code 56 (KEY_LEFTALT)
>    Event code 57 (KEY_SPACE)
>    Event code 58 (KEY_CAPSLOCK)
>    Event code 59 (KEY_F1)
>    Event code 60 (KEY_F2)
>    Event code 61 (KEY_F3)
>    Event code 62 (KEY_F4)
>    Event code 63 (KEY_F5)
>    Event code 64 (KEY_F6)
>    Event code 65 (KEY_F7)
>    Event code 66 (KEY_F8)
>    Event code 67 (KEY_F9)
>    Event code 68 (KEY_F10)
>    Event code 69 (KEY_NUMLOCK)
>    Event code 70 (KEY_SCROLLLOCK)
>    Event code 71 (KEY_KP7)
>    Event code 72 (KEY_KP8)
>    Event code 73 (KEY_KP9)
>    Event code 74 (KEY_KPMINUS)
>    Event code 75 (KEY_KP4)
>    Event code 76 (KEY_KP5)
>    Event code 77 (KEY_KP6)
>    Event code 78 (KEY_KPPLUS)
>    Event code 79 (KEY_KP1)
>    Event code 80 (KEY_KP2)
>    Event code 81 (KEY_KP3)
>    Event code 82 (KEY_KP0)
>    Event code 83 (KEY_KPDOT)
>    Event code 85 (KEY_ZENKAKUHANKAKU)
>    Event code 86 (KEY_102ND)
>    Event code 87 (KEY_F11)
>    Event code 88 (KEY_F12)
>    Event code 89 (KEY_RO)
>    Event code 90 (KEY_KATAKANA)
>    Event code 91 (KEY_HIRAGANA)
>    Event code 92 (KEY_HENKAN)
>    Event code 93 (KEY_KATAKANAHIRAGANA)
>    Event code 94 (KEY_MUHENKAN)
>    Event code 95 (KEY_KPJPCOMMA)
>    Event code 96 (KEY_KPENTER)
>    Event code 97 (KEY_RIGHTCTRL)
>    Event code 98 (KEY_KPSLASH)
>    Event code 99 (KEY_SYSRQ)
>    Event code 100 (KEY_RIGHTALT)
>    Event code 102 (KEY_HOME)
>    Event code 103 (KEY_UP)
>    Event code 104 (KEY_PAGEUP)
>    Event code 105 (KEY_LEFT)
>    Event code 106 (KEY_RIGHT)
>    Event code 107 (KEY_END)
>    Event code 108 (KEY_DOWN)
>    Event code 109 (KEY_PAGEDOWN)
>    Event code 110 (KEY_INSERT)
>    Event code 111 (KEY_DELETE)
>    Event code 112 (KEY_MACRO)
>    Event code 113 (KEY_MUTE)
>    Event code 114 (KEY_VOLUMEDOWN)
>    Event code 115 (KEY_VOLUMEUP)
>    Event code 116 (KEY_POWER)
>    Event code 117 (KEY_KPEQUAL)
>    Event code 118 (KEY_KPPLUSMINUS)
>    Event code 119 (KEY_PAUSE)
>    Event code 121 (KEY_KPCOMMA)
>    Event code 122 (KEY_HANGUEL)
>    Event code 123 (KEY_HANJA)
>    Event code 124 (KEY_YEN)
>    Event code 125 (KEY_LEFTMETA)
>    Event code 126 (KEY_RIGHTMETA)
>    Event code 127 (KEY_COMPOSE)
>    Event code 128 (KEY_STOP)
>    Event code 140 (KEY_CALC)
>    Event code 142 (KEY_SLEEP)
>    Event code 143 (KEY_WAKEUP)
>    Event code 155 (KEY_MAIL)
>    Event code 156 (KEY_BOOKMARKS)
>    Event code 157 (KEY_COMPUTER)
>    Event code 158 (KEY_BACK)
>    Event code 159 (KEY_FORWARD)
>    Event code 163 (KEY_NEXTSONG)
>    Event code 164 (KEY_PLAYPAUSE)
>    Event code 165 (KEY_PREVIOUSSONG)
>    Event code 166 (KEY_STOPCD)
>    Event code 172 (KEY_HOMEPAGE)
>    Event code 173 (KEY_REFRESH)
>    Event code 183 (KEY_F13)
>    Event code 184 (KEY_F14)
>    Event code 185 (KEY_F15)
>    Event code 217 (KEY_SEARCH)
>    Event code 226 (KEY_MEDIA)
>  Event type 4 (EV_MSC)
>    Event code 4 (MSC_SCAN)
>  Event type 17 (EV_LED)
>    Event code 0 (LED_NUML) state 0
>    Event code 1 (LED_CAPSL) state 0
>    Event code 2 (LED_SCROLLL) state 0
> Key repeat handling:
>  Repeat type 20 (EV_REP)
>    Repeat code 0 (REP_DELAY)
>      Value    250
>    Repeat code 1 (REP_PERIOD)
>      Value     33
> Properties:
> Testing ... (interrupt to exit)
> Event: time 1691384520.078772, type 4 (EV_MSC), code 4 (MSC_SCAN), value 10
> Event: time 1691384520.078772, type 1 (EV_KEY), code 16 (KEY_Q), value 1
> Event: time 1691384520.078772, -------------- SYN_REPORT ------------
> Event: time 1691384520.157298, type 4 (EV_MSC), code 4 (MSC_SCAN), value 10
> Event: time 1691384520.157298, type 1 (EV_KEY), code 16 (KEY_Q), value 0
> Event: time 1691384520.157298, -------------- SYN_REPORT ------------
> Event: time 1691384520.849991, type 4 (EV_MSC), code 4 (MSC_SCAN), value 11
> Event: time 1691384520.849991, type 1 (EV_KEY), code 17 (KEY_W), value 1
> Event: time 1691384520.849991, -------------- SYN_REPORT ------------
> Event: time 1691384520.919630, type 4 (EV_MSC), code 4 (MSC_SCAN), value 11
> Event: time 1691384520.919630, type 1 (EV_KEY), code 17 (KEY_W), value 0
> Event: time 1691384520.919630, -------------- SYN_REPORT ------------
> Event: time 1691384521.311986, type 4 (EV_MSC), code 4 (MSC_SCAN), value 12
> Event: time 1691384521.311986, type 1 (EV_KEY), code 18 (KEY_E), value 1
> Event: time 1691384521.311986, -------------- SYN_REPORT ------------
> Event: time 1691384521.417578, type 4 (EV_MSC), code 4 (MSC_SCAN), value 12
> Event: time 1691384521.417578, type 1 (EV_KEY), code 18 (KEY_E), value 0
> Event: time 1691384521.417578, -------------- SYN_REPORT ------------
> Event: time 1691384521.639505, type 4 (EV_MSC), code 4 (MSC_SCAN), value 13
> Event: time 1691384521.639505, type 1 (EV_KEY), code 19 (KEY_R), value 1
> Event: time 1691384521.639505, -------------- SYN_REPORT ------------
> Event: time 1691384521.709170, type 4 (EV_MSC), code 4 (MSC_SCAN), value 13
> Event: time 1691384521.709170, type 1 (EV_KEY), code 19 (KEY_R), value 0
> Event: time 1691384521.709170, -------------- SYN_REPORT ------------
> Event: time 1691384521.879657, type 4 (EV_MSC), code 4 (MSC_SCAN), value 14
> Event: time 1691384521.879657, type 1 (EV_KEY), code 20 (KEY_T), value 1
> Event: time 1691384521.879657, -------------- SYN_REPORT ------------
> Event: time 1691384521.958384, type 4 (EV_MSC), code 4 (MSC_SCAN), value 14
> Event: time 1691384521.958384, type 1 (EV_KEY), code 20 (KEY_T), value 0
> Event: time 1691384521.958384, -------------- SYN_REPORT ------------
> Event: time 1691384522.188556, type 4 (EV_MSC), code 4 (MSC_SCAN), value 15
> Event: time 1691384522.188556, type 1 (EV_KEY), code 21 (KEY_Y), value 1
> 
>     I have reported this time ago in libinput, and they
> replied it might be a kernel problem, since booting
> my kernel 5.16 makes it work correctly again anytime.
> 
>    Could anyone please shed some light on this little
> problem?
> 
>    Best regards.
> 
>    José Pekkarinen

^ permalink raw reply

* atkbd input regression
From: José Ramón Muñoz Pekkarinen @ 2023-08-07  5:19 UTC (permalink / raw)
  To: linux-input; +Cc: dmitry.torokhov, gregkh

    Sorry again, now in plaintext mode.

    Hi,

    I'd like to seek help or advise in a possible
regression that I detected on my system(a
Thinkpad L14 Gen 1) that concerns the atkbd
module. My OS is a gentoo, with self built
kernels, and a plasma desktop environment
using wayland. Up to the kernel 5.16.x my
builtin keyboard worked(and works still)
fine both in tty and the plasma session, but
from that kernel on, the keyboard works only
in tty, and no longer in the plasma session.

    The input config section of my 5.16.17 kernel:

#
# Input device support
#
CONFIG_INPUT=y
CONFIG_INPUT_LEDS=m
# CONFIG_INPUT_FF_MEMLESS is not set
# CONFIG_INPUT_SPARSEKMAP is not set
# CONFIG_INPUT_MATRIXKMAP is not set

#
# Userland interfaces
#
CONFIG_INPUT_MOUSEDEV=m
# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
CONFIG_INPUT_JOYDEV=m
CONFIG_INPUT_EVDEV=m
# CONFIG_INPUT_EVBUG is not set

#
# Input Device Drivers
#
CONFIG_INPUT_KEYBOARD=y
# CONFIG_KEYBOARD_ADP5588 is not set
# CONFIG_KEYBOARD_ADP5589 is not set
# CONFIG_KEYBOARD_APPLESPI is not set
CONFIG_KEYBOARD_ATKBD=y
# CONFIG_KEYBOARD_QT1050 is not set
# CONFIG_KEYBOARD_QT1070 is not set
# CONFIG_KEYBOARD_QT2160 is not set
# CONFIG_KEYBOARD_DLINK_DIR685 is not set
# CONFIG_KEYBOARD_LKKBD is not set
# CONFIG_KEYBOARD_GPIO is not set
# CONFIG_KEYBOARD_GPIO_POLLED is not set
# CONFIG_KEYBOARD_TCA6416 is not set
# CONFIG_KEYBOARD_TCA8418 is not set
# CONFIG_KEYBOARD_MATRIX is not set
# CONFIG_KEYBOARD_LM8323 is not set
# CONFIG_KEYBOARD_LM8333 is not set
# CONFIG_KEYBOARD_MAX7359 is not set
# CONFIG_KEYBOARD_MCS is not set
# CONFIG_KEYBOARD_MPR121 is not set
# CONFIG_KEYBOARD_NEWTON is not set
# CONFIG_KEYBOARD_OPENCORES is not set
# CONFIG_KEYBOARD_SAMSUNG is not set
# CONFIG_KEYBOARD_STOWAWAY is not set
# CONFIG_KEYBOARD_SUNKBD is not set
# CONFIG_KEYBOARD_TM2_TOUCHKEY is not set
# CONFIG_KEYBOARD_XTKBD is not set
# CONFIG_KEYBOARD_CYPRESS_SF is not set

    And the same for 6.4.7:

#
# Input device support
#
CONFIG_INPUT=y
CONFIG_INPUT_LEDS=m
# CONFIG_INPUT_FF_MEMLESS is not set
# CONFIG_INPUT_SPARSEKMAP is not set
# CONFIG_INPUT_MATRIXKMAP is not set
CONFIG_INPUT_VIVALDIFMAP=y

#
# Userland interfaces
#
CONFIG_INPUT_MOUSEDEV=m
# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
CONFIG_INPUT_JOYDEV=m
CONFIG_INPUT_EVDEV=m
# CONFIG_INPUT_EVBUG is not set

#
# Input Device Drivers
#
CONFIG_INPUT_KEYBOARD=y
# CONFIG_KEYBOARD_ADP5588 is not set
# CONFIG_KEYBOARD_ADP5589 is not set
# CONFIG_KEYBOARD_APPLESPI is not set
CONFIG_KEYBOARD_ATKBD=y
# CONFIG_KEYBOARD_QT1050 is not set
# CONFIG_KEYBOARD_QT1070 is not set
# CONFIG_KEYBOARD_QT2160 is not set
# CONFIG_KEYBOARD_DLINK_DIR685 is not set
# CONFIG_KEYBOARD_LKKBD is not set
# CONFIG_KEYBOARD_GPIO is not set
# CONFIG_KEYBOARD_GPIO_POLLED is not set
# CONFIG_KEYBOARD_TCA6416 is not set
# CONFIG_KEYBOARD_TCA8418 is not set
# CONFIG_KEYBOARD_MATRIX is not set
# CONFIG_KEYBOARD_LM8323 is not set
# CONFIG_KEYBOARD_LM8333 is not set
# CONFIG_KEYBOARD_MAX7359 is not set
# CONFIG_KEYBOARD_MCS is not set
# CONFIG_KEYBOARD_MPR121 is not set
# CONFIG_KEYBOARD_NEWTON is not set
# CONFIG_KEYBOARD_OPENCORES is not set
# CONFIG_KEYBOARD_PINEPHONE is not set
# CONFIG_KEYBOARD_SAMSUNG is not set
# CONFIG_KEYBOARD_STOWAWAY is not set
# CONFIG_KEYBOARD_SUNKBD is not set
# CONFIG_KEYBOARD_TM2_TOUCHKEY is not set
# CONFIG_KEYBOARD_XTKBD is not set
# CONFIG_KEYBOARD_CYPRESS_SF is not set

    If I run evtest, the keyboard is detected, and I can
retrieve its events. From my kernel 6.4, this is an
example of it's output.

# evtest
No device specified, trying to scan all of /dev/input/event*
Available devices:
/dev/input/event0:      AT Translated Set 2 keyboard
/dev/input/event1:      Power Button
/dev/input/event10:     SEM HCT Keyboard Consumer Control
/dev/input/event11:     SEM HCT Keyboard System Control
/dev/input/event12:     Logitech USB Optical Mouse
/dev/input/event2:      Lid Switch
/dev/input/event3:      Sleep Button
/dev/input/event4:      Power Button
/dev/input/event5:      Video Bus
/dev/input/event6:      ThinkPad Extra Buttons
/dev/input/event7:      SynPS/2 Synaptics TouchPad
/dev/input/event8:      TPPS/2 Elan TrackPoint
/dev/input/event9:      SEM HCT Keyboard
Select the device event number [0-12]: 0
Input driver version is 1.0.1
Input device ID: bus 0x11 vendor 0x1 product 0x1 version 0xab54
Input device name: "AT Translated Set 2 keyboard"
Supported events:
 Event type 0 (EV_SYN)
 Event type 1 (EV_KEY)
   Event code 1 (KEY_ESC)
   Event code 2 (KEY_1)
   Event code 3 (KEY_2)
   Event code 4 (KEY_3)
   Event code 5 (KEY_4)
   Event code 6 (KEY_5)
   Event code 7 (KEY_6)
   Event code 8 (KEY_7)
   Event code 9 (KEY_8)
   Event code 10 (KEY_9)
   Event code 11 (KEY_0)
   Event code 12 (KEY_MINUS)
   Event code 13 (KEY_EQUAL)
   Event code 14 (KEY_BACKSPACE)
   Event code 15 (KEY_TAB)
   Event code 16 (KEY_Q)
   Event code 17 (KEY_W)
   Event code 18 (KEY_E)
   Event code 19 (KEY_R)
   Event code 20 (KEY_T)
   Event code 21 (KEY_Y)
   Event code 22 (KEY_U)
   Event code 23 (KEY_I)
   Event code 24 (KEY_O)
   Event code 25 (KEY_P)
   Event code 26 (KEY_LEFTBRACE)
   Event code 27 (KEY_RIGHTBRACE)
   Event code 28 (KEY_ENTER)
   Event code 29 (KEY_LEFTCTRL)
   Event code 30 (KEY_A)
   Event code 31 (KEY_S)
   Event code 32 (KEY_D)
   Event code 33 (KEY_F)
   Event code 34 (KEY_G)
   Event code 35 (KEY_H)
   Event code 36 (KEY_J)
   Event code 37 (KEY_K)
   Event code 38 (KEY_L)
   Event code 39 (KEY_SEMICOLON)
   Event code 40 (KEY_APOSTROPHE)
   Event code 41 (KEY_GRAVE)
   Event code 42 (KEY_LEFTSHIFT)
   Event code 43 (KEY_BACKSLASH)
   Event code 44 (KEY_Z)
   Event code 45 (KEY_X)
   Event code 46 (KEY_C)
   Event code 47 (KEY_V)
   Event code 48 (KEY_B)
   Event code 49 (KEY_N)
   Event code 50 (KEY_M)
   Event code 51 (KEY_COMMA)
   Event code 52 (KEY_DOT)
   Event code 53 (KEY_SLASH)
   Event code 54 (KEY_RIGHTSHIFT)
   Event code 55 (KEY_KPASTERISK)
   Event code 56 (KEY_LEFTALT)
   Event code 57 (KEY_SPACE)
   Event code 58 (KEY_CAPSLOCK)
   Event code 59 (KEY_F1)
   Event code 60 (KEY_F2)
   Event code 61 (KEY_F3)
   Event code 62 (KEY_F4)
   Event code 63 (KEY_F5)
   Event code 64 (KEY_F6)
   Event code 65 (KEY_F7)
   Event code 66 (KEY_F8)
   Event code 67 (KEY_F9)
   Event code 68 (KEY_F10)
   Event code 69 (KEY_NUMLOCK)
   Event code 70 (KEY_SCROLLLOCK)
   Event code 71 (KEY_KP7)
   Event code 72 (KEY_KP8)
   Event code 73 (KEY_KP9)
   Event code 74 (KEY_KPMINUS)
   Event code 75 (KEY_KP4)
   Event code 76 (KEY_KP5)
   Event code 77 (KEY_KP6)
   Event code 78 (KEY_KPPLUS)
   Event code 79 (KEY_KP1)
   Event code 80 (KEY_KP2)
   Event code 81 (KEY_KP3)
   Event code 82 (KEY_KP0)
   Event code 83 (KEY_KPDOT)
   Event code 85 (KEY_ZENKAKUHANKAKU)
   Event code 86 (KEY_102ND)
   Event code 87 (KEY_F11)
   Event code 88 (KEY_F12)
   Event code 89 (KEY_RO)
   Event code 90 (KEY_KATAKANA)
   Event code 91 (KEY_HIRAGANA)
   Event code 92 (KEY_HENKAN)
   Event code 93 (KEY_KATAKANAHIRAGANA)
   Event code 94 (KEY_MUHENKAN)
   Event code 95 (KEY_KPJPCOMMA)
   Event code 96 (KEY_KPENTER)
   Event code 97 (KEY_RIGHTCTRL)
   Event code 98 (KEY_KPSLASH)
   Event code 99 (KEY_SYSRQ)
   Event code 100 (KEY_RIGHTALT)
   Event code 102 (KEY_HOME)
   Event code 103 (KEY_UP)
   Event code 104 (KEY_PAGEUP)
   Event code 105 (KEY_LEFT)
   Event code 106 (KEY_RIGHT)
   Event code 107 (KEY_END)
   Event code 108 (KEY_DOWN)
   Event code 109 (KEY_PAGEDOWN)
   Event code 110 (KEY_INSERT)
   Event code 111 (KEY_DELETE)
   Event code 112 (KEY_MACRO)
   Event code 113 (KEY_MUTE)
   Event code 114 (KEY_VOLUMEDOWN)
   Event code 115 (KEY_VOLUMEUP)
   Event code 116 (KEY_POWER)
   Event code 117 (KEY_KPEQUAL)
   Event code 118 (KEY_KPPLUSMINUS)
   Event code 119 (KEY_PAUSE)
   Event code 121 (KEY_KPCOMMA)
   Event code 122 (KEY_HANGUEL)
   Event code 123 (KEY_HANJA)
   Event code 124 (KEY_YEN)
   Event code 125 (KEY_LEFTMETA)
   Event code 126 (KEY_RIGHTMETA)
   Event code 127 (KEY_COMPOSE)
   Event code 128 (KEY_STOP)
   Event code 140 (KEY_CALC)
   Event code 142 (KEY_SLEEP)
   Event code 143 (KEY_WAKEUP)
   Event code 155 (KEY_MAIL)
   Event code 156 (KEY_BOOKMARKS)
   Event code 157 (KEY_COMPUTER)
   Event code 158 (KEY_BACK)
   Event code 159 (KEY_FORWARD)
   Event code 163 (KEY_NEXTSONG)
   Event code 164 (KEY_PLAYPAUSE)
   Event code 165 (KEY_PREVIOUSSONG)
   Event code 166 (KEY_STOPCD)
   Event code 172 (KEY_HOMEPAGE)
   Event code 173 (KEY_REFRESH)
   Event code 183 (KEY_F13)
   Event code 184 (KEY_F14)
   Event code 185 (KEY_F15)
   Event code 217 (KEY_SEARCH)
   Event code 226 (KEY_MEDIA)
 Event type 4 (EV_MSC)
   Event code 4 (MSC_SCAN)
 Event type 17 (EV_LED)
   Event code 0 (LED_NUML) state 0
   Event code 1 (LED_CAPSL) state 0
   Event code 2 (LED_SCROLLL) state 0
Key repeat handling:
 Repeat type 20 (EV_REP)
   Repeat code 0 (REP_DELAY)
     Value    250
   Repeat code 1 (REP_PERIOD)
     Value     33
Properties:
Testing ... (interrupt to exit)
Event: time 1691384520.078772, type 4 (EV_MSC), code 4 (MSC_SCAN), value 10
Event: time 1691384520.078772, type 1 (EV_KEY), code 16 (KEY_Q), value 1
Event: time 1691384520.078772, -------------- SYN_REPORT ------------
Event: time 1691384520.157298, type 4 (EV_MSC), code 4 (MSC_SCAN), value 10
Event: time 1691384520.157298, type 1 (EV_KEY), code 16 (KEY_Q), value 0
Event: time 1691384520.157298, -------------- SYN_REPORT ------------
Event: time 1691384520.849991, type 4 (EV_MSC), code 4 (MSC_SCAN), value 11
Event: time 1691384520.849991, type 1 (EV_KEY), code 17 (KEY_W), value 1
Event: time 1691384520.849991, -------------- SYN_REPORT ------------
Event: time 1691384520.919630, type 4 (EV_MSC), code 4 (MSC_SCAN), value 11
Event: time 1691384520.919630, type 1 (EV_KEY), code 17 (KEY_W), value 0
Event: time 1691384520.919630, -------------- SYN_REPORT ------------
Event: time 1691384521.311986, type 4 (EV_MSC), code 4 (MSC_SCAN), value 12
Event: time 1691384521.311986, type 1 (EV_KEY), code 18 (KEY_E), value 1
Event: time 1691384521.311986, -------------- SYN_REPORT ------------
Event: time 1691384521.417578, type 4 (EV_MSC), code 4 (MSC_SCAN), value 12
Event: time 1691384521.417578, type 1 (EV_KEY), code 18 (KEY_E), value 0
Event: time 1691384521.417578, -------------- SYN_REPORT ------------
Event: time 1691384521.639505, type 4 (EV_MSC), code 4 (MSC_SCAN), value 13
Event: time 1691384521.639505, type 1 (EV_KEY), code 19 (KEY_R), value 1
Event: time 1691384521.639505, -------------- SYN_REPORT ------------
Event: time 1691384521.709170, type 4 (EV_MSC), code 4 (MSC_SCAN), value 13
Event: time 1691384521.709170, type 1 (EV_KEY), code 19 (KEY_R), value 0
Event: time 1691384521.709170, -------------- SYN_REPORT ------------
Event: time 1691384521.879657, type 4 (EV_MSC), code 4 (MSC_SCAN), value 14
Event: time 1691384521.879657, type 1 (EV_KEY), code 20 (KEY_T), value 1
Event: time 1691384521.879657, -------------- SYN_REPORT ------------
Event: time 1691384521.958384, type 4 (EV_MSC), code 4 (MSC_SCAN), value 14
Event: time 1691384521.958384, type 1 (EV_KEY), code 20 (KEY_T), value 0
Event: time 1691384521.958384, -------------- SYN_REPORT ------------
Event: time 1691384522.188556, type 4 (EV_MSC), code 4 (MSC_SCAN), value 15
Event: time 1691384522.188556, type 1 (EV_KEY), code 21 (KEY_Y), value 1

    I have reported this time ago in libinput, and they
replied it might be a kernel problem, since booting
my kernel 5.16 makes it work correctly again anytime.

   Could anyone please shed some light on this little
problem?

   Best regards.

   José Pekkarinen

^ permalink raw reply

* Re: Fwd: 6.5 - 6.4.7 Regression : ASUS UM5302TA Keyboard don't work
From: Hans de Goede @ 2023-08-06 15:19 UTC (permalink / raw)
  To: August Wikerfors, Linux regressions mailing list
  Cc: Mario Limonciello, Linux Input Devices, ACPI Asus,
	Linux x86 Platform Drivers, Linux Kernel Mailing List,
	Bagas Sanjaya, Dmitry Torokhov, Corentin Chary, Guilhem Lettron
In-Reply-To: <cc9e37b4-b5cb-fd4d-84b8-5b824afe710a@redhat.com>

Hi,

On 8/5/23 15:09, Hans de Goede wrote:
> Hi,
> 
> On 8/4/23 17:26, August Wikerfors wrote:
>> On 2023-07-30 06:49, Linux regression tracking (Thorsten Leemhuis) wrote:
>>> Lo!
>>>
>>> On 30.07.23 04:41, Bagas Sanjaya wrote:
>>>>
>>>> I notice a regression report on Bugzilla [1]. Quoting from it:
>>>>
>>>>> On a kernel 6.4.5 and less, the keyboard is working fine.
>>>>>
>>>>> Beginning with 6.5 rc1 and 6.4.7 any key don't respond.
>>>
>>> That is a AMD Ryzen Laptop. And if that really started from
>>> v6.4.6..v6.4.7 then I guess there is a decent chance that this is caused
>>> by ```ACPI: resource: Remove "Zen" specific match and quirks``` from
>>> Mario. Hence adding him to the list of recipients.
>>
>> Confirmed now, see https://bugzilla.kernel.org/show_bug.cgi?id=217726#c9
>>
>> #regzbot introduced: a9c4a912b7dc7ff922d4b9261160c001558f9755
> 
> We just have received 2 bug reports for Fedora which I believe are also
> this issue (not confirmed yet):
> 
> https://bugzilla.redhat.com/show_bug.cgi?id=2229165
> https://bugzilla.redhat.com/show_bug.cgi?id=2229317
> 
> I'm going to create a Fedora 6.4.y test-kernel with a9c4a912b7dc7ff
> reverted.
> 
> IMHO we really should revert a9c4a912b7dc7ff upstream,
> at least for the 6.4.y series where it seems to be doing
> more harm then good.
> 
> And propably also for 6.5-rc# for now until we figure out
> a better solution.

Testing has shown that reverting also fixes the 2 Fedora bugs.

I have submitted a revert of a9c4a912b7dc ("ACPI: resource: Remove "Zen" specific match and quirks") upstream now, because I believe that that is the best way to fix the regressions caused by this (until we figure out a better way to deal with the kbd interrupt trigger-type issues):

https://lore.kernel.org/linux-acpi/20230806151453.10690-1-hdegoede@redhat.com/

Regards,

Hans


^ permalink raw reply


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