[{"id":33297,"web_url":"https://patchwork.libcamera.org/comment/33297/","msgid":"<480215f2-2edd-4540-b8a1-336ff3095657@amlogic.com>","date":"2025-02-05T11:46:54","subject":"Re: [PATCH v2 2/2] libcamera: ipa: Add C3 ISP IPA","submitter":{"id":217,"url":"https://patchwork.libcamera.org/api/people/217/","name":"Keke Li","email":"keke.li@amlogic.com"},"content":"Hi Dan\n\nThanks for your reply.\n\nOn 2025/1/22 22:32, Dan Scally wrote:\n> [ EXTERNAL EMAIL ]\n>\n> Hi Keke, thanks for the patch\n>\n> On 27/12/2024 10:58, Keke Li wrote:\n>> The C3 ISP IPA is used to process 3A statistics\n>> and generate parameters for ISP hardware.\n>>\n>> Signed-off-by: Keke Li <keke.li@amlogic.com>\n>> ---\n>>   include/linux/c3-isp-config.h            | 564 +++++++++++++++++++++++\n>>   meson_options.txt                        |   2 +-\n>>   src/ipa/c3-isp/algorithms/agc.cpp        | 260 +++++++++++\n>>   src/ipa/c3-isp/algorithms/agc.h          |  50 ++\n>>   src/ipa/c3-isp/algorithms/algorithm.h    |  31 ++\n>>   src/ipa/c3-isp/algorithms/awb.cpp        | 257 +++++++++++\n>>   src/ipa/c3-isp/algorithms/awb.h          |  42 ++\n>>   src/ipa/c3-isp/algorithms/blc.cpp        | 102 ++++\n>>   src/ipa/c3-isp/algorithms/blc.h          |  40 ++\n>>   src/ipa/c3-isp/algorithms/ccm.cpp        |  86 ++++\n>>   src/ipa/c3-isp/algorithms/ccm.h          |  40 ++\n>>   src/ipa/c3-isp/algorithms/csc.cpp        |  64 +++\n>>   src/ipa/c3-isp/algorithms/csc.h          |  32 ++\n>>   src/ipa/c3-isp/algorithms/meson.build    |  10 +\n>>   src/ipa/c3-isp/algorithms/post_gamma.cpp |  64 +++\n>>   src/ipa/c3-isp/algorithms/post_gamma.h   |  33 ++\n>>   src/ipa/c3-isp/c3-isp.cpp                | 386 ++++++++++++++++\n>>   src/ipa/c3-isp/data/imx290.yaml          |  30 ++\n>>   src/ipa/c3-isp/data/meson.build          |   9 +\n>>   src/ipa/c3-isp/ipa_context.cpp           | 251 ++++++++++\n>>   src/ipa/c3-isp/ipa_context.h             | 110 +++++\n>>   src/ipa/c3-isp/meson.build               |  32 ++\n>>   src/ipa/c3-isp/module.h                  |  28 ++\n>>   src/ipa/c3-isp/params.cpp                | 127 +++++\n>>   src/ipa/c3-isp/params.h                  | 133 ++++++\n> This is a lot of additions for a single commit. It might be \n> administratively easier to break them\n> down; the header file should certainly have its own commit, and each \n> of the algorithms could also\n> very easily have their own commit. It would be nice also to have \n> additions for libtuning if its\n> possible so that users can generate tuning files for this IPA \n> automatically.\n\n\nOK, will break them down.\n\n>>   25 files changed, 2782 insertions(+), 1 deletion(-)\n>>   create mode 100644 include/linux/c3-isp-config.h\n>>   create mode 100644 src/ipa/c3-isp/algorithms/agc.cpp\n>>   create mode 100644 src/ipa/c3-isp/algorithms/agc.h\n>>   create mode 100644 src/ipa/c3-isp/algorithms/algorithm.h\n>>   create mode 100755 src/ipa/c3-isp/algorithms/awb.cpp\n>>   create mode 100755 src/ipa/c3-isp/algorithms/awb.h\n>>   create mode 100644 src/ipa/c3-isp/algorithms/blc.cpp\n>>   create mode 100644 src/ipa/c3-isp/algorithms/blc.h\n>>   create mode 100644 src/ipa/c3-isp/algorithms/ccm.cpp\n>>   create mode 100644 src/ipa/c3-isp/algorithms/ccm.h\n>>   create mode 100644 src/ipa/c3-isp/algorithms/csc.cpp\n>>   create mode 100644 src/ipa/c3-isp/algorithms/csc.h\n>>   create mode 100644 src/ipa/c3-isp/algorithms/meson.build\n>>   create mode 100644 src/ipa/c3-isp/algorithms/post_gamma.cpp\n>>   create mode 100644 src/ipa/c3-isp/algorithms/post_gamma.h\n>>   create mode 100644 src/ipa/c3-isp/c3-isp.cpp\n>>   create mode 100644 src/ipa/c3-isp/data/imx290.yaml\n>>   create mode 100644 src/ipa/c3-isp/data/meson.build\n>>   create mode 100644 src/ipa/c3-isp/ipa_context.cpp\n>>   create mode 100644 src/ipa/c3-isp/ipa_context.h\n>>   create mode 100644 src/ipa/c3-isp/meson.build\n>>   create mode 100644 src/ipa/c3-isp/module.h\n>>   create mode 100644 src/ipa/c3-isp/params.cpp\n>>   create mode 100644 src/ipa/c3-isp/params.h\n>>\n>> diff --git a/include/linux/c3-isp-config.h \n>> b/include/linux/c3-isp-config.h\n>> new file mode 100644\n>> index 00000000..ee673ed0\n>> --- /dev/null\n>> +++ b/include/linux/c3-isp-config.h\n> As above; this needs to go in its own commit. The commit message \n> should emphasise that it's a\n> temporary addition - once the driver is merged to the kernel then \n> it'll be handled by a script we have.\n\n\nOK, will indicate that it's a temporary addition.\n\n>> @@ -0,0 +1,564 @@\n>> +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */\n>> +/*\n>> + * Copyright (C) 2024 Amlogic, Inc. All rights reserved\n>> + */\n>> +\n>> +#ifndef _UAPI_C3_ISP_CONFIG_H_\n>> +#define _UAPI_C3_ISP_CONFIG_H_\n>> +\n>> +#include <linux/types.h>\n>> +\n>> +/*\n>> + * Frames are split into zones of almost equal width and height - a \n>> zone is a\n>> + * rectangular tile of a frame. The metering blocks within the ISP \n>> 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 \n>> 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>> +     __u16 rg;\n>> +     __u16 bg;\n>> +     __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>> +     struct c3_isp_awb_zone_stats stats[C3_ISP_AWB_MAX_ZONES];\n>> +} __attribute__((aligned(16)));\n>> +\n>> +/**\n>> + * struct c3_isp_ae_zone_stats - AE statistics of a zone\n>> + *\n>> + * AE zone stats is aligned with 8 bytes.\n>> + * This is a 5-bin histogram and the total sum is normalized to 0xffff.\n>> + * So hist2 = 0xffff - (hist0 + hist1 + hist3 + hist4)\n>> + *\n>> + * @hist0: the global normalized pixel count for bin 0\n>> + * @hist1: the global normalized pixel count for bin 1\n>> + * @hist3: the global normalized pixel count for bin 3\n>> + * @hist4: the global normalized pixel count for bin 4\n>> + */\n>> +struct c3_isp_ae_zone_stats {\n>> +     __u16 hist0;\n>> +     __u16 hist1;\n>> +     __u16 hist3;\n>> +     __u16 hist4;\n>> +};\n>> +\n>> +/**\n>> + * struct c3_isp_ae_stats - Exposure statistics information\n>> + *\n>> + * AE statistical information consists of all blocks information and \n>> a 1024-bin\n>> + * histogram.\n>> + *\n>> + * @stats: array of auto exposure block statistics\n>> + * @reserved: undefined buffer space\n>> + * @hist: a 1024-bin histogram for the entire image\n>> + */\n>> +struct c3_isp_ae_stats {\n>> +     struct c3_isp_ae_zone_stats stats[C3_ISP_AE_MAX_ZONES];\n>> +     __u32 reserved[2];\n>> +     __u32 hist[1024];\n>> +} __attribute__((aligned(16)));\n>> +\n>> +/**\n>> + * struct c3_isp_af_zone_stats - AF statistics of a zone\n>> + *\n>> + * AF zone stats is aligned with 8 bytes.\n>> + * The zonal accumulated contrast metrics are stored in floating \n>> point format\n>> + * with 16 bits mantissa and 5 or 6 bits exponent. Apart from \n>> contrast metrics\n>> + * we accumulate squared image and quartic image data over the zone.\n>> + *\n>> + * @i2_mat: the mantissa of zonal squared image pixel sum\n>> + * @i4_mat: the mantissa of zonal quartic image pixel sum\n>> + * @e4_mat: the mantissa of zonal multi-directional quartic edge sum\n>> + * @e4_exp: the exponent of zonal multi-directional quartic edge sum\n>> + * @i2_exp: the exponent of zonal squared image pixel sum\n>> + * @i4_exp: the exponent of zonal quartic image pixel sum\n>> + */\n>> +struct c3_isp_af_zone_stats {\n>> +     __u16 i2_mat;\n>> +     __u16 i4_mat;\n>> +     __u16 e4_mat;\n>> +     __u16 e4_exp : 5;\n>> +     __u16 i2_exp : 5;\n>> +     __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>> +     struct c3_isp_af_zone_stats stats[C3_ISP_AF_MAX_ZONES];\n>> +     __u32 reserved[2];\n>> +} __attribute__((aligned(16)));\n>> +\n>> +/**\n>> + * struct c3_isp_stats_info - V4L2_META_FMT_C3ISP_STATS\n>> + *\n>> + * Contains ISP statistics\n>> + *\n>> + * @awb: auto white balance stats\n>> + * @ae: auto exposure stats\n>> + * @af: auto focus stats\n>> + */\n>> +struct c3_isp_stats_info {\n>> +     struct c3_isp_awb_stats awb;\n>> +     struct c3_isp_ae_stats ae;\n>> +     struct c3_isp_af_stats af;\n>> +};\n>> +\n>> +/**\n>> + * enum c3_isp_params_buffer_version -  C3 ISP parameters block \n>> versioning\n>> + *\n>> + * @C3_ISP_PARAMS_BUFFER_V0: First version of C3 ISP parameters block\n>> + */\n>> +enum c3_isp_params_buffer_version {\n>> +     C3_ISP_PARAMS_BUFFER_V0,\n>> +};\n>> +\n>> +/**\n>> + * enum c3_isp_params_block_type - Enumeration of C3 ISP parameter \n>> blocks\n>> + *\n>> + * Each block configures a specific processing block of the C3 ISP.\n>> + * The block type allows the driver to correctly interpret the \n>> parameters block\n>> + * data.\n>> + *\n>> + * @C3_ISP_PARAMS_BLOCK_AWB_GAINS: White balance gains\n>> + * @C3_ISP_PARAMS_BLOCK_AWB_CONFIG: AWB statistic format \n>> configuration for all\n>> + *                                  blocks that control how stats \n>> are generated\n>> + * @C3_ISP_PARAMS_BLOCK_AE_CONFIG: AE statistic format configuration \n>> for all\n>> + *                                 blocks that control how stats are \n>> generated\n>> + * @C3_ISP_PARAMS_BLOCK_AF_CONFIG: AF statistic format configuration \n>> for all\n>> + *                                 blocks that control how stats are \n>> generated\n>> + * @C3_ISP_PARAMS_BLOCK_PST_GAMMA: post gamma parameters\n>> + * @C3_ISP_PARAMS_BLOCK_CCM: Color correction matrix parameters\n>> + * @C3_ISP_PARAMS_BLOCK_CSC: Color space conversion parameters\n>> + * @C3_ISP_PARAMS_BLOCK_BLC: Black level correction parameters\n>> + * @C3_ISP_PARAMS_BLOCK_SENTINEL: First non-valid block index\n>> + */\n>> +enum c3_isp_params_block_type {\n>> +     C3_ISP_PARAMS_BLOCK_AWB_GAINS,\n>> +     C3_ISP_PARAMS_BLOCK_AWB_CONFIG,\n>> +     C3_ISP_PARAMS_BLOCK_AE_CONFIG,\n>> +     C3_ISP_PARAMS_BLOCK_AF_CONFIG,\n>> +     C3_ISP_PARAMS_BLOCK_PST_GAMMA,\n>> +     C3_ISP_PARAMS_BLOCK_CCM,\n>> +     C3_ISP_PARAMS_BLOCK_CSC,\n>> +     C3_ISP_PARAMS_BLOCK_BLC,\n>> +     C3_ISP_PARAMS_BLOCK_SENTINEL\n>> +};\n>> +\n>> +#define C3_ISP_PARAMS_BLOCK_FL_DISABLE (1U << 0)\n>> +#define C3_ISP_PARAMS_BLOCK_FL_ENABLE (1U << 1)\n>> +\n>> +/**\n>> + * struct c3_isp_params_block_header - C3 ISP parameter block header\n>> + *\n>> + * This structure represents the common part of all the ISP \n>> configuration\n>> + * blocks. Each parameters block shall embed an instance of this \n>> structure type\n>> + * as its first member, followed by the block-specific configuration \n>> data. The\n>> + * driver inspects this common header to discern the block type and \n>> its size and\n>> + * properly handle the block content by casting it to the correct \n>> 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 \n>> 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 \n>> 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 \n>> field. In\n>> + * this case userspace may optionally omit the remainder of the \n>> configuration\n>> + * block, which will be ignored by the driver.\n>> + *\n>> + * When a new configuration of an ISP block needs to be applied \n>> 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 \n>> block header\n>> + * fields (@type, @flags and @size) and the block-specific parameters.\n>> + *\n>> + * For example:\n>> + *\n>> + * .. code-block:: c\n>> + *\n>> + *   void populate_pst_gamma(struct c3_isp_params_block_header \n>> *block) {\n>> + *           struct c3_isp_params_pst_gamma *gamma =\n>> + *                   (struct c3_isp_params_pst_gamma *)block;\n>> + *\n>> + *           gamma->header.type = C3_ISP_PARAMS_BLOCK_PST_GAMMA;\n>> + *           gamma->header.flags = C3_ISP_PARAMS_BLOCK_FL_ENABLE;\n>> + *           gamma->header.size = sizeof(*gamma);\n>> + *\n>> + *           for (unsigned int i = 0; i < 129; i++)\n>> + *                   gamma->pst_gamma_lut[i] = i;\n>> + *   }\n>> + *\n>> + * @type: The parameters block type from \n>> :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 \n>> header\n>> + */\n>> +struct c3_isp_params_block_header {\n>> +     __u16 type;\n>> +     __u16 flags;\n>> +     __u32 size;\n>> +};\n>> +\n>> +/**\n>> + * struct c3_isp_params_awb_gains - Gains for auto-white balance\n>> + *\n>> + * This struct allows users to configure the gains for white balance.\n>> + * There are four gain settings corresponding to each colour channel in\n>> + * the bayer domain. All of the gains are stored in Q4.8 format.\n>> + *\n>> + * header.type should be set to C3_ISP_PARAMS_BLOCK_AWB_GAINS\n>> + * from :c:type:`c3_isp_params_block_type`\n>> + *\n>> + * @header: The C3 ISP parameters block header\n>> + * @gr_gain: Multiplier for Gr channel (Q4.8 format)\n>> + * @r_gain: Multiplier for R channel (Q4.8 format)\n>> + * @b_gain: Multiplier for B channel (Q4.8 format)\n>> + * @gb_gain: Multiplier for Gb channel (Q4.8 format)\n>> + */\n>> +struct c3_isp_params_awb_gains {\n>> +     struct c3_isp_params_block_header header;\n>> +     __u16 gr_gain;\n>> +     __u16 r_gain;\n>> +     __u16 b_gain;\n>> +     __u16 gb_gain;\n>> +} __attribute__((aligned(8)));\n>> +\n>> +/**\n>> + * enum c3_isp_params_awb_tap_points - Tap points for the AWB \n>> statistics\n>> + * @C3_ISP_AWB_STATS_TAP_FE: immediately after the optical frontend \n>> 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 \n>> balance block\n>> + * @C3_ISP_AWB_STATS_TAP_AFTER_WB: immediately after the white \n>> balance block\n>> + */\n>> +enum c3_isp_params_awb_tap_point {\n>> +     C3_ISP_AWB_STATS_TAP_OFE = 0,\n>> +     C3_ISP_AWB_STATS_TAP_GE,\n>> +     C3_ISP_AWB_STATS_TAP_BEFORE_WB,\n>> +     C3_ISP_AWB_STATS_TAP_AFTER_WB,\n>> +};\n>> +\n>> +/**\n>> + * struct c3_isp_params_awb_config - Stats settings for auto-white \n>> balance\n>> + *\n>> + * This struct allows the configuration of the statistics generated \n>> 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>> + *           value: 0: disable, 1: enable\n>> + * @horiz_zones_num: active number of hotizontal zones [0..32]\n>> + * @vert_zones_num: active number of vertical zones [0..24]\n>> + * @rg_min: minimum R/G ratio (Q4.8 format)\n>> + * @rg_max: maximum R/G ratio (Q4.8 format)\n>> + * @bg_min: minimum B/G ratio (Q4.8 format)\n>> + * @bg_max: maximum B/G ratio (Q4.8 format)\n>> + * @rg_low: R/G ratio trim low (Q4.8 format)\n>> + * @rg_high: R/G ratio trim hight (Q4.8 format)\n>> + * @bg_low: B/G ratio trim low (Q4.8 format)\n>> + * @bg_high: B/G ratio trim high (Q4.8 format)\n>> + * @zone_weight: array of weights for AWB statistics zones [0..15]\n>> + * @horiz_cood: the horizontal coordinate of points on the diagonal \n>> [0..2888]\n>> + * @vert_cood: the vertical coordinate of points on the diagonal \n>> [0..2240]\n>> + */\n>> +struct c3_isp_params_awb_config {\n>> +     struct c3_isp_params_block_header header;\n>> +     __u8 tap_point;\n>> +     __u8 satur_vald;\n>> +     __u8 horiz_zones_num;\n>> +     __u8 vert_zones_num;\n>> +     __u16 rg_min;\n>> +     __u16 rg_max;\n>> +     __u16 bg_min;\n>> +     __u16 bg_max;\n>> +     __u16 rg_low;\n>> +     __u16 rg_high;\n>> +     __u16 bg_low;\n>> +     __u16 bg_high;\n>> +     __u8 zone_weight[C3_ISP_AWB_MAX_ZONES];\n>> +     __u16 horiz_cood[C3_ISP_AWB_MAX_PT_NUM];\n>> +     __u16 vert_cood[C3_ISP_AWB_MAX_PT_NUM];\n>> +} __attribute__((aligned(8)));\n>> +\n>> +/**\n>> + * enum c3_isp_params_ae_tap_points - Tap points for the AE statistics\n>> + * @C3_ISP_AE_STATS_TAP_GE: immediately after the green equal block\n>> + * @C3_ISP_AE_STATS_TAP_MLS: immediately after the mesh lens shading \n>> block\n>> + */\n>> +enum c3_isp_params_ae_tap_point {\n>> +     C3_ISP_AE_STATS_TAP_GE = 0,\n>> +     C3_ISP_AE_STATS_TAP_MLS,\n>> +};\n>> +\n>> +/**\n>> + * struct c3_isp_params_ae_config - Stats settings for auto-exposure\n>> + *\n>> + * This struct allows the configuration of the statistics generated for\n>> + * auto exposure.\n>> + *\n>> + * header.type should be set to C3_ISP_PARAMS_BLOCK_AE_CONFIG\n>> + * from :c:type:`c3_isp_params_block_type`\n>> + *\n>> + * @header: the C3 ISP parameters block header\n>> + * @horiz_zones_num: active number of horizontal zones [0..17]\n>> + * @vert_zones_num: active number of vertical zones [0..15]\n>> + * @tap_point: the tap point from enum c3_isp_params_ae_tap_point\n>> + * @zone_weight: array of weights for AE statistics zones [0..15]\n>> + * @horiz_cood: the horizontal coordinate of points on the diagonal \n>> [0..2888]\n>> + * @vert_cood: the vertical coordinate of points on the diagonal \n>> [0..2240]\n>> + * @reserved: applications must zero this array\n>> + */\n>> +struct c3_isp_params_ae_config {\n>> +     struct c3_isp_params_block_header header;\n>> +     __u8 tap_point;\n>> +     __u8 horiz_zones_num;\n>> +     __u8 vert_zones_num;\n>> +     __u8 zone_weight[C3_ISP_AE_MAX_ZONES];\n>> +     __u16 horiz_cood[C3_ISP_AE_MAX_PT_NUM];\n>> +     __u16 vert_cood[C3_ISP_AE_MAX_PT_NUM];\n>> +     __u16 reserved[3];\n>> +} __attribute__((aligned(8)));\n>> +\n>> +/**\n>> + * enum c3_isp_params_af_tap_points - Tap points for the AF statistics\n>> + * @C3_ISP_AF_STATS_TAP_SNR: immediately after the spatial noise \n>> reduce block\n>> + * @C3_ISP_AF_STATS_TAP_DMS: immediately after the demosaic block\n>> + */\n>> +enum c3_isp_params_af_tap_point {\n>> +     C3_ISP_AF_STATS_TAP_SNR = 0,\n>> +     C3_ISP_AF_STATS_TAP_DMS,\n>> +};\n>> +\n>> +/**\n>> + * struct c3_isp_params_af_config - Stats settings for auto-focus\n>> + *\n>> + * This struct allows the configuration of the statistics generated for\n>> + * auto focus.\n>> + *\n>> + * header.type should be set to C3_ISP_PARAMS_BLOCK_AF_CONFIG\n>> + * from :c:type:`c3_isp_params_block_type`\n>> + *\n>> + * @header: the C3 ISP parameters block header\n>> + * @tap_point: the tap point from enum c3_isp_params_af_tap_point\n>> + * @horiz_zones_num: active number of hotizontal zones [0..17]\n>> + * @vert_zones_num: active number of vertical zones [0..15]\n>> + * @reserved: applications must zero this array\n>> + * @horiz_cood: the horizontal coordinate of points on the diagonal \n>> [0..2888]\n>> + * @vert_cood: the vertical coordinate of points on the diagonal \n>> [0..2240]\n>> + */\n>> +struct c3_isp_params_af_config {\n>> +     struct c3_isp_params_block_header header;\n>> +     __u8 tap_point;\n>> +     __u8 horiz_zones_num;\n>> +     __u8 vert_zones_num;\n>> +     __u8 reserved[5];\n>> +     __u16 horiz_cood[C3_ISP_AF_MAX_PT_NUM];\n>> +     __u16 vert_cood[C3_ISP_AF_MAX_PT_NUM];\n>> +} __attribute__((aligned(8)));\n>> +\n>> +/**\n>> + * struct c3_isp_params_pst_gamma - Post gamma configuration\n>> + *\n>> + * This struct allows the configuration of the look up table for\n>> + * post gamma. The gamma curve consists of 129 points, so need to\n>> + * set lut[129].\n>> + *\n>> + * header.type should be set to C3_ISP_PARAMS_BLOCK_PST_GAMMA\n>> + * from :c:type:`c3_isp_params_block_type`\n>> + *\n>> + * @header: the C3 ISP parameters block header\n>> + * @lut: lookup table for P-Stitch gamma [0..1023]\n>> + * @reserved: applications must zero this array\n>> + */\n>> +struct c3_isp_params_pst_gamma {\n>> +     struct c3_isp_params_block_header header;\n>> +     __u16 lut[129];\n>> +     __u16 reserved[3];\n>> +} __attribute__((aligned(8)));\n>> +\n>> +/**\n>> + * struct c3_isp_params_ccm - ISP CCM configuration\n>> + *\n>> + * This struct allows the configuration of the matrix for\n>> + * color correction. The matrix consists of 3 x 3 points,\n>> + * so need to set matrix[3][3].\n>> + *\n>> + * header.type should be set to C3_ISP_PARAMS_BLOCK_CCM\n>> + * from :c:type:`c3_isp_params_block_type`\n>> + *\n>> + * @header: the C3 ISP parameters block header\n>> + * @matrix: a 3 x 3 matrix used for color correction,\n>> + *          the value of matrix[x][y] is orig_value x 256. \n>> [-4096..4095]\n>> + * @reserved: applications must zero this array\n>> + */\n>> +struct c3_isp_params_ccm {\n>> +     struct c3_isp_params_block_header header;\n>> +     __s16 matrix[3][3];\n>> +     __u16 reserved[3];\n>> +} __attribute__((aligned(8)));\n>> +\n>> +/**\n>> + * struct c3_isp_params_csc - ISP Color Space Conversion configuration\n>> + *\n>> + * This struct allows the configuration of the matrix for color space\n>> + * conversion. The matrix consists of 3 x 3 points, so need to set \n>> 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. \n>> [-4096..4095]\n>> + * @reserved: applications must zero this array\n>> + */\n>> +struct c3_isp_params_csc {\n>> +     struct c3_isp_params_block_header header;\n>> +     __s16 matrix[3][3];\n>> +     __u16 reserved[3];\n>> +} __attribute__((aligned(8)));\n>> +\n>> +/**\n>> + * struct c3_isp_params_blc - ISP Black Level Correction configuration\n>> + *\n>> + * This struct allows the configuration of the block level offset \n>> for each\n>> + * color channel.\n>> + *\n>> + * header.type should be set to C3_ISP_PARAMS_BLOCK_BLC\n>> + * from :c:type:`c3_isp_params_block_type`\n>> + *\n>> + * @header: the C3 ISP parameters block header\n>> + * @gr_ofst: Gr blc offset (Q4.8 format)\n>> + * @r_ofst: R blc offset (Q4.8 format)\n>> + * @b_ofst: B blc offset (Q4.8 format)\n>> + * @gb_ofst: Gb blc offset(Q4.8 format)\n>> + */\n>> +struct c3_isp_params_blc {\n>> +     struct c3_isp_params_block_header header;\n>> +     __u16 gr_ofst;\n>> +     __u16 r_ofst;\n>> +     __u16 b_ofst;\n>> +     __u16 gb_ofst;\n>> +};\n>> +\n>> +/**\n>> + * define C3_ISP_PARAMS_MAX_SIZE - Maximum size of all C3 ISP \n>> Parameters\n>> + *\n>> + * Though the parameters for the C3 ISP are passed as optional \n>> blocks, the\n>> + * driver still needs to know the absolute maximum size so that it \n>> can allocate\n>> + * a buffer sized appropriately to accommodate userspace attempting \n>> to set all\n>> + * possible parameters in a single frame.\n>> + */\n>> +#define C3_ISP_PARAMS_MAX_SIZE                     \\\n>> +     (sizeof(struct c3_isp_params_awb_gains) +  \\\n>> +      sizeof(struct c3_isp_params_awb_config) + \\\n>> +      sizeof(struct c3_isp_params_ae_config) +  \\\n>> +      sizeof(struct c3_isp_params_af_config) +  \\\n>> +      sizeof(struct c3_isp_params_pst_gamma) +  \\\n>> +      sizeof(struct c3_isp_params_ccm) +        \\\n>> +      sizeof(struct c3_isp_params_csc) +        \\\n>> +      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 \n>> structure\n>> + * which contains a :c:type:`c3_isp_param_block_header` entry as first\n>> + * member. Userspace populates the @data buffer with configuration \n>> parameters\n>> + * for the blocks that it intends to configure. As a consequence, \n>> the data\n>> + * buffer effective size changes according to the number of ISP \n>> blocks that\n>> + * userspace intends to configure.\n>> + *\n>> + * The parameters buffer is versioned by the @version field to allow \n>> modifying\n>> + * and extending its definition. Userspace should populate the \n>> @version field to\n>> + * inform the driver about the version it intends to use. The driver \n>> will parse\n>> + * and handle the @data buffer according to the data layout specific \n>> to the\n>> + * indicated revision and return an error if the desired revision is \n>> not\n>> + * supported.\n>> + *\n>> + * For each ISP block that userspace wants to configure, a \n>> block-specific\n>> + * structure is appended to the @data buffer, one after the other \n>> without gaps\n>> + * in between nor overlaps. Userspace shall populate the @total_size \n>> 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>> + *   +-------------------- struct c3_isp_params_cfg ---- \n>> ------------------+\n>> + *   | version = \n>> C3_ISP_PARAM_BUFFER_V0;                                   |\n>> + *   | data_size = sizeof(struct c3_isp_params_awb_gains) \n>> +                |\n>> + *   |              sizeof(struct c3_isp_params_awb_config);       |\n>> + *   | +------------------------- data \n>> ---------------------------------+ |\n>> + *   | | +------------ struct c3_isp_params_awb_gains) \n>> ------------------+ |\n>> + *   | | | +---------  struct c3_isp_params_block_header header \n>> -----+ | | |\n>> + *   | | | | type = C3_ISP_PARAMS_BLOCK_AWB_GAINS;                   \n>> | | | |\n>> + *   | | | | flags = C3_ISP_PARAMS_BLOCK_FL_NONE;                    \n>> | | | |\n>> + *   | | | | size = sizeof(struct c3_isp_params_awb_gains);          \n>> | | | |\n>> + *   | | | \n>> +---------------------------------------------------------+ | | |\n>> + *   | | | gr_gain = \n>> ...;                                              | | |\n>> + *   | | | r_gain = \n>> ...;                                               | | |\n>> + *   | | | b_gain = \n>> ...;                                               | | |\n>> + *   | | | gb_gain = \n>> ...;                                              | | |\n>> + *   | | +------------------ struct c3_isp_params_awb_config \n>> ----------+ | |\n>> + *   | | | +---------- struct c3_isp_param_block_header header \n>> ------+ | | |\n>> + *   | | | | type = C3_ISP_PARAMS_BLOCK_AWB_CONFIG;                  \n>> | | | |\n>> + *   | | | | flags = C3_ISP_PARAMS_BLOCK_FL_NONE;                    \n>> | | | |\n>> + *   | | | | size = sizeof(struct c3_isp_params_awb_config)          \n>> | | | |\n>> + *   | | | \n>> +---------------------------------------------------------+ | | |\n>> + *   | | | tap_point = \n>> ...;                                            | | |\n>> + *   | | | satur_vald = \n>> ...;                                           | | |\n>> + *   | | | horiz_zones_num = \n>> ...;                                      | | |\n>> + *   | | | vert_zones_num = \n>> ...;                                       | | |\n>> + *   | | \n>> +-------------------------------------------------------------+ | |\n>> + *   | \n>> +-----------------------------------------------------------------+ |\n>> + * \n>> +---------------------------------------------------------------------+\n>> + *\n>> + * @version: The C3 ISP parameters buffer version\n>> + * @data_size: The C3 ISP configuration data effective size, \n>> excluding this\n>> + *             header\n>> + * @data: The C3 ISP configuration blocks data\n>> + */\n>> +struct c3_isp_params_cfg {\n>> +     __u32 version;\n>> +     __u32 data_size;\n>> +     __u8 data[C3_ISP_PARAMS_MAX_SIZE];\n>> +};\n>> +\n>> +#endif\n>> diff --git a/meson_options.txt b/meson_options.txt\n>> index d59f4c04..352f981b 100644\n>> --- a/meson_options.txt\n>> +++ b/meson_options.txt\n>> @@ -32,7 +32,7 @@ option('gstreamer',\n>>\n>>   option('ipas',\n>>           type : 'array',\n>> -        choices : ['ipu3', 'mali-c55', 'rkisp1', 'rpi/vc4', \n>> 'simple', 'vimc'],\n>> +        choices : ['c3-isp', 'ipu3', 'mali-c55', 'rkisp1', \n>> 'rpi/vc4', 'simple', 'vimc'],\n>>           description : 'Select which IPA modules to build')\n>>\n>>   option('lc-compliance',\n>> diff --git a/src/ipa/c3-isp/algorithms/agc.cpp \n>> b/src/ipa/c3-isp/algorithms/agc.cpp\n>> new file mode 100644\n>> index 00000000..16eb0b46\n>> --- /dev/null\n>> +++ b/src/ipa/c3-isp/algorithms/agc.cpp\n>> @@ -0,0 +1,260 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2024, Amlogic\n>> + *\n>> + * C3ISP AGC/AEC mean-based control algorithm\n>> + */\n>> +\n>> +#include \"agc.h\"\n>> +\n>> +#include <algorithm>\n>> +#include <chrono>\n>> +#include <cmath>\n>> +#include <tuple>\n>> +#include <vector>\n>> +\n>> +#include <libcamera/base/log.h>\n>> +#include <libcamera/base/utils.h>\n>> +\n>> +#include <libcamera/control_ids.h>\n>> +\n>> +#include <libcamera/ipa/core_ipa_interface.h>\n>> +\n>> +#include \"libcamera/internal/yaml_parser.h\"\n>> +\n>> +#include \"libipa/histogram.h\"\n>> +\n>> +/**\n>> + * \\file agc.h\n>> + */\n>> +\n>> +namespace libcamera {\n>> +\n>> +using namespace std::literals::chrono_literals;\n>> +\n>> +namespace ipa::c3isp::algorithms {\n>> +\n>> +/**\n>> + * \\class Agc\n>> + * \\brief A mean-based auto-exposure algorithm\n>> + */\n>> +\n>> +LOG_DEFINE_CATEGORY(C3ISPAgc)\n>> +\n>> +Agc::Agc()\n>> +     : 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 \n>> 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>> +           const YamlObject &tuningData)\n>> +{\n>> +     int ret;\n>> +\n>> +     ret = parseTuningData(tuningData);\n>> +     if (ret)\n>> +             return ret;\n>> +\n>> +     context.ctrlMap.merge(controls());\n>> +\n>> +     return 0;\n>> +}\n> No AeEnable control? So it can only do automatic exposure, not manual?\n\n\nWill add AeEnable control.\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>> +                [[maybe_unused]] const IPACameraSensorInfo &configInfo)\n>> +{\n>> +     const IPASessionConfiguration &configuration = \n>> context.configuration;\n>> +     IPAActiveState &activeState = context.activeState;\n>> +\n>> +     /* Configure the default gain and exposure */\n>> +     activeState.agc.gain = configuration.sensor.minAnalogueGain;\n>> +     activeState.agc.exposure = 10ms / \n>> configuration.sensor.lineDuration;\n> In IPAC3ISP::updateControls() you get the actual default exposure from \n> the sensor's V4L2 controls -\n> can we use that instead?\n\n\nWill check this issue.\n\n>> +\n>> +     activeState.agc.constraintMode = constraintModes().begin()->first;\n>> +     activeState.agc.exposureMode = \n>> exposureModeHelpers().begin()->first;\n>> +\n>> +     setLimits(configuration.sensor.minShutterSpeed,\n>> +               configuration.sensor.maxShutterSpeed,\n>> +               configuration.sensor.minAnalogueGain,\n>> +               configuration.sensor.maxAnalogueGain);\n>> +\n>> +     resetFrameCount();\n>> +\n>> +     return 0;\n>> +}\n>> +\n>> +/**\n>> + * \\copydoc libcamera::ipa::Algorithm::prepare\n>> + */\n>> +void Agc::prepare(IPAContext &context, const uint32_t frame,\n>> +               [[maybe_unused]] IPAFrameContext &frameContext,\n>> +               C3ISPParams *params)\n>> +{\n>> +     if (frame)\n>> +             return;\n>> +\n>> +     auto AeCfg = params->block<BlockType::AEConfig>();\n>> +     AeCfg.setEnabled(C3_ISP_PARAMS_BLOCK_FL_ENABLE);\n>> +\n>> +     AeCfg->tap_point = C3_ISP_AE_STATS_TAP_MLS;\n>> +     AeCfg->horiz_zones_num = horizonalZonesNum_;\n>> +     AeCfg->vert_zones_num = verticalZonesNum_;\n>> +\n>> +     for (unsigned int i = 0; i < AeCfg->horiz_zones_num * \n>> AeCfg->vert_zones_num; i++)\n>> +             AeCfg->zone_weight[i] = 1;\n> unsigned int numZones = horizonalZonesNum_ * verticalZonesNum_;\n> Span<uint8_t> weights{ AeCfg->zone_weight, numZones };\n>\n> std::fill(weights.begin(), weights.end(), 1);\n>\n>\n> Same elsewhere in the code that you fill the weights array\n>\n\nWill use this method.\n\n>> +\n>> +     Size sensorSize = context.configuration.sensor.size;\n>> +     uint8_t maxPointNum = std::max(AeCfg->horiz_zones_num, \n>> AeCfg->vert_zones_num) + 1;\n>> +\n>> +     for (unsigned int i = 0; i < maxPointNum; i++) {\n>> +             uint16_t hidx = i * sensorSize.width / \n>> AeCfg->horiz_zones_num;\n>> +\n>> +             /* Aligned with 2 */\n>> +             hidx = hidx / 2 * 2;\n>> +             AeCfg->horiz_cood[i] = std::min(hidx, \n>> (uint16_t)sensorSize.width);\n> Mild preference for \"coord\" over \"cood\" - I suppose that applies to \n> the config header too.\n\n\nOK, will use \"coord\".\n\n>> +\n>> +             uint16_t vidx = i * sensorSize.height / \n>> AeCfg->vert_zones_num;\n>> +\n>> +             /* Aligned with 2 */\n>> +             vidx = vidx / 2 * 2;\n>> +             AeCfg->vert_cood[i] = std::min(vidx, \n>> (uint16_t)sensorSize.height);\n> Same here\n\n\nWill use \"coord\".\n\n>> +     }\n>> +}\n>> +\n>> +Histogram Agc::parseStatistics(const c3_isp_stats_info *stats)\n>> +{\n>> +     const struct c3_isp_ae_stats *info = &stats->ae;\n>> +     uint16_t zonesNum = horizonalZonesNum_ * verticalZonesNum_;\n>> +     std::vector<uint8_t> means(zonesNum, 0);\n>> +\n>> +     /*\n>> +      * Each zone has a 5-bin histogram and the\n>> +      * total sum is normalized to 65535.\n>> +      * For the convenience of calculation,\n>> +      * it can be assumed that:\n>> +      * hist0 represents the number of brightness 0,\n> Not 32? I would normally expect the centre of the bin to be targeted\n\n\nWill reconsider this issue.\n\n>> +      * hist1 represents the number of brightness 64,\n>> +      * hist2 represents the number of brightness 128,\n>> +      * hist3 represents the number of brightness 192,\n>> +      * hist4 represents the number of brightness 255.\n>> +      *\n>> +      * Finally, the average brightness of a zone can be calculated.\n>> +      */\n>> +     for (unsigned int i = 0; i < zonesNum; i++) {\n>> +             uint16_t hist2 = 65535 - info->stats[i].hist0 - \n>> info->stats[i].hist1 - info->stats[i].hist3 - info->stats[i].hist4;\n>> +\n>> +             uint32_t lumaSum = info->stats[i].hist0 * 0 + \n>> info->stats[i].hist1 * 64 + hist2 * 128 + info->stats[i].hist3 * 192 \n>> + info->stats[i].hist4 * 255;\n>> +\n>> +             means[i] = lumaSum / 65535;\n>\n> This could do with a touch of tidying. How about something like...\n>\n>\n>     for (unsigned int i = 0; i < zonesNum; i++) {\n>         uint16_t hist2 = 65535 - info->stats[i].hist0\n>                    - info->stats[i].hist1\n>                    - info->stats[i].hist3\n>                    - info->stats[i].hist4;\n>\n>         uint32_t lumaSum = info->stats[i].hist0 * 0\n>                  + info->stats[i].hist1 * 64\n>                  + hist2 * 128\n>                  + info->stats[i].hist3 * 192\n>                  + info->stats[i].hist4 * 255;\n>\n>         means[i] = lumaSum / 65535;\n>     }\n\n\nWill tidy.\n\n>> +     }\n>> +\n>> +     lumaMeans_ = means;\n>> +\n>> +     /* This is a 1024-bin histogram */\n>> +     uint32_t *hist = const_cast<uint32_t *>(info->hist);\n>> +\n>> +     return Histogram(Span<uint32_t>(hist, std::size(info->hist)));\n>> +}\n>> +\n>> +/**\n>> + * \\brief Estimate the relative luminance of the frame with a given \n>> 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 \n>> 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>> +     double sum = 0.0;\n>> +\n>> +     for (unsigned int i = 0; i < lumaMeans_.size(); i++)\n>> +             sum += std::min(lumaMeans_[i] * gain, 255.0);\n>> +\n>> +     return 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 \n>> algorithm\n>> + *\n>> + * Identify the current image brightness, and use that to estimate \n>> the optimal\n>> + * new exposure and gain for the scene.\n>> + */\n>> +void Agc::process(IPAContext &context, [[maybe_unused]] const \n>> uint32_t frame,\n>> +               IPAFrameContext &frameContext,\n>> +               const c3_isp_stats_info *stats,\n>> +               ControlList &metadata)\n>> +{\n>> +     Histogram hist = parseStatistics(stats);\n>> +\n>> +     utils::Duration shutterTime;\n>> +     double aGain, dGain;\n>> +\n>> +     /*\n>> +      * The Agc algorithm needs to know the effective exposure value \n>> that was\n>> +      * applied to the sensor when the statistics were collected.\n>> +      */\n>> +     utils::Duration exposureTime = \n>> context.configuration.sensor.lineDuration * \n>> frameContext.sensor.exposure;\n>> +     double analogueGain = frameContext.sensor.gain;\n>> +     utils::Duration effectiveExposureValue = exposureTime * \n>> analogueGain;\n>> +\n>> +     std::tie(shutterTime, aGain, dGain) =\n>> + calculateNewEv(context.activeState.agc.constraintMode,\n>> + context.activeState.agc.exposureMode, hist,\n>> +                            effectiveExposureValue);\n>> +\n>> +     LOG(C3ISPAgc, Debug)\n>> +             << \"Shutter time, analogue gain and digital gain are \"\n>> +             << shutterTime << \", \" << aGain << \" and \" << dGain;\n>> +\n>> +     IPAActiveState &activeState = context.activeState;\n>> +\n>> +     activeState.agc.exposure = shutterTime / \n>> context.configuration.sensor.lineDuration;\n>> +     activeState.agc.gain = aGain;\n>> +\n>> +     metadata.set(controls::AnalogueGain, frameContext.sensor.gain);\n>> +     metadata.set(controls::ExposureTime, \n>> exposureTime.get<std::micro>());\n>> +\n>> +     uint32_t vTotal = context.configuration.sensor.size.height + \n>> context.configuration.sensor.defVBlank;\n>> +     utils::Duration frameDuration = \n>> context.configuration.sensor.lineDuration * vTotal;\n>> +     metadata.set(controls::FrameDuration, \n>> frameDuration.get<std::micro>());\n>> +\n>> +     lumaMeans_ = {};\n>> +}\n>> +\n>> +REGISTER_IPA_ALGORITHM(Agc, \"Agc\")\n>> +\n>> +} /* namespace ipa::c3isp::algorithms */\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/ipa/c3-isp/algorithms/agc.h \n>> b/src/ipa/c3-isp/algorithms/agc.h\n>> new file mode 100644\n>> index 00000000..b0d1b500\n>> --- /dev/null\n>> +++ b/src/ipa/c3-isp/algorithms/agc.h\n>> @@ -0,0 +1,50 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2024, Amlogic\n>> + *\n>> + * C3ISP AGC/AEC mean-based control algorithm\n>> + */\n>> +\n>> +#pragma once\n>> +\n>> +#include <linux/c3-isp-config.h>\n>> +\n>> +#include <libcamera/base/span.h>\n>> +#include <libcamera/base/utils.h>\n>> +\n>> +#include <libcamera/geometry.h>\n>> +\n>> +#include \"libipa/agc_mean_luminance.h\"\n>> +#include \"algorithm.h\"\n>> +\n>> +namespace libcamera {\n>> +\n>> +namespace ipa::c3isp::algorithms {\n>> +\n>> +class Agc : public Algorithm, public AgcMeanLuminance\n>> +{\n>> +public:\n>> +     Agc();\n>> +     ~Agc() = default;\n>> +\n>> +     int init(IPAContext &context, const YamlObject &tuningData) \n>> override;\n>> +     int configure(IPAContext &context, const IPACameraSensorInfo \n>> &configInfo) override;\n>> +     void prepare(IPAContext &context, const uint32_t frame,\n>> +                  IPAFrameContext &frameContext,\n>> +                  C3ISPParams *params) override;\n>> +     void process(IPAContext &context, const uint32_t frame,\n>> +                  IPAFrameContext &frameContext,\n>> +                  const c3_isp_stats_info *stats,\n>> +                  ControlList &metadata) override;\n>> +private:\n>> +     Histogram parseStatistics(const c3_isp_stats_info *stats);\n>> +     double estimateLuminance(double gain) const override;\n>> +\n>> +     std::vector<uint8_t> lumaMeans_;\n>> +     uint8_t horizonalZonesNum_;\n>> +     uint8_t verticalZonesNum_;\n>> +};\n>> +\n>> +} /* namespace ipa::c3isp::algorithms */\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/ipa/c3-isp/algorithms/algorithm.h \n>> b/src/ipa/c3-isp/algorithms/algorithm.h\n>> new file mode 100644\n>> index 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>> +     Algorithm()\n>> +             : disabled_(false)\n>> +     {\n>> +     }\n>> +\n>> +     bool disabled_;\n>> +};\n>> +\n>> +} /* namespace ipa::c3isp */\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/ipa/c3-isp/algorithms/awb.cpp \n>> b/src/ipa/c3-isp/algorithms/awb.cpp\n>> new file mode 100755\n>> index 00000000..f8ae3c1f\n>> --- /dev/null\n>> +++ b/src/ipa/c3-isp/algorithms/awb.cpp\n>> @@ -0,0 +1,257 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2024, Amlogic\n>> + *\n>> + * C3ISP AWB control algorithm\n>> + */\n>> +\n>> +#include \"awb.h\"\n>> +\n>> +#include <algorithm>\n>> +#include <ios>\n> Is this used?\n\n\nWill remove this.\n\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>> +     : horizonalZonesNum_(32), verticalZonesNum_(24)\n>> +{\n>> +}\n>> +\n>> +/**\n>> + * \\copydoc libcamera::ipa::Algorithm::configure\n>> + */\n>> +int Awb::configure(IPAContext &context,\n>> +                [[maybe_unused]] const IPACameraSensorInfo &configInfo)\n>> +{\n>> +     IPAActiveState &activeState = context.activeState;\n>> +\n>> +     activeState.awb.gains.manual.red = 1.0;\n>> +     activeState.awb.gains.manual.blue = 1.0;\n>> +     activeState.awb.gains.manual.green = 1.0;\n> I think you can drop the explicit green, it's always 1.0 and that's \n> well understood.\n\n\nOK, will drop the explicit green.\n\n>> +\n>> +     activeState.awb.gains.automatic.red = 1.0;\n>> +     activeState.awb.gains.automatic.blue = 1.0;\n>> +     activeState.awb.gains.automatic.green = 1.0;\n>> +     activeState.awb.autoEnabled = true;\n>> +\n>> +     return 0;\n>> +}\n>> +\n>> +/**\n>> + * \\copydoc libcamera::ipa::Algorithm::queueRequest\n>> + */\n>> +void Awb::queueRequest(IPAContext &context,\n>> +                    [[maybe_unused]] const uint32_t frame,\n>> +                    IPAFrameContext &frameContext,\n>> +                    const ControlList &controls)\n>> +{\n>> +     auto &awb = context.activeState.awb;\n>> +\n>> +     const auto &awbEnable = controls.get(controls::AwbEnable);\n>> +     if (awbEnable && *awbEnable != awb.autoEnabled) {\n>> +             awb.autoEnabled = *awbEnable;\n>> +\n>> +             LOG(C3ISPAwb, Debug)\n>> +                     << (*awbEnable ? \"Enabling\" : \"Disabling\") << \" \n>> AWB\";\n>> +     }\n>> +\n>> +     const auto &colourGains = controls.get(controls::ColourGains);\n>> +     if (colourGains && !awb.autoEnabled) {\n>> +             awb.gains.manual.red = (*colourGains)[0];\n>> +             awb.gains.manual.blue = (*colourGains)[1];\n>> +\n>> +             LOG(C3ISPAwb, Debug)\n>> +                     << \"Set colour gains to red: \" << \n>> awb.gains.manual.red\n>> +                     << \", blue: \" << awb.gains.manual.blue;\n>> +     }\n>> +\n>> +     frameContext.awb.autoEnabled = awb.autoEnabled;\n>> +\n>> +     if (!awb.autoEnabled) {\n>> +             frameContext.awb.gains.red = awb.gains.manual.red;\n>> +             frameContext.awb.gains.green = 1.0;\n>> +             frameContext.awb.gains.blue = awb.gains.manual.blue;\n>> +     }\n>> +}\n>> +\n>> +/**\n>> + * \\copydoc libcamera::ipa::Algorithm::prepare\n>> + */\n>> +void Awb::prepare(IPAContext &context, const uint32_t frame,\n>> +               IPAFrameContext &frameContext, C3ISPParams *params)\n>> +{\n>> +     /*\n>> +      * This is the latest time we can read the active state. This \n>> is the\n>> +      * most up-to-date automatic values we can read.\n>> +      */\n>> +     if (frameContext.awb.autoEnabled) {\n>> +             frameContext.awb.gains.red = \n>> context.activeState.awb.gains.automatic.red;\n>> +             frameContext.awb.gains.green = \n>> context.activeState.awb.gains.automatic.green;\n>> +             frameContext.awb.gains.blue = \n>> context.activeState.awb.gains.automatic.blue;\n>> +     }\n>> +\n>> +     auto AWBGains = params->block<BlockType::AWBGains>();\n>> +     AWBGains.setEnabled(C3_ISP_PARAMS_BLOCK_FL_ENABLE);\n>> +\n>> +     AWBGains->gr_gain = std::clamp<int>(256 * \n>> frameContext.awb.gains.green, 0, 0xfff);\n>> +     AWBGains->r_gain = std::clamp<int>(256 * \n>> frameContext.awb.gains.red, 0, 0xfff);\n>> +     AWBGains->b_gain = std::clamp<int>(256 * \n>> frameContext.awb.gains.blue, 0, 0xfff);\n>> +     AWBGains->gb_gain = std::clamp<int>(256 * \n>> frameContext.awb.gains.green, 0, 0xfff);\n> You can just specify 1.0 instead of using \n> frameContext.awb.gains.green. We also have helpers to put\n> things into Q4.8 format - it's the floatingToFixedPoint() function in \n> src/ipa/libipa/fixedpoint.cpp\n\n\nOK, will use 1.0 and floatingToFixedPoint().\n\n>> +\n>> +     if (frame)\n>> +             return;\n>> +\n>> +     auto AWBCfg = params->block<BlockType::AWBConfig>();\n>> +     AWBCfg.setEnabled(C3_ISP_PARAMS_BLOCK_FL_ENABLE);\n>> +\n>> +     AWBCfg->tap_point = C3_ISP_AWB_STATS_TAP_BEFORE_WB;\n>> +     AWBCfg->satur_vald = 1;\n>> +     AWBCfg->horiz_zones_num = horizonalZonesNum_;\n>> +     AWBCfg->vert_zones_num = verticalZonesNum_;\n>> +     AWBCfg->rg_min = 75;\n>> +     AWBCfg->rg_max = 256;\n>> +     AWBCfg->bg_min = 44;\n>> +     AWBCfg->bg_max = 222;\n>> +     AWBCfg->rg_low = 93;\n>> +     AWBCfg->rg_high = 244;\n>> +     AWBCfg->bg_low = 61;\n>> +     AWBCfg->bg_high = 205;\n>> +\n>> +     for (unsigned int i = 0; i < AWBCfg->horiz_zones_num * \n>> AWBCfg->vert_zones_num; i++)\n>> +             AWBCfg->zone_weight[i] = 1;\n> unsigned int numZones = horizonalZonesNum_ * verticalZonesNum_;\n> Span<uint8_t> weights{ AWBCfg->zone_weight, numZones };\n> std::fill(weights.begin(), weights.end(), 1);\n\n\nWill use this method.\n\n>> +\n>> +     Size sensorSize = context.configuration.sensor.size;\n>> +     uint8_t maxPointNum = std::max(AWBCfg->horiz_zones_num, \n>> AWBCfg->vert_zones_num) + 1;\n>> +\n>> +     for (unsigned int i = 0; i < maxPointNum; i++) {\n>> +             uint16_t hidx = i * sensorSize.width / \n>> AWBCfg->horiz_zones_num;\n>> +\n>> +             /* Aligned with 2 */\n>> +             hidx = hidx / 2 * 2;\n>> +             AWBCfg->horiz_cood[i] = std::min(hidx, \n>> (uint16_t)sensorSize.width);\n>> +\n>> +             uint16_t vidx = i * sensorSize.height / \n>> AWBCfg->vert_zones_num;\n>> +\n>> +             /* Aligned with 2 */\n>> +             vidx = vidx / 2 * 2;\n>> +             AWBCfg->vert_cood[i] = std::min(vidx, \n>> (uint16_t)sensorSize.height);\n>> +     }\n>> +}\n>> +\n>> +uint32_t Awb::estimateCCT(double red, double green, double blue)\n>> +{\n>> +     /* Convert the RGB values to CIE tristimulus values (XYZ) */\n>> +     double X = (-0.14282) * (red) + (1.54924) * (green) + \n>> (-0.95641) * (blue);\n>> +     double Y = (-0.32466) * (red) + (1.57837) * (green) + \n>> (-0.73191) * (blue);\n>> +     double Z = (-0.68202) * (red) + (0.77073) * (green) + (0.56332) \n>> * (blue);\n>> +\n>> +     /* Calculate the normalized chromaticity values */\n>> +     double x = X / (X + Y + Z);\n>> +     double y = Y / (X + Y + Z);\n>> +\n>> +     /* Calculate CCT */\n>> +     double n = (x - 0.3320) / (0.1858 - y);\n>> +     return 449 * n * n * n + 3525 * n * n + 6823.3 * n + 5520.33;\n>> +}\n> We have a helper for this in src/ipa/libipa/colours.cpp\n\n\nWill check this issue.\n\n>> +\n>> +/**\n>> + * \\copydoc libcamera::ipa::Algorithm::process\n>> + */\n>> +void Awb::process([[maybe_unused]] IPAContext &context,\n> context is used, so you can drop the qualifier\n\n\nWill drop [[maybe_unused]]\n\n>> +               [[maybe_unused]] const uint32_t frame,\n>> +               IPAFrameContext &frameContext,\n>> +               const c3_isp_stats_info *stats,\n>> +               ControlList &metadata)\n>> +{\n>> +     IPAActiveState &activeState = context.activeState;\n>> +     const struct c3_isp_awb_stats *awb = &stats->awb;\n>> +     uint16_t zoneCnt = horizonalZonesNum_ * verticalZonesNum_;\n>> +     uint32_t rgSum = 0;\n>> +     uint32_t bgSum = 0;\n>> +     double rgMean;\n>> +     double bgMean;\n>> +     double greenMean;\n>> +     double blueMean;\n>> +     double redMean;\n> We'd typically declare variables close to their use, so rather than \n> double rgMean here...\n\n\nWill use float.\n\n>> +\n>> +     for (unsigned int i = 0; i < zoneCnt; i++) {\n>> +             rgSum += awb->stats[i].rg;\n>> +             bgSum += awb->stats[i].bg;\n>> +     }\n>> +\n>> +     rgMean = rgSum / zoneCnt / 4096.0;\n> ...It would be \"double rgMean = rgSum / zoneCnt / 4096.0;\" here\n\n\nOK\n\n>> +     bgMean = bgSum / zoneCnt / 4096.0;\n>> +\n>> +     /*\n>> +      * To simplify the calculation,\n>> +      * the green mean is hardcoded to 1.0\n>> +      */\n>> +\n>> +     greenMean = 1.0;\n> Similarly here I think you can drop the green variables; the \n> hard-coded 1.0 is well understood\n\n\nOK. will drop the green variables.\n\n>> +     redMean = rgMean * greenMean;\n>> +     blueMean = bgMean * greenMean;\n> And this \"* greenMean\" is unecessary since that value is only ever \n> 1.0, which means you only need\n> either rg/bgMean or red/blueMean, not both\n\n\nWill drop the \"greenMean\"\n\n>> +\n>> +     activeState.awb.temperatureK = estimateCCT(redMean, greenMean, \n>> blueMean);\n>> +\n>> +     /* Metadata shall contain the up to date measurement */\n>> +     metadata.set(controls::ColourTemperature, \n>> activeState.awb.temperatureK);\n>> +\n>> +     /*\n>> +      * Estimate the red and blue gains to apply in a grey world.\n>> +      * The green gain is hardcoded to 1.0. Avoid division by zero\n>> +      * by clamping the divisor to mininum value of 0.0625.\n>> +      */\n>> +     double redGain = greenMean / std::max(redMean, 0.0625);\n>> +     double blueGain = greenMean / std::max(blueMean, 0.0625);\n>> +\n>> +     /*\n>> +      * Clamp the gain values to the hardware, which express gains \n>> as Q4.8\n>> +      * unsigned integer values. Set the minimum just above zero to \n>> avoid\n>> +      * divisions by zero.\n>> +      */\n>> +     redGain = std::clamp(redGain, 1.0 / 256, 4095.0 / 256);\n>> +     blueGain = std::clamp(blueGain, 1.0 / 256, 4095.0 / 256);\n>\n> There's a helper to convert from floating to Q4.8 in libipa - use \n> those please\n>\n\nWill use the helper to convert.\n\n>> +\n>> +     /* Filter the values to avoid oscillations. */\n>> +     double speed = 0.2;\n>> +     redGain = speed * redGain + (1 - speed) * \n>> activeState.awb.gains.automatic.red;\n>> +     blueGain = speed * blueGain + (1 - speed) * \n>> activeState.awb.gains.automatic.blue;\n>> +\n>> +     activeState.awb.gains.automatic.red = redGain;\n>> +     activeState.awb.gains.automatic.blue = blueGain;\n>> +     activeState.awb.gains.automatic.green = 1.0;\n>> +\n>> +     metadata.set(controls::AwbEnable, frameContext.awb.autoEnabled);\n>> +     metadata.set(controls::ColourGains, { \n>> static_cast<float>(frameContext.awb.gains.red),\n>> + static_cast<float>(frameContext.awb.gains.blue) });\n>> +\n>> +     LOG(C3ISPAwb, Debug) << \"Gains: R \" << \n>> activeState.awb.gains.automatic.red\n>> +                          << \", G \" << \n>> activeState.awb.gains.automatic.green\n>> +                          << \", B \" << \n>> activeState.awb.gains.automatic.blue\n>> +                          << \", Ct \" << activeState.awb.temperatureK \n>> << \"K\";\n>> +}\n>> +\n>> +REGISTER_IPA_ALGORITHM(Awb, \"Awb\")\n>> +\n>> +} /* namespace ipa::c3isp::algorithms */\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/ipa/c3-isp/algorithms/awb.h \n>> b/src/ipa/c3-isp/algorithms/awb.h\n>> new file mode 100755\n>> index 00000000..63ed84b2\n>> --- /dev/null\n>> +++ b/src/ipa/c3-isp/algorithms/awb.h\n>> @@ -0,0 +1,42 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2024, Amlogic\n>> + *\n>> + * C3ISP AWB control algorithm\n>> + */\n>> +\n>> +#pragma once\n>> +\n>> +#include \"algorithm.h\"\n>> +\n>> +namespace libcamera {\n>> +\n>> +namespace ipa::c3isp::algorithms {\n>> +\n>> +class Awb : public Algorithm\n>> +{\n>> +public:\n>> +     Awb();\n>> +     ~Awb() = default;\n>> +\n>> +     int configure(IPAContext &context, const IPACameraSensorInfo \n>> &configInfo) override;\n>> +     void queueRequest(IPAContext &context, const uint32_t frame,\n>> +                       IPAFrameContext &frameContext,\n>> +                       const ControlList &controls) override;\n>> +     void prepare(IPAContext &context, const uint32_t frame,\n>> +                  IPAFrameContext &frameContext,\n>> +                  C3ISPParams *params) override;\n>> +     void process(IPAContext &context, const uint32_t frame,\n>> +                  IPAFrameContext &frameContext,\n>> +                  const c3_isp_stats_info *stats,\n>> +                  ControlList &metadata) override;\n>> +\n>> +private:\n>> +     uint32_t estimateCCT(double red, double green, double blue);\n>> +     uint8_t horizonalZonesNum_;\n>> +     uint8_t verticalZonesNum_;\n>> +};\n>> +\n>> +} /* namespace ipa::c3isp::algorithms */\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/ipa/c3-isp/algorithms/blc.cpp \n>> b/src/ipa/c3-isp/algorithms/blc.cpp\n>> new file mode 100644\n>> index 00000000..781c8c2f\n>> --- /dev/null\n>> +++ b/src/ipa/c3-isp/algorithms/blc.cpp\n>> @@ -0,0 +1,102 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2024, Amlogic\n>> + *\n>> + * C3ISP Black Level Correction control\n>> + */\n>> +\n>> +#include \"blc.h\"\n>> +\n>> +#include <libcamera/base/log.h>\n>> +#include <libcamera/control_ids.h>\n>> +\n>> +#include \"libcamera/internal/yaml_parser.h\"\n>> +\n>> +/**\n>> + * \\file blc.h\n>> + */\n>> +\n>> +namespace libcamera {\n>> +\n>> +namespace ipa::c3isp::algorithms {\n>> +\n>> +/**\n>> + * \\class Blc\n>> + * \\brief C3 ISP Black Level Correction control\n>> + *\n>> + * The pixels output by the camera normally include a black level, \n>> because\n>> + * sensors do not always report a signal level of '0' for black. \n>> Pixels at or\n>> + * below this level should be considered black. To achieve that, the \n>> 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 \n>> region of the\n>> + * camera sensor, or measured during the camera tuning process. The \n>> 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 \n>> &tuningData)\n>> +{\n>> +     std::optional<int16_t> levelRed = \n>> tuningData[\"BlcR\"].get<int16_t>();\n>> +     std::optional<int16_t> levelGreenR = \n>> tuningData[\"BlcGr\"].get<int16_t>();\n>> +     std::optional<int16_t> levelGreenB = \n>> tuningData[\"BlcGb\"].get<int16_t>();\n>> +     std::optional<int16_t> levelBlue = \n>> tuningData[\"BlcB\"].get<int16_t>();\n>> +\n>> +     blackLevelRed_ = levelRed.value_or(4096);\n>> +     blackLevelGreenR_ = levelGreenR.value_or(4096);\n>> +     blackLevelGreenB_ = levelGreenB.value_or(4096);\n>> +     blackLevelBlue_ = levelBlue.value_or(4096);\n>> +\n>> +     LOG(C3ISPBlc, Debug)\n>> +             << \"Black Levels: red \" << blackLevelRed_\n>> +             << \", green (red) \" << blackLevelGreenR_\n>> +             << \", green (blue) \" << blackLevelGreenB_\n>> +             << \", blue \" << blackLevelBlue_;\n>> +\n>> +     return 0;\n>> +}\n>> +\n>> +/**\n>> + * \\copydoc libcamera::ipa::Algorithm::prepare\n>> + */\n>> +void Blc::prepare([[maybe_unused]] IPAContext &context, const \n>> uint32_t frame,\n>> +               [[maybe_unused]] IPAFrameContext &frameContext,\n>> +               C3ISPParams *params)\n>> +{\n>> +     if (frame)\n>> +             return;\n>> +\n>> +     auto blcCfg = params->block<BlockType::Blc>();\n>> +     blcCfg.setEnabled(C3_ISP_PARAMS_BLOCK_FL_ENABLE);\n>> +\n>> +     blcCfg->gr_ofst = blackLevelGreenR_;\n>> +     blcCfg->r_ofst = blackLevelRed_;\n>> +     blcCfg->b_ofst = blackLevelBlue_;\n>> +     blcCfg->gb_ofst = blackLevelGreenB_;\n>> +}\n>> +\n>> +/**\n>> + * \\copydoc libcamera::ipa::Algorithm::process\n>> + */\n>> +void Blc::process([[maybe_unused]] IPAContext &context,\n>> +               [[maybe_unused]] const uint32_t frame,\n>> +               [[maybe_unused]] IPAFrameContext &frameContext,\n>> +               [[maybe_unused]] const c3_isp_stats_info *stats,\n>> +               ControlList &metadata)\n>> +{\n>> +     metadata.set(controls::SensorBlackLevels,\n>> +                  { static_cast<int32_t>(blackLevelRed_),\n>> + static_cast<int32_t>(blackLevelGreenR_),\n>> + static_cast<int32_t>(blackLevelGreenB_),\n>> +                    static_cast<int32_t>(blackLevelBlue_) });\n>> +}\n>> +\n>> +REGISTER_IPA_ALGORITHM(Blc, \"Blc\")\n>\n>\n> This is all fine, though one addition you might consider is to check \n> if the CameraSensorHelper\n> reports any black level values for the sensor, and use those if \n> there's none in the tuning data.\n> Check the blc.cpp in mali-c55 IPA for an example.\n>\n\nWill refer to the blc.cpp in mali-c55 IPA\n\n>> +\n>> +} /* namespace ipa::c3isp::algorithms */\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/ipa/c3-isp/algorithms/blc.h \n>> b/src/ipa/c3-isp/algorithms/blc.h\n>> new file mode 100644\n>> index 00000000..702163f7\n>> --- /dev/null\n>> +++ b/src/ipa/c3-isp/algorithms/blc.h\n>> @@ -0,0 +1,40 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2024, Amlogic\n>> + *\n>> + * C3ISP Black Level Correction control\n>> + */\n>> +\n>> +#pragma once\n>> +\n>> +#include \"algorithm.h\"\n>> +\n>> +namespace libcamera {\n>> +\n>> +namespace ipa::c3isp::algorithms {\n>> +\n>> +class Blc : public Algorithm\n>> +{\n>> +public:\n>> +     Blc() {}\n>> +     ~Blc() = default;\n>> +\n>> +     int init(IPAContext &context, const YamlObject &tuningData) \n>> override;\n>> +     void prepare(IPAContext &context, const uint32_t frame,\n>> +                  IPAFrameContext &frameContext,\n>> +                  C3ISPParams *params) override;\n>> +     void process(IPAContext &context, const uint32_t frame,\n>> +                  IPAFrameContext &frameContext,\n>> +                  const c3_isp_stats_info *stats,\n>> +                  ControlList &metadata) override;\n>> +\n>> +private:\n>> +     int16_t blackLevelRed_;\n>> +     int16_t blackLevelGreenR_;\n>> +     int16_t blackLevelGreenB_;\n>> +     int16_t blackLevelBlue_;\n>> +};\n>> +\n>> +} /* namespace ipa::c3isp::algorithms */\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/ipa/c3-isp/algorithms/ccm.cpp \n>> b/src/ipa/c3-isp/algorithms/ccm.cpp\n>> new file mode 100644\n>> index 00000000..43f146d6\n>> --- /dev/null\n>> +++ b/src/ipa/c3-isp/algorithms/ccm.cpp\n>> @@ -0,0 +1,86 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2024, Amlogic\n>> + *\n>> + * C3ISP Color Correction Matrix control\n>> + */\n>> +\n>> +#include \"ccm.h\"\n>> +\n>> +#include <libcamera/base/log.h>\n>> +#include <libcamera/base/utils.h>\n>> +\n>> +#include <libcamera/control_ids.h>\n>> +#include <libcamera/ipa/core_ipa_interface.h>\n>> +\n>> +#include \"libcamera/internal/yaml_parser.h\"\n>> +#include \"libipa/interpolator.h\"\n>> +\n>> +/**\n>> + * \\file ccm.h\n>> + */\n>> +\n>> +namespace libcamera {\n>> +\n>> +namespace ipa::c3isp::algorithms {\n>> +\n>> +/**\n>> + * \\class Ccm\n>> + * \\brief A color correction matrix algorithm\n>> + */\n>> +\n>> +LOG_DEFINE_CATEGORY(C3ISPCcm)\n>> +\n>> +Ccm::Ccm()\n>> +{\n>> +}\n>> +\n>> +/**\n>> + * \\copydoc libcamera::ipa::Algorithm::init\n>> + */\n>> +int Ccm::init([[maybe_unused]] IPAContext &context, const YamlObject \n>> &tuningData)\n>> +{\n>> +     ccmCoeff = \n>> tuningData[\"CcmCoeff\"].getList<int>().value_or(std::vector<int>{});\n>\n>\n> This needs some validation; you're relying on it being an array of 9 \n> values below, so that needs to\n> be verified.\n\n\nWill verify.\n\n>\n>> +\n>> +     return 0;\n>> +}\n>> +\n>> +/**\n>> + * \\copydoc libcamera::ipa::Algorithm::prepare\n>> + */\n>> +void Ccm::prepare([[maybe_unused]] IPAContext &context,\n>> +               [[maybe_unused]] const uint32_t frame,\n>> +               [[maybe_unused]] IPAFrameContext &frameContext, \n>> C3ISPParams *params)\n>> +{\n>> +     auto CcmCfg = params->block<BlockType::Ccm>();\n>> +     CcmCfg.setEnabled(C3_ISP_PARAMS_BLOCK_FL_ENABLE);\n>> +\n>> +     for (unsigned int i = 0; i < 3; i++) {\n>> +             for (unsigned int j = 0; j < 3; j++) {\n>> +                     CcmCfg->matrix[i][j] = ccmCoeff[i * 3 + j];\n>> +             }\n>> +     }\n>> +}\n>> +\n>> +/**\n>> + * \\copydoc libcamera::ipa::Algorithm::process\n>> + */\n>> +void Ccm::process([[maybe_unused]] IPAContext &context,\n>> +               [[maybe_unused]] const uint32_t frame,\n>> +               [[maybe_unused]] IPAFrameContext &frameContext,\n>> +               [[maybe_unused]] const c3_isp_stats_info *stats,\n>> +               ControlList &metadata)\n>> +{\n>> +     float m[9];\n>> +     for (unsigned int i = 0; i < 3; i++) {\n>> +             for (unsigned int j = 0; j < 3; j++)\n>> +                     m[i * 3 + j] = ccmCoeff[i * 3 + j];\n>> +     }\n>> +     metadata.set(controls::ColourCorrectionMatrix, m);\n>> +}\n>> +\n>> +REGISTER_IPA_ALGORITHM(Ccm, \"Ccm\")\n>> +\n>> +} /* namespace ipa::c3isp::algorithms */\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/ipa/c3-isp/algorithms/ccm.h \n>> b/src/ipa/c3-isp/algorithms/ccm.h\n>> new file mode 100644\n>> index 00000000..9d8115d4\n>> --- /dev/null\n>> +++ b/src/ipa/c3-isp/algorithms/ccm.h\n>> @@ -0,0 +1,40 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2024, Amlogic\n>> + *\n>> + * C3ISP Color Correction Matrix control\n>> + */\n>> +\n>> +#pragma once\n>> +\n>> +#include <linux/c3-isp-config.h>\n>> +\n>> +#include <libipa/interpolator.h>\n>> +\n>> +#include \"algorithm.h\"\n>> +\n>> +namespace libcamera {\n>> +\n>> +namespace ipa::c3isp::algorithms {\n>> +\n>> +class Ccm : public Algorithm\n>> +{\n>> +public:\n>> +     Ccm();\n>> +     ~Ccm() = default;\n>> +\n>> +     int init(IPAContext &context, const YamlObject &tuningData) \n>> override;\n>> +     void prepare(IPAContext &context, const uint32_t frame,\n>> +                  IPAFrameContext &frameContext,\n>> +                  C3ISPParams *params) override;\n>> +     void process(IPAContext &context, const uint32_t frame,\n>> +                  IPAFrameContext &frameContext, const \n>> c3_isp_stats_info *stats,\n>> +                  ControlList &metadata) override;\n>> +\n>> +private:\n>> +     std::vector<int> ccmCoeff;\n>> +};\n>> +\n>> +} /* namespace ipa::c3isp::algorithms */\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/ipa/c3-isp/algorithms/csc.cpp \n>> b/src/ipa/c3-isp/algorithms/csc.cpp\n>> new file mode 100644\n>> index 00000000..8e066f57\n>> --- /dev/null\n>> +++ b/src/ipa/c3-isp/algorithms/csc.cpp\n>> @@ -0,0 +1,64 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2024, Amlogic\n>> + *\n>> + * C3ISP Color Space Conversion control\n>> + */\n>> +\n>> +#include \"csc.h\"\n>> +\n>> +#include <libcamera/base/log.h>\n>> +#include <libcamera/control_ids.h>\n>> +\n>> +/**\n>> + * \\file csc.h\n>> + */\n>> +\n>> +namespace libcamera {\n>> +\n>> +namespace ipa::c3isp::algorithms {\n>> +\n>> +/**\n>> + * \\class Csc\n>> + * \\brief Color Space Conversion algorithm\n>> + */\n>> +\n>> +LOG_DEFINE_CATEGORY(C3ISPCsc)\n>> +\n>> +Csc::Csc()\n>> +{\n>> +}\n>> +\n>> +/**\n>> + * \\copydoc libcamera::ipa::Algorithm::init\n>> + */\n>> +int Csc::init([[maybe_unused]] IPAContext &context,\n>> +           [[maybe_unused]] const YamlObject &tuningData)\n>> +{\n>> +     cscCoeff = \n>> tuningData[\"CscCoeff\"].getList<int>().value_or(std::vector<int>{});\n> Likewise, validation please.\n\n\nWill verify.\n\n>> +\n>> +     return 0;\n>> +}\n>> +\n>> +/**\n>> + * \\copydoc libcamera::ipa::Algorithm::prepare\n>> + */\n>> +void Csc::prepare([[maybe_unused]] IPAContext &context,\n>> +               [[maybe_unused]] const uint32_t frame,\n>> +               [[maybe_unused]] IPAFrameContext &frameContext,\n>> +               C3ISPParams *params)\n>> +{\n>> +     auto CscCfg = params->block<BlockType::Csc>();\n>> +     CscCfg.setEnabled(C3_ISP_PARAMS_BLOCK_FL_ENABLE);\n>> +\n>> +     for (unsigned int i = 0; i < 3; i++) {\n>> +             for (unsigned int j = 0; j < 3; j++)\n>> +                     CscCfg->matrix[i][j] = cscCoeff[i * 3 + j];\n>> +     }\n>> +}\n>> +\n>> +REGISTER_IPA_ALGORITHM(Csc, \"Csc\")\n>> +\n>> +} /* namespace ipa::c3isp::algorithms */\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/ipa/c3-isp/algorithms/csc.h \n>> b/src/ipa/c3-isp/algorithms/csc.h\n>> new file mode 100644\n>> index 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>> +     Csc();\n>> +     ~Csc() = default;\n>> +\n>> +     int init(IPAContext &context, const YamlObject &tuningData) \n>> override;\n>> +     void prepare(IPAContext &context, const uint32_t frame,\n>> +                  IPAFrameContext &frameContext,\n>> +                  C3ISPParams *params) override;\n>> +private:\n>> +     std::vector<int> cscCoeff;\n>> +};\n>> +\n>> +} /* namespace ipa::c3isp::algorithms */\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/ipa/c3-isp/algorithms/meson.build \n>> b/src/ipa/c3-isp/algorithms/meson.build\n>> new file mode 100644\n>> index 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>> +])\n>> diff --git a/src/ipa/c3-isp/algorithms/post_gamma.cpp \n>> b/src/ipa/c3-isp/algorithms/post_gamma.cpp\n>> new file mode 100644\n>> index 00000000..cd62a604\n>> --- /dev/null\n>> +++ b/src/ipa/c3-isp/algorithms/post_gamma.cpp\n>> @@ -0,0 +1,64 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2024, Amlogic\n>> + *\n>> + * C3ISP Post Gamma control\n>> + */\n>> +\n>> +#include \"post_gamma.h\"\n>> +\n>> +#include <libcamera/base/log.h>\n>> +#include <libcamera/control_ids.h>\n>> +\n>> +/**\n>> + * \\file post_gamma.h\n>> + */\n>> +\n>> +namespace libcamera {\n>> +\n>> +namespace ipa::c3isp::algorithms {\n>> +\n>> +/**\n>> + * \\class PostGamma\n>> + * \\brief A post gamma algorithm\n>> + */\n>> +\n>> +LOG_DEFINE_CATEGORY(C3ISPPostGamma)\n>> +\n>> +PostGamma::PostGamma()\n>> +{\n>> +}\n>> +\n>> +/**\n>> + * \\copydoc libcamera::ipa::Algorithm::init\n>> + */\n>> +int PostGamma::init([[maybe_unused]] IPAContext &context,\n>> +                 [[maybe_unused]] const YamlObject &tuningData)\n>> +{\n>> +     gammaLut =\n>> + \n>> tuningData[\"GammaLut\"].getList<uint16_t>().value_or(std::vector<uint16_t>{});\n> And here, needs validation.\n\n\nWill verify.\n\n>> +\n>> +     return 0;\n>> +}\n>> +\n>> +/**\n>> + * \\copydoc libcamera::ipa::Algorithm::prepare\n>> + */\n>> +void PostGamma::prepare([[maybe_unused]] IPAContext &context,\n>> +                     [[maybe_unused]] const uint32_t frame,\n>> +                     [[maybe_unused]] IPAFrameContext &frameContext,\n>> +                     C3ISPParams *params)\n>> +{\n>> +     auto PostGammaCfg = params->block<BlockType::PostGamma>();\n>> +     PostGammaCfg.setEnabled(C3_ISP_PARAMS_BLOCK_FL_ENABLE);\n>> +\n>> +     for (unsigned int i = 0; i < 129; i++) {\n>> +             PostGammaCfg->lut[i] = gammaLut[i];\n>> +     }\n>> +}\n>> +\n>> +REGISTER_IPA_ALGORITHM(PostGamma, \"PostGamma\")\n>> +\n>> +} /* namespace ipa::c3isp::algorithms */\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/ipa/c3-isp/algorithms/post_gamma.h \n>> b/src/ipa/c3-isp/algorithms/post_gamma.h\n>> new file mode 100644\n>> index 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>> +     PostGamma();\n>> +     ~PostGamma() = default;\n>> +\n>> +     int init(IPAContext &context, const YamlObject &tuningData) \n>> override;\n>> +     void prepare(IPAContext &context, const uint32_t frame,\n>> +                  IPAFrameContext &frameContext,\n>> +                  C3ISPParams *params) override;\n>> +\n>> +private:\n>> +     std::vector<uint16_t> gammaLut;\n>> +};\n>> +\n>> +} /* namespace ipa::c3isp::algorithms */\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/ipa/c3-isp/c3-isp.cpp b/src/ipa/c3-isp/c3-isp.cpp\n>> new file mode 100644\n>> index 00000000..9331576d\n>> --- /dev/null\n>> +++ b/src/ipa/c3-isp/c3-isp.cpp\n>> @@ -0,0 +1,386 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2024, Amlogic Inc.\n>> + *\n>> + * c3-isp.cpp - Amlogic Image Processing Algorithms\n>> + */\n>> +\n>> +#include <algorithm>\n>> +#include <array>\n>> +#include <chrono>\n>> +#include <stdint.h>\n>> +#include <string.h>\n>> +\n>> +#include <linux/c3-isp-config.h>\n>> +#include <linux/v4l2-controls.h>\n>> +\n>> +#include <libcamera/base/file.h>\n>> +#include <libcamera/base/log.h>\n>> +\n>> +#include <libcamera/control_ids.h>\n>> +#include <libcamera/controls.h>\n>> +#include <libcamera/framebuffer.h>\n>> +#include <libcamera/request.h>\n>> +\n>> +#include <libcamera/ipa/c3isp_ipa_interface.h>\n>> +#include <libcamera/ipa/ipa_interface.h>\n>> +#include <libcamera/ipa/ipa_module_info.h>\n>> +\n>> +#include \"libcamera/internal/formats.h\"\n>> +#include \"libcamera/internal/mapped_framebuffer.h\"\n>> +#include \"libcamera/internal/yaml_parser.h\"\n>> +\n>> +#include \"algorithms/algorithm.h\"\n>> +\n>> +#include \"ipa_context.h\"\n>> +#include \"params.h\"\n>> +\n>> +namespace libcamera {\n>> +\n>> +LOG_DEFINE_CATEGORY(IPAC3ISP)\n>> +\n>> +using namespace std::literals::chrono_literals;\n>> +\n>> +namespace ipa::c3isp {\n>> +\n>> +static constexpr uint32_t kMaxFrameContexts = 16;\n>> +\n>> +class IPAC3ISP : public IPAC3ISPInterface, public Module\n>> +{\n>> +public:\n>> +     IPAC3ISP();\n>> +\n>> +     int init(const IPASettings &settings, unsigned int hwRevision,\n>> +              const IPACameraSensorInfo &sensorInfo,\n>> +              const ControlInfoMap &sensorControls,\n>> +              ControlInfoMap *ipaControls) override;\n>> +     int start() override;\n>> +     void stop() override;\n>> +\n>> +     int configure(const IPAConfigInfo &ipaConfig,\n>> +                   ControlInfoMap *ipaControls) override;\n>> +     void mapBuffers(const std::vector<IPABuffer> &buffers) override;\n>> +     void unmapBuffers(const std::vector<unsigned int> &ids) override;\n>> +\n>> +     void queueRequest(const uint32_t frame, const ControlList \n>> &controls) override;\n>> +     void fillParamsBuffer(const uint32_t frame, const uint32_t \n>> bufferId) override;\n>> +     void processStatsBuffer(const uint32_t frame, const uint32_t \n>> bufferId,\n>> +                             const ControlList &sensorControls) \n>> override;\n>> +\n>> +protected:\n>> +     std::string logPrefix() const override;\n>> +\n>> +private:\n>> +     void updateControls(const IPACameraSensorInfo &sensorInfo,\n>> +                         const ControlInfoMap &sensorControls,\n>> +                         ControlInfoMap *ipaControls);\n>> +     void setControls(unsigned int frame);\n>> +\n>> +     std::map<unsigned int, FrameBuffer> buffers_;\n>> +     std::map<unsigned int, MappedFrameBuffer> mappedBuffers_;\n>> +\n>> +     ControlInfoMap sensorControls_;\n>> +\n>> +     struct IPAContext context_;\n>> +};\n>> +\n>> +namespace {\n>> +\n>> +const ControlInfoMap::Map c3ispControls{\n>> +     { &controls::AwbEnable, ControlInfo(false, true) },\n>> +     { &controls::ColourGains, ControlInfo(0.0f, 3.996f, 1.0f) },\n>> +};\n>\n>\n> It would be better to have these within the Awb algorithm and reported \n> out through a ControlInfoMap\n> in the IPA context.\n>\n\nOK, will use your method.\n\n>> +\n>> +} /* namespace */\n>> +\n>> +IPAC3ISP::IPAC3ISP()\n>> +     : context_({ {}, {}, { kMaxFrameContexts }, {}, {} })\n>> +{\n>> +}\n>> +\n>> +std::string IPAC3ISP::logPrefix() const\n>> +{\n>> +     return \"c3isp\";\n>> +}\n>> +\n>> +int IPAC3ISP::init(const IPASettings &settings, unsigned int \n>> hwRevision,\n>> +                const IPACameraSensorInfo &sensorInfo,\n>> +                const ControlInfoMap &sensorControls,\n>> +                ControlInfoMap *ipaControls)\n>> +{\n>> +     LOG(IPAC3ISP, Debug) << \"Hardware revision is \" << hwRevision;\n> This isn't used apart from this debug print. Is it necessary to pass \n> it through to the IPA?\n\n\nWill drop this parameter.\n\n>> +\n>> +     context_.camHelper = \n>> CameraSensorHelperFactoryBase::create(settings.sensorModel);\n>> +     if (!context_.camHelper) {\n>> +             LOG(IPAC3ISP, Error)\n>> +                     << \"Failed to create camera sensor helper for \"\n>> +                     << settings.sensorModel;\n>> +             return -ENODEV;\n>> +     }\n>> +\n>> +     context_.configuration.sensor.lineDuration =\n>> +             sensorInfo.minLineLength * 1.0s / sensorInfo.pixelRate;\n>> +\n>> +     File file(settings.configurationFile);\n>> +     if (!file.open(File::OpenModeFlag::ReadOnly)) {\n>> +             int ret = file.error();\n>> +             LOG(IPAC3ISP, Error)\n>> +                     << \"Failed to open configuration file \"\n>> +                     << settings.configurationFile << \": \" << \n>> strerror(-ret);\n>> +             return ret;\n>> +     }\n>> +\n>> +     std::unique_ptr<libcamera::YamlObject> data = \n>> YamlParser::parse(file);\n>> +     if (!data)\n>> +             return -EINVAL;\n>> +\n>> +     unsigned int version = (*data)[\"version\"].get<uint32_t>(0);\n>> +     if (version != 1) {\n>> +             LOG(IPAC3ISP, Error)\n>> +                     << \"Invalid tuning file version \" << version;\n>> +             return -EINVAL;\n>> +     }\n>> +\n>> +     if (!data->contains(\"algorithms\")) {\n>> +             LOG(IPAC3ISP, Error)\n>> +                     << \"Tuning file doesn't contain any algorithm\";\n>> +             return -EINVAL;\n>> +     }\n>> +\n>> +     int ret = createAlgorithms(context_, (*data)[\"algorithms\"]);\n>> +     if (ret)\n>> +             return ret;\n>> +\n>> +     updateControls(sensorInfo, sensorControls, ipaControls);\n>> +\n>> +     return 0;\n>> +}\n>> +\n>> +int IPAC3ISP::start()\n>> +{\n>> +     setControls(0);\n>> +\n>> +     return 0;\n>> +}\n>> +\n>> +void IPAC3ISP::stop()\n>> +{\n>> +     context_.frameContexts.clear();\n>> +}\n>> +\n>> +int IPAC3ISP::configure(const IPAConfigInfo &ipaConfig,\n>> +                     ControlInfoMap *ipaControls)\n>> +{\n>> +     sensorControls_ = ipaConfig.sensorControls;\n>> +\n>> +     const auto itExp = sensorControls_.find(V4L2_CID_EXPOSURE);\n>> +     int32_t minExposure = itExp->second.min().get<int32_t>();\n>> +     int32_t maxExposure = itExp->second.max().get<int32_t>();\n>> +\n>> +     const auto itGain = sensorControls_.find(V4L2_CID_ANALOGUE_GAIN);\n>> +     int32_t minGain = itGain->second.min().get<int32_t>();\n>> +     int32_t maxGain = itGain->second.max().get<int32_t>();\n>> +\n>> +     LOG(IPAC3ISP, Debug)\n>> +             << \"Exposure: [\" << minExposure << \", \" << maxExposure\n>> +             << \"], gain: [\" << minGain << \", \" << maxGain << \"]\";\n>> +\n>> +     context_.configuration = {};\n>> +     context_.activeState = {};\n>> +     context_.frameContexts.clear();\n>> +\n>> +     const IPACameraSensorInfo &info = ipaConfig.sensorInfo;\n>> +\n>> +     const ControlInfo vBlank = \n>> sensorControls_.find(V4L2_CID_VBLANK)->second;\n>> +     context_.configuration.sensor.defVBlank = \n>> vBlank.def().get<int32_t>();\n>> +     context_.configuration.sensor.size = info.outputSize;\n>> +     context_.configuration.sensor.lineDuration = info.minLineLength \n>> * 1.0s / info.pixelRate;\n>> +\n>> +     updateControls(info, sensorControls_, ipaControls);\n>> +\n>> +     context_.configuration.sensor.minShutterSpeed =\n>> +             minExposure * context_.configuration.sensor.lineDuration;\n>> +     context_.configuration.sensor.maxShutterSpeed =\n>> +             maxExposure * context_.configuration.sensor.lineDuration;\n>> +     context_.configuration.sensor.minAnalogueGain =\n>> +             context_.camHelper->gain(minGain);\n>> +     context_.configuration.sensor.maxAnalogueGain =\n>> +             context_.camHelper->gain(maxGain);\n>> +\n>> +     for (auto const &a : algorithms()) {\n>> +             Algorithm *algo = static_cast<Algorithm *>(a.get());\n>> +\n>> +             if (algo->disabled_)\n>> +                     continue;\n>> +\n>> +             int ret = algo->configure(context_, info);\n>> +             if (ret)\n>> +                     return ret;\n>> +     }\n>> +\n>> +     return 0;\n>> +}\n>> +\n>> +void IPAC3ISP::mapBuffers(const std::vector<IPABuffer> &buffers)\n>> +{\n>> +     for (const IPABuffer &buffer : buffers) {\n>> +             auto elem = buffers_.emplace(std::piecewise_construct,\n>> + std::forward_as_tuple(buffer.id),\n>> + std::forward_as_tuple(buffer.planes));\n>> +             const FrameBuffer &fb = elem.first->second;\n>> +\n>> +             MappedFrameBuffer mappedBuffer(&fb, \n>> MappedFrameBuffer::MapFlag::ReadWrite);\n>> +             if (!mappedBuffer.isValid()) {\n>> +                     LOG(IPAC3ISP, Fatal) << \"Failed tommap buffer: \"\n>> +                                          << \n>> strerror(mappedBuffer.error());\n>> +             }\n>> +\n>> +             mappedBuffers_.emplace(buffer.id, \n>> std::move(mappedBuffer));\n>> +     }\n>> +}\n>> +\n>> +void IPAC3ISP::unmapBuffers(const std::vector<unsigned int> &ids)\n>> +{\n>> +     for (unsigned int id : ids) {\n>> +             const auto fb = buffers_.find(id);\n>> +             if (fb == buffers_.end())\n>> +                     continue;\n>> +\n>> +             mappedBuffers_.erase(id);\n>> +             buffers_.erase(id);\n>> +     }\n>> +}\n>> +\n>> +void IPAC3ISP::queueRequest(const uint32_t frame, const ControlList \n>> &controls)\n>> +{\n>> +     IPAFrameContext &frameContext = \n>> context_.frameContexts.alloc(frame);\n>> +\n>> +     for (auto const &a : algorithms()) {\n>> +             Algorithm *algo = static_cast<Algorithm *>(a.get());\n>> +             if (algo->disabled_)\n>> +                     continue;\n>> +             algo->queueRequest(context_, frame, frameContext, \n>> controls);\n>> +     }\n>> +}\n>> +\n>> +void IPAC3ISP::fillParamsBuffer(const uint32_t frame, const uint32_t \n>> bufferId)\n>> +{\n>> +     IPAFrameContext &frameContext = context_.frameContexts.get(frame);\n>> +\n>> +     C3ISPParams params(mappedBuffers_.at(bufferId).planes()[0]);\n>> +\n>> +     for (auto const &algo : algorithms())\n>> +             algo->prepare(context_, frame, frameContext, &params);\n>> +\n>> +     paramsBufferReady.emit(frame, params.size());\n>> +}\n>> +\n>> +void IPAC3ISP::processStatsBuffer(const uint32_t frame, const \n>> uint32_t bufferId,\n>> +                               const ControlList &sensorControls)\n>> +{\n>> +     IPAFrameContext &frameContext = context_.frameContexts.get(frame);\n>> +\n>> +     const c3_isp_stats_info *stats = nullptr;\n>> +\n>> +     stats = reinterpret_cast<c3_isp_stats_info *>(\n>> +             mappedBuffers_.at(bufferId).planes()[0].data());\n>> +\n>> +     frameContext.sensor.exposure =\n>> + sensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>();\n>> +     frameContext.sensor.gain =\n>> + \n>> context_.camHelper->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>());\n>> +\n>> +     ControlList metadata(controls::controls);\n>> +\n>> +     for (auto const &a : algorithms()) {\n>> +             Algorithm *algo = static_cast<Algorithm *>(a.get());\n>> +             if (algo->disabled_)\n>> +                     continue;\n>> +             algo->process(context_, frame, frameContext, stats, \n>> metadata);\n>> +     }\n>> +\n>> +     setControls(frame);\n>> +\n>> +     metadataReady.emit(frame, metadata);\n>> +}\n>> +\n>> +void IPAC3ISP::updateControls(const IPACameraSensorInfo &sensorInfo,\n>> +                           const ControlInfoMap &sensorControls,\n>> +                           ControlInfoMap *ipaControls)\n>> +{\n>> +     ControlInfoMap::Map ctrlMap = c3ispControls;\n>> +\n>> +     double lineDuration = \n>> context_.configuration.sensor.lineDuration.get<std::micro>();\n>> +     const ControlInfo &v4l2Exposure = \n>> sensorControls.find(V4L2_CID_EXPOSURE)->second;\n>> +     int32_t minExposure = v4l2Exposure.min().get<int32_t>() * \n>> lineDuration;\n>> +     int32_t maxExposure = v4l2Exposure.max().get<int32_t>() * \n>> lineDuration;\n>> +     int32_t defExposure = v4l2Exposure.def().get<int32_t>() * \n>> lineDuration;\n>> +     ctrlMap.emplace(std::piecewise_construct,\n>> + std::forward_as_tuple(&controls::ExposureTime),\n>> +                     std::forward_as_tuple(minExposure, maxExposure, \n>> defExposure));\n>> +\n>> +     const ControlInfo &v4l2Gain = \n>> sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;\n>> +     float minGain = \n>> context_.camHelper->gain(v4l2Gain.min().get<int32_t>());\n>> +     float maxGain = \n>> context_.camHelper->gain(v4l2Gain.max().get<int32_t>());\n>> +     float defGain = \n>> context_.camHelper->gain(v4l2Gain.def().get<int32_t>());\n>> +     ctrlMap.emplace(std::piecewise_construct,\n>> + std::forward_as_tuple(&controls::AnalogueGain),\n>> +                     std::forward_as_tuple(minGain, maxGain, defGain));\n>> +\n>> +     const ControlInfo &v4l2HBlank = \n>> sensorControls.find(V4L2_CID_HBLANK)->second;\n>> +     uint32_t hblank = v4l2HBlank.def().get<int32_t>();\n>> +     uint32_t lineLength = sensorInfo.outputSize.width + hblank;\n>> +\n>> +     const ControlInfo &v4l2VBlank = \n>> sensorControls.find(V4L2_CID_VBLANK)->second;\n>> +     std::array<uint32_t, 3> frameHeights{\n>> +             v4l2VBlank.min().get<int32_t>() + \n>> sensorInfo.outputSize.height,\n>> +             v4l2VBlank.max().get<int32_t>() + \n>> sensorInfo.outputSize.height,\n>> +             v4l2VBlank.def().get<int32_t>() + \n>> sensorInfo.outputSize.height,\n>> +     };\n>> +\n>> +     std::array<int64_t, 3> frameDurations;\n>> +     for (unsigned int i = 0; i < frameHeights.size(); ++i) {\n>> +             uint64_t frameSize = lineLength * frameHeights[i];\n>> +             frameDurations[i] = frameSize / (sensorInfo.pixelRate / \n>> 1000000U);\n>> +     }\n>> +\n>> +     ctrlMap[&controls::FrameDurationLimits] = \n>> ControlInfo(frameDurations[0],\n>> + frameDurations[1],\n>> + frameDurations[2]);\n>> +     ctrlMap.insert(context_.ctrlMap.begin(), context_.ctrlMap.end());\n>> +     *ipaControls = ControlInfoMap(std::move(ctrlMap), \n>> controls::controls);\n>> +}\n>> +\n>> +void IPAC3ISP::setControls(unsigned int frame)\n>> +{\n>> +     uint32_t exposure = context_.activeState.agc.exposure;\n>> +     uint32_t gain = \n>> context_.camHelper->gainCode(context_.activeState.agc.gain);\n>> +\n>> +     ControlList ctrls(sensorControls_);\n>> +     ctrls.set(V4L2_CID_EXPOSURE, static_cast<int32_t>(exposure));\n>> +     ctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast<int32_t>(gain));\n>> +\n>> +     setSensorControls.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>> +     IPA_MODULE_API_VERSION,\n>> +     1,\n>> +     \"c3isp\",\n>> +     \"c3isp\",\n>> +};\n>> +\n>> +IPAInterface *ipaCreate()\n>> +{\n>> +     return new ipa::c3isp::IPAC3ISP();\n>> +}\n>> +}\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/ipa/c3-isp/data/imx290.yaml \n>> b/src/ipa/c3-isp/data/imx290.yaml\n>> new file mode 100644\n>> index 00000000..498a3684\n>> --- /dev/null\n>> +++ b/src/ipa/c3-isp/data/imx290.yaml\n>> @@ -0,0 +1,30 @@\n>> +# SPDX-License-Identifier: CC0-1.0\n>> +%YAML 1.1\n>> +---\n>> +version: 1\n>> +algorithms:\n>> +  - Agc:\n>> +  - Awb:\n>> +  - PostGamma:\n>> +      GammaLut: [\n>> +                   0,   86,  134,  169,  198,  223,  245, 265,  283, \n>> 300, 316, 331,\n>> +                   346, 359, 373,  385,  397,  409,  420, 431,  441, \n>> 452, 462, 471,\n>> +                   481, 490, 499,  508,  516,  525,  533, 541,  549, \n>> 557, 565, 572,\n>> +                   580, 587, 594,  601,  608,  615,  622, 629,  635, \n>> 642, 648, 655,\n>> +                   661, 667, 673,  679,  685,  691,  697, 703,  708, \n>> 714, 720, 725,\n>> +                   731, 736, 742,  747,  752,  758,  763, 768,  773, \n>> 778, 783, 788,\n>> +                   793, 798, 803,  808,  812,  817,  822, 827,  831, \n>> 836, 840, 845,\n>> +                   849, 854, 858,  863,  867,  872,  876, 880,  884, \n>> 889, 893, 897,\n>> +                   901, 905, 910,  914,  918,  922,  926, 930,  934, \n>> 938, 942, 946,\n>> +                   950, 953, 957,  961,  965,  969,  972, 976,  980, \n>> 984, 987, 991,\n>> +                   995, 998, 1002, 1006, 1009, 1013, 1016, 1020, 1023\n>> +                ]\n>> +  - Ccm:\n>> +      CcmCoeff: [ 533, -191, -86, -147, 474, -71, 23, -208, 441 ]\n>> +  - Csc:\n>> +      CscCoeff: [ 54, 183, 19, -29, -99, 128, 128, -116, -12 ]\n>> +  - Blc:\n>> +      BlcR: 20089\n>> +      BlcGr: 20090\n>> +      BlcGb: 20081\n>> +      BlcB: 20084\n> Separate commit please\n\n\nWill separate commit.\n\n>> diff --git a/src/ipa/c3-isp/data/meson.build \n>> b/src/ipa/c3-isp/data/meson.build\n>> new file mode 100644\n>> index 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')\n>> diff --git a/src/ipa/c3-isp/ipa_context.cpp \n>> b/src/ipa/c3-isp/ipa_context.cpp\n>> new file mode 100644\n>> index 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 \n>> parameters that\n>> + * remain constant during the capture session, from IPA module start \n>> to stop.\n>> + * It is typically set during the configure() operation of the IPA \n>> 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 \n>> to be\n>> + * maintained by algorithms across frames. Unlike the session \n>> configuration,\n>> + * the active state is mutable and constantly updated by algorithms. \n>> 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 \n>> 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 \n>> values.\n>> + *    The active state thus contains a consolidated view of the \n>> value of all\n>> + *    controls handled by the algorithm.\n>> + *\n>> + *  - The value of parameters computed by the algorithm when running \n>> in auto\n>> + *    mode. Algorithms running in auto mode compute new parameters \n>> every\n>> + *    time statistics buffers are received (either synchronously, or\n>> + *    possibly in a background thread). The latest computed value of \n>> those\n>> + *    parameters is stored in the active state in the process() \n>> function.\n>> + *\n>> + * Each of the members in the active state belongs to a specific \n>> algorithm. A\n>> + * member may be read by any algorithm, but shall only be written by \n>> 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 \n>> algorithm\n>> + * based on the latest processed statistics. All other variables \n>> 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 \n>> values are\n>> + *   typically set in the queueRequest() function, from the \n>> consolidated\n>> + *   control values stored in the active state. The frame context \n>> thus stores\n>> + *   values for all controls related to the algorithm, not limited \n>> to the\n>> + *   controls specified in the corresponding request, but \n>> 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 \n>> instance the\n>> + *   colour gains for the AWB algorithm, the control value will be \n>> stored in\n>> + *   the frame context in the queueRequest() function only when \n>> operating in\n>> + *   manual mode. When operating in auto mode, the values are \n>> computed by the\n>> + *   algorithm in process(), stored in the active state, and copied \n>> to the\n>> + *   frame context in prepare(), just before being stored in the ISP \n>> parameters\n>> + *   buffer.\n>> + *\n>> + *   The queueRequest() function can also store ancillary data in \n>> the frame\n>> + *   context, such as flags to indicate if (and what) control values \n>> have\n>> + *   changed compared to the previous request.\n>> + *\n>> + * - Status information computed by the algorithm for a frame. For \n>> instance,\n>> + *   the colour temperature estimated by the AWB algorithm from ISP \n>> statistics\n>> + *   calculated on a frame is stored in the frame context for that \n>> 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 \n>> 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 \n>> 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 \n>> 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 \n>> control\n>> + *\n>> + * \\var IPAFrameContext::agc.updateMetering\n>> + * \\brief Indicate if new ISP AGC metering parameters need to be \n>> 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 \n>> algorithms\n>> + *\n>> + * \\var IPAContext::frameContexts\n>> + * \\brief Ring buffer of per-frame contexts\n>> + */\n>> +\n>> +} /* namespace libcamera::ipa::c3isp */\n>> diff --git a/src/ipa/c3-isp/ipa_context.h b/src/ipa/c3-isp/ipa_context.h\n>> new file mode 100644\n>> index 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>> +     struct {\n>> +             utils::Duration minShutterSpeed;\n>> +             utils::Duration maxShutterSpeed;\n>> +             double minAnalogueGain;\n>> +             double maxAnalogueGain;\n>> +\n>> +             int32_t defVBlank;\n>> +             utils::Duration lineDuration;\n>> +             Size size;\n>> +     } sensor;\n>> +};\n>> +\n>> +struct IPAActiveState {\n>> +     struct {\n>> +             uint32_t exposure;\n>> +             double gain;\n>> +             uint32_t constraintMode;\n>> +             uint32_t exposureMode;\n> If you include the control_ids header you can use \n> controls::AeConstraintModeEnum /\n> AeExposureModeEnum here which is a bit more explicit\n\n\nWill check this issue.\n\n>> +     } agc;\n>> +\n>> +     struct {\n>> +             struct {\n>> +                     struct {\n>> +                             double red;\n>> +                             double green;\n>> +                             double blue;\n>> +                     } manual;\n>> +                     struct {\n>> +                             double red;\n>> +                             double green;\n>> +                             double blue;\n>> +                     } automatic;\n>> +             } gains;\n>> +\n>> +             unsigned int temperatureK;\n>> +             bool autoEnabled;\n>> +     } awb;\n>> +};\n>> +\n>> +struct IPAFrameContext : public FrameContext {\n>> +     struct {\n>> +             uint32_t exposure;\n>> +             double gain;\n>> +             bool autoEnabled;\n>> +             controls::AeConstraintModeEnum constraintMode;\n>> +             controls::AeExposureModeEnum exposureMode;\n>> +             controls::AeMeteringModeEnum meteringMode;\n> Ah - like this!\n\n\nOK.\n\n>> +             utils::Duration maxFrameDuration;\n>> +             bool updateMetering;\n>> +     } agc;\n>> +\n>> +     struct {\n>> +             struct {\n>> +                     double red;\n>> +                     double green;\n>> +                     double blue;\n>> +             } gains;\n>> +\n>> +             bool autoEnabled;\n>> +     } awb;\n>> +\n>> +     struct {\n>> +             uint32_t exposure;\n>> +             double gain;\n>> +     } sensor;\n>> +};\n>> +\n>> +struct IPAContext {\n>> +     IPASessionConfiguration configuration;\n>> +     IPAActiveState activeState;\n>> +\n>> +     FCQueue<IPAFrameContext> frameContexts;\n>> +\n>> +     ControlInfoMap::Map ctrlMap;\n>> +\n>> +     /* Interface to the Camera Helper */\n>> +     std::unique_ptr<CameraSensorHelper> camHelper;\n>> +};\n>> +\n>> +} /* namespace ipa::c3isp */\n>> +\n>> +} /* namespace libcamera*/\n>> diff --git a/src/ipa/c3-isp/meson.build b/src/ipa/c3-isp/meson.build\n>> new file mode 100644\n>> index 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@', \n>> '@OUTPUT@'],\n>> +                  install : false,\n>> +                  build_by_default : true)\n>> +endif\n>> +\n>> +ipa_names += ipa_name\n>> diff --git a/src/ipa/c3-isp/module.h b/src/ipa/c3-isp/module.h\n>> new file mode 100644\n>> index 00000000..1a66c5d4\n>> --- /dev/null\n>> +++ b/src/ipa/c3-isp/module.h\n>> @@ -0,0 +1,28 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2024, Amlogic\n>> + *\n>> + * C3ISP IPA Module\n>> + */\n>> +\n>> +#pragma once\n>> +\n>> +#include <linux/c3-isp-config.h>\n>> +\n>> +#include <libcamera/ipa/c3isp_ipa_interface.h>\n>> +\n>> +#include <libipa/module.h>\n>> +\n>> +#include \"ipa_context.h\"\n>> +#include \"params.h\"\n>> +\n>> +namespace libcamera {\n>> +\n>> +namespace ipa::c3isp {\n>> +\n>> +using Module = ipa::Module<IPAContext, IPAFrameContext, \n>> IPACameraSensorInfo,\n>> +                        C3ISPParams, c3_isp_stats_info>;\n>> +\n>> +} /* namespace ipa::c3isp */\n>> +\n>> +} /* namespace libcamera*/\n>> diff --git a/src/ipa/c3-isp/params.cpp b/src/ipa/c3-isp/params.cpp\n>> new file mode 100644\n>> index 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>> +     enum c3_isp_params_block_type type;\n>> +     size_t size;\n>> +};\n>> +\n>> +#define C3ISP_BLOCK_TYPE_ENTRY(block, id, type)                      \\\n>> + {                                                            \\\n>> + BlockType::block,                                    \\\n>> + {                                                    \\\n>> + C3_ISP_PARAMS_BLOCK_##id,                    \\\n>> +                             sizeof(struct c3_isp_params_##type), \\\n>> + }                                                    \\\n>> +     }\n>> +\n>> +const std::map<BlockType, BlockTypeInfo> kBlockTypeInfo = {\n>> +     C3ISP_BLOCK_TYPE_ENTRY(AWBGains, AWB_GAINS, awb_gains),\n>> +     C3ISP_BLOCK_TYPE_ENTRY(AWBConfig, AWB_CONFIG, awb_config),\n>> +     C3ISP_BLOCK_TYPE_ENTRY(AEConfig, AE_CONFIG, ae_config),\n>> +     C3ISP_BLOCK_TYPE_ENTRY(AFConfig, AF_CONFIG, af_config),\n>> +     C3ISP_BLOCK_TYPE_ENTRY(PostGamma, PST_GAMMA, pst_gamma),\n>> +     C3ISP_BLOCK_TYPE_ENTRY(Ccm, CCM, ccm),\n>> +     C3ISP_BLOCK_TYPE_ENTRY(Csc, CSC, csc),\n>> +     C3ISP_BLOCK_TYPE_ENTRY(Blc, BLC, blc),\n>> +};\n>> +\n>> +} /* namespace */\n>> +\n>> +C3ISPParamsBlockBase::C3ISPParamsBlockBase(BlockType type,\n>> +                                        const Span<uint8_t> &data)\n>> +     : type_(type), data_(data)\n>> +{\n>> +     header_ = data.subspan(0, sizeof(c3_isp_params_block_header));\n>> +}\n>> +\n>> +void C3ISPParamsBlockBase::setEnabled(uint16_t flags)\n>> +{\n>> +     struct c3_isp_params_block_header *header =\n>> +             reinterpret_cast<struct c3_isp_params_block_header \n>> *>(header_.data());\n>> +\n>> +     header->flags = flags;\n>> +}\n>> +\n>> +C3ISPParams::C3ISPParams(Span<uint8_t> data)\n>> +     : data_(data), used_(0)\n>> +{\n>> +     struct c3_isp_params_cfg *buffer =\n>> +             reinterpret_cast<struct c3_isp_params_cfg *>(data.data());\n>> +\n>> +     buffer->version = C3_ISP_PARAMS_BUFFER_V0;\n>> +     buffer->data_size = 0;\n>> +\n>> +     used_ += offsetof(struct c3_isp_params_cfg, data);\n>> +}\n>> +\n>> +Span<uint8_t> C3ISPParams::block(BlockType type)\n>> +{\n>> +     auto infoIt = kBlockTypeInfo.find(type);\n>> +     if (infoIt == kBlockTypeInfo.end()) {\n>> +             LOG(C3ISPParams, Error)\n>> +                     << \"Invalid parameters type \"\n>> +                     << utils::to_underlying(type);\n>> +             return {};\n>> +     }\n>> +\n>> +     const BlockTypeInfo &info = infoIt->second;\n>> +\n>> +     auto cacheIt = blocks_.find(type);\n>> +     if (cacheIt != blocks_.end())\n>> +             return cacheIt->second;\n>> +\n>> +     size_t size = info.size;\n>> +     if (size > data_.size() - used_) {\n>> +             LOG(C3ISPParams, Error)\n>> +                     << \"Out of memory to allocate block type \"\n> Not really an _allocation_ per se. I think I'd re-word this to make \n> clear that the buffer ran out of\n> space.\n\n\nWill re-word this.\n\n>> +                     << utils::to_underlying(type);\n>> +             return {};\n>> +     }\n>> +\n>> +     Span<uint8_t> block = data_.subspan(used_, info.size);\n>> +     used_ += block.size();\n>> +\n>> +     struct c3_isp_params_cfg *buffer =\n>> +             reinterpret_cast<struct c3_isp_params_cfg \n>> *>(data_.data());\n>> +     buffer->data_size += block.size();\n>> +\n>> +     memset(block.data(), 0, block.size());\n>> +\n>> +     struct c3_isp_params_block_header *header =\n>> +             reinterpret_cast<struct c3_isp_params_block_header \n>> *>(block.data());\n>> +     header->type = info.type;\n>> +     header->size = block.size();\n>> +\n>> +     blocks_[type] = block;\n>> +\n>> +     return block;\n>> +}\n>> +\n>> +} /* namespace ipa::c3isp */\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/ipa/c3-isp/params.h b/src/ipa/c3-isp/params.h\n>> new file mode 100644\n>> index 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>> +     AWBGains,\n>> +     AWBConfig,\n>> +     AEConfig,\n>> +     AFConfig,\n>> +     PostGamma,\n>> +     Ccm,\n>> +     Csc,\n>> +     Blc,\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>> + template<>                                               \\\n>> +     struct block_type<BlockType::blocktype> {                \\\n>> +             using type = struct c3_isp_params_##blockStruct; \\\n>> +     };\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>> +     C3ISPParamsBlockBase(BlockType type, const Span<uint8_t> &data);\n>> +\n>> +     Span<uint8_t> data() const { return data_; }\n>> +\n>> +     void setEnabled(uint16_t flags);\n>> +\n>> +private:\n>> +     LIBCAMERA_DISABLE_COPY(C3ISPParamsBlockBase)\n>> +\n>> +     BlockType type_;\n>> +     Span<uint8_t> header_;\n>> +     Span<uint8_t> data_;\n>> +};\n>> +\n>> +template<BlockType B>\n>> +class C3ISPParamsBlock : public C3ISPParamsBlockBase\n>> +{\n>> +public:\n>> +     using Type = typename details::block_type<B>::type;\n>> +\n>> +     C3ISPParamsBlock(const Span<uint8_t> &data)\n>> +             : C3ISPParamsBlockBase(B, data)\n>> +     {\n>> +     }\n>> +\n>> +     const Type *operator->() const\n>> +     {\n>> +             return reinterpret_cast<const Type *>(data().data());\n>> +     }\n>> +\n>> +     Type *operator->()\n>> +     {\n>> +             return reinterpret_cast<Type *>(data().data());\n>> +     }\n>> +\n>> +     const Type &operator*() const &\n>> +     {\n>> +             return *reinterpret_cast<const Type *>(data().data());\n>> +     }\n>> +\n>> +     const Type &operator*() &\n>> +     {\n>> +             return *reinterpret_cast<Type *>(data().data());\n>> +     }\n>> +};\n>> +\n>> +class C3ISPParams\n>> +{\n>> +public:\n>> +     C3ISPParams(Span<uint8_t> data);\n>> +\n>> +     template<BlockType B>\n>> +     C3ISPParamsBlock<B> block()\n>> +     {\n>> +             return C3ISPParamsBlock<B>(block(B));\n>> +     }\n>> +\n>> +     size_t size() const { return used_; }\n>> +\n>> +private:\n>> +     friend class C3ISPParamsBlockBase;\n>> +\n>> +     Span<uint8_t> block(BlockType type);\n>> +\n>> +     Span<uint8_t> data_;\n>> +     size_t used_;\n>> +\n>> +     std::map<BlockType, Span<uint8_t>> blocks_;\n>> +};\n>> +\n>> +} /* namespace ipa::c3isp */\n>> +\n>> +} /* namespace libcamera */","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 54B63C32F2\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  5 Feb 2025 11:47:06 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 436F8685C0;\n\tWed,  5 Feb 2025 12:47:05 +0100 (CET)","from HK3PR03CU002.outbound.protection.outlook.com\n\t(mail-eastasiaazlp170110002.outbound.protection.outlook.com\n\t[IPv6:2a01:111:f403:c400::2])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id E99356053F\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  5 Feb 2025 12:47:02 +0100 (CET)","from TYSPR03MB8627.apcprd03.prod.outlook.com (2603:1096:405:8a::9)\n\tby JH0PR03MB8187.apcprd03.prod.outlook.com (2603:1096:990:48::5)\n\twith Microsoft SMTP Server (version=TLS1_2,\n\tcipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8398.24;\n\tWed, 5 Feb 2025 11:46:57 +0000","from TYSPR03MB8627.apcprd03.prod.outlook.com\n\t([fe80::cf16:aa54:9bd5:26f]) by\n\tTYSPR03MB8627.apcprd03.prod.outlook.com\n\t([fe80::cf16:aa54:9bd5:26f%4]) with mapi id 15.20.8398.021;\n\tWed, 5 Feb 2025 11:46:57 +0000"],"Authentication-Results":["lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=amlogic.com header.i=@amlogic.com\n\theader.b=\"QV+awW/p\"; dkim-atps=neutral","dkim=none (message not signed)\n\theader.d=none;dmarc=none action=none header.from=amlogic.com;"],"ARC-Seal":"i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none;\n\tb=HCgrfiMH7EqEP2N2r88uJTeI+GElc8X23kJCBf1dBQdEYZqlFbRKN1hMBSEQU3Yw1sf7WASeHar8fRfK22pwxWTCu3NiCYg5De1xVxB/YByUsSui7yGz1rnBztc7MF8Jj8MW0cnngQDApdnVQBPibiU4qyEIBEEOx8+lQDpnAWC6pUnp3KBX0d6ncHyslyGGwuSVRBpyJOcWmuSC3Tr3BZFiwYcbs82uGrtpRMBjllDNfWeO89XH0jDdMe7oFgKvUMYgbzExm9TdV8KlKvwz7JGW40wffNFwYYSIZKUi2Yzo8LpatHrlw+eLH+J8r65T6rAEQioxR/B4lP8IlSvRWA==","ARC-Message-Signature":"i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com;\n\ts=arcselector10001;\n\th=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1;\n\tbh=+xeVIZ6OP0mJxZ5qMGXvkrhx6ixKD/vQYJ7CgXgi/uw=;\n\tb=tYMxL2olfgaD9RRy+2JQgk5gB5WGMjeFSgNMKr7b1m12iYyS3FRLgBdEf8orjosLpIuIWsaToBtZyR4BXnYRnPLsX1SUVQHnwY+yURa1nmsXwZF3sO2/MDTLZ2+TO0p7TCYOfjPB/N+1OHT9vg1aqGdGlt2KziWuwhlb8dbBzbn277R7Fjuc+In29MmpEeUwJOzdoLdA6BzGVcOrpOYN8bptRTXeJz+wC2QlBAs1I6Xr83XIEbCV5MaYfg+0R2tg0RhiFrASxdjBZHFQeDqjxujqZPhvy775Jocz47J7FdF+9iUCaU+Ja+Y6+/Z2mYB8JjHn0RELwrG3r0OJhx22Ew==","ARC-Authentication-Results":"i=1; mx.microsoft.com 1; spf=pass\n\tsmtp.mailfrom=amlogic.com;\n\tdmarc=pass action=none header.from=amlogic.com; \n\tdkim=pass header.d=amlogic.com; arc=none","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=amlogic.com;\n\ts=selector1;\n\th=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;\n\tbh=+xeVIZ6OP0mJxZ5qMGXvkrhx6ixKD/vQYJ7CgXgi/uw=;\n\tb=QV+awW/pedh+TqUEzegob5qWZ+UIO0Cw5FYq/OqERmaBLalTYMkwtA8xfLZpH0k1Ll3xm9dUw4l/etDtJ3eDp7BSH3hlaPbTrbf9VS1w1XhcxJ3H4POMyhMLwt7nNB6fZMrXgLnBGhCLPK04/8h3USVwQKKTRgiKYQySi4/tI29Pr0EUlEBHL12eytv/ulrIru+qNVi3J81otgfih7InBjKheeHtj2gdcIkwTO6A5LJ0a3eND1ggE+Z8mQNyGpvBijKpydfP/ITIcrwzvVKjaHHO1tFzw/6xsp+Lqfr0RWSzm83SmWSqJsDbY12cO5LamLW9zFkQroRNBs2sxYbXaQ==","Message-ID":"<480215f2-2edd-4540-b8a1-336ff3095657@amlogic.com>","Date":"Wed, 5 Feb 2025 19:46:54 +0800","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v2 2/2] libcamera: ipa: Add C3 ISP IPA","To":"Dan Scally <dan.scally@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Cc":"kieran.bingham@ideasonboard.com, laurent.pinchart@ideasonboard.com,\n\tjacopo.mondi@ideasonboard.com","References":"<20241227105840.159559-1-keke.li@amlogic.com>\n\t<20241227105840.159559-3-keke.li@amlogic.com>\n\t<7a4ffb1f-51e8-40f9-b2c8-ac09df58f457@ideasonboard.com>","Content-Language":"en-US","From":"Keke Li <keke.li@amlogic.com>","In-Reply-To":"<7a4ffb1f-51e8-40f9-b2c8-ac09df58f457@ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"8bit","X-ClientProxiedBy":"TY2PR02CA0056.apcprd02.prod.outlook.com\n\t(2603:1096:404:e2::20) To TYSPR03MB8627.apcprd03.prod.outlook.com\n\t(2603:1096:405:8a::9)","MIME-Version":"1.0","X-MS-PublicTrafficType":"Email","X-MS-TrafficTypeDiagnostic":"TYSPR03MB8627:EE_|JH0PR03MB8187:EE_","X-MS-Office365-Filtering-Correlation-Id":"c2aa3151-b624-4841-55b3-08dd45dacb21","X-MS-Exchange-SenderADCheck":"1","X-MS-Exchange-AntiSpam-Relay":"0","X-Microsoft-Antispam":"BCL:0;ARA:13230040|366016|1800799024|376014;","X-Microsoft-Antispam-Message-Info":"=?utf-8?q?gOD3RvT7tGUS66iu0oW5EnjMB8vk?=\n\t=?utf-8?q?ZUkOjt7Zr6KhchshjXE5jVi9+ea2bGGXsc1WEq9ui09FDxjmJ9FhIhi4?=\n\t=?utf-8?q?5oT49Xj6zkxs7qUC2lSD1zIcXWSXAf36oflDmlnlkvs2hF6HFIJK5C4z?=\n\t=?utf-8?q?vCOpdxlP6ZwnncMfsZRJiZeyYAv00RmyYHeLdHERrbK8xNfAPJsd4eif?=\n\t=?utf-8?q?crhx/NrarYAy634QVUAwZTUfLZA32ZDrASbwQLUNNtcrsRIr2oiYWx4w?=\n\t=?utf-8?q?6fcXvFuAzXO0vElU0nedZ+gB5+9kP2o59cJ4aETce/OxYB7ZkI+gml/U?=\n\t=?utf-8?q?KTJW+kkWef901k/RT830LvaaYxHBhPNXkQjFKQwVnNI+eH5plhRuaEGb?=\n\t=?utf-8?q?7+TbfeLdzg7a2JoNwBsOPf/3UZSpPOWfkRYGSlx9Ol8SzLd/Enk3atMz?=\n\t=?utf-8?q?DKtnDle5LGhn8RBgwTmCedjvw7OHpp/AhJa29hrGbULFE4FqGfslyPRF?=\n\t=?utf-8?q?DG2xR6juwE10M7GCHL5JQeuIzRYGJpQiXaz5waZ2qaGiY9gWw9OHL3C0?=\n\t=?utf-8?q?1+ReZtGQxHvl3dVAJIsUDij6dr5xeWxyippGw/eQFQQE6BKP7c5bHkWK?=\n\t=?utf-8?q?jYn21PS7UmB9M/UoK5E8lD+QkDG1aWtCIAELM5w73xJGRLnAueLLY1Pb?=\n\t=?utf-8?q?p3POOaloj8tUBp/V7iQMhigdcRlHR9YQV7f9KUtqUANziq6Dh9NxyaF1?=\n\t=?utf-8?q?yqefaIco30bpUWpkNpBal5REwrkpFT8zMH3dZ8pHu031D5nVtpS0UsHd?=\n\t=?utf-8?q?cwEwxRnFZni8XdNFNMgp+AyAYRkHdDKJM6lZcMXPutncNuvBdc2huoHG?=\n\t=?utf-8?q?TX5pQHikj8ttL0mCj479xn/0x0RO+Ouh+BScUksXU1v83qVlvLx++Xsp?=\n\t=?utf-8?q?aJ6vKAWw/p++9g2Z79lSYbHOqJX3oJcrMrOgb4Vnm53L/R4X8zY+qIE9?=\n\t=?utf-8?q?Srkh3g3MtSp15XeU6BtMrW17Tfab15WCIXjsnu92jBzQJE2TNqktt7rx?=\n\t=?utf-8?q?5JPXkwnl8QrOcJ4z1QWUUS1HYuIc+uPVK2lQgEQ7QP43+65EU8nBlqeY?=\n\t=?utf-8?q?QYGXWiSQbs04MSBj8tOmMxr1OsAvpwrFm+07+jCM0n7gAM56xTkOWuz8?=\n\t=?utf-8?q?jcyby1ClffFDUkBKKro6BheDtJOlhbyAcyrDj1YRhw2R4c0zRH6bCifM?=\n\t=?utf-8?q?omAVHgflHagH9JYfefnlLa9nXzstQat0NyI9pUYDBhxeMyZ7B+TSgiKd?=\n\t=?utf-8?q?6ReUcyq7I8tqRcbvK6LF2DhRxN5PdtsX0zqwpdBQN7iRwyNG5f2TpF3V?=\n\t=?utf-8?q?vssCD0VMHXxv5QCiI+p/kB7enRQjVIWAIEgeQA+AVzMk7uJi+wAskrEl?=\n\t=?utf-8?q?Q4wDk0QQAgM2BwzhxSnbTiINKf1MW2jLo6AXSmRmkk8odlOYQQ=3D=3D?=","X-Forefront-Antispam-Report":"CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:;\n\tIPV:NLI; SFV:NSPM; H:TYSPR03MB8627.apcprd03.prod.outlook.com; PTR:;\n\tCAT:NONE; \n\tSFS:(13230040)(366016)(1800799024)(376014); DIR:OUT; SFP:1102; ","X-MS-Exchange-AntiSpam-MessageData-ChunkCount":"1","X-MS-Exchange-AntiSpam-MessageData-0":"=?utf-8?q?J4RfZRhEwbM3DBGzGvTLiVxes?=\n\t=?utf-8?q?CYVaTh49CcblvC9v9WOqFH+/JfC2S22VDnDlWyBKwR+9HLtxE/W58RDz?=\n\t=?utf-8?q?33ylYpxJFtJ6l0wY2McDg02PbTN4fwCoXOC13K5YS62ZtC26n80hKdKO?=\n\t=?utf-8?q?/OTjLI6BPD5a0GJboVn+Yko1SOc2PlKpVrzVXPxiQoXcC69cm/uO+Q/l?=\n\t=?utf-8?q?SVmIF0DQHzpF37WWDCmP1OmQxJJzaktFMgYhgdHTT/ZLwVeNZO4qsGQs?=\n\t=?utf-8?q?fOHxO6nQy84zUKxK6W3oNw0KHcycbVjF2hdUo4fRxWO7iExxUEHJ19KK?=\n\t=?utf-8?q?bsAchyvj7FhjqjEdct8xK6LHyfIF1J+Dim695rgFOVQ0YaVtjbtM/trL?=\n\t=?utf-8?q?FZNjAPlcViOYGDuY24SQVYz5rfiio72jlDx5imWw1PqeKinRjX1LM+yi?=\n\t=?utf-8?q?Zl9QgaxvRUvTOXUsY/nBEm3Vm4rpvFJ231nSFLMLZE2OtlaA4qVrlFcg?=\n\t=?utf-8?q?x/3/VnRACN6n4IZIU+fsDvx93imAZOdnlFvkfnPTDzFIlE8mj/IuFg/H?=\n\t=?utf-8?q?yb5PVZSbfs7GhjbP/v2MeldbDkw0+SOVy9VDiCho4a9AjoqRFs9ruGVx?=\n\t=?utf-8?q?lvsDuRtB1zQAEEgRPrii5TAtahEl9LCRTJqHyX3Sk08BKbgfHXYLjY0Z?=\n\t=?utf-8?q?7bB7B9x++jM2d/R/gvvOVvQrqJHVA2zg/Qd9VCTjFCcmB+0hSlKCuPIw?=\n\t=?utf-8?q?Zf7wH4iADlK3mmMGdTEzY1AL0Mq6mkxsKc5cJl8QoQEiZ2VfAH6E6Rjp?=\n\t=?utf-8?q?59vKMrIlyyQoUsjrlComi/AwTzQXNbd2+Pdvp1anTMN3LtDKtJDWQL6S?=\n\t=?utf-8?q?a/BG6LOP5dSnmk1N69tHo7LAOlBuQJNWRBLBx6awdKyW/rdE2csyxDa5?=\n\t=?utf-8?q?tO2N5uFjKQYbZT0D6Ej+J8FJQNV2csTsmgW+DbizHfxEeEMDeI3r/SkC?=\n\t=?utf-8?q?8eUI6aPxRZNvSL/aR3yLJHJaz3KkKqPKe7V3hKGGds2LblEbfBUqhxfo?=\n\t=?utf-8?q?zNmpLi6Yj883OzhxVjS4jq0XTFZ2B7epG8ZS6xoSaMcMFMpTVzieJMwv?=\n\t=?utf-8?q?057ROPrAwuBae+Rn0vj5g7p3jp44yScPB6HIK3Cstk/st8rF10eFRkGm?=\n\t=?utf-8?q?VvbIhJCzNQ4xkxRF1CB5gR86CsPsoB/FeUQidC/VBu3FlVzyZ2WMBdTx?=\n\t=?utf-8?q?bdr+v1evwI4bbJJ3OXR/hmP8R2lbZUfpSkhXoa/wFlOG6UDI+jH/YDSJ?=\n\t=?utf-8?q?+m7/tfGtOloSHJrtqSgO/6V3gB09LVpu6cpOud2pExESUphfL3sZBtDO?=\n\t=?utf-8?q?tvmSo2BtmdyzR3gcsaEC4n85nqZ6HdLYLemPn3K8GtGgmVGyj7JK2U5I?=\n\t=?utf-8?q?hwaPt24eDdPzD/rtzToaOV+QKO9pIrMkgULiE2r6yMtCUUFpmI1NJq7F?=\n\t=?utf-8?q?NHa4aKFvWjRazyr/ewxtCPLk9Ebrlmd6+1BlQoCBW72xY645NbyC2C+6?=\n\t=?utf-8?q?OBy+Is/ycwyfwB1FUJoe8MTRnalsYKSz6H5ua7AP6ndwZdRNvFQ5scHI?=\n\t=?utf-8?q?qprWipq/kbW2vp8NmMY1rMc8ZvXSQdZRDTc5xsLmG0G2EpSF6uqP/51W?=\n\t=?utf-8?q?HHKI3fhcBHYpjZCHLT4a1Prpx9N25ejRvGC0Pgouih5APaHifAy4bKVG?=\n\t=?utf-8?q?u+Dldx1?=","X-OriginatorOrg":"amlogic.com","X-MS-Exchange-CrossTenant-Network-Message-Id":"c2aa3151-b624-4841-55b3-08dd45dacb21","X-MS-Exchange-CrossTenant-AuthSource":"TYSPR03MB8627.apcprd03.prod.outlook.com","X-MS-Exchange-CrossTenant-AuthAs":"Internal","X-MS-Exchange-CrossTenant-OriginalArrivalTime":"05 Feb 2025 11:46:57.3875\n\t(UTC)","X-MS-Exchange-CrossTenant-FromEntityHeader":"Hosted","X-MS-Exchange-CrossTenant-Id":"0df2add9-25ca-4b3a-acb4-c99ddf0b1114","X-MS-Exchange-CrossTenant-MailboxType":"HOSTED","X-MS-Exchange-CrossTenant-UserPrincipalName":"IYLJgh+aWyiToDx8rVsj+vKcgaM4H4yMB5ZkVCozEVq/NMXZET2How9SGyJ0+I1aeag+pRMsBUbXjDUpppZ+iA==","X-MS-Exchange-Transport-CrossTenantHeadersStamped":"JH0PR03MB8187","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>"}}]