@@ -9,39 +9,74 @@
#include <libcamera/base/utils.h>
-#include "libcamera/internal/value_node.h"
-
namespace libcamera {
namespace ipa::mali_c55::algorithms {
LOG_DEFINE_CATEGORY(MaliC55Lsc)
-int Lsc::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData)
+/* Gain values in [1, 5] range */
+static constexpr unsigned int kMeshScale = 6;
+
+/* Mali-C55 hw supports configurable mesh sizes; we fix it to 32. */
+static constexpr unsigned int kMeshSize = 32;
+static constexpr unsigned int kGridSize = kMeshSize * kMeshSize;
+
+/* Per-colour component page offsets in the mesh table. */
+static constexpr unsigned int kRedOffset = 0;
+static constexpr unsigned int kGreenOffset = 1024;
+static constexpr unsigned int kBlueOffset = 2048;
+
+/*
+ * \todo Mali-C55 supports up to 4 colour temperatures.
+ *
+ * Unfortunately the uAPI only expose MALI_C55_NUM_MESH_SHADING_ELEMENTS (3072)
+ * gain elements, which correspond to three pages of 1024 (32x32) entries.
+ */
+static constexpr unsigned int kMaxColourTemperature = 3;
+
+/*
+ * The LSC algorithm implementation only supports 32x32 grids. Create a list of
+ * positions from the grid size.
+ */
+std::vector<double> Lsc::segmentsToPosition() const
{
- if (!tuningData.contains("meshScale")) {
- LOG(MaliC55Lsc, Error) << "meshScale missing from tuningData";
- return -EINVAL;
- }
+ std::vector<double> positions(kMeshSize);
+ for (double i = 0.0; i < kMeshSize; ++i)
+ positions[i] = i / (kMeshSize - 1);
- meshScale_ = tuningData["meshScale"].get<uint32_t>(0);
+ return positions;
+}
- const ValueNode &yamlSets = tuningData["sets"];
- if (!yamlSets.isList()) {
- LOG(MaliC55Lsc, Error) << "LSC tables missing or invalid";
- return -EINVAL;
- }
+int Lsc::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData)
+{
+ gridPos_ = segmentsToPosition();
+
+ return lscAlgo_.init(tuningData, context.ctrlMap, {
+ .keys = { "r", "g", "b" },
+ .numHCells = kMeshSize,
+ .numVCells = kMeshSize,
+ .sensorSize = context.sensorInfo.activeAreaSize
+ });
+}
- size_t tableSize = 0;
- const auto &sets = yamlSets.asList();
- for (const auto &yamlSet : sets) {
- uint32_t ct = yamlSet["ct"].get<uint32_t>(0);
+int Lsc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)
+{
+ int ret = lscAlgo_.configure(context.activeState.lsc, configInfo.analogCrop,
+ gridPos_, gridPos_);
+ if (ret)
+ return ret;
- if (!ct) {
- LOG(MaliC55Lsc, Error) << "Invalid colour temperature";
- return -EINVAL;
- }
+ /* Re-initialize the mesh tables and reserve space for enough entries. */
+ mesh_ = std::vector<uint32_t>(kGridSize * kMaxColourTemperature);
+ colourTemperatures_.clear();
+ /*
+ * Get the lsc tables per colour components and populate mesh_ with
+ * their content.
+ */
+ const auto &components = lscAlgo_.getComponents();
+ for (auto const &[ct, component] : components) {
if (std::count(colourTemperatures_.begin(),
colourTemperatures_.end(), ct)) {
LOG(MaliC55Lsc, Error)
@@ -49,44 +84,29 @@ int Lsc::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData)
return -EINVAL;
}
- std::vector<uint8_t> rTable =
- yamlSet["r"].get<std::vector<uint8_t>>().value_or(utils::defopt);
- std::vector<uint8_t> gTable =
- yamlSet["g"].get<std::vector<uint8_t>>().value_or(utils::defopt);
- std::vector<uint8_t> bTable =
- yamlSet["b"].get<std::vector<uint8_t>>().value_or(utils::defopt);
-
- /*
- * Some validation to do; only 16x16 and 32x32 tables of
- * coefficients are acceptable, and all tables across all of the
- * sets must be the same size. The first time we encounter a
- * table we check that it is an acceptable size and if so make
- * sure all other tables are of equal size.
- */
- if (!tableSize) {
- if (rTable.size() != 256 && rTable.size() != 1024) {
- LOG(MaliC55Lsc, Error)
- << "Invalid table size for colour temperature " << ct;
- return -EINVAL;
- }
- tableSize = rTable.size();
- }
+ std::vector<uint8_t> rTable = component.at("r");
+ std::vector<uint8_t> gTable = component.at("g");
+ std::vector<uint8_t> bTable = component.at("b");
- if (rTable.size() != tableSize ||
- gTable.size() != tableSize ||
- bTable.size() != tableSize) {
+ /* Only 32x32 tables of coefficients are accepted. */
+ if (rTable.size() != kGridSize || gTable.size() != kGridSize ||
+ bTable.size() != kGridSize) {
LOG(MaliC55Lsc, Error)
- << "Invalid or mismatched table size for colour temperature " << ct;
+ << "Invalid table size for colour temperature " << ct;
return -EINVAL;
}
- if (colourTemperatures_.size() >= 3) {
+ if (colourTemperatures_.size() >= kMaxColourTemperature) {
LOG(MaliC55Lsc, Error)
<< "A maximum of 3 colour temperatures are supported";
return -EINVAL;
}
- for (unsigned int i = 0; i < tableSize; i++) {
+ /*
+ * Create the mesh table entries by assembling up to 3 gains per
+ * colour temperature in a u32 word.
+ */
+ for (unsigned int i = 0; i < kGridSize; i++) {
mesh_[kRedOffset + i] |=
(rTable[i] << (colourTemperatures_.size() * 8));
mesh_[kGreenOffset + i] |=
@@ -98,34 +118,35 @@ int Lsc::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData)
colourTemperatures_.push_back(ct);
}
- /*
- * The mesh has either 16x16 or 32x32 nodes, we tell the driver which it
- * is based on the number of values in the tuning data's table.
- */
- if (tableSize == 256)
- meshSize_ = 15;
- else
- meshSize_ = 31;
-
return 0;
}
-void Lsc::fillConfigParamsBlock(MaliC55Params *params) const
+/**
+ * \copydoc libcamera::ipa::Algorithm::queueRequest
+ */
+void Lsc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext, const ControlList &controls)
+{
+ lscAlgo_.queueRequest(context.activeState.lsc, frameContext.lsc,
+ controls);
+}
+
+void Lsc::programMeshTables(MaliC55Params *params) const
{
auto block = params->block<MaliC55Blocks::MeshShadingConfig>();
block->mesh_show = false;
- block->mesh_scale = meshScale_;
+ block->mesh_scale = kMeshScale;
block->mesh_page_r = 0;
block->mesh_page_g = 1;
block->mesh_page_b = 2;
- block->mesh_width = meshSize_;
- block->mesh_height = meshSize_;
+ block->mesh_width = kMeshSize - 1;
+ block->mesh_height = kMeshSize - 1;
std::copy(mesh_.begin(), mesh_.end(), block->mesh);
}
-void Lsc::fillSelectionParamsBlock(MaliC55Params *params, uint8_t bank,
+void Lsc::configureMeshBlending(MaliC55Params *params, uint8_t bank,
uint8_t alpha) const
{
auto block = params->block<MaliC55Blocks::MeshShadingSel>();
@@ -187,7 +208,7 @@ void Lsc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
std::tie(bank, alpha) = findBankAndAlpha(temperatureK);
}
- fillSelectionParamsBlock(params, bank, alpha);
+ configureMeshBlending(params, bank, alpha);
if (frame > 0)
return;
@@ -196,7 +217,19 @@ void Lsc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
* If this is the first frame, we need to load the parsed coefficient
* tables from tuning data to the ISP.
*/
- fillConfigParamsBlock(params);
+ programMeshTables(params);
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::process
+ */
+void Lsc::process([[maybe_unused]] IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext,
+ [[maybe_unused]] const mali_c55_stats_buffer *stats,
+ ControlList &metadata)
+{
+ lscAlgo_.process(frameContext.lsc, metadata);
}
REGISTER_IPA_ALGORITHM(Lsc, "Lsc")
@@ -5,10 +5,19 @@
* Mali-C55 Lens shading correction algorithm
*/
-#include <map>
+#include <vector>
#include <tuple>
+#include <linux/media/arm/mali-c55-config.h>
+
+#include "libcamera/internal/value_node.h"
+
+#include "libipa/fixedpoint.h"
+#include "libipa/lsc.h"
+
#include "algorithm.h"
+#include "ipa_context.h"
+#include "params.h"
namespace libcamera {
@@ -21,23 +30,30 @@ public:
~Lsc() = default;
int init(IPAContext &context, const ValueNode &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,
MaliC55Params *params) override;
+ void process(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const mali_c55_stats_buffer *stats,
+ ControlList &metadata) override;
private:
- static constexpr unsigned int kRedOffset = 0;
- static constexpr unsigned int kGreenOffset = 1024;
- static constexpr unsigned int kBlueOffset = 2048;
-
- void fillConfigParamsBlock(MaliC55Params *params) const;
- void fillSelectionParamsBlock(MaliC55Params *params,
- uint8_t bank, uint8_t alpha) const;
+ std::vector<double> segmentsToPosition() const;
+ void programMeshTables(MaliC55Params *params) const;
+ void configureMeshBlending(MaliC55Params *params,
+ uint8_t bank, uint8_t alpha) const;
std::tuple<uint8_t, uint8_t> findBankAndAlpha(uint32_t ct) const;
- std::vector<uint32_t> mesh_ = std::vector<uint32_t>(3072);
std::vector<uint32_t> colourTemperatures_;
- uint32_t meshScale_;
- uint32_t meshSize_;
+ std::vector<uint32_t> mesh_;
+
+ std::vector<double> gridPos_;
+
+ LscAlgorithm<uint8_t, UQ<2, 6>> lscAlgo_;
};
} /* namespace ipa::mali_c55::algorithms */
@@ -20,6 +20,7 @@
#include "libipa/awb.h"
#include "libipa/ccm.h"
#include "libipa/fixedpoint.h"
+#include "libipa/lsc.h"
namespace libcamera {
@@ -64,6 +65,8 @@ struct IPAActiveState {
ipa::awb::ActiveState awb;
ipa::ccm::ActiveState ccm;
+
+ ipa::lsc::ActiveState lsc;
};
struct IPAFrameContext : public FrameContext {
@@ -76,6 +79,8 @@ struct IPAFrameContext : public FrameContext {
ipa::awb::FrameContext awb;
ipa::ccm::FrameContext ccm;
+
+ ipa::lsc::FrameContext lsc;
};
struct IPAContext {
Port the Mali-C55 LSC algorithm to use libIPA LscAlgorithm. Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> --- src/ipa/mali-c55/algorithms/lsc.cpp | 163 ++++++++++++++++++++++-------------- src/ipa/mali-c55/algorithms/lsc.h | 38 ++++++--- src/ipa/mali-c55/ipa_context.h | 5 ++ 3 files changed, 130 insertions(+), 76 deletions(-)