[PATCH 07/10] ipa: mali-c55: Add Agc algorithm
Daniel Scally
dan.scally at ideasonboard.com
Thu Jun 13 15:25:59 CEST 2024
Add a new algorithm and associated infrastructure for Agc. The
tuning files for uncalibrated sensors is extended to enable the
algorithm.
Acked-by: Nayden Kanchev <nayden.kanchev at arm.com>
Co-developed-by: Jacopo Mondi <jacopo.mondi at ideasonboard.com>
Signed-off-by: Jacopo Mondi <jacopo.mondi at ideasonboard.com>
Signed-off-by: Daniel Scally <dan.scally at ideasonboard.com>
---
src/ipa/mali-c55/algorithms/agc.cpp | 450 ++++++++++++++++++++++++
src/ipa/mali-c55/algorithms/agc.h | 82 +++++
src/ipa/mali-c55/algorithms/meson.build | 1 +
src/ipa/mali-c55/data/uncalibrated.yaml | 1 +
src/ipa/mali-c55/ipa_context.h | 32 ++
src/ipa/mali-c55/mali-c55.cpp | 54 ++-
6 files changed, 618 insertions(+), 2 deletions(-)
create mode 100644 src/ipa/mali-c55/algorithms/agc.cpp
create mode 100644 src/ipa/mali-c55/algorithms/agc.h
diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp
new file mode 100644
index 00000000..77908641
--- /dev/null
+++ b/src/ipa/mali-c55/algorithms/agc.cpp
@@ -0,0 +1,450 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board Oy
+ *
+ * agc.cpp - AGC/AEC mean-based control algorithm
+ */
+
+#include "agc.h"
+
+#include <math.h>
+
+#include <libcamera/base/log.h>
+
+#include <libcamera/control_ids.h>
+#include <libcamera/property_ids.h>
+
+namespace libcamera {
+
+using namespace std::literals::chrono_literals;
+
+namespace ipa::mali_c55::algorithms {
+
+LOG_DEFINE_CATEGORY(MaliC55Agc)
+
+/*
+ * Number of histogram bins. This is only true for the specific configuration we
+ * set to the ISP; 4 separate histograms of 256 bins each. If that configuration
+ * ever changes then this constant will need updating.
+ */
+static constexpr unsigned int kNumHistogramBins = 256;
+
+/*
+ * The Mali-C55 ISP has a digital gain block which allows setting gain in Q5.8
+ * format, a range of 0.0 to (very nearly) 32.0. We clamp from 1.0 to the actual
+ * max value.
+ */
+static constexpr double kMinDigitalGain = 1.0;
+static constexpr double kMaxDigitalGain = 8191 * pow(2, -8);
+
+uint32_t AgcStatistics::decodeBinValue(uint16_t binVal)
+{
+ int exponent = (binVal & 0xf000) >> 12;
+ int mantissa = binVal & 0xfff;
+
+ if (!exponent)
+ return mantissa * 2;
+ else
+ return (mantissa + 4096) * pow(2, exponent);
+}
+
+/*
+ * We configure the ISP to give us 4 histograms of 256 bins each, with
+ * a single histogram per colour channel (R/Gr/Gb/B). The memory space
+ * containing the data is a single block containing all 4 histograms
+ * with the position of each colour's histogram within it dependent on
+ * the bayer pattern of the data input to the ISP.
+ *
+ * NOTE: The validity of this function depends on the parameters we have
+ * configured. With different skip/offset x, y values not all of the
+ * colour channels would be populated, and they may not be in the same
+ * planes as calculated here.
+ */
+int AgcStatistics::setBayerOrderIndices(BayerFormat::Order bayerOrder)
+{
+ switch (bayerOrder) {
+ case BayerFormat::Order::RGGB:
+ rIndex_ = 0;
+ grIndex_ = 1;
+ gbIndex_ = 2;
+ bIndex_ = 3;
+ break;
+ case BayerFormat::Order::GRBG:
+ grIndex_ = 0;
+ rIndex_ = 1;
+ bIndex_ = 2;
+ gbIndex_ = 3;
+ break;
+ case BayerFormat::Order::GBRG:
+ gbIndex_ = 0;
+ bIndex_ = 1;
+ rIndex_ = 2;
+ grIndex_ = 3;
+ break;
+ case BayerFormat::Order::BGGR:
+ bIndex_ = 0;
+ gbIndex_ = 1;
+ grIndex_ = 2;
+ rIndex_ = 3;
+ break;
+ default:
+ LOG(MaliC55Agc, Error)
+ << "Invalid bayer format " << bayerOrder;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void AgcStatistics::parseStatistics(const mali_c55_stats_buffer *stats)
+{
+ uint32_t r[256], g[256], b[256], y[256];
+
+ /*
+ * We need to decode the bin values for each histogram from their 16-bit
+ * compressed values to a 32-bit value. We also take the average of the
+ * Gr/Gb values into a single green histogram.
+ */
+ for (unsigned int i = 0; i < 256; i++) {
+ r[i] = decodeBinValue(stats->ae_1024bin_hist.bins[i + (256 * rIndex_)]);
+ g[i] = (decodeBinValue(stats->ae_1024bin_hist.bins[i + (256 * grIndex_)]) +
+ decodeBinValue(stats->ae_1024bin_hist.bins[i + (256 * gbIndex_)])) / 2;
+ b[i] = decodeBinValue(stats->ae_1024bin_hist.bins[i + (256 * bIndex_)]);
+
+ y[i] = (r[i] * .299) + (g[i] * .587) + (b[i] * .114);
+ }
+
+ rHist = Histogram(Span<uint32_t>(r, kNumHistogramBins));
+ gHist = Histogram(Span<uint32_t>(g, kNumHistogramBins));
+ bHist = Histogram(Span<uint32_t>(b, kNumHistogramBins));
+ yHist = Histogram(Span<uint32_t>(y, kNumHistogramBins));
+}
+
+Agc::Agc()
+ : AgcMeanLuminance()
+{
+}
+
+int Agc::init(IPAContext &context, const YamlObject &tuningData)
+{
+ int ret = parseTuningData(tuningData);
+ if (ret)
+ return ret;
+
+ context.ctrlMap[&controls::AeEnable] = ControlInfo(false, true);
+ context.ctrlMap[&controls::DigitalGain] = ControlInfo(
+ static_cast<float>(kMinDigitalGain),
+ static_cast<float>(kMaxDigitalGain),
+ static_cast<float>(kMinDigitalGain)
+ );
+ context.ctrlMap.merge(controls());
+
+ return 0;
+}
+
+int Agc::configure(IPAContext &context,
+ [[maybe_unused]] const IPACameraSensorInfo &configInfo)
+{
+ int ret = statistics_.setBayerOrderIndices(context.configuration.sensor.bayerOrder);
+ if (ret)
+ return ret;
+
+ /*
+ * Defaults; we use whatever the sensor's default exposure is and the
+ * minimum analogue gain. AEGC is _active_ by default.
+ */
+ context.activeState.agc.autoEnabled = true;
+ context.activeState.agc.automatic.sensorGain = context.configuration.agc.minAnalogueGain;
+ context.activeState.agc.automatic.exposure = context.configuration.agc.defaultExposure;
+ context.activeState.agc.automatic.ispGain = kMinDigitalGain;
+ context.activeState.agc.manual.sensorGain = context.configuration.agc.minAnalogueGain;
+ context.activeState.agc.manual.exposure = context.configuration.agc.defaultExposure;
+ context.activeState.agc.manual.ispGain = kMinDigitalGain;
+ context.activeState.agc.constraintMode = constraintModes().begin()->first;
+ context.activeState.agc.exposureMode = exposureModeHelpers().begin()->first;
+
+ /* \todo Run this again when FrameDurationLimits is passed in */
+ setLimits(context.configuration.agc.minShutterSpeed,
+ context.configuration.agc.maxShutterSpeed,
+ context.configuration.agc.minAnalogueGain,
+ context.configuration.agc.maxAnalogueGain);
+
+ resetFrameCount();
+
+ return 0;
+}
+
+void Agc::queueRequest(IPAContext &context, const uint32_t frame,
+ [[maybe_unused]] IPAFrameContext &frameContext,
+ const ControlList &controls)
+{
+ auto &agc = context.activeState.agc;
+
+ const auto &constraintMode = controls.get(controls::AeConstraintMode);
+ agc.constraintMode = constraintMode.value_or(agc.constraintMode);
+
+ const auto &exposureMode = controls.get(controls::AeExposureMode);
+ agc.exposureMode = exposureMode.value_or(agc.exposureMode);
+
+ const auto &agcEnable = controls.get(controls::AeEnable);
+ if (agcEnable && *agcEnable != agc.autoEnabled) {
+ agc.autoEnabled = *agcEnable;
+
+ LOG(MaliC55Agc, Info)
+ << (agc.autoEnabled ? "Enabling" : "Disabling")
+ << " AGC";
+ }
+
+ /*
+ * If the automatic exposure and gain is enabled we have no further work
+ * to do here...
+ */
+ if (agc.autoEnabled)
+ return;
+
+ /*
+ * ...otherwise we need to look for exposure and gain controls and use
+ * those to set the activeState.
+ */
+ const auto &exposure = controls.get(controls::ExposureTime);
+ if (exposure) {
+ agc.manual.exposure = *exposure * 1.0us / context.configuration.sensor.lineDuration;
+
+ LOG(MaliC55Agc, Debug)
+ << "Exposure set to " << agc.manual.exposure
+ << " on request sequence " << frame;
+ }
+
+ const auto &analogueGain = controls.get(controls::AnalogueGain);
+ if (analogueGain) {
+ agc.manual.sensorGain = *analogueGain;
+
+ LOG(MaliC55Agc, Debug)
+ << "Analogue gain set to " << agc.manual.sensorGain
+ << " on request sequence " << frame;
+ }
+
+ const auto &digitalGain = controls.get(controls::DigitalGain);
+ if (digitalGain) {
+ agc.manual.ispGain = *digitalGain;
+
+ LOG(MaliC55Agc, Debug)
+ << "Digital gain set to " << agc.manual.ispGain
+ << " on request sequence " << frame;
+ }
+}
+
+size_t Agc::fillGainParamBlock(IPAContext &context,
+ IPAFrameContext &frameContext,
+ mali_c55_params_block_header *block)
+{
+ IPAActiveState &activeState = context.activeState;
+ double gain;
+
+ if (activeState.agc.autoEnabled)
+ gain = activeState.agc.automatic.ispGain;
+ else
+ gain = activeState.agc.manual.ispGain;
+
+ struct mali_c55_params_digital_gain *digital_gain =
+ reinterpret_cast<struct mali_c55_params_digital_gain *>(block);
+
+ digital_gain->header.type = MALI_C55_PARAM_BLOCK_DIGITAL_GAIN;
+ digital_gain->header.enabled = true;
+ digital_gain->header.size = sizeof(struct mali_c55_params_digital_gain);
+
+ digital_gain->gain = int(gain * pow(2, 8));
+ frameContext.agc.ispGain = gain;
+
+ return sizeof(struct mali_c55_params_digital_gain);
+}
+
+size_t Agc::fillParamsBuffer(mali_c55_params_block_header *block,
+ enum mali_c55_param_block_type type)
+{
+ struct mali_c55_params_aexp_hist *params =
+ reinterpret_cast<struct mali_c55_params_aexp_hist *>(block);
+
+ params->header.type = type;
+ params->header.enabled = true;
+ params->header.size = sizeof(struct mali_c55_params_aexp_hist);
+
+ /* Collect every 3rd pixel horizontally */
+ params->skip_x = 1;
+ /* Start from first column */
+ params->offset_x = 0;
+ /* Collect every pixel vertically */
+ params->skip_y = 0;
+ /* Start from the first row */
+ params->offset_y = 0;
+ /* 1x scaling (i.e. none) */
+ params->scale_bottom = 0;
+ params->scale_top = 0;
+ /* Collect all Bayer planes into 4 separate histograms */
+ params->plane_mode = 1;
+ /* Tap the data immediately after the digital gain block */
+ params->tap_point = MALI_C55_AEXP_HIST_TAP_FS;
+
+ return sizeof(struct mali_c55_params_aexp_hist);
+}
+
+size_t Agc::fillWeightsArrayBuffer(mali_c55_params_block_header *block,
+ enum mali_c55_param_block_type type)
+{
+ struct mali_c55_params_aexp_weights *params =
+ reinterpret_cast<struct mali_c55_params_aexp_weights *>(block);
+
+ params->header.type = type;
+ params->header.enabled = true;
+ params->header.size = sizeof(struct mali_c55_params_aexp_weights);
+
+ /* We use every zone - a 15x15 grid */
+ params->nodes_used_horiz = 15;
+ params->nodes_used_vert = 15;
+
+ /*
+ * We uniformly weight the zones to 1 - this results in the collected
+ * histograms containing a true pixel count, which we can then use to
+ * approximate colour channel averages for the image.
+ */
+ Span<uint8_t> weights{
+ params->zone_weights,
+ MALI_C55_MAX_ZONES
+ };
+ std::fill(weights.begin(), weights.end(), 1);
+
+ return sizeof(struct mali_c55_params_aexp_weights);
+}
+
+void Agc::prepare(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ mali_c55_params_block_header *block)
+{
+ size_t paramSize = fillGainParamBlock(context, frameContext, block);
+
+ if (frame > 0)
+ return;
+
+ char *params = reinterpret_cast<char *>(block);
+ block = reinterpret_cast<mali_c55_params_block_header *>
+ (params + paramSize);
+
+ paramSize += fillParamsBuffer(block, MALI_C55_PARAM_BLOCK_AEXP_HIST);
+ block = reinterpret_cast<mali_c55_params_block_header *>
+ (params + paramSize);
+
+ paramSize += fillWeightsArrayBuffer(block, MALI_C55_PARAM_BLOCK_AEXP_HIST_WEIGHTS);
+ block = reinterpret_cast<mali_c55_params_block_header *>
+ (params + paramSize);
+
+ paramSize += fillParamsBuffer(block, MALI_C55_PARAM_BLOCK_AEXP_IHIST);
+ block = reinterpret_cast<mali_c55_params_block_header *>
+ (params + paramSize);
+
+ fillWeightsArrayBuffer(block, MALI_C55_PARAM_BLOCK_AEXP_IHIST_WEIGHTS);
+}
+
+double Agc::estimateLuminance(const double gain) const
+{
+ double rAvg = statistics_.rHist.interQuantileMean(0, 1) * gain;
+ double gAvg = statistics_.gHist.interQuantileMean(0, 1) * gain;
+ double bAvg = statistics_.bHist.interQuantileMean(0, 1) * gain;
+ double yAvg = (rAvg * .299) + (gAvg * .587) + (bAvg * .114);
+
+ return yAvg / kNumHistogramBins;
+}
+
+/**
+ * The function estimates the correlated color temperature using
+ * from RGB color space input.
+ * In physics and color science, the Planckian locus or black body locus is
+ * the path or locus that the color of an incandescent black body would take
+ * in a particular chromaticity space as the blackbody temperature changes.
+ *
+ * If a narrow range of color temperatures is considered (those encapsulating
+ * daylight being the most practical case) one can approximate the Planckian
+ * locus in order to calculate the CCT in terms of chromaticity coordinates.
+ *
+ * More detailed information can be found in:
+ * https://en.wikipedia.org/wiki/Color_temperature#Approximation
+ */
+uint32_t Agc::estimateCCT() const
+{
+ double red = statistics_.rHist.interQuantileMean(0, 1);
+ double green = statistics_.gHist.interQuantileMean(0, 1);
+ double blue = statistics_.bHist.interQuantileMean(0, 1);
+
+ /* Convert the RGB values to CIE tristimulus values (XYZ) */
+ double X = (-0.14282) * (red) + (1.54924) * (green) + (-0.95641) * (blue);
+ double Y = (-0.32466) * (red) + (1.57837) * (green) + (-0.73191) * (blue);
+ double Z = (-0.68202) * (red) + (0.77073) * (green) + (0.56332) * (blue);
+
+ /* Calculate the normalized chromaticity values */
+ double x = X / (X + Y + Z);
+ double y = Y / (X + Y + Z);
+
+ /* Calculate CCT */
+ double n = (x - 0.3320) / (0.1858 - y);
+ uint32_t ct = 449 * n * n * n + 3525 * n * n + 6823.3 * n + 5520.33;
+
+ LOG(MaliC55Agc, Debug) << "Estimated Colour Temperature: " << ct;
+
+ return ct;
+}
+
+void Agc::process(IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const mali_c55_stats_buffer *stats,
+ [[maybe_unused]] ControlList &metadata)
+{
+ IPASessionConfiguration &configuration = context.configuration;
+ IPAActiveState &activeState = context.activeState;
+
+ if (!stats) {
+ LOG(MaliC55Agc, Error) << "No statistics buffer passed to Agc";
+ return;
+ }
+
+ statistics_.parseStatistics(stats);
+ context.activeState.agc.temperatureK = estimateCCT();
+
+ /*
+ * The Agc algorithm needs to know the effective exposure value that was
+ * applied to the sensor when the statistics were collected.
+ */
+ uint32_t exposure = frameContext.agc.exposure;
+ double analogueGain = frameContext.agc.sensorGain;
+ double digitalGain = frameContext.agc.ispGain;
+ double totalGain = analogueGain * digitalGain;
+ utils::Duration currentShutter = exposure * configuration.sensor.lineDuration;
+ utils::Duration effectiveExposureValue = currentShutter * totalGain;
+
+ utils::Duration shutterTime;
+ double aGain, dGain;
+ std::tie(shutterTime, aGain, dGain) =
+ calculateNewEv(activeState.agc.constraintMode,
+ activeState.agc.exposureMode, statistics_.yHist,
+ effectiveExposureValue);
+
+ dGain = std::clamp(dGain, kMinDigitalGain, kMaxDigitalGain);
+
+ LOG(MaliC55Agc, Debug)
+ << "Divided up shutter, analogue gain and digital gain are "
+ << shutterTime << ", " << aGain << " and " << dGain;
+
+ activeState.agc.automatic.exposure = shutterTime / configuration.sensor.lineDuration;
+ activeState.agc.automatic.sensorGain = aGain;
+ activeState.agc.automatic.ispGain = dGain;
+
+ metadata.set(controls::ExposureTime, currentShutter.get<std::micro>());
+ metadata.set(controls::AnalogueGain, frameContext.agc.sensorGain);
+ metadata.set(controls::DigitalGain, frameContext.agc.ispGain);
+ metadata.set(controls::ColourTemperature, context.activeState.agc.temperatureK);
+}
+
+REGISTER_IPA_ALGORITHM(Agc, "Agc")
+
+} /* namespace ipa::mali_c55::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/mali-c55/algorithms/agc.h b/src/ipa/mali-c55/algorithms/agc.h
new file mode 100644
index 00000000..78619f42
--- /dev/null
+++ b/src/ipa/mali-c55/algorithms/agc.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Ideas on Board Oy
+ *
+ * agc.h - Mali C55 AGC/AEC mean-based control algorithm
+ */
+
+#pragma once
+
+#include <libcamera/base/utils.h>
+
+#include "libcamera/internal/bayer_format.h"
+
+#include "libipa/agc_mean_luminance.h"
+#include "libipa/histogram.h"
+
+#include "algorithm.h"
+#include "ipa_context.h"
+
+namespace libcamera {
+
+namespace ipa::mali_c55::algorithms {
+
+class AgcStatistics
+{
+public:
+ AgcStatistics()
+ {
+ }
+
+ int setBayerOrderIndices(BayerFormat::Order bayerOrder);
+ uint32_t decodeBinValue(uint16_t binVal);
+ void parseStatistics(const mali_c55_stats_buffer *stats);
+
+ Histogram rHist;
+ Histogram gHist;
+ Histogram bHist;
+ Histogram yHist;
+private:
+ unsigned int rIndex_;
+ unsigned int grIndex_;
+ unsigned int gbIndex_;
+ unsigned int bIndex_;
+};
+
+class Agc : public Algorithm, public AgcMeanLuminance
+{
+public:
+ Agc();
+ ~Agc() = default;
+
+ int init(IPAContext &context, const YamlObject &tuningData) override;
+ int configure(IPAContext &context,
+ const IPACameraSensorInfo &configInfo) override;
+ void queueRequest(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const ControlList &controls) override;
+ void prepare(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ mali_c55_params_block_header *block) override;
+ void process(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const mali_c55_stats_buffer *stats,
+ ControlList &metadata) override;
+
+private:
+ double estimateLuminance(const double gain) const override;
+ size_t fillGainParamBlock(IPAContext &context,
+ IPAFrameContext &frameContext,
+ mali_c55_params_block_header *block);
+ size_t fillParamsBuffer(mali_c55_params_block_header *block,
+ enum mali_c55_param_block_type type);
+ size_t fillWeightsArrayBuffer(mali_c55_params_block_header *block,
+ enum mali_c55_param_block_type type);
+ uint32_t estimateCCT() const;
+
+ AgcStatistics statistics_;
+};
+
+} /* namespace ipa::mali_c55::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/mali-c55/algorithms/meson.build b/src/ipa/mali-c55/algorithms/meson.build
index d84432b9..96808431 100644
--- a/src/ipa/mali-c55/algorithms/meson.build
+++ b/src/ipa/mali-c55/algorithms/meson.build
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: CC0-1.0
mali_c55_ipa_algorithms = files([
+ 'agc.cpp',
'blc.cpp',
])
diff --git a/src/ipa/mali-c55/data/uncalibrated.yaml b/src/ipa/mali-c55/data/uncalibrated.yaml
index 2cdc39a8..6dcc0295 100644
--- a/src/ipa/mali-c55/data/uncalibrated.yaml
+++ b/src/ipa/mali-c55/data/uncalibrated.yaml
@@ -3,4 +3,5 @@
---
version: 1
algorithms:
+ - Agc:
...
diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h
index 9e408a17..73a7cd78 100644
--- a/src/ipa/mali-c55/ipa_context.h
+++ b/src/ipa/mali-c55/ipa_context.h
@@ -7,8 +7,11 @@
#pragma once
+#include <libcamera/base/utils.h>
#include <libcamera/controls.h>
+#include "libcamera/internal/bayer_format.h"
+
#include <libipa/fc_queue.h>
namespace libcamera {
@@ -16,15 +19,44 @@ namespace libcamera {
namespace ipa::mali_c55 {
struct IPASessionConfiguration {
+ struct {
+ utils::Duration minShutterSpeed;
+ utils::Duration maxShutterSpeed;
+ uint32_t defaultExposure;
+ double minAnalogueGain;
+ double maxAnalogueGain;
+ } agc;
+
+ struct {
+ BayerFormat::Order bayerOrder;
+ utils::Duration lineDuration;
+ } sensor;
};
struct IPAActiveState {
+ struct {
+ struct {
+ uint32_t exposure;
+ double sensorGain;
+ double ispGain;
+ } automatic;
+ struct {
+ uint32_t exposure;
+ double sensorGain;
+ double ispGain;
+ } manual;
+ bool autoEnabled;
+ uint32_t constraintMode;
+ uint32_t exposureMode;
+ uint32_t temperatureK;
+ } agc;
};
struct IPAFrameContext : public FrameContext {
struct {
uint32_t exposure;
double sensorGain;
+ double ispGain;
} agc;
};
diff --git a/src/ipa/mali-c55/mali-c55.cpp b/src/ipa/mali-c55/mali-c55.cpp
index 2b4910d3..c397e2ef 100644
--- a/src/ipa/mali-c55/mali-c55.cpp
+++ b/src/ipa/mali-c55/mali-c55.cpp
@@ -33,6 +33,8 @@ namespace libcamera {
LOG_DEFINE_CATEGORY(IPAMaliC55)
+using namespace std::literals::chrono_literals;
+
namespace ipa::mali_c55 {
/* Maximum number of frame contexts to be held */
@@ -60,6 +62,9 @@ protected:
std::string logPrefix() const override;
private:
+ void updateSessionConfiguration(const IPACameraSensorInfo &info,
+ const ControlInfoMap &sensorControls,
+ BayerFormat::Order bayerOrder);
void updateControls(const IPACameraSensorInfo &sensorInfo,
const ControlInfoMap &sensorControls,
ControlInfoMap *ipaControls);
@@ -133,7 +138,21 @@ int IPAMaliC55::init(const IPASettings &settings, const IPAConfigInfo &ipaConfig
void IPAMaliC55::setControls()
{
+ IPAActiveState &activeState = context_.activeState;
+ uint32_t exposure;
+ uint32_t gain;
+
+ if (activeState.agc.autoEnabled) {
+ exposure = activeState.agc.automatic.exposure;
+ gain = camHelper_->gainCode(activeState.agc.automatic.sensorGain);
+ } else {
+ exposure = activeState.agc.manual.exposure;
+ gain = camHelper_->gainCode(activeState.agc.manual.sensorGain);
+ }
+
ControlList ctrls(sensorControls_);
+ ctrls.set(V4L2_CID_EXPOSURE, static_cast<int32_t>(exposure));
+ ctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast<int32_t>(gain));
setSensorControls.emit(ctrls);
}
@@ -148,6 +167,36 @@ void IPAMaliC55::stop()
context_.frameContexts.clear();
}
+void IPAMaliC55::updateSessionConfiguration(const IPACameraSensorInfo &info,
+ const ControlInfoMap &sensorControls,
+ BayerFormat::Order bayerOrder)
+{
+ context_.configuration.sensor.bayerOrder = bayerOrder;
+
+ const ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second;
+ int32_t minExposure = v4l2Exposure.min().get<int32_t>();
+ int32_t maxExposure = v4l2Exposure.max().get<int32_t>();
+ int32_t defExposure = v4l2Exposure.def().get<int32_t>();
+
+ const ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;
+ int32_t minGain = v4l2Gain.min().get<int32_t>();
+ int32_t maxGain = v4l2Gain.max().get<int32_t>();
+
+ /*
+ * When the AGC computes the new exposure values for a frame, it needs
+ * to know the limits for shutter speed and analogue gain.
+ * As it depends on the sensor, update it with the controls.
+ *
+ * \todo take VBLANK into account for maximum shutter speed
+ */
+ context_.configuration.sensor.lineDuration = info.minLineLength * 1.0s / info.pixelRate;
+ context_.configuration.agc.minShutterSpeed = minExposure * context_.configuration.sensor.lineDuration;
+ context_.configuration.agc.maxShutterSpeed = maxExposure * context_.configuration.sensor.lineDuration;
+ context_.configuration.agc.defaultExposure = defExposure;
+ context_.configuration.agc.minAnalogueGain = camHelper_->gain(minGain);
+ context_.configuration.agc.maxAnalogueGain = camHelper_->gain(maxGain);
+}
+
void IPAMaliC55::updateControls(const IPACameraSensorInfo &sensorInfo,
const ControlInfoMap &sensorControls,
ControlInfoMap *ipaControls)
@@ -209,8 +258,7 @@ void IPAMaliC55::updateControls(const IPACameraSensorInfo &sensorInfo,
*ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);
}
-int IPAMaliC55::configure(const IPAConfigInfo &ipaConfig,
- [[maybe_unused]] uint8_t bayerOrder,
+int IPAMaliC55::configure(const IPAConfigInfo &ipaConfig, uint8_t bayerOrder,
ControlInfoMap *ipaControls)
{
sensorControls_ = ipaConfig.sensorControls;
@@ -222,6 +270,8 @@ int IPAMaliC55::configure(const IPAConfigInfo &ipaConfig,
const IPACameraSensorInfo &info = ipaConfig.sensorInfo;
+ updateSessionConfiguration(info, ipaConfig.sensorControls,
+ static_cast<BayerFormat::Order>(bayerOrder));
updateControls(info, ipaConfig.sensorControls, ipaControls);
for (auto const &a : algorithms()) {
--
2.30.2
More information about the libcamera-devel
mailing list