diff options
Diffstat (limited to 'src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support')
7 files changed, 1186 insertions, 0 deletions
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/eglfs_kms_support.pro b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/eglfs_kms_support.pro new file mode 100644 index 0000000000..6355fe6abd --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/eglfs_kms_support.pro @@ -0,0 +1,28 @@ +TARGET = QtEglFsKmsSupport +CONFIG += no_module_headers internal_module +load(qt_module) + +QT += core-private gui-private platformsupport-private eglfs_device_lib-private + +INCLUDEPATH += $$PWD/../.. + +# Avoid X11 header collision +DEFINES += MESA_EGL_NO_X11_HEADERS + +CONFIG += link_pkgconfig +!contains(QT_CONFIG, no-pkg-config) { + PKGCONFIG += libdrm +} else { + LIBS += -ldrm +} + +CONFIG += egl +QMAKE_LFLAGS += $$QMAKE_LFLAGS_NOUNDEF + +SOURCES += $$PWD/qeglfskmsintegration.cpp \ + $$PWD/qeglfskmsdevice.cpp \ + $$PWD/qeglfskmsscreen.cpp \ + +HEADERS += $$PWD/qeglfskmsintegration.h \ + $$PWD/qeglfskmsdevice.h \ + $$PWD/qeglfskmsscreen.h \ diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.cpp new file mode 100644 index 0000000000..f4ffee569d --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.cpp @@ -0,0 +1,404 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfskmsdevice.h" +#include "qeglfskmsscreen.h" + +#include "qeglfsintegration.h" + +#include <QtCore/QLoggingCategory> +#include <QtCore/private/qcore_unix_p.h> +#include <QtGui/private/qguiapplication_p.h> + +#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug) + +enum OutputConfiguration { + OutputConfigOff, + OutputConfigPreferred, + OutputConfigCurrent, + OutputConfigMode, + OutputConfigModeline +}; + +int QEglFSKmsDevice::crtcForConnector(drmModeResPtr resources, drmModeConnectorPtr connector) +{ + for (int i = 0; i < connector->count_encoders; i++) { + drmModeEncoderPtr encoder = drmModeGetEncoder(m_dri_fd, connector->encoders[i]); + if (!encoder) { + qWarning("Failed to get encoder"); + continue; + } + + quint32 possibleCrtcs = encoder->possible_crtcs; + drmModeFreeEncoder(encoder); + + for (int j = 0; j < resources->count_crtcs; j++) { + bool isPossible = possibleCrtcs & (1 << j); + bool isAvailable = !(m_crtc_allocator & 1 << resources->crtcs[j]); + + if (isPossible && isAvailable) + return j; + } + } + + return -1; +} + +static const char * const connector_type_names[] = { + "None", + "VGA", + "DVI", + "DVI", + "DVI", + "Composite", + "TV", + "LVDS", + "CTV", + "DIN", + "DP", + "HDMI", + "HDMI", + "TV", + "eDP", +}; + +static QByteArray nameForConnector(const drmModeConnectorPtr connector) +{ + QByteArray connectorName("UNKNOWN"); + + if (connector->connector_type < ARRAY_LENGTH(connector_type_names)) + connectorName = connector_type_names[connector->connector_type]; + + connectorName += QByteArray::number(connector->connector_type_id); + + return connectorName; +} + +static bool parseModeline(const QByteArray &text, drmModeModeInfoPtr mode) +{ + char hsync[16]; + char vsync[16]; + float fclock; + + mode->type = DRM_MODE_TYPE_USERDEF; + mode->hskew = 0; + mode->vscan = 0; + mode->vrefresh = 0; + mode->flags = 0; + + if (sscanf(text.constData(), "%f %hd %hd %hd %hd %hd %hd %hd %hd %15s %15s", + &fclock, + &mode->hdisplay, + &mode->hsync_start, + &mode->hsync_end, + &mode->htotal, + &mode->vdisplay, + &mode->vsync_start, + &mode->vsync_end, + &mode->vtotal, hsync, vsync) != 11) + return false; + + mode->clock = fclock * 1000; + + if (strcmp(hsync, "+hsync") == 0) + mode->flags |= DRM_MODE_FLAG_PHSYNC; + else if (strcmp(hsync, "-hsync") == 0) + mode->flags |= DRM_MODE_FLAG_NHSYNC; + else + return false; + + if (strcmp(vsync, "+vsync") == 0) + mode->flags |= DRM_MODE_FLAG_PVSYNC; + else if (strcmp(vsync, "-vsync") == 0) + mode->flags |= DRM_MODE_FLAG_NVSYNC; + else + return false; + + return true; +} + +QEglFSKmsScreen *QEglFSKmsDevice::screenForConnector(drmModeResPtr resources, drmModeConnectorPtr connector, QPoint pos) +{ + const QByteArray connectorName = nameForConnector(connector); + + const int crtc = crtcForConnector(resources, connector); + if (crtc < 0) { + qWarning() << "No usable crtc/encoder pair for connector" << connectorName; + return Q_NULLPTR; + } + + OutputConfiguration configuration; + QSize configurationSize; + drmModeModeInfo configurationModeline; + + const QByteArray mode = m_integration->outputSettings().value(QString::fromUtf8(connectorName)) + .value(QStringLiteral("mode"), QStringLiteral("preferred")).toByteArray().toLower(); + if (mode == "off") { + configuration = OutputConfigOff; + } else if (mode == "preferred") { + configuration = OutputConfigPreferred; + } else if (mode == "current") { + configuration = OutputConfigCurrent; + } else if (sscanf(mode.constData(), "%dx%d", &configurationSize.rwidth(), &configurationSize.rheight()) == 2) { + configuration = OutputConfigMode; + } else if (parseModeline(mode, &configurationModeline)) { + configuration = OutputConfigModeline; + } else { + qWarning("Invalid mode \"%s\" for output %s", mode.constData(), connectorName.constData()); + configuration = OutputConfigPreferred; + } + + const uint32_t crtc_id = resources->crtcs[crtc]; + + if (configuration == OutputConfigOff) { + qCDebug(qLcEglfsKmsDebug) << "Turning off output" << connectorName; + drmModeSetCrtc(m_dri_fd, crtc_id, 0, 0, 0, 0, 0, Q_NULLPTR); + return Q_NULLPTR; + } + + // Skip disconnected output + if (configuration == OutputConfigPreferred && connector->connection == DRM_MODE_DISCONNECTED) { + qCDebug(qLcEglfsKmsDebug) << "Skipping disconnected output" << connectorName; + return Q_NULLPTR; + } + + // Get the current mode on the current crtc + drmModeModeInfo crtc_mode; + memset(&crtc_mode, 0, sizeof crtc_mode); + if (drmModeEncoderPtr encoder = drmModeGetEncoder(m_dri_fd, connector->connector_id)) { + drmModeCrtcPtr crtc = drmModeGetCrtc(m_dri_fd, encoder->crtc_id); + drmModeFreeEncoder(encoder); + + if (!crtc) + return Q_NULLPTR; + + if (crtc->mode_valid) + crtc_mode = crtc->mode; + + drmModeFreeCrtc(crtc); + } + + QList<drmModeModeInfo> modes; + modes.reserve(connector->count_modes); + qCDebug(qLcEglfsKmsDebug) << connectorName << "mode count:" << connector->count_modes; + for (int i = 0; i < connector->count_modes; i++) { + const drmModeModeInfo &mode = connector->modes[i]; + qCDebug(qLcEglfsKmsDebug) << "mode" << i << mode.hdisplay << "x" << mode.vdisplay + << '@' << mode.vrefresh << "hz"; + modes << connector->modes[i]; + } + + int preferred = -1; + int current = -1; + int configured = -1; + int best = -1; + + for (int i = modes.size() - 1; i >= 0; i--) { + const drmModeModeInfo &m = modes.at(i); + + if (configuration == OutputConfigMode && + m.hdisplay == configurationSize.width() && + m.vdisplay == configurationSize.height()) { + configured = i; + } + + if (!memcmp(&crtc_mode, &m, sizeof m)) + current = i; + + if (m.type & DRM_MODE_TYPE_PREFERRED) + preferred = i; + + best = i; + } + + if (configuration == OutputConfigModeline) { + modes << configurationModeline; + configured = modes.size() - 1; + } + + if (current < 0 && crtc_mode.clock != 0) { + modes << crtc_mode; + current = mode.size() - 1; + } + + if (configuration == OutputConfigCurrent) + configured = current; + + int selected_mode = -1; + + if (configured >= 0) + selected_mode = configured; + else if (preferred >= 0) + selected_mode = preferred; + else if (current >= 0) + selected_mode = current; + else if (best >= 0) + selected_mode = best; + + if (selected_mode < 0) { + qWarning() << "No modes available for output" << connectorName; + return Q_NULLPTR; + } else { + int width = modes[selected_mode].hdisplay; + int height = modes[selected_mode].vdisplay; + int refresh = modes[selected_mode].vrefresh; + qCDebug(qLcEglfsKmsDebug) << "Selected mode" << selected_mode << ":" << width << "x" << height + << '@' << refresh << "hz for output" << connectorName; + } + static const int width = qEnvironmentVariableIntValue("QT_QPA_EGLFS_PHYSICAL_WIDTH"); + static const int height = qEnvironmentVariableIntValue("QT_QPA_EGLFS_PHYSICAL_HEIGHT"); + QSizeF size(width, height); + if (size.isEmpty()) { + size.setWidth(connector->mmWidth); + size.setHeight(connector->mmHeight); + } + QEglFSKmsOutput output = { + QString::fromUtf8(connectorName), + connector->connector_id, + crtc_id, + size, + selected_mode, + false, + drmModeGetCrtc(m_dri_fd, crtc_id), + modes, + connector->subpixel, + connectorProperty(connector, QByteArrayLiteral("DPMS")) + }; + + m_crtc_allocator |= (1 << output.crtc_id); + m_connector_allocator |= (1 << output.connector_id); + + return createScreen(m_integration, this, output, pos); +} + +drmModePropertyPtr QEglFSKmsDevice::connectorProperty(drmModeConnectorPtr connector, const QByteArray &name) +{ + drmModePropertyPtr prop; + + for (int i = 0; i < connector->count_props; i++) { + prop = drmModeGetProperty(m_dri_fd, connector->props[i]); + if (!prop) + continue; + if (strcmp(prop->name, name.constData()) == 0) + return prop; + drmModeFreeProperty(prop); + } + + return Q_NULLPTR; +} + +QEglFSKmsDevice::QEglFSKmsDevice(QEglFSKmsIntegration *integration, const QString &path) + : m_integration(integration) + , m_path(path) + , m_dri_fd(-1) + , m_crtc_allocator(0) + , m_connector_allocator(0) +{ +} + +QEglFSKmsDevice::~QEglFSKmsDevice() +{ +} + +void QEglFSKmsDevice::createScreens() +{ + drmModeResPtr resources = drmModeGetResources(m_dri_fd); + if (!resources) { + qWarning("drmModeGetResources failed"); + return; + } + + QEglFSKmsScreen *primaryScreen = Q_NULLPTR; + QList<QPlatformScreen *> siblings; + QPoint pos(0, 0); + QEglFSIntegration *integration = static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration()); + + for (int i = 0; i < resources->count_connectors; i++) { + drmModeConnectorPtr connector = drmModeGetConnector(m_dri_fd, resources->connectors[i]); + if (!connector) + continue; + + QEglFSKmsScreen *screen = screenForConnector(resources, connector, pos); + if (screen) { + integration->addScreen(screen); + pos.rx() += screen->geometry().width(); + siblings << screen; + + if (!primaryScreen) + primaryScreen = screen; + } + + drmModeFreeConnector(connector); + } + + drmModeFreeResources(resources); + + if (!m_integration->separateScreens()) { + Q_FOREACH (QPlatformScreen *screen, siblings) + static_cast<QEglFSKmsScreen *>(screen)->setVirtualSiblings(siblings); + } +} + +int QEglFSKmsDevice::fd() const +{ + return m_dri_fd; +} + +QString QEglFSKmsDevice::devicePath() const +{ + return m_path; +} + +QEglFSKmsScreen *QEglFSKmsDevice::createScreen(QEglFSKmsIntegration *integration, QEglFSKmsDevice *device, QEglFSKmsOutput output, QPoint position) +{ + return new QEglFSKmsScreen(integration, device, output, position); +} + +void QEglFSKmsDevice::setFd(int fd) +{ + m_dri_fd = fd; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.h new file mode 100644 index 0000000000..ffa8bcbaa5 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEGLFSKMSDEVICE_H +#define QEGLFSKMSDEVICE_H + +#include "qeglfskmsintegration.h" +#include "qeglfskmsscreen.h" + +#include <xf86drm.h> +#include <xf86drmMode.h> + +QT_BEGIN_NAMESPACE + +class Q_EGLFS_EXPORT QEglFSKmsDevice +{ +public: + QEglFSKmsDevice(QEglFSKmsIntegration *integration, const QString &path); + virtual ~QEglFSKmsDevice(); + + virtual bool open() = 0; + virtual void close() = 0; + + virtual void createScreens(); + + virtual EGLNativeDisplayType device() const = 0; + int fd() const; + QString devicePath() const; + +protected: + virtual QEglFSKmsScreen *createScreen(QEglFSKmsIntegration *integration, + QEglFSKmsDevice *device, + QEglFSKmsOutput output, + QPoint position); + void setFd(int fd); + +private: + Q_DISABLE_COPY(QEglFSKmsDevice) + + QEglFSKmsIntegration *m_integration; + QString m_path; + int m_dri_fd; + + quint32 m_crtc_allocator; + quint32 m_connector_allocator; + + int crtcForConnector(drmModeResPtr resources, drmModeConnectorPtr connector); + QEglFSKmsScreen *screenForConnector(drmModeResPtr resources, drmModeConnectorPtr connector, QPoint pos); + drmModePropertyPtr connectorProperty(drmModeConnectorPtr connector, const QByteArray &name); + + static void pageFlipHandler(int fd, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + void *user_data); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp new file mode 100644 index 0000000000..e25e481878 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfskmsintegration.h" +#include "qeglfskmsdevice.h" +#include "qeglfskmsscreen.h" +#include "qeglfswindow.h" +#include "qeglfscursor.h" + +#include <QtPlatformSupport/private/qeglconvenience_p.h> +#include <QtCore/QJsonDocument> +#include <QtCore/QJsonObject> +#include <QtCore/QJsonArray> +#include <QtGui/qpa/qplatformwindow.h> +#include <QtGui/qpa/qplatformcursor.h> +#include <QtGui/QScreen> + +#include <xf86drm.h> +#include <xf86drmMode.h> + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(qLcEglfsKmsDebug, "qt.qpa.eglfs.kms") + +QEglFSKmsIntegration::QEglFSKmsIntegration() + : m_device(Q_NULLPTR) + , m_hwCursor(false) + , m_pbuffers(false) + , m_separateScreens(false) +{} + +void QEglFSKmsIntegration::platformInit() +{ + loadConfig(); + + if (!m_devicePath.isEmpty()) { + qCDebug(qLcEglfsKmsDebug) << "Using DRM device" << m_devicePath << "specified in config file"; + } + + m_device = createDevice(m_devicePath); + if (Q_UNLIKELY(!m_device->open())) + qFatal("Could not open device %s - aborting!", qPrintable(m_devicePath)); +} + +void QEglFSKmsIntegration::platformDestroy() +{ + m_device->close(); + delete m_device; + m_device = Q_NULLPTR; +} + +EGLNativeDisplayType QEglFSKmsIntegration::platformDisplay() const +{ + Q_ASSERT(m_device); + return reinterpret_cast<EGLNativeDisplayType>(m_device->device()); +} + +bool QEglFSKmsIntegration::usesDefaultScreen() +{ + return false; +} + +void QEglFSKmsIntegration::screenInit() +{ + m_device->createScreens(); +} + +QSurfaceFormat QEglFSKmsIntegration::surfaceFormatFor(const QSurfaceFormat &inputFormat) const +{ + QSurfaceFormat format(inputFormat); + format.setRenderableType(QSurfaceFormat::OpenGLES); + format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); + format.setRedBufferSize(8); + format.setGreenBufferSize(8); + format.setBlueBufferSize(8); + return format; +} + +bool QEglFSKmsIntegration::hasCapability(QPlatformIntegration::Capability cap) const +{ + switch (cap) { + case QPlatformIntegration::ThreadedPixmaps: + case QPlatformIntegration::OpenGL: + case QPlatformIntegration::ThreadedOpenGL: + return true; + default: + return false; + } +} + +void QEglFSKmsIntegration::waitForVSync(QPlatformSurface *surface) const +{ + QWindow *window = static_cast<QWindow *>(surface->surface()); + QEglFSKmsScreen *screen = static_cast<QEglFSKmsScreen *>(window->screen()->handle()); + + screen->waitForFlip(); +} + +bool QEglFSKmsIntegration::supportsPBuffers() const +{ + return m_pbuffers; +} + +bool QEglFSKmsIntegration::hwCursor() const +{ + return m_hwCursor; +} + +bool QEglFSKmsIntegration::separateScreens() const +{ + return m_separateScreens; +} + +QMap<QString, QVariantMap> QEglFSKmsIntegration::outputSettings() const +{ + return m_outputSettings; +} + +QEglFSKmsDevice *QEglFSKmsIntegration::device() const +{ + return m_device; +} + +void QEglFSKmsIntegration::loadConfig() +{ + static QByteArray json = qgetenv("QT_QPA_EGLFS_KMS_CONFIG"); + if (json.isEmpty()) + return; + + qCDebug(qLcEglfsKmsDebug) << "Loading KMS setup from" << json; + + QFile file(QString::fromUtf8(json)); + if (!file.open(QFile::ReadOnly)) { + qCDebug(qLcEglfsKmsDebug) << "Could not open config file" + << json << "for reading"; + return; + } + + const QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); + if (!doc.isObject()) { + qCDebug(qLcEglfsKmsDebug) << "Invalid config file" << json + << "- no top-level JSON object"; + return; + } + + const QJsonObject object = doc.object(); + + m_hwCursor = object.value(QStringLiteral("hwcursor")).toBool(m_hwCursor); + m_pbuffers = object.value(QStringLiteral("pbuffers")).toBool(m_pbuffers); + m_devicePath = object.value(QStringLiteral("device")).toString(); + m_separateScreens = object.value(QStringLiteral("separateScreens")).toBool(m_separateScreens); + + const QJsonArray outputs = object.value(QStringLiteral("outputs")).toArray(); + for (int i = 0; i < outputs.size(); i++) { + const QVariantMap outputSettings = outputs.at(i).toObject().toVariantMap(); + + if (outputSettings.contains(QStringLiteral("name"))) { + const QString name = outputSettings.value(QStringLiteral("name")).toString(); + + if (m_outputSettings.contains(name)) { + qCDebug(qLcEglfsKmsDebug) << "Output" << name << "configured multiple times!"; + } + + m_outputSettings.insert(name, outputSettings); + } + } + + qCDebug(qLcEglfsKmsDebug) << "Configuration:\n" + << "\thwcursor:" << m_hwCursor << "\n" + << "\tpbuffers:" << m_pbuffers << "\n" + << "\tseparateScreens:" << m_separateScreens << "\n" + << "\toutputs:" << m_outputSettings; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.h new file mode 100644 index 0000000000..c630d93fce --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEGLFSKMSINTEGRATION_H +#define QEGLFSKMSINTEGRATION_H + +#include "qeglfsdeviceintegration.h" +#include <QtCore/QMap> +#include <QtCore/QVariant> +#include <QtCore/QLoggingCategory> + +QT_BEGIN_NAMESPACE + +class QEglFSKmsDevice; + +Q_EGLFS_EXPORT Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug) + +class Q_EGLFS_EXPORT QEglFSKmsIntegration : public QEGLDeviceIntegration +{ +public: + QEglFSKmsIntegration(); + + void platformInit() Q_DECL_OVERRIDE; + void platformDestroy() Q_DECL_OVERRIDE; + EGLNativeDisplayType platformDisplay() const Q_DECL_OVERRIDE; + bool usesDefaultScreen() Q_DECL_OVERRIDE; + void screenInit() Q_DECL_OVERRIDE; + QSurfaceFormat surfaceFormatFor(const QSurfaceFormat &inputFormat) const Q_DECL_OVERRIDE; + bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE; + void waitForVSync(QPlatformSurface *surface) const Q_DECL_OVERRIDE; + bool supportsPBuffers() const Q_DECL_OVERRIDE; + + virtual bool hwCursor() const; + virtual bool separateScreens() const; + QMap<QString, QVariantMap> outputSettings() const; + + QEglFSKmsDevice *device() const; + +protected: + virtual QEglFSKmsDevice *createDevice(const QString &devicePath) = 0; + +private: + void loadConfig(); + + QEglFSKmsDevice *m_device; + bool m_hwCursor; + bool m_pbuffers; + bool m_separateScreens; + QString m_devicePath; + QMap<QString, QVariantMap> m_outputSettings; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp new file mode 100644 index 0000000000..f614351a40 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfskmsscreen.h" +#include "qeglfskmsdevice.h" +#include "qeglfsintegration.h" + +#include <QtCore/QLoggingCategory> + +#include <QtGui/private/qguiapplication_p.h> +#include <QtPlatformSupport/private/qfbvthandler_p.h> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug) + +class QEglFSKmsInterruptHandler : public QObject +{ +public: + QEglFSKmsInterruptHandler(QEglFSKmsScreen *screen) : m_screen(screen) { + m_vtHandler = static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration())->vtHandler(); + connect(m_vtHandler, &QFbVtHandler::interrupted, this, &QEglFSKmsInterruptHandler::restoreVideoMode); + connect(m_vtHandler, &QFbVtHandler::aboutToSuspend, this, &QEglFSKmsInterruptHandler::restoreVideoMode); + } + +public slots: + void restoreVideoMode() { m_screen->restoreMode(); } + +private: + QFbVtHandler *m_vtHandler; + QEglFSKmsScreen *m_screen; +}; + +QEglFSKmsScreen::QEglFSKmsScreen(QEglFSKmsIntegration *integration, + QEglFSKmsDevice *device, + QEglFSKmsOutput output, + QPoint position) + : QEglFSScreen(eglGetDisplay(reinterpret_cast<EGLNativeDisplayType>(device->device()))) + , m_integration(integration) + , m_device(device) + , m_output(output) + , m_pos(position) + , m_powerState(PowerStateOn) + , m_interruptHandler(new QEglFSKmsInterruptHandler(this)) +{ + m_siblings << this; +} + +QEglFSKmsScreen::~QEglFSKmsScreen() +{ + if (m_output.dpms_prop) { + drmModeFreeProperty(m_output.dpms_prop); + m_output.dpms_prop = Q_NULLPTR; + } + restoreMode(); + if (m_output.saved_crtc) { + drmModeFreeCrtc(m_output.saved_crtc); + m_output.saved_crtc = Q_NULLPTR; + } + delete m_interruptHandler; +} + +QRect QEglFSKmsScreen::geometry() const +{ + const int mode = m_output.mode; + return QRect(m_pos.x(), m_pos.y(), + m_output.modes[mode].hdisplay, + m_output.modes[mode].vdisplay); +} + +int QEglFSKmsScreen::depth() const +{ + return 32; +} + +QImage::Format QEglFSKmsScreen::format() const +{ + return QImage::Format_RGB32; +} + +QSizeF QEglFSKmsScreen::physicalSize() const +{ + return m_output.physical_size; +} + +QDpi QEglFSKmsScreen::logicalDpi() const +{ + const QSizeF ps = physicalSize(); + const QSize s = geometry().size(); + + if (!ps.isEmpty() && !s.isEmpty()) + return QDpi(25.4 * s.width() / ps.width(), + 25.4 * s.height() / ps.height()); + else + return QDpi(100, 100); +} + +Qt::ScreenOrientation QEglFSKmsScreen::nativeOrientation() const +{ + return Qt::PrimaryOrientation; +} + +Qt::ScreenOrientation QEglFSKmsScreen::orientation() const +{ + return Qt::PrimaryOrientation; +} + +QString QEglFSKmsScreen::name() const +{ + return m_output.name; +} + +void QEglFSKmsScreen::destroySurface() +{ +} + +void QEglFSKmsScreen::waitForFlip() +{ +} + +void QEglFSKmsScreen::flip() +{ +} + +void QEglFSKmsScreen::flipFinished() +{ +} + +void QEglFSKmsScreen::restoreMode() +{ + if (m_output.mode_set && m_output.saved_crtc) { + drmModeSetCrtc(m_device->fd(), + m_output.saved_crtc->crtc_id, + m_output.saved_crtc->buffer_id, + 0, 0, + &m_output.connector_id, 1, + &m_output.saved_crtc->mode); + + m_output.mode_set = false; + } +} + +qreal QEglFSKmsScreen::refreshRate() const +{ + quint32 refresh = m_output.modes[m_output.mode].vrefresh; + return refresh > 0 ? refresh : 60; +} + +QPlatformScreen::SubpixelAntialiasingType QEglFSKmsScreen::subpixelAntialiasingTypeHint() const +{ + switch (m_output.subpixel) { + default: + case DRM_MODE_SUBPIXEL_UNKNOWN: + case DRM_MODE_SUBPIXEL_NONE: + return Subpixel_None; + case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB: + return Subpixel_RGB; + case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR: + return Subpixel_BGR; + case DRM_MODE_SUBPIXEL_VERTICAL_RGB: + return Subpixel_VRGB; + case DRM_MODE_SUBPIXEL_VERTICAL_BGR: + return Subpixel_VBGR; + } +} + +QPlatformScreen::PowerState QEglFSKmsScreen::powerState() const +{ + return m_powerState; +} + +void QEglFSKmsScreen::setPowerState(QPlatformScreen::PowerState state) +{ + if (!m_output.dpms_prop) + return; + + drmModeConnectorSetProperty(m_device->fd(), m_output.connector_id, + m_output.dpms_prop->prop_id, (int)state); + m_powerState = state; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h new file mode 100644 index 0000000000..ed26ca0419 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEGLFSKMSSCREEN_H +#define QEGLFSKMSSCREEN_H + +#include "qeglfskmsintegration.h" +#include "qeglfsscreen.h" +#include <QtCore/QList> +#include <QtCore/QMutex> + +#include <xf86drm.h> +#include <xf86drmMode.h> + +QT_BEGIN_NAMESPACE + +class QEglFSKmsDevice; +class QEglFSKmsInterruptHandler; + +struct QEglFSKmsOutput +{ + QString name; + uint32_t connector_id; + uint32_t crtc_id; + QSizeF physical_size; + int mode; // index of selected mode in list below + bool mode_set; + drmModeCrtcPtr saved_crtc; + QList<drmModeModeInfo> modes; + int subpixel; + drmModePropertyPtr dpms_prop; +}; + +class Q_EGLFS_EXPORT QEglFSKmsScreen : public QEglFSScreen +{ +public: + QEglFSKmsScreen(QEglFSKmsIntegration *integration, + QEglFSKmsDevice *device, + QEglFSKmsOutput output, + QPoint position); + ~QEglFSKmsScreen(); + + QRect geometry() const Q_DECL_OVERRIDE; + int depth() const Q_DECL_OVERRIDE; + QImage::Format format() const Q_DECL_OVERRIDE; + + QSizeF physicalSize() const Q_DECL_OVERRIDE; + QDpi logicalDpi() const Q_DECL_OVERRIDE; + Qt::ScreenOrientation nativeOrientation() const Q_DECL_OVERRIDE; + Qt::ScreenOrientation orientation() const Q_DECL_OVERRIDE; + + QString name() const Q_DECL_OVERRIDE; + + qreal refreshRate() const Q_DECL_OVERRIDE; + + QList<QPlatformScreen *> virtualSiblings() const Q_DECL_OVERRIDE { return m_siblings; } + void setVirtualSiblings(QList<QPlatformScreen *> sl) { m_siblings = sl; } + + QEglFSKmsIntegration *integration() const { return m_integration; } + QEglFSKmsDevice *device() const { return m_device; } + + void destroySurface(); + + virtual void waitForFlip(); + virtual void flip(); + virtual void flipFinished(); + + QEglFSKmsOutput &output() { return m_output; } + void restoreMode(); + + SubpixelAntialiasingType subpixelAntialiasingTypeHint() const Q_DECL_OVERRIDE; + + QPlatformScreen::PowerState powerState() const Q_DECL_OVERRIDE; + void setPowerState(QPlatformScreen::PowerState state) Q_DECL_OVERRIDE; + +private: + QEglFSKmsIntegration *m_integration; + QEglFSKmsDevice *m_device; + + QEglFSKmsOutput m_output; + QPoint m_pos; + + QList<QPlatformScreen *> m_siblings; + + PowerState m_powerState; + + QEglFSKmsInterruptHandler *m_interruptHandler; +}; + +QT_END_NAMESPACE + +#endif |