diff --git a/src/ipa/libipa/fixedpoint.cpp b/src/ipa/libipa/fixedpoint.cpp
index 03053668ed79..97cdd9b3f7aa 100644
--- a/src/ipa/libipa/fixedpoint.cpp
+++ b/src/ipa/libipa/fixedpoint.cpp
@@ -185,6 +185,65 @@ namespace ipa {
  * Represents values in the range [0.0, 4095.9375] with a resolution of 1/16.
  */
 
+/**
+ * \struct ScaledFixedPointQTraits
+ * \brief Wrap a FixedPointQTraits with a linear scaling factor
+ *
+ * This trait extends an existing fixed-point quantisation policy
+ * by applying an additional multiplicative scale between the
+ * floating-point and quantised domains.
+ *
+ * \tparam Q The base fixed-point traits type
+ * \tparam Scale The scale factor applied to the floating-point domain
+ */
+
+/**
+ * \typedef ScaledFixedPointQTraits::QuantizedType
+ * \copydoc FixedPointQTraits::QuantizedType
+ */
+
+/**
+ * \var ScaledFixedPointQTraits::scale
+ * \brief The constant scaling factor applied to the floating-point domain
+ *
+ * Floating-point inputs are divided by this factor before quantisation,
+ * and multiplied by it after dequantisation.
+ */
+
+/**
+ * \var ScaledFixedPointQTraits::qmin
+ * \copydoc FixedPointQTraits::qmin
+ */
+
+/**
+ * \var ScaledFixedPointQTraits::qmax
+ * \copydoc FixedPointQTraits::qmax
+ */
+
+/**
+ * \var ScaledFixedPointQTraits::min
+ * \copydoc FixedPointQTraits::min
+ */
+
+/**
+ * \var ScaledFixedPointQTraits::max
+ * \copydoc FixedPointQTraits::max
+ */
+
+/**
+ * \fn ScaledFixedPointQTraits::fromFloat(float v)
+ * \copydoc FixedPointQTraits::fromFloat(float v)
+ *
+ * The input value \a v is divided by the scaling factor before conversion.
+ */
+
+/**
+ * \fn ScaledFixedPointQTraits::toFloat(QuantizedType q)
+ * \copydoc FixedPointQTraits::toFloat()
+ *
+ * The output value is multiplied by the scaling factor after conversion.
+ */
+
 } /* namespace ipa */
 
 } /* namespace libcamera */
diff --git a/src/ipa/libipa/fixedpoint.h b/src/ipa/libipa/fixedpoint.h
index 05dd97e64f40..1e984350111c 100644
--- a/src/ipa/libipa/fixedpoint.h
+++ b/src/ipa/libipa/fixedpoint.h
@@ -113,6 +113,30 @@ using UQ5_8 = Quantized<FixedPointQTraits<5, 8, uint16_t>>;
 using Q12_4 = Quantized<FixedPointQTraits<12, 4, int16_t>>;
 using UQ12_4 = Quantized<FixedPointQTraits<12, 4, uint16_t>>;
 
+template<typename Q, int Scale>
+struct ScaledFixedPointQTraits {
+	using QuantizedType = typename Q::QuantizedType;
+
+	static constexpr float scale = static_cast<float>(Scale);
+
+	/* Re-expose base limits, adjusted by the scaling factor */
+	static constexpr QuantizedType qmin = Q::qmin;
+	static constexpr QuantizedType qmax = Q::qmax;
+	static constexpr float min = Q::min * scale;
+	static constexpr float max = Q::max * scale;
+
+	static QuantizedType fromFloat(float v)
+	{
+		v = std::clamp(v, min, max);
+		return Q::fromFloat(v / scale);
+	}
+
+	static float toFloat(QuantizedType q)
+	{
+		return Q::toFloat(q) * scale;
+	}
+};
+
 } /* namespace ipa */
 
 } /* namespace libcamera */
