[{"id":32582,"web_url":"https://patchwork.libcamera.org/comment/32582/","msgid":"<173349310963.3135963.8962259011871799814@ping.linuxembedded.co.uk>","date":"2024-12-06T13:51:49","subject":"Re: [PATCH v4 05/11] ipa: mali-c55: Add Mali-C55 ISP IPA module","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Daniel Scally (2024-11-15 12:25:34)\n> Add a barebones IPA module for the Mali-C55 ISP. In this initial\n> implementation pretty much only buffer plumbing is implemented.\n> \n> Acked-by: Nayden Kanchev  <nayden.kanchev@arm.com>\n> Co-developed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n> Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>\n> ---\n> Changes in v4:\n> \n>         - None\n> \n> Changes in v3:\n> \n>         - Fixed some parameter naming inconsistency\n> \n> Changes in v2:\n> \n>         - None\n> \n>  Documentation/Doxyfile-common.in        |   1 +\n>  include/libcamera/ipa/mali-c55.mojom    |  34 +++\n>  include/libcamera/ipa/meson.build       |   1 +\n>  meson_options.txt                       |   2 +-\n>  src/ipa/mali-c55/algorithms/algorithm.h |  39 +++\n>  src/ipa/mali-c55/algorithms/meson.build |   4 +\n>  src/ipa/mali-c55/data/meson.build       |   8 +\n>  src/ipa/mali-c55/data/uncalibrated.yaml |   6 +\n>  src/ipa/mali-c55/ipa_context.cpp        | 101 +++++++\n>  src/ipa/mali-c55/ipa_context.h          |  42 +++\n>  src/ipa/mali-c55/mali-c55.cpp           | 339 ++++++++++++++++++++++++\n>  src/ipa/mali-c55/meson.build            |  33 +++\n>  src/ipa/mali-c55/module.h               |  27 ++\n>  13 files changed, 636 insertions(+), 1 deletion(-)\n>  create mode 100644 include/libcamera/ipa/mali-c55.mojom\n>  create mode 100644 src/ipa/mali-c55/algorithms/algorithm.h\n>  create mode 100644 src/ipa/mali-c55/algorithms/meson.build\n>  create mode 100644 src/ipa/mali-c55/data/meson.build\n>  create mode 100644 src/ipa/mali-c55/data/uncalibrated.yaml\n>  create mode 100644 src/ipa/mali-c55/ipa_context.cpp\n>  create mode 100644 src/ipa/mali-c55/ipa_context.h\n>  create mode 100644 src/ipa/mali-c55/mali-c55.cpp\n>  create mode 100644 src/ipa/mali-c55/meson.build\n>  create mode 100644 src/ipa/mali-c55/module.h\n> \n> diff --git a/Documentation/Doxyfile-common.in b/Documentation/Doxyfile-common.in\n> index a70aee43..045c19dd 100644\n> --- a/Documentation/Doxyfile-common.in\n> +++ b/Documentation/Doxyfile-common.in\n> @@ -32,6 +32,7 @@ RECURSIVE              = YES\n>  EXCLUDE_PATTERNS       = @TOP_BUILDDIR@/include/libcamera/ipa/*_serializer.h \\\n>                           @TOP_BUILDDIR@/include/libcamera/ipa/*_proxy.h \\\n>                           @TOP_BUILDDIR@/include/libcamera/ipa/ipu3_*.h \\\n> +                         @TOP_BUILDDIR@/include/libcamera/ipa/mali-c55_*.h \\\n>                           @TOP_BUILDDIR@/include/libcamera/ipa/raspberrypi_*.h \\\n>                           @TOP_BUILDDIR@/include/libcamera/ipa/rkisp1_*.h \\\n>                           @TOP_BUILDDIR@/include/libcamera/ipa/vimc_*.h\n> diff --git a/include/libcamera/ipa/mali-c55.mojom b/include/libcamera/ipa/mali-c55.mojom\n> new file mode 100644\n> index 00000000..5d7eb4ee\n> --- /dev/null\n> +++ b/include/libcamera/ipa/mali-c55.mojom\n> @@ -0,0 +1,34 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +\n> +module ipa.mali_c55;\n> +\n> +import \"include/libcamera/ipa/core.mojom\";\n> +\n> +struct IPAConfigInfo {\n> +       libcamera.IPACameraSensorInfo sensorInfo;\n> +       libcamera.ControlInfoMap sensorControls;\n> +};\n> +\n> +interface IPAMaliC55Interface {\n> +       init(libcamera.IPASettings settings, IPAConfigInfo configInfo)\n> +               => (int32 ret, libcamera.ControlInfoMap ipaControls);\n> +       start() => (int32 ret);\n> +       stop();\n> +\n> +       configure(IPAConfigInfo configInfo, uint8 bayerOrder)\n> +               => (int32 ret, libcamera.ControlInfoMap ipaControls);\n> +\n> +       mapBuffers(array<libcamera.IPABuffer> buffers, bool readOnly);\n> +       unmapBuffers(array<libcamera.IPABuffer> buffers);\n> +\n> +       [async] queueRequest(uint32 request, libcamera.ControlList reqControls);\n> +       [async] fillParams(uint32 request, uint32 bufferId);\n> +       [async] processStats(uint32 request, uint32 bufferId,\n> +                            libcamera.ControlList sensorControls);\n> +};\n> +\n> +interface IPAMaliC55EventInterface {\n> +       paramsComputed(uint32 request);\n> +       statsProcessed(uint32 request, libcamera.ControlList metadata);\n> +       setSensorControls(libcamera.ControlList sensorControls);\n> +};\n> diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build\n> index bf55e124..3129f119 100644\n> --- a/include/libcamera/ipa/meson.build\n> +++ b/include/libcamera/ipa/meson.build\n> @@ -64,6 +64,7 @@ libcamera_ipa_headers += custom_target('core_ipa_serializer_h',\n>  # Mapping from pipeline handler name to mojom file\n>  pipeline_ipa_mojom_mapping = {\n>      'ipu3': 'ipu3.mojom',\n> +    'mali-c55': 'mali-c55.mojom',\n>      'rkisp1': 'rkisp1.mojom',\n>      'rpi/vc4': 'raspberrypi.mojom',\n>      'simple': 'soft.mojom',\n> diff --git a/meson_options.txt b/meson_options.txt\n> index 7aa41249..5365601c 100644\n> --- a/meson_options.txt\n> +++ b/meson_options.txt\n> @@ -32,7 +32,7 @@ option('gstreamer',\n>  \n>  option('ipas',\n>          type : 'array',\n> -        choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'],\n> +        choices : ['ipu3', 'mali-c55', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'],\n>          description : 'Select which IPA modules to build')\n>  \n>  option('lc-compliance',\n> diff --git a/src/ipa/mali-c55/algorithms/algorithm.h b/src/ipa/mali-c55/algorithms/algorithm.h\n> new file mode 100644\n> index 00000000..36a3bff0\n> --- /dev/null\n> +++ b/src/ipa/mali-c55/algorithms/algorithm.h\n> @@ -0,0 +1,39 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2024, Ideas On Board\n> + *\n> + * algorithm.h - Mali-C55 control algorithm interface\n> + */\n> +\n> +#pragma once\n> +\n> +#include <linux/mali-c55-config.h>\n> +\n> +#include <libipa/algorithm.h>\n> +\n> +#include \"module.h\"\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa::mali_c55 {\n> +\n> +class Algorithm : public libcamera::ipa::Algorithm<Module>\n> +{\n> +};\n> +\n> +union mali_c55_params_block {\n> +       struct mali_c55_params_block_header *header;\n> +       struct mali_c55_params_sensor_off_preshading *sensor_offs;\n> +       struct mali_c55_params_aexp_hist *aexp_hist;\n> +       struct mali_c55_params_aexp_weights *aexp_weights;\n> +       struct mali_c55_params_digital_gain *digital_gain;\n> +       struct mali_c55_params_awb_gains *awb_gains;\n> +       struct mali_c55_params_awb_config *awb_config;\n> +       struct mali_c55_params_mesh_shading_config *shading_config;\n> +       struct mali_c55_params_mesh_shading_selection *shading_selection;\n> +       __u8 *data;\n> +};\n> +\n> +} /* namespace ipa::mali_c55 */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/mali-c55/algorithms/meson.build b/src/ipa/mali-c55/algorithms/meson.build\n> new file mode 100644\n> index 00000000..f2203b15\n> --- /dev/null\n> +++ b/src/ipa/mali-c55/algorithms/meson.build\n> @@ -0,0 +1,4 @@\n> +# SPDX-License-Identifier: CC0-1.0\n> +\n> +mali_c55_ipa_algorithms = files([\n> +])\n> diff --git a/src/ipa/mali-c55/data/meson.build b/src/ipa/mali-c55/data/meson.build\n> new file mode 100644\n> index 00000000..88109d17\n> --- /dev/null\n> +++ b/src/ipa/mali-c55/data/meson.build\n> @@ -0,0 +1,8 @@\n> +# SPDX-License-Identifier: CC0-1.0\n> +\n> +conf_files = files([\n> +    'uncalibrated.yaml'\n> +])\n> +\n> +install_data(conf_files,\n> +             install_dir : ipa_data_dir / 'mali-c55')\n> diff --git a/src/ipa/mali-c55/data/uncalibrated.yaml b/src/ipa/mali-c55/data/uncalibrated.yaml\n> new file mode 100644\n> index 00000000..2cdc39a8\n> --- /dev/null\n> +++ b/src/ipa/mali-c55/data/uncalibrated.yaml\n> @@ -0,0 +1,6 @@\n> +# SPDX-License-Identifier: CC0-1.0\n> +%YAML 1.1\n> +---\n> +version: 1\n> +algorithms:\n> +...\n> diff --git a/src/ipa/mali-c55/ipa_context.cpp b/src/ipa/mali-c55/ipa_context.cpp\n> new file mode 100644\n> index 00000000..99f76ecd\n> --- /dev/null\n> +++ b/src/ipa/mali-c55/ipa_context.cpp\n> @@ -0,0 +1,101 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2024, Ideas On Board\n> + *\n> + * ipa_context.cpp - MaliC55 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::mali_c55 {\n> +\n> +/**\n> + * \\struct IPASessionConfiguration\n> + * \\brief Session configuration for the IPA module\n> + *\n> + * The session configuration contains all IPA configuration parameters that\n> + * remain constant during the capture session, from IPA module start to stop.\n> + * It is typically set during the configure() operation of the IPA module, but\n> + * may also be updated in the start() operation.\n> + */\n> +\n> +/**\n> + * \\struct IPAActiveState\n> + * \\brief Active state for algorithms\n> + *\n> + * The active state contains all algorithm-specific data that needs to be\n> + * maintained by algorithms across frames. Unlike the session configuration,\n> + * the active state is mutable and constantly updated by algorithms. The active\n> + * state is accessible through the IPAContext structure.\n> + *\n> + * The active state stores two distinct categories of information:\n> + *\n> + *  - The consolidated value of all algorithm controls. Requests passed to\n> + *    the queueRequest() function store values for controls that the\n> + *    application wants to modify for that particular frame, and the\n> + *    queueRequest() function updates the active state with those values.\n> + *    The active state thus contains a consolidated view of the value of all\n> + *    controls handled by the algorithm.\n> + *\n> + *  - The value of parameters computed by the algorithm when running in auto\n> + *    mode. Algorithms running in auto mode compute new parameters every\n> + *    time statistics buffers are received (either synchronously, or\n> + *    possibly in a background thread). The latest computed value of those\n> + *    parameters is stored in the active state in the process() function.\n> + *\n> + * Each of the members in the active state belongs to a specific algorithm. A\n> + * member may be read by any algorithm, but shall only be written by its owner.\n> + */\n> +\n> +/**\n> + * \\struct IPAFrameContext\n> + * \\brief Per-frame context for algorithms\n> + *\n> + * The frame context stores two distinct categories of information:\n> + *\n> + * - The value of the controls to be applied to the frame. These values are\n> + *   typically set in the queueRequest() function, from the consolidated\n> + *   control values stored in the active state. The frame context thus stores\n> + *   values for all controls related to the algorithm, not limited to the\n> + *   controls specified in the corresponding request, but consolidated from all\n> + *   requests that have been queued so far.\n> + *\n> + *   For controls that can be set manually or computed by an algorithm\n> + *   (depending on the algorithm operation mode), such as for instance the\n> + *   colour gains for the AWB algorithm, the control value will be stored in\n> + *   the frame context in the queueRequest() function only when operating in\n> + *   manual mode. When operating in auto mode, the values are computed by the\n> + *   algorithm in process(), stored in the active state, and copied to the\n> + *   frame context in prepare(), just before being stored in the ISP parameters\n> + *   buffer.\n> + *\n> + *   The queueRequest() function can also store ancillary data in the frame\n> + *   context, such as flags to indicate if (and what) control values have\n> + *   changed compared to the previous request.\n> + *\n> + * - Status information computed by the algorithm for a frame. For instance,\n> + *   the colour temperature estimated by the AWB algorithm from ISP statistics\n> + *   calculated on a frame is stored in the frame context for that frame in\n> + *   the process() function.\n> + */\n> +\n> +/**\n> + * \\struct IPAContext\n> + * \\brief Global IPA context data shared between all algorithms\n> + *\n> + * \\var IPAContext::configuration\n> + * \\brief The IPA session configuration, immutable during the session\n> + *\n> + * \\var IPAContext::activeState\n> + * \\brief The IPA active state, storing the latest state for all algorithms\n> + *\n> + * \\var IPAContext::frameContexts\n> + * \\brief Ring buffer of per-frame contexts\n> + */\n> +\n> +} /* namespace libcamera::ipa::mali_c55 */\n> diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h\n> new file mode 100644\n> index 00000000..9e408a17\n> --- /dev/null\n> +++ b/src/ipa/mali-c55/ipa_context.h\n> @@ -0,0 +1,42 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2024, Ideas On Board\n> + *\n> + * ipa_context.h - Mali-C55 IPA Context\n> + */\n> +\n> +#pragma once\n> +\n> +#include <libcamera/controls.h>\n> +\n> +#include <libipa/fc_queue.h>\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa::mali_c55 {\n> +\n> +struct IPASessionConfiguration {\n> +};\n> +\n> +struct IPAActiveState {\n> +};\n> +\n> +struct IPAFrameContext : public FrameContext {\n> +       struct {\n> +               uint32_t exposure;\n> +               double sensorGain;\n> +       } agc;\n> +};\n> +\n> +struct IPAContext {\n> +       IPASessionConfiguration configuration;\n> +       IPAActiveState activeState;\n> +\n> +       FCQueue<IPAFrameContext> frameContexts;\n> +\n> +       ControlInfoMap::Map ctrlMap;\n> +};\n> +\n> +} /* namespace ipa::mali_c55 */\n> +\n> +} /* namespace libcamera*/\n> diff --git a/src/ipa/mali-c55/mali-c55.cpp b/src/ipa/mali-c55/mali-c55.cpp\n> new file mode 100644\n> index 00000000..7efc0124\n> --- /dev/null\n> +++ b/src/ipa/mali-c55/mali-c55.cpp\n> @@ -0,0 +1,339 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2023, Ideas on Board Oy\n> + *\n> + * mali-c55.cpp - Mali-C55 ISP image processing algorithms\n> + */\n> +\n> +#include <map>\n> +#include <string.h>\n> +#include <vector>\n> +\n> +#include <linux/mali-c55-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/ipa/ipa_interface.h>\n> +#include <libcamera/ipa/ipa_module_info.h>\n> +#include <libcamera/ipa/mali-c55_ipa_interface.h>\n> +\n> +#include \"libcamera/internal/bayer_format.h\"\n> +#include \"libcamera/internal/mapped_framebuffer.h\"\n> +#include \"libcamera/internal/yaml_parser.h\"\n> +\n> +#include \"algorithms/algorithm.h\"\n> +#include \"libipa/camera_sensor_helper.h\"\n> +\n> +#include \"ipa_context.h\"\n> +\n> +namespace libcamera {\n> +\n> +LOG_DEFINE_CATEGORY(IPAMaliC55)\n> +\n> +namespace ipa::mali_c55 {\n> +\n> +/* Maximum number of frame contexts to be held */\n> +static constexpr uint32_t kMaxFrameContexts = 16;\n> +\n> +class IPAMaliC55 : public IPAMaliC55Interface, public Module\n> +{\n> +public:\n> +       IPAMaliC55();\n> +\n> +       int init(const IPASettings &settings, const IPAConfigInfo &ipaConfig,\n> +                ControlInfoMap *ipaControls) override;\n> +       int start() override;\n> +       void stop() override;\n> +       int configure(const IPAConfigInfo &ipaConfig, uint8_t bayerOrder,\n> +                     ControlInfoMap *ipaControls) override;\n> +       void mapBuffers(const std::vector<IPABuffer> &buffers, bool readOnly) override;\n> +       void unmapBuffers(const std::vector<IPABuffer> &buffers) override;\n> +       void queueRequest(const uint32_t request, const ControlList &controls) override;\n> +       void fillParams(unsigned int request, uint32_t bufferId) override;\n> +       void processStats(unsigned int request, unsigned int bufferId,\n> +                         const ControlList &sensorControls) 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();\n> +\n> +       std::map<unsigned int, MappedFrameBuffer> buffers_;\n> +\n> +       ControlInfoMap sensorControls_;\n> +\n> +       /* Interface to the Camera Helper */\n> +       std::unique_ptr<CameraSensorHelper> camHelper_;\n> +\n> +       /* Local parameter storage */\n> +       struct IPAContext context_;\n> +};\n> +\n> +namespace {\n> +\n> +} /* namespace */\n> +\n> +IPAMaliC55::IPAMaliC55()\n> +       : context_({ {}, {}, { kMaxFrameContexts }, {} })\n> +{\n> +}\n\nThis is the only quibble I'd have (but can be updated on top if you\nprefer, if we don't need another version).\n\n\nSee https://patchwork.libcamera.org/patch/22093/\n\n\n> +\n> +std::string IPAMaliC55::logPrefix() const\n> +{\n> +       return \"mali-c55\";\n> +}\n> +\n> +int IPAMaliC55::init(const IPASettings &settings, const IPAConfigInfo &ipaConfig,\n> +                    ControlInfoMap *ipaControls)\n> +{\n> +       camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);\n> +       if (!camHelper_) {\n> +               LOG(IPAMaliC55, Error)\n> +                       << \"Failed to create camera sensor helper for \"\n> +                       << settings.sensorModel;\n> +               return -ENODEV;\n> +       }\n> +\n> +       File file(settings.configurationFile);\n> +       if (!file.open(File::OpenModeFlag::ReadOnly)) {\n> +               int ret = file.error();\n> +               LOG(IPAMaliC55, Error)\n> +                       << \"Failed to open configuration file \"\n> +                       << settings.configurationFile << \": \" << strerror(-ret);\n> +               return ret;\n> +       }\n> +\n> +       std::unique_ptr<libcamera::YamlObject> data = YamlParser::parse(file);\n> +       if (!data)\n> +               return -EINVAL;\n> +\n> +       if (!data->contains(\"algorithms\")) {\n> +               LOG(IPAMaliC55, 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(ipaConfig.sensorInfo, ipaConfig.sensorControls, ipaControls);\n> +\n> +       return 0;\n> +}\n> +\n> +void IPAMaliC55::setControls()\n> +{\n> +       ControlList ctrls(sensorControls_);\n> +\n> +       setSensorControls.emit(ctrls);\n> +}\n> +\n> +int IPAMaliC55::start()\n> +{\n> +       return 0;\n> +}\n> +\n> +void IPAMaliC55::stop()\n> +{\n> +       context_.frameContexts.clear();\n> +}\n> +\n> +void IPAMaliC55::updateControls(const IPACameraSensorInfo &sensorInfo,\n> +                               const ControlInfoMap &sensorControls,\n> +                               ControlInfoMap *ipaControls)\n> +{\n> +       ControlInfoMap::Map ctrlMap;\n> +\n> +       /*\n> +        * Compute the frame duration limits.\n> +        *\n> +        * The frame length is computed assuming a fixed line length combined\n> +        * with the vertical frame sizes.\n> +        */\n> +       const ControlInfo &v4l2HBlank = 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 = sensorControls.find(V4L2_CID_VBLANK)->second;\n> +       std::array<uint32_t, 3> frameHeights{\n> +               v4l2VBlank.min().get<int32_t>() + sensorInfo.outputSize.height,\n> +               v4l2VBlank.max().get<int32_t>() + sensorInfo.outputSize.height,\n> +               v4l2VBlank.def().get<int32_t>() + 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 / 1000000U);\n> +       }\n> +\n> +       ctrlMap[&controls::FrameDurationLimits] = ControlInfo(frameDurations[0],\n> +                                                             frameDurations[1],\n> +                                                             frameDurations[2]);\n> +\n> +       /*\n> +        * Compute exposure time limits from the V4L2_CID_EXPOSURE control\n> +        * limits and the line duration.\n> +        */\n> +       double lineDuration = sensorInfo.minLineLength / sensorInfo.pixelRate;\n> +\n> +       const ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second;\n> +       int32_t minExposure = v4l2Exposure.min().get<int32_t>() * lineDuration;\n> +       int32_t maxExposure = v4l2Exposure.max().get<int32_t>() * lineDuration;\n> +       int32_t defExposure = v4l2Exposure.def().get<int32_t>() * lineDuration;\n> +       ctrlMap[&controls::ExposureTime] = ControlInfo(minExposure, maxExposure, defExposure);\n> +\n> +       /* Compute the analogue gain limits. */\n> +       const ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;\n> +       float minGain = camHelper_->gain(v4l2Gain.min().get<int32_t>());\n> +       float maxGain = camHelper_->gain(v4l2Gain.max().get<int32_t>());\n> +       float defGain = camHelper_->gain(v4l2Gain.def().get<int32_t>());\n> +       ctrlMap[&controls::AnalogueGain] = ControlInfo(minGain, maxGain, defGain);\n\nIf all of that is the same as every other libipa IPA then I think theres\nsome opportunity to refactor into a helper here, or otherwise into the\ncommon AEGC components ... but can be on top still.\n\n\n> +\n> +       /*\n> +        * Merge in any controls that we support either statically or from the\n> +        * algorithms.\n> +        */\n> +       ctrlMap.merge(context_.ctrlMap);\n> +\n> +       *ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);\n> +}\n> +\n> +int IPAMaliC55::configure(const IPAConfigInfo &ipaConfig,\n> +                         [[maybe_unused]] uint8_t bayerOrder,\n> +                         ControlInfoMap *ipaControls)\n> +{\n> +       sensorControls_ = ipaConfig.sensorControls;\n> +\n> +       /* Clear the IPA context before the streaming session. */\n> +       context_.configuration = {};\n> +       context_.activeState = {};\n> +       context_.frameContexts.clear();\n> +\n> +       const IPACameraSensorInfo &info = ipaConfig.sensorInfo;\n> +\n> +       updateControls(info, ipaConfig.sensorControls, ipaControls);\n> +\n> +       for (auto const &a : algorithms()) {\n> +               Algorithm *algo = static_cast<Algorithm *>(a.get());\n> +\n> +               int ret = algo->configure(context_, info);\n> +               if (ret)\n> +                       return ret;\n> +       }\n> +\n> +       return 0;\n> +}\n> +\n> +void IPAMaliC55::mapBuffers(const std::vector<IPABuffer> &buffers, bool readOnly)\n> +{\n> +       for (const IPABuffer &buffer : buffers) {\n> +               const FrameBuffer fb(buffer.planes);\n\nI think the MappedFrameBuffer should probably be split out here and\nerror checked at time of mapping, to at least report a warning/Error\nmessage, but we're in a void function ... hrm.\n\n\nProbably something to add to a todo list ?\n\nBut we don't expect these to fail...\n\n\nAcked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n> +               buffers_.emplace(\n> +                       buffer.id,\n> +                       MappedFrameBuffer(\n> +                               &fb,\n> +                               readOnly ? MappedFrameBuffer::MapFlag::Read\n> +                                        : MappedFrameBuffer::MapFlag::ReadWrite));\n> +       }\n> +}\n> +\n> +void IPAMaliC55::unmapBuffers(const std::vector<IPABuffer> &buffers)\n> +{\n> +       for (const IPABuffer &buffer : buffers) {\n> +               auto it = buffers_.find(buffer.id);\n> +               if (it == buffers_.end())\n> +                       continue;\n> +\n> +               buffers_.erase(buffer.id);\n> +       }\n> +}\n> +\n> +void IPAMaliC55::queueRequest(const uint32_t request, const ControlList &controls)\n> +{\n> +       IPAFrameContext &frameContext = context_.frameContexts.alloc(request);\n> +\n> +       for (auto const &a : algorithms()) {\n> +               Algorithm *algo = static_cast<Algorithm *>(a.get());\n> +\n> +               algo->queueRequest(context_, request, frameContext, controls);\n> +       }\n> +}\n> +\n> +void IPAMaliC55::fillParams(unsigned int request,\n> +                           [[maybe_unused]] uint32_t bufferId)\n> +{\n> +       struct mali_c55_params_buffer *params;\n> +       IPAFrameContext &frameContext = context_.frameContexts.get(request);\n> +\n> +       params = reinterpret_cast<mali_c55_params_buffer *>(\n> +               buffers_.at(bufferId).planes()[0].data());\n> +       memset(params, 0, sizeof(mali_c55_params_buffer));\n> +\n> +       params->version = MALI_C55_PARAM_BUFFER_V1;\n> +\n> +       for (auto const &algo : algorithms()) {\n> +               algo->prepare(context_, request, frameContext, params);\n> +\n> +               ASSERT(params->total_size <= MALI_C55_PARAMS_MAX_SIZE);\n> +       }\n> +\n> +       paramsComputed.emit(request);\n> +}\n> +\n> +void IPAMaliC55::processStats(unsigned int request, unsigned int bufferId,\n> +                             const ControlList &sensorControls)\n> +{\n> +       IPAFrameContext &frameContext = context_.frameContexts.get(request);\n> +       const mali_c55_stats_buffer *stats = nullptr;\n> +\n> +       stats = reinterpret_cast<mali_c55_stats_buffer *>(\n> +               buffers_.at(bufferId).planes()[0].data());\n> +\n> +       frameContext.agc.exposure =\n> +               sensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>();\n> +       frameContext.agc.sensorGain =\n> +               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> +\n> +               algo->process(context_, request, frameContext, stats, metadata);\n> +       }\n> +\n> +       setControls();\n> +\n> +       statsProcessed.emit(request, metadata);\n> +}\n> +\n> +} /* namespace ipa::mali_c55 */\n> +\n> +/*\n> + * External IPA module interface\n> + */\n> +extern \"C\" {\n> +const struct IPAModuleInfo ipaModuleInfo = {\n> +       IPA_MODULE_API_VERSION,\n> +       1,\n> +       \"mali-c55\",\n> +       \"mali-c55\",\n> +};\n> +\n> +IPAInterface *ipaCreate()\n> +{\n> +       return new ipa::mali_c55::IPAMaliC55();\n> +}\n> +\n> +} /* extern \"C\" */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/mali-c55/meson.build b/src/ipa/mali-c55/meson.build\n> new file mode 100644\n> index 00000000..864d90ec\n> --- /dev/null\n> +++ b/src/ipa/mali-c55/meson.build\n> @@ -0,0 +1,33 @@\n> +# SPDX-License-Identifier: CC0-1.0\n> +\n> +subdir('algorithms')\n> +subdir('data')\n> +\n> +ipa_name = 'ipa_mali_c55'\n> +\n> +mali_c55_ipa_sources = files([\n> +    'ipa_context.cpp',\n> +    'mali-c55.cpp'\n> +])\n> +\n> +mali_c55_ipa_sources += mali_c55_ipa_algorithms\n> +\n> +mod = shared_module(ipa_name,\n> +                    mali_c55_ipa_sources,\n> +                    name_prefix : '',\n> +                    include_directories : [ipa_includes, libipa_includes],\n> +                    dependencies : libcamera_private,\n> +                    link_with : libipa,\n> +                    install : true,\n> +                    install_dir : ipa_install_dir)\n> +\n> +if ipa_sign_module\n> +    custom_target(ipa_name + '.so.sign',\n> +                  input : mod,\n> +                  output : ipa_name + '.so.sign',\n> +                  command : [ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@'],\n> +                  install : false,\n> +                  build_by_default : true)\n> +endif\n> +\n> +ipa_names += ipa_name\n> diff --git a/src/ipa/mali-c55/module.h b/src/ipa/mali-c55/module.h\n> new file mode 100644\n> index 00000000..1d85ec1f\n> --- /dev/null\n> +++ b/src/ipa/mali-c55/module.h\n> @@ -0,0 +1,27 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2024, Ideas On Board\n> + *\n> + * module.h - Mali-C55 IPA Module\n> + */\n> +\n> +#pragma once\n> +\n> +#include <linux/mali-c55-config.h>\n> +\n> +#include <libcamera/ipa/mali-c55_ipa_interface.h>\n> +\n> +#include <libipa/module.h>\n> +\n> +#include \"ipa_context.h\"\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa::mali_c55 {\n> +\n> +using Module = ipa::Module<IPAContext, IPAFrameContext, IPACameraSensorInfo,\n> +                          mali_c55_params_buffer, mali_c55_stats_buffer>;\n> +\n> +} /* namespace ipa::mali_c55 */\n> +\n> +} /* namespace libcamera*/\n> -- \n> 2.30.2\n>","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 D5B82BF415\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  6 Dec 2024 13:51:55 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 3D6306614D;\n\tFri,  6 Dec 2024 14:51:55 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 443A9618B3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  6 Dec 2024 14:51:53 +0100 (CET)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 8D167641;\n\tFri,  6 Dec 2024 14:51:23 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"QDfHghK4\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1733493083;\n\tbh=ktReXDmGNPzdTvJ23B7nrCzLcyOqeQGyF+bt4OCV25I=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=QDfHghK4lxqBPm2X9yEtEQ6ZAFqh0mnW1OZmU6ewPmI+SC4jE+TnqhiEp3YIZ0d5g\n\tBO/VFSR2Nh245ZQ0VJc2/23wEgg9joBJZJ+y+vLy9+l0pc4EtzFmP8sOSyYfXqcNNW\n\thzf52IxNBrYGUGoPQI7Weom/D8J66pAE63owD2O0=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20241115122540.478103-6-dan.scally@ideasonboard.com>","References":"<20241115122540.478103-1-dan.scally@ideasonboard.com>\n\t<20241115122540.478103-6-dan.scally@ideasonboard.com>","Subject":"Re: [PATCH v4 05/11] ipa: mali-c55: Add Mali-C55 ISP IPA module","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"Anthony.McGivern@arm.com, Daniel Scally <dan.scally@ideasonboard.com>,\n\tNayden Kanchev <nayden.kanchev@arm.com>,\n\tJacopo Mondi <jacopo.mondi@ideasonboard.com>","To":"Daniel Scally <dan.scally@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Fri, 06 Dec 2024 13:51:49 +0000","Message-ID":"<173349310963.3135963.8962259011871799814@ping.linuxembedded.co.uk>","User-Agent":"alot/0.10","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>"}},{"id":32586,"web_url":"https://patchwork.libcamera.org/comment/32586/","msgid":"<b2aba0e6-ddd4-435a-9bc3-f94b68c7b438@ideasonboard.com>","date":"2024-12-06T14:14:58","subject":"Re: [PATCH v4 05/11] ipa: mali-c55: Add Mali-C55 ISP IPA module","submitter":{"id":156,"url":"https://patchwork.libcamera.org/api/people/156/","name":"Dan Scally","email":"dan.scally@ideasonboard.com"},"content":"Hi Kieran - thanks for the review\n\nOn 06/12/2024 13:51, Kieran Bingham wrote:\n> Quoting Daniel Scally (2024-11-15 12:25:34)\n>> Add a barebones IPA module for the Mali-C55 ISP. In this initial\n>> implementation pretty much only buffer plumbing is implemented.\n>>\n>> Acked-by: Nayden Kanchev  <nayden.kanchev@arm.com>\n>> Co-developed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n>> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n>> Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>\n>> ---\n>> Changes in v4:\n>>\n>>          - None\n>>\n>> Changes in v3:\n>>\n>>          - Fixed some parameter naming inconsistency\n>>\n>> Changes in v2:\n>>\n>>          - None\n>>\n>>   Documentation/Doxyfile-common.in        |   1 +\n>>   include/libcamera/ipa/mali-c55.mojom    |  34 +++\n>>   include/libcamera/ipa/meson.build       |   1 +\n>>   meson_options.txt                       |   2 +-\n>>   src/ipa/mali-c55/algorithms/algorithm.h |  39 +++\n>>   src/ipa/mali-c55/algorithms/meson.build |   4 +\n>>   src/ipa/mali-c55/data/meson.build       |   8 +\n>>   src/ipa/mali-c55/data/uncalibrated.yaml |   6 +\n>>   src/ipa/mali-c55/ipa_context.cpp        | 101 +++++++\n>>   src/ipa/mali-c55/ipa_context.h          |  42 +++\n>>   src/ipa/mali-c55/mali-c55.cpp           | 339 ++++++++++++++++++++++++\n>>   src/ipa/mali-c55/meson.build            |  33 +++\n>>   src/ipa/mali-c55/module.h               |  27 ++\n>>   13 files changed, 636 insertions(+), 1 deletion(-)\n>>   create mode 100644 include/libcamera/ipa/mali-c55.mojom\n>>   create mode 100644 src/ipa/mali-c55/algorithms/algorithm.h\n>>   create mode 100644 src/ipa/mali-c55/algorithms/meson.build\n>>   create mode 100644 src/ipa/mali-c55/data/meson.build\n>>   create mode 100644 src/ipa/mali-c55/data/uncalibrated.yaml\n>>   create mode 100644 src/ipa/mali-c55/ipa_context.cpp\n>>   create mode 100644 src/ipa/mali-c55/ipa_context.h\n>>   create mode 100644 src/ipa/mali-c55/mali-c55.cpp\n>>   create mode 100644 src/ipa/mali-c55/meson.build\n>>   create mode 100644 src/ipa/mali-c55/module.h\n>>\n>> diff --git a/Documentation/Doxyfile-common.in b/Documentation/Doxyfile-common.in\n>> index a70aee43..045c19dd 100644\n>> --- a/Documentation/Doxyfile-common.in\n>> +++ b/Documentation/Doxyfile-common.in\n>> @@ -32,6 +32,7 @@ RECURSIVE              = YES\n>>   EXCLUDE_PATTERNS       = @TOP_BUILDDIR@/include/libcamera/ipa/*_serializer.h \\\n>>                            @TOP_BUILDDIR@/include/libcamera/ipa/*_proxy.h \\\n>>                            @TOP_BUILDDIR@/include/libcamera/ipa/ipu3_*.h \\\n>> +                         @TOP_BUILDDIR@/include/libcamera/ipa/mali-c55_*.h \\\n>>                            @TOP_BUILDDIR@/include/libcamera/ipa/raspberrypi_*.h \\\n>>                            @TOP_BUILDDIR@/include/libcamera/ipa/rkisp1_*.h \\\n>>                            @TOP_BUILDDIR@/include/libcamera/ipa/vimc_*.h\n>> diff --git a/include/libcamera/ipa/mali-c55.mojom b/include/libcamera/ipa/mali-c55.mojom\n>> new file mode 100644\n>> index 00000000..5d7eb4ee\n>> --- /dev/null\n>> +++ b/include/libcamera/ipa/mali-c55.mojom\n>> @@ -0,0 +1,34 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +\n>> +module ipa.mali_c55;\n>> +\n>> +import \"include/libcamera/ipa/core.mojom\";\n>> +\n>> +struct IPAConfigInfo {\n>> +       libcamera.IPACameraSensorInfo sensorInfo;\n>> +       libcamera.ControlInfoMap sensorControls;\n>> +};\n>> +\n>> +interface IPAMaliC55Interface {\n>> +       init(libcamera.IPASettings settings, IPAConfigInfo configInfo)\n>> +               => (int32 ret, libcamera.ControlInfoMap ipaControls);\n>> +       start() => (int32 ret);\n>> +       stop();\n>> +\n>> +       configure(IPAConfigInfo configInfo, uint8 bayerOrder)\n>> +               => (int32 ret, libcamera.ControlInfoMap ipaControls);\n>> +\n>> +       mapBuffers(array<libcamera.IPABuffer> buffers, bool readOnly);\n>> +       unmapBuffers(array<libcamera.IPABuffer> buffers);\n>> +\n>> +       [async] queueRequest(uint32 request, libcamera.ControlList reqControls);\n>> +       [async] fillParams(uint32 request, uint32 bufferId);\n>> +       [async] processStats(uint32 request, uint32 bufferId,\n>> +                            libcamera.ControlList sensorControls);\n>> +};\n>> +\n>> +interface IPAMaliC55EventInterface {\n>> +       paramsComputed(uint32 request);\n>> +       statsProcessed(uint32 request, libcamera.ControlList metadata);\n>> +       setSensorControls(libcamera.ControlList sensorControls);\n>> +};\n>> diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build\n>> index bf55e124..3129f119 100644\n>> --- a/include/libcamera/ipa/meson.build\n>> +++ b/include/libcamera/ipa/meson.build\n>> @@ -64,6 +64,7 @@ libcamera_ipa_headers += custom_target('core_ipa_serializer_h',\n>>   # Mapping from pipeline handler name to mojom file\n>>   pipeline_ipa_mojom_mapping = {\n>>       'ipu3': 'ipu3.mojom',\n>> +    'mali-c55': 'mali-c55.mojom',\n>>       'rkisp1': 'rkisp1.mojom',\n>>       'rpi/vc4': 'raspberrypi.mojom',\n>>       'simple': 'soft.mojom',\n>> diff --git a/meson_options.txt b/meson_options.txt\n>> index 7aa41249..5365601c 100644\n>> --- a/meson_options.txt\n>> +++ b/meson_options.txt\n>> @@ -32,7 +32,7 @@ option('gstreamer',\n>>   \n>>   option('ipas',\n>>           type : 'array',\n>> -        choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'],\n>> +        choices : ['ipu3', 'mali-c55', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'],\n>>           description : 'Select which IPA modules to build')\n>>   \n>>   option('lc-compliance',\n>> diff --git a/src/ipa/mali-c55/algorithms/algorithm.h b/src/ipa/mali-c55/algorithms/algorithm.h\n>> new file mode 100644\n>> index 00000000..36a3bff0\n>> --- /dev/null\n>> +++ b/src/ipa/mali-c55/algorithms/algorithm.h\n>> @@ -0,0 +1,39 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2024, Ideas On Board\n>> + *\n>> + * algorithm.h - Mali-C55 control algorithm interface\n>> + */\n>> +\n>> +#pragma once\n>> +\n>> +#include <linux/mali-c55-config.h>\n>> +\n>> +#include <libipa/algorithm.h>\n>> +\n>> +#include \"module.h\"\n>> +\n>> +namespace libcamera {\n>> +\n>> +namespace ipa::mali_c55 {\n>> +\n>> +class Algorithm : public libcamera::ipa::Algorithm<Module>\n>> +{\n>> +};\n>> +\n>> +union mali_c55_params_block {\n>> +       struct mali_c55_params_block_header *header;\n>> +       struct mali_c55_params_sensor_off_preshading *sensor_offs;\n>> +       struct mali_c55_params_aexp_hist *aexp_hist;\n>> +       struct mali_c55_params_aexp_weights *aexp_weights;\n>> +       struct mali_c55_params_digital_gain *digital_gain;\n>> +       struct mali_c55_params_awb_gains *awb_gains;\n>> +       struct mali_c55_params_awb_config *awb_config;\n>> +       struct mali_c55_params_mesh_shading_config *shading_config;\n>> +       struct mali_c55_params_mesh_shading_selection *shading_selection;\n>> +       __u8 *data;\n>> +};\n>> +\n>> +} /* namespace ipa::mali_c55 */\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/ipa/mali-c55/algorithms/meson.build b/src/ipa/mali-c55/algorithms/meson.build\n>> new file mode 100644\n>> index 00000000..f2203b15\n>> --- /dev/null\n>> +++ b/src/ipa/mali-c55/algorithms/meson.build\n>> @@ -0,0 +1,4 @@\n>> +# SPDX-License-Identifier: CC0-1.0\n>> +\n>> +mali_c55_ipa_algorithms = files([\n>> +])\n>> diff --git a/src/ipa/mali-c55/data/meson.build b/src/ipa/mali-c55/data/meson.build\n>> new file mode 100644\n>> index 00000000..88109d17\n>> --- /dev/null\n>> +++ b/src/ipa/mali-c55/data/meson.build\n>> @@ -0,0 +1,8 @@\n>> +# SPDX-License-Identifier: CC0-1.0\n>> +\n>> +conf_files = files([\n>> +    'uncalibrated.yaml'\n>> +])\n>> +\n>> +install_data(conf_files,\n>> +             install_dir : ipa_data_dir / 'mali-c55')\n>> diff --git a/src/ipa/mali-c55/data/uncalibrated.yaml b/src/ipa/mali-c55/data/uncalibrated.yaml\n>> new file mode 100644\n>> index 00000000..2cdc39a8\n>> --- /dev/null\n>> +++ b/src/ipa/mali-c55/data/uncalibrated.yaml\n>> @@ -0,0 +1,6 @@\n>> +# SPDX-License-Identifier: CC0-1.0\n>> +%YAML 1.1\n>> +---\n>> +version: 1\n>> +algorithms:\n>> +...\n>> diff --git a/src/ipa/mali-c55/ipa_context.cpp b/src/ipa/mali-c55/ipa_context.cpp\n>> new file mode 100644\n>> index 00000000..99f76ecd\n>> --- /dev/null\n>> +++ b/src/ipa/mali-c55/ipa_context.cpp\n>> @@ -0,0 +1,101 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2024, Ideas On Board\n>> + *\n>> + * ipa_context.cpp - MaliC55 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::mali_c55 {\n>> +\n>> +/**\n>> + * \\struct IPASessionConfiguration\n>> + * \\brief Session configuration for the IPA module\n>> + *\n>> + * The session configuration contains all IPA configuration parameters that\n>> + * remain constant during the capture session, from IPA module start to stop.\n>> + * It is typically set during the configure() operation of the IPA module, but\n>> + * may also be updated in the start() operation.\n>> + */\n>> +\n>> +/**\n>> + * \\struct IPAActiveState\n>> + * \\brief Active state for algorithms\n>> + *\n>> + * The active state contains all algorithm-specific data that needs to be\n>> + * maintained by algorithms across frames. Unlike the session configuration,\n>> + * the active state is mutable and constantly updated by algorithms. The active\n>> + * state is accessible through the IPAContext structure.\n>> + *\n>> + * The active state stores two distinct categories of information:\n>> + *\n>> + *  - The consolidated value of all algorithm controls. Requests passed to\n>> + *    the queueRequest() function store values for controls that the\n>> + *    application wants to modify for that particular frame, and the\n>> + *    queueRequest() function updates the active state with those values.\n>> + *    The active state thus contains a consolidated view of the value of all\n>> + *    controls handled by the algorithm.\n>> + *\n>> + *  - The value of parameters computed by the algorithm when running in auto\n>> + *    mode. Algorithms running in auto mode compute new parameters every\n>> + *    time statistics buffers are received (either synchronously, or\n>> + *    possibly in a background thread). The latest computed value of those\n>> + *    parameters is stored in the active state in the process() function.\n>> + *\n>> + * Each of the members in the active state belongs to a specific algorithm. A\n>> + * member may be read by any algorithm, but shall only be written by its owner.\n>> + */\n>> +\n>> +/**\n>> + * \\struct IPAFrameContext\n>> + * \\brief Per-frame context for algorithms\n>> + *\n>> + * The frame context stores two distinct categories of information:\n>> + *\n>> + * - The value of the controls to be applied to the frame. These values are\n>> + *   typically set in the queueRequest() function, from the consolidated\n>> + *   control values stored in the active state. The frame context thus stores\n>> + *   values for all controls related to the algorithm, not limited to the\n>> + *   controls specified in the corresponding request, but consolidated from all\n>> + *   requests that have been queued so far.\n>> + *\n>> + *   For controls that can be set manually or computed by an algorithm\n>> + *   (depending on the algorithm operation mode), such as for instance the\n>> + *   colour gains for the AWB algorithm, the control value will be stored in\n>> + *   the frame context in the queueRequest() function only when operating in\n>> + *   manual mode. When operating in auto mode, the values are computed by the\n>> + *   algorithm in process(), stored in the active state, and copied to the\n>> + *   frame context in prepare(), just before being stored in the ISP parameters\n>> + *   buffer.\n>> + *\n>> + *   The queueRequest() function can also store ancillary data in the frame\n>> + *   context, such as flags to indicate if (and what) control values have\n>> + *   changed compared to the previous request.\n>> + *\n>> + * - Status information computed by the algorithm for a frame. For instance,\n>> + *   the colour temperature estimated by the AWB algorithm from ISP statistics\n>> + *   calculated on a frame is stored in the frame context for that frame in\n>> + *   the process() function.\n>> + */\n>> +\n>> +/**\n>> + * \\struct IPAContext\n>> + * \\brief Global IPA context data shared between all algorithms\n>> + *\n>> + * \\var IPAContext::configuration\n>> + * \\brief The IPA session configuration, immutable during the session\n>> + *\n>> + * \\var IPAContext::activeState\n>> + * \\brief The IPA active state, storing the latest state for all algorithms\n>> + *\n>> + * \\var IPAContext::frameContexts\n>> + * \\brief Ring buffer of per-frame contexts\n>> + */\n>> +\n>> +} /* namespace libcamera::ipa::mali_c55 */\n>> diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h\n>> new file mode 100644\n>> index 00000000..9e408a17\n>> --- /dev/null\n>> +++ b/src/ipa/mali-c55/ipa_context.h\n>> @@ -0,0 +1,42 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2024, Ideas On Board\n>> + *\n>> + * ipa_context.h - Mali-C55 IPA Context\n>> + */\n>> +\n>> +#pragma once\n>> +\n>> +#include <libcamera/controls.h>\n>> +\n>> +#include <libipa/fc_queue.h>\n>> +\n>> +namespace libcamera {\n>> +\n>> +namespace ipa::mali_c55 {\n>> +\n>> +struct IPASessionConfiguration {\n>> +};\n>> +\n>> +struct IPAActiveState {\n>> +};\n>> +\n>> +struct IPAFrameContext : public FrameContext {\n>> +       struct {\n>> +               uint32_t exposure;\n>> +               double sensorGain;\n>> +       } agc;\n>> +};\n>> +\n>> +struct IPAContext {\n>> +       IPASessionConfiguration configuration;\n>> +       IPAActiveState activeState;\n>> +\n>> +       FCQueue<IPAFrameContext> frameContexts;\n>> +\n>> +       ControlInfoMap::Map ctrlMap;\n>> +};\n>> +\n>> +} /* namespace ipa::mali_c55 */\n>> +\n>> +} /* namespace libcamera*/\n>> diff --git a/src/ipa/mali-c55/mali-c55.cpp b/src/ipa/mali-c55/mali-c55.cpp\n>> new file mode 100644\n>> index 00000000..7efc0124\n>> --- /dev/null\n>> +++ b/src/ipa/mali-c55/mali-c55.cpp\n>> @@ -0,0 +1,339 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2023, Ideas on Board Oy\n>> + *\n>> + * mali-c55.cpp - Mali-C55 ISP image processing algorithms\n>> + */\n>> +\n>> +#include <map>\n>> +#include <string.h>\n>> +#include <vector>\n>> +\n>> +#include <linux/mali-c55-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/ipa/ipa_interface.h>\n>> +#include <libcamera/ipa/ipa_module_info.h>\n>> +#include <libcamera/ipa/mali-c55_ipa_interface.h>\n>> +\n>> +#include \"libcamera/internal/bayer_format.h\"\n>> +#include \"libcamera/internal/mapped_framebuffer.h\"\n>> +#include \"libcamera/internal/yaml_parser.h\"\n>> +\n>> +#include \"algorithms/algorithm.h\"\n>> +#include \"libipa/camera_sensor_helper.h\"\n>> +\n>> +#include \"ipa_context.h\"\n>> +\n>> +namespace libcamera {\n>> +\n>> +LOG_DEFINE_CATEGORY(IPAMaliC55)\n>> +\n>> +namespace ipa::mali_c55 {\n>> +\n>> +/* Maximum number of frame contexts to be held */\n>> +static constexpr uint32_t kMaxFrameContexts = 16;\n>> +\n>> +class IPAMaliC55 : public IPAMaliC55Interface, public Module\n>> +{\n>> +public:\n>> +       IPAMaliC55();\n>> +\n>> +       int init(const IPASettings &settings, const IPAConfigInfo &ipaConfig,\n>> +                ControlInfoMap *ipaControls) override;\n>> +       int start() override;\n>> +       void stop() override;\n>> +       int configure(const IPAConfigInfo &ipaConfig, uint8_t bayerOrder,\n>> +                     ControlInfoMap *ipaControls) override;\n>> +       void mapBuffers(const std::vector<IPABuffer> &buffers, bool readOnly) override;\n>> +       void unmapBuffers(const std::vector<IPABuffer> &buffers) override;\n>> +       void queueRequest(const uint32_t request, const ControlList &controls) override;\n>> +       void fillParams(unsigned int request, uint32_t bufferId) override;\n>> +       void processStats(unsigned int request, unsigned int bufferId,\n>> +                         const ControlList &sensorControls) 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();\n>> +\n>> +       std::map<unsigned int, MappedFrameBuffer> buffers_;\n>> +\n>> +       ControlInfoMap sensorControls_;\n>> +\n>> +       /* Interface to the Camera Helper */\n>> +       std::unique_ptr<CameraSensorHelper> camHelper_;\n>> +\n>> +       /* Local parameter storage */\n>> +       struct IPAContext context_;\n>> +};\n>> +\n>> +namespace {\n>> +\n>> +} /* namespace */\n>> +\n>> +IPAMaliC55::IPAMaliC55()\n>> +       : context_({ {}, {}, { kMaxFrameContexts }, {} })\n>> +{\n>> +}\n> This is the only quibble I'd have (but can be updated on top if you\n> prefer, if we don't need another version).\n>\n>\n> See https://patchwork.libcamera.org/patch/22093/\nYeah sure\n>\n>> +\n>> +std::string IPAMaliC55::logPrefix() const\n>> +{\n>> +       return \"mali-c55\";\n>> +}\n>> +\n>> +int IPAMaliC55::init(const IPASettings &settings, const IPAConfigInfo &ipaConfig,\n>> +                    ControlInfoMap *ipaControls)\n>> +{\n>> +       camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);\n>> +       if (!camHelper_) {\n>> +               LOG(IPAMaliC55, Error)\n>> +                       << \"Failed to create camera sensor helper for \"\n>> +                       << settings.sensorModel;\n>> +               return -ENODEV;\n>> +       }\n>> +\n>> +       File file(settings.configurationFile);\n>> +       if (!file.open(File::OpenModeFlag::ReadOnly)) {\n>> +               int ret = file.error();\n>> +               LOG(IPAMaliC55, Error)\n>> +                       << \"Failed to open configuration file \"\n>> +                       << settings.configurationFile << \": \" << strerror(-ret);\n>> +               return ret;\n>> +       }\n>> +\n>> +       std::unique_ptr<libcamera::YamlObject> data = YamlParser::parse(file);\n>> +       if (!data)\n>> +               return -EINVAL;\n>> +\n>> +       if (!data->contains(\"algorithms\")) {\n>> +               LOG(IPAMaliC55, 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(ipaConfig.sensorInfo, ipaConfig.sensorControls, ipaControls);\n>> +\n>> +       return 0;\n>> +}\n>> +\n>> +void IPAMaliC55::setControls()\n>> +{\n>> +       ControlList ctrls(sensorControls_);\n>> +\n>> +       setSensorControls.emit(ctrls);\n>> +}\n>> +\n>> +int IPAMaliC55::start()\n>> +{\n>> +       return 0;\n>> +}\n>> +\n>> +void IPAMaliC55::stop()\n>> +{\n>> +       context_.frameContexts.clear();\n>> +}\n>> +\n>> +void IPAMaliC55::updateControls(const IPACameraSensorInfo &sensorInfo,\n>> +                               const ControlInfoMap &sensorControls,\n>> +                               ControlInfoMap *ipaControls)\n>> +{\n>> +       ControlInfoMap::Map ctrlMap;\n>> +\n>> +       /*\n>> +        * Compute the frame duration limits.\n>> +        *\n>> +        * The frame length is computed assuming a fixed line length combined\n>> +        * with the vertical frame sizes.\n>> +        */\n>> +       const ControlInfo &v4l2HBlank = 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 = sensorControls.find(V4L2_CID_VBLANK)->second;\n>> +       std::array<uint32_t, 3> frameHeights{\n>> +               v4l2VBlank.min().get<int32_t>() + sensorInfo.outputSize.height,\n>> +               v4l2VBlank.max().get<int32_t>() + sensorInfo.outputSize.height,\n>> +               v4l2VBlank.def().get<int32_t>() + 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 / 1000000U);\n>> +       }\n>> +\n>> +       ctrlMap[&controls::FrameDurationLimits] = ControlInfo(frameDurations[0],\n>> +                                                             frameDurations[1],\n>> +                                                             frameDurations[2]);\n>> +\n>> +       /*\n>> +        * Compute exposure time limits from the V4L2_CID_EXPOSURE control\n>> +        * limits and the line duration.\n>> +        */\n>> +       double lineDuration = sensorInfo.minLineLength / sensorInfo.pixelRate;\n>> +\n>> +       const ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second;\n>> +       int32_t minExposure = v4l2Exposure.min().get<int32_t>() * lineDuration;\n>> +       int32_t maxExposure = v4l2Exposure.max().get<int32_t>() * lineDuration;\n>> +       int32_t defExposure = v4l2Exposure.def().get<int32_t>() * lineDuration;\n>> +       ctrlMap[&controls::ExposureTime] = ControlInfo(minExposure, maxExposure, defExposure);\n>> +\n>> +       /* Compute the analogue gain limits. */\n>> +       const ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;\n>> +       float minGain = camHelper_->gain(v4l2Gain.min().get<int32_t>());\n>> +       float maxGain = camHelper_->gain(v4l2Gain.max().get<int32_t>());\n>> +       float defGain = camHelper_->gain(v4l2Gain.def().get<int32_t>());\n>> +       ctrlMap[&controls::AnalogueGain] = ControlInfo(minGain, maxGain, defGain);\n> If all of that is the same as every other libipa IPA then I think theres\n> some opportunity to refactor into a helper here, or otherwise into the\n> common AEGC components ... but can be on top still.\nI will stick this on the to-do list\n>\n>\n>> +\n>> +       /*\n>> +        * Merge in any controls that we support either statically or from the\n>> +        * algorithms.\n>> +        */\n>> +       ctrlMap.merge(context_.ctrlMap);\n>> +\n>> +       *ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);\n>> +}\n>> +\n>> +int IPAMaliC55::configure(const IPAConfigInfo &ipaConfig,\n>> +                         [[maybe_unused]] uint8_t bayerOrder,\n>> +                         ControlInfoMap *ipaControls)\n>> +{\n>> +       sensorControls_ = ipaConfig.sensorControls;\n>> +\n>> +       /* Clear the IPA context before the streaming session. */\n>> +       context_.configuration = {};\n>> +       context_.activeState = {};\n>> +       context_.frameContexts.clear();\n>> +\n>> +       const IPACameraSensorInfo &info = ipaConfig.sensorInfo;\n>> +\n>> +       updateControls(info, ipaConfig.sensorControls, ipaControls);\n>> +\n>> +       for (auto const &a : algorithms()) {\n>> +               Algorithm *algo = static_cast<Algorithm *>(a.get());\n>> +\n>> +               int ret = algo->configure(context_, info);\n>> +               if (ret)\n>> +                       return ret;\n>> +       }\n>> +\n>> +       return 0;\n>> +}\n>> +\n>> +void IPAMaliC55::mapBuffers(const std::vector<IPABuffer> &buffers, bool readOnly)\n>> +{\n>> +       for (const IPABuffer &buffer : buffers) {\n>> +               const FrameBuffer fb(buffer.planes);\n> I think the MappedFrameBuffer should probably be split out here and\n> error checked at time of mapping, to at least report a warning/Error\n> message, but we're in a void function ... hrm.\n>\n>\n> Probably something to add to a todo list ?\n\nSure, it doesn't have to be a void though, we can change that. I suspect this is a similar problem \nfor the other IPA modules too.\n>\n> But we don't expect these to fail...\n>\n>\n> Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n\nThanks\n\n>\n>> +               buffers_.emplace(\n>> +                       buffer.id,\n>> +                       MappedFrameBuffer(\n>> +                               &fb,\n>> +                               readOnly ? MappedFrameBuffer::MapFlag::Read\n>> +                                        : MappedFrameBuffer::MapFlag::ReadWrite));\n>> +       }\n>> +}\n>> +\n>> +void IPAMaliC55::unmapBuffers(const std::vector<IPABuffer> &buffers)\n>> +{\n>> +       for (const IPABuffer &buffer : buffers) {\n>> +               auto it = buffers_.find(buffer.id);\n>> +               if (it == buffers_.end())\n>> +                       continue;\n>> +\n>> +               buffers_.erase(buffer.id);\n>> +       }\n>> +}\n>> +\n>> +void IPAMaliC55::queueRequest(const uint32_t request, const ControlList &controls)\n>> +{\n>> +       IPAFrameContext &frameContext = context_.frameContexts.alloc(request);\n>> +\n>> +       for (auto const &a : algorithms()) {\n>> +               Algorithm *algo = static_cast<Algorithm *>(a.get());\n>> +\n>> +               algo->queueRequest(context_, request, frameContext, controls);\n>> +       }\n>> +}\n>> +\n>> +void IPAMaliC55::fillParams(unsigned int request,\n>> +                           [[maybe_unused]] uint32_t bufferId)\n>> +{\n>> +       struct mali_c55_params_buffer *params;\n>> +       IPAFrameContext &frameContext = context_.frameContexts.get(request);\n>> +\n>> +       params = reinterpret_cast<mali_c55_params_buffer *>(\n>> +               buffers_.at(bufferId).planes()[0].data());\n>> +       memset(params, 0, sizeof(mali_c55_params_buffer));\n>> +\n>> +       params->version = MALI_C55_PARAM_BUFFER_V1;\n>> +\n>> +       for (auto const &algo : algorithms()) {\n>> +               algo->prepare(context_, request, frameContext, params);\n>> +\n>> +               ASSERT(params->total_size <= MALI_C55_PARAMS_MAX_SIZE);\n>> +       }\n>> +\n>> +       paramsComputed.emit(request);\n>> +}\n>> +\n>> +void IPAMaliC55::processStats(unsigned int request, unsigned int bufferId,\n>> +                             const ControlList &sensorControls)\n>> +{\n>> +       IPAFrameContext &frameContext = context_.frameContexts.get(request);\n>> +       const mali_c55_stats_buffer *stats = nullptr;\n>> +\n>> +       stats = reinterpret_cast<mali_c55_stats_buffer *>(\n>> +               buffers_.at(bufferId).planes()[0].data());\n>> +\n>> +       frameContext.agc.exposure =\n>> +               sensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>();\n>> +       frameContext.agc.sensorGain =\n>> +               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>> +\n>> +               algo->process(context_, request, frameContext, stats, metadata);\n>> +       }\n>> +\n>> +       setControls();\n>> +\n>> +       statsProcessed.emit(request, metadata);\n>> +}\n>> +\n>> +} /* namespace ipa::mali_c55 */\n>> +\n>> +/*\n>> + * External IPA module interface\n>> + */\n>> +extern \"C\" {\n>> +const struct IPAModuleInfo ipaModuleInfo = {\n>> +       IPA_MODULE_API_VERSION,\n>> +       1,\n>> +       \"mali-c55\",\n>> +       \"mali-c55\",\n>> +};\n>> +\n>> +IPAInterface *ipaCreate()\n>> +{\n>> +       return new ipa::mali_c55::IPAMaliC55();\n>> +}\n>> +\n>> +} /* extern \"C\" */\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/ipa/mali-c55/meson.build b/src/ipa/mali-c55/meson.build\n>> new file mode 100644\n>> index 00000000..864d90ec\n>> --- /dev/null\n>> +++ b/src/ipa/mali-c55/meson.build\n>> @@ -0,0 +1,33 @@\n>> +# SPDX-License-Identifier: CC0-1.0\n>> +\n>> +subdir('algorithms')\n>> +subdir('data')\n>> +\n>> +ipa_name = 'ipa_mali_c55'\n>> +\n>> +mali_c55_ipa_sources = files([\n>> +    'ipa_context.cpp',\n>> +    'mali-c55.cpp'\n>> +])\n>> +\n>> +mali_c55_ipa_sources += mali_c55_ipa_algorithms\n>> +\n>> +mod = shared_module(ipa_name,\n>> +                    mali_c55_ipa_sources,\n>> +                    name_prefix : '',\n>> +                    include_directories : [ipa_includes, libipa_includes],\n>> +                    dependencies : libcamera_private,\n>> +                    link_with : libipa,\n>> +                    install : true,\n>> +                    install_dir : ipa_install_dir)\n>> +\n>> +if ipa_sign_module\n>> +    custom_target(ipa_name + '.so.sign',\n>> +                  input : mod,\n>> +                  output : ipa_name + '.so.sign',\n>> +                  command : [ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@'],\n>> +                  install : false,\n>> +                  build_by_default : true)\n>> +endif\n>> +\n>> +ipa_names += ipa_name\n>> diff --git a/src/ipa/mali-c55/module.h b/src/ipa/mali-c55/module.h\n>> new file mode 100644\n>> index 00000000..1d85ec1f\n>> --- /dev/null\n>> +++ b/src/ipa/mali-c55/module.h\n>> @@ -0,0 +1,27 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2024, Ideas On Board\n>> + *\n>> + * module.h - Mali-C55 IPA Module\n>> + */\n>> +\n>> +#pragma once\n>> +\n>> +#include <linux/mali-c55-config.h>\n>> +\n>> +#include <libcamera/ipa/mali-c55_ipa_interface.h>\n>> +\n>> +#include <libipa/module.h>\n>> +\n>> +#include \"ipa_context.h\"\n>> +\n>> +namespace libcamera {\n>> +\n>> +namespace ipa::mali_c55 {\n>> +\n>> +using Module = ipa::Module<IPAContext, IPAFrameContext, IPACameraSensorInfo,\n>> +                          mali_c55_params_buffer, mali_c55_stats_buffer>;\n>> +\n>> +} /* namespace ipa::mali_c55 */\n>> +\n>> +} /* namespace libcamera*/\n>> -- \n>> 2.30.2\n>>","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 84B77BE173\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  6 Dec 2024 14:15:04 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 3810A6615F;\n\tFri,  6 Dec 2024 15:15:04 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B3D8C66152\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  6 Dec 2024 15:15:01 +0100 (CET)","from [192.168.0.43]\n\t(cpc141996-chfd3-2-0-cust928.12-3.cable.virginm.net [86.13.91.161])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 0C0EE641;\n\tFri,  6 Dec 2024 15:14:32 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"WPzXhPWO\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1733494472;\n\tbh=d5+OWhBfwlh70GXkEIrYFFy5qvistKx9ydyU92rybOU=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=WPzXhPWOcBmUBlF3p0q+FIFEJvmqUk47CEZ7I1nJ0VI/cn4acTFNDCwbAUHmiZs40\n\tl6c5/zQMaTMiuz07tS3URxZ/eRZqx2uuXsZpyaI4cm1UIUtCjGZqc57SpJb9PQjhN1\n\tdxt9tHScHPzt+5OAgy1Vx3HWsfwYQz27S5XdLLeI=","Message-ID":"<b2aba0e6-ddd4-435a-9bc3-f94b68c7b438@ideasonboard.com>","Date":"Fri, 6 Dec 2024 14:14:58 +0000","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v4 05/11] ipa: mali-c55: Add Mali-C55 ISP IPA module","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Cc":"Anthony.McGivern@arm.com, Nayden Kanchev <nayden.kanchev@arm.com>,\n\tJacopo Mondi <jacopo.mondi@ideasonboard.com>","References":"<20241115122540.478103-1-dan.scally@ideasonboard.com>\n\t<20241115122540.478103-6-dan.scally@ideasonboard.com>\n\t<173349310963.3135963.8962259011871799814@ping.linuxembedded.co.uk>","Content-Language":"en-US","From":"Dan Scally <dan.scally@ideasonboard.com>","Autocrypt":"addr=dan.scally@ideasonboard.com; keydata=\n\t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","In-Reply-To":"<173349310963.3135963.8962259011871799814@ping.linuxembedded.co.uk>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","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>"}}]