[libcamera-devel] [PATCH 4/5] libcamera: ipu3: Add CIO2Device class

Jacopo Mondi jacopo at jmondi.org
Wed Feb 20 14:17:56 CET 2019


Add a CIO2Device class that represents the CIO2 unit associated with a
Camera. Implement image format negotiation in the CIO2Device and create
a CIO2Device instance for each camera created by the IPU3 pipeline
handler.

Signed-off-by: Jacopo Mondi <jacopo at jmondi.org>
---
 src/libcamera/pipeline/ipu3/cio2.cpp    | 266 ++++++++++++++++++++++++
 src/libcamera/pipeline/ipu3/ipu3.cpp    | 158 +++-----------
 src/libcamera/pipeline/ipu3/ipu3.h      |  51 +++--
 src/libcamera/pipeline/ipu3/meson.build |   1 +
 4 files changed, 335 insertions(+), 141 deletions(-)
 create mode 100644 src/libcamera/pipeline/ipu3/cio2.cpp

diff --git a/src/libcamera/pipeline/ipu3/cio2.cpp b/src/libcamera/pipeline/ipu3/cio2.cpp
new file mode 100644
index 000000000000..b0eeff987f64
--- /dev/null
+++ b/src/libcamera/pipeline/ipu3/cio2.cpp
@@ -0,0 +1,266 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * cio2.cpp - IPU3 CIO2 unit
+ */
+
+#include <string>
+#include <vector>
+
+#include "ipu3.h"
+#include "media_device.h"
+#include "v4l2_device.h"
+#include "v4l2_subdevice.h"
+
+/*
+ * CIO2Device represents one of the four 'CIO2' units the Intel IPU3 ISP
+ * provides.
+ *
+ * Each CIO2 unit has associated one image sensor, which provides RAW image
+ * data to be processed by one of the IPU3's IMGU units.
+ */
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(IPU3)
+
+CIO2Device::CIO2Device()
+	: cio2_(nullptr), csi2_(nullptr), sensor_(nullptr)
+{
+}
+
+CIO2Device::~CIO2Device()
+{
+	delete cio2_;
+	delete csi2_;
+	delete sensor_;
+}
+
+/*
+ * Create and open the video device and video subdevices associated with
+ * this CIO2 unit.
+ */
+int CIO2Device::open(unsigned int index, MediaDevice *cio2MediaDev)
+{
+	int ret;
+
+	std::string cio2Name = "ipu3-cio2 " + std::to_string(index);
+	MediaEntity *cio2Entity = cio2MediaDev->getEntityByName(cio2Name);
+	if (!cio2Entity) {
+		LOG(IPU3, Error)
+			<< "Failed to get entity '" << cio2Name << "'";
+		return -EINVAL;
+	}
+
+	std::string csi2Name = "ipu3-csi2 " + std::to_string(index);
+	MediaEntity *csi2Entity = cio2MediaDev->getEntityByName(csi2Name);
+	if (!csi2Entity) {
+		LOG(IPU3, Error)
+			<< "Failed to get entity '" << csi2Name << "'";
+		return -EINVAL;
+	}
+
+	const std::vector<MediaPad *> &pads = csi2Entity->pads();
+	if (pads.empty())
+		return -EINVAL;
+
+	/* IPU3 CSI-2 receivers have a single sink pad at index 0. */
+	MediaPad *sink = pads[0];
+	const std::vector<MediaLink *> &links = sink->links();
+	if (links.empty())
+		return -EINVAL;
+
+	/*
+	 * Verify that the CSI-2 receiver is connected to a sensor and enable
+	 * the media link between the two.
+	 */
+	MediaLink *link = links[0];
+	MediaEntity *sensorEntity = link->source()->entity();
+	if (sensorEntity->function() != MEDIA_ENT_F_CAM_SENSOR)
+		return -EINVAL;
+
+	if (link->setEnabled(true))
+		return -EINVAL;
+
+	/*
+	 * Create and open video devices and subdevices associated with
+	 * the camera.
+	 */
+	cio2_ = new V4L2Device(cio2Entity);
+	ret = cio2_->open();
+	if (ret)
+		return ret;
+
+	sensor_ = new V4L2Subdevice(sensorEntity);
+	ret = sensor_->open();
+	if (ret)
+		return ret;
+
+	csi2_ = new V4L2Subdevice(csi2Entity);
+	ret = csi2_->open();
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int CIO2Device::sensorSetFormat(unsigned int width, unsigned int height,
+				V4L2SubdeviceFormat *format)
+{
+	unsigned int best = ~0;
+	bool found = false;
+	int ret;
+
+	ret = sensor_->getFormat(0, format);
+	if (ret)
+		return ret;
+
+	/*
+	 * Enumerate the sensor formats until a matching one is found.
+	 * Re-use the default media_bus pixel format as it is configured
+	 * in the sensor.
+	 */
+	V4L2SubdeviceFormatEnum bestEnum = {};
+	V4L2SubdeviceFormatEnum formatEnum = {};
+	formatEnum.index = 0;
+	formatEnum.mbus_code = format->mbus_code;
+
+	while (true) {
+		unsigned int diff;
+
+		int ret = sensor_->enumFormat(0, &formatEnum);
+		if (ret)
+			break;
+
+		diff = (abs(formatEnum.minWidth - width) +
+			abs(formatEnum.minHeight - height));
+
+		if (formatEnum.minWidth >= width &&
+		    formatEnum.minHeight >= height &&
+		    diff < best) {
+			best = diff;
+			bestEnum = formatEnum;
+			found = true;
+		}
+
+		LOG(IPU3, Debug)
+			<< "Enumerate image sizes : " << formatEnum.index
+			<< " = " << formatEnum.minWidth << "x"
+			<< formatEnum.minHeight
+			<< "Diff " << diff << " Best " << best;
+
+		formatEnum.index++;
+	}
+	if (!found) {
+		LOG(IPU3, Error)
+			<< "Failed to find image resolution for: "
+			<< width << "x" << height;
+
+		return -EINVAL;
+	}
+
+	format->width = bestEnum.minWidth;
+	format->height = bestEnum.minHeight;
+	ret = sensor_->setFormat(0, format);
+	if (ret)
+		return ret;
+
+	/* Make sure everything is all right. */
+	if (format->width < width || format->height < height) {
+		LOG(IPU3, Error)
+			<< "Failed to find image resolution for: "
+			<< width << "x" << height << " - Got : "
+			<< format->width << "x" << format->height
+			<< " instead";
+
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Configure the CIO2 unit to provide big enough images to satisfy
+ * the requested width and height.
+ *
+ * The images captured from the CIO2 unit will be fed to an IMGU unit that
+ * can scale and crop on them to obtain the desired output sizes.
+ */
+int CIO2Device::configure(const IPU3DeviceFormat &format)
+{
+	V4L2SubdeviceFormat subdevFormat = {};
+	V4L2DeviceFormat devFormat = {};
+	int ret;
+
+	ret = sensorSetFormat(format.width, format.height, &subdevFormat);
+	if (ret)
+		return ret;
+
+	LOG(IPU3, Debug)
+		<< "'" << sensor_->deviceName() << "':0 = "
+		<< subdevFormat.width << "x" << subdevFormat.height << " - "
+		<< std::hex << subdevFormat.mbus_code;
+
+	/* Propagate the format along the pipeline. */
+	ret = csi2_->setFormat(0, &subdevFormat);
+	if (ret)
+		return ret;
+
+	ret = cio2_->getFormat(&devFormat);
+	if (ret)
+		return ret;
+
+	devFormat.width = subdevFormat.width;
+	devFormat.height = subdevFormat.height;
+	devFormat.fourcc = V4L2_PIX_FMT_IPU3_SGRBG10;
+
+	ret = cio2_->setFormat(&devFormat);
+	if (ret)
+		return ret;
+
+	LOG(IPU3, Debug)
+		<< cio2_->driverName() << ": "
+		<< devFormat.width << "x" << devFormat.height
+		<< "- 0x" << std::hex << devFormat.fourcc << " planes: "
+		<< devFormat.planesCount;
+
+	/* Store the applied format in the cio2Format_ member for re-use. */
+	cio2Format_.width = devFormat.width;
+	cio2Format_.height = devFormat.height;
+	cio2Format_.fourcc = devFormat.fourcc;
+
+	return 0;
+}
+
+const IPU3DeviceFormat &CIO2Device::format() const
+{
+	return cio2Format_;
+}
+
+int CIO2Device::exportBuffers(BufferPool *pool)
+{
+	return cio2_->exportBuffers(pool);
+}
+
+int CIO2Device::releaseBuffers()
+{
+	return cio2_->releaseBuffers();
+}
+
+int CIO2Device::queueBuffer(Buffer *buffer)
+{
+	return cio2_->queueBuffer(buffer);
+}
+
+int CIO2Device::streamOn()
+{
+	return cio2_->streamOn();
+}
+
+int CIO2Device::streamOff()
+{
+	return cio2_->streamOff();
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index 07029dd763c9..5fcdd6335db6 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -60,74 +60,37 @@ PipelineHandlerIPU3::streamConfiguration(Camera *camera,
 int PipelineHandlerIPU3::configureStreams(Camera *camera,
 					  std::map<Stream *, StreamConfiguration> &config)
 {
-	IPU3CameraData *data = cameraData(camera);
-	StreamConfiguration *cfg = &config[&data->stream_];
-	V4L2Subdevice *sensor = data->sensor_;
-	V4L2Subdevice *csi2 = data->csi2_;
-	V4L2Device *cio2 = data->cio2_;
-	V4L2SubdeviceFormat subdevFormat = {};
-	V4L2DeviceFormat devFormat = {};
+	CIO2Device *cio2 = &cameraData(camera)->cio2Unit_;
+	Stream *stream = &cameraData(camera)->stream_;
+	StreamConfiguration *cfg = &config[stream];
+	IPU3DeviceFormat outputFormat = {};
 	int ret;
 
-	/*
-	 * 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.
-	 */
-
-	ret = sensor->getFormat(0, &subdevFormat);
-	if (ret)
-		return ret;
-
-	subdevFormat.width = cfg->width;
-	subdevFormat.height = cfg->height;
-	ret = sensor->setFormat(0, &subdevFormat);
-	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;
+	/* Validate the requested image format and resolution. */
+	if (cfg->pixelFormat != V4L2_PIX_FMT_NV12) {
+		LOG(IPU3, Error) << "Image format not supported";
 		return -EINVAL;
 	}
+	outputFormat.width = cfg->width;
+	outputFormat.height = cfg->height;
+	outputFormat.fourcc = cfg->pixelFormat;
 
-	ret = csi2->setFormat(0, &subdevFormat);
+	ret = cio2->configure(outputFormat);
 	if (ret)
 		return ret;
 
-	ret = cio2->getFormat(&devFormat);
-	if (ret)
-		return ret;
-
-	devFormat.width = subdevFormat.width;
-	devFormat.height = subdevFormat.height;
-	devFormat.fourcc = cfg->pixelFormat;
-
-	ret = cio2->setFormat(&devFormat);
-	if (ret)
-		return ret;
-
-	LOG(IPU3, Info) << cio2->driverName() << ": "
-			<< devFormat.width << "x" << devFormat.height
-			<< "- 0x" << std::hex << devFormat.fourcc << " planes: "
-			<< devFormat.planes;
-
 	return 0;
 }
 
 int PipelineHandlerIPU3::allocateBuffers(Camera *camera, Stream *stream)
 {
-	IPU3CameraData *data = cameraData(camera);
 	const StreamConfiguration &cfg = stream->configuration();
+	CIO2Device *cio2 = &cameraData(camera)->cio2Unit_;
 
 	if (!cfg.bufferCount)
 		return -EINVAL;
 
-	int ret = data->cio2_->exportBuffers(&stream->bufferPool());
+	int ret = cio2->exportBuffers(&stream->bufferPool());
 	if (ret) {
 		LOG(IPU3, Error) << "Failed to request memory";
 		return ret;
@@ -138,9 +101,9 @@ int PipelineHandlerIPU3::allocateBuffers(Camera *camera, Stream *stream)
 
 int PipelineHandlerIPU3::freeBuffers(Camera *camera, Stream *stream)
 {
-	IPU3CameraData *data = cameraData(camera);
+	CIO2Device *cio2 = &cameraData(camera)->cio2Unit_;
 
-	int ret = data->cio2_->releaseBuffers();
+	int ret = cio2->releaseBuffers();
 	if (ret) {
 		LOG(IPU3, Error) << "Failed to release memory";
 		return ret;
@@ -151,10 +114,10 @@ int PipelineHandlerIPU3::freeBuffers(Camera *camera, Stream *stream)
 
 int PipelineHandlerIPU3::start(const Camera *camera)
 {
-	IPU3CameraData *data = cameraData(camera);
+	CIO2Device *cio2 = &cameraData(camera)->cio2Unit_;
 	int ret;
 
-	ret = data->cio2_->streamOn();
+	ret = cio2->streamOn();
 	if (ret) {
 		LOG(IPU3, Info) << "Failed to start camera " << camera->name();
 		return ret;
@@ -165,16 +128,16 @@ int PipelineHandlerIPU3::start(const Camera *camera)
 
 void PipelineHandlerIPU3::stop(const Camera *camera)
 {
-	IPU3CameraData *data = cameraData(camera);
+	CIO2Device *cio2 = &cameraData(camera)->cio2Unit_;
 
-	if (data->cio2_->streamOff())
+	if (cio2->streamOff())
 		LOG(IPU3, Info) << "Failed to stop camera " << camera->name();
 }
 
 int PipelineHandlerIPU3::queueRequest(const Camera *camera, Request *request)
 {
-	IPU3CameraData *data = cameraData(camera);
-	Stream *stream = &data->stream_;
+	CIO2Device *cio2 = &cameraData(camera)->cio2Unit_;
+	Stream *stream = &cameraData(camera)->stream_;
 
 	Buffer *buffer = request->findBuffer(stream);
 	if (!buffer) {
@@ -183,7 +146,7 @@ int PipelineHandlerIPU3::queueRequest(const Camera *camera, Request *request)
 		return -ENOENT;
 	}
 
-	data->cio2_->queueBuffer(buffer);
+	cio2->queueBuffer(buffer);
 
 	return 0;
 }
@@ -271,79 +234,22 @@ void PipelineHandlerIPU3::registerCameras()
 	 */
 	unsigned int numCameras = 0;
 	for (unsigned int id = 0; id < 4; ++id) {
-		std::string csi2Name = "ipu3-csi2 " + std::to_string(id);
-		MediaEntity *csi2 = cio2_->getEntityByName(csi2Name);
-		int ret;
+		std::unique_ptr<IPU3CameraData> data =
+			utils::make_unique<IPU3CameraData>();
 
 		/*
-		 * This shall not happen, as the device enumerator matched
-		 * all entities described in the cio2_dm DeviceMatch.
-		 *
-		 * As this check is basically free, better stay safe than sorry.
+		 * If opening the CIO2 unit fails, the Camera instance won't
+		 * be registered and the 'data' unique pointers goes out of
+		 * scope and delete the objects they manage.
 		 */
-		if (!csi2)
-			continue;
-
-		const std::vector<MediaPad *> &pads = csi2->pads();
-		if (pads.empty())
-			continue;
-
-		/* IPU3 CSI-2 receivers have a single sink pad at index 0. */
-		MediaPad *sink = pads[0];
-		const std::vector<MediaLink *> &links = sink->links();
-		if (links.empty())
-			continue;
-
-		/*
-		 * Verify that the receiver is connected to a sensor, enable
-		 * the media link between the two, and create a Camera with
-		 * a unique name.
-		 */
-		MediaLink *link = links[0];
-		MediaEntity *sensor = link->source()->entity();
-		if (sensor->function() != MEDIA_ENT_F_CAM_SENSOR)
-			continue;
-
-		if (link->setEnabled(true))
+		if (data->cio2Unit_.open(id, cio2_.get()))
 			continue;
 
-		std::unique_ptr<IPU3CameraData> data = utils::make_unique<IPU3CameraData>();
-
-		std::string cameraName = sensor->name() + " " + std::to_string(id);
+		std::string cameraName = data->cio2Unit_.sensor_->deviceName()
+				       + " " + std::to_string(id);
 		std::vector<Stream *> streams{ &data->stream_ };
-		std::shared_ptr<Camera> camera = Camera::create(this, cameraName, streams);
-
-		/*
-		 * Create and open video devices and subdevices associated with
-		 * the camera.
-		 *
-		 * If any of these operations fails, the Camera instance won't
-		 * be registered. The 'camera' shared pointer and the 'data'
-		 * unique pointers go out of scope and delete the objects they
-		 * manage.
-		 */
-		std::string cio2Name = "ipu3-cio2 " + std::to_string(id);
-		MediaEntity *cio2 = cio2_->getEntityByName(cio2Name);
-		if (!cio2) {
-			LOG(IPU3, Error)
-				<< "Failed to get entity '" << cio2Name << "'";
-			continue;
-		}
-
-		data->cio2_ = new V4L2Device(cio2);
-		ret = data->cio2_->open();
-		if (ret)
-			continue;
-
-		data->sensor_ = new V4L2Subdevice(sensor);
-		ret = data->sensor_->open();
-		if (ret)
-			continue;
-
-		data->csi2_ = new V4L2Subdevice(csi2);
-		ret = data->csi2_->open();
-		if (ret)
-			continue;
+		std::shared_ptr<Camera> camera = Camera::create(this, cameraName,
+								streams);
 
 		setCameraData(camera.get(), std::move(data));
 		registerCamera(std::move(camera));
diff --git a/src/libcamera/pipeline/ipu3/ipu3.h b/src/libcamera/pipeline/ipu3/ipu3.h
index 48c2a3e16980..2a8b6f13b1c7 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.h
+++ b/src/libcamera/pipeline/ipu3/ipu3.h
@@ -23,6 +23,40 @@
 
 namespace libcamera {
 
+struct IPU3DeviceFormat {
+	unsigned int width;
+	unsigned int height;
+	unsigned int fourcc;
+};
+
+class CIO2Device
+{
+public:
+	CIO2Device();
+	CIO2Device(const CIO2Device &) = delete;
+	~CIO2Device();
+
+	int open(unsigned int index, MediaDevice *cio2MediaDev);
+	int sensorSetFormat(unsigned int width, unsigned int height,
+			    V4L2SubdeviceFormat *format);
+	int configure(const IPU3DeviceFormat &format);
+	const IPU3DeviceFormat &format() const;
+
+	int exportBuffers(BufferPool *pool);
+	int releaseBuffers();
+
+	int queueBuffer(Buffer *buffer);
+
+	int streamOn();
+	int streamOff();
+
+	V4L2Device *cio2_;
+	V4L2Subdevice *csi2_;
+	V4L2Subdevice *sensor_;
+
+	IPU3DeviceFormat cio2Format_;
+};
+
 class PipelineHandlerIPU3 : public PipelineHandler
 {
 public:
@@ -49,19 +83,8 @@ private:
 	class IPU3CameraData : public CameraData
 	{
 	public:
-		IPU3CameraData()
-			: cio2_(nullptr), csi2_(nullptr), sensor_(nullptr) {}
-
-		~IPU3CameraData()
-		{
-			delete cio2_;
-			delete csi2_;
-			delete sensor_;
-		}
-
-		V4L2Device *cio2_;
-		V4L2Subdevice *csi2_;
-		V4L2Subdevice *sensor_;
+		/* Each camera has a CIO2 unit associated. */
+		CIO2Device cio2Unit_;
 
 		Stream stream_;
 	};
@@ -76,8 +99,6 @@ private:
 
 	std::shared_ptr<MediaDevice> cio2_;
 	std::shared_ptr<MediaDevice> imgu_;
-	IMGUDevice imgu0_;
-	IMGUDevice imgu1_;
 };
 
 } /* namespace libcamera */
diff --git a/src/libcamera/pipeline/ipu3/meson.build b/src/libcamera/pipeline/ipu3/meson.build
index 0ab766a257a0..fcb2d319d517 100644
--- a/src/libcamera/pipeline/ipu3/meson.build
+++ b/src/libcamera/pipeline/ipu3/meson.build
@@ -1,3 +1,4 @@
 libcamera_sources += files([
+    'cio2.cpp',
     'ipu3.cpp',
 ])
-- 
2.20.1



More information about the libcamera-devel mailing list