vdr-plugin-softhddevice-drm-gles 1.6.7
drmdevice.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2
16#include <cerrno>
17#include <cstdint>
18#include <cstdio>
19#include <cstring>
20#include <cinttypes>
21#include <string>
22#include <vector>
23
24#include <fcntl.h>
25#include <unistd.h>
26
27#ifdef USE_GLES
28#include <assert.h>
29#include <EGL/egl.h>
30#include <EGL/eglext.h>
31#endif
32
33#include <drm_fourcc.h>
34#include <xf86drm.h>
35#include <xf86drmMode.h>
36
37#include "config.h"
38#include "drmdevice.h"
39#include "drmplane.h"
40#include "logger.h"
41#include "videorender.h"
42
50 : m_pRender(render),
51 m_pConfig(config),
52 m_userDrmDevice(m_pConfig->ConfigDrmDevice),
53 m_userDrmConnector(m_pConfig->ConfigDrmConnector)
54{
55 sDrmMode *drmMode = nullptr;
58 else if (m_pConfig->UserSetDrmMode.width > 0)
60
61 if (drmMode) {
64 m_userReqDisplayRefreshRate = drmMode->refreshRateHz;
66 LOGDEBUG("%s requested display mode: %dx%d@%.2f%s", __FUNCTION__,
68 }
69
72}
73
75{
76 LOGDEBUG2(L_DRM, "drmdevice: %s", __FUNCTION__);
77}
78
81{
83 if (*resources == NULL) {
84 LOGERROR("drmdevice: %s: cannot retrieve DRM resources (%d): %m", __FUNCTION__, errno);
85 return -1;
86 }
87 return 0;
88}
89
96static int TestCaps(int fd)
97{
99
100 if (drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &test) < 0 || test == 0)
101 return 1;
102
104 return 1;
105
107 return 1;
108
109 if (drmGetCap(fd, DRM_CAP_PRIME, &test) < 0)
110 return 1;
111
113 return 1;
114
116 return 1;
117
118 return 0;
119}
120
131static int OpenDrmDevice(const char* device, drmModeRes **resources)
132{
133 int fd = -1;
134
135 if ((fd = open(device, O_RDWR)) < 0)
136 return -1;
137
139 close(fd);
140 return -1;
141 }
142
143 return fd;
144}
145
154{
155 int MAX_DRM_DEVICES = 64;
156
158 int num_devices, fd = -1;
159
161 if (num_devices < 0) {
162 LOGERROR("drmdevice: %s: drmGetDevices2 failed: %s", __FUNCTION__, strerror(-num_devices));
163 return fd;
164 }
165
166 for (int i = 0; i < num_devices && fd < 0; i++) {
167 drmDevicePtr device = devices[i];
168 int ret;
169
170 if (!(device->available_nodes & (1 << DRM_NODE_PRIMARY)))
171 continue;
172 fd = open(device->nodes[DRM_NODE_PRIMARY], O_RDWR);
173 if (fd < 0)
174 continue;
175
176 if (TestCaps(fd)) {
177 close(fd);
178 fd = -1;
179 continue;
180 }
181
183 if (!ret)
184 break;
185 close(fd);
186 fd = -1;
187 }
189
190 if (fd < 0)
191 LOGERROR("drmdevice: %s: no drm device found!", __FUNCTION__);
192
193 return fd;
194}
195
200{
201 const char *typeName = drmModeGetConnectorTypeName(connector->connector_type);
202
203 return (typeName ? typeName : std::string("Unknown")) + "-" + std::to_string(connector->connector_type_id);
204}
205
217{
219 int i;
220
221 // search for the user requested connector (can be unconnected)
222 for (i = 0; i < resources->count_connectors && userRequestedConnector; i++) {
223 connector = drmModeGetConnector(fd, resources->connectors[i]);
224 if (connector && connector->count_modes > 0) {
226 return connector;
227 }
229 connector = NULL;
230 }
231
233 LOGWARNING("drmdevice: %s: cannot open user requested DRM connector %s, try others", __FUNCTION__, userRequestedConnector);
234
235 // search for a connected connector
236 for (i = 0; i < resources->count_connectors; i++) {
237 connector = drmModeGetConnector(fd, resources->connectors[i]);
238 if (connector && connector->connection == DRM_MODE_CONNECTED && connector->count_modes > 0)
239 return connector;
241 connector = NULL;
242 }
243
244 // search for an unconnected connector, but with available modes
245 // this is a workaround for RPI: in case we don't have a monitor connected
246 // we can load an edid file at boot time, where the available modes are listed.
247 // To bring softhddevice up, we also have to go through the not connected connectors
248 for (i = 0; i < resources->count_connectors; i++) {
249 connector = drmModeGetConnector(fd, resources->connectors[i]);
250 if (connector && connector->count_modes > 0)
251 return connector;
253 connector = NULL;
254 }
255
256 // we couldn't find a connector
257 return connector;
258}
259
267{
268 return (double)modeInfo->clock * 1000.0 / ((double)modeInfo->htotal * (double)modeInfo->vtotal);
269}
270
274static inline bool InterlacedMode(uint32_t flags)
275{
276 return (flags & DRM_MODE_FLAG_INTERLACE) != 0;
277}
278
282static inline bool AlmostEqual(double a, double b)
283{
284 return std::abs(a - b) < 0.001;
285}
286
301static bool Contains(drmModeModeInfo *mode, std::vector<sDrmMode> modes)
302{
303 for (size_t i = 0; i < modes.size(); i++) {
304 bool interlaced = InterlacedMode(mode->flags);
305
306 if (mode->hdisplay != modes[i].width ||
307 mode->vdisplay != modes[i].height ||
308 !AlmostEqual(GetRefreshRateHz(mode), modes[i].refreshRateHz) ||
309 interlaced != modes[i].interlaced) {
310
311 continue;
312 }
313
314 return true;
315 }
316
317 return false;
318}
319
324{
325 std::vector<sDrmMode> modes = m_pConfig->CollectedDrmModes;
326
327 // sometimes the stream has no framerate, use 50.00 as default
328 double refreshRateHz = mode->refreshRateHz ? mode->refreshRateHz : 50.00;
329 if (!mode->refreshRateHz && mode->interlaced)
330 refreshRateHz /= 2;
331
332 for (size_t i = 0; i < modes.size(); i++) {
333 if (mode->width != modes[i].width ||
334 mode->height != modes[i].height ||
335 !AlmostEqual(refreshRateHz, modes[i].refreshRateHz) ||
336 mode->interlaced != modes[i].interlaced) {
337
338 continue;
339 }
340
341 return true;
342 }
343
344 LOGDEBUG2(L_DRM, "drmdevice: %s: can't handle mode %dx%d@%.2f%s", __FUNCTION__,
345 mode->width, mode->height, mode->refreshRateHz,
346 mode->interlaced ? "i" : "");
347
348 return false;
349}
350
367{
368 drmModeRes *resources = nullptr;
370 drmModeEncoder *encoder = nullptr;
373 int i;
374 uint32_t j, k;
375
376 // first try to open the user given drm device
377 if (m_userDrmDevice) {
378 LOGDEBUG2(L_DRM, "drmdevice: %s: Try open user requested device %s", __FUNCTION__, m_userDrmDevice);
380 }
381
382 // if manually set device failed, try to find a drm device
383 if (m_fdDrm < 0) {
384 if (m_userDrmDevice)
385 LOGWARNING("drmdevice: %s: Could not open user requested device %s, try other devices!", __FUNCTION__, m_userDrmDevice);
387 }
388
389 if (m_fdDrm < 0) {
390 LOGERROR("drmdevice: %s: Could not open device!", __FUNCTION__);
391 return -1;
392 }
393
394 LOGDEBUG2(L_DRM, "drmdevice: %s: fd: %d DRM have %i connectors, %i crtcs, %i encoders", __FUNCTION__,
395 m_fdDrm, resources->count_connectors, resources->count_crtcs,
396 resources->count_encoders);
397
398 // find a connector
400 if (!connector) {
401 LOGERROR("drmdevice: %s: cannot retrieve DRM connector (%d): %m", __FUNCTION__, errno);
402 return -errno;
403 }
404 m_connectorId = connector->connector_id;
406 bool connected = connector->connection == DRM_MODE_CONNECTED;
407
408 // fill config with available connector modes for later selection from setup menu
410 for (i = 0; i < connector->count_modes; i++) {
412 std::vector<sDrmMode> drmModeWhitelist(DrmModeWhitelist.begin(), DrmModeWhitelist.end());
414 continue;
416 continue;
417 m_pConfig->CollectedDrmModes.push_back({current_mode->hdisplay,
418 current_mode->vdisplay,
421 LOGDEBUG2(L_DRM, "drmdevice: %s: adding display mode %dx%d@%.2f%s", __FUNCTION__,
423 InterlacedMode(current_mode->flags) ? "i" : "");
424 }
425
426 // find connector mode
427 if (FindMode())
428 return -1;
429
431
432 // find encoder
433 for (i = 0; i < resources->count_encoders; i++) {
435 if (encoder->encoder_id == connector->encoder_id)
436 break;
438 encoder = NULL;
439 }
440
441 if (encoder) {
442 m_crtcId = encoder->crtc_id;
443 LOGDEBUG2(L_DRM, "drmdevice: %s: have encoder, m_crtcId %d", __FUNCTION__, m_crtcId);
444 } else {
446 if (crtc_id == -1) {
447 LOGERROR("drmdevice: %s: No crtc found!", __FUNCTION__);
448 return -errno;
449 }
450
452 LOGDEBUG2(L_DRM, "drmdevice: %s: have no encoder, m_crtcId %d", __FUNCTION__, m_crtcId);
453 }
454
455 for (i = 0; i < resources->count_crtcs; i++) {
456 if (resources->crtcs[i] == m_crtcId) {
457 m_crtcIndex = i;
458 break;
459 }
460 }
461
463 if (m_hdrMetadata != 0)
464 LOGDEBUG2(L_DRM, "drmdevice: %s: HDR output metadata ID %d in connector %d", __FUNCTION__, m_hdrMetadata, m_connectorId);
465
466 LOGINFO("DRM Setup: Using Monitor Mode %dx%d@%.2fHz on %s (%s), m_crtcId %d crtc_idx %d",
467 m_drmModeInfo.hdisplay, m_drmModeInfo.vdisplay, GetRefreshRateHz(&m_drmModeInfo), m_connectorName.c_str(), connected ? "connected" : "not connected", m_crtcId, m_crtcIndex);
468
470
471 // find planes
473 LOGERROR("drmdevice: %s: cannot retrieve PlaneResources (%d): %m", __FUNCTION__, errno);
474 return -1;
475 }
476
477 // test and list the local planes
478 cDrmPlane best_primary_video_plane; // NV12 capable primary plane with the lowest plane_id
479 cDrmPlane best_overlay_video_plane; // NV12 capable overlay plane with the lowest plane_id
480 cDrmPlane best_primary_osd_plane; // AR24 capable primary plane with the highest plane_id
481 cDrmPlane best_overlay_osd_plane; // AR24 capable overlay plane with the highest plane_id
482
483 // collect candidates for pip (NV12 overlay plane). The best one is chosen after we decided which
484 // planes are used for video and osd
485 std::vector<cDrmPlane> overlayNV12Candidates;
486
487 for (j = 0; j < planeRes->count_planes; j++) {
489
490 if (plane == NULL) {
491 LOGERROR("drmdevice: %s: cannot query DRM-KMS plane %d", __FUNCTION__, j);
492 continue;
493 }
494
495 uint64_t type = 0;
496 uint64_t zpos = 0;
497 char pixelformats[256];
498
499 if (plane->possible_crtcs & (1 << m_crtcIndex)) {
500 if (GetPropertyValue(planeRes->planes[j], DRM_MODE_OBJECT_PLANE, "type", &type)) {
501 LOGDEBUG2(L_DRM, "drmdevice: %s: Failed to get property 'type'", __FUNCTION__);
502 }
503 if (GetPropertyValue(planeRes->planes[j], DRM_MODE_OBJECT_PLANE, "zpos", &zpos)) {
504 LOGDEBUG2(L_DRM, "drmdevice: %s: Failed to get property 'zpos'", __FUNCTION__);
505 } else {
506 m_useZpos = true;
507 }
508
509 LOGDEBUG2(L_DRM, "drmdevice: %s: %s: id %i possible_crtcs %i", __FUNCTION__,
510 (type == DRM_PLANE_TYPE_PRIMARY) ? "PRIMARY " :
511 (type == DRM_PLANE_TYPE_OVERLAY) ? "OVERLAY " :
512 (type == DRM_PLANE_TYPE_CURSOR) ? "CURSOR " : "UNKNOWN",
513 plane->plane_id, plane->possible_crtcs);
514 strcpy(pixelformats, " ");
515
516 // test pixel format and plane caps
517 for (k = 0; k < plane->count_formats; k++) {
518 if (encoder->possible_crtcs & plane->possible_crtcs) {
519 char tmp[10];
520 switch (plane->formats[k]) {
521 case DRM_FORMAT_NV12:
522 snprintf(tmp, sizeof(tmp), " %4.4s", (char *)&plane->formats[k]);
524 if (type == DRM_PLANE_TYPE_PRIMARY && !best_primary_video_plane.GetId()) {
525 best_primary_video_plane.SetId(plane->plane_id);
526 best_primary_video_plane.SetType(type);
528 strcat(pixelformats, "! ");
529 }
530 if (type == DRM_PLANE_TYPE_OVERLAY && !best_overlay_video_plane.GetId()) {
531 best_overlay_video_plane.SetId(plane->plane_id);
532 best_overlay_video_plane.SetType(type);
534 strcat(pixelformats, "! ");
535 }
536 // store overlay NV12 plane as a candidate for pip
537 if (type == DRM_PLANE_TYPE_OVERLAY) {
539 cand.SetId(plane->plane_id);
540 cand.SetType(type);
541 cand.SetZpos(zpos);
542 overlayNV12Candidates.push_back(cand);
543 }
544 break;
546 snprintf(tmp, sizeof(tmp), " %4.4s", (char *)&plane->formats[k]);
548 if (type == DRM_PLANE_TYPE_PRIMARY) {
549 best_primary_osd_plane.SetId(plane->plane_id);
550 best_primary_osd_plane.SetType(type);
552 strcat(pixelformats, "! ");
553 }
554 if (type == DRM_PLANE_TYPE_OVERLAY) {
555 best_overlay_osd_plane.SetId(plane->plane_id);
556 best_overlay_osd_plane.SetType(type);
558 strcat(pixelformats, "! ");
559 }
560 break;
561 default:
562 break;
563 }
564 }
565 }
566 LOGDEBUG2(L_DRM, "drmdevice: %s", __FUNCTION__, pixelformats);
567 }
569 }
570
571 // See which planes we should use for video and osd
572 if (best_primary_video_plane.GetId() && best_overlay_osd_plane.GetId()) {
581 } else if (best_overlay_video_plane.GetId() && best_primary_osd_plane.GetId()) {
590 m_useZpos = true;
591 } else {
592 LOGERROR("drmdevice: %s: No suitable planes found!", __FUNCTION__);
593 return -1;
594 }
595
596 // now pick the best pip plane from the NV12 overlay candidates we collected above
599 uint32_t pid = cand.GetId();
600 if (pid == m_videoPlane.GetId() || pid == m_osdPlane.GetId())
601 continue;
602 if (!best_overlay_pip_plane.GetId() || pid > best_overlay_pip_plane.GetId()) {
604 }
605 }
606
607 if (best_overlay_pip_plane.GetId()) {
612 } else {
613 LOGWARNING("drmdevice: %s: No suitable plane for Picture-in-Picture found. PIP will be disabled.", __FUNCTION__);
614 }
615
616 // debug output
617 if (best_primary_video_plane.GetId()) {
618 LOGDEBUG2(L_DRM, "drmdevice: %s: best_primary_video_plane: plane_id %d, type %s, zpos %" PRIu64 "", __FUNCTION__,
619 best_primary_video_plane.GetId(), best_primary_video_plane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY", best_primary_video_plane.GetZpos());
620 }
621 if (best_overlay_video_plane.GetId()) {
622 LOGDEBUG2(L_DRM, "drmdevice: %s: best_overlay_video_plane: plane_id %d, type %s, zpos %" PRIu64 "", __FUNCTION__,
623 best_overlay_video_plane.GetId(), best_overlay_video_plane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY", best_overlay_video_plane.GetZpos());
624 }
625 if (best_primary_osd_plane.GetId()) {
626 LOGDEBUG2(L_DRM, "drmdevice: %s: best_primary_osd_plane: plane_id %d, type %s, zpos %" PRIu64 "", __FUNCTION__,
627 best_primary_osd_plane.GetId(), best_primary_osd_plane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY", best_primary_osd_plane.GetZpos());
628 }
629 if (best_overlay_osd_plane.GetId()) {
630 LOGDEBUG2(L_DRM, "drmdevice: %s: best_overlay_osd_plane: plane_id %d, type %s, zpos %" PRIu64 "", __FUNCTION__,
631 best_overlay_osd_plane.GetId(), best_overlay_osd_plane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY", best_overlay_osd_plane.GetZpos());
632 }
633 if (best_overlay_pip_plane.GetId()) {
634 LOGDEBUG2(L_DRM, "drmdevice: %s: best_overlay_pip_plane: plane_id %d, type %s, zpos %" PRIu64 "", __FUNCTION__,
635 best_overlay_pip_plane.GetId(), best_overlay_pip_plane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY", best_overlay_pip_plane.GetZpos());
636 }
637
638 // fill the plane's properties to speed up SetPropertyRequest later
641
642 if (m_pipPlane.GetId())
644
645 // Check, if we can set z-order (meson and rpi have fixed z-order, which cannot be changed)
647 m_useZpos = false;
648 }
650 m_useZpos = false;
651 }
653 m_useZpos = false;
654 }
655
656 // m_useZpos was set, if video is on OVERLAY, and osd is on PRIMARY
657 // Check if the OVERLAY plane really got a higher zpos than the PRIMARY plane
658 // If not, change their zpos values or hardcode them to
659 // 1 OVERLAY (Video)
660 // 0 PRIMARY (Osd)
662 char str_zpos[256];
663 strcpy(str_zpos, "drmdevice: Init: zpos values are wrong, so ");
665 // is this possible?
666 strcat(str_zpos, "hardcode them to 0 and 1, because they are equal");
667 m_zposPrimary = 0;
668 m_zposOverlay = 1;
669 } else {
670 strcat(str_zpos, "switch them");
674 }
675 LOGDEBUG2(L_DRM, "%s", str_zpos);
676 }
677
678 if (drmSetMaster(m_fdDrm) < 0) {
679 LOGDEBUG2(L_DRM, "drmdevice: Failed to set drm master, try authorize instead: {}", strerror(errno));
680
682 if (drmGetMagic(m_fdDrm, &magic) < 0)
683 LOGFATAL("drmdevice: Failed to get drm magic: {}", strerror(errno));
684
685 if (drmAuthMagic(m_fdDrm, magic) < 0)
686 LOGFATAL("drmdevice: Failed to authorize drm magic: {}", strerror(errno));
687 }
688
692
693 if (m_pipPlane.GetId()) {
694 LOGINFO("DRM setup - CRTC: %i video_plane: %i (%s %" PRIu64 ") osd_plane: %i (%s %" PRIu64 ") pip_plane: %i (%s %" PRIu64 ") m_useZpos: %d",
695 m_crtcId,
697 m_videoPlane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY",
700 m_osdPlane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY",
703 m_pipPlane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY",
705 m_useZpos);
706 } else {
707 LOGINFO("DRM setup - CRTC: %i video_plane: %i (%s %" PRIu64 ") osd_plane: %i (%s %" PRIu64 ") m_useZpos: %d (pip disbled)",
708 m_crtcId,
710 m_videoPlane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY",
713 m_osdPlane.GetType() == DRM_PLANE_TYPE_PRIMARY ? "PRIMARY" : "OVERLAY",
715 m_useZpos);
716 }
717
718 return 0;
719}
720
731{
733 drmModeModeInfo *drmmode = nullptr;
734
735 // find the user requested mode
736 if (m_userReqDisplayWidth > 0) {
737 for (int i = 0; i < connector->count_modes; i++) {
739 if(current_mode->hdisplay == m_userReqDisplayWidth &&
743
745 LOGDEBUG2(L_DRM, "drmdevice: %s: Use user requested mode: %dx%d@%.2f%s", __FUNCTION__,
746 drmmode->hdisplay, drmmode->vdisplay, GetRefreshRateHz(drmmode), InterlacedMode(drmmode->flags) ? "i" : "");
747 break;
748 }
749 }
750 if (!drmmode)
751 LOGWARNING("drmdevice: %s: User requested mode not found, try default modes", __FUNCTION__);
752 }
753
754 double preferredHz[3] = {50.0, 60.0, 0.0};
755
756 // find the highest resolution (progressive) mode with 50, 60 or any refresh rate
757 if (!drmmode) {
758 int j = 0;
759 while (!drmmode && preferredHz[j]) {
760 for (int i = 0, width = 0; i < connector->count_modes; i++) {
762 if (InterlacedMode(current_mode->flags))
763 continue;
764 if (preferredHz[j] && std::round(GetRefreshRateHz(current_mode) * 100.0) / 100.0 != preferredHz[j])
765 continue;
766
767 int current_width = current_mode->hdisplay;
768 if (current_width > width) {
770 width = current_width;
771 }
772 }
773 j++;
774 }
775
776 if (drmmode)
777 LOGDEBUG2(L_DRM, "drmdevice: %s: Use mode with the biggest width: %dx%d@%.2f", __FUNCTION__,
778 drmmode->hdisplay, drmmode->vdisplay, GetRefreshRateHz(drmmode));
779 }
780
782
783 if (!drmmode) {
784 LOGERROR("drmdevice: %s: No monitor mode found! Probably no monitor connected, giving up!", __FUNCTION__);
785 return -1;
786 }
787
789 drmmode->hdisplay,
790 drmmode->vdisplay,
792 InterlacedMode(drmmode->flags)
793 };
794
797
799
801
802 return 0;
803}
804
817{
818 sDrmMode *drmMode = nullptr;
819
822 else if (m_pConfig->UserSetDrmMode.width > 0)
824
825 if (drmMode) {
828 m_userReqDisplayRefreshRate = drmMode->refreshRateHz;
830 LOGDEBUG("%s requested display mode: %dx%d@%.2f%s", __FUNCTION__,
832 }
833
834 return FindMode();
835}
836
837#ifdef USE_GLES
845{
846 int w = OsdWidth();
847 int h = OsdHeight();
850
852 if (!m_pGbmDevice) {
853 LOGERROR("drmdevice: %s: failed to create gbm device!", __FUNCTION__);
854 return -1;
855 }
856
858 if (!m_pGbmSurface) {
859 LOGERROR("drmdevice: %s: failed to create %d x %d surface bo", __FUNCTION__, w, h);
860 return -1;
861 }
862
863 return 0;
864}
865
868
871{
874};
875
882{
884 EGL_BUFFER_SIZE, 32,
890 };
891 EGLConfig *configs = nullptr;
892 EGLint matched = 0;
893 EGLint count = 0;
895 if (count < 1)
896 LOGFATAL("drmdevice: %s: no EGL configs to choose from", __FUNCTION__);
897
898 LOGDEBUG2(L_OPENGL, "drmdevice: %s: %d EGL configs found", __FUNCTION__, count);
899
900 configs = (EGLConfig *)malloc(count * sizeof(*configs));
901 if (!configs)
902 LOGFATAL("drmdevice: %s: can't allocate space for EGL configs", __FUNCTION__);
903
905 if (!matched) {
906 free(configs);
907 LOGFATAL("drmdevice: %s: no EGL configs with appropriate attributes", __FUNCTION__);
908 }
909
910 LOGDEBUG2(L_OPENGL, "drmdevice: %s: %d appropriate EGL configs found, which match attributes", __FUNCTION__, matched);
911
913 for (int i = 0; i < matched; ++i) {
916
918 chosen = configs[i];
919 break;
920 }
921 }
922
923 free(configs);
924 if (chosen == NULL)
925 LOGFATAL("drmdevice: %s: no matching gbm config found", __FUNCTION__);
926
927 return chosen;
928}
929
937{
939
944
946 if (!m_eglDisplay) {
947 LOGERROR("drmdevice: %s: failed to get eglDisplay", __FUNCTION__);
948 return -1;
949 }
950
952 LOGERROR("drmdevice: %s: eglInitialize failed", __FUNCTION__);
953 return -1;
954 }
955
956 LOGDEBUG2(L_OPENGL, "drmdevice: %s: Using display %p with EGL version %d.%d", __FUNCTION__, m_eglDisplay, iMajorVersion, iMinorVersion);
961
963
966 if (!m_eglContext) {
967 LOGERROR("drmdevice: %s: failed to create eglContext", __FUNCTION__);
968 return -1;
969 }
970
973 LOGERROR("drmdevice: %s: failed to create eglSurface", __FUNCTION__);
974 return -1;
975 }
976
980
981 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);
982
983 m_glInitiated = true;
984 LOGINFO("DRM Setup: EGL context initialized");
985
986 return 0;
987}
988
994static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
995{
997 cDrmBuffer *buf = (cDrmBuffer *)data;
998
999 if (buf->Id())
1000 drmModeRmFB(drm_fd, buf->Id());
1001
1002 delete(buf);
1003}
1004
1007
1008__attribute__ ((weak)) int
1009gbm_bo_get_fd(struct gbm_bo *bo);
1010
1013
1014__attribute__ ((weak)) int
1016
1019
1021gbm_bo_get_offset(struct gbm_bo *bo, int plane);
1022
1031{
1033 uint32_t mod_flags = 0;
1034 int ret = -1;
1035
1036 // the buffer was already allocated
1037 if (buf)
1038 return buf;
1039
1041
1045 uint64_t modifiers[4] = {0};
1047 const int num_planes = gbm_bo_get_plane_count(bo);
1048 buf->SetNumPlanes(num_planes);
1049 for (int i = 0; i < num_planes; i++) {
1050 buf->SetHandle(i, gbm_bo_get_handle_for_plane(bo, i).u32);
1051 buf->SetPitch(i, gbm_bo_get_stride_for_plane(bo, i));
1052 buf->SetOffset(i, gbm_bo_get_offset(bo, i));
1053 modifiers[i] = modifiers[0];
1054 buf->SetSize(i, buf->Height() * buf->Pitch(i));
1055 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));
1056 }
1057 buf->SetNumObjects(1);
1058 buf->SetObjectIndex(0, 0);
1059 buf->SetDmaBufHandle(gbm_bo_get_fd(bo));
1060
1061 if (modifiers[0]) {
1063 LOGDEBUG2(L_DRM, "drmdevice: %s: Using modifier %" PRIx64 "", __FUNCTION__, modifiers[0]);
1064 }
1065
1066 uint32_t id;
1067 // Add FB
1068 ret = drmModeAddFB2WithModifiers(m_fdDrm, buf->Width(), buf->Height(), buf->PixFmt(),
1069 buf->PrimeHandle(), buf->Pitch(), buf->Offset(), modifiers, &id, mod_flags);
1070 buf->SetId(id);
1071 }
1072
1073 if (ret) {
1074 if (mod_flags)
1075 LOGDEBUG2(L_DRM, "drmdevice: %s: Modifiers failed!", __FUNCTION__);
1076
1077 buf->SetNumPlanes(1);
1078 uint32_t tmpHandle[4] = { gbm_bo_get_handle(bo).u32, 0, 0, 0};
1079 uint32_t tmpStride[4] = { gbm_bo_get_stride(bo), 0, 0, 0};
1080 uint32_t tmpSize[4] = { buf->Height() * buf->Width() * buf->Pitch(0), 0, 0, 0};
1081 memcpy(buf->PrimeHandle(), tmpHandle, sizeof(tmpHandle));
1082 memcpy(buf->Pitch(), tmpStride, sizeof(tmpStride));
1083 memcpy(buf->Size(), tmpSize, sizeof(tmpSize));
1084 memset(buf->Offset(), 0, 16);
1085 buf->SetNumObjects(1);
1086 buf->SetObjectIndex(0, 0);
1087 buf->SetDmaBufHandle(gbm_bo_get_fd(bo));
1088
1089 uint32_t id;
1090 ret = drmModeAddFB2(m_fdDrm, buf->Width(), buf->Height(), buf->PixFmt(),
1091 buf->PrimeHandle(), buf->Pitch(), buf->Offset(), &id, 0);
1092 buf->SetId(id);
1093 }
1094
1095 if (ret) {
1096 LOGFATAL("drmdevice: %s: cannot create framebuffer (%d): %m", __FUNCTION__, errno);
1097 delete buf;
1098 return NULL;
1099 }
1100
1101 uint32_t pixFmt = buf->PixFmt();
1102 LOGDEBUG2(L_DRM, "drmdevice: %s: New GL buffer %d x %d pix_fmt %4.4s fb_id %d", __FUNCTION__,
1103 buf->Width(), buf->Height(), (char *)&pixFmt, buf->Id());
1104
1106 return buf;
1107}
1108#endif
1109
1121{
1122 int i;
1123
1124 for (i = 0; i < resources->count_crtcs; i++) {
1125 const uint32_t crtc_mask = 1 << i;
1126 const uint32_t crtc_id = resources->crtcs[i];
1127 if (encoder->possible_crtcs & crtc_mask) {
1128 return crtc_id;
1129 }
1130 }
1131
1132 return -1;
1133}
1134
1144{
1145 int i;
1146
1147 for (i = 0; i < connector->count_encoders; i++) {
1148 const uint32_t encoder_id = connector->encoders[i];
1150
1151 if (encoder) {
1154 if (crtc_id != 0) {
1155 return crtc_id;
1156 }
1157 }
1158 }
1159
1160 return -1;
1161}
1162
1167{
1168 LOGDEBUG2(L_DRM, "drmdevice: %s: closing fd %d", __FUNCTION__, m_fdDrm);
1170
1171 close(m_fdDrm);
1172 m_fdDrm = -1;
1173}
1174
1192
1206 const char *propName, uint64_t value)
1207{
1208 uint32_t i;
1209 uint64_t id = 0;
1213
1214 for (i = 0; i < objectProps->count_props; i++) {
1215 if ((Prop = drmModeGetProperty(m_fdDrm, objectProps->props[i])) == NULL)
1216 LOGDEBUG2(L_DRM, "drmdevice: %s: Unable to query property", __FUNCTION__);
1217
1218 if (strcmp(propName, Prop->name) == 0) {
1219 id = Prop->prop_id;
1221 break;
1222 }
1223
1225 }
1226
1228
1229 if (id == 0)
1230 LOGDEBUG2(L_DRM, "drmdevice: %s Unable to find value for property \'%s\'.", __FUNCTION__, propName);
1231
1233}
1234
1247{
1248 uint32_t i;
1249 int found = 0;
1253
1254 if (!objectProps)
1255 return -1;
1256
1257 for (i = 0; i < objectProps->count_props; i++) {
1258 if ((Prop = drmModeGetProperty(m_fdDrm, objectProps->props[i])) == NULL)
1259 LOGDEBUG2(L_DRM, "drmdevice: %s: Unable to query property", __FUNCTION__);
1260
1261 if (strcmp(propName, Prop->name) == 0) {
1262 *value = objectProps->prop_values[i];
1263 found = 1;
1264 }
1265
1267
1268 if (found)
1269 break;
1270 }
1271
1273
1274 if (!found) {
1275 LOGDEBUG2(L_DRM, "drmdevice: %s: Unable to find value for property \'%s\'.", __FUNCTION__, propName);
1276 return -1;
1277 }
1278
1279 return 0;
1280}
1281
1292{
1293 uint32_t i;
1294 int found = 0;
1295 int value = 0;
1296
1300
1301 if (!objectProps)
1302 return 0;
1303
1304 for (i = 0; i < objectProps->count_props; i++) {
1305 if ((Prop = drmModeGetProperty(m_fdDrm, objectProps->props[i])) == NULL)
1306 LOGDEBUG2(L_DRM, "drmdevice: %s: Unable to query property", __FUNCTION__);
1307
1308 if (strcmp(propName, Prop->name) == 0) {
1309 value = objectProps->props[i];
1310 found = 1;
1311 }
1312
1314
1315 if (found)
1316 break;
1317 }
1318
1320
1321 if (!found)
1322 LOGDEBUG2(L_DRM, "drmdevice: %s: Unable to find value for property \'%s\'.", __FUNCTION__, propName);
1323
1324 return value;
1325}
1326
1334
1346
1354{
1356}
1357
1362{
1363 memset(&m_drmEventCtx, 0, sizeof(m_drmEventCtx));
1364 m_drmEventCtx.version = 2;
1365}
1366
1367/*
1368 * drmModeAtomic* wrapper functions
1369 */
1374
1379
1384
1389
1394
1399
1404
1409
1414
1419
1421{
1422 int ret = drmModeCreatePropertyBlob(m_fdDrm, data, size, blobID);
1423 if (ret)
1424 LOGDEBUG2(L_DRM, "drmdevice: %s: failed to create hdr property blob: id %d, data %p (%d) ret %d", __FUNCTION__, blobID, data, size, ret);
1425
1426 return ret;
1427}
1428
1430{
1432 if (ret)
1433 LOGDEBUG2(L_DRM, "drmdevice: %s: failed to set hdr property blob: blob id %d connector_id %d, m_hdrMetadata %d ret %d",
1435
1436 return ret;
1437}
1438
DRM Buffer.
Definition drmbuffer.h:48
bool m_glInitiated
true, if OpenGL/ES context is initiated
Definition drmdevice.h:194
int SetConnectorHdrOutputMetadata(drmModeAtomicReqPtr, uint32_t)
uint64_t OsdHeight(void)
Definition drmdevice.h:101
int m_userReqDisplayHeight
user requested display height
Definition drmdevice.h:165
drmModeModeInfo m_drmModeInfo
mode info
Definition drmdevice.h:155
cDrmDevice(cVideoRender *, cSoftHdConfig *)
Create a drm device.
Definition drmdevice.cpp:49
const char * m_userDrmConnector
user requested drm connector
Definition drmdevice.h:163
int InitGbm(void)
Init gbm device and surface.
cDrmBuffer * GetBufFromBo(struct gbm_bo *)
Get a drm buffer from a gbm buffer object.
int GetPropertyValue(uint32_t, uint32_t, const char *, uint64_t *)
Get a drm property value.
int SetConnectorColorspace(drmModeAtomicReqPtr, uint32_t)
int InitEGL(void)
Init EGL context.
int CreateModeBlob(uint32_t *)
int m_userReqOsdWidth
user requested osd width
Definition drmdevice.h:168
int CreatePropertyBlob(uint32_t *)
Wrapper to create a property blob.
uint32_t m_hdrMetadata
property id of HDR_OUTPUT_METADATA
Definition drmdevice.h:158
int GetVideoPlaneColorRange(uint64_t *)
cDrmPlane m_videoPlane
the video drm plane
Definition drmdevice.h:174
int DestroyHdrBlob(uint32_t)
struct gbm_surface * m_pGbmSurface
pointer to the gbm surface
Definition drmdevice.h:189
uint32_t GetPropertyID(uint32_t, uint32_t, const char *)
Get a property ID.
int FindMode(void)
Find a suitable mode from the current connector.
int SetVideoPlaneColorEncoding(drmModeAtomicReqPtr, uint32_t)
EGLContext m_eglContext
EGL context.
Definition drmdevice.h:193
int SetPropertyRequest(drmModeAtomicReqPtr, uint32_t, uint32_t, const char *, uint64_t)
Add a drm property to an atomic modeset request.
int SetCrtcModeId(drmModeAtomicReqPtr, uint32_t)
int32_t FindCrtcForConnector(const drmModeRes *, const drmModeConnector *)
Find the CRTC_ID for the given connector.
EGLSurface m_eglSurface
EGL surface.
Definition drmdevice.h:191
int HandleEvent(void)
Poll for a drm event.
bool CanHandleMode(sDrmMode *)
Return true, if the given mode is one of the collected ones.
bool m_userReqDisplayInterlaced
user requested display interlaced mode
Definition drmdevice.h:167
double m_userReqDisplayRefreshRate
user requested display refresh rate
Definition drmdevice.h:166
int m_userReqDisplayWidth
user requested display width
Definition drmdevice.h:164
cSoftHdConfig * m_pConfig
pointer to cSoftHdConfig object
Definition drmdevice.h:150
int DestroyModeBlob(uint32_t)
cDrmPlane m_pipPlane
the pip drm plane
Definition drmdevice.h:177
drmModeCrtc * m_drmModeCrtcSaved
saved CRTC infos
Definition drmdevice.h:159
int CreateHdrBlob(struct hdr_output_metadata *, size_t, uint32_t *)
int m_userReqOsdHeight
user requested osd height
Definition drmdevice.h:169
int m_fdDrm
drm file descriptor
Definition drmdevice.h:152
uint64_t m_zposPip
zpos of pip plane
Definition drmdevice.h:176
std::string m_connectorName
drm connector name
Definition drmdevice.h:154
int SetConnectorHdrBlobProperty(uint32_t)
EGLDisplay m_eglDisplay
EGL display.
Definition drmdevice.h:192
void SaveCrtc(void)
Save information of a CRTC.
int ReInit(void)
Re-Init the drm device with a new connector mode.
~cDrmDevice(void)
Definition drmdevice.cpp:74
uint32_t m_connectorId
connector id
Definition drmdevice.h:153
void RestoreCrtc(void)
Restore information of a CRTC.
void Close(void)
Close the drm file handle.
uint32_t m_crtcId
current crtc ID
Definition drmdevice.h:156
int SetConnectorCrtcId(drmModeAtomicReqPtr)
uint32_t m_crtcIndex
current crtc index
Definition drmdevice.h:157
int SetCrtcActive(drmModeAtomicReqPtr, uint32_t)
uint64_t OsdWidth(void)
Definition drmdevice.h:100
cVideoRender * m_pRender
pointer to cVideoRender object
Definition drmdevice.h:149
int Init(void)
Initiate the drm device.
uint64_t m_zposPrimary
zpos of primary plane
Definition drmdevice.h:173
drmEventContext m_drmEventCtx
drm event context
Definition drmdevice.h:160
bool m_useZpos
is set, if drm hardware can use zpos
Definition drmdevice.h:171
struct gbm_device * m_pGbmDevice
pointer to the gbm device
Definition drmdevice.h:188
cDrmPlane m_osdPlane
the osd drm plane
Definition drmdevice.h:175
int SetVideoPlaneColorRange(drmModeAtomicReqPtr, uint32_t)
void InitEvent(void)
Init the event context.
const char * m_userDrmDevice
user requested drm device
Definition drmdevice.h:162
uint64_t m_zposOverlay
zpos of overlay plane
Definition drmdevice.h:172
DRM Plane.
Definition drmplane.h:23
uint64_t GetType(void)
Definition drmplane.h:41
uint64_t GetZpos(void)
Definition drmplane.h:53
void SetId(uint32_t id)
Definition drmplane.h:40
int HasZpos(int)
Check, if the plane is able to set the zpos property.
Definition drmplane.cpp:179
void FillProperties(int)
Fill the plane properties.
Definition drmplane.cpp:34
void SetZpos(uint64_t zpos)
Definition drmplane.h:54
void SetType(uint64_t type)
Definition drmplane.h:42
uint32_t GetId(void)
Definition drmplane.h:39
Plugin Configuration.
Definition config.h:49
sDrmMode RequestedDrmMode
is set to the requested mode which should be changed to
Definition config.h:137
std::vector< sDrmMode > CollectedDrmModes
collected available drm modes on the current connector
Definition config.h:132
sDrmMode AutoDetectedDrmMode
auto detected mode on the first startup (maybe equal to UserSetMode)
Definition config.h:133
const char * ConfigOsdResolution
osd resolution (syntax: "1920x1080")
Definition config.h:118
sDrmMode UserSetDrmMode
user requested drm mode on the current connector
Definition config.h:134
sDrmMode CurrentDrmMode
currently used drm mode on the current connector
Definition config.h:135
Video Renderer.
void SetScreenSize(int, int, double, bool)
Wrapper to set the screen size in the device.
void SetOsdSize(int, int)
Wrapper to set the osd size in the device.
Plugin Configuration Header File.
static double GetRefreshRateHz(drmModeModeInfo *modeInfo)
Calculate the refresh rate of the given mode to get the precise value and don't use m_drmModeInfo....
int plane
PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display
static int TestCaps(int fd)
Test drm capabilities.
Definition drmdevice.cpp:96
PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC get_platform_surface
static std::string ConnectorName(drmModeConnector *connector)
Returns the connector type name if available.
__attribute__((weak)) union gbm_bo_handle gbm_bo_get_handle_for_plane(struct gbm_bo *bo
static bool InterlacedMode(uint32_t flags)
Returns true, if the interlaced flag of the mode is set.
static bool AlmostEqual(double a, double b)
Return true, if both values are equivalent within a tolerance.
DRM Device Header File.
DRM Plane Header File.
constexpr std::array DrmModeWhitelist
Whitelist of possible drm modes.
Definition drmdevice.h:46
drmModeConnector * FindDrmConnector(int, drmModeRes *, const char *)
Find a suitable connector, preferably a connected one.
static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
Callback function to destroy a drm buffer which stays in the gbm_bo's user data.
EGLConfig GetEGLConfig(void)
Get a suitable EGLConfig.
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)
Find the CRTC_ID for the given encoder.
static int get_resources(int fd, drmModeRes **resources)
Definition drmdevice.cpp:80
static bool Contains(drmModeModeInfo *mode, std::vector< sDrmMode > modes)
Test, if the given mode is included in the given array.
static const EGLint context_attribute_list[]
static int OpenDrmDevice(const char *device, drmModeRes **resources)
Open the given device.
#define LOGDEBUG2
log to LOG_DEBUG and add a prefix
Definition logger.h:47
#define LOGDEBUG
log to LOG_DEBUG
Definition logger.h:45
#define LOGERROR
log to LOG_ERR
Definition logger.h:39
#define LOGWARNING
log to LOG_WARN
Definition logger.h:41
#define LOGINFO
log to LOG_INFO
Definition logger.h:43
#define LOGFATAL
log to LOG_ERR and abort
Definition logger.h:37
#define EGL_CHECK(stmt)
eglCheckError macro
Definition misc.h:62
@ L_DRM
drm logs
Definition logger.h:60
@ L_OPENGL
opengl osd logs
Definition logger.h:65
Logger Header File.
Holds possible display configurations.
Definition config.h:30
int width
display width
Definition config.h:31
bool interlaced
is this an interlaced mode?
Definition config.h:34
int height
display height
Definition config.h:32
double refreshRateHz
display refresh rate
Definition config.h:33
Video Renderer (Display) Header File.