5965805d86
Two pieces — both shipped:
1. Media controller binding closes the last v4l2-compliance
failure from 8.6 (DECODER_CMD, which requires has_media on
stateless decoders) and unlocks the V4L2 request API for
libva-v4l2-request.
2. Multi-frame streaming test exercises the daemon's
AVCodecContext state preservation across many REQ_DECODE
calls — Phase 8.6's tests pushed exactly one keyframe per
invocation; real content has P-frame references.
Compliance now reaches **49/49 passing.**
Kernel (kernel/daedalus_v4l2_main.{c,h}):
- Added `struct media_device mdev` to daedalus_dev.
- media_device_init(&mdev) BEFORE v4l2_device_register so
v4l2-core sees v4l2_dev.mdev = &mdev and binds the m2m
entities into the graph during register.
- After video_register_device:
v4l2_m2m_register_media_controller(..., MEDIA_ENT_F_PROC_VIDEO_DECODER)
then media_device_register so userspace sees the complete
graph in /dev/mediaN with the decoder entity tagged.
- daedalus_remove unwinds in reverse: unregister media,
unregister mc, unregister video, release m2m, unregister
v4l2, cleanup mdev.
- Error paths added for both new failure points.
Test harness (tools/test_m2m_stream.c, new):
- Multi-frame V4L2 m2m client: parses IVF → 4-deep buffer
rings on both queues → per-frame QBUF/DQBUF loop →
concatenates decoded NV12 to output file. Returns 0 only
if every input frame decoded without error.
- Same codec vocabulary as test_m2m_decode (vp9 | av1 |
h264 via 5th arg).
Verification on hertz (Pi 5, 6.12.75+rpt-rpi-2712):
v4l2-compliance: 49 tests, 49 passed, 0 failed, 0 warnings.
$ v4l2-ctl --list-devices
daedalus-fourier V3D7+NEON (platform:daedalus_v4l2):
/dev/video0
/dev/media3
VP9 320×240 30 frames (1 keyframe + 29 P-frames, 3.46 MB
NV12): byte-for-byte match vs `ffmpeg -i in.ivf -pix_fmt
nv12 -f rawvideo`.
VP9 1920×1080 10 frames (31 MB NV12 through the dmabuf
path): byte-for-byte match vs same reference command.
Daemon log shows cookies 1..30 all completing cleanly in
order; lazily-opened AVCodecContext maintains reference
frames across the chardev round-trips.
Clean SIGTERM + rmmod, no oops/WARN.
Roadmap update (docs/roadmap.md):
- 8.7 marked closed with closure-doc reference.
- 8.8 reshaped: perf profiling, QPU dispatch substitution
via daedalus-fourier, multi-frame AV1/H.264, HDR (P010M).
Per correctness-before-speed:
- Order-correct media controller lifecycle (init → bind
v4l2_dev → register video → register mc → register
media; reverse for teardown).
- 4-deep buffer rings on both queues — the scheduler
actually pipelines multiple in-flight cookies through
the chardev (not just one-at-a-time as in 8.5/8.6 tests).
- Bit-exact comparison against ffmpeg, not "looks right."
- All resource paths cleaned on every error branch.
Phase 8.8 next: profile daemon hot loops, dlopen
daedalus-fourier from the daemon, swap FFmpeg per-block
calls for daedalus_dispatch_* where the kernel matches,
target 30fps@1080p from 30fps-floor-is-fine memory.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
107 lines
3.8 KiB
C
107 lines
3.8 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
/*
|
|
* daedalus-v4l2 — kernel-internal device/state declarations.
|
|
*
|
|
* Shared between daedalus_v4l2_main.c (V4L2 m2m driver) and
|
|
* daedalus_v4l2_chardev.c (kernel↔daemon bridge). The chardev
|
|
* needs to look up in-flight V4L2 requests by cookie to complete
|
|
* the m2m job when RESP_FRAME arrives — that path lives in
|
|
* daedalus_complete_resp_frame().
|
|
*/
|
|
#ifndef DAEDALUS_V4L2_MAIN_H
|
|
#define DAEDALUS_V4L2_MAIN_H
|
|
|
|
#include <linux/list.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <media/v4l2-device.h>
|
|
#include <media/v4l2-dev.h>
|
|
#include <media/v4l2-mem2mem.h>
|
|
#include <media/media-device.h>
|
|
|
|
#include "daedalus_v4l2_proto.h"
|
|
|
|
/**
|
|
* struct daedalus_dev - top-level device state (singleton for now)
|
|
* @pdev: owning platform device (synthesised in module_init)
|
|
* @v4l2_dev: V4L2 device parent for any video_device we register
|
|
* @vdev: video_device exposed as /dev/videoNN
|
|
* @m2m_dev: mem2mem device shared by all per-open contexts
|
|
* @m2m_lock: serialises vb2 queue + v4l2 ioctl ops
|
|
* @inflight: list of struct daedalus_inflight (REQ_DECODE sent,
|
|
* RESP_FRAME not yet returned)
|
|
* @inflight_lock: protects @inflight
|
|
*
|
|
* Singleton per-module instance. Multi-instance support (one
|
|
* decoder per /dev/videoNN) would require breaking g_daedalus_dev
|
|
* out of daedalus_v4l2_main.c; not needed yet.
|
|
*/
|
|
struct daedalus_dev {
|
|
struct platform_device *pdev;
|
|
struct v4l2_device v4l2_dev;
|
|
struct video_device vdev;
|
|
struct v4l2_m2m_dev *m2m_dev;
|
|
struct media_device mdev;
|
|
struct mutex m2m_lock;
|
|
struct list_head inflight;
|
|
struct mutex inflight_lock;
|
|
};
|
|
|
|
/* Module-wide singleton accessor (chardev needs this for RESP_FRAME). */
|
|
struct daedalus_dev *daedalus_get_dev(void);
|
|
|
|
/**
|
|
* daedalus_next_cookie() - shared cookie allocator
|
|
*
|
|
* Returns the next monotonically increasing request cookie.
|
|
* Used by both the V4L2 m2m device_run path (for REQ_DECODE
|
|
* from real OUTPUT buffers) and the chardev debugfs
|
|
* test_decode path (for hand-crafted REQ_DECODE injection),
|
|
* so the two namespaces never collide and RESP_FRAME logs
|
|
* stay deterministic.
|
|
*/
|
|
u32 daedalus_next_cookie(void);
|
|
|
|
/**
|
|
* daedalus_complete_resp_frame() - chardev RESP_FRAME completion
|
|
* @cookie: cookie carried by the matching REQ_DECODE
|
|
* @fr: RESP_FRAME header from the daemon
|
|
* @pixels: inline pixel bytes following the header in the
|
|
* chardev payload (may be NULL if @pixels_len == 0)
|
|
* @pixels_len: number of inline pixel bytes
|
|
*
|
|
* Called from the chardev write() path on RESP_FRAME. Looks up
|
|
* the in-flight request, copies inline pixel data into the
|
|
* CAPTURE vb2 buffer if available (Phase 8.5 path; Phase 8.6
|
|
* skips the copy because the daemon decoded directly into the
|
|
* dmabuf), then completes both src+dst buffers and finishes
|
|
* the m2m job. Silently drops responses for unknown cookies
|
|
* (pr_warn_ratelimited).
|
|
*/
|
|
void daedalus_complete_resp_frame(u32 cookie,
|
|
const struct daedalus_resp_frame *fr,
|
|
const u8 *pixels, size_t pixels_len);
|
|
|
|
/**
|
|
* daedalus_export_capture_dmabuf() - chardev GET_DMABUF backend
|
|
* @cookie: cookie from the matching REQ_DECODE
|
|
* @plane: plane index (0-based) within the CAPTURE buffer
|
|
* @flags: flags for dma_buf_fd (O_CLOEXEC etc.)
|
|
* @out_fd: out: installed dmabuf fd in the calling task's
|
|
* fd table (only valid when return value == 0)
|
|
*
|
|
* Called from the chardev DAEDALUS_IOC_GET_DMABUF ioctl
|
|
* handler. Looks up the in-flight V4L2 request by cookie,
|
|
* exports the CAPTURE vb2 buffer's plane as a dma_buf via
|
|
* vb2_core_expbuf in the daemon's task context. Caller must
|
|
* NOT touch out_fd on non-zero return.
|
|
*
|
|
* Return: 0 on success, -EINVAL for unknown cookie or bad
|
|
* plane, propagated -errno from vb2_core_expbuf otherwise.
|
|
*/
|
|
int daedalus_export_capture_dmabuf(u32 cookie, u32 plane, u32 flags,
|
|
int *out_fd);
|
|
|
|
#endif /* DAEDALUS_V4L2_MAIN_H */
|