==================================== 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 :doc:`tcon`. The *hardware* side — the DPU/DSI block, the LF-CPNX EPD-FPGA, and the panel power rails — is in :doc:`/display/panel`; the FPGA control GPIOs and the front-light enables are in :doc:`/soc/pinctrl`. 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. Architecture at a glance ======================== .. code-block:: text 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 :doc:`/display/panel`. 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. 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``): .. list-table:: :header-rows: 1 :widths: 42 58 * - 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 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 :doc:`ebc-interface`, since the kernel driver internals (:doc:`tcon`) and this userspace-facing page both need the same facts. 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 :doc:`ebc-interface`, 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. 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. 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 :doc:`/soc/pinctrl`). 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``: .. list-table:: :header-rows: 1 :widths: 34 66 * - 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. 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 :doc:`/userspace/onyx-platform`); - ``chmod 0666 /dev/ebc`` (on the ``charger`` trigger too, so the charging / sleep screen can be drawn); - mounts ``/onyxconfig`` (the ``onyxconfig`` partition, :doc:`/partition-map`) 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``: .. list-table:: :header-rows: 1 :widths: 34 66 * - 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 :doc:`/userspace/onyx-platform` * - ``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) 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 :doc:`/userspace/firmware-blobs`) 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 :doc:`/soc/pinctrl`; ``/sys/wacom_ts/{caldata,rawdata}`` expose pen calibration. Scope boundary ============== This page covers the userspace services and the built-in kernel EPDC driver interface; the ioctl/waveform contract is in :doc:`ebc-interface`, the register-level driver internals — the Lattice ECP5 FPGA TCON command set, the LUT engine, and the EPD-PMIC/LM3630A register maps — are in :doc:`tcon`, and the display hardware — the LF-CPNX EPD-FPGA, panel power sequencing and the boot splash — is in :doc:`/display/panel`. The Onyx Android framework above this layer (the note/handwriting engine, the launcher's refresh-mode policy) is application-layer and out of scope. 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: :doc:`ebc-interface` (ioctls, update modes, waveform format), :doc:`tcon` (register-level driver internals), :doc:`/display/panel` (DPU/DSI + EPD-FPGA hardware), :doc:`/soc/pinctrl` (EPD-FPGA control GPIOs, front-light enables, touch/pen pins), :doc:`/userspace/firmware-blobs` (touch firmware, and the CONFIG_EXTRA_FIRMWARE discussion), :doc:`/partition-map` (the ``onyxconfig`` partition).