[v2,3/3] libcamera: v4l2_device: Make setFrameStartEnabled() generic
diff mbox series

Message ID 20261005-v4l2-events-v2-3-e4883190489d@ideasonboard.com
State New
Headers show
Series
  • Add generic V4L2 Events support
Related show

Commit Message

Daniel Scally Oct. 5, 2026, 3:40 p.m. UTC
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 subscription details are
parametised. This will allow consumers to subscribe to any supported
events.

Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
---
Changes in v2:

	- use kernel uAPI directly instead of class V4L2Event and
	  V4L2EventSubscription
---
 include/libcamera/internal/v4l2_device.h           |  5 ++--
 src/libcamera/pipeline/ipu3/cio2.cpp               |  9 +++++--
 src/libcamera/pipeline/mali-c55/mali-c55.cpp       | 10 ++++++--
 src/libcamera/pipeline/rkisp1/rkisp1.cpp           | 10 +++++---
 .../pipeline/rpi/common/pipeline_base.cpp          |  8 +++++--
 src/libcamera/pipeline/simple/simple.cpp           |  8 +++++--
 src/libcamera/v4l2_device.cpp                      | 28 ++++++++++++----------
 7 files changed, 53 insertions(+), 25 deletions(-)

Patch
diff mbox series

diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h
index 6b77dd46f654e4b95db0327e73164719c47a77d9..124f6cd9e477b8881de8f7c69d304a144a66c67a 100644
--- a/include/libcamera/internal/v4l2_device.h
+++ b/include/libcamera/internal/v4l2_device.h
@@ -10,6 +10,7 @@ 
 #include <map>
 #include <memory>
 #include <optional>
+#include <set>
 #include <span>
 #include <stdint.h>
 #include <vector>
@@ -47,7 +48,7 @@  public:
 	std::string devicePath() const;
 
 	bool supportsEvents(v4l2_event_subscription &sub);
-	int setFrameStartEnabled(bool enable);
+	int setEventsEnabled(v4l2_event_subscription &sub, bool enable);
 	Signal<const v4l2_event &> eventReady;
 
 	void updateControlInfo();
@@ -90,7 +91,7 @@  private:
 	UniqueFD fd_;
 
 	std::unique_ptr<EventNotifier> fdEventNotifier_;
-	bool frameStartEnabled_;
+	std::set<std::tuple<uint32_t, uint32_t>> subscribedEvents_;
 };
 
 } /* namespace libcamera */
diff --git a/src/libcamera/pipeline/ipu3/cio2.cpp b/src/libcamera/pipeline/ipu3/cio2.cpp
index 7481b2686df070e94fc5f698141db3d3b97896ea..876f78ace06cbc96fb8100cfc4ed76c73de1d449 100644
--- a/src/libcamera/pipeline/ipu3/cio2.cpp
+++ b/src/libcamera/pipeline/ipu3/cio2.cpp
@@ -356,7 +356,10 @@  int CIO2Device::start()
 		return ret;
 	}
 
-	ret = csi2_->setFrameStartEnabled(true);
+	v4l2_event_subscription sub{};
+	sub.type = V4L2_EVENT_FRAME_SYNC;
+
+	ret = csi2_->setEventsEnabled(sub, true);
 	if (ret) {
 		stop();
 		return ret;
@@ -367,9 +370,11 @@  int CIO2Device::start()
 
 int CIO2Device::stop()
 {
+	v4l2_event_subscription sub{};
 	int ret;
 
-	csi2_->setFrameStartEnabled(false);
+	sub.type = V4L2_EVENT_FRAME_SYNC;
+	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 da3153ad7fafc133cdde6accb5e3bd54f80520c9..270efb520c53f10cf456ce89a34e8ba40270d560 100644
--- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp
+++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp
@@ -1393,7 +1393,10 @@  int PipelineHandlerMaliC55::start(Camera *camera, [[maybe_unused]] const Control
 		return ret;
 	}
 
-	ret = isp_->setFrameStartEnabled(true);
+	v4l2_event_subscription sub{};
+	sub.type = V4L2_EVENT_FRAME_SYNC;
+
+	ret = isp_->setEventsEnabled(sub, true);
 	if (ret)
 		LOG(MaliC55, Error) << "Failed to enable frame start events";
 
@@ -1404,7 +1407,10 @@  void PipelineHandlerMaliC55::stopDevice(Camera *camera)
 {
 	MaliC55CameraData *data = cameraData(camera);
 
-	isp_->setFrameStartEnabled(false);
+	v4l2_event_subscription sub{};
+	sub.type = V4L2_EVENT_FRAME_SYNC;
+
+	isp_->setEventsEnabled(sub, false);
 
 	if (auto *mem = std::get_if<MaliC55CameraData::Memory>(&data->input_)) {
 		ivc_->streamOff();
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
index e49dd546ee0a5207c75a364564d5673fd8463b30..967f39a9feb6d9d40d0b8df366adbf7d6863458c 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
@@ -1304,7 +1304,9 @@  int PipelineHandlerRkISP1::start(Camera *camera, [[maybe_unused]] const ControlL
 			return ret;
 	}
 
-	isp_->setFrameStartEnabled(true);
+	v4l2_event_subscription sub{};
+	sub.type = V4L2_EVENT_FRAME_SYNC;
+	isp_->setEventsEnabled(sub, true);
 
 	activeCamera_ = camera;
 
@@ -1317,7 +1319,9 @@  void PipelineHandlerRkISP1::stopDevice(Camera *camera)
 	RkISP1CameraData *data = cameraData(camera);
 	int ret;
 
-	isp_->setFrameStartEnabled(false);
+	v4l2_event_subscription sub{};
+	sub.type = V4L2_EVENT_FRAME_SYNC;
+	isp_->setEventsEnabled(sub, false);
 
 	data->ipa_->stop();
 
@@ -1653,7 +1657,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 2315a1c47dc26ed88fa149106a25dcf1ffbc3ec0..cb642967cf315cb8325fab6edb3723831073aec1 100644
--- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
+++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
@@ -702,7 +702,9 @@  int PipelineHandlerBase::start(Camera *camera, const ControlList *controls)
 	data->state_ = CameraData::State::Idle;
 
 	/* Enable SOF event generation. */
-	data->frontendDevice()->setFrameStartEnabled(true);
+	v4l2_event_subscription sub{};
+	sub.type = V4L2_EVENT_FRAME_SYNC;
+	data->frontendDevice()->setEventsEnabled(sub, true);
 
 	data->platformStart();
 
@@ -731,7 +733,9 @@  void PipelineHandlerBase::stopDevice(Camera *camera)
 	}
 
 	/* Disable SOF event generation. */
-	data->frontendDevice()->setFrameStartEnabled(false);
+	v4l2_event_subscription sub{};
+	sub.type = V4L2_EVENT_FRAME_SYNC;
+	data->frontendDevice()->setEventsEnabled(sub, false);
 
 	data->clearIncompleteRequests();
 
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
index 75e068bf516d80af6c8aeeb94bf00ba265744fdd..04f82a55168e7c87f7024a962f85b818347b3236 100644
--- a/src/libcamera/pipeline/simple/simple.cpp
+++ b/src/libcamera/pipeline/simple/simple.cpp
@@ -1682,7 +1682,9 @@  int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
 
 	data->delayedCtrls_->reset();
 	if (frameStartEmitter) {
-		ret = frameStartEmitter->setFrameStartEnabled(true);
+		v4l2_event_subscription sub{};
+		sub.type = V4L2_EVENT_FRAME_SYNC;
+		ret = frameStartEmitter->setEventsEnabled(sub, true);
 		if (ret) {
 			stop(camera);
 			return ret;
@@ -1726,7 +1728,9 @@  void SimplePipelineHandler::stopDevice(Camera *camera)
 	V4L2Subdevice *frameStartEmitter = data->frameStartEmitter_;
 
 	if (frameStartEmitter) {
-		frameStartEmitter->setFrameStartEnabled(false);
+		v4l2_event_subscription sub{};
+		sub.type = V4L2_EVENT_FRAME_SYNC;
+		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 192edd802a22d3edc7055132eb84ca00e095dd26..03001fbc6ffd906715d195033676e5d5d529f493 100644
--- a/src/libcamera/v4l2_device.cpp
+++ b/src/libcamera/v4l2_device.cpp
@@ -57,8 +57,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)
 {
 }
 
@@ -489,30 +488,35 @@  bool V4L2Device::supportsEvents(v4l2_event_subscription &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(v4l2_event_subscription &sub, bool enable)
 {
-	if (frameStartEnabled_ == enable)
-		return 0;
+	std::tuple<uint32_t, uint32_t> subTypeId(sub.type, sub.id);
 
-	struct v4l2_event_subscription event{};
-	event.type = V4L2_EVENT_FRAME_SYNC;
+	if (subscribedEvents_.contains(subTypeId))
+		return 0;
 
 	unsigned long request = enable ? VIDIOC_SUBSCRIBE_EVENT
 			      : VIDIOC_UNSUBSCRIBE_EVENT;
-	int ret = ioctl(request, &event);
+	int ret = ioctl(request, &sub);
 	if (enable && ret)
 		return ret;
 
 	fdEventNotifier_->setEnabled(enable);
-	frameStartEnabled_ = enable;
+
+	if (enable)
+		subscribedEvents_.insert(subTypeId);
+	else
+		subscribedEvents_.erase(subTypeId);
 
 	return ret;
 }