diff --git a/include/libcamera/internal/converter/converter_v4l2_m2m.h b/include/libcamera/internal/converter/converter_v4l2_m2m.h
index bc85bff7a07b..6d2453bb9e06 100644
--- a/include/libcamera/internal/converter/converter_v4l2_m2m.h
+++ b/include/libcamera/internal/converter/converter_v4l2_m2m.h
@@ -24,6 +24,7 @@
 
 namespace libcamera {
 
+class ControlList;
 class FrameBuffer;
 class MediaDevice;
 class Size;
@@ -73,6 +74,8 @@ public:
 	std::pair<Rectangle, Rectangle> inputCropBounds() override { return inputCropBounds_; }
 	std::pair<Rectangle, Rectangle> inputCropBounds(const Stream *stream) override;
 
+	int applyControls(const Stream *stream, ControlList &ctrls, const V4L2Request *request = nullptr);
+
 	int allocateRequests(unsigned int count,
 			     std::vector<std::unique_ptr<V4L2Request>> *requests);
 
@@ -94,6 +97,8 @@ private:
 		int start();
 		void stop();
 
+		int applyControls(ControlList &ctrls, const V4L2Request *request = nullptr);
+
 		int queueBuffers(FrameBuffer *input, FrameBuffer *output,
 				 const V4L2Request *request = nullptr);
 
diff --git a/src/libcamera/converter/converter_v4l2_m2m.cpp b/src/libcamera/converter/converter_v4l2_m2m.cpp
index e57db8a438ab..48e49f143cf1 100644
--- a/src/libcamera/converter/converter_v4l2_m2m.cpp
+++ b/src/libcamera/converter/converter_v4l2_m2m.cpp
@@ -18,6 +18,7 @@
 #include <libcamera/base/signal.h>
 #include <libcamera/base/utils.h>
 
+#include <libcamera/controls.h>
 #include <libcamera/framebuffer.h>
 #include <libcamera/geometry.h>
 #include <libcamera/stream.h>
@@ -251,6 +252,20 @@ void V4L2M2MConverter::V4L2M2MStream::captureBufferReady(FrameBuffer *buffer)
 	converter_->outputBufferReady.emit(buffer);
 }
 
+/**
+ * \brief Apply controls
+ * \param[in] ctrls The controls to apply
+ * \param[in] request An optional request
+ *
+ * \return 0 on success or a negative error code otherwise
+ * \see V4L2Device::setControls()
+ */
+int V4L2M2MConverter::V4L2M2MStream::applyControls(ControlList &ctrls,
+						   const V4L2Request *request)
+{
+	return m2m_->capture()->setControls(&ctrls, request);
+}
+
 /* -----------------------------------------------------------------------------
  * V4L2M2MConverter
  */
@@ -744,6 +759,25 @@ int V4L2M2MConverter::queueBuffers(FrameBuffer *input,
 	return 0;
 }
 
+/**
+ * \brief Apply controls
+ * \param[in] stream The stream on which to apply the controls
+ * \param[in] ctrls The controls to apply
+ * \param[in] request An optional request
+ *
+ * \return 0 on success or a negative error code otherwise
+ * \see V4L2Device::setControls()
+ */
+int V4L2M2MConverter::applyControls(const Stream *stream, ControlList &ctrls,
+				    const V4L2Request *request)
+{
+	auto iter = streams_.find(stream);
+	if (iter == streams_.end())
+		return -EINVAL;
+
+	return iter->second->applyControls(ctrls, request);
+}
+
 /**
  * \copydoc libcamera::MediaDevice::allocateRequests
  */
