Raspberry Pi Touch Display 2 (5-inch) on BeagleY-AI: black screen after successful panel init
Hardware: BeagleY-AI (TI AM67A / J722S), kernel 6.1.83-ti-arm64-r74
Display: Raspberry Pi Touch Display 2, 5-inch (ILI9881C MIPI DSI panel, 720x1280, 2 data lanes, connected via CSI1 FPC connector)
Touch: Goodix GT911 at 0x5d on i2c-3, no interrupt pin (using polling)
What works
The display pipeline comes up successfully:
-
Ported
rpi-panel-v2-regulatordriver: the MCU at 0x45 on i2c-3 is bound and functional, backlight and panel power sequencing work -
Ported ILI9881C panel driver with the
raspberrypi,dsi-5inchentry: all 185 DSI LP init commands succeed -
DCS read of
GET_POWER_MODE(0x0A) returns0x9c: display on, sleep out, booster on -
DRM pipeline: crtc-1 active, DSI-1 connector linked, plane with framebuffer assigned, mode 720x1280@60Hz
-
Framebuffer at 32-bit DMA address (
cma=128M@0xb0000000, dma_addr=0xb0200000) -
MIPI switch GPIOs correct: OE=LOW (switch enabled), SEL=LOW (DSI selected)
-
HDMI output works fine
-
Touch works (Goodix polling mode at ~60 Hz)
What does not work
Screen is lit (backlight on) but completely black. No image visible. Writing test patterns to /dev/fb0 produces no visible change.
What we tried and ruled out
All of these made no difference:
-
Fixing mode validation (drm_mode_set_crtcinfo)
-
Fixing pixel clock (htotal=937, clock=83393 kHz)
-
Adding MEDIA_BUS_FMT_RGB888_1X24 in get_modes
-
prepare_upstream_first = true
-
PANEL_ON_IN_PREPARE flag (DISPLAY_ON sent in LP mode before HS video)
-
MIPI_DSI_MODE_VIDEO_BURST mode flags (cdns-dsi does not support burst mode, returns -ENOTSUPP)
-
MIPI_DSI_CLOCK_NON_CONTINUOUS
-
CMA placement at 32-bit address range
-
Removing SET_TEAR_ON from prepare()
What we think the root cause is
We dug into the cdns-dsi driver (drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c) and read the hardware registers at 0x30500000 via /dev/mem. Here is what we found:
PHY_CTL register (0x08) = 0x00000011
This means only DATA_LANE_EN(1) is set (physical lane 0). DATA_LANE_EN(2) (physical lane 1) is not set. The LANE_STS register confirms: lane 0 is in WRITE state (transmitting), lane 1 is stuck in START state (never transitions).
The source code in cdns_dsi_init_link() has:
for (i = 1; i < output->dev->lanes; i++)
val |= DATA_LANE_EN(i);
The DATA_LANE_EN macro uses 1-based indexing: BIT((x) - 1). So for a 2-lane panel, the loop runs only for i=1 (enabling physical lane 0), and misses i=2 (physical lane 1). Changing < to <= would fix the loop.
However, when we disassembled the compiled kernel binary, the ARM64 code actually uses b.cs (branch if carry set, meaning branch if lanes >= i), which already produces i=1,2 for lanes=2. This suggests the lanes value loaded at runtime from phy_opts.mipi_dphy.lanes might be 1, not 2, despite the panel descriptor having .lanes = 2.
MAIN_STS register (0x24) = 0x0000000F: PLL locked, clock lane ready, both data lanes report ready. DPHY_ERR = 0 (no errors). The DSI link appears healthy at the physical level, but only one data lane is actually transmitting.
We also tried rebuilding the kernel from the same git tag (beagleboard/linux at 6.1.83-ti-arm64-r74) with the same config (from /proc/config.gz) and the lane fix applied. The rebuilt kernels consistently have broken DSI interrupts (0 counts on IRQ 366 at /proc/interrupts) while the original kernel has 191 interrupts. All DCS commands time out with -110. We could not determine why: the source is identical, the config is identical, and the toolchain is the same (gcc 14.2.0 on the board itself). This prevented us from testing the lane fix.
Questions for the community
-
Why might
phy_opts.mipi_dphy.lanesbe 1 at runtime when the panel descriptor sets.lanes = 2? Is there a DT property or DSI host quirk that overrides the lane count? -
Has anyone successfully used a 2-lane MIPI DSI panel with the cdns-dsi driver on AM67A/J721E? If so, what was needed?
-
Is the off-by-one in the DATA_LANE_EN loop a known issue? The same loop pattern exists in the upstream Cadence DSI driver.
-
Are there known issues with rebuilding the BeagleBoard kernel from the git tag that would cause interrupts to break? We tried both cross-compilation (aarch64-linux-gnu-gcc 13.3.0) and native compilation on the board (gcc 14.2.0), same result.
Full kernel logs, device tree overlay, and register dumps available if helpful.