Show a patch.

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

{
    "id": 23926,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/23926/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/23926/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/1.1/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": "<20250724065256.75175-10-dan.scally@ideasonboard.com>",
    "date": "2025-07-24T06:52:55",
    "name": "[09/10] libcamera: mali-c55: Use the RZG2LCRU class",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "b938486aa0a560fde9b3d14fa515283d8460d1eb",
    "submitter": {
        "id": 156,
        "url": "https://patchwork.libcamera.org/api/1.1/people/156/?format=api",
        "name": "Dan Scally",
        "email": "dan.scally@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/23926/mbox/",
    "series": [
        {
            "id": 5319,
            "url": "https://patchwork.libcamera.org/api/1.1/series/5319/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5319",
            "date": "2025-07-24T06:52:46",
            "name": "Support memory input mode in mali-c55",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/5319/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/23926/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/23926/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 5E613C3323\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 24 Jul 2025 06:53:32 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 32B056909F;\n\tThu, 24 Jul 2025 08:53:29 +0200 (CEST)",
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 929B269086\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 24 Jul 2025 08:53:16 +0200 (CEST)",
            "from mail.ideasonboard.com\n\t(cpc141996-chfd3-2-0-cust928.12-3.cable.virginm.net [86.13.91.161])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id B86ED7F0;\n\tThu, 24 Jul 2025 08:52:37 +0200 (CEST)"
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"GdueKGGM\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1753339957;\n\tbh=Py0ZdPv1jQAnOc+V9n/Y+D6Y4uSbbR6I3rIxip3/9EY=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=GdueKGGMYpnrQ/kj18TpZNM9Hotb+3wqu10YX/31JQj3WanSS2r0WOTOFvgkR6tl0\n\tWsiwkKn1Iv4QK6sXMbq50BFeVLAPlYpVEyeogllYrNMKF5+Mo3i/KjzkeM12JjQUTD\n\tqiz8lE9cvllR02RCLCqxIJMHC/NcVNfcXN17/t/U=",
        "From": "Daniel Scally <dan.scally@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Cc": "Daniel Scally <dan.scally@ideasonboard.com>",
        "Subject": "[PATCH 09/10] libcamera: mali-c55: Use the RZG2LCRU class",
        "Date": "Thu, 24 Jul 2025 07:52:55 +0100",
        "Message-Id": "<20250724065256.75175-10-dan.scally@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.34.1",
        "In-Reply-To": "<20250724065256.75175-1-dan.scally@ideasonboard.com>",
        "References": "<20250724065256.75175-1-dan.scally@ideasonboard.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "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": "Use the RZG2LCRU class to add the ability to the mali-c55 pipeline\nhandler to read frames from memory and process them. The data flow\nis different to an inline configuration; instead of filling the\nparams buffer immediately on queueRequestDevice(), queued requests\nare parked in a pending queue until a buffer from the CRU device is\navailable to be queued to that device. Once that buffer is filled\nwith image data by the CRU the parameter buffer is filled and the\nimage buffer queued to the ISP.\n\nSigned-off-by: Daniel Scally <dan.scally@ideasonboard.com>\n---\n src/libcamera/pipeline/mali-c55/mali-c55.cpp | 208 ++++++++++++++++++-\n 1 file changed, 201 insertions(+), 7 deletions(-)",
    "diff": "diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp\nindex 5944823d..2a396950 100644\n--- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp\n+++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp\n@@ -19,6 +19,7 @@\n #include <libcamera/base/log.h>\n \n #include <libcamera/camera.h>\n+#include <libcamera/controls.h>\n #include <libcamera/formats.h>\n #include <libcamera/geometry.h>\n #include <libcamera/property_ids.h>\n@@ -42,6 +43,8 @@\n #include \"libcamera/internal/v4l2_subdevice.h\"\n #include \"libcamera/internal/v4l2_videodevice.h\"\n \n+#include \"rzg2l-cru.h\"\n+\n namespace {\n \n bool isFormatRaw(const libcamera::PixelFormat &pixFmt)\n@@ -84,6 +87,7 @@ struct MaliC55FrameInfo {\n \n \tFrameBuffer *paramBuffer;\n \tFrameBuffer *statBuffer;\n+\tFrameBuffer *rawBuffer;\n \n \tbool paramsDone;\n \tbool statsDone;\n@@ -112,13 +116,14 @@ public:\n \tPixelFormat adjustRawFormat(const PixelFormat &pixFmt) const;\n \tSize adjustRawSizes(const PixelFormat &pixFmt, const Size &rawSize) const;\n \n-\tstd::unique_ptr<CameraSensor> sensor_;\n+\tstd::shared_ptr<CameraSensor> sensor_;\n \n \tMediaEntity *entity_;\n-\tstd::unique_ptr<V4L2Subdevice> csi_;\n+\tstd::shared_ptr<V4L2Subdevice> csi_;\n \tstd::unique_ptr<V4L2Subdevice> sd_;\n \tStream frStream_;\n \tStream dsStream_;\n+\tstd::unique_ptr<RZG2LCRU> cru_;\n \n \tstd::unique_ptr<ipa::mali_c55::IPAProxyMaliC55> ipa_;\n \tstd::vector<IPABuffer> ipaStatBuffers_;\n@@ -199,6 +204,9 @@ void MaliC55CameraData::setSensorControls(const ControlList &sensorControls)\n \n const std::vector<Size> MaliC55CameraData::sizes(unsigned int mbusCode) const\n {\n+\tif (cru_)\n+\t\treturn cru_->sizes(mbusCode);\n+\n \tif (sensor_)\n \t\treturn sensor_->sizes(mbusCode);\n \n@@ -222,6 +230,9 @@ const std::vector<Size> MaliC55CameraData::sizes(unsigned int mbusCode) const\n \n const Size MaliC55CameraData::resolution() const\n {\n+\tif (cru_)\n+\t\treturn cru_->resolution();\n+\n \tif (sensor_)\n \t\treturn sensor_->resolution();\n \n@@ -615,11 +626,14 @@ public:\n \tint start(Camera *camera, const ControlList *controls) override;\n \tvoid stopDevice(Camera *camera) override;\n \n+\tvoid queuePendingRequests();\n+\tvoid cancelPendingRequests();\n \tint queueRequestDevice(Camera *camera, Request *request) override;\n \n \tvoid imageBufferReady(FrameBuffer *buffer);\n \tvoid paramsBufferReady(FrameBuffer *buffer);\n \tvoid statsBufferReady(FrameBuffer *buffer);\n+\tvoid cruBufferReady(FrameBuffer *buffer);\n \tvoid paramsComputed(unsigned int requestId, uint32_t bytesused);\n \tvoid statsProcessed(unsigned int requestId, const ControlList &metadata);\n \n@@ -683,11 +697,15 @@ private:\n \t\t\t\tconst std::string &name);\n \tbool registerTPGCamera(MediaLink *link);\n \tbool registerSensorCamera(MediaLink *link);\n+\tbool registerMemoryInputCamera();\n \n \tMediaDevice *media_;\n+\tMediaDevice *cruMedia_;\n \tstd::unique_ptr<V4L2Subdevice> isp_;\n \tstd::unique_ptr<V4L2VideoDevice> stats_;\n \tstd::unique_ptr<V4L2VideoDevice> params_;\n+\tstd::unique_ptr<V4L2Subdevice> ivc_;\n+\tstd::unique_ptr<V4L2VideoDevice> input_;\n \n \tstd::vector<std::unique_ptr<FrameBuffer>> statsBuffers_;\n \tstd::queue<FrameBuffer *> availableStatsBuffers_;\n@@ -697,6 +715,11 @@ private:\n \n \tstd::map<unsigned int, MaliC55FrameInfo> frameInfoMap_;\n \n+\t/* Requests for which no buffer has been queued to the CRU device yet. */\n+\tstd::queue<Request *> pendingRequests_;\n+\t/* Requests queued to the CRU device but not yet processed by the ISP. */\n+\tstd::queue<Request *> processingRequests_;\n+\n \tstd::array<MaliC55Pipe, MaliC55NumPipes> pipes_;\n \n \tbool dsFitted_;\n@@ -929,9 +952,16 @@ int PipelineHandlerMaliC55::configure(Camera *camera,\n \n \t/* Link the graph depending if we are operating the TPG or a sensor. */\n \tMaliC55CameraData *data = cameraData(camera);\n-\tif (data->csi_) {\n+\tif (data->cru_) {\n+\t\tconst MediaEntity *ivcEntity = ivc_->entity();\n+\t\tret = ivcEntity->getPadByIndex(1)->links()[0]->setEnabled(true);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t} else if (data->csi_) {\n \t\tconst MediaEntity *csiEntity = data->csi_->entity();\n \t\tret = csiEntity->getPadByIndex(1)->links()[0]->setEnabled(true);\n+\t\tif (ret)\n+\t\t\treturn ret;\n \t} else {\n \t\tret = data->entity_->getPadByIndex(0)->links()[0]->setEnabled(true);\n \t}\n@@ -952,14 +982,43 @@ int PipelineHandlerMaliC55::configure(Camera *camera,\n \t\t\treturn ret;\n \t}\n \n+\t/*\n+\t * This could be a CSI receiver directly connected to the ISP, or else\n+\t * one in the CRU's graph.\n+\t */\n \tif (data->csi_) {\n \t\tret = data->csi_->setFormat(0, &subdevFormat);\n \t\tif (ret)\n \t\t\treturn ret;\n \n-\t\tret = data->csi_->getFormat(1, &subdevFormat);\n-\t\tif (ret)\n-\t\t\treturn ret;\n+\t\tif (data->cru_) {\n+\t\t\tV4L2DeviceFormat inputFormat;\n+\n+\t\t\tret = data->cru_->configure(&subdevFormat, &inputFormat);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\n+\t\t\t/*\n+\t\t\t* The IVC video device needs to be configured with the same\n+\t\t\t* format as the CRU.\n+\t\t\t*/\n+\n+\t\t\tret = input_->setFormat(&inputFormat);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\n+\t\t\tret = ivc_->setFormat(0, &subdevFormat);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\n+\t\t\tret = ivc_->getFormat(1, &subdevFormat);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t} else {\n+\t\t\tret = data->csi_->getFormat(1, &subdevFormat);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n \t}\n \n \tV4L2DeviceFormat statsFormat;\n@@ -1120,6 +1179,11 @@ void PipelineHandlerMaliC55::freeBuffers(Camera *camera)\n \tif (params_->releaseBuffers())\n \t\tLOG(MaliC55, Error) << \"Failed to release params buffers\";\n \n+\tif (data->cru_) {\n+\t\tif (input_->releaseBuffers())\n+\t\t\tLOG(MaliC55, Error) << \"Failed to release input buffers\";\n+\t}\n+\n \treturn;\n }\n \n@@ -1135,6 +1199,12 @@ int PipelineHandlerMaliC55::allocateBuffers(Camera *camera)\n \t\tdata->dsStream_.configuration().bufferCount,\n \t});\n \n+\tif (input_) {\n+\t\tret = input_->importBuffers(RZG2LCRU::kBufferCount);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\t}\n+\n \tret = stats_->allocateBuffers(bufferCount, &statsBuffers_);\n \tif (ret < 0)\n \t\treturn ret;\n@@ -1174,6 +1244,24 @@ int PipelineHandlerMaliC55::start(Camera *camera, [[maybe_unused]] const Control\n \tif (ret)\n \t\treturn ret;\n \n+\tif (data->cru_) {\n+\t\tret = data->cru_->start();\n+\t\tif (ret) {\n+\t\t\tLOG(MaliC55, Error)\n+\t\t\t\t<< \"Failed to start CRU \" << camera->id();\n+\t\t\tfreeBuffers(camera);\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tret = input_->streamOn();\n+\t\tif (ret) {\n+\t\t\tLOG(MaliC55, Error)\n+\t\t\t\t<< \"Failed to start IVC\" << camera->id();\n+\t\t\tfreeBuffers(camera);\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n \tif (data->ipa_) {\n \t\tret = data->ipa_->start();\n \t\tif (ret) {\n@@ -1263,6 +1351,12 @@ void PipelineHandlerMaliC55::stopDevice(Camera *camera)\n \t\tpipe.cap->releaseBuffers();\n \t}\n \n+\tif (data->cru_) {\n+\t\tcancelPendingRequests();\n+\t\tinput_->streamOff();\n+\t\tdata->cru_->stop();\n+\t}\n+\n \tstats_->streamOff();\n \tparams_->streamOff();\n \tif (data->ipa_)\n@@ -1366,10 +1460,80 @@ void PipelineHandlerMaliC55::applyScalerCrop(Camera *camera,\n \t}\n }\n \n+void PipelineHandlerMaliC55::cancelPendingRequests()\n+{\n+\tprocessingRequests_ = {};\n+\n+\twhile (!pendingRequests_.empty()) {\n+\t\tRequest *request = pendingRequests_.front();\n+\n+\t\tfor (auto it : request->buffers()) {\n+\t\t\tFrameBuffer *buffer = it.second;\n+\t\t\tbuffer->_d()->cancel();\n+\t\t\tcompleteBuffer(request, buffer);\n+\t\t}\n+\n+\t\tcompleteRequest(request);\n+\t\tpendingRequests_.pop();\n+\t}\n+}\n+\n+void PipelineHandlerMaliC55::queuePendingRequests()\n+{\n+\twhile (!pendingRequests_.empty()) {\n+\t\tRequest *request = pendingRequests_.front();\n+\n+\t\tif (availableStatsBuffers_.empty()) {\n+\t\t\tLOG(MaliC55, Error) << \"Stats buffer underrun\";\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tif (availableParamsBuffers_.empty()) {\n+\t\t\tLOG(MaliC55, Error) << \"Params buffer underrun\";\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tMaliC55FrameInfo frameInfo;\n+\t\tframeInfo.request = request;\n+\n+\t\tMaliC55CameraData *data = cameraData(request->_d()->camera());\n+\t\tframeInfo.rawBuffer = data->cru_->queueBuffer(request);\n+\t\tif (!frameInfo.rawBuffer)\n+\t\t\treturn;\n+\n+\t\tframeInfo.statBuffer = availableStatsBuffers_.front();\n+\t\tavailableStatsBuffers_.pop();\n+\t\tframeInfo.paramBuffer = availableParamsBuffers_.front();\n+\t\tavailableParamsBuffers_.pop();\n+\n+\t\tframeInfo.paramsDone = false;\n+\t\tframeInfo.statsDone = false;\n+\n+\t\tframeInfoMap_[request->sequence()] = frameInfo;\n+\n+\t\tdata->ipa_->queueRequest(request->sequence(), request->controls());\n+\n+\t\tpendingRequests_.pop();\n+\t\tprocessingRequests_.push(request);\n+\t}\n+}\n+\n int PipelineHandlerMaliC55::queueRequestDevice(Camera *camera, Request *request)\n {\n \tMaliC55CameraData *data = cameraData(camera);\n \n+\t/*\n+\t * If we're in memory input mode, we need to pop the requests onto the\n+\t * pending list until a CRU buffer is ready...otherwise we can just do\n+\t * everything immediately.\n+\t */\n+\tif (data->cru_) {\n+\t\tpendingRequests_.push(request);\n+\t\tqueuePendingRequests();\n+\n+\t\treturn 0;\n+\t}\n+\n \t/* Do not run the IPA if the TPG is in use. */\n \tif (!data->ipa_) {\n \t\tMaliC55FrameInfo frameInfo;\n@@ -1434,7 +1598,8 @@ MaliC55FrameInfo *PipelineHandlerMaliC55::findFrameInfo(FrameBuffer *buffer)\n {\n \tfor (auto &[sequence, info] : frameInfoMap_) {\n \t\tif (info.paramBuffer == buffer ||\n-\t\t    info.statBuffer == buffer)\n+\t\t    info.statBuffer == buffer ||\n+\t\t    info.rawBuffer == buffer)\n \t\t\treturn &info;\n \t}\n \n@@ -1496,6 +1661,32 @@ void PipelineHandlerMaliC55::statsBufferReady(FrameBuffer *buffer)\n \t\t\t\t sensorControls);\n }\n \n+void PipelineHandlerMaliC55::cruBufferReady(FrameBuffer *buffer)\n+{\n+\tMaliC55FrameInfo *info = findFrameInfo(buffer);\n+\tRequest *request = info->request;\n+\tASSERT(info);\n+\n+\tif (buffer->metadata().status == FrameMetadata::FrameCancelled) {\n+\t\tfor (auto it : request->buffers()) {\n+\t\t\tFrameBuffer *b = it.second;\n+\t\t\tb->_d()->cancel();\n+\t\t\tcompleteBuffer(request, b);\n+\t\t}\n+\n+\t\tframeInfoMap_.erase(request->sequence());\n+\t\tcompleteRequest(request);\n+\t\treturn;\n+\t}\n+\n+\trequest->metadata().set(controls::SensorTimestamp,\n+\t\t\t\tbuffer->metadata().timestamp);\n+\n+\t/* Ought we do something with the sensor's controls here...? */\n+\tMaliC55CameraData *data = cameraData(request->_d()->camera());\n+\tdata->ipa_->fillParams(request->sequence(), info->paramBuffer->cookie());\n+}\n+\n void PipelineHandlerMaliC55::paramsComputed(unsigned int requestId, uint32_t bytesused)\n {\n \tMaliC55FrameInfo &frameInfo = frameInfoMap_[requestId];\n@@ -1516,6 +1707,9 @@ void PipelineHandlerMaliC55::paramsComputed(unsigned int requestId, uint32_t byt\n \n \t\tpipe->cap->queueBuffer(buffer);\n \t}\n+\n+\tif (data->cru_)\n+\t\tinput_->queueBuffer(frameInfo.rawBuffer);\n }\n \n void PipelineHandlerMaliC55::statsProcessed(unsigned int requestId,\n",
    "prefixes": [
        "09/10"
    ]
}