diff options
Diffstat (limited to 'src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp')
-rw-r--r-- | src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp | 240 |
1 files changed, 178 insertions, 62 deletions
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(¤tMode->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 |