From patchwork Tue May 19 03:25:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 3816 X-Patchwork-Delegate: laurent.pinchart@ideasonboard.com 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 61A2B60E3A for ; Tue, 19 May 2020 05:25:20 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="BVKl8yYR"; dkim-atps=neutral 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 CAFF49CD for ; Tue, 19 May 2020 05:25:19 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1589858720; bh=pVnkRRDLRmwusKRSntd0NNVubZZda3Fh7S1LinbKcGU=; h=From:To:Subject:Date:In-Reply-To:References:From; b=BVKl8yYRdIReLxK4/DYRYpwG8zvQCfWwen4ip6UXjuzgJTAZzyHj3vpzlM89pdVCx oCnyileKlvvCR7RjtTZwsIUy5dcEX5HHTHXURs1FyTIPmLncUJZkC6UT9Xkbzcxt5a 7FfixeA1sPQVbmvz06MKf1EciAg+YGV7TrlSre8o= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Tue, 19 May 2020 06:25:02 +0300 Message-Id: <20200519032505.17307-6-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200519032505.17307-1-laurent.pinchart@ideasonboard.com> References: <20200519032505.17307-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 5/8] cam: Add DRM helper classes 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: Tue, 19 May 2020 03:25:21 -0000 To prepare for viewfinder operation through the DRM/KMS API, add a set of helper classes that encapsulate the libdrm functions. Signed-off-by: Laurent Pinchart --- src/cam/drm.cpp | 661 ++++++++++++++++++++++++++++++++++++++++++++ src/cam/drm.h | 334 ++++++++++++++++++++++ src/cam/meson.build | 14 +- 3 files changed, 1008 insertions(+), 1 deletion(-) create mode 100644 src/cam/drm.cpp create mode 100644 src/cam/drm.h diff --git a/src/cam/drm.cpp b/src/cam/drm.cpp new file mode 100644 index 000000000000..c0677b1337d7 --- /dev/null +++ b/src/cam/drm.cpp @@ -0,0 +1,661 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Ideas on Board Oy + * + * drm.cpp - DRM/KMS Helpers + */ + +/* Include our own copy of drm_mode.h first. */ +#include + +#include "drm.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace DRM { + +Object::Object(Device *dev, uint32_t id, Type type) + : id_(id), dev_(dev), type_(type) +{ + /* Retrieve properties from the objects that support them. */ + if (type != TypeConnector && type != TypeCrtc && + type != TypeEncoder && type != TypePlane) + return; + + /* + * We can't distinguish between failures due to the object having no + * property and failures due to other conditions. Assume we use the API + * correctly and consider the object has no property. + */ + drmModeObjectProperties *properties = drmModeObjectGetProperties(dev->fd(), id, type); + if (!properties) + return; + + properties_.reserve(properties->count_props); + for (uint32_t i = 0; i < properties->count_props; ++i) + properties_.emplace_back(properties->props[i], + properties->prop_values[i]); + + drmModeFreeObjectProperties(properties); +} + +Object::~Object() +{ +} + +const Property *Object::property(const std::string &name) const +{ + for (const PropertyValue &pv : properties_) { + const Property *property = static_cast(dev_->object(pv.id())); + if (property && property->name() == name) + return property; + } + + return nullptr; +} + +const PropertyValue *Object::propertyValue(const std::string &name) const +{ + for (const PropertyValue &pv : properties_) { + const Property *property = static_cast(dev_->object(pv.id())); + if (property && property->name() == name) + return &pv; + } + + return nullptr; +} + +Property::Property(Device *dev, drmModePropertyRes *property) + : Object(dev, property->prop_id, TypeProperty), + name_(property->name), flags_(property->flags), + values_(property->values, property->values + property->count_values), + blobs_(property->blob_ids, property->blob_ids + property->count_blobs) +{ + if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) + type_ = TypeRange; + else if (drm_property_type_is(property, DRM_MODE_PROP_ENUM)) + type_ = TypeEnum; + else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) + type_ = TypeBlob; + else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) + type_ = TypeBitmask; + else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) + type_ = TypeObject; + else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) + type_ = TypeSignedRange; + else + type_ = TypeUnknown; + + for (int i = 0; i < property->count_enums; ++i) + enums_[property->enums[i].value] = property->enums[i].name; +} + +Blob::Blob(Device *dev, const libcamera::Span &data) + : Object(dev, 0, Object::TypeBlob) +{ + drmModeCreatePropertyBlob(dev->fd(), data.data(), data.size(), &id_); +} + +Blob::~Blob() +{ + if (isValid()) + drmModeDestroyPropertyBlob(device()->fd(), id()); +} + +Mode::Mode(const drmModeModeInfo &mode) + : drmModeModeInfo(mode) +{ +} + +std::unique_ptr Mode::toBlob(Device *dev) const +{ + libcamera::Span data{ reinterpret_cast(this), + sizeof(*this) }; + return std::make_unique(dev, data); +} + +Crtc::Crtc(Device *dev, const drmModeCrtc *crtc, unsigned int index) + : Object(dev, crtc->crtc_id, Object::TypeCrtc), index_(index) +{ +} + +Encoder::Encoder(Device *dev, const drmModeEncoder *encoder) + : Object(dev, encoder->encoder_id, Object::TypeEncoder), + type_(encoder->encoder_type) +{ + const std::list &crtcs = dev->crtcs(); + possibleCrtcs_.reserve(crtcs.size()); + + for (const Crtc &crtc : crtcs) { + if (encoder->possible_crtcs & (1 << crtc.index())) + possibleCrtcs_.push_back(&crtc); + } + + possibleCrtcs_.shrink_to_fit(); +} + +namespace { + +const std::map connectorTypeNames{ + { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, + { DRM_MODE_CONNECTOR_VGA, "VGA" }, + { DRM_MODE_CONNECTOR_DVII, "DVI-I" }, + { DRM_MODE_CONNECTOR_DVID, "DVI-D" }, + { DRM_MODE_CONNECTOR_DVIA, "DVI-A" }, + { DRM_MODE_CONNECTOR_Composite, "Composite" }, + { DRM_MODE_CONNECTOR_SVIDEO, "S-Video" }, + { DRM_MODE_CONNECTOR_LVDS, "LVDS" }, + { DRM_MODE_CONNECTOR_Component, "Component" }, + { DRM_MODE_CONNECTOR_9PinDIN, "9-Pin-DIN" }, + { DRM_MODE_CONNECTOR_DisplayPort, "DP" }, + { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" }, + { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" }, + { DRM_MODE_CONNECTOR_TV, "TV" }, + { DRM_MODE_CONNECTOR_eDP, "eDP" }, + { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" }, + { DRM_MODE_CONNECTOR_DSI, "DSI" }, + { DRM_MODE_CONNECTOR_DPI, "DPI" }, + { DRM_MODE_CONNECTOR_WRITEBACK, "writeback" }, +}; + +} /* namespace */ + +Connector::Connector(Device *dev, const drmModeConnector *connector) + : Object(dev, connector->connector_id, Object::TypeConnector), + type_(connector->connector_type) +{ + auto typeName = connectorTypeNames.find(connector->connector_type); + if (typeName == connectorTypeNames.end()) { + std::cerr + << "Invalid connector type " + << connector->connector_type << std::endl; + typeName = connectorTypeNames.find(DRM_MODE_CONNECTOR_Unknown); + } + + name_ = std::string(typeName->second) + "-" + + std::to_string(connector->connector_type_id); + + switch (connector->connection) { + case DRM_MODE_CONNECTED: + status_ = Status::Connected; + break; + + case DRM_MODE_DISCONNECTED: + status_ = Status::Disconnected; + break; + + case DRM_MODE_UNKNOWNCONNECTION: + default: + status_ = Status::Unknown; + break; + } + + const std::list &encoders = dev->encoders(); + + encoders_.reserve(connector->count_encoders); + + for (int i = 0; i < connector->count_encoders; ++i) { + uint32_t encoderId = connector->encoders[i]; + auto encoder = std::find_if(encoders.begin(), encoders.end(), + [=](const Encoder &encoder) { + return encoder.id() == encoderId; + }); + if (encoder == encoders.end()) { + std::cerr + << "Encoder " << encoderId << " not found" + << std::endl; + continue; + } + + encoders_.push_back(&*encoder); + } + + encoders_.shrink_to_fit(); + + modes_ = { connector->modes, connector->modes + connector->count_modes }; +} + +Plane::Plane(Device *dev, const drmModePlane *plane) + : Object(dev, plane->plane_id, Object::TypePlane), + possibleCrtcsMask_(plane->possible_crtcs) +{ + formats_ = { plane->formats, plane->formats + plane->count_formats }; + + const std::list &crtcs = dev->crtcs(); + possibleCrtcs_.reserve(crtcs.size()); + + for (const Crtc &crtc : crtcs) { + if (plane->possible_crtcs & (1 << crtc.index())) + possibleCrtcs_.push_back(&crtc); + } + + possibleCrtcs_.shrink_to_fit(); +} + +bool Plane::supportsFormat(const libcamera::PixelFormat &format) const +{ + return std::find(formats_.begin(), formats_.end(), format.fourcc()) + != formats_.end(); +} + +int Plane::setup() +{ + const PropertyValue *pv = propertyValue("type"); + if (!pv) + return -EINVAL; + + switch (pv->value()) { + case DRM_PLANE_TYPE_OVERLAY: + type_ = TypeOverlay; + break; + + case DRM_PLANE_TYPE_PRIMARY: + type_ = TypePrimary; + break; + + case DRM_PLANE_TYPE_CURSOR: + type_ = TypeCursor; + break; + + default: + return -EINVAL; + } + + return 0; +} + +FrameBuffer::FrameBuffer(Device *dev) + : Object(dev, 0, Object::TypeFb) +{ +} + +FrameBuffer::~FrameBuffer() +{ + for (FrameBuffer::Plane &plane : planes_) { + struct drm_gem_close gem_close = { + .handle = plane.handle, + .pad = 0, + }; + int ret; + + do { + ret = ioctl(device()->fd(), DRM_IOCTL_GEM_CLOSE, &gem_close); + } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); + + if (ret == -1) { + ret = -errno; + std::cerr + << "Failed to close GEM object: " + << strerror(-ret) << std::endl; + } + } + + drmModeRmFB(device()->fd(), id()); +} + +AtomicRequest::AtomicRequest(Device *dev) + : dev_(dev), valid_(true) +{ + request_ = drmModeAtomicAlloc(); + if (!request_) + valid_ = false; +} + +AtomicRequest::~AtomicRequest() +{ + if (request_) + drmModeAtomicFree(request_); +} + +int AtomicRequest::addProperty(const Object *object, const std::string &property, + uint64_t value) +{ + if (!valid_) + return -EINVAL; + + const Property *prop = object->property(property); + if (!prop) { + valid_ = false; + return -EINVAL; + } + + return addProperty(object->id(), prop->id(), value); +} + +int AtomicRequest::addProperty(const Object *object, const std::string &property, + std::unique_ptr blob) +{ + if (!valid_) + return -EINVAL; + + const Property *prop = object->property(property); + if (!prop) { + valid_ = false; + return -EINVAL; + } + + int ret = addProperty(object->id(), prop->id(), blob->id()); + if (ret < 0) + return ret; + + blobs_.emplace_back(std::move(blob)); + + return 0; +} + +int AtomicRequest::addProperty(uint32_t object, uint32_t property, uint64_t value) +{ + int ret = drmModeAtomicAddProperty(request_, object, property, value); + if (ret < 0) { + valid_ = false; + return ret; + } + + return 0; +} + +int AtomicRequest::commit(unsigned int flags) +{ + if (!valid_) + return -EINVAL; + + uint32_t drmFlags = 0; + if (flags & FlagAllowModeset) + drmFlags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + if (flags & FlagAsync) + drmFlags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK; + + return drmModeAtomicCommit(dev_->fd(), request_, drmFlags, this); +} + +Device::Device() + : fd_(-1) +{ +} + +Device::~Device() +{ + if (fd_ != -1) + drmClose(fd_); +} + +int Device::init() +{ + constexpr size_t NODE_NAME_MAX = sizeof("/dev/dri/card255"); + char name[NODE_NAME_MAX]; + int ret; + + /* + * Open the first DRM/KMS device. The libdrm drmOpen*() functions + * require either a module name or a bus ID, which we don't have, so + * bypass them. The automatic module loading and device node creation + * from drmOpen() is of no practical use as any modern system will + * handle that through udev or an equivalent component. + */ + snprintf(name, sizeof(name), "/dev/dri/card%u", 0); + fd_ = open(name, O_RDWR | O_CLOEXEC); + if (fd_ < 0) { + ret = -errno; + std::cerr + << "Failed to open DRM/KMS device " << name << ": " + << strerror(-ret) << std::endl; + return ret; + } + + /* + * Enable the atomic APIs. This also enables automatically the + * universal planes API. + */ + ret = drmSetClientCap(fd_, DRM_CLIENT_CAP_ATOMIC, 1); + if (ret < 0) { + ret = -errno; + std::cerr + << "Failed to enable atomic capability: " + << strerror(-ret) << std::endl; + return ret; + } + + /* List all the resources. */ + ret = getResources(); + if (ret < 0) + return ret; + + notifier_ = new libcamera::EventNotifier(fd_, libcamera::EventNotifier::Read); + notifier_->activated.connect(this, &Device::drmEvent); + + return 0; +} + +int Device::getResources() +{ + int ret; + + std::unique_ptr resources{ + drmModeGetResources(fd_), + &drmModeFreeResources + }; + if (!resources) { + ret = -errno; + std::cerr + << "Failed to get DRM/KMS resources: " + << strerror(-ret) << std::endl; + return ret; + } + + for (int i = 0; i < resources->count_crtcs; ++i) { + drmModeCrtc *crtc = drmModeGetCrtc(fd_, resources->crtcs[i]); + if (!crtc) { + ret = -errno; + std::cerr + << "Failed to get CRTC: " << strerror(-ret) + << std::endl; + return ret; + } + + crtcs_.emplace_back(this, crtc, i); + drmModeFreeCrtc(crtc); + + Crtc &obj = crtcs_.back(); + objects_[obj.id()] = &obj; + } + + for (int i = 0; i < resources->count_encoders; ++i) { + drmModeEncoder *encoder = + drmModeGetEncoder(fd_, resources->encoders[i]); + if (!encoder) { + ret = -errno; + std::cerr + << "Failed to get encoder: " << strerror(-ret) + << std::endl; + return ret; + } + + encoders_.emplace_back(this, encoder); + drmModeFreeEncoder(encoder); + + Encoder &obj = encoders_.back(); + objects_[obj.id()] = &obj; + } + + for (int i = 0; i < resources->count_connectors; ++i) { + drmModeConnector *connector = + drmModeGetConnector(fd_, resources->connectors[i]); + if (!connector) { + ret = -errno; + std::cerr + << "Failed to get connector: " << strerror(-ret) + << std::endl; + return ret; + } + + connectors_.emplace_back(this, connector); + drmModeFreeConnector(connector); + + Connector &obj = connectors_.back(); + objects_[obj.id()] = &obj; + } + + std::unique_ptr planes{ + drmModeGetPlaneResources(fd_), + &drmModeFreePlaneResources + }; + if (!planes) { + ret = -errno; + std::cerr + << "Failed to get DRM/KMS planes: " + << strerror(-ret) << std::endl; + return ret; + } + + for (uint32_t i = 0; i < planes->count_planes; ++i) { + drmModePlane *plane = + drmModeGetPlane(fd_, planes->planes[i]); + if (!plane) { + ret = -errno; + std::cerr + << "Failed to get plane: " << strerror(-ret) + << std::endl; + return ret; + } + + planes_.emplace_back(this, plane); + drmModeFreePlane(plane); + + Plane &obj = planes_.back(); + objects_[obj.id()] = &obj; + } + + /* Set the possible planes for each CRTC. */ + for (Crtc &crtc : crtcs_) { + for (const Plane &plane : planes_) { + if (plane.possibleCrtcsMask_ & (1 << crtc.index())) + crtc.planes_.push_back(&plane); + } + } + + /* Collect all property IDs and create Property instances. */ + std::set properties; + for (const auto &object : objects_) { + for (const PropertyValue &value : object.second->properties()) + properties.insert(value.id()); + } + + for (uint32_t id : properties) { + drmModePropertyRes *property = drmModeGetProperty(fd_, id); + if (!property) { + ret = -errno; + std::cerr + << "Failed to get property: " << strerror(-ret) + << std::endl; + continue; + } + + properties_.emplace_back(this, property); + drmModeFreeProperty(property); + + Property &obj = properties_.back(); + objects_[obj.id()] = &obj; + } + + /* Finally, perform all delayed setup of mode objects. */ + for (auto &object : objects_) { + ret = object.second->setup(); + if (ret < 0) { + std::cerr + << "Failed to setup object " << object.second->id() + << ": " << strerror(-ret) << std::endl; + return ret; + } + } + + return 0; +} + +const Object *Device::object(uint32_t id) +{ + const auto iter = objects_.find(id); + if (iter == objects_.end()) + return nullptr; + + return iter->second; +} + +std::unique_ptr Device::createFrameBuffer( + const libcamera::FrameBuffer &buffer, + const libcamera::PixelFormat &format, + const libcamera::Size &size, unsigned int stride) +{ + std::unique_ptr fb{ new FrameBuffer(this) }; + + uint32_t handles[4] = {}; + uint32_t pitches[4] = {}; + uint32_t offsets[4] = {}; + int ret; + + const std::vector &planes = buffer.planes(); + fb->planes_.reserve(planes.size()); + + unsigned int i = 0; + for (const libcamera::FrameBuffer::Plane &plane : planes) { + uint32_t handle; + + ret = drmPrimeFDToHandle(fd_, plane.fd.fd(), &handle); + if (ret < 0) { + ret = -errno; + std::cerr + << "Unable to import framebuffer dmabuf: " + << strerror(-ret) << std::endl; + return nullptr; + } + + fb->planes_.push_back({ handle }); + + handles[i] = handle; + pitches[i] = stride; + offsets[i] = 0; /* TODO */ + ++i; + } + + ret = drmModeAddFB2(fd_, size.width, size.height, format.fourcc(), handles, + pitches, offsets, &fb->id_, 0); + if (ret < 0) { + ret = -errno; + std::cerr + << "Failed to add framebuffer: " + << strerror(-ret) << std::endl; + return nullptr; + } + + return fb; +} + +void Device::drmEvent(libcamera::EventNotifier *notifier) +{ + drmEventContext ctx{}; + ctx.version = DRM_EVENT_CONTEXT_VERSION; + ctx.page_flip_handler = &Device::pageFlipComplete; + + drmHandleEvent(fd_, &ctx); +} + +void Device::pageFlipComplete(int fd, unsigned int sequence, unsigned int tv_sec, + unsigned int tv_usec, void *user_data) +{ + AtomicRequest *request = static_cast(user_data); + request->device()->requestComplete.emit(request); +} + +} /* namespace DRM */ diff --git a/src/cam/drm.h b/src/cam/drm.h new file mode 100644 index 000000000000..c1c2bab4b121 --- /dev/null +++ b/src/cam/drm.h @@ -0,0 +1,334 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Ideas on Board Oy + * + * drm.h - DRM/KMS Helpers + */ +#ifndef __CAM_DRM_H__ +#define __CAM_DRM_H__ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace libcamera { +class EventNotifier; +class FrameBuffer; +class PixelFormat; +struct Size; +} /* namespace libcamera */ + +namespace DRM { + +class Device; +class Plane; +class Property; +class PropertyValue; + +class Object +{ +public: + enum Type { + TypeCrtc = DRM_MODE_OBJECT_CRTC, + TypeConnector = DRM_MODE_OBJECT_CONNECTOR, + TypeEncoder = DRM_MODE_OBJECT_ENCODER, + TypeMode = DRM_MODE_OBJECT_MODE, + TypeProperty = DRM_MODE_OBJECT_PROPERTY, + TypeFb = DRM_MODE_OBJECT_FB, + TypeBlob = DRM_MODE_OBJECT_BLOB, + TypePlane = DRM_MODE_OBJECT_PLANE, + TypeAny = DRM_MODE_OBJECT_ANY, + }; + + Object(Device *dev, uint32_t id, Type type); + virtual ~Object(); + + Device *device() const { return dev_; } + uint32_t id() const { return id_; } + Type type() const { return type_; } + + const Property *property(const std::string &name) const; + const PropertyValue *propertyValue(const std::string &name) const; + const std::vector &properties() const { return properties_; } + +protected: + virtual int setup() + { + return 0; + } + + uint32_t id_; + +private: + friend Device; + + Device *dev_; + Type type_; + std::vector properties_; +}; + +class Property : public Object +{ +public: + enum Type { + TypeUnknown = 0, + TypeRange, + TypeEnum, + TypeBlob, + TypeBitmask, + TypeObject, + TypeSignedRange, + }; + + Property(Device *dev, drmModePropertyRes *property); + + Type type() const { return type_; } + const std::string &name() const { return name_; } + + bool isImmutable() const { return flags_ & DRM_MODE_PROP_IMMUTABLE; } + + const std::vector values() const { return values_; } + const std::map &enums() const { return enums_; } + const std::vector blobs() const { return blobs_; } + +private: + Type type_; + std::string name_; + uint32_t flags_; + std::vector values_; + std::map enums_; + std::vector blobs_; +}; + +class PropertyValue +{ +public: + PropertyValue(uint32_t id, uint64_t value) + : id_(id), value_(value) + { + } + + uint32_t id() const { return id_; } + uint32_t value() const { return value_; } + +private: + uint32_t id_; + uint64_t value_; +}; + +class Blob : public Object +{ +public: + Blob(Device *dev, const libcamera::Span &data); + ~Blob(); + + bool isValid() const { return id() != 0; } +}; + +class Mode : public drmModeModeInfo +{ +public: + Mode(const drmModeModeInfo &mode); + + std::unique_ptr toBlob(Device *dev) const; +}; + +class Crtc : public Object +{ +public: + Crtc(Device *dev, const drmModeCrtc *crtc, unsigned int index); + + unsigned int index() const { return index_; } + const std::vector &planes() const { return planes_; } + +private: + friend Device; + + unsigned int index_; + std::vector planes_; +}; + +class Encoder : public Object +{ +public: + Encoder(Device *dev, const drmModeEncoder *encoder); + + uint32_t type() const { return type_; } + + const std::vector &possibleCrtcs() const { return possibleCrtcs_; } + +private: + uint32_t type_; + std::vector possibleCrtcs_; +}; + +class Connector : public Object +{ +public: + enum Status { + Connected, + Disconnected, + Unknown, + }; + + Connector(Device *dev, const drmModeConnector *connector); + + uint32_t type() const { return type_; } + const std::string &name() const { return name_; } + + Status status() const { return status_; } + + const std::vector &encoders() const { return encoders_; } + const std::vector &modes() const { return modes_; } + +private: + uint32_t type_; + std::string name_; + Status status_; + std::vector encoders_; + std::vector modes_; +}; + +class Plane : public Object +{ +public: + enum Type { + TypeOverlay, + TypePrimary, + TypeCursor, + }; + + Plane(Device *dev, const drmModePlane *plane); + + Type type() const { return type_; } + const std::vector &formats() const { return formats_; } + const std::vector &possibleCrtcs() const { return possibleCrtcs_; } + + bool supportsFormat(const libcamera::PixelFormat &format) const; + +protected: + int setup() override; + +private: + friend class Device; + + Type type_; + std::vector formats_; + std::vector possibleCrtcs_; + uint32_t possibleCrtcsMask_; +}; + +class FrameBuffer : public Object +{ +public: + struct Plane { + uint32_t handle; + }; + + ~FrameBuffer(); + +private: + friend class Device; + + FrameBuffer(Device *dev); + + std::vector planes_; +}; + +class AtomicRequest +{ +public: + enum Flags { + FlagAllowModeset = (1 << 0), + FlagAsync = (1 << 1), + }; + + AtomicRequest(Device *dev); + ~AtomicRequest(); + + Device *device() const { return dev_; } + bool isValid() const { return valid_; } + + int addProperty(const Object *object, const std::string &property, + uint64_t value); + int addProperty(const Object *object, const std::string &property, + std::unique_ptr blob); + int commit(unsigned int flags = 0); + +private: + AtomicRequest(const AtomicRequest &) = delete; + AtomicRequest(const AtomicRequest &&) = delete; + AtomicRequest &operator=(const AtomicRequest &) = delete; + AtomicRequest &operator=(const AtomicRequest &&) = delete; + + int addProperty(uint32_t object, uint32_t property, uint64_t value); + + Device *dev_; + bool valid_; + drmModeAtomicReq *request_; + std::list> blobs_; +}; + +class Device +{ +public: + Device(); + ~Device(); + + int init(); + + int fd() const { return fd_; } + + const std::list &crtcs() const { return crtcs_; } + const std::list &encoders() const { return encoders_; } + const std::list &connectors() const { return connectors_; } + const std::list &planes() const { return planes_; } + const std::list &properties() const { return properties_; } + + const Object *object(uint32_t id); + + std::unique_ptr createFrameBuffer( + const libcamera::FrameBuffer &buffer, + const libcamera::PixelFormat &format, + const libcamera::Size &size, unsigned int stride); + + libcamera::Signal requestComplete; + +private: + Device(const Device &) = delete; + Device(const Device &&) = delete; + Device &operator=(const Device &) = delete; + Device &operator=(const Device &&) = delete; + + int getResources(); + + void drmEvent(libcamera::EventNotifier *notifier); + static void pageFlipComplete(int fd, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, + void *user_data); + + int fd_; + + std::list crtcs_; + std::list encoders_; + std::list connectors_; + std::list planes_; + std::list properties_; + + std::map objects_; + + libcamera::EventNotifier *notifier_; +}; + +} /* namespace DRM */ + +#endif /* __CAM_DRM_H__ */ diff --git a/src/cam/meson.build b/src/cam/meson.build index 6ba49e82fbd1..b0a8a7580dcc 100644 --- a/src/cam/meson.build +++ b/src/cam/meson.build @@ -10,6 +10,18 @@ cam_sources = files([ 'stream_options.cpp', ]) +cam_cpp_args = [] + +libdrm = dependency('libdrm', required : false) + +if libdrm.found() +cam_cpp_args += [ '-DHAVE_KMS' ] +cam_sources += files([ + 'drm.cpp', +]) +endif + cam = executable('cam', cam_sources, - dependencies : [ libatomic, libcamera_dep ], + dependencies : [ libatomic, libcamera_dep, libdrm ], + cpp_args : cam_cpp_args, install : true)