From nobody Fri Apr 3 05:04:03 2026 Received: from mail.mainlining.org (mail.mainlining.org [5.75.144.95]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A7F802FDC37; Sat, 28 Feb 2026 01:57:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=5.75.144.95 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772243869; cv=none; b=IJshqEA+yfINsYhwQqVUi9/r3ipPY/ULEx2GXx6cclMYzAvEBKe3jHxL9X/S/+EEuOm5zMXvZhRPnmU4rG3eOi5emULBxXDNp2LUNX4LJy5Smot9mgNtCG9egH4Dz2HetYtYZFnbcZlH0gZ8NZdY8GYmiC/tfyvxH8pexKGMAP8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772243869; c=relaxed/simple; bh=hLEJw7NfpeakgWD5ooAEJP4vVxpcsaKGoIj7IO2V05M=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=HPEWTVUMTj2EzcJo8xeEFAq7sPz1y1JyVHHQ7udtZzhMkMTaD77RO6ANJA0sJTiGhIat82HnJoY67rywkkpEC/H+Kz6NIXQadwLxb33s2GzIkz83MZiNMeRwvDjCrHSn+CPh81/u/Th7JLm/Cg5u8+mLCbDiQ7QowY8FdlpTb/s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mainlining.org; spf=pass smtp.mailfrom=mainlining.org; dkim=pass (2048-bit key) header.d=mainlining.org header.i=@mainlining.org header.b=SD1wmzrE; dkim=permerror (0-bit key) header.d=mainlining.org header.i=@mainlining.org header.b=xGG6EyKO; arc=none smtp.client-ip=5.75.144.95 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mainlining.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mainlining.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=mainlining.org header.i=@mainlining.org header.b="SD1wmzrE"; dkim=permerror (0-bit key) header.d=mainlining.org header.i=@mainlining.org header.b="xGG6EyKO" DKIM-Signature: v=1; a=rsa-sha256; s=202507r; d=mainlining.org; c=relaxed/relaxed; h=To:Message-Id:Subject:Date:From; t=1772243805; bh=d7WI4CCze1zokydljk3Xtok 4i1TlNCgc0AyutExxLSc=; b=SD1wmzrEZdu7TtG1zGrpXFZFio8MT5TVivjARxgNUidfbAjhl/ jvIRPlliBvg9W1J5wjwhgyqHSaZY0sZESgzKCPtOSWEn99gFi8hh+t1ymqjSqu//DoRifbN3bkB 8XBlpr/lEf+f21/EaS5/k0GyVbbV39YHvpclsanUrE9JDLNYID9klScFbQZ3G3PXszObW9A5C0a u0bqMVbpCMe2cD9DK2a2+W243HhITrvYdo7Ynkk38INSfOvNzq3eF0G7QP4rERxCas2Www7G4gh 50E+IbT3O+wT2B4Bn4d3aKhWZEdi0GaM2z9GMeKLltLA6Ah58qwfuSrof+vBVWgp4rQ==; DKIM-Signature: v=1; a=ed25519-sha256; s=202507e; d=mainlining.org; c=relaxed/relaxed; h=To:Message-Id:Subject:Date:From; t=1772243805; bh=d7WI4CCze1zokydljk3Xtok 4i1TlNCgc0AyutExxLSc=; b=xGG6EyKO0YJR+0/e0LFzVEBPDhitJCsjVDVV8LTzw5Oq9eMrHk EI0eq/u1TRPtAeeEUrm89ppbfstYMnUxDsDQ==; From: Aelin Reidel Date: Sat, 28 Feb 2026 02:56:10 +0100 Subject: [PATCH v2 1/3] dt-bindings: input: document Goodix GTX8 Touchscreen ICs Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260228-gtx8-v2-1-3a408c365f6c@mainlining.org> References: <20260228-gtx8-v2-0-3a408c365f6c@mainlining.org> In-Reply-To: <20260228-gtx8-v2-0-3a408c365f6c@mainlining.org> To: Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Hans de Goede , Neil Armstrong , Henrik Rydberg Cc: linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux@mainlining.org, phone-devel@vger.kernel.org, ~postmarketos/upstreaming@lists.sr.ht, Aelin Reidel X-Mailer: b4 0.14.2 Document the Goodix GT9886 and GT9896 which are part of the GTX8 series of Touchscreen controller ICs from Goodix. Reviewed-by: Rob Herring (Arm) Signed-off-by: Aelin Reidel Acked-by: Hans de Goede --- .../bindings/input/touchscreen/goodix,gt9886.yaml | 71 ++++++++++++++++++= ++++ 1 file changed, 71 insertions(+) diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix,gt9= 886.yaml b/Documentation/devicetree/bindings/input/touchscreen/goodix,gt988= 6.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6307495c2746313cfc32cdbb701= 455d1596be435 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/goodix,gt9886.yaml @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/touchscreen/goodix,gt9886.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Goodix GTX8 series touchscreen controller + +maintainers: + - Aelin Reidel + +allOf: + - $ref: touchscreen.yaml# + +properties: + compatible: + enum: + - goodix,gt9886 + - goodix,gt9896 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + reset-gpios: + maxItems: 1 + + avdd-supply: + description: Analog power supply regulator on AVDD pin + + vddio-supply: + description: power supply regulator on VDDIO pin + + touchscreen-inverted-x: true + touchscreen-inverted-y: true + touchscreen-size-x: true + touchscreen-size-y: true + touchscreen-swapped-x-y: true + +additionalProperties: false + +required: + - compatible + - reg + - interrupts + - avdd-supply + - touchscreen-size-x + - touchscreen-size-y + +examples: + - | + #include + #include + i2c { + #address-cells =3D <1>; + #size-cells =3D <0>; + touchscreen@5d { + compatible =3D "goodix,gt9886"; + reg =3D <0x5d>; + interrupt-parent =3D <&gpio>; + interrupts =3D <9 IRQ_TYPE_LEVEL_LOW>; + reset-gpios =3D <&gpio1 8 GPIO_ACTIVE_LOW>; + avdd-supply =3D <&ts_avdd>; + touchscreen-size-x =3D <1080>; + touchscreen-size-y =3D <2340>; + }; + }; + +... --=20 2.53.0 From nobody Fri Apr 3 05:04:03 2026 Received: from mail.mainlining.org (mail.mainlining.org [5.75.144.95]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 04A402F0673; Sat, 28 Feb 2026 01:58:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=5.75.144.95 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772243928; cv=none; b=TfoO0hbnz1oVdwksNAJuAm7C5V7W0Wg8wTlcWZq5PgiZuYqTGGUBhLlPamUcTYMoIo9Dcy8YTd008osLbEM343+YEPOPKkxJr04SmjMWCq+UtCJlQ76DERy2dqBof5s/t4qNU9tUPEYQCC2JZMg6+AWaBPgfNgHIyeSw3aSiIWo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772243928; c=relaxed/simple; bh=olaD23Sefgx6tGYrUb9P//ipgXQIRB5J04BUq5A0Hn8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=JjLpy7iSqOqh0vQlQ8hHAHznoObyuo4DJtsAjfgLBc3w+PTgIQEd4B9Unqq2GxvwxyAdRz/vvgHj5aMWB7LgqF8OiTlgnRLgXc5GZBfpUYrdESyzkaXye5IP1NshUWSO7iTuuqI2YrsINUW2mw1K2pqmAJAJ2dzN5NW9M73MJwI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mainlining.org; spf=pass smtp.mailfrom=mainlining.org; dkim=pass (2048-bit key) header.d=mainlining.org header.i=@mainlining.org header.b=iQzrHABc; dkim=permerror (0-bit key) header.d=mainlining.org header.i=@mainlining.org header.b=SCpKiI42; arc=none smtp.client-ip=5.75.144.95 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mainlining.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mainlining.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=mainlining.org header.i=@mainlining.org header.b="iQzrHABc"; dkim=permerror (0-bit key) header.d=mainlining.org header.i=@mainlining.org header.b="SCpKiI42" DKIM-Signature: v=1; a=rsa-sha256; s=202507r; d=mainlining.org; c=relaxed/relaxed; h=To:Message-Id:Subject:Date:From; t=1772243805; bh=atS+EGDrzYXC//pgDUJsVAi ruvdFGwR4JZXzUpCAnt4=; b=iQzrHABcuhWTYjKAYaHjE21DUIRDXMCFkqkqC8JvpEItWrU6hT lvzMWaP1RXUJ8o6w0cdYrAy1ybSWlDZ8HwQXsgo26WyU3fyRSUP+QtPggfcdaXIZMuSTKlibR5L SaKcpD6bFGTjbtM+AxNHL0RL8XYSNp+fEXtkD6L9H5kQC1U7VpUtXtZxIuPjV3eNxXW+mpmXSpx tjbC/HKt0ohxs+nhTJDJP0rF64ADiQGxuOXoIaD1vipjaKKxIo4Ruz0wwaxbRiyEH7ZieI38AdM my1V6H8t/u5ULQMqu7uCXaieTTpb1TLHYY9nkyXC01duQTiPQr/yfSRQO1gud3l0ERA==; DKIM-Signature: v=1; a=ed25519-sha256; s=202507e; d=mainlining.org; c=relaxed/relaxed; h=To:Message-Id:Subject:Date:From; t=1772243805; bh=atS+EGDrzYXC//pgDUJsVAi ruvdFGwR4JZXzUpCAnt4=; b=SCpKiI42uU1Qeiws5u9Wvz9YdeENP5h11rFWrp9LeCqkYmGUuV YePXKnStCPP14VfbV89V3rG8GEhGpVkpokDg==; From: Aelin Reidel Date: Sat, 28 Feb 2026 02:56:11 +0100 Subject: [PATCH v2 2/3] Input: add support for Goodix GTX8 Touchscreen ICs Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260228-gtx8-v2-2-3a408c365f6c@mainlining.org> References: <20260228-gtx8-v2-0-3a408c365f6c@mainlining.org> In-Reply-To: <20260228-gtx8-v2-0-3a408c365f6c@mainlining.org> To: Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Hans de Goede , Neil Armstrong , Henrik Rydberg Cc: linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux@mainlining.org, phone-devel@vger.kernel.org, ~postmarketos/upstreaming@lists.sr.ht, Aelin Reidel , Piyush Raj Chouhan , Alexander Koskovich X-Mailer: b4 0.14.2 Add initial support for the Goodix GTX8 touchscreen ICs. These ICs support SPI and I2C interfaces, up to 10 finger touch, stylus and gesture events. This driver is derived from the Goodix gtx8_driver_linux available at [1] and only supports the GT9886 and GT9896 ICs present in the Xiaomi Mi 9T and Xiaomi Redmi Note 10 Pro smartphones. The current implementation only supports Normandy and Yellowstone type ICs, aka only GT9886 and GT9896. It is also limited to I2C only, since I don't have a device with GTX8 over SPI at hand. Adding support for SPI should be fairly easy in the future, since the code uses a regmap. Support for advanced features like: - Firmware updates - Stylus events - Gesture events - Nanjing IC support is not included in current version. The current support requires a previously flashed firmware to be present. As I did not have access to datasheets for these ICs, I extracted the addresses from a couple of config files using a small tool [2]. The addresses are identical for the same IC families in all configs I observed, however not all of them make sense and I stubbed out firmware request support due to this. [1] https://github.com/goodix/gtx8_driver_linux [2] https://github.com/sm7150-mainline/goodix-cfg-bin Tested-by: Piyush Raj Chouhan Tested-by: Alexander Koskovich Signed-off-by: Aelin Reidel Acked-by: Hans de Goede --- drivers/input/touchscreen/Kconfig | 15 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/goodix_gtx8.c | 563 ++++++++++++++++++++++++++++= ++++ drivers/input/touchscreen/goodix_gtx8.h | 141 ++++++++ 4 files changed, 720 insertions(+) diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/= Kconfig index 7d5b72ee07fa1313da39a625b5129a0459720865..099ccd3679383dcf037bc7c6e6a= 3dbf0741722b4 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -429,6 +429,21 @@ config TOUCHSCREEN_GOODIX_BERLIN_SPI To compile this driver as a module, choose M here: the module will be called goodix_berlin_spi. =20 +config TOUCHSCREEN_GOODIX_GTX8 + tristate "Goodix GTX8 touchscreen" + depends on I2C + select REGMAP_I2C + help + Say Y here if you have a Goodix GTX8 IC connected to + your system via I2C. This driver supports Normandy and + Yellowstone ICs like the GT9886 and GT9896. + They are commonly found in mobile phones. + + if unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called goodix_gtx8. + config TOUCHSCREEN_HIDEEP tristate "HiDeep Touch IC" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen= /Makefile index ab9abd151078831a4b22d6998e00ef74fe01c356..9bcb8f01ea785dcbe2a22bd3293= 601dd4259ba1d 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_TOUCHSCREEN_GOODIX) +=3D goodix_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_CORE) +=3D goodix_berlin_core.o obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_I2C) +=3D goodix_berlin_i2c.o obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_SPI) +=3D goodix_berlin_spi.o +obj-$(CONFIG_TOUCHSCREEN_GOODIX_GTX8) +=3D goodix_gtx8.o obj-$(CONFIG_TOUCHSCREEN_HIDEEP) +=3D hideep.o obj-$(CONFIG_TOUCHSCREEN_HIMAX_HX852X) +=3D himax_hx852x.o obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX) +=3D hynitron_cstxxx.o diff --git a/drivers/input/touchscreen/goodix_gtx8.c b/drivers/input/touchs= creen/goodix_gtx8.c new file mode 100644 index 0000000000000000000000000000000000000000..3fbebfa8b75672866788adf89d8= 3be16e32abf81 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8.c @@ -0,0 +1,563 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Goodix GTX8 Touchscreens + * + * Copyright (c) 2019 - 2020 Goodix, Inc. + * Copyright (C) 2023 Linaro Ltd. + * Copyright (c) 2025 Aelin Reidel + * + * Based on gtx8_driver_linux vendor driver and goodix_berlin kernel drive= r. + * + * The driver currently relies on the pre-flashed firmware and only suppor= ts + * Normandy / Yellowstone ICs. + * Pen support is also missing. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "goodix_gtx8.h" + +static const struct regmap_config goodix_gtx8_regmap_conf =3D { + .reg_bits =3D 16, + .val_bits =3D 8, + .max_raw_read =3D I2C_MAX_TRANSFER_SIZE, + .max_raw_write =3D I2C_MAX_TRANSFER_SIZE, +}; + +/* vendor & product left unassigned here, should probably be updated from = fw info */ +static const struct input_id goodix_gtx8_input_id =3D { + .bustype =3D BUS_I2C, +}; + +static bool goodix_gtx8_checksum_valid_normandy(const u8 *data, int size) +{ + u8 cal_checksum =3D 0; + int i; + + if (size < GOODIX_GTX8_CHECKSUM_SIZE) + return false; + + for (i =3D 0; i < size; i++) + cal_checksum +=3D data[i]; + + return cal_checksum =3D=3D 0; +} + +static bool goodix_gtx8_checksum_valid_yellowstone(const u8 *data, int siz= e) +{ + u16 cal_checksum =3D 0; + u16 r_checksum; + int i; + + if (size < GOODIX_GTX8_CHECKSUM_SIZE) + return false; + + for (i =3D 0; i < size - GOODIX_GTX8_CHECKSUM_SIZE; i++) + cal_checksum +=3D data[i]; + + r_checksum =3D get_unaligned_be16(&data[i]); + + return cal_checksum =3D=3D r_checksum; +} + +static int goodix_gtx8_get_remaining_contacts(struct goodix_gtx8_core *cd, + int n) +{ + size_t offset =3D cd->ic_data->header_size + GOODIX_GTX8_TOUCH_SIZE + + GOODIX_GTX8_CHECKSUM_SIZE; + u32 addr =3D cd->ic_data->touch_data_addr + offset; + int error; + + error =3D regmap_raw_read(cd->regmap, addr, &cd->event_buffer[offset], + (n - 1) * GOODIX_GTX8_TOUCH_SIZE); + if (error) { + dev_err_ratelimited(cd->dev, "failed to get touch data, %d\n", + error); + return error; + } + + return 0; +} + +static void goodix_gtx8_report_state(struct goodix_gtx8_core *cd, u8 touch= _num, + union goodix_gtx8_touch *touch_data) +{ + union goodix_gtx8_touch *t; + int i; + u8 finger_id; + + for (i =3D 0; i < touch_num; i++) { + t =3D &touch_data[i]; + + if (cd->ic_data->ic_type =3D=3D IC_TYPE_NORMANDY) { + input_mt_slot(cd->input_dev, t->normandy.finger_id); + input_mt_report_slot_state(cd->input_dev, + MT_TOOL_FINGER, true); + + touchscreen_report_pos(cd->input_dev, &cd->props, + __le16_to_cpu(t->normandy.x), + __le16_to_cpu(t->normandy.y), + true); + input_report_abs(cd->input_dev, ABS_MT_TOUCH_MAJOR, + t->normandy.w); + } else { + finger_id =3D FIELD_GET( + GOODIX_GTX8_FINGER_ID_MASK_YELLOWSTONE, + t->yellowstone.finger_id); + input_mt_slot(cd->input_dev, finger_id); + input_mt_report_slot_state(cd->input_dev, + MT_TOOL_FINGER, true); + + touchscreen_report_pos(cd->input_dev, &cd->props, + __be16_to_cpu(t->yellowstone.x), + __be16_to_cpu(t->yellowstone.y), + true); + input_report_abs(cd->input_dev, ABS_MT_TOUCH_MAJOR, + t->yellowstone.w); + } + } + + input_mt_sync_frame(cd->input_dev); + input_sync(cd->input_dev); +} + +static void goodix_gtx8_touch_handler(struct goodix_gtx8_core *cd, u8 touc= h_num, + union goodix_gtx8_touch *touch_data) +{ + int error; + + touch_num =3D FIELD_GET(GOODIX_GTX8_TOUCH_COUNT_MASK, touch_num); + + if (touch_num > GOODIX_GTX8_MAX_TOUCH) { + dev_warn(cd->dev, "invalid touch num %d\n", touch_num); + return; + } + + if (touch_num > 1) { + /* read additional contact data if more than 1 touch event */ + error =3D goodix_gtx8_get_remaining_contacts(cd, touch_num); + if (error) + return; + } + + if (touch_num) { + /* + * Normandy checksum is for the entire read buffer, + * Yellowstone is only for the touch data (since header + * has a separate checksum) + */ + if (cd->ic_data->ic_type =3D=3D IC_TYPE_NORMANDY) { + int len =3D GOODIX_GTX8_HEADER_SIZE_NORMANDY + + touch_num * GOODIX_GTX8_TOUCH_SIZE + + GOODIX_GTX8_CHECKSUM_SIZE; + if (!goodix_gtx8_checksum_valid_normandy( + cd->event_buffer, len)) { + dev_err(cd->dev, + "touch data checksum error: %*ph\n", + len, cd->event_buffer); + return; + } + } else { + int len =3D touch_num * GOODIX_GTX8_TOUCH_SIZE + + GOODIX_GTX8_CHECKSUM_SIZE; + if (!goodix_gtx8_checksum_valid_yellowstone( + (u8 *)touch_data, len)) { + dev_err(cd->dev, + "touch data checksum error: %*ph\n", + len, (u8 *)touch_data); + return; + } + } + } + + goodix_gtx8_report_state(cd, touch_num, touch_data); +} + +static irqreturn_t goodix_gtx8_irq(int irq, void *data) +{ + struct goodix_gtx8_core *cd =3D data; + struct goodix_gtx8_event_normandy *ev_normandy; + struct goodix_gtx8_event_yellowstone *ev_yellowstone; + union goodix_gtx8_touch *touch_data; + int error; + u8 status, touch_num; + + error =3D regmap_raw_read( + cd->regmap, cd->ic_data->touch_data_addr, cd->event_buffer, + cd->ic_data->header_size + GOODIX_GTX8_TOUCH_SIZE + + GOODIX_GTX8_CHECKSUM_SIZE); + if (error) { + dev_warn_ratelimited( + cd->dev, "failed to get event head data: %d\n", error); + goto out; + } + + /* + * Both IC types have the same data in the header, just at different + * offsets + */ + if (cd->ic_data->ic_type =3D=3D IC_TYPE_NORMANDY) { + ev_normandy =3D + (struct goodix_gtx8_event_normandy *)cd->event_buffer; + status =3D ev_normandy->hdr.status; + touch_num =3D ev_normandy->hdr.touch_num; + touch_data =3D (union goodix_gtx8_touch *)ev_normandy->data; + } else { + ev_yellowstone =3D (struct goodix_gtx8_event_yellowstone *) + cd->event_buffer; + status =3D ev_yellowstone->hdr.status; + touch_num =3D ev_yellowstone->hdr.touch_num; + touch_data =3D (union goodix_gtx8_touch *)ev_yellowstone->data; + } + + if (status =3D=3D 0) + goto out; + + /* Yellowstone ICs have a checksum for the header */ + if (cd->ic_data->ic_type =3D=3D IC_TYPE_YELLOWSTONE && + !goodix_gtx8_checksum_valid_yellowstone( + cd->event_buffer, GOODIX_GTX8_HEADER_SIZE_YELLOWSTONE)) { + dev_warn_ratelimited(cd->dev, + "touch head checksum error: %*ph\n", + (int)GOODIX_GTX8_HEADER_SIZE_YELLOWSTONE, + cd->event_buffer); + goto out_clear; + } + + if (status & GOODIX_GTX8_TOUCH_EVENT) + goodix_gtx8_touch_handler(cd, touch_num, touch_data); + + if (status & GOODIX_GTX8_REQUEST_EVENT) { + /* + * All configs seen so far either set the firmware request + * address to 0 (Normandy) or have it equal the touch data + * address (Yellowstone). Neither seems correct, and this + * is not testable. Therefore it is currently omitted. + */ + dev_dbg(cd->dev, "received request event, ignoring\n"); + } + +out_clear: + /* Clear up status field */ + regmap_write(cd->regmap, cd->ic_data->touch_data_addr, 0); + +out: + return IRQ_HANDLED; +} + +static int goodix_gtx8_input_dev_config(struct goodix_gtx8_core *cd) +{ + struct input_dev *input_dev; + int error; + + input_dev =3D devm_input_allocate_device(cd->dev); + if (!input_dev) + return -ENOMEM; + + cd->input_dev =3D input_dev; + input_set_drvdata(input_dev, cd); + + input_dev->name =3D "Goodix GTX8 Capacitive TouchScreen"; + input_dev->phys =3D "input/ts"; + + input_dev->id =3D goodix_gtx8_input_id; + + input_set_abs_params(cd->input_dev, ABS_MT_POSITION_X, 0, SZ_64K - 1, 0, + 0); + input_set_abs_params(cd->input_dev, ABS_MT_POSITION_Y, 0, SZ_64K - 1, 0, + 0); + input_set_abs_params(cd->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + + touchscreen_parse_properties(cd->input_dev, true, &cd->props); + + error =3D input_mt_init_slots(cd->input_dev, GOODIX_GTX8_MAX_TOUCH, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (error) + return error; + + error =3D input_register_device(cd->input_dev); + if (error) + return error; + + return 0; +} + +static int goodix_gtx8_read_version(struct goodix_gtx8_core *cd) +{ + int error; + + /* + * The vendor driver reads a whole lot more data to calculate and + * verify a checksum. Without documentation, we don't know what + * most of that data is, so we only read the parts we know about + * and instead ensure their values are as expected + */ + error =3D regmap_raw_read(cd->regmap, cd->ic_data->fw_version_addr, + &cd->fw_version, sizeof(cd->fw_version)); + if (error) { + dev_err(cd->dev, "error reading fw version, %d\n", error); + return error; + } + + /* + * Since we don't verify the checksum, do a basic check that the + * product ID meets expectations + */ + if (memcmp(cd->fw_version.product_id, cd->ic_data->product_id, + sizeof(cd->fw_version.product_id))) { + dev_err(cd->dev, "unexpected product ID, got: %c%c%c%c\n", + cd->fw_version.product_id[0], + cd->fw_version.product_id[1], + cd->fw_version.product_id[2], + cd->fw_version.product_id[3]); + return -EINVAL; + } + + return 0; +} + +static int goodix_gtx8_dev_confirm(struct goodix_gtx8_core *cd) +{ + u8 rx_buf[1]; + int retry =3D 3; + int error; + + while (retry--) { + /* + * test_addr appears to always be the touch_data_addr for + * Normandy, but it doesn't really matter since all we + * need is a valid address + */ + error =3D regmap_raw_read(cd->regmap, + cd->ic_data->touch_data_addr, rx_buf, + sizeof(rx_buf)); + + if (!error) + return 0; + + usleep_range(5000, 5100); + } + + dev_err(cd->dev, "device confirm failed\n"); + + return -EINVAL; +} + +static int goodix_gtx8_power_on(struct goodix_gtx8_core *cd) +{ + int error; + + error =3D regulator_enable(cd->vddio); + if (error) { + dev_err(cd->dev, "Failed to enable VDDIO: %d\n", error); + return error; + } + + error =3D regulator_enable(cd->avdd); + if (error) { + dev_err(cd->dev, "Failed to enable AVDD: %d\n", error); + goto err_vddio_disable; + } + + /* Vendors usually configure the power on delay as 300ms */ + msleep(GOODIX_GTX8_POWER_ON_DELAY_MS); + + gpiod_set_value_cansleep(cd->reset_gpio, 0); + + /* Vendor waits 5ms for firmware to initialize */ + usleep_range(5000, 5100); + + error =3D goodix_gtx8_dev_confirm(cd); + if (error) + goto err_dev_reset; + + /* Vendor waits 100ms for firmware to fully boot */ + msleep(GOODIX_GTX8_NORMAL_RESET_DELAY_MS); + + return 0; + +err_dev_reset: + gpiod_set_value_cansleep(cd->reset_gpio, 1); + regulator_disable(cd->avdd); +err_vddio_disable: + regulator_disable(cd->vddio); + return error; +} + +static void goodix_gtx8_power_off(struct goodix_gtx8_core *cd) +{ + gpiod_set_value_cansleep(cd->reset_gpio, 1); + regulator_disable(cd->avdd); + regulator_disable(cd->vddio); +} + +static int goodix_gtx8_suspend(struct device *dev) +{ + struct goodix_gtx8_core *cd =3D dev_get_drvdata(dev); + + disable_irq(cd->irq); + goodix_gtx8_power_off(cd); + + return 0; +} + +static int goodix_gtx8_resume(struct device *dev) +{ + struct goodix_gtx8_core *cd =3D dev_get_drvdata(dev); + int error; + + error =3D goodix_gtx8_power_on(cd); + if (error) + return error; + + enable_irq(cd->irq); + + return 0; +} + +EXPORT_GPL_SIMPLE_DEV_PM_OPS(goodix_gtx8_pm_ops, goodix_gtx8_suspend, + goodix_gtx8_resume); + +static void goodix_gtx8_power_off_act(void *data) +{ + struct goodix_gtx8_core *cd =3D data; + + goodix_gtx8_power_off(cd); +} + +static int goodix_gtx8_probe(struct i2c_client *client) +{ + struct goodix_gtx8_core *cd; + struct regmap *regmap; + int error; + + cd =3D devm_kzalloc(&client->dev, sizeof(*cd), GFP_KERNEL); + if (!cd) + return -ENOMEM; + + regmap =3D devm_regmap_init_i2c(client, &goodix_gtx8_regmap_conf); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + cd->dev =3D &client->dev; + cd->irq =3D client->irq; + cd->regmap =3D regmap; + cd->ic_data =3D i2c_get_match_data(client); + + cd->event_buffer =3D + devm_kzalloc(cd->dev, cd->ic_data->event_size, GFP_KERNEL); + if (!cd->event_buffer) + return -ENOMEM; + + cd->reset_gpio =3D + devm_gpiod_get_optional(cd->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(cd->reset_gpio)) + return dev_err_probe(cd->dev, PTR_ERR(cd->reset_gpio), + "Failed to request reset GPIO\n"); + + cd->avdd =3D devm_regulator_get(cd->dev, "avdd"); + if (IS_ERR(cd->avdd)) + return dev_err_probe(cd->dev, PTR_ERR(cd->avdd), + "Failed to request AVDD regulator\n"); + + cd->vddio =3D devm_regulator_get(cd->dev, "vddio"); + if (IS_ERR(cd->vddio)) + return dev_err_probe(cd->dev, PTR_ERR(cd->vddio), + "Failed to request VDDIO regulator\n"); + + error =3D goodix_gtx8_power_on(cd); + if (error) { + dev_err(cd->dev, "failed power on"); + return error; + } + + error =3D devm_add_action_or_reset(cd->dev, goodix_gtx8_power_off_act, + cd); + if (error) + return error; + + error =3D goodix_gtx8_read_version(cd); + if (error) { + dev_err(cd->dev, "failed to get version info"); + return error; + } + + error =3D goodix_gtx8_input_dev_config(cd); + if (error) { + dev_err(cd->dev, "failed to set input device"); + return error; + } + + error =3D devm_request_threaded_irq(cd->dev, cd->irq, NULL, + goodix_gtx8_irq, IRQF_ONESHOT, + "goodix-gtx8", cd); + if (error) { + dev_err(cd->dev, "request threaded IRQ failed: %d\n", error); + return error; + } + + dev_set_drvdata(cd->dev, cd); + + dev_dbg(cd->dev, + "Goodix GT%c%c%c%c Touchscreen Controller, Version %d.%d.%d.%d\n", + cd->fw_version.product_id[0], cd->fw_version.product_id[1], + cd->fw_version.product_id[2], cd->fw_version.product_id[3], + cd->fw_version.fw_version[0], cd->fw_version.fw_version[1], + cd->fw_version.fw_version[2], cd->fw_version.fw_version[3]); + + return 0; +} + +static const struct goodix_gtx8_ic_data gt9886_data =3D { + .event_size =3D GOODIX_GTX8_EVENT_SIZE_NORMANDY, + .fw_version_addr =3D GOODIX_GTX8_FW_VERSION_ADDR_NORMANDY, + .header_size =3D GOODIX_GTX8_HEADER_SIZE_NORMANDY, + .ic_type =3D IC_TYPE_NORMANDY, + .product_id =3D { '9', '8', '8', '6' }, + .touch_data_addr =3D GOODIX_GTX8_TOUCH_DATA_ADDR_NORMANDY, +}; + +static const struct goodix_gtx8_ic_data gt9896_data =3D { + .event_size =3D GOODIX_GTX8_EVENT_SIZE_YELLOWSTONE, + .fw_version_addr =3D GOODIX_GTX8_FW_VERSION_ADDR_YELLOWSTONE, + .header_size =3D GOODIX_GTX8_HEADER_SIZE_YELLOWSTONE, + .ic_type =3D IC_TYPE_YELLOWSTONE, + .product_id =3D { '9', '8', '9', '6' }, + .touch_data_addr =3D GOODIX_GTX8_TOUCH_DATA_ADDR_YELLOWSTONE, +}; + +static const struct i2c_device_id goodix_gtx8_i2c_id[] =3D { + { .name =3D "gt9886", .driver_data =3D (long)>9886_data }, + { .name =3D "gt9896", .driver_data =3D (long)>9896_data }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, goodix_gtx8_i2c_id); + +static const struct of_device_id goodix_gtx8_of_match[] =3D { + { .compatible =3D "goodix,gt9886", .data =3D >9886_data }, + { .compatible =3D "goodix,gt9896", .data =3D >9896_data }, + {}, +}; +MODULE_DEVICE_TABLE(of, goodix_gtx8_of_match); + +static struct i2c_driver goodix_gtx8_driver =3D { + .probe =3D goodix_gtx8_probe, + .id_table =3D goodix_gtx8_i2c_id, + .driver =3D { + .name =3D "goodix-gtx8", + .of_match_table =3D of_match_ptr(goodix_gtx8_of_match), + .pm =3D pm_sleep_ptr(&goodix_gtx8_pm_ops), + }, +}; +module_i2c_driver(goodix_gtx8_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Goodix GTX8 Touchscreen driver"); +MODULE_AUTHOR("Aelin Reidel "); diff --git a/drivers/input/touchscreen/goodix_gtx8.h b/drivers/input/touchs= creen/goodix_gtx8.h new file mode 100644 index 0000000000000000000000000000000000000000..9b13cfce38720b35bb11f0d7f56= d671b31664ade --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8.h @@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __GOODIX_GTX8_H__ +#define __GOODIX_GTX8_H__ + +#include + +#define GOODIX_GTX8_NORMAL_RESET_DELAY_MS 100 +#define GOODIX_GTX8_POWER_ON_DELAY_MS 300 + +#define GOODIX_GTX8_TOUCH_EVENT BIT(7) +#define GOODIX_GTX8_REQUEST_EVENT BIT(6) +#define GOODIX_GTX8_TOUCH_COUNT_MASK GENMASK(3, 0) +#define GOODIX_GTX8_FINGER_ID_MASK_YELLOWSTONE GENMASK(7, 4) + +#define GOODIX_GTX8_MAX_TOUCH 10 +#define GOODIX_GTX8_CHECKSUM_SIZE sizeof(u16) + +#define GOODIX_GTX8_FW_VERSION_ADDR_NORMANDY 0x4535 +#define GOODIX_GTX8_FW_VERSION_ADDR_YELLOWSTONE 0x4022 +#define GOODIX_GTX8_TOUCH_DATA_ADDR_NORMANDY 0x4100 +#define GOODIX_GTX8_TOUCH_DATA_ADDR_YELLOWSTONE 0x4180 + +#define I2C_MAX_TRANSFER_SIZE 256 + +enum goodix_gtx8_ic_type { + IC_TYPE_NORMANDY, + IC_TYPE_YELLOWSTONE, +}; + +struct goodix_gtx8_ic_data { + size_t event_size; + /* + * This is technically not the firmware version address + * referenced in the vendor driver, but rather the + * address of the product ID part. The meaning of the + * other parts is unknown and they are therefore omitted + * for now. + */ + int fw_version_addr; + size_t header_size; + enum goodix_gtx8_ic_type ic_type; + char product_id[4]; + int touch_data_addr; +}; + +struct goodix_gtx8_header_normandy { + u8 status; + /* Only the lower 4 bits are actually used */ + u8 touch_num; +}; +#define GOODIX_GTX8_HEADER_SIZE_NORMANDY \ + sizeof(struct goodix_gtx8_header_normandy) + +struct goodix_gtx8_header_yellowstone { + u8 status; + /* Most likely unused */ + u8 __unknown1; + /* Only the lower 4 bits are actually used */ + u8 touch_num; + /* Most likely unused */ + u8 __unknown2[3]; + __le16 checksum; +} __packed __aligned(1); +#define GOODIX_GTX8_HEADER_SIZE_YELLOWSTONE \ + sizeof(struct goodix_gtx8_header_yellowstone) + +struct goodix_gtx8_touch_normandy { + u8 finger_id; + __le16 x; + __le16 y; + u8 w; + u8 __unknown[2]; +} __packed __aligned(1); + +struct goodix_gtx8_touch_yellowstone { + /* + * Only the upper 4 bits are used, lower 4 bits are + * probably the sensor ID. + */ + u8 finger_id; + u8 __unknown1; + __be16 x; + __be16 y; + /* + * Vendor driver claims that this is a single __be16, + * but testing shows that it likely isn't. + */ + u8 __unknown2; + u8 w; +} __packed __aligned(1); + +union goodix_gtx8_touch { + struct goodix_gtx8_touch_normandy normandy; + struct goodix_gtx8_touch_yellowstone yellowstone; +}; +#define GOODIX_GTX8_TOUCH_SIZE sizeof(union goodix_gtx8_touch) + +struct goodix_gtx8_event_normandy { + struct goodix_gtx8_header_normandy hdr; + /* The data below is u16 aligned */ + u8 data[GOODIX_GTX8_TOUCH_SIZE * GOODIX_GTX8_MAX_TOUCH + + GOODIX_GTX8_CHECKSUM_SIZE]; +}; +#define GOODIX_GTX8_EVENT_SIZE_NORMANDY \ + sizeof(struct goodix_gtx8_event_normandy) + +struct goodix_gtx8_event_yellowstone { + struct goodix_gtx8_header_yellowstone hdr; + /* The data below is u16 aligned */ + u8 data[GOODIX_GTX8_TOUCH_SIZE * GOODIX_GTX8_MAX_TOUCH + + GOODIX_GTX8_CHECKSUM_SIZE]; +}; +#define GOODIX_GTX8_EVENT_SIZE_YELLOWSTONE \ + sizeof(struct goodix_gtx8_event_yellowstone) + +struct goodix_gtx8_fw_version { + /* 4 digits IC number */ + char product_id[4]; + /* Most likely unused */ + u8 __unknown[4]; + /* Four components version number */ + u8 fw_version[4]; +}; + +struct goodix_gtx8_core { + struct device *dev; + struct regmap *regmap; + struct regulator *avdd; + struct regulator *vddio; + struct gpio_desc *reset_gpio; + struct touchscreen_properties props; + struct goodix_gtx8_fw_version fw_version; + struct input_dev *input_dev; + int irq; + const struct goodix_gtx8_ic_data *ic_data; + u8 *event_buffer; +}; + +extern const struct dev_pm_ops goodix_gtx8_pm_ops; + +#endif --=20 2.53.0 From nobody Fri Apr 3 05:04:03 2026 Received: from mail.mainlining.org (mail.mainlining.org [5.75.144.95]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CB66317A2E8; Sat, 28 Feb 2026 01:57:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=5.75.144.95 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772243861; cv=none; b=CsfsA5N5Ay2vNETaP79Zt/1e2Of9SloYG89LTL8OePQGe2T9rK6n48qtM4PExCjSM3NVQuSemTkEbKyKdZBkoYu8T9/nIF2ixOVNiMqgPvAmBKzDazoJk6R47vJZeqj5og6YDbavalZsCF9RhBKPKUIalR8wH+r40DMAbazyMfU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772243861; c=relaxed/simple; bh=mqgh3KinL8lR1abG10D3/1AkSqQqDN7cUVnCfUjIrMU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Ys8bljMDSCdzmspvuhoAAMP8WjAliUGC/5h+59DXQRoKgQFmy3TlMHdwpxy6P4RVLMvPKx/1Kn4LBr9f3HzKSrOF+y+OFmuO39FeLgtavBbSA/SVkrUlN4bsWfAfwWyUXL4HnKNToO0CvxPgQCm2CrYJlGT484aZRdUZjoj3UrI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mainlining.org; spf=pass smtp.mailfrom=mainlining.org; dkim=pass (2048-bit key) header.d=mainlining.org header.i=@mainlining.org header.b=H7t7r3lP; dkim=permerror (0-bit key) header.d=mainlining.org header.i=@mainlining.org header.b=RzD43SKF; arc=none smtp.client-ip=5.75.144.95 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mainlining.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mainlining.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=mainlining.org header.i=@mainlining.org header.b="H7t7r3lP"; dkim=permerror (0-bit key) header.d=mainlining.org header.i=@mainlining.org header.b="RzD43SKF" DKIM-Signature: v=1; a=rsa-sha256; s=202507r; d=mainlining.org; c=relaxed/relaxed; h=To:Message-Id:Subject:Date:From; t=1772243806; bh=AUlBsE2lv1qFAMOJ2b89Ub+ ErBhxWmCUaBMB+doDrJo=; b=H7t7r3lPPRBDb6qSRAX1bxmoYFzI4/ZWtASkui5DWEt8Vsf9jn EPpLTmD7ykAV5lyvELt5Z9o4q2iaTNK6b4pcwpaCc0KRSfbyFvh8zw+of8+4ZGXoxYzJBfCZexM v9DjQNpsCg79ddfELi3+t6sgWygVC1e1aKEr9zb3N7f/r+ZqTV3KCwrW4quTPntz8jv/AnpBvQ6 ueJJmw5g6hnGMzTMi2lveVEgESnDrBkXskjICdq9z+19xZR8ycVzqKKqVqIL5xM7Jye1Ms9/LXv MFeIDtybT1/GGLff6An7SrKqZI2qE/+6Dmv3TwQ6MTSZ1ZJiH9s/Lc+lFxc0Oom1IIQ==; DKIM-Signature: v=1; a=ed25519-sha256; s=202507e; d=mainlining.org; c=relaxed/relaxed; h=To:Message-Id:Subject:Date:From; t=1772243806; bh=AUlBsE2lv1qFAMOJ2b89Ub+ ErBhxWmCUaBMB+doDrJo=; b=RzD43SKFn7EO0XqcIKpQfsWJhKMs2OGOkwbs/p0MFgaFM+wG0l 6ryVaWk4sY1hBvYmFWjTMo0Gj05cFLXrGSDA==; From: Aelin Reidel Date: Sat, 28 Feb 2026 02:56:12 +0100 Subject: [PATCH v2 3/3] MAINTAINERS: add an entry for Goodix GTX8 Touchscreen driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260228-gtx8-v2-3-3a408c365f6c@mainlining.org> References: <20260228-gtx8-v2-0-3a408c365f6c@mainlining.org> In-Reply-To: <20260228-gtx8-v2-0-3a408c365f6c@mainlining.org> To: Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Hans de Goede , Neil Armstrong , Henrik Rydberg Cc: linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux@mainlining.org, phone-devel@vger.kernel.org, ~postmarketos/upstreaming@lists.sr.ht, Aelin Reidel X-Mailer: b4 0.14.2 Add MAINTAINERS entry for the Goodix GTX8 Touchscreen IC driver. Signed-off-by: Aelin Reidel Acked-by: Hans de Goede --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 14899f1de77ed2e8a583cf7b0fea25725c8534cb..c76f9fbe51f929f7eded37760cb= 5c83dfa337d0b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10849,6 +10849,13 @@ M: Maud Spierings S: Maintained F: Documentation/devicetree/bindings/connector/gocontroll,moduline-module-= slot.yaml =20 +GOODIX GTX8 TOUCHSCREEN +M: Aelin Reidel +L: linux-input@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/input/touchscreen/goodix,gt9886.yaml +F: drivers/input/touchscreen/goodix_gtx8* + GOODIX TOUCHSCREEN M: Hans de Goede L: linux-input@vger.kernel.org --=20 2.53.0