Research: A2 Waveform Mode — Kernel Transform Analysis
Status: Research complete. NOP patch tool ready. Blocked on test session.
Problem
TWRP requests A2 waveform mode (0x0c) for fast e-ink refresh, but the kernel’s onyx_waveform_mode_transform() converts it to GC16 (mode 2), causing slow full-refresh behavior. Kernel log confirms: waveform_mode[2] is_convert[1].
Two Display Update Paths
| Path | Used by | Transform applied? |
|---|---|---|
| /dev/ebc ioctl (0x700c) | TWRP, stock recovery | YES — waveform transform table applied |
| onyx_epdc_set_mode() kernel symbol | HWC/SurfaceFlinger (stock Android) | NO — bypasses transform entirely |
Stock Android A2 mode works because apps use Path 2 (via HWC), which bypasses the transform. TWRP must use Path 1 (the only option without SurfaceFlinger), so it hits the transform.
Root Cause: Transform Function
| Field | Value |
|---|---|
| Function | onyx_waveform_mode_transform |
| Kernel offset | 0x427a8c |
| Called from | 0x41d410 (BL 0x427a8c inside SEND_UPDATE handler) |
| Waveform version in firmware | 0x19 (“onyx waveform sg”) |
| What it does to A2 (0x0c) | Maps to AUTO (0xFF) → then map_auto_mode() at 0x427bc8 converts AUTO → GC16 (0x02) |
Fix: NOP the Transform Call
| Field | Value |
|---|---|
| Patch offset | 0x41d410 |
| Original bytes | 0x9400299F (BL 0x427a8c) |
| Patched bytes | 0xD503201F (NOP) |
| Effect | All waveform modes pass through unchanged. AUTO (0xFF) still → GC16 via map_auto_mode(). |
| Tool | tools/patch-kernel-waveform-transform.py |
# Extract kernel, apply patch, repack
magiskboot unpack boot.img
python3 tools/patch-kernel-waveform-transform.py kernel [--dry-run]
magiskboot repack boot.img patched-boot.img
# Flash via TWRP dd or EDL
Alternative: RET at function entry (Method 2)
Patch offset 0x427a8c (first instruction of the transform function) with 0xD65F03C0 (RET). Same effect but patches the function itself rather than the call site — affects any other callers too.
Key EPDC Ioctl Numbers
| Ioctl | Name | Status |
|---|---|---|
| 0x7000 | GET_EBC_BUFFER | Confirmed |
| 0x7001 | SET_EBC_SEND_BUFFER | Confirmed |
| 0x7002 | GET_EBC_BUFFER_INFO | Confirmed |
| 0x700c | SET_EBC_SEND_UPDATE | Confirmed — main update command |
| 0x700d | SET_EBC_WAIT_ALL_UPDATE_COMPLETE | Confirmed |
| 0x700e | SET_EBC_CLEAR_ALL_UPDATE | Confirmed |
| 0x7014 | SET_EBC_GAMMA_TAB | Confirmed |
Full 34-command ioctl dispatch table found in kernel binary at 0x41eaf8. See docs/research/a2-waveform-mode.md for complete listing.
ebc-probe Warning
⚠️ Do NOT run ebc-probe in TWRP. Even phase 1 (ioctl probe with zeroed buffers) causes kernel panic → 900E EDL dump mode. The EBC driver does not tolerate probing when SurfaceFlinger/HWC is not running. ebc-probe requires a full Android boot.