summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cam/drm.cpp663
-rw-r--r--src/cam/drm.h331
-rw-r--r--src/cam/meson.build13
3 files changed, 1007 insertions, 0 deletions
diff --git a/src/cam/drm.cpp b/src/cam/drm.cpp
new file mode 100644
index 00000000..da317e27
--- /dev/null
+++ b/src/cam/drm.cpp
@@ -0,0 +1,663 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2021, 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 automatically enables 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 00000000..ee230402
--- /dev/null
+++ b/src/cam/drm.h
@@ -0,0 +1,331 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2021, 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 e692ea35..b47add55 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)