Patch Detail
Show a patch.
GET /api/1.1/patches/22450/?format=api
{ "id": 22450, "url": "https://patchwork.libcamera.org/api/1.1/patches/22450/?format=api", "web_url": "https://patchwork.libcamera.org/patch/22450/", "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": "<20241227105840.159559-3-keke.li@amlogic.com>", "date": "2024-12-27T10:58:40", "name": "[v2,2/2] libcamera: ipa: Add C3 ISP IPA", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "ab4bd075d64cff4085e0589ebd992347b699d6fe", "submitter": { "id": 217, "url": "https://patchwork.libcamera.org/api/1.1/people/217/?format=api", "name": "Keke Li", "email": "keke.li@amlogic.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/22450/mbox/", "series": [ { "id": 4926, "url": "https://patchwork.libcamera.org/api/1.1/series/4926/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4926", "date": "2024-12-27T10:58:38", "name": "Add Amlogic C3 ISP pipeline handler and IPA", "version": 2, "mbox": "https://patchwork.libcamera.org/series/4926/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/22450/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/22450/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 042CFC32C8\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 27 Dec 2024 10:59:09 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 539E3684D0;\n\tFri, 27 Dec 2024 11:59:06 +0100 (CET)", "from mail-sh.amlogic.com (unknown [58.32.228.46])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 05E21684C0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 27 Dec 2024 11:58:54 +0100 (CET)", "from droid10.amlogic.com (10.18.11.213) by mail-sh.amlogic.com\n\t(10.18.11.5) with Microsoft SMTP Server id 15.1.2507.39;\n\tFri, 27 Dec 2024 18:58:53 +0800" ], "From": "Keke Li <keke.li@amlogic.com>", "To": "<libcamera-devel@lists.libcamera.org>", "CC": "<kieran.bingham@ideasonboard.com>, <laurent.pinchart@ideasonboard.com>, \n\t<dan.scally@ideasonboard.com>, <jacopo.mondi@ideasonboard.com>, Keke Li\n\t<keke.li@amlogic.com>", "Subject": "[PATCH v2 2/2] libcamera: ipa: Add C3 ISP IPA", "Date": "Fri, 27 Dec 2024 18:58:40 +0800", "Message-ID": "<20241227105840.159559-3-keke.li@amlogic.com>", "X-Mailer": "git-send-email 2.29.0", "In-Reply-To": "<20241227105840.159559-1-keke.li@amlogic.com>", "References": "<20241227105840.159559-1-keke.li@amlogic.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Content-Type": "text/plain", "X-Originating-IP": "[10.18.11.213]", "X-BeenThere": "libcamera-devel@lists.libcamera.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "<libcamera-devel.lists.libcamera.org>", "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>", "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>", "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>", "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>", "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "The C3 ISP IPA is used to process 3A statistics\nand generate parameters for ISP hardware.\n\nSigned-off-by: Keke Li <keke.li@amlogic.com>\n---\n include/linux/c3-isp-config.h | 564 +++++++++++++++++++++++\n meson_options.txt | 2 +-\n src/ipa/c3-isp/algorithms/agc.cpp | 260 +++++++++++\n src/ipa/c3-isp/algorithms/agc.h | 50 ++\n src/ipa/c3-isp/algorithms/algorithm.h | 31 ++\n src/ipa/c3-isp/algorithms/awb.cpp | 257 +++++++++++\n src/ipa/c3-isp/algorithms/awb.h | 42 ++\n src/ipa/c3-isp/algorithms/blc.cpp | 102 ++++\n src/ipa/c3-isp/algorithms/blc.h | 40 ++\n src/ipa/c3-isp/algorithms/ccm.cpp | 86 ++++\n src/ipa/c3-isp/algorithms/ccm.h | 40 ++\n src/ipa/c3-isp/algorithms/csc.cpp | 64 +++\n src/ipa/c3-isp/algorithms/csc.h | 32 ++\n src/ipa/c3-isp/algorithms/meson.build | 10 +\n src/ipa/c3-isp/algorithms/post_gamma.cpp | 64 +++\n src/ipa/c3-isp/algorithms/post_gamma.h | 33 ++\n src/ipa/c3-isp/c3-isp.cpp | 386 ++++++++++++++++\n src/ipa/c3-isp/data/imx290.yaml | 30 ++\n src/ipa/c3-isp/data/meson.build | 9 +\n src/ipa/c3-isp/ipa_context.cpp | 251 ++++++++++\n src/ipa/c3-isp/ipa_context.h | 110 +++++\n src/ipa/c3-isp/meson.build | 32 ++\n src/ipa/c3-isp/module.h | 28 ++\n src/ipa/c3-isp/params.cpp | 127 +++++\n src/ipa/c3-isp/params.h | 133 ++++++\n 25 files changed, 2782 insertions(+), 1 deletion(-)\n create mode 100644 include/linux/c3-isp-config.h\n create mode 100644 src/ipa/c3-isp/algorithms/agc.cpp\n create mode 100644 src/ipa/c3-isp/algorithms/agc.h\n create mode 100644 src/ipa/c3-isp/algorithms/algorithm.h\n create mode 100755 src/ipa/c3-isp/algorithms/awb.cpp\n create mode 100755 src/ipa/c3-isp/algorithms/awb.h\n create mode 100644 src/ipa/c3-isp/algorithms/blc.cpp\n create mode 100644 src/ipa/c3-isp/algorithms/blc.h\n create mode 100644 src/ipa/c3-isp/algorithms/ccm.cpp\n create mode 100644 src/ipa/c3-isp/algorithms/ccm.h\n create mode 100644 src/ipa/c3-isp/algorithms/csc.cpp\n create mode 100644 src/ipa/c3-isp/algorithms/csc.h\n create mode 100644 src/ipa/c3-isp/algorithms/meson.build\n create mode 100644 src/ipa/c3-isp/algorithms/post_gamma.cpp\n create mode 100644 src/ipa/c3-isp/algorithms/post_gamma.h\n create mode 100644 src/ipa/c3-isp/c3-isp.cpp\n create mode 100644 src/ipa/c3-isp/data/imx290.yaml\n create mode 100644 src/ipa/c3-isp/data/meson.build\n create mode 100644 src/ipa/c3-isp/ipa_context.cpp\n create mode 100644 src/ipa/c3-isp/ipa_context.h\n create mode 100644 src/ipa/c3-isp/meson.build\n create mode 100644 src/ipa/c3-isp/module.h\n create mode 100644 src/ipa/c3-isp/params.cpp\n create mode 100644 src/ipa/c3-isp/params.h", "diff": "diff --git a/include/linux/c3-isp-config.h b/include/linux/c3-isp-config.h\nnew file mode 100644\nindex 00000000..ee673ed0\n--- /dev/null\n+++ b/include/linux/c3-isp-config.h\n@@ -0,0 +1,564 @@\n+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */\n+/*\n+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved\n+ */\n+\n+#ifndef _UAPI_C3_ISP_CONFIG_H_\n+#define _UAPI_C3_ISP_CONFIG_H_\n+\n+#include <linux/types.h>\n+\n+/*\n+ * Frames are split into zones of almost equal width and height - a zone is a\n+ * rectangular tile of a frame. The metering blocks within the ISP collect\n+ * aggregated statistics per zone.\n+ */\n+#define C3_ISP_AE_MAX_ZONES (17 * 15)\n+#define C3_ISP_AF_MAX_ZONES (17 * 15)\n+#define C3_ISP_AWB_MAX_ZONES (32 * 24)\n+\n+/* The maximum number of point on the diagonal of the frame for statistics */\n+#define C3_ISP_AE_MAX_PT_NUM 18\n+#define C3_ISP_AF_MAX_PT_NUM 18\n+#define C3_ISP_AWB_MAX_PT_NUM 33\n+\n+/**\n+ * struct c3_isp_awb_zone_stats - AWB statistics of a zone\n+ *\n+ * AWB zone stats is aligned with 8 bytes\n+ *\n+ * @rg: the ratio of R / G in a zone\n+ * @bg: the ratio of B / G in a zone\n+ * @pixel_sum: the total number of pixels used in a zone\n+ */\n+struct c3_isp_awb_zone_stats {\n+\t__u16 rg;\n+\t__u16 bg;\n+\t__u32 pixel_sum;\n+};\n+\n+/**\n+ * struct c3_isp_awb_stats - Auto white balance statistics information.\n+ *\n+ * AWB statistical information of all zones.\n+ *\n+ * @stats: array of auto white balance statistics\n+ */\n+struct c3_isp_awb_stats {\n+\tstruct c3_isp_awb_zone_stats stats[C3_ISP_AWB_MAX_ZONES];\n+} __attribute__((aligned(16)));\n+\n+/**\n+ * struct c3_isp_ae_zone_stats - AE statistics of a zone\n+ *\n+ * AE zone stats is aligned with 8 bytes.\n+ * This is a 5-bin histogram and the total sum is normalized to 0xffff.\n+ * So hist2 = 0xffff - (hist0 + hist1 + hist3 + hist4)\n+ *\n+ * @hist0: the global normalized pixel count for bin 0\n+ * @hist1: the global normalized pixel count for bin 1\n+ * @hist3: the global normalized pixel count for bin 3\n+ * @hist4: the global normalized pixel count for bin 4\n+ */\n+struct c3_isp_ae_zone_stats {\n+\t__u16 hist0;\n+\t__u16 hist1;\n+\t__u16 hist3;\n+\t__u16 hist4;\n+};\n+\n+/**\n+ * struct c3_isp_ae_stats - Exposure statistics information\n+ *\n+ * AE statistical information consists of all blocks information and a 1024-bin\n+ * histogram.\n+ *\n+ * @stats: array of auto exposure block statistics\n+ * @reserved: undefined buffer space\n+ * @hist: a 1024-bin histogram for the entire image\n+ */\n+struct c3_isp_ae_stats {\n+\tstruct c3_isp_ae_zone_stats stats[C3_ISP_AE_MAX_ZONES];\n+\t__u32 reserved[2];\n+\t__u32 hist[1024];\n+} __attribute__((aligned(16)));\n+\n+/**\n+ * struct c3_isp_af_zone_stats - AF statistics of a zone\n+ *\n+ * AF zone stats is aligned with 8 bytes.\n+ * The zonal accumulated contrast metrics are stored in floating point format\n+ * with 16 bits mantissa and 5 or 6 bits exponent. Apart from contrast metrics\n+ * we accumulate squared image and quartic image data over the zone.\n+ *\n+ * @i2_mat: the mantissa of zonal squared image pixel sum\n+ * @i4_mat: the mantissa of zonal quartic image pixel sum\n+ * @e4_mat: the mantissa of zonal multi-directional quartic edge sum\n+ * @e4_exp: the exponent of zonal multi-directional quartic edge sum\n+ * @i2_exp: the exponent of zonal squared image pixel sum\n+ * @i4_exp: the exponent of zonal quartic image pixel sum\n+ */\n+struct c3_isp_af_zone_stats {\n+\t__u16 i2_mat;\n+\t__u16 i4_mat;\n+\t__u16 e4_mat;\n+\t__u16 e4_exp : 5;\n+\t__u16 i2_exp : 5;\n+\t__u16 i4_exp : 6;\n+};\n+\n+/**\n+ * struct c3_isp_af_stats - Auto Focus statistics information\n+ *\n+ * AF statistical information of each zone\n+ *\n+ * @stats: array of auto focus block statistics\n+ * @reserved: undefined buffer space\n+ */\n+struct c3_isp_af_stats {\n+\tstruct c3_isp_af_zone_stats stats[C3_ISP_AF_MAX_ZONES];\n+\t__u32 reserved[2];\n+} __attribute__((aligned(16)));\n+\n+/**\n+ * struct c3_isp_stats_info - V4L2_META_FMT_C3ISP_STATS\n+ *\n+ * Contains ISP statistics\n+ *\n+ * @awb: auto white balance stats\n+ * @ae: auto exposure stats\n+ * @af: auto focus stats\n+ */\n+struct c3_isp_stats_info {\n+\tstruct c3_isp_awb_stats awb;\n+\tstruct c3_isp_ae_stats ae;\n+\tstruct c3_isp_af_stats af;\n+};\n+\n+/**\n+ * enum c3_isp_params_buffer_version - C3 ISP parameters block versioning\n+ *\n+ * @C3_ISP_PARAMS_BUFFER_V0: First version of C3 ISP parameters block\n+ */\n+enum c3_isp_params_buffer_version {\n+\tC3_ISP_PARAMS_BUFFER_V0,\n+};\n+\n+/**\n+ * enum c3_isp_params_block_type - Enumeration of C3 ISP parameter blocks\n+ *\n+ * Each block configures a specific processing block of the C3 ISP.\n+ * The block type allows the driver to correctly interpret the parameters block\n+ * data.\n+ *\n+ * @C3_ISP_PARAMS_BLOCK_AWB_GAINS: White balance gains\n+ * @C3_ISP_PARAMS_BLOCK_AWB_CONFIG: AWB statistic format configuration for all\n+ * blocks that control how stats are generated\n+ * @C3_ISP_PARAMS_BLOCK_AE_CONFIG: AE statistic format configuration for all\n+ * blocks that control how stats are generated\n+ * @C3_ISP_PARAMS_BLOCK_AF_CONFIG: AF statistic format configuration for all\n+ * blocks that control how stats are generated\n+ * @C3_ISP_PARAMS_BLOCK_PST_GAMMA: post gamma parameters\n+ * @C3_ISP_PARAMS_BLOCK_CCM: Color correction matrix parameters\n+ * @C3_ISP_PARAMS_BLOCK_CSC: Color space conversion parameters\n+ * @C3_ISP_PARAMS_BLOCK_BLC: Black level correction parameters\n+ * @C3_ISP_PARAMS_BLOCK_SENTINEL: First non-valid block index\n+ */\n+enum c3_isp_params_block_type {\n+\tC3_ISP_PARAMS_BLOCK_AWB_GAINS,\n+\tC3_ISP_PARAMS_BLOCK_AWB_CONFIG,\n+\tC3_ISP_PARAMS_BLOCK_AE_CONFIG,\n+\tC3_ISP_PARAMS_BLOCK_AF_CONFIG,\n+\tC3_ISP_PARAMS_BLOCK_PST_GAMMA,\n+\tC3_ISP_PARAMS_BLOCK_CCM,\n+\tC3_ISP_PARAMS_BLOCK_CSC,\n+\tC3_ISP_PARAMS_BLOCK_BLC,\n+\tC3_ISP_PARAMS_BLOCK_SENTINEL\n+};\n+\n+#define C3_ISP_PARAMS_BLOCK_FL_DISABLE (1U << 0)\n+#define C3_ISP_PARAMS_BLOCK_FL_ENABLE (1U << 1)\n+\n+/**\n+ * struct c3_isp_params_block_header - C3 ISP parameter block header\n+ *\n+ * This structure represents the common part of all the ISP configuration\n+ * blocks. Each parameters block shall embed an instance of this structure type\n+ * as its first member, followed by the block-specific configuration data. The\n+ * driver inspects this common header to discern the block type and its size and\n+ * properly handle the block content by casting it to the correct block-specific\n+ * type.\n+ *\n+ * The @type field is one of the values enumerated by\n+ * :c:type:`c3_isp_params_block_type` and specifies how the data should be\n+ * interpreted by the driver. The @size field specifies the size of the\n+ * parameters block and is used by the driver for validation purposes. The\n+ * @flags field is a bitmask of per-block flags C3_ISP_PARAMS_FL*.\n+ *\n+ * When userspace wants to disable an ISP block the\n+ * C3_ISP_PARAMS_BLOCK_FL_DISABLED bit should be set in the @flags field. In\n+ * this case userspace may optionally omit the remainder of the configuration\n+ * block, which will be ignored by the driver.\n+ *\n+ * When a new configuration of an ISP block needs to be applied userspace\n+ * shall fully populate the ISP block and omit setting the\n+ * C3_ISP_PARAMS_BLOCK_FL_DISABLED bit in the @flags field.\n+ *\n+ * Userspace is responsible for correctly populating the parameters block header\n+ * fields (@type, @flags and @size) and the block-specific parameters.\n+ *\n+ * For example:\n+ *\n+ * .. code-block:: c\n+ *\n+ *\tvoid populate_pst_gamma(struct c3_isp_params_block_header *block) {\n+ *\t\tstruct c3_isp_params_pst_gamma *gamma =\n+ *\t\t\t(struct c3_isp_params_pst_gamma *)block;\n+ *\n+ *\t\tgamma->header.type = C3_ISP_PARAMS_BLOCK_PST_GAMMA;\n+ *\t\tgamma->header.flags = C3_ISP_PARAMS_BLOCK_FL_ENABLE;\n+ *\t\tgamma->header.size = sizeof(*gamma);\n+ *\n+ *\t\tfor (unsigned int i = 0; i < 129; i++)\n+ *\t\t\tgamma->pst_gamma_lut[i] = i;\n+ *\t}\n+ *\n+ * @type: The parameters block type from :c:type:`c3_isp_params_block_type`\n+ * @flags: A bitmask of block flags\n+ * @size: Size (in bytes) of the parameters block, including this header\n+ */\n+struct c3_isp_params_block_header {\n+\t__u16 type;\n+\t__u16 flags;\n+\t__u32 size;\n+};\n+\n+/**\n+ * struct c3_isp_params_awb_gains - Gains for auto-white balance\n+ *\n+ * This struct allows users to configure the gains for white balance.\n+ * There are four gain settings corresponding to each colour channel in\n+ * the bayer domain. All of the gains are stored in Q4.8 format.\n+ *\n+ * header.type should be set to C3_ISP_PARAMS_BLOCK_AWB_GAINS\n+ * from :c:type:`c3_isp_params_block_type`\n+ *\n+ * @header: The C3 ISP parameters block header\n+ * @gr_gain: Multiplier for Gr channel (Q4.8 format)\n+ * @r_gain: Multiplier for R channel (Q4.8 format)\n+ * @b_gain: Multiplier for B channel (Q4.8 format)\n+ * @gb_gain: Multiplier for Gb channel (Q4.8 format)\n+ */\n+struct c3_isp_params_awb_gains {\n+\tstruct c3_isp_params_block_header header;\n+\t__u16 gr_gain;\n+\t__u16 r_gain;\n+\t__u16 b_gain;\n+\t__u16 gb_gain;\n+} __attribute__((aligned(8)));\n+\n+/**\n+ * enum c3_isp_params_awb_tap_points - Tap points for the AWB statistics\n+ * @C3_ISP_AWB_STATS_TAP_FE: immediately after the optical frontend block\n+ * @C3_ISP_AWB_STATS_TAP_GE: immediately after the green equal block\n+ * @C3_ISP_AWB_STATS_TAP_BEFORE_WB: immediately before the white balance block\n+ * @C3_ISP_AWB_STATS_TAP_AFTER_WB: immediately after the white balance block\n+ */\n+enum c3_isp_params_awb_tap_point {\n+\tC3_ISP_AWB_STATS_TAP_OFE = 0,\n+\tC3_ISP_AWB_STATS_TAP_GE,\n+\tC3_ISP_AWB_STATS_TAP_BEFORE_WB,\n+\tC3_ISP_AWB_STATS_TAP_AFTER_WB,\n+};\n+\n+/**\n+ * struct c3_isp_params_awb_config - Stats settings for auto-white balance\n+ *\n+ * This struct allows the configuration of the statistics generated for auto\n+ * white balance.\n+ *\n+ * header.type should be set to C3_ISP_PARAMS_BLOCK_AWB_CONFIG\n+ * from :c:type:`c3_isp_params_block_type`\n+ *\n+ * @header: the C3 ISP parameters block header\n+ * @tap_point: the tap point from enum c3_isp_params_awb_tap_point\n+ * @satur_vald: AWB statistic over saturation control\n+ *\t\tvalue: 0: disable, 1: enable\n+ * @horiz_zones_num: active number of hotizontal zones [0..32]\n+ * @vert_zones_num: active number of vertical zones [0..24]\n+ * @rg_min: minimum R/G ratio (Q4.8 format)\n+ * @rg_max: maximum R/G ratio (Q4.8 format)\n+ * @bg_min: minimum B/G ratio (Q4.8 format)\n+ * @bg_max: maximum B/G ratio (Q4.8 format)\n+ * @rg_low: R/G ratio trim low (Q4.8 format)\n+ * @rg_high: R/G ratio trim hight (Q4.8 format)\n+ * @bg_low: B/G ratio trim low (Q4.8 format)\n+ * @bg_high: B/G ratio trim high (Q4.8 format)\n+ * @zone_weight: array of weights for AWB statistics zones [0..15]\n+ * @horiz_cood: the horizontal coordinate of points on the diagonal [0..2888]\n+ * @vert_cood: the vertical coordinate of points on the diagonal [0..2240]\n+ */\n+struct c3_isp_params_awb_config {\n+\tstruct c3_isp_params_block_header header;\n+\t__u8 tap_point;\n+\t__u8 satur_vald;\n+\t__u8 horiz_zones_num;\n+\t__u8 vert_zones_num;\n+\t__u16 rg_min;\n+\t__u16 rg_max;\n+\t__u16 bg_min;\n+\t__u16 bg_max;\n+\t__u16 rg_low;\n+\t__u16 rg_high;\n+\t__u16 bg_low;\n+\t__u16 bg_high;\n+\t__u8 zone_weight[C3_ISP_AWB_MAX_ZONES];\n+\t__u16 horiz_cood[C3_ISP_AWB_MAX_PT_NUM];\n+\t__u16 vert_cood[C3_ISP_AWB_MAX_PT_NUM];\n+} __attribute__((aligned(8)));\n+\n+/**\n+ * enum c3_isp_params_ae_tap_points - Tap points for the AE statistics\n+ * @C3_ISP_AE_STATS_TAP_GE: immediately after the green equal block\n+ * @C3_ISP_AE_STATS_TAP_MLS: immediately after the mesh lens shading block\n+ */\n+enum c3_isp_params_ae_tap_point {\n+\tC3_ISP_AE_STATS_TAP_GE = 0,\n+\tC3_ISP_AE_STATS_TAP_MLS,\n+};\n+\n+/**\n+ * struct c3_isp_params_ae_config - Stats settings for auto-exposure\n+ *\n+ * This struct allows the configuration of the statistics generated for\n+ * auto exposure.\n+ *\n+ * header.type should be set to C3_ISP_PARAMS_BLOCK_AE_CONFIG\n+ * from :c:type:`c3_isp_params_block_type`\n+ *\n+ * @header: the C3 ISP parameters block header\n+ * @horiz_zones_num: active number of horizontal zones [0..17]\n+ * @vert_zones_num: active number of vertical zones [0..15]\n+ * @tap_point: the tap point from enum c3_isp_params_ae_tap_point\n+ * @zone_weight: array of weights for AE statistics zones [0..15]\n+ * @horiz_cood: the horizontal coordinate of points on the diagonal [0..2888]\n+ * @vert_cood: the vertical coordinate of points on the diagonal [0..2240]\n+ * @reserved: applications must zero this array\n+ */\n+struct c3_isp_params_ae_config {\n+\tstruct c3_isp_params_block_header header;\n+\t__u8 tap_point;\n+\t__u8 horiz_zones_num;\n+\t__u8 vert_zones_num;\n+\t__u8 zone_weight[C3_ISP_AE_MAX_ZONES];\n+\t__u16 horiz_cood[C3_ISP_AE_MAX_PT_NUM];\n+\t__u16 vert_cood[C3_ISP_AE_MAX_PT_NUM];\n+\t__u16 reserved[3];\n+} __attribute__((aligned(8)));\n+\n+/**\n+ * enum c3_isp_params_af_tap_points - Tap points for the AF statistics\n+ * @C3_ISP_AF_STATS_TAP_SNR: immediately after the spatial noise reduce block\n+ * @C3_ISP_AF_STATS_TAP_DMS: immediately after the demosaic block\n+ */\n+enum c3_isp_params_af_tap_point {\n+\tC3_ISP_AF_STATS_TAP_SNR = 0,\n+\tC3_ISP_AF_STATS_TAP_DMS,\n+};\n+\n+/**\n+ * struct c3_isp_params_af_config - Stats settings for auto-focus\n+ *\n+ * This struct allows the configuration of the statistics generated for\n+ * auto focus.\n+ *\n+ * header.type should be set to C3_ISP_PARAMS_BLOCK_AF_CONFIG\n+ * from :c:type:`c3_isp_params_block_type`\n+ *\n+ * @header: the C3 ISP parameters block header\n+ * @tap_point: the tap point from enum c3_isp_params_af_tap_point\n+ * @horiz_zones_num: active number of hotizontal zones [0..17]\n+ * @vert_zones_num: active number of vertical zones [0..15]\n+ * @reserved: applications must zero this array\n+ * @horiz_cood: the horizontal coordinate of points on the diagonal [0..2888]\n+ * @vert_cood: the vertical coordinate of points on the diagonal [0..2240]\n+ */\n+struct c3_isp_params_af_config {\n+\tstruct c3_isp_params_block_header header;\n+\t__u8 tap_point;\n+\t__u8 horiz_zones_num;\n+\t__u8 vert_zones_num;\n+\t__u8 reserved[5];\n+\t__u16 horiz_cood[C3_ISP_AF_MAX_PT_NUM];\n+\t__u16 vert_cood[C3_ISP_AF_MAX_PT_NUM];\n+} __attribute__((aligned(8)));\n+\n+/**\n+ * struct c3_isp_params_pst_gamma - Post gamma configuration\n+ *\n+ * This struct allows the configuration of the look up table for\n+ * post gamma. The gamma curve consists of 129 points, so need to\n+ * set lut[129].\n+ *\n+ * header.type should be set to C3_ISP_PARAMS_BLOCK_PST_GAMMA\n+ * from :c:type:`c3_isp_params_block_type`\n+ *\n+ * @header: the C3 ISP parameters block header\n+ * @lut: lookup table for P-Stitch gamma [0..1023]\n+ * @reserved: applications must zero this array\n+ */\n+struct c3_isp_params_pst_gamma {\n+\tstruct c3_isp_params_block_header header;\n+\t__u16 lut[129];\n+\t__u16 reserved[3];\n+} __attribute__((aligned(8)));\n+\n+/**\n+ * struct c3_isp_params_ccm - ISP CCM configuration\n+ *\n+ * This struct allows the configuration of the matrix for\n+ * color correction. The matrix consists of 3 x 3 points,\n+ * so need to set matrix[3][3].\n+ *\n+ * header.type should be set to C3_ISP_PARAMS_BLOCK_CCM\n+ * from :c:type:`c3_isp_params_block_type`\n+ *\n+ * @header: the C3 ISP parameters block header\n+ * @matrix: a 3 x 3 matrix used for color correction,\n+ * the value of matrix[x][y] is orig_value x 256. [-4096..4095]\n+ * @reserved: applications must zero this array\n+ */\n+struct c3_isp_params_ccm {\n+\tstruct c3_isp_params_block_header header;\n+\t__s16 matrix[3][3];\n+\t__u16 reserved[3];\n+} __attribute__((aligned(8)));\n+\n+/**\n+ * struct c3_isp_params_csc - ISP Color Space Conversion configuration\n+ *\n+ * This struct allows the configuration of the matrix for color space\n+ * conversion. The matrix consists of 3 x 3 points, so need to set matrix[3][3].\n+ *\n+ * header.type should be set to C3_ISP_PARAMS_BLOCK_CSC\n+ * from :c:type:`c3_isp_params_block_type`\n+ *\n+ * @header: the C3 ISP parameters block header\n+ * @matrix: a 3x3 matrix used for the color space conversion,\n+ * the value of matrix[x][y] is orig_value x 256. [-4096..4095]\n+ * @reserved: applications must zero this array\n+ */\n+struct c3_isp_params_csc {\n+\tstruct c3_isp_params_block_header header;\n+\t__s16 matrix[3][3];\n+\t__u16 reserved[3];\n+} __attribute__((aligned(8)));\n+\n+/**\n+ * struct c3_isp_params_blc - ISP Black Level Correction configuration\n+ *\n+ * This struct allows the configuration of the block level offset for each\n+ * color channel.\n+ *\n+ * header.type should be set to C3_ISP_PARAMS_BLOCK_BLC\n+ * from :c:type:`c3_isp_params_block_type`\n+ *\n+ * @header: the C3 ISP parameters block header\n+ * @gr_ofst: Gr blc offset (Q4.8 format)\n+ * @r_ofst: R blc offset (Q4.8 format)\n+ * @b_ofst: B blc offset (Q4.8 format)\n+ * @gb_ofst: Gb blc offset(Q4.8 format)\n+ */\n+struct c3_isp_params_blc {\n+\tstruct c3_isp_params_block_header header;\n+\t__u16 gr_ofst;\n+\t__u16 r_ofst;\n+\t__u16 b_ofst;\n+\t__u16 gb_ofst;\n+};\n+\n+/**\n+ * define C3_ISP_PARAMS_MAX_SIZE - Maximum size of all C3 ISP Parameters\n+ *\n+ * Though the parameters for the C3 ISP are passed as optional blocks, the\n+ * driver still needs to know the absolute maximum size so that it can allocate\n+ * a buffer sized appropriately to accommodate userspace attempting to set all\n+ * possible parameters in a single frame.\n+ */\n+#define C3_ISP_PARAMS_MAX_SIZE \\\n+\t(sizeof(struct c3_isp_params_awb_gains) + \\\n+\t sizeof(struct c3_isp_params_awb_config) + \\\n+\t sizeof(struct c3_isp_params_ae_config) + \\\n+\t sizeof(struct c3_isp_params_af_config) + \\\n+\t sizeof(struct c3_isp_params_pst_gamma) + \\\n+\t sizeof(struct c3_isp_params_ccm) + \\\n+\t sizeof(struct c3_isp_params_csc) + \\\n+\t sizeof(struct c3_isp_params_blc))\n+\n+/**\n+ * struct c3_isp_params_cfg - C3 ISP configuration parameters\n+ *\n+ * This struct contains the configuration parameters of the C3 ISP\n+ * algorithms, serialized by userspace into an opaque data buffer. Each\n+ * configuration parameter block is represented by a block-specific structure\n+ * which contains a :c:type:`c3_isp_param_block_header` entry as first\n+ * member. Userspace populates the @data buffer with configuration parameters\n+ * for the blocks that it intends to configure. As a consequence, the data\n+ * buffer effective size changes according to the number of ISP blocks that\n+ * userspace intends to configure.\n+ *\n+ * The parameters buffer is versioned by the @version field to allow modifying\n+ * and extending its definition. Userspace should populate the @version field to\n+ * inform the driver about the version it intends to use. The driver will parse\n+ * and handle the @data buffer according to the data layout specific to the\n+ * indicated revision and return an error if the desired revision is not\n+ * supported.\n+ *\n+ * For each ISP block that userspace wants to configure, a block-specific\n+ * structure is appended to the @data buffer, one after the other without gaps\n+ * in between nor overlaps. Userspace shall populate the @total_size field with\n+ * the effective size, in bytes, of the @data buffer.\n+ *\n+ * The expected memory layout of the parameters buffer is::\n+ *\n+ *\t+-------------------- struct c3_isp_params_cfg ---- ------------------+\n+ *\t| version = C3_ISP_PARAM_BUFFER_V0; |\n+ *\t| data_size = sizeof(struct c3_isp_params_awb_gains) + |\n+ *\t| sizeof(struct c3_isp_params_awb_config); |\n+ *\t| +------------------------- data ---------------------------------+ |\n+ *\t| | +------------ struct c3_isp_params_awb_gains) ------------------+ |\n+ *\t| | | +--------- struct c3_isp_params_block_header header -----+ | | |\n+ *\t| | | | type = C3_ISP_PARAMS_BLOCK_AWB_GAINS; | | | |\n+ *\t| | | | flags = C3_ISP_PARAMS_BLOCK_FL_NONE; | | | |\n+ *\t| | | | size = sizeof(struct c3_isp_params_awb_gains); | | | |\n+ *\t| | | +---------------------------------------------------------+ | | |\n+ *\t| | | gr_gain = ...; | | |\n+ *\t| | | r_gain = ...; | | |\n+ *\t| | | b_gain = ...; | | |\n+ *\t| | | gb_gain = ...; | | |\n+ *\t| | +------------------ struct c3_isp_params_awb_config ----------+ | |\n+ *\t| | | +---------- struct c3_isp_param_block_header header ------+ | | |\n+ *\t| | | | type = C3_ISP_PARAMS_BLOCK_AWB_CONFIG; | | | |\n+ *\t| | | | flags = C3_ISP_PARAMS_BLOCK_FL_NONE; | | | |\n+ *\t| | | | size = sizeof(struct c3_isp_params_awb_config) | | | |\n+ *\t| | | +---------------------------------------------------------+ | | |\n+ *\t| | | tap_point = ...; | | |\n+ *\t| | | satur_vald = ...; | | |\n+ *\t| | | horiz_zones_num = ...; | | |\n+ *\t| | | vert_zones_num = ...; | | |\n+ *\t| | +-------------------------------------------------------------+ | |\n+ *\t| +-----------------------------------------------------------------+ |\n+ *\t+---------------------------------------------------------------------+\n+ *\n+ * @version: The C3 ISP parameters buffer version\n+ * @data_size: The C3 ISP configuration data effective size, excluding this\n+ * header\n+ * @data: The C3 ISP configuration blocks data\n+ */\n+struct c3_isp_params_cfg {\n+\t__u32 version;\n+\t__u32 data_size;\n+\t__u8 data[C3_ISP_PARAMS_MAX_SIZE];\n+};\n+\n+#endif\ndiff --git a/meson_options.txt b/meson_options.txt\nindex d59f4c04..352f981b 100644\n--- a/meson_options.txt\n+++ b/meson_options.txt\n@@ -32,7 +32,7 @@ option('gstreamer',\n \n option('ipas',\n type : 'array',\n- choices : ['ipu3', 'mali-c55', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'],\n+ choices : ['c3-isp', 'ipu3', 'mali-c55', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'],\n description : 'Select which IPA modules to build')\n \n option('lc-compliance',\ndiff --git a/src/ipa/c3-isp/algorithms/agc.cpp b/src/ipa/c3-isp/algorithms/agc.cpp\nnew file mode 100644\nindex 00000000..16eb0b46\n--- /dev/null\n+++ b/src/ipa/c3-isp/algorithms/agc.cpp\n@@ -0,0 +1,260 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic\n+ *\n+ * C3ISP AGC/AEC mean-based control algorithm\n+ */\n+\n+#include \"agc.h\"\n+\n+#include <algorithm>\n+#include <chrono>\n+#include <cmath>\n+#include <tuple>\n+#include <vector>\n+\n+#include <libcamera/base/log.h>\n+#include <libcamera/base/utils.h>\n+\n+#include <libcamera/control_ids.h>\n+\n+#include <libcamera/ipa/core_ipa_interface.h>\n+\n+#include \"libcamera/internal/yaml_parser.h\"\n+\n+#include \"libipa/histogram.h\"\n+\n+/**\n+ * \\file agc.h\n+ */\n+\n+namespace libcamera {\n+\n+using namespace std::literals::chrono_literals;\n+\n+namespace ipa::c3isp::algorithms {\n+\n+/**\n+ * \\class Agc\n+ * \\brief A mean-based auto-exposure algorithm\n+ */\n+\n+LOG_DEFINE_CATEGORY(C3ISPAgc)\n+\n+Agc::Agc()\n+\t: horizonalZonesNum_(17), verticalZonesNum_(15)\n+{\n+}\n+\n+/**\n+ * \\brief Initialise the AGC algorithm from tuning files\n+ * \\param[in] context The shared IPA context\n+ * \\param[in] tuningData The YamlObject containing Agc tuning data\n+ *\n+ * This function calls the base class' tuningData parsers to discover which\n+ * control values are supported.\n+ *\n+ * \\return 0 on success or errors from the base class\n+ */\n+int Agc::init(IPAContext &context,\n+\t const YamlObject &tuningData)\n+{\n+\tint ret;\n+\n+\tret = parseTuningData(tuningData);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tcontext.ctrlMap.merge(controls());\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\brief Configure the AGC given a configInfo\n+ * \\param[in] context The shared IPA context\n+ * \\param[in] configInfo The IPA configuration data\n+ *\n+ * \\return 0\n+ */\n+int Agc::configure(IPAContext &context,\n+\t\t [[maybe_unused]] const IPACameraSensorInfo &configInfo)\n+{\n+\tconst IPASessionConfiguration &configuration = context.configuration;\n+\tIPAActiveState &activeState = context.activeState;\n+\n+\t/* Configure the default gain and exposure */\n+\tactiveState.agc.gain = configuration.sensor.minAnalogueGain;\n+\tactiveState.agc.exposure = 10ms / configuration.sensor.lineDuration;\n+\n+\tactiveState.agc.constraintMode = constraintModes().begin()->first;\n+\tactiveState.agc.exposureMode = exposureModeHelpers().begin()->first;\n+\n+\tsetLimits(configuration.sensor.minShutterSpeed,\n+\t\t configuration.sensor.maxShutterSpeed,\n+\t\t configuration.sensor.minAnalogueGain,\n+\t\t configuration.sensor.maxAnalogueGain);\n+\n+\tresetFrameCount();\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::prepare\n+ */\n+void Agc::prepare(IPAContext &context, const uint32_t frame,\n+\t\t [[maybe_unused]] IPAFrameContext &frameContext,\n+\t\t C3ISPParams *params)\n+{\n+\tif (frame)\n+\t\treturn;\n+\n+\tauto AeCfg = params->block<BlockType::AEConfig>();\n+\tAeCfg.setEnabled(C3_ISP_PARAMS_BLOCK_FL_ENABLE);\n+\n+\tAeCfg->tap_point = C3_ISP_AE_STATS_TAP_MLS;\n+\tAeCfg->horiz_zones_num = horizonalZonesNum_;\n+\tAeCfg->vert_zones_num = verticalZonesNum_;\n+\n+\tfor (unsigned int i = 0; i < AeCfg->horiz_zones_num * AeCfg->vert_zones_num; i++)\n+\t\tAeCfg->zone_weight[i] = 1;\n+\n+\tSize sensorSize = context.configuration.sensor.size;\n+\tuint8_t maxPointNum = std::max(AeCfg->horiz_zones_num, AeCfg->vert_zones_num) + 1;\n+\n+\tfor (unsigned int i = 0; i < maxPointNum; i++) {\n+\t\tuint16_t hidx = i * sensorSize.width / AeCfg->horiz_zones_num;\n+\n+\t\t/* Aligned with 2 */\n+\t\thidx = hidx / 2 * 2;\n+\t\tAeCfg->horiz_cood[i] = std::min(hidx, (uint16_t)sensorSize.width);\n+\n+\t\tuint16_t vidx = i * sensorSize.height / AeCfg->vert_zones_num;\n+\n+\t\t/* Aligned with 2 */\n+\t\tvidx = vidx / 2 * 2;\n+\t\tAeCfg->vert_cood[i] = std::min(vidx, (uint16_t)sensorSize.height);\n+\t}\n+}\n+\n+Histogram Agc::parseStatistics(const c3_isp_stats_info *stats)\n+{\n+\tconst struct c3_isp_ae_stats *info = &stats->ae;\n+\tuint16_t zonesNum = horizonalZonesNum_ * verticalZonesNum_;\n+\tstd::vector<uint8_t> means(zonesNum, 0);\n+\n+\t/*\n+\t * Each zone has a 5-bin histogram and the\n+\t * total sum is normalized to 65535.\n+\t * For the convenience of calculation,\n+\t * it can be assumed that:\n+\t * hist0 represents the number of brightness 0,\n+\t * hist1 represents the number of brightness 64,\n+\t * hist2 represents the number of brightness 128,\n+\t * hist3 represents the number of brightness 192,\n+\t * hist4 represents the number of brightness 255.\n+\t *\n+\t * Finally, the average brightness of a zone can be calculated.\n+\t */\n+\tfor (unsigned int i = 0; i < zonesNum; i++) {\n+\t\tuint16_t hist2 = 65535 - info->stats[i].hist0 - info->stats[i].hist1 - info->stats[i].hist3 - info->stats[i].hist4;\n+\n+\t\tuint32_t lumaSum = info->stats[i].hist0 * 0 + info->stats[i].hist1 * 64 + hist2 * 128 + info->stats[i].hist3 * 192 + info->stats[i].hist4 * 255;\n+\n+\t\tmeans[i] = lumaSum / 65535;\n+\t}\n+\n+\tlumaMeans_ = means;\n+\n+\t/* This is a 1024-bin histogram */\n+\tuint32_t *hist = const_cast<uint32_t *>(info->hist);\n+\n+\treturn Histogram(Span<uint32_t>(hist, std::size(info->hist)));\n+}\n+\n+/**\n+ * \\brief Estimate the relative luminance of the frame with a given gain\n+ * \\param[in] gain The gain to apply in estimating luminance\n+ *\n+ * The estimation is based on the average value of the zone. Each\n+ * average value is multiplied by the gain, and then saturated to\n+ * to approximate the sensor behaviour at high brightness values.\n+ * The approximation is quite rough, as it doesn't take into account\n+ * non-linearities when approaching saturation.\n+ *\n+ * The values are normalized to the [0.0, 1.0] range, where 1.0 corresponds\n+ * to a theoretical perfect reflector of 100% reference white.\n+ *\n+ * More detailed information can be found in:\n+ * https://en.wikipedia.org/wiki/Relative_luminance\n+ *\n+ * \\return The relative luminance of the frame\n+ */\n+double Agc::estimateLuminance(double gain) const\n+{\n+\tdouble sum = 0.0;\n+\n+\tfor (unsigned int i = 0; i < lumaMeans_.size(); i++)\n+\t\tsum += std::min(lumaMeans_[i] * gain, 255.0);\n+\n+\treturn sum / lumaMeans_.size() / 255;\n+}\n+/**\n+ * \\brief Process C3 ISP statistics, and run AGC operations\n+ * \\param[in] context The shared IPA context\n+ * \\param[in] frame The current frame sequence number\n+ * \\param[in] frameContext The current frame context\n+ * \\param[in] stats The C3 ISP statistics and ISP results\n+ * \\param[out] metadata Metadata for the frame, to be filled by the algorithm\n+ *\n+ * Identify the current image brightness, and use that to estimate the optimal\n+ * new exposure and gain for the scene.\n+ */\n+void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n+\t\t IPAFrameContext &frameContext,\n+\t\t const c3_isp_stats_info *stats,\n+\t\t ControlList &metadata)\n+{\n+\tHistogram hist = parseStatistics(stats);\n+\n+\tutils::Duration shutterTime;\n+\tdouble aGain, dGain;\n+\n+\t/*\n+\t * The Agc algorithm needs to know the effective exposure value that was\n+\t * applied to the sensor when the statistics were collected.\n+\t */\n+\tutils::Duration exposureTime = context.configuration.sensor.lineDuration * frameContext.sensor.exposure;\n+\tdouble analogueGain = frameContext.sensor.gain;\n+\tutils::Duration effectiveExposureValue = exposureTime * analogueGain;\n+\n+\tstd::tie(shutterTime, aGain, dGain) =\n+\t\tcalculateNewEv(context.activeState.agc.constraintMode,\n+\t\t\t context.activeState.agc.exposureMode, hist,\n+\t\t\t effectiveExposureValue);\n+\n+\tLOG(C3ISPAgc, Debug)\n+\t\t<< \"Shutter time, analogue gain and digital gain are \"\n+\t\t<< shutterTime << \", \" << aGain << \" and \" << dGain;\n+\n+\tIPAActiveState &activeState = context.activeState;\n+\n+\tactiveState.agc.exposure = shutterTime / context.configuration.sensor.lineDuration;\n+\tactiveState.agc.gain = aGain;\n+\n+\tmetadata.set(controls::AnalogueGain, frameContext.sensor.gain);\n+\tmetadata.set(controls::ExposureTime, exposureTime.get<std::micro>());\n+\n+\tuint32_t vTotal = context.configuration.sensor.size.height + context.configuration.sensor.defVBlank;\n+\tutils::Duration frameDuration = context.configuration.sensor.lineDuration * vTotal;\n+\tmetadata.set(controls::FrameDuration, frameDuration.get<std::micro>());\n+\n+\tlumaMeans_ = {};\n+}\n+\n+REGISTER_IPA_ALGORITHM(Agc, \"Agc\")\n+\n+} /* namespace ipa::c3isp::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/c3-isp/algorithms/agc.h b/src/ipa/c3-isp/algorithms/agc.h\nnew file mode 100644\nindex 00000000..b0d1b500\n--- /dev/null\n+++ b/src/ipa/c3-isp/algorithms/agc.h\n@@ -0,0 +1,50 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic\n+ *\n+ * C3ISP AGC/AEC mean-based control algorithm\n+ */\n+\n+#pragma once\n+\n+#include <linux/c3-isp-config.h>\n+\n+#include <libcamera/base/span.h>\n+#include <libcamera/base/utils.h>\n+\n+#include <libcamera/geometry.h>\n+\n+#include \"libipa/agc_mean_luminance.h\"\n+#include \"algorithm.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa::c3isp::algorithms {\n+\n+class Agc : public Algorithm, public AgcMeanLuminance\n+{\n+public:\n+\tAgc();\n+\t~Agc() = default;\n+\n+\tint init(IPAContext &context, const YamlObject &tuningData) override;\n+\tint configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override;\n+\tvoid prepare(IPAContext &context, const uint32_t frame,\n+\t\t IPAFrameContext &frameContext,\n+\t\t C3ISPParams *params) override;\n+\tvoid process(IPAContext &context, const uint32_t frame,\n+\t\t IPAFrameContext &frameContext,\n+\t\t const c3_isp_stats_info *stats,\n+\t\t ControlList &metadata) override;\n+private:\n+\tHistogram parseStatistics(const c3_isp_stats_info *stats);\n+\tdouble estimateLuminance(double gain) const override;\n+\n+\tstd::vector<uint8_t> lumaMeans_;\n+\tuint8_t horizonalZonesNum_;\n+\tuint8_t verticalZonesNum_;\n+};\n+\n+} /* namespace ipa::c3isp::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/c3-isp/algorithms/algorithm.h b/src/ipa/c3-isp/algorithms/algorithm.h\nnew file mode 100644\nindex 00000000..eb40512c\n--- /dev/null\n+++ b/src/ipa/c3-isp/algorithms/algorithm.h\n@@ -0,0 +1,31 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic\n+ *\n+ * C3ISP control algorithm interface\n+ */\n+\n+#pragma once\n+\n+#include <libipa/algorithm.h>\n+\n+#include \"module.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa::c3isp {\n+\n+class Algorithm : public libcamera::ipa::Algorithm<Module>\n+{\n+public:\n+\tAlgorithm()\n+\t\t: disabled_(false)\n+\t{\n+\t}\n+\n+\tbool disabled_;\n+};\n+\n+} /* namespace ipa::c3isp */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/c3-isp/algorithms/awb.cpp b/src/ipa/c3-isp/algorithms/awb.cpp\nnew file mode 100755\nindex 00000000..f8ae3c1f\n--- /dev/null\n+++ b/src/ipa/c3-isp/algorithms/awb.cpp\n@@ -0,0 +1,257 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic\n+ *\n+ * C3ISP AWB control algorithm\n+ */\n+\n+#include \"awb.h\"\n+\n+#include <algorithm>\n+#include <ios>\n+\n+#include <libcamera/base/log.h>\n+\n+#include <libcamera/control_ids.h>\n+\n+#include <libcamera/ipa/core_ipa_interface.h>\n+\n+/**\n+ * \\file awb.h\n+ */\n+\n+namespace libcamera {\n+\n+namespace ipa::c3isp::algorithms {\n+\n+/**\n+ * \\class Awb\n+ * \\brief A Grey world white balance correction algorithm\n+ */\n+\n+LOG_DEFINE_CATEGORY(C3ISPAwb)\n+\n+Awb::Awb()\n+\t: horizonalZonesNum_(32), verticalZonesNum_(24)\n+{\n+}\n+\n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::configure\n+ */\n+int Awb::configure(IPAContext &context,\n+\t\t [[maybe_unused]] const IPACameraSensorInfo &configInfo)\n+{\n+\tIPAActiveState &activeState = context.activeState;\n+\n+\tactiveState.awb.gains.manual.red = 1.0;\n+\tactiveState.awb.gains.manual.blue = 1.0;\n+\tactiveState.awb.gains.manual.green = 1.0;\n+\n+\tactiveState.awb.gains.automatic.red = 1.0;\n+\tactiveState.awb.gains.automatic.blue = 1.0;\n+\tactiveState.awb.gains.automatic.green = 1.0;\n+\tactiveState.awb.autoEnabled = true;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::queueRequest\n+ */\n+void Awb::queueRequest(IPAContext &context,\n+\t\t [[maybe_unused]] const uint32_t frame,\n+\t\t IPAFrameContext &frameContext,\n+\t\t const ControlList &controls)\n+{\n+\tauto &awb = context.activeState.awb;\n+\n+\tconst auto &awbEnable = controls.get(controls::AwbEnable);\n+\tif (awbEnable && *awbEnable != awb.autoEnabled) {\n+\t\tawb.autoEnabled = *awbEnable;\n+\n+\t\tLOG(C3ISPAwb, Debug)\n+\t\t\t<< (*awbEnable ? \"Enabling\" : \"Disabling\") << \" AWB\";\n+\t}\n+\n+\tconst auto &colourGains = controls.get(controls::ColourGains);\n+\tif (colourGains && !awb.autoEnabled) {\n+\t\tawb.gains.manual.red = (*colourGains)[0];\n+\t\tawb.gains.manual.blue = (*colourGains)[1];\n+\n+\t\tLOG(C3ISPAwb, Debug)\n+\t\t\t<< \"Set colour gains to red: \" << awb.gains.manual.red\n+\t\t\t<< \", blue: \" << awb.gains.manual.blue;\n+\t}\n+\n+\tframeContext.awb.autoEnabled = awb.autoEnabled;\n+\n+\tif (!awb.autoEnabled) {\n+\t\tframeContext.awb.gains.red = awb.gains.manual.red;\n+\t\tframeContext.awb.gains.green = 1.0;\n+\t\tframeContext.awb.gains.blue = awb.gains.manual.blue;\n+\t}\n+}\n+\n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::prepare\n+ */\n+void Awb::prepare(IPAContext &context, const uint32_t frame,\n+\t\t IPAFrameContext &frameContext, C3ISPParams *params)\n+{\n+\t/*\n+\t * This is the latest time we can read the active state. This is the\n+\t * most up-to-date automatic values we can read.\n+\t */\n+\tif (frameContext.awb.autoEnabled) {\n+\t\tframeContext.awb.gains.red = context.activeState.awb.gains.automatic.red;\n+\t\tframeContext.awb.gains.green = context.activeState.awb.gains.automatic.green;\n+\t\tframeContext.awb.gains.blue = context.activeState.awb.gains.automatic.blue;\n+\t}\n+\n+\tauto AWBGains = params->block<BlockType::AWBGains>();\n+\tAWBGains.setEnabled(C3_ISP_PARAMS_BLOCK_FL_ENABLE);\n+\n+\tAWBGains->gr_gain = std::clamp<int>(256 * frameContext.awb.gains.green, 0, 0xfff);\n+\tAWBGains->r_gain = std::clamp<int>(256 * frameContext.awb.gains.red, 0, 0xfff);\n+\tAWBGains->b_gain = std::clamp<int>(256 * frameContext.awb.gains.blue, 0, 0xfff);\n+\tAWBGains->gb_gain = std::clamp<int>(256 * frameContext.awb.gains.green, 0, 0xfff);\n+\n+\tif (frame)\n+\t\treturn;\n+\n+\tauto AWBCfg = params->block<BlockType::AWBConfig>();\n+\tAWBCfg.setEnabled(C3_ISP_PARAMS_BLOCK_FL_ENABLE);\n+\n+\tAWBCfg->tap_point = C3_ISP_AWB_STATS_TAP_BEFORE_WB;\n+\tAWBCfg->satur_vald = 1;\n+\tAWBCfg->horiz_zones_num = horizonalZonesNum_;\n+\tAWBCfg->vert_zones_num = verticalZonesNum_;\n+\tAWBCfg->rg_min = 75;\n+\tAWBCfg->rg_max = 256;\n+\tAWBCfg->bg_min = 44;\n+\tAWBCfg->bg_max = 222;\n+\tAWBCfg->rg_low = 93;\n+\tAWBCfg->rg_high = 244;\n+\tAWBCfg->bg_low = 61;\n+\tAWBCfg->bg_high = 205;\n+\n+\tfor (unsigned int i = 0; i < AWBCfg->horiz_zones_num * AWBCfg->vert_zones_num; i++)\n+\t\tAWBCfg->zone_weight[i] = 1;\n+\n+\tSize sensorSize = context.configuration.sensor.size;\n+\tuint8_t maxPointNum = std::max(AWBCfg->horiz_zones_num, AWBCfg->vert_zones_num) + 1;\n+\n+\tfor (unsigned int i = 0; i < maxPointNum; i++) {\n+\t\tuint16_t hidx = i * sensorSize.width / AWBCfg->horiz_zones_num;\n+\n+\t\t/* Aligned with 2 */\n+\t\thidx = hidx / 2 * 2;\n+\t\tAWBCfg->horiz_cood[i] = std::min(hidx, (uint16_t)sensorSize.width);\n+\n+\t\tuint16_t vidx = i * sensorSize.height / AWBCfg->vert_zones_num;\n+\n+\t\t/* Aligned with 2 */\n+\t\tvidx = vidx / 2 * 2;\n+\t\tAWBCfg->vert_cood[i] = std::min(vidx, (uint16_t)sensorSize.height);\n+\t}\n+}\n+\n+uint32_t Awb::estimateCCT(double red, double green, double blue)\n+{\n+\t/* Convert the RGB values to CIE tristimulus values (XYZ) */\n+\tdouble X = (-0.14282) * (red) + (1.54924) * (green) + (-0.95641) * (blue);\n+\tdouble Y = (-0.32466) * (red) + (1.57837) * (green) + (-0.73191) * (blue);\n+\tdouble Z = (-0.68202) * (red) + (0.77073) * (green) + (0.56332) * (blue);\n+\n+\t/* Calculate the normalized chromaticity values */\n+\tdouble x = X / (X + Y + Z);\n+\tdouble y = Y / (X + Y + Z);\n+\n+\t/* Calculate CCT */\n+\tdouble n = (x - 0.3320) / (0.1858 - y);\n+\treturn 449 * n * n * n + 3525 * n * n + 6823.3 * n + 5520.33;\n+}\n+\n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::process\n+ */\n+void Awb::process([[maybe_unused]] IPAContext &context,\n+\t\t [[maybe_unused]] const uint32_t frame,\n+\t\t IPAFrameContext &frameContext,\n+\t\t const c3_isp_stats_info *stats,\n+\t\t ControlList &metadata)\n+{\n+\tIPAActiveState &activeState = context.activeState;\n+\tconst struct c3_isp_awb_stats *awb = &stats->awb;\n+\tuint16_t zoneCnt = horizonalZonesNum_ * verticalZonesNum_;\n+\tuint32_t rgSum = 0;\n+\tuint32_t bgSum = 0;\n+\tdouble rgMean;\n+\tdouble bgMean;\n+\tdouble greenMean;\n+\tdouble blueMean;\n+\tdouble redMean;\n+\n+\tfor (unsigned int i = 0; i < zoneCnt; i++) {\n+\t\trgSum += awb->stats[i].rg;\n+\t\tbgSum += awb->stats[i].bg;\n+\t}\n+\n+\trgMean = rgSum / zoneCnt / 4096.0;\n+\tbgMean = bgSum / zoneCnt / 4096.0;\n+\n+\t/*\n+\t * To simplify the calculation,\n+\t * the green mean is hardcoded to 1.0\n+\t */\n+\n+\tgreenMean = 1.0;\n+\tredMean = rgMean * greenMean;\n+\tblueMean = bgMean * greenMean;\n+\n+\tactiveState.awb.temperatureK = estimateCCT(redMean, greenMean, blueMean);\n+\n+\t/* Metadata shall contain the up to date measurement */\n+\tmetadata.set(controls::ColourTemperature, activeState.awb.temperatureK);\n+\n+\t/*\n+\t * Estimate the red and blue gains to apply in a grey world.\n+\t * The green gain is hardcoded to 1.0. Avoid division by zero\n+\t * by clamping the divisor to mininum value of 0.0625.\n+\t */\n+\tdouble redGain = greenMean / std::max(redMean, 0.0625);\n+\tdouble blueGain = greenMean / std::max(blueMean, 0.0625);\n+\n+\t/*\n+\t * Clamp the gain values to the hardware, which express gains as Q4.8\n+\t * unsigned integer values. Set the minimum just above zero to avoid\n+\t * divisions by zero.\n+\t */\n+\tredGain = std::clamp(redGain, 1.0 / 256, 4095.0 / 256);\n+\tblueGain = std::clamp(blueGain, 1.0 / 256, 4095.0 / 256);\n+\n+\t/* Filter the values to avoid oscillations. */\n+\tdouble speed = 0.2;\n+\tredGain = speed * redGain + (1 - speed) * activeState.awb.gains.automatic.red;\n+\tblueGain = speed * blueGain + (1 - speed) * activeState.awb.gains.automatic.blue;\n+\n+\tactiveState.awb.gains.automatic.red = redGain;\n+\tactiveState.awb.gains.automatic.blue = blueGain;\n+\tactiveState.awb.gains.automatic.green = 1.0;\n+\n+\tmetadata.set(controls::AwbEnable, frameContext.awb.autoEnabled);\n+\tmetadata.set(controls::ColourGains, { static_cast<float>(frameContext.awb.gains.red),\n+\t\t\t\t\t static_cast<float>(frameContext.awb.gains.blue) });\n+\n+\tLOG(C3ISPAwb, Debug) << \"Gains: R \" << activeState.awb.gains.automatic.red\n+\t\t\t << \", G \" << activeState.awb.gains.automatic.green\n+\t\t\t << \", B \" << activeState.awb.gains.automatic.blue\n+\t\t\t << \", Ct \" << activeState.awb.temperatureK << \"K\";\n+}\n+\n+REGISTER_IPA_ALGORITHM(Awb, \"Awb\")\n+\n+} /* namespace ipa::c3isp::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/c3-isp/algorithms/awb.h b/src/ipa/c3-isp/algorithms/awb.h\nnew file mode 100755\nindex 00000000..63ed84b2\n--- /dev/null\n+++ b/src/ipa/c3-isp/algorithms/awb.h\n@@ -0,0 +1,42 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic\n+ *\n+ * C3ISP AWB control algorithm\n+ */\n+\n+#pragma once\n+\n+#include \"algorithm.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa::c3isp::algorithms {\n+\n+class Awb : public Algorithm\n+{\n+public:\n+\tAwb();\n+\t~Awb() = default;\n+\n+\tint configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override;\n+\tvoid queueRequest(IPAContext &context, const uint32_t frame,\n+\t\t\t IPAFrameContext &frameContext,\n+\t\t\t const ControlList &controls) override;\n+\tvoid prepare(IPAContext &context, const uint32_t frame,\n+\t\t IPAFrameContext &frameContext,\n+\t\t C3ISPParams *params) override;\n+\tvoid process(IPAContext &context, const uint32_t frame,\n+\t\t IPAFrameContext &frameContext,\n+\t\t const c3_isp_stats_info *stats,\n+\t\t ControlList &metadata) override;\n+\n+private:\n+\tuint32_t estimateCCT(double red, double green, double blue);\n+\tuint8_t horizonalZonesNum_;\n+\tuint8_t verticalZonesNum_;\n+};\n+\n+} /* namespace ipa::c3isp::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/c3-isp/algorithms/blc.cpp b/src/ipa/c3-isp/algorithms/blc.cpp\nnew file mode 100644\nindex 00000000..781c8c2f\n--- /dev/null\n+++ b/src/ipa/c3-isp/algorithms/blc.cpp\n@@ -0,0 +1,102 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic\n+ *\n+ * C3ISP Black Level Correction control\n+ */\n+\n+#include \"blc.h\"\n+\n+#include <libcamera/base/log.h>\n+#include <libcamera/control_ids.h>\n+\n+#include \"libcamera/internal/yaml_parser.h\"\n+\n+/**\n+ * \\file blc.h\n+ */\n+\n+namespace libcamera {\n+\n+namespace ipa::c3isp::algorithms {\n+\n+/**\n+ * \\class Blc\n+ * \\brief C3 ISP Black Level Correction control\n+ *\n+ * The pixels output by the camera normally include a black level, because\n+ * sensors do not always report a signal level of '0' for black. Pixels at or\n+ * below this level should be considered black. To achieve that, the C3 ISP BLC\n+ * algorithm subtracts a configurable offset from all pixels.\n+ *\n+ * The black level can be measured at runtime from an optical dark region of the\n+ * camera sensor, or measured during the camera tuning process. The first option\n+ * isn't currently supported.\n+ */\n+\n+LOG_DEFINE_CATEGORY(C3ISPBlc)\n+\n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::init\n+ */\n+int Blc::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData)\n+{\n+\tstd::optional<int16_t> levelRed = tuningData[\"BlcR\"].get<int16_t>();\n+\tstd::optional<int16_t> levelGreenR = tuningData[\"BlcGr\"].get<int16_t>();\n+\tstd::optional<int16_t> levelGreenB = tuningData[\"BlcGb\"].get<int16_t>();\n+\tstd::optional<int16_t> levelBlue = tuningData[\"BlcB\"].get<int16_t>();\n+\n+\tblackLevelRed_ = levelRed.value_or(4096);\n+\tblackLevelGreenR_ = levelGreenR.value_or(4096);\n+\tblackLevelGreenB_ = levelGreenB.value_or(4096);\n+\tblackLevelBlue_ = levelBlue.value_or(4096);\n+\n+\tLOG(C3ISPBlc, Debug)\n+\t\t<< \"Black Levels: red \" << blackLevelRed_\n+\t\t<< \", green (red) \" << blackLevelGreenR_\n+\t\t<< \", green (blue) \" << blackLevelGreenB_\n+\t\t<< \", blue \" << blackLevelBlue_;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::prepare\n+ */\n+void Blc::prepare([[maybe_unused]] IPAContext &context, const uint32_t frame,\n+\t\t [[maybe_unused]] IPAFrameContext &frameContext,\n+\t\t C3ISPParams *params)\n+{\n+\tif (frame)\n+\t\treturn;\n+\n+\tauto blcCfg = params->block<BlockType::Blc>();\n+\tblcCfg.setEnabled(C3_ISP_PARAMS_BLOCK_FL_ENABLE);\n+\n+\tblcCfg->gr_ofst = blackLevelGreenR_;\n+\tblcCfg->r_ofst = blackLevelRed_;\n+\tblcCfg->b_ofst = blackLevelBlue_;\n+\tblcCfg->gb_ofst = blackLevelGreenB_;\n+}\n+\n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::process\n+ */\n+void Blc::process([[maybe_unused]] IPAContext &context,\n+\t\t [[maybe_unused]] const uint32_t frame,\n+\t\t [[maybe_unused]] IPAFrameContext &frameContext,\n+\t\t [[maybe_unused]] const c3_isp_stats_info *stats,\n+\t\t ControlList &metadata)\n+{\n+\tmetadata.set(controls::SensorBlackLevels,\n+\t\t { static_cast<int32_t>(blackLevelRed_),\n+\t\t static_cast<int32_t>(blackLevelGreenR_),\n+\t\t static_cast<int32_t>(blackLevelGreenB_),\n+\t\t static_cast<int32_t>(blackLevelBlue_) });\n+}\n+\n+REGISTER_IPA_ALGORITHM(Blc, \"Blc\")\n+\n+} /* namespace ipa::c3isp::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/c3-isp/algorithms/blc.h b/src/ipa/c3-isp/algorithms/blc.h\nnew file mode 100644\nindex 00000000..702163f7\n--- /dev/null\n+++ b/src/ipa/c3-isp/algorithms/blc.h\n@@ -0,0 +1,40 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic\n+ *\n+ * C3ISP Black Level Correction control\n+ */\n+\n+#pragma once\n+\n+#include \"algorithm.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa::c3isp::algorithms {\n+\n+class Blc : public Algorithm\n+{\n+public:\n+\tBlc() {}\n+\t~Blc() = default;\n+\n+\tint init(IPAContext &context, const YamlObject &tuningData) override;\n+\tvoid prepare(IPAContext &context, const uint32_t frame,\n+\t\t IPAFrameContext &frameContext,\n+\t\t C3ISPParams *params) override;\n+\tvoid process(IPAContext &context, const uint32_t frame,\n+\t\t IPAFrameContext &frameContext,\n+\t\t const c3_isp_stats_info *stats,\n+\t\t ControlList &metadata) override;\n+\n+private:\n+\tint16_t blackLevelRed_;\n+\tint16_t blackLevelGreenR_;\n+\tint16_t blackLevelGreenB_;\n+\tint16_t blackLevelBlue_;\n+};\n+\n+} /* namespace ipa::c3isp::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/c3-isp/algorithms/ccm.cpp b/src/ipa/c3-isp/algorithms/ccm.cpp\nnew file mode 100644\nindex 00000000..43f146d6\n--- /dev/null\n+++ b/src/ipa/c3-isp/algorithms/ccm.cpp\n@@ -0,0 +1,86 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic\n+ *\n+ * C3ISP Color Correction Matrix control\n+ */\n+\n+#include \"ccm.h\"\n+\n+#include <libcamera/base/log.h>\n+#include <libcamera/base/utils.h>\n+\n+#include <libcamera/control_ids.h>\n+#include <libcamera/ipa/core_ipa_interface.h>\n+\n+#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libipa/interpolator.h\"\n+\n+/**\n+ * \\file ccm.h\n+ */\n+\n+namespace libcamera {\n+\n+namespace ipa::c3isp::algorithms {\n+\n+/**\n+ * \\class Ccm\n+ * \\brief A color correction matrix algorithm\n+ */\n+\n+LOG_DEFINE_CATEGORY(C3ISPCcm)\n+\n+Ccm::Ccm()\n+{\n+}\n+\n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::init\n+ */\n+int Ccm::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData)\n+{\n+\tccmCoeff = tuningData[\"CcmCoeff\"].getList<int>().value_or(std::vector<int>{});\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::prepare\n+ */\n+void Ccm::prepare([[maybe_unused]] IPAContext &context,\n+\t\t [[maybe_unused]] const uint32_t frame,\n+\t\t [[maybe_unused]] IPAFrameContext &frameContext, C3ISPParams *params)\n+{\n+\tauto CcmCfg = params->block<BlockType::Ccm>();\n+\tCcmCfg.setEnabled(C3_ISP_PARAMS_BLOCK_FL_ENABLE);\n+\n+\tfor (unsigned int i = 0; i < 3; i++) {\n+\t\tfor (unsigned int j = 0; j < 3; j++) {\n+\t\t\tCcmCfg->matrix[i][j] = ccmCoeff[i * 3 + j];\n+\t\t}\n+\t}\n+}\n+\n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::process\n+ */\n+void Ccm::process([[maybe_unused]] IPAContext &context,\n+\t\t [[maybe_unused]] const uint32_t frame,\n+\t\t [[maybe_unused]] IPAFrameContext &frameContext,\n+\t\t [[maybe_unused]] const c3_isp_stats_info *stats,\n+\t\t ControlList &metadata)\n+{\n+\tfloat m[9];\n+\tfor (unsigned int i = 0; i < 3; i++) {\n+\t\tfor (unsigned int j = 0; j < 3; j++)\n+\t\t\tm[i * 3 + j] = ccmCoeff[i * 3 + j];\n+\t}\n+\tmetadata.set(controls::ColourCorrectionMatrix, m);\n+}\n+\n+REGISTER_IPA_ALGORITHM(Ccm, \"Ccm\")\n+\n+} /* namespace ipa::c3isp::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/c3-isp/algorithms/ccm.h b/src/ipa/c3-isp/algorithms/ccm.h\nnew file mode 100644\nindex 00000000..9d8115d4\n--- /dev/null\n+++ b/src/ipa/c3-isp/algorithms/ccm.h\n@@ -0,0 +1,40 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic\n+ *\n+ * C3ISP Color Correction Matrix control\n+ */\n+\n+#pragma once\n+\n+#include <linux/c3-isp-config.h>\n+\n+#include <libipa/interpolator.h>\n+\n+#include \"algorithm.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa::c3isp::algorithms {\n+\n+class Ccm : public Algorithm\n+{\n+public:\n+\tCcm();\n+\t~Ccm() = default;\n+\n+\tint init(IPAContext &context, const YamlObject &tuningData) override;\n+\tvoid prepare(IPAContext &context, const uint32_t frame,\n+\t\t IPAFrameContext &frameContext,\n+\t\t C3ISPParams *params) override;\n+\tvoid process(IPAContext &context, const uint32_t frame,\n+\t\t IPAFrameContext &frameContext, const c3_isp_stats_info *stats,\n+\t\t ControlList &metadata) override;\n+\n+private:\n+\tstd::vector<int> ccmCoeff;\n+};\n+\n+} /* namespace ipa::c3isp::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/c3-isp/algorithms/csc.cpp b/src/ipa/c3-isp/algorithms/csc.cpp\nnew file mode 100644\nindex 00000000..8e066f57\n--- /dev/null\n+++ b/src/ipa/c3-isp/algorithms/csc.cpp\n@@ -0,0 +1,64 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic\n+ *\n+ * C3ISP Color Space Conversion control\n+ */\n+\n+#include \"csc.h\"\n+\n+#include <libcamera/base/log.h>\n+#include <libcamera/control_ids.h>\n+\n+/**\n+ * \\file csc.h\n+ */\n+\n+namespace libcamera {\n+\n+namespace ipa::c3isp::algorithms {\n+\n+/**\n+ * \\class Csc\n+ * \\brief Color Space Conversion algorithm\n+ */\n+\n+LOG_DEFINE_CATEGORY(C3ISPCsc)\n+\n+Csc::Csc()\n+{\n+}\n+\n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::init\n+ */\n+int Csc::init([[maybe_unused]] IPAContext &context,\n+\t [[maybe_unused]] const YamlObject &tuningData)\n+{\n+\tcscCoeff = tuningData[\"CscCoeff\"].getList<int>().value_or(std::vector<int>{});\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::prepare\n+ */\n+void Csc::prepare([[maybe_unused]] IPAContext &context,\n+\t\t [[maybe_unused]] const uint32_t frame,\n+\t\t [[maybe_unused]] IPAFrameContext &frameContext,\n+\t\t C3ISPParams *params)\n+{\n+\tauto CscCfg = params->block<BlockType::Csc>();\n+\tCscCfg.setEnabled(C3_ISP_PARAMS_BLOCK_FL_ENABLE);\n+\n+\tfor (unsigned int i = 0; i < 3; i++) {\n+\t\tfor (unsigned int j = 0; j < 3; j++)\n+\t\t\tCscCfg->matrix[i][j] = cscCoeff[i * 3 + j];\n+\t}\n+}\n+\n+REGISTER_IPA_ALGORITHM(Csc, \"Csc\")\n+\n+} /* namespace ipa::c3isp::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/c3-isp/algorithms/csc.h b/src/ipa/c3-isp/algorithms/csc.h\nnew file mode 100644\nindex 00000000..13b89dd0\n--- /dev/null\n+++ b/src/ipa/c3-isp/algorithms/csc.h\n@@ -0,0 +1,32 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic\n+ *\n+ * C3ISP Color Space Conversion control\n+ */\n+\n+#pragma once\n+\n+#include \"algorithm.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa::c3isp::algorithms {\n+\n+class Csc : public Algorithm\n+{\n+public:\n+\tCsc();\n+\t~Csc() = default;\n+\n+\tint init(IPAContext &context, const YamlObject &tuningData) override;\n+\tvoid prepare(IPAContext &context, const uint32_t frame,\n+\t\t IPAFrameContext &frameContext,\n+\t\t C3ISPParams *params) override;\n+private:\n+\tstd::vector<int> cscCoeff;\n+};\n+\n+} /* namespace ipa::c3isp::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/c3-isp/algorithms/meson.build b/src/ipa/c3-isp/algorithms/meson.build\nnew file mode 100644\nindex 00000000..5e7e76dd\n--- /dev/null\n+++ b/src/ipa/c3-isp/algorithms/meson.build\n@@ -0,0 +1,10 @@\n+# SPDX-License-Identifier: CC0-1.0\n+\n+c3isp_ipa_algorithms = files([\n+ 'agc.cpp',\n+ 'awb.cpp',\n+ 'blc.cpp',\n+ 'ccm.cpp',\n+ 'csc.cpp',\n+ 'post_gamma.cpp'\n+])\ndiff --git a/src/ipa/c3-isp/algorithms/post_gamma.cpp b/src/ipa/c3-isp/algorithms/post_gamma.cpp\nnew file mode 100644\nindex 00000000..cd62a604\n--- /dev/null\n+++ b/src/ipa/c3-isp/algorithms/post_gamma.cpp\n@@ -0,0 +1,64 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic\n+ *\n+ * C3ISP Post Gamma control\n+ */\n+\n+#include \"post_gamma.h\"\n+\n+#include <libcamera/base/log.h>\n+#include <libcamera/control_ids.h>\n+\n+/**\n+ * \\file post_gamma.h\n+ */\n+\n+namespace libcamera {\n+\n+namespace ipa::c3isp::algorithms {\n+\n+/**\n+ * \\class PostGamma\n+ * \\brief A post gamma algorithm\n+ */\n+\n+LOG_DEFINE_CATEGORY(C3ISPPostGamma)\n+\n+PostGamma::PostGamma()\n+{\n+}\n+\n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::init\n+ */\n+int PostGamma::init([[maybe_unused]] IPAContext &context,\n+\t\t [[maybe_unused]] const YamlObject &tuningData)\n+{\n+\tgammaLut =\n+\t\ttuningData[\"GammaLut\"].getList<uint16_t>().value_or(std::vector<uint16_t>{});\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::prepare\n+ */\n+void PostGamma::prepare([[maybe_unused]] IPAContext &context,\n+\t\t\t[[maybe_unused]] const uint32_t frame,\n+\t\t\t[[maybe_unused]] IPAFrameContext &frameContext,\n+\t\t\tC3ISPParams *params)\n+{\n+\tauto PostGammaCfg = params->block<BlockType::PostGamma>();\n+\tPostGammaCfg.setEnabled(C3_ISP_PARAMS_BLOCK_FL_ENABLE);\n+\n+\tfor (unsigned int i = 0; i < 129; i++) {\n+\t\tPostGammaCfg->lut[i] = gammaLut[i];\n+\t}\n+}\n+\n+REGISTER_IPA_ALGORITHM(PostGamma, \"PostGamma\")\n+\n+} /* namespace ipa::c3isp::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/c3-isp/algorithms/post_gamma.h b/src/ipa/c3-isp/algorithms/post_gamma.h\nnew file mode 100644\nindex 00000000..0bfa134b\n--- /dev/null\n+++ b/src/ipa/c3-isp/algorithms/post_gamma.h\n@@ -0,0 +1,33 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic\n+ *\n+ * C3ISP Post Gamma control\n+ */\n+\n+#pragma once\n+\n+#include \"algorithm.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa::c3isp::algorithms {\n+\n+class PostGamma : public Algorithm\n+{\n+public:\n+\tPostGamma();\n+\t~PostGamma() = default;\n+\n+\tint init(IPAContext &context, const YamlObject &tuningData) override;\n+\tvoid prepare(IPAContext &context, const uint32_t frame,\n+\t\t IPAFrameContext &frameContext,\n+\t\t C3ISPParams *params) override;\n+\n+private:\n+\tstd::vector<uint16_t> gammaLut;\n+};\n+\n+} /* namespace ipa::c3isp::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/c3-isp/c3-isp.cpp b/src/ipa/c3-isp/c3-isp.cpp\nnew file mode 100644\nindex 00000000..9331576d\n--- /dev/null\n+++ b/src/ipa/c3-isp/c3-isp.cpp\n@@ -0,0 +1,386 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic Inc.\n+ *\n+ * c3-isp.cpp - Amlogic Image Processing Algorithms\n+ */\n+\n+#include <algorithm>\n+#include <array>\n+#include <chrono>\n+#include <stdint.h>\n+#include <string.h>\n+\n+#include <linux/c3-isp-config.h>\n+#include <linux/v4l2-controls.h>\n+\n+#include <libcamera/base/file.h>\n+#include <libcamera/base/log.h>\n+\n+#include <libcamera/control_ids.h>\n+#include <libcamera/controls.h>\n+#include <libcamera/framebuffer.h>\n+#include <libcamera/request.h>\n+\n+#include <libcamera/ipa/c3isp_ipa_interface.h>\n+#include <libcamera/ipa/ipa_interface.h>\n+#include <libcamera/ipa/ipa_module_info.h>\n+\n+#include \"libcamera/internal/formats.h\"\n+#include \"libcamera/internal/mapped_framebuffer.h\"\n+#include \"libcamera/internal/yaml_parser.h\"\n+\n+#include \"algorithms/algorithm.h\"\n+\n+#include \"ipa_context.h\"\n+#include \"params.h\"\n+\n+namespace libcamera {\n+\n+LOG_DEFINE_CATEGORY(IPAC3ISP)\n+\n+using namespace std::literals::chrono_literals;\n+\n+namespace ipa::c3isp {\n+\n+static constexpr uint32_t kMaxFrameContexts = 16;\n+\n+class IPAC3ISP : public IPAC3ISPInterface, public Module\n+{\n+public:\n+\tIPAC3ISP();\n+\n+\tint init(const IPASettings &settings, unsigned int hwRevision,\n+\t\t const IPACameraSensorInfo &sensorInfo,\n+\t\t const ControlInfoMap &sensorControls,\n+\t\t ControlInfoMap *ipaControls) override;\n+\tint start() override;\n+\tvoid stop() override;\n+\n+\tint configure(const IPAConfigInfo &ipaConfig,\n+\t\t ControlInfoMap *ipaControls) override;\n+\tvoid mapBuffers(const std::vector<IPABuffer> &buffers) override;\n+\tvoid unmapBuffers(const std::vector<unsigned int> &ids) override;\n+\n+\tvoid queueRequest(const uint32_t frame, const ControlList &controls) override;\n+\tvoid fillParamsBuffer(const uint32_t frame, const uint32_t bufferId) override;\n+\tvoid processStatsBuffer(const uint32_t frame, const uint32_t bufferId,\n+\t\t\t\tconst ControlList &sensorControls) override;\n+\n+protected:\n+\tstd::string logPrefix() const override;\n+\n+private:\n+\tvoid updateControls(const IPACameraSensorInfo &sensorInfo,\n+\t\t\t const ControlInfoMap &sensorControls,\n+\t\t\t ControlInfoMap *ipaControls);\n+\tvoid setControls(unsigned int frame);\n+\n+\tstd::map<unsigned int, FrameBuffer> buffers_;\n+\tstd::map<unsigned int, MappedFrameBuffer> mappedBuffers_;\n+\n+\tControlInfoMap sensorControls_;\n+\n+\tstruct IPAContext context_;\n+};\n+\n+namespace {\n+\n+const ControlInfoMap::Map c3ispControls{\n+\t{ &controls::AwbEnable, ControlInfo(false, true) },\n+\t{ &controls::ColourGains, ControlInfo(0.0f, 3.996f, 1.0f) },\n+};\n+\n+} /* namespace */\n+\n+IPAC3ISP::IPAC3ISP()\n+\t: context_({ {}, {}, { kMaxFrameContexts }, {}, {} })\n+{\n+}\n+\n+std::string IPAC3ISP::logPrefix() const\n+{\n+\treturn \"c3isp\";\n+}\n+\n+int IPAC3ISP::init(const IPASettings &settings, unsigned int hwRevision,\n+\t\t const IPACameraSensorInfo &sensorInfo,\n+\t\t const ControlInfoMap &sensorControls,\n+\t\t ControlInfoMap *ipaControls)\n+{\n+\tLOG(IPAC3ISP, Debug) << \"Hardware revision is \" << hwRevision;\n+\n+\tcontext_.camHelper = CameraSensorHelperFactoryBase::create(settings.sensorModel);\n+\tif (!context_.camHelper) {\n+\t\tLOG(IPAC3ISP, Error)\n+\t\t\t<< \"Failed to create camera sensor helper for \"\n+\t\t\t<< settings.sensorModel;\n+\t\treturn -ENODEV;\n+\t}\n+\n+\tcontext_.configuration.sensor.lineDuration =\n+\t\tsensorInfo.minLineLength * 1.0s / sensorInfo.pixelRate;\n+\n+\tFile file(settings.configurationFile);\n+\tif (!file.open(File::OpenModeFlag::ReadOnly)) {\n+\t\tint ret = file.error();\n+\t\tLOG(IPAC3ISP, Error)\n+\t\t\t<< \"Failed to open configuration file \"\n+\t\t\t<< settings.configurationFile << \": \" << strerror(-ret);\n+\t\treturn ret;\n+\t}\n+\n+\tstd::unique_ptr<libcamera::YamlObject> data = YamlParser::parse(file);\n+\tif (!data)\n+\t\treturn -EINVAL;\n+\n+\tunsigned int version = (*data)[\"version\"].get<uint32_t>(0);\n+\tif (version != 1) {\n+\t\tLOG(IPAC3ISP, Error)\n+\t\t\t<< \"Invalid tuning file version \" << version;\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (!data->contains(\"algorithms\")) {\n+\t\tLOG(IPAC3ISP, Error)\n+\t\t\t<< \"Tuning file doesn't contain any algorithm\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tint ret = createAlgorithms(context_, (*data)[\"algorithms\"]);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tupdateControls(sensorInfo, sensorControls, ipaControls);\n+\n+\treturn 0;\n+}\n+\n+int IPAC3ISP::start()\n+{\n+\tsetControls(0);\n+\n+\treturn 0;\n+}\n+\n+void IPAC3ISP::stop()\n+{\n+\tcontext_.frameContexts.clear();\n+}\n+\n+int IPAC3ISP::configure(const IPAConfigInfo &ipaConfig,\n+\t\t\tControlInfoMap *ipaControls)\n+{\n+\tsensorControls_ = ipaConfig.sensorControls;\n+\n+\tconst auto itExp = sensorControls_.find(V4L2_CID_EXPOSURE);\n+\tint32_t minExposure = itExp->second.min().get<int32_t>();\n+\tint32_t maxExposure = itExp->second.max().get<int32_t>();\n+\n+\tconst auto itGain = sensorControls_.find(V4L2_CID_ANALOGUE_GAIN);\n+\tint32_t minGain = itGain->second.min().get<int32_t>();\n+\tint32_t maxGain = itGain->second.max().get<int32_t>();\n+\n+\tLOG(IPAC3ISP, Debug)\n+\t\t<< \"Exposure: [\" << minExposure << \", \" << maxExposure\n+\t\t<< \"], gain: [\" << minGain << \", \" << maxGain << \"]\";\n+\n+\tcontext_.configuration = {};\n+\tcontext_.activeState = {};\n+\tcontext_.frameContexts.clear();\n+\n+\tconst IPACameraSensorInfo &info = ipaConfig.sensorInfo;\n+\n+\tconst ControlInfo vBlank = sensorControls_.find(V4L2_CID_VBLANK)->second;\n+\tcontext_.configuration.sensor.defVBlank = vBlank.def().get<int32_t>();\n+\tcontext_.configuration.sensor.size = info.outputSize;\n+\tcontext_.configuration.sensor.lineDuration = info.minLineLength * 1.0s / info.pixelRate;\n+\n+\tupdateControls(info, sensorControls_, ipaControls);\n+\n+\tcontext_.configuration.sensor.minShutterSpeed =\n+\t\tminExposure * context_.configuration.sensor.lineDuration;\n+\tcontext_.configuration.sensor.maxShutterSpeed =\n+\t\tmaxExposure * context_.configuration.sensor.lineDuration;\n+\tcontext_.configuration.sensor.minAnalogueGain =\n+\t\tcontext_.camHelper->gain(minGain);\n+\tcontext_.configuration.sensor.maxAnalogueGain =\n+\t\tcontext_.camHelper->gain(maxGain);\n+\n+\tfor (auto const &a : algorithms()) {\n+\t\tAlgorithm *algo = static_cast<Algorithm *>(a.get());\n+\n+\t\tif (algo->disabled_)\n+\t\t\tcontinue;\n+\n+\t\tint ret = algo->configure(context_, info);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+void IPAC3ISP::mapBuffers(const std::vector<IPABuffer> &buffers)\n+{\n+\tfor (const IPABuffer &buffer : buffers) {\n+\t\tauto elem = buffers_.emplace(std::piecewise_construct,\n+\t\t\t\t\t std::forward_as_tuple(buffer.id),\n+\t\t\t\t\t std::forward_as_tuple(buffer.planes));\n+\t\tconst FrameBuffer &fb = elem.first->second;\n+\n+\t\tMappedFrameBuffer mappedBuffer(&fb, MappedFrameBuffer::MapFlag::ReadWrite);\n+\t\tif (!mappedBuffer.isValid()) {\n+\t\t\tLOG(IPAC3ISP, Fatal) << \"Failed tommap buffer: \"\n+\t\t\t\t\t << strerror(mappedBuffer.error());\n+\t\t}\n+\n+\t\tmappedBuffers_.emplace(buffer.id, std::move(mappedBuffer));\n+\t}\n+}\n+\n+void IPAC3ISP::unmapBuffers(const std::vector<unsigned int> &ids)\n+{\n+\tfor (unsigned int id : ids) {\n+\t\tconst auto fb = buffers_.find(id);\n+\t\tif (fb == buffers_.end())\n+\t\t\tcontinue;\n+\n+\t\tmappedBuffers_.erase(id);\n+\t\tbuffers_.erase(id);\n+\t}\n+}\n+\n+void IPAC3ISP::queueRequest(const uint32_t frame, const ControlList &controls)\n+{\n+\tIPAFrameContext &frameContext = context_.frameContexts.alloc(frame);\n+\n+\tfor (auto const &a : algorithms()) {\n+\t\tAlgorithm *algo = static_cast<Algorithm *>(a.get());\n+\t\tif (algo->disabled_)\n+\t\t\tcontinue;\n+\t\talgo->queueRequest(context_, frame, frameContext, controls);\n+\t}\n+}\n+\n+void IPAC3ISP::fillParamsBuffer(const uint32_t frame, const uint32_t bufferId)\n+{\n+\tIPAFrameContext &frameContext = context_.frameContexts.get(frame);\n+\n+\tC3ISPParams params(mappedBuffers_.at(bufferId).planes()[0]);\n+\n+\tfor (auto const &algo : algorithms())\n+\t\talgo->prepare(context_, frame, frameContext, ¶ms);\n+\n+\tparamsBufferReady.emit(frame, params.size());\n+}\n+\n+void IPAC3ISP::processStatsBuffer(const uint32_t frame, const uint32_t bufferId,\n+\t\t\t\t const ControlList &sensorControls)\n+{\n+\tIPAFrameContext &frameContext = context_.frameContexts.get(frame);\n+\n+\tconst c3_isp_stats_info *stats = nullptr;\n+\n+\tstats = reinterpret_cast<c3_isp_stats_info *>(\n+\t\tmappedBuffers_.at(bufferId).planes()[0].data());\n+\n+\tframeContext.sensor.exposure =\n+\t\tsensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>();\n+\tframeContext.sensor.gain =\n+\t\tcontext_.camHelper->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>());\n+\n+\tControlList metadata(controls::controls);\n+\n+\tfor (auto const &a : algorithms()) {\n+\t\tAlgorithm *algo = static_cast<Algorithm *>(a.get());\n+\t\tif (algo->disabled_)\n+\t\t\tcontinue;\n+\t\talgo->process(context_, frame, frameContext, stats, metadata);\n+\t}\n+\n+\tsetControls(frame);\n+\n+\tmetadataReady.emit(frame, metadata);\n+}\n+\n+void IPAC3ISP::updateControls(const IPACameraSensorInfo &sensorInfo,\n+\t\t\t const ControlInfoMap &sensorControls,\n+\t\t\t ControlInfoMap *ipaControls)\n+{\n+\tControlInfoMap::Map ctrlMap = c3ispControls;\n+\n+\tdouble lineDuration = context_.configuration.sensor.lineDuration.get<std::micro>();\n+\tconst ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second;\n+\tint32_t minExposure = v4l2Exposure.min().get<int32_t>() * lineDuration;\n+\tint32_t maxExposure = v4l2Exposure.max().get<int32_t>() * lineDuration;\n+\tint32_t defExposure = v4l2Exposure.def().get<int32_t>() * lineDuration;\n+\tctrlMap.emplace(std::piecewise_construct,\n+\t\t\tstd::forward_as_tuple(&controls::ExposureTime),\n+\t\t\tstd::forward_as_tuple(minExposure, maxExposure, defExposure));\n+\n+\tconst ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;\n+\tfloat minGain = context_.camHelper->gain(v4l2Gain.min().get<int32_t>());\n+\tfloat maxGain = context_.camHelper->gain(v4l2Gain.max().get<int32_t>());\n+\tfloat defGain = context_.camHelper->gain(v4l2Gain.def().get<int32_t>());\n+\tctrlMap.emplace(std::piecewise_construct,\n+\t\t\tstd::forward_as_tuple(&controls::AnalogueGain),\n+\t\t\tstd::forward_as_tuple(minGain, maxGain, defGain));\n+\n+\tconst ControlInfo &v4l2HBlank = sensorControls.find(V4L2_CID_HBLANK)->second;\n+\tuint32_t hblank = v4l2HBlank.def().get<int32_t>();\n+\tuint32_t lineLength = sensorInfo.outputSize.width + hblank;\n+\n+\tconst ControlInfo &v4l2VBlank = sensorControls.find(V4L2_CID_VBLANK)->second;\n+\tstd::array<uint32_t, 3> frameHeights{\n+\t\tv4l2VBlank.min().get<int32_t>() + sensorInfo.outputSize.height,\n+\t\tv4l2VBlank.max().get<int32_t>() + sensorInfo.outputSize.height,\n+\t\tv4l2VBlank.def().get<int32_t>() + sensorInfo.outputSize.height,\n+\t};\n+\n+\tstd::array<int64_t, 3> frameDurations;\n+\tfor (unsigned int i = 0; i < frameHeights.size(); ++i) {\n+\t\tuint64_t frameSize = lineLength * frameHeights[i];\n+\t\tframeDurations[i] = frameSize / (sensorInfo.pixelRate / 1000000U);\n+\t}\n+\n+\tctrlMap[&controls::FrameDurationLimits] = ControlInfo(frameDurations[0],\n+\t\t\t\t\t\t\t frameDurations[1],\n+\t\t\t\t\t\t\t frameDurations[2]);\n+\tctrlMap.insert(context_.ctrlMap.begin(), context_.ctrlMap.end());\n+\t*ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);\n+}\n+\n+void IPAC3ISP::setControls(unsigned int frame)\n+{\n+\tuint32_t exposure = context_.activeState.agc.exposure;\n+\tuint32_t gain = context_.camHelper->gainCode(context_.activeState.agc.gain);\n+\n+\tControlList ctrls(sensorControls_);\n+\tctrls.set(V4L2_CID_EXPOSURE, static_cast<int32_t>(exposure));\n+\tctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast<int32_t>(gain));\n+\n+\tsetSensorControls.emit(frame, ctrls);\n+}\n+\n+} /* namespace ipa::c3isp */\n+\n+/*\n+ * External IPA module interface\n+ */\n+\n+extern \"C\" {\n+const struct IPAModuleInfo ipaModuleInfo = {\n+\tIPA_MODULE_API_VERSION,\n+\t1,\n+\t\"c3isp\",\n+\t\"c3isp\",\n+};\n+\n+IPAInterface *ipaCreate()\n+{\n+\treturn new ipa::c3isp::IPAC3ISP();\n+}\n+}\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/c3-isp/data/imx290.yaml b/src/ipa/c3-isp/data/imx290.yaml\nnew file mode 100644\nindex 00000000..498a3684\n--- /dev/null\n+++ b/src/ipa/c3-isp/data/imx290.yaml\n@@ -0,0 +1,30 @@\n+# SPDX-License-Identifier: CC0-1.0\n+%YAML 1.1\n+---\n+version: 1\n+algorithms:\n+ - Agc:\n+ - Awb:\n+ - PostGamma:\n+ GammaLut: [\n+ 0, 86, 134, 169, 198, 223, 245, 265, 283, 300, 316, 331,\n+ 346, 359, 373, 385, 397, 409, 420, 431, 441, 452, 462, 471,\n+ 481, 490, 499, 508, 516, 525, 533, 541, 549, 557, 565, 572,\n+ 580, 587, 594, 601, 608, 615, 622, 629, 635, 642, 648, 655,\n+ 661, 667, 673, 679, 685, 691, 697, 703, 708, 714, 720, 725,\n+ 731, 736, 742, 747, 752, 758, 763, 768, 773, 778, 783, 788,\n+ 793, 798, 803, 808, 812, 817, 822, 827, 831, 836, 840, 845,\n+ 849, 854, 858, 863, 867, 872, 876, 880, 884, 889, 893, 897,\n+ 901, 905, 910, 914, 918, 922, 926, 930, 934, 938, 942, 946,\n+ 950, 953, 957, 961, 965, 969, 972, 976, 980, 984, 987, 991,\n+ 995, 998, 1002, 1006, 1009, 1013, 1016, 1020, 1023\n+ ]\n+ - Ccm:\n+ CcmCoeff: [ 533, -191, -86, -147, 474, -71, 23, -208, 441 ]\n+ - Csc:\n+ CscCoeff: [ 54, 183, 19, -29, -99, 128, 128, -116, -12 ]\n+ - Blc:\n+ BlcR: 20089\n+ BlcGr: 20090\n+ BlcGb: 20081\n+ BlcB: 20084\ndiff --git a/src/ipa/c3-isp/data/meson.build b/src/ipa/c3-isp/data/meson.build\nnew file mode 100644\nindex 00000000..e9ecfc2c\n--- /dev/null\n+++ b/src/ipa/c3-isp/data/meson.build\n@@ -0,0 +1,9 @@\n+# SPDX-License-Identifier: CC0-1.0\n+\n+conf_files = files([\n+ 'imx290.yaml',\n+])\n+\n+install_data(conf_files,\n+ install_dir : ipa_data_dir / 'c3isp',\n+ install_tag : 'runtime')\ndiff --git a/src/ipa/c3-isp/ipa_context.cpp b/src/ipa/c3-isp/ipa_context.cpp\nnew file mode 100644\nindex 00000000..3d087460\n--- /dev/null\n+++ b/src/ipa/c3-isp/ipa_context.cpp\n@@ -0,0 +1,251 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic Inc.\n+ *\n+ * C3ISP IPA Context\n+ */\n+\n+#include \"ipa_context.h\"\n+\n+/**\n+ * \\file ipa_context.h\n+ * \\brief Context and state information shared between the algorithms\n+ */\n+\n+namespace libcamera::ipa::c3isp {\n+\n+/**\n+ * \\struct IPASessionConfiguration\n+ * \\brief Session configuration for the IPA module\n+ *\n+ * The session configuration contains all IPA configuration parameters that\n+ * remain constant during the capture session, from IPA module start to stop.\n+ * It is typically set during the configure() operation of the IPA module, but\n+ * may also be updated in the start() operation.\n+ */\n+\n+/**\n+ * \\var IPASessionConfiguration::sensor\n+ * \\brief Sensor-specific configuration of the IPA\n+ *\n+ * \\var IPASessionConfiguration::sensor.minShutterSpeed\n+ * \\brief Minimum shutter speed supported with the sensor\n+ *\n+ * \\var IPASessionConfiguration::sensor.maxShutterSpeed\n+ * \\brief Maximum shutter speed supported with the sensor\n+ *\n+ * \\var IPASessionConfiguration::sensor.minAnalogueGain\n+ * \\brief Minimum analogue gain supported with the sensor\n+ *\n+ * \\var IPASessionConfiguration::sensor.maxAnalogueGain\n+ * \\brief Maximum analogue gain supported with the sensor\n+ *\n+ * \\var IPASessionConfiguration::sensor.defVBlank\n+ * \\brief The default vblank value of the sensor\n+ *\n+ * \\var IPASessionConfiguration::sensor.lineDuration\n+ * \\brief Line duration in microseconds\n+ *\n+ * \\var IPASessionConfiguration::sensor.size\n+ * \\brief Sensor output resolution\n+ */\n+\n+/**\n+ * \\struct IPAActiveState\n+ * \\brief Active state for algorithms\n+ *\n+ * The active state contains all algorithm-specific data that needs to be\n+ * maintained by algorithms across frames. Unlike the session configuration,\n+ * the active state is mutable and constantly updated by algorithms. The active\n+ * state is accessible through the IPAContext structure.\n+ *\n+ * The active state stores two distinct categories of information:\n+ *\n+ * - The consolidated value of all algorithm controls. Requests passed to\n+ * the queueRequest() function store values for controls that the\n+ * application wants to modify for that particular frame, and the\n+ * queueRequest() function updates the active state with those values.\n+ * The active state thus contains a consolidated view of the value of all\n+ * controls handled by the algorithm.\n+ *\n+ * - The value of parameters computed by the algorithm when running in auto\n+ * mode. Algorithms running in auto mode compute new parameters every\n+ * time statistics buffers are received (either synchronously, or\n+ * possibly in a background thread). The latest computed value of those\n+ * parameters is stored in the active state in the process() function.\n+ *\n+ * Each of the members in the active state belongs to a specific algorithm. A\n+ * member may be read by any algorithm, but shall only be written by its owner.\n+ */\n+\n+/**\n+ * \\var IPAActiveState::agc\n+ * \\brief State for the Automatic Gain Control algorithm\n+ *\n+ * The \\a automatic variables track the latest values computed by algorithm\n+ * based on the latest processed statistics. All other variables track the\n+ * consolidated controls requested in queued requests.\n+ *\n+ * \\var IPAActiveState::agc.exposure\n+ * \\brief Automatic exposure time expressed as a number of lines\n+ *\n+ * \\var IPAActiveState::agc.gain\n+ * \\brief Automatic analogue gain multiplier\n+ *\n+ * \\var IPAActiveState::agc.constraintMode\n+ * \\brief Constraint mode as set by the AeConstraintMode control\n+ *\n+ * \\var IPAActiveState::agc.exposureMode\n+ * \\brief Exposure mode as set by the AeExposureMode control\n+ */\n+\n+/**\n+ * \\var IPAActiveState::awb\n+ * \\brief State for the Automatic White Balance algorithm\n+ *\n+ * \\struct IPAActiveState::awb.gains\n+ * \\brief White balance gains\n+ *\n+ * \\struct IPAActiveState::awb.gains.manual\n+ * \\brief Manual white balance gains (set through requests)\n+ *\n+ * \\var IPAActiveState::awb.gains.manual.red\n+ * \\brief Manual white balance gain for R channel\n+ *\n+ * \\var IPAActiveState::awb.gains.manual.green\n+ * \\brief Manual white balance gain for G channel\n+ *\n+ * \\var IPAActiveState::awb.gains.manual.blue\n+ * \\brief Manual white balance gain for B channel\n+ *\n+ * \\struct IPAActiveState::awb.gains.automatic\n+ * \\brief Automatic white balance gains (computed by the algorithm)\n+ *\n+ * \\var IPAActiveState::awb.gains.automatic.red\n+ * \\brief Automatic white balance gain for R channel\n+ *\n+ * \\var IPAActiveState::awb.gains.automatic.green\n+ * \\brief Automatic white balance gain for G channel\n+ *\n+ * \\var IPAActiveState::awb.gains.automatic.blue\n+ * \\brief Automatic white balance gain for B channel\n+ *\n+ * \\var IPAActiveState::awb.temperatureK\n+ * \\brief Estimated color temperature\n+ *\n+ * \\var IPAActiveState::awb.autoEnabled\n+ * \\brief Whether the Auto White Balance algorithm is enabled\n+ */\n+\n+/**\n+ * \\struct IPAFrameContext\n+ * \\brief Per-frame context for algorithms\n+ *\n+ * The frame context stores two distinct categories of information:\n+ *\n+ * - The value of the controls to be applied to the frame. These values are\n+ * typically set in the queueRequest() function, from the consolidated\n+ * control values stored in the active state. The frame context thus stores\n+ * values for all controls related to the algorithm, not limited to the\n+ * controls specified in the corresponding request, but consolidated from all\n+ * requests that have been queued so far.\n+ *\n+ * For controls that can be set manually or computed by an algorithm\n+ * (depending on the algorithm operation mode), such as for instance the\n+ * colour gains for the AWB algorithm, the control value will be stored in\n+ * the frame context in the queueRequest() function only when operating in\n+ * manual mode. When operating in auto mode, the values are computed by the\n+ * algorithm in process(), stored in the active state, and copied to the\n+ * frame context in prepare(), just before being stored in the ISP parameters\n+ * buffer.\n+ *\n+ * The queueRequest() function can also store ancillary data in the frame\n+ * context, such as flags to indicate if (and what) control values have\n+ * changed compared to the previous request.\n+ *\n+ * - Status information computed by the algorithm for a frame. For instance,\n+ * the colour temperature estimated by the AWB algorithm from ISP statistics\n+ * calculated on a frame is stored in the frame context for that frame in\n+ * the process() function.\n+ */\n+\n+/**\n+ * \\var IPAFrameContext::agc\n+ * \\brief Automatic Gain Control parameters for this frame\n+ *\n+ * The exposure and gain are provided by the AGC algorithm, and are to be\n+ * applied to the sensor in order to take effect for this frame.\n+ *\n+ * \\var IPAFrameContext::agc.exposure\n+ * \\brief Exposure time expressed as a number of lines computed by the algorithm\n+ *\n+ * \\var IPAFrameContext::agc.gain\n+ * \\brief Analogue gain multiplier computed by the algorithm\n+ *\n+ * The gain should be adapted to the sensor specific gain code before applying.\n+ *\n+ * \\var IPAFrameContext::agc.autoEnabled\n+ * \\brief Manual/automatic AGC state as set by the AeEnable control\n+ *\n+ * \\var IPAFrameContext::agc.constraintMode\n+ * \\brief Constraint mode as set by the AeConstraintMode control\n+ *\n+ * \\var IPAFrameContext::agc.exposureMode\n+ * \\brief Exposure mode as set by the AeExposureMode control\n+ *\n+ * \\var IPAFrameContext::agc.meteringMode\n+ * \\brief Metering mode as set by the AeMeteringMode control\n+ *\n+ * \\var IPAFrameContext::agc.maxFrameDuration\n+ * \\brief Maximum frame duration as set by the FrameDurationLimits control\n+ *\n+ * \\var IPAFrameContext::agc.updateMetering\n+ * \\brief Indicate if new ISP AGC metering parameters need to be applied\n+ */\n+\n+/**\n+ * \\var IPAFrameContext::awb\n+ * \\brief Automatic White Balance parameters for this frame\n+ *\n+ * \\struct IPAFrameContext::awb.gains\n+ * \\brief White balance gains\n+ *\n+ * \\var IPAFrameContext::awb.gains.red\n+ * \\brief White balance gain for R channel\n+ *\n+ * \\var IPAFrameContext::awb.gains.green\n+ * \\brief White balance gain for G channel\n+ *\n+ * \\var IPAFrameContext::awb.gains.blue\n+ * \\brief White balance gain for B channel\n+ *\n+ * \\var IPAFrameContext::awb.autoEnabled\n+ * \\brief Whether the Auto White Balance algorithm is enabled\n+ */\n+\n+/**\n+ * \\var IPAFrameContext::sensor\n+ * \\brief Sensor configuration that used been used for this frame\n+ *\n+ * \\var IPAFrameContext::sensor.exposure\n+ * \\brief Exposure time expressed as a number of lines\n+ *\n+ * \\var IPAFrameContext::sensor.gain\n+ * \\brief Analogue gain multiplier\n+ */\n+\n+/**\n+ * \\struct IPAContext\n+ * \\brief Global IPA context data shared between all algorithms\n+ *\n+ * \\var IPAContext::configuration\n+ * \\brief The IPA session configuration, immutable during the session\n+ *\n+ * \\var IPAContext::activeState\n+ * \\brief The IPA active state, storing the latest state for all algorithms\n+ *\n+ * \\var IPAContext::frameContexts\n+ * \\brief Ring buffer of per-frame contexts\n+ */\n+\n+} /* namespace libcamera::ipa::c3isp */\ndiff --git a/src/ipa/c3-isp/ipa_context.h b/src/ipa/c3-isp/ipa_context.h\nnew file mode 100644\nindex 00000000..f1519c4c\n--- /dev/null\n+++ b/src/ipa/c3-isp/ipa_context.h\n@@ -0,0 +1,110 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic Inc.\n+ *\n+ * C3ISP IPA Context\n+ */\n+\n+#pragma once\n+\n+#include <memory>\n+\n+#include <linux/c3-isp-config.h>\n+\n+#include <libcamera/base/utils.h>\n+\n+#include <libcamera/control_ids.h>\n+#include <libcamera/controls.h>\n+#include <libcamera/geometry.h>\n+#include <libcamera/ipa/core_ipa_interface.h>\n+\n+#include <libipa/camera_sensor_helper.h>\n+#include <libipa/fc_queue.h>\n+\n+namespace libcamera {\n+\n+namespace ipa::c3isp {\n+\n+struct IPASessionConfiguration {\n+\tstruct {\n+\t\tutils::Duration minShutterSpeed;\n+\t\tutils::Duration maxShutterSpeed;\n+\t\tdouble minAnalogueGain;\n+\t\tdouble maxAnalogueGain;\n+\n+\t\tint32_t defVBlank;\n+\t\tutils::Duration lineDuration;\n+\t\tSize size;\n+\t} sensor;\n+};\n+\n+struct IPAActiveState {\n+\tstruct {\n+\t\tuint32_t exposure;\n+\t\tdouble gain;\n+\t\tuint32_t constraintMode;\n+\t\tuint32_t exposureMode;\n+\t} agc;\n+\n+\tstruct {\n+\t\tstruct {\n+\t\t\tstruct {\n+\t\t\t\tdouble red;\n+\t\t\t\tdouble green;\n+\t\t\t\tdouble blue;\n+\t\t\t} manual;\n+\t\t\tstruct {\n+\t\t\t\tdouble red;\n+\t\t\t\tdouble green;\n+\t\t\t\tdouble blue;\n+\t\t\t} automatic;\n+\t\t} gains;\n+\n+\t\tunsigned int temperatureK;\n+\t\tbool autoEnabled;\n+\t} awb;\n+};\n+\n+struct IPAFrameContext : public FrameContext {\n+\tstruct {\n+\t\tuint32_t exposure;\n+\t\tdouble gain;\n+\t\tbool autoEnabled;\n+\t\tcontrols::AeConstraintModeEnum constraintMode;\n+\t\tcontrols::AeExposureModeEnum exposureMode;\n+\t\tcontrols::AeMeteringModeEnum meteringMode;\n+\t\tutils::Duration maxFrameDuration;\n+\t\tbool updateMetering;\n+\t} agc;\n+\n+\tstruct {\n+\t\tstruct {\n+\t\t\tdouble red;\n+\t\t\tdouble green;\n+\t\t\tdouble blue;\n+\t\t} gains;\n+\n+\t\tbool autoEnabled;\n+\t} awb;\n+\n+\tstruct {\n+\t\tuint32_t exposure;\n+\t\tdouble gain;\n+\t} sensor;\n+};\n+\n+struct IPAContext {\n+\tIPASessionConfiguration configuration;\n+\tIPAActiveState activeState;\n+\n+\tFCQueue<IPAFrameContext> frameContexts;\n+\n+\tControlInfoMap::Map ctrlMap;\n+\n+\t/* Interface to the Camera Helper */\n+\tstd::unique_ptr<CameraSensorHelper> camHelper;\n+};\n+\n+} /* namespace ipa::c3isp */\n+\n+} /* namespace libcamera*/\ndiff --git a/src/ipa/c3-isp/meson.build b/src/ipa/c3-isp/meson.build\nnew file mode 100644\nindex 00000000..fa5c6be0\n--- /dev/null\n+++ b/src/ipa/c3-isp/meson.build\n@@ -0,0 +1,32 @@\n+# SPDX-License-Identifier: CC0-1.0\n+\n+subdir('algorithms')\n+subdir('data')\n+\n+ipa_name = 'ipa_c3isp'\n+\n+c3isp_ipa_sources = files([\n+ 'ipa_context.cpp',\n+ 'params.cpp',\n+ 'c3-isp.cpp',\n+])\n+\n+c3isp_ipa_sources += c3isp_ipa_algorithms\n+\n+mod = shared_module(ipa_name, c3isp_ipa_sources,\n+ name_prefix : '',\n+ include_directories : [ipa_includes],\n+ dependencies : [libcamera_private, libipa_dep],\n+ install : true,\n+ install_dir : ipa_install_dir)\n+\n+if ipa_sign_module\n+ custom_target(ipa_name + '.so.sign',\n+ input : mod,\n+ output : ipa_name + '.so.sign',\n+ command : [ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@'],\n+ install : false,\n+ build_by_default : true)\n+endif\n+\n+ipa_names += ipa_name\ndiff --git a/src/ipa/c3-isp/module.h b/src/ipa/c3-isp/module.h\nnew file mode 100644\nindex 00000000..1a66c5d4\n--- /dev/null\n+++ b/src/ipa/c3-isp/module.h\n@@ -0,0 +1,28 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic\n+ *\n+ * C3ISP IPA Module\n+ */\n+\n+#pragma once\n+\n+#include <linux/c3-isp-config.h>\n+\n+#include <libcamera/ipa/c3isp_ipa_interface.h>\n+\n+#include <libipa/module.h>\n+\n+#include \"ipa_context.h\"\n+#include \"params.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa::c3isp {\n+\n+using Module = ipa::Module<IPAContext, IPAFrameContext, IPACameraSensorInfo,\n+\t\t\t C3ISPParams, c3_isp_stats_info>;\n+\n+} /* namespace ipa::c3isp */\n+\n+} /* namespace libcamera*/\ndiff --git a/src/ipa/c3-isp/params.cpp b/src/ipa/c3-isp/params.cpp\nnew file mode 100644\nindex 00000000..fd682b53\n--- /dev/null\n+++ b/src/ipa/c3-isp/params.cpp\n@@ -0,0 +1,127 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic Inc.\n+ *\n+ * C3ISP ISP Parameters\n+ */\n+\n+#include \"params.h\"\n+\n+#include <map>\n+#include <stddef.h>\n+#include <string.h>\n+\n+#include <linux/c3-isp-config.h>\n+#include <linux/videodev2.h>\n+\n+#include <libcamera/base/log.h>\n+#include <libcamera/base/utils.h>\n+\n+namespace libcamera {\n+\n+LOG_DEFINE_CATEGORY(C3ISPParams)\n+\n+namespace ipa::c3isp {\n+\n+namespace {\n+\n+struct BlockTypeInfo {\n+\tenum c3_isp_params_block_type type;\n+\tsize_t size;\n+};\n+\n+#define C3ISP_BLOCK_TYPE_ENTRY(block, id, type) \\\n+\t{ \\\n+\t\tBlockType::block, \\\n+\t\t{ \\\n+\t\t\tC3_ISP_PARAMS_BLOCK_##id, \\\n+\t\t\t\tsizeof(struct c3_isp_params_##type), \\\n+\t\t} \\\n+\t}\n+\n+const std::map<BlockType, BlockTypeInfo> kBlockTypeInfo = {\n+\tC3ISP_BLOCK_TYPE_ENTRY(AWBGains, AWB_GAINS, awb_gains),\n+\tC3ISP_BLOCK_TYPE_ENTRY(AWBConfig, AWB_CONFIG, awb_config),\n+\tC3ISP_BLOCK_TYPE_ENTRY(AEConfig, AE_CONFIG, ae_config),\n+\tC3ISP_BLOCK_TYPE_ENTRY(AFConfig, AF_CONFIG, af_config),\n+\tC3ISP_BLOCK_TYPE_ENTRY(PostGamma, PST_GAMMA, pst_gamma),\n+\tC3ISP_BLOCK_TYPE_ENTRY(Ccm, CCM, ccm),\n+\tC3ISP_BLOCK_TYPE_ENTRY(Csc, CSC, csc),\n+\tC3ISP_BLOCK_TYPE_ENTRY(Blc, BLC, blc),\n+};\n+\n+} /* namespace */\n+\n+C3ISPParamsBlockBase::C3ISPParamsBlockBase(BlockType type,\n+\t\t\t\t\t const Span<uint8_t> &data)\n+\t: type_(type), data_(data)\n+{\n+\theader_ = data.subspan(0, sizeof(c3_isp_params_block_header));\n+}\n+\n+void C3ISPParamsBlockBase::setEnabled(uint16_t flags)\n+{\n+\tstruct c3_isp_params_block_header *header =\n+\t\treinterpret_cast<struct c3_isp_params_block_header *>(header_.data());\n+\n+\theader->flags = flags;\n+}\n+\n+C3ISPParams::C3ISPParams(Span<uint8_t> data)\n+\t: data_(data), used_(0)\n+{\n+\tstruct c3_isp_params_cfg *buffer =\n+\t\treinterpret_cast<struct c3_isp_params_cfg *>(data.data());\n+\n+\tbuffer->version = C3_ISP_PARAMS_BUFFER_V0;\n+\tbuffer->data_size = 0;\n+\n+\tused_ += offsetof(struct c3_isp_params_cfg, data);\n+}\n+\n+Span<uint8_t> C3ISPParams::block(BlockType type)\n+{\n+\tauto infoIt = kBlockTypeInfo.find(type);\n+\tif (infoIt == kBlockTypeInfo.end()) {\n+\t\tLOG(C3ISPParams, Error)\n+\t\t\t<< \"Invalid parameters type \"\n+\t\t\t<< utils::to_underlying(type);\n+\t\treturn {};\n+\t}\n+\n+\tconst BlockTypeInfo &info = infoIt->second;\n+\n+\tauto cacheIt = blocks_.find(type);\n+\tif (cacheIt != blocks_.end())\n+\t\treturn cacheIt->second;\n+\n+\tsize_t size = info.size;\n+\tif (size > data_.size() - used_) {\n+\t\tLOG(C3ISPParams, Error)\n+\t\t\t<< \"Out of memory to allocate block type \"\n+\t\t\t<< utils::to_underlying(type);\n+\t\treturn {};\n+\t}\n+\n+\tSpan<uint8_t> block = data_.subspan(used_, info.size);\n+\tused_ += block.size();\n+\n+\tstruct c3_isp_params_cfg *buffer =\n+\t\treinterpret_cast<struct c3_isp_params_cfg *>(data_.data());\n+\tbuffer->data_size += block.size();\n+\n+\tmemset(block.data(), 0, block.size());\n+\n+\tstruct c3_isp_params_block_header *header =\n+\t\treinterpret_cast<struct c3_isp_params_block_header *>(block.data());\n+\theader->type = info.type;\n+\theader->size = block.size();\n+\n+\tblocks_[type] = block;\n+\n+\treturn block;\n+}\n+\n+} /* namespace ipa::c3isp */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/c3-isp/params.h b/src/ipa/c3-isp/params.h\nnew file mode 100644\nindex 00000000..9bb3877b\n--- /dev/null\n+++ b/src/ipa/c3-isp/params.h\n@@ -0,0 +1,133 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic Inc.\n+ *\n+ * C3ISP ISP Parameters\n+ */\n+\n+#pragma once\n+\n+#include <map>\n+#include <stdint.h>\n+\n+#include <linux/c3-isp-config.h>\n+\n+#include <libcamera/base/class.h>\n+#include <libcamera/base/span.h>\n+\n+namespace libcamera {\n+\n+namespace ipa::c3isp {\n+\n+enum class BlockType {\n+\tAWBGains,\n+\tAWBConfig,\n+\tAEConfig,\n+\tAFConfig,\n+\tPostGamma,\n+\tCcm,\n+\tCsc,\n+\tBlc,\n+};\n+\n+namespace details {\n+\n+template<BlockType B>\n+struct block_type {\n+};\n+\n+#define C3ISP_DEFINE_BLOCK_TYPE(blocktype, blockStruct) \\\n+\ttemplate<> \\\n+\tstruct block_type<BlockType::blocktype> { \\\n+\t\tusing type = struct c3_isp_params_##blockStruct; \\\n+\t};\n+\n+C3ISP_DEFINE_BLOCK_TYPE(AWBGains, awb_gains)\n+C3ISP_DEFINE_BLOCK_TYPE(AWBConfig, awb_config)\n+C3ISP_DEFINE_BLOCK_TYPE(AEConfig, ae_config)\n+C3ISP_DEFINE_BLOCK_TYPE(AFConfig, af_config)\n+C3ISP_DEFINE_BLOCK_TYPE(PostGamma, pst_gamma)\n+C3ISP_DEFINE_BLOCK_TYPE(Ccm, ccm)\n+C3ISP_DEFINE_BLOCK_TYPE(Csc, csc)\n+C3ISP_DEFINE_BLOCK_TYPE(Blc, blc)\n+\n+} /* namespace details */\n+\n+class C3ISPParams;\n+\n+class C3ISPParamsBlockBase\n+{\n+public:\n+\tC3ISPParamsBlockBase(BlockType type, const Span<uint8_t> &data);\n+\n+\tSpan<uint8_t> data() const { return data_; }\n+\n+\tvoid setEnabled(uint16_t flags);\n+\n+private:\n+\tLIBCAMERA_DISABLE_COPY(C3ISPParamsBlockBase)\n+\n+\tBlockType type_;\n+\tSpan<uint8_t> header_;\n+\tSpan<uint8_t> data_;\n+};\n+\n+template<BlockType B>\n+class C3ISPParamsBlock : public C3ISPParamsBlockBase\n+{\n+public:\n+\tusing Type = typename details::block_type<B>::type;\n+\n+\tC3ISPParamsBlock(const Span<uint8_t> &data)\n+\t\t: C3ISPParamsBlockBase(B, data)\n+\t{\n+\t}\n+\n+\tconst Type *operator->() const\n+\t{\n+\t\treturn reinterpret_cast<const Type *>(data().data());\n+\t}\n+\n+\tType *operator->()\n+\t{\n+\t\treturn reinterpret_cast<Type *>(data().data());\n+\t}\n+\n+\tconst Type &operator*() const &\n+\t{\n+\t\treturn *reinterpret_cast<const Type *>(data().data());\n+\t}\n+\n+\tconst Type &operator*() &\n+\t{\n+\t\treturn *reinterpret_cast<Type *>(data().data());\n+\t}\n+};\n+\n+class C3ISPParams\n+{\n+public:\n+\tC3ISPParams(Span<uint8_t> data);\n+\n+\ttemplate<BlockType B>\n+\tC3ISPParamsBlock<B> block()\n+\t{\n+\t\treturn C3ISPParamsBlock<B>(block(B));\n+\t}\n+\n+\tsize_t size() const { return used_; }\n+\n+private:\n+\tfriend class C3ISPParamsBlockBase;\n+\n+\tSpan<uint8_t> block(BlockType type);\n+\n+\tSpan<uint8_t> data_;\n+\tsize_t used_;\n+\n+\tstd::map<BlockType, Span<uint8_t>> blocks_;\n+};\n+\n+} /* namespace ipa::c3isp */\n+\n+} /* namespace libcamera */\n", "prefixes": [ "v2", "2/2" ] }