src.nth.io/

summaryrefslogtreecommitdiff
path: root/main/display.c
AgeCommit message (Collapse)AuthorFilesLines
2026-06-14M3: panel renders — TC358762 bridge init + non-burst DSILuke Hoersten1-134/+221
The Hosyond 5" panel uses Pi 7" v1.1 architecture (ATTINY ID 0xC3 + TC358762 DSI-to-DPI bridge). Getting it to paint needed three things that ESP-IDF doesn't expose: 1. TC358762 bridge configuration — 16 register writes via DSI Generic Long Write (DT=0x29) packets, transcribed from Linux's tc358762.c. 2. Non-burst video mode — IDF hardcodes BURST_WITH_SYNC_PULSES, but TC358762 requires NON_BURST_WITH_SYNC_PULSES. Overridden via the mipi_dsi_host_ll_* API after esp_lcd_new_panel_dpi() and before panel_init(). 3. ATTINY v1.1 power-on sequence + SPI-proxy bridge wake. The legacy REG_POWERON(0x85) path doesn't apply to 0xC3 firmware; instead write PORTC/PORTA/PORTB/PORTC in order, then later release bridge reset and proxy-write TC358762 SYSPMCTRL=0 through the ATTINY's ADDR_H/L + WR_DATA_H/L registers. Reaching the LL/HAL APIs requires shadowing esp_lcd_dsi_bus_t so we can pick the mipi_dsi_hal_context_t out of the private struct. Documented the layout dependency at the shadow definition. Tuned config (observed stable on ESP32-P4 per embenix's reference): - 1 data lane @ 600 Mbps - DPI 26 MHz (Linux modeline is 25.98) - timings HSW=2 HBP=46 HFP=210 / VSW=20 VBP=4 VFP=22 - RGB888 end-to-end, R,G,B byte order (BGR was wrong) - disable_lp=0 so LP windows are available for the bridge writes
2026-06-14M3 WIP: DSI bring-up + state machine + identity overlayLuke Hoersten1-46/+152
DSI / panel: - LDO_VO3 acquired at 2500 mV (VDD_MIPI_DPHY) — without this the PHY PLL busy-waits forever inside esp_lcd_new_dsi_bus. - PSRAM bumped to 200 MHz (CONFIG_IDF_EXPERIMENTAL_FEATURES) to keep the DPI fed without underruns. - Pi-7"-style 800x480 panel init (REG_POWERON, PORTA/PORTB/PWM/PORTC) + 16-bit RGB565 framebuffer with portrait-mode rotation buffer in PSRAM. Currently shows garbled output — known issue, next commit switches to the TC358762-bridge-aware init sequence. State + UX: - viewport_state simplified to AWAKE/ASLEEP only; "unconfigured" is a flag, not a state. Tap always toggles; content choice (identity vs frame) is driven by the configured flag. - BOOT button arms a 15 s identity overlay via local_screens_overlay; expired callback returns to prior state. - Boot-done indicator: two backlight flashes (no usable LED GPIO on the Waveshare board — GPIO 35 BOOT is shared with EMAC TXD1). - Best-effort subsystem init in app_main; display init deferred to its own task so a panel hang can't block networking. Display test pattern: solid R/G/B for 2 s each on boot to characterize the garble independent of text rendering.
2026-06-13M7: touch + outbound /state POST to ScryptedLuke Hoersten1-0/+2
state_client.{h,c}: - Worker task drains a depth-1 queue (xQueueOverwrite gives the replace-on-full semantics from the spec — in-flight POST is never cancelled; the next queued entry is overwritten by newer state). - esp_http_client POST to <scrypted>/state with Content-Type application/ json, User-Agent ScryptedViewport/<version>, Connection: close, 1s timeout. Body: {"viewport":"<name>","state":"wake"|"sleep"}. - Any non-2xx or transport error increments state_post_failures and is otherwise ignored. Silently drops if no Scrypted URL is configured. touch.{h,c}: - FT5426 capacitive touch on the shared I2C bus at 0x38. - 30ms polling task; tracks down/up transitions, detects taps as down-then-up within 500ms with a 150ms debounce. - On tap, toggles wake/sleep via state_machine_set_local(), which drives the local transition AND fires state_client_post() at Scrypted. state_machine adds state_machine_set_local(): runs the same transition as state_machine_set() then enqueues an outbound POST. Idle-timer expiry now uses this path so Scrypted sees idle-driven sleeps too (the spec's "tighten the race with /frame 409" path stays in place as the fallback). display.h exposes display_i2c_bus(); touch.c uses it instead of re-initializing the same I2C port. app_main starts state_client right after the state machine and starts touch after display init (touch is skipped if display isn't up since they share the bus). CMakeLists.txt: add esp_http_client to REQUIRES. Build clean against ESP-IDF 5.4 (binary ~860 KB; jumped ~210 KB from M6 because esp_http_client + cJSON path pulls in tcp_transport, mbedtls, http_parser). TESTING.md M7: flask-based test receiver, tap dispatch verification, failure-path check (kill receiver, confirm counter increments), queue-coalescing behavior, no-POST-on-Scrypted-initiated, no-POST- when-unconfigured.
2026-06-13M5: POST /frame — hardware JPEG decode + orientation-aware paintLuke Hoersten1-0/+38
jpeg_decoder.{h,c} wraps esp_driver_jpeg (ESP32-P4 hardware decoder). - One-time engine + DMA-aligned PSRAM scratch buffer setup (1 MB input, 768 KB output @ panel native 800x480 RGB565). - try_lock + unlock so concurrent /frame POSTs get 503 instead of queueing, per spec. - jpeg_decoder_get_info() reports the dimensions; the http handler validates them against the effective resolution before painting. display.h adds display_present_rgb565(src, w, h): - Landscape: src is 800x480, memcpy 1:1 into the panel framebuffer. - Portrait: src is 480x800, software rotate 90° CW into the 800x480 panel framebuffer. (PPA / 2D-DMA hardware rotation is a later optimization if portrait latency matters.) http_api.c adds POST /frame: - Content-Type must be image/jpeg → else 400. - Empty body → 400. > 1 MB → 413. Display not initialized → 500. - jpeg_decoder_try_lock(0) for concurrency: second post returns 503. - Body streamed into the decoder's input buffer in chunks. - Decode failure or dimension mismatch → 400 + decode_errors++. - Paint failure → 500. - Success → frames_received++, last_frame_us = esp_timer_get_time(), 204 No Content. app_main initializes the JPEG decoder after display_init(). Both are best-effort: failures log a warning and leave the rest of the firmware running. CMakeLists.txt: add esp_driver_jpeg to REQUIRES. Known gap (M6 closes it): /frame currently paints regardless of wake/sleep state. The 409-when-asleep rule lands with POST /state in M6. Build clean against ESP-IDF 5.4 (binary ~640 KB). TESTING.md M5 expanded with portrait/landscape test commands, ImageMagick test-image recipes, the full negative matrix (wrong Content-Type, oversize, wrong dims, concurrent, garbage), and the M6 dependency note.
2026-06-13M3: display driver (Pi-style DSI panel: TC358762 + ATTINY MCU)Luke Hoersten1-0/+275
display.{h,c} brings up the Hosyond 5\" 800x480 panel via the Raspberry Pi 7\" touchscreen architecture: - I2C init to the panel-side MCU at 0x45 (Pi 7\" register map ported from drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c). - POWERON write + 120ms settle. - ESP32-P4 MIPI-DSI bring-up in DPI video mode: 2-lane, 480 Mbps, 800x480 @ 60Hz, RGB565, canonical Pi 7\" timings (HSW=18/HBP=20/HFP=62, VSW=4/VBP=27/VFP=18, 30 MHz pixel clock). - Gamma-corrected (^2.2) PWM brightness via REG_PWM (0x86). - display_sleep / display_wake hooks for M6. - display_fill() and display_test_pattern() (8 vertical color bars) for M3 acceptance and reuse by M8 local screens. app_main calls display_init() as best-effort — if the panel isn't attached or wiring is wrong, the rest of the firmware (Ethernet, mDNS, /state) keeps running. On success it paints the test pattern. Pin assignments (PIN_I2C_SDA=7, PIN_I2C_SCL=8) match Waveshare's bundled-panel BSP convention but are flagged TODO until the user's specific FPC adapter wiring is confirmed. main/CMakeLists.txt: add driver, esp_driver_i2c, esp_lcd to REQUIRES. Build verified clean against ESP-IDF 5.4 for target esp32p4. M3 acceptance (color bars on screen) cannot be verified without a flashed board + connected panel.