From patchwork Wed Feb 20 13:17:53 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 589 Return-Path: Received: from relay2-d.mail.gandi.net (relay2-d.mail.gandi.net [217.70.183.194]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 77351601E4 for ; Wed, 20 Feb 2019 14:17:40 +0100 (CET) X-Originating-IP: 2.224.242.101 Received: from uno.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay2-d.mail.gandi.net (Postfix) with ESMTPSA id 033DC4000B; Wed, 20 Feb 2019 13:17:39 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Wed, 20 Feb 2019 14:17:53 +0100 Message-Id: <20190220131757.14004-2-jacopo@jmondi.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190220131757.14004-1-jacopo@jmondi.org> References: <20190220131757.14004-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 1/5] libcamera: v4l2_subdevice: Make crop/compose rectangle a reference X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 20 Feb 2019 13:17:40 -0000 As the crop/compose rectangle provided to setCrop and setCompose methods is never modified, make it a const reference. Signed-off-by: Jacopo Mondi --- src/libcamera/include/v4l2_subdevice.h | 6 +++--- src/libcamera/v4l2_subdevice.cpp | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h index 40becd0ca99b..18000d6ed06b 100644 --- a/src/libcamera/include/v4l2_subdevice.h +++ b/src/libcamera/include/v4l2_subdevice.h @@ -44,8 +44,8 @@ public: std::string deviceNode() const { return deviceNode_; } std::string deviceName() const { return deviceName_; } - int setCrop(unsigned int pad, Rectangle *rect); - int setCompose(unsigned int pad, Rectangle *rect); + int setCrop(unsigned int pad, const Rectangle &rect); + int setCompose(unsigned int pad, const Rectangle &rect); int enumFormat(unsigned int pad, V4L2SubdeviceFormatEnum *formatEnum); int getFormat(unsigned int pad, V4L2SubdeviceFormat *format); @@ -53,7 +53,7 @@ public: private: int setSelection(unsigned int pad, unsigned int target, - Rectangle *rect); + const Rectangle &rect); std::string deviceNode_; std::string deviceName_; diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp index 4411ffa51460..2e73edee68c9 100644 --- a/src/libcamera/v4l2_subdevice.cpp +++ b/src/libcamera/v4l2_subdevice.cpp @@ -203,11 +203,11 @@ void V4L2Subdevice::close() /** * \brief Set a crop rectangle on one of the V4L2 subdevice pads * \param[in] pad The 0-indexed pad number the rectangle is to be applied to - * \param[inout] rect The rectangle describing crop target area + * \param[in] rect The rectangle describing crop target area * * \return 0 on success, or a negative error code otherwise */ -int V4L2Subdevice::setCrop(unsigned int pad, Rectangle *rect) +int V4L2Subdevice::setCrop(unsigned int pad, const Rectangle &rect) { return setSelection(pad, V4L2_SEL_TGT_CROP, rect); } @@ -215,11 +215,11 @@ int V4L2Subdevice::setCrop(unsigned int pad, Rectangle *rect) /** * \brief Set a compose rectangle on one of the V4L2 subdevice pads * \param[in] pad The 0-indexed pad number the rectangle is to be applied to - * \param[inout] rect The rectangle describing the compose target area + * \param[in] rect The rectangle describing the compose target area * * \return 0 on success, or a negative error code otherwise */ -int V4L2Subdevice::setCompose(unsigned int pad, Rectangle *rect) +int V4L2Subdevice::setCompose(unsigned int pad, const Rectangle &rect) { return setSelection(pad, V4L2_SEL_TGT_COMPOSE, rect); } @@ -333,7 +333,7 @@ int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format) } int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target, - Rectangle *rect) + const Rectangle &rect) { struct v4l2_subdev_selection sel = {}; @@ -342,10 +342,10 @@ int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target, sel.target = target; sel.flags = 0; - sel.r.left = rect->y; - sel.r.top = rect->x; - sel.r.width = rect->w; - sel.r.height = rect->h; + sel.r.left = rect.y; + sel.r.top = rect.x; + sel.r.width = rect.w; + sel.r.height = rect.h; int ret = ioctl(fd_, VIDIOC_SUBDEV_S_SELECTION, &sel); if (ret < 0) { From patchwork Wed Feb 20 13:17:54 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 590 Return-Path: Received: from relay2-d.mail.gandi.net (relay2-d.mail.gandi.net [217.70.183.194]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1C1CC601E4 for ; Wed, 20 Feb 2019 14:17:41 +0100 (CET) X-Originating-IP: 2.224.242.101 Received: from uno.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay2-d.mail.gandi.net (Postfix) with ESMTPSA id A976540014; Wed, 20 Feb 2019 13:17:40 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Wed, 20 Feb 2019 14:17:54 +0100 Message-Id: <20190220131757.14004-3-jacopo@jmondi.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190220131757.14004-1-jacopo@jmondi.org> References: <20190220131757.14004-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 2/5] libcamera: ipu3: Return video output default configuration X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 20 Feb 2019 13:17:41 -0000 Return default configuration for the output stream produced by the imgu 'output' video node. Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund --- src/libcamera/pipeline/ipu3/ipu3.cpp | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index 9694d0ce51ab..9065073913a2 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -99,21 +99,11 @@ PipelineHandlerIPU3::streamConfiguration(Camera *camera, { IPU3CameraData *data = cameraData(camera); std::map configs; - V4L2SubdeviceFormat format = {}; - - /* - * FIXME: As of now, return the image format reported by the sensor. - * In future good defaults should be provided for each stream. - */ - if (data->sensor_->getFormat(0, &format)) { - LOG(IPU3, Error) << "Failed to create stream configurations"; - return configs; - } StreamConfiguration config = {}; - config.width = format.width; - config.height = format.height; - config.pixelFormat = V4L2_PIX_FMT_IPU3_SGRBG10; + config.width = 2560; + config.height = 1920; + config.pixelFormat = V4L2_PIX_FMT_NV12; config.bufferCount = 4; configs[&data->stream_] = config; From patchwork Wed Feb 20 13:17:55 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 591 Return-Path: Received: from relay2-d.mail.gandi.net (relay2-d.mail.gandi.net [217.70.183.194]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id ABBC1601E3 for ; Wed, 20 Feb 2019 14:17:41 +0100 (CET) X-Originating-IP: 2.224.242.101 Received: from uno.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay2-d.mail.gandi.net (Postfix) with ESMTPSA id 46D8C40016; Wed, 20 Feb 2019 13:17:41 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Wed, 20 Feb 2019 14:17:55 +0100 Message-Id: <20190220131757.14004-4-jacopo@jmondi.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190220131757.14004-1-jacopo@jmondi.org> References: <20190220131757.14004-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 3/5] libcamera: ipu3: Break-out ipu3 header file X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 20 Feb 2019 13:17:41 -0000 As the class grows, break out the class definitions in a separate header file, which can be used by other ipu3-related cpp files that will be added in next commits. Signed-off-by: Jacopo Mondi --- src/libcamera/pipeline/ipu3/ipu3.cpp | 56 +----------------- src/libcamera/pipeline/ipu3/ipu3.h | 85 ++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 55 deletions(-) create mode 100644 src/libcamera/pipeline/ipu3/ipu3.h diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index 9065073913a2..07029dd763c9 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -14,6 +14,7 @@ #include "device_enumerator.h" #include "log.h" +#include "ipu3.h" #include "media_device.h" #include "pipeline_handler.h" #include "utils.h" @@ -24,61 +25,6 @@ namespace libcamera { LOG_DEFINE_CATEGORY(IPU3) -class PipelineHandlerIPU3 : public PipelineHandler -{ -public: - PipelineHandlerIPU3(CameraManager *manager); - ~PipelineHandlerIPU3(); - - std::map - streamConfiguration(Camera *camera, - std::vector &streams) override; - int configureStreams(Camera *camera, - std::map &config) override; - - int allocateBuffers(Camera *camera, Stream *stream) override; - int freeBuffers(Camera *camera, Stream *stream) override; - - int start(const Camera *camera) override; - void stop(const Camera *camera) override; - - int queueRequest(const Camera *camera, Request *request) override; - - bool match(DeviceEnumerator *enumerator); - -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_; - - Stream stream_; - }; - - IPU3CameraData *cameraData(const Camera *camera) - { - return static_cast( - PipelineHandler::cameraData(camera)); - } - - void registerCameras(); - - std::shared_ptr cio2_; - std::shared_ptr imgu_; -}; - PipelineHandlerIPU3::PipelineHandlerIPU3(CameraManager *manager) : PipelineHandler(manager), cio2_(nullptr), imgu_(nullptr) { diff --git a/src/libcamera/pipeline/ipu3/ipu3.h b/src/libcamera/pipeline/ipu3/ipu3.h new file mode 100644 index 000000000000..48c2a3e16980 --- /dev/null +++ b/src/libcamera/pipeline/ipu3/ipu3.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * ipu3.h - Pipeline handler for Intel IPU3 + */ + +#ifndef __LIBCAMERA_PIPELINE_IPU3_H__ +#define __LIBCAMERA_PIPELINE_IPU3_H__ + +#include +#include + +#include +#include +#include + +#include "device_enumerator.h" +#include "media_device.h" +#include "pipeline_handler.h" +#include "v4l2_device.h" +#include "v4l2_subdevice.h" + +namespace libcamera { + +class PipelineHandlerIPU3 : public PipelineHandler +{ +public: + PipelineHandlerIPU3(CameraManager *manager); + ~PipelineHandlerIPU3(); + + std::map + streamConfiguration(Camera *camera, + std::vector &streams) override; + int configureStreams(Camera *camera, + std::map &config) override; + + int allocateBuffers(Camera *camera, Stream *stream) override; + int freeBuffers(Camera *camera, Stream *stream) override; + + int start(const Camera *camera) override; + void stop(const Camera *camera) override; + + int queueRequest(const Camera *camera, Request *request) override; + + bool match(DeviceEnumerator *enumerator); + +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_; + + Stream stream_; + }; + + IPU3CameraData *cameraData(const Camera *camera) + { + return static_cast( + PipelineHandler::cameraData(camera)); + } + + void registerCameras(); + + std::shared_ptr cio2_; + std::shared_ptr imgu_; + IMGUDevice imgu0_; + IMGUDevice imgu1_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_PIPELINE_IPU3_H__ */ From patchwork Wed Feb 20 13:17:56 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 592 Return-Path: Received: from relay2-d.mail.gandi.net (relay2-d.mail.gandi.net [217.70.183.194]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 7442E601EA for ; Wed, 20 Feb 2019 14:17:42 +0100 (CET) X-Originating-IP: 2.224.242.101 Received: from uno.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay2-d.mail.gandi.net (Postfix) with ESMTPSA id D840D40016; Wed, 20 Feb 2019 13:17:41 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Wed, 20 Feb 2019 14:17:56 +0100 Message-Id: <20190220131757.14004-5-jacopo@jmondi.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190220131757.14004-1-jacopo@jmondi.org> References: <20190220131757.14004-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 4/5] libcamera: ipu3: Add CIO2Device class X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 20 Feb 2019 13:17:43 -0000 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 --- 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 +#include + +#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 &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 &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 &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 data = + utils::make_unique(); /* - * 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 &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 &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 data = utils::make_unique(); - - std::string cameraName = sensor->name() + " " + std::to_string(id); + std::string cameraName = data->cio2Unit_.sensor_->deviceName() + + " " + std::to_string(id); std::vector streams{ &data->stream_ }; - std::shared_ptr 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::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 cio2_; std::shared_ptr 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', ]) From patchwork Wed Feb 20 13:17:57 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 593 Return-Path: Received: from relay2-d.mail.gandi.net (relay2-d.mail.gandi.net [217.70.183.194]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3C7F160100 for ; Wed, 20 Feb 2019 14:17:43 +0100 (CET) X-Originating-IP: 2.224.242.101 Received: from uno.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay2-d.mail.gandi.net (Postfix) with ESMTPSA id A704C40016; Wed, 20 Feb 2019 13:17:42 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Wed, 20 Feb 2019 14:17:57 +0100 Message-Id: <20190220131757.14004-6-jacopo@jmondi.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190220131757.14004-1-jacopo@jmondi.org> References: <20190220131757.14004-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 5/5] libcamera: ipu3: Add IMGUDevice class X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 20 Feb 2019 13:17:43 -0000 Add an IMGUDevice class that represents all devices associated with the IPU3 imgu unit. Imgu units will be freely assigned to cameras, depending on the number of streams that are required. Add two imgu instances to the ipu3 pipeline, and provide methods to configure the format on the imgu internal components. Signed-off-by: Jacopo Mondi --- src/libcamera/pipeline/ipu3/imgu.cpp | 338 ++++++++++++++++++++++++ src/libcamera/pipeline/ipu3/ipu3.cpp | 71 +++++ src/libcamera/pipeline/ipu3/ipu3.h | 34 +++ src/libcamera/pipeline/ipu3/meson.build | 1 + 4 files changed, 444 insertions(+) create mode 100644 src/libcamera/pipeline/ipu3/imgu.cpp diff --git a/src/libcamera/pipeline/ipu3/imgu.cpp b/src/libcamera/pipeline/ipu3/imgu.cpp new file mode 100644 index 000000000000..7945764eb989 --- /dev/null +++ b/src/libcamera/pipeline/ipu3/imgu.cpp @@ -0,0 +1,338 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * imgu.cpp - IPU3 IMGU unit + */ + +#include +#include + +#include + +#include "geometry.h" +#include "ipu3.h" +#include "media_device.h" +#include "v4l2_device.h" +#include "v4l2_subdevice.h" + +/* + * IMGUDevice represents one of the two 'IMGU' units the Intel IPU3 ISP + * provides. + * + * IMGU units are fed with image data captured from a CIO2 instance and with + * configuration parameters for 3A tuning. + * + * Each IMGU handles 2 video pipes at the time, and each pipe provide 2 image + * outputs ('output' and 'viewfinder') and one statistics metadata output. + * + * TODO: support concurrent pipes + * TODO: support multiple video capture streams (output + viewfinder) + */ + +namespace libcamera { + +LOG_DECLARE_CATEGORY(IPU3) + +IMGUDevice::IMGUDevice() + : owner_(nullptr), imgu_(nullptr), input_(nullptr), + output_(nullptr), viewfinder_(nullptr), stat_(nullptr) +{ +} + +IMGUDevice::~IMGUDevice() +{ + delete imgu_; + delete input_; + delete output_; + delete viewfinder_; + delete stat_; +} + +int IMGUDevice::open(unsigned int index, MediaDevice *imguMediaDev) +{ + int ret; + + std::string imguName = "ipu3-imgu " + std::to_string(index); + MediaEntity *entity = imguMediaDev->getEntityByName(imguName); + if (!entity) { + LOG(IPU3, Error) << "Failed to get entity '" << imguName << "'"; + return -ENODEV; + } + + imgu_ = new V4L2Subdevice(entity); + ret = imgu_->open(); + if (ret) + return ret; + + std::string inputName = imguName + " input"; + entity = imguMediaDev->getEntityByName(inputName); + if (!entity) { + LOG(IPU3, Error) << "Failed to get entity '" << inputName << "'"; + return -ENODEV; + } + + input_ = new V4L2Device(entity); + ret = input_->open(); + if (ret) + return ret; + + std::string outputName = imguName + " output"; + entity = imguMediaDev->getEntityByName(outputName); + if (!entity) { + LOG(IPU3, Error) << "Failed to get entity '" << outputName << "'"; + return -ENODEV; + } + + output_ = new V4L2Device(entity); + ret = output_->open(); + if (ret) + return ret; + + std::string viewfinderName = imguName + " viewfinder"; + entity = imguMediaDev->getEntityByName(viewfinderName); + if (!entity) { + LOG(IPU3, Error) << "Failed to get entity '" + << viewfinderName << "'"; + return -ENODEV; + } + + viewfinder_ = new V4L2Device(entity); + ret = viewfinder_->open(); + if (ret) + return ret; + + std::string statName = imguName + " 3a stat"; + entity = imguMediaDev->getEntityByName(statName); + if (!entity) { + LOG(IPU3, Error) << "Failed to get entity '" + << statName << "'"; + return -ENODEV; + } + + stat_ = new V4L2Device(entity); + ret = stat_->open(); + if (ret) + return ret; + + /* Link entities to configure the IMGU unit for capture. */ + MediaLink *link = imguMediaDev->link(inputName, 0, imguName, 0); + if (!link) { + LOG(IPU3, Error) + << "Failed to get link '" << inputName << "':0 -> '" + << imguName << "':0"; + return -EINVAL; + } + link->setEnabled(true); + + link = imguMediaDev->link(imguName, 2, outputName, 0); + if (!link) { + LOG(IPU3, Error) + << "Failed to get link '" << imguName << "':2 -> '" + << outputName << "':0"; + return -EINVAL; + } + link->setEnabled(true); + + link = imguMediaDev->link(imguName, 3, viewfinderName, 0); + if (!link) { + LOG(IPU3, Error) + << "Failed to get link '" << imguName << "':3 -> '" + << viewfinderName << "':0"; + return -EINVAL; + } + link->setEnabled(true); + + link = imguMediaDev->link(imguName, 4, statName, 0); + if (!link) { + LOG(IPU3, Error) + << "Failed to get link '" << imguName << "':4 -> '" + << statName << "':0"; + return -EINVAL; + } + link->setEnabled(true); + + return 0; +} + +int IMGUDevice::configure(const IPU3DeviceFormat &inputFormat, + const IPU3DeviceFormat &outputFormat) +{ + int ret; + + Rectangle selectionRectangle = {}; + selectionRectangle.x = 0; + selectionRectangle.y = 0; + selectionRectangle.w = inputFormat.width; + selectionRectangle.h = inputFormat.height; + + ret = imgu_->setCrop(IMGU_PAD_INPUT, selectionRectangle); + if (ret) + return ret; + + LOG(IPU3, Debug) + << "'" << imgu_->deviceName() << "':" << IMGU_PAD_INPUT << " = " + << "crop: (0,0)/" << selectionRectangle.w << "x" + << selectionRectangle.h; + + ret = imgu_->setCompose(IMGU_PAD_INPUT, selectionRectangle); + if (ret) + return ret; + + LOG(IPU3, Debug) + << "'" << imgu_->deviceName() << "':" << IMGU_PAD_INPUT << " = " + << "compose: (0,0)/" << selectionRectangle.w << "x" + << selectionRectangle.h; + + V4L2SubdeviceFormat subdevFormat = {}; + ret = imgu_->getFormat(IMGU_PAD_INPUT, &subdevFormat); + if (ret) + return ret; + + LOG(IPU3, Debug) + << "'" << imgu_->deviceName() << "':" << IMGU_PAD_INPUT << " = " + << subdevFormat.width << "x" << subdevFormat.height << " - " + << std::hex << subdevFormat.mbus_code; + + /* + * Apply image format to the 'imgu' unit subdevices. + * + * 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. + */ + subdevFormat = {}; + subdevFormat.width = outputFormat.width; + subdevFormat.height = outputFormat.height; + subdevFormat.mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10; + + ret = imgu_->setFormat(IMGU_PAD_INPUT, &subdevFormat); + if (ret) + return ret; + + LOG(IPU3, Debug) + << "'" << imgu_->deviceName() << "':" << IMGU_PAD_INPUT << " = " + << subdevFormat.width << "x" << subdevFormat.height << " - " + << std::hex << subdevFormat.mbus_code; + + /* + * Configure the 'imgu output' and 'imgu 3a stat' pads with the + * desired output image sizes. + */ + subdevFormat = {}; + subdevFormat.width = outputFormat.width;; + subdevFormat.height = outputFormat.height; + subdevFormat.mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10; + + ret = imgu_->setFormat(IMGU_PAD_OUTPUT, &subdevFormat); + if (ret) + return ret; + + LOG(IPU3, Debug) + << "'" << imgu_->deviceName() << "':" << IMGU_PAD_OUTPUT << " = " + << subdevFormat.width << "x" << subdevFormat.height << " - " + << std::hex << subdevFormat.mbus_code; + + subdevFormat = {}; + subdevFormat.width = outputFormat.width;; + subdevFormat.height = outputFormat.height; + subdevFormat.mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10; + + ret = imgu_->setFormat(IMGU_PAD_STAT, &subdevFormat); + if (ret) + return ret; + + LOG(IPU3, Debug) + << "'" << imgu_->deviceName() << "':" << IMGU_PAD_STAT << " = " + << subdevFormat.width << "x" << subdevFormat.height << " - " + << std::hex << subdevFormat.mbus_code; + + /* + * FIXME: Set the 'imgu viewfinder' to hardcoded size + * It shall be configured as secondary stream when support for + * multiple streams is added. + */ + subdevFormat = {}; + subdevFormat.width = outputFormat.width / 2; + subdevFormat.height = outputFormat.height / 2; + subdevFormat.mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10; + + ret = imgu_->setFormat(IMGU_PAD_VF, &subdevFormat); + if (ret) + return ret; + + LOG(IPU3, Debug) + << "'" << imgu_->deviceName() << "':" << IMGU_PAD_VF << " = " + << subdevFormat.width << "x" << subdevFormat.height << " - " + << std::hex << subdevFormat.mbus_code; + + /* + * Apply the CIO2 provided 'inputFormat' to the 'imgu input' video + * device, and the requested 'outputFormat' to the to the 'imgu output' + * 'imgu viewfinder' and 'imgu 3a stat' nodes. + * + * FIXME: Keep the viewfinder size reduced. + */ + V4L2DeviceFormat devFormat = {}; + devFormat.width = inputFormat.width; + devFormat.height = inputFormat.height; + devFormat.fourcc = inputFormat.fourcc; + + ret = input_->setFormat(&devFormat); + if (ret) + return ret; + + LOG(IPU3, Debug) + << "'" << input_->driverName() << "': " + << devFormat.width << "x" << devFormat.height + << "- 0x" << std::hex << devFormat.fourcc << " planes: " + << devFormat.planesCount; + + devFormat = {}; + devFormat.width = outputFormat.width; + devFormat.height = outputFormat.height; + devFormat.fourcc = outputFormat.fourcc; + V4L2DeviceFormat tmpFormat = devFormat; + + ret = output_->setFormat(&tmpFormat); + if (ret) + return ret; + + LOG(IPU3, Debug) + << "'" << output_->driverName() << "': " + << tmpFormat.width << "x" << tmpFormat.height + << "- 0x" << std::hex << tmpFormat.fourcc << " planes: " + << tmpFormat.planesCount; + + tmpFormat = devFormat; + ret = stat_->setFormat(&tmpFormat); + if (ret) + return ret; + + LOG(IPU3, Debug) + << "'" << stat_->driverName() << "': " + << tmpFormat.width << "x" << tmpFormat.height + << "- 0x" << std::hex << tmpFormat.fourcc << " planes: " + << tmpFormat.planesCount; + + tmpFormat = devFormat; + tmpFormat.width = devFormat.width / 2; + tmpFormat.height = devFormat.height / 2; + + ret = viewfinder_->setFormat(&tmpFormat); + if (ret) + return ret; + + LOG(IPU3, Debug) + << "'" << viewfinder_->driverName() << "': " + << tmpFormat.width << "x" << tmpFormat.height + << "- 0x" << std::hex << tmpFormat.fourcc << " planes: " + << tmpFormat.planesCount; + + return 0; +} + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index 5fcdd6335db6..8b4c88048f6c 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -64,6 +64,7 @@ int PipelineHandlerIPU3::configureStreams(Camera *camera, Stream *stream = &cameraData(camera)->stream_; StreamConfiguration *cfg = &config[stream]; IPU3DeviceFormat outputFormat = {}; + IMGUDevice *imgu; int ret; /* Validate the requested image format and resolution. */ @@ -75,10 +76,46 @@ int PipelineHandlerIPU3::configureStreams(Camera *camera, outputFormat.height = cfg->height; outputFormat.fourcc = cfg->pixelFormat; + /* + * FIXME: as of now, a single stream is supported, so we can claim + * the first available imgu unit, if any. When multiple streams per + * camera will be supported, a single camera could use both the imgu + * units and prevent other cameras to be used. + */ + if (imgu0_.available()) + imgu = &imgu0_; + else if (imgu1_.available()) + imgu = &imgu1_; + else + return -EBUSY; + + /* + * FIXME: the imgu unit shall be released when streams gets released. + * How to deal with cross-camera dependencies ? + */ + imgu->acquire(camera); + + /* + * Configure the CIO2 unit to provide images in a resolution that + * satisfies the desired output format. + */ ret = cio2->configure(outputFormat); if (ret) return ret; + /* + * Get the CIO2 output format and apply it to the IMGU input and + * apply the output format to the IMGU output. + * + * Hardcode the fourcc code output from CIO2 to IMGU. + */ + IPU3DeviceFormat inputFormat = cio2->format(); + inputFormat.fourcc = V4L2_PIX_FMT_IPU3_SGRBG10; + + ret = imgu->configure(inputFormat, outputFormat); + if (ret) + return ret; + return 0; } @@ -205,12 +242,22 @@ bool PipelineHandlerIPU3::match(DeviceEnumerator *enumerator) if (cio2_->disableLinks()) goto error_close_cio2; + if (imgu_->open()) + goto error_close_cio2; + registerCameras(); + if (registerImgus()) + goto error_close_imgu; + cio2_->close(); + imgu_->close(); return true; +error_close_imgu: + imgu_->close(); + error_close_cio2: cio2_->close(); @@ -221,6 +268,30 @@ error_release_mdev: return false; } +/* + * IPU3 has two 'imgu' units which can be freely assigned to cameras; + * + * Create instances for both the units, creating video devices and subdevices + * associated with an instance. + */ + +int PipelineHandlerIPU3::registerImgus() +{ + int ret = imgu0_.open(0, imgu_.get()); + if (ret) { + LOG(IPU3, Error) << "Failed to open 'imgu0' unit"; + return ret; + } + + ret = imgu1_.open(1, imgu_.get()); + if (ret) { + LOG(IPU3, Error) << "Failed to open 'imgu1' unit"; + return ret; + } + + 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 diff --git a/src/libcamera/pipeline/ipu3/ipu3.h b/src/libcamera/pipeline/ipu3/ipu3.h index 2a8b6f13b1c7..83d6e7cf27db 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.h +++ b/src/libcamera/pipeline/ipu3/ipu3.h @@ -57,6 +57,36 @@ public: IPU3DeviceFormat cio2Format_; }; +class IMGUDevice +{ +public: + static constexpr unsigned int IMGU_PAD_INPUT = 0; + static constexpr unsigned int IMGU_PAD_OUTPUT = 2; + static constexpr unsigned int IMGU_PAD_VF = 3; + static constexpr unsigned int IMGU_PAD_STAT = 4; + + IMGUDevice(); + IMGUDevice(const IMGUDevice &) = delete; + ~IMGUDevice(); + + Camera *owner_; + bool available() const { return owner_ == nullptr; } + void acquire(Camera *camera) { owner_ = camera; } + void release() { owner_ = nullptr; } + + int open(unsigned int index, MediaDevice *imguMediaDev); + + int configure(const IPU3DeviceFormat &cio2Format, + const IPU3DeviceFormat &imguFormat); + + V4L2Subdevice *imgu_; + V4L2Device *input_; + V4L2Device *output_; + V4L2Device *viewfinder_; + V4L2Device *stat_; + /* TODO: add param video device for 3A tuning */ +}; + class PipelineHandlerIPU3 : public PipelineHandler { public: @@ -95,10 +125,14 @@ private: PipelineHandler::cameraData(camera)); } + int registerImgus(); void registerCameras(); std::shared_ptr cio2_; std::shared_ptr imgu_; + + IMGUDevice imgu0_; + IMGUDevice imgu1_; }; } /* namespace libcamera */ diff --git a/src/libcamera/pipeline/ipu3/meson.build b/src/libcamera/pipeline/ipu3/meson.build index fcb2d319d517..67f1a5e656d9 100644 --- a/src/libcamera/pipeline/ipu3/meson.build +++ b/src/libcamera/pipeline/ipu3/meson.build @@ -1,4 +1,5 @@ libcamera_sources += files([ 'cio2.cpp', + 'imgu.cpp', 'ipu3.cpp', ])