[1/3] pipeline: raspberrypi: vc4: Configure format on Unicam subdev
diff mbox series

Message ID 20260210-pi4-upstream-v1-1-279841c15fba@ideasonboard.com
State New
Headers show
Series
  • Raspberry Pi: Update VC4 pipeline for upstream
Related show

Commit Message

Jai Luthra Feb. 10, 2026, 8:25 a.m. UTC
From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

The mainline Unicam driver creates a V4L2 subdevice, which needs to be
configured. Create and open a corresponding V4L2Subdevice instance and
configure the format on its sink pad in the platformConfigure()
function.

Presence of the Unicam subdev is required, to avoid extra complexity.
This drops support for the driver from the Raspberry Pi downstream
kernel. Users are expected to update their kernel to use the mainline
Unicam driver.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com>
---
 src/libcamera/pipeline/rpi/vc4/vc4.cpp | 100 ++++++++++++++++++++++++++++++---
 1 file changed, 91 insertions(+), 9 deletions(-)

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
index 8a80439e9082241782bdf1a9b46445caf64f2acd..cd1ec4f486990bf620f44bc6cd3ef6ae90c91327 100644
--- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
+++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
@@ -15,6 +15,7 @@ 
 
 #include "libcamera/internal/device_enumerator.h"
 #include "libcamera/internal/dma_buf_allocator.h"
+#include "libcamera/internal/v4l2_subdevice.h"
 
 #include "../common/pipeline_base.h"
 #include "../common/rpi_stream.h"
@@ -33,6 +34,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
@@ -83,6 +88,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_;
@@ -203,7 +210,7 @@  bool PipelineHandlerVc4::match(DeviceEnumerator *enumerator)
 
 			std::unique_ptr<RPi::CameraData> cameraData = std::make_unique<Vc4CameraData>(this);
 			int ret = RPi::PipelineHandlerBase::registerCamera(cameraData,
-									   unicamDevice, "unicam-image",
+									   unicamDevice, "unicam",
 									   ispDevice, entity);
 			if (ret)
 				LOG(RPI, Error) << "Failed to register camera "
@@ -315,16 +322,19 @@  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 *ispOutput0 = isp->getEntityByName("bcm2835-isp0-output0");
 	MediaEntity *ispCapture1 = isp->getEntityByName("bcm2835-isp0-capture1");
 	MediaEntity *ispCapture2 = isp->getEntityByName("bcm2835-isp0-capture2");
 	MediaEntity *ispCapture3 = isp->getEntityByName("bcm2835-isp0-capture3");
 
-	if (!unicamImage || !ispOutput0 || !ispCapture1 || !ispCapture2 || !ispCapture3)
+	if (!unicamSubdev || !unicamImage || !ispOutput0 || !ispCapture1 ||
+	    !ispCapture2 || !ispCapture3)
 		return -ENOENT;
 
-	/* Locate and open the unicam video streams. */
+	/* Create the unicam subdev and video streams. */
+	data->unicamSubdev_ = std::make_unique<V4L2Subdevice>(unicamSubdev);
 	data->unicam_[Unicam::Image] = RPi::Stream("Unicam Image", unicamImage);
 
 	/* An embedded data node will not be present if the sensor does not support it. */
@@ -363,6 +373,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]);
@@ -371,7 +385,7 @@  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;
 	}
@@ -542,9 +556,54 @@  int Vc4CameraData::platformPipelineConfigure(const std::unique_ptr<YamlObject> &
 
 int Vc4CameraData::platformConfigure(const RPi::RPiCameraConfiguration *rpiConfig)
 {
+	/*
+	 * 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.
+	 */
 	const std::vector<StreamParams> &rawStreams = rpiConfig->rawStreams_;
 	const std::vector<StreamParams> &outStreams = rpiConfig->outStreams_;
-	int ret;
 
 	V4L2VideoDevice *unicam = unicam_[Unicam::Image].dev();
 	V4L2DeviceFormat unicamFormat;
@@ -568,14 +627,37 @@  int Vc4CameraData::platformConfigure(const RPi::RPiCameraConfiguration *rpiConfi
 	if (ret)
 		return ret;
 
-	ret = isp_[Isp::Input].dev()->setFormat(&unicamFormat);
-	if (ret)
-		return ret;
-
 	LOG(RPI, Info) << "Sensor: " << sensor_->id()
 		       << " - Selected sensor format: " << rpiConfig->sensorFormat_
 		       << " - Selected unicam format: " << unicamFormat;
 
+	/*
+	 * Configure the Unicam embedded data output format only if the sensor
+	 * supports it.
+	 */
+	if (sensorMetadata_) {
+		V4L2SubdeviceFormat embeddedFormat = sensor_->embeddedDataFormat();
+		V4L2DeviceFormat format{};
+		format.fourcc = V4L2PixelFormat(V4L2_META_FMT_SENSOR_DATA);
+		format.planes[0].size = embeddedFormat.size.width * embeddedFormat.size.height;
+
+		LOG(RPI, Debug) << "Setting embedded data format " << format;
+		ret = unicam_[Unicam::Embedded].dev()->setFormat(&format);
+		if (ret) {
+			LOG(RPI, Error) << "Failed to set format on Unicam embedded: "
+					<< format;
+			return ret;
+		}
+	}
+
+	/*
+	 * 3. Configure the ISP.
+	 */
+
+	ret = isp_[Isp::Input].dev()->setFormat(&unicamFormat);
+	if (ret)
+		return ret;
+
 	/* Use a sensible small default size if no output streams are configured. */
 	Size maxSize = outStreams.empty() ? Size(320, 240) : outStreams[0].cfg->size;
 	V4L2DeviceFormat format;