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 | |
10 | QT_BEGIN_NAMESPACE |
11 | |
12 | Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug) |
13 | |
14 | QEglFSKmsEglDeviceScreen::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 | |
52 | QEglFSKmsEglDeviceScreen::~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 | |
78 | QPlatformCursor *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 | |
90 | void 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 | |
140 | QT_END_NAMESPACE |
141 | |