diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
index da925ba5f793..81e0bc1b8f7a 100644
--- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
+++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
@@ -5,6 +5,8 @@
  * vc4.cpp - Pipeline handler for VC4-based Raspberry Pi devices
  */
 
+#include <memory>
+
 #include <linux/bcm2835-isp.h>
 #include <linux/v4l2-controls.h>
 #include <linux/videodev2.h>
@@ -12,6 +14,7 @@
 #include <libcamera/formats.h>
 
 #include "libcamera/internal/device_enumerator.h"
+#include "libcamera/internal/v4l2_subdevice.h"
 
 #include "../common/pipeline_base.h"
 #include "../common/rpi_stream.h"
@@ -32,6 +35,10 @@ namespace {
 enum class Unicam : unsigned int { Image, Embedded };
 enum class Isp : unsigned int { Input, Output0, Output1, Stats };
 
+static constexpr unsigned int kUnicamSinkPad = 0;
+static constexpr unsigned int kUnicamSourceImagePad = 1;
+static constexpr unsigned int kUnicamSourceMetadataPad = 2;
+
 } /* namespace */
 
 class Vc4CameraData final : public RPi::CameraData
@@ -82,6 +89,8 @@ public:
 	void setIspControls(const ControlList &controls);
 	void setCameraTimeout(uint32_t maxFrameLengthMs);
 
+	std::unique_ptr<V4L2Subdevice> unicamSubdev_;
+
 	/* Array of Unicam and ISP device streams and associated buffers/streams. */
 	RPi::Device<Unicam, 2> unicam_;
 	RPi::Device<Isp, 4> isp_;
@@ -310,6 +319,7 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
 	if (!data->dmaHeap_.isValid())
 		return -ENOMEM;
 
+	MediaEntity *unicamSubdev = unicam->getEntityByName("unicam");
 	MediaEntity *unicamImage = unicam->getEntityByName("unicam-image");
 	MediaEntity *unicamEmbedded = unicam->getEntityByName("unicam-embedded");
 	MediaEntity *ispOutput0 = isp->getEntityByName("bcm2835-isp0-output0");
@@ -321,7 +331,13 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
 	    !ispOutput0 || !ispCapture1 || !ispCapture2 || !ispCapture3)
 		return -ENOENT;
 
-	/* Create the unicam video streams. */
+	if (!unicamSubdev) {
+		LOG(RPI, Error) << "Downstream Unicam driver not supported, please update your kernel!";
+		return -EINVAL;
+	}
+
+	/* Create the unicam subdev and video streams. */
+	data->unicamSubdev_ = std::make_unique<V4L2Subdevice>(unicamSubdev);
 	data->unicam_[Unicam::Image] = RPi::Stream("Unicam Image", unicamImage);
 	data->unicam_[Unicam::Embedded] = RPi::Stream("Unicam Embedded", unicamEmbedded);
 
@@ -350,6 +366,10 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
 	 * The below grouping is just for convenience so that we can easily
 	 * iterate over all streams in one go.
 	 */
+	int ret = data->unicamSubdev_->open();
+	if (ret < 0)
+		return ret;
+
 	data->streams_.push_back(&data->unicam_[Unicam::Image]);
 	if (data->sensorMetadata_)
 		data->streams_.push_back(&data->unicam_[Unicam::Embedded]);
@@ -358,16 +378,11 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
 		data->streams_.push_back(&stream);
 
 	for (auto stream : data->streams_) {
-		int ret = stream->dev()->open();
+		ret = stream->dev()->open();
 		if (ret)
 			return ret;
 	}
 
-	if (!data->unicam_[Unicam::Image].dev()->caps().hasMediaController()) {
-		LOG(RPI, Error) << "Unicam driver does not use the MediaController, please update your kernel!";
-		return -EINVAL;
-	}
-
 	/* Write up all the IPA connections. */
 	data->ipa_->processStatsComplete.connect(data, &Vc4CameraData::processStatsComplete);
 	data->ipa_->prepareIspComplete.connect(data, &Vc4CameraData::prepareIspComplete);
@@ -530,7 +545,50 @@ int Vc4CameraData::platformPipelineConfigure(const std::unique_ptr<YamlObject> &
 int Vc4CameraData::platformConfigure(const RPi::RPiCameraConfiguration *rpiConfig)
 {
 	/*
-	 * 1. Configure the Unicam video devices.
+	 * 1. Configure the Unicam subdev.
+	 *
+	 * Start by setting up routes, and then set the formats on the sink pad
+	 * streams. They will be automatically propagated to the source pads by
+	 * the kernel.
+	 */
+
+	const V4L2Subdevice::Stream imageStream{
+		kUnicamSinkPad,
+		sensor_->imageStream().stream
+	};
+	const V4L2Subdevice::Stream embeddedDataStream{
+		kUnicamSinkPad,
+		sensor_->embeddedDataStream().value_or(V4L2Subdevice::Stream{}).stream
+	};
+
+	V4L2Subdevice::Routing routing;
+
+	routing.emplace_back(imageStream, V4L2Subdevice::Stream{ kUnicamSourceImagePad, 0 },
+			     V4L2_SUBDEV_ROUTE_FL_ACTIVE);
+
+	if (sensorMetadata_)
+		routing.emplace_back(embeddedDataStream,
+				     V4L2Subdevice::Stream{ kUnicamSourceMetadataPad, 0 },
+				     V4L2_SUBDEV_ROUTE_FL_ACTIVE);
+
+	int ret = unicamSubdev_->setRouting(&routing);
+	if (ret)
+		return ret;
+
+	V4L2SubdeviceFormat subdevFormat = rpiConfig->sensorFormat_;
+	ret = unicamSubdev_->setFormat(imageStream, &subdevFormat);
+	if (ret)
+		return ret;
+
+	if (sensorMetadata_) {
+		subdevFormat = sensor_->embeddedDataFormat();
+		ret = unicamSubdev_->setFormat(embeddedDataStream, &subdevFormat);
+		if (ret)
+			return ret;
+	}
+
+	/*
+	 * 2. Configure the Unicam video devices.
 	 */
 
 	V4L2VideoDevice *unicam = unicam_[Unicam::Image].dev();
@@ -553,7 +611,7 @@ int Vc4CameraData::platformConfigure(const RPi::RPiCameraConfiguration *rpiConfi
 								     BayerFormat::Packing::CSI2);
 	}
 
-	int ret = unicam->setFormat(&unicamFormat);
+	ret = unicam->setFormat(&unicamFormat);
 	if (ret)
 		return ret;
 
@@ -581,7 +639,7 @@ int Vc4CameraData::platformConfigure(const RPi::RPiCameraConfiguration *rpiConfi
 	}
 
 	/*
-	 * 2. Configure the ISP.
+	 * 3. Configure the ISP.
 	 */
 
 	ret = isp_[Isp::Input].dev()->setFormat(&unicamFormat);
