[libcamera-devel] [PATCH 1/2] libcamera: ipu3: Initialize controls in the IPA

Laurent Pinchart laurent.pinchart at ideasonboard.com
Sun Jul 25 03:35:03 CEST 2021


Hi Jacopo,

Thank you for the patch.

On Thu, Jul 22, 2021 at 05:01:09PM +0200, Jacopo Mondi wrote:
> On Thu, Jul 22, 2021 at 11:58:02AM +0530, Umang Jain wrote:
> > On 7/16/21 8:02 PM, Jacopo Mondi wrote:
> > > All the IPU3 Camera controls are currently initialized by the pipeline
> > > handler which initializes them using the camera sensor configuration and
> > > platform specific requirements.
> > >
> > > However, some controls are better initialized by the IPA, which might,
> > > in example, cap the exposure times and frame duration to the constraints
> > > of its algorithms implementation.
> >
> > Makes sense to me.
> >
> > > Also, moving forward, the IPA should register controls to report its
> > > capabilities, in example the ability to enable/disable 3A algorithms on
> > > request.
> > >
> > > Move the existing controls initialization to the IPA, by providing
> > > the sensor configuration and its controls to the IPU3IPA::init()
> > > function, which initializes controls and returns them to the pipeline
> > > through an output parameter.
> > >
> > > The existing controls initialization has been copied verbatim from the
> > > pipeline handler to the IPA, if not a for few line breaks adjustments
> >
> > s/if not a for few/if not, for a few   maybe?
> 
> So:
> pipeline handler to the IPA, if not, for a few line ?
> 
> Not a native speaker but the double comma doesn't really feel right
> here.

If the intent was to say that the only difference is a few line breaks,
no extra comma is needed after "not". A comma (or even a full stop)
would however be useful after "adjustments".

> > > and the resulting Camera controls values are not changed .
> > >
> > > Signed-off-by: Jacopo Mondi <jacopo at jmondi.org>
> > > ---
> > >   include/libcamera/ipa/ipu3.mojom     |  8 ++-
> > >   src/ipa/ipu3/ipu3.cpp                | 71 ++++++++++++++++++--
> > >   src/libcamera/pipeline/ipu3/ipu3.cpp | 96 ++++++++++++----------------
> > >   3 files changed, 116 insertions(+), 59 deletions(-)
> > >
> > > diff --git a/include/libcamera/ipa/ipu3.mojom b/include/libcamera/ipa/ipu3.mojom
> > > index 911a3a072464..eafefa8b7fe3 100644
> > > --- a/include/libcamera/ipa/ipu3.mojom
> > > +++ b/include/libcamera/ipa/ipu3.mojom
> > > @@ -30,6 +30,11 @@ struct IPU3Action {
> > >   	libcamera.ControlList controls;
> > >   };
> > > +struct IPAInitInfo {
> > > +	libcamera.IPACameraSensorInfo sensorInfo;
> > > +	libcamera.ControlInfoMap sensorControls;
> > > +};
> > > +
> > >   struct IPAConfigInfo {
> > >   	libcamera.IPACameraSensorInfo sensorInfo;
> > >   	map<uint32, libcamera.ControlInfoMap> entityControls;
> > > @@ -38,7 +43,8 @@ struct IPAConfigInfo {
> > >   };
> > >   interface IPAIPU3Interface {
> > > -	init(libcamera.IPASettings settings) => (int32 ret);
> > > +	init(IPAInitInfo initInfo)
> > > +		=> (int32 ret, libcamera.ControlInfoMap ipaControls);
> >
> > Ah, this is interesting:
> >
> > At first I had a knee jerk reaction that this is wrong and the correct way
> > is:
> >
> > 	+	init(IPAInitInfo initInfo, libcamera.ControlInfoMap ipaControls)
> > 	+						=> (int32 ret);
> >
> > But then I see the code generation and saw it was on the correct lines
> >
> >         virtual int32_t init(
> >                 const IPAInitInfo &initInfo,
> >                 ControlInfoMap *ipaControls) = 0;
> >
> > Then, I also read ipaControls is a output parameters, so this is the way to
> > let mojom know about out parameter use-cases I assume.
> 
> Yes, took me a while to figure it out
> 
> >
> > Have you tested CTS with this change with IPA running in isolated mode? It
> 
> Good catch!!
> 
> It's enough to run
> 
> # LIBCAMERA_IPA_FORCE_ISOLATION=1 cam -c1 --list-controls
> [0:10:59.880617099] [11495] ERROR IPU3 ipu3.cpp:1201 Exposure control not initializaed by the IPA
> Camera 1 not found
> 
> So we cannot use output parameters when running isolated IPA ?
> That's quite a showstopper :(
> 
> 
> > should be easier with LIBCAMERA_IPA_FORCE_ISOLATION env variable now. I am a
> > bit concerned about hitting any serialize/de-serialize issue popping up with
> > this change, in respect to out parameter i.e. ipaControls. Maybe Paul can
> > educate me on this aspect.
> 
> And me as well :)
> 
> >
> > I finally gave a thought of handling the Intel IPA with this change. I don't
> > see any major issues, more or less, we'll need to replicate this change
> > there as well (Pass controls to IPA and init them). I just want to make sure
> > of the timing of merge (ofcourse after attaining R-b tags on this series).
> > Would it be acceptable as a  general rule that, merge of this series (and
> > such series likewise in future) should be done after /someone/ prepares
> > changes for Intel IPA and post them on the list as well? This will help
> > minimize the lag-time of Intel IPA catching up to the libcamera master.
> >
> > Rest looks good to me so,
> >
> > Reviewed-by: Umang Jain <umang.jain at ideasonboard.com>
> 
> Thanks, let's see about the output parameter thing.
> >
> >
> > >   	start() => (int32 ret);
> > >   	stop();
> > > diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp
> > > index 71698d36e50f..d3c69bc07bd0 100644
> > > --- a/src/ipa/ipu3/ipu3.cpp
> > > +++ b/src/ipa/ipu3/ipu3.cpp
> > > @@ -5,8 +5,10 @@
> > >    * ipu3.cpp - IPU3 Image Processing Algorithms
> > >    */
> > > +#include <array>
> > >   #include <stdint.h>
> > >   #include <sys/mman.h>
> > > +#include <utility>
> > >   #include <linux/intel-ipu3.h>
> > >   #include <linux/v4l2-controls.h>
> > > @@ -38,7 +40,8 @@ namespace ipa::ipu3 {
> > >   class IPAIPU3 : public IPAIPU3Interface
> > >   {
> > >   public:
> > > -	int init(const IPASettings &settings) override;
> > > +	int init(const IPAInitInfo &initInfo, ControlInfoMap *ipaControls) override;
> > > +
> > >   	int start() override;
> > >   	void stop() override {}
> > > @@ -86,14 +89,74 @@ private:
> > >   	struct ipu3_uapi_grid_config bdsGrid_;
> > >   };
> > > -int IPAIPU3::init(const IPASettings &settings)
> > > +/**
> > > + * Initialize the IPA module and its controls.
> > > + *
> > > + * This function receives the camera sensor information from the pipeline
> > > + * handler, computes the limits of the controls it handles and returns
> > > + * them in the \a ipaControls output parameter.
> > > + */
> > > +int IPAIPU3::init(const IPAInitInfo &initInfo, ControlInfoMap *ipaControls)
> > >   {
> > > -	camHelper_ = CameraSensorHelperFactory::create(settings.sensorModel);
> > > +	const IPACameraSensorInfo &sensorInfo = initInfo.sensorInfo;
> > > +
> > > +	/* Initialize the camera sensor helper. */
> > > +	camHelper_ = CameraSensorHelperFactory::create(sensorInfo.model);
> > >   	if (camHelper_ == nullptr) {
> > > -		LOG(IPAIPU3, Error) << "Failed to create camera sensor helper for " << settings.sensorModel;
> > > +		LOG(IPAIPU3, Error) << "Failed to create camera sensor helper for "
> > > +				    << sensorInfo.model;
> > >   		return -ENODEV;
> > >   	}
> > > +	/* Initialize Controls. */
> > > +	const ControlInfoMap &sensorControls = initInfo.sensorControls;
> > > +	ControlInfoMap::Map controls{};
> > > +
> > > +	/*
> > > +	 * Compute exposure time limits.
> > > +	 *
> > > +	 * Initialize the control using the line length and pixel rate of the
> > > +	 * current configuration converted to microseconds. Use the
> > > +	 * V4L2_CID_EXPOSURE control to get exposure min, max and default and
> > > +	 * convert it from lines to microseconds.
> > > +	 */
> > > +	double lineDuration = sensorInfo.lineLength / (sensorInfo.pixelRate / 1e6);
> > > +	const ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second;
> > > +	int32_t minExposure = v4l2Exposure.min().get<int32_t>() * lineDuration;
> > > +	int32_t maxExposure = v4l2Exposure.max().get<int32_t>() * lineDuration;
> > > +	int32_t defExposure = v4l2Exposure.def().get<int32_t>() * lineDuration;
> > > +	controls[&controls::ExposureTime] = ControlInfo(minExposure, maxExposure,
> > > +							defExposure);
> > > +
> > > +	/*
> > > +	 * Compute the frame duration limits.
> > > +	 *
> > > +	 * The frame length is computed assuming a fixed line length combined
> > > +	 * with the vertical frame sizes.
> > > +	 */
> > > +	const ControlInfo &v4l2HBlank = sensorControls.find(V4L2_CID_HBLANK)->second;
> > > +	uint32_t hblank = v4l2HBlank.def().get<int32_t>();
> > > +	uint32_t lineLength = sensorInfo.outputSize.width + hblank;
> > > +
> > > +	const ControlInfo &v4l2VBlank = sensorControls.find(V4L2_CID_VBLANK)->second;
> > > +	std::array<uint32_t, 3> frameHeights{
> > > +		v4l2VBlank.min().get<int32_t>() + sensorInfo.outputSize.height,
> > > +		v4l2VBlank.max().get<int32_t>() + sensorInfo.outputSize.height,
> > > +		v4l2VBlank.def().get<int32_t>() + sensorInfo.outputSize.height,
> > > +	};
> > > +
> > > +	std::array<int64_t, 3> frameDurations;
> > > +	for (unsigned int i = 0; i < frameHeights.size(); ++i) {
> > > +		uint64_t frameSize = lineLength * frameHeights[i];
> > > +		frameDurations[i] = frameSize / (sensorInfo.pixelRate / 1000000U);
> > > +	}
> > > +
> > > +	controls[&controls::FrameDurationLimits] = ControlInfo(frameDurations[0],
> > > +							       frameDurations[1],
> > > +							       frameDurations[2]);
> > > +
> > > +	*ipaControls = std::move(controls);
> > > +
> > >   	return 0;
> > >   }
> > > diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > index 76c3bb3d8aa9..22df9c3650af 100644
> > > --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > @@ -88,6 +88,8 @@ public:
> > >   	std::queue<Request *> pendingRequests_;
> > > +	ControlInfoMap ipaControls_;
> > > +
> > >   private:
> > >   	void queueFrameAction(unsigned int id,
> > >   			      const ipa::ipu3::IPU3Action &action);
> > > @@ -940,7 +942,6 @@ int PipelineHandlerIPU3::initControls(IPU3CameraData *data)
> > >   		return ret;
> > >   	ControlInfoMap::Map controls = IPU3Controls;
> > > -	const ControlInfoMap &sensorControls = sensor->controls();
> > >   	const std::vector<int32_t> &testPatternModes = sensor->testPatternModes();
> > >   	if (!testPatternModes.empty()) {
> > >   		std::vector<ControlValue> values;
> > > @@ -952,58 +953,6 @@ int PipelineHandlerIPU3::initControls(IPU3CameraData *data)
> > >   		controls[&controls::draft::TestPatternMode] = ControlInfo(values);
> > >   	}
> > > -	/*
> > > -	 * Compute exposure time limits.
> > > -	 *
> > > -	 * Initialize the control using the line length and pixel rate of the
> > > -	 * current configuration converted to microseconds. Use the
> > > -	 * V4L2_CID_EXPOSURE control to get exposure min, max and default and
> > > -	 * convert it from lines to microseconds.
> > > -	 */
> > > -	double lineDuration = sensorInfo.lineLength
> > > -			    / (sensorInfo.pixelRate / 1e6);
> > > -	const ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second;
> > > -	int32_t minExposure = v4l2Exposure.min().get<int32_t>() * lineDuration;
> > > -	int32_t maxExposure = v4l2Exposure.max().get<int32_t>() * lineDuration;
> > > -	int32_t defExposure = v4l2Exposure.def().get<int32_t>() * lineDuration;
> > > -
> > > -	/*
> > > -	 * \todo Report the actual exposure time, use the default for the
> > > -	 * moment.
> > > -	 */
> > > -	data->exposureTime_ = defExposure;
> > > -
> > > -	controls[&controls::ExposureTime] = ControlInfo(minExposure, maxExposure,
> > > -							defExposure);
> > > -
> > > -	/*
> > > -	 * Compute the frame duration limits.
> > > -	 *
> > > -	 * The frame length is computed assuming a fixed line length combined
> > > -	 * with the vertical frame sizes.
> > > -	 */
> > > -	const ControlInfo &v4l2HBlank = sensorControls.find(V4L2_CID_HBLANK)->second;
> > > -	uint32_t hblank = v4l2HBlank.def().get<int32_t>();
> > > -	uint32_t lineLength = sensorInfo.outputSize.width + hblank;
> > > -
> > > -	const ControlInfo &v4l2VBlank = sensorControls.find(V4L2_CID_VBLANK)->second;
> > > -	std::array<uint32_t, 3> frameHeights{
> > > -		v4l2VBlank.min().get<int32_t>() + sensorInfo.outputSize.height,
> > > -		v4l2VBlank.max().get<int32_t>() + sensorInfo.outputSize.height,
> > > -		v4l2VBlank.def().get<int32_t>() + sensorInfo.outputSize.height,
> > > -	};
> > > -
> > > -	std::array<int64_t, 3> frameDurations;
> > > -	for (unsigned int i = 0; i < frameHeights.size(); ++i) {
> > > -		uint64_t frameSize = lineLength * frameHeights[i];
> > > -		frameDurations[i] = frameSize / (sensorInfo.pixelRate / 1000000U);
> > > -	}
> > > -
> > > -	controls[&controls::FrameDurationLimits] =
> > > -		ControlInfo(frameDurations[0],
> > > -			    frameDurations[1],
> > > -			    frameDurations[2]);
> > > -
> > >   	/*
> > >   	 * Compute the scaler crop limits.
> > >   	 *
> > > @@ -1057,6 +1006,10 @@ int PipelineHandlerIPU3::initControls(IPU3CameraData *data)
> > >   	controls[&controls::ScalerCrop] = ControlInfo(minCrop, maxCrop, maxCrop);
> > > +	/* Add the IPA registered controls to list of camera controls. */
> > > +	for (const auto &ipaControl : data->ipaControls_)
> > > +		controls[ipaControl.first] = ipaControl.second;
> > > +
> > >   	data->controlInfo_ = std::move(controls);
> > >   	return 0;
> > > @@ -1208,13 +1161,48 @@ int IPU3CameraData::loadIPA()
> > >   	ipa_->queueFrameAction.connect(this, &IPU3CameraData::queueFrameAction);
> > > +	/*
> > > +	 * Pass the sensor info the the IPA to initialize controls.
> > > +	 *
> > > +	 * \todo The limits of the registered controls depend on the current
> > > +	 * sensor configuration. Initialize the sensor using its resolution as
> > > +	 * its initial configuration and use it to compute the controls limits
> > > +	 * in the IPA.
> > > +	 */
> > >   	CameraSensor *sensor = cio2_.sensor();
> > > -	int ret = ipa_->init(IPASettings{ "", sensor->model() });
> > > +	V4L2SubdeviceFormat sensorFormat = {};
> > > +	sensorFormat.size = sensor->resolution();
> > > +	int ret = sensor->setFormat(&sensorFormat);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	IPACameraSensorInfo sensorInfo{};
> > > +	ret = sensor->sensorInfo(&sensorInfo);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	ipa::ipu3::IPAInitInfo initInfo{
> > > +		sensorInfo,
> > > +		sensor->controls(),
> > > +	};
> > > +	ipaControls_ = {};
> > > +	ret = ipa_->init(initInfo, &ipaControls_);
> > >   	if (ret) {
> > >   		LOG(IPU3, Error) << "Failed to initialise the IPU3 IPA";
> > >   		return ret;
> > >   	}
> > > +	/*
> > > +	 * \todo Report the actual exposure time, use the default for the
> > > +	 * moment.
> > > +	 */
> > > +	const auto exposureInfo = ipaControls_.find(&controls::ExposureTime);
> > > +	if (exposureInfo == ipaControls_.end()) {
> > > +		LOG(IPU3, Error) << "Exposure control not initializaed by the IPA";
> > > +		return -EINVAL;
> > > +	}
> > > +	exposureTime_ = exposureInfo->second.def().get<int32_t>();
> > > +
> > >   	return 0;
> > >   }

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list