Show a patch.

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

{
    "id": 19801,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/19801/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/19801/",
    "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": "<20240326092237.14748-1-jacopo.mondi@ideasonboard.com>",
    "date": "2024-03-26T09:22:36",
    "name": "[v2] libcamera: pipeline: Add Mali-C55 ISP pipeline",
    "commit_ref": "0fbc801a1d186a6f2d11fcbe5854711497746a7f",
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "1ee29a464e8fd3eca6a46748e63226a2f3017c23",
    "submitter": {
        "id": 143,
        "url": "https://patchwork.libcamera.org/api/1.1/people/143/?format=api",
        "name": "Jacopo Mondi",
        "email": "jacopo.mondi@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/19801/mbox/",
    "series": [
        {
            "id": 4240,
            "url": "https://patchwork.libcamera.org/api/1.1/series/4240/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4240",
            "date": "2024-03-26T09:22:36",
            "name": "[v2] libcamera: pipeline: Add Mali-C55 ISP pipeline",
            "version": 2,
            "mbox": "https://patchwork.libcamera.org/series/4240/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/19801/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/19801/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 D9B3FC0DA4\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 26 Mar 2024 09:22:57 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 05AA563311;\n\tTue, 26 Mar 2024 10:22:57 +0100 (CET)",
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 83E646303D\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 26 Mar 2024 10:22:55 +0100 (CET)",
            "from localhost.localdomain (93-61-96-190.ip145.fastwebnet.it\n\t[93.61.96.190])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 59CFA497;\n\tTue, 26 Mar 2024 10:22:23 +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=\"lyxQaBZm\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1711444943;\n\tbh=WURBA1XSq74Nu2vlPgHCdXnBTNUlB5r6hmS0w3y7ufw=;\n\th=From:To:Cc:Subject:Date:From;\n\tb=lyxQaBZmEFft+/56l2RvbXOKeR3c0eb5YT2uRyuU0ICs2nmTZTqcdtWRzAIVI9fVb\n\t9cyjWjX5uVimytpkuMK7OYBOiuYSsJLRboEriH4O8UaB7uROyjWDEUZrubX/y0qo5z\n\tM0H4esscsc0Lck8Dk/HbIfKijjaqDvpZy/Ug9w7U=",
        "From": "Jacopo Mondi <jacopo.mondi@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Cc": "Jacopo Mondi <jacopo.mondi@ideasonboard.com>,\n\tDaniel Scally <dan.scally@ideasonboard.com>,\n\tNayden Kanchev <nayden.kanchev@arm.com>,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>",
        "Subject": "[PATCH v2] libcamera: pipeline: Add Mali-C55 ISP pipeline",
        "Date": "Tue, 26 Mar 2024 10:22:36 +0100",
        "Message-ID": "<20240326092237.14748-1-jacopo.mondi@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.44.0",
        "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 a pipeline handler for the Mali-C55 ISP.\n\nThe pipeline doesn't currently support an IPA and does not run\nany 3a algorithm but only handles the media graph topology and\nformats/sizes configuration\n\nCo-developed-by: Daniel Scally <dan.scally@ideasonboard.com>\nSigned-off-by: Daniel Scally <dan.scally@ideasonboard.com>\nSigned-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\nAcked-by: Nayden Kanchev <nayden.kanchev@arm.com>\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n---\nCI:\nhttps://gitlab.freedesktop.org/pinchartl/libcamera/-/pipelines/1131635\n\nv1->v2:\n- rebased on master (v4l2-subdevice format rework and Stream/Routing)\n- Fix trailing ';' in namespace declaration\n- Use [[fallthrough]]\n- Use std::find() instead of std::find_if() in generateConfiguration()\n- Return nullptr in generateConfiguration() if the request is not valid\n---\n meson.build                                  |    1 +\n meson_options.txt                            |    1 +\n src/libcamera/pipeline/mali-c55/mali-c55.cpp | 1066 ++++++++++++++++++\n src/libcamera/pipeline/mali-c55/meson.build  |    5 +\n 4 files changed, 1073 insertions(+)\n create mode 100644 src/libcamera/pipeline/mali-c55/mali-c55.cpp\n create mode 100644 src/libcamera/pipeline/mali-c55/meson.build\n\n--\n2.44.0",
    "diff": "diff --git a/meson.build b/meson.build\nindex cb6b666a7449..740ead1be85f 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -198,6 +198,7 @@ arch_x86 = ['x86', 'x86_64']\n pipelines_support = {\n     'imx8-isi':     arch_arm,\n     'ipu3':         arch_x86,\n+    'mali-c55':     arch_arm,\n     'rkisp1':       arch_arm,\n     'rpi/vc4':      arch_arm,\n     'simple':       arch_arm,\ndiff --git a/meson_options.txt b/meson_options.txt\nindex 99dab96d7b21..7c4f6d3a0af6 100644\n--- a/meson_options.txt\n+++ b/meson_options.txt\n@@ -43,6 +43,7 @@ option('pipelines',\n             'auto',\n             'imx8-isi',\n             'ipu3',\n+            'mali-c55',\n             'rkisp1',\n             'rpi/vc4',\n             'simple',\ndiff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp\nnew file mode 100644\nindex 000000000000..78343553bafa\n--- /dev/null\n+++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp\n@@ -0,0 +1,1066 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Ideas on Board Oy\n+ *\n+ * mali-c55.cpp - Pipeline Handler for ARM's Mali-C55 ISP\n+ */\n+\n+#include <algorithm>\n+#include <array>\n+#include <map>\n+#include <memory>\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/internal/bayer_format.h\"\n+#include \"libcamera/internal/camera.h\"\n+#include \"libcamera/internal/camera_sensor.h\"\n+#include \"libcamera/internal/device_enumerator.h\"\n+#include \"libcamera/internal/media_device.h\"\n+#include \"libcamera/internal/pipeline_handler.h\"\n+#include \"libcamera/internal/v4l2_subdevice.h\"\n+#include \"libcamera/internal/v4l2_videodevice.h\"\n+\n+namespace {\n+\n+bool isFormatRaw(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(MaliC55)\n+\n+const std::map<libcamera::PixelFormat, unsigned int> maliC55FmtToCode = {\n+\t/* \\todo Support all formats supported by the driver in libcamera. */\n+\n+\t{ formats::RGB565, MEDIA_BUS_FMT_RGB121212_1X36 },\n+\t{ formats::RGB888, MEDIA_BUS_FMT_RGB121212_1X36 },\n+\t{ formats::YUYV, MEDIA_BUS_FMT_YUV10_1X30 },\n+\t{ formats::UYVY, MEDIA_BUS_FMT_YUV10_1X30 },\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+\n+\t/* RAW formats, FR pipe only. */\n+\t{ formats::SGBRG8, MEDIA_BUS_FMT_SGBRG8_1X8 },\n+\t{ formats::SRGGB8, MEDIA_BUS_FMT_SRGGB8_1X8 },\n+\t{ formats::SBGGR8, MEDIA_BUS_FMT_SBGGR8_1X8 },\n+\t{ formats::SGRBG8, MEDIA_BUS_FMT_SGRBG8_1X8 },\n+\t{ formats::SGBRG10, MEDIA_BUS_FMT_SGBRG10_1X10 },\n+\t{ formats::SRGGB10, MEDIA_BUS_FMT_SRGGB10_1X10 },\n+\t{ formats::SBGGR10, MEDIA_BUS_FMT_SBGGR10_1X10 },\n+\t{ formats::SGRBG10, MEDIA_BUS_FMT_SGRBG10_1X10 },\n+\t{ formats::SGBRG12, MEDIA_BUS_FMT_SGBRG12_1X12 },\n+\t{ formats::SRGGB12, MEDIA_BUS_FMT_SRGGB12_1X12 },\n+\t{ formats::SBGGR12, MEDIA_BUS_FMT_SBGGR12_1X12 },\n+\t{ formats::SGRBG12, MEDIA_BUS_FMT_SGRBG12_1X12 },\n+\t{ formats::SGBRG14, MEDIA_BUS_FMT_SGBRG14_1X14 },\n+\t{ formats::SRGGB14, MEDIA_BUS_FMT_SRGGB14_1X14 },\n+\t{ formats::SBGGR14, MEDIA_BUS_FMT_SBGGR14_1X14 },\n+\t{ formats::SGRBG14, MEDIA_BUS_FMT_SGRBG14_1X14 },\n+\t{ formats::SGBRG16, MEDIA_BUS_FMT_SGBRG16_1X16 },\n+\t{ formats::SRGGB16, MEDIA_BUS_FMT_SRGGB16_1X16 },\n+\t{ formats::SBGGR16, MEDIA_BUS_FMT_SBGGR16_1X16 },\n+\t{ formats::SGRBG16, MEDIA_BUS_FMT_SGRBG16_1X16 },\n+};\n+\n+constexpr Size kMaliC55MinSize = { 128, 128 };\n+constexpr Size kMaliC55MaxSize = { 8192, 8192 };\n+constexpr unsigned int kMaliC55ISPInternalFormat = MEDIA_BUS_FMT_RGB121212_1X36;\n+\n+class MaliC55CameraData : public Camera::Private\n+{\n+public:\n+\tMaliC55CameraData(PipelineHandler *pipe, MediaEntity *entity)\n+\t\t: Camera::Private(pipe), entity_(entity)\n+\t{\n+\t}\n+\n+\tint init();\n+\n+\t/* Deflect these functionalities to either TPG or CameraSensor. */\n+\tconst std::vector<unsigned int> mbusCodes() const;\n+\tconst std::vector<Size> sizes(unsigned int mbusCode) const;\n+\tconst Size resolution() const;\n+\n+\tPixelFormat bestRawFormat() const;\n+\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+\n+\tMediaEntity *entity_;\n+\tstd::unique_ptr<V4L2Subdevice> csi_;\n+\tstd::unique_ptr<V4L2Subdevice> sd_;\n+\tStream frStream_;\n+\tStream dsStream_;\n+\n+private:\n+\tvoid initTPGData();\n+\n+\tstd::string id_;\n+\tstd::vector<unsigned int> tpgCodes_;\n+\tstd::vector<Size> tpgSizes_;\n+\tSize tpgResolution_;\n+};\n+\n+int MaliC55CameraData::init()\n+{\n+\tint ret;\n+\n+\tsd_ = std::make_unique<V4L2Subdevice>(entity_);\n+\tret = sd_->open();\n+\tif (ret) {\n+\t\tLOG(MaliC55, Error) << \"Failed to open sensor subdevice\";\n+\t\treturn ret;\n+\t}\n+\n+\t/* If this camera is created from TPG, we return here. */\n+\tif (entity_->name() == \"mali-c55 tpg\") {\n+\t\tinitTPGData();\n+\t\treturn 0;\n+\t}\n+\n+\t/*\n+\t * Register a CameraSensor if we connect to a sensor and create\n+\t * an entity for the connected CSI-2 receiver.\n+\t */\n+\tsensor_ = std::make_unique<CameraSensor>(entity_);\n+\tret = sensor_->init();\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tconst MediaPad *sourcePad = entity_->getPadByIndex(0);\n+\tMediaEntity *csiEntity = sourcePad->links()[0]->sink()->entity();\n+\n+\tcsi_ = std::make_unique<V4L2Subdevice>(csiEntity);\n+\tif (csi_->open()) {\n+\t\tLOG(MaliC55, Error) << \"Failed to open CSI-2 subdevice\";\n+\t\treturn false;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+void MaliC55CameraData::initTPGData()\n+{\n+\t/* Replicate the CameraSensor implementation for TPG. */\n+\tV4L2Subdevice::Formats formats = sd_->formats(0);\n+\tif (formats.empty())\n+\t\treturn;\n+\n+\ttpgCodes_ = utils::map_keys(formats);\n+\tstd::sort(tpgCodes_.begin(), tpgCodes_.end());\n+\n+\tfor (const auto &format : formats) {\n+\t\tconst std::vector<SizeRange> &ranges = format.second;\n+\t\tstd::transform(ranges.begin(), ranges.end(), std::back_inserter(tpgSizes_),\n+\t\t\t       [](const SizeRange &range) { return range.max; });\n+\t}\n+\n+\ttpgResolution_ = tpgSizes_.back();\n+}\n+\n+const std::vector<unsigned int> MaliC55CameraData::mbusCodes() const\n+{\n+\tif (sensor_)\n+\t\treturn sensor_->mbusCodes();\n+\n+\treturn tpgCodes_;\n+}\n+\n+const std::vector<Size> MaliC55CameraData::sizes(unsigned int mbusCode) const\n+{\n+\tif (sensor_)\n+\t\treturn sensor_->sizes(mbusCode);\n+\n+\tV4L2Subdevice::Formats formats = sd_->formats(0);\n+\tif (formats.empty())\n+\t\treturn {};\n+\n+\tstd::vector<Size> sizes;\n+\tconst auto &format = formats.find(mbusCode);\n+\tif (format == formats.end())\n+\t\treturn {};\n+\n+\tconst std::vector<SizeRange> &ranges = format->second;\n+\tstd::transform(ranges.begin(), ranges.end(), std::back_inserter(sizes),\n+\t\t       [](const SizeRange &range) { return range.max; });\n+\n+\tstd::sort(sizes.begin(), sizes.end());\n+\n+\treturn sizes;\n+}\n+\n+const Size MaliC55CameraData::resolution() const\n+{\n+\tif (sensor_)\n+\t\treturn sensor_->resolution();\n+\n+\treturn tpgResolution_;\n+}\n+\n+PixelFormat MaliC55CameraData::bestRawFormat() const\n+{\n+\tunsigned int bitDepth = 0;\n+\tPixelFormat rawFormat;\n+\n+\t/*\n+\t * Iterate over all the supported PixelFormat and find the one\n+\t * supported by the camera with the largest bitdepth.\n+\t */\n+\tfor (const auto &maliFormat : maliC55FmtToCode) {\n+\t\tPixelFormat pixFmt = maliFormat.first;\n+\t\tif (!isFormatRaw(pixFmt))\n+\t\t\tcontinue;\n+\n+\t\tunsigned int rawCode = maliFormat.second;\n+\t\tconst auto rawSizes = sizes(rawCode);\n+\t\tif (rawSizes.empty())\n+\t\t\tcontinue;\n+\n+\t\tBayerFormat bayer = BayerFormat::fromMbusCode(rawCode);\n+\t\tif (bayer.bitDepth > bitDepth) {\n+\t\t\tbitDepth = bayer.bitDepth;\n+\t\t\trawFormat = pixFmt;\n+\t\t}\n+\t}\n+\n+\treturn rawFormat;\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 MaliC55CameraData::adjustRawFormat(const PixelFormat &rawFmt) const\n+{\n+\t/* Make sure the provided raw format is supported by the pipeline. */\n+\tauto it = maliC55FmtToCode.find(rawFmt);\n+\tif (it == maliC55FmtToCode.end())\n+\t\treturn bestRawFormat();\n+\n+\t/* Now make sure the RAW mbus code is supported by the image source. */\n+\tunsigned int rawCode = it->second;\n+\tconst auto rawSizes = sizes(rawCode);\n+\tif (rawSizes.empty())\n+\t\treturn bestRawFormat();\n+\n+\treturn rawFmt;\n+}\n+\n+Size MaliC55CameraData::adjustRawSizes(const PixelFormat &rawFmt, const Size &rawSize) const\n+{\n+\t/* Just make sure the format is supported. */\n+\tauto it = maliC55FmtToCode.find(rawFmt);\n+\tif (it == maliC55FmtToCode.end())\n+\t\treturn {};\n+\n+\t/* Check if the size is natively supported. */\n+\tunsigned int rawCode = it->second;\n+\tconst auto rawSizes = sizes(rawCode);\n+\tauto sizeIt = std::find(rawSizes.begin(), rawSizes.end(), rawSize);\n+\tif (sizeIt != rawSizes.end())\n+\t\treturn rawSize;\n+\n+\t/* Or adjust it to the closest supported size. */\n+\tuint16_t distance = std::numeric_limits<uint16_t>::max();\n+\tSize bestSize;\n+\tfor (const Size &size : rawSizes) {\n+\t\tuint16_t dist = std::abs(static_cast<int>(rawSize.width) -\n+\t\t\t\t\t static_cast<int>(size.width)) +\n+\t\t\t\tstd::abs(static_cast<int>(rawSize.height) -\n+\t\t\t\t\t static_cast<int>(size.height));\n+\t\tif (dist < distance) {\n+\t\t\tdist = distance;\n+\t\t\tbestSize = size;\n+\t\t}\n+\t}\n+\n+\treturn bestSize;\n+}\n+\n+class MaliC55CameraConfiguration : public CameraConfiguration\n+{\n+public:\n+\tMaliC55CameraConfiguration(MaliC55CameraData *data)\n+\t\t: CameraConfiguration(), data_(data)\n+\t{\n+\t}\n+\n+\tStatus validate() override;\n+\n+\tV4L2SubdeviceFormat sensorFormat_;\n+\n+private:\n+\tstatic constexpr unsigned int kMaxStreams = 2;\n+\n+\tconst MaliC55CameraData *data_;\n+};\n+\n+CameraConfiguration::Status MaliC55CameraConfiguration::validate()\n+{\n+\tStatus status = Valid;\n+\n+\tif (config_.empty())\n+\t\treturn Invalid;\n+\n+\t/* Only 2 streams available. */\n+\tif (config_.size() > kMaxStreams) {\n+\t\tconfig_.resize(kMaxStreams);\n+\t\tstatus = Adjusted;\n+\t}\n+\n+\tbool frPipeAvailable = true;\n+\tStreamConfiguration *rawConfig = nullptr;\n+\tfor (StreamConfiguration &config : config_) {\n+\t\tif (!isFormatRaw(config.pixelFormat))\n+\t\t\tcontinue;\n+\n+\t\tif (rawConfig) {\n+\t\t\tLOG(MaliC55, Error)\n+\t\t\t\t<< \"Only a single RAW stream is supported\";\n+\t\t\treturn Invalid;\n+\t\t}\n+\n+\t\trawConfig = &config;\n+\t}\n+\n+\tSize maxSize = kMaliC55MaxSize;\n+\tif (rawConfig) {\n+\t\t/*\n+\t\t * \\todo Take into account the Bayer components ordering once\n+\t\t * we support rotations.\n+\t\t */\n+\t\tPixelFormat rawFormat =\n+\t\t\tdata_->adjustRawFormat(rawConfig->pixelFormat);\n+\t\tif (rawFormat != rawConfig->pixelFormat) {\n+\t\t\tLOG(MaliC55, Debug)\n+\t\t\t\t<< \"RAW format adjusted to \" << rawFormat;\n+\t\t\trawConfig->pixelFormat = rawFormat;\n+\t\t\tstatus = Adjusted;\n+\t\t}\n+\n+\t\tSize rawSize =\n+\t\t\tdata_->adjustRawSizes(rawFormat, rawConfig->size);\n+\t\tif (rawSize != rawConfig->size) {\n+\t\t\tLOG(MaliC55, Debug)\n+\t\t\t\t<< \"RAW sizes adjusted to \" << rawSize;\n+\t\t\trawConfig->size = rawSize;\n+\t\t\tstatus = Adjusted;\n+\t\t}\n+\n+\t\tmaxSize = rawSize;\n+\n+\t\trawConfig->setStream(const_cast<Stream *>(&data_->frStream_));\n+\t\tfrPipeAvailable = false;\n+\t}\n+\n+\t/* Adjust processed streams. */\n+\tSize maxYuvSize;\n+\tfor (StreamConfiguration &config : config_) {\n+\t\tif (isFormatRaw(config.pixelFormat))\n+\t\t\tcontinue;\n+\n+\t\t/* Adjust format and size for processed streams. */\n+\t\tconst auto it = maliC55FmtToCode.find(config.pixelFormat);\n+\t\tif (it == maliC55FmtToCode.end()) {\n+\t\t\tLOG(MaliC55, Debug)\n+\t\t\t\t<< \"Format adjusted to \" << formats::RGB565;\n+\t\t\tconfig.pixelFormat = formats::RGB565;\n+\t\t\tstatus = Adjusted;\n+\t\t}\n+\n+\t\tSize size = std::clamp(config.size, kMaliC55MinSize, maxSize);\n+\t\tif (size != config.size) {\n+\t\t\tLOG(MaliC55, 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 (maxYuvSize < size)\n+\t\t\tmaxYuvSize = size;\n+\n+\t\tif (frPipeAvailable) {\n+\t\t\tconfig.setStream(const_cast<Stream *>(&data_->frStream_));\n+\t\t\tfrPipeAvailable = false;\n+\t\t} else {\n+\t\t\tconfig.setStream(const_cast<Stream *>(&data_->dsStream_));\n+\t\t}\n+\t}\n+\n+\t/* Compute the sensor format. */\n+\n+\t/* If there's a RAW config, sensor configuration follows it. */\n+\tif (rawConfig) {\n+\t\tconst auto it = maliC55FmtToCode.find(rawConfig->pixelFormat);\n+\t\tsensorFormat_.code = it->second;\n+\t\tsensorFormat_.size = rawConfig->size;\n+\n+\t\treturn status;\n+\t}\n+\n+\t/* If there's no RAW config, compute the sensor configuration here. */\n+\tPixelFormat rawFormat = data_->bestRawFormat();\n+\tconst auto it = maliC55FmtToCode.find(rawFormat);\n+\tsensorFormat_.code = it->second;\n+\n+\tuint16_t distance = std::numeric_limits<uint16_t>::max();\n+\tconst auto sizes = data_->sizes(it->second);\n+\tSize bestSize;\n+\tfor (const auto &size : sizes) {\n+\t\t/* Skip sensor sizes that are smaller than the max YUV size. */\n+\t\tif (maxYuvSize.width > size.width ||\n+\t\t    maxYuvSize.height > size.height)\n+\t\t\tcontinue;\n+\n+\t\tuint16_t dist = std::abs(static_cast<int>(maxYuvSize.width) -\n+\t\t\t\t\t static_cast<int>(size.width)) +\n+\t\t\t\tstd::abs(static_cast<int>(maxYuvSize.height) -\n+\t\t\t\t\t static_cast<int>(size.height));\n+\t\tif (dist < distance) {\n+\t\t\tdist = distance;\n+\t\t\tbestSize = size;\n+\t\t}\n+\t}\n+\tsensorFormat_.size = bestSize;\n+\n+\tLOG(MaliC55, Debug) << \"Computed sensor configuration \" << sensorFormat_;\n+\n+\treturn status;\n+}\n+\n+class PipelineHandlerMaliC55 : public PipelineHandler\n+{\n+public:\n+\tPipelineHandlerMaliC55(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 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 bufferReady(FrameBuffer *buffer);\n+\n+\tbool match(DeviceEnumerator *enumerator) override;\n+\n+private:\n+\tstruct MaliC55Pipe {\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\tMaliC55FR,\n+\t\tMaliC55DS,\n+\t\tMaliC55NumPipes,\n+\t};\n+\n+\tMaliC55CameraData *cameraData(Camera *camera)\n+\t{\n+\t\treturn static_cast<MaliC55CameraData *>(camera->_d());\n+\t}\n+\n+\tMaliC55Pipe *pipeFromStream(MaliC55CameraData *data, Stream *stream)\n+\t{\n+\t\tif (stream == &data->frStream_)\n+\t\t\treturn &pipes_[MaliC55FR];\n+\t\telse if (stream == &data->dsStream_)\n+\t\t\treturn &pipes_[MaliC55DS];\n+\t\telse\n+\t\t\tLOG(MaliC55, Fatal) << \"Stream \" << stream << \" not valid\";\n+\t\treturn nullptr;\n+\t}\n+\n+\tMaliC55Pipe *pipeFromStream(MaliC55CameraData *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 (MaliC55Pipe &pipe : pipes_)\n+\t\t\tpipe.stream = nullptr;\n+\t}\n+\n+\tint configureRawStream(MaliC55CameraData *data,\n+\t\t\t       const StreamConfiguration &config,\n+\t\t\t       V4L2SubdeviceFormat &subdevFormat);\n+\tint configureProcessedStream(MaliC55CameraData *data,\n+\t\t\t\t     const StreamConfiguration &config,\n+\t\t\t\t     V4L2SubdeviceFormat &subdevFormat);\n+\n+\tvoid registerMaliCamera(std::unique_ptr<MaliC55CameraData> data,\n+\t\t\t\tconst std::string &name);\n+\tbool registerTPGCamera(MediaLink *link);\n+\tbool registerSensorCamera(MediaLink *link);\n+\n+\tMediaDevice *media_;\n+\tstd::unique_ptr<V4L2Subdevice> isp_;\n+\n+\tstd::array<MaliC55Pipe, MaliC55NumPipes> pipes_;\n+\n+\tbool dsFitted_;\n+};\n+\n+PipelineHandlerMaliC55::PipelineHandlerMaliC55(CameraManager *manager)\n+\t: PipelineHandler(manager), dsFitted_(true)\n+{\n+}\n+\n+std::unique_ptr<CameraConfiguration>\n+PipelineHandlerMaliC55::generateConfiguration(Camera *camera,\n+\t\t\t\t\t      Span<const StreamRole> roles)\n+{\n+\tMaliC55CameraData *data = cameraData(camera);\n+\tstd::unique_ptr<CameraConfiguration> config =\n+\t\tstd::make_unique<MaliC55CameraConfiguration>(data);\n+\tbool frPipeAvailable = true;\n+\n+\tif (roles.empty())\n+\t\treturn config;\n+\n+\t/* Check if one stream is RAW to reserve the FR pipe for it. */\n+\tif (std::find(roles.begin(), roles.end(), StreamRole::Raw) != roles.end())\n+\t\tfrPipeAvailable = false;\n+\n+\tfor (const StreamRole &role : roles) {\n+\t\tstruct MaliC55Pipe *pipe;\n+\n+\t\t/* Assign pipe for this role. */\n+\t\tif (role == StreamRole::Raw) {\n+\t\t\tpipe = &pipes_[MaliC55FR];\n+\t\t} else {\n+\t\t\tif (frPipeAvailable) {\n+\t\t\t\tpipe = &pipes_[MaliC55FR];\n+\t\t\t\tfrPipeAvailable = false;\n+\t\t\t} else {\n+\t\t\t\tpipe = &pipes_[MaliC55DS];\n+\t\t\t}\n+\t\t}\n+\n+\t\tSize size = std::min(Size{ 1920, 1080 }, data->resolution());\n+\t\tPixelFormat pixelFormat;\n+\n+\t\tswitch (role) {\n+\t\tcase StreamRole::StillCapture:\n+\t\t\tsize = data->resolution();\n+\t\t\t[[fallthrough]];\n+\t\tcase StreamRole::VideoRecording:\n+\t\t\tpixelFormat = formats::NV12;\n+\t\t\tbreak;\n+\n+\t\tcase StreamRole::Viewfinder:\n+\t\t\tpixelFormat = formats::RGB565;\n+\t\t\tbreak;\n+\n+\t\tcase StreamRole::Raw:\n+\t\t\tpixelFormat = data->bestRawFormat();\n+\t\t\tif (!pixelFormat.isValid()) {\n+\t\t\t\tLOG(MaliC55, Error)\n+\t\t\t\t\t<< \"Camera does not support RAW formats\";\n+\t\t\t\treturn nullptr;\n+\t\t\t}\n+\n+\t\t\tsize = data->resolution();\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\tLOG(MaliC55, Error)\n+\t\t\t\t<< \"Requested stream role not supported: \" << role;\n+\t\t\treturn nullptr;\n+\t\t}\n+\n+\t\tstd::map<PixelFormat, std::vector<SizeRange>> formats;\n+\t\tfor (const auto &maliFormat : maliC55FmtToCode) {\n+\t\t\tPixelFormat pixFmt = maliFormat.first;\n+\t\t\tbool isRaw = isFormatRaw(pixFmt);\n+\n+\t\t\t/* RAW formats are only supported on the FR pipe. */\n+\t\t\tif (pipe != &pipes_[MaliC55FR] && isRaw)\n+\t\t\t\tcontinue;\n+\n+\t\t\tif (isRaw) {\n+\t\t\t\t/* Make sure the mbus code is supported. */\n+\t\t\t\tunsigned int rawCode = maliFormat.second;\n+\t\t\t\tconst auto sizes = data->sizes(rawCode);\n+\t\t\t\tif (sizes.empty())\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\t/* And list all sizes the sensor can produce. */\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\t/* Processed formats are always available. */\n+\t\t\t\tSize maxSize = std::min(kMaliC55MaxSize,\n+\t\t\t\t\t\t\tdata->resolution());\n+\t\t\t\tformats[pixFmt] = { kMaliC55MinSize, 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 PipelineHandlerMaliC55::configureRawStream(MaliC55CameraData *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+\tMaliC55Pipe *pipe = pipeFromStream(data, stream);\n+\n+\tif (pipe != &pipes_[MaliC55FR]) {\n+\t\tLOG(MaliC55, Fatal) << \"Only the FR pipe supports RAW capture.\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/* Enable the debayer route to set fixed internal format on pad #0. */\n+\tV4L2Subdevice::Routing routing = {};\n+\trouting.emplace_back(V4L2Subdevice::Stream{ 0, 0 },\n+\t\t\t     V4L2Subdevice::Stream{ 1, 0 },\n+\t\t\t     V4L2_SUBDEV_ROUTE_FL_ACTIVE);\n+\n+\tint ret = pipe->resizer->setRouting(&routing, V4L2Subdevice::ActiveFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tunsigned int rawCode = subdevFormat.code;\n+\tsubdevFormat.code = kMaliC55ISPInternalFormat;\n+\tret = pipe->resizer->setFormat(0, &subdevFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Enable the bypass route and apply RAW formats there. */\n+\trouting.clear();\n+\trouting.emplace_back(V4L2Subdevice::Stream{ 2, 0 },\n+\t\t\t     V4L2Subdevice::Stream{ 1, 0 },\n+\t\t\t     V4L2_SUBDEV_ROUTE_FL_ACTIVE);\n+\tret = pipe->resizer->setRouting(&routing, V4L2Subdevice::ActiveFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tsubdevFormat.code = rawCode;\n+\tret = pipe->resizer->setFormat(2, &subdevFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = pipe->resizer->setFormat(1, &subdevFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn 0;\n+}\n+\n+int PipelineHandlerMaliC55::configureProcessedStream(MaliC55CameraData *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+\tMaliC55Pipe *pipe = pipeFromStream(data, stream);\n+\n+\t/* Enable the debayer route on the resizer pipe. */\n+\tV4L2Subdevice::Routing routing = {};\n+\trouting.emplace_back(V4L2Subdevice::Stream{ 0, 0 },\n+\t\t\t     V4L2Subdevice::Stream{ 1, 0 },\n+\t\t\t     V4L2_SUBDEV_ROUTE_FL_ACTIVE);\n+\n+\tint ret = pipe->resizer->setRouting(&routing, V4L2Subdevice::ActiveFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tsubdevFormat.code = kMaliC55ISPInternalFormat;\n+\tret = pipe->resizer->setFormat(0, &subdevFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* \\todo Configure the resizer crop/compose rectangles. */\n+\tRectangle ispCrop = { 0, 0, config.size };\n+\tret = pipe->resizer->setSelection(0, V4L2_SEL_TGT_CROP, &ispCrop);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = pipe->resizer->setSelection(0, V4L2_SEL_TGT_COMPOSE, &ispCrop);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tsubdevFormat.code = maliC55FmtToCode.find(config.pixelFormat)->second;\n+\treturn pipe->resizer->setFormat(1, &subdevFormat);\n+}\n+\n+int PipelineHandlerMaliC55::configure(Camera *camera,\n+\t\t\t\t      CameraConfiguration *config)\n+{\n+\tresetPipes();\n+\n+\tint ret = media_->disableLinks();\n+\tif (ret)\n+\t\treturn ret;\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+\t\tconst MediaEntity *csiEntity = data->csi_->entity();\n+\t\tret = csiEntity->getPadByIndex(1)->links()[0]->setEnabled(true);\n+\t} else {\n+\t\tret = data->entity_->getPadByIndex(0)->links()[0]->setEnabled(true);\n+\t}\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tMaliC55CameraConfiguration *maliConfig =\n+\t\tstatic_cast<MaliC55CameraConfiguration *>(config);\n+\tV4L2SubdeviceFormat subdevFormat = maliConfig->sensorFormat_;\n+\tret = data->sd_->getFormat(0, &subdevFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\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_->setFormat(1, &subdevFormat);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\t/*\n+\t * Propagate the format to the ISP sink pad and configure the input\n+\t * crop rectangle (no crop at the moment).\n+\t *\n+\t * \\todo Configure the CSI-2 receiver.\n+\t */\n+\tret = isp_->setFormat(0, &subdevFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tRectangle ispCrop(0, 0, subdevFormat.size);\n+\tret = isp_->setSelection(0, V4L2_SEL_TGT_CROP, &ispCrop);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/*\n+\t * Configure the resizer: fixed format the sink pad; use the media\n+\t * bus code associated with the desired capture format on the source\n+\t * pad.\n+\t *\n+\t * Configure the crop and compose rectangles to match the desired\n+\t * stream output size\n+\t *\n+\t * \\todo Make the crop/scaler configurable\n+\t */\n+\tfor (const StreamConfiguration &streamConfig : *config) {\n+\t\tStream *stream = streamConfig.stream();\n+\t\tMaliC55Pipe *pipe = pipeFromStream(data, stream);\n+\n+\t\tif (isFormatRaw(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(MaliC55, Error) << \"Failed to configure pipeline\";\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\t/* Now apply the pixel format and size to the capture device. */\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+\treturn 0;\n+}\n+\n+int PipelineHandlerMaliC55::exportFrameBuffers(Camera *camera, Stream *stream,\n+\t\t\t\t\t       std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n+{\n+\tMaliC55Pipe *pipe = pipeFromStream(cameraData(camera), stream);\n+\tunsigned int count = stream->configuration().bufferCount;\n+\n+\treturn pipe->cap->exportBuffers(count, buffers);\n+}\n+\n+int PipelineHandlerMaliC55::start([[maybe_unused]] Camera *camera, [[maybe_unused]] const ControlList *controls)\n+{\n+\tfor (MaliC55Pipe &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(MaliC55, 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(MaliC55, Error) << \"Failed to start stream\";\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+void PipelineHandlerMaliC55::stopDevice([[maybe_unused]] Camera *camera)\n+{\n+\tfor (MaliC55Pipe &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 PipelineHandlerMaliC55::queueRequestDevice(Camera *camera, Request *request)\n+{\n+\tint ret;\n+\n+\tfor (auto &[stream, buffer] : request->buffers()) {\n+\t\tMaliC55Pipe *pipe = pipeFromStream(cameraData(camera), stream);\n+\n+\t\tret = pipe->cap->queueBuffer(buffer);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+void PipelineHandlerMaliC55::bufferReady(FrameBuffer *buffer)\n+{\n+\tRequest *request = buffer->request();\n+\n+\tcompleteBuffer(request, buffer);\n+\n+\tif (request->hasPendingBuffers())\n+\t\treturn;\n+\n+\tcompleteRequest(request);\n+}\n+\n+void PipelineHandlerMaliC55::registerMaliCamera(std::unique_ptr<MaliC55CameraData> data,\n+\t\t\t\t\t\tconst std::string &name)\n+{\n+\tstd::set<Stream *> streams{ &data->frStream_ };\n+\tif (dsFitted_)\n+\t\tstreams.insert(&data->dsStream_);\n+\n+\tstd::shared_ptr<Camera> camera = Camera::create(std::move(data),\n+\t\t\t\t\t\t\tname, streams);\n+\tregisterCamera(std::move(camera));\n+}\n+\n+/*\n+ * The only camera we support through direct connection to the ISP is the\n+ * Mali-C55 TPG. Check we have that and warn if not.\n+ */\n+bool PipelineHandlerMaliC55::registerTPGCamera(MediaLink *link)\n+{\n+\tconst std::string &name = link->source()->entity()->name();\n+\tif (name != \"mali-c55 tpg\") {\n+\t\tLOG(MaliC55, Warning) << \"Unsupported direct connection to \"\n+\t\t\t\t      << link->source()->entity()->name();\n+\t\t/*\n+\t\t * Return true and just skip registering a camera for this\n+\t\t * entity.\n+\t\t */\n+\t\treturn true;\n+\t}\n+\n+\tstd::unique_ptr<MaliC55CameraData> data =\n+\t\tstd::make_unique<MaliC55CameraData>(this, link->source()->entity());\n+\n+\tif (data->init())\n+\t\treturn false;\n+\n+\tregisterMaliCamera(std::move(data), name);\n+\n+\treturn true;\n+}\n+\n+/*\n+ * Register a Camera for each sensor connected to the ISP through a CSI-2\n+ * receiver.\n+ *\n+ * \\todo Support more complex topologies, such as video muxes.\n+ */\n+bool PipelineHandlerMaliC55::registerSensorCamera(MediaLink *ispLink)\n+{\n+\tMediaEntity *csi2 = ispLink->source()->entity();\n+\tconst MediaPad *csi2Sink = csi2->getPadByIndex(0);\n+\n+\tfor (MediaLink *link : csi2Sink->links()) {\n+\t\tMediaEntity *sensor = link->source()->entity();\n+\t\tunsigned int function = sensor->function();\n+\n+\t\tif (function != MEDIA_ENT_F_CAM_SENSOR)\n+\t\t\tcontinue;\n+\n+\t\tstd::unique_ptr<MaliC55CameraData> data =\n+\t\t\tstd::make_unique<MaliC55CameraData>(this, sensor);\n+\t\tif (data->init())\n+\t\t\treturn false;\n+\n+\t\t/* \\todo: Init properties and controls. */\n+\n+\t\tregisterMaliCamera(std::move(data), sensor->name());\n+\t}\n+\n+\treturn true;\n+}\n+\n+bool PipelineHandlerMaliC55::match(DeviceEnumerator *enumerator)\n+{\n+\tconst MediaPad *ispSink;\n+\n+\t/*\n+\t * We search for just the ISP subdevice and the full resolution pipe.\n+\t * The TPG and the downscale pipe are both optional blocks and may not\n+\t * be fitted.\n+\t */\n+\tDeviceMatch dm(\"mali-c55\");\n+\tdm.add(\"mali-c55 isp\");\n+\tdm.add(\"mali-c55 resizer fr\");\n+\tdm.add(\"mali-c55 fr\");\n+\n+\tmedia_ = acquireMediaDevice(enumerator, dm);\n+\tif (!media_)\n+\t\treturn false;\n+\n+\tisp_ = V4L2Subdevice::fromEntityName(media_, \"mali-c55 isp\");\n+\tif (isp_->open() < 0)\n+\t\treturn false;\n+\n+\tMaliC55Pipe *frPipe = &pipes_[MaliC55FR];\n+\tfrPipe->resizer = V4L2Subdevice::fromEntityName(media_, \"mali-c55 resizer fr\");\n+\tif (frPipe->resizer->open() < 0)\n+\t\treturn false;\n+\n+\tfrPipe->cap = V4L2VideoDevice::fromEntityName(media_, \"mali-c55 fr\");\n+\tif (frPipe->cap->open() < 0)\n+\t\treturn false;\n+\n+\tfrPipe->cap->bufferReady.connect(this, &PipelineHandlerMaliC55::bufferReady);\n+\n+\tdsFitted_ = !!media_->getEntityByName(\"mali-c55 ds\");\n+\tif (dsFitted_) {\n+\t\tLOG(MaliC55, Debug) << \"Downscaler pipe is fitted\";\n+\n+\t\tMaliC55Pipe *dsPipe = &pipes_[MaliC55DS];\n+\n+\t\tdsPipe->resizer = V4L2Subdevice::fromEntityName(media_, \"mali-c55 resizer ds\");\n+\t\tif (dsPipe->resizer->open() < 0)\n+\t\t\treturn false;\n+\n+\t\tdsPipe->cap = V4L2VideoDevice::fromEntityName(media_, \"mali-c55 ds\");\n+\t\tif (dsPipe->cap->open() < 0)\n+\t\t\treturn false;\n+\n+\t\tdsPipe->cap->bufferReady.connect(this, &PipelineHandlerMaliC55::bufferReady);\n+\t}\n+\n+\tispSink = isp_->entity()->getPadByIndex(0);\n+\tif (!ispSink || ispSink->links().empty()) {\n+\t\tLOG(MaliC55, Error) << \"ISP sink pad error\";\n+\t\treturn false;\n+\t}\n+\n+\t/*\n+\t * We could have several links pointing to the ISP's sink pad, which\n+\t * will be from entities with one of the following functions:\n+\t *\n+\t * MEDIA_ENT_F_CAM_SENSOR - The test pattern generator\n+\t * MEDIA_ENT_F_VID_IF_BRIDGE - A CSI-2 receiver\n+\t * MEDIA_ENT_F_IO_V4L - An input device\n+\t *\n+\t * The last one will be unsupported for now. The TPG is relatively easy,\n+\t * we just register a Camera for it. If we have a CSI-2 receiver we need\n+\t * to check its sink pad and register Cameras for anything connected to\n+\t * it (probably...there are some complex situations in which that might\n+\t * not be true but let's pretend they don't exist until we come across\n+\t * them)\n+\t */\n+\tbool registered;\n+\tfor (MediaLink *link : ispSink->links()) {\n+\t\tunsigned int function = link->source()->entity()->function();\n+\n+\t\tswitch (function) {\n+\t\tcase MEDIA_ENT_F_CAM_SENSOR:\n+\t\t\tregistered = registerTPGCamera(link);\n+\t\t\tif (!registered)\n+\t\t\t\treturn registered;\n+\n+\t\t\tbreak;\n+\t\tcase MEDIA_ENT_F_VID_IF_BRIDGE:\n+\t\t\tregistered = registerSensorCamera(link);\n+\t\t\tif (!registered)\n+\t\t\t\treturn registered;\n+\n+\t\t\tbreak;\n+\t\tcase MEDIA_ENT_F_IO_V4L:\n+\t\t\tLOG(MaliC55, Warning) << \"Memory input not yet supported\";\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tLOG(MaliC55, Error) << \"Unsupported entity function\";\n+\t\t\treturn false;\n+\t\t}\n+\t}\n+\n+\treturn true;\n+}\n+\n+REGISTER_PIPELINE_HANDLER(PipelineHandlerMaliC55)\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/pipeline/mali-c55/meson.build b/src/libcamera/pipeline/mali-c55/meson.build\nnew file mode 100644\nindex 000000000000..30fd29b928d5\n--- /dev/null\n+++ b/src/libcamera/pipeline/mali-c55/meson.build\n@@ -0,0 +1,5 @@\n+# SPDX-License-Identifier: CC0-1.0\n+\n+libcamera_sources += files([\n+    'mali-c55.cpp'\n+])\n",
    "prefixes": [
        "v2"
    ]
}