[5/5] ipa: rkisp1: agc: Plumb mode-selection and frame duration controls
diff mbox series

Message ID 20240405144729.2992219-6-paul.elder@ideasonboard.com
State Superseded
Headers show
Series
  • ipa: rkisp1: Improve AGC (plumbing)
Related show

Commit Message

Paul Elder April 5, 2024, 2:47 p.m. UTC
Plumb controls for setting metering mode, exposure mode, constraint
mode, and frame duration limits. Also report them as available controls,
as well as in metadata.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
---
 src/ipa/rkisp1/algorithms/agc.cpp     | 67 ++++++++++++++++++++-------
 src/ipa/rkisp1/algorithms/agc.h       |  5 ++
 src/ipa/rkisp1/algorithms/algorithm.h |  2 +
 src/ipa/rkisp1/ipa_context.h          |  4 ++
 src/ipa/rkisp1/rkisp1.cpp             | 10 ++++
 5 files changed, 72 insertions(+), 16 deletions(-)

Comments

Dan Scally April 10, 2024, 3 p.m. UTC | #1
Hi Paul

On 05/04/2024 15:47, Paul Elder wrote:
> Plumb controls for setting metering mode, exposure mode, constraint
> mode, and frame duration limits. Also report them as available controls,
> as well as in metadata.
>
> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
> ---
>   src/ipa/rkisp1/algorithms/agc.cpp     | 67 ++++++++++++++++++++-------
>   src/ipa/rkisp1/algorithms/agc.h       |  5 ++
>   src/ipa/rkisp1/algorithms/algorithm.h |  2 +
>   src/ipa/rkisp1/ipa_context.h          |  4 ++
>   src/ipa/rkisp1/rkisp1.cpp             | 10 ++++
>   5 files changed, 72 insertions(+), 16 deletions(-)
>
> diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
> index a1b6eb39..ed4d6330 100644
> --- a/src/ipa/rkisp1/algorithms/agc.cpp
> +++ b/src/ipa/rkisp1/algorithms/agc.cpp
> @@ -11,6 +11,7 @@
>   #include <chrono>
>   #include <cmath>
>   #include <tuple>
> +#include <vector>
>   
>   #include <libcamera/base/log.h>
>   #include <libcamera/base/utils.h>
> @@ -49,6 +50,7 @@ int Agc::parseMeteringModes(IPAContext &context, const YamlObject &tuningData,
>   		return -EINVAL;
>   	}
>   
> +	std::vector<ControlValue> availableMeteringModes;
>   	for (const auto &[key, value] : yamlMeteringModes.asDict()) {
>   		if (controls::AeMeteringModeNameValueMap.find(key) ==
>   		    controls::AeMeteringModeNameValueMap.end()) {
> @@ -67,9 +69,14 @@ int Agc::parseMeteringModes(IPAContext &context, const YamlObject &tuningData,
>   			return -ENODATA;
>   		}
>   
> -		meteringModes_[controls::AeMeteringModeNameValueMap.at(key)] = weights;
> +		int32_t control = controls::AeMeteringModeNameValueMap.at(key);
> +		meteringModes_[control] = weights;
> +		availableMeteringModes.push_back(control);
>   	}
>   
> +	Algorithm::controls_[&controls::AeMeteringMode] =
> +		ControlInfo(availableMeteringModes);
> +
>   	return 0;
>   }
>   
> @@ -131,9 +138,25 @@ int Agc::init(IPAContext &context, const YamlObject &tuningData)
>   
>   	context.ctrlMap.merge(controls());
>   
> +	defaultConstraintMode_ = constraintModes().begin()->first;
> +	defaultExposureMode_ = exposureModeHelpers().begin()->first;
> +	defaultMeteringMode_ = meteringModes_.begin()->first;
> +
> +	Algorithm::controls_.merge(ControlInfoMap::Map(controls()));
> +
>   	return 0;
>   }
>   
> +void Agc::configureExposureModeHelpers(IPAContext &context, utils::Duration maxShutterSpeed)
> +{
> +	for (auto &[id, helper] : exposureModeHelpers()) {
> +		helper->configure(context.configuration.sensor.minShutterSpeed,
> +				  maxShutterSpeed,
> +				  context.configuration.sensor.minAnalogueGain,
> +				  context.configuration.sensor.maxAnalogueGain);
> +	}
> +}
> +
>   /**
>    * \brief Configure the AGC given a configInfo
>    * \param[in] context The shared IPA context
> @@ -153,12 +176,8 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)
>   	context.activeState.agc.manual.dgain = 1;
>   	context.activeState.agc.autoEnabled = !context.configuration.raw;
>   
> -	/*
> -	* \todo We should use the first available mode rather than assume that
> -	* the "Normal" modes are present in tuning data.
> -	*/
> -	context.activeState.agc.constraintMode = controls::ConstraintNormal;
> -	context.activeState.agc.exposureMode = controls::ExposureNormal;
> +	context.activeState.agc.constraintMode = defaultConstraintMode_;
> +	context.activeState.agc.exposureMode = defaultExposureMode_;
>   
>   	/*
>   	 * Define the measurement window for AGC as a centered rectangle
> @@ -169,13 +188,7 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)
>   	context.configuration.agc.measureWindow.h_size = 3 * configInfo.outputSize.width / 4;
>   	context.configuration.agc.measureWindow.v_size = 3 * configInfo.outputSize.height / 4;
>   
> -	for (auto &[id, helper] : exposureModeHelpers()) {
> -		/* \todo Run this again when FrameDurationLimits is passed in */
> -		helper->configure(context.configuration.sensor.minShutterSpeed,
> -				  context.configuration.sensor.maxShutterSpeed,
> -				  context.configuration.sensor.minAnalogueGain,
> -				  context.configuration.sensor.maxAnalogueGain);
> -	}
> +	configureExposureModeHelpers(context, context.configuration.sensor.maxShutterSpeed);
>   
>   	return 0;
>   }
> @@ -223,6 +236,20 @@ void Agc::queueRequest(IPAContext &context,
>   		frameContext.agc.exposure = agc.manual.exposure;
>   		frameContext.agc.gain = agc.manual.gain;
>   	}
> +
> +	const auto &meteringMode = controls.get(controls::AeMeteringMode);
> +	frameContext.agc.meteringMode = meteringMode.value_or(defaultMeteringMode_);
> +
> +	const auto &exposureMode = controls.get(controls::AeExposureMode);
> +	frameContext.agc.exposureMode = exposureMode.value_or(defaultExposureMode_);
> +
> +	const auto &constraintMode = controls.get(controls::AeConstraintMode);
> +	frameContext.agc.constraintMode = constraintMode.value_or(defaultConstraintMode_);


Wouldn't this mean the modes revert to default unless we pass in those controls with every request? 
I have this same operation as:


frameContext.agc.exposureMode = exposureMode.value_or(frameContext.agc.exposureMode)


so that we keep the already configured value instead.

> +
> +	const auto &frameDurationLimits = controls.get(controls::FrameDurationLimits);
> +	frameContext.agc.maxShutterSpeed = frameDurationLimits
> +					 ? std::chrono::milliseconds((*frameDurationLimits).back())
> +					 : 60ms;
>   }
>   
>   /**
> @@ -262,8 +289,7 @@ void Agc::prepare(IPAContext &context, const uint32_t frame,
>   		params->meas.hst_config.hist_weight,
>   		context.hw->numHistogramWeights
>   	};
> -	/* \todo Get this from control */
> -	std::vector<uint8_t> &modeWeights = meteringModes_.at(controls::MeteringMatrix);
> +	std::vector<uint8_t> &modeWeights = meteringModes_.at(frameContext.agc.meteringMode);
>   	std::copy(modeWeights.begin(), modeWeights.end(), weights.begin());
>   
>   	std::stringstream str;
> @@ -290,6 +316,7 @@ void Agc::fillMetadata(IPAContext &context, IPAFrameContext &frameContext,
>   				     * frameContext.sensor.exposure;
>   	metadata.set(controls::AnalogueGain, frameContext.sensor.gain);
>   	metadata.set(controls::ExposureTime, exposureTime.get<std::micro>());
> +	metadata.set(controls::AeEnable, frameContext.agc.autoEnabled);
>   
>   	/* \todo Use VBlank value calculated from each frame exposure. */
>   	uint32_t vTotal = context.configuration.sensor.size.height
> @@ -297,6 +324,10 @@ void Agc::fillMetadata(IPAContext &context, IPAFrameContext &frameContext,
>   	utils::Duration frameDuration = context.configuration.sensor.lineDuration
>   				      * vTotal;
>   	metadata.set(controls::FrameDuration, frameDuration.get<std::micro>());
> +
> +	metadata.set(controls::AeMeteringMode, frameContext.agc.meteringMode);
> +	metadata.set(controls::AeExposureMode, frameContext.agc.exposureMode);
> +	metadata.set(controls::AeConstraintMode, frameContext.agc.constraintMode);
>   }
>   
>   void Agc::parseStatistics(const rkisp1_stat_buffer *stats,
> @@ -378,6 +409,10 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,
>   
>   	ASSERT(stats->meas_type & RKISP1_CIF_ISP_STAT_AUTOEXP);
>   
> +	utils::Duration maxShutterSpeed = std::min(context.configuration.sensor.maxShutterSpeed,
> +						   frameContext.agc.maxShutterSpeed);
> +	configureExposureModeHelpers(context, maxShutterSpeed);
> +
>   	parseStatistics(stats, context);
>   
>   	/*
> diff --git a/src/ipa/rkisp1/algorithms/agc.h b/src/ipa/rkisp1/algorithms/agc.h
> index 43e3d5b2..c05ba4be 100644
> --- a/src/ipa/rkisp1/algorithms/agc.h
> +++ b/src/ipa/rkisp1/algorithms/agc.h
> @@ -47,6 +47,7 @@ private:
>   	int parseMeteringModes(IPAContext &context, const YamlObject &tuningData,
>   			       const char *prop);
>   	uint8_t predivider(Size &size);
> +	void configureExposureModeHelpers(IPAContext &context, utils::Duration maxShutterSpeed);
>   
>   	void fillMetadata(IPAContext &context, IPAFrameContext &frameContext,
>   			  ControlList &metadata);
> @@ -58,6 +59,10 @@ private:
>   	Span<const uint8_t> expMeans_;
>   
>   	std::map<int32_t, std::vector<uint8_t>> meteringModes_;
> +
> +	int32_t defaultConstraintMode_;
> +	int32_t defaultExposureMode_;
> +	int32_t defaultMeteringMode_;
>   };
>   
>   } /* namespace ipa::rkisp1::algorithms */
> diff --git a/src/ipa/rkisp1/algorithms/algorithm.h b/src/ipa/rkisp1/algorithms/algorithm.h
> index 9454c9a1..c3a002b8 100644
> --- a/src/ipa/rkisp1/algorithms/algorithm.h
> +++ b/src/ipa/rkisp1/algorithms/algorithm.h
> @@ -25,6 +25,8 @@ public:
>   
>   	bool disabled_;
>   	bool supportsRaw_;
> +
> +	ControlInfoMap::Map controls_;
>   };
>   
>   } /* namespace ipa::rkisp1 */
> diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h
> index a70c7eb3..dc876da0 100644
> --- a/src/ipa/rkisp1/ipa_context.h
> +++ b/src/ipa/rkisp1/ipa_context.h
> @@ -114,6 +114,10 @@ struct IPAFrameContext : public FrameContext {
>   		double gain;
>   		double dgain;
>   		bool autoEnabled;
> +		int32_t meteringMode;
> +		int32_t exposureMode;
> +		int32_t constraintMode;
> +		utils::Duration maxShutterSpeed;
>   	} agc;
>   
>   	struct {
> diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp
> index d66dfdd7..3654b5a6 100644
> --- a/src/ipa/rkisp1/rkisp1.cpp
> +++ b/src/ipa/rkisp1/rkisp1.cpp
> @@ -80,6 +80,7 @@ private:
>   	std::map<unsigned int, MappedFrameBuffer> mappedBuffers_;
>   
>   	ControlInfoMap sensorControls_;
> +	ControlInfoMap::Map algoControls_;
>   
>   	/* Interface to the Camera Helper */
>   	std::unique_ptr<CameraSensorHelper> camHelper_;
> @@ -193,6 +194,14 @@ int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision,
>   	if (ret)
>   		return ret;
>   
> +	for (auto const &a : algorithms()) {
> +		Algorithm *algo = static_cast<Algorithm *>(a.get());
> +
> +		/* \todo Avoid merging duplicate controls */
> +		if (!algo->controls_.empty())
> +			algoControls_.merge(ControlInfoMap::Map(algo->controls_));
> +	}
> +
>   	/* Initialize controls. */
>   	updateControls(sensorInfo, sensorControls, ipaControls);
>   
> @@ -377,6 +386,7 @@ void IPARkISP1::updateControls(const IPACameraSensorInfo &sensorInfo,
>   			       ControlInfoMap *ipaControls)
>   {
>   	ControlInfoMap::Map ctrlMap = rkisp1Controls;
> +	ctrlMap.merge(algoControls_);
>   
>   	/*
>   	 * Compute exposure time limits from the V4L2_CID_EXPOSURE control

Patch
diff mbox series

diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
index a1b6eb39..ed4d6330 100644
--- a/src/ipa/rkisp1/algorithms/agc.cpp
+++ b/src/ipa/rkisp1/algorithms/agc.cpp
@@ -11,6 +11,7 @@ 
 #include <chrono>
 #include <cmath>
 #include <tuple>
+#include <vector>
 
 #include <libcamera/base/log.h>
 #include <libcamera/base/utils.h>
@@ -49,6 +50,7 @@  int Agc::parseMeteringModes(IPAContext &context, const YamlObject &tuningData,
 		return -EINVAL;
 	}
 
+	std::vector<ControlValue> availableMeteringModes;
 	for (const auto &[key, value] : yamlMeteringModes.asDict()) {
 		if (controls::AeMeteringModeNameValueMap.find(key) ==
 		    controls::AeMeteringModeNameValueMap.end()) {
@@ -67,9 +69,14 @@  int Agc::parseMeteringModes(IPAContext &context, const YamlObject &tuningData,
 			return -ENODATA;
 		}
 
-		meteringModes_[controls::AeMeteringModeNameValueMap.at(key)] = weights;
+		int32_t control = controls::AeMeteringModeNameValueMap.at(key);
+		meteringModes_[control] = weights;
+		availableMeteringModes.push_back(control);
 	}
 
+	Algorithm::controls_[&controls::AeMeteringMode] =
+		ControlInfo(availableMeteringModes);
+
 	return 0;
 }
 
@@ -131,9 +138,25 @@  int Agc::init(IPAContext &context, const YamlObject &tuningData)
 
 	context.ctrlMap.merge(controls());
 
+	defaultConstraintMode_ = constraintModes().begin()->first;
+	defaultExposureMode_ = exposureModeHelpers().begin()->first;
+	defaultMeteringMode_ = meteringModes_.begin()->first;
+
+	Algorithm::controls_.merge(ControlInfoMap::Map(controls()));
+
 	return 0;
 }
 
+void Agc::configureExposureModeHelpers(IPAContext &context, utils::Duration maxShutterSpeed)
+{
+	for (auto &[id, helper] : exposureModeHelpers()) {
+		helper->configure(context.configuration.sensor.minShutterSpeed,
+				  maxShutterSpeed,
+				  context.configuration.sensor.minAnalogueGain,
+				  context.configuration.sensor.maxAnalogueGain);
+	}
+}
+
 /**
  * \brief Configure the AGC given a configInfo
  * \param[in] context The shared IPA context
@@ -153,12 +176,8 @@  int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)
 	context.activeState.agc.manual.dgain = 1;
 	context.activeState.agc.autoEnabled = !context.configuration.raw;
 
-	/*
-	* \todo We should use the first available mode rather than assume that
-	* the "Normal" modes are present in tuning data.
-	*/
-	context.activeState.agc.constraintMode = controls::ConstraintNormal;
-	context.activeState.agc.exposureMode = controls::ExposureNormal;
+	context.activeState.agc.constraintMode = defaultConstraintMode_;
+	context.activeState.agc.exposureMode = defaultExposureMode_;
 
 	/*
 	 * Define the measurement window for AGC as a centered rectangle
@@ -169,13 +188,7 @@  int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)
 	context.configuration.agc.measureWindow.h_size = 3 * configInfo.outputSize.width / 4;
 	context.configuration.agc.measureWindow.v_size = 3 * configInfo.outputSize.height / 4;
 
-	for (auto &[id, helper] : exposureModeHelpers()) {
-		/* \todo Run this again when FrameDurationLimits is passed in */
-		helper->configure(context.configuration.sensor.minShutterSpeed,
-				  context.configuration.sensor.maxShutterSpeed,
-				  context.configuration.sensor.minAnalogueGain,
-				  context.configuration.sensor.maxAnalogueGain);
-	}
+	configureExposureModeHelpers(context, context.configuration.sensor.maxShutterSpeed);
 
 	return 0;
 }
@@ -223,6 +236,20 @@  void Agc::queueRequest(IPAContext &context,
 		frameContext.agc.exposure = agc.manual.exposure;
 		frameContext.agc.gain = agc.manual.gain;
 	}
+
+	const auto &meteringMode = controls.get(controls::AeMeteringMode);
+	frameContext.agc.meteringMode = meteringMode.value_or(defaultMeteringMode_);
+
+	const auto &exposureMode = controls.get(controls::AeExposureMode);
+	frameContext.agc.exposureMode = exposureMode.value_or(defaultExposureMode_);
+
+	const auto &constraintMode = controls.get(controls::AeConstraintMode);
+	frameContext.agc.constraintMode = constraintMode.value_or(defaultConstraintMode_);
+
+	const auto &frameDurationLimits = controls.get(controls::FrameDurationLimits);
+	frameContext.agc.maxShutterSpeed = frameDurationLimits
+					 ? std::chrono::milliseconds((*frameDurationLimits).back())
+					 : 60ms;
 }
 
 /**
@@ -262,8 +289,7 @@  void Agc::prepare(IPAContext &context, const uint32_t frame,
 		params->meas.hst_config.hist_weight,
 		context.hw->numHistogramWeights
 	};
-	/* \todo Get this from control */
-	std::vector<uint8_t> &modeWeights = meteringModes_.at(controls::MeteringMatrix);
+	std::vector<uint8_t> &modeWeights = meteringModes_.at(frameContext.agc.meteringMode);
 	std::copy(modeWeights.begin(), modeWeights.end(), weights.begin());
 
 	std::stringstream str;
@@ -290,6 +316,7 @@  void Agc::fillMetadata(IPAContext &context, IPAFrameContext &frameContext,
 				     * frameContext.sensor.exposure;
 	metadata.set(controls::AnalogueGain, frameContext.sensor.gain);
 	metadata.set(controls::ExposureTime, exposureTime.get<std::micro>());
+	metadata.set(controls::AeEnable, frameContext.agc.autoEnabled);
 
 	/* \todo Use VBlank value calculated from each frame exposure. */
 	uint32_t vTotal = context.configuration.sensor.size.height
@@ -297,6 +324,10 @@  void Agc::fillMetadata(IPAContext &context, IPAFrameContext &frameContext,
 	utils::Duration frameDuration = context.configuration.sensor.lineDuration
 				      * vTotal;
 	metadata.set(controls::FrameDuration, frameDuration.get<std::micro>());
+
+	metadata.set(controls::AeMeteringMode, frameContext.agc.meteringMode);
+	metadata.set(controls::AeExposureMode, frameContext.agc.exposureMode);
+	metadata.set(controls::AeConstraintMode, frameContext.agc.constraintMode);
 }
 
 void Agc::parseStatistics(const rkisp1_stat_buffer *stats,
@@ -378,6 +409,10 @@  void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,
 
 	ASSERT(stats->meas_type & RKISP1_CIF_ISP_STAT_AUTOEXP);
 
+	utils::Duration maxShutterSpeed = std::min(context.configuration.sensor.maxShutterSpeed,
+						   frameContext.agc.maxShutterSpeed);
+	configureExposureModeHelpers(context, maxShutterSpeed);
+
 	parseStatistics(stats, context);
 
 	/*
diff --git a/src/ipa/rkisp1/algorithms/agc.h b/src/ipa/rkisp1/algorithms/agc.h
index 43e3d5b2..c05ba4be 100644
--- a/src/ipa/rkisp1/algorithms/agc.h
+++ b/src/ipa/rkisp1/algorithms/agc.h
@@ -47,6 +47,7 @@  private:
 	int parseMeteringModes(IPAContext &context, const YamlObject &tuningData,
 			       const char *prop);
 	uint8_t predivider(Size &size);
+	void configureExposureModeHelpers(IPAContext &context, utils::Duration maxShutterSpeed);
 
 	void fillMetadata(IPAContext &context, IPAFrameContext &frameContext,
 			  ControlList &metadata);
@@ -58,6 +59,10 @@  private:
 	Span<const uint8_t> expMeans_;
 
 	std::map<int32_t, std::vector<uint8_t>> meteringModes_;
+
+	int32_t defaultConstraintMode_;
+	int32_t defaultExposureMode_;
+	int32_t defaultMeteringMode_;
 };
 
 } /* namespace ipa::rkisp1::algorithms */
diff --git a/src/ipa/rkisp1/algorithms/algorithm.h b/src/ipa/rkisp1/algorithms/algorithm.h
index 9454c9a1..c3a002b8 100644
--- a/src/ipa/rkisp1/algorithms/algorithm.h
+++ b/src/ipa/rkisp1/algorithms/algorithm.h
@@ -25,6 +25,8 @@  public:
 
 	bool disabled_;
 	bool supportsRaw_;
+
+	ControlInfoMap::Map controls_;
 };
 
 } /* namespace ipa::rkisp1 */
diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h
index a70c7eb3..dc876da0 100644
--- a/src/ipa/rkisp1/ipa_context.h
+++ b/src/ipa/rkisp1/ipa_context.h
@@ -114,6 +114,10 @@  struct IPAFrameContext : public FrameContext {
 		double gain;
 		double dgain;
 		bool autoEnabled;
+		int32_t meteringMode;
+		int32_t exposureMode;
+		int32_t constraintMode;
+		utils::Duration maxShutterSpeed;
 	} agc;
 
 	struct {
diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp
index d66dfdd7..3654b5a6 100644
--- a/src/ipa/rkisp1/rkisp1.cpp
+++ b/src/ipa/rkisp1/rkisp1.cpp
@@ -80,6 +80,7 @@  private:
 	std::map<unsigned int, MappedFrameBuffer> mappedBuffers_;
 
 	ControlInfoMap sensorControls_;
+	ControlInfoMap::Map algoControls_;
 
 	/* Interface to the Camera Helper */
 	std::unique_ptr<CameraSensorHelper> camHelper_;
@@ -193,6 +194,14 @@  int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision,
 	if (ret)
 		return ret;
 
+	for (auto const &a : algorithms()) {
+		Algorithm *algo = static_cast<Algorithm *>(a.get());
+
+		/* \todo Avoid merging duplicate controls */
+		if (!algo->controls_.empty())
+			algoControls_.merge(ControlInfoMap::Map(algo->controls_));
+	}
+
 	/* Initialize controls. */
 	updateControls(sensorInfo, sensorControls, ipaControls);
 
@@ -377,6 +386,7 @@  void IPARkISP1::updateControls(const IPACameraSensorInfo &sensorInfo,
 			       ControlInfoMap *ipaControls)
 {
 	ControlInfoMap::Map ctrlMap = rkisp1Controls;
+	ctrlMap.merge(algoControls_);
 
 	/*
 	 * Compute exposure time limits from the V4L2_CID_EXPOSURE control