diff --git a/src/ipa/libipa/lsc.h b/src/ipa/libipa/lsc.h
index d48336b169f4..6dc76b6572b3 100644
--- a/src/ipa/libipa/lsc.h
+++ b/src/ipa/libipa/lsc.h
@@ -53,6 +53,9 @@ public:
 template<typename U>
 class LscAlgorithm : public LscAlgorithmBase
 {
+private:
+	using T = typename U::QuantizedType;
+
 public:
 	int init(const ValueNode &tuningData, ControlInfoMap::Map &controls,
 		 const LscDescriptor &descriptor)
@@ -94,7 +97,7 @@ public:
 		      const std::vector<double> &yPos)
 	{
 		LOG(Lsc, Debug) << "Sample Lsc data for " << analogCrop;
-		lsc::ComponentsMap lscData =
+		lsc::ComponentsMap<T> lscData =
 			impl_->sampleForCrop(analogCrop, xPos, yPos);
 
 		/*
@@ -112,20 +115,20 @@ public:
 		return 0;
 	}
 
-	const lsc::Components interpolateComponents(unsigned int ct)
+	const lsc::Components<T> interpolateComponents(unsigned int ct)
 	{
 		return sets_.getInterpolated(ct);
 	}
 
-	const lsc::ComponentsMap getComponents()
+	const lsc::ComponentsMap<T> getComponents()
 	{
 		return lscData_;
 	}
 
 private:
 	std::unique_ptr<LscImplementation<U>> impl_;
-	Interpolator<lsc::Components> sets_;
-	lsc::ComponentsMap lscData_;
+	Interpolator<lsc::Components<T>> sets_;
+	lsc::ComponentsMap<T> lscData_;
 	bool polynomial_;
 };
 
diff --git a/src/ipa/libipa/lsc_base.cpp b/src/ipa/libipa/lsc_base.cpp
index 0c3a10d25cf8..ff5d47e85c01 100644
--- a/src/ipa/libipa/lsc_base.cpp
+++ b/src/ipa/libipa/lsc_base.cpp
@@ -32,25 +32,28 @@ namespace ipa {
 namespace lsc {
 
 /**
- * \typedef Components
+ * \class Components
  * \brief Associate a colour components with a list of gains
+ * \tparam T The type used to store the gain values when loaded from tuning file
  *
  * Lsc tables are defined as a list of gain values associated to a colour
  * component.
  *
  * As different ISP support different colour components (usually 'r', 'gr',
  * 'gb', 'b' or just 'r', 'g', 'b') this class associates a string
- * identifier for the colour component to a list of gains.
+ * identifier for the colour component to a list of gains of type \a T.
  *
- * Each key name shall match an entry in the tuning file.
+ * Each key name shall match an entry in the tuning file and the type \a T
+ * shall match the size of the registers where gains are stored.
  *
  * The list of keys is provided to the LscAlgorithm class using \a
  * LscDescriptor::keys.
  */
 
 /**
- * \typedef ComponentsMap
+ * \class ComponentsMap
  * \brief Associate a colour temperature to a lsc table
+ * \tparam T The type used to store the gain values when loaded from tuning file
  *
  * An lsc table is generated during the tuning phase for a specific light
  * temperature, and a tuning file usually contains lsc tables generated for
@@ -61,10 +64,10 @@ namespace lsc {
 
 #ifndef __DOXYGEN__
 template<>
-void Interpolator<lsc::Components>::
-	interpolate(const lsc::Components &a,
-		    const lsc::Components &b,
-		    lsc::Components &dest,
+void Interpolator<lsc::Components<uint16_t>>::
+	interpolate(const lsc::Components<uint16_t> &a,
+		    const lsc::Components<uint16_t> &b,
+		    lsc::Components<uint16_t> &dest,
 		    double lambda)
 {
 	for (auto const &[k, v] : a)
diff --git a/src/ipa/libipa/lsc_base.h b/src/ipa/libipa/lsc_base.h
index 721fd137ef21..4edcb2637d1b 100644
--- a/src/ipa/libipa/lsc_base.h
+++ b/src/ipa/libipa/lsc_base.h
@@ -26,8 +26,15 @@ namespace ipa {
 
 namespace lsc {
 
-using Components = std::map<std::string, std::vector<uint16_t>>;
-using ComponentsMap = std::map<unsigned int, Components>;
+template<typename T>
+class Components : public std::map<std::string, std::vector<T>>
+{
+};
+
+template<typename T>
+class ComponentsMap : public std::map<unsigned int, Components<T>>
+{
+};
 
 } /* namespace lsc */
 
@@ -43,10 +50,10 @@ void interpolateVector(const std::vector<T> &a, const std::vector<T> &b,
 }
 
 template<>
-void Interpolator<lsc::Components>::
-	interpolate(const lsc::Components &a,
-		    const lsc::Components &b,
-		    lsc::Components &dest,
+void Interpolator<lsc::Components<uint16_t>>::
+	interpolate(const lsc::Components<uint16_t> &a,
+		    const lsc::Components<uint16_t> &b,
+		    lsc::Components<uint16_t> &dest,
 		    double lambda);
 #endif /* __DOXYGEN__ */
 
@@ -66,7 +73,7 @@ public:
 	virtual int parseLscData(const ValueNode &tuningData,
 				 const LscDescriptor &descriptor) = 0;
 
-	virtual lsc::ComponentsMap
+	virtual lsc::ComponentsMap<typename U::QuantizedType>
 	sampleForCrop(const Rectangle &cropRectangle,
 		      std::vector<double> xPos, std::vector<double> yPos);
 };
diff --git a/src/ipa/libipa/lsc_polynomial.h b/src/ipa/libipa/lsc_polynomial.h
index 04d7465b3af0..a84a736734e1 100644
--- a/src/ipa/libipa/lsc_polynomial.h
+++ b/src/ipa/libipa/lsc_polynomial.h
@@ -67,6 +67,9 @@ protected:
 template<typename U>
 class LscPolynomial : public LscPolynomialBase, public LscImplementation<U>
 {
+private:
+	using T = typename U::QuantizedType;
+
 public:
 	int parseLscData(const ValueNode &yamlSets,
 			 const LscDescriptor &descriptor) override
@@ -74,14 +77,14 @@ public:
 		return LscPolynomialBase::parseLscData(yamlSets, descriptor);
 	}
 
-	lsc::ComponentsMap
+	lsc::ComponentsMap<T>
 	sampleForCrop(const Rectangle &cropRectangle,
 		      std::vector<double> xPos, std::vector<double> yPos) override
 	{
-		lsc::ComponentsMap components;
+		lsc::ComponentsMap<T> components;
 
 		for (const auto &[t, c] : lscData_) {
-			lsc::Components comp;
+			lsc::Components<T> comp;
 
 			for (const auto &[k, p] : c) {
 				comp.emplace(std::piecewise_construct,
@@ -97,17 +100,17 @@ public:
 	}
 
 private:
-	std::vector<uint16_t> samplePolynomial(const lsc::Polynomial &poly,
-					       Span<const double> xPositions,
-					       Span<const double> yPositions,
-					       const Rectangle &cropRectangle)
+	std::vector<T> samplePolynomial(const lsc::Polynomial &poly,
+					Span<const double> xPositions,
+					Span<const double> yPositions,
+					const Rectangle &cropRectangle)
 	{
 		double m = poly.getM();
 		double x0 = cropRectangle.x / m;
 		double y0 = cropRectangle.y / m;
 		double w = cropRectangle.width / m;
 		double h = cropRectangle.height / m;
-		std::vector<uint16_t> samples;
+		std::vector<T> samples;
 
 		samples.reserve(xPositions.size() * yPositions.size());
 
diff --git a/src/ipa/libipa/lsc_table.cpp b/src/ipa/libipa/lsc_table.cpp
index bb3c76b12332..aa391534335e 100644
--- a/src/ipa/libipa/lsc_table.cpp
+++ b/src/ipa/libipa/lsc_table.cpp
@@ -13,99 +13,6 @@ LOG_DEFINE_CATEGORY(LscTable)
 
 namespace ipa {
 
-/**
- * \class LscTableBase
- * \brief Base class for LscTable
- *
- * Base class for LscTable for non-templated functions.
- */
-
-/**
- * \brief Parse tabular lsc data
- * \param[in] sets The tuning file content
- * \param[in] descriptor The lsc engine descriptor
- *
- * Parse the lsc data in tabular form from the \a sets tuning data.
- *
- * \return 0 on success or a negative error number otherwise
- */
-int LscTableBase::parseLscData(const ValueNode &sets,
-			       const LscDescriptor &descriptor)
-{
-	for (const auto &set : sets.asList()) {
-		uint32_t ct = set["ct"].get<uint32_t>(0);
-
-		int ret = parseLscComponent(set, ct, descriptor);
-		if (ret)
-			return ret;
-	}
-
-	if (lscData_.empty()) {
-		LOG(LscTable, Error) << "Failed to load any sets";
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-int LscTableBase::parseLscComponent(const ValueNode &yamlSet,
-				    unsigned int ct, const LscDescriptor &descriptor)
-{
-	lsc::Components component;
-	for (auto &k : descriptor.keys) {
-		auto [it, inserted] = component.emplace(
-			std::piecewise_construct,
-			std::forward_as_tuple(k.c_str()),
-			std::forward_as_tuple(parseTable(yamlSet,
-							 k.c_str(),
-							 descriptor.numHCells,
-							 descriptor.numVCells)));
-		if (!inserted || it->second.empty()) {
-			LOG(LscTable, Error)
-				<< "Set " << k << " for color temperature "
-				<< ct << " is missing";
-			return -EINVAL;
-		}
-	}
-
-	auto [it, inserted] = lscData_.emplace(ct, component);
-	if (!inserted) {
-		LOG(LscTable, Error)
-			<< "Multiple sets found for color temperature "
-			<< ct;
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-std::vector<uint16_t> LscTableBase::parseTable(const ValueNode &tuningData,
-					       const char *prop,
-					       unsigned int numHCells,
-					       unsigned int numVCells)
-{
-	unsigned int lscNumSamples = numHCells * numVCells;
-
-	std::vector<uint16_t> table =
-		tuningData[prop].get<std::vector<uint16_t>>().value_or(utils::defopt);
-	if (table.size() != lscNumSamples) {
-		LOG(LscTable, Error)
-			<< "Invalid '" << prop << "' values: expected "
-			<< lscNumSamples
-			<< " elements, got " << table.size();
-		return {};
-	}
-
-	return table;
-}
-
-/**
- * \var LscTableBase::lscData_
- * \brief The tabular lsc data
- *
- * Maps colour temperatures to per-colour channel vector of gains
- */
-
 /**
  * \class LscTable
  * \brief Table based lsc algorithm implementation
@@ -119,8 +26,13 @@ std::vector<uint16_t> LscTableBase::parseTable(const ValueNode &tuningData,
 
 /**
  * \fn LscTable::parseLscData()
+ * \brief Parse tabular lsc data
+ * \param[in] sets The tuning file content
+ * \param[in] descriptor The lsc engine descriptor
+ *
+ * Parse the lsc data in tabular form from the \a sets tuning data.
  *
- * \copydoc LscTableBase::parseLscData()
+ * \return 0 on success or a negative error number otherwise
  */
 
 } /* namespace ipa */
diff --git a/src/ipa/libipa/lsc_table.h b/src/ipa/libipa/lsc_table.h
index 65e04b09c3ff..fe4e133060a6 100644
--- a/src/ipa/libipa/lsc_table.h
+++ b/src/ipa/libipa/lsc_table.h
@@ -23,34 +23,33 @@ LOG_DECLARE_CATEGORY(LscTable)
 
 namespace ipa {
 
-class LscTableBase
+template<typename U>
+class LscTable : public LscImplementation<U>
 {
-protected:
-	int parseLscData(const ValueNode &sets,
-			 const LscDescriptor &descriptor);
-
 private:
-	int parseLscComponent(const ValueNode &yamlSet,
-			      unsigned int ct, const LscDescriptor &descriptor);
-	std::vector<uint16_t> parseTable(const ValueNode &tuningData,
-					 const char *prop,
-					 unsigned int numHCells,
-					 unsigned int numVCells);
-protected:
-	lsc::ComponentsMap lscData_;
-};
+	using T = typename U::QuantizedType;
 
-template<typename U>
-class LscTable : public LscTableBase, public LscImplementation<U>
-{
 public:
 	int parseLscData(const ValueNode &sets,
 			 const LscDescriptor &descriptor) override
 	{
-		return LscTableBase::parseLscData(sets, descriptor);
+		for (const auto &set : sets.asList()) {
+			uint32_t ct = set["ct"].get<uint32_t>(0);
+
+			int ret = parseLscComponent(set, ct, descriptor);
+			if (ret)
+				return ret;
+		}
+
+		if (lscData_.empty()) {
+			LOG(LscTable, Error) << "Failed to load any sets";
+			return -EINVAL;
+		}
+
+		return 0;
 	}
 
-	lsc::ComponentsMap
+	lsc::ComponentsMap<T>
 	sampleForCrop([[maybe_unused]] const Rectangle &cropRectangle,
 		      [[maybe_unused]] std::vector<double> xPos,
 		      [[maybe_unused]] std::vector<double> yPos) override
@@ -59,6 +58,59 @@ public:
 			<< "Tabular LSC data doesn't support resampling";
 		return lscData_;
 	}
+
+private:
+	int parseLscComponent(const ValueNode &yamlSet,
+			      unsigned int ct, const LscDescriptor &descriptor)
+	{
+		lsc::Components<T> component;
+		for (auto &k : descriptor.keys) {
+			auto [it, inserted] = component.emplace(
+				std::piecewise_construct,
+				std::forward_as_tuple(k.c_str()),
+				std::forward_as_tuple(parseTable(yamlSet,
+								 k.c_str(),
+								 descriptor.numHCells,
+								 descriptor.numVCells)));
+			if (!inserted || it->second.empty()) {
+				LOG(LscTable, Error)
+					<< "Set " << k << " for color temperature "
+					<< ct << " is missing";
+				return -EINVAL;
+			}
+		}
+
+		auto [it, inserted] = lscData_.emplace(ct, component);
+		if (!inserted) {
+			LOG(LscTable, Error)
+				<< "Multiple sets found for color temperature "
+				<< ct;
+			return -EINVAL;
+		}
+
+		return 0;
+	}
+
+	std::vector<T> parseTable(const ValueNode &tuningData,
+				  const char *prop, unsigned int numHCells,
+				  unsigned int numVCells)
+	{
+		unsigned int kLscNumSamples = numHCells * numVCells;
+
+		std::vector<T> table =
+			tuningData[prop].get<std::vector<T>>().value_or(utils::defopt);
+		if (table.size() != kLscNumSamples) {
+			LOG(LscTable, Error)
+				<< "Invalid '" << prop << "' values: expected "
+				<< kLscNumSamples
+				<< " elements, got " << table.size();
+			return {};
+		}
+
+		return table;
+	}
+
+	lsc::ComponentsMap<T> lscData_;
 };
 
 } /* namespace ipa */
diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp
index 79d68ac7d6dd..9e3dc517f6cb 100644
--- a/src/ipa/rkisp1/algorithms/lsc.cpp
+++ b/src/ipa/rkisp1/algorithms/lsc.cpp
@@ -176,7 +176,7 @@ void LensShadingCorrection::setParameters(rkisp1_cif_isp_lsc_config &config)
 }
 
 void LensShadingCorrection::copyTable(rkisp1_cif_isp_lsc_config &config,
-				      const lsc::Components &set)
+				      const lsc::Components<uint16_t> &set)
 {
 	const auto &r = set.at("r");
 	std::copy(r.begin(), r.end(), &config.r_data_tbl[0][0]);
@@ -235,7 +235,7 @@ void LensShadingCorrection::prepare([[maybe_unused]] IPAContext &context,
 
 	setParameters(*config);
 
-	const lsc::Components &set = lscAlgo_.interpolateComponents(quantizedCt);
+	const lsc::Components<uint16_t> &set = lscAlgo_.interpolateComponents(quantizedCt);
 	copyTable(*config, set);
 
 	lastAppliedCt_ = ct;
diff --git a/src/ipa/rkisp1/algorithms/lsc.h b/src/ipa/rkisp1/algorithms/lsc.h
index 31156f56cd21..eb32997d64bc 100644
--- a/src/ipa/rkisp1/algorithms/lsc.h
+++ b/src/ipa/rkisp1/algorithms/lsc.h
@@ -42,7 +42,8 @@ public:
 		     ControlList &metadata) override;
 private:
 	void setParameters(rkisp1_cif_isp_lsc_config &config);
-	void copyTable(rkisp1_cif_isp_lsc_config &config, const lsc::Components &set0);
+	void copyTable(rkisp1_cif_isp_lsc_config &config,
+		       const lsc::Components<uint16_t> &set0);
 	std::vector<double> parseSizes(const ValueNode &tuningData,
 				       const char *prop);
 	std::vector<double> sizesListToPositions(Span<const double> sizes);
