diff --git a/src/ipa/libipa/agc.cpp b/src/ipa/libipa/agc.cpp
index 7438825d30..54aa8207d4 100644
--- a/src/ipa/libipa/agc.cpp
+++ b/src/ipa/libipa/agc.cpp
@@ -26,6 +26,8 @@
 
 #include <libcamera/ipa/core_ipa_interface.h>
 
+#include "exposure_mode_helper.h"
+
 /**
  * \file agc.h
  * \brief libipa AGC algorithm
@@ -222,6 +224,196 @@ namespace agc {
 
 } /* namespace agc */
 
+namespace {
+
+/*
+ * Default relative luminance target
+ *
+ * This value should be chosen so that when the camera points at a grey target,
+ * the resulting image brightness looks "right". Custom values can be passed
+ * as the relativeLuminanceTarget value in sensor tuning files.
+ */
+static constexpr double kDefaultRelativeLuminanceTarget = 0.16;
+
+int parseRelativeLuminanceTarget(AgcMeanLuminance::Configuration &config, const ValueNode &tuningData)
+{
+	auto &target = tuningData["relativeLuminanceTarget"];
+	if (!target) {
+		config.relativeLuminanceTarget = { { { { 0.0, kDefaultRelativeLuminanceTarget } } } };
+		return 0;
+	}
+
+	auto pwl = target.get<Pwl>();
+	if (!pwl) {
+		LOG(Agc, Error)
+			<< "Failed to load relative luminance target.";
+		return -EINVAL;
+	}
+
+	config.relativeLuminanceTarget = std::move(*pwl);
+	return 0;
+}
+
+int parseConstraint(AgcMeanLuminance::Configuration &config, const ValueNode &modeDict, int32_t id)
+{
+	for (const auto &[boundName, content] : modeDict.asDict()) {
+		if (boundName != "upper" && boundName != "lower") {
+			LOG(Agc, Warning)
+				<< "Ignoring unknown constraint bound '" << boundName << "'";
+			continue;
+		}
+
+		unsigned int idx = static_cast<unsigned int>(boundName == "upper");
+		auto bound = static_cast<AgcMeanLuminance::AgcConstraint::Bound>(idx);
+		double qLo = content["qLo"].get<double>().value_or(0.98);
+		double qHi = content["qHi"].get<double>().value_or(1.0);
+		auto yTarget = content["yTarget"].get<Pwl>();
+		if (!yTarget) {
+			LOG(Agc, Error)
+				<< "Failed to parse yTarget";
+			return -EINVAL;
+		}
+
+		AgcMeanLuminance::AgcConstraint constraint = {
+			bound,
+			qLo,
+			qHi,
+			std::move(*yTarget),
+		};
+		auto &constraints = config.constraintModes[id];
+
+		if (idx)
+			constraints.push_back(std::move(constraint));
+		else
+			constraints.insert(constraints.begin(), std::move(constraint));
+	}
+
+	return 0;
+}
+
+int parseConstraintModes(AgcMeanLuminance::Configuration &config, const ValueNode &tuningData)
+{
+	const ValueNode &constraintModes = tuningData[controls::AeConstraintMode.name()];
+	if (constraintModes.isDictionary()) {
+		for (const auto &[modeName, modeDict] : constraintModes.asDict()) {
+			auto it = controls::AeConstraintModeNameValueMap.find(modeName);
+			if (it == controls::AeConstraintModeNameValueMap.end()) {
+				LOG(Agc, Warning)
+					<< "Skipping unknown constraint mode '" << modeName << "'";
+				continue;
+			}
+
+			if (!modeDict.isDictionary()) {
+				LOG(Agc, Error)
+					<< "Invalid constraint mode '" << modeName << "'";
+				return -EINVAL;
+			}
+
+			int ret = parseConstraint(config, modeDict, it->second);
+			if (ret)
+				return ret;
+		}
+	}
+
+	/*
+	 * If the tuning data file contains no constraints then we use the
+	 * default constraint that the IPU3/RkISP1 Agc algorithms were adhering
+	 * to anyway before centralisation; this constraint forces the top 2% of
+	 * the histogram to be at least 0.5.
+	 */
+	if (config.constraintModes.empty()) {
+		AgcMeanLuminance::AgcConstraint constraint = {
+			AgcMeanLuminance::AgcConstraint::Bound::Lower,
+			0.98,
+			1.0,
+			Pwl({ { { 0.0, 0.5 } } }),
+		};
+
+		config.constraintModes[controls::AeConstraintModeNormal].push_back(
+			std::move(constraint));
+	}
+
+	return 0;
+}
+
+int parseExposureModes(AgcMeanLuminance::Configuration &config, const ValueNode &tuningData)
+{
+	const ValueNode &exposureModes = tuningData[controls::AeExposureMode.name()];
+	if (exposureModes.isDictionary()) {
+		for (const auto &[modeName, modeValues] : exposureModes.asDict()) {
+			auto it = controls::AeExposureModeNameValueMap.find(modeName);
+			if (it == controls::AeExposureModeNameValueMap.end()) {
+				LOG(Agc, Warning)
+					<< "Skipping unknown exposure mode '" << modeName << "'";
+				continue;
+			}
+
+			if (!modeValues.isDictionary()) {
+				LOG(Agc, Error)
+					<< "Invalid exposure mode '" << modeName << "'";
+				return -EINVAL;
+			}
+
+			std::vector<uint32_t> exposureTimes =
+				modeValues["exposureTime"].get<std::vector<uint32_t>>().value_or(utils::defopt);
+			std::vector<double> gains =
+				modeValues["gain"].get<std::vector<double>>().value_or(utils::defopt);
+
+			if (exposureTimes.size() != gains.size()) {
+				LOG(Agc, Error)
+					<< "Exposure time and gain array sizes unequal";
+				return -EINVAL;
+			}
+
+			if (exposureTimes.empty()) {
+				LOG(Agc, Error)
+					<< "Exposure time and gain arrays are empty";
+				return -EINVAL;
+			}
+
+			std::vector<ExposureModeHelper::Stage> stages;
+			for (size_t i = 0; i < exposureTimes.size(); i++) {
+				stages.push_back({
+					.exposureTime = std::chrono::microseconds(exposureTimes[i]),
+					.gain = gains[i],
+				});
+			}
+
+			config.exposureModeHelpers.try_emplace(it->second, std::move(stages));
+		}
+	}
+
+	/*
+	 * If we don't have any exposure modes in the tuning data we create an
+	 * ExposureModeHelper using an empty vector of stages. This will result
+	 * in the ExposureModeHelper simply driving the exposure time as high as
+	 * possible before touching gain.
+	 */
+	if (config.exposureModeHelpers.empty())
+		config.exposureModeHelpers.try_emplace(
+			controls::AeExposureModeNormal,
+			std::vector<ExposureModeHelper::Stage>{});
+
+	return 0;
+}
+
+int parseTuningData(AgcMeanLuminance::Configuration &config, const ValueNode &tuningData)
+{
+	int ret;
+
+	ret = parseRelativeLuminanceTarget(config, tuningData);
+	if (ret)
+		return ret;
+
+	ret = parseConstraintModes(config, tuningData);
+	if (ret)
+		return ret;
+
+	return parseExposureModes(config, tuningData);
+}
+
+} /* namespace */
+
 /**
  * \class AgcAlgorithm
  * \brief libIPA LSC algorithm algorithm
@@ -238,6 +430,57 @@ namespace agc {
  * virtual function, they should simply call the identically named member
  * function of AgcAlgorithm.
  *
+ * When a CameraSensorHelper is provided, AgcMeanLuminance is used and support for
+ * exposure (controls::AeExposureMode) and constraint (controls::AeConstraintMode)
+ * modes, and specifying the default luminance target is available via the tuning data.
+ *
+ * For controls::AeConstraintMode, the dictionary under the key "AeConstraintMode"
+ * is parsed: per-mode setting dictionaries with the key being a value from
+ * controls::AeConstraintModeNameValueMap. The yTarget can either be provided
+ * as single value or as array in which case it is interpreted as a PWL mapping
+ * lux levels to yTarget values. Each mode dict may contain either a "lower" or
+ * "upper" key or both, for example:
+ *
+ * \code{.unparsed}
+ * algorithms:
+ *   - Agc:
+ *       AeConstraintMode:
+ *         Normal:
+ *           lower:
+ *             qLo: 0.98
+ *             qHi: 1.0
+ *             yTarget: 0.5
+ *         Highlight:
+ *           lower:
+ *             qLo: 0.98
+ *             qHi: 1.0
+ *             yTarget: 0.5
+ *           upper:
+ *             qLo: 0.98
+ *             qHi: 1.0
+ *             yTarget: [ 100, 0.8, 20000, 0.5 ]
+ *
+ * \endcode
+ *
+ * For controls::AeExposureMode, the dictionary under the key AeExposureMode
+ * is parsed: per-mode setting dictionaries with the key being a value from
+ * controls::AeExposureModeNameValueMap. Each mode dict should contain
+ * an array of exposure times with the key "exposureTime" and an array of gain
+ * values with the key "gain", in this format:
+ *
+ * \code{.unparsed}
+ * algorithms:
+ *   - Agc:
+ *       AeExposureMode:
+ *         Normal:
+ *           exposureTime: [ 100, 10000, 30000, 60000, 120000 ]
+ *           gain: [ 2.0, 4.0, 6.0, 8.0, 10.0 ]
+ *         Short:
+ *           exposureTime: [ 100, 10000, 30000, 60000, 120000 ]
+ *           gain: [ 2.0, 4.0, 6.0, 8.0, 10.0 ]
+ *
+ * \endcode
+ *
  * \todo DigitalGain, DigitalGainMode
  * \todo Expand documentation
  */
@@ -298,9 +541,8 @@ namespace agc {
  * in ConfigurationParams::ctrlMap.
  *
  * If a CameraSensorHelper is provided in \a sensor, an instance of
- * AgcMeanLuminance is instantiated and the tuning data is loaded by
- * calling AgcMeanLuminance::parseTuningData(). Otherwise an instance
- * of AgcMSV is instantiated, and in this case the tuning data is ignored.
+ * AgcMeanLuminance is instantiated. Otherwise an instance of AgcMSV
+ * is instantiated, and in this case the tuning data is ignored.
  *
  * \return 0 on success, or a negative error code
  *
@@ -310,10 +552,12 @@ int AgcAlgorithm::init(const ValueNode &tuningData, CameraSensorHelper *sensor,
 		       const ConfigurationParams &config)
 {
 	if (sensor) {
-		auto &impl = impl_.emplace<AgcMeanLuminance>();
-		int ret = impl.parseTuningData(tuningData);
+		AgcMeanLuminance::Configuration agcConfig;
+		int ret = parseTuningData(agcConfig, tuningData);
 		if (ret)
 			return ret;
+
+		impl_.emplace<AgcMeanLuminance>(std::move(agcConfig));
 	} else {
 		impl_.emplace<AgcMSV>();
 	}
diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp
index 4c47770424..4f2b5edef6 100644
--- a/src/ipa/libipa/agc_mean_luminance.cpp
+++ b/src/ipa/libipa/agc_mean_luminance.cpp
@@ -9,7 +9,6 @@
 
 #include <algorithm>
 #include <cmath>
-#include <span>
 
 #include <libcamera/base/log.h>
 #include <libcamera/base/utils.h>
@@ -18,8 +17,6 @@
 
 #include "exposure_mode_helper.h"
 
-using namespace libcamera::controls;
-
 /**
  * \file agc_mean_luminance.h
  * \brief Class implementing mean luminance AEGC
@@ -39,15 +36,6 @@ namespace ipa {
  */
 static constexpr uint32_t kNumStartupFrames = 10;
 
-/*
- * Default relative luminance target
- *
- * This value should be chosen so that when the camera points at a grey target,
- * the resulting image brightness looks "right". Custom values can be passed
- * as the relativeLuminanceTarget value in sensor tuning files.
- */
-static constexpr double kDefaultRelativeLuminanceTarget = 0.16;
-
 /*
  * Maximum relative luminance target
  *
@@ -148,17 +136,13 @@ static constexpr unsigned int kDefaultLuxLevel = 500;
  * section of a histogram to a target value, where the target and the boundaries
  * of the section of the histogram used in the calculation are taken from the
  * values defined for the currently configured AeConstraintMode within the
- * tuning data. This class provides a helper function to parse those tuning data
- * to discover the constraints, and so requires a specific format for those
- * data which is described in \ref parseTuningData(). The gain from the first
- * stage is then clamped to the gain from this stage.
+ * tuning data. The gain from the first stage is then clamped to the gain from
+ * this stage.
  *
  * The final gain is used to adjust the effective exposure value of the image,
  * and that new exposure value is divided into exposure time, analogue gain and
  * digital gain according to the selected AeExposureMode. This class uses the
- * \ref ExposureModeHelper class to assist in that division, and expects the
- * data needed to initialise that class to be present in tuning data in a
- * format described in \ref parseTuningData().
+ * \ref ExposureModeHelper class to assist in that division.
  *
  * In order to be able to use this algorithm an IPA module needs to be able to
  * do the following:
@@ -170,175 +154,23 @@ static constexpr unsigned int kDefaultLuxLevel = 500;
  *
  * IPA modules that want to use this class to implement their AEGC algorithm
  * should derive AgcMeanLuminance::Traits and override the necessary functions.
- * The users must call parseTuningData() in init(), and must also call configure()
- * and setLimits() in configure(). They may then use calculateNewEv() in process().
- * If the limits passed to setLimits() change for any reason (for example, in
- * response to a FrameDurationLimit control being passed in queueRequest()) then
- * setLimits() must be called again with the new values.
+ * The users must call configure() and setLimits() in configure(). They may
+ * then use calculateNewEv() in process(). If the limits passed to setLimits()
+ * change for any reason (for example, in response to a FrameDurationLimit control
+ * being passed in queueRequest()) then setLimits() must be called again with the
+ * new values.
  */
 
-AgcMeanLuminance::AgcMeanLuminance()
-	: filteredExposure_(0s), luxWarningEnabled_(true), frameCount_(0)
+AgcMeanLuminance::AgcMeanLuminance(Configuration &&config)
+	: filteredExposure_(0s), luxWarningEnabled_(true), frameCount_(0),
+	  relativeLuminanceTarget_(std::move(config.relativeLuminanceTarget)),
+	  constraintModes_(std::move(config.constraintModes)),
+	  exposureModeHelpers_(std::move(config.exposureModeHelpers))
 {
 }
 
 AgcMeanLuminance::~AgcMeanLuminance() = default;
 
-int AgcMeanLuminance::parseRelativeLuminanceTarget(const ValueNode &tuningData)
-{
-	auto &target = tuningData["relativeLuminanceTarget"];
-	if (!target) {
-		relativeLuminanceTarget_ = { { { { 0.0, kDefaultRelativeLuminanceTarget } } } };
-		return 0;
-	}
-
-	auto pwl = target.get<Pwl>();
-	if (!pwl) {
-		LOG(AgcMeanLuminance, Error)
-			<< "Failed to load relative luminance target.";
-		return -EINVAL;
-	}
-
-	relativeLuminanceTarget_ = std::move(*pwl);
-	return 0;
-}
-
-int AgcMeanLuminance::parseConstraint(const ValueNode &modeDict, int32_t id)
-{
-	for (const auto &[boundName, content] : modeDict.asDict()) {
-		if (boundName != "upper" && boundName != "lower") {
-			LOG(AgcMeanLuminance, Warning)
-				<< "Ignoring unknown constraint bound '" << boundName << "'";
-			continue;
-		}
-
-		unsigned int idx = static_cast<unsigned int>(boundName == "upper");
-		AgcConstraint::Bound bound = static_cast<AgcConstraint::Bound>(idx);
-		double qLo = content["qLo"].get<double>().value_or(0.98);
-		double qHi = content["qHi"].get<double>().value_or(1.0);
-		auto yTarget = content["yTarget"].get<Pwl>();
-		if (!yTarget) {
-			LOG(AgcMeanLuminance, Error)
-				<< "Failed to parse yTarget";
-			return -EINVAL;
-		}
-
-		AgcConstraint constraint = { bound, qLo, qHi, std::move(*yTarget) };
-		auto &constraints = constraintModes_[id];
-
-		if (idx)
-			constraints.push_back(std::move(constraint));
-		else
-			constraints.insert(constraints.begin(), std::move(constraint));
-	}
-
-	return 0;
-}
-
-int AgcMeanLuminance::parseConstraintModes(const ValueNode &tuningData)
-{
-	const ValueNode &constraintModes = tuningData[controls::AeConstraintMode.name()];
-	if (constraintModes.isDictionary()) {
-		for (const auto &[modeName, modeDict] : constraintModes.asDict()) {
-			auto it = AeConstraintModeNameValueMap.find(modeName);
-			if (it == AeConstraintModeNameValueMap.end()) {
-				LOG(AgcMeanLuminance, Warning)
-					<< "Skipping unknown constraint mode '" << modeName << "'";
-				continue;
-			}
-
-			if (!modeDict.isDictionary()) {
-				LOG(AgcMeanLuminance, Error)
-					<< "Invalid constraint mode '" << modeName << "'";
-				return -EINVAL;
-			}
-
-			int ret = parseConstraint(modeDict, it->second);
-			if (ret)
-				return ret;
-		}
-	}
-
-	/*
-	 * If the tuning data file contains no constraints then we use the
-	 * default constraint that the IPU3/RkISP1 Agc algorithms were adhering
-	 * to anyway before centralisation; this constraint forces the top 2% of
-	 * the histogram to be at least 0.5.
-	 */
-	if (constraintModes_.empty()) {
-		AgcConstraint constraint = {
-			AgcConstraint::Bound::Lower,
-			0.98,
-			1.0,
-			Pwl({ { { 0.0, 0.5 } } })
-		};
-
-		constraintModes_[controls::AeConstraintModeNormal].push_back(std::move(constraint));
-	}
-
-	return 0;
-}
-
-int AgcMeanLuminance::parseExposureModes(const ValueNode &tuningData)
-{
-	const ValueNode &exposureModes = tuningData[controls::AeExposureMode.name()];
-	if (exposureModes.isDictionary()) {
-		for (const auto &[modeName, modeValues] : exposureModes.asDict()) {
-			auto it = AeExposureModeNameValueMap.find(modeName);
-			if (it == AeExposureModeNameValueMap.end()) {
-				LOG(AgcMeanLuminance, Warning)
-					<< "Skipping unknown exposure mode '" << modeName << "'";
-				continue;
-			}
-
-			if (!modeValues.isDictionary()) {
-				LOG(AgcMeanLuminance, Error)
-					<< "Invalid exposure mode '" << modeName << "'";
-				return -EINVAL;
-			}
-
-			std::vector<uint32_t> exposureTimes =
-				modeValues["exposureTime"].get<std::vector<uint32_t>>().value_or(utils::defopt);
-			std::vector<double> gains =
-				modeValues["gain"].get<std::vector<double>>().value_or(utils::defopt);
-
-			if (exposureTimes.size() != gains.size()) {
-				LOG(AgcMeanLuminance, Error)
-					<< "Exposure time and gain array sizes unequal";
-				return -EINVAL;
-			}
-
-			if (exposureTimes.empty()) {
-				LOG(AgcMeanLuminance, Error)
-					<< "Exposure time and gain arrays are empty";
-				return -EINVAL;
-			}
-
-			std::vector<ExposureModeHelper::Stage> stages;
-			for (size_t i = 0; i < exposureTimes.size(); i++) {
-				stages.push_back({
-					.exposureTime = std::chrono::microseconds(exposureTimes[i]),
-					.gain = gains[i],
-				});
-			}
-
-			exposureModeHelpers_.try_emplace(it->second, std::move(stages));
-		}
-	}
-
-	/*
-	 * If we don't have any exposure modes in the tuning data we create an
-	 * ExposureModeHelper using an empty vector of stages. This will result
-	 * in the ExposureModeHelper simply driving the exposure time as high as
-	 * possible before touching gain.
-	 */
-	if (exposureModeHelpers_.empty())
-		exposureModeHelpers_.try_emplace(controls::AeExposureModeNormal,
-						 std::vector<ExposureModeHelper::Stage>{});
-
-	return 0;
-}
-
 /**
  * \brief Configure the exposure mode helpers
  * \param[in] lineDuration The sensor line length
@@ -358,76 +190,6 @@ void AgcMeanLuminance::configure(utils::Duration lineDuration,
 	frameCount_ = 0;
 }
 
-/**
- * \brief Parse tuning data for AeConstraintMode and AeExposureMode controls
- * \param[in] tuningData the ValueNode representing the tuning data
- *
- * This function parses tuning data to build the list of allowed values for the
- * AeConstraintMode and AeExposureMode controls. Those tuning data must provide
- * the data in a specific format; the Agc algorithm's tuning data should contain
- * a dictionary called AeConstraintMode containing per-mode setting dictionaries
- * with the key being a value from \ref controls::AeConstraintModeNameValueMap.
- * The yTarget can either be provided as single value or as array in which case
- * it is interpreted as a PWL mapping lux levels to yTarget values. Each mode
- * dict may contain either a "lower" or "upper" key or both, for example:
- *
- * \code{.unparsed}
- * algorithms:
- *   - Agc:
- *       AeConstraintMode:
- *         Normal:
- *           lower:
- *             qLo: 0.98
- *             qHi: 1.0
- *             yTarget: 0.5
- *         Highlight:
- *           lower:
- *             qLo: 0.98
- *             qHi: 1.0
- *             yTarget: 0.5
- *           upper:
- *             qLo: 0.98
- *             qHi: 1.0
- *             yTarget: [ 100, 0.8, 20000, 0.5 ]
- *
- * \endcode
- *
- * For the AeExposureMode control the data should contain a dictionary called
- * AeExposureMode containing per-mode setting dictionaries with the key being a
- * value from \ref controls::AeExposureModeNameValueMap. Each mode dict should
- * contain an array of exposure times with the key "exposureTime" and an array
- * of gain values with the key "gain", in this format:
- *
- * \code{.unparsed}
- * algorithms:
- *   - Agc:
- *       AeExposureMode:
- *         Normal:
- *           exposureTime: [ 100, 10000, 30000, 60000, 120000 ]
- *           gain: [ 2.0, 4.0, 6.0, 8.0, 10.0 ]
- *         Short:
- *           exposureTime: [ 100, 10000, 30000, 60000, 120000 ]
- *           gain: [ 2.0, 4.0, 6.0, 8.0, 10.0 ]
- *
- * \endcode
- *
- * \return 0 on success or a negative error code
- */
-int AgcMeanLuminance::parseTuningData(const ValueNode &tuningData)
-{
-	int ret;
-
-	ret = parseRelativeLuminanceTarget(tuningData);
-	if (ret)
-		return ret;
-
-	ret = parseConstraintModes(tuningData);
-	if (ret)
-		return ret;
-
-	return parseExposureModes(tuningData);
-}
-
 /**
  * \brief Set the ExposureModeHelper limits for this class
  * \param[in] minExposureTime Minimum exposure time to allow
@@ -452,12 +214,12 @@ void AgcMeanLuminance::setLimits(utils::Duration minExposureTime,
 
 /**
  * \fn AgcMeanLuminance::constraintModes()
- * \brief Get the constraint modes that have been parsed from tuning data
+ * \brief Get the available constraint modes
  */
 
 /**
  * \fn AgcMeanLuminance::exposureModeHelpers()
- * \brief Get the ExposureModeHelpers that have been parsed from tuning data
+ * \brief Get the available ExposureModeHelpers
  */
 
 /**
diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h
index f799618ffc..c32bf44a2e 100644
--- a/src/ipa/libipa/agc_mean_luminance.h
+++ b/src/ipa/libipa/agc_mean_luminance.h
@@ -8,13 +8,10 @@
 #pragma once
 
 #include <map>
-#include <memory>
 #include <vector>
 
 #include <libcamera/base/utils.h>
 
-#include "libcamera/internal/value_node.h"
-
 #include "exposure_mode_helper.h"
 #include "histogram.h"
 #include "pwl.h"
@@ -26,9 +23,6 @@ namespace ipa {
 class AgcMeanLuminance
 {
 public:
-	AgcMeanLuminance();
-	~AgcMeanLuminance();
-
 	struct AgcConstraint {
 		enum class Bound {
 			Lower = 0,
@@ -40,6 +34,15 @@ public:
 		Pwl yTarget;
 	};
 
+	struct Configuration {
+		Pwl relativeLuminanceTarget;
+		std::map<int32_t, std::vector<AgcConstraint>> constraintModes;
+		std::map<int32_t, ExposureModeHelper> exposureModeHelpers;
+	};
+
+	AgcMeanLuminance(Configuration &&config);
+	~AgcMeanLuminance();
+
 	struct Traits {
 		virtual ~Traits() = default;
 		virtual double estimateLuminance(double gain) const = 0;
@@ -80,20 +83,17 @@ public:
 	double effectiveYTarget(double lux, double exposureCompensation) const;
 
 private:
-	int parseRelativeLuminanceTarget(const ValueNode &tuningData);
-	int parseConstraint(const ValueNode &modeDict, int32_t id);
-	int parseConstraintModes(const ValueNode &tuningData);
-	int parseExposureModes(const ValueNode &tuningData);
 	double estimateInitialGain(const Traits &traits, double yTarget) const;
 	double constraintClampGain(const Params &params, double gain) const;
 	utils::Duration filterExposure(utils::Duration exposureValue);
 
 	utils::Duration filteredExposure_;
 	mutable bool luxWarningEnabled_;
-	Pwl relativeLuminanceTarget_;
 	uint64_t frameCount_;
 
 	std::vector<AgcConstraint> additionalConstraints_;
+
+	Pwl relativeLuminanceTarget_;
 	std::map<int32_t, std::vector<AgcConstraint>> constraintModes_;
 	std::map<int32_t, ExposureModeHelper> exposureModeHelpers_;
 };
