From patchwork Wed Sep 2 10:44:08 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9449 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 59F72BF019 for ; Wed, 2 Sep 2020 10:44:23 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1B2D1629DD; Wed, 2 Sep 2020 12:44:23 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="cnjOQD7L"; dkim-atps=neutral Received: from mail-wm1-x32c.google.com (mail-wm1-x32c.google.com [IPv6:2a00:1450:4864:20::32c]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5D3D1629D3 for ; Wed, 2 Sep 2020 12:44:21 +0200 (CEST) Received: by mail-wm1-x32c.google.com with SMTP id b79so3992675wmb.4 for ; Wed, 02 Sep 2020 03:44:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=9S57DD6Y8ShJRT8lIuO9PZgcgBB6SXr5dhY17Tdvgvg=; b=cnjOQD7Lm6zbgnTmdkNt05BiHONFYi349B6fxEj4N8ohnVLMXdSv6FawbsMwz6whMG sUSVQFz95tJTjiaNZWiJyreo8pdxk2529NrrgHeR+qb3a7SSup+wQ57tdV20itSq9/PI i8btAKgMpaZarfIzpaZRupP/cA3sGLVgNaJb/HLgMSMW4F+mdQwrx0BvRwajvbminL0/ RJJMIeTaDexySeZ14rSn6+Pf0c7a5fFmU4SWxMoECNgQT0kMYFi+oMLJI3tVlbA6CatS tYuISjJorOZr9bH0qF9Qc9U3Dz3hchyHdv6vDy0JyeGd3+3nj59PakfErcj2YxfII/SW e2Cw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=9S57DD6Y8ShJRT8lIuO9PZgcgBB6SXr5dhY17Tdvgvg=; b=EuBS4Eq7HDbntDQ7Pr3PRFU9sdslpuv0uUMH+isWjhuGh3iHSDk9QN/BUM7MkZPSkF sLDxQRX3uljEE8tzQMjdRSPoRQlmhcAKkuZbAZWgL0Bn2uWTyQmeVE2luM2Bk4SZvbVb P360eQnvFHSbO76Q2ze4d1DRKznwGTPAyXQie5wQlfi3DE5e90YvX5cojDBACG/7A0QB uJDo0YaZ2FKd4KJxi9sQtoxpoq2zY98cGaYxQflPVQUwQ0dTUzT+PyFlsw4xynKXWAx8 JLC7hEm4tPIoCVReSypjqZ/wi0hDQOCoEYjLTquPyIuRY3SXuA21cfTErbxKGudQVegj LiNA== X-Gm-Message-State: AOAM530khw/OLUrKYvQVsR5gcemW0lMULTyHfqgunVccgnp8H8C7Y+VS PqHOlciTk+NYlAYkgtuVnnumGVUJdlYiPg== X-Google-Smtp-Source: ABdhPJxv9Vgqw6gLWjTqpWyfKLDm0aOk34kCopxWVT8ErPEFYW43zqLS6kkPCvs8oq7T8GgTk7s7ew== X-Received: by 2002:a7b:cb4d:: with SMTP id v13mr55992wmj.56.1599043460572; Wed, 02 Sep 2020 03:44:20 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id m3sm5583062wmb.26.2020.09.02.03.44.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 02 Sep 2020 03:44:20 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Wed, 2 Sep 2020 11:44:08 +0100 Message-Id: <20200902104410.7569-7-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200902104410.7569-1-david.plowman@raspberrypi.com> References: <20200902104410.7569-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v6 6/8] libcamera: raspberrypi: Set camera flips correctly from user transform X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The Raspberry Pi pipeline handler allows all transforms except those involving a transpose. The user transform is combined with any inherent rotation of the camera, and the camera's H and V flip bits are set accordingly. Note that the validate() method has to work out what the final Bayer order of any raw streams will be, before configure() actually applies the transform to the sensor. We make a note of the "native" (untransformed) Bayer order when the system starts, so that we can deduce transformed Bayer orders more easily. Signed-off-by: David Plowman Reviewed-by: Kieran Bingham --- .../pipeline/raspberrypi/raspberrypi.cpp | 146 ++++++++++++++++-- 1 file changed, 134 insertions(+), 12 deletions(-) diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index 1087257..29112da 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -23,6 +23,7 @@ #include +#include "libcamera/internal/bayer_format.h" #include "libcamera/internal/camera_sensor.h" #include "libcamera/internal/device_enumerator.h" #include "libcamera/internal/ipa_manager.h" @@ -287,6 +288,7 @@ class RPiCameraData : public CameraData public: RPiCameraData(PipelineHandler *pipe) : CameraData(pipe), sensor_(nullptr), state_(State::Stopped), + supportsFlips_(false), flipsAlterBayerOrder_(false), dropFrame_(false), ispOutputCount_(0) { } @@ -335,6 +337,17 @@ public: std::queue embeddedQueue_; std::deque requestQueue_; + /* + * The remaining field are for handling horizontal and vertical flips + * that sensors may support. + */ + bool supportsFlips_; + bool flipsAlterBayerOrder_; + BayerFormat::Order nativeBayerOrder_; + + Transform combinedTransform_; + Transform userTransform_; + private: void checkRequestCompleted(); void tryRunPipeline(); @@ -348,12 +361,12 @@ private: class RPiCameraConfiguration : public CameraConfiguration { public: - RPiCameraConfiguration(const RPiCameraData *data); + RPiCameraConfiguration(RPiCameraData *data); Status validate() override; private: - const RPiCameraData *data_; + RPiCameraData *data_; }; class PipelineHandlerRPi : public PipelineHandler @@ -388,7 +401,7 @@ private: MediaDevice *isp_; }; -RPiCameraConfiguration::RPiCameraConfiguration(const RPiCameraData *data) +RPiCameraConfiguration::RPiCameraConfiguration(RPiCameraData *data) : CameraConfiguration(), data_(data) { } @@ -400,11 +413,56 @@ CameraConfiguration::Status RPiCameraConfiguration::validate() if (config_.empty()) return Invalid; - if (transform != Transform::Identity) { - transform = Transform::Identity; + /* + * What if the platform has a non-90 degree rotation? We can't even + * "adjust" the configuration and carry on. Alternatively, raising an + * error means the platform can never run. Let's just print a warning + * and continue regardless; the rotation is effectively set to zero. + */ + int32_t rotation = data_->sensor_->properties().get(properties::Rotation); + bool success; + Transform combined = transform * transformFromRotation(rotation, &success); + if (!success) + LOG(RPI, Warning) << "Invalid rotation of " << rotation + << " degrees - ignoring"; + + /* + * We combine the platform and user transform, but must "adjust away" + * any combined result that includes a transform, as we can't do those. + * In this case, flipping only the transpose bit is helpful to + * applications - they either get the transform they requested, or have + * to do a simple transpose themselves (they don't have to worry about + * the other possible cases). + */ + if (!!(combined & Transform::Transpose)) { + /* + * Flipping the transpose bit in "transform" flips it in + * combined result too (as it's the last thing that happens), + * which is of course clearing it. + */ + transform ^= Transform::Transpose; + combined &= ~Transform::Transpose; status = Adjusted; } + /* + * We also check if the sensor doesn't do h/vflips at all, in which + * case we clear them, and the application will have to do everything. + */ + if (!data_->supportsFlips_ && !!combined) { + transform &= Transform::Transpose; + combined = Transform::Identity; + status = Adjusted; + } + + /* + * Store the final combined transform that configure() will need to + * apply to the sensor, and the associated user transform that gives + * rise to it (which is what the IPAs will see). + */ + data_->combinedTransform_ = combined; + data_->userTransform_ = transform; + unsigned int rawCount = 0, outCount = 0, count = 0, maxIndex = 0; std::pair outSize[2]; Size maxSize; @@ -420,7 +478,23 @@ CameraConfiguration::Status RPiCameraConfiguration::validate() if (ret) return Invalid; - PixelFormat sensorPixFormat = sensorFormat.fourcc.toPixelFormat(); + /* + * Some sensors change their Bayer order when they are + * h-flipped or v-flipped, according to the transform. + * If this one does, we must advertise the transformed + * Bayer order in the raw stream. Note how we must + * fetch the "native" (i.e. untransformed) Bayer order, + * because the sensor may currently be flipped! + */ + V4L2PixelFormat fourcc = sensorFormat.fourcc; + if (data_->flipsAlterBayerOrder_) { + BayerFormat bayer(fourcc); + bayer.order = data_->nativeBayerOrder_; + bayer = bayer.transform(combined); + fourcc = bayer.toV4L2PixelFormat(); + } + + PixelFormat sensorPixFormat = fourcc.toPixelFormat(); if (cfg.size != sensorFormat.size || cfg.pixelFormat != sensorPixFormat) { cfg.size = sensorFormat.size; @@ -967,6 +1041,48 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator) /* Initialize the camera properties. */ data->properties_ = data->sensor_->properties(); + /* + * We cache three things about the sensor in relation to transforms + * (meaning horizontal and vertical flips). + * + * Firstly, does it support them? + * Secondly, if you use them does it affect the Bayer ordering? + * Thirdly, what is the "native" Bayer order, when no transforms are + * applied? + * + * As part of answering the final question, we reset the camera to + * no transform at all. + */ + + V4L2VideoDevice *dev = data->unicam_[Unicam::Image].dev(); + const struct v4l2_query_ext_ctrl *hflipCtrl = dev->controlInfo(V4L2_CID_HFLIP); + if (hflipCtrl) { + /* We assume it will support vflips too... */ + data->supportsFlips_ = true; + data->flipsAlterBayerOrder_ = hflipCtrl->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + ControlList ctrls(dev->controls()); + ctrls.set(V4L2_CID_HFLIP, 0); + ctrls.set(V4L2_CID_VFLIP, 0); + dev->setControls(&ctrls); + } + + /* Look for a valid Bayer format. */ + V4L2VideoDevice::Formats fmts = dev->formats(); + BayerFormat bayerFormat; + for (const auto &iter : fmts) { + V4L2PixelFormat v4l2Format = iter.first; + bayerFormat = BayerFormat(v4l2Format); + if (bayerFormat.isValid()) + break; + } + + if (!bayerFormat.isValid()) { + LOG(RPI, Error) << "No Bayer format found"; + return false; + } + data->nativeBayerOrder_ = bayerFormat.order; + /* * List the available output streams. * Currently cannot do Unicam streams! @@ -1172,12 +1288,18 @@ int RPiCameraData::configureIPA() sensorMetadata_ = result.data[2]; } - /* Configure the H/V flip controls based on the sensor rotation. */ - ControlList ctrls(unicam_[Unicam::Image].dev()->controls()); - int32_t rotation = sensor_->properties().get(properties::Rotation); - ctrls.set(V4L2_CID_HFLIP, static_cast(!!rotation)); - ctrls.set(V4L2_CID_VFLIP, static_cast(!!rotation)); - unicam_[Unicam::Image].dev()->setControls(&ctrls); + /* + * Configure the H/V flip controls based on the combination of + * the sensor and user transform. + */ + if (supportsFlips_) { + ControlList ctrls(unicam_[Unicam::Image].dev()->controls()); + ctrls.set(V4L2_CID_HFLIP, + static_cast(!!(combinedTransform_ & Transform::HFlip))); + ctrls.set(V4L2_CID_VFLIP, + static_cast(!!(combinedTransform_ & Transform::VFlip))); + unicam_[Unicam::Image].dev()->setControls(&ctrls); + } } if (result.operation & RPI_IPA_CONFIG_SENSOR) {