Patch Detail
Show a patch.
GET /api/patches/21871/?format=api
{ "id": 21871, "url": "https://patchwork.libcamera.org/api/patches/21871/?format=api", "web_url": "https://patchwork.libcamera.org/patch/21871/", "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": "<20241108105117.137121-6-jacopo.mondi@ideasonboard.com>", "date": "2024-11-08T10:51:15", "name": "[v2,5/6] libcamera: Add CameraSensor implementation for raw V4L2 sensors", "commit_ref": null, "pull_url": null, "state": "accepted", "archived": false, "hash": "f0f14588f732baeaa86e5019d2ef7b03d41ce95e", "submitter": { "id": 143, "url": "https://patchwork.libcamera.org/api/people/143/?format=api", "name": "Jacopo Mondi", "email": "jacopo.mondi@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/21871/mbox/", "series": [ { "id": 4783, "url": "https://patchwork.libcamera.org/api/series/4783/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4783", "date": "2024-11-08T10:51:10", "name": "libcamera: Initial support for generic line based metadata", "version": 2, "mbox": "https://patchwork.libcamera.org/series/4783/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/21871/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/21871/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 0096DC32A9\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 8 Nov 2024 10:51:36 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 97C66657B4;\n\tFri, 8 Nov 2024 11:51:35 +0100 (CET)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2C8A1654A1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 8 Nov 2024 11:51:27 +0100 (CET)", "from ideasonboard.com (93-61-96-190.ip145.fastwebnet.it\n\t[93.61.96.190])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 28327353;\n\tFri, 8 Nov 2024 11:51:17 +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=\"oBiPQ4bv\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1731063077;\n\tbh=6h1yEp6aQVkBEMxGMDPPOY+Z9mmfD6+Q/iTEzRq15zA=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=oBiPQ4bv+2LdjhnvvvZldzj1IURCT18Xv9V+1rfDHgVJhSQfZU1Jf3LrB1zqv/hct\n\tMyGqeFVqiBSLpZsog9Jz2Y9/eVJ/Jx0b8eAN6AEGbPOIlXagC/2aT3RhXdmtfD9gfb\n\tfR6LLeA0xOZ4UL9FwbrGO9SzYNCcFqlFstouMmSk=", "From": "Jacopo Mondi <jacopo.mondi@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Cc": "Jacopo Mondi <jacopo.mondi@ideasonboard.com>, naush@raspberrypi.com,\n\tLaurent Pinchart <laurent.pinchart@ideasonboard.com>", "Subject": "[PATCH v2 5/6] libcamera: Add CameraSensor implementation for raw\n\tV4L2 sensors", "Date": "Fri, 8 Nov 2024 11:51:15 +0100", "Message-ID": "<20241108105117.137121-6-jacopo.mondi@ideasonboard.com>", "X-Mailer": "git-send-email 2.47.0", "In-Reply-To": "<20241108105117.137121-1-jacopo.mondi@ideasonboard.com>", "References": "<20241108105117.137121-1-jacopo.mondi@ideasonboard.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "X-BeenThere": "libcamera-devel@lists.libcamera.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "<libcamera-devel.lists.libcamera.org>", "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>", "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>", "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>", "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>", "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\nAdd a new CameraSensorRaw implementation of the CameraSensor interface\ntailored to devices that implement the new V4L2 raw camera sensors API.\n\nThis new class duplicates code from the CameraSensorLegacy class. The\ntwo classes will be refactored to share code.\n\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\nSigned-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n---\nChanges since combined RFC:\n\n- Use std::abs() from cmath instead that fabs() from math.h\n- Set factory priority explicitly\n- Set flipsAlterBayerOrder_\n- Fix build warning due to missing default case in cfa switch\n- Check for read-only hblank using V4L2_CTRL_FLAG_READ_ONLY\n---\n Documentation/Doxyfile-internal.in | 1 +\n src/libcamera/sensor/camera_sensor_raw.cpp | 1055 ++++++++++++++++++++\n src/libcamera/sensor/meson.build | 1 +\n 3 files changed, 1057 insertions(+)\n create mode 100644 src/libcamera/sensor/camera_sensor_raw.cpp", "diff": "diff --git a/Documentation/Doxyfile-internal.in b/Documentation/Doxyfile-internal.in\nindex b5ad7e7ff6c7..5343bc2b131c 100644\n--- a/Documentation/Doxyfile-internal.in\n+++ b/Documentation/Doxyfile-internal.in\n@@ -26,6 +26,7 @@ EXCLUDE = @TOP_SRCDIR@/include/libcamera/base/span.h \\\n @TOP_SRCDIR@/src/libcamera/ipc_pipe_unixsocket.cpp \\\n @TOP_SRCDIR@/src/libcamera/pipeline/ \\\n @TOP_SRCDIR@/src/libcamera/sensor/camera_sensor_legacy.cpp \\\n+ @TOP_SRCDIR@/src/libcamera/sensor/camera_sensor_raw.cpp \\\n @TOP_SRCDIR@/src/libcamera/tracepoints.cpp \\\n @TOP_BUILDDIR@/include/libcamera/internal/tracepoints.h \\\n @TOP_BUILDDIR@/include/libcamera/ipa/soft_ipa_interface.h \\\ndiff --git a/src/libcamera/sensor/camera_sensor_raw.cpp b/src/libcamera/sensor/camera_sensor_raw.cpp\nnew file mode 100644\nindex 000000000000..4c653121d547\n--- /dev/null\n+++ b/src/libcamera/sensor/camera_sensor_raw.cpp\n@@ -0,0 +1,1055 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Ideas on Board Oy.\n+ *\n+ * camera_sensor_raw.cpp - A raw camera sensor using the V4L2 streams API\n+ */\n+\n+#include <algorithm>\n+#include <cmath>\n+#include <float.h>\n+#include <iomanip>\n+#include <limits.h>\n+#include <map>\n+#include <memory>\n+#include <optional>\n+#include <string.h>\n+#include <string>\n+#include <vector>\n+\n+#include <libcamera/base/class.h>\n+#include <libcamera/base/log.h>\n+#include <libcamera/base/utils.h>\n+\n+#include <libcamera/camera.h>\n+#include <libcamera/control_ids.h>\n+#include <libcamera/controls.h>\n+#include <libcamera/geometry.h>\n+#include <libcamera/orientation.h>\n+#include <libcamera/property_ids.h>\n+#include <libcamera/transform.h>\n+\n+#include <libcamera/ipa/core_ipa_interface.h>\n+\n+#include \"libcamera/internal/bayer_format.h\"\n+#include \"libcamera/internal/camera_lens.h\"\n+#include \"libcamera/internal/camera_sensor.h\"\n+#include \"libcamera/internal/camera_sensor_properties.h\"\n+#include \"libcamera/internal/formats.h\"\n+#include \"libcamera/internal/media_device.h\"\n+#include \"libcamera/internal/sysfs.h\"\n+#include \"libcamera/internal/v4l2_subdevice.h\"\n+\n+namespace libcamera {\n+\n+class BayerFormat;\n+class CameraLens;\n+class MediaEntity;\n+class SensorConfiguration;\n+\n+struct CameraSensorProperties;\n+\n+enum class Orientation;\n+\n+LOG_DECLARE_CATEGORY(CameraSensor)\n+\n+class CameraSensorRaw : public CameraSensor, protected Loggable\n+{\n+public:\n+\tCameraSensorRaw(const MediaEntity *entity);\n+\t~CameraSensorRaw();\n+\n+\tstatic std::variant<std::unique_ptr<CameraSensor>, int>\n+\tmatch(MediaEntity *entity);\n+\n+\tconst std::string &model() const override { return model_; }\n+\tconst std::string &id() const override { return id_; }\n+\n+\tconst MediaEntity *entity() const override { return entity_; }\n+\tV4L2Subdevice *device() override { return subdev_.get(); }\n+\n+\tCameraLens *focusLens() override { return focusLens_.get(); }\n+\n+\tconst std::vector<unsigned int> &mbusCodes() const override { return mbusCodes_; }\n+\tstd::vector<Size> sizes(unsigned int mbusCode) const override;\n+\tSize resolution() const override;\n+\n+\tV4L2SubdeviceFormat getFormat(const std::vector<unsigned int> &mbusCodes,\n+\t\t\t\t const Size &size) const override;\n+\tint setFormat(V4L2SubdeviceFormat *format,\n+\t\t Transform transform = Transform::Identity) override;\n+\tint tryFormat(V4L2SubdeviceFormat *format) const override;\n+\n+\tint applyConfiguration(const SensorConfiguration &config,\n+\t\t\t Transform transform = Transform::Identity,\n+\t\t\t V4L2SubdeviceFormat *sensorFormat = nullptr) override;\n+\n+\tconst ControlList &properties() const override { return properties_; }\n+\tint sensorInfo(IPACameraSensorInfo *info) const override;\n+\tTransform computeTransform(Orientation *orientation) const override;\n+\tBayerFormat::Order bayerOrder(Transform t) const override;\n+\n+\tconst ControlInfoMap &controls() const override;\n+\tControlList getControls(const std::vector<uint32_t> &ids) override;\n+\tint setControls(ControlList *ctrls) override;\n+\n+\tconst std::vector<controls::draft::TestPatternModeEnum> &\n+\ttestPatternModes() const override { return testPatternModes_; }\n+\tint setTestPatternMode(controls::draft::TestPatternModeEnum mode) override;\n+\n+protected:\n+\tstd::string logPrefix() const override;\n+\n+private:\n+\tLIBCAMERA_DISABLE_COPY(CameraSensorRaw)\n+\n+\tstd::optional<int> init();\n+\tint initProperties();\n+\tvoid initStaticProperties();\n+\tvoid initTestPatternModes();\n+\tint applyTestPatternMode(controls::draft::TestPatternModeEnum mode);\n+\n+\tconst MediaEntity *entity_;\n+\tstd::unique_ptr<V4L2Subdevice> subdev_;\n+\n+\tstruct Streams {\n+\t\tV4L2Subdevice::Stream sink;\n+\t\tV4L2Subdevice::Stream source;\n+\t};\n+\n+\tstruct {\n+\t\tStreams image;\n+\t\tstd::optional<Streams> edata;\n+\t} streams_;\n+\n+\tconst CameraSensorProperties *staticProps_;\n+\n+\tstd::string model_;\n+\tstd::string id_;\n+\n+\tV4L2Subdevice::Formats formats_;\n+\tstd::vector<unsigned int> mbusCodes_;\n+\tstd::vector<Size> sizes_;\n+\tstd::vector<controls::draft::TestPatternModeEnum> testPatternModes_;\n+\tcontrols::draft::TestPatternModeEnum testPatternMode_;\n+\n+\tSize pixelArraySize_;\n+\tRectangle activeArea_;\n+\tBayerFormat::Order cfaPattern_;\n+\tbool supportFlips_;\n+\tbool flipsAlterBayerOrder_;\n+\tOrientation mountingOrientation_;\n+\n+\tControlList properties_;\n+\n+\tstd::unique_ptr<CameraLens> focusLens_;\n+};\n+\n+/**\n+ * \\class CameraSensorRaw\n+ * \\brief A camera sensor based on V4L2 subdevices\n+ *\n+ * This class supports single-subdev sensors with a single source pad and one\n+ * or two internal sink pads (for the image and embedded data streams).\n+ */\n+\n+CameraSensorRaw::CameraSensorRaw(const MediaEntity *entity)\n+\t: entity_(entity), staticProps_(nullptr), supportFlips_(false),\n+\t flipsAlterBayerOrder_(false), properties_(properties::properties)\n+{\n+}\n+\n+CameraSensorRaw::~CameraSensorRaw() = default;\n+\n+std::variant<std::unique_ptr<CameraSensor>, int>\n+CameraSensorRaw::match(MediaEntity *entity)\n+{\n+\t/* Check the entity type. */\n+\tif (entity->type() != MediaEntity::Type::V4L2Subdevice ||\n+\t entity->function() != MEDIA_ENT_F_CAM_SENSOR) {\n+\t\tlibcamera::LOG(CameraSensor, Debug)\n+\t\t\t<< entity->name() << \": unsupported entity type (\"\n+\t\t\t<< utils::to_underlying(entity->type())\n+\t\t\t<< \") or function (\" << utils::hex(entity->function()) << \")\";\n+\t\treturn { 0 };\n+\t}\n+\n+\t/* Count and check the number of pads. */\n+\tstatic constexpr uint32_t kPadFlagsMask = MEDIA_PAD_FL_SINK\n+\t\t\t\t\t\t| MEDIA_PAD_FL_SOURCE\n+\t\t\t\t\t\t| MEDIA_PAD_FL_INTERNAL;\n+\tunsigned int numSinks = 0;\n+\tunsigned int numSources = 0;\n+\n+\tfor (const MediaPad *pad : entity->pads()) {\n+\t\tswitch (pad->flags() & kPadFlagsMask) {\n+\t\tcase MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_INTERNAL:\n+\t\t\tnumSinks++;\n+\t\t\tbreak;\n+\n+\t\tcase MEDIA_PAD_FL_SOURCE:\n+\t\t\tnumSources++;\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\tlibcamera::LOG(CameraSensor, Debug)\n+\t\t\t\t<< entity->name() << \": unsupported pad \" << pad->index()\n+\t\t\t\t<< \" type \" << utils::hex(pad->flags());\n+\t\t\treturn { 0 };\n+\t\t}\n+\t}\n+\n+\tif (numSinks < 1 || numSinks > 2 || numSources != 1) {\n+\t\tlibcamera::LOG(CameraSensor, Debug)\n+\t\t\t<< entity->name() << \": unsupported number of sinks (\"\n+\t\t\t<< numSinks << \") or sources (\" << numSources << \")\";\n+\t\treturn { 0 };\n+\t}\n+\n+\t/*\n+\t * The entity matches. Create the camera sensor and initialize it. The\n+\t * init() function will perform further match checks.\n+\t */\n+\tstd::unique_ptr<CameraSensorRaw> sensor =\n+\t\tstd::make_unique<CameraSensorRaw>(entity);\n+\n+\tstd::optional<int> err = sensor->init();\n+\tif (err)\n+\t\treturn { *err };\n+\n+\treturn { std::move(sensor) };\n+}\n+\n+std::optional<int> CameraSensorRaw::init()\n+{\n+\t/* Create and open the subdev. */\n+\tsubdev_ = std::make_unique<V4L2Subdevice>(entity_);\n+\tint ret = subdev_->open();\n+\tif (ret)\n+\t\treturn { ret };\n+\n+\t/*\n+\t * 1. Identify the pads.\n+\t */\n+\n+\t/*\n+\t * First locate the source pad. The match() function guarantees there\n+\t * is one and only one source pad.\n+\t */\n+\tunsigned int sourcePad = UINT_MAX;\n+\n+\tfor (const MediaPad *pad : entity_->pads()) {\n+\t\tif (pad->flags() & MEDIA_PAD_FL_SOURCE) {\n+\t\t\tsourcePad = pad->index();\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * Iterate over the routes to identify the streams on the source pad,\n+\t * and the internal sink pads.\n+\t */\n+\tV4L2Subdevice::Routing routing = {};\n+\tret = subdev_->getRouting(&routing, V4L2Subdevice::TryFormat);\n+\tif (ret)\n+\t\treturn { ret };\n+\n+\tbool imageStreamFound = false;\n+\n+\tfor (const V4L2Subdevice::Route &route : routing) {\n+\t\tif (route.source.pad != sourcePad) {\n+\t\t\tLOG(CameraSensor, Error) << \"Invalid route \" << route;\n+\t\t\treturn { -EINVAL };\n+\t\t}\n+\n+\t\t/* Identify the stream type based on the supported formats. */\n+\t\tV4L2Subdevice::Formats formats = subdev_->formats(route.source);\n+\n+\t\tstd::optional<MediaBusFormatInfo::Type> type;\n+\n+\t\tfor (const auto &[code, sizes] : formats) {\n+\t\t\tconst MediaBusFormatInfo &info =\n+\t\t\t\tMediaBusFormatInfo::info(code);\n+\t\t\tif (info.isValid()) {\n+\t\t\t\ttype = info.type;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (!type) {\n+\t\t\tLOG(CameraSensor, Warning)\n+\t\t\t\t<< \"No known format on pad \" << route.source;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tswitch (*type) {\n+\t\tcase MediaBusFormatInfo::Type::Image:\n+\t\t\tif (imageStreamFound) {\n+\t\t\t\tLOG(CameraSensor, Error)\n+\t\t\t\t\t<< \"Multiple internal image streams (\"\n+\t\t\t\t\t<< streams_.image.sink << \" and \"\n+\t\t\t\t\t<< route.sink << \")\";\n+\t\t\t\treturn { -EINVAL };\n+\t\t\t}\n+\n+\t\t\timageStreamFound = true;\n+\t\t\tstreams_.image.sink = route.sink;\n+\t\t\tstreams_.image.source = route.source;\n+\t\t\tbreak;\n+\n+\t\tcase MediaBusFormatInfo::Type::Metadata:\n+\t\t\t/*\n+\t\t\t * Skip metadata streams that are not sensor embedded\n+\t\t\t * data. The source stream reports a generic metadata\n+\t\t\t * format, check the sink stream for the exact format.\n+\t\t\t */\n+\t\t\tformats = subdev_->formats(route.sink);\n+\t\t\tif (formats.size() != 1)\n+\t\t\t\tcontinue;\n+\n+\t\t\tif (MediaBusFormatInfo::info(formats.cbegin()->first).type !=\n+\t\t\t MediaBusFormatInfo::Type::EmbeddedData)\n+\t\t\t\tcontinue;\n+\n+\t\t\tif (streams_.edata) {\n+\t\t\t\tLOG(CameraSensor, Error)\n+\t\t\t\t\t<< \"Multiple internal embedded data streams (\"\n+\t\t\t\t\t<< streams_.edata->sink << \" and \"\n+\t\t\t\t\t<< route.sink << \")\";\n+\t\t\t\treturn { -EINVAL };\n+\t\t\t}\n+\n+\t\t\tstreams_.edata = { route.sink, route.source };\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (!imageStreamFound) {\n+\t\tLOG(CameraSensor, Error) << \"No image stream found\";\n+\t\treturn { -EINVAL };\n+\t}\n+\n+\tLOG(CameraSensor, Debug)\n+\t\t<< \"Found image stream \" << streams_.image.sink\n+\t\t<< \" -> \" << streams_.image.source;\n+\n+\tif (streams_.edata)\n+\t\tLOG(CameraSensor, Debug)\n+\t\t\t<< \"Found embedded data stream \" << streams_.edata->sink\n+\t\t\t<< \" -> \" << streams_.edata->source;\n+\n+\t/*\n+\t * 2. Enumerate and cache the media bus codes, sizes and colour filter\n+\t * array order for the image stream.\n+\t */\n+\n+\t/*\n+\t * Get the native sensor CFA pattern. It is simpler to retrieve it from\n+\t * the internal image sink pad as it is guaranteed to expose a single\n+\t * format, and is not affected by flips.\n+\t */\n+\tV4L2Subdevice::Formats formats = subdev_->formats(streams_.image.sink);\n+\tif (formats.size() != 1) {\n+\t\tLOG(CameraSensor, Error)\n+\t\t\t<< \"Image pad has \" << formats.size()\n+\t\t\t<< \" formats, expected 1\";\n+\t\treturn { -EINVAL };\n+\t}\n+\n+\tuint32_t nativeFormat = formats.cbegin()->first;\n+\tconst BayerFormat &bayerFormat = BayerFormat::fromMbusCode(nativeFormat);\n+\tif (!bayerFormat.isValid()) {\n+\t\tLOG(CameraSensor, Error)\n+\t\t\t<< \"Invalid native format \" << nativeFormat;\n+\t\treturn { 0 };\n+\t}\n+\n+\tcfaPattern_ = bayerFormat.order;\n+\n+\t/*\n+\t * Retrieve and cache the media bus codes and sizes on the source image\n+\t * stream.\n+\t */\n+\tformats_ = subdev_->formats(streams_.image.source);\n+\tif (formats_.empty()) {\n+\t\tLOG(CameraSensor, Error) << \"No image format found\";\n+\t\treturn { -EINVAL };\n+\t}\n+\n+\t/* Populate and sort the media bus codes and the sizes. */\n+\tfor (const auto &[code, ranges] : formats_) {\n+\t\t/* Drop non-raw formats (in case we have a hybrid sensor). */\n+\t\tconst MediaBusFormatInfo &info = MediaBusFormatInfo::info(code);\n+\t\tif (info.colourEncoding != PixelFormatInfo::ColourEncodingRAW)\n+\t\t\tcontinue;\n+\n+\t\tmbusCodes_.push_back(code);\n+\t\tstd::transform(ranges.begin(), ranges.end(), std::back_inserter(sizes_),\n+\t\t\t [](const SizeRange &range) { return range.max; });\n+\t}\n+\n+\tif (mbusCodes_.empty()) {\n+\t\tLOG(CameraSensor, Debug) << \"No raw image formats found\";\n+\t\treturn { 0 };\n+\t}\n+\n+\tstd::sort(mbusCodes_.begin(), mbusCodes_.end());\n+\tstd::sort(sizes_.begin(), sizes_.end());\n+\n+\t/*\n+\t * Remove duplicate sizes. There are no duplicate media bus codes as\n+\t * they are the keys in the formats map.\n+\t */\n+\tauto last = std::unique(sizes_.begin(), sizes_.end());\n+\tsizes_.erase(last, sizes_.end());\n+\n+\t/*\n+\t * 3. Query selection rectangles. Retrieve properties, and verify that\n+\t * all the expected selection rectangles are supported.\n+\t */\n+\n+\tRectangle rect;\n+\tret = subdev_->getSelection(streams_.image.sink, V4L2_SEL_TGT_CROP_BOUNDS,\n+\t\t\t\t &rect);\n+\tif (ret) {\n+\t\tLOG(CameraSensor, Error) << \"No pixel array crop bounds\";\n+\t\treturn { ret };\n+\t}\n+\n+\tpixelArraySize_ = rect.size();\n+\n+\tret = subdev_->getSelection(streams_.image.sink, V4L2_SEL_TGT_CROP_DEFAULT,\n+\t\t\t\t &activeArea_);\n+\tif (ret) {\n+\t\tLOG(CameraSensor, Error) << \"No pixel array crop default\";\n+\t\treturn { ret };\n+\t}\n+\n+\tret = subdev_->getSelection(streams_.image.sink, V4L2_SEL_TGT_CROP,\n+\t\t\t\t &rect);\n+\tif (ret) {\n+\t\tLOG(CameraSensor, Error) << \"No pixel array crop rectangle\";\n+\t\treturn { ret };\n+\t}\n+\n+\t/*\n+\t * 4. Verify that all required controls are present.\n+\t */\n+\n+\tconst ControlIdMap &controls = subdev_->controls().idmap();\n+\n+\tstatic constexpr uint32_t mandatoryControls[] = {\n+\t\tV4L2_CID_ANALOGUE_GAIN,\n+\t\tV4L2_CID_CAMERA_ORIENTATION,\n+\t\tV4L2_CID_EXPOSURE,\n+\t\tV4L2_CID_HBLANK,\n+\t\tV4L2_CID_PIXEL_RATE,\n+\t\tV4L2_CID_VBLANK,\n+\t};\n+\n+\tret = 0;\n+\n+\tfor (uint32_t ctrl : mandatoryControls) {\n+\t\tif (!controls.count(ctrl)) {\n+\t\t\tLOG(CameraSensor, Error)\n+\t\t\t\t<< \"Mandatory V4L2 control \" << utils::hex(ctrl)\n+\t\t\t\t<< \" not available\";\n+\t\t\tret = -EINVAL;\n+\t\t}\n+\t}\n+\n+\tif (ret) {\n+\t\tLOG(CameraSensor, Error)\n+\t\t\t<< \"The sensor kernel driver needs to be fixed\";\n+\t\tLOG(CameraSensor, Error)\n+\t\t\t<< \"See Documentation/sensor_driver_requirements.rst in the libcamera sources for more information\";\n+\t\treturn { ret };\n+\t}\n+\n+\t/*\n+\t * Verify if sensor supports horizontal/vertical flips\n+\t *\n+\t * \\todo Handle horizontal and vertical flips independently.\n+\t */\n+\tconst struct v4l2_query_ext_ctrl *hflipInfo = subdev_->controlInfo(V4L2_CID_HFLIP);\n+\tconst struct v4l2_query_ext_ctrl *vflipInfo = subdev_->controlInfo(V4L2_CID_VFLIP);\n+\tif (hflipInfo && !(hflipInfo->flags & V4L2_CTRL_FLAG_READ_ONLY) &&\n+\t vflipInfo && !(vflipInfo->flags & V4L2_CTRL_FLAG_READ_ONLY)) {\n+\t\tsupportFlips_ = true;\n+\n+\t\tif (hflipInfo->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT ||\n+\t\t vflipInfo->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT)\n+\t\t\tflipsAlterBayerOrder_ = true;\n+\t}\n+\n+\tif (!supportFlips_)\n+\t\tLOG(CameraSensor, Debug)\n+\t\t\t<< \"Camera sensor does not support horizontal/vertical flip\";\n+\n+\t/*\n+\t * 5. Discover ancillary devices.\n+\t *\n+\t * \\todo This code may be shared by different V4L2 sensor classes.\n+\t */\n+\tfor (MediaEntity *ancillary : entity_->ancillaryEntities()) {\n+\t\tswitch (ancillary->function()) {\n+\t\tcase MEDIA_ENT_F_LENS:\n+\t\t\tfocusLens_ = std::make_unique<CameraLens>(ancillary);\n+\t\t\tret = focusLens_->init();\n+\t\t\tif (ret) {\n+\t\t\t\tLOG(CameraSensor, Error)\n+\t\t\t\t\t<< \"Lens initialisation failed, lens disabled\";\n+\t\t\t\tfocusLens_.reset();\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\tLOG(CameraSensor, Warning)\n+\t\t\t\t<< \"Unsupported ancillary entity function \"\n+\t\t\t\t<< ancillary->function();\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * 6. Initialize properties.\n+\t */\n+\n+\tret = initProperties();\n+\tif (ret)\n+\t\treturn { ret };\n+\n+\t/*\n+\t * 7. Initialize controls.\n+\t */\n+\n+\t/*\n+\t * Set HBLANK to the minimum to start with a well-defined line length,\n+\t * allowing IPA modules that do not modify HBLANK to use the sensor\n+\t * minimum line length in their calculations.\n+\t */\n+\tconst struct v4l2_query_ext_ctrl *hblankInfo = subdev_->controlInfo(V4L2_CID_HBLANK);\n+\tif (hblankInfo && !(hblankInfo->flags & V4L2_CTRL_FLAG_READ_ONLY)) {\n+\t\tControlList ctrl(subdev_->controls());\n+\n+\t\tctrl.set(V4L2_CID_HBLANK, static_cast<int32_t>(hblankInfo->minimum));\n+\t\tret = subdev_->setControls(&ctrl);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tret = applyTestPatternMode(controls::draft::TestPatternModeEnum::TestPatternModeOff);\n+\tif (ret)\n+\t\treturn { ret };\n+\n+\treturn {};\n+}\n+\n+int CameraSensorRaw::initProperties()\n+{\n+\tmodel_ = subdev_->model();\n+\tproperties_.set(properties::Model, utils::toAscii(model_));\n+\n+\t/* Generate a unique ID for the sensor. */\n+\tid_ = sysfs::firmwareNodePath(subdev_->devicePath());\n+\tif (id_.empty()) {\n+\t\tLOG(CameraSensor, Error) << \"Can't generate sensor ID\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/* Initialize the static properties from the sensor database. */\n+\tinitStaticProperties();\n+\n+\t/* Retrieve and register properties from the kernel interface. */\n+\tconst ControlInfoMap &controls = subdev_->controls();\n+\n+\tconst auto &orientation = controls.find(V4L2_CID_CAMERA_ORIENTATION);\n+\tif (orientation != controls.end()) {\n+\t\tint32_t v4l2Orientation = orientation->second.def().get<int32_t>();\n+\t\tint32_t propertyValue;\n+\n+\t\tswitch (v4l2Orientation) {\n+\t\tdefault:\n+\t\t\tLOG(CameraSensor, Warning)\n+\t\t\t\t<< \"Unsupported camera location \"\n+\t\t\t\t<< v4l2Orientation << \", setting to External\";\n+\t\t\t[[fallthrough]];\n+\t\tcase V4L2_CAMERA_ORIENTATION_EXTERNAL:\n+\t\t\tpropertyValue = properties::CameraLocationExternal;\n+\t\t\tbreak;\n+\t\tcase V4L2_CAMERA_ORIENTATION_FRONT:\n+\t\t\tpropertyValue = properties::CameraLocationFront;\n+\t\t\tbreak;\n+\t\tcase V4L2_CAMERA_ORIENTATION_BACK:\n+\t\t\tpropertyValue = properties::CameraLocationBack;\n+\t\t\tbreak;\n+\t\t}\n+\t\tproperties_.set(properties::Location, propertyValue);\n+\t} else {\n+\t\tLOG(CameraSensor, Warning) << \"Failed to retrieve the camera location\";\n+\t}\n+\n+\tconst auto &rotationControl = controls.find(V4L2_CID_CAMERA_SENSOR_ROTATION);\n+\tif (rotationControl != controls.end()) {\n+\t\tint32_t propertyValue = rotationControl->second.def().get<int32_t>();\n+\n+\t\t/*\n+\t\t * Cache the Transform associated with the camera mounting\n+\t\t * rotation for later use in computeTransform().\n+\t\t */\n+\t\tbool success;\n+\t\tmountingOrientation_ = orientationFromRotation(propertyValue, &success);\n+\t\tif (!success) {\n+\t\t\tLOG(CameraSensor, Warning)\n+\t\t\t\t<< \"Invalid rotation of \" << propertyValue\n+\t\t\t\t<< \" degrees - ignoring\";\n+\t\t\tmountingOrientation_ = Orientation::Rotate0;\n+\t\t}\n+\n+\t\tproperties_.set(properties::Rotation, propertyValue);\n+\t} else {\n+\t\tLOG(CameraSensor, Warning)\n+\t\t\t<< \"Rotation control not available, default to 0 degrees\";\n+\t\tproperties_.set(properties::Rotation, 0);\n+\t\tmountingOrientation_ = Orientation::Rotate0;\n+\t}\n+\n+\tproperties_.set(properties::PixelArraySize, pixelArraySize_);\n+\tproperties_.set(properties::PixelArrayActiveAreas, { activeArea_ });\n+\n+\t/* Color filter array pattern. */\n+\tuint32_t cfa;\n+\n+\tswitch (cfaPattern_) {\n+\tcase BayerFormat::BGGR:\n+\t\tcfa = properties::draft::BGGR;\n+\t\tbreak;\n+\tcase BayerFormat::GBRG:\n+\t\tcfa = properties::draft::GBRG;\n+\t\tbreak;\n+\tcase BayerFormat::GRBG:\n+\t\tcfa = properties::draft::GRBG;\n+\t\tbreak;\n+\tcase BayerFormat::RGGB:\n+\t\tcfa = properties::draft::RGGB;\n+\t\tbreak;\n+\tcase BayerFormat::MONO:\n+\tdefault:\n+\t\tcfa = properties::draft::MONO;\n+\t\tbreak;\n+\t}\n+\n+\tproperties_.set(properties::draft::ColorFilterArrangement, cfa);\n+\n+\treturn 0;\n+}\n+\n+void CameraSensorRaw::initStaticProperties()\n+{\n+\tstaticProps_ = CameraSensorProperties::get(model_);\n+\tif (!staticProps_)\n+\t\treturn;\n+\n+\t/* Register the properties retrieved from the sensor database. */\n+\tproperties_.set(properties::UnitCellSize, staticProps_->unitCellSize);\n+\n+\tinitTestPatternModes();\n+}\n+\n+void CameraSensorRaw::initTestPatternModes()\n+{\n+\tconst auto &v4l2TestPattern = controls().find(V4L2_CID_TEST_PATTERN);\n+\tif (v4l2TestPattern == controls().end()) {\n+\t\tLOG(CameraSensor, Debug) << \"V4L2_CID_TEST_PATTERN is not supported\";\n+\t\treturn;\n+\t}\n+\n+\tconst auto &testPatternModes = staticProps_->testPatternModes;\n+\tif (testPatternModes.empty()) {\n+\t\t/*\n+\t\t * The camera sensor supports test patterns but we don't know\n+\t\t * how to map them so this should be fixed.\n+\t\t */\n+\t\tLOG(CameraSensor, Debug) << \"No static test pattern map for \\'\"\n+\t\t\t\t\t << model() << \"\\'\";\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * Create a map that associates the V4L2 control index to the test\n+\t * pattern mode by reversing the testPatternModes map provided by the\n+\t * camera sensor properties. This makes it easier to verify if the\n+\t * control index is supported in the below for loop that creates the\n+\t * list of supported test patterns.\n+\t */\n+\tstd::map<int32_t, controls::draft::TestPatternModeEnum> indexToTestPatternMode;\n+\tfor (const auto &it : testPatternModes)\n+\t\tindexToTestPatternMode[it.second] = it.first;\n+\n+\tfor (const ControlValue &value : v4l2TestPattern->second.values()) {\n+\t\tconst int32_t index = value.get<int32_t>();\n+\n+\t\tconst auto it = indexToTestPatternMode.find(index);\n+\t\tif (it == indexToTestPatternMode.end()) {\n+\t\t\tLOG(CameraSensor, Debug)\n+\t\t\t\t<< \"Test pattern mode \" << index << \" ignored\";\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\ttestPatternModes_.push_back(it->second);\n+\t}\n+}\n+\n+std::vector<Size> CameraSensorRaw::sizes(unsigned int mbusCode) const\n+{\n+\tstd::vector<Size> sizes;\n+\n+\tconst auto &format = formats_.find(mbusCode);\n+\tif (format == formats_.end())\n+\t\treturn sizes;\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+Size CameraSensorRaw::resolution() const\n+{\n+\treturn std::min(sizes_.back(), activeArea_.size());\n+}\n+\n+V4L2SubdeviceFormat\n+CameraSensorRaw::getFormat(const std::vector<unsigned int> &mbusCodes,\n+\t\t\t const Size &size) const\n+{\n+\tunsigned int desiredArea = size.width * size.height;\n+\tunsigned int bestArea = UINT_MAX;\n+\tfloat desiredRatio = static_cast<float>(size.width) / size.height;\n+\tfloat bestRatio = FLT_MAX;\n+\tconst Size *bestSize = nullptr;\n+\tuint32_t bestCode = 0;\n+\n+\tfor (unsigned int code : mbusCodes) {\n+\t\tconst auto formats = formats_.find(code);\n+\t\tif (formats == formats_.end())\n+\t\t\tcontinue;\n+\n+\t\tfor (const SizeRange &range : formats->second) {\n+\t\t\tconst Size &sz = range.max;\n+\n+\t\t\tif (sz.width < size.width || sz.height < size.height)\n+\t\t\t\tcontinue;\n+\n+\t\t\tfloat ratio = static_cast<float>(sz.width) / sz.height;\n+\t\t\tfloat ratioDiff = std::abs(ratio - desiredRatio);\n+\t\t\tunsigned int area = sz.width * sz.height;\n+\t\t\tunsigned int areaDiff = area - desiredArea;\n+\n+\t\t\tif (ratioDiff > bestRatio)\n+\t\t\t\tcontinue;\n+\n+\t\t\tif (ratioDiff < bestRatio || areaDiff < bestArea) {\n+\t\t\t\tbestRatio = ratioDiff;\n+\t\t\t\tbestArea = areaDiff;\n+\t\t\t\tbestSize = &sz;\n+\t\t\t\tbestCode = code;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tif (!bestSize) {\n+\t\tLOG(CameraSensor, Debug) << \"No supported format or size found\";\n+\t\treturn {};\n+\t}\n+\n+\tV4L2SubdeviceFormat format{\n+\t\t.code = bestCode,\n+\t\t.size = *bestSize,\n+\t\t.colorSpace = ColorSpace::Raw,\n+\t};\n+\n+\treturn format;\n+}\n+\n+int CameraSensorRaw::setFormat(V4L2SubdeviceFormat *format, Transform transform)\n+{\n+\t/* Configure flips if the sensor supports that. */\n+\tif (supportFlips_) {\n+\t\tControlList flipCtrls(subdev_->controls());\n+\n+\t\tflipCtrls.set(V4L2_CID_HFLIP,\n+\t\t\t static_cast<int32_t>(!!(transform & Transform::HFlip)));\n+\t\tflipCtrls.set(V4L2_CID_VFLIP,\n+\t\t\t static_cast<int32_t>(!!(transform & Transform::VFlip)));\n+\n+\t\tint ret = subdev_->setControls(&flipCtrls);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\t/* Apply format on the subdev. */\n+\tint ret = subdev_->setFormat(streams_.image.source, format);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tsubdev_->updateControlInfo();\n+\treturn 0;\n+}\n+\n+int CameraSensorRaw::tryFormat(V4L2SubdeviceFormat *format) const\n+{\n+\treturn subdev_->setFormat(streams_.image.source, format,\n+\t\t\t\t V4L2Subdevice::Whence::TryFormat);\n+}\n+\n+int CameraSensorRaw::applyConfiguration(const SensorConfiguration &config,\n+\t\t\t\t\tTransform transform,\n+\t\t\t\t\tV4L2SubdeviceFormat *sensorFormat)\n+{\n+\tif (!config.isValid()) {\n+\t\tLOG(CameraSensor, Error) << \"Invalid sensor configuration\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tstd::vector<unsigned int> filteredCodes;\n+\tstd::copy_if(mbusCodes_.begin(), mbusCodes_.end(),\n+\t\t std::back_inserter(filteredCodes),\n+\t\t [&config](unsigned int mbusCode) {\n+\t\t\t BayerFormat bayer = BayerFormat::fromMbusCode(mbusCode);\n+\t\t\t if (bayer.bitDepth == config.bitDepth)\n+\t\t\t\t return true;\n+\t\t\t return false;\n+\t\t });\n+\tif (filteredCodes.empty()) {\n+\t\tLOG(CameraSensor, Error)\n+\t\t\t<< \"Cannot find any format with bit depth \"\n+\t\t\t<< config.bitDepth;\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/*\n+\t * Compute the sensor's data frame size by applying the cropping\n+\t * rectangle, subsampling and output crop to the sensor's pixel array\n+\t * size.\n+\t *\n+\t * \\todo The actual size computation is for now ignored and only the\n+\t * output size is considered. This implies that resolutions obtained\n+\t * with two different cropping/subsampling will look identical and\n+\t * only the first found one will be considered.\n+\t */\n+\tV4L2SubdeviceFormat subdevFormat = {};\n+\tfor (unsigned int code : filteredCodes) {\n+\t\tfor (const Size &size : sizes(code)) {\n+\t\t\tif (size.width != config.outputSize.width ||\n+\t\t\t size.height != config.outputSize.height)\n+\t\t\t\tcontinue;\n+\n+\t\t\tsubdevFormat.code = code;\n+\t\t\tsubdevFormat.size = size;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tif (!subdevFormat.code) {\n+\t\tLOG(CameraSensor, Error) << \"Invalid output size in sensor configuration\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tint ret = setFormat(&subdevFormat, transform);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/*\n+\t * Return to the caller the format actually applied to the sensor.\n+\t * This is relevant if transform has changed the bayer pattern order.\n+\t */\n+\tif (sensorFormat)\n+\t\t*sensorFormat = subdevFormat;\n+\n+\t/* \\todo Handle AnalogCrop. Most sensors do not support set_selection */\n+\t/* \\todo Handle scaling in the digital domain. */\n+\n+\treturn 0;\n+}\n+\n+int CameraSensorRaw::sensorInfo(IPACameraSensorInfo *info) const\n+{\n+\tinfo->model = model();\n+\n+\t/*\n+\t * The active area size is a static property, while the crop\n+\t * rectangle needs to be re-read as it depends on the sensor\n+\t * configuration.\n+\t */\n+\tinfo->activeAreaSize = { activeArea_.width, activeArea_.height };\n+\n+\tint ret = subdev_->getSelection(streams_.image.sink, V4L2_SEL_TGT_CROP,\n+\t\t\t\t\t&info->analogCrop);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/*\n+\t * IPACameraSensorInfo::analogCrop::x and IPACameraSensorInfo::analogCrop::y\n+\t * are defined relatively to the active pixel area, while V4L2's\n+\t * TGT_CROP target is defined in respect to the full pixel array.\n+\t *\n+\t * Compensate it by subtracting the active area offset.\n+\t */\n+\tinfo->analogCrop.x -= activeArea_.x;\n+\tinfo->analogCrop.y -= activeArea_.y;\n+\n+\t/* The bit depth and image size depend on the currently applied format. */\n+\tV4L2SubdeviceFormat format{};\n+\tret = subdev_->getFormat(streams_.image.source, &format);\n+\tif (ret)\n+\t\treturn ret;\n+\tinfo->bitsPerPixel = MediaBusFormatInfo::info(format.code).bitsPerPixel;\n+\tinfo->outputSize = format.size;\n+\n+\tstd::optional<int32_t> cfa = properties_.get(properties::draft::ColorFilterArrangement);\n+\tinfo->cfaPattern = cfa ? *cfa : properties::draft::RGB;\n+\n+\t/*\n+\t * Retrieve the pixel rate, line length and minimum/maximum frame\n+\t * duration through V4L2 controls. Support for the V4L2_CID_PIXEL_RATE,\n+\t * V4L2_CID_HBLANK and V4L2_CID_VBLANK controls is mandatory.\n+\t */\n+\tControlList ctrls = subdev_->getControls({ V4L2_CID_PIXEL_RATE,\n+\t\t\t\t\t\t V4L2_CID_HBLANK,\n+\t\t\t\t\t\t V4L2_CID_VBLANK });\n+\tif (ctrls.empty()) {\n+\t\tLOG(CameraSensor, Error)\n+\t\t\t<< \"Failed to retrieve camera info controls\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tinfo->pixelRate = ctrls.get(V4L2_CID_PIXEL_RATE).get<int64_t>();\n+\n+\tconst ControlInfo hblank = ctrls.infoMap()->at(V4L2_CID_HBLANK);\n+\tinfo->minLineLength = info->outputSize.width + hblank.min().get<int32_t>();\n+\tinfo->maxLineLength = info->outputSize.width + hblank.max().get<int32_t>();\n+\n+\tconst ControlInfo vblank = ctrls.infoMap()->at(V4L2_CID_VBLANK);\n+\tinfo->minFrameLength = info->outputSize.height + vblank.min().get<int32_t>();\n+\tinfo->maxFrameLength = info->outputSize.height + vblank.max().get<int32_t>();\n+\n+\treturn 0;\n+}\n+\n+Transform CameraSensorRaw::computeTransform(Orientation *orientation) const\n+{\n+\t/*\n+\t * If we cannot do any flips we cannot change the native camera mounting\n+\t * orientation.\n+\t */\n+\tif (!supportFlips_) {\n+\t\t*orientation = mountingOrientation_;\n+\t\treturn Transform::Identity;\n+\t}\n+\n+\t/*\n+\t * Now compute the required transform to obtain 'orientation' starting\n+\t * from the mounting rotation.\n+\t *\n+\t * As a note:\n+\t * \torientation / mountingOrientation_ = transform\n+\t * \tmountingOrientation_ * transform = orientation\n+\t */\n+\tTransform transform = *orientation / mountingOrientation_;\n+\n+\t/*\n+\t * If transform contains any Transpose we cannot do it, so adjust\n+\t * 'orientation' to report the image native orientation and return Identity.\n+\t */\n+\tif (!!(transform & Transform::Transpose)) {\n+\t\t*orientation = mountingOrientation_;\n+\t\treturn Transform::Identity;\n+\t}\n+\n+\treturn transform;\n+}\n+\n+BayerFormat::Order CameraSensorRaw::bayerOrder(Transform t) const\n+{\n+\tif (!flipsAlterBayerOrder_)\n+\t\treturn cfaPattern_;\n+\n+\t/*\n+\t * Apply the transform to the native (i.e. untransformed) Bayer order,\n+\t * using the rest of the Bayer format supplied by the caller.\n+\t */\n+\tBayerFormat format{ cfaPattern_, 8, BayerFormat::Packing::None };\n+\treturn format.transform(t).order;\n+}\n+\n+const ControlInfoMap &CameraSensorRaw::controls() const\n+{\n+\treturn subdev_->controls();\n+}\n+\n+ControlList CameraSensorRaw::getControls(const std::vector<uint32_t> &ids)\n+{\n+\treturn subdev_->getControls(ids);\n+}\n+\n+int CameraSensorRaw::setControls(ControlList *ctrls)\n+{\n+\treturn subdev_->setControls(ctrls);\n+}\n+\n+int CameraSensorRaw::setTestPatternMode(controls::draft::TestPatternModeEnum mode)\n+{\n+\tif (testPatternMode_ == mode)\n+\t\treturn 0;\n+\n+\tif (testPatternModes_.empty()) {\n+\t\tLOG(CameraSensor, Error)\n+\t\t\t<< \"Camera sensor does not support test pattern modes.\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\treturn applyTestPatternMode(mode);\n+}\n+\n+int CameraSensorRaw::applyTestPatternMode(controls::draft::TestPatternModeEnum mode)\n+{\n+\tif (testPatternModes_.empty())\n+\t\treturn 0;\n+\n+\tauto it = std::find(testPatternModes_.begin(), testPatternModes_.end(),\n+\t\t\t mode);\n+\tif (it == testPatternModes_.end()) {\n+\t\tLOG(CameraSensor, Error) << \"Unsupported test pattern mode \"\n+\t\t\t\t\t << mode;\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tLOG(CameraSensor, Debug) << \"Apply test pattern mode \" << mode;\n+\n+\tint32_t index = staticProps_->testPatternModes.at(mode);\n+\tControlList ctrls{ controls() };\n+\tctrls.set(V4L2_CID_TEST_PATTERN, index);\n+\n+\tint ret = setControls(&ctrls);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\ttestPatternMode_ = mode;\n+\n+\treturn 0;\n+}\n+\n+std::string CameraSensorRaw::logPrefix() const\n+{\n+\treturn \"'\" + entity_->name() + \"'\";\n+}\n+\n+REGISTER_CAMERA_SENSOR(CameraSensorRaw, 0)\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/sensor/meson.build b/src/libcamera/sensor/meson.build\nindex f0d588977a4f..dce74ed6ac88 100644\n--- a/src/libcamera/sensor/meson.build\n+++ b/src/libcamera/sensor/meson.build\n@@ -4,4 +4,5 @@ libcamera_internal_sources += files([\n 'camera_sensor.cpp',\n 'camera_sensor_legacy.cpp',\n 'camera_sensor_properties.cpp',\n+ 'camera_sensor_raw.cpp',\n ])\n", "prefixes": [ "v2", "5/6" ] }