[{"id":14220,"web_url":"https://patchwork.libcamera.org/comment/14220/","msgid":"<CAHW6GYK2i7JzF_pUd1UGMDZTwg_=Sg6U-NYSX2QNegTWCrPnZw@mail.gmail.com>","date":"2020-12-11T13:57:49","subject":"Re: [libcamera-devel] [PATCH v7 2/3] libcamera: raspberrypi: Add\n\tcontrol of sensor vblanking","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 revised patch!\n\nOn Fri, 11 Dec 2020 at 13:35, Naushir Patuck <naush@raspberrypi.com> wrote:\n>\n> Add support for setting V4L2_CID_VBLANK appropriately when setting\n> V4L2_CID_EXPOSURE. This will allow adaptive framerates during\n> viewfinder use cases (e.g. when the exposure time goes above 33ms, we\n> can reduce the framerate to lower than 30fps).\n>\n> The minimum and maximum frame durations are provided via libcamera\n> controls, and will prioritise exposure time limits over any AGC request.\n>\n> V4L2_CID_VBLANK is controlled through the staggered writer, just like\n> the exposure and gain controls.\n>\n> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>\n> ---\n>  include/libcamera/ipa/raspberrypi.h           |  1 +\n>  src/ipa/raspberrypi/cam_helper.cpp            | 35 +++++++++++++++-\n>  src/ipa/raspberrypi/cam_helper.hpp            | 15 ++++++-\n>  src/ipa/raspberrypi/cam_helper_imx219.cpp     | 13 +++++-\n>  src/ipa/raspberrypi/cam_helper_imx477.cpp     | 11 ++++-\n>  src/ipa/raspberrypi/cam_helper_ov5647.cpp     | 11 ++++-\n>  src/ipa/raspberrypi/raspberrypi.cpp           | 42 ++++++++++++++++---\n>  .../pipeline/raspberrypi/raspberrypi.cpp      |  3 +-\n>  8 files changed, 118 insertions(+), 13 deletions(-)\n>\n> diff --git a/include/libcamera/ipa/raspberrypi.h b/include/libcamera/ipa/raspberrypi.h\n> index 01fe5abc..160ca681 100644\n> --- a/include/libcamera/ipa/raspberrypi.h\n> +++ b/include/libcamera/ipa/raspberrypi.h\n> @@ -65,6 +65,7 @@ static const ControlInfoMap Controls = {\n>         { &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) },\n>         { &controls::ColourCorrectionMatrix, ControlInfo(-16.0f, 16.0f) },\n>         { &controls::ScalerCrop, ControlInfo(Rectangle{}, Rectangle(65535, 65535, 65535, 65535), Rectangle{}) },\n> +       { &controls::FrameDurations, ControlInfo(1.0e3f, 1.0e9f) },\n\nJust wondering whether it looks a bit strange to initialise explicitly\nwith floats now that they're int64_t, though I guess it makes no real\ndifference...\n\nBut other than this tiny thing:\n\nReviewed-by: David Plowman <david.plowman@raspberrypi.com>\nTested-by: David Plowman <david.plowman@raspberrypi.com>\n\nThanks!\nDavid\n\n>  };\n>\n>  } /* namespace RPi */\n> diff --git a/src/ipa/raspberrypi/cam_helper.cpp b/src/ipa/raspberrypi/cam_helper.cpp\n> index c8ac3232..7fd3e7f6 100644\n> --- a/src/ipa/raspberrypi/cam_helper.cpp\n> +++ b/src/ipa/raspberrypi/cam_helper.cpp\n> @@ -34,8 +34,10 @@ CamHelper *CamHelper::Create(std::string const &cam_name)\n>         return nullptr;\n>  }\n>\n> -CamHelper::CamHelper(MdParser *parser)\n> -       : parser_(parser), initialized_(false)\n> +CamHelper::CamHelper(MdParser *parser, unsigned int maxFrameLength,\n> +                    unsigned int frameIntegrationDiff)\n> +       : parser_(parser), initialized_(false), maxFrameLength_(maxFrameLength),\n> +         frameIntegrationDiff_(frameIntegrationDiff)\n>  {\n>  }\n>\n> @@ -56,6 +58,35 @@ double CamHelper::Exposure(uint32_t exposure_lines) const\n>         return exposure_lines * mode_.line_length / 1000.0;\n>  }\n>\n> +uint32_t CamHelper::GetVBlanking(double &exposure, double minFrameDuration,\n> +                                double maxFrameDuration) const\n> +{\n> +       uint32_t frameLengthMin, frameLengthMax, vblank;\n> +       uint32_t exposureLines = ExposureLines(exposure);\n> +\n> +       assert(initialized_);\n> +\n> +       /*\n> +        * Clip frame length by the frame duration range and the maximum allowable\n> +        * value in the sensor, given by maxFrameLength_.\n> +        */\n> +       frameLengthMin = std::clamp<uint32_t>(1e3 * minFrameDuration / mode_.line_length,\n> +                                             mode_.height, maxFrameLength_);\n> +       frameLengthMax = std::clamp<uint32_t>(1e3 * maxFrameDuration / mode_.line_length,\n> +                                             mode_.height, maxFrameLength_);\n> +       /*\n> +        * Limit the exposure to the maximum frame duration requested, and\n> +        * re-calculate if it has been clipped.\n> +        */\n> +       exposureLines = std::min(frameLengthMax - frameIntegrationDiff_, exposureLines);\n> +       exposure = Exposure(exposureLines);\n> +\n> +       /* Limit the vblank to the range allowed by the frame length limits. */\n> +       vblank = std::clamp(exposureLines + frameIntegrationDiff_,\n> +                           frameLengthMin, frameLengthMax) - mode_.height;\n> +       return vblank;\n> +}\n> +\n>  void CamHelper::SetCameraMode(const CameraMode &mode)\n>  {\n>         mode_ = mode;\n> diff --git a/src/ipa/raspberrypi/cam_helper.hpp b/src/ipa/raspberrypi/cam_helper.hpp\n> index 044c2866..b1739a57 100644\n> --- a/src/ipa/raspberrypi/cam_helper.hpp\n> +++ b/src/ipa/raspberrypi/cam_helper.hpp\n> @@ -62,12 +62,15 @@ class CamHelper\n>  {\n>  public:\n>         static CamHelper *Create(std::string const &cam_name);\n> -       CamHelper(MdParser *parser);\n> +       CamHelper(MdParser *parser, unsigned int maxFrameLength,\n> +                 unsigned int frameIntegrationDiff);\n>         virtual ~CamHelper();\n>         void SetCameraMode(const CameraMode &mode);\n>         MdParser &Parser() const { return *parser_; }\n>         uint32_t ExposureLines(double exposure_us) const;\n>         double Exposure(uint32_t exposure_lines) const; // in us\n> +       virtual uint32_t GetVBlanking(double &exposure_us, double minFrameDuration,\n> +                                     double maxFrameDuration) const;\n>         virtual uint32_t GainCode(double gain) const = 0;\n>         virtual double Gain(uint32_t gain_code) const = 0;\n>         virtual void GetDelays(int &exposure_delay, int &gain_delay) const;\n> @@ -76,10 +79,20 @@ public:\n>         virtual unsigned int HideFramesModeSwitch() const;\n>         virtual unsigned int MistrustFramesStartup() const;\n>         virtual unsigned int MistrustFramesModeSwitch() const;\n> +\n>  protected:\n>         MdParser *parser_;\n>         CameraMode mode_;\n> +\n> +private:\n>         bool initialized_;\n> +       /* Largest possible frame length, in units of lines. */\n> +       unsigned int maxFrameLength_;\n> +       /*\n> +        * Smallest difference between the frame length and integration time,\n> +        * in units of lines.\n> +        */\n> +       unsigned int frameIntegrationDiff_;\n>  };\n>\n>  // This is for registering camera helpers with the system, so that the\n> diff --git a/src/ipa/raspberrypi/cam_helper_imx219.cpp b/src/ipa/raspberrypi/cam_helper_imx219.cpp\n> index db8ab879..8688ec09 100644\n> --- a/src/ipa/raspberrypi/cam_helper_imx219.cpp\n> +++ b/src/ipa/raspberrypi/cam_helper_imx219.cpp\n> @@ -49,13 +49,22 @@ public:\n>         double Gain(uint32_t gain_code) const override;\n>         unsigned int MistrustFramesModeSwitch() const override;\n>         bool SensorEmbeddedDataPresent() const override;\n> +\n> +private:\n> +       /*\n> +        * Smallest difference between the frame length and integration time,\n> +        * in units of lines.\n> +        */\n> +       static constexpr int frameIntegrationDiff = 4;\n> +       /* Largest possible frame length, in units of lines. */\n> +       static constexpr int maxFrameLength = 0xffff;\n>  };\n>\n>  CamHelperImx219::CamHelperImx219()\n>  #if ENABLE_EMBEDDED_DATA\n> -       : CamHelper(new MdParserImx219())\n> +       : CamHelper(new MdParserImx219(), maxFrameLength, frameIntegrationDiff)\n>  #else\n> -       : CamHelper(new MdParserRPi())\n> +       : CamHelper(new MdParserRPi(), maxFrameLength, frameIntegrationDiff)\n>  #endif\n>  {\n>  }\n> diff --git a/src/ipa/raspberrypi/cam_helper_imx477.cpp b/src/ipa/raspberrypi/cam_helper_imx477.cpp\n> index 0e896ac7..53961310 100644\n> --- a/src/ipa/raspberrypi/cam_helper_imx477.cpp\n> +++ b/src/ipa/raspberrypi/cam_helper_imx477.cpp\n> @@ -38,10 +38,19 @@ public:\n>         uint32_t GainCode(double gain) const override;\n>         double Gain(uint32_t gain_code) const override;\n>         bool SensorEmbeddedDataPresent() const override;\n> +\n> +private:\n> +       /*\n> +        * Smallest difference between the frame length and integration time,\n> +        * in units of lines.\n> +        */\n> +       static constexpr int frameIntegrationDiff = 22;\n> +       /* Largest possible frame length, in units of lines. */\n> +       static constexpr int maxFrameLength = 0xffdc;\n>  };\n>\n>  CamHelperImx477::CamHelperImx477()\n> -       : CamHelper(new MdParserImx477())\n> +       : CamHelper(new MdParserImx477(), maxFrameLength, frameIntegrationDiff)\n>  {\n>  }\n>\n> diff --git a/src/ipa/raspberrypi/cam_helper_ov5647.cpp b/src/ipa/raspberrypi/cam_helper_ov5647.cpp\n> index dc5d8275..7630c127 100644\n> --- a/src/ipa/raspberrypi/cam_helper_ov5647.cpp\n> +++ b/src/ipa/raspberrypi/cam_helper_ov5647.cpp\n> @@ -22,6 +22,15 @@ public:\n>         unsigned int HideFramesModeSwitch() const override;\n>         unsigned int MistrustFramesStartup() const override;\n>         unsigned int MistrustFramesModeSwitch() const override;\n> +\n> +private:\n> +       /*\n> +        * Smallest difference between the frame length and integration time,\n> +        * in units of lines.\n> +        */\n> +       static constexpr int frameIntegrationDiff = 4;\n> +       /* Largest possible frame length, in units of lines. */\n> +       static constexpr int maxFrameLength = 0xffff;\n>  };\n>\n>  /*\n> @@ -30,7 +39,7 @@ public:\n>   */\n>\n>  CamHelperOv5647::CamHelperOv5647()\n> -       : CamHelper(new MdParserRPi())\n> +       : CamHelper(new MdParserRPi(), maxFrameLength, frameIntegrationDiff)\n>  {\n>  }\n>\n> diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp\n> index 60cfdc27..f6d2b543 100644\n> --- a/src/ipa/raspberrypi/raspberrypi.cpp\n> +++ b/src/ipa/raspberrypi/raspberrypi.cpp\n> @@ -58,6 +58,8 @@ namespace libcamera {\n>  /* Configure the sensor with these values initially. */\n>  constexpr double DefaultAnalogueGain = 1.0;\n>  constexpr unsigned int DefaultExposureTime = 20000;\n> +constexpr double defaultMinFrameDuration = 1e6 / 30.0;\n> +constexpr double defaultMaxFrameDuration = 1e6 / 0.01;\n>\n>  LOG_DEFINE_CATEGORY(IPARPI)\n>\n> @@ -145,6 +147,9 @@ private:\n>         /* LS table allocation passed in from the pipeline handler. */\n>         FileDescriptor lsTableHandle_;\n>         void *lsTable_;\n> +\n> +       /* Frame duration (1/fps) given in microseconds. */\n> +       double minFrameDuration_, maxFrameDuration_;\n>  };\n>\n>  int IPARPi::init(const IPASettings &settings)\n> @@ -266,7 +271,8 @@ void IPARPi::configure(const CameraSensorInfo &sensorInfo,\n>                 sensorMetadata = helper_->SensorEmbeddedDataPresent();\n>\n>                 result->data.push_back(gainDelay);\n> -               result->data.push_back(exposureDelay);\n> +               result->data.push_back(exposureDelay); /* FOR EXPOSURE ctrl */\n> +               result->data.push_back(exposureDelay); /* For VBLANK ctrl */\n>                 result->data.push_back(sensorMetadata);\n>\n>                 result->operation |= RPi::IPA_CONFIG_STAGGERED_WRITE;\n> @@ -335,6 +341,8 @@ void IPARPi::configure(const CameraSensorInfo &sensorInfo,\n>                 AgcStatus agcStatus;\n>                 agcStatus.shutter_time = DefaultExposureTime;\n>                 agcStatus.analogue_gain = DefaultAnalogueGain;\n> +               minFrameDuration_ = defaultMinFrameDuration;\n> +               maxFrameDuration_ = defaultMaxFrameDuration;\n>                 applyAGC(&agcStatus, ctrls);\n>\n>                 result->controls.emplace_back(ctrls);\n> @@ -712,6 +720,18 @@ void IPARPi::queueRequest(const ControlList &controls)\n>                         break;\n>                 }\n>\n> +               case controls::FRAME_DURATIONS: {\n> +                       auto frameDurations = ctrl.second.get<Span<const int64_t>>();\n> +\n> +                       /* This will be applied once AGC recalculations occur. */\n> +                       minFrameDuration_ = frameDurations[0] ? frameDurations[0] : defaultMinFrameDuration;\n> +                       maxFrameDuration_ = frameDurations[1] ? frameDurations[1] : defaultMaxFrameDuration;\n> +                       libcameraMetadata_.set(controls::FrameDurations,\n> +                                              { static_cast<int64_t>(minFrameDuration_),\n> +                                                static_cast<int64_t>(maxFrameDuration_) });\n> +                       break;\n> +               }\n> +\n>                 default:\n>                         LOG(IPARPI, Warning)\n>                                 << \"Ctrl \" << controls::controls.at(ctrl.first)->name()\n> @@ -882,7 +902,12 @@ void IPARPi::applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls)\n>  void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls)\n>  {\n>         int32_t gainCode = helper_->GainCode(agcStatus->analogue_gain);\n> -       int32_t exposureLines = helper_->ExposureLines(agcStatus->shutter_time);\n> +\n> +       /* GetVBlanking might clip exposure time to the fps limits. */\n> +       double exposure = agcStatus->shutter_time;\n> +       int32_t vblanking = helper_->GetVBlanking(exposure, minFrameDuration_,\n> +                                                 maxFrameDuration_);\n> +       int32_t exposureLines = helper_->ExposureLines(exposure);\n>\n>         if (unicamCtrls_.find(V4L2_CID_ANALOGUE_GAIN) == unicamCtrls_.end()) {\n>                 LOG(IPARPI, Error) << \"Can't find analogue gain control\";\n> @@ -894,13 +919,20 @@ void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls)\n>                 return;\n>         }\n>\n> -       LOG(IPARPI, Debug) << \"Applying AGC Exposure: \" << agcStatus->shutter_time\n> -                          << \" (Shutter lines: \" << exposureLines << \") Gain: \"\n> +       LOG(IPARPI, Debug) << \"Applying AGC Exposure: \" << exposure\n> +                          << \" (Shutter lines: \" << exposureLines << \", AGC requested \"\n> +                          << agcStatus->shutter_time << \") Gain: \"\n>                            << agcStatus->analogue_gain << \" (Gain Code: \"\n>                            << gainCode << \")\";\n>\n> -       ctrls.set(V4L2_CID_ANALOGUE_GAIN, gainCode);\n> +       /*\n> +        * Due to the behavior of V4L2, the current value of VBLANK could clip the\n> +        * exposure time without us knowing. The next time though this function should\n> +        * clip exposure correctly.\n> +        */\n> +       ctrls.set(V4L2_CID_VBLANK, vblanking);\n>         ctrls.set(V4L2_CID_EXPOSURE, exposureLines);\n> +       ctrls.set(V4L2_CID_ANALOGUE_GAIN, gainCode);\n>  }\n>\n>  void IPARPi::applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls)\n> diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> index 2ec1f6e7..13349f31 100644\n> --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> @@ -1221,7 +1221,8 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config)\n>                 if (!staggeredCtrl_) {\n>                         staggeredCtrl_.init(unicam_[Unicam::Image].dev(),\n>                                             { { V4L2_CID_ANALOGUE_GAIN, result.data[resultIdx++] },\n> -                                             { V4L2_CID_EXPOSURE, result.data[resultIdx++] } });\n> +                                             { V4L2_CID_EXPOSURE, result.data[resultIdx++] },\n> +                                             { V4L2_CID_VBLANK, result.data[resultIdx++] } });\n>                         sensorMetadata_ = result.data[resultIdx++];\n>                 }\n>         }\n> --\n> 2.25.1\n>\n> _______________________________________________\n> libcamera-devel mailing list\n> libcamera-devel@lists.libcamera.org\n> https://lists.libcamera.org/listinfo/libcamera-devel","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 47475BD808\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 11 Dec 2020 13:58:04 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id BB17F67F9E;\n\tFri, 11 Dec 2020 14:58:03 +0100 (CET)","from mail-ot1-x341.google.com (mail-ot1-x341.google.com\n\t[IPv6:2607:f8b0:4864:20::341])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 8D08767F06\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 11 Dec 2020 14:58:02 +0100 (CET)","by mail-ot1-x341.google.com with SMTP id x13so8257080oto.8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 11 Dec 2020 05:58:02 -0800 (PST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=raspberrypi.com header.i=@raspberrypi.com\n\theader.b=\"t6KV4nmw\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google;\n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=ALPvlg77dx6YHL5wnix/1ph+VStOAYsoSYdhtMUlj4k=;\n\tb=t6KV4nmw7g9c3Z0Km9WYr064Y3Az/2t9B7i3dOWZ2BE0h1PbW5ptBV2u+d4k7azQHx\n\tppyqTy8rs8crxstvHDG8XRXcthYJ4SFJODqnBJKn5lg3dj2jbNmvn6se6RI8TQcixiIn\n\t0k2zd2jFRFEW1WGGiCTFA0ijKg4/g17hmQn+0Wp1Idnzp01ojeiXWUGlo5hLRcl1RxLm\n\tfiQGAya35nozFTIfepZMKQBmxnrbsH8LfX9P09+6V2YbJ7SwgqYz6KK9sZcwt7dqHWgy\n\tEG8/aawjJwDV7Xe3BV87Tc1cqE5l2wB6w8g1BaiGnyE4ybc4Z1ToERmF+9kQRlbtn6EK\n\t+jrg==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=ALPvlg77dx6YHL5wnix/1ph+VStOAYsoSYdhtMUlj4k=;\n\tb=mB97ocKaekLylLI1uJLcqxIC9jGqgJNd/bAzeRm10mEWCa41/dmZwGRT4mRdvdtX73\n\tBnLyRkE1e9E0PVjLK5Htrl1Cwkng6hRCXb4rKT+/0lhvEepaRAefThDJVRBX54U9jhhf\n\t9NrptO6kIGFw+kD1ZWjYI9oYRBiW1OOscaPBwbHXtBiu92dTMElniLbxWuNfgkpMpNj4\n\t3QfwEzWB2MJoQWZm2TK18kbPaTbHruFyNseZwAR6XCSASCltRmpG8k+kJiXqwK5VaI2e\n\t/kK9Xre7JwB4AxGGF+gPYzQtNE3VPma7j5+rXF3HwUQshpgdL1HeZSjB7sMMg4iQ1TWC\n\tp8Yw==","X-Gm-Message-State":"AOAM5327b4oojBtg5XZKGZqawyCqjx9iTPAJ/otv6P2pCH9RbRCtNCBP\n\tOj7j1dhd4jZya5pD/tAu8cSG6gPei+cG/JdUFXUo9A==","X-Google-Smtp-Source":"ABdhPJygRr04dGGqiY935qI/pEDi1mjGuFl1hxymOkEc7jAZ6xAJEcBH/FAuvjGar7/WAfV/rJHjZlCWnSsUemMQvII=","X-Received":"by 2002:a05:6830:18ee:: with SMTP id\n\td14mr9457389otf.317.1607695081221; \n\tFri, 11 Dec 2020 05:58:01 -0800 (PST)","MIME-Version":"1.0","References":"<20201211133510.391037-1-naush@raspberrypi.com>\n\t<20201211133510.391037-2-naush@raspberrypi.com>","In-Reply-To":"<20201211133510.391037-2-naush@raspberrypi.com>","From":"David Plowman <david.plowman@raspberrypi.com>","Date":"Fri, 11 Dec 2020 13:57:49 +0000","Message-ID":"<CAHW6GYK2i7JzF_pUd1UGMDZTwg_=Sg6U-NYSX2QNegTWCrPnZw@mail.gmail.com>","To":"Naushir Patuck <naush@raspberrypi.com>","Subject":"Re: [libcamera-devel] [PATCH v7 2/3] libcamera: raspberrypi: Add\n\tcontrol of sensor vblanking","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>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":14221,"web_url":"https://patchwork.libcamera.org/comment/14221/","msgid":"<CAEmqJPoEKBJQh4RRODXkp9ojo6u1kqjzB1JeQE6EQR8JYdzzMA@mail.gmail.com>","date":"2020-12-11T14:03:25","subject":"Re: [libcamera-devel] [PATCH v7 2/3] libcamera: raspberrypi: Add\n\tcontrol of sensor vblanking","submitter":{"id":34,"url":"https://patchwork.libcamera.org/api/people/34/","name":"Naushir Patuck","email":"naush@raspberrypi.com"},"content":"Hi David,\n\n\n\nOn Fri, 11 Dec 2020 at 13:58, David Plowman <david.plowman@raspberrypi.com>\nwrote:\n\n> Hi Naush\n>\n> Thanks for the revised patch!\n>\n> On Fri, 11 Dec 2020 at 13:35, Naushir Patuck <naush@raspberrypi.com>\n> wrote:\n> >\n> > Add support for setting V4L2_CID_VBLANK appropriately when setting\n> > V4L2_CID_EXPOSURE. This will allow adaptive framerates during\n> > viewfinder use cases (e.g. when the exposure time goes above 33ms, we\n> > can reduce the framerate to lower than 30fps).\n> >\n> > The minimum and maximum frame durations are provided via libcamera\n> > controls, and will prioritise exposure time limits over any AGC request.\n> >\n> > V4L2_CID_VBLANK is controlled through the staggered writer, just like\n> > the exposure and gain controls.\n> >\n> > Signed-off-by: Naushir Patuck <naush@raspberrypi.com>\n> > ---\n> >  include/libcamera/ipa/raspberrypi.h           |  1 +\n> >  src/ipa/raspberrypi/cam_helper.cpp            | 35 +++++++++++++++-\n> >  src/ipa/raspberrypi/cam_helper.hpp            | 15 ++++++-\n> >  src/ipa/raspberrypi/cam_helper_imx219.cpp     | 13 +++++-\n> >  src/ipa/raspberrypi/cam_helper_imx477.cpp     | 11 ++++-\n> >  src/ipa/raspberrypi/cam_helper_ov5647.cpp     | 11 ++++-\n> >  src/ipa/raspberrypi/raspberrypi.cpp           | 42 ++++++++++++++++---\n> >  .../pipeline/raspberrypi/raspberrypi.cpp      |  3 +-\n> >  8 files changed, 118 insertions(+), 13 deletions(-)\n> >\n> > diff --git a/include/libcamera/ipa/raspberrypi.h\n> b/include/libcamera/ipa/raspberrypi.h\n> > index 01fe5abc..160ca681 100644\n> > --- a/include/libcamera/ipa/raspberrypi.h\n> > +++ b/include/libcamera/ipa/raspberrypi.h\n> > @@ -65,6 +65,7 @@ static const ControlInfoMap Controls = {\n> >         { &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) },\n> >         { &controls::ColourCorrectionMatrix, ControlInfo(-16.0f, 16.0f)\n> },\n> >         { &controls::ScalerCrop, ControlInfo(Rectangle{},\n> Rectangle(65535, 65535, 65535, 65535), Rectangle{}) },\n> > +       { &controls::FrameDurations, ControlInfo(1.0e3f, 1.0e9f) },\n>\n> Just wondering whether it looks a bit strange to initialise explicitly\n> with floats now that they're int64_t, though I guess it makes no real\n> difference...\n>\n\nThis is wrong!  I forgot to update this bit of code, thanks for spotting\nthat!\n\n\n>\n> But other than this tiny thing:\n>\n> Reviewed-by: David Plowman <david.plowman@raspberrypi.com>\n> Tested-by: David Plowman <david.plowman@raspberrypi.com>\n>\n> Thanks!\n> David\n>\n> >  };\n> >\n> >  } /* namespace RPi */\n> > diff --git a/src/ipa/raspberrypi/cam_helper.cpp\n> b/src/ipa/raspberrypi/cam_helper.cpp\n> > index c8ac3232..7fd3e7f6 100644\n> > --- a/src/ipa/raspberrypi/cam_helper.cpp\n> > +++ b/src/ipa/raspberrypi/cam_helper.cpp\n> > @@ -34,8 +34,10 @@ CamHelper *CamHelper::Create(std::string const\n> &cam_name)\n> >         return nullptr;\n> >  }\n> >\n> > -CamHelper::CamHelper(MdParser *parser)\n> > -       : parser_(parser), initialized_(false)\n> > +CamHelper::CamHelper(MdParser *parser, unsigned int maxFrameLength,\n> > +                    unsigned int frameIntegrationDiff)\n> > +       : parser_(parser), initialized_(false),\n> maxFrameLength_(maxFrameLength),\n> > +         frameIntegrationDiff_(frameIntegrationDiff)\n> >  {\n> >  }\n> >\n> > @@ -56,6 +58,35 @@ double CamHelper::Exposure(uint32_t exposure_lines)\n> const\n> >         return exposure_lines * mode_.line_length / 1000.0;\n> >  }\n> >\n> > +uint32_t CamHelper::GetVBlanking(double &exposure, double\n> minFrameDuration,\n> > +                                double maxFrameDuration) const\n> > +{\n> > +       uint32_t frameLengthMin, frameLengthMax, vblank;\n> > +       uint32_t exposureLines = ExposureLines(exposure);\n> > +\n> > +       assert(initialized_);\n> > +\n> > +       /*\n> > +        * Clip frame length by the frame duration range and the maximum\n> allowable\n> > +        * value in the sensor, given by maxFrameLength_.\n> > +        */\n> > +       frameLengthMin = std::clamp<uint32_t>(1e3 * minFrameDuration /\n> mode_.line_length,\n> > +                                             mode_.height,\n> maxFrameLength_);\n> > +       frameLengthMax = std::clamp<uint32_t>(1e3 * maxFrameDuration /\n> mode_.line_length,\n> > +                                             mode_.height,\n> maxFrameLength_);\n> > +       /*\n> > +        * Limit the exposure to the maximum frame duration requested,\n> and\n> > +        * re-calculate if it has been clipped.\n> > +        */\n> > +       exposureLines = std::min(frameLengthMax - frameIntegrationDiff_,\n> exposureLines);\n> > +       exposure = Exposure(exposureLines);\n> > +\n> > +       /* Limit the vblank to the range allowed by the frame length\n> limits. */\n> > +       vblank = std::clamp(exposureLines + frameIntegrationDiff_,\n> > +                           frameLengthMin, frameLengthMax) -\n> mode_.height;\n> > +       return vblank;\n> > +}\n> > +\n> >  void CamHelper::SetCameraMode(const CameraMode &mode)\n> >  {\n> >         mode_ = mode;\n> > diff --git a/src/ipa/raspberrypi/cam_helper.hpp\n> b/src/ipa/raspberrypi/cam_helper.hpp\n> > index 044c2866..b1739a57 100644\n> > --- a/src/ipa/raspberrypi/cam_helper.hpp\n> > +++ b/src/ipa/raspberrypi/cam_helper.hpp\n> > @@ -62,12 +62,15 @@ class CamHelper\n> >  {\n> >  public:\n> >         static CamHelper *Create(std::string const &cam_name);\n> > -       CamHelper(MdParser *parser);\n> > +       CamHelper(MdParser *parser, unsigned int maxFrameLength,\n> > +                 unsigned int frameIntegrationDiff);\n> >         virtual ~CamHelper();\n> >         void SetCameraMode(const CameraMode &mode);\n> >         MdParser &Parser() const { return *parser_; }\n> >         uint32_t ExposureLines(double exposure_us) const;\n> >         double Exposure(uint32_t exposure_lines) const; // in us\n> > +       virtual uint32_t GetVBlanking(double &exposure_us, double\n> minFrameDuration,\n> > +                                     double maxFrameDuration) const;\n> >         virtual uint32_t GainCode(double gain) const = 0;\n> >         virtual double Gain(uint32_t gain_code) const = 0;\n> >         virtual void GetDelays(int &exposure_delay, int &gain_delay)\n> const;\n> > @@ -76,10 +79,20 @@ public:\n> >         virtual unsigned int HideFramesModeSwitch() const;\n> >         virtual unsigned int MistrustFramesStartup() const;\n> >         virtual unsigned int MistrustFramesModeSwitch() const;\n> > +\n> >  protected:\n> >         MdParser *parser_;\n> >         CameraMode mode_;\n> > +\n> > +private:\n> >         bool initialized_;\n> > +       /* Largest possible frame length, in units of lines. */\n> > +       unsigned int maxFrameLength_;\n> > +       /*\n> > +        * Smallest difference between the frame length and integration\n> time,\n> > +        * in units of lines.\n> > +        */\n> > +       unsigned int frameIntegrationDiff_;\n> >  };\n> >\n> >  // This is for registering camera helpers with the system, so that the\n> > diff --git a/src/ipa/raspberrypi/cam_helper_imx219.cpp\n> b/src/ipa/raspberrypi/cam_helper_imx219.cpp\n> > index db8ab879..8688ec09 100644\n> > --- a/src/ipa/raspberrypi/cam_helper_imx219.cpp\n> > +++ b/src/ipa/raspberrypi/cam_helper_imx219.cpp\n> > @@ -49,13 +49,22 @@ public:\n> >         double Gain(uint32_t gain_code) const override;\n> >         unsigned int MistrustFramesModeSwitch() const override;\n> >         bool SensorEmbeddedDataPresent() const override;\n> > +\n> > +private:\n> > +       /*\n> > +        * Smallest difference between the frame length and integration\n> time,\n> > +        * in units of lines.\n> > +        */\n> > +       static constexpr int frameIntegrationDiff = 4;\n> > +       /* Largest possible frame length, in units of lines. */\n> > +       static constexpr int maxFrameLength = 0xffff;\n> >  };\n> >\n> >  CamHelperImx219::CamHelperImx219()\n> >  #if ENABLE_EMBEDDED_DATA\n> > -       : CamHelper(new MdParserImx219())\n> > +       : CamHelper(new MdParserImx219(), maxFrameLength,\n> frameIntegrationDiff)\n> >  #else\n> > -       : CamHelper(new MdParserRPi())\n> > +       : CamHelper(new MdParserRPi(), maxFrameLength,\n> frameIntegrationDiff)\n> >  #endif\n> >  {\n> >  }\n> > diff --git a/src/ipa/raspberrypi/cam_helper_imx477.cpp\n> b/src/ipa/raspberrypi/cam_helper_imx477.cpp\n> > index 0e896ac7..53961310 100644\n> > --- a/src/ipa/raspberrypi/cam_helper_imx477.cpp\n> > +++ b/src/ipa/raspberrypi/cam_helper_imx477.cpp\n> > @@ -38,10 +38,19 @@ public:\n> >         uint32_t GainCode(double gain) const override;\n> >         double Gain(uint32_t gain_code) const override;\n> >         bool SensorEmbeddedDataPresent() const override;\n> > +\n> > +private:\n> > +       /*\n> > +        * Smallest difference between the frame length and integration\n> time,\n> > +        * in units of lines.\n> > +        */\n> > +       static constexpr int frameIntegrationDiff = 22;\n> > +       /* Largest possible frame length, in units of lines. */\n> > +       static constexpr int maxFrameLength = 0xffdc;\n> >  };\n> >\n> >  CamHelperImx477::CamHelperImx477()\n> > -       : CamHelper(new MdParserImx477())\n> > +       : CamHelper(new MdParserImx477(), maxFrameLength,\n> frameIntegrationDiff)\n> >  {\n> >  }\n> >\n> > diff --git a/src/ipa/raspberrypi/cam_helper_ov5647.cpp\n> b/src/ipa/raspberrypi/cam_helper_ov5647.cpp\n> > index dc5d8275..7630c127 100644\n> > --- a/src/ipa/raspberrypi/cam_helper_ov5647.cpp\n> > +++ b/src/ipa/raspberrypi/cam_helper_ov5647.cpp\n> > @@ -22,6 +22,15 @@ public:\n> >         unsigned int HideFramesModeSwitch() const override;\n> >         unsigned int MistrustFramesStartup() const override;\n> >         unsigned int MistrustFramesModeSwitch() const override;\n> > +\n> > +private:\n> > +       /*\n> > +        * Smallest difference between the frame length and integration\n> time,\n> > +        * in units of lines.\n> > +        */\n> > +       static constexpr int frameIntegrationDiff = 4;\n> > +       /* Largest possible frame length, in units of lines. */\n> > +       static constexpr int maxFrameLength = 0xffff;\n> >  };\n> >\n> >  /*\n> > @@ -30,7 +39,7 @@ public:\n> >   */\n> >\n> >  CamHelperOv5647::CamHelperOv5647()\n> > -       : CamHelper(new MdParserRPi())\n> > +       : CamHelper(new MdParserRPi(), maxFrameLength,\n> frameIntegrationDiff)\n> >  {\n> >  }\n> >\n> > diff --git a/src/ipa/raspberrypi/raspberrypi.cpp\n> b/src/ipa/raspberrypi/raspberrypi.cpp\n> > index 60cfdc27..f6d2b543 100644\n> > --- a/src/ipa/raspberrypi/raspberrypi.cpp\n> > +++ b/src/ipa/raspberrypi/raspberrypi.cpp\n> > @@ -58,6 +58,8 @@ namespace libcamera {\n> >  /* Configure the sensor with these values initially. */\n> >  constexpr double DefaultAnalogueGain = 1.0;\n> >  constexpr unsigned int DefaultExposureTime = 20000;\n> > +constexpr double defaultMinFrameDuration = 1e6 / 30.0;\n> > +constexpr double defaultMaxFrameDuration = 1e6 / 0.01;\n> >\n> >  LOG_DEFINE_CATEGORY(IPARPI)\n> >\n> > @@ -145,6 +147,9 @@ private:\n> >         /* LS table allocation passed in from the pipeline handler. */\n> >         FileDescriptor lsTableHandle_;\n> >         void *lsTable_;\n> > +\n> > +       /* Frame duration (1/fps) given in microseconds. */\n> > +       double minFrameDuration_, maxFrameDuration_;\n> >  };\n> >\n> >  int IPARPi::init(const IPASettings &settings)\n> > @@ -266,7 +271,8 @@ void IPARPi::configure(const CameraSensorInfo\n> &sensorInfo,\n> >                 sensorMetadata = helper_->SensorEmbeddedDataPresent();\n> >\n> >                 result->data.push_back(gainDelay);\n> > -               result->data.push_back(exposureDelay);\n> > +               result->data.push_back(exposureDelay); /* FOR EXPOSURE\n> ctrl */\n> > +               result->data.push_back(exposureDelay); /* For VBLANK\n> ctrl */\n> >                 result->data.push_back(sensorMetadata);\n> >\n> >                 result->operation |= RPi::IPA_CONFIG_STAGGERED_WRITE;\n> > @@ -335,6 +341,8 @@ void IPARPi::configure(const CameraSensorInfo\n> &sensorInfo,\n> >                 AgcStatus agcStatus;\n> >                 agcStatus.shutter_time = DefaultExposureTime;\n> >                 agcStatus.analogue_gain = DefaultAnalogueGain;\n> > +               minFrameDuration_ = defaultMinFrameDuration;\n> > +               maxFrameDuration_ = defaultMaxFrameDuration;\n> >                 applyAGC(&agcStatus, ctrls);\n> >\n> >                 result->controls.emplace_back(ctrls);\n> > @@ -712,6 +720,18 @@ void IPARPi::queueRequest(const ControlList\n> &controls)\n> >                         break;\n> >                 }\n> >\n> > +               case controls::FRAME_DURATIONS: {\n> > +                       auto frameDurations = ctrl.second.get<Span<const\n> int64_t>>();\n> > +\n> > +                       /* This will be applied once AGC recalculations\n> occur. */\n> > +                       minFrameDuration_ = frameDurations[0] ?\n> frameDurations[0] : defaultMinFrameDuration;\n> > +                       maxFrameDuration_ = frameDurations[1] ?\n> frameDurations[1] : defaultMaxFrameDuration;\n> > +                       libcameraMetadata_.set(controls::FrameDurations,\n> > +                                              {\n> static_cast<int64_t>(minFrameDuration_),\n> > +\n> static_cast<int64_t>(maxFrameDuration_) });\n> > +                       break;\n> > +               }\n> > +\n> >                 default:\n> >                         LOG(IPARPI, Warning)\n> >                                 << \"Ctrl \" << controls::controls.at\n> (ctrl.first)->name()\n> > @@ -882,7 +902,12 @@ void IPARPi::applyAWB(const struct AwbStatus\n> *awbStatus, ControlList &ctrls)\n> >  void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList\n> &ctrls)\n> >  {\n> >         int32_t gainCode = helper_->GainCode(agcStatus->analogue_gain);\n> > -       int32_t exposureLines =\n> helper_->ExposureLines(agcStatus->shutter_time);\n> > +\n> > +       /* GetVBlanking might clip exposure time to the fps limits. */\n> > +       double exposure = agcStatus->shutter_time;\n> > +       int32_t vblanking = helper_->GetVBlanking(exposure,\n> minFrameDuration_,\n> > +                                                 maxFrameDuration_);\n> > +       int32_t exposureLines = helper_->ExposureLines(exposure);\n> >\n> >         if (unicamCtrls_.find(V4L2_CID_ANALOGUE_GAIN) ==\n> unicamCtrls_.end()) {\n> >                 LOG(IPARPI, Error) << \"Can't find analogue gain control\";\n> > @@ -894,13 +919,20 @@ void IPARPi::applyAGC(const struct AgcStatus\n> *agcStatus, ControlList &ctrls)\n> >                 return;\n> >         }\n> >\n> > -       LOG(IPARPI, Debug) << \"Applying AGC Exposure: \" <<\n> agcStatus->shutter_time\n> > -                          << \" (Shutter lines: \" << exposureLines << \")\n> Gain: \"\n> > +       LOG(IPARPI, Debug) << \"Applying AGC Exposure: \" << exposure\n> > +                          << \" (Shutter lines: \" << exposureLines << \",\n> AGC requested \"\n> > +                          << agcStatus->shutter_time << \") Gain: \"\n> >                            << agcStatus->analogue_gain << \" (Gain Code: \"\n> >                            << gainCode << \")\";\n> >\n> > -       ctrls.set(V4L2_CID_ANALOGUE_GAIN, gainCode);\n> > +       /*\n> > +        * Due to the behavior of V4L2, the current value of VBLANK\n> could clip the\n> > +        * exposure time without us knowing. The next time though this\n> function should\n> > +        * clip exposure correctly.\n> > +        */\n> > +       ctrls.set(V4L2_CID_VBLANK, vblanking);\n> >         ctrls.set(V4L2_CID_EXPOSURE, exposureLines);\n> > +       ctrls.set(V4L2_CID_ANALOGUE_GAIN, gainCode);\n> >  }\n> >\n> >  void IPARPi::applyDG(const struct AgcStatus *dgStatus, ControlList\n> &ctrls)\n> > diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> > index 2ec1f6e7..13349f31 100644\n> > --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> > +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> > @@ -1221,7 +1221,8 @@ int RPiCameraData::configureIPA(const\n> CameraConfiguration *config)\n> >                 if (!staggeredCtrl_) {\n> >                         staggeredCtrl_.init(unicam_[Unicam::Image].dev(),\n> >                                             { { V4L2_CID_ANALOGUE_GAIN,\n> result.data[resultIdx++] },\n> > -                                             { V4L2_CID_EXPOSURE,\n> result.data[resultIdx++] } });\n> > +                                             { V4L2_CID_EXPOSURE,\n> result.data[resultIdx++] },\n> > +                                             { V4L2_CID_VBLANK,\n> result.data[resultIdx++] } });\n> >                         sensorMetadata_ = result.data[resultIdx++];\n> >                 }\n> >         }\n> > --\n> > 2.25.1\n> >\n> > _______________________________________________\n> > libcamera-devel mailing list\n> > libcamera-devel@lists.libcamera.org\n> > https://lists.libcamera.org/listinfo/libcamera-devel\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 03FE1BD80A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 11 Dec 2020 14:03:46 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 85F6567FA0;\n\tFri, 11 Dec 2020 15:03:45 +0100 (CET)","from mail-lj1-x244.google.com (mail-lj1-x244.google.com\n\t[IPv6:2a00:1450:4864:20::244])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 05FAB67F06\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 11 Dec 2020 15:03:43 +0100 (CET)","by mail-lj1-x244.google.com with SMTP id m13so10972525ljo.11\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 11 Dec 2020 06:03:42 -0800 (PST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=raspberrypi.com header.i=@raspberrypi.com\n\theader.b=\"hJ/9/mfq\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google;\n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=fLL6EkBiQWhFH+qLU922cnlkbCIgKAeDE4IrqYeaW9k=;\n\tb=hJ/9/mfq/2evvKXfSueTbhi+/VT/6HfahrsLtlassviGKJY8QVKeECejWgXgza6YV/\n\tbxMQbv7HS+2VnQe8u8ntKAf5NxURkXuVVQBiwR8twhS3P9rqfCl1nSntq0pqhefT5Ihk\n\tuksluh2J6sdWfuY39gPOaIc8bOVu7azVKbnenqOEcc2FQD+MorNgi6QN3FW6uf0Oz7ui\n\tXT2+wm514/aRsy+uEhzEC/+6kRlmXbaJgo7gsbFX8loyEFf8po/APKD4G1MCrKXTfhlE\n\tnzN0BxBwpNm4+YHLu6W/4UmAL1JJ1053P4aG6SXXyUZChgd0U7dzn/Ko12FPjew3SG1P\n\tdFHw==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=fLL6EkBiQWhFH+qLU922cnlkbCIgKAeDE4IrqYeaW9k=;\n\tb=t1FDwPo0f6a6d117RjCfqpK0wF+o8Pw/i8YfEw2tJNKtTyWS3+YTU/apY9ja562W6E\n\tP9epm9srkx2q72fJwn5g4HVcHmT62zNu4PrilboT20G78RSE4Vbhb3H06yuHdcG/XPuZ\n\tZ9R09OQPAgzEs8z/UXIw62MhXJ4XuOqwdfzHjMVBZjvUkFHy/vwPd/wJh66lra2kwrCC\n\tCQ7n4tcg1WgeNzl3DUU0BO/n/6j52lyJC1YxxfmjbcMfV8GaSKN8LtOr+0WQRMwiR1Nh\n\tcGB8OaqKnKTGE5Cb/nAr6lf4QmhtxqUiYT24Pl7cN6kGKDwKJqsoQsKK9h4JIRVxubaz\n\tz/LA==","X-Gm-Message-State":"AOAM530vbPhjPA0rte5gmRGDxhN2bFngqliyq+8aVyAbCtoPn9DLPPKI\n\tN+rSwXWkknhXebdA2UJsSKnnjMrvd4v/VfgP6V0hcQ==","X-Google-Smtp-Source":"ABdhPJzut1xaQUBTsFzBXDi0t+Gfda6RfnaYtXmuLFlacr9Ii6d4oQORY1daXb5+ppTIKOHbbrM/1ABdf2oHzessayw=","X-Received":"by 2002:a2e:93d6:: with SMTP id\n\tp22mr5440992ljh.169.1607695421238; \n\tFri, 11 Dec 2020 06:03:41 -0800 (PST)","MIME-Version":"1.0","References":"<20201211133510.391037-1-naush@raspberrypi.com>\n\t<20201211133510.391037-2-naush@raspberrypi.com>\n\t<CAHW6GYK2i7JzF_pUd1UGMDZTwg_=Sg6U-NYSX2QNegTWCrPnZw@mail.gmail.com>","In-Reply-To":"<CAHW6GYK2i7JzF_pUd1UGMDZTwg_=Sg6U-NYSX2QNegTWCrPnZw@mail.gmail.com>","From":"Naushir Patuck <naush@raspberrypi.com>","Date":"Fri, 11 Dec 2020 14:03:25 +0000","Message-ID":"<CAEmqJPoEKBJQh4RRODXkp9ojo6u1kqjzB1JeQE6EQR8JYdzzMA@mail.gmail.com>","To":"David Plowman <david.plowman@raspberrypi.com>","Subject":"Re: [libcamera-devel] [PATCH v7 2/3] libcamera: raspberrypi: Add\n\tcontrol of sensor vblanking","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>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Content-Type":"multipart/mixed;\n\tboundary=\"===============4567786263909170329==\"","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]