[libcamera-devel,v4,19/31] libcamera: ipu3: Configure 'output' and 'viewfinder'

Message ID 20190320163055.22056-20-jacopo@jmondi.org
State Superseded
Headers show
Series
  • libcamera: ipu3: Add ImgU support + multiple streams
Related show

Commit Message

Jacopo Mondi March 20, 2019, 4:30 p.m. UTC
Re-work stream configuration to handle the two video streams 'output'
and 'viewfinder' separately.

As the IPU3 driver requires viewfinder and stat video nodes to be
operated not to stall ImgU processing, keep track of which streams have
been requested by the application, and configure 'output', 'viewfinder'
and 'stat' regardless of the user requests.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
---
 src/libcamera/pipeline/ipu3/ipu3.cpp | 259 ++++++++++++++++++++++-----
 1 file changed, 216 insertions(+), 43 deletions(-)

Patch

diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index 44e5eb48549e..4ed8c1fec1e3 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -140,6 +140,7 @@  private:
 		ImgUDevice *imgu;
 
 		Stream streams_[IPU3_STREAMS_COUNT];
+		unsigned int activeStreamsMask;
 	};
 
 	IPU3CameraData *cameraData(const Camera *camera)
@@ -148,6 +149,44 @@  private:
 			PipelineHandler::cameraData(camera));
 	}
 
+	bool isOutput(IPU3CameraData *data, Stream *stream)
+	{
+		return &data->streams_[IPU3_STREAM_OUTPUT] == stream;
+	}
+	bool isOutputActive(IPU3CameraData *data)
+	{
+		return (data->activeStreamsMask & (1 << IPU3_STREAM_OUTPUT)) ?
+			true : false;
+	}
+	void setOutputActive(IPU3CameraData *data)
+	{
+		data->activeStreamsMask |= (1 << IPU3_STREAM_OUTPUT);
+	}
+	bool isViewfinder(IPU3CameraData *data, Stream *stream)
+	{
+		return &data->streams_[IPU3_STREAM_VF] == stream;
+	}
+	bool isViewfinderActive(IPU3CameraData *data)
+	{
+		return (data->activeStreamsMask & (1 << IPU3_STREAM_VF)) ?
+			true : false;
+	}
+	void setViewfinderActive(IPU3CameraData *data)
+	{
+		data->activeStreamsMask |= (1 << IPU3_STREAM_VF);
+	}
+	bool isStreamActive(IPU3CameraData *data, Stream *stream)
+	{
+		if (isOutput(data, stream) &&
+		    isOutputActive(data))
+			return true;
+		if (isViewfinder(data, stream) &&
+		    isViewfinderActive(data))
+			return true;
+
+		return false;
+	}
+
 	int mediaBusToCIO2Format(unsigned int code);
 	V4L2Device *openDevice(MediaDevice *media, const std::string &name);
 	V4L2Subdevice *openSubdevice(MediaDevice *media,
@@ -158,9 +197,17 @@  private:
 
 	int initImgU(ImgUDevice *imgu);
 
-	int setImguFormat(ImgUDevice *imguDevice,
-			  const StreamConfiguration &config,
-			  Rectangle *rect);
+	int setInputFormat(ImgUDevice *imguDevice,
+			   const StreamConfiguration &config,
+			   Rectangle *rect);
+	int setOutputFormat(ImgUDevice *imguDevice,
+			    V4L2Device *output,
+			    const StreamConfiguration &config);
+	int setViewfinderFormat(ImgUDevice *imguDevice,
+				V4L2Device *Viewfinder,
+				const StreamConfiguration &config);
+	int setStatFormat(ImgUDevice *imguDevice,
+			  const StreamConfiguration &config);
 	int setCIO2Format(CIO2Device *cio2Device,
 			  const StreamConfiguration &config,
 			  V4L2SubdeviceFormat *format);
@@ -251,32 +298,66 @@  PipelineHandlerIPU3::streamConfiguration(Camera *camera,
 }
 
 int PipelineHandlerIPU3::configureStreams(Camera *camera,
-					  std::map<Stream *, StreamConfiguration> &config)
+					  std::map<Stream *,
+						   StreamConfiguration> &config)
 {
 	IPU3CameraData *data = cameraData(camera);
-	const StreamConfiguration &cfg = config[&data->streams_[0]];
 	V4L2Device *viewfinder = data->imgu->viewfinder;
 	V4L2Device *output = data->imgu->output;
 	V4L2Device *input = data->imgu->input;
 	V4L2Device *cio2 = data->cio2.output;
+	StreamConfiguration CIO2Config = {};
 	int ret;
 
-	LOG(IPU3, Info)
-		<< "Requested image format: " << cfg.width << "x"
-		<< cfg.height << " - " << std::hex << std::setw(8)
-		<< cfg.pixelFormat << " on camera:'" << camera->name() << "'";
+	/* Remove previously configured stream masks to store the new ones. */
+	data->activeStreamsMask = 0;
 
-	/*
-	 * Verify that the requested size respects the IPU3 alignement
-	 * requirements: the image width shall be a multiple of 8 pixels and
-	 * its height a multiple of 4 pixels.
-	 *
-	 * \todo: consider the BDS scaling factor requirements:
-	 * "the downscaling factor must be an integer value multiple of 1/32"
-	 */
-	if (cfg.width % 8 || cfg.height % 4) {
-		LOG(IPU3, Error) << "Stream format not support: bad alignement";
-		return -EINVAL;
+	for (auto const &streamConfig : config) {
+		Stream *stream = streamConfig.first;
+		const StreamConfiguration &cfg = streamConfig.second;
+
+		/*
+		 * Verify that the requested size respects the IPU3 alignement
+		 * requirements: the image width shall be a multiple of 8 pixels
+		 * and its height a multiple of 4 pixels.
+		 *
+		 * \todo: consider the BDS scaling factor requirements: "the
+		 * downscaling factor must be an integer value multiple of 1/32"
+		 */
+		if (cfg.width % 8 || cfg.height % 4) {
+			LOG(IPU3, Error)
+				<< "Stream format not support: bad alignement";
+			return -EINVAL;
+		}
+
+		LOG(IPU3, Info)
+			<< "Requested image format: " << cfg.width << "x"
+			<< cfg.height << " - " << std::hex << std::setw(8)
+			<< cfg.pixelFormat << " on camera:'" << camera->name()
+			<< "'";
+
+		/*
+		 * FIXME: As viewfinder should be operated even when
+		 * applications do not intend to use it, we need to keep track
+		 * of which streams have to be configured, to make meaningful
+		 * decisions at configure and request queueing time.
+		 *
+		 * Walk here all the streams to configure and collect the
+		 * active ones in a bitmaks.
+		 */
+		if (isOutput(data, stream))
+			setOutputActive(data);
+		if (isViewfinder(data, stream))
+			setViewfinderActive(data);
+
+		/*
+		 * Collect the maximum width and height: IPU3 can downscale
+		 * only.
+		 */
+		if (cfg.width > CIO2Config.width)
+			CIO2Config.width = cfg.width;
+		if (cfg.height > CIO2Config.height)
+			CIO2Config.height = cfg.height;
 	}
 
 	/*
@@ -293,7 +374,7 @@  int PipelineHandlerIPU3::configureStreams(Camera *camera,
 	 * input.
 	 */
 	V4L2SubdeviceFormat sensorFormat = {};
-	ret = setCIO2Format(&data->cio2, cfg, &sensorFormat);
+	ret = setCIO2Format(&data->cio2, CIO2Config, &sensorFormat);
 	if (ret)
 		return ret;
 
@@ -307,41 +388,64 @@  int PipelineHandlerIPU3::configureStreams(Camera *camera,
 	if (ret)
 		return ret;
 
-	LOG(IPU3, Debug)
-		<< "CIO2 output format = " << cio2Format.toString();
+	LOG(IPU3, Debug) << "CIO2 output format = " << cio2Format.toString();
 
 	ret = input->setFormat(&cio2Format);
 	if (ret)
 		return ret;
 
-	/* Apply pad formats and crop/compose rectangle to the ImgU. */
+	/* Apply pad formats and crop/compose rectangle to the ImgU input. */
 	Rectangle rect = {
 		.x = 0,
 		.y = 0,
 		.w = cio2Format.width,
 		.h = cio2Format.height,
 	};
-	ret = setImguFormat(data->imgu, cfg, &rect);
+	ret = setInputFormat(data->imgu, CIO2Config, &rect);
 	if (ret)
 		return ret;
 
-	/* Apply the format to the ImgU output and viewfinder devices. */
-	V4L2DeviceFormat outputFormat = {};
-	outputFormat.width = cfg.width;
-	outputFormat.height = cfg.height;
-	outputFormat.fourcc = V4L2_PIX_FMT_NV12;
-	outputFormat.planesCount = 2;
+	for (auto const &streamConfig : config) {
+		Stream *stream = streamConfig.first;
+		const StreamConfiguration &cfg = streamConfig.second;
 
-	ret = output->setFormat(&outputFormat);
-	if (ret)
-		return ret;
+		if (isOutput(data, stream)) {
+			ret = setOutputFormat(data->imgu, output, cfg);
+			if (ret)
+				return ret;
 
-	LOG(IPU3, Debug)
-		<< "ImgU output format = " << outputFormat.toString();
+			ret = setStatFormat(data->imgu, cfg);
+			if (ret)
+				return ret;
 
-	ret = viewfinder->setFormat(&outputFormat);
-	if (ret)
-		return ret;
+			/*
+			 * FIXME: even if viewfinder is not in use, we need to
+			 * configure and operate it. Use the same size used
+			 * for main output.
+			 */
+			if (!isViewfinderActive(data)) {
+				ret = setViewfinderFormat(data->imgu,
+							  viewfinder, cfg);
+				if (ret)
+					return ret;
+			}
+		} else if (isViewfinder(data, stream)) {
+			ret = setViewfinderFormat(data->imgu, viewfinder, cfg);
+			if (ret)
+				return ret;
+
+			/* Operating the main output is mandatory. */
+			if (!isOutputActive(data)) {
+				ret = setOutputFormat(data->imgu, output, cfg);
+				if (ret)
+					return ret;
+
+				ret = setStatFormat(data->imgu, cfg);
+				if (ret)
+					return ret;
+			}
+		}
+	}
 
 	return 0;
 }
@@ -974,9 +1078,9 @@  void PipelineHandlerIPU3::deleteCIO2(CIO2Device *cio2)
 	delete cio2->sensor;
 }
 
-int PipelineHandlerIPU3::setImguFormat(ImgUDevice *imguDevice,
-				       const StreamConfiguration &config,
-				       Rectangle *rect)
+int PipelineHandlerIPU3::setInputFormat(ImgUDevice *imguDevice,
+					const StreamConfiguration &config,
+					Rectangle *rect)
 {
 	V4L2Subdevice *imgu = imguDevice->imgu;
 	int ret;
@@ -1012,17 +1116,86 @@  int PipelineHandlerIPU3::setImguFormat(ImgUDevice *imguDevice,
 	if (ret)
 		return ret;
 
+	LOG(IPU3, Debug) << "ImgU GDC format = " << imguFormat.toString();
+
+	return 0;
+}
+
+int PipelineHandlerIPU3::setOutputFormat(ImgUDevice *imguDevice,
+					 V4L2Device *output,
+					 const StreamConfiguration &config)
+{
+	V4L2Subdevice *imgu = imguDevice->imgu;
+	int ret;
+
+	V4L2SubdeviceFormat imguFormat = {};
+	imguFormat.width = config.width;
+	imguFormat.height = config.height;
+	imguFormat.mbus_code = MEDIA_BUS_FMT_FIXED;
+
 	ret = imgu->setFormat(ImgUDevice::PAD_OUTPUT, &imguFormat);
 	if (ret)
 		return ret;
 
+	V4L2DeviceFormat outputFormat = {};
+	outputFormat.width = config.width;
+	outputFormat.height = config.height;
+	outputFormat.fourcc = V4L2_PIX_FMT_NV12;
+	outputFormat.planesCount = 2;
+
+	ret = output->setFormat(&outputFormat);
+	if (ret)
+		return ret;
+
 	LOG(IPU3, Debug)
-		<< "ImgU GDC format = " << imguFormat.toString();
+		<< "ImgU output format = " << outputFormat.toString();
+
+	return 0;
+}
+
+int PipelineHandlerIPU3::setViewfinderFormat(ImgUDevice *imguDevice,
+					     V4L2Device *viewfinder,
+					     const StreamConfiguration &config)
+{
+	V4L2Subdevice *imgu = imguDevice->imgu;
+	int ret;
+
+	V4L2SubdeviceFormat imguFormat = {};
+	imguFormat.width = config.width;
+	imguFormat.height = config.height;
+	imguFormat.mbus_code = MEDIA_BUS_FMT_FIXED;
 
 	ret = imgu->setFormat(ImgUDevice::PAD_VF, &imguFormat);
 	if (ret)
 		return ret;
 
+	V4L2DeviceFormat viewfinderFormat = {};
+	viewfinderFormat.width = config.width;
+	viewfinderFormat.height = config.height;
+	viewfinderFormat.fourcc = V4L2_PIX_FMT_NV12;
+	viewfinderFormat.planesCount = 2;
+
+	ret = viewfinder->setFormat(&viewfinderFormat);
+	if (ret)
+		return ret;
+
+	LOG(IPU3, Debug)
+		<< "ImgU viewfinder format = " << viewfinderFormat.toString();
+
+	return 0;
+}
+
+int PipelineHandlerIPU3::setStatFormat(ImgUDevice *imguDevice,
+				       const StreamConfiguration &config)
+{
+	V4L2Subdevice *imgu = imguDevice->imgu;
+	int ret;
+
+	V4L2SubdeviceFormat imguFormat = {};
+	imguFormat.width = config.width;
+	imguFormat.height = config.height;
+	imguFormat.mbus_code = MEDIA_BUS_FMT_FIXED;
+
 	ret = imgu->setFormat(ImgUDevice::PAD_STAT, &imguFormat);
 	if (ret)
 		return ret;