diff options
Diffstat (limited to 'src/cam/drm.cpp')
-rw-r--r-- | src/cam/drm.cpp | 717 |
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 */ |