# RM69A10 AMOLED display bring-up on Debian riscv64 - T-Display-K230 Status: working. The RM69A10 AMOLED (568x1232 portrait, 2-lane MIPI-DSI, mainline `canaan-drm` fork) renders opaque content from a clean boot with no manual register pokes. The fbcon login prompt shows on-panel, `/dev/fb0` is 16bpp, and the VO vsync IRQ climbs steadily with zero `flip_done`/`vblank` timeouts. ## The fix (the entire diff) Two hunks, both in `drivers/gpu/drm/canaan/canaan_drv.c` (see `patches/kernel.patch`). `canaan_vo.c`, `canaan_dsi.c`, `canaan_plane.h` are stock. 1. Power domain, in `canaan_drm_platform_probe` after `pm_runtime_enable`: ```c pm_runtime_get_sync(disp_dev); /* held for the driver's lifetime; put never called */ ``` This holds the display power domain. The in-kernel fbdev boot modeset never opens `/dev/dri/card0`, so without this ref nothing holds runtime PM and the genpd governor suspends `disp_domain` mid-scanout; the VO/DSI block then powers off and its registers read `0xffffffff`. (`pd_ignore_unused` blocks only boot-time gating, not runtime-PM suspend.) 2. fbdev color depth, in `canaan_drm_bind`: ```c drm_fbdev_generic_setup(drm_dev, 16); /* was 32 */ ``` ## The alpha constraint (durable hardware fact + MUI corollary) The K230 OSD has no 32-bit-opaque pixel format: 32bpp is always ARGB8888 with per-pixel alpha, and the mixer computes `per_pixel_alpha x global_alpha` (no per-pixel-alpha bypass; the vendor MPP API `k_vo_video_osd_attr` exposes only a `global_alptha` byte). An fbdev/XRGB client clears its buffer to alpha=0, so the whole primary plane composites transparent and only the VO background reaches the glass (`VO_DISP_BACKGROUND` = `0xffffff` renders pink through `YUV2RGB`). RGB565 (16bpp) has no alpha channel, so it is always opaque; RGB888 is opaque too. 32bpp is the only trap. The constraint is fbdev-specific: a native DRM/KMS client presenting AR24 buffers with authored `0xFF` alpha works fine at 32bpp. Only the fbdev path, which cannot author alpha, must use an alpha-less format. Corollary for MUI: meshtasticd's device-ui goes through LVGL's fbdev path, so `LV_COLOR_DEPTH=16` (RGB565) matches the 16bpp `/dev/fb0`. `DITH=1` is already enabled in the VO pipeline, so RGB565 banding on the AMOLED is a non-issue. ## Hardware / debug reference - Live register access: with `disp_domain` held on (fix 1) and STRICT_DEVMEM off, VO MMIO `0x9084_xxxx` is readable/writable from userspace `/dev/mem`. A peek/poke/strobe tool lives at `/tmp/vo` (tmpfs, rebuild after every reboot). - Shadow registers: the VO is vsync-latched. Any live config poke must be followed by a LOAD_CTL strobe `writel(0x11, 0x90840004)` to latch staged config into the active set. Readback returns the staged (write) shadow. - Background is YUV-mapped: `VO_DISP_BACKGROUND` (`0x908403d0`) passes through `YUV2RGB`; `0xffffff` -> pink, `0x008080` -> green. Cosmetic once fbcon/LVGL draws opaque over it; poke it live rather than baking a value (kept out of the landed diff). - DRM plane 31 = video layer (NV12/NV21/NV16/NV61); plane 33 = primary OSD (XR24/AR24/RG16/...). Both DMA-fetch and composite correctly. - OSD register geography: OSD blocks are 0x30 apart (OSD3=0x850, OSD4=0x880, OSD5=0x8b0); per-OSD layout INFO+0 / SIZE+4 / VLU0+8 / DMA_CTRL+0x18 / STRIDE+0x1c / ADDRSEL+0x20. `dma_map(3)` = `K_VO_OSD_MAP_1234_TO_4321` (a byte-order swap, not a fetch gate). ADDRSEL reads back 0x1100 = 0x100 written + sticky reset bit 0x1000. Vendor MPP exposes only OSD0-3. - DSI / PHY: rm69a10 connector `{n=4, m=97, voc=0x19, hs_freq=0x96}` (the fork's 4th arg to `k230_dsi_config_4lan_phy` is the stock `0x96`, not the voc). PLL m-2=97, n-1=4 -> 475.2 Mbps/lane. PHY lock polls `PHY_STATUS(0xb0)==0x1fbd`. `kd_vo_enable` = `writel(0x11, VO+0x04)`. Mixer: `GLBEN(0x3c0)`, `MIXSEL(0x3cc)=0x3210`, `m950(0x950)=0xba987654`, `YUV2RGB(0x380)=1`, `DITH(0x3d4)=1`, `RGB2YUV(0x340)=0x11111111`. ## DTS wiring (unchanged and correct) `canaan/k230-tdisplay.dts` includes `display-rm69a10-568x1232.dtsi`: - DIS_EN (IO35 = gpio1 l3) master power = always-on fixed regulator `reg_disp_3v3`. - LCD_EN (IO25 = gpio0 l25) hogged high. - LCD_RST (IO22 = gpio0 l22) `dsi_reset-gpios` ACTIVE_HIGH (the `canaan,universal` driver pulses reset logical 1->0->1, correctly driving the RM69A10 active-low NRESET). All three pins are SEL0=GPIO in U-Boot; no bootloader change. Result: DSI binds (`canaan-drm ... bound 90850000.dsi`), panel attaches (`canaan,universal`), `/dev/dri/card0` + connector `card0-DSI-1` at mode 568x1232. ## UI orientation - landscape (device-ui layer, not driver) The panel scans out natively portrait 568x1232 (DTS/dtsi timing stays portrait). The product is a T-Pager-style two-piece held landscape (screen unit + magnet-attached QWERTY keyboard on the long edge), so MUI renders landscape 1232x568 via LVGL rotation. Orientation is a device-ui decision made after scanout works, not a driver concern: - device-ui / meshtasticd: `Display: { Rotate: 90 }` (or 270) + a landscape geometry. device-ui has multiple geometry presets (not just `VIEW_320x240`, e.g. the Seeed indicator runs 480x480 at 1x1), so start from an existing preset adaptable to 1232x568. - Touch axes must be rotated to match (GT9895 report is portrait-native; see TOUCH-BRINGUP.md). ## Build + deploy Kernel builds out-of-tree in WSL Debian: `cd /home/deb/k230-boot && make CROSS_COMPILE=riscv64-linux-gnu- output/ulinux.bin` (compiles the kernel Image, wraps it in an OpenSBI FW_PAYLOAD, gzips, and `mkimage -A riscv -C gzip -O linux -T kernel -a 0 -e 0 -n linux` -> `output/ulinux.bin`). Deploy: `scp output/ulinux.bin k230:/tmp/`, then `ssh k230 bash /tmp/deploy-kernel.sh` (copies to `/boot/vmlinuz-6.6.36`, keeps the prior kernel as `-phaseA.bak`, writes extlinux with a fallback entry l1 = old kernel + no-panel DTB). Reboot (~20s). Confirm: `cat /sys/class/graphics/fb0/bits_per_pixel` = 16. Full build in `CI.md` / `BUILD.md`. ## Upstreamable (dri-devel candidates) Both fixes belong upstream if this driver line heads to mainline; the next person to boot mainline on a K230 board with fbcon hits both: - `pm_runtime_get_sync` in probe (fbdev boot modeset never opens the device, so the display-subsystem genpd suspends out from under scanout). - fbdev default of 16bpp/RGB565 (the OSD has no 32-bit-opaque format; a 32bpp fbcon composites transparent). At minimum document the alpha constraint; ideally default fbdev to 16bpp. ## MUI The color MUI runs in landscape on this display via meshtasticd built from the `native-fb` env - see `BUILD-MESHTASTICD.md` for the build and `TOUCH-BRINGUP.md` for touch. - Color depth is a non-issue: LVGL `LV_COLOR_DEPTH=32` renders fine onto the 16bpp RGB565 fb (LGFX/FBDriver converts). No LVGL rebuild to 16bpp needed. - Enable via config: `Display: {Panel: FB, Width: 1232, Height: 568}` + `Config: DisplayMode: COLOR`. ("FB" is a valid Portduino panel string in the `native-fb` build.) - Landscape = LVGL rotation, config-driven: device-ui `FBDriver` reads env `MESHTASTIC_FB_ROTATION` (0/1/2/3), set by the firmware from `Display.Rotate` + `OffsetRotate`. Right-side-up at 90; `OffsetRotate:3` = 270 (config-only). These firmware + device-ui changes are maintained upstream. - The universal `VIEW_320x240` device-ui layout scales to any resolution (LV_PCT-responsive). The 2.8.0 landscape layout reflow (home/config screens) is the device-ui author's domain.