From 93dd85141b35460a83dac86083581ba1c1975927 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 7 Sep 2017 15:01:27 +0200 Subject: kms: Discover all available planes Task-number: QTBUG-63058 Change-Id: I655384916bedbeb0da516e2eaa177d1128272d1c Reviewed-by: Andy Nichols Reviewed-by: Qt CI Bot --- src/platformsupport/kmsconvenience/qkmsdevice.cpp | 128 ++++++++++++++++++++++ src/platformsupport/kmsconvenience/qkmsdevice_p.h | 77 +++++++++++++ 2 files changed, 205 insertions(+) diff --git a/src/platformsupport/kmsconvenience/qkmsdevice.cpp b/src/platformsupport/kmsconvenience/qkmsdevice.cpp index e1f2459bc0..48ad984bf5 100644 --- a/src/platformsupport/kmsconvenience/qkmsdevice.cpp +++ b/src/platformsupport/kmsconvenience/qkmsdevice.cpp @@ -384,6 +384,20 @@ QPlatformScreen *QKmsDevice::createScreenForConnector(drmModeResPtr resources, output.drm_format = drmFormat; output.clone_source = cloneSource; + QString planeListStr; + for (const QKmsPlane &plane : qAsConst(m_planes)) { + if (plane.possibleCrtcs & (1 << output.crtc_index)) { + output.available_planes.append(plane); + planeListStr.append(QString::number(plane.id)); + planeListStr.append(QLatin1Char(' ')); + } + } + qCDebug(qLcKmsDebug, "Output %s can use %d planes: %s", + connectorName.constData(), output.available_planes.count(), qPrintable(planeListStr)); + + // This is for the EGLDevice/EGLStream backend. On some of those devices one + // may want to target a pre-configured plane. It is probably useless for + // eglfs_kms and others. Do not confuse with generic plane support (available_planes). bool ok; int idx = qEnvironmentVariableIntValue("QT_QPA_EGLFS_KMS_PLANE_INDEX", &ok); if (ok) { @@ -504,12 +518,16 @@ void QKmsDevice::createScreens() } } + drmSetClientCap(m_dri_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); + drmModeResPtr resources = drmModeGetResources(m_dri_fd); if (!resources) { qErrnoWarning(errno, "drmModeGetResources failed"); return; } + discoverPlanes(); + QVector screens; int wantedConnectorIndex = -1; @@ -628,6 +646,116 @@ void QKmsDevice::registerScreenCloning(QPlatformScreen *screen, Q_UNUSED(screensCloningThisScreen); } +// drm_property_type_is is not available in old headers +static inline bool propTypeIs(drmModePropertyPtr prop, uint32_t type) +{ + if (prop->flags & DRM_MODE_PROP_EXTENDED_TYPE) + return (prop->flags & DRM_MODE_PROP_EXTENDED_TYPE) == type; + return prop->flags & type; +} + +void QKmsDevice::enumerateProperties(drmModeObjectPropertiesPtr objProps, PropCallback callback) +{ + for (uint32_t propIdx = 0; propIdx < objProps->count_props; ++propIdx) { + drmModePropertyPtr prop = drmModeGetProperty(m_dri_fd, objProps->props[propIdx]); + if (!prop) + continue; + + const quint64 value = objProps->prop_values[propIdx]; + qCDebug(qLcKmsDebug, " property %d: id = %u name = '%s'", propIdx, prop->prop_id, prop->name); + + if (propTypeIs(prop, DRM_MODE_PROP_SIGNED_RANGE)) { + qCDebug(qLcKmsDebug, " type is SIGNED_RANGE, value is %lld, possible values are:", qint64(value)); + for (int i = 0; i < prop->count_values; ++i) + qCDebug(qLcKmsDebug, " %lld", qint64(prop->values[i])); + } else if (propTypeIs(prop, DRM_MODE_PROP_RANGE)) { + qCDebug(qLcKmsDebug, " type is RANGE, value is %llu, possible values are:", value); + for (int i = 0; i < prop->count_values; ++i) + qCDebug(qLcKmsDebug, " %llu", quint64(prop->values[i])); + } else if (propTypeIs(prop, DRM_MODE_PROP_ENUM)) { + qCDebug(qLcKmsDebug, " type is ENUM, value is %llu, possible values are:", value); + for (int i = 0; i < prop->count_enums; ++i) + qCDebug(qLcKmsDebug, " enum %d: %s - %llu", i, prop->enums[i].name, prop->enums[i].value); + } else if (propTypeIs(prop, DRM_MODE_PROP_BITMASK)) { + qCDebug(qLcKmsDebug, " type is BITMASK, value is %llu, possible bits are:", value); + for (int i = 0; i < prop->count_enums; ++i) + qCDebug(qLcKmsDebug, " bitmask %d: %s - %u", i, prop->enums[i].name, 1 << prop->enums[i].value); + } else if (propTypeIs(prop, DRM_MODE_PROP_BLOB)) { + qCDebug(qLcKmsDebug, " type is BLOB"); + } else if (propTypeIs(prop, DRM_MODE_PROP_OBJECT)) { + qCDebug(qLcKmsDebug, " type is OBJECT"); + } + + callback(prop, value); + + drmModeFreeProperty(prop); + } +} + +void QKmsDevice::discoverPlanes() +{ + m_planes.clear(); + + drmModePlaneResPtr planeResources = drmModeGetPlaneResources(m_dri_fd); + if (!planeResources) + return; + + const int countPlanes = planeResources->count_planes; + qCDebug(qLcKmsDebug, "Found %d planes", countPlanes); + for (int planeIdx = 0; planeIdx < countPlanes; ++planeIdx) { + drmModePlanePtr drmplane = drmModeGetPlane(m_dri_fd, planeResources->planes[planeIdx]); + if (!drmplane) { + qCDebug(qLcKmsDebug, "Failed to query plane %d, ignoring", planeIdx); + continue; + } + + QKmsPlane plane; + plane.id = drmplane->plane_id; + plane.possibleCrtcs = drmplane->possible_crtcs; + + const int countFormats = drmplane->count_formats; + QString formatStr; + for (int i = 0; i < countFormats; ++i) { + uint32_t f = drmplane->formats[i]; + plane.supportedFormats.append(f); + QString s; + s.sprintf("%c%c%c%c ", f, f >> 8, f >> 16, f >> 24); + formatStr += s; + } + + qCDebug(qLcKmsDebug, "plane %d: id = %u countFormats = %d possibleCrtcs = 0x%x supported formats = %s", + planeIdx, plane.id, countFormats, plane.possibleCrtcs, qPrintable(formatStr)); + + drmModeFreePlane(drmplane); + + drmModeObjectPropertiesPtr objProps = drmModeObjectGetProperties(m_dri_fd, plane.id, DRM_MODE_OBJECT_PLANE); + if (!objProps) { + qCDebug(qLcKmsDebug, "Failed to query plane %d object properties, ignoring", planeIdx); + continue; + } + + enumerateProperties(objProps, [this, &plane](drmModePropertyPtr prop, quint64 value) { + if (!strcmp(prop->name, "type")) { + plane.type = QKmsPlane::Type(value); + } else if (!strcmp(prop->name, "rotation")) { + plane.initialRotation = QKmsPlane::Rotations(int(value)); + plane.availableRotations = 0; + if (propTypeIs(prop, DRM_MODE_PROP_BITMASK)) { + for (int i = 0; i < prop->count_enums; ++i) + plane.availableRotations |= QKmsPlane::Rotation(1 << prop->enums[i].value); + } + plane.rotationPropertyId = prop->prop_id; + } + }); + + m_planes.append(plane); + + drmModeFreeObjectProperties(objProps); + } + + drmModeFreePlaneResources(planeResources); +} + int QKmsDevice::fd() const { return m_dri_fd; diff --git a/src/platformsupport/kmsconvenience/qkmsdevice_p.h b/src/platformsupport/kmsconvenience/qkmsdevice_p.h index 872104e49d..5eecedec39 100644 --- a/src/platformsupport/kmsconvenience/qkmsdevice_p.h +++ b/src/platformsupport/kmsconvenience/qkmsdevice_p.h @@ -61,6 +61,41 @@ #include #include +#include + +// In less fortunate cases one may need to build on a system with dev headers +// from the dark ages. Let's pull a GL and define the missing stuff outselves. + +#ifndef DRM_PLANE_TYPE_OVERLAY +#define DRM_PLANE_TYPE_OVERLAY 0 +#endif +#ifndef DRM_PLANE_TYPE_PRIMARY +#define DRM_PLANE_TYPE_PRIMARY 1 +#endif +#ifndef DRM_PLANE_TYPE_CURSOR +#define DRM_PLANE_TYPE_CURSOR 2 +#endif + +#ifndef DRM_CLIENT_CAP_UNIVERSAL_PLANES +#define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2 +#endif +#ifndef DRM_CLIENT_CAP_ATOMIC +#define DRM_CLIENT_CAP_ATOMIC 3 +#endif + +#ifndef DRM_MODE_PROP_EXTENDED_TYPE +#define DRM_MODE_PROP_EXTENDED_TYPE 0x0000ffc0 +#endif +#ifndef DRM_MODE_PROP_TYPE +#define DRM_MODE_PROP_TYPE(n) ((n) << 6) +#endif +#ifndef DRM_MODE_PROP_OBJECT +#define DRM_MODE_PROP_OBJECT DRM_MODE_PROP_TYPE(1) +#endif +#ifndef DRM_MODE_PROP_SIGNED_RANGE +#define DRM_MODE_PROP_SIGNED_RANGE DRM_MODE_PROP_TYPE(2) +#endif + QT_BEGIN_NAMESPACE class QKmsDevice; @@ -99,6 +134,42 @@ private: QMap m_outputSettings; }; +// NB! QKmsPlane does not store the current state and offers no functions to +// change object properties. Any such functionality belongs to subclasses since +// in some cases atomic operations will be desired where a mere +// drmModeObjectSetProperty would not be acceptable. +struct QKmsPlane +{ + enum Type { + OverlayPlane = DRM_PLANE_TYPE_OVERLAY, + PrimaryPlane = DRM_PLANE_TYPE_PRIMARY, + CursorPlane = DRM_PLANE_TYPE_CURSOR + }; + + enum Rotation { + Rotation0 = 1 << 0, + Rotation90 = 1 << 1, + Rotation180 = 1 << 2, + Rotation270 = 1 << 3, + RotationReflectX = 1 << 4, + RotationReflectY = 1 << 5 + }; + Q_DECLARE_FLAGS(Rotations, Rotation) + + uint32_t id = 0; + Type type = OverlayPlane; + + int possibleCrtcs = 0; + + QVector supportedFormats; + + Rotations initialRotation = Rotation0; + Rotations availableRotations = Rotation0; + uint32_t rotationPropertyId = 0; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QKmsPlane::Rotations) + struct QKmsOutput { QString name; @@ -119,6 +190,7 @@ struct QKmsOutput bool forced_plane_set = false; uint32_t drm_format = DRM_FORMAT_XRGB8888; QString clone_source; + QVector available_planes; void restoreMode(QKmsDevice *device); void cleanup(QKmsDevice *device); @@ -168,6 +240,9 @@ protected: ScreenInfo *vinfo); drmModePropertyPtr connectorProperty(drmModeConnectorPtr connector, const QByteArray &name); drmModePropertyBlobPtr connectorPropertyBlob(drmModeConnectorPtr connector, const QByteArray &name); + typedef std::function PropCallback; + void enumerateProperties(drmModeObjectPropertiesPtr objProps, PropCallback callback); + void discoverPlanes(); QKmsScreenConfig *m_screenConfig; QString m_path; @@ -175,6 +250,8 @@ protected: quint32 m_crtc_allocator; + QVector m_planes; + private: Q_DISABLE_COPY(QKmsDevice) }; -- cgit v1.2.3