[libcamera-devel,v2,07/11] libcamera: ipu3: Attach to an IPA and allow it to set sensor controls
diff mbox series

Message ID 20201229160318.77536-8-niklas.soderlund@ragnatech.se
State Superseded
Delegated to: Niklas Söderlund
Headers show
Series
  • libcamera: ipu3: Attach to an skeleton IPA
Related show

Commit Message

Niklas Söderlund Dec. 29, 2020, 4:03 p.m. UTC
Attach to the IPA and allow it to push V4L2 controls that applies on the
camera sensor. The IPA is not fully integrated in the pipeline as
statistics and parameters buffers are not yet allocated, processed by
the IPA nor queued to the hardware.

Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
---
* Changes since v1
- Rewrite to not use CameraSensor.
- Fix mistake where configuration sen to IPA was overwritten.
- Check that IPA configuration was successful before starting.
- Update commit message.
---
 src/libcamera/pipeline/ipu3/ipu3.cpp | 103 +++++++++++++++++++++++++++
 1 file changed, 103 insertions(+)

Comments

Jean-Michel Hautbois Dec. 30, 2020, 5 p.m. UTC | #1
Hi Niklas,

Thanks for the patch.

On 29/12/2020 17:03, Niklas Söderlund wrote:
> Attach to the IPA and allow it to push V4L2 controls that applies on the
> camera sensor. The IPA is not fully integrated in the pipeline as
> statistics and parameters buffers are not yet allocated, processed by
> the IPA nor queued to the hardware.
> 
> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> ---
> * Changes since v1
> - Rewrite to not use CameraSensor.
> - Fix mistake where configuration sen to IPA was overwritten.
> - Check that IPA configuration was successful before starting.
> - Update commit message.
> ---
>  src/libcamera/pipeline/ipu3/ipu3.cpp | 103 +++++++++++++++++++++++++++
>  1 file changed, 103 insertions(+)
> 
> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> index a87ce8f3378ba2fe..95f1b75dc8be5d40 100644
> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> @@ -14,11 +14,14 @@
>  #include <libcamera/camera.h>
>  #include <libcamera/control_ids.h>
>  #include <libcamera/formats.h>
> +#include <libcamera/ipa/ipu3.h>
>  #include <libcamera/request.h>
>  #include <libcamera/stream.h>
>  
>  #include "libcamera/internal/camera_sensor.h"
> +#include "libcamera/internal/delayed_controls.h"
>  #include "libcamera/internal/device_enumerator.h"
> +#include "libcamera/internal/ipa_manager.h"
>  #include "libcamera/internal/log.h"
>  #include "libcamera/internal/media_device.h"
>  #include "libcamera/internal/pipeline_handler.h"
> @@ -53,6 +56,8 @@ public:
>  	{
>  	}
>  
> +	int loadIPA();
> +
>  	void imguOutputBufferReady(FrameBuffer *buffer);
>  	void cio2BufferReady(FrameBuffer *buffer);
>  
> @@ -62,6 +67,11 @@ public:
>  	Stream outStream_;
>  	Stream vfStream_;
>  	Stream rawStream_;
> +
> +	std::unique_ptr<DelayedControls> delayedCtrls_;
> +
> +private:
> +	void actOnIpa(unsigned int id, const IPAOperationData &op);
>  };
>  
>  class IPU3CameraConfiguration : public CameraConfiguration
> @@ -590,6 +600,13 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
>  	IPU3CameraData *data = cameraData(camera);
>  	CIO2Device *cio2 = &data->cio2_;
>  	ImgUDevice *imgu = data->imgu_;
> +
> +	CameraSensorInfo sensorInfo = {};
> +	std::map<unsigned int, IPAStream> streamConfig;
> +	std::map<unsigned int, const ControlInfoMap &> entityControls;
> +	IPAOperationData ipaConfig;
> +	IPAOperationData result = {};
> +
>  	int ret;
>  
>  	/* Allocate buffers for internal pipeline usage. */
> @@ -597,6 +614,11 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
>  	if (ret)
>  		return ret;
>  
> +	IPAOperationData ipaData = {};
> +	ret = data->ipa_->start(ipaData, nullptr);
> +	if (ret)
> +		goto error;
> +
>  	/*
>  	 * Start the ImgU video devices, buffers will be queued to the
>  	 * ImgU output and viewfinder when requests will be queued.
> @@ -612,9 +634,40 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
>  		goto error;
>  	}
>  
> +	/* Inform IPA of stream configuration and sensor controls. */
> +	ret = data->cio2_.sensor()->sensorInfo(&sensorInfo);
> +	if (ret) {
> +		/* \todo Turn to hard failure once sensors info is mandatory. */
> +		LOG(IPU3, Warning) << "Camera sensor information not available";
> +		sensorInfo = {};
> +		ret = 0;
> +	}
> +
> +	streamConfig[0] = {
> +		.pixelFormat = data->outStream_.configuration().pixelFormat,
> +		.size = data->outStream_.configuration().size,
> +	};
> +	streamConfig[1] = {
> +		.pixelFormat = data->vfStream_.configuration().pixelFormat,
> +		.size = data->vfStream_.configuration().size,
> +	};
> +
> +	entityControls.emplace(0, data->cio2_.sensor()->controls());
> +
> +	data->ipa_->configure(sensorInfo, streamConfig, entityControls,
> +			      ipaConfig, &result);
> +
> +	if ((result.operation != IPU3_IPA_STATUS_CONFIGURATION) ||
> +	    (result.data.size() != 1) || (result.data.at(0) != 1)) {
> +		LOG(IPU3, Warning) << "Failed to configure IPA";
> +		ret = -EINVAL;
> +		goto error;
> +	}
> +
>  	return 0;
>  
>  error:
> +	data->ipa_->stop();
>  	freeBuffers(camera);
>  	LOG(IPU3, Error) << "Failed to start camera " << camera->id();
>  
> @@ -631,6 +684,8 @@ void PipelineHandlerIPU3::stop(Camera *camera)
>  	if (ret)
>  		LOG(IPU3, Warning) << "Failed to stop camera " << camera->id();
>  
> +	data->ipa_->stop();
> +
>  	freeBuffers(camera);
>  }
>  
> @@ -762,12 +817,32 @@ int PipelineHandlerIPU3::registerCameras()
>  		if (ret)
>  			continue;
>  
> +		ret = data->loadIPA();
> +		if (ret)
> +			continue;
> +
>  		/* Initialize the camera properties. */
>  		data->properties_ = cio2->sensor()->properties();
>  
>  		/* Initialze the camera controls. */
>  		data->controlInfo_ = IPU3Controls;
>  
> +		/*
> +		 * \todo Read dealy values from the sensor itself or from a
> +		 * a sensor database. For now use generic values taken from
> +		 * the Raspberry Pi and listed as generic values.
> +		 */
> +		std::unordered_map<uint32_t, unsigned int> delays = {
> +			{ V4L2_CID_ANALOGUE_GAIN, 1 },
> +			{ V4L2_CID_EXPOSURE, 2 },
> +		};

I agree, the controls we want to have need to be parsed in some way.
I am wondering what makes a control beeing a "delayedControl" and what
is not ?

Looking at the RPi pipeline and IPA, I can see the
V4L2_CID_ANALOGUE_GAIN and V4L2_CID_EXPOSURE controls beeing delayed
controls, but in the RPi IPA there is for example a call to the
V4L2_CID_DIGITAL_GAIN control.

Would we need a database to know which controls are effectively to be
delayed ones, and when the sensor is configured, parse the controls
available and add the corresponding ones to the map ?
This is an open question :-).

> +		data->delayedCtrls_ =
> +			std::make_unique<DelayedControls>(cio2->sensor()->device(),
> +							  delays);
> +		data->cio2_.frameStart().connect(data->delayedCtrls_.get(),
> +						 &DelayedControls::applyControls);
> +
>  		/**
>  		 * \todo Dynamically assign ImgU and output devices to each
>  		 * stream and camera; as of now, limit support to two cameras
> @@ -811,6 +886,34 @@ int PipelineHandlerIPU3::registerCameras()
>  	return numCameras ? 0 : -ENODEV;
>  }
>  
> +int IPU3CameraData::loadIPA()
> +{
> +	ipa_ = IPAManager::createIPA(pipe_, 1, 1);
> +	if (!ipa_)
> +		return -ENOENT;
> +
> +	ipa_->queueFrameAction.connect(this, &IPU3CameraData::actOnIpa);
> +
> +	ipa_->init(IPASettings{});
> +
> +	return 0;
> +}
> +
> +void IPU3CameraData::actOnIpa([[maybe_unused]] unsigned int id,
> +			      const IPAOperationData &action)
> +{
> +	switch (action.operation) {
> +	case IPU3_IPA_ACTION_SET_SENSOR_CONTROLS: {
> +		const ControlList &controls = action.controls[0];
> +		delayedCtrls_->push(controls);
> +		break;
> +	}
> +	default:
> +		LOG(IPU3, Error) << "Unknown action " << action.operation;
> +		break;
> +	}
> +}
> +
>  /* -----------------------------------------------------------------------------
>   * Buffer Ready slots
>   */
>
Umang Jain Jan. 7, 2021, noon UTC | #2
Hi Niklas,

On 12/29/20 9:33 PM, Niklas Söderlund wrote:
> Attach to the IPA and allow it to push V4L2 controls that applies on the
> camera sensor. The IPA is not fully integrated in the pipeline as
> statistics and parameters buffers are not yet allocated, processed by
> the IPA nor queued to the hardware.
>
> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> ---
> * Changes since v1
> - Rewrite to not use CameraSensor.
> - Fix mistake where configuration sen to IPA was overwritten.
> - Check that IPA configuration was successful before starting.
> - Update commit message.
> ---
>   src/libcamera/pipeline/ipu3/ipu3.cpp | 103 +++++++++++++++++++++++++++
>   1 file changed, 103 insertions(+)
>
> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> index a87ce8f3378ba2fe..95f1b75dc8be5d40 100644
> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> @@ -14,11 +14,14 @@
>   #include <libcamera/camera.h>
>   #include <libcamera/control_ids.h>
>   #include <libcamera/formats.h>
> +#include <libcamera/ipa/ipu3.h>
>   #include <libcamera/request.h>
>   #include <libcamera/stream.h>
>   
>   #include "libcamera/internal/camera_sensor.h"
> +#include "libcamera/internal/delayed_controls.h"
>   #include "libcamera/internal/device_enumerator.h"
> +#include "libcamera/internal/ipa_manager.h"
>   #include "libcamera/internal/log.h"
>   #include "libcamera/internal/media_device.h"
>   #include "libcamera/internal/pipeline_handler.h"
> @@ -53,6 +56,8 @@ public:
>   	{
>   	}
>   
> +	int loadIPA();
> +
>   	void imguOutputBufferReady(FrameBuffer *buffer);
>   	void cio2BufferReady(FrameBuffer *buffer);
>   
> @@ -62,6 +67,11 @@ public:
>   	Stream outStream_;
>   	Stream vfStream_;
>   	Stream rawStream_;
> +
> +	std::unique_ptr<DelayedControls> delayedCtrls_;
> +
> +private:
> +	void actOnIpa(unsigned int id, const IPAOperationData &op);
>   };
>   
>   class IPU3CameraConfiguration : public CameraConfiguration
> @@ -590,6 +600,13 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
>   	IPU3CameraData *data = cameraData(camera);
>   	CIO2Device *cio2 = &data->cio2_;
>   	ImgUDevice *imgu = data->imgu_;
> +
> +	CameraSensorInfo sensorInfo = {};
> +	std::map<unsigned int, IPAStream> streamConfig;
> +	std::map<unsigned int, const ControlInfoMap &> entityControls;
> +	IPAOperationData ipaConfig;
> +	IPAOperationData result = {};
> +
>   	int ret;
>   
>   	/* Allocate buffers for internal pipeline usage. */
> @@ -597,6 +614,11 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
>   	if (ret)
>   		return ret;
>   
> +	IPAOperationData ipaData = {};
> +	ret = data->ipa_->start(ipaData, nullptr);
> +	if (ret)
> +		goto error;
> +
>   	/*
>   	 * Start the ImgU video devices, buffers will be queued to the
>   	 * ImgU output and viewfinder when requests will be queued.
> @@ -612,9 +634,40 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
>   		goto error;
>   	}
>   
> +	/* Inform IPA of stream configuration and sensor controls. */
> +	ret = data->cio2_.sensor()->sensorInfo(&sensorInfo);
> +	if (ret) {
> +		/* \todo Turn to hard failure once sensors info is mandatory. */
> +		LOG(IPU3, Warning) << "Camera sensor information not available";
> +		sensorInfo = {};
> +		ret = 0;
> +	}
> +
> +	streamConfig[0] = {
> +		.pixelFormat = data->outStream_.configuration().pixelFormat,
> +		.size = data->outStream_.configuration().size,
> +	};
> +	streamConfig[1] = {
> +		.pixelFormat = data->vfStream_.configuration().pixelFormat,
> +		.size = data->vfStream_.configuration().size,
> +	};
> +
> +	entityControls.emplace(0, data->cio2_.sensor()->controls());
> +
> +	data->ipa_->configure(sensorInfo, streamConfig, entityControls,
> +			      ipaConfig, &result);
> +
> +	if ((result.operation != IPU3_IPA_STATUS_CONFIGURATION) ||
> +	    (result.data.size() != 1) || (result.data.at(0) != 1)) {
> +		LOG(IPU3, Warning) << "Failed to configure IPA";
> +		ret = -EINVAL;
> +		goto error;
> +	}
> +
>   	return 0;
>   
>   error:
> +	data->ipa_->stop();
>   	freeBuffers(camera);
>   	LOG(IPU3, Error) << "Failed to start camera " << camera->id();
>   
> @@ -631,6 +684,8 @@ void PipelineHandlerIPU3::stop(Camera *camera)
>   	if (ret)
>   		LOG(IPU3, Warning) << "Failed to stop camera " << camera->id();
>   
> +	data->ipa_->stop();
> +
>   	freeBuffers(camera);
>   }
>   
> @@ -762,12 +817,32 @@ int PipelineHandlerIPU3::registerCameras()
>   		if (ret)
>   			continue;
>   
> +		ret = data->loadIPA();
> +		if (ret)
> +			continue;
> +
>   		/* Initialize the camera properties. */
>   		data->properties_ = cio2->sensor()->properties();
>   
>   		/* Initialze the camera controls. */
>   		data->controlInfo_ = IPU3Controls;
>   
> +		/*
> +		 * \todo Read dealy values from the sensor itself or from a
s/dealy/delay/
> +		 * a sensor database. For now use generic values taken from
> +		 * the Raspberry Pi and listed as generic values.
may be you meant : s/generic values/'generic values'  ?
> +		 */
> +		std::unordered_map<uint32_t, unsigned int> delays = {
> +			{ V4L2_CID_ANALOGUE_GAIN, 1 },
> +			{ V4L2_CID_EXPOSURE, 2 },
> +		};
> +
> +		data->delayedCtrls_ =
> +			std::make_unique<DelayedControls>(cio2->sensor()->device(),
> +							  delays);
> +		data->cio2_.frameStart().connect(data->delayedCtrls_.get(),
> +						 &DelayedControls::applyControls);
> +
>   		/**
>   		 * \todo Dynamically assign ImgU and output devices to each
>   		 * stream and camera; as of now, limit support to two cameras
> @@ -811,6 +886,34 @@ int PipelineHandlerIPU3::registerCameras()
>   	return numCameras ? 0 : -ENODEV;
>   }
>   
> +int IPU3CameraData::loadIPA()
> +{
> +	ipa_ = IPAManager::createIPA(pipe_, 1, 1);
> +	if (!ipa_)
> +		return -ENOENT;
> +
> +	ipa_->queueFrameAction.connect(this, &IPU3CameraData::actOnIpa);
> +
> +	ipa_->init(IPASettings{});
> +
> +	return 0;
> +}
> +
> +void IPU3CameraData::actOnIpa([[maybe_unused]] unsigned int id,
> +			      const IPAOperationData &action)
> +{
> +	switch (action.operation) {
> +	case IPU3_IPA_ACTION_SET_SENSOR_CONTROLS: {
> +		const ControlList &controls = action.controls[0];
> +		delayedCtrls_->push(controls);
> +		break;
> +	}
> +	default:
> +		LOG(IPU3, Error) << "Unknown action " << action.operation;
> +		break;
> +	}
> +}
> +
>   /* -----------------------------------------------------------------------------
>    * Buffer Ready slots
>    */
Jacopo Mondi Jan. 7, 2021, 12:40 p.m. UTC | #3
Hi Niklas,

On Tue, Dec 29, 2020 at 05:03:14PM +0100, Niklas Söderlund wrote:
> Attach to the IPA and allow it to push V4L2 controls that applies on the
> camera sensor. The IPA is not fully integrated in the pipeline as
> statistics and parameters buffers are not yet allocated, processed by
> the IPA nor queued to the hardware.
>
> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> ---
> * Changes since v1
> - Rewrite to not use CameraSensor.
> - Fix mistake where configuration sen to IPA was overwritten.
> - Check that IPA configuration was successful before starting.
> - Update commit message.
> ---
>  src/libcamera/pipeline/ipu3/ipu3.cpp | 103 +++++++++++++++++++++++++++
>  1 file changed, 103 insertions(+)
>
> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> index a87ce8f3378ba2fe..95f1b75dc8be5d40 100644
> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> @@ -14,11 +14,14 @@
>  #include <libcamera/camera.h>
>  #include <libcamera/control_ids.h>
>  #include <libcamera/formats.h>
> +#include <libcamera/ipa/ipu3.h>
>  #include <libcamera/request.h>
>  #include <libcamera/stream.h>
>
>  #include "libcamera/internal/camera_sensor.h"
> +#include "libcamera/internal/delayed_controls.h"
>  #include "libcamera/internal/device_enumerator.h"
> +#include "libcamera/internal/ipa_manager.h"
>  #include "libcamera/internal/log.h"
>  #include "libcamera/internal/media_device.h"
>  #include "libcamera/internal/pipeline_handler.h"
> @@ -53,6 +56,8 @@ public:
>  	{
>  	}
>
> +	int loadIPA();
> +
>  	void imguOutputBufferReady(FrameBuffer *buffer);
>  	void cio2BufferReady(FrameBuffer *buffer);
>
> @@ -62,6 +67,11 @@ public:
>  	Stream outStream_;
>  	Stream vfStream_;
>  	Stream rawStream_;
> +
> +	std::unique_ptr<DelayedControls> delayedCtrls_;
> +
> +private:
> +	void actOnIpa(unsigned int id, const IPAOperationData &op);
>  };
>
>  class IPU3CameraConfiguration : public CameraConfiguration
> @@ -590,6 +600,13 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
>  	IPU3CameraData *data = cameraData(camera);
>  	CIO2Device *cio2 = &data->cio2_;
>  	ImgUDevice *imgu = data->imgu_;
> +
> +	CameraSensorInfo sensorInfo = {};
> +	std::map<unsigned int, IPAStream> streamConfig;
> +	std::map<unsigned int, const ControlInfoMap &> entityControls;
> +	IPAOperationData ipaConfig;
> +	IPAOperationData result = {};

You could s/= {}/{}
but it's minor

> +
>  	int ret;
>
>  	/* Allocate buffers for internal pipeline usage. */
> @@ -597,6 +614,11 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
>  	if (ret)
>  		return ret;
>
> +	IPAOperationData ipaData = {};
> +	ret = data->ipa_->start(ipaData, nullptr);
> +	if (ret)
> +		goto error;
> +
>  	/*
>  	 * Start the ImgU video devices, buffers will be queued to the
>  	 * ImgU output and viewfinder when requests will be queued.
> @@ -612,9 +634,40 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
>  		goto error;
>  	}
>
> +	/* Inform IPA of stream configuration and sensor controls. */
> +	ret = data->cio2_.sensor()->sensorInfo(&sensorInfo);
> +	if (ret) {
> +		/* \todo Turn to hard failure once sensors info is mandatory. */

we're rather working in the direction of making SensorInfo never fail.
I think you can drop this todo and hard fail here as if you have a
failure, it's because something bad happened when reading controls or
selection targets.

> +		LOG(IPU3, Warning) << "Camera sensor information not available";
> +		sensorInfo = {};
> +		ret = 0;
> +	}
> +
> +	streamConfig[0] = {
> +		.pixelFormat = data->outStream_.configuration().pixelFormat,
> +		.size = data->outStream_.configuration().size,
> +	};
> +	streamConfig[1] = {
> +		.pixelFormat = data->vfStream_.configuration().pixelFormat,
> +		.size = data->vfStream_.configuration().size,
> +	};

Is this ok if one of the two streams (likely vfStream_) is not
assigned ?

> +
> +	entityControls.emplace(0, data->cio2_.sensor()->controls());
> +
> +	data->ipa_->configure(sensorInfo, streamConfig, entityControls,
> +			      ipaConfig, &result);
> +
> +	if ((result.operation != IPU3_IPA_STATUS_CONFIGURATION) ||

This seems like it can't happen at the moment, but it's ok

> +	    (result.data.size() != 1) || (result.data.at(0) != 1)) {

I think the fact that returning 1 in data[0] means success should be
documented in the IPA protocol. Ah wait, we don't have one.

> +		LOG(IPU3, Warning) << "Failed to configure IPA";
> +		ret = -EINVAL;
> +		goto error;
> +	}
> +
>  	return 0;
>
>  error:
> +	data->ipa_->stop();
>  	freeBuffers(camera);
>  	LOG(IPU3, Error) << "Failed to start camera " << camera->id();
>
> @@ -631,6 +684,8 @@ void PipelineHandlerIPU3::stop(Camera *camera)
>  	if (ret)
>  		LOG(IPU3, Warning) << "Failed to stop camera " << camera->id();
>
> +	data->ipa_->stop();
> +
>  	freeBuffers(camera);
>  }
>
> @@ -762,12 +817,32 @@ int PipelineHandlerIPU3::registerCameras()
>  		if (ret)
>  			continue;
>
> +		ret = data->loadIPA();
> +		if (ret)
> +			continue;
> +
>  		/* Initialize the camera properties. */
>  		data->properties_ = cio2->sensor()->properties();
>
>  		/* Initialze the camera controls. */
>  		data->controlInfo_ = IPU3Controls;
>
> +		/*
> +		 * \todo Read dealy values from the sensor itself or from a
> +		 * a sensor database. For now use generic values taken from
> +		 * the Raspberry Pi and listed as generic values.
> +		 */
> +		std::unordered_map<uint32_t, unsigned int> delays = {
> +			{ V4L2_CID_ANALOGUE_GAIN, 1 },
> +			{ V4L2_CID_EXPOSURE, 2 },
> +		};
> +
> +		data->delayedCtrls_ =
> +			std::make_unique<DelayedControls>(cio2->sensor()->device(),
> +							  delays);

sigh

we should get the sensor database in sooner than later and move this
to the camera sensor before the same patter spread in multiple
pipelines

> +		data->cio2_.frameStart().connect(data->delayedCtrls_.get(),
> +						 &DelayedControls::applyControls);
> +
>  		/**
>  		 * \todo Dynamically assign ImgU and output devices to each
>  		 * stream and camera; as of now, limit support to two cameras
> @@ -811,6 +886,34 @@ int PipelineHandlerIPU3::registerCameras()
>  	return numCameras ? 0 : -ENODEV;
>  }
>
> +int IPU3CameraData::loadIPA()
> +{
> +	ipa_ = IPAManager::createIPA(pipe_, 1, 1);
> +	if (!ipa_)
> +		return -ENOENT;
> +
> +	ipa_->queueFrameAction.connect(this, &IPU3CameraData::actOnIpa);
> +
> +	ipa_->init(IPASettings{});
> +
> +	return 0;
> +}
> +
> +void IPU3CameraData::actOnIpa([[maybe_unused]] unsigned int id,
> +			      const IPAOperationData &action)
> +{
> +	switch (action.operation) {
> +	case IPU3_IPA_ACTION_SET_SENSOR_CONTROLS: {
> +		const ControlList &controls = action.controls[0];
> +		delayedCtrls_->push(controls);
> +		break;
> +	}
> +	default:
> +		LOG(IPU3, Error) << "Unknown action " << action.operation;
> +		break;
> +	}
> +}
> +

Overall, that's a good starting point
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>

Thanks
   j

>  /* -----------------------------------------------------------------------------
>   * Buffer Ready slots
>   */
> --
> 2.29.2
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
Laurent Pinchart Jan. 10, 2021, 4:10 p.m. UTC | #4
Hi Jean-Michel,

On Wed, Dec 30, 2020 at 06:00:51PM +0100, Jean-Michel Hautbois wrote:
> On 29/12/2020 17:03, Niklas Söderlund wrote:
> > Attach to the IPA and allow it to push V4L2 controls that applies on the
> > camera sensor. The IPA is not fully integrated in the pipeline as
> > statistics and parameters buffers are not yet allocated, processed by
> > the IPA nor queued to the hardware.
> > 
> > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> > ---
> > * Changes since v1
> > - Rewrite to not use CameraSensor.
> > - Fix mistake where configuration sen to IPA was overwritten.
> > - Check that IPA configuration was successful before starting.
> > - Update commit message.
> > ---
> >  src/libcamera/pipeline/ipu3/ipu3.cpp | 103 +++++++++++++++++++++++++++
> >  1 file changed, 103 insertions(+)
> > 
> > diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > index a87ce8f3378ba2fe..95f1b75dc8be5d40 100644
> > --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> > +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > @@ -14,11 +14,14 @@
> >  #include <libcamera/camera.h>
> >  #include <libcamera/control_ids.h>
> >  #include <libcamera/formats.h>
> > +#include <libcamera/ipa/ipu3.h>
> >  #include <libcamera/request.h>
> >  #include <libcamera/stream.h>
> >  
> >  #include "libcamera/internal/camera_sensor.h"
> > +#include "libcamera/internal/delayed_controls.h"
> >  #include "libcamera/internal/device_enumerator.h"
> > +#include "libcamera/internal/ipa_manager.h"
> >  #include "libcamera/internal/log.h"
> >  #include "libcamera/internal/media_device.h"
> >  #include "libcamera/internal/pipeline_handler.h"
> > @@ -53,6 +56,8 @@ public:
> >  	{
> >  	}
> >  
> > +	int loadIPA();
> > +
> >  	void imguOutputBufferReady(FrameBuffer *buffer);
> >  	void cio2BufferReady(FrameBuffer *buffer);
> >  
> > @@ -62,6 +67,11 @@ public:
> >  	Stream outStream_;
> >  	Stream vfStream_;
> >  	Stream rawStream_;
> > +
> > +	std::unique_ptr<DelayedControls> delayedCtrls_;
> > +
> > +private:
> > +	void actOnIpa(unsigned int id, const IPAOperationData &op);
> >  };
> >  
> >  class IPU3CameraConfiguration : public CameraConfiguration
> > @@ -590,6 +600,13 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
> >  	IPU3CameraData *data = cameraData(camera);
> >  	CIO2Device *cio2 = &data->cio2_;
> >  	ImgUDevice *imgu = data->imgu_;
> > +
> > +	CameraSensorInfo sensorInfo = {};
> > +	std::map<unsigned int, IPAStream> streamConfig;
> > +	std::map<unsigned int, const ControlInfoMap &> entityControls;
> > +	IPAOperationData ipaConfig;
> > +	IPAOperationData result = {};
> > +
> >  	int ret;
> >  
> >  	/* Allocate buffers for internal pipeline usage. */
> > @@ -597,6 +614,11 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
> >  	if (ret)
> >  		return ret;
> >  
> > +	IPAOperationData ipaData = {};
> > +	ret = data->ipa_->start(ipaData, nullptr);
> > +	if (ret)
> > +		goto error;
> > +
> >  	/*
> >  	 * Start the ImgU video devices, buffers will be queued to the
> >  	 * ImgU output and viewfinder when requests will be queued.
> > @@ -612,9 +634,40 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
> >  		goto error;
> >  	}
> >  
> > +	/* Inform IPA of stream configuration and sensor controls. */
> > +	ret = data->cio2_.sensor()->sensorInfo(&sensorInfo);
> > +	if (ret) {
> > +		/* \todo Turn to hard failure once sensors info is mandatory. */
> > +		LOG(IPU3, Warning) << "Camera sensor information not available";
> > +		sensorInfo = {};
> > +		ret = 0;
> > +	}
> > +
> > +	streamConfig[0] = {
> > +		.pixelFormat = data->outStream_.configuration().pixelFormat,
> > +		.size = data->outStream_.configuration().size,
> > +	};
> > +	streamConfig[1] = {
> > +		.pixelFormat = data->vfStream_.configuration().pixelFormat,
> > +		.size = data->vfStream_.configuration().size,
> > +	};
> > +
> > +	entityControls.emplace(0, data->cio2_.sensor()->controls());
> > +
> > +	data->ipa_->configure(sensorInfo, streamConfig, entityControls,
> > +			      ipaConfig, &result);
> > +
> > +	if ((result.operation != IPU3_IPA_STATUS_CONFIGURATION) ||
> > +	    (result.data.size() != 1) || (result.data.at(0) != 1)) {
> > +		LOG(IPU3, Warning) << "Failed to configure IPA";
> > +		ret = -EINVAL;
> > +		goto error;
> > +	}
> > +
> >  	return 0;
> >  
> >  error:
> > +	data->ipa_->stop();
> >  	freeBuffers(camera);
> >  	LOG(IPU3, Error) << "Failed to start camera " << camera->id();
> >  
> > @@ -631,6 +684,8 @@ void PipelineHandlerIPU3::stop(Camera *camera)
> >  	if (ret)
> >  		LOG(IPU3, Warning) << "Failed to stop camera " << camera->id();
> >  
> > +	data->ipa_->stop();
> > +
> >  	freeBuffers(camera);
> >  }
> >  
> > @@ -762,12 +817,32 @@ int PipelineHandlerIPU3::registerCameras()
> >  		if (ret)
> >  			continue;
> >  
> > +		ret = data->loadIPA();
> > +		if (ret)
> > +			continue;
> > +
> >  		/* Initialize the camera properties. */
> >  		data->properties_ = cio2->sensor()->properties();
> >  
> >  		/* Initialze the camera controls. */
> >  		data->controlInfo_ = IPU3Controls;
> >  
> > +		/*
> > +		 * \todo Read dealy values from the sensor itself or from a
> > +		 * a sensor database. For now use generic values taken from
> > +		 * the Raspberry Pi and listed as generic values.
> > +		 */
> > +		std::unordered_map<uint32_t, unsigned int> delays = {
> > +			{ V4L2_CID_ANALOGUE_GAIN, 1 },
> > +			{ V4L2_CID_EXPOSURE, 2 },
> > +		};
> 
> I agree, the controls we want to have need to be parsed in some way.
> I am wondering what makes a control beeing a "delayedControl" and what
> is not ?
> 
> Looking at the RPi pipeline and IPA, I can see the
> V4L2_CID_ANALOGUE_GAIN and V4L2_CID_EXPOSURE controls beeing delayed
> controls, but in the RPi IPA there is for example a call to the
> V4L2_CID_DIGITAL_GAIN control.
> 
> Would we need a database to know which controls are effectively to be
> delayed ones, and when the sensor is configured, parse the controls
> available and add the corresponding ones to the map ?
> This is an open question :-).

Yes, we do, and the DelayedControl instance should be moved to the
CameraSensor class, with control handling becoming more transparent for
the pipeline handlers.

> > +		data->delayedCtrls_ =
> > +			std::make_unique<DelayedControls>(cio2->sensor()->device(),
> > +							  delays);
> > +		data->cio2_.frameStart().connect(data->delayedCtrls_.get(),
> > +						 &DelayedControls::applyControls);
> > +
> >  		/**
> >  		 * \todo Dynamically assign ImgU and output devices to each
> >  		 * stream and camera; as of now, limit support to two cameras
> > @@ -811,6 +886,34 @@ int PipelineHandlerIPU3::registerCameras()
> >  	return numCameras ? 0 : -ENODEV;
> >  }
> >  
> > +int IPU3CameraData::loadIPA()
> > +{
> > +	ipa_ = IPAManager::createIPA(pipe_, 1, 1);
> > +	if (!ipa_)
> > +		return -ENOENT;
> > +
> > +	ipa_->queueFrameAction.connect(this, &IPU3CameraData::actOnIpa);
> > +
> > +	ipa_->init(IPASettings{});
> > +
> > +	return 0;
> > +}
> > +
> > +void IPU3CameraData::actOnIpa([[maybe_unused]] unsigned int id,
> > +			      const IPAOperationData &action)
> > +{
> > +	switch (action.operation) {
> > +	case IPU3_IPA_ACTION_SET_SENSOR_CONTROLS: {
> > +		const ControlList &controls = action.controls[0];
> > +		delayedCtrls_->push(controls);
> > +		break;
> > +	}
> > +	default:
> > +		LOG(IPU3, Error) << "Unknown action " << action.operation;
> > +		break;
> > +	}
> > +}
> > +
> >  /* -----------------------------------------------------------------------------
> >   * Buffer Ready slots
> >   */
Laurent Pinchart Jan. 10, 2021, 4:46 p.m. UTC | #5
Hi Niklas,

Thank you for the patch.

On Thu, Jan 07, 2021 at 01:40:50PM +0100, Jacopo Mondi wrote:
> On Tue, Dec 29, 2020 at 05:03:14PM +0100, Niklas Söderlund wrote:
> > Attach to the IPA and allow it to push V4L2 controls that applies on the

s/applies on/apply to/

> > camera sensor. The IPA is not fully integrated in the pipeline as
> > statistics and parameters buffers are not yet allocated, processed by
> > the IPA nor queued to the hardware.
> >
> > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> > ---
> > * Changes since v1
> > - Rewrite to not use CameraSensor.
> > - Fix mistake where configuration sen to IPA was overwritten.
> > - Check that IPA configuration was successful before starting.
> > - Update commit message.
> > ---
> >  src/libcamera/pipeline/ipu3/ipu3.cpp | 103 +++++++++++++++++++++++++++
> >  1 file changed, 103 insertions(+)
> >
> > diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > index a87ce8f3378ba2fe..95f1b75dc8be5d40 100644
> > --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> > +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > @@ -14,11 +14,14 @@
> >  #include <libcamera/camera.h>
> >  #include <libcamera/control_ids.h>
> >  #include <libcamera/formats.h>
> > +#include <libcamera/ipa/ipu3.h>
> >  #include <libcamera/request.h>
> >  #include <libcamera/stream.h>
> >
> >  #include "libcamera/internal/camera_sensor.h"
> > +#include "libcamera/internal/delayed_controls.h"
> >  #include "libcamera/internal/device_enumerator.h"
> > +#include "libcamera/internal/ipa_manager.h"
> >  #include "libcamera/internal/log.h"
> >  #include "libcamera/internal/media_device.h"
> >  #include "libcamera/internal/pipeline_handler.h"
> > @@ -53,6 +56,8 @@ public:
> >  	{
> >  	}
> >
> > +	int loadIPA();
> > +
> >  	void imguOutputBufferReady(FrameBuffer *buffer);
> >  	void cio2BufferReady(FrameBuffer *buffer);
> >
> > @@ -62,6 +67,11 @@ public:
> >  	Stream outStream_;
> >  	Stream vfStream_;
> >  	Stream rawStream_;
> > +
> > +	std::unique_ptr<DelayedControls> delayedCtrls_;
> > +
> > +private:
> > +	void actOnIpa(unsigned int id, const IPAOperationData &op);
> >  };
> >
> >  class IPU3CameraConfiguration : public CameraConfiguration
> > @@ -590,6 +600,13 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
> >  	IPU3CameraData *data = cameraData(camera);
> >  	CIO2Device *cio2 = &data->cio2_;
> >  	ImgUDevice *imgu = data->imgu_;
> > +
> > +	CameraSensorInfo sensorInfo = {};
> > +	std::map<unsigned int, IPAStream> streamConfig;
> > +	std::map<unsigned int, const ControlInfoMap &> entityControls;
> > +	IPAOperationData ipaConfig;
> > +	IPAOperationData result = {};
> 
> You could s/= {}/{}
> but it's minor
> 
> > +
> >  	int ret;
> >
> >  	/* Allocate buffers for internal pipeline usage. */
> > @@ -597,6 +614,11 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
> >  	if (ret)
> >  		return ret;
> >
> > +	IPAOperationData ipaData = {};
> > +	ret = data->ipa_->start(ipaData, nullptr);
> > +	if (ret)
> > +		goto error;
> > +
> >  	/*
> >  	 * Start the ImgU video devices, buffers will be queued to the
> >  	 * ImgU output and viewfinder when requests will be queued.
> > @@ -612,9 +634,40 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
> >  		goto error;
> >  	}
> >
> > +	/* Inform IPA of stream configuration and sensor controls. */
> > +	ret = data->cio2_.sensor()->sensorInfo(&sensorInfo);
> > +	if (ret) {
> > +		/* \todo Turn to hard failure once sensors info is mandatory. */
> 
> we're rather working in the direction of making SensorInfo never fail.
> I think you can drop this todo and hard fail here as if you have a
> failure, it's because something bad happened when reading controls or
> selection targets.

Agreed.

> > +		LOG(IPU3, Warning) << "Camera sensor information not available";
> > +		sensorInfo = {};
> > +		ret = 0;
> > +	}
> > +
> > +	streamConfig[0] = {
> > +		.pixelFormat = data->outStream_.configuration().pixelFormat,
> > +		.size = data->outStream_.configuration().size,
> > +	};
> > +	streamConfig[1] = {
> > +		.pixelFormat = data->vfStream_.configuration().pixelFormat,
> > +		.size = data->vfStream_.configuration().size,
> > +	};
> 
> Is this ok if one of the two streams (likely vfStream_) is not
> assigned ?

Based on our current IPA interface, I'd say only active streams should
be reported here. We're however moving to a per-pipeline handler IPA
protocol, and it would be fine having a hardcoded array of two streams,
as long, of course, as there's enough information for the IPA to tell
which streams are active (invalid pixel format for instance). Obviously
a hardcoded array isn't required with the custom IPA protocol, we can
still device to pass the active streams only in that case.

The StreamConfiguration() stored in a Stream is initialized with
appropriate defaults, but if we configure the camera with two streams,
and then again with a single stream, I think we'll have stale values.

> > +
> > +	entityControls.emplace(0, data->cio2_.sensor()->controls());
> > +
> > +	data->ipa_->configure(sensorInfo, streamConfig, entityControls,
> > +			      ipaConfig, &result);
> > +
> > +	if ((result.operation != IPU3_IPA_STATUS_CONFIGURATION) ||
> 
> This seems like it can't happen at the moment, but it's ok

Yes, I'm not sure what the idea behind the check is.

> > +	    (result.data.size() != 1) || (result.data.at(0) != 1)) {
> 
> I think the fact that returning 1 in data[0] means success should be
> documented in the IPA protocol. Ah wait, we don't have one.

RPi checks

	if (result.operation & RPi::IPA_CONFIG_FAILED)

and we could do something similar for now.

> > +		LOG(IPU3, Warning) << "Failed to configure IPA";
> > +		ret = -EINVAL;
> > +		goto error;
> > +	}
> > +
> >  	return 0;
> >
> >  error:
> > +	data->ipa_->stop();
> >  	freeBuffers(camera);
> >  	LOG(IPU3, Error) << "Failed to start camera " << camera->id();
> >
> > @@ -631,6 +684,8 @@ void PipelineHandlerIPU3::stop(Camera *camera)
> >  	if (ret)
> >  		LOG(IPU3, Warning) << "Failed to stop camera " << camera->id();
> >
> > +	data->ipa_->stop();
> > +
> >  	freeBuffers(camera);
> >  }
> >
> > @@ -762,12 +817,32 @@ int PipelineHandlerIPU3::registerCameras()
> >  		if (ret)
> >  			continue;
> >
> > +		ret = data->loadIPA();
> > +		if (ret)
> > +			continue;
> > +
> >  		/* Initialize the camera properties. */
> >  		data->properties_ = cio2->sensor()->properties();
> >
> >  		/* Initialze the camera controls. */
> >  		data->controlInfo_ = IPU3Controls;
> >
> > +		/*
> > +		 * \todo Read dealy values from the sensor itself or from a
> > +		 * a sensor database. For now use generic values taken from
> > +		 * the Raspberry Pi and listed as generic values.
> > +		 */
> > +		std::unordered_map<uint32_t, unsigned int> delays = {
> > +			{ V4L2_CID_ANALOGUE_GAIN, 1 },
> > +			{ V4L2_CID_EXPOSURE, 2 },
> > +		};
> > +
> > +		data->delayedCtrls_ =
> > +			std::make_unique<DelayedControls>(cio2->sensor()->device(),
> > +							  delays);
> 
> sigh
> 
> we should get the sensor database in sooner than later and move this
> to the camera sensor before the same patter spread in multiple
> pipelines

I can't disagree on this :-) (moving on the review of the corresponding
patches next)

> > +		data->cio2_.frameStart().connect(data->delayedCtrls_.get(),
> > +						 &DelayedControls::applyControls);
> > +
> >  		/**
> >  		 * \todo Dynamically assign ImgU and output devices to each
> >  		 * stream and camera; as of now, limit support to two cameras
> > @@ -811,6 +886,34 @@ int PipelineHandlerIPU3::registerCameras()
> >  	return numCameras ? 0 : -ENODEV;
> >  }
> >
> > +int IPU3CameraData::loadIPA()
> > +{
> > +	ipa_ = IPAManager::createIPA(pipe_, 1, 1);
> > +	if (!ipa_)
> > +		return -ENOENT;
> > +
> > +	ipa_->queueFrameAction.connect(this, &IPU3CameraData::actOnIpa);
> > +
> > +	ipa_->init(IPASettings{});
> > +
> > +	return 0;
> > +}
> > +
> > +void IPU3CameraData::actOnIpa([[maybe_unused]] unsigned int id,
> > +			      const IPAOperationData &action)

Maybe s/actOnIpa/queueFrameAction/ to match the other pipeline handlers
?

> > +{
> > +	switch (action.operation) {
> > +	case IPU3_IPA_ACTION_SET_SENSOR_CONTROLS: {
> > +		const ControlList &controls = action.controls[0];
> > +		delayedCtrls_->push(controls);
> > +		break;
> > +	}
> > +	default:
> > +		LOG(IPU3, Error) << "Unknown action " << action.operation;
> > +		break;
> > +	}
> > +}
> > +
> 
> Overall, that's a good starting point
> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>

Same,

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> >  /* -----------------------------------------------------------------------------
> >   * Buffer Ready slots
> >   */
Umang Jain Jan. 11, 2021, 4:40 p.m. UTC | #6
Hi Niklas,

Quick comment on my findings last friday,

On 12/29/20 9:33 PM, Niklas Söderlund wrote:
> Attach to the IPA and allow it to push V4L2 controls that applies on the
> camera sensor. The IPA is not fully integrated in the pipeline as
> statistics and parameters buffers are not yet allocated, processed by
> the IPA nor queued to the hardware.
>
> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> ---
> * Changes since v1
> - Rewrite to not use CameraSensor.
> - Fix mistake where configuration sen to IPA was overwritten.
> - Check that IPA configuration was successful before starting.
> - Update commit message.
> ---
>   src/libcamera/pipeline/ipu3/ipu3.cpp | 103 +++++++++++++++++++++++++++
>   1 file changed, 103 insertions(+)
>
> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> index a87ce8f3378ba2fe..95f1b75dc8be5d40 100644
> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> @@ -14,11 +14,14 @@
>   #include <libcamera/camera.h>
>   #include <libcamera/control_ids.h>
>   #include <libcamera/formats.h>
> +#include <libcamera/ipa/ipu3.h>
>   #include <libcamera/request.h>
>   #include <libcamera/stream.h>
>   
>   #include "libcamera/internal/camera_sensor.h"
> +#include "libcamera/internal/delayed_controls.h"
>   #include "libcamera/internal/device_enumerator.h"
> +#include "libcamera/internal/ipa_manager.h"
>   #include "libcamera/internal/log.h"
>   #include "libcamera/internal/media_device.h"
>   #include "libcamera/internal/pipeline_handler.h"
> @@ -53,6 +56,8 @@ public:
>   	{
>   	}
>   
> +	int loadIPA();
> +
>   	void imguOutputBufferReady(FrameBuffer *buffer);
>   	void cio2BufferReady(FrameBuffer *buffer);
>   
> @@ -62,6 +67,11 @@ public:
>   	Stream outStream_;
>   	Stream vfStream_;
>   	Stream rawStream_;
> +
> +	std::unique_ptr<DelayedControls> delayedCtrls_;
> +
> +private:
> +	void actOnIpa(unsigned int id, const IPAOperationData &op);
>   };
>   
>   class IPU3CameraConfiguration : public CameraConfiguration
> @@ -590,6 +600,13 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
>   	IPU3CameraData *data = cameraData(camera);
>   	CIO2Device *cio2 = &data->cio2_;
>   	ImgUDevice *imgu = data->imgu_;
> +
> +	CameraSensorInfo sensorInfo = {};
> +	std::map<unsigned int, IPAStream> streamConfig;
> +	std::map<unsigned int, const ControlInfoMap &> entityControls;
> +	IPAOperationData ipaConfig;
> +	IPAOperationData result = {};
> +
>   	int ret;
>   
>   	/* Allocate buffers for internal pipeline usage. */
> @@ -597,6 +614,11 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
>   	if (ret)
>   		return ret;
>   
> +	IPAOperationData ipaData = {};
> +	ret = data->ipa_->start(ipaData, nullptr);
> +	if (ret)
> +		goto error;
> +
>   	/*
>   	 * Start the ImgU video devices, buffers will be queued to the
>   	 * ImgU output and viewfinder when requests will be queued.
> @@ -612,9 +634,40 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
>   		goto error;
>   	}
>   
> +	/* Inform IPA of stream configuration and sensor controls. */
> +	ret = data->cio2_.sensor()->sensorInfo(&sensorInfo);
> +	if (ret) {
> +		/* \todo Turn to hard failure once sensors info is mandatory. */
> +		LOG(IPU3, Warning) << "Camera sensor information not available";
> +		sensorInfo = {};
> +		ret = 0;
> +	}
> +
> +	streamConfig[0] = {
> +		.pixelFormat = data->outStream_.configuration().pixelFormat,
> +		.size = data->outStream_.configuration().size,
> +	};
> +	streamConfig[1] = {
> +		.pixelFormat = data->vfStream_.configuration().pixelFormat,
> +		.size = data->vfStream_.configuration().size,
> +	};
> +
> +	entityControls.emplace(0, data->cio2_.sensor()->controls());
> +
> +	data->ipa_->configure(sensorInfo, streamConfig, entityControls,
> +			      ipaConfig, &result);
> +
> +	if ((result.operation != IPU3_IPA_STATUS_CONFIGURATION) ||
> +	    (result.data.size() != 1) || (result.data.at(0) != 1)) {
> +		LOG(IPU3, Warning) << "Failed to configure IPA";
> +		ret = -EINVAL;
> +		goto error;
Running your branch as is, `ipu3/ipa` triggered this path and spit out 
EBUSY with freeBuffers() below - so one of the quick things Jacopo 
noticed that you need to stop the ImgU and CIO2 here first. I had 
applied the change locally and it fixed it for me.

I also wrote a associated patch(nothing major) for simplifying error 
paths with subject "ipu3: Simplify error path" - maybe it's interesting 
to you.
> +	}
> +
>   	return 0;
>   
>   error:
> +	data->ipa_->stop();
>   	freeBuffers(camera);
>   	LOG(IPU3, Error) << "Failed to start camera " << camera->id();
>   
> @@ -631,6 +684,8 @@ void PipelineHandlerIPU3::stop(Camera *camera)
>   	if (ret)
>   		LOG(IPU3, Warning) << "Failed to stop camera " << camera->id();
>   
> +	data->ipa_->stop();
> +
>   	freeBuffers(camera);
>   }
>   
> @@ -762,12 +817,32 @@ int PipelineHandlerIPU3::registerCameras()
>   		if (ret)
>   			continue;
>   
> +		ret = data->loadIPA();
> +		if (ret)
> +			continue;
> +
>   		/* Initialize the camera properties. */
>   		data->properties_ = cio2->sensor()->properties();
>   
>   		/* Initialze the camera controls. */
>   		data->controlInfo_ = IPU3Controls;
>   
> +		/*
> +		 * \todo Read dealy values from the sensor itself or from a
> +		 * a sensor database. For now use generic values taken from
> +		 * the Raspberry Pi and listed as generic values.
> +		 */
> +		std::unordered_map<uint32_t, unsigned int> delays = {
> +			{ V4L2_CID_ANALOGUE_GAIN, 1 },
> +			{ V4L2_CID_EXPOSURE, 2 },
> +		};
> +
> +		data->delayedCtrls_ =
> +			std::make_unique<DelayedControls>(cio2->sensor()->device(),
> +							  delays);
> +		data->cio2_.frameStart().connect(data->delayedCtrls_.get(),
> +						 &DelayedControls::applyControls);
> +
>   		/**
>   		 * \todo Dynamically assign ImgU and output devices to each
>   		 * stream and camera; as of now, limit support to two cameras
> @@ -811,6 +886,34 @@ int PipelineHandlerIPU3::registerCameras()
>   	return numCameras ? 0 : -ENODEV;
>   }
>   
> +int IPU3CameraData::loadIPA()
> +{
> +	ipa_ = IPAManager::createIPA(pipe_, 1, 1);
> +	if (!ipa_)
> +		return -ENOENT;
> +
> +	ipa_->queueFrameAction.connect(this, &IPU3CameraData::actOnIpa);
> +
> +	ipa_->init(IPASettings{});
> +
> +	return 0;
> +}
> +
> +void IPU3CameraData::actOnIpa([[maybe_unused]] unsigned int id,
> +			      const IPAOperationData &action)
> +{
> +	switch (action.operation) {
> +	case IPU3_IPA_ACTION_SET_SENSOR_CONTROLS: {
> +		const ControlList &controls = action.controls[0];
> +		delayedCtrls_->push(controls);
> +		break;
> +	}
> +	default:
> +		LOG(IPU3, Error) << "Unknown action " << action.operation;
> +		break;
> +	}
> +}
> +
>   /* -----------------------------------------------------------------------------
>    * Buffer Ready slots
>    */
Niklas Söderlund Feb. 3, 2021, 2:35 p.m. UTC | #7
Hi Jacopo and Laurent,

Thanks for your feedback.

On 2021-01-10 18:46:46 +0200, Laurent Pinchart wrote:
> Hi Niklas,
> 
> Thank you for the patch.
> 
> On Thu, Jan 07, 2021 at 01:40:50PM +0100, Jacopo Mondi wrote:
> > On Tue, Dec 29, 2020 at 05:03:14PM +0100, Niklas Söderlund wrote:
> > > Attach to the IPA and allow it to push V4L2 controls that applies on the
> 
> s/applies on/apply to/
> 
> > > camera sensor. The IPA is not fully integrated in the pipeline as
> > > statistics and parameters buffers are not yet allocated, processed by
> > > the IPA nor queued to the hardware.
> > >
> > > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> > > ---
> > > * Changes since v1
> > > - Rewrite to not use CameraSensor.
> > > - Fix mistake where configuration sen to IPA was overwritten.
> > > - Check that IPA configuration was successful before starting.
> > > - Update commit message.
> > > ---
> > >  src/libcamera/pipeline/ipu3/ipu3.cpp | 103 +++++++++++++++++++++++++++
> > >  1 file changed, 103 insertions(+)
> > >
> > > diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > index a87ce8f3378ba2fe..95f1b75dc8be5d40 100644
> > > --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > @@ -14,11 +14,14 @@
> > >  #include <libcamera/camera.h>
> > >  #include <libcamera/control_ids.h>
> > >  #include <libcamera/formats.h>
> > > +#include <libcamera/ipa/ipu3.h>
> > >  #include <libcamera/request.h>
> > >  #include <libcamera/stream.h>
> > >
> > >  #include "libcamera/internal/camera_sensor.h"
> > > +#include "libcamera/internal/delayed_controls.h"
> > >  #include "libcamera/internal/device_enumerator.h"
> > > +#include "libcamera/internal/ipa_manager.h"
> > >  #include "libcamera/internal/log.h"
> > >  #include "libcamera/internal/media_device.h"
> > >  #include "libcamera/internal/pipeline_handler.h"
> > > @@ -53,6 +56,8 @@ public:
> > >  	{
> > >  	}
> > >
> > > +	int loadIPA();
> > > +
> > >  	void imguOutputBufferReady(FrameBuffer *buffer);
> > >  	void cio2BufferReady(FrameBuffer *buffer);
> > >
> > > @@ -62,6 +67,11 @@ public:
> > >  	Stream outStream_;
> > >  	Stream vfStream_;
> > >  	Stream rawStream_;
> > > +
> > > +	std::unique_ptr<DelayedControls> delayedCtrls_;
> > > +
> > > +private:
> > > +	void actOnIpa(unsigned int id, const IPAOperationData &op);
> > >  };
> > >
> > >  class IPU3CameraConfiguration : public CameraConfiguration
> > > @@ -590,6 +600,13 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
> > >  	IPU3CameraData *data = cameraData(camera);
> > >  	CIO2Device *cio2 = &data->cio2_;
> > >  	ImgUDevice *imgu = data->imgu_;
> > > +
> > > +	CameraSensorInfo sensorInfo = {};
> > > +	std::map<unsigned int, IPAStream> streamConfig;
> > > +	std::map<unsigned int, const ControlInfoMap &> entityControls;
> > > +	IPAOperationData ipaConfig;
> > > +	IPAOperationData result = {};
> > 
> > You could s/= {}/{}
> > but it's minor
> > 

I could but I find it harder to read.

> > > +
> > >  	int ret;
> > >
> > >  	/* Allocate buffers for internal pipeline usage. */
> > > @@ -597,6 +614,11 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
> > >  	if (ret)
> > >  		return ret;
> > >
> > > +	IPAOperationData ipaData = {};
> > > +	ret = data->ipa_->start(ipaData, nullptr);
> > > +	if (ret)
> > > +		goto error;
> > > +
> > >  	/*
> > >  	 * Start the ImgU video devices, buffers will be queued to the
> > >  	 * ImgU output and viewfinder when requests will be queued.
> > > @@ -612,9 +634,40 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
> > >  		goto error;
> > >  	}
> > >
> > > +	/* Inform IPA of stream configuration and sensor controls. */
> > > +	ret = data->cio2_.sensor()->sensorInfo(&sensorInfo);
> > > +	if (ret) {
> > > +		/* \todo Turn to hard failure once sensors info is mandatory. */
> > 
> > we're rather working in the direction of making SensorInfo never fail.
> > I think you can drop this todo and hard fail here as if you have a
> > failure, it's because something bad happened when reading controls or
> > selection targets.
> 
> Agreed.

I thinks this shows the age of this patch :-)

This TODO was recorded as the call failed at the time of patch creation.  
Now the calls succeeds and this should indeed be turned into a hard 
fail. Will do so in next version.

> 
> > > +		LOG(IPU3, Warning) << "Camera sensor information not available";
> > > +		sensorInfo = {};
> > > +		ret = 0;
> > > +	}
> > > +
> > > +	streamConfig[0] = {
> > > +		.pixelFormat = data->outStream_.configuration().pixelFormat,
> > > +		.size = data->outStream_.configuration().size,
> > > +	};
> > > +	streamConfig[1] = {
> > > +		.pixelFormat = data->vfStream_.configuration().pixelFormat,
> > > +		.size = data->vfStream_.configuration().size,
> > > +	};
> > 
> > Is this ok if one of the two streams (likely vfStream_) is not
> > assigned ?
> 
> Based on our current IPA interface, I'd say only active streams should
> be reported here. We're however moving to a per-pipeline handler IPA
> protocol, and it would be fine having a hardcoded array of two streams,
> as long, of course, as there's enough information for the IPA to tell
> which streams are active (invalid pixel format for instance). Obviously
> a hardcoded array isn't required with the custom IPA protocol, we can
> still device to pass the active streams only in that case.
> 
> The StreamConfiguration() stored in a Stream is initialized with
> appropriate defaults, but if we configure the camera with two streams,
> and then again with a single stream, I think we'll have stale values.

The viewfinder is always configured in configue(), even if it's not 
needed by the configuration we are asked to do, so it will never be 
stale. It's also always started in start(). The only different between 
if the configuration contains viewifinder or not is that we won't allow 
applications to queue buffers to it if its not.

I'm open to any solution here that moves us forward as quickly as 
possible. But given that the stream configuration is not consumed by the 
IPA and is only expressed here to satisfy our current IPA interface that 
that we know will be replaced soon I would prefers to either keep this 
as-is or remove this and simply pass an empty map to ipa 
configuration(). Creating something complex here to only report active 
streams seems a bit pointless at this point.

> 
> > > +
> > > +	entityControls.emplace(0, data->cio2_.sensor()->controls());
> > > +
> > > +	data->ipa_->configure(sensorInfo, streamConfig, entityControls,
> > > +			      ipaConfig, &result);
> > > +
> > > +	if ((result.operation != IPU3_IPA_STATUS_CONFIGURATION) ||
> > 
> > This seems like it can't happen at the moment, but it's ok
> 
> Yes, I'm not sure what the idea behind the check is.
> 
> > > +	    (result.data.size() != 1) || (result.data.at(0) != 1)) {
> > 
> > I think the fact that returning 1 in data[0] means success should be
> > documented in the IPA protocol. Ah wait, we don't have one.
> 
> RPi checks
> 
> 	if (result.operation & RPi::IPA_CONFIG_FAILED)
> 
> and we could do something similar for now.

Wops, All of this result check is left from a experiment I did. This 
should indeed be removed. Thanks for spotting it.

> 
> > > +		LOG(IPU3, Warning) << "Failed to configure IPA";
> > > +		ret = -EINVAL;
> > > +		goto error;
> > > +	}
> > > +
> > >  	return 0;
> > >
> > >  error:
> > > +	data->ipa_->stop();
> > >  	freeBuffers(camera);
> > >  	LOG(IPU3, Error) << "Failed to start camera " << camera->id();
> > >
> > > @@ -631,6 +684,8 @@ void PipelineHandlerIPU3::stop(Camera *camera)
> > >  	if (ret)
> > >  		LOG(IPU3, Warning) << "Failed to stop camera " << camera->id();
> > >
> > > +	data->ipa_->stop();
> > > +
> > >  	freeBuffers(camera);
> > >  }
> > >
> > > @@ -762,12 +817,32 @@ int PipelineHandlerIPU3::registerCameras()
> > >  		if (ret)
> > >  			continue;
> > >
> > > +		ret = data->loadIPA();
> > > +		if (ret)
> > > +			continue;
> > > +
> > >  		/* Initialize the camera properties. */
> > >  		data->properties_ = cio2->sensor()->properties();
> > >
> > >  		/* Initialze the camera controls. */
> > >  		data->controlInfo_ = IPU3Controls;
> > >
> > > +		/*
> > > +		 * \todo Read dealy values from the sensor itself or from a
> > > +		 * a sensor database. For now use generic values taken from
> > > +		 * the Raspberry Pi and listed as generic values.
> > > +		 */
> > > +		std::unordered_map<uint32_t, unsigned int> delays = {
> > > +			{ V4L2_CID_ANALOGUE_GAIN, 1 },
> > > +			{ V4L2_CID_EXPOSURE, 2 },
> > > +		};
> > > +
> > > +		data->delayedCtrls_ =
> > > +			std::make_unique<DelayedControls>(cio2->sensor()->device(),
> > > +							  delays);
> > 
> > sigh
> > 
> > we should get the sensor database in sooner than later and move this
> > to the camera sensor before the same patter spread in multiple
> > pipelines
> 
> I can't disagree on this :-) (moving on the review of the corresponding
> patches next)

I too would like to get the database in place as soon as possible.

> 
> > > +		data->cio2_.frameStart().connect(data->delayedCtrls_.get(),
> > > +						 &DelayedControls::applyControls);
> > > +
> > >  		/**
> > >  		 * \todo Dynamically assign ImgU and output devices to each
> > >  		 * stream and camera; as of now, limit support to two cameras
> > > @@ -811,6 +886,34 @@ int PipelineHandlerIPU3::registerCameras()
> > >  	return numCameras ? 0 : -ENODEV;
> > >  }
> > >
> > > +int IPU3CameraData::loadIPA()
> > > +{
> > > +	ipa_ = IPAManager::createIPA(pipe_, 1, 1);
> > > +	if (!ipa_)
> > > +		return -ENOENT;
> > > +
> > > +	ipa_->queueFrameAction.connect(this, &IPU3CameraData::actOnIpa);
> > > +
> > > +	ipa_->init(IPASettings{});
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +void IPU3CameraData::actOnIpa([[maybe_unused]] unsigned int id,
> > > +			      const IPAOperationData &action)
> 
> Maybe s/actOnIpa/queueFrameAction/ to match the other pipeline handlers
> ?

Sure.

> 
> > > +{
> > > +	switch (action.operation) {
> > > +	case IPU3_IPA_ACTION_SET_SENSOR_CONTROLS: {
> > > +		const ControlList &controls = action.controls[0];
> > > +		delayedCtrls_->push(controls);
> > > +		break;
> > > +	}
> > > +	default:
> > > +		LOG(IPU3, Error) << "Unknown action " << action.operation;
> > > +		break;
> > > +	}
> > > +}
> > > +
> > 
> > Overall, that's a good starting point
> > Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
> 
> Same,
> 
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> 
> > >  /* -----------------------------------------------------------------------------
> > >   * Buffer Ready slots
> > >   */
> 
> -- 
> Regards,
> 
> Laurent Pinchart

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index a87ce8f3378ba2fe..95f1b75dc8be5d40 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -14,11 +14,14 @@ 
 #include <libcamera/camera.h>
 #include <libcamera/control_ids.h>
 #include <libcamera/formats.h>
+#include <libcamera/ipa/ipu3.h>
 #include <libcamera/request.h>
 #include <libcamera/stream.h>
 
 #include "libcamera/internal/camera_sensor.h"
+#include "libcamera/internal/delayed_controls.h"
 #include "libcamera/internal/device_enumerator.h"
+#include "libcamera/internal/ipa_manager.h"
 #include "libcamera/internal/log.h"
 #include "libcamera/internal/media_device.h"
 #include "libcamera/internal/pipeline_handler.h"
@@ -53,6 +56,8 @@  public:
 	{
 	}
 
+	int loadIPA();
+
 	void imguOutputBufferReady(FrameBuffer *buffer);
 	void cio2BufferReady(FrameBuffer *buffer);
 
@@ -62,6 +67,11 @@  public:
 	Stream outStream_;
 	Stream vfStream_;
 	Stream rawStream_;
+
+	std::unique_ptr<DelayedControls> delayedCtrls_;
+
+private:
+	void actOnIpa(unsigned int id, const IPAOperationData &op);
 };
 
 class IPU3CameraConfiguration : public CameraConfiguration
@@ -590,6 +600,13 @@  int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
 	IPU3CameraData *data = cameraData(camera);
 	CIO2Device *cio2 = &data->cio2_;
 	ImgUDevice *imgu = data->imgu_;
+
+	CameraSensorInfo sensorInfo = {};
+	std::map<unsigned int, IPAStream> streamConfig;
+	std::map<unsigned int, const ControlInfoMap &> entityControls;
+	IPAOperationData ipaConfig;
+	IPAOperationData result = {};
+
 	int ret;
 
 	/* Allocate buffers for internal pipeline usage. */
@@ -597,6 +614,11 @@  int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
 	if (ret)
 		return ret;
 
+	IPAOperationData ipaData = {};
+	ret = data->ipa_->start(ipaData, nullptr);
+	if (ret)
+		goto error;
+
 	/*
 	 * Start the ImgU video devices, buffers will be queued to the
 	 * ImgU output and viewfinder when requests will be queued.
@@ -612,9 +634,40 @@  int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] ControlList *con
 		goto error;
 	}
 
+	/* Inform IPA of stream configuration and sensor controls. */
+	ret = data->cio2_.sensor()->sensorInfo(&sensorInfo);
+	if (ret) {
+		/* \todo Turn to hard failure once sensors info is mandatory. */
+		LOG(IPU3, Warning) << "Camera sensor information not available";
+		sensorInfo = {};
+		ret = 0;
+	}
+
+	streamConfig[0] = {
+		.pixelFormat = data->outStream_.configuration().pixelFormat,
+		.size = data->outStream_.configuration().size,
+	};
+	streamConfig[1] = {
+		.pixelFormat = data->vfStream_.configuration().pixelFormat,
+		.size = data->vfStream_.configuration().size,
+	};
+
+	entityControls.emplace(0, data->cio2_.sensor()->controls());
+
+	data->ipa_->configure(sensorInfo, streamConfig, entityControls,
+			      ipaConfig, &result);
+
+	if ((result.operation != IPU3_IPA_STATUS_CONFIGURATION) ||
+	    (result.data.size() != 1) || (result.data.at(0) != 1)) {
+		LOG(IPU3, Warning) << "Failed to configure IPA";
+		ret = -EINVAL;
+		goto error;
+	}
+
 	return 0;
 
 error:
+	data->ipa_->stop();
 	freeBuffers(camera);
 	LOG(IPU3, Error) << "Failed to start camera " << camera->id();
 
@@ -631,6 +684,8 @@  void PipelineHandlerIPU3::stop(Camera *camera)
 	if (ret)
 		LOG(IPU3, Warning) << "Failed to stop camera " << camera->id();
 
+	data->ipa_->stop();
+
 	freeBuffers(camera);
 }
 
@@ -762,12 +817,32 @@  int PipelineHandlerIPU3::registerCameras()
 		if (ret)
 			continue;
 
+		ret = data->loadIPA();
+		if (ret)
+			continue;
+
 		/* Initialize the camera properties. */
 		data->properties_ = cio2->sensor()->properties();
 
 		/* Initialze the camera controls. */
 		data->controlInfo_ = IPU3Controls;
 
+		/*
+		 * \todo Read dealy values from the sensor itself or from a
+		 * a sensor database. For now use generic values taken from
+		 * the Raspberry Pi and listed as generic values.
+		 */
+		std::unordered_map<uint32_t, unsigned int> delays = {
+			{ V4L2_CID_ANALOGUE_GAIN, 1 },
+			{ V4L2_CID_EXPOSURE, 2 },
+		};
+
+		data->delayedCtrls_ =
+			std::make_unique<DelayedControls>(cio2->sensor()->device(),
+							  delays);
+		data->cio2_.frameStart().connect(data->delayedCtrls_.get(),
+						 &DelayedControls::applyControls);
+
 		/**
 		 * \todo Dynamically assign ImgU and output devices to each
 		 * stream and camera; as of now, limit support to two cameras
@@ -811,6 +886,34 @@  int PipelineHandlerIPU3::registerCameras()
 	return numCameras ? 0 : -ENODEV;
 }
 
+int IPU3CameraData::loadIPA()
+{
+	ipa_ = IPAManager::createIPA(pipe_, 1, 1);
+	if (!ipa_)
+		return -ENOENT;
+
+	ipa_->queueFrameAction.connect(this, &IPU3CameraData::actOnIpa);
+
+	ipa_->init(IPASettings{});
+
+	return 0;
+}
+
+void IPU3CameraData::actOnIpa([[maybe_unused]] unsigned int id,
+			      const IPAOperationData &action)
+{
+	switch (action.operation) {
+	case IPU3_IPA_ACTION_SET_SENSOR_CONTROLS: {
+		const ControlList &controls = action.controls[0];
+		delayedCtrls_->push(controls);
+		break;
+	}
+	default:
+		LOG(IPU3, Error) << "Unknown action " << action.operation;
+		break;
+	}
+}
+
 /* -----------------------------------------------------------------------------
  * Buffer Ready slots
  */