You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1290 lines
42 KiB
1290 lines
42 KiB
/* |
|
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(); |
|
}
|
|
|