1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 The Qt Company Ltd.
4 ** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
5 ** Copyright (C) 2016 Pelagicore AG
6 ** Contact: https://www.qt.io/licensing/
7 **
8 ** This file is part of the plugins of the Qt Toolkit.
9 **
10 ** $QT_BEGIN_LICENSE:LGPL$
11 ** Commercial License Usage
12 ** Licensees holding valid commercial Qt licenses may use this file in
13 ** accordance with the commercial license agreement provided with the
14 ** Software or, alternatively, in accordance with the terms contained in
15 ** a written agreement between you and The Qt Company. For licensing terms
16 ** and conditions see https://www.qt.io/terms-conditions. For further
17 ** information use the contact form at https://www.qt.io/contact-us.
18 **
19 ** GNU Lesser General Public License Usage
20 ** Alternatively, this file may be used under the terms of the GNU Lesser
21 ** General Public License version 3 as published by the Free Software
22 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
23 ** packaging of this file. Please review the following information to
24 ** ensure the GNU Lesser General Public License version 3 requirements
25 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
26 **
27 ** GNU General Public License Usage
28 ** Alternatively, this file may be used under the terms of the GNU
29 ** General Public License version 2.0 or (at your option) the GNU General
30 ** Public license version 3 or any later version approved by the KDE Free
31 ** Qt Foundation. The licenses are as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
33 ** included in the packaging of this file. Please review the following
34 ** information to ensure the GNU General Public License requirements will
35 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
36 ** https://www.gnu.org/licenses/gpl-3.0.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qeglfskmsvsp2screen.h"
43 #include "qeglfskmsvsp2device.h"
44 #include <qeglfskmshelpers.h>
45 
46 #include <QtCore/QLoggingCategory>
47 #include <QtGui/private/qguiapplication_p.h>
48 
49 #include <drm_fourcc.h>
50 #include <xf86drm.h>
51 #include <fcntl.h>
52 
53 //TODO move to qlinuxmediadevice?
54 #include <cstdlib> //this needs to go before mediactl/mediactl.h because it uses size_t without including it
55 extern "C" {
56 #include <mediactl/mediactl.h>
57 #include <mediactl/v4l2subdev.h> //needed in header for default arguments
58 }
59 
60 QT_BEGIN_NAMESPACE
61 
Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)62 Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
63 
64 static inline uint32_t drmFormatToGbmFormat(uint32_t drmFormat)
65 {
66     Q_ASSERT(DRM_FORMAT_XRGB8888 == GBM_FORMAT_XRGB8888);
67     return drmFormat;
68 }
69 
gbmFormatToDrmFormat(uint32_t gbmFormat)70 static inline uint32_t gbmFormatToDrmFormat(uint32_t gbmFormat) //TODO: is this needed?
71 {
72     Q_ASSERT(DRM_FORMAT_XRGB8888 == GBM_FORMAT_XRGB8888);
73     return gbmFormat;
74 }
75 
dmaBufferDestroyedHandler(gbm_bo * gbmBo,void * data)76 void QEglFSKmsVsp2Screen::dmaBufferDestroyedHandler(gbm_bo *gbmBo, void *data)
77 {
78     Q_UNUSED(gbmBo); //TODO: do we need to do something with it after all?
79     auto dmabuf = static_cast<DmaBuffer *>(data);
80     //TODO: need some extra cleanup here?
81     delete dmabuf;
82 }
83 
dmaBufferForGbmBuffer(gbm_bo * gbmBo)84 QEglFSKmsVsp2Screen::DmaBuffer *QEglFSKmsVsp2Screen::dmaBufferForGbmBuffer(gbm_bo *gbmBo)
85 {
86     auto existingBuffer = static_cast<DmaBuffer *>(gbm_bo_get_user_data(gbmBo));
87     if (existingBuffer)
88         return existingBuffer;
89 
90     uint32_t handle = gbm_bo_get_handle(gbmBo).u32;
91     QScopedPointer<DmaBuffer> fb(new DmaBuffer);
92     int ret = drmPrimeHandleToFD(device()->fd(), handle, DRM_CLOEXEC, &fb->dmabufFd);
93     if (ret) {
94         qWarning("Failed to create dmabuf file descriptor for buffer with drmPrimeHandleToFd");
95         return nullptr;
96     }
97 
98     gbm_bo_set_user_data(gbmBo, fb.data(), dmaBufferDestroyedHandler);
99     return fb.take();
100 }
101 
QEglFSKmsVsp2Screen(QEglFSKmsDevice * device,const QKmsOutput & output)102 QEglFSKmsVsp2Screen::QEglFSKmsVsp2Screen(QEglFSKmsDevice *device, const QKmsOutput &output)
103     : QEglFSKmsScreen(device, output)
104     , m_blender(new Blender(this))
105 {
106 }
107 
createSurface()108 gbm_surface *QEglFSKmsVsp2Screen::createSurface()
109 {
110     if (!m_gbmSurface) {
111         uint32_t gbmFormat = drmFormatToGbmFormat(m_output.drm_format);
112         qCDebug(qLcEglfsKmsDebug, "Creating gbm_surface for screen %s with format 0x%x", qPrintable(name()), gbmFormat);
113         Q_ASSERT(rawGeometry().isValid());
114         m_gbmSurface = gbm_surface_create(static_cast<QEglFSKmsVsp2Device *>(device())->gbmDevice(),
115                                            uint(rawGeometry().width()),
116                                            uint(rawGeometry().height()),
117                                            gbmFormat,
118                                            GBM_BO_USE_RENDERING);
119     }
120 
121     if (!m_blendDevice)
122         initVsp2();
123 
124     if (m_frameBuffers[m_backFb].dmabufFd == -1)
125         initDumbFrameBuffers();
126 
127     return m_gbmSurface; // not owned, gets destroyed in QEglFSKmsGbmIntegration::destroyNativeWindow()
128 }
129 
resetSurface()130 void QEglFSKmsVsp2Screen::resetSurface()
131 {
132     m_gbmSurface = nullptr;
133 }
134 
initDumbFrameBuffers()135 void QEglFSKmsVsp2Screen::initDumbFrameBuffers()
136 {
137     for (auto &fb : m_frameBuffers)
138         initDumbFrameBuffer(fb);
139 }
140 
initVsp2()141 void QEglFSKmsVsp2Screen::initVsp2()
142 {
143     qCDebug(qLcEglfsKmsDebug, "Initializing Vsp2 hardware");
144     m_blendDevice.reset(new QVsp2BlendingDevice(rawGeometry().size()));
145 
146     // Enable input for main buffer drawn by the compositor (always on)
147     initQtLayer();
148 }
149 
initQtLayer()150 void QEglFSKmsVsp2Screen::initQtLayer()
151 {
152     const QSize screenSize = rawGeometry().size();
153     const uint bytesPerLine = uint(screenSize.width()) * 4; //TODO: is this ok?
154     bool formatSet = m_blendDevice->enableInput(m_qtLayer, QRect(QPoint(), screenSize), m_output.drm_format, bytesPerLine);
155     if (!formatSet) {
156         const uint32_t fallbackFormat = DRM_FORMAT_ARGB8888;
157         qWarning() << "Failed to set format" << q_fourccToString(m_output.drm_format)
158                    << "falling back to" << q_fourccToString(fallbackFormat);
159         formatSet = m_blendDevice->enableInput(m_qtLayer, QRect(QPoint(), screenSize), fallbackFormat, bytesPerLine);
160         if (!formatSet)
161             qFatal("Failed to set vsp2 blending format");
162     }
163 }
164 
addLayer(int dmabufFd,const QSize & size,const QPoint & position,uint drmPixelFormat,uint bytesPerLine)165 int QEglFSKmsVsp2Screen::addLayer(int dmabufFd, const QSize &size, const QPoint &position, uint drmPixelFormat, uint bytesPerLine)
166 {
167     int index = m_blendDevice->enableInput(QRect(position, size), drmPixelFormat, bytesPerLine);
168     if (index != -1) {
169         m_blendDevice->setInputBuffer(index, dmabufFd);
170         int id = index; //TODO: maybe make id something independent of layer index?
171         qCDebug(qLcEglfsKmsDebug) << "Enabled extra layer for vsp input" << index;
172         return id;
173     }
174     qWarning() << "Failed to add layer";
175     return -1;
176 }
177 
setLayerBuffer(int id,int dmabufFd)178 void QEglFSKmsVsp2Screen::setLayerBuffer(int id, int dmabufFd)
179 {
180     int layerIndex = id;
181     m_blendDevice->setInputBuffer(layerIndex, dmabufFd);
182     if (!m_blendScheduled) {
183         m_blendScheduled = true;
184         QCoreApplication::postEvent(m_blender.data(), new QEvent(QEvent::User));
185     }
186 }
187 
setLayerPosition(int id,const QPoint & position)188 void QEglFSKmsVsp2Screen::setLayerPosition(int id, const QPoint &position)
189 {
190     int layerIndex = id;
191     m_blendDevice->setInputPosition(layerIndex, position);
192 }
193 
setLayerAlpha(int id,qreal alpha)194 void QEglFSKmsVsp2Screen::setLayerAlpha(int id, qreal alpha)
195 {
196     int layerIndex = id;
197     m_blendDevice->setInputAlpha(layerIndex, alpha);
198 }
199 
removeLayer(int id)200 bool QEglFSKmsVsp2Screen::removeLayer(int id)
201 {
202     int layerIndex = id;
203     m_blendDevice->disableInput(layerIndex);
204     return true;
205 }
206 
addBlendListener(void (* callback)())207 void QEglFSKmsVsp2Screen::addBlendListener(void (*callback)())
208 {
209     m_blendFinishedCallbacks.append(callback);
210 }
211 
flip()212 void QEglFSKmsVsp2Screen::flip()
213 {
214     if (!m_gbmSurface) {
215         qWarning("Cannot sync before platform init!");
216         return;
217     }
218 
219     if (!m_blendScheduled && !m_nextGbmBo) {
220         m_nextGbmBo = gbm_surface_lock_front_buffer(m_gbmSurface);
221 
222         if (!m_nextGbmBo) {
223             qWarning("Could not lock GBM surface front buffer!");
224             return;
225         }
226 
227         m_blendScheduled = true;
228         QCoreApplication::postEvent(m_blender.data(), new QEvent(QEvent::User));
229     }
230 }
231 
ensureModeSet()232 void QEglFSKmsVsp2Screen::ensureModeSet()
233 {
234     const int driFd = device()->fd();
235     QKmsOutput &op(output());
236     if (!op.mode_set) {
237         int ret = drmModeSetCrtc(driFd,
238                                  op.crtc_id,
239                                  m_frameBuffers[m_backFb].drmBufferId,
240                                  0, 0,
241                                  &op.connector_id, 1,
242                                  &op.modes[op.mode]);
243 
244         if (ret == -1) {
245             qErrnoWarning(errno, "Could not set DRM mode!");
246         } else {
247             op.mode_set = true;
248             setPowerState(PowerStateOn);
249         }
250     }
251 }
252 
doDrmFlip()253 void QEglFSKmsVsp2Screen::doDrmFlip()
254 {
255     QKmsOutput &op(output());
256     const int driFd = device()->fd();
257 
258     int ret = drmModePageFlip(driFd,
259                               op.crtc_id,
260                               m_frameBuffers[m_backFb].drmBufferId,
261                               0,
262                               this);
263 
264     if (ret)
265         qErrnoWarning("Could not queue DRM page flip on screen %s", qPrintable(name()));
266 
267     m_backFb = (m_backFb + 1) % 2;
268 }
269 
blendAndFlipDrm()270 void QEglFSKmsVsp2Screen::blendAndFlipDrm()
271 {
272     m_blendScheduled = false;
273     if (!m_nextGbmBo && !m_blendDevice->isDirty())
274         return;
275 
276     FrameBuffer &backBuffer = m_frameBuffers[m_backFb];
277     if (backBuffer.dmabufFd == -1)
278         initDumbFrameBuffers();
279 
280     if (m_nextGbmBo) {
281         Q_ASSERT(m_nextGbmBo != m_currentGbmBo);
282         int compositorBackBufferDmaFd = dmaBufferForGbmBuffer(m_nextGbmBo)->dmabufFd;
283         m_blendDevice->setInputBuffer(m_qtLayer, compositorBackBufferDmaFd);
284 
285         if (m_currentGbmBo)
286             gbm_surface_release_buffer(m_gbmSurface, m_currentGbmBo);
287         m_currentGbmBo = m_nextGbmBo;
288         m_nextGbmBo = nullptr;
289     }
290 
291     ensureModeSet();
292 
293     if (!m_blendDevice)
294         initVsp2();
295 
296     if (!m_blendDevice->isDirty())
297         return;
298 
299     const int driFd = device()->fd();
300     drmVBlank vBlank;
301     vBlank.request.type = static_cast<drmVBlankSeqType>(DRM_VBLANK_RELATIVE | DRM_VBLANK_SECONDARY); //TODO: make secondary configurable (or automatic)
302     vBlank.request.sequence = 1;
303     vBlank.request.signal = 0;
304     drmWaitVBlank(driFd, &vBlank);
305 
306     if (!m_blendDevice->blend(backBuffer.dmabufFd)) {
307         qWarning() << "Vsp2: Blending failed";
308 
309         // For some reason, a failed blend may often mess up the qt layer, so reinitialize it here
310         m_blendDevice->disableInput(m_qtLayer);
311         initQtLayer();
312     }
313 
314     for (auto cb : m_blendFinishedCallbacks)
315         cb();
316 
317     doDrmFlip();
318 }
319 
initDumbFrameBuffer(FrameBuffer & fb)320 void QEglFSKmsVsp2Screen::initDumbFrameBuffer(FrameBuffer &fb)
321 {
322     QKmsOutput &op(output());
323     const uint32_t width = op.modes[op.mode].hdisplay;
324     const uint32_t height = op.modes[op.mode].vdisplay;
325 
326     Q_ASSERT(fb.dmabufFd == -1);
327     const uint32_t dumbBufferFlags = 0; //TODO: do we want some flags? What's possible?
328     const uint32_t bpp = 32;
329 
330     drm_mode_create_dumb creq = {
331         height,
332         width,
333         bpp,
334         dumbBufferFlags,
335         0, 0, 0 //return values
336     };
337 
338     const int driFd = device()->fd();
339     if (drmIoctl(driFd, DRM_IOCTL_MODE_CREATE_DUMB, &creq) == -1)
340         qFatal("Failed to create dumb buffer: %s", strerror(errno));
341 
342 //    uint32_t handles[4] = { gbm_bo_get_handle(bo).u32 };
343 //    uint32_t strides[4] = { gbm_bo_get_stride(bo) };
344 //    uint32_t offsets[4] = { 0 };
345 //    uint32_t pixelFormat = gbmFormatToDrmFormat(gbm_bo_get_format(bo));
346 
347     //TODO: support additional planes
348     uint32_t gbmBoHandles[4] = { creq.handle, 0, 0, 0 };
349     uint32_t strides[4] = { creq.pitch, 0, 0, 0 };
350     uint32_t offsets[4] = { 0 };
351     uint32_t pixelFormat = DRM_FORMAT_ARGB8888; //TODO: support other formats?
352     uint32_t drmFlags = 0;
353 
354     qCDebug(qLcEglfsKmsDebug) << "Adding FB" << QSize(width, height)
355                               << ", DRM format" << q_fourccToString(pixelFormat);
356     int ret = drmModeAddFB2(driFd, width, height, pixelFormat,
357                             gbmBoHandles, strides, offsets, &fb.drmBufferId, drmFlags);
358     if (ret)
359         qFatal("drmModeAddFB2 failed: %s", strerror(errno));
360 
361     drmPrimeHandleToFD(driFd, gbmBoHandles[0], DRM_CLOEXEC, &fb.dmabufFd);
362 }
363 
event(QEvent * event)364 bool QEglFSKmsVsp2Screen::Blender::event(QEvent *event)
365 {
366     switch (event->type()) {
367     case QEvent::User:
368         m_screen->blendAndFlipDrm();
369         return true;
370     default:
371         return QObject::event(event);
372     }
373 }
374 
375 QT_END_NAMESPACE
376