# Platform & SDK Support timps supports nine Ingenic SoC families: **T10, T20, T21, T23, T30, T31, T40, T41, C100**. **Unreleased:** T32 and T33 build against OpenIMP (experimental, see below). This page summarizes the cross-platform structure the codebase is built around; it deliberately does not duplicate the exhaustive per-function availability matrix already maintained in `docs/sdk-feature-gaps.md`, or the rotation deep-dive in `docs/rotation.md` — read those two documents directly for full depth. What a given camera can do is reported live by `GET /control` (`caps`); for the OpenIMP side see . ## SDK headers per platform The vendored Ingenic IMP headers (from the [gtxaspec/ingenic-headers](https://github.com/gtxaspec/ingenic-headers) git submodule under `include/`) are pinned per platform in the `Makefile`: | Platform | Header path | | --- | --- | | T31 | `include/T31/1.1.6/en` | | C100 | `include/C100/2.1.0/en` | | T21 | `include/T21/1.0.33/zh` | | T23 | `include/T23/1.3.0/en` (must be ≥1.1.2 — earlier headers are missing a struct member that, combined with a newer libimp, makes the framesource silently deliver no frames; `fs_create()` has a compile-time tripwire against this mismatch) | | T30 | `include/T30/1.0.5/zh` | | T40 | `include/T40/1.3.1/en` (matches the libimp 1.3.1 thingino ships; the older 1.2.0 header lacks `IMPEncoderStream.isVI`, so `IMP_Encoder_GetStream` would overrun a struct one word too short — `video_thread()` has a compile-time tripwire against this) | | T41 | `include/T41/1.2.6/en` (matches the libimp 1.2.6 thingino ships) | | T20, T10 | `include/T20/3.12.0/zh` (T10 has no distinct header set and builds against T20's) | | T32 | `include/T32/2.2.0/en` (**unreleased**, the API line the OpenIMP T32 libimp follows) | | T33 | `include/T33/2.0.2.1/en` (**unreleased**, the API line the OpenIMP T33 libimp follows) | ## Two encoder-API generations `src/hal/hal_ingenic.c` branches its encoder setup between two SDK generations: - **New generation** — `IMP_Encoder_SetDefaultParam`-based configuration: **T31, C100, T40, T41**. - **Old generation** — manual `IMPEncoderChnAttr`/profile/rate-control struct assembly: **T10, T20, T21, T23, T30**, and **T32/T33** (**unreleased**: its own rate-control structs, and the T41 form of the ISP sensor/tuning API). Every encoder-facing feature decision in the codebase (and in `docs/sdk-feature-gaps.md`'s roadmap of unused SDK capabilities) has to account for this split. ## ISP tuning API split Separately, the **ISP tuning** call signatures diverge on just two platforms: `ISP_NEW_TUNING_API` (`src/isp_caps.h`) is defined only for **T40/T41**, which use a reworked API taking an `IMPVI_NUM` + pointer argument form for many tuning calls, and route flips through `SetHVFLIP` instead of separate `SetISPHflip`/`SetISPVflip` calls. Every other platform (including the old-generation-encoder ones) uses the classic tuning API. ## ISP tuning capability matrix (`image.*`) `src/isp_caps.h` is the single source of truth both `hal_ingenic.c` (which calls guard which SDK function) and `/control`'s `caps.image` list consult — see [Configuration Reference](Configuration-Reference.md#image--isp-tuning) for the full per-key table. Summary: | Capability | Platforms | | --- | --- | | Brightness/contrast/saturation/sharpness, anti-flicker, running-mode, sensor FPS | All SoCs | | Hue (`SetBcshHue`) | T23, T31, T40, T41, C100 | | Backlight comp / defog / DPC strength | T23, T31, C100; defog and DPC also on T30 (since v1.12.0, vendor and OpenIMP) and on T41 with OpenIMP (ISP module ratios 4/3, since v1.11.0); backlight not on T30 | | DRC / WDR strength | T21, T23, T31, C100; T30 (since v1.12.0, vendor and OpenIMP); T41 with OpenIMP (module ratio 2, since v1.11.0) | | T30 ISP keys (since v1.12.0, vendor and OpenIMP libimp) | `sinter_strength`/`temper_strength`/`dpc_strength` map 0..255 onto the T30's 50..150 % (128 = 100 %, the vendor picture), `dpc_strength` via `SetDPStrength`; `drc_strength` via `Get/SetRawDRC` (0 = off, 128 = restore the attributes read before the first change, else manual strength); `defog_strength` via `SetAntiFogAttr` (≤128 off, 129–170 weak, 171–212 medium, 213+ strong). No hue on T30 | | Colour effect (`colorfx`) / scene preset (`scene`) | OpenIMP only: colorfx on T10, T20, T21, T23, T30 (since v1.12.0), T31; scene on T10, T20, T21, T23, T31 (not T30: the call is a no-op there) | | Highlight depress, sinter/temper NR, max analog/digital gain, manual WB, ISP-level hflip/vflip | All **except** T40/T41 (`ISP_NEW_TUNING_API`); sinter NR (since 1.9.33) and temper NR (since v1.10.0) on T41 with OpenIMP; max analog gain on T40 since v1.12.0 (vendor and OpenIMP, `AeExprInfo.AeMaxAGain`) | | AE compensation | All except T21; T40 since v1.12.0 (vendor and OpenIMP); T41 with OpenIMP (since 1.9.33) | | AE average luminance (`GetAeLuma`, secondary day/night metric) | T21, T23, T31, C100 | | Real sensor-attr readback (`GetSensorAttr`) | T23, T31, T32, T33, T40, T41, C100 (absent on T10/T20/T21/T30) | A build with no `PLATFORM_*` macro defined at all (the host `make sim` build) enables every capability, so the WebUI can be fully exercised against `timpsd-sim`. ## Audio capability matrix (`audio.*`) From `src/audio_caps.h`: every SoC supports `IMP_AI_SetVol`/`SetGain`, `EnableHpf`/`DisableHpf`, `EnableAgc`/`DisableAgc`, and `EnableNs`/`DisableNs`. **Analog PGA gain** (`IMP_AI_SetAlcGain`, `audio.alc_gain`) is restricted to **T21, T31, C100** (T10 shares T20's headers, which also lack it, so T10 has no ALC either). See [Audio](Audio.md) for how this interacts with the native speaker pipeline. ## Motion detection capability (`motion.*`) From `src/motion_caps.h`: whether `IMP_IVS` move detection exists, and the maximum grid cell budget, come directly from whichever SDK header set a platform's build links against (`IMP_IVS_MOVE_MAX_ROI_CNT`) — 52 cells on essentially every SDK version timps builds against today, with the one documented historical exception being the old T10/T20 3.9.0 SDK (4 cells), which the current Makefile no longer selects (it pins T20 3.12.0). See [Motion Detection](Motion-Detection.md). ## Rotation capability tiers Image rotation (`videoN.rotation`; always built since v1.12.1, up to v1.12.0 the build option `USE_ROTATE`, off by default) has the most platform-fragmented capability story in the codebase — full detail lives in `docs/rotation.md`; the tiers are: | Tier | Macro | Platforms | Mechanism | | --- | --- | --- | --- | | No rotation primitive | *(none)* | C100; T10, T20, T21, T23, T30 with the vendor libimp | `rotation` accepts only `0`; any 90/180/270 request is coerced to 0 with a warning. | | FrameSource software rotate | `ROT_HAS_FS_ROTATE` | T31; T10, T20, T21, T23, T30 with OpenIMP | `IMP_FrameSource_SetChnRotate` — rotation happens inside libimp, not visible to the rest of timps as "software," but still not true hardware. Hardware OSD/privacy overlays work on rotated 90/270 streams (fixed 2026-07-23) but are confined to a top-band region on non-square rotated streams due to an OSD coordinate range-check quirk (vendor libimp only). On T10/T20/T21 (OpenIMP only, since v1.10.0, not yet tested on hardware): sub streams only, 16-aligned, at most 704x576, whatever the libimp reports; `video0` and anything larger start unrotated. Every OpenIMP SoC needs an OpenIMP that rotates there (T1x first in commit `6b7f5ae`); without `OpenIMP_Cap_FsRotate` timps probes `SetChnRotate` with a 0x0 picture at start and brings the stream up unrotated when the OpenIMP stubs (`9cab219`, returns success without rotating) or refuses (`6857610`, `0c02f45`, `636b6ac`) it. Since v1.12.0 T23 and T30 OpenIMP builds use the same path (OpenIMP `next` `17082ed3` or later); an OpenIMP exporting `OpenIMP_Cap_FsRotate` skips the 0x0 probe, and `OpenIMP_FS_RotateLimits` sets the envelope per channel (current OpenIMP: T31 4096x4096 align 2; T10/T20/T21 704x576, T23 and T30 1280x720, align 16; no fps cap). The timps-own T23 software path was removed in v1.12.0, together with its `USE_SW_ROTATE` knob. | | True hardware I2D rotate | `ROT_HAS_HW_I2D` | T40, T41 | The dedicated I2D hardware block. Full framerate, all resolutions. With the vendor libimp timps applies the same top-band OSD/privacy clamp on 90/270 as on T31 (`video..rotate_path` = `band`; whether this libimp needs it is not verified on hardware); OpenIMP and `180` draw over the whole frame. The only tier where `rotation=180` is a real, distinct, **per-channel** capability (elsewhere, 180 was removed as a redundant, incorrectly-per-stream-modeled *global* ISP flip — use `image.hflip`+`image.vflip` instead, which is always available). | `ROT_HAS_90` (gating whether 90/270 are accepted at all) is defined whenever either apply-path macro above is active, or on the host sim. Rotation direction conventions are **not** uniform across platforms (T40/T41: 90° = clockwise; T31 and T10/T20/T21/T23/T30: 90° = counter-clockwise, kept that way specifically for numeric compatibility with prudynt-t's raw `rotTo90` enum values) — see `docs/rotation.md` for the full rationale and on-device test history. Since v1.11.0 `GET /control` reports the path of a running stream in `video..rotate_path` (`off`, `band`, `hw`; `sw` only from timps ≤ 1.11.1). ## ROI and FrontCrop tiers Since v1.10.0, encoder ROI (`roi..*`) and ISP FrontCrop (`image.fcrop_*`) carry a per-SoC tier in `src/roi_caps.h`, reported as `caps.roi.tier` / `caps.fcrop.tier`: **effective** = device-tested, **experimental** = the call exists and returns 0 but the effect is unverified (or known absent), **unsupported** = refused. Both setters are bound weak, so a libimp without the symbol reports unsupported at run time. The OpenIMP tiers come from device tests by the OpenIMP maintainer (OpenIMP d03ca24, open-tx-isp 3cf9bdac), not from timps itself. | SoC | ROI vendor | ROI OpenIMP | FrontCrop vendor | FrontCrop OpenIMP | | --- | --- | --- | --- | --- | | T10 | experimental | **effective** | unsupported | **effective**, live | | T20 | experimental | **effective** | unsupported | **effective**, live (downscaled streams only) | | T21 | experimental (no effect seen) | **effective** (`OPENIMP_T21_ROI=0` turns it off → experimental) | unsupported | **effective**, live | | T23 | unsupported (encoder only stores the region) | **effective** when the libimp exports `OpenIMP_Cap_T23HelixRoi` (OpenIMP v2026.10.11-rc1+, since v1.10.1), otherwise unsupported; `OPENIMP_T23_ROI=0` → unsupported | unsupported (untested) | **effective**, live with the driver's `fcrop_upscale_pct` parameter (open-tx-isp b15ef235); otherwise experimental, **start-only** (needs 3cf9bdac+) | | T30 | unsupported | **effective** since v1.12.0 (family A `IMP_Encoder_SetChnROI`, OpenIMP 03dd0ce3) | unsupported | unsupported | | C100 | unsupported | — | unsupported | — | | T31 | unsupported (no ROI symbol in 1.1.x) | **effective** (`SetChnRoiAttr`, needs OpenIMP d03ca24 + fec3e69; `OPENIMP_T31_ROI=0` → unsupported) | experimental, live (no zoom without open-tx-isp `fcrop_upscale_pct`) | **effective**, live with the driver's `fcrop_upscale_pct` parameter (open-tx-isp 27dc8be4+); otherwise experimental, live | | T40, T41 | unsupported (vendor T41 takes deltas only; not device-tested) | **experimental** since v1.11.0 when the libimp exports `OpenIMP_Cap_EncRoi` (QP deltas only; a T41 libimp before OpenIMP 958f7f3 ignores regions under FixQP: `caps.roi.streams[N]` = `"fixqp"`) | unsupported | **experimental** since v1.11.0 through `IMP_ISP_Tuning_SetAutoZoom` when the libimp exports `OpenIMP_Cap_AutoZoom` (`caps.fcrop.api` = `autozoom`, no upscaling); T41 live up to the driver envelope (`t41_msca_live_max_w/h`, 1920x1080 on current open-tx-isp, 768x432 before), a larger stream restarts its FrameSource (~45 ms, new keyframe); T40 live up to 4K | T23/T31 FrontCrop with OpenIMP: timps checks at start for `/sys/module/tx_isp_t23|t31/parameters/fcrop_upscale_pct`, which only drivers with the T23 crop-hang fix (open-tx-isp 164225e5/25b1d8c7) or the T31 guard (27dc8be4) have. Without it, T23 stays start-only (older drivers hung the pipeline on a crop change): a POST is persisted and applied at the next `/etc/init.d/S95timps restart`. A window smaller than a stream that is not scaled down is refused with `-EINVAL` (one WARN, `caps.fcrop.state` = `rejected`). Since v1.11.0 `image.fcrop_upscale_pct` (live) writes that parameter, so the scaler may upscale a window down to half the stream size; the ceiling `caps.fcrop.upscale_max` is 100 on both (device-tested; T31 at 150 hard-hangs until the hardware watchdog resets the camera) and -1 on every other SoC/libimp. ## Day/night colour statistics The colour trigger of v1.11.0 (`daynight.color_pct`) reads per-zone AWB statistics: `GetAwbStatistics` on T40/T41, `GetAwbZone` on T23/T31/C100, and on T10/T20 with OpenIMP a tuning ioctl on libimp's `/dev/video0` fd. With OpenIMP on T23/T31 and T40/T41 only where the libimp exports `OpenIMP_Cap_AwbZone` / `OpenIMP_Cap_AwbStatistics`. Vendor T10/T20/T21, T21 with OpenIMP and T30 have none (`daynight.color_trigger` = `unavailable`). Device-tested on T40 vendor and T20 OpenIMP only. ROI QP ranges: delta -25..25 everywhere (-26 gave invalid H.264 on T20); T21 and OpenIMP T23 honour only about -12..+13 (`caps.roi.qp_delta_eff`), positive deltas are also capped by `max_qp`. An absolute QP is sent as a clamped delta when it is more than 25 from a possible picture QP (`caps.roi.abs_window`). T10/T20 crop only downscaled (sub) streams; a main stream at sensor size cannot zoom there (`caps.fcrop.zoomable`). Raising a tier after a device test is one line in `src/roi_caps.h` plus its `ROI_MATRIX` entry in the Makefile (`make test-roi`). ## FrameSource keepalive default Since v1.12.0 the default is `general.fs_keepalive = 1`: every build keeps its FrameSources running from start to stop. `-1` (`auto`, the default before) resolves per build, from `FS_KEEPALIVE_AUTO` in `src/hal/fs_ref.h`: | Build | Auto resolves to | Why | | --- | --- | --- | | T23 with `USE_OPENIMP` | **on** | FrameSource disable/enable edges while streaming hung a T23 on the open stack. | | T41 with `USE_OPENIMP` | **on** | Reproduced hang (3/3): one channel stopped and restarted within ~200 ms while the other ran. | | T40 with `USE_OPENIMP` (since v1.11.0) | **on** | Without frames the open T40 driver runs no AE, so day/night read a frozen exposure. | | T10, T20, T21, T30, T31 with `USE_OPENIMP` | off | No hang known; idle cost of a 24/7 FrameSource unmeasured on these (mostly small-RAM) SoCs. | | Every vendor-libimp build (incl. T23, T41) | off | Same; the vendor T41 has not been tested for the fault. | `general.fs_keepalive = 1` (the default) turns it on anywhere, `0` turns it off anywhere. An unknown word parses as auto, so a typo turns the keepalive off on every "off" build above. See [Architecture](Architecture.md#framesource-keepalive-generalfs_keepalive). ## Where to go deeper - **`docs/sdk-feature-gaps.md`** — an independent, function-by-function audit of every `IMP_*` SDK declaration across all nine platforms against what timps actually calls today, with a fleet-weighted prioritized list of unused capabilities (live encoder bitrate/GOP/QP reconfiguration, backchannel echo cancellation, live IVS sensitivity without a full rebuild, AE zone weighting, ROI-based encoding, ePTZ, WDR, privacy mosaic, and more) — useful reading before proposing a new feature that touches the SDK directly. - **`docs/rotation.md`** — the full rotation story: exact per-SoC resolution/framerate constraints, the T31 hardware-OSD-on-rotated-stream investigation trail, direction-convention history, and how the other thingino streamers (prudynt-t, raptor, strero) compare.