diff options
Diffstat (limited to 'src/plugins/platforms/eglfs')
16 files changed, 2301 insertions, 1 deletions
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_kms_support/eglfs_kms_support.pro b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/eglfs_kms_support.pro index 88cf6db33b..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 @@ -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_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..f85d6183f6 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.cpp @@ -0,0 +1,242 @@ +/**************************************************************************** +** +** 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::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::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..c09ee205c8 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** 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 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..42af93287d --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.cpp @@ -0,0 +1,359 @@ +/**************************************************************************** +** +** 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); +} + +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..19f65e7e7d --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** 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); + 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..21de052b87 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qlinuxmediadevice.cpp @@ -0,0 +1,607 @@ +/**************************************************************************** +** +** 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); +} + +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::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..848099eb46 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qlinuxmediadevice.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** 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 bool setSubdevFormat(struct media_pad *pad, const QSize &size, uint32_t mbusFormat = MEDIA_BUS_FMT_ARGB8888_1X32); + + 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..c9b9bb5b7f --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qvsp2blendingdevice.cpp @@ -0,0 +1,326 @@ +/**************************************************************************** +** +** 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.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::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"; + 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() +{ + if (!m_wpfOutput->streamOff()) { + //TODO: perhaps it's better to try to continue with the other inputs? + return false; + } + + for (auto &input : m_inputs) { + if (input.enabled) { + if (!input.rpfInput->streamOff()) + return false; + } + } + + return true; +} + +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..ff2d581d72 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qvsp2blendingdevice.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** 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 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; + 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 + 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 |