Skip to content

media: apple: avd: frame queue, queued open-firmware transport, picture overlap - #713

Merged
RyanTheTide merged 48 commits into
aurora-wipfrom
avd/rewrite-integ
Nov 1, 2026
Merged

RyanTheTide merged 48 commits into
aurora-wipfrom
avd/rewrite-integ

Conversation

@RyanTheTide

@RyanTheTide RyanTheTide commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

AVD frame-queue rewrite, integration branch. It carries the queue core and PM, a queued transport for the open firmware, H.264/HEVC/VP9 picture overlap, and fixes for the timeouts seen on the J616s. Based on aurora-wip (f41ac3d merged in, cbbe442), so it carries the PMC SOC floor consumer from #773. The current hardware build is k4m (a1b7685).

J616s (M4 Pro), rewrite + PMC floor: H.264 1080p 1094 fps, against 334 on the stock driver and 2582 on macOS. Every frame was bit-exact. The full tables are under Hardware results.

What is in it

  1. Frame-queue core and PM (avd/rewrite-core media: apple: avd: introduce frame slots and backend transport foundation #701, avd/power-idle media: apple: avd: runtime and system PM for the frame queue #707): 16 frame slots with tags, in-order retire per context, per-frame watchdog, queue reset, runtime/system PM, DART empty-domain recovery. Apple MCPU backend present but disabled until its register and reply facts are verified.
  2. Queued transport for the open firmware (needs aurora-silicon/avd-fw HID: haptics for the Apple SPI trackpad #4, queue ABI 2):
    • The host streams each frame's instruction program into a 0x1800-word ring in CM3 SRAM (+0x0). Firmware publishes its read count at info+0x38, the host its write count at info+0x3c, so programs of any length stream through.
    • A 64-byte descriptor per frame (tag, codec, VP, ring position, word count, PP kick, continuation kicks for multi-segment HEVC/VP9/AV1) is posted with a doorbell on mailbox 0 (+0x54). Firmware feeds the VP FIFO, starts PP itself in submission order, and posts a tagged completion 0x5453xxxx on mailbox 1.
    • No PIODMA, so the second DART stream that the current device trees lack is not needed.
    • Chosen after every firmware boot from the image's info block (ABI 2, SRAM ring feature, SRAM aperture matching the node); anything else keeps the legacy transport. The firmware boots once at probe so the transport and codec list are known before clients look.
    • Each frame in flight owns one hardware instruction FIFO (its slot index); in flight is capped at the FIFO count (15, or 7 on T8103/T8140).
    • T6040 now loads avd-fw-v5-t2.bin (its SRAM aperture), falling back to v5-t1. T6040 HEVC is listed when the queue image's codec mask has it.
  3. Picture overlap:
    • H.264 and HEVC keep two banks per stream: VP to PP FIFO, pipe state, row/column scratch and H.264 slice copies. A picture records its bank in its CAPTURE buffer and frees it at retire, so the VP of picture N+1 runs while the PP of picture N is busy.
    • VP9 also uses two banks. An adaptive frame's probability dependency is released at its VP DONE, after CPU backward adaptation of its counts. Non-refreshing, frame-parallel and error-resilient frames have no dependency.
    • VP9 overlap needs the optional tagged VP DONE event (queue feature bit 2, avd-fw: queue ABI 2, SRAM ring programs and multi-kick PP avd-fw#4 bf49a31b). Firmware without it keeps VP9 at one picture per stream.
    • AV1 stays one picture per stream: CDF and segmentation-tail visibility at VP DONE is not proven yet.
    • Separate streams overlap for every codec.
    • Multi-VP H.264 (VP4..7) is on avd/rewrite-integ-mvp2 until it is shown bit-exact.
    • The v4l2-mem2mem core gains v4l2_m2m_job_finish_detach(). With it, a job can leave the m2m queue while its CAPTURE buffer is held (HOLD_CAPTURE_BUF) without the job_finish WARN.
  4. Fixes found on hardware during the night (each is a separate commit):
    • IRQ/reset deadlock: a queue reset held queue_mutex and waited in disable_irq() for the threaded handler, which waited for queue_mutex.
    • The legacy shim dropped a VP DONE with no frame in flight, so that picture's PP never started. PP is now kicked for every VP DONE.
    • Empty-mailbox re-read: a repeated PP DONE completed the next frame early. MBOX1_STATUS bit 17 is now checked first.
    • Error storm: a stream that fails in hardware is stopped. Recoveries are spaced and bounded, and a failed recovery retires the decoder.
    • FFmpeg's HEVC slice layout (absolute data_byte_offset, data-only bit_size) is rewritten to the NAL-relative form. This was the cause of the "H0 error" storm.
    • Multi-context dispatch: a single-context reservation in job_ready starved later streams for seconds. Every context now enters the m2m queue, and jobs without a slot are deferred.
    • Hang hardening after three hard hangs on the J616s:
      • recovery stops the CM3, blocks DMA at the DART and waits 5 ms before the pmgr reset;
      • at most two recoveries a minute, then the decoder is retired until reload;
      • a PP DONE only counts after this frame's VP DONE and PP kick;
      • legacy VP writes need verified queue room;
      • no decoder MMIO with interrupts off.
    • Every recovery step is logged before it runs.
  5. Small: VP queue pacing polls the level instead of sleeping a fixed time; queue host test builds again.

Not in this PR

The current driver got the same safety fixes, without the rewrite, in #725 and #758 (both merged).

Codec frame_ops branches (#700, #702, #703, #704) and the Apple MCPU backend activation. The legacy codec builders feed both transports.

Tests

  • Module pair built as external modules against fleet14c and fleet14d, ARCH=arm64 LLVM=1 W=1: 0 warnings.
  • run-host-tests 158/158 on the branch, with the avd-codec-tail test moved to the rewrite.
  • tests/run-host.sh PASS. VP9 dependency/bank tests run under ASan/UBSan. Legacy VP9 programs and probability contexts are byte-identical to the previous revision.
  • avd-fw HID: haptics for the Apple SPI trackpad #4: make test, plus a unicorn run of the real CM3 code on all seven images: SRAM ring, wrap, streaming, multi-kick, stray PP DONE, in-order PP, and VP(1) issued before PP(0) DONE.
  • KUnit on arm64 (QEMU): tests/.kunitconfig and avd-pm.kunitconfig 38/38.

Hardware results

Method:

  • Tool: avd-bench, with the stream demuxed into memory so file I/O is not measured.
  • Correctness: every decoded frame's MD5 is compared with an FFmpeg software decode of the same file, and a row only counts if all frames match.
  • Each figure is the best of 3 passes.
  • Clips are synthetic 1080p and 4K streams per codec, one stream (N=1) unless stated. Qn = frames queued per stream.
  • The rewrite is loaded by module swap with no reboot, with strict DART translation for AVD (AVD: safety and recovery (park DMA on retire, rest instead of retire, strict DART flushing for media/ML engines) #758).
  • Raw JSON, MD5 tables and kernel logs are in the lead's report tree (engines-20261101/avd/{j616s-k4m,m2-k4k}).

J616s (T6040), fleet14c with the PMC SOC floor (#773), k4m, 2026-11-01 18:31. Zero DART or AVD kernel lines during the run.

fps Rewrite + floor (Q16 unless noted) Stock + floor (Q4), same boot Stock, no floor macOS Rewrite vs macOS
H.264 1080p 1094 (Q4); 630 Q1, 1087 Q16 692 334 2582 42 %
H.264 4K 414 248 117 681 61 %
HEVC 1080p 2135 hidden by stock fw hidden 2889 74 %
HEVC 4K 699 hidden hidden 740 94 %
VP9 1080p 878 548 248 897 98 %
VP9 4K 244 183 82 249 98 %
AV1 1080p 1047 1105 533 2395 44 %
AV1 4K 409 419 199 739 55 %
N=4 aggregate H.264 1088, HEVC 2505

The macOS figures are the 10-31 baseline on the same J616s: VideoToolbox decode with in-memory demux and asynchronous decode, the same shape as avd-bench. HEVC is new on the J616s: the stock firmware does not list it. AV1 is serialized on the rewrite, so it matches the stock path with the floor and gains nothing more.

M2 (T8112), fleet14, k4k (9b125f8), 2026-11-01 16:00, no PMC floor on this SoC. Zero kernel lines.

fps Rewrite Stock
H.264 1080p / 4K 748 / 319 504 / 190
HEVC 1080p / 4K 1523 / 489 847 / 336
VP9 1080p / 4K (before VP9 overlap) 363 / 123 369 / 123

M2 (T8112), fleet14d, k4m (this branch's driver source), 2026-11-01 19:01. Bench rows were bit-exact with zero kernel lines.

fps k4m k4k (no VP9 overlap) Stock
VP9 1080p Q16 / Q1 587 / 345 363 369
VP9 4K 163 123 123
VP9 1080p N=4 aggregate 635
H.264 1080p / 4K 748 / 319 748 / 319 504 / 190
HEVC 1080p / 4K 1527 / 490 1523 / 489 847 / 336

Fluster conformance, M2 fleet14d (rewrite k4m) vs the stock in-tree driver (fleet14b, #725 + #758). conf.py feeds every vector through avd-bench and compares its MD5 with the Fluster reference.

Suite Rewrite Stock Upstream reference
JVT-AVC_V1 (135) 62 pass, 52 hw-unsupported (field coding) 62 77
JCT-VC-HEVC_V1 (147) 140 140 145
VP9-TEST-VECTORS (305) 216 (2 tool-unsupported: 4:2:2/4:4:4) 216 216

The per-vector results are the same as stock: no new failures and no timeouts.

The decoder rejected some unsupported H.264 vectors with a hardware error. It recovered and was reloaded 9 times, as on stock. Every DART fault in the run is an in-flight DMA hitting the deliberately empty domain while a recovery has DMA blocked, between "recover: block DMA" and "recover: reset". None fall outside a recovery, there were no hangs, and the machine stayed up.

The H.264 fixes on avd/conformance-fixes (68/135 on the stock transport) follow as a separate series.

Not run yet on the rewrite: the M1 Pro and the Neo.

How to run it:

# on the target, as root, with a kit (matched apple-avd.ko + v4l2-mem2mem.ko + fw/) in $K
rmmod apple_avd && rmmod v4l2_mem2mem      # apple_ave also holds v4l2_mem2mem on T6040: rmmod it first, modprobe it after
echo -n $K/fw > /sys/module/firmware_class/parameters/path
insmod $K/v4l2-mem2mem.ko && insmod $K/apple-avd.ko
dmesg | grep -i avd            # "using open-queue transport (15 frames in flight)"
python3 /tmp/avd-bench/bench-table.py run --machine <m> --kit /tmp/mediakit \
  --bench /tmp/avd-bench/avd-bench --video /dev/videoN --media /dev/mediaN --out /tmp/avd-results --passes 6
# rollback: rmmod apple_avd v4l2_mem2mem; echo > /sys/module/firmware_class/parameters/path; modprobe apple_avd

🤖 Generated with Claude Code

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Keep the legacy open firmware serialized while moving program storage and completion ownership into tagged frame slots. Leave Borage activation dependent on the register and completion facts absent from the clean specification.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Add deterministic host tests against production helpers and KUnit coverage of slot reuse, ordered retirement, watchdog wrap, inbox retry, command builders and completion parsing. Track the unproved Borage fields explicitly and preserve session generations over firmware reboot.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Remove contexts from the scheduling list before releasing their m2m state, reject already-scheduled jobs during teardown, and keep Apple fp pointers absent on the open backend. Preserve tagged slots over inbox-full retries and avoid periodic watchdog wakeups when only submitted frames remain.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Keep the CODECS trailer intersection with the SoC capability table when selecting the open backend. Bound firmware copies by the mapped code resource.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Only the H.264 compatibility builder explicitly completes held slice requests during preparation. Continue submitting legacy HEVC requests that retain their capture buffer.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
…ation

Prevent queued contexts from observing the same free capacity. Run preparation on a context work item because m2m try_schedule can invoke device_run synchronously while the queue mutex is held. Drain that work before freeing the context.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Keep power through slot retirement and context drains, restore firmware before retrying reserved frames, and defer DMA releases while the DART is inactive. Re-enable a retained Apple MCPU image before mailbox link setup, without uploading it again. Pause and drain the queue for system sleep and final teardown.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Request the mailbox IRQ without automatic enable, open it only after successful firmware boot, and synchronize and close it after backend suspend. Close the host IRQ on teardown even if the final PM transition fails.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Map only DT-declared PIODMA and SRAM resources and replay the shifted SRAM base before every open CM3 boot. Keep reset, empty-domain attach/detach and firmware reboot ordered; refuse to reboot if the DMA domain cannot be restored. Keep host IRQs closed until old slots are invalidated. Add synthetic-RAM boot replay and recovery-gating KUnit coverage. Queued PIODMA use still requires the DT to describe the DMA stream as well as decode.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Replay PIODMA setup in the common boot dispatcher so queued open transports inherit it even with their own mailbox boot implementation. Keep firmware generation handling and initial-only backend fallback in the same dispatcher, and test the real open boot path through it.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Brings in the frame-queue core (avd/rewrite-core) and its runtime and
system PM, PIODMA replay and DART recovery (avd/power-idle).

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Each frame in VP to PP flight owns one hardware instruction FIFO, named
by EXEC bits 7:4. The hardware has 15 of them on v3 and later tier 1
parts and 7 on T8103 and T8140, so the 16-slot queue must not keep more
frames in flight than the variant has FIFOs.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
T6040 decodes HEVC with the v5 header. Add HEVC to its capabilities but
keep it hidden unless the loaded open firmware lists h265 in its CODECS
trailer, so older firmware without the trailer behaves as before.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
When the VP instruction queue is at least half full the open transport
slept a fixed 500-650 us (100-150 us on parts with v5 pacing) before the
next segment. Poll the queue level every 10 us instead and continue as
soon as it drops below half, keeping the old sleep as the upper bound.
The submission order and queue thresholds do not change.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The open transport writes every instruction word to the VP FIFO register
from the CPU, sleeps while the FIFO is half full, and kicks the PP from
the VP-done interrupt thread, one frame at a time.

avd-fw queue ABI 2 moves all of that into the CM3. The host streams each
frame program into a word ring in CM3 SRAM, posts a 64-byte descriptor
and rings mailbox 0; the firmware feeds the VP FIFO, writes the PP kick
(plus one continuation kick per extra segment for HEVC, VP9 and AV1)
when the VP is done, and posts a tagged completion on mailbox 1. No
PIODMA is used, so the second DART stream that the current device trees
lack is not needed.

The transport is chosen after every firmware boot from the image info
block: ABI 2 with the SRAM ring, and an SRAM aperture matching the node.
Anything else keeps the legacy transport. Each frame in flight gets its
own instruction FIFO (the slot index), and the queue keeps at most the
FIFO count in flight. Frames of one context stay serialized by the
queue core, so different streams overlap; within a stream the gain is
the removed CPU word writes, pacing sleeps and interrupt round trip.

T6040 now loads avd-fw-v5-t2, whose SRAM aperture matches the node, and
falls back to v5-t1 when that image is not installed.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
CM3 SRAM keeps its contents across a firmware reload. After an ABI 2
image ran, a later boot of an older image would still show the queue
info magic and the driver would pick the queued transport for firmware
that does not speak it. Clear the magic before every open-firmware boot
so only an image that publishes it again selects queue mode.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
With the firmware chaining VP to PP, the VP of picture N+1 can run while
the PP of picture N is still busy, but the legacy H.264 builder kept one
set of scratch buffers, one VP to PP instruction FIFO and one slice table
per context, and the queue core held the m2m scheduler until a picture
retired.

Give H.264 two banks of everything the hardware touches while decoding a
picture (instruction FIFO, pipe state, above, loop-filter and MV row
buffers) and of the slice copies. A new picture takes a free bank and
records it in its CAPTURE buffer; retirement frees that bank. Pictures
whose last slice never arrived restart in their own bank. The legacy
transport keeps one bank, so nothing changes there.

The queue core now lets a legacy codec context hold as many pictures as
it has banks, carries each frame's FIFO address to the transport, and
only keeps the m2m scheduler for slices that hold their CAPTURE buffer,
so the next picture is prepared while the previous one decodes.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
… the queue image

HEVC gets the same two-bank treatment as H.264 on the queued transport:
the VP to PP instruction FIFO, pipe state and all row/column scratch
exist once per bank, a picture records its bank in its CAPTURE buffer,
and a new done hook frees it at retirement. The VP of picture N+1 then
overlaps the PP of picture N. The legacy transport keeps one bank. The
per-buffer bank field is now shared by both codecs.

Codecs a variant holds back until a newer firmware (fw_caps_required,
T6040 HEVC) are now also enabled when the loaded image runs the queue
ABI and lists the codec in its info-block codec mask, not only through
a CODECS trailer. Falling back to the legacy transport restores the
trailer-derived set.

Both the transport choice and the codec list come from what the image
reports at boot, and clients enumerate formats and size their contexts
before a first decode would power the decoder up, so boot the firmware
once at probe and let it autosuspend.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
avd_frame gained a DMA address for its instruction FIFO, and the host
build of the queue test has no dma_addr_t. Define it in the host shim.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
…eue_mutex

The threaded interrupt handler takes queue_mutex before it touches the
decoder. Queue reset runs from the queue worker with queue_mutex held,
and its recovery called disable_irq(), which waits for that handler
thread. When an interrupt arrived around a frame timeout, the handler
blocked on queue_mutex and the reset blocked on the handler: the worker
and the IRQ thread deadlocked with the mutex held. Runtime suspend had a
similar cycle with a submission waiting for it under queue_mutex.

Close hardware access with a spinlock instead: the handler runs the
backend's interrupt code only under irq_lock, with hw_on set and the
interrupt generation it sampled before taking queue_mutex unchanged.
Turning the IRQ off clears hw_on and bumps the generation under that
lock, so an access in progress finishes first and a handler woken by an
older firmware instance leaves the hardware alone, and then uses
disable_irq_nosync(). Device removal, which holds no queue_mutex, still
synchronizes with the handler.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The old driver kicked the PP for every VP DONE message of the open
firmware. The legacy shim in the frame queue dropped a VP DONE that came
in while no frame was in flight. That happens when a surplus PP DONE of a
multi-kick picture completed the next frame early: the next frame's VP
DONE then found no active frame, its PP was never started, and it timed
out. Kick PP for the last submitting context in that case, as before.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Reading MBOX1_RETRIEVE when the mailbox is empty returns the previous
message again. On a spurious or early interrupt that is a repeated PP
DONE, which completes the next frame before its PP has run. The next PP
then never reports done, the frame times out and the decoder is reset.
The legacy shim and the queued transport both popped unconditionally.

Check the empty flag in MBOX1_STATUS (bit 17) first, give a message that
is not readable yet up to 10 us, and otherwise acknowledge and ignore the
interrupt. Both transports share the helper.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
A frame timeout on the J616s was followed by the machine going down with
nothing in the journal, so nothing showed how far the recovery got.
Log every recovery step (stop firmware, reset, DART flush, firmware boot)
before it runs, with the DART, runtime PM and IRQ state at the start,
refuse and warn once if recovery is ever entered with the decoder powered
off, and name the codec, slot state, frames in flight and transport in
the timeout message.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
avd-inst.h builds every instruction word with FIELD_PREP() but relied on
an indirect include. The arm64 build gets it through other headers; the
UML KUnit build of the avd tests does not and fails.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The recovery trail queried the runtime PM state before the existing
inactive-DART check, which the PM KUnit test exercises with no device.
Check the DART first and log that case on its own.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
…e context

job_ready() admitted at most one context at a time (queued_ctx) and only
while a slot was free. Contexts that queued work while another was
admitted were rejected, and were retried only from the queue worker,
which walks the context list in order: the first context won every time
and later ones could wait for seconds. With four streams the starved
stream's OUTPUT buffer was not returned within FFmpeg's two-second poll
("Failed waiting on OUTPUT buffer") and the run failed, with no decoder
timeout or error.

Drop the reservation and the capacity condition from job_ready() so every
context with work enters the m2m job queue, which runs jobs one at a time
in arrival order. A job that finds no free slot or no transport capacity
is kept as m2m's running job and restarted by the queue worker once a
frame retires; draining its context finishes it.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The deferred-dispatch restart in the queue worker called the backend's
max_inflight() unconditionally. The PM KUnit fixtures use backends
without a transport and crashed there. Treat a missing hook as one frame.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The J616s hung hard three times during decode benchmarks, each time
after frame timeouts and decoder resets, once beyond what the hardware
watchdog could recover. A stuck bus access, a reset that cuts DMA in
flight, or DMA into memory the driver already gave back can do that.
Close each of those paths:

- Recovery stops the CM3 firmware, then blocks the decoder's DMA by
  attaching the empty IOMMU domain and lets in-flight transfers drain
  into DART faults, and only then asserts the pmgr reset. The domain is
  restored after the reset and checked before the firmware boots.
- A third recovery within a minute, or any failed recovery, retires the
  decoder: every later frame fails at submission without touching the
  hardware until the module is reloaded. A stream is stopped at its
  first frame that fails in hardware.
- The legacy transport never writes the VP FIFO register without room in
  its queue: it checks the level before every word (the old code only
  looked between segments, and programs exceed the 128-word queue),
  waits up to 2 ms for room, and otherwise leaves the frame to the
  watchdog.
- A PP DONE completes the active legacy frame only after its VP finished
  and its PP was kicked. A surplus PP DONE of a multi-kick picture no
  longer completes the next frame while the engine is still writing it.
- The interrupt handler's hardware access lock is now a mutex, so no
  decoder register is touched with interrupts off.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
…tting

Closing a stream (STREAMOFF, close or an m2m job abort) while it still
owned a slot reset the whole decoder, cancelling every other stream's
frames with their DMA in flight. On the J616s both resets logged before
the 04:11 hard hang came from this path (no frame had timed out), each
when FFmpeg gave up on one of four concurrent streams.

Cancel the closing stream's frames that never reached the decoder, then
wait for the ones it already owns. They complete normally, or the queue
worker times them out and recovers as for any other stuck frame. Only
if frames are still owned a second after the frame timeout does the
drain fall back to the old reset, and it says so in the log.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The queued open-firmware transport copied each frame's instruction
program into the CM3 data SRAM with memcpy_toio(), which on arm64 issues
64-bit stores for aligned runs and byte stores for unaligned ends. Every
other access to that SRAM (the info block, the descriptors, the boot
time clear) is a 32-bit readl()/writel(), and the firmware consumes the
ring as 32-bit words.

The first decode through this transport on an M2 hung the machine hard
right after the doorbell, before any completion or log line, which is
what an unsupported access width on that bus would do. Use
__iowrite32_copy() so the ring is written with the same word stores as
the rest of the SRAM.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Keep a queued frame's FIFO and PP continuation metadata independent of
later preparation on its context. Encode its FIFO index in every VP
continuation and END, check the mailbox before transferring ownership,
and split SRAM copies at both the ring and u32 counter boundaries.
Never let a reserved mailbox retry be overtaken by a later picture of
that context; already-posted pictures and unrelated contexts still overlap.
Legacy VP and PP instruction semantics remain unchanged.

Drain tagged replies, retain fatal I/O completions until quiesced reset,
and add dynamic-debug descriptor, ring-counter and mailbox snapshots at
submission, completion and timeout. A sole fatal frame must not retire
before recovery merely because no other frame can arm the watchdog.

Add host coverage of the production H.264 builder, queued submit, stream,
IRQ and T8112 FIFO configuration. Extend the firmware Unicorn harness
with reset-vector boot, CM3/NVIC routing and real mailbox status bits.
The companion MIT firmware patch fixes the missing queue IRQ routes and
the erroneous use of an IRQ-source bit as the outbox EMPTY status bit.
The old harness injected both interrupt delivery and the incorrect status,
so arbitrary-word transport tests concealed those firmware failures.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Plain job_finish() warns for every HOLD_CAPTURE-capable queue, even when
the current source is a final slice. buf_done_and_job_finish() releases
OUTPUT and final CAPTURE immediately, so neither API can hand both active
buffers to an asynchronous frame slot while releasing the scheduler.

Add job_finish_detach() to atomically remove the current pair and finish
dispatch without completing either buffer. Document the driver's DMA
ownership and eventual completion obligations. Reject partial HOLD slices;
they continue to use the existing held-buffer completion API.

Use this handoff for AVD's final slices to preserve early dispatch and
VP/PP overlap. Return slot-owned buffers only after tagged retirement or
quiesced recovery. Reject stale deferred submit work whose context no
longer owns the running m2m dispatch. Keep existing HOLD warnings intact.

Exercise the production core helpers and AVD submit/send/retire with a
strict host scheduler: held/final slices, overlapping pictures, two
contexts, capacity and inbox retries, stale work and error paths.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Resolve the legacy driver conflicts at the rewrite's queue and PM layer.
Park DMA and close IRQ access before an exhausted budget returns buffers,
and leave the empty DART domain attached after a failed reset or boot.
Keep a decoder resting until its budget window expires, then reset while
still parked before restoring DMA and booting. Runtime resume must not
boot resting or retired hardware. Use the unbound watchdog workqueue.

Preserve the queued transport, detached dispatch, quiesce-before-reset,
per-stream stop and closing-context drain fixes. Retain the upstream AV1
reference bounds fix and all non-AVD updates from the merge parent.

Add production recovery/PM host coverage for failed reset/boot, budget
exhaustion and parked runtime resume, plus dispatch and KUnit rest cases.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
…sses

A queued frame must use the FIFO saved by its program builder. Reject a
missing address before configuring registers or posting a descriptor,
rather than substituting a mutable context FIFO during a retry. Include
the saved DMA address in the existing dynamic-debug program record.

Exercise the H.264 bank allocator and selector, IDR/P/B address emitters,
all valid slots, and the v3/v5 host register layouts. Inject a zero address
into each bank-1 buffer and require the host test to fail. Replay the
resulting descriptors, programs and MMIO in a routed Unicorn workload
matrix, holding decoder DONE events and checking DMA operands and tags.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Brings in #758 follow-ups and #773 (PMC performance floor with the AVD
perf-domain consumer). The one conflict is probe: keep the rewrite's
avd_pm_enable() and firmware probe boot, and settle the attached domain
links to suspended before it, as #773 does for the stock driver.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Give queued VP9 pictures separate DMA banks and retain them until PP
retirement. Publish backward probability adaptation from a tagged VP DONE
callback, so the next picture can prepare and run VP while PP is active.
Forward-only, non-refreshing and error-resilient producers impose no count
wait. Preserve the preceding picture's temporal-MV input across banks.

Negotiate an optional queue ABI 2 VP notification, with a separate full-tag
record and no early release of descriptor or buffer ownership. Include the
matching MIT firmware patch and host/Unicorn regressions. Firmware without
the feature retains one VP9 picture per stream; legacy programs and saved
probability contexts have an independent before/after comparison harness.

Document AV1's CDF and segmentation-tail dependencies pending evidence for
their visibility and CPU synchronization at VP DONE.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Extract avd_open_capabilities from avd-backend-open.c after the transport
split. Update the boundary structures and mutations for the current parser,
including its saved firmware capability mask and bounded 64-byte trailer.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Readiness can be checked while STREAMON is still allocating codec storage.
Keep ctx->priv unpublished until all VP9 banks and count-pointer tables
are initialized, and pair its publication with the readiness callback's
acquire load. Failed allocations free their local context without exposing
partially initialized banks.

Exercise readiness before start, from every bank allocation, and after
stop in the production host fixture.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
@RyanTheTide
RyanTheTide marked this pull request as ready for review November 1, 2026 07:53
The optional tagged VP DONE event is now committed in avd-fw
(bf49a31b, queue feature bit 2), so the patch kept under tests/ is
redundant. Remove it and name the firmware commit instead.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The CM3 IRQ routing and FIFO EMPTY bit 17 fixes are committed in avd-fw
(50d7f869), so the patch kept under tests/ is redundant. Remove it and name
the firmware commit instead.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Keep two complete AV1 scratch banks on queued firmware advertising tagged
VP DONE. Look ahead at the pending request's frame control and follow its
actual primary-reference timestamp. Default CDFs and references outside
the in-flight set do not wait for an unrelated picture.

The open firmware's VP event does not establish CPU visibility of AV1
hardware-written CDF, segmentation or motion-field tails. Retain PP
retirement dependencies for those inputs. When frame-end CDF updates or
segmentation-map writes are disabled, copy preparation's immutable input
snapshots into the consumer bank instead of reading live CAPTURE storage.
Only PP retirement releases the saved picture's bank.

Preserve the legacy emitters and tail preparation, add production host
flag/ownership tests and a routed AV1 Unicorn overlap test, and document
the remaining visibility evidence requirement.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
…nputs"

This reverts commit 2f9bd00.

The AV1 overlap has host and emulator tests but has not run on AV1
hardware (T6040, T8140) yet, and this branch is about to merge with the
code that was qualified on the J616s and the M2. The change is kept on
avd/rewrite-av1-overlap and comes back once it is bit-exact on hardware.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
@AxxzyWasTaken

Copy link
Copy Markdown
Contributor

@RyanTheTide, a bug report for the queued transport, since issues are off on this repo.

J616C (M4 Max): the queued AVD transport decodes VP9 with segmentation wrong

Machine and build

  • MacBook Pro 16" M4 Max, apple,j616c / apple,t6041, Arch Linux ARM.
  • Kernel 7.2.10 (local J616C tree) with drivers/media/platform/apple/avd from integration/aurora 21f7ffc, built as an external module, plus the v4l2_m2m_job_finish_detach() core from 6e3a36c.
  • avd-fw 6062e851 v5-t2, built with clang 22 and byte-identical to the alarm-aurora PKGBUILD hashes.
  • asahi,os-fw-version 26.6.2; asahi,system-fw-version unknown.
  • VA-API through libva 2.24.1 + libva-v4l2_request; ffmpeg n9.0.2.

Queue mode on T6041 needs the T6040 sram window (0x281094000). With it the driver logs ABI 2 aperture 0x1094000 features 0xf and using open-queue transport (15 frames in flight). Our board DT had 0x281092000, which silently stays on legacy (#903).

Result

In open-queue, VP9 with segmentation isn't bit-exact. The same clips are bit-exact in open-compat. Comparison is ffmpeg framemd5, VA-API against software, 2 runs each:

clip open-queue open-compat
testsrc2 1080p, libvpx -aq-mode 1 (segmentation) 118/120 frames differ 0
testsrc2 1080p, -aq-mode 2 118/120 not run
mandelbrot 1080p, -aq-mode 1 / -aq-mode 2 117/120, 118/120 0 (aq 2)
the same encodes without -aq-mode (incl. 2-pass, alt-ref, 4 tile columns) 0 0
YouTube VP9 1080p / 480p 117/120, 82/120, varies by a frame or two between runs; PSNR avg 49.6 dB, min 44.9 dB 0
H.264 1080p/switch; AV1 480p-4K, 2x2 tiles, 10-bit 0 0

No AVD kernel lines, no timeouts and no DART faults in any of these runs.

Shortest reproduction:

ffmpeg -f lavfi -i testsrc2=size=1920x1080:rate=30:duration=4 -c:v libvpx-vp9 -deadline good -cpu-used 2 \
  -row-mt 1 -tile-columns 2 -auto-alt-ref 1 -lag-in-frames 25 -aq-mode 1 -b:v 3M -g 120 -pix_fmt yuv420p seg.webm
ffmpeg -i seg.webm -pix_fmt yuv420p -f framemd5 sw.md5
LIBVA_DRIVER_NAME=v4l2_request ffmpeg -hwaccel vaapi -hwaccel_device /dev/dri/card1 -i seg.webm -pix_fmt yuv420p -f framemd5 hw.md5

Every clip above has frame_parallel_decoding_mode=1 and refresh_frame_context=1. With -aq-mode, segmentation_enabled=1 is set in every frame, with segmentation_temporal_update=1 in about 12 of 120. The YouTube clip has segmentation in 29 of its first 30 frames, with temporal update in 27.

Likely cause (not verified)

avd_vp9_ready() only holds the next prepare for a producer that does backward adaptation (avd_vp9_needs_counts(): refresh, not frame-parallel, not error-resilient). This PR says the same: "Non-refreshing, frame-parallel and error-resilient frames have no dependency." But a frame-parallel frame with segmentation still reads the previous frame's segment map: it predicts from it with temporal update, or keeps it when update_map is 0. Nothing waits for that map, so the next picture can read it while the previous one is still writing it. Which stage writes the map wasn't checked.

Most YouTube VP9 is segmented, so on queue mode this hits most YouTube playback. It probably affects the J616s too. Bench streams without segmentation, like the ones here and in #787/#793 if they were encoded that way, wouldn't catch it.

For now this Max stays on open-compat, which is bit-exact for everything above.

notmysegv pushed a commit to notmysegv/linux that referenced this pull request Nov 4, 2026
…tion/aurora

Bring integration/aurora up to aurora-wip b359258, the source of
7.3.0-aurora-fleet14f, promoted on the M1 Pro, M2, Neo and J616s on
2026-11-01 and 11-02. The range also covers the fleet14c, fleet14d and
fleet14e sources (f41ac3d, 4759557, 757fc07), each booted on
hardware. It adds aurora-silicon#773, aurora-silicon#776, aurora-silicon#777, aurora-silicon#737, aurora-silicon#713, aurora-silicon#785, aurora-silicon#786, aurora-silicon#788,
aurora-silicon#789 and aurora-silicon#787. No conflicts.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
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