diff --git a/plugins/platforms/drm/CMakeLists.txt b/plugins/platforms/drm/CMakeLists.txt
index 3f6a99a359..4f3a3ab3cf 100644
--- a/plugins/platforms/drm/CMakeLists.txt
+++ b/plugins/platforms/drm/CMakeLists.txt
@@ -1,5 +1,9 @@
set(DRM_SOURCES
drm_backend.cpp
+ drm_object.cpp
+ drm_object_connector.cpp
+ drm_object_crtc.cpp
+ drm_object_plane.cpp
drm_output.cpp
drm_buffer.cpp
drm_inputeventfilter.cpp
diff --git a/plugins/platforms/drm/drm_backend.cpp b/plugins/platforms/drm/drm_backend.cpp
index 1d840d2dcc..dc2b796965 100644
--- a/plugins/platforms/drm/drm_backend.cpp
+++ b/plugins/platforms/drm/drm_backend.cpp
@@ -19,6 +19,9 @@ along with this program. If not, see .
*********************************************************************/
#include "drm_backend.h"
#include "drm_output.h"
+#include "drm_object_connector.h"
+#include "drm_object_crtc.h"
+#include "drm_object_plane.h"
#include "composite.h"
#include "cursor.h"
#include "logging.h"
@@ -84,6 +87,7 @@ DrmBackend::~DrmBackend()
while (m_pageFlipsPending != 0) {
QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
}
+ qDeleteAll(m_planes);
qDeleteAll(m_outputs);
delete m_cursor[0];
delete m_cursor[1];
@@ -242,6 +246,46 @@ void DrmBackend::openDrm()
}
);
m_drmId = device->sysNum();
+
+ // trying to activate Atomic Mode Setting (this means also Universal Planes)
+ if (qEnvironmentVariableIsSet("KWIN_DRM_AMS")) {
+ if (drmSetClientCap(m_fd, DRM_CLIENT_CAP_ATOMIC, 1) == 0) {
+ qCDebug(KWIN_DRM) << "Using Atomic Mode Setting.";
+ m_atomicModeSetting = true;
+
+ ScopedDrmPointer planeResources(drmModeGetPlaneResources(m_fd));
+ if (!planeResources) {
+ qCWarning(KWIN_DRM) << "Failed to get plane resources. Falling back to legacy mode";
+ m_atomicModeSetting = false;
+ }
+
+ if (m_atomicModeSetting) {
+ qCDebug(KWIN_DRM) << "Number of planes:" << planeResources->count_planes;
+
+ // create the plane objects
+ for (unsigned int i = 0; i < planeResources->count_planes; ++i) {
+ drmModePlane *kplane = drmModeGetPlane(m_fd, planeResources->planes[i]);
+ DrmPlane *p = new DrmPlane(kplane->plane_id, m_fd);
+
+ if (p->init()) {
+ p->setPossibleCrtcs(kplane->possible_crtcs);
+ p->setFormats(kplane->formats, kplane->count_formats);
+ m_planes << p;
+ } else {
+ delete p;
+ }
+ }
+
+ if (m_planes.isEmpty()) {
+ qCWarning(KWIN_DRM) << "Failed to create any plane. Falling back to legacy mode";
+ m_atomicModeSetting = false;
+ }
+ }
+ } else {
+ qCWarning(KWIN_DRM) << "drmSetClientCap for Atomic Mode Setting failed. Using legacy mode.";
+ }
+ }
+
queryResources();
if (m_outputs.isEmpty()) {
qCWarning(KWIN_DRM) << "No outputs, cannot render, will terminate now";
@@ -320,12 +364,37 @@ void DrmBackend::queryResources()
DrmOutput *drmOutput = new DrmOutput(this);
connect(drmOutput, &DrmOutput::dpmsChanged, this, &DrmBackend::outputDpmsChanged);
drmOutput->m_crtcId = crtcId;
+ drmOutput->m_connector = connector->connector_id;
+
+ if (m_atomicModeSetting) {
+ drmOutput->m_crtc = new DrmCrtc(crtcId, m_fd);
+ if (drmOutput->m_crtc->init()) {
+ drmOutput->m_crtc->setOutput(drmOutput);
+ } else {
+ qCWarning(KWIN_DRM) << "Crtc object failed, skipping output on connector" << connector->connector_id;
+ delete drmOutput->m_crtc;
+ delete drmOutput;
+ continue;
+ }
+
+ drmOutput->m_conn = new DrmConnector(connector->connector_id, m_fd);
+ if (drmOutput->m_conn->init()) {
+ drmOutput->m_conn->setOutput(drmOutput);
+ } else {
+ qCWarning(KWIN_DRM) << "Connector object failed, skipping output on connector" << connector->connector_id;
+ delete drmOutput->m_conn;
+ delete drmOutput;
+ continue;
+ }
+ }
+
if (crtc->mode_valid) {
drmOutput->m_mode = crtc->mode;
} else {
drmOutput->m_mode = connector->modes[0];
}
- drmOutput->m_connector = connector->connector_id;
+ qCDebug(KWIN_DRM) << "For new output use mode " << drmOutput->m_mode.name;
+
if (!drmOutput->init(connector.data())) {
qCWarning(KWIN_DRM) << "Failed to create output for connector " << connector->connector_id;
delete drmOutput;
@@ -625,6 +694,7 @@ DrmBuffer *DrmBackend::createBuffer(gbm_surface *surface)
{
#if HAVE_GBM
DrmBuffer *b = new DrmBuffer(this, surface);
+ b->m_deleteAfterPageFlip = true;
m_buffers << b;
return b;
#else
diff --git a/plugins/platforms/drm/drm_backend.h b/plugins/platforms/drm/drm_backend.h
index 0a731691eb..7aec98a54f 100644
--- a/plugins/platforms/drm/drm_backend.h
+++ b/plugins/platforms/drm/drm_backend.h
@@ -54,6 +54,7 @@ class Udev;
class UdevMonitor;
class DrmOutput;
+class DrmPlane;
class KWIN_EXPORT DrmBackend : public Platform
@@ -85,11 +86,19 @@ public:
QVector buffers() const {
return m_buffers;
}
+ QVector planes() const {
+ return m_planes;
+ }
void bufferDestroyed(DrmBuffer *b);
void outputWentOff();
void checkOutputsAreOn();
+ // returns use of AMS, default is not/legacy
+ bool atomicModeSetting() const {
+ return m_atomicModeSetting;
+ }
+
void setGbmDevice(gbm_device *device) {
m_gbmDevice = device;
}
@@ -100,7 +109,6 @@ public:
public Q_SLOTS:
void turnOutputsOn();
-
Q_SIGNALS:
void outputRemoved(KWin::DrmOutput *output);
void outputAdded(KWin::DrmOutput *output);
@@ -130,11 +138,14 @@ private:
int m_drmId = 0;
QVector m_outputs;
DrmBuffer *m_cursor[2];
+ bool m_atomicModeSetting = false;
bool m_cursorEnabled = false;
int m_cursorIndex = 0;
int m_pageFlipsPending = 0;
bool m_active = false;
QVector m_buffers;
+ // all available planes: primarys, cursors and overlays
+ QVector m_planes;
QScopedPointer m_dpmsFilter;
KWayland::Server::OutputManagementInterface *m_outputManagement = nullptr;
gbm_device *m_gbmDevice = nullptr;
diff --git a/plugins/platforms/drm/drm_buffer.h b/plugins/platforms/drm/drm_buffer.h
index 47db4f8d6e..00b513b27f 100644
--- a/plugins/platforms/drm/drm_buffer.h
+++ b/plugins/platforms/drm/drm_buffer.h
@@ -58,6 +58,10 @@ public:
bool isGbm() const {
return m_bo != nullptr;
}
+ bool deleteAfterPageFlip() const {
+ return m_deleteAfterPageFlip;
+ }
+
void releaseGbm();
private:
@@ -74,6 +78,7 @@ private:
quint64 m_bufferSize = 0;
void *m_memory = nullptr;
QImage *m_image = nullptr;
+ bool m_deleteAfterPageFlip = false;
};
}
diff --git a/plugins/platforms/drm/drm_object.cpp b/plugins/platforms/drm/drm_object.cpp
new file mode 100644
index 0000000000..d3a438d29c
--- /dev/null
+++ b/plugins/platforms/drm/drm_object.cpp
@@ -0,0 +1,145 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2016 Roman Gilg
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*********************************************************************/
+#include "drm_object.h"
+#include "logging.h"
+
+namespace KWin
+{
+
+/*
+ * Defintions for class DrmObject
+ */
+
+DrmObject::DrmObject(uint32_t object_id, int fd)
+ : m_fd(fd)
+ , m_id(object_id)
+{
+}
+
+DrmObject::~DrmObject()
+{
+ foreach(Property* p, m_props)
+ delete p;
+}
+
+void DrmObject::initProp(int n, drmModeObjectProperties *properties, QVector enumNames)
+{
+ m_props.resize(m_propsNames.size());
+ for (unsigned int i = 0; i < properties->count_props; ++i) {
+ drmModePropertyRes *prop = drmModeGetProperty(m_fd, properties->props[i]);
+ if (!prop) {
+ continue;
+ }
+ if (prop->name == m_propsNames[n]) {
+ qCDebug(KWIN_DRM).nospace() << m_id << ": " << prop->name << "' (id " << prop->prop_id
+ << "): " << properties->prop_values[i];
+ m_props[n] = new Property(prop, properties->prop_values[i], enumNames);
+ }
+ drmModeFreeProperty(prop);
+ }
+}
+
+void DrmObject::setPropValue(int index, uint64_t new_value)
+{
+ Q_ASSERT(index < m_props.size());
+ m_props[index]->setValue(new_value);
+ return;
+}
+
+bool DrmObject::atomicAddProperty(drmModeAtomicReq *req, int prop, uint64_t value)
+{
+ uint32_t mask = 1U << prop;
+ if ((m_propsPending | m_propsValid) & mask && value == propValue(prop)) {
+ // no change necessary, don't add property for next atomic commit
+ return true;
+ }
+ if (drmModeAtomicAddProperty(req, m_id, m_props[prop]->propId(), value) < 0) {
+ // error when adding property
+ return false;
+ }
+ m_propsPending |= mask;
+ m_propsValid &= ~mask;
+ // adding property was successful
+ return true;
+}
+
+/*
+ * Defintions for struct Prop
+ */
+
+DrmObject::Property::Property(drmModePropertyRes *prop, uint64_t val, QVector enumNames)
+ : m_propId(prop->prop_id)
+ , m_propName(prop->name)
+ , m_value(val)
+{
+ if (!enumNames.isEmpty()) {
+ qCDebug(KWIN_DRM) << m_propName << " has enums:" << enumNames;
+ m_enumNames = enumNames;
+ initEnumMap(prop);
+ }
+}
+
+DrmObject::Property::~Property() = default;
+
+void DrmObject::Property::initEnumMap(drmModePropertyRes *prop)
+{
+ if (!(prop->flags & DRM_MODE_PROP_ENUM) || prop->count_enums < 1) {
+ qCWarning(KWIN_DRM) << "Property '" << prop->name << "' ( id ="
+ << m_propId << ") should be enum valued, but it is not.";
+ return;
+ }
+
+ int nameCount = m_enumNames.size();
+ m_enumMap.resize(nameCount);
+
+ qCDebug(KWIN_DRM).nospace() << "Test all " << prop->count_enums <<
+ " possible enums" <<":";
+
+ for (int i = 0; i < prop->count_enums; i++) {
+
+ struct drm_mode_property_enum *en = &prop->enums[i];
+ int j = 0;
+
+ while (QByteArray(en->name) != m_enumNames[j]) {
+ j++;
+ if (j == nameCount) {
+ break;
+ }
+ }
+
+ if (j == nameCount) {
+ qCWarning(KWIN_DRM).nospace() << m_propName << " has unrecognized enum '" << en->name << "'";
+ } else {
+ qCDebug(KWIN_DRM).nospace() << "Enum '"
+ << en->name << "': runtime-value = " << en->value;
+ m_enumMap[j] = en->value;
+ }
+ }
+
+ if (KWIN_DRM().isDebugEnabled()) {
+ for (int i = 0; i < m_enumMap.size(); i++) {
+ if (m_value == m_enumMap[i]) {
+ qCDebug(KWIN_DRM) << "=>" << m_propName << "with mapped enum value" << m_enumNames[i];
+ }
+ }
+ }
+}
+
+}
diff --git a/plugins/platforms/drm/drm_object.h b/plugins/platforms/drm/drm_object.h
new file mode 100644
index 0000000000..168c9c092d
--- /dev/null
+++ b/plugins/platforms/drm/drm_object.h
@@ -0,0 +1,137 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2016 Roman Gilg
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*********************************************************************/
+#ifndef KWIN_DRM_OBJECT_H
+#define KWIN_DRM_OBJECT_H
+
+#include
+#include
+
+// drm
+#include
+
+
+namespace KWin
+{
+
+class DrmOutput;
+
+class DrmObject
+{
+public:
+ // creates drm object by its id delivered by the kernel
+ DrmObject(uint32_t object_id, int fd);
+
+ virtual ~DrmObject() = 0;
+
+ enum class AtomicReturn {
+ NoChange,
+ Success,
+ Error
+ };
+
+ virtual bool init() = 0;
+
+ uint32_t id() const {
+ return m_id;
+ }
+
+ DrmOutput* output() const {
+ return m_output;
+ }
+ void setOutput(DrmOutput* output) {
+ m_output = output;
+ }
+
+ uint32_t propId(int index) {
+ return m_props[index]->propId();
+ }
+ uint64_t propValue(int index) {
+ return m_props[index]->value();
+ }
+ void setPropValue(int index, uint64_t new_value);
+
+ uint32_t propsPending() {
+ return m_propsPending;
+ }
+ uint32_t propsValid() {
+ return m_propsValid;
+ }
+ void setPropsPending(uint32_t value) {
+ m_propsPending = value;
+ }
+ void setPropsValid(uint32_t value) {
+ m_propsValid = value;
+ }
+
+ bool atomicAddProperty(drmModeAtomicReq *req, int prop, uint64_t value);
+
+protected:
+ const int m_fd = 0;
+ const uint32_t m_id = 0;
+
+ DrmOutput *m_output = nullptr;
+
+ QVector m_propsNames; // for comparision with received name of DRM object
+ class Property;
+ QVector m_props;
+
+ uint32_t m_propsPending = 0;
+ uint32_t m_propsValid = 0;
+
+ virtual bool initProps() = 0; // only derived classes know names and quantity of properties
+ void initProp(int n, drmModeObjectProperties *properties, QVector enumNames = QVector(0));
+
+ class Property
+ {
+ public:
+ Property(drmModePropertyRes *prop, uint64_t val, QVector enumNames);
+ virtual ~Property();
+
+ void initEnumMap(drmModePropertyRes *prop);
+
+ uint64_t enumMap(int n) {
+ return m_enumMap[n]; // TODO: test on index out of bounds?
+ }
+
+ uint32_t propId() {
+ return m_propId;
+ }
+ uint32_t value() {
+ return m_value;
+ }
+ void setValue(uint64_t new_value) {
+ m_value = new_value;
+ }
+
+ private:
+ uint32_t m_propId = 0;
+ QByteArray m_propName;
+
+ uint64_t m_value = 0;
+ QVector m_enumMap;
+ QVector m_enumNames;
+ };
+};
+
+
+}
+
+#endif
+
diff --git a/plugins/platforms/drm/drm_object_connector.cpp b/plugins/platforms/drm/drm_object_connector.cpp
new file mode 100644
index 0000000000..67a695797f
--- /dev/null
+++ b/plugins/platforms/drm/drm_object_connector.cpp
@@ -0,0 +1,64 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2016 Roman Gilg
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*********************************************************************/
+#include "drm_object_connector.h"
+#include "logging.h"
+
+namespace KWin
+{
+
+DrmConnector::DrmConnector(uint32_t connector_id, int fd)
+ : DrmObject(connector_id, fd)
+{
+
+}
+
+DrmConnector::~DrmConnector() = default;
+
+bool DrmConnector::init()
+{
+ qCDebug(KWIN_DRM) << "Creating connector" << m_id;
+
+ if (!initProps()) {
+ return false;
+ }
+ return true;
+}
+
+bool DrmConnector::initProps()
+{
+ m_propsNames = {
+ QByteArrayLiteral("CRTC_ID"),
+ };
+
+ drmModeObjectProperties *properties = drmModeObjectGetProperties(m_fd, m_id, DRM_MODE_OBJECT_CONNECTOR);
+ if (!properties) {
+ qCWarning(KWIN_DRM) << "Failed to get properties for connector " << m_id ;
+ return false;
+ }
+
+ int propCount = int(PropertyIndex::Count);
+ for (int j = 0; j < propCount; ++j) {
+ initProp(j, properties);
+ }
+ drmModeFreeObjectProperties(properties);
+ return true;
+}
+
+}
diff --git a/plugins/platforms/drm/drm_object_connector.h b/plugins/platforms/drm/drm_object_connector.h
new file mode 100644
index 0000000000..dcaf200c0d
--- /dev/null
+++ b/plugins/platforms/drm/drm_object_connector.h
@@ -0,0 +1,48 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2016 Roman Gilg
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*********************************************************************/
+#ifndef KWIN_DRM_OBJECT_CONNECTOR_H
+#define KWIN_DRM_OBJECT_CONNECTOR_H
+
+#include "drm_object.h"
+
+namespace KWin
+{
+
+class DrmConnector : public DrmObject
+{
+public:
+ DrmConnector(uint32_t connector_id, int fd);
+
+ virtual ~DrmConnector();
+
+ bool init();
+
+ enum class PropertyIndex {
+ CrtcId = 0,
+ Count
+ };
+
+ bool initProps();
+};
+
+}
+
+#endif
+
diff --git a/plugins/platforms/drm/drm_object_crtc.cpp b/plugins/platforms/drm/drm_object_crtc.cpp
new file mode 100644
index 0000000000..1d5f03c8f7
--- /dev/null
+++ b/plugins/platforms/drm/drm_object_crtc.cpp
@@ -0,0 +1,64 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2016 Roman Gilg
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*********************************************************************/
+#include "drm_object_crtc.h"
+#include "logging.h"
+
+namespace KWin
+{
+
+DrmCrtc::DrmCrtc(uint32_t crtc_id, int fd)
+ : DrmObject(crtc_id, fd)
+{
+}
+
+DrmCrtc::~DrmCrtc() = default;
+
+bool DrmCrtc::init()
+{
+ qCDebug(KWIN_DRM) << "Creating CRTC" << m_id;
+
+ if (!initProps()) {
+ return false;
+ }
+ return true;
+}
+
+bool DrmCrtc::initProps()
+{
+ m_propsNames = {
+ QByteArrayLiteral("MODE_ID"),
+ QByteArrayLiteral("ACTIVE"),
+ };
+
+ drmModeObjectProperties *properties = drmModeObjectGetProperties(m_fd, m_id, DRM_MODE_OBJECT_CRTC);
+ if (!properties) {
+ qCWarning(KWIN_DRM) << "Failed to get properties for crtc " << m_id ;
+ return false;
+ }
+
+ int propCount = int(PropertyIndex::Count);
+ for (int j = 0; j < propCount; ++j) {
+ initProp(j, properties);
+ }
+ drmModeFreeObjectProperties(properties);
+ return true;
+}
+
+}
diff --git a/plugins/platforms/drm/drm_object_crtc.h b/plugins/platforms/drm/drm_object_crtc.h
new file mode 100644
index 0000000000..cc9b5c94e1
--- /dev/null
+++ b/plugins/platforms/drm/drm_object_crtc.h
@@ -0,0 +1,51 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2016 Roman Gilg
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*********************************************************************/
+#ifndef KWIN_DRM_OBJECT_CRTC_H
+#define KWIN_DRM_OBJECT_CRTC_H
+
+#include "drm_object.h"
+
+namespace KWin
+{
+
+class DrmBuffer;
+
+class DrmCrtc : public DrmObject
+{
+public:
+ DrmCrtc(uint32_t crtc_id, int fd);
+
+ virtual ~DrmCrtc();
+
+ bool init();
+
+ enum class PropertyIndex {
+ ModeId = 0,
+ Active,
+ Count
+ };
+
+ bool initProps();
+};
+
+}
+
+#endif
+
diff --git a/plugins/platforms/drm/drm_object_plane.cpp b/plugins/platforms/drm/drm_object_plane.cpp
new file mode 100644
index 0000000000..85b4b44470
--- /dev/null
+++ b/plugins/platforms/drm/drm_object_plane.cpp
@@ -0,0 +1,174 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2016 Roman Gilg
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*********************************************************************/
+#include "drm_object_plane.h"
+#include "drm_buffer.h"
+#include "drm_pointer.h"
+#include "logging.h"
+
+namespace KWin
+{
+
+DrmPlane::DrmPlane(uint32_t plane_id, int fd)
+ : DrmObject(plane_id, fd)
+{
+}
+
+DrmPlane::~DrmPlane() = default;
+
+bool DrmPlane::init()
+{
+ qCDebug(KWIN_DRM) << "Initialize plane" << m_id;
+ ScopedDrmPointer<_drmModePlane, &drmModeFreePlane> p(drmModeGetPlane(m_fd, m_id));
+
+ if (!p) {
+ qCWarning(KWIN_DRM) << "Failed to get kernel plane" << m_id;
+ return false;
+ }
+
+ m_possibleCrtcs = p->possible_crtcs;
+
+ m_formats.resize(p->count_formats);
+ for (int i = 0; i < p->count_formats; i++) {
+ m_formats[i] = p->formats[i];
+ }
+
+ if (!initProps()) {
+ return false;
+ }
+ return true;
+}
+
+bool DrmPlane::initProps()
+{
+ m_propsNames = {
+ QByteArrayLiteral("type"),
+ QByteArrayLiteral("SRC_X"),
+ QByteArrayLiteral("SRC_Y"),
+ QByteArrayLiteral("SRC_W"),
+ QByteArrayLiteral("SRC_H"),
+ QByteArrayLiteral("CRTC_X"),
+ QByteArrayLiteral("CRTC_Y"),
+ QByteArrayLiteral("CRTC_W"),
+ QByteArrayLiteral("CRTC_H"),
+ QByteArrayLiteral("FB_ID"),
+ QByteArrayLiteral("CRTC_ID"),
+ };
+
+ QVector typeNames = {
+ QByteArrayLiteral("Primary"),
+ QByteArrayLiteral("Cursor"),
+ QByteArrayLiteral("Overlay"),
+ };
+
+ drmModeObjectProperties *properties = drmModeObjectGetProperties(m_fd, m_id, DRM_MODE_OBJECT_PLANE);
+ if (!properties){
+ qCWarning(KWIN_DRM) << "Failed to get properties for plane " << m_id ;
+ return false;
+ }
+
+ int propCount = int(PropertyIndex::Count);
+ for (int j = 0; j < propCount; ++j) {
+ if (j == int(PropertyIndex::Type)) {
+ initProp(j, properties, typeNames);
+ } else {
+ initProp(j, properties);
+ }
+ }
+
+ drmModeFreeObjectProperties(properties);
+ return true;
+}
+
+DrmPlane::TypeIndex DrmPlane::type()
+{
+ uint64_t value = propValue(int(PropertyIndex::Type));
+ int typeCount = int(TypeIndex::Count);
+ for (int i = 0; i < typeCount; i++) {
+ if (m_props[int(PropertyIndex::Type)]->enumMap(i) == value) {
+ return TypeIndex(i);
+ }
+ }
+ return TypeIndex::Overlay;
+}
+
+bool DrmPlane::isCrtcSupported(uint32_t crtc)
+{
+ ScopedDrmPointer<_drmModeRes, &drmModeFreeResources> res(drmModeGetResources(m_fd));
+ if (!res) {
+ qCWarning(KWIN_DRM) << "Failed to get drm resources";
+ }
+ for (int c = 0; c < res->count_crtcs; c++) {
+ if (res->crtcs[c] != crtc) {
+ continue;
+ }
+ qCDebug(KWIN_DRM) << "Mask " << m_possibleCrtcs << ", idx " << c;
+ if (m_possibleCrtcs & (1 << c)) {
+ return true;
+ }
+ }
+ qCDebug(KWIN_DRM) << "CRTC" << crtc << "not supported";
+ return false;
+}
+
+
+void DrmPlane::setFormats(uint32_t const *f, int fcount)
+{
+ m_formats.resize(fcount);
+ for (int i = 0; i < fcount; i++) {
+ m_formats[i] = *f;
+ }
+}
+
+DrmObject::AtomicReturn DrmPlane::atomicReqPlanePopulate(drmModeAtomicReq *req)
+{
+ bool ret = true;
+
+ if (m_next) {
+ setPropValue(int(PropertyIndex::FbId), m_next->bufferId());
+ } else {
+ setPropValue(int(PropertyIndex::FbId), 0);
+ setPropValue(int(PropertyIndex::SrcX), 0);
+ setPropValue(int(PropertyIndex::SrcY), 0);
+ setPropValue(int(PropertyIndex::SrcW), 0);
+ setPropValue(int(PropertyIndex::SrcH), 0);
+ setPropValue(int(PropertyIndex::CrtcX), 0);
+ setPropValue(int(PropertyIndex::CrtcY), 0);
+ setPropValue(int(PropertyIndex::CrtcW), 0);
+ setPropValue(int(PropertyIndex::CrtcH), 0);
+ }
+
+ m_propsPending = 0;
+
+ for (int i = int(PropertyIndex::SrcX); i < int(PropertyIndex::CrtcId); i++) {
+ ret &= atomicAddProperty(req, i, propValue(i));
+ }
+ ret &= atomicAddProperty(req, int(PropertyIndex::CrtcId), m_next ? propValue(int(PropertyIndex::CrtcId)) : 0);
+
+ if (!ret) {
+ qCWarning(KWIN_DRM) << "Failed to populate atomic plane" << m_id;
+ return DrmObject::AtomicReturn::Error;
+ }
+ if (!m_propsPending) {
+ return DrmObject::AtomicReturn::NoChange;
+ }
+ return DrmObject::AtomicReturn::Success;
+}
+
+}
diff --git a/plugins/platforms/drm/drm_object_plane.h b/plugins/platforms/drm/drm_object_plane.h
new file mode 100644
index 0000000000..bef27ce9fc
--- /dev/null
+++ b/plugins/platforms/drm/drm_object_plane.h
@@ -0,0 +1,106 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2016 Roman Gilg
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*********************************************************************/
+#ifndef KWIN_DRM_OBJECT_PLANE_H
+#define KWIN_DRM_OBJECT_PLANE_H
+
+#include "drm_object.h"
+// drm
+#include
+
+namespace KWin
+{
+
+class DrmBuffer;
+
+class DrmPlane : public DrmObject
+{
+public:
+ DrmPlane(uint32_t plane_id, int fd);
+
+ virtual ~DrmPlane();
+
+ enum class PropertyIndex {
+ Type = 0,
+ SrcX,
+ SrcY,
+ SrcW,
+ SrcH,
+ CrtcX,
+ CrtcY,
+ CrtcW,
+ CrtcH,
+ FbId,
+ CrtcId,
+ Count
+ };
+
+ enum class TypeIndex {
+ Primary = 0,
+ Cursor,
+ Overlay,
+ Count
+ };
+
+ bool init();
+ bool initProps();
+ TypeIndex type();
+ bool isCrtcSupported(uint32_t crtc);
+ DrmObject::AtomicReturn atomicReqPlanePopulate(drmModeAtomicReq *req);
+
+ DrmBuffer *current(){
+ return m_current;
+ }
+ DrmBuffer *next(){
+ return m_next;
+ }
+ void setCurrent(DrmBuffer *b){
+ m_current = b;
+ }
+ void setNext(DrmBuffer *b){
+ m_next = b;
+ }
+
+ QVector formats(){
+ return m_formats;
+ }
+ void setFormats(uint32_t const *f, int fcount);
+
+ void setPossibleCrtcs(uint32_t value){
+ m_possibleCrtcs = value;
+ }
+ uint32_t possibleCrtcs(){
+ return m_possibleCrtcs;
+ }
+
+private:
+ DrmBuffer *m_current = nullptr;
+ DrmBuffer *m_next = nullptr;
+
+ // TODO: See weston drm_output_check_plane_format for future use of these member variables
+ QVector m_formats; // Possible formats, which can be presented on this plane
+
+ // TODO: when using overlay planes in the future: restrict possible screens / crtcs of planes
+ uint32_t m_possibleCrtcs;
+};
+
+}
+
+#endif
+
diff --git a/plugins/platforms/drm/drm_output.cpp b/plugins/platforms/drm/drm_output.cpp
index 69b1ed6c41..abcdc4dde1 100644
--- a/plugins/platforms/drm/drm_output.cpp
+++ b/plugins/platforms/drm/drm_output.cpp
@@ -19,6 +19,11 @@ along with this program. If not, see .
*********************************************************************/
#include "drm_output.h"
#include "drm_backend.h"
+#include "drm_object_plane.h"
+#include "drm_object_crtc.h"
+#include "drm_object_connector.h"
+
+#include
#include "composite.h"
#include "logind.h"
@@ -58,6 +63,8 @@ DrmOutput::~DrmOutput()
{
hideCursor();
cleanupBlackBuffer();
+ delete m_crtc;
+ delete m_conn;
delete m_waylandOutput.data();
delete m_waylandOutputDevice.data();
}
@@ -97,55 +104,6 @@ QRect DrmOutput::geometry() const
return QRect(m_globalPos, size());
}
-bool DrmOutput::present(DrmBuffer *buffer)
-{
- if (!buffer || buffer->bufferId() == 0) {
- return false;
- }
- if (!LogindIntegration::self()->isActiveSession()) {
- m_currentBuffer = buffer;
- return false;
- }
- if (m_dpmsMode != DpmsMode::On) {
- return false;
- }
- if (m_currentBuffer) {
- return false;
- }
- if (m_lastStride != buffer->stride() || m_lastGbm != buffer->isGbm()) {
- // need to set a new mode first
- if (!setMode(buffer)) {
- return false;
- }
- }
- const bool ok = drmModePageFlip(m_backend->fd(), m_crtcId, buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0;
- if (ok) {
- m_currentBuffer = buffer;
- } else {
- qCWarning(KWIN_DRM) << "Page flip failed";
- buffer->releaseGbm();
- }
- return ok;
-}
-
-void DrmOutput::pageFlipped()
-{
- if (!m_currentBuffer) {
- return;
- }
- m_currentBuffer->releaseGbm();
- m_currentBuffer = nullptr;
- cleanupBlackBuffer();
-}
-
-void DrmOutput::cleanupBlackBuffer()
-{
- if (m_blackBuffer) {
- delete m_blackBuffer;
- m_blackBuffer = nullptr;
- }
-}
-
static KWayland::Server::OutputInterface::DpmsMode toWaylandDpmsMode(DrmOutput::DpmsMode mode)
{
using namespace KWayland::Server;
@@ -185,6 +143,11 @@ bool DrmOutput::init(drmModeConnector *connector)
initEdid(connector);
initDpms(connector);
initUuid();
+ if (m_backend->atomicModeSetting()) {
+ if (!initPrimaryPlane()) {
+ return false;
+ }
+ }
m_savedCrtc.reset(drmModeGetCrtc(m_backend->fd(), m_crtcId));
if (!blank()) {
return false;
@@ -240,6 +203,10 @@ bool DrmOutput::init(drmModeConnector *connector)
// read in mode information
for (int i = 0; i < connector->count_modes; ++i) {
+
+ // TODO: in AMS here we could read and store for later every mode's blob_id
+ // would simplify isCurrentMode(..) and presentAtomically(..) in case of mode set
+
auto *m = &connector->modes[i];
KWayland::Server::OutputInterface::ModeFlags flags;
KWayland::Server::OutputDeviceInterface::ModeFlags deviceflags;
@@ -321,31 +288,6 @@ bool DrmOutput::isCurrentMode(const drmModeModeInfo *mode) const
&& qstrcmp(mode->name, m_mode.name) == 0;
}
-bool DrmOutput::blank()
-{
- if (!m_blackBuffer) {
- m_blackBuffer = m_backend->createBuffer(size());
- if (!m_blackBuffer->map()) {
- cleanupBlackBuffer();
- return false;
- }
- m_blackBuffer->image()->fill(Qt::black);
- }
- return setMode(m_blackBuffer);
-}
-
-bool DrmOutput::setMode(DrmBuffer *buffer)
-{
- if (drmModeSetCrtc(m_backend->fd(), m_crtcId, buffer->bufferId(), 0, 0, &m_connector, 1, &m_mode) == 0) {
- m_lastStride = buffer->stride();
- m_lastGbm = buffer->isGbm();
- return true;
- } else {
- qCWarning(KWIN_DRM) << "Mode setting failed";
- return false;
- }
-}
-
static bool verifyEdidHeader(drmModePropertyBlobPtr edid)
{
const uint8_t *data = reinterpret_cast(edid->data);
@@ -505,6 +447,61 @@ void DrmOutput::initEdid(drmModeConnector *connector)
m_edid.physicalSize = extractPhysicalSize(edid.data());
}
+bool DrmOutput::initPrimaryPlane()
+{
+ for (int i = 0; i < m_backend->planes().size(); ++i) {
+ DrmPlane* p = m_backend->planes()[i];
+ if (!p) {
+ continue;
+ }
+ if (p->type() != DrmPlane::TypeIndex::Primary) {
+ continue;
+ }
+ if (p->output()) { // Plane already has an output
+ continue;
+ }
+ if (m_primaryPlane) { // Output already has a primary plane
+ continue;
+ }
+ if (!p->isCrtcSupported(m_crtcId)) {
+ continue;
+ }
+ p->setOutput(this);
+ m_primaryPlane = p;
+ qCDebug(KWIN_DRM) << "Initialized primary plane" << p->id() << "on CRTC" << m_crtcId;
+ return true;
+ }
+ qCCritical(KWIN_DRM) << "Failed to initialize primary plane.";
+ return false;
+}
+
+bool DrmOutput::initCursorPlane() // TODO: Add call in init (but needs layer support in general first)
+{
+ for (int i = 0; i < m_backend->planes().size(); ++i) {
+ DrmPlane* p = m_backend->planes()[i];
+ if (!p) {
+ continue;
+ }
+ if (p->type() != DrmPlane::TypeIndex::Cursor) {
+ continue;
+ }
+ if (p->output()) { // Plane already has an output
+ continue;
+ }
+ if (m_cursorPlane) { // Output already has a cursor plane
+ continue;
+ }
+ if (!p->isCrtcSupported(m_crtcId)) {
+ continue;
+ }
+ p->setOutput(this);
+ m_cursorPlane = p;
+ qCDebug(KWIN_DRM) << "Initialized cursor plane" << p->id() << "on CRTC" << m_crtcId;
+ return true;
+ }
+ return false;
+}
+
void DrmOutput::initDpms(drmModeConnector *connector)
{
for (int i = 0; i < connector->count_props; ++i) {
@@ -525,12 +522,30 @@ void DrmOutput::setDpms(DrmOutput::DpmsMode mode)
return;
}
if (mode == m_dpmsMode) {
+ qCDebug(KWIN_DRM) << "New DPMS mode equals old mode. DPMS unchanged.";
return;
}
- if (drmModeConnectorSetProperty(m_backend->fd(), m_connector, m_dpms->prop_id, uint64_t(mode)) != 0) {
- qCWarning(KWIN_DRM) << "Setting DPMS failed";
- return;
+
+ if (m_backend->atomicModeSetting()) {
+ drmModeAtomicReq *req = drmModeAtomicAlloc();
+
+ if (atomicReqModesetPopulate(req, mode == DpmsMode::On) == DrmObject::AtomicReturn::Error) {
+ qCWarning(KWIN_DRM) << "Failed to populate atomic request for output" << m_crtcId;
+ return;
+ }
+ if (drmModeAtomicCommit(m_backend->fd(), req, DRM_MODE_ATOMIC_ALLOW_MODESET, this)) {
+ qCWarning(KWIN_DRM) << "Failed to commit atomic request for output" << m_crtcId;
+ } else {
+ qCDebug(KWIN_DRM) << "DPMS set for output" << m_crtcId;
+ }
+ drmModeAtomicFree(req);
+ } else {
+ if (drmModeConnectorSetProperty(m_backend->fd(), m_connector, m_dpms->prop_id, uint64_t(mode)) < 0) {
+ qCWarning(KWIN_DRM) << "Setting DPMS failed";
+ return;
+ }
}
+
m_dpmsMode = mode;
if (m_waylandOutput) {
m_waylandOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsMode));
@@ -621,5 +636,251 @@ bool DrmOutput::commitChanges()
return true;
}
+void DrmOutput::pageFlipped()
+{
+ if (m_backend->atomicModeSetting()){
+ foreach (DrmPlane *p, m_planesFlipList) {
+ pageFlippedBufferRemover(p->current(), p->next());
+ p->setCurrent(p->next());
+ p->setNext(nullptr);
+ }
+ m_planesFlipList.clear();
+
+ } else {
+ if (!m_nextBuffer) {
+ return;
+ }
+ pageFlippedBufferRemover(m_currentBuffer, m_nextBuffer);
+ m_currentBuffer = m_nextBuffer;
+ m_nextBuffer = nullptr;
+ }
+ cleanupBlackBuffer();
+}
+
+void DrmOutput::pageFlippedBufferRemover(DrmBuffer *oldbuffer, DrmBuffer *newbuffer)
+{
+ if (newbuffer->deleteAfterPageFlip()) {
+ if ( oldbuffer && oldbuffer != newbuffer ) {
+ delete oldbuffer;
+ }
+ } else {
+ // although oldbuffer's pointer is remapped in pageFlipped(),
+ // we ignore the pointer completely anywhere else in this case
+ newbuffer->releaseGbm();
+ }
+}
+
+void DrmOutput::cleanupBlackBuffer()
+{
+ if (m_blackBuffer) {
+ delete m_blackBuffer;
+ m_blackBuffer = nullptr;
+ }
+}
+
+bool DrmOutput::blank()
+{
+ if (!m_blackBuffer) {
+ m_blackBuffer = m_backend->createBuffer(size());
+ if (!m_blackBuffer->map()) {
+ cleanupBlackBuffer();
+ return false;
+ }
+ m_blackBuffer->image()->fill(Qt::black);
+ }
+ // TODO: Do this atomically
+ return setModeLegacy(m_blackBuffer);
+}
+
+bool DrmOutput::present(DrmBuffer *buffer)
+{
+ if (!buffer || buffer->bufferId() == 0) {
+ return false;
+ }
+ if (m_backend->atomicModeSetting()) {
+ return presentAtomically(buffer);
+ } else {
+ return presentLegacy(buffer);
+ }
+}
+
+bool DrmOutput::presentAtomically(DrmBuffer *buffer)
+{
+ if (!LogindIntegration::self()->isActiveSession()) {
+ qCWarning(KWIN_DRM) << "Logind session not active.";
+ return false;
+ }
+ if (m_dpmsMode != DpmsMode::On) {
+ qCWarning(KWIN_DRM) << "No present() while screen off.";
+ return false;
+ }
+ if (m_primaryPlane->next()) {
+ qCWarning(KWIN_DRM) << "Page not yet flipped.";
+ return false;
+ }
+
+ DrmObject::AtomicReturn ret;
+ uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT;
+
+ // TODO: throwing an exception would be really handy here! (would mean change of compile options)
+ drmModeAtomicReq *req = drmModeAtomicAlloc();
+ if (!req) {
+ qCWarning(KWIN_DRM) << "DRM: couldn't allocate atomic request";
+ delete buffer;
+ return false;
+ }
+
+ // Do we need to set a new mode first?
+ bool doModeset = !m_primaryPlane->current();
+ if (doModeset) {
+ qCDebug(KWIN_DRM) << "Atomic Modeset requested";
+
+ if (drmModeCreatePropertyBlob(m_backend->fd(), &m_mode, sizeof(m_mode), &m_blobId)) {
+ qCWarning(KWIN_DRM) << "Failed to create property blob";
+ delete buffer;
+ return false;
+ }
+
+ ret = atomicReqModesetPopulate(req, true);
+ if (ret == DrmObject::AtomicReturn::Error){
+ drmModeAtomicFree(req);
+ delete buffer;
+ return false;
+ }
+ if (ret == DrmObject::AtomicReturn::Success) {
+ flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
+ }
+ }
+
+ m_primaryPlane->setNext(buffer); // TODO: Later not only use the primary plane for the buffer!
+ // i.e.: Assign planes
+ bool anyDamage = false;
+ foreach (DrmPlane* p, m_backend->planes()){
+ if (p->output() != this) {
+ continue;
+ }
+ ret = p->atomicReqPlanePopulate(req);
+ if (ret == DrmObject::AtomicReturn::Error) {
+ drmModeAtomicFree(req);
+ m_primaryPlane->setNext(nullptr);
+ m_planesFlipList.clear();
+ delete buffer;
+ return false;
+ }
+ if (ret == DrmObject::AtomicReturn::Success) {
+ anyDamage = true;
+ m_planesFlipList << p;
+ }
+ }
+
+ // no damage but force flip for atleast the primary plane anyway
+ if (!anyDamage) {
+ m_primaryPlane->setPropsValid(0);
+ if (m_primaryPlane->atomicReqPlanePopulate(req) == DrmObject::AtomicReturn::Error) {
+ drmModeAtomicFree(req);
+ m_primaryPlane->setNext(nullptr);
+ m_planesFlipList.clear();
+ delete buffer;
+ return false;
+ }
+ m_planesFlipList << m_primaryPlane;
+ }
+
+ if (drmModeAtomicCommit(m_backend->fd(), req, flags, this)) {
+ qCWarning(KWIN_DRM) << "Atomic request failed to commit:" << strerror(errno);
+ drmModeAtomicFree(req);
+ m_primaryPlane->setNext(nullptr);
+ m_planesFlipList.clear();
+ delete buffer;
+ return false;
+ }
+
+ if (doModeset) {
+ m_crtc->setPropsValid(m_crtc->propsValid() | m_crtc->propsPending());
+ m_conn->setPropsValid(m_conn->propsValid() | m_conn->propsPending());
+ }
+ foreach (DrmPlane* p, m_planesFlipList) {
+ p->setPropsValid(p->propsValid() | p->propsPending());
+ }
+
+ drmModeAtomicFree(req);
+ return true;
+}
+
+
+bool DrmOutput::presentLegacy(DrmBuffer *buffer)
+{
+ if (m_nextBuffer) {
+ return false;
+ }
+ if (!LogindIntegration::self()->isActiveSession()) {
+ m_nextBuffer = buffer;
+ return false;
+ }
+ if (m_dpmsMode != DpmsMode::On) {
+ return false;
+ }
+
+ // Do we need to set a new mode first?
+ if (m_lastStride != buffer->stride() || m_lastGbm != buffer->isGbm()){
+ if (!setModeLegacy(buffer))
+ return false;
+ }
+ int errno_save = 0;
+ const bool ok = drmModePageFlip(m_backend->fd(), m_crtcId, buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0;
+ if (ok) {
+ m_nextBuffer = buffer;
+ } else {
+ errno_save = errno;
+ qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno);
+ delete buffer;
+ }
+ return ok;
+}
+
+bool DrmOutput::setModeLegacy(DrmBuffer *buffer)
+{
+ if (drmModeSetCrtc(m_backend->fd(), m_crtcId, buffer->bufferId(), 0, 0, &m_connector, 1, &m_mode) == 0) {
+ m_lastStride = buffer->stride();
+ m_lastGbm = buffer->isGbm();
+ return true;
+ } else {
+ qCWarning(KWIN_DRM) << "Mode setting failed";
+ return false;
+ }
+}
+
+DrmObject::AtomicReturn DrmOutput::atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable)
+{
+ if (enable) {
+ m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::SrcW), m_mode.hdisplay << 16);
+ m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::SrcH), m_mode.vdisplay << 16);
+ m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::CrtcW), m_mode.hdisplay);
+ m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::CrtcH), m_mode.vdisplay);
+ } else {
+ m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::SrcW), 0);
+ m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::SrcH), 0);
+ m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::CrtcW), 0);
+ m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::CrtcH), 0);
+ }
+
+ bool ret = true;
+
+ m_crtc->setPropsPending(0);
+ m_conn->setPropsPending(0);
+
+ ret &= m_conn->atomicAddProperty(req, int(DrmConnector::PropertyIndex::CrtcId), enable ? m_crtc->id() : 0);
+ ret &= m_crtc->atomicAddProperty(req, int(DrmCrtc::PropertyIndex::ModeId), enable ? m_blobId : 0);
+ ret &= m_crtc->atomicAddProperty(req, int(DrmCrtc::PropertyIndex::Active), enable);
+
+ if (!ret) {
+ qCWarning(KWIN_DRM) << "Failed to populate atomic modeset";
+ return DrmObject::AtomicReturn::Error;
+ }
+ if (!m_crtc->propsPending() && !m_conn->propsPending()) {
+ return DrmObject::AtomicReturn::NoChange;
+ }
+ return DrmObject::AtomicReturn::Success;
+}
}
diff --git a/plugins/platforms/drm/drm_output.h b/plugins/platforms/drm/drm_output.h
index 628bb5da95..aef6b8a9dd 100644
--- a/plugins/platforms/drm/drm_output.h
+++ b/plugins/platforms/drm/drm_output.h
@@ -21,11 +21,13 @@ along with this program. If not, see .
#define KWIN_DRM_OUTPUT_H
#include "drm_pointer.h"
+#include "drm_object.h"
#include
#include
#include
#include
+#include
#include
namespace KWayland
@@ -44,6 +46,9 @@ namespace KWin
class DrmBackend;
class DrmBuffer;
+class DrmPlane;
+class DrmConnector;
+class DrmCrtc;
class DrmOutput : public QObject
{
@@ -59,9 +64,9 @@ public:
void showCursor(DrmBuffer *buffer);
void hideCursor();
void moveCursor(const QPoint &globalPos);
+ bool init(drmModeConnector *connector);
bool present(DrmBuffer *buffer);
void pageFlipped();
- bool init(drmModeConnector *connector);
void restoreSaved();
bool blank();
@@ -75,6 +80,7 @@ public:
QRect geometry() const;
QString name() const;
int currentRefreshRate() const;
+ // These values are defined by the kernel
enum class DpmsMode {
On = DRM_MODE_DPMS_ON,
Standby = DRM_MODE_DPMS_STANDBY,
@@ -97,13 +103,20 @@ private:
friend class DrmBackend;
DrmOutput(DrmBackend *backend);
void cleanupBlackBuffer();
- bool setMode(DrmBuffer *buffer);
+ bool presentAtomically(DrmBuffer *buffer);
+ bool presentLegacy(DrmBuffer *buffer);
+ bool setModeLegacy(DrmBuffer *buffer);
void initEdid(drmModeConnector *connector);
void initDpms(drmModeConnector *connector);
bool isCurrentMode(const drmModeModeInfo *mode) const;
void initUuid();
void setGlobalPos(const QPoint &pos);
+ void pageFlippedBufferRemover(DrmBuffer *oldbuffer, DrmBuffer *newbuffer);
+ bool initPrimaryPlane();
+ bool initCursorPlane();
+ DrmObject::AtomicReturn atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable);
+
DrmBackend *m_backend;
QPoint m_globalPos;
quint32 m_crtcId = 0;
@@ -112,10 +125,11 @@ private:
bool m_lastGbm = false;
drmModeModeInfo m_mode;
DrmBuffer *m_currentBuffer = nullptr;
+ DrmBuffer *m_nextBuffer = nullptr;
DrmBuffer *m_blackBuffer = nullptr;
struct CrtcCleanup {
static void inline cleanup(_drmModeCrtc *ptr) {
- drmModeFreeCrtc(ptr);
+ drmModeFreeCrtc(ptr); // TODO: Atomically? See compositor-drm.c l.3670
}
};
Edid m_edid;
@@ -126,8 +140,14 @@ private:
KWin::ScopedDrmPointer<_drmModeProperty, &drmModeFreeProperty> m_dpms;
DpmsMode m_dpmsMode = DpmsMode::On;
QByteArray m_uuid;
-};
+ DrmConnector *m_conn = nullptr;
+ DrmCrtc *m_crtc = nullptr;
+ uint32_t m_blobId = 0;
+ DrmPlane* m_primaryPlane = nullptr;
+ DrmPlane* m_cursorPlane = nullptr;
+ QVector m_planesFlipList;
+};
}
diff --git a/plugins/platforms/drm/egl_gbm_backend.cpp b/plugins/platforms/drm/egl_gbm_backend.cpp
index 9ed4c03cdc..38e130be65 100644
--- a/plugins/platforms/drm/egl_gbm_backend.cpp
+++ b/plugins/platforms/drm/egl_gbm_backend.cpp
@@ -234,10 +234,8 @@ void EglGbmBackend::present()
void EglGbmBackend::presentOnOutput(EglGbmBackend::Output &o)
{
eglSwapBuffers(eglDisplay(), o.eglSurface);
- auto oldBuffer = o.buffer;
o.buffer = m_backend->createBuffer(o.gbmSurface);
m_backend->present(o.buffer, o.output);
- delete oldBuffer;
if (supportsBufferAge()) {
eglQuerySurface(eglDisplay(), o.eglSurface, EGL_BUFFER_AGE_EXT, &o.bufferAge);
}