* [PATCH v5 0/3] Input: support for STM FTS5
@ 2026-08-10 15:21 David Heidelberg via B4 Relay
2026-08-10 15:21 ` [PATCH v5 1/3] dt-bindings: input: touchscreen: st,stmfts: Introduce " David Heidelberg via B4 Relay
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: David Heidelberg via B4 Relay @ 2026-08-10 15:21 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, Konrad Dybcio, Dmitry Baryshkov
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>
---
Changes in v5:
- 3.3V is analog, 1.8V is digital supply. Fix that.
- Name the error variables "err" (Dmitry)
- Use scoped_guard() for ->running, and only set it once the scan
mode has actually been enabled. (Dmitry)
- Use i2c_master_send() instead of open-coding a single-message
i2c_transfer(). (Dmitry)
- Drivers may report coordinates outside the declared min/max, so
remove the clamping. (Dmitry)
- BTN_TOUCH and INPUT_PROP_DIRECT are already set up by
input_mt_init_slots(..., INPUT_MT_DIRECT). (Dmitry)
- Let the input core track contacts: drop the touch_id/stylus_id
bitmaps and the hand-rolled BTN_TOUCH reporting, and sync the frame
once per interrupt with input_mt_sync_frame() instead of calling
input_sync() per contact event. The controller pushes contacts one
by one into the event FIFO and only resends the ones that changed,
so INPUT_MT_DROP_UNUSED is wrong here and is dropped as well. (Dmitry)
- Restore the FTS4 interrupts accidentally dropped by introduction of
FTS5.
- mode-switch-gpios isn't required with stmfts5. Some designs, such as
Xiaomi Mi 8, has no such GPIO connected and is limited to AP mode.
- Nit: updated example to use interrupts-extended.
- Nit: (maximum x/y coordinate reported + 1), corrected example.
- Link to v4: https://lore.kernel.org/r/20260409-stmfts5-v4-0-64fe62027db5@ixit.cz
Changes in v4:
- Wrap everything below enabling the supplies into stmfts_configure()
to avoid bunch of gotos to power off on error (Dmitry T.)
- Finished chip specific ops and removed is_fts5. (Dmitry T.)
- Link to v3: https://lore.kernel.org/r/20260403-stmfts5-v3-0-5da768cfd201@ixit.cz
Changes in v3:
- s/touchscreen_pins/touchscreen_irq_n. (Konrad)
- Use interrupts-extended. (Konrad)
- Fixed rebase conflict against 8665ceb926ec ("Input: stmfts - use guard notation when acquiring mutex")
- Rename switch-gpios to mode-switch-gpios.
- Do not define properties in if:then: branches. (Krzysztof)
- Link to v2: https://lore.kernel.org/r/20260315-stmfts5-v2-0-70bc83ee9591@ixit.cz
Changes in v2:
- Fix typo in the binding s/switch-gpio/switch-gpios/.
- Deduplacate allOf. (Rob yamllint)
- Add missing S-off-by. (Dmitry B.)
- Dropped irq-gpios as it's not needed. (Konrad)
- Correct x and y touchscreen area size. (Konrad)
- Correct reset introduction commit description. (Krzysztof)
- Partially implemented chip specific ops. (Dmitry T.)
- Separeted license naming cleanup into separate commit (Dmitry T.)
- Link to v1: https://lore.kernel.org/r/20260301-stmfts5-v1-0-22c458b9ac68@ixit.cz
---
David Heidelberg (2):
dt-bindings: input: touchscreen: st,stmfts: Introduce STM FTS5
Input: stmfts - support FTS5
Petr Hodina (1):
arm64: dts: qcom: sdm845-google: Add STM FTS touchscreen support
.../bindings/input/touchscreen/st,stmfts.yaml | 29 +-
.../arm64/boot/dts/qcom/sdm845-google-blueline.dts | 19 +-
arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi | 2 +-
drivers/input/touchscreen/stmfts.c | 430 +++++++++++++++++++--
4 files changed, 436 insertions(+), 44 deletions(-)
---
base-commit: 95d6a9ccef99117115e41e9adb271243bd5e985b
change-id: 20260214-stmfts5-b47311fbd732
Best regards,
--
David Heidelberg <david@ixit.cz>
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v5 1/3] dt-bindings: input: touchscreen: st,stmfts: Introduce STM FTS5 2026-08-10 15:21 [PATCH v5 0/3] Input: support for STM FTS5 David Heidelberg via B4 Relay @ 2026-08-10 15:21 ` David Heidelberg via B4 Relay 2026-08-11 7:03 ` Krzysztof Kozlowski 2026-08-10 15:21 ` [PATCH v5 2/3] Input: stmfts - support FTS5 David Heidelberg via B4 Relay 2026-08-10 15:21 ` [PATCH v5 3/3] arm64: dts: qcom: sdm845-google: Add STM FTS touchscreen support David Heidelberg via B4 Relay 2 siblings, 1 reply; 6+ messages in thread From: David Heidelberg via B4 Relay @ 2026-08-10 15:21 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 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 | 29 ++++++++++++++++------ 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml b/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml index 64c4f24ea3dd0..3de4bc5cd3f78 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml @@ -9,22 +9,21 @@ title: ST-Microelectronics FingerTip touchscreen controller maintainers: - Krzysztof Kozlowski <krzk@kernel.org> description: The ST-Microelectronics FingerTip device provides a basic touchscreen functionality. Along with it the user can enable the touchkey which can work as a basic HOME and BACK key for phones. -allOf: - - $ref: touchscreen.yaml# - properties: compatible: - const: st,stmfts + enum: + - st,stmfts + - st,stmfts5 reg: maxItems: 1 avdd-supply: description: Analogic power supply interrupts: @@ -35,42 +34,56 @@ properties: touch-key-connected: type: boolean description: The touchkey feature is connected vdd-supply: description: Power supply + mode-switch-gpios: + description: Optional switch between touchscreen SLPI and AP mode. + maxItems: 1 + reset-gpios: description: Reset GPIO (active-low) maxItems: 1 required: - compatible - reg - avdd-supply - interrupts - vdd-supply +allOf: + - $ref: touchscreen.yaml# + - if: + not: + properties: + compatible: + const: st,stmfts5 + then: + properties: + mode-switch-gpios: false + unevaluatedProperties: false examples: - | #include <dt-bindings/interrupt-controller/irq.h> i2c { #address-cells = <1>; #size-cells = <0>; touchscreen@49 { compatible = "st,stmfts"; reg = <0x49>; - interrupt-parent = <&gpa1>; - interrupts = <1 IRQ_TYPE_LEVEL_LOW>; - touchscreen-size-x = <1599>; - touchscreen-size-y = <2559>; + interrupts-extended = <&gpa1 1 IRQ_TYPE_LEVEL_LOW>; + touchscreen-size-x = <1600>; + touchscreen-size-y = <2560>; touch-key-connected; avdd-supply = <&ldo30_reg>; vdd-supply = <&ldo31_reg>; ledvdd-supply = <&ldo33_reg>; }; }; -- 2.55.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v5 1/3] dt-bindings: input: touchscreen: st,stmfts: Introduce STM FTS5 2026-08-10 15:21 ` [PATCH v5 1/3] dt-bindings: input: touchscreen: st,stmfts: Introduce " David Heidelberg via B4 Relay @ 2026-08-11 7:03 ` Krzysztof Kozlowski 0 siblings, 0 replies; 6+ messages in thread From: Krzysztof Kozlowski @ 2026-08-11 7:03 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 On Mon, Aug 10, 2026 at 05:21:40PM +0200, David Heidelberg wrote: > touchscreen@49 { > compatible = "st,stmfts"; > reg = <0x49>; > - interrupt-parent = <&gpa1>; > - interrupts = <1 IRQ_TYPE_LEVEL_LOW>; > - touchscreen-size-x = <1599>; > - touchscreen-size-y = <2559>; > + interrupts-extended = <&gpa1 1 IRQ_TYPE_LEVEL_LOW>; > + touchscreen-size-x = <1600>; > + touchscreen-size-y = <2560>; This is not explained in the commit msg. > touch-key-connected; > avdd-supply = <&ldo30_reg>; > vdd-supply = <&ldo31_reg>; > ledvdd-supply = <&ldo33_reg>; > }; > }; > > -- > 2.55.0 > ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v5 2/3] Input: stmfts - support FTS5 2026-08-10 15:21 [PATCH v5 0/3] Input: support for STM FTS5 David Heidelberg via B4 Relay 2026-08-10 15:21 ` [PATCH v5 1/3] dt-bindings: input: touchscreen: st,stmfts: Introduce " David Heidelberg via B4 Relay @ 2026-08-10 15:21 ` David Heidelberg via B4 Relay 2026-08-13 19:42 ` Dmitry Torokhov 2026-08-10 15:21 ` [PATCH v5 3/3] arm64: dts: qcom: sdm845-google: Add STM FTS touchscreen support David Heidelberg via B4 Relay 2 siblings, 1 reply; 6+ messages in thread From: David Heidelberg via B4 Relay @ 2026-08-10 15:21 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 From: David Heidelberg <david@ixit.cz> FTS support SLPI and AP mode, introduce mode-switch GPIO to switch between those two. Currently we can handle only full power AP mode, so we just keep the AP on. Useful for devices like Pixel 3 (blueline), Pixel 4a (sunfish), Xiaomi Mi 8 (dipper), and many others. Based on work of Petr Hodina <petr.hodina@protonmail.com> Signed-off-by: David Heidelberg <david@ixit.cz> --- drivers/input/touchscreen/stmfts.c | 430 ++++++++++++++++++++++++++++++++++--- 1 file changed, 396 insertions(+), 34 deletions(-) diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index 972687797f826..1da113d7197e6 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -1,23 +1,27 @@ // 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 + * + * 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/gpio/consumer.h> #include <linux/i2c.h> #include <linux/input/mt.h> #include <linux/input/touchscreen.h> #include <linux/interrupt.h> #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> /* I2C commands */ #define STMFTS_READ_INFO 0x80 #define STMFTS_READ_STATUS 0x84 #define STMFTS_READ_ONE_EVENT 0x85 #define STMFTS_READ_ALL_EVENT 0x86 @@ -30,16 +34,17 @@ #define STMFTS_SS_HOVER_SENSE_ON 0x95 #define STMFTS_MS_KEY_SENSE_OFF 0x9a #define STMFTS_MS_KEY_SENSE_ON 0x9b #define STMFTS_SYSTEM_RESET 0xa0 #define STMFTS_CLEAR_EVENT_STACK 0xa1 #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 #define STMFTS_EV_MULTI_TOUCH_DETECTED 0x02 #define STMFTS_EV_MULTI_TOUCH_ENTER 0x03 #define STMFTS_EV_MULTI_TOUCH_LEAVE 0x04 #define STMFTS_EV_MULTI_TOUCH_MOTION 0x05 #define STMFTS_EV_HOVER_ENTER 0x07 @@ -47,22 +52,42 @@ #define STMFTS_EV_HOVER_MOTION 0x09 #define STMFTS_EV_KEY_STATUS 0x0e #define STMFTS_EV_ERROR 0x0f #define STMFTS_EV_CONTROLLER_READY 0x10 #define STMFTS_EV_SLEEP_OUT_CONTROLLER_READY 0x11 #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 #define STMFTS_MASK_KEY_MENU 0x01 #define STMFTS_MASK_KEY_BACK 0x02 #define STMFTS_EVENT_SIZE 8 #define STMFTS_STACK_DEPTH 32 @@ -71,19 +96,22 @@ #define STMFTS_DEV_NAME "stmfts" static const struct regulator_bulk_data stmfts_supplies[] = { { .supply = "vdd" }, { .supply = "avdd" }, }; struct stmfts_data { + const struct stmfts_chip_ops *ops; + struct i2c_client *client; struct input_dev *input; struct gpio_desc *reset_gpio; + struct gpio_desc *mode_switch_gpio; struct led_classdev led_cdev; struct mutex mutex; struct touchscreen_properties prop; struct regulator_bulk_data *supplies; /* @@ -100,19 +128,31 @@ struct stmfts_data { u8 data[STMFTS_DATA_MAX_SIZE]; struct completion cmd_done; bool use_key; bool led_status; bool hover_enabled; + bool stylus_enabled; bool running; }; +struct stmfts_chip_ops { + int (*configure)(struct stmfts_data *sdata); + void (*power_off)(struct stmfts_data *sdata); + int (*setup_input)(struct stmfts_data *sdata); + int (*input_open)(struct input_dev *dev); + void (*input_close)(struct input_dev *dev); + void (*parse_events)(struct stmfts_data *sdata); + int (*set_hover)(struct stmfts_data *sdata, bool enable); + int (*runtime_resume)(struct stmfts_data *sdata); +}; + static int stmfts_brightness_set(struct led_classdev *led_cdev, enum led_brightness value) { struct stmfts_data *sdata = container_of(led_cdev, struct stmfts_data, led_cdev); int err; if (value != sdata->led_status && sdata->ledvdd) { @@ -165,16 +205,17 @@ static int stmfts_read_events(struct stmfts_data *sdata) ret = i2c_transfer(sdata->client->adapter, msgs, ARRAY_SIZE(msgs)); if (ret < 0) return ret; return ret == ARRAY_SIZE(msgs) ? 0 : -EIO; } +/* FTS4 event handling functions */ static void stmfts_report_contact_event(struct stmfts_data *sdata, const u8 event[]) { u8 slot_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4; u16 x = event[1] | ((event[2] & STMFTS_MASK_X_MSB) << 8); u16 y = (event[2] >> 4) | (event[3] << 4); u8 maj = event[4]; u8 min = event[5]; @@ -200,16 +241,114 @@ static void stmfts_report_contact_release(struct stmfts_data *sdata, u8 slot_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4; input_mt_slot(sdata->input, slot_id); input_mt_report_slot_inactive(sdata->input); 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; + + /* 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; + break; + } + fallthrough; /* Report as finger if stylus not enabled */ + + case STMFTS_TOUCH_TYPE_FINGER: + case STMFTS_TOUCH_TYPE_GLOVE: + tool = MT_TOOL_FINGER; + break; + + case STMFTS_TOUCH_TYPE_PALM: + /* Palm touch - report but can be filtered by userspace */ + tool = MT_TOOL_PALM; + break; + + case STMFTS_TOUCH_TYPE_HOVER: + tool = MT_TOOL_FINGER; + area = 0; + distance = 255; + 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; + } + + input_mt_slot(sdata->input, touch_id); + 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); +} + +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; + + input_mt_slot(sdata->input, touch_id); + input_mt_report_slot_inactive(sdata->input); +} + static void stmfts_report_hover_event(struct stmfts_data *sdata, const u8 event[]) { u16 x = (event[2] << 4) | (event[4] >> 4); u16 y = (event[3] << 4) | (event[4] & STMFTS_MASK_Y_LSB); u8 z = event[5]; input_report_abs(sdata->input, ABS_X, x); @@ -292,29 +431,72 @@ static void stmfts_parse_events(struct stmfts_data *sdata) default: dev_err(&sdata->client->dev, "unknown event %#02x\n", event[0]); } } } +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: + goto sync; + + 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]); + } + } + +sync: + input_mt_sync_frame(sdata->input); + input_sync(sdata->input); +} + static irqreturn_t stmfts_irq_handler(int irq, void *dev) { struct stmfts_data *sdata = dev; int err; guard(mutex)(&sdata->mutex); err = stmfts_read_events(sdata); if (unlikely(err)) dev_err(&sdata->client->dev, "failed to read events: %d\n", err); else - stmfts_parse_events(sdata); + sdata->ops->parse_events(sdata); return IRQ_HANDLED; } static int stmfts_command(struct stmfts_data *sdata, const u8 cmd) { int err; @@ -326,16 +508,29 @@ static int stmfts_command(struct stmfts_data *sdata, const u8 cmd) if (!wait_for_completion_timeout(&sdata->cmd_done, msecs_to_jiffies(1000))) return -ETIMEDOUT; return 0; } +static int stmfts5_set_scan_mode(struct stmfts_data *sdata, const u8 val) +{ + u8 scan_mode_cmd[3] = { STMFTS5_SET_SCAN_MODE, 0x00, val }; + int err; + + err = i2c_master_send(sdata->client, scan_mode_cmd, + sizeof(scan_mode_cmd)); + if (err != sizeof(scan_mode_cmd)) + return err < 0 ? err : -EIO; + + return 0; +} + static int stmfts_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; @@ -365,16 +560,37 @@ static int stmfts_input_open(struct input_dev *dev) /* I can still use only the touch screen */ dev_warn(&sdata->client->dev, "failed to enable touchkey\n"); } 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; + + err = stmfts5_set_scan_mode(sdata, 0xff); + if (err) { + pm_runtime_put_sync(&sdata->client->dev); + return err; + } + + scoped_guard(mutex, &sdata->mutex) + sdata->running = true; + + return 0; +} + static void stmfts_input_close(struct input_dev *dev) { struct stmfts_data *sdata = input_get_drvdata(dev); int err; err = i2c_smbus_write_byte(sdata->client, STMFTS_MS_MT_SENSE_OFF); if (err) dev_warn(&sdata->client->dev, @@ -398,16 +614,32 @@ static void stmfts_input_close(struct input_dev *dev) if (err) dev_warn(&sdata->client->dev, "failed to disable touchkey: %d\n", err); } 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); + + scoped_guard(mutex, &sdata->mutex) + sdata->running = false; + + pm_runtime_put_sync(&sdata->client->dev); +} + static ssize_t stmfts_sysfs_chip_id(struct device *dev, struct device_attribute *attr, char *buf) { struct stmfts_data *sdata = dev_get_drvdata(dev); return sysfs_emit(buf, "%#x\n", sdata->chip_id); } @@ -482,20 +714,18 @@ static ssize_t stmfts_sysfs_hover_enable_write(struct device *dev, if (kstrtoul(buf, 0, &value)) return -EINVAL; hover = !!value; guard(mutex)(&sdata->mutex); if (hover != sdata->hover_enabled) { - if (sdata->running) { - err = i2c_smbus_write_byte(sdata->client, - value ? STMFTS_SS_HOVER_SENSE_ON : - STMFTS_SS_HOVER_SENSE_OFF); + if (sdata->running && sdata->ops->set_hover) { + err = sdata->ops->set_hover(sdata, hover); if (err) return err; } sdata->hover_enabled = hover; } return len; @@ -551,16 +781,20 @@ static void stmfts_reset(struct stmfts_data *sdata) gpiod_set_value_cansleep(sdata->reset_gpio, 0); msleep(50); } static int stmfts_configure(struct stmfts_data *sdata) { int err; + err = stmfts_read_system_info(sdata); + if (err) + return err; + err = stmfts_command(sdata, STMFTS_SYSTEM_RESET); if (err) return err; err = stmfts_command(sdata, STMFTS_SLEEP_OUT); if (err) return err; @@ -596,55 +830,123 @@ static int stmfts_power_on(struct stmfts_data *sdata) * The datasheet does not specify the power on time, but considering * that the reset time is < 10ms, I sleep 20ms to be sure */ msleep(20); if (sdata->reset_gpio) stmfts_reset(sdata); - err = stmfts_read_system_info(sdata); - if (err) - goto err_disable_regulators; - enable_irq(sdata->client->irq); msleep(50); - err = stmfts_configure(sdata); + err = sdata->ops->configure(sdata); if (err) goto err_disable_irq; /* * At this point no one is using the touchscreen * and I don't really care about the return value */ (void)i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_IN); return 0; err_disable_irq: disable_irq(sdata->client->irq); -err_disable_regulators: regulator_bulk_disable(ARRAY_SIZE(stmfts_supplies), sdata->supplies); return err; } +static int stmfts5_configure(struct stmfts_data *sdata) +{ + u8 event[STMFTS_EVENT_SIZE]; + int err; + + /* Verify I2C communication */ + err = i2c_smbus_read_i2c_block_data(sdata->client, + STMFTS_READ_ALL_EVENT, + sizeof(event), event); + if (err < 0) + return err; + + return 0; +} + +static void stmfts5_chip_power_off(struct stmfts_data *sdata) +{ + i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_IN); + msleep(20); +} + static void stmfts_power_off(void *data) { struct stmfts_data *sdata = data; disable_irq(sdata->client->irq); if (sdata->reset_gpio) gpiod_set_value_cansleep(sdata->reset_gpio, 1); + if (sdata->ops->power_off) + sdata->ops->power_off(sdata); + regulator_bulk_disable(ARRAY_SIZE(stmfts_supplies), sdata->supplies); } +static int stmfts_setup_input(struct stmfts_data *sdata) +{ + struct device *dev = &sdata->client->dev; + + 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) { + input_set_capability(sdata->input, EV_KEY, KEY_MENU); + input_set_capability(sdata->input, EV_KEY, KEY_BACK); + } + + return input_mt_init_slots(sdata->input, STMFTS_MAX_FINGERS, + INPUT_MT_DIRECT); +} + +static int stmfts5_setup_input(struct stmfts_data *sdata) +{ + struct device *dev = &sdata->client->dev; + + sdata->mode_switch_gpio = devm_gpiod_get_optional(dev, "mode-switch", + GPIOD_OUT_HIGH); + if (IS_ERR(sdata->mode_switch_gpio)) + return dev_err_probe(dev, PTR_ERR(sdata->mode_switch_gpio), + "Failed to get GPIO 'switch'\n"); + + /* Set resolution for accurate calibration */ + if (!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); + } + + 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"); + + return input_mt_init_slots(sdata->input, STMFTS_MAX_FINGERS, + INPUT_MT_DIRECT); +} + +static int stmfts_set_hover(struct stmfts_data *sdata, bool enable) +{ + return i2c_smbus_write_byte(sdata->client, + enable ? STMFTS_SS_HOVER_SENSE_ON : + STMFTS_SS_HOVER_SENSE_OFF); +} + static int stmfts_enable_led(struct stmfts_data *sdata) { int err; /* get the regulator for powering the leds on */ sdata->ledvdd = devm_regulator_get(&sdata->client->dev, "ledvdd"); if (IS_ERR(sdata->ledvdd)) return PTR_ERR(sdata->ledvdd); @@ -680,16 +982,18 @@ static int stmfts_probe(struct i2c_client *client) return -ENOMEM; i2c_set_clientdata(client, sdata); sdata->client = client; mutex_init(&sdata->mutex); init_completion(&sdata->cmd_done); + sdata->ops = of_device_get_match_data(dev); + err = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(stmfts_supplies), stmfts_supplies, &sdata->supplies); if (err) return err; sdata->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); @@ -698,37 +1002,28 @@ static int stmfts_probe(struct i2c_client *client) "Failed to get GPIO 'reset'\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; + sdata->input->open = sdata->ops->input_open; + sdata->input->close = sdata->ops->input_close; 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); 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); - - sdata->use_key = device_property_read_bool(dev, "touch-key-connected"); - if (sdata->use_key) { - 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); + err = sdata->ops->setup_input(sdata); if (err) return err; input_set_drvdata(sdata->input, sdata); /* * stmfts_power_on expects interrupt to be disabled, but * at this point the device is still off and I do not trust @@ -789,27 +1084,71 @@ static int stmfts_runtime_suspend(struct device *dev) ret = i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_IN); if (ret) dev_warn(dev, "failed to suspend device: %d\n", ret); return ret; } +static int stmfts_chip_runtime_resume(struct stmfts_data *sdata) +{ + return i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_OUT); +} + +static int stmfts5_chip_runtime_resume(struct stmfts_data *sdata) +{ + struct i2c_client *client = sdata->client; + struct device *dev = &client->dev; + u8 int_enable_cmd[4] = { 0xB6, 0x00, 0x2C, 0x01 }; + int err; + + err = i2c_smbus_write_byte(client, STMFTS_SLEEP_OUT); + if (err) + return err; + + msleep(20); + + /* Perform capacitance tuning after wakeup */ + err = i2c_smbus_write_byte(client, STMFTS_MS_CX_TUNING); + if (err) + dev_warn(dev, "MS_CX_TUNING failed: %d\n", err); + msleep(20); + + err = i2c_smbus_write_byte(client, STMFTS_SS_CX_TUNING); + if (err) + dev_warn(dev, "SS_CX_TUNING failed: %d\n", err); + msleep(20); + + /* Force calibration */ + err = i2c_smbus_write_byte(client, STMFTS_FULL_FORCE_CALIBRATION); + if (err) + dev_warn(dev, "FORCE_CALIBRATION failed: %d\n", err); + msleep(50); + + /* Enable controller interrupts */ + err = i2c_master_send(client, int_enable_cmd, sizeof(int_enable_cmd)); + if (err != sizeof(int_enable_cmd)) + return err < 0 ? err : -EIO; + + msleep(20); + + return 0; +} + static int stmfts_runtime_resume(struct device *dev) { struct stmfts_data *sdata = dev_get_drvdata(dev); - struct i2c_client *client = sdata->client; - int ret; + int err; - ret = i2c_smbus_write_byte(client, STMFTS_SLEEP_OUT); - if (ret) - dev_err(dev, "failed to resume device: %d\n", ret); + err = sdata->ops->runtime_resume(sdata); + if (err) + dev_err(dev, "failed to resume device: %d\n", err); - return ret; + return err; } static int stmfts_suspend(struct device *dev) { struct stmfts_data *sdata = dev_get_drvdata(dev); stmfts_power_off(sdata); @@ -824,18 +1163,39 @@ static int stmfts_resume(struct device *dev) } static const struct dev_pm_ops stmfts_pm_ops = { SYSTEM_SLEEP_PM_OPS(stmfts_suspend, stmfts_resume) RUNTIME_PM_OPS(stmfts_runtime_suspend, stmfts_runtime_resume, NULL) }; #ifdef CONFIG_OF +static const struct stmfts_chip_ops stmfts4_ops = { + .configure = stmfts_configure, + .setup_input = stmfts_setup_input, + .input_open = stmfts_input_open, + .input_close = stmfts_input_close, + .parse_events = stmfts_parse_events, + .set_hover = stmfts_set_hover, + .runtime_resume = stmfts_chip_runtime_resume, +}; + +static const struct stmfts_chip_ops stmfts5_ops = { + .configure = stmfts5_configure, + .power_off = stmfts5_chip_power_off, + .setup_input = stmfts5_setup_input, + .input_open = stmfts5_input_open, + .input_close = stmfts5_input_close, + .parse_events = stmfts5_parse_events, + .runtime_resume = stmfts5_chip_runtime_resume, +}; + static const struct of_device_id stmfts_of_match[] = { - { .compatible = "st,stmfts", }, + { .compatible = "st,stmfts", .data = &stmfts4_ops }, + { .compatible = "st,stmfts5", .data = &stmfts5_ops }, { }, }; MODULE_DEVICE_TABLE(of, stmfts_of_match); #endif static const struct i2c_device_id stmfts_id[] = { { .name = "stmfts" }, { } @@ -853,10 +1213,12 @@ static struct i2c_driver stmfts_driver = { .probe = stmfts_probe, .remove = stmfts_remove, .id_table = stmfts_id, }; 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"); -- 2.55.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v5 2/3] Input: stmfts - support FTS5 2026-08-10 15:21 ` [PATCH v5 2/3] Input: stmfts - support FTS5 David Heidelberg via B4 Relay @ 2026-08-13 19:42 ` Dmitry Torokhov 0 siblings, 0 replies; 6+ messages in thread From: Dmitry Torokhov @ 2026-08-13 19:42 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 Hi David, On Mon, Aug 10, 2026 at 05:21:41PM +0200, David Heidelberg via B4 Relay wrote: > From: David Heidelberg <david@ixit.cz> > > FTS support SLPI and AP mode, introduce mode-switch GPIO to switch between > those two. Currently we can handle only full power AP mode, so we just > keep the AP on. > > Useful for devices like Pixel 3 (blueline), Pixel 4a (sunfish), > Xiaomi Mi 8 (dipper), and many others. > > Based on work of Petr Hodina <petr.hodina@protonmail.com> > Signed-off-by: David Heidelberg <david@ixit.cz> > --- > drivers/input/touchscreen/stmfts.c | 430 ++++++++++++++++++++++++++++++++++--- > 1 file changed, 396 insertions(+), 34 deletions(-) > > diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c > index 972687797f826..1da113d7197e6 100644 > --- a/drivers/input/touchscreen/stmfts.c > +++ b/drivers/input/touchscreen/stmfts.c > @@ -1,23 +1,27 @@ > // 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 > + * > + * 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/gpio/consumer.h> > #include <linux/i2c.h> > #include <linux/input/mt.h> > #include <linux/input/touchscreen.h> > #include <linux/interrupt.h> > #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> > > /* I2C commands */ > #define STMFTS_READ_INFO 0x80 > #define STMFTS_READ_STATUS 0x84 > #define STMFTS_READ_ONE_EVENT 0x85 > #define STMFTS_READ_ALL_EVENT 0x86 > @@ -30,16 +34,17 @@ > #define STMFTS_SS_HOVER_SENSE_ON 0x95 > #define STMFTS_MS_KEY_SENSE_OFF 0x9a > #define STMFTS_MS_KEY_SENSE_ON 0x9b > #define STMFTS_SYSTEM_RESET 0xa0 > #define STMFTS_CLEAR_EVENT_STACK 0xa1 > #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 > #define STMFTS_EV_MULTI_TOUCH_DETECTED 0x02 > #define STMFTS_EV_MULTI_TOUCH_ENTER 0x03 > #define STMFTS_EV_MULTI_TOUCH_LEAVE 0x04 > #define STMFTS_EV_MULTI_TOUCH_MOTION 0x05 > #define STMFTS_EV_HOVER_ENTER 0x07 > @@ -47,22 +52,42 @@ > #define STMFTS_EV_HOVER_MOTION 0x09 > #define STMFTS_EV_KEY_STATUS 0x0e > #define STMFTS_EV_ERROR 0x0f > #define STMFTS_EV_CONTROLLER_READY 0x10 > #define STMFTS_EV_SLEEP_OUT_CONTROLLER_READY 0x11 > #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 > #define STMFTS_MASK_KEY_MENU 0x01 > #define STMFTS_MASK_KEY_BACK 0x02 > > #define STMFTS_EVENT_SIZE 8 > #define STMFTS_STACK_DEPTH 32 > @@ -71,19 +96,22 @@ > #define STMFTS_DEV_NAME "stmfts" > > static const struct regulator_bulk_data stmfts_supplies[] = { > { .supply = "vdd" }, > { .supply = "avdd" }, > }; > > struct stmfts_data { > + const struct stmfts_chip_ops *ops; > + > struct i2c_client *client; > struct input_dev *input; > struct gpio_desc *reset_gpio; > + struct gpio_desc *mode_switch_gpio; > struct led_classdev led_cdev; > struct mutex mutex; > > struct touchscreen_properties prop; > > struct regulator_bulk_data *supplies; > > /* > @@ -100,19 +128,31 @@ struct stmfts_data { > > u8 data[STMFTS_DATA_MAX_SIZE]; > > struct completion cmd_done; > > bool use_key; > bool led_status; > bool hover_enabled; > + bool stylus_enabled; > bool running; > }; > > +struct stmfts_chip_ops { > + int (*configure)(struct stmfts_data *sdata); > + void (*power_off)(struct stmfts_data *sdata); > + int (*setup_input)(struct stmfts_data *sdata); > + int (*input_open)(struct input_dev *dev); > + void (*input_close)(struct input_dev *dev); > + void (*parse_events)(struct stmfts_data *sdata); > + int (*set_hover)(struct stmfts_data *sdata, bool enable); > + int (*runtime_resume)(struct stmfts_data *sdata); > +}; > + > static int stmfts_brightness_set(struct led_classdev *led_cdev, > enum led_brightness value) > { > struct stmfts_data *sdata = container_of(led_cdev, > struct stmfts_data, led_cdev); > int err; > > if (value != sdata->led_status && sdata->ledvdd) { > @@ -165,16 +205,17 @@ static int stmfts_read_events(struct stmfts_data *sdata) > > ret = i2c_transfer(sdata->client->adapter, msgs, ARRAY_SIZE(msgs)); > if (ret < 0) > return ret; > > return ret == ARRAY_SIZE(msgs) ? 0 : -EIO; > } > > +/* FTS4 event handling functions */ > static void stmfts_report_contact_event(struct stmfts_data *sdata, > const u8 event[]) > { > u8 slot_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4; > u16 x = event[1] | ((event[2] & STMFTS_MASK_X_MSB) << 8); > u16 y = (event[2] >> 4) | (event[3] << 4); > u8 maj = event[4]; > u8 min = event[5]; > @@ -200,16 +241,114 @@ static void stmfts_report_contact_release(struct stmfts_data *sdata, > u8 slot_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4; > > input_mt_slot(sdata->input, slot_id); > input_mt_report_slot_inactive(sdata->input); > > 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; > + > + /* 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); This does not match the comment above. This treats byte[4] as Y MSB, but comment above says that it is Y LSB... > + > + /* Parse pressure - ensure non-zero for active touch */ > + area = event[5]; > + if (area <= 0 && touch_type != STMFTS_TOUCH_TYPE_HOVER) { Area can't be less than 0, it's a u8. > + /* 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; > + } Should this check be pas the switch()? No need to complain about area for invalid touch types. > + > + /* Parse touch area with improved bit extraction */ > + maj = (((event[0] & 0x0C) << 2) | ((event[6] & 0xF0) >> 4)); Why do we need event[0] in major? It contains event ID... Could you add an comment explaining it. > + 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; > + break; > + } > + fallthrough; /* Report as finger if stylus not enabled */ > + > + case STMFTS_TOUCH_TYPE_FINGER: > + case STMFTS_TOUCH_TYPE_GLOVE: > + tool = MT_TOOL_FINGER; > + break; > + > + case STMFTS_TOUCH_TYPE_PALM: > + /* Palm touch - report but can be filtered by userspace */ > + tool = MT_TOOL_PALM; > + break; > + > + case STMFTS_TOUCH_TYPE_HOVER: > + tool = MT_TOOL_FINGER; > + area = 0; > + distance = 255; > + 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; > + } > + > + input_mt_slot(sdata->input, touch_id); > + 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); > +} > + > +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; > + > + input_mt_slot(sdata->input, touch_id); > + input_mt_report_slot_inactive(sdata->input); > +} > + > static void stmfts_report_hover_event(struct stmfts_data *sdata, > const u8 event[]) > { > u16 x = (event[2] << 4) | (event[4] >> 4); > u16 y = (event[3] << 4) | (event[4] & STMFTS_MASK_Y_LSB); > u8 z = event[5]; > > input_report_abs(sdata->input, ABS_X, x); > @@ -292,29 +431,72 @@ static void stmfts_parse_events(struct stmfts_data *sdata) > > default: > dev_err(&sdata->client->dev, > "unknown event %#02x\n", event[0]); > } > } > } > > +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: > + goto sync; > + > + 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]); > + } > + } > + > +sync: > + input_mt_sync_frame(sdata->input); > + input_sync(sdata->input); > +} > + > static irqreturn_t stmfts_irq_handler(int irq, void *dev) > { > struct stmfts_data *sdata = dev; > int err; > > guard(mutex)(&sdata->mutex); > > err = stmfts_read_events(sdata); > if (unlikely(err)) > dev_err(&sdata->client->dev, > "failed to read events: %d\n", err); > else > - stmfts_parse_events(sdata); > + sdata->ops->parse_events(sdata); > > return IRQ_HANDLED; > } > > static int stmfts_command(struct stmfts_data *sdata, const u8 cmd) > { > int err; > > @@ -326,16 +508,29 @@ static int stmfts_command(struct stmfts_data *sdata, const u8 cmd) > > if (!wait_for_completion_timeout(&sdata->cmd_done, > msecs_to_jiffies(1000))) > return -ETIMEDOUT; > > return 0; > } > > +static int stmfts5_set_scan_mode(struct stmfts_data *sdata, const u8 val) > +{ > + u8 scan_mode_cmd[3] = { STMFTS5_SET_SCAN_MODE, 0x00, val }; > + int err; > + > + err = i2c_master_send(sdata->client, scan_mode_cmd, > + sizeof(scan_mode_cmd)); > + if (err != sizeof(scan_mode_cmd)) > + return err < 0 ? err : -EIO; > + > + return 0; > +} > + > static int stmfts_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; > @@ -365,16 +560,37 @@ static int stmfts_input_open(struct input_dev *dev) > /* I can still use only the touch screen */ > dev_warn(&sdata->client->dev, > "failed to enable touchkey\n"); > } > > 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; > + > + err = stmfts5_set_scan_mode(sdata, 0xff); > + if (err) { > + pm_runtime_put_sync(&sdata->client->dev); > + return err; > + } > + > + scoped_guard(mutex, &sdata->mutex) > + sdata->running = true; > + > + return 0; I wonder if input open/close can not be merged into shared implementations. You already have set_hover() and other helpers... > +} > + > static void stmfts_input_close(struct input_dev *dev) > { > struct stmfts_data *sdata = input_get_drvdata(dev); > int err; > > err = i2c_smbus_write_byte(sdata->client, STMFTS_MS_MT_SENSE_OFF); > if (err) > dev_warn(&sdata->client->dev, > @@ -398,16 +614,32 @@ static void stmfts_input_close(struct input_dev *dev) > if (err) > dev_warn(&sdata->client->dev, > "failed to disable touchkey: %d\n", err); > } > > 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); > + > + scoped_guard(mutex, &sdata->mutex) > + sdata->running = false; > + > + pm_runtime_put_sync(&sdata->client->dev); > +} > + > static ssize_t stmfts_sysfs_chip_id(struct device *dev, > struct device_attribute *attr, char *buf) > { > struct stmfts_data *sdata = dev_get_drvdata(dev); > > return sysfs_emit(buf, "%#x\n", sdata->chip_id); > } > > @@ -482,20 +714,18 @@ static ssize_t stmfts_sysfs_hover_enable_write(struct device *dev, > if (kstrtoul(buf, 0, &value)) > return -EINVAL; > > hover = !!value; > > guard(mutex)(&sdata->mutex); > > if (hover != sdata->hover_enabled) { > - if (sdata->running) { > - err = i2c_smbus_write_byte(sdata->client, > - value ? STMFTS_SS_HOVER_SENSE_ON : > - STMFTS_SS_HOVER_SENSE_OFF); > + if (sdata->running && sdata->ops->set_hover) { > + err = sdata->ops->set_hover(sdata, hover); > if (err) > return err; > } > > sdata->hover_enabled = hover; > } > > return len; > @@ -551,16 +781,20 @@ static void stmfts_reset(struct stmfts_data *sdata) > gpiod_set_value_cansleep(sdata->reset_gpio, 0); > msleep(50); > } > > static int stmfts_configure(struct stmfts_data *sdata) > { > int err; > > + err = stmfts_read_system_info(sdata); > + if (err) > + return err; > + > err = stmfts_command(sdata, STMFTS_SYSTEM_RESET); > if (err) > return err; > > err = stmfts_command(sdata, STMFTS_SLEEP_OUT); > if (err) > return err; > > @@ -596,55 +830,123 @@ static int stmfts_power_on(struct stmfts_data *sdata) > * The datasheet does not specify the power on time, but considering > * that the reset time is < 10ms, I sleep 20ms to be sure > */ > msleep(20); > > if (sdata->reset_gpio) > stmfts_reset(sdata); > > - err = stmfts_read_system_info(sdata); > - if (err) > - goto err_disable_regulators; > - > enable_irq(sdata->client->irq); > > msleep(50); > > - err = stmfts_configure(sdata); > + err = sdata->ops->configure(sdata); > if (err) > goto err_disable_irq; > > /* > * At this point no one is using the touchscreen > * and I don't really care about the return value > */ > (void)i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_IN); > > return 0; > > err_disable_irq: > disable_irq(sdata->client->irq); > -err_disable_regulators: > regulator_bulk_disable(ARRAY_SIZE(stmfts_supplies), sdata->supplies); > return err; > } > > +static int stmfts5_configure(struct stmfts_data *sdata) > +{ > + u8 event[STMFTS_EVENT_SIZE]; > + int err; > + > + /* Verify I2C communication */ > + err = i2c_smbus_read_i2c_block_data(sdata->client, > + STMFTS_READ_ALL_EVENT, > + sizeof(event), event); > + if (err < 0) > + return err; > + > + return 0; > +} > + > +static void stmfts5_chip_power_off(struct stmfts_data *sdata) > +{ > + i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_IN); > + msleep(20); > +} > + > static void stmfts_power_off(void *data) > { > struct stmfts_data *sdata = data; > > disable_irq(sdata->client->irq); > > if (sdata->reset_gpio) > gpiod_set_value_cansleep(sdata->reset_gpio, 1); > > + if (sdata->ops->power_off) > + sdata->ops->power_off(sdata); As Sashiko mentioned, you want to do this before asserting reset. > + > regulator_bulk_disable(ARRAY_SIZE(stmfts_supplies), sdata->supplies); > } > > +static int stmfts_setup_input(struct stmfts_data *sdata) > +{ > + struct device *dev = &sdata->client->dev; > + > + 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) { > + input_set_capability(sdata->input, EV_KEY, KEY_MENU); > + input_set_capability(sdata->input, EV_KEY, KEY_BACK); > + } > + > + return input_mt_init_slots(sdata->input, STMFTS_MAX_FINGERS, > + INPUT_MT_DIRECT); > +} > + > +static int stmfts5_setup_input(struct stmfts_data *sdata) > +{ > + struct device *dev = &sdata->client->dev; > + > + sdata->mode_switch_gpio = devm_gpiod_get_optional(dev, "mode-switch", > + GPIOD_OUT_HIGH); > + if (IS_ERR(sdata->mode_switch_gpio)) > + return dev_err_probe(dev, PTR_ERR(sdata->mode_switch_gpio), > + "Failed to get GPIO 'switch'\n"); > + > + /* Set resolution for accurate calibration */ > + if (!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); > + } > + > + 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"); > + > + return input_mt_init_slots(sdata->input, STMFTS_MAX_FINGERS, > + INPUT_MT_DIRECT); > +} > + > +static int stmfts_set_hover(struct stmfts_data *sdata, bool enable) > +{ > + return i2c_smbus_write_byte(sdata->client, > + enable ? STMFTS_SS_HOVER_SENSE_ON : > + STMFTS_SS_HOVER_SENSE_OFF); > +} > + > static int stmfts_enable_led(struct stmfts_data *sdata) > { > int err; > > /* get the regulator for powering the leds on */ > sdata->ledvdd = devm_regulator_get(&sdata->client->dev, "ledvdd"); > if (IS_ERR(sdata->ledvdd)) > return PTR_ERR(sdata->ledvdd); > @@ -680,16 +982,18 @@ static int stmfts_probe(struct i2c_client *client) > return -ENOMEM; > > i2c_set_clientdata(client, sdata); > > sdata->client = client; > mutex_init(&sdata->mutex); > init_completion(&sdata->cmd_done); > > + sdata->ops = of_device_get_match_data(dev); > + > err = devm_regulator_bulk_get_const(dev, > ARRAY_SIZE(stmfts_supplies), > stmfts_supplies, > &sdata->supplies); > if (err) > return err; > > sdata->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); > @@ -698,37 +1002,28 @@ static int stmfts_probe(struct i2c_client *client) > "Failed to get GPIO 'reset'\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; > + sdata->input->open = sdata->ops->input_open; > + sdata->input->close = sdata->ops->input_close; > > 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); > > 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); > - > - sdata->use_key = device_property_read_bool(dev, "touch-key-connected"); > - if (sdata->use_key) { > - 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); > + err = sdata->ops->setup_input(sdata); > if (err) > return err; > > input_set_drvdata(sdata->input, sdata); > > /* > * stmfts_power_on expects interrupt to be disabled, but > * at this point the device is still off and I do not trust > @@ -789,27 +1084,71 @@ static int stmfts_runtime_suspend(struct device *dev) > > ret = i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_IN); > if (ret) > dev_warn(dev, "failed to suspend device: %d\n", ret); > > return ret; > } > > +static int stmfts_chip_runtime_resume(struct stmfts_data *sdata) > +{ > + return i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_OUT); > +} > + > +static int stmfts5_chip_runtime_resume(struct stmfts_data *sdata) > +{ > + struct i2c_client *client = sdata->client; > + struct device *dev = &client->dev; > + u8 int_enable_cmd[4] = { 0xB6, 0x00, 0x2C, 0x01 }; > + int err; > + > + err = i2c_smbus_write_byte(client, STMFTS_SLEEP_OUT); > + if (err) > + return err; > + > + msleep(20); > + > + /* Perform capacitance tuning after wakeup */ > + err = i2c_smbus_write_byte(client, STMFTS_MS_CX_TUNING); > + if (err) > + dev_warn(dev, "MS_CX_TUNING failed: %d\n", err); > + msleep(20); > + > + err = i2c_smbus_write_byte(client, STMFTS_SS_CX_TUNING); > + if (err) > + dev_warn(dev, "SS_CX_TUNING failed: %d\n", err); > + msleep(20); > + > + /* Force calibration */ > + err = i2c_smbus_write_byte(client, STMFTS_FULL_FORCE_CALIBRATION); > + if (err) > + dev_warn(dev, "FORCE_CALIBRATION failed: %d\n", err); > + msleep(50); > + > + /* Enable controller interrupts */ > + err = i2c_master_send(client, int_enable_cmd, sizeof(int_enable_cmd)); > + if (err != sizeof(int_enable_cmd)) > + return err < 0 ? err : -EIO; > + > + msleep(20); > + > + return 0; > +} > + > static int stmfts_runtime_resume(struct device *dev) > { > struct stmfts_data *sdata = dev_get_drvdata(dev); > - struct i2c_client *client = sdata->client; > - int ret; > + int err; > > - ret = i2c_smbus_write_byte(client, STMFTS_SLEEP_OUT); > - if (ret) > - dev_err(dev, "failed to resume device: %d\n", ret); > + err = sdata->ops->runtime_resume(sdata); > + if (err) > + dev_err(dev, "failed to resume device: %d\n", err); > > - return ret; > + return err; > } > > static int stmfts_suspend(struct device *dev) > { > struct stmfts_data *sdata = dev_get_drvdata(dev); > > stmfts_power_off(sdata); > > @@ -824,18 +1163,39 @@ static int stmfts_resume(struct device *dev) > } > > static const struct dev_pm_ops stmfts_pm_ops = { > SYSTEM_SLEEP_PM_OPS(stmfts_suspend, stmfts_resume) > RUNTIME_PM_OPS(stmfts_runtime_suspend, stmfts_runtime_resume, NULL) > }; > > #ifdef CONFIG_OF > +static const struct stmfts_chip_ops stmfts4_ops = { > + .configure = stmfts_configure, > + .setup_input = stmfts_setup_input, > + .input_open = stmfts_input_open, > + .input_close = stmfts_input_close, > + .parse_events = stmfts_parse_events, > + .set_hover = stmfts_set_hover, > + .runtime_resume = stmfts_chip_runtime_resume, > +}; > + > +static const struct stmfts_chip_ops stmfts5_ops = { > + .configure = stmfts5_configure, > + .power_off = stmfts5_chip_power_off, > + .setup_input = stmfts5_setup_input, > + .input_open = stmfts5_input_open, > + .input_close = stmfts5_input_close, > + .parse_events = stmfts5_parse_events, > + .runtime_resume = stmfts5_chip_runtime_resume, > +}; > + > static const struct of_device_id stmfts_of_match[] = { > - { .compatible = "st,stmfts", }, > + { .compatible = "st,stmfts", .data = &stmfts4_ops }, > + { .compatible = "st,stmfts5", .data = &stmfts5_ops }, > { }, > }; > MODULE_DEVICE_TABLE(of, stmfts_of_match); > #endif > > static const struct i2c_device_id stmfts_id[] = { > { .name = "stmfts" }, > { } > @@ -853,10 +1213,12 @@ static struct i2c_driver stmfts_driver = { > .probe = stmfts_probe, > .remove = stmfts_remove, > .id_table = stmfts_id, > }; > > 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"); > Thanks. -- Dmitry ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v5 3/3] arm64: dts: qcom: sdm845-google: Add STM FTS touchscreen support 2026-08-10 15:21 [PATCH v5 0/3] Input: support for STM FTS5 David Heidelberg via B4 Relay 2026-08-10 15:21 ` [PATCH v5 1/3] dt-bindings: input: touchscreen: st,stmfts: Introduce " David Heidelberg via B4 Relay 2026-08-10 15:21 ` [PATCH v5 2/3] Input: stmfts - support FTS5 David Heidelberg via B4 Relay @ 2026-08-10 15:21 ` David Heidelberg via B4 Relay 2 siblings, 0 replies; 6+ messages in thread From: David Heidelberg via B4 Relay @ 2026-08-10 15:21 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, Konrad Dybcio, Dmitry Baryshkov From: Petr Hodina <petr.hodina@protonmail.com> Basic touchscreen connected to second i2c bus. Signed-off-by: Petr Hodina <petr.hodina@protonmail.com> Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.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 | 19 ++++++++++++++++++- arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sdm845-google-blueline.dts b/arch/arm64/boot/dts/qcom/sdm845-google-blueline.dts index fa89be500fb85..51c835dca43f1 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-google-blueline.dts +++ b/arch/arm64/boot/dts/qcom/sdm845-google-blueline.dts @@ -21,17 +21,34 @@ &framebuffer0 { stride = <(1080 * 4)>; }; &i2c2 { clock-frequency = <1000000>; status = "okay"; - /* ST,FTS @ 49 */ + touchscreen@49 { + compatible = "st,stmfts5"; + reg = <0x49>; + + pinctrl-0 = <&touchscreen_irq_n>, <&touchscreen_reset>; + pinctrl-names = "default"; + + interrupts-extended = <&tlmm 125 IRQ_TYPE_LEVEL_LOW>; + + mode-switch-gpios = <&tlmm 136 GPIO_ACTIVE_HIGH>; + reset-gpios = <&tlmm 99 GPIO_ACTIVE_LOW>; + + avdd-supply = <&vreg_l19a_3p3>; + vdd-supply = <&vreg_l14a_1p8>; + + touchscreen-size-x = <1080>; + touchscreen-size-y = <2160>; + }; }; &mdss_dsi0 { vdda-supply = <&vdda_mipi_dsi0_1p2>; status = "okay"; panel@0 { diff --git a/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi index 9368fd6b87515..bd3b5d7b7396e 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi @@ -492,17 +492,17 @@ nfc_enable_default: nfc-enable-default-state { touchscreen_reset: ts-reset-state { pins = "gpio99"; function = "gpio"; drive-strength = <8>; bias-pull-up; }; - touchscreen_pins: ts-pins-gpio-state { + touchscreen_irq_n: ts-irq-n-gpio-state { pins = "gpio125"; function = "gpio"; drive-strength = <2>; bias-disable; }; touchscreen_i2c_pins: qup-i2c2-gpio-state { pins = "gpio27", "gpio28"; -- 2.55.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-08-13 19:42 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-08-10 15:21 [PATCH v5 0/3] Input: support for STM FTS5 David Heidelberg via B4 Relay 2026-08-10 15:21 ` [PATCH v5 1/3] dt-bindings: input: touchscreen: st,stmfts: Introduce " David Heidelberg via B4 Relay 2026-08-11 7:03 ` Krzysztof Kozlowski 2026-08-10 15:21 ` [PATCH v5 2/3] Input: stmfts - support FTS5 David Heidelberg via B4 Relay 2026-08-13 19:42 ` Dmitry Torokhov 2026-08-10 15:21 ` [PATCH v5 3/3] arm64: dts: qcom: sdm845-google: Add STM FTS touchscreen support David Heidelberg via B4 Relay
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox