Skip to content

Adding Ambarella S3L support - #71

Open
DeathCamel58 wants to merge 11 commits into
OpenIPC:masterfrom
DeathCamel58:s3l-hal
Open

DeathCamel58 wants to merge 11 commits into
OpenIPC:masterfrom
DeathCamel58:s3l-hal

Conversation

@DeathCamel58

@DeathCamel58 DeathCamel58 commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Adds a hardware backend for the Ambarella S3L (S3LM), tested on an Amcrest IP4M-1026E (Dahua IPC-HFW1430SN, S3L + OV4689). It drives the IAV driver (/dev/iav) through ioctls only and links no vendor library, so it builds for ARM with either float ABI. It's picked when the driver reports an S3L (AMBARELLA_CHIP in /proc/ambarella/iav).

What works

  • Video: H.264 and H.265 (bitrate, CBR/VBR, GOP, profile, forced IDR), timestamps from the encoder's PTS, and RTSP/fMP4.
  • MJPEG and snapshots:
    • MJPEG on the second encoder stream, or from the third source buffer above 720×720.
    • JPEG snapshots are frames of the MJPEG stream.
  • Sub-stream: a smaller H.264/H.265 stream beside the main one (HAL side; publishing it is Answer recorder ONVIF calls and publish a real sub stream #62's plumbing).
  • OSD: through IAV's encode overlay, with indexed colour, double-buffered and clipped to the stream.
  • Frame rates and sizes:
    • Per-stream frame rates within the DSP's encoder load limit (570600 MB/s on S3L).
    • Main stream sizes the source buffers can't take directly (e.g. 640×360 on a 2688×1512 sensor) are cropped, or rounded to the 16×8 grid with a warning.
  • ISP and 3A in Divinus:
    • The ISP configuration comes from a file named by system.sensor_config, or else /etc/sensors/<sensor>.bin for the detected sensor. OpenIPC's S3L package generates it at build time from options, with no vendor data: see ambarella-osdrv-s3l: generate the OV4689 ISP configuration at build time firmware#2793 for the generator and the OV4689 preset. With that in the image, Divinus needs no ISP configuration of its own.
    • Auto exposure through the VIN AGC/shutter ioctls, with anti-flicker.
    • Auto white balance from the DSP's per-tile statistics.
    • Night-mode grayscale.
    • New isp options: auto_exposure, exposure_target and auto_white_balance (see doc/config.md).
  • Not yet: audio. With audio enabled, Divinus warns and runs without it.

Majestic on S3L

Majestic also ships for S3L (OpenIPC/firmware#2785). On this board, both streamers now start preview on a fresh boot and keep a steady timestamp OSD: Majestic since its master+f2fa260 build, after the report on OpenIPC/firmware#2787. Divinus resets IAV's source-buffer types itself and double-buffers its OSD regions, so it has needed neither fix.

Notes for review

  • DSP microcode: both the SDK's 284712, which OpenIPC now ships (ambarella-osdrv-s3l: ship the DSP microcode from SDK 2.5.0_20181109 firmware#2782), and this camera's own 267837 work. The DSP takes the 3A statistics setup into effect only when preview starts, and the setup can only be sent once preview runs. In the first preview after the microcode loads, 284712 sends no statistics and 267837 sends stale ones (the same three buffers over and over). So on every start the HAL sends the setup, waits a frame, and starts preview again before any stream encodes, which costs about 0.15 s. If the driver refuses to go back to idle (an emulated DSP does), preview is still running: the HAL warns and carries on. Measured on fresh boots, both microcodes then give live statistics from the first frame.
  • IAV keeps state across runs (source-buffer types, overlays, mirror/flip), so the HAL sets them on every start. IAV state changes run with signals blocked, because a signal while the driver waits for the DSP left IAV refusing every later start until a reboot.
  • Sizes and frame rates are fixed when the pipeline starts. Changing a stream's size, or raising its frame rate, needs a restart; the README footnote says so.
  • MJPEG quality comes from mjpeg.qfactor, which media_mjpeg_enable passes on for every platform since Passing the MJPEG qfactor to the encoder #68.
  • Building: ./build.sh s3l (also in CI) uses OpenIPC's toolchain.ambarella-s3l. The S3L's Cortex-A9 has VFPv3, while the armhf-musl toolchain emits VFPv4 fused multiply-adds, which fault on it with SIGILL.
  • H.264 profiles: the S3L's IAV has no Baseline, so Baseline is encoded as Main with a warning. Rate modes other than CBR and VBR also warn.
  • Sizes and frame rates: the main buffer is sized for the H.264/H.265 stream (a larger MJPEG shares it), and the VIN runs at the fastest enabled stream's rate.
  • Platform enum: HAL_PLATFORM_S3L is inserted before T31 in the platform enum. Nothing stores or indexes the numeric values.

Testing

On the IP4M-1026E, netbooted, with each microcode:

  • H.264 and H.265 2688×1512 over RTSP;
  • a 640×360 main stream;
  • a 640×360 H.264 sub-stream beside the main one;
  • an OSD image region;
  • MJPEG 640×360 with /image.jpg snapshots;
  • flip on and then off across restarts;
  • audio enabled (warns, video-only RTSP);
  • AE/AWB with live statistics from the first frame after a fresh boot, on both microcodes;
  • H.264 profiles 0, 1 and 2 (Main, Main, High), and mp4 1280×720 with MJPEG at 1920×1080;
  • MJPEG qfactor 30 and 90 (matching JPEG quantisation tables);
  • the ISP configuration found as /etc/sensors/ov4689.bin with no sensor_config set.

Every commit builds with the S3L toolchain, and CI builds every target in build.yml, s3l included. The HAL was also read over in several review rounds (concurrency, memory safety against driver-reported sizes and the ISP file, integration with the other HALs) until a round came back with nothing left to fix (ten rounds).

Related OpenIPC work for this board: OpenIPC/firmware#2782 (SDK microcode), OpenIPC/linux#177 (overlayfs) and OpenIPC/linux#178 (board DTB).

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add an ioctl-only hardware backend for Ambarella S3L

✨ Enhancement ⚙️ Configuration changes 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Add an Ambarella S3L backend for H.264/H.265, MJPEG, snapshots, and on-screen display.
Diagram

graph TD
  Detect["S3L detection"] --> Media["Media integration"] --> HAL["S3L HAL"] --> IAV["IAV driver"]
  Config["ISP options"] --> Media
  ISP["Sensor ISP file"] --> HAL
  IAV --> AAA["3A feedback"] --> HAL
  Media --> Output["Streams and snapshots"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Link the vendor SDK
  • ➕ Would replace some explicit ioctl definitions and driver-structure handling with vendor APIs.
  • ➖ Introduces a vendor-library dependency and may constrain float-ABI compatibility and firmware packaging.

Recommendation: Keep the ioctl-only HAL: it fits the existing platform-backend model and the stated ARM ABI requirements. The trade-off is maintaining and validating driver-specific definitions and buffer handling.

Files changed (14) +1756 / -3

Enhancement (10) +1737 / -1
s3l_aaa.cImplement S3L exposure and white-balance feedback +272/-0

Implement S3L exposure and white-balance feedback

• Parses DSP statistics and uses VIN shutter and gain controls for automatic exposure. A worker also applies white balance, anti-flicker behavior, and ISP adjustments.

src/hal/amba/s3l_aaa.c

s3l_hal.cImplement the S3L IAV hardware backend +1149/-0

Implement the S3L IAV hardware backend

• Adds platform detection, sensor ISP loading, pipeline and encoder control, and encoded-frame delivery. It also handles JPEG snapshots, double-buffered overlays, driver state across runs, and 3A startup.

src/hal/amba/s3l_hal.c

s3l_hal.hDeclare S3L backend interfaces +61/-0

Declare S3L backend interfaces

• Exposes S3L pipeline, video, overlay, snapshot, and ISP-control functions to the shared media paths.

src/hal/amba/s3l_hal.h

s3l_iav.hDefine S3L driver protocol constants +131/-0

Define S3L driver protocol constants

• Adds IAV and VIN ioctl identifiers, buffer layouts, encoder constants, ISP file structures, and overlay definitions used by the backend.

src/hal/amba/s3l_iav.h

support.cDetect S3L during ARM platform identification +3/-0

Detect S3L during ARM platform identification

• Checks for the S3L backend before existing ARM platform probes.

src/hal/support.c

support.hExpose S3L detection to platform support +3/-0

Expose S3L detection to platform support

• Includes the S3L HAL interface for ARM builds, regardless of float ABI.

src/hal/support.h

types.hAdd the S3L platform identifier +1/-0

Add the S3L platform identifier

• Introduces HAL_PLATFORM_S3L for platform dispatch.

src/hal/types.h

jpeg.cSource S3L snapshots from encoder streams +17/-0

Source S3L snapshots from encoder streams

• Uses an active MJPEG stream for snapshots when available; otherwise creates and manages a JPEG encoder stream.

src/jpeg.c

media.cConnect S3L to media startup and stream control +85/-1

Connect S3L to media startup and stream control

• Wires S3L into pipeline lifecycle, encoder creation, callbacks, IDR requests, and grayscale control. It passes 3A settings to the HAL and disables unsupported audio rather than advertising an empty track.

src/media.c

region.cRoute OSD regions through the S3L overlay +15/-0

Route OSD regions through the S3L overlay

• Connects region creation, bitmap updates, and removal to the S3L HAL.

src/region.c

Documentation (2) +7 / -2
README.mdDocument S3L support and operational limits +2/-0

Document S3L support and operational limits

• Adds S3L to the platform matrix and explains its ISP, OSD, snapshot, and restart-dependent stream behavior.

README.md

config.mdDocument S3L sensor and 3A settings +5/-2

Document S3L sensor and 3A settings

• Explains sensor-specific ISP file fallback and the new exposure and white-balance options.

doc/config.md

Other (2) +12 / -0
app_config.cParse and persist automatic ISP options +9/-0

Parse and persist automatic ISP options

• Adds defaults, parsing, and saved configuration fields for auto exposure, exposure target, and auto white balance.

src/app_config.c

app_config.hStore automatic ISP settings +3/-0

Store automatic ISP settings

• Extends AppConfig with exposure and white-balance controls consumed by the S3L backend.

src/app_config.h

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Larger JPEG streams use the wrong size 🐞 Bug ≡ Correctness
Description
s3l_pipeline_create() disables the requested sub-buffer when it is at least as large as the
MP4-sized main buffer, and s3l_video_create() then programs the JPEG or MJPEG window at the
main-buffer size. With 1280×720 MP4 and 1920×1080 MJPEG, the MJPEG stream and snapshots encode at
1280×720; a larger standalone JPEG configuration follows the same path.
Code

src/hal/amba/s3l_hal.c[R481-482]

+    if (sized && (_s3l_sub_w >= _s3l_main_w || _s3l_sub_h >= _s3l_main_h))
+        _s3l_mjpeg_buf = 0;
Evidence
The integration passes MP4 dimensions as the main pipeline size when MP4 is enabled, while passing
MJPEG or JPEG dimensions as the sub size. A sub size at least as large as main is discarded, and the
MJPEG/JPEG encoder window is selected from the resulting main dimensions rather than its configured
dimensions.

src/media.c[1004-1022]
src/hal/amba/s3l_hal.c[479-504]
src/hal/amba/s3l_hal.c[634-662]
src/jpeg.c[38-48]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A JPEG or MJPEG size larger than the MP4 main size is silently replaced by the main-buffer size.
## Fix Focus Areas
- src/media.c[1013-1022]
- src/hal/amba/s3l_hal.c[479-524]
- src/hal/amba/s3l_hal.c[636-662]
## Recommended Fix
Size the physical main source buffer for the largest required stream when a requested sub-buffer cannot hold it, and independently program each encoder window for its configured dimensions. Preserve the smaller MP4 window when enlarging the shared buffer; reject unsupported combinations rather than silently changing output dimensions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Small JPEG sizes can stop video startup 🐞 Bug ≡ Correctness
Description
s3l_pipeline_create() applies its 240×240 minimum check only when the second buffer has no
explicitly requested size. A configured 160×120 JPEG or MJPEG stream therefore puts a 160×120 second
buffer into SET_SRCBUF_SETUP, despite the HAL's stated minimum, so the pipeline can fail to start
instead of handling that supported configuration.
Code

src/hal/amba/s3l_hal.c[R492-493]

+    if (!(_s3l_mjpeg_buf == 1 && sized) && (b1w < S3L_SRCBUF_MIN || b1h < S3L_SRCBUF_MIN))
+        b1w = b1h = 0;
Evidence
Configuration parsing permits 160×120 for JPEG and MJPEG. s3l_sub_size() retains that aligned
size, but the minimum-size branch excludes explicitly sized buffers; the dimensions are then copied
into the source-buffer setup sent to IAV.

src/app_config.c[574-611]
src/hal/amba/s3l_hal.c[418-424]
src/hal/amba/s3l_hal.c[479-495]
src/hal/amba/s3l_hal.c[551-564]
src/hal/amba/s3l_iav.h[63-66]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Configured JPEG and MJPEG dimensions below the S3L source-buffer minimum bypass the minimum-size check and reach the driver.
## Fix Focus Areas
- src/hal/amba/s3l_hal.c[479-495]
- src/hal/amba/s3l_hal.c[551-563]
## Recommended Fix
Apply the source-buffer minimum to explicitly requested sizes too. Choose a valid buffer and encoder window where supported, or fail with a clear size validation error before submitting an undersized source-buffer setup.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Exposure startup failures go unnoticed 🐞 Bug ☼ Reliability
Description
s3l_pipeline_create() discards the failure result from s3l_ae_start() and continues creating the
video pipeline. If thread creation fails, streaming can start without automatic exposure, white
balance, or the thread that flushes night-mode ISP changes.
Code

src/hal/amba/s3l_hal.c[590]

+    s3l_ae_start(_s3l_fd, _s3l_fps);
Evidence
s3l_ae_start() explicitly returns failure when pthread_create() fails. Its caller ignores that
result, although this thread also performs periodic s3l_isp_flush() calls.

src/hal/amba/s3l_hal.c[583-604]
src/hal/amba/s3l_aaa.c[254-263]
src/hal/amba/s3l_aaa.c[142-149]
src/hal/amba/s3l_aaa.c[227-249]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The pipeline reports success even when its AE and ISP-flush thread fails to start.
## Fix Focus Areas
- src/hal/amba/s3l_hal.c[583-604]
- src/hal/amba/s3l_aaa.c[254-263]
## Recommended Fix
Check `s3l_ae_start()` and fail pipeline creation if it fails. Roll back the already-started preview, statistics threads, mappings and socket on that failure path.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. White balance can use mismatched gains 🐞 Bug ≡ Correctness
Description
s3l_aaa_frame() publishes red gain, blue gain and sequence through separate volatile fields
without synchronizing with s3l_ae_thread(). When statistics arrive while the exposure thread reads
them, it can consume a sequence with gains from different frames and apply an inconsistent
white-balance update.
Code

src/hal/amba/s3l_aaa.c[R82-85]

+        _dsp_wr = f.gr[f.n_tiles / 2];
+        _dsp_wb = f.gb[f.n_tiles / 2];
+        _dsp_tiles = f.n_tiles;
+        _dsp_seq++;
Evidence
The statistics path writes the gains and increments the sequence separately; the exposure path tests
the sequence and reads both gains separately. The only shared-field qualifier is volatile, with no
lock or atomic snapshot across these operations.

src/hal/amba/s3l_aaa.c[64-85]
src/hal/amba/s3l_aaa.c[200-210]
src/hal/amba/s3l_hal.c[131-149]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The statistics and exposure threads exchange a multi-field white-balance result without synchronization.
## Fix Focus Areas
- src/hal/amba/s3l_aaa.c[64-85]
- src/hal/amba/s3l_aaa.c[200-210]
## Recommended Fix
Protect publication and retrieval of the gains, tile count and sequence with one mutex, or use a validated atomic snapshot protocol so each reader obtains values from one completed result.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View review recommended (1)
5. Saving settings turns the user's audio option off for good ✓ Resolved
Description
On S3L, media_start and media_audio_enable switch audio off for the current run by clearing
app_config.audio_enable, which is the same field app_config_save writes to divinus.yaml. If a
user later sends /api/cmd?save (for example after changing an unrelated setting), the file gets
audio: enable: false, so their audio choice is lost and stays off once a build with S3L audio
support is installed.

Grey Divider

Tip of the day
💡 Did you know, you can pick a display mode instead of tweaking every Display option by hand

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@DeathCamel58

Copy link
Copy Markdown
Contributor Author

Thanks for the review. I've force-pushed the series with fixes folded into the commits they belong to; every commit still builds. Retested on the IP4M-1026E with the SDK microcode:

  1. Larger JPEG/MJPEG uses the main size: that's intended. The main buffer is sized for the H.264/H.265 stream, and an MJPEG or JPEG that isn't smaller shares it at that size (the PR description says so). What was missing is any sign of it at runtime, so it now warns: 1920x1080 is not below the main buffer, encoding it at 1280x720.
  2. Small JPEG sizes stop startup: confirmed, and the real limit is stricter than the HAL's 240×240. IAV also refuses a sub-buffer scaled down from the main one by 6 or more (check_zoom_limit: ... max zoom out factor 6). A 160×120 MJPEG/JPEG on a 2688×1512 main buffer failed SET_SRCBUF_SETUP. A size below either limit is now scaled up at its aspect, with a warning: 160×120 is encoded at 464×344, and /image.jpg returns 464×344.
  3. AE thread start failure: left as is. It's logged with HAL_DANGER ("Starting the AE thread failed!"), so it isn't silent. If a thread can't be created, streaming with fixed exposure is more useful than no video.
  4. White balance from mismatched frames: fixed. The statistics thread and the AE thread now hand the gains, tile count and sequence over under a mutex, so both gains always come from one frame.
  5. Saving turns audio off: fixed. app_config.audio_requested keeps audio.enable as configured, and that's what app_config_save writes; the S3L code still clears audio_enable for the run. Tested: with audio: enable: true, /api/cmd?save keeps true. /api/audio?enable=false followed by a save writes false, and enable=true followed by a save writes true again (while the run itself stays without audio).

@DeathCamel58

Copy link
Copy Markdown
Contributor Author

I've force-pushed one change to the base commit, made for the review of OpenIPC/firmware#2793: the ISP configuration file is now relocatable (S3LISP03).

The file's table pointers used to carry the IMG buffer's absolute DSP address. If a mem= or carveout change ever moved the buffer, the pointers would go stale silently. Now the generator writes them as offsets into the buffer, with a relocation list, and s3l_img_load() does the following:

  • checks every relocation and every pointer against the file's own image range;
  • adds the address QUERY_BUF reports for the buffer.

The older S3LISP01/02 formats are dropped; nothing shipped them. doc/config.md is updated.

Tested on the IP4M-1026E: the relocated OV4689 file loads, and the picture and 3A come up as before. Every commit still builds. #2793 and this PR need to go in together, because the generator's output and this loader now use the same new format.

@openipc-ai

Copy link
Copy Markdown

Tried this branch (HEAD 08ea485) against the Ambarella S3L target and wanted to share two findings plus what worked. Everything below was run in emulation — the OpenIPC qemu-bench s3l machine with its amb-dsp device, booting the shipped openipc.s3l-nor-ultimate rootfs (vendor iav.ko/dsp.ko, kernel 3.10.104) — not on real S3L silicon, so take the kernel-side behaviour as "what the vendor iav.ko does under QEMU" rather than a hardware verdict. The QEMU core is a Cortex‑A9, same as real S3L.

TL;DR: with the right toolchain and one change to the preview bring‑up, divinus runs on S3L and serves a decodable H.264 + JPEG stream. Two things tripped it up:


1. build.sh has no S3L target, and the nearest armhf-musl toolchain miscompiles for the A9

There's no s3l/ambarella case in build.sh. The obvious stand‑in — ./build.sh armhf-musl (the toolchain.sigmastar-infinity6 toolchain) — builds and links fine, but the binary SIGILLs immediately at startup:

 Divinus (rev 08ea485) for ambarella
Chip ID: S3L
...
Error occured (4)! Quitting...

Error occured (4) is the SIGILL handler in main.c (signo 4). Cause: that toolchain pins the FPU to VFPv4 and emits fused‑MAC instructions —

$ readelf -A divinus | grep -E 'FP_arch|SIMD'
  Tag_FP_arch: VFPv4
  Tag_Advanced_SIMD_arch: NEONv1 with Fused-MAC

— but S3L's Cortex‑A9 is VFPv3/NEONv1 (no FMA), so the first FMA faults. Passing -mfpu=neon on OPT doesn't help; that toolchain's specs override it.

Fix: build with a VFPv3 hard‑float musl toolchain. OpenIPC ships exactly the right one, which is also what the S3L rootfs itself is built with:

https://github.com/openipc/firmware/releases/download/toolchain/toolchain.ambarella-s3l.tgz
# prefix arm-openipc-linux-musleabihf-  → Tag_FP_arch VFPv3, interp /lib/ld-musl-armhf.so.1
make -C src CC=<toolchain>/bin/arm-openipc-linux-musleabihf-gcc OPT="-Os -s"

Result is VFPv3/NEONv1 and matches the rootfs loader. Might be worth adding an s3l/amba case to build.sh that pulls toolchain.ambarella-s3l.


2. The 3A‑stats preview restart in s3l_hal.c is rejected (ENTER_IDLE from PREVIEW → EPERM)

With the VFPv3 build, divinus gets much further — detects the chip, reads the VIN, opens /dev/iav — then fails during bring‑up:

[s3l_iav] IAV state 0xff
[s3l_iav] VIN 2688x1512, main buffer 1920x1080
...
[s3l_iav] ENTER_IDLE failed: Not a tty
[media] Pipeline creation failed with 0x1!
[hal] Failed to start SDK!

(The IAV state 0xff is correct — that's IAV_STATE_INIT.) An ioctl() trace via LD_PRELOAD shows the failing call is the second ENTER_IDLE — the one in the 3A‑statistics workaround (s3l_hal.c, right after the first ENABLE_PREVIEW):

nr 0x10 GET_IAV_STATE   ret 0      (state = 0xff INIT)
nr 0x11 ENTER_IDLE      ret 0      first one, INIT -> IDLE   ✓
nr 0x15/0x30/0x18 ...    ret 0      VIN + QUERY_BUF
nr 0x12 ENABLE_PREVIEW  ret 0      IDLE -> PREVIEW           ✓
nr 0x60 x3 (IMG_UPDATE) ret 0
nr 0x11 ENTER_IDLE      ret -1 EPERM   second one, PREVIEW -> IDLE   ✗

So ENTER_IDLE from INIT is fine, but re‑entering IDLE from PREVIEW to let the 3A setup take effect is rejected by this iav.ko with EPERM. (Side note: the error print reads it as strerror(errno) = "Not a tty"/ENOTTY, but the ioctl's actual errno at return is EPERM — the message is a touch misleading.)

The restart only exists to make auto‑exposure statistics take effect, which is moot in emulation (the DSP produces synthetic frames, ISP tuning aside). Gating it out:

/* s3l_hal.c, after the first ENABLE_PREVIEW + s3l_aaa_config */
if (getenv("S3L_NO_PREVIEW_RESTART") == NULL) {
    if (s3l_ioctl(S3L_IOC_ENTER_IDLE, 0, "ENTER_IDLE") ||
        s3l_ioctl(S3L_IOC_ENABLE_PREVIEW, 0, "ENABLE_PREVIEW"))
        return EXIT_FAILURE;
    s3l_aaa_config(_s3l_fd);
}

…lets the SDK start and encode. I don't have real S3L hardware to say whether the PREVIEW→IDLE transition is accepted there (it may well be, and this is only a QEMU limitation) — flagging it mainly because (a) the error message points at the wrong errno, and (b) you may want a fallback when the re‑idle is refused rather than failing the whole pipeline.


What worked

With the VFPv3 toolchain + the restart gated out, divinus runs cleanly on the emulated S3L:

[media] SDK has started successfully!
  • mp4 H.264 1920×1088, SPS 67 42 c0 29 (Baseline L4.1) — pulled over RTSP (rtsp://.../stream0, auth off) and decoded clean with ffmpeg
  • jpeg baseline 1280×720 — decoded clean

Two small config notes for anyone else testing on S3L:

  • the emulated DSP does H.264 + MJPEG only (no H.265), so set mp4.codec: H264; and the default 3840×2160 is larger than the OV4689 source — 1920×1080 works;
  • a full divinus.yaml is needed (a trimmed one fails app_config_parse on a missing required key before it reaches any HAL code); /etc/sensors/<sensor>.bin being absent is non‑fatal (logs "image will stay black", which is fine in emulation).

Happy to share the exact QEMU boot recipe if useful.

@DeathCamel58

DeathCamel58 commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks for running it under qemu-bench, that's a useful second target. I've force-pushed the branch, rebased onto current master (now that #67–#70 are in), with these changes:

1. Toolchain. ./build.sh s3l (alias armhf-vfp3) now uses OpenIPC's toolchain.ambarella-s3l, and s3l is in the CI list in build.yml. Built with it, the binary is Tag_FP_arch: VFPv3 / NEONv1, with no fused multiply-adds. That's what I'd built with locally all along, from the same toolchain the S3L rootfs uses, which is why the SIGILL never showed up here.

2. The preview restart.

  • Fallback: if ENTER_IDLE from PREVIEW is refused, the HAL now warns ("Restarting preview for the 3A statistics was refused (…); they may stay stale until the next start") and carries on with preview running, instead of failing the pipeline.
  • On real hardware the transition is accepted. The HAL has done it on every start this week on an IP4M-1026E, with both the SDK microcode and the camera's own, so the refusal looks specific to the emulated DSP. That also means the new branch only runs when a driver refuses, so on hardware it's compile-tested only. I re-tested the normal path on the camera after the change: the restart is taken, no warning, H.264 2688×1512 over RTSP.
  • The misleading errno: this one wasn't S3L-specific. Divinus's HAL_* macros pick their colours with isatty(STDERR_FILENO) before the message's arguments are evaluated. With stderr redirected (your run), isatty() sets errno to ENOTTY, so every strerror(errno) in a HAL message reads "Not a tty". On a terminal the message is right, which is why I'd never seen it. Since it affects every HAL, the fix is a small separate PR: Keeping errno for HAL messages logged to a file or pipe #74. With it, your trace would have printed ENTER_IDLE failed: Operation not permitted.

3. Also in this push:

  • The S3L case's own MJPEG-quality line is gone: since Passing the MJPEG qfactor to the encoder #68, media_mjpeg_enable passes qfactor on for every platform. Checked on the camera: qfactor 30 gives about 8.2 KB snapshots, 90 about 26.8 KB.
  • The PR description is updated.

On your config notes: right, the real DSP does H.265 as well. mp4.codec: H265 at 2688×1512 is what I run, while the emulated one is limited to H.264/MJPEG. A configured size larger than the sensor is meant to be handled: the HAL clamps the main buffer to the VIN, so the template's 3840×2160 should come out as 2688×1512. If it failed under QEMU instead, I'd like to see that log. If the QEMU boot recipe is easy to share, I'd like to have it for checking future HAL changes.

wberube pushed a commit that referenced this pull request Nov 8, 2026
The HAL_* macros pick their colours with isatty(STDERR_FILENO) before a
message's arguments are evaluated. When stderr is not a terminal (a log
file, a pipe, a service manager), isatty() sets errno to ENOTTY, so every
message that reports strerror(errno) printed "Not a tty" (musl) or
"Inappropriate ioctl for device" (glibc) instead of the failure it was
about. The colour test now keeps errno.

Found while running the Ambarella S3L backend (#71) with its log
redirected: a refused ioctl that returned EPERM was reported as "Not a
tty". Tested with a HAL_DANGER(..., strerror(errno)) after errno = EPERM:
stderr to a file printed "Inappropriate ioctl for device" before and
"Operation not permitted" after; on a terminal both print the latter.
@openipc-ai

Copy link
Copy Markdown

Thanks — the rebase and the fallback both look right, and the isatty/errno root-cause explains the "Not a tty" perfectly.

Two things on the back of that: I took the PREVIEW→IDLE refusal back to the emulated DSP, and here's the boot recipe you asked for.

The re-idle is now accepted on the emulated DSP too

Your datapoint — that the restart is accepted on the IP4M-1026E with both microcodes — was the clue that this was an emulation gap, not a HAL bug. With loglevel=8, iav's iav_goto_idle from STATE_PREVIEW was making two waits that both timed out (2 s each) because the stand-in DSP never reported the transition:

dsp_set_enc_sub_mode(358): Entering DSP ENC sub mode from [1] to [0]
dsp_set_enc_sub_mode(363): [TIMEOUT] Enter sub mode event.
dsp_cmd: CMD_DSP_SUSPEND_OPERATION_MODE (0x1000003)
dsp_set_op_mode(413): [TIMEOUT] Enter mode event, current op_mode 1.
#iav_error# iav_disable_preview(1645): Failed to enter IDLE mode.

Reading the shipped dsp.ko back, the re-idle needs the DSP to report two things the stand-in wasn't: op mode dropping to IDLE after CMD_DSP_SUSPEND_OPERATION_MODE (iav leaves a mode with SUSPEND, not SET_OPERATION_MODE(IDLE)), and the VCAP setup mode dropping to IDLE after iav stops the capture timer with CMD_VCAP_TMR_MODE. Fixed in OpenIPC/qemu-bench#251 (merged).

So on the emulated S3L divinus now runs your unpatched bring-up — the normal restart path, no fallback — straight through to SDK has started successfully! and a decodable H.264 1920×1088 + MJPEG 1280×720 over RTSP. The restart completes in ~230 ms. The fallback you added is still the right thing for a driver that genuinely refuses it; it just won't trigger under qemu-bench any more.

(One unrelated thing I see on the emulated DSP, non-fatal: iav_query_yuvdesc returns EAGAIN and the HAL retries — the raw-YUV descriptor readback, not the encode path. That's an emulation limitation on my side, flagging only so you know it isn't your code if you ever see it in a trace.)

QEMU boot recipe

Everything is OpenIPC public artefacts; no real hardware.

1. Build the emulator (gives qemu-system-arm with the s3l machine + amb-dsp):

git clone https://github.com/OpenIPC/qemu-bench && cd qemu-bench
bash qemu/setup.sh          # clones QEMU 10.2.0, applies the SoC models, builds
./qemu-src/build/qemu-system-arm -M help | grep '^s3l'

2. Kernel + rootfs. zImage is the OpenIPC linux kernel for the ambarella-s3l branch (3.10.104); the rootfs is the shipped openipc.s3l-nor-ultimate image, which carries the vendor iav.ko/dsp.ko. Unpack the rootfs to a tree and drop your cross-built divinus + divinus.yaml into it.

3. Boot as an initramfs with a small /init that mounts, loads the modules, and runs divinus:

cat > root/init <<'INIT'
#!/bin/sh
mount -t proc proc /proc; mount -t sysfs sysfs /sys
mount -t devtmpfs dev /dev; mount -t tmpfs tmpfs /tmp; mount -t tmpfs tmpfs /var
mkdir -p /var/run /var/log /etc/sensors
ifconfig eth0 10.0.2.15 netmask 255.255.255.0 up; route add default gw 10.0.2.2
load_ambarella -i            # insmods iav.ko/dsp.ko, creates /dev/iav
divinus
INIT
chmod +x root/init
(cd root && find . | cpio -o -H newc | gzip -9) > rd.gz

./qemu-src/build/qemu-system-arm \
    -M s3l,dsp=on,sensor=ov4689 -m 256M -nographic -monitor none \
    -kernel zImage -initrd rd.gz \
    -global amb-dsp.dump-dir=/tmp/frames \
    -nic user,model=xm-dwmac,hostfwd=tcp::5600-:554 \
    -append "console=ttyS0,115200 rdinit=/init loglevel=4 s3l.env_sensor"
  • dsp=on enables the amb-dsp encode model; sensor=ov4689 matches the rootfs.
  • -global amb-dsp.dump-dir=DIR writes every encoded frame host-side as frame-s<stream>-<N>.{h264,jpg} — handy for ffprobe without touching the network.
  • hostfwd=tcp::5600-:554 forwards RTSP to the host: ffmpeg -rtsp_transport tcp -i rtsp://127.0.0.1:5600/stream0 ....
  • For a HAL-level trace, LD_PRELOAD a tiny shim that logs ioctl() on /dev/iav, or boot with loglevel=8 to get iav/dsp's own debug (that's how the two findings above surfaced).

Config notes for S3L under QEMU: the emulated DSP does H.264 + MJPEG only (no H.265), so mp4.codec: H264; a full divinus.yaml is needed (a trimmed one fails app_config_parse); /etc/sensors/<sensor>.bin being absent is non-fatal ("image will stay black", fine in emulation).

Happy to help wire this into a CI check if that's useful on your side.

A HAL for the Ambarella S3L (S3LM) over its IAV driver (/dev/iav), for
ARM with either float ABI: it drives the driver through ioctls and links
no vendor library. It is picked when the IAV
driver reports an S3L (AMBARELLA_CHIP in /proc/ambarella/iav).

- VIN mode and frame rate, mirror/flip through VIN SET_MIRROR, the
  source buffers, preview, and one H.264 stream read from the bitstream
  buffer (RTSP, MP4). Encoder settings are the driver's defaults for
  now. MJPEG is skipped on S3L.
- ISP configuration: system.sensor_config names an S3LISP03 file
  (made from options, with no vendor data, by the generator OpenIPC's
  ambarella-osdrv-s3l package carries in src/isp), loaded
  into the IMG buffer before the first ENABLE_PREVIEW and applied with
  the IMG 'p' set ioctls. Without it, the one for the sensor the VIN
  reports is loaded, /etc/sensors/<sensor>.bin, as OpenIPC's S3L package
  generates them.
  Its table pointers are IMG-buffer offsets with a relocation list, and
  the HAL adds the buffer's address as the driver reports it, so the
  file does not depend on where the buffer is.
  The file's sizes and offsets are checked against the IMG buffer, and
  against the blocks the HAL keeps below the command rings, before
  anything is copied or applied. The IMG command rings are zeroed
  first: after a cold boot DRAM holds garbage there that the DSP would
  otherwise read as commands.
- 3A in Divinus: DSP statistics (CONFIG_STATISTICS / GET_STATISTICS,
  two slices per frame stitched), auto exposure through the VIN
  AGC/shutter ioctls every Ambarella sensor driver implements, with
  anti-flicker, auto white balance from the per-tile raw G/R, G/B
  median (handed to the AE thread under a lock, so both gains come
  from one frame), and bad-pixel thresholds that follow the gain. New isp
  options auto_exposure, exposure_target and auto_white_balance.
- The DSP takes the 3A statistics setup into effect only when preview
  starts, and the setup can only be sent once preview runs. In the
  first preview after the microcode is loaded, the SDK's 2018/10/28
  284712 (which OpenIPC ships) sends no statistics, and this camera's
  own 267837 sends stale ones: the same three buffers over and over. So
  the setup is sent, left in place for a frame, and preview started
  again before any stream encodes, on every start (about 0.15 s). A
  driver that refuses to go back to idle (an emulated DSP does) leaves
  preview running, and the HAL carries on with a warning.
- Grayscale for night mode, by swapping the chroma-scale curve.
- The sensor name for /api/status, from the VIN device info.
- build.sh s3l (and CI): OpenIPC's toolchain.ambarella-s3l. The S3L is a
  Cortex-A9 with VFPv3; the armhf-musl toolchain emits VFPv4 fused
  multiply-adds, which fault here.
- No audio yet: with audio enabled, Divinus warns and runs without it.
  Saving the configuration keeps audio.enable as it was set, so the
  choice is not lost (app_config.audio_requested).
- The main source buffer is sized for the H.264/H.265 stream, and the
  VIN runs at the fastest enabled stream's frame rate.
- IAV state changes (idle, preview, encode start/stop) run with signals
  blocked: the driver waits for the DSP interruptibly, and a signal in
  that window (a service stop repeating SIGTERM) left IAV stuck,
  refusing every later start until a reboot.

Tested on an Amcrest IP4M-1026E (Dahua IPC-HFW1430SN, S3L + OV4689):
H.264 2688x1512 over RTSP from a cold boot, AE/AWB, night-mode
grayscale, stop/start/restart cycles, with both the SDK microcode
(284712) and the camera's own (267837). In the first preview after boot,
the 3A statistics now change from frame to frame with both (with 267837
they had cycled through three fixed buffers). Builds for all build.yml
targets, s3l among them (VFPv3/NEONv1).
MJPEG was skipped on S3L. It now runs as its own IAV stream (MJPEG
stream type) from the second source buffer, sized to mjpeg width/height
(s3l_sub_size(), called before the pipeline is created), with the
quality from mjpeg.qfactor (which media_mjpeg_enable passes on for
every platform since OpenIPC#68). When MJPEG is the only
stream, the main buffer already has its size and a sub-buffer cannot
match it, so MJPEG encodes from the main buffer instead. The frame
thread routes each frame by the stream id in its descriptor and only
splits NAL units for H.264.

Tested on the IP4M-1026E: H.264 2688x1512 over RTSP plus 640x360 MJPEG
at 20 fps, qfactor 30 and 90 giving the matching JPEG quantisation
tables (8.8 KB and 27 KB frames); MJPEG-only at 1280x720.
The mp4 bitrate, mode, GOP and profile were ignored: the encoder ran on
the driver defaults (8 Mbit/s smart CBR, GOP 30, Main). Before encoding
starts the H.264 config now takes the GOP length (an IDR every GOP) and
the profile, and the bitrate stream config the average bitrate in bit/s
with CBR or VBR, read-modify-write so the driver's QP limits stay. Other
modes keep the driver's smart CBR, with a warning.

request_idr() now forces an IDR through the stream config, so a new
RTSP client no longer waits for the next GOP.

The S3L's IAV encodes Main or High only: Baseline is encoded as Main,
with a warning.

Tested on the IP4M-1026E: 4096 kbit/s CBR, GOP 40, High measures about
4.2 Mbit/s with keyframes exactly every 40 frames; 2048 kbit/s VBR
measures 2.2 Mbit/s; a second RTSP client connecting mid-stream puts an
IDR off the GOP grid in the first client's stream. Profiles 1 and 2 come
out as Main and High; profile 0 comes out as Main.
The second source buffer is limited to 720x720 (IAV: "cannot be larger
than 720x720"), so an MJPEG stream of 1280x720 or more could not start.
Sizes above that now use the third source buffer, which takes up to the
sensor size. Source buffers that are not used are switched off rather
than left as the previous run configured them; IAV keeps that setup, and
a stale third buffer larger than the main one made SET_SRCBUF_SETUP fail.

Tested on the IP4M-1026E with H.264 2688x1512: MJPEG 640x360 (second
buffer), 1280x720 and 1920x1080 (third buffer) at 20 fps, and
MJPEG-only at 1280x720 right after a 1920x1080 run.
/image.jpg (and the ONVIF snapshot URI) did not work on S3L. Snapshots
now follow T31: with MJPEG on they reuse the MJPEG stream at its
resolution; otherwise jpeg_init() opens its own encoder stream, an
MJPEG-type IAV stream at jpeg width/height and qfactor running at about
1 fps. A snapshot waits for that stream's next frame, which the frame
thread copies only while a snapshot is pending; frames of the snapshot
stream are not passed on as video.

Every stream now also sets its frame-rate divider explicitly, since IAV
keeps per-stream settings across runs and a stream left at the
snapshot rate would otherwise stay at 1 fps.

Tested on the IP4M-1026E with H.264 2688x1512 alongside: own snapshot
stream at 1280x720 / q85 and 1920x1080 (5 of 5 snapshots, about 0.8 s
each); with MJPEG 640x360 on, snapshots from it (0.1 s each) and MJPEG
at 20 fps; MJPEG back at 20 fps after a snapshot-stream run.
mp4.codec H265 was refused on S3L. The encoder supports it: it now runs
as an IAV H.265 stream, configured like H.264 through the H.265 config
ioctl (GOP length, an IDR every GOP; the driver's Main profile) and the
H.265 stream configs (bitrate with CBR/VBR, forced IDR), which mirror the
H.264 ones at +0x2000.

IAV hands an H.265 frame over one slice at a time (three slices at
2688x1512, each with its own descriptor carrying the slice index and
count). The frame thread collects a frame's slices, starting only at
slice 0, and passes the whole frame on with one timestamp and its
H.265 NAL types.

Tested on the IP4M-1026E: 2688x1512 H.265 Main over RTSP at 20 fps,
4096 kbit/s CBR measuring about 4.1 Mbit/s, keyframes exactly every 40
frames, a forced IDR when a second client connects, recordings that
decode without errors, /video.mp4, alongside MJPEG, and restarts.
H.264 unchanged.
Frames were stamped with the time the frame thread read their
descriptor. After a large keyframe the next frames wait in IAV's queue
while the keyframe is sent, so they were stamped late and then in a
burst (steps of 9963, 2033, 1466 instead of 4500 at 20 fps); recordings
logged "non monotonically increasing dts" around every keyframe and
probing guessed 60 fps. Frames now carry the 90 kHz PTS from their
descriptor, which the RTP layer expects ("the encoder's capture time");
the read time stays as the fallback when a descriptor has none.

Tested on the IP4M-1026E: H.264 and H.265 RTSP recordings step exactly
4500 per frame at 20 fps with no ffmpeg warnings (5-8 per 10 s
before), and /video.mp4 reports 20 fps; MJPEG and snapshots unchanged.
Regions were not drawn on S3L. They now use IAV's encode overlay: up to
10 areas per stream (one per OSD region), 8-bit indexed pixels with a
256-entry CLUT of V, U, Y and alpha, in the overlay buffer (QUERY_BUF 4).
The S3L driver's overlay insert is 248 bytes, with 10 areas against the
S2L SDK's 4.

Each ARGB1555 bitmap becomes indexed colour: index 0 is transparent,
opaque colours get BT.601 limited-range entries at the region's opacity,
and an image with more than 255 colours falls back to 3-3-2. Every region
has two pages, so an update writes the idle one and the DSP never reads a
half-written bitmap. Areas follow the driver's rules (even x and width,
y and height multiples of 4) and are clipped to the stream. The regions
are drawn on the H.264/H.265 stream and put back whenever it starts.

IAV keeps overlays across runs, and an area that does not fit a stream
stops it from starting, so every stream's overlay is cleared when preview
starts.

Tested on the IP4M-1026E at 2688x1512: a timestamp updating every
second, text with an outline at half opacity, a PNG with transparent
parts and over 255 colours, regions clipped at the right and bottom
edges, an off-frame one skipped, odd positions snapped, a region removed,
and the overlay kept across an encoder restart from /api/mp4. Builds for
all 12 targets in build.yml.
An H.264/H.265 stream could only be the main one, from the main
source buffer. Now the HAL allows three encode streams, and one smaller
than the main picture is encoded from the source buffer of its size:
the second one, or the third when MJPEG put its own size there.

The second buffer is limited to 720x720 and its size is fixed when
preview starts, so it is planned then. MJPEG or the snapshots keep it at
their size when they are at most 720x720; otherwise, or when neither is
on, it is 640 wide at the main aspect (640x360 for 2688x1512), so a
640x360 sub-stream, the size the RTSP sub stream of OpenIPC#62
asks for, finds it. A size no buffer has is refused with the sizes there
are (media.c then stops Divinus starting, as for any stream it cannot
create).

The on-screen display stays on the stream from the main buffer, and the
frame thread now watches all three streams.

Divinus itself creates no second H.264/H.265 stream yet: that comes with
the sub-stream plumbing of OpenIPC#62. Until then, this path is
taken only when a streamer creates one.

Tested on the IP4M-1026E, with a local hook in media.c creating a
640x360 H.264 sub-stream next to the 2688x1512 main stream (main at 20
fps throughout): alone; with MJPEG at 1280x720 (three streams) and at
640x360 (sharing the buffer); with 1 fps JPEG snapshots at 1920x1080;
and with MJPEG at 640x480, where the sub-stream is refused and, with the
hook, the rest runs. Without a sub-stream, the board's default (main
stream, OSD) runs as before. Builds for all 12 targets in build.yml.
IAV refuses to start a stream when all streams together exceed the
DSP's encoder load, in macroblocks per second (570600 on S3L, "1080p60 +
480p60"). Each stream's frame rate was set only after it started, at the
VIN's, so the check counted every stream at the full rate: MJPEG at the
main size (or the yaml template's 3840x2160, which falls back to it) beside
the 2688x1512 main stream never started.

The frame rate is now set before the start, as the configured fraction
of the VIN's (snapshots 1 fps as before). The HAL adds up the streams'
load first: MJPEG and the snapshots that would not fit run at the highest
frame rate that does, with a warning (and are refused if none does);
an H.264/H.265 stream that would not fit is refused with the numbers,
which stops Divinus starting. A stream asking for more than the
rate the pipeline started with runs at that rate, with a warning. A
stream is enabled before its encoder starts, so its first frame (the
IDR) is not dropped.

Tested on the IP4M-1026E: MJPEG at 3840x2160 and 20 fps runs at
2688x1512 and 15 fps next to the main stream at 20 fps (it did not start
before); MJPEG at 1920x1080 and 15 fps gives exactly 15 fps; with a
640x360 sub-stream and full-size MJPEG, MJPEG drops to 14 fps; the
board's default is unchanged. The refusal of an H.264/H.265 stream was
not hit, as no case here adds one after the budget is used. Builds for
all 12 targets in build.yml.
The main source buffer scales the VIN down by less than 4, and buffers
and encode streams are a multiple of 16 wide and of 8 high. A main stream
outside that, such as 640x360 or 960x540 on a 2688x1512 sensor, failed
SET_SRCBUF_SETUP and Divinus exited without video.

A size four or more times below the VIN is now encoded as a centred window
of the smallest main buffer there can be (688x384 here), so only the edges
are cropped, and sizes off the 16x8 grid are rounded up with a warning
(960x540 is encoded as 960x544); the requested size is matched against the
buffers the same way. The second source buffer, at least 240x240 in this
mode, is left off when it would be smaller, and MJPEG or the snapshots use
the main buffer. Its type is now set on every start, as for the third and
fourth: IAV keeps it across runs, so a run with it off left the next one
unable to encode from it. An MJPEG or JPEG size too small for a
sub-buffer (at least 240x240, and scaled down from the main buffer by less
than 6, IAV's check_zoom_limit) is scaled up at its aspect with a warning,
and one not below the main buffer warns that it is encoded at the main
buffer's size. The README notes that a stream's size, or a
higher frame rate, takes a restart of Divinus to change.

Tested on the IP4M-1026E: main streams of 640x360 (exited before),
960x540 (encoded 960x544), 656x370 (a 656x376 window of a 688x384 buffer),
1280x720 and 2688x1512; MJPEG-only 960x540; MJPEG at 640x360 on the second
buffer before and after a run with it off; MJPEG and JPEG at 160x120
(SET_SRCBUF_SETUP failed before; now 464x344 with a warning), and MJPEG at
1920x1080 beside a 1280x720 main stream (warns, encodes 1280x720).
@DeathCamel58

Copy link
Copy Markdown
Contributor Author

Thanks for chasing the re-idle down in the DSP model, and for the recipe. I've saved it, and I'll use it to check HAL changes before they reach a camera. A CI check built on it would be welcome; let's look at that once this PR is in.

Your note that a missing /etc/sensors/<sensor>.bin is non-fatal under QEMU made me check the same thing on the IP4M-1026E, and on the real DSP it isn't. With no ISP configuration, the DSP produces no frames at all. The YUV descriptor queries and the 3A statistics time out, and START_ENCODE waits and gives up. Then the preview restart times out the same way it did under emulation ("Enter sub mode event", then "Enter mode event, current op_mode 254"). That left IAV in state 7 (exiting preview), and every later start failed until a reboot. So the old warning, "the image will stay black", was wrong on hardware.

I've force-pushed the branch, still on the same base. A missing or rejected ISP configuration is now an error ("No ISP configuration loaded (…); the DSP produces no image without one"), raised before preview starts, so the DSP stays idle. Tested on the camera without a reboot in between: no file and an old-format file each give that error and leave IAV idle, and a valid file straight after starts normally and streams. doc/config.md now says the file is required on S3L.

Under qemu-bench this means the image needs an ISP file. Generating it is one command, with the generator and preset from OpenIPC/firmware (general/package/legacy/ambarella-osdrv-s3l/src/isp/):

python3 s3l_isp_gen.py root/etc/sensors/ov4689.bin --preset ov4689.yaml

Builds with BR2_PACKAGE_DIVINUS install it as /etc/sensors/ov4689.bin already.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants