From patchwork Tue Feb 10 08:25:46 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jai Luthra X-Patchwork-Id: 26117 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id C81D2BD78E for ; Tue, 10 Feb 2026 08:26:21 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7ED2F6218A; Tue, 10 Feb 2026 09:26:21 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Q/vmT5GI"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D9011620FA for ; Tue, 10 Feb 2026 09:26:19 +0100 (CET) Received: from mail.ideasonboard.com (unknown [IPv6:2401:4900:1c30:2edd:807a:f3c0:8d1b:28a]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 2D6F513BE; Tue, 10 Feb 2026 09:25:32 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1770711933; bh=OrK6JcYDdjORHifWcAmGdAks9aF31390jgRrY9Tobm8=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Q/vmT5GIo1n03a3+aPr2HLiiTHCZDPx99PcM3IeL+/ctDh3MQU9M/2CFYBk6KR0ku dkwqkiEanJAHDBYDwfGBBy3Ya9Q+OZHfP1PS5yp/AF8awHhimKVXJs0DBTYGyAJd2u fvxwFKLlkpj/B45oNwbxaN1TLAAZPe+PG2qWKN4Y= From: Jai Luthra Date: Tue, 10 Feb 2026 13:55:46 +0530 Subject: [PATCH 1/3] pipeline: raspberrypi: vc4: Configure format on Unicam subdev MIME-Version: 1.0 Message-Id: <20260210-pi4-upstream-v1-1-279841c15fba@ideasonboard.com> References: <20260210-pi4-upstream-v1-0-279841c15fba@ideasonboard.com> In-Reply-To: <20260210-pi4-upstream-v1-0-279841c15fba@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck , David Plowman , Laurent Pinchart , Kieran Bingham , Jacopo Mondi , Daniel Scally , Jai Luthra X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=7487; i=jai.luthra@ideasonboard.com; h=from:subject:message-id; bh=Jbb3fTOUZ9G6JH4sr+GgvFPhuftsbpV9f0J4YxTZi0k=; b=owEBbQKS/ZANAwAKAUPekfkkmnFFAcsmYgBpiuudhbFJIuaO9qNwJhEfb+GWhE2ki6cyy2+C+ 5ITQSUVBfOJAjMEAAEKAB0WIQRN4NgY5dV16NRar8VD3pH5JJpxRQUCaYrrnQAKCRBD3pH5JJpx RR95EAC8iDy4akoPLcNUjFqlN3bRwy+VHUUZCDLT3xs2qAIO4iuYft6Y6uHsRXz6Q0kWGUQ9sW+ l5EUHMptha/WWiWK+sqI6p5qcNHQENlfNADHRbZTwk5EzHa5chFanh/jkGBQ5NeG1NRTziFCNnh zeCHcIbwa1A9+fyoMLgt4RZb/sPI8mneW+x5ypjuZU9QHaZa8ry8yxBPQSfCd0+YfPmPL8kXMvx OastyeoX3UavHVFnpzLpsfwx0tpaOhNDrJu/wATgj5mw+w6UeWM/sxT+HW1dzCRDbV/xqlvFTOi BMn6BDdNEqv4BACZgbtX9+vGZzxscV7dzBJ2ptwat2J3DkOm2Swn5ToQ8kxxAmkvASAe3HLEC3T aixi4azV3a1oSUgxiHfQ0Mnj4NJc+qnpMuK2wFBQJR5BKfOmEFqRLKSRA/OIZ5kV0GqaYXNY2lf iQnLk4VGW5upOwXh7Z51p8YjssURFghCVUHjsZKjPO3d+aR8ZvxS2VyuFV2qa7Qxi8nST2wkdJe ej9xwrfcNeaRBjSk3Hv/KvFq4qG3CzgXzyOeLCHbwPI/1+DyTzRttNwPVpvny0m4SpXWglG6HH4 wkfFrTvvUvZxO7RyzWgmE/cSL35sEfNc0S2cmnDQd0i7IpQe6gD90jI//TlmH9KRsSEJIL604L2 ZJ8tf8VhzBUONkQ== X-Developer-Key: i=jai.luthra@ideasonboard.com; a=openpgp; fpr=4DE0D818E5D575E8D45AAFC543DE91F9249A7145 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" From: Laurent Pinchart 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 Signed-off-by: Jai Luthra --- src/libcamera/pipeline/rpi/vc4/vc4.cpp | 100 ++++++++++++++++++++++++++++++--- 1 file changed, 91 insertions(+), 9 deletions(-) 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 unicamSubdev_; + /* Array of Unicam and ISP device streams and associated buffers/streams. */ RPi::Device unicam_; RPi::Device isp_; @@ -203,7 +210,7 @@ bool PipelineHandlerVc4::match(DeviceEnumerator *enumerator) std::unique_ptr cameraData = std::make_unique(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 &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(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 &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 &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 & 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 &rawStreams = rpiConfig->rawStreams_; const std::vector &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;