From patchwork Fri Mar 15 00:16:08 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 19728 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 8E017BD808 for ; Fri, 15 Mar 2024 00:16:39 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D634162CAE; Fri, 15 Mar 2024 01:16:38 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="JeIAtNl5"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 450A362C93 for ; Fri, 15 Mar 2024 01:16:29 +0100 (CET) Received: from pendragon.ideasonboard.com (81-175-209-231.bb.dnainternet.fi [81.175.209.231]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 909B4A9A for ; Fri, 15 Mar 2024 01:16:05 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1710461765; bh=GqtDp/jzJkaFBfGx4Qgz8/jXuDT4suXoHYmy2+893xQ=; h=From:To:Subject:Date:In-Reply-To:References:From; b=JeIAtNl5z2lH7o0ARzHwaKwx8iIXzMjV55uLYaerdow0oUZ/VveSid/ngJMgFEmaU 4imxjdOdrTTmZnbIfnsbdJR4no5Erdbdn45G8ssXF4JdCkOYjuGIFB2dplxjotQhDf bvw6cOA9TahWX++AalFRaV1QlS7IO5bgTSr3D59Y= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 09/14] libcamera: v4l2_subdevice: Add V4L2Subdevice::Route structure Date: Fri, 15 Mar 2024 02:16:08 +0200 Message-ID: <20240315001613.2033-10-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.43.2 In-Reply-To: <20240315001613.2033-1-laurent.pinchart@ideasonboard.com> References: <20240315001613.2033-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 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 V4L2Subdevice class deals with streams in two places: - In routing tables, streams as expressed as a pad number and a stream number in a v4l2_subdev_route instance. - In the format and selection get and set functions, streams as expressed using the Stream structure, which binds the pad number and stream number. Expressing streams in different ways requires pipeline handlers and other helpers to convert between the two representations. This isn't much of an issue yet as libcamera has little stream-aware code, but it is expected to increasingly become a burden. To simplify the API, introduce a V4L2Subdevice::Route structure that mimicks the kernel v4l2_subdev_route structure but represents streams as V4L2Subdevice::Stream instances. This will improve seamless integration of routes, formats and selection rectangles. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham --- Changes since combined RFC: - Fix compilation in imx8-isi pipeline handler - Fix indentation in comments Changes since v1: - Clear routing at the beginning of getRouting() and setRouting() --- include/libcamera/internal/v4l2_subdevice.h | 19 +++- src/libcamera/pipeline/imx8-isi/imx8-isi.cpp | 14 +-- src/libcamera/pipeline/simple/simple.cpp | 8 +- src/libcamera/v4l2_subdevice.cpp | 108 +++++++++++++++++-- 4 files changed, 123 insertions(+), 26 deletions(-) diff --git a/include/libcamera/internal/v4l2_subdevice.h b/include/libcamera/internal/v4l2_subdevice.h index 2939dc2411c6..01ed4c2fc397 100644 --- a/include/libcamera/internal/v4l2_subdevice.h +++ b/include/libcamera/internal/v4l2_subdevice.h @@ -95,7 +95,23 @@ public: unsigned int stream; }; - using Routing = std::vector; + struct Route { + Route() + : flags(0) + { + } + + Route(const Stream &snk, const Stream &src, uint32_t f) + : sink(snk), source(src), flags(f) + { + } + + Stream sink; + Stream source; + uint32_t flags; + }; + + using Routing = std::vector; explicit V4L2Subdevice(const MediaEntity *entity); ~V4L2Subdevice(); @@ -174,6 +190,7 @@ static inline bool operator!=(const V4L2Subdevice::Stream &lhs, } std::ostream &operator<<(std::ostream &out, const V4L2Subdevice::Stream &stream); +std::ostream &operator<<(std::ostream &out, const V4L2Subdevice::Route &route); std::ostream &operator<<(std::ostream &out, const V4L2Subdevice::Routing &routing); } /* namespace libcamera */ diff --git a/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp b/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp index a3782aea2ba9..63082cea7e56 100644 --- a/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp +++ b/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp @@ -827,16 +827,10 @@ int PipelineHandlerISI::configure(Camera *camera, CameraConfiguration *c) unsigned int xbarFirstSource = crossbar_->entity()->pads().size() / 2 + 1; for (const auto &[idx, config] : utils::enumerate(*c)) { - struct v4l2_subdev_route route = { - .sink_pad = data->xbarSink_, - .sink_stream = 0, - .source_pad = static_cast(xbarFirstSource + idx), - .source_stream = 0, - .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, - .reserved = {} - }; - - routing.push_back(route); + uint32_t sourcePad = xbarFirstSource + idx; + routing.emplace_back(V4L2Subdevice::Stream{ data->xbarSink_, 0 }, + V4L2Subdevice::Stream{ sourcePad, 0 }, + V4L2_SUBDEV_ROUTE_FL_ACTIVE); } int ret = crossbar_->setRouting(&routing, V4L2Subdevice::ActiveFormat); diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp index feea26fd124f..01f2a97798ba 100644 --- a/src/libcamera/pipeline/simple/simple.cpp +++ b/src/libcamera/pipeline/simple/simple.cpp @@ -852,17 +852,17 @@ std::vector SimpleCameraData::routedSourcePads(MediaPad *sink) std::vector pads; - for (const struct v4l2_subdev_route &route : routing) { - if (sink->index() != route.sink_pad || + for (const V4L2Subdevice::Route &route : routing) { + if (sink->index() != route.sink.pad || !(route.flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) continue; - const MediaPad *pad = entity->getPadByIndex(route.source_pad); + const MediaPad *pad = entity->getPadByIndex(route.source.pad); if (!pad) { LOG(SimplePipeline, Warning) << "Entity " << entity->name() << " has invalid route source pad " - << route.source_pad; + << route.source.pad; } pads.push_back(pad); diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp index deef681e0d3a..1076b7006b0b 100644 --- a/src/libcamera/v4l2_subdevice.cpp +++ b/src/libcamera/v4l2_subdevice.cpp @@ -898,6 +898,53 @@ std::ostream &operator<<(std::ostream &out, const V4L2Subdevice::Stream &stream) return out; } +/** + * \class V4L2Subdevice::Route + * \brief V4L2 subdevice routing table entry + * + * This class models a route in the subdevice routing table. It is similar to + * the v4l2_subdev_route structure, but uses the V4L2Subdevice::Stream class + * for easier usage with the V4L2Subdevice stream-aware functions. + * + * \var V4L2Subdevice::Route::sink + * \brief The sink stream of the route + * + * \var V4L2Subdevice::Route::source + * \brief The source stream of the route + * + * \var V4L2Subdevice::Route::flags + * \brief The route flags (V4L2_SUBDEV_ROUTE_FL_*) + */ + +/** + * \fn V4L2Subdevice::Route::Route() + * \brief Construct a Route with default streams + */ + +/** + * \fn V4L2Subdevice::Route::Route(const Stream &sink, const Stream &source, + * uint32_t flags) + * \brief Construct a Route from \a sink to \a source + * \param[in] sink The sink stream + * \param[in] source The source stream + * \param[in] flags The route flags + */ + +/** + * \brief Insert a text representation of a V4L2Subdevice::Route into an + * output stream + * \param[in] out The output stream + * \param[in] route The V4L2Subdevice::Route + * \return The output stream \a out + */ +std::ostream &operator<<(std::ostream &out, const V4L2Subdevice::Route &route) +{ + out << route.sink << " -> " << route.source + << " (" << utils::hex(route.flags) << ")"; + + return out; +} + /** * \typedef V4L2Subdevice::Routing * \brief V4L2 subdevice routing table @@ -907,7 +954,7 @@ std::ostream &operator<<(std::ostream &out, const V4L2Subdevice::Stream &stream) /** * \brief Insert a text representation of a V4L2Subdevice::Routing into an - * output stream + * output stream * \param[in] out The output stream * \param[in] routing The V4L2Subdevice::Routing * \return The output stream \a out @@ -915,10 +962,7 @@ std::ostream &operator<<(std::ostream &out, const V4L2Subdevice::Stream &stream) std::ostream &operator<<(std::ostream &out, const V4L2Subdevice::Routing &routing) { for (const auto &[i, route] : utils::enumerate(routing)) { - out << "[" << i << "] " - << route.sink_pad << "/" << route.sink_stream << " -> " - << route.source_pad << "/" << route.source_stream - << " (" << utils::hex(route.flags) << ")"; + out << "[" << i << "] " << route; if (i != routing.size() - 1) out << ", "; } @@ -1272,6 +1316,30 @@ int V4L2Subdevice::setFormat(const Stream &stream, V4L2SubdeviceFormat *format, * \return 0 on success or a negative error code otherwise */ +namespace { + +void routeFromKernel(V4L2Subdevice::Route &route, + const struct v4l2_subdev_route &kroute) +{ + route.sink.pad = kroute.sink_pad; + route.sink.stream = kroute.sink_stream; + route.source.pad = kroute.source_pad; + route.source.stream = kroute.source_stream; + route.flags = kroute.flags; +} + +void routeToKernel(const V4L2Subdevice::Route &route, + struct v4l2_subdev_route &kroute) +{ + kroute.sink_pad = route.sink.pad; + kroute.sink_stream = route.sink.stream; + kroute.source_pad = route.source.pad; + kroute.source_stream = route.source.stream; + kroute.flags = route.flags; +} + +} /* namespace */ + /** * \brief Retrieve the subdevice's internal routing table * \param[out] routing The routing table @@ -1282,6 +1350,8 @@ int V4L2Subdevice::setFormat(const Stream &stream, V4L2SubdeviceFormat *format, */ int V4L2Subdevice::getRouting(Routing *routing, Whence whence) { + routing->clear(); + if (!caps_.hasStreams()) return 0; @@ -1300,8 +1370,8 @@ int V4L2Subdevice::getRouting(Routing *routing, Whence whence) return ret; } - routing->resize(rt.num_routes); - rt.routes = reinterpret_cast(routing->data()); + std::vector routes{ rt.num_routes }; + rt.routes = reinterpret_cast(routes.data()); ret = ioctl(VIDIOC_SUBDEV_G_ROUTING, &rt); if (ret) { @@ -1310,11 +1380,16 @@ int V4L2Subdevice::getRouting(Routing *routing, Whence whence) return ret; } - if (rt.num_routes != routing->size()) { + if (rt.num_routes != routes.size()) { LOG(V4L2, Error) << "Invalid number of routes"; return -EINVAL; } + routing->resize(rt.num_routes); + + for (const auto &[i, route] : utils::enumerate(routes)) + routeFromKernel((*routing)[i], route); + return 0; } @@ -1332,13 +1407,20 @@ int V4L2Subdevice::getRouting(Routing *routing, Whence whence) */ int V4L2Subdevice::setRouting(Routing *routing, Whence whence) { - if (!caps_.hasStreams()) + if (!caps_.hasStreams()) { + routing->clear(); return 0; + } + + std::vector routes{ routing->size() }; + + for (const auto &[i, route] : utils::enumerate(*routing)) + routeToKernel(route, routes[i]); struct v4l2_subdev_routing rt = {}; rt.which = whence; - rt.num_routes = routing->size(); - rt.routes = reinterpret_cast(routing->data()); + rt.num_routes = routes.size(); + rt.routes = reinterpret_cast(routes.data()); int ret = ioctl(VIDIOC_SUBDEV_S_ROUTING, &rt); if (ret) { @@ -1346,8 +1428,12 @@ int V4L2Subdevice::setRouting(Routing *routing, Whence whence) return ret; } + routes.resize(rt.num_routes); routing->resize(rt.num_routes); + for (const auto &[i, route] : utils::enumerate(routes)) + routeFromKernel((*routing)[i], route); + return 0; }