LILYGO T-Display-K230 · hardware note · observed
The GT9895 bound, probed, and created /dev/input/event0 while reporting nothing at all. Every status check passed. The fault was one word in the device tree: the interrupt was declared edge‑triggered, and a Goodix Berlin part holds its interrupt line low until it is acknowledged.
the measurement
Everything that is easy to check was passing. The driver bound at
i2c 1-005d, goodix_berlin probed,
/dev/input/event0 appeared, and the part answered
32‑bit‑addressed i2c reads. None of that is the same as a
controller that is scanning the glass, which is the whole lesson of this
board and the reason this site exists.
interrupts = <23 …> | what changed | /proc/interrupts |
|---|---|---|
IRQ_TYPE_EDGE_FALLING (0x02) | nothing else — same kernel, same image | 0 |
IRQ_TYPE_LEVEL_LOW (0x08) | the device tree word, and only that | 2173 over one 45 s capture |
87: 2173 gpio-k230 23 Level goodix-berlin
That line is read back off the running board, so the setting under test is the setting that was live — not the one the source says should have been pushed. The gap between the two is not hypothetical here: the clock patch described in the panel note was built, shipped and never looked at, and it had blanked the screen.
the cause
Our touch node was cloned from the Canaan reference board's, which describes an Edtech FT5306 — a different controller that happens to sit on the same pins. The comment in our device tree said as much. The trigger type came along for the ride and was never derived from any Goodix source.
touchscreen@38 {
compatible = "edt,edt-ft5306"; /* not what is on this board */
reg = <0x38>;
reset-gpios = <&gpio0_ports 24 GPIO_ACTIVE_LOW>;
interrupt-parent = <&gpio0_ports>;
interrupts = <23 IRQ_TYPE_EDGE_FALLING>; /* inherited */
}; what it reports
Event: time ..., type 3 (EV_ABS), code 57 (ABS_MT_TRACKING_ID), value 27 Event: time ..., type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 456 Event: time ..., type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 888 Event: time ..., type 3 (EV_ABS), code 48 (ABS_MT_TOUCH_MAJOR), value 3 Event: time ..., type 1 (EV_KEY), code 330 (BTN_TOUCH), value 1 Event: time ..., type 3 (EV_ABS), code 0 (ABS_X), value 456 Event: time ..., type 3 (EV_ABS), code 1 (ABS_Y), value 888 Event: time ..., -------------- SYN_REPORT ------------
Tracking IDs are allocated and retired, BTN_TOUCH goes 1 and
0, and ABS_MT_TOUCH_MAJOR ramps as the contact patch grows.
A slot‑0 trajectory decoded from a 60 s capture, with
ABS_MT_SLOT honoured, spans X 86..728 and Y 765..1765
continuously: both axes move, and the motion is smooth rather than
quantised to a handful of values. That is the difference between a device
node and a digitizer.
| axis | min | max | panel |
|---|---|---|---|
ABS_X, ABS_MT_POSITION_X | 0 | 1023 | 568 pixels wide |
ABS_Y, ABS_MT_POSITION_Y | 0 | 2399 | 1232 pixels tall |
The two grids do not match, and the driver cannot make them
match. goodix_berlin reports through
touchscreen_report_pos(), which applies the swap and invert
properties and does not scale;
touchscreen-size-x / touchscreen-size-y only
declare the advertised maximum, and the controller's raw values pass
through unchanged. So the input device speaks the digitizer's native
1024×2400 and anything consuming it scales by 568/1024 and
1232/2400. That is the ordinary Linux arrangement, and it is worth
knowing before something lands a touch a factor of two away from the
finger.
Declaring those two properties was itself a fix. Without them the driver
advertises 0 .. SZ_64K - 1 on both axes for a sensor that
reports 0..1023 and 0..2399 — a client scaling by the advertised
maximum would have collapsed every touch into the top-left corner.
what is not known
the cost of the pin
The pinned Xuantie kernel is 6.6 and carries
drivers/input/touchscreen/goodix.c, which serves the older
GT9xx generation. goodix_berlin landed in v6.9, and even at
v6.18 the i2c driver matches only goodix,gt9916 while the
binding enumerates gt9897 and gt9916. Mainline
has never supported the GT9895 at any version.
So the driver is carried here as an explicit backport under
nix/patches/goodix-berlin/, and the Berlin programming model
turned out to cover this part unchanged — the register layout was
checked byte‑for‑byte against LilyGO's own RT‑Smart
gt9895.c, which performs no initialisation the backport is
missing. Nothing in the kernel needed fixing. The cost of the pin is one
backport to carry forward, recorded so that it is visible the next time
the kernel revision moves.
Evidence: docs/evidence/touch-reports.md,
docs/evidence/gt9895-port.md,
docs/evidence/touch-probe.txt,
and docs/evidence/gt9895-touch.md.
Source read for this note: the backported
goodix_berlin_core.c under nix/patches/,
drivers/gpio/gpio-k230.c and
arch/riscv/boot/dts/canaan/k230-canmv-v3-lcd.dts in the
pinned tree, and LilyGO's RT‑Smart gt9895.c. The
vendor trees are not committed here, so those paths are named and not
linked.