[{"id":14888,"web_url":"https://patchwork.libcamera.org/comment/14888/","msgid":"<4b384bc4-4c10-f7fb-f26d-34c294bfaac2@ideasonboard.com>","date":"2021-02-01T17:26:39","subject":"Re: [libcamera-devel] [PATCH v3] libcamera: ipu3: Add rotation to\n\tipu3 pipeline","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Hi Fabian,\n\nOn 22/01/2021 12:01, Fabian Wüthrich wrote:\n> Use the same transformation logic as in the raspberry pipeline to\n> implement rotations in the ipu3 pipeline.\n> \n> Tested on a Surface Book 2 with an experimental driver for OV5693.\n\nAs far as I can see, this doesn't yet support setting the rotation as a\ncontrol from an application? But I don't think we should require that of\nthis patch.\n\nI believe getting this rotation support in will help progress the topic,\nbut I'm weary that we might need to consider later how applications\ndetermine if we have or haven't rotated in the pipeline.\n\nBut I think we can handle that later when we hit more directly.\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\nI believe Jacopo's tag has carried forwards on this too? If so - I'll\nlet Jacopo handle merging.\n\n--\nKieran\n\n\n> \n> Signed-off-by: Fabian Wüthrich <me@fabwu.ch>\n> ---\n> Changes in v2:\n>  - Cache rotationTransform in CameraData\n>  - Use separate controls for sensor\n> \n> Changes in v3:\n>  - Default rotation to 0 if sensor doesn't expose it\n> \n>  src/libcamera/pipeline/ipu3/ipu3.cpp | 90 +++++++++++++++++++++++++++-\n>  1 file changed, 88 insertions(+), 2 deletions(-)\n> \n> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp\n> index 73304ea7..d620acf3 100644\n> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp\n> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp\n> @@ -14,6 +14,7 @@\n>  #include <libcamera/camera.h>\n>  #include <libcamera/control_ids.h>\n>  #include <libcamera/formats.h>\n> +#include <libcamera/property_ids.h>\n>  #include <libcamera/request.h>\n>  #include <libcamera/stream.h>\n>  \n> @@ -62,6 +63,15 @@ public:\n>  \tStream outStream_;\n>  \tStream vfStream_;\n>  \tStream rawStream_;\n> +\n> +\t/* Save transformation given by the sensor rotation */\n> +\tTransform rotationTransform_;\n> +\n> +\t/*\n> +\t * Manage horizontal and vertical flips supported (or not) by the\n> +\t * sensor.\n> +\t */\n> +\tbool supportsFlips_;\n>  };\n>  \n>  class IPU3CameraConfiguration : public CameraConfiguration\n> @@ -74,6 +84,9 @@ public:\n>  \tconst StreamConfiguration &cio2Format() const { return cio2Configuration_; }\n>  \tconst ImgUDevice::PipeConfig imguConfig() const { return pipeConfig_; }\n>  \n> +\t/* Cache the combinedTransform_ that will be applied to the sensor */\n> +\tTransform combinedTransform_;\n> +\n>  private:\n>  \t/*\n>  \t * The IPU3CameraData instance is guaranteed to be valid as long as the\n> @@ -143,11 +156,49 @@ CameraConfiguration::Status IPU3CameraConfiguration::validate()\n>  \tif (config_.empty())\n>  \t\treturn Invalid;\n>  \n> -\tif (transform != Transform::Identity) {\n> -\t\ttransform = Transform::Identity;\n> +\tTransform combined = transform * data_->rotationTransform_;\n> +\n> +\t/*\n> +\t * We combine the platform and user transform, but must \"adjust away\"\n> +\t * any combined result that includes a transposition, 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 the\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\t/*\n> +\t\t * If the sensor can do no transforms, then combined must be\n> +\t\t * changed to the identity. The only user transform that gives\n> +\t\t * rise to this is the inverse of the rotation. (Recall that\n> +\t\t * combined = transform * rotationTransform.)\n> +\t\t */\n> +\t\ttransform = -data_->rotationTransform_;\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 to save us working it out again.\n> +\t */\n> +\tcombinedTransform_ = combined;\n> +\n>  \t/* Cap the number of entries to the available streams. */\n>  \tif (config_.size() > IPU3_MAX_STREAMS) {\n>  \t\tconfig_.resize(IPU3_MAX_STREAMS);\n> @@ -540,6 +591,19 @@ int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)\n>  \t\treturn ret;\n>  \t}\n>  \n> +\t/*\n> +\t * Configure the H/V flip controls based on the combination of\n> +\t * the sensor and user transform.\n> +\t */\n> +\tif (data->supportsFlips_) {\n> +\t\tControlList sensor_ctrls(cio2->sensor()->controls());\n> +\t\tsensor_ctrls.set(V4L2_CID_HFLIP,\n> +\t\t\t\t static_cast<int32_t>(!!(config->combinedTransform_ & Transform::HFlip)));\n> +\t\tsensor_ctrls.set(V4L2_CID_VFLIP,\n> +\t\t\t\t static_cast<int32_t>(!!(config->combinedTransform_ & Transform::VFlip)));\n> +\t\tcio2->sensor()->setControls(&sensor_ctrls);\n> +\t}\n> +\n>  \treturn 0;\n>  }\n>  \n> @@ -775,9 +839,31 @@ int PipelineHandlerIPU3::registerCameras()\n>  \t\t/* Initialize the camera properties. */\n>  \t\tdata->properties_ = cio2->sensor()->properties();\n>  \n> +\t\t/* Convert the sensor rotation to a transformation */\n> +\t\tint32_t rotation;\n> +\t\tif (data->properties_.contains(properties::Rotation)) {\n> +\t\t\trotation = data->properties_.get(properties::Rotation);\n> +\t\t} else {\n> +\t\t\t/* If sensor driver doesn't expose rotation, default rotation to 0 */\n> +\t\t\trotation = 0;\n> +\t\t\tLOG(IPU3, Warning) << \"Rotation control not exposed by \" << cio2->sensor()->id()\n> +\t\t\t\t\t   << \". Assume rotation 0.\";\n> +\t\t}\n> +\n> +\t\tbool success;\n> +\t\tdata->rotationTransform_ = transformFromRotation(rotation, &success);\n> +\t\tif (!success)\n> +\t\t\tLOG(IPU3, Warning) << \"Invalid rotation of \" << rotation << \" degrees - ignoring\";\n> +\n>  \t\t/* Initialze the camera controls. */\n>  \t\tdata->controlInfo_ = IPU3Controls;\n>  \n> +\t\tControlList ctrls = cio2->sensor()->getControls({ V4L2_CID_HFLIP });\n> +\t\tif (!ctrls.empty()) {\n> +\t\t\t/* We assume it will support vflips too... */\n> +\t\t\tdata->supportsFlips_ = true;\n> +\t\t}\n> +\n>  \t\t/**\n>  \t\t * \\todo Dynamically assign ImgU and output devices to each\n>  \t\t * stream and camera; as of now, limit support to two cameras\n>","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 9003AC33BB\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  1 Feb 2021 17:26:45 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 2684868411;\n\tMon,  1 Feb 2021 18:26:45 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 50AFF683FF\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  1 Feb 2021 18:26:43 +0100 (CET)","from [192.168.0.20]\n\t(cpc89244-aztw30-2-0-cust3082.18-1.cable.virginm.net [86.31.172.11])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id A53CA556;\n\tMon,  1 Feb 2021 18:26:42 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"cpwzAZWB\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1612200402;\n\tbh=g5X4Vr5Mfz5EXMNkl1o2EAtQQj7rl/KFzNch9PSrBEs=;\n\th=Reply-To:Subject:To:References:From:Cc:Date:In-Reply-To:From;\n\tb=cpwzAZWBP0dD9BHtCoXw7Ai6RgQQqWNNjYXllQcYu2QdZZQPOUVrmVWGPB1Ut71ER\n\tkyjQt9LMjYBA3sG1dP3fWGfpZJUWchJPR6XOeUo2DxJLdvljwJOEuQl1XxeQa7svil\n\t02OkHT1B/eTuQY3MHCC9jUwB6UXJKjmwYHxfDBoI=","To":"=?utf-8?q?Fabian_W=C3=BCthrich?= <me@fabwu.ch>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20210116150257.2970-1-me@fabwu.ch>\n\t<20210122120132.3717-1-me@fabwu.ch>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Autocrypt":"addr=kieran.bingham@ideasonboard.com; keydata=\n\t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\n\tWZw01QYHU/GUV/zHJSFk","Organization":"Ideas on Board","Message-ID":"<4b384bc4-4c10-f7fb-f26d-34c294bfaac2@ideasonboard.com>","Date":"Mon, 1 Feb 2021 17:26:39 +0000","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101\n\tThunderbird/68.10.0","MIME-Version":"1.0","In-Reply-To":"<20210122120132.3717-1-me@fabwu.ch>","Content-Language":"en-GB","Subject":"Re: [libcamera-devel] [PATCH v3] libcamera: ipu3: Add rotation to\n\tipu3 pipeline","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>","Reply-To":"kieran.bingham@ideasonboard.com","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"base64","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":14902,"web_url":"https://patchwork.libcamera.org/comment/14902/","msgid":"<7a29cfe9-41ec-7007-730a-9bb02823506e@fabwu.ch>","date":"2021-02-02T08:13:15","subject":"Re: [libcamera-devel] [PATCH v3] libcamera: ipu3: Add rotation to\n\tipu3 pipeline","submitter":{"id":77,"url":"https://patchwork.libcamera.org/api/people/77/","name":"Fabian Wüthrich","email":"me@fabwu.ch"},"content":"Hi Kieran,\n\nThanks for your review.\n\nOn 01.02.21 18:26, Kieran Bingham wrote:\n> Hi Fabian,\n> \n> On 22/01/2021 12:01, Fabian Wüthrich wrote:\n>> Use the same transformation logic as in the raspberry pipeline to\n>> implement rotations in the ipu3 pipeline.\n>>\n>> Tested on a Surface Book 2 with an experimental driver for OV5693.\n> \n> As far as I can see, this doesn't yet support setting the rotation as a\n> control from an application? But I don't think we should require that of\n> this patch.\n> \n\nI think this line should allow applications to apply a rotation but I haven't\ntested it yet.\n\n>> -\tif (transform != Transform::Identity) {\n>> -\t\ttransform = Transform::Identity;\n>> +\tTransform combined = transform * data_->rotationTransform_;\n\n> I believe getting this rotation support in will help progress the topic,\n> but I'm weary that we might need to consider later how applications\n> determine if we have or haven't rotated in the pipeline.\n> \n> But I think we can handle that later when we hit more directly.\n> \n\nAgree. The purpose of this patch is to make rotations work accross the whole\nstack (ACPI SSDB buffer -> fwnodes -> V4L2 controls -> libcamera).\n\n> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> \n\nThanks for the tag :)\n\n> I believe Jacopo's tag has carried forwards on this too? If so - I'll\n> let Jacopo handle merging.\n> \n\nWhile rebasing my changes, I saw that a new method `initControls()` was added.\n\nShould I move this part into `initControls()`?\n\n>> @@ -775,9 +839,31 @@ int PipelineHandlerIPU3::registerCameras()\n>>  \t\t/* Initialize the camera properties. */\n>>  \t\tdata->properties_ = cio2->sensor()->properties();\n>>  \n>> +\t\t/* Convert the sensor rotation to a transformation */\n>> +\t\tint32_t rotation;\n>> +\t\tif (data->properties_.contains(properties::Rotation)) {\n>> +\t\t\trotation = data->properties_.get(properties::Rotation);\n>> +\t\t} else {\n>> +\t\t\t/* If sensor driver doesn't expose rotation, default rotation to 0 */\n>> +\t\t\trotation = 0;\n>> +\t\t\tLOG(IPU3, Warning) << \"Rotation control not exposed by \" << cio2->sensor()->id()\n>> +\t\t\t\t\t   << \". Assume rotation 0.\";\n>> +\t\t}\n>> +\n>> +\t\tbool success;\n>> +\t\tdata->rotationTransform_ = transformFromRotation(rotation, &success);\n>> +\t\tif (!success)\n>> +\t\t\tLOG(IPU3, Warning) << \"Invalid rotation of \" << rotation << \" degrees - ignoring\";\n>> +\n>>  \t\t/* Initialze the camera controls. */\n>>  \t\tdata->controlInfo_ = IPU3Controls;\n>>  \n>> +\t\tControlList ctrls = cio2->sensor()->getControls({ V4L2_CID_HFLIP });\n>> +\t\tif (!ctrls.empty()) {\n>> +\t\t\t/* We assume it will support vflips too... */\n>> +\t\t\tdata->supportsFlips_ = true;\n>> +\t\t}\n>> +\n>>  \t\t/**\n>>  \t\t * \\todo Dynamically assign ImgU and output devices to each\n>>  \t\t * stream and camera; as of now, limit support to two cameras\n>>\n\nBest,\nFabian\n\n> --\n> Kieran\n> \n> \n>>\n>> Signed-off-by: Fabian Wüthrich <me@fabwu.ch>\n>> ---\n>> Changes in v2:\n>>  - Cache rotationTransform in CameraData\n>>  - Use separate controls for sensor\n>>\n>> Changes in v3:\n>>  - Default rotation to 0 if sensor doesn't expose it\n>>\n>>  src/libcamera/pipeline/ipu3/ipu3.cpp | 90 +++++++++++++++++++++++++++-\n>>  1 file changed, 88 insertions(+), 2 deletions(-)\n>>\n>> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp\n>> index 73304ea7..d620acf3 100644\n>> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp\n>> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp\n>> @@ -14,6 +14,7 @@\n>>  #include <libcamera/camera.h>\n>>  #include <libcamera/control_ids.h>\n>>  #include <libcamera/formats.h>\n>> +#include <libcamera/property_ids.h>\n>>  #include <libcamera/request.h>\n>>  #include <libcamera/stream.h>\n>>  \n>> @@ -62,6 +63,15 @@ public:\n>>  \tStream outStream_;\n>>  \tStream vfStream_;\n>>  \tStream rawStream_;\n>> +\n>> +\t/* Save transformation given by the sensor rotation */\n>> +\tTransform rotationTransform_;\n>> +\n>> +\t/*\n>> +\t * Manage horizontal and vertical flips supported (or not) by the\n>> +\t * sensor.\n>> +\t */\n>> +\tbool supportsFlips_;\n>>  };\n>>  \n>>  class IPU3CameraConfiguration : public CameraConfiguration\n>> @@ -74,6 +84,9 @@ public:\n>>  \tconst StreamConfiguration &cio2Format() const { return cio2Configuration_; }\n>>  \tconst ImgUDevice::PipeConfig imguConfig() const { return pipeConfig_; }\n>>  \n>> +\t/* Cache the combinedTransform_ that will be applied to the sensor */\n>> +\tTransform combinedTransform_;\n>> +\n>>  private:\n>>  \t/*\n>>  \t * The IPU3CameraData instance is guaranteed to be valid as long as the\n>> @@ -143,11 +156,49 @@ CameraConfiguration::Status IPU3CameraConfiguration::validate()\n>>  \tif (config_.empty())\n>>  \t\treturn Invalid;\n>>  \n>> -\tif (transform != Transform::Identity) {\n>> -\t\ttransform = Transform::Identity;\n>> +\tTransform combined = transform * data_->rotationTransform_;\n>> +\n>> +\t/*\n>> +\t * We combine the platform and user transform, but must \"adjust away\"\n>> +\t * any combined result that includes a transposition, 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 the\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\t/*\n>> +\t\t * If the sensor can do no transforms, then combined must be\n>> +\t\t * changed to the identity. The only user transform that gives\n>> +\t\t * rise to this is the inverse of the rotation. (Recall that\n>> +\t\t * combined = transform * rotationTransform.)\n>> +\t\t */\n>> +\t\ttransform = -data_->rotationTransform_;\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 to save us working it out again.\n>> +\t */\n>> +\tcombinedTransform_ = combined;\n>> +\n>>  \t/* Cap the number of entries to the available streams. */\n>>  \tif (config_.size() > IPU3_MAX_STREAMS) {\n>>  \t\tconfig_.resize(IPU3_MAX_STREAMS);\n>> @@ -540,6 +591,19 @@ int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)\n>>  \t\treturn ret;\n>>  \t}\n>>  \n>> +\t/*\n>> +\t * Configure the H/V flip controls based on the combination of\n>> +\t * the sensor and user transform.\n>> +\t */\n>> +\tif (data->supportsFlips_) {\n>> +\t\tControlList sensor_ctrls(cio2->sensor()->controls());\n>> +\t\tsensor_ctrls.set(V4L2_CID_HFLIP,\n>> +\t\t\t\t static_cast<int32_t>(!!(config->combinedTransform_ & Transform::HFlip)));\n>> +\t\tsensor_ctrls.set(V4L2_CID_VFLIP,\n>> +\t\t\t\t static_cast<int32_t>(!!(config->combinedTransform_ & Transform::VFlip)));\n>> +\t\tcio2->sensor()->setControls(&sensor_ctrls);\n>> +\t}\n>> +\n>>  \treturn 0;\n>>  }\n>>  \n>> @@ -775,9 +839,31 @@ int PipelineHandlerIPU3::registerCameras()\n>>  \t\t/* Initialize the camera properties. */\n>>  \t\tdata->properties_ = cio2->sensor()->properties();\n>>  \n>> +\t\t/* Convert the sensor rotation to a transformation */\n>> +\t\tint32_t rotation;\n>> +\t\tif (data->properties_.contains(properties::Rotation)) {\n>> +\t\t\trotation = data->properties_.get(properties::Rotation);\n>> +\t\t} else {\n>> +\t\t\t/* If sensor driver doesn't expose rotation, default rotation to 0 */\n>> +\t\t\trotation = 0;\n>> +\t\t\tLOG(IPU3, Warning) << \"Rotation control not exposed by \" << cio2->sensor()->id()\n>> +\t\t\t\t\t   << \". Assume rotation 0.\";\n>> +\t\t}\n>> +\n>> +\t\tbool success;\n>> +\t\tdata->rotationTransform_ = transformFromRotation(rotation, &success);\n>> +\t\tif (!success)\n>> +\t\t\tLOG(IPU3, Warning) << \"Invalid rotation of \" << rotation << \" degrees - ignoring\";\n>> +\n>>  \t\t/* Initialze the camera controls. */\n>>  \t\tdata->controlInfo_ = IPU3Controls;\n>>  \n>> +\t\tControlList ctrls = cio2->sensor()->getControls({ V4L2_CID_HFLIP });\n>> +\t\tif (!ctrls.empty()) {\n>> +\t\t\t/* We assume it will support vflips too... */\n>> +\t\t\tdata->supportsFlips_ = true;\n>> +\t\t}\n>> +\n>>  \t\t/**\n>>  \t\t * \\todo Dynamically assign ImgU and output devices to each\n>>  \t\t * stream and camera; as of now, limit support to two cameras\n>>\n>","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 4CED8BD161\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue,  2 Feb 2021 08:13:18 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id D669F68421;\n\tTue,  2 Feb 2021 09:13:17 +0100 (CET)","from gusto4.metanet.ch (gusto4.metanet.ch [80.74.154.158])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 08C0260306\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  2 Feb 2021 09:13:15 +0100 (CET)","from [IPv6:2001:1715:9d9c:4e40:c8ee:888d:e6cb:532f] (localhost\n\t[127.0.0.1]) by gusto4.metanet.ch (Postfix) with ESMTPSA id\n\t8A3054F015C2; Tue,  2 Feb 2021 09:13:15 +0100 (CET)"],"Authentication-Results":["lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=fabwu.ch header.i=@fabwu.ch header.b=\"TIzEszyb\";\n\tdkim-atps=neutral","gusto.metanet.ch;\n\tspf=pass (sender IP is 2001:1715:9d9c:4e40:c8ee:888d:e6cb:532f)\n\tsmtp.mailfrom=me@fabwu.ch\n\tsmtp.helo=[IPv6:2001:1715:9d9c:4e40:c8ee:888d:e6cb:532f]"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=fabwu.ch; s=default; \n\tt=1612253595; bh=xKsNFfRuPKooGOCc+0CBh28arr3qxDUF5IMIQ3Nu0fU=;\n\th=Subject:To:From;\n\tb=TIzEszybNoJk0QrTh6T1rliLYuLYlZXCyAZ1m/s+Rj02lm+c6NXHtLlTSrW+Y2nHY\n\tKPIf6MfEqymZntrm5w6g3GUay7EtnZPDEn90wxDMbpM0PVPp89APC9ha9CvnuBy6cs\n\t3VjLdFDul1I/SQhJzGo7InV2MKujoo9HMTEveoh8=","Received-SPF":"pass (gusto.metanet.ch: connection is authenticated)","To":"kieran.bingham@ideasonboard.com, libcamera-devel@lists.libcamera.org","References":"<20210116150257.2970-1-me@fabwu.ch>\n\t<20210122120132.3717-1-me@fabwu.ch>\n\t<4b384bc4-4c10-f7fb-f26d-34c294bfaac2@ideasonboard.com>","From":"=?utf-8?q?Fabian_W=C3=BCthrich?= <me@fabwu.ch>","Message-ID":"<7a29cfe9-41ec-7007-730a-9bb02823506e@fabwu.ch>","Date":"Tue, 2 Feb 2021 09:13:15 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101\n\tThunderbird/78.7.0","MIME-Version":"1.0","In-Reply-To":"<4b384bc4-4c10-f7fb-f26d-34c294bfaac2@ideasonboard.com>","Content-Language":"en-US","Subject":"Re: [libcamera-devel] [PATCH v3] libcamera: ipu3: Add rotation to\n\tipu3 pipeline","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=\"utf-8\"","Content-Transfer-Encoding":"base64","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":14909,"web_url":"https://patchwork.libcamera.org/comment/14909/","msgid":"<20210202175658.oae42p5jj4msvbh6@uno.localdomain>","date":"2021-02-02T17:56:58","subject":"Re: [libcamera-devel] [PATCH v3] libcamera: ipu3: Add rotation to\n\tipu3 pipeline","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi Fabian,\n\nOn Tue, Feb 02, 2021 at 09:13:15AM +0100, Fabian Wüthrich wrote:\n> Hi Kieran,\n>\n> Thanks for your review.\n>\n> On 01.02.21 18:26, Kieran Bingham wrote:\n> > Hi Fabian,\n> >\n> > On 22/01/2021 12:01, Fabian Wüthrich wrote:\n> >> Use the same transformation logic as in the raspberry pipeline to\n> >> implement rotations in the ipu3 pipeline.\n> >>\n> >> Tested on a Surface Book 2 with an experimental driver for OV5693.\n> >\n> > As far as I can see, this doesn't yet support setting the rotation as a\n> > control from an application? But I don't think we should require that of\n> > this patch.\n> >\n>\n> I think this line should allow applications to apply a rotation but I haven't\n> tested it yet.\n>\n> >> -\tif (transform != Transform::Identity) {\n> >> -\t\ttransform = Transform::Identity;\n> >> +\tTransform combined = transform * data_->rotationTransform_;\n>\n> > I believe getting this rotation support in will help progress the topic,\n> > but I'm weary that we might need to consider later how applications\n> > determine if we have or haven't rotated in the pipeline.\n> >\n> > But I think we can handle that later when we hit more directly.\n> >\n>\n> Agree. The purpose of this patch is to make rotations work accross the whole\n> stack (ACPI SSDB buffer -> fwnodes -> V4L2 controls -> libcamera).\n>\n> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> >\n>\n> Thanks for the tag :)\n>\n> > I believe Jacopo's tag has carried forwards on this too? If so - I'll\n> > let Jacopo handle merging.\n> >\n>\n> While rebasing my changes, I saw that a new method `initControls()` was added.\n>\n> Should I move this part into `initControls()`?\n\nYes please, sorry about this, there has been quite some churn in the ipu3.cpp\nfile recently.\n\nI tried re-basing your patch on the latest master and some older\nversion to rebase locally, but it doesn't cleanly apply. Could you\nplease rebase and re-send (and while at there collect the two small\ncomments I added here) ?\n\nThanks\n   j\n\n>\n> >> @@ -775,9 +839,31 @@ int PipelineHandlerIPU3::registerCameras()\n> >>  \t\t/* Initialize the camera properties. */\n> >>  \t\tdata->properties_ = cio2->sensor()->properties();\n> >>\n> >> +\t\t/* Convert the sensor rotation to a transformation */\n> >> +\t\tint32_t rotation;\n> >> +\t\tif (data->properties_.contains(properties::Rotation)) {\n> >> +\t\t\trotation = data->properties_.get(properties::Rotation);\n> >> +\t\t} else {\n> >> +\t\t\t/* If sensor driver doesn't expose rotation, default rotation to 0 */\n> >> +\t\t\trotation = 0;\n> >> +\t\t\tLOG(IPU3, Warning) << \"Rotation control not exposed by \" << cio2->sensor()->id()\n> >> +\t\t\t\t\t   << \". Assume rotation 0.\";\n> >> +\t\t}\n> >> +\n> >> +\t\tbool success;\n> >> +\t\tdata->rotationTransform_ = transformFromRotation(rotation, &success);\n> >> +\t\tif (!success)\n> >> +\t\t\tLOG(IPU3, Warning) << \"Invalid rotation of \" << rotation << \" degrees - ignoring\";\n> >> +\n> >>  \t\t/* Initialze the camera controls. */\n> >>  \t\tdata->controlInfo_ = IPU3Controls;\n> >>\n> >> +\t\tControlList ctrls = cio2->sensor()->getControls({ V4L2_CID_HFLIP });\n> >> +\t\tif (!ctrls.empty()) {\n> >> +\t\t\t/* We assume it will support vflips too... */\n> >> +\t\t\tdata->supportsFlips_ = true;\n> >> +\t\t}\n> >> +\n> >>  \t\t/**\n> >>  \t\t * \\todo Dynamically assign ImgU and output devices to each\n> >>  \t\t * stream and camera; as of now, limit support to two cameras\n> >>\n>\n> Best,\n> Fabian\n>\n> > --\n> > Kieran\n> >\n> >\n> >>\n> >> Signed-off-by: Fabian Wüthrich <me@fabwu.ch>\n> >> ---\n> >> Changes in v2:\n> >>  - Cache rotationTransform in CameraData\n> >>  - Use separate controls for sensor\n> >>\n> >> Changes in v3:\n> >>  - Default rotation to 0 if sensor doesn't expose it\n> >>\n> >>  src/libcamera/pipeline/ipu3/ipu3.cpp | 90 +++++++++++++++++++++++++++-\n> >>  1 file changed, 88 insertions(+), 2 deletions(-)\n> >>\n> >> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp\n> >> index 73304ea7..d620acf3 100644\n> >> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp\n> >> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp\n> >> @@ -14,6 +14,7 @@\n> >>  #include <libcamera/camera.h>\n> >>  #include <libcamera/control_ids.h>\n> >>  #include <libcamera/formats.h>\n> >> +#include <libcamera/property_ids.h>\n> >>  #include <libcamera/request.h>\n> >>  #include <libcamera/stream.h>\n> >>\n> >> @@ -62,6 +63,15 @@ public:\n> >>  \tStream outStream_;\n> >>  \tStream vfStream_;\n> >>  \tStream rawStream_;\n> >> +\n> >> +\t/* Save transformation given by the sensor rotation */\n> >> +\tTransform rotationTransform_;\n> >> +\n> >> +\t/*\n> >> +\t * Manage horizontal and vertical flips supported (or not) by the\n> >> +\t * sensor.\n> >> +\t */\n> >> +\tbool supportsFlips_;\n> >>  };\n> >>\n> >>  class IPU3CameraConfiguration : public CameraConfiguration\n> >> @@ -74,6 +84,9 @@ public:\n> >>  \tconst StreamConfiguration &cio2Format() const { return cio2Configuration_; }\n> >>  \tconst ImgUDevice::PipeConfig imguConfig() const { return pipeConfig_; }\n> >>\n> >> +\t/* Cache the combinedTransform_ that will be applied to the sensor */\n> >> +\tTransform combinedTransform_;\n> >> +\n> >>  private:\n> >>  \t/*\n> >>  \t * The IPU3CameraData instance is guaranteed to be valid as long as the\n> >> @@ -143,11 +156,49 @@ CameraConfiguration::Status IPU3CameraConfiguration::validate()\n> >>  \tif (config_.empty())\n> >>  \t\treturn Invalid;\n> >>\n> >> -\tif (transform != Transform::Identity) {\n> >> -\t\ttransform = Transform::Identity;\n> >> +\tTransform combined = transform * data_->rotationTransform_;\n> >> +\n> >> +\t/*\n> >> +\t * We combine the platform and user transform, but must \"adjust away\"\n> >> +\t * any combined result that includes a transposition, 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 the\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\t/*\n> >> +\t\t * If the sensor can do no transforms, then combined must be\n> >> +\t\t * changed to the identity. The only user transform that gives\n> >> +\t\t * rise to this is the inverse of the rotation. (Recall that\n> >> +\t\t * combined = transform * rotationTransform.)\n> >> +\t\t */\n> >> +\t\ttransform = -data_->rotationTransform_;\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 to save us working it out again.\n> >> +\t */\n> >> +\tcombinedTransform_ = combined;\n> >> +\n> >>  \t/* Cap the number of entries to the available streams. */\n> >>  \tif (config_.size() > IPU3_MAX_STREAMS) {\n> >>  \t\tconfig_.resize(IPU3_MAX_STREAMS);\n> >> @@ -540,6 +591,19 @@ int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)\n> >>  \t\treturn ret;\n> >>  \t}\n> >>\n> >> +\t/*\n> >> +\t * Configure the H/V flip controls based on the combination of\n> >> +\t * the sensor and user transform.\n> >> +\t */\n> >> +\tif (data->supportsFlips_) {\n> >> +\t\tControlList sensor_ctrls(cio2->sensor()->controls());\n> >> +\t\tsensor_ctrls.set(V4L2_CID_HFLIP,\n> >> +\t\t\t\t static_cast<int32_t>(!!(config->combinedTransform_ & Transform::HFlip)));\n> >> +\t\tsensor_ctrls.set(V4L2_CID_VFLIP,\n> >> +\t\t\t\t static_cast<int32_t>(!!(config->combinedTransform_ & Transform::VFlip)));\n> >> +\t\tcio2->sensor()->setControls(&sensor_ctrls);\n> >> +\t}\n> >> +\n> >>  \treturn 0;\n> >>  }\n> >>\n> >> @@ -775,9 +839,31 @@ int PipelineHandlerIPU3::registerCameras()\n> >>  \t\t/* Initialize the camera properties. */\n> >>  \t\tdata->properties_ = cio2->sensor()->properties();\n> >>\n> >> +\t\t/* Convert the sensor rotation to a transformation */\n> >> +\t\tint32_t rotation;\n> >> +\t\tif (data->properties_.contains(properties::Rotation)) {\n> >> +\t\t\trotation = data->properties_.get(properties::Rotation);\n> >> +\t\t} else {\n> >> +\t\t\t/* If sensor driver doesn't expose rotation, default rotation to 0 */\n> >> +\t\t\trotation = 0;\n> >> +\t\t\tLOG(IPU3, Warning) << \"Rotation control not exposed by \" << cio2->sensor()->id()\n> >> +\t\t\t\t\t   << \". Assume rotation 0.\";\n> >> +\t\t}\n> >> +\n> >> +\t\tbool success;\n> >> +\t\tdata->rotationTransform_ = transformFromRotation(rotation, &success);\n> >> +\t\tif (!success)\n> >> +\t\t\tLOG(IPU3, Warning) << \"Invalid rotation of \" << rotation << \" degrees - ignoring\";\n> >> +\n> >>  \t\t/* Initialze the camera controls. */\n> >>  \t\tdata->controlInfo_ = IPU3Controls;\n> >>\n> >> +\t\tControlList ctrls = cio2->sensor()->getControls({ V4L2_CID_HFLIP });\n> >> +\t\tif (!ctrls.empty()) {\n> >> +\t\t\t/* We assume it will support vflips too... */\n> >> +\t\t\tdata->supportsFlips_ = true;\n> >> +\t\t}\n> >> +\n> >>  \t\t/**\n> >>  \t\t * \\todo Dynamically assign ImgU and output devices to each\n> >>  \t\t * stream and camera; as of now, limit support to two cameras\n> >>\n> >","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 6102CBD161\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue,  2 Feb 2021 17:56:40 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id EB31D6842E;\n\tTue,  2 Feb 2021 18:56:39 +0100 (CET)","from relay10.mail.gandi.net (relay10.mail.gandi.net\n\t[217.70.178.230])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 723D660307\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  2 Feb 2021 18:56:38 +0100 (CET)","from uno.localdomain (93-61-96-190.ip145.fastwebnet.it\n\t[93.61.96.190]) (Authenticated sender: jacopo@jmondi.org)\n\tby relay10.mail.gandi.net (Postfix) with ESMTPSA id 9AC1B24000A;\n\tTue,  2 Feb 2021 17:56:37 +0000 (UTC)"],"Date":"Tue, 2 Feb 2021 18:56:58 +0100","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"Fabian =?utf-8?b?V8O8dGhyaWNo?= <me@fabwu.ch>","Message-ID":"<20210202175658.oae42p5jj4msvbh6@uno.localdomain>","References":"<20210116150257.2970-1-me@fabwu.ch>\n\t<20210122120132.3717-1-me@fabwu.ch>\n\t<4b384bc4-4c10-f7fb-f26d-34c294bfaac2@ideasonboard.com>\n\t<7a29cfe9-41ec-7007-730a-9bb02823506e@fabwu.ch>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<7a29cfe9-41ec-7007-730a-9bb02823506e@fabwu.ch>","Subject":"Re: [libcamera-devel] [PATCH v3] libcamera: ipu3: Add rotation to\n\tipu3 pipeline","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>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"base64","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]