From patchwork Fri Sep 25 15:16:24 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Scally X-Patchwork-Id: 28381 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 41AD8BDCBD for ; Fri, 25 Sep 2026 15:16:40 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A6C5C689C5; Fri, 25 Sep 2026 17:16:38 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="fc+KgTsE"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3A87A689BC for ; Fri, 25 Sep 2026 17:16:31 +0200 (CEST) Received: from [127.0.1.1] (chfd-03-b2-v4wan-176392-cust229.vm15.cable.virginm.net [82.19.20.230]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1A82A12F; Fri, 25 Sep 2026 17:14:43 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1790349283; bh=g2yHqCL0KCftbCHzgW5pVSWusJM+kuuWobw0fthKEZY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=fc+KgTsE5tEUGKEl9YNBzBPbNaFwNR5GZ+Ey0uXzWlB9zT2yxCPj/DDD11282GG+q MY7bLfxm1DGrTr0J7tHOQCL+HK/cU3BFMkzbZhH9Na/ZtXb/BJpHlhD8X8UJ+Mzs7b S2ugX4E/vYOXn5wCaxIR4wRNLSZo89zMpe2nWeJQ= From: Daniel Scally Date: Fri, 25 Sep 2026 16:16:24 +0100 Subject: [PATCH 4/4] libcamera: v4l2_device: Make setFrameStartEnabled() generic MIME-Version: 1.0 Message-Id: <20260925-v4l2-events-v1-4-b2ec212eb5d0@ideasonboard.com> References: <20260925-v4l2-events-v1-0-b2ec212eb5d0@ideasonboard.com> In-Reply-To: <20260925-v4l2-events-v1-0-b2ec212eb5d0@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Daniel Scally X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=9409; i=dan.scally@ideasonboard.com; h=from:subject:message-id; bh=g2yHqCL0KCftbCHzgW5pVSWusJM+kuuWobw0fthKEZY=; b=owEBbQKS/ZANAwAKAchJV3psRXUyAcsmYgBqtpBNQ7bWTP+nRMQhkkB/gRo8/HAmzrlIK9IHA 13U79doGfuJAjMEAAEKAB0WIQQqyuwyDnZdb+mxmm/ISVd6bEV1MgUCaraQTQAKCRDISVd6bEV1 Mt+2D/9LtfijexbWYXoDVwvVuliB/C3ND5vu7D0BVXcSEhyQJiiUdDAo6/1itCI6FPA4fcgAiR4 MCvrn2KPkCsK840S9xPs1qzt3hojeJy2P/oezm6mnVa4BOjrrKzthMyU/xiNj1NNh3L8CKF1I6d 3N6U3gfCZfJlnmiymaOB9IntwH9PbAq7fDi6sJArzESSznjZ9IWl/PlgTyZ1tcaM3UnNZdhRmAc Eyi/UvrMQwBAmzYih9P9H91fdi0ni41qfJxauknDpJZcw8f7FMG0fUDAjto7wGMlraiddubXyuY OU7+yntwxWO4GZBbr36VtsoM119Zzu2HmnvLBru3ezFIL1kJqvdDwTD+81F9nvdwFvnzK2U7FJz 5i8SVbTxE6vOt2ieH1gWl7H8Wg4FJzGm4XT6m59MLgGVMn7TivpYXdbbCouwJWDL1ewmat0Owpn pHhwJcNtCHwLkxfkxzTBvZr3yRBtJYtlw2ARe0QXO4KAzk3hVQiq6+9b9pWvkyORmtba8ovLzOV SiGAn9uz10aECjYHTC8o0FX7ooNefBqqInIBSEZ2+Pi65YTuvLibgEk1LxKW95nUdP6lVISMmtm 193DSBnBsDJMMvum0nAvLGwkeZjTp8qxb475HqpNH/M/VXS44yHJ/+5FWHBN96LEgHagWKdqAck 6t+Izq9lXPVYlaA== X-Developer-Key: i=dan.scally@ideasonboard.com; a=openpgp; fpr=EEC699ACA1B7CB5D31330C0BBD501C2A3546CCF6 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The current implementation of V4L2Device::setFrameStartEnabled hard codes V4L2_EVENT_FRAME_SYNC as it passes the subscription to the kernel. Re-work the function such that the subcription details are parametised. This will allow consumers to suscribe to any supported events. Signed-off-by: Daniel Scally --- include/libcamera/internal/v4l2_device.h | 5 ++-- src/libcamera/pipeline/ipu3/cio2.cpp | 6 ++-- src/libcamera/pipeline/mali-c55/mali-c55.cpp | 6 ++-- src/libcamera/pipeline/rkisp1/rkisp1.cpp | 8 ++++-- .../pipeline/rpi/common/pipeline_base.cpp | 6 ++-- src/libcamera/pipeline/simple/simple.cpp | 6 ++-- src/libcamera/v4l2_device.cpp | 32 ++++++++++++++++------ 7 files changed, 47 insertions(+), 22 deletions(-) diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h index 0c3bbcf2df39f581ceffb9afa6b008368b302156..384323be6db1d46249200b4b4375ac4c85695502 100644 --- a/include/libcamera/internal/v4l2_device.h +++ b/include/libcamera/internal/v4l2_device.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -48,7 +49,7 @@ public: std::string devicePath() const; bool supportsEvents(V4L2EventSubscription &sub); - int setFrameStartEnabled(bool enable); + int setEventsEnabled(V4L2EventSubscription &sub, bool enable); Signal> eventReady; void updateControlInfo(); @@ -91,7 +92,7 @@ private: UniqueFD fd_; std::unique_ptr fdEventNotifier_; - bool frameStartEnabled_; + std::set subscribedEvents_; }; } /* namespace libcamera */ diff --git a/src/libcamera/pipeline/ipu3/cio2.cpp b/src/libcamera/pipeline/ipu3/cio2.cpp index 7481b2686df070e94fc5f698141db3d3b97896ea..c5b51c222e8544fad6b6a0112dccae35ce3622f0 100644 --- a/src/libcamera/pipeline/ipu3/cio2.cpp +++ b/src/libcamera/pipeline/ipu3/cio2.cpp @@ -356,7 +356,8 @@ int CIO2Device::start() return ret; } - ret = csi2_->setFrameStartEnabled(true); + V4L2EventSubscription sub(V4L2Event::Type::FrameSync); + ret = csi2_->setEventsEnabled(sub, true); if (ret) { stop(); return ret; @@ -369,7 +370,8 @@ int CIO2Device::stop() { int ret; - csi2_->setFrameStartEnabled(false); + V4L2EventSubscription sub(V4L2Event::Type::FrameSync); + csi2_->setEventsEnabled(sub, false); ret = output_->streamOff(); diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp index 627882f194447912cbed7d97e20bd8baec0e982a..ad5ed3d55958fb496d54da32d8bb3d5d9ec6a758 100644 --- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp +++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp @@ -1393,7 +1393,8 @@ int PipelineHandlerMaliC55::start(Camera *camera, [[maybe_unused]] const Control return ret; } - ret = isp_->setFrameStartEnabled(true); + V4L2EventSubscription sub(V4L2Event::Type::FrameSync); + ret = isp_->setEventsEnabled(sub, true); if (ret) LOG(MaliC55, Error) << "Failed to enable frame start events"; @@ -1404,7 +1405,8 @@ void PipelineHandlerMaliC55::stopDevice(Camera *camera) { MaliC55CameraData *data = cameraData(camera); - isp_->setFrameStartEnabled(false); + V4L2EventSubscription sub(V4L2Event::Type::FrameSync); + isp_->setEventsEnabled(sub, false); if (auto *mem = std::get_if(&data->input_)) { ivc_->streamOff(); diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index 53ec0007cc95c5ab2da0a5d8a86b5a3330d55f99..9b2ca055aa10f0b7ef5625ab06ec5b4b2ea38f66 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -1305,7 +1305,8 @@ int PipelineHandlerRkISP1::start(Camera *camera, [[maybe_unused]] const ControlL return ret; } - isp_->setFrameStartEnabled(true); + V4L2EventSubscription sub(V4L2Event::Type::FrameSync); + isp_->setEventsEnabled(sub, true); activeCamera_ = camera; @@ -1318,7 +1319,8 @@ void PipelineHandlerRkISP1::stopDevice(Camera *camera) RkISP1CameraData *data = cameraData(camera); int ret; - isp_->setFrameStartEnabled(false); + V4L2EventSubscription sub(V4L2Event::Type::FrameSync); + isp_->setEventsEnabled(sub, false); data->ipa_->stop(); @@ -1654,7 +1656,7 @@ void PipelineHandlerRkISP1::imageBufferReady(FrameBuffer *buffer) * Record the sensor's timestamp in the request metadata. * * \todo The sensor timestamp should be better estimated by connecting - * to the V4L2Device::frameStart signal. + * to the V4L2Device::FrameSync signal. */ request->_d()->metadata().set(controls::SensorTimestamp, metadata.timestamp); diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp index c6b51e8c335635788cdc99b7a33e37df7de8a1cb..8c5945cab31755025fad8fa849dc2935876252ca 100644 --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp @@ -703,7 +703,8 @@ int PipelineHandlerBase::start(Camera *camera, const ControlList *controls) data->state_ = CameraData::State::Idle; /* Enable SOF event generation. */ - data->frontendDevice()->setFrameStartEnabled(true); + V4L2EventSubscription sub(V4L2Event::Type::FrameSync); + data->frontendDevice()->setEventsEnabled(sub, true); data->platformStart(); @@ -732,7 +733,8 @@ void PipelineHandlerBase::stopDevice(Camera *camera) } /* Disable SOF event generation. */ - data->frontendDevice()->setFrameStartEnabled(false); + V4L2EventSubscription sub(V4L2Event::Type::FrameSync); + data->frontendDevice()->setEventsEnabled(sub, false); data->clearIncompleteRequests(); diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp index af3caf74ff56d4e6cc45ad098a93e9fde45449c2..b6c2aa9592bdd27b517274e4d2bd99a59040b42b 100644 --- a/src/libcamera/pipeline/simple/simple.cpp +++ b/src/libcamera/pipeline/simple/simple.cpp @@ -1679,7 +1679,8 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL data->delayedCtrls_->reset(); if (frameStartEmitter) { - ret = frameStartEmitter->setFrameStartEnabled(true); + V4L2EventSubscription sub(V4L2Event::Type::FrameSync); + ret = frameStartEmitter->setEventsEnabled(sub, true); if (ret) { stop(camera); return ret; @@ -1723,7 +1724,8 @@ void SimplePipelineHandler::stopDevice(Camera *camera) V4L2Subdevice *frameStartEmitter = data->frameStartEmitter_; if (frameStartEmitter) { - frameStartEmitter->setFrameStartEnabled(false); + V4L2EventSubscription sub(V4L2Event::Type::FrameSync); + frameStartEmitter->setEventsEnabled(sub, false); frameStartEmitter->eventReady.connect(data, &SimpleCameraData::handleEvent); } diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp index f231bbb17d8528aca274388b8dcaccc0dc339ad3..b93f3fff7280be1f0357341f217931cd290ba97f 100644 --- a/src/libcamera/v4l2_device.cpp +++ b/src/libcamera/v4l2_device.cpp @@ -58,8 +58,7 @@ LOG_DEFINE_CATEGORY(V4L2) * at open() time, and the \a logTag to prefix log messages with. */ V4L2Device::V4L2Device(const std::string &deviceNode) - : deviceNode_(deviceNode), fdEventNotifier_(nullptr), - frameStartEnabled_(false) + : deviceNode_(deviceNode), fdEventNotifier_(nullptr) { } @@ -499,21 +498,32 @@ bool V4L2Device::supportsEvents(V4L2EventSubscription &sub) } /** - * \brief Enable or disable frame start event notification + * \brief Enable or event notifications + * \param[in] sub Details of the event to subscribe to * \param[in] enable True to enable frame start events, false to disable them * - * This function enables or disables generation of frame start events. Once - * enabled, the events are signalled through the eventReady signal. + * This function enables or disables generation of events for a particular + * subscriptions. Once enabled, the events are signalled through the eventReady + * signal. * * \return 0 on success, a negative error code otherwise */ -int V4L2Device::setFrameStartEnabled(bool enable) +int V4L2Device::setEventsEnabled(V4L2EventSubscription &sub, bool enable) { - if (frameStartEnabled_ == enable) + if (sub.type() >= V4L2Event::Type::NumberOfEventTypes) + return -EINVAL; + + if (subscribedEvents_.find(sub) != subscribedEvents_.end()) return 0; struct v4l2_event_subscription event{}; - event.type = V4L2_EVENT_FRAME_SYNC; + + auto v4l2EventType = V4L2Event::typeToV4L2(sub.type()); + if (!v4l2EventType) + return -EINVAL; + + event.type = *v4l2EventType; + event.id = sub.id(); unsigned long request = enable ? VIDIOC_SUBSCRIBE_EVENT : VIDIOC_UNSUBSCRIBE_EVENT; @@ -522,7 +532,11 @@ int V4L2Device::setFrameStartEnabled(bool enable) return ret; fdEventNotifier_->setEnabled(enable); - frameStartEnabled_ = enable; + + if (enable) + subscribedEvents_.insert(sub); + else + subscribedEvents_.erase(sub); return ret; }