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