Linux Input/HID development
 help / color / mirror / Atom feed
* Re: [PATCH v4 1/3] HID: nintendo: Add preliminary Switch 2 controller driver
From: Silvan Jegen @ 2026-05-06 19:17 UTC (permalink / raw)
  To: Vicki Pfau; +Cc: Dmitry Torokhov, Jiri Kosina, Benjamin Tissoires, linux-input
In-Reply-To: <20260415073142.1303505-2-vi@endrift.com>

Hi!

Just some more small things (that I unfortunately missed the first time
around) below.

Vicki Pfau <vi@endrift.com> wrote:
> This adds a new driver for the Switch 2 controllers. The Switch 2 uses an
> unusual split-interface design such that input and rumble occur on the main
> HID interface, but all other communication occurs over a "configuration"
> interface. This is the case on both USB and Bluetooth, so this new driver
> uses a split-driver design with the HID interface being the "main" driver
> and the configuration interface is a secondary driver that looks up to the
> HID interface, sharing resources on a common struct.
> 
> Due to using a non-standard pairing interface as well as Bluetooth
> communications being extremely limited in the kernel, a custom interface
> between userspace and the kernel will need to be designed, along with
> bringup in BlueZ. That is beyond the scope of this initial patch, which
> only contains the generic HID and USB configuration interface drivers.
> 
> This initial work supports general input for the Joy-Con 2, Pro Controller
> 2, and GameCube NSO controllers. IMU, rumble and battery support is not yet
> present.
> 
> Signed-off-by: Vicki Pfau <vi@endrift.com>
> ---
>  MAINTAINERS                                   |    1 +
>  drivers/hid/Kconfig                           |   11 +-
>  drivers/hid/hid-ids.h                         |    4 +
>  drivers/hid/hid-nintendo.c                    | 1194 ++++++++++++++++-
>  drivers/hid/hid-nintendo.h                    |   72 +
>  drivers/input/joystick/Kconfig                |   11 +
>  drivers/input/joystick/Makefile               |    1 +
>  drivers/input/joystick/nintendo-switch2-usb.c |  353 +++++
>  8 files changed, 1637 insertions(+), 10 deletions(-)
>  create mode 100644 drivers/hid/hid-nintendo.h
>  create mode 100644 drivers/input/joystick/nintendo-switch2-usb.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 7b277d5bf3d1..4d1a28df5fd2 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -18743,6 +18743,7 @@ F:	drivers/scsi/nsp32*
>  
>  NINTENDO HID DRIVER
>  M:	Daniel J. Ogorchock <djogorchock@gmail.com>
> +M:	Vicki Pfau <vi@endrift.com>
>  L:	linux-input@vger.kernel.org
>  S:	Maintained
>  F:	drivers/hid/hid-nintendo*
> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index c1d9f7c6a5f2..1a293a6c02c2 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -826,10 +826,13 @@ config HID_NINTENDO
>  	depends on LEDS_CLASS
>  	select POWER_SUPPLY
>  	help
> -	Adds support for the Nintendo Switch Joy-Cons, NSO, Pro Controller.
> -	All controllers support bluetooth, and the Pro Controller also supports
> -	its USB mode. This also includes support for the Nintendo Switch Online
> -	Controllers which include the NES, Genesis, SNES, and N64 controllers.
> +	Adds support for the Nintendo Switch Joy-Cons, NSO, Pro Controller, as
> +	well as Nintendo Switch 2 Joy-Cons, Pro Controller, and NSO GameCube
> +	controllers. All Switch controllers support bluetooth, and the Pro
> +	Controller also supports its USB mode. This also includes support for
> +	the Nintendo Switch Online Controllers which include the NES, Genesis,
> +	SNES, and N64 controllers. Switch 2 controllers currently only support
> +	USB mode.
>  
>  	To compile this driver as a module, choose M here: the
>  	module will be called hid-nintendo.
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index 4ab7640b119a..a794dad7980f 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -1073,6 +1073,10 @@
>  #define USB_DEVICE_ID_NINTENDO_SNESCON	0x2017
>  #define USB_DEVICE_ID_NINTENDO_GENCON	0x201e
>  #define USB_DEVICE_ID_NINTENDO_N64CON	0x2019
> +#define USB_DEVICE_ID_NINTENDO_NS2_JOYCONR	0x2066
> +#define USB_DEVICE_ID_NINTENDO_NS2_JOYCONL	0x2067
> +#define USB_DEVICE_ID_NINTENDO_NS2_PROCON	0x2069
> +#define USB_DEVICE_ID_NINTENDO_NS2_GCCON	0x2073
>  
>  #define USB_VENDOR_ID_NOVATEK		0x0603
>  #define USB_DEVICE_ID_NOVATEK_PCT	0x0600
> diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c
> index 29008c2cc530..ac84e32ed0bd 100644
> --- a/drivers/hid/hid-nintendo.c
> +++ b/drivers/hid/hid-nintendo.c
> @@ -1,11 +1,13 @@
>  // SPDX-License-Identifier: GPL-2.0+
>  /*
> - * HID driver for Nintendo Switch Joy-Cons and Pro Controllers
> + * HID driver for Nintendo Switch Joy-Cons and Pro Controllers, as well as
> + * Nintendo Switch 2 Joy-Cons, Pro Controller, and GameCube Controller
>   *
>   * Copyright (c) 2019-2021 Daniel J. Ogorchock <djogorchock@gmail.com>
>   * Portions Copyright (c) 2020 Nadia Holmquist Pedersen <nadia@nhp.sh>
>   * Copyright (c) 2022 Emily Strickland <linux@emily.st>
>   * Copyright (c) 2023 Ryan McClelland <rymcclel@gmail.com>
> + * Copyright (c) 2026 Valve Software
>   *
>   * The following resources/projects were referenced for this driver:
>   *   https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
> @@ -13,6 +15,8 @@
>   *   https://github.com/FrotBot/SwitchProConLinuxUSB
>   *   https://github.com/MTCKC/ProconXInput
>   *   https://github.com/Davidobot/BetterJoyForCemu
> + *   https://gist.github.com/shinyquagsire23/66f006b46c56216acbaac6c1e2279b64
> + *   https://github.com/ndeadly/switch2_controller_research
>   *   hid-wiimote kernel hid driver
>   *   hid-logitech-hidpp driver
>   *   hid-sony driver
> @@ -29,6 +33,7 @@
>   */
>  
>  #include "hid-ids.h"
> +#include "hid-nintendo.h"
>  #include <linux/unaligned.h>
>  #include <linux/delay.h>
>  #include <linux/device.h>
> @@ -41,6 +46,8 @@
>  #include <linux/module.h>
>  #include <linux/power_supply.h>
>  #include <linux/spinlock.h>
> +#include <linux/usb.h>
> +#include "usbhid/usbhid.h"
>  
>  /*
>   * Reference the url below for the following HID report defines:
> @@ -2614,7 +2621,7 @@ static int joycon_ctlr_handle_event(struct joycon_ctlr *ctlr, u8 *data,
>  	return ret;
>  }
>  
> -static int nintendo_hid_event(struct hid_device *hdev,
> +static int joycon_event(struct hid_device *hdev,
>  			      struct hid_report *report, u8 *raw_data, int size)
>  {
>  	struct joycon_ctlr *ctlr = hid_get_drvdata(hdev);
> @@ -2625,7 +2632,7 @@ static int nintendo_hid_event(struct hid_device *hdev,
>  	return joycon_ctlr_handle_event(ctlr, raw_data, size);
>  }
>  
> -static int nintendo_hid_probe(struct hid_device *hdev,
> +static int joycon_probe(struct hid_device *hdev,
>  			    const struct hid_device_id *id)
>  {
>  	int ret;
> @@ -2729,7 +2736,7 @@ static int nintendo_hid_probe(struct hid_device *hdev,
>  	return ret;
>  }
>  
> -static void nintendo_hid_remove(struct hid_device *hdev)
> +static void joycon_remove(struct hid_device *hdev)
>  {
>  	struct joycon_ctlr *ctlr = hid_get_drvdata(hdev);
>  	unsigned long flags;
> @@ -2748,7 +2755,9 @@ static void nintendo_hid_remove(struct hid_device *hdev)
>  	hid_hw_stop(hdev);
>  }
>  
> -static int nintendo_hid_resume(struct hid_device *hdev)
> +#ifdef CONFIG_PM
> +
> +static int joycon_resume(struct hid_device *hdev)
>  {
>  	struct joycon_ctlr *ctlr = hid_get_drvdata(hdev);
>  	int ret;
> @@ -2771,7 +2780,7 @@ static int nintendo_hid_resume(struct hid_device *hdev)
>  	return ret;
>  }
>  
> -static int nintendo_hid_suspend(struct hid_device *hdev, pm_message_t message)
> +static int joycon_suspend(struct hid_device *hdev, pm_message_t message)
>  {
>  	struct joycon_ctlr *ctlr = hid_get_drvdata(hdev);
>  
> @@ -2790,7 +2799,1120 @@ static int nintendo_hid_suspend(struct hid_device *hdev, pm_message_t message)
>  	return 0;
>  }
>  
> +#endif
> +
> +/*
> + * =============================================================================
> + * Switch 2 support
> + * =============================================================================
> + */
> +#define NS2_BTNR_B	BIT(0)
> +#define NS2_BTNR_A	BIT(1)
> +#define NS2_BTNR_Y	BIT(2)
> +#define NS2_BTNR_X	BIT(3)
> +#define NS2_BTNR_R	BIT(4)
> +#define NS2_BTNR_ZR	BIT(5)
> +#define NS2_BTNR_PLUS	BIT(6)
> +#define NS2_BTNR_RS	BIT(7)
> +
> +#define NS2_BTNL_DOWN	BIT(0)
> +#define NS2_BTNL_RIGHT	BIT(1)
> +#define NS2_BTNL_LEFT	BIT(2)
> +#define NS2_BTNL_UP	BIT(3)
> +#define NS2_BTNL_L	BIT(4)
> +#define NS2_BTNL_ZL	BIT(5)
> +#define NS2_BTNL_MINUS	BIT(6)
> +#define NS2_BTNL_LS	BIT(7)
> +
> +#define NS2_BTN3_C	BIT(4)
> +#define NS2_BTN3_SR	BIT(6)
> +#define NS2_BTN3_SL	BIT(7)
> +
> +#define NS2_BTN_JCR_HOME	BIT(0)
> +#define NS2_BTN_JCR_GR		BIT(2)
> +#define NS2_BTN_JCR_C		NS2_BTN3_C
> +#define NS2_BTN_JCR_SR		NS2_BTN3_SR
> +#define NS2_BTN_JCR_SL		NS2_BTN3_SL
> +
> +#define NS2_BTN_JCL_CAPTURE	BIT(0)
> +#define NS2_BTN_JCL_GL		BIT(2)
> +#define NS2_BTN_JCL_SR		NS2_BTN3_SR
> +#define NS2_BTN_JCL_SL		NS2_BTN3_SL
> +
> +#define NS2_BTN_PRO_HOME	BIT(0)
> +#define NS2_BTN_PRO_CAPTURE	BIT(1)
> +#define NS2_BTN_PRO_GR		BIT(2)
> +#define NS2_BTN_PRO_GL		BIT(3)
> +#define NS2_BTN_PRO_C		NS2_BTN3_C
> +
> +#define NS2_BTN_GC_HOME		BIT(0)
> +#define NS2_BTN_GC_CAPTURE	BIT(1)
> +#define NS2_BTN_GC_C		NS2_BTN3_C
> +
> +#define NS2_TRIGGER_RANGE	4095
> +#define NS2_AXIS_MIN		-32768
> +#define NS2_AXIS_MAX		32767
> +
> +#define NS2_MAX_PLAYER_ID	8
> +
> +#define NS2_MAX_INIT_RETRIES	4
> +
> +#define NS2_FLASH_ADDR_SERIAL			0x13002
> +#define NS2_FLASH_ADDR_FACTORY_PRIMARY_CALIB	0x130a8
> +#define NS2_FLASH_ADDR_FACTORY_SECONDARY_CALIB	0x130e8
> +#define NS2_FLASH_ADDR_FACTORY_TRIGGER_CALIB	0x13140
> +#define NS2_FLASH_ADDR_USER_PRIMARY_CALIB	0x1fc040
> +#define NS2_FLASH_ADDR_USER_SECONDARY_CALIB	0x1fc080
> +
> +#define NS2_FLASH_SIZE_SERIAL 0x10
> +#define NS2_FLASH_SIZE_FACTORY_AXIS_CALIB 9
> +#define NS2_FLASH_SIZE_FACTORY_TRIGGER_CALIB 2
> +#define NS2_FLASH_SIZE_USER_AXIS_CALIB 11
> +
> +#define NS2_USER_CALIB_MAGIC 0xa1b2
> +
> +#define NS2_FEATURE_BUTTONS	BIT(0)
> +#define NS2_FEATURE_ANALOG	BIT(1)
> +#define NS2_FEATURE_IMU		BIT(2)
> +#define NS2_FEATURE_MOUSE	BIT(4)
> +#define NS2_FEATURE_RUMBLE	BIT(5)
> +#define NS2_FEATURE_MAGNETO	BIT(7)
> +
> +enum switch2_subcmd_flash {
> +	NS2_SUBCMD_FLASH_READ_BLOCK = 0x01,
> +	NS2_SUBCMD_FLASH_WRITE_BLOCK = 0x02,
> +	NS2_SUBCMD_FLASH_ERASE_BLOCK = 0x03,
> +	NS2_SUBCMD_FLASH_READ = 0x04,
> +	NS2_SUBCMD_FLASH_WRITE = 0x05,
> +};
> +
> +enum switch2_subcmd_init {
> +	NS2_SUBCMD_INIT_SELECT_REPORT = 0xa,
> +	NS2_SUBCMD_INIT_USB = 0xd,
> +};
> +
> +enum switch2_subcmd_feature_select {
> +	NS2_SUBCMD_FEATSEL_GET_INFO = 0x1,
> +	NS2_SUBCMD_FEATSEL_SET_MASK = 0x2,
> +	NS2_SUBCMD_FEATSEL_CLEAR_MASK = 0x3,
> +	NS2_SUBCMD_FEATSEL_ENABLE = 0x4,
> +	NS2_SUBCMD_FEATSEL_DISABLE = 0x5,
> +};
> +
> +enum switch2_subcmd_grip {
> +	NS2_SUBCMD_GRIP_GET_INFO = 0x1,
> +	NS2_SUBCMD_GRIP_ENABLE_BUTTONS = 0x2,
> +	NS2_SUBCMD_GRIP_GET_INFO_EXT = 0x3,
> +};
> +
> +enum switch2_subcmd_led {
> +	NS2_SUBCMD_LED_P1 = 0x1,
> +	NS2_SUBCMD_LED_P2 = 0x2,
> +	NS2_SUBCMD_LED_P3 = 0x3,
> +	NS2_SUBCMD_LED_P4 = 0x4,
> +	NS2_SUBCMD_LED_ALL_ON = 0x5,
> +	NS2_SUBCMD_LED_ALL_OFF = 0x6,
> +	NS2_SUBCMD_LED_PATTERN = 0x7,
> +	NS2_SUBCMD_LED_BLINK = 0x8,
> +};
> +
> +enum switch2_subcmd_fw_info {
> +	NS2_SUBCMD_FW_INFO_GET = 0x1,
> +};
> +
> +enum switch2_ctlr_type {
> +	NS2_CTLR_TYPE_JCL = 0x00,
> +	NS2_CTLR_TYPE_JCR = 0x01,
> +	NS2_CTLR_TYPE_PRO = 0x02,
> +	NS2_CTLR_TYPE_GC = 0x03,
> +};
> +
> +enum switch2_report_id {
> +	NS2_REPORT_UNIFIED = 0x05,
> +	NS2_REPORT_JCL = 0x07,
> +	NS2_REPORT_JCR = 0x08,
> +	NS2_REPORT_PRO = 0x09,
> +	NS2_REPORT_GC = 0x0a,
> +};
> +
> +enum switch2_init_step {
> +	NS2_INIT_READ_SERIAL,
> +	NS2_INIT_GET_FIRMWARE_INFO,
> +	NS2_INIT_READ_FACTORY_PRIMARY_CALIB,
> +	NS2_INIT_READ_FACTORY_SECONDARY_CALIB,
> +	NS2_INIT_READ_FACTORY_TRIGGER_CALIB,
> +	NS2_INIT_READ_USER_PRIMARY_CALIB,
> +	NS2_INIT_READ_USER_SECONDARY_CALIB,
> +	NS2_INIT_SET_FEATURE_MASK,
> +	NS2_INIT_ENABLE_FEATURES,
> +	NS2_INIT_GRIP_BUTTONS,
> +	NS2_INIT_REPORT_FORMAT,
> +	NS2_INIT_SET_PLAYER_LEDS,
> +	NS2_INIT_INPUT,
> +	NS2_INIT_FINISH,
> +	NS2_INIT_DONE,
> +};
> +
> +struct switch2_version_info {
> +	uint8_t major;
> +	uint8_t minor;
> +	uint8_t patch;
> +	uint8_t ctlr_type;
> +	__le32 unk;
> +	int8_t dsp_major;
> +	int8_t dsp_minor;
> +	int8_t dsp_patch;
> +	int8_t dsp_type;
> +};
> +
> +struct switch2_axis_calibration {
> +	uint16_t neutral;
> +	uint16_t negative;
> +	uint16_t positive;
> +};
> +
> +struct switch2_stick_calibration {
> +	struct switch2_axis_calibration x;
> +	struct switch2_axis_calibration y;
> +};
> +
> +struct switch2_controller {
> +	struct hid_device *hdev;
> +	struct switch2_cfg_intf *cfg;
> +
> +	char name[64];
> +	char phys[64];
> +	struct list_head entry;
> +	struct mutex lock;
> +
> +	enum switch2_ctlr_type ctlr_type;
> +	enum switch2_init_step init_step;
> +	struct input_dev __rcu *input;
> +	char serial[NS2_FLASH_SIZE_SERIAL + 1];
> +	struct switch2_version_info version;
> +
> +	struct switch2_stick_calibration stick_calib[2];
> +	uint8_t lt_zero;
> +	uint8_t rt_zero;
> +
> +	uint32_t player_id;
> +	struct led_classdev leds[4];
> +};
> +
> +static DEFINE_MUTEX(switch2_controllers_lock);
> +static LIST_HEAD(switch2_controllers);
> +
> +struct switch2_ctlr_button_mapping {
> +	uint32_t code;
> +	int byte;
> +	uint32_t bit;
> +};
> +
> +static const struct switch2_ctlr_button_mapping ns2_left_joycon_button_mappings[] = {
> +	{ BTN_DPAD_LEFT,	0, NS2_BTNL_LEFT,	},
> +	{ BTN_DPAD_UP,		0, NS2_BTNL_UP,		},
> +	{ BTN_DPAD_DOWN,	0, NS2_BTNL_DOWN,	},
> +	{ BTN_DPAD_RIGHT,	0, NS2_BTNL_RIGHT,	},
> +	{ BTN_TL,		0, NS2_BTNL_L,		},
> +	{ BTN_TL2,		0, NS2_BTNL_ZL,		},
> +	{ BTN_SELECT,		0, NS2_BTNL_MINUS,	},
> +	{ BTN_THUMBL,		0, NS2_BTNL_LS,		},
> +	{ KEY_RECORD,		1, NS2_BTN_JCL_CAPTURE,	},
> +	{ BTN_GRIPR,		1, NS2_BTN_JCL_SL,	},
> +	{ BTN_GRIPR2,		1, NS2_BTN_JCL_SR,	},
> +	{ BTN_GRIPL,		1, NS2_BTN_JCL_GL,	},
> +	{ /* sentinel */ },
> +};
> +
> +static const struct switch2_ctlr_button_mapping ns2_right_joycon_button_mappings[] = {
> +	{ BTN_SOUTH,	0, NS2_BTNR_A,		},
> +	{ BTN_EAST,	0, NS2_BTNR_B,		},
> +	{ BTN_NORTH,	0, NS2_BTNR_X,		},
> +	{ BTN_WEST,	0, NS2_BTNR_Y,		},
> +	{ BTN_TR,	0, NS2_BTNR_R,		},
> +	{ BTN_TR2,	0, NS2_BTNR_ZR,		},
> +	{ BTN_START,	0, NS2_BTNR_PLUS,	},
> +	{ BTN_THUMBR,	0, NS2_BTNR_RS,		},
> +	{ BTN_C,	1, NS2_BTN_JCR_C,	},
> +	{ BTN_MODE,	1, NS2_BTN_JCR_HOME,	},
> +	{ BTN_GRIPL2,	1, NS2_BTN_JCR_SL,	},
> +	{ BTN_GRIPL,	1, NS2_BTN_JCR_SR,	},
> +	{ BTN_GRIPR,	1, NS2_BTN_JCR_GR,	},
> +	{ /* sentinel */ },
> +};
> +
> +static const struct switch2_ctlr_button_mapping ns2_procon_mappings[] = {
> +	{ BTN_SOUTH,	0, NS2_BTNR_A,		},
> +	{ BTN_EAST,	0, NS2_BTNR_B,		},
> +	{ BTN_NORTH,	0, NS2_BTNR_X,		},
> +	{ BTN_WEST,	0, NS2_BTNR_Y,		},
> +	{ BTN_TL,	1, NS2_BTNL_L,		},
> +	{ BTN_TR,	0, NS2_BTNR_R,		},
> +	{ BTN_TL2,	1, NS2_BTNL_ZL,		},
> +	{ BTN_TR2,	0, NS2_BTNR_ZR,		},
> +	{ BTN_SELECT,	1, NS2_BTNL_MINUS,	},
> +	{ BTN_START,	0, NS2_BTNR_PLUS,	},
> +	{ BTN_THUMBL,	1, NS2_BTNL_LS,		},
> +	{ BTN_THUMBR,	0, NS2_BTNR_RS,		},
> +	{ BTN_MODE,	2, NS2_BTN_PRO_HOME	},
> +	{ KEY_RECORD,	2, NS2_BTN_PRO_CAPTURE	},
> +	{ BTN_GRIPR,	2, NS2_BTN_PRO_GR	},
> +	{ BTN_GRIPL,	2, NS2_BTN_PRO_GL	},
> +	{ BTN_C,	2, NS2_BTN_PRO_C	},
> +	{ /* sentinel */ },
> +};
> +
> +static const struct switch2_ctlr_button_mapping ns2_gccon_mappings[] = {
> +	{ BTN_SOUTH,	0, NS2_BTNR_A,		},
> +	{ BTN_EAST,	0, NS2_BTNR_B,		},
> +	{ BTN_NORTH,	0, NS2_BTNR_X,		},
> +	{ BTN_WEST,	0, NS2_BTNR_Y,		},
> +	{ BTN_TL2,	1, NS2_BTNL_L,		},
> +	{ BTN_TR2,	0, NS2_BTNR_R,		},
> +	{ BTN_TL,	1, NS2_BTNL_ZL,		},
> +	{ BTN_TR,	0, NS2_BTNR_ZR,		},
> +	{ BTN_SELECT,	1, NS2_BTNL_MINUS,	},
> +	{ BTN_START,	0, NS2_BTNR_PLUS,	},
> +	{ BTN_MODE,	2, NS2_BTN_GC_HOME	},
> +	{ KEY_RECORD,	2, NS2_BTN_GC_CAPTURE	},
> +	{ BTN_C,	2, NS2_BTN_GC_C		},
> +	{ /* sentinel */ },
> +};
> +
> +static const uint8_t switch2_init_cmd_data[] = {
> +	/*
> +	 * The last 6 bytes of this packet are the MAC address of
> +	 * the console, but we don't need that for USB
> +	 */
> +	0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
> +};
> +
> +static const uint8_t switch2_one_data[] = { 0x01, 0x00, 0x00, 0x00 };
> +
> +static const uint8_t switch2_feature_mask[] = {
> +	NS2_FEATURE_BUTTONS | NS2_FEATURE_ANALOG | NS2_FEATURE_IMU,
> +	0x00, 0x00, 0x00
> +};
> +
> +static void switch2_init_step_done(struct switch2_controller *ns2, enum switch2_init_step step)
> +{
> +	if (ns2->init_step != step)
> +		return;
> +
> +	ns2->init_step++;
> +}
> +
> +static inline bool switch2_ctlr_is_joycon(enum switch2_ctlr_type type)
> +{
> +	return type == NS2_CTLR_TYPE_JCL || type == NS2_CTLR_TYPE_JCR;
> +}
> +
> +static int switch2_set_leds(struct switch2_controller *ns2)
> +{
> +	int i;
> +	uint8_t message[8] = { 0 };
> +
> +	for (i = 0; i < JC_NUM_LEDS; i++)
> +		message[0] |= (!!ns2->leds[i].brightness) << i;
> +
> +	if (!ns2->cfg)
> +		return -ENOTCONN;
> +	return ns2->cfg->send_command(NS2_CMD_LED, NS2_SUBCMD_LED_PATTERN,
> +		&message, sizeof(message),
> +		ns2->cfg);
> +}
> +
> +static int switch2_player_led_brightness_set(struct led_classdev *led,
> +					    enum led_brightness brightness)
> +{
> +	struct device *dev = led->dev->parent;
> +	struct hid_device *hdev = to_hid_device(dev);
> +	struct switch2_controller *ns2 = hid_get_drvdata(hdev);
> +
> +	if (!ns2)
> +		return -ENODEV;
> +
> +	guard(mutex)(&ns2->lock);
> +	return switch2_set_leds(ns2);
> +}
> +
> +static void switch2_leds_create(struct switch2_controller *ns2)
> +{
> +	struct hid_device *hdev = ns2->hdev;
> +	struct led_classdev *led;
> +	int i;
> +	int player_led_pattern;
> +
> +	player_led_pattern = ns2->player_id % JC_NUM_LED_PATTERNS;
> +	hid_dbg(hdev, "assigned player %d led pattern", player_led_pattern + 1);
> +
> +	for (i = 0; i < JC_NUM_LEDS; i++) {
> +		led = &ns2->leds[i];
> +		led->brightness = joycon_player_led_patterns[player_led_pattern][i];
> +		led->max_brightness = 1;
> +		led->brightness_set_blocking = switch2_player_led_brightness_set;
> +		led->flags = LED_CORE_SUSPENDRESUME | LED_HW_PLUGGABLE;
> +	}
> +}
> +
> +static void switch2_config_buttons(struct input_dev *idev,
> +	const struct switch2_ctlr_button_mapping button_mappings[])
> +{
> +	const struct switch2_ctlr_button_mapping *button;
> +
> +	for (button = button_mappings; button->code; button++)
> +		input_set_capability(idev, EV_KEY, button->code);
> +}
> +
> +static int switch2_init_input(struct switch2_controller *ns2)
> +{
> +	struct input_dev *input;
> +	struct hid_device *hdev = ns2->hdev;
> +	int i;
> +	int ret;
> +
> +	switch2_init_step_done(ns2, NS2_INIT_FINISH);
> +
> +	rcu_read_lock();
> +	input = rcu_dereference(ns2->input);
> +	rcu_read_unlock();
> +
> +	if (input)
> +		return 0;
> +
> +	input = devm_input_allocate_device(&hdev->dev);
> +	if (!input)
> +		return -ENOMEM;
> +
> +	input_set_drvdata(input, ns2);
> +	input->dev.parent = &hdev->dev;
> +	input->id.bustype = hdev->bus;
> +	input->id.vendor = hdev->vendor;
> +	input->id.product = hdev->product;
> +	input->id.version = hdev->version;
> +	input->uniq = ns2->serial;
> +	input->name = ns2->name;
> +	input->phys = hdev->phys;
> +
> +	switch (ns2->ctlr_type) {
> +	case NS2_CTLR_TYPE_JCL:
> +		input_set_abs_params(input, ABS_X, NS2_AXIS_MIN, NS2_AXIS_MAX, 32, 128);
> +		input_set_abs_params(input, ABS_Y, NS2_AXIS_MIN, NS2_AXIS_MAX, 32, 128);
> +		switch2_config_buttons(input, ns2_left_joycon_button_mappings);
> +		break;
> +	case NS2_CTLR_TYPE_JCR:
> +		input_set_abs_params(input, ABS_X, NS2_AXIS_MIN, NS2_AXIS_MAX, 32, 128);
> +		input_set_abs_params(input, ABS_Y, NS2_AXIS_MIN, NS2_AXIS_MAX, 32, 128);
> +		switch2_config_buttons(input, ns2_right_joycon_button_mappings);
> +		break;
> +	case NS2_CTLR_TYPE_GC:
> +		input_set_abs_params(input, ABS_X, NS2_AXIS_MIN, NS2_AXIS_MAX, 32, 128);
> +		input_set_abs_params(input, ABS_Y, NS2_AXIS_MIN, NS2_AXIS_MAX, 32, 128);
> +		input_set_abs_params(input, ABS_RX, NS2_AXIS_MIN, NS2_AXIS_MAX, 32, 128);
> +		input_set_abs_params(input, ABS_RY, NS2_AXIS_MIN, NS2_AXIS_MAX, 32, 128);
> +		input_set_abs_params(input, ABS_Z, 0, NS2_TRIGGER_RANGE, 32, 128);
> +		input_set_abs_params(input, ABS_RZ, 0, NS2_TRIGGER_RANGE, 32, 128);
> +		input_set_abs_params(input, ABS_HAT0X, -1, 1, 0, 0);
> +		input_set_abs_params(input, ABS_HAT0Y, -1, 1, 0, 0);
> +		switch2_config_buttons(input, ns2_gccon_mappings);
> +		break;
> +	case NS2_CTLR_TYPE_PRO:
> +		input_set_abs_params(input, ABS_X, NS2_AXIS_MIN, NS2_AXIS_MAX, 32, 128);
> +		input_set_abs_params(input, ABS_Y, NS2_AXIS_MIN, NS2_AXIS_MAX, 32, 128);
> +		input_set_abs_params(input, ABS_RX, NS2_AXIS_MIN, NS2_AXIS_MAX, 32, 128);
> +		input_set_abs_params(input, ABS_RY, NS2_AXIS_MIN, NS2_AXIS_MAX, 32, 128);
> +		input_set_abs_params(input, ABS_HAT0X, -1, 1, 0, 0);
> +		input_set_abs_params(input, ABS_HAT0Y, -1, 1, 0, 0);
> +		switch2_config_buttons(input, ns2_procon_mappings);
> +		break;
> +	default:
> +		input_free_device(input);
> +		return -EINVAL;
> +	}
> +
> +	hid_info(ns2->hdev, "Firmware version %u.%u.%u (type %i)\n", ns2->version.major,
> +		ns2->version.minor, ns2->version.patch, ns2->version.ctlr_type);
> +	if (ns2->version.dsp_type >= 0)
> +		hid_info(ns2->hdev, "DSP version %u.%u.%u\n", ns2->version.dsp_major,
> +			ns2->version.dsp_minor, ns2->version.dsp_patch);
> +
> +	ret = input_register_device(input);
> +	if (ret < 0) {
> +		hid_err(ns2->hdev, "Failed to register input; ret=%d\n", ret);

According to the documentation of input_register_device, we have to call
input_free_device(input) here.

> +		return ret;
> +	}
> +
> +	for (i = 0; i < JC_NUM_LEDS; i++) {
> +		struct led_classdev *led = &ns2->leds[i];
> +		char *name = devm_kasprintf(&input->dev, GFP_KERNEL, "%s:%s:%s",
> +				      dev_name(&input->dev),
> +				      "green",
> +				      joycon_player_led_names[i]);
> +
> +		if (!name)

I assume we have to call input_unregister_device here as well, as we do
so in the error case below already.

With these comments addressed this is (for what it's worth)

Reviewed-by: Silvan Jegen <s.jegen@gmail.com>

I have bought the Nintendo Switch 2 Pro Controller and have tested
the current implementation of this particular controller using
evtest. Everything worked as expected so please free to add my Tested-by
tag below as well.

Tested-by: Silvan Jegen <s.jegen@gmail.com>

Cheers,
Silvan

> +			return -ENOMEM;
> +
> +		led->name = name;
> +		ret = devm_led_classdev_register(&input->dev, led);
> +		if (ret < 0) {
> +			dev_err(&input->dev, "Failed to register player %d LED; ret=%d\n",
> +				i + 1, ret);
> +			input_unregister_device(input);
> +			return ret;
> +		}
> +	}
> +
> +	rcu_assign_pointer(ns2->input, input);
> +	synchronize_rcu();
> +	return 0;
> +}
> +
> +static struct switch2_controller *switch2_get_controller(const char *phys)
> +{
> +	struct switch2_controller *ns2;
> +
> +	guard(mutex)(&switch2_controllers_lock);
> +	list_for_each_entry(ns2, &switch2_controllers, entry) {
> +		if (strncmp(ns2->phys, phys, sizeof(ns2->phys)) == 0)
> +			return ns2;
> +	}
> +	ns2 = kzalloc(sizeof(*ns2), GFP_KERNEL);
> +	if (!ns2)
> +		return ERR_PTR(-ENOMEM);
> +
> +	mutex_init(&ns2->lock);
> +	INIT_LIST_HEAD(&ns2->entry);
> +	list_add(&ns2->entry, &switch2_controllers);
> +	strscpy(ns2->phys, phys, sizeof(ns2->phys));
> +	return ns2;
> +}
> +
> +static void switch2_controller_put(struct switch2_controller *ns2)
> +{
> +	struct input_dev *input;
> +	bool do_free;
> +
> +	guard(mutex)(&switch2_controllers_lock);
> +	mutex_lock(&ns2->lock);
> +
> +	rcu_read_lock();
> +	input = rcu_dereference(ns2->input);
> +	rcu_read_unlock();
> +
> +	rcu_assign_pointer(ns2->input, NULL);
> +	synchronize_rcu();
> +
> +	ns2->init_step = 0;
> +	do_free = !ns2->hdev && !ns2->cfg;
> +	mutex_unlock(&ns2->lock);
> +
> +	if (input)
> +		input_unregister_device(input);
> +
> +	if (do_free) {
> +		list_del_init(&ns2->entry);
> +		mutex_destroy(&ns2->lock);
> +		kfree(ns2);
> +	}
> +}
> +
> +static bool switch2_parse_stick_calibration(struct switch2_stick_calibration *calib,
> +	const uint8_t *data)
> +{
> +	static const uint8_t UNCALIBRATED[9] = {
> +		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
> +	};
> +	if (memcmp(UNCALIBRATED, data, sizeof(UNCALIBRATED)) == 0)
> +		return false;
> +
> +	calib->x.neutral = data[0];
> +	calib->x.neutral |= (data[1] & 0x0F) << 8;
> +
> +	calib->y.neutral = data[1] >> 4;
> +	calib->y.neutral |= data[2] << 4;
> +
> +	calib->x.positive = data[3];
> +	calib->x.positive |= (data[4] & 0x0F) << 8;
> +
> +	calib->y.positive = data[4] >> 4;
> +	calib->y.positive |= data[5] << 4;
> +
> +	calib->x.negative = data[6];
> +	calib->x.negative |= (data[7] & 0x0F) << 8;
> +
> +	calib->y.negative = data[7] >> 4;
> +	calib->y.negative |= data[8] << 4;
> +
> +	return true;
> +}
> +
> +static void switch2_handle_flash_read(struct switch2_controller *ns2, uint8_t size,
> +	uint32_t address, const uint8_t *data)
> +{
> +	bool ok;
> +
> +	switch (address) {
> +	case NS2_FLASH_ADDR_SERIAL:
> +		if (size != NS2_FLASH_SIZE_SERIAL)
> +			return;
> +		memcpy(ns2->serial, data, size);
> +		switch2_init_step_done(ns2, NS2_INIT_READ_SERIAL);
> +		break;
> +	case NS2_FLASH_ADDR_FACTORY_PRIMARY_CALIB:
> +		if (size != NS2_FLASH_SIZE_FACTORY_AXIS_CALIB)
> +			return;
> +		switch2_init_step_done(ns2, NS2_INIT_READ_FACTORY_PRIMARY_CALIB);
> +		ok = switch2_parse_stick_calibration(&ns2->stick_calib[0], data);
> +		if (ok) {
> +			hid_dbg(ns2->hdev, "Got factory primary stick calibration:\n");
> +			hid_dbg(ns2->hdev, "Left max: %i, neutral: %i, right max: %i\n",
> +				ns2->stick_calib[0].x.negative,
> +				ns2->stick_calib[0].x.neutral,
> +				ns2->stick_calib[0].x.positive);
> +			hid_dbg(ns2->hdev, "Down max: %i, neutral: %i, up max: %i\n",
> +				ns2->stick_calib[0].y.negative,
> +				ns2->stick_calib[0].y.neutral,
> +				ns2->stick_calib[0].y.positive);
> +		} else {
> +			hid_dbg(ns2->hdev, "Factory primary stick calibration not present\n");
> +		}
> +		break;
> +	case NS2_FLASH_ADDR_FACTORY_SECONDARY_CALIB:
> +		if (size != NS2_FLASH_SIZE_FACTORY_AXIS_CALIB)
> +			return;
> +		switch2_init_step_done(ns2, NS2_INIT_READ_FACTORY_SECONDARY_CALIB);
> +		ok = switch2_parse_stick_calibration(&ns2->stick_calib[1], data);
> +		if (ok) {
> +			hid_dbg(ns2->hdev, "Got factory secondary stick calibration:\n");
> +			hid_dbg(ns2->hdev, "Left max: %i, neutral: %i, right max: %i\n",
> +				ns2->stick_calib[1].x.negative,
> +				ns2->stick_calib[1].x.neutral,
> +				ns2->stick_calib[1].x.positive);
> +			hid_dbg(ns2->hdev, "Down max: %i, neutral: %i, up max: %i\n",
> +				ns2->stick_calib[1].y.negative,
> +				ns2->stick_calib[1].y.neutral,
> +				ns2->stick_calib[1].y.positive);
> +		} else {
> +			hid_dbg(ns2->hdev, "Factory secondary stick calibration not present\n");
> +		}
> +		break;
> +	case NS2_FLASH_ADDR_FACTORY_TRIGGER_CALIB:
> +		if (size != NS2_FLASH_SIZE_FACTORY_TRIGGER_CALIB)
> +			return;
> +		switch2_init_step_done(ns2, NS2_INIT_READ_FACTORY_TRIGGER_CALIB);
> +		if (data[0] != 0xFF && data[1] != 0xFF) {
> +			ns2->lt_zero = data[0];
> +			ns2->rt_zero = data[1];
> +
> +			hid_dbg(ns2->hdev, "Got factory trigger calibration:\n");
> +			hid_dbg(ns2->hdev, "Left zero point: %i\n", ns2->lt_zero);
> +			hid_dbg(ns2->hdev, "Right zero point: %i\n", ns2->rt_zero);
> +		} else {
> +			hid_dbg(ns2->hdev, "Factory trigger calibration not present\n");
> +		}
> +		break;
> +	case NS2_FLASH_ADDR_USER_PRIMARY_CALIB:
> +		if (size != NS2_FLASH_SIZE_USER_AXIS_CALIB)
> +			return;
> +		switch2_init_step_done(ns2, NS2_INIT_READ_USER_PRIMARY_CALIB);
> +		if (__le16_to_cpu(*(__le16 *)data) != NS2_USER_CALIB_MAGIC) {
> +			hid_dbg(ns2->hdev, "No user primary stick calibration present\n");
> +			break;
> +		}
> +
> +		ok = switch2_parse_stick_calibration(&ns2->stick_calib[0], &data[2]);
> +		if (ok) {
> +			hid_dbg(ns2->hdev, "Got user primary stick calibration:\n");
> +			hid_dbg(ns2->hdev, "Left max: %i, neutral: %i, right max: %i\n",
> +				ns2->stick_calib[0].x.negative,
> +				ns2->stick_calib[0].x.neutral,
> +				ns2->stick_calib[0].x.positive);
> +			hid_dbg(ns2->hdev, "Down max: %i, neutral: %i, up max: %i\n",
> +				ns2->stick_calib[0].y.negative,
> +				ns2->stick_calib[0].y.neutral,
> +				ns2->stick_calib[0].y.positive);
> +		} else {
> +			hid_dbg(ns2->hdev, "No user primary stick calibration present\n");
> +		}
> +		break;
> +	case NS2_FLASH_ADDR_USER_SECONDARY_CALIB:
> +		if (size != NS2_FLASH_SIZE_USER_AXIS_CALIB)
> +			return;
> +		switch2_init_step_done(ns2, NS2_INIT_READ_USER_SECONDARY_CALIB);
> +		if (__le16_to_cpu(*(__le16 *)data) != NS2_USER_CALIB_MAGIC) {
> +			hid_dbg(ns2->hdev, "No user secondary stick calibration present\n");
> +			break;
> +		}
> +
> +		ok = switch2_parse_stick_calibration(&ns2->stick_calib[1], &data[2]);
> +		if (ok) {
> +			hid_dbg(ns2->hdev, "Got user secondary stick calibration:\n");
> +			hid_dbg(ns2->hdev, "Left max: %i, neutral: %i, right max: %i\n",
> +				ns2->stick_calib[1].x.negative,
> +				ns2->stick_calib[1].x.neutral,
> +				ns2->stick_calib[1].x.positive);
> +			hid_dbg(ns2->hdev, "Down max: %i, neutral: %i, up max: %i\n",
> +				ns2->stick_calib[1].y.negative,
> +				ns2->stick_calib[1].y.neutral,
> +				ns2->stick_calib[1].y.positive);
> +		} else {
> +			hid_dbg(ns2->hdev, "No user secondary stick calibration present\n");
> +		}
> +		break;
> +	}
> +}
> +
> +static void switch2_report_buttons(struct input_dev *input, const uint8_t *bytes,
> +	const struct switch2_ctlr_button_mapping button_mappings[])
> +{
> +	const struct switch2_ctlr_button_mapping *button;
> +
> +	for (button = button_mappings; button->code; button++)
> +		input_report_key(input, button->code, bytes[button->byte] & button->bit);
> +}
> +
> +static void switch2_report_axis(struct input_dev *input, struct switch2_axis_calibration *calib,
> +	int axis, bool invert, int value)
> +{
> +	if (calib && calib->neutral && calib->negative && calib->positive) {
> +		value -= calib->neutral;
> +		value *= NS2_AXIS_MAX + 1;
> +		if (value < 0)
> +			value /= calib->negative;
> +		else
> +			value /= calib->positive;
> +	} else {
> +		value = (value - 2048) * 16;
> +	}
> +
> +	if (invert)
> +		value = -value;
> +	input_report_abs(input, axis,
> +		clamp(value, NS2_AXIS_MIN, NS2_AXIS_MAX));
> +}
> +
> +static void switch2_report_stick(struct input_dev *input, struct switch2_stick_calibration *calib,
> +	int x, bool invert_x, int y, bool invert_y, const uint8_t *data)
> +{
> +	switch2_report_axis(input, &calib->x, x, invert_x, data[0] | ((data[1] & 0x0F) << 8));
> +	switch2_report_axis(input, &calib->y, y, invert_y, (data[1] >> 4) | (data[2] << 4));
> +}
> +
> +static void switch2_report_trigger(struct input_dev *input, uint8_t zero, int abs, uint8_t data)
> +{
> +	int value = (NS2_TRIGGER_RANGE + 1) * (data - zero) / (232 - zero);
> +
> +	input_report_abs(input, abs, clamp(value, 0, NS2_TRIGGER_RANGE));
> +}
> +
> +static int switch2_event(struct hid_device *hdev, struct hid_report *report, uint8_t *raw_data,
> +	int size)
> +{
> +	struct switch2_controller *ns2 = hid_get_drvdata(hdev);
> +	struct input_dev *input;
> +
> +	if (report->type != HID_INPUT_REPORT)
> +		return 0;
> +
> +	if (size < 15)
> +		return -EINVAL;
> +
> +	guard(rcu)();
> +	input = rcu_dereference(ns2->input);
> +
> +	if (!input)
> +		return 0;
> +
> +	switch (report->id) {
> +	case NS2_REPORT_UNIFIED:
> +		/*
> +		 * TODO
> +		 * This won't be sent unless the report type gets changed via command
> +		 * 03-0A, but we should support it at some point regardless.
> +		 */
> +		break;
> +	case NS2_REPORT_JCL:
> +		switch2_report_stick(input, &ns2->stick_calib[0], ABS_X, false,
> +			ABS_Y, true, &raw_data[6]);
> +		switch2_report_buttons(input, &raw_data[3], ns2_left_joycon_button_mappings);
> +		break;
> +	case NS2_REPORT_JCR:
> +		switch2_report_stick(input, &ns2->stick_calib[0], ABS_X, false,
> +			ABS_Y, true, &raw_data[6]);
> +		switch2_report_buttons(input, &raw_data[3], ns2_right_joycon_button_mappings);
> +		break;
> +	case NS2_REPORT_GC:
> +		input_report_abs(input, ABS_HAT0X,
> +			!!(raw_data[4] & NS2_BTNL_RIGHT) -
> +			!!(raw_data[4] & NS2_BTNL_LEFT));
> +		input_report_abs(input, ABS_HAT0Y,
> +			!!(raw_data[4] & NS2_BTNL_DOWN) -
> +			!!(raw_data[4] & NS2_BTNL_UP));
> +		switch2_report_buttons(input, &raw_data[3], ns2_gccon_mappings);
> +		switch2_report_stick(input, &ns2->stick_calib[0], ABS_X, false,
> +			ABS_Y, true, &raw_data[6]);
> +		switch2_report_stick(input, &ns2->stick_calib[1], ABS_RX, false,
> +			ABS_RY, true, &raw_data[9]);
> +		switch2_report_trigger(input, ns2->lt_zero, ABS_Z, raw_data[13]);
> +		switch2_report_trigger(input, ns2->rt_zero, ABS_RZ, raw_data[14]);
> +		break;
> +	case NS2_REPORT_PRO:
> +		input_report_abs(input, ABS_HAT0X,
> +			!!(raw_data[4] & NS2_BTNL_RIGHT) -
> +			!!(raw_data[4] & NS2_BTNL_LEFT));
> +		input_report_abs(input, ABS_HAT0Y,
> +			!!(raw_data[4] & NS2_BTNL_DOWN) -
> +			!!(raw_data[4] & NS2_BTNL_UP));
> +		switch2_report_buttons(input, &raw_data[3], ns2_procon_mappings);
> +		switch2_report_stick(input, &ns2->stick_calib[0], ABS_X, false,
> +			ABS_Y, true, &raw_data[6]);
> +		switch2_report_stick(input, &ns2->stick_calib[1], ABS_RX, false,
> +			ABS_RY, true, &raw_data[9]);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	input_sync(input);
> +	return 0;
> +}
> +
> +static int switch2_features_enable(struct switch2_controller *ns2, int features)
> +{
> +	__le32 feature_bits = __cpu_to_le32(features);
> +
> +	if (!ns2->cfg)
> +		return -ENOTCONN;
> +	return ns2->cfg->send_command(NS2_CMD_FEATSEL, NS2_SUBCMD_FEATSEL_ENABLE,
> +		&feature_bits, sizeof(feature_bits),
> +		ns2->cfg);
> +}
> +
> +static int switch2_read_flash(struct switch2_controller *ns2, uint32_t address,
> +	uint8_t size)
> +{
> +	uint8_t message[8] = { size, 0x7e };
> +
> +	if (!ns2->cfg)
> +		return -ENOTCONN;
> +	*(__le32 *)&message[4] = __cpu_to_le32(address);
> +	return ns2->cfg->send_command(NS2_CMD_FLASH, NS2_SUBCMD_FLASH_READ, message,
> +		sizeof(message), ns2->cfg);
> +}
> +
> +static int switch2_set_player_id(struct switch2_controller *ns2, uint32_t player_id)
> +{
> +	int i;
> +	int player_led_pattern = player_id % JC_NUM_LED_PATTERNS;
> +
> +	for (i = 0; i < JC_NUM_LEDS; i++)
> +		ns2->leds[i].brightness = joycon_player_led_patterns[player_led_pattern][i];
> +
> +	return switch2_set_leds(ns2);
> +}
> +
> +static int switch2_set_report_format(struct switch2_controller *ns2, enum switch2_report_id fmt)
> +{
> +	__le32 format_id = __cpu_to_le32(fmt);
> +
> +	if (!ns2->cfg)
> +		return -ENOTCONN;
> +	return ns2->cfg->send_command(NS2_CMD_INIT, NS2_SUBCMD_INIT_SELECT_REPORT,
> +		&format_id, sizeof(format_id),
> +		ns2->cfg);
> +}
> +
> +static int switch2_init_controller(struct switch2_controller *ns2)
> +{
> +	if (ns2->init_step == NS2_INIT_DONE)
> +		return 0;
> +
> +	if (!ns2->cfg)
> +		return -ENOTCONN;
> +
> +	switch (ns2->init_step) {
> +	case NS2_INIT_READ_SERIAL:
> +		return switch2_read_flash(ns2, NS2_FLASH_ADDR_SERIAL,
> +			NS2_FLASH_SIZE_SERIAL);
> +	case NS2_INIT_GET_FIRMWARE_INFO:
> +		return ns2->cfg->send_command(NS2_CMD_FW_INFO, NS2_SUBCMD_FW_INFO_GET,
> +			NULL, 0, ns2->cfg);
> +	case NS2_INIT_READ_FACTORY_PRIMARY_CALIB:
> +		return switch2_read_flash(ns2, NS2_FLASH_ADDR_FACTORY_PRIMARY_CALIB,
> +			NS2_FLASH_SIZE_FACTORY_AXIS_CALIB);
> +	case NS2_INIT_READ_FACTORY_SECONDARY_CALIB:
> +		if (switch2_ctlr_is_joycon(ns2->ctlr_type)) {
> +			switch2_init_step_done(ns2, ns2->init_step);
> +			return switch2_init_controller(ns2);
> +		}
> +		return switch2_read_flash(ns2, NS2_FLASH_ADDR_FACTORY_SECONDARY_CALIB,
> +			NS2_FLASH_SIZE_FACTORY_AXIS_CALIB);
> +	case NS2_INIT_READ_FACTORY_TRIGGER_CALIB:
> +		if (ns2->ctlr_type != NS2_CTLR_TYPE_GC) {
> +			switch2_init_step_done(ns2, ns2->init_step);
> +			return switch2_init_controller(ns2);
> +		}
> +		return switch2_read_flash(ns2, NS2_FLASH_ADDR_FACTORY_TRIGGER_CALIB,
> +			NS2_FLASH_SIZE_FACTORY_TRIGGER_CALIB);
> +	case NS2_INIT_READ_USER_PRIMARY_CALIB:
> +		return switch2_read_flash(ns2, NS2_FLASH_ADDR_USER_PRIMARY_CALIB,
> +			NS2_FLASH_SIZE_USER_AXIS_CALIB);
> +	case NS2_INIT_READ_USER_SECONDARY_CALIB:
> +		if (switch2_ctlr_is_joycon(ns2->ctlr_type)) {
> +			switch2_init_step_done(ns2, ns2->init_step);
> +			return switch2_init_controller(ns2);
> +		}
> +		return switch2_read_flash(ns2, NS2_FLASH_ADDR_USER_SECONDARY_CALIB,
> +			NS2_FLASH_SIZE_USER_AXIS_CALIB);
> +	case NS2_INIT_SET_FEATURE_MASK:
> +		return ns2->cfg->send_command(NS2_CMD_FEATSEL, NS2_SUBCMD_FEATSEL_SET_MASK,
> +			switch2_feature_mask, sizeof(switch2_feature_mask), ns2->cfg);
> +	case NS2_INIT_ENABLE_FEATURES:
> +		return switch2_features_enable(ns2, NS2_FEATURE_BUTTONS | NS2_FEATURE_ANALOG);
> +	case NS2_INIT_GRIP_BUTTONS:
> +		if (!switch2_ctlr_is_joycon(ns2->ctlr_type)) {
> +			switch2_init_step_done(ns2, ns2->init_step);
> +			return switch2_init_controller(ns2);
> +		}
> +		return ns2->cfg->send_command(NS2_CMD_GRIP, NS2_SUBCMD_GRIP_ENABLE_BUTTONS,
> +			switch2_one_data, sizeof(switch2_one_data),
> +			ns2->cfg);
> +	case NS2_INIT_REPORT_FORMAT:
> +		switch (ns2->ctlr_type) {
> +		case NS2_CTLR_TYPE_JCL:
> +			return switch2_set_report_format(ns2, NS2_REPORT_JCL);
> +		case NS2_CTLR_TYPE_JCR:
> +			return switch2_set_report_format(ns2, NS2_REPORT_JCR);
> +		case NS2_CTLR_TYPE_PRO:
> +			return switch2_set_report_format(ns2, NS2_REPORT_PRO);
> +		case NS2_CTLR_TYPE_GC:
> +			return switch2_set_report_format(ns2, NS2_REPORT_GC);
> +		default:
> +			switch2_init_step_done(ns2, ns2->init_step);
> +			return switch2_init_controller(ns2);
> +		}
> +	case NS2_INIT_SET_PLAYER_LEDS:
> +		return switch2_set_player_id(ns2, ns2->player_id);
> +	case NS2_INIT_INPUT:
> +		return ns2->cfg->send_command(NS2_CMD_INIT, NS2_SUBCMD_INIT_USB,
> +			switch2_init_cmd_data, sizeof(switch2_init_cmd_data), ns2->cfg);
> +	case NS2_INIT_FINISH:
> +		if (ns2->hdev)
> +			return switch2_init_input(ns2);
> +		break;
> +	default:
> +		WARN_ON_ONCE(1);
> +		break;
> +	}
> +	return 0;
> +}
> +
> +int switch2_receive_command(struct switch2_controller *ns2,
> +	const uint8_t *message, size_t length)
> +{
> +	const struct switch2_cmd_header *header;
> +	int ret = 0;
> +
> +	if (length < 8)
> +		return -EINVAL;
> +
> +	print_hex_dump_debug("got cmd: ", DUMP_PREFIX_OFFSET, 16, 1, message, length, false);
> +
> +	guard(mutex)(&ns2->lock);
> +
> +	header = (const struct switch2_cmd_header *)message;
> +	if (!(header->flags & NS2_FLAG_OK)) {
> +		ret = -EIO;
> +		goto exit;
> +	}
> +	message = &message[8];
> +	switch (header->command) {
> +	case NS2_CMD_FLASH:
> +		if (header->subcommand == NS2_SUBCMD_FLASH_READ) {
> +			uint8_t read_size;
> +			uint32_t read_address;
> +
> +			if (length < 16) {
> +				ret = -EINVAL;
> +				goto exit;
> +			}
> +			read_size = message[0];
> +			read_address = __le32_to_cpu(*(__le32 *)&message[4]);
> +			if (length < read_size + 16) {
> +				ret = -EINVAL;
> +				goto exit;
> +			}
> +			switch2_handle_flash_read(ns2, read_size, read_address, &message[8]);
> +		}
> +		break;
> +	case NS2_CMD_INIT:
> +		if (header->subcommand == NS2_SUBCMD_INIT_USB)
> +			switch2_init_step_done(ns2, NS2_INIT_INPUT);
> +		else if (header->subcommand == NS2_SUBCMD_INIT_SELECT_REPORT)
> +			switch2_init_step_done(ns2, NS2_INIT_REPORT_FORMAT);
> +		break;
> +	case NS2_CMD_GRIP:
> +		if (header->subcommand == NS2_SUBCMD_GRIP_ENABLE_BUTTONS)
> +			switch2_init_step_done(ns2, NS2_INIT_GRIP_BUTTONS);
> +		break;
> +	case NS2_CMD_LED:
> +		if (header->subcommand == NS2_SUBCMD_LED_PATTERN)
> +			switch2_init_step_done(ns2, NS2_INIT_SET_PLAYER_LEDS);
> +		break;
> +	case NS2_CMD_FEATSEL:
> +		if (header->subcommand == NS2_SUBCMD_FEATSEL_SET_MASK)
> +			switch2_init_step_done(ns2, NS2_INIT_SET_FEATURE_MASK);
> +		else if (header->subcommand == NS2_SUBCMD_FEATSEL_ENABLE)
> +			switch2_init_step_done(ns2, NS2_INIT_ENABLE_FEATURES);
> +		break;
> +	case NS2_CMD_FW_INFO:
> +		if (header->subcommand == NS2_SUBCMD_FW_INFO_GET) {
> +			if (length < sizeof(ns2->version)) {
> +				ret = -EINVAL;
> +				goto exit;
> +			}
> +			memcpy(&ns2->version, message, sizeof(ns2->version));
> +			ns2->ctlr_type = ns2->version.ctlr_type;
> +			switch2_init_step_done(ns2, NS2_INIT_GET_FIRMWARE_INFO);
> +		}
> +		break;
> +	default:
> +		break;
> +	}
> +
> +exit:
> +	if (ns2->init_step < NS2_INIT_DONE)
> +		switch2_init_controller(ns2);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(switch2_receive_command);
> +
> +int switch2_controller_attach_cfg(const char *phys, struct switch2_cfg_intf *cfg)
> +{
> +	struct switch2_controller *ns2 = switch2_get_controller(phys);
> +
> +	if (IS_ERR(ns2))
> +		return PTR_ERR(ns2);
> +
> +	cfg->parent = ns2;
> +
> +	guard(mutex)(&ns2->lock);
> +	WARN_ON(ns2->cfg);
> +	ns2->cfg = cfg;
> +
> +	if (ns2->hdev)
> +		return switch2_init_controller(ns2);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(switch2_controller_attach_cfg);
> +
> +void switch2_controller_detach_cfg(struct switch2_controller *ns2)
> +{
> +	mutex_lock(&ns2->lock);
> +	WARN_ON(ns2 != ns2->cfg->parent);
> +	ns2->cfg = NULL;
> +	mutex_unlock(&ns2->lock);
> +	switch2_controller_put(ns2);
> +}
> +EXPORT_SYMBOL_GPL(switch2_controller_detach_cfg);
> +
> +static int switch2_probe(struct hid_device *hdev, const struct hid_device_id *id)
> +{
> +	struct switch2_controller *ns2;
> +	struct usb_device *udev;
> +	char phys[64];
> +	int ret;
> +
> +	if (!hid_is_usb(hdev))
> +		return -ENODEV;
> +
> +	udev = hid_to_usb_dev(hdev);
> +	if (usb_make_path(udev, phys, sizeof(phys)) < 0)
> +		return -EINVAL;
> +
> +	ret = hid_parse(hdev);
> +	if (ret) {
> +		hid_err(hdev, "parse failed %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
> +	if (ret) {
> +		hid_err(hdev, "hw_start failed %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = hid_hw_open(hdev);
> +	if (ret) {
> +		hid_err(hdev, "hw_open failed %d\n", ret);
> +		goto err_stop;
> +	}
> +
> +	ns2 = switch2_get_controller(phys);
> +	if (IS_ERR(ns2)) {
> +		ret = PTR_ERR(ns2);
> +		goto err_close;
> +	}
> +
> +	guard(mutex)(&ns2->lock);
> +	WARN_ON(ns2->hdev);
> +	ns2->hdev = hdev;
> +	switch (hdev->product | (hdev->vendor << 16)) {
> +	default:
> +		strscpy(ns2->name, hdev->name, sizeof(ns2->name));
> +		break;
> +	/* Some controllers have slightly wrong names so we override them */
> +	case USB_DEVICE_ID_NINTENDO_NS2_JOYCONR | (USB_VENDOR_ID_NINTENDO << 16):
> +		/* Missing the "2" in the name */
> +		strscpy(ns2->name, "Nintendo Joy-Con 2 (R)", sizeof(ns2->name));
> +		break;
> +	case USB_DEVICE_ID_NINTENDO_NS2_GCCON | (USB_VENDOR_ID_NINTENDO << 16):
> +		/* Has "Nintendo" in the name twice */
> +		strscpy(ns2->name, "Nintendo GameCube Controller", sizeof(ns2->name));
> +		break;
> +	}
> +
> +	ns2->player_id = U32_MAX;
> +	ret = ida_alloc(&nintendo_player_id_allocator, GFP_KERNEL);
> +	if (ret < 0)
> +		hid_warn(hdev, "Failed to allocate player ID, skipping; ret=%d\n", ret);
> +	else
> +		ns2->player_id = ret;
> +
> +	switch2_leds_create(ns2);
> +
> +	hid_set_drvdata(hdev, ns2);
> +
> +	if (ns2->cfg)
> +		return switch2_init_controller(ns2);
> +
> +	return 0;
> +
> +err_close:
> +	hid_hw_close(hdev);
> +err_stop:
> +	hid_hw_stop(hdev);
> +
> +	return ret;
> +}
> +
> +static void switch2_remove(struct hid_device *hdev)
> +{
> +	struct switch2_controller *ns2 = hid_get_drvdata(hdev);
> +
> +	hid_hw_close(hdev);
> +	mutex_lock(&ns2->lock);
> +	WARN_ON(ns2->hdev != hdev);
> +	ns2->hdev = NULL;
> +	mutex_unlock(&ns2->lock);
> +	ida_free(&nintendo_player_id_allocator, ns2->player_id);
> +	switch2_controller_put(ns2);
> +	hid_hw_stop(hdev);
> +}
> +
>  static const struct hid_device_id nintendo_hid_devices[] = {
> +	/* Switch devices */
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO,
>  			 USB_DEVICE_ID_NINTENDO_PROCON) },
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO,
> @@ -2813,10 +3935,69 @@ static const struct hid_device_id nintendo_hid_devices[] = {
>  			 USB_DEVICE_ID_NINTENDO_GENCON) },
>  	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
>  			 USB_DEVICE_ID_NINTENDO_N64CON) },
> +	/* Switch 2 devices */
> +	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO,
> +			 USB_DEVICE_ID_NINTENDO_NS2_JOYCONL) },
> +	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO,
> +			 USB_DEVICE_ID_NINTENDO_NS2_JOYCONR) },
> +	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO,
> +			 USB_DEVICE_ID_NINTENDO_NS2_PROCON) },
> +	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO,
> +			 USB_DEVICE_ID_NINTENDO_NS2_GCCON) },
>  	{ }
>  };
>  MODULE_DEVICE_TABLE(hid, nintendo_hid_devices);
>  
> +static bool nintendo_is_switch2(struct hid_device *hdev)
> +{
> +	return hdev->vendor == USB_VENDOR_ID_NINTENDO &&
> +		hdev->product >= USB_DEVICE_ID_NINTENDO_NS2_JOYCONR;
> +}
> +
> +static void nintendo_hid_remove(struct hid_device *hdev)
> +{
> +	if (nintendo_is_switch2(hdev))
> +		switch2_remove(hdev);
> +	else
> +		joycon_remove(hdev);
> +}
> +
> +static int nintendo_hid_event(struct hid_device *hdev,
> +			      struct hid_report *report, u8 *raw_data, int size)
> +{
> +	if (nintendo_is_switch2(hdev))
> +		return switch2_event(hdev, report, raw_data, size);
> +	else
> +		return joycon_event(hdev, report, raw_data, size);
> +}
> +
> +static int nintendo_hid_probe(struct hid_device *hdev,
> +			    const struct hid_device_id *id)
> +{
> +	if (nintendo_is_switch2(hdev))
> +		return switch2_probe(hdev, id);
> +	else
> +		return joycon_probe(hdev, id);
> +}
> +
> +#ifdef CONFIG_PM
> +static int nintendo_hid_resume(struct hid_device *hdev)
> +{
> +	if (nintendo_is_switch2(hdev))
> +		return 0;
> +	else
> +		return joycon_resume(hdev);
> +}
> +
> +static int nintendo_hid_suspend(struct hid_device *hdev, pm_message_t message)
> +{
> +	if (nintendo_is_switch2(hdev))
> +		return 0;
> +	else
> +		return joycon_suspend(hdev, message);
> +}
> +#endif
> +
>  static struct hid_driver nintendo_hid_driver = {
>  	.name		= "nintendo",
>  	.id_table	= nintendo_hid_devices,
> @@ -2844,4 +4025,5 @@ MODULE_LICENSE("GPL");
>  MODULE_AUTHOR("Ryan McClelland <rymcclel@gmail.com>");
>  MODULE_AUTHOR("Emily Strickland <linux@emily.st>");
>  MODULE_AUTHOR("Daniel J. Ogorchock <djogorchock@gmail.com>");
> +MODULE_AUTHOR("Vicki Pfau <vi@endrift.com>");
>  MODULE_DESCRIPTION("Driver for Nintendo Switch Controllers");
> diff --git a/drivers/hid/hid-nintendo.h b/drivers/hid/hid-nintendo.h
> new file mode 100644
> index 000000000000..7aff22f30266
> --- /dev/null
> +++ b/drivers/hid/hid-nintendo.h
> @@ -0,0 +1,72 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * HID driver for Nintendo Switch 2 controllers
> + *
> + * Copyright (c) 2025 Valve Software
> + *
> + * This driver is based on the following work:
> + *   https://gist.github.com/shinyquagsire23/66f006b46c56216acbaac6c1e2279b64
> + *   https://github.com/ndeadly/switch2_controller_research
> + */
> +
> +#ifndef __HID_NINTENDO_H
> +#define __HID_NINTENDO_H
> +
> +#include <linux/bits.h>
> +
> +#define NS2_FLAG_OK	BIT(0)
> +#define NS2_FLAG_NACK	BIT(2)
> +
> +enum switch2_cmd {
> +	NS2_CMD_NFC = 0x01,
> +	NS2_CMD_FLASH = 0x02,
> +	NS2_CMD_INIT = 0x03,
> +	NS2_CMD_GRIP = 0x08,
> +	NS2_CMD_LED = 0x09,
> +	NS2_CMD_VIBRATE = 0x0a,
> +	NS2_CMD_BATTERY = 0x0b,
> +	NS2_CMD_FEATSEL = 0x0c,
> +	NS2_CMD_FW_UPD = 0x0d,
> +	NS2_CMD_FW_INFO = 0x10,
> +	NS2_CMD_BT_PAIR = 0x15,
> +};
> +
> +enum switch2_direction {
> +	NS2_DIR_IN = 0x00,
> +	NS2_DIR_OUT = 0x90,
> +};
> +
> +enum switch2_transport {
> +	NS2_TRANS_USB = 0x00,
> +	NS2_TRANS_BT = 0x01,
> +};
> +
> +struct switch2_cmd_header {
> +	uint8_t command;
> +	uint8_t flags;
> +	uint8_t transport;
> +	uint8_t subcommand;
> +	uint8_t unk1;
> +	uint8_t length;
> +	uint16_t unk2;
> +};
> +static_assert(sizeof(struct switch2_cmd_header) == 8);
> +
> +struct device;
> +struct switch2_controller;
> +struct switch2_cfg_intf {
> +	struct switch2_controller *parent;
> +	struct device *dev;
> +
> +	int (*send_command)(enum switch2_cmd command, uint8_t subcommand,
> +		const void *message, size_t length,
> +		struct switch2_cfg_intf *intf);
> +};
> +
> +int switch2_controller_attach_cfg(const char *phys, struct switch2_cfg_intf *cfg);
> +void switch2_controller_detach_cfg(struct switch2_controller *controller);
> +
> +int switch2_receive_command(struct switch2_controller *controller,
> +	const uint8_t *message, size_t length);
> +
> +#endif
> diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
> index 7755e5b454d2..868262c6ccd9 100644
> --- a/drivers/input/joystick/Kconfig
> +++ b/drivers/input/joystick/Kconfig
> @@ -422,4 +422,15 @@ config JOYSTICK_SEESAW
>  	  To compile this driver as a module, choose M here: the module will be
>  	  called adafruit-seesaw.
>  
> +config JOYSTICK_NINTENDO_SWITCH2_USB
> +	tristate "Wired Nintendo Switch 2 controller support"
> +	depends on HID_NINTENDO
> +	depends on USB
> +	help
> +	  Say Y here if you want to enable support for wired Nintendo Switch 2
> +	  controllers.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called nintendo-switch2-usb.
> +
>  endif
> diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
> index 9976f596a920..8f92900ae885 100644
> --- a/drivers/input/joystick/Makefile
> +++ b/drivers/input/joystick/Makefile
> @@ -34,6 +34,7 @@ obj-$(CONFIG_JOYSTICK_SIDEWINDER)	+= sidewinder.o
>  obj-$(CONFIG_JOYSTICK_SPACEBALL)	+= spaceball.o
>  obj-$(CONFIG_JOYSTICK_SPACEORB)		+= spaceorb.o
>  obj-$(CONFIG_JOYSTICK_STINGER)		+= stinger.o
> +obj-$(CONFIG_JOYSTICK_NINTENDO_SWITCH2_USB)	+= nintendo-switch2-usb.o
>  obj-$(CONFIG_JOYSTICK_TMDC)		+= tmdc.o
>  obj-$(CONFIG_JOYSTICK_TURBOGRAFX)	+= turbografx.o
>  obj-$(CONFIG_JOYSTICK_TWIDJOY)		+= twidjoy.o
> diff --git a/drivers/input/joystick/nintendo-switch2-usb.c b/drivers/input/joystick/nintendo-switch2-usb.c
> new file mode 100644
> index 000000000000..ebd89d852e21
> --- /dev/null
> +++ b/drivers/input/joystick/nintendo-switch2-usb.c
> @@ -0,0 +1,353 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * USB driver for Nintendo Switch 2 controllers configuration interface
> + *
> + * Copyright (c) 2025 Valve Software
> + *
> + * This driver is based on the following work:
> + *   https://gist.github.com/shinyquagsire23/66f006b46c56216acbaac6c1e2279b64
> + *   https://github.com/ndeadly/switch2_controller_research
> + */
> +
> +#include "../../hid/hid-ids.h"
> +#include "../../hid/hid-nintendo.h"
> +#include <linux/module.h>
> +#include <linux/usb/input.h>
> +
> +#define NS2_BULK_SIZE 64
> +#define NS2_IN_URBS 2
> +#define NS2_OUT_URBS 4
> +
> +static struct usb_driver switch2_usb;
> +
> +struct switch2_urb {
> +	struct urb *urb;
> +	uint8_t *data;
> +	bool active;
> +};
> +
> +struct switch2_usb {
> +	struct switch2_cfg_intf cfg;
> +	struct usb_device *udev;
> +
> +	struct switch2_urb bulk_in[NS2_IN_URBS];
> +	struct usb_anchor bulk_in_anchor;
> +	spinlock_t bulk_in_lock;
> +
> +	struct switch2_urb bulk_out[NS2_OUT_URBS];
> +	struct usb_anchor bulk_out_anchor;
> +	spinlock_t bulk_out_lock;
> +
> +	int message_in;
> +	struct work_struct message_in_work;
> +};
> +
> +static void switch2_bulk_in(struct urb *urb)
> +{
> +	struct switch2_usb *ns2_usb = urb->context;
> +	int i;
> +	bool schedule = false;
> +	unsigned long flags;
> +
> +	switch (urb->status) {
> +	case 0:
> +		schedule = true;
> +		break;
> +	case -ECONNRESET:
> +	case -ENOENT:
> +	case -ESHUTDOWN:
> +		dev_dbg(&ns2_usb->udev->dev, "shutting down input urb: %d\n", urb->status);
> +		return;
> +	default:
> +		dev_dbg(&ns2_usb->udev->dev, "unknown input urb status: %d\n", urb->status);
> +		break;
> +	}
> +
> +	spin_lock_irqsave(&ns2_usb->bulk_in_lock, flags);
> +	for (i = 0; i < NS2_IN_URBS; i++) {
> +		int err;
> +		struct switch2_urb *ns2_urb;
> +
> +		if (ns2_usb->bulk_in[i].urb == urb) {
> +			ns2_usb->message_in = i;
> +			continue;
> +		}
> +
> +		if (ns2_usb->bulk_in[i].active)
> +			continue;
> +
> +		ns2_urb = &ns2_usb->bulk_in[i];
> +		usb_anchor_urb(ns2_urb->urb, &ns2_usb->bulk_in_anchor);
> +		err = usb_submit_urb(ns2_urb->urb, GFP_ATOMIC);
> +		if (err) {
> +			usb_unanchor_urb(ns2_urb->urb);
> +			dev_dbg(&ns2_usb->udev->dev, "failed to queue input urb: %d\n", err);
> +		} else {
> +			ns2_urb->active = true;
> +		}
> +	}
> +	spin_unlock_irqrestore(&ns2_usb->bulk_in_lock, flags);
> +
> +	if (schedule)
> +		schedule_work(&ns2_usb->message_in_work);
> +}
> +
> +static void switch2_bulk_out(struct urb *urb)
> +{
> +	struct switch2_usb *ns2_usb = urb->context;
> +	int i;
> +
> +	guard(spinlock_irqsave)(&ns2_usb->bulk_out_lock);
> +
> +	switch (urb->status) {
> +	case 0:
> +		break;
> +	case -ECONNRESET:
> +	case -ENOENT:
> +	case -ESHUTDOWN:
> +		dev_dbg(&ns2_usb->udev->dev, "shutting down output urb: %d\n", urb->status);
> +		return;
> +	default:
> +		dev_dbg(&ns2_usb->udev->dev, "unknown output urb status: %d\n", urb->status);
> +		return;
> +	}
> +
> +	for (i = 0; i < NS2_OUT_URBS; i++) {
> +		if (ns2_usb->bulk_out[i].urb != urb)
> +			continue;
> +
> +		ns2_usb->bulk_out[i].active = false;
> +		break;
> +	}
> +}
> +
> +static int switch2_usb_send_cmd(enum switch2_cmd command, uint8_t subcommand,
> +	const void *message, size_t size, struct switch2_cfg_intf *cfg)
> +{
> +	struct switch2_usb *ns2_usb = (struct switch2_usb *)cfg;
> +	struct switch2_urb *urb = NULL;
> +	int i;
> +	int ret;
> +	unsigned long flags;
> +
> +	struct switch2_cmd_header header = {
> +		command, NS2_DIR_OUT | NS2_FLAG_OK, NS2_TRANS_USB, subcommand, 0, size
> +	};
> +
> +	if (WARN_ON(size > 56))
> +		return -EINVAL;
> +
> +	spin_lock_irqsave(&ns2_usb->bulk_out_lock, flags);
> +	for (i = 0; i < NS2_OUT_URBS; i++) {
> +		if (ns2_usb->bulk_out[i].active)
> +			continue;
> +
> +		urb = &ns2_usb->bulk_out[i];
> +		urb->active = true;
> +		break;
> +	}
> +	spin_unlock_irqrestore(&ns2_usb->bulk_out_lock, flags);
> +
> +	if (!urb) {
> +		dev_warn(&ns2_usb->udev->dev, "output queue full, dropping message\n");
> +		return -ENOBUFS;
> +	}
> +
> +	memcpy(urb->data, &header, sizeof(header));
> +	if (message && size)
> +		memcpy(&urb->data[8], message, size);
> +	urb->urb->transfer_buffer_length = size + sizeof(header);
> +
> +	print_hex_dump_debug("sending cmd: ", DUMP_PREFIX_OFFSET, 16, 1, urb->data,
> +		size + sizeof(header), false);
> +
> +	usb_anchor_urb(urb->urb, &ns2_usb->bulk_out_anchor);
> +	ret = usb_submit_urb(urb->urb, GFP_ATOMIC);
> +	if (ret) {
> +		if (ret != -ENODEV)
> +			dev_warn(&ns2_usb->udev->dev, "failed to submit output urb: %i", ret);
> +		urb->active = false;
> +		usb_unanchor_urb(urb->urb);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void switch2_usb_message_in_work(struct work_struct *work)
> +{
> +	struct switch2_usb *ns2_usb = container_of(work, struct switch2_usb, message_in_work);
> +	struct switch2_urb *urb;
> +	int err;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&ns2_usb->bulk_in_lock, flags);
> +	urb = &ns2_usb->bulk_in[ns2_usb->message_in];
> +	spin_unlock_irqrestore(&ns2_usb->bulk_in_lock, flags);
> +
> +	err = switch2_receive_command(ns2_usb->cfg.parent, urb->urb->transfer_buffer,
> +		urb->urb->actual_length);
> +	if (err)
> +		dev_dbg(&ns2_usb->udev->dev, "receive command failed: %d\n", err);
> +
> +	spin_lock_irqsave(&ns2_usb->bulk_in_lock, flags);
> +	urb->active = false;
> +	spin_unlock_irqrestore(&ns2_usb->bulk_in_lock, flags);
> +}
> +
> +static int switch2_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
> +{
> +	struct switch2_usb *ns2_usb;
> +	struct usb_device *udev;
> +	struct usb_endpoint_descriptor *bulk_in, *bulk_out;
> +	char phys[64];
> +	int ret;
> +	int i;
> +
> +	udev = interface_to_usbdev(intf);
> +	if (usb_make_path(udev, phys, sizeof(phys)) < 0)
> +		return -EINVAL;
> +
> +	ret = usb_find_common_endpoints(intf->cur_altsetting, &bulk_in, &bulk_out, NULL, NULL);
> +	if (ret) {
> +		dev_err(&intf->dev, "failed to find bulk EPs\n");
> +		return ret;
> +	}
> +
> +	ns2_usb = devm_kzalloc(&intf->dev, sizeof(*ns2_usb), GFP_KERNEL);
> +	if (!ns2_usb)
> +		return -ENOMEM;
> +
> +	ns2_usb->udev = udev;
> +	for (i = 0; i < NS2_IN_URBS; i++) {
> +		ns2_usb->bulk_in[i].urb = usb_alloc_urb(0, GFP_KERNEL);
> +		if (!ns2_usb->bulk_in[i].urb) {
> +			ret = -ENOMEM;
> +			goto err_free_in;
> +		}
> +
> +		ns2_usb->bulk_in[i].data = usb_alloc_coherent(udev, NS2_BULK_SIZE, GFP_KERNEL,
> +			&ns2_usb->bulk_in[i].urb->transfer_dma);
> +		if (!ns2_usb->bulk_in[i].data) {
> +			ret = -ENOMEM;
> +			goto err_free_in;
> +		}
> +
> +		usb_fill_bulk_urb(ns2_usb->bulk_in[i].urb, udev,
> +			usb_rcvbulkpipe(udev, bulk_in->bEndpointAddress),
> +			ns2_usb->bulk_in[i].data, NS2_BULK_SIZE, switch2_bulk_in, ns2_usb);
> +		ns2_usb->bulk_in[i].urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
> +	}
> +
> +	for (i = 0; i < NS2_OUT_URBS; i++) {
> +		ns2_usb->bulk_out[i].urb = usb_alloc_urb(0, GFP_KERNEL);
> +		if (!ns2_usb->bulk_out[i].urb) {
> +			ret = -ENOMEM;
> +			goto err_free_out;
> +		}
> +
> +		ns2_usb->bulk_out[i].data = usb_alloc_coherent(udev, NS2_BULK_SIZE, GFP_KERNEL,
> +			&ns2_usb->bulk_out[i].urb->transfer_dma);
> +		if (!ns2_usb->bulk_out[i].data) {
> +			ret = -ENOMEM;
> +			goto err_free_out;
> +		}
> +
> +		usb_fill_bulk_urb(ns2_usb->bulk_out[i].urb, udev,
> +			usb_sndbulkpipe(udev, bulk_out->bEndpointAddress),
> +			ns2_usb->bulk_out[i].data, NS2_BULK_SIZE, switch2_bulk_out, ns2_usb);
> +		ns2_usb->bulk_out[i].urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
> +	}
> +
> +	ns2_usb->bulk_in[0].active = true;
> +	ret = usb_submit_urb(ns2_usb->bulk_in[0].urb, GFP_ATOMIC);
> +	if (ret < 0)
> +		goto err_free_out;
> +
> +	init_usb_anchor(&ns2_usb->bulk_out_anchor);
> +	spin_lock_init(&ns2_usb->bulk_out_lock);
> +	init_usb_anchor(&ns2_usb->bulk_in_anchor);
> +	spin_lock_init(&ns2_usb->bulk_in_lock);
> +	INIT_WORK(&ns2_usb->message_in_work, switch2_usb_message_in_work);
> +
> +	usb_set_intfdata(intf, ns2_usb);
> +
> +	ns2_usb->cfg.dev = &ns2_usb->udev->dev;
> +	ns2_usb->cfg.send_command = switch2_usb_send_cmd;
> +
> +	ret = switch2_controller_attach_cfg(phys, &ns2_usb->cfg);
> +	if (ret < 0)
> +		goto err_kill_urb;
> +
> +	return 0;
> +
> +err_kill_urb:
> +	usb_kill_urb(ns2_usb->bulk_in[0].urb);
> +err_free_out:
> +	for (i = 0; i < NS2_OUT_URBS; i++) {
> +		usb_free_coherent(ns2_usb->udev, NS2_BULK_SIZE, ns2_usb->bulk_out[i].data,
> +			ns2_usb->bulk_out[i].urb->transfer_dma);
> +		usb_free_urb(ns2_usb->bulk_out[i].urb);
> +	}
> +err_free_in:
> +	for (i = 0; i < NS2_IN_URBS; i++) {
> +		usb_free_coherent(ns2_usb->udev, NS2_BULK_SIZE, ns2_usb->bulk_in[i].data,
> +			ns2_usb->bulk_in[i].urb->transfer_dma);
> +		usb_free_urb(ns2_usb->bulk_in[i].urb);
> +	}
> +	devm_kfree(&intf->dev, ns2_usb);
> +
> +	return ret;
> +}
> +
> +static void switch2_usb_disconnect(struct usb_interface *intf)
> +{
> +	struct switch2_usb *ns2_usb = usb_get_intfdata(intf);
> +	unsigned long flags;
> +	int i;
> +
> +	spin_lock_irqsave(&ns2_usb->bulk_out_lock, flags);
> +	usb_kill_anchored_urbs(&ns2_usb->bulk_out_anchor);
> +	for (i = 0; i < NS2_OUT_URBS; i++) {
> +		usb_free_coherent(ns2_usb->udev, NS2_BULK_SIZE, ns2_usb->bulk_out[i].data,
> +			ns2_usb->bulk_out[i].urb->transfer_dma);
> +		usb_free_urb(ns2_usb->bulk_out[i].urb);
> +	}
> +	spin_unlock_irqrestore(&ns2_usb->bulk_out_lock, flags);
> +
> +	spin_lock_irqsave(&ns2_usb->bulk_in_lock, flags);
> +	usb_kill_anchored_urbs(&ns2_usb->bulk_in_anchor);
> +	cancel_work_sync(&ns2_usb->message_in_work);
> +	for (i = 0; i < NS2_IN_URBS; i++) {
> +		usb_free_coherent(ns2_usb->udev, NS2_BULK_SIZE, ns2_usb->bulk_in[i].data,
> +			ns2_usb->bulk_in[i].urb->transfer_dma);
> +		usb_free_urb(ns2_usb->bulk_in[i].urb);
> +	}
> +	spin_unlock_irqrestore(&ns2_usb->bulk_in_lock, flags);
> +
> +	switch2_controller_detach_cfg(ns2_usb->cfg.parent);
> +}
> +
> +#define SWITCH2_CONTROLLER(vend, prod) \
> +	USB_DEVICE_AND_INTERFACE_INFO(vend, prod, USB_CLASS_VENDOR_SPEC, 0, 0)
> +
> +static const struct usb_device_id switch2_usb_devices[] = {
> +	{ SWITCH2_CONTROLLER(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_NS2_JOYCONL) },
> +	{ SWITCH2_CONTROLLER(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_NS2_JOYCONR) },
> +	{ SWITCH2_CONTROLLER(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_NS2_PROCON) },
> +	{ SWITCH2_CONTROLLER(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_NS2_GCCON) },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(usb, switch2_usb_devices);
> +
> +static struct usb_driver switch2_usb = {
> +	.name		= "switch2",
> +	.id_table	= switch2_usb_devices,
> +	.probe		= switch2_usb_probe,
> +	.disconnect	= switch2_usb_disconnect,
> +};
> +module_usb_driver(switch2_usb);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Vicki Pfau <vi@endrift.com>");
> +MODULE_DESCRIPTION("Driver for Nintendo Switch 2 Controllers");



^ permalink raw reply

* Re: [PATCH 4/9] dt-bindings: iio: adc: mt6359: Add MT6365 PMIC AuxADC
From: Rob Herring (Arm) @ 2026-05-06 15:32 UTC (permalink / raw)
  To: Louis-Alexis Eyraud
  Cc: AngeloGioacchino Del Regno, Dmitry Torokhov, Nuno Sá, kernel,
	linux-kernel, devicetree, Chen Zhong, Krzysztof Kozlowski,
	linux-arm-kernel, Sen Chu, David Lechner, Jonathan Cameron,
	Matthias Brugger, linux-iio, linux-input, Lee Jones,
	linux-mediatek, Sean Wang, Andy Shevchenko, linux-pm,
	Conor Dooley, Macpaul Lin
In-Reply-To: <20260429-mediatek-genio-mt6365-cleanup-v1-4-6f43838be92f@collabora.com>


On Wed, 29 Apr 2026 11:44:17 +0200, Louis-Alexis Eyraud wrote:
> Add compatible string for the AuxADC block found on the MT6365 PMIC,
> that is compatible with the one found in MT6359.
> 
> Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
> ---
>  .../bindings/iio/adc/mediatek,mt6359-auxadc.yaml        | 17 +++++++++++------
>  1 file changed, 11 insertions(+), 6 deletions(-)
> 

Acked-by: Rob Herring (Arm) <robh@kernel.org>


^ permalink raw reply

* Re: [PATCH 3/9] dt-bindings: input: mediatek,pmic-keys: Add MT6365 support
From: Rob Herring (Arm) @ 2026-05-06 15:32 UTC (permalink / raw)
  To: Louis-Alexis Eyraud
  Cc: linux-iio, Macpaul Lin, Sean Wang, Jonathan Cameron, kernel,
	Lee Jones, Dmitry Torokhov, Conor Dooley, linux-kernel,
	linux-input, devicetree, Krzysztof Kozlowski, linux-pm,
	Nuno Sá, David Lechner, Matthias Brugger,
	AngeloGioacchino Del Regno, linux-arm-kernel, Sen Chu,
	linux-mediatek, Andy Shevchenko, Chen Zhong
In-Reply-To: <20260429-mediatek-genio-mt6365-cleanup-v1-3-6f43838be92f@collabora.com>


On Wed, 29 Apr 2026 11:44:16 +0200, Louis-Alexis Eyraud wrote:
> Add compatible string for the pmic keys block found on the MT6365 PMIC,
> that is compatible with the one found in MT6359.
> 
> Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
> ---
>  .../bindings/input/mediatek,pmic-keys.yaml          | 21 +++++++++++++--------
>  1 file changed, 13 insertions(+), 8 deletions(-)
> 

Acked-by: Rob Herring (Arm) <robh@kernel.org>


^ permalink raw reply

* Re: [PATCH 2/9] dt-bindings: mfd: mediatek: mt6397: Add MT6365 PMIC support
From: Rob Herring (Arm) @ 2026-05-06 15:32 UTC (permalink / raw)
  To: Louis-Alexis Eyraud
  Cc: AngeloGioacchino Del Regno, Dmitry Torokhov, Krzysztof Kozlowski,
	kernel, Andy Shevchenko, Conor Dooley, linux-kernel,
	Matthias Brugger, Nuno Sá, David Lechner, Macpaul Lin,
	linux-iio, linux-pm, Jonathan Cameron, Sean Wang, Sen Chu,
	linux-mediatek, linux-arm-kernel, Lee Jones, linux-input,
	Chen Zhong, devicetree
In-Reply-To: <20260429-mediatek-genio-mt6365-cleanup-v1-2-6f43838be92f@collabora.com>


On Wed, 29 Apr 2026 11:44:15 +0200, Louis-Alexis Eyraud wrote:
> MT6365 PMIC is compatible with MT6359, so add the compatible strings
> for the main and sub devices (regulator, rtc, audio codec).
> 
> Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
> ---
>  Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml | 13 +++++++++++++
>  1 file changed, 13 insertions(+)
> 

Acked-by: Rob Herring (Arm) <robh@kernel.org>


^ permalink raw reply

* Re: [PATCH 1/9] dt-bindings: mfd: mediatek: mt6397: Add rtc for MT6359
From: Rob Herring (Arm) @ 2026-05-06 15:31 UTC (permalink / raw)
  To: Louis-Alexis Eyraud
  Cc: Sen Chu, Macpaul Lin, Sean Wang, Lee Jones, linux-mediatek,
	Jonathan Cameron, devicetree, kernel, Nuno Sá, linux-input,
	Conor Dooley, David Lechner, AngeloGioacchino Del Regno,
	linux-arm-kernel, Chen Zhong, linux-pm, Matthias Brugger,
	Dmitry Torokhov, linux-iio, Krzysztof Kozlowski, Andy Shevchenko,
	linux-kernel
In-Reply-To: <20260429-mediatek-genio-mt6365-cleanup-v1-1-6f43838be92f@collabora.com>


On Wed, 29 Apr 2026 11:44:14 +0200, Louis-Alexis Eyraud wrote:
> The rtc block of MT6359 PMIC is compatible with the one found in MT6358
> but this compatibility was never expressed in the dt-bindings, so add
> the missing compatible string for the rtc subnode.
> 
> Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
> ---
>  Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml | 1 +
>  1 file changed, 1 insertion(+)
> 

Acked-by: Rob Herring (Arm) <robh@kernel.org>


^ permalink raw reply

* Re: [PATCH] HID: mcp2221: Fix heap buffer overflow in mcp2221_raw_event()
From: Benoît Sevens @ 2026-05-06 14:40 UTC (permalink / raw)
  To: Rishi Gupta, Jiri Kosina, Benjamin Tissoires
  Cc: linux-i2c, linux-input, linux-kernel
In-Reply-To: <20260415114752.1181079-1-bsevens@google.com>

Hi,

Just checking in to see if anyone had a chance to look at this patch,
or if there is any feedback I can address.

Thanks!

^ permalink raw reply

* Re: [PATCH v3 0/4] HID: Proper fix for OOM in hid-core
From: Lee Jones @ 2026-05-06  9:16 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Jiri Kosina, Filipe Laíns, Bastien Nocera, Ping Cheng,
	Jason Gerecke, Viresh Kumar, Johan Hovold, Alex Elder,
	Greg Kroah-Hartman, Icenowy Zheng, linux-input, linux-kernel,
	greybus-dev, linux-staging, linux-usb, stable
In-Reply-To: <20260504-wip-fix-core-v3-0-ce1f11f4968f@kernel.org>

On Mon, 04 May 2026, Benjamin Tissoires wrote:

> Commit 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing
> bogus memset()") enforced the provided data to be at least the size of
> the declared buffer in the report descriptor to prevent a buffer
> overflow.
> 
> We only had corner cases of malicious devices exposing the OOM because
> in most cases, the buffer provided by the transport layer needs to be
> allocated at probe time and is large enough to handle all the possible
> reports.
> 
> However, the patch from above, which enforces the spec a little bit more
> introduced both regressions for devices not following the spec (not
> necesserally malicious), but also a stream of errors for those devices.
> 
> Let's revert to the old behavior by giving more information to HID core
> to be able to decide whether it can or not memset the rest of the buffer
> to 0 and continue the processing.
> 
> Note that the first commit makes an API change, but the callers are
> relatively limited, so it should be fine on its own. The second patch
> can't really make the same kind of API change because we have too many
> callers in various subsystems. We can switch them one by one to the safe
> approach when needed.
> 
> The last 2 patches are small cleanups I initially put together with the
> 2 first patches, but they can be applied on their own and don't need to
> be pulled in stable like the first 2.
> 
> Cheers,
> Benjamin
> 
> Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
> ---
> Changes in v3:
> - fixed ghib -> ghid in greybus
> - fixed i386 size_t debug size reported by kernel-bot
> - Link to v2: https://lore.kernel.org/r/20260416-wip-fix-core-v2-0-be92570e5627@kernel.org
> 
> Changes in v2:
> - added a small blurb explaining the difference between the safe and the
>   non safe version of hid_safe_input_report
> - Link to v1: https://lore.kernel.org/r/20260415-wip-fix-core-v1-0-ed3c4c823175@kernel.org
> 
> ---
> Benjamin Tissoires (4):
>       HID: pass the buffer size to hid_report_raw_event
>       HID: core: introduce hid_safe_input_report()
>       HID: multitouch: use __free(kfree) to clean up temporary buffers
>       HID: wacom: use __free(kfree) to clean up temporary buffers
> 
>  drivers/hid/bpf/hid_bpf_dispatch.c |  6 ++--
>  drivers/hid/hid-core.c             | 67 ++++++++++++++++++++++++++++++--------
>  drivers/hid/hid-gfrm.c             |  4 +--
>  drivers/hid/hid-logitech-hidpp.c   |  2 +-
>  drivers/hid/hid-multitouch.c       | 18 ++++------
>  drivers/hid/hid-primax.c           |  2 +-
>  drivers/hid/hid-vivaldi-common.c   |  2 +-
>  drivers/hid/i2c-hid/i2c-hid-core.c |  7 ++--
>  drivers/hid/usbhid/hid-core.c      | 11 ++++---
>  drivers/hid/wacom_sys.c            | 46 +++++++++-----------------
>  drivers/staging/greybus/hid.c      |  2 +-
>  include/linux/hid.h                |  6 ++--
>  include/linux/hid_bpf.h            | 14 +++++---
>  13 files changed, 109 insertions(+), 78 deletions(-)

What's the plan for this set Benjamin? -rcs or -next?

-- 
Lee Jones

^ permalink raw reply

* Re: [PATCH v3 1/2] dt-bindings: input: Document Imagis ISA1200 haptic motor driver
From: Linus Walleij @ 2026-05-06  8:01 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Svyatoslav Ryhel, Dmitry Torokhov, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, linux-input, devicetree,
	linux-kernel
In-Reply-To: <20260506-nautilus-of-abstract-efficiency-eebe94@quoll>

On Wed, May 6, 2026 at 9:39 AM Krzysztof Kozlowski <krzk@kernel.org> wrote:
> On Sun, May 03, 2026 at 07:52:42PM +0300, Svyatoslav Ryhel wrote:
> > Document the Imagis ISA1200 haptic motor driver, used primarily in mobile
> > handheld devices and capable of supporting up to two motors.
> >
> > The exact datasheet for the ISA1200 is not available; all data was modeled
> > based on available downstream kernel sources for various devices and
> > fragments of information scattered across the internet.
> >
> > Tested-by: Linus Walleij <linusw@kernel.org> # Samsung GT-I9070 Janice
>
> Drop the tag here, you cannot test a binding. It's not possible or
> otherwise explain how YAML file was tested by this device.

Right, I was probably sloppy and replied Tested-by on the cover
letter.

Yours,
Linus Walleij

^ permalink raw reply

* Re: [PATCH v3 1/2] dt-bindings: input: Document Imagis ISA1200 haptic motor driver
From: Krzysztof Kozlowski @ 2026-05-06  7:39 UTC (permalink / raw)
  To: Svyatoslav Ryhel
  Cc: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Linus Walleij, linux-input, devicetree, linux-kernel
In-Reply-To: <20260503165243.215979-2-clamor95@gmail.com>

On Sun, May 03, 2026 at 07:52:42PM +0300, Svyatoslav Ryhel wrote:
> Document the Imagis ISA1200 haptic motor driver, used primarily in mobile
> handheld devices and capable of supporting up to two motors.
> 
> The exact datasheet for the ISA1200 is not available; all data was modeled
> based on available downstream kernel sources for various devices and
> fragments of information scattered across the internet.
> 
> Tested-by: Linus Walleij <linusw@kernel.org> # Samsung GT-I9070 Janice

Drop the tag here, you cannot test a binding. It's not possible or
otherwise explain how YAML file was tested by this device.

Best regards,
Krzysztof


^ permalink raw reply

* RE: [PATCH v2 0/4] iio: introduce devm_ API for hid sensro setup and cleanup
From: Zhang, Lixu @ 2026-05-06  1:51 UTC (permalink / raw)
  To: Jonathan Cameron, srinivas pandruvada
  Cc: Sanjay Chitroda, jikos@kernel.org, Lechner, David,
	nuno.sa@analog.com, andy@kernel.org, sakari.ailus@linux.intel.com,
	linux-input@vger.kernel.org, linux-iio@vger.kernel.org,
	linux-kernel@vger.kernel.org
In-Reply-To: <20260505173302.4284a077@jic23-huawei>

>-----Original Message-----
>From: Jonathan Cameron <jic23@kernel.org>
>Sent: Wednesday, May 6, 2026 12:33 AM
>To: srinivas pandruvada <srinivas.pandruvada@linux.intel.com>
>Cc: Sanjay Chitroda <sanjayembeddedse@gmail.com>; jikos@kernel.org;
>Lechner, David <dlechner@baylibre.com>; nuno.sa@analog.com;
>andy@kernel.org; sakari.ailus@linux.intel.com; linux-input@vger.kernel.org;
>linux-iio@vger.kernel.org; linux-kernel@vger.kernel.org; Zhang, Lixu
><lixu.zhang@intel.com>
>Subject: Re: [PATCH v2 0/4] iio: introduce devm_ API for hid sensro setup and
>cleanup
>
>On Fri, 01 May 2026 04:53:38 -0700
>srinivas pandruvada <srinivas.pandruvada@linux.intel.com> wrote:
>
>> + Lixu
>>
>> On Wed, 2026-04-29 at 23:29 +0530, Sanjay Chitroda wrote:
>> > From: Sanjay Chitroda <sanjayembeddedse@gmail.com>
>> >
>> > Key highlights:
>> > - Prepare change as pre-requisite for devm conversion for HID IIO
>> >   drivers by removing redundant argument
>> > - Add devm API to setup trigger and clenaup resource using
>> >   devm_add_action_or_reset()
>> > - few sample driver update using devm conversion to auto release
>> > resource
>> >
>>
>> devm_* calls are fine but needs tests particularly when forces ISH PCI
>> drivers unbind, while iio-sensor-proxy has open sessions.
>>
>> Lixu, Please check.
>Hold off perhaps until we have a v3 with fixes for the existing devm calls against
>wrong struct device.

Sure, I'll test the ISH unbind scenario once v3 is out.

Thanks,
Lixu

>
>Thanks,
>
>Jonathan
>
>>
>> Thanks,
>> Srinivas
>>


^ permalink raw reply

* Re: [PATCH v4 00/11] Input: support for STM FTS5
From: Dmitry Torokhov @ 2026-05-05 21:54 UTC (permalink / raw)
  To: david
  Cc: Maxime Coquelin, Alexandre Torgue, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Henrik Rydberg,
	Bjorn Andersson, Konrad Dybcio, Petr Hodina, linux-input,
	linux-stm32, linux-arm-kernel, linux-kernel, Krzysztof Kozlowski,
	devicetree, linux-arm-msm, phone-devel, Konrad Dybcio
In-Reply-To: <20260409-stmfts5-v4-0-64fe62027db5@ixit.cz>

On Thu, Apr 09, 2026 at 12:15:43AM +0200, David Heidelberg via B4 Relay wrote:
> Used on various phones. Minimal viable driver.
> 
> Includes device-tree enabling touchscreen on Pixel 3.
> 
> What is missing:
>  - switching between AP and SLPI mode (to be able to wake up phone by touch)
>  - firmware loading
>  - anything above basic touch
> 
> Signed-off-by: David Heidelberg <david@ixit.cz>

Applied #1 through #8 (#5 with minor edits). 

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH v5 4/9] dt-bindings: pinctrl: mediatek,mt65xx: Add MT6392 pinctrl
From: Rob Herring (Arm) @ 2026-05-05 19:41 UTC (permalink / raw)
  To: Luca Leonardo Scorcia
  Cc: Sen Chu, Matthias Brugger, Mark Brown, Gary Bisson,
	AngeloGioacchino Del Regno, Lee Jones, linux-mediatek,
	linux-kernel, linux-input, Louis-Alexis Eyraud, linux-arm-kernel,
	linux-gpio, devicetree, Julien Massot, Fabien Parent, Macpaul Lin,
	Val Packett, Liam Girdwood, Linus Walleij, linux-pm, Sean Wang,
	Dmitry Torokhov, Conor Dooley, Akari Tsuyukusa, Chen Zhong,
	Krzysztof Kozlowski
In-Reply-To: <20260420213529.1645560-5-l.scorcia@gmail.com>


On Mon, 20 Apr 2026 22:30:03 +0100, Luca Leonardo Scorcia wrote:
> Add a compatible for the pinctrl device of the MT6392 PMIC, a variant of
> the already supported MT6397.
> 
> Signed-off-by: Luca Leonardo Scorcia <l.scorcia@gmail.com>
> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
> ---
>  .../pinctrl/mediatek,mt65xx-pinctrl.yaml      |  1 +
>  .../pinctrl/mediatek,mt6392-pinfunc.h         | 39 +++++++++++++++++++
>  2 files changed, 40 insertions(+)
>  create mode 100644 include/dt-bindings/pinctrl/mediatek,mt6392-pinfunc.h
> 

Acked-by: Rob Herring (Arm) <robh@kernel.org>


^ permalink raw reply

* Re: [PATCH v3] Input: ads7846 - don't use scratch for tx_buf when clearing register
From: Dmitry Torokhov @ 2026-05-05 18:10 UTC (permalink / raw)
  To: Kris Bahnsen
  Cc: Marek Vasut, stable, Mark Featherston, linux-input, linux-kernel
In-Reply-To: <c49600c3-a78d-4d74-82bd-7f95328388a5@embeddedTS.com>

On Tue, May 05, 2026 at 09:21:50AM -0700, Kris Bahnsen wrote:
> Dmitry,
> 
> On 5/4/26 8:01 PM, Dmitry Torokhov wrote:
> > Hi Kris,
> > 
> > On Thu, Apr 30, 2026 at 05:37:38PM +0000, Kris Bahnsen wrote:
> >> The workaround for XPT2046 clears the command register, giving the
> >> touchscreen controller a NOP. The change incorrectly re-uses the
> >> req->scratch variable which is used as rx_buf for xfer[5], so by
> >> the time xfer[6] occurs, the contents of req->scratch may not be
> >> 0. It was found that the touchscreen controller can end up in
> >> a completely unresponsive state due to it being given a command
> >> the driver does not expect.
> >>
> >> Instead, rely on the spi_transfer behavior of tx_buf being NULL to
> >> transmit all 0 bits and use the scratch variable for the rx_buf for
> >> both the 1 byte command to and 2 byte response from the controller.
> >>
> >> This change was tested on real TSC2046 and ADS7843 controllers,
> >> but not the XPT2046 the workaround was originally created for.
> >> Confirming that the original modification to clear the command
> >> register does not impact either real controller.
> >>
> >> Fixes: 781a07da9bb94 ("Input: ads7846 - add dummy command register clearing cycle")
> >> Cc: stable@vger.kernel.org
> >> Co-developed-by: Mark Featherston <mark@embeddedTS.com>
> >> Signed-off-by: Mark Featherston <mark@embeddedTS.com>
> >> Signed-off-by: Kris Bahnsen <kris@embeddedTS.com>
> >> ---
> >>
> >> V1 -> V2: Don't use rx_buf when clearing command reg
> >> V2 -> V3: Modify original 2 xfer command to eliminate dev_err()
> >>           output on xfer with len and NULL buffers
> >>
> >>  drivers/input/touchscreen/ads7846.c | 3 +--
> >>  1 file changed, 1 insertion(+), 2 deletions(-)
> >>
> >> diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
> >> index 4b39f7212d35c..488bcc8393293 100644
> >> --- a/drivers/input/touchscreen/ads7846.c
> >> +++ b/drivers/input/touchscreen/ads7846.c
> >> @@ -403,8 +403,7 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
> >>  	spi_message_add_tail(&req->xfer[5], &req->msg);
> >>  
> >>  	/* clear the command register */
> >> -	req->scratch = 0;
> >> -	req->xfer[6].tx_buf = &req->scratch;
> >> +	req->xfer[6].rx_buf = &req->scratch;
> > 
> > Sashiko (I believe correctly) pointed out that by doing this "scratch"
> > is now write only and this may cause DMA from the device stomp on
> > message status and other unrelated data that shares the same cacheline
> > with scracth. While it was already a problem before now it is even more
> > likely.
> > 
> > Since scratch is now write-only I believe moving it below "sample"
> > forces it into separate cacheline and fixes this problem. Could you
> > please try making this change?
> 
> Apologies, I'm not quite certain I understand what you mean by
> "moving it below sample." Do you mean relocating the xfer[6] block
> immediately below the xfer[3] block like so? If yes, I can get this
> tested and a v4 patch together. If not, can you please clarify?

I meant doing this:

diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index 093f4b56cc18..04ba98b62f70 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -328,7 +328,6 @@ struct ser_req {
 	u8			ref_on;
 	u8			command;
 	u8			ref_off;
-	u16			scratch;
 	struct spi_message	msg;
 	struct spi_transfer	xfer[8];
 	/*
@@ -336,6 +335,7 @@ struct ser_req {
 	 * transfer buffers to live in their own cache lines.
 	 */
 	__be16 sample ____cacheline_aligned;
+	u16 scratch;
 };
 
 struct ads7845_ser_req {

Thanks.

-- 
Dmitry

^ permalink raw reply related

* Re: [PATCH v2 0/4] iio: introduce devm_ API for hid sensro setup and cleanup
From: Jonathan Cameron @ 2026-05-05 16:33 UTC (permalink / raw)
  To: srinivas pandruvada
  Cc: Sanjay Chitroda, jikos, dlechner, nuno.sa, andy, sakari.ailus,
	linux-input, linux-iio, linux-kernel, Zhang Lixu
In-Reply-To: <15509f5e5a6642d2a4b0ccc13bc7d40d3b79f72a.camel@linux.intel.com>

On Fri, 01 May 2026 04:53:38 -0700
srinivas pandruvada <srinivas.pandruvada@linux.intel.com> wrote:

> + Lixu
> 
> On Wed, 2026-04-29 at 23:29 +0530, Sanjay Chitroda wrote:
> > From: Sanjay Chitroda <sanjayembeddedse@gmail.com>
> > 
> > Key highlights:
> > - Prepare change as pre-requisite for devm conversion for HID IIO
> >   drivers by removing redundant argument
> > - Add devm API to setup trigger and clenaup resource using
> >   devm_add_action_or_reset()
> > - few sample driver update using devm conversion to auto release
> > resource
> >   
> 
> devm_* calls are fine but needs tests particularly when forces ISH PCI
> drivers unbind, while iio-sensor-proxy has open sessions.
> 
> Lixu, Please check.
Hold off perhaps until we have a v3 with fixes for the existing devm
calls against wrong struct device.

Thanks,

Jonathan

> 
> Thanks,
> Srinivas
> 
> 
> 
> > changes in v2:
> > - Following input from Jonathan and Andy, squash initial patch v1
> >   series in single change as individual change should not break
> > anything
> > - Add devm API support and two driver using the same
> > - v1 series ->
> > https://lore.kernel.org/all/20260428071613.1134053-1-sanjayembedded@gmail.com/
> > 
> > Testing:
> >   - Compiled with W=1
> >   - Build-tested on QEMU x86_64
> > 
> > Based on further feedback and reviews, I would extend this series to
> > convert all HID IIO driver to use devm_* API.
> > 
> > Thanks,
> > Sanjay Chitroda
> > 
> > Sanjay Chitroda (4):
> >   iio: hid-sensors: drop redundant iio_dev argument
> >   iio: hid-sensors: introduce device managed API
> >   iio: gyro: drop hid_sensor_remove_trigger() using devm API
> >   iio: humidity: drop hid_sensor_remove_trigger() using devm API
> > 
> >  drivers/iio/accel/hid-sensor-accel-3d.c       |  4 +--
> >  .../common/hid-sensors/hid-sensor-trigger.c   | 27
> > +++++++++++++++++--
> >  .../common/hid-sensors/hid-sensor-trigger.h   |  5 ++--
> >  drivers/iio/gyro/hid-sensor-gyro-3d.c         | 10 +++----
> >  drivers/iio/humidity/hid-sensor-humidity.c    | 10 +++----
> >  drivers/iio/light/hid-sensor-als.c            |  4 +--
> >  drivers/iio/light/hid-sensor-prox.c           |  4 +--
> >  drivers/iio/magnetometer/hid-sensor-magn-3d.c |  4 +--
> >  drivers/iio/orientation/hid-sensor-incl-3d.c  |  4 +--
> >  drivers/iio/orientation/hid-sensor-rotation.c |  4 +--
> >  .../position/hid-sensor-custom-intel-hinge.c  |  4 +--
> >  drivers/iio/pressure/hid-sensor-press.c       |  4 +--
> >  .../iio/temperature/hid-sensor-temperature.c  |  4 +--
> >  13 files changed, 52 insertions(+), 36 deletions(-)
> > 
> > 
> > base-commit: eade2b843d9b1f668fc1775f15611bb0a1999cd9  


^ permalink raw reply

* Re: [PATCH v2 4/4] iio: humidity: drop hid_sensor_remove_trigger() using devm API
From: Jonathan Cameron @ 2026-05-05 16:32 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Sanjay Chitroda, Andy Shevchenko, jikos, srinivas.pandruvada,
	dlechner, nuno.sa, andy, sakari.ailus, linux-input, linux-iio,
	linux-kernel
In-Reply-To: <CAHp75VdzPr+SLCPT85zw6aK8pSueBHr-OFpJ1Vmsow61e8=p4w@mail.gmail.com>

On Fri, 1 May 2026 21:25:35 +0300
Andy Shevchenko <andy.shevchenko@gmail.com> wrote:

> On Thu, Apr 30, 2026 at 10:21 PM Sanjay Chitroda
> <sanjayembeddedse@gmail.com> wrote:
> > On 30 April 2026 1:03:56 am IST, Andy Shevchenko <andriy.shevchenko@intel.com> wrote:  
> > >On Wed, Apr 29, 2026 at 11:29:18PM +0530, Sanjay Chitroda wrote:
> > >  
> > >> Use devm_hid_sensor_setup_trigger() to automatically release resource  
> 
> resources
> 
> > >> during fail, unbind or removal of driver using devres framework.  
> 
> failure
> 
> > >>
> > >> This simplify the setup, remove goto, avoid manual resource cleanup in  
> 
> simplifies
> removes
> and avoids
> 
> OR
> 
> "This is done in a way to simplify..."
> 
> > >> teardown path.  
> 
> ...
> 
> > >> +    ret = devm_hid_sensor_setup_trigger(&indio_dev->dev, indio_dev, name,
> > >> +                                        &humid_st->common_attributes);  
> > >
> > >I believe the first parameter is utterly wrong here.
> > >Or other way around, same issue but in the previous patch.
> > >  
> > Thank you Andy for the review comment.
> >
> > It looks in same humidity probe of hid sensor with devm API two device pointer are used &pdev->dev and &indio_dev->dev; ideally all devm should have same parent device for devres resource framework and over here preferable and consistent device should be &pdev->dev;
> >
> > I would first update existing devm_* API to have consistent device and on top of that will use same device in devm conversation.
> >
> > While for gyro change device is consistent as &pdev->dev across all devm API.  
> 
> The idea is that you have to go deeply understanding the object
> lifetimes for the cases of different device instances along with
> userspace communication channels (all possible ABIs the driver uses).
> With only that the proper parameter may be chosen or even confirmed
> that device managed resources must not be used. Yeah, this is one of
> the downsides of devm_*() APIs.
> 
FWIW it's always wrong from a design point of view to use iio_dev->dev
for devm.  It works but leaves us open to getting the ordering wrong
as some other stuff (including things you can't necessarily see explicitly
in a driver) will be using the parent device.

Jonathan


^ permalink raw reply

* Re: [PATCH v3] Input: ads7846 - don't use scratch for tx_buf when clearing register
From: Kris Bahnsen @ 2026-05-05 16:21 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Marek Vasut, stable, Mark Featherston, linux-input, linux-kernel
In-Reply-To: <aflcL6y_ugHV5p8s@google.com>

Dmitry,

On 5/4/26 8:01 PM, Dmitry Torokhov wrote:
> Hi Kris,
> 
> On Thu, Apr 30, 2026 at 05:37:38PM +0000, Kris Bahnsen wrote:
>> The workaround for XPT2046 clears the command register, giving the
>> touchscreen controller a NOP. The change incorrectly re-uses the
>> req->scratch variable which is used as rx_buf for xfer[5], so by
>> the time xfer[6] occurs, the contents of req->scratch may not be
>> 0. It was found that the touchscreen controller can end up in
>> a completely unresponsive state due to it being given a command
>> the driver does not expect.
>>
>> Instead, rely on the spi_transfer behavior of tx_buf being NULL to
>> transmit all 0 bits and use the scratch variable for the rx_buf for
>> both the 1 byte command to and 2 byte response from the controller.
>>
>> This change was tested on real TSC2046 and ADS7843 controllers,
>> but not the XPT2046 the workaround was originally created for.
>> Confirming that the original modification to clear the command
>> register does not impact either real controller.
>>
>> Fixes: 781a07da9bb94 ("Input: ads7846 - add dummy command register clearing cycle")
>> Cc: stable@vger.kernel.org
>> Co-developed-by: Mark Featherston <mark@embeddedTS.com>
>> Signed-off-by: Mark Featherston <mark@embeddedTS.com>
>> Signed-off-by: Kris Bahnsen <kris@embeddedTS.com>
>> ---
>>
>> V1 -> V2: Don't use rx_buf when clearing command reg
>> V2 -> V3: Modify original 2 xfer command to eliminate dev_err()
>>           output on xfer with len and NULL buffers
>>
>>  drivers/input/touchscreen/ads7846.c | 3 +--
>>  1 file changed, 1 insertion(+), 2 deletions(-)
>>
>> diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
>> index 4b39f7212d35c..488bcc8393293 100644
>> --- a/drivers/input/touchscreen/ads7846.c
>> +++ b/drivers/input/touchscreen/ads7846.c
>> @@ -403,8 +403,7 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
>>  	spi_message_add_tail(&req->xfer[5], &req->msg);
>>  
>>  	/* clear the command register */
>> -	req->scratch = 0;
>> -	req->xfer[6].tx_buf = &req->scratch;
>> +	req->xfer[6].rx_buf = &req->scratch;
> 
> Sashiko (I believe correctly) pointed out that by doing this "scratch"
> is now write only and this may cause DMA from the device stomp on
> message status and other unrelated data that shares the same cacheline
> with scracth. While it was already a problem before now it is even more
> likely.
> 
> Since scratch is now write-only I believe moving it below "sample"
> forces it into separate cacheline and fixes this problem. Could you
> please try making this change?

Apologies, I'm not quite certain I understand what you mean by
"moving it below sample." Do you mean relocating the xfer[6] block
immediately below the xfer[3] block like so? If yes, I can get this
tested and a v4 patch together. If not, can you please clarify?


diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index 4b39f7212d35..6d57865ff505 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -390,6 +390,11 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
        req->xfer[3].len = 2;
        spi_message_add_tail(&req->xfer[3], &req->msg);
 
+       /* clear the command register */
+       req->xfer[6].rx_buf = &req->scratch;
+       req->xfer[6].len = 1;
+       spi_message_add_tail(&req->xfer[6], &req->msg);
+
        /* REVISIT:  take a few more samples, and compare ... */
 
        /* converter in low power mode & enable PENIRQ */
@@ -402,12 +407,6 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
        req->xfer[5].len = 2;
        spi_message_add_tail(&req->xfer[5], &req->msg);
 
-       /* clear the command register */
-       req->scratch = 0;
-       req->xfer[6].tx_buf = &req->scratch;
-       req->xfer[6].len = 1;
-       spi_message_add_tail(&req->xfer[6], &req->msg);
-
        req->xfer[7].rx_buf = &req->scratch;
        req->xfer[7].len = 2;
        CS_CHANGE(req->xfer[7]);

> 
> Thanks.
> 

-- 
Kris Bahnsen
Software Engineer
embeddedTS


^ permalink raw reply related

* Re: [PATCH 1/3] Input: atmel_mxt_ts - fix boundary check in mxt_prepare_cfg_mem
From: Dmitry Torokhov @ 2026-05-05 15:03 UTC (permalink / raw)
  To: Ricardo Ribalda; +Cc: Nick Dyer, linux-input, linux-kernel, stable
In-Reply-To: <CANiDSCv+h_ry7W1e1mFNLhont-1xigEZj6jL3m=FVgv2UC+KzQ@mail.gmail.com>

Hi Ricardo,

On Tue, May 05, 2026 at 11:08:15AM +0200, Ricardo Ribalda wrote:
> HI Dmitry
> 
> FWIW this patch looks correct to me...

Thank you for looking this over.

> 
> Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> 
> But there are a couple of things that look weird.
> 
> 1) The patch line (1503) does not seem to match your tree
> https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git/tree/drivers/input/touchscreen/atmel_mxt_ts.c#n1503

Yeah, I have an unrelated path in my queue that affects line offsets,

> 
> 2) The sscanf just before this check has two conversions (val and
> offset), but you only check for ret != 1. Should't it be ret !=2? or I
> am missing something?

"%n" format specifier does not increment number of successfully parsed
elements returned by sscanf(). It kind of makes sense although may look
surprising.

Thanks.

-- 
Dmitry

^ permalink raw reply

* [dtor-input:next] BUILD SUCCESS e7b91b21755924c2b26bd4f925c1b538a48b2370
From: kernel test robot @ 2026-05-05 14:38 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input

tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git next
branch HEAD: e7b91b21755924c2b26bd4f925c1b538a48b2370  Input: atmel_mxt_ts - set byte_offset as signed

elapsed time: 728m

configs tested: 222
configs skipped: 15

The following configs have been built successfully.
More configs may be tested in the coming days.

tested configs:
alpha                             allnoconfig    gcc-15.2.0
alpha                            allyesconfig    gcc-15.2.0
alpha                               defconfig    gcc-15.2.0
arc                              allmodconfig    clang-16
arc                               allnoconfig    gcc-15.2.0
arc                              allyesconfig    clang-23
arc                                 defconfig    gcc-15.2.0
arc                   randconfig-001-20260505    gcc-8.5.0
arc                   randconfig-002-20260505    gcc-8.5.0
arm                               allnoconfig    gcc-15.2.0
arm                              allyesconfig    clang-16
arm                                 defconfig    gcc-15.2.0
arm                            mmp2_defconfig    gcc-15.2.0
arm                   randconfig-001-20260505    gcc-8.5.0
arm                   randconfig-002-20260505    gcc-8.5.0
arm                   randconfig-003-20260505    gcc-8.5.0
arm                   randconfig-004-20260505    gcc-8.5.0
arm64                            allmodconfig    clang-23
arm64                             allnoconfig    gcc-15.2.0
arm64                               defconfig    gcc-15.2.0
arm64                 randconfig-001-20260505    gcc-14.3.0
arm64                 randconfig-002-20260505    gcc-14.3.0
arm64                 randconfig-003-20260505    gcc-14.3.0
arm64                 randconfig-004-20260505    gcc-14.3.0
csky                             alldefconfig    gcc-15.2.0
csky                             allmodconfig    gcc-15.2.0
csky                              allnoconfig    gcc-15.2.0
csky                                defconfig    gcc-15.2.0
csky                  randconfig-001-20260505    gcc-14.3.0
csky                  randconfig-002-20260505    gcc-14.3.0
hexagon                          allmodconfig    gcc-15.2.0
hexagon                           allnoconfig    gcc-15.2.0
hexagon                             defconfig    gcc-15.2.0
hexagon                        randconfig-001    gcc-11.5.0
hexagon               randconfig-001-20260505    clang-23
hexagon               randconfig-001-20260505    gcc-11.5.0
hexagon                        randconfig-002    gcc-11.5.0
hexagon               randconfig-002-20260505    clang-23
hexagon               randconfig-002-20260505    gcc-11.5.0
i386                             allmodconfig    clang-20
i386                              allnoconfig    gcc-15.2.0
i386                             allyesconfig    clang-20
i386                 buildonly-randconfig-001    gcc-14
i386        buildonly-randconfig-001-20260505    gcc-14
i386                 buildonly-randconfig-002    gcc-14
i386        buildonly-randconfig-002-20260505    gcc-14
i386                 buildonly-randconfig-003    gcc-14
i386        buildonly-randconfig-003-20260505    gcc-14
i386                 buildonly-randconfig-004    gcc-14
i386        buildonly-randconfig-004-20260505    gcc-14
i386                 buildonly-randconfig-005    gcc-14
i386        buildonly-randconfig-005-20260505    gcc-14
i386                 buildonly-randconfig-006    gcc-14
i386        buildonly-randconfig-006-20260505    gcc-14
i386                                defconfig    gcc-15.2.0
i386                  randconfig-001-20260505    clang-20
i386                  randconfig-002-20260505    clang-20
i386                  randconfig-003-20260505    clang-20
i386                  randconfig-004-20260505    clang-20
i386                  randconfig-005-20260505    clang-20
i386                  randconfig-006-20260505    clang-20
i386                  randconfig-007-20260505    clang-20
i386                  randconfig-011-20260505    clang-20
i386                  randconfig-012-20260505    clang-20
i386                  randconfig-013-20260505    clang-20
i386                  randconfig-014-20260505    clang-20
i386                  randconfig-015-20260505    clang-20
i386                  randconfig-016-20260505    clang-20
i386                  randconfig-017-20260505    clang-20
loongarch                        allmodconfig    clang-23
loongarch                         allnoconfig    gcc-15.2.0
loongarch                           defconfig    clang-19
loongarch                      randconfig-001    gcc-11.5.0
loongarch             randconfig-001-20260505    clang-23
loongarch             randconfig-001-20260505    gcc-11.5.0
loongarch                      randconfig-002    gcc-11.5.0
loongarch             randconfig-002-20260505    clang-23
loongarch             randconfig-002-20260505    gcc-11.5.0
m68k                             allmodconfig    gcc-15.2.0
m68k                              allnoconfig    gcc-15.2.0
m68k                             allyesconfig    clang-16
m68k                                defconfig    clang-19
microblaze                        allnoconfig    gcc-15.2.0
microblaze                       allyesconfig    gcc-15.2.0
microblaze                          defconfig    clang-19
mips                             allmodconfig    gcc-15.2.0
mips                              allnoconfig    gcc-15.2.0
mips                             allyesconfig    gcc-15.2.0
mips                  decstation_64_defconfig    gcc-15.2.0
nios2                            allmodconfig    clang-23
nios2                             allnoconfig    clang-23
nios2                               defconfig    clang-19
nios2                          randconfig-001    gcc-11.5.0
nios2                 randconfig-001-20260505    clang-23
nios2                 randconfig-001-20260505    gcc-11.5.0
nios2                          randconfig-002    gcc-11.5.0
nios2                 randconfig-002-20260505    clang-23
nios2                 randconfig-002-20260505    gcc-11.5.0
openrisc                         allmodconfig    clang-23
openrisc                          allnoconfig    clang-23
openrisc         de0_nano_multicore_defconfig    gcc-15.2.0
openrisc                            defconfig    gcc-15.2.0
parisc                           allmodconfig    gcc-15.2.0
parisc                            allnoconfig    clang-23
parisc                           allyesconfig    clang-19
parisc                              defconfig    gcc-15.2.0
parisc                generic-32bit_defconfig    gcc-15.2.0
parisc                         randconfig-001    gcc-14.3.0
parisc                randconfig-001-20260505    gcc-14.3.0
parisc                         randconfig-002    gcc-14.3.0
parisc                randconfig-002-20260505    gcc-14.3.0
parisc64                            defconfig    clang-19
powerpc                          allmodconfig    gcc-15.2.0
powerpc                           allnoconfig    clang-23
powerpc                        randconfig-001    gcc-14.3.0
powerpc               randconfig-001-20260505    gcc-14.3.0
powerpc                        randconfig-002    gcc-14.3.0
powerpc               randconfig-002-20260505    gcc-14.3.0
powerpc                     tqm8541_defconfig    clang-23
powerpc64                      randconfig-001    gcc-14.3.0
powerpc64             randconfig-001-20260505    gcc-14.3.0
powerpc64                      randconfig-002    gcc-14.3.0
powerpc64             randconfig-002-20260505    gcc-14.3.0
riscv                            allmodconfig    clang-23
riscv                             allnoconfig    clang-23
riscv                            allyesconfig    clang-16
riscv                               defconfig    gcc-15.2.0
riscv                 randconfig-001-20260505    gcc-10.5.0
riscv                 randconfig-002-20260505    gcc-10.5.0
s390                             allmodconfig    clang-19
s390                              allnoconfig    clang-23
s390                             allyesconfig    gcc-15.2.0
s390                                defconfig    gcc-15.2.0
s390                  randconfig-001-20260505    gcc-10.5.0
s390                  randconfig-002-20260505    gcc-10.5.0
sh                               allmodconfig    gcc-15.2.0
sh                                allnoconfig    clang-23
sh                               allyesconfig    clang-19
sh                                  defconfig    gcc-14
sh                    randconfig-001-20260505    gcc-10.5.0
sh                    randconfig-002-20260505    gcc-10.5.0
sh                          urquell_defconfig    gcc-15.2.0
sparc                             allnoconfig    clang-23
sparc                               defconfig    gcc-15.2.0
sparc                          randconfig-001    gcc-15.2.0
sparc                 randconfig-001-20260505    gcc-15.2.0
sparc                          randconfig-002    gcc-15.2.0
sparc                 randconfig-002-20260505    gcc-15.2.0
sparc64                          allmodconfig    clang-23
sparc64                             defconfig    gcc-14
sparc64                        randconfig-001    gcc-15.2.0
sparc64               randconfig-001-20260505    gcc-15.2.0
sparc64                        randconfig-002    gcc-15.2.0
sparc64               randconfig-002-20260505    gcc-15.2.0
um                               allmodconfig    clang-19
um                                allnoconfig    clang-23
um                               allyesconfig    gcc-14
um                               allyesconfig    gcc-15.2.0
um                                  defconfig    gcc-14
um                             i386_defconfig    gcc-14
um                             randconfig-001    gcc-15.2.0
um                    randconfig-001-20260505    gcc-15.2.0
um                             randconfig-002    gcc-15.2.0
um                    randconfig-002-20260505    gcc-15.2.0
um                           x86_64_defconfig    gcc-14
x86_64                           allmodconfig    clang-20
x86_64                            allnoconfig    clang-23
x86_64                           allyesconfig    clang-20
x86_64      buildonly-randconfig-001-20260505    clang-20
x86_64      buildonly-randconfig-002-20260505    clang-20
x86_64      buildonly-randconfig-003-20260505    clang-20
x86_64      buildonly-randconfig-004-20260505    clang-20
x86_64      buildonly-randconfig-005-20260505    clang-20
x86_64      buildonly-randconfig-006-20260505    clang-20
x86_64                              defconfig    gcc-14
x86_64                                  kexec    clang-20
x86_64                         randconfig-001    clang-20
x86_64                randconfig-001-20260505    clang-20
x86_64                         randconfig-002    clang-20
x86_64                randconfig-002-20260505    clang-20
x86_64                         randconfig-003    clang-20
x86_64                randconfig-003-20260505    clang-20
x86_64                         randconfig-004    clang-20
x86_64                randconfig-004-20260505    clang-20
x86_64                         randconfig-005    clang-20
x86_64                randconfig-005-20260505    clang-20
x86_64                         randconfig-006    clang-20
x86_64                randconfig-006-20260505    clang-20
x86_64                         randconfig-011    clang-20
x86_64                randconfig-011-20260505    clang-20
x86_64                         randconfig-012    clang-20
x86_64                randconfig-012-20260505    clang-20
x86_64                         randconfig-013    clang-20
x86_64                randconfig-013-20260505    clang-20
x86_64                         randconfig-014    clang-20
x86_64                randconfig-014-20260505    clang-20
x86_64                         randconfig-015    clang-20
x86_64                randconfig-015-20260505    clang-20
x86_64                         randconfig-016    clang-20
x86_64                randconfig-016-20260505    clang-20
x86_64                randconfig-071-20260505    clang-20
x86_64                randconfig-071-20260505    gcc-14
x86_64                randconfig-072-20260505    clang-20
x86_64                randconfig-073-20260505    clang-20
x86_64                randconfig-073-20260505    gcc-14
x86_64                randconfig-074-20260505    clang-20
x86_64                randconfig-074-20260505    gcc-14
x86_64                randconfig-075-20260505    clang-20
x86_64                randconfig-076-20260505    clang-20
x86_64                               rhel-9.4    clang-20
x86_64                           rhel-9.4-bpf    gcc-14
x86_64                          rhel-9.4-func    clang-20
x86_64                    rhel-9.4-kselftests    clang-20
x86_64                         rhel-9.4-kunit    gcc-14
x86_64                           rhel-9.4-ltp    gcc-14
x86_64                          rhel-9.4-rust    clang-20
xtensa                            allnoconfig    clang-23
xtensa                           allyesconfig    clang-23
xtensa                         randconfig-001    gcc-15.2.0
xtensa                randconfig-001-20260505    gcc-15.2.0
xtensa                         randconfig-002    gcc-15.2.0
xtensa                randconfig-002-20260505    gcc-15.2.0

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply

* Re: [PATCH 6/6] leds: led-class: mark classdev as unregistering early
From: James Ye @ 2026-05-05 14:16 UTC (permalink / raw)
  To: kernel test robot
  Cc: jikos, bentiss, lee, pavel, oe-kbuild-all, linux-input,
	linux-leds, linux-kernel, denis.benato
In-Reply-To: <202605050414.YJmW3t4y-lkp@intel.com>

On Tue, 5 May 2026 at 12:24, kernel test robot <lkp@intel.com> wrote:
>
> Hi James,
>
> kernel test robot noticed the following build errors:

This is due to CONFIG_HID_BATTERY_STRENGTH being turned off, I will
fix it in the next version.

^ permalink raw reply

* Re: [PATCH v2 2/2] Input: isa1200 - new driver for Imagis ISA1200
From: Svyatoslav Ryhel @ 2026-05-05 11:42 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	linux-input, devicetree, linux-kernel
In-Reply-To: <CAD++jLk_SojErY4gxjixzwEF3sfSownH=jBYaoPD2LeoxibhWw@mail.gmail.com>

вт, 5 трав. 2026 р. о 13:39 Linus Walleij <linusw@kernel.org> пише:
>
> On Thu, Apr 30, 2026 at 11:46 AM Svyatoslav Ryhel <clamor95@gmail.com> wrote:
> > чт, 30 квіт. 2026 р. о 12:22 Linus Walleij <linusw@kernel.org> пише:
>
> > In you original code
> > /*
> > * This is done in the vendor tree with the commment
> > * "Duty 0x64 == nForce 90", and no force feedback happens
> > * unless we do this.
> > */
> > if (isa->clk)
> > regmap_write(isa->map, ISA1200_HCTRL5, 0x64);
> >
> > 0x64 is actually some conversion of duty cycle, you got this accurately.
>
> I wonder if 0x64 = 100 is simply 100% duty cycle?
> So the value in this register can only be 0..100.
>

Assumption is interesting, but it is still just a guess based on
observation of a single hw configuration. For example, Samsung Galaxy
Tab 10.1 (GT-P7501) passes 133 (0x85) into duty register.

> Yours,
> Linus Walleij

^ permalink raw reply

* Re: [PATCH v2 2/2] Input: isa1200 - new driver for Imagis ISA1200
From: Linus Walleij @ 2026-05-05 10:39 UTC (permalink / raw)
  To: Svyatoslav Ryhel
  Cc: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	linux-input, devicetree, linux-kernel
In-Reply-To: <CAPVz0n3h+zWi-b-ZTLqwXR-9JNGcUgVodUgo+ywhBfK-UDQJAw@mail.gmail.com>

On Thu, Apr 30, 2026 at 11:46 AM Svyatoslav Ryhel <clamor95@gmail.com> wrote:
> чт, 30 квіт. 2026 р. о 12:22 Linus Walleij <linusw@kernel.org> пише:

> In you original code
> /*
> * This is done in the vendor tree with the commment
> * "Duty 0x64 == nForce 90", and no force feedback happens
> * unless we do this.
> */
> if (isa->clk)
> regmap_write(isa->map, ISA1200_HCTRL5, 0x64);
>
> 0x64 is actually some conversion of duty cycle, you got this accurately.

I wonder if 0x64 = 100 is simply 100% duty cycle?
So the value in this register can only be 0..100.

Yours,
Linus Walleij

^ permalink raw reply

* Re: [PATCH 3/3] Input: atmel_mxt_ts - use __free() for obuf in mxt_object_show
From: Ricardo Ribalda @ 2026-05-05  9:10 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: Nick Dyer, linux-input, linux-kernel
In-Reply-To: <20260504185448.4055973-3-dmitry.torokhov@gmail.com>

On Mon, 4 May 2026 at 20:54, Dmitry Torokhov <dmitry.torokhov@gmail.com> wrote:
>
> Use the __free(kfree) macro for the obuf allocation in mxt_object_show()
> to simplify the code.
>
> Assisted-by: Gemini:gemini-3.1-pro
> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> ---
>  drivers/input/touchscreen/atmel_mxt_ts.c | 10 +++-------
>  1 file changed, 3 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
> index d660cc5b5fe3..6af4db5ed117 100644
> --- a/drivers/input/touchscreen/atmel_mxt_ts.c
> +++ b/drivers/input/touchscreen/atmel_mxt_ts.c
> @@ -2870,14 +2870,12 @@ static ssize_t mxt_object_show(struct device *dev,
>         int count = 0;
>         int i, j;
>         int error;
> -       u8 *obuf;
>
>         /* Pre-allocate buffer large enough to hold max sized object. */
> -       obuf = kmalloc(256, GFP_KERNEL);
> +       u8 *obuf __free(kfree) = kmalloc(256, GFP_KERNEL);
>         if (!obuf)
>                 return -ENOMEM;
>
> -       error = 0;
>         for (i = 0; i < data->info->object_num; i++) {
>                 object = data->object_table + i;
>
> @@ -2892,15 +2890,13 @@ static ssize_t mxt_object_show(struct device *dev,
>
>                         error = __mxt_read_reg(data->client, addr, size, obuf);
>                         if (error)
> -                               goto done;
> +                               return error;
>
>                         count = mxt_show_instance(buf, count, object, j, obuf);
>                 }
>         }
>
> -done:
> -       kfree(obuf);
> -       return error ?: count;
> +       return count;
>  }
>
>  static int mxt_check_firmware_format(struct device *dev,
> --
> 2.54.0.545.g6539524ca2-goog
>


-- 
Ricardo Ribalda

^ permalink raw reply

* Re: [PATCH 1/3] Input: atmel_mxt_ts - fix boundary check in mxt_prepare_cfg_mem
From: Ricardo Ribalda @ 2026-05-05  9:08 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: Nick Dyer, linux-input, linux-kernel, stable
In-Reply-To: <20260504185448.4055973-1-dmitry.torokhov@gmail.com>

HI Dmitry

FWIW this patch looks correct to me...

Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>

But there are a couple of things that look weird.

1) The patch line (1503) does not seem to match your tree
https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git/tree/drivers/input/touchscreen/atmel_mxt_ts.c#n1503

2) The sscanf just before this check has two conversions (val and
offset), but you only check for ret != 1. Should't it be ret !=2? or I
am missing something?

On Mon, 4 May 2026 at 20:54, Dmitry Torokhov <dmitry.torokhov@gmail.com> wrote:
>
> When a configuration file provides an object size that is larger than the
> driver's known mxt_obj_size(object), the driver intends to discard the
> extra bytes.
>
> The loop iterates using for (i = 0; i < size; i++). Inside the loop, the
> condition to skip processing extra bytes is:
>
>     if (i > mxt_obj_size(object))
>         continue;
>
> Since i is a 0-based index, the valid indices for the object are 0 through
> mxt_obj_size(object) - 1.
>
> When i == mxt_obj_size(object), the condition evaluates to false, and the
> code processes the byte instead of discarding it.
>
> This causes the code to calculate byte_offset = reg + i - cfg->start_ofs
> and writes the byte there, overwriting exactly one byte of the adjacent
> instance or object.
>
> Update the boundary check to skip extra bytes correctly by using >=.
>
> Fixes: 50a77c658b80 ("Input: atmel_mxt_ts - download device config using firmware loader")
> Cc: stable@vger.kernel.org
> Assisted-by: Gemini:gemini-3.1-pro
> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> ---
>  drivers/input/touchscreen/atmel_mxt_ts.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
> index d62bf2c95578..28b2bd889c70 100644
> --- a/drivers/input/touchscreen/atmel_mxt_ts.c
> +++ b/drivers/input/touchscreen/atmel_mxt_ts.c
> @@ -1503,7 +1503,7 @@ static int mxt_prepare_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg)
>                         }
>                         cfg->raw_pos += offset;
>
> -                       if (i > mxt_obj_size(object))
> +                       if (i >= mxt_obj_size(object))
>                                 continue;
>
>                         byte_offset = reg + i - cfg->start_ofs;
> --
> 2.54.0.545.g6539524ca2-goog
>


-- 
Ricardo Ribalda

^ permalink raw reply

* [PATCH] Input: xpad - remove unused xpad_init_output intf parameter
From: Tobias Klauser @ 2026-05-05  8:08 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input

The intf parameter to xpad_init_output is unused since commit
c01b5e7464f0 ("Input: xpad - don't depend on endpoint order"). Remove
it.

Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
---
 drivers/input/joystick/xpad.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index feb8f368f834..4c5528eb94b3 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -1388,8 +1388,8 @@ static void xpad_irq_out(struct urb *urb)
 	}
 }
 
-static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad,
-			struct usb_endpoint_descriptor *ep_irq_out)
+static int xpad_init_output(struct usb_xpad *xpad,
+			    struct usb_endpoint_descriptor *ep_irq_out)
 {
 	int error;
 
@@ -2136,7 +2136,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
 		goto err_free_in_urb;
 	}
 
-	error = xpad_init_output(intf, xpad, ep_irq_out);
+	error = xpad_init_output(xpad, ep_irq_out);
 	if (error)
 		goto err_free_in_urb;
 
-- 
2.52.0


^ permalink raw reply related

* [PATCH v2 20/20] Input: rmi4 - update formatting in F12
From: Dmitry Torokhov @ 2026-05-05  4:59 UTC (permalink / raw)
  To: linux-input; +Cc: Marge Yang, Greg Kroah-Hartman, linux-kernel
In-Reply-To: <20260505045952.1570713-1-dmitry.torokhov@gmail.com>

Clean up various style and formatting issues in the F12 code.

Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 drivers/input/rmi4/rmi_f12.c | 120 +++++++++++++++++------------------
 1 file changed, 60 insertions(+), 60 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c
index bf8c4a0e10de..88c28089de99 100644
--- a/drivers/input/rmi4/rmi_f12.c
+++ b/drivers/input/rmi4/rmi_f12.c
@@ -51,7 +51,6 @@ struct f12_data {
 	const struct rmi_register_desc_item *data6;
 	u16 data6_offset;
 
-
 	/* F12 Data9 reports relative data */
 	const struct rmi_register_desc_item *data9;
 	u16 data9_offset;
@@ -124,8 +123,8 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12)
 		return -ENODEV;
 	}
 
-	ret = rmi_read_block(rmi_dev, fn->fd.control_base_addr + offset, buf,
-				item->reg_size);
+	ret = rmi_read_block(rmi_dev, fn->fd.control_base_addr + offset,
+			     buf, item->reg_size);
 	if (ret)
 		return ret;
 
@@ -163,7 +162,7 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12)
 	if (rmi_get_register_desc_item(&f12->query_reg_desc,
 				       RMI_F12_QUERY_RESOLUTION)) {
 		offset = rmi_register_desc_calc_reg_offset(&f12->query_reg_desc,
-						RMI_F12_QUERY_RESOLUTION);
+							   RMI_F12_QUERY_RESOLUTION);
 		query_dpm_addr = fn->fd.query_base_addr	+ offset;
 		ret = rmi_read(fn->rmi_dev, query_dpm_addr, buf);
 		if (ret) {
@@ -248,18 +247,17 @@ static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1, u32 size)
 
 static irqreturn_t rmi_f12_attention(int irq, void *ctx)
 {
-	int retval;
 	struct rmi_function *fn = ctx;
 	struct rmi_device *rmi_dev = fn->rmi_dev;
 	struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev);
 	struct f12_data *f12 = dev_get_drvdata(&fn->dev);
 	struct rmi_2d_sensor *sensor = &f12->sensor;
 	u32 valid_bytes = sensor->pkt_size;
+	int retval;
 
 	if (drvdata->attn_data.data) {
 		valid_bytes = min_t(u32, sensor->attn_size, drvdata->attn_data.size);
-		memcpy(sensor->data_pkt, drvdata->attn_data.data,
-			valid_bytes);
+		memcpy(sensor->data_pkt, drvdata->attn_data.data, valid_bytes);
 		drvdata->attn_data.data += valid_bytes;
 		drvdata->attn_data.size -= valid_bytes;
 	} else {
@@ -273,70 +271,74 @@ static irqreturn_t rmi_f12_attention(int irq, void *ctx)
 	}
 
 	if (f12->data1)
-		rmi_f12_process_objects(f12,
-			&sensor->data_pkt[f12->data1_offset], valid_bytes);
+		rmi_f12_process_objects(f12, &sensor->data_pkt[f12->data1_offset],
+					valid_bytes);
 
 	input_mt_sync_frame(sensor->input);
 
 	return IRQ_HANDLED;
 }
 
-static int rmi_f12_write_control_regs(struct rmi_function *fn)
+static int rmi_f12_update_dribble(struct rmi_function *fn, struct f12_data *f12)
 {
-	int ret;
 	const struct rmi_register_desc_item *item;
 	struct rmi_device *rmi_dev = fn->rmi_dev;
-	struct f12_data *f12 = dev_get_drvdata(&fn->dev);
-	int control_size;
-	char buf[3];
-	u16 control_offset = 0;
 	u8 subpacket_offset = 0;
+	u16 control_offset;
+	u32 control_size;
+	int error;
+	u8 buf[3];
 
-	if (f12->has_dribble
-	    && (f12->sensor.dribble != RMI_REG_STATE_DEFAULT)) {
-		item = rmi_get_register_desc_item(&f12->control_reg_desc, 20);
-		if (item) {
-			control_offset = rmi_register_desc_calc_reg_offset(
-						&f12->control_reg_desc, 20);
-
-			/*
-			 * The byte containing the EnableDribble bit will be
-			 * in either byte 0 or byte 2 of control 20. Depending
-			 * on the existence of subpacket 0. If control 20 is
-			 * larger then 3 bytes, just read the first 3.
-			 */
-			control_size = min(item->reg_size, 3U);
-
-			ret = rmi_read_block(rmi_dev, fn->fd.control_base_addr
-					+ control_offset, buf, control_size);
-			if (ret)
-				return ret;
-
-			if (rmi_register_desc_has_subpacket(item, 0))
-				subpacket_offset += 1;
-
-			switch (f12->sensor.dribble) {
-			case RMI_REG_STATE_OFF:
-				buf[subpacket_offset] &= ~BIT(2);
-				break;
-			case RMI_REG_STATE_ON:
-				buf[subpacket_offset] |= BIT(2);
-				break;
-			case RMI_REG_STATE_DEFAULT:
-			default:
-				break;
-			}
+	item = rmi_get_register_desc_item(&f12->control_reg_desc, 20);
+	if (!item)
+		return 0;
 
-			ret = rmi_write_block(rmi_dev,
-				fn->fd.control_base_addr + control_offset,
-				buf, control_size);
-			if (ret)
-				return ret;
-		}
+	control_offset = rmi_register_desc_calc_reg_offset(&f12->control_reg_desc, 20);
+
+	/*
+	 * The byte containing the EnableDribble bit will be
+	 * in either byte 0 or byte 2 of control 20. Depending
+	 * on the existence of subpacket 0. If control 20 is
+	 * larger then 3 bytes, just read the first 3.
+	 */
+	control_size = min(item->reg_size, 3U);
+
+	error = rmi_read_block(rmi_dev, fn->fd.control_base_addr + control_offset,
+			       buf, control_size);
+	if (error)
+		return error;
+
+	if (rmi_register_desc_has_subpacket(item, 0))
+		subpacket_offset += 1;
+
+	switch (f12->sensor.dribble) {
+	case RMI_REG_STATE_OFF:
+		buf[subpacket_offset] &= ~BIT(2);
+		break;
+	case RMI_REG_STATE_ON:
+		buf[subpacket_offset] |= BIT(2);
+		break;
+	case RMI_REG_STATE_DEFAULT:
+	default:
+		break;
 	}
 
+	error = rmi_write_block(rmi_dev, fn->fd.control_base_addr + control_offset,
+				buf, control_size);
+	if (error)
+		return error;
+
 	return 0;
+}
 
+static int rmi_f12_write_control_regs(struct rmi_function *fn)
+{
+	struct f12_data *f12 = dev_get_drvdata(&fn->dev);
+
+	if (f12->has_dribble && f12->sensor.dribble != RMI_REG_STATE_DEFAULT)
+		return rmi_f12_update_dribble(fn, f12);
+
+	return 0;
 }
 
 static int rmi_f12_config(struct rmi_function *fn)
@@ -362,7 +364,7 @@ static int rmi_f12_config(struct rmi_function *fn)
 	ret = rmi_f12_write_control_regs(fn);
 	if (ret)
 		dev_warn(&fn->dev,
-			"Failed to write F12 control registers: %d\n", ret);
+			 "Failed to write F12 control registers: %d\n", ret);
 
 	return 0;
 }
@@ -433,16 +435,14 @@ static int rmi_f12_probe(struct rmi_function *fn)
 	}
 	sensor->pkt_size = pkt_size;
 
-	sensor->axis_align =
-		f12->sensor_pdata.axis_align;
+	sensor->axis_align = f12->sensor_pdata.axis_align;
 
 	sensor->x_mm = f12->sensor_pdata.x_mm;
 	sensor->y_mm = f12->sensor_pdata.y_mm;
 	sensor->dribble = f12->sensor_pdata.dribble;
 
 	if (sensor->sensor_type == rmi_sensor_default)
-		sensor->sensor_type =
-			f12->sensor_pdata.sensor_type;
+		sensor->sensor_type = f12->sensor_pdata.sensor_type;
 
 	rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: data packet size: %u\n", __func__,
 		sensor->pkt_size);
-- 
2.54.0.545.g6539524ca2-goog


^ permalink raw reply related


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