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; From patchwork Tue Feb 10 08:25:47 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jai Luthra X-Patchwork-Id: 26118 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 C02B2BD78E for ; Tue, 10 Feb 2026 08:26:27 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1EC4A6218D; Tue, 10 Feb 2026 09:26:27 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="HmHMju4u"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id AC4D262189 for ; Tue, 10 Feb 2026 09:26:25 +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 08CC4E47; Tue, 10 Feb 2026 09:25:38 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1770711939; bh=ysN0DRl7vWo0cl4TEV65LG5MCLsPrpcEsV5nYZ/PW4A=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=HmHMju4uOvzA0nXGBXz+OXBroWt/nF3CkRLUF5lZL/yRMTkVWGRHWmnlU13Ift+Sg TW6BGPxQCi3ZPw3ClznOqfwRF/btDBOScYF7nT6bd+wm1N2KbnceXflwgyEo0Ws1zu KBSKYDrCm1K42TQQrPMiYjqNlny+uXKgzApHdXYM= From: Jai Luthra Date: Tue, 10 Feb 2026 13:55:47 +0530 Subject: [PATCH 2/3] include: uapi: Drop controls for VC4 ISP params MIME-Version: 1.0 Message-Id: <20260210-pi4-upstream-v1-2-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=16286; i=jai.luthra@ideasonboard.com; h=from:subject:message-id; bh=ysN0DRl7vWo0cl4TEV65LG5MCLsPrpcEsV5nYZ/PW4A=; b=owEBbQKS/ZANAwAKAUPekfkkmnFFAcsmYgBpiuueU/whMOUuxZgi3hq4XMKsw19hQA2PPidlr Y4qHF81szeJAjMEAAEKAB0WIQRN4NgY5dV16NRar8VD3pH5JJpxRQUCaYrrngAKCRBD3pH5JJpx RU+bEACL0dE3YltRH7fUjkQjGYsuxcJhB09LR5hbHp+u0ey6u7saEvM/LXwEfeajvQ5ect2lLO6 LEWsKZ42jxlHr1gYKpCLAprVZWPU9RzrXU4L9ofK0Q3Tn6YuAdoeSenSMN37JS+RrUSfKm2pc28 Ttrg8fI3lBmeoIAPb+EjIkPuieC+ZLr4q8v2oq9CdrMCVt05zYITnUW8fu8iwYIKYaUtGgmJGDc 3xAwZeFXg7kOaLsxPulLkRdc/mj22fGA3cjEq2scvb9gBDMx9VBarPbJZkZTT909V9tbMIUQa0E VPrvBsW9gEm8BGzYJOVBknwUq9ln4kzGcaW908NiUqau8/RK9W+DtgENeDHOX6kAj5jWtHW4tnD GNYS6PAqhpePS6LcHj9Gj/KpHJ71F+NqHzmTK8d5HdGScgO11pGfFfTK/dxiUmNS3z1VTExEH4b tf0WY5ltS7SmvfNRblRRQnuUSvtnCK7zLpiEfyi5vnCRGo2rpVkeZeiBNqO87jU4tS698tUOWUb GxDaw6J+VtOlaN4FAeQmmbP16jK90QdpCkDHu46waiRK6px8lrK78pnPzOd8tVBwUJNPUVm6w+a sPcbL0K7eYyoSHTXTpLblqVAkXrfD3HToRukZ7vSHgkJtIO+aIbz8nzP00t9g0mJmPv9Oe24zss 0bcaULuhK9hVMpA== 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" Update the BCM2835 ISP kernel header to replace custom V4L2 controls with an extensible parameter buffer interface, aligning with the approach used by other upstream ISP drivers (rkisp1, mali-c55). Add new bcm2835_isp_awb_gains and bcm2835_isp_digital_gain structures for AWB and digital gain, which were previously configured through standard V4L2 controls (V4L2_CID_RED_BALANCE, V4L2_CID_BLUE_BALANCE, V4L2_CID_DIGITAL_GAIN) and now need dedicated parameter blocks. Register V4L2_META_FMT_BCM2835_ISP_PARAMS as the fourcc for the new metadata format in videodev2.h, and drop the control ID base from the v4l2-controls.h. Signed-off-by: Jai Luthra --- include/linux/bcm2835-isp.h | 276 +++++++++++++++++++++++++++++++++++------- include/linux/v4l2-controls.h | 3 - include/linux/videodev2.h | 3 +- 3 files changed, 232 insertions(+), 50 deletions(-) diff --git a/include/linux/bcm2835-isp.h b/include/linux/bcm2835-isp.h index 5f0f78e3e93f994487a1123263d2b26e7ce5b947..0f3c1b4dbb06313ce661c492cbd023e93f005d54 100644 --- a/include/linux/bcm2835-isp.h +++ b/include/linux/bcm2835-isp.h @@ -13,26 +13,8 @@ #ifndef __BCM2835_ISP_H_ #define __BCM2835_ISP_H_ -#include - -#define V4L2_CID_USER_BCM2835_ISP_CC_MATRIX \ - (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0001) -#define V4L2_CID_USER_BCM2835_ISP_LENS_SHADING \ - (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0002) -#define V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL \ - (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0003) -#define V4L2_CID_USER_BCM2835_ISP_GEQ \ - (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0004) -#define V4L2_CID_USER_BCM2835_ISP_GAMMA \ - (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0005) -#define V4L2_CID_USER_BCM2835_ISP_DENOISE \ - (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0006) -#define V4L2_CID_USER_BCM2835_ISP_SHARPEN \ - (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0007) -#define V4L2_CID_USER_BCM2835_ISP_DPC \ - (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0008) -#define V4L2_CID_USER_BCM2835_ISP_CDN \ - (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0009) +#include + /* * All structs below are directly mapped onto the equivalent structs in * drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h @@ -62,8 +44,7 @@ struct bcm2835_isp_ccm { }; /** - * struct bcm2835_isp_custom_ccm - Custom CCM applied with the - * V4L2_CID_USER_BCM2835_ISP_CC_MATRIX ctrl. + * struct bcm2835_isp_custom_ccm - Custom CCM configuration. * * @enabled: Enable custom CCM. * @ccm: Custom CCM coefficients and offsets. @@ -75,8 +56,7 @@ struct bcm2835_isp_custom_ccm { /** * enum bcm2835_isp_gain_format - format of the gains in the lens shading - * tables used with the - * V4L2_CID_USER_BCM2835_ISP_LENS_SHADING ctrl. + * tables. * * @GAIN_FORMAT_U0P8_1: Gains are u0.8 format, starting at 1.0 * @GAIN_FORMAT_U1P7_0: Gains are u1.7 format, starting at 0.0 @@ -99,9 +79,7 @@ enum bcm2835_isp_gain_format { }; /** - * struct bcm2835_isp_lens_shading - Lens shading tables supplied with the - * V4L2_CID_USER_BCM2835_ISP_LENS_SHADING - * ctrl. + * struct bcm2835_isp_lens_shading - Lens shading tables. * * @enabled: Enable lens shading. * @grid_cell_size: Size of grid cells in samples (16, 32, 64, 128 or 256). @@ -128,13 +106,13 @@ struct bcm2835_isp_lens_shading { }; /** - * struct bcm2835_isp_black_level - Sensor black level set with the - * V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL ctrl. + * struct bcm2835_isp_black_level - Sensor black level configuration. * * @enabled: Enable black level. * @black_level_r: Black level for red channel. * @black_level_g: Black level for green channels. * @black_level_b: Black level for blue channel. + * @padding: Unused padding. */ struct bcm2835_isp_black_level { __u32 enabled; @@ -145,8 +123,7 @@ struct bcm2835_isp_black_level { }; /** - * struct bcm2835_isp_geq - Green equalisation parameters set with the - * V4L2_CID_USER_BCM2835_ISP_GEQ ctrl. + * struct bcm2835_isp_geq - Green equalisation parameters. * * @enabled: Enable green equalisation. * @offset: Fixed offset of the green equalisation threshold. @@ -161,13 +138,12 @@ struct bcm2835_isp_geq { #define BCM2835_NUM_GAMMA_PTS 33 /** - * struct bcm2835_isp_gamma - Gamma parameters set with the - * V4L2_CID_USER_BCM2835_ISP_GAMMA ctrl. + * struct bcm2835_isp_gamma - Gamma parameters. * * @enabled: Enable gamma adjustment. - * @X: X values of the points defining the gamma curve. + * @x: X values of the points defining the gamma curve. * Values should be scaled to 16 bits. - * @Y: Y values of the points defining the gamma curve. + * @y: Y values of the points defining the gamma curve. * Values should be scaled to 16 bits. */ struct bcm2835_isp_gamma { @@ -179,9 +155,9 @@ struct bcm2835_isp_gamma { /** * enum bcm2835_isp_cdn_mode - Mode of operation for colour denoise. * - * @CDN_MODE_FAST: Fast (but lower quality) colour denoise + * @CDN_MODE_FAST: Fast (but lower quality) colour denoise * algorithm, typically used for video recording. - * @CDN_HIGH_QUALITY: High quality (but slower) colour denoise + * @CDN_MODE_HIGH_QUALITY: High quality (but slower) colour denoise * algorithm, typically used for stills capture. */ enum bcm2835_isp_cdn_mode { @@ -190,11 +166,10 @@ enum bcm2835_isp_cdn_mode { }; /** - * struct bcm2835_isp_cdn - Colour denoise parameters set with the - * V4L2_CID_USER_BCM2835_ISP_CDN ctrl. + * struct bcm2835_isp_cdn - Colour denoise parameters. * * @enabled: Enable colour denoise. - * @cdn_mode: Colour denoise operating mode (see enum &bcm2835_isp_cdn_mode) + * @mode: Colour denoise operating mode (see enum &bcm2835_isp_cdn_mode) */ struct bcm2835_isp_cdn { __u32 enabled; @@ -202,8 +177,7 @@ struct bcm2835_isp_cdn { }; /** - * struct bcm2835_isp_denoise - Denoise parameters set with the - * V4L2_CID_USER_BCM2835_ISP_DENOISE ctrl. + * struct bcm2835_isp_denoise - Denoise parameters. * * @enabled: Enable denoise. * @constant: Fixed offset of the noise threshold. @@ -218,8 +192,7 @@ struct bcm2835_isp_denoise { }; /** - * struct bcm2835_isp_sharpen - Sharpen parameters set with the - * V4L2_CID_USER_BCM2835_ISP_SHARPEN ctrl. + * struct bcm2835_isp_sharpen - Sharpen parameters. * * @enabled: Enable sharpening. * @threshold: Threshold at which to start sharpening pixels. @@ -247,8 +220,7 @@ enum bcm2835_isp_dpc_mode { }; /** - * struct bcm2835_isp_dpc - Defective pixel correction (DPC) parameters set - * with the V4L2_CID_USER_BCM2835_ISP_DPC ctrl. + * struct bcm2835_isp_dpc - Defective pixel correction (DPC) parameters. * * @enabled: Enable DPC. * @strength: DPC strength (see enum &bcm2835_isp_dpc_mode). @@ -258,6 +230,218 @@ struct bcm2835_isp_dpc { __u32 strength; }; +/** + * struct bcm2835_isp_awb_gains - AWB gains configuration. + * + * @r_gain: Red channel AWB gain. + * @b_gain: Blue channel AWB gain. + */ +struct bcm2835_isp_awb_gains { + struct bcm2835_isp_rational r_gain; + struct bcm2835_isp_rational b_gain; +}; + +/** + * struct bcm2835_isp_digital_gain - Digital gain configuration. + * + * @gain: Digital gain value. + */ +struct bcm2835_isp_digital_gain { + struct bcm2835_isp_rational gain; +}; + +/* + * BCM2835 ISP extensible parameters buffer definitions. + * + * The extensible parameters mechanism allows userspace to submit ISP + * configuration parameters as a buffer containing a series of tagged + * blocks rather than individual V4L2 controls. This enables atomic + * application of multiple parameters in a single operation. + */ + +/** + * enum bcm2835_isp_param_buffer_version - BCM2835 ISP parameters buffer version + * + * @BCM2835_ISP_PARAM_BUFFER_V1: First version of parameters buffer format + */ +enum bcm2835_isp_param_buffer_version { + BCM2835_ISP_PARAM_BUFFER_V1 = V4L2_ISP_PARAMS_VERSION_V1, +}; + +/** + * enum bcm2835_isp_param_block_type - BCM2835 ISP parameter block types + * + * This enumeration defines the types of parameters blocks that can be + * included in the extensible parameters buffer. Each block type corresponds + * to a specific ISP processing block configuration. + * + * @BCM2835_ISP_PARAM_BLOCK_BLACK_LEVEL: Black level configuration + * @BCM2835_ISP_PARAM_BLOCK_GEQ: Green equalisation configuration + * @BCM2835_ISP_PARAM_BLOCK_GAMMA: Gamma curve configuration + * @BCM2835_ISP_PARAM_BLOCK_DENOISE: Denoise configuration + * @BCM2835_ISP_PARAM_BLOCK_SHARPEN: Sharpening configuration + * @BCM2835_ISP_PARAM_BLOCK_DPC: Defective pixel correction configuration + * @BCM2835_ISP_PARAM_BLOCK_CDN: Colour denoise configuration + * @BCM2835_ISP_PARAM_BLOCK_CC_MATRIX: Colour correction matrix configuration + * @BCM2835_ISP_PARAM_BLOCK_LENS_SHADING: Lens shading table configuration + * @BCM2835_ISP_PARAM_BLOCK_AWB_GAINS: AWB gains configuration + * @BCM2835_ISP_PARAM_BLOCK_DIGITAL_GAIN: Digital gain configuration + */ +enum bcm2835_isp_param_block_type { + BCM2835_ISP_PARAM_BLOCK_BLACK_LEVEL, + BCM2835_ISP_PARAM_BLOCK_GEQ, + BCM2835_ISP_PARAM_BLOCK_GAMMA, + BCM2835_ISP_PARAM_BLOCK_DENOISE, + BCM2835_ISP_PARAM_BLOCK_SHARPEN, + BCM2835_ISP_PARAM_BLOCK_DPC, + BCM2835_ISP_PARAM_BLOCK_CDN, + BCM2835_ISP_PARAM_BLOCK_CC_MATRIX, + BCM2835_ISP_PARAM_BLOCK_LENS_SHADING, + BCM2835_ISP_PARAM_BLOCK_AWB_GAINS, + BCM2835_ISP_PARAM_BLOCK_DIGITAL_GAIN, +}; + +/** + * struct bcm2835_isp_params_black_level - Black level parameters block + * + * @header: Block header (type = BCM2835_ISP_PARAM_BLOCK_BLACK_LEVEL) + * @black_level: Black level configuration + */ +struct bcm2835_isp_params_black_level { + struct v4l2_isp_params_block_header header; + struct bcm2835_isp_black_level black_level; +} __attribute__((aligned(8))); + +/** + * struct bcm2835_isp_params_geq - Green equalisation parameters block + * + * @header: Block header (type = BCM2835_ISP_PARAM_BLOCK_GEQ) + * @geq: Green equalisation configuration + */ +struct bcm2835_isp_params_geq { + struct v4l2_isp_params_block_header header; + struct bcm2835_isp_geq geq; +} __attribute__((aligned(8))); + +/** + * struct bcm2835_isp_params_gamma - Gamma parameters block + * + * @header: Block header (type = BCM2835_ISP_PARAM_BLOCK_GAMMA) + * @gamma: Gamma curve configuration + */ +struct bcm2835_isp_params_gamma { + struct v4l2_isp_params_block_header header; + struct bcm2835_isp_gamma gamma; +} __attribute__((aligned(8))); + +/** + * struct bcm2835_isp_params_denoise - Denoise parameters block + * + * @header: Block header (type = BCM2835_ISP_PARAM_BLOCK_DENOISE) + * @denoise: Denoise configuration + */ +struct bcm2835_isp_params_denoise { + struct v4l2_isp_params_block_header header; + struct bcm2835_isp_denoise denoise; +} __attribute__((aligned(8))); + +/** + * struct bcm2835_isp_params_sharpen - Sharpen parameters block + * + * @header: Block header (type = BCM2835_ISP_PARAM_BLOCK_SHARPEN) + * @sharpen: Sharpening configuration + */ +struct bcm2835_isp_params_sharpen { + struct v4l2_isp_params_block_header header; + struct bcm2835_isp_sharpen sharpen; +} __attribute__((aligned(8))); + +/** + * struct bcm2835_isp_params_dpc - Defective pixel correction parameters block + * + * @header: Block header (type = BCM2835_ISP_PARAM_BLOCK_DPC) + * @dpc: DPC configuration + */ +struct bcm2835_isp_params_dpc { + struct v4l2_isp_params_block_header header; + struct bcm2835_isp_dpc dpc; +} __attribute__((aligned(8))); + +/** + * struct bcm2835_isp_params_cdn - Colour denoise parameters block + * + * @header: Block header (type = BCM2835_ISP_PARAM_BLOCK_CDN) + * @cdn: Colour denoise configuration + */ +struct bcm2835_isp_params_cdn { + struct v4l2_isp_params_block_header header; + struct bcm2835_isp_cdn cdn; +} __attribute__((aligned(8))); + +/** + * struct bcm2835_isp_params_cc_matrix - Colour correction matrix parameters block + * + * @header: Block header (type = BCM2835_ISP_PARAM_BLOCK_CC_MATRIX) + * @ccm: Colour correction matrix configuration + */ +struct bcm2835_isp_params_cc_matrix { + struct v4l2_isp_params_block_header header; + struct bcm2835_isp_custom_ccm ccm; +} __attribute__((aligned(8))); + +/** + * struct bcm2835_isp_params_lens_shading - Lens shading parameters block + * + * @header: Block header (type = BCM2835_ISP_PARAM_BLOCK_LENS_SHADING) + * @ls: Lens shading configuration (includes dmabuf fd for table data) + */ +struct bcm2835_isp_params_lens_shading { + struct v4l2_isp_params_block_header header; + struct bcm2835_isp_lens_shading ls; +} __attribute__((aligned(8))); + +/** + * struct bcm2835_isp_params_awb_gains - AWB gains parameters block + * + * @header: Block header (type = BCM2835_ISP_PARAM_BLOCK_AWB_GAINS) + * @awb_gains: AWB gains configuration + */ +struct bcm2835_isp_params_awb_gains { + struct v4l2_isp_params_block_header header; + struct bcm2835_isp_awb_gains awb_gains; +} __attribute__((aligned(8))); + +/** + * struct bcm2835_isp_params_digital_gain - Digital gain parameters block + * + * @header: Block header (type = BCM2835_ISP_PARAM_BLOCK_DIGITAL_GAIN) + * @digital_gain: Digital gain configuration + */ +struct bcm2835_isp_params_digital_gain { + struct v4l2_isp_params_block_header header; + struct bcm2835_isp_digital_gain digital_gain; +} __attribute__((aligned(8))); + +/** + * define BCM2835_ISP_PARAMS_MAX_SIZE - Maximum size of all ISP parameters + * + * This defines the maximum size needed to accommodate all possible parameter + * blocks in a single buffer. Drivers use this to allocate appropriately + * sized buffers. + */ +#define BCM2835_ISP_PARAMS_MAX_SIZE \ + (sizeof(struct bcm2835_isp_params_black_level) + \ + sizeof(struct bcm2835_isp_params_geq) + \ + sizeof(struct bcm2835_isp_params_gamma) + \ + sizeof(struct bcm2835_isp_params_denoise) + \ + sizeof(struct bcm2835_isp_params_sharpen) + \ + sizeof(struct bcm2835_isp_params_dpc) + \ + sizeof(struct bcm2835_isp_params_cdn) + \ + sizeof(struct bcm2835_isp_params_cc_matrix) + \ + sizeof(struct bcm2835_isp_params_lens_shading) + \ + sizeof(struct bcm2835_isp_params_awb_gains) + \ + sizeof(struct bcm2835_isp_params_digital_gain)) + /* * ISP statistics structures. * diff --git a/include/linux/v4l2-controls.h b/include/linux/v4l2-controls.h index 25914de2cd7a39a168f59cd80ed331b4b339659f..0400a68b02d481f4bd5dffcd474a7f413c6bd278 100644 --- a/include/linux/v4l2-controls.h +++ b/include/linux/v4l2-controls.h @@ -178,9 +178,6 @@ enum v4l2_colorfx { */ #define V4L2_CID_USER_CCS_BASE (V4L2_CID_USER_BASE + 0x10f0) -/* The base for the bcm2835-isp driver controls. - * We reserve 16 controls for this driver. */ -#define V4L2_CID_USER_BCM2835_ISP_BASE (V4L2_CID_USER_BASE + 0x10e0) /* * The base for Allegro driver controls. * We reserve 16 controls for this driver. diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index b40b43d72196cee5f677f96dddb8452600cd296c..5f38e786496be83600e3924abd9c9aeff81d1ca9 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -843,7 +843,8 @@ struct v4l2_pix_format { #define V4L2_META_FMT_UVC_MSXU_1_5 v4l2_fourcc('U', 'V', 'C', 'M') /* UVC MSXU metadata */ #define V4L2_META_FMT_VIVID v4l2_fourcc('V', 'I', 'V', 'D') /* Vivid Metadata */ #define V4L2_META_FMT_SENSOR_DATA v4l2_fourcc('S', 'E', 'N', 'S') /* Sensor Ancillary metadata */ -#define V4L2_META_FMT_BCM2835_ISP_STATS v4l2_fourcc('B', 'S', 'T', 'A') /* BCM2835 ISP image statistics output */ +#define V4L2_META_FMT_BCM2835_ISP_STATS v4l2_fourcc('B', 'S', 'T', 'A') /* BCM2835 ISP stats */ +#define V4L2_META_FMT_BCM2835_ISP_PARAMS v4l2_fourcc('B', 'C', 'M', 'P') /* BCM2835 ISP params */ /* Vendor specific - used for RK_ISP1 camera sub-system */ #define V4L2_META_FMT_RK_ISP1_PARAMS v4l2_fourcc('R', 'K', '1', 'P') /* Rockchip ISP1 3A Parameters */ From patchwork Tue Feb 10 08:25:48 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jai Luthra X-Patchwork-Id: 26119 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 63166BD78E for ; Tue, 10 Feb 2026 08:26:33 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1B8D862190; Tue, 10 Feb 2026 09:26:33 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="X5E6GSdv"; 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 666B46218C for ; Tue, 10 Feb 2026 09:26:31 +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 80F57E47; Tue, 10 Feb 2026 09:25:44 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1770711945; bh=AhgfcfeTf14AaWAjbj0ONq8K31iAB2AR2Dr+UC64D+o=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=X5E6GSdvtcjIo+BwuLB6TPY61TbFNZtbiLzl0MB8qsZmDTxwwpAnwNUSk4hRsXFBj 29sAcWY1LI0rZ2IWBYzddIw/yB+8qV1DFOLPwyOzmbT+hKCZE9dt/H3SoHt9JfQTnQ uluOvWDU4nf9zcOajdLT48coCUgKCVCIDCLcEYcI= From: Jai Luthra Date: Tue, 10 Feb 2026 13:55:48 +0530 Subject: [PATCH 3/3] pipeline/ipa: rpi: vc4: Use extensible parameter buffers for ISP configuration MIME-Version: 1.0 Message-Id: <20260210-pi4-upstream-v1-3-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=37431; i=jai.luthra@ideasonboard.com; h=from:subject:message-id; bh=AhgfcfeTf14AaWAjbj0ONq8K31iAB2AR2Dr+UC64D+o=; b=owEBbQKS/ZANAwAKAUPekfkkmnFFAcsmYgBpiuuf4iumtmro0Ie8ysOn1Jsothx++/01MDzzb DGkuJBbkXeJAjMEAAEKAB0WIQRN4NgY5dV16NRar8VD3pH5JJpxRQUCaYrrnwAKCRBD3pH5JJpx RfOqD/0aAo27p4vxWIzjhA3QqOEHRWklg0aSQo+1s+kgeYJqJRlDgvuXQ+XyLYQ1NdoywhbtqtH q7FUC+nhgCfwqMiVadHlW8bOZgwUrn0gElYKLTMAx0tr7RRCBumj4BDzybQZ+a8ZndrSLyscezl e3Hr5sXrq+vDUg4ACNEVOA/Z24SO9A1PAbJnHVW+ECDjyqkQTG7A2hcHScfP+pMhKpflIh5wjCC GPnqWRQO/+A309elBOUXYYKRn16Q4cFN7M4yDPhx1b5sPIma1vkm492VzhjHLvDMEjGxxrnQPrX ra5Kud9xGEgEurzdRv22gtFF/qIXauaa8ohEWofcFfAO25tvSLqNQ6a0ErSJ1QT7zX4/NyX219T fw+4HSSNqd/391CSioGfnFDnaZ9XHISwuwS5/VNNZFUoBTp270fB/0wxEbNI76ut6yLLhyD0Z5t h5/YDLGhJg3/IFm6zN+pg4dBFKMWi+xGk3dxzPuMvJqRvRo+SN2enKXv9eFdBKfzT/pgFUSrp/7 6TuX+gpq4DIjPnBnpyG2858mppHJlDe/O0WgphEvKmo/nULLUDy7ln9/T9jplg1Cn/AT2wByAA1 ifIydpqbRa0eDn0vPqDYdFsj6eG+yjcQYnyTZc3X1+Y4SrgwFCebBh0EIwIAoMNdFzI4BEy077D KPcYNS+XgYmeXKg== 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" The downstream VC4 (BCM2835) ISP driver used custom V4L2 controls to pass per-frame ISP configuration from userspace, which diverged from upstream ISP drivers (rkisp1, mali-c55, PiSP) which all use parameter buffers. As the driver is being upstreamed now, switch the VC4 IPA and pipeline handler to use the extensible parameter buffer interface instead. Introduce a Bcm2835Params helper class (params.h) that wraps V4L2Params. Each algorithm's applyXYZ function now writes directly into typed parameter blocks of the params buffer using this class. On the pipeline handler side, update the ISP media entity names to match the upstream driver. Register the new bcm2835-isp-params metadata output node as an additional ISP stream. Parameter buffer handling is a bit more complicated compared to PiSP, as we use extensible buffers. So, the buffer is acquired per frame in tryRunPipeline(), and is mapped and passed to the IPA for populating. The IPA signals the completion along with the actual number of bytes used in prepareIspComplete(). Also clean up the now unused ISP control related code, and populate the lens shading DMABUF in the IPA itself instead of patching it in the pipeline handler. Signed-off-by: Jai Luthra --- include/libcamera/ipa/raspberrypi.mojom | 15 +- src/ipa/rpi/common/ipa_base.cpp | 11 +- src/ipa/rpi/common/ipa_base.h | 3 + src/ipa/rpi/vc4/params.h | 77 ++++++++ src/ipa/rpi/vc4/vc4.cpp | 232 +++++++++++-------------- src/libcamera/pipeline/rpi/common/rpi_stream.h | 1 + src/libcamera/pipeline/rpi/pisp/pisp.cpp | 6 +- src/libcamera/pipeline/rpi/vc4/vc4.cpp | 108 +++++++----- 8 files changed, 260 insertions(+), 193 deletions(-) diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom index 12b083e9d04b14a215382ccb7e9d39cd38a8b1bc..c9ef2952773ee94523a1d9aa1a73ef4bb5539f9f 100644 --- a/include/libcamera/ipa/raspberrypi.mojom +++ b/include/libcamera/ipa/raspberrypi.mojom @@ -32,12 +32,12 @@ struct BufferIds { uint32 bayer; uint32 embedded; uint32 stats; + uint32 params; }; struct ConfigParams { uint32 transform; libcamera.ControlInfoMap sensorControls; - libcamera.ControlInfoMap ispControls; libcamera.ControlInfoMap lensControls; /* VC4 specific */ libcamera.SharedFD lsTableHandle; @@ -226,7 +226,7 @@ interface IPARPiEventInterface { * processing the frame. The embedded data buffer may be recycled after * this event. */ - prepareIspComplete(BufferIds buffers, bool stitchSwapBuffers); + prepareIspComplete(BufferIds buffers, bool stitchSwapBuffers, uint32 paramsBytesUsed); /** * \fn processStatsComplete() @@ -251,17 +251,6 @@ interface IPARPiEventInterface { */ metadataReady(libcamera.ControlList metadata); - /** - * \fn setIspControls() - * \brief Signal ISP controls to be applied. - * \param[in] controls List of controls to be applied. - * - * This asynchronous event is signalled to the pipeline handler during - * the \a prepareISP signal after all algorithms have been run and the - * IPA requires ISP controls to be applied for the frame. - */ - setIspControls(libcamera.ControlList controls); - /** * \fn setDelayedControls() * \brief Signal Sensor controls to be applied. diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp index 7072b38c765ed237d8a484940ff861c33e507e36..217a9d3e8aed42c8695d638648e90d0359842412 100644 --- a/src/ipa/rpi/common/ipa_base.cpp +++ b/src/ipa/rpi/common/ipa_base.cpp @@ -427,6 +427,7 @@ void IpaBase::prepareIsp(const PrepareParams ¶ms) unsigned int ipaContext = params.ipaContext % rpiMetadata_.size(); RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext]; Span embeddedBuffer; + Span paramsBuffer; rpiMetadata.clear(); fillDeviceStatus(params.sensorControls, ipaContext); @@ -441,6 +442,13 @@ void IpaBase::prepareIsp(const PrepareParams ¶ms) embeddedBuffer = it->second.planes()[0]; } + if (params.buffers.params) { + auto it = buffers_.find(params.buffers.params); + ASSERT(it != buffers_.end()); + paramsBuffer = it->second.planes()[0]; + platformParamsBufferInit(paramsBuffer); + } + /* * AGC wants to know the algorithm status from the time it actioned the * sensor exposure/gain changes. So fetch it from the metadata list @@ -506,7 +514,8 @@ void IpaBase::prepareIsp(const PrepareParams ¶ms) reportMetadata(ipaContext); /* Ready to push the input buffer into the ISP. */ - prepareIspComplete.emit(params.buffers, stitchSwapBuffers_); + prepareIspComplete.emit(params.buffers, stitchSwapBuffers_, + platformParamsBytesUsed()); } void IpaBase::processStats(const ProcessParams ¶ms) diff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h index 5348f2ea42820308badf34d29c99fa6cf78aaf28..3704b718d885a6c4f43c0aa144f927dfeaacb0a8 100644 --- a/src/ipa/rpi/common/ipa_base.h +++ b/src/ipa/rpi/common/ipa_base.h @@ -79,6 +79,8 @@ protected: /* Whether the stitch block (if available) needs to swap buffers. */ bool stitchSwapBuffers_; + virtual size_t platformParamsBytesUsed() const { return 0; } + private: /* Number of metadata objects available in the context list. */ static constexpr unsigned int numMetadataContexts = 16; @@ -87,6 +89,7 @@ private: virtual int32_t platformStart(const ControlList &controls, StartResult *result) = 0; virtual int32_t platformConfigure(const ConfigParams ¶ms, ConfigResult *result) = 0; + virtual void platformParamsBufferInit([[maybe_unused]] Span paramsBuffer) {} virtual void platformPrepareIsp(const PrepareParams ¶ms, RPiController::Metadata &rpiMetadata) = 0; virtual void platformPrepareAgc(RPiController::Metadata &rpiMetadata) = 0; diff --git a/src/ipa/rpi/vc4/params.h b/src/ipa/rpi/vc4/params.h new file mode 100644 index 0000000000000000000000000000000000000000..db6307944e78d8183637782a093ba23b5ff1e0e3 --- /dev/null +++ b/src/ipa/rpi/vc4/params.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Ideas On Board + * + * Raspberry Pi VC4/BCM2835 ISP Parameters + */ + +#pragma once + +#include + +#include + +namespace libcamera { + +namespace ipa::RPi { + +enum class BlockType { + BlackLevel, + Geq, + Gamma, + Denoise, + Sharpen, + Dpc, + Cdn, + CcMatrix, + LensShading, + AwbGains, + DGain, +}; + +namespace details { + +template +struct block_type { +}; + +#define BCM2835_DEFINE_BLOCK_TYPE(id, structName, blockId) \ + template<> \ + struct block_type { \ + using type = struct bcm2835_isp_params_##structName; \ + static constexpr bcm2835_isp_param_block_type blockType = \ + BCM2835_ISP_PARAM_BLOCK_##blockId; \ + }; + +BCM2835_DEFINE_BLOCK_TYPE(BlackLevel, black_level, BLACK_LEVEL) +BCM2835_DEFINE_BLOCK_TYPE(Geq, geq, GEQ) +BCM2835_DEFINE_BLOCK_TYPE(Gamma, gamma, GAMMA) +BCM2835_DEFINE_BLOCK_TYPE(Denoise, denoise, DENOISE) +BCM2835_DEFINE_BLOCK_TYPE(Sharpen, sharpen, SHARPEN) +BCM2835_DEFINE_BLOCK_TYPE(Dpc, dpc, DPC) +BCM2835_DEFINE_BLOCK_TYPE(Cdn, cdn, CDN) +BCM2835_DEFINE_BLOCK_TYPE(CcMatrix, cc_matrix, CC_MATRIX) +BCM2835_DEFINE_BLOCK_TYPE(LensShading, lens_shading, LENS_SHADING) +BCM2835_DEFINE_BLOCK_TYPE(AwbGains, awb_gains, AWB_GAINS) +BCM2835_DEFINE_BLOCK_TYPE(DGain, digital_gain, DIGITAL_GAIN) + +struct params_traits { + using id_type = BlockType; + template + using id_to_details = block_type; +}; + +} /* namespace details */ + +class Bcm2835Params : public V4L2Params +{ +public: + Bcm2835Params(Span data) : V4L2Params(data, + BCM2835_ISP_PARAM_BUFFER_V1) + { + } +}; + +} /* namespace ipa::RPi */ + +} /* namespace libcamera */ diff --git a/src/ipa/rpi/vc4/vc4.cpp b/src/ipa/rpi/vc4/vc4.cpp index 2b205b2861bdb5bf81abd61fc538e9d39215293e..b35ece7185136f720e4b8a3afefc1c78f92d42b9 100644 --- a/src/ipa/rpi/vc4/vc4.cpp +++ b/src/ipa/rpi/vc4/vc4.cpp @@ -30,6 +30,7 @@ #include "controller/lux_status.h" #include "controller/noise_status.h" #include "controller/sharpen_status.h" +#include "params.h" namespace libcamera { @@ -59,34 +60,39 @@ private: int32_t platformStart(const ControlList &controls, StartResult *result) override; int32_t platformConfigure(const ConfigParams ¶ms, ConfigResult *result) override; - void platformPrepareIsp(const PrepareParams ¶ms, RPiController::Metadata &rpiMetadata) override; - void platformPrepareAgc([[maybe_unused]] RPiController::Metadata &rpiMetadata) override; + void platformParamsBufferInit(Span paramsBuffer) override; + void platformPrepareIsp([[maybe_unused]] const PrepareParams ¶ms, + RPiController::Metadata &rpiMetadata) override; + void platformPrepareAgc(RPiController::Metadata &rpiMetadata) override; RPiController::StatisticsPtr platformProcessStats(Span mem) override; void handleControls(const ControlList &controls) override; - bool validateIspControls(); - - void applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls); - void applyDG(double digitalGain, const struct AwbStatus *awbStatus, ControlList &ctrls); - void applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls); - void applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, ControlList &ctrls); - void applyGamma(const struct ContrastStatus *contrastStatus, ControlList &ctrls); - void applyGEQ(const struct GeqStatus *geqStatus, ControlList &ctrls); - void applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList &ctrls); - void applySharpen(const struct SharpenStatus *sharpenStatus, ControlList &ctrls); - void applyDPC(const struct DpcStatus *dpcStatus, ControlList &ctrls); - void applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls); + + size_t platformParamsBytesUsed() const override + { + return (ispParams_.has_value()) ? ispParams_->bytesused() : 0; + } + + void applyAWB(const struct AwbStatus *awbStatus, Bcm2835Params ¶ms); + void applyDG(double digitalGain, const struct AwbStatus *awbStatus, Bcm2835Params ¶ms); + void applyCCM(const struct CcmStatus *ccmStatus, Bcm2835Params ¶ms); + void applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, Bcm2835Params ¶ms); + void applyGamma(const struct ContrastStatus *contrastStatus, Bcm2835Params ¶ms); + void applyGEQ(const struct GeqStatus *geqStatus, Bcm2835Params ¶ms); + void applyDenoise(const struct DenoiseStatus *denoiseStatus, Bcm2835Params ¶ms); + void applySharpen(const struct SharpenStatus *sharpenStatus, Bcm2835Params ¶ms); + void applyDPC(const struct DpcStatus *dpcStatus, Bcm2835Params ¶ms); + void applyLS(const struct AlscStatus *lsStatus, Bcm2835Params ¶ms); void applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls); void resampleTable(uint16_t dest[], const std::vector &src, int destW, int destH); - /* VC4 ISP controls. */ - ControlInfoMap ispCtrls_; - ControlList ctrls_; - /* LS table allocation passed in from the pipeline handler. */ SharedFD lsTableHandle_; void *lsTable_; + /* Params buffer for the current frame. */ + std::optional ispParams_; + /* Remember the most recent AWB values. */ AwbStatus lastAwbStatus_; }; @@ -113,13 +119,6 @@ int32_t IpaVc4::platformStart([[maybe_unused]] const ControlList &controls, int32_t IpaVc4::platformConfigure(const ConfigParams ¶ms, [[maybe_unused]] ConfigResult *result) { - ispCtrls_ = params.ispControls; - ctrls_ = ControlList(ispCtrls_); - if (!validateIspControls()) { - LOG(IPARPI, Error) << "ISP control validation failed."; - return -1; - } - /* Store the lens shading table pointer and handle if available. */ if (params.lsTableHandle.isValid()) { /* Remove any previous table, if there was one. */ @@ -144,51 +143,55 @@ int32_t IpaVc4::platformConfigure(const ConfigParams ¶ms, [[maybe_unused]] C return 0; } +void IpaVc4::platformParamsBufferInit(Span paramsBuffer) +{ + /* Initialize the extensible parameter buffer */ + ispParams_.emplace(paramsBuffer); +} + void IpaVc4::platformPrepareIsp([[maybe_unused]] const PrepareParams ¶ms, RPiController::Metadata &rpiMetadata) { - ControlList &ctrls = ctrls_; - /* Lock the metadata buffer to avoid constant locks/unlocks. */ std::unique_lock lock(rpiMetadata); AwbStatus *awbStatus = rpiMetadata.getLocked("awb.status"); if (awbStatus) { - applyAWB(awbStatus, ctrls); + applyAWB(awbStatus, *ispParams_); lastAwbStatus_ = *awbStatus; } CcmStatus *ccmStatus = rpiMetadata.getLocked("ccm.status"); if (ccmStatus) - applyCCM(ccmStatus, ctrls); + applyCCM(ccmStatus, *ispParams_); AlscStatus *lsStatus = rpiMetadata.getLocked("alsc.status"); if (lsStatus) - applyLS(lsStatus, ctrls); + applyLS(lsStatus, *ispParams_); ContrastStatus *contrastStatus = rpiMetadata.getLocked("contrast.status"); if (contrastStatus) - applyGamma(contrastStatus, ctrls); + applyGamma(contrastStatus, *ispParams_); BlackLevelStatus *blackLevelStatus = rpiMetadata.getLocked("black_level.status"); if (blackLevelStatus) - applyBlackLevel(blackLevelStatus, ctrls); + applyBlackLevel(blackLevelStatus, *ispParams_); GeqStatus *geqStatus = rpiMetadata.getLocked("geq.status"); if (geqStatus) - applyGEQ(geqStatus, ctrls); + applyGEQ(geqStatus, *ispParams_); DenoiseStatus *denoiseStatus = rpiMetadata.getLocked("denoise.status"); if (denoiseStatus) - applyDenoise(denoiseStatus, ctrls); + applyDenoise(denoiseStatus, *ispParams_); SharpenStatus *sharpenStatus = rpiMetadata.getLocked("sharpen.status"); if (sharpenStatus) - applySharpen(sharpenStatus, ctrls); + applySharpen(sharpenStatus, *ispParams_); DpcStatus *dpcStatus = rpiMetadata.getLocked("dpc.status"); if (dpcStatus) - applyDPC(dpcStatus, ctrls); + applyDPC(dpcStatus, *ispParams_); const AfStatus *afStatus = rpiMetadata.getLocked("af.status"); if (afStatus) { @@ -205,10 +208,7 @@ void IpaVc4::platformPrepareAgc(RPiController::Metadata &rpiMetadata) double digitalGain = delayedAgcStatus ? delayedAgcStatus->digitalGain : agcStatus_.digitalGain; AwbStatus *awbStatus = rpiMetadata.getLocked("awb.status"); - applyDG(digitalGain, awbStatus, ctrls_); - - setIspControls.emit(ctrls_); - ctrls_ = ControlList(ispCtrls_); + applyDG(digitalGain, awbStatus, *ispParams_); } RPiController::StatisticsPtr IpaVc4::platformProcessStats(Span mem) @@ -325,48 +325,26 @@ void IpaVc4::handleControls(const ControlList &controls) } } -bool IpaVc4::validateIspControls() +void IpaVc4::applyAWB(const struct AwbStatus *awbStatus, Bcm2835Params ¶ms) { - static const uint32_t ctrls[] = { - V4L2_CID_RED_BALANCE, - V4L2_CID_BLUE_BALANCE, - V4L2_CID_DIGITAL_GAIN, - V4L2_CID_USER_BCM2835_ISP_CC_MATRIX, - V4L2_CID_USER_BCM2835_ISP_GAMMA, - V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL, - V4L2_CID_USER_BCM2835_ISP_GEQ, - V4L2_CID_USER_BCM2835_ISP_DENOISE, - V4L2_CID_USER_BCM2835_ISP_SHARPEN, - V4L2_CID_USER_BCM2835_ISP_DPC, - V4L2_CID_USER_BCM2835_ISP_LENS_SHADING, - V4L2_CID_USER_BCM2835_ISP_CDN, - }; - - for (auto c : ctrls) { - if (ispCtrls_.find(c) == ispCtrls_.end()) { - LOG(IPARPI, Error) << "Unable to find ISP control " - << utils::hex(c); - return false; - } - } - - return true; -} + auto block = params.block(); -void IpaVc4::applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls) -{ LOG(IPARPI, Debug) << "Applying WB R: " << awbStatus->gainR << " B: " << awbStatus->gainB; - ctrls.set(V4L2_CID_RED_BALANCE, - static_cast(awbStatus->gainR * 1000)); - ctrls.set(V4L2_CID_BLUE_BALANCE, - static_cast(awbStatus->gainB * 1000)); + block->awb_gains.r_gain.num = static_cast(awbStatus->gainR * 1000); + block->awb_gains.r_gain.den = 1000; + block->awb_gains.b_gain.num = static_cast(awbStatus->gainB * 1000); + block->awb_gains.b_gain.den = 1000; + + block.setEnabled(true); } void IpaVc4::applyDG(double digitalGain, - const struct AwbStatus *awbStatus, ControlList &ctrls) + const struct AwbStatus *awbStatus, Bcm2835Params ¶ms) { + auto block = params.block(); + if (awbStatus) { /* * We must apply sufficient extra digital gain to stop any of the channel gains being @@ -380,13 +358,16 @@ void IpaVc4::applyDG(double digitalGain, digitalGain *= extraGain; } - ctrls.set(V4L2_CID_DIGITAL_GAIN, - static_cast(digitalGain * 1000)); + block->digital_gain.gain.num = static_cast(digitalGain * 1000); + block->digital_gain.gain.den = 1000; + + block.setEnabled(true); } -void IpaVc4::applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls) +void IpaVc4::applyCCM(const struct CcmStatus *ccmStatus, Bcm2835Params ¶ms) { - bcm2835_isp_custom_ccm ccm; + auto block = params.block(); + bcm2835_isp_custom_ccm &ccm = block->ccm; for (int i = 0; i < 9; i++) { ccm.ccm.ccm[i / 3][i % 3].den = 1000; @@ -395,30 +376,28 @@ void IpaVc4::applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls) ccm.enabled = 1; ccm.ccm.offsets[0] = ccm.ccm.offsets[1] = ccm.ccm.offsets[2] = 0; - - ControlValue c(Span{ reinterpret_cast(&ccm), - sizeof(ccm) }); - ctrls.set(V4L2_CID_USER_BCM2835_ISP_CC_MATRIX, c); + block.setEnabled(true); } -void IpaVc4::applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, ControlList &ctrls) +void IpaVc4::applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, + Bcm2835Params ¶ms) { - bcm2835_isp_black_level blackLevel; + auto block = params.block(); + bcm2835_isp_black_level &blackLevel = block->black_level; blackLevel.enabled = 1; blackLevel.black_level_r = blackLevelStatus->blackLevelR; blackLevel.black_level_g = blackLevelStatus->blackLevelG; blackLevel.black_level_b = blackLevelStatus->blackLevelB; - - ControlValue c(Span{ reinterpret_cast(&blackLevel), - sizeof(blackLevel) }); - ctrls.set(V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL, c); + block.setEnabled(true); } -void IpaVc4::applyGamma(const struct ContrastStatus *contrastStatus, ControlList &ctrls) +void IpaVc4::applyGamma(const struct ContrastStatus *contrastStatus, + Bcm2835Params ¶ms) { const unsigned int numGammaPoints = controller_.getHardwareConfig().numGammaPoints; - struct bcm2835_isp_gamma gamma; + auto block = params.block(); + struct bcm2835_isp_gamma &gamma = block->gamma; for (unsigned int i = 0; i < numGammaPoints - 1; i++) { int x = i < 16 ? i * 1024 @@ -431,31 +410,27 @@ void IpaVc4::applyGamma(const struct ContrastStatus *contrastStatus, ControlList gamma.x[numGammaPoints - 1] = 65535; gamma.y[numGammaPoints - 1] = 65535; gamma.enabled = 1; - - ControlValue c(Span{ reinterpret_cast(&gamma), - sizeof(gamma) }); - ctrls.set(V4L2_CID_USER_BCM2835_ISP_GAMMA, c); + block.setEnabled(true); } -void IpaVc4::applyGEQ(const struct GeqStatus *geqStatus, ControlList &ctrls) +void IpaVc4::applyGEQ(const struct GeqStatus *geqStatus, Bcm2835Params ¶ms) { - bcm2835_isp_geq geq; + auto block = params.block(); + bcm2835_isp_geq &geq = block->geq; geq.enabled = 1; geq.offset = geqStatus->offset; geq.slope.den = 1000; geq.slope.num = 1000 * geqStatus->slope; - - ControlValue c(Span{ reinterpret_cast(&geq), - sizeof(geq) }); - ctrls.set(V4L2_CID_USER_BCM2835_ISP_GEQ, c); + block.setEnabled(true); } -void IpaVc4::applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList &ctrls) +void IpaVc4::applyDenoise(const struct DenoiseStatus *denoiseStatus, Bcm2835Params ¶ms) { + auto blockDenoise = params.block(); using RPiController::DenoiseMode; - bcm2835_isp_denoise denoise; + bcm2835_isp_denoise &denoise = blockDenoise->denoise; DenoiseMode mode = static_cast(denoiseStatus->mode); denoise.enabled = mode != DenoiseMode::Off; @@ -464,9 +439,11 @@ void IpaVc4::applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList denoise.slope.den = 1000; denoise.strength.num = 1000 * denoiseStatus->strength; denoise.strength.den = 1000; + blockDenoise.setEnabled(denoise.enabled); /* Set the CDN mode to match the SDN operating mode. */ - bcm2835_isp_cdn cdn; + auto blockCdn = params.block(); + bcm2835_isp_cdn &cdn = blockCdn->cdn; switch (mode) { case DenoiseMode::ColourFast: cdn.enabled = 1; @@ -479,19 +456,13 @@ void IpaVc4::applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList default: cdn.enabled = 0; } - - ControlValue c(Span{ reinterpret_cast(&denoise), - sizeof(denoise) }); - ctrls.set(V4L2_CID_USER_BCM2835_ISP_DENOISE, c); - - c = ControlValue(Span{ reinterpret_cast(&cdn), - sizeof(cdn) }); - ctrls.set(V4L2_CID_USER_BCM2835_ISP_CDN, c); + blockCdn.setEnabled(cdn.enabled); } -void IpaVc4::applySharpen(const struct SharpenStatus *sharpenStatus, ControlList &ctrls) +void IpaVc4::applySharpen(const struct SharpenStatus *sharpenStatus, Bcm2835Params ¶ms) { - bcm2835_isp_sharpen sharpen; + auto block = params.block(); + bcm2835_isp_sharpen &sharpen = block->sharpen; sharpen.enabled = 1; sharpen.threshold.num = 1000 * sharpenStatus->threshold; @@ -500,25 +471,20 @@ void IpaVc4::applySharpen(const struct SharpenStatus *sharpenStatus, ControlList sharpen.strength.den = 1000; sharpen.limit.num = 1000 * sharpenStatus->limit; sharpen.limit.den = 1000; - - ControlValue c(Span{ reinterpret_cast(&sharpen), - sizeof(sharpen) }); - ctrls.set(V4L2_CID_USER_BCM2835_ISP_SHARPEN, c); + block.setEnabled(true); } -void IpaVc4::applyDPC(const struct DpcStatus *dpcStatus, ControlList &ctrls) +void IpaVc4::applyDPC(const struct DpcStatus *dpcStatus, Bcm2835Params ¶ms) { - bcm2835_isp_dpc dpc; + auto block = params.block(); + bcm2835_isp_dpc &dpc = block->dpc; dpc.enabled = 1; dpc.strength = dpcStatus->strength; - - ControlValue c(Span{ reinterpret_cast(&dpc), - sizeof(dpc) }); - ctrls.set(V4L2_CID_USER_BCM2835_ISP_DPC, c); + block.setEnabled(true); } -void IpaVc4::applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls) +void IpaVc4::applyLS(const struct AlscStatus *lsStatus, Bcm2835Params ¶ms) { /* * Program lens shading tables into pipeline. @@ -542,24 +508,24 @@ void IpaVc4::applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls) /* We're going to supply corner sampled tables, 16 bit samples. */ w++, h++; - bcm2835_isp_lens_shading ls = { + if (!lsTableHandle_.isValid() || !lsTable_ || w * h * 4 * sizeof(uint16_t) > MaxLsGridSize) { + LOG(IPARPI, Error) << "Do not have a correctly allocated lens shading table!"; + return; + } + + auto block = params.block(); + block->ls = { .enabled = 1, .grid_cell_size = cellSize, .grid_width = w, .grid_stride = w, .grid_height = h, - /* .dmabuf will be filled in by pipeline handler. */ - .dmabuf = 0, + .dmabuf = lsTableHandle_.get(), .ref_transform = 0, .corner_sampled = 1, .gain_format = GAIN_FORMAT_U4P10 }; - if (!lsTable_ || w * h * 4 * sizeof(uint16_t) > MaxLsGridSize) { - LOG(IPARPI, Error) << "Do not have a correctly allocate lens shading table!"; - return; - } - if (lsStatus) { /* Format will be u4.10 */ uint16_t *grid = static_cast(lsTable_); @@ -570,9 +536,7 @@ void IpaVc4::applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls) resampleTable(grid + 3 * w * h, lsStatus->b, w, h); } - ControlValue c(Span{ reinterpret_cast(&ls), - sizeof(ls) }); - ctrls.set(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING, c); + block.setEnabled(true); } void IpaVc4::applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls) diff --git a/src/libcamera/pipeline/rpi/common/rpi_stream.h b/src/libcamera/pipeline/rpi/common/rpi_stream.h index 300a352a7d39b7ea6908777696d9a22a4520936d..cc18dcc8fa8dd70deb030027be06ea3cd6e2677a 100644 --- a/src/libcamera/pipeline/rpi/common/rpi_stream.h +++ b/src/libcamera/pipeline/rpi/common/rpi_stream.h @@ -30,6 +30,7 @@ enum BufferMask { MaskStats = 0x010000, MaskEmbeddedData = 0x020000, MaskBayerData = 0x040000, + MaskParams = 0x080000, }; struct BufferObject { diff --git a/src/libcamera/pipeline/rpi/pisp/pisp.cpp b/src/libcamera/pipeline/rpi/pisp/pisp.cpp index 7bcba32b9b58b1a8782f956d20248e4828b2ba52..4a5dcab209890313630280d2593e08ceae39ce67 100644 --- a/src/libcamera/pipeline/rpi/pisp/pisp.cpp +++ b/src/libcamera/pipeline/rpi/pisp/pisp.cpp @@ -754,7 +754,8 @@ public: void beOutputDequeue(FrameBuffer *buffer); void processStatsComplete(const ipa::RPi::BufferIds &buffers); - void prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers); + void prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers, + unsigned int paramsBytesUsed); void setCameraTimeout(uint32_t maxFrameLengthMs); /* Array of CFE and ISP device streams and associated buffers/streams. */ @@ -1868,7 +1869,8 @@ void PiSPCameraData::setCameraTimeout(uint32_t maxFrameLengthMs) cfe_[Cfe::Output0].dev()->setDequeueTimeout(timeout); } -void PiSPCameraData::prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers) +void PiSPCameraData::prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers, + [[maybe_unused]] unsigned int paramsBytesUsed) { unsigned int embeddedId = buffers.embedded & RPi::MaskID; unsigned int bayerId = buffers.bayer & RPi::MaskID; diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp index cd1ec4f486990bf620f44bc6cd3ef6ae90c91327..b403992d1f5d32fd79436d7c5652afd9715ada0b 100644 --- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp +++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp @@ -32,7 +32,7 @@ using StreamParams = RPi::RPiCameraConfiguration::StreamParams; namespace { enum class Unicam : unsigned int { Image, Embedded }; -enum class Isp : unsigned int { Input, Output0, Output1, Stats }; +enum class Isp : unsigned int { Input, Output0, Output1, Stats, Params }; static constexpr unsigned int kUnicamSinkPad = 0; static constexpr unsigned int kUnicamSourceImagePad = 1; @@ -84,15 +84,15 @@ public: void ispOutputDequeue(FrameBuffer *buffer); void processStatsComplete(const ipa::RPi::BufferIds &buffers); - void prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers); - void setIspControls(const ControlList &controls); + void prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers, + unsigned int paramsBytesUsed); 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_; + RPi::Device isp_; /* DMAHEAP allocation helper. */ DmaBufAllocator dmaHeap_; @@ -266,6 +266,13 @@ int PipelineHandlerVc4::allocateBuffers(Camera *camera) std::max(data->config_.minUnicamBuffers, minBuffers - numRawBuffers); + } else if (stream == &data->isp_[Isp::Params]) { + /* + * Parameter buffers are dequeued asyncrhonously by the driver + * as soon as it sends each parameter to VC4. Ideally, 1 buffer + * would be sufficient, but we alot 2 to be safe. + */ + numBuffers = 2; } else if (stream == &data->unicam_[Unicam::Embedded]) { /* * Embedded data buffers are (currently) for internal use, and @@ -302,10 +309,11 @@ int PipelineHandlerVc4::allocateBuffers(Camera *camera) } /* - * Pass the stats and embedded data buffers to the IPA. No other + * Pass the stats, embedded data and params buffers to the IPA. No other * buffers need to be passed. */ mapBuffers(camera, data->isp_[Isp::Stats].getBuffers(), RPi::MaskStats); + mapBuffers(camera, data->isp_[Isp::Params].getBuffers(), RPi::MaskParams); if (data->sensorMetadata_) mapBuffers(camera, data->unicam_[Unicam::Embedded].getBuffers(), RPi::MaskEmbeddedData); @@ -324,13 +332,14 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr &camer 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 (!unicamSubdev || !unicamImage || !ispOutput0 || !ispCapture1 || - !ispCapture2 || !ispCapture3) + MediaEntity *ispOutput0 = isp->getEntityByName("bcm2835-isp-output0"); + MediaEntity *ispCapture0 = isp->getEntityByName("bcm2835-isp-capture0"); + MediaEntity *ispCapture1 = isp->getEntityByName("bcm2835-isp-capture1"); + MediaEntity *ispCapture2 = isp->getEntityByName("bcm2835-isp-stats2"); + MediaEntity *ispParams = isp->getEntityByName("bcm2835-isp-params"); + + if (!unicamSubdev || !unicamImage || !ispOutput0 || !ispCapture0 || + !ispCapture1 || !ispCapture2 || !ispParams) return -ENOENT; /* Create the unicam subdev and video streams. */ @@ -347,9 +356,12 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr &camer /* Tag the ISP input stream as an import stream. */ data->isp_[Isp::Input] = RPi::Stream("ISP Input", ispOutput0, StreamFlag::ImportOnly); - data->isp_[Isp::Output0] = RPi::Stream("ISP Output0", ispCapture1); - data->isp_[Isp::Output1] = RPi::Stream("ISP Output1", ispCapture2); - data->isp_[Isp::Stats] = RPi::Stream("ISP Stats", ispCapture3); + data->isp_[Isp::Output0] = RPi::Stream("ISP Output0", ispCapture0); + data->isp_[Isp::Output1] = RPi::Stream("ISP Output1", ispCapture1); + data->isp_[Isp::Stats] = RPi::Stream("ISP Stats", ispCapture2); + /* Tag the ISP params stream as MMAP (for writing into it in the IPA) and recurrent. */ + data->isp_[Isp::Params] = RPi::Stream("ISP Params", ispParams, + StreamFlag::RequiresMmap | StreamFlag::Recurrent); /* Wire up all the buffer connections. */ data->unicam_[Unicam::Image].dev()->bufferReady.connect(data, &Vc4CameraData::unicamBufferDequeue); @@ -357,6 +369,7 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr &camer data->isp_[Isp::Output0].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue); data->isp_[Isp::Output1].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue); data->isp_[Isp::Stats].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue); + data->isp_[Isp::Params].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue); if (data->sensorMetadata_ ^ !!data->unicam_[Unicam::Embedded].dev()) { LOG(RPI, Warning) << "Mismatch between Unicam and CamHelper for embedded data usage!"; @@ -398,7 +411,6 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr &camer /* Write up all the IPA connections. */ data->ipa_->processStatsComplete.connect(data, &Vc4CameraData::processStatsComplete); data->ipa_->prepareIspComplete.connect(data, &Vc4CameraData::prepareIspComplete); - data->ipa_->setIspControls.connect(data, &Vc4CameraData::setIspControls); data->ipa_->setCameraTimeout.connect(data, &Vc4CameraData::setCameraTimeout); /* @@ -757,6 +769,16 @@ int Vc4CameraData::platformConfigure(const RPi::RPiCameraConfiguration *rpiConfi return ret; } + /* ISP parameters input format. */ + format = {}; + format.fourcc = V4L2PixelFormat(V4L2_META_FMT_BCM2835_ISP_PARAMS); + ret = isp_[Isp::Params].dev()->setFormat(&format); + if (ret) { + LOG(RPI, Error) << "Failed to set format on ISP params stream: " + << format; + return ret; + } + /* * Configure the Unicam embedded data output format only if the sensor * supports it. @@ -800,8 +822,6 @@ int Vc4CameraData::platformConfigure(const RPi::RPiCameraConfiguration *rpiConfi int Vc4CameraData::platformConfigureIpa(ipa::RPi::ConfigParams ¶ms) { - params.ispControls = isp_[Isp::Input].dev()->controls(); - /* Allocate the lens shading table via dmaHeap and pass to the IPA. */ if (!lsTable_.isValid()) { lsTable_ = SharedFD(dmaHeap_.alloc("ls_grid", ipa::RPi::MaxLsGridSize)); @@ -945,46 +965,41 @@ void Vc4CameraData::processStatsComplete(const ipa::RPi::BufferIds &buffers) } void Vc4CameraData::prepareIspComplete(const ipa::RPi::BufferIds &buffers, - [[maybe_unused]] bool stitchSwapBuffers) + [[maybe_unused]] bool stitchSwapBuffers, + unsigned int paramsBytesUsed) { unsigned int embeddedId = buffers.embedded & RPi::MaskID; - unsigned int bayer = buffers.bayer & RPi::MaskID; + unsigned int bayerId = buffers.bayer & RPi::MaskID; + unsigned int paramsId = buffers.params & RPi::MaskID; FrameBuffer *buffer; if (!isRunning()) return; - buffer = unicam_[Unicam::Image].getBuffers().at(bayer & RPi::MaskID).buffer; - LOG(RPI, Debug) << "Input re-queue to ISP, buffer id " << (bayer & RPi::MaskID) + /* Queue params buffer */ + buffer = isp_[Isp::Params].getBuffers().at(paramsId).buffer; + buffer->_d()->metadata().planes()[0].bytesused = paramsBytesUsed; + LOG(RPI, Debug) << "Params re-queue to ISP, buffer id " << paramsId + << ", timestamp: " << buffer->metadata().timestamp + << ", bytes used: " << buffer->_d()->metadata().planes()[0].bytesused; + + isp_[Isp::Params].queueBuffer(buffer); + + /* Queue input buffer */ + buffer = unicam_[Unicam::Image].getBuffers().at(bayerId).buffer; + LOG(RPI, Debug) << "Input re-queue to ISP, buffer id " << bayerId << ", timestamp: " << buffer->metadata().timestamp; isp_[Isp::Input].queueBuffer(buffer); if (sensorMetadata_ && embeddedId) { - buffer = unicam_[Unicam::Embedded].getBuffers().at(embeddedId & RPi::MaskID).buffer; + buffer = unicam_[Unicam::Embedded].getBuffers().at(embeddedId).buffer; handleStreamBuffer(buffer, &unicam_[Unicam::Embedded]); } handleState(); } -void Vc4CameraData::setIspControls(const ControlList &controls) -{ - ControlList ctrls = controls; - - if (ctrls.contains(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING)) { - ControlValue &value = - const_cast(ctrls.get(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING)); - Span s = value.data(); - bcm2835_isp_lens_shading *ls = - reinterpret_cast(s.data()); - ls->dmabuf = lsTable_.get(); - } - - isp_[Isp::Input].dev()->setControls(&ctrls); - handleState(); -} - void Vc4CameraData::setCameraTimeout(uint32_t maxFrameLengthMs) { /* @@ -1026,9 +1041,6 @@ void Vc4CameraData::tryRunPipeline() unsigned int bayer = unicam_[Unicam::Image].getBufferId(bayerFrame.buffer); - LOG(RPI, Debug) << "Signalling prepareIsp:" - << " Bayer buffer id: " << bayer; - ipa::RPi::PrepareParams params; params.buffers.bayer = RPi::MaskBayerData | bayer; params.sensorControls = std::move(bayerFrame.controls); @@ -1037,6 +1049,16 @@ void Vc4CameraData::tryRunPipeline() params.delayContext = bayerFrame.delayContext; params.buffers.embedded = 0; + const RPi::BufferObject ¶mBufObj = isp_[Isp::Params].acquireBuffer(); + ASSERT(paramBufObj.mapped); + + unsigned int param = isp_[Isp::Params].getBufferId(paramBufObj.buffer); + params.buffers.params = RPi::MaskParams | param; + + LOG(RPI, Debug) << "Signalling prepareIsp:" + << " Bayer buffer id: " << bayer + << " Param buffer id: " << param; + if (embeddedBuffer) { unsigned int embeddedId = unicam_[Unicam::Embedded].getBufferId(embeddedBuffer);