[{"id":37557,"web_url":"https://patchwork.libcamera.org/comment/37557/","msgid":"<CAEmqJPo04AFX7TwTHRpH3uzo4JmkgAGdcrPUcjbpx=-WO+oajQ@mail.gmail.com>","date":"2026-01-12T09:45:00","subject":"Re: [PATCH v3] libcamera: rpi: Make the controller min frame\n\tduration configurable","submitter":{"id":34,"url":"https://patchwork.libcamera.org/api/people/34/","name":"Naushir Patuck","email":"naush@raspberrypi.com"},"content":"Hi David,\n\nThanks for the patch.\n\nOn Wed, 7 Jan 2026 at 18:14, David Plowman\n<david.plowman@raspberrypi.com> wrote:\n>\n> The controller min frame duration is used to rate limit how often we\n> run IPAs. Historically this has been set to 33333us, meaning that the\n> algorithms effectively skip frames when the camera is running faster\n> than 30fps.\n>\n> This patch adds a small amount of plumbing that allows this value to\n> be set in the Raspberry Pi configuration file. Some applications or\n> platforms (such as Pi 5) are easily capable of running these more\n> often, should there be a need to do so.\n>\n> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>\n\nReviewed-by: Naushir Patuck <naush@raspberrypi.com>\n\n> ---\n>  include/libcamera/ipa/raspberrypi.mojom       |  1 +\n>  src/ipa/rpi/common/ipa_base.cpp               | 18 +++++++-------\n>  src/ipa/rpi/common/ipa_base.h                 |  2 ++\n>  .../pipeline/rpi/common/pipeline_base.cpp     | 24 ++++++++++++++-----\n>  .../pipeline/rpi/common/pipeline_base.h       |  5 ++++\n>  .../pipeline/rpi/pisp/data/example.yaml       |  6 +++++\n>  .../pipeline/rpi/vc4/data/example.yaml        |  6 +++++\n>  7 files changed, 47 insertions(+), 15 deletions(-)\n>\n> diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom\n> index 12b083e9..1b7e0358 100644\n> --- a/include/libcamera/ipa/raspberrypi.mojom\n> +++ b/include/libcamera/ipa/raspberrypi.mojom\n> @@ -18,6 +18,7 @@ struct SensorConfig {\n>  struct InitParams {\n>         bool lensPresent;\n>         libcamera.IPACameraSensorInfo sensorInfo;\n> +       float controllerMinFrameDurationUs;\n>         /* PISP specific */\n>         libcamera.SharedFD fe;\n>         libcamera.SharedFD be;\n> diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp\n> index 14aba450..c0f69b79 100644\n> --- a/src/ipa/rpi/common/ipa_base.cpp\n> +++ b/src/ipa/rpi/common/ipa_base.cpp\n> @@ -46,14 +46,6 @@ constexpr Duration defaultExposureTime = 20.0ms;\n>  constexpr Duration defaultMinFrameDuration = 1.0s / 30.0;\n>  constexpr Duration defaultMaxFrameDuration = 250.0s;\n>\n> -/*\n> - * Determine the minimum allowable inter-frame duration to run the controller\n> - * algorithms. If the pipeline handler provider frames at a rate higher than this,\n> - * we rate-limit the controller Prepare() and Process() calls to lower than or\n> - * equal to this rate.\n> - */\n> -constexpr Duration controllerMinFrameDuration = 1.0s / 30.0;\n> -\n>  /* List of controls handled by the Raspberry Pi IPA */\n>  const ControlInfoMap::Map ipaControls{\n>         /* \\todo Move this to the Camera class */\n> @@ -184,6 +176,14 @@ int32_t IpaBase::init(const IPASettings &settings, const InitParams &params, Ini\n>\n>         result->controlInfo = ControlInfoMap(std::move(ctrlMap), controls::controls);\n>\n> +       /*\n> +        * This determines the minimum allowable inter-frame duration to run the\n> +        * controller algorithms. If the pipeline handler provider frames at a\n> +        * rate higher than this, we rate-limit the controller Prepare() and\n> +        * Process() calls to lower than or equal to this rate.\n> +        */\n> +       controllerMinFrameDuration_ = params.controllerMinFrameDurationUs * 1us;\n> +\n>         return platformInit(params, result);\n>  }\n>\n> @@ -465,7 +465,7 @@ void IpaBase::prepareIsp(const PrepareParams &params)\n>         /* Allow a 10% margin on the comparison below. */\n>         Duration delta = (frameTimestamp - lastRunTimestamp_) * 1.0ns;\n>         if (lastRunTimestamp_ && frameCount_ > invalidCount_ &&\n> -           delta < controllerMinFrameDuration * 0.9 && !hdrChange) {\n> +           delta < controllerMinFrameDuration_ * 0.9 && !hdrChange) {\n>                 /*\n>                  * Ensure we merge the previous frame's metadata with the current\n>                  * frame. This will not overwrite exposure/gain values for the\n> diff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h\n> index 5348f2ea..90f018b2 100644\n> --- a/src/ipa/rpi/common/ipa_base.h\n> +++ b/src/ipa/rpi/common/ipa_base.h\n> @@ -142,6 +142,8 @@ private:\n>         } flickerState_;\n>\n>         bool awbEnabled_;\n> +\n> +       utils::Duration controllerMinFrameDuration_;\n>  };\n>\n>  } /* namespace ipa::RPi */\n> diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n> index fb8e466f..b7655d8d 100644\n> --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n> +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n> @@ -33,6 +33,12 @@ LOG_DEFINE_CATEGORY(RPI)\n>\n>  using StreamFlag = RPi::Stream::StreamFlag;\n>\n> +/*\n> + * The IPA's algorithms will not be called more often than this many\n> + * microseconds. The default corresponds to 30fps.\n> + */\n> +constexpr float defaultControllerMinimumFrameDurationUs = 1000000.0 / 30.0;\n> +\n>  namespace {\n>\n>  constexpr unsigned int defaultRawBitDepth = 12;\n> @@ -800,6 +806,12 @@ int PipelineHandlerBase::registerCamera(std::unique_ptr<RPi::CameraData> &camera\n>         if (!data->sensor_)\n>                 return -EINVAL;\n>\n> +       ret = data->loadPipelineConfiguration();\n> +       if (ret) {\n> +               LOG(RPI, Error) << \"Unable to load pipeline configuration\";\n> +               return ret;\n> +       }\n> +\n>         /* Populate the map of sensor supported formats and sizes. */\n>         for (auto const mbusCode : data->sensor_->mbusCodes())\n>                 data->sensorFormats_.emplace(mbusCode,\n> @@ -859,12 +871,6 @@ int PipelineHandlerBase::registerCamera(std::unique_ptr<RPi::CameraData> &camera\n>         if (ret)\n>                 return ret;\n>\n> -       ret = data->loadPipelineConfiguration();\n> -       if (ret) {\n> -               LOG(RPI, Error) << \"Unable to load pipeline configuration\";\n> -               return ret;\n> -       }\n> -\n>         /* Setup the general IPA signal handlers. */\n>         data->frontendDevice()->dequeueTimeout.connect(data, &RPi::CameraData::cameraTimeout);\n>         data->frontendDevice()->frameStart.connect(data, &RPi::CameraData::frameStarted);\n> @@ -1096,6 +1102,7 @@ int CameraData::loadPipelineConfiguration()\n>  {\n>         config_ = {\n>                 .cameraTimeoutValue = 0,\n> +               .controllerMinFrameDurationUs = defaultControllerMinimumFrameDurationUs,\n>         };\n>\n>         /* Initial configuration of the platform, in case no config file is present */\n> @@ -1145,6 +1152,9 @@ int CameraData::loadPipelineConfiguration()\n>                 frontendDevice()->setDequeueTimeout(config_.cameraTimeoutValue * 1ms);\n>         }\n>\n> +       config_.controllerMinFrameDurationUs =\n> +               phConfig[\"controller_min_frame_duration_us\"].get<double>(config_.controllerMinFrameDurationUs);\n> +\n>         return platformPipelineConfigure(root);\n>  }\n>\n> @@ -1173,6 +1183,8 @@ int CameraData::loadIPA(ipa::RPi::InitResult *result)\n>         }\n>\n>         params.lensPresent = !!sensor_->focusLens();\n> +       params.controllerMinFrameDurationUs = config_.controllerMinFrameDurationUs;\n> +\n>         ret = platformInitIpa(params);\n>         if (ret)\n>                 return ret;\n> diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.h b/src/libcamera/pipeline/rpi/common/pipeline_base.h\n> index 6257a934..597eb587 100644\n> --- a/src/libcamera/pipeline/rpi/common/pipeline_base.h\n> +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.h\n> @@ -169,6 +169,11 @@ public:\n>                  * on frame durations.\n>                  */\n>                 unsigned int cameraTimeoutValue;\n> +               /*\n> +                * The minimum frame duration between the IPA's calls to the\n> +                * algorithms themselves (in microseconds).\n> +                */\n> +               float controllerMinFrameDurationUs;\n>         };\n>\n>         Config config_;\n> diff --git a/src/libcamera/pipeline/rpi/pisp/data/example.yaml b/src/libcamera/pipeline/rpi/pisp/data/example.yaml\n> index baf03be7..c5edbba0 100644\n> --- a/src/libcamera/pipeline/rpi/pisp/data/example.yaml\n> +++ b/src/libcamera/pipeline/rpi/pisp/data/example.yaml\n> @@ -36,5 +36,11 @@\n>                  # framebuffers required for its operation.\n>                  #\n>                  # \"disable_hdr\": false,\n> +\n> +                # Limits the rate at which IPAs are called. The algorithms will\n> +                # be skipped until this many microseconds have elapsed since\n> +                # the last call. The default value represents a 30fps limit.\n> +                #\n> +                # \"controller_min_frame_duration_us\": 33333.333,\n>          }\n>  }\n> diff --git a/src/libcamera/pipeline/rpi/vc4/data/example.yaml b/src/libcamera/pipeline/rpi/vc4/data/example.yaml\n> index 27e54348..2ee2b864 100644\n> --- a/src/libcamera/pipeline/rpi/vc4/data/example.yaml\n> +++ b/src/libcamera/pipeline/rpi/vc4/data/example.yaml\n> @@ -37,5 +37,11 @@\n>                  # timeout value.\n>                  #\n>                  # \"camera_timeout_value_ms\": 0,\n> +\n> +                # Limits the rate at which IPAs are called. The algorithms will\n> +                # be skipped until this many microseconds have elapsed since\n> +                # the last call. The default value represents a 30fps limit.\n> +                #\n> +                # \"controller_min_frame_duration_us\": 33333.333,\n>          }\n>  }\n> --\n> 2.47.3\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id E5C64BDCBF\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 12 Jan 2026 09:45:40 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id F198561FA0;\n\tMon, 12 Jan 2026 10:45:39 +0100 (CET)","from mail-ua1-x92f.google.com (mail-ua1-x92f.google.com\n\t[IPv6:2607:f8b0:4864:20::92f])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 9B78E61F9F\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 12 Jan 2026 10:45:38 +0100 (CET)","by mail-ua1-x92f.google.com with SMTP id\n\ta1e0cc1a2514c-93f5d55ee5aso198636241.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 12 Jan 2026 01:45:38 -0800 (PST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=raspberrypi.com header.i=@raspberrypi.com\n\theader.b=\"N+8ymeCX\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google; t=1768211137; x=1768815937;\n\tdarn=lists.libcamera.org; \n\th=cc:to:subject:message-id:date:from:in-reply-to:references\n\t:mime-version:from:to:cc:subject:date:message-id:reply-to;\n\tbh=0LSUolk3HkoIvxAiqFOwsdSxpHUyDc/g2yiPFH1vg0g=;\n\tb=N+8ymeCXImQbQeEcgzIJL903KC6KIUu++jw1r+SXlc/Vfack/v1Xjp7PHFMFLYivnm\n\tRAH2ycxQPbet+h4Y+hDWsr+eci7vxvukX3cMHg6SxXeAe41zPbmFD4fw/6vm3Kqjz329\n\tos2IZG1YbekitSYmCANHdqH+UwHTOGDMKeLbB/ZsR6lBcAi/nT6yb0OfHJQJcWPqxzUt\n\t96qpPqG82/2fnIUlX/9rWippB4XnUhN476dpLoPj13jG/0uCuVqemqEzYFCcIfppF79Z\n\tzZWZG0EkR6vIxev7OlWKemQ4WDdlh06y3YvZ6u1z2coD+mDcmE8UJlOitOoieq9Py1G0\n\tpxfQ==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1768211137; x=1768815937;\n\th=cc:to:subject:message-id:date:from:in-reply-to:references\n\t:mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date\n\t:message-id:reply-to;\n\tbh=0LSUolk3HkoIvxAiqFOwsdSxpHUyDc/g2yiPFH1vg0g=;\n\tb=X0fFpRc/s9/+Q11x2Z9RfjrovrL0Fo7+3yWnOfAzx+MYjsXChbSuYl6hXbw08gq4ZD\n\tH7hyzqPrFthmH3LF5GHx2LADtNSMdE0qOqbIoP7ZP1rWfSDLl9d8irMbYO/2L1HRzQ35\n\twZEzV2t4NY7JMgYU1mPAI1TrArhQ2WB2g62pMXH0CZNixoagUQ/HeAbVlgFT6GIq7Y2S\n\tQwWHaq+zEtopvorqHgtzYWMwaBek9jZsUsIhbpvRjV5FmYrNlnb+db1+KcqBESDJlYMF\n\tbslscDkhrQPnczQa9CWBJPEy542IlP5fjAbd/FYBxvetP12JRfoApfw1COAkdnOMFNbW\n\tnhZw==","X-Gm-Message-State":"AOJu0YwhdnANfO8b8IaYeezPfJFk6SaP5HqvXLpiBznDUFrJh5NNSnHL\n\tuGvTbqKeTj3SHeLFat8ZOXkhdm/oyVKtFWDSOqfhaDTjVb70vmeveQWLNQCT0WBVTL5TVFfcCHe\n\trR0dNTmedcMKm27UrWWTdqp/YhET58TaTgHmbr5ZW1g==","X-Gm-Gg":"AY/fxX77pGCINVApQDg7P3k6iuxLnDxKwzTzyIvNkIKBKFWruPO/THMQ8/nJx093QNu\n\txmmf/tsDoBp+JrpK47zKZsiwKZaYFdrHTtJWohq2KLR4rY2JjA8WX9nxyPrAUBwyp+s1EuExAjE\n\t4o1Heb1EAsmATOhd11gI6MZeQM5vyNVPxHfnFmVD4nK2BcsNWOj1tStSt0G47G480/HYsiN/XT4\n\tw8y0W40nvMwpb2f1fMKgQWegIgkjOCDPLhUbHClIaqgVXaRuKDUuhwCFPbKAxCclDbBPpwl5FSy\n\tKgga9scT99m2ZInHCmvBOoGw7/3FQ1hdoEe4kg==","X-Google-Smtp-Source":"AGHT+IG3ha6c5Dmajkj6avohRJE3FyzxSV2mUSmcGjGBIbt1N3LNcdlOw8nKOwjJkEUHUX9xDFkqO9nRPYn6kfi40o0=","X-Received":"by 2002:a05:6102:17c8:b0:5d4:927:5e0 with SMTP id\n\tada2fe7eead31-5ecb69846f5mr2938691137.7.1768211137205;\n\tMon, 12 Jan 2026 01:45:37 -0800 (PST)","MIME-Version":"1.0","References":"<20260107181445.18005-1-david.plowman@raspberrypi.com>","In-Reply-To":"<20260107181445.18005-1-david.plowman@raspberrypi.com>","From":"Naushir Patuck <naush@raspberrypi.com>","Date":"Mon, 12 Jan 2026 09:45:00 +0000","X-Gm-Features":"AZwV_QieVmfWK_NYdYQjsSlN5YnCKKuHa7AVBEYhsg4FT6Cko8dzk1UZr2C49-U","Message-ID":"<CAEmqJPo04AFX7TwTHRpH3uzo4JmkgAGdcrPUcjbpx=-WO+oajQ@mail.gmail.com>","Subject":"Re: [PATCH v3] libcamera: rpi: Make the controller min frame\n\tduration configurable","To":"David Plowman <david.plowman@raspberrypi.com>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"UTF-8\"","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":37935,"web_url":"https://patchwork.libcamera.org/comment/37935/","msgid":"<176942064821.1693075.10330723886061204034@ping.linuxembedded.co.uk>","date":"2026-01-26T09:44:08","subject":"Re: [PATCH v3] libcamera: rpi: Make the controller min frame\n\tduration configurable","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Naushir Patuck (2026-01-12 09:45:00)\n> Hi David,\n> \n> Thanks for the patch.\n> \n> On Wed, 7 Jan 2026 at 18:14, David Plowman\n> <david.plowman@raspberrypi.com> wrote:\n> >\n> > The controller min frame duration is used to rate limit how often we\n> > run IPAs. Historically this has been set to 33333us, meaning that the\n> > algorithms effectively skip frames when the camera is running faster\n> > than 30fps.\n> >\n> > This patch adds a small amount of plumbing that allows this value to\n> > be set in the Raspberry Pi configuration file. Some applications or\n> > platforms (such as Pi 5) are easily capable of running these more\n> > often, should there be a need to do so.\n> >\n> > Signed-off-by: David Plowman <david.plowman@raspberrypi.com>\n> \n> Reviewed-by: Naushir Patuck <naush@raspberrypi.com>\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n> \n> > ---\n> >  include/libcamera/ipa/raspberrypi.mojom       |  1 +\n> >  src/ipa/rpi/common/ipa_base.cpp               | 18 +++++++-------\n> >  src/ipa/rpi/common/ipa_base.h                 |  2 ++\n> >  .../pipeline/rpi/common/pipeline_base.cpp     | 24 ++++++++++++++-----\n> >  .../pipeline/rpi/common/pipeline_base.h       |  5 ++++\n> >  .../pipeline/rpi/pisp/data/example.yaml       |  6 +++++\n> >  .../pipeline/rpi/vc4/data/example.yaml        |  6 +++++\n> >  7 files changed, 47 insertions(+), 15 deletions(-)\n> >\n> > diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom\n> > index 12b083e9..1b7e0358 100644\n> > --- a/include/libcamera/ipa/raspberrypi.mojom\n> > +++ b/include/libcamera/ipa/raspberrypi.mojom\n> > @@ -18,6 +18,7 @@ struct SensorConfig {\n> >  struct InitParams {\n> >         bool lensPresent;\n> >         libcamera.IPACameraSensorInfo sensorInfo;\n> > +       float controllerMinFrameDurationUs;\n> >         /* PISP specific */\n> >         libcamera.SharedFD fe;\n> >         libcamera.SharedFD be;\n> > diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp\n> > index 14aba450..c0f69b79 100644\n> > --- a/src/ipa/rpi/common/ipa_base.cpp\n> > +++ b/src/ipa/rpi/common/ipa_base.cpp\n> > @@ -46,14 +46,6 @@ constexpr Duration defaultExposureTime = 20.0ms;\n> >  constexpr Duration defaultMinFrameDuration = 1.0s / 30.0;\n> >  constexpr Duration defaultMaxFrameDuration = 250.0s;\n> >\n> > -/*\n> > - * Determine the minimum allowable inter-frame duration to run the controller\n> > - * algorithms. If the pipeline handler provider frames at a rate higher than this,\n> > - * we rate-limit the controller Prepare() and Process() calls to lower than or\n> > - * equal to this rate.\n> > - */\n> > -constexpr Duration controllerMinFrameDuration = 1.0s / 30.0;\n> > -\n> >  /* List of controls handled by the Raspberry Pi IPA */\n> >  const ControlInfoMap::Map ipaControls{\n> >         /* \\todo Move this to the Camera class */\n> > @@ -184,6 +176,14 @@ int32_t IpaBase::init(const IPASettings &settings, const InitParams &params, Ini\n> >\n> >         result->controlInfo = ControlInfoMap(std::move(ctrlMap), controls::controls);\n> >\n> > +       /*\n> > +        * This determines the minimum allowable inter-frame duration to run the\n> > +        * controller algorithms. If the pipeline handler provider frames at a\n> > +        * rate higher than this, we rate-limit the controller Prepare() and\n> > +        * Process() calls to lower than or equal to this rate.\n> > +        */\n> > +       controllerMinFrameDuration_ = params.controllerMinFrameDurationUs * 1us;\n> > +\n> >         return platformInit(params, result);\n> >  }\n> >\n> > @@ -465,7 +465,7 @@ void IpaBase::prepareIsp(const PrepareParams &params)\n> >         /* Allow a 10% margin on the comparison below. */\n> >         Duration delta = (frameTimestamp - lastRunTimestamp_) * 1.0ns;\n> >         if (lastRunTimestamp_ && frameCount_ > invalidCount_ &&\n> > -           delta < controllerMinFrameDuration * 0.9 && !hdrChange) {\n> > +           delta < controllerMinFrameDuration_ * 0.9 && !hdrChange) {\n> >                 /*\n> >                  * Ensure we merge the previous frame's metadata with the current\n> >                  * frame. This will not overwrite exposure/gain values for the\n> > diff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h\n> > index 5348f2ea..90f018b2 100644\n> > --- a/src/ipa/rpi/common/ipa_base.h\n> > +++ b/src/ipa/rpi/common/ipa_base.h\n> > @@ -142,6 +142,8 @@ private:\n> >         } flickerState_;\n> >\n> >         bool awbEnabled_;\n> > +\n> > +       utils::Duration controllerMinFrameDuration_;\n> >  };\n> >\n> >  } /* namespace ipa::RPi */\n> > diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n> > index fb8e466f..b7655d8d 100644\n> > --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n> > +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n> > @@ -33,6 +33,12 @@ LOG_DEFINE_CATEGORY(RPI)\n> >\n> >  using StreamFlag = RPi::Stream::StreamFlag;\n> >\n> > +/*\n> > + * The IPA's algorithms will not be called more often than this many\n> > + * microseconds. The default corresponds to 30fps.\n> > + */\n> > +constexpr float defaultControllerMinimumFrameDurationUs = 1000000.0 / 30.0;\n> > +\n> >  namespace {\n> >\n> >  constexpr unsigned int defaultRawBitDepth = 12;\n> > @@ -800,6 +806,12 @@ int PipelineHandlerBase::registerCamera(std::unique_ptr<RPi::CameraData> &camera\n> >         if (!data->sensor_)\n> >                 return -EINVAL;\n> >\n> > +       ret = data->loadPipelineConfiguration();\n> > +       if (ret) {\n> > +               LOG(RPI, Error) << \"Unable to load pipeline configuration\";\n> > +               return ret;\n> > +       }\n> > +\n> >         /* Populate the map of sensor supported formats and sizes. */\n> >         for (auto const mbusCode : data->sensor_->mbusCodes())\n> >                 data->sensorFormats_.emplace(mbusCode,\n> > @@ -859,12 +871,6 @@ int PipelineHandlerBase::registerCamera(std::unique_ptr<RPi::CameraData> &camera\n> >         if (ret)\n> >                 return ret;\n> >\n> > -       ret = data->loadPipelineConfiguration();\n> > -       if (ret) {\n> > -               LOG(RPI, Error) << \"Unable to load pipeline configuration\";\n> > -               return ret;\n> > -       }\n> > -\n> >         /* Setup the general IPA signal handlers. */\n> >         data->frontendDevice()->dequeueTimeout.connect(data, &RPi::CameraData::cameraTimeout);\n> >         data->frontendDevice()->frameStart.connect(data, &RPi::CameraData::frameStarted);\n> > @@ -1096,6 +1102,7 @@ int CameraData::loadPipelineConfiguration()\n> >  {\n> >         config_ = {\n> >                 .cameraTimeoutValue = 0,\n> > +               .controllerMinFrameDurationUs = defaultControllerMinimumFrameDurationUs,\n> >         };\n> >\n> >         /* Initial configuration of the platform, in case no config file is present */\n> > @@ -1145,6 +1152,9 @@ int CameraData::loadPipelineConfiguration()\n> >                 frontendDevice()->setDequeueTimeout(config_.cameraTimeoutValue * 1ms);\n> >         }\n> >\n> > +       config_.controllerMinFrameDurationUs =\n> > +               phConfig[\"controller_min_frame_duration_us\"].get<double>(config_.controllerMinFrameDurationUs);\n> > +\n> >         return platformPipelineConfigure(root);\n> >  }\n> >\n> > @@ -1173,6 +1183,8 @@ int CameraData::loadIPA(ipa::RPi::InitResult *result)\n> >         }\n> >\n> >         params.lensPresent = !!sensor_->focusLens();\n> > +       params.controllerMinFrameDurationUs = config_.controllerMinFrameDurationUs;\n> > +\n> >         ret = platformInitIpa(params);\n> >         if (ret)\n> >                 return ret;\n> > diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.h b/src/libcamera/pipeline/rpi/common/pipeline_base.h\n> > index 6257a934..597eb587 100644\n> > --- a/src/libcamera/pipeline/rpi/common/pipeline_base.h\n> > +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.h\n> > @@ -169,6 +169,11 @@ public:\n> >                  * on frame durations.\n> >                  */\n> >                 unsigned int cameraTimeoutValue;\n> > +               /*\n> > +                * The minimum frame duration between the IPA's calls to the\n> > +                * algorithms themselves (in microseconds).\n> > +                */\n> > +               float controllerMinFrameDurationUs;\n> >         };\n> >\n> >         Config config_;\n> > diff --git a/src/libcamera/pipeline/rpi/pisp/data/example.yaml b/src/libcamera/pipeline/rpi/pisp/data/example.yaml\n> > index baf03be7..c5edbba0 100644\n> > --- a/src/libcamera/pipeline/rpi/pisp/data/example.yaml\n> > +++ b/src/libcamera/pipeline/rpi/pisp/data/example.yaml\n> > @@ -36,5 +36,11 @@\n> >                  # framebuffers required for its operation.\n> >                  #\n> >                  # \"disable_hdr\": false,\n> > +\n> > +                # Limits the rate at which IPAs are called. The algorithms will\n> > +                # be skipped until this many microseconds have elapsed since\n> > +                # the last call. The default value represents a 30fps limit.\n> > +                #\n> > +                # \"controller_min_frame_duration_us\": 33333.333,\n> >          }\n> >  }\n> > diff --git a/src/libcamera/pipeline/rpi/vc4/data/example.yaml b/src/libcamera/pipeline/rpi/vc4/data/example.yaml\n> > index 27e54348..2ee2b864 100644\n> > --- a/src/libcamera/pipeline/rpi/vc4/data/example.yaml\n> > +++ b/src/libcamera/pipeline/rpi/vc4/data/example.yaml\n> > @@ -37,5 +37,11 @@\n> >                  # timeout value.\n> >                  #\n> >                  # \"camera_timeout_value_ms\": 0,\n> > +\n> > +                # Limits the rate at which IPAs are called. The algorithms will\n> > +                # be skipped until this many microseconds have elapsed since\n> > +                # the last call. The default value represents a 30fps limit.\n> > +                #\n> > +                # \"controller_min_frame_duration_us\": 33333.333,\n> >          }\n> >  }\n> > --\n> > 2.47.3\n> >","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id C8E37C3220\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 26 Jan 2026 09:44:13 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 710AB61FCA;\n\tMon, 26 Jan 2026 10:44:12 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2421961A35\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 26 Jan 2026 10:44:11 +0100 (CET)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 858CD22F;\n\tMon, 26 Jan 2026 10:43:35 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"IPdBCsTM\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1769420615;\n\tbh=t/b6KjQ+wKAVdsGBHhmlmRvzPyg5LV0y4wDGEVIj8Uw=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=IPdBCsTM0heSFGnVJErIzL8yzyLzM6tcvjdrx8nhxht2Dnd1AaVwYBHswlpg8gCZn\n\t/NG6V2UbJcI6WkLr9hpv4zB/8zXIIBZCsQFVfD6O9toiWO769dKwc+coJoWXTT2Mdm\n\tWp6NS9YLKcdfcM0KN+cvZnVcLSEbTxNy6O/hIDOE=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<CAEmqJPo04AFX7TwTHRpH3uzo4JmkgAGdcrPUcjbpx=-WO+oajQ@mail.gmail.com>","References":"<20260107181445.18005-1-david.plowman@raspberrypi.com>\n\t<CAEmqJPo04AFX7TwTHRpH3uzo4JmkgAGdcrPUcjbpx=-WO+oajQ@mail.gmail.com>","Subject":"Re: [PATCH v3] libcamera: rpi: Make the controller min frame\n\tduration configurable","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","To":"David Plowman <david.plowman@raspberrypi.com>,\n\tNaushir Patuck <naush@raspberrypi.com>","Date":"Mon, 26 Jan 2026 09:44:08 +0000","Message-ID":"<176942064821.1693075.10330723886061204034@ping.linuxembedded.co.uk>","User-Agent":"alot/0.9.1","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]