Cubie A7Z + Camera 4K (IMX415): continuous capture is unusable — ISP runs a 2-frame DOL stitch with only one buffer

Board: Radxa Cubie A7Z (Allwinner A733)
Camera: Radxa Camera 4K (Sony IMX415)
OS: Debian 11 Bullseye, kernel 5.15.147-21-a733
libisp: ISP602, branch libisp-dev, commit 665dc2e9613716c25abb6b64af145a73b989271e (2024-12-18)
Hi all,
We’re building a commercial product on the A7Z and have hit a hard blocker on the camera pipeline. I’ve traced it about as far as I can from outside the closed ISP library and wanted to post the full findings — partly to ask for help, partly so the next person searching for this finds something more detailed than “it’s broken.”
Happy to run any diagnostic you want on request.
Symptom
Single-shot capture is clean. num-buffers=1 at either 4K or 1080p gives a perfectly good frame — correct exposure, correct colour.
Continuous capture is not. Every frame comes out with:
a hard vertical split down the middle of the image, with each half processed differently
pink/magenta colour fringing, strongest along vertical edges
occasionally an entire frame in monochrome (AWB appears to fail outright on some runs)
This affects every frame in a continuous stream, not just the first few — so it isn’t AE/AWB converging over time. Resolution makes no difference: 4K and 1080p are both affected.
Pipeline used:

gst-launch-1.0 v4l2src device=/dev/video1 en-awisp=1 en-largemode=1 num-buffers=60 ! \

video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1 ! \

jpegenc ! multifilesink location=frame_%03d.jpg

Root cause (as far as I can see it)

The ISP is running a 2-frame DOL-HDR stitch but only ever receives one buffer. From the logs:

[ISP]STITCH_2IN1_LINNER

[ISP]ISP Set Sync Mode = 0x3, isp_sync_mode = 0x20003

[ISP]find imx415_mipi_1920_1080_30_0 [imx415_mipi_1080p_isp_cfg] isp config

[ISP_ERR]isp_stat_process_buffer, line: 265,AEWB: stats error, skipping buffer.

[ISP_WARN]can not use sync mode.(buffer0 = 0xffff8c0418c0, buffer1 = (nil))

buffer1 = (nil) is the crux. The ISP is configured to fuse a long and a short exposure, gets one real buffer and one null, and blends against nothing — which produces exactly the split-frame and colour-fringing artefacts we see.
The kernel sensor driver is not the cause. I pulled the matching source (radxa/allwinner-bsp, branch cubie-aiot-v1.4.6, drivers/vin/modules/sensor/imx415_mipi.c) and checked:
sensor_win_sizes contains only single-exposure modes (4K30, 1080p30) — there is no DOL/WDR entry to select
the register tables explicitly set 0x302C = 0x00 (DOL mode = normal) and 0x302D = 0x00 (wdsel = normal exposure)
isp_wdr_mode therefore defaults to normal, and sensor_g_mbus_config() takes the single-channel branch
So the sensor is already in linear mode. Something above it is still asking for a 2-frame stitch.
That “something” appears to be libisp.so. STITCH_2IN1 and isp_sync_mode don’t appear anywhere in the kernel BSP source — only in /usr/lib/aarch64-linux-gnu/libisp.so. The IMX415 tuning also isn’t in the kernel’s ISP config tree (drivers/vfe/isp_cfg/SENSOR_H/ only has gc1034, hm2131, ov2710, ov2775), so it looks baked into the library.
There’s also no runtime escape hatch — the only v4l2 controls exposed on /dev/video1 are:

brightness, contrast, saturation, hue, white_balance_automatic

No WDR/HDR/stitch/mode control at all.

What we tried (all unsuccessful)

Attempt: Device-tree overlay work_mode 0x01 → 0x00

Result: Camera stopped enumerating entirely (/dev/video1 gone). Restored. Not an HDR toggle.

Attempt: Making the ISP tuning context writable (chmod 777 /mnt, remove 0-byte stubs)

Result: Worth keeping — ISP now saves/loads a valid 38872-byte context and init is consistent. Did not fix banding.

Attempt: Manual exposure + gain via /dev/v4l-subdev0

Result: Values apply, image still banded. Confirms it’s the stitch, not the 3A.

Attempt: 4K continuous vs 1080p continuous
Result: Both affected.

Attempt: Capturing RAW Bayer to bypass the ISP

Result: Blocked — see below.

On the RAW-bypass idea

The sensor’s native output is SGBRG10_1X10 and all four video nodes advertise RAW Bayer formats, so this looked promising. But the media topology routes every video node through the ISP:

imx415 → sunxi_mipi.1 → sunxi_csi.1 → sunxi_tdm_rx.0 → sunxi_isp.0 → sunxi_scaler.0 → vin_video0

sunxi_tdm_rx.2 and .3 have unrouted source pads and the raw data clearly exists at sunxi_csi.1, but there’s no capture node tapped before sunxi_isp.0, and sunxi_isp.0 has an [ENABLED,IMMUTABLE] self-link. So RAW capture isn’t reachable without kernel/DT work.
What we’re asking
Is there a working linear/SDR (non-DOL) ISP tuning for the IMX415 on A733? That’s really all we need — we’d happily trade HDR dynamic range for a correct, even image. For a security/CV application, predictable linear output is better than adaptive HDR.
If not, is a libisp.so fix planned? Any rough timeline would help us plan.
Alternatively — can the IMX415 tuning source be shared so we can build a linear tuning with the ISP tuning tool ourselves?
Failing all of the above: is there a supported way to capture RAW Bayer pre-ISP on this topology? If a DT change or an additional video node can expose the CSI output directly, we’re prepared to do the software debayering ourselves.
We’re not looking for a workaround so much as a direction — happy to do the engineering if the path exists.
Related
This looks like the same underlying issue as radxa-build/radxa-a733 issue #3 (“tuning file for imx415 exists but is not working… artifacts and unusable colour tuning”), just with more detail on where it originates.
Also worth flagging while I’m here, since it affects the same product: the hardware H.264 encoder never emits an SPS NAL unit, so encoded output is undecodable. Same issue thread mentions it. We’ve fallen back to MJPEG, which works but costs us 5–8× the storage. If both are being looked at together, that’d be useful to know.
Thanks — and genuinely, thanks for how much of the BSP is open. Being able to pull the sensor driver and rule it out saved us days.

I don’t know much about the inner process, but pay attention to en-largemode.

A common mistake:

en-largemode=1 and video1 is for 3840x2160

en-largemode=0 and video0 is for 1920x1080

Here are the pipeline that work (encode and decode) and if using radxa image (and not customized anything) you should run with sudo (i think):

HD

gst-launch-1.0 -e v4l2src device=/dev/video0 en-awisp=1 en-largemode=0 num-buffers=300 ! video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1 ! queue ! omxh264videoenc target-bitrate=4000000 ! filesink location=stream_cam.h264

4K

gst-launch-1.0 -e v4l2src device=/dev/video1 en-awisp=1 en-largemode=1 num-buffers=300 ! video/x-raw,format=NV12,width=3840,height=2160,framerate=30/1 ! queue ! omxh264videoenc target-bitrate=4000000 ! filesink location=stream_cam_3840x2160.h264

They can all be decoded and played with ffplay on an x64 box:

rock@radxa-cubie-a7s:~$ ffprobe stream_cam.h264
ffprobe version 4.3.9-0+deb11u2 Copyright (c) 2007-2025 the FFmpeg developers
built with gcc 10 (Debian 10.2.1-6)
configuration: --prefix=/usr --extra-version=0+deb11u2 --toolchain=hardened --libdir=/usr/lib/aarch64-linux-gnu --incdir=/usr/include/aarch64-linux-gnu --arch=arm64 --enable-gpl --disable-stripping --enable-avresample --disable-filter=resample --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libdav1d --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librabbitmq --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --enable-pocketsphinx --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared
libavutil 56. 51.100 / 56. 51.100
libavcodec 58. 91.100 / 58. 91.100
libavformat 58. 45.100 / 58. 45.100
libavdevice 58. 10.100 / 58. 10.100
libavfilter 7. 85.100 / 7. 85.100
libavresample 4. 0. 0 / 4. 0. 0
libswscale 5. 7.100 / 5. 7.100
libswresample 3. 7.100 / 3. 7.100
libpostproc 55. 7.100 / 55. 7.100
Input #0, h264, from ‘stream_cam.h264’:
Duration: N/A, bitrate: N/A
Stream #0:0: Video: h264 (High), yuv420p(progressive), 1920x1080, 30 fps, 30 tbr, 1200k tbn, 60 tbc
rock@radxa-cubie-a7s:~$ ffprobe stream_cam_3840x2160.h264
ffprobe version 4.3.9-0+deb11u2 Copyright (c) 2007-2025 the FFmpeg developers
built with gcc 10 (Debian 10.2.1-6)
configuration: --prefix=/usr --extra-version=0+deb11u2 --toolchain=hardened --libdir=/usr/lib/aarch64-linux-gnu --incdir=/usr/include/aarch64-linux-gnu --arch=arm64 --enable-gpl --disable-stripping --enable-avresample --disable-filter=resample --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libdav1d --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librabbitmq --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --enable-pocketsphinx --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared
libavutil 56. 51.100 / 56. 51.100
libavcodec 58. 91.100 / 58. 91.100
libavformat 58. 45.100 / 58. 45.100
libavdevice 58. 10.100 / 58. 10.100
libavfilter 7. 85.100 / 7. 85.100
libavresample 4. 0. 0 / 4. 0. 0
libswscale 5. 7.100 / 5. 7.100
libswresample 3. 7.100 / 3. 7.100
libpostproc 55. 7.100 / 55. 7.100
Input #0, h264, from ‘stream_cam_3840x2160.h264’:
Duration: N/A, bitrate: N/A
Stream #0:0: Video: h264 (High), yuv420p(progressive), 3840x2160, 30 fps, 30 tbr, 1200k tbn, 60 tbc

Play it on x64 with ffplay:

ffplay -i stream_cam.h264

Yes, libisp-dev is kind of unstable and old: Wed Dec 18 15:26:13 2024 +0800 and need to be fixed. Hopefully. There are some workaround here on the forum and on internet.

Tip: You should reboot the board if libisp-dev fails for some reason to guarantee stable and smooth encoding. And watch for board temp as explained here on the forum.

Decoding:

gst-launch-1.0 -e filesrc location=./stream_cam.h264 ! h264parse ! queue ! omxh264dec ! fakesink
Setting pipeline to PAUSED …
DEBUG : cedarc CdcIniParserInit:41: load conf file /etc/cedarc.conf ok!

DEBUG : cedarc AwOmxComponentInit:40: OMXCORE: aw_omx_component_init f59d0810

INFO : cedarc <log_setlevel:73>: Set log level to 5 from /vendor/etc/cedarc.conf
WARNING: omx_vdec <__AwOmxVdecSetParameter:1632>: Set Parameter called in valid state
Pipeline is PREROLLING …
ERROR : omx_vdec AwOmxVdecPortGetFormat:365: erro: pParamData->nIndex > m_sPortFormatType.nIndex
ERROR : cedarc DebugCheckConfig:396: now cedarc log level:5
WARNING: cedarc InitializeVideoDecoder:718: warning: the nDeInterlaceHoldingFrameBufferNum is 0
Pipeline is PREROLLED …
Setting pipeline to PLAYING …
New clock: GstSystemClock
Got EOS from element “pipeline0”.
EOS received - stopping pipeline…
Execution ended after 0:00:01.836295181
Setting pipeline to NULL …
Freeing pipeline …

1 Like

Follow-up: en-largemode was most of it — but the 4K path itself still runs a broken 2-frame stitch (+ H.264 encoder times out on A7Z)

Thank you avaf for the reply, but the en-largemode / device-node pairing was the missing piece and I’d got it wrong. Posting full results in case they’re useful, because the outcome isn’t quite “fixed.”

TL;DR: video0 + en-largemode=0 + 1080p is clean and doesn’t engage the stitch at all. The 4K path engages it even when correctly paired, and still fails with buffer1 = (nil). Separately, omxh264videoenc initialises fine with sudo on A7Z but the encoder never raises its completion interrupt.

What I had wrong

I was running en-largemode=1 on /dev/video1 while requesting 1920x1080 — pulling 1080p frames through the 4K path. That produced a hard vertical split with heavy magenta banding on every frame. Entirely my mistake, and your note caught it.

Test 1 — correctly paired 1080p :white_check_mark:

gst-launch-1.0 -e v4l2src device=/dev/video0 en-awisp=1 en-largemode=0 num-buffers=90 ! \
  video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1 ! \
  jpegenc ! multifilesink location=hd_%03d.jpg

The stitch never engages. Comparing logs to the mismatched run, all of this is now absent:

[ISP]STITCH_2IN1_LINNER              <- gone
[ISP]ISP Set Sync Mode = 0x3          <- gone
[ISP]open isp device[1] success!      <- gone (only isp0 opens)
[ISP_WARN]can not use sync mode
          (buffer1 = (nil))           <- gone

Image is dramatically better. One residual: a faint vertical seam at the exact horizontal centre, left half marginally darker/warmer than the right, plus some soft pastel pink/cyan patches in flat areas. Cosmetic rather than broken — it doesn’t affect our CV pipeline — but it does suggest the ISP still processes the frame in two halves with slightly different calibration. Screenshot attached.

Test 2 - correctly paired 4K :cross_mark:

gst-launch-1.0 -e v4l2src device=/dev/video1 en-awisp=1 en-largemode=1 num-buffers=90 ! \
  video/x-raw,format=NV12,width=3840,height=2160,framerate=30/1 ! \
  jpegenc ! multifilesink location=k4_%03d.jpg

This is the part I think is worth your attention. Correctly paired, using the imx415_mipi_3840_2160_isp_cfg config — and the stitch still fails:

[ISP]STITCH_2IN1_LINNER
[ISP]ISP Set Sync Mode = 0x3, isp_sync_mode = 0x20003
[ISP]open isp device[0] success!
[ISP]open isp device[1] success!
[ISP_WARN]can not use sync mode.(buffer0 = 0xffff840597b0, buffer1 = (nil))

Two ISP instances come up for the 2-frame fusion, but only one buffer ever arrives. So the DOL-HDR mode appears broken independent of configuration — it isn’t something a user can pair their way out of.

Throughput is also low: 90 frames took 36 seconds (~2.5 fps) at 4K30.

Consistent with the kernel side, for what it’s worth: imx415_mipi.c in radxa/allwinner-bsp (cubie-aiot-v1.4.6) has only single-exposure entries in sensor_win_sizes[], and sets 0x302C = 0x00 (DOL off) / 0x302D = 0x00. So the sensor is in linear mode while the ISP is asking for a stitched pair — which would explain the null second buffer.

We’ve settled on 1080p/video0 for production. Happy with that; just flagging that 4K isn’t currently a usable option.


H.264 encoder on A7Z — different failure than I first reported

You were right about sudo. Without it:

open /dev/cedar_dev faild, fd = -1
CdcIonOpen: open /dev/dma_heap failed: Permission denied

With sudo it gets much further — VE initialises, buffers allocate:

INFO : cedarc <veEnvInit:139>: open /dev/cedar_dev fd = 7
INFO : cedarc <ve2_env_init:65>: open /dev/cedar_dev_ve2 fd = 33
DEBUG: cedarc <VeSetSpeed:2218>: *** set ve freq to 624 Mhz ***
DEBUG: omx_venc allocate buffer, InPort, nSizeBytes: 3110400

…and then dies on the first frame:

ERROR : cedarc <h264EncFrame:6956>: h264 encoder wait interrupt overtime
ERROR : omx_venc <ComponentVencThread:4145>: VideoEncodeOneFrame, failed, result: Venc_Result_Error
ERROR : OpenMAX component in error state Hardware (0x80001009)

Zero frames encoded, so the output has no headers — ffprobe reports Video: h264, none, unspecified size. I’d previously described this as “never emits SPS,” which was wrong: the missing SPS is a symptom of nothing being encoded at all.

I notice your working example was run on radxa-cubie-a7s. Mine is an A7Z. Two questions:

  1. Has omxh264videoenc been confirmed working on A7Z specifically, or is this A7S-only so far?

  2. Could this be a VE power-domain / clock issue? My boot log shows:

   sunxi:sunxi_pd_test-soc@3000000:pd_ve_enc_test@0:[WARN]: runtime_suspend disable clock

Is there something that needs enabling for the encoder domain on A7Z?

Also: is there a supported way to grant a non-root user access to /dev/cedar_dev* and /dev/dma_heap/system? A udev rule / group would be much better than running our service as root.

Environment: A7Z, Debian 11, kernel 5.15.147-21-a733, libisp-dev commit 665dc2e9 (2024-12-18), cedarc stable_v1.3.0_common commit 02914e20 (2022-08-22). Rebooted before every test as suggested.

  1. Sorry, i can only confirm it works on my A7S, but don’t see why it would not on A7Z
  2. i think it’s safe to ignore the Warning, i have this as well.

With udev rules you can run without the sudo.