Show a patch.

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

{
    "id": 25474,
    "url": "https://patchwork.libcamera.org/api/patches/25474/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/25474/",
    "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": "<20251210164055.17856-10-david.plowman@raspberrypi.com>",
    "date": "2025-12-10T16:15:24",
    "name": "[09/11] ipa: rpi: Support memory cameras",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "7fe8f54cebf09b9c0e72d17756155b91dd6ce57a",
    "submitter": {
        "id": 42,
        "url": "https://patchwork.libcamera.org/api/people/42/?format=api",
        "name": "David Plowman",
        "email": "david.plowman@raspberrypi.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/25474/mbox/",
    "series": [
        {
            "id": 5650,
            "url": "https://patchwork.libcamera.org/api/series/5650/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5650",
            "date": "2025-12-10T16:15:15",
            "name": "Bayer re-processing",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/5650/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/25474/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/25474/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 64454C326B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 10 Dec 2025 16:41:13 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 7F7C9614BB;\n\tWed, 10 Dec 2025 17:41:12 +0100 (CET)",
            "from mail-wm1-x333.google.com (mail-wm1-x333.google.com\n\t[IPv6:2a00:1450:4864:20::333])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id CCACD614CD\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 10 Dec 2025 17:41:06 +0100 (CET)",
            "by mail-wm1-x333.google.com with SMTP id\n\t5b1f17b1804b1-47796a837c7so68795e9.0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 10 Dec 2025 08:41:06 -0800 (PST)",
            "from davidp-pi5.pitowers.org\n\t([2a00:1098:3142:1f:88ea:c658:5b20:5e46])\n\tby smtp.gmail.com with ESMTPSA id\n\t5b1f17b1804b1-47a88371e13sm1270415e9.11.2025.12.10.08.41.05\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tWed, 10 Dec 2025 08:41:05 -0800 (PST)"
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=raspberrypi.com header.i=@raspberrypi.com\n\theader.b=\"GW77hlHX\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google; t=1765384866; x=1765989666;\n\tdarn=lists.libcamera.org; \n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:from:to:cc:subject:date\n\t:message-id:reply-to;\n\tbh=blvqyUtkLPoAGDVhsamTfHUttwbtDGPkwxR46PSscrY=;\n\tb=GW77hlHXXzbYBO/sGZCby0w8ecSfCPqUg1MSjhn315fDuo5jvZbtyolPVZLu2X3aj5\n\tIagC4g+bnvhHhJbYaG8EGn7ukqmjWb7CBwdwkxY2AioY4CkAyXdnDjKsZY427SX4o3Qa\n\tbdy/0k/6g8XY/azQQMm3tF0wOOhrE7w4m1rgqExBRt9PBdKDWaaS5Ncd3BRSC1k8FZ2D\n\trmCCJ1h90gOB5d5LLHUPp9YuChXy+s3XC1Kc7voZ007ZO7772cSuD0ryxteAnnCBSL9j\n\tQfGOPc8mBo4rJRshIzO34VcQ25jxt7WKV/bPtW5EKQjUiYnpy3nbmOIcUu+dEj/Bli6q\n\tqAlQ==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1765384866; x=1765989666;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n\t:to:cc:subject:date:message-id:reply-to;\n\tbh=blvqyUtkLPoAGDVhsamTfHUttwbtDGPkwxR46PSscrY=;\n\tb=QfCGaJGz4W2Q8hHMhhOC13iNmzry4+5nHz05c9veuenxHzOhMuQrcXg2coe99uCmzN\n\tc9NuI5sij4AIBztyEl64GvEI5k8MauZ+5mErkOFVv0Dk0QNfGlrLUGAjXRVN96kTRPFI\n\tVXfuxKnlmUY4Q70iolvoqTP+qCfMpyXHPRcYJvTpxbtlAD1YliXQfex9a21zn6Zln4OF\n\tcROTSTbruAVPUX0MFTjjjtlWPKYKvlZZXa8TlNIp8EmOI5psVFopbeMFRpgfVcrDx/ZX\n\tAmrm9kmVQhxbJVyNuteeB/J0QwZ7ejAFEbJQejRdiK5BQEbhOx3VzuJLJyawmojQyjp+\n\tqgPg==",
        "X-Gm-Message-State": "AOJu0Ywnb3ZhOPvWAlvniLa8IhneMFPR2vaTmZ4WFVR9k1JCSjrErIOS\n\thM5y0mo6F+2+MpmUyrTYhqjJMDuEZ+rvDhIVHi8Y6HP+AgejwwkEdRUwHc7Ri4tTa14zmJERxPE\n\tusZSY",
        "X-Gm-Gg": "ASbGncu8mvm5UpHU7yq1VJZXoIkIRqdpHN3EbkiuGBDauBWjQS5af6YpE+ZCdP2T85c\n\tYH+tlWgJn/ZlsyzgonLf8ZcAzHhxmYTks2MeCnEdwUlgMdIeh6iP+EP5lJsVcvJr37v+jDCaDke\n\t4T8t8zS0dN0Q3hL60o61Xab/EgA+zPgwLQ8gwJc0oH6I2l4k2tsm0H98aeaA84QM1x+VvagSR2z\n\tZQJ7naYSe2zxaR0PB12CP2DWtVOICddJeDY6kc54hzN79PP+8UWCZHYlFrOWMsz9vqR2ABcRgN+\n\tlgeob/oq0enAYuRhTdvW8ixAdmqSUiVockME8x4yVYJU2V9dIISe2Z8jkTSk8vQOKrkgzNm4xnN\n\t2mhRTGNbvtMrpw6iPo7IOHstIrYWOmLYNN4giCdRwFq6W31Jm/T5MVZzNOviLL6qSKwH1ga3hlh\n\t+v0t2idRYximM3wh/6e/1Ore5XSY3dmr0bnHqb+/kA1vE1CzWsDm/qQELljHN2jsfrt8jl2wRNm\n\tpuCapprR7RQNwAMx8J8E+GnEk0lX8sbtE87bNxz",
        "X-Google-Smtp-Source": "AGHT+IGuo/hbgCB3jxXHWsxACNjKjgMny7K6/XQAAIvwKeKqpvoQNNfYUwx3/r0IKpA5dUb6UyfXnQ==",
        "X-Received": "by 2002:a05:600c:3421:b0:479:1348:c61e with SMTP id\n\t5b1f17b1804b1-47a83cadf28mr19865365e9.20.1765384865950; \n\tWed, 10 Dec 2025 08:41:05 -0800 (PST)",
        "From": "David Plowman <david.plowman@raspberrypi.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Cc": "David Plowman <david.plowman@raspberrypi.com>",
        "Subject": "[PATCH 09/11] ipa: rpi: Support memory cameras",
        "Date": "Wed, 10 Dec 2025 16:15:24 +0000",
        "Message-ID": "<20251210164055.17856-10-david.plowman@raspberrypi.com>",
        "X-Mailer": "git-send-email 2.47.3",
        "In-Reply-To": "<20251210164055.17856-1-david.plowman@raspberrypi.com>",
        "References": "<20251210164055.17856-1-david.plowman@raspberrypi.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": "Add support for \"memory cameras\" (for Bayer reprocessing).\n\nMostly very minor changes, but the way we fill in certain bits of\nmetadata before running the IPAs changes significantly (see\nprepareIspFillMetadata).\n\nFor example, regular sensors get device status information from the\nthe sensor (or possibly from values we wrote to the sensor a few\nframes back), whereas \"memory cameras\" assume that the exposure/gain\nvalues passed in with the request controls will be the ones for\nsetting up the tuning algorithms.\n\nSigned-off-by: David Plowman <david.plowman@raspberrypi.com>\n---\n src/ipa/rpi/common/ipa_base.cpp | 135 +++++++++++++++++++++++++-------\n src/ipa/rpi/common/ipa_base.h   |   5 ++\n 2 files changed, 111 insertions(+), 29 deletions(-)",
    "diff": "diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp\nindex 04e737f6..7413c086 100644\n--- a/src/ipa/rpi/common/ipa_base.cpp\n+++ b/src/ipa/rpi/common/ipa_base.cpp\n@@ -195,6 +195,8 @@ int32_t IpaBase::init(const IPASettings &settings, const InitParams &params, Ini\n int32_t IpaBase::configure(const IPACameraSensorInfo &sensorInfo, const ConfigParams &params,\n \t\t\t   ConfigResult *result)\n {\n+\tisMemoryCamera_ = sensorInfo.model == \"memory\";\n+\n \tsensorCtrls_ = params.sensorControls;\n \n \tif (!validateSensorControls()) {\n@@ -310,10 +312,13 @@ void IpaBase::start(const ControlList &controls, StartResult *result)\n \t/*\n \t * SwitchMode may supply updated exposure/gain values to use.\n \t * agcStatus_ will store these values for us to use until delayed_status values\n-\t * start to appear.\n+\t * start to appear. Initialise digital gain to 1, as we'll otherwise end up with\n+\t * completely black output if AGC isn't running (which is unusual, but does\n+\t * happen with memory cameras).\n \t */\n \tagcStatus_.exposureTime = 0.0s;\n \tagcStatus_.analogueGain = 0.0;\n+\tagcStatus_.digitalGain = 1.0;\n \n \tmetadata.get(\"agc.status\", agcStatus_);\n \tif (agcStatus_.exposureTime && agcStatus_.analogueGain) {\n@@ -417,6 +422,75 @@ void IpaBase::unmapBuffers(const std::vector<unsigned int> &ids)\n \t}\n }\n \n+void IpaBase::prepareIspFillMetadata(const PrepareParams &params, unsigned int ipaContext,\n+\t\t\t\t     Span<uint8_t> &embeddedBuffer, bool &hdrChange)\n+{\n+\tRPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext];\n+\n+\trpiMetadata.clear();\n+\thdrChange = false;\n+\n+\tif (!isMemoryCamera_) {\n+\t\t/*\n+\t\t * Regular cameras need to get the device status from the sensor, find the\n+\t\t * appropriate embedded data buffers (if any), and so forth.\n+\t\t */\n+\t\tfillDeviceStatus(params.sensorControls, ipaContext);\n+\t\tfillSyncParams(params, ipaContext);\n+\n+\t\tif (params.buffers.embedded) {\n+\t\t\t/*\n+\t\t\t * Pipeline handler has supplied us with an embedded data buffer,\n+\t\t\t * we must pass it to the CamHelper for parsing.\n+\t\t\t */\n+\t\t\tauto it = buffers_.find(params.buffers.embedded);\n+\t\t\tASSERT(it != buffers_.end());\n+\t\t\tembeddedBuffer = it->second.planes()[0];\n+\t\t}\n+\n+\t\t/*\n+\t\t * AGC wants to know the algorithm status from the time it actioned the\n+\t\t * sensor exposure/gain changes. So fetch it from the metadata list\n+\t\t * indexed by the IPA cookie returned, and put it in the current frame\n+\t\t * metadata.\n+\t\t *\n+\t\t * Note if the HDR mode has changed, as things like tonemaps may need updating.\n+\t\t */\n+\t\tAgcStatus agcStatus;\n+\t\tRPiController::Metadata &delayedMetadata = rpiMetadata_[params.delayContext];\n+\t\tif (!delayedMetadata.get<AgcStatus>(\"agc.status\", agcStatus)) {\n+\t\t\trpiMetadata.set(\"agc.delayed_status\", agcStatus);\n+\t\t\thdrChange = agcStatus.hdr.mode != hdrStatus_.mode;\n+\t\t\thdrStatus_ = agcStatus.hdr;\n+\t\t}\n+\t} else {\n+\t\t/*\n+\t\t * Memory cameras make the device status from exposure/gain information passed\n+\t\t * in the request controls. Making a \"fake\" device status like this will cause\n+\t\t * the correct values to be used to set up all the tuning algorithms for this\n+\t\t * frame.\n+\t\t */\n+\t\tDeviceStatus deviceStatus = {};\n+\n+\t\tconst auto exposureTime = params.requestControls.get(controls::ExposureTime);\n+\t\tif (exposureTime)\n+\t\t\tdeviceStatus.exposureTime = Duration(*exposureTime * 1000);\n+\t\telse\n+\t\t\tdeviceStatus.exposureTime = Duration(1ms);\n+\n+\t\tconst auto analogueGain = params.requestControls.get(controls::AnalogueGain);\n+\t\tif (analogueGain)\n+\t\t\tdeviceStatus.analogueGain = *analogueGain;\n+\t\telse\n+\t\t\tdeviceStatus.analogueGain = 1.0;\n+\n+\t\tLOG(IPARPI, Debug) << \"Exposure time \" << deviceStatus.exposureTime\n+\t\t\t\t   << \" AG \" << deviceStatus.analogueGain;\n+\n+\t\trpiMetadata.set(\"device.status\", deviceStatus);\n+\t}\n+}\n+\n void IpaBase::prepareIsp(const PrepareParams &params)\n {\n \tapplyControls(params.requestControls);\n@@ -430,37 +504,14 @@ 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-\n-\trpiMetadata.clear();\n-\tfillDeviceStatus(params.sensorControls, ipaContext);\n-\tfillSyncParams(params, ipaContext);\n-\n-\tif (params.buffers.embedded) {\n-\t\t/*\n-\t\t * Pipeline handler has supplied us with an embedded data buffer,\n-\t\t * we must pass it to the CamHelper for parsing.\n-\t\t */\n-\t\tauto it = buffers_.find(params.buffers.embedded);\n-\t\tASSERT(it != buffers_.end());\n-\t\tembeddedBuffer = it->second.planes()[0];\n-\t}\n+\tbool hdrChange;\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-\t * indexed by the IPA cookie returned, and put it in the current frame\n-\t * metadata.\n-\t *\n-\t * Note if the HDR mode has changed, as things like tonemaps may need updating.\n+\t * This call fills in certain metadata items that subsequent calls to prepare\n+\t * will expect to find. Note that different things happen here for regular\n+\t * sensors and \"memory cameras\".\n \t */\n-\tAgcStatus agcStatus;\n-\tbool hdrChange = false;\n-\tRPiController::Metadata &delayedMetadata = rpiMetadata_[params.delayContext];\n-\tif (!delayedMetadata.get<AgcStatus>(\"agc.status\", agcStatus)) {\n-\t\trpiMetadata.set(\"agc.delayed_status\", agcStatus);\n-\t\thdrChange = agcStatus.hdr.mode != hdrStatus_.mode;\n-\t\thdrStatus_ = agcStatus.hdr;\n-\t}\n+\tprepareIspFillMetadata(params, ipaContext, embeddedBuffer, hdrChange);\n \n \t/*\n \t * This may overwrite the DeviceStatus using values from the sensor\n@@ -650,6 +701,18 @@ void IpaBase::setMode(const IPACameraSensorInfo &sensorInfo)\n \t */\n \tmode_.sensitivity = helper_->getModeSensitivity(mode_);\n \n+\tif (isMemoryCamera_) {\n+\t\t/*\n+\t\t * For memory cameras, the min/max gain/exposure doesn't make much\n+\t\t * sense (and our \"sensor\" won't have any V4L2 controls), but we do\n+\t\t * need to set the camera mode information that we have so far, so\n+\t\t * that the control algorithms will see it.\n+\t\t */\n+\t\thelper_->setCameraMode(mode_);\n+\n+\t\treturn;\n+\t}\n+\n \tconst ControlInfo &gainCtrl = sensorCtrls_.at(V4L2_CID_ANALOGUE_GAIN);\n \tconst ControlInfo &exposureTimeCtrl = sensorCtrls_.at(V4L2_CID_EXPOSURE);\n \n@@ -690,6 +753,10 @@ void IpaBase::setCameraTimeoutValue()\n \n bool IpaBase::validateSensorControls()\n {\n+\t/* Don't need any of these controls with memory cameras. */\n+\tif (isMemoryCamera_)\n+\t\treturn true;\n+\n \tstatic const uint32_t ctrls[] = {\n \t\tV4L2_CID_ANALOGUE_GAIN,\n \t\tV4L2_CID_EXPOSURE,\n@@ -955,6 +1022,11 @@ void IpaBase::applyControls(const ControlList &controls)\n \t\tcase controls::EXPOSURE_TIME: {\n \t\t\tRPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(\n \t\t\t\tcontroller_.getAlgorithm(\"agc\"));\n+\n+\t\t\t/* Don't print a \"no AGC\" warning if this is a memory camera. */\n+\t\t\tif (!agc && isMemoryCamera_)\n+\t\t\t\tbreak;\n+\n \t\t\tif (!agc) {\n \t\t\t\tLOG(IPARPI, Warning)\n \t\t\t\t\t<< \"Could not set EXPOSURE_TIME - no AGC algorithm\";\n@@ -981,6 +1053,11 @@ void IpaBase::applyControls(const ControlList &controls)\n \t\tcase controls::ANALOGUE_GAIN: {\n \t\t\tRPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(\n \t\t\t\tcontroller_.getAlgorithm(\"agc\"));\n+\n+\t\t\t/* Don't print a \"no AGC\" warning if this is a memory camera. */\n+\t\t\tif (!agc && isMemoryCamera_)\n+\t\t\t\tbreak;\n+\n \t\t\tif (!agc) {\n \t\t\t\tLOG(IPARPI, Warning)\n \t\t\t\t\t<< \"Could not set ANALOGUE_GAIN - no AGC algorithm\";\ndiff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h\nindex 0ebd80db..94efd9a9 100644\n--- a/src/ipa/rpi/common/ipa_base.h\n+++ b/src/ipa/rpi/common/ipa_base.h\n@@ -45,6 +45,9 @@ public:\n \tvoid mapBuffers(const std::vector<IPABuffer> &buffers) override;\n \tvoid unmapBuffers(const std::vector<unsigned int> &ids) override;\n \n+\tvoid prepareIspFillMetadata(const PrepareParams &params, unsigned int ipaContext,\n+\t\t\t\t    Span<uint8_t> &embeddedBuffer, bool &hdrChange);\n+\n \tvoid prepareIsp(const PrepareParams &params) override;\n \tvoid processStats(const ProcessParams &params) override;\n \n@@ -143,6 +146,8 @@ private:\n \t\tutils::Duration manualPeriod;\n \t} flickerState_;\n \n+\tbool isMemoryCamera_;\n+\n \tbool cnnEnableInputTensor_;\n \tbool awbEnabled_;\n };\n",
    "prefixes": [
        "09/11"
    ]
}