From patchwork Wed Feb 6 06:08:03 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 527 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 380F761026 for ; Wed, 6 Feb 2019 07:08:26 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D278E2D8 for ; Wed, 6 Feb 2019 07:08:25 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433305; bh=O3mcBuKcrm1QhL6KMm2kJOMjJjiCP6DKOSN+AZnzdIs=; h=From:To:Subject:Date:In-Reply-To:References:From; b=c4mBXQoVCjcVM1UynQzI+UhqlSo41zuj45Dx/WMtqADAeJmrBUP1qzlwl9TfnaEVi 5rz/N1HLbO0B/YI8tSaTnZnnnOkpFSqQuAi309APUBQp+mskKMEu+mfQDWixbYlJDb PcsRCvBLJFoJ91Tjm1U0Sd0EP331byM6Ysz3f5ng= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:03 +0200 Message-Id: <20190206060818.13907-13-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190206060818.13907-1-laurent.pinchart@ideasonboard.com> References: <20190206060818.13907-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 12/27] libcamera: Add V4L2Subdevice X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 06 Feb 2019 06:08:28 -0000 From: Jacopo Mondi Add V4L2Subdevice class that provides an interface to interact with V4L2 defined sub-devices. Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart --- src/libcamera/include/v4l2_subdevice.h | 51 +++++ src/libcamera/meson.build | 1 + src/libcamera/v4l2_subdevice.cpp | 265 +++++++++++++++++++++++++ 3 files changed, 317 insertions(+) create mode 100644 src/libcamera/include/v4l2_subdevice.h create mode 100644 src/libcamera/v4l2_subdevice.cpp diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h new file mode 100644 index 000000000000..8fd666078985 --- /dev/null +++ b/src/libcamera/include/v4l2_subdevice.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_subdevice.h - V4L2 Subdevice + */ +#ifndef __LIBCAMERA_V4L2_SUBDEVICE_H__ +#define __LIBCAMERA_V4L2_SUBDEVICE_H__ + +#include + +namespace libcamera { + +struct Rectangle; + +struct V4L2SubdeviceFormat { + uint32_t mbus_code; + uint32_t width; + uint32_t height; +}; + +class V4L2Subdevice +{ +public: + explicit V4L2Subdevice(const MediaEntity *entity); + V4L2Subdevice(const V4L2Subdevice &) = delete; + V4L2Subdevice &operator=(const V4L2Subdevice &) = delete; + + int open(); + bool isOpen() const; + void close(); + + std::string deviceNode() const { return deviceNode_; } + + int setCrop(unsigned int pad, Rectangle *rect); + int setCompose(unsigned int pad, Rectangle *rect); + + int getFormat(unsigned int pad, V4L2SubdeviceFormat *format); + int setFormat(unsigned int pad, V4L2SubdeviceFormat *format); + +private: + int setSelection(unsigned int pad, unsigned int target, + Rectangle *rect); + + std::string deviceNode_; + int fd_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_V4L2_SUBDEVICE_H__ */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 8b33c4b25c30..ac991dc59d18 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -15,6 +15,7 @@ libcamera_sources = files([ 'stream.cpp', 'timer.cpp', 'v4l2_device.cpp', + 'v4l2_subdevice.cpp', ]) libcamera_headers = files([ diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp new file mode 100644 index 000000000000..1d5b11115dbb --- /dev/null +++ b/src/libcamera/v4l2_subdevice.cpp @@ -0,0 +1,265 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_subdevice.cpp - V4L2 Subdevice + */ + +#include +#include +#include +#include + +#include + +#include "geometry.h" +#include "log.h" +#include "media_object.h" +#include "v4l2_subdevice.h" + +/** + * \file v4l2_subdevice.h + * \brief V4L2 Subdevice API + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(V4L2Subdev) + +/** + * \struct V4L2SubdeviceFormat + * \brief The V4L2 sub-device image format and sizes + * + * This structure describes the format of images when transported between + * separate components connected through a physical bus, such as image sensor + * and image receiver or between components part of the same System-on-Chip that + * realize an image transformation pipeline. + * + * The format of images when transported on physical interconnections is known + * as the "media bus format", and it is identified by a resolution and a pixel + * format identification code, known as the "media bus code", not to be confused + * with the fourcc code that identify the format of images when stored in memory + * (see V4L2Device::V4L2DeviceFormat). + * + * Media Bus formats supported by the V4L2 APIs are described in Section + * 4.15.3.4.1 of the "Part I - Video for Linux API" chapter of the "Linux Media + * Infrastructure userspace API", part of the Linux kernel documentation. + * + * Image media bus formats are properties of the subdev pads. When images are + * transported between two media pads identified by a 0-indexed number, the + * image bus format configured on the two pads should match (according to the + * underlying driver format matching criteria) in order to prepare for a + * successful streaming operation. For a more detailed description of the image + * format negotiation process when performed between V4L2 subdevices, refer to + * Section 4.15.3.1 of the above mentioned Linux kernel documentation section. + */ + +/** + * \var V4L2SubdeviceFormat::width + * \brief The image width in pixels + */ + +/** + * \var V4L2SubdeviceFormat::height + * \brief The image height in pixels + */ + +/** + * \var V4L2SubdeviceFormat::mbus_code + * \brief The image format bus code + */ + +/** + * \class V4L2Subdevice + * \brief A V4L2 subdevice as exposed by the Linux kernel + * + * The V4L2Subdevice class provides an API to the "Sub-device interface" as + * described in section 4.15 of the "Linux Media Infrastructure userspace API" + * chapter of the Linux Kernel documentation. + * + * A V4L2Subdevice is constructed from a MediaEntity instance, using the system + * path of the entity's device node. No API call other than open(), isOpen() + * and close() shall be called on an unopened device instance. Upon destruction + * any device left open will be closed, and any resources released. + */ + +/** + * \brief Create a V4L2 subdevice from a MediaEntity using its device node + * path + */ +V4L2Subdevice::V4L2Subdevice(const MediaEntity *entity) + : deviceNode_(entity->deviceNode()), fd_(-1) +{ +} + +/** + * \brief Open a V4L2 subdevice + * + * \return 0 on success, a negative error code otherwise + */ +int V4L2Subdevice::open() +{ + int ret; + + if (isOpen()) { + LOG(V4L2Subdev, Error) << "Device already open"; + return -EBUSY; + } + + ret = ::open(deviceNode_.c_str(), O_RDWR); + if (ret < 0) { + ret = -errno; + LOG(V4L2Subdev, Error) + << "Failed to open V4L2 subdevice '" << deviceNode_ + << "': " << strerror(-ret); + return ret; + } + fd_ = ret; + + return 0; +} + +/** + * \brief Check if the subdevice is open + * \return True if the subdevice is open, false otherwise + */ +bool V4L2Subdevice::isOpen() const +{ + return fd_ != -1; +} + +/** + * \brief Close the subdevice, releasing any resources acquired by open() + */ +void V4L2Subdevice::close() +{ + if (!isOpen()) + return; + + ::close(fd_); + fd_ = -1; +} + +/** + * \fn V4L2Subdevice::deviceNode() + * \brief Retrieve the path of the device node associated with the subdevice + * + * \return The subdevice's device node system path + */ + +/** + * \brief Set a crop rectangle on one of the V4L2 subdevice pads + * \param[in] pad The 0-indexed pad number the rectangle is to be applied to + * \param[inout] rect The rectangle describing crop target area + * + * \return 0 on success, or a negative error code otherwise + */ +int V4L2Subdevice::setCrop(unsigned int pad, Rectangle *rect) +{ + return setSelection(pad, V4L2_SEL_TGT_CROP, rect); +} + +/** + * \brief Set a compose rectangle on one of the V4L2 subdevice pads + * \param[in] pad The 0-indexed pad number the rectangle is to be applied to + * \param[inout] rect The rectangle describing the compose target area + * + * \return 0 on success, or a negative error code otherwise + */ +int V4L2Subdevice::setCompose(unsigned int pad, Rectangle *rect) +{ + return setSelection(pad, V4L2_SEL_TGT_COMPOSE, rect); +} + +/** + * \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 + * + * \return 0 for success, a negative error code otherwise + */ +int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format) +{ + struct v4l2_subdev_format subdevFmt = {}; + subdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + subdevFmt.pad = pad; + + int ret = ioctl(fd_, VIDIOC_SUBDEV_G_FMT, &subdevFmt); + if (ret) { + ret = -errno; + LOG(V4L2Subdev, Error) + << "Unable to get format on pad " << pad + << " of " << deviceNode_ << ": " << strerror(-ret); + return ret; + } + + format->width = subdevFmt.format.width; + format->height = subdevFmt.format.height; + format->mbus_code = subdevFmt.format.code; + + return 0; +} + +/** + * \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 + * + * Apply the requested image format to the desired media pad and return the + * actually applied format parameters, as \ref V4L2Subdevice::getFormat would + * do. + * + * \return 0 for success, a negative error code otherwise + */ +int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format) +{ + struct v4l2_subdev_format subdevFmt = {}; + subdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + subdevFmt.pad = pad; + subdevFmt.format.width = format->width; + subdevFmt.format.height = format->height; + subdevFmt.format.code = format->mbus_code; + + int ret = ioctl(fd_, VIDIOC_SUBDEV_S_FMT, &subdevFmt); + if (ret) { + ret = -errno; + LOG(Error) << "Unable to set format: " << strerror(-ret); + return ret; + } + + format->width = subdevFmt.format.width; + format->height = subdevFmt.format.height; + format->mbus_code = subdevFmt.format.code; + + return 0; +} + +int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target, + Rectangle *rect) +{ + struct v4l2_subdev_selection sel = {}; + + sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sel.pad = pad; + sel.target = target; + sel.flags = 0; + + sel.r.left = rect->y; + sel.r.top = rect->x; + sel.r.width = rect->w; + sel.r.height = rect->h; + + int ret = ioctl(fd_, VIDIOC_SUBDEV_S_SELECTION, sel); + if (ret < 0) { + ret = -errno; + LOG(V4L2Subdev, Error) + << "Unable to set rectangle " << target << " on pad " + << pad << " of " << deviceNode_ << ": " + << strerror(-ret); + return ret; + } + + return 0; +} + +} /* namespace libcamera */