vdr-plugin-softhddevice-drm-gles 1.5.9-20e15de
drmdevice.cpp
Go to the documentation of this file.
1
24#include <cerrno>
25#include <cstdint>
26#include <cstdio>
27#include <cstring>
28#include <cinttypes>
29#include <vector>
30
31#include <fcntl.h>
32#include <unistd.h>
33
34#ifdef USE_GLES
35#include <assert.h>
36#include <EGL/egl.h>
37#include <EGL/eglext.h>
38#endif
39
40#include <drm_fourcc.h>
41#include <xf86drm.h>
42#include <xf86drmMode.h>
43
44#include "drmdevice.h"
45#include "drmplane.h"
46#include "logger.h"
47#include "videorender.h"
48
49/*****************************************************************************
50 * cDrmDevice class
51 ****************************************************************************/
52
59cDrmDevice::cDrmDevice(cVideoRender *render, const char* resolution)
60 : m_pRender(render)
61{
62 if (resolution)
64}
65
70{
71 LOGDEBUG2(L_DRM, "drmdevice: %s", __FUNCTION__);
72}
73
74static int get_resources(int fd, drmModeRes **resources)
75{
76 *resources = drmModeGetResources(fd);
77 if (*resources == NULL) {
78 LOGERROR("drmdevice: %s: cannot retrieve DRM resources (%d): %m", __FUNCTION__, errno);
79 return -1;
80 }
81 return 0;
82}
83
89static int TestCaps(int fd)
90{
91 uint64_t test;
92
93 if (drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &test) < 0 || test == 0)
94 return 1;
95
96 if (drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) != 0)
97 return 1;
98
99 if (drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1) != 0)
100 return 1;
101
102 if (drmGetCap(fd, DRM_CAP_PRIME, &test) < 0)
103 return 1;
104
105 if (drmGetCap(fd, DRM_PRIME_CAP_EXPORT, &test) < 0)
106 return 1;
107
108 if (drmGetCap(fd, DRM_PRIME_CAP_IMPORT, &test) < 0)
109 return 1;
110
111 return 0;
112}
113
114#define MAX_DRM_DEVICES 64
120static int FindDrmDevice(drmModeRes **resources)
121{
122 drmDevicePtr devices[MAX_DRM_DEVICES] = { NULL };
123 int num_devices, fd = -1;
124
125 num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES);
126 if (num_devices < 0) {
127 LOGERROR("drmdevice: %s: drmGetDevices2 failed: %s", __FUNCTION__, strerror(-num_devices));
128 return fd;
129 }
130
131 for (int i = 0; i < num_devices && fd < 0; i++) {
132 drmDevicePtr device = devices[i];
133 int ret;
134
135 if (!(device->available_nodes & (1 << DRM_NODE_PRIMARY)))
136 continue;
137 fd = open(device->nodes[DRM_NODE_PRIMARY], O_RDWR);
138 if (fd < 0)
139 continue;
140
141 if (TestCaps(fd)) {
142 close(fd);
143 fd = -1;
144 continue;
145 }
146
147 ret = get_resources(fd, resources);
148 if (!ret)
149 break;
150 close(fd);
151 fd = -1;
152 }
153 drmFreeDevices(devices, num_devices);
154
155 if (fd < 0)
156 LOGERROR("drmdevice: %s: no drm device found!", __FUNCTION__);
157
158 return fd;
159}
160
164static drmModeConnector *FindDrmConnector(int fd, drmModeRes *resources)
165{
166 drmModeConnector *connector = NULL;
167 int i;
168
169 // search for a connected connector
170 for (i = 0; i < resources->count_connectors; i++) {
171 connector = drmModeGetConnector(fd, resources->connectors[i]);
172 if (connector && connector->connection == DRM_MODE_CONNECTED)
173 return connector;
174 drmModeFreeConnector(connector);
175 connector = NULL;
176 }
177
178 // search for a not connected connector, but with available modes
179 // this is a workaround for RPI: in case we don't have a monitor connected
180 // we can load an edid file at boot time, where the available modes are listed.
181 // To bring softhddevice up, we also have to go through the not connected connectors
182 for (i = 0; i < resources->count_connectors; i++) {
183 connector = drmModeGetConnector(fd, resources->connectors[i]);
184 if (connector && connector->count_modes > 0)
185 return connector;
186 drmModeFreeConnector(connector);
187 connector = NULL;
188 }
189
190 // we couldn't find a connector
191 return connector;
192}
193
197static int GetPropertyValue(int fdDrm, uint32_t objectID,
198 uint32_t objectType, const char *propName, uint64_t *value)
199{
200 uint32_t i;
201 int found = 0;
202 drmModePropertyPtr Prop;
203 drmModeObjectPropertiesPtr objectProps =
204 drmModeObjectGetProperties(fdDrm, objectID, objectType);
205
206 for (i = 0; i < objectProps->count_props; i++) {
207 if ((Prop = drmModeGetProperty(fdDrm, objectProps->props[i])) == NULL)
208 LOGDEBUG2(L_DRM, "drmdevice: %s: Unable to query property", __FUNCTION__);
209
210 if (strcmp(propName, Prop->name) == 0) {
211 *value = objectProps->prop_values[i];
212 found = 1;
213 }
214
215 drmModeFreeProperty(Prop);
216
217 if (found)
218 break;
219 }
220
221 drmModeFreeObjectProperties(objectProps);
222
223 if (!found) {
224 LOGDEBUG2(L_DRM, "drmdevice: %s: Unable to find value for property \'%s\'.", __FUNCTION__, propName);
225 return -1;
226 }
227
228 return 0;
229}
230
237{
238 drmModeRes *resources;
239 drmModeConnector *connector;
240 drmModeEncoder *encoder = NULL;
241 drmModeModeInfo *drmmode = NULL;
242 drmModePlane *plane;
243 drmModePlaneRes *planeRes;
244 int i;
245 uint32_t j, k;
246
247 // find a drm device
248 m_fdDrm = FindDrmDevice(&resources);
249 if (m_fdDrm < 0) {
250 LOGERROR("drmdevice: %s: Could not open device!", __FUNCTION__);
251 return -1;
252 }
253
254 LOGDEBUG2(L_DRM, "drmdevice: %s: fd: %d DRM have %i connectors, %i crtcs, %i encoders", __FUNCTION__,
255 m_fdDrm, resources->count_connectors, resources->count_crtcs,
256 resources->count_encoders);
257
258 // find a connector
259 connector = FindDrmConnector(m_fdDrm, resources);
260 if (!connector) {
261 LOGERROR("drmdevice: %s: cannot retrieve DRM connector (%d): %m", __FUNCTION__, errno);
262 return -errno;
263 }
264 m_connectorId = connector->connector_id;
265
266 // find a user requested mode
268 for (i = 0; i < connector->count_modes; i++) {
269 drmModeModeInfo *current_mode = &connector->modes[i];
270 if(current_mode->hdisplay == m_userReqDisplayWidth && current_mode->vdisplay == m_userReqDisplayHeight &&
271 current_mode->vrefresh == m_userReqDisplayRefreshRate && !(current_mode->flags & DRM_MODE_FLAG_INTERLACE)) {
272 drmmode = current_mode;
273 LOGDEBUG2(L_DRM, "drmdevice: %s: Use user requested mode: %dx%d@%d", __FUNCTION__, drmmode->hdisplay, drmmode->vdisplay, drmmode->vrefresh);
274 break;
275 }
276 }
277 if (!drmmode)
278 LOGWARNING("drmdevice: %s: User requested mode not found, try default modes", __FUNCTION__);
279 }
280
281 uint32_t preferred_hz[3] = {50, 60, 0};
282
283 // find the highest resolution mode with 50, 60 or any refresh rate
284 if (!drmmode) {
285 j = 0;
286 int width;
287 while (!drmmode && preferred_hz[j]) {
288 for (i = 0, width = 0; i < connector->count_modes; i++) {
289 drmModeModeInfo *current_mode = &connector->modes[i];
290 if (preferred_hz[j] && current_mode->vrefresh != preferred_hz[j])
291 continue;
292
293 int current_width = current_mode->hdisplay;
294 if (current_width > width) {
295 drmmode = current_mode;
296 width = current_width;
297 }
298 }
299 j++;
300 }
301
302 if (drmmode)
303 LOGDEBUG2(L_DRM, "drmdevice: %s: Use mode with the biggest width: %dx%d@%d", __FUNCTION__,
304 drmmode->hdisplay, drmmode->vdisplay, drmmode->vrefresh);
305 }
306
307 if (!drmmode) {
308 LOGERROR("drmdevice: %s: No monitor mode found! Probably no monitor connected, giving up!", __FUNCTION__);
309 return -1;
310 }
311
312 memcpy(&m_drmModeInfo, drmmode, sizeof(drmModeModeInfo));
313
314 // find encoder
315 for (i = 0; i < resources->count_encoders; i++) {
316 encoder = drmModeGetEncoder(m_fdDrm, resources->encoders[i]);
317 if (encoder->encoder_id == connector->encoder_id)
318 break;
319 drmModeFreeEncoder(encoder);
320 encoder = NULL;
321 }
322
323 if (encoder) {
324 m_crtcId = encoder->crtc_id;
325 LOGDEBUG2(L_DRM, "drmdevice: %s: have encoder, m_crtcId %d", __FUNCTION__, m_crtcId);
326 } else {
327 int32_t crtc_id = FindCrtcForConnector(resources, connector);
328 if (crtc_id == -1) {
329 LOGERROR("drmdevice: %s: No crtc found!", __FUNCTION__);
330 return -errno;
331 }
332
333 m_crtcId = crtc_id;
334 LOGDEBUG2(L_DRM, "drmdevice: %s: have no encoder, m_crtcId %d", __FUNCTION__, m_crtcId);
335 }
336
337 for (i = 0; i < resources->count_crtcs; i++) {
338 if (resources->crtcs[i] == m_crtcId) {
339 m_crtcIndex = i;
340 break;
341 }
342 }
343
344 // Calculate the refresh rate. Don't use m_drmModeInfo.vrefresh, because we need the precise value.
345 double refreshRateHz = (double)m_drmModeInfo.clock * 1000.0 / ((double)m_drmModeInfo.htotal * (double)m_drmModeInfo.vtotal);
346
347 m_pRender->SetScreenSize(m_drmModeInfo.hdisplay, m_drmModeInfo.vdisplay, refreshRateHz);
348
349 LOGINFO("DRM Setup: Using Monitor Mode %dx%d@%.2fHz, m_crtcId %d crtc_idx %d",
350 m_drmModeInfo.hdisplay, m_drmModeInfo.vdisplay, refreshRateHz, m_crtcId, m_crtcIndex);
351
352 drmModeFreeConnector(connector);
353
354
355 // find planes
356 if ((planeRes = drmModeGetPlaneResources(m_fdDrm)) == NULL) {
357 LOGERROR("drmdevice: %s: cannot retrieve PlaneResources (%d): %m", __FUNCTION__, errno);
358 return -1;
359 }
360
361 // test and list the local planes
362 cDrmPlane best_primary_video_plane; // NV12 capable primary plane with the lowest plane_id
363 cDrmPlane best_overlay_video_plane; // NV12 capable overlay plane with the lowest plane_id
364 cDrmPlane best_primary_osd_plane; // AR24 capable primary plane with the highest plane_id
365 cDrmPlane best_overlay_osd_plane; // AR24 capable overlay plane with the highest plane_id
366
367 // collect candidates for pip (NV12 overlay plane). The best one is chosen after we decided which
368 // planes are used for video and osd
369 std::vector<cDrmPlane> overlayNV12Candidates;
370
371 for (j = 0; j < planeRes->count_planes; j++) {
372 plane = drmModeGetPlane(m_fdDrm, planeRes->planes[j]);
373
374 if (plane == NULL) {
375 LOGERROR("drmdevice: %s: cannot query DRM-KMS plane %d", __FUNCTION__, j);
376 continue;
377 }
378
379 uint64_t type = 0;
380 uint64_t zpos = 0;
381 char pixelformats[256];
382
383 if (plane->possible_crtcs & (1 << m_crtcIndex)) {
384 if (GetPropertyValue(m_fdDrm, planeRes->planes[j],
385 DRM_MODE_OBJECT_PLANE, "type", &type)) {
386 LOGDEBUG2(L_DRM, "drmdevice: %s: Failed to get property 'type'", __FUNCTION__);
387 }
388 if (GetPropertyValue(m_fdDrm, planeRes->planes[j],
389 DRM_MODE_OBJECT_PLANE, "zpos", &zpos)) {
390 LOGDEBUG2(L_DRM, "drmdevice: %s: Failed to get property 'zpos'", __FUNCTION__);
391 } else {
392 m_useZpos = true;
393 }
394
395 LOGDEBUG2(L_DRM, "drmdevice: %s: %s: id %i possible_crtcs %i", __FUNCTION__,
396 (type == DRM_PLANE_TYPE_PRIMARY) ? "PRIMARY " :
397 (type == DRM_PLANE_TYPE_OVERLAY) ? "OVERLAY " :
398 (type == DRM_PLANE_TYPE_CURSOR) ? "CURSOR " : "UNKNOWN",
399 plane->plane_id, plane->possible_crtcs);
400 strcpy(pixelformats, " ");
401
402 // test pixel format and plane caps
403 for (k = 0; k < plane->count_formats; k++) {
404 if (encoder->possible_crtcs & plane->possible_crtcs) {
405 char tmp[10];
406 switch (plane->formats[k]) {
407 case DRM_FORMAT_NV12:
408 snprintf(tmp, sizeof(tmp), " %4.4s", (char *)&plane->formats[k]);
409 strcat(pixelformats, tmp);
410 if (type == DRM_PLANE_TYPE_PRIMARY && !best_primary_video_plane.GetId()) {
411 best_primary_video_plane.SetId(plane->plane_id);
412 best_primary_video_plane.SetType(type);
413 best_primary_video_plane.SetZpos(zpos);
414 strcat(pixelformats, "! ");
415 }
416 if (type == DRM_PLANE_TYPE_OVERLAY && !best_overlay_video_plane.GetId()) {
417 best_overlay_video_plane.SetId(plane->plane_id);
418 best_overlay_video_plane.SetType(type);
419 best_overlay_video_plane.SetZpos(zpos);
420 strcat(pixelformats, "! ");
421 }
422 // store overlay NV12 plane as a candidate for pip
423 if (type == DRM_PLANE_TYPE_OVERLAY) {
424 cDrmPlane cand;
425 cand.SetId(plane->plane_id);
426 cand.SetType(type);
427 cand.SetZpos(zpos);
428 overlayNV12Candidates.push_back(cand);
429 }
430 break;
431 case DRM_FORMAT_ARGB8888:
432 snprintf(tmp, sizeof(tmp), " %4.4s", (char *)&plane->formats[k]);
433 strcat(pixelformats, tmp);
434 if (type == DRM_PLANE_TYPE_PRIMARY) {
435 best_primary_osd_plane.SetId(plane->plane_id);
436 best_primary_osd_plane.SetType(type);
437 best_primary_osd_plane.SetZpos(zpos);
438 strcat(pixelformats, "! ");
439 }
440 if (type == DRM_PLANE_TYPE_OVERLAY) {
441 best_overlay_osd_plane.SetId(plane->plane_id);
442 best_overlay_osd_plane.SetType(type);
443 best_overlay_osd_plane.SetZpos(zpos);
444 strcat(pixelformats, "! ");
445 }
446 break;
447 default:
448 break;
449 }
450 }
451 }
452 LOGDEBUG2(L_DRM, "drmdevice: %s", __FUNCTION__, pixelformats);
453 }
454 drmModeFreePlane(plane);
455 }
456
457 // See which planes we should use for video and osd
458 if (best_primary_video_plane.GetId() && best_overlay_osd_plane.GetId()) {
459 m_videoPlane.SetId(best_primary_video_plane.GetId());
460 m_videoPlane.SetType(best_primary_video_plane.GetType());
461 m_zposPrimary = best_primary_video_plane.GetZpos();
463 m_osdPlane.SetId(best_overlay_osd_plane.GetId());
464 m_osdPlane.SetType(best_overlay_osd_plane.GetType());
465 m_zposOverlay = best_overlay_osd_plane.GetZpos();
467 } else if (best_overlay_video_plane.GetId() && best_primary_osd_plane.GetId()) {
468 m_videoPlane.SetId(best_overlay_video_plane.GetId());
469 m_videoPlane.SetType(best_overlay_video_plane.GetType());
470 m_zposOverlay = best_overlay_video_plane.GetZpos();
472 m_osdPlane.SetId(best_primary_osd_plane.GetId());
473 m_osdPlane.SetType(best_primary_osd_plane.GetType());
474 m_zposPrimary = best_primary_osd_plane.GetZpos();
476 m_useZpos = true;
477 } else {
478 LOGERROR("drmdevice: %s: No suitable planes found!", __FUNCTION__);
479 return -1;
480 }
481
482 // now pick the best pip plane from the NV12 overlay candidates we collected above
483 cDrmPlane best_overlay_pip_plane;
484 for (cDrmPlane &cand : overlayNV12Candidates) {
485 uint32_t pid = cand.GetId();
486 if (pid == m_videoPlane.GetId() || pid == m_osdPlane.GetId())
487 continue;
488 if (!best_overlay_pip_plane.GetId() || pid > best_overlay_pip_plane.GetId()) {
489 best_overlay_pip_plane = cand;
490 }
491 }
492
493 if (best_overlay_pip_plane.GetId()) {
494 m_pipPlane.SetId(best_overlay_pip_plane.GetId());
495 m_pipPlane.SetType(best_overlay_pip_plane.GetType());
496 m_zposPip = best_overlay_pip_plane.GetZpos();
498 } else {
499 LOGWARNING("drmdevice: %s: No suitable plane for Picture-in-Picture found. PIP will be disabled.", __FUNCTION__);
500 }
501
502 // debug output
503 if (best_primary_video_plane.GetId()) {
504 LOGDEBUG2(L_DRM, "drmdevice: %s: best_primary_video_plane: plane_id %d, type %s, zpos %" PRIu64 "", __FUNCTION__,
505 best_primary_video_plane.GetId(), best_primary_video_plane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY", best_primary_video_plane.GetZpos());
506 }
507 if (best_overlay_video_plane.GetId()) {
508 LOGDEBUG2(L_DRM, "drmdevice: %s: best_overlay_video_plane: plane_id %d, type %s, zpos %" PRIu64 "", __FUNCTION__,
509 best_overlay_video_plane.GetId(), best_overlay_video_plane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY", best_overlay_video_plane.GetZpos());
510 }
511 if (best_primary_osd_plane.GetId()) {
512 LOGDEBUG2(L_DRM, "drmdevice: %s: best_primary_osd_plane: plane_id %d, type %s, zpos %" PRIu64 "", __FUNCTION__,
513 best_primary_osd_plane.GetId(), best_primary_osd_plane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY", best_primary_osd_plane.GetZpos());
514 }
515 if (best_overlay_osd_plane.GetId()) {
516 LOGDEBUG2(L_DRM, "drmdevice: %s: best_overlay_osd_plane: plane_id %d, type %s, zpos %" PRIu64 "", __FUNCTION__,
517 best_overlay_osd_plane.GetId(), best_overlay_osd_plane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY", best_overlay_osd_plane.GetZpos());
518 }
519 if (best_overlay_pip_plane.GetId()) {
520 LOGDEBUG2(L_DRM, "drmdevice: %s: best_overlay_pip_plane: plane_id %d, type %s, zpos %" PRIu64 "", __FUNCTION__,
521 best_overlay_pip_plane.GetId(), best_overlay_pip_plane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY", best_overlay_pip_plane.GetZpos());
522 }
523
524 // fill the plane's properties to speed up SetPropertyRequest later
527
528 if (m_pipPlane.GetId())
530
531 // Check, if we can set z-order (meson and rpi have fixed z-order, which cannot be changed)
533 m_useZpos = false;
534 }
536 m_useZpos = false;
537 }
539 m_useZpos = false;
540 }
541
542 // m_useZpos was set, if video is on OVERLAY, and osd is on PRIMARY
543 // Check if the OVERLAY plane really got a higher zpos than the PRIMARY plane
544 // If not, change their zpos values or hardcode them to
545 // 1 OVERLAY (Video)
546 // 0 PRIMARY (Osd)
548 char str_zpos[256];
549 strcpy(str_zpos, "drmdevice: Init: zpos values are wrong, so ");
551 // is this possible?
552 strcat(str_zpos, "hardcode them to 0 and 1, because they are equal");
553 m_zposPrimary = 0;
554 m_zposOverlay = 1;
555 } else {
556 strcat(str_zpos, "switch them");
557 uint64_t zpos_tmp = m_zposPrimary;
559 m_zposOverlay = zpos_tmp;
560 }
561 LOGDEBUG2(L_DRM, "%s", str_zpos);
562 }
563
564 if (drmSetMaster(m_fdDrm) < 0) {
565 LOGDEBUG2(L_DRM, "drmdevice: Failed to set drm master, try authorize instead: {}", strerror(errno));
566
567 drm_magic_t magic;
568 if (drmGetMagic(m_fdDrm, &magic) < 0)
569 LOGFATAL("drmdevice: Failed to get drm magic: {}", strerror(errno));
570
571 if (drmAuthMagic(m_fdDrm, magic) < 0)
572 LOGFATAL("drmdevice: Failed to authorize drm magic: {}", strerror(errno));
573 }
574
575 drmModeFreePlaneResources(planeRes);
576 drmModeFreeEncoder(encoder);
577 drmModeFreeResources(resources);
578
579 if (m_pipPlane.GetId()) {
580 LOGINFO("DRM setup - CRTC: %i video_plane: %i (%s %" PRIu64 ") osd_plane: %i (%s %" PRIu64 ") pip_plane: %i (%s %" PRIu64 ") m_useZpos: %d",
581 m_crtcId,
583 m_videoPlane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY",
586 m_osdPlane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY",
589 m_pipPlane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY",
591 m_useZpos);
592 } else {
593 LOGINFO("DRM setup - CRTC: %i video_plane: %i (%s %" PRIu64 ") osd_plane: %i (%s %" PRIu64 ") m_useZpos: %d (pip disbled)",
594 m_crtcId,
596 m_videoPlane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY",
599 m_osdPlane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY",
601 m_useZpos);
602 }
603
604#ifdef USE_GLES
605 if (m_pRender->OglOsdDisabled())
606 return 0;
607
608 // init gbm
609 if (InitGbm(m_drmModeInfo.hdisplay, m_drmModeInfo.vdisplay, DRM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING)) {
610 LOGERROR("drmdevice: %s: failed to init gbm device and surface!", __FUNCTION__);
611 return -1;
612 }
613
614 // init egl
615 if (InitEGL()) {
616 LOGERROR("drmdevice: %s: failed to init egl!", __FUNCTION__);
617 return -1;
618 }
619#endif
620
621 return 0;
622}
623
624#ifdef USE_GLES
636int cDrmDevice::InitGbm(int w, int h, uint32_t format, uint64_t modifier)
637{
638 m_pGbmDevice = gbm_create_device(m_fdDrm);
639 if (!m_pGbmDevice) {
640 LOGERROR("drmdevice: %s: failed to create gbm device!", __FUNCTION__);
641 return -1;
642 }
643
644 m_pGbmSurface = gbm_surface_create(m_pGbmDevice, w, h, format, modifier);
645 if (!m_pGbmSurface) {
646 LOGERROR("drmdevice: %s: failed to create %d x %d surface bo", __FUNCTION__, w, h);
647 return -1;
648 }
649
650 return 0;
651}
652
653PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display = NULL;
654PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC get_platform_surface = NULL;
655
656static const EGLint context_attribute_list[] =
657{
658 EGL_CONTEXT_CLIENT_VERSION, 2,
659 EGL_NONE
660};
661
665EGLConfig cDrmDevice::GetEGLConfig(void)
666{
667 EGLint config_attribute_list[] = {
668 EGL_BUFFER_SIZE, 32,
669 EGL_STENCIL_SIZE, EGL_DONT_CARE,
670 EGL_DEPTH_SIZE, EGL_DONT_CARE,
671 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
672 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
673 EGL_NONE
674 };
675 EGLConfig *configs = nullptr;
676 EGLint matched = 0;
677 EGLint count = 0;
678 EGL_CHECK(eglGetConfigs(m_eglDisplay, NULL, 0, &count));
679 if (count < 1)
680 LOGFATAL("drmdevice: %s: no EGL configs to choose from", __FUNCTION__);
681
682 LOGDEBUG2(L_OPENGL, "drmdevice: %s: %d EGL configs found", __FUNCTION__, count);
683
684 configs = (EGLConfig *)malloc(count * sizeof(*configs));
685 if (!configs)
686 LOGFATAL("drmdevice: %s: can't allocate space for EGL configs", __FUNCTION__);
687
688 EGL_CHECK(eglChooseConfig(m_eglDisplay, config_attribute_list, configs, count, &matched));
689 if (!matched) {
690 free(configs);
691 LOGFATAL("drmdevice: %s: no EGL configs with appropriate attributes", __FUNCTION__);
692 }
693
694 LOGDEBUG2(L_OPENGL, "drmdevice: %s: %d appropriate EGL configs found, which match attributes", __FUNCTION__, matched);
695
696 EGLConfig chosen = NULL;
697 for (int i = 0; i < matched; ++i) {
698 EGLint gbm_format;
699 EGL_CHECK(eglGetConfigAttrib(m_eglDisplay, configs[i], EGL_NATIVE_VISUAL_ID, &gbm_format));
700
701 if (gbm_format == GBM_FORMAT_ARGB8888) {
702 chosen = configs[i];
703 break;
704 }
705 }
706
707 free(configs);
708 if (chosen == NULL)
709 LOGFATAL("drmdevice: %s: no matching gbm config found", __FUNCTION__);
710
711 return chosen;
712}
713
720int cDrmDevice::InitEGL(void)
721{
722 EGLint iMajorVersion, iMinorVersion;
723
724 PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
725 assert(get_platform_display != NULL);
726 PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC get_platform_surface = (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
727 assert(get_platform_surface != NULL);
728
729 EGL_CHECK(m_eglDisplay = get_platform_display(EGL_PLATFORM_GBM_KHR, m_pGbmDevice, NULL));
730 if (!m_eglDisplay) {
731 LOGERROR("drmdevice: %s: failed to get eglDisplay", __FUNCTION__);
732 return -1;
733 }
734
735 if (!eglInitialize(m_eglDisplay, &iMajorVersion, &iMinorVersion)) {
736 LOGERROR("drmdevice: %s: eglInitialize failed", __FUNCTION__);
737 return -1;
738 }
739
740 LOGDEBUG2(L_OPENGL, "drmdevice: %s: Using display %p with EGL version %d.%d", __FUNCTION__, m_eglDisplay, iMajorVersion, iMinorVersion);
741 EGL_CHECK(LOGDEBUG2(L_OPENGL, " EGL Version: \"%s\"", eglQueryString(m_eglDisplay, EGL_VERSION)));
742 EGL_CHECK(LOGDEBUG2(L_OPENGL, " EGL Vendor: \"%s\"", eglQueryString(m_eglDisplay, EGL_VENDOR)));
743 EGL_CHECK(LOGDEBUG2(L_OPENGL, " EGL Extensions: \"%s\"", eglQueryString(m_eglDisplay, EGL_EXTENSIONS)));
744 EGL_CHECK(LOGDEBUG2(L_OPENGL, " EGL APIs: \"%s\"", eglQueryString(m_eglDisplay, EGL_CLIENT_APIS)));
745
746 EGLConfig eglConfig = GetEGLConfig();
747
748 EGL_CHECK(eglBindAPI(EGL_OPENGL_ES_API));
749 EGL_CHECK(m_eglContext = eglCreateContext(m_eglDisplay, eglConfig, EGL_NO_CONTEXT, context_attribute_list));
750 if (!m_eglContext) {
751 LOGERROR("drmdevice: %s: failed to create eglContext", __FUNCTION__);
752 return -1;
753 }
754
755 EGL_CHECK(m_eglSurface = get_platform_surface(m_eglDisplay, eglConfig, m_pGbmSurface, NULL));
756 if (m_eglSurface == EGL_NO_SURFACE) {
757 LOGERROR("drmdevice: %s: failed to create eglSurface", __FUNCTION__);
758 return -1;
759 }
760
761 EGLint s_width, s_height;
762 EGL_CHECK(eglQuerySurface(m_eglDisplay, m_eglSurface, EGL_WIDTH, &s_width));
763 EGL_CHECK(eglQuerySurface(m_eglDisplay, m_eglSurface, EGL_HEIGHT, &s_height));
764
765 LOGDEBUG2(L_OPENGL, "drmdevice: %s: GLSurface %p on EGLDisplay %p for %d x %d BO created", __FUNCTION__, m_eglSurface, m_eglDisplay, s_width, s_height);
766
767 m_glInitiated = true;
768 LOGINFO("DRM Setup: EGL context initialized");
769
770 return 0;
771}
772
773static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
774{
775 int drm_fd = gbm_device_get_fd(gbm_bo_get_device(bo));
776 cDrmBuffer *buf = (cDrmBuffer *)data;
777
778 if (buf->Id())
779 drmModeRmFB(drm_fd, buf->Id());
780
781 delete(buf);
782}
783
784__attribute__ ((weak)) union gbm_bo_handle
785gbm_bo_get_handle_for_plane(struct gbm_bo *bo, int plane);
786
787__attribute__ ((weak)) int
788gbm_bo_get_fd(struct gbm_bo *bo);
789
790__attribute__ ((weak)) uint64_t
791gbm_bo_get_modifier(struct gbm_bo *bo);
792
793__attribute__ ((weak)) int
794gbm_bo_get_plane_count(struct gbm_bo *bo);
795
796__attribute__ ((weak)) uint32_t
797gbm_bo_get_stride_for_plane(struct gbm_bo *bo, int plane);
798
799__attribute__ ((weak)) uint32_t
800gbm_bo_get_offset(struct gbm_bo *bo, int plane);
801
809cDrmBuffer *cDrmDevice::GetBufFromBo(struct gbm_bo *bo)
810{
811 cDrmBuffer *buf = (cDrmBuffer *)gbm_bo_get_user_data(bo);
812 uint32_t mod_flags = 0;
813 int ret = -1;
814
815 // the buffer was already allocated
816 if (buf)
817 return buf;
818
819 buf = new cDrmBuffer(m_fdDrm, gbm_bo_get_width(bo), gbm_bo_get_height(bo), gbm_bo_get_format(bo), bo);
820
821 if (gbm_bo_get_handle_for_plane && gbm_bo_get_modifier &&
822 gbm_bo_get_plane_count && gbm_bo_get_stride_for_plane &&
823 gbm_bo_get_offset) {
824 uint64_t modifiers[4] = {0};
825 modifiers[0] = gbm_bo_get_modifier(bo);
826 const int num_planes = gbm_bo_get_plane_count(bo);
827 buf->SetNumPlanes(num_planes);
828 for (int i = 0; i < num_planes; i++) {
829 buf->SetHandle(i, gbm_bo_get_handle_for_plane(bo, i).u32);
830 buf->SetPitch(i, gbm_bo_get_stride_for_plane(bo, i));
831 buf->SetOffset(i, gbm_bo_get_offset(bo, i));
832 modifiers[i] = modifiers[0];
833 buf->SetSize(i, buf->Height() * buf->Pitch(i));
834 LOGDEBUG2(L_DRM, "drmdevice: %s: %d: handle %d pitch %d, offset %d, size %d", __FUNCTION__, i, buf->PrimeHandle(i), buf->Pitch(i), buf->Offset(i), buf->Size(i));
835 }
836 buf->SetNumObjects(1);
837 buf->SetObjectIndex(0, 0);
838 buf->SetDmaBufHandle(gbm_bo_get_fd(bo));
839
840 if (modifiers[0]) {
841 mod_flags = DRM_MODE_FB_MODIFIERS;
842 LOGDEBUG2(L_DRM, "drmdevice: %s: Using modifier %" PRIx64 "", __FUNCTION__, modifiers[0]);
843 }
844
845 uint32_t id;
846 // Add FB
847 ret = drmModeAddFB2WithModifiers(m_fdDrm, buf->Width(), buf->Height(), buf->PixFmt(),
848 buf->PrimeHandle(), buf->Pitch(), buf->Offset(), modifiers, &id, mod_flags);
849 buf->SetId(id);
850 }
851
852 if (ret) {
853 if (mod_flags)
854 LOGDEBUG2(L_DRM, "drmdevice: %s: Modifiers failed!", __FUNCTION__);
855
856 buf->SetNumPlanes(1);
857 uint32_t tmpHandle[4] = { gbm_bo_get_handle(bo).u32, 0, 0, 0};
858 uint32_t tmpStride[4] = { gbm_bo_get_stride(bo), 0, 0, 0};
859 uint32_t tmpSize[4] = { buf->Height() * buf->Width() * buf->Pitch(0), 0, 0, 0};
860 memcpy(buf->PrimeHandle(), tmpHandle, sizeof(tmpHandle));
861 memcpy(buf->Pitch(), tmpStride, sizeof(tmpStride));
862 memcpy(buf->Size(), tmpSize, sizeof(tmpSize));
863 memset(buf->Offset(), 0, 16);
864 buf->SetNumObjects(1);
865 buf->SetObjectIndex(0, 0);
866 buf->SetDmaBufHandle(gbm_bo_get_fd(bo));
867
868 uint32_t id;
869 ret = drmModeAddFB2(m_fdDrm, buf->Width(), buf->Height(), buf->PixFmt(),
870 buf->PrimeHandle(), buf->Pitch(), buf->Offset(), &id, 0);
871 buf->SetId(id);
872 }
873
874 if (ret) {
875 LOGFATAL("drmdevice: %s: cannot create framebuffer (%d): %m", __FUNCTION__, errno);
876 delete buf;
877 return NULL;
878 }
879
880 uint32_t pixFmt = buf->PixFmt();
881 LOGDEBUG2(L_DRM, "drmdevice: %s: New GL buffer %d x %d pix_fmt %4.4s fb_id %d", __FUNCTION__,
882 buf->Width(), buf->Height(), (char *)&pixFmt, buf->Id());
883
884 gbm_bo_set_user_data(bo, buf, drm_fb_destroy_callback);
885 return buf;
886}
887#endif
888
892static int32_t FindCrtcForEncoder(const drmModeRes *resources, const drmModeEncoder *encoder)
893{
894 int i;
895
896 for (i = 0; i < resources->count_crtcs; i++) {
897 const uint32_t crtc_mask = 1 << i;
898 const uint32_t crtc_id = resources->crtcs[i];
899 if (encoder->possible_crtcs & crtc_mask) {
900 return crtc_id;
901 }
902 }
903
904 return -1;
905}
906
910int32_t cDrmDevice::FindCrtcForConnector(const drmModeRes *resources, const drmModeConnector *connector)
911{
912 int i;
913
914 for (i = 0; i < connector->count_encoders; i++) {
915 const uint32_t encoder_id = connector->encoders[i];
916 drmModeEncoder *encoder = drmModeGetEncoder(m_fdDrm, encoder_id);
917
918 if (encoder) {
919 const int32_t crtc_id = FindCrtcForEncoder(resources, encoder);
920 drmModeFreeEncoder(encoder);
921 if (crtc_id != 0) {
922 return crtc_id;
923 }
924 }
925 }
926
927 return -1;
928}
929
934{
935 LOGDEBUG2(L_DRM, "drmdevice: %s: closing fd %d", __FUNCTION__, m_fdDrm);
936 drmDropMaster(m_fdDrm);
937
938 close(m_fdDrm);
939 m_fdDrm = -1;
940}
941
946{
947 return drmModeCreatePropertyBlob(m_fdDrm, &m_drmModeInfo, sizeof(m_drmModeInfo), modeID);
948}
949
953int cDrmDevice::SetPropertyRequest(drmModeAtomicReqPtr ModeReq,
954 uint32_t objectID, uint32_t objectType,
955 const char *propName, uint64_t value)
956{
957 uint32_t i;
958 uint64_t id = 0;
959 drmModePropertyPtr Prop;
960 drmModeObjectPropertiesPtr objectProps =
961 drmModeObjectGetProperties(m_fdDrm, objectID, objectType);
962
963 for (i = 0; i < objectProps->count_props; i++) {
964 if ((Prop = drmModeGetProperty(m_fdDrm, objectProps->props[i])) == NULL)
965 LOGDEBUG2(L_DRM, "drmdevice: %s: Unable to query property", __FUNCTION__);
966
967 if (strcmp(propName, Prop->name) == 0) {
968 id = Prop->prop_id;
969 drmModeFreeProperty(Prop);
970 break;
971 }
972
973 drmModeFreeProperty(Prop);
974 }
975
976 drmModeFreeObjectProperties(objectProps);
977
978 if (id == 0)
979 LOGDEBUG2(L_DRM, "drmdevice: %s Unable to find value for property \'%s\'.", __FUNCTION__, propName);
980
981 return drmModeAtomicAddProperty(ModeReq, objectID, id, value);
982}
983
988{
989 m_drmModeCrtcSaved = drmModeGetCrtc(m_fdDrm, m_crtcId);
990}
991
996{
997 if (m_drmModeCrtcSaved) {
998 drmModeSetCrtc(m_fdDrm, m_drmModeCrtcSaved->crtc_id, m_drmModeCrtcSaved->buffer_id,
1000 drmModeFreeCrtc(m_drmModeCrtcSaved);
1001 }
1002}
1003
1008{
1009 return drmHandleEvent(m_fdDrm, &m_drmEventCtx);
1010}
1011
1016{
1017 memset(&m_drmEventCtx, 0, sizeof(m_drmEventCtx));
1018 m_drmEventCtx.version = 2;
1019}
void SetNumPlanes(int numPlanes)
Definition drmbuffer.h:81
uint32_t Pitch(int idx)
Definition drmbuffer.h:93
uint32_t Width(void)
Definition drmbuffer.h:64
uint32_t Height(void)
Definition drmbuffer.h:66
void SetOffset(int idx, uint32_t offset)
Definition drmbuffer.h:92
void SetId(int id)
Definition drmbuffer.h:78
void SetNumObjects(int numObjects)
Definition drmbuffer.h:84
void SetSize(int idx, uint32_t size)
Definition drmbuffer.h:98
uint32_t Size(int idx)
Definition drmbuffer.h:96
void SetPitch(int idx, uint32_t pitch)
Definition drmbuffer.h:95
uint32_t PrimeHandle(int idx)
Definition drmbuffer.h:87
void SetHandle(int idx, uint32_t handle)
Definition drmbuffer.h:89
int Id(void)
Definition drmbuffer.h:77
uint32_t PixFmt(void)
Definition drmbuffer.h:68
void SetObjectIndex(int idx, uint32_t objIdx)
Definition drmbuffer.h:85
uint32_t Offset(int idx)
Definition drmbuffer.h:90
void SetDmaBufHandle(uint32_t fd)
Definition drmbuffer.h:83
uint32_t m_userReqDisplayRefreshRate
user requested display refresh rate
Definition drmdevice.h:100
int m_userReqDisplayHeight
user requested display height
Definition drmdevice.h:99
drmModeModeInfo m_drmModeInfo
mode info
Definition drmdevice.h:92
int CreatePropertyBlob(uint32_t *)
Creates a property blob.
cDrmPlane m_videoPlane
the video drm plane
Definition drmdevice.h:105
cDrmDevice(cVideoRender *, const char *)
cDrmDevice constructor
Definition drmdevice.cpp:59
int SetPropertyRequest(drmModeAtomicReqPtr, uint32_t, uint32_t, const char *, uint64_t)
Add a property to a request.
int32_t FindCrtcForConnector(const drmModeRes *, const drmModeConnector *)
Finds the CRTC_ID for the given connector.
int HandleEvent(void)
Polls for a drm event.
int m_userReqDisplayWidth
user requested display width
Definition drmdevice.h:98
cDrmPlane m_pipPlane
the pip drm plane
Definition drmdevice.h:108
drmModeCrtc * m_drmModeCrtcSaved
saved CRTC infos
Definition drmdevice.h:95
int m_fdDrm
drm file descriptor
Definition drmdevice.h:90
uint64_t m_zposPip
zpos of pip plane
Definition drmdevice.h:107
void SaveCrtc(void)
Saves information of a CRTC.
~cDrmDevice(void)
cDrmDevice destructor
Definition drmdevice.cpp:69
uint32_t m_connectorId
connector id
Definition drmdevice.h:91
void RestoreCrtc(void)
Restore information of a CRTC.
void Close(void)
Close drm file handle.
uint32_t m_crtcId
current crtc ID
Definition drmdevice.h:93
uint32_t m_crtcIndex
current crtc index
Definition drmdevice.h:94
cVideoRender * m_pRender
pointer to cVideoRender object
Definition drmdevice.h:88
int Init(void)
Initiate the drm device.
uint64_t m_zposPrimary
zpos of primary plane
Definition drmdevice.h:104
drmEventContext m_drmEventCtx
drm event context
Definition drmdevice.h:96
bool m_useZpos
is set, if drm hardware can use zpos
Definition drmdevice.h:102
cDrmPlane m_osdPlane
the osd drm plane
Definition drmdevice.h:106
void InitEvent(void)
Init the event context.
uint64_t m_zposOverlay
zpos of overlay plane
Definition drmdevice.h:103
cDrmPlane - DRM plane class
Definition drmplane.h:29
uint64_t GetType(void)
Definition drmplane.h:47
uint64_t GetZpos(void)
Definition drmplane.h:59
void SetId(uint32_t id)
Definition drmplane.h:46
int HasZpos(int)
Check, if the plane is able to set the zpos property.
Definition drmplane.cpp:191
void FillProperties(int)
Fill the plane properties.
Definition drmplane.cpp:46
void SetZpos(uint64_t zpos)
Definition drmplane.h:60
void SetType(uint64_t type)
Definition drmplane.h:48
uint32_t GetId(void)
Definition drmplane.h:45
cVideoRender - Video render class
void SetScreenSize(int, int, double)
Wrapper to set the screen size in the device.
static int TestCaps(int fd)
Test drm capabilities.
Definition drmdevice.cpp:89
static int FindDrmDevice(drmModeRes **resources)
Find and open a suitable device with the wanted capabilities.
static int32_t FindCrtcForEncoder(const drmModeRes *resources, const drmModeEncoder *encoder)
Finds the CRTC_ID for the given encoder.
static int get_resources(int fd, drmModeRes **resources)
Definition drmdevice.cpp:74
#define MAX_DRM_DEVICES
static int GetPropertyValue(int fdDrm, uint32_t objectID, uint32_t objectType, const char *propName, uint64_t *value)
Gets a property value.
static drmModeConnector * FindDrmConnector(int fd, drmModeRes *resources)
Find a suitable connector, preferably a connected one.
DRM device header file.
DRM plane class header.
Logger class header file.
#define LOGDEBUG2
Definition logger.h:45
#define LOGERROR
Definition logger.h:41
#define L_DRM
Definition logger.h:57
#define LOGWARNING
Definition logger.h:42
#define L_OPENGL
Definition logger.h:62
#define LOGINFO
Definition logger.h:43
#define LOGFATAL
Logger macros.
Definition logger.h:40
Rendering class header file.