[libcamera-devel] [PATCH v2 5/8] cam: Add DRM helper classes
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Wed Aug 4 10:38:46 CEST 2021
Hi Paul,
On Wed, Aug 04, 2021 at 02:48:44PM +0900, paul.elder at 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 at 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 at 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
More information about the libcamera-devel
mailing list