[libcamera-devel,06/11] pipeline: ipu3: Support sensors using only CIO2
diff mbox series

Message ID 20230318234014.29506-7-dan.scally@ideasonboard.com
State New
Headers show
Series
  • Support OV7251 in IPU3 pipeline and qcam
Related show

Commit Message

Dan Scally March 18, 2023, 11:40 p.m. UTC
Some sensors that need to be supported by the IPU3 pipeline are not
supported by the Imgu's kernel driver, but can be used by simply
treating the CIO2's capture device as the source of image data. In
that case much of the processing in the IPU3 pipeline needs to be
disabled. Guard it behind checks that query the CIO2Device::needsImgu
field.

Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
---
 src/libcamera/pipeline/ipu3/ipu3.cpp | 214 +++++++++++++++------------
 1 file changed, 118 insertions(+), 96 deletions(-)

Comments

Laurent Pinchart April 19, 2023, 5:55 a.m. UTC | #1
Hi Dan,

Thank you for the patch.

On Sat, Mar 18, 2023 at 11:40:09PM +0000, Daniel Scally via libcamera-devel wrote:
> Some sensors that need to be supported by the IPU3 pipeline are not
> supported by the Imgu's kernel driver, but can be used by simply
> treating the CIO2's capture device as the source of image data. In
> that case much of the processing in the IPU3 pipeline needs to be
> disabled. Guard it behind checks that query the CIO2Device::needsImgu
> field.

Conceptually speaking, I don't think this should be gated by
CIO2Device::needsImgu(). It's not really the CIO2 itself that needs the
ImgU. Could we store that flag in the IPU3CameraData class instead ?
Moving the code that checks if the sensor format is compatible with the
ImgU to the IPU3CameraData class would also be nice. All decisions
related to whether or not to use the ImgU in the pipeline for a camera
would then be done in the top-level pipeline handler code instead of the
individual components. I would also then rename the needsImgU field to
useImgU.

> Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
> ---
>  src/libcamera/pipeline/ipu3/ipu3.cpp | 214 +++++++++++++++------------
>  1 file changed, 118 insertions(+), 96 deletions(-)
> 
> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> index 3077c6bb..cc7da299 100644
> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> @@ -489,6 +489,34 @@ int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
>  	V4L2DeviceFormat outputFormat;
>  	int ret;
>  
> +	/*
> +	 * Pass the requested stream size to the CIO2 unit and get back the
> +	 * adjusted format to be propagated to the ImgU output devices.
> +	 */
> +	const Size &sensorSize = config->cio2Format().size;
> +	V4L2DeviceFormat cio2Format;
> +	ret = cio2->configure(sensorSize, config->combinedTransform_, &cio2Format);
> +	if (ret)
> +		return ret;
> +
> +	IPACameraSensorInfo sensorInfo;
> +	cio2->sensor()->sensorInfo(&sensorInfo);
> +	data->cropRegion_ = sensorInfo.analogCrop;
> +
> +	/*
> +	 * If the ImgU gets configured, its driver seems to expect that
> +	 * buffers will be queued to its outputs, as otherwise the next
> +	 * capture session that uses the ImgU fails when queueing
> +	 * buffers to its input.
> +	 *
> +	 * If no ImgU configuration has been computed, it means only a RAW
> +	 * stream has been requested: return here to skip the ImgU configuration
> +	 * part.
> +	 */
> +	ImgUDevice::PipeConfig imguConfig = config->imguConfig();
> +	if (imguConfig.isNull())
> +		return 0;
> +
>  	/*
>  	 * FIXME: enabled links in one ImgU pipe interfere with capture
>  	 * operations on the other one. This can be easily triggered by
> @@ -531,34 +559,6 @@ int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
>  	if (ret)
>  		return ret;
>  
> -	/*
> -	 * Pass the requested stream size to the CIO2 unit and get back the
> -	 * adjusted format to be propagated to the ImgU output devices.
> -	 */
> -	const Size &sensorSize = config->cio2Format().size;
> -	V4L2DeviceFormat cio2Format;
> -	ret = cio2->configure(sensorSize, config->combinedTransform_, &cio2Format);
> -	if (ret)
> -		return ret;
> -
> -	IPACameraSensorInfo sensorInfo;
> -	cio2->sensor()->sensorInfo(&sensorInfo);
> -	data->cropRegion_ = sensorInfo.analogCrop;
> -
> -	/*
> -	 * If the ImgU gets configured, its driver seems to expect that
> -	 * buffers will be queued to its outputs, as otherwise the next
> -	 * capture session that uses the ImgU fails when queueing
> -	 * buffers to its input.
> -	 *
> -	 * If no ImgU configuration has been computed, it means only a RAW
> -	 * stream has been requested: return here to skip the ImgU configuration
> -	 * part.
> -	 */
> -	ImgUDevice::PipeConfig imguConfig = config->imguConfig();
> -	if (imguConfig.isNull())
> -		return 0;
> -
>  	ret = imgu->configure(imguConfig, &cio2Format);
>  	if (ret)
>  		return ret;
> @@ -660,36 +660,39 @@ int PipelineHandlerIPU3::exportFrameBuffers(Camera *camera, Stream *stream,
>  int PipelineHandlerIPU3::allocateBuffers(Camera *camera)
>  {
>  	IPU3CameraData *data = cameraData(camera);
> +	CIO2Device *cio2 = &data->cio2_;
>  	ImgUDevice *imgu = data->imgu_;
>  	unsigned int bufferCount;
>  	int ret;
>  
> -	bufferCount = std::max({
> -		data->outStream_.configuration().bufferCount,
> -		data->vfStream_.configuration().bufferCount,
> -		data->rawStream_.configuration().bufferCount,
> -	});
> +	if (cio2->needsImgu()) {
> +		bufferCount = std::max({
> +			data->outStream_.configuration().bufferCount,
> +			data->vfStream_.configuration().bufferCount,
> +			data->rawStream_.configuration().bufferCount,
> +		});
>  
> -	ret = imgu->allocateBuffers(bufferCount);
> -	if (ret < 0)
> -		return ret;
> +		ret = imgu->allocateBuffers(bufferCount);
> +		if (ret < 0)
> +			return ret;
>  
> -	/* Map buffers to the IPA. */
> -	unsigned int ipaBufferId = 1;
> +		/* Map buffers to the IPA. */
> +		unsigned int ipaBufferId = 1;
>  
> -	for (const std::unique_ptr<FrameBuffer> &buffer : imgu->paramBuffers_) {
> -		buffer->setCookie(ipaBufferId++);
> -		ipaBuffers_.emplace_back(buffer->cookie(), buffer->planes());
> -	}
> +		for (const std::unique_ptr<FrameBuffer> &buffer : imgu->paramBuffers_) {
> +			buffer->setCookie(ipaBufferId++);
> +			ipaBuffers_.emplace_back(buffer->cookie(), buffer->planes());
> +		}
>  
> -	for (const std::unique_ptr<FrameBuffer> &buffer : imgu->statBuffers_) {
> -		buffer->setCookie(ipaBufferId++);
> -		ipaBuffers_.emplace_back(buffer->cookie(), buffer->planes());
> -	}
> +		for (const std::unique_ptr<FrameBuffer> &buffer : imgu->statBuffers_) {
> +			buffer->setCookie(ipaBufferId++);
> +			ipaBuffers_.emplace_back(buffer->cookie(), buffer->planes());
> +		}
>  
> -	data->ipa_->mapBuffers(ipaBuffers_);
> +		data->ipa_->mapBuffers(ipaBuffers_);
> +		data->frameInfos_.init(imgu->paramBuffers_, imgu->statBuffers_);
> +	}
>  
> -	data->frameInfos_.init(imgu->paramBuffers_, imgu->statBuffers_);
>  	data->frameInfos_.bufferAvailable.connect(
>  		data, &IPU3CameraData::queuePendingRequests);
>  
> @@ -699,17 +702,20 @@ int PipelineHandlerIPU3::allocateBuffers(Camera *camera)
>  int PipelineHandlerIPU3::freeBuffers(Camera *camera)
>  {
>  	IPU3CameraData *data = cameraData(camera);
> +	CIO2Device *cio2 = &data->cio2_;
>  
>  	data->frameInfos_.clear();
>  
> -	std::vector<unsigned int> ids;
> -	for (IPABuffer &ipabuf : ipaBuffers_)
> -		ids.push_back(ipabuf.id);
> +	if (cio2->needsImgu()) {
> +		std::vector<unsigned int> ids;
> +		for (IPABuffer &ipabuf : ipaBuffers_)
> +			ids.push_back(ipabuf.id);
>  
> -	data->ipa_->unmapBuffers(ids);
> -	ipaBuffers_.clear();
> +		data->ipa_->unmapBuffers(ids);
> +		data->imgu_->freeBuffers();
>  
> -	data->imgu_->freeBuffers();
> +		ipaBuffers_.clear();
> +	}
>  
>  	return 0;
>  }
> @@ -736,10 +742,6 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] const ControlLis
>  	if (ret)
>  		return ret;
>  
> -	ret = data->ipa_->start();
> -	if (ret)
> -		goto error;
> -
>  	data->delayedCtrls_->reset();
>  
>  	/*
> @@ -750,16 +752,24 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] const ControlLis
>  	if (ret)
>  		goto error;
>  
> -	ret = imgu->start();
> -	if (ret)
> -		goto error;
> +	if (cio2->needsImgu()) {
> +		ret = data->ipa_->start();
> +		if (ret)
> +			goto error;
> +
> +		ret = imgu->start();
> +		if (ret)
> +			goto error;
> +	}
>  
>  	return 0;
>  
>  error:
> -	imgu->stop();
>  	cio2->stop();
> -	data->ipa_->stop();
> +	if (cio2->needsImgu()) {
> +		imgu->stop();
> +		data->ipa_->stop();
> +	}
>  	freeBuffers(camera);
>  	LOG(IPU3, Error) << "Failed to start camera " << camera->id();
>  
> @@ -773,9 +783,11 @@ void PipelineHandlerIPU3::stopDevice(Camera *camera)
>  
>  	data->cancelPendingRequests();
>  
> -	data->ipa_->stop();
> +	if (data->cio2_.needsImgu()) {
> +		data->ipa_->stop();
> +		ret |= data->imgu_->stop();
> +	}
>  
> -	ret |= data->imgu_->stop();
>  	ret |= data->cio2_.stop();
>  	if (ret)
>  		LOG(IPU3, Warning) << "Failed to stop camera " << camera->id();
> @@ -806,7 +818,7 @@ void IPU3CameraData::queuePendingRequests()
>  	while (!pendingRequests_.empty()) {
>  		Request *request = pendingRequests_.front();
>  
> -		IPU3Frames::Info *info = frameInfos_.create(request, true);
> +		IPU3Frames::Info *info = frameInfos_.create(request, cio2_.needsImgu());
>  		if (!info)
>  			break;
>  
> @@ -829,7 +841,8 @@ void IPU3CameraData::queuePendingRequests()
>  
>  		info->rawBuffer = rawBuffer;
>  
> -		ipa_->queueRequest(info->id, request->controls());
> +		if (cio2_.needsImgu())
> +			ipa_->queueRequest(info->id, request->controls());
>  
>  		pendingRequests_.pop();
>  		processingRequests_.push(request);
> @@ -1070,10 +1083,6 @@ int PipelineHandlerIPU3::registerCameras()
>  		if (ret)
>  			continue;
>  
> -		ret = data->loadIPA();
> -		if (ret)
> -			continue;
> -
>  		/* Initialize the camera properties. */
>  		data->properties_ = cio2->sensor()->properties();
>  
> @@ -1104,36 +1113,44 @@ int PipelineHandlerIPU3::registerCameras()
>  					   << cio2->sensor()->id()
>  					   << ". Assume rotation 0";
>  
> -		/**
> -		 * \todo Dynamically assign ImgU and output devices to each
> -		 * stream and camera; as of now, limit support to two cameras
> -		 * only, and assign imgu0 to the first one and imgu1 to the
> -		 * second.
> -		 */
> -		data->imgu_ = numCameras ? &imgu1_ : &imgu0_;
> -
>  		/*
>  		 * Connect video devices' 'bufferReady' signals to their
>  		 * slot to implement the image processing pipeline.
> -		 *
> -		 * Frames produced by the CIO2 unit are passed to the
> -		 * associated ImgU input where they get processed and
> -		 * returned through the ImgU main and secondary outputs.
>  		 */
>  		data->cio2_.bufferReady().connect(data.get(),
> -					&IPU3CameraData::cio2BufferReady);
> +						  &IPU3CameraData::cio2BufferReady);
>  		data->cio2_.bufferAvailable.connect(
>  			data.get(), &IPU3CameraData::queuePendingRequests);
> -		data->imgu_->input_->bufferReady.connect(&data->cio2_,
> -					&CIO2Device::tryReturnBuffer);
> -		data->imgu_->output_->bufferReady.connect(data.get(),
> -					&IPU3CameraData::imguOutputBufferReady);
> -		data->imgu_->viewfinder_->bufferReady.connect(data.get(),
> -					&IPU3CameraData::imguOutputBufferReady);
> -		data->imgu_->param_->bufferReady.connect(data.get(),
> -					&IPU3CameraData::paramBufferReady);
> -		data->imgu_->stat_->bufferReady.connect(data.get(),
> -					&IPU3CameraData::statBufferReady);
> +
> +		if (cio2->needsImgu()) {
> +			ret = data->loadIPA();
> +			if (ret)
> +				continue;
> +
> +			/**
> +			 * \todo Dynamically assign ImgU and output devices to each
> +			 * stream and camera; as of now, limit support to two cameras
> +			 * only, and assign imgu0 to the first one and imgu1 to the
> +			 * second.
> +			 */
> +			data->imgu_ = numCameras ? &imgu1_ : &imgu0_;
> +
> +			/*
> +			* Frames produced by the CIO2 unit are passed to the
> +			* associated ImgU input where they get processed and
> +			* returned through the ImgU main and secondary outputs.
> +			*/
> +			data->imgu_->input_->bufferReady.connect(&data->cio2_,
> +						&CIO2Device::tryReturnBuffer);
> +			data->imgu_->output_->bufferReady.connect(data.get(),
> +						&IPU3CameraData::imguOutputBufferReady);
> +			data->imgu_->viewfinder_->bufferReady.connect(data.get(),
> +						&IPU3CameraData::imguOutputBufferReady);
> +			data->imgu_->param_->bufferReady.connect(data.get(),
> +						&IPU3CameraData::paramBufferReady);
> +			data->imgu_->stat_->bufferReady.connect(data.get(),
> +						&IPU3CameraData::statBufferReady);
> +		}
>  
>  		/* Create and register the Camera instance. */
>  		const std::string &cameraId = cio2->sensor()->id();
> @@ -1330,10 +1347,15 @@ void IPU3CameraData::cio2BufferReady(FrameBuffer *buffer)
>  
>  	info->effectiveSensorControls = delayedCtrls_->get(buffer->metadata().sequence);
>  
> -	if (request->findBuffer(&rawStream_))
> +	if (request->findBuffer(&rawStream_)) {
>  		pipe()->completeBuffer(request, buffer);
>  
> -	ipa_->fillParamsBuffer(info->id, info->paramBuffer->cookie());
> +		if (frameInfos_.tryComplete(info))
> +			pipe()->completeRequest(request);
> +	}
> +
> +	if (cio2_.needsImgu())
> +		ipa_->fillParamsBuffer(info->id, info->paramBuffer->cookie());
>  }
>  
>  void IPU3CameraData::paramBufferReady(FrameBuffer *buffer)

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index 3077c6bb..cc7da299 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -489,6 +489,34 @@  int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
 	V4L2DeviceFormat outputFormat;
 	int ret;
 
+	/*
+	 * Pass the requested stream size to the CIO2 unit and get back the
+	 * adjusted format to be propagated to the ImgU output devices.
+	 */
+	const Size &sensorSize = config->cio2Format().size;
+	V4L2DeviceFormat cio2Format;
+	ret = cio2->configure(sensorSize, config->combinedTransform_, &cio2Format);
+	if (ret)
+		return ret;
+
+	IPACameraSensorInfo sensorInfo;
+	cio2->sensor()->sensorInfo(&sensorInfo);
+	data->cropRegion_ = sensorInfo.analogCrop;
+
+	/*
+	 * If the ImgU gets configured, its driver seems to expect that
+	 * buffers will be queued to its outputs, as otherwise the next
+	 * capture session that uses the ImgU fails when queueing
+	 * buffers to its input.
+	 *
+	 * If no ImgU configuration has been computed, it means only a RAW
+	 * stream has been requested: return here to skip the ImgU configuration
+	 * part.
+	 */
+	ImgUDevice::PipeConfig imguConfig = config->imguConfig();
+	if (imguConfig.isNull())
+		return 0;
+
 	/*
 	 * FIXME: enabled links in one ImgU pipe interfere with capture
 	 * operations on the other one. This can be easily triggered by
@@ -531,34 +559,6 @@  int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
 	if (ret)
 		return ret;
 
-	/*
-	 * Pass the requested stream size to the CIO2 unit and get back the
-	 * adjusted format to be propagated to the ImgU output devices.
-	 */
-	const Size &sensorSize = config->cio2Format().size;
-	V4L2DeviceFormat cio2Format;
-	ret = cio2->configure(sensorSize, config->combinedTransform_, &cio2Format);
-	if (ret)
-		return ret;
-
-	IPACameraSensorInfo sensorInfo;
-	cio2->sensor()->sensorInfo(&sensorInfo);
-	data->cropRegion_ = sensorInfo.analogCrop;
-
-	/*
-	 * If the ImgU gets configured, its driver seems to expect that
-	 * buffers will be queued to its outputs, as otherwise the next
-	 * capture session that uses the ImgU fails when queueing
-	 * buffers to its input.
-	 *
-	 * If no ImgU configuration has been computed, it means only a RAW
-	 * stream has been requested: return here to skip the ImgU configuration
-	 * part.
-	 */
-	ImgUDevice::PipeConfig imguConfig = config->imguConfig();
-	if (imguConfig.isNull())
-		return 0;
-
 	ret = imgu->configure(imguConfig, &cio2Format);
 	if (ret)
 		return ret;
@@ -660,36 +660,39 @@  int PipelineHandlerIPU3::exportFrameBuffers(Camera *camera, Stream *stream,
 int PipelineHandlerIPU3::allocateBuffers(Camera *camera)
 {
 	IPU3CameraData *data = cameraData(camera);
+	CIO2Device *cio2 = &data->cio2_;
 	ImgUDevice *imgu = data->imgu_;
 	unsigned int bufferCount;
 	int ret;
 
-	bufferCount = std::max({
-		data->outStream_.configuration().bufferCount,
-		data->vfStream_.configuration().bufferCount,
-		data->rawStream_.configuration().bufferCount,
-	});
+	if (cio2->needsImgu()) {
+		bufferCount = std::max({
+			data->outStream_.configuration().bufferCount,
+			data->vfStream_.configuration().bufferCount,
+			data->rawStream_.configuration().bufferCount,
+		});
 
-	ret = imgu->allocateBuffers(bufferCount);
-	if (ret < 0)
-		return ret;
+		ret = imgu->allocateBuffers(bufferCount);
+		if (ret < 0)
+			return ret;
 
-	/* Map buffers to the IPA. */
-	unsigned int ipaBufferId = 1;
+		/* Map buffers to the IPA. */
+		unsigned int ipaBufferId = 1;
 
-	for (const std::unique_ptr<FrameBuffer> &buffer : imgu->paramBuffers_) {
-		buffer->setCookie(ipaBufferId++);
-		ipaBuffers_.emplace_back(buffer->cookie(), buffer->planes());
-	}
+		for (const std::unique_ptr<FrameBuffer> &buffer : imgu->paramBuffers_) {
+			buffer->setCookie(ipaBufferId++);
+			ipaBuffers_.emplace_back(buffer->cookie(), buffer->planes());
+		}
 
-	for (const std::unique_ptr<FrameBuffer> &buffer : imgu->statBuffers_) {
-		buffer->setCookie(ipaBufferId++);
-		ipaBuffers_.emplace_back(buffer->cookie(), buffer->planes());
-	}
+		for (const std::unique_ptr<FrameBuffer> &buffer : imgu->statBuffers_) {
+			buffer->setCookie(ipaBufferId++);
+			ipaBuffers_.emplace_back(buffer->cookie(), buffer->planes());
+		}
 
-	data->ipa_->mapBuffers(ipaBuffers_);
+		data->ipa_->mapBuffers(ipaBuffers_);
+		data->frameInfos_.init(imgu->paramBuffers_, imgu->statBuffers_);
+	}
 
-	data->frameInfos_.init(imgu->paramBuffers_, imgu->statBuffers_);
 	data->frameInfos_.bufferAvailable.connect(
 		data, &IPU3CameraData::queuePendingRequests);
 
@@ -699,17 +702,20 @@  int PipelineHandlerIPU3::allocateBuffers(Camera *camera)
 int PipelineHandlerIPU3::freeBuffers(Camera *camera)
 {
 	IPU3CameraData *data = cameraData(camera);
+	CIO2Device *cio2 = &data->cio2_;
 
 	data->frameInfos_.clear();
 
-	std::vector<unsigned int> ids;
-	for (IPABuffer &ipabuf : ipaBuffers_)
-		ids.push_back(ipabuf.id);
+	if (cio2->needsImgu()) {
+		std::vector<unsigned int> ids;
+		for (IPABuffer &ipabuf : ipaBuffers_)
+			ids.push_back(ipabuf.id);
 
-	data->ipa_->unmapBuffers(ids);
-	ipaBuffers_.clear();
+		data->ipa_->unmapBuffers(ids);
+		data->imgu_->freeBuffers();
 
-	data->imgu_->freeBuffers();
+		ipaBuffers_.clear();
+	}
 
 	return 0;
 }
@@ -736,10 +742,6 @@  int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] const ControlLis
 	if (ret)
 		return ret;
 
-	ret = data->ipa_->start();
-	if (ret)
-		goto error;
-
 	data->delayedCtrls_->reset();
 
 	/*
@@ -750,16 +752,24 @@  int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] const ControlLis
 	if (ret)
 		goto error;
 
-	ret = imgu->start();
-	if (ret)
-		goto error;
+	if (cio2->needsImgu()) {
+		ret = data->ipa_->start();
+		if (ret)
+			goto error;
+
+		ret = imgu->start();
+		if (ret)
+			goto error;
+	}
 
 	return 0;
 
 error:
-	imgu->stop();
 	cio2->stop();
-	data->ipa_->stop();
+	if (cio2->needsImgu()) {
+		imgu->stop();
+		data->ipa_->stop();
+	}
 	freeBuffers(camera);
 	LOG(IPU3, Error) << "Failed to start camera " << camera->id();
 
@@ -773,9 +783,11 @@  void PipelineHandlerIPU3::stopDevice(Camera *camera)
 
 	data->cancelPendingRequests();
 
-	data->ipa_->stop();
+	if (data->cio2_.needsImgu()) {
+		data->ipa_->stop();
+		ret |= data->imgu_->stop();
+	}
 
-	ret |= data->imgu_->stop();
 	ret |= data->cio2_.stop();
 	if (ret)
 		LOG(IPU3, Warning) << "Failed to stop camera " << camera->id();
@@ -806,7 +818,7 @@  void IPU3CameraData::queuePendingRequests()
 	while (!pendingRequests_.empty()) {
 		Request *request = pendingRequests_.front();
 
-		IPU3Frames::Info *info = frameInfos_.create(request, true);
+		IPU3Frames::Info *info = frameInfos_.create(request, cio2_.needsImgu());
 		if (!info)
 			break;
 
@@ -829,7 +841,8 @@  void IPU3CameraData::queuePendingRequests()
 
 		info->rawBuffer = rawBuffer;
 
-		ipa_->queueRequest(info->id, request->controls());
+		if (cio2_.needsImgu())
+			ipa_->queueRequest(info->id, request->controls());
 
 		pendingRequests_.pop();
 		processingRequests_.push(request);
@@ -1070,10 +1083,6 @@  int PipelineHandlerIPU3::registerCameras()
 		if (ret)
 			continue;
 
-		ret = data->loadIPA();
-		if (ret)
-			continue;
-
 		/* Initialize the camera properties. */
 		data->properties_ = cio2->sensor()->properties();
 
@@ -1104,36 +1113,44 @@  int PipelineHandlerIPU3::registerCameras()
 					   << cio2->sensor()->id()
 					   << ". Assume rotation 0";
 
-		/**
-		 * \todo Dynamically assign ImgU and output devices to each
-		 * stream and camera; as of now, limit support to two cameras
-		 * only, and assign imgu0 to the first one and imgu1 to the
-		 * second.
-		 */
-		data->imgu_ = numCameras ? &imgu1_ : &imgu0_;
-
 		/*
 		 * Connect video devices' 'bufferReady' signals to their
 		 * slot to implement the image processing pipeline.
-		 *
-		 * Frames produced by the CIO2 unit are passed to the
-		 * associated ImgU input where they get processed and
-		 * returned through the ImgU main and secondary outputs.
 		 */
 		data->cio2_.bufferReady().connect(data.get(),
-					&IPU3CameraData::cio2BufferReady);
+						  &IPU3CameraData::cio2BufferReady);
 		data->cio2_.bufferAvailable.connect(
 			data.get(), &IPU3CameraData::queuePendingRequests);
-		data->imgu_->input_->bufferReady.connect(&data->cio2_,
-					&CIO2Device::tryReturnBuffer);
-		data->imgu_->output_->bufferReady.connect(data.get(),
-					&IPU3CameraData::imguOutputBufferReady);
-		data->imgu_->viewfinder_->bufferReady.connect(data.get(),
-					&IPU3CameraData::imguOutputBufferReady);
-		data->imgu_->param_->bufferReady.connect(data.get(),
-					&IPU3CameraData::paramBufferReady);
-		data->imgu_->stat_->bufferReady.connect(data.get(),
-					&IPU3CameraData::statBufferReady);
+
+		if (cio2->needsImgu()) {
+			ret = data->loadIPA();
+			if (ret)
+				continue;
+
+			/**
+			 * \todo Dynamically assign ImgU and output devices to each
+			 * stream and camera; as of now, limit support to two cameras
+			 * only, and assign imgu0 to the first one and imgu1 to the
+			 * second.
+			 */
+			data->imgu_ = numCameras ? &imgu1_ : &imgu0_;
+
+			/*
+			* Frames produced by the CIO2 unit are passed to the
+			* associated ImgU input where they get processed and
+			* returned through the ImgU main and secondary outputs.
+			*/
+			data->imgu_->input_->bufferReady.connect(&data->cio2_,
+						&CIO2Device::tryReturnBuffer);
+			data->imgu_->output_->bufferReady.connect(data.get(),
+						&IPU3CameraData::imguOutputBufferReady);
+			data->imgu_->viewfinder_->bufferReady.connect(data.get(),
+						&IPU3CameraData::imguOutputBufferReady);
+			data->imgu_->param_->bufferReady.connect(data.get(),
+						&IPU3CameraData::paramBufferReady);
+			data->imgu_->stat_->bufferReady.connect(data.get(),
+						&IPU3CameraData::statBufferReady);
+		}
 
 		/* Create and register the Camera instance. */
 		const std::string &cameraId = cio2->sensor()->id();
@@ -1330,10 +1347,15 @@  void IPU3CameraData::cio2BufferReady(FrameBuffer *buffer)
 
 	info->effectiveSensorControls = delayedCtrls_->get(buffer->metadata().sequence);
 
-	if (request->findBuffer(&rawStream_))
+	if (request->findBuffer(&rawStream_)) {
 		pipe()->completeBuffer(request, buffer);
 
-	ipa_->fillParamsBuffer(info->id, info->paramBuffer->cookie());
+		if (frameInfos_.tryComplete(info))
+			pipe()->completeRequest(request);
+	}
+
+	if (cio2_.needsImgu())
+		ipa_->fillParamsBuffer(info->id, info->paramBuffer->cookie());
 }
 
 void IPU3CameraData::paramBufferReady(FrameBuffer *buffer)