From patchwork Fri Oct 24 15:10:58 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Julien Vuillaumier X-Patchwork-Id: 24810 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 8B8B8BE080 for ; Fri, 24 Oct 2025 15:12:00 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4CA53609B8; Fri, 24 Oct 2025 17:12:00 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=nxp.com header.i=@nxp.com header.b="VPFyg0HU"; dkim-atps=neutral Received: from OSPPR02CU001.outbound.protection.outlook.com (mail-norwayeastazlp170130007.outbound.protection.outlook.com [IPv6:2a01:111:f403:c20f::7]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5B2A56097E for ; Fri, 24 Oct 2025 17:11:59 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=Ug3xhWrsl1xzaTfrwZeRlP7brMjWsDJAAueUkKmKeeCZR13vcjYD8ZTYDFjrAkfTwvW5wPZd20uAYn/7MRyZnMuBjBIuHVISzSCXY7FD3ScA8oZqARrRpqcxIiPw4wACJK/MLOq7BtynmIrlAVLio5iBR6/YwUqScmlXji1kPZR6NqYsL2w1f6/TaRG/9CUx5RylNIUthlIgFdrESe/aqYlGD2Dh+qYAEtUBhKO4bfl+CEvqNBqL9TKvvhCO7W8r0M0JqEFkd/5j+v3pFZ5AzmXOWMVnegRLvG7soQCXc/Tqb4GEuRh61c79Rd+WLdaVjHOZnuncAIN0LN3uYnXeZA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=Vb/YeCjVaYSAZ2P53BvQnfIoJItQtp6cAEBx3DjboCo=; b=vaMVYnF6ierocBwXA2VvC+fgPG92z5E2jlUcZevxHzZ1snDow17kV1OnHDVpsuws3MfP/ZhQozqCWFvXcE5LrwLGblb5u0GH5Prb8DXhDRSTYNnIT6ZxJh/zLWdVwvCh+eqe/yVuLE7TSW4QsrCz1beelJMI6PWnEhYzmB/h1gFE4mieHbfBvZ0rnAlTbSRLaIreagnI9Th7O+VQILbin/6s4EuAQvSsq7JTAUfIpw3FAO1M3/TPmRax7kPXMFrW5soVch7A7qNGhhf2zJKtLo0l5vxQJjk3KR+TlOnWe7MK1IYebKEtX1EpdanscTPefvJW981r8IBv7D3xGzVaWQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Vb/YeCjVaYSAZ2P53BvQnfIoJItQtp6cAEBx3DjboCo=; b=VPFyg0HUZ8ZAf7s3zw0H2hG3GG/S+t0SZ4M56YlqecMPN4I9nKgdQNs5lkC8c3KHaIsR0bVIzLivZH+8YE0FlSKknCh/hpwdkT9IaOBOrIwKfSTO/aZiMM8T3ZuH25ReCrgrfdiUCaZ4ZEX2cUlxwf61QON6x08JDuKMTEk4lqr70pwemTddUb6bY8z6RJOJ6gFDAYE/3LYXtH7YxIpx4tg8HJMuiFXbWZQLTXFwFoVS1PnrY13T0f41ApD3H20N30JeTYT1rPGao6EZtWBU68PFR5QiFZNjcnGJGk6EQ8hD7MSKrdW7tdr0jLJjXcfv2Va9eSbCip1eakmzGW7DmA== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from AM9PR04MB8147.eurprd04.prod.outlook.com (2603:10a6:20b:3e0::22) by VI0PR04MB11784.eurprd04.prod.outlook.com (2603:10a6:800:2ea::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9253.13; Fri, 24 Oct 2025 15:11:57 +0000 Received: from AM9PR04MB8147.eurprd04.prod.outlook.com ([fe80::b387:72c6:e33c:8656]) by AM9PR04MB8147.eurprd04.prod.outlook.com ([fe80::b387:72c6:e33c:8656%7]) with mapi id 15.20.9253.011; Fri, 24 Oct 2025 15:11:56 +0000 From: Julien Vuillaumier To: libcamera-devel@lists.libcamera.org Cc: Julien Vuillaumier Subject: [RFC PATCH v1 2/2] libcamera: camera_sensor: Add support for auxiliary image Date: Fri, 24 Oct 2025 17:10:58 +0200 Message-Id: <20251024151058.1523308-3-julien.vuillaumier@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251024151058.1523308-1-julien.vuillaumier@nxp.com> References: <20251024151058.1523308-1-julien.vuillaumier@nxp.com> X-ClientProxiedBy: AM8P189CA0019.EURP189.PROD.OUTLOOK.COM (2603:10a6:20b:218::24) To AM9PR04MB8147.eurprd04.prod.outlook.com (2603:10a6:20b:3e0::22) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: AM9PR04MB8147:EE_|VI0PR04MB11784:EE_ X-MS-Office365-Filtering-Correlation-Id: 69c9281d-7fd3-4661-c6b0-08de130fabb2 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; ARA:13230040|19092799006|52116014|376014|366016|1800799024|38350700014; X-Microsoft-Antispam-Message-Info: w0AIRPwnBZL4b85ACz3NTUDlnmvIouLrtpSVvwDY6GyRTFGhKwFuBdPbJljHtXIrKLmVKGJ8hCpm4nW/8Q3a7wuZu56Y3oZCVQQBQdxYSpMCEkAarMkJdITw9Q9ArnZaoy7y8WvlhXEzlwi9G5o4AS7FC19WMelMIujBHtZ/a4ddRA4VJkBKSiHnS1dUcBnaVxlggIthrfYytNCiAkmoEgSm6I548FaiVrzBkx+Av9D7deOQhJvtOTwtdoPHoGvWV+a4tCo971zSjPD9EXvGRZhExtE/iZoMiIYSDQtf9Y/9BnFx6SP8g2U5T8XKhHctT/iLxcqDAs6iz+Prf2ex1oexOfUqffGRpze87W39rLEyzOWLechAgTssfXRtTkX41bMojDOSYi2do+4VXxvUnKHCQ/rt7c0k8Tf7HyAyLM46vdtTyVtadcI978fw56c8xL6SK10YaAdZorf1iEHRFTZTd19LgVqrDEU1HkvYpIPeFEXcdQgjghVjHWwT5WrNyGu05OlKFaG6ynWqYRuMMQGYdVliF5gr3f6CPjmQ+qByAF8HuJbBrtBmUUk/a2BeTcp8zYJVgbV3dk8qV764xeKnRwKaj3h4LkWVGNkB1rFe0ZaigaoZk2hLez3uJRDrSY2xS8zQ+osOu8vEhw3bgDPoOXu3iA1qT/9GjZDMitZvWdfdi4DKvyN/N2ohxfB8T7EkrOZgXQ6mcWLbNrP2i8fSP2e9yOID319vVUOpS5CPO9uJrouCneNgTipMYAM6yhf7GiV5rYS1bZuKVZRovNlTHt2iZ1oscXDwb78F/FMT3tL+5yOm6ETutRHNOE0bpupDmj3NVgu3W6QX0vhKNvB368ybBEv3wWnCrCatuvQGFR/Njj07PMicc07XYGW6gYmFcdD+WK+myex9eMjy8SCfdCwaR10M5zZCWAYoIFoV/Ttw6r9LhJqGUuD5fnKyXKY4U0fV7Cai1LX/frCz//Lg/Ee4gTZpf2CT+d6mgQGDaJeXd+Dd8m6XPd8ydwa6t7L6vRDAfnzNmH2ojsrnCBAAA/HR0h1ASstAbUEgO+viJRD5+coTRX767p4tnYYS4+dDnpOGqoaMOhx4K1/ZLYY6GlXdciCUMO0rTdKdpP4Jzz9t5UgNbgqNsejAdDWyAuaeRe8k3hzIKv34AefIMWkYJ7HuqWGHnzUj0oUfM/FArZlVolKMgFARwnzQRYFYx56bHFZv0wq9QlZXHYXyMMqr6wDXEItjSfdXSs+E9ZMpRDq0KuEE7qlQcBIUKy1mH6pOfeVCG1IxTWCQSo+PpzhjR9+3jp61FhGaaAnyyYfXaMd8RhLpkfyCxcyNjifO8nQjdwFlQUQYCfqOuQa0EeeqxdOV/J6WbS/O9fhrActlZBHxUBAD2oOZjkmWMdK81CvYgN440RcNKv3tx2ZOr0pnfEgzoBi62+eJOZeB/6PWuAMudj5QM0cVd15i4WGTuwix4QlptouWejhBEjhEGXWVVmwisLTUbyWTfIGn/8luulW//KUAL6e0euak8sN4 X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:AM9PR04MB8147.eurprd04.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230040)(19092799006)(52116014)(376014)(366016)(1800799024)(38350700014); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: M6jbWtZPZFSvayV4KLoWWf2Npt3JA5a2F/cPk3KgatnRHjxmHh0M+Lt9+iN9UVjCB22ZbrwY+9cxHsP5xOPn0lGkhn9dddCAQ0A8oP3l0zEFUYP8tCqBc/doO5ZkRl3NSTXXuc1T/x9Ybc1t4u8BlmGtF7gw/EadmjGXeWKFuv40l564BtavHIBrDWxxF88LrBWDkfmRVI/drSZOMGgwpyJX9K7wKDePBZAdlFXXcTcz+RpmTEiBKa7A0z9XxMjwN5p9DEIDxdZ0r5X5k/a3XTd+Id8rXKZA6SZ8eEOu3oqlJcjRpfDZ29Eq5rMq8DbmEAy43/sqc3KzKR9gMB6SVV/aCy1SlciKhzhuUvXj+08Ftf+cHM+Hb8Z/RcdWZyoMunAuXgwjaghL2G5hqx5XBFXvRhwFOpliOKMMuubhjSslYU6HkeMGaK163bK2rInWB4kBhQfsrk52zPB/59S80mY4MePHdO7yw/iXb06wjkn6hvHsM1AS97P9KUhPMfTSzIo3VKxEoF3gYhi44W+unpC4S39JxU54wtmr/5TzsjWffcl9bHdhTVUH5uhRzqYCXG/T1upNqDMqrKgc8riXlHfuPfDgsDJ/KjFn8Cu+cK+k8x5xJbk46CcpeGyFqqCOiYHxL9tkEd6wuiul0OyAS7ryheLDl7p2QuQ5r/TgCoxHSbVXg28XlT9PPt4YEvGh14J4M4G6/F5qV0avwjFDB4xvWcgbaVH3GWqLB105YfzvaTNXeUMyvgrbUv0b2GR4Lzy2it/+ZJMOUDnDN+orWU+Ds9xcr8cw5Jm+8TL2eT3M3lHTz8YvxE9rbKGGzrpxf5Tt//BpPqP37rx/KtJJ2DEGle+303JRUQleyY+murtFUbVY+41wrlsqtKJvFmkIC3FpgbYPITchQAY3WtBkXJ6+rP1ie7J1vTArAfLGnJljD5vU2njlZ+4w2jpASu9DzNd7XahHr8PgqncOM8KKHAGEt8uSVKSm5Sc+xPotA+iao0leluKQiI/bxty22LKTAqeFsyzDjK9An6T4iftcxovXJcr54RscRAqathkbirDGVHDJYjo7vz7tWUEDme03pTw7GzCrHHVbGWxWmGLHJDcODllQ06AjjSC7hNxcmd7n4wp7CAI1sUL9V0m8jeeQLt5RTRPcTn+Gf8MPp9770R28b6Zpx+/m/ez7wTMGsXcCW6t62rWbnLdJ7Ed5JLeSnqqgX2B4lQ7vJEjIGEwuWi8a+V5R3og24c9Lx+hOGgmTDjlhSm6VQZG+Pjrvs5oNW7Xwyr+wolQE4Nax6G96xSvfPn0mvbkAC46HI1F971tVIOZncoQoLQuvK3g6Jhx8zTEBkk3lDUauNloLikLOoBtPVeybUg3b72pp8iSFNaiAQz9xhX5AyIkIKqIVIyrM0X3wOWrC2UYuS/ijZrC+JeLy9FPA+dwr9X0bOt5GT15V5kc3OZ0LiCeB79iKpyWa6UzA5mr/p+1DjZ0jkCSb2GUce0TDMv74hUv7MAh+S/I6PTu3RmjeKAxeyX3KctydFXoCGyuZ8Z3J82jeVj30Hkqu/YEGP0d4XAPOk1IjEvu5yOawFtMvtVhz/G2OtZUBK274lzUf58xMnNEeX6Z2tg== X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 69c9281d-7fd3-4661-c6b0-08de130fabb2 X-MS-Exchange-CrossTenant-AuthSource: AM9PR04MB8147.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 24 Oct 2025 15:11:56.1147 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: M8aYyrwQ5TGdQsLEvUrS/3gFc3b6vV0T4/xAvudeb2VDNZ2NUHEeF2TeRrW3QVWPsRMSWjNOenw9zZHMmVw+FJEq8tkdkfzOPAwVnzISrQs= X-MS-Exchange-Transport-CrossTenantHeadersStamped: VI0PR04MB11784 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" Some sensors can produce and transmit an auxiliary image over a dedicated stream, separate from the image stream. Examples for such sensor are the ones supporting HDR or A/B mode captures. Add support for that type of stream in the CameraSensor interface, and implement it for the CameraSensorRaw class. The CameraSensorLegacy uses the default stub implementation, as the corresponding kernel drivers don't support an auxiliary image. Signed-off-by: Julien Vuillaumier --- include/libcamera/internal/camera_sensor.h | 3 + src/libcamera/sensor/camera_sensor.cpp | 52 ++++++++++ src/libcamera/sensor/camera_sensor_raw.cpp | 112 ++++++++++++++++++--- 3 files changed, 151 insertions(+), 16 deletions(-) diff --git a/include/libcamera/internal/camera_sensor.h b/include/libcamera/internal/camera_sensor.h index f6ef4df1..6df928f5 100644 --- a/include/libcamera/internal/camera_sensor.h +++ b/include/libcamera/internal/camera_sensor.h @@ -68,6 +68,9 @@ public: virtual std::optional embeddedDataStream() const; virtual V4L2SubdeviceFormat embeddedDataFormat() const; virtual int setEmbeddedDataEnabled(bool enable); + virtual std::optional auxiliaryStream() const; + virtual V4L2SubdeviceFormat auxiliaryFormat() const; + virtual int setAuxiliaryEnabled(bool enable); virtual const ControlList &properties() const = 0; virtual int sensorInfo(IPACameraSensorInfo *info) const = 0; diff --git a/src/libcamera/sensor/camera_sensor.cpp b/src/libcamera/sensor/camera_sensor.cpp index 4f2fd269..3bf05f11 100644 --- a/src/libcamera/sensor/camera_sensor.cpp +++ b/src/libcamera/sensor/camera_sensor.cpp @@ -266,6 +266,58 @@ int CameraSensor::setEmbeddedDataEnabled(bool enable) return enable ? -ENOSTR : 0; } +/** + * \brief Retrieve the auxiliary image source stream + * + * Some sensors produce an auxiliary image stream separate from the image + * stream. This function indicates if the sensor supports this feature by + * returning the auxialiary stream on the sensor's source pad if available, + * or an std::optional<> without a value otheriwse. + * + * \return The auxiliary source stream + */ +std::optional CameraSensor::auxiliaryStream() const +{ + return {}; +} + +/** + * \brief Retrieve the format on the auxiliary stream + * + * When an auxiliary image stream is available, this function returns the + * corresponding format on the sensor's source pad. The format may vary with + * the image stream format, and should therefore be retrieved after configuring + * the image stream. + * + * If the sensor doesn't support auxiliary stream, this function returns a + * default-constructed format. + * + * \return The format on the embedded data stream + */ +V4L2SubdeviceFormat CameraSensor::auxiliaryFormat() const +{ + return {}; +} + +/** + * \brief Enable or disable the auxiliary image stream + * \param[in] enable True to enable the auxiliary image stream, false to disable it + * + * For sensors that support it function enables or disables generation of + * auxiliary image stream. Some of such sensors always produce an auxiliary + * stream, in which case this function return -EISCONN if the caller attempts to + * disable it. + * + * If the sensor doesn't support an auxiliary image stream, this function + * returns 0 when \a enable is false, and -ENOSTR otherwise. + * + * \return 0 on success, or a negative error code otherwise + */ +int CameraSensor::setAuxiliaryEnabled(bool enable) +{ + return enable ? -ENOSTR : 0; +} + /** * \fn CameraSensor::properties() * \brief Retrieve the camera sensor properties diff --git a/src/libcamera/sensor/camera_sensor_raw.cpp b/src/libcamera/sensor/camera_sensor_raw.cpp index ad4a94ad..ecc64b44 100644 --- a/src/libcamera/sensor/camera_sensor_raw.cpp +++ b/src/libcamera/sensor/camera_sensor_raw.cpp @@ -89,6 +89,9 @@ public: std::optional embeddedDataStream() const override; V4L2SubdeviceFormat embeddedDataFormat() const override; int setEmbeddedDataEnabled(bool enable) override; + virtual std::optional auxiliaryStream() const override; + virtual V4L2SubdeviceFormat auxiliaryFormat() const override; + virtual int setAuxiliaryEnabled(bool enable) override; const ControlList &properties() const override { return properties_; } int sensorInfo(IPACameraSensorInfo *info) const override; @@ -127,6 +130,7 @@ private: struct { Streams image; std::optional edata; + std::optional auxiliary; } streams_; const CameraSensorProperties *staticProps_; @@ -206,7 +210,7 @@ CameraSensorRaw::match(MediaEntity *entity) } } - if (numSinks < 1 || numSinks > 2 || numSources != 1) { + if (numSinks < 1 || numSinks > 3 || numSources != 1) { libcamera::LOG(CameraSensor, Debug) << entity->name() << ": unsupported number of sinks (" << numSinks << ") or sources (" << numSources << ")"; @@ -277,6 +281,7 @@ std::optional CameraSensorRaw::init() } bool imageStreamFound = false; + bool auxiliaryStreamFound = false; for (const V4L2Subdevice::Route &route : routing) { if (route.source.pad != sourcePad) { @@ -306,17 +311,21 @@ std::optional CameraSensorRaw::init() switch (*type) { case MediaBusFormatInfo::Type::Image: - if (imageStreamFound) { + /* Assume that primary image is on the stream 0/0 */ + if (!imageStreamFound && + route.source.pad == 0 && route.source.stream == 0) { + imageStreamFound = true; + streams_.image = { route.sink, route.source }; + } else if (!auxiliaryStreamFound) { + auxiliaryStreamFound = true; + streams_.auxiliary = { route.sink, route.source }; + } else { LOG(CameraSensor, Error) << "Multiple internal image streams (" << streams_.image.sink << " and " << route.sink << ")"; return { -EINVAL }; } - - imageStreamFound = true; - streams_.image.sink = route.sink; - streams_.image.source = route.source; break; case MediaBusFormatInfo::Type::Metadata: @@ -363,6 +372,11 @@ std::optional CameraSensorRaw::init() << "Found embedded data stream " << streams_.edata->sink << " -> " << streams_.edata->source; + if (streams_.auxiliary) + LOG(CameraSensor, Debug) + << "Found auxiliary stream " << streams_.auxiliary->sink + << " -> " << streams_.auxiliary->source; + /* Restore the routes to their initial state */ ret = subdev_->setRouting(&routing); if (ret) { @@ -963,20 +977,22 @@ V4L2SubdeviceFormat CameraSensorRaw::embeddedDataFormat() const int CameraSensorRaw::setEmbeddedDataEnabled(bool enable) { + int ret; + if (!streams_.edata) return enable ? -ENOSTR : 0; - V4L2Subdevice::Routing routing{ 2 }; - - routing[0].sink = streams_.image.sink; - routing[0].source = streams_.image.source; - routing[0].flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE; - - routing[1].sink = streams_.edata->sink; - routing[1].source = streams_.edata->source; - routing[1].flags = enable ? V4L2_SUBDEV_ROUTE_FL_ACTIVE : 0; + V4L2Subdevice::Routing routing; + ret = subdev_->getRouting(&routing); + if (ret) + return ret; - int ret = subdev_->setRouting(&routing); + for (V4L2Subdevice::Route &route : routing) { + if (route.source != streams_.edata->source) + continue; + route.flags = enable ? V4L2_SUBDEV_ROUTE_FL_ACTIVE : 0; + } + ret = subdev_->setRouting(&routing); if (ret) return ret; @@ -1002,6 +1018,70 @@ int CameraSensorRaw::setEmbeddedDataEnabled(bool enable) return 0; } +std::optional CameraSensorRaw::auxiliaryStream() const +{ + if (!streams_.auxiliary) + return {}; + + return { streams_.auxiliary->source }; +} + +V4L2SubdeviceFormat CameraSensorRaw::auxiliaryFormat() const +{ + if (!streams_.auxiliary) + return {}; + + V4L2SubdeviceFormat format; + int ret = subdev_->getFormat(streams_.auxiliary->source, &format); + if (ret) + return {}; + + return format; +} + +int CameraSensorRaw::setAuxiliaryEnabled(bool enable) +{ + int ret; + + if (!streams_.auxiliary) + return enable ? -ENOSTR : 0; + + V4L2Subdevice::Routing routing; + ret = subdev_->getRouting(&routing); + if (ret) + return ret; + + for (V4L2Subdevice::Route &route : routing) { + if (route.source != streams_.auxiliary->source) + continue; + route.flags = enable ? V4L2_SUBDEV_ROUTE_FL_ACTIVE : 0; + } + ret = subdev_->setRouting(&routing); + if (ret) + return ret; + + /* + * Check if the auxiliary stream has been enabled or disabled + * correctly. Assume at least one route will match the auxiliary + * source stream, as there would be something seriously wrong + * otherwise. + */ + bool enabled = false; + + for (const V4L2Subdevice::Route &route : routing) { + if (route.source != streams_.auxiliary->source) + continue; + + enabled = route.flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE; + break; + } + + if (enabled != enable) + return enabled ? -EISCONN : -ENOSTR; + + return 0; +} + int CameraSensorRaw::sensorInfo(IPACameraSensorInfo *info) const { info->model = model();