Show a patch.

GET /api/patches/9449/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 9449,
    "url": "https://patchwork.libcamera.org/api/patches/9449/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/9449/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20200902104410.7569-7-david.plowman@raspberrypi.com>",
    "date": "2020-09-02T10:44:08",
    "name": "[libcamera-devel,v6,6/8] libcamera: raspberrypi: Set camera flips correctly from user transform",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "87e7bc633cd873d1264614c4eb466114f6390ee6",
    "submitter": {
        "id": 42,
        "url": "https://patchwork.libcamera.org/api/people/42/?format=api",
        "name": "David Plowman",
        "email": "david.plowman@raspberrypi.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/9449/mbox/",
    "series": [
        {
            "id": 1256,
            "url": "https://patchwork.libcamera.org/api/series/1256/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=1256",
            "date": "2020-09-02T10:44:02",
            "name": "2D transforms",
            "version": 6,
            "mbox": "https://patchwork.libcamera.org/series/1256/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/9449/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/9449/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>",
        "X-Original-To": "parsemail@patchwork.libcamera.org",
        "Delivered-To": "parsemail@patchwork.libcamera.org",
        "Received": [
            "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 59F72BF019\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  2 Sep 2020 10:44:23 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 1B2D1629DD;\n\tWed,  2 Sep 2020 12:44:23 +0200 (CEST)",
            "from mail-wm1-x32c.google.com (mail-wm1-x32c.google.com\n\t[IPv6:2a00:1450:4864:20::32c])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 5D3D1629D3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  2 Sep 2020 12:44:21 +0200 (CEST)",
            "by mail-wm1-x32c.google.com with SMTP id b79so3992675wmb.4\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 02 Sep 2020 03:44:21 -0700 (PDT)",
            "from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72])\n\tby smtp.gmail.com with ESMTPSA id\n\tm3sm5583062wmb.26.2020.09.02.03.44.19\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tWed, 02 Sep 2020 03:44:20 -0700 (PDT)"
        ],
        "Authentication-Results": "lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=raspberrypi.com header.i=@raspberrypi.com\n\theader.b=\"cnjOQD7L\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google;\n\th=from:to:cc:subject:date:message-id:in-reply-to:references\n\t:mime-version:content-transfer-encoding;\n\tbh=9S57DD6Y8ShJRT8lIuO9PZgcgBB6SXr5dhY17Tdvgvg=;\n\tb=cnjOQD7Lm6zbgnTmdkNt05BiHONFYi349B6fxEj4N8ohnVLMXdSv6FawbsMwz6whMG\n\tsUSVQFz95tJTjiaNZWiJyreo8pdxk2529NrrgHeR+qb3a7SSup+wQ57tdV20itSq9/PI\n\ti8btAKgMpaZarfIzpaZRupP/cA3sGLVgNaJb/HLgMSMW4F+mdQwrx0BvRwajvbminL0/\n\tRJJMIeTaDexySeZ14rSn6+Pf0c7a5fFmU4SWxMoECNgQT0kMYFi+oMLJI3tVlbA6CatS\n\ttYuISjJorOZr9bH0qF9Qc9U3Dz3hchyHdv6vDy0JyeGd3+3nj59PakfErcj2YxfII/SW\n\te2Cw==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references:mime-version:content-transfer-encoding;\n\tbh=9S57DD6Y8ShJRT8lIuO9PZgcgBB6SXr5dhY17Tdvgvg=;\n\tb=EuBS4Eq7HDbntDQ7Pr3PRFU9sdslpuv0uUMH+isWjhuGh3iHSDk9QN/BUM7MkZPSkF\n\tsLDxQRX3uljEE8tzQMjdRSPoRQlmhcAKkuZbAZWgL0Bn2uWTyQmeVE2luM2Bk4SZvbVb\n\tP360eQnvFHSbO76Q2ze4d1DRKznwGTPAyXQie5wQlfi3DE5e90YvX5cojDBACG/7A0QB\n\tuJDo0YaZ2FKd4KJxi9sQtoxpoq2zY98cGaYxQflPVQUwQ0dTUzT+PyFlsw4xynKXWAx8\n\tJLC7hEm4tPIoCVReSypjqZ/wi0hDQOCoEYjLTquPyIuRY3SXuA21cfTErbxKGudQVegj\n\tLiNA==",
        "X-Gm-Message-State": "AOAM530khw/OLUrKYvQVsR5gcemW0lMULTyHfqgunVccgnp8H8C7Y+VS\n\tPqHOlciTk+NYlAYkgtuVnnumGVUJdlYiPg==",
        "X-Google-Smtp-Source": "ABdhPJxv9Vgqw6gLWjTqpWyfKLDm0aOk34kCopxWVT8ErPEFYW43zqLS6kkPCvs8oq7T8GgTk7s7ew==",
        "X-Received": "by 2002:a7b:cb4d:: with SMTP id v13mr55992wmj.56.1599043460572; \n\tWed, 02 Sep 2020 03:44:20 -0700 (PDT)",
        "From": "David Plowman <david.plowman@raspberrypi.com>",
        "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\n\tflips correctly from user transform",
        "X-BeenThere": "libcamera-devel@lists.libcamera.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "<libcamera-devel.lists.libcamera.org>",
        "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>",
        "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>",
        "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>",
        "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "The Raspberry Pi pipeline handler allows all transforms except those\ninvolving a transpose. The user transform is combined with any\ninherent rotation of the camera, and the camera's H and V flip bits\nare set accordingly.\n\nNote that the validate() method has to work out what the final Bayer\norder of any raw streams will be, before configure() actually applies\nthe transform to the sensor. We make a note of the \"native\"\n(untransformed) Bayer order when the system starts, so that we can\ndeduce transformed Bayer orders more easily.\n\nSigned-off-by: David Plowman <david.plowman@raspberrypi.com>\n---\n .../pipeline/raspberrypi/raspberrypi.cpp      | 146 ++++++++++++++++--\n 1 file changed, 134 insertions(+), 12 deletions(-)",
    "diff": "diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\nindex 1087257..29112da 100644\n--- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n+++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n@@ -23,6 +23,7 @@\n \n #include <linux/videodev2.h>\n \n+#include \"libcamera/internal/bayer_format.h\"\n #include \"libcamera/internal/camera_sensor.h\"\n #include \"libcamera/internal/device_enumerator.h\"\n #include \"libcamera/internal/ipa_manager.h\"\n@@ -287,6 +288,7 @@ class RPiCameraData : public CameraData\n public:\n \tRPiCameraData(PipelineHandler *pipe)\n \t\t: CameraData(pipe), sensor_(nullptr), state_(State::Stopped),\n+\t\t  supportsFlips_(false), flipsAlterBayerOrder_(false),\n \t\t  dropFrame_(false), ispOutputCount_(0)\n \t{\n \t}\n@@ -335,6 +337,17 @@ public:\n \tstd::queue<FrameBuffer *> embeddedQueue_;\n \tstd::deque<Request *> requestQueue_;\n \n+\t/*\n+\t * The remaining field are for handling horizontal and vertical flips\n+\t * that sensors may support.\n+\t */\n+\tbool supportsFlips_;\n+\tbool flipsAlterBayerOrder_;\n+\tBayerFormat::Order nativeBayerOrder_;\n+\n+\tTransform combinedTransform_;\n+\tTransform userTransform_;\n+\n private:\n \tvoid checkRequestCompleted();\n \tvoid tryRunPipeline();\n@@ -348,12 +361,12 @@ private:\n class RPiCameraConfiguration : public CameraConfiguration\n {\n public:\n-\tRPiCameraConfiguration(const RPiCameraData *data);\n+\tRPiCameraConfiguration(RPiCameraData *data);\n \n \tStatus validate() override;\n \n private:\n-\tconst RPiCameraData *data_;\n+\tRPiCameraData *data_;\n };\n \n class PipelineHandlerRPi : public PipelineHandler\n@@ -388,7 +401,7 @@ private:\n \tMediaDevice *isp_;\n };\n \n-RPiCameraConfiguration::RPiCameraConfiguration(const RPiCameraData *data)\n+RPiCameraConfiguration::RPiCameraConfiguration(RPiCameraData *data)\n \t: CameraConfiguration(), data_(data)\n {\n }\n@@ -400,11 +413,56 @@ CameraConfiguration::Status RPiCameraConfiguration::validate()\n \tif (config_.empty())\n \t\treturn Invalid;\n \n-\tif (transform != Transform::Identity) {\n-\t\ttransform = Transform::Identity;\n+\t/*\n+\t * What if the platform has a non-90 degree rotation? We can't even\n+\t * \"adjust\" the configuration and carry on. Alternatively, raising an\n+\t * error means the platform can never run. Let's just print a warning\n+\t * and continue regardless; the rotation is effectively set to zero.\n+\t */\n+\tint32_t rotation = data_->sensor_->properties().get(properties::Rotation);\n+\tbool success;\n+\tTransform combined = transform * transformFromRotation(rotation, &success);\n+\tif (!success)\n+\t\tLOG(RPI, Warning) << \"Invalid rotation of \" << rotation\n+\t\t\t\t  << \" degrees - ignoring\";\n+\n+\t/*\n+\t * We combine the platform and user transform, but must \"adjust away\"\n+\t * any combined result that includes a transform, as we can't do those.\n+\t * In this case, flipping only the transpose bit is helpful to\n+\t * applications - they either get the transform they requested, or have\n+\t * to do a simple transpose themselves (they don't have to worry about\n+\t * the other possible cases).\n+\t */\n+\tif (!!(combined & Transform::Transpose)) {\n+\t\t/*\n+\t\t * Flipping the transpose bit in \"transform\" flips it in\n+\t\t * combined result too (as it's the last thing that happens),\n+\t\t * which is of course clearing it.\n+\t\t */\n+\t\ttransform ^= Transform::Transpose;\n+\t\tcombined &= ~Transform::Transpose;\n \t\tstatus = Adjusted;\n \t}\n \n+\t/*\n+\t * We also check if the sensor doesn't do h/vflips at all, in which\n+\t * case we clear them, and the application will have to do everything.\n+\t */\n+\tif (!data_->supportsFlips_ && !!combined) {\n+\t\ttransform &= Transform::Transpose;\n+\t\tcombined = Transform::Identity;\n+\t\tstatus = Adjusted;\n+\t}\n+\n+\t/*\n+\t * Store the final combined transform that configure() will need to\n+\t * apply to the sensor, and the associated user transform that gives\n+\t * rise to it (which is what the IPAs will see).\n+\t */\n+\tdata_->combinedTransform_ = combined;\n+\tdata_->userTransform_ = transform;\n+\n \tunsigned int rawCount = 0, outCount = 0, count = 0, maxIndex = 0;\n \tstd::pair<int, Size> outSize[2];\n \tSize maxSize;\n@@ -420,7 +478,23 @@ CameraConfiguration::Status RPiCameraConfiguration::validate()\n \t\t\tif (ret)\n \t\t\t\treturn Invalid;\n \n-\t\t\tPixelFormat sensorPixFormat = sensorFormat.fourcc.toPixelFormat();\n+\t\t\t/*\n+\t\t\t * Some sensors change their Bayer order when they are\n+\t\t\t * h-flipped or v-flipped, according to the transform.\n+\t\t\t * If this one does, we must advertise the transformed\n+\t\t\t * Bayer order in the raw stream. Note how we must\n+\t\t\t * fetch the \"native\" (i.e. untransformed) Bayer order,\n+\t\t\t * because the sensor may currently be flipped!\n+\t\t\t */\n+\t\t\tV4L2PixelFormat fourcc = sensorFormat.fourcc;\n+\t\t\tif (data_->flipsAlterBayerOrder_) {\n+\t\t\t\tBayerFormat bayer(fourcc);\n+\t\t\t\tbayer.order = data_->nativeBayerOrder_;\n+\t\t\t\tbayer = bayer.transform(combined);\n+\t\t\t\tfourcc = bayer.toV4L2PixelFormat();\n+\t\t\t}\n+\n+\t\t\tPixelFormat sensorPixFormat = fourcc.toPixelFormat();\n \t\t\tif (cfg.size != sensorFormat.size ||\n \t\t\t    cfg.pixelFormat != sensorPixFormat) {\n \t\t\t\tcfg.size = sensorFormat.size;\n@@ -967,6 +1041,48 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator)\n \t/* Initialize the camera properties. */\n \tdata->properties_ = data->sensor_->properties();\n \n+\t/*\n+\t * We cache three things about the sensor in relation to transforms\n+\t * (meaning horizontal and vertical flips).\n+\t *\n+\t * Firstly, does it support them?\n+\t * Secondly, if you use them does it affect the Bayer ordering?\n+\t * Thirdly, what is the \"native\" Bayer order, when no transforms are\n+\t * applied?\n+\t *\n+\t * As part of answering the final question, we reset the camera to\n+\t * no transform at all.\n+\t */\n+\n+\tV4L2VideoDevice *dev = data->unicam_[Unicam::Image].dev();\n+\tconst struct v4l2_query_ext_ctrl *hflipCtrl = dev->controlInfo(V4L2_CID_HFLIP);\n+\tif (hflipCtrl) {\n+\t\t/* We assume it will support vflips too... */\n+\t\tdata->supportsFlips_ = true;\n+\t\tdata->flipsAlterBayerOrder_ = hflipCtrl->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT;\n+\n+\t\tControlList ctrls(dev->controls());\n+\t\tctrls.set(V4L2_CID_HFLIP, 0);\n+\t\tctrls.set(V4L2_CID_VFLIP, 0);\n+\t\tdev->setControls(&ctrls);\n+\t}\n+\n+\t/* Look for a valid Bayer format. */\n+\tV4L2VideoDevice::Formats fmts = dev->formats();\n+\tBayerFormat bayerFormat;\n+\tfor (const auto &iter : fmts) {\n+\t\tV4L2PixelFormat v4l2Format = iter.first;\n+\t\tbayerFormat = BayerFormat(v4l2Format);\n+\t\tif (bayerFormat.isValid())\n+\t\t\tbreak;\n+\t}\n+\n+\tif (!bayerFormat.isValid()) {\n+\t\tLOG(RPI, Error) << \"No Bayer format found\";\n+\t\treturn false;\n+\t}\n+\tdata->nativeBayerOrder_ = bayerFormat.order;\n+\n \t/*\n \t * List the available output streams.\n \t * Currently cannot do Unicam streams!\n@@ -1172,12 +1288,18 @@ int RPiCameraData::configureIPA()\n \t\t\tsensorMetadata_ = result.data[2];\n \t\t}\n \n-\t\t/* Configure the H/V flip controls based on the sensor rotation. */\n-\t\tControlList ctrls(unicam_[Unicam::Image].dev()->controls());\n-\t\tint32_t rotation = sensor_->properties().get(properties::Rotation);\n-\t\tctrls.set(V4L2_CID_HFLIP, static_cast<int32_t>(!!rotation));\n-\t\tctrls.set(V4L2_CID_VFLIP, static_cast<int32_t>(!!rotation));\n-\t\tunicam_[Unicam::Image].dev()->setControls(&ctrls);\n+\t\t/*\n+\t\t * Configure the H/V flip controls based on the combination of\n+\t\t * the sensor and user transform.\n+\t\t */\n+\t\tif (supportsFlips_) {\n+\t\t\tControlList ctrls(unicam_[Unicam::Image].dev()->controls());\n+\t\t\tctrls.set(V4L2_CID_HFLIP,\n+\t\t\t\t  static_cast<int32_t>(!!(combinedTransform_ & Transform::HFlip)));\n+\t\t\tctrls.set(V4L2_CID_VFLIP,\n+\t\t\t\t  static_cast<int32_t>(!!(combinedTransform_ & Transform::VFlip)));\n+\t\t\tunicam_[Unicam::Image].dev()->setControls(&ctrls);\n+\t\t}\n \t}\n \n \tif (result.operation & RPI_IPA_CONFIG_SENSOR) {\n",
    "prefixes": [
        "libcamera-devel",
        "v6",
        "6/8"
    ]
}