ESP32-P4: DSIBus (MIPI-DSI video mode, PPA rotation) and two builder fixes - #16
bitcoin3us wants to merge 3 commits into
Conversation
|
Wow, huge work again! Same question here, whether https://github.com/MicroPythonOS/lvgl_micropython/blob/integration/CONTRIBUTING.md was followed. I'm myself unsure how we can best work on our lvgl_micropython branch while still allowing to resync with upstream if it gets updated... so suggestions are always welcome! |
|
Good question, and partly no: #13 to #16 target My suggestion for the split:
Happy to convert #15 to a patch too if you'd rather have all |
esp32_src/dsi_bus.c existed but was neither compiled nor registered, and did not build against ESP-IDF 5.5 (esp_lcd_new_panel_io_dsi is esp_lcd_new_panel_io_dbi there, plus a few typos). This wires it in, guarded by SOC_MIPI_DSI_SUPPORTED, and reworks it around how the IDF DPI panel actually works: - The DPI panel driver allocates screen-sized frame buffers itself and its DMA scans them out continuously. allocate_framebuffer() therefore hands out real, correctly sized buffers that init() repoints at the panel's frame buffers (so len() is right for the display framework and LVGL renders straight into the scanned-out memory); partial buffers are refused with a clear error. - esp_lcd_panel_init() (the start of the video stream) is deferred to the first tx_color, so the display driver can configure the panel over DCS (tx_param/rx_param through the DBI panel IO) first, the order Espressif's own MIPI panel drivers use. - A flush is a cache write-back plus, when the flushed buffer is not the one being scanned out, a swap at the next frame boundary; the refresh-done interrupt reports completion (flush_ready) once the panel has picked the buffer up. Intermediate areas of a multi-area update complete immediately. LVGL's inclusive area coordinates are converted for esp_lcd_panel_draw_bitmap(). - The DSI PHY's internal LDO (ESP32-P4: channel 3, 2500 mV) can be acquired by the bus (phy_ldo_channel / phy_ldo_voltage_mv kwargs), dpi_clock_freq (MHz) is separate from the lane bit rate, and del/free paths no longer leak or double-declare. Verified on the Waveshare ESP32-P4-WIFI6-Touch-LCD-4.3 (ST7701 480x800, 2 lanes at 500 Mbps, DPI 30 MHz, RGB565, two PSRAM frame buffers): DCS reads answer (RDDPM 0x9C, RDDMADCTL 0x00, RDDCOLMOD 0x50), LVGL renders into the panel buffers and full-screen refreshes complete in one frame period (14-17 ms) with no DMA underruns at 200 MHz PSRAM. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Panels on the DSI video interface cannot swap rows and columns, so a landscape UI on a portrait panel needs the picture rotated on the way to the frame buffer. DSIBus(rotation=90|180|270) does that with the ESP32-P4's PPA (pixel processing accelerator): LVGL renders the rotated (logical) picture into the bus's own screen-sized buffers, and on the last flush of an update the whole picture is rotated by the PPA into the panel's back buffer, which is then swapped in at the next frame boundary (the refresh-done interrupt reports completion as before). The panel side is always double-buffered in this mode; intermediate areas of a multi-area update complete immediately. The angle follows LVGL's convention, so pass the same rotation to lv_display. Frame buffers are now allocated cache-line aligned (the PPA and the DPI DMA both need it), the PPA driver's include path is added where the chip has one, and rotation is refused on chips without a PPA. Verified on the Waveshare ESP32-P4-WIFI6-Touch-LCD-4.3 (480x800 panel, rotation=90): LVGL runs at 800x480, a forced full-screen refresh takes ~29 ms (render + PPA rotation + vsync) against ~15 ms unrotated. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… alignment Two build fixes as .patch files applied by MicroPythonOS's scripts/build_mpos.sh (see CONTRIBUTING.md), rather than edits to builder/ in this branch. esp32_user_c_modules_abspath.patch (apply in this repo's root): builder/esp32.py passed USER_C_MODULES=../../../../../ext_mod/micropython.cmake, which CMake resolves against lib/micropython/ports/esp32's real location. When lib/ is symlinked to another checkout (git worktrees sharing one MicroPython tree and build directory) that silently compiles the OTHER checkout's ext_mod. The patch passes the absolute path instead. esp32_riscv_frozen_native_align.patch (apply in lib/micropython): frozen @micropython.native / viper functions crashed with an illegal instruction on their first call on the ESP32-P4. tools/mpy-tool.py emits their machine code in a code section with aligned(2) for RISC-V, but the RISC-V assembler never pads a 2-byte alignment inside a code section, so every array following an odd-sized one starts at an odd address; jalr clears the low bit and the CPU decodes garbage. A 4-byte alignment is padded, but only when the file is assembled without linker relaxation (with relaxation the padding is R_RISCV_ALIGN NOPs, which fail on odd sizes: "3 bytes required for alignment ... can't relax section"). So the patch makes mpy-tool.py use aligned(4) for RV32/RV64, as it already does for Xtensa, and compiles frozen_content.c with -mno-relax on RISC-V targets. Xtensa builds are unchanged. Both were previously done by builder/esp32.py code in this PR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1c3d09e to
145be62
Compare
|
Awesome! Agreed for the suggestion on the split, although rather than just committing on integration, wouldn't it make sense to have each tweak done on a feat/ branch and then merged into integration? Or is that overkill? Does that mean you have to create 2 pull requests - one for the new feat/ branch and one for integration? |
|
Yes, that's exactly the flow, sorry for the ambiguous wording. By "direct commits" I meant normal commits rather than |
What
Display support for the ESP32-P4's MIPI-DSI interface, brought up on the Waveshare ESP32-P4-WIFI6-Touch-LCD-4.3 (ST7701, 480x800, 2 lanes) for MicroPythonOS. Three commits, on top of current
integration:lcd_bus: add DSIBus—esp32_src/dsi_bus.cexisted but was neither compiled nor registered and did not build against ESP-IDF 5.5. Now wired in (guarded bySOC_MIPI_DSI_SUPPORTED) and reworked around how the IDF DPI panel works:allocate_framebuffer()hands out real, correctly sized buffers whichinit()repoints at the panel's frame buffers: LVGL renders straight into what is scanned out (partial buffers are refused with a clear error);tx_color, so the display driver configures the panel over DCS first (tx_param/rx_paramthrough the DBI panel IO), the order Espressif's own MIPI panel drivers use;flush_ready. Intermediate areas of a multi-area update complete immediately;bus_id, data_lanes, freq(lane Mbps),dpi_clock_freq(MHz),virtual_channel, the h/v sync porches and pulse widths,phy_ldo_channel/phy_ldo_voltage_mv(the P4's internal LDO that powers the DSI PHY, channel 3 on the Espressif and Waveshare boards),rotation.lcd_bus: DSIBus rotation through the PPA— video-interface panels cannot swap rows and columns, soDSIBus(rotation=90|180|270)lets LVGL render the rotated picture into the bus's own buffers and rotates every finished update into the panel's back buffer with the P4's pixel processing accelerator (tear-free, panel side always double-buffered). Pass the same rotation to the LVGL display; LVGL rotates touch coordinates itself.scripts/build_mpos.sh(companion PR: build: apply lvgl_micropython's USER_C_MODULES and RISC-V native patches MicroPythonOS#312):esp32_user_c_modules_abspath.patch(applied in this repo's root):builder/esp32.pypassesUSER_C_MODULESas an absolute path. The relative../../../../../ext_mod/micropython.cmakeis resolved againstlib/micropython's real location, so a build from a git worktree withlib/symlinked silently compiled the other checkout'sext_mod.esp32_riscv_frozen_native_align.patch(applied inlib/micropython): frozen@micropython.native/viper functions crashed with an illegal instruction on the P4.mpy-tool.pyemits their machine code withaligned(2), but the RISC-V assembler never pads a 2-byte alignment inside a code section, so arrays after an odd-sized one start at an odd address. The patch usesaligned(4)for RV32/RV64 (as for Xtensa) and compilesfrozen_content.cwith-mno-relaxon RISC-V (both are needed: with relaxation on, 4-byte padding fails to link on odd sizes). This affects every RISC-V ESP32 target (C3/C6/P4) with frozen native code; Xtensa builds are unchanged.Verified on the board
Patch form verified with a P4 build from pristine files: both patches applied forward, the worktree's own
ext_modwas compiled via the absolute path, 0 frozen native-code arrays at an odd address, and WAV playback (frozen native helpers) runs on the board without a crash.DCS reads answer (RDDPM 0x9C, RDDMADCTL 0x00, RDDCOLMOD 0x50), LVGL renders into the panel buffers, forced full-screen refreshes complete in one 60 Hz frame (14-17 ms) unrotated and ~29 ms with PPA rotation (render + rotation + vsync), no DMA underruns with PSRAM at 200 MHz.
All 4213 frozen
fun_dataarrays 4-byte aligned after the alignment fix (5 were odd before); WAV playback through the frozen native helpers runs to completion.GT911 touch and the ES8311 speaker work through the same image (board file in the MicroPythonOS PR).
The MicroPythonOS side (esp32p4 build target, board detection, ST7701-over-DSI driver, board file) is a separate PR that depends on this one; the plan is in MicroPythonOS/MicroPythonOS#307. Happy to offer the DSI bus upstream once it has settled here.
🤖 Generated with Claude Code
With thanks to the scientists and engineers who did the hard, unglamorous work that got us here.