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

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