1 /*
2  * GStreamer
3  * Copyright (C) 2018 Carlos Rafael Giani <dv@pseudoterminal.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <gudev/gudev.h>
24 #include "gstgl_gbm_utils.h"
25 
26 GST_DEBUG_CATEGORY_EXTERN (gst_gl_gbm_debug);
27 #define GST_CAT_DEFAULT gst_gl_gbm_debug
28 
29 const gchar *
gst_gl_gbm_get_name_for_drm_connector(drmModeConnector * connector)30 gst_gl_gbm_get_name_for_drm_connector (drmModeConnector * connector)
31 {
32   g_assert (connector != NULL);
33 
34   switch (connector->connector_type) {
35     case DRM_MODE_CONNECTOR_Unknown:
36       return "Unknown";
37     case DRM_MODE_CONNECTOR_VGA:
38       return "VGA";
39     case DRM_MODE_CONNECTOR_DVII:
40       return "DVI-I";
41     case DRM_MODE_CONNECTOR_DVID:
42       return "DVI-D";
43     case DRM_MODE_CONNECTOR_DVIA:
44       return "DVI-A";
45     case DRM_MODE_CONNECTOR_Composite:
46       return "Composite";
47     case DRM_MODE_CONNECTOR_SVIDEO:
48       return "S-Video";
49     case DRM_MODE_CONNECTOR_LVDS:
50       return "LVDS";
51     case DRM_MODE_CONNECTOR_Component:
52       return "Component";
53     case DRM_MODE_CONNECTOR_9PinDIN:
54       return "9-Pin DIN";
55     case DRM_MODE_CONNECTOR_DisplayPort:
56       return "DP";
57     case DRM_MODE_CONNECTOR_HDMIA:
58       return "HDMI-A";
59     case DRM_MODE_CONNECTOR_HDMIB:
60       return "HDMI-B";
61     case DRM_MODE_CONNECTOR_TV:
62       return "TV";
63     case DRM_MODE_CONNECTOR_eDP:
64       return "eDP";
65     case DRM_MODE_CONNECTOR_VIRTUAL:
66       return "Virtual";
67     case DRM_MODE_CONNECTOR_DSI:
68       return "DSI";
69     case DRM_MODE_CONNECTOR_DPI:
70       return "DPI";
71     default:
72       return "<unknown>";
73   }
74 }
75 
76 
77 const gchar *
gst_gl_gbm_get_name_for_drm_encoder(drmModeEncoder * encoder)78 gst_gl_gbm_get_name_for_drm_encoder (drmModeEncoder * encoder)
79 {
80   switch (encoder->encoder_type) {
81     case DRM_MODE_ENCODER_NONE:
82       return "none";
83     case DRM_MODE_ENCODER_DAC:
84       return "DAC";
85     case DRM_MODE_ENCODER_TMDS:
86       return "TMDS";
87     case DRM_MODE_ENCODER_LVDS:
88       return "LVDS";
89     case DRM_MODE_ENCODER_TVDAC:
90       return "TVDAC";
91     case DRM_MODE_ENCODER_VIRTUAL:
92       return "Virtual";
93     case DRM_MODE_ENCODER_DSI:
94       return "DSI";
95     default:
96       return "<unknown>";
97   }
98 }
99 
100 
101 const gchar *
gst_gl_gbm_format_to_string(guint32 format)102 gst_gl_gbm_format_to_string (guint32 format)
103 {
104   if (format == GBM_BO_FORMAT_XRGB8888)
105     format = GBM_FORMAT_XRGB8888;
106   if (format == GBM_BO_FORMAT_ARGB8888)
107     format = GBM_FORMAT_ARGB8888;
108 
109   switch (format) {
110     case GBM_FORMAT_C8:
111       return "C8";
112     case GBM_FORMAT_RGB332:
113       return "RGB332";
114     case GBM_FORMAT_BGR233:
115       return "BGR233";
116     case GBM_FORMAT_NV12:
117       return "NV12";
118     case GBM_FORMAT_XRGB4444:
119       return "XRGB4444";
120     case GBM_FORMAT_XBGR4444:
121       return "XBGR4444";
122     case GBM_FORMAT_RGBX4444:
123       return "RGBX4444";
124     case GBM_FORMAT_BGRX4444:
125       return "BGRX4444";
126     case GBM_FORMAT_XRGB1555:
127       return "XRGB1555";
128     case GBM_FORMAT_XBGR1555:
129       return "XBGR1555";
130     case GBM_FORMAT_RGBX5551:
131       return "RGBX5551";
132     case GBM_FORMAT_BGRX5551:
133       return "BGRX5551";
134     case GBM_FORMAT_ARGB4444:
135       return "ARGB4444";
136     case GBM_FORMAT_ABGR4444:
137       return "ABGR4444";
138     case GBM_FORMAT_RGBA4444:
139       return "RGBA4444";
140     case GBM_FORMAT_BGRA4444:
141       return "BGRA4444";
142     case GBM_FORMAT_ARGB1555:
143       return "ARGB1555";
144     case GBM_FORMAT_ABGR1555:
145       return "ABGR1555";
146     case GBM_FORMAT_RGBA5551:
147       return "RGBA5551";
148     case GBM_FORMAT_BGRA5551:
149       return "BGRA5551";
150     case GBM_FORMAT_RGB565:
151       return "RGB565";
152     case GBM_FORMAT_BGR565:
153       return "BGR565";
154     case GBM_FORMAT_YUYV:
155       return "YUYV";
156     case GBM_FORMAT_YVYU:
157       return "YVYU";
158     case GBM_FORMAT_UYVY:
159       return "UYVY";
160     case GBM_FORMAT_VYUY:
161       return "VYUY";
162     case GBM_FORMAT_RGB888:
163       return "RGB888";
164     case GBM_FORMAT_BGR888:
165       return "BGR888";
166     case GBM_FORMAT_XRGB8888:
167       return "XRGB8888";
168     case GBM_FORMAT_XBGR8888:
169       return "XBGR8888";
170     case GBM_FORMAT_RGBX8888:
171       return "RGBX8888";
172     case GBM_FORMAT_BGRX8888:
173       return "BGRX8888";
174     case GBM_FORMAT_AYUV:
175       return "AYUV";
176     case GBM_FORMAT_XRGB2101010:
177       return "XRGB2101010";
178     case GBM_FORMAT_XBGR2101010:
179       return "XBGR2101010";
180     case GBM_FORMAT_RGBX1010102:
181       return "RGBX1010102";
182     case GBM_FORMAT_BGRX1010102:
183       return "BGRX1010102";
184     case GBM_FORMAT_ARGB8888:
185       return "ARGB8888";
186     case GBM_FORMAT_ABGR8888:
187       return "ABGR8888";
188     case GBM_FORMAT_RGBA8888:
189       return "RGBA8888";
190     case GBM_FORMAT_BGRA8888:
191       return "BGRA8888";
192     case GBM_FORMAT_ARGB2101010:
193       return "ARGB2101010";
194     case GBM_FORMAT_ABGR2101010:
195       return "ABGR2101010";
196     case GBM_FORMAT_RGBA1010102:
197       return "RGBA1010102";
198     case GBM_FORMAT_BGRA1010102:
199       return "BGRA1010102";
200 
201     default:
202       return "<unknown>";
203   }
204 
205   return NULL;
206 }
207 
208 
209 int
gst_gl_gbm_depth_from_format(guint32 format)210 gst_gl_gbm_depth_from_format (guint32 format)
211 {
212   if (format == GBM_BO_FORMAT_XRGB8888)
213     format = GBM_FORMAT_XRGB8888;
214   if (format == GBM_BO_FORMAT_ARGB8888)
215     format = GBM_FORMAT_ARGB8888;
216 
217   switch (format) {
218     case GBM_FORMAT_C8:
219     case GBM_FORMAT_RGB332:
220     case GBM_FORMAT_BGR233:
221       return 8;
222 
223     case GBM_FORMAT_NV12:
224     case GBM_FORMAT_XRGB4444:
225     case GBM_FORMAT_XBGR4444:
226     case GBM_FORMAT_RGBX4444:
227     case GBM_FORMAT_BGRX4444:
228       return 12;
229 
230     case GBM_FORMAT_XRGB1555:
231     case GBM_FORMAT_XBGR1555:
232     case GBM_FORMAT_RGBX5551:
233     case GBM_FORMAT_BGRX5551:
234       return 15;
235 
236     case GBM_FORMAT_ARGB4444:
237     case GBM_FORMAT_ABGR4444:
238     case GBM_FORMAT_RGBA4444:
239     case GBM_FORMAT_BGRA4444:
240     case GBM_FORMAT_ARGB1555:
241     case GBM_FORMAT_ABGR1555:
242     case GBM_FORMAT_RGBA5551:
243     case GBM_FORMAT_BGRA5551:
244     case GBM_FORMAT_RGB565:
245     case GBM_FORMAT_BGR565:
246     case GBM_FORMAT_YUYV:
247     case GBM_FORMAT_YVYU:
248     case GBM_FORMAT_UYVY:
249     case GBM_FORMAT_VYUY:
250       return 16;
251 
252     case GBM_FORMAT_RGB888:
253     case GBM_FORMAT_BGR888:
254     case GBM_FORMAT_XRGB8888:
255     case GBM_FORMAT_XBGR8888:
256     case GBM_FORMAT_RGBX8888:
257     case GBM_FORMAT_BGRX8888:
258     case GBM_FORMAT_AYUV:
259       return 24;
260 
261     case GBM_FORMAT_XRGB2101010:
262     case GBM_FORMAT_XBGR2101010:
263     case GBM_FORMAT_RGBX1010102:
264     case GBM_FORMAT_BGRX1010102:
265       return 30;
266 
267     case GBM_FORMAT_ARGB8888:
268     case GBM_FORMAT_ABGR8888:
269     case GBM_FORMAT_RGBA8888:
270     case GBM_FORMAT_BGRA8888:
271     case GBM_FORMAT_ARGB2101010:
272     case GBM_FORMAT_ABGR2101010:
273     case GBM_FORMAT_RGBA1010102:
274     case GBM_FORMAT_BGRA1010102:
275       return 32;
276 
277     default:
278       GST_ERROR ("unknown GBM format %" G_GUINT32_FORMAT, format);
279   }
280 
281   return 0;
282 }
283 
284 
285 int
gst_gl_gbm_bpp_from_format(guint32 format)286 gst_gl_gbm_bpp_from_format (guint32 format)
287 {
288   if (format == GBM_BO_FORMAT_XRGB8888)
289     format = GBM_FORMAT_XRGB8888;
290   if (format == GBM_BO_FORMAT_ARGB8888)
291     format = GBM_FORMAT_ARGB8888;
292 
293   switch (format) {
294     case GBM_FORMAT_C8:
295     case GBM_FORMAT_RGB332:
296     case GBM_FORMAT_BGR233:
297       return 8;
298 
299     case GBM_FORMAT_NV12:
300       return 12;
301 
302     case GBM_FORMAT_XRGB4444:
303     case GBM_FORMAT_XBGR4444:
304     case GBM_FORMAT_RGBX4444:
305     case GBM_FORMAT_BGRX4444:
306     case GBM_FORMAT_ARGB4444:
307     case GBM_FORMAT_ABGR4444:
308     case GBM_FORMAT_RGBA4444:
309     case GBM_FORMAT_BGRA4444:
310     case GBM_FORMAT_XRGB1555:
311     case GBM_FORMAT_XBGR1555:
312     case GBM_FORMAT_RGBX5551:
313     case GBM_FORMAT_BGRX5551:
314     case GBM_FORMAT_ARGB1555:
315     case GBM_FORMAT_ABGR1555:
316     case GBM_FORMAT_RGBA5551:
317     case GBM_FORMAT_BGRA5551:
318     case GBM_FORMAT_RGB565:
319     case GBM_FORMAT_BGR565:
320     case GBM_FORMAT_YUYV:
321     case GBM_FORMAT_YVYU:
322     case GBM_FORMAT_UYVY:
323     case GBM_FORMAT_VYUY:
324       return 16;
325 
326     case GBM_FORMAT_RGB888:
327     case GBM_FORMAT_BGR888:
328       return 24;
329 
330     case GBM_FORMAT_XRGB8888:
331     case GBM_FORMAT_XBGR8888:
332     case GBM_FORMAT_RGBX8888:
333     case GBM_FORMAT_BGRX8888:
334     case GBM_FORMAT_ARGB8888:
335     case GBM_FORMAT_ABGR8888:
336     case GBM_FORMAT_RGBA8888:
337     case GBM_FORMAT_BGRA8888:
338     case GBM_FORMAT_XRGB2101010:
339     case GBM_FORMAT_XBGR2101010:
340     case GBM_FORMAT_RGBX1010102:
341     case GBM_FORMAT_BGRX1010102:
342     case GBM_FORMAT_ARGB2101010:
343     case GBM_FORMAT_ABGR2101010:
344     case GBM_FORMAT_RGBA1010102:
345     case GBM_FORMAT_BGRA1010102:
346     case GBM_FORMAT_AYUV:
347       return 32;
348 
349     default:
350       GST_ERROR ("unknown GBM format %" G_GUINT32_FORMAT, format);
351   }
352 
353   return 0;
354 }
355 
356 
357 static void
gst_gl_gbm_drm_fb_destroy_callback(struct gbm_bo * bo,void * data)358 gst_gl_gbm_drm_fb_destroy_callback (struct gbm_bo *bo, void *data)
359 {
360   int drm_fd = gbm_device_get_fd (gbm_bo_get_device (bo));
361   GstGLDRMFramebuffer *fb = (GstGLDRMFramebuffer *) (data);
362 
363   if (fb->fb_id)
364     drmModeRmFB (drm_fd, fb->fb_id);
365 
366   g_slice_free1 (sizeof (GstGLDRMFramebuffer), fb);
367 }
368 
369 
370 GstGLDRMFramebuffer *
gst_gl_gbm_drm_fb_get_from_bo(struct gbm_bo * bo)371 gst_gl_gbm_drm_fb_get_from_bo (struct gbm_bo *bo)
372 {
373   GstGLDRMFramebuffer *fb;
374   int drm_fd;
375   guint32 width, height, stride, format, handle;
376   int depth, bpp;
377   int ret;
378 
379   /* We want to use this buffer object (abbr. "bo") as a scanout buffer.
380    * To that end, we associate the bo with the DRM by using drmModeAddFB().
381    * However, this needs to be called exactly once for the given bo, and the
382    * counterpart, drmModeRmFB(), needs to be called when the bo is cleaned up.
383    *
384    * To fulfill these requirements, add extra framebuffer information to the
385    * bo as "user data". This way, if this user data pointer is NULL, it means
386    * that no framebuffer information was generated yet & the bo was not set
387    * as a scanout buffer with drmModeAddFB() yet, and we have perform these
388    * steps. Otherwise, if it is non-NULL, we know we do not have to set up
389    * anything (since it was done already) and just return the pointer to the
390    * framebuffer information. */
391   fb = (GstGLDRMFramebuffer *) (gbm_bo_get_user_data (bo));
392   if (fb != NULL) {
393     /* The bo was already set up as a scanout framebuffer. Just
394      * return the framebuffer information. */
395     return fb;
396   }
397 
398   /* If this point is reached, then we have to setup the bo as a
399    * scanout framebuffer. */
400 
401   drm_fd = gbm_device_get_fd (gbm_bo_get_device (bo));
402 
403   fb = g_slice_alloc0 (sizeof (GstGLDRMFramebuffer));
404   fb->bo = bo;
405 
406   width = gbm_bo_get_width (bo);
407   height = gbm_bo_get_height (bo);
408   stride = gbm_bo_get_stride (bo);
409   format = gbm_bo_get_format (bo);
410   handle = gbm_bo_get_handle (bo).u32;
411 
412   depth = gst_gl_gbm_depth_from_format (format);
413   bpp = gst_gl_gbm_bpp_from_format (format);
414 
415   GST_DEBUG ("Attempting to add GBM BO as scanout framebuffer width/height: %"
416       G_GUINT32_FORMAT "/%" G_GUINT32_FORMAT " pixels  stride: %"
417       G_GUINT32_FORMAT " bytes  format: %s  depth: %d bits  total bpp: %d bits",
418       width, height, stride, gst_gl_gbm_format_to_string (format), depth, bpp);
419 
420   /* Set the bo as a scanout framebuffer */
421   ret = drmModeAddFB (drm_fd, width, height, depth, bpp, stride, handle,
422       &fb->fb_id);
423   if (ret != 0) {
424     GST_ERROR ("Failed to add GBM BO as scanout framebuffer: %s (%d)",
425         g_strerror (errno), errno);
426     g_slice_free1 (sizeof (GstGLDRMFramebuffer), fb);
427     return NULL;
428   }
429 
430   /* Add the framebuffer information to the bo as user data, and also install a callback
431    * that cleans up this extra information whenever the bo itself is discarded */
432   gbm_bo_set_user_data (bo, fb, gst_gl_gbm_drm_fb_destroy_callback);
433 
434   return fb;
435 }
436 
437 
438 int
gst_gl_gbm_find_and_open_drm_node(void)439 gst_gl_gbm_find_and_open_drm_node (void)
440 {
441   /* In here we use GUDev to try to autodetect the GPU */
442 
443   int drm_fd = -1;
444   GUdevClient *gudev_client = NULL;
445   GUdevEnumerator *gudev_enum = NULL;
446   GList *devlist = NULL;
447   GList *deventry = NULL;
448   const gchar *subsystems[2] = { "drm", NULL };
449 
450   gudev_client = g_udev_client_new (subsystems);
451   if (gudev_client == NULL) {
452     GST_ERROR ("Could not create gudev client");
453     goto cleanup;
454   }
455   GST_DEBUG ("Created gudev client");
456 
457   gudev_enum = g_udev_enumerator_new (gudev_client);
458   if (gudev_enum == NULL) {
459     GST_ERROR ("Could not create gudev enumerator");
460     goto cleanup;
461   }
462   GST_DEBUG ("Created gudev enumerator");
463 
464   /* TODO: To be 100% sure we pick the right device, also check
465    * if this is a GPU, because a pure scanout device could also
466    * have a DRM subsystem for example. However, currently it is
467    * unclear how to do that. By trying to create an EGL context? */
468   g_udev_enumerator_add_match_subsystem (gudev_enum, "drm");
469   devlist = g_udev_enumerator_execute (gudev_enum);
470   GST_DEBUG ("Scanned for udev devices with a drm subsytem");
471 
472   if (devlist == NULL) {
473     GST_WARNING ("Found no matching DRM devices");
474     goto cleanup;
475   }
476   GST_DEBUG ("Got %u potentially matching device(s)", g_list_length (devlist));
477 
478   for (deventry = devlist; deventry != NULL; deventry = deventry->next) {
479     GUdevDevice *gudevice = G_UDEV_DEVICE (deventry->data);
480     const gchar *devnode = g_udev_device_get_device_file (gudevice);
481 
482     if ((devnode == NULL) || !g_str_has_prefix (devnode, "/dev/dri/card"))
483       continue;
484 
485     GST_DEBUG ("Found DRM device with device node \"%s\"", devnode);
486 
487     drm_fd = open (devnode, O_RDWR | O_CLOEXEC);
488     if (drm_fd < 0) {
489       GST_WARNING ("Cannot open device node \"%s\": %s (%d)", devnode,
490           g_strerror (errno), errno);
491       continue;
492     }
493 
494     GST_DEBUG ("Device node \"%s\" is a valid DRM device node", devnode);
495     break;
496   }
497 
498 
499 done:
500 
501   if (devlist != NULL) {
502     g_list_free_full (devlist, g_object_unref);
503     devlist = NULL;
504     GST_DEBUG ("Cleaned up device list");
505   }
506 
507   if (gudev_enum != NULL) {
508     g_object_unref (G_OBJECT (gudev_enum));
509     gudev_enum = NULL;
510     GST_DEBUG ("Cleaned up gudev enumerator");
511   }
512 
513   if (gudev_client != NULL) {
514     g_object_unref (G_OBJECT (gudev_client));
515     gudev_client = NULL;
516     GST_DEBUG ("Cleaned up gudev client");
517   }
518 
519   return drm_fd;
520 
521 
522 cleanup:
523 
524   if (drm_fd >= 0) {
525     close (drm_fd);
526     drm_fd = -1;
527   }
528 
529   goto done;
530 }
531