[11/11] ipa: mali-c55: Port to use LscAlgorithm
diff mbox series

Message ID 20260615-libipa-algorithms-v1-11-e949c937422e@ideasonboard.com
State New
Headers show
Series
  • ipa: libipa: Introduce libipa algorithms
Related show

Commit Message

Jacopo Mondi June 15, 2026, 2:05 p.m. UTC
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(-)

Patch
diff mbox series

diff --git a/src/ipa/mali-c55/algorithms/lsc.cpp b/src/ipa/mali-c55/algorithms/lsc.cpp
index 36e50163aecc..624b393b80e5 100644
--- a/src/ipa/mali-c55/algorithms/lsc.cpp
+++ b/src/ipa/mali-c55/algorithms/lsc.cpp
@@ -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")
diff --git a/src/ipa/mali-c55/algorithms/lsc.h b/src/ipa/mali-c55/algorithms/lsc.h
index 3d35fd72bfa8..79ad42094c42 100644
--- a/src/ipa/mali-c55/algorithms/lsc.h
+++ b/src/ipa/mali-c55/algorithms/lsc.h
@@ -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 */
diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h
index 2f2092677fbf..749309115d84 100644
--- a/src/ipa/mali-c55/ipa_context.h
+++ b/src/ipa/mali-c55/ipa_context.h
@@ -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 {