[libcamera-devel,v2,06/14] libcamera: ipu3: Propagate image format

Message ID 20190312121242.2253-7-jacopo@jmondi.org
State Superseded
Headers show
Series
  • libcamera: ipu3: ImgU support
Related show

Commit Message

Jacopo Mondi March 12, 2019, 12:12 p.m. UTC
Apply the requested image format to the sensor device, and apply the
adjusted one to the CIO2 device, the ImgU subdevice and its input and
output video devices.

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

Patch

diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index 009ee341f18c..3502c39f5e31 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -16,6 +16,7 @@ 
 #include <libcamera/stream.h>
 
 #include "device_enumerator.h"
+#include "geometry.h"
 #include "log.h"
 #include "media_device.h"
 #include "pipeline_handler.h"
@@ -29,6 +30,11 @@  LOG_DEFINE_CATEGORY(IPU3)
 
 class ImgUDevice {
 public:
+	static constexpr unsigned int PAD_INPUT = 0;
+	static constexpr unsigned int PAD_OUTPUT = 2;
+	static constexpr unsigned int PAD_VF = 3;
+	static constexpr unsigned int PAD_STAT = 4;
+
 	ImgUDevice()
 		: imgu(nullptr), input(nullptr), output(nullptr),
 		  viewfinder(nullptr), stat(nullptr)
@@ -134,6 +140,13 @@  private:
 
 	int initImgU(ImgUDevice *imgu);
 
+	int setImguFormat(ImgUDevice *imguDevice,
+			  const StreamConfiguration &config,
+			  Rectangle *rect);
+	int setCIO2Format(CIO2Device *cio2Device,
+			  const StreamConfiguration &config,
+			  V4L2SubdeviceFormat *format);
+
 	void registerCameras();
 
 	ImgUDevice imgus_[IPU3_IMGU_COUNT];
@@ -206,60 +219,86 @@  int PipelineHandlerIPU3::configureStreams(Camera *camera,
 					  std::map<Stream *, StreamConfiguration> &config)
 {
 	IPU3CameraData *data = cameraData(camera);
-	StreamConfiguration *cfg = &config[&data->stream_];
-	V4L2Subdevice *sensor = data->cio2.sensor;
-	V4L2Subdevice *csi2 = data->cio2.csi2;
+	const StreamConfiguration &cfg = config[&data->stream_];
+	V4L2Device *viewfinder = data->imgu->viewfinder;
+	V4L2Device *output = data->imgu->output;
+	V4L2Device *input = data->imgu->input;
 	V4L2Device *cio2 = data->cio2.output;
-	V4L2SubdeviceFormat subdevFormat = {};
-	V4L2DeviceFormat devFormat = {};
 	int ret;
 
+	LOG(IPU3, Info)
+		<< "Requested image format: " << cfg.width << "x"
+		<< cfg.height << " - " << std::hex << std::setw(8)
+		<< cfg.pixelFormat << " on camera:'" << camera->name() << "'";
+
 	/*
-	 * FIXME: as of now, the format gets applied to the sensor and is
-	 * propagated along the pipeline. It should instead be applied on the
-	 * capture device and the sensor format calculated accordingly.
+	 * 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;
+	}
 
-	ret = sensor->getFormat(0, &subdevFormat);
+	/*
+	 * Pass the requested output image size to the sensor and get back the
+	 * adjusted one to be propagated to the CIO2 device and to the ImgU
+	 * input.
+	 */
+	V4L2SubdeviceFormat sensorFormat = {};
+	ret = setCIO2Format(&data->cio2, cfg, &sensorFormat);
 	if (ret)
 		return ret;
 
-	subdevFormat.width = cfg->width;
-	subdevFormat.height = cfg->height;
-	ret = sensor->setFormat(0, &subdevFormat);
+	/* Apply the CIO2 image format to the CIO2 output and ImgU input. */
+	V4L2DeviceFormat cio2Format = {};
+	cio2Format.width = sensorFormat.width;
+	cio2Format.height = sensorFormat.height;
+	cio2Format.fourcc = mediaBusToCIO2Format(sensorFormat.mbus_code);
+	cio2Format.planesCount = 1;
+	ret = cio2->setFormat(&cio2Format);
 	if (ret)
 		return ret;
 
-	/* Return error if the requested format cannot be applied to sensor. */
-	if (subdevFormat.width != cfg->width ||
-	    subdevFormat.height != cfg->height) {
-		LOG(IPU3, Error)
-			<< "Failed to apply image format "
-			<< subdevFormat.width << "x" << subdevFormat.height
-			<< " - got: " << cfg->width << "x" << cfg->height;
-		return -EINVAL;
-	}
+	LOG(IPU3, Debug)
+		<< "CIO2 output format = " << cio2Format.toString();
 
-	ret = csi2->setFormat(0, &subdevFormat);
+	ret = input->setFormat(&cio2Format);
 	if (ret)
 		return ret;
 
-	ret = cio2->getFormat(&devFormat);
+	/* Apply pad formats and crop/compose rectangle to the ImgU. */
+	Rectangle rect = {
+		.x = 0,
+		.y = 0,
+		.w = cio2Format.width,
+		.h = cio2Format.height,
+	};
+	ret = setImguFormat(data->imgu, cfg, &rect);
 	if (ret)
 		return ret;
 
-	devFormat.width = subdevFormat.width;
-	devFormat.height = subdevFormat.height;
-	devFormat.fourcc = cfg->pixelFormat;
+	/* 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;
 
-	ret = cio2->setFormat(&devFormat);
+	ret = output->setFormat(&outputFormat);
 	if (ret)
 		return ret;
 
-	LOG(IPU3, Info) << cio2->driverName() << ": "
-			<< devFormat.width << "x" << devFormat.height
-			<< "- 0x" << std::hex << devFormat.fourcc << " planes: "
-			<< devFormat.planes;
+	LOG(IPU3, Debug)
+		<< "ImgU output format = " << outputFormat.toString();
+
+	ret = viewfinder->setFormat(&outputFormat);
+	if (ret)
+		return ret;
 
 	return 0;
 }
@@ -682,6 +721,125 @@  void PipelineHandlerIPU3::deleteCIO2(CIO2Device *cio2)
 	delete cio2->sensor;
 }
 
+int PipelineHandlerIPU3::setImguFormat(ImgUDevice *imguDevice,
+				       const StreamConfiguration &config,
+				       Rectangle *rect)
+{
+	V4L2Subdevice *imgu = imguDevice->imgu;
+	int ret;
+
+	/*
+	 * Configure the 'imgu' subdevice with the requested sizes.
+	 *
+	 * FIXME: the IPU3 driver implementation shall be changed to use the
+	 * actual input sizes as 'imgu input' subdevice sizes, and use the
+	 * desired output sizes to configure the crop/compose rectangles.  The
+	 * current implementation uses output sizes as 'imgu input' sizes, and
+	 * uses the input dimension to configure the crop/compose rectangles,
+	 * which contradicts the V4L2 specifications.
+	 */
+	ret = imgu->setCrop(ImgUDevice::PAD_INPUT, rect);
+	if (ret)
+		return ret;
+
+	ret = imgu->setCompose(ImgUDevice::PAD_INPUT, rect);
+	if (ret)
+		return ret;
+
+	LOG(IPU3, Debug)
+		<< "ImgU input feeder and BDS rectangle = (0,0)/"
+		<< rect->w << "x" << rect->h;
+
+	V4L2SubdeviceFormat imguFormat = {};
+	imguFormat.width = config.width;
+	imguFormat.height = config.height;
+	imguFormat.mbus_code = MEDIA_BUS_FMT_FIXED;
+
+	ret = imgu->setFormat(ImgUDevice::PAD_INPUT, &imguFormat);
+	if (ret)
+		return ret;
+
+	ret = imgu->setFormat(ImgUDevice::PAD_OUTPUT, &imguFormat);
+	if (ret)
+		return ret;
+
+	LOG(IPU3, Debug)
+		<< "ImgU GDC format = " << imguFormat.toString();
+
+	ret = imgu->setFormat(ImgUDevice::PAD_VF, &imguFormat);
+	if (ret)
+		return ret;
+
+	ret = imgu->setFormat(ImgUDevice::PAD_STAT, &imguFormat);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int PipelineHandlerIPU3::setCIO2Format(CIO2Device *cio2Device,
+				       const StreamConfiguration &config,
+				       V4L2SubdeviceFormat *format)
+{
+	unsigned int imageSize = config.width * config.height;
+	V4L2Subdevice *sensor = cio2Device->sensor;
+	V4L2Subdevice *csi2 = cio2Device->csi2;
+	unsigned int best = ~0;
+	bool found = false;
+	int ret;
+
+	const SubdevFormatEnum formats = sensor->formats(0);
+	for (auto it = formats.begin(); it != formats.end(); ++it) {
+		/* Only consider formats consumable by the CIO2 unit. */
+		int cio2Code = mediaBusToCIO2Format(it->first);
+		if (cio2Code == -EINVAL)
+			continue;
+
+		for (const SizeRange &size : it->second) {
+			/*
+			 * Only select formats bigger than the requested sizes
+			 * as the IPU3 cannot up-scale.
+			 */
+			if (size.maxWidth < config.width ||
+			    size.maxHeight < config.height)
+				continue;
+
+			unsigned int diff = size.maxWidth * size.maxHeight
+					  - imageSize;
+			if (diff >= best)
+				continue;
+
+			best = diff;
+			found = true;
+
+			format->width = size.maxWidth;
+			format->height = size.maxHeight;
+			format->mbus_code = it->first;
+		}
+	}
+	if (!found) {
+		LOG(IPU3, Error)
+			<< "Unable to find image format suitable to produce: "
+			<< config.width << "x" << config.height
+			<< "- 0x" << std::hex << std::setfill('0')
+			<< std::setw(8) << config.pixelFormat;
+		return -EINVAL;
+	}
+
+	/* Apply the selected format to the sensor and the CSI-2 receiver. */
+	ret = sensor->setFormat(0, format);
+	if (ret)
+		return ret;
+
+	ret = csi2->setFormat(0, format);
+	if (ret)
+		return ret;
+
+	LOG(IPU3, Debug) << "CIO2 Image format: " << format->toString();
+
+	return 0;
+}
+
 /*
  * Cameras are created associating an image sensor (represented by a
  * media entity with function MEDIA_ENT_F_CAM_SENSOR) to one of the four