[{"id":37507,"web_url":"https://patchwork.libcamera.org/comment/37507/","msgid":"<CAEmqJPojT4JY8SPsOeS13s8x8cdxjX+xZnNiVaROu=cb3RWdOQ@mail.gmail.com>","date":"2026-01-07T10:19:01","subject":"Re: [PATCH] libcamera: rpi: Make the controller min frame duration\n\tconfigurable","submitter":{"id":34,"url":"https://patchwork.libcamera.org/api/people/34/","name":"Naushir Patuck","email":"naush@raspberrypi.com"},"content":"Hi David,\n\nThank you for this change.  Few minor comments below:\n\nOn Wed, 7 Jan 2026 at 09:38, 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>  include/libcamera/ipa/raspberrypi.mojom       |  1 +\n>  src/ipa/rpi/common/ipa_base.cpp               | 11 ++++++++-\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, 48 insertions(+), 7 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..2fd101da 100644\n> --- a/src/ipa/rpi/common/ipa_base.cpp\n> +++ b/src/ipa/rpi/common/ipa_base.cpp\n> @@ -184,6 +184,15 @@ 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> +       double dur_us = params.controllerMinFrameDurationUs;\n> +       controllerMinFrameDuration_ = std::chrono::duration<double, std::micro>(dur_us);\n\nProbably should use utils::Duration in place of std::chrono.\n\n> +\n>         return platformInit(params, result);\n>  }\n>\n> @@ -465,7 +474,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\nI wonder if it would be better to use unsigned int instead of float\nhere to match cameraTimeoutValue?  Not too bothered either way.\n\nRegards,\nNaush\n\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 50E0ABE08B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  7 Jan 2026 10:19:41 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 4962061FA0;\n\tWed,  7 Jan 2026 11:19:40 +0100 (CET)","from mail-vk1-xa36.google.com (mail-vk1-xa36.google.com\n\t[IPv6:2607:f8b0:4864:20::a36])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 510FC61F61\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  7 Jan 2026 11:19:38 +0100 (CET)","by mail-vk1-xa36.google.com with SMTP id\n\t71dfb90a1353d-55a4ea09d34so55144e0c.3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 07 Jan 2026 02:19: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=\"GCLuJdhE\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google; t=1767781177; x=1768385977;\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=jDC2pJXVcgmmL+RZlACdlEAz2rFW7tw8Q6/7pisDmls=;\n\tb=GCLuJdhEH02721bhMnC8oj2PJY997YiMG6cu6X7N6UeeCu5QqM4HoP7vidDNNJz3Wj\n\tyrca4osMbkmXsTf811GR1bmpWzHhLM5qq9hdA95XjKC7ay1YnWXdh6oezsfF8daf9lgq\n\t25Bekdegft/yBtZy9pmiNDCvuBkBR+NK6RUQieYjzLGfjxL+JV/sjl2AobjdvvmRYxlv\n\tEx34nyZ8DJln1P5vPlPxRhkzRU5S9oZeT6+DqpLbrkbjIjzysncmv8Nd2SHibcSuoKVI\n\tWM6/RLS0wsz9lnm2LSyprZPJs7MuMOc1xAQtFbCaeLoeaj2jQ4nrrRa1jVQB7piOiHdw\n\t6v5g==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1767781177; x=1768385977;\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=jDC2pJXVcgmmL+RZlACdlEAz2rFW7tw8Q6/7pisDmls=;\n\tb=NwZMDPj0pHDu6hvMNZABRz9+rXlQsSEMrncTNvSyAtOr9+09pfXT3p93lbiY4FgIew\n\tz/zPjK3R9HCoVadoY20ZlKFQCoEoRVidOx/ziAJCmkzYdzjtxll7lbZsdMReDgUeK6D4\n\tZ+SLQWvTKSk+2i5XgGkLFCDGajt8RPoZh+OiQvwjQnKbUpoiyzieqjOn+qPiHM90qCis\n\tdCuWzcotURQRCbqGWy1jMUuAuyMU4wfkARdn+NRAwvBAdKqlppIWo8BhgtXAGW3OFOt2\n\twNVLP3UA4Pl8UKPMNSXorViFMiHexlai+WvUwufWdNmEXG4kI6CA1WpO6T4t0ffv9Sqr\n\tHpxA==","X-Gm-Message-State":"AOJu0YwBmi4aAIQaC+yZ9b0u4H80iHxsaUMfkIwarVtY+t+hwJusTtW6\n\tp4mCmknJzxmIaW31vmRKB/pWeGMSGuOzVn0dSuxbkELGG0PD+bMwepl2h2VEWdmK4rZ49WgXPGL\n\taWylKDTlHoMVlDiut3pvoF3/AipRePYPlLz3esG+wzQ==","X-Gm-Gg":"AY/fxX7qLEnPtHyjaFK013FyuBPmYiQnSVDkultc673sgBown5ThUAk5ukvsM/Jhcqb\n\tvL3XfYgQ4xxi0V8C2UNv8n56Sq+pOOS7kC8iiitmEcOi9ScfIU++67qccRTJIMotP0RbifoAMGO\n\tCUIcSKQlyfAFdoeJG35+AScaRPyS5Odr6Xr2cXTsf9oYScameA2JG80Q/OXCx93Qk49JHnMnqU4\n\tuLsFx0i9M++mx55Fdnjy6tJ83Uv9N7UryybEIfucG3m+7a7SF5ybbQYg6RI8tTfksLYzN5o2rh/\n\thWb1U5W3LuxYrwx3TycZf7kvjuk=","X-Google-Smtp-Source":"AGHT+IE87dYuCcvspM8Px8qqLUiD5MVjiPmBFaPrcE5Pih58QSTVGnA8ztB8F3pHmmZJsJGDXpBBnZVe7L4YtunRksI=","X-Received":"by 2002:a05:6102:190a:b0:5e4:9555:8984 with SMTP id\n\tada2fe7eead31-5ecb6957bd8mr293989137.6.1767781176975; Wed, 07 Jan 2026\n\t02:19:36 -0800 (PST)","MIME-Version":"1.0","References":"<20260107093821.14600-1-david.plowman@raspberrypi.com>","In-Reply-To":"<20260107093821.14600-1-david.plowman@raspberrypi.com>","From":"Naushir Patuck <naush@raspberrypi.com>","Date":"Wed, 7 Jan 2026 10:19:01 +0000","X-Gm-Features":"AQt7F2qDONw44WiHIwvY7wqd56d77nFvGrOgAVX8eHX48LzjTVDORoOlCCPkg0Q","Message-ID":"<CAEmqJPojT4JY8SPsOeS13s8x8cdxjX+xZnNiVaROu=cb3RWdOQ@mail.gmail.com>","Subject":"Re: [PATCH] libcamera: rpi: Make the controller min frame duration\n\tconfigurable","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":37509,"web_url":"https://patchwork.libcamera.org/comment/37509/","msgid":"<CAHW6GYKVvhn8OWYeeY0ERcJntxH4rd_cdj2rkFB+amsUYRBQJw@mail.gmail.com>","date":"2026-01-07T11:03:56","subject":"Re: [PATCH] libcamera: rpi: Make the controller min frame duration\n\tconfigurable","submitter":{"id":42,"url":"https://patchwork.libcamera.org/api/people/42/","name":"David Plowman","email":"david.plowman@raspberrypi.com"},"content":"Hi Naush\n\nThanks for the review!\n\nOn Wed, 7 Jan 2026 at 10:19, Naushir Patuck <naush@raspberrypi.com> wrote:\n>\n> Hi David,\n>\n> Thank you for this change.  Few minor comments below:\n>\n> On Wed, 7 Jan 2026 at 09:38, 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> >  include/libcamera/ipa/raspberrypi.mojom       |  1 +\n> >  src/ipa/rpi/common/ipa_base.cpp               | 11 ++++++++-\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, 48 insertions(+), 7 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..2fd101da 100644\n> > --- a/src/ipa/rpi/common/ipa_base.cpp\n> > +++ b/src/ipa/rpi/common/ipa_base.cpp\n> > @@ -184,6 +184,15 @@ 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> > +       double dur_us = params.controllerMinFrameDurationUs;\n> > +       controllerMinFrameDuration_ = std::chrono::duration<double, std::micro>(dur_us);\n>\n> Probably should use utils::Duration in place of std::chrono.\n\nYes, I will find something tidier, those duration/chrono casts always\ndo my head in a bit! I guess\n\ncontrollerMinFrameDuration_ = params.controllerMinFrameDurationUs * 1us;\n\nwould be fine and looks better.\n\n>\n> > +\n> >         return platformInit(params, result);\n> >  }\n> >\n> > @@ -465,7 +474,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> I wonder if it would be better to use unsigned int instead of float\n> here to match cameraTimeoutValue?  Not too bothered either way.\n\nSlightly inclined to leave this, but again, I don't really mind. I've\nbeen developing a bit of a philosophical aversion to using ints for\nmicroseconds in general, though I agree it would make no difference\nhere.\n\nAnyway, version 2 incoming!\n\nDavid\n\n>\n> Regards,\n> Naush\n>\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 A68CEBDCBF\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  7 Jan 2026 11:04:11 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id AB5E361FC1;\n\tWed,  7 Jan 2026 12:04:10 +0100 (CET)","from mail-qv1-xf36.google.com (mail-qv1-xf36.google.com\n\t[IPv6:2607:f8b0:4864:20::f36])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id DF75061F84\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  7 Jan 2026 12:04:08 +0100 (CET)","by mail-qv1-xf36.google.com with SMTP id\n\t6a1803df08f44-88a34450f19so19219056d6.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 07 Jan 2026 03:04:08 -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=\"D2G8Zz/u\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google; t=1767783848; x=1768388648;\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=a+1+VUuSkAeQoKtlWM6NXVVapcc0QrG5t9L5OxY2WOQ=;\n\tb=D2G8Zz/u25K4ZBntWYS0JfOcCtVz51VZ0ez9+nXX6W7bWx1rz5YrYUpdEmhWxL/VrY\n\tJPc3C6VK+/komdOjHwBqk8dwFPJZRKBB50qnu6oIiFX+FWLazZOXicfEvLIFD9pAJOCx\n\tQWv+I0pTy8nUp02oLMtA6UkNTA1v1/kFhuzK9DpmPXaa22hWbVA6c1EWqQea36ylUni6\n\tEAq9T0e1GuXajtYxBtIoOOHRhh/Y0BlrQTQHQ1Y7/RC419o433Uv/1uU7XswrnFzMsR+\n\ttNjvlb9vnXEEtkkfkurfXRkxazWOYgHTFPh0jEoc7MfKuovwJxSGRo0HSi1ekGtI97Ed\n\tEgJg==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1767783848; x=1768388648;\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=a+1+VUuSkAeQoKtlWM6NXVVapcc0QrG5t9L5OxY2WOQ=;\n\tb=FPB96gd+5KnF6JD5NoTbPDIDOADxIEMeBs+Ow6oeZ6J3/O4MF9jFxjMOpNNwpH7vJL\n\tnb2UOGXsfsXwu3zhSPhOsHafMfOldtju3Xww/l/JcNQwGx6YdzmIBEvW06kB9YimO70U\n\tBNFRORAEfWRkCSRKYeoD6Algx2km0t0pvsaRrFUD563C4D4+zAgvER78qLchC3XhCown\n\tnTpf3x5G8lYm0lj3N3tmaT+35BMaVi/Ka0pzFwo37fLLHF4oefxHEfM6bQfzalQOeyVP\n\tissOTAD6LvfTIFd50RpVVw5e3z3h971bZGJGJMxSep7LuOZguQuri2IbgitoGs9AcXHt\n\tTZrQ==","X-Gm-Message-State":"AOJu0YyZzUyfmVlrJlEOU46PX6elH2HtnY3S0tecLvZBYSBFrXBhj+5h\n\t8XsJd/UNMhjY1unQj5LiQY4Td5jA10fLOwlFaxfGsMh93NyuuQhd+BVzRcH3kxNco6OXrMm6Egu\n\tveFPxeS6Wiay7mr41u1qlIBQ32bRIyFBSFZ/rwD+U0viLmmsCAPReYl28+Q==","X-Gm-Gg":"AY/fxX7wpYAIKPyQlwiLJ55MJR+JmJS+oMQlrEq7ZFmbWWLts5OYwBy1+yBud/hm8ew\n\tMPXyLY55PsLW4VL8yUkvkjd4nbCfcvxjin5VM8fTl2a2K1Ux2QPHd0DeVbauNhvSGxhgH77RqYN\n\tG1rXStrYPDhJzDh6w6G48PL/BK05mQj77YSDP3sKPRIUD82Q757RQvVINggBBL5+d+Ykz3qrx4C\n\t/0DXg5Uy37u9tGij080Qw6bq3qPWFbPsa8yEzhrT6Jk5B1WbLCB/0nZ2IQi8EgUS/aXi9LiIXQe\n\tbQxi/ZEnXzs6FpuTB0GbNQ1eMdWri0AQcOxlRzywmIUeQdmw1ohrVHxcaQJCMJiW+ZdHBIK/LNl\n\twEPHMLywXQI7D","X-Google-Smtp-Source":"AGHT+IHDw25rWTlto3suxqJRI2Pf+KoxXFfFUQIHBYViAdxIhzHHLaZWoYECQGBwqrNvxSiPQfpH/0+g5ABqZSeW5Y8=","X-Received":"by 2002:ad4:5c87:0:b0:888:7c7e:fce1 with SMTP id\n\t6a1803df08f44-89084175250mr30949376d6.4.1767783847485;\n\tWed, 07 Jan 2026 03:04:07 -0800 (PST)","MIME-Version":"1.0","References":"<20260107093821.14600-1-david.plowman@raspberrypi.com>\n\t<CAEmqJPojT4JY8SPsOeS13s8x8cdxjX+xZnNiVaROu=cb3RWdOQ@mail.gmail.com>","In-Reply-To":"<CAEmqJPojT4JY8SPsOeS13s8x8cdxjX+xZnNiVaROu=cb3RWdOQ@mail.gmail.com>","From":"David Plowman <david.plowman@raspberrypi.com>","Date":"Wed, 7 Jan 2026 11:03:56 +0000","X-Gm-Features":"AQt7F2pwDEUx7vdKDy3-UGBS9PolA8YiZo1h9nyLCXxtLgE_SOL6q8cAa7esU20","Message-ID":"<CAHW6GYKVvhn8OWYeeY0ERcJntxH4rd_cdj2rkFB+amsUYRBQJw@mail.gmail.com>","Subject":"Re: [PATCH] libcamera: rpi: Make the controller min frame duration\n\tconfigurable","To":"Naushir Patuck <naush@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>"}}]