[libcamera-devel,v3,6/9] ipu3: Configure imgu1 when necessary
diff mbox series

Message ID 20220629103018.4025635-7-chenghaoyang@google.com
State Superseded
Delegated to: Umang Jain
Headers show
Series
  • Use two imgus in ipu3 pipeline handler
Related show

Commit Message

Cheng-Hao Yang June 29, 2022, 10:30 a.m. UTC
This patch configure and setup imgu1 when still capture and other
streams are enabled. If only still capture is enabled, imgu0 is still
being used with IPU3PipeModeVideo (i.e. VideoSnapshot) to ensure 3A is
working.

Still capture stream will be enabled in the following patches.

Signed-off-by: Harvey Yang <chenghaoyang@chromium.org>
---
 src/libcamera/pipeline/ipu3/ipu3.cpp | 173 +++++++++++++++++++++++----
 1 file changed, 149 insertions(+), 24 deletions(-)

Comments

Umang Jain July 27, 2022, 11:10 a.m. UTC | #1
Hi Harvey,

On 6/29/22 16:00, Harvey Yang via libcamera-devel wrote:
> This patch configure and setup imgu1 when still capture and other
> streams are enabled. If only still capture is enabled, imgu0 is still
> being used with IPU3PipeModeVideo (i.e. VideoSnapshot) to ensure 3A is
> working.
>
> Still capture stream will be enabled in the following patches.
>
> Signed-off-by: Harvey Yang <chenghaoyang@chromium.org>
> ---
>   src/libcamera/pipeline/ipu3/ipu3.cpp | 173 +++++++++++++++++++++++----
>   1 file changed, 149 insertions(+), 24 deletions(-)
>
> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> index a13fb881..ec9d14d1 100644
> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> @@ -55,9 +55,11 @@ public:
>   
>   	int loadIPA();
>   
> +	void tryReturnBuffer(FrameBuffer *buffer);
>   	void imguOutputBufferReady(FrameBuffer *buffer);
>   	void cio2BufferReady(FrameBuffer *buffer);
>   	void paramBufferReady(FrameBuffer *buffer);
> +	void captureParamBufferReady(FrameBuffer *buffer);
>   	void statBufferReady(FrameBuffer *buffer);
>   	void queuePendingRequests();
>   	void cancelPendingRequests();
> @@ -93,6 +95,8 @@ private:
>   	void paramsBufferReady(unsigned int id);
>   	void setSensorControls(unsigned int id, const ControlList &sensorControls,
>   			       const ControlList &lensControls);
> +
> +	std::map<FrameBuffer *, int> bufferReturnCounters;

'_' suffix for private?

These are not any buffer return counters but cio2 buffer counters. Hence,

	std::map<FrameBuffer *, int> cio2BufferReturnCounters_;

    would be more appropriate

>   };
>   
>   class IPU3CameraConfiguration : public CameraConfiguration
> @@ -106,7 +110,8 @@ public:
>   	Status validate() override;
>   
>   	const StreamConfiguration &cio2Format() const { return cio2Configuration_; }
> -	const ImgUDevice::PipeConfig imguConfig() const { return pipeConfig_; }
> +	const ImgUDevice::PipeConfig imguConfig0() const { return pipeConfig0_; }
> +	const ImgUDevice::PipeConfig imguConfig1() const { return pipeConfig1_; }
>   
>   	/* Cache the combinedTransform_ that will be applied to the sensor */
>   	Transform combinedTransform_;
> @@ -120,7 +125,8 @@ private:
>   	const IPU3CameraData *data_;
>   
>   	StreamConfiguration cio2Configuration_;
> -	ImgUDevice::PipeConfig pipeConfig_;
> +	ImgUDevice::PipeConfig pipeConfig0_;
> +	ImgUDevice::PipeConfig pipeConfig1_;
>   };
>   
>   class PipelineHandlerIPU3 : public PipelineHandler
> @@ -168,6 +174,8 @@ private:
>   	MediaDevice *cio2MediaDev_;
>   	MediaDevice *imguMediaDev_;
>   
> +	bool useImgu1_ = false;
> +
>   	Camera *inUseCamera_ = nullptr;
>   
>   	std::vector<IPABuffer> ipaBuffers_;
> @@ -408,8 +416,8 @@ CameraConfiguration::Status IPU3CameraConfiguration::validate()
>   
>   	/* Only compute the ImgU configuration if a YUV stream has been requested. */
>   	if (yuvCount) {
> -		pipeConfig_ = data_->imgu0_->calculatePipeConfig(&pipe);
> -		if (pipeConfig_.isNull()) {
> +		pipeConfig0_ = data_->imgu0_->calculatePipeConfig(&pipe);
> +		if (pipeConfig0_.isNull()) {
>   			LOG(IPU3, Error) << "Failed to calculate pipe configuration: "
>   					 << "unsupported resolutions.";
>   			return Invalid;
> @@ -519,8 +527,13 @@ int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
>   	IPU3CameraData *data = cameraData(camera);
>   	Stream *outStream = &data->outStream_;
>   	Stream *vfStream = &data->vfStream_;
> +	Stream *outCaptureStream = &data->outCaptureStream_;
>   	CIO2Device *cio2 = &data->cio2_;
>   	V4L2DeviceFormat outputFormat;
> +
> +	ImgUDevice::PipeConfig imguConfig1 = config->imguConfig1();
> +	useImgu1_ = !imguConfig1.isNull();
> +
>   	int ret;
>   
>   	/*
> @@ -565,6 +578,12 @@ int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
>   	if (ret)
>   		return ret;
>   
> +	if (useImgu1_) {
> +		ret = imgu1_.enableLinks(true);
> +		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.
> @@ -607,14 +626,20 @@ int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
>   	 * stream has been requested: return here to skip the ImgU configuration
>   	 * part.
>   	 */
> -	ImgUDevice::PipeConfig imguConfig = config->imguConfig();
> -	if (imguConfig.isNull())
> +	ImgUDevice::PipeConfig imguConfig0 = config->imguConfig0();
> +	if (imguConfig0.isNull())
>   		return 0;
>   
> -	ret = imgu0_.configure(imguConfig, &cio2Format);
> +	ret = imgu0_.configure(imguConfig0, &cio2Format);
>   	if (ret)
>   		return ret;
>   
> +	if (useImgu1_) {
> +		ret = imgu1_.configure(imguConfig1, &cio2Format);
> +		if (ret)
> +			return ret;
> +	}
> +
>   	/* Apply the format to the configured streams output devices. */
>   	StreamConfiguration *mainCfg = nullptr;
>   	StreamConfiguration *vfCfg = nullptr;
> @@ -633,6 +658,15 @@ int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
>   			ret = imgu0_.configureViewfinder(cfg, &outputFormat);
>   			if (ret)
>   				return ret;
> +		} else if (stream == outCaptureStream) {
> +			ASSERT(useImgu1_);
> +			ret = imgu1_.configureOutput(cfg, &outputFormat);
> +			if (ret)
> +				return ret;
> +
> +			ret = imgu1_.configureViewfinder(cfg, &outputFormat);
> +			if (ret)
> +				return ret;
>   		}
>   	}
>   
> @@ -648,22 +682,34 @@ int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
>   	}
>   
>   	/* Apply the "pipe_mode" control to the ImgU subdevice. */
> -	ControlList ctrls(imgu0_.imgu_->controls());
> +	ControlList ctrls0(imgu0_.imgu_->controls());
>   	/*
>   	 * Set the ImgU pipe mode to 'Video' unconditionally to have statistics
>   	 * generated.
> -	 *
> -	 * \todo Figure out what the 'Still Capture' mode is meant for, and use
> -	 * it accordingly.
>   	 */
> -	ctrls.set(V4L2_CID_IPU3_PIPE_MODE,
> -		  static_cast<int32_t>(IPU3PipeModeVideo));
> -	ret = imgu0_.imgu_->setControls(&ctrls);
> +	ctrls0.set(V4L2_CID_IPU3_PIPE_MODE,
> +		   static_cast<int32_t>(IPU3PipeModeVideo));
> +	ret = imgu0_.imgu_->setControls(&ctrls0);
>   	if (ret) {
>   		LOG(IPU3, Error) << "Unable to set pipe_mode control";

        LOG(IPU3, Error) << "imgu0: Unable to set pipe_mode control";


>   		return ret;
>   	}
>   
> +	if (useImgu1_) {
> +		ControlList ctrls1(imgu1_.imgu_->controls());
> +		/*
> +		 * \todo Figure out what the 'Still Capture' mode is meant for,
> +		 * and use it accordingly.
> +		 */
> +		ctrls1.set(V4L2_CID_IPU3_PIPE_MODE,
> +			   static_cast<int32_t>(IPU3PipeModeStillCapture));
> +		ret = imgu1_.imgu_->setControls(&ctrls1);
> +		if (ret) {
> +			LOG(IPU3, Error) << "Unable to set pipe_mode control";

        LOG(IPU3, Error) << "imgu1: Unable to set pipe_mode control";

> +			return ret;
> +		}
> +	}
> +
>   	ipa::ipu3::IPAConfigInfo configInfo;
>   	configInfo.sensorControls = data->cio2_.sensor()->controls();
>   
> @@ -672,8 +718,8 @@ int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
>   		configInfo.lensControls = lens->controls();
>   
>   	configInfo.sensorInfo = sensorInfo;
> -	configInfo.bdsOutputSize = config->imguConfig().bds;
> -	configInfo.iif = config->imguConfig().iif;
> +	configInfo.bdsOutputSize = config->imguConfig0().bds;
> +	configInfo.iif = config->imguConfig0().iif;
>   
>   	ret = data->ipa_->configure(configInfo, &data->ipaControls_);
>   	if (ret) {
> @@ -793,8 +839,8 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] const ControlLis
>   	CIO2Device *cio2 = &data->cio2_;
>   	int ret;
>   
> -	imgu0_.input_->bufferReady.connect(&data->cio2_,
> -					   &CIO2Device::tryReturnBuffer);
> +	imgu0_.input_->bufferReady.connect(data,
> +					   &IPU3CameraData::tryReturnBuffer);
>   	imgu0_.output_->bufferReady.connect(data,
>   					    &IPU3CameraData::imguOutputBufferReady);
>   	imgu0_.viewfinder_->bufferReady.connect(data,
> @@ -803,6 +849,16 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] const ControlLis
>   					   &IPU3CameraData::paramBufferReady);
>   	imgu0_.stat_->bufferReady.connect(data,
>   					  &IPU3CameraData::statBufferReady);
> +
> +	if (useImgu1_) {
> +		imgu1_.input_->bufferReady.connect(data,
> +				&IPU3CameraData::tryReturnBuffer);
> +		imgu1_.output_->bufferReady.connect(data,
> +				&IPU3CameraData::imguOutputBufferReady);
> +		imgu1_.param_->bufferReady.connect(data,
> +				&IPU3CameraData::captureParamBufferReady);
> +	}
> +
>   	/* Disable test pattern mode on the sensor, if any. */
>   	ret = cio2->sensor()->setTestPatternMode(
>   		controls::draft::TestPatternModeEnum::TestPatternModeOff);
> @@ -834,10 +890,17 @@ int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] const ControlLis
>   	if (ret)
>   		goto error;
>   
> +	if (useImgu1_) {
> +		ret = imgu1_.start();
> +		if (ret)
> +			goto error;
> +	}
> +
>   	return 0;
>   
>   error:
>   	imgu0_.stop();
> +	imgu1_.stop();
>   	cio2->stop();
>   	data->ipa_->stop();
>   	freeBuffers(camera);
> @@ -848,6 +911,11 @@ error:
>   	imgu0_.viewfinder_->bufferReady.disconnect();
>   	imgu0_.param_->bufferReady.disconnect();
>   	imgu0_.stat_->bufferReady.disconnect();
> +	imgu1_.input_->bufferReady.disconnect();
> +	imgu1_.output_->bufferReady.disconnect();
> +	imgu1_.param_->bufferReady.disconnect();
> +
> +	useImgu1_ = false;
>   	inUseCamera_ = nullptr;
>   
>   	return ret;
> @@ -863,6 +931,7 @@ void PipelineHandlerIPU3::stopDevice(Camera *camera)
>   	data->ipa_->stop();
>   
>   	ret |= imgu0_.stop();
> +	ret |= imgu1_.stop();
>   	ret |= data->cio2_.stop();
>   	if (ret)
>   		LOG(IPU3, Warning) << "Failed to stop camera " << camera->id();
> @@ -874,7 +943,11 @@ void PipelineHandlerIPU3::stopDevice(Camera *camera)
>   	data->imgu0_->viewfinder_->bufferReady.disconnect();
>   	data->imgu0_->param_->bufferReady.disconnect();
>   	data->imgu0_->stat_->bufferReady.disconnect();
> +	data->imgu1_->input_->bufferReady.disconnect();
> +	data->imgu1_->output_->bufferReady.disconnect();
> +	data->imgu1_->param_->bufferReady.disconnect();
>   
> +	useImgu1_ = false;


As mentioned in the previous patches, it would be better abstracted 
under IPU3CameraData helper function.

>   	inUseCamera_ = nullptr;
>   }
>   
> @@ -1320,22 +1393,45 @@ void IPU3CameraData::paramsBufferReady(unsigned int id)
>   	if (!info)
>   		return;
>   
> +	bool hasYuv = false;
> +	bool hasCapture = false;
>   	/* Queue all buffers from the request aimed for the ImgU. */
>   	for (auto it : info->request->buffers()) {
>   		const Stream *stream = it.first;
>   		FrameBuffer *outbuffer = it.second;
>   
> -		if (stream == &outStream_)
> +		if (stream == &outStream_) {
> +			hasYuv = true;
>   			imgu0_->output_->queueBuffer(outbuffer);
> -		else if (stream == &vfStream_)
> +		} else if (stream == &vfStream_) {
> +			hasYuv = true;
>   			imgu0_->viewfinder_->queueBuffer(outbuffer);
> +		} else if (stream == &outCaptureStream_) {
> +			hasCapture = true;
> +
> +			imgu1_->output_->queueBuffer(outbuffer);
> +		}


Yes, this was what I was thinking a while ago.

I wonder this fundamental question that if queuing buffers  both to 
output of imgu0 and output of imgu1 makes sense in any case?

Or is it the case that we need to queue buffers to output get imgu0 
streaming, otherwise it'll stall?

>   	}
>   
> -	imgu0_->param_->queueBuffer(info->paramBuffer);
> -	imgu0_->stat_->queueBuffer(info->statBuffer);
> -	imgu0_->input_->queueBuffer(info->rawBuffer);
> +	if (hasYuv) {
> +		bufferReturnCounters[info->rawBuffer] += 1;
>   
> -	info->captureParamDequeued = true;
> +		imgu0_->param_->queueBuffer(info->paramBuffer);
> +		imgu0_->stat_->queueBuffer(info->statBuffer);
> +		imgu0_->input_->queueBuffer(info->rawBuffer);
> +	} else {
> +		info->paramDequeued = true;
> +		info->metadataProcessed = true;
> +	}
> +
> +	if (hasCapture) {
> +		bufferReturnCounters[info->rawBuffer] += 1;
> +
> +		imgu1_->param_->queueBuffer(info->captureParamBuffer);
> +		imgu1_->input_->queueBuffer(info->rawBuffer);
> +	} else {
> +		info->captureParamDequeued = true;
> +	}
>   }
>   
>   void IPU3CameraData::metadataReady(unsigned int id, const ControlList &metadata)
> @@ -1356,6 +1452,15 @@ void IPU3CameraData::metadataReady(unsigned int id, const ControlList &metadata)
>    * Buffer Ready slots
>    */
>   
> +void IPU3CameraData::tryReturnBuffer(FrameBuffer *buffer)
> +{
> +	if (--bufferReturnCounters[buffer] > 0)
> +		return;
> +
> +	bufferReturnCounters.erase(buffer);
> +	cio2_.tryReturnBuffer(buffer);
> +}
> +
>   /**
>    * \brief Handle buffers completion at the ImgU output
>    * \param[in] buffer The completed buffer
> @@ -1448,6 +1553,26 @@ void IPU3CameraData::paramBufferReady(FrameBuffer *buffer)
>   		pipe()->completeRequest(request);
>   }
>   
> +void IPU3CameraData::captureParamBufferReady(FrameBuffer *buffer)
> +{
> +	IPU3Frames::Info *info = frameInfos_.find(buffer);
> +	if (!info)
> +		return;
> +
> +	info->captureParamDequeued = true;
> +
> +	/*
> +	 * tryComplete() will delete info if it completes the IPU3Frame.
> +	 * In that event, we must have obtained the Request before hand.
> +	 *
> +	 * \todo Improve the FrameInfo API to avoid this type of issue
> +	 */
> +	Request *request = info->request;
> +
> +	if (frameInfos_.tryComplete(info))
> +		pipe()->completeRequest(request);
> +}
> +
>   void IPU3CameraData::statBufferReady(FrameBuffer *buffer)
>   {
>   	IPU3Frames::Info *info = frameInfos_.find(buffer);

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index a13fb881..ec9d14d1 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -55,9 +55,11 @@  public:
 
 	int loadIPA();
 
+	void tryReturnBuffer(FrameBuffer *buffer);
 	void imguOutputBufferReady(FrameBuffer *buffer);
 	void cio2BufferReady(FrameBuffer *buffer);
 	void paramBufferReady(FrameBuffer *buffer);
+	void captureParamBufferReady(FrameBuffer *buffer);
 	void statBufferReady(FrameBuffer *buffer);
 	void queuePendingRequests();
 	void cancelPendingRequests();
@@ -93,6 +95,8 @@  private:
 	void paramsBufferReady(unsigned int id);
 	void setSensorControls(unsigned int id, const ControlList &sensorControls,
 			       const ControlList &lensControls);
+
+	std::map<FrameBuffer *, int> bufferReturnCounters;
 };
 
 class IPU3CameraConfiguration : public CameraConfiguration
@@ -106,7 +110,8 @@  public:
 	Status validate() override;
 
 	const StreamConfiguration &cio2Format() const { return cio2Configuration_; }
-	const ImgUDevice::PipeConfig imguConfig() const { return pipeConfig_; }
+	const ImgUDevice::PipeConfig imguConfig0() const { return pipeConfig0_; }
+	const ImgUDevice::PipeConfig imguConfig1() const { return pipeConfig1_; }
 
 	/* Cache the combinedTransform_ that will be applied to the sensor */
 	Transform combinedTransform_;
@@ -120,7 +125,8 @@  private:
 	const IPU3CameraData *data_;
 
 	StreamConfiguration cio2Configuration_;
-	ImgUDevice::PipeConfig pipeConfig_;
+	ImgUDevice::PipeConfig pipeConfig0_;
+	ImgUDevice::PipeConfig pipeConfig1_;
 };
 
 class PipelineHandlerIPU3 : public PipelineHandler
@@ -168,6 +174,8 @@  private:
 	MediaDevice *cio2MediaDev_;
 	MediaDevice *imguMediaDev_;
 
+	bool useImgu1_ = false;
+
 	Camera *inUseCamera_ = nullptr;
 
 	std::vector<IPABuffer> ipaBuffers_;
@@ -408,8 +416,8 @@  CameraConfiguration::Status IPU3CameraConfiguration::validate()
 
 	/* Only compute the ImgU configuration if a YUV stream has been requested. */
 	if (yuvCount) {
-		pipeConfig_ = data_->imgu0_->calculatePipeConfig(&pipe);
-		if (pipeConfig_.isNull()) {
+		pipeConfig0_ = data_->imgu0_->calculatePipeConfig(&pipe);
+		if (pipeConfig0_.isNull()) {
 			LOG(IPU3, Error) << "Failed to calculate pipe configuration: "
 					 << "unsupported resolutions.";
 			return Invalid;
@@ -519,8 +527,13 @@  int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
 	IPU3CameraData *data = cameraData(camera);
 	Stream *outStream = &data->outStream_;
 	Stream *vfStream = &data->vfStream_;
+	Stream *outCaptureStream = &data->outCaptureStream_;
 	CIO2Device *cio2 = &data->cio2_;
 	V4L2DeviceFormat outputFormat;
+
+	ImgUDevice::PipeConfig imguConfig1 = config->imguConfig1();
+	useImgu1_ = !imguConfig1.isNull();
+
 	int ret;
 
 	/*
@@ -565,6 +578,12 @@  int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
 	if (ret)
 		return ret;
 
+	if (useImgu1_) {
+		ret = imgu1_.enableLinks(true);
+		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.
@@ -607,14 +626,20 @@  int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
 	 * stream has been requested: return here to skip the ImgU configuration
 	 * part.
 	 */
-	ImgUDevice::PipeConfig imguConfig = config->imguConfig();
-	if (imguConfig.isNull())
+	ImgUDevice::PipeConfig imguConfig0 = config->imguConfig0();
+	if (imguConfig0.isNull())
 		return 0;
 
-	ret = imgu0_.configure(imguConfig, &cio2Format);
+	ret = imgu0_.configure(imguConfig0, &cio2Format);
 	if (ret)
 		return ret;
 
+	if (useImgu1_) {
+		ret = imgu1_.configure(imguConfig1, &cio2Format);
+		if (ret)
+			return ret;
+	}
+
 	/* Apply the format to the configured streams output devices. */
 	StreamConfiguration *mainCfg = nullptr;
 	StreamConfiguration *vfCfg = nullptr;
@@ -633,6 +658,15 @@  int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
 			ret = imgu0_.configureViewfinder(cfg, &outputFormat);
 			if (ret)
 				return ret;
+		} else if (stream == outCaptureStream) {
+			ASSERT(useImgu1_);
+			ret = imgu1_.configureOutput(cfg, &outputFormat);
+			if (ret)
+				return ret;
+
+			ret = imgu1_.configureViewfinder(cfg, &outputFormat);
+			if (ret)
+				return ret;
 		}
 	}
 
@@ -648,22 +682,34 @@  int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
 	}
 
 	/* Apply the "pipe_mode" control to the ImgU subdevice. */
-	ControlList ctrls(imgu0_.imgu_->controls());
+	ControlList ctrls0(imgu0_.imgu_->controls());
 	/*
 	 * Set the ImgU pipe mode to 'Video' unconditionally to have statistics
 	 * generated.
-	 *
-	 * \todo Figure out what the 'Still Capture' mode is meant for, and use
-	 * it accordingly.
 	 */
-	ctrls.set(V4L2_CID_IPU3_PIPE_MODE,
-		  static_cast<int32_t>(IPU3PipeModeVideo));
-	ret = imgu0_.imgu_->setControls(&ctrls);
+	ctrls0.set(V4L2_CID_IPU3_PIPE_MODE,
+		   static_cast<int32_t>(IPU3PipeModeVideo));
+	ret = imgu0_.imgu_->setControls(&ctrls0);
 	if (ret) {
 		LOG(IPU3, Error) << "Unable to set pipe_mode control";
 		return ret;
 	}
 
+	if (useImgu1_) {
+		ControlList ctrls1(imgu1_.imgu_->controls());
+		/*
+		 * \todo Figure out what the 'Still Capture' mode is meant for,
+		 * and use it accordingly.
+		 */
+		ctrls1.set(V4L2_CID_IPU3_PIPE_MODE,
+			   static_cast<int32_t>(IPU3PipeModeStillCapture));
+		ret = imgu1_.imgu_->setControls(&ctrls1);
+		if (ret) {
+			LOG(IPU3, Error) << "Unable to set pipe_mode control";
+			return ret;
+		}
+	}
+
 	ipa::ipu3::IPAConfigInfo configInfo;
 	configInfo.sensorControls = data->cio2_.sensor()->controls();
 
@@ -672,8 +718,8 @@  int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
 		configInfo.lensControls = lens->controls();
 
 	configInfo.sensorInfo = sensorInfo;
-	configInfo.bdsOutputSize = config->imguConfig().bds;
-	configInfo.iif = config->imguConfig().iif;
+	configInfo.bdsOutputSize = config->imguConfig0().bds;
+	configInfo.iif = config->imguConfig0().iif;
 
 	ret = data->ipa_->configure(configInfo, &data->ipaControls_);
 	if (ret) {
@@ -793,8 +839,8 @@  int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] const ControlLis
 	CIO2Device *cio2 = &data->cio2_;
 	int ret;
 
-	imgu0_.input_->bufferReady.connect(&data->cio2_,
-					   &CIO2Device::tryReturnBuffer);
+	imgu0_.input_->bufferReady.connect(data,
+					   &IPU3CameraData::tryReturnBuffer);
 	imgu0_.output_->bufferReady.connect(data,
 					    &IPU3CameraData::imguOutputBufferReady);
 	imgu0_.viewfinder_->bufferReady.connect(data,
@@ -803,6 +849,16 @@  int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] const ControlLis
 					   &IPU3CameraData::paramBufferReady);
 	imgu0_.stat_->bufferReady.connect(data,
 					  &IPU3CameraData::statBufferReady);
+
+	if (useImgu1_) {
+		imgu1_.input_->bufferReady.connect(data,
+				&IPU3CameraData::tryReturnBuffer);
+		imgu1_.output_->bufferReady.connect(data,
+				&IPU3CameraData::imguOutputBufferReady);
+		imgu1_.param_->bufferReady.connect(data,
+				&IPU3CameraData::captureParamBufferReady);
+	}
+
 	/* Disable test pattern mode on the sensor, if any. */
 	ret = cio2->sensor()->setTestPatternMode(
 		controls::draft::TestPatternModeEnum::TestPatternModeOff);
@@ -834,10 +890,17 @@  int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] const ControlLis
 	if (ret)
 		goto error;
 
+	if (useImgu1_) {
+		ret = imgu1_.start();
+		if (ret)
+			goto error;
+	}
+
 	return 0;
 
 error:
 	imgu0_.stop();
+	imgu1_.stop();
 	cio2->stop();
 	data->ipa_->stop();
 	freeBuffers(camera);
@@ -848,6 +911,11 @@  error:
 	imgu0_.viewfinder_->bufferReady.disconnect();
 	imgu0_.param_->bufferReady.disconnect();
 	imgu0_.stat_->bufferReady.disconnect();
+	imgu1_.input_->bufferReady.disconnect();
+	imgu1_.output_->bufferReady.disconnect();
+	imgu1_.param_->bufferReady.disconnect();
+
+	useImgu1_ = false;
 	inUseCamera_ = nullptr;
 
 	return ret;
@@ -863,6 +931,7 @@  void PipelineHandlerIPU3::stopDevice(Camera *camera)
 	data->ipa_->stop();
 
 	ret |= imgu0_.stop();
+	ret |= imgu1_.stop();
 	ret |= data->cio2_.stop();
 	if (ret)
 		LOG(IPU3, Warning) << "Failed to stop camera " << camera->id();
@@ -874,7 +943,11 @@  void PipelineHandlerIPU3::stopDevice(Camera *camera)
 	data->imgu0_->viewfinder_->bufferReady.disconnect();
 	data->imgu0_->param_->bufferReady.disconnect();
 	data->imgu0_->stat_->bufferReady.disconnect();
+	data->imgu1_->input_->bufferReady.disconnect();
+	data->imgu1_->output_->bufferReady.disconnect();
+	data->imgu1_->param_->bufferReady.disconnect();
 
+	useImgu1_ = false;
 	inUseCamera_ = nullptr;
 }
 
@@ -1320,22 +1393,45 @@  void IPU3CameraData::paramsBufferReady(unsigned int id)
 	if (!info)
 		return;
 
+	bool hasYuv = false;
+	bool hasCapture = false;
 	/* Queue all buffers from the request aimed for the ImgU. */
 	for (auto it : info->request->buffers()) {
 		const Stream *stream = it.first;
 		FrameBuffer *outbuffer = it.second;
 
-		if (stream == &outStream_)
+		if (stream == &outStream_) {
+			hasYuv = true;
 			imgu0_->output_->queueBuffer(outbuffer);
-		else if (stream == &vfStream_)
+		} else if (stream == &vfStream_) {
+			hasYuv = true;
 			imgu0_->viewfinder_->queueBuffer(outbuffer);
+		} else if (stream == &outCaptureStream_) {
+			hasCapture = true;
+
+			imgu1_->output_->queueBuffer(outbuffer);
+		}
 	}
 
-	imgu0_->param_->queueBuffer(info->paramBuffer);
-	imgu0_->stat_->queueBuffer(info->statBuffer);
-	imgu0_->input_->queueBuffer(info->rawBuffer);
+	if (hasYuv) {
+		bufferReturnCounters[info->rawBuffer] += 1;
 
-	info->captureParamDequeued = true;
+		imgu0_->param_->queueBuffer(info->paramBuffer);
+		imgu0_->stat_->queueBuffer(info->statBuffer);
+		imgu0_->input_->queueBuffer(info->rawBuffer);
+	} else {
+		info->paramDequeued = true;
+		info->metadataProcessed = true;
+	}
+
+	if (hasCapture) {
+		bufferReturnCounters[info->rawBuffer] += 1;
+
+		imgu1_->param_->queueBuffer(info->captureParamBuffer);
+		imgu1_->input_->queueBuffer(info->rawBuffer);
+	} else {
+		info->captureParamDequeued = true;
+	}
 }
 
 void IPU3CameraData::metadataReady(unsigned int id, const ControlList &metadata)
@@ -1356,6 +1452,15 @@  void IPU3CameraData::metadataReady(unsigned int id, const ControlList &metadata)
  * Buffer Ready slots
  */
 
+void IPU3CameraData::tryReturnBuffer(FrameBuffer *buffer)
+{
+	if (--bufferReturnCounters[buffer] > 0)
+		return;
+
+	bufferReturnCounters.erase(buffer);
+	cio2_.tryReturnBuffer(buffer);
+}
+
 /**
  * \brief Handle buffers completion at the ImgU output
  * \param[in] buffer The completed buffer
@@ -1448,6 +1553,26 @@  void IPU3CameraData::paramBufferReady(FrameBuffer *buffer)
 		pipe()->completeRequest(request);
 }
 
+void IPU3CameraData::captureParamBufferReady(FrameBuffer *buffer)
+{
+	IPU3Frames::Info *info = frameInfos_.find(buffer);
+	if (!info)
+		return;
+
+	info->captureParamDequeued = true;
+
+	/*
+	 * tryComplete() will delete info if it completes the IPU3Frame.
+	 * In that event, we must have obtained the Request before hand.
+	 *
+	 * \todo Improve the FrameInfo API to avoid this type of issue
+	 */
+	Request *request = info->request;
+
+	if (frameInfos_.tryComplete(info))
+		pipe()->completeRequest(request);
+}
+
 void IPU3CameraData::statBufferReady(FrameBuffer *buffer)
 {
 	IPU3Frames::Info *info = frameInfos_.find(buffer);