summaryrefslogtreecommitdiff
path: root/src/cam/drm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/cam/drm.cpp')
-rw-r--r--src/cam/drm.cpp717
1 files changed, 0 insertions, 717 deletions
diff --git a/src/cam/drm.cpp b/src/cam/drm.cpp
deleted file mode 100644
index 2e4d7985..00000000
--- a/src/cam/drm.cpp
+++ /dev/null
@@ -1,717 +0,0 @@
-/* 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 <dirent.h>
-#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 (const auto &plane : planes_) {
- struct drm_gem_close gem_close = {
- .handle = plane.second.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;
- if (flags & FlagTestOnly)
- drmFlags |= DRM_MODE_ATOMIC_TEST_ONLY;
-
- return drmModeAtomicCommit(dev_->fd(), request_, drmFlags, this);
-}
-
-Device::Device()
- : fd_(-1)
-{
-}
-
-Device::~Device()
-{
- if (fd_ != -1)
- drmClose(fd_);
-}
-
-int Device::init()
-{
- int ret = openCard();
- if (ret < 0) {
- std::cerr << "Failed to open any DRM/KMS device: "
- << 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()->addFdEvent(fd_, EventLoop::Read,
- std::bind(&Device::drmEvent, this));
-
- return 0;
-}
-
-int Device::openCard()
-{
- const std::string dirName = "/dev/dri/";
- bool found = false;
- int ret;
-
- /*
- * Open the first DRM/KMS device beginning with /dev/dri/card. 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.
- */
- DIR *folder = opendir(dirName.c_str());
- if (!folder) {
- ret = -errno;
- std::cerr << "Failed to open " << dirName
- << " directory: " << strerror(-ret) << std::endl;
- return ret;
- }
-
- for (struct dirent *res; (res = readdir(folder));) {
- uint64_t cap;
-
- if (strncmp(res->d_name, "card", 4))
- continue;
-
- const std::string devName = dirName + res->d_name;
- fd_ = open(devName.c_str(), O_RDWR | O_CLOEXEC);
- if (fd_ < 0) {
- ret = -errno;
- std::cerr << "Failed to open DRM/KMS device " << devName << ": "
- << strerror(-ret) << std::endl;
- continue;
- }
-
- /*
- * Skip devices that don't support the modeset API, to avoid
- * selecting a DRM device corresponding to a GPU. There is no
- * modeset capability, but the kernel returns an error for most
- * caps if mode setting isn't support by the driver. The
- * DRM_CAP_DUMB_BUFFER capability is one of those, other would
- * do as well. The capability value itself isn't relevant.
- */
- ret = drmGetCap(fd_, DRM_CAP_DUMB_BUFFER, &cap);
- if (ret < 0) {
- drmClose(fd_);
- fd_ = -1;
- continue;
- }
-
- found = true;
- break;
- }
-
- closedir(folder);
-
- return found ? 0 : -ENOENT;
-}
-
-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,
- const std::array<uint32_t, 4> &strides)
-{
- std::unique_ptr<FrameBuffer> fb{ new FrameBuffer(this) };
-
- uint32_t handles[4] = {};
- uint32_t offsets[4] = {};
- int ret;
-
- const std::vector<libcamera::FrameBuffer::Plane> &planes = buffer.planes();
-
- unsigned int i = 0;
- for (const libcamera::FrameBuffer::Plane &plane : planes) {
- int fd = plane.fd.get();
- uint32_t handle;
-
- auto iter = fb->planes_.find(fd);
- if (iter == fb->planes_.end()) {
- ret = drmPrimeFDToHandle(fd_, plane.fd.get(), &handle);
- if (ret < 0) {
- ret = -errno;
- std::cerr
- << "Unable to import framebuffer dmabuf: "
- << strerror(-ret) << std::endl;
- return nullptr;
- }
-
- fb->planes_[fd] = { handle };
- } else {
- handle = iter->second.handle;
- }
-
- handles[i] = handle;
- offsets[i] = plane.offset;
- ++i;
- }
-
- ret = drmModeAddFB2(fd_, size.width, size.height, format.fourcc(), handles,
- strides.data(), 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 */