[v5,46/47] ipa: simple: agc: Port to `AgcAlgorithm`
diff mbox series

Message ID 20260817114349.994123-47-barnabas.pocze@ideasonboard.com
State Superseded
Headers show
Series
  • ipa: libipa: agc rework
Related show

Commit Message

Barnabás Pőcze Aug. 17, 2026, 11:43 a.m. UTC
Use the `AgcAlgorithm` class to implement the agc algorithm.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
---
 src/ipa/simple/algorithms/agc.cpp | 136 +++++++++++++++++-------------
 src/ipa/simple/algorithms/agc.h   |  12 ++-
 src/ipa/simple/ipa_context.h      |  20 +----
 src/ipa/simple/soft_simple.cpp    |  55 +-----------
 4 files changed, 93 insertions(+), 130 deletions(-)

Comments

Stefan Klug Aug. 20, 2026, 11:23 a.m. UTC | #1
Hi Barnabás,

Quoting Barnabás Pőcze (2026-08-17 13:43:47)
> Use the `AgcAlgorithm` class to implement the agc algorithm.
> 
> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
> Reviewed-by: Milan Zamazal <mzamazal@redhat.com>

I skimmed through it and all looks good. So I guess it is all fine.

Acked-by: Stefan Klug <stefan.klug@ideasonboard.com>

Best regards,
Stefan

> ---
>  src/ipa/simple/algorithms/agc.cpp | 136 +++++++++++++++++-------------
>  src/ipa/simple/algorithms/agc.h   |  12 ++-
>  src/ipa/simple/ipa_context.h      |  20 +----
>  src/ipa/simple/soft_simple.cpp    |  55 +-----------
>  4 files changed, 93 insertions(+), 130 deletions(-)
> 
> diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp
> index e455141240..4abd471dcf 100644
> --- a/src/ipa/simple/algorithms/agc.cpp
> +++ b/src/ipa/simple/algorithms/agc.cpp
> @@ -11,30 +11,76 @@
>  
>  #include <libipa/histogram.h>
>  
> -#include "control_ids.h"
> -
>  namespace libcamera {
>  
>  LOG_DEFINE_CATEGORY(IPASoftExposure)
>  
>  namespace ipa::soft::algorithms {
>  
> +namespace {
> +
> +class AgcTraits : public AgcMeanLuminance::Traits
> +{
> +public:
> +        AgcTraits(const Histogram &yHist)
> +                : yHist_(yHist)
> +        {
> +        }
> +
> +        double estimateLuminance(double gain) const override
> +        {
> +               /*
> +                * TODO: Improve by asking the weight of saturating and non-saturating
> +                * bins directly from the histogram
> +                */
> +               double sum = 0;
> +
> +               for (size_t i = 0; i < yHist_.bins(); i++)
> +                       sum += std::min<double>(yHist_.bins(), i * gain) * yHist_[i];
> +
> +               return sum / yHist_.total() / yHist_.bins();
> +        }
> +
> +private:
> +        const Histogram &yHist_;
> +};
> +
> +} /* namespace */
> +
> +int Agc::init(IPAContext &context, const ValueNode &tuningData)
> +{
> +       int ret = agc_.init(tuningData, context.camHelper.get());
> +       if (ret)
> +               return ret;
> +
> +       return agc_.configure(context.configuration.agc, context.activeState.agc, {
> +               .sensorInfo = context.sensorInfo,
> +               .sensorControls = context.sensorControls,
> +               .ctrlMap = context.ctrlMap,
> +               .autoAllowed = true,
> +       });
> +}
> +
>  int Agc::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo)
>  {
> -       agc_.setLimits({
> -               .exposure = {
> -                       context.configuration.agc.exposureMin,
> -                       context.configuration.agc.exposureMax,
> -               },
> -               .gain = {
> -                       context.configuration.agc.againMin,
> -                       context.configuration.agc.againMax,
> -               },
> -               .gainMinStep = context.configuration.agc.againMinStep,
> -               .gain1 = context.configuration.agc.again10,
> +       return agc_.configure(context.configuration.agc, context.activeState.agc, {
> +               .sensorInfo = context.sensorInfo,
> +               .sensorControls = context.sensorControls,
> +               .ctrlMap = context.ctrlMap,
> +               .autoAllowed = true,
>         });
> +}
> +
> +void Agc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame,
> +                      IPAFrameContext &frameContext, const ControlList &controls)
> +{
> +       agc_.queueRequest(context.configuration.agc, context.activeState.agc, frameContext.agc, controls);
> +}
>  
> -       return 0;
> +void Agc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
> +                 IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params)
> +{
> +       agc_.prepare(context.activeState.agc, frameContext.agc);
>  }
>  
>  void Agc::process(IPAContext &context,
> @@ -43,51 +89,25 @@ void Agc::process(IPAContext &context,
>                   const SwIspStats *stats,
>                   ControlList &metadata)
>  {
> -       utils::Duration exposureTime =
> -               context.configuration.agc.lineDuration * frameContext.sensor.exposure;
> -       metadata.set(controls::ExposureTime, exposureTime.get<std::micro>());
> -       metadata.set(controls::AnalogueGain, frameContext.sensor.gain);
> -
> -       if (!context.activeState.agc.valid) {
> -               /*
> -                * Init active-state from sensor values in case updateExposure()
> -                * does not run for the first frame.
> -                */
> -               context.activeState.agc.exposure = frameContext.sensor.exposure;
> -               context.activeState.agc.again = frameContext.sensor.gain;
> -               context.activeState.agc.valid = true;
> -       }
> -
> -       if (!stats->valid) {
> -               /*
> -                * Use the new exposure and gain values calculated the last time
> -                * there were valid stats.
> -                */
> -               frameContext.agc.exposure = context.activeState.agc.exposure;
> -               frameContext.agc.gain = context.activeState.agc.again;
> -               return;
> +       if (stats->valid) {
> +               auto histogram = stats->yHistogram;
> +
> +               const unsigned int blackLevelHistIdx =
> +                       context.activeState.blc.level * std::size(histogram) / 256;
> +               for (unsigned int i = 0; i < blackLevelHistIdx; i++)
> +                       histogram[blackLevelHistIdx] += histogram[i];
> +
> +               Histogram yHist({ histogram.begin() + blackLevelHistIdx, histogram.end() });
> +
> +               agc_.process(context.configuration.agc, context.activeState.agc, frameContext.agc, {{
> +                       .traits = AgcTraits(yHist),
> +                       .yHist = yHist,
> +                       .exposure = frameContext.sensor.exposure,
> +                       .gain = frameContext.sensor.gain,
> +               }}, metadata);
> +       } else {
> +               agc_.process(context.configuration.agc, context.activeState.agc, frameContext.agc, {}, metadata);
>         }
> -
> -       auto histogram = stats->yHistogram;
> -       const unsigned int blackLevelHistIdx =
> -               context.activeState.blc.level * std::size(histogram) / 256;
> -
> -       for (unsigned int i = 0; i < blackLevelHistIdx; i++)
> -               histogram[blackLevelHistIdx] += histogram[i];
> -
> -       const auto &newEv = agc_.calculateNewEv({
> -               .yHist = {
> -                       { histogram.begin() + blackLevelHistIdx, histogram.end() },
> -               },
> -               .exposure = frameContext.sensor.exposure,
> -               .gain = frameContext.sensor.gain,
> -       });
> -
> -       frameContext.agc.exposure = newEv.exposure;
> -       frameContext.agc.gain = newEv.analogueGain;
> -
> -       context.activeState.agc.exposure = frameContext.agc.exposure;
> -       context.activeState.agc.again = frameContext.agc.gain;
>  }
>  
>  REGISTER_IPA_ALGORITHM(Agc, "Agc")
> diff --git a/src/ipa/simple/algorithms/agc.h b/src/ipa/simple/algorithms/agc.h
> index 2e156e135c..6e37ce6eff 100644
> --- a/src/ipa/simple/algorithms/agc.h
> +++ b/src/ipa/simple/algorithms/agc.h
> @@ -9,7 +9,7 @@
>  
>  #include "algorithm.h"
>  
> -#include <libipa/agc_msv.h>
> +#include <libipa/agc.h>
>  
>  namespace libcamera {
>  
> @@ -18,15 +18,23 @@ namespace ipa::soft::algorithms {
>  class Agc : public Algorithm
>  {
>  public:
> +       int init(IPAContext &context, const ValueNode &tuningData) override;
> +
>         int configure(IPAContext &context, const IPAConfigInfo &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, DebayerParams *params) override;
> +
>         void process(IPAContext &context, const uint32_t frame,
>                      IPAFrameContext &frameContext,
>                      const SwIspStats *stats,
>                      ControlList &metadata) override;
>  
>  private:
> -       AgcMSV agc_;
> +       AgcAlgorithm agc_;
>  };
>  
>  } /* namespace ipa::soft::algorithms */
> diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h
> index 651c62bf0f..f288a73e9d 100644
> --- a/src/ipa/simple/ipa_context.h
> +++ b/src/ipa/simple/ipa_context.h
> @@ -17,6 +17,7 @@
>  #include "libcamera/internal/matrix.h"
>  #include "libcamera/internal/vector.h"
>  
> +#include <libipa/agc.h>
>  #include <libipa/awb.h>
>  #include <libipa/camera_sensor_helper.h>
>  #include <libipa/ccm.h>
> @@ -29,11 +30,7 @@ namespace libcamera {
>  namespace ipa::soft {
>  
>  struct IPASessionConfiguration {
> -       struct {
> -               uint32_t exposureMin, exposureMax;
> -               double againMin, againMax, again10, againMinStep;
> -               utils::Duration lineDuration;
> -       } agc;
> +       ipa::agc::Session agc;
>         struct {
>                 std::optional<uint8_t> level;
>         } black;
> @@ -42,12 +39,7 @@ struct IPASessionConfiguration {
>  struct IPAActiveState {
>         ipa::awb::ActiveState awb;
>         ipa::ccm::ActiveState ccm;
> -
> -       struct {
> -               uint32_t exposure;
> -               double again;
> -               bool valid;
> -       } agc;
> +       ipa::agc::ActiveState agc;
>  
>         struct {
>                 uint8_t level;
> @@ -68,11 +60,7 @@ struct IPAActiveState {
>  struct IPAFrameContext : public FrameContext {
>         ipa::awb::FrameContext awb;
>         ipa::ccm::FrameContext ccm;
> -
> -       struct {
> -               uint32_t exposure;
> -               double gain;
> -       } agc;
> +       ipa::agc::FrameContext agc;
>  
>         struct {
>                 uint32_t exposure;
> diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
> index 67978c8be8..081fbb724c 100644
> --- a/src/ipa/simple/soft_simple.cpp
> +++ b/src/ipa/simple/soft_simple.cpp
> @@ -107,6 +107,7 @@ int IPASoftSimple::init(const IPASettings &settings,
>         }
>  
>         context_.sensorInfo = sensorInfo;
> +       context_.sensorControls = sensorControls;
>  
>         /* Load the tuning data file */
>         File file(settings.configurationFile);
> @@ -180,22 +181,6 @@ int IPASoftSimple::init(const IPASettings &settings,
>         ControlInfoMap::Map ctrlMap = context_.ctrlMap;
>         *ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);
>  
> -       /*
> -        * Check if the sensor driver supports the controls required by the
> -        * Soft IPA.
> -        * Don't save the min and max control values yet, as e.g. the limits
> -        * for V4L2_CID_EXPOSURE depend on the configured sensor resolution.
> -        */
> -       if (sensorControls.find(V4L2_CID_EXPOSURE) == sensorControls.end()) {
> -               LOG(IPASoft, Error) << "Don't have exposure control";
> -               return -EINVAL;
> -       }
> -
> -       if (sensorControls.find(V4L2_CID_ANALOGUE_GAIN) == sensorControls.end()) {
> -               LOG(IPASoft, Error) << "Don't have gain control";
> -               return -EINVAL;
> -       }
> -
>         return 0;
>  }
>  
> @@ -203,55 +188,17 @@ int IPASoftSimple::configure(const IPAConfigInfo &configInfo, ControlInfoMap *ip
>  {
>         context_.sensorControls = configInfo.sensorControls;
>  
> -       const ControlInfo &exposureInfo = context_.sensorControls.find(V4L2_CID_EXPOSURE)->second;
> -       const ControlInfo &gainInfo = context_.sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;
> -
>         /* Clear the IPA context before the streaming session. */
>         context_.configuration = {};
>         context_.activeState = {};
>         context_.frameContexts.clear();
>  
> -       context_.configuration.agc.lineDuration =
> -               context_.sensorInfo.minLineLength * 1.0s / context_.sensorInfo.pixelRate;
> -       context_.configuration.agc.exposureMin = exposureInfo.min().get<int32_t>();
> -       context_.configuration.agc.exposureMax = exposureInfo.max().get<int32_t>();
> -       if (!context_.configuration.agc.exposureMin) {
> -               LOG(IPASoft, Warning) << "Minimum exposure is zero, that can't be linear";
> -               context_.configuration.agc.exposureMin = 1;
> -       }
> -
> -       int32_t againMin = gainInfo.min().get<int32_t>();
> -       int32_t againMax = gainInfo.max().get<int32_t>();
> -       int32_t againDef = gainInfo.def().get<int32_t>();
> -
> -       if (context_.camHelper) {
> -               context_.configuration.agc.againMin = context_.camHelper->gain(againMin);
> -               context_.configuration.agc.againMax = context_.camHelper->gain(againMax);
> -               context_.configuration.agc.again10 = std::max(context_.configuration.agc.againMin, 1.0);
> -               context_.configuration.agc.againMinStep =
> -                       (context_.configuration.agc.againMax -
> -                        context_.configuration.agc.againMin) /
> -                       100.0;
> -       } else {
> -               context_.configuration.agc.againMax = againMax;
> -               context_.configuration.agc.again10 = againDef;
> -               context_.configuration.agc.againMin = againMin;
> -               context_.configuration.agc.againMinStep = 1.0;
> -       }
> -
>         for (const auto &algo : algorithms()) {
>                 int ret = algo->configure(context_, configInfo);
>                 if (ret)
>                         return ret;
>         }
>  
> -       LOG(IPASoft, Info)
> -               << "Exposure " << context_.configuration.agc.exposureMin << "-"
> -               << context_.configuration.agc.exposureMax
> -               << ", gain " << context_.configuration.agc.againMin << "-"
> -               << context_.configuration.agc.againMax
> -               << " (" << context_.configuration.agc.againMinStep << ")";
> -
>         *ipaControls = { ControlInfoMap::Map(context_.ctrlMap), controls::controls };
>  
>         return 0;
> -- 
> 2.55.0
>

Patch
diff mbox series

diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp
index e455141240..4abd471dcf 100644
--- a/src/ipa/simple/algorithms/agc.cpp
+++ b/src/ipa/simple/algorithms/agc.cpp
@@ -11,30 +11,76 @@ 
 
 #include <libipa/histogram.h>
 
-#include "control_ids.h"
-
 namespace libcamera {
 
 LOG_DEFINE_CATEGORY(IPASoftExposure)
 
 namespace ipa::soft::algorithms {
 
+namespace {
+
+class AgcTraits : public AgcMeanLuminance::Traits
+{
+public:
+        AgcTraits(const Histogram &yHist)
+                : yHist_(yHist)
+        {
+        }
+
+        double estimateLuminance(double gain) const override
+        {
+		/*
+		 * TODO: Improve by asking the weight of saturating and non-saturating
+		 * bins directly from the histogram
+		 */
+		double sum = 0;
+
+		for (size_t i = 0; i < yHist_.bins(); i++)
+			sum += std::min<double>(yHist_.bins(), i * gain) * yHist_[i];
+
+		return sum / yHist_.total() / yHist_.bins();
+        }
+
+private:
+        const Histogram &yHist_;
+};
+
+} /* namespace */
+
+int Agc::init(IPAContext &context, const ValueNode &tuningData)
+{
+	int ret = agc_.init(tuningData, context.camHelper.get());
+	if (ret)
+		return ret;
+
+	return agc_.configure(context.configuration.agc, context.activeState.agc, {
+		.sensorInfo = context.sensorInfo,
+		.sensorControls = context.sensorControls,
+		.ctrlMap = context.ctrlMap,
+		.autoAllowed = true,
+	});
+}
+
 int Agc::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo)
 {
-	agc_.setLimits({
-		.exposure = {
-			context.configuration.agc.exposureMin,
-			context.configuration.agc.exposureMax,
-		},
-		.gain = {
-			context.configuration.agc.againMin,
-			context.configuration.agc.againMax,
-		},
-		.gainMinStep = context.configuration.agc.againMinStep,
-		.gain1 = context.configuration.agc.again10,
+	return agc_.configure(context.configuration.agc, context.activeState.agc, {
+		.sensorInfo = context.sensorInfo,
+		.sensorControls = context.sensorControls,
+		.ctrlMap = context.ctrlMap,
+		.autoAllowed = true,
 	});
+}
+
+void Agc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame,
+		       IPAFrameContext &frameContext, const ControlList &controls)
+{
+	agc_.queueRequest(context.configuration.agc, context.activeState.agc, frameContext.agc, controls);
+}
 
-	return 0;
+void Agc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
+		  IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params)
+{
+	agc_.prepare(context.activeState.agc, frameContext.agc);
 }
 
 void Agc::process(IPAContext &context,
@@ -43,51 +89,25 @@  void Agc::process(IPAContext &context,
 		  const SwIspStats *stats,
 		  ControlList &metadata)
 {
-	utils::Duration exposureTime =
-		context.configuration.agc.lineDuration * frameContext.sensor.exposure;
-	metadata.set(controls::ExposureTime, exposureTime.get<std::micro>());
-	metadata.set(controls::AnalogueGain, frameContext.sensor.gain);
-
-	if (!context.activeState.agc.valid) {
-		/*
-		 * Init active-state from sensor values in case updateExposure()
-		 * does not run for the first frame.
-		 */
-		context.activeState.agc.exposure = frameContext.sensor.exposure;
-		context.activeState.agc.again = frameContext.sensor.gain;
-		context.activeState.agc.valid = true;
-	}
-
-	if (!stats->valid) {
-		/*
-		 * Use the new exposure and gain values calculated the last time
-		 * there were valid stats.
-		 */
-		frameContext.agc.exposure = context.activeState.agc.exposure;
-		frameContext.agc.gain = context.activeState.agc.again;
-		return;
+	if (stats->valid) {
+		auto histogram = stats->yHistogram;
+
+		const unsigned int blackLevelHistIdx =
+			context.activeState.blc.level * std::size(histogram) / 256;
+		for (unsigned int i = 0; i < blackLevelHistIdx; i++)
+			histogram[blackLevelHistIdx] += histogram[i];
+
+		Histogram yHist({ histogram.begin() + blackLevelHistIdx, histogram.end() });
+
+		agc_.process(context.configuration.agc, context.activeState.agc, frameContext.agc, {{
+			.traits = AgcTraits(yHist),
+			.yHist = yHist,
+			.exposure = frameContext.sensor.exposure,
+			.gain = frameContext.sensor.gain,
+		}}, metadata);
+	} else {
+		agc_.process(context.configuration.agc, context.activeState.agc, frameContext.agc, {}, metadata);
 	}
-
-	auto histogram = stats->yHistogram;
-	const unsigned int blackLevelHistIdx =
-		context.activeState.blc.level * std::size(histogram) / 256;
-
-	for (unsigned int i = 0; i < blackLevelHistIdx; i++)
-		histogram[blackLevelHistIdx] += histogram[i];
-
-	const auto &newEv = agc_.calculateNewEv({
-		.yHist = {
-			{ histogram.begin() + blackLevelHistIdx, histogram.end() },
-		},
-		.exposure = frameContext.sensor.exposure,
-		.gain = frameContext.sensor.gain,
-	});
-
-	frameContext.agc.exposure = newEv.exposure;
-	frameContext.agc.gain = newEv.analogueGain;
-
-	context.activeState.agc.exposure = frameContext.agc.exposure;
-	context.activeState.agc.again = frameContext.agc.gain;
 }
 
 REGISTER_IPA_ALGORITHM(Agc, "Agc")
diff --git a/src/ipa/simple/algorithms/agc.h b/src/ipa/simple/algorithms/agc.h
index 2e156e135c..6e37ce6eff 100644
--- a/src/ipa/simple/algorithms/agc.h
+++ b/src/ipa/simple/algorithms/agc.h
@@ -9,7 +9,7 @@ 
 
 #include "algorithm.h"
 
-#include <libipa/agc_msv.h>
+#include <libipa/agc.h>
 
 namespace libcamera {
 
@@ -18,15 +18,23 @@  namespace ipa::soft::algorithms {
 class Agc : public Algorithm
 {
 public:
+	int init(IPAContext &context, const ValueNode &tuningData) override;
+
 	int configure(IPAContext &context, const IPAConfigInfo &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, DebayerParams *params) override;
+
 	void process(IPAContext &context, const uint32_t frame,
 		     IPAFrameContext &frameContext,
 		     const SwIspStats *stats,
 		     ControlList &metadata) override;
 
 private:
-	AgcMSV agc_;
+	AgcAlgorithm agc_;
 };
 
 } /* namespace ipa::soft::algorithms */
diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h
index 651c62bf0f..f288a73e9d 100644
--- a/src/ipa/simple/ipa_context.h
+++ b/src/ipa/simple/ipa_context.h
@@ -17,6 +17,7 @@ 
 #include "libcamera/internal/matrix.h"
 #include "libcamera/internal/vector.h"
 
+#include <libipa/agc.h>
 #include <libipa/awb.h>
 #include <libipa/camera_sensor_helper.h>
 #include <libipa/ccm.h>
@@ -29,11 +30,7 @@  namespace libcamera {
 namespace ipa::soft {
 
 struct IPASessionConfiguration {
-	struct {
-		uint32_t exposureMin, exposureMax;
-		double againMin, againMax, again10, againMinStep;
-		utils::Duration lineDuration;
-	} agc;
+	ipa::agc::Session agc;
 	struct {
 		std::optional<uint8_t> level;
 	} black;
@@ -42,12 +39,7 @@  struct IPASessionConfiguration {
 struct IPAActiveState {
 	ipa::awb::ActiveState awb;
 	ipa::ccm::ActiveState ccm;
-
-	struct {
-		uint32_t exposure;
-		double again;
-		bool valid;
-	} agc;
+	ipa::agc::ActiveState agc;
 
 	struct {
 		uint8_t level;
@@ -68,11 +60,7 @@  struct IPAActiveState {
 struct IPAFrameContext : public FrameContext {
 	ipa::awb::FrameContext awb;
 	ipa::ccm::FrameContext ccm;
-
-	struct {
-		uint32_t exposure;
-		double gain;
-	} agc;
+	ipa::agc::FrameContext agc;
 
 	struct {
 		uint32_t exposure;
diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
index 67978c8be8..081fbb724c 100644
--- a/src/ipa/simple/soft_simple.cpp
+++ b/src/ipa/simple/soft_simple.cpp
@@ -107,6 +107,7 @@  int IPASoftSimple::init(const IPASettings &settings,
 	}
 
 	context_.sensorInfo = sensorInfo;
+	context_.sensorControls = sensorControls;
 
 	/* Load the tuning data file */
 	File file(settings.configurationFile);
@@ -180,22 +181,6 @@  int IPASoftSimple::init(const IPASettings &settings,
 	ControlInfoMap::Map ctrlMap = context_.ctrlMap;
 	*ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);
 
-	/*
-	 * Check if the sensor driver supports the controls required by the
-	 * Soft IPA.
-	 * Don't save the min and max control values yet, as e.g. the limits
-	 * for V4L2_CID_EXPOSURE depend on the configured sensor resolution.
-	 */
-	if (sensorControls.find(V4L2_CID_EXPOSURE) == sensorControls.end()) {
-		LOG(IPASoft, Error) << "Don't have exposure control";
-		return -EINVAL;
-	}
-
-	if (sensorControls.find(V4L2_CID_ANALOGUE_GAIN) == sensorControls.end()) {
-		LOG(IPASoft, Error) << "Don't have gain control";
-		return -EINVAL;
-	}
-
 	return 0;
 }
 
@@ -203,55 +188,17 @@  int IPASoftSimple::configure(const IPAConfigInfo &configInfo, ControlInfoMap *ip
 {
 	context_.sensorControls = configInfo.sensorControls;
 
-	const ControlInfo &exposureInfo = context_.sensorControls.find(V4L2_CID_EXPOSURE)->second;
-	const ControlInfo &gainInfo = context_.sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;
-
 	/* Clear the IPA context before the streaming session. */
 	context_.configuration = {};
 	context_.activeState = {};
 	context_.frameContexts.clear();
 
-	context_.configuration.agc.lineDuration =
-		context_.sensorInfo.minLineLength * 1.0s / context_.sensorInfo.pixelRate;
-	context_.configuration.agc.exposureMin = exposureInfo.min().get<int32_t>();
-	context_.configuration.agc.exposureMax = exposureInfo.max().get<int32_t>();
-	if (!context_.configuration.agc.exposureMin) {
-		LOG(IPASoft, Warning) << "Minimum exposure is zero, that can't be linear";
-		context_.configuration.agc.exposureMin = 1;
-	}
-
-	int32_t againMin = gainInfo.min().get<int32_t>();
-	int32_t againMax = gainInfo.max().get<int32_t>();
-	int32_t againDef = gainInfo.def().get<int32_t>();
-
-	if (context_.camHelper) {
-		context_.configuration.agc.againMin = context_.camHelper->gain(againMin);
-		context_.configuration.agc.againMax = context_.camHelper->gain(againMax);
-		context_.configuration.agc.again10 = std::max(context_.configuration.agc.againMin, 1.0);
-		context_.configuration.agc.againMinStep =
-			(context_.configuration.agc.againMax -
-			 context_.configuration.agc.againMin) /
-			100.0;
-	} else {
-		context_.configuration.agc.againMax = againMax;
-		context_.configuration.agc.again10 = againDef;
-		context_.configuration.agc.againMin = againMin;
-		context_.configuration.agc.againMinStep = 1.0;
-	}
-
 	for (const auto &algo : algorithms()) {
 		int ret = algo->configure(context_, configInfo);
 		if (ret)
 			return ret;
 	}
 
-	LOG(IPASoft, Info)
-		<< "Exposure " << context_.configuration.agc.exposureMin << "-"
-		<< context_.configuration.agc.exposureMax
-		<< ", gain " << context_.configuration.agc.againMin << "-"
-		<< context_.configuration.agc.againMax
-		<< " (" << context_.configuration.agc.againMinStep << ")";
-
 	*ipaControls = { ControlInfoMap::Map(context_.ctrlMap), controls::controls };
 
 	return 0;