src.nth.io/

summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorLuke Hoersten <[email protected]>2026-06-14 19:43:13 -0500
committerLuke Hoersten <[email protected]>2026-06-14 19:43:13 -0500
commit5bed2f0ccc9ca3bab80e1d2ed2a9dddc621428c6 (patch)
treeaca53e3af41283bf1263f2b818a49b0aea0dc29d /main
parentea54684e304a17b981df9a282572de13fb965a60 (diff)
touch: retry + hard-reset when FT5426 boots into wedged 0xff state
After a long bench session of taps + power cycles, the FT5426 on this unit started returning dev_mode=0xff on every read. The polling loop interpreted that as 15 simultaneous touches forever (td_status bits 0..3 all set), wedged `was_down=true` permanently, and no tap or long-press ever fired — the screen looked dead. Two layers of defence in touch_init: 1. retry the dev_mode read up to 10 × 30 ms in case the chip is just slow to come up after PC_RST_TP_N is released. 2. if still stuck, pulse PC_RST_TP_N via a new display_touch_reset_pulse() helper (drop reset for 20 ms then release + 50 ms settle), then retry the read another 10 × 30 ms. Plus a polling-loop sanity check: an FT5x06 supports max 5 simultaneous points, so a TD_STATUS lower-nibble value > 5 is bogus and the poll cycle is skipped. That keeps a wedged chip from producing phantom "15 touches" reports if it slips into 0xff during runtime. If both attempts still report 0xff, we log loudly and let the polling task run — it harmlessly drops cycles via the >5 sanity check. Wake still works via POST /state from Scrypted; this only disables touch gestures on a hardware-stuck boot.
Diffstat (limited to 'main')
-rw-r--r--main/display.c16
-rw-r--r--main/display.h5
-rw-r--r--main/touch.c29
3 files changed, 48 insertions, 2 deletions
diff --git a/main/display.c b/main/display.c
index a704891..8c5ffb2 100644
--- a/main/display.c
+++ b/main/display.c
@@ -218,6 +218,22 @@ static esp_err_t touch_release_reset(void)
PC_LED_EN | PC_RST_TP_N | PC_RST_LCD_N | PC_RST_BRIDGE_N);
}
+// Hard-reset the touch IC: drop PC_RST_TP_N for ~20 ms, then release.
+// touch_init() calls this if its dev_mode read keeps returning 0xff —
+// usually unwedges a stuck FT5426 without needing a power cycle.
+esp_err_t display_touch_reset_pulse(void)
+{
+ if (!s_panel_mcu) return ESP_ERR_INVALID_STATE;
+ // Hold touch in reset (clear PC_RST_TP_N), keep everything else as-is.
+ esp_err_t err = mcu_write_u8(REG_PORTC,
+ PC_LED_EN | PC_RST_LCD_N | PC_RST_BRIDGE_N);
+ if (err != ESP_OK) return err;
+ vTaskDelay(pdMS_TO_TICKS(20));
+ err = touch_release_reset();
+ vTaskDelay(pdMS_TO_TICKS(50));
+ return err;
+}
+
// ============================================================================
// TC358762 bridge configuration (DSI Generic Long Write packets)
// Sequence mirrors Linux drivers/gpu/drm/bridge/tc358762.c tc358762_init().
diff --git a/main/display.h b/main/display.h
index 9092efb..cb2b913 100644
--- a/main/display.h
+++ b/main/display.h
@@ -27,6 +27,11 @@ esp_err_t display_set_brightness(uint8_t brightness_0_100);
esp_err_t display_sleep(void);
esp_err_t display_wake(void);
+// Pulse the touch IC's reset line (PC_RST_TP_N) via the panel MCU. Used
+// by touch_init() when the FT5426 boots into a wedged state and refuses
+// to leave dev_mode=0xff.
+esp_err_t display_touch_reset_pulse(void);
+
// Blit an RGB565 source image to the panel, applying the current
// orientation. Source dimensions must match the effective resolution:
// portrait -> src is 480x800 (rotated 90° CW into the 800x480 panel)
diff --git a/main/touch.c b/main/touch.c
index 7cde4bf..6f91665 100644
--- a/main/touch.c
+++ b/main/touch.c
@@ -67,6 +67,10 @@ static void touch_task(void *arg)
if (ft_read(FT_REG_DEV_MODE, buf, sizeof(buf)) != ESP_OK) continue;
uint8_t touches = buf[FT_REG_TD_STATUS] & 0x0F;
+ // FT5x06 supports max 5 simultaneous points. A reading above that
+ // is bogus (typically the bus is parking 0xff because the IC isn't
+ // responding) — drop the cycle to avoid faking a phantom press.
+ if (touches > 5) continue;
uint64_t now = (uint64_t)esp_timer_get_time();
if (touches > 0) {
@@ -112,12 +116,33 @@ esp_err_t touch_init(void)
ESP_RETURN_ON_ERROR(i2c_master_bus_add_device(bus, &cfg, &s_dev),
TAG, "i2c add FT5426");
- uint8_t dev_mode = 0;
- esp_err_t err = ft_read(FT_REG_DEV_MODE, &dev_mode, 1);
+ // FT5426 sometimes returns 0xff on the first read after its reset is
+ // released (especially on cold boot before its internal firmware has
+ // finished booting). 0xff causes the polling loop to misread 15
+ // touches forever. Two-stage recovery: short wait + retry, then a
+ // hard reset pulse via the panel MCU if that still doesn't help.
+ uint8_t dev_mode = 0xff;
+ esp_err_t err = ESP_FAIL;
+ for (int attempt = 0; attempt < 2; ++attempt) {
+ for (int i = 0; i < 10; ++i) {
+ err = ft_read(FT_REG_DEV_MODE, &dev_mode, 1);
+ if (err == ESP_OK && dev_mode != 0xff) break;
+ vTaskDelay(pdMS_TO_TICKS(30));
+ }
+ if (err == ESP_OK && dev_mode != 0xff) break;
+ if (attempt == 0) {
+ ESP_LOGW(TAG, "FT5426 stuck at 0xff — pulsing PC_RST_TP_N");
+ display_touch_reset_pulse();
+ }
+ }
if (err != ESP_OK) {
ESP_LOGE(TAG, "FT5426 @0x38 unreachable (%s)", esp_err_to_name(err));
return err;
}
+ if (dev_mode == 0xff) {
+ ESP_LOGW(TAG, "FT5426 still stuck after hard reset — touch will "
+ "not register; tap/long-press disabled this boot");
+ }
ESP_LOGI(TAG, "FT5426 ack'd (dev_mode=0x%02x); "
"tap=toggle wake/sleep, %dms hold=info overlay",
dev_mode, LONG_PRESS_MS);