[RFC,v1,3/8] ipa: libipa: agc: Calculate vblank and frame duration sooner
diff mbox series

Message ID 20260827104108.1432632-4-barnabas.pocze@ideasonboard.com
State New
Headers show
Series
  • ipa: libipa: agc: Take exposure margin into account
Related show

Commit Message

Barnabás Pőcze Aug. 27, 2026, 10:41 a.m. UTC
Calculating the vblank and frame duration is problematic in `process()`
because at the moment it is calculated for an already finished frame
based on the new suggested exposure time.

Instead, move the calculation to `prepare()` where the frame's
exposure and gain are finalized.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
---
 src/ipa/ipu3/algorithms/agc.cpp     |  2 +-
 src/ipa/libipa/agc.cpp              | 49 ++++++++++-------------------
 src/ipa/libipa/agc.h                |  6 ++--
 src/ipa/mali-c55/algorithms/agc.cpp |  2 +-
 src/ipa/rkisp1/algorithms/agc.cpp   |  2 +-
 src/ipa/softisp/algorithms/agc.cpp  |  2 +-
 6 files changed, 22 insertions(+), 41 deletions(-)

Comments

Stefan Klug Aug. 27, 2026, 1:27 p.m. UTC | #1
Hi Barnabás,

Quoting Barnabás Pőcze (2026-08-27 12:41:03)
> Calculating the vblank and frame duration is problematic in `process()`
> because at the moment it is calculated for an already finished frame
> based on the new suggested exposure time.
> 
> Instead, move the calculation to `prepare()` where the frame's
> exposure and gain are finalized.

This is mostly a duplicate of https://patchwork.libcamera.org/patch/26358/
(or the rebased version of it:
https://git.ideasonboard.com/sklug/libcamera/commit/cd346af78bc3b0aa3e5aa08e41b54b93f6ebf86e )


Could we keep the processFrameDuration() function around for now? That
would lower the rebasing pain for me as I still have two places
where that function gets called. Maybe the first one will become
unnecessary...

Best regards,
Stefan



> 
> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
> ---
>  src/ipa/ipu3/algorithms/agc.cpp     |  2 +-
>  src/ipa/libipa/agc.cpp              | 49 ++++++++++-------------------
>  src/ipa/libipa/agc.h                |  6 ++--
>  src/ipa/mali-c55/algorithms/agc.cpp |  2 +-
>  src/ipa/rkisp1/algorithms/agc.cpp   |  2 +-
>  src/ipa/softisp/algorithms/agc.cpp  |  2 +-
>  6 files changed, 22 insertions(+), 41 deletions(-)
> 
> diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp
> index b0f535b990..b4b49aa638 100644
> --- a/src/ipa/ipu3/algorithms/agc.cpp
> +++ b/src/ipa/ipu3/algorithms/agc.cpp
> @@ -110,7 +110,7 @@ void Agc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
>                   IPAFrameContext &frameContext,
>                   [[maybe_unused]] ipu3_uapi_params *params)
>  {
> -       agc_.prepare(context.activeState.agc, frameContext.agc);
> +       agc_.prepare(context.configuration.agc, context.activeState.agc, frameContext.agc);
>  }
>  
>  Histogram Agc::parseStatistics(const ipu3_uapi_stats_3a *stats,
> diff --git a/src/ipa/libipa/agc.cpp b/src/ipa/libipa/agc.cpp
> index fab34152cc..b969118063 100644
> --- a/src/ipa/libipa/agc.cpp
> +++ b/src/ipa/libipa/agc.cpp
> @@ -650,6 +650,7 @@ void AgcAlgorithm::queueRequest(const agc::Session &session, agc::ActiveState &s
>  
>  /**
>   * \brief Prepare a frame
> + * \param[in] session The agc session configuration
>   * \param[in] state The agc active state
>   * \param[in] frameContext The agc frame context
>   *
> @@ -659,11 +660,10 @@ void AgcAlgorithm::queueRequest(const agc::Session &session, agc::ActiveState &s
>   * \ref agc::FrameContext::gain "frameContext.gain" will be finalized
>   * and may be used by the caller (see agc::prepareControls()).
>   *
> - * \todo Finalize \ref agc::FrameContext::vblank "frameContext.vblank" as well
> - *
>   * \sa Algorithm::prepare()
>   */
> -void AgcAlgorithm::prepare(agc::ActiveState &state, agc::FrameContext &frameContext)
> +void AgcAlgorithm::prepare(const agc::Session& session, agc::ActiveState &state,
> +                          agc::FrameContext &frameContext)
>  {
>         uint32_t activeAutoExposure = state.automatic.exposure;
>         double activeAutoGain = state.automatic.gain;
> @@ -694,6 +694,19 @@ void AgcAlgorithm::prepare(agc::ActiveState &state, agc::FrameContext &frameCont
>         }
>  
>         frameContext.yTarget = state.automatic.yTarget;
> +
> +       /*
> +       * Expand the target frame duration so that we do not run faster than
> +       * the minimum frame duration when we have short exposures.
> +       */
> +       const auto frameDuration = std::max<uint32_t>(
> +               frameContext.minFrameDuration / session.lineDuration,
> +               frameContext.exposure);
> +
> +       frameContext.vblank = frameDuration - session.sensor.outputSize.height;
> +
> +       /* Update frame duration accounting for line length quantization. */
> +       frameContext.frameDuration = frameDuration * session.lineDuration;
>  }
>  
>  /**
> @@ -724,7 +737,6 @@ void AgcAlgorithm::process(const agc::Session &session, agc::ActiveState &state,
>                            ControlList &metadata)
>  {
>         if (!params) {
> -               processFrameDuration(session, frameContext, frameContext.minFrameDuration);
>                 fillMetadata(session, frameContext, metadata);
>                 return;
>         }
> @@ -828,38 +840,9 @@ void AgcAlgorithm::process(const agc::Session &session, agc::ActiveState &state,
>                 << "quantization-gain: " << state.automatic.quantizationGain << ", "
>                 << "digital-gain: " << state.automatic.digitalGain;
>  
> -       /*
> -        * Expand the target frame duration so that we do not run faster than
> -        * the minimum frame duration when we have short exposures.
> -        */
> -       processFrameDuration(session, frameContext,
> -                            std::max(frameContext.minFrameDuration, newExposureTime));
> -
>         fillMetadata(session, frameContext, metadata);
>  }
>  
> -/**
> - * \brief Process frame duration and compute vblank
> - * \param[in] session The session parameters
> - * \param[in] frameContext The current frame context
> - * \param[in] frameDuration The target frame duration
> - *
> - * Compute and populate vblank from the target frame duration.
> - */
> -void AgcAlgorithm::processFrameDuration(const agc::Session &session,
> -                                       agc::FrameContext &frameContext,
> -                                       utils::Duration frameDuration)
> -{
> -       const utils::Duration &lineDuration = session.lineDuration;
> -
> -       frameContext.vblank =
> -               (frameDuration / lineDuration) - session.sensor.outputSize.height;
> -
> -       /* Update frame duration accounting for line length quantization. */
> -       frameContext.frameDuration =
> -               (session.sensor.outputSize.height + frameContext.vblank) * lineDuration;
> -}
> -
>  void AgcAlgorithm::fillMetadata(const agc::Session &session,
>                                 const agc::FrameContext &frameContext,
>                                 ControlList &metadata)
> diff --git a/src/ipa/libipa/agc.h b/src/ipa/libipa/agc.h
> index a5700d2625..9f95f23ea8 100644
> --- a/src/ipa/libipa/agc.h
> +++ b/src/ipa/libipa/agc.h
> @@ -146,16 +146,14 @@ public:
>         void queueRequest(const agc::Session &session, agc::ActiveState &state,
>                           agc::FrameContext &frameContext, const ControlList &controls);
>  
> -       void prepare(agc::ActiveState &state, agc::FrameContext &frameContext);
> +       void prepare(const agc::Session& session, agc::ActiveState &state,
> +                    agc::FrameContext &frameContext);
>  
>         void process(const agc::Session &session, agc::ActiveState &state,
>                      agc::FrameContext &frameContext, std::optional<ProcessParams> &&params,
>                      ControlList &metadata);
>  
>  private:
> -       void processFrameDuration(const agc::Session &session,
> -                                 agc::FrameContext &frameContext,
> -                                 utils::Duration frameDuration);
>         void fillMetadata(const agc::Session &session,
>                           const agc::FrameContext &frameContext,
>                           ControlList &metadata);
> diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp
> index 093c8c6fea..bb1683036f 100644
> --- a/src/ipa/mali-c55/algorithms/agc.cpp
> +++ b/src/ipa/mali-c55/algorithms/agc.cpp
> @@ -207,7 +207,7 @@ void Agc::fillWeightsArrayBuffer(MaliC55Params *params, const enum MaliC55Blocks
>  void Agc::prepare(IPAContext &context, const uint32_t frame,
>                   IPAFrameContext &frameContext, MaliC55Params *params)
>  {
> -       agc_.prepare(context.activeState.agc, frameContext.agc);
> +       agc_.prepare(context.configuration.agc, context.activeState.agc, frameContext.agc);
>  
>         if (frame > 0)
>                 return;
> diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
> index fc1e48f0c5..32966797c2 100644
> --- a/src/ipa/rkisp1/algorithms/agc.cpp
> +++ b/src/ipa/rkisp1/algorithms/agc.cpp
> @@ -208,7 +208,7 @@ void Agc::queueRequest(IPAContext &context,
>  void Agc::prepare(IPAContext &context, const uint32_t frame,
>                   IPAFrameContext &frameContext, RkISP1Params *params)
>  {
> -       agc_.prepare(context.activeState.agc, frameContext.agc);
> +       agc_.prepare(context.configuration.agc, context.activeState.agc, frameContext.agc);
>  
>         if (context.configuration.compress.supported) {
>                 frameContext.compress.enable = true;
> diff --git a/src/ipa/softisp/algorithms/agc.cpp b/src/ipa/softisp/algorithms/agc.cpp
> index ea234bc2ae..7d2a53b385 100644
> --- a/src/ipa/softisp/algorithms/agc.cpp
> +++ b/src/ipa/softisp/algorithms/agc.cpp
> @@ -74,7 +74,7 @@ void Agc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t fram
>  void Agc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
>                   IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params)
>  {
> -       agc_.prepare(context.activeState.agc, frameContext.agc);
> +       agc_.prepare(context.configuration.agc, context.activeState.agc, frameContext.agc);
>  }
>  
>  void Agc::process(IPAContext &context,
> -- 
> 2.55.0
>
Barnabás Pőcze Aug. 27, 2026, 1:42 p.m. UTC | #2
2026. 08. 27. 15:27 keltezéssel, Stefan Klug írta:
> Hi Barnabás,
> 
> Quoting Barnabás Pőcze (2026-08-27 12:41:03)
>> Calculating the vblank and frame duration is problematic in `process()`
>> because at the moment it is calculated for an already finished frame
>> based on the new suggested exposure time.
>>
>> Instead, move the calculation to `prepare()` where the frame's
>> exposure and gain are finalized.
> 
> This is mostly a duplicate of https://patchwork.libcamera.org/patch/26358/
> (or the rebased version of it:
> https://git.ideasonboard.com/sklug/libcamera/commit/cd346af78bc3b0aa3e5aa08e41b54b93f6ebf86e )
> 
> 
> Could we keep the processFrameDuration() function around for now? That
> would lower the rebasing pain for me as I still have two places
> where that function gets called. Maybe the first one will become
> unnecessary...
> 

To be honest I'm very keen to remove the function altogether. But I see that
the change you refer to indeed uses the function in two places. In any case,
I just want to make sure that the exposure time used to calculate the vblank
is the correct one. So I can move the call into `prepare()` if that's is better
for you? Or any suggestions?


> Best regards,
> Stefan
> 
> 
> 
>>
>> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
>> ---
>>   src/ipa/ipu3/algorithms/agc.cpp     |  2 +-
>>   src/ipa/libipa/agc.cpp              | 49 ++++++++++-------------------
>>   src/ipa/libipa/agc.h                |  6 ++--
>>   src/ipa/mali-c55/algorithms/agc.cpp |  2 +-
>>   src/ipa/rkisp1/algorithms/agc.cpp   |  2 +-
>>   src/ipa/softisp/algorithms/agc.cpp  |  2 +-
>>   6 files changed, 22 insertions(+), 41 deletions(-)
>>
>> diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp
>> index b0f535b990..b4b49aa638 100644
>> --- a/src/ipa/ipu3/algorithms/agc.cpp
>> +++ b/src/ipa/ipu3/algorithms/agc.cpp
>> @@ -110,7 +110,7 @@ void Agc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
>>                    IPAFrameContext &frameContext,
>>                    [[maybe_unused]] ipu3_uapi_params *params)
>>   {
>> -       agc_.prepare(context.activeState.agc, frameContext.agc);
>> +       agc_.prepare(context.configuration.agc, context.activeState.agc, frameContext.agc);
>>   }
>>   
>>   Histogram Agc::parseStatistics(const ipu3_uapi_stats_3a *stats,
>> diff --git a/src/ipa/libipa/agc.cpp b/src/ipa/libipa/agc.cpp
>> index fab34152cc..b969118063 100644
>> --- a/src/ipa/libipa/agc.cpp
>> +++ b/src/ipa/libipa/agc.cpp
>> @@ -650,6 +650,7 @@ void AgcAlgorithm::queueRequest(const agc::Session &session, agc::ActiveState &s
>>   
>>   /**
>>    * \brief Prepare a frame
>> + * \param[in] session The agc session configuration
>>    * \param[in] state The agc active state
>>    * \param[in] frameContext The agc frame context
>>    *
>> @@ -659,11 +660,10 @@ void AgcAlgorithm::queueRequest(const agc::Session &session, agc::ActiveState &s
>>    * \ref agc::FrameContext::gain "frameContext.gain" will be finalized
>>    * and may be used by the caller (see agc::prepareControls()).
>>    *
>> - * \todo Finalize \ref agc::FrameContext::vblank "frameContext.vblank" as well
>> - *
>>    * \sa Algorithm::prepare()
>>    */
>> -void AgcAlgorithm::prepare(agc::ActiveState &state, agc::FrameContext &frameContext)
>> +void AgcAlgorithm::prepare(const agc::Session& session, agc::ActiveState &state,
>> +                          agc::FrameContext &frameContext)
>>   {
>>          uint32_t activeAutoExposure = state.automatic.exposure;
>>          double activeAutoGain = state.automatic.gain;
>> @@ -694,6 +694,19 @@ void AgcAlgorithm::prepare(agc::ActiveState &state, agc::FrameContext &frameCont
>>          }
>>   
>>          frameContext.yTarget = state.automatic.yTarget;
>> +
>> +       /*
>> +       * Expand the target frame duration so that we do not run faster than
>> +       * the minimum frame duration when we have short exposures.
>> +       */
>> +       const auto frameDuration = std::max<uint32_t>(
>> +               frameContext.minFrameDuration / session.lineDuration,
>> +               frameContext.exposure);
>> +
>> +       frameContext.vblank = frameDuration - session.sensor.outputSize.height;
>> +
>> +       /* Update frame duration accounting for line length quantization. */
>> +       frameContext.frameDuration = frameDuration * session.lineDuration;
>>   }
>>   
>>   /**
>> @@ -724,7 +737,6 @@ void AgcAlgorithm::process(const agc::Session &session, agc::ActiveState &state,
>>                             ControlList &metadata)
>>   {
>>          if (!params) {
>> -               processFrameDuration(session, frameContext, frameContext.minFrameDuration);
>>                  fillMetadata(session, frameContext, metadata);
>>                  return;
>>          }
>> @@ -828,38 +840,9 @@ void AgcAlgorithm::process(const agc::Session &session, agc::ActiveState &state,
>>                  << "quantization-gain: " << state.automatic.quantizationGain << ", "
>>                  << "digital-gain: " << state.automatic.digitalGain;
>>   
>> -       /*
>> -        * Expand the target frame duration so that we do not run faster than
>> -        * the minimum frame duration when we have short exposures.
>> -        */
>> -       processFrameDuration(session, frameContext,
>> -                            std::max(frameContext.minFrameDuration, newExposureTime));
>> -
>>          fillMetadata(session, frameContext, metadata);
>>   }
>>   
>> -/**
>> - * \brief Process frame duration and compute vblank
>> - * \param[in] session The session parameters
>> - * \param[in] frameContext The current frame context
>> - * \param[in] frameDuration The target frame duration
>> - *
>> - * Compute and populate vblank from the target frame duration.
>> - */
>> -void AgcAlgorithm::processFrameDuration(const agc::Session &session,
>> -                                       agc::FrameContext &frameContext,
>> -                                       utils::Duration frameDuration)
>> -{
>> -       const utils::Duration &lineDuration = session.lineDuration;
>> -
>> -       frameContext.vblank =
>> -               (frameDuration / lineDuration) - session.sensor.outputSize.height;
>> -
>> -       /* Update frame duration accounting for line length quantization. */
>> -       frameContext.frameDuration =
>> -               (session.sensor.outputSize.height + frameContext.vblank) * lineDuration;
>> -}
>> -
>>   void AgcAlgorithm::fillMetadata(const agc::Session &session,
>>                                  const agc::FrameContext &frameContext,
>>                                  ControlList &metadata)
>> diff --git a/src/ipa/libipa/agc.h b/src/ipa/libipa/agc.h
>> index a5700d2625..9f95f23ea8 100644
>> --- a/src/ipa/libipa/agc.h
>> +++ b/src/ipa/libipa/agc.h
>> @@ -146,16 +146,14 @@ public:
>>          void queueRequest(const agc::Session &session, agc::ActiveState &state,
>>                            agc::FrameContext &frameContext, const ControlList &controls);
>>   
>> -       void prepare(agc::ActiveState &state, agc::FrameContext &frameContext);
>> +       void prepare(const agc::Session& session, agc::ActiveState &state,
>> +                    agc::FrameContext &frameContext);
>>   
>>          void process(const agc::Session &session, agc::ActiveState &state,
>>                       agc::FrameContext &frameContext, std::optional<ProcessParams> &&params,
>>                       ControlList &metadata);
>>   
>>   private:
>> -       void processFrameDuration(const agc::Session &session,
>> -                                 agc::FrameContext &frameContext,
>> -                                 utils::Duration frameDuration);
>>          void fillMetadata(const agc::Session &session,
>>                            const agc::FrameContext &frameContext,
>>                            ControlList &metadata);
>> diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp
>> index 093c8c6fea..bb1683036f 100644
>> --- a/src/ipa/mali-c55/algorithms/agc.cpp
>> +++ b/src/ipa/mali-c55/algorithms/agc.cpp
>> @@ -207,7 +207,7 @@ void Agc::fillWeightsArrayBuffer(MaliC55Params *params, const enum MaliC55Blocks
>>   void Agc::prepare(IPAContext &context, const uint32_t frame,
>>                    IPAFrameContext &frameContext, MaliC55Params *params)
>>   {
>> -       agc_.prepare(context.activeState.agc, frameContext.agc);
>> +       agc_.prepare(context.configuration.agc, context.activeState.agc, frameContext.agc);
>>   
>>          if (frame > 0)
>>                  return;
>> diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
>> index fc1e48f0c5..32966797c2 100644
>> --- a/src/ipa/rkisp1/algorithms/agc.cpp
>> +++ b/src/ipa/rkisp1/algorithms/agc.cpp
>> @@ -208,7 +208,7 @@ void Agc::queueRequest(IPAContext &context,
>>   void Agc::prepare(IPAContext &context, const uint32_t frame,
>>                    IPAFrameContext &frameContext, RkISP1Params *params)
>>   {
>> -       agc_.prepare(context.activeState.agc, frameContext.agc);
>> +       agc_.prepare(context.configuration.agc, context.activeState.agc, frameContext.agc);
>>   
>>          if (context.configuration.compress.supported) {
>>                  frameContext.compress.enable = true;
>> diff --git a/src/ipa/softisp/algorithms/agc.cpp b/src/ipa/softisp/algorithms/agc.cpp
>> index ea234bc2ae..7d2a53b385 100644
>> --- a/src/ipa/softisp/algorithms/agc.cpp
>> +++ b/src/ipa/softisp/algorithms/agc.cpp
>> @@ -74,7 +74,7 @@ void Agc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t fram
>>   void Agc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
>>                    IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params)
>>   {
>> -       agc_.prepare(context.activeState.agc, frameContext.agc);
>> +       agc_.prepare(context.configuration.agc, context.activeState.agc, frameContext.agc);
>>   }
>>   
>>   void Agc::process(IPAContext &context,
>> -- 
>> 2.55.0
>>

Patch
diff mbox series

diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp
index b0f535b990..b4b49aa638 100644
--- a/src/ipa/ipu3/algorithms/agc.cpp
+++ b/src/ipa/ipu3/algorithms/agc.cpp
@@ -110,7 +110,7 @@  void Agc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
 		  IPAFrameContext &frameContext,
 		  [[maybe_unused]] ipu3_uapi_params *params)
 {
-	agc_.prepare(context.activeState.agc, frameContext.agc);
+	agc_.prepare(context.configuration.agc, context.activeState.agc, frameContext.agc);
 }
 
 Histogram Agc::parseStatistics(const ipu3_uapi_stats_3a *stats,
diff --git a/src/ipa/libipa/agc.cpp b/src/ipa/libipa/agc.cpp
index fab34152cc..b969118063 100644
--- a/src/ipa/libipa/agc.cpp
+++ b/src/ipa/libipa/agc.cpp
@@ -650,6 +650,7 @@  void AgcAlgorithm::queueRequest(const agc::Session &session, agc::ActiveState &s
 
 /**
  * \brief Prepare a frame
+ * \param[in] session The agc session configuration
  * \param[in] state The agc active state
  * \param[in] frameContext The agc frame context
  *
@@ -659,11 +660,10 @@  void AgcAlgorithm::queueRequest(const agc::Session &session, agc::ActiveState &s
  * \ref agc::FrameContext::gain "frameContext.gain" will be finalized
  * and may be used by the caller (see agc::prepareControls()).
  *
- * \todo Finalize \ref agc::FrameContext::vblank "frameContext.vblank" as well
- *
  * \sa Algorithm::prepare()
  */
-void AgcAlgorithm::prepare(agc::ActiveState &state, agc::FrameContext &frameContext)
+void AgcAlgorithm::prepare(const agc::Session& session, agc::ActiveState &state,
+			   agc::FrameContext &frameContext)
 {
 	uint32_t activeAutoExposure = state.automatic.exposure;
 	double activeAutoGain = state.automatic.gain;
@@ -694,6 +694,19 @@  void AgcAlgorithm::prepare(agc::ActiveState &state, agc::FrameContext &frameCont
 	}
 
 	frameContext.yTarget = state.automatic.yTarget;
+
+	/*
+	* Expand the target frame duration so that we do not run faster than
+	* the minimum frame duration when we have short exposures.
+	*/
+	const auto frameDuration = std::max<uint32_t>(
+		frameContext.minFrameDuration / session.lineDuration,
+		frameContext.exposure);
+
+	frameContext.vblank = frameDuration - session.sensor.outputSize.height;
+
+	/* Update frame duration accounting for line length quantization. */
+	frameContext.frameDuration = frameDuration * session.lineDuration;
 }
 
 /**
@@ -724,7 +737,6 @@  void AgcAlgorithm::process(const agc::Session &session, agc::ActiveState &state,
 			   ControlList &metadata)
 {
 	if (!params) {
-		processFrameDuration(session, frameContext, frameContext.minFrameDuration);
 		fillMetadata(session, frameContext, metadata);
 		return;
 	}
@@ -828,38 +840,9 @@  void AgcAlgorithm::process(const agc::Session &session, agc::ActiveState &state,
 		<< "quantization-gain: " << state.automatic.quantizationGain << ", "
 		<< "digital-gain: " << state.automatic.digitalGain;
 
-	/*
-	 * Expand the target frame duration so that we do not run faster than
-	 * the minimum frame duration when we have short exposures.
-	 */
-	processFrameDuration(session, frameContext,
-			     std::max(frameContext.minFrameDuration, newExposureTime));
-
 	fillMetadata(session, frameContext, metadata);
 }
 
-/**
- * \brief Process frame duration and compute vblank
- * \param[in] session The session parameters
- * \param[in] frameContext The current frame context
- * \param[in] frameDuration The target frame duration
- *
- * Compute and populate vblank from the target frame duration.
- */
-void AgcAlgorithm::processFrameDuration(const agc::Session &session,
-					agc::FrameContext &frameContext,
-					utils::Duration frameDuration)
-{
-	const utils::Duration &lineDuration = session.lineDuration;
-
-	frameContext.vblank =
-		(frameDuration / lineDuration) - session.sensor.outputSize.height;
-
-	/* Update frame duration accounting for line length quantization. */
-	frameContext.frameDuration =
-		(session.sensor.outputSize.height + frameContext.vblank) * lineDuration;
-}
-
 void AgcAlgorithm::fillMetadata(const agc::Session &session,
 				const agc::FrameContext &frameContext,
 				ControlList &metadata)
diff --git a/src/ipa/libipa/agc.h b/src/ipa/libipa/agc.h
index a5700d2625..9f95f23ea8 100644
--- a/src/ipa/libipa/agc.h
+++ b/src/ipa/libipa/agc.h
@@ -146,16 +146,14 @@  public:
 	void queueRequest(const agc::Session &session, agc::ActiveState &state,
 			  agc::FrameContext &frameContext, const ControlList &controls);
 
-	void prepare(agc::ActiveState &state, agc::FrameContext &frameContext);
+	void prepare(const agc::Session& session, agc::ActiveState &state,
+		     agc::FrameContext &frameContext);
 
 	void process(const agc::Session &session, agc::ActiveState &state,
 		     agc::FrameContext &frameContext, std::optional<ProcessParams> &&params,
 		     ControlList &metadata);
 
 private:
-	void processFrameDuration(const agc::Session &session,
-				  agc::FrameContext &frameContext,
-				  utils::Duration frameDuration);
 	void fillMetadata(const agc::Session &session,
 			  const agc::FrameContext &frameContext,
 			  ControlList &metadata);
diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp
index 093c8c6fea..bb1683036f 100644
--- a/src/ipa/mali-c55/algorithms/agc.cpp
+++ b/src/ipa/mali-c55/algorithms/agc.cpp
@@ -207,7 +207,7 @@  void Agc::fillWeightsArrayBuffer(MaliC55Params *params, const enum MaliC55Blocks
 void Agc::prepare(IPAContext &context, const uint32_t frame,
 		  IPAFrameContext &frameContext, MaliC55Params *params)
 {
-	agc_.prepare(context.activeState.agc, frameContext.agc);
+	agc_.prepare(context.configuration.agc, context.activeState.agc, frameContext.agc);
 
 	if (frame > 0)
 		return;
diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
index fc1e48f0c5..32966797c2 100644
--- a/src/ipa/rkisp1/algorithms/agc.cpp
+++ b/src/ipa/rkisp1/algorithms/agc.cpp
@@ -208,7 +208,7 @@  void Agc::queueRequest(IPAContext &context,
 void Agc::prepare(IPAContext &context, const uint32_t frame,
 		  IPAFrameContext &frameContext, RkISP1Params *params)
 {
-	agc_.prepare(context.activeState.agc, frameContext.agc);
+	agc_.prepare(context.configuration.agc, context.activeState.agc, frameContext.agc);
 
 	if (context.configuration.compress.supported) {
 		frameContext.compress.enable = true;
diff --git a/src/ipa/softisp/algorithms/agc.cpp b/src/ipa/softisp/algorithms/agc.cpp
index ea234bc2ae..7d2a53b385 100644
--- a/src/ipa/softisp/algorithms/agc.cpp
+++ b/src/ipa/softisp/algorithms/agc.cpp
@@ -74,7 +74,7 @@  void Agc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t fram
 void Agc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
 		  IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params)
 {
-	agc_.prepare(context.activeState.agc, frameContext.agc);
+	agc_.prepare(context.configuration.agc, context.activeState.agc, frameContext.agc);
 }
 
 void Agc::process(IPAContext &context,