/**************************************************************************** ** ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //#include #include "qkmsscreen.h" #include "qkmscursor.h" #include "qkmsdevice.h" #include "qkmscontext.h" #include #include #include QT_BEGIN_NAMESPACE //Fallback mode (taken from Wayland DRM demo compositor) static drmModeModeInfo builtin_1024x768 = { 63500, //clock 1024, 1072, 1176, 1328, 0, 768, 771, 775, 798, 0, 59920, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, 0, "1024x768" }; QKmsScreen::QKmsScreen(QKmsDevice *device, int connectorId) : m_device(device), m_current_bo(0), m_next_bo(0), m_connectorId(connectorId), m_depth(32), m_format(QImage::Format_Invalid), m_refreshTime(16000), m_modeSet(false) { m_cursor = new QKmsCursor(this); initializeScreenMode(); } QKmsScreen::~QKmsScreen() { delete m_cursor; } QRect QKmsScreen::geometry() const { return m_geometry; } int QKmsScreen::depth() const { return m_depth; } QImage::Format QKmsScreen::format() const { return m_format; } QSizeF QKmsScreen::physicalSize() const { return m_physicalSize; } QPlatformCursor *QKmsScreen::cursor() const { return m_cursor; } void QKmsScreen::initializeScreenMode() { //Determine optimal mode for screen drmModeRes *resources = drmModeGetResources(m_device->fd()); if (!resources) qFatal("drmModeGetResources failed"); drmModeConnector *connector = drmModeGetConnector(m_device->fd(), m_connectorId); drmModeModeInfo *mode = 0; if (connector->count_modes > 0) mode = &connector->modes[0]; else mode = &builtin_1024x768; drmModeEncoder *encoder = drmModeGetEncoder(m_device->fd(), connector->encoders[0]); if (encoder == 0) qFatal("No encoder for connector."); int i; for (i = 0; i < resources->count_crtcs; i++) { if (encoder->possible_crtcs & (1 << i)) break; } if (i == resources->count_crtcs) qFatal("No usable crtc for encoder."); m_oldCrtc = drmModeGetCrtc(m_device->fd(), encoder->crtc_id); m_crtcId = resources->crtcs[i]; m_mode = *mode; m_geometry = QRect(0, 0, m_mode.hdisplay, m_mode.vdisplay); qDebug() << "kms initialized with geometry" << m_geometry; m_depth = 32; m_format = QImage::Format_RGB32; m_physicalSize = QSizeF(connector->mmWidth, connector->mmHeight); m_gbmSurface = gbm_surface_create(m_device->gbmDevice(), m_mode.hdisplay, m_mode.vdisplay, GBM_BO_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); qDebug() << "created gbm surface" << m_gbmSurface << m_mode.hdisplay << m_mode.vdisplay; //Cleanup drmModeFreeEncoder(encoder); drmModeFreeConnector(connector); drmModeFreeResources(resources); } QSurfaceFormat QKmsScreen::tweakFormat(const QSurfaceFormat &format) { QSurfaceFormat fmt = format; fmt.setRedBufferSize(8); fmt.setGreenBufferSize(8); fmt.setBlueBufferSize(8); if (fmt.alphaBufferSize() != -1) fmt.setAlphaBufferSize(8); return fmt; } void QKmsScreen::initializeWithFormat(const QSurfaceFormat &format) { EGLDisplay display = m_device->eglDisplay(); EGLConfig config = q_configFromGLFormat(display, tweakFormat(format), true); m_eglWindowSurface = eglCreateWindowSurface(display, config, (EGLNativeWindowType)m_gbmSurface, NULL); qDebug() << "created window surface"; } void QKmsScreen::swapBuffers() { eglSwapBuffers(m_device->eglDisplay(), m_eglWindowSurface); m_next_bo = gbm_surface_lock_front_buffer(m_gbmSurface); if (!m_next_bo) qFatal("kms: Failed to lock front buffer"); performPageFlip(); } void QKmsScreen::performPageFlip() { if (!m_next_bo) return; uint32_t width = gbm_bo_get_width(m_next_bo); uint32_t height = gbm_bo_get_height(m_next_bo); uint32_t stride = gbm_bo_get_pitch(m_next_bo); uint32_t handle = gbm_bo_get_handle(m_next_bo).u32; uint32_t fb_id; int ret = drmModeAddFB(m_device->fd(), width, height, 24, 32, stride, handle, &fb_id); if (ret) { qFatal("kms: Failed to create fb: fd %d, w %d, h %d, stride %d, handle %d, ret %d", m_device->fd(), width, height, stride, handle, ret); } if (!m_modeSet) { //Set the Mode of the screen. int ret = drmModeSetCrtc(m_device->fd(), m_crtcId, fb_id, 0, 0, &m_connectorId, 1, &m_mode); if (ret) qFatal("failed to set mode"); m_modeSet = true; // Initialize cursor static int hideCursor = qgetenv("QT_QPA_KMS_HIDECURSOR").toInt(); if (!hideCursor) { QCursor cursor(Qt::ArrowCursor); m_cursor->changeCursor(&cursor, 0); } } int pageFlipStatus = drmModePageFlip(m_device->fd(), m_crtcId, fb_id, DRM_MODE_PAGE_FLIP_EVENT, this); if (pageFlipStatus) qWarning("Pageflip status: %d", pageFlipStatus); } void QKmsScreen::handlePageFlipped() { if (m_current_bo) gbm_surface_release_buffer(m_gbmSurface, m_current_bo); m_current_bo = m_next_bo; m_next_bo = 0; } QKmsDevice * QKmsScreen::device() const { return m_device; } void QKmsScreen::waitForPageFlipComplete() { while (m_next_bo) { #if 0 //Check manually if there is something to be read on the device //as there are senarios where the signal is not received (starvation) fd_set fdSet; timeval timeValue; int returnValue; FD_ZERO(&fdSet); FD_SET(m_device->fd(), &fdSet); timeValue.tv_sec = 0; timeValue.tv_usec = 1000; returnValue = select(1, &fdSet, 0, 0, &timeValue); printf("select returns %d\n", returnValue); #endif m_device->handlePageFlipCompleted(); } } QT_END_NAMESPACE