1290 lines
42 KiB
C++
1290 lines
42 KiB
C++
/*
|
|
KWin - the KDE window manager
|
|
This file is part of the KDE project.
|
|
|
|
SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
#include "mock_drm.h"
|
|
|
|
#include <errno.h>
|
|
extern "C" {
|
|
#include <libxcvt/libxcvt.h>
|
|
}
|
|
#include <math.h>
|
|
|
|
#include <memory>
|
|
|
|
#include <QMap>
|
|
#include <QDebug>
|
|
|
|
// Mock impls
|
|
|
|
static QMap<int, MockGpu*> s_gpus;
|
|
|
|
static MockGpu *getGpu(int fd)
|
|
{
|
|
return s_gpus[fd];
|
|
}
|
|
|
|
MockGpu::MockGpu(int fd, const QString &devNode, int numCrtcs, int gammaSize)
|
|
: fd(fd)
|
|
, devNode(devNode)
|
|
{
|
|
s_gpus.insert(fd, this);
|
|
for (int i = 0; i < numCrtcs; i++) {
|
|
const auto &plane = std::make_shared<MockPlane>(this, PlaneType::Primary, i);
|
|
crtcs << std::make_shared<MockCrtc>(this, plane, i, gammaSize);
|
|
planes << plane;
|
|
}
|
|
deviceCaps.insert(MOCKDRM_DEVICE_CAP_ATOMIC, 1);
|
|
deviceCaps.insert(DRM_CAP_DUMB_BUFFER, 1);
|
|
deviceCaps.insert(DRM_CAP_PRIME, DRM_PRIME_CAP_IMPORT | DRM_PRIME_CAP_EXPORT);
|
|
deviceCaps.insert(DRM_CAP_ASYNC_PAGE_FLIP, 0);
|
|
deviceCaps.insert(DRM_CAP_ADDFB2_MODIFIERS, 1);
|
|
}
|
|
|
|
MockGpu::~MockGpu()
|
|
{
|
|
s_gpus.remove(fd);
|
|
}
|
|
|
|
MockPropertyBlob *MockGpu::getBlob(uint32_t id) const
|
|
{
|
|
auto it = std::find_if(propertyBlobs.begin(), propertyBlobs.end(), [id](const auto &propBlob) {
|
|
return propBlob->id == id;
|
|
});
|
|
return it == propertyBlobs.end() ? nullptr : it->get();
|
|
}
|
|
|
|
MockConnector *MockGpu::findConnector(uint32_t id) const
|
|
{
|
|
auto it = std::find_if(connectors.constBegin(), connectors.constEnd(), [id](const auto &c){return c->id == id;});
|
|
return it == connectors.constEnd() ? nullptr : (*it).get();
|
|
}
|
|
|
|
MockCrtc *MockGpu::findCrtc(uint32_t id) const
|
|
{
|
|
auto it = std::find_if(crtcs.constBegin(), crtcs.constEnd(), [id](const auto &c){return c->id == id;});
|
|
return it == crtcs.constEnd() ? nullptr : (*it).get();
|
|
}
|
|
|
|
MockPlane *MockGpu::findPlane(uint32_t id) const
|
|
{
|
|
auto it = std::find_if(planes.constBegin(), planes.constEnd(), [id](const auto &p){return p->id == id;});
|
|
return it == planes.constEnd() ? nullptr : (*it).get();
|
|
}
|
|
|
|
void MockGpu::flipPage(uint32_t crtcId)
|
|
{
|
|
auto crtc = findCrtc(crtcId);
|
|
Q_ASSERT(crtc);
|
|
for (const auto &plane : std::as_const(planes)) {
|
|
if (plane->getProp(QStringLiteral("CRTC_ID")) == crtc->id) {
|
|
plane->currentFb = plane->nextFb;
|
|
}
|
|
}
|
|
// TODO page flip event?
|
|
}
|
|
|
|
//
|
|
|
|
MockObject::MockObject(MockGpu *gpu)
|
|
: id(gpu->idCounter++)
|
|
, gpu(gpu)
|
|
{
|
|
gpu->objects << this;
|
|
}
|
|
|
|
MockObject::~MockObject()
|
|
{
|
|
gpu->objects.removeOne(this);
|
|
}
|
|
|
|
uint64_t MockObject::getProp(const QString &propName) const
|
|
{
|
|
for (const auto &prop : std::as_const(props)) {
|
|
if (prop.name == propName) {
|
|
return prop.value;
|
|
}
|
|
}
|
|
Q_UNREACHABLE();
|
|
}
|
|
|
|
void MockObject::setProp(const QString &propName, uint64_t value)
|
|
{
|
|
for (auto &prop : props) {
|
|
if (prop.name == propName) {
|
|
prop.value = value;
|
|
return;
|
|
}
|
|
}
|
|
Q_UNREACHABLE();
|
|
}
|
|
|
|
uint32_t MockObject::getPropId(const QString &propName) const
|
|
{
|
|
for (const auto &prop : std::as_const(props)) {
|
|
if (prop.name == propName) {
|
|
return prop.id;
|
|
}
|
|
}
|
|
Q_UNREACHABLE();
|
|
}
|
|
|
|
//
|
|
|
|
MockProperty::MockProperty(MockObject *obj, QString name, uint64_t initialValue, uint32_t flags, QList<QByteArray> enums)
|
|
: obj(obj)
|
|
, id(obj->gpu->idCounter++)
|
|
, flags(flags)
|
|
, name(name)
|
|
, value(initialValue)
|
|
, enums(enums)
|
|
{
|
|
qDebug("Added property %s with id %u to object %u", qPrintable(name), id, obj->id);
|
|
}
|
|
|
|
MockPropertyBlob::MockPropertyBlob(MockGpu *gpu, const void *d, size_t size)
|
|
: gpu(gpu)
|
|
, id(gpu->idCounter++)
|
|
, data(malloc(size))
|
|
, size(size)
|
|
{
|
|
memcpy(data, d, size);
|
|
}
|
|
|
|
MockPropertyBlob::~MockPropertyBlob()
|
|
{
|
|
free(data);
|
|
}
|
|
|
|
//
|
|
|
|
#define addProp(name, value, flags) props << MockProperty(this, QStringLiteral(name), value, flags)
|
|
|
|
MockConnector::MockConnector(MockGpu *gpu, bool nonDesktop)
|
|
: MockObject(gpu)
|
|
, connection(DRM_MODE_CONNECTED)
|
|
, type(DRM_MODE_CONNECTOR_DisplayPort)
|
|
, encoder(std::make_shared<MockEncoder>(gpu, 0xFF))
|
|
{
|
|
gpu->encoders << encoder;
|
|
addProp("CRTC_ID", 0, DRM_MODE_PROP_ATOMIC);
|
|
|
|
addProp("Subpixel", DRM_MODE_SUBPIXEL_UNKNOWN, DRM_MODE_PROP_IMMUTABLE);
|
|
addProp("non-desktop", nonDesktop, DRM_MODE_PROP_IMMUTABLE);
|
|
addProp("vrr_capable", 0, DRM_MODE_PROP_IMMUTABLE);
|
|
|
|
addProp("DPMS", DRM_MODE_DPMS_OFF, 0);
|
|
addProp("EDID", 0, DRM_MODE_PROP_BLOB | DRM_MODE_PROP_IMMUTABLE);
|
|
|
|
addMode(1920, 1080, 60.0);
|
|
}
|
|
|
|
void MockConnector::addMode(uint32_t width, uint32_t height, float refreshRate, bool preferred)
|
|
{
|
|
auto modeInfo = libxcvt_gen_mode_info(width, height, refreshRate, false, false);
|
|
|
|
drmModeModeInfo mode{
|
|
.clock = uint32_t(modeInfo->dot_clock),
|
|
.hdisplay = uint16_t(modeInfo->hdisplay),
|
|
.hsync_start = modeInfo->hsync_start,
|
|
.hsync_end = modeInfo->hsync_end,
|
|
.htotal = modeInfo->htotal,
|
|
.hskew = 0,
|
|
.vdisplay = uint16_t(modeInfo->vdisplay),
|
|
.vsync_start = modeInfo->vsync_start,
|
|
.vsync_end = modeInfo->vsync_end,
|
|
.vtotal = modeInfo->vtotal,
|
|
.vscan = 1,
|
|
.vrefresh = uint32_t(modeInfo->vrefresh),
|
|
.flags = modeInfo->mode_flags,
|
|
.type = DRM_MODE_TYPE_DRIVER,
|
|
};
|
|
if (preferred) {
|
|
mode.type |= DRM_MODE_TYPE_PREFERRED;
|
|
}
|
|
sprintf(mode.name, "%dx%d@%d", width, height, mode.vrefresh);
|
|
|
|
modes.push_back(mode);
|
|
free(modeInfo);
|
|
}
|
|
|
|
void MockConnector::setVrrCapable(bool cap)
|
|
{
|
|
auto &prop = *std::ranges::find_if(props, [](const auto &prop) {
|
|
return prop.name == "vrr_capable";
|
|
});
|
|
prop.value = cap ? 1 : 0;
|
|
}
|
|
|
|
//
|
|
|
|
MockCrtc::MockCrtc(MockGpu *gpu, const std::shared_ptr<MockPlane> &legacyPlane, int pipeIndex, int gamma_size)
|
|
: MockObject(gpu)
|
|
, pipeIndex(pipeIndex)
|
|
, gamma_size(gamma_size)
|
|
, legacyPlane(legacyPlane)
|
|
{
|
|
addProp("MODE_ID", 0, DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_BLOB);
|
|
addProp("ACTIVE", 0, DRM_MODE_PROP_ATOMIC);
|
|
addProp("GAMMA_LUT", 0, DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_BLOB);
|
|
addProp("GAMMA_LUT_SIZE", gamma_size, DRM_MODE_PROP_ATOMIC);
|
|
}
|
|
|
|
|
|
//
|
|
|
|
MockPlane::MockPlane(MockGpu *gpu, PlaneType type, int crtcIndex)
|
|
: MockObject(gpu)
|
|
, possibleCrtcs(1 << crtcIndex)
|
|
, type(type)
|
|
{
|
|
props << MockProperty(this, QStringLiteral("type"), static_cast<uint64_t>(type), DRM_MODE_PROP_IMMUTABLE | DRM_MODE_PROP_ENUM,
|
|
{QByteArrayLiteral("Primary"), QByteArrayLiteral("Overlay"), QByteArrayLiteral("Cursor")});
|
|
addProp("FB_ID", 0, DRM_MODE_PROP_ATOMIC);
|
|
addProp("CRTC_ID", 0, DRM_MODE_PROP_ATOMIC);
|
|
addProp("CRTC_X", 0, DRM_MODE_PROP_ATOMIC);
|
|
addProp("CRTC_Y", 0, DRM_MODE_PROP_ATOMIC);
|
|
addProp("CRTC_W", 0, DRM_MODE_PROP_ATOMIC);
|
|
addProp("CRTC_H", 0, DRM_MODE_PROP_ATOMIC);
|
|
addProp("SRC_X", 0, DRM_MODE_PROP_ATOMIC);
|
|
addProp("SRC_Y", 0, DRM_MODE_PROP_ATOMIC);
|
|
addProp("SRC_W", 0, DRM_MODE_PROP_ATOMIC);
|
|
addProp("SRC_H", 0, DRM_MODE_PROP_ATOMIC);
|
|
}
|
|
|
|
//
|
|
|
|
MockEncoder::MockEncoder(MockGpu* gpu, uint32_t possible_crtcs)
|
|
: MockObject(gpu)
|
|
, possible_crtcs(possible_crtcs)
|
|
{
|
|
}
|
|
|
|
|
|
//
|
|
|
|
MockFb::MockFb(MockGpu *gpu, uint32_t width, uint32_t height)
|
|
: id(gpu->idCounter++)
|
|
, width(width)
|
|
, height(height)
|
|
, gpu(gpu)
|
|
{
|
|
gpu->fbs << this;
|
|
}
|
|
|
|
MockFb::~MockFb()
|
|
{
|
|
gpu->fbs.removeOne(this);
|
|
}
|
|
|
|
// drm functions
|
|
|
|
#define GPU(fd, error) \
|
|
auto gpu = getGpu(fd); \
|
|
if (!gpu) { \
|
|
qWarning("invalid fd %d", fd); \
|
|
errno = EINVAL; \
|
|
return error; \
|
|
} \
|
|
std::scoped_lock lock(gpu->m_mutex);
|
|
|
|
drmVersionPtr drmGetVersion(int fd)
|
|
{
|
|
GPU(fd, nullptr);
|
|
drmVersionPtr ptr = new drmVersion;
|
|
ptr->name = new char[gpu->name.size() + 1];
|
|
strcpy(ptr->name, gpu->name.data());
|
|
return ptr;
|
|
}
|
|
|
|
void drmFreeVersion(drmVersionPtr ptr)
|
|
{
|
|
Q_ASSERT(ptr);
|
|
delete[] ptr->name;
|
|
delete ptr;
|
|
}
|
|
|
|
int drmSetClientCap(int fd, uint64_t capability, uint64_t value)
|
|
{
|
|
GPU(fd, -EINVAL);
|
|
if (capability == DRM_CLIENT_CAP_ATOMIC) {
|
|
if (!gpu->deviceCaps[MOCKDRM_DEVICE_CAP_ATOMIC]) {
|
|
return -(errno = ENOTSUP);
|
|
}
|
|
qDebug("Setting DRM_CLIENT_CAP_ATOMIC to %lu", value);
|
|
}
|
|
gpu->clientCaps.insert(capability, value);
|
|
return 0;
|
|
}
|
|
|
|
int drmGetCap(int fd, uint64_t capability, uint64_t *value)
|
|
{
|
|
GPU(fd, -EINVAL);
|
|
if (gpu->deviceCaps.contains(capability)) {
|
|
*value = gpu->deviceCaps[capability];
|
|
return 0;
|
|
}
|
|
qDebug("Could not find capability %lu", capability);
|
|
return -(errno = EINVAL);
|
|
}
|
|
|
|
int drmHandleEvent(int fd, drmEventContextPtr evctx)
|
|
{
|
|
GPU(fd, -EINVAL);
|
|
return -(errno = ENOTSUP);
|
|
}
|
|
|
|
int drmIoctl(int fd, unsigned long request, void *arg)
|
|
{
|
|
if (request == DRM_IOCTL_PRIME_FD_TO_HANDLE) {
|
|
GPU(fd, -EINVAL);
|
|
auto args = static_cast<drm_prime_handle *>(arg);
|
|
args->handle = 42; // just pass a dummy value so the request doesn't fail
|
|
return 0;
|
|
} else if (request == DRM_IOCTL_PRIME_HANDLE_TO_FD) {
|
|
return -(errno = ENOTSUP);
|
|
} else if (request == DRM_IOCTL_GEM_CLOSE) {
|
|
GPU(fd, -EINVAL);
|
|
return 0;
|
|
} else if (request == DRM_IOCTL_MODE_ATOMIC) {
|
|
const auto args = static_cast<drm_mode_atomic *>(arg);
|
|
auto req = drmModeAtomicAlloc();
|
|
const uint32_t *const objects = reinterpret_cast<const uint32_t *>(args->objs_ptr);
|
|
const uint32_t *const propsCounts = reinterpret_cast<const uint32_t *>(args->count_props_ptr);
|
|
const uint32_t *const props = reinterpret_cast<const uint32_t *>(args->props_ptr);
|
|
const uint64_t *const values = reinterpret_cast<const uint64_t *>(args->prop_values_ptr);
|
|
uint32_t propIndex = 0;
|
|
for (uint32_t objIndex = 0; objIndex < args->count_objs; objIndex++) {
|
|
const uint32_t objectId = objects[objIndex];
|
|
const uint32_t count = propsCounts[objIndex];
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
drmModeAtomicAddProperty(req, objectId, props[propIndex + i], values[propIndex + i]);
|
|
}
|
|
propIndex += count;
|
|
}
|
|
int ret = drmModeAtomicCommit(fd, req, args->flags, reinterpret_cast<void *>(args->user_data));
|
|
drmModeAtomicFree(req);
|
|
return ret;
|
|
} else if (request == DRM_IOCTL_MODE_RMFB) {
|
|
drmModeRmFB(fd, *static_cast<const uint32_t *>(arg));
|
|
}
|
|
return -(errno = ENOTSUP);
|
|
}
|
|
|
|
drmModeResPtr drmModeGetResources(int fd)
|
|
{
|
|
GPU(fd, nullptr)
|
|
drmModeResPtr res = new drmModeRes;
|
|
|
|
res->count_connectors = gpu->connectors.count();
|
|
res->connectors = res->count_connectors ? new uint32_t[res->count_connectors] : nullptr;
|
|
int i = 0;
|
|
for (const auto &conn : std::as_const(gpu->connectors)) {
|
|
res->connectors[i++] = conn->id;
|
|
}
|
|
|
|
res->count_encoders = gpu->encoders.count();
|
|
res->encoders = res->count_encoders ? new uint32_t[res->count_encoders] : nullptr;
|
|
i = 0;
|
|
for (const auto &enc : std::as_const(gpu->encoders)) {
|
|
res->encoders[i++] = enc->id;
|
|
}
|
|
|
|
res->count_crtcs = gpu->crtcs.count();
|
|
res->crtcs = res->count_crtcs ? new uint32_t[res->count_crtcs] : nullptr;
|
|
i = 0;
|
|
for (const auto &crtc : std::as_const(gpu->crtcs)) {
|
|
res->crtcs[i++] = crtc->id;
|
|
}
|
|
|
|
res->count_fbs = gpu->fbs.count();
|
|
res->fbs = res->count_fbs ? new uint32_t[res->count_fbs] : nullptr;
|
|
i = 0;
|
|
for (const auto &fb : std::as_const(gpu->fbs)) {
|
|
res->fbs[i++] = fb->id;
|
|
}
|
|
|
|
res->min_width = 0;
|
|
res->min_height = 0;
|
|
res->max_width = 2 << 14;
|
|
res->max_height = 2 << 14;
|
|
|
|
gpu->resPtrs << res;
|
|
return res;
|
|
}
|
|
|
|
int drmModeAddFB(int fd, uint32_t width, uint32_t height, uint8_t depth,
|
|
uint8_t bpp, uint32_t pitch, uint32_t bo_handle,
|
|
uint32_t *buf_id)
|
|
{
|
|
GPU(fd, EINVAL)
|
|
auto fb = new MockFb(gpu, width, height);
|
|
*buf_id = fb->id;
|
|
return 0;
|
|
}
|
|
|
|
int drmModeAddFB2(int fd, uint32_t width, uint32_t height,
|
|
uint32_t pixel_format, const uint32_t bo_handles[4],
|
|
const uint32_t pitches[4], const uint32_t offsets[4],
|
|
uint32_t *buf_id, uint32_t flags)
|
|
{
|
|
GPU(fd, EINVAL)
|
|
auto fb = new MockFb(gpu, width, height);
|
|
*buf_id = fb->id;
|
|
return 0;
|
|
}
|
|
|
|
int drmModeAddFB2WithModifiers(int fd, uint32_t width, uint32_t height,
|
|
uint32_t pixel_format, const uint32_t bo_handles[4],
|
|
const uint32_t pitches[4], const uint32_t offsets[4],
|
|
const uint64_t modifier[4], uint32_t *buf_id,
|
|
uint32_t flags)
|
|
{
|
|
GPU(fd, EINVAL)
|
|
if (!gpu->deviceCaps.contains(DRM_CAP_ADDFB2_MODIFIERS)) {
|
|
return -(errno = ENOTSUP);
|
|
}
|
|
auto fb = new MockFb(gpu, width, height);
|
|
*buf_id = fb->id;
|
|
return 0;
|
|
}
|
|
|
|
int drmModeRmFB(int fd, uint32_t bufferId)
|
|
{
|
|
GPU(fd, EINVAL)
|
|
auto it = std::find_if(gpu->fbs.begin(), gpu->fbs.end(), [bufferId](const auto &fb){return fb->id == bufferId;});
|
|
if (it == gpu->fbs.end()) {
|
|
qWarning("invalid bufferId %u passed to drmModeRmFB", bufferId);
|
|
return EINVAL;
|
|
} else {
|
|
auto fb = *it;
|
|
gpu->fbs.erase(it);
|
|
for (const auto &plane : std::as_const(gpu->planes)) {
|
|
if (plane->nextFb == fb) {
|
|
plane->nextFb = nullptr;
|
|
}
|
|
if (plane->currentFb == fb) {
|
|
qWarning("current fb %u of plane %u got removed. Deactivating plane", bufferId, plane->id);
|
|
plane->setProp(QStringLiteral("CRTC_ID"), 0);
|
|
plane->setProp(QStringLiteral("FB_ID"), 0);
|
|
plane->currentFb = nullptr;
|
|
|
|
auto crtc = gpu->findCrtc(plane->getProp(QStringLiteral("CRTC_ID")));
|
|
Q_ASSERT(crtc);
|
|
crtc->setProp(QStringLiteral("ACTIVE"), 0);
|
|
qWarning("deactvating crtc %u", crtc->id);
|
|
|
|
for (const auto &conn : std::as_const(gpu->connectors)) {
|
|
if (conn->getProp(QStringLiteral("CRTC_ID")) == crtc->id) {
|
|
conn->setProp(QStringLiteral("CRTC_ID"), 0);
|
|
qWarning("deactvating connector %u", conn->id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
delete fb;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
drmModeCrtcPtr drmModeGetCrtc(int fd, uint32_t crtcId)
|
|
{
|
|
GPU(fd, nullptr);
|
|
if (auto crtc = gpu->findCrtc(crtcId)) {
|
|
drmModeCrtcPtr c = new drmModeCrtc;
|
|
c->crtc_id = crtcId;
|
|
c->buffer_id = crtc->currentFb ? crtc->currentFb->id : 0;
|
|
c->gamma_size = crtc->gamma_size;
|
|
c->mode_valid = crtc->modeValid;
|
|
c->mode = crtc->mode;
|
|
c->x = 0;
|
|
c->y = 0;
|
|
c->width = crtc->mode.hdisplay;
|
|
c->height = crtc->mode.vdisplay;
|
|
gpu->drmCrtcs << c;
|
|
return c;
|
|
} else {
|
|
qWarning("invalid crtcId %u passed to drmModeGetCrtc", crtcId);
|
|
errno = EINVAL;
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId,
|
|
uint32_t x, uint32_t y, uint32_t *connectors, int count,
|
|
drmModeModeInfoPtr mode)
|
|
{
|
|
GPU(fd, -EINVAL);
|
|
auto crtc = gpu->findCrtc(crtcId);
|
|
if (!crtc) {
|
|
qWarning("invalid crtcId %u passed to drmModeSetCrtc", crtcId);
|
|
return -(errno = EINVAL);
|
|
}
|
|
auto oldModeBlob = crtc->getProp(QStringLiteral("MODE_ID"));
|
|
uint32_t modeBlob = 0;
|
|
if (mode) {
|
|
drmModeCreatePropertyBlob(fd, mode, sizeof(drmModeModeInfo), &modeBlob);
|
|
}
|
|
|
|
auto req = drmModeAtomicAlloc();
|
|
req->legacyEmulation = true;
|
|
drmModeAtomicAddProperty(req, crtcId, crtc->getPropId(QStringLiteral("MODE_ID")), modeBlob);
|
|
drmModeAtomicAddProperty(req, crtcId, crtc->getPropId(QStringLiteral("ACTIVE")), modeBlob && count);
|
|
QList<uint32_t> conns;
|
|
for (int i = 0; i < count; i++) {
|
|
conns << connectors[i];
|
|
}
|
|
for (const auto &conn : std::as_const(gpu->connectors)) {
|
|
if (conns.contains(conn->id)) {
|
|
drmModeAtomicAddProperty(req, conn->id, conn->getPropId(QStringLiteral("CRTC_ID")), modeBlob ? crtc->id : 0);
|
|
conns.removeOne(conn->id);
|
|
} else if (conn->getProp(QStringLiteral("CRTC_ID")) == crtc->id) {
|
|
drmModeAtomicAddProperty(req, conn->id, conn->getPropId(QStringLiteral("CRTC_ID")), 0);
|
|
}
|
|
}
|
|
if (!conns.isEmpty()) {
|
|
for (const auto &c : std::as_const(conns)) {
|
|
qWarning("invalid connector %u passed to drmModeSetCrtc", c);
|
|
}
|
|
drmModeAtomicFree(req);
|
|
return -(errno = EINVAL);
|
|
}
|
|
drmModeAtomicAddProperty(req, crtc->legacyPlane->id, crtc->legacyPlane->getPropId(QStringLiteral("CRTC_ID")), modeBlob && count ? crtc->id : 0);
|
|
drmModeAtomicAddProperty(req, crtc->legacyPlane->id, crtc->legacyPlane->getPropId(QStringLiteral("CRTC_X")), x);
|
|
drmModeAtomicAddProperty(req, crtc->legacyPlane->id, crtc->legacyPlane->getPropId(QStringLiteral("CRTC_Y")), y);
|
|
drmModeAtomicAddProperty(req, crtc->legacyPlane->id, crtc->legacyPlane->getPropId(QStringLiteral("CRTC_W")), mode->hdisplay - x);
|
|
drmModeAtomicAddProperty(req, crtc->legacyPlane->id, crtc->legacyPlane->getPropId(QStringLiteral("CRTC_H")), mode->vdisplay - y);
|
|
drmModeAtomicAddProperty(req, crtc->legacyPlane->id, crtc->legacyPlane->getPropId(QStringLiteral("FB_ID")), bufferId);
|
|
int result = drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_ALLOW_MODESET, nullptr);
|
|
drmModeAtomicFree(req);
|
|
if (result == 0) {
|
|
drmModeDestroyPropertyBlob(fd, oldModeBlob);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int drmModeSetCursor(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height)
|
|
{
|
|
GPU(fd, -EINVAL);
|
|
if (auto crtc = gpu->findCrtc(crtcId)) {
|
|
crtc->cursorRect.setSize(QSize(width, height));
|
|
return 0;
|
|
} else {
|
|
qWarning("invalid crtcId %u passed to drmModeSetCursor", crtcId);
|
|
return -(errno = EINVAL);
|
|
}
|
|
}
|
|
|
|
int drmModeSetCursor2(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height, int32_t hot_x, int32_t hot_y)
|
|
{
|
|
GPU(fd, -EINVAL);
|
|
return -(errno = ENOTSUP);
|
|
}
|
|
|
|
int drmModeMoveCursor(int fd, uint32_t crtcId, int x, int y)
|
|
{
|
|
GPU(fd, -EINVAL);
|
|
if (auto crtc = gpu->findCrtc(crtcId)) {
|
|
crtc->cursorRect.moveTo(x, y);
|
|
return 0;
|
|
} else {
|
|
qWarning("invalid crtcId %u passed to drmModeMoveCursor", crtcId);
|
|
return -(errno = EINVAL);
|
|
}
|
|
}
|
|
|
|
drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id)
|
|
{
|
|
GPU(fd, nullptr);
|
|
auto it = std::find_if(gpu->encoders.constBegin(), gpu->encoders.constEnd(), [encoder_id](const auto &e){return e->id == encoder_id;});
|
|
if (it == gpu->encoders.constEnd()) {
|
|
qWarning("invalid encoder_id %u passed to drmModeGetEncoder", encoder_id);
|
|
errno = EINVAL;
|
|
return nullptr;
|
|
} else {
|
|
auto encoder = *it;
|
|
drmModeEncoderPtr enc = new drmModeEncoder;
|
|
enc->encoder_id = encoder_id;
|
|
enc->crtc_id = encoder->crtc ? encoder->crtc->id : 0;
|
|
enc->encoder_type = 0;
|
|
enc->possible_crtcs = encoder->possible_crtcs;
|
|
enc->possible_clones = encoder->possible_clones;
|
|
|
|
gpu->drmEncoders << enc;
|
|
return enc;
|
|
}
|
|
}
|
|
|
|
// Instance ID of (some) specific connector type, incremented
|
|
// for each new connector (of any type) being created.
|
|
// There are no particular guarantees on the _stability_ of
|
|
// connector type "instance IDs" issued by the kernel,
|
|
// so simply giving each (new) connector a fresh ID is
|
|
// acceptable.
|
|
static std::atomic<int> autoIncrementedConnectorId{};
|
|
|
|
drmModeConnectorPtr drmModeGetConnector(int fd, uint32_t connectorId)
|
|
{
|
|
GPU(fd, nullptr);
|
|
if (auto conn = gpu->findConnector(connectorId)) {
|
|
drmModeConnectorPtr c = new drmModeConnector{};
|
|
c->connector_id = conn->id;
|
|
c->connection = conn->connection;
|
|
|
|
c->connector_type = conn->type;
|
|
c->connector_type_id = autoIncrementedConnectorId++;
|
|
|
|
c->encoder_id = conn->encoder ? conn->encoder->id : 0;
|
|
c->count_encoders = conn->encoder ? 1 : 0;
|
|
c->encoders = c->count_encoders ? new uint32_t[1] : nullptr;
|
|
if (c->encoders) {
|
|
c->encoders[0] = conn->encoder->id;
|
|
}
|
|
c->count_modes = conn->modes.count();
|
|
c->modes = c->count_modes ? new drmModeModeInfo[c->count_modes] : nullptr;
|
|
for (int i = 0; i < c->count_modes; i++) {
|
|
c->modes[i] = conn->modes[i];
|
|
}
|
|
c->mmHeight = 900;
|
|
c->mmWidth = 1600;
|
|
c->subpixel = DRM_MODE_SUBPIXEL_HORIZONTAL_RGB;
|
|
|
|
// these are not used nor will they be
|
|
c->count_props = -1;
|
|
c->props = nullptr;
|
|
c->prop_values = nullptr;
|
|
|
|
gpu->drmConnectors << c;
|
|
return c;
|
|
} else {
|
|
qWarning("invalid connectorId %u passed to drmModeGetConnector", connectorId);
|
|
errno = EINVAL;
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
drmModeConnectorPtr drmModeGetConnectorCurrent(int fd, uint32_t connector_id)
|
|
{
|
|
return drmModeGetConnector(fd, connector_id);
|
|
}
|
|
|
|
int drmModeCrtcSetGamma(int fd, uint32_t crtc_id, uint32_t size, uint16_t *red, uint16_t *green, uint16_t *blue)
|
|
{
|
|
return -(errno = ENOTSUP);
|
|
}
|
|
|
|
int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id, uint32_t flags, void *user_data)
|
|
{
|
|
GPU(fd, -EINVAL);
|
|
auto crtc = gpu->findCrtc(crtc_id);
|
|
if (!crtc) {
|
|
qWarning("invalid crtc_id %u passed to drmModePageFlip", crtc_id);
|
|
return -(errno = EINVAL);
|
|
}
|
|
auto req = drmModeAtomicAlloc();
|
|
req->legacyEmulation = true;
|
|
drmModeAtomicAddProperty(req, crtc->legacyPlane->id, crtc->legacyPlane->getPropId(QStringLiteral("FB_ID")), fb_id);
|
|
int result = drmModeAtomicCommit(fd, req, flags, user_data);
|
|
drmModeAtomicFree(req);
|
|
return result;
|
|
}
|
|
|
|
|
|
drmModePlaneResPtr drmModeGetPlaneResources(int fd)
|
|
{
|
|
GPU(fd, nullptr);
|
|
drmModePlaneResPtr res = new drmModePlaneRes;
|
|
res->count_planes = gpu->planes.count();
|
|
res->planes = res->count_planes ? new uint32_t[res->count_planes] : nullptr;
|
|
for (uint i = 0; i < res->count_planes; i++) {
|
|
res->planes[i] = gpu->planes[i]->id;
|
|
}
|
|
gpu->drmPlaneRes << res;
|
|
return res;
|
|
}
|
|
|
|
drmModePlanePtr drmModeGetPlane(int fd, uint32_t plane_id)
|
|
{
|
|
GPU(fd, nullptr);
|
|
if (auto plane = gpu->findPlane(plane_id)) {
|
|
drmModePlanePtr p = new drmModePlane;
|
|
p->plane_id = plane_id;
|
|
p->crtc_id = plane->getProp(QStringLiteral("CRTC_ID"));
|
|
p->crtc_x = plane->getProp(QStringLiteral("CRTC_X"));
|
|
p->crtc_y = plane->getProp(QStringLiteral("CRTC_Y"));
|
|
p->fb_id = plane->getProp(QStringLiteral("FB_ID"));
|
|
p->x = plane->getProp(QStringLiteral("SRC_X"));
|
|
p->y = plane->getProp(QStringLiteral("SRC_Y"));
|
|
p->possible_crtcs = plane->possibleCrtcs;
|
|
|
|
// unused atm:
|
|
p->count_formats = 0;
|
|
p->formats = nullptr;
|
|
p->gamma_size = 0;
|
|
|
|
gpu->drmPlanes << p;
|
|
return p;
|
|
} else {
|
|
qWarning("invalid plane_id %u passed to drmModeGetPlane", plane_id);
|
|
errno = EINVAL;
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
drmModePropertyPtr drmModeGetProperty(int fd, uint32_t propertyId)
|
|
{
|
|
GPU(fd, nullptr);
|
|
for (const auto &obj : std::as_const(gpu->objects)) {
|
|
for (auto &prop : std::as_const(obj->props)) {
|
|
if (prop.id == propertyId) {
|
|
drmModePropertyPtr p = new drmModePropertyRes;
|
|
p->prop_id = prop.id;
|
|
p->flags = prop.flags;
|
|
auto arr = prop.name.toLocal8Bit();
|
|
strcpy(p->name, arr.constData());
|
|
|
|
p->count_blobs = prop.flags & DRM_MODE_PROP_BLOB ? 1 : 0;
|
|
if (p->count_blobs) {
|
|
p->blob_ids = new uint32_t[1];
|
|
p->blob_ids[0] = prop.value;
|
|
} else {
|
|
p->blob_ids = nullptr;
|
|
}
|
|
|
|
p->count_enums = prop.enums.count();
|
|
p->enums = new drm_mode_property_enum[p->count_enums];
|
|
for (int i = 0; i < p->count_enums; i++) {
|
|
strcpy(p->enums[i].name, prop.enums[i].constData());
|
|
p->enums[i].value = i;
|
|
}
|
|
|
|
p->count_values = 1;
|
|
p->values = new uint64_t[1];
|
|
p->values[0] = prop.value;
|
|
|
|
gpu->drmProps << p;
|
|
return p;
|
|
}
|
|
}
|
|
}
|
|
qWarning("invalid propertyId %u passed to drmModeGetProperty", propertyId);
|
|
errno = EINVAL;
|
|
return nullptr;
|
|
}
|
|
|
|
void drmModeFreeProperty(drmModePropertyPtr ptr)
|
|
{
|
|
if (!ptr) {
|
|
return;
|
|
}
|
|
for (const auto &gpu : std::as_const(s_gpus)) {
|
|
if (gpu->drmProps.removeOne(ptr)) {
|
|
delete[] ptr->values;
|
|
delete[] ptr->blob_ids;
|
|
delete[] ptr->enums;
|
|
delete ptr;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
drmModePropertyBlobPtr drmModeGetPropertyBlob(int fd, uint32_t blob_id)
|
|
{
|
|
GPU(fd, nullptr);
|
|
if (blob_id == 0) {
|
|
return nullptr;
|
|
}
|
|
auto it = std::find_if(gpu->propertyBlobs.begin(), gpu->propertyBlobs.end(), [blob_id](const auto &blob) {
|
|
return blob->id == blob_id;
|
|
});
|
|
if (it == gpu->propertyBlobs.end()) {
|
|
qWarning("invalid blob_id %u passed to drmModeGetPropertyBlob", blob_id);
|
|
errno = EINVAL;
|
|
return nullptr;
|
|
} else {
|
|
auto blob = new drmModePropertyBlobRes;
|
|
blob->id = (*it)->id;
|
|
blob->length = (*it)->size;
|
|
blob->data = malloc(blob->length);
|
|
memcpy(blob->data, (*it)->data, blob->length);
|
|
return blob;
|
|
}
|
|
}
|
|
|
|
void drmModeFreePropertyBlob(drmModePropertyBlobPtr ptr)
|
|
{
|
|
if (!ptr) {
|
|
return;
|
|
}
|
|
for (const auto &gpu : std::as_const(s_gpus)) {
|
|
if (gpu->drmPropertyBlobs.removeOne(ptr)) {
|
|
free(ptr->data);
|
|
delete ptr;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
int drmModeConnectorSetProperty(int fd, uint32_t connector_id, uint32_t property_id, uint64_t value)
|
|
{
|
|
return drmModeObjectSetProperty(fd, connector_id, DRM_MODE_OBJECT_CONNECTOR, property_id, value);
|
|
}
|
|
|
|
static uint32_t getType(MockObject *obj)
|
|
{
|
|
if (dynamic_cast<MockConnector*>(obj)) {
|
|
return DRM_MODE_OBJECT_CONNECTOR;
|
|
} else if (dynamic_cast<MockCrtc*>(obj)) {
|
|
return DRM_MODE_OBJECT_CRTC;
|
|
} else if (dynamic_cast<MockPlane*>(obj)) {
|
|
return DRM_MODE_OBJECT_PLANE;
|
|
} else {
|
|
return DRM_MODE_OBJECT_ANY;
|
|
}
|
|
}
|
|
|
|
drmModeObjectPropertiesPtr drmModeObjectGetProperties(int fd, uint32_t object_id, uint32_t object_type)
|
|
{
|
|
GPU(fd, nullptr);
|
|
auto it = std::find_if(gpu->objects.constBegin(), gpu->objects.constEnd(), [object_id](const auto &obj){return obj->id == object_id;});
|
|
if (it == gpu->objects.constEnd()) {
|
|
qWarning("invalid object_id %u passed to drmModeObjectGetProperties", object_id);
|
|
errno = EINVAL;
|
|
return nullptr;
|
|
} else {
|
|
auto obj = *it;
|
|
if (auto type = getType(obj); type != object_type) {
|
|
qWarning("wrong object_type %u passed to drmModeObjectGetProperties for object %u with type %u", object_type, object_id, type);
|
|
errno = EINVAL;
|
|
return nullptr;
|
|
}
|
|
QList<MockProperty> props;
|
|
bool deviceAtomic = gpu->clientCaps.contains(DRM_CLIENT_CAP_ATOMIC) && gpu->clientCaps[DRM_CLIENT_CAP_ATOMIC];
|
|
for (const auto &prop : std::as_const(obj->props)) {
|
|
if (deviceAtomic || !(prop.flags & DRM_MODE_PROP_ATOMIC)) {
|
|
props << prop;
|
|
}
|
|
}
|
|
drmModeObjectPropertiesPtr p = new drmModeObjectProperties;
|
|
p->count_props = props.count();
|
|
p->props = new uint32_t[p->count_props];
|
|
p->prop_values = new uint64_t[p->count_props];
|
|
int i = 0;
|
|
for (const auto &prop : std::as_const(props)) {
|
|
p->props[i] = prop.id;
|
|
p->prop_values[i] = prop.value;
|
|
i++;
|
|
}
|
|
gpu->drmObjectProperties << p;
|
|
return p;
|
|
}
|
|
}
|
|
|
|
void drmModeFreeObjectProperties(drmModeObjectPropertiesPtr ptr)
|
|
{
|
|
for (const auto &gpu : std::as_const(s_gpus)) {
|
|
if (gpu->drmObjectProperties.removeOne(ptr)) {
|
|
delete[] ptr->props;
|
|
delete[] ptr->prop_values;
|
|
delete ptr;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
int drmModeObjectSetProperty(int fd, uint32_t object_id, uint32_t object_type, uint32_t property_id, uint64_t value)
|
|
{
|
|
GPU(fd, -EINVAL);
|
|
auto it = std::find_if(gpu->objects.constBegin(), gpu->objects.constEnd(), [object_id](const auto &obj){return obj->id == object_id;});
|
|
if (it == gpu->objects.constEnd()) {
|
|
qWarning("invalid object_id %u passed to drmModeObjectSetProperty", object_id);
|
|
return -(errno = EINVAL);
|
|
} else {
|
|
auto obj = *it;
|
|
if (auto type = getType(obj); type != object_type) {
|
|
qWarning("wrong object_type %u passed to drmModeObjectSetProperty for object %u with type %u", object_type, object_id, type);
|
|
return -(errno = EINVAL);
|
|
}
|
|
auto req = drmModeAtomicAlloc();
|
|
req->legacyEmulation = true;
|
|
drmModeAtomicAddProperty(req, object_id, property_id, value);
|
|
int result = drmModeAtomicCommit(fd, req, 0, nullptr);
|
|
drmModeAtomicFree(req);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
static QList<drmModeAtomicReqPtr> s_atomicReqs;
|
|
|
|
drmModeAtomicReqPtr drmModeAtomicAlloc(void)
|
|
{
|
|
auto req = new drmModeAtomicReq;
|
|
s_atomicReqs << req;
|
|
return req;
|
|
}
|
|
|
|
void drmModeAtomicFree(drmModeAtomicReqPtr req)
|
|
{
|
|
s_atomicReqs.removeOne(req);
|
|
delete req;
|
|
}
|
|
|
|
int drmModeAtomicAddProperty(drmModeAtomicReqPtr req, uint32_t object_id, uint32_t property_id, uint64_t value)
|
|
{
|
|
if (!req) {
|
|
return -(errno = EINVAL);
|
|
}
|
|
Prop p;
|
|
p.obj = object_id;
|
|
p.prop = property_id;
|
|
p.value = value;
|
|
req->props << p;
|
|
return req->props.count();
|
|
}
|
|
|
|
static bool checkIfEqual(const drmModeModeInfo &one, const drmModeModeInfo &two)
|
|
{
|
|
return one.clock == two.clock
|
|
&& one.hdisplay == two.hdisplay
|
|
&& one.hsync_start == two.hsync_start
|
|
&& one.hsync_end == two.hsync_end
|
|
&& one.htotal == two.htotal
|
|
&& one.hskew == two.hskew
|
|
&& one.vdisplay == two.vdisplay
|
|
&& one.vsync_start == two.vsync_start
|
|
&& one.vsync_end == two.vsync_end
|
|
&& one.vtotal == two.vtotal
|
|
&& one.vscan == two.vscan
|
|
&& one.vrefresh == two.vrefresh;
|
|
}
|
|
|
|
int drmModeAtomicCommit(int fd, drmModeAtomicReqPtr req, uint32_t flags, void *user_data)
|
|
{
|
|
GPU(fd, -EINVAL);
|
|
if (!req->legacyEmulation && (!gpu->clientCaps.contains(DRM_CLIENT_CAP_ATOMIC) || !gpu->clientCaps[DRM_CLIENT_CAP_ATOMIC])) {
|
|
qWarning("drmModeAtomicCommit requires the atomic capability");
|
|
return -(errno = EINVAL);
|
|
}
|
|
|
|
// verify flags
|
|
if ((flags & DRM_MODE_ATOMIC_NONBLOCK) && (flags & DRM_MODE_ATOMIC_ALLOW_MODESET)) {
|
|
qWarning() << "NONBLOCK and MODESET are not allowed together";
|
|
return -(errno = EINVAL);
|
|
} else if ((flags & DRM_MODE_ATOMIC_TEST_ONLY) && (flags & DRM_MODE_PAGE_FLIP_EVENT)) {
|
|
qWarning() << "TEST_ONLY and PAGE_FLIP_EVENT are not allowed together";
|
|
return -(errno = EINVAL);
|
|
} else if (flags & DRM_MODE_PAGE_FLIP_ASYNC) {
|
|
qWarning() << "PAGE_FLIP_ASYNC is currently not supported with AMS";
|
|
return -(errno = EINVAL);
|
|
}
|
|
|
|
QList<MockConnector> connCopies;
|
|
for (const auto &conn : std::as_const(gpu->connectors)) {
|
|
connCopies << *conn;
|
|
}
|
|
QList<MockCrtc> crtcCopies;
|
|
for (const auto &crtc : std::as_const(gpu->crtcs)) {
|
|
crtcCopies << *crtc;
|
|
}
|
|
QList<MockPlane> planeCopies;
|
|
for (const auto &plane : std::as_const(gpu->planes)) {
|
|
planeCopies << *plane;
|
|
}
|
|
|
|
QList<MockObject *> objects;
|
|
for (int i = 0; i < connCopies.count(); i++) {
|
|
objects << &connCopies[i];
|
|
}
|
|
for (int i = 0; i < crtcCopies.count(); i++) {
|
|
objects << &crtcCopies[i];
|
|
}
|
|
for (int i = 0; i < planeCopies.count(); i++) {
|
|
objects << &planeCopies[i];
|
|
}
|
|
|
|
// apply changes to the copies
|
|
for (int i = 0; i < req->props.count(); i++) {
|
|
auto p = req->props[i];
|
|
auto it = std::find_if(objects.constBegin(), objects.constEnd(), [p](const auto &obj){return obj->id == p.obj;});
|
|
if (it == objects.constEnd()) {
|
|
qWarning("Object %u in atomic request not found!", p.obj);
|
|
return -(errno = EINVAL);
|
|
}
|
|
auto &obj = *it;
|
|
if (obj->id == p.obj) {
|
|
auto prop = std::find_if(obj->props.begin(), obj->props.end(), [p](const auto &prop){return prop.id == p.prop;});
|
|
if (prop == obj->props.end()) {
|
|
qWarning("Property %u in atomic request for object %u not found!", p.prop, p.obj);
|
|
return -(errno = EINVAL);
|
|
}
|
|
if (prop->value != p.value) {
|
|
if (!(flags & DRM_MODE_ATOMIC_ALLOW_MODESET) && (prop->name == QStringLiteral("CRTC_ID") || prop->name == QStringLiteral("ACTIVE"))) {
|
|
qWarning("Atomic request without DRM_MODE_ATOMIC_ALLOW_MODESET tries to do a modeset with object %u", obj->id);
|
|
return -(errno = EINVAL);
|
|
}
|
|
if (prop->flags & DRM_MODE_PROP_BLOB) {
|
|
auto blobExists = gpu->getBlob(p.value) != nullptr;
|
|
if (blobExists != (p.value > 0)) {
|
|
qWarning("Atomic request tries to set property %s on obj %u to invalid blob id %lu", qPrintable(prop->name), obj->id, p.value);
|
|
return -(errno = EINVAL);
|
|
}
|
|
}
|
|
prop->value = p.value;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check if the desired changes are allowed
|
|
struct Pipeline {
|
|
MockCrtc *crtc;
|
|
QList<MockConnector *> conns;
|
|
MockPlane *primaryPlane = nullptr;
|
|
};
|
|
QList<Pipeline> pipelines;
|
|
for (int i = 0; i < crtcCopies.count(); i++) {
|
|
if (crtcCopies[i].getProp(QStringLiteral("ACTIVE"))) {
|
|
auto blob = gpu->getBlob(crtcCopies[i].getProp(QStringLiteral("MODE_ID")));
|
|
if (!blob) {
|
|
qWarning("Atomic request tries to enable CRTC %u without a mode", crtcCopies[i].id);
|
|
return -(errno = EINVAL);
|
|
} else if (blob->size != sizeof(drmModeModeInfo)) {
|
|
qWarning("Atomic request tries to enable CRTC %u with an invalid mode blob", crtcCopies[i].id);
|
|
return -(errno = EINVAL);
|
|
}
|
|
Pipeline pipeline;
|
|
pipeline.crtc = &crtcCopies[i];
|
|
pipelines << pipeline;
|
|
}
|
|
}
|
|
for (int i = 0; i < connCopies.count(); i++) {
|
|
if (auto crtc = connCopies[i].getProp(QStringLiteral("CRTC_ID"))) {
|
|
bool found = false;
|
|
for (int p = 0; p < pipelines.count(); p++) {
|
|
if (pipelines[p].crtc->id == crtc) {
|
|
pipelines[p].conns << &connCopies[i];
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
qWarning("CRTC_ID of connector %u points to inactive or wrong crtc", connCopies[i].id);
|
|
return -(errno = EINVAL);
|
|
}
|
|
}
|
|
}
|
|
for (int i = 0; i < planeCopies.count(); i++) {
|
|
if (auto crtc = planeCopies[i].getProp(QStringLiteral("CRTC_ID"))) {
|
|
bool found = false;
|
|
for (int p = 0; p < pipelines.count(); p++) {
|
|
if (pipelines[p].crtc->id == crtc) {
|
|
if (pipelines[p].primaryPlane) {
|
|
qWarning("crtc %u has more than one primary planes assigned: %u and %u", pipelines[p].crtc->id, pipelines[p].primaryPlane->id, planeCopies[i].id);
|
|
return -(errno = EINVAL);
|
|
} else if (!(planeCopies[i].possibleCrtcs & (1 << pipelines[p].crtc->pipeIndex))) {
|
|
qWarning("crtc %u is not suitable for primary plane %u", pipelines[p].crtc->id, planeCopies[i].id);
|
|
return -(errno = EINVAL);
|
|
} else {
|
|
pipelines[p].primaryPlane = &planeCopies[i];
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!found) {
|
|
qWarning("CRTC_ID of plane %u points to inactive or wrong crtc", planeCopies[i].id);
|
|
return -(errno = EINVAL);
|
|
}
|
|
auto fbId = planeCopies[i].getProp(QStringLiteral("FB_ID"));
|
|
if (!fbId) {
|
|
qWarning("FB_ID of active plane %u is 0", planeCopies[i].id);
|
|
return -(errno = EINVAL);
|
|
}
|
|
auto it = std::find_if(gpu->fbs.constBegin(), gpu->fbs.constEnd(), [fbId](auto fb){return fb->id == fbId;});
|
|
if (it == gpu->fbs.constEnd()) {
|
|
qWarning("FB_ID %lu of active plane %u is invalid", fbId, planeCopies[i].id);
|
|
return -(errno = EINVAL);
|
|
}
|
|
planeCopies[i].nextFb = *it;
|
|
} else {
|
|
planeCopies[i].nextFb = nullptr;
|
|
}
|
|
}
|
|
for (const auto &p : std::as_const(pipelines)) {
|
|
if (p.conns.isEmpty()) {
|
|
qWarning("Active crtc %u has no assigned connectors", p.crtc->id);
|
|
return -(errno = EINVAL);
|
|
} else if (!p.primaryPlane) {
|
|
qWarning("Active crtc %u has no assigned primary plane", p.crtc->id);
|
|
return -(errno = EINVAL);
|
|
} else {
|
|
drmModeModeInfo mode = *static_cast<drmModeModeInfo*>(gpu->getBlob(p.crtc->getProp(QStringLiteral("MODE_ID")))->data);
|
|
for (const auto &conn : p.conns) {
|
|
bool modeFound = std::find_if(conn->modes.constBegin(), conn->modes.constEnd(), [mode](const auto &m){
|
|
return checkIfEqual(mode, m);
|
|
}) != conn->modes.constEnd();
|
|
if (!modeFound) {
|
|
qWarning("mode on crtc %u is incompatible with connector %u", p.crtc->id, conn->id);
|
|
return -(errno = EINVAL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if wanted, apply them
|
|
|
|
if (!(flags & DRM_MODE_ATOMIC_TEST_ONLY)) {
|
|
for (auto &conn : std::as_const(gpu->connectors)) {
|
|
auto it = std::find_if(connCopies.constBegin(), connCopies.constEnd(), [conn](auto c){return c.id == conn->id;});
|
|
if (it == connCopies.constEnd()) {
|
|
qCritical("implementation error: can't find connector %u", conn->id);
|
|
return -(errno = EINVAL);
|
|
}
|
|
*conn = *it;
|
|
}
|
|
for (auto &crtc : std::as_const(gpu->crtcs)) {
|
|
auto it = std::find_if(crtcCopies.constBegin(), crtcCopies.constEnd(), [crtc](auto c){return c.id == crtc->id;});
|
|
if (it == crtcCopies.constEnd()) {
|
|
qCritical("implementation error: can't find crtc %u", crtc->id);
|
|
return -(errno = EINVAL);
|
|
}
|
|
*crtc = *it;
|
|
}
|
|
for (auto &plane : std::as_const(gpu->planes)) {
|
|
auto it = std::find_if(planeCopies.constBegin(), planeCopies.constEnd(), [plane](auto c){return c.id == plane->id;});
|
|
if (it == planeCopies.constEnd()) {
|
|
qCritical("implementation error: can't find plane %u", plane->id);
|
|
return -(errno = EINVAL);
|
|
}
|
|
*plane = *it;
|
|
}
|
|
|
|
if (flags & DRM_MODE_PAGE_FLIP_EVENT) {
|
|
// Unsupported
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int drmModeCreatePropertyBlob(int fd, const void *data, size_t size, uint32_t *id)
|
|
{
|
|
GPU(fd, -EINVAL);
|
|
if (!data || !size || !id) {
|
|
return -(errno = EINVAL);
|
|
}
|
|
auto blob = std::make_unique<MockPropertyBlob>(gpu, data, size);
|
|
*id = blob->id;
|
|
gpu->propertyBlobs.push_back(std::move(blob));
|
|
return 0;
|
|
}
|
|
|
|
int drmModeDestroyPropertyBlob(int fd, uint32_t id)
|
|
{
|
|
GPU(fd, -EINVAL);
|
|
auto it = std::remove_if(gpu->propertyBlobs.begin(), gpu->propertyBlobs.end(), [id](const auto &blob) {
|
|
return blob->id == id;
|
|
});
|
|
if (it == gpu->propertyBlobs.end()) {
|
|
return -(errno = EINVAL);
|
|
} else {
|
|
gpu->propertyBlobs.erase(it, gpu->propertyBlobs.end());
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int drmModeCreateLease(int fd, const uint32_t *objects, int num_objects, int flags, uint32_t *lessee_id)
|
|
{
|
|
return -(errno = ENOTSUP);
|
|
}
|
|
|
|
drmModeLesseeListPtr drmModeListLessees(int fd)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
drmModeObjectListPtr drmModeGetLease(int fd)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
int drmModeRevokeLease(int fd, uint32_t lessee_id)
|
|
{
|
|
return -(errno = ENOTSUP);
|
|
}
|
|
|
|
void drmModeFreeResources(drmModeResPtr ptr)
|
|
{
|
|
for (const auto &gpu : std::as_const(s_gpus)) {
|
|
if (gpu->resPtrs.removeOne(ptr)) {
|
|
delete[] ptr->connectors;
|
|
delete[] ptr->crtcs;
|
|
delete[] ptr->encoders;
|
|
delete[] ptr->fbs;
|
|
delete ptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
void drmModeFreePlaneResources(drmModePlaneResPtr ptr)
|
|
{
|
|
for (const auto &gpu : std::as_const(s_gpus)) {
|
|
if (gpu->drmPlaneRes.removeOne(ptr)) {
|
|
delete[] ptr->planes;
|
|
delete ptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
void drmModeFreeCrtc(drmModeCrtcPtr ptr)
|
|
{
|
|
for (const auto &gpu : std::as_const(s_gpus)) {
|
|
if (gpu->drmCrtcs.removeOne(ptr)) {
|
|
delete ptr;
|
|
return;
|
|
}
|
|
}
|
|
Q_UNREACHABLE();
|
|
}
|
|
|
|
void drmModeFreeConnector(drmModeConnectorPtr ptr)
|
|
{
|
|
for (const auto &gpu : std::as_const(s_gpus)) {
|
|
if (gpu->drmConnectors.removeOne(ptr)) {
|
|
delete[] ptr->encoders;
|
|
delete[] ptr->props;
|
|
delete[] ptr->prop_values;
|
|
delete ptr;
|
|
return;
|
|
}
|
|
}
|
|
Q_UNREACHABLE();
|
|
}
|
|
|
|
void drmModeFreeEncoder(drmModeEncoderPtr ptr)
|
|
{
|
|
for (const auto &gpu : std::as_const(s_gpus)) {
|
|
if (gpu->drmEncoders.removeOne(ptr)) {
|
|
delete ptr;
|
|
return;
|
|
}
|
|
}
|
|
Q_UNREACHABLE();
|
|
}
|
|
|
|
void drmModeFreePlane(drmModePlanePtr ptr)
|
|
{
|
|
for (const auto &gpu : std::as_const(s_gpus)) {
|
|
if (gpu->drmPlanes.removeOne(ptr)) {
|
|
delete ptr;
|
|
return;
|
|
}
|
|
}
|
|
Q_UNREACHABLE();
|
|
}
|