| Message ID | 20260615-libipa-algorithms-v1-11-e949c937422e@ideasonboard.com |
|---|---|
| State | Superseded |
| Headers | show |
| Series |
|
| Related | show |
Quoting Jacopo Mondi (2026-06-15 15:05:36) > 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(-) > > 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); A bit like with AwbMeas - I haven't seen a need to rename here. Was there a reason you didn't like the existing names ? Does that match something else ? Only concern is that it's adding more changes into a big patchset, but otherwise nothing seems wrong that I can see, and we get our common parsing and handling so: Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > +} > + > +/** > + * \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 { > > -- > 2.54.0 >
Hi Kieran On Mon, Jun 22, 2026 at 07:14:21PM +0100, Kieran Bingham wrote: > Quoting Jacopo Mondi (2026-06-15 15:05:36) > > 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(-) > > > > 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); > > A bit like with AwbMeas - I haven't seen a need to rename here. Was > there a reason you didn't like the existing names ? Does that match > something else ? I admit it's totally arbitrary from my side, but I can't correlate fillConfigParamsBlock() and fillSelectionParamsBlock() with their function which is better (imho) expressed by programMeshTables() and configureMeshBlending() respectively. > > Only concern is that it's adding more changes into a big patchset, but > otherwise nothing seems wrong that I can see, and we get our common > parsing and handling so: I admit the name change is unrelated, but the third time I had to look at the function body to know what they were doing, I decide to rename. I can backtrack indeed. > > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Thanks j > > > > +} > > + > > +/** > > + * \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 { > > > > -- > > 2.54.0 > >
Hi Jacopo On 15/06/2026 15:05, Jacopo Mondi wrote: > 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(-) > > 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; The IPU3 lens shading block actually has the concept of a configurable scale too, so I wonder if this is something that could be done in the base algorithms. It could be done on top though; and 5.0x is probably quite a high gain for LSC anyway > + > +/* 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. > + */ I think that the 4th colour temperature is not supposed to be usable, despite its declaration in the documentation. > +static constexpr unsigned int kMaxColourTemperature = 3; s/kMaxColourTemperature/kMaxColourTemperatures > + > +/* > + * 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) context is used, so the decorator can be dropped. > +{ > + 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; Very mild preference not to have the blank lines there between the states / contexts All very minor points: Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com> > }; > > 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 { >
On Tue, Jun 23, 2026 at 11:06:25AM +0100, Dan Scally wrote: > Hi Jacopo > > On 15/06/2026 15:05, Jacopo Mondi wrote: > > 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(-) > > > > 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; > > The IPU3 lens shading block actually has the concept of a configurable scale > too, so I wonder if this is something that could be done in the base > algorithms. It could be done on top though; and 5.0x is probably quite a > high gain for LSC anyway > mmm, configurable scale seems something that belongs to the IPA module itself, doens't it ? Do you think we can generalize it ? Using which values ? > > + > > +/* 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. > > + */ > > I think that the 4th colour temperature is not supposed to be usable, > despite its declaration in the documentation. Ack, I made the \todo /* * \todo Clarify if Mali-C55 can support up to 4 colour temperatures. * * 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; > > s/kMaxColourTemperature/kMaxColourTemperatures > ack > > + > > +/* > > + * 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) > > context is used, so the decorator can be dropped. > Sure, nice catch > > +{ > > + 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; > > Very mild preference not to have the blank lines there between the states / contexts Ack, I'll remove it (I had actually added a blank line between awb and ccm already :) > > All very minor points: > > Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com> Thanks! > > > }; > > 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 { > > >
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 {
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(-)