This document captures the current shared understanding of the Ingenic T31 ISP hardware and the open-source tx-isp-t31.ko reimplementation in this repository. It is meant to be a durable architectural reference, not a point-in-time debugging log.
- Open-source implementation:
driver/ - OEM reference binary:
OEM-tx-isp-t31.ko - User-space ABI consumer: Ingenic
libimp.so - Primary goal: behavioral equivalence with the OEM driver so
libimp.soworks unmodified
The OEM binary is the authoritative reference for register values, sequencing, ioctl contracts, and error behavior.
At a high level, the T31 camera stack is:
- Image sensor outputs raw Bayer data
- CSI receives serialized sensor data
- VIC/VIN/core path moves frames into the ISP processing pipeline
- ISP processing blocks transform raw Bayer into tuned output
- Frame-source devices expose processed frames/channels to user space
libimp.soconfigures the pipeline through ioctls and tuning interfaces
The open-source driver models this as a kernel module plus several logical/platform subdevices rather than one monolithic code path.
The current driver models the T31 ISP around these key resources:
| Resource | Address / IRQ | Notes |
|---|---|---|
| ISP core MMIO | 0x13300000 |
Main ISP register space; mapped large enough to cover LUT windows used by OEM writes |
| CSI window | 0x10022000 |
Camera Serial Interface / DPHY-facing window |
| VIC window | 0x133e0000 |
VIC control/config window |
| Primary IRQ | 37 |
isp-m0, ISP core path |
| Secondary IRQ | 38 |
isp-w02, VIC / secondary path |
Important implementation detail: tx_isp_init_memory_mappings() maps the ISP core at size 0x90000, specifically to cover large OEM register ranges such as LSC and DEIR/GIB-related windows.
The source tree is organized by functional block:
| File | Role |
|---|---|
driver/tx-isp-module.c |
Module entry/exit, platform devices, shared register helpers, IRQ adoption/dispatch |
driver/tx_isp_core.c |
Core probe, MMIO mappings, core init, ISP ISR, first-frame bring-up logic |
driver/tx_isp_csi.c |
CSI device / stream control |
driver/tx_isp_vic.c |
VIC device / frame movement / buffer handling |
driver/tx_isp_vin.c |
VIN-facing subdevice |
driver/tx_isp_fs.c |
Frame-source channels presented to user space |
driver/tx_isp_tuning.c |
Tuning subsystem, ISP block init, parameter IOCTLs, per-block updates |
driver/tx_isp_subdev*.c |
Subdevice registration, graph creation, pad/link management |
driver/include/ |
Shared headers, structs, register-map helpers |
tx-isp-module.c creates logical platform devices for:
tx-isp(top-level wrapper)isp-m0(core / primary IRQ domain)isp-w00(VIN)isp-w01(CSI)isp-w02(VIC)isp-fs(frame source)
The subdevice graph is constructed in tx_isp_subdev_mgmt.c.
tx_isp_create_subdev_graph()builds the runtime graphtx_isp_create_basic_pipeline()creates the basic CSI → VIC pipeline when no registry is present
This graph-based model is the main in-tree abstraction for representing how the ISP hardware blocks are wired together.
The shared helpers in tx-isp-module.c are central:
system_reg_write(u32 reg, u32 value)system_reg_read(u32 reg)
These helpers resolve the live ISP base from ourISPdev->core_regs and are the preferred way to program ISP offsets from tuning code. Many OEM-aligned helpers (system_reg_write_awb, system_reg_write_clm, system_reg_write_gib, etc.) use the same base mechanism and prepend block-enable writes when needed.
The current driver bring-up is split across module init, platform probe, and core init:
tx_isp_init()intx-isp-module.c- allocates
ourISPdev - registers platform devices
- registers/initializes subdevice platform drivers
- creates the tuning node
- builds the subdevice graph
- allocates
tx_isp_core_probe()intx_isp_core.c- binds the existing
ourISPdev - initializes memory mappings first
- initializes tuning state after MMIO is available
- binds the existing
ispcore_core_ops_init()- runs the core activation sequence
- calls
tisp_init()once the VIC is in the ready state
This ordering matters: many failures seen during bring-up were caused by programming registers before mappings, clocks, or IRQ ownership were correct.
During stream-on / early ISR handling, two actions are especially important:
mbus_to_bayer_write()writes ISP register0x8with the live CFA/Bayer pattern derived from sensor mbus format plusshvfliptisp_top_sel()sets bit 31 of ISP register0xcon first interrupt to release top-level processing
If CFA phase is wrong, demosaic output is visibly corrupted.
driver/tx_isp_tuning.c is the bulk of the ISP behavior model. It contains:
- block initialization (
tiziano_*_init) - parameter bank storage and IOCTL set/get handlers
- day/night and WDR mode switching
- gain / EV / CT refresh hooks
- DMA-backed statistics setup for AE/AWB/ADR/DPC-style paths
The current tisp_init() sequence initializes blocks in an OEM-inspired order, including:
- AE / AWB / Gamma
- GIB / LSC / CCM / DMSC
- Sharpen / SDNS / MDNS / CLM
- DPC / Defog / ADR / AF / BCSH / YDNS / RDNS
ISP top-level block bypass is computed in tisp_compute_top_bypass_from_params() and written to register 0xc.
The in-tree documented block bits are:
- bit 2: DPC
- bit 4: LSC
- bit 5: GIB
- bit 7: ADR
- bit 8: DMSC
- bit 10: Gamma
- bit 11: Defog
- bit 12: CLM / BCSH path
- bit 14: Sharpen
- bit 15: SDNS
- bit 16: MDNS
- bit 17: YDNS
The first 32 words of the active tuning block contribute to OEM bypass computation, then local safety/debug masks can re-bypass specific blocks.
The main image-processing blocks currently modeled in-tree are:
- AE / AWB / AF: exposure, white balance, autofocus statistics/control
- DPC: defect-pixel correction
- GIB / GB: green imbalance / green-balance related stages
- LSC: lens shading correction
- DMSC: demosaic
- CCM / BCSH / CLM: color correction, brightness/contrast/saturation/hue, color luminance mapping
- Gamma: LUT-based tone mapping
- Sharpen: detail enhancement
- SDNS / MDNS / RDNS / YDNS: spatial, motion, raw, and luma denoise families
- Defog / ADR / WDR: dynamic range and tone-mapping related paths
driver/REGMAP_ADR_YDNS.md documents the ADR/YDNS register windows and some OEM ordering constraints.
Several subsystems maintain separate linear and WDR banks. The important global switches are:
tisp_s_wdr_en()for WDR transition sequencing- day/night control paths that swap
tparams_day,tparams_night, andtparams_active
Mode changes are not just single-bit toggles; they trigger ordered re-initialization or re-selection of multiple per-block parameter banks.
The driver uses multiple classes of tuning data:
- active day/night tuning blobs (
tparams_day,tparams_night,tparams_active) - hand-modeled or reverse-engineered parameter arrays in
tx_isp_tuning.c - DMA-fed runtime statistics for AE/AWB/ADR-style logic
- synthetic placeholder tables where OEM-calibrated content is still missing
external/ingenic-sdk/3.10/isp/t31/OEM_TUNING_BLOB_MANIFEST.md is the best current map of which subsystem tables are still synthetic or only partially populated.
These rules have repeatedly proven important:
- OEM register writes and write ordering matter
- clock/reset ordering matters
- IRQ ownership must not be duplicated or double-disabled
- struct layouts and ioctl sizes must remain compatible with
libimp.so - image-path fixes must be validated against both live output and OEM HLIL/decompilation
- dual-IRQ model (
37+38) - MMIO base ownership and
system_reg_*access pattern - overall source-file/module decomposition
- top-bypass architecture
- major block inventory and many register windows
- exact image-quality parity for all enabled blocks
- some runtime statistics consumers and per-frame tuning decisions
- full confidence in every block's live enable state under all modes
- OEM ae0_tune2 gain distribution (Phase H) needs exact threshold-based mode
- tisp_ae_tune function (scene-adaptive EV table adjustment) not yet implemented
The AE (Auto-Exposure) subsystem is the most complex algorithm in the ISP driver. See docs/AE_CONVERGENCE_ARCHITECTURE.md for the full reverse-engineered architecture including:
- OEM ae0_tune2 (0x500b8): 34-argument convergence controller with EV-domain FIFO, 64-bit interpolation, histogram-based brightness feedback
- Critical domain mismatch discovery:
_lum_listvalues (100-140000) are EV-domain targets, NOT 0-255 brightness - The
data_9a2ecflag enables brightness feedback by scaling ev_list/lum_list tables based on wmean - Zone data format: 4-word DMA entries with packed 21-bit R/G/B per zone
- OEM ae0_weight_mean2 (0x4f93c): separate R/G/B channel processing with 64-bit weighted accumulation
README.mdCLAUDE.mddocs/AE_CONVERGENCE_ARCHITECTURE.md— AE convergence algorithm deep divedriver/REGMAP_ADR_YDNS.mddriver/TX_ISP_VIDEO_S_STREAM_VERIFIED.mdexternal/ingenic-sdk/3.10/isp/t31/OEM_TUNING_BLOB_MANIFEST.md