Message ID | 20210730010306.19956-6-laurent.pinchart@ideasonboard.com |
---|---|
State | Accepted |
Headers | show |
Series |
|
Related | show |
Hi Laurent, On Fri, Jul 30, 2021 at 04:03:03AM +0300, Laurent Pinchart wrote: > 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 <laurent.pinchart@ideasonboard.com> oof, that was long... isn't there a library for this or something? :p Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> > --- > Changes since v1: > > - Use drm.h and drm_mode.h from libdrm > - Drop writeback connector > --- > src/cam/drm.cpp | 663 ++++++++++++++++++++++++++++++++++++++++++++ > src/cam/drm.h | 331 ++++++++++++++++++++++ > src/cam/meson.build | 13 + > 3 files changed, 1007 insertions(+) > 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..56a6cbd2442e > --- /dev/null > +++ b/src/cam/drm.cpp > @@ -0,0 +1,663 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * Copyright (C) 2020, Ideas on Board Oy > + * > + * drm.cpp - DRM/KMS Helpers > + */ > + > +#include "drm.h" > + > +#include <algorithm> > +#include <errno.h> > +#include <fcntl.h> > +#include <iostream> > +#include <set> > +#include <string.h> > +#include <sys/ioctl.h> > +#include <sys/stat.h> > +#include <sys/types.h> > + > +#include <libcamera/framebuffer.h> > +#include <libcamera/geometry.h> > +#include <libcamera/pixel_format.h> > + > +#include <libdrm/drm_mode.h> > + > +#include "event_loop.h" > + > +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<const Property *>(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<const Property *>(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<const uint8_t> &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<Blob> Mode::toBlob(Device *dev) const > +{ > + libcamera::Span<const uint8_t> data{ reinterpret_cast<const uint8_t *>(this), > + sizeof(*this) }; > + return std::make_unique<Blob>(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<Crtc> &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<uint32_t, const char *> 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" }, > +}; > + > +} /* 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<Encoder> &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 &e) { > + return e.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<Crtc> &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> 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; > + > + EventLoop::instance()->addEvent(fd_, EventLoop::Read, > + std::bind(&Device::drmEvent, this)); > + > + return 0; > +} > + > +int Device::getResources() > +{ > + int ret; > + > + std::unique_ptr<drmModeRes, decltype(&drmModeFreeResources)> 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<drmModePlaneRes, decltype(&drmModeFreePlaneResources)> 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<uint32_t> 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<FrameBuffer> Device::createFrameBuffer( > + const libcamera::FrameBuffer &buffer, > + const libcamera::PixelFormat &format, > + const libcamera::Size &size, unsigned int stride) > +{ > + std::unique_ptr<FrameBuffer> fb{ new FrameBuffer(this) }; > + > + uint32_t handles[4] = {}; > + uint32_t pitches[4] = {}; > + uint32_t offsets[4] = {}; > + int ret; > + > + const std::vector<libcamera::FrameBuffer::Plane> &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() > +{ > + drmEventContext ctx{}; > + ctx.version = DRM_EVENT_CONTEXT_VERSION; > + ctx.page_flip_handler = &Device::pageFlipComplete; > + > + drmHandleEvent(fd_, &ctx); > +} > + > +void Device::pageFlipComplete([[maybe_unused]] int fd, > + [[maybe_unused]] unsigned int sequence, > + [[maybe_unused]] unsigned int tv_sec, > + [[maybe_unused]] unsigned int tv_usec, > + void *user_data) > +{ > + AtomicRequest *request = static_cast<AtomicRequest *>(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..e1395bc9c7e6 > --- /dev/null > +++ b/src/cam/drm.h > @@ -0,0 +1,331 @@ > +/* 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 <list> > +#include <map> > +#include <memory> > +#include <string> > +#include <vector> > + > +#include <libcamera/base/signal.h> > +#include <libcamera/base/span.h> > + > +#include <libdrm/drm.h> > +#include <xf86drm.h> > +#include <xf86drmMode.h> > + > +namespace libcamera { > +class FrameBuffer; > +class PixelFormat; > +class 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<PropertyValue> &properties() const { return properties_; } > + > +protected: > + virtual int setup() > + { > + return 0; > + } > + > + uint32_t id_; > + > +private: > + friend Device; > + > + Device *dev_; > + Type type_; > + std::vector<PropertyValue> 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<uint64_t> values() const { return values_; } > + const std::map<uint32_t, std::string> &enums() const { return enums_; } > + const std::vector<uint32_t> blobs() const { return blobs_; } > + > +private: > + Type type_; > + std::string name_; > + uint32_t flags_; > + std::vector<uint64_t> values_; > + std::map<uint32_t, std::string> enums_; > + std::vector<uint32_t> 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<const uint8_t> &data); > + ~Blob(); > + > + bool isValid() const { return id() != 0; } > +}; > + > +class Mode : public drmModeModeInfo > +{ > +public: > + Mode(const drmModeModeInfo &mode); > + > + std::unique_ptr<Blob> 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<const Plane *> &planes() const { return planes_; } > + > +private: > + friend Device; > + > + unsigned int index_; > + std::vector<const Plane *> planes_; > +}; > + > +class Encoder : public Object > +{ > +public: > + Encoder(Device *dev, const drmModeEncoder *encoder); > + > + uint32_t type() const { return type_; } > + > + const std::vector<const Crtc *> &possibleCrtcs() const { return possibleCrtcs_; } > + > +private: > + uint32_t type_; > + std::vector<const Crtc *> 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<const Encoder *> &encoders() const { return encoders_; } > + const std::vector<Mode> &modes() const { return modes_; } > + > +private: > + uint32_t type_; > + std::string name_; > + Status status_; > + std::vector<const Encoder *> encoders_; > + std::vector<Mode> 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<uint32_t> &formats() const { return formats_; } > + const std::vector<const Crtc *> &possibleCrtcs() const { return possibleCrtcs_; } > + > + bool supportsFormat(const libcamera::PixelFormat &format) const; > + > +protected: > + int setup() override; > + > +private: > + friend class Device; > + > + Type type_; > + std::vector<uint32_t> formats_; > + std::vector<const Crtc *> possibleCrtcs_; > + uint32_t possibleCrtcsMask_; > +}; > + > +class FrameBuffer : public Object > +{ > +public: > + struct Plane { > + uint32_t handle; > + }; > + > + ~FrameBuffer(); > + > +private: > + friend class Device; > + > + FrameBuffer(Device *dev); > + > + std::vector<Plane> 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> 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<std::unique_ptr<Blob>> blobs_; > +}; > + > +class Device > +{ > +public: > + Device(); > + ~Device(); > + > + int init(); > + > + int fd() const { return fd_; } > + > + const std::list<Crtc> &crtcs() const { return crtcs_; } > + const std::list<Encoder> &encoders() const { return encoders_; } > + const std::list<Connector> &connectors() const { return connectors_; } > + const std::list<Plane> &planes() const { return planes_; } > + const std::list<Property> &properties() const { return properties_; } > + > + const Object *object(uint32_t id); > + > + std::unique_ptr<FrameBuffer> createFrameBuffer( > + const libcamera::FrameBuffer &buffer, > + const libcamera::PixelFormat &format, > + const libcamera::Size &size, unsigned int stride); > + > + libcamera::Signal<AtomicRequest *> requestComplete; > + > +private: > + Device(const Device &) = delete; > + Device(const Device &&) = delete; > + Device &operator=(const Device &) = delete; > + Device &operator=(const Device &&) = delete; > + > + int getResources(); > + > + void drmEvent(); > + static void pageFlipComplete(int fd, unsigned int sequence, > + unsigned int tv_sec, unsigned int tv_usec, > + void *user_data); > + > + int fd_; > + > + std::list<Crtc> crtcs_; > + std::list<Encoder> encoders_; > + std::list<Connector> connectors_; > + std::list<Plane> planes_; > + std::list<Property> properties_; > + > + std::map<uint32_t, Object *> objects_; > +}; > + > +} /* namespace DRM */ > + > +#endif /* __CAM_DRM_H__ */ > diff --git a/src/cam/meson.build b/src/cam/meson.build > index e692ea351987..b47add55b0cb 100644 > --- a/src/cam/meson.build > +++ b/src/cam/meson.build > @@ -19,10 +19,23 @@ 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_public, > + libdrm, > libevent, > ], > + cpp_args : cam_cpp_args, > install : true) > -- > Regards, > > Laurent Pinchart >
Hi Laurent, On Wed, Aug 04, 2021 at 02:48:44PM +0900, paul.elder@ideasonboard.com wrote: > Hi Laurent, > > On Fri, Jul 30, 2021 at 04:03:03AM +0300, Laurent Pinchart wrote: > > 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 <laurent.pinchart@ideasonboard.com> > > oof, that was long... isn't there a library for this or something? :p > > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> > > > --- > > Changes since v1: > > > > - Use drm.h and drm_mode.h from libdrm > > - Drop writeback connector > > --- > > src/cam/drm.cpp | 663 ++++++++++++++++++++++++++++++++++++++++++++ > > src/cam/drm.h | 331 ++++++++++++++++++++++ > > src/cam/meson.build | 13 + > > 3 files changed, 1007 insertions(+) > > 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..56a6cbd2442e > > --- /dev/null > > +++ b/src/cam/drm.cpp > > @@ -0,0 +1,663 @@ > > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > > +/* > > + * Copyright (C) 2020, Ideas on Board Oy s/2020/2021 ? Also for the header. Paul > > + * > > + * drm.cpp - DRM/KMS Helpers > > + */ > > + > > +#include "drm.h" > > + > > +#include <algorithm> > > +#include <errno.h> > > +#include <fcntl.h> > > +#include <iostream> > > +#include <set> > > +#include <string.h> > > +#include <sys/ioctl.h> > > +#include <sys/stat.h> > > +#include <sys/types.h> > > + > > +#include <libcamera/framebuffer.h> > > +#include <libcamera/geometry.h> > > +#include <libcamera/pixel_format.h> > > + > > +#include <libdrm/drm_mode.h> > > + > > +#include "event_loop.h" > > + > > +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<const Property *>(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<const Property *>(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<const uint8_t> &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<Blob> Mode::toBlob(Device *dev) const > > +{ > > + libcamera::Span<const uint8_t> data{ reinterpret_cast<const uint8_t *>(this), > > + sizeof(*this) }; > > + return std::make_unique<Blob>(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<Crtc> &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<uint32_t, const char *> 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" }, > > +}; > > + > > +} /* 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<Encoder> &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 &e) { > > + return e.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<Crtc> &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> 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; > > + > > + EventLoop::instance()->addEvent(fd_, EventLoop::Read, > > + std::bind(&Device::drmEvent, this)); > > + > > + return 0; > > +} > > + > > +int Device::getResources() > > +{ > > + int ret; > > + > > + std::unique_ptr<drmModeRes, decltype(&drmModeFreeResources)> 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<drmModePlaneRes, decltype(&drmModeFreePlaneResources)> 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<uint32_t> 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<FrameBuffer> Device::createFrameBuffer( > > + const libcamera::FrameBuffer &buffer, > > + const libcamera::PixelFormat &format, > > + const libcamera::Size &size, unsigned int stride) > > +{ > > + std::unique_ptr<FrameBuffer> fb{ new FrameBuffer(this) }; > > + > > + uint32_t handles[4] = {}; > > + uint32_t pitches[4] = {}; > > + uint32_t offsets[4] = {}; > > + int ret; > > + > > + const std::vector<libcamera::FrameBuffer::Plane> &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() > > +{ > > + drmEventContext ctx{}; > > + ctx.version = DRM_EVENT_CONTEXT_VERSION; > > + ctx.page_flip_handler = &Device::pageFlipComplete; > > + > > + drmHandleEvent(fd_, &ctx); > > +} > > + > > +void Device::pageFlipComplete([[maybe_unused]] int fd, > > + [[maybe_unused]] unsigned int sequence, > > + [[maybe_unused]] unsigned int tv_sec, > > + [[maybe_unused]] unsigned int tv_usec, > > + void *user_data) > > +{ > > + AtomicRequest *request = static_cast<AtomicRequest *>(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..e1395bc9c7e6 > > --- /dev/null > > +++ b/src/cam/drm.h > > @@ -0,0 +1,331 @@ > > +/* 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 <list> > > +#include <map> > > +#include <memory> > > +#include <string> > > +#include <vector> > > + > > +#include <libcamera/base/signal.h> > > +#include <libcamera/base/span.h> > > + > > +#include <libdrm/drm.h> > > +#include <xf86drm.h> > > +#include <xf86drmMode.h> > > + > > +namespace libcamera { > > +class FrameBuffer; > > +class PixelFormat; > > +class 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<PropertyValue> &properties() const { return properties_; } > > + > > +protected: > > + virtual int setup() > > + { > > + return 0; > > + } > > + > > + uint32_t id_; > > + > > +private: > > + friend Device; > > + > > + Device *dev_; > > + Type type_; > > + std::vector<PropertyValue> 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<uint64_t> values() const { return values_; } > > + const std::map<uint32_t, std::string> &enums() const { return enums_; } > > + const std::vector<uint32_t> blobs() const { return blobs_; } > > + > > +private: > > + Type type_; > > + std::string name_; > > + uint32_t flags_; > > + std::vector<uint64_t> values_; > > + std::map<uint32_t, std::string> enums_; > > + std::vector<uint32_t> 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<const uint8_t> &data); > > + ~Blob(); > > + > > + bool isValid() const { return id() != 0; } > > +}; > > + > > +class Mode : public drmModeModeInfo > > +{ > > +public: > > + Mode(const drmModeModeInfo &mode); > > + > > + std::unique_ptr<Blob> 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<const Plane *> &planes() const { return planes_; } > > + > > +private: > > + friend Device; > > + > > + unsigned int index_; > > + std::vector<const Plane *> planes_; > > +}; > > + > > +class Encoder : public Object > > +{ > > +public: > > + Encoder(Device *dev, const drmModeEncoder *encoder); > > + > > + uint32_t type() const { return type_; } > > + > > + const std::vector<const Crtc *> &possibleCrtcs() const { return possibleCrtcs_; } > > + > > +private: > > + uint32_t type_; > > + std::vector<const Crtc *> 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<const Encoder *> &encoders() const { return encoders_; } > > + const std::vector<Mode> &modes() const { return modes_; } > > + > > +private: > > + uint32_t type_; > > + std::string name_; > > + Status status_; > > + std::vector<const Encoder *> encoders_; > > + std::vector<Mode> 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<uint32_t> &formats() const { return formats_; } > > + const std::vector<const Crtc *> &possibleCrtcs() const { return possibleCrtcs_; } > > + > > + bool supportsFormat(const libcamera::PixelFormat &format) const; > > + > > +protected: > > + int setup() override; > > + > > +private: > > + friend class Device; > > + > > + Type type_; > > + std::vector<uint32_t> formats_; > > + std::vector<const Crtc *> possibleCrtcs_; > > + uint32_t possibleCrtcsMask_; > > +}; > > + > > +class FrameBuffer : public Object > > +{ > > +public: > > + struct Plane { > > + uint32_t handle; > > + }; > > + > > + ~FrameBuffer(); > > + > > +private: > > + friend class Device; > > + > > + FrameBuffer(Device *dev); > > + > > + std::vector<Plane> 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> 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<std::unique_ptr<Blob>> blobs_; > > +}; > > + > > +class Device > > +{ > > +public: > > + Device(); > > + ~Device(); > > + > > + int init(); > > + > > + int fd() const { return fd_; } > > + > > + const std::list<Crtc> &crtcs() const { return crtcs_; } > > + const std::list<Encoder> &encoders() const { return encoders_; } > > + const std::list<Connector> &connectors() const { return connectors_; } > > + const std::list<Plane> &planes() const { return planes_; } > > + const std::list<Property> &properties() const { return properties_; } > > + > > + const Object *object(uint32_t id); > > + > > + std::unique_ptr<FrameBuffer> createFrameBuffer( > > + const libcamera::FrameBuffer &buffer, > > + const libcamera::PixelFormat &format, > > + const libcamera::Size &size, unsigned int stride); > > + > > + libcamera::Signal<AtomicRequest *> requestComplete; > > + > > +private: > > + Device(const Device &) = delete; > > + Device(const Device &&) = delete; > > + Device &operator=(const Device &) = delete; > > + Device &operator=(const Device &&) = delete; > > + > > + int getResources(); > > + > > + void drmEvent(); > > + static void pageFlipComplete(int fd, unsigned int sequence, > > + unsigned int tv_sec, unsigned int tv_usec, > > + void *user_data); > > + > > + int fd_; > > + > > + std::list<Crtc> crtcs_; > > + std::list<Encoder> encoders_; > > + std::list<Connector> connectors_; > > + std::list<Plane> planes_; > > + std::list<Property> properties_; > > + > > + std::map<uint32_t, Object *> objects_; > > +}; > > + > > +} /* namespace DRM */ > > + > > +#endif /* __CAM_DRM_H__ */ > > diff --git a/src/cam/meson.build b/src/cam/meson.build > > index e692ea351987..b47add55b0cb 100644 > > --- a/src/cam/meson.build > > +++ b/src/cam/meson.build > > @@ -19,10 +19,23 @@ 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_public, > > + libdrm, > > libevent, > > ], > > + cpp_args : cam_cpp_args, > > install : true) > > -- > > Regards, > > > > Laurent Pinchart > >
Hi Paul, On Wed, Aug 04, 2021 at 02:48:44PM +0900, paul.elder@ideasonboard.com wrote: > On Fri, Jul 30, 2021 at 04:03:03AM +0300, Laurent Pinchart wrote: > > 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 <laurent.pinchart@ideasonboard.com> > > oof, that was long... isn't there a library for this or something? :p Funny you mention that, no later than yesterday I happened to discuss https://github.com/tomba/kmsxx.git in this context. I've considered it, but wanted to minimize the external dependencies for cam. kmsxx also doesn't have a stable ABI yet, but that could possibly be worked around with static linking. > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> > > > --- > > Changes since v1: > > > > - Use drm.h and drm_mode.h from libdrm > > - Drop writeback connector > > --- > > src/cam/drm.cpp | 663 ++++++++++++++++++++++++++++++++++++++++++++ > > src/cam/drm.h | 331 ++++++++++++++++++++++ > > src/cam/meson.build | 13 + > > 3 files changed, 1007 insertions(+) > > 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..56a6cbd2442e > > --- /dev/null > > +++ b/src/cam/drm.cpp > > @@ -0,0 +1,663 @@ > > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > > +/* > > + * Copyright (C) 2020, Ideas on Board Oy > > + * > > + * drm.cpp - DRM/KMS Helpers > > + */ > > + > > +#include "drm.h" > > + > > +#include <algorithm> > > +#include <errno.h> > > +#include <fcntl.h> > > +#include <iostream> > > +#include <set> > > +#include <string.h> > > +#include <sys/ioctl.h> > > +#include <sys/stat.h> > > +#include <sys/types.h> > > + > > +#include <libcamera/framebuffer.h> > > +#include <libcamera/geometry.h> > > +#include <libcamera/pixel_format.h> > > + > > +#include <libdrm/drm_mode.h> > > + > > +#include "event_loop.h" > > + > > +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<const Property *>(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<const Property *>(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<const uint8_t> &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<Blob> Mode::toBlob(Device *dev) const > > +{ > > + libcamera::Span<const uint8_t> data{ reinterpret_cast<const uint8_t *>(this), > > + sizeof(*this) }; > > + return std::make_unique<Blob>(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<Crtc> &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<uint32_t, const char *> 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" }, > > +}; > > + > > +} /* 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<Encoder> &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 &e) { > > + return e.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<Crtc> &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> 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; > > + > > + EventLoop::instance()->addEvent(fd_, EventLoop::Read, > > + std::bind(&Device::drmEvent, this)); > > + > > + return 0; > > +} > > + > > +int Device::getResources() > > +{ > > + int ret; > > + > > + std::unique_ptr<drmModeRes, decltype(&drmModeFreeResources)> 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<drmModePlaneRes, decltype(&drmModeFreePlaneResources)> 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<uint32_t> 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<FrameBuffer> Device::createFrameBuffer( > > + const libcamera::FrameBuffer &buffer, > > + const libcamera::PixelFormat &format, > > + const libcamera::Size &size, unsigned int stride) > > +{ > > + std::unique_ptr<FrameBuffer> fb{ new FrameBuffer(this) }; > > + > > + uint32_t handles[4] = {}; > > + uint32_t pitches[4] = {}; > > + uint32_t offsets[4] = {}; > > + int ret; > > + > > + const std::vector<libcamera::FrameBuffer::Plane> &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() > > +{ > > + drmEventContext ctx{}; > > + ctx.version = DRM_EVENT_CONTEXT_VERSION; > > + ctx.page_flip_handler = &Device::pageFlipComplete; > > + > > + drmHandleEvent(fd_, &ctx); > > +} > > + > > +void Device::pageFlipComplete([[maybe_unused]] int fd, > > + [[maybe_unused]] unsigned int sequence, > > + [[maybe_unused]] unsigned int tv_sec, > > + [[maybe_unused]] unsigned int tv_usec, > > + void *user_data) > > +{ > > + AtomicRequest *request = static_cast<AtomicRequest *>(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..e1395bc9c7e6 > > --- /dev/null > > +++ b/src/cam/drm.h > > @@ -0,0 +1,331 @@ > > +/* 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 <list> > > +#include <map> > > +#include <memory> > > +#include <string> > > +#include <vector> > > + > > +#include <libcamera/base/signal.h> > > +#include <libcamera/base/span.h> > > + > > +#include <libdrm/drm.h> > > +#include <xf86drm.h> > > +#include <xf86drmMode.h> > > + > > +namespace libcamera { > > +class FrameBuffer; > > +class PixelFormat; > > +class 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<PropertyValue> &properties() const { return properties_; } > > + > > +protected: > > + virtual int setup() > > + { > > + return 0; > > + } > > + > > + uint32_t id_; > > + > > +private: > > + friend Device; > > + > > + Device *dev_; > > + Type type_; > > + std::vector<PropertyValue> 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<uint64_t> values() const { return values_; } > > + const std::map<uint32_t, std::string> &enums() const { return enums_; } > > + const std::vector<uint32_t> blobs() const { return blobs_; } > > + > > +private: > > + Type type_; > > + std::string name_; > > + uint32_t flags_; > > + std::vector<uint64_t> values_; > > + std::map<uint32_t, std::string> enums_; > > + std::vector<uint32_t> 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<const uint8_t> &data); > > + ~Blob(); > > + > > + bool isValid() const { return id() != 0; } > > +}; > > + > > +class Mode : public drmModeModeInfo > > +{ > > +public: > > + Mode(const drmModeModeInfo &mode); > > + > > + std::unique_ptr<Blob> 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<const Plane *> &planes() const { return planes_; } > > + > > +private: > > + friend Device; > > + > > + unsigned int index_; > > + std::vector<const Plane *> planes_; > > +}; > > + > > +class Encoder : public Object > > +{ > > +public: > > + Encoder(Device *dev, const drmModeEncoder *encoder); > > + > > + uint32_t type() const { return type_; } > > + > > + const std::vector<const Crtc *> &possibleCrtcs() const { return possibleCrtcs_; } > > + > > +private: > > + uint32_t type_; > > + std::vector<const Crtc *> 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<const Encoder *> &encoders() const { return encoders_; } > > + const std::vector<Mode> &modes() const { return modes_; } > > + > > +private: > > + uint32_t type_; > > + std::string name_; > > + Status status_; > > + std::vector<const Encoder *> encoders_; > > + std::vector<Mode> 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<uint32_t> &formats() const { return formats_; } > > + const std::vector<const Crtc *> &possibleCrtcs() const { return possibleCrtcs_; } > > + > > + bool supportsFormat(const libcamera::PixelFormat &format) const; > > + > > +protected: > > + int setup() override; > > + > > +private: > > + friend class Device; > > + > > + Type type_; > > + std::vector<uint32_t> formats_; > > + std::vector<const Crtc *> possibleCrtcs_; > > + uint32_t possibleCrtcsMask_; > > +}; > > + > > +class FrameBuffer : public Object > > +{ > > +public: > > + struct Plane { > > + uint32_t handle; > > + }; > > + > > + ~FrameBuffer(); > > + > > +private: > > + friend class Device; > > + > > + FrameBuffer(Device *dev); > > + > > + std::vector<Plane> 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> 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<std::unique_ptr<Blob>> blobs_; > > +}; > > + > > +class Device > > +{ > > +public: > > + Device(); > > + ~Device(); > > + > > + int init(); > > + > > + int fd() const { return fd_; } > > + > > + const std::list<Crtc> &crtcs() const { return crtcs_; } > > + const std::list<Encoder> &encoders() const { return encoders_; } > > + const std::list<Connector> &connectors() const { return connectors_; } > > + const std::list<Plane> &planes() const { return planes_; } > > + const std::list<Property> &properties() const { return properties_; } > > + > > + const Object *object(uint32_t id); > > + > > + std::unique_ptr<FrameBuffer> createFrameBuffer( > > + const libcamera::FrameBuffer &buffer, > > + const libcamera::PixelFormat &format, > > + const libcamera::Size &size, unsigned int stride); > > + > > + libcamera::Signal<AtomicRequest *> requestComplete; > > + > > +private: > > + Device(const Device &) = delete; > > + Device(const Device &&) = delete; > > + Device &operator=(const Device &) = delete; > > + Device &operator=(const Device &&) = delete; > > + > > + int getResources(); > > + > > + void drmEvent(); > > + static void pageFlipComplete(int fd, unsigned int sequence, > > + unsigned int tv_sec, unsigned int tv_usec, > > + void *user_data); > > + > > + int fd_; > > + > > + std::list<Crtc> crtcs_; > > + std::list<Encoder> encoders_; > > + std::list<Connector> connectors_; > > + std::list<Plane> planes_; > > + std::list<Property> properties_; > > + > > + std::map<uint32_t, Object *> objects_; > > +}; > > + > > +} /* namespace DRM */ > > + > > +#endif /* __CAM_DRM_H__ */ > > diff --git a/src/cam/meson.build b/src/cam/meson.build > > index e692ea351987..b47add55b0cb 100644 > > --- a/src/cam/meson.build > > +++ b/src/cam/meson.build > > @@ -19,10 +19,23 @@ 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_public, > > + libdrm, > > libevent, > > ], > > + cpp_args : cam_cpp_args, > > install : true)
diff --git a/src/cam/drm.cpp b/src/cam/drm.cpp new file mode 100644 index 000000000000..56a6cbd2442e --- /dev/null +++ b/src/cam/drm.cpp @@ -0,0 +1,663 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Ideas on Board Oy + * + * drm.cpp - DRM/KMS Helpers + */ + +#include "drm.h" + +#include <algorithm> +#include <errno.h> +#include <fcntl.h> +#include <iostream> +#include <set> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <libcamera/framebuffer.h> +#include <libcamera/geometry.h> +#include <libcamera/pixel_format.h> + +#include <libdrm/drm_mode.h> + +#include "event_loop.h" + +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<const Property *>(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<const Property *>(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<const uint8_t> &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<Blob> Mode::toBlob(Device *dev) const +{ + libcamera::Span<const uint8_t> data{ reinterpret_cast<const uint8_t *>(this), + sizeof(*this) }; + return std::make_unique<Blob>(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<Crtc> &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<uint32_t, const char *> 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" }, +}; + +} /* 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<Encoder> &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 &e) { + return e.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<Crtc> &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> 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; + + EventLoop::instance()->addEvent(fd_, EventLoop::Read, + std::bind(&Device::drmEvent, this)); + + return 0; +} + +int Device::getResources() +{ + int ret; + + std::unique_ptr<drmModeRes, decltype(&drmModeFreeResources)> 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<drmModePlaneRes, decltype(&drmModeFreePlaneResources)> 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<uint32_t> 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<FrameBuffer> Device::createFrameBuffer( + const libcamera::FrameBuffer &buffer, + const libcamera::PixelFormat &format, + const libcamera::Size &size, unsigned int stride) +{ + std::unique_ptr<FrameBuffer> fb{ new FrameBuffer(this) }; + + uint32_t handles[4] = {}; + uint32_t pitches[4] = {}; + uint32_t offsets[4] = {}; + int ret; + + const std::vector<libcamera::FrameBuffer::Plane> &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() +{ + drmEventContext ctx{}; + ctx.version = DRM_EVENT_CONTEXT_VERSION; + ctx.page_flip_handler = &Device::pageFlipComplete; + + drmHandleEvent(fd_, &ctx); +} + +void Device::pageFlipComplete([[maybe_unused]] int fd, + [[maybe_unused]] unsigned int sequence, + [[maybe_unused]] unsigned int tv_sec, + [[maybe_unused]] unsigned int tv_usec, + void *user_data) +{ + AtomicRequest *request = static_cast<AtomicRequest *>(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..e1395bc9c7e6 --- /dev/null +++ b/src/cam/drm.h @@ -0,0 +1,331 @@ +/* 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 <list> +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include <libcamera/base/signal.h> +#include <libcamera/base/span.h> + +#include <libdrm/drm.h> +#include <xf86drm.h> +#include <xf86drmMode.h> + +namespace libcamera { +class FrameBuffer; +class PixelFormat; +class 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<PropertyValue> &properties() const { return properties_; } + +protected: + virtual int setup() + { + return 0; + } + + uint32_t id_; + +private: + friend Device; + + Device *dev_; + Type type_; + std::vector<PropertyValue> 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<uint64_t> values() const { return values_; } + const std::map<uint32_t, std::string> &enums() const { return enums_; } + const std::vector<uint32_t> blobs() const { return blobs_; } + +private: + Type type_; + std::string name_; + uint32_t flags_; + std::vector<uint64_t> values_; + std::map<uint32_t, std::string> enums_; + std::vector<uint32_t> 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<const uint8_t> &data); + ~Blob(); + + bool isValid() const { return id() != 0; } +}; + +class Mode : public drmModeModeInfo +{ +public: + Mode(const drmModeModeInfo &mode); + + std::unique_ptr<Blob> 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<const Plane *> &planes() const { return planes_; } + +private: + friend Device; + + unsigned int index_; + std::vector<const Plane *> planes_; +}; + +class Encoder : public Object +{ +public: + Encoder(Device *dev, const drmModeEncoder *encoder); + + uint32_t type() const { return type_; } + + const std::vector<const Crtc *> &possibleCrtcs() const { return possibleCrtcs_; } + +private: + uint32_t type_; + std::vector<const Crtc *> 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<const Encoder *> &encoders() const { return encoders_; } + const std::vector<Mode> &modes() const { return modes_; } + +private: + uint32_t type_; + std::string name_; + Status status_; + std::vector<const Encoder *> encoders_; + std::vector<Mode> 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<uint32_t> &formats() const { return formats_; } + const std::vector<const Crtc *> &possibleCrtcs() const { return possibleCrtcs_; } + + bool supportsFormat(const libcamera::PixelFormat &format) const; + +protected: + int setup() override; + +private: + friend class Device; + + Type type_; + std::vector<uint32_t> formats_; + std::vector<const Crtc *> possibleCrtcs_; + uint32_t possibleCrtcsMask_; +}; + +class FrameBuffer : public Object +{ +public: + struct Plane { + uint32_t handle; + }; + + ~FrameBuffer(); + +private: + friend class Device; + + FrameBuffer(Device *dev); + + std::vector<Plane> 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> 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<std::unique_ptr<Blob>> blobs_; +}; + +class Device +{ +public: + Device(); + ~Device(); + + int init(); + + int fd() const { return fd_; } + + const std::list<Crtc> &crtcs() const { return crtcs_; } + const std::list<Encoder> &encoders() const { return encoders_; } + const std::list<Connector> &connectors() const { return connectors_; } + const std::list<Plane> &planes() const { return planes_; } + const std::list<Property> &properties() const { return properties_; } + + const Object *object(uint32_t id); + + std::unique_ptr<FrameBuffer> createFrameBuffer( + const libcamera::FrameBuffer &buffer, + const libcamera::PixelFormat &format, + const libcamera::Size &size, unsigned int stride); + + libcamera::Signal<AtomicRequest *> requestComplete; + +private: + Device(const Device &) = delete; + Device(const Device &&) = delete; + Device &operator=(const Device &) = delete; + Device &operator=(const Device &&) = delete; + + int getResources(); + + void drmEvent(); + static void pageFlipComplete(int fd, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, + void *user_data); + + int fd_; + + std::list<Crtc> crtcs_; + std::list<Encoder> encoders_; + std::list<Connector> connectors_; + std::list<Plane> planes_; + std::list<Property> properties_; + + std::map<uint32_t, Object *> objects_; +}; + +} /* namespace DRM */ + +#endif /* __CAM_DRM_H__ */ diff --git a/src/cam/meson.build b/src/cam/meson.build index e692ea351987..b47add55b0cb 100644 --- a/src/cam/meson.build +++ b/src/cam/meson.build @@ -19,10 +19,23 @@ 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_public, + libdrm, libevent, ], + cpp_args : cam_cpp_args, install : true)
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 <laurent.pinchart@ideasonboard.com> --- Changes since v1: - Use drm.h and drm_mode.h from libdrm - Drop writeback connector --- src/cam/drm.cpp | 663 ++++++++++++++++++++++++++++++++++++++++++++ src/cam/drm.h | 331 ++++++++++++++++++++++ src/cam/meson.build | 13 + 3 files changed, 1007 insertions(+) create mode 100644 src/cam/drm.cpp create mode 100644 src/cam/drm.h