diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp
index 0f3b862f5bc3..94d138cd5710 100644
--- a/src/v4l2/v4l2_camera.cpp
+++ b/src/v4l2/v4l2_camera.cpp
@@ -12,13 +12,15 @@
 
 #include <libcamera/base/log.h>
 
+#include <libcamera/control_ids.h>
+
 using namespace libcamera;
 
 LOG_DECLARE_CATEGORY(V4L2Compat)
 
 V4L2Camera::V4L2Camera(std::shared_ptr<Camera> camera)
-	: camera_(camera), isRunning_(false), bufferAllocator_(nullptr),
-	  efd_(-1), bufferAvailableCount_(0)
+	: camera_(camera), controls_(controls::controls), isRunning_(false),
+	  bufferAllocator_(nullptr), efd_(-1), bufferAvailableCount_(0)
 {
 	camera_->requestCompleted.connect(this, &V4L2Camera::requestComplete);
 }
@@ -202,10 +204,12 @@ int V4L2Camera::streamOn()
 	if (isRunning_)
 		return 0;
 
-	int ret = camera_->start();
+	int ret = camera_->start(&controls_);
 	if (ret < 0)
 		return ret == -EACCES ? -EBUSY : ret;
 
+	controls_.clear();
+
 	isRunning_ = true;
 
 	for (Request *req : pendingRequests_) {
@@ -265,6 +269,8 @@ int V4L2Camera::qbuf(unsigned int index)
 		return 0;
 	}
 
+	request->controls().merge(std::move(controls_));
+
 	ret = camera_->queueRequest(request);
 	if (ret < 0) {
 		LOG(V4L2Compat, Error) << "Can't queue request";
diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h
index 9a0b04551c9d..e54371fb4e00 100644
--- a/src/v4l2/v4l2_camera.h
+++ b/src/v4l2/v4l2_camera.h
@@ -16,6 +16,7 @@
 #include <libcamera/base/shared_fd.h>
 
 #include <libcamera/camera.h>
+#include <libcamera/controls.h>
 #include <libcamera/framebuffer.h>
 #include <libcamera/framebuffer_allocator.h>
 
@@ -50,6 +51,8 @@ public:
 				  const libcamera::Size &size,
 				  libcamera::StreamConfiguration *streamConfigOut);
 
+	libcamera::ControlList &controls() { return controls_; }
+
 	int allocBuffers(unsigned int count);
 	void freeBuffers();
 	int getBufferFd(unsigned int index);
@@ -71,6 +74,8 @@ private:
 	std::shared_ptr<libcamera::Camera> camera_;
 	std::unique_ptr<libcamera::CameraConfiguration> config_;
 
+	libcamera::ControlList controls_;
+
 	bool isRunning_;
 
 	libcamera::Mutex bufferLock_;
diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
index da1170536a8f..00ba9ed360eb 100644
--- a/src/v4l2/v4l2_camera_proxy.cpp
+++ b/src/v4l2/v4l2_camera_proxy.cpp
@@ -22,6 +22,8 @@
 #include <libcamera/base/utils.h>
 
 #include <libcamera/camera.h>
+#include <libcamera/controls.h>
+#include <libcamera/control_ids.h>
 #include <libcamera/formats.h>
 
 #include "libcamera/internal/v4l2_pixelformat.h"
@@ -33,6 +35,7 @@
 #define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
 
 using namespace libcamera;
+using namespace std::literals::chrono_literals;
 
 LOG_DECLARE_CATEGORY(V4L2Compat)
 
@@ -755,6 +758,23 @@ int V4L2CameraProxy::vidioc_streamoff(V4L2CameraFile *file, int *arg)
 	return ret;
 }
 
+int V4L2CameraProxy::vidioc_s_parm(V4L2CameraFile *file, struct v4l2_streamparm *arg)
+{
+	LOG(V4L2Compat, Debug)
+		<< "[" << file->description() << "] " << __func__ << "()";
+
+	if (!validateBufferType(arg->type))
+		return -EINVAL;
+
+	struct v4l2_fract *timeperframe = &arg->parm.capture.timeperframe;
+	utils::Duration frameDuration = 1.0s * timeperframe->numerator / timeperframe->denominator;
+
+	int64_t uDuration = frameDuration.get<std::micro>();
+	vcam_->controls().set(controls::FrameDurationLimits, { uDuration, uDuration });
+
+	return 0;
+}
+
 const std::set<unsigned long> V4L2CameraProxy::supportedIoctls_ = {
 	VIDIOC_QUERYCAP,
 	VIDIOC_ENUM_FRAMESIZES,
@@ -775,6 +795,7 @@ const std::set<unsigned long> V4L2CameraProxy::supportedIoctls_ = {
 	VIDIOC_EXPBUF,
 	VIDIOC_STREAMON,
 	VIDIOC_STREAMOFF,
+	VIDIOC_S_PARM,
 };
 
 int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long longRequest, void *arg)
@@ -862,6 +883,9 @@ int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long longRequest, void
 	case VIDIOC_STREAMOFF:
 		ret = vidioc_streamoff(file, static_cast<int *>(arg));
 		break;
+	case VIDIOC_S_PARM:
+		ret = vidioc_s_parm(file, static_cast<struct v4l2_streamparm *>(arg));
+		break;
 	default:
 		ret = -ENOTTY;
 		break;
diff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h
index 3d8784dfdbf1..c957db5349cc 100644
--- a/src/v4l2/v4l2_camera_proxy.h
+++ b/src/v4l2/v4l2_camera_proxy.h
@@ -67,6 +67,7 @@ private:
 	int vidioc_expbuf(V4L2CameraFile *file, struct v4l2_exportbuffer *arg);
 	int vidioc_streamon(V4L2CameraFile *file, int *arg);
 	int vidioc_streamoff(V4L2CameraFile *file, int *arg);
+	int vidioc_s_parm(V4L2CameraFile *file, struct v4l2_streamparm *arg);
 
 	bool hasOwnership(V4L2CameraFile *file);
 	int acquire(V4L2CameraFile *file);
