Patch Detail
Show a patch.
GET /api/patches/27165/?format=api
{ "id": 27165, "url": "https://patchwork.libcamera.org/api/patches/27165/?format=api", "web_url": "https://patchwork.libcamera.org/patch/27165/", "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": "<20260703122543.1991189-7-paul.elder@ideasonboard.com>", "date": "2026-07-03T12:25:12", "name": "[RFC,06/19] pipeline: rkisp2: Implement pipeline handler for rkisp2", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "064f5b84e4a01fd312f738a7d1c101f9ad0023d2", "submitter": { "id": 17, "url": "https://patchwork.libcamera.org/api/people/17/?format=api", "name": "Paul Elder", "email": "paul.elder@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/27165/mbox/", "series": [ { "id": 6035, "url": "https://patchwork.libcamera.org/api/series/6035/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=6035", "date": "2026-07-03T12:25:06", "name": "Add support for rkisp2", "version": 1, "mbox": "https://patchwork.libcamera.org/series/6035/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/27165/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/27165/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 0FA98C328C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 3 Jul 2026 12:26:24 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id B24E065FD3;\n\tFri, 3 Jul 2026 14:26:23 +0200 (CEST)", "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 B05BC65FC3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 3 Jul 2026 14:26:21 +0200 (CEST)", "from neptunite.hamster-moth.ts.net (unknown\n\t[IPv6:2404:7a81:160:2100:a2cc:2f45:3bd7:2589])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id CC5791121;\n\tFri, 3 Jul 2026 14:25:32 +0200 (CEST)" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"LdSLUBx2\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1783081535;\n\tbh=W3siiXg6HQg73xR/LjWsqYVkkFKQb9abJkGtdXeF6hU=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=LdSLUBx2+5Gw6NpQuwiuXJ2MGSx2gxKvbfpYUUMLvxX6/voKSnN7gtx3DuQsZmW3i\n\tsAndyftUvANyUKwy6WP4sQD7ySbQxae7QrDPtr8bS+klkoiCNrwxKSRniPeVzCEaQ7\n\tDaKqhI5VCOBnM9Sg/QKKFWxEfTGqXHXLhWKKZO8Q=", "From": "Paul Elder <paul.elder@ideasonboard.com>", "To": "laurent.pinchart@ideasonboard.com", "Cc": "Paul Elder <paul.elder@ideasonboard.com>, michael.riesch@collabora.com, \n\txuhf@rock-chips.com, stefan.klug@ideasonboard.com,\n\tkieran.bingham@ideasonboard.com, dan.scally@ideasonboard.com,\n\tjacopo.mondi@ideasonboard.com, nicolas.dufresne@collabora.com,\n\tlibcamera-devel@lists.libcamera.org", "Subject": "[RFC PATCH 06/19] pipeline: rkisp2: Implement pipeline handler for\n\trkisp2", "Date": "Fri, 3 Jul 2026 21:25:12 +0900", "Message-ID": "<20260703122543.1991189-7-paul.elder@ideasonboard.com>", "X-Mailer": "git-send-email 2.47.2", "In-Reply-To": "<20260703122543.1991189-1-paul.elder@ideasonboard.com>", "References": "<20260703122543.1991189-1-paul.elder@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": "Implement the pipeline handler for rkisp2. This currently supports the\nISP version integrated on the Rockchip RK3588. The current version only\nsupports memory-to-memory mode and only the main path and only raw\nsensors.\n\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\n---\n Documentation/runtime_configuration.rst | 9 +\n meson.build | 1 +\n meson_options.txt | 1 +\n src/libcamera/pipeline/rkisp2/meson.build | 5 +\n src/libcamera/pipeline/rkisp2/rkisp2.cpp | 1305 +++++++++++++++++++++\n 5 files changed, 1321 insertions(+)\n create mode 100644 src/libcamera/pipeline/rkisp2/meson.build\n create mode 100644 src/libcamera/pipeline/rkisp2/rkisp2.cpp", "diff": "diff --git a/Documentation/runtime_configuration.rst b/Documentation/runtime_configuration.rst\nindex 2cdffb335a66..15dc118bff7b 100644\n--- a/Documentation/runtime_configuration.rst\n+++ b/Documentation/runtime_configuration.rst\n@@ -81,6 +81,8 @@ Configuration file example\n supported_devices:\n - driver: mxc-isi\n software_isp: true\n+ rkisp2:\n+ isp_enable: true\n software_isp:\n copy_input_buffer: false\n measure:\n@@ -157,6 +159,13 @@ pipelines.simple.supported_devices.driver, pipelines.simple.supported_devices.so\n \n Example `software_isp` value: ``true``\n \n+pipelines.rkisp2.isp_enable\n+ Configure whether or not to use the ISP. Default (when unconfigured) is\n+ true. When set to false the ISP will not be used, so only the VICAP will be\n+ used for capture.\n+\n+ Example value: ``false``\n+\n software_isp.copy_input_buffer\n Define whether input buffers should be copied into standard (cached)\n memory in software ISP. This is done by default to prevent very slow\ndiff --git a/meson.build b/meson.build\nindex 3b8d54a654a3..a86ab176b9ae 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -219,6 +219,7 @@ pipelines_support = {\n 'ipu3': arch_x86,\n 'mali-c55': arch_arm,\n 'rkisp1': arch_arm,\n+ 'rkisp2': arch_arm,\n 'rpi/pisp': arch_arm,\n 'rpi/vc4': arch_arm,\n 'simple': ['any'],\ndiff --git a/meson_options.txt b/meson_options.txt\nindex 2638c77cb8b0..5443e3698432 100644\n--- a/meson_options.txt\n+++ b/meson_options.txt\n@@ -82,6 +82,7 @@ option('pipelines',\n 'ipu3',\n 'mali-c55',\n 'rkisp1',\n+ 'rkisp2',\n 'rpi/pisp',\n 'rpi/vc4',\n 'simple',\ndiff --git a/src/libcamera/pipeline/rkisp2/meson.build b/src/libcamera/pipeline/rkisp2/meson.build\nnew file mode 100644\nindex 000000000000..86f43fb7fcc9\n--- /dev/null\n+++ b/src/libcamera/pipeline/rkisp2/meson.build\n@@ -0,0 +1,5 @@\n+# SPDX-License-Identifier: CC0-1.0\n+\n+libcamera_internal_sources += files([\n+ 'rkisp2.cpp',\n+])\ndiff --git a/src/libcamera/pipeline/rkisp2/rkisp2.cpp b/src/libcamera/pipeline/rkisp2/rkisp2.cpp\nnew file mode 100644\nindex 000000000000..0d335a980e32\n--- /dev/null\n+++ b/src/libcamera/pipeline/rkisp2/rkisp2.cpp\n@@ -0,0 +1,1305 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2026, Ideas on Board Oy.\n+ *\n+ * Pipeline handler for Rockchip ISP2\n+ */\n+\n+#include <algorithm>\n+#include <deque>\n+#include <memory>\n+#include <set>\n+#include <vector>\n+\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/formats.h>\n+#include <libcamera/geometry.h>\n+\n+#include <libcamera/ipa/core_ipa_interface.h>\n+#include <libcamera/ipa/rkisp2_ipa_interface.h>\n+#include <libcamera/ipa/rkisp2_ipa_proxy.h>\n+\n+#include \"libcamera/internal/buffer_queue.h\"\n+#include \"libcamera/internal/camera.h\"\n+#include \"libcamera/internal/camera_manager.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/formats.h\"\n+#include \"libcamera/internal/framebuffer.h\"\n+#include \"libcamera/internal/global_configuration.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/sequence_sync_helper.h\"\n+#include \"libcamera/internal/v4l2_subdevice.h\"\n+#include \"libcamera/internal/v4l2_videodevice.h\"\n+\n+namespace libcamera {\n+\n+static const Size ispMaxSize = Size(4416, 3312);\n+static const Size vicapMaxSize = Size(8192, 8192);\n+\n+/* \\todo Re-add NV21 and YUV422 and YVU422 after it's fixed in the driver */\n+const std::map<PixelFormat, uint32_t> formatToMediaBus = {\n+\t{ formats::UYVY, MEDIA_BUS_FMT_YUYV8_2X8 },\n+\t{ formats::NV12, MEDIA_BUS_FMT_YUYV8_2X8 },\n+\t{ formats::NV16, MEDIA_BUS_FMT_YUYV8_2X8 },\n+\t{ formats::NV61, MEDIA_BUS_FMT_YUYV8_2X8 },\n+\t{ formats::YUV420, MEDIA_BUS_FMT_YUYV8_1_5X8 },\n+\t{ formats::YVU420, MEDIA_BUS_FMT_YUYV8_1_5X8 },\n+\t{ formats::R8, MEDIA_BUS_FMT_YUYV8_2X8 },\n+};\n+\n+/* \\todo Deduplicate this (we have the same thing in rkisp1) */\n+const std::map<PixelFormat, uint32_t> rawFormats = {\n+\t{ formats::SBGGR8, MEDIA_BUS_FMT_SBGGR8_1X8 },\n+\t{ formats::SGBRG8, MEDIA_BUS_FMT_SGBRG8_1X8 },\n+\t{ formats::SGRBG8, MEDIA_BUS_FMT_SGRBG8_1X8 },\n+\t{ formats::SRGGB8, MEDIA_BUS_FMT_SRGGB8_1X8 },\n+\t{ formats::SBGGR10, MEDIA_BUS_FMT_SBGGR10_1X10 },\n+\t{ formats::SGBRG10, MEDIA_BUS_FMT_SGBRG10_1X10 },\n+\t{ formats::SGRBG10, MEDIA_BUS_FMT_SGRBG10_1X10 },\n+\t{ formats::SRGGB10, MEDIA_BUS_FMT_SRGGB10_1X10 },\n+\t{ formats::SBGGR12, MEDIA_BUS_FMT_SBGGR12_1X12 },\n+\t{ formats::SGBRG12, MEDIA_BUS_FMT_SGBRG12_1X12 },\n+\t{ formats::SGRBG12, MEDIA_BUS_FMT_SGRBG12_1X12 },\n+\t{ formats::SRGGB12, MEDIA_BUS_FMT_SRGGB12_1X12 },\n+};\n+\n+LOG_DEFINE_CATEGORY(RkISP2)\n+\n+class PipelineHandlerRkISP2;\n+\n+struct RkISP2FrameInfo {\n+\tRkISP2FrameInfo(Request *_request,\n+\t\t\tFrameBuffer *_buffer,\n+\t\t\tconst ControlList &_metadata = ControlList(controls::controls),\n+\t\t\tbool _metadataProcessed = false)\n+\t\t: request(_request), mainPathBuffer(_buffer), metadataProcessed(_metadataProcessed)\n+\t{\n+\t\tmetadata.merge(_metadata);\n+\t}\n+\n+\tRequest *request = nullptr;\n+\tFrameBuffer *mainPathBuffer = nullptr;\n+\tControlList metadata = ControlList(controls::controls);\n+\tbool metadataProcessed = false;\n+};\n+\n+struct RkISP2RequestInfo {\n+\tRequest *request = nullptr;\n+\tsize_t sequence = 0;\n+\tbool sequenceValid = false;\n+};\n+\n+class RkISP2CameraData : public Camera::Private\n+{\n+public:\n+\tRkISP2CameraData(PipelineHandler *pipe, MediaDevice *media)\n+\t\t: Camera::Private(pipe), media_(media), frame_(0)\n+\t{\n+\t}\n+\n+\t~RkISP2CameraData()\n+\t{\n+\t}\n+\n+\tint init(bool usingIsp, CameraSensor *sensor, V4L2VideoDevice *video,\n+\t\t V4L2VideoDevice *rawrd, V4L2Subdevice *isp,\n+\t\t V4L2VideoDevice *mainPath, V4L2VideoDevice *param,\n+\t\t V4L2VideoDevice *stat);\n+\tPixelFormat getSensorFormat(unsigned int mbusCode, const Size &size,\n+\t\t\t\t const Size &maxSize);\n+\n+\tPipelineHandlerRkISP2 *pipe();\n+\tconst PipelineHandlerRkISP2 *pipe() const;\n+\tint loadIPA();\n+\n+\tvoid computeParamBuffers(unsigned int frame);\n+\n+\tvoid tryCompleteRequest(Request *request, FrameBuffer *buffer);\n+\tvoid tryCompleteRequest(const ControlList &metadata);\n+\n+\tvoid vicapBufferReady(FrameBuffer *buffer);\n+\tvoid rawrdBufferReady(FrameBuffer *buffer);\n+\tvoid ispBufferReady(FrameBuffer *buffer);\n+\tvoid statBufferReady(FrameBuffer *buffer);\n+\n+\tvoid paramsComputed(unsigned int frame, unsigned int bufferId, unsigned int bytesused);\n+\tvoid setSensorControls(unsigned int frame, const ControlList &sensorControls);\n+\tvoid metadataReady(const ControlList &metadata);\n+\n+\t/* These are the buffers that go between the vicap and the isp */\n+\tstd::vector<std::unique_ptr<FrameBuffer>> internalBuffers_;\n+\n+\tstd::vector<std::unique_ptr<FrameBuffer>> statBuffers_;\n+\tstd::unique_ptr<BufferQueue> paramQueue_;\n+\n+\tstd::unique_ptr<DelayedControls> delayedCtrls_;\n+\n+\t/*\n+\t * These are the buffers that are half-complete, as in either metadata\n+\t * is ready or the frame has been completed from the ISP. Both\n+\t * ready-handlers populate and complete from the front of the queue.\n+\t *\n+\t * The stat-ready handler flushes the queue however, because in general\n+\t * more images complete than stats do, so this prevents the queue from\n+\t * perpetually growing and making the metadata out of date.\n+\t */\n+\tstd::deque<RkISP2FrameInfo> pendingCompleteBuffers_;\n+\n+\t/*\n+\t * This is for tracking sequence numbers of requests for synchronizing\n+\t * between stats and params and images for any dropped frames\n+\t */\n+\tstd::deque<RkISP2RequestInfo> pendingCompleteRequests_;\n+\n+\tstd::vector<IPABuffer> ipaBuffers_;\n+\n+\tbool usingIsp_;\n+\tbool isRaw_;\n+\n+\tMediaDevice *media_;\n+\tCameraSensor *sensor_;\n+\tV4L2VideoDevice *video_;\n+\tV4L2VideoDevice *rawrd_;\n+\tV4L2Subdevice *isp_;\n+\tV4L2VideoDevice *mainPath_;\n+\tV4L2VideoDevice *param_;\n+\tV4L2VideoDevice *stat_;\n+\tStream stream_;\n+\n+\tstd::unique_ptr<ipa::rkisp2::IPAProxyRkISP2> ipa_;\n+\tControlInfoMap ipaControls_;\n+\n+\t/*\n+\t * The sensor frame sequence of the last request queued to the pipeline\n+\t * handler\n+\t */\n+\tunsigned int frame_;\n+\tSequenceSyncHelper syncHelper_;\n+};\n+\n+class RkISP2CameraConfiguration : public CameraConfiguration\n+{\n+public:\n+\tRkISP2CameraConfiguration(const RkISP2CameraData *data);\n+\n+\tStatus validate() override;\n+\n+\tconst V4L2SubdeviceFormat &sensorFormat() { return sensorFormat_; }\n+\n+private:\n+\tconst RkISP2CameraData *data_;\n+\n+\tV4L2SubdeviceFormat sensorFormat_;\n+};\n+\n+namespace {\n+\n+/*\n+ * This many internal buffers (or rather parameter and statistics buffer\n+ * pairs) ensures that the pipeline runs smoothly, without frame drops.\n+ */\n+static constexpr unsigned int kRkISP2MinBufferCount = 4;\n+\n+} /* namespace */\n+\n+class PipelineHandlerRkISP2 : public PipelineHandler\n+{\n+public:\n+\tPipelineHandlerRkISP2(CameraManager *manager);\n+\n+\tstd::unique_ptr<CameraConfiguration>\n+\tgenerateConfiguration(Camera *camera,\n+\t\t\t Span<const StreamRole> roles) override;\n+\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+\tbool match(DeviceEnumerator *enumerator) override;\n+\n+private:\n+\tRkISP2CameraData *cameraData(Camera *camera)\n+\t{\n+\t\treturn static_cast<RkISP2CameraData *>(camera->_d());\n+\t}\n+\n+\tint updateControls(RkISP2CameraData *data);\n+\tbool createCamera(bool usingIsp);\n+\tint processControls(RkISP2CameraData *data, const ControlList &ctrls);\n+\n+\tint allocateBuffers(Camera *camera);\n+\tint freeBuffers(Camera *camera);\n+\n+\tSize clampSensorSize(RkISP2CameraData *data, unsigned int mbus,\n+\t\t\t const Size &maxSize);\n+\n+\tstd::shared_ptr<MediaDevice> media_;\n+\tstd::unique_ptr<CameraSensor> sensor_;\n+\tstd::unique_ptr<V4L2Subdevice> csi_;\n+\tstd::unique_ptr<V4L2Subdevice> cif_;\n+\tstd::unique_ptr<V4L2VideoDevice> video_;\n+\n+\tstd::shared_ptr<MediaDevice> ispMedia_;\n+\tstd::unique_ptr<V4L2VideoDevice> rawrd_;\n+\tstd::unique_ptr<V4L2Subdevice> isp_;\n+\tstd::unique_ptr<V4L2VideoDevice> mainPath_;\n+\tstd::unique_ptr<V4L2VideoDevice> param_;\n+\tstd::unique_ptr<V4L2VideoDevice> stat_;\n+};\n+\n+int RkISP2CameraData::init(bool usingIsp, CameraSensor *sensor,\n+\t\t\t V4L2VideoDevice *video, V4L2VideoDevice *rawrd,\n+\t\t\t V4L2Subdevice *isp,\n+\t\t\t V4L2VideoDevice *mainPath, V4L2VideoDevice *param,\n+\t\t\t V4L2VideoDevice *stat)\n+{\n+\tusingIsp_ = usingIsp;\n+\tsensor_ = sensor;\n+\tvideo_ = video;\n+\trawrd_ = rawrd;\n+\tisp_ = isp;\n+\tmainPath_ = mainPath;\n+\tparam_ = param;\n+\tstat_ = stat;\n+\n+\tControlInfoMap::Map ctrls;\n+\n+\tauto &testPatterns = sensor_->testPatternModes();\n+\tif (testPatterns.size()) {\n+\t\tctrls.emplace(&controls::draft::TestPatternMode,\n+\t\t\t ControlInfo(testPatterns.front(), testPatterns.back(),\n+\t\t\t\t\t testPatterns.front()));\n+\t} else\n+\t\tLOG(RkISP2, Warning) << \"Sensor doesn't support any test pattern modes\";\n+\n+\tcontrolInfo_ = ControlInfoMap(std::move(ctrls), controls::controls);\n+\n+\tvideo_->bufferReady.connect(this, &RkISP2CameraData::vicapBufferReady);\n+\tif (mainPath_) {\n+\t\trawrd_->bufferReady.connect(this, &RkISP2CameraData::rawrdBufferReady);\n+\t\tmainPath_->bufferReady.connect(this, &RkISP2CameraData::ispBufferReady);\n+\t\tstat_->bufferReady.connect(this, &RkISP2CameraData::statBufferReady);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+PixelFormat RkISP2CameraData::getSensorFormat(unsigned int mbusCode,\n+\t\t\t\t\t const Size &size,\n+\t\t\t\t\t const Size &maxSize)\n+{\n+\tstd::vector<unsigned int> mbusCodes = { mbusCode };\n+\tV4L2SubdeviceFormat format = sensor_->getFormat(mbusCodes, size, maxSize);\n+\tconst auto &ret = std::find_if(rawFormats.begin(), rawFormats.end(),\n+\t\t\t\t [format](const auto &value) { return value.second == format.code; });\n+\tif (ret != rawFormats.end())\n+\t\treturn (*ret).first;\n+\n+\tLOG(RkISP2, Error) << \"No raw format supported by sensor\";\n+\treturn formats::SRGGB10;\n+}\n+\n+PipelineHandlerRkISP2 *RkISP2CameraData::pipe()\n+{\n+\treturn static_cast<PipelineHandlerRkISP2 *>(Camera::Private::pipe());\n+}\n+\n+const PipelineHandlerRkISP2 *RkISP2CameraData::pipe() const\n+{\n+\treturn static_cast<const PipelineHandlerRkISP2 *>(Camera::Private::pipe());\n+}\n+\n+int RkISP2CameraData::loadIPA()\n+{\n+\tipa_ = pipe()->createIPA<ipa::rkisp2::IPAProxyRkISP2>(1, 1);\n+\tif (!ipa_)\n+\t\treturn -ENOENT;\n+\n+\tipa_->paramsComputed.connect(this, &RkISP2CameraData::paramsComputed);\n+\tipa_->setSensorControls.connect(this, &RkISP2CameraData::setSensorControls);\n+\tipa_->metadataReady.connect(this, &RkISP2CameraData::metadataReady);\n+\n+\t/* The IPA tuning file is made from the sensor name. */\n+\tstd::string ipaTuningFile =\n+\t\tipa_->configurationFile(sensor_->model() + \".yaml\", \"uncalibrated.yaml\");\n+\n+\tIPACameraSensorInfo sensorInfo{};\n+\tint ret = sensor_->sensorInfo(&sensorInfo);\n+\tif (ret) {\n+\t\tLOG(RkISP2, Error) << \"Camera sensor information not available\";\n+\t\treturn ret;\n+\t}\n+\n+\tret = ipa_->init({ ipaTuningFile, sensor_->model() },\n+\t\t\t sensorInfo, sensor_->controls(),\n+\t\t\t &ipaControls_);\n+\tif (ret < 0) {\n+\t\tLOG(RkISP2, Error) << \"IPA initialization failure\";\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+void RkISP2CameraData::computeParamBuffers(unsigned int frame)\n+{\n+\twhile (paramQueue_->nextSequence() <= frame) {\n+\t\tif (paramQueue_->empty(BufferQueue::Idle)) {\n+\t\t\tLOG(RkISP2, Warning) << \"Out of param buffers\";\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tuint32_t paramsSequence;\n+\t\tFrameBuffer *paramBuffer = paramQueue_->front(BufferQueue::Idle);\n+\t\tparamQueue_->prepareBuffer(¶msSequence);\n+\t\tipa_->computeParams(paramsSequence, paramBuffer->cookie());\n+\t}\n+}\n+\n+void RkISP2CameraData::vicapBufferReady(FrameBuffer *buffer)\n+{\n+\tRequest *request = buffer->request();\n+\n+\tif (!mainPath_ || isRaw_) {\n+\t\tpipe()->completeBuffer(request, buffer);\n+\t\tpipe()->completeRequest(request);\n+\t\treturn;\n+\t}\n+\n+\tif (buffer->metadata().status == FrameMetadata::FrameCancelled)\n+\t\treturn;\n+\n+\trawrd_->queueBuffer(buffer);\n+}\n+\n+void RkISP2CameraData::rawrdBufferReady(FrameBuffer *buffer)\n+{\n+\tif (buffer->metadata().status == FrameMetadata::FrameCancelled)\n+\t\treturn;\n+\n+\tvideo_->queueBuffer(buffer);\n+}\n+\n+void RkISP2CameraData::ispBufferReady(FrameBuffer *buffer)\n+{\n+\tRequest *request = buffer->request();\n+\n+\tif (buffer->metadata().status == FrameMetadata::FrameCancelled) {\n+\t\tsyncHelper_.cancelFrame();\n+\t\tpipe()->completeBuffer(request, buffer);\n+\t\tpipe()->completeRequest(request);\n+\t\treturn;\n+\t}\n+\n+\tRkISP2RequestInfo info = pendingCompleteRequests_.front();\n+\tpendingCompleteRequests_.pop_front();\n+\tASSERT(info.request == buffer->request());\n+\n+\tint droppedFrames = syncHelper_.gotFrame(info.sequence, buffer->metadata().sequence);\n+\tif (droppedFrames > 0) {\n+\t\tLOG(RkISP2, Debug)\n+\t\t\t<< \"Dropped frames \" << droppedFrames << \" expected \"\n+\t\t\t<< info.sequence << \" got \" << buffer->metadata().sequence;\n+\t}\n+\n+\tpipe()->completeBuffer(request, buffer);\n+\ttryCompleteRequest(request, buffer);\n+}\n+\n+void RkISP2CameraData::statBufferReady(FrameBuffer *buffer)\n+{\n+\tsize_t sequence = buffer->metadata().sequence;\n+\n+\tif (buffer->metadata().status == FrameMetadata::FrameCancelled)\n+\t\treturn;\n+\n+\tipa_->processStats(sequence, buffer->cookie(),\n+\t\t\t delayedCtrls_->get(sequence));\n+\n+\tstat_->queueBuffer(buffer);\n+}\n+\n+void RkISP2CameraData::paramsComputed(unsigned int frame,\n+\t\t\t\t unsigned int bufferId,\n+\t\t\t\t unsigned int bytesused)\n+{\n+\tFrameBuffer *buffer = paramQueue_->front(BufferQueue::Preparing);\n+\n+\tASSERT(buffer->cookie() == bufferId);\n+\n+\tbuffer->_d()->metadata().planes()[0].bytesused = bytesused;\n+\n+\tint ret = paramQueue_->preparedBuffer();\n+\tif (ret < 0) {\n+\t\tLOG(RkISP2, Error)\n+\t\t\t<< \"Failed to queue parameter buffer for frame \"\n+\t\t\t<< frame << \": \" << strerror(-ret);\n+\t}\n+}\n+\n+void RkISP2CameraData::setSensorControls([[maybe_unused]] unsigned int frame,\n+\t\t\t\t\t const ControlList &sensorControls)\n+{\n+\tdelayedCtrls_->push(sensorControls);\n+}\n+\n+void RkISP2CameraData::metadataReady(const ControlList &metadata)\n+{\n+\ttryCompleteRequest(metadata);\n+}\n+\n+void RkISP2CameraData::tryCompleteRequest(Request *request, FrameBuffer *buffer)\n+{\n+\tif (pendingCompleteBuffers_.empty()) {\n+\t\tpendingCompleteBuffers_.emplace_back(request, buffer);\n+\t\treturn;\n+\t}\n+\n+\tRkISP2FrameInfo &info = pendingCompleteBuffers_.front();\n+\tif (info.request != nullptr || info.mainPathBuffer != nullptr || !info.metadataProcessed) {\n+\t\tpendingCompleteBuffers_.emplace_back(request, buffer);\n+\t\treturn;\n+\t}\n+\n+\tinfo = pendingCompleteBuffers_.front();\n+\trequest->_d()->metadata().merge(info.metadata);\n+\tpendingCompleteBuffers_.pop_front();\n+\n+\tpipe()->completeRequest(request);\n+}\n+\n+void RkISP2CameraData::tryCompleteRequest(const ControlList &metadata)\n+{\n+\tif (pendingCompleteBuffers_.empty()) {\n+\t\tpendingCompleteBuffers_.emplace_back(nullptr, nullptr, metadata, true);\n+\t\treturn;\n+\t}\n+\n+\tRkISP2FrameInfo &info = pendingCompleteBuffers_.front();\n+\tif (!info.metadata.empty() || info.metadataProcessed) {\n+\t\t/*\n+\t\t * Generally more metadata complete than images, so flush the\n+\t\t * queue when adding new metadata to to the queue to prevent\n+\t\t * metadata in the queue from becoming too old\n+\t\t */\n+\t\tpendingCompleteBuffers_.clear();\n+\t\tpendingCompleteBuffers_.emplace_back(nullptr, nullptr, metadata, true);\n+\t\treturn;\n+\t}\n+\n+\tRequest *request = info.request;\n+\trequest->_d()->metadata().merge(metadata);\n+\tpendingCompleteBuffers_.pop_front();\n+\n+\tpipe()->completeRequest(request);\n+}\n+\n+RkISP2CameraConfiguration::RkISP2CameraConfiguration(const RkISP2CameraData *data)\n+\t: CameraConfiguration(), data_(data)\n+{\n+}\n+\n+CameraConfiguration::Status RkISP2CameraConfiguration::validate()\n+{\n+\tconst CameraSensor *sensor = data_->sensor_;\n+\tstd::vector<unsigned int> mbusCodes;\n+\tStatus status = Valid;\n+\n+\tif (config_.empty())\n+\t\treturn Invalid;\n+\n+\t/*\n+\t * Make sure that if a sensor configuration has been requested it\n+\t * is valid.\n+\t */\n+\tif (sensorConfig) {\n+\t\tif (!sensorConfig->isValid()) {\n+\t\t\tLOG(RkISP2, Error)\n+\t\t\t\t<< \"Invalid sensor configuration request\";\n+\n+\t\t\treturn Invalid;\n+\t\t}\n+\n+\t\tunsigned int bitDepth = sensorConfig->bitDepth;\n+\t\tif (bitDepth != 8 && bitDepth != 10 && bitDepth != 12) {\n+\t\t\tLOG(RkISP2, Error)\n+\t\t\t\t<< \"Invalid sensor configuration bit depth\";\n+\n+\t\t\treturn Invalid;\n+\t\t}\n+\t}\n+\n+\t/* \\todo Support self path */\n+\tif (config_.size() != 1) {\n+\t\tconfig_.resize(1);\n+\t\tstatus = Adjusted;\n+\t}\n+\n+\tStreamConfiguration &cfg = config_[0];\n+\n+\t/* \\todo Support YUV sensors */\n+\tconst PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat);\n+\tbool usingIsp = data_->usingIsp_;\n+\tif (usingIsp && info.colourEncoding == PixelFormatInfo::ColourEncodingRAW)\n+\t\tusingIsp = false;\n+\n+\tconst Size &maxSize = usingIsp ? ispMaxSize : vicapMaxSize;\n+\n+\tif (!usingIsp) {\n+\t\tif (!rawFormats.count(cfg.pixelFormat)) {\n+\t\t\tcfg.pixelFormat = formats::SRGGB10;\n+\t\t\tstatus = Adjusted;\n+\t\t}\n+\n+\t\tunsigned int mbusCode = rawFormats.at(cfg.pixelFormat);\n+\t\tauto sizes = sensor->sizes(mbusCode);\n+\n+\t\tSize bestSize;\n+\t\tfor (const Size &s : sizes) {\n+\t\t\t/* Ignore smaller sizes. */\n+\t\t\tif (s.width < cfg.size.width ||\n+\t\t\t s.height < cfg.size.height)\n+\t\t\t\tcontinue;\n+\n+\t\t\t/* Make sure the width stays in the limits. */\n+\t\t\tif (s.width > maxSize.width)\n+\t\t\t\tcontinue;\n+\n+\t\t\tbestSize = s;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (bestSize.isNull()) {\n+\t\t\tLOG(RkISP2, Error) << \"Unable to find a suitable sensor format\";\n+\t\t\treturn Invalid;\n+\t\t}\n+\n+\t\tif (bestSize != cfg.size)\n+\t\t\tstatus = Adjusted;\n+\t\tcfg.size = bestSize;\n+\n+\t\tmbusCodes = { mbusCode };\n+\t\tsensorFormat_ = sensor->getFormat(mbusCodes, cfg.size, maxSize);\n+\n+\t\tASSERT(sensorFormat_.code == mbusCode);\n+\t\tASSERT(sensorFormat_.size == cfg.size);\n+\n+\t\treturn status;\n+\t}\n+\n+\tif (!formatToMediaBus.count(cfg.pixelFormat)) {\n+\t\tcfg.pixelFormat = formats::UYVY;\n+\t\tstatus = Adjusted;\n+\t}\n+\n+\t/* \\todo Adjust sizes a bit better */\n+\tif (cfg.size > ispMaxSize) {\n+\t\tcfg.size = ispMaxSize;\n+\t\tstatus = Adjusted;\n+\t}\n+\tV4L2DeviceFormat format;\n+\tformat.fourcc = data_->video_->toV4L2PixelFormat(cfg.pixelFormat);\n+\tformat.size = cfg.size;\n+\n+\tint ret = data_->mainPath_->tryFormat(&format);\n+\tif (ret)\n+\t\treturn Invalid;\n+\n+\tcfg.bufferCount = 4;\n+\n+\tstd::transform(rawFormats.begin(), rawFormats.end(),\n+\t\t std::back_inserter(mbusCodes),\n+\t\t [](const auto &value) { return value.second; });\n+\tsensorFormat_ = sensor->getFormat(mbusCodes, cfg.size, maxSize);\n+\tif (sensorFormat_.size.isNull())\n+\t\tstatus = Invalid;\n+\n+\treturn status;\n+}\n+\n+PipelineHandlerRkISP2::PipelineHandlerRkISP2(CameraManager *manager)\n+\t: PipelineHandler(manager)\n+{\n+}\n+\n+Size PipelineHandlerRkISP2::clampSensorSize(RkISP2CameraData *data, unsigned int mbus,\n+\t\t\t\t\t const Size &maxSize)\n+{\n+\tconst std::vector<Size> &sizes = data->sensor_->sizes(mbus);\n+\n+\tfor (auto it = sizes.rbegin(); it != sizes.rend(); ++it) {\n+\t\tif (it->width <= maxSize.width &&\n+\t\t it->height <= maxSize.height)\n+\t\t\treturn *it;\n+\t}\n+\n+\treturn *sizes.begin();\n+}\n+\n+std::unique_ptr<CameraConfiguration>\n+PipelineHandlerRkISP2::generateConfiguration(Camera *camera,\n+\t\t\t\t\t Span<const StreamRole> roles)\n+{\n+\tRkISP2CameraData *data = cameraData(camera);\n+\tauto config = std::make_unique<RkISP2CameraConfiguration>(data);\n+\tif (roles.empty())\n+\t\treturn config;\n+\n+\tif (roles.size() > 1) {\n+\t\tLOG(RkISP2, Error) << \"Too many roles requested\";\n+\t\treturn config;\n+\t}\n+\n+\tconst StreamRole &role = roles[0];\n+\n+\tbool isRaw = !data->usingIsp_ || role == StreamRole::Raw;\n+\tSize maxSize = isRaw ? vicapMaxSize : ispMaxSize;\n+\n+\tSize defaultSize = { 1920, 1080 };\n+\tunsigned int defaultMbusCode = MEDIA_BUS_FMT_SRGGB10_1X10;\n+\tPixelFormat rawFormat = data->getSensorFormat(defaultMbusCode,\n+\t\t\t\t\t\t defaultSize, maxSize);\n+\n+\t/* Enumerate formats */\n+\tstd::vector<SizeRange> sizes = { { Size(32, 32), maxSize } };\n+\tauto makeStream = [sizes](auto const &pair) {\n+\t\treturn std::make_pair(pair.first, sizes);\n+\t};\n+\tstd::map<PixelFormat, std::vector<SizeRange>> streamFormats;\n+\tstd::transform(formatToMediaBus.begin(), formatToMediaBus.end(),\n+\t\t std::inserter(streamFormats, streamFormats.end()),\n+\t\t makeStream);\n+\tstd::transform(rawFormats.begin(), rawFormats.end(),\n+\t\t std::inserter(streamFormats, streamFormats.end()),\n+\t\t makeStream);\n+\n+\tStreamFormats formats(streamFormats);\n+\tStreamConfiguration cfg(formats);\n+\t/* UYVY is always supported by this ISP */\n+\tcfg.pixelFormat = isRaw ? rawFormat : formats::UYVY;\n+\tcfg.size = clampSensorSize(data, defaultMbusCode, maxSize);\n+\tcfg.colorSpace = isRaw ? ColorSpace::Raw : ColorSpace::Sycc;\n+\tcfg.bufferCount = 4;\n+\n+\tconfig->addConfiguration(cfg);\n+\n+\tconfig->validate();\n+\n+\treturn config;\n+}\n+\n+int PipelineHandlerRkISP2::configure(Camera *camera,\n+\t\t\t\t CameraConfiguration *c)\n+{\n+\tRkISP2CameraData *data = cameraData(camera);\n+\tRkISP2CameraConfiguration *config =\n+\t\tstatic_cast<RkISP2CameraConfiguration *>(c);\n+\t/* \\todo Support multiple streams */\n+\tStreamConfiguration &cfg = config->at(0);\n+\tint ret;\n+\n+\tconst PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat);\n+\tdata->isRaw_ = info.colourEncoding == PixelFormatInfo::ColourEncodingRAW;\n+\n+\tV4L2SubdeviceFormat format = config->sensorFormat();\n+\tLOG(RkISP2, Debug) << \"Configuring sensor with \" << format;\n+\n+\tif (config->sensorConfig)\n+\t\tret = sensor_->applyConfiguration(*config->sensorConfig,\n+\t\t\t\t\t\t Transform::Identity, &format);\n+\telse\n+\t\tret = sensor_->setFormat(&format);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tLOG(RkISP2, Debug) << \"Sensor configured with \" << format;\n+\n+\tLOG(RkISP2, Debug) << \"Configuring CSI with : \" << format;\n+\tret = csi_->setFormat(0, &format);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tLOG(RkISP2, Debug) << \"Configuring VICAP with : \" << format;\n+\tret = cif_->setFormat(0, &format);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = cif_->setFormat(1, &format);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tSize maxSize = data->isRaw_ ? vicapMaxSize : ispMaxSize;\n+\tPixelFormat vicapPixelFormat =\n+\t\tdata->getSensorFormat(format.code, format.size, maxSize);\n+\n+\tV4L2DeviceFormat vicapOutputFormat;\n+\tvicapOutputFormat.fourcc = video_->toV4L2PixelFormat(vicapPixelFormat);\n+\tvicapOutputFormat.size = format.size;\n+\n+\tLOG(RkISP2, Debug) << \"Configuring VICAP capture node with : \" << vicapOutputFormat;\n+\tret = data->video_->setFormat(&vicapOutputFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (!data->usingIsp_ || data->isRaw_) {\n+\t\tcfg.setStream(&data->stream_);\n+\t\tcfg.stride = vicapOutputFormat.planes[0].bpl;\n+\t\treturn 0;\n+\t}\n+\n+\t/*\n+\t * \\todo Figure out if these should go in the pipeline handler\n+\t * or camera data\n+\t */\n+\tLOG(RkISP2, Debug) << \"Configuring rawrd0 with: \" << vicapOutputFormat;\n+\tret = rawrd_->setFormat(&vicapOutputFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tLOG(RkISP2, Debug) << \"Configuring ISP input with: \" << format;\n+\tret = isp_->setFormat(0, &format);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tRectangle ispInCrop(0, 0, format.size);\n+\tLOG(RkISP2, Debug) << \"Configuring ISP sink crop with: \" << ispInCrop;\n+\tret = isp_->setSelection(0, V4L2_SEL_TGT_CROP, &ispInCrop);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tformat.code = formatToMediaBus.at(cfg.pixelFormat);\n+\tformat.size = cfg.size;\n+\tLOG(RkISP2, Debug) << \"Configuring ISP output with: \" << format;\n+\tret = isp_->setFormat(1, &format);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tRectangle ispOutCrop(0, 0, cfg.size);\n+\tLOG(RkISP2, Debug) << \"Configuring ISP source crop with: \" << ispOutCrop;\n+\tret = isp_->setSelection(2, V4L2_SEL_TGT_CROP, &ispOutCrop);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tV4L2DeviceFormat outputFormat;\n+\toutputFormat.fourcc = mainPath_->toV4L2PixelFormat(cfg.pixelFormat);\n+\toutputFormat.size = cfg.size;\n+\n+\tLOG(RkISP2, Debug) << \"Configuring main path with: \" << outputFormat;\n+\tret = mainPath_->setFormat(&outputFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (outputFormat.size != cfg.size ||\n+\t outputFormat.fourcc != data->mainPath_->toV4L2PixelFormat(cfg.pixelFormat)) {\n+\t\tLOG(RkISP2, Error)\n+\t\t\t<< \"Unable to configure capture in \" << cfg.toString();\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tcfg.setStream(&data->stream_);\n+\tcfg.stride = outputFormat.planes[0].bpl;\n+\n+\tV4L2DeviceFormat paramFormat;\n+\tparamFormat.fourcc = V4L2PixelFormat(V4L2_META_FMT_RKISP2_PARAMS);\n+\tret = param_->setFormat(¶mFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tV4L2DeviceFormat statFormat;\n+\tstatFormat.fourcc = V4L2PixelFormat(V4L2_META_FMT_RKISP2_STATS);\n+\tret = stat_->setFormat(&statFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tIPACameraSensorInfo sensorInfo;\n+\tret = data->sensor_->sensorInfo(&sensorInfo);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tint colorSpaceEncoding = -1;\n+\tint colorSpaceRange = -1;\n+\tif (cfg.colorSpace) {\n+\t\tcolorSpaceEncoding = static_cast<int>(cfg.colorSpace->ycbcrEncoding);\n+\t\tcolorSpaceRange = static_cast<int>(cfg.colorSpace->range);\n+\t}\n+\n+\t/* Inform IPA of stream configuration and sensor controls. */\n+\tipa::rkisp2::IPAConfigInfo ipaConfig{ sensorInfo,\n+\t\t\t\t\t data->sensor_->controls(),\n+\t\t\t\t\t colorSpaceEncoding,\n+\t\t\t\t\t colorSpaceRange };\n+\n+\tret = data->ipa_->configure(ipaConfig, &data->ipaControls_);\n+\tif (ret) {\n+\t\tLOG(RkISP2, Error) << \"failed configuring IPA (\" << ret << \")\";\n+\t\treturn ret;\n+\t}\n+\n+\treturn updateControls(data);\n+}\n+\n+int PipelineHandlerRkISP2::exportFrameBuffers(Camera *camera, Stream *stream,\n+\t\t\t\t\t std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n+{\n+\tunsigned int count = stream->configuration().bufferCount;\n+\tRkISP2CameraData *data = cameraData(camera);\n+\n+\tif (!data->usingIsp_ || data->isRaw_)\n+\t\treturn data->video_->exportBuffers(count, buffers);\n+\n+\treturn data->mainPath_->exportBuffers(count, buffers);\n+}\n+\n+int PipelineHandlerRkISP2::start(Camera *camera,\n+\t\t\t\t const ControlList *controls)\n+{\n+\tRkISP2CameraData *data = cameraData(camera);\n+\tunsigned int count = data->stream_.configuration().bufferCount;\n+\tbool useMP = data->usingIsp_ && !data->isRaw_;\n+\tutils::ScopeExitActions actions;\n+\tint ret;\n+\n+\tdata->frame_ = 0;\n+\n+\tLOG(RkISP2, Debug) << (useMP ? \"Using\" : \"Not using\") << \" main path\";\n+\n+\tif (useMP) {\n+\t\t/* Allocate buffers for params and stats */\n+\t\tret = allocateBuffers(camera);\n+\t\tif (ret) {\n+\t\t\tLOG(RkISP2, Error) << \"Failed to allocate buffers\";\n+\t\t\treturn ret;\n+\t\t}\n+\t\tactions += [&]() { freeBuffers(camera); };\n+\n+\t\t/* \\todo Support start controls */\n+\t\tret = data->ipa_->start();\n+\t\tif (ret) {\n+\t\t\tLOG(RkISP2, Error)\n+\t\t\t\t<< \"Failed to start IPA \" << camera->id();\n+\t\t\treturn ret;\n+\t\t}\n+\t\tactions += [&]() { data->ipa_->stop(); };\n+\n+\t\tret = data->param_->streamOn();\n+\t\tif (ret) {\n+\t\t\tLOG(RkISP2, Error)\n+\t\t\t\t<< \"Failed to start parameters \" << camera->id();\n+\t\t\treturn ret;\n+\t\t}\n+\t\tactions += [&]() { data->param_->streamOff(); };\n+\t}\n+\n+\tret = data->video_->importBuffers(kRkISP2MinBufferCount);\n+\tif (ret < 0) {\n+\t\tLOG(RkISP2, Error) << \"Failed to import buffers to vicap\";\n+\t\treturn ret;\n+\t}\n+\n+\tactions += [&]() { data->video_->releaseBuffers(); };\n+\n+\tif (useMP) {\n+\t\tret = data->rawrd_->importBuffers(count);\n+\t\tif (ret < 0) {\n+\t\t\tLOG(RkISP2, Error) << \"Failed to import buffers to rawrd\";\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tactions += [&]() { data->rawrd_->releaseBuffers(); };\n+\n+\t\tret = data->mainPath_->importBuffers(count);\n+\t\tif (ret < 0) {\n+\t\t\tLOG(RkISP2, Error) << \"Failed to import buffers to main path\";\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tactions += [&]() { data->mainPath_->releaseBuffers(); };\n+\n+\t\tauto queueBuffers = [&](const std::vector<std::unique_ptr<FrameBuffer>> &buffers,\n+\t\t\t\t\tV4L2VideoDevice *device, std::string_view name) {\n+\t\t\tfor (const std::unique_ptr<FrameBuffer> &buffer : buffers) {\n+\t\t\t\tret = device->queueBuffer(buffer.get());\n+\t\t\t\tif (ret < 0) {\n+\t\t\t\t\tLOG(RkISP2, Warning)\n+\t\t\t\t\t\t<< \"Failed to queue buffer \"\n+\t\t\t\t\t\t<< &buffer << \" to \" << name\n+\t\t\t\t\t\t<< \": \" << ret;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t};\n+\n+\t\tqueueBuffers(data->internalBuffers_, data->video_, \"vicap\");\n+\t\tqueueBuffers(data->statBuffers_, data->stat_, \"stat\");\n+\t}\n+\n+\tret = data->video_->streamOn();\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tactions += [&]() { data->video_->streamOff(); };\n+\n+\tif (useMP) {\n+\t\tret = data->rawrd_->streamOn();\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\n+\t\tactions += [&]() { data->rawrd_->streamOff(); };\n+\n+\t\tret = data->stat_->streamOn();\n+\t\tif (ret) {\n+\t\t\tLOG(RkISP2, Error)\n+\t\t\t\t<< \"Failed to start stats \" << camera->id();\n+\t\t\treturn ret;\n+\t\t}\n+\t\tactions += [&]() { data->stat_->streamOff(); };\n+\n+\t\tret = data->mainPath_->streamOn();\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\n+\t\tactions += [&]() { data->mainPath_->streamOff(); };\n+\t}\n+\n+\tif (controls) {\n+\t\tret = processControls(data, *controls);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\t}\n+\n+\tif (useMP)\n+\t\tdata->isp_->setFrameStartEnabled(true);\n+\n+\tactions.release();\n+\treturn 0;\n+}\n+\n+void PipelineHandlerRkISP2::stopDevice(Camera *camera)\n+{\n+\tRkISP2CameraData *data = cameraData(camera);\n+\tbool useMP = data->usingIsp_ && !data->isRaw_;\n+\n+\tif (useMP)\n+\t\tdata->isp_->setFrameStartEnabled(false);\n+\n+\tdata->video_->streamOff();\n+\tdata->video_->releaseBuffers();\n+\n+\tif (!useMP)\n+\t\treturn;\n+\n+\tdata->ipa_->stop();\n+\n+\tdata->rawrd_->streamOff();\n+\tdata->rawrd_->releaseBuffers();\n+\n+\tdata->mainPath_->streamOff();\n+\tdata->mainPath_->releaseBuffers();\n+\n+\tdata->stat_->streamOff();\n+\tdata->stat_->releaseBuffers();\n+\n+\tdata->param_->streamOff();\n+\n+\tdata->internalBuffers_.clear();\n+\tdata->statBuffers_.clear();\n+\tfreeBuffers(camera);\n+}\n+\n+int PipelineHandlerRkISP2::queueRequestDevice(Camera *camera, Request *request)\n+{\n+\tRkISP2CameraData *data = cameraData(camera);\n+\tFrameBuffer *buffer = request->findBuffer(&data->stream_);\n+\tif (!buffer) {\n+\t\tLOG(RkISP2, Error)\n+\t\t\t<< \"Attempt to queue request with invalid stream\";\n+\t\treturn -ENOENT;\n+\t}\n+\n+\tint ret = processControls(data, request->controls());\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tif (!data->usingIsp_ || data->isRaw_)\n+\t\treturn data->video_->queueBuffer(buffer);\n+\n+\tint correction = data->syncHelper_.correction();\n+\tdata->frame_ += correction;\n+\tdata->syncHelper_.pushCorrection(correction);\n+\n+\tdata->pendingCompleteRequests_.push_back({ request, data->frame_, true });\n+\n+\tdata->ipa_->queueRequest(data->frame_, request->controls());\n+\tdata->computeParamBuffers(data->frame_);\n+\tdata->frame_++;\n+\n+\treturn data->mainPath_->queueBuffer(buffer);\n+}\n+\n+int PipelineHandlerRkISP2::updateControls(RkISP2CameraData *data)\n+{\n+\tControlInfoMap::Map controls;\n+\n+\t/* Add the pipeline handler registered controls to list of camera controls. */\n+\tfor (const auto &phControl : data->controlInfo_)\n+\t\tcontrols[phControl.first] = phControl.second;\n+\n+\t/* Add the IPA registered controls to list of camera controls. */\n+\tfor (const auto &ipaControl : data->ipaControls_)\n+\t\tcontrols[ipaControl.first] = ipaControl.second;\n+\n+\tdata->controlInfo_ = ControlInfoMap(std::move(controls),\n+\t\t\t\t\t controls::controls);\n+\n+\treturn 0;\n+}\n+\n+bool PipelineHandlerRkISP2::createCamera(bool usingIsp)\n+{\n+\tstd::unique_ptr<RkISP2CameraData> data =\n+\t\tstd::make_unique<RkISP2CameraData>(this, media_.get());\n+\tif (data->init(usingIsp, sensor_.get(), video_.get(), rawrd_.get(),\n+\t\t isp_.get(), mainPath_.get(), param_.get(), stat_.get())) {\n+\t\tLOG(RkISP2, Error) << \"Failed to initialize data\";\n+\t\treturn false;\n+\t}\n+\n+\t/* Initialize the camera properties. */\n+\tdata->properties_ = data->sensor_->properties();\n+\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\t{ V4L2_CID_VBLANK, { delays.vblankDelay, true } },\n+\t};\n+\n+\tif (usingIsp) {\n+\t\tdata->delayedCtrls_ =\n+\t\t\tstd::make_unique<DelayedControls>(data->sensor_->device(),\n+\t\t\t\t\t\t\t params);\n+\t\tisp_->frameStart.connect(data->delayedCtrls_.get(),\n+\t\t\t\t\t &DelayedControls::applyControls);\n+\n+\t\tint ret = data->loadIPA();\n+\t\tif (ret) {\n+\t\t\tLOG(RkISP2, Error) << \"Failed to load IPA\";\n+\t\t\treturn false;\n+\t\t}\n+\n+\t\tupdateControls(data.get());\n+\n+\t\tdata->paramQueue_ =\n+\t\t\tstd::make_unique<BufferQueue>(std::make_unique<BufferQueueDelegate<V4L2VideoDevice>>(param_.get()),\n+\t\t\t\t\t\t BufferQueue::PrepareStage, \"Params\");\n+\t}\n+\n+\tconst std::string &id = data->sensor_->id();\n+\tstd::set<Stream *> streams{ &data->stream_ };\n+\tstd::shared_ptr<Camera> camera =\n+\t\tCamera::create(std::move(data), id, streams);\n+\tregisterCamera(std::move(camera));\n+\n+\tLOG(RkISP2, Debug)\n+\t\t<< \"RkISP2 device registered \"\n+\t\t<< (usingIsp ? \"with\" : \"without\") << \" ISP\";\n+\n+\treturn true;\n+}\n+\n+bool PipelineHandlerRkISP2::match(DeviceEnumerator *enumerator)\n+{\n+\tDeviceMatch dm(\"rockchip-cif\");\n+\t/* \\todo Generalize this for the other csi ports */\n+\t/*\n+\t * I think we will have one camera per csi port, and then 4 streams\n+\t * each that correspond to the 4 channels. Not sure how to handle\n+\t * routing to the ISP though. For now we'll just assume one camera.\n+\t */\n+\tdm.add(\"rkcif-mipi2\");\n+\t/* \\todo Generalize this for the other channels */\n+\tdm.add(\"rkcif-mipi2-id0\");\n+\tdm.add(\"dw-mipi-csi2rx fdd30000.csi\");\n+\n+\tmedia_ = acquireMediaDevice(enumerator, dm);\n+\tif (!media_)\n+\t\treturn false;\n+\n+\tcsi_ = V4L2Subdevice::fromEntityName(media_.get(), \"dw-mipi-csi2rx fdd30000.csi\");\n+\tif (!csi_ || csi_->open() < 0) {\n+\t\tLOG(RkISP2, Error) << \"Failed to open csi\";\n+\t\treturn false;\n+\t}\n+\n+\tcif_ = V4L2Subdevice::fromEntityName(media_.get(), \"rkcif-mipi2\");\n+\tif (!cif_ || cif_->open() < 0) {\n+\t\tLOG(RkISP2, Error) << \"Failed to open cif\";\n+\t\treturn false;\n+\t}\n+\n+\t/* \\todo Support multiple streams */\n+\tvideo_ = V4L2VideoDevice::fromEntityName(media_.get(), \"rkcif-mipi2-id0\");\n+\tif (!video_ || video_->open() < 0) {\n+\t\tLOG(RkISP2, Error) << \"Failed to open capture device\";\n+\t\treturn false;\n+\t}\n+\n+\tfor (MediaEntity *entity : media_->locateEntities(MEDIA_ENT_F_CAM_SENSOR)) {\n+\t\tLOG(RkISP2, Debug) << \"Identified \" << entity->name();\n+\t\tsensor_ = CameraSensorFactoryBase::create(entity);\n+\t\t/* Just get the first sensor for now */\n+\t\tif (sensor_)\n+\t\t\tbreak;\n+\t}\n+\n+\tif (!sensor_) {\n+\t\tLOG(RkISP2, Error) << \"Failed to find sensor\";\n+\t\treturn false;\n+\t}\n+\n+\tconst GlobalConfiguration &configuration = cameraManager()->_d()->configuration();\n+\tbool usingIsp = configuration.configuration()[\"pipelines\"][\"rkisp2\"][\"isp_enable\"].get<bool>(true);\n+\tif (!usingIsp) {\n+\t\tLOG(RkISP2, Info) << \"ISP disabled in configuration file\";\n+\t\treturn createCamera(usingIsp);\n+\t}\n+\n+\t/* Match ISP */\n+\n+\tDeviceMatch dmIsp(\"rkisp2\");\n+\tdmIsp.add(\"rkisp2_isp\");\n+\t/* \\todo Generalize this for the other channels */\n+\tdmIsp.add(\"rkisp2_rawrd0\");\n+\t/* \\todo Support self path */\n+\tdmIsp.add(\"rkisp2_mainpath\");\n+\n+\tispMedia_ = acquireMediaDevice(enumerator, dmIsp);\n+\tif (!ispMedia_) {\n+\t\tusingIsp = false;\n+\t\tLOG(RkISP2, Debug) << \"ISP not found\";\n+\t\treturn createCamera(usingIsp);\n+\t}\n+\n+\t/* \\todo Support the other rawrd nodes */\n+\trawrd_ = V4L2VideoDevice::fromEntityName(ispMedia_.get(), \"rkisp2_rawrd0\");\n+\tif (!rawrd_ || rawrd_->open() < 0) {\n+\t\tLOG(RkISP2, Error) << \"Failed to open rkisp2 rawrd device\";\n+\t\treturn false;\n+\t}\n+\n+\tisp_ = V4L2Subdevice::fromEntityName(ispMedia_.get(), \"rkisp2_isp\");\n+\tif (!isp_ || isp_->open() < 0) {\n+\t\tLOG(RkISP2, Error) << \"Failed to open rkisp2 isp\";\n+\t\treturn false;\n+\t}\n+\n+\t/* \\todo Support self path */\n+\tmainPath_ = V4L2VideoDevice::fromEntityName(ispMedia_.get(), \"rkisp2_mainpath\");\n+\tif (!mainPath_ || mainPath_->open() < 0) {\n+\t\tLOG(RkISP2, Error) << \"Failed to open rkisp2 main path\";\n+\t\treturn false;\n+\t}\n+\n+\tparam_ = V4L2VideoDevice::fromEntityName(ispMedia_.get(), \"rkisp2_params\");\n+\tif (!param_ || param_->open() < 0) {\n+\t\tLOG(RkISP2, Error) << \"Failed to open rkisp2 params\";\n+\t\treturn false;\n+\t}\n+\n+\tstat_ = V4L2VideoDevice::fromEntityName(ispMedia_.get(), \"rkisp2_stats\");\n+\tif (!stat_ || stat_->open() < 0) {\n+\t\tLOG(RkISP2, Error) << \"Failed to open rkisp2 stats\";\n+\t\treturn false;\n+\t}\n+\n+\treturn createCamera(true);\n+}\n+\n+int PipelineHandlerRkISP2::processControls(RkISP2CameraData *data, const ControlList &ctrls)\n+{\n+\tconst auto &testPattern = ctrls.get(controls::draft::TestPatternMode);\n+\tif (testPattern)\n+\t\tdata->sensor_->setTestPatternMode(static_cast<controls::draft::TestPatternModeEnum>(*testPattern));\n+\n+\treturn 0;\n+}\n+\n+/* This is only called when using the ISP */\n+int PipelineHandlerRkISP2::allocateBuffers(Camera *camera)\n+{\n+\tRkISP2CameraData *data = cameraData(camera);\n+\tunsigned int ipaBufferId = 1;\n+\tutils::ScopeExitActions actions;\n+\n+\tint ret = data->video_->exportBuffers(kRkISP2MinBufferCount, &data->internalBuffers_);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tactions += [&]() { data->video_->releaseBuffers(); };\n+\n+\tret = data->stat_->allocateBuffers(kRkISP2MinBufferCount, &data->statBuffers_);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tactions += [&]() { data->stat_->releaseBuffers(); };\n+\n+\tret = data->paramQueue_->allocateBuffers(kRkISP2MinBufferCount);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tactions += [&]() { data->paramQueue_->releaseBuffers(); };\n+\n+\tauto pushBuffers = [&](const std::vector<std::unique_ptr<FrameBuffer>> &buffers) {\n+\t\tfor (const std::unique_ptr<FrameBuffer> &buffer : buffers) {\n+\t\t\tSpan<const FrameBuffer::Plane> planes = buffer->planes();\n+\n+\t\t\tbuffer->setCookie(ipaBufferId++);\n+\t\t\tdata->ipaBuffers_.emplace_back(buffer->cookie(),\n+\t\t\t\t\t\t std::vector<FrameBuffer::Plane>{ planes.begin(),\n+\t\t\t\t\t\t\t\t\t\t\tplanes.end() });\n+\t\t}\n+\t};\n+\n+\tpushBuffers(data->paramQueue_->buffers());\n+\tpushBuffers(data->statBuffers_);\n+\n+\tdata->ipa_->mapBuffers(data->ipaBuffers_);\n+\n+\tactions.release();\n+\treturn 0;\n+}\n+\n+int PipelineHandlerRkISP2::freeBuffers(Camera *camera)\n+{\n+\tRkISP2CameraData *data = cameraData(camera);\n+\n+\tstd::vector<unsigned int> ids;\n+\tfor (IPABuffer &ipabuf : data->ipaBuffers_)\n+\t\tids.push_back(ipabuf.id);\n+\n+\tdata->ipa_->unmapBuffers(ids);\n+\tdata->ipaBuffers_.clear();\n+\n+\tdata->paramQueue_->releaseBuffers();\n+\n+\treturn 0;\n+}\n+\n+REGISTER_PIPELINE_HANDLER(PipelineHandlerRkISP2, \"rkisp2\")\n+\n+} /* namespace libcamera */\n", "prefixes": [ "RFC", "06/19" ] }