[DRM plugin] Correct Atomic Mode Setting
This patch makes the AMS execution path work with the new DrmCrtc and DrmBuffer structure and solves major issues about: * VT switching * DPMS * Hot plugging * Logout * Memory leaks Test Plan: Tested with Gl and QPainter. Reviewers: #kwin Subscribers: kwin, #kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D5191
This commit is contained in:
parent
efedddd905
commit
d15cb52682
12 changed files with 430 additions and 343 deletions
|
@ -154,8 +154,10 @@ void DrmBackend::checkOutputsAreOn()
|
|||
void DrmBackend::activate(bool active)
|
||||
{
|
||||
if (active) {
|
||||
qCDebug(KWIN_DRM) << "Activating session.";
|
||||
reactivate();
|
||||
} else {
|
||||
qCDebug(KWIN_DRM) << "Deactivating session.";
|
||||
deactivate();
|
||||
}
|
||||
}
|
||||
|
@ -171,7 +173,9 @@ void DrmBackend::reactivate()
|
|||
const QPoint cp = Cursor::pos() - softwareCursorHotspot();
|
||||
for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) {
|
||||
DrmOutput *o = *it;
|
||||
o->pageFlipped();
|
||||
// only relevant in atomic mode
|
||||
o->m_modesetRequested = true;
|
||||
o->pageFlipped(); // TODO: Do we really need this?
|
||||
o->m_crtc->blank();
|
||||
o->showCursor(c);
|
||||
o->moveCursor(cp);
|
||||
|
@ -214,6 +218,12 @@ void DrmBackend::pageFlipHandler(int fd, unsigned int frame, unsigned int sec, u
|
|||
if (output->m_backend->m_pageFlipsPending == 0) {
|
||||
// TODO: improve, this currently means we wait for all page flips or all outputs.
|
||||
// It would be better to driver the repaint per output
|
||||
|
||||
if (output->m_dpmsAtomicOffPending) {
|
||||
output->m_modesetRequested = true;
|
||||
output->dpmsAtomicOff();
|
||||
}
|
||||
|
||||
if (Compositor::self()) {
|
||||
Compositor::self()->bufferSwapComplete();
|
||||
}
|
||||
|
@ -268,12 +278,12 @@ void DrmBackend::openDrm()
|
|||
// 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);
|
||||
DrmPlane *p = new DrmPlane(kplane->plane_id, this);
|
||||
if (p->atomicInit()) {
|
||||
m_planes << p;
|
||||
if (p->type() == DrmPlane::TypeIndex::Overlay) {
|
||||
m_overlayPlanes << p;
|
||||
}
|
||||
} else {
|
||||
delete p;
|
||||
}
|
||||
|
@ -297,26 +307,26 @@ void DrmBackend::openDrm()
|
|||
}
|
||||
|
||||
for (int i = 0; i < res->count_connectors; ++i) {
|
||||
m_connectors << new DrmConnector(res->connectors[i], m_fd);
|
||||
m_connectors << new DrmConnector(res->connectors[i], this);
|
||||
}
|
||||
for (int i = 0; i < res->count_crtcs; ++i) {
|
||||
m_crtcs << new DrmCrtc(res->crtcs[i], m_fd, i);
|
||||
m_crtcs << new DrmCrtc(res->crtcs[i], this, i);
|
||||
}
|
||||
|
||||
if (m_atomicModeSetting) {
|
||||
auto tryInit = [] (DrmObject *o) -> bool {
|
||||
if (o->init()) {
|
||||
auto tryAtomicInit = [] (DrmObject *obj) -> bool {
|
||||
if (obj->atomicInit()) {
|
||||
return false;
|
||||
} else {
|
||||
delete o;
|
||||
delete obj;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
m_connectors.erase(std::remove_if(m_connectors.begin(), m_connectors.end(), tryInit), m_connectors.end());
|
||||
m_crtcs.erase(std::remove_if(m_crtcs.begin(), m_crtcs.end(), tryInit), m_crtcs.end());
|
||||
m_connectors.erase(std::remove_if(m_connectors.begin(), m_connectors.end(), tryAtomicInit), m_connectors.end());
|
||||
m_crtcs.erase(std::remove_if(m_crtcs.begin(), m_crtcs.end(), tryAtomicInit), m_crtcs.end());
|
||||
}
|
||||
|
||||
queryResources();
|
||||
updateOutputs();
|
||||
|
||||
if (m_outputs.isEmpty()) {
|
||||
qCWarning(KWIN_DRM) << "No outputs, cannot render, will terminate now";
|
||||
|
@ -341,7 +351,7 @@ void DrmBackend::openDrm()
|
|||
}
|
||||
if (device->hasProperty("HOTPLUG", "1")) {
|
||||
qCDebug(KWIN_DRM) << "Received hot plug event for monitored drm device";
|
||||
queryResources();
|
||||
updateOutputs();
|
||||
m_cursorIndex = (m_cursorIndex + 1) % 2;
|
||||
updateCursor();
|
||||
}
|
||||
|
@ -355,7 +365,7 @@ void DrmBackend::openDrm()
|
|||
initCursor();
|
||||
}
|
||||
|
||||
void DrmBackend::queryResources()
|
||||
void DrmBackend::updateOutputs()
|
||||
{
|
||||
if (m_fd < 0) {
|
||||
return;
|
||||
|
@ -717,5 +727,4 @@ void DrmBackend::outputDpmsChanged()
|
|||
setOutputsEnabled(enabled);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -90,6 +90,9 @@ public:
|
|||
QVector<DrmPlane*> planes() const {
|
||||
return m_planes;
|
||||
}
|
||||
QVector<DrmPlane*> overlayPlanes() const {
|
||||
return m_overlayPlanes;
|
||||
}
|
||||
|
||||
void outputWentOff();
|
||||
void checkOutputsAreOn();
|
||||
|
@ -128,7 +131,7 @@ private:
|
|||
void activate(bool active);
|
||||
void reactivate();
|
||||
void deactivate();
|
||||
void queryResources();
|
||||
void updateOutputs();
|
||||
void setCursor();
|
||||
void updateCursor();
|
||||
void moveCursor();
|
||||
|
@ -157,6 +160,7 @@ private:
|
|||
bool m_active = false;
|
||||
// all available planes: primarys, cursors and overlays
|
||||
QVector<DrmPlane*> m_planes;
|
||||
QVector<DrmPlane*> m_overlayPlanes;
|
||||
QScopedPointer<DpmsInputEventFilter> m_dpmsFilter;
|
||||
KWayland::Server::OutputManagementInterface *m_outputManagement = nullptr;
|
||||
gbm_device *m_gbmDevice = nullptr;
|
||||
|
|
|
@ -18,6 +18,8 @@ You should have received a copy of the GNU General Public License
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#include "drm_object.h"
|
||||
|
||||
#include "drm_backend.h"
|
||||
#include "logging.h"
|
||||
|
||||
namespace KWin
|
||||
|
@ -27,8 +29,8 @@ namespace KWin
|
|||
* Defintions for class DrmObject
|
||||
*/
|
||||
|
||||
DrmObject::DrmObject(uint32_t object_id, int fd)
|
||||
: m_fd(fd)
|
||||
DrmObject::DrmObject(uint32_t object_id, DrmBackend *backend)
|
||||
: m_backend(backend)
|
||||
, m_id(object_id)
|
||||
{
|
||||
}
|
||||
|
@ -43,7 +45,7 @@ void DrmObject::initProp(int n, drmModeObjectProperties *properties, QVector<QBy
|
|||
{
|
||||
m_props.resize(m_propsNames.size());
|
||||
for (unsigned int i = 0; i < properties->count_props; ++i) {
|
||||
drmModePropertyRes *prop = drmModeGetProperty(m_fd, properties->props[i]);
|
||||
drmModePropertyRes *prop = drmModeGetProperty(m_backend->fd(), properties->props[i]);
|
||||
if (!prop) {
|
||||
continue;
|
||||
}
|
||||
|
@ -56,27 +58,27 @@ void DrmObject::initProp(int n, drmModeObjectProperties *properties, QVector<QBy
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
if (drmModeAtomicAddProperty(req, m_id, m_props[prop]->propId(), value) <= 0) {
|
||||
qCWarning(KWIN_DRM) << "Adding property" << m_propsNames[prop] << "to atomic commit failed for object" << this;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DrmObject::atomicPopulate(drmModeAtomicReq *req)
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
for (int i = 0; i < m_props.size(); i++) {
|
||||
ret &= atomicAddProperty(req, i, m_props[i]->value());
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
qCWarning(KWIN_DRM) << "Failed to populate atomic plane" << m_id;
|
||||
return false;
|
||||
}
|
||||
m_propsPending |= mask;
|
||||
m_propsValid &= ~mask;
|
||||
// adding property was successful
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,73 +30,58 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
namespace KWin
|
||||
{
|
||||
|
||||
class DrmBackend;
|
||||
class DrmOutput;
|
||||
|
||||
class DrmObject
|
||||
{
|
||||
public:
|
||||
// creates drm object by its id delivered by the kernel
|
||||
DrmObject(uint32_t object_id, int fd);
|
||||
DrmObject(uint32_t object_id, DrmBackend *backend);
|
||||
|
||||
virtual ~DrmObject() = 0;
|
||||
|
||||
enum class AtomicReturn {
|
||||
NoChange,
|
||||
Success,
|
||||
Error
|
||||
};
|
||||
|
||||
virtual bool init() = 0;
|
||||
virtual bool atomicInit() = 0;
|
||||
|
||||
uint32_t id() const {
|
||||
return m_id;
|
||||
}
|
||||
|
||||
DrmOutput* output() const {
|
||||
DrmOutput *output() const {
|
||||
return m_output;
|
||||
}
|
||||
void setOutput(DrmOutput* output) {
|
||||
m_output = output;
|
||||
}
|
||||
|
||||
uint32_t propId(int index) {
|
||||
return m_props[index]->propId();
|
||||
uint32_t propId(int prop) {
|
||||
return m_props[prop]->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;
|
||||
uint64_t value(int prop) {
|
||||
return m_props[prop]->value();
|
||||
}
|
||||
|
||||
bool atomicAddProperty(drmModeAtomicReq *req, int prop, uint64_t value);
|
||||
void setValue(int prop, uint64_t new_value)
|
||||
{
|
||||
Q_ASSERT(prop < m_props.size());
|
||||
m_props[prop]->setValue(new_value);
|
||||
}
|
||||
|
||||
virtual bool atomicPopulate(drmModeAtomicReq *req);
|
||||
|
||||
protected:
|
||||
const int m_fd = 0;
|
||||
const uint32_t m_id = 0;
|
||||
|
||||
DrmOutput *m_output = nullptr;
|
||||
|
||||
QVector<QByteArray> m_propsNames; // for comparision with received name of DRM object
|
||||
class Property;
|
||||
QVector<Property*> 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<QByteArray> enumNames = QVector<QByteArray>(0));
|
||||
bool atomicAddProperty(drmModeAtomicReq *req, int prop, uint64_t value);
|
||||
|
||||
DrmBackend *m_backend;
|
||||
const uint32_t m_id = 0;
|
||||
DrmOutput *m_output = nullptr;
|
||||
|
||||
// for comparision with received name of DRM object
|
||||
QVector<QByteArray> m_propsNames;
|
||||
class Property;
|
||||
QVector<Property *> m_props;
|
||||
|
||||
class Property
|
||||
{
|
||||
|
@ -113,7 +98,7 @@ protected:
|
|||
uint32_t propId() {
|
||||
return m_propId;
|
||||
}
|
||||
uint32_t value() {
|
||||
uint64_t value() {
|
||||
return m_value;
|
||||
}
|
||||
void setValue(uint64_t new_value) {
|
||||
|
|
|
@ -18,16 +18,17 @@ You should have received a copy of the GNU General Public License
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#include "drm_object_connector.h"
|
||||
#include "drm_backend.h"
|
||||
#include "drm_pointer.h"
|
||||
#include "logging.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
DrmConnector::DrmConnector(uint32_t connector_id, int fd)
|
||||
: DrmObject(connector_id, fd)
|
||||
DrmConnector::DrmConnector(uint32_t connector_id, DrmBackend *backend)
|
||||
: DrmObject(connector_id, backend)
|
||||
{
|
||||
ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> con(drmModeGetConnector(fd, connector_id));
|
||||
ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> con(drmModeGetConnector(backend->fd(), connector_id));
|
||||
if (!con) {
|
||||
return;
|
||||
}
|
||||
|
@ -38,7 +39,7 @@ DrmConnector::DrmConnector(uint32_t connector_id, int fd)
|
|||
|
||||
DrmConnector::~DrmConnector() = default;
|
||||
|
||||
bool DrmConnector::init()
|
||||
bool DrmConnector::atomicInit()
|
||||
{
|
||||
qCDebug(KWIN_DRM) << "Creating connector" << m_id;
|
||||
|
||||
|
@ -54,7 +55,7 @@ bool DrmConnector::initProps()
|
|||
QByteArrayLiteral("CRTC_ID"),
|
||||
};
|
||||
|
||||
drmModeObjectProperties *properties = drmModeObjectGetProperties(m_fd, m_id, DRM_MODE_OBJECT_CONNECTOR);
|
||||
drmModeObjectProperties *properties = drmModeObjectGetProperties(m_backend->fd(), m_id, DRM_MODE_OBJECT_CONNECTOR);
|
||||
if (!properties) {
|
||||
qCWarning(KWIN_DRM) << "Failed to get properties for connector " << m_id ;
|
||||
return false;
|
||||
|
@ -70,7 +71,7 @@ bool DrmConnector::initProps()
|
|||
|
||||
bool DrmConnector::isConnected()
|
||||
{
|
||||
ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> con(drmModeGetConnector(m_fd, m_id));
|
||||
ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> con(drmModeGetConnector(m_backend->fd(), m_id));
|
||||
if (!con) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -28,11 +28,11 @@ namespace KWin
|
|||
class DrmConnector : public DrmObject
|
||||
{
|
||||
public:
|
||||
DrmConnector(uint32_t connector_id, int fd);
|
||||
DrmConnector(uint32_t connector_id, DrmBackend *backend);
|
||||
|
||||
virtual ~DrmConnector();
|
||||
|
||||
bool init();
|
||||
bool atomicInit();
|
||||
|
||||
enum class PropertyIndex {
|
||||
CrtcId = 0,
|
||||
|
|
|
@ -26,8 +26,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
namespace KWin
|
||||
{
|
||||
|
||||
DrmCrtc::DrmCrtc(uint32_t crtc_id, int fd, int resIndex)
|
||||
: DrmObject(crtc_id, fd),
|
||||
DrmCrtc::DrmCrtc(uint32_t crtc_id, DrmBackend *backend, int resIndex)
|
||||
: DrmObject(crtc_id, backend),
|
||||
m_resIndex(resIndex)
|
||||
{
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ DrmCrtc::~DrmCrtc()
|
|||
{
|
||||
}
|
||||
|
||||
bool DrmCrtc::init()
|
||||
bool DrmCrtc::atomicInit()
|
||||
{
|
||||
qCDebug(KWIN_DRM) << "Atomic init for CRTC:" << resIndex() << "id:" << m_id;
|
||||
|
||||
|
@ -53,7 +53,7 @@ bool DrmCrtc::initProps()
|
|||
QByteArrayLiteral("ACTIVE"),
|
||||
};
|
||||
|
||||
drmModeObjectProperties *properties = drmModeObjectGetProperties(m_fd, m_id, DRM_MODE_OBJECT_CRTC);
|
||||
drmModeObjectProperties *properties = drmModeObjectGetProperties(m_backend->fd(), m_id, DRM_MODE_OBJECT_CRTC);
|
||||
if (!properties) {
|
||||
qCWarning(KWIN_DRM) << "Failed to get properties for crtc " << m_id ;
|
||||
return false;
|
||||
|
@ -69,7 +69,7 @@ bool DrmCrtc::initProps()
|
|||
|
||||
void DrmCrtc::flipBuffer()
|
||||
{
|
||||
if (m_currentBuffer && m_output->m_backend->deleteBufferAfterPageFlip() && m_currentBuffer != m_nextBuffer) {
|
||||
if (m_currentBuffer && m_backend->deleteBufferAfterPageFlip() && m_currentBuffer != m_nextBuffer) {
|
||||
delete m_currentBuffer;
|
||||
}
|
||||
m_currentBuffer = m_nextBuffer;
|
||||
|
@ -82,7 +82,7 @@ void DrmCrtc::flipBuffer()
|
|||
bool DrmCrtc::blank()
|
||||
{
|
||||
if (!m_blackBuffer) {
|
||||
DrmDumbBuffer *blackBuffer = m_output->m_backend->createBuffer(m_output->pixelSize());
|
||||
DrmDumbBuffer *blackBuffer = m_backend->createBuffer(m_output->pixelSize());
|
||||
if (!blackBuffer->map()) {
|
||||
delete blackBuffer;
|
||||
return false;
|
||||
|
@ -91,9 +91,8 @@ bool DrmCrtc::blank()
|
|||
m_blackBuffer = blackBuffer;
|
||||
}
|
||||
|
||||
// TODO: Do this atomically
|
||||
if (m_output->setModeLegacy(m_blackBuffer)) {
|
||||
if (m_currentBuffer && m_output->m_backend->deleteBufferAfterPageFlip()) {
|
||||
if (m_currentBuffer && m_backend->deleteBufferAfterPageFlip()) {
|
||||
delete m_currentBuffer;
|
||||
delete m_nextBuffer;
|
||||
}
|
||||
|
|
|
@ -32,11 +32,11 @@ class DrmDumbBuffer;
|
|||
class DrmCrtc : public DrmObject
|
||||
{
|
||||
public:
|
||||
DrmCrtc(uint32_t crtc_id, int fd, int resIndex);
|
||||
DrmCrtc(uint32_t crtc_id, DrmBackend *backend, int resIndex);
|
||||
|
||||
virtual ~DrmCrtc();
|
||||
|
||||
bool init();
|
||||
bool atomicInit();
|
||||
|
||||
enum class PropertyIndex {
|
||||
ModeId = 0,
|
||||
|
@ -64,7 +64,6 @@ public:
|
|||
bool blank();
|
||||
|
||||
private:
|
||||
DrmBackend *m_backend;
|
||||
int m_resIndex;
|
||||
|
||||
DrmBuffer *m_currentBuffer = nullptr;
|
||||
|
|
|
@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#include "drm_object_plane.h"
|
||||
#include "drm_backend.h"
|
||||
#include "drm_buffer.h"
|
||||
#include "drm_pointer.h"
|
||||
#include "logging.h"
|
||||
|
@ -25,17 +26,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
namespace KWin
|
||||
{
|
||||
|
||||
DrmPlane::DrmPlane(uint32_t plane_id, int fd)
|
||||
: DrmObject(plane_id, fd)
|
||||
DrmPlane::DrmPlane(uint32_t plane_id, DrmBackend *backend)
|
||||
: DrmObject(plane_id, backend)
|
||||
{
|
||||
}
|
||||
|
||||
DrmPlane::~DrmPlane() = default;
|
||||
|
||||
bool DrmPlane::init()
|
||||
DrmPlane::~DrmPlane()
|
||||
{
|
||||
qCDebug(KWIN_DRM) << "Initialize plane" << m_id;
|
||||
ScopedDrmPointer<_drmModePlane, &drmModeFreePlane> p(drmModeGetPlane(m_fd, m_id));
|
||||
delete m_current;
|
||||
delete m_next;
|
||||
}
|
||||
|
||||
bool DrmPlane::atomicInit()
|
||||
{
|
||||
qCDebug(KWIN_DRM) << "Atomic init for plane:" << m_id;
|
||||
ScopedDrmPointer<_drmModePlane, &drmModeFreePlane> p(drmModeGetPlane(m_backend->fd(), m_id));
|
||||
|
||||
if (!p) {
|
||||
qCWarning(KWIN_DRM) << "Failed to get kernel plane" << m_id;
|
||||
|
@ -44,8 +49,9 @@ bool DrmPlane::init()
|
|||
|
||||
m_possibleCrtcs = p->possible_crtcs;
|
||||
|
||||
m_formats.resize(p->count_formats);
|
||||
for (int i = 0; i < p->count_formats; i++) {
|
||||
int count_formats = p->count_formats;
|
||||
m_formats.resize(count_formats);
|
||||
for (int i = 0; i < count_formats; i++) {
|
||||
m_formats[i] = p->formats[i];
|
||||
}
|
||||
|
||||
|
@ -77,7 +83,7 @@ bool DrmPlane::initProps()
|
|||
QByteArrayLiteral("Overlay"),
|
||||
};
|
||||
|
||||
drmModeObjectProperties *properties = drmModeObjectGetProperties(m_fd, m_id, DRM_MODE_OBJECT_PLANE);
|
||||
drmModeObjectProperties *properties = drmModeObjectGetProperties(m_backend->fd(), m_id, DRM_MODE_OBJECT_PLANE);
|
||||
if (!properties){
|
||||
qCWarning(KWIN_DRM) << "Failed to get properties for plane " << m_id ;
|
||||
return false;
|
||||
|
@ -98,77 +104,48 @@ bool DrmPlane::initProps()
|
|||
|
||||
DrmPlane::TypeIndex DrmPlane::type()
|
||||
{
|
||||
uint64_t value = propValue(int(PropertyIndex::Type));
|
||||
uint64_t v = value(int(PropertyIndex::Type));
|
||||
int typeCount = int(TypeIndex::Count);
|
||||
for (int i = 0; i < typeCount; i++) {
|
||||
if (m_props[int(PropertyIndex::Type)]->enumMap(i) == value) {
|
||||
if (m_props[int(PropertyIndex::Type)]->enumMap(i) == v) {
|
||||
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::setNext(DrmBuffer *b){
|
||||
setValue(int(PropertyIndex::FbId), b ? b->bufferId() : 0);
|
||||
m_next = b;
|
||||
}
|
||||
|
||||
|
||||
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 DrmPlane::atomicPopulate(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);
|
||||
for (int i = 1; i < m_props.size(); i++) {
|
||||
ret &= atomicAddProperty(req, i, m_props[i]->value());
|
||||
}
|
||||
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
if (!m_propsPending) {
|
||||
return DrmObject::AtomicReturn::NoChange;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DrmPlane::flipBuffer()
|
||||
{
|
||||
m_current = m_next;
|
||||
m_next = nullptr;
|
||||
}
|
||||
|
||||
void DrmPlane::flipBufferWithDelete()
|
||||
{
|
||||
if (m_current != m_next) {
|
||||
delete m_current;
|
||||
}
|
||||
return DrmObject::AtomicReturn::Success;
|
||||
flipBuffer();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,9 +32,9 @@ class DrmBuffer;
|
|||
class DrmPlane : public DrmObject
|
||||
{
|
||||
public:
|
||||
DrmPlane(uint32_t plane_id, int fd);
|
||||
DrmPlane(uint32_t plane_id, DrmBackend *backend);
|
||||
|
||||
virtual ~DrmPlane();
|
||||
~DrmPlane();
|
||||
|
||||
enum class PropertyIndex {
|
||||
Type = 0,
|
||||
|
@ -58,36 +58,31 @@ public:
|
|||
Count
|
||||
};
|
||||
|
||||
bool init();
|
||||
bool atomicInit();
|
||||
bool initProps();
|
||||
TypeIndex type();
|
||||
bool isCrtcSupported(uint32_t crtc);
|
||||
DrmObject::AtomicReturn atomicReqPlanePopulate(drmModeAtomicReq *req);
|
||||
|
||||
DrmBuffer *current(){
|
||||
return m_current;
|
||||
bool isCrtcSupported(int resIndex) const {
|
||||
return (m_possibleCrtcs & (1 << resIndex));
|
||||
}
|
||||
DrmBuffer *next(){
|
||||
return m_next;
|
||||
}
|
||||
void setCurrent(DrmBuffer *b){
|
||||
m_current = b;
|
||||
}
|
||||
void setNext(DrmBuffer *b){
|
||||
m_next = b;
|
||||
}
|
||||
|
||||
QVector<uint32_t> formats(){
|
||||
QVector<uint32_t> formats() const {
|
||||
return m_formats;
|
||||
}
|
||||
void setFormats(uint32_t const *f, int fcount);
|
||||
|
||||
void setPossibleCrtcs(uint32_t value){
|
||||
m_possibleCrtcs = value;
|
||||
DrmBuffer *current() const {
|
||||
return m_current;
|
||||
}
|
||||
uint32_t possibleCrtcs(){
|
||||
return m_possibleCrtcs;
|
||||
DrmBuffer *next() const {
|
||||
return m_next;
|
||||
}
|
||||
void setCurrent(DrmBuffer *b) {
|
||||
m_current = b;
|
||||
}
|
||||
void setNext(DrmBuffer *b);
|
||||
|
||||
bool atomicPopulate(drmModeAtomicReq *req);
|
||||
void flipBuffer();
|
||||
void flipBufferWithDelete();
|
||||
|
||||
private:
|
||||
DrmBuffer *m_current = nullptr;
|
||||
|
|
|
@ -63,6 +63,20 @@ DrmOutput::~DrmOutput()
|
|||
{
|
||||
hideCursor();
|
||||
m_crtc->blank();
|
||||
|
||||
if (m_primaryPlane) {
|
||||
// TODO: when having multiple planes, also clean up these
|
||||
m_primaryPlane->setOutput(nullptr);
|
||||
|
||||
if (m_backend->deleteBufferAfterPageFlip()) {
|
||||
delete m_primaryPlane->current();
|
||||
}
|
||||
m_primaryPlane->setCurrent(nullptr);
|
||||
}
|
||||
|
||||
m_crtc->setOutput(nullptr);
|
||||
m_conn->setOutput(nullptr);
|
||||
|
||||
delete m_waylandOutput.data();
|
||||
delete m_waylandOutputDevice.data();
|
||||
}
|
||||
|
@ -72,10 +86,8 @@ void DrmOutput::releaseGbm()
|
|||
if (DrmBuffer *b = m_crtc->current()) {
|
||||
b->releaseGbm();
|
||||
}
|
||||
if (m_primaryPlane) {
|
||||
if (m_primaryPlane->current()) {
|
||||
m_primaryPlane->current()->releaseGbm();
|
||||
}
|
||||
if (m_primaryPlane && m_primaryPlane->current()) {
|
||||
m_primaryPlane->current()->releaseGbm();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,10 +188,10 @@ bool DrmOutput::init(drmModeConnector *connector)
|
|||
if (!initPrimaryPlane()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!m_crtc->blank()) {
|
||||
} else if (!m_crtc->blank()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
setDpms(DpmsMode::On);
|
||||
if (!m_waylandOutput.isNull()) {
|
||||
delete m_waylandOutput.data();
|
||||
|
@ -496,7 +508,7 @@ bool DrmOutput::initPrimaryPlane()
|
|||
if (m_primaryPlane) { // Output already has a primary plane
|
||||
continue;
|
||||
}
|
||||
if (!p->isCrtcSupported(m_crtc->id())) {
|
||||
if (!p->isCrtcSupported(m_crtc->resIndex())) {
|
||||
continue;
|
||||
}
|
||||
p->setOutput(this);
|
||||
|
@ -524,7 +536,7 @@ bool DrmOutput::initCursorPlane() // TODO: Add call in init (but needs lay
|
|||
if (m_cursorPlane) { // Output already has a cursor plane
|
||||
continue;
|
||||
}
|
||||
if (!p->isCrtcSupported(m_crtc->id())) {
|
||||
if (!p->isCrtcSupported(m_crtc->resIndex())) {
|
||||
continue;
|
||||
}
|
||||
p->setOutput(this);
|
||||
|
@ -554,45 +566,70 @@ void DrmOutput::setDpms(DrmOutput::DpmsMode mode)
|
|||
if (m_dpms.isNull()) {
|
||||
return;
|
||||
}
|
||||
if (mode == m_dpmsMode) {
|
||||
if (mode == m_dpmsModePending) {
|
||||
qCDebug(KWIN_DRM) << "New DPMS mode equals old mode. DPMS unchanged.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_backend->atomicModeSetting()) {
|
||||
drmModeAtomicReq *req = drmModeAtomicAlloc();
|
||||
m_dpmsModePending = mode;
|
||||
|
||||
if (atomicReqModesetPopulate(req, mode == DpmsMode::On) == DrmObject::AtomicReturn::Error) {
|
||||
qCWarning(KWIN_DRM) << "Failed to populate atomic request for output" << m_crtc->id();
|
||||
return;
|
||||
}
|
||||
if (drmModeAtomicCommit(m_backend->fd(), req, DRM_MODE_ATOMIC_ALLOW_MODESET, this)) {
|
||||
qCWarning(KWIN_DRM) << "Failed to commit atomic request for output" << m_crtc->id();
|
||||
if (m_backend->atomicModeSetting()) {
|
||||
m_modesetRequested = true;
|
||||
if (mode == DpmsMode::On) {
|
||||
if (m_pageFlipPending) {
|
||||
m_pageFlipPending = false;
|
||||
Compositor::self()->bufferSwapComplete();
|
||||
}
|
||||
dpmsOnHandler();
|
||||
} else {
|
||||
qCDebug(KWIN_DRM) << "DPMS set for output" << m_crtc->id();
|
||||
m_dpmsAtomicOffPending = true;
|
||||
if (!m_pageFlipPending) {
|
||||
dpmsAtomicOff();
|
||||
}
|
||||
}
|
||||
drmModeAtomicFree(req);
|
||||
} else {
|
||||
if (drmModeConnectorSetProperty(m_backend->fd(), m_conn->id(), m_dpms->prop_id, uint64_t(mode)) < 0) {
|
||||
m_dpmsModePending = m_dpmsMode;
|
||||
qCWarning(KWIN_DRM) << "Setting DPMS failed";
|
||||
return;
|
||||
}
|
||||
if (mode == DpmsMode::On) {
|
||||
dpmsOnHandler();
|
||||
} else {
|
||||
dpmsOffHandler();
|
||||
}
|
||||
m_dpmsMode = m_dpmsModePending;
|
||||
}
|
||||
}
|
||||
|
||||
void DrmOutput::dpmsOnHandler()
|
||||
{
|
||||
qCDebug(KWIN_DRM) << "DPMS mode set for output" << m_crtc->id() << "to On.";
|
||||
|
||||
m_dpmsMode = mode;
|
||||
if (m_waylandOutput) {
|
||||
m_waylandOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsMode));
|
||||
m_waylandOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsModePending));
|
||||
}
|
||||
emit dpmsChanged();
|
||||
if (m_dpmsMode != DpmsMode::On) {
|
||||
m_backend->outputWentOff();
|
||||
} else {
|
||||
m_backend->checkOutputsAreOn();
|
||||
|
||||
m_backend->checkOutputsAreOn();
|
||||
if (!m_backend->atomicModeSetting()) {
|
||||
m_crtc->blank();
|
||||
if (Compositor *compositor = Compositor::self()) {
|
||||
compositor->addRepaintFull();
|
||||
}
|
||||
}
|
||||
if (Compositor *compositor = Compositor::self()) {
|
||||
compositor->addRepaintFull();
|
||||
}
|
||||
}
|
||||
|
||||
void DrmOutput::dpmsOffHandler()
|
||||
{
|
||||
qCDebug(KWIN_DRM) << "DPMS mode set for output" << m_crtc->id() << "to Off.";
|
||||
|
||||
if (m_waylandOutput) {
|
||||
m_waylandOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsModePending));
|
||||
}
|
||||
emit dpmsChanged();
|
||||
|
||||
m_backend->outputWentOff();
|
||||
}
|
||||
|
||||
QString DrmOutput::name() const
|
||||
|
@ -681,36 +718,51 @@ bool DrmOutput::commitChanges()
|
|||
|
||||
void DrmOutput::pageFlipped()
|
||||
{
|
||||
m_pageFlipPending = false;
|
||||
|
||||
if (!m_crtc) {
|
||||
return;
|
||||
}
|
||||
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_crtc->next()) {
|
||||
// on manual vt switch
|
||||
if (DrmBuffer *b = m_crtc->current()) {
|
||||
b->releaseGbm();
|
||||
// Egl based surface buffers get destroyed, QPainter based dumb buffers not
|
||||
// TODO: split up DrmOutput in two for dumb and egl/gbm surface buffer compatible subclasses completely?
|
||||
if (m_backend->deleteBufferAfterPageFlip()) {
|
||||
if (m_backend->atomicModeSetting()) {
|
||||
if (!m_primaryPlane->next()) {
|
||||
// on manual vt switch
|
||||
// TODO: when we later use overlay planes it might happen, that we have a page flip with only
|
||||
// damage on one of these, and therefore the primary plane has no next buffer
|
||||
// -> Then we don't want to return here!
|
||||
if (m_primaryPlane->current()) {
|
||||
m_primaryPlane->current()->releaseGbm();
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
for (DrmPlane *p : m_nextPlanesFlipList) {
|
||||
p->flipBufferWithDelete();
|
||||
}
|
||||
m_nextPlanesFlipList.clear();
|
||||
} else {
|
||||
if (!m_crtc->next()) {
|
||||
// on manual vt switch
|
||||
if (DrmBuffer *b = m_crtc->current()) {
|
||||
b->releaseGbm();
|
||||
}
|
||||
}
|
||||
m_crtc->flipBuffer();
|
||||
}
|
||||
} else {
|
||||
if (m_backend->atomicModeSetting()){
|
||||
for (DrmPlane *p : m_nextPlanesFlipList) {
|
||||
p->flipBuffer();
|
||||
}
|
||||
m_nextPlanesFlipList.clear();
|
||||
} else {
|
||||
m_crtc->flipBuffer();
|
||||
}
|
||||
m_crtc->flipBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
void DrmOutput::pageFlippedBufferRemover(DrmBuffer *oldbuffer, DrmBuffer *newbuffer)
|
||||
{
|
||||
if (oldbuffer && m_backend->deleteBufferAfterPageFlip() && oldbuffer != newbuffer) {
|
||||
delete oldbuffer;
|
||||
}
|
||||
}
|
||||
|
||||
bool DrmOutput::present(DrmBuffer *buffer)
|
||||
{
|
||||
if (!buffer || buffer->bufferId() == 0) {
|
||||
|
@ -723,110 +775,61 @@ bool DrmOutput::present(DrmBuffer *buffer)
|
|||
}
|
||||
}
|
||||
|
||||
bool DrmOutput::dpmsAtomicOff()
|
||||
{
|
||||
m_dpmsAtomicOffPending = false;
|
||||
|
||||
// TODO: With multiple planes: deactivate all of them here
|
||||
delete m_primaryPlane->next();
|
||||
m_primaryPlane->setNext(nullptr);
|
||||
m_nextPlanesFlipList << m_primaryPlane;
|
||||
|
||||
if (!doAtomicCommit(AtomicCommitMode::Test)) {
|
||||
qCDebug(KWIN_DRM) << "Atomic test commit to Dpms Off failed. Aborting.";
|
||||
return false;
|
||||
}
|
||||
if (!doAtomicCommit(AtomicCommitMode::Real)) {
|
||||
qCDebug(KWIN_DRM) << "Atomic commit to Dpms Off failed. This should have never happened! Aborting.";
|
||||
return false;
|
||||
}
|
||||
m_nextPlanesFlipList.clear();
|
||||
dpmsOffHandler();
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
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()) {
|
||||
|
||||
if (m_pageFlipPending) {
|
||||
qCWarning(KWIN_DRM) << "Page not yet flipped.";
|
||||
return false;
|
||||
}
|
||||
|
||||
DrmObject::AtomicReturn ret;
|
||||
uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT;
|
||||
m_primaryPlane->setNext(buffer);
|
||||
m_nextPlanesFlipList << m_primaryPlane;
|
||||
|
||||
// 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";
|
||||
if (!doAtomicCommit(AtomicCommitMode::Test)) {
|
||||
//TODO: When we use planes for layered rendering, fallback to renderer instead. Also for direct scanout?
|
||||
qCDebug(KWIN_DRM) << "Atomic test commit failed. Aborting present.";
|
||||
if (this->m_backend->deleteBufferAfterPageFlip()) {
|
||||
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());
|
||||
if (!doAtomicCommit(AtomicCommitMode::Real)) {
|
||||
qCDebug(KWIN_DRM) << "Atomic commit failed. This should have never happened! Aborting present.";
|
||||
return false;
|
||||
}
|
||||
foreach (DrmPlane* p, m_planesFlipList) {
|
||||
p->setPropsValid(p->propsValid() | p->propsPending());
|
||||
}
|
||||
|
||||
drmModeAtomicFree(req);
|
||||
m_pageFlipPending = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool DrmOutput::presentLegacy(DrmBuffer *buffer)
|
||||
{
|
||||
if (m_crtc->next()) {
|
||||
|
@ -869,37 +872,135 @@ bool DrmOutput::setModeLegacy(DrmBuffer *buffer)
|
|||
}
|
||||
}
|
||||
|
||||
DrmObject::AtomicReturn DrmOutput::atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable)
|
||||
bool DrmOutput::doAtomicCommit(AtomicCommitMode mode)
|
||||
{
|
||||
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);
|
||||
drmModeAtomicReq *req = drmModeAtomicAlloc();
|
||||
|
||||
auto errorHandler = [this, mode, req] () {
|
||||
if (mode == AtomicCommitMode::Test) {
|
||||
// TODO: when we later test overlay planes, make sure we change only the right stuff back
|
||||
}
|
||||
if (req) {
|
||||
drmModeAtomicFree(req);
|
||||
}
|
||||
|
||||
if (m_dpmsMode != m_dpmsModePending) {
|
||||
qCWarning(KWIN_DRM) << "Setting DPMS failed";
|
||||
m_dpmsModePending = m_dpmsMode;
|
||||
if (m_dpmsMode != DpmsMode::On) {
|
||||
dpmsOffHandler();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: see above, rework later for overlay planes!
|
||||
for (DrmPlane *p : m_nextPlanesFlipList) {
|
||||
p->setNext(nullptr);
|
||||
}
|
||||
m_nextPlanesFlipList.clear();
|
||||
|
||||
};
|
||||
|
||||
if (!req) {
|
||||
qCWarning(KWIN_DRM) << "DRM: couldn't allocate atomic request";
|
||||
errorHandler();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t flags = 0;
|
||||
|
||||
// Do we need to set a new mode?
|
||||
if (m_modesetRequested) {
|
||||
if (m_dpmsModePending == DpmsMode::On) {
|
||||
if (drmModeCreatePropertyBlob(m_backend->fd(), &m_mode, sizeof(m_mode), &m_blobId) != 0) {
|
||||
qCWarning(KWIN_DRM) << "Failed to create property blob";
|
||||
errorHandler();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!atomicReqModesetPopulate(req, m_dpmsModePending == DpmsMode::On)){
|
||||
qCWarning(KWIN_DRM) << "Failed to populate Atomic Modeset";
|
||||
errorHandler();
|
||||
return false;
|
||||
}
|
||||
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
||||
}
|
||||
|
||||
if (mode == AtomicCommitMode::Real) {
|
||||
if (m_dpmsModePending == DpmsMode::On) {
|
||||
if (!(flags & DRM_MODE_ATOMIC_ALLOW_MODESET)) {
|
||||
// TODO: Evaluating this condition should only be necessary, as long as we expect older kernels than 4.10.
|
||||
flags |= DRM_MODE_ATOMIC_NONBLOCK;
|
||||
}
|
||||
flags |= DRM_MODE_PAGE_FLIP_EVENT;
|
||||
}
|
||||
} 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);
|
||||
flags |= DRM_MODE_ATOMIC_TEST_ONLY;
|
||||
}
|
||||
|
||||
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);
|
||||
// TODO: Make sure when we use more than one plane at a time, that we go through this list in the right order.
|
||||
for (int i = m_nextPlanesFlipList.size() - 1; 0 <= i; i-- ) {
|
||||
DrmPlane *p = m_nextPlanesFlipList[i];
|
||||
ret &= p->atomicPopulate(req);
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
qCWarning(KWIN_DRM) << "Failed to populate atomic modeset";
|
||||
return DrmObject::AtomicReturn::Error;
|
||||
qCWarning(KWIN_DRM) << "Failed to populate atomic planes. Abort atomic commit!";
|
||||
errorHandler();
|
||||
return false;
|
||||
}
|
||||
if (!m_crtc->propsPending() && !m_conn->propsPending()) {
|
||||
return DrmObject::AtomicReturn::NoChange;
|
||||
|
||||
if (drmModeAtomicCommit(m_backend->fd(), req, flags, this)) {
|
||||
qCWarning(KWIN_DRM) << "Atomic request failed to commit:" << strerror(errno);
|
||||
errorHandler();
|
||||
return false;
|
||||
}
|
||||
return DrmObject::AtomicReturn::Success;
|
||||
|
||||
if (mode == AtomicCommitMode::Real && (flags & DRM_MODE_ATOMIC_ALLOW_MODESET)) {
|
||||
qCDebug(KWIN_DRM) << "Atomic Modeset successful.";
|
||||
m_modesetRequested = false;
|
||||
m_dpmsMode = m_dpmsModePending;
|
||||
}
|
||||
|
||||
drmModeAtomicFree(req);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DrmOutput::atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcX), 0);
|
||||
m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcY), 0);
|
||||
m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcW), m_mode.hdisplay << 16);
|
||||
m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcH), m_mode.vdisplay << 16);
|
||||
m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcW), m_mode.hdisplay);
|
||||
m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcH), m_mode.vdisplay);
|
||||
m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcId), m_crtc->id());
|
||||
} else {
|
||||
if (m_backend->deleteBufferAfterPageFlip()) {
|
||||
delete m_primaryPlane->current();
|
||||
delete m_primaryPlane->next();
|
||||
}
|
||||
m_primaryPlane->setCurrent(nullptr);
|
||||
m_primaryPlane->setNext(nullptr);
|
||||
|
||||
m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcX), 0);
|
||||
m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcY), 0);
|
||||
m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcW), 0);
|
||||
m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcH), 0);
|
||||
m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcW), 0);
|
||||
m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcH), 0);
|
||||
m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcId), 0);
|
||||
}
|
||||
m_conn->setValue(int(DrmConnector::PropertyIndex::CrtcId), enable ? m_crtc->id() : 0);
|
||||
m_crtc->setValue(int(DrmCrtc::PropertyIndex::ModeId), enable ? m_blobId : 0);
|
||||
m_crtc->setValue(int(DrmCrtc::PropertyIndex::Active), enable);
|
||||
|
||||
bool ret = true;
|
||||
ret &= m_conn->atomicPopulate(req);
|
||||
ret &= m_crtc->atomicPopulate(req);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -95,7 +95,8 @@ public:
|
|||
};
|
||||
void setDpms(DpmsMode mode);
|
||||
bool isDpmsEnabled() const {
|
||||
return m_dpmsMode == DpmsMode::On;
|
||||
// We care for current as well as pending mode in order to allow first present in AMS.
|
||||
return m_dpmsModePending == DpmsMode::On;
|
||||
}
|
||||
|
||||
QByteArray uuid() const {
|
||||
|
@ -111,6 +112,13 @@ private:
|
|||
// and save the connector ids in the DrmCrtc instance.
|
||||
DrmOutput(DrmBackend *backend);
|
||||
bool presentAtomically(DrmBuffer *buffer);
|
||||
|
||||
enum class AtomicCommitMode {
|
||||
Test,
|
||||
Real
|
||||
};
|
||||
bool doAtomicCommit(AtomicCommitMode mode);
|
||||
|
||||
bool presentLegacy(DrmBuffer *buffer);
|
||||
bool setModeLegacy(DrmBuffer *buffer);
|
||||
void initEdid(drmModeConnector *connector);
|
||||
|
@ -120,10 +128,13 @@ private:
|
|||
void setGlobalPos(const QPoint &pos);
|
||||
void setScale(qreal scale);
|
||||
|
||||
void pageFlippedBufferRemover(DrmBuffer *oldbuffer, DrmBuffer *newbuffer);
|
||||
bool initPrimaryPlane();
|
||||
bool initCursorPlane();
|
||||
DrmObject::AtomicReturn atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable);
|
||||
|
||||
void dpmsOnHandler();
|
||||
void dpmsOffHandler();
|
||||
bool dpmsAtomicOff();
|
||||
bool atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable);
|
||||
|
||||
DrmBackend *m_backend;
|
||||
DrmConnector *m_conn = nullptr;
|
||||
|
@ -138,12 +149,16 @@ private:
|
|||
QPointer<KWayland::Server::OutputChangeSet> m_changeset;
|
||||
KWin::ScopedDrmPointer<_drmModeProperty, &drmModeFreeProperty> m_dpms;
|
||||
DpmsMode m_dpmsMode = DpmsMode::On;
|
||||
DpmsMode m_dpmsModePending = DpmsMode::On;
|
||||
QByteArray m_uuid;
|
||||
|
||||
uint32_t m_blobId = 0;
|
||||
DrmPlane* m_primaryPlane = nullptr;
|
||||
DrmPlane* m_cursorPlane = nullptr;
|
||||
QVector<DrmPlane*> m_planesFlipList;
|
||||
QVector<DrmPlane*> m_nextPlanesFlipList;
|
||||
bool m_pageFlipPending = false;
|
||||
bool m_dpmsAtomicOffPending = false;
|
||||
bool m_modesetRequested = true;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue