10.3. The e-ink software stack (Onyx EPDC)

The display on the Note Air5 C is not driven by a normal Android graphics stack. The MSM DRM/KMS display driver is not built (# CONFIG_DRM_MSM is not set — the DRM core is compiled in, but nothing binds the panel through it), and the display is instead an Onyx software EPD controller (EPDC): a kernel framebuffer driver that composes grayscale/colour update regions, looks up the E-Ink waveform for the current temperature, and pushes frames through the SoC display interface to the panel’s timing controller. This page documents that stack from userspace down to the built-in kernel driver’s interface. The register-level internals of that driver — the Lattice-FPGA TCON command/flash protocol, the EPDC update engine, the EPD-PMIC and front-light register maps, and the full function catalogue, all recovered by static disassembly — are in E-ink driver internals (register-level, from the kernel). The hardware side — the DPU/DSI block, the LF-CPNX EPD-FPGA, and the panel power rails — is in Display pipeline — DSI transport to the color e-ink TCON; the FPGA control GPIOs and the front-light enables are in Pin control — TLMM and the PMIC GPIOs.

Everything here is from the decompressed kernel Image (artifacts/kernel) and artifacts/boot_a/kernel_config.txt, plus the userspace pieces extracted from system_a.img / vendor_a.img. No code was executed.

10.3.1. Architecture at a glance

Onyx app / framework
     │  update request: Surface::transferEpdc(rect + waveform_mode)
     ▼  (via patched libgui.so — android::EpdcWrapper)
SurfaceFlinger  (Onyx-patched)
     │  mergeByMode → onyx_epdc_screenRefresh →
     │  ioctl(/dev/ebc, SET_EBC_SEND_UPDATE = 0x700c, hwc_epdc_llist[])
     ▼
/dev/ebc  ── ioctl ──►  CONFIG_FB_ONYX_SOFTWARE_EPDC   (built-in kernel driver)
     │                     • builds per-update LUTs from the waveform
     │                     • temperature-compensated waveform lookup
     ▼
MSM SDE / DSI intf  (TCON-sync mode: MDP_FB_FLAG_SYNC_TCON)
     │
     ▼
LF-CPNX EPD-FPGA (TCON)  ──►  10.3" colour E-Ink panel
     (see /display/panel)

side channels (NOT in the frame-submit path):
  • /dev/onyx/listener (FIFO) → libonyx_epd_listener.so → Java onEpdEvent()
    — EPD *event notifications* up to the framework; issues no ioctl
  • libonyx_neo_dither.so (dithering) / libonyx_cfa.so (RGBW) — bitmap
    pre-processors run before submission; no /dev/ebc access

The key architectural fact is that the SoC’s own display hardware (the SDE/DPU and a DSI interface) is still used, but only as a pixel transport into the e-ink TCON. The DTB carries the SDE/DSI nodes and puts the DSI link into TCON mode (onyx,tcon-mode-support); that hardware-side evidence is documented in Display pipeline — DSI transport to the color e-ink TCON. On the kernel side the transport shows up as a TCON-synchronised path — sde_hw_intf_enable_timing_engine_tcon, MDP_FB_FLAG_SYNC_TCON, dsi tcon_mode_support — and the EPDC is configured with CONFIG_ONYX_EPDC_TCON_TYPE_LCDIF and CONFIG_ONYX_EPDC_SEND_MODE_VIDEO (its generic “LCDIF video” send path mapped onto that DSI/SDE intf). The actual electrophoretic timing is done by the LF-CPNX FPGA acting as the panel TCON.

10.3.2. The kernel driver (CONFIG_FB_ONYX_SOFTWARE_EPDC)

The EPDC is built into the kernel, not a module — there is no epdc.ko in /vendor/lib/modules. (The one display-related module present, lcd.ko, is the generic upstream drivers/video/backlight/lcd.c LCD-class helper, a dependency, not the EPD driver.) The driver registers a framebuffer — fb%d: onyx_epdc frame buffer, using %dK of video memory — and the character device /dev/ebc.

Relevant build-time options (kernel_config.txt):

Option

Meaning

CONFIG_FB_ONYX_SOFTWARE_EPDC=y

the software EPD controller itself

CONFIG_ONYX_EPDC_TCON_TYPE_LCDIF=y

drive the panel through the LCDIF/display-interface path (here the MSM SDE/DSI intf), not a dedicated hardware EBC

CONFIG_ONYX_EPDC_SEND_MODE_VIDEO=y

frames are streamed as video to the TCON

CONFIG_ONYX_EPDC_INIT_WAVE_FIRMWARE=y + CONFIG_ONYX_EPDC_FIRMWARE_WAVEFORM_KERNEL=y

the waveform is a firmware blob, sourced from the kernel’s built-in firmware (see The waveform)

CONFIG_ONYX_EPDC_DISPLAY_BUF_PIXEL_FORMAT_RGBA=y

the display buffer is RGBA (colour panel; see Colour (CFA / RGBW))

CONFIG_ONYX_EPDC_HANDWRITE_BUF_MALLOC_FROM_ION=y

the handwriting buffer is ION memory — the low-latency pen path

CONFIG_ONYX_EPDC_SEGMENT=y

segmented / partial-region updates

CONFIG_ONYX_EPDC_MFD_REGISTER=y / CONFIG_ONYX_EPDC_POWER_USE_REGULATOR=y

registered as an MFD; panel power via the regulator framework

10.3.2.1. The /dev/ebc ioctl interface

Userspace drives the panel entirely through ioctl on /dev/ebc. The ioctl command set, the update-request struct, the named update modes/schemes (A2, REGAL/GU, CFA, dither, handwrite, merge/snapshot) and the .wbf waveform file format are documented once, in The /dev/ebc interface: ioctls, update modes, and the waveform format, since the kernel driver internals (E-ink driver internals (register-level, from the kernel)) and this userspace-facing page both need the same facts.

10.3.3. The waveform

Every update is temperature-compensated (userspace supplies temp, the driver selects the matching per-temperature LUT). The waveform blob’s identity, its CONFIG_EXTRA_FIRMWARE build-in, and the header/temperature- band/LUT-tree layout are documented once in The /dev/ebc interface: ioctls, update modes, and the waveform format, which also covers the TCON firmware and panel configs built in alongside it — including why that makes the e-ink path’s firmware effectively below userspace despite being served by an ordinary built-in kernel driver.

10.3.4. Colour (CFA / RGBW)

The Note Air5 C is a colour device: a colour-filter-array (Kaleido-class) layer over the panel. The colour handling shows up at three levels:

  • kernel: the cfa_mode update path and the RGBA display buffer (CONFIG_ONYX_EPDC_DISPLAY_BUF_PIXEL_FORMAT_RGBA);

  • userspace: libonyx_cfa.so (a ColorUtils implementation with red/green/blue/white/gray accessors and an …toRgbwBitmap conversion — the panel’s subpixels are RGBW), and libonyx_neo_dither.so for the dithering that colour/gray e-ink requires.

10.3.5. Front-light

The front-light is two LM3630A LED drivers (a warm-white and a cold-white string; their enable GPIOs — gpio11 / gpio73 — are in Pin control — TLMM and the PMIC GPIOs). It is driven by Onyx’s own LM3630A driver, CONFIG_ONYX_BACKLIGHT=y + CONFIG_ONYX_BACKLIGHT_LM3630A=y — note the mainline CONFIG_BACKLIGHT_LM3630A is explicitly off. It exposes four backlight class devices, chmod’d for the framework by init.onyx.rc:

sysfs node

Role

/sys/class/backlight/warm/brightness

raw warm-LED channel (one LM3630A)

/sys/class/backlight/white/brightness

raw cold/white-LED channel (the other LM3630A)

/sys/class/backlight/onyx_bl_br/brightness

combined brightness abstraction

/sys/class/backlight/onyx_bl_ct/brightness

combined colour-temperature abstraction (warm↔cold blend)

So the user-facing “brightness” and “warm/cool” sliders map to onyx_bl_br / onyx_bl_ct, which the driver decomposes into the two raw LM3630A channels.

10.3.6. Userspace glue (init + libraries)

/vendor/etc/init/hw/init.onyx.rc wires the interfaces the framework uses:

  • creates the FIFO /dev/onyx/listener (0666, system) — the pipe the libonyx_epd_listener.so event listener reads and forwards to the framework (it issues no /dev/ebc ioctl; see The Onyx/Boox platform layer);

  • chmod 0666 /dev/ebc (on the charger trigger too, so the charging / sleep screen can be drawn);

  • mounts /onyxconfig (the onyxconfig partition, Partition map and checksums) and its mmkv key-value store used by the Onyx config service;

  • exposes /sys/onyx_misc/* control knobs: captp_disable (capacitive touch), stylus_disable, hall_ctl (cover magnet), wacom_pm (pen power), key_control, and onyx_active_pen/usi_{enable,haptic_type, haptic_strength} (USI active-pen with haptics).

The Onyx display/input libraries in /system/lib64:

Library

Role

libonyx_epd_listener.so

EPD event listener — reads the /dev/onyx/listener FIFO and forwards events to the Java framework (onEpdEvent); issues no /dev/ebc ioctl. The actual /dev/ebc client is the Onyx-patched SurfaceFlinger / libgui.so (EpdcWrapper), see The Onyx/Boox platform layer

libonyx_neo_dither.so

dithering for gray/colour e-ink

libonyx_cfa.so

colour-filter-array / RGBW colour processing

libonyx_pen_touch_reader.so

low-latency pen/touch input reader (feeds the ION handwriting buffer)

10.3.7. Touch and pen

The panel’s input side is multi-vendor at build time — the kernel enables Parade (CONFIG_TOUCHSCREEN_ONYX_PARADE + …CYTTSP5/6), Focaltech (…ONYX_FTS), Elan, Istaric and Wacom drivers, and three fingerprint vendors (CONFIG_ONYX_FINGERPRINT_{CHIPSAILING,FORTSENSE,MICROARRAY}) — but this unit ships a Parade capacitive controller (pt_tp@24, PIP2 protocol; firmware in Firmware-blob inventory (the payloads)) and a Wacom EMR digitiser (wacom@09, with CONFIG_ONYX_WACOM_FW_UPDATE and USI active-pen support). The controllers, their IRQ/reset/power GPIOs and wakeup routing are in Pin control — TLMM and the PMIC GPIOs; /sys/wacom_ts/{caldata,rawdata} expose pen calibration.

10.3.8. Scope boundary

This page covers the userspace services and the built-in kernel EPDC driver interface; the ioctl/waveform contract is in The /dev/ebc interface: ioctls, update modes, and the waveform format, the register-level driver internals — the Lattice ECP5 FPGA TCON command set, the LUT engine, and the EPD-PMIC/LM3630A register maps — are in E-ink driver internals (register-level, from the kernel), and the display hardware — the LF-CPNX EPD-FPGA, panel power sequencing and the boot splash — is in Display pipeline — DSI transport to the color e-ink TCON. The Onyx Android framework above this layer (the note/handwriting engine, the launcher’s refresh-mode policy) is application-layer and out of scope.

10.3.9. Provenance

Source:

artifacts/kernel (built-in onyx_epdc driver strings: TCON-sync intf, a2_enhance / gu_regal / cfa_mode / dither tokens), artifacts/boot_a/kernel_config.txt (CONFIG_FB_ONYX_SOFTWARE_EPDC and CONFIG_ONYX_EPDC_* / CONFIG_ONYX_BACKLIGHT_* / CONFIG_EXTRA_FIRMWARE options), system_a.img (waveform/eink_waveform.wbf, libonyx_epd_listener.so, libonyx_cfa.so, libonyx_neo_dither.so, libonyx_pen_touch_reader.so), vendor_a.img (etc/init/hw/init.onyx.rc, lib/modules/lcd.ko).

Method:

ext4 images read read-only with 7z; kernel read with strings / xxd; module identity from .modinfo. No code executed.

Cross-refs:

The /dev/ebc interface: ioctls, update modes, and the waveform format (ioctls, update modes, waveform format), E-ink driver internals (register-level, from the kernel) (register-level driver internals), Display pipeline — DSI transport to the color e-ink TCON (DPU/DSI + EPD-FPGA hardware), Pin control — TLMM and the PMIC GPIOs (EPD-FPGA control GPIOs, front-light enables, touch/pen pins), Firmware-blob inventory (the payloads) (touch firmware, and the CONFIG_EXTRA_FIRMWARE discussion), Partition map and checksums (the onyxconfig partition).