Linux Input/HID development
 help / color / mirror / Atom feed
* Re: [PATCH v2 0/2] iio: orientation: hid-sensor-rotation: fix quaternion alignment
From: Andy Shevchenko @ 2026-03-02  8:58 UTC (permalink / raw)
  To: David Lechner
  Cc: Jonathan Cameron, Nuno Sá, Andy Shevchenko, Jiri Kosina,
	Srinivas Pandruvada, linux-iio, linux-kernel, Jonathan Cameron,
	linux-input, Lixu Zhang
In-Reply-To: <20260228-iio-fix-repeat-alignment-v2-0-d58bfaa2920d@baylibre.com>

On Sat, Feb 28, 2026 at 02:02:21PM -0600, David Lechner wrote:
> The main point of this series is to fix a regression reported in
> hid-sensor-rotation where the alignment of the quaternion field in the
> data was inadvertently changed from 16 bytes to 8 bytes. This is an
> unusually case (one of only 2 in the kernel) where the .repeat field of
> struct iio_scan_type is used and we have such a requirement. (The other
> case uses u16 instead of u32, so it wasn't affected.)
> 
> To make the reason for the alignment more explicit to future readers,
> we introduce a new macro, IIO_DECLARE_QUATERNION(), to declare the
> array with proper alignment. This is meant to follow the pattern of
> the similar IIO_DECLARE_BUFFER_WITH_TS() macro.

Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>

But this is in conflict with the other hack-patch in another series.
I'm a bit lost what patch 2 fixes here and that hack-patch fixes
in the same driver. Shouldn't survive only one?

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH 1/4] iio: orientation: hid-sensor-rotation: add timestamp hack to not break userspace
From: Andy Shevchenko @ 2026-03-02  8:50 UTC (permalink / raw)
  To: David Lechner
  Cc: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, Nuno Sá,
	Andy Shevchenko, Lars Möllendorf, Lars-Peter Clausen,
	Greg Kroah-Hartman, Jonathan Cameron, Lixu Zhang, linux-input,
	linux-iio, linux-kernel
In-Reply-To: <20260301-iio-fix-timestamp-alignment-v1-1-1a54980bfb90@baylibre.com>

On Sun, Mar 01, 2026 at 02:24:50PM -0600, David Lechner wrote:
> Add a hack to push two timestamps in the hid-sensor-rotation scan data
> to avoid breaking userspace applications that depend on the timestamp
> being at the incorrect location in the scan data due to unintentional
> misalignment in older kernels.
> 
> When this driver was written, the timestamp was in the correct location
> because of the way iio_compute_scan_bytes() was implemented at the time.
> (Samples were 24 bytes each.) Then commit 883f61653069 ("iio: buffer:
> align the size of scan bytes to size of the largest element") changed
> the computed scan_bytes to be a different size (32 bytes), which caused
> iio_push_to_buffers_with_timestamp() to place the timestamp at an
> incorrect offset.
> 
> There have been long periods of time (6 years each) where the timestamp
> was in either location, so to not break either case, we open-code the
> timestamps to be pushed to both locations in the scan data.

...

> +		/*
> +		 * HACK: There are two copies of the same timestamp in case of

Usually we use FIXME in such cases. HACK is something which goes with
"do not apply".

Does it mean it will stay forever?

> +		 * userspace depending on broken alignment from older kernels.
> +		 */
> +		aligned_s64 timestamp[2];

...

> +		/*
> +		 * HACK: IIO previously had an incorrect implementation of

Ditto.

> +		 * iio_push_to_buffers_with_timestamp() that put the timestamp
> +		 * in the last 8 bytes of the buffer, which was incorrect
> +		 * according to the IIO ABI. To avoid breaking userspace that
> +		 * depended on this broken behavior, we put the timestamp in
> +		 * both the correct place and the old incorrect place.
> +		 */
> +		rot_state->scan.timestamp[0] = rot_state->timestamp;
> +		rot_state->scan.timestamp[1] = rot_state->timestamp;

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH 4/4] iio: buffer: fix timestamp alignment when quaternion in scan
From: Andy Shevchenko @ 2026-03-02  8:47 UTC (permalink / raw)
  To: David Lechner
  Cc: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, Nuno Sá,
	Andy Shevchenko, Lars Möllendorf, Lars-Peter Clausen,
	Greg Kroah-Hartman, Jonathan Cameron, Lixu Zhang, linux-input,
	linux-iio, linux-kernel
In-Reply-To: <20260301-iio-fix-timestamp-alignment-v1-4-1a54980bfb90@baylibre.com>

On Sun, Mar 01, 2026 at 02:24:53PM -0600, David Lechner wrote:
> Fix timestamp alignment when a scan buffer contains an element larger
> than sizeof(int64_t). Currently s32 quaternions are the only such
> element, and the one driver that has this (hid-sensor-rotation) has a
> workaround in place already so this change does not affect it.
> 
> Previously, we assumed that the timestamp would always be 8-byte aligned
> relative to the end of the scan buffer, but in the case of a scan buffer
> a 16-byte quaternion vector, scan_bytes == 32, but the timestamp needs
> to be placed at offset 16, not 24.

...

> -		((int64_t *)data)[ts_offset] = timestamp;

> +		*(int64_t *)(data + ts_offset) = timestamp;

What's the point in modifying this? The comment you added suffice for
the original line.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH v2] iio: orientation: hid-sensor-rotation: use ext_scan_type
From: Andy Shevchenko @ 2026-03-02  8:17 UTC (permalink / raw)
  To: David Lechner
  Cc: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, Nuno Sá,
	Andy Shevchenko, linux-input, linux-iio, linux-kernel
In-Reply-To: <20260301-iio-hid-sensor-rotation-cleanup-v2-1-245c6ad59afc@baylibre.com>

On Sun, Mar 01, 2026 at 05:46:48PM -0600, David Lechner wrote:
> Make use of ext_scan_type to handle the dynamic realbits size of the
> quaternion data. This lets us implement it using static data rather than
> having to duplicate the channel info for each driver instance.

I like this change!
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
(assuming somebody from Intel tests and confirms it working)

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH 06/10] dt-bindings: input: touchscreen: st,stmfts: Introduce reset GPIO
From: Krzysztof Kozlowski @ 2026-03-02  7:17 UTC (permalink / raw)
  To: David Heidelberg
  Cc: Dmitry Torokhov, 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, devicetree,
	linux-arm-msm, phone-devel
In-Reply-To: <20260301-stmfts5-v1-6-22c458b9ac68@ixit.cz>

On Sun, Mar 01, 2026 at 06:51:20PM +0100, David Heidelberg wrote:
> FTS may have associated reset GPIO, document it.

Commit msg is wrong. May have or have? Hardware is fixed, unless you add
the GPIO for driver which does not exist in the hardware.

> 
> Signed-off-by: David Heidelberg <david@ixit.cz>
> ---
>  Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml | 4 ++++
>  1 file changed, 4 insertions(+)

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v2 1/4] Input: elo - fix style issues
From: Josh Law @ 2026-03-02  7:13 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, linux-kernel, Josh Law
In-Reply-To: <aaTYvqdymKquoVWJ@google.com>

2 Mar 2026 00:25:16 Dmitry Torokhov <dmitry.torokhov@gmail.com>:

> Hi Josh,
>
> On Sat, Feb 28, 2026 at 07:52:58PM +0000, Josh Law wrote:
>> Fix array constness and spacing issues reported by checkpatch.
>>
>> Signed-off-by: Josh Law <objecting@objecting.org>
>
> Why the difference between From: and Signed-off-by?
>
>> ---
>> drivers/input/touchscreen/elo.c | 4 ++--
>> 1 file changed, 2 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c
>> index 434b9b47e964..40ae132cde21 100644
>> --- a/drivers/input/touchscreen/elo.c
>> +++ b/drivers/input/touchscreen/elo.c
>> @@ -257,7 +257,7 @@ static int elo_command_10(struct elo *elo, unsigned char *packet)
>>
>> static int elo_setup_10(struct elo *elo)
>> {
>> -   static const char *elo_types[] = { "Accu", "Dura", "Intelli", "Carroll" };
>> +   static const char * const elo_types[] = { "Accu", "Dura", "Intelli", "Carroll" };
>>     struct input_dev *dev = elo->dev;
>>     unsigned char packet[ELO10_PACKET_LEN] = { ELO10_ID_CMD };
>>
>> @@ -273,7 +273,7 @@ static int elo_setup_10(struct elo *elo)
>>
>>     dev_info(&elo->serio->dev,
>>          "%sTouch touchscreen, fw: %02x.%02x, features: 0x%02x, controller: 0x%02x\n",
>> -        elo_types[(packet[1] -'0') & 0x03],
>> +        elo_types[(packet[1] - '0') & 0x03],
>>          packet[5], packet[4], packet[3], packet[7]);
>>
>>     return 0;
>
> Thanks.
>
> --
> Dmitry



Sorry, because my main email can't send emails and I prefer to use my objecting.org email

V/R

^ permalink raw reply

* Re: [PATCH][next] HID: hid-lenovo-go-s: Fix spelling mistake "configuratiion" -> "configuration"
From: Mark Pearson @ 2026-03-02  1:34 UTC (permalink / raw)
  To: Colin Ian King, Derek J . Clark, Jiri Kosina, Benjamin Tissoires,
	linux-input
  Cc: kernel-janitors, linux-kernel
In-Reply-To: <20260227231606.421263-1-colin.i.king@gmail.com>

On Fri, Feb 27, 2026, at 6:16 PM, Colin Ian King wrote:
> There is a spelling mistake in a dev_err_probe message. Fix it.
>
> Signed-off-by: Colin Ian King <colin.i.king@gmail.com>
> ---
>  drivers/hid/hid-lenovo-go-s.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/hid/hid-lenovo-go-s.c b/drivers/hid/hid-lenovo-go-s.c
> index cacc5bd5ed2b..dbb88492fbba 100644
> --- a/drivers/hid/hid-lenovo-go-s.c
> +++ b/drivers/hid/hid-lenovo-go-s.c
> @@ -1401,7 +1401,7 @@ static int hid_gos_cfg_probe(struct hid_device *hdev,
>  	ret = devm_device_add_group(gos_cdev_rgb.led_cdev.dev, &rgb_attr_group);
>  	if (ret) {
>  		dev_err_probe(&hdev->dev, ret,
> -			      "Failed to create RGB configuratiion attributes\n");
> +			      "Failed to create RGB configuration attributes\n");
>  		return ret;
>  	}
> 
> -- 
> 2.51.0

Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>

^ permalink raw reply

* Re: [PATCH] HID: hid-lenovo-go: Remove unneeded semicolon
From: Mark Pearson @ 2026-03-02  1:33 UTC (permalink / raw)
  To: Chen Ni, Derek J . Clark
  Cc: Jiri Kosina, Benjamin Tissoires, linux-input, linux-kernel
In-Reply-To: <20260228033936.285945-1-nichen@iscas.ac.cn>

On Fri, Feb 27, 2026, at 10:39 PM, Chen Ni wrote:
> Remove unnecessary semicolons after switch statements and function
> bodies. Most issues were reported by Coccinelle/coccicheck using the
> semantic patch at scripts/coccinelle/misc/semicolon.cocci. Additional
> instances found during manual code review were also fixed.
>
> Signed-off-by: Chen Ni <nichen@iscas.ac.cn>
> ---
>  drivers/hid/hid-lenovo-go.c | 52 ++++++++++++++++++-------------------
>  1 file changed, 26 insertions(+), 26 deletions(-)
>
> diff --git a/drivers/hid/hid-lenovo-go.c b/drivers/hid/hid-lenovo-go.c
> index 6972d13802e2..77e3823447e5 100644
> --- a/drivers/hid/hid-lenovo-go.c
> +++ b/drivers/hid/hid-lenovo-go.c
> @@ -455,7 +455,7 @@ static int hid_go_feature_status_event(struct 
> command_report *cmd_rep)
>  			return 0;
>  		default:
>  			return -EINVAL;
> -		};
> +		}
>  	case FEATURE_IMU_BYPASS:
>  		switch (cmd_rep->device_type) {
>  		case LEFT_CONTROLLER:
> @@ -466,7 +466,7 @@ static int hid_go_feature_status_event(struct 
> command_report *cmd_rep)
>  			return 0;
>  		default:
>  			return -EINVAL;
> -		};
> +		}
>  		break;
>  	case FEATURE_LIGHT_ENABLE:
>  		drvdata.rgb_en = cmd_rep->data[0];
> @@ -481,7 +481,7 @@ static int hid_go_feature_status_event(struct 
> command_report *cmd_rep)
>  			return 0;
>  		default:
>  			return -EINVAL;
> -		};
> +		}
>  		break;
>  	case FEATURE_TOUCHPAD_ENABLE:
>  		drvdata.tp_en = cmd_rep->data[0];
> @@ -515,7 +515,7 @@ static int hid_go_motor_event(struct command_report 
> *cmd_rep)
>  			return 0;
>  		default:
>  			return -EINVAL;
> -		};
> +		}
>  		break;
>  	case RUMBLE_MODE:
>  		switch (cmd_rep->device_type) {
> @@ -527,7 +527,7 @@ static int hid_go_motor_event(struct command_report 
> *cmd_rep)
>  			return 0;
>  		default:
>  			return -EINVAL;
> -		};
> +		}
>  	case TP_VIBRATION_ENABLE:
>  		drvdata.tp_vibration_en = cmd_rep->data[0];
>  		return 0;
> @@ -625,7 +625,7 @@ static int hid_go_os_mode_cfg_event(struct 
> command_report *cmd_rep)
>  		return 0;
>  	default:
>  		return -EINVAL;
> -	};
> +	}
>  }
> 
>  static int hid_go_set_event_return(struct command_report *cmd_rep)
> @@ -699,14 +699,14 @@ static int hid_go_raw_event(struct hid_device 
> *hdev, struct hid_report *report,
>  		default:
>  			ret = -EINVAL;
>  			break;
> -		};
> +		}
>  		break;
>  	case OS_MODE_DATA:
>  		ret = hid_go_os_mode_cfg_event(cmd_rep);
>  		break;
>  	default:
>  		goto passthrough;
> -	};
> +	}
>  	dev_dbg(&hdev->dev, "Rx data as raw input report: [%*ph]\n",
>  		GO_PACKET_SIZE, data);
> 
> @@ -925,7 +925,7 @@ static ssize_t feature_status_store(struct device *dev,
>  		break;
>  	default:
>  		return -EINVAL;
> -	};
> +	}
> 
>  	if (ret < 0)
>  		return ret;
> @@ -1013,7 +1013,7 @@ static ssize_t feature_status_show(struct device *dev,
>  			break;
>  		default:
>  			return -EINVAL;
> -		};
> +		}
>  		count = sysfs_emit(buf, "%u\n", i);
>  		break;
>  	case FEATURE_FPS_SWITCH_STATUS:
> @@ -1032,7 +1032,7 @@ static ssize_t feature_status_show(struct device *dev,
>  		break;
>  	default:
>  		return -EINVAL;
> -	};
> +	}
> 
>  	return count;
>  }
> @@ -1070,7 +1070,7 @@ static ssize_t feature_status_options(struct device *dev,
>  		break;
>  	default:
>  		return -EINVAL;
> -	};
> +	}
> 
>  	if (count)
>  		buf[count - 1] = '\n';
> @@ -1111,7 +1111,7 @@ static ssize_t motor_config_store(struct device *dev,
>  		ret = sysfs_match_string(intensity_text, buf);
>  		val = ret;
>  		break;
> -	};
> +	}
> 
>  	if (ret < 0)
>  		return ret;
> @@ -1161,7 +1161,7 @@ static ssize_t motor_config_show(struct device *dev,
>  			break;
>  		default:
>  			return -EINVAL;
> -		};
> +		}
>  		if (i >= ARRAY_SIZE(enabled_status_text))
>  			return -EINVAL;
> 
> @@ -1177,7 +1177,7 @@ static ssize_t motor_config_show(struct device *dev,
>  			break;
>  		default:
>  			return -EINVAL;
> -		};
> +		}
>  		if (i >= ARRAY_SIZE(rumble_mode_text))
>  			return -EINVAL;
> 
> @@ -1197,7 +1197,7 @@ static ssize_t motor_config_show(struct device *dev,
> 
>  		count = sysfs_emit(buf, "%s\n", intensity_text[i]);
>  		break;
> -	};
> +	}
> 
>  	return count;
>  }
> @@ -1232,7 +1232,7 @@ static ssize_t motor_config_options(struct device *dev,
>  					       enabled_status_text[i]);
>  		}
>  		break;
> -	};
> +	}
> 
>  	if (count)
>  		buf[count - 1] = '\n';
> @@ -1333,7 +1333,7 @@ static ssize_t device_status_show(struct device *dev,
>  		break;
>  	default:
>  		return -EINVAL;
> -	};
> +	}
> 
>  	if (i >= ARRAY_SIZE(cal_status_text))
>  		return -EINVAL;
> @@ -1459,7 +1459,7 @@ static int rgb_attr_show(void)
>  	index = drvdata.rgb_profile + 3;
> 
>  	return rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, index, 0, 0);
> -};
> +}
> 
>  static ssize_t rgb_effect_store(struct device *dev,
>  				struct device_attribute *attr, const char *buf,
> @@ -1489,7 +1489,7 @@ static ssize_t rgb_effect_store(struct device *dev,
> 
>  	drvdata.rgb_effect = effect;
>  	return count;
> -};
> +}
> 
>  static ssize_t rgb_effect_show(struct device *dev,
>  			       struct device_attribute *attr, char *buf)
> @@ -1552,7 +1552,7 @@ static ssize_t rgb_speed_store(struct device *dev,
>  	drvdata.rgb_speed = val;
> 
>  	return count;
> -};
> +}
> 
>  static ssize_t rgb_speed_show(struct device *dev, struct 
> device_attribute *attr,
>  			      char *buf)
> @@ -1594,7 +1594,7 @@ static ssize_t rgb_mode_store(struct device *dev, 
> struct device_attribute *attr,
>  	drvdata.rgb_mode = val;
> 
>  	return count;
> -};
> +}
> 
>  static ssize_t rgb_mode_show(struct device *dev, struct 
> device_attribute *attr,
>  			     char *buf)
> @@ -1609,7 +1609,7 @@ static ssize_t rgb_mode_show(struct device *dev, 
> struct device_attribute *attr,
>  		return -EINVAL;
> 
>  	return sysfs_emit(buf, "%s\n", rgb_mode_text[drvdata.rgb_mode]);
> -};
> +}
> 
>  static ssize_t rgb_mode_index_show(struct device *dev,
>  				   struct device_attribute *attr, char *buf)
> @@ -1649,7 +1649,7 @@ static ssize_t rgb_profile_store(struct device *dev,
>  	drvdata.rgb_profile = val;
> 
>  	return count;
> -};
> +}
> 
>  static ssize_t rgb_profile_show(struct device *dev,
>  				struct device_attribute *attr, char *buf)
> @@ -1665,7 +1665,7 @@ static ssize_t rgb_profile_show(struct device *dev,
>  		return -EINVAL;
> 
>  	return sysfs_emit(buf, "%hhu\n", drvdata.rgb_profile);
> -};
> +}
> 
>  static ssize_t rgb_profile_range_show(struct device *dev,
>  				      struct device_attribute *attr, char *buf)
> @@ -1704,7 +1704,7 @@ static void hid_go_brightness_set(struct 
> led_classdev *led_cdev,
>  		break;
>  	default:
>  		dev_err(led_cdev->dev, "Failed to write RGB profile: %i\n", ret);
> -	};
> +	}
>  }
> 
>  #define LEGO_DEVICE_ATTR_RW(_name, _attrname, _dtype, _rtype, _group)         \
> -- 
> 2.25.1

Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>

^ permalink raw reply

* Re: [PATCH] HID: hid-lenovo-go-s: Remove unneeded semicolon
From: Mark Pearson @ 2026-03-02  1:32 UTC (permalink / raw)
  To: Chen Ni, Derek J . Clark
  Cc: Jiri Kosina, Benjamin Tissoires, linux-input, linux-kernel
In-Reply-To: <20260228034343.286005-1-nichen@iscas.ac.cn>

On Fri, Feb 27, 2026, at 10:43 PM, Chen Ni wrote:
> Remove unnecessary semicolons reported by Coccinelle/coccicheck and the
> semantic patch at scripts/coccinelle/misc/semicolon.cocci.
>
> Signed-off-by: Chen Ni <nichen@iscas.ac.cn>
> ---
>  drivers/hid/hid-lenovo-go-s.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/hid/hid-lenovo-go-s.c 
> b/drivers/hid/hid-lenovo-go-s.c
> index cacc5bd5ed2b..d1eb067509f6 100644
> --- a/drivers/hid/hid-lenovo-go-s.c
> +++ b/drivers/hid/hid-lenovo-go-s.c
> @@ -1102,7 +1102,7 @@ static void hid_gos_brightness_set(struct 
> led_classdev *led_cdev,
>  	default:
>  		dev_err(led_cdev->dev, "Failed to write RGB profile: %i\n",
>  			ret);
> -	};
> +	}
>  }
> 
>  #define LEGOS_DEVICE_ATTR_RW(_name, _attrname, _rtype, _group)         
>         \
> -- 
> 2.25.1

Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>

^ permalink raw reply

* Re: [PATCH 2/2] HID: input: Add HID_BATTERY_QUIRK_DYNAMIC for Elan touchscreens
From: Dmitry Torokhov @ 2026-03-02  0:44 UTC (permalink / raw)
  To: Hans de Goede, Sebastian Reichel
  Cc: Jiri Kosina, Benjamin Tissoires, linux-input, ggrundik
In-Reply-To: <20260228145258.76937-2-johannes.goede@oss.qualcomm.com>

Hi Hans,

On Sat, Feb 28, 2026 at 03:52:58PM +0100, Hans de Goede wrote:
> Elan touchscreens have a HID-battery device for the stylus which is always
> there even if there is no stylus.
> 
> This is causing upower to report an empty battery for the stylus and some
> desktop-environments will show a notification about this, which is quite
> annoying.
> 
> Because of this the HID-battery is being ignored on all Elan I2c and USB
> touchscreens, but this causes there to be no battery reporting for
> the stylus at all.
> 
> This adds a new HID_BATTERY_QUIRK_DYNAMIC and uses these for the Elan
> touchscreens.
> 
> This new quirks causes the present value of the battery to start at 0,
> which will make userspace ignore it and only sets present to 1 after
> receiving a battery input report which only happens when the stylus
> gets in range.

My understanding was that "present" attribute is to be used for
removable batteries (i.e. when a battery can be extracted from either
from the system or from a peripheral) and we want to notify userspace of
that fact). For example, if a laptop can have an additional battery UI
can show if it is installed or not, and it should not simply ignore such
power supplies.

On the other hand the "Unknown" operating status signals that we
actually do not know the state of the battery, and fits better in our
situation.

Anyway, I think we need Sebastian's input here.

> 
> Reported-by: ggrundik@gmail.com
> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221118
> Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>
> ---
>  drivers/hid/hid-input.c | 14 +++++++++++---
>  include/linux/hid.h     |  1 +
>  2 files changed, 12 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
> index 67ca1e88ce13..8fc20df99b97 100644
> --- a/drivers/hid/hid-input.c
> +++ b/drivers/hid/hid-input.c
> @@ -354,6 +354,7 @@ static enum power_supply_property hidinput_battery_props[] = {
>  #define HID_BATTERY_QUIRK_FEATURE	(1 << 1) /* ask for feature report */
>  #define HID_BATTERY_QUIRK_IGNORE	(1 << 2) /* completely ignore the battery */
>  #define HID_BATTERY_QUIRK_AVOID_QUERY	(1 << 3) /* do not query the battery */
> +#define HID_BATTERY_QUIRK_DYNAMIC	(1 << 4) /* report present only after life signs */
>  
>  static const struct hid_device_id hid_battery_quirks[] = {
>  	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
> @@ -398,8 +399,8 @@ static const struct hid_device_id hid_battery_quirks[] = {
>  	 * Elan HID touchscreens seem to all report a non present battery,
>  	 * set HID_BATTERY_QUIRK_IGNORE for all Elan I2C and USB HID devices.
>  	 */
> -	{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_IGNORE },
> -	{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_IGNORE },
> +	{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_DYNAMIC },
> +	{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_DYNAMIC },
>  	{}
>  };
>  
> @@ -456,11 +457,14 @@ static int hidinput_get_battery_property(struct power_supply *psy,
>  	int ret = 0;
>  
>  	switch (prop) {
> -	case POWER_SUPPLY_PROP_PRESENT:
>  	case POWER_SUPPLY_PROP_ONLINE:
>  		val->intval = 1;
>  		break;
>  
> +	case POWER_SUPPLY_PROP_PRESENT:
> +		val->intval = dev->battery_present;
> +		break;
> +
>  	case POWER_SUPPLY_PROP_CAPACITY:
>  		if (dev->battery_status != HID_BATTERY_REPORTED &&
>  		    !dev->battery_avoid_query) {
> @@ -573,6 +577,8 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
>  	if (quirks & HID_BATTERY_QUIRK_AVOID_QUERY)
>  		dev->battery_avoid_query = true;
>  
> +	dev->battery_present = (quirks & HID_BATTERY_QUIRK_DYNAMIC) ? false : true;
> +
>  	dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg);
>  	if (IS_ERR(dev->battery)) {
>  		error = PTR_ERR(dev->battery);
> @@ -628,6 +634,7 @@ static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
>  		return;
>  
>  	if (hidinput_update_battery_charge_status(dev, usage, value)) {
> +		dev->battery_present = true;
>  		power_supply_changed(dev->battery);
>  		return;
>  	}
> @@ -643,6 +650,7 @@ static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
>  	if (dev->battery_status != HID_BATTERY_REPORTED ||
>  	    capacity != dev->battery_capacity ||
>  	    ktime_after(ktime_get_coarse(), dev->battery_ratelimit_time)) {
> +		dev->battery_present = true;
>  		dev->battery_capacity = capacity;
>  		dev->battery_status = HID_BATTERY_REPORTED;
>  		dev->battery_ratelimit_time =
> diff --git a/include/linux/hid.h b/include/linux/hid.h
> index dce862cafbbd..d9b54f0e8671 100644
> --- a/include/linux/hid.h
> +++ b/include/linux/hid.h
> @@ -682,6 +682,7 @@ struct hid_device {
>  	__s32 battery_charge_status;
>  	enum hid_battery_status battery_status;
>  	bool battery_avoid_query;
> +	bool battery_present;
>  	ktime_t battery_ratelimit_time;
>  #endif
>  

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH v2 1/4] Input: elo - fix style issues
From: Dmitry Torokhov @ 2026-03-02  0:25 UTC (permalink / raw)
  To: Josh Law; +Cc: linux-input, linux-kernel, Josh Law
In-Reply-To: <20260228195258.2468296-1-objecting@objecting.org>

Hi Josh,

On Sat, Feb 28, 2026 at 07:52:58PM +0000, Josh Law wrote:
> Fix array constness and spacing issues reported by checkpatch.
> 
> Signed-off-by: Josh Law <objecting@objecting.org>

Why the difference between From: and Signed-off-by?

> ---
>  drivers/input/touchscreen/elo.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c
> index 434b9b47e964..40ae132cde21 100644
> --- a/drivers/input/touchscreen/elo.c
> +++ b/drivers/input/touchscreen/elo.c
> @@ -257,7 +257,7 @@ static int elo_command_10(struct elo *elo, unsigned char *packet)
>  
>  static int elo_setup_10(struct elo *elo)
>  {
> -	static const char *elo_types[] = { "Accu", "Dura", "Intelli", "Carroll" };
> +	static const char * const elo_types[] = { "Accu", "Dura", "Intelli", "Carroll" };
>  	struct input_dev *dev = elo->dev;
>  	unsigned char packet[ELO10_PACKET_LEN] = { ELO10_ID_CMD };
>  
> @@ -273,7 +273,7 @@ static int elo_setup_10(struct elo *elo)
>  
>  	dev_info(&elo->serio->dev,
>  		 "%sTouch touchscreen, fw: %02x.%02x, features: 0x%02x, controller: 0x%02x\n",
> -		 elo_types[(packet[1] -'0') & 0x03],
> +		 elo_types[(packet[1] - '0') & 0x03],
>  		 packet[5], packet[4], packet[3], packet[7]);
>  
>  	return 0;

Thanks.

-- 
Dmitry

^ permalink raw reply

* [PATCH v2] iio: orientation: hid-sensor-rotation: use ext_scan_type
From: David Lechner @ 2026-03-01 23:46 UTC (permalink / raw)
  To: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, Nuno Sá,
	Andy Shevchenko
  Cc: linux-input, linux-iio, linux-kernel, David Lechner

Make use of ext_scan_type to handle the dynamic realbits size of the
quaternion data. This lets us implement it using static data rather than
having to duplicate the channel info for each driver instance.

Signed-off-by: David Lechner <dlechner@baylibre.com>
---
This is something I noticed we could do while looking at an unrelated
bug. I've tested this using the same script from [1] and confirmed that
that the scan type didn't change. Before and after are both:

$ cat in_rot_quaternion_type
le:s16/32X4>>0

[1]: https://lore.kernel.org/linux-iio/20260301-iio-fix-timestamp-alignment-v1-1-1a54980bfb90@baylibre.com/
---
Changes in v2:
- Dropped DEV_ROT_SCAN_TYPE_8BIT.
- Tested using /dev/uhid.
- Link to v1: https://lore.kernel.org/r/20260214-iio-hid-sensor-rotation-cleanup-v1-1-3aec9a533c0f@baylibre.com
---
 drivers/iio/orientation/hid-sensor-rotation.c | 71 ++++++++++++++++-----------
 1 file changed, 43 insertions(+), 28 deletions(-)

diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c
index e759f91a710a..3cfd0b323514 100644
--- a/drivers/iio/orientation/hid-sensor-rotation.c
+++ b/drivers/iio/orientation/hid-sensor-rotation.c
@@ -34,6 +34,27 @@ static const u32 rotation_sensitivity_addresses[] = {
 	HID_USAGE_SENSOR_ORIENT_QUATERNION,
 };
 
+enum {
+	DEV_ROT_SCAN_TYPE_16BIT,
+	DEV_ROT_SCAN_TYPE_32BIT,
+};
+
+static const struct iio_scan_type dev_rot_scan_types[] = {
+	[DEV_ROT_SCAN_TYPE_16BIT] = {
+		.sign = 's',
+		.realbits = 16,
+		/* Storage bits has to stay 32 to not break userspace. */
+		.storagebits = 32,
+		.repeat = 4,
+	},
+	[DEV_ROT_SCAN_TYPE_32BIT] = {
+		.sign = 's',
+		.realbits = 32,
+		.storagebits = 32,
+		.repeat = 4,
+	},
+};
+
 /* Channel definitions */
 static const struct iio_chan_spec dev_rot_channels[] = {
 	{
@@ -45,23 +66,14 @@ static const struct iio_chan_spec dev_rot_channels[] = {
 					BIT(IIO_CHAN_INFO_OFFSET) |
 					BIT(IIO_CHAN_INFO_SCALE) |
 					BIT(IIO_CHAN_INFO_HYSTERESIS),
-		.scan_index = 0
+		.scan_index = 0,
+		.has_ext_scan_type = 1,
+		.ext_scan_type = dev_rot_scan_types,
+		.num_ext_scan_type = ARRAY_SIZE(dev_rot_scan_types),
 	},
 	IIO_CHAN_SOFT_TIMESTAMP(1)
 };
 
-/* Adjust channel real bits based on report descriptor */
-static void dev_rot_adjust_channel_bit_mask(struct iio_chan_spec *chan,
-						int size)
-{
-	chan->scan_type.sign = 's';
-	/* Real storage bits will change based on the report desc. */
-	chan->scan_type.realbits = size * 8;
-	/* Maximum size of a sample to capture is u32 */
-	chan->scan_type.storagebits = sizeof(u32) * 8;
-	chan->scan_type.repeat = 4;
-}
-
 /* Channel read_raw handler */
 static int dev_rot_read_raw(struct iio_dev *indio_dev,
 				struct iio_chan_spec const *chan,
@@ -136,9 +148,25 @@ static int dev_rot_write_raw(struct iio_dev *indio_dev,
 	return ret;
 }
 
+static int dev_rot_get_current_scan_type(const struct iio_dev *indio_dev,
+					 const struct iio_chan_spec *chan)
+{
+	struct dev_rot_state *rot_state = iio_priv(indio_dev);
+
+	switch (rot_state->quaternion.size / 4) {
+	case sizeof(s16):
+		return DEV_ROT_SCAN_TYPE_16BIT;
+	case sizeof(s32):
+		return DEV_ROT_SCAN_TYPE_32BIT;
+	default:
+		return -EINVAL;
+	}
+}
+
 static const struct iio_info dev_rot_info = {
 	.read_raw_multi = &dev_rot_read_raw,
 	.write_raw = &dev_rot_write_raw,
+	.get_current_scan_type = &dev_rot_get_current_scan_type,
 };
 
 /* Callback handler to send event after all samples are received and captured */
@@ -196,7 +224,6 @@ static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev,
 /* Parse report which is specific to an usage id*/
 static int dev_rot_parse_report(struct platform_device *pdev,
 				struct hid_sensor_hub_device *hsdev,
-				struct iio_chan_spec *channels,
 				unsigned usage_id,
 				struct dev_rot_state *st)
 {
@@ -210,9 +237,6 @@ static int dev_rot_parse_report(struct platform_device *pdev,
 	if (ret)
 		return ret;
 
-	dev_rot_adjust_channel_bit_mask(&channels[0],
-		st->quaternion.size / 4);
-
 	dev_dbg(&pdev->dev, "dev_rot %x:%x\n", st->quaternion.index,
 		st->quaternion.report_id);
 
@@ -271,22 +295,13 @@ static int hid_dev_rot_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	indio_dev->channels = devm_kmemdup(&pdev->dev, dev_rot_channels,
-					   sizeof(dev_rot_channels),
-					   GFP_KERNEL);
-	if (!indio_dev->channels) {
-		dev_err(&pdev->dev, "failed to duplicate channels\n");
-		return -ENOMEM;
-	}
-
-	ret = dev_rot_parse_report(pdev, hsdev,
-				   (struct iio_chan_spec *)indio_dev->channels,
-					hsdev->usage, rot_state);
+	ret = dev_rot_parse_report(pdev, hsdev, hsdev->usage, rot_state);
 	if (ret) {
 		dev_err(&pdev->dev, "failed to setup attributes\n");
 		return ret;
 	}
 
+	indio_dev->channels = dev_rot_channels;
 	indio_dev->num_channels = ARRAY_SIZE(dev_rot_channels);
 	indio_dev->info = &dev_rot_info;
 	indio_dev->name = name;

---
base-commit: 3fa5e5702a82d259897bd7e209469bc06368bf31
change-id: 20260214-iio-hid-sensor-rotation-cleanup-84e8410926ef

Best regards,
-- 
David Lechner <dlechner@baylibre.com>


^ permalink raw reply related

* Re: [PATCH 08/10] dt-bindings: input: touchscreen: st,stmfts: Introduce STM FTS5
From: Dmitry Baryshkov @ 2026-03-01 22:40 UTC (permalink / raw)
  To: david
  Cc: Dmitry Torokhov, 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
In-Reply-To: <20260301-stmfts5-v1-8-22c458b9ac68@ixit.cz>

On Sun, Mar 01, 2026 at 06:51:22PM +0100, David Heidelberg via B4 Relay wrote:
> From: David Heidelberg <david@ixit.cz>
> 
> Introduce more recent STM FTS5 touchscreen support.
> 
> Signed-off-by: David Heidelberg <david@ixit.cz>
> ---
>  .../bindings/input/touchscreen/st,stmfts.yaml           | 17 ++++++++++++++++-
>  1 file changed, 16 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml b/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml
> index 64c4f24ea3dd0..329d89977bdbc 100644
> --- a/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml
> +++ b/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml
> @@ -19,7 +19,9 @@ allOf:
>  
>  properties:
>    compatible:
> -    const: st,stmfts
> +    enum:
> +      - st,stmfts
> +      - st,stmfts5
>  
>    reg:
>      maxItems: 1
> @@ -53,6 +55,19 @@ required:
>  
>  unevaluatedProperties: false
>  
> +allOf:
> +  - if:
> +      properties:
> +        compatible:
> +          const: st,stmfts5
> +    then:
> +      properties:
> +        switch-gpio:
> +          description: Switch between SLPI and AP mode.

This doesn't sounds like the GPIO on the touchscreen, more like the
external schematic component. If it need sto be turned to one position,
it might be better to use GPIO hog for that.

> +
> +      required:
> +        - switch-gpio
> +
>  examples:
>    - |
>      #include <dt-bindings/interrupt-controller/irq.h>
> 
> -- 
> 2.51.0
> 
> 

-- 
With best wishes
Dmitry

^ permalink raw reply

* Re: [PATCH] iio: orientation: hid-sensor-rotation: use ext_scan_type
From: David Lechner @ 2026-03-01 22:12 UTC (permalink / raw)
  To: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, Nuno Sá,
	Andy Shevchenko
  Cc: linux-input, linux-iio, linux-kernel
In-Reply-To: <20260214-iio-hid-sensor-rotation-cleanup-v1-1-3aec9a533c0f@baylibre.com>

On 2/14/26 3:25 PM, David Lechner wrote:
> Make use of ext_scan_type to handle the dynamic realbits size of the
> quaternion data. This lets us implement it using static data rather than
> having to duplicate the channel info for each driver instance.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
> ---
> This is something I noticed we could do while looking at an unrelated
> bug. I'm not sure I have hardware I could test this on, so it is only
> compile-tested. It would be good to get a Tested-by: from someone before
> applying this. Also, I'm not sure if 8, 16 and 32-bit data are all
> possible, so if someone knows, please chime in.
While looking at some bugs in this driver, I found in the code where
we can be certain that only 16 and 32-bit are used. And I figured out
a way to test with /dev/uhid. So I will drop the 8-bit case, test it
and send a v2.


^ permalink raw reply

* Re: [PATCH 09/10] Input: stmfts - support FTS5
From: Dmitry Torokhov @ 2026-03-01 22:10 UTC (permalink / raw)
  To: David Heidelberg via B4 Relay
  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, David Heidelberg
In-Reply-To: <20260301-stmfts5-v1-9-22c458b9ac68@ixit.cz>

Hi David,

On Sun, Mar 01, 2026 at 06:51:23PM +0100, David Heidelberg via B4 Relay wrote:
> From: Petr Hodina <petr.hodina@protonmail.com>
> 
> Introduce basic FTS5 support.
> 
> FTS support SLPI and AP mode, introduce switch GPIO to switch between
> those two. Currently we can handle only full power AP mode, so we just
> switch to it.
> 
> Useful for devices like Pixel 3 (blueline).

I see "is_fts5" sprinkled throughout the code. I wonder: can we define
chip-specific operations and call them instead of doing conditional
logic which is hard to expand (if ever needed) to support additional
variants.
> 
> Nitpick: changed GPL v2 to GPL in module license.

Please split in to a separate patch.

Thanks.

-- 
Dmitry

^ permalink raw reply

* [PATCH 4/4] iio: buffer: fix timestamp alignment when quaternion in scan
From: David Lechner @ 2026-03-01 20:24 UTC (permalink / raw)
  To: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, Nuno Sá,
	Andy Shevchenko, Lars Möllendorf, Lars-Peter Clausen,
	Greg Kroah-Hartman
  Cc: Jonathan Cameron, Lixu Zhang, linux-input, linux-iio,
	linux-kernel, David Lechner
In-Reply-To: <20260301-iio-fix-timestamp-alignment-v1-0-1a54980bfb90@baylibre.com>

Fix timestamp alignment when a scan buffer contains an element larger
than sizeof(int64_t). Currently s32 quaternions are the only such
element, and the one driver that has this (hid-sensor-rotation) has a
workaround in place already so this change does not affect it.

Previously, we assumed that the timestamp would always be 8-byte aligned
relative to the end of the scan buffer, but in the case of a scan buffer
a 16-byte quaternion vector, scan_bytes == 32, but the timestamp needs
to be placed at offset 16, not 24.

Signed-off-by: David Lechner <dlechner@baylibre.com>
---

To test this, I used hid-sensor-rotation minus the first patch in the
series so that we can see that the timestamp actually moved to the
correct location.

Before this patch, the timestamp (8 bytes ending with "98 18") is in the
wrong location.

00000000  6a 18 00 00 ac f3 ff ff  83 2d 00 00 02 d3 ff ff  |j........-......|
00000010  00 00 00 00 00 00 00 00  5a 17 a0 2a 73 cb 98 18  |........Z..*s...|

00000020  ad 17 00 00 6a f4 ff ff  35 2b 00 00 ca d0 ff ff  |....j...5+......|
00000030  00 00 00 00 00 00 00 00  2a a6 bb 30 73 cb 98 18  |........*..0s...|

00000040  92 1e 00 00 50 ec ff ff  ea c1 ff ff 78 f0 ff ff  |....P.......x...|
00000050  00 00 00 00 00 00 00 00  8f 3b a7 39 77 cb 98 18  |.........;.9w...|

After this patch, timestamp is now in the correct location.

00000000  55 0f 00 00 dd 1f 00 00  af 0b 00 00 ec 3e 00 00  |U............>..|
00000010  c7 17 68 42 6d d0 98 18  00 00 00 00 00 00 00 00  |..hBm...........|

00000020  57 0e 00 00 c8 1f 00 00  d1 0e 00 00 42 3e 00 00  |W...........B>..|
00000030  56 a2 87 48 6d d0 98 18  00 00 00 00 00 00 00 00  |V..Hm...........|

00000040  a3 e2 ff ff d3 1b 00 00  0b c9 ff ff cc 20 00 00  |............. ..|
00000050  27 59 4d b3 72 d0 98 18  00 00 00 00 00 00 00 00  |'YM.r...........|

I also tested this with a different driver not affected by this bug to
make sure that the timestamp is still in the correct location for all
other drivers.
---
 include/linux/iio/buffer.h | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h
index d37f82678f71..ac19b39bdbe4 100644
--- a/include/linux/iio/buffer.h
+++ b/include/linux/iio/buffer.h
@@ -34,8 +34,16 @@ static inline int iio_push_to_buffers_with_timestamp(struct iio_dev *indio_dev,
 	void *data, int64_t timestamp)
 {
 	if (ACCESS_PRIVATE(indio_dev, scan_timestamp)) {
-		size_t ts_offset = indio_dev->scan_bytes / sizeof(int64_t) - 1;
-		((int64_t *)data)[ts_offset] = timestamp;
+		size_t ts_offset = indio_dev->scan_bytes -
+			ACCESS_PRIVATE(indio_dev, largest_scan_element_size);
+
+		/*
+		 * The size of indio_dev->scan_bytes is always aligned to
+		 * largest_scan_element_size (see iio_compute_scan_bytes()).
+		 * And this size is always going to be >= sizeof(timestamp).
+		 * So to correctly place the timestamp, it goes at this offset.
+		 */
+		*(int64_t *)(data + ts_offset) = timestamp;
 	}
 
 	return iio_push_to_buffers(indio_dev, data);

-- 
2.43.0


^ permalink raw reply related

* [PATCH 3/4] iio: buffer: cache largest scan element size
From: David Lechner @ 2026-03-01 20:24 UTC (permalink / raw)
  To: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, Nuno Sá,
	Andy Shevchenko, Lars Möllendorf, Lars-Peter Clausen,
	Greg Kroah-Hartman
  Cc: Jonathan Cameron, Lixu Zhang, linux-input, linux-iio,
	linux-kernel, David Lechner
In-Reply-To: <20260301-iio-fix-timestamp-alignment-v1-0-1a54980bfb90@baylibre.com>

Cache the largest scan element size of elements enabled in a scan
buffer. This will be used later to ensure proper alignment of the
timestamp element in the scan buffer.

The new field could not be placed in struct iio_dev_opaque because we
will need to access it in a static inline function later, so we make it
__private instead. It is only intended to be used by core IIO code.

Signed-off-by: David Lechner <dlechner@baylibre.com>
---
 drivers/iio/industrialio-buffer.c | 14 +++++++++++---
 include/linux/iio/iio.h           |  3 +++
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 71dfc81cb9e5..83e9392f949f 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -765,7 +765,8 @@ static int iio_storage_bytes_for_timestamp(struct iio_dev *indio_dev)
 
 static int iio_compute_scan_bytes(struct iio_dev *indio_dev,
 				  const unsigned long *mask, bool timestamp,
-				  unsigned int *scan_bytes)
+				  unsigned int *scan_bytes,
+				  unsigned int *largest_element_size)
 {
 	unsigned int bytes = 0;
 	int length, i, largest = 0;
@@ -793,6 +794,9 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev,
 
 	*scan_bytes = ALIGN(bytes, largest);
 
+	if (largest_element_size)
+		*largest_element_size = largest;
+
 	return 0;
 }
 
@@ -848,7 +852,7 @@ static int iio_buffer_update_bytes_per_datum(struct iio_dev *indio_dev,
 		return 0;
 
 	ret = iio_compute_scan_bytes(indio_dev, buffer->scan_mask,
-				     buffer->scan_timestamp, &bytes);
+				     buffer->scan_timestamp, &bytes, NULL);
 	if (ret)
 		return ret;
 
@@ -892,6 +896,7 @@ struct iio_device_config {
 	unsigned int watermark;
 	const unsigned long *scan_mask;
 	unsigned int scan_bytes;
+	unsigned int largest_scan_element_size;
 	bool scan_timestamp;
 };
 
@@ -997,7 +1002,8 @@ static int iio_verify_update(struct iio_dev *indio_dev,
 	}
 
 	ret = iio_compute_scan_bytes(indio_dev, scan_mask, scan_timestamp,
-				     &config->scan_bytes);
+				     &config->scan_bytes,
+				     &config->largest_scan_element_size);
 	if (ret)
 		return ret;
 
@@ -1155,6 +1161,8 @@ static int iio_enable_buffers(struct iio_dev *indio_dev,
 	indio_dev->active_scan_mask = config->scan_mask;
 	ACCESS_PRIVATE(indio_dev, scan_timestamp) = config->scan_timestamp;
 	indio_dev->scan_bytes = config->scan_bytes;
+	ACCESS_PRIVATE(indio_dev, largest_scan_element_size) =
+		config->largest_scan_element_size;
 	iio_dev_opaque->currentmode = config->mode;
 
 	iio_update_demux(indio_dev);
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index a9ecff191bd9..85bcb5f8ae15 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -584,6 +584,8 @@ struct iio_buffer_setup_ops {
  *			and owner
  * @buffer:		[DRIVER] any buffer present
  * @scan_bytes:		[INTERN] num bytes captured to be fed to buffer demux
+ * @largest_scan_element_size: [INTERN] cache of the largest scan element size
+ *			       among the channels selected in the scan mask
  * @available_scan_masks: [DRIVER] optional array of allowed bitmasks. Sort the
  *			   array in order of preference, the most preferred
  *			   masks first.
@@ -610,6 +612,7 @@ struct iio_dev {
 
 	struct iio_buffer		*buffer;
 	int				scan_bytes;
+	unsigned int			__private largest_scan_element_size;
 
 	const unsigned long		*available_scan_masks;
 	unsigned int			__private masklength;

-- 
2.43.0


^ permalink raw reply related

* [PATCH 2/4] iio: buffer: check return value of iio_compute_scan_bytes()
From: David Lechner @ 2026-03-01 20:24 UTC (permalink / raw)
  To: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, Nuno Sá,
	Andy Shevchenko, Lars Möllendorf, Lars-Peter Clausen,
	Greg Kroah-Hartman
  Cc: Jonathan Cameron, Lixu Zhang, linux-input, linux-iio,
	linux-kernel, David Lechner
In-Reply-To: <20260301-iio-fix-timestamp-alignment-v1-0-1a54980bfb90@baylibre.com>

Check return value of iio_compute_scan_bytes() as it can return an
error.

The result is moved to an output parameter while we are touching this
as we will need to add a second output parameter in a later change.

The return type of iio_buffer_update_bytes_per_datum() also had to be
changed to propagate the error.

Signed-off-by: David Lechner <dlechner@baylibre.com>
---
 drivers/iio/industrialio-buffer.c | 36 +++++++++++++++++++++++++-----------
 1 file changed, 25 insertions(+), 11 deletions(-)

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 46f36a6ed271..71dfc81cb9e5 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -764,7 +764,8 @@ static int iio_storage_bytes_for_timestamp(struct iio_dev *indio_dev)
 }
 
 static int iio_compute_scan_bytes(struct iio_dev *indio_dev,
-				  const unsigned long *mask, bool timestamp)
+				  const unsigned long *mask, bool timestamp,
+				  unsigned int *scan_bytes)
 {
 	unsigned int bytes = 0;
 	int length, i, largest = 0;
@@ -790,8 +791,9 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev,
 		largest = max(largest, length);
 	}
 
-	bytes = ALIGN(bytes, largest);
-	return bytes;
+	*scan_bytes = ALIGN(bytes, largest);
+
+	return 0;
 }
 
 static void iio_buffer_activate(struct iio_dev *indio_dev,
@@ -836,18 +838,23 @@ static int iio_buffer_disable(struct iio_buffer *buffer,
 	return buffer->access->disable(buffer, indio_dev);
 }
 
-static void iio_buffer_update_bytes_per_datum(struct iio_dev *indio_dev,
-					      struct iio_buffer *buffer)
+static int iio_buffer_update_bytes_per_datum(struct iio_dev *indio_dev,
+					     struct iio_buffer *buffer)
 {
 	unsigned int bytes;
+	int ret;
 
 	if (!buffer->access->set_bytes_per_datum)
-		return;
+		return 0;
 
-	bytes = iio_compute_scan_bytes(indio_dev, buffer->scan_mask,
-				       buffer->scan_timestamp);
+	ret = iio_compute_scan_bytes(indio_dev, buffer->scan_mask,
+				     buffer->scan_timestamp, &bytes);
+	if (ret)
+		return ret;
 
 	buffer->access->set_bytes_per_datum(buffer, bytes);
+
+	return 0;
 }
 
 static int iio_buffer_request_update(struct iio_dev *indio_dev,
@@ -855,7 +862,10 @@ static int iio_buffer_request_update(struct iio_dev *indio_dev,
 {
 	int ret;
 
-	iio_buffer_update_bytes_per_datum(indio_dev, buffer);
+	ret = iio_buffer_update_bytes_per_datum(indio_dev, buffer);
+	if (ret)
+		return ret;
+
 	if (buffer->access->request_update) {
 		ret = buffer->access->request_update(buffer);
 		if (ret) {
@@ -898,6 +908,7 @@ static int iio_verify_update(struct iio_dev *indio_dev,
 	struct iio_buffer *buffer;
 	bool scan_timestamp;
 	unsigned int modes;
+	int ret;
 
 	if (insert_buffer &&
 	    bitmap_empty(insert_buffer->scan_mask, masklength)) {
@@ -985,8 +996,11 @@ static int iio_verify_update(struct iio_dev *indio_dev,
 		scan_mask = compound_mask;
 	}
 
-	config->scan_bytes = iio_compute_scan_bytes(indio_dev,
-						    scan_mask, scan_timestamp);
+	ret = iio_compute_scan_bytes(indio_dev, scan_mask, scan_timestamp,
+				     &config->scan_bytes);
+	if (ret)
+		return ret;
+
 	config->scan_mask = scan_mask;
 	config->scan_timestamp = scan_timestamp;
 

-- 
2.43.0


^ permalink raw reply related

* [PATCH 1/4] iio: orientation: hid-sensor-rotation: add timestamp hack to not break userspace
From: David Lechner @ 2026-03-01 20:24 UTC (permalink / raw)
  To: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, Nuno Sá,
	Andy Shevchenko, Lars Möllendorf, Lars-Peter Clausen,
	Greg Kroah-Hartman
  Cc: Jonathan Cameron, Lixu Zhang, linux-input, linux-iio,
	linux-kernel, David Lechner
In-Reply-To: <20260301-iio-fix-timestamp-alignment-v1-0-1a54980bfb90@baylibre.com>

Add a hack to push two timestamps in the hid-sensor-rotation scan data
to avoid breaking userspace applications that depend on the timestamp
being at the incorrect location in the scan data due to unintentional
misalignment in older kernels.

When this driver was written, the timestamp was in the correct location
because of the way iio_compute_scan_bytes() was implemented at the time.
(Samples were 24 bytes each.) Then commit 883f61653069 ("iio: buffer:
align the size of scan bytes to size of the largest element") changed
the computed scan_bytes to be a different size (32 bytes), which caused
iio_push_to_buffers_with_timestamp() to place the timestamp at an
incorrect offset.

There have been long periods of time (6 years each) where the timestamp
was in either location, so to not break either case, we open-code the
timestamps to be pushed to both locations in the scan data.

Reported-by: Jonathan Cameron <jic23@kernel.org>
Closes: https://lore.kernel.org/linux-iio/20260215162351.79f40b32@jic23-huawei/
Fixes: 883f61653069 ("iio: buffer: align the size of scan bytes to size of the largest element")
Signed-off-by: David Lechner <dlechner@baylibre.com>
---

I found that I could emulate this thanks to /dev/uhid. And thanks to AI
code generators, I was able to reasonably quickly make a script that
worked for emulating "HID-SENSOR-20008a". I'll include the script at the
end.

I set up the buffer like this:

cd /sys/bus/iio/devices/iio:device1/buffer0
echo 1 > in_rot_quaternion_en
echo 1 > in_timestamp_en
echo 1 > enable

Before this series is applied, we can see that the timestamp (group of 8
ending in "98 18") is at offset of 24 in the 32-byte data.

hd /dev/iio\:device1

00000000  6a 18 00 00 ac f3 ff ff  83 2d 00 00 02 d3 ff ff  |j........-......|
00000010  00 00 00 00 00 00 00 00  5a 17 a0 2a 73 cb 98 18  |........Z..*s...|

00000020  ad 17 00 00 6a f4 ff ff  35 2b 00 00 ca d0 ff ff  |....j...5+......|
00000030  00 00 00 00 00 00 00 00  2a a6 bb 30 73 cb 98 18  |........*..0s...|

00000040  92 1e 00 00 50 ec ff ff  ea c1 ff ff 78 f0 ff ff  |....P.......x...|
00000050  00 00 00 00 00 00 00 00  8f 3b a7 39 77 cb 98 18  |.........;.9w...|

After the first patch, we can see that the timestamp is now repeated at
both the correct and previous incorrect offsets (24 and 32). (Normally,
the last 8 bytes would be all 00 for padding.)

00000000  dd e0 ff ff 0e e0 ff ff  75 07 00 00 90 3f 00 00  |........u....?..|
00000010  f4 38 82 d0 3a cc 98 18  f4 38 82 d0 3a cc 98 18  |.8..:....8..:...|

00000020  a0 e0 ff ff 1d e0 ff ff  a0 0a 00 00 1c 3f 00 00  |.............?..|
00000030  3a 29 9f d6 3a cc 98 18  3a 29 9f d6 3a cc 98 18  |:)..:...:)..:...|

00000040  a9 e1 ff ff 1e 14 00 00  6c c1 ff ff 98 f2 ff ff  |........l.......|
00000050  39 21 77 11 55 cc 98 18  39 21 77 11 55 cc 98 18  |9!w.U...9!w.U...|

Test script:

import math
import os
import struct
import time

UHID_DESTROY = 1
UHID_CREATE2 = 11
UHID_INPUT2 = 12

UHID_DATA_MAX = 4096
BUS_USB = 0x03

class UHIDRotationSensor:
    def __init__(self, device_path: str = "/dev/uhid") -> None:
        """Initialize the virtual UHID rotation sensor.

        Args:
            device_path: Path to the UHID character device.
        """
        self.device_path = device_path
        self.fd = -1

    def open_device(self) -> None:
        """Open the UHID device."""
        self.fd = os.open(self.device_path, os.O_RDWR)

    def close_device(self) -> None:
        """Close the UHID device."""
        if self.fd >= 0:
            os.close(self.fd)
            self.fd = -1

    def create_device(self) -> None:
        """Create and register a virtual HID rotation sensor."""
        # HID descriptor for Sensor Device Orientation (HID-SENSOR-20008a)
        report_desc = bytes(
            [
                0x05,
                0x20,  # Usage Page: Sensor
                0x09,
                0x8A,  # Usage: Device Orientation (0x20008A)
                0xA1,
                0x01,  # Collection: Application

                # Input report (Report ID 1): quaternion x, y, z, w (s16)
                0x85,
                0x01,  # Report ID 1
                0x0A,
                0x83,
                0x04,  # Usage: Orientation Quaternion (0x200483)
                0x16,
                0x00,
                0x80,  # Logical Minimum: -32768
                0x26,
                0xFF,
                0x7F,  # Logical Maximum: 32767
                0x75,
                0x10,  # Report Size: 16
                0x95,
                0x04,  # Report Count: 4
                0x81,
                0x02,  # Input: Data, Variable, Absolute

                # Feature report (Report ID 2): report state, power state,
                # and report interval
                0x85,
                0x02,  # Report ID 2
                0x0A,
                0x16,
                0x03,  # Usage: Property Report State (0x200316)
                0x15,
                0x01,  # Logical Minimum: 1
                0x25,
                0x05,  # Logical Maximum: 5
                0x75,
                0x08,
                0x95,
                0x01,
                0xB1,
                0x02,  # Feature: Data, Variable, Absolute

                0x0A,
                0x19,
                0x03,  # Usage: Property Power State (0x200319)
                0x15,
                0x01,
                0x25,
                0x05,
                0x75,
                0x08,
                0x95,
                0x01,
                0xB1,
                0x02,  # Feature: Data, Variable, Absolute

                0x0A,
                0x0E,
                0x03,  # Usage: Property Report Interval (0x20030E)
                0x15,
                0x00,  # Logical Minimum: 0
                0x27,
                0xFF,
                0xFF,
                0xFF,
                0x7F,  # Logical Maximum: 2147483647
                0x75,
                0x20,  # Report Size: 32
                0x95,
                0x01,  # Report Count: 1
                0xB1,
                0x02,  # Feature: Data, Variable, Absolute

                0xC0,  # End Collection
            ]
        )

        # Build struct uhid_create2_req payload
        create_req_header = struct.pack(
            "<128s64s64sHHIIII",
            b"HID-SENSOR-20008a",  # name
            b"AD Inc",  # phys
            b"",  # uniq
            len(report_desc),  # rd_size
            BUS_USB,  # bus
            0x0001,  # vendor
            0x0001,  # product
            0,  # version
            0,  # country
        )

        create_req = (
            create_req_header
            + report_desc
            + b"\x00" * (UHID_DATA_MAX - len(report_desc))
        )
        event = struct.pack("<I", UHID_CREATE2) + create_req

        if self.fd < 0:
            raise RuntimeError("UHID device is not open")

        os.write(self.fd, event)

    def send_rotation_data(self, qx: int, qy: int, qz: int, qw: int) -> None:
        """Send one quaternion sample report.

        Args:
            qx: Quaternion X component (signed 16-bit).
            qy: Quaternion Y component (signed 16-bit).
            qz: Quaternion Z component (signed 16-bit).
            qw: Quaternion W component (signed 16-bit).
        """
        report_id = 1
        report = struct.pack(
            "<Bhhhh",
            report_id,
            max(-32768, min(32767, qx)),
            max(-32768, min(32767, qy)),
            max(-32768, min(32767, qz)),
            max(-32768, min(32767, qw)),
        )

        input_req = (
            struct.pack("<H", len(report))
            + report
            + b"\x00" * (UHID_DATA_MAX - len(report))
        )
        event = struct.pack("<I", UHID_INPUT2) + input_req

        if self.fd < 0:
            raise RuntimeError("UHID device is not open")

        os.write(self.fd, event)

    def run(self) -> None:
        """Run the virtual sensor simulation loop."""
        try:
            self.open_device()
            self.create_device()
            time.sleep(0.5)

            angle = 0.0
            while True:
                # Simulate changing orientation in quaternion form.
                half_angle = angle * 0.5
                qx = int(8192 * math.sin(half_angle * 0.7))
                qy = int(8192 * math.cos(half_angle * 0.5))
                qz = int(16384 * math.sin(half_angle))
                qw = int(16384 * math.cos(half_angle))

                self.send_rotation_data(qx, qy, qz, qw)
                angle += 0.1
                time.sleep(0.1)

        except KeyboardInterrupt:
            print("\nStopping...")
        finally:
            self.close_device()

if __name__ == "__main__":
    sensor = UHIDRotationSensor()
    sensor.run()
---
 drivers/iio/orientation/hid-sensor-rotation.c | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c
index e759f91a710a..a8bce5151dcb 100644
--- a/drivers/iio/orientation/hid-sensor-rotation.c
+++ b/drivers/iio/orientation/hid-sensor-rotation.c
@@ -20,7 +20,11 @@ struct dev_rot_state {
 	struct hid_sensor_hub_attribute_info quaternion;
 	struct {
 		s32 sampled_vals[4];
-		aligned_s64 timestamp;
+		/*
+		 * HACK: There are two copies of the same timestamp in case of
+		 * userspace depending on broken alignment from older kernels.
+		 */
+		aligned_s64 timestamp[2];
 	} scan;
 	int scale_pre_decml;
 	int scale_post_decml;
@@ -154,8 +158,18 @@ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev,
 		if (!rot_state->timestamp)
 			rot_state->timestamp = iio_get_time_ns(indio_dev);
 
-		iio_push_to_buffers_with_timestamp(indio_dev, &rot_state->scan,
-						   rot_state->timestamp);
+		/*
+		 * HACK: IIO previously had an incorrect implementation of
+		 * iio_push_to_buffers_with_timestamp() that put the timestamp
+		 * in the last 8 bytes of the buffer, which was incorrect
+		 * according to the IIO ABI. To avoid breaking userspace that
+		 * depended on this broken behavior, we put the timestamp in
+		 * both the correct place and the old incorrect place.
+		 */
+		rot_state->scan.timestamp[0] = rot_state->timestamp;
+		rot_state->scan.timestamp[1] = rot_state->timestamp;
+
+		iio_push_to_buffers(indio_dev, &rot_state->scan);
 
 		rot_state->timestamp = 0;
 	}

-- 
2.43.0


^ permalink raw reply related

* [PATCH 0/4] iio: buffer: fix timestamp alignment (in rare case)
From: David Lechner @ 2026-03-01 20:24 UTC (permalink / raw)
  To: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, Nuno Sá,
	Andy Shevchenko, Lars Möllendorf, Lars-Peter Clausen,
	Greg Kroah-Hartman
  Cc: Jonathan Cameron, Lixu Zhang, linux-input, linux-iio,
	linux-kernel, David Lechner

In [1], it was pointed out that the iio_push_to_buffers_with_timestamp()
function is not putting the timestamp at the correct offset in the scan
buffer in rare cases where the largest scan element size is larger than
sizeof(int64_t).

[1]: https://lore.kernel.org/linux-iio/20260215162351.79f40b32@jic23-huawei/

This only affected one driver, namely hid-sensor-rotation since it is
the only driver that meets the condition. To fix things up, first we
fix the hid-sensor-rotation driver in a way that preserves compatibility
with the broken timestamp alignment. Then we are free to fix the core
IIO code without affecting any users.

Signed-off-by: David Lechner <dlechner@baylibre.com>
---
David Lechner (4):
      iio: orientation: hid-sensor-rotation: add timestamp hack to not break userspace
      iio: buffer: check return value of iio_compute_scan_bytes()
      iio: buffer: cache largest scan element size
      iio: buffer: fix timestamp alignment when quaternion in scan

 drivers/iio/industrialio-buffer.c             | 44 ++++++++++++++++++++-------
 drivers/iio/orientation/hid-sensor-rotation.c | 20 ++++++++++--
 include/linux/iio/buffer.h                    | 12 ++++++--
 include/linux/iio/iio.h                       |  3 ++
 4 files changed, 63 insertions(+), 16 deletions(-)
---
base-commit: 3fa5e5702a82d259897bd7e209469bc06368bf31
change-id: 20260228-iio-fix-timestamp-alignment-89ade1af458b

Best regards,
-- 
David Lechner <dlechner@baylibre.com>


^ permalink raw reply

* Re: [PATCH 08/10] dt-bindings: input: touchscreen: st,stmfts: Introduce STM FTS5
From: Rob Herring (Arm) @ 2026-03-01 19:34 UTC (permalink / raw)
  To: David Heidelberg
  Cc: Petr Hodina, linux-arm-msm, Krzysztof Kozlowski, Henrik Rydberg,
	linux-kernel, Conor Dooley, Bjorn Andersson, Konrad Dybcio,
	devicetree, linux-input, Maxime Coquelin, linux-stm32,
	Alexandre Torgue, Dmitry Torokhov, phone-devel, linux-arm-kernel,
	Krzysztof Kozlowski
In-Reply-To: <20260301-stmfts5-v1-8-22c458b9ac68@ixit.cz>


On Sun, 01 Mar 2026 18:51:22 +0100, David Heidelberg wrote:
> Introduce more recent STM FTS5 touchscreen support.
> 
> Signed-off-by: David Heidelberg <david@ixit.cz>
> ---
>  .../bindings/input/touchscreen/st,stmfts.yaml           | 17 ++++++++++++++++-
>  1 file changed, 16 insertions(+), 1 deletion(-)
> 

My bot found errors running 'make dt_binding_check' on your patch:

yamllint warnings/errors:
./Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml:58:1: [error] duplication of key "allOf" in mapping (key-duplicates)

dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml: ignoring, error parsing file
./Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml:58:1: found duplicate key "allOf" with value "[]" (original value: "[]")
make[2]: *** Deleting file 'Documentation/devicetree/bindings/input/touchscreen/st,stmfts.example.dts'
Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml:58:1: found duplicate key "allOf" with value "[]" (original value: "[]")
make[2]: *** [Documentation/devicetree/bindings/Makefile:26: Documentation/devicetree/bindings/input/touchscreen/st,stmfts.example.dts] Error 1
make[2]: *** Waiting for unfinished jobs....
make[1]: *** [/builds/robherring/dt-review-ci/linux/Makefile:1597: dt_binding_check] Error 2
make: *** [Makefile:248: __sub-make] Error 2

doc reference errors (make refcheckdocs):

See https://patchwork.kernel.org/project/devicetree/patch/20260301-stmfts5-v1-8-22c458b9ac68@ixit.cz

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.


^ permalink raw reply

* Re: [PATCH 1/2] iio: hid-sensor-gyro-3d: move iio_device_register() to end of probe()
From: Bhargav Joshi @ 2026-03-01 19:30 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: jikos, srinivas.pandruvada, dlechner, nuno.sa, andy, linux-iio,
	linux-kernel, linux-input
In-Reply-To: <20260301114450.17f79213@jic23-huawei>

On Sun, Mar 1, 2026 at 5:15 PM Jonathan Cameron <jic23@kernel.org> wrote:
>
> On Sun,  1 Mar 2026 00:43:59 +0530
> Bhargav Joshi <rougueprince47@gmail.com> wrote:
>
> > Currently, calling iio_device_register() before
> > sensor_hub_register_callback() may create a race condition where the
> > device is exposed to userspace before callbacks are wired.
>
> We needs some more here on 'why' this is a problem.
> Whilst this is an unusual arrangement, I couldn't immediately find
> anything that was actually broken.  It's possible data will turn up
> after we tear down the callbacks and before the userspace interfaces
> are removed, but I think that just results in dropping data. Given it's
> in a race anyway I don't think we care about that. The callbacks
> don't seem to be involved in device configuration which can go on whether
> or not the callbacks are registered.  Maybe there is something that
> won't get acknowledged if they aren't in place in time?
>
> For a fix like this we'd normally want to see a clear flow that
> leads to a bug.
>
> Also a fixes tag so we know how far to backport.
>

Hi Jonathan,

I originally submitted the patch for the driver because calling
iio_device_register() before the callbacks are wired up violates
standard IIO LIFO ordering. I assumed this created a dangerous race
condition with userspace. However, as you correctly pointed out, the
HID sensor hub safely drops those early events without crashing, even
if it is unusual behaviour still won't have a hard crash.

My underlying goal was simply a structural cleanup to align the
probe() and remove() sequences with standard IIO architecture.

Would you like me to send a v2 of this patch with a revised commit
message classifying it purely as a structural cleanup (and without a
Fixes tag), or is the current driver behavior acceptable enough that I
should just drop this patch entirely?

Thanks,
Bhargav

> >
> > Move iio_device_register() to the end of the probe() function to prevent
> > race condition.
> >
> > Consequently, update the error handling path in probe() and in remove()
> > ensuring that iio_device_unregister() is called first to cut off
> > userspace access before the hardware callbacks are removed.
> >
> > Signed-off-by: Bhargav Joshi <rougueprince47@gmail.com>
> > ---
> >  drivers/iio/gyro/hid-sensor-gyro-3d.c | 20 ++++++++++----------
> >  1 file changed, 10 insertions(+), 10 deletions(-)
> >
> > diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c
> > index c43990c518f7..8e3628cd8529 100644
> > --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c
> > +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c
> > @@ -333,12 +333,6 @@ static int hid_gyro_3d_probe(struct platform_device *pdev)
> >               return ret;
> >       }
> >
> > -     ret = iio_device_register(indio_dev);
> > -     if (ret) {
> > -             dev_err(&pdev->dev, "device register failed\n");
> > -             goto error_remove_trigger;
> > -     }
> > -
> >       gyro_state->callbacks.send_event = gyro_3d_proc_event;
> >       gyro_state->callbacks.capture_sample = gyro_3d_capture_sample;
> >       gyro_state->callbacks.pdev = pdev;
> > @@ -346,13 +340,19 @@ static int hid_gyro_3d_probe(struct platform_device *pdev)
> >                                       &gyro_state->callbacks);
> >       if (ret < 0) {
> >               dev_err(&pdev->dev, "callback reg failed\n");
> > -             goto error_iio_unreg;
> > +             goto error_remove_trigger;
> > +     }
> > +
> > +     ret = iio_device_register(indio_dev);
> > +     if (ret) {
> > +             dev_err(&pdev->dev, "device register failed\n");
> > +             goto error_remove_callback;
> >       }
> >
> >       return ret;
> >
> > -error_iio_unreg:
> > -     iio_device_unregister(indio_dev);
> > +error_remove_callback:
> > +     sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);
> >  error_remove_trigger:
> >       hid_sensor_remove_trigger(indio_dev, &gyro_state->common_attributes);
> >       return ret;
> > @@ -365,8 +365,8 @@ static void hid_gyro_3d_remove(struct platform_device *pdev)
> >       struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> >       struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
> >
> > -     sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);
> >       iio_device_unregister(indio_dev);
> > +     sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);
> >       hid_sensor_remove_trigger(indio_dev, &gyro_state->common_attributes);
> >  }
> >
>

^ permalink raw reply

* Re: [PATCH 00/10] Input: support for STM FTS5
From: David Heidelberg @ 2026-03-01 18:10 UTC (permalink / raw)
  To: Dmitry Torokhov, Maxime Coquelin, Alexandre Torgue, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Henrik Rydberg,
	Bjorn Andersson, Konrad Dybcio
  Cc: Petr Hodina, linux-input, linux-stm32, linux-arm-kernel,
	linux-kernel, Krzysztof Kozlowski, devicetree, linux-arm-msm,
	phone-devel
In-Reply-To: <20260301-stmfts5-v1-0-22c458b9ac68@ixit.cz>

On 01/03/2026 18:51, David Heidelberg via B4 Relay wrote:
> Used on various phones. Minimal basic support.
> 
> Includes device-tree with possibility to enable touchscreen on Pixel 3.
> 
> Sending as RFC, as this is first, seemingly clean version which works.
> 
> What is missing:
>   - firmware loading
>   - switching between AP and SLPI mode (to wake up phone by touch)
>   - anything above basic touch
> 
> Signed-off-by: David Heidelberg <david@ixit.cz>
> ---

First, I said it's RFC, but I didn't marked the series such as, sorry bout that.

Second, already found typo in binding: s/switch-gpio/switch-gpios/ .

At last, the series, until merged can be found at:
   https://codeberg.org/sdm845/linux/commits/branch/b4/stmfts5



^ permalink raw reply

* [PATCH 10/10] arm64: dts: qcom: sdm845-google: Add STM FTS touchscreen support
From: David Heidelberg via B4 Relay @ 2026-03-01 17:51 UTC (permalink / raw)
  To: Dmitry Torokhov, Maxime Coquelin, Alexandre Torgue, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Henrik Rydberg,
	Bjorn Andersson, Konrad Dybcio
  Cc: Petr Hodina, linux-input, linux-stm32, linux-arm-kernel,
	linux-kernel, Krzysztof Kozlowski, devicetree, linux-arm-msm,
	phone-devel, David Heidelberg
In-Reply-To: <20260301-stmfts5-v1-0-22c458b9ac68@ixit.cz>

From: Petr Hodina <petr.hodina@protonmail.com>

Basic touchscreen connected to second i2c bus.

Signed-off-by: Petr Hodina <petr.hodina@protonmail.com>
Co-developed-by: David Heidelberg <david@ixit.cz>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
 arch/arm64/boot/dts/qcom/sdm845-google-blueline.dts | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/qcom/sdm845-google-blueline.dts b/arch/arm64/boot/dts/qcom/sdm845-google-blueline.dts
index fa89be500fb85..2501104b06e1b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-google-blueline.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-google-blueline.dts
@@ -26,7 +26,26 @@ &i2c2 {
 
 	status = "okay";
 
-	/* ST,FTS @ 49 */
+	touchscreen@49 {
+		compatible = "st,stmfts5";
+		reg = <0x49>;
+
+		pinctrl-0 = <&touchscreen_pins &touchscreen_reset>;
+		pinctrl-names = "default";
+
+		interrupt-parent = <&tlmm>;
+		interrupts = <125 IRQ_TYPE_LEVEL_LOW>;
+
+		irq-gpios = <&tlmm 125 GPIO_ACTIVE_HIGH>;
+		switch-gpios = <&tlmm 136 GPIO_ACTIVE_HIGH>;
+		reset-gpios = <&tlmm 99 GPIO_ACTIVE_LOW>;
+
+		avdd-supply = <&vreg_l14a_1p8>;
+		vdd-supply = <&vreg_l19a_3p3>;
+
+		touchscreen-size-x = <1079>;
+		touchscreen-size-y = <2159>;
+	};
 };
 
 &mdss_dsi0 {

-- 
2.51.0



^ permalink raw reply related

* [PATCH 09/10] Input: stmfts - support FTS5
From: David Heidelberg via B4 Relay @ 2026-03-01 17:51 UTC (permalink / raw)
  To: Dmitry Torokhov, Maxime Coquelin, Alexandre Torgue, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Henrik Rydberg,
	Bjorn Andersson, Konrad Dybcio
  Cc: Petr Hodina, linux-input, linux-stm32, linux-arm-kernel,
	linux-kernel, Krzysztof Kozlowski, devicetree, linux-arm-msm,
	phone-devel, David Heidelberg
In-Reply-To: <20260301-stmfts5-v1-0-22c458b9ac68@ixit.cz>

From: Petr Hodina <petr.hodina@protonmail.com>

Introduce basic FTS5 support.

FTS support SLPI and AP mode, introduce switch GPIO to switch between
those two. Currently we can handle only full power AP mode, so we just
switch to it.

Useful for devices like Pixel 3 (blueline).

Nitpick: changed GPL v2 to GPL in module license.

Signed-off-by: Petr Hodina <petr.hodina@protonmail.com>
Co-developed-by: David Heidelberg <david@ixit.cz>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
 drivers/input/touchscreen/stmfts.c | 484 +++++++++++++++++++++++++++++++++++--
 1 file changed, 461 insertions(+), 23 deletions(-)

diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c
index a4d8e81aba275..5f0f2d59300e4 100644
--- a/drivers/input/touchscreen/stmfts.c
+++ b/drivers/input/touchscreen/stmfts.c
@@ -1,8 +1,13 @@
 // SPDX-License-Identifier: GPL-2.0
-// STMicroelectronics FTS Touchscreen device driver
-//
-// Copyright (c) 2017 Samsung Electronics Co., Ltd.
-// Copyright (c) 2017 Andi Shyti <andi@etezian.org>
+/* STMicroelectronics FTS Touchscreen device driver
+ *
+ * Supports version FTS4, FTS5.
+ *
+ * Copyright 2017 Samsung Electronics Co., Ltd.
+ * Copyright 2017 Andi Shyti <andi@etezian.org>
+ * Copyright David Heidelberg <david@ixit.cz>
+ * Copyright Petr Hodina <petr.hodina@protonmail.com>
+ */
 
 #include <linux/delay.h>
 #include <linux/i2c.h>
@@ -12,6 +17,7 @@
 #include <linux/irq.h>
 #include <linux/leds.h>
 #include <linux/module.h>
+#include <linux/of_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
 
@@ -34,6 +40,7 @@
 #define STMFTS_FULL_FORCE_CALIBRATION		0xa2
 #define STMFTS_MS_CX_TUNING			0xa3
 #define STMFTS_SS_CX_TUNING			0xa4
+#define STMFTS5_SET_SCAN_MODE			0xa0
 
 /* events */
 #define STMFTS_EV_NO_EVENT			0x00
@@ -51,12 +58,32 @@
 #define STMFTS_EV_STATUS			0x16
 #define STMFTS_EV_DEBUG				0xdb
 
+/* events FTS5 */
+#define STMFTS5_EV_CONTROLLER_READY		0x03
+/* FTM5 event IDs (full byte, not masked) */
+#define STMFTS5_EV_MULTI_TOUCH_ENTER		0x13
+#define STMFTS5_EV_MULTI_TOUCH_MOTION		0x23
+#define STMFTS5_EV_MULTI_TOUCH_LEAVE		0x33
+#define STMFTS5_EV_STATUS_UPDATE		0x43
+#define STMFTS5_EV_USER_REPORT			0x53
+#define STMFTS5_EV_DEBUG			0xe3
+#define STMFTS5_EV_ERROR			0xf3
+
 /* multi touch related event masks */
 #define STMFTS_MASK_EVENT_ID			0x0f
 #define STMFTS_MASK_TOUCH_ID			0xf0
 #define STMFTS_MASK_LEFT_EVENT			0x0f
 #define STMFTS_MASK_X_MSB			0x0f
 #define STMFTS_MASK_Y_LSB			0xf0
+#define STMFTS5_MASK_TOUCH_TYPE			0x0f
+
+/* touch type classifications */
+#define STMFTS_TOUCH_TYPE_INVALID		0x00
+#define STMFTS_TOUCH_TYPE_FINGER		0x01
+#define STMFTS_TOUCH_TYPE_GLOVE			0x02
+#define STMFTS_TOUCH_TYPE_STYLUS		0x03
+#define STMFTS_TOUCH_TYPE_PALM			0x04
+#define STMFTS_TOUCH_TYPE_HOVER			0x05
 
 /* key related event masks */
 #define STMFTS_MASK_KEY_NO_TOUCH		0x00
@@ -77,7 +104,9 @@ static const struct regulator_bulk_data stmfts_supplies[] = {
 struct stmfts_data {
 	struct i2c_client *client;
 	struct input_dev *input;
+	struct gpio_desc *irq_gpio;
 	struct gpio_desc *reset_gpio;
+	struct gpio_desc *switch_gpio;
 	struct led_classdev led_cdev;
 	struct mutex mutex;
 
@@ -101,9 +130,14 @@ struct stmfts_data {
 
 	struct completion cmd_done;
 
+	unsigned long touch_id;
+	unsigned long stylus_id;
+
+	bool is_fts5;
 	bool use_key;
 	bool led_status;
 	bool hover_enabled;
+	bool stylus_enabled;
 	bool running;
 };
 
@@ -169,6 +203,7 @@ static int stmfts_read_events(struct stmfts_data *sdata)
 	return ret == ARRAY_SIZE(msgs) ? 0 : -EIO;
 }
 
+/* FTS4 event handling functions */
 static void stmfts_report_contact_event(struct stmfts_data *sdata,
 					const u8 event[])
 {
@@ -204,6 +239,157 @@ static void stmfts_report_contact_release(struct stmfts_data *sdata,
 	input_sync(sdata->input);
 }
 
+/* FTS5 event handling functions */
+static void stmfts5_report_contact_event(struct stmfts_data *sdata,
+					 const u8 event[])
+{
+	u8 area;
+	u8 maj;
+	u8 min;
+	/* FTM5 event format:
+	 * event[0] = event ID (0x13/0x23)
+	 * event[1] = touch type (low 4 bits) | touch ID (high 4 bits)
+	 * event[2] = X LSB
+	 * event[3] = X MSB (low 4 bits) | Y MSB (high 4 bits)
+	 * event[4] = Y LSB
+	 * event[5] = pressure
+	 * event[6] = major (low 4 bits) | minor (high 4 bits)
+	 * event[7] = minor (high 2 bits)
+	 */
+	u8 touch_id = (event[1] & STMFTS_MASK_TOUCH_ID) >> 4;
+	u8 touch_type = event[1] & STMFTS5_MASK_TOUCH_TYPE;
+	int x, y, distance;
+	unsigned int tool = MT_TOOL_FINGER;
+	bool touch_condition = true;
+
+	/* Parse coordinates with better precision */
+	x = (((int)event[3] & STMFTS_MASK_X_MSB) << 8) | event[2];
+	y = ((int)event[4] << 4) | ((event[3] & STMFTS_MASK_Y_LSB) >> 4);
+
+	/* Parse pressure - ensure non-zero for active touch */
+	area = event[5];
+	if (area <= 0 && touch_type != STMFTS_TOUCH_TYPE_HOVER) {
+		/* Should not happen for contact events. Set minimum pressure
+		 * to prevent touch from being dropped
+		 */
+		dev_warn_once(&sdata->client->dev,
+			      "zero pressure on contact event, slot %d\n", touch_id);
+		area = 1;
+	}
+
+	/* Parse touch area with improved bit extraction */
+	maj = (((event[0] & 0x0C) << 2) | ((event[6] & 0xF0) >> 4));
+	min = (((event[7] & 0xC0) >> 2) | (event[6] & 0x0F));
+
+	/* Distance is 0 for touching, max for hovering */
+	distance = 0;
+
+	/* Classify touch type and set appropriate tool and parameters */
+	switch (touch_type) {
+	case STMFTS_TOUCH_TYPE_STYLUS:
+		if (sdata->stylus_enabled) {
+			tool = MT_TOOL_PEN;
+			__set_bit(touch_id, &sdata->stylus_id);
+			__clear_bit(touch_id, &sdata->touch_id);
+			break;
+		}
+		fallthrough; /* Report as finger if stylus not enabled */
+
+	case STMFTS_TOUCH_TYPE_FINGER:
+	case STMFTS_TOUCH_TYPE_GLOVE:
+		tool = MT_TOOL_FINGER;
+		__set_bit(touch_id, &sdata->touch_id);
+		__clear_bit(touch_id, &sdata->stylus_id);
+		break;
+
+	case STMFTS_TOUCH_TYPE_PALM:
+		/* Palm touch - report but can be filtered by userspace */
+		tool = MT_TOOL_PALM;
+		__set_bit(touch_id, &sdata->touch_id);
+		__clear_bit(touch_id, &sdata->stylus_id);
+		break;
+
+	case STMFTS_TOUCH_TYPE_HOVER:
+		tool = MT_TOOL_FINGER;
+		touch_condition = false;
+		area = 0;
+		distance = 255;
+		__set_bit(touch_id, &sdata->touch_id);
+		__clear_bit(touch_id, &sdata->stylus_id);
+		break;
+
+	case STMFTS_TOUCH_TYPE_INVALID:
+	default:
+		dev_warn(&sdata->client->dev,
+			 "invalid touch type %d for slot %d\n",
+			 touch_type, touch_id);
+		return;
+	}
+
+	/* Boundary check - some devices report max value, adjust */
+	if (x >= sdata->prop.max_x)
+		x = sdata->prop.max_x - 1;
+	if (y >= sdata->prop.max_y)
+		y = sdata->prop.max_y - 1;
+
+	input_mt_slot(sdata->input, touch_id);
+	input_report_key(sdata->input, BTN_TOUCH, touch_condition);
+	input_mt_report_slot_state(sdata->input, tool, true);
+
+	input_report_abs(sdata->input, ABS_MT_POSITION_X, x);
+	input_report_abs(sdata->input, ABS_MT_POSITION_Y, y);
+	input_report_abs(sdata->input, ABS_MT_TOUCH_MAJOR, maj);
+	input_report_abs(sdata->input, ABS_MT_TOUCH_MINOR, min);
+	input_report_abs(sdata->input, ABS_MT_PRESSURE, area);
+	input_report_abs(sdata->input, ABS_MT_DISTANCE, distance);
+
+	input_sync(sdata->input);
+}
+
+static void stmfts5_report_contact_release(struct stmfts_data *sdata,
+					   const u8 event[])
+{
+	/* FTM5 format: touch ID is in high 4 bits of event[1] */
+	u8 touch_id = (event[1] & STMFTS_MASK_TOUCH_ID) >> 4;
+	u8 touch_type = event[1] & STMFTS5_MASK_TOUCH_TYPE;
+	unsigned int tool = MT_TOOL_FINGER;
+
+	/* Determine tool type based on touch classification */
+	switch (touch_type) {
+	case STMFTS_TOUCH_TYPE_STYLUS:
+		if (sdata->stylus_enabled) {
+			tool = MT_TOOL_PEN;
+			__clear_bit(touch_id, &sdata->stylus_id);
+		} else {
+			__clear_bit(touch_id, &sdata->touch_id);
+		}
+		break;
+
+	case STMFTS_TOUCH_TYPE_PALM:
+		tool = MT_TOOL_PALM;
+		__clear_bit(touch_id, &sdata->touch_id);
+		break;
+
+	case STMFTS_TOUCH_TYPE_FINGER:
+	case STMFTS_TOUCH_TYPE_GLOVE:
+	case STMFTS_TOUCH_TYPE_HOVER:
+	default:
+		tool = MT_TOOL_FINGER;
+		__clear_bit(touch_id, &sdata->touch_id);
+		break;
+	}
+
+	input_mt_slot(sdata->input, touch_id);
+	input_report_abs(sdata->input, ABS_MT_PRESSURE, 0);
+	input_mt_report_slot_state(sdata->input, tool, false);
+
+	/* Report BTN_TOUCH only if no touches remain */
+	if (!sdata->touch_id && !sdata->stylus_id)
+		input_report_key(sdata->input, BTN_TOUCH, 0);
+
+	input_sync(sdata->input);
+}
+
 static void stmfts_report_hover_event(struct stmfts_data *sdata,
 				      const u8 event[])
 {
@@ -251,7 +437,6 @@ static void stmfts_parse_events(struct stmfts_data *sdata)
 		u8 *event = &sdata->data[i * STMFTS_EVENT_SIZE];
 
 		switch (event[0]) {
-
 		case STMFTS_EV_CONTROLLER_READY:
 		case STMFTS_EV_SLEEP_OUT_CONTROLLER_READY:
 		case STMFTS_EV_STATUS:
@@ -264,7 +449,6 @@ static void stmfts_parse_events(struct stmfts_data *sdata)
 		}
 
 		switch (event[0] & STMFTS_MASK_EVENT_ID) {
-
 		case STMFTS_EV_MULTI_TOUCH_ENTER:
 		case STMFTS_EV_MULTI_TOUCH_MOTION:
 			stmfts_report_contact_event(sdata, event);
@@ -298,6 +482,45 @@ static void stmfts_parse_events(struct stmfts_data *sdata)
 	}
 }
 
+static void stmfts5_parse_events(struct stmfts_data *sdata)
+{
+	for (int i = 0; i < STMFTS_STACK_DEPTH; i++) {
+		u8 *event = &sdata->data[i * STMFTS_EVENT_SIZE];
+
+		switch (event[0]) {
+		case STMFTS5_EV_CONTROLLER_READY:
+			complete(&sdata->cmd_done);
+			fallthrough;
+
+		case STMFTS_EV_NO_EVENT:
+		case STMFTS5_EV_STATUS_UPDATE:
+		case STMFTS5_EV_USER_REPORT:
+		case STMFTS5_EV_DEBUG:
+			return;
+
+		case STMFTS5_EV_MULTI_TOUCH_ENTER:
+		case STMFTS5_EV_MULTI_TOUCH_MOTION:
+			stmfts5_report_contact_event(sdata, event);
+			break;
+
+		case STMFTS5_EV_MULTI_TOUCH_LEAVE:
+			stmfts5_report_contact_release(sdata, event);
+			break;
+
+		case STMFTS5_EV_ERROR:
+			dev_warn(&sdata->client->dev,
+				 "error code: 0x%x%x%x%x%x%x",
+				 event[6], event[5], event[4],
+				 event[3], event[2], event[1]);
+			break;
+
+		default:
+			dev_err(&sdata->client->dev,
+				"unknown FTS5 event %#02x\n", event[0]);
+		}
+	}
+}
+
 static irqreturn_t stmfts_irq_handler(int irq, void *dev)
 {
 	struct stmfts_data *sdata = dev;
@@ -306,11 +529,15 @@ static irqreturn_t stmfts_irq_handler(int irq, void *dev)
 	mutex_lock(&sdata->mutex);
 
 	err = stmfts_read_events(sdata);
-	if (unlikely(err))
+	if (unlikely(err)) {
 		dev_err(&sdata->client->dev,
 			"failed to read events: %d\n", err);
-	else
-		stmfts_parse_events(sdata);
+	} else {
+		if (sdata->is_fts5)
+			stmfts5_parse_events(sdata);
+		else
+			stmfts_parse_events(sdata);
+	}
 
 	mutex_unlock(&sdata->mutex);
 	return IRQ_HANDLED;
@@ -333,6 +560,25 @@ static int stmfts_command(struct stmfts_data *sdata, const u8 cmd)
 	return 0;
 }
 
+static int stmfts5_set_scan_mode(struct stmfts_data *sdata, const u8 val)
+{
+	int err;
+
+	u8 scan_mode_cmd[3] = { STMFTS5_SET_SCAN_MODE, 0x00, val };
+	struct i2c_msg msg = {
+		.addr = sdata->client->addr,
+		.len = sizeof(scan_mode_cmd),
+		.buf = scan_mode_cmd,
+	};
+
+	err = i2c_transfer(sdata->client->adapter, &msg, 1);
+	if (err != 1)
+		return err < 0 ? err : -EIO;
+
+	return 0;
+
+}
+
 static int stmfts_input_open(struct input_dev *dev)
 {
 	struct stmfts_data *sdata = input_get_drvdata(dev);
@@ -372,6 +618,28 @@ static int stmfts_input_open(struct input_dev *dev)
 	return 0;
 }
 
+static int stmfts5_input_open(struct input_dev *dev)
+{
+	struct stmfts_data *sdata = input_get_drvdata(dev);
+	int err;
+
+	err = pm_runtime_resume_and_get(&sdata->client->dev);
+	if (err)
+		return err;
+
+	mutex_lock(&sdata->mutex);
+	sdata->running = true;
+	mutex_unlock(&sdata->mutex);
+
+	err = stmfts5_set_scan_mode(sdata, 0xff);
+	if (err) {
+		pm_runtime_put_sync(&sdata->client->dev);
+		return err;
+	}
+
+	return 0;
+}
+
 static void stmfts_input_close(struct input_dev *dev)
 {
 	struct stmfts_data *sdata = input_get_drvdata(dev);
@@ -406,6 +674,23 @@ static void stmfts_input_close(struct input_dev *dev)
 	pm_runtime_put_sync(&sdata->client->dev);
 }
 
+static void stmfts5_input_close(struct input_dev *dev)
+{
+	struct stmfts_data *sdata = input_get_drvdata(dev);
+	int err;
+
+	err = stmfts5_set_scan_mode(sdata, 0x00);
+	if (err)
+		dev_warn(&sdata->client->dev,
+			 "failed to disable touchscreen: %d\n", err);
+
+	mutex_lock(&sdata->mutex);
+	sdata->running = false;
+	mutex_unlock(&sdata->mutex);
+
+	pm_runtime_put_sync(&sdata->client->dev);
+}
+
 static ssize_t stmfts_sysfs_chip_id(struct device *dev,
 				struct device_attribute *attr, char *buf)
 {
@@ -485,7 +770,7 @@ static ssize_t stmfts_sysfs_hover_enable_write(struct device *dev,
 	if (value && sdata->hover_enabled)
 		goto out;
 
-	if (sdata->running)
+	if (sdata->running && !sdata->is_fts5)
 		err = i2c_smbus_write_byte(sdata->client,
 					   value ? STMFTS_SS_HOVER_SENSE_ON :
 						   STMFTS_SS_HOVER_SENSE_OFF);
@@ -615,6 +900,41 @@ static int stmfts_power_on(struct stmfts_data *sdata)
 	return err;
 }
 
+static int stmfts5_power_on(struct stmfts_data *sdata)
+{
+	int err, ret;
+	u8 event[STMFTS_EVENT_SIZE];
+
+	err = regulator_bulk_enable(ARRAY_SIZE(stmfts_supplies),
+				    sdata->supplies);
+	if (err)
+		return err;
+
+	/* Power stabilization delay */
+	msleep(20);
+
+	if (sdata->reset_gpio)
+		stmfts_reset(sdata);
+
+	/* Verify I2C communication */
+	ret = i2c_smbus_read_i2c_block_data(sdata->client,
+					    STMFTS_READ_ALL_EVENT,
+					    sizeof(event), event);
+	if (ret < 0) {
+		err = ret;
+		goto power_off;
+	}
+
+	enable_irq(sdata->client->irq);
+
+	return 0;
+
+power_off:
+	regulator_bulk_disable(ARRAY_SIZE(stmfts_supplies),
+			       sdata->supplies);
+	return err;
+}
+
 static void stmfts_power_off(void *data)
 {
 	struct stmfts_data *sdata = data;
@@ -624,6 +944,11 @@ static void stmfts_power_off(void *data)
 	if (sdata->reset_gpio)
 		gpiod_set_value_cansleep(sdata->reset_gpio, 1);
 
+	if (sdata->is_fts5) {
+		i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_IN);
+		msleep(20);
+	}
+
 	regulator_bulk_disable(ARRAY_SIZE(stmfts_supplies),
 			       sdata->supplies);
 }
@@ -657,6 +982,7 @@ static int stmfts_probe(struct i2c_client *client)
 	struct device *dev = &client->dev;
 	int err;
 	struct stmfts_data *sdata;
+	const struct of_device_id *match;
 
 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
 						I2C_FUNC_SMBUS_BYTE_DATA |
@@ -673,6 +999,12 @@ static int stmfts_probe(struct i2c_client *client)
 	mutex_init(&sdata->mutex);
 	init_completion(&sdata->cmd_done);
 
+	match = of_match_device(dev->driver->of_match_table, dev);
+	if (match && of_device_is_compatible(dev->of_node, "st,stmfts5"))
+		sdata->is_fts5 = true;
+	else
+		sdata->is_fts5 = false;
+
 	err = devm_regulator_bulk_get_const(dev,
 					    ARRAY_SIZE(stmfts_supplies),
 					    stmfts_supplies,
@@ -686,34 +1018,90 @@ static int stmfts_probe(struct i2c_client *client)
 		return dev_err_probe(dev, PTR_ERR(sdata->reset_gpio),
 				     "Failed to get GPIO 'reset'\n");
 
+	if (sdata->is_fts5) {
+		sdata->irq_gpio = devm_gpiod_get_optional(dev, "irq",
+							  GPIOD_IN);
+		if (IS_ERR(sdata->irq_gpio))
+			return dev_err_probe(dev, PTR_ERR(sdata->irq_gpio),
+					"Failed to get GPIO 'irq'\n");
+
+		sdata->switch_gpio = devm_gpiod_get_optional(&client->dev, "switch",
+							     GPIOD_OUT_HIGH);
+		if (IS_ERR(sdata->switch_gpio))
+			return dev_err_probe(dev, PTR_ERR(sdata->switch_gpio),
+					     "Failed to get GPIO 'switch'\n");
+
+	}
+
 	sdata->input = devm_input_allocate_device(dev);
 	if (!sdata->input)
 		return -ENOMEM;
 
 	sdata->input->name = STMFTS_DEV_NAME;
 	sdata->input->id.bustype = BUS_I2C;
-	sdata->input->open = stmfts_input_open;
-	sdata->input->close = stmfts_input_close;
+	if (sdata->is_fts5) {
+		sdata->input->open = stmfts5_input_open;
+		sdata->input->close = stmfts5_input_close;
+	} else {
+		sdata->input->open = stmfts_input_open;
+		sdata->input->close = stmfts_input_close;
+	}
+
+	/* FTS5-specific input properties */
+	if (sdata->is_fts5) {
+		/* Mark as direct input device for calibration support */
+		__set_bit(INPUT_PROP_DIRECT, sdata->input->propbit);
+
+		/* Set up basic touch capabilities */
+		input_set_capability(sdata->input, EV_KEY, BTN_TOUCH);
+	}
 
 	input_set_capability(sdata->input, EV_ABS, ABS_MT_POSITION_X);
 	input_set_capability(sdata->input, EV_ABS, ABS_MT_POSITION_Y);
 	touchscreen_parse_properties(sdata->input, true, &sdata->prop);
 
+	/* Set resolution for accurate calibration (FTS5) */
+	if (sdata->is_fts5 && !input_abs_get_res(sdata->input, ABS_MT_POSITION_X)) {
+		input_abs_set_res(sdata->input, ABS_MT_POSITION_X, 10);
+		input_abs_set_res(sdata->input, ABS_MT_POSITION_Y, 10);
+	}
+
+	/* Enhanced MT parameters */
 	input_set_abs_params(sdata->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
 	input_set_abs_params(sdata->input, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0);
-	input_set_abs_params(sdata->input, ABS_MT_ORIENTATION, 0, 255, 0, 0);
 	input_set_abs_params(sdata->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
-	input_set_abs_params(sdata->input, ABS_DISTANCE, 0, 255, 0, 0);
+
+	if (sdata->is_fts5) {
+		input_set_abs_params(sdata->input, ABS_MT_DISTANCE, 0, 255, 0, 0);
+
+		/* Enable stylus support if requested */
+		sdata->stylus_enabled = device_property_read_bool(dev,
+								  "stylus-enabled");
+	} else {
+		input_set_abs_params(sdata->input, ABS_MT_ORIENTATION, 0, 255, 0, 0);
+		input_set_abs_params(sdata->input, ABS_DISTANCE, 0, 255, 0, 0);
+	}
 
 	sdata->use_key = device_property_read_bool(dev,
 						   "touch-key-connected");
-	if (sdata->use_key) {
+	if (sdata->use_key && !sdata->is_fts5) {
 		input_set_capability(sdata->input, EV_KEY, KEY_MENU);
 		input_set_capability(sdata->input, EV_KEY, KEY_BACK);
 	}
 
-	err = input_mt_init_slots(sdata->input,
-				  STMFTS_MAX_FINGERS, INPUT_MT_DIRECT);
+	/* Initialize touch tracking bitmaps (FTS5) */
+	if (sdata->is_fts5) {
+		sdata->touch_id = 0;
+		sdata->stylus_id = 0;
+
+		/* Initialize MT slots with support for pen tool type */
+		err = input_mt_init_slots(sdata->input, STMFTS_MAX_FINGERS,
+					  INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	} else {
+		err = input_mt_init_slots(sdata->input, STMFTS_MAX_FINGERS,
+					  INPUT_MT_DIRECT);
+	}
+
 	if (err)
 		return err;
 
@@ -733,9 +1121,14 @@ static int stmfts_probe(struct i2c_client *client)
 	if (err)
 		return err;
 
-	dev_dbg(dev, "initializing ST-Microelectronics FTS...\n");
+	dev_dbg(dev, "initializing ST-Microelectronics FTS%s...\n",
+		sdata->is_fts5 ? "5" : "");
+
 
-	err = stmfts_power_on(sdata);
+	if (sdata->is_fts5)
+		err = stmfts5_power_on(sdata);
+	else
+		err = stmfts_power_on(sdata);
 	if (err)
 		return err;
 
@@ -747,7 +1140,7 @@ static int stmfts_probe(struct i2c_client *client)
 	if (err)
 		return err;
 
-	if (sdata->use_key) {
+	if (sdata->use_key && !sdata->is_fts5) {
 		err = stmfts_enable_led(sdata);
 		if (err) {
 			/*
@@ -791,8 +1184,47 @@ static int stmfts_runtime_resume(struct device *dev)
 	int ret;
 
 	ret = i2c_smbus_write_byte(client, STMFTS_SLEEP_OUT);
-	if (ret)
+	if (ret) {
 		dev_err(dev, "failed to resume device: %d\n", ret);
+		return ret;
+	}
+
+	if (sdata->is_fts5) {
+		msleep(20);
+
+		/* Perform capacitance tuning after wakeup */
+		ret = i2c_smbus_write_byte(client, STMFTS_MS_CX_TUNING);
+		if (ret)
+			dev_warn(dev, "MS_CX_TUNING failed: %d\n", ret);
+		msleep(20);
+
+		ret = i2c_smbus_write_byte(client, STMFTS_SS_CX_TUNING);
+		if (ret)
+			dev_warn(dev, "SS_CX_TUNING failed: %d\n", ret);
+		msleep(20);
+
+		/* Force calibration */
+		ret = i2c_smbus_write_byte(client, STMFTS_FULL_FORCE_CALIBRATION);
+		if (ret)
+			dev_warn(dev, "FORCE_CALIBRATION failed: %d\n", ret);
+		msleep(50);
+
+		/* Enable controller interrupts */
+		u8 int_enable_cmd[4] = {0xB6, 0x00, 0x2C, 0x01};
+		struct i2c_msg msg = {
+			.addr = client->addr,
+			.len = 4,
+			.buf = int_enable_cmd,
+		};
+
+		ret = i2c_transfer(client->adapter, &msg, 1);
+		if (ret != 1)
+			return ret < 0 ? ret : -EIO;
+
+		msleep(20);
+
+		return 0;
+	}
 
 	return ret;
 }
@@ -810,7 +1242,10 @@ static int stmfts_resume(struct device *dev)
 {
 	struct stmfts_data *sdata = dev_get_drvdata(dev);
 
-	return stmfts_power_on(sdata);
+	if (sdata->is_fts5)
+		return stmfts5_power_on(sdata);
+	else
+		return stmfts_power_on(sdata);
 }
 
 static const struct dev_pm_ops stmfts_pm_ops = {
@@ -821,6 +1256,7 @@ static const struct dev_pm_ops stmfts_pm_ops = {
 #ifdef CONFIG_OF
 static const struct of_device_id stmfts_of_match[] = {
 	{ .compatible = "st,stmfts", },
+	{ .compatible = "st,stmfts5", },
 	{ },
 };
 MODULE_DEVICE_TABLE(of, stmfts_of_match);
@@ -848,5 +1284,7 @@ static struct i2c_driver stmfts_driver = {
 module_i2c_driver(stmfts_driver);
 
 MODULE_AUTHOR("Andi Shyti <andi.shyti@samsung.com>");
+MODULE_AUTHOR("David Heidelberg <david@ixit.cz>");
+MODULE_AUTHOR("Petr Hodina <petr.hodina@protonmail.com>");
 MODULE_DESCRIPTION("STMicroelectronics FTS Touch Screen");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");

-- 
2.51.0



^ permalink raw reply related


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