Patch Detail
Show a patch.
GET /api/1.1/patches/22885/?format=api
{ "id": 22885, "url": "https://patchwork.libcamera.org/api/1.1/patches/22885/?format=api", "web_url": "https://patchwork.libcamera.org/patch/22885/", "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": "<20250227105733.187611-3-keke.li@amlogic.com>", "date": "2025-02-27T10:57:24", "name": "[v3,02/11] libcamera: pipeline: Add c3-isp pipeline handler", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "7c5479a008e6c75bf18d08e18a5ac0401b6e1828", "submitter": { "id": 217, "url": "https://patchwork.libcamera.org/api/1.1/people/217/?format=api", "name": "Keke Li", "email": "keke.li@amlogic.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/22885/mbox/", "series": [ { "id": 5027, "url": "https://patchwork.libcamera.org/api/1.1/series/5027/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5027", "date": "2025-02-27T10:57:22", "name": "Add Amlogic C3 ISP pipeline handler and IPA", "version": 3, "mbox": "https://patchwork.libcamera.org/series/5027/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/22885/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/22885/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 4CB40BF415\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 27 Feb 2025 10:57:48 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 8DEBB6875B;\n\tThu, 27 Feb 2025 11:57:46 +0100 (CET)", "from mail-sh.amlogic.com (unknown [58.32.228.46])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id EEE366874F\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 27 Feb 2025 11:57:40 +0100 (CET)", "from droid10.amlogic.com (10.18.11.213) by mail-sh.amlogic.com\n\t(10.18.11.5) with Microsoft SMTP Server id 15.1.2507.39;\n\tThu, 27 Feb 2025 18:57:37 +0800" ], "From": "Keke Li <keke.li@amlogic.com>", "To": "<libcamera-devel@lists.libcamera.org>", "CC": "<kieran.bingham@ideasonboard.com>, <laurent.pinchart@ideasonboard.com>, \n\t<dan.scally@ideasonboard.com>, Keke Li <keke.li@amlogic.com>", "Subject": "[PATCH v3 02/11] libcamera: pipeline: Add c3-isp pipeline handler", "Date": "Thu, 27 Feb 2025 18:57:24 +0800", "Message-ID": "<20250227105733.187611-3-keke.li@amlogic.com>", "X-Mailer": "git-send-email 2.29.0", "In-Reply-To": "<20250227105733.187611-1-keke.li@amlogic.com>", "References": "<20250227105733.187611-1-keke.li@amlogic.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Content-Type": "text/plain", "X-Originating-IP": "[10.18.11.213]", "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 Amlogic C3 ISP pipeline handler supports\n3-channel image output, 1-channel 3A statistical\ninformation ouput and 1-channel parameters input.\n\nSigned-off-by: Keke Li <keke.li@amlogic.com>\n---\n Documentation/Doxyfile-common.in | 1 +\n include/libcamera/ipa/c3-isp.mojom | 34 +\n include/libcamera/ipa/meson.build | 1 +\n meson_options.txt | 1 +\n src/libcamera/pipeline/c3-isp/c3-isp.cpp | 1313 +++++++++++++++++++++\n src/libcamera/pipeline/c3-isp/meson.build | 5 +\n 6 files changed, 1355 insertions(+)\n create mode 100644 include/libcamera/ipa/c3-isp.mojom\n create mode 100644 src/libcamera/pipeline/c3-isp/c3-isp.cpp\n create mode 100644 src/libcamera/pipeline/c3-isp/meson.build", "diff": "diff --git a/Documentation/Doxyfile-common.in b/Documentation/Doxyfile-common.in\nindex 045c19dd..19f28886 100644\n--- a/Documentation/Doxyfile-common.in\n+++ b/Documentation/Doxyfile-common.in\n@@ -31,6 +31,7 @@ RECURSIVE = YES\n \n EXCLUDE_PATTERNS = @TOP_BUILDDIR@/include/libcamera/ipa/*_serializer.h \\\n @TOP_BUILDDIR@/include/libcamera/ipa/*_proxy.h \\\n+ @TOP_BUILDDIR@/include/libcamera/ipa/c3-isp_*.h \\\n @TOP_BUILDDIR@/include/libcamera/ipa/ipu3_*.h \\\n @TOP_BUILDDIR@/include/libcamera/ipa/mali-c55_*.h \\\n @TOP_BUILDDIR@/include/libcamera/ipa/raspberrypi_*.h \\\ndiff --git a/include/libcamera/ipa/c3-isp.mojom b/include/libcamera/ipa/c3-isp.mojom\nnew file mode 100644\nindex 00000000..f8556256\n--- /dev/null\n+++ b/include/libcamera/ipa/c3-isp.mojom\n@@ -0,0 +1,34 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+\n+module ipa.c3isp;\n+\n+import \"include/libcamera/ipa/core.mojom\";\n+\n+struct IPAConfigInfo {\n+\tlibcamera.IPACameraSensorInfo sensorInfo;\n+\tlibcamera.ControlInfoMap sensorControls;\n+};\n+\n+interface IPAC3ISPInterface {\n+\tinit(libcamera.IPASettings settings, IPAConfigInfo configInfo)\n+\t\t=> (int32 ret, libcamera.ControlInfoMap ipaControls);\n+\tstart() => (int32 ret);\n+\tstop();\n+\n+\tconfigure(IPAConfigInfo configInfo)\n+\t\t=> (int32 ret, libcamera.ControlInfoMap ipaControls);\n+\n+\tmapBuffers(array<libcamera.IPABuffer> buffers, bool readOnly);\n+\tunmapBuffers(array<libcamera.IPABuffer> buffers);\n+\n+\t[async] queueRequest(uint32 request, libcamera.ControlList reqControls);\n+\t[async] computeParams(uint32 request, uint32 bufferId);\n+\t[async] processStats(uint32 request, uint32 bufferId,\n+\t\t\t libcamera.ControlList sensorControls);\n+};\n+\n+interface IPAC3ISPEventInterface {\n+\tparamsComputed(uint32 request, uint32 bytesused);\n+\tstatsProcessed(uint32 request, libcamera.ControlList metadata);\n+\tsetSensorControls(libcamera.ControlList sensorControls);\n+};\ndiff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build\nindex 3129f119..bc0fa7d3 100644\n--- a/include/libcamera/ipa/meson.build\n+++ b/include/libcamera/ipa/meson.build\n@@ -63,6 +63,7 @@ libcamera_ipa_headers += custom_target('core_ipa_serializer_h',\n \n # Mapping from pipeline handler name to mojom file\n pipeline_ipa_mojom_mapping = {\n+ 'c3-isp': 'c3-isp.mojom',\n 'ipu3': 'ipu3.mojom',\n 'mali-c55': 'mali-c55.mojom',\n 'rkisp1': 'rkisp1.mojom',\ndiff --git a/meson_options.txt b/meson_options.txt\nindex f19bca91..998a4463 100644\n--- a/meson_options.txt\n+++ b/meson_options.txt\n@@ -46,6 +46,7 @@ option('pipelines',\n choices : [\n 'all',\n 'auto',\n+ 'c3-isp',\n 'imx8-isi',\n 'ipu3',\n 'mali-c55',\ndiff --git a/src/libcamera/pipeline/c3-isp/c3-isp.cpp b/src/libcamera/pipeline/c3-isp/c3-isp.cpp\nnew file mode 100644\nindex 00000000..90baedc8\n--- /dev/null\n+++ b/src/libcamera/pipeline/c3-isp/c3-isp.cpp\n@@ -0,0 +1,1313 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic Inc.\n+ *\n+ * Pipeline Handler for Amlogic C3 ISP\n+ */\n+\n+#include <algorithm>\n+#include <array>\n+#include <map>\n+#include <memory>\n+#include <queue>\n+#include <set>\n+#include <string>\n+\n+#include <linux/media-bus-format.h>\n+#include <linux/media.h>\n+\n+#include <libcamera/base/log.h>\n+\n+#include <libcamera/camera.h>\n+#include <libcamera/formats.h>\n+#include <libcamera/geometry.h>\n+#include <libcamera/stream.h>\n+\n+#include <libcamera/ipa/c3-isp_ipa_interface.h>\n+#include <libcamera/ipa/c3-isp_ipa_proxy.h>\n+#include <libcamera/ipa/core_ipa_interface.h>\n+\n+#include \"libcamera/internal/bayer_format.h\"\n+#include \"libcamera/internal/camera.h\"\n+#include \"libcamera/internal/camera_sensor.h\"\n+#include \"libcamera/internal/delayed_controls.h\"\n+#include \"libcamera/internal/device_enumerator.h\"\n+#include \"libcamera/internal/framebuffer.h\"\n+#include \"libcamera/internal/ipa_manager.h\"\n+#include \"libcamera/internal/media_device.h\"\n+#include \"libcamera/internal/pipeline_handler.h\"\n+#include \"libcamera/internal/request.h\"\n+#include \"libcamera/internal/v4l2_subdevice.h\"\n+#include \"libcamera/internal/v4l2_videodevice.h\"\n+\n+namespace {\n+\n+bool isFmtRaw(const libcamera::PixelFormat &pixFmt)\n+{\n+\treturn libcamera::PixelFormatInfo::info(pixFmt).colourEncoding ==\n+\t libcamera::PixelFormatInfo::ColourEncodingRAW;\n+}\n+\n+} /* namespace */\n+\n+namespace libcamera {\n+\n+LOG_DEFINE_CATEGORY(C3ISP)\n+\n+const std::map<libcamera::PixelFormat, unsigned int> C3ISPFmtToCode = {\n+\t{ formats::R8, MEDIA_BUS_FMT_YUV10_1X30 },\n+\t{ formats::NV12, MEDIA_BUS_FMT_YUV10_1X30 },\n+\t{ formats::NV21, MEDIA_BUS_FMT_YUV10_1X30 },\n+\t{ formats::NV16, MEDIA_BUS_FMT_YUV10_1X30 },\n+\t{ formats::NV61, MEDIA_BUS_FMT_YUV10_1X30 },\n+\n+\t/* RAW formats, STILL pipe only. */\n+\t{ formats::SRGGB12, MEDIA_BUS_FMT_SRGGB16_1X16 },\n+\t{ formats::SBGGR12, MEDIA_BUS_FMT_SBGGR16_1X16 },\n+\t{ formats::SGRBG12, MEDIA_BUS_FMT_SGRBG16_1X16 },\n+\t{ formats::SGBRG12, MEDIA_BUS_FMT_SGBRG16_1X16 },\n+};\n+\n+constexpr Size kC3ISPMinSize = { 160, 120 };\n+constexpr Size kC3ISPMaxSize = { 2888, 2240 };\n+\n+struct C3ISPFrameInfo {\n+\tRequest *request;\n+\n+\tFrameBuffer *paramBuffer;\n+\tFrameBuffer *statBuffer;\n+\n+\tbool paramsDone;\n+\tbool statsDone;\n+};\n+\n+class C3ISPCameraData : public Camera::Private\n+{\n+public:\n+\tC3ISPCameraData(PipelineHandler *pipe, MediaEntity *entity)\n+\t\t: Camera::Private(pipe), entity_(entity)\n+\t{\n+\t}\n+\n+\tint init();\n+\tint loadIPA();\n+\n+\tint pixfmtToMbusCode(const PixelFormat &pixFmt) const;\n+\tconst PixelFormat &bestRawFmt() const;\n+\n+\tPixelFormat adjustRawFmt(const PixelFormat &pixFmt) const;\n+\tSize adjustRawSizes(const PixelFormat &pixFmt, const Size &rawSize) const;\n+\n+\tvoid updateControls(const ControlInfoMap &ipaControls);\n+\n+\tMediaEntity *entity_;\n+\tstd::unique_ptr<CameraSensor> sensor_;\n+\tstd::unique_ptr<V4L2Subdevice> csi_;\n+\tstd::unique_ptr<V4L2Subdevice> adap_;\n+\tStream viewStream;\n+\tStream stillStream;\n+\tStream videoStream;\n+\n+\tstd::unique_ptr<ipa::c3isp::IPAProxyC3ISP> ipa_;\n+\tstd::vector<IPABuffer> ipaStatBuffers_;\n+\tstd::vector<IPABuffer> ipaParamBuffers_;\n+\n+\tstd::unique_ptr<DelayedControls> delayedCtrls_;\n+\n+private:\n+\tvoid setSensorControls(const ControlList &sensorControls);\n+};\n+\n+int C3ISPCameraData::init()\n+{\n+\t/* Register a CameraSensor */\n+\tsensor_ = CameraSensorFactoryBase::create(entity_);\n+\tif (!sensor_)\n+\t\treturn -EINVAL;\n+\n+\tconst MediaPad *sensorSrc = entity_->getPadByIndex(0);\n+\tMediaEntity *csiEntity = sensorSrc->links()[0]->sink()->entity();\n+\n+\tcsi_ = std::make_unique<V4L2Subdevice>(csiEntity);\n+\tif (csi_->open()) {\n+\t\tLOG(C3ISP, Error) << \"Failed to open CSI-2 subdevice\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tconst MediaPad *csiSrc = csiEntity->getPadByIndex(1);\n+\tMediaEntity *adapEntity = csiSrc->links()[0]->sink()->entity();\n+\n+\tadap_ = std::make_unique<V4L2Subdevice>(adapEntity);\n+\tif (adap_->open()) {\n+\t\tLOG(C3ISP, Error) << \"Failed to open adapter subdevice\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+void C3ISPCameraData::setSensorControls(const ControlList &sensorControls)\n+{\n+\tdelayedCtrls_->push(sensorControls);\n+}\n+\n+/*\n+ * Search the sensor's supported formats for the one with a matching bayer\n+ * order and the greatest bitdepth.\n+ */\n+int C3ISPCameraData::pixfmtToMbusCode(const PixelFormat &pixFmt) const\n+{\n+\tauto it = C3ISPFmtToCode.find(pixFmt);\n+\tif (it == C3ISPFmtToCode.end())\n+\t\treturn -EINVAL;\n+\n+\tBayerFormat bayerFmt = BayerFormat::fromMbusCode(it->second);\n+\tif (!bayerFmt.isValid())\n+\t\treturn -EINVAL;\n+\n+\tunsigned int snsMbusCode = 0;\n+\tunsigned int bitDepth = 0;\n+\tfor (const auto &code : sensor_->mbusCodes()) {\n+\t\tBayerFormat snsBayerFmt = BayerFormat::fromMbusCode(code);\n+\t\tif (!snsBayerFmt.isValid())\n+\t\t\tcontinue;\n+\n+\t\tif (snsBayerFmt.order != bayerFmt.order)\n+\t\t\tcontinue;\n+\n+\t\tif (snsBayerFmt.bitDepth > bitDepth) {\n+\t\t\tbitDepth = snsBayerFmt.bitDepth;\n+\t\t\tsnsMbusCode = code;\n+\t\t}\n+\t}\n+\n+\tif (!snsMbusCode)\n+\t\treturn -EINVAL;\n+\n+\treturn snsMbusCode;\n+}\n+\n+/* Find a RAW PixelFormat supported by both the ISP and the sensor. */\n+const PixelFormat &C3ISPCameraData::bestRawFmt() const\n+{\n+\tstatic const PixelFormat invalidPixFmt = {};\n+\n+\tfor (const auto &code : sensor_->mbusCodes()) {\n+\t\tBayerFormat sensorBayer = BayerFormat::fromMbusCode(code);\n+\n+\t\tif (!sensorBayer.isValid())\n+\t\t\tcontinue;\n+\n+\t\tfor (const auto &[pixFmt, rawCode] : C3ISPFmtToCode) {\n+\t\t\tif (!isFmtRaw(pixFmt))\n+\t\t\t\tcontinue;\n+\n+\t\t\tBayerFormat bayer = BayerFormat::fromMbusCode(rawCode);\n+\t\t\tif (bayer.order == sensorBayer.order)\n+\t\t\t\treturn pixFmt;\n+\t\t}\n+\t}\n+\n+\tLOG(C3ISP, Error) << \"Can't get a compatible format\";\n+\n+\treturn invalidPixFmt;\n+}\n+\n+/*\n+ * Make sure the provided raw pixel format is supported and adjust it to\n+ * one of the supported ones if it's not.\n+ */\n+PixelFormat C3ISPCameraData::adjustRawFmt(const PixelFormat &rawFmt) const\n+{\n+\tint rawCode = pixfmtToMbusCode(rawFmt);\n+\tif (rawCode < 0)\n+\t\treturn bestRawFmt();\n+\n+\tconst auto rawSizes = sensor_->sizes(rawCode);\n+\tif (rawSizes.empty())\n+\t\treturn bestRawFmt();\n+\n+\treturn rawFmt;\n+}\n+\n+Size C3ISPCameraData::adjustRawSizes(const PixelFormat &rawFmt, const Size &size) const\n+{\n+\tSize rawSize = size.boundedTo(kC3ISPMaxSize);\n+\n+\tint rawCode = pixfmtToMbusCode(rawFmt);\n+\tif (rawCode < 0)\n+\t\treturn {};\n+\n+\tconst auto rawSizes = sensor_->sizes(rawCode);\n+\n+\tauto sizeIt = std::find(rawSizes.begin(), rawSizes.end(), rawSize);\n+\tif (sizeIt != rawSizes.end())\n+\t\treturn rawSize;\n+\n+\t/* Adjust the rawSize to the closest supported size */\n+\tuint16_t distance = std::numeric_limits<uint16_t>::max();\n+\tSize bestSize;\n+\tfor (const Size &sz : rawSizes) {\n+\t\tuint16_t dist = std::abs(static_cast<int>(rawSize.width) -\n+\t\t\t\t\t static_cast<int>(sz.width)) +\n+\t\t\t\tstd::abs(static_cast<int>(rawSize.height) -\n+\t\t\t\t\t static_cast<int>(sz.height));\n+\t\tif (dist < distance) {\n+\t\t\tdistance = dist;\n+\t\t\tbestSize = sz;\n+\t\t}\n+\t}\n+\n+\treturn bestSize;\n+}\n+\n+void C3ISPCameraData::updateControls(const ControlInfoMap &ipaControls)\n+{\n+\tControlInfoMap::Map controls;\n+\n+\tfor (auto const &c : ipaControls)\n+\t\tcontrols.emplace(c.first, c.second);\n+\n+\tcontrolInfo_ = ControlInfoMap(std::move(controls), controls::controls);\n+}\n+\n+int C3ISPCameraData::loadIPA()\n+{\n+\tint ret;\n+\n+\tipa_ = IPAManager::createIPA<ipa::c3isp::IPAProxyC3ISP>(pipe(), 1, 1);\n+\tif (!ipa_)\n+\t\treturn -ENOENT;\n+\n+\tipa_->setSensorControls.connect(this, &C3ISPCameraData::setSensorControls);\n+\n+\tstd::string ipaTuningFile = ipa_->configurationFile(sensor_->model() + \".yaml\",\n+\t\t\t\t\t\t\t \"uncalibrated.yaml\");\n+\n+\tipa::c3isp::IPAConfigInfo ipaConfig{};\n+\n+\tret = sensor_->sensorInfo(&ipaConfig.sensorInfo);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tipaConfig.sensorControls = sensor_->controls();\n+\n+\tControlInfoMap ipaControls;\n+\tret = ipa_->init({ ipaTuningFile, sensor_->model() }, ipaConfig, &ipaControls);\n+\tif (ret) {\n+\t\tLOG(C3ISP, Error) << \"Failed to initialize IPA\";\n+\t\treturn ret;\n+\t}\n+\n+\tupdateControls(ipaControls);\n+\n+\treturn 0;\n+}\n+\n+class C3ISPCameraConfiguration : public CameraConfiguration\n+{\n+public:\n+\tC3ISPCameraConfiguration(C3ISPCameraData *data)\n+\t\t: CameraConfiguration(), data_(data)\n+\t{\n+\t}\n+\n+\tStatus validate() override;\n+\tconst Transform &combinedTransform() { return combinedTransform_; }\n+\n+\tV4L2SubdeviceFormat sensorFormat_;\n+\n+private:\n+\tstatic constexpr unsigned int kMaxStreams = 3;\n+\n+\tconst C3ISPCameraData *data_;\n+\tTransform combinedTransform_;\n+};\n+\n+CameraConfiguration::Status C3ISPCameraConfiguration::validate()\n+{\n+\tStatus status = Valid;\n+\n+\tif (config_.empty())\n+\t\treturn Invalid;\n+\n+\tif (config_.size() > kMaxStreams) {\n+\t\tconfig_.resize(kMaxStreams);\n+\t\tstatus = Adjusted;\n+\t}\n+\n+\tOrientation requestOrientation = orientation;\n+\tcombinedTransform_ = data_->sensor_->computeTransform(&orientation);\n+\tif (orientation != requestOrientation)\n+\t\tstatus = Adjusted;\n+\n+\tbool stillPipeAvailable = true;\n+\tStreamConfiguration *rawConfig = nullptr;\n+\tfor (StreamConfiguration &config : config_) {\n+\t\tif (!isFmtRaw(config.pixelFormat))\n+\t\t\tcontinue;\n+\n+\t\tif (rawConfig) {\n+\t\t\tLOG(C3ISP, Error) << \"Only support a RAW stream\";\n+\t\t\treturn Invalid;\n+\t\t}\n+\n+\t\trawConfig = &config;\n+\t}\n+\n+\t/*\n+\t * The C3 ISP can not upscale. Limit the configuration to the ISP\n+\t * capabilities and the sensor resolution.\n+\t */\n+\tSize maxSize = kC3ISPMaxSize.boundedTo(data_->sensor_->resolution());\n+\tif (rawConfig) {\n+\t\tPixelFormat rawFmt =\n+\t\t\tdata_->adjustRawFmt(rawConfig->pixelFormat);\n+\n+\t\tif (!rawFmt.isValid())\n+\t\t\treturn Invalid;\n+\n+\t\tif (rawFmt != rawConfig->pixelFormat) {\n+\t\t\tLOG(C3ISP, Debug)\n+\t\t\t\t<< \"Adjust RAW format to \" << rawFmt;\n+\t\t\trawConfig->pixelFormat = rawFmt;\n+\t\t\tstatus = Adjusted;\n+\t\t}\n+\n+\t\tSize rawSize = data_->adjustRawSizes(rawFmt, rawConfig->size);\n+\t\tif (rawSize != rawConfig->size) {\n+\t\t\tLOG(C3ISP, Debug)\n+\t\t\t\t<< \"Adjust RAW size to \" << rawSize;\n+\t\t\trawConfig->size = rawSize;\n+\t\t\tstatus = Adjusted;\n+\t\t}\n+\n+\t\tmaxSize = rawSize;\n+\n+\t\tconst PixelFormatInfo &info = PixelFormatInfo::info(rawConfig->pixelFormat);\n+\t\trawConfig->stride = info.stride(rawConfig->size.width, 0, 16);\n+\t\trawConfig->frameSize = info.frameSize(rawConfig->size, 4);\n+\n+\t\trawConfig->setStream(const_cast<Stream *>(&data_->stillStream));\n+\t\tstillPipeAvailable = false;\n+\t}\n+\n+\t/* Adjust processed streams */\n+\n+\tbool videoPipeAvailable = true;\n+\tSize maxConfigSize;\n+\tfor (StreamConfiguration &config : config_) {\n+\t\tif (isFmtRaw(config.pixelFormat))\n+\t\t\tcontinue;\n+\n+\t\tconst auto it = C3ISPFmtToCode.find(config.pixelFormat);\n+\t\tif (it == C3ISPFmtToCode.end()) {\n+\t\t\tLOG(C3ISP, Debug)\n+\t\t\t\t<< \"Format adjusted to \" << formats::NV12;\n+\t\t\tconfig.pixelFormat = formats::NV12;\n+\t\t\tstatus = Adjusted;\n+\t\t}\n+\n+\t\tSize size = std::clamp(config.size, kC3ISPMinSize, maxSize);\n+\t\tif (size != config.size) {\n+\t\t\tLOG(C3ISP, Debug)\n+\t\t\t\t<< \"Size adjusted to \" << size;\n+\t\t\tconfig.size = size;\n+\t\t\tstatus = Adjusted;\n+\t\t}\n+\n+\t\tif (maxConfigSize < size)\n+\t\t\tmaxConfigSize = size;\n+\n+\t\tif (stillPipeAvailable) {\n+\t\t\tconfig.setStream(const_cast<Stream *>(&data_->stillStream));\n+\t\t\tstillPipeAvailable = false;\n+\t\t} else if (videoPipeAvailable) {\n+\t\t\tconfig.setStream(const_cast<Stream *>(&data_->videoStream));\n+\t\t\tvideoPipeAvailable = false;\n+\t\t} else {\n+\t\t\tconfig.setStream(const_cast<Stream *>(&data_->viewStream));\n+\t\t}\n+\t}\n+\n+\t/* Configure the sensor format */\n+\n+\t/* If there's a RAW config, sensor format follow it */\n+\tif (rawConfig) {\n+\t\tsensorFormat_.code = data_->pixfmtToMbusCode(rawConfig->pixelFormat);\n+\t\tsensorFormat_.size = rawConfig->size;\n+\n+\t\tLOG(C3ISP, Debug) << \"RAW configuration format \" << sensorFormat_;\n+\n+\t\treturn status;\n+\t}\n+\n+\t/* If there's no RAW config, compture the sensor format */\n+\tPixelFormat rawFormat = data_->bestRawFmt();\n+\tif (!rawFormat.isValid())\n+\t\treturn Invalid;\n+\n+\tsensorFormat_.code = data_->pixfmtToMbusCode(rawFormat);\n+\tsensorFormat_.size = data_->adjustRawSizes(rawFormat, maxConfigSize);\n+\n+\tLOG(C3ISP, Debug) << \"Sensor format \" << sensorFormat_;\n+\n+\treturn status;\n+}\n+\n+class PipelineHandlerC3ISP : public PipelineHandler\n+{\n+public:\n+\tPipelineHandlerC3ISP(CameraManager *manager);\n+\n+\tstd::unique_ptr<CameraConfiguration> generateConfiguration(Camera *camera,\n+\t\t\t\t\t\t\t\t Span<const StreamRole> roles) override;\n+\tint configure(Camera *camera, CameraConfiguration *config) override;\n+\n+\tint exportFrameBuffers(Camera *camera, Stream *stream,\n+\t\t\t std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;\n+\n+\tint allocateBuffers(Camera *camera);\n+\tvoid freeBuffers(Camera *camera);\n+\n+\tint start(Camera *camera, const ControlList *controls) override;\n+\tvoid stopDevice(Camera *camera) override;\n+\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 paramsComputed(unsigned int requestId, unsigned int bytesused);\n+\tvoid statsProcessed(unsigned int requestId, const ControlList &metadata);\n+\n+\tbool match(DeviceEnumerator *enumerator) override;\n+\n+private:\n+\tstruct C3ISPPipe {\n+\t\tstd::unique_ptr<V4L2Subdevice> resizer;\n+\t\tstd::unique_ptr<V4L2VideoDevice> cap;\n+\t\tStream *stream;\n+\t};\n+\n+\tenum {\n+\t\tC3ISPVIEW,\n+\t\tC3ISPVIDEO,\n+\t\tC3ISPSTILL,\n+\t\tC3ISPNumPipes,\n+\t};\n+\n+\tC3ISPCameraData *cameraData(Camera *camera)\n+\t{\n+\t\treturn static_cast<C3ISPCameraData *>(camera->_d());\n+\t}\n+\n+\tC3ISPPipe *pipeFromStream(C3ISPCameraData *data, Stream *stream)\n+\t{\n+\t\tif (stream == &data->viewStream)\n+\t\t\treturn &pipes_[C3ISPVIEW];\n+\t\telse if (stream == &data->stillStream)\n+\t\t\treturn &pipes_[C3ISPSTILL];\n+\t\telse if (stream == &data->videoStream)\n+\t\t\treturn &pipes_[C3ISPVIDEO];\n+\t\telse\n+\t\t\tLOG(C3ISP, Fatal) << \"Invalid stream: \" << stream;\n+\n+\t\treturn nullptr;\n+\t}\n+\n+\tC3ISPPipe *pipeFromStream(C3ISPCameraData *data, const Stream *stream)\n+\t{\n+\t\treturn pipeFromStream(data, const_cast<Stream *>(stream));\n+\t}\n+\n+\tvoid resetPipes()\n+\t{\n+\t\tfor (C3ISPPipe &pipe : pipes_)\n+\t\t\tpipe.stream = nullptr;\n+\t}\n+\n+\tint pipesStart();\n+\tvoid pipesStop();\n+\n+\tint configureRawStream(C3ISPCameraData *data,\n+\t\t\t const StreamConfiguration &config,\n+\t\t\t V4L2SubdeviceFormat &subdevFormat);\n+\tint configureProcessedStream(C3ISPCameraData *data,\n+\t\t\t\t const StreamConfiguration &config,\n+\t\t\t\t V4L2SubdeviceFormat &subdevFormat);\n+\n+\tbool createCamera(MediaLink *ispLink);\n+\tvoid tryComplete(C3ISPFrameInfo *info);\n+\tC3ISPFrameInfo *findFrameInfo(FrameBuffer *buffer);\n+\tC3ISPFrameInfo *findFrameInfo(Request *request);\n+\tvoid clearIncompleteRequests();\n+\n+\tMediaDevice *media_;\n+\tstd::unique_ptr<V4L2Subdevice> isp_;\n+\tstd::unique_ptr<V4L2VideoDevice> params_;\n+\tstd::unique_ptr<V4L2VideoDevice> stats_;\n+\n+\tstd::vector<std::unique_ptr<FrameBuffer>> statsBuffers_;\n+\tstd::queue<FrameBuffer *> availableStatsBuffers_;\n+\n+\tstd::vector<std::unique_ptr<FrameBuffer>> paramsBuffers_;\n+\tstd::queue<FrameBuffer *> availableParamsBuffers_;\n+\n+\tstd::map<unsigned int, C3ISPFrameInfo> frameInfoMap_;\n+\n+\tstd::array<C3ISPPipe, C3ISPNumPipes> pipes_;\n+};\n+\n+PipelineHandlerC3ISP::PipelineHandlerC3ISP(CameraManager *manager)\n+\t: PipelineHandler(manager)\n+{\n+}\n+\n+std::unique_ptr<CameraConfiguration>\n+PipelineHandlerC3ISP::generateConfiguration(Camera *camera,\n+\t\t\t\t\t Span<const StreamRole> roles)\n+{\n+\tC3ISPCameraData *data = cameraData(camera);\n+\tstd::unique_ptr<CameraConfiguration> config =\n+\t\tstd::make_unique<C3ISPCameraConfiguration>(data);\n+\tbool isRoleRaw = false;\n+\n+\tif (roles.empty())\n+\t\treturn config;\n+\n+\t/* Reserve the C3ISPSTILL pipe for Raw role */\n+\tif (std::find(roles.begin(), roles.end(), StreamRole::Raw) != roles.end())\n+\t\tisRoleRaw = true;\n+\n+\tfor (const StreamRole &role : roles) {\n+\t\tstruct C3ISPPipe *pipe;\n+\t\tPixelFormat pixelFormat;\n+\t\tSize size = std::min(Size{ 1920, 1080 }, data->sensor_->resolution());\n+\n+\t\tswitch (role) {\n+\t\tcase StreamRole::StillCapture:\n+\t\t\tsize = data->sensor_->resolution();\n+\t\t\tpixelFormat = formats::NV12;\n+\t\t\tpipe = isRoleRaw ? nullptr : &pipes_[C3ISPSTILL];\n+\t\t\tbreak;\n+\n+\t\tcase StreamRole::VideoRecording:\n+\t\t\tpixelFormat = formats::NV12;\n+\t\t\tpipe = &pipes_[C3ISPVIDEO];\n+\t\t\tbreak;\n+\n+\t\tcase StreamRole::Viewfinder:\n+\t\t\tpixelFormat = formats::NV12;\n+\t\t\tpipe = &pipes_[C3ISPVIEW];\n+\t\t\tbreak;\n+\n+\t\tcase StreamRole::Raw:\n+\t\t\tpixelFormat = data->bestRawFmt();\n+\t\t\tif (!pixelFormat.isValid()) {\n+\t\t\t\tLOG(C3ISP, Error)\n+\t\t\t\t\t<< \"Failed to get Raw format\";\n+\t\t\t\treturn nullptr;\n+\t\t\t}\n+\n+\t\t\tsize = data->sensor_->resolution();\n+\t\t\tpipe = &pipes_[C3ISPSTILL];\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\tLOG(C3ISP, Error) << \"Invalid stream role: \" << role;\n+\t\t\treturn nullptr;\n+\t\t}\n+\n+\t\tstd::map<PixelFormat, std::vector<SizeRange>> formats;\n+\t\tfor (const auto &c3Format : C3ISPFmtToCode) {\n+\t\t\tPixelFormat pixFmt = c3Format.first;\n+\t\t\tbool isRaw = isFmtRaw(pixFmt);\n+\n+\t\t\tif (pipe != &pipes_[C3ISPSTILL] && isRaw)\n+\t\t\t\tcontinue;\n+\n+\t\t\tif (isRaw) {\n+\t\t\t\tint rawCode = data->pixfmtToMbusCode(pixFmt);\n+\t\t\t\tif (rawCode < 0)\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\tconst auto sizes = data->sensor_->sizes(rawCode);\n+\t\t\t\tif (sizes.empty())\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\tstd::vector<SizeRange> sizeRanges;\n+\t\t\t\tstd::transform(sizes.begin(), sizes.end(),\n+\t\t\t\t\t std::back_inserter(sizeRanges),\n+\t\t\t\t\t [](const Size &s) {\n+\t\t\t\t\t\t return SizeRange(s);\n+\t\t\t\t\t });\n+\n+\t\t\t\tformats[pixFmt] = sizeRanges;\n+\t\t\t} else {\n+\t\t\t\tSize maxSize = std::min(kC3ISPMaxSize,\n+\t\t\t\t\t\t\tdata->sensor_->resolution());\n+\t\t\t\tformats[pixFmt] = { kC3ISPMinSize, maxSize };\n+\t\t\t}\n+\t\t}\n+\n+\t\tStreamFormats streamFormats(formats);\n+\t\tStreamConfiguration cfg(streamFormats);\n+\t\tcfg.pixelFormat = pixelFormat;\n+\t\tcfg.bufferCount = 4;\n+\t\tcfg.size = size;\n+\n+\t\tconfig->addConfiguration(cfg);\n+\t}\n+\n+\tif (config->validate() == CameraConfiguration::Invalid)\n+\t\treturn nullptr;\n+\n+\treturn config;\n+}\n+\n+int PipelineHandlerC3ISP::configureRawStream(C3ISPCameraData *data,\n+\t\t\t\t\t const StreamConfiguration &config,\n+\t\t\t\t\t V4L2SubdeviceFormat &subdevFormat)\n+{\n+\tStream *stream = config.stream();\n+\tC3ISPPipe *pipe = pipeFromStream(data, stream);\n+\n+\tif (pipe != &pipes_[C3ISPSTILL]) {\n+\t\tLOG(C3ISP, Error) << \"Failed to match the RAW pipe\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tconst MediaEntity *resizerEntity = pipe->resizer->entity();\n+\tint ret = resizerEntity->getPadByIndex(0)->links()[0]->setEnabled(true);\n+\tif (ret) {\n+\t\tLOG(C3ISP, Error) << \"Couldn't enable resizer's sink link\";\n+\t\treturn ret;\n+\t}\n+\n+\tauto it = C3ISPFmtToCode.find(config.pixelFormat);\n+\tif (it == C3ISPFmtToCode.end()) {\n+\t\tLOG(C3ISP, Error) << \"Failed to find the RAW pixel format\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tsubdevFormat.code = it->second;\n+\tret = isp_->setFormat(5, &subdevFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = pipe->resizer->setFormat(0, &subdevFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn 0;\n+}\n+\n+int PipelineHandlerC3ISP::configureProcessedStream(C3ISPCameraData *data,\n+\t\t\t\t\t\t const StreamConfiguration &config,\n+\t\t\t\t\t\t V4L2SubdeviceFormat &subdevFormat)\n+{\n+\tStream *stream = config.stream();\n+\tC3ISPPipe *pipe = pipeFromStream(data, stream);\n+\n+\tconst MediaEntity *resizerEntity = pipe->resizer->entity();\n+\tint ret = resizerEntity->getPadByIndex(0)->links()[0]->setEnabled(true);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tauto it = C3ISPFmtToCode.find(config.pixelFormat);\n+\tif (it == C3ISPFmtToCode.end()) {\n+\t\tLOG(C3ISP, Error) << \"Failed to find the processed pixel format\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tunsigned int pad;\n+\tif (pipe == &pipes_[C3ISPVIEW])\n+\t\tpad = 3;\n+\telse if (pipe == &pipes_[C3ISPVIDEO])\n+\t\tpad = 4;\n+\telse if (pipe == &pipes_[C3ISPSTILL])\n+\t\tpad = 5;\n+\telse {\n+\t\tLOG(C3ISP, Error) << \"Failed to match the pipe\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tsubdevFormat.code = it->second;\n+\tret = isp_->setFormat(pad, &subdevFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = pipe->resizer->setFormat(0, &subdevFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Compute the scaler-in to scaler-out ratio: first center-crop to align\n+\t * the FOV to the desired resolution, then scale to the desired size.\n+\t */\n+\tSize scalerIn = subdevFormat.size.boundedToAspectRatio(config.size);\n+\tint xCrop = (subdevFormat.size.width - scalerIn.width) / 2;\n+\tint yCrop = (subdevFormat.size.height - scalerIn.height) / 2;\n+\n+\tRectangle cropRect = { xCrop, yCrop, scalerIn };\n+\tret = pipe->resizer->setSelection(0, V4L2_SEL_TGT_CROP, &cropRect);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tRectangle scaleRect = { 0, 0, config.size };\n+\tret = pipe->resizer->setSelection(0, V4L2_SEL_TGT_COMPOSE, &scaleRect);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tsubdevFormat.size = scaleRect.size();\n+\n+\treturn pipe->resizer->setFormat(1, &subdevFormat);\n+}\n+\n+int PipelineHandlerC3ISP::configure(Camera *camera, CameraConfiguration *config)\n+{\n+\tresetPipes();\n+\n+\tint ret = media_->disableLinks();\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tC3ISPCameraData *data = cameraData(camera);\n+\n+\t/* Enable the link between sensor and CSI-2 */\n+\tconst MediaEntity *csiEntity = data->csi_->entity();\n+\tret = csiEntity->getPadByIndex(0)->links()[0]->setEnabled(true);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Enable the link between CSI-2 and adapter */\n+\tconst MediaEntity *adapEntity = data->adap_->entity();\n+\tret = adapEntity->getPadByIndex(0)->links()[0]->setEnabled(true);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Enable the link between adapter and ISP */\n+\tconst MediaEntity *ispEntity = isp_->entity();\n+\tret = ispEntity->getPadByIndex(0)->links()[0]->setEnabled(true);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tC3ISPCameraConfiguration *c3Config = static_cast<C3ISPCameraConfiguration *>(config);\n+\tV4L2SubdeviceFormat subdevFormat = c3Config->sensorFormat_;\n+\n+\tret = data->sensor_->setFormat(&subdevFormat, c3Config->combinedTransform());\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = data->csi_->setFormat(0, &subdevFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = data->adap_->setFormat(0, &subdevFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = isp_->setFormat(0, &subdevFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tfor (const StreamConfiguration &streamConfig : *config) {\n+\t\tStream *stream = streamConfig.stream();\n+\t\tC3ISPPipe *pipe = pipeFromStream(data, stream);\n+\n+\t\tif (isFmtRaw(streamConfig.pixelFormat))\n+\t\t\tret = configureRawStream(data, streamConfig, subdevFormat);\n+\t\telse\n+\t\t\tret = configureProcessedStream(data, streamConfig, subdevFormat);\n+\t\tif (ret) {\n+\t\t\tLOG(C3ISP, Error) << \"Failed to configure pipeline\";\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tV4L2DeviceFormat captureFormat;\n+\t\tcaptureFormat.fourcc = pipe->cap->toV4L2PixelFormat(streamConfig.pixelFormat);\n+\t\tcaptureFormat.size = streamConfig.size;\n+\n+\t\tret = pipe->cap->setFormat(&captureFormat);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tpipe->stream = stream;\n+\t}\n+\n+\t/* Enable the link between parameter node and ISP */\n+\tret = ispEntity->getPadByIndex(1)->links()[0]->setEnabled(true);\n+\tif (ret) {\n+\t\tLOG(C3ISP, Error) << \"Failed to enable param link\";\n+\t\treturn ret;\n+\t}\n+\n+\t/* Enable the link between ISP and 3A stats node */\n+\tret = ispEntity->getPadByIndex(2)->links()[0]->setEnabled(true);\n+\tif (ret) {\n+\t\tLOG(C3ISP, Error) << \"Failed to enable stats link\";\n+\t\treturn ret;\n+\t}\n+\n+\t/* Inform the IPA of the sensor configuration */\n+\tipa::c3isp::IPAConfigInfo ipaConfig{};\n+\n+\tret = data->sensor_->sensorInfo(&ipaConfig.sensorInfo);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tipaConfig.sensorControls = data->sensor_->controls();\n+\n+\tControlInfoMap ipaControls;\n+\tret = data->ipa_->configure(ipaConfig, &ipaControls);\n+\tif (ret) {\n+\t\tLOG(C3ISP, Error) << \"Failed to configure IPA\";\n+\t\treturn ret;\n+\t}\n+\n+\tdata->updateControls(ipaControls);\n+\n+\treturn 0;\n+}\n+\n+int PipelineHandlerC3ISP::exportFrameBuffers(Camera *camera, Stream *stream,\n+\t\t\t\t\t std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n+{\n+\tC3ISPPipe *pipe = pipeFromStream(cameraData(camera), stream);\n+\tunsigned int count = stream->configuration().bufferCount;\n+\n+\treturn pipe->cap->exportBuffers(count, buffers);\n+}\n+\n+int PipelineHandlerC3ISP::allocateBuffers(Camera *camera)\n+{\n+\tC3ISPCameraData *data = cameraData(camera);\n+\tunsigned int ipaBufferId = 1;\n+\tunsigned int bufferCount;\n+\tint ret;\n+\n+\tbufferCount = std::max({ data->viewStream.configuration().bufferCount,\n+\t\t\t\t data->videoStream.configuration().bufferCount,\n+\t\t\t\t data->stillStream.configuration().bufferCount });\n+\n+\tret = stats_->allocateBuffers(bufferCount, &statsBuffers_);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tfor (std::unique_ptr<FrameBuffer> &buffer : statsBuffers_) {\n+\t\tbuffer->setCookie(ipaBufferId++);\n+\t\tdata->ipaStatBuffers_.emplace_back(buffer->cookie(),\n+\t\t\t\t\t\t buffer->planes());\n+\t\tavailableStatsBuffers_.push(buffer.get());\n+\t}\n+\n+\tret = params_->allocateBuffers(bufferCount, ¶msBuffers_);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tfor (std::unique_ptr<FrameBuffer> &buffer : paramsBuffers_) {\n+\t\tbuffer->setCookie(ipaBufferId++);\n+\t\tdata->ipaParamBuffers_.emplace_back(buffer->cookie(),\n+\t\t\t\t\t\t buffer->planes());\n+\t\tavailableParamsBuffers_.push(buffer.get());\n+\t}\n+\n+\tdata->ipa_->mapBuffers(data->ipaStatBuffers_, true);\n+\tdata->ipa_->mapBuffers(data->ipaParamBuffers_, false);\n+\n+\treturn 0;\n+}\n+\n+void PipelineHandlerC3ISP::freeBuffers(Camera *camera)\n+{\n+\tC3ISPCameraData *data = cameraData(camera);\n+\n+\twhile (!availableStatsBuffers_.empty())\n+\t\tavailableStatsBuffers_.pop();\n+\twhile (!availableParamsBuffers_.empty())\n+\t\tavailableParamsBuffers_.pop();\n+\n+\tstatsBuffers_.clear();\n+\tparamsBuffers_.clear();\n+\n+\tdata->ipa_->unmapBuffers(data->ipaStatBuffers_);\n+\tdata->ipa_->unmapBuffers(data->ipaParamBuffers_);\n+\n+\tdata->ipaStatBuffers_.clear();\n+\tdata->ipaParamBuffers_.clear();\n+\n+\tif (stats_->releaseBuffers())\n+\t\tLOG(C3ISP, Error) << \"Failed to release stats buffers\";\n+\n+\tif (params_->releaseBuffers())\n+\t\tLOG(C3ISP, Error) << \"Failed to release params buffers\";\n+}\n+\n+int PipelineHandlerC3ISP::pipesStart()\n+{\n+\tfor (C3ISPPipe &pipe : pipes_) {\n+\t\tif (!pipe.stream)\n+\t\t\tcontinue;\n+\n+\t\tStream *stream = pipe.stream;\n+\n+\t\tint ret = pipe.cap->importBuffers(stream->configuration().bufferCount);\n+\t\tif (ret) {\n+\t\t\tLOG(C3ISP, Error) << \"Failed to import buffers\";\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tret = pipe.cap->streamOn();\n+\t\tif (ret) {\n+\t\t\tLOG(C3ISP, Error) << \"Failed to start stream\";\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+void PipelineHandlerC3ISP::pipesStop()\n+{\n+\tfor (C3ISPPipe &pipe : pipes_) {\n+\t\tif (!pipe.stream)\n+\t\t\tcontinue;\n+\n+\t\tpipe.cap->streamOff();\n+\t\tpipe.cap->releaseBuffers();\n+\t}\n+}\n+\n+int PipelineHandlerC3ISP::start(Camera *camera,\n+\t\t\t\t[[maybe_unused]] const ControlList *controls)\n+{\n+\tC3ISPCameraData *data = cameraData(camera);\n+\tint ret;\n+\n+\tret = allocateBuffers(camera);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = data->ipa_->start();\n+\tif (ret) {\n+\t\tLOG(C3ISP, Error) << \"Failed to start IPA\";\n+\n+\t\tfreeBuffers(camera);\n+\t\treturn ret;\n+\t}\n+\n+\tret = pipesStart();\n+\tif (ret) {\n+\t\tLOG(C3ISP, Error) << \"Failed to start pipe\";\n+\n+\t\tdata->ipa_->stop();\n+\t\tfreeBuffers(camera);\n+\t\treturn ret;\n+\t}\n+\n+\tret = stats_->streamOn();\n+\tif (ret) {\n+\t\tLOG(C3ISP, Error) << \"Failed to start stats\";\n+\n+\t\tpipesStop();\n+\t\tdata->ipa_->stop();\n+\t\tfreeBuffers(camera);\n+\t\treturn ret;\n+\t}\n+\n+\tret = params_->streamOn();\n+\tif (ret) {\n+\t\tLOG(C3ISP, Error) << \"Failed to start params\";\n+\n+\t\tstats_->streamOff();\n+\t\tpipesStop();\n+\t\tdata->ipa_->stop();\n+\t\tfreeBuffers(camera);\n+\t\treturn ret;\n+\t}\n+\n+\tret = isp_->setFrameStartEnabled(true);\n+\tif (ret) {\n+\t\tLOG(C3ISP, Error) << \"Failed to enable frame start\";\n+\n+\t\tparams_->streamOff();\n+\t\tstats_->streamOff();\n+\t\tpipesStop();\n+\t\tdata->ipa_->stop();\n+\t\tfreeBuffers(camera);\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+void PipelineHandlerC3ISP::stopDevice(Camera *camera)\n+{\n+\tC3ISPCameraData *data = cameraData(camera);\n+\n+\tisp_->setFrameStartEnabled(false);\n+\n+\tparams_->streamOff();\n+\tstats_->streamOff();\n+\tdata->ipa_->stop();\n+\tfreeBuffers(camera);\n+\n+\tpipesStop();\n+\tclearIncompleteRequests();\n+}\n+\n+int PipelineHandlerC3ISP::queueRequestDevice(Camera *camera, Request *request)\n+{\n+\tC3ISPCameraData *data = cameraData(camera);\n+\n+\tif (availableStatsBuffers_.empty()) {\n+\t\tLOG(C3ISP, Error) << \"No available stats buffer\";\n+\t\treturn -ENOENT;\n+\t}\n+\n+\tif (availableParamsBuffers_.empty()) {\n+\t\tLOG(C3ISP, Error) << \"No available params buffer\";\n+\t\treturn -ENOENT;\n+\t}\n+\n+\tC3ISPFrameInfo frameInfo;\n+\tframeInfo.request = request;\n+\n+\tframeInfo.statBuffer = availableStatsBuffers_.front();\n+\tavailableStatsBuffers_.pop();\n+\tframeInfo.paramBuffer = availableParamsBuffers_.front();\n+\tavailableParamsBuffers_.pop();\n+\n+\tframeInfo.paramsDone = false;\n+\tframeInfo.statsDone = false;\n+\n+\tframeInfoMap_[request->sequence()] = frameInfo;\n+\n+\tdata->ipa_->queueRequest(request->sequence(), request->controls());\n+\tdata->ipa_->computeParams(request->sequence(),\n+\t\t\t\t frameInfo.paramBuffer->cookie());\n+\n+\treturn 0;\n+}\n+\n+C3ISPFrameInfo *PipelineHandlerC3ISP::findFrameInfo(Request *request)\n+{\n+\tfor (auto &[sequence, info] : frameInfoMap_) {\n+\t\tif (info.request == request)\n+\t\t\treturn &info;\n+\t}\n+\n+\treturn nullptr;\n+}\n+\n+C3ISPFrameInfo *PipelineHandlerC3ISP::findFrameInfo(FrameBuffer *buffer)\n+{\n+\tfor (auto &[sequence, info] : frameInfoMap_) {\n+\t\tif (info.paramBuffer == buffer || info.statBuffer == buffer)\n+\t\t\treturn &info;\n+\t}\n+\n+\treturn nullptr;\n+}\n+\n+void PipelineHandlerC3ISP::clearIncompleteRequests()\n+{\n+\tfor (auto &[sequence, info] : frameInfoMap_) {\n+\t\tif (info.request)\n+\t\t\tcancelRequest(info.request);\n+\t}\n+\n+\tframeInfoMap_.clear();\n+}\n+\n+void PipelineHandlerC3ISP::tryComplete(C3ISPFrameInfo *info)\n+{\n+\tif (!info->paramsDone)\n+\t\treturn;\n+\n+\tif (!info->statsDone)\n+\t\treturn;\n+\n+\tRequest *request = info->request;\n+\tif (request->hasPendingBuffers())\n+\t\treturn;\n+\n+\tif (info->statBuffer)\n+\t\tavailableStatsBuffers_.push(info->statBuffer);\n+\n+\tif (info->paramBuffer)\n+\t\tavailableParamsBuffers_.push(info->paramBuffer);\n+\n+\tframeInfoMap_.erase(request->sequence());\n+\n+\tcompleteRequest(request);\n+}\n+\n+void PipelineHandlerC3ISP::imageBufferReady(FrameBuffer *buffer)\n+{\n+\tRequest *request = buffer->request();\n+\tC3ISPFrameInfo *frameInfo = findFrameInfo(request);\n+\tASSERT(frameInfo);\n+\n+\tif (completeBuffer(request, buffer))\n+\t\ttryComplete(frameInfo);\n+}\n+\n+void PipelineHandlerC3ISP::paramsBufferReady(FrameBuffer *buffer)\n+{\n+\tC3ISPFrameInfo *frameInfo = findFrameInfo(buffer);\n+\tASSERT(frameInfo);\n+\n+\tframeInfo->paramsDone = true;\n+\n+\ttryComplete(frameInfo);\n+}\n+\n+void PipelineHandlerC3ISP::statsBufferReady(FrameBuffer *buffer)\n+{\n+\tC3ISPFrameInfo *frameInfo = findFrameInfo(buffer);\n+\tASSERT(frameInfo);\n+\n+\tRequest *request = frameInfo->request;\n+\tC3ISPCameraData *data = cameraData(request->_d()->camera());\n+\n+\tControlList sensorControls = data->delayedCtrls_->get(buffer->metadata().sequence);\n+\n+\tdata->ipa_->processStats(request->sequence(), buffer->cookie(),\n+\t\t\t\t sensorControls);\n+}\n+\n+void PipelineHandlerC3ISP::paramsComputed(unsigned int requestId,\n+\t\t\t\t\t unsigned int bytesused)\n+{\n+\tC3ISPFrameInfo &frameInfo = frameInfoMap_[requestId];\n+\tRequest *request = frameInfo.request;\n+\tC3ISPCameraData *data = cameraData(request->_d()->camera());\n+\n+\tframeInfo.paramBuffer->_d()->metadata().planes()[0].bytesused = bytesused;\n+\n+\tparams_->queueBuffer(frameInfo.paramBuffer);\n+\tstats_->queueBuffer(frameInfo.statBuffer);\n+\n+\tfor (auto &[stream, buffer] : request->buffers()) {\n+\t\tC3ISPPipe *pipe = pipeFromStream(data, stream);\n+\n+\t\tpipe->cap->queueBuffer(buffer);\n+\t}\n+}\n+\n+void PipelineHandlerC3ISP::statsProcessed(unsigned int requestId,\n+\t\t\t\t\t const ControlList &metadata)\n+{\n+\tC3ISPFrameInfo &frameInfo = frameInfoMap_[requestId];\n+\n+\tframeInfo.statsDone = true;\n+\tframeInfo.request->metadata().merge(metadata);\n+\n+\ttryComplete(&frameInfo);\n+}\n+\n+bool PipelineHandlerC3ISP::createCamera(MediaLink *ispLink)\n+{\n+\tMediaEntity *adap = ispLink->source()->entity();\n+\tconst MediaPad *adapSink = adap->getPadByIndex(0);\n+\tMediaEntity *csi = adapSink->links()[0]->source()->entity();\n+\tconst MediaPad *csiSink = csi->getPadByIndex(0);\n+\n+\tMediaEntity *sensor = csiSink->links()[0]->source()->entity();\n+\tif (sensor->function() != MEDIA_ENT_F_CAM_SENSOR)\n+\t\treturn false;\n+\n+\tstd::unique_ptr<C3ISPCameraData> data =\n+\t\tstd::make_unique<C3ISPCameraData>(this, sensor);\n+\tif (data->init())\n+\t\treturn false;\n+\n+\t/* Generic values for sensor */\n+\tconst CameraSensorProperties::SensorDelays &delays = data->sensor_->sensorDelays();\n+\tstd::unordered_map<uint32_t, DelayedControls::ControlParams> params = {\n+\t\t{ V4L2_CID_ANALOGUE_GAIN, { delays.gainDelay, false } },\n+\t\t{ V4L2_CID_EXPOSURE, { delays.exposureDelay, false } },\n+\t};\n+\n+\tdata->delayedCtrls_ =\n+\t\tstd::make_unique<DelayedControls>(data->sensor_->device(), params);\n+\tisp_->frameStart.connect(data->delayedCtrls_.get(),\n+\t\t\t\t &DelayedControls::applyControls);\n+\n+\tif (data->loadIPA())\n+\t\treturn false;\n+\n+\tdata->ipa_->statsProcessed.connect(this, &PipelineHandlerC3ISP::statsProcessed);\n+\tdata->ipa_->paramsComputed.connect(this, &PipelineHandlerC3ISP::paramsComputed);\n+\n+\tstd::set<Stream *> streams{ &data->viewStream, &data->stillStream, &data->videoStream };\n+\n+\tstd::shared_ptr<Camera> camera = Camera::create(std::move(data), sensor->name(), streams);\n+\n+\tregisterCamera(std::move(camera));\n+\n+\treturn true;\n+}\n+\n+bool PipelineHandlerC3ISP::match(DeviceEnumerator *enumerator)\n+{\n+\tconst MediaPad *ispSink;\n+\n+\tDeviceMatch dm(\"c3-isp\");\n+\tdm.add(\"c3-mipi-csi2\");\n+\tdm.add(\"c3-mipi-adapter\");\n+\tdm.add(\"c3-isp-core\");\n+\n+\tmedia_ = acquireMediaDevice(enumerator, dm);\n+\tif (!media_)\n+\t\treturn false;\n+\n+\tisp_ = V4L2Subdevice::fromEntityName(media_, \"c3-isp-core\");\n+\tif (isp_->open() < 0)\n+\t\treturn false;\n+\n+\tstats_ = V4L2VideoDevice::fromEntityName(media_, \"c3-isp-stats\");\n+\tif (stats_->open() < 0)\n+\t\treturn false;\n+\n+\tparams_ = V4L2VideoDevice::fromEntityName(media_, \"c3-isp-params\");\n+\tif (params_->open() < 0)\n+\t\treturn false;\n+\n+\tfor (unsigned int i = C3ISPVIEW; i < C3ISPNumPipes; i++) {\n+\t\tC3ISPPipe *ispPipe = &pipes_[i];\n+\n+\t\tstd::ostringstream resizer;\n+\t\tresizer << \"c3-isp-resizer\" << i;\n+\t\tispPipe->resizer = V4L2Subdevice::fromEntityName(media_, resizer.str());\n+\t\tif (ispPipe->resizer->open() < 0)\n+\t\t\treturn false;\n+\n+\t\tstd::ostringstream capture;\n+\t\tcapture << \"c3-isp-cap\" << i;\n+\t\tispPipe->cap = V4L2VideoDevice::fromEntityName(media_, capture.str());\n+\t\tif (ispPipe->cap->open() < 0)\n+\t\t\treturn false;\n+\n+\t\tispPipe->cap->bufferReady.connect(this, &PipelineHandlerC3ISP::imageBufferReady);\n+\t}\n+\n+\tstats_->bufferReady.connect(this, &PipelineHandlerC3ISP::statsBufferReady);\n+\tparams_->bufferReady.connect(this, &PipelineHandlerC3ISP::paramsBufferReady);\n+\n+\tispSink = isp_->entity()->getPadByIndex(0);\n+\tif (!ispSink || ispSink->links().empty())\n+\t\treturn false;\n+\n+\tif (!createCamera(ispSink->links()[0])) {\n+\t\tLOG(C3ISP, Error) << \"Failed to create camera\";\n+\t\treturn false;\n+\t}\n+\n+\treturn true;\n+}\n+\n+REGISTER_PIPELINE_HANDLER(PipelineHandlerC3ISP, \"c3isp\")\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/pipeline/c3-isp/meson.build b/src/libcamera/pipeline/c3-isp/meson.build\nnew file mode 100644\nindex 00000000..5f8b23f1\n--- /dev/null\n+++ b/src/libcamera/pipeline/c3-isp/meson.build\n@@ -0,0 +1,5 @@\n+# SPDX-License-Identifier: CC0-1.0\n+\n+libcamera_internal_sources += files([\n+ 'c3-isp.cpp'\n+])\n", "prefixes": [ "v3", "02/11" ] }