Patch Detail
Show a patch.
GET /api/1.1/patches/15099/?format=api
{ "id": 15099, "url": "https://patchwork.libcamera.org/api/1.1/patches/15099/?format=api", "web_url": "https://patchwork.libcamera.org/patch/15099/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/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": "<CAJ+kdVH_0cE_Mj-P-P5-+rYeQKHJM2TTSN2wT904GFi6yN=4qQ@mail.gmail.com>", "date": "2021-12-09T11:59:04", "name": "[libcamera-devel] libcamera: pipeline: simple: Add support for controls", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "13a42b2db1b607ef827644a8269b1367fe0a9efc", "submitter": { "id": 108, "url": "https://patchwork.libcamera.org/api/1.1/people/108/?format=api", "name": "Benjamin Schaaf", "email": "ben.schaaf@gmail.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/15099/mbox/", "series": [ { "id": 2831, "url": "https://patchwork.libcamera.org/api/1.1/series/2831/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=2831", "date": "2021-12-09T11:59:04", "name": "[libcamera-devel] libcamera: pipeline: simple: Add support for controls", "version": 1, "mbox": "https://patchwork.libcamera.org/series/2831/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/15099/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/15099/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 17A20BDB13\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 9 Dec 2021 11:59:19 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C48976086A;\n\tThu, 9 Dec 2021 12:59:18 +0100 (CET)", "from mail-yb1-xb32.google.com (mail-yb1-xb32.google.com\n\t[IPv6:2607:f8b0:4864:20::b32])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id AADCB607DE\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 9 Dec 2021 12:59:16 +0100 (CET)", "by mail-yb1-xb32.google.com with SMTP id 131so13087821ybc.7\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 09 Dec 2021 03:59:16 -0800 (PST)" ], "Authentication-Results": "lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"O8lqURTN\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112;\n\th=mime-version:from:date:message-id:subject:to;\n\tbh=wDA4qvP4DTeS/Vf2iPE3WC/kUaCc6Dp4WRLtPeQr8iM=;\n\tb=O8lqURTNhWmLWqSSoIxd1nvllfzbBBY4mibzZ0SXeeA/9z5jaqnaa5Oa46ksH+33oP\n\tuysUAwQgBnxXbcJxKQM6TwFmGsI3zejR+TB8q9NnLFTJljNrFubkge99QbnSwjVR3ysd\n\tldW2AuJk3QTNW4JhLUFokCNzNW90G2VhNgvFE6nqdXR22wiHacXAbDTGvhMK6wxagDAv\n\txcGEMi0eTcY69Cu2EM2MYTOBApaDugJHS98Uzyff3wtdYfrYPHP1ALGIolkPsqzlmMl9\n\thhJtAqy3O1snXEnBZEYLecL6/f3BOojTnGDnzVsabZbGx7rCfeWmLG2iuo44UzvcyGNx\n\t5yjg==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=x-gm-message-state:mime-version:from:date:message-id:subject:to;\n\tbh=wDA4qvP4DTeS/Vf2iPE3WC/kUaCc6Dp4WRLtPeQr8iM=;\n\tb=zVBDfRsCGFXXkmCbn0l4T8YLX3ydPEKuIpWgIjBrOxuWWyJUDVFKUIXXZinQv7Q1re\n\tnLLhv1Zc+3U803CNq1wM7EHOiU+JAU9wL+YWgiAvPjWj2r0aH9Syihd4PvR3Q9ham5Jr\n\tqi2HVc7OofJiQe8DZxz7C4liL5pQ8hicyKDVGqgs6W0vkJeu5NBZ3X1bQHDnQvU6y6AU\n\tVGAN2mkJdQ//1YxeDUdng+DPW/3WikJUJuDDPPIGtpiYwRXVI7tCi9Tp7XOJLhAtZF8l\n\txfGz5DK5+n2FH+rDKJ07G9/AbIWAfYrK6RT64iF5lTIiC0U6ZajKmDyzt4wC7pzKE4l6\n\t8R1A==", "X-Gm-Message-State": "AOAM533I6U8SGujjyWN0/8f/KQrdyYMq3Jrrt98QRZ2iIfk0WAda9P0S\n\tA0EnmuzGyh+JO0Gr1K1sDMUYQps6ox2pBByb0Zq62q5zaKqvIg==", "X-Google-Smtp-Source": "ABdhPJyUPtM+LJL2Y4gqMwXTQumBu4TUq5KabAC6J8z8fBzgM/ODDKrR0sBv2jLKXs77iCjgCrTTw6Kr7R6/2OkFyIg=", "X-Received": "by 2002:a25:9bc9:: with SMTP id w9mr5665777ybo.398.1639051155168;\n\tThu, 09 Dec 2021 03:59:15 -0800 (PST)", "MIME-Version": "1.0", "From": "Benjamin Schaaf <ben.schaaf@gmail.com>", "Date": "Thu, 9 Dec 2021 22:59:04 +1100", "Message-ID": "<CAJ+kdVH_0cE_Mj-P-P5-+rYeQKHJM2TTSN2wT904GFi6yN=4qQ@mail.gmail.com>", "To": "libcamera-devel@lists.libcamera.org", "Content-Type": "text/plain; charset=\"UTF-8\"", "Subject": "[libcamera-devel] [PATCH] libcamera: pipeline: simple: Add support\n\tfor controls", "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>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "Controls and control info are translated between libcamera and V4L2\ninside the simple pipeline. Request controls are applied when a request\nis next to be completed.\n\nThis also adds some additional draft controls needed for the PinePhone.\n\nBug: https://bugs.libcamera.org/show_bug.cgi?id=98\nSigned-off-by: Benjamin Schaaf <ben.schaaf@gmail.com>\n---\n src/libcamera/control_ids.yaml | 24 ++\n src/libcamera/pipeline/simple/controls.cpp | 242 +++++++++++++++++++++\n src/libcamera/pipeline/simple/controls.h | 30 +++\n src/libcamera/pipeline/simple/meson.build | 1 +\n src/libcamera/pipeline/simple/simple.cpp | 30 ++-\n src/libcamera/v4l2_device.cpp | 12 +-\n 6 files changed, 337 insertions(+), 2 deletions(-)\n create mode 100644 src/libcamera/pipeline/simple/controls.cpp\n create mode 100644 src/libcamera/pipeline/simple/controls.h\n\n return v4l2MenuControlInfo(ctrl);", "diff": "diff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml\nindex 9d4638ae..2af230c3 100644\n--- a/src/libcamera/control_ids.yaml\n+++ b/src/libcamera/control_ids.yaml\n@@ -406,6 +406,30 @@ controls:\n The camera will cancel any active or completed metering sequence.\n The AE algorithm is reset to its initial state.\n\n+ - AutoGain:\n+ type: bool\n+ draft: true\n+ description: |\n+ Control for Automatic Gain. Currently identical to V4L2_CID_AUTOGAIN.\n+\n+ - AfEnabled:\n+ type: bool\n+ draft: true\n+ description: |\n+ Control for AF. Currently identical to V4L2_CID_FOCUS_AUTO.\n+\n+ - AfStart:\n+ type: void\n+ draft: true\n+ description: |\n+ Control for AF. Currently identical to V4L2_CID_AUTO_FOCUS_START.\n+\n+ - AfStop:\n+ type: void\n+ draft: true\n+ description: |\n+ Control for AF. Currently identical to V4L2_CID_AUTO_FOCUS_END.\n+\n - AfTrigger:\n type: int32_t\n draft: true\ndiff --git a/src/libcamera/pipeline/simple/controls.cpp\nb/src/libcamera/pipeline/simple/controls.cpp\nnew file mode 100644\nindex 00000000..2d3cfc62\n--- /dev/null\n+++ b/src/libcamera/pipeline/simple/controls.cpp\n@@ -0,0 +1,242 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Benjamin Schaaf\n+ *\n+ * controls.cpp - Simple pipeline control conversion\n+ */\n+\n+#include \"controls.h\"\n+\n+#include <linux/v4l2-controls.h>\n+\n+#include <libcamera/base/log.h>\n+\n+#include <libcamera/control_ids.h>\n+\n+namespace libcamera {\n+\n+LOG_DECLARE_CATEGORY(SimplePipeline)\n+\n+/*\n+ * These controls can be directly mapped between libcamera and V4L2 without\n+ * doing any conversion to the ControlValue.\n+ */\n+namespace {\n+std::unordered_map<unsigned int, unsigned int> controlsToV4L2 = {\n+ { controls::AUTO_GAIN, V4L2_CID_AUTOGAIN },\n+ { controls::AF_ENABLED, V4L2_CID_FOCUS_AUTO },\n+ { controls::AF_START, V4L2_CID_AUTO_FOCUS_START },\n+ { controls::AF_STOP, V4L2_CID_AUTO_FOCUS_STOP },\n+ { controls::AE_ENABLE, V4L2_CID_EXPOSURE_AUTO },\n+ { controls::EXPOSURE_VALUE, V4L2_CID_EXPOSURE },\n+ { controls::DIGITAL_GAIN, V4L2_CID_GAIN },\n+ { controls::ANALOGUE_GAIN, V4L2_CID_ANALOGUE_GAIN },\n+ { controls::AF_STATE, V4L2_CID_AUTO_FOCUS_STATUS },\n+};\n+std::unordered_map<unsigned int, unsigned int> controlsFromV4L2;\n+}\n+\n+/**\n+ * \\brief Convert from a libcamera control to a V4L2 control.\n+ *\n+ * Can optionally convert the libcamera control and/or a set of libcamera\n+ * control values to their V4L2 equivalents.\n+ */\n+bool simpleControlToV4L2(unsigned int control,\n+ unsigned int *v4l2_control,\n+ const ControlValue *control_values,\n+ ControlValue *v4l2_values,\n+ size_t num_values)\n+{\n+ // Convert controls\n+ if (v4l2_control) {\n+ auto it = controlsToV4L2.find(control);\n+ if (it == controlsToV4L2.end())\n+ return false;\n+\n+ *v4l2_control = it->second;\n+ }\n+\n+ // Convert values\n+ if (num_values == 0)\n+ return true;\n+\n+ switch (control) {\n+ case controls::AE_ENABLE:\n+ for (size_t i = 0; i < num_values; ++i)\n+ v4l2_values[i] =\nControlValue((int32_t)(control_values[i].get<bool>() ?\nV4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL));\n+ return true;\n+ case controls::EXPOSURE_VALUE:\n+ case controls::DIGITAL_GAIN:\n+ case controls::ANALOGUE_GAIN:\n+ for (size_t i = 0; i < num_values; ++i)\n+ v4l2_values[i] =\nControlValue((int32_t)control_values[i].get<float>());\n+ return true;\n+ // Read only\n+ case controls::AF_STATE:\n+ return false;\n+ default:\n+ for (size_t i = 0; i < num_values; ++i)\n+ v4l2_values[i] = control_values[i];\n+ return true;\n+ }\n+}\n+\n+/**\n+ * \\brief Convert from a V4L2 control to a libcamera control.\n+ *\n+ * Can optionally convert the V4L2 control and/or a set of V4L2 control values\n+ * to their libcamera equivalents.\n+ */\n+bool simpleControlFromV4L2(unsigned int v4l2_control,\n+ unsigned int *control,\n+ const ControlValue *v4l2_values,\n+ ControlValue *control_values,\n+ size_t num_values)\n+{\n+ // Initialize the inverse of controlsToV4L2\n+ if (controlsFromV4L2.empty()) {\n+ for (const auto &v : controlsToV4L2) {\n+ controlsFromV4L2[v.second] = v.first;\n+ }\n+ }\n+\n+ // Convert control\n+ if (control) {\n+ auto it = controlsFromV4L2.find(v4l2_control);\n+ if (it == controlsFromV4L2.end())\n+ return false;\n+\n+ *control = it->second;\n+ }\n+\n+ // Convert values\n+ if (num_values == 0)\n+ return true;\n+\n+ switch (v4l2_control) {\n+ case V4L2_CID_EXPOSURE_AUTO:\n+ for (size_t i = 0; i < num_values; ++i)\n+ control_values[i] =\nControlValue(v4l2_values[i].get<int32_t>() == V4L2_EXPOSURE_AUTO);\n+ return true;\n+ case V4L2_CID_EXPOSURE:\n+ case V4L2_CID_GAIN:\n+ case V4L2_CID_ANALOGUE_GAIN:\n+ for (size_t i = 0; i < num_values; ++i)\n+ control_values[i] =\nControlValue((float)v4l2_values[i].get<int32_t>());\n+ return true;\n+ case V4L2_CID_AUTO_FOCUS_STATUS:\n+ for (size_t i = 0; i < num_values; ++i) {\n+ switch (v4l2_values[i].get<int32_t>()) {\n+ case V4L2_AUTO_FOCUS_STATUS_IDLE:\n+ control_values[i] =\nControlValue((int32_t)controls::draft::AfStateInactive);\n+ break;\n+ case V4L2_AUTO_FOCUS_STATUS_BUSY:\n+ control_values[i] =\nControlValue((int32_t)controls::draft::AfStateActiveScan);\n+ break;\n+ case V4L2_AUTO_FOCUS_STATUS_REACHED:\n+ control_values[i] =\nControlValue((int32_t)controls::draft::AfStatePassiveFocused);\n+ break;\n+ case V4L2_AUTO_FOCUS_STATUS_FAILED:\n+ control_values[i] =\nControlValue((int32_t)controls::draft::AfStatePassiveUnfocused);\n+ break;\n+ default:\n+ LOG(SimplePipeline, Error)\n+ << \"AUTO_FOCUS_STATUS has invalid value: \"\n+ << utils::hex(v4l2_values[i].get<int32_t>());\n+ /*TODO: Log Error*/\n+ return false;\n+ }\n+ }\n+ return true;\n+ default:\n+ for (size_t i = 0; i < num_values; ++i)\n+ control_values[i] = v4l2_values[i];\n+ return true;\n+ }\n+}\n+\n+/**\n+ * \\brief Convert a ControlInfoMap from V4L2 to libcamera.\n+ *\n+ * Converts both the control identifiers as well as all values.\n+ */\n+ControlInfoMap simpleControlInfoFromV4L2(const ControlInfoMap &v4l2_info_map)\n+{\n+ ControlInfoMap::Map info_map;\n+\n+ for (const auto &pair : v4l2_info_map) {\n+ unsigned int v4l2_control = pair.first->id();\n+ const ControlInfo &v4l2_info = pair.second;\n+\n+ unsigned int control;\n+ ControlValue def;\n+ if (!simpleControlFromV4L2(v4l2_control, &control,\n&v4l2_info.def(), &def, 1))\n+ continue;\n+\n+ const ControlId *control_id = controls::controls.at(control);\n+\n+ // ControlInfo has either a list of values or a minimum and\n+ // maximum. This includes controls that have no values or are\n+ // booleans.\n+ ControlInfo info;\n+ if (v4l2_info.values().empty()) {\n+ ControlValue min, max;\n+ simpleControlFromV4L2(v4l2_control, nullptr,\n&v4l2_info.min(), &min, 1);\n+ simpleControlFromV4L2(v4l2_control, nullptr,\n&v4l2_info.max(), &max, 1);\n+ info = ControlInfo(std::move(min), std::move(max), std::move(def));\n+ } else {\n+ std::vector<ControlValue> values;\n+ values.resize(v4l2_info.values().size());\n+ simpleControlFromV4L2(v4l2_control, nullptr,\nv4l2_info.values().data(), values.data(), values.size());\n+ info = ControlInfo(std::move(values), std::move(def));\n+ }\n+ info_map.emplace(control_id, std::move(info));\n+ }\n+\n+ return ControlInfoMap(std::move(info_map), controls::controls);\n+}\n+\n+/**\n+ * \\brief Convert a control list from libcamera to V4L2.\n+ */\n+ControlList simpleControlListToV4L2(const ControlList &controls)\n+{\n+ ControlList v4l2_controls;\n+ for (const auto &pair : controls) {\n+ unsigned int control = pair.first;\n+ const ControlValue &value = pair.second;\n+\n+ unsigned int v4l2_control;\n+ ControlValue v4l2_value;\n+ if (!simpleControlToV4L2(control, &v4l2_control, &value,\n&v4l2_value, 1)) {\n+ LOG(SimplePipeline, Warning)\n+ << \"Control \" << utils::hex(control)\n+ << \" does not have a V4L2 equivalent\";\n+ continue;\n+ }\n+\n+ v4l2_controls.set(v4l2_control, v4l2_value);\n+ }\n+ return v4l2_controls;\n+}\n+\n+/**\n+ * \\brief Convert a control list from V4L2 to libcamera.\n+ */\n+ControlList simpleControlListFromV4L2(const ControlList &v4l2_controls)\n+{\n+ ControlList controls;\n+ for (const auto &pair : v4l2_controls) {\n+ unsigned int v4l2_control = pair.first;\n+ const ControlValue &v4l2_value = pair.second;\n+\n+ unsigned int control;\n+ ControlValue value;\n+ if (simpleControlFromV4L2(v4l2_control, &control,\n&v4l2_value, &value, 1))\n+ controls.set(control, value);\n+ }\n+ return controls;\n+}\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/pipeline/simple/controls.h\nb/src/libcamera/pipeline/simple/controls.h\nnew file mode 100644\nindex 00000000..cdbcaed1\n--- /dev/null\n+++ b/src/libcamera/pipeline/simple/controls.h\n@@ -0,0 +1,30 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Benjamin Schaaf\n+ *\n+ * controls.h - Simple pipeline control conversion\n+ */\n+\n+#pragma once\n+\n+#include <libcamera/controls.h>\n+\n+namespace libcamera {\n+\n+bool simpleControlToV4L2(unsigned int control,\n+ unsigned int *v4l2_control,\n+ const ControlValue *control_values,\n+ ControlValue *v4l2_values,\n+ size_t num_values);\n+bool simpleControlFromV4L2(unsigned int v4l2_control,\n+ unsigned int *control,\n+ const ControlValue *v4l2_values,\n+ ControlValue *control_values,\n+ size_t num_values);\n+\n+ControlInfoMap simpleControlInfoFromV4L2(const ControlInfoMap &v4l2_info);\n+\n+ControlList simpleControlListToV4L2(const ControlList &controls);\n+ControlList simpleControlListFromV4L2(const ControlList &controls);\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/pipeline/simple/meson.build\nb/src/libcamera/pipeline/simple/meson.build\nindex 9c99b32f..0c60d65a 100644\n--- a/src/libcamera/pipeline/simple/meson.build\n+++ b/src/libcamera/pipeline/simple/meson.build\n@@ -1,6 +1,7 @@\n # SPDX-License-Identifier: CC0-1.0\n\n libcamera_sources += files([\n+ 'controls.cpp',\n 'converter.cpp',\n 'simple.cpp',\n ])\ndiff --git a/src/libcamera/pipeline/simple/simple.cpp\nb/src/libcamera/pipeline/simple/simple.cpp\nindex a597e27f..1717a1a7 100644\n--- a/src/libcamera/pipeline/simple/simple.cpp\n+++ b/src/libcamera/pipeline/simple/simple.cpp\n@@ -36,6 +36,7 @@\n #include \"libcamera/internal/v4l2_subdevice.h\"\n #include \"libcamera/internal/v4l2_videodevice.h\"\n\n+#include \"controls.h\"\n #include \"converter.h\"\n\n namespace libcamera {\n@@ -181,6 +182,7 @@ public:\n int setupLinks();\n int setupFormats(V4L2SubdeviceFormat *format,\n V4L2Subdevice::Whence whence);\n+ int setRequestControls(Request *request);\n void bufferReady(FrameBuffer *buffer);\n\n unsigned int streamIndex(const Stream *stream) const\n@@ -519,7 +521,8 @@ int SimpleCameraData::init()\n formats_[fmt] = &config;\n }\n\n- properties_ = sensor_->properties();\n+ properties_ = simpleControlListFromV4L2(sensor_->properties());\n+ controlInfo_ = simpleControlInfoFromV4L2(sensor_->controls());\n\n return 0;\n }\n@@ -624,6 +627,23 @@ int\nSimpleCameraData::setupFormats(V4L2SubdeviceFormat *format,\n return 0;\n }\n\n+int SimpleCameraData::setRequestControls(Request *request)\n+{\n+ SimplePipelineHandler *pipe = SimpleCameraData::pipe();\n+\n+ // Apply controls only to one entity. If there's a subdevice use that.\n+ V4L2Device *control_device = video_;\n+ for (const SimpleCameraData::Entity &e : entities_) {\n+ V4L2Subdevice *subdev = pipe->subdev(e.entity);\n+ if (subdev) {\n+ control_device = subdev;\n+ }\n+ }\n+\n+ ControlList controls = simpleControlListToV4L2(request->controls());\n+ return control_device->setControls(&controls);\n+}\n+\n void SimpleCameraData::bufferReady(FrameBuffer *buffer)\n {\n SimplePipelineHandler *pipe = SimpleCameraData::pipe();\n@@ -666,6 +686,10 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)\n return;\n }\n\n+ // Set the controls for the next queued request\n+ if (!queuedRequests_.empty())\n+ setRequestControls(queuedRequests_.front());\n+\n /*\n * Record the sensor's timestamp in the request metadata. The request\n * needs to be obtained from the user-facing buffer, as internal\n@@ -1033,6 +1057,10 @@ int SimplePipelineHandler::start(Camera\n*camera, [[maybe_unused]] const ControlL\n return ret;\n }\n\n+ // Apply controls from first request\n+ if (!data->queuedRequests_.empty())\n+ data->setRequestControls(data->queuedRequests_.front());\n+\n if (data->useConverter_) {\n ret = data->converter_->start();\n if (ret < 0) {\ndiff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\nindex 39f36009..7a3b56a2 100644\n--- a/src/libcamera/v4l2_device.cpp\n+++ b/src/libcamera/v4l2_device.cpp\n@@ -293,6 +293,13 @@ int V4L2Device::setControls(ControlList *ctrls)\n /* Set the v4l2_ext_control value for the write operation. */\n ControlValue &value = ctrl->second;\n switch (iter->first->type()) {\n+ case ControlTypeBool:\n+ v4l2Ctrl.value64 = value.get<bool>();\n+ break;\n+\n+ case ControlTypeNone:\n+ break;\n+\n case ControlTypeInteger64:\n v4l2Ctrl.value64 = value.get<int64_t>();\n break;\n@@ -476,7 +483,6 @@ ControlType V4L2Device::v4l2CtrlType(uint32_t ctrlType)\n return ControlTypeInteger64;\n\n case V4L2_CTRL_TYPE_MENU:\n- case V4L2_CTRL_TYPE_BUTTON:\n case V4L2_CTRL_TYPE_BITMASK:\n case V4L2_CTRL_TYPE_INTEGER_MENU:\n /*\n@@ -485,6 +491,7 @@ ControlType V4L2Device::v4l2CtrlType(uint32_t ctrlType)\n */\n return ControlTypeInteger32;\n\n+ case V4L2_CTRL_TYPE_BUTTON:\n default:\n return ControlTypeNone;\n }\n@@ -527,6 +534,9 @@ ControlInfo V4L2Device::v4l2ControlInfo(const\nv4l2_query_ext_ctrl &ctrl)\n static_cast<int64_t>(ctrl.maximum),\n static_cast<int64_t>(ctrl.default_value));\n\n+ case V4L2_CTRL_TYPE_BUTTON:\n+ return ControlInfo(ControlValue(), ControlValue(), ControlValue());\n+\n case V4L2_CTRL_TYPE_INTEGER_MENU:\n case V4L2_CTRL_TYPE_MENU:\n", "prefixes": [ "libcamera-devel" ] }