From patchwork Wed Sep 16 14:39:41 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jai Luthra X-Patchwork-Id: 28305 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 197A8BDE6B for ; Wed, 16 Sep 2026 14:40:37 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id AEF1F68701; Wed, 16 Sep 2026 16:40:36 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="qLhHqX+D"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E567C686F0 for ; Wed, 16 Sep 2026 16:40:35 +0200 (CEST) Received: from mail.ideasonboard.com (unknown [IPv6:2401:4900:1c69:85de:a3c9:9357:b164:bf0b]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 22B6B558; Wed, 16 Sep 2026 16:38:53 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1789569534; bh=5xjRr/fvqvnBv7bThBZIMjip4JxhXOHVEAl5K/cg8bI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=qLhHqX+DAallCcsRzkq2zvz4uR8CmgZ2bVlxtHrLboC8pK572SEAfahggmoqJuMiA cBf1vmcCkuV33vgom0hYOFrSODkMzFerpQb+PwXtiO+Yrj4aLZ11wPsLi5bSfe9kyu jiDk4vVfWaroSGt+Pa5U7ZKXrCUe2p74VS+uKDpc= From: Jai Luthra Date: Wed, 16 Sep 2026 20:09:41 +0530 Subject: [PATCH v3 5/8] pipeline: rpi: vc4: Configure format on Unicam subdev MIME-Version: 1.0 Message-Id: <20260916-pi4-upstream-v3-5-4666708d8829@ideasonboard.com> References: <20260916-pi4-upstream-v3-0-4666708d8829@ideasonboard.com> In-Reply-To: <20260916-pi4-upstream-v3-0-4666708d8829@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck , David Plowman , Laurent Pinchart , Kieran Bingham , Paul Elder , Jai Luthra X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=7539; i=jai.luthra@ideasonboard.com; h=from:subject:message-id; bh=cIjOb6OQhWDt9bzS/EqkCFdby6eEyeP5Ey4QQOklY0k=; b=owEBbQKS/ZANAwAKAUPekfkkmnFFAcsmYgBqqqpBKWmuLWzVmEkHE/UaVLiDw4ZRbCmUIw0+c LqL0MiV0fGJAjMEAAEKAB0WIQRN4NgY5dV16NRar8VD3pH5JJpxRQUCaqqqQQAKCRBD3pH5JJpx Rd+yEACLuNCfee5xiQIObbX1CvFXLyxyWqJYNmMSj8fba8ECIBx/NYnaNfbLjHfx2JFIOcaT9El P7eR8vEB4mNGy3KFbq8nSM4JGGHZASFvMkQV3d/I9WIYX1EMFtW2ft+7KZ+Kh+zsHeg1Yd7Sij8 /QWtveRb+QxEHZqHI7P8cZCEAHBwcDUyK2f+plXd6pXxEFtYRGIaycqG2J9KiL0ThF2ZRCY831I MPpqWQYysREuRCI0mzDV5JaHtYgdCzAUZbiMEkLuUdcZDZXPFJVB6n4W2ZG67YfaAjX/BT7NuEK QdAYMKlNEiK57wgem/wghgZQFqnblAHMXHX/30RKUz/Gbbrm6Dxf5mfq9sXSXqIPGEjmX/56hrP t9ltZhvfhzHX7EXkIU85o1mlBpb112N9GI9WWcyIkYvL0Ni+xy4sjHnkPGFrZRkfuE3yIsVT8u5 UXiGrtKt7B9OWjNM0qCEu/roZDHE0+cpUzt2bqOx0ZNLhNajbfTXcEx4Hz/z9QK06LNiCjwF116 FltqWTFkMGhM2gkfquFPSUw3IrbzJIbta3Skx8NDwmohrNUofi9QkkfnV4jMK+C2Rqxka4tzsrW d5zKVD6H+xa1QCjRPM6GsBK6S8LY73mG1VBPOBN6ccW+Yk1M/wtt6vq7eUkOOUo1dAysCMDCmVH c+gZDlPhVb7h5Cw== 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 Reviewed-by: Paul Elder Tested-by: Paul Elder 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 dca2c1c12549..27cd59248451 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 @@ -84,6 +89,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_; @@ -204,7 +211,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 " @@ -316,16 +323,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. */ @@ -364,6 +374,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]); @@ -372,7 +386,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; } @@ -543,9 +557,54 @@ int Vc4CameraData::platformPipelineConfigure(const std::unique_ptr &r 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; @@ -569,14 +628,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;