From patchwork Tue Mar 26 08:38: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: 807 Return-Path: Received: from relay10.mail.gandi.net (relay10.mail.gandi.net [217.70.178.230]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4027F611A7 for ; Tue, 26 Mar 2019 09:38:38 +0100 (CET) Received: from uno.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay10.mail.gandi.net (Postfix) with ESMTPSA id A9C65240006; Tue, 26 Mar 2019 08:38:37 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Tue, 26 Mar 2019 09:38:55 +0100 Message-Id: <20190326083902.26121-13-jacopo@jmondi.org> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190326083902.26121-1-jacopo@jmondi.org> References: <20190326083902.26121-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 12/19] libcamera: ipu3: Apply image format to the pipeline 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: Tue, 26 Mar 2019 08:38:39 -0000 Apply the requested image format to the CIO2 device, and apply the resulting adjusted one to the the ImgU subdevice and its input and output video devices. Signed-off-by: Jacopo Mondi --- src/libcamera/pipeline/ipu3/ipu3.cpp | 300 +++++++++++++++++++++++---- 1 file changed, 265 insertions(+), 35 deletions(-) diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index 0a059b75cadf..63b84706b9b2 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -52,6 +52,20 @@ public: static constexpr unsigned int PAD_VF = 3; static constexpr unsigned int PAD_STAT = 4; + /* ImgU output identifiers: used as indexes for the below maps. */ + enum OutputId { + MAIN_OUTPUT, + SECONDARY_OUTPUT, + STAT, + }; + + /* ImgU output descriptor: group data specific to an ImgU output. */ + struct outputDesc { + V4L2Device *dev; + unsigned int pad; + std::string name; + }; + ImgUDevice() : imgu_(nullptr), input_(nullptr), output_(nullptr), viewfinder_(nullptr), stat_(nullptr) @@ -67,7 +81,25 @@ public: delete stat_; } + /* Imgu output map accessors. */ + V4L2Device *outputDevice(enum OutputId id) + { + return outputMap[id].dev; + } + unsigned int outputPad(enum OutputId id) + { + return outputMap[id].pad; + } + const std::string &outputName(enum OutputId id) + { + return outputMap[id].name; + } + int init(MediaDevice *media, unsigned int index); + int configureInput(const StreamConfiguration &config, + const V4L2SubdeviceFormat &cio2Format); + int configureOutput(enum OutputId id, + const StreamConfiguration &config); unsigned int index_; std::string name_; @@ -79,6 +111,9 @@ public: V4L2Device *viewfinder_; V4L2Device *stat_; /* \todo Add param video device for 3A tuning */ + + /* ImgU output map: associate an output id with its descriptor. */ + std::map outputMap; }; class CIO2Device @@ -98,6 +133,8 @@ public: const std::string &name() const; int init(const MediaDevice *media, unsigned int index); + int configure(const StreamConfiguration &config, + V4L2SubdeviceFormat *format); V4L2Device *output_; V4L2Subdevice *csi2_; @@ -203,61 +240,62 @@ int PipelineHandlerIPU3::configureStreams(Camera *camera, std::map &config) { IPU3CameraData *data = cameraData(camera); - StreamConfiguration *cfg = &config[&data->stream_]; - V4L2Subdevice *sensor = data->cio2_.sensor_; - V4L2Subdevice *csi2 = data->cio2_.csi2_; - V4L2Device *cio2 = data->cio2_.output_; - V4L2SubdeviceFormat subdevFormat = {}; - V4L2DeviceFormat devFormat = {}; + const StreamConfiguration &cfg = config[&data->stream_]; + CIO2Device *cio2 = &data->cio2_; + ImgUDevice *imgu = data->imgu_; int ret; + LOG(IPU3, Info) + << "Requested image format: " << cfg.width << "x" + << cfg.height << "-0x" << 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) and the camera maximum sizes. + * + * \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 size not support: bad alignement"; + return -EINVAL; + } + + SizeRange cio2MaxSize = cio2->maxSizes_.second; + if (cfg.width > cio2MaxSize.maxWidth || + cfg.height > cio2MaxSize.maxHeight) { + LOG(IPU3, Error) << "Stream size not support: too big"; + 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 to the ImgU devices. + */ + V4L2SubdeviceFormat cio2Format = {}; + ret = cio2->configure(cfg, &cio2Format); if (ret) return ret; - subdevFormat.width = cfg->width; - subdevFormat.height = cfg->height; - ret = sensor->setFormat(0, &subdevFormat); + ret = imgu->configureInput(cfg, 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; - } - - ret = csi2->setFormat(0, &subdevFormat); + /* Apply the format to the ImgU output, viewfinder and stat. */ + ret = imgu->configureOutput(ImgUDevice::MAIN_OUTPUT, cfg); if (ret) return ret; - ret = cio2->getFormat(&devFormat); + ret = imgu->configureOutput(ImgUDevice::SECONDARY_OUTPUT, cfg); if (ret) return ret; - devFormat.width = subdevFormat.width; - devFormat.height = subdevFormat.height; - devFormat.fourcc = cfg->pixelFormat; - - ret = cio2->setFormat(&devFormat); + ret = imgu->configureOutput(ImgUDevice::STAT, cfg); if (ret) return ret; - LOG(IPU3, Info) << cio2->driverName() << ": " - << devFormat.width << "x" << devFormat.height - << "- 0x" << std::hex << devFormat.fourcc << " planes: " - << devFormat.planes; - return 0; } @@ -539,16 +577,140 @@ int ImgUDevice::init(MediaDevice *media, unsigned int index) if (ret) return ret; + struct outputDesc desc = {}; + desc.dev = output_; + desc.pad = PAD_OUTPUT; + desc.name = "output"; + outputMap[MAIN_OUTPUT] = desc; + viewfinder_ = V4L2Device::fromEntityName(media, name_ + " viewfinder"); ret = viewfinder_->open(); if (ret) return ret; + desc = {}; + desc.dev = viewfinder_; + desc.pad = PAD_VF; + desc.name = "viewfinder"; + outputMap[SECONDARY_OUTPUT] = desc; + stat_ = V4L2Device::fromEntityName(media, name_ + " 3a stat"); ret = stat_->open(); if (ret) return ret; + desc = {}; + desc.dev = stat_; + desc.pad = PAD_STAT; + desc.name = "stat"; + outputMap[STAT] = desc; + + return 0; +} + +/** + * \brief Configure the ImgU unit input + * \param[in] config The requested GDC format configuration + * \param[in] cio2Format The CIO2 output format to be applied to ImgU input + * + * FIXME: the IPU3 driver implementation shall be changed to use the + * CIO2 output sizes as 'ImgU Input' subdevice sizes, and use the + * GDC output sizes to configure the crop/compose rectangles. + * The current IPU3 driver implementation uses GDC output sizes as + * 'ImgU Input' sizes, and the CIO2 output sizes to configure the + * crop/compose rectangles, contradicting the V4L2 specification. + * + * \return 0 on success or a negative error code otherwise + */ +int ImgUDevice::configureInput(const StreamConfiguration &config, + const V4L2SubdeviceFormat &cio2Format) +{ + int ret; + + /* Configure the ImgU subdevice input pad with the requested sizes. */ + V4L2DeviceFormat inputFormat = {}; + inputFormat.width = cio2Format.width; + inputFormat.height = cio2Format.height; + inputFormat.fourcc = mediaBusToCIO2Format(cio2Format.mbus_code); + inputFormat.planesCount = 1; + + ret = input_->setFormat(&inputFormat); + if (ret) + return ret; + + LOG(IPU3, Debug) << "ImgU input format = " << inputFormat.toString(); + + Rectangle rect = { + .x = 0, + .y = 0, + .w = cio2Format.width, + .h = cio2Format.height, + }; + ret = imgu_->setCrop(PAD_INPUT, &rect); + if (ret) + return ret; + + ret = imgu_->setCompose(PAD_INPUT, &rect); + if (ret) + return ret; + + LOG(IPU3, Debug) << "ImgU input feeder and BDS rectangle = " + << rect.toString(); + + V4L2SubdeviceFormat imguFormat = {}; + imguFormat.width = config.width; + imguFormat.height = config.height; + imguFormat.mbus_code = MEDIA_BUS_FMT_FIXED; + + ret = imgu_->setFormat(PAD_INPUT, &imguFormat); + if (ret) + return ret; + + LOG(IPU3, Debug) << "ImgU GDC format = " << imguFormat.toString(); + + return 0; +} + +/** + * \brief Configure the ImgU unit \a id video output + * \param[in] id The output device node identifier + * \param[in] config The requested configuration + * + * \return 0 on success or a negative error code otherwise + */ +int ImgUDevice::configureOutput(enum OutputId id, + const StreamConfiguration &config) +{ + V4L2Device *output = outputDevice(id); + unsigned int pad = outputPad(id); + const std::string name = outputName(id); + + V4L2SubdeviceFormat imguFormat = {}; + imguFormat.width = config.width; + imguFormat.height = config.height; + imguFormat.mbus_code = MEDIA_BUS_FMT_FIXED; + + int ret = imgu_->setFormat(pad, &imguFormat); + if (ret) + return ret; + + /* No need to apply format to the stat node. */ + if (id == STAT) + return 0; + + 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 " << name << " format = " + << outputFormat.toString(); + return 0; } @@ -650,6 +812,74 @@ int CIO2Device::init(const MediaDevice *media, unsigned int index) return 0; } +/** + * \brief Configure the CIO2 unit + * \param[in] config The requested configuration + * \param[out] format The CIO2 unit output image format to be applied on ImgU + * + * \return 0 on success or a negative error code otherwise + */ +int CIO2Device::configure(const StreamConfiguration &config, + V4L2SubdeviceFormat *format) +{ + unsigned int imageSize = config.width * config.height; + unsigned int best = ~0; + int ret; + + for (auto it : sensor_->formats(0)) { + /* Only consider formats consumable by the CIO2 unit. */ + if (mediaBusToCIO2Format(it.first) < 0) + 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; + + format->width = size.maxWidth; + format->height = size.maxHeight; + format->mbus_code = it.first; + } + } + + /* + * Apply the selected format to the sensor, the CSI-2 receiver and + * the CIO2 output device. + */ + ret = sensor_->setFormat(0, format); + if (ret) + return ret; + + ret = csi2_->setFormat(0, format); + if (ret) + return ret; + + V4L2DeviceFormat cio2Format = {}; + cio2Format.width = format->width; + cio2Format.height = format->height; + cio2Format.fourcc = mediaBusToCIO2Format(format->mbus_code); + cio2Format.planesCount = 1; + + ret = output_->setFormat(&cio2Format); + if (ret) + return ret; + + LOG(IPU3, Debug) << "CIO2 output format = " << cio2Format.toString(); + + return 0; +} + REGISTER_PIPELINE_HANDLER(PipelineHandlerIPU3); } /* namespace libcamera */