summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/eglfs
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/eglfs')
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp23
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h4
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfsintegration.cpp8
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfswindow.cpp17
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfswindow_p.h2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/deviceintegration.pro1
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorintegration.h2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.cpp5
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.h1
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.pro8
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp8
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp50
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h12
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp54
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.h5
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp240
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h34
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.cpp80
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.h67
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/eglfs_kms_egldevice.pro2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevice.cpp6
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp10
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.cpp10
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/eglfs_kms_support.pro5
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmshelpers.h60
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp22
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.h2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp88
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h19
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/eglfs_kms_vsp2.json3
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/eglfs_kms_vsp2.pro31
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2device.cpp136
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2device.h78
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.cpp250
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.h81
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2main.cpp56
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.cpp365
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.h116
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qlinuxmediadevice.cpp627
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qlinuxmediadevice.h116
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qvsp2blendingdevice.cpp333
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qvsp2blendingdevice.h95
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp71
43 files changed, 2942 insertions, 261 deletions
diff --git a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp
index e411ea55e9..1b8f50a1c6 100644
--- a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp
+++ b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp
@@ -103,7 +103,7 @@ QStringList QEglFSDeviceIntegrationFactory::keys(const QString &pluginPath)
QEglFSDeviceIntegration *QEglFSDeviceIntegrationFactory::create(const QString &key, const QString &pluginPath)
{
- QEglFSDeviceIntegration *integration = Q_NULLPTR;
+ QEglFSDeviceIntegration *integration = nullptr;
#if QT_CONFIG(library)
if (!pluginPath.isEmpty()) {
QCoreApplication::addLibraryPath(pluginPath);
@@ -351,9 +351,28 @@ bool QEglFSDeviceIntegration::supportsSurfacelessContexts() const
return true;
}
+QFunctionPointer QEglFSDeviceIntegration::platformFunction(const QByteArray &function) const
+{
+ Q_UNUSED(function);
+ return nullptr;
+}
+
+void *QEglFSDeviceIntegration::nativeResourceForIntegration(const QByteArray &name)
+{
+ Q_UNUSED(name);
+ return nullptr;
+}
+
+void *QEglFSDeviceIntegration::nativeResourceForScreen(const QByteArray &resource, QScreen *screen)
+{
+ Q_UNUSED(resource);
+ Q_UNUSED(screen);
+ return nullptr;
+}
+
void *QEglFSDeviceIntegration::wlDisplay() const
{
- return Q_NULLPTR;
+ return nullptr;
}
EGLConfig QEglFSDeviceIntegration::chooseConfig(EGLDisplay display, const QSurfaceFormat &format)
diff --git a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h
index 4335554912..71ffb4c69a 100644
--- a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h
+++ b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h
@@ -103,7 +103,9 @@ public:
virtual int framebufferIndex() const;
virtual bool supportsPBuffers() const;
virtual bool supportsSurfacelessContexts() const;
-
+ virtual QFunctionPointer platformFunction(const QByteArray &function) const;
+ virtual void *nativeResourceForIntegration(const QByteArray &name);
+ virtual void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen);
virtual void *wlDisplay() const;
static EGLConfig chooseConfig(EGLDisplay display, const QSurfaceFormat &format);
diff --git a/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp b/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp
index 9a0be489a8..33878a5f50 100644
--- a/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp
+++ b/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp
@@ -324,13 +324,14 @@ void *QEglFSIntegration::nativeResourceForIntegration(const QByteArray &resource
result = qt_egl_device_integration()->wlDisplay();
break;
default:
+ result = qt_egl_device_integration()->nativeResourceForIntegration(resource);
break;
}
return result;
}
-void *QEglFSIntegration::nativeResourceForScreen(const QByteArray &resource, QScreen *)
+void *QEglFSIntegration::nativeResourceForScreen(const QByteArray &resource, QScreen *screen)
{
void *result = 0;
@@ -341,6 +342,7 @@ void *QEglFSIntegration::nativeResourceForScreen(const QByteArray &resource, QSc
result = reinterpret_cast<void*>(nativeDisplay());
break;
default:
+ result = qt_egl_device_integration()->nativeResourceForScreen(resource, screen);
break;
}
@@ -427,11 +429,9 @@ QFunctionPointer QEglFSIntegration::platformFunction(const QByteArray &function)
#if QT_CONFIG(evdev)
if (function == QEglFSFunctions::loadKeymapTypeIdentifier())
return QFunctionPointer(loadKeymapStatic);
-#else
- Q_UNUSED(function)
#endif
- return 0;
+ return qt_egl_device_integration()->platformFunction(function);
}
void QEglFSIntegration::loadKeymapStatic(const QString &filename)
diff --git a/src/plugins/platforms/eglfs/api/qeglfswindow.cpp b/src/plugins/platforms/eglfs/api/qeglfswindow.cpp
index 17e4aa1df8..29cfd4ea79 100644
--- a/src/plugins/platforms/eglfs/api/qeglfswindow.cpp
+++ b/src/plugins/platforms/eglfs/api/qeglfswindow.cpp
@@ -41,6 +41,7 @@
#include <qpa/qwindowsysteminterface.h>
#include <qpa/qplatformintegration.h>
#include <private/qguiapplication_p.h>
+#include <private/qwindow_p.h>
#ifndef QT_NO_OPENGL
# include <QtGui/private/qopenglcontext_p.h>
# include <QtGui/QOpenGLContext>
@@ -99,7 +100,6 @@ void QEglFSWindow::create()
if (window()->type() == Qt::Desktop) {
QRect fullscreenRect(QPoint(), screen()->availableGeometry().size());
- QPlatformWindow::setGeometry(fullscreenRect);
QWindowSystemInterface::handleGeometryChange(window(), fullscreenRect);
return;
}
@@ -235,21 +235,16 @@ void QEglFSWindow::setVisible(bool visible)
void QEglFSWindow::setGeometry(const QRect &r)
{
- QRect rect;
- bool forceFullscreen = m_flags.testFlag(HasNativeWindow);
- if (forceFullscreen)
+ QRect rect = r;
+ if (m_flags.testFlag(HasNativeWindow))
rect = screen()->availableGeometry();
- else
- rect = r;
- const bool changed = rect != QPlatformWindow::geometry();
QPlatformWindow::setGeometry(rect);
- // if we corrected the size, trigger a resize event
- if (rect != r)
- QWindowSystemInterface::handleGeometryChange(window(), rect, r);
+ QWindowSystemInterface::handleGeometryChange(window(), rect);
- if (changed)
+ const QRect lastReportedGeometry = qt_window_private(window())->geometry;
+ if (rect != lastReportedGeometry)
QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size()));
}
diff --git a/src/plugins/platforms/eglfs/api/qeglfswindow_p.h b/src/plugins/platforms/eglfs/api/qeglfswindow_p.h
index 6bda262523..c61f04f569 100644
--- a/src/plugins/platforms/eglfs/api/qeglfswindow_p.h
+++ b/src/plugins/platforms/eglfs/api/qeglfswindow_p.h
@@ -95,7 +95,7 @@ public:
EGLNativeWindowType eglWindow() const;
EGLSurface surface() const;
- QEglFSScreen *screen() const;
+ QEglFSScreen *screen() const override;
bool hasNativeWindow() const { return m_flags.testFlag(HasNativeWindow); }
diff --git a/src/plugins/platforms/eglfs/deviceintegration/deviceintegration.pro b/src/plugins/platforms/eglfs/deviceintegration/deviceintegration.pro
index 92ee83fd8f..919ecd01f6 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/deviceintegration.pro
+++ b/src/plugins/platforms/eglfs/deviceintegration/deviceintegration.pro
@@ -4,6 +4,7 @@ QT_FOR_CONFIG += gui-private
qtConfig(egl_x11): SUBDIRS += eglfs_x11
qtConfig(eglfs_gbm): SUBDIRS *= eglfs_kms_support eglfs_kms
qtConfig(eglfs_egldevice): SUBDIRS *= eglfs_kms_support eglfs_kms_egldevice
+qtConfig(eglfs_vsp2): SUBDIRS += eglfs_kms_vsp2
qtConfig(eglfs_brcm): SUBDIRS += eglfs_brcm
qtConfig(eglfs_mali): SUBDIRS += eglfs_mali
qtConfig(eglfs_viv): SUBDIRS += eglfs_viv
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorintegration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorintegration.h
index 513a5063fb..be5524cba3 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorintegration.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorintegration.h
@@ -45,7 +45,7 @@
#include <QtCore/QLoggingCategory>
#include <QtCore/QFunctionPointer>
-typedef const char *(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC) ();
+typedef QByteArray (EGLAPIENTRYP PFNQGSGETDISPLAYSPROC) ();
typedef void (EGLAPIENTRYP PFNQGSSETDISPLAYPROC) (uint screen);
QT_BEGIN_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.cpp
index 4546088327..7654034f85 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.cpp
@@ -110,6 +110,11 @@ uint QEglFSEmulatorScreen::id() const
return m_id;
}
+QString QEglFSEmulatorScreen::name() const
+{
+ return m_description;
+}
+
void QEglFSEmulatorScreen::initFromJsonObject(const QJsonObject &description)
{
QJsonValue value;
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.h
index 3e5113c9c2..c4994720fa 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.h
@@ -62,6 +62,7 @@ public:
qreal refreshRate() const override;
Qt::ScreenOrientation nativeOrientation() const override;
Qt::ScreenOrientation orientation() const override;
+ QString name() const override;
uint id() const;
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.pro b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.pro
index e522c0ee1b..43170a3875 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.pro
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.pro
@@ -4,7 +4,7 @@ PLUGIN_TYPE = egldeviceintegrations
PLUGIN_CLASS_NAME = QEglFSKmsGbmIntegrationPlugin
load(qt_plugin)
-QT += core-private gui-private eglfsdeviceintegration-private eglfs_kms_support-private kms_support-private
+QT += core-private gui-private eglfsdeviceintegration-private eglfs_kms_support-private kms_support-private edid_support-private
INCLUDEPATH += $$PWD/../../api $$PWD/../eglfs_kms_support
@@ -19,11 +19,13 @@ SOURCES += $$PWD/qeglfskmsgbmmain.cpp \
$$PWD/qeglfskmsgbmintegration.cpp \
$$PWD/qeglfskmsgbmdevice.cpp \
$$PWD/qeglfskmsgbmscreen.cpp \
- $$PWD/qeglfskmsgbmcursor.cpp
+ $$PWD/qeglfskmsgbmcursor.cpp \
+ $$PWD/qeglfskmsgbmwindow.cpp
HEADERS += $$PWD/qeglfskmsgbmintegration.h \
$$PWD/qeglfskmsgbmdevice.h \
$$PWD/qeglfskmsgbmscreen.h \
- $$PWD/qeglfskmsgbmcursor.h
+ $$PWD/qeglfskmsgbmcursor.h \
+ $$PWD/qeglfskmsgbmwindow.h
OTHER_FILES += $$PWD/eglfs_kms.json
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp
index 19790e5c45..9bd7fee1fb 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp
@@ -68,7 +68,7 @@ Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
QEglFSKmsGbmCursor::QEglFSKmsGbmCursor(QEglFSKmsGbmScreen *screen)
: m_screen(screen)
, m_cursorSize(64, 64) // 64x64 is the old standard size, we now try to query the real size below
- , m_bo(Q_NULLPTR)
+ , m_bo(nullptr)
, m_cursorImage(0, 0, 0, 0, 0, 0)
, m_state(CursorPendingVisible)
{
@@ -118,7 +118,7 @@ QEglFSKmsGbmCursor::~QEglFSKmsGbmCursor()
if (m_bo) {
gbm_bo_destroy(m_bo);
- m_bo = Q_NULLPTR;
+ m_bo = nullptr;
}
}
@@ -132,7 +132,7 @@ void QEglFSKmsGbmCursor::updateMouseStatus()
m_state = visible ? CursorPendingVisible : CursorPendingHidden;
#ifndef QT_NO_CURSOR
- changeCursor(Q_NULLPTR, m_screen->topLevelAt(pos()));
+ changeCursor(nullptr, m_screen->topLevelAt(pos()));
#endif
}
@@ -204,7 +204,7 @@ void QEglFSKmsGbmCursor::changeCursor(QCursor *windowCursor, QWindow *window)
painter.drawImage(0, 0, *m_cursorImage.image());
painter.end();
- gbm_bo_write(m_bo, cursorImage.constBits(), cursorImage.byteCount());
+ gbm_bo_write(m_bo, cursorImage.constBits(), cursorImage.sizeInBytes());
uint32_t handle = gbm_bo_get_handle(m_bo).u32;
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp
index e218d580a2..20127ae7f7 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp
@@ -53,28 +53,17 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
-void QEglFSKmsGbmDevice::pageFlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data)
-{
- Q_UNUSED(fd);
- Q_UNUSED(sequence);
- Q_UNUSED(tv_sec);
- Q_UNUSED(tv_usec);
-
- QEglFSKmsScreen *screen = static_cast<QEglFSKmsScreen *>(user_data);
- screen->flipFinished();
-}
-
QEglFSKmsGbmDevice::QEglFSKmsGbmDevice(QKmsScreenConfig *screenConfig, const QString &path)
: QEglFSKmsDevice(screenConfig, path)
- , m_gbm_device(Q_NULLPTR)
- , m_globalCursor(Q_NULLPTR)
+ , m_gbm_device(nullptr)
+ , m_globalCursor(nullptr)
{
}
bool QEglFSKmsGbmDevice::open()
{
Q_ASSERT(fd() == -1);
- Q_ASSERT(m_gbm_device == Q_NULLPTR);
+ Q_ASSERT(m_gbm_device == nullptr);
int fd = qt_safe_open(devicePath().toLocal8Bit().constData(), O_RDWR | O_CLOEXEC);
if (fd == -1) {
@@ -103,7 +92,7 @@ void QEglFSKmsGbmDevice::close()
if (m_gbm_device) {
gbm_device_destroy(m_gbm_device);
- m_gbm_device = Q_NULLPTR;
+ m_gbm_device = nullptr;
}
if (fd() != -1) {
@@ -134,24 +123,13 @@ void QEglFSKmsGbmDevice::destroyGlobalCursor()
if (m_globalCursor) {
qCDebug(qLcEglfsKmsDebug, "Destroying global GBM mouse cursor");
delete m_globalCursor;
- m_globalCursor = Q_NULLPTR;
+ m_globalCursor = nullptr;
}
}
-void QEglFSKmsGbmDevice::handleDrmEvent()
-{
- drmEventContext drmEvent;
- memset(&drmEvent, 0, sizeof(drmEvent));
- drmEvent.version = 2;
- drmEvent.vblank_handler = nullptr;
- drmEvent.page_flip_handler = pageFlipHandler;
-
- drmHandleEvent(fd(), &drmEvent);
-}
-
QPlatformScreen *QEglFSKmsGbmDevice::createScreen(const QKmsOutput &output)
{
- QEglFSKmsGbmScreen *screen = new QEglFSKmsGbmScreen(this, output);
+ QEglFSKmsGbmScreen *screen = new QEglFSKmsGbmScreen(this, output, false);
if (!m_globalCursor && screenConfig()->hwCursor()) {
qCDebug(qLcEglfsKmsDebug, "Creating new global GBM mouse cursor");
@@ -161,4 +139,20 @@ QPlatformScreen *QEglFSKmsGbmDevice::createScreen(const QKmsOutput &output)
return screen;
}
+QPlatformScreen *QEglFSKmsGbmDevice::createHeadlessScreen()
+{
+ return new QEglFSKmsGbmScreen(this, QKmsOutput(), true);
+}
+
+void QEglFSKmsGbmDevice::registerScreenCloning(QPlatformScreen *screen,
+ QPlatformScreen *screenThisScreenClones,
+ const QVector<QPlatformScreen *> &screensCloningThisScreen)
+{
+ if (!screenThisScreenClones && screensCloningThisScreen.isEmpty())
+ return;
+
+ QEglFSKmsGbmScreen *gbmScreen = static_cast<QEglFSKmsGbmScreen *>(screen);
+ gbmScreen->initCloning(screenThisScreenClones, screensCloningThisScreen);
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h
index 08ca28d48e..518e2ce58b 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h
@@ -65,9 +65,11 @@ public:
QPlatformCursor *globalCursor() const;
void destroyGlobalCursor();
- void handleDrmEvent();
-
QPlatformScreen *createScreen(const QKmsOutput &output) override;
+ QPlatformScreen *createHeadlessScreen() override;
+ void registerScreenCloning(QPlatformScreen *screen,
+ QPlatformScreen *screenThisScreenClones,
+ const QVector<QPlatformScreen *> &screensCloningThisScreen) override;
private:
Q_DISABLE_COPY(QEglFSKmsGbmDevice)
@@ -75,12 +77,6 @@ private:
gbm_device *m_gbm_device;
QEglFSKmsGbmCursor *m_globalCursor;
-
- static void pageFlipHandler(int fd,
- unsigned int sequence,
- unsigned int tv_sec,
- unsigned int tv_usec,
- void *user_data);
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp
index b6cdcf92b6..402338197d 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp
@@ -43,19 +43,13 @@
#include "qeglfskmsgbmdevice.h"
#include "qeglfskmsgbmscreen.h"
#include "qeglfskmsgbmcursor.h"
+#include "qeglfskmsgbmwindow.h"
#include "private/qeglfscursor_p.h"
-#include <QtDeviceDiscoverySupport/private/qdevicediscovery_p.h>
#include <QtCore/QLoggingCategory>
-#include <QtCore/QJsonDocument>
-#include <QtCore/QJsonObject>
-#include <QtCore/QJsonArray>
-#include <QtGui/qpa/qplatformwindow.h>
-#include <QtGui/qpa/qplatformcursor.h>
#include <QtGui/QScreen>
+#include <QtDeviceDiscoverySupport/private/qdevicediscovery_p.h>
-#include <xf86drm.h>
-#include <xf86drmMode.h>
#include <gbm.h>
QT_BEGIN_NAMESPACE
@@ -67,20 +61,35 @@ QEglFSKmsGbmIntegration::QEglFSKmsGbmIntegration()
qCDebug(qLcEglfsKmsDebug, "New DRM/KMS via GBM integration created");
}
-EGLNativeWindowType QEglFSKmsGbmIntegration::createNativeWindow(QPlatformWindow *platformWindow,
- const QSize &size,
- const QSurfaceFormat &format)
+#ifndef EGL_EXT_platform_base
+typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC) (EGLenum platform, void *native_display, const EGLint *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC) (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLint *attrib_list);
+#endif
+
+#ifndef EGL_PLATFORM_GBM_KHR
+#define EGL_PLATFORM_GBM_KHR 0x31D7
+#endif
+
+EGLDisplay QEglFSKmsGbmIntegration::createDisplay(EGLNativeDisplayType nativeDisplay)
{
- Q_UNUSED(size);
- Q_UNUSED(format);
+ qCDebug(qLcEglfsKmsDebug, "Querying EGLDisplay");
+ EGLDisplay display;
+
+ PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplay = nullptr;
+ const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+ if (extensions && (strstr(extensions, "EGL_KHR_platform_gbm") || strstr(extensions, "EGL_MESA_platform_gbm"))) {
+ getPlatformDisplay = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
+ eglGetProcAddress("eglGetPlatformDisplayEXT"));
+ }
- QEglFSKmsGbmScreen *screen = static_cast<QEglFSKmsGbmScreen *>(platformWindow->screen());
- if (screen->surface()) {
- qWarning("Only single window per screen supported!");
- return 0;
+ if (getPlatformDisplay) {
+ display = getPlatformDisplay(EGL_PLATFORM_GBM_KHR, nativeDisplay, nullptr);
+ } else {
+ qCDebug(qLcEglfsKmsDebug, "No eglGetPlatformDisplay for GBM, falling back to eglGetDisplay");
+ display = eglGetDisplay(nativeDisplay);
}
- return reinterpret_cast<EGLNativeWindowType>(screen->createSurface());
+ return display;
}
EGLNativeWindowType QEglFSKmsGbmIntegration::createNativeOffscreenWindow(const QSurfaceFormat &format)
@@ -88,7 +97,6 @@ EGLNativeWindowType QEglFSKmsGbmIntegration::createNativeOffscreenWindow(const Q
Q_UNUSED(format);
Q_ASSERT(device());
- qCDebug(qLcEglfsKmsDebug) << "Creating native off screen window";
gbm_surface *surface = gbm_surface_create(static_cast<QEglFSKmsGbmDevice *>(device())->gbmDevice(),
1, 1,
GBM_FORMAT_XRGB8888,
@@ -117,8 +125,7 @@ QPlatformCursor *QEglFSKmsGbmIntegration::createCursor(QPlatformScreen *screen)
void QEglFSKmsGbmIntegration::presentBuffer(QPlatformSurface *surface)
{
QWindow *window = static_cast<QWindow *>(surface->surface());
- QEglFSKmsScreen *screen = static_cast<QEglFSKmsScreen *>(window->screen()->handle());
-
+ QEglFSKmsGbmScreen *screen = static_cast<QEglFSKmsGbmScreen *>(window->screen()->handle());
screen->flip();
}
@@ -143,4 +150,9 @@ QKmsDevice *QEglFSKmsGbmIntegration::createDevice()
return new QEglFSKmsGbmDevice(screenConfig(), path);
}
+QEglFSWindow *QEglFSKmsGbmIntegration::createWindow(QWindow *window) const
+{
+ return new QEglFSKmsGbmWindow(window, this);
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.h
index 38f132d72e..71f232abf9 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.h
@@ -55,14 +55,13 @@ class QEglFSKmsGbmIntegration : public QEglFSKmsIntegration
public:
QEglFSKmsGbmIntegration();
- EGLNativeWindowType createNativeWindow(QPlatformWindow *platformWindow,
- const QSize &size,
- const QSurfaceFormat &format) override;
+ EGLDisplay createDisplay(EGLNativeDisplayType nativeDisplay) override;
EGLNativeWindowType createNativeOffscreenWindow(const QSurfaceFormat &format) override;
void destroyNativeWindow(EGLNativeWindowType window) override;
QPlatformCursor *createCursor(QPlatformScreen *screen) const override;
void presentBuffer(QPlatformSurface *surface) override;
+ QEglFSWindow *createWindow(QWindow *window) const override;
protected:
QKmsDevice *createDevice() override;
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp
index 87fb3146c7..4742143121 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
+** Copyright (C) 2017 The Qt Company Ltd.
** 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/
**
@@ -55,6 +55,18 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
+static inline uint32_t drmFormatToGbmFormat(uint32_t drmFormat)
+{
+ Q_ASSERT(DRM_FORMAT_XRGB8888 == GBM_FORMAT_XRGB8888);
+ return drmFormat;
+}
+
+static inline uint32_t gbmFormatToDrmFormat(uint32_t gbmFormat)
+{
+ Q_ASSERT(DRM_FORMAT_XRGB8888 == GBM_FORMAT_XRGB8888);
+ return gbmFormat;
+}
+
void QEglFSKmsGbmScreen::bufferDestroyedHandler(gbm_bo *bo, void *data)
{
FrameBuffer *fb = static_cast<FrameBuffer *>(data);
@@ -77,29 +89,34 @@ QEglFSKmsGbmScreen::FrameBuffer *QEglFSKmsGbmScreen::framebufferForBufferObject(
uint32_t width = gbm_bo_get_width(bo);
uint32_t height = gbm_bo_get_height(bo);
- uint32_t stride = gbm_bo_get_stride(bo);
- uint32_t handle = gbm_bo_get_handle(bo).u32;
+ uint32_t handles[4] = { gbm_bo_get_handle(bo).u32 };
+ uint32_t strides[4] = { gbm_bo_get_stride(bo) };
+ uint32_t offsets[4] = { 0 };
+ uint32_t pixelFormat = gbmFormatToDrmFormat(gbm_bo_get_format(bo));
QScopedPointer<FrameBuffer> fb(new FrameBuffer);
+ qCDebug(qLcEglfsKmsDebug, "Adding FB, size %ux%u, DRM format 0x%x", width, height, pixelFormat);
- int ret = drmModeAddFB(device()->fd(), width, height, 24, 32,
- stride, handle, &fb->fb);
+ int ret = drmModeAddFB2(device()->fd(), width, height, pixelFormat,
+ handles, strides, offsets, &fb->fb, 0);
if (ret) {
qWarning("Failed to create KMS FB!");
- return Q_NULLPTR;
+ return nullptr;
}
gbm_bo_set_user_data(bo, fb.data(), bufferDestroyedHandler);
return fb.take();
}
-QEglFSKmsGbmScreen::QEglFSKmsGbmScreen(QKmsDevice *device, const QKmsOutput &output)
- : QEglFSKmsScreen(device, output)
- , m_gbm_surface(Q_NULLPTR)
- , m_gbm_bo_current(Q_NULLPTR)
- , m_gbm_bo_next(Q_NULLPTR)
- , m_cursor(Q_NULLPTR)
+QEglFSKmsGbmScreen::QEglFSKmsGbmScreen(QKmsDevice *device, const QKmsOutput &output, bool headless)
+ : QEglFSKmsScreen(device, output, headless)
+ , m_gbm_surface(nullptr)
+ , m_gbm_bo_current(nullptr)
+ , m_gbm_bo_next(nullptr)
+ , m_flipPending(false)
+ , m_cursor(nullptr)
+ , m_cloneSource(nullptr)
{
}
@@ -114,6 +131,8 @@ QEglFSKmsGbmScreen::~QEglFSKmsGbmScreen()
QPlatformCursor *QEglFSKmsGbmScreen::cursor() const
{
QKmsScreenConfig *config = device()->screenConfig();
+ if (config->headless())
+ return nullptr;
if (config->hwCursor()) {
if (!config->separateScreens())
return static_cast<QEglFSKmsGbmDevice *>(device())->globalCursor();
@@ -132,47 +151,112 @@ QPlatformCursor *QEglFSKmsGbmScreen::cursor() const
gbm_surface *QEglFSKmsGbmScreen::createSurface()
{
if (!m_gbm_surface) {
- qCDebug(qLcEglfsKmsDebug) << "Creating window for screen" << name();
+ uint32_t gbmFormat = drmFormatToGbmFormat(m_output.drm_format);
+ qCDebug(qLcEglfsKmsDebug, "Creating gbm_surface for screen %s with format 0x%x", qPrintable(name()), gbmFormat);
m_gbm_surface = gbm_surface_create(static_cast<QEglFSKmsGbmDevice *>(device())->gbmDevice(),
rawGeometry().width(),
rawGeometry().height(),
- GBM_FORMAT_XRGB8888,
+ gbmFormat,
GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
}
- return m_gbm_surface;
+ return m_gbm_surface; // not owned, gets destroyed in QEglFSKmsGbmIntegration::destroyNativeWindow() via QEglFSKmsGbmWindow::invalidateSurface()
}
-void QEglFSKmsGbmScreen::destroySurface()
+void QEglFSKmsGbmScreen::resetSurface()
{
- if (m_gbm_bo_current) {
- gbm_bo_destroy(m_gbm_bo_current);
- m_gbm_bo_current = Q_NULLPTR;
- }
+ m_gbm_surface = nullptr;
+}
- if (m_gbm_bo_next) {
- gbm_bo_destroy(m_gbm_bo_next);
- m_gbm_bo_next = Q_NULLPTR;
+void QEglFSKmsGbmScreen::initCloning(QPlatformScreen *screenThisScreenClones,
+ const QVector<QPlatformScreen *> &screensCloningThisScreen)
+{
+ // clone destinations need to know the clone source
+ const bool clonesAnother = screenThisScreenClones != nullptr;
+ if (clonesAnother && !screensCloningThisScreen.isEmpty()) {
+ qWarning("QEglFSKmsGbmScreen %s cannot be clone source and destination at the same time", qPrintable(name()));
+ return;
}
+ if (clonesAnother)
+ m_cloneSource = static_cast<QEglFSKmsGbmScreen *>(screenThisScreenClones);
+
+ // clone sources need to know their additional destinations
+ for (QPlatformScreen *s : screensCloningThisScreen) {
+ CloneDestination d;
+ d.screen = static_cast<QEglFSKmsGbmScreen *>(s);
+ m_cloneDests.append(d);
+ }
+}
+
+void QEglFSKmsGbmScreen::ensureModeSet(uint32_t fb)
+{
+ QKmsOutput &op(output());
+ const int fd = device()->fd();
+
+ if (!op.mode_set) {
+ op.mode_set = true;
+
+ bool doModeSet = true;
+ drmModeCrtcPtr currentMode = drmModeGetCrtc(fd, op.crtc_id);
+ const bool alreadySet = currentMode && !memcmp(&currentMode->mode, &op.modes[op.mode], sizeof(drmModeModeInfo));
+ if (currentMode)
+ drmModeFreeCrtc(currentMode);
+ if (alreadySet) {
+ static bool alwaysDoSet = qEnvironmentVariableIntValue("QT_QPA_EGLFS_ALWAYS_SET_MODE");
+ if (!alwaysDoSet) {
+ qCDebug(qLcEglfsKmsDebug, "Mode already set, skipping modesetting for screen %s", qPrintable(name()));
+ doModeSet = false;
+ }
+ }
- if (m_gbm_surface) {
- gbm_surface_destroy(m_gbm_surface);
- m_gbm_surface = Q_NULLPTR;
+ if (doModeSet) {
+ qCDebug(qLcEglfsKmsDebug, "Setting mode for screen %s", qPrintable(name()));
+ int ret = drmModeSetCrtc(fd,
+ op.crtc_id,
+ fb,
+ 0, 0,
+ &op.connector_id, 1,
+ &op.modes[op.mode]);
+
+ if (ret == 0)
+ setPowerState(PowerStateOn);
+ else
+ qErrnoWarning(errno, "Could not set DRM mode for screen %s", qPrintable(name()));
+ }
}
}
void QEglFSKmsGbmScreen::waitForFlip()
{
+ if (m_headless || m_cloneSource)
+ return;
+
// Don't lock the mutex unless we actually need to
if (!m_gbm_bo_next)
return;
QMutexLocker lock(&m_waitForFlipMutex);
- while (m_gbm_bo_next)
- static_cast<QEglFSKmsGbmDevice *>(device())->handleDrmEvent();
+ while (m_gbm_bo_next) {
+ drmEventContext drmEvent;
+ memset(&drmEvent, 0, sizeof(drmEvent));
+ drmEvent.version = 2;
+ drmEvent.vblank_handler = nullptr;
+ drmEvent.page_flip_handler = pageFlipHandler;
+ drmHandleEvent(device()->fd(), &drmEvent);
+ }
}
void QEglFSKmsGbmScreen::flip()
{
+ // For headless screen just return silently. It is not necessarily an error
+ // to end up here, so show no warnings.
+ if (m_headless)
+ return;
+
+ if (m_cloneSource) {
+ qWarning("Screen %s clones another screen. swapBuffers() not allowed.", qPrintable(name()));
+ return;
+ }
+
if (!m_gbm_surface) {
qWarning("Cannot sync before platform init!");
return;
@@ -185,60 +269,92 @@ void QEglFSKmsGbmScreen::flip()
}
FrameBuffer *fb = framebufferForBufferObject(m_gbm_bo_next);
+ ensureModeSet(fb->fb);
QKmsOutput &op(output());
const int fd = device()->fd();
- const uint32_t w = op.modes[op.mode].hdisplay;
- const uint32_t h = op.modes[op.mode].vdisplay;
-
- if (!op.mode_set) {
- int ret = drmModeSetCrtc(fd,
- op.crtc_id,
- fb->fb,
- 0, 0,
- &op.connector_id, 1,
- &op.modes[op.mode]);
-
- if (ret == -1) {
- qErrnoWarning(errno, "Could not set DRM mode!");
- } else {
- op.mode_set = true;
- setPowerState(PowerStateOn);
-
- if (!op.plane_set) {
- op.plane_set = true;
- if (op.wants_plane) {
- int ret = drmModeSetPlane(fd, op.plane_id, op.crtc_id,
- uint32_t(-1), 0,
- 0, 0, w, h,
- 0 << 16, 0 << 16, w << 16, h << 16);
- if (ret == -1)
- qErrnoWarning(errno, "drmModeSetPlane failed");
- }
- }
- }
- }
-
+ m_flipPending = true;
int ret = drmModePageFlip(fd,
op.crtc_id,
fb->fb,
DRM_MODE_PAGE_FLIP_EVENT,
this);
if (ret) {
- qErrnoWarning("Could not queue DRM page flip!");
+ qErrnoWarning("Could not queue DRM page flip on screen %s", qPrintable(name()));
+ m_flipPending = false;
gbm_surface_release_buffer(m_gbm_surface, m_gbm_bo_next);
- m_gbm_bo_next = Q_NULLPTR;
+ m_gbm_bo_next = nullptr;
+ return;
+ }
+
+ for (CloneDestination &d : m_cloneDests) {
+ if (d.screen != this) {
+ d.screen->ensureModeSet(fb->fb);
+ d.cloneFlipPending = true;
+ int ret = drmModePageFlip(fd,
+ d.screen->output().crtc_id,
+ fb->fb,
+ DRM_MODE_PAGE_FLIP_EVENT,
+ d.screen);
+ if (ret) {
+ qErrnoWarning("Could not queue DRM page flip for clone screen %s", qPrintable(name()));
+ d.cloneFlipPending = false;
+ }
+ }
}
}
+void QEglFSKmsGbmScreen::pageFlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data)
+{
+ Q_UNUSED(fd);
+ Q_UNUSED(sequence);
+ Q_UNUSED(tv_sec);
+ Q_UNUSED(tv_usec);
+
+ QEglFSKmsGbmScreen *screen = static_cast<QEglFSKmsGbmScreen *>(user_data);
+ screen->flipFinished();
+}
+
void QEglFSKmsGbmScreen::flipFinished()
{
+ if (m_cloneSource) {
+ m_cloneSource->cloneDestFlipFinished(this);
+ return;
+ }
+
+ m_flipPending = false;
+ updateFlipStatus();
+}
+
+void QEglFSKmsGbmScreen::cloneDestFlipFinished(QEglFSKmsGbmScreen *cloneDestScreen)
+{
+ for (CloneDestination &d : m_cloneDests) {
+ if (d.screen == cloneDestScreen) {
+ d.cloneFlipPending = false;
+ break;
+ }
+ }
+ updateFlipStatus();
+}
+
+void QEglFSKmsGbmScreen::updateFlipStatus()
+{
+ Q_ASSERT(!m_cloneSource);
+
+ if (m_flipPending)
+ return;
+
+ for (const CloneDestination &d : m_cloneDests) {
+ if (d.cloneFlipPending)
+ return;
+ }
+
if (m_gbm_bo_current)
gbm_surface_release_buffer(m_gbm_surface,
m_gbm_bo_current);
m_gbm_bo_current = m_gbm_bo_next;
- m_gbm_bo_next = Q_NULLPTR;
+ m_gbm_bo_next = nullptr;
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h
index 341cc95bbe..f5a2122723 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h
@@ -54,34 +54,54 @@ class QEglFSKmsGbmCursor;
class QEglFSKmsGbmScreen : public QEglFSKmsScreen
{
public:
- QEglFSKmsGbmScreen(QKmsDevice *device, const QKmsOutput &output);
+ QEglFSKmsGbmScreen(QKmsDevice *device, const QKmsOutput &output, bool headless);
~QEglFSKmsGbmScreen();
QPlatformCursor *cursor() const override;
- gbm_surface *surface() const { return m_gbm_surface; }
gbm_surface *createSurface();
- void destroySurface();
+ void resetSurface();
+
+ void initCloning(QPlatformScreen *screenThisScreenClones,
+ const QVector<QPlatformScreen *> &screensCloningThisScreen);
void waitForFlip() override;
- void flip() override;
- void flipFinished() override;
+
+ void flip();
private:
+ void flipFinished();
+ void ensureModeSet(uint32_t fb);
+ void cloneDestFlipFinished(QEglFSKmsGbmScreen *cloneDestScreen);
+ void updateFlipStatus();
+
+ static void pageFlipHandler(int fd,
+ unsigned int sequence,
+ unsigned int tv_sec,
+ unsigned int tv_usec,
+ void *user_data);
+
gbm_surface *m_gbm_surface;
gbm_bo *m_gbm_bo_current;
gbm_bo *m_gbm_bo_next;
+ bool m_flipPending;
QScopedPointer<QEglFSKmsGbmCursor> m_cursor;
struct FrameBuffer {
- FrameBuffer() : fb(0) {}
- uint32_t fb;
+ uint32_t fb = 0;
};
static void bufferDestroyedHandler(gbm_bo *bo, void *data);
FrameBuffer *framebufferForBufferObject(gbm_bo *bo);
+ QEglFSKmsGbmScreen *m_cloneSource;
+ struct CloneDestination {
+ QEglFSKmsGbmScreen *screen = nullptr;
+ bool cloneFlipPending = false;
+ };
+ QVector<CloneDestination> m_cloneDests;
+
static QMutex m_waitForFlipMutex;
};
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.cpp
new file mode 100644
index 0000000000..110a592dec
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.cpp
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** 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 "qeglfskmsgbmwindow.h"
+#include "qeglfskmsgbmintegration.h"
+#include "qeglfskmsgbmscreen.h"
+
+#include <QtEglSupport/private/qeglconvenience_p.h>
+
+QT_BEGIN_NAMESPACE
+
+void QEglFSKmsGbmWindow::resetSurface()
+{
+ QEglFSKmsGbmScreen *gbmScreen = static_cast<QEglFSKmsGbmScreen *>(screen());
+ EGLDisplay display = gbmScreen->display();
+ QSurfaceFormat platformFormat = m_integration->surfaceFormatFor(window()->requestedFormat());
+ m_config = QEglFSDeviceIntegration::chooseConfig(display, platformFormat);
+ m_format = q_glFormatFromConfig(display, m_config, platformFormat);
+ // One fullscreen window per screen -> the native window is simply the gbm_surface the screen created.
+ m_window = reinterpret_cast<EGLNativeWindowType>(gbmScreen->createSurface());
+
+ PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC createPlatformWindowSurface = nullptr;
+ const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+ if (extensions && (strstr(extensions, "EGL_KHR_platform_gbm") || strstr(extensions, "EGL_MESA_platform_gbm"))) {
+ createPlatformWindowSurface = reinterpret_cast<PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC>(
+ eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT"));
+ }
+
+ if (createPlatformWindowSurface) {
+ m_surface = createPlatformWindowSurface(display, m_config, reinterpret_cast<void *>(m_window), nullptr);
+ } else {
+ qCDebug(qLcEglfsKmsDebug, "No eglCreatePlatformWindowSurface for GBM, falling back to eglCreateWindowSurface");
+ m_surface = eglCreateWindowSurface(display, m_config, m_window, nullptr);
+ }
+}
+
+void QEglFSKmsGbmWindow::invalidateSurface()
+{
+ QEglFSKmsGbmScreen *gbmScreen = static_cast<QEglFSKmsGbmScreen *>(screen());
+ QEglFSWindow::invalidateSurface();
+ gbmScreen->resetSurface();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.h
new file mode 100644
index 0000000000..a19cf7e8bc
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** 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 QEGLFSKMSGBMWINDOW_H
+#define QEGLFSKMSGBMWINDOW_H
+
+#include "private/qeglfswindow_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QEglFSKmsGbmIntegration;
+
+class QEglFSKmsGbmWindow : public QEglFSWindow
+{
+public:
+ QEglFSKmsGbmWindow(QWindow *w, const QEglFSKmsGbmIntegration *integration)
+ : QEglFSWindow(w),
+ m_integration(integration)
+ { }
+ void resetSurface() override;
+ void invalidateSurface() override;
+
+private:
+ const QEglFSKmsGbmIntegration *m_integration;
+};
+
+QT_END_NAMESPACE
+
+#endif // QEGLFSKMSGBMWINDOW_H
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/eglfs_kms_egldevice.pro b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/eglfs_kms_egldevice.pro
index a2dc9c4a50..36f037ac6c 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/eglfs_kms_egldevice.pro
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/eglfs_kms_egldevice.pro
@@ -1,6 +1,6 @@
TARGET = qeglfs-kms-egldevice-integration
-QT += core-private gui-private eglfsdeviceintegration-private eglfs_kms_support-private kms_support-private
+QT += core-private gui-private eglfsdeviceintegration-private eglfs_kms_support-private kms_support-private edid_support-private
INCLUDEPATH += $$PWD/../../api $$PWD/../eglfs_kms_support
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevice.cpp
index 0a66a897a1..8c8e6260f1 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevice.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevice.cpp
@@ -58,7 +58,7 @@ bool QEglFSKmsEglDevice::open()
{
Q_ASSERT(fd() == -1);
- int fd = drmOpen(devicePath().toLocal8Bit().constData(), Q_NULLPTR);
+ int fd = drmOpen(devicePath().toLocal8Bit().constData(), nullptr);
if (Q_UNLIKELY(fd < 0))
qFatal("Could not open DRM (NV) device");
@@ -77,9 +77,9 @@ void QEglFSKmsEglDevice::close()
setFd(-1);
}
-EGLNativeDisplayType QEglFSKmsEglDevice::nativeDisplay() const
+void *QEglFSKmsEglDevice::nativeDisplay() const
{
- return reinterpret_cast<EGLNativeDisplayType>(m_devInt->eglDevice());
+ return m_devInt->eglDevice();
}
QPlatformScreen *QEglFSKmsEglDevice::createScreen(const QKmsOutput &output)
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp
index 3af21d768e..a67457a6a5 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp
@@ -51,7 +51,7 @@ QT_BEGIN_NAMESPACE
QEglFSKmsEglDeviceIntegration::QEglFSKmsEglDeviceIntegration()
: m_egl_device(EGL_NO_DEVICE_EXT)
- , m_funcs(Q_NULLPTR)
+ , m_funcs(nullptr)
{
qCDebug(qLcEglfsKmsDebug, "New DRM/KMS on EGLDevice integration created");
}
@@ -75,7 +75,7 @@ EGLDisplay QEglFSKmsEglDeviceIntegration::createDisplay(EGLNativeDisplayType nat
EGLDisplay display;
if (m_funcs->has_egl_platform_device) {
- display = m_funcs->get_platform_display(EGL_PLATFORM_DEVICE_EXT, nativeDisplay, Q_NULLPTR);
+ display = m_funcs->get_platform_display(EGL_PLATFORM_DEVICE_EXT, nativeDisplay, nullptr);
} else {
qWarning("EGL_EXT_platform_device not available, falling back to legacy path!");
display = eglGetDisplay(nativeDisplay);
@@ -162,7 +162,7 @@ void QEglFSKmsEglDeviceWindow::resetSurface()
qCDebug(qLcEglfsKmsDebug, "Could not query number of EGLStream FIFO frames");
}
- if (!m_integration->m_funcs->get_output_layers(display, Q_NULLPTR, Q_NULLPTR, 0, &count) || count == 0) {
+ if (!m_integration->m_funcs->get_output_layers(display, nullptr, nullptr, 0, &count) || count == 0) {
qWarning("No output layers found");
return;
}
@@ -172,7 +172,7 @@ void QEglFSKmsEglDeviceWindow::resetSurface()
QVector<EGLOutputLayerEXT> layers;
layers.resize(count);
EGLint actualCount;
- if (!m_integration->m_funcs->get_output_layers(display, Q_NULLPTR, layers.data(), count, &actualCount)) {
+ if (!m_integration->m_funcs->get_output_layers(display, nullptr, layers.data(), count, &actualCount)) {
qWarning("Failed to get layers");
return;
}
@@ -180,7 +180,7 @@ void QEglFSKmsEglDeviceWindow::resetSurface()
QEglFSKmsEglDeviceScreen *cur_screen = static_cast<QEglFSKmsEglDeviceScreen *>(screen());
Q_ASSERT(cur_screen);
QKmsOutput &output(cur_screen->output());
- const uint32_t wantedId = !output.wants_plane ? output.crtc_id : output.plane_id;
+ const uint32_t wantedId = !output.wants_forced_plane ? output.crtc_id : output.forced_plane_id;
qCDebug(qLcEglfsKmsDebug, "Searching for id: %d", wantedId);
EGLOutputLayerEXT layer = EGL_NO_OUTPUT_LAYER_EXT;
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.cpp
index a27c89faab..531b73d1dc 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.cpp
@@ -105,12 +105,12 @@ void QEglFSKmsEglDeviceScreen::waitForFlip()
qErrnoWarning(errno, "drmModeSetCrtc failed");
}
- if (!op.plane_set) {
- op.plane_set = true;
+ if (!op.forced_plane_set) {
+ op.forced_plane_set = true;
- if (op.wants_plane) {
- qCDebug(qLcEglfsKmsDebug, "Setting plane %u", op.plane_id);
- int ret = drmModeSetPlane(fd, op.plane_id, op.crtc_id, uint32_t(-1), 0,
+ if (op.wants_forced_plane) {
+ qCDebug(qLcEglfsKmsDebug, "Setting plane %u", op.forced_plane_id);
+ int ret = drmModeSetPlane(fd, op.forced_plane_id, op.crtc_id, uint32_t(-1), 0,
0, 0, w, h,
0 << 16, 0 << 16, w << 16, h << 16);
if (ret == -1)
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
index 3c0a0ce30f..4d1321079c 100644
--- 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
@@ -2,7 +2,7 @@ TARGET = QtEglFsKmsSupport
CONFIG += no_module_headers internal_module
load(qt_module)
-QT += core-private gui-private eglfsdeviceintegration-private kms_support-private
+QT += core-private gui-private eglfsdeviceintegration-private kms_support-private edid_support-private
INCLUDEPATH += $$PWD/../../api
@@ -19,4 +19,5 @@ SOURCES += $$PWD/qeglfskmsintegration.cpp \
HEADERS += $$PWD/qeglfskmsintegration.h \
$$PWD/qeglfskmsdevice.h \
- $$PWD/qeglfskmsscreen.h
+ $$PWD/qeglfskmsscreen.h \
+ $$PWD/qeglfskmshelpers.h
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmshelpers.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmshelpers.h
new file mode 100644
index 0000000000..c9e5f04a7b
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmshelpers.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** 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 QEGLFSKMSHELPERS_H
+#define QEGLFSKMSHELPERS_H
+
+#include <QString>
+
+QT_BEGIN_NAMESPACE
+
+inline QString q_fourccToString(uint code)
+{
+ QString s;
+ s.reserve(4);
+ s.append(code & 0xff);
+ s.append((code >> 8) & 0xff);
+ s.append((code >> 16) & 0xff);
+ s.append((code >> 24) & 0xff);
+ return s;
+}
+
+QT_END_NAMESPACE
+
+#endif // QEGLFSKMSHELPERS_H
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp
index c77151181e..06bc272050 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp
@@ -55,7 +55,7 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(qLcEglfsKmsDebug, "qt.qpa.eglfs.kms")
QEglFSKmsIntegration::QEglFSKmsIntegration()
- : m_device(Q_NULLPTR),
+ : m_device(nullptr),
m_screenConfig(new QKmsScreenConfig)
{
}
@@ -78,7 +78,7 @@ void QEglFSKmsIntegration::platformDestroy()
qCDebug(qLcEglfsKmsDebug, "platformDestroy: Closing DRM device");
m_device->close();
delete m_device;
- m_device = Q_NULLPTR;
+ m_device = nullptr;
}
EGLNativeDisplayType QEglFSKmsIntegration::platformDisplay() const
@@ -133,6 +133,24 @@ bool QEglFSKmsIntegration::supportsPBuffers() const
return m_screenConfig->supportsPBuffers();
}
+void *QEglFSKmsIntegration::nativeResourceForIntegration(const QByteArray &name)
+{
+ if (name == QByteArrayLiteral("dri_fd") && m_device)
+ return (void *) (qintptr) m_device->fd();
+
+ return nullptr;
+}
+
+void *QEglFSKmsIntegration::nativeResourceForScreen(const QByteArray &resource, QScreen *screen)
+{
+ QEglFSKmsScreen *s = static_cast<QEglFSKmsScreen *>(screen->handle());
+ if (s) {
+ if (resource == QByteArrayLiteral("dri_crtcid"))
+ return (void *) (qintptr) s->output().crtc_id;
+ }
+ return nullptr;
+}
+
QKmsDevice *QEglFSKmsIntegration::device() const
{
return m_device;
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.h
index 9955616919..e2c37f60fc 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.h
@@ -69,6 +69,8 @@ public:
bool hasCapability(QPlatformIntegration::Capability cap) const override;
void waitForVSync(QPlatformSurface *surface) const override;
bool supportsPBuffers() const override;
+ void *nativeResourceForIntegration(const QByteArray &name) override;
+ void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen) override;
QKmsDevice *device() const;
QKmsScreenConfig *screenConfig() const;
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp
index 3951f46a82..5e45b42abe 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2017 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
** Copyright (C) 2016 Pelagicore AG
** Contact: https://www.qt.io/licensing/
**
@@ -68,14 +68,31 @@ private:
QEglFSKmsScreen *m_screen;
};
-QEglFSKmsScreen::QEglFSKmsScreen(QKmsDevice *device, const QKmsOutput &output)
- : QEglFSScreen(eglGetDisplay((EGLNativeDisplayType) device->nativeDisplay()))
+QEglFSKmsScreen::QEglFSKmsScreen(QKmsDevice *device, const QKmsOutput &output, bool headless)
+ : QEglFSScreen(static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration())->display())
, m_device(device)
, m_output(output)
, m_powerState(PowerStateOn)
, m_interruptHandler(new QEglFSKmsInterruptHandler(this))
+ , m_headless(headless)
{
m_siblings << this; // gets overridden later
+
+ if (m_output.edid_blob) {
+ QByteArray edid(reinterpret_cast<const char *>(m_output.edid_blob->data), m_output.edid_blob->length);
+ if (m_edid.parse(edid))
+ qCDebug(qLcEglfsKmsDebug, "EDID data for output \"%s\": identifier '%s', manufacturer '%s', model '%s', serial '%s', physical size: %.2fx%.2f",
+ name().toLatin1().constData(),
+ m_edid.identifier.toLatin1().constData(),
+ m_edid.manufacturer.toLatin1().constData(),
+ m_edid.model.toLatin1().constData(),
+ m_edid.serialNumber.toLatin1().constData(),
+ m_edid.physicalSize.width(), m_edid.physicalSize.height());
+ else
+ qCDebug(qLcEglfsKmsDebug) << "Failed to parse EDID data for output" << name(); // keep this debug, not warning
+ } else {
+ qCDebug(qLcEglfsKmsDebug) << "No EDID data for output" << name();
+ }
}
QEglFSKmsScreen::~QEglFSKmsScreen()
@@ -93,6 +110,9 @@ void QEglFSKmsScreen::setVirtualPosition(const QPoint &pos)
// geometry() calls rawGeometry() and may apply additional transforms.
QRect QEglFSKmsScreen::rawGeometry() const
{
+ if (m_headless)
+ return QRect(QPoint(0, 0), m_device->screenConfig()->headlessSize());
+
const int mode = m_output.mode;
return QRect(m_pos.x(), m_pos.y(),
m_output.modes[mode].hdisplay,
@@ -101,12 +121,30 @@ QRect QEglFSKmsScreen::rawGeometry() const
int QEglFSKmsScreen::depth() const
{
- return 32;
+ return format() == QImage::Format_RGB16 ? 16 : 32;
}
QImage::Format QEglFSKmsScreen::format() const
{
- return QImage::Format_RGB32;
+ // the result can be slightly incorrect, it won't matter in practice
+ switch (m_output.drm_format) {
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_ABGR8888:
+ return QImage::Format_ARGB32;
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_BGR565:
+ return QImage::Format_RGB16;
+ case DRM_FORMAT_XRGB2101010:
+ return QImage::Format_RGB30;
+ case DRM_FORMAT_XBGR2101010:
+ return QImage::Format_BGR30;
+ case DRM_FORMAT_ARGB2101010:
+ return QImage::Format_A2RGB30_Premultiplied;
+ case DRM_FORMAT_ABGR2101010:
+ return QImage::Format_A2BGR30_Premultiplied;
+ default:
+ return QImage::Format_RGB32;
+ }
}
QSizeF QEglFSKmsScreen::physicalSize() const
@@ -143,22 +181,25 @@ Qt::ScreenOrientation QEglFSKmsScreen::orientation() const
QString QEglFSKmsScreen::name() const
{
- return m_output.name;
+ return !m_headless ? m_output.name : QStringLiteral("qt_Headless");
}
-void QEglFSKmsScreen::destroySurface()
+QString QEglFSKmsScreen::manufacturer() const
{
+ return m_edid.manufacturer;
}
-void QEglFSKmsScreen::waitForFlip()
+QString QEglFSKmsScreen::model() const
{
+ return m_edid.model.isEmpty() ? m_edid.identifier : m_edid.model;
}
-void QEglFSKmsScreen::flip()
+QString QEglFSKmsScreen::serialNumber() const
{
+ return m_edid.serialNumber;
}
-void QEglFSKmsScreen::flipFinished()
+void QEglFSKmsScreen::waitForFlip()
{
}
@@ -169,10 +210,35 @@ void QEglFSKmsScreen::restoreMode()
qreal QEglFSKmsScreen::refreshRate() const
{
+ if (m_headless)
+ return 60;
+
quint32 refresh = m_output.modes[m_output.mode].vrefresh;
return refresh > 0 ? refresh : 60;
}
+QVector<QPlatformScreen::Mode> QEglFSKmsScreen::modes() const
+{
+ QVector<QPlatformScreen::Mode> list;
+ list.reserve(m_output.modes.size());
+
+ for (const drmModeModeInfo &info : qAsConst(m_output.modes))
+ list.append({QSize(info.hdisplay, info.vdisplay),
+ qreal(info.vrefresh > 0 ? info.vrefresh : 60)});
+
+ return list;
+}
+
+int QEglFSKmsScreen::currentMode() const
+{
+ return m_output.mode;
+}
+
+int QEglFSKmsScreen::preferredMode() const
+{
+ return m_output.preferred_mode;
+}
+
QPlatformScreen::SubpixelAntialiasingType QEglFSKmsScreen::subpixelAntialiasingTypeHint() const
{
return m_output.subpixelAntialiasingTypeHint();
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h
index 80bbb0c7f1..7f395aacb7 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h
@@ -47,6 +47,7 @@
#include <QtCore/QMutex>
#include <QtKmsSupport/private/qkmsdevice_p.h>
+#include <QtEdidSupport/private/qedidparser_p.h>
QT_BEGIN_NAMESPACE
@@ -55,7 +56,7 @@ class QEglFSKmsInterruptHandler;
class Q_EGLFS_EXPORT QEglFSKmsScreen : public QEglFSScreen
{
public:
- QEglFSKmsScreen(QKmsDevice *device, const QKmsOutput &output);
+ QEglFSKmsScreen(QKmsDevice *device, const QKmsOutput &output, bool headless = false);
~QEglFSKmsScreen();
void setVirtualPosition(const QPoint &pos);
@@ -72,18 +73,23 @@ public:
QString name() const override;
+ QString manufacturer() const override;
+ QString model() const override;
+ QString serialNumber() const override;
+
qreal refreshRate() const override;
QList<QPlatformScreen *> virtualSiblings() const override { return m_siblings; }
void setVirtualSiblings(QList<QPlatformScreen *> sl) { m_siblings = sl; }
- QKmsDevice *device() const { return m_device; }
+ QVector<QPlatformScreen::Mode> modes() const override;
- void destroySurface();
+ int currentMode() const override;
+ int preferredMode() const override;
+
+ QKmsDevice *device() const { return m_device; }
virtual void waitForFlip();
- virtual void flip();
- virtual void flipFinished();
QKmsOutput &output() { return m_output; }
void restoreMode();
@@ -97,6 +103,7 @@ protected:
QKmsDevice *m_device;
QKmsOutput m_output;
+ QEdidParser m_edid;
QPoint m_pos;
QList<QPlatformScreen *> m_siblings;
@@ -104,6 +111,8 @@ protected:
PowerState m_powerState;
QEglFSKmsInterruptHandler *m_interruptHandler;
+
+ bool m_headless;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/eglfs_kms_vsp2.json b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/eglfs_kms_vsp2.json
new file mode 100644
index 0000000000..b898dc27cb
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/eglfs_kms_vsp2.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "eglfs_kms_vsp2" ]
+}
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/eglfs_kms_vsp2.pro b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/eglfs_kms_vsp2.pro
new file mode 100644
index 0000000000..826c2f989f
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/eglfs_kms_vsp2.pro
@@ -0,0 +1,31 @@
+TARGET = qeglfs-kms-vsp2-integration
+
+PLUGIN_TYPE = egldeviceintegrations
+PLUGIN_CLASS_NAME = QEglFSKmsVsp2IntegrationPlugin
+load(qt_plugin)
+
+QT += core-private gui-private eglfsdeviceintegration-private eglfs_kms_support-private kms_support-private edid_support-private
+
+INCLUDEPATH += $$PWD/../../api $$PWD/../eglfs_kms_support
+
+# Avoid X11 header collision, use generic EGL native types
+DEFINES += QT_EGL_NO_X11
+
+QMAKE_USE += gbm drm v4l2
+CONFIG += egl
+QMAKE_LFLAGS += $$QMAKE_LFLAGS_NOUNDEF
+
+SOURCES += $$PWD/qeglfskmsvsp2main.cpp \
+ $$PWD/qeglfskmsvsp2integration.cpp \
+ $$PWD/qeglfskmsvsp2device.cpp \
+ $$PWD/qeglfskmsvsp2screen.cpp \
+ $$PWD/qlinuxmediadevice.cpp \
+ $$PWD/qvsp2blendingdevice.cpp
+
+HEADERS += $$PWD/qeglfskmsvsp2integration.h \
+ $$PWD/qeglfskmsvsp2device.h \
+ $$PWD/qeglfskmsvsp2screen.h \
+ $$PWD/qlinuxmediadevice.h \
+ $$PWD/qvsp2blendingdevice.h
+
+OTHER_FILES += $$PWD/eglfs_kms.json
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2device.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2device.cpp
new file mode 100644
index 0000000000..44f855910c
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2device.cpp
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+** Copyright (C) 2017 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 "qeglfskmsvsp2device.h"
+#include "qeglfskmsvsp2screen.h"
+
+#include "qeglfsintegration_p.h"
+
+#include <QtCore/QLoggingCategory>
+#include <QtCore/private/qcore_unix_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
+
+QEglFSKmsVsp2Device::QEglFSKmsVsp2Device(QKmsScreenConfig *screenConfig, const QString &path)
+ : QEglFSKmsDevice(screenConfig, path)
+{
+}
+
+bool QEglFSKmsVsp2Device::open()
+{
+ Q_ASSERT(fd() == -1);
+ Q_ASSERT(m_gbm_device == nullptr);
+
+ int fd = qt_safe_open(devicePath().toLocal8Bit().constData(), O_RDWR | O_CLOEXEC);
+ if (fd == -1) {
+ qErrnoWarning("Could not open DRM device %s", qPrintable(devicePath()));
+ return false;
+ }
+
+ qCDebug(qLcEglfsKmsDebug) << "Creating GBM device for file descriptor" << fd
+ << "obtained from" << devicePath();
+ m_gbm_device = gbm_create_device(fd);
+ if (!m_gbm_device) {
+ qErrnoWarning("Could not create GBM device");
+ qt_safe_close(fd);
+ fd = -1;
+ return false;
+ }
+
+ setFd(fd);
+
+ return true;
+}
+
+void QEglFSKmsVsp2Device::close()
+{
+ // Note: screens are gone at this stage.
+
+ if (m_gbm_device) {
+ gbm_device_destroy(m_gbm_device);
+ m_gbm_device = nullptr;
+ }
+
+ if (fd() != -1) {
+ qt_safe_close(fd());
+ setFd(-1);
+ }
+}
+
+void *QEglFSKmsVsp2Device::nativeDisplay() const
+{
+ return m_gbm_device;
+}
+
+gbm_device * QEglFSKmsVsp2Device::gbmDevice() const
+{
+ return m_gbm_device;
+}
+
+QPlatformScreen *QEglFSKmsVsp2Device::createScreen(const QKmsOutput &output)
+{
+ auto *screen = new QEglFSKmsVsp2Screen(this, output);
+
+ return screen;
+}
+
+QPlatformScreen *QEglFSKmsVsp2Device::createHeadlessScreen()
+{
+ qWarning() << Q_FUNC_INFO << "Not implemented yet";
+ return nullptr;
+}
+
+void QEglFSKmsVsp2Device::registerScreenCloning(QPlatformScreen *screen,
+ QPlatformScreen *screenThisScreenClones,
+ const QVector<QPlatformScreen *> &screensCloningThisScreen)
+{
+ Q_UNUSED(screen);
+ qWarning() << Q_FUNC_INFO << "Not implemented yet";
+ if (!screenThisScreenClones && screensCloningThisScreen.isEmpty())
+ return;
+
+// auto *vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen);
+// vsp2Screen->initCloning(screenThisScreenClones, screensCloningThisScreen);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2device.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2device.h
new file mode 100644
index 0000000000..c795fa4005
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2device.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+** Copyright (C) 2017 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 QEGLFSKMSVSP2DEVICE_H
+#define QEGLFSKMSVSP2DEVICE_H
+
+#include <qeglfskmsdevice.h>
+
+#include <gbm.h>
+
+QT_BEGIN_NAMESPACE
+
+class QEglFSKmsScreen;
+
+class QEglFSKmsVsp2Device: public QEglFSKmsDevice
+{
+public:
+ QEglFSKmsVsp2Device(QKmsScreenConfig *screenConfig, const QString &path);
+
+ bool open() override;
+ void close() override;
+
+ void *nativeDisplay() const override;
+ gbm_device *gbmDevice() const;
+
+ QPlatformScreen *createScreen(const QKmsOutput &output) override;
+ QPlatformScreen *createHeadlessScreen() override;
+ void registerScreenCloning(QPlatformScreen *screen,
+ QPlatformScreen *screenThisScreenClones,
+ const QVector<QPlatformScreen *> &screensCloningThisScreen) override;
+
+private:
+ Q_DISABLE_COPY(QEglFSKmsVsp2Device)
+
+ gbm_device *m_gbm_device = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QEGLFSKMSVSP2DEVICE_H
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.cpp
new file mode 100644
index 0000000000..0d9b6b6290
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.cpp
@@ -0,0 +1,250 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+** Copyright (C) 2017 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 "qeglfskmsvsp2integration.h"
+#include "qeglfskmsvsp2device.h"
+#include "qeglfskmsvsp2screen.h"
+#include "private/qeglfswindow_p.h"
+
+#include <QtDeviceDiscoverySupport/private/qdevicediscovery_p.h>
+#include <QtEglSupport/private/qeglconvenience_p.h>
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QJsonDocument>
+#include <QtCore/QJsonObject>
+#include <QtCore/QJsonArray>
+#include <QtGui/qpa/qplatformwindow.h>
+#include <QtGui/QScreen>
+#include <QtPlatformHeaders/qeglfsfunctions.h>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <gbm.h>
+
+QT_BEGIN_NAMESPACE
+
+QEglFSKmsVsp2Integration::QEglFSKmsVsp2Integration()
+{
+ qCDebug(qLcEglfsKmsDebug, "New DRM/KMS via Vsp2 integration created");
+}
+
+#ifndef EGL_EXT_platform_base
+typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC) (EGLenum platform, void *native_display, const EGLint *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC) (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLint *attrib_list);
+#endif
+
+#ifndef EGL_PLATFORM_GBM_KHR
+#define EGL_PLATFORM_GBM_KHR 0x31D7
+#endif
+
+EGLDisplay QEglFSKmsVsp2Integration::createDisplay(EGLNativeDisplayType nativeDisplay)
+{
+ qCDebug(qLcEglfsKmsDebug, "Querying EGLDisplay");
+ EGLDisplay display;
+
+ PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplay = nullptr;
+ const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+ if (extensions && (strstr(extensions, "EGL_KHR_platform_gbm") || strstr(extensions, "EGL_MESA_platform_gbm"))) {
+ getPlatformDisplay = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
+ eglGetProcAddress("eglGetPlatformDisplayEXT"));
+ }
+
+ if (getPlatformDisplay) {
+ display = getPlatformDisplay(EGL_PLATFORM_GBM_KHR, nativeDisplay, nullptr);
+ } else {
+ qCDebug(qLcEglfsKmsDebug, "No eglGetPlatformDisplay for GBM, falling back to eglGetDisplay");
+ display = eglGetDisplay(nativeDisplay);
+ }
+
+ return display;
+}
+
+EGLNativeWindowType QEglFSKmsVsp2Integration::createNativeOffscreenWindow(const QSurfaceFormat &format)
+{
+ Q_UNUSED(format);
+ Q_ASSERT(device());
+
+ gbm_surface *surface = gbm_surface_create(static_cast<QEglFSKmsVsp2Device *>(device())->gbmDevice(),
+ 1, 1,
+ GBM_FORMAT_XRGB8888,
+ GBM_BO_USE_RENDERING);
+
+ return reinterpret_cast<EGLNativeWindowType>(surface);
+}
+
+void QEglFSKmsVsp2Integration::destroyNativeWindow(EGLNativeWindowType window)
+{
+ gbm_surface *surface = reinterpret_cast<gbm_surface *>(window);
+ //TODO call screen destroysurface instead
+ gbm_surface_destroy(surface);
+}
+
+void QEglFSKmsVsp2Integration::presentBuffer(QPlatformSurface *surface)
+{
+ QWindow *window = static_cast<QWindow *>(surface->surface());
+ auto *screen = static_cast<QEglFSKmsVsp2Screen *>(window->screen()->handle());
+ screen->flip();
+}
+
+QFunctionPointer QEglFSKmsVsp2Integration::platformFunction(const QByteArray &function) const
+{
+ if (function == QEglFSFunctions::vsp2AddLayerTypeIdentifier())
+ return QFunctionPointer(addLayerStatic);
+ if (function == QEglFSFunctions::vsp2RemoveLayerTypeIdentifier())
+ return QFunctionPointer(removeLayerStatic);
+ if (function == QEglFSFunctions::vsp2SetLayerBufferTypeIdentifier())
+ return QFunctionPointer(setLayerBufferStatic);
+ if (function == QEglFSFunctions::vsp2SetLayerPositionTypeIdentifier())
+ return QFunctionPointer(setLayerPositionStatic);
+ if (function == QEglFSFunctions::vsp2SetLayerAlphaTypeIdentifier())
+ return QFunctionPointer(setLayerAlphaStatic);
+ if (function == QEglFSFunctions::vsp2AddBlendListenerTypeIdentifier())
+ return QFunctionPointer(addBlendListenerStatic);
+
+ return nullptr;
+}
+
+QKmsDevice *QEglFSKmsVsp2Integration::createDevice()
+{
+ QString path = screenConfig()->devicePath();
+ if (!path.isEmpty()) {
+ qCDebug(qLcEglfsKmsDebug) << "VSP2: Using DRM device" << path << "specified in config file";
+ } else {
+ QDeviceDiscovery *d = QDeviceDiscovery::create(QDeviceDiscovery::Device_VideoMask);
+ const QStringList devices = d->scanConnectedDevices();
+ qCDebug(qLcEglfsKmsDebug) << "Found the following video devices:" << devices;
+ d->deleteLater();
+
+ if (Q_UNLIKELY(devices.isEmpty()))
+ qFatal("Could not find DRM device!");
+
+ path = devices.first();
+ qCDebug(qLcEglfsKmsDebug) << "Using" << path;
+ }
+
+ return new QEglFSKmsVsp2Device(screenConfig(), path);
+}
+
+int QEglFSKmsVsp2Integration::addLayerStatic(const QScreen *screen, int dmabufFd, const QSize &size, const QPoint &position, uint pixelFormat, uint bytesPerLine)
+{
+ auto vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen->handle());
+ return vsp2Screen->addLayer(dmabufFd, size, position, pixelFormat, bytesPerLine);
+}
+
+bool QEglFSKmsVsp2Integration::removeLayerStatic(const QScreen *screen, int id)
+{
+ auto vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen->handle());
+ return vsp2Screen->removeLayer(id);
+}
+
+void QEglFSKmsVsp2Integration::setLayerBufferStatic(const QScreen *screen, int id, int dmabufFd)
+{
+ auto vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen->handle());
+ vsp2Screen->setLayerBuffer(id, dmabufFd);
+}
+
+void QEglFSKmsVsp2Integration::setLayerPositionStatic(const QScreen *screen, int id, const QPoint &position)
+{
+ auto vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen->handle());
+ vsp2Screen->setLayerPosition(id, position);
+}
+
+void QEglFSKmsVsp2Integration::setLayerAlphaStatic(const QScreen *screen, int id, qreal alpha)
+{
+ auto vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen->handle());
+ vsp2Screen->setLayerAlpha(id, alpha);
+}
+
+void QEglFSKmsVsp2Integration::addBlendListenerStatic(const QScreen *screen, void(*callback)())
+{
+ auto vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen->handle());
+ vsp2Screen->addBlendListener(callback);
+}
+
+class QEglFSKmsVsp2Window : public QEglFSWindow
+{
+public:
+ QEglFSKmsVsp2Window(QWindow *w, const QEglFSKmsVsp2Integration *integration)
+ : QEglFSWindow(w)
+ , m_integration(integration)
+ {}
+ void resetSurface() override;
+ void invalidateSurface() override;
+ const QEglFSKmsVsp2Integration *m_integration;
+};
+
+void QEglFSKmsVsp2Window::resetSurface()
+{
+ auto *vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen());
+ EGLDisplay display = vsp2Screen->display();
+ QSurfaceFormat platformFormat = m_integration->surfaceFormatFor(window()->requestedFormat());
+ m_config = QEglFSDeviceIntegration::chooseConfig(display, platformFormat);
+ m_format = q_glFormatFromConfig(display, m_config, platformFormat);
+ // One fullscreen window per screen -> the native window is simply the gbm_surface the screen created.
+ m_window = reinterpret_cast<EGLNativeWindowType>(vsp2Screen->createSurface());
+
+ PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC createPlatformWindowSurface = nullptr;
+ const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+ if (extensions && (strstr(extensions, "EGL_KHR_platform_gbm") || strstr(extensions, "EGL_MESA_platform_gbm"))) {
+ createPlatformWindowSurface = reinterpret_cast<PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC>(
+ eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT"));
+ }
+
+ if (createPlatformWindowSurface) {
+ m_surface = createPlatformWindowSurface(display, m_config, reinterpret_cast<void *>(m_window), nullptr);
+ } else {
+ qCDebug(qLcEglfsKmsDebug, "No eglCreatePlatformWindowSurface for GBM, falling back to eglCreateWindowSurface");
+ m_surface = eglCreateWindowSurface(display, m_config, m_window, nullptr);
+ }
+}
+
+void QEglFSKmsVsp2Window::invalidateSurface()
+{
+ auto *vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen());
+ QEglFSWindow::invalidateSurface();
+ vsp2Screen->resetSurface();
+}
+
+QEglFSWindow *QEglFSKmsVsp2Integration::createWindow(QWindow *window) const
+{
+ return new QEglFSKmsVsp2Window(window, this);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.h
new file mode 100644
index 0000000000..b1a8a2edf3
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+** Copyright (C) 2017 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 QEGLFSKMSVSP2INTEGRATION_H
+#define QEGLFSKMSVSP2INTEGRATION_H
+
+#include "qeglfskmsintegration.h"
+#include <QtCore/QMap>
+#include <QtCore/QVariant>
+
+QT_BEGIN_NAMESPACE
+
+class QEglFSKmsDevice;
+
+class QEglFSKmsVsp2Integration : public QEglFSKmsIntegration
+{
+public:
+ QEglFSKmsVsp2Integration();
+
+ EGLDisplay createDisplay(EGLNativeDisplayType nativeDisplay) override;
+ EGLNativeWindowType createNativeOffscreenWindow(const QSurfaceFormat &format) override;
+ void destroyNativeWindow(EGLNativeWindowType window) override;
+
+ void presentBuffer(QPlatformSurface *surface) override;
+ QEglFSWindow *createWindow(QWindow *window) const override;
+
+ QFunctionPointer platformFunction(const QByteArray &function) const override;
+
+protected:
+ QKmsDevice *createDevice() override;
+
+private:
+ static int addLayerStatic(const QScreen *screen, int dmabufFd, const QSize &size, const QPoint &position, uint pixelFormat, uint bytesPerLine);
+ static bool removeLayerStatic(const QScreen *screen, int id);
+ static void setLayerBufferStatic(const QScreen *screen, int id, int dmabufFd);
+ static void setLayerPositionStatic(const QScreen *screen, int id, const QPoint &position);
+ static void setLayerAlphaStatic(const QScreen *screen, int id, qreal alpha);
+ static void addBlendListenerStatic(const QScreen *screen, void(*callback)());
+};
+
+QT_END_NAMESPACE
+
+#endif // QEGLFSKMSVSP2INTEGRATION_H
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2main.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2main.cpp
new file mode 100644
index 0000000000..1345d38359
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2main.cpp
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the qmake spec 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 "private/qeglfsdeviceintegration_p.h"
+#include "qeglfskmsvsp2integration.h"
+
+QT_BEGIN_NAMESPACE
+
+class QEglFSKmsVsp2IntegrationPlugin : public QEglFSDeviceIntegrationPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QEglFSDeviceIntegrationFactoryInterface_iid FILE "eglfs_kms_vsp2.json")
+
+public:
+ QEglFSDeviceIntegration *create() override { return new QEglFSKmsVsp2Integration; }
+};
+
+QT_END_NAMESPACE
+
+#include "qeglfskmsvsp2main.moc"
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.cpp
new file mode 100644
index 0000000000..88b401c920
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.cpp
@@ -0,0 +1,365 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+** 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 "qeglfskmsvsp2screen.h"
+#include "qeglfskmsvsp2device.h"
+#include <qeglfskmshelpers.h>
+
+#include <QtCore/QLoggingCategory>
+#include <QtGui/private/qguiapplication_p.h>
+
+#include <drm_fourcc.h>
+#include <xf86drm.h>
+#include <fcntl.h>
+
+//TODO move to qlinuxmediadevice?
+#include <cstdlib> //this needs to go before mediactl/mediactl.h because it uses size_t without including it
+extern "C" {
+#include <mediactl/mediactl.h>
+#include <mediactl/v4l2subdev.h> //needed in header for default arguments
+}
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
+
+static inline uint32_t drmFormatToGbmFormat(uint32_t drmFormat)
+{
+ Q_ASSERT(DRM_FORMAT_XRGB8888 == GBM_FORMAT_XRGB8888);
+ return drmFormat;
+}
+
+static inline uint32_t gbmFormatToDrmFormat(uint32_t gbmFormat) //TODO: is this needed?
+{
+ Q_ASSERT(DRM_FORMAT_XRGB8888 == GBM_FORMAT_XRGB8888);
+ return gbmFormat;
+}
+
+void QEglFSKmsVsp2Screen::dmaBufferDestroyedHandler(gbm_bo *gbmBo, void *data)
+{
+ Q_UNUSED(gbmBo); //TODO: do we need to do something with it after all?
+ auto dmabuf = static_cast<DmaBuffer *>(data);
+ //TODO: need some extra cleanup here?
+ delete dmabuf;
+}
+
+QEglFSKmsVsp2Screen::DmaBuffer *QEglFSKmsVsp2Screen::dmaBufferForGbmBuffer(gbm_bo *gbmBo)
+{
+ auto existingBuffer = static_cast<DmaBuffer *>(gbm_bo_get_user_data(gbmBo));
+ if (existingBuffer)
+ return existingBuffer;
+
+ uint32_t handle = gbm_bo_get_handle(gbmBo).u32;
+ QScopedPointer<DmaBuffer> fb(new DmaBuffer);
+ int ret = drmPrimeHandleToFD(device()->fd(), handle, DRM_CLOEXEC, &fb->dmabufFd);
+ if (ret) {
+ qWarning("Failed to create dmabuf file descriptor for buffer with drmPrimeHandleToFd");
+ return nullptr;
+ }
+
+ gbm_bo_set_user_data(gbmBo, fb.data(), dmaBufferDestroyedHandler);
+ return fb.take();
+}
+
+QEglFSKmsVsp2Screen::QEglFSKmsVsp2Screen(QKmsDevice *device, const QKmsOutput &output)
+ : QEglFSKmsScreen(device, output)
+ , m_blender(new Blender(this))
+{
+}
+
+gbm_surface *QEglFSKmsVsp2Screen::createSurface()
+{
+ if (!m_gbmSurface) {
+ uint32_t gbmFormat = drmFormatToGbmFormat(m_output.drm_format);
+ qCDebug(qLcEglfsKmsDebug, "Creating gbm_surface for screen %s with format 0x%x", qPrintable(name()), gbmFormat);
+ Q_ASSERT(rawGeometry().isValid());
+ m_gbmSurface = gbm_surface_create(static_cast<QEglFSKmsVsp2Device *>(device())->gbmDevice(),
+ uint(rawGeometry().width()),
+ uint(rawGeometry().height()),
+ gbmFormat,
+ GBM_BO_USE_RENDERING);
+ }
+
+ if (!m_blendDevice)
+ initVsp2();
+
+ if (m_frameBuffers[m_backFb].dmabufFd == -1)
+ initDumbFrameBuffers();
+
+ return m_gbmSurface; // not owned, gets destroyed in QEglFSKmsGbmIntegration::destroyNativeWindow()
+}
+
+void QEglFSKmsVsp2Screen::resetSurface()
+{
+ m_gbmSurface = nullptr;
+}
+
+void QEglFSKmsVsp2Screen::initDumbFrameBuffers()
+{
+ for (auto &fb : m_frameBuffers)
+ initDumbFrameBuffer(fb);
+}
+
+void QEglFSKmsVsp2Screen::initVsp2()
+{
+ qCDebug(qLcEglfsKmsDebug, "Initializing Vsp2 hardware");
+ const QSize screenSize = rawGeometry().size();
+ m_blendDevice.reset(new QVsp2BlendingDevice(screenSize));
+
+ // Enable input for main buffer drawn by the compositor (always on)
+ const uint bytesPerLine = uint(screenSize.width()) * 4; //TODO: is this ok?
+ bool formatSet = m_blendDevice->enableInput(m_qtLayer, QRect(QPoint(), screenSize), m_output.drm_format, bytesPerLine);
+ if (!formatSet) {
+ const uint32_t fallbackFormat = DRM_FORMAT_ARGB8888;
+ qWarning() << "Failed to set format" << q_fourccToString(m_output.drm_format)
+ << "falling back to" << q_fourccToString(fallbackFormat);
+ formatSet = m_blendDevice->enableInput(m_qtLayer, QRect(QPoint(), screenSize), fallbackFormat, bytesPerLine);
+ if (!formatSet)
+ qFatal("Failed to set vsp2 blending format");
+ }
+}
+
+int QEglFSKmsVsp2Screen::addLayer(int dmabufFd, const QSize &size, const QPoint &position, uint drmPixelFormat, uint bytesPerLine)
+{
+ int index = m_blendDevice->enableInput(QRect(position, size), drmPixelFormat, bytesPerLine);
+ if (index != -1) {
+ m_blendDevice->setInputBuffer(index, dmabufFd);
+ int id = index; //TODO: maybe make id something independent of layer index?
+ qCDebug(qLcEglfsKmsDebug) << "Enabled extra layer for vsp input" << index;
+ return id;
+ }
+ qWarning() << "Failed to add layer";
+ return -1;
+}
+
+void QEglFSKmsVsp2Screen::setLayerBuffer(int id, int dmabufFd)
+{
+ int layerIndex = id;
+ m_blendDevice->setInputBuffer(layerIndex, dmabufFd);
+ if (!m_blendScheduled) {
+ m_blendScheduled = true;
+ QCoreApplication::postEvent(m_blender.data(), new QEvent(QEvent::User));
+ }
+}
+
+void QEglFSKmsVsp2Screen::setLayerPosition(int id, const QPoint &position)
+{
+ int layerIndex = id;
+ m_blendDevice->setInputPosition(layerIndex, position);
+}
+
+void QEglFSKmsVsp2Screen::setLayerAlpha(int id, qreal alpha)
+{
+ int layerIndex = id;
+ m_blendDevice->setInputAlpha(layerIndex, alpha);
+}
+
+bool QEglFSKmsVsp2Screen::removeLayer(int id)
+{
+ int layerIndex = id;
+ m_blendDevice->disableInput(layerIndex);
+ return true;
+}
+
+void QEglFSKmsVsp2Screen::addBlendListener(void (*callback)())
+{
+ m_blendFinishedCallbacks.append(callback);
+}
+
+void QEglFSKmsVsp2Screen::flip()
+{
+ if (!m_gbmSurface) {
+ qWarning("Cannot sync before platform init!");
+ return;
+ }
+
+ if (!m_blendScheduled && !m_nextGbmBo) {
+ m_nextGbmBo = gbm_surface_lock_front_buffer(m_gbmSurface);
+
+ if (!m_nextGbmBo) {
+ qWarning("Could not lock GBM surface front buffer!");
+ return;
+ }
+
+ m_blendScheduled = true;
+ QCoreApplication::postEvent(m_blender.data(), new QEvent(QEvent::User));
+ }
+}
+
+void QEglFSKmsVsp2Screen::ensureModeSet()
+{
+ const int driFd = device()->fd();
+ QKmsOutput &op(output());
+ if (!op.mode_set) {
+ int ret = drmModeSetCrtc(driFd,
+ op.crtc_id,
+ m_frameBuffers[m_backFb].drmBufferId,
+ 0, 0,
+ &op.connector_id, 1,
+ &op.modes[op.mode]);
+
+ if (ret == -1) {
+ qErrnoWarning(errno, "Could not set DRM mode!");
+ } else {
+ op.mode_set = true;
+ setPowerState(PowerStateOn);
+ }
+ }
+}
+
+void QEglFSKmsVsp2Screen::doDrmFlip()
+{
+ QKmsOutput &op(output());
+ const int driFd = device()->fd();
+
+ int ret = drmModePageFlip(driFd,
+ op.crtc_id,
+ m_frameBuffers[m_backFb].drmBufferId,
+ 0,
+ this);
+
+ if (ret)
+ qErrnoWarning("Could not queue DRM page flip on screen %s", qPrintable(name()));
+
+ m_backFb = (m_backFb + 1) % 2;
+}
+
+void QEglFSKmsVsp2Screen::blendAndFlipDrm()
+{
+ m_blendScheduled = false;
+ if (!m_nextGbmBo && !m_blendDevice->isDirty())
+ return;
+
+ FrameBuffer &backBuffer = m_frameBuffers[m_backFb];
+ if (backBuffer.dmabufFd == -1)
+ initDumbFrameBuffers();
+
+ if (m_nextGbmBo) {
+ Q_ASSERT(m_nextGbmBo != m_currentGbmBo);
+ int compositorBackBufferDmaFd = dmaBufferForGbmBuffer(m_nextGbmBo)->dmabufFd;
+ m_blendDevice->setInputBuffer(m_qtLayer, compositorBackBufferDmaFd);
+
+ if (m_currentGbmBo)
+ gbm_surface_release_buffer(m_gbmSurface, m_currentGbmBo);
+ m_currentGbmBo = m_nextGbmBo;
+ m_nextGbmBo = nullptr;
+ }
+
+ ensureModeSet();
+
+ if (!m_blendDevice)
+ initVsp2();
+
+ if (!m_blendDevice->isDirty())
+ return;
+
+ const int driFd = device()->fd();
+ drmVBlank vBlank;
+ vBlank.request.type = static_cast<drmVBlankSeqType>(DRM_VBLANK_RELATIVE | DRM_VBLANK_SECONDARY); //TODO: make secondary configurable (or automatic)
+ vBlank.request.sequence = 1;
+ vBlank.request.signal = 0;
+ drmWaitVBlank(driFd, &vBlank);
+
+ if (!m_blendDevice->blend(backBuffer.dmabufFd))
+ qWarning() << "Vsp2: blending failed";
+
+ for (auto cb : m_blendFinishedCallbacks)
+ cb();
+
+ doDrmFlip();
+}
+
+void QEglFSKmsVsp2Screen::initDumbFrameBuffer(FrameBuffer &fb)
+{
+ QKmsOutput &op(output());
+ const uint32_t width = op.modes[op.mode].hdisplay;
+ const uint32_t height = op.modes[op.mode].vdisplay;
+
+ Q_ASSERT(fb.dmabufFd == -1);
+ const uint32_t dumbBufferFlags = 0; //TODO: do we want some flags? What's possible?
+ const uint32_t bpp = 32;
+
+ drm_mode_create_dumb creq = {
+ height,
+ width,
+ bpp,
+ dumbBufferFlags,
+ 0, 0, 0 //return values
+ };
+
+ const int driFd = device()->fd();
+ if (drmIoctl(driFd, DRM_IOCTL_MODE_CREATE_DUMB, &creq) == -1)
+ qFatal("Failed to create dumb buffer: %s", strerror(errno));
+
+// uint32_t handles[4] = { gbm_bo_get_handle(bo).u32 };
+// uint32_t strides[4] = { gbm_bo_get_stride(bo) };
+// uint32_t offsets[4] = { 0 };
+// uint32_t pixelFormat = gbmFormatToDrmFormat(gbm_bo_get_format(bo));
+
+ //TODO: support additional planes
+ uint32_t gbmBoHandles[4] = { creq.handle, 0, 0, 0 };
+ uint32_t strides[4] = { creq.pitch, 0, 0, 0 };
+ uint32_t offsets[4] = { 0 };
+ uint32_t pixelFormat = DRM_FORMAT_ARGB8888; //TODO: support other formats?
+ uint32_t drmFlags = 0;
+
+ qCDebug(qLcEglfsKmsDebug) << "Adding FB" << QSize(width, height)
+ << ", DRM format" << q_fourccToString(pixelFormat);
+ int ret = drmModeAddFB2(driFd, width, height, pixelFormat,
+ gbmBoHandles, strides, offsets, &fb.drmBufferId, drmFlags);
+ if (ret)
+ qFatal("drmModeAddFB2 failed: %s", strerror(errno));
+
+ drmPrimeHandleToFD(driFd, gbmBoHandles[0], DRM_CLOEXEC, &fb.dmabufFd);
+}
+
+bool QEglFSKmsVsp2Screen::Blender::event(QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::User:
+ m_screen->blendAndFlipDrm();
+ return true;
+ default:
+ return QObject::event(event);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.h
new file mode 100644
index 0000000000..fa03e36785
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+** Copyright (C) 2017 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 QEGLFSKMSVSP2SCREEN_H
+#define QEGLFSKMSVSP2SCREEN_H
+
+#include "qeglfskmsscreen.h"
+#include "qvsp2blendingdevice.h"
+#include <QtCore/QMutex>
+
+#include <gbm.h>
+
+QT_BEGIN_NAMESPACE
+
+class QEglFSKmsVsp2Screen : public QEglFSKmsScreen
+{
+public:
+ QEglFSKmsVsp2Screen(QKmsDevice *device, const QKmsOutput &output);
+
+ gbm_surface *createSurface();
+ void resetSurface();
+
+ void initDumbFrameBuffers();
+ void initVsp2();
+
+ //TODO: use a fixed index API instead of auto increment?
+ int addLayer(int dmabufFd, const QSize &size, const QPoint &position, uint drmPixelFormat, uint bytesPerLine);
+ void setLayerBuffer(int id, int dmabufFd);
+ void setLayerPosition(int id, const QPoint &position);
+ void setLayerAlpha(int id, qreal alpha);
+ bool removeLayer(int id);
+ void addBlendListener(void (*callback)());
+
+ void flip();
+ void blendAndFlipDrm();
+
+private:
+ class Blender : public QObject // This is a workaround we really would want the screen to be a QObject
+ {
+ public:
+ Blender(QEglFSKmsVsp2Screen *screen) : m_screen(screen) {}
+ ~Blender() override {}
+ bool event(QEvent *event) override;
+ QEglFSKmsVsp2Screen *m_screen = nullptr;
+ };
+ QScopedArrayPointer<Blender> m_blender;
+
+ gbm_surface *m_gbmSurface = nullptr;
+ gbm_bo *m_currentGbmBo = nullptr;
+ gbm_bo *m_nextGbmBo = nullptr;
+
+ QScopedPointer<QVsp2BlendingDevice> m_blendDevice;
+
+ struct FrameBuffer { //these are for buffers that have been blended by the bru
+ uint32_t drmBufferId = 0;
+ int dmabufFd = -1;
+ };
+ std::array<FrameBuffer, 2> m_frameBuffers;
+ uint m_backFb = 0;
+ void initDumbFrameBuffer(FrameBuffer &fb);
+ QVector<void (*)()> m_blendFinishedCallbacks;
+
+ struct DmaBuffer { //these are for qt buffers before blending with additional layers (gbm buffer data)
+ int dmabufFd = -1;
+ };
+ static void dmaBufferDestroyedHandler(gbm_bo *gbmBo, void *data);
+ DmaBuffer *dmaBufferForGbmBuffer(gbm_bo *gbmBo);
+
+ void ensureModeSet();
+ void doDrmFlip();
+
+ bool m_blendScheduled = false;
+ int m_qtLayer = 0; //TODO: add API for changing this
+};
+
+QT_END_NAMESPACE
+
+#endif // QEGLFSKMSVSP2SCREEN_H
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qlinuxmediadevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qlinuxmediadevice.cpp
new file mode 100644
index 0000000000..25b0c39050
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qlinuxmediadevice.cpp
@@ -0,0 +1,627 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** 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 "qeglfsintegration_p.h"
+#include "qlinuxmediadevice.h"
+#include <qeglfskmshelpers.h>
+
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QSize>
+#include <QtCore/QRect>
+
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#include <cstdlib> //this needs to go before mediactl/mediactl.h because it uses size_t without including it
+extern "C" {
+#include <mediactl/mediactl.h>
+#include <mediactl/v4l2subdev.h>
+}
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
+
+static QString mediaBusFmtToString(uint code)
+{
+ switch (code) {
+ case MEDIA_BUS_FMT_FIXED: return "FIXED";
+ case MEDIA_BUS_FMT_RGB444_1X12: return "RGB444_1X12";
+// case MEDIA_BUS_FMT_RGB444_2X8_PADHI_B: return "RGB444_2X8_PADHI_B";
+// case MEDIA_BUS_FMT_RGB444_2X8_PADHI_L: return "RGB444_2X8_PADHI_L";
+// case MEDIA_BUS_FMT_RGB555_2X8_PADHI_B: return "RGB555_2X8_PADHI_B";
+// case MEDIA_BUS_FMT_RGB555_2X8_PADHI_L: return "RGB555_2X8_PADHI_L";
+ case MEDIA_BUS_FMT_RGB565_1X16: return "RGB565_1X16";
+ case MEDIA_BUS_FMT_BGR565_2X8_BE: return "BGR565_2X8_BE";
+ case MEDIA_BUS_FMT_BGR565_2X8_LE: return "BGR565_2X8_LE";
+ case MEDIA_BUS_FMT_RGB565_2X8_BE: return "RGB565_2X8_BE";
+ case MEDIA_BUS_FMT_RGB565_2X8_LE: return "RGB565_2X8_LE";
+ case MEDIA_BUS_FMT_RGB666_1X18: return "RGB666_1X18";
+ case MEDIA_BUS_FMT_RBG888_1X24: return "RBG888_1X24";
+// case MEDIA_BUS_FMT_RGB666_1X24_CPADH: return "RGB666_1X24_CPADH";
+ case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: return "RGB666_1X7X3_SPWG";
+ case MEDIA_BUS_FMT_BGR888_1X24: return "BGR888_1X24";
+ case MEDIA_BUS_FMT_GBR888_1X24: return "GBR888_1X24";
+ case MEDIA_BUS_FMT_RGB888_1X24: return "RGB888_1X24";
+ case MEDIA_BUS_FMT_RGB888_2X12_BE: return "RGB888_2X12_BE";
+ case MEDIA_BUS_FMT_RGB888_2X12_LE: return "RGB888_2X12_LE";
+ case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: return "RGB888_1X7X4_SPWG";
+// case MEDIA_BUS_FMT_RGB888_1X7X4_JEID: return "RGB888_1X7X4_JEID";
+ case MEDIA_BUS_FMT_ARGB8888_1X32: return "ARGB8888_1X32";
+ case MEDIA_BUS_FMT_RGB888_1X32_PADHI: return "RGB888_1X32_PADHI";
+// case MEDIA_BUS_FMT_RGB101010_1X30: return "RGB101010_1X30";
+// case MEDIA_BUS_FMT_RGB121212_1X36: return "RGB121212_1X36";
+// case MEDIA_BUS_FMT_RGB161616_1X48: return "RGB161616_1X48";
+ case MEDIA_BUS_FMT_Y8_1X8: return "Y8_1X8";
+ case MEDIA_BUS_FMT_UV8_1X8: return "UV8_1X8";
+ case MEDIA_BUS_FMT_UYVY8_1_5X8: return "UYVY8_1_5X8";
+ case MEDIA_BUS_FMT_VYUY8_1_5X8: return "VYUY8_1_5X8";
+ case MEDIA_BUS_FMT_YUYV8_1_5X8: return "YUYV8_1_5X8";
+ case MEDIA_BUS_FMT_YVYU8_1_5X8: return "YVYU8_1_5X8";
+ case MEDIA_BUS_FMT_UYVY8_2X8: return "UYVY8_2X8";
+ case MEDIA_BUS_FMT_VYUY8_2X8: return "VYUY8_2X8";
+ case MEDIA_BUS_FMT_YUYV8_2X8: return "YUYV8_2X8";
+ case MEDIA_BUS_FMT_YVYU8_2X8: return "YVYU8_2X8";
+ case MEDIA_BUS_FMT_Y10_1X10: return "Y10_1X10";
+ case MEDIA_BUS_FMT_UYVY10_2X10: return "UYVY10_2X10";
+ case MEDIA_BUS_FMT_VYUY10_2X10: return "VYUY10_2X10";
+ case MEDIA_BUS_FMT_YUYV10_2X10: return "YUYV10_2X10";
+ case MEDIA_BUS_FMT_YVYU10_2X10: return "YVYU10_2X10";
+ case MEDIA_BUS_FMT_Y12_1X12: return "Y12_1X12";
+ case MEDIA_BUS_FMT_UYVY12_2X12: return "UYVY12_2X12";
+ case MEDIA_BUS_FMT_VYUY12_2X12: return "VYUY12_2X12";
+ case MEDIA_BUS_FMT_YUYV12_2X12: return "YUYV12_2X12";
+ case MEDIA_BUS_FMT_YVYU12_2X12: return "YVYU12_2X12";
+ case MEDIA_BUS_FMT_UYVY8_1X16: return "UYVY8_1X16";
+ case MEDIA_BUS_FMT_VYUY8_1X16: return "VYUY8_1X16";
+ case MEDIA_BUS_FMT_YUYV8_1X16: return "YUYV8_1X16";
+ case MEDIA_BUS_FMT_YVYU8_1X16: return "YVYU8_1X16";
+ case MEDIA_BUS_FMT_YDYUYDYV8_1X16: return "YDYUYDYV8_1X16";
+ case MEDIA_BUS_FMT_UYVY10_1X20: return "UYVY10_1X20";
+ case MEDIA_BUS_FMT_VYUY10_1X20: return "VYUY10_1X20";
+ case MEDIA_BUS_FMT_YUYV10_1X20: return "YUYV10_1X20";
+ case MEDIA_BUS_FMT_YVYU10_1X20: return "YVYU10_1X20";
+ case MEDIA_BUS_FMT_VUY8_1X24: return "VUY8_1X24";
+ case MEDIA_BUS_FMT_YUV8_1X24: return "YUV8_1X24";
+// case MEDIA_BUS_FMT_UYYVYY8_0_5X24: return "UYYVYY8_0_5X24";
+ case MEDIA_BUS_FMT_UYVY12_1X24: return "UYVY12_1X24";
+ case MEDIA_BUS_FMT_VYUY12_1X24: return "VYUY12_1X24";
+ case MEDIA_BUS_FMT_YUYV12_1X24: return "YUYV12_1X24";
+ case MEDIA_BUS_FMT_YVYU12_1X24: return "YVYU12_1X24";
+ case MEDIA_BUS_FMT_YUV10_1X30: return "YUV10_1X30";
+// case MEDIA_BUS_FMT_UYYVYY10_0_5X30: return "UYYVYY10_0_5X30";
+ case MEDIA_BUS_FMT_AYUV8_1X32: return "AYUV8_1X32";
+// case MEDIA_BUS_FMT_UYYVYY12_0_5X36: return "UYYVYY12_0_5X36";
+// case MEDIA_BUS_FMT_YUV12_1X36: return "YUV12_1X36";
+// case MEDIA_BUS_FMT_YUV16_1X48: return "YUV16_1X48";
+// case MEDIA_BUS_FMT_UYYVYY16_0_5X48: return "UYYVYY16_0_5X48";
+ case MEDIA_BUS_FMT_SBGGR8_1X8: return "SBGGR8_1X8";
+ case MEDIA_BUS_FMT_SGBRG8_1X8: return "SGBRG8_1X8";
+ case MEDIA_BUS_FMT_SGRBG8_1X8: return "SGRBG8_1X8";
+ case MEDIA_BUS_FMT_SRGGB8_1X8: return "SRGGB8_1X8";
+ case MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8: return "SBGGR10_ALAW8_1X8";
+ case MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8: return "SGBRG10_ALAW8_1X8";
+ case MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8: return "SGRBG10_ALAW8_1X8";
+ case MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8: return "SRGGB10_ALAW8_1X8";
+ case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8: return "SBGGR10_DPCM8_1X8";
+ case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8: return "SGBRG10_DPCM8_1X8";
+ case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8: return "SGRBG10_DPCM8_1X8";
+ case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8: return "SRGGB10_DPCM8_1X8";
+// case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_B: return "SBGGR10_2X8_PADHI_B";
+// case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_L: return "SBGGR10_2X8_PADHI_L";
+// case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_B: return "SBGGR10_2X8_PADLO_B";
+// case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_L: return "SBGGR10_2X8_PADLO_L";
+ case MEDIA_BUS_FMT_SBGGR10_1X10: return "SBGGR10_1X10";
+ case MEDIA_BUS_FMT_SGBRG10_1X10: return "SGBRG10_1X10";
+ case MEDIA_BUS_FMT_SGRBG10_1X10: return "SGRBG10_1X10";
+ case MEDIA_BUS_FMT_SRGGB10_1X10: return "SRGGB10_1X10";
+ case MEDIA_BUS_FMT_SBGGR12_1X12: return "SBGGR12_1X12";
+ case MEDIA_BUS_FMT_SGBRG12_1X12: return "SGBRG12_1X12";
+ case MEDIA_BUS_FMT_SGRBG12_1X12: return "SGRBG12_1X12";
+ case MEDIA_BUS_FMT_SRGGB12_1X12: return "SRGGB12_1X12";
+ case MEDIA_BUS_FMT_SBGGR14_1X14: return "SBGGR14_1X14";
+ case MEDIA_BUS_FMT_SGBRG14_1X14: return "SGBRG14_1X14";
+ case MEDIA_BUS_FMT_SGRBG14_1X14: return "SGRBG14_1X14";
+ case MEDIA_BUS_FMT_SRGGB14_1X14: return "SRGGB14_1X14";
+ case MEDIA_BUS_FMT_SBGGR16_1X16: return "SBGGR16_1X16";
+ case MEDIA_BUS_FMT_SGBRG16_1X16: return "SGBRG16_1X16";
+ case MEDIA_BUS_FMT_SGRBG16_1X16: return "SGRBG16_1X16";
+ case MEDIA_BUS_FMT_SRGGB16_1X16: return "SRGGB16_1X16";
+ case MEDIA_BUS_FMT_JPEG_1X8: return "JPEG_1X8";
+ case MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8: return "S5C_UYVY_JPEG_1X8";
+ case MEDIA_BUS_FMT_AHSV8888_1X32: return "AHSV8888_1X32";
+ default: return QString(code);
+ }
+}
+
+static QDebug operator<<(QDebug debug, const struct v4l2_mbus_framefmt &format)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace() << "v4l2_mbus_framefmt("
+ << "code: " << mediaBusFmtToString(format.code) << ", "
+ << "size: " << format.width << "x" << format.height << ")";
+ return debug;
+}
+
+static QDebug operator<<(QDebug debug, const struct v4l2_pix_format_mplane &format)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace() << "v4l2_pix_format_mplane("
+ << "pixel format: " << q_fourccToString(format.pixelformat) << ", "
+ << "size: " << format.width << "x" << format.height << ", "
+ << "planes: " << format.num_planes << ")";
+ return debug;
+}
+
+QLinuxMediaDevice::QLinuxMediaDevice(const QString &devicePath)
+ : m_mediaDevice(media_device_new(devicePath.toStdString().c_str()))
+{
+ if (!m_mediaDevice)
+ qFatal("Couldn't get media device");
+
+ if (media_device_enumerate(m_mediaDevice))
+ qFatal("Couldn't enumerate media device");
+
+ m_info = media_get_info(m_mediaDevice);
+
+ qCDebug(qLcEglfsKmsDebug) << "Opened linux media device:"
+ << "\n\t Path:" << devicePath
+ << "\n\t Model:" << model()
+ << "\n\t Device name:" << deviceName();
+
+ resetLinks();
+}
+
+QLinuxMediaDevice::~QLinuxMediaDevice()
+{
+ if (m_mediaDevice)
+ media_device_unref(m_mediaDevice);
+}
+
+QString QLinuxMediaDevice::model()
+{
+ return QString(m_info->model);
+}
+
+QString QLinuxMediaDevice::deviceName()
+{
+ return QString(m_info->bus_info).split(":").last();
+}
+
+bool QLinuxMediaDevice::resetLinks()
+{
+ if (media_reset_links(m_mediaDevice)) {
+ qWarning() << "Could not reset media controller links.";
+ return false;
+ }
+ qCDebug(qLcEglfsKmsDebug) << "Reset media links";
+ return true;
+}
+
+struct media_link *QLinuxMediaDevice::parseLink(const QString &link)
+{
+ char *endp = nullptr;;
+ struct media_link *mediaLink = media_parse_link(m_mediaDevice, link.toStdString().c_str(), &endp);
+
+ if (!mediaLink)
+ qWarning() << "Failed to parse media link:" << link;
+
+ return mediaLink;
+}
+
+struct media_pad *QLinuxMediaDevice::parsePad(const QString &pad)
+{
+ struct media_pad *mediaPad = media_parse_pad(m_mediaDevice, pad.toStdString().c_str(), nullptr);
+
+ if (!mediaPad)
+ qWarning() << "Failed to parse media pad:" << pad;
+
+ return mediaPad;
+}
+
+bool QLinuxMediaDevice::enableLink(struct media_link *link)
+{
+ if (media_setup_link(m_mediaDevice, link->source, link->sink, 1)) {
+ qWarning() << "Failed to enable media link.";
+ return false;
+ }
+ return true;
+}
+
+bool QLinuxMediaDevice::disableLink(struct media_link *link)
+{
+ if (media_setup_link(m_mediaDevice, link->source, link->sink, 0)) {
+ qWarning() << "Failed to disable media link.";
+ return false;
+ }
+ return true;
+}
+
+media_entity *QLinuxMediaDevice::getEntity(const QString &name)
+{
+ struct media_entity *entity = media_get_entity_by_name(m_mediaDevice, name.toStdString().c_str(), name.length());
+
+ if (!entity)
+ qWarning() << "Failed to get media entity:" << name;
+
+ return entity;
+}
+
+int QLinuxMediaDevice::openVideoDevice(const QString &name)
+{
+ struct media_entity *entity = getEntity(name);
+ const char *deviceName = media_entity_get_devname(entity);
+ int fd = open(deviceName, O_RDWR);
+ qCDebug(qLcEglfsKmsDebug) << "Opened video device:" << deviceName << "with fd" << fd;
+ return fd;
+}
+
+QLinuxMediaDevice::CaptureSubDevice::CaptureSubDevice(QLinuxMediaDevice *mediaDevice, const QString &name)
+ : m_subdevFd(mediaDevice->openVideoDevice(name))
+{
+}
+
+bool QLinuxMediaDevice::CaptureSubDevice::setFormat(const QSize &size, uint32_t pixelFormat)
+{
+ Q_ASSERT(size.isValid());
+ struct v4l2_format format;
+ memset(&format, 0, sizeof(struct v4l2_format));
+ format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ if (ioctl(m_subdevFd, VIDIOC_G_FMT, &format) == -1) {
+ qErrnoWarning("VIDIOC_G_FMT for capture device failed");
+ return false;
+ }
+
+ format.fmt.pix_mp.width = static_cast<uint>(size.width());
+ format.fmt.pix_mp.height = static_cast<uint>(size.height());
+ format.fmt.pix_mp.field = V4L2_FIELD_NONE;
+ format.fmt.pix_mp.pixelformat = pixelFormat;
+ format.fmt.pix_mp.num_planes = 1;
+ format.fmt.pix_mp.flags = 0;
+ format.fmt.pix_mp.plane_fmt[0].bytesperline = 0;
+ format.fmt.pix_mp.plane_fmt[0].sizeimage = 0;
+
+ if (ioctl(m_subdevFd, VIDIOC_S_FMT, &format) == -1) {
+ qWarning() << "Capture device" << m_subdevFd << "VIDIOC_S_FMT with format" << format.fmt.pix_mp
+ << "failed:" << strerror(errno);
+ return false;
+ }
+
+ qCDebug(qLcEglfsKmsDebug) << "Capture device" << m_subdevFd << "format set:" << format.fmt.pix_mp;
+ return true;
+}
+
+bool QLinuxMediaDevice::CaptureSubDevice::clearBuffers()
+{
+ struct v4l2_requestbuffers requestBuffers;
+ memset(&requestBuffers, 0, sizeof(requestBuffers));
+ requestBuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ requestBuffers.memory = V4L2_MEMORY_DMABUF;
+ requestBuffers.count = 0;
+ if (ioctl(m_subdevFd, VIDIOC_REQBUFS, &requestBuffers) == -1) {
+ qWarning("Capture device %d: VIDIOC_REQBUFS clear failed: %s", m_subdevFd, strerror(errno));
+ return false;
+ }
+ qCDebug(qLcEglfsKmsDebug, "Capture device %d: Deallocced buffers with REQBUF, now has %d buffers", m_subdevFd, requestBuffers.count);
+ Q_ASSERT(requestBuffers.count == 0);
+ return true;
+}
+
+bool QLinuxMediaDevice::CaptureSubDevice::requestBuffer()
+{
+ struct v4l2_requestbuffers requestBuffers;
+ memset(&requestBuffers, 0, sizeof(requestBuffers));
+ requestBuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ requestBuffers.memory = V4L2_MEMORY_DMABUF;
+ requestBuffers.count = 1;
+ if (ioctl(m_subdevFd, VIDIOC_REQBUFS, &requestBuffers) == -1) {
+ if (errno == EINVAL)
+ qWarning("Capture device %d: Multi-planar capture or dma buffers not supported", m_subdevFd);
+ qWarning("Capture device %d: VIDIOC_REQBUFS failed: %s", m_subdevFd, strerror(errno));
+ return false;
+ }
+ Q_ASSERT(requestBuffers.count == 1);
+ return true;
+}
+
+bool QLinuxMediaDevice::CaptureSubDevice::queueBuffer(int dmabufFd, const QSize &bufferSize)
+{
+ const uint numPlanes = 1;
+ struct v4l2_buffer buffer;
+ memset(&buffer, 0, sizeof(buffer));
+ buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ buffer.memory = V4L2_MEMORY_DMABUF;
+ buffer.index = 0;
+
+ struct v4l2_plane planes[VIDEO_MAX_PLANES];
+ buffer.m.planes = planes;
+ buffer.length = numPlanes;
+ memset(planes, 0, sizeof(planes));
+ for (uint i = 0; i < numPlanes; i++) {
+ buffer.m.planes[i].m.fd = dmabufFd;
+ buffer.m.planes[i].length = static_cast<uint>(bufferSize.width() * bufferSize.height() * 4); //TODO: don't harcode bpp
+ buffer.m.planes[i].bytesused = static_cast<uint>(bufferSize.width() * bufferSize.height() * 4); //TODO: don't harcode bpp
+ }
+
+ if (ioctl(m_subdevFd, VIDIOC_QBUF, &buffer) == -1) {
+ qWarning("Capture device %d: VIDIOC_QBUF failed for dma buffer with fd %d: %s",
+ m_subdevFd, dmabufFd, strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+bool QLinuxMediaDevice::CaptureSubDevice::dequeueBuffer()
+{
+ const int numPlanes = 1;
+ struct v4l2_buffer buffer;
+ struct v4l2_plane planes[VIDEO_MAX_PLANES];
+
+ memset(&buffer, 0, sizeof(buffer));
+ buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ buffer.memory = V4L2_MEMORY_DMABUF;
+ buffer.index = 0;
+ buffer.m.planes = planes;
+ buffer.length = numPlanes;
+ memset(planes, 0, sizeof(planes));
+
+ if (ioctl(m_subdevFd, VIDIOC_DQBUF, &buffer) == -1) {
+ qWarning("Capture device %d: VIDIOC_DQBUF failed: %s", m_subdevFd, strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+bool QLinuxMediaDevice::CaptureSubDevice::streamOn()
+{
+ return QLinuxMediaDevice::streamOn(m_subdevFd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+}
+
+bool QLinuxMediaDevice::CaptureSubDevice::streamOff()
+{
+ return QLinuxMediaDevice::streamOff(m_subdevFd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+}
+
+QLinuxMediaDevice::OutputSubDevice::OutputSubDevice(QLinuxMediaDevice *mediaDevice, const QString &name)
+ : m_subdevFd(mediaDevice->openVideoDevice(name))
+{
+}
+
+bool QLinuxMediaDevice::OutputSubDevice::setFormat(const QSize &size, uint32_t pixelFormat, uint32_t bytesPerLine)
+{
+ Q_ASSERT(size.isValid());
+ struct v4l2_format format;
+ memset(&format, 0, sizeof(struct v4l2_format));
+ format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ if (ioctl(m_subdevFd, VIDIOC_G_FMT, &format) == -1) {
+ qErrnoWarning("VIDIOC_G_FMT for output device failed");
+ return false;
+ }
+
+ format.fmt.pix_mp.width = static_cast<uint>(size.width());
+ format.fmt.pix_mp.height = static_cast<uint>(size.height());
+ format.fmt.pix_mp.field = V4L2_FIELD_NONE;
+ format.fmt.pix_mp.pixelformat = pixelFormat;
+ format.fmt.pix_mp.num_planes = 1;
+ format.fmt.pix_mp.flags = 0;
+ format.fmt.pix_mp.plane_fmt[0].bytesperline = bytesPerLine;
+ format.fmt.pix_mp.plane_fmt[0].sizeimage = 0;
+
+ if (ioctl(m_subdevFd, VIDIOC_S_FMT, &format) == -1) {
+ qWarning() << "Output device" << m_subdevFd << "VIDIOC_S_FMT with format" << format.fmt.pix_mp
+ << "failed:" << strerror(errno);
+ return false;
+ }
+
+ qCDebug(qLcEglfsKmsDebug) << "Output device device" << m_subdevFd << "format set:" << format.fmt.pix_mp;
+ return true;
+}
+
+bool QLinuxMediaDevice::OutputSubDevice::clearBuffers()
+{
+ struct v4l2_requestbuffers requestBuffers;
+ memset(&requestBuffers, 0, sizeof(requestBuffers));
+ requestBuffers.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ requestBuffers.memory = V4L2_MEMORY_DMABUF;
+ requestBuffers.count = 0;
+ if (ioctl(m_subdevFd, VIDIOC_REQBUFS, &requestBuffers) == -1) {
+ qWarning("Output device %d: VIDIOC_REQBUFS clear failed: %s", m_subdevFd, strerror(errno));
+ return false;
+ }
+ qCDebug(qLcEglfsKmsDebug, "Output device %d: Deallocced buffers with REQBUF, now has %d buffers", m_subdevFd, requestBuffers.count);
+ Q_ASSERT(requestBuffers.count == 0);
+ return true;
+}
+
+bool QLinuxMediaDevice::OutputSubDevice::requestBuffer()
+{
+ struct v4l2_requestbuffers requestBuffers;
+ memset(&requestBuffers, 0, sizeof(requestBuffers));
+ requestBuffers.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ requestBuffers.memory = V4L2_MEMORY_DMABUF;
+ requestBuffers.count = 1;
+ if (ioctl(m_subdevFd, VIDIOC_REQBUFS, &requestBuffers) == -1) {
+ if (errno == EINVAL)
+ qWarning("Output device %d: Multi-planar output or dma buffers not supported", m_subdevFd);
+ qWarning("Output device %d: VIDIOC_REQBUFS failed: %s", m_subdevFd, strerror(errno));
+ return false;
+ }
+ qCDebug(qLcEglfsKmsDebug) << "REQBUF returned" << requestBuffers.count << "buffers for output device" << m_subdevFd;
+ return true;
+}
+
+bool QLinuxMediaDevice::OutputSubDevice::queueBuffer(int dmabufFd, uint bytesUsed, uint length)
+{
+ const int numPlanes = 1;
+ struct v4l2_buffer buffer;
+ memset(&buffer, 0, sizeof(buffer));
+ buffer.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ buffer.memory = V4L2_MEMORY_DMABUF;
+ buffer.index = 0;
+ buffer.length = numPlanes;
+ buffer.bytesused = bytesUsed;
+ buffer.field = V4L2_FIELD_NONE; //TODO: what is this?
+
+ struct v4l2_plane planes[numPlanes];
+ memset(planes, 0, sizeof(planes));
+ buffer.m.planes = planes;
+
+ for (int i = 0; i < numPlanes; i++) {
+ buffer.m.planes[i].m.fd = dmabufFd;
+ buffer.m.planes[i].length = length;
+ buffer.m.planes[i].bytesused = bytesUsed;
+ }
+
+ if (ioctl(m_subdevFd, VIDIOC_QBUF, &buffer) == -1) {
+ qWarning("Output device %d: VIDIOC_QBUF failed for dmabuf %d: %s", m_subdevFd, dmabufFd, strerror(errno));
+ return false;
+ }
+
+ if (!(buffer.flags & V4L2_BUF_FLAG_QUEUED)) {
+ qWarning() << "Queued flag not set on buffer for output device";
+ return false;
+ }
+
+ return true;
+}
+
+bool QLinuxMediaDevice::OutputSubDevice::streamOn()
+{
+ return QLinuxMediaDevice::streamOn(m_subdevFd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+}
+
+bool QLinuxMediaDevice::OutputSubDevice::streamOff()
+{
+ return QLinuxMediaDevice::streamOff(m_subdevFd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+}
+
+int QLinuxMediaDevice::openVideoDevice(media_pad *pad)
+{
+ const char *deviceName = media_entity_get_devname(pad->entity);
+ int fd = open(deviceName, O_RDWR);
+ qCDebug(qLcEglfsKmsDebug) << "Opened video device:" << deviceName << "with fd" << fd;
+ return fd;
+}
+
+bool QLinuxMediaDevice::setSubdevFormat(struct media_pad *pad, const QSize &size, uint32_t mbusFormat)
+{
+ Q_ASSERT(size.isValid());
+ struct v4l2_mbus_framefmt format;
+ format.width = static_cast<uint>(size.width());
+ format.height = static_cast<uint>(size.height());
+ format.code = mbusFormat;
+
+ if (v4l2_subdev_set_format(pad->entity, &format, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE)) {
+ qWarning() << "Setting v4l2_subdev_set_format failed for format" << format;
+ return false;
+ }
+
+ if (format.code != mbusFormat) {
+ qWarning() << "Got" << mediaBusFmtToString(format.code) << "instead of"
+ << mediaBusFmtToString(mbusFormat) << "when setting subdevice format";
+ return false;
+ }
+
+ qCDebug(qLcEglfsKmsDebug) << "Set format to" << format << "for entity" << pad->entity << "index" << pad->index;
+ return true;
+}
+
+bool QLinuxMediaDevice::setSubdevAlpha(int subdevFd, qreal alpha)
+{
+ struct v4l2_control control;
+ control.id = V4L2_CID_ALPHA_COMPONENT;
+ control.value = static_cast<__s32>(alpha * 0xff);
+ if (ioctl(subdevFd, VIDIOC_S_CTRL, &control) == -1) {
+ qErrnoWarning("Setting alpha (%d) failed", control.value);
+ return false;
+ }
+ return true;
+}
+
+bool QLinuxMediaDevice::setSubdevSelection(struct media_pad *pad, const QRect &geometry, uint target)
+{
+ Q_ASSERT(geometry.isValid());
+ struct v4l2_rect rect;
+ rect.left = geometry.left();
+ rect.top = geometry.top();
+ rect.width = static_cast<uint>(geometry.width());
+ rect.height = static_cast<uint>(geometry.height());
+
+ int ret = v4l2_subdev_set_selection(pad->entity, &rect, pad->index, target, V4L2_SUBDEV_FORMAT_ACTIVE);
+ if (ret) {
+ qWarning() << "Setting subdev selection failed.";
+ return false;
+ }
+
+ return true;
+}
+
+bool QLinuxMediaDevice::setSubdevCrop(struct media_pad *pad, const QRect &geometry)
+{
+ return setSubdevSelection(pad, geometry, V4L2_SEL_TGT_CROP);
+}
+
+bool QLinuxMediaDevice::setSubdevCompose(struct media_pad *pad, const QRect &geometry)
+{
+ return setSubdevSelection(pad, geometry, V4L2_SEL_TGT_COMPOSE);
+}
+
+bool QLinuxMediaDevice::streamOn(int subDeviceFd, v4l2_buf_type bufferType)
+{
+ if (ioctl(subDeviceFd, VIDIOC_STREAMON, &bufferType) == -1) {
+ qWarning("VIDIOC_STREAMON failed for subdevice %d: %s", subDeviceFd, strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+bool QLinuxMediaDevice::streamOff(int subDeviceFd, v4l2_buf_type bufferType)
+{
+ if (ioctl(subDeviceFd, VIDIOC_STREAMOFF, &bufferType) == -1) {
+ qWarning("VIDIOC_STREAMOFF failed for subdevice %d: %s", subDeviceFd, strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qlinuxmediadevice.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qlinuxmediadevice.h
new file mode 100644
index 0000000000..26f863214b
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qlinuxmediadevice.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** 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 QLINUXMEDIADEVICE_H
+#define QLINUXMEDIADEVICE_H
+
+#include <linux/v4l2-mediabus.h>
+
+#include <QtCore/qglobal.h>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+class QSize;
+class QRect;
+
+class QLinuxMediaDevice
+{
+public:
+ QLinuxMediaDevice(const QString &devicePath);
+ ~QLinuxMediaDevice();
+
+ QString model();
+ QString deviceName();
+ bool resetLinks();
+ struct media_link *parseLink(const QString &link);
+ struct media_pad *parsePad(const QString &pad);
+ bool enableLink(struct media_link *link);
+ bool disableLink(struct media_link *link);
+ struct media_entity *getEntity(const QString &name);
+ int openVideoDevice(const QString &name);
+
+ class CaptureSubDevice
+ {
+ public:
+ CaptureSubDevice(QLinuxMediaDevice *mediaDevice, const QString &name);
+ bool setFormat(const QSize &size, uint32_t pixelFormat = V4L2_PIX_FMT_ABGR32); //TODO: fix to match output device
+ bool clearBuffers();
+ bool requestBuffer();
+ bool queueBuffer(int dmabufFd, const QSize &bufferSize);
+ bool dequeueBuffer();
+ bool streamOn();
+ bool streamOff();
+ private:
+ int m_subdevFd = -1;
+ };
+
+ class OutputSubDevice
+ {
+ public:
+ OutputSubDevice(QLinuxMediaDevice *mediaDevice, const QString &name);
+ bool setFormat(const QSize &size, uint32_t pixelFormat, uint32_t bytesPerLine);
+ bool clearBuffers();
+ bool requestBuffer();
+ bool queueBuffer(int dmabufFd, uint bytesUsed, uint length);
+ bool streamOn();
+ bool streamOff();
+ private:
+ int m_subdevFd = -1;
+ };
+
+ static int openVideoDevice(media_pad *pad);
+
+ static bool setSubdevFormat(struct media_pad *pad, const QSize &size, uint32_t mbusFormat = MEDIA_BUS_FMT_ARGB8888_1X32);
+ static bool setSubdevAlpha(int subdevFd, qreal alpha);
+
+ static bool setSubdevSelection(struct media_pad *pad, const QRect &geometry, uint target);
+ static bool setSubdevCrop(struct media_pad *pad, const QRect &geometry);
+ static bool setSubdevCompose(struct media_pad *pad, const QRect &geometry);
+
+private:
+ static bool streamOn(int subDeviceFd, v4l2_buf_type bufferType);
+ static bool streamOff(int subDeviceFd, v4l2_buf_type bufferType);
+ struct media_device *m_mediaDevice = nullptr;
+ const struct media_device_info *m_info = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QLINUXMEDIADEVICE_H
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qvsp2blendingdevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qvsp2blendingdevice.cpp
new file mode 100644
index 0000000000..879d312341
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qvsp2blendingdevice.cpp
@@ -0,0 +1,333 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** 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 "qvsp2blendingdevice.h"
+#include <qeglfskmshelpers.h>
+
+#include <QDebug>
+#include <QtCore/QLoggingCategory>
+
+#include <drm_fourcc.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
+
+//TODO: is this the right place for these conversion functions?
+static inline uint drmFormatToV4l2PixelFormat(uint drmFormat) {
+ //ARGB8888 == ABGR32 because linux media list stuff in the opposite order, but the fourcc is the same
+ Q_ASSERT(DRM_FORMAT_ARGB8888 == V4L2_PIX_FMT_ABGR32);
+ return drmFormat;
+}
+
+static uint drmFormatToMediaBusFormat(uint drmFormat)
+{
+ switch (drmFormat) {
+ case DRM_FORMAT_RGB888:
+ return MEDIA_BUS_FMT_RGB888_1X24;
+ case DRM_FORMAT_BGR888:
+ return MEDIA_BUS_FMT_BGR888_1X24;
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_XBGR8888:
+// return MEDIA_BUS_FMT_RGB888_1X32_PADHI; // doesn't work on renesas m3, just use fallthrough to argb for now
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_BGRX8888:
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_BGRA8888:
+ return MEDIA_BUS_FMT_ARGB8888_1X32;
+ default:
+ qWarning() << "Unknown drm format" << q_fourccToString(drmFormat) << "defaulting to argb8888";
+ return MEDIA_BUS_FMT_ARGB8888_1X32;
+ }
+}
+
+QVsp2BlendingDevice::QVsp2BlendingDevice(const QSize &screenSize)
+ : m_mediaDevice("/dev/media0")
+ , m_screenSize(screenSize)
+{
+ QLinuxMediaDevice &md = m_mediaDevice;
+ QString deviceName = md.deviceName();
+
+ if (md.model() != QString("VSP2"))
+ qWarning() << "Unsupported media device model:" << md.model();
+
+ if (deviceName != "fe960000.vsp")
+ qWarning() << "Unknown media device name:" << deviceName;
+
+ const int numInputs = 5;
+
+ for (int i = 0; i < numInputs; ++i) {
+ Input input;
+ input.linkToBru = md.parseLink(QString("'%1 rpf.%2':1 -> '%1 bru':%2").arg(deviceName).arg(i));
+ input.inputFormatPad = md.parsePad(QString("'%1 rpf.%2':0").arg(deviceName).arg(i));
+ input.outputFormatPad = md.parsePad(QString("'%1 rpf.%2':1").arg(deviceName).arg(i));
+ input.outputFormatFd = QLinuxMediaDevice::openVideoDevice(input.outputFormatPad);
+ input.bruInputFormatPad = md.parsePad(QString("'%1 bru':%2").arg(deviceName).arg(i));
+ input.rpfInput = new QLinuxMediaDevice::OutputSubDevice(&md, QString("%1 rpf.%2 input").arg(deviceName).arg(i));
+ m_inputs.append(input);
+ }
+
+ m_wpfOutput = new QLinuxMediaDevice::CaptureSubDevice(&md, QString("%1 wpf.0 output").arg(deviceName));
+
+ // Setup links for output
+ md.enableLink(md.parseLink(QString("'%1 bru':5 -> '%1 wpf.0':0").arg(deviceName)));
+ md.enableLink(md.parseLink(QString("'%1 wpf.0':1 -> '%1 wpf.0 output':0").arg(deviceName)));
+
+ // Output pads
+ auto bruOutputFormatPad = md.parsePad(QString("'%1 bru':5").arg(deviceName));
+ auto wpfInputFormatPad = md.parsePad(QString("'%1 wpf.0':0").arg(deviceName));
+ auto wpfOutputFormatPad = md.parsePad(QString("'%1 wpf.0':1").arg(deviceName));
+
+ m_wpfOutput->setFormat(screenSize);
+ QLinuxMediaDevice::setSubdevFormat(bruOutputFormatPad, screenSize);
+ QLinuxMediaDevice::setSubdevFormat(wpfInputFormatPad, screenSize);
+ QLinuxMediaDevice::setSubdevFormat(wpfOutputFormatPad, screenSize);
+
+ m_wpfOutput->requestBuffer();
+}
+
+bool QVsp2BlendingDevice::enableInput(int i, const QRect &bufferGeometry, uint drmFormat, uint bytesPerLine)
+{
+ qCDebug(qLcEglfsKmsDebug) << "Blend unit: Enabling input" << i;
+ if (m_inputs[i].enabled) {
+ qWarning("Vsp2: Input %d already enabled", i);
+ return false;
+ }
+
+ if (!bufferGeometry.isValid()) { //TODO: bounds checking as well?
+ qWarning() << "Vsp2: Invalid buffer geometry";
+ return false;
+ }
+
+ Input &input = m_inputs[i];
+ if (!m_mediaDevice.enableLink(input.linkToBru))
+ return false;
+
+ uint pixelFormat = drmFormatToV4l2PixelFormat(drmFormat);
+ if (!setInputFormat(i, bufferGeometry, pixelFormat, bytesPerLine)) {
+ disableInput(i);
+ return false;
+ }
+
+ input.rpfInput->requestBuffer();
+ return true;
+}
+
+int QVsp2BlendingDevice::enableInput(const QRect &bufferGeometry, uint drmFormat, uint bytesPerLine)
+{
+ for (int i = 0; i < m_inputs.size(); ++i) {
+ if (!m_inputs[i].enabled)
+ return enableInput(i, bufferGeometry, drmFormat, bytesPerLine) ? i : -1;
+ }
+ qWarning() << "Vsp2: No more inputs available in blend unit";
+ return -1;
+}
+
+bool QVsp2BlendingDevice::disableInput(int i)
+{
+ qCDebug(qLcEglfsKmsDebug) << "Vsp2: disabling input" << i;
+ if (!m_inputs[i].enabled) {
+ qWarning("Vsp2: Input %d already disabled", i);
+ return false;
+ }
+ m_mediaDevice.disableLink(m_inputs[i].linkToBru);
+ m_inputs[i].rpfInput->clearBuffers();
+ m_inputs[i].enabled = false;
+ return true;
+}
+
+bool QVsp2BlendingDevice::setInputBuffer(int index, int dmabufFd)
+{
+ Input &input = m_inputs[index];
+
+ if (!input.enabled) {
+ qWarning() << "Vsp2: Can't queue on disabled input" << index;
+ return false;
+ }
+
+ // Don't queue the buffer yet, store it here and wait until blending
+ if (input.dmabuf.fd != dmabufFd) {
+ m_dirty = true;
+ input.dmabuf.fd = dmabufFd;
+ }
+ return true;
+}
+
+bool QVsp2BlendingDevice::setInputPosition(int index, const QPoint &position)
+{
+ Input &input = m_inputs[index];
+
+ if (input.geometry.topLeft() == position)
+ return true;
+
+ m_dirty = true;
+ input.geometry.moveTopLeft(position);
+ return QLinuxMediaDevice::setSubdevCompose(input.bruInputFormatPad, input.geometry);
+}
+
+bool QVsp2BlendingDevice::setInputAlpha(int index, qreal alpha)
+{
+ Input &input = m_inputs[index];
+ if (input.alpha == alpha)
+ return true;
+
+ m_dirty = true;
+ input.alpha = alpha;
+ return QLinuxMediaDevice::setSubdevAlpha(input.outputFormatFd, alpha);
+}
+
+bool QVsp2BlendingDevice::blend(int outputDmabufFd)
+{
+ if (!m_dirty)
+ qWarning("Blending without being dirty, should not be necessary");
+
+ if (!m_inputs[0].enabled) {
+ qWarning("Vsp2: Can't blend with layer 0 disabled");
+ return false;
+ }
+
+ // Queue dma input buffers
+ for (int i=0; i < m_inputs.size(); ++i) {
+ auto &input = m_inputs[i];
+ if (input.enabled) {
+ if (!input.rpfInput->queueBuffer(input.dmabuf.fd, input.dmabuf.bytesUsed, input.dmabuf.length)) {
+ qWarning() << "Vsp2: Failed to queue buffer for input" << i
+ << "with dmabuf" << input.dmabuf.fd
+ << "and size" << input.geometry.size();
+
+ if (!disableInput(i))
+ qWarning() << "Vsp2: Failed to disable input" << i;
+ }
+ }
+ }
+
+ if (!m_wpfOutput->queueBuffer(outputDmabufFd, m_screenSize)) {
+ qWarning() << "Vsp2: Failed to queue blending output buffer" << outputDmabufFd << m_screenSize;
+ return false;
+ }
+
+ if (!streamOn()) {
+ qWarning() << "Vsp2: Failed to start streaming";
+ return false;
+ }
+
+ if (!m_wpfOutput->dequeueBuffer()) {
+ qWarning() << "Vsp2: Failed to dequeue blending output buffer";
+ if (!streamOff())
+ qWarning() << "Vsp2: Failed to stop streaming when recovering after a broken blend.";
+ return false;
+ }
+
+ if (!streamOff()) {
+ qWarning() << "Vsp2: Failed to stop streaming";
+ return false;
+ }
+
+ m_dirty = false;
+ return true;
+}
+
+int QVsp2BlendingDevice::numInputs() const
+{
+ return m_inputs.size();
+}
+
+bool QVsp2BlendingDevice::streamOn()
+{
+ for (auto &input : m_inputs) {
+ if (input.enabled) {
+ if (!input.rpfInput->streamOn()) {
+ //TODO: perhaps it's better to try to continue with the other inputs?
+ return false;
+ }
+ }
+ }
+
+ return m_wpfOutput->streamOn();
+}
+
+bool QVsp2BlendingDevice::streamOff()
+{
+ bool succeeded = m_wpfOutput->streamOff();
+ for (auto &input : m_inputs) {
+ if (input.enabled)
+ succeeded &= input.rpfInput->streamOff();
+ }
+ return succeeded;
+}
+
+bool QVsp2BlendingDevice::setInputFormat(int i, const QRect &bufferGeometry, uint pixelFormat, uint bytesPerLine)
+{
+ Input &input = m_inputs[i];
+
+ Q_ASSERT(bufferGeometry.isValid());
+
+ const uint bpp = 4; //TODO: don't hardcode bpp, get it from pixelFormat?
+ input.enabled = true;
+ input.geometry = bufferGeometry;
+ input.dmabuf.bytesUsed = bpp * static_cast<uint>(bufferGeometry.width()) * static_cast<uint>(bufferGeometry.height());
+ input.dmabuf.length = static_cast<uint>(bufferGeometry.height()) * bytesPerLine;
+
+ const QSize size = bufferGeometry.size();
+
+ if (!input.rpfInput->setFormat(size, pixelFormat, bytesPerLine)) // rpf.x input
+ return false;
+
+ const uint mediaBusFormat = drmFormatToMediaBusFormat(pixelFormat);
+ if (!QLinuxMediaDevice::setSubdevFormat(input.inputFormatPad, size, mediaBusFormat)) // rpf.x:0
+ return false;
+
+ if (!QLinuxMediaDevice::setSubdevFormat(input.outputFormatPad, size, mediaBusFormat)) // rpf.x:1
+ return false;
+
+ if (!QLinuxMediaDevice::setSubdevFormat(input.bruInputFormatPad, size, mediaBusFormat)) // bru:x
+ return false;
+
+ if (!QLinuxMediaDevice::setSubdevCrop(input.inputFormatPad, QRect(QPoint(0, 0), size)))
+ return false;
+
+ if (!QLinuxMediaDevice::setSubdevCompose(input.bruInputFormatPad, bufferGeometry))
+ return false;
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qvsp2blendingdevice.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qvsp2blendingdevice.h
new file mode 100644
index 0000000000..be48954f47
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qvsp2blendingdevice.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** 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 QVSP2BLENDINGDEVICE_H
+#define QVSP2BLENDINGDEVICE_H
+
+#include <QtCore/QRect>
+#include <QtCore/QVector>
+#include <QtCore/qglobal.h>
+
+#include "qlinuxmediadevice.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSize;
+
+class QVsp2BlendingDevice
+{
+public:
+ QVsp2BlendingDevice(const QSize& screenSize); //TODO: add support for output format as well?
+ bool enableInput(int i, const QRect &bufferGeometry, uint drmFormat, uint bytesPerLine);
+ int enableInput(const QRect &bufferGeometry, uint drmFormat, uint bytesPerLine);
+ bool disableInput(int i);
+ bool setInputBuffer(int index, int dmabufFd);
+ bool setInputPosition(int index, const QPoint &position);
+ bool setInputAlpha(int index, qreal alpha);
+ bool blend(int outputDmabufFd);
+ int numInputs() const;
+ bool isDirty() const { return m_dirty; }
+private:
+ bool streamOn();
+ bool streamOff();
+ bool setInputFormat(int i, const QRect &bufferGeometry, uint pixelFormat, uint bytesPerLine);
+ QLinuxMediaDevice m_mediaDevice;
+ QLinuxMediaDevice::CaptureSubDevice *m_wpfOutput = nullptr; // wpf output
+ struct Input {
+ bool enabled = false;
+ QRect geometry;
+ qreal alpha = 1;
+ struct {
+ int fd = -1;
+ uint bytesUsed = 0;
+ uint length = 0;
+ } dmabuf;
+ struct media_link *linkToBru = nullptr; //rpf.x:1 -> bru:x
+ struct media_pad *inputFormatPad = nullptr; // rpf.x:0
+ struct media_pad *outputFormatPad = nullptr; // rpf.x:1
+ int outputFormatFd = -1; // rpf.x:1 (again, because v4l2_subdev_* doesn't have a way to set alpha)
+ struct media_pad *bruInputFormatPad = nullptr; // bru:x
+ QLinuxMediaDevice::OutputSubDevice *rpfInput = nullptr; // rpf.x input
+ };
+ QVector<struct Input> m_inputs;
+ const QSize m_screenSize;
+ bool m_dirty = true;
+};
+
+QT_END_NAMESPACE
+
+#endif // QVSP2BLENDINGDEVICE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp
index 64d0d9b515..2e84915c80 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp
@@ -63,83 +63,12 @@ private:
QAtomicInt running;
-static Qt::MouseButtons translateMouseButtons(int s)
-{
- Qt::MouseButtons ret = 0;
- if (s & XCB_BUTTON_MASK_1)
- ret |= Qt::LeftButton;
- if (s & XCB_BUTTON_MASK_2)
- ret |= Qt::MidButton;
- if (s & XCB_BUTTON_MASK_3)
- ret |= Qt::RightButton;
- return ret;
-}
-
-static Qt::MouseButton translateMouseButton(xcb_button_t s)
-{
- switch (s) {
- case 1: return Qt::LeftButton;
- case 2: return Qt::MidButton;
- case 3: return Qt::RightButton;
- // Button values 4-7 were already handled as Wheel events, and won't occur here.
- case 8: return Qt::BackButton; // Also known as Qt::ExtraButton1
- case 9: return Qt::ForwardButton; // Also known as Qt::ExtraButton2
- case 10: return Qt::ExtraButton3;
- case 11: return Qt::ExtraButton4;
- case 12: return Qt::ExtraButton5;
- case 13: return Qt::ExtraButton6;
- case 14: return Qt::ExtraButton7;
- case 15: return Qt::ExtraButton8;
- case 16: return Qt::ExtraButton9;
- case 17: return Qt::ExtraButton10;
- case 18: return Qt::ExtraButton11;
- case 19: return Qt::ExtraButton12;
- case 20: return Qt::ExtraButton13;
- case 21: return Qt::ExtraButton14;
- case 22: return Qt::ExtraButton15;
- case 23: return Qt::ExtraButton16;
- case 24: return Qt::ExtraButton17;
- case 25: return Qt::ExtraButton18;
- case 26: return Qt::ExtraButton19;
- case 27: return Qt::ExtraButton20;
- case 28: return Qt::ExtraButton21;
- case 29: return Qt::ExtraButton22;
- case 30: return Qt::ExtraButton23;
- case 31: return Qt::ExtraButton24;
- default: return Qt::NoButton;
- }
-}
-
void EventReader::run()
{
- Qt::MouseButtons buttons;
-
xcb_generic_event_t *event = nullptr;
while (running.load() && (event = xcb_wait_for_event(m_integration->connection()))) {
uint response_type = event->response_type & ~0x80;
switch (response_type) {
- case XCB_BUTTON_PRESS: {
- xcb_button_press_event_t *press = (xcb_button_press_event_t *)event;
- QPoint p(press->event_x, press->event_y);
- buttons = (buttons & ~0x7) | translateMouseButtons(press->state);
- buttons |= translateMouseButton(press->detail);
- QWindowSystemInterface::handleMouseEvent(0, press->time, p, p, buttons);
- break;
- }
- case XCB_BUTTON_RELEASE: {
- xcb_button_release_event_t *release = (xcb_button_release_event_t *)event;
- QPoint p(release->event_x, release->event_y);
- buttons = (buttons & ~0x7) | translateMouseButtons(release->state);
- buttons &= ~translateMouseButton(release->detail);
- QWindowSystemInterface::handleMouseEvent(0, release->time, p, p, buttons);
- break;
- }
- case XCB_MOTION_NOTIFY: {
- xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *)event;
- QPoint p(motion->event_x, motion->event_y);
- QWindowSystemInterface::handleMouseEvent(0, motion->time, p, p, buttons);
- break;
- }
case XCB_CLIENT_MESSAGE: {
xcb_client_message_event_t *client = (xcb_client_message_event_t *) event;
const xcb_atom_t *atoms = m_integration->atoms();