Display engine: the DRC never receives the backend's pixels #22

Closed
opened 2026-08-27 23:22:27 +00:00 by tiagoagueda · 1 comment
Owner

Half resolved 2026-08-28 — see 44-hdmi.md. Retitled, because the original
framing was wrong in both directions.

The encoder works, and needed no new driver

The A80's HDMI is a Synopsys DesignWare HDMI TX v1.32a with an Allwinner vendor PHY — the
same arrangement as the A83T, not the Allwinner-native controller the A10/A31 use. Read off the
block itself:

byte +000 = 13  DESIGN_ID      +002 = a0  PRODUCT_ID0
byte +001 = 2a  REVISION_ID    +003 = c1  PRODUCT_ID1
byte +006 = fe  CONFIG2_ID  ->  DW_HDMI_PHY_VENDOR_PHY

So mainline's existing allwinner,sun8i-a83t-dw-hdmi and allwinner,sun8i-a83t-hdmi-phy
compatibles drive it unmodified. This was DT only.

Verified with a TV attached: connector reports connected, 256 bytes of EDID read back over
DDC
, 1920x1080@60 set with TMDS and the TCON1 pixel clock both at 148.5 MHz, and disabling
the CRTC makes the TV report "no signal".

What is actually missing: the display engine

The old text called the pipeline complete because every node binds. They do — but nothing had
ever consumed their output
, because this SoC has never had an encoder. The path has never
scanned out, and it does not work.

With modetest holding a mode, everything except one block is correct:

TCON1 GCTL  = 0x80000001   enabled
TCON1 CTL   = 0x800001e0   channel 1 on; HT 2200, VT 1125, HSPW 44, VSPW 5
BE1 MODCTL  = 0x00000103   backend enabled, started, layer 0 enabled
BE1 layer 0 = valid DRAM address, pitch 7680, XRGB8888
DRC1        = 0x00000000   <- all zeros

sun6i_drc.c is 124 lines that deassert a reset and enable two clocks and write no registers
at all
, assuming the block powers up passing data through. On the A80 it does not. From the
vendor BSP (lowlevel_sun9iw1/iep_drc_ebios.{c,h}):

register mainline the A80 needs
gnectl 0x00 never written en bit 0, mod bits 8-9
drcsize 0x04 never written (h-1) << 16 | (w-1)
CSC 0xc0-0xec never written identity matrix from drc_csc_tab[0..11]

Stepping it up on the live pipeline:

DRC state screen
all zeros (mainline) static — no pixels reach TCON1
+ drcsize + gnectl.en, mode 2 solid red, pattern faintly visible
+ identity CSC static
+ BT.601 YUV→RGB CSC solid green (reproducible A/B/A)

The red was explained: the CSC held 0x04a7…, the BT.601 YUV→RGB matrix left by the vendor
bootloader, and we were feeding it RGB.

Where it is stuck

That A/B is the useful measurement. The DRC's output is a constant derived from the CSC offset
terms
— identity has zero offsets and gives noise, YUV601 has non-zero offsets and gives flat
green. It does not depend on the framebuffer at all.

So the DRC runs but never receives the backend's pixels. The BE→DRC interconnect is the open
question, and it is the one thing between here and a picture.

Already ruled out: DE_BE_Output_Select() writes bits 20-22 of the backend MODE_CTL (0x800),
and the vendor calls it as DE_BE_Output_Select(screen_id, screen_id) with 1 and 2 mapping to 0
— so those bits should be zero, and ours are.

Worth looking at next: DE_SYS at 0x03000000 and DISP_SYS at 0x03010000. Mainline maps
only 0x30 bytes of the first, purely as a clock controller (de_clocks), and never touches the
second at all.

Tools for this are in probe/: deprobe.ko dumps BE1/DRC1/TCON1 live, drcfix.ko programs the
DRC with selectable mode and CSC.


⚠️ Nothing has been submitted upstream. See 42-upstreaming.md.

**Half resolved 2026-08-28** — see [44-hdmi.md](44-hdmi.md). Retitled, because the original framing was wrong in both directions. ## The encoder works, and needed no new driver The A80's HDMI is a **Synopsys DesignWare HDMI TX v1.32a with an Allwinner vendor PHY** — the same arrangement as the A83T, not the Allwinner-native controller the A10/A31 use. Read off the block itself: ``` byte +000 = 13 DESIGN_ID +002 = a0 PRODUCT_ID0 byte +001 = 2a REVISION_ID +003 = c1 PRODUCT_ID1 byte +006 = fe CONFIG2_ID -> DW_HDMI_PHY_VENDOR_PHY ``` So mainline's existing `allwinner,sun8i-a83t-dw-hdmi` and `allwinner,sun8i-a83t-hdmi-phy` compatibles drive it **unmodified**. This was DT only. Verified with a TV attached: connector reports `connected`, **256 bytes of EDID read back over DDC**, `1920x1080@60` set with TMDS and the TCON1 pixel clock both at 148.5 MHz, and disabling the CRTC makes the TV report "no signal". ## What is actually missing: the display engine The old text called the pipeline complete because every node binds. They do — but **nothing had ever consumed their output**, because this SoC has never had an encoder. The path has never scanned out, and it does not work. With `modetest` holding a mode, everything except one block is correct: ``` TCON1 GCTL = 0x80000001 enabled TCON1 CTL = 0x800001e0 channel 1 on; HT 2200, VT 1125, HSPW 44, VSPW 5 BE1 MODCTL = 0x00000103 backend enabled, started, layer 0 enabled BE1 layer 0 = valid DRAM address, pitch 7680, XRGB8888 DRC1 = 0x00000000 <- all zeros ``` `sun6i_drc.c` is 124 lines that deassert a reset and enable two clocks and **write no registers at all**, assuming the block powers up passing data through. On the A80 it does not. From the vendor BSP (`lowlevel_sun9iw1/iep_drc_ebios.{c,h}`): | register | mainline | the A80 needs | |---|---|---| | `gnectl` `0x00` | never written | `en` bit 0, `mod` bits 8-9 | | `drcsize` `0x04` | never written | `(h-1) << 16 \| (w-1)` | | CSC `0xc0`-`0xec` | never written | identity matrix from `drc_csc_tab[0..11]` | Stepping it up on the live pipeline: | DRC state | screen | |---|---| | all zeros (mainline) | **static** — no pixels reach TCON1 | | `+ drcsize + gnectl.en`, mode 2 | **solid red**, pattern faintly visible | | `+ identity CSC` | **static** | | `+ BT.601 YUV→RGB CSC` | **solid green** (reproducible A/B/A) | The red was explained: the CSC held `0x04a7…`, the BT.601 YUV→RGB matrix left by the vendor bootloader, and we were feeding it RGB. ## Where it is stuck That A/B is the useful measurement. The DRC's output is a **constant derived from the CSC offset terms** — identity has zero offsets and gives noise, YUV601 has non-zero offsets and gives flat green. It does not depend on the framebuffer at all. **So the DRC runs but never receives the backend's pixels. The BE→DRC interconnect is the open question, and it is the one thing between here and a picture.** Already ruled out: `DE_BE_Output_Select()` writes bits 20-22 of the backend `MODE_CTL` (`0x800`), and the vendor calls it as `DE_BE_Output_Select(screen_id, screen_id)` with 1 and 2 mapping to 0 — so those bits should be zero, and ours are. Worth looking at next: **`DE_SYS` at `0x03000000` and `DISP_SYS` at `0x03010000`**. Mainline maps only `0x30` bytes of the first, purely as a clock controller (`de_clocks`), and never touches the second at all. Tools for this are in `probe/`: `deprobe.ko` dumps BE1/DRC1/TCON1 live, `drcfix.ko` programs the DRC with selectable mode and CSC. --- ⚠️ **Nothing has been submitted upstream.** See [42-upstreaming.md](42-upstreaming.md).
tiagoagueda changed title from HDMI / display output: pipeline exists in mainline, encoder does not to Display engine: the DRC never receives the backend's pixels 2026-08-28 09:27:57 +00:00
Author
Owner

Resolved 2026-08-28. There is a picture.

The framebuffer console appears on the TV at boot and modetest puts a SMPTE pattern on
screen at 1920x1080@60 with no probe modules loaded.

Four independent faults, each of which alone gives a screen full of noise while every
register reads back as correctly configured hardware:

  1. sun6i_drc.c writes no registers at all. The A80's DRC needs its size, an identity
    CSC over the BT.709 matrix it holds at reset, and a double-buffer latch - without
    which the registers read back perfectly and do nothing. Measured through the DRC's own
    write-back: programmed gives 57376/65536 non-zero and 8 distinct values (the bars),
    stock mainline gives 0.
  2. The layer address is in bits and arrived 0x20000000 low. The framebuffer was at
    physical 0xfc900000; the hardware was told 0xdc900000. Both give the same low word
    once shifted, differing only in the four high bits.
  3. An opaque XRGB8888 layer was composited away - the blender falls back to per-pixel
    alpha and the A80 reads those padding bits as zero.
  4. TCON1 was scanning out DE 0 while the pipeline runs on DE 1. mainline has the
    mechanism (needs_de_be_mux) but sets it only for the A31.

Kernel commits on draco-aw80: 15592f068, 3a822cea9, 133105f40.

The original framing of this issue - "the DRC never receives the backend's pixels" - was
wrong, and wrong because of a probe that read the layer address register as a byte address
when it is a bit address. Written up in
46-hdmi-picture.md.

Follow-ups opened separately for the two things this leaves behind.

**Resolved 2026-08-28. There is a picture.** The framebuffer console appears on the TV at boot and `modetest` puts a SMPTE pattern on screen at 1920x1080@60 with **no probe modules loaded**. Four independent faults, each of which alone gives a screen full of noise while every register reads back as correctly configured hardware: 1. **`sun6i_drc.c` writes no registers at all.** The A80's DRC needs its size, an identity CSC over the BT.709 matrix it holds at reset, and a **double-buffer latch** - without which the registers read back perfectly and do nothing. Measured through the DRC's own write-back: programmed gives 57376/65536 non-zero and 8 distinct values (the bars), stock mainline gives 0. 2. **The layer address is in bits and arrived 0x20000000 low.** The framebuffer was at physical `0xfc900000`; the hardware was told `0xdc900000`. Both give the same low word once shifted, differing only in the four high bits. 3. **An opaque XRGB8888 layer was composited away** - the blender falls back to per-pixel alpha and the A80 reads those padding bits as zero. 4. **TCON1 was scanning out DE 0** while the pipeline runs on DE 1. mainline has the mechanism (`needs_de_be_mux`) but sets it only for the A31. Kernel commits on `draco-aw80`: `15592f068`, `3a822cea9`, `133105f40`. The original framing of this issue - *"the DRC never receives the backend's pixels"* - was wrong, and wrong because of a probe that read the layer address register as a byte address when it is a bit address. Written up in [46-hdmi-picture.md](../src/branch/main/46-hdmi-picture.md). Follow-ups opened separately for the two things this leaves behind.
Sign in to join this conversation.
No description provided.