1// Copyright (C) 2016 Pelagicore AG
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qeglfskmsegldevicescreen.h"
5#include "qeglfskmsegldevice.h"
6#include <QGuiApplication>
7#include <QLoggingCategory>
8#include <errno.h>
9
10QT_BEGIN_NAMESPACE
11
12Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
13
14QEglFSKmsEglDeviceScreen::QEglFSKmsEglDeviceScreen(QEglFSKmsDevice *device, const QKmsOutput &output)
15 : QEglFSKmsScreen(device, output)
16 , m_default_fb_handle(uint32_t(-1))
17 , m_default_fb_id(uint32_t(-1))
18{
19 const int fd = device->fd();
20
21 struct drm_mode_create_dumb createRequest;
22 createRequest.width = output.size.width();
23 createRequest.height = output.size.height();
24 createRequest.bpp = 32;
25 createRequest.flags = 0;
26
27 qCDebug(qLcEglfsKmsDebug, "Creating dumb fb %dx%d", createRequest.width, createRequest.height);
28
29 int ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, arg: &createRequest);
30 if (ret < 0)
31 qFatal(msg: "Unable to create dumb buffer.\n");
32
33 m_default_fb_handle = createRequest.handle;
34
35 uint32_t handles[4] = { 0, 0, 0, 0 };
36 uint32_t pitches[4] = { 0, 0, 0, 0 };
37 uint32_t offsets[4] = { 0, 0, 0, 0 };
38
39 handles[0] = createRequest.handle;
40 pitches[0] = createRequest.pitch;
41 offsets[0] = 0;
42
43 ret = drmModeAddFB2(fd, width: createRequest.width, height: createRequest.height, DRM_FORMAT_ARGB8888, bo_handles: handles,
44 pitches, offsets, buf_id: &m_default_fb_id, flags: 0);
45 if (ret)
46 qFatal(msg: "Unable to add fb\n");
47
48 qCDebug(qLcEglfsKmsDebug, "Added dumb fb %dx%d handle:%u pitch:%d id:%u", createRequest.width, createRequest.height,
49 createRequest.handle, createRequest.pitch, m_default_fb_id);
50}
51
52QEglFSKmsEglDeviceScreen::~QEglFSKmsEglDeviceScreen()
53{
54 int ret;
55 const int fd = device()->fd();
56
57 if (m_default_fb_id != uint32_t(-1)) {
58 ret = drmModeRmFB(fd, bufferId: m_default_fb_id);
59 if (ret)
60 qErrnoWarning(msg: "drmModeRmFB failed");
61 }
62
63 if (m_default_fb_handle != uint32_t(-1)) {
64 struct drm_mode_destroy_dumb destroyRequest;
65 destroyRequest.handle = m_default_fb_handle;
66
67 ret = drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, arg: &destroyRequest);
68 if (ret)
69 qErrnoWarning(msg: "DRM_IOCTL_MODE_DESTROY_DUMB failed");
70 }
71
72 const int remainingScreenCount = qGuiApp->screens().size();
73 qCDebug(qLcEglfsKmsDebug, "Screen dtor. Remaining screens: %d", remainingScreenCount);
74 if (!remainingScreenCount && !device()->screenConfig()->separateScreens())
75 static_cast<QEglFSKmsEglDevice *>(device())->destroyGlobalCursor();
76}
77
78QPlatformCursor *QEglFSKmsEglDeviceScreen::cursor() const
79{
80 // The base class creates a cursor via integration->createCursor()
81 // in its ctor. With separateScreens just use that. Otherwise
82 // there's a virtual desktop and the device has a global cursor
83 // and the base class has no dedicated cursor at all.
84 // config->hwCursor() is ignored for now, just use the standard OpenGL cursor.
85 return device()->screenConfig()->separateScreens()
86 ? QEglFSScreen::cursor()
87 : static_cast<QEglFSKmsEglDevice *>(device())->globalCursor();
88}
89
90void QEglFSKmsEglDeviceScreen::waitForFlip()
91{
92 QKmsOutput &op(output());
93 const int fd = device()->fd();
94 const uint32_t w = op.modes[op.mode].hdisplay;
95 const uint32_t h = op.modes[op.mode].vdisplay;
96
97 if (!op.mode_set) {
98 op.mode_set = true;
99
100 drmModeCrtcPtr currentMode = drmModeGetCrtc(fd, crtcId: op.crtc_id);
101 const bool alreadySet = currentMode && currentMode->width == w && currentMode->height == h;
102 if (currentMode)
103 drmModeFreeCrtc(ptr: currentMode);
104 if (alreadySet) {
105 // Maybe detecting the DPMS mode could help here, but there are no properties
106 // exposed on the connector apparently. So rely on an env var for now.
107 // Note that typically, we need to set crtc with the default fb even if the
108 // mode did not change, unless QT_QPA_EGLFS_ALWAYS_SET_MODE is explicitly specified.
109 static bool envVarSet = false;
110 static bool alwaysDoSet = qEnvironmentVariableIntValue(varName: "QT_QPA_EGLFS_ALWAYS_SET_MODE", ok: &envVarSet);
111 if (envVarSet && !alwaysDoSet) {
112 qCDebug(qLcEglfsKmsDebug, "Mode already set");
113 return;
114 }
115 }
116
117 qCDebug(qLcEglfsKmsDebug, "Setting mode");
118 int ret = drmModeSetCrtc(fd, crtcId: op.crtc_id,
119 bufferId: m_default_fb_id, x: 0, y: 0,
120 connectors: &op.connector_id, count: 1,
121 mode: &op.modes[op.mode]);
122 if (ret)
123 qErrnoWarning(errno, msg: "drmModeSetCrtc failed");
124 }
125
126 if (!op.forced_plane_set) {
127 op.forced_plane_set = true;
128
129 if (op.wants_forced_plane) {
130 qCDebug(qLcEglfsKmsDebug, "Setting plane %u", op.forced_plane_id);
131 int ret = drmModeSetPlane(fd, plane_id: op.forced_plane_id, crtc_id: op.crtc_id, fb_id: m_default_fb_id, flags: 0,
132 crtc_x: 0, crtc_y: 0, crtc_w: w, crtc_h: h,
133 src_x: 0 << 16, src_y: 0 << 16, src_w: op.size.width() << 16, src_h: op.size.height() << 16);
134 if (ret == -1)
135 qErrnoWarning(errno, msg: "drmModeSetPlane failed");
136 }
137 }
138}
139
140QT_END_NAMESPACE
141

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtbase/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.cpp