From patchwork Fri Mar 13 23:38:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 3085 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8A87A62826 for ; Sat, 14 Mar 2020 00:39:05 +0100 (CET) Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id CD92312F3; Sat, 14 Mar 2020 00:39:04 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1584142745; bh=OuYSMq66Kicl43m1yxRxpkunkmtmaYs0y8lU4TFqw5o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rs8l8kZMPN1CoyFTJJd26RgqBEt2MuHlI6bpF5HNferBXfdqPxbhw+sAw6AzoRotI HBdUR43977TI9YUAwaFY3VIy62bS2KLeFGMJ/TpdHTl5EW+Sn58YTtyKtkQKTw96tX xsEAgNxKXRDN6Tqp6m5eIkk3yJom2vrSm1Ohkroc= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Cc: Martijn Braam , Andrey Konovalov , Mickael GUENE , Benjamin GAIGNARD Date: Sat, 14 Mar 2020 01:38:49 +0200 Message-Id: <20200313233856.25202-2-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200313233856.25202-1-laurent.pinchart@ideasonboard.com> References: <20200313233856.25202-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 1/8] [DNI] include: linux: Extend VIDIOC_ENUM_FMT to support MC-centric devices 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: , X-List-Received-Date: Fri, 13 Mar 2020 23:39:05 -0000 This is an experimental change that hasn't been accepted in mainline yet. Its commit message for the Linux kernel is as follows. media: v4l2: Extend VIDIOC_ENUM_FMT to support MC-centric devices The VIDIOC_ENUM_FMT ioctl enumerates all formats supported by a video node. For MC-centric devices, its behaviour has always been ill-defined, with drivers implementing one of the following behaviours: - No support for VIDIOC_ENUM_FMT at all - Enumerating all formats supported by the video node, regardless of the configuration of the pipeline - Enumerating formats supported by the video node for the active configuration of the connected subdevice The first behaviour is obviously useless for applications. The second behaviour provides the most information, but doesn't offer a way to find what formats are compatible with a given pipeline configuration. The third behaviour fixes that, but with the drawback that applications can't enumerate all supported formats anymore, and have to modify the active configuration of the pipeline to enumerate formats. The situation is messy as none of the implemented behaviours are ideal, and userspace can't predict what will happen as the behaviour is driver-specific. To fix this, let's extend the VIDIOC_ENUM_FMT with a missing capability: enumerating pixel formats for a given media bus code. The media bus code is passed through the v4l2_fmtdesc structure in a new mbus_code field (repurposed from the reserved fields), and an additional flag is added to report if the driver supports this API extension. With this capability in place, applications can enumerate pixel formats for a given media bus code without modifying the active configuration of the device. The current behaviour of the ioctl is preserved when the new mbus_code field is set to 0, ensuring compatibility with existing userspace. This behaviour is now documented as mandatory for MC-centric devices as well as the traditional video node-centric devices. This allows applications to query MC-centric devices for all the supported pixel formats, as well as for the pixel formats corresponding to a given media bus code. Signed-off-by: Laurent Pinchart --- include/linux/videodev2.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index ab40b3272ed2..120130c4aaae 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -769,13 +769,15 @@ struct v4l2_fmtdesc { __u32 flags; __u8 description[32]; /* Description string */ __u32 pixelformat; /* Format fourcc */ - __u32 reserved[4]; + __u32 mbus_code; /* Media bus code */ + __u32 reserved[3]; }; #define V4L2_FMT_FLAG_COMPRESSED 0x0001 #define V4L2_FMT_FLAG_EMULATED 0x0002 #define V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM 0x0004 #define V4L2_FMT_FLAG_DYN_RESOLUTION 0x0008 +#define V4L2_FMT_FLAG_MBUS_CODE 0x0010 /* Frame Size and frame rate enumeration */ /* From patchwork Fri Mar 13 23:38:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 3086 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0CB4C62930 for ; Sat, 14 Mar 2020 00:39:06 +0100 (CET) Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 6779A1367; Sat, 14 Mar 2020 00:39:05 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1584142745; bh=J0c2nfJFXlI6E4sVzYJWTGJhC6/tlaySqex0A1Ia7Ck=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=s0n9IkkV33XCJVAO1ILOQXCMuAQylc1h1Ve11XwYrel0LH1twyzBZazuvZlkp7ia7 Ndhuo9vp4F9HL83WfksGWRNVRKO4nQEUjW5+dA4lqLxDtcrIXgjI/1IxzKoHsz+zri NATDfg+eDPHW+O6VI24f9s7BmXH8QPLx5y6Fgu/g= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Cc: Martijn Braam , Andrey Konovalov , Mickael GUENE , Benjamin GAIGNARD Date: Sat, 14 Mar 2020 01:38:50 +0200 Message-Id: <20200313233856.25202-3-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200313233856.25202-1-laurent.pinchart@ideasonboard.com> References: <20200313233856.25202-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 2/8] libcamera: utils: Add string join function 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: , X-List-Received-Date: Fri, 13 Mar 2020 23:39:06 -0000 Add a utils::join() function to join elements of a container into a string, with a separator and an optional conversion function if the elements are not implicitly convertible to std::string. Signed-off-by: Laurent Pinchart Reviewed-by: Niklas Söderlund --- src/libcamera/include/utils.h | 44 +++++++++++++++++++++++++++++++++++ src/libcamera/utils.cpp | 16 +++++++++++++ test/utils.cpp | 7 +++++- 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/libcamera/include/utils.h b/src/libcamera/include/utils.h index 940597760ee2..74ceed760cb3 100644 --- a/src/libcamera/include/utils.h +++ b/src/libcamera/include/utils.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -109,6 +110,49 @@ inline _hex hex(uint64_t value, unsigned int width) size_t strlcpy(char *dst, const char *src, size_t size); +#ifndef __DOXYGEN__ +template +std::string join(const Container &items, const std::string &sep, UnaryOp op) +{ + std::ostringstream ss; + bool first = true; + + for (typename Container::const_iterator it = std::begin(items); + it != std::end(items); ++it) { + if (!first) + ss << sep; + else + first = false; + + ss << op(*it); + } + + return ss.str(); +} + +template +std::string join(const Container &items, const std::string &sep) +{ + std::ostringstream ss; + bool first = true; + + for (typename Container::const_iterator it = std::begin(items); + it != std::end(items); ++it) { + if (!first) + ss << sep; + else + first = false; + + ss << *it; + } + + return ss.str(); +} +#else +template +std::string join(const Container &items, const std::string &sep, UnaryOp op = nullptr); +#endif + namespace details { class StringSplitter diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp index f566e88cec5b..1d0e583756cf 100644 --- a/src/libcamera/utils.cpp +++ b/src/libcamera/utils.cpp @@ -291,6 +291,22 @@ details::StringSplitter::iterator details::StringSplitter::end() const return iterator(this, std::string::npos); } +/** + * \fn template \ + * std::string utils::join(const Container &items, const std::string &sep, UnaryOp op) + * \brief Join elements of a container in a string with a separator + * \param[in] items The container + * \param[in] sep The separator to add between elements + * \param[in] op A function that converts individual elements to strings + * + * This function joins all elements in the \a items container into a string and + * returns it. The \a sep separator is added between elements. If the container + * elements are not implicitly convertible to std::string, the \a op function + * shall be provided to perform conversion of elements to std::string. + * + * \return A string that concatenates all elements in the container + */ + /** * \fn split(const std::string &str, const std::string &delim) * \brief Split a string based on a delimiter diff --git a/test/utils.cpp b/test/utils.cpp index 58816f153066..2fca89ef3278 100644 --- a/test/utils.cpp +++ b/test/utils.cpp @@ -99,7 +99,7 @@ protected: return TestFail; } - /* utils::split() test. */ + /* utils::join() and utils::split() test. */ std::vector elements = { "/bin", "/usr/bin", @@ -111,6 +111,11 @@ protected: for (const auto &element : elements) path += (path.empty() ? "" : ":") + element; + if (path != utils::join(elements, ":")) { + cerr << "utils::join() test failed" << endl; + return TestFail; + } + std::vector dirs; for (const auto &dir : utils::split(path, ":")) From patchwork Fri Mar 13 23:38:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 3087 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id AD2EA62930 for ; Sat, 14 Mar 2020 00:39:06 +0100 (CET) Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 07E0A140C; Sat, 14 Mar 2020 00:39:05 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1584142746; bh=ccFH0E4QJ0xIQdFikzS5FmkxKJO2msekLDi02QZbSNg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nKD0NwAjPD4yRecKLt47aygST8FQ9b9xBxIlmrEpqOf7XDA3NPRatMtYrmm2F5JST 0PENmCNTN63G+J7V4hKeMolJ4nfNBCwVKjiLp0KSK/N7pUDrgq1Q/97ya/+kN9mJ6j mN7gjGWgQOsPN5zvYhAtjIAtn+Ojf9FFYxM4a53I= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Cc: Martijn Braam , Andrey Konovalov , Mickael GUENE , Benjamin GAIGNARD Date: Sat, 14 Mar 2020 01:38:51 +0200 Message-Id: <20200313233856.25202-4-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200313233856.25202-1-laurent.pinchart@ideasonboard.com> References: <20200313233856.25202-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 3/8] libcamera: v4l2_subdevice: Extend [gs]etFormat() to specify format type 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: , X-List-Received-Date: Fri, 13 Mar 2020 23:39:06 -0000 The V4L2 subdevice API exposes ACTIVE and TRY formats, but the V4L2Subdevice getFormat() and setFormat() operations only apply on ACTIVE formats. Extend them to support TRY formats as well. Signed-off-by: Laurent Pinchart Reviewed-by: Niklas Söderlund --- src/libcamera/include/v4l2_subdevice.h | 11 +++++++++-- src/libcamera/v4l2_subdevice.cpp | 23 +++++++++++++++++++---- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h index 9c077674f997..4a04eadfb1f9 100644 --- a/src/libcamera/include/v4l2_subdevice.h +++ b/src/libcamera/include/v4l2_subdevice.h @@ -32,6 +32,11 @@ struct V4L2SubdeviceFormat { class V4L2Subdevice : public V4L2Device { public: + enum Whence { + ActiveFormat, + TryFormat, + }; + explicit V4L2Subdevice(const MediaEntity *entity); V4L2Subdevice(const V4L2Subdevice &) = delete; V4L2Subdevice &operator=(const V4L2Subdevice &) = delete; @@ -46,8 +51,10 @@ public: ImageFormats formats(unsigned int pad); - int getFormat(unsigned int pad, V4L2SubdeviceFormat *format); - int setFormat(unsigned int pad, V4L2SubdeviceFormat *format); + int getFormat(unsigned int pad, V4L2SubdeviceFormat *format, + Whence whence = ActiveFormat); + int setFormat(unsigned int pad, V4L2SubdeviceFormat *format, + Whence whence = ActiveFormat); static V4L2Subdevice *fromEntityName(const MediaDevice *media, const std::string &entity); diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp index f2bcd7f73c5c..d9c90a34c0d8 100644 --- a/src/libcamera/v4l2_subdevice.cpp +++ b/src/libcamera/v4l2_subdevice.cpp @@ -95,6 +95,15 @@ const std::string V4L2SubdeviceFormat::toString() const * any device left open will be closed, and any resources released. */ +/** + * \enum V4L2Subdevice::Whence + * \brief Specify the type of format for getFormat() and setFormat() operations + * \var V4L2Subdevice::ActiveFormat + * \brief The format operation applies to ACTIVE formats + * \var V4L2Subdevice::TryFormat + * \brief The format operation applies to TRY formats + */ + /** * \brief Create a V4L2 subdevice from a MediaEntity using its device node * path @@ -184,12 +193,15 @@ ImageFormats V4L2Subdevice::formats(unsigned int pad) * \brief Retrieve the image format set on one of the V4L2 subdevice pads * \param[in] pad The 0-indexed pad number the format is to be retrieved from * \param[out] format The image bus format + * \param[in] whence The format to retrieve, ACTIVE or TRY * \return 0 on success or a negative error code otherwise */ -int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format) +int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format, + Whence whence) { struct v4l2_subdev_format subdevFmt = {}; - subdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + subdevFmt.which = whence == ActiveFormat ? V4L2_SUBDEV_FORMAT_ACTIVE + : V4L2_SUBDEV_FORMAT_TRY; subdevFmt.pad = pad; int ret = ioctl(VIDIOC_SUBDEV_G_FMT, &subdevFmt); @@ -211,6 +223,7 @@ int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format) * \brief Set an image format on one of the V4L2 subdevice pads * \param[in] pad The 0-indexed pad number the format is to be applied to * \param[inout] format The image bus format to apply to the subdevice's pad + * \param[in] whence The format to set, ACTIVE or TRY * * Apply the requested image format to the desired media pad and return the * actually applied format parameters, as \ref V4L2Subdevice::getFormat would @@ -218,10 +231,12 @@ int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format) * * \return 0 on success or a negative error code otherwise */ -int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format) +int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format, + Whence whence) { struct v4l2_subdev_format subdevFmt = {}; - subdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + subdevFmt.which = whence == ActiveFormat ? V4L2_SUBDEV_FORMAT_ACTIVE + : V4L2_SUBDEV_FORMAT_TRY; subdevFmt.pad = pad; subdevFmt.format.width = format->size.width; subdevFmt.format.height = format->size.height; From patchwork Fri Mar 13 23:38:52 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 3088 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5023862826 for ; Sat, 14 Mar 2020 00:39:07 +0100 (CET) Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 96F8D143F; Sat, 14 Mar 2020 00:39:06 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1584142747; bh=l85MigF0qRX6ZlqK91ZOjfx5Ybo9V3LC2jZ+5ypO6Fo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=S06GE9lTCKCE4DKvVT6BeoF8rTZ/C7BI3ydjrR2HtIjT04bTNhC47EIxK1fB1ZW4n c8HIAB49tUl5gfdARfJSSRsKUPTu5GuzCDIWbUypxugaP54YzGVZkEjvMD3fBcGMnX ee7c2vXHS68/F5wDZacfKAK+8azWsYXdQZnRdjVY= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Cc: Martijn Braam , Andrey Konovalov , Mickael GUENE , Benjamin GAIGNARD Date: Sat, 14 Mar 2020 01:38:52 +0200 Message-Id: <20200313233856.25202-5-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200313233856.25202-1-laurent.pinchart@ideasonboard.com> References: <20200313233856.25202-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 4/8] libcamera: v4l2_videodevice: Support filtering formats by media bus code 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: , X-List-Received-Date: Fri, 13 Mar 2020 23:39:08 -0000 Add support for the recent V4L2 extension to VIDIOC_ENUM_FMT that allows filtering pixel formats by media bus codes. Signed-off-by: Laurent Pinchart --- src/libcamera/include/v4l2_videodevice.h | 4 ++-- src/libcamera/v4l2_videodevice.cpp | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h index d051c9060f09..72e7a50e6c3d 100644 --- a/src/libcamera/include/v4l2_videodevice.h +++ b/src/libcamera/include/v4l2_videodevice.h @@ -184,7 +184,7 @@ public: int getFormat(V4L2DeviceFormat *format); int setFormat(V4L2DeviceFormat *format); - ImageFormats formats(); + ImageFormats formats(uint32_t code = 0); int setCrop(Rectangle *rect); int setCompose(Rectangle *rect); @@ -220,7 +220,7 @@ private: int getFormatSingleplane(V4L2DeviceFormat *format); int setFormatSingleplane(V4L2DeviceFormat *format); - std::vector enumPixelformats(); + std::vector enumPixelformats(uint32_t code); std::vector enumSizes(unsigned int pixelFormat); int setSelection(unsigned int target, Rectangle *rect); diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index ca0d147f5fc1..ccc5faba3c27 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -839,16 +839,19 @@ int V4L2VideoDevice::setFormatSingleplane(V4L2DeviceFormat *format) /** * \brief Enumerate all pixel formats and frame sizes + * \param[in] code Restrict formats to this media bus code. * * Enumerate all pixel formats and frame sizes supported by the video device. + * If the \a code argument is not zero, only formats compatible with that media + * bus code will be enumerated. * * \return A list of the supported video device formats */ -ImageFormats V4L2VideoDevice::formats() +ImageFormats V4L2VideoDevice::formats(uint32_t code) { ImageFormats formats; - for (unsigned int pixelformat : enumPixelformats()) { + for (unsigned int pixelformat : enumPixelformats(code)) { std::vector sizes = enumSizes(pixelformat); if (sizes.empty()) return {}; @@ -864,7 +867,7 @@ ImageFormats V4L2VideoDevice::formats() return formats; } -std::vector V4L2VideoDevice::enumPixelformats() +std::vector V4L2VideoDevice::enumPixelformats(uint32_t code) { std::vector formats; int ret; @@ -873,11 +876,18 @@ std::vector V4L2VideoDevice::enumPixelformats() struct v4l2_fmtdesc pixelformatEnum = {}; pixelformatEnum.index = index; pixelformatEnum.type = bufferType_; + pixelformatEnum.mbus_code = code; ret = ioctl(VIDIOC_ENUM_FMT, &pixelformatEnum); if (ret) break; + if (code && !(pixelformatEnum.flags & V4L2_FMT_FLAG_MBUS_CODE)) { + LOG(V4L2, Error) + << "Media bus code filtering not supported by the device"; + return {}; + } + formats.push_back(pixelformatEnum.pixelformat); } From patchwork Fri Mar 13 23:38:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 3089 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D0A5262938 for ; Sat, 14 Mar 2020 00:39:07 +0100 (CET) Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 323EC184A; Sat, 14 Mar 2020 00:39:07 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1584142747; bh=LrsspT9ELByFH4FEUA+hZP9TBMIXM+ke8hkA1+XUzkc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=e6nDDcJVV1UIgTxlB0A4G+lZ6R5w7vI2+bKSKG5eXtdwf9on2glM6r5j6nqHtlefu Du/5I5SCd+iuqfIetbSvBHH23hfqnW5U+UK1ROKEcXfe6ILsG8A525Bp8vqwb1UUIp /fNIIyACI+93lXB+hJWpS0qUb2Y2XNjnjMoKA2QQ= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Cc: Martijn Braam , Andrey Konovalov , Mickael GUENE , Benjamin GAIGNARD Date: Sat, 14 Mar 2020 01:38:53 +0200 Message-Id: <20200313233856.25202-6-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200313233856.25202-1-laurent.pinchart@ideasonboard.com> References: <20200313233856.25202-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 5/8] libcamera: v4l2_videodevice: Expose the device capabilities 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: , X-List-Received-Date: Fri, 13 Mar 2020 23:39:08 -0000 Add a caps() function that exposes the V4L2 capabilities for the device. This is useful for generic code that can't hardcode any a priori knowledge of the device, such as in a simple pipeline handler. Signed-off-by: Laurent Pinchart Reviewed-by: Niklas Söderlund --- src/libcamera/include/v4l2_videodevice.h | 2 ++ src/libcamera/v4l2_videodevice.cpp | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h index 72e7a50e6c3d..b2e12608084a 100644 --- a/src/libcamera/include/v4l2_videodevice.h +++ b/src/libcamera/include/v4l2_videodevice.h @@ -182,6 +182,8 @@ public: const char *deviceName() const { return caps_.card(); } const char *busName() const { return caps_.bus_info(); } + const V4L2Capability &caps() const { return caps_; } + int getFormat(V4L2DeviceFormat *format); int setFormat(V4L2DeviceFormat *format); ImageFormats formats(uint32_t code = 0); diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index ccc5faba3c27..86b262ba2436 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -625,6 +625,12 @@ void V4L2VideoDevice::close() * \return The string containing the device location */ +/** + * \fn V4L2VideoDevice::caps() + * \brief Retrieve the device V4L2 capabilities + * \return The device V4L2 capabilities + */ + std::string V4L2VideoDevice::logPrefix() const { return deviceNode() + (V4L2_TYPE_IS_OUTPUT(bufferType_) ? "[out]" : "[cap]"); From patchwork Fri Mar 13 23:38:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 3090 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6E1EC62826 for ; Sat, 14 Mar 2020 00:39:08 +0100 (CET) Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C272F1854; Sat, 14 Mar 2020 00:39:07 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1584142748; bh=ntkBsA9pLZuz2jgxwEgP5mGrgPBqce/PuPPjrfLPvdw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dQn0AqDPrgpuzJibZvGnynjnjmr8CiVh/jEP3MgCn9J+L9lBdCDa98vZcOSTsrEsR GXzen8yg2v0Qy9LIWig+xuMm7stYDO8fjVqKAdnwTX9jfpfx0dBdVaiEu0R0EgQN70 Mbl1vYqtnzl3W3WNXKRImrlmp7l/oljJTXjEDEKA= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Cc: Martijn Braam , Andrey Konovalov , Mickael GUENE , Benjamin GAIGNARD Date: Sat, 14 Mar 2020 01:38:54 +0200 Message-Id: <20200313233856.25202-7-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200313233856.25202-1-laurent.pinchart@ideasonboard.com> References: <20200313233856.25202-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 6/8] libcamera: v4l2_videodevice: Downgrade 4CC conversion errors to warnings 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: , X-List-Received-Date: Fri, 13 Mar 2020 23:39:09 -0000 Failing to convert between V4L2 and DRM 4CCs indicates something is likely wrong, but doesn't necessarily prevent the camera from being usable. It may for instance only limit the number of supported formats. Downgrade the related log messages from LogError to LogWarning. Signed-off-by: Laurent Pinchart --- src/libcamera/v4l2_videodevice.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index 86b262ba2436..f68b67b33293 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -1485,7 +1485,7 @@ PixelFormat V4L2VideoDevice::toPixelFormat(uint32_t v4l2Fourcc) * class. Until we fix the logger, work around it. */ libcamera::_log(__FILE__, __LINE__, _LOG_CATEGORY(V4L2)(), - LogError).stream() + LogWarning).stream() << "Unsupported V4L2 pixel format " << utils::hex(v4l2Fourcc); return 0; @@ -1566,7 +1566,8 @@ uint32_t V4L2VideoDevice::toV4L2Fourcc(PixelFormat pixelFormat, bool multiplanar * \todo We can't use LOG() in a static method of a Loggable * class. Until we fix the logger, work around it. */ - libcamera::_log(__FILE__, __LINE__, _LOG_CATEGORY(V4L2)(), LogError).stream() + libcamera::_log(__FILE__, __LINE__, _LOG_CATEGORY(V4L2)(), + LogWarning).stream() << "Unsupported V4L2 pixel format " << utils::hex(pixelFormat); return 0; From patchwork Fri Mar 13 23:38:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 3091 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5E50962928 for ; Sat, 14 Mar 2020 00:39:09 +0100 (CET) Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 683511900; Sat, 14 Mar 2020 00:39:08 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1584142749; bh=l/COplkD/CSkzA9gCu0JsiviApvV0IrCuUFMZpFmhXc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=TNUw9aO3lpvd+7+mvHjgH+Zx6us8kzpyMTysKJXVwzJihM7a6/54LyBhW7i/8eYbb WKA7kfhVl9fvTDmk2aTWplTu08CVcuv9cSAuEkq61i4Go8EfpoiUKJ6+CiZG96LKe7 T0PLyPqEmMKxKVZu9s2QrEBU3/hbKHRhut1HsskM= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Cc: Martijn Braam , Andrey Konovalov , Mickael GUENE , Benjamin GAIGNARD Date: Sat, 14 Mar 2020 01:38:55 +0200 Message-Id: <20200313233856.25202-8-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200313233856.25202-1-laurent.pinchart@ideasonboard.com> References: <20200313233856.25202-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 7/8] libcamera: v4l2_videodevice: Map V4L2_PIX_FMT_GREY to DRM FourCC 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: , X-List-Received-Date: Fri, 13 Mar 2020 23:39:09 -0000 DRM has a format for 8-bit greyscale data, DRM_FORMAT_R8. Despite the 'R' name, which comes from GL/Vulkan to mean single-channel data, the format maps to greyscale for display. We can thus map it to V4L2_PIX_FMT_GREY. Signed-off-by: Laurent Pinchart --- src/libcamera/v4l2_videodevice.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index f68b67b33293..f89bf2ff781e 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -1473,12 +1473,15 @@ PixelFormat V4L2VideoDevice::toPixelFormat(uint32_t v4l2Fourcc) case V4L2_PIX_FMT_NV21M: return DRM_FORMAT_NV21; + /* Greyscale formats. */ + case V4L2_PIX_FMT_GREY: + return DRM_FORMAT_R8; + /* Compressed formats. */ case V4L2_PIX_FMT_MJPEG: return DRM_FORMAT_MJPEG; /* V4L2 formats not yet supported by DRM. */ - case V4L2_PIX_FMT_GREY: default: /* * \todo We can't use LOG() in a static method of a Loggable @@ -1557,6 +1560,10 @@ uint32_t V4L2VideoDevice::toV4L2Fourcc(PixelFormat pixelFormat, bool multiplanar case DRM_FORMAT_NV21: return V4L2_PIX_FMT_NV21; + /* Greyscale formats. */ + case DRM_FORMAT_R8: + return V4L2_PIX_FMT_GREY; + /* Compressed formats. */ case DRM_FORMAT_MJPEG: return V4L2_PIX_FMT_MJPEG; From patchwork Fri Mar 13 23:38:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 3092 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C2AEA62930 for ; Sat, 14 Mar 2020 00:39:10 +0100 (CET) Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 54E531912; Sat, 14 Mar 2020 00:39:09 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1584142750; bh=gz2Mmziu1XJlRtUFXVy5H3cJZ3fnTCVPsLV5fHO+TrU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WiSpa/f7V4XmzouKjanu2neYZIprrSPCrgNpbxzKdNwJWkJv+CIQ1FgM9xremQ3nf 7cgHduCY2HGglICXD6lt8IqFCXF21Hw8n+/t5SZcIRjTSFgey5ov1L24S6YDsDLu2S ZHbIbSJUResLjbg5JS0wGZ1WZtMtGYLjL5PsbkFs= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Cc: Martijn Braam , Andrey Konovalov , Mickael GUENE , Benjamin GAIGNARD Date: Sat, 14 Mar 2020 01:38:56 +0200 Message-Id: <20200313233856.25202-9-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200313233856.25202-1-laurent.pinchart@ideasonboard.com> References: <20200313233856.25202-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 8/8] libcamera: pipeline: Add a simple pipeline handler 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: , X-List-Received-Date: Fri, 13 Mar 2020 23:39:11 -0000 From: Martijn Braam This new pipeline handler aims at supporting any simple device without requiring any device-specific code. Simple devices are currently defined as a graph made of one or multiple camera sensors and a single video node, with each sensor connected to the video node through a linear pipeline. The simple pipeline handler will automatically parse the media graph, enumerate sensors, build supported stream configurations, and configure the pipeline, without any device-specific knowledge. It doesn't support configuration of any processing in the pipeline at the moment, but may be extended to support simple processing such as format conversion or scaling in the future. The only device-specific information in the pipeline handler is the list of supported drivers, required for device matching. We may be able to remove this in the future by matching with the simple pipeline handler as a last resort option, after all other pipeline handlers have been tried. Signed-off-by: Martijn Braam Signed-off-by: Laurent Pinchart --- src/libcamera/pipeline/meson.build | 1 + src/libcamera/pipeline/simple/meson.build | 3 + src/libcamera/pipeline/simple/simple.cpp | 693 ++++++++++++++++++++++ 3 files changed, 697 insertions(+) create mode 100644 src/libcamera/pipeline/simple/meson.build create mode 100644 src/libcamera/pipeline/simple/simple.cpp diff --git a/src/libcamera/pipeline/meson.build b/src/libcamera/pipeline/meson.build index 0d466225a72e..606ba31a0319 100644 --- a/src/libcamera/pipeline/meson.build +++ b/src/libcamera/pipeline/meson.build @@ -5,3 +5,4 @@ libcamera_sources += files([ subdir('ipu3') subdir('rkisp1') +subdir('simple') diff --git a/src/libcamera/pipeline/simple/meson.build b/src/libcamera/pipeline/simple/meson.build new file mode 100644 index 000000000000..4945a3e173cf --- /dev/null +++ b/src/libcamera/pipeline/simple/meson.build @@ -0,0 +1,3 @@ +libcamera_sources += files([ + 'simple.cpp', +]) diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp new file mode 100644 index 000000000000..a0c4e77906e9 --- /dev/null +++ b/src/libcamera/pipeline/simple/simple.cpp @@ -0,0 +1,693 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Laurent Pinchart + * Copyright (C) 2019, Martijn Braam + * + * simple.cpp - Pipeline handler for simple pipelines + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "camera_sensor.h" +#include "device_enumerator.h" +#include "log.h" +#include "media_device.h" +#include "pipeline_handler.h" +#include "v4l2_subdevice.h" +#include "v4l2_videodevice.h" + +namespace libcamera { + +LOG_DEFINE_CATEGORY(SimplePipeline) + +class SimplePipelineHandler; + +class SimpleCameraData : public CameraData +{ +public: + SimpleCameraData(PipelineHandler *pipe, MediaEntity *sensor, + MediaEntity *video); + + bool isValid() const { return sensor_ != nullptr; } + std::set streams() { return { &stream_ }; } + + int init(); + int setupLinks(); + int setupFormats(V4L2SubdeviceFormat *format, + V4L2Subdevice::Whence whence); + + struct Entity { + MediaEntity *entity; + MediaLink *link; + }; + + struct Configuration { + uint32_t code; + std::vector formats; + Size size; + }; + + Stream stream_; + std::unique_ptr sensor_; + std::list entities_; + + std::vector configs_; + std::map formats_; +}; + +class SimpleCameraConfiguration : public CameraConfiguration +{ +public: + SimpleCameraConfiguration(Camera *camera, SimpleCameraData *data); + + Status validate() override; + + const V4L2SubdeviceFormat &sensorFormat() { return sensorFormat_; } + +private: + /* + * The SimpleCameraData instance is guaranteed to be valid as long as + * the corresponding Camera instance is valid. In order to borrow a + * reference to the camera data, store a new reference to the camera. + */ + std::shared_ptr camera_; + const SimpleCameraData *data_; + + V4L2SubdeviceFormat sensorFormat_; +}; + +class SimplePipelineHandler : public PipelineHandler +{ +public: + SimplePipelineHandler(CameraManager *manager); + ~SimplePipelineHandler(); + + CameraConfiguration *generateConfiguration(Camera *camera, + const StreamRoles &roles) override; + int configure(Camera *camera, CameraConfiguration *config) override; + + int exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) override; + int importFrameBuffers(Camera *camera, Stream *stream) override; + void freeFrameBuffers(Camera *camera, Stream *stream) override; + + int start(Camera *camera) override; + void stop(Camera *camera) override; + + bool match(DeviceEnumerator *enumerator) override; + + V4L2VideoDevice *video() { return video_; } + V4L2Subdevice *subdev(const MediaEntity *entity); + +protected: + int queueRequestDevice(Camera *camera, Request *request) override; + +private: + SimpleCameraData *cameraData(const Camera *camera) + { + return static_cast( + PipelineHandler::cameraData(camera)); + } + + int initLinks(); + + int createCamera(MediaEntity *sensor); + + void bufferReady(FrameBuffer *buffer); + + MediaDevice *media_; + V4L2VideoDevice *video_; + std::map subdevs_; + + Camera *activeCamera_; +}; + +/* ----------------------------------------------------------------------------- + * Camera Data + */ +SimpleCameraData::SimpleCameraData(PipelineHandler *pipe, MediaEntity *sensor, + MediaEntity *video) + : CameraData(pipe) +{ + int ret; + + /* + * Walk the pipeline towards the video node and store all entities + * along the way. + */ + MediaEntity *source = sensor; + + while (source) { + /* If we have reached the video node, we're done. */ + if (source == video) + break; + + /* Use the first output pad that has links. */ + MediaPad *sourcePad = nullptr; + for (MediaPad *pad : source->pads()) { + if ((pad->flags() & MEDIA_PAD_FL_SOURCE) && + !pad->links().empty()) { + sourcePad = pad; + break; + } + } + + if (!sourcePad) + return; + + /* Use the first link that isn't immutable and disabled. */ + MediaLink *sourceLink = nullptr; + for (MediaLink *link : sourcePad->links()) { + if ((link->flags() & MEDIA_LNK_FL_ENABLED) || + !(link->flags() & MEDIA_LNK_FL_IMMUTABLE)) { + sourceLink = link; + break; + } + } + + if (!sourceLink) + return; + + entities_.push_back({ source, sourceLink }); + + source = sourceLink->sink()->entity(); + + /* Avoid infinite loops. */ + auto iter = std::find_if(entities_.begin(), entities_.end(), + [&](const Entity &entity) { + return entity.entity == source; + }); + if (iter != entities_.end()) { + LOG(SimplePipeline, Info) << "Loop detected in pipeline"; + return; + } + } + + /* We have a valid pipeline, create the camera sensor. */ + sensor_ = std::make_unique(sensor); + ret = sensor_->init(); + if (ret) { + sensor_.reset(); + return; + } +} + +int SimpleCameraData::init() +{ + SimplePipelineHandler *pipe = static_cast(pipe_); + V4L2VideoDevice *video = pipe->video(); + int ret; + + /* + * Enumerate the possible pipeline configurations. For each media bus + * format supported by the sensor, propagate the formats through the + * pipeline, and enumerate the corresponding possible V4L2 pixel + * formats on the video node. + */ + configs_.reserve(sensor_->mbusCodes().size()); + + for (unsigned int code : sensor_->mbusCodes()) { + V4L2SubdeviceFormat format{ code, sensor_->resolution() }; + + /* + * Setup links first as some subdev drivers take active links + * into account to propaget TRY formats. So is life :-( + */ + ret = setupLinks(); + if (ret < 0) + return ret; + + ret = setupFormats(&format, V4L2Subdevice::TryFormat); + if (ret < 0) + return ret; + + ImageFormats formats = video->formats(format.mbus_code); + + Configuration config; + config.code = code; + config.formats = formats.formats(); + config.size = format.size; + + LOG(SimplePipeline, Debug) + << "Adding configuration for " << config.size.toString() + << " in pixel formats [ " + << utils::join(config.formats, ", ", + [](unsigned int f) { return std::to_string(f); }) + << " ]"; + + configs_.emplace_back(std::move(config)); + + /* + * Store the configuration in the formats_ map, mapping + * PixelFormat to configuration. Any previously stored value is + * overwritten, as the pipeline handler currently doesn't care + * about how a particular PixelFormat is achieved. + */ + for (unsigned int format : formats.formats()) { + PixelFormat pixelFormat = video->toPixelFormat(format); + if (pixelFormat) + formats_[pixelFormat] = &configs_.back(); + } + } + + if (formats_.empty()) { + LOG(SimplePipeline, Error) << "No valid configuration found"; + return -EINVAL; + } + + return 0; +} + +int SimpleCameraData::setupLinks() +{ + int ret; + + /* + * Configure all links along the pipeline. Some entities may not allow + * multiple sink links to be enabled together, even on different sink + * pads. We must thus start by disabling all sink links (but the one we + * want to enable) before enabling the pipeline link. + */ + for (SimpleCameraData::Entity &e : entities_) { + for (MediaPad *pad : e.link->sink()->entity()->pads()) { + for (MediaLink *link : pad->links()) { + if (link == e.link) + continue; + + if ((link->flags() & MEDIA_LNK_FL_ENABLED) && + !(link->flags() & MEDIA_LNK_FL_IMMUTABLE)) { + ret = link->setEnabled(false); + if (ret < 0) + return ret; + } + } + } + + if (!(e.link->flags() & MEDIA_LNK_FL_ENABLED)) { + ret = e.link->setEnabled(true); + if (ret < 0) + return ret; + } + } + + return 0; +} + +int SimpleCameraData::setupFormats(V4L2SubdeviceFormat *format, + V4L2Subdevice::Whence whence) +{ + SimplePipelineHandler *pipe = static_cast(pipe_); + int ret; + + /* + * Configure the format on the sensor output and propagate it through + * the pipeline. + */ + ret = sensor_->setFormat(format); + if (ret < 0) + return ret; + + for (const Entity &e : entities_) { + MediaLink *link = e.link; + MediaPad *source = link->source(); + MediaPad *sink = link->sink(); + + if (source->entity() != sensor_->entity()) { + V4L2Subdevice *subdev = pipe->subdev(source->entity()); + subdev->getFormat(source->index(), format, whence); + } + + if (sink->entity()->function() != MEDIA_ENT_F_IO_V4L) { + V4L2Subdevice *subdev = pipe->subdev(sink->entity()); + subdev->setFormat(sink->index(), format, whence); + } + + LOG(SimplePipeline, Debug) + << "Link '" << source->entity()->name() + << "':" << source->index() + << " -> '" << sink->entity()->name() + << "':" << sink->index() + << " configured with format " << format->toString(); + } + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Camera Configuration + */ + +SimpleCameraConfiguration::SimpleCameraConfiguration(Camera *camera, + SimpleCameraData *data) + : CameraConfiguration(), camera_(camera->shared_from_this()), + data_(data) +{ +} + +CameraConfiguration::Status SimpleCameraConfiguration::validate() +{ + Status status = Valid; + + if (config_.empty()) + return Invalid; + + /* Cap the number of entries to the available streams. */ + if (config_.size() > 1) { + config_.resize(1); + status = Adjusted; + } + + StreamConfiguration &cfg = config_[0]; + + /* Adjust the pixel format. */ + auto it = data_->formats_.find(cfg.pixelFormat); + if (it == data_->formats_.end()) + it = data_->formats_.begin(); + + PixelFormat pixelFormat = it->first; + const SimpleCameraData::Configuration *pipeConfig = it->second; + + if (cfg.pixelFormat != pixelFormat) { + LOG(SimplePipeline, Debug) << "Adjusting pixel format"; + cfg.pixelFormat = pixelFormat; + status = Adjusted; + } + + if (cfg.size != pipeConfig->size) { + LOG(SimplePipeline, Debug) + << "Adjusting size from " << cfg.size.toString() + << " to " << pipeConfig->size.toString(); + cfg.size = pipeConfig->size; + status = Adjusted; + } + + cfg.bufferCount = 3; + + return status; +} + +/* ----------------------------------------------------------------------------- + * Pipeline Handler + */ + +SimplePipelineHandler::SimplePipelineHandler(CameraManager *manager) + : PipelineHandler(manager), video_(nullptr) +{ +} + +SimplePipelineHandler::~SimplePipelineHandler() +{ + delete video_; +} + +CameraConfiguration *SimplePipelineHandler::generateConfiguration(Camera *camera, + const StreamRoles &roles) +{ + SimpleCameraData *data = cameraData(camera); + CameraConfiguration *config = + new SimpleCameraConfiguration(camera, data); + + if (roles.empty()) + return config; + + const auto it = data->formats_.begin(); + const PixelFormat &pixelFormat = it->first; + const SimpleCameraData::Configuration *pipeConfig = it->second; + + StreamConfiguration cfg{}; + cfg.pixelFormat = pixelFormat; + cfg.size = pipeConfig->size; + + config->addConfiguration(cfg); + + config->validate(); + + return config; +} + +int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c) +{ + SimpleCameraConfiguration *config = + static_cast(c); + SimpleCameraData *data = cameraData(camera); + StreamConfiguration &cfg = config->at(0); + int ret; + + /* + * Configure links on the pipeline and propagate formats from the + * sensor to the video node. + */ + ret = data->setupLinks(); + if (ret < 0) + return ret; + + const SimpleCameraData::Configuration *pipeConfig = + data->formats_[cfg.pixelFormat]; + + V4L2SubdeviceFormat format{ pipeConfig->code, data->sensor_->resolution() }; + + ret = data->setupFormats(&format, V4L2Subdevice::ActiveFormat); + if (ret < 0) + return ret; + + /* Configure the video node. */ + uint32_t fourcc = video_->toV4L2Fourcc(cfg.pixelFormat); + + V4L2DeviceFormat outputFormat = {}; + outputFormat.fourcc = fourcc; + outputFormat.size = cfg.size; + + ret = video_->setFormat(&outputFormat); + if (ret) + return ret; + + if (outputFormat.size != cfg.size || outputFormat.fourcc != fourcc) { + LOG(SimplePipeline, Error) + << "Unable to configure capture in " << cfg.toString(); + return -EINVAL; + } + + cfg.setStream(&data->stream_); + + return 0; +} + +int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) +{ + unsigned int count = stream->configuration().bufferCount; + + return video_->exportBuffers(count, buffers); +} + +int SimplePipelineHandler::importFrameBuffers(Camera *camera, Stream *stream) +{ + unsigned int count = stream->configuration().bufferCount; + + return video_->importBuffers(count); +} + +void SimplePipelineHandler::freeFrameBuffers(Camera *camera, Stream *stream) +{ + video_->releaseBuffers(); +} + +int SimplePipelineHandler::start(Camera *camera) +{ + int ret = video_->streamOn(); + if (ret < 0) + return 0; + + activeCamera_ = camera; + + return 0; +} + +void SimplePipelineHandler::stop(Camera *camera) +{ + video_->streamOff(); + activeCamera_ = nullptr; +} + +int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request) +{ + SimpleCameraData *data = cameraData(camera); + Stream *stream = &data->stream_; + + FrameBuffer *buffer = request->findBuffer(stream); + if (!buffer) { + LOG(SimplePipeline, Error) + << "Attempt to queue request with invalid stream"; + return -ENOENT; + } + + return video_->queueBuffer(buffer); +} + +/* ----------------------------------------------------------------------------- + * Match and Setup + */ + +bool SimplePipelineHandler::match(DeviceEnumerator *enumerator) +{ + static const char * const drivers[] = { + "imx7-csi", + "sun6i-csi", + }; + + for (const char *driver : drivers) { + DeviceMatch dm(driver); + media_ = acquireMediaDevice(enumerator, dm); + if (media_) + break; + } + + if (!media_) + return false; + + /* + * Locate sensors and video nodes. We only support pipelines with at + * least one sensor and exactly one video captude node. + */ + std::vector sensors; + std::vector videos; + + for (MediaEntity *entity : media_->entities()) { + switch (entity->function()) { + case MEDIA_ENT_F_CAM_SENSOR: + sensors.push_back(entity); + break; + + case MEDIA_ENT_F_IO_V4L: + if (entity->pads().size() == 1 && + (entity->pads()[0]->flags() & MEDIA_PAD_FL_SINK)) + videos.push_back(entity); + break; + + default: + break; + } + } + + if (sensors.empty()) { + LOG(SimplePipeline, Info) << "No sensor found"; + return false; + } + + if (videos.size() != 1) { + LOG(SimplePipeline, Info) + << "Pipeline with " << videos.size() + << " video capture nodes is not supported"; + return false; + } + + /* Locate and open the capture video node. */ + video_ = new V4L2VideoDevice(videos[0]); + if (video_->open() < 0) + return false; + + if (video_->caps().isMultiplanar()) { + LOG(SimplePipeline, Info) + << "V4L2 multiplanar devices are not supported"; + return false; + } + + video_->bufferReady.connect(this, &SimplePipelineHandler::bufferReady); + + /* + * Create one camera data instance for each sensor and gather all + * entities in all pipelines. + */ + std::vector> pipelines; + std::set entities; + + pipelines.reserve(sensors.size()); + + for (MediaEntity *sensor : sensors) { + std::unique_ptr data = + std::make_unique(this, sensor, + videos[0]); + if (!data->isValid()) { + LOG(SimplePipeline, Info) + << "No valid pipeline for sensor '" + << sensor->name() << "', skipping"; + continue; + } + + for (SimpleCameraData::Entity &entity : data->entities_) + entities.insert(entity.entity); + + pipelines.push_back(std::move(data)); + } + + if (entities.empty()) + return false; + + /* Create and open V4L2Subdev instances for all the entities. */ + for (MediaEntity *entity : entities) { + auto elem = subdevs_.emplace(std::piecewise_construct, + std::forward_as_tuple(entity), + std::forward_as_tuple(entity)); + V4L2Subdevice *subdev = &elem.first->second; + int ret = subdev->open(); + if (ret < 0) { + LOG(SimplePipeline, Error) + << "Failed to open " << subdev->deviceNode() + << ": " << strerror(-ret); + return false; + } + } + + /* Initialize each pipeline and register a corresponding camera. */ + for (std::unique_ptr &data : pipelines) { + int ret = data->init(); + if (ret < 0) + continue; + + std::shared_ptr camera = + Camera::create(this, data->sensor_->entity()->name(), + data->streams()); + registerCamera(std::move(camera), std::move(data)); + } + + return true; +} + +V4L2Subdevice *SimplePipelineHandler::subdev(const MediaEntity *entity) +{ + auto iter = subdevs_.find(entity); + if (iter == subdevs_.end()) + return nullptr; + + return &iter->second; +} + +/* ----------------------------------------------------------------------------- + * Buffer Handling + */ + +void SimplePipelineHandler::bufferReady(FrameBuffer *buffer) +{ + ASSERT(activeCamera_); + Request *request = buffer->request(); + completeBuffer(activeCamera_, request, buffer); + completeRequest(activeCamera_, request); +} + +REGISTER_PIPELINE_HANDLER(SimplePipelineHandler); + +} /* namespace libcamera */