Ingenic ISP Tuning Parameters, Explained
Every Ingenic camera ships with a handful of hidden tweaks under the hood,
settings with names like isp_memopt and isp_ch0_pre_dequeue_time that sit
in a config file rather than a menu. The defaults are fine, until they are
not: a camera that runs out of memory and drops the feed, a stream that
stutters under load, a log that drowns everything useful in routine chatter.
When one of those happens, these are the knobs you reach for. They all live
in the same place, so it is worth a quick look at that first.
Where these settings live
All of these settings arrive as name=value pairs on a single line, written
into a file on the camera at build time. A T31 loads them like this:
tx_isp_t31 isp_clk=200000000 isp_day_night_switch_drop_frame_num=0 \
isp_ch0_pre_dequeue_time=24 isp_ch0_pre_dequeue_interrupt_process=0 \
isp_memopt=1 print_level=1
The kernel driver tx-isp reads the line and accepts each pair. In the driver
source, a pair is declared like this:
module_param(print_level, int, S_IRUGO);
MODULE_PARM_DESC(print_level, "isp print level");
One thing to know before going any further. The actual image-processing code inside the ISP is a closed binary supplied by Ingenic; only the thin outer layer is open source. The open code tells us a knob exists, and it tells us its name, but often not what the knob does inside. From here on, the excerpts show what the open code says; the plain-English explanation is the part that had to be reasoned out.
isp_memopt
This setting controls how much memory the ISP is allowed to use. Think of the ISP as a workshop with several workstations in a row. The picture moves along the line, and each workstation does one small job. Every workstation needs storage space to hold the picture while it works on it.
On a cheap camera chip, there is very little RAM. The isp_memopt setting
tells the ISP which workstations to switch off, so the ones that remain have
enough space to do their job. Switching a workstation off saves memory, but it
also means that particular bit of picture polish does not get done.
The code names three workstations it can switch off:
/* 0:nomal 1:bypass Lynne+BGM 2:bypass Lynne+BGM+Ass 3-f:nomal */
static int isp_memopt = 1;
Lynne, BGM, and Ass are the names Ingenic gave to three of the workstations; nobody explains what the names stand for, so we treat them as labels. What matters is the trade-off:
| Value | What happens |
|---|---|
0 |
All workstations on |
1 |
Switch off Lynne and BGM |
2 |
Switch off Lynne, BGM, and Ass |
3 |
Same as 0 (all on) |
The setting is actually two numbers packed into one: the low part controls the
main path, the high part controls a second path used by cameras with two
sensors. A normal single-sensor camera just uses 0, 1, or 2.
The build system sets 1 automatically for any chip with 64 MB of RAM or
less. That is the sensible trade-off: lose a little polish, gain a lot of
room.
isp_ch0_pre_dequeue_time
This setting controls how early the ISP hands over each picture. The ISP produces pictures one after another, like frames off a film reel. When a frame is finished, the ISP must hand it to the video encoder, which compresses it into a stream. This hand-over takes a little time.
The ISP only knows a frame is completely finished when it has drawn the last row of the picture. If it waits until that exact moment before starting the hand-over, the encoder sits idle for the whole duration of the hand-over, and the pipeline hiccups.
This setting fixes that. It says: start the hand-over this many milliseconds before the frame is officially declared finished. The picture data is already nearly all there, so starting early is safe, and it means the encoder can get going without waiting.
MODULE_PARM_DESC(isp_ch0_pre_dequeue_time, "isp pre dequeue time, unit ms");
A value of 24 means the hand-over starts 24 ms early. A bigger number hides
more of the pause, but if it is too big, the hand-over can start before the
last rows exist, which shows up as a torn or corrupted strip at the bottom of
the picture. A value of 0 turns the feature off and behaves the old way.
Channel 0 is the full-resolution output, the main stream. Channel 1 is the downscaled sub stream. Both are always present.
isp_ch0_pre_dequeue_interrupt_process
This setting controls the extra work allowed during the early hand-over. While the ISP is doing its normal work, it occasionally gets interrupted by small urgent jobs. This setting controls whether those urgent jobs are allowed to run during the early hand-over window described above.
MODULE_PARM_DESC(isp_ch0_pre_dequeue_interrupt_process, "isp pre dequeue interrupt process");
A value of 0 means no - during the early hand-over, the ISP focuses only on
handing the frame over, and nothing interrupts it. This is the simplest and
safest choice for a camera with one sensor, and it is what Thingino ships by
default.
A nonzero value allows some urgent jobs to run alongside the hand-over. That is useful only in special cases, such as a sensor or multi-camera setup that needs its interrupts handled earlier to keep everything in sync.
print_level
This setting controls how much the ISP talks. The ISP can print messages to
the kernel log about what it is doing. It controls how chatty it is. The
levels are spelled out in the open source (tx-isp-debug.h):
#define ISP_INFO_LEVEL 0x0
#define ISP_WARNING_LEVEL 0x1
#define ISP_ERROR_LEVEL 0x2
Messages have a level. A message is printed only if its level is at or above
the print_level setting.
| Value | What gets printed |
|---|---|
0 |
Everything: info, warnings, errors |
1 |
Warnings and errors |
2 |
Errors only |
3 and above |
Nothing |
Thingino defaults to 1 everywhere except the old T10/T20/T21 chips. That
gives warnings and errors in the log without drowning in routine info.
One quirk: when an error gets printed, the driver also prints a stack trace, a
dump of what the code was doing at that moment. So level 2 produces errors
plus a stack trace for each one.
isp_ch0_pre_dequeue_valid_lines
This setting defines what counts as “finished”. Recall that the early hand-over starts before the frame is officially done. It needs a rule for “done”. This setting supplies the rule: it tells the ISP how many picture rows must be valid before the frame counts as complete.
MODULE_PARM_DESC(isp_ch0_pre_dequeue_valid_lines, "isp pre dequeue valid lines");
A picture is drawn row by row, from the top down. The ISP counts the rows as they arrive. When it has seen this many valid rows, it declares the frame finished and hands it over. The value is a number of rows, and it pairs with the pre-dequeue time from earlier: the time says how early to reach for the frame, and this says how much of the frame must exist before the reach is legal.
isp_ch1_dequeue_delay_time
Channel 1 is the downscaled sub stream that every camera produces alongside the full-resolution main stream. This setting is the channel 1 version of the early hand-over timing.
MODULE_PARM_DESC(isp_ch1_dequeue_delay_time, "isp pre dequeue time, unit ms");
It is measured in milliseconds and behaves the same way as
isp_ch0_pre_dequeue_time, but for the sub stream.
isp_day_night_switch_drop_frame_num
This setting controls the frames tossed during the day/night changeover. Most cameras switch between a color picture for daytime and a black-and-white picture (with an infrared lamp) for nighttime. At the exact moment of the switch, the image sensor is still settling: colors are wrong, the exposure is wild, the first frames are garbage.
This setting tells the ISP how many frames to throw away at that moment, before any reach the video stream.
MODULE_PARM_DESC(isp_day_night_switch_drop_frame_num, "isp day night switch drop frame number");
A value of 0 drops nothing, so the first ugly frames appear in the stream. A
small number skips just the worst of them, keeping the switch brief. A large
number hides the mess entirely but adds a short freeze to the picture.
direct_mode
This setting controls how the ISP hands frames to the encoder. The ISP and the video encoder are two separate pieces of hardware. Normally the ISP writes each finished frame into a shared memory buffer, and the encoder reads it back out again - a stop at a relay station between two trains. This setting (present on the T23 and T41) changes that hand-off.
MODULE_PARM_DESC(direct_mode, "isp direct mode");
| Value | Mode | What it means |
|---|---|---|
0 |
Non-direct | Every frame goes through the shared buffer |
1 |
Direct | Frames go straight from the ISP to the encoder with no buffer stop |
2 |
Semi-direct | A middle path, useful with two sensors |
The direct modes are faster and use less memory because they skip the buffer copy, but they only work when the two chips are set up to talk directly to each other. The full answer involves IVDC, below.
IVDC
IVDC is the short cut between the ISP and the encoder. The name is short for ISP-VPU Direct Connect. “VPU” is the video processing unit, the hardware that does the compressing. IVDC is a direct cable between the ISP and the VPU: instead of the ISP writing a frame to memory and the VPU reading it back, the frame flows straight from one to the other.
That saves a full frame’s worth of memory and a copy step, which matters a lot on a camera with two sensors, where everything is done twice. Two settings control how much of the picture IVDC holds at once.
ivdc_mem_line
This setting controls how many rows IVDC parks in memory. Even a direct connection needs a little staging room, because the ISP and the VPU do not move perfectly in lockstep. This setting decides how many rows of the picture IVDC keeps parked in memory (DDR) while it transfers them.
MODULE_PARM_DESC(ivdc_mem_line, "ivdc mem line");
A row here is one full width of the picture, rounded up to a multiple of 256
pixels. The value 0 (the default) means “store a whole frame”, which is the
safe, memory-hungry choice. A value greater than zero caps the storage at that
many rows, trading less memory for tighter timing. This setting exists on the
T23 and T41, and only matters on a multi-sensor camera.
ivdc_threshold_line
This setting controls how much IVDC buffers before it flushes. Where
ivdc_mem_line sets the maximum rows stored, ivdc_threshold_line sets the
amount that must build up before IVDC moves it along. It is a cache size,
measured in rows, rounded to a multiple of 256 pixels.
MODULE_PARM_DESC(ivdc_threshold_line, "ivdc threshold line");
The value 0 (the default) sets the threshold to half a frame. A value greater
than zero sets it to that many rows. Bigger thresholds move data in larger
chunks, which is more efficient but needs more memory; smaller thresholds move
it sooner. Like ivdc_mem_line, it is a multi-sensor setting on the T23 and
T41.
mipi_switch_gpio
This setting names the pin that selects the active sensor. A two-sensor camera does not run both sensors at full speed all the time. It switches between them, and the switch is a physical wire controlled by a GPIO pin on the chip.
mipi_switch_gpio=7
It exists only on the T23 and only when the camera has more than one sensor.
The value is the GPIO number, usually 7.
isp_clk, isp_clka, isp_clks
These are three clock speeds for three different parts of the ISP. The ISP is not one circuit running at one speed. It has three separate clocks:
| Setting | What it clocks |
|---|---|
isp_clk |
The ISP core itself, where the picture processing happens |
isp_clka |
The AXI bus, the data highway into and out of the ISP |
isp_clks |
The scaler, which resizes the picture |
Each clock can be sourced from one of the chip’s clock generators (SCLKA, MPLL, VPLL, and so on) and set to a speed. These are calculated and wired up by the build system, so they are not normally set by hand. They show up in the load line because the driver needs to be told what to run at, and because tuning a camera for a specific sensor sometimes means nudging one of them.
The T30 knobs
The T30 predates the modern ISP, and its settings are much blunter: plain numbers for the output size and crop rectangle, and buffer counts for the LDC (lens distortion correction) stage. These are not worth memorizing unless the camera uses a T30; they are listed here for completeness.
| Setting | Meaning |
|---|---|
ispw / isph |
Width and height of the ISP output, in pixels |
isptop / ispleft |
Top and left edges of the crop rectangle |
ispcrop / ispcropwh / ispcroptl |
Crop on/off, crop size, crop corner |
isp_scaler / isp_scalerwh |
Scaler on/off and output size |
isp_m1_bufs / isp_m2_bufs |
Number of LDC input and output buffers |
Changing the settings
Everything above is one trade-off after another: memory for polish, latency for safety, chatter for silence. Most of the time the build system already picked the right side of each trade for the chip’s RAM size, and the defaults are correct for a single-sensor camera. The settings exist for the cases where they are not.
Settings are controlled through menuconfig options in Config.soc.in, under
“ISP Kernel Module Configuration”. Two things must be set for each: the switch
that enables it, and the value. For example, on a T31:
BR2_ISP_CH0_PRE_DEQUEUE_TIME=y
BR2_ISP_CH0_PRE_DEQUEUE_TIME_VALUE=24
BR2_ISP_CH0_PRE_DEQUEUE_INTERRUPT_PROCESS=y
BR2_ISP_CH0_PRE_DEQUEUE_INTERRUPT_PROCESS_VALUE=0
BR2_ISP_PRINT_LEVEL_1=y
Settings with no dedicated menu option can be passed as raw text through
BR2_ISP_PARAMS, which is added to the load line as-is.
To check what is actually loaded, on the camera:
cat /etc/modules.d/20-isp
cat /sys/module/tx_isp_t31/parameters/isp_memopt
The module is named tx_isp_<soc>, so change the t31 to match the chip.
To see what the ISP is actually doing at runtime, read the companion piece Reading /proc/jz/isp/isp-m0: your camera’s health dashboard, which decodes the live status dashboard these parameters feed into.