{"id":22293,"url":"https://patchwork.libcamera.org/api/patches/22293/?format=json","web_url":"https://patchwork.libcamera.org/patch/22293/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20241212021246.115736-3-keke.li@amlogic.com>","date":"2024-12-12T02:12:46","name":"[2/2] libcamera: ipa: Add C3 ISP IPA","commit_ref":null,"pull_url":null,"state":"superseded","archived":false,"hash":"e513385418d67a59b6e2feb043087e272959795c","submitter":{"id":217,"url":"https://patchwork.libcamera.org/api/people/217/?format=json","name":"Keke Li","email":"keke.li@amlogic.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/22293/mbox/","series":[{"id":4875,"url":"https://patchwork.libcamera.org/api/series/4875/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=4875","date":"2024-12-12T02:12:44","name":"Add Amlogic C3 ISP pipeline handler and IPA","version":1,"mbox":"https://patchwork.libcamera.org/series/4875/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/22293/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/22293/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 5734CC3260\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 12 Dec 2024 02:13:02 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 2E23467EC5;\n\tThu, 12 Dec 2024 03:13:02 +0100 (CET)","from mail-sh.amlogic.com (unknown [58.32.228.46])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 1C7B867EBA\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 12 Dec 2024 03:12: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\tThu, 12 Dec 2024 10:12:52 +0800"],"From":"Keke Li <keke.li@amlogic.com>","To":"<libcamera-devel@lists.libcamera.org>","CC":"Keke Li <keke.li@amlogic.com>","Subject":"[PATCH 2/2] libcamera: ipa: Add C3 ISP IPA","Date":"Thu, 12 Dec 2024 10:12:46 +0800","Message-ID":"<20241212021246.115736-3-keke.li@amlogic.com>","X-Mailer":"git-send-email 2.29.0","In-Reply-To":"<20241212021246.115736-1-keke.li@amlogic.com>","References":"<20241212021246.115736-1-keke.li@amlogic.com>","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","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            | 555 +++++++++++++++++++++++\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, 2773 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..007ef258\n--- /dev/null\n+++ b/include/linux/c3-isp-config.h\n@@ -0,0 +1,555 @@\n+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */\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\n+ * 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\n+ * all blocks information and a 1024-bin 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\n+ * in floating point format with 16 bits mantissa and\n+ * 5 or 6 bits exponent.\n+ * Apart from contrast metrics we accumulate squared image and\n+ * 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_buffer - 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_buffer {\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\n+ * the parameters block data.\n+ *\n+ * @C3_ISP_PARAMS_BLOCK_AWB_GAINS: White balance gains\n+ * @C3_ISP_PARAMS_BLOCK_AWB_CONFIG: AE statistics parameters\n+ * @C3_ISP_PARAMS_BLOCK_AE_CONFIG: AE statistics parameters\n+ * @C3_ISP_PARAMS_BLOCK_AF_CONFIG: AF statistics parameters\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_NONE 0\n+#define C3_ISP_PARAMS_BLOCK_FL_DISABLED BIT(0)\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_NONE;\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. There\n+ * are four gain settings corresponding to each colour channel in the bayer domain.\n+ * 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+};\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        value: 0: disable, 1: enable\n+ * @horiz_zones_num: active zones number of hotizontal [0..32]\n+ * @vert_zones_num: active zones number of vertical [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 point on the diagonal [0..2888]\n+ * @vert_cood: the vertical coordinate of point 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+};\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 zones number of hotizontal [0..17]\n+ * @vert_zones_num: active zones number of vertical [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 point on the diagonal [0..2888]\n+ * @vert_cood: the vertical coordinate of point on the diagonal [0..2240]\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+};\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 zones number of hotizontal [0..17]\n+ * @vert_zones_num: active zones number of vertical [0..15]\n+ * @horiz_cood: the horizontal coordinate of point on the diagonal [0..2888]\n+ * @vert_cood: the vertical coordinate of point 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__u16 horiz_cood[C3_ISP_AF_MAX_PT_NUM];\n+\t__u16 vert_cood[C3_ISP_AF_MAX_PT_NUM];\n+};\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: look up table for P-Stitch gamma [0..1023]\n+ */\n+struct c3_isp_params_pst_gamma {\n+\tstruct c3_isp_params_block_header header;\n+\t__u16 lut[129];\n+};\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+ */\n+struct c3_isp_params_ccm {\n+\tstruct c3_isp_params_block_header header;\n+\t__s16 matrix[3][3];\n+};\n+\n+/**\n+ * struct c3_isp_params_csc - ISP Color Space Conversion configuration\n+ *\n+ * This struct allows the configuration of the matrix for\n+ * color space conversion. 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_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+ */\n+struct c3_isp_params_csc {\n+\tstruct c3_isp_params_block_header header;\n+\t__s16 matrix[3][3];\n+};\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\n+ * for each 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,\n+ *\t\texcluding this header\n+ * @data: The C3 ISP configuration blocks data\n+ */\n+struct c3_isp_params_cfg {\n+\t__u8 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 bd782830..c4306f00 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', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'],\n+        choices : ['c3-isp', 'ipu3', '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..35674be3\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_NONE);\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_buffer *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_buffer *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..d67092c2\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_buffer *stats,\n+\t\t     ControlList &metadata) override;\n+private:\n+\tHistogram parseStatistics(const c3_isp_stats_buffer *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..cbeccc3e\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_NONE);\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_NONE);\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  [[maybe_unused]] const c3_isp_stats_buffer *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..5f0cbfbb\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_buffer *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..3c364596\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_NONE);\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_buffer *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..4b17adfa\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_buffer *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..115a5a37\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_NONE);\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_buffer *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..b5199136\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_buffer *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..23d86d1e\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_NONE);\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..3f1dcf66\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_NONE);\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..02ce1b0b\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, &params);\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_buffer *stats = nullptr;\n+\n+\tstats = reinterpret_cast<c3_isp_stats_buffer *>(\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..d2706766\n--- /dev/null\n+++ b/src/ipa/c3-isp/data/imx290.yaml\n@@ -0,0 +1,30 @@\n+# SPDX-License-Identifier: CC0-1.0\r\n+%YAML 1.1\r\n+---\r\n+version: 1\r\n+algorithms:\r\n+  - Agc:\r\n+  - Awb:\r\n+  - PostGamma:\r\n+      GammaLut: [\r\n+                   0,   86,  134,  169,  198,  223,  245,  265,  283, 300, 316, 331,\r\n+                   346, 359, 373,  385,  397,  409,  420,  431,  441, 452, 462, 471,\r\n+                   481, 490, 499,  508,  516,  525,  533,  541,  549, 557, 565, 572,\r\n+                   580, 587, 594,  601,  608,  615,  622,  629,  635, 642, 648, 655,\r\n+                   661, 667, 673,  679,  685,  691,  697,  703,  708, 714, 720, 725,\r\n+                   731, 736, 742,  747,  752,  758,  763,  768,  773, 778, 783, 788,\r\n+                   793, 798, 803,  808,  812,  817,  822,  827,  831, 836, 840, 845,\r\n+                   849, 854, 858,  863,  867,  872,  876,  880,  884, 889, 893, 897,\r\n+                   901, 905, 910,  914,  918,  922,  926,  930,  934, 938, 942, 946,\r\n+                   950, 953, 957,  961,  965,  969,  972,  976,  980, 984, 987, 991,\r\n+                   995, 998, 1002, 1006, 1009, 1013, 1016, 1020, 1023\r\n+                ]\r\n+  - Ccm:\r\n+      CcmCoeff: [ 533, -191, -86, -147, 474, -71, 23, -208, 441 ]\r\n+  - Csc:\r\n+      CscCoeff: [ 54, 183, 19, -29, -99, 128, 128, -116, -12 ]\r\n+  - Blc:\r\n+      BlcR: 20089\r\n+      BlcGr: 20090\r\n+      BlcGb: 20081\r\n+      BlcB: 20084\r\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..07116f80\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_buffer>;\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":["2/2"]}