vdr-plugin-softhddevice-drm-gles 1.6.8-daba64b
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
781 if (drmmode)
783
785
786 if (!drmmode) {
787 LOGERROR("drmdevice: %s: No monitor mode found! Probably no monitor connected, giving up!", __FUNCTION__);
788 return -1;
789 }
790
792 m_drmModeInfo.hdisplay,
793 m_drmModeInfo.vdisplay,
796 };
797
800
802
803 return 0;
804}
805
818{
819 sDrmMode *drmMode = nullptr;
820
823 else if (m_pConfig->UserSetDrmMode.width > 0)
825
826 if (drmMode) {
829 m_userReqDisplayRefreshRate = drmMode->refreshRateHz;
831 LOGDEBUG("%s requested display mode: %dx%d@%.2f%s", __FUNCTION__,
833 }
834
835 return FindMode();
836}
837
838#ifdef USE_GLES
846{
847 int w = OsdWidth();
848 int h = OsdHeight();
851
853 if (!m_pGbmDevice) {
854 LOGERROR("drmdevice: %s: failed to create gbm device!", __FUNCTION__);
855 return -1;
856 }
857
859 if (!m_pGbmSurface) {
861 LOGERROR("drmdevice: %s: failed to create %d x %d surface bo", __FUNCTION__, w, h);
862 return -1;
863 }
864
865 return 0;
866}
867
872{
873 if (m_pGbmSurface) {
875 m_pGbmSurface = nullptr;
876 }
877
878 if (m_pGbmDevice) {
880 m_pGbmDevice = nullptr;
881 }
882}
883
886
889{
892};
893
900{
902 EGL_BUFFER_SIZE, 32,
908 };
909 EGLConfig *configs = nullptr;
910 EGLint matched = 0;
911 EGLint count = 0;
913 if (count < 1)
914 LOGFATAL("drmdevice: %s: no EGL configs to choose from", __FUNCTION__);
915
916 LOGDEBUG2(L_OPENGL, "drmdevice: %s: %d EGL configs found", __FUNCTION__, count);
917
918 configs = (EGLConfig *)malloc(count * sizeof(*configs));
919 if (!configs)
920 LOGFATAL("drmdevice: %s: can't allocate space for EGL configs", __FUNCTION__);
921
923 if (!matched) {
924 free(configs);
925 LOGFATAL("drmdevice: %s: no EGL configs with appropriate attributes", __FUNCTION__);
926 }
927
928 LOGDEBUG2(L_OPENGL, "drmdevice: %s: %d appropriate EGL configs found, which match attributes", __FUNCTION__, matched);
929
931 for (int i = 0; i < matched; ++i) {
934
936 chosen = configs[i];
937 break;
938 }
939 }
940
941 free(configs);
942 if (chosen == NULL)
943 LOGFATAL("drmdevice: %s: no matching gbm config found", __FUNCTION__);
944
945 return chosen;
946}
947
955{
957
962
964 if (!m_eglDisplay) {
965 LOGERROR("drmdevice: %s: failed to get eglDisplay", __FUNCTION__);
966 return -1;
967 }
968
970 LOGERROR("drmdevice: %s: eglInitialize failed", __FUNCTION__);
971 return -1;
972 }
973
974 LOGDEBUG2(L_OPENGL, "drmdevice: %s: Using display %p with EGL version %d.%d", __FUNCTION__, m_eglDisplay, iMajorVersion, iMinorVersion);
979
981
984 if (!m_eglContext) {
985 LOGERROR("drmdevice: %s: failed to create eglContext", __FUNCTION__);
986 return -1;
987 }
988
991 LOGERROR("drmdevice: %s: failed to create eglSurface", __FUNCTION__);
992 return -1;
993 }
994
998
999 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);
1000
1001 m_glInitiated = true;
1002 LOGINFO("DRM Setup: EGL context initialized");
1003
1004 return 0;
1005}
1006
1012static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
1013{
1015 cDrmBuffer *buf = (cDrmBuffer *)data;
1016
1017 if (buf->Id())
1018 drmModeRmFB(drm_fd, buf->Id());
1019
1020 delete(buf);
1021}
1022
1025
1026__attribute__ ((weak)) int
1027gbm_bo_get_fd(struct gbm_bo *bo);
1028
1031
1032__attribute__ ((weak)) int
1034
1037
1039gbm_bo_get_offset(struct gbm_bo *bo, int plane);
1040
1049{
1051 uint32_t mod_flags = 0;
1052 int ret = -1;
1053
1054 // the buffer was already allocated
1055 if (buf)
1056 return buf;
1057
1059
1063 uint64_t modifiers[4] = {0};
1065 const int num_planes = gbm_bo_get_plane_count(bo);
1066 buf->SetNumPlanes(num_planes);
1067 for (int i = 0; i < num_planes; i++) {
1068 buf->SetHandle(i, gbm_bo_get_handle_for_plane(bo, i).u32);
1069 buf->SetPitch(i, gbm_bo_get_stride_for_plane(bo, i));
1070 buf->SetOffset(i, gbm_bo_get_offset(bo, i));
1071 modifiers[i] = modifiers[0];
1072 buf->SetSize(i, buf->Height() * buf->Pitch(i));
1073 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));
1074 }
1075 buf->SetNumObjects(1);
1076 buf->SetObjectIndex(0, 0);
1077 buf->SetDmaBufHandle(gbm_bo_get_fd(bo));
1078
1079 if (modifiers[0]) {
1081 LOGDEBUG2(L_DRM, "drmdevice: %s: Using modifier %" PRIx64 "", __FUNCTION__, modifiers[0]);
1082 }
1083
1084 uint32_t id;
1085 // Add FB
1086 ret = drmModeAddFB2WithModifiers(m_fdDrm, buf->Width(), buf->Height(), buf->PixFmt(),
1087 buf->PrimeHandle(), buf->Pitch(), buf->Offset(), modifiers, &id, mod_flags);
1088 buf->SetId(id);
1089 }
1090
1091 if (ret) {
1092 if (mod_flags)
1093 LOGDEBUG2(L_DRM, "drmdevice: %s: Modifiers failed!", __FUNCTION__);
1094
1095 buf->SetNumPlanes(1);
1096 uint32_t tmpHandle[4] = { gbm_bo_get_handle(bo).u32, 0, 0, 0};
1097 uint32_t tmpStride[4] = { gbm_bo_get_stride(bo), 0, 0, 0};
1098 uint32_t tmpSize[4] = { buf->Height() * buf->Width() * buf->Pitch(0), 0, 0, 0};
1099 memcpy(buf->PrimeHandle(), tmpHandle, sizeof(tmpHandle));
1100 memcpy(buf->Pitch(), tmpStride, sizeof(tmpStride));
1101 memcpy(buf->Size(), tmpSize, sizeof(tmpSize));
1102 memset(buf->Offset(), 0, 16);
1103 buf->SetNumObjects(1);
1104 buf->SetObjectIndex(0, 0);
1105 buf->SetDmaBufHandle(gbm_bo_get_fd(bo));
1106
1107 uint32_t id;
1108 ret = drmModeAddFB2(m_fdDrm, buf->Width(), buf->Height(), buf->PixFmt(),
1109 buf->PrimeHandle(), buf->Pitch(), buf->Offset(), &id, 0);
1110 buf->SetId(id);
1111 }
1112
1113 if (ret) {
1114 LOGFATAL("drmdevice: %s: cannot create framebuffer (%d): %m", __FUNCTION__, errno);
1115 delete buf;
1116 return NULL;
1117 }
1118
1119 uint32_t pixFmt = buf->PixFmt();
1120 LOGDEBUG2(L_DRM, "drmdevice: %s: New GL buffer %d x %d pix_fmt %4.4s fb_id %d", __FUNCTION__,
1121 buf->Width(), buf->Height(), (char *)&pixFmt, buf->Id());
1122
1124 return buf;
1125}
1126#endif
1127
1139{
1140 int i;
1141
1142 for (i = 0; i < resources->count_crtcs; i++) {
1143 const uint32_t crtc_mask = 1 << i;
1144 const uint32_t crtc_id = resources->crtcs[i];
1145 if (encoder->possible_crtcs & crtc_mask) {
1146 return crtc_id;
1147 }
1148 }
1149
1150 return -1;
1151}
1152
1162{
1163 int i;
1164
1165 for (i = 0; i < connector->count_encoders; i++) {
1166 const uint32_t encoder_id = connector->encoders[i];
1168
1169 if (encoder) {
1172 if (crtc_id != 0) {
1173 return crtc_id;
1174 }
1175 }
1176 }
1177
1178 return -1;
1179}
1180
1185{
1186 LOGDEBUG2(L_DRM, "drmdevice: %s: closing fd %d", __FUNCTION__, m_fdDrm);
1188
1189 close(m_fdDrm);
1190 m_fdDrm = -1;
1191}
1192
1210
1224 const char *propName, uint64_t value)
1225{
1226 uint32_t i;
1227 uint64_t id = 0;
1231
1232 for (i = 0; i < objectProps->count_props; i++) {
1233 if ((Prop = drmModeGetProperty(m_fdDrm, objectProps->props[i])) == NULL)
1234 LOGDEBUG2(L_DRM, "drmdevice: %s: Unable to query property", __FUNCTION__);
1235
1236 if (strcmp(propName, Prop->name) == 0) {
1237 id = Prop->prop_id;
1239 break;
1240 }
1241
1243 }
1244
1246
1247 if (id == 0)
1248 LOGDEBUG2(L_DRM, "drmdevice: %s Unable to find value for property \'%s\'.", __FUNCTION__, propName);
1249
1251}
1252
1265{
1266 uint32_t i;
1267 int found = 0;
1271
1272 if (!objectProps)
1273 return -1;
1274
1275 for (i = 0; i < objectProps->count_props; i++) {
1276 if ((Prop = drmModeGetProperty(m_fdDrm, objectProps->props[i])) == NULL)
1277 LOGDEBUG2(L_DRM, "drmdevice: %s: Unable to query property", __FUNCTION__);
1278
1279 if (strcmp(propName, Prop->name) == 0) {
1280 *value = objectProps->prop_values[i];
1281 found = 1;
1282 }
1283
1285
1286 if (found)
1287 break;
1288 }
1289
1291
1292 if (!found) {
1293 LOGDEBUG2(L_DRM, "drmdevice: %s: Unable to find value for property \'%s\'.", __FUNCTION__, propName);
1294 return -1;
1295 }
1296
1297 return 0;
1298}
1299
1310{
1311 uint32_t i;
1312 int found = 0;
1313 int value = 0;
1314
1318
1319 if (!objectProps)
1320 return 0;
1321
1322 for (i = 0; i < objectProps->count_props; i++) {
1323 if ((Prop = drmModeGetProperty(m_fdDrm, objectProps->props[i])) == NULL)
1324 LOGDEBUG2(L_DRM, "drmdevice: %s: Unable to query property", __FUNCTION__);
1325
1326 if (strcmp(propName, Prop->name) == 0) {
1327 value = objectProps->props[i];
1328 found = 1;
1329 }
1330
1332
1333 if (found)
1334 break;
1335 }
1336
1338
1339 if (!found)
1340 LOGDEBUG2(L_DRM, "drmdevice: %s: Unable to find value for property \'%s\'.", __FUNCTION__, propName);
1341
1342 return value;
1343}
1344
1352
1364
1372{
1374}
1375
1380{
1381 memset(&m_drmEventCtx, 0, sizeof(m_drmEventCtx));
1382 m_drmEventCtx.version = 2;
1383}
1384
1385/*
1386 * drmModeAtomic* wrapper functions
1387 */
1392
1397
1402
1407
1412
1417
1422
1427
1432
1437
1439{
1440 int ret = drmModeCreatePropertyBlob(m_fdDrm, data, size, blobID);
1441 if (ret)
1442 LOGDEBUG2(L_DRM, "drmdevice: %s: failed to create hdr property blob: id %d, data %p (%d) ret %d", __FUNCTION__, blobID, data, size, ret);
1443
1444 return ret;
1445}
1446
1448{
1450 if (ret)
1451 LOGDEBUG2(L_DRM, "drmdevice: %s: failed to set hdr property blob: blob id %d connector_id %d, m_hdrMetadata %d ret %d",
1453
1454 return ret;
1455}
1456
DRM Buffer.
Definition drmbuffer.h:48
bool m_glInitiated
true, if OpenGL/ES context is initiated
Definition drmdevice.h:197
int SetConnectorHdrOutputMetadata(drmModeAtomicReqPtr, uint32_t)
uint64_t OsdHeight(void)
Definition drmdevice.h:104
int m_userReqDisplayHeight
user requested display height
Definition drmdevice.h:168
drmModeModeInfo m_drmModeInfo
mode info
Definition drmdevice.h:158
cDrmDevice(cVideoRender *, cSoftHdConfig *)
Create a drm device.
Definition drmdevice.cpp:49
const char * m_userDrmConnector
user requested drm connector
Definition drmdevice.h:166
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:171
int CreatePropertyBlob(uint32_t *)
Wrapper to create a property blob.
uint32_t m_hdrMetadata
property id of HDR_OUTPUT_METADATA
Definition drmdevice.h:161
int GetVideoPlaneColorRange(uint64_t *)
cDrmPlane m_videoPlane
the video drm plane
Definition drmdevice.h:177
int DestroyHdrBlob(uint32_t)
struct gbm_surface * m_pGbmSurface
pointer to the gbm surface
Definition drmdevice.h:192
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:196
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:194
void ExitGbm(void)
Free gbm device and surface.
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:170
double m_userReqDisplayRefreshRate
user requested display refresh rate
Definition drmdevice.h:169
int m_userReqDisplayWidth
user requested display width
Definition drmdevice.h:167
cSoftHdConfig * m_pConfig
pointer to cSoftHdConfig object
Definition drmdevice.h:153
int DestroyModeBlob(uint32_t)
cDrmPlane m_pipPlane
the pip drm plane
Definition drmdevice.h:180
drmModeCrtc * m_drmModeCrtcSaved
saved CRTC infos
Definition drmdevice.h:162
int CreateHdrBlob(struct hdr_output_metadata *, size_t, uint32_t *)
int m_userReqOsdHeight
user requested osd height
Definition drmdevice.h:172
int m_fdDrm
drm file descriptor
Definition drmdevice.h:155
uint64_t m_zposPip
zpos of pip plane
Definition drmdevice.h:179
std::string m_connectorName
drm connector name
Definition drmdevice.h:157
int SetConnectorHdrBlobProperty(uint32_t)
EGLDisplay m_eglDisplay
EGL display.
Definition drmdevice.h:195
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:156
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:159
int SetConnectorCrtcId(drmModeAtomicReqPtr)
uint32_t m_crtcIndex
current crtc index
Definition drmdevice.h:160
int SetCrtcActive(drmModeAtomicReqPtr, uint32_t)
uint64_t OsdWidth(void)
Definition drmdevice.h:103
cVideoRender * m_pRender
pointer to cVideoRender object
Definition drmdevice.h:152
int Init(void)
Initiate the drm device.
uint64_t m_zposPrimary
zpos of primary plane
Definition drmdevice.h:176
drmEventContext m_drmEventCtx
drm event context
Definition drmdevice.h:163
bool m_useZpos
is set, if drm hardware can use zpos
Definition drmdevice.h:174
struct gbm_device * m_pGbmDevice
pointer to the gbm device
Definition drmdevice.h:191
cDrmPlane m_osdPlane
the osd drm plane
Definition drmdevice.h:178
int SetVideoPlaneColorRange(drmModeAtomicReqPtr, uint32_t)
void InitEvent(void)
Init the event context.
const char * m_userDrmDevice
user requested drm device
Definition drmdevice.h:165
uint64_t m_zposOverlay
zpos of overlay plane
Definition drmdevice.h:175
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:65
sDrmMode RequestedDrmMode
is set to the requested mode which should be changed to
Definition config.h:154
std::vector< sDrmMode > CollectedDrmModes
collected available drm modes on the current connector
Definition config.h:149
sDrmMode AutoDetectedDrmMode
auto detected mode on the first startup (maybe equal to UserSetMode)
Definition config.h:150
const char * ConfigOsdResolution
osd resolution (syntax: "1920x1080")
Definition config.h:135
sDrmMode UserSetDrmMode
user requested drm mode on the current connector
Definition config.h:151
sDrmMode CurrentDrmMode
currently used drm mode on the current connector
Definition config.h:152
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.