From patchwork Tue Mar 12 12:12:34 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 721 Return-Path: Received: from relay5-d.mail.gandi.net (relay5-d.mail.gandi.net [217.70.183.197]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 42791611A2 for ; Tue, 12 Mar 2019 13:12:23 +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 relay5-d.mail.gandi.net (Postfix) with ESMTPSA id BE6CE1C0005; Tue, 12 Mar 2019 12:12:22 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Tue, 12 Mar 2019 13:12:34 +0100 Message-Id: <20190312121242.2253-7-jacopo@jmondi.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190312121242.2253-1-jacopo@jmondi.org> References: <20190312121242.2253-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 06/14] libcamera: ipu3: Propagate image format 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, 12 Mar 2019 12:12:23 -0000 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 --- src/libcamera/pipeline/ipu3/ipu3.cpp | 220 +++++++++++++++++++++++---- 1 file changed, 189 insertions(+), 31 deletions(-) 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 #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 &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