Show a patch.

GET /api/patches/26119/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 26119,
    "url": "https://patchwork.libcamera.org/api/patches/26119/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/26119/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20260210-pi4-upstream-v1-3-279841c15fba@ideasonboard.com>",
    "date": "2026-02-10T08:25:48",
    "name": "[3/3] pipeline/ipa: rpi: vc4: Use extensible parameter buffers for ISP configuration",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "4429080a3821d5002619fb26de9306007e559ecc",
    "submitter": {
        "id": 223,
        "url": "https://patchwork.libcamera.org/api/people/223/?format=api",
        "name": "Jai Luthra",
        "email": "jai.luthra@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/26119/mbox/",
    "series": [
        {
            "id": 5780,
            "url": "https://patchwork.libcamera.org/api/series/5780/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5780",
            "date": "2026-02-10T08:25:45",
            "name": "Raspberry Pi: Update VC4 pipeline for upstream",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/5780/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/26119/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/26119/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>",
        "X-Original-To": "parsemail@patchwork.libcamera.org",
        "Delivered-To": "parsemail@patchwork.libcamera.org",
        "Received": [
            "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 63166BD78E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 10 Feb 2026 08:26:33 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 1B8D862190;\n\tTue, 10 Feb 2026 09:26:33 +0100 (CET)",
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 666B46218C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 10 Feb 2026 09:26:31 +0100 (CET)",
            "from mail.ideasonboard.com (unknown\n\t[IPv6:2401:4900:1c30:2edd:807a:f3c0:8d1b:28a])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 80F57E47;\n\tTue, 10 Feb 2026 09:25:44 +0100 (CET)"
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"X5E6GSdv\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1770711945;\n\tbh=AhgfcfeTf14AaWAjbj0ONq8K31iAB2AR2Dr+UC64D+o=;\n\th=From:Date:Subject:References:In-Reply-To:To:Cc:From;\n\tb=X5E6GSdvtcjIo+BwuLB6TPY61TbFNZtbiLzl0MB8qsZmDTxwwpAnwNUSk4hRsXFBj\n\t29sAcWY1LI0rZ2IWBYzddIw/yB+8qV1DFOLPwyOzmbT+hKCZE9dt/H3SoHt9JfQTnQ\n\tuluOvWDU4nf9zcOajdLT48coCUgKCVCIDCLcEYcI=",
        "From": "Jai Luthra <jai.luthra@ideasonboard.com>",
        "Date": "Tue, 10 Feb 2026 13:55:48 +0530",
        "Subject": "[PATCH 3/3] pipeline/ipa: rpi: vc4: Use extensible parameter\n\tbuffers for ISP configuration",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=\"utf-8\"",
        "Content-Transfer-Encoding": "7bit",
        "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 <naush@raspberrypi.com>, \n\tDavid Plowman <david.plowman@raspberrypi.com>, \n\tLaurent Pinchart <laurent.pinchart@ideasonboard.com>, \n\tKieran Bingham <kieran.bingham@ideasonboard.com>, \n\tJacopo Mondi <jacopo.mondi@ideasonboard.com>, \n\tDaniel Scally <dan.scally@ideasonboard.com>, \n\tJai Luthra <jai.luthra@ideasonboard.com>",
        "X-Mailer": "b4 0.14.2",
        "X-Developer-Signature": "v=1; a=openpgp-sha256; l=37431;\n\ti=jai.luthra@ideasonboard.com; h=from:subject:message-id;\n\tbh=AhgfcfeTf14AaWAjbj0ONq8K31iAB2AR2Dr+UC64D+o=;\n\tb=owEBbQKS/ZANAwAKAUPekfkkmnFFAcsmYgBpiuuf4iumtmro0Ie8ysOn1Jsothx++/01MDzzb\n\tDGkuJBbkXeJAjMEAAEKAB0WIQRN4NgY5dV16NRar8VD3pH5JJpxRQUCaYrrnwAKCRBD3pH5JJpx\n\tRfOqD/0aAo27p4vxWIzjhA3QqOEHRWklg0aSQo+1s+kgeYJqJRlDgvuXQ+XyLYQ1NdoywhbtqtH\n\tq7FUC+nhgCfwqMiVadHlW8bOZgwUrn0gElYKLTMAx0tr7RRCBumj4BDzybQZ+a8ZndrSLyscezl\n\te3Hr5sXrq+vDUg4ACNEVOA/Z24SO9A1PAbJnHVW+ECDjyqkQTG7A2hcHScfP+pMhKpflIh5wjCC\n\tGPnqWRQO/+A309elBOUXYYKRn16Q4cFN7M4yDPhx1b5sPIma1vkm492VzhjHLvDMEjGxxrnQPrX\n\tra5Kud9xGEgEurzdRv22gtFF/qIXauaa8ohEWofcFfAO25tvSLqNQ6a0ErSJ1QT7zX4/NyX219T\n\tfw+4HSSNqd/391CSioGfnFDnaZ9XHISwuwS5/VNNZFUoBTp270fB/0wxEbNI76ut6yLLhyD0Z5t\n\th5/YDLGhJg3/IFm6zN+pg4dBFKMWi+xGk3dxzPuMvJqRvRo+SN2enKXv9eFdBKfzT/pgFUSrp/7\n\t6TuX+gpq4DIjPnBnpyG2858mppHJlDe/O0WgphEvKmo/nULLUDy7ln9/T9jplg1Cn/AT2wByAA1\n\tifIydpqbRa0eDn0vPqDYdFsj6eG+yjcQYnyTZc3X1+Y4SrgwFCebBh0EIwIAoMNdFzI4BEy077D\n\tKPcYNS+XgYmeXKg==",
        "X-Developer-Key": "i=jai.luthra@ideasonboard.com; a=openpgp;\n\tfpr=4DE0D818E5D575E8D45AAFC543DE91F9249A7145",
        "X-BeenThere": "libcamera-devel@lists.libcamera.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "<libcamera-devel.lists.libcamera.org>",
        "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>",
        "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>",
        "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>",
        "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "The downstream VC4 (BCM2835) ISP driver used custom V4L2 controls to\npass per-frame ISP configuration from userspace, which diverged from\nupstream ISP drivers (rkisp1, mali-c55, PiSP) which all use parameter\nbuffers. As the driver is being upstreamed now, switch the VC4 IPA and\npipeline handler to use the extensible parameter buffer interface\ninstead.\n\nIntroduce a Bcm2835Params helper class (params.h) that wraps V4L2Params.\nEach algorithm's applyXYZ function now writes directly into typed\nparameter blocks of the params buffer using this class.\n\nOn the pipeline handler side, update the ISP media entity names to match\nthe upstream driver. Register the new bcm2835-isp-params metadata output\nnode as an additional ISP stream.\n\nParameter buffer handling is a bit more complicated compared to PiSP, as\nwe use extensible buffers. So, the buffer is acquired per frame in\ntryRunPipeline(), and is mapped and passed to the IPA for populating.\nThe IPA signals the completion along with the actual number of bytes\nused in prepareIspComplete().\n\nAlso clean up the now unused ISP control related code, and populate the\nlens shading DMABUF in the IPA itself instead of patching it in the\npipeline handler.\n\nSigned-off-by: Jai Luthra <jai.luthra@ideasonboard.com>\n---\n include/libcamera/ipa/raspberrypi.mojom        |  15 +-\n src/ipa/rpi/common/ipa_base.cpp                |  11 +-\n src/ipa/rpi/common/ipa_base.h                  |   3 +\n src/ipa/rpi/vc4/params.h                       |  77 ++++++++\n src/ipa/rpi/vc4/vc4.cpp                        | 232 +++++++++++--------------\n src/libcamera/pipeline/rpi/common/rpi_stream.h |   1 +\n src/libcamera/pipeline/rpi/pisp/pisp.cpp       |   6 +-\n src/libcamera/pipeline/rpi/vc4/vc4.cpp         | 108 +++++++-----\n 8 files changed, 260 insertions(+), 193 deletions(-)",
    "diff": "diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom\nindex 12b083e9d04b14a215382ccb7e9d39cd38a8b1bc..c9ef2952773ee94523a1d9aa1a73ef4bb5539f9f 100644\n--- a/include/libcamera/ipa/raspberrypi.mojom\n+++ b/include/libcamera/ipa/raspberrypi.mojom\n@@ -32,12 +32,12 @@ struct BufferIds {\n \tuint32 bayer;\n \tuint32 embedded;\n \tuint32 stats;\n+\tuint32 params;\n };\n \n struct ConfigParams {\n \tuint32 transform;\n \tlibcamera.ControlInfoMap sensorControls;\n-\tlibcamera.ControlInfoMap ispControls;\n \tlibcamera.ControlInfoMap lensControls;\n         /* VC4 specific */\n \tlibcamera.SharedFD lsTableHandle;\n@@ -226,7 +226,7 @@ interface IPARPiEventInterface {\n \t * processing the frame. The embedded data buffer may be recycled after\n \t * this event.\n \t */\n-\tprepareIspComplete(BufferIds buffers, bool stitchSwapBuffers);\n+\tprepareIspComplete(BufferIds buffers, bool stitchSwapBuffers, uint32 paramsBytesUsed);\n \n \t/**\n \t * \\fn processStatsComplete()\n@@ -251,17 +251,6 @@ interface IPARPiEventInterface {\n \t */\n \tmetadataReady(libcamera.ControlList metadata);\n \n-\t/**\n-\t * \\fn setIspControls()\n-\t * \\brief Signal ISP controls to be applied.\n-\t * \\param[in] controls List of controls to be applied.\n-\t *\n-\t * This asynchronous event is signalled to the pipeline handler during\n-\t * the \\a prepareISP signal after all algorithms have been run and the\n-\t * IPA requires ISP controls to be applied for the frame.\n-\t */\n-\tsetIspControls(libcamera.ControlList controls);\n-\n \t/**\n \t * \\fn setDelayedControls()\n \t * \\brief Signal Sensor controls to be applied.\ndiff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp\nindex 7072b38c765ed237d8a484940ff861c33e507e36..217a9d3e8aed42c8695d638648e90d0359842412 100644\n--- a/src/ipa/rpi/common/ipa_base.cpp\n+++ b/src/ipa/rpi/common/ipa_base.cpp\n@@ -427,6 +427,7 @@ void IpaBase::prepareIsp(const PrepareParams &params)\n \tunsigned int ipaContext = params.ipaContext % rpiMetadata_.size();\n \tRPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext];\n \tSpan<uint8_t> embeddedBuffer;\n+\tSpan<uint8_t> paramsBuffer;\n \n \trpiMetadata.clear();\n \tfillDeviceStatus(params.sensorControls, ipaContext);\n@@ -441,6 +442,13 @@ void IpaBase::prepareIsp(const PrepareParams &params)\n \t\tembeddedBuffer = it->second.planes()[0];\n \t}\n \n+\tif (params.buffers.params) {\n+\t\tauto it = buffers_.find(params.buffers.params);\n+\t\tASSERT(it != buffers_.end());\n+\t\tparamsBuffer = it->second.planes()[0];\n+\t\tplatformParamsBufferInit(paramsBuffer);\n+\t}\n+\n \t/*\n \t * AGC wants to know the algorithm status from the time it actioned the\n \t * sensor exposure/gain changes. So fetch it from the metadata list\n@@ -506,7 +514,8 @@ void IpaBase::prepareIsp(const PrepareParams &params)\n \t\treportMetadata(ipaContext);\n \n \t/* Ready to push the input buffer into the ISP. */\n-\tprepareIspComplete.emit(params.buffers, stitchSwapBuffers_);\n+\tprepareIspComplete.emit(params.buffers, stitchSwapBuffers_,\n+\t\t\t\tplatformParamsBytesUsed());\n }\n \n void IpaBase::processStats(const ProcessParams &params)\ndiff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h\nindex 5348f2ea42820308badf34d29c99fa6cf78aaf28..3704b718d885a6c4f43c0aa144f927dfeaacb0a8 100644\n--- a/src/ipa/rpi/common/ipa_base.h\n+++ b/src/ipa/rpi/common/ipa_base.h\n@@ -79,6 +79,8 @@ protected:\n \t/* Whether the stitch block (if available) needs to swap buffers. */\n \tbool stitchSwapBuffers_;\n \n+\tvirtual size_t platformParamsBytesUsed() const { return 0; }\n+\n private:\n \t/* Number of metadata objects available in the context list. */\n \tstatic constexpr unsigned int numMetadataContexts = 16;\n@@ -87,6 +89,7 @@ private:\n \tvirtual int32_t platformStart(const ControlList &controls, StartResult *result) = 0;\n \tvirtual int32_t platformConfigure(const ConfigParams &params, ConfigResult *result) = 0;\n \n+\tvirtual void platformParamsBufferInit([[maybe_unused]] Span<uint8_t> paramsBuffer) {}\n \tvirtual void platformPrepareIsp(const PrepareParams &params,\n \t\t\t\t\tRPiController::Metadata &rpiMetadata) = 0;\n \tvirtual void platformPrepareAgc(RPiController::Metadata &rpiMetadata) = 0;\ndiff --git a/src/ipa/rpi/vc4/params.h b/src/ipa/rpi/vc4/params.h\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..db6307944e78d8183637782a093ba23b5ff1e0e3\n--- /dev/null\n+++ b/src/ipa/rpi/vc4/params.h\n@@ -0,0 +1,77 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2026, Ideas On Board\n+ *\n+ * Raspberry Pi VC4/BCM2835 ISP Parameters\n+ */\n+\n+#pragma once\n+\n+#include <linux/bcm2835-isp.h>\n+\n+#include <libipa/v4l2_params.h>\n+\n+namespace libcamera {\n+\n+namespace ipa::RPi {\n+\n+enum class BlockType {\n+\tBlackLevel,\n+\tGeq,\n+\tGamma,\n+\tDenoise,\n+\tSharpen,\n+\tDpc,\n+\tCdn,\n+\tCcMatrix,\n+\tLensShading,\n+\tAwbGains,\n+\tDGain,\n+};\n+\n+namespace details {\n+\n+template<BlockType B>\n+struct block_type {\n+};\n+\n+#define BCM2835_DEFINE_BLOCK_TYPE(id, structName, blockId)                \\\n+\ttemplate<>                                                        \\\n+\tstruct block_type<BlockType::id> {                                \\\n+\t\tusing type = struct bcm2835_isp_params_##structName;      \\\n+\t\tstatic constexpr bcm2835_isp_param_block_type blockType = \\\n+\t\t\tBCM2835_ISP_PARAM_BLOCK_##blockId;                \\\n+\t};\n+\n+BCM2835_DEFINE_BLOCK_TYPE(BlackLevel, black_level, BLACK_LEVEL)\n+BCM2835_DEFINE_BLOCK_TYPE(Geq, geq, GEQ)\n+BCM2835_DEFINE_BLOCK_TYPE(Gamma, gamma, GAMMA)\n+BCM2835_DEFINE_BLOCK_TYPE(Denoise, denoise, DENOISE)\n+BCM2835_DEFINE_BLOCK_TYPE(Sharpen, sharpen, SHARPEN)\n+BCM2835_DEFINE_BLOCK_TYPE(Dpc, dpc, DPC)\n+BCM2835_DEFINE_BLOCK_TYPE(Cdn, cdn, CDN)\n+BCM2835_DEFINE_BLOCK_TYPE(CcMatrix, cc_matrix, CC_MATRIX)\n+BCM2835_DEFINE_BLOCK_TYPE(LensShading, lens_shading, LENS_SHADING)\n+BCM2835_DEFINE_BLOCK_TYPE(AwbGains, awb_gains, AWB_GAINS)\n+BCM2835_DEFINE_BLOCK_TYPE(DGain, digital_gain, DIGITAL_GAIN)\n+\n+struct params_traits {\n+\tusing id_type = BlockType;\n+\ttemplate<id_type Id>\n+\tusing id_to_details = block_type<Id>;\n+};\n+\n+} /* namespace details */\n+\n+class Bcm2835Params : public V4L2Params<details::params_traits>\n+{\n+public:\n+\tBcm2835Params(Span<uint8_t> data) : V4L2Params(data,\n+\t\t\t\t\t\t       BCM2835_ISP_PARAM_BUFFER_V1)\n+\t{\n+\t}\n+};\n+\n+} /* namespace ipa::RPi */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/rpi/vc4/vc4.cpp b/src/ipa/rpi/vc4/vc4.cpp\nindex 2b205b2861bdb5bf81abd61fc538e9d39215293e..b35ece7185136f720e4b8a3afefc1c78f92d42b9 100644\n--- a/src/ipa/rpi/vc4/vc4.cpp\n+++ b/src/ipa/rpi/vc4/vc4.cpp\n@@ -30,6 +30,7 @@\n #include \"controller/lux_status.h\"\n #include \"controller/noise_status.h\"\n #include \"controller/sharpen_status.h\"\n+#include \"params.h\"\n \n namespace libcamera {\n \n@@ -59,34 +60,39 @@ private:\n \tint32_t platformStart(const ControlList &controls, StartResult *result) override;\n \tint32_t platformConfigure(const ConfigParams &params, ConfigResult *result) override;\n \n-\tvoid platformPrepareIsp(const PrepareParams &params, RPiController::Metadata &rpiMetadata) override;\n-\tvoid platformPrepareAgc([[maybe_unused]] RPiController::Metadata &rpiMetadata) override;\n+\tvoid platformParamsBufferInit(Span<uint8_t> paramsBuffer) override;\n+\tvoid platformPrepareIsp([[maybe_unused]] const PrepareParams &params,\n+\t\t\t\tRPiController::Metadata &rpiMetadata) override;\n+\tvoid platformPrepareAgc(RPiController::Metadata &rpiMetadata) override;\n \tRPiController::StatisticsPtr platformProcessStats(Span<uint8_t> mem) override;\n \n \tvoid handleControls(const ControlList &controls) override;\n-\tbool validateIspControls();\n-\n-\tvoid applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls);\n-\tvoid applyDG(double digitalGain, const struct AwbStatus *awbStatus, ControlList &ctrls);\n-\tvoid applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls);\n-\tvoid applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, ControlList &ctrls);\n-\tvoid applyGamma(const struct ContrastStatus *contrastStatus, ControlList &ctrls);\n-\tvoid applyGEQ(const struct GeqStatus *geqStatus, ControlList &ctrls);\n-\tvoid applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList &ctrls);\n-\tvoid applySharpen(const struct SharpenStatus *sharpenStatus, ControlList &ctrls);\n-\tvoid applyDPC(const struct DpcStatus *dpcStatus, ControlList &ctrls);\n-\tvoid applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls);\n+\n+\tsize_t platformParamsBytesUsed() const override\n+\t{\n+\t\treturn (ispParams_.has_value()) ? ispParams_->bytesused() : 0;\n+\t}\n+\n+\tvoid applyAWB(const struct AwbStatus *awbStatus, Bcm2835Params &params);\n+\tvoid applyDG(double digitalGain, const struct AwbStatus *awbStatus, Bcm2835Params &params);\n+\tvoid applyCCM(const struct CcmStatus *ccmStatus, Bcm2835Params &params);\n+\tvoid applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, Bcm2835Params &params);\n+\tvoid applyGamma(const struct ContrastStatus *contrastStatus, Bcm2835Params &params);\n+\tvoid applyGEQ(const struct GeqStatus *geqStatus, Bcm2835Params &params);\n+\tvoid applyDenoise(const struct DenoiseStatus *denoiseStatus, Bcm2835Params &params);\n+\tvoid applySharpen(const struct SharpenStatus *sharpenStatus, Bcm2835Params &params);\n+\tvoid applyDPC(const struct DpcStatus *dpcStatus, Bcm2835Params &params);\n+\tvoid applyLS(const struct AlscStatus *lsStatus, Bcm2835Params &params);\n \tvoid applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls);\n \tvoid resampleTable(uint16_t dest[], const std::vector<double> &src, int destW, int destH);\n \n-\t/* VC4 ISP controls. */\n-\tControlInfoMap ispCtrls_;\n-\tControlList ctrls_;\n-\n \t/* LS table allocation passed in from the pipeline handler. */\n \tSharedFD lsTableHandle_;\n \tvoid *lsTable_;\n \n+\t/* Params buffer for the current frame. */\n+\tstd::optional<Bcm2835Params> ispParams_;\n+\n \t/* Remember the most recent AWB values. */\n \tAwbStatus lastAwbStatus_;\n };\n@@ -113,13 +119,6 @@ int32_t IpaVc4::platformStart([[maybe_unused]] const ControlList &controls,\n \n int32_t IpaVc4::platformConfigure(const ConfigParams &params, [[maybe_unused]] ConfigResult *result)\n {\n-\tispCtrls_ = params.ispControls;\n-\tctrls_ = ControlList(ispCtrls_);\n-\tif (!validateIspControls()) {\n-\t\tLOG(IPARPI, Error) << \"ISP control validation failed.\";\n-\t\treturn -1;\n-\t}\n-\n \t/* Store the lens shading table pointer and handle if available. */\n \tif (params.lsTableHandle.isValid()) {\n \t\t/* Remove any previous table, if there was one. */\n@@ -144,51 +143,55 @@ int32_t IpaVc4::platformConfigure(const ConfigParams &params, [[maybe_unused]] C\n \treturn 0;\n }\n \n+void IpaVc4::platformParamsBufferInit(Span<uint8_t> paramsBuffer)\n+{\n+\t/* Initialize the extensible parameter buffer */\n+\tispParams_.emplace(paramsBuffer);\n+}\n+\n void IpaVc4::platformPrepareIsp([[maybe_unused]] const PrepareParams &params,\n \t\t\t\tRPiController::Metadata &rpiMetadata)\n {\n-\tControlList &ctrls = ctrls_;\n-\n \t/* Lock the metadata buffer to avoid constant locks/unlocks. */\n \tstd::unique_lock<RPiController::Metadata> lock(rpiMetadata);\n \n \tAwbStatus *awbStatus = rpiMetadata.getLocked<AwbStatus>(\"awb.status\");\n \tif (awbStatus) {\n-\t\tapplyAWB(awbStatus, ctrls);\n+\t\tapplyAWB(awbStatus, *ispParams_);\n \t\tlastAwbStatus_ = *awbStatus;\n \t}\n \n \tCcmStatus *ccmStatus = rpiMetadata.getLocked<CcmStatus>(\"ccm.status\");\n \tif (ccmStatus)\n-\t\tapplyCCM(ccmStatus, ctrls);\n+\t\tapplyCCM(ccmStatus, *ispParams_);\n \n \tAlscStatus *lsStatus = rpiMetadata.getLocked<AlscStatus>(\"alsc.status\");\n \tif (lsStatus)\n-\t\tapplyLS(lsStatus, ctrls);\n+\t\tapplyLS(lsStatus, *ispParams_);\n \n \tContrastStatus *contrastStatus = rpiMetadata.getLocked<ContrastStatus>(\"contrast.status\");\n \tif (contrastStatus)\n-\t\tapplyGamma(contrastStatus, ctrls);\n+\t\tapplyGamma(contrastStatus, *ispParams_);\n \n \tBlackLevelStatus *blackLevelStatus = rpiMetadata.getLocked<BlackLevelStatus>(\"black_level.status\");\n \tif (blackLevelStatus)\n-\t\tapplyBlackLevel(blackLevelStatus, ctrls);\n+\t\tapplyBlackLevel(blackLevelStatus, *ispParams_);\n \n \tGeqStatus *geqStatus = rpiMetadata.getLocked<GeqStatus>(\"geq.status\");\n \tif (geqStatus)\n-\t\tapplyGEQ(geqStatus, ctrls);\n+\t\tapplyGEQ(geqStatus, *ispParams_);\n \n \tDenoiseStatus *denoiseStatus = rpiMetadata.getLocked<DenoiseStatus>(\"denoise.status\");\n \tif (denoiseStatus)\n-\t\tapplyDenoise(denoiseStatus, ctrls);\n+\t\tapplyDenoise(denoiseStatus, *ispParams_);\n \n \tSharpenStatus *sharpenStatus = rpiMetadata.getLocked<SharpenStatus>(\"sharpen.status\");\n \tif (sharpenStatus)\n-\t\tapplySharpen(sharpenStatus, ctrls);\n+\t\tapplySharpen(sharpenStatus, *ispParams_);\n \n \tDpcStatus *dpcStatus = rpiMetadata.getLocked<DpcStatus>(\"dpc.status\");\n \tif (dpcStatus)\n-\t\tapplyDPC(dpcStatus, ctrls);\n+\t\tapplyDPC(dpcStatus, *ispParams_);\n \n \tconst AfStatus *afStatus = rpiMetadata.getLocked<AfStatus>(\"af.status\");\n \tif (afStatus) {\n@@ -205,10 +208,7 @@ void IpaVc4::platformPrepareAgc(RPiController::Metadata &rpiMetadata)\n \tdouble digitalGain = delayedAgcStatus ? delayedAgcStatus->digitalGain : agcStatus_.digitalGain;\n \tAwbStatus *awbStatus = rpiMetadata.getLocked<AwbStatus>(\"awb.status\");\n \n-\tapplyDG(digitalGain, awbStatus, ctrls_);\n-\n-\tsetIspControls.emit(ctrls_);\n-\tctrls_ = ControlList(ispCtrls_);\n+\tapplyDG(digitalGain, awbStatus, *ispParams_);\n }\n \n RPiController::StatisticsPtr IpaVc4::platformProcessStats(Span<uint8_t> mem)\n@@ -325,48 +325,26 @@ void IpaVc4::handleControls(const ControlList &controls)\n \t}\n }\n \n-bool IpaVc4::validateIspControls()\n+void IpaVc4::applyAWB(const struct AwbStatus *awbStatus, Bcm2835Params &params)\n {\n-\tstatic const uint32_t ctrls[] = {\n-\t\tV4L2_CID_RED_BALANCE,\n-\t\tV4L2_CID_BLUE_BALANCE,\n-\t\tV4L2_CID_DIGITAL_GAIN,\n-\t\tV4L2_CID_USER_BCM2835_ISP_CC_MATRIX,\n-\t\tV4L2_CID_USER_BCM2835_ISP_GAMMA,\n-\t\tV4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL,\n-\t\tV4L2_CID_USER_BCM2835_ISP_GEQ,\n-\t\tV4L2_CID_USER_BCM2835_ISP_DENOISE,\n-\t\tV4L2_CID_USER_BCM2835_ISP_SHARPEN,\n-\t\tV4L2_CID_USER_BCM2835_ISP_DPC,\n-\t\tV4L2_CID_USER_BCM2835_ISP_LENS_SHADING,\n-\t\tV4L2_CID_USER_BCM2835_ISP_CDN,\n-\t};\n-\n-\tfor (auto c : ctrls) {\n-\t\tif (ispCtrls_.find(c) == ispCtrls_.end()) {\n-\t\t\tLOG(IPARPI, Error) << \"Unable to find ISP control \"\n-\t\t\t\t\t   << utils::hex(c);\n-\t\t\treturn false;\n-\t\t}\n-\t}\n-\n-\treturn true;\n-}\n+\tauto block = params.block<BlockType::AwbGains>();\n \n-void IpaVc4::applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls)\n-{\n \tLOG(IPARPI, Debug) << \"Applying WB R: \" << awbStatus->gainR << \" B: \"\n \t\t\t   << awbStatus->gainB;\n \n-\tctrls.set(V4L2_CID_RED_BALANCE,\n-\t\t  static_cast<int32_t>(awbStatus->gainR * 1000));\n-\tctrls.set(V4L2_CID_BLUE_BALANCE,\n-\t\t  static_cast<int32_t>(awbStatus->gainB * 1000));\n+\tblock->awb_gains.r_gain.num = static_cast<int32_t>(awbStatus->gainR * 1000);\n+\tblock->awb_gains.r_gain.den = 1000;\n+\tblock->awb_gains.b_gain.num = static_cast<int32_t>(awbStatus->gainB * 1000);\n+\tblock->awb_gains.b_gain.den = 1000;\n+\n+\tblock.setEnabled(true);\n }\n \n void IpaVc4::applyDG(double digitalGain,\n-\t\t     const struct AwbStatus *awbStatus, ControlList &ctrls)\n+\t\t     const struct AwbStatus *awbStatus, Bcm2835Params &params)\n {\n+\tauto block = params.block<BlockType::DGain>();\n+\n \tif (awbStatus) {\n \t\t/*\n \t\t * We must apply sufficient extra digital gain to stop any of the channel gains being\n@@ -380,13 +358,16 @@ void IpaVc4::applyDG(double digitalGain,\n \t\tdigitalGain *= extraGain;\n \t}\n \n-\tctrls.set(V4L2_CID_DIGITAL_GAIN,\n-\t\t  static_cast<int32_t>(digitalGain * 1000));\n+\tblock->digital_gain.gain.num = static_cast<int32_t>(digitalGain * 1000);\n+\tblock->digital_gain.gain.den = 1000;\n+\n+\tblock.setEnabled(true);\n }\n \n-void IpaVc4::applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls)\n+void IpaVc4::applyCCM(const struct CcmStatus *ccmStatus, Bcm2835Params &params)\n {\n-\tbcm2835_isp_custom_ccm ccm;\n+\tauto block = params.block<BlockType::CcMatrix>();\n+\tbcm2835_isp_custom_ccm &ccm = block->ccm;\n \n \tfor (int i = 0; i < 9; i++) {\n \t\tccm.ccm.ccm[i / 3][i % 3].den = 1000;\n@@ -395,30 +376,28 @@ void IpaVc4::applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls)\n \n \tccm.enabled = 1;\n \tccm.ccm.offsets[0] = ccm.ccm.offsets[1] = ccm.ccm.offsets[2] = 0;\n-\n-\tControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&ccm),\n-\t\t\t\t\t    sizeof(ccm) });\n-\tctrls.set(V4L2_CID_USER_BCM2835_ISP_CC_MATRIX, c);\n+\tblock.setEnabled(true);\n }\n \n-void IpaVc4::applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, ControlList &ctrls)\n+void IpaVc4::applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus,\n+\t\t\t     Bcm2835Params &params)\n {\n-\tbcm2835_isp_black_level blackLevel;\n+\tauto block = params.block<BlockType::BlackLevel>();\n+\tbcm2835_isp_black_level &blackLevel = block->black_level;\n \n \tblackLevel.enabled = 1;\n \tblackLevel.black_level_r = blackLevelStatus->blackLevelR;\n \tblackLevel.black_level_g = blackLevelStatus->blackLevelG;\n \tblackLevel.black_level_b = blackLevelStatus->blackLevelB;\n-\n-\tControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&blackLevel),\n-\t\t\t\t\t    sizeof(blackLevel) });\n-\tctrls.set(V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL, c);\n+\tblock.setEnabled(true);\n }\n \n-void IpaVc4::applyGamma(const struct ContrastStatus *contrastStatus, ControlList &ctrls)\n+void IpaVc4::applyGamma(const struct ContrastStatus *contrastStatus,\n+\t\t\tBcm2835Params &params)\n {\n \tconst unsigned int numGammaPoints = controller_.getHardwareConfig().numGammaPoints;\n-\tstruct bcm2835_isp_gamma gamma;\n+\tauto block = params.block<BlockType::Gamma>();\n+\tstruct bcm2835_isp_gamma &gamma = block->gamma;\n \n \tfor (unsigned int i = 0; i < numGammaPoints - 1; i++) {\n \t\tint x = i < 16 ? i * 1024\n@@ -431,31 +410,27 @@ void IpaVc4::applyGamma(const struct ContrastStatus *contrastStatus, ControlList\n \tgamma.x[numGammaPoints - 1] = 65535;\n \tgamma.y[numGammaPoints - 1] = 65535;\n \tgamma.enabled = 1;\n-\n-\tControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&gamma),\n-\t\t\t\t\t    sizeof(gamma) });\n-\tctrls.set(V4L2_CID_USER_BCM2835_ISP_GAMMA, c);\n+\tblock.setEnabled(true);\n }\n \n-void IpaVc4::applyGEQ(const struct GeqStatus *geqStatus, ControlList &ctrls)\n+void IpaVc4::applyGEQ(const struct GeqStatus *geqStatus, Bcm2835Params &params)\n {\n-\tbcm2835_isp_geq geq;\n+\tauto block = params.block<BlockType::Geq>();\n+\tbcm2835_isp_geq &geq = block->geq;\n \n \tgeq.enabled = 1;\n \tgeq.offset = geqStatus->offset;\n \tgeq.slope.den = 1000;\n \tgeq.slope.num = 1000 * geqStatus->slope;\n-\n-\tControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&geq),\n-\t\t\t\t\t    sizeof(geq) });\n-\tctrls.set(V4L2_CID_USER_BCM2835_ISP_GEQ, c);\n+\tblock.setEnabled(true);\n }\n \n-void IpaVc4::applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList &ctrls)\n+void IpaVc4::applyDenoise(const struct DenoiseStatus *denoiseStatus, Bcm2835Params &params)\n {\n+\tauto blockDenoise = params.block<BlockType::Denoise>();\n \tusing RPiController::DenoiseMode;\n \n-\tbcm2835_isp_denoise denoise;\n+\tbcm2835_isp_denoise &denoise = blockDenoise->denoise;\n \tDenoiseMode mode = static_cast<DenoiseMode>(denoiseStatus->mode);\n \n \tdenoise.enabled = mode != DenoiseMode::Off;\n@@ -464,9 +439,11 @@ void IpaVc4::applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList\n \tdenoise.slope.den = 1000;\n \tdenoise.strength.num = 1000 * denoiseStatus->strength;\n \tdenoise.strength.den = 1000;\n+\tblockDenoise.setEnabled(denoise.enabled);\n \n \t/* Set the CDN mode to match the SDN operating mode. */\n-\tbcm2835_isp_cdn cdn;\n+\tauto blockCdn = params.block<BlockType::Cdn>();\n+\tbcm2835_isp_cdn &cdn = blockCdn->cdn;\n \tswitch (mode) {\n \tcase DenoiseMode::ColourFast:\n \t\tcdn.enabled = 1;\n@@ -479,19 +456,13 @@ void IpaVc4::applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList\n \tdefault:\n \t\tcdn.enabled = 0;\n \t}\n-\n-\tControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&denoise),\n-\t\t\t\t\t    sizeof(denoise) });\n-\tctrls.set(V4L2_CID_USER_BCM2835_ISP_DENOISE, c);\n-\n-\tc = ControlValue(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&cdn),\n-\t\t\t\t\t      sizeof(cdn) });\n-\tctrls.set(V4L2_CID_USER_BCM2835_ISP_CDN, c);\n+\tblockCdn.setEnabled(cdn.enabled);\n }\n \n-void IpaVc4::applySharpen(const struct SharpenStatus *sharpenStatus, ControlList &ctrls)\n+void IpaVc4::applySharpen(const struct SharpenStatus *sharpenStatus, Bcm2835Params &params)\n {\n-\tbcm2835_isp_sharpen sharpen;\n+\tauto block = params.block<BlockType::Sharpen>();\n+\tbcm2835_isp_sharpen &sharpen = block->sharpen;\n \n \tsharpen.enabled = 1;\n \tsharpen.threshold.num = 1000 * sharpenStatus->threshold;\n@@ -500,25 +471,20 @@ void IpaVc4::applySharpen(const struct SharpenStatus *sharpenStatus, ControlList\n \tsharpen.strength.den = 1000;\n \tsharpen.limit.num = 1000 * sharpenStatus->limit;\n \tsharpen.limit.den = 1000;\n-\n-\tControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&sharpen),\n-\t\t\t\t\t    sizeof(sharpen) });\n-\tctrls.set(V4L2_CID_USER_BCM2835_ISP_SHARPEN, c);\n+\tblock.setEnabled(true);\n }\n \n-void IpaVc4::applyDPC(const struct DpcStatus *dpcStatus, ControlList &ctrls)\n+void IpaVc4::applyDPC(const struct DpcStatus *dpcStatus, Bcm2835Params &params)\n {\n-\tbcm2835_isp_dpc dpc;\n+\tauto block = params.block<BlockType::Dpc>();\n+\tbcm2835_isp_dpc &dpc = block->dpc;\n \n \tdpc.enabled = 1;\n \tdpc.strength = dpcStatus->strength;\n-\n-\tControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&dpc),\n-\t\t\t\t\t    sizeof(dpc) });\n-\tctrls.set(V4L2_CID_USER_BCM2835_ISP_DPC, c);\n+\tblock.setEnabled(true);\n }\n \n-void IpaVc4::applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls)\n+void IpaVc4::applyLS(const struct AlscStatus *lsStatus, Bcm2835Params &params)\n {\n \t/*\n \t * Program lens shading tables into pipeline.\n@@ -542,24 +508,24 @@ void IpaVc4::applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls)\n \n \t/* We're going to supply corner sampled tables, 16 bit samples. */\n \tw++, h++;\n-\tbcm2835_isp_lens_shading ls = {\n+\tif (!lsTableHandle_.isValid() || !lsTable_ || w * h * 4 * sizeof(uint16_t) > MaxLsGridSize) {\n+\t\tLOG(IPARPI, Error) << \"Do not have a correctly allocated lens shading table!\";\n+\t\treturn;\n+\t}\n+\n+\tauto block = params.block<BlockType::LensShading>();\n+\tblock->ls = {\n \t\t.enabled = 1,\n \t\t.grid_cell_size = cellSize,\n \t\t.grid_width = w,\n \t\t.grid_stride = w,\n \t\t.grid_height = h,\n-\t\t/* .dmabuf will be filled in by pipeline handler. */\n-\t\t.dmabuf = 0,\n+\t\t.dmabuf = lsTableHandle_.get(),\n \t\t.ref_transform = 0,\n \t\t.corner_sampled = 1,\n \t\t.gain_format = GAIN_FORMAT_U4P10\n \t};\n \n-\tif (!lsTable_ || w * h * 4 * sizeof(uint16_t) > MaxLsGridSize) {\n-\t\tLOG(IPARPI, Error) << \"Do not have a correctly allocate lens shading table!\";\n-\t\treturn;\n-\t}\n-\n \tif (lsStatus) {\n \t\t/* Format will be u4.10 */\n \t\tuint16_t *grid = static_cast<uint16_t *>(lsTable_);\n@@ -570,9 +536,7 @@ void IpaVc4::applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls)\n \t\tresampleTable(grid + 3 * w * h, lsStatus->b, w, h);\n \t}\n \n-\tControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&ls),\n-\t\t\t\t\t    sizeof(ls) });\n-\tctrls.set(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING, c);\n+\tblock.setEnabled(true);\n }\n \n void IpaVc4::applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls)\ndiff --git a/src/libcamera/pipeline/rpi/common/rpi_stream.h b/src/libcamera/pipeline/rpi/common/rpi_stream.h\nindex 300a352a7d39b7ea6908777696d9a22a4520936d..cc18dcc8fa8dd70deb030027be06ea3cd6e2677a 100644\n--- a/src/libcamera/pipeline/rpi/common/rpi_stream.h\n+++ b/src/libcamera/pipeline/rpi/common/rpi_stream.h\n@@ -30,6 +30,7 @@ enum BufferMask {\n \tMaskStats\t\t= 0x010000,\n \tMaskEmbeddedData\t= 0x020000,\n \tMaskBayerData\t\t= 0x040000,\n+\tMaskParams\t\t= 0x080000,\n };\n \n struct BufferObject {\ndiff --git a/src/libcamera/pipeline/rpi/pisp/pisp.cpp b/src/libcamera/pipeline/rpi/pisp/pisp.cpp\nindex 7bcba32b9b58b1a8782f956d20248e4828b2ba52..4a5dcab209890313630280d2593e08ceae39ce67 100644\n--- a/src/libcamera/pipeline/rpi/pisp/pisp.cpp\n+++ b/src/libcamera/pipeline/rpi/pisp/pisp.cpp\n@@ -754,7 +754,8 @@ public:\n \tvoid beOutputDequeue(FrameBuffer *buffer);\n \n \tvoid processStatsComplete(const ipa::RPi::BufferIds &buffers);\n-\tvoid prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers);\n+\tvoid prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers,\n+\t\t\t\tunsigned int paramsBytesUsed);\n \tvoid setCameraTimeout(uint32_t maxFrameLengthMs);\n \n \t/* Array of CFE and ISP device streams and associated buffers/streams. */\n@@ -1868,7 +1869,8 @@ void PiSPCameraData::setCameraTimeout(uint32_t maxFrameLengthMs)\n \tcfe_[Cfe::Output0].dev()->setDequeueTimeout(timeout);\n }\n \n-void PiSPCameraData::prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers)\n+void PiSPCameraData::prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers,\n+\t\t\t\t\t[[maybe_unused]] unsigned int paramsBytesUsed)\n {\n \tunsigned int embeddedId = buffers.embedded & RPi::MaskID;\n \tunsigned int bayerId = buffers.bayer & RPi::MaskID;\ndiff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp\nindex cd1ec4f486990bf620f44bc6cd3ef6ae90c91327..b403992d1f5d32fd79436d7c5652afd9715ada0b 100644\n--- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp\n+++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp\n@@ -32,7 +32,7 @@ using StreamParams = RPi::RPiCameraConfiguration::StreamParams;\n namespace {\n \n enum class Unicam : unsigned int { Image, Embedded };\n-enum class Isp : unsigned int { Input, Output0, Output1, Stats };\n+enum class Isp : unsigned int { Input, Output0, Output1, Stats, Params };\n \n static constexpr unsigned int kUnicamSinkPad = 0;\n static constexpr unsigned int kUnicamSourceImagePad = 1;\n@@ -84,15 +84,15 @@ public:\n \tvoid ispOutputDequeue(FrameBuffer *buffer);\n \n \tvoid processStatsComplete(const ipa::RPi::BufferIds &buffers);\n-\tvoid prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers);\n-\tvoid setIspControls(const ControlList &controls);\n+\tvoid prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers,\n+\t\t\t\tunsigned int paramsBytesUsed);\n \tvoid setCameraTimeout(uint32_t maxFrameLengthMs);\n \n \tstd::unique_ptr<V4L2Subdevice> unicamSubdev_;\n \n \t/* Array of Unicam and ISP device streams and associated buffers/streams. */\n \tRPi::Device<Unicam, 2> unicam_;\n-\tRPi::Device<Isp, 4> isp_;\n+\tRPi::Device<Isp, 5> isp_;\n \n \t/* DMAHEAP allocation helper. */\n \tDmaBufAllocator dmaHeap_;\n@@ -266,6 +266,13 @@ int PipelineHandlerVc4::allocateBuffers(Camera *camera)\n \t\t\t\t     std::max<int>(data->config_.minUnicamBuffers,\n \t\t\t\t\t\t   minBuffers - numRawBuffers);\n \n+\t\t} else if (stream == &data->isp_[Isp::Params]) {\n+\t\t\t/*\n+\t\t\t * Parameter buffers are dequeued asyncrhonously by the driver\n+\t\t\t * as soon as it sends each parameter to VC4. Ideally, 1 buffer\n+\t\t\t * would be sufficient, but we alot 2 to be safe.\n+\t\t\t */\n+\t\t\tnumBuffers = 2;\n \t\t} else if (stream == &data->unicam_[Unicam::Embedded]) {\n \t\t\t/*\n \t\t\t * Embedded data buffers are (currently) for internal use, and\n@@ -302,10 +309,11 @@ int PipelineHandlerVc4::allocateBuffers(Camera *camera)\n \t}\n \n \t/*\n-\t * Pass the stats and embedded data buffers to the IPA. No other\n+\t * Pass the stats, embedded data and params buffers to the IPA. No other\n \t * buffers need to be passed.\n \t */\n \tmapBuffers(camera, data->isp_[Isp::Stats].getBuffers(), RPi::MaskStats);\n+\tmapBuffers(camera, data->isp_[Isp::Params].getBuffers(), RPi::MaskParams);\n \tif (data->sensorMetadata_)\n \t\tmapBuffers(camera, data->unicam_[Unicam::Embedded].getBuffers(),\n \t\t\t   RPi::MaskEmbeddedData);\n@@ -324,13 +332,14 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer\n \n \tMediaEntity *unicamSubdev = unicam->getEntityByName(\"unicam\");\n \tMediaEntity *unicamImage = unicam->getEntityByName(\"unicam-image\");\n-\tMediaEntity *ispOutput0 = isp->getEntityByName(\"bcm2835-isp0-output0\");\n-\tMediaEntity *ispCapture1 = isp->getEntityByName(\"bcm2835-isp0-capture1\");\n-\tMediaEntity *ispCapture2 = isp->getEntityByName(\"bcm2835-isp0-capture2\");\n-\tMediaEntity *ispCapture3 = isp->getEntityByName(\"bcm2835-isp0-capture3\");\n-\n-\tif (!unicamSubdev || !unicamImage || !ispOutput0 || !ispCapture1 ||\n-\t    !ispCapture2 || !ispCapture3)\n+\tMediaEntity *ispOutput0 = isp->getEntityByName(\"bcm2835-isp-output0\");\n+\tMediaEntity *ispCapture0 = isp->getEntityByName(\"bcm2835-isp-capture0\");\n+\tMediaEntity *ispCapture1 = isp->getEntityByName(\"bcm2835-isp-capture1\");\n+\tMediaEntity *ispCapture2 = isp->getEntityByName(\"bcm2835-isp-stats2\");\n+\tMediaEntity *ispParams = isp->getEntityByName(\"bcm2835-isp-params\");\n+\n+\tif (!unicamSubdev || !unicamImage || !ispOutput0 || !ispCapture0 ||\n+\t    !ispCapture1 || !ispCapture2 || !ispParams)\n \t\treturn -ENOENT;\n \n \t/* Create the unicam subdev and video streams. */\n@@ -347,9 +356,12 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer\n \n \t/* Tag the ISP input stream as an import stream. */\n \tdata->isp_[Isp::Input] = RPi::Stream(\"ISP Input\", ispOutput0, StreamFlag::ImportOnly);\n-\tdata->isp_[Isp::Output0] = RPi::Stream(\"ISP Output0\", ispCapture1);\n-\tdata->isp_[Isp::Output1] = RPi::Stream(\"ISP Output1\", ispCapture2);\n-\tdata->isp_[Isp::Stats] = RPi::Stream(\"ISP Stats\", ispCapture3);\n+\tdata->isp_[Isp::Output0] = RPi::Stream(\"ISP Output0\", ispCapture0);\n+\tdata->isp_[Isp::Output1] = RPi::Stream(\"ISP Output1\", ispCapture1);\n+\tdata->isp_[Isp::Stats] = RPi::Stream(\"ISP Stats\", ispCapture2);\n+\t/* Tag the ISP params stream as MMAP (for writing into it in the IPA) and recurrent. */\n+\tdata->isp_[Isp::Params] = RPi::Stream(\"ISP Params\", ispParams,\n+\t\t\t\t\t      StreamFlag::RequiresMmap | StreamFlag::Recurrent);\n \n \t/* Wire up all the buffer connections. */\n \tdata->unicam_[Unicam::Image].dev()->bufferReady.connect(data, &Vc4CameraData::unicamBufferDequeue);\n@@ -357,6 +369,7 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer\n \tdata->isp_[Isp::Output0].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);\n \tdata->isp_[Isp::Output1].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);\n \tdata->isp_[Isp::Stats].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);\n+\tdata->isp_[Isp::Params].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);\n \n \tif (data->sensorMetadata_ ^ !!data->unicam_[Unicam::Embedded].dev()) {\n \t\tLOG(RPI, Warning) << \"Mismatch between Unicam and CamHelper for embedded data usage!\";\n@@ -398,7 +411,6 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer\n \t/* Write up all the IPA connections. */\n \tdata->ipa_->processStatsComplete.connect(data, &Vc4CameraData::processStatsComplete);\n \tdata->ipa_->prepareIspComplete.connect(data, &Vc4CameraData::prepareIspComplete);\n-\tdata->ipa_->setIspControls.connect(data, &Vc4CameraData::setIspControls);\n \tdata->ipa_->setCameraTimeout.connect(data, &Vc4CameraData::setCameraTimeout);\n \n \t/*\n@@ -757,6 +769,16 @@ int Vc4CameraData::platformConfigure(const RPi::RPiCameraConfiguration *rpiConfi\n \t\treturn ret;\n \t}\n \n+\t/* ISP parameters input format. */\n+\tformat = {};\n+\tformat.fourcc = V4L2PixelFormat(V4L2_META_FMT_BCM2835_ISP_PARAMS);\n+\tret = isp_[Isp::Params].dev()->setFormat(&format);\n+\tif (ret) {\n+\t\tLOG(RPI, Error) << \"Failed to set format on ISP params stream: \"\n+\t\t\t\t<< format;\n+\t\treturn ret;\n+\t}\n+\n \t/*\n \t * Configure the Unicam embedded data output format only if the sensor\n \t * supports it.\n@@ -800,8 +822,6 @@ int Vc4CameraData::platformConfigure(const RPi::RPiCameraConfiguration *rpiConfi\n \n int Vc4CameraData::platformConfigureIpa(ipa::RPi::ConfigParams &params)\n {\n-\tparams.ispControls = isp_[Isp::Input].dev()->controls();\n-\n \t/* Allocate the lens shading table via dmaHeap and pass to the IPA. */\n \tif (!lsTable_.isValid()) {\n \t\tlsTable_ = SharedFD(dmaHeap_.alloc(\"ls_grid\", ipa::RPi::MaxLsGridSize));\n@@ -945,46 +965,41 @@ void Vc4CameraData::processStatsComplete(const ipa::RPi::BufferIds &buffers)\n }\n \n void Vc4CameraData::prepareIspComplete(const ipa::RPi::BufferIds &buffers,\n-\t\t\t\t       [[maybe_unused]] bool stitchSwapBuffers)\n+\t\t\t\t       [[maybe_unused]] bool stitchSwapBuffers,\n+\t\t\t\t       unsigned int paramsBytesUsed)\n {\n \tunsigned int embeddedId = buffers.embedded & RPi::MaskID;\n-\tunsigned int bayer = buffers.bayer & RPi::MaskID;\n+\tunsigned int bayerId = buffers.bayer & RPi::MaskID;\n+\tunsigned int paramsId = buffers.params & RPi::MaskID;\n \tFrameBuffer *buffer;\n \n \tif (!isRunning())\n \t\treturn;\n \n-\tbuffer = unicam_[Unicam::Image].getBuffers().at(bayer & RPi::MaskID).buffer;\n-\tLOG(RPI, Debug) << \"Input re-queue to ISP, buffer id \" << (bayer & RPi::MaskID)\n+\t/* Queue params buffer */\n+\tbuffer = isp_[Isp::Params].getBuffers().at(paramsId).buffer;\n+\tbuffer->_d()->metadata().planes()[0].bytesused = paramsBytesUsed;\n+\tLOG(RPI, Debug) << \"Params re-queue to ISP, buffer id \" << paramsId\n+\t\t\t<< \", timestamp: \" << buffer->metadata().timestamp\n+\t\t\t<< \", bytes used: \" << buffer->_d()->metadata().planes()[0].bytesused;\n+\n+\tisp_[Isp::Params].queueBuffer(buffer);\n+\n+\t/* Queue input buffer */\n+\tbuffer = unicam_[Unicam::Image].getBuffers().at(bayerId).buffer;\n+\tLOG(RPI, Debug) << \"Input re-queue to ISP, buffer id \" << bayerId\n \t\t\t<< \", timestamp: \" << buffer->metadata().timestamp;\n \n \tisp_[Isp::Input].queueBuffer(buffer);\n \n \tif (sensorMetadata_ && embeddedId) {\n-\t\tbuffer = unicam_[Unicam::Embedded].getBuffers().at(embeddedId & RPi::MaskID).buffer;\n+\t\tbuffer = unicam_[Unicam::Embedded].getBuffers().at(embeddedId).buffer;\n \t\thandleStreamBuffer(buffer, &unicam_[Unicam::Embedded]);\n \t}\n \n \thandleState();\n }\n \n-void Vc4CameraData::setIspControls(const ControlList &controls)\n-{\n-\tControlList ctrls = controls;\n-\n-\tif (ctrls.contains(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING)) {\n-\t\tControlValue &value =\n-\t\t\tconst_cast<ControlValue &>(ctrls.get(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING));\n-\t\tSpan<uint8_t> s = value.data();\n-\t\tbcm2835_isp_lens_shading *ls =\n-\t\t\treinterpret_cast<bcm2835_isp_lens_shading *>(s.data());\n-\t\tls->dmabuf = lsTable_.get();\n-\t}\n-\n-\tisp_[Isp::Input].dev()->setControls(&ctrls);\n-\thandleState();\n-}\n-\n void Vc4CameraData::setCameraTimeout(uint32_t maxFrameLengthMs)\n {\n \t/*\n@@ -1026,9 +1041,6 @@ void Vc4CameraData::tryRunPipeline()\n \n \tunsigned int bayer = unicam_[Unicam::Image].getBufferId(bayerFrame.buffer);\n \n-\tLOG(RPI, Debug) << \"Signalling prepareIsp:\"\n-\t\t\t<< \" Bayer buffer id: \" << bayer;\n-\n \tipa::RPi::PrepareParams params;\n \tparams.buffers.bayer = RPi::MaskBayerData | bayer;\n \tparams.sensorControls = std::move(bayerFrame.controls);\n@@ -1037,6 +1049,16 @@ void Vc4CameraData::tryRunPipeline()\n \tparams.delayContext = bayerFrame.delayContext;\n \tparams.buffers.embedded = 0;\n \n+\tconst RPi::BufferObject &paramBufObj = isp_[Isp::Params].acquireBuffer();\n+\tASSERT(paramBufObj.mapped);\n+\n+\tunsigned int param = isp_[Isp::Params].getBufferId(paramBufObj.buffer);\n+\tparams.buffers.params = RPi::MaskParams | param;\n+\n+\tLOG(RPI, Debug) << \"Signalling prepareIsp:\"\n+\t\t\t<< \" Bayer buffer id: \" << bayer\n+\t\t\t<< \" Param buffer id: \" << param;\n+\n \tif (embeddedBuffer) {\n \t\tunsigned int embeddedId = unicam_[Unicam::Embedded].getBufferId(embeddedBuffer);\n \n",
    "prefixes": [
        "3/3"
    ]
}