ecc5a14fdf9460740e7f87207f7d0375c155c5ea
[platform/upstream/gstreamer.git] / gst-libs / gst / vaapi / gstvaapidisplay_drm.c
1 /*
2  *  gstvaapidisplay_drm.c - VA/DRM display abstraction
3  *
4  *  Copyright (C) 2012-2013 Intel Corporation
5  *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public License
9  *  as published by the Free Software Foundation; either version 2.1
10  *  of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free
19  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA 02110-1301 USA
21  */
22
23 /**
24  * SECTION:gstvaapidisplay_drm
25  * @short_description: VA/DRM display abstraction
26  */
27
28 #define _GNU_SOURCE
29 #include "sysdeps.h"
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <libudev.h>
33 #include <xf86drm.h>
34 #include <va/va_drm.h>
35 #include "gstvaapiutils.h"
36 #include "gstvaapidisplay_priv.h"
37 #include "gstvaapidisplay_drm.h"
38 #include "gstvaapidisplay_drm_priv.h"
39 #include "gstvaapiwindow_drm.h"
40
41 #define DEBUG_VAAPI_DISPLAY 1
42 #include "gstvaapidebug.h"
43
44 G_DEFINE_TYPE_WITH_PRIVATE (GstVaapiDisplayDRM, gst_vaapi_display_drm,
45     GST_TYPE_VAAPI_DISPLAY);
46
47 typedef enum
48 {
49   DRM_DEVICE_LEGACY = 1,
50   DRM_DEVICE_RENDERNODES,
51 } DRMDeviceType;
52
53 static DRMDeviceType g_drm_device_type;
54 static GMutex g_drm_device_type_lock;
55 static const gchar *allowed_subsystems[] = { "pci", "platform", NULL };
56
57 static gboolean
58 supports_vaapi (int fd)
59 {
60   gboolean ret;
61   VADisplay va_dpy;
62
63   va_dpy = vaGetDisplayDRM (fd);
64   if (!va_dpy)
65     return FALSE;
66
67   ret = vaapi_initialize (va_dpy);
68   vaTerminate (va_dpy);
69   return ret;
70 }
71
72 /* Get default device path. Actually, the first match in the DRM subsystem */
73 static const gchar *
74 get_default_device_path (GstVaapiDisplay * display)
75 {
76   GstVaapiDisplayDRMPrivate *const priv =
77       GST_VAAPI_DISPLAY_DRM_PRIVATE (display);
78   const gchar *syspath, *devpath;
79   struct udev *udev = NULL;
80   struct udev_device *device, *parent;
81   struct udev_enumerate *e = NULL;
82   struct udev_list_entry *l;
83   gint i;
84   int fd;
85
86   if (!priv->device_path_default) {
87     udev = udev_new ();
88     if (!udev)
89       goto end;
90
91     e = udev_enumerate_new (udev);
92     if (!e)
93       goto end;
94
95     udev_enumerate_add_match_subsystem (e, "drm");
96     switch (g_drm_device_type) {
97       case DRM_DEVICE_LEGACY:
98         udev_enumerate_add_match_sysname (e, "card[0-9]*");
99         break;
100       case DRM_DEVICE_RENDERNODES:
101         udev_enumerate_add_match_sysname (e, "renderD[0-9]*");
102         break;
103       default:
104         GST_ERROR ("unknown drm device type (%d)", g_drm_device_type);
105         goto end;
106     }
107     udev_enumerate_scan_devices (e);
108     udev_list_entry_foreach (l, udev_enumerate_get_list_entry (e)) {
109       syspath = udev_list_entry_get_name (l);
110       device = udev_device_new_from_syspath (udev, syspath);
111       parent = udev_device_get_parent (device);
112
113       for (i = 0; allowed_subsystems[i] != NULL; i++)
114         if (g_strcmp0 (udev_device_get_subsystem (parent),
115                 allowed_subsystems[i]) == 0)
116           break;
117
118       if (allowed_subsystems[i] == NULL) {
119         udev_device_unref (device);
120         continue;
121       }
122
123       devpath = udev_device_get_devnode (device);
124       fd = open (devpath, O_RDWR | O_CLOEXEC);
125       if (fd < 0) {
126         udev_device_unref (device);
127         continue;
128       }
129
130       if (supports_vaapi (fd))
131         priv->device_path_default = g_strdup (devpath);
132       close (fd);
133       udev_device_unref (device);
134       if (priv->device_path_default)
135         break;
136     }
137
138   end:
139     if (e)
140       udev_enumerate_unref (e);
141     if (udev)
142       udev_unref (udev);
143   }
144   return priv->device_path_default;
145 }
146
147 /* Reconstruct a device path without our prefix */
148 static const gchar *
149 get_device_path (GstVaapiDisplay * display)
150 {
151   GstVaapiDisplayDRMPrivate *const priv =
152       GST_VAAPI_DISPLAY_DRM_PRIVATE (display);
153   const gchar *device_path = priv->device_path;
154
155   if (!device_path || *device_path == '\0')
156     return NULL;
157   return device_path;
158 }
159
160 /* Mangle device path with our prefix */
161 static gboolean
162 set_device_path (GstVaapiDisplay * display, const gchar * device_path)
163 {
164   GstVaapiDisplayDRMPrivate *const priv =
165       GST_VAAPI_DISPLAY_DRM_PRIVATE (display);
166
167   g_free (priv->device_path);
168   priv->device_path = NULL;
169
170   if (!device_path) {
171     device_path = get_default_device_path (display);
172     if (!device_path)
173       return FALSE;
174   }
175   priv->device_path = g_strdup (device_path);
176   return priv->device_path != NULL;
177 }
178
179 /* Set device path from file descriptor */
180 static gboolean
181 set_device_path_from_fd (GstVaapiDisplay * display, gint drm_device)
182 {
183   GstVaapiDisplayDRMPrivate *const priv =
184       GST_VAAPI_DISPLAY_DRM_PRIVATE (display);
185   const gchar *busid, *path, *str;
186   gsize busid_length, path_length;
187   struct udev *udev = NULL;
188   struct udev_device *device;
189   struct udev_enumerate *e = NULL;
190   struct udev_list_entry *l;
191   gboolean success = FALSE;
192   gint i;
193
194   g_free (priv->device_path);
195   priv->device_path = NULL;
196
197   if (drm_device < 0)
198     goto end;
199
200   busid = drmGetBusid (drm_device);
201   if (!busid)
202     goto end;
203
204   for (i = 0; allowed_subsystems[i] != NULL; i++) {
205     busid_length = strlen (allowed_subsystems[i]);
206
207     if (strncmp (busid, allowed_subsystems[i], busid_length) == 0) {
208       busid += busid_length + 1;
209       busid_length = strlen (busid);
210     }
211   }
212
213   if (allowed_subsystems[i] == NULL)
214     goto end;
215
216   udev = udev_new ();
217   if (!udev)
218     goto end;
219
220   e = udev_enumerate_new (udev);
221   if (!e)
222     goto end;
223
224   udev_enumerate_add_match_subsystem (e, "drm");
225   udev_enumerate_scan_devices (e);
226   udev_list_entry_foreach (l, udev_enumerate_get_list_entry (e)) {
227     path = udev_list_entry_get_name (l);
228     str = strstr (path, busid);
229     if (!str || str <= path || str[-1] != '/')
230       continue;
231
232     path_length = strlen (path);
233     if (str + busid_length >= path + path_length)
234       continue;
235     if (strncmp (&str[busid_length], "/drm/card", 9) != 0 &&
236         strncmp (&str[busid_length], "/drm/renderD", 12) != 0)
237       continue;
238
239     device = udev_device_new_from_syspath (udev, path);
240     if (!device)
241       continue;
242
243     path = udev_device_get_devnode (device);
244     priv->device_path = g_strdup (path);
245     udev_device_unref (device);
246     break;
247   }
248   success = TRUE;
249
250 end:
251   if (e)
252     udev_enumerate_unref (e);
253   if (udev)
254     udev_unref (udev);
255   return success;
256 }
257
258 static gboolean
259 gst_vaapi_display_drm_bind_display (GstVaapiDisplay * display,
260     gpointer native_display)
261 {
262   GstVaapiDisplayDRMPrivate *const priv =
263       GST_VAAPI_DISPLAY_DRM_PRIVATE (display);
264
265   priv->drm_device = GPOINTER_TO_INT (native_display);
266   priv->use_foreign_display = TRUE;
267
268   if (!set_device_path_from_fd (display, priv->drm_device))
269     return FALSE;
270   return TRUE;
271 }
272
273 static gboolean
274 gst_vaapi_display_drm_open_display (GstVaapiDisplay * display,
275     const gchar * name)
276 {
277   GstVaapiDisplayDRMPrivate *const priv =
278       GST_VAAPI_DISPLAY_DRM_PRIVATE (display);
279
280   if (!set_device_path (display, name))
281     return FALSE;
282
283   priv->drm_device = open (get_device_path (display), O_RDWR | O_CLOEXEC);
284   if (priv->drm_device < 0)
285     return FALSE;
286   priv->use_foreign_display = FALSE;
287
288   return TRUE;
289 }
290
291 static void
292 gst_vaapi_display_drm_close_display (GstVaapiDisplay * display)
293 {
294   GstVaapiDisplayDRMPrivate *const priv =
295       GST_VAAPI_DISPLAY_DRM_PRIVATE (display);
296
297   if (priv->drm_device >= 0) {
298     if (!priv->use_foreign_display)
299       close (priv->drm_device);
300     priv->drm_device = -1;
301   }
302
303   g_clear_pointer (&priv->device_path, g_free);
304   g_clear_pointer (&priv->device_path_default, g_free);
305 }
306
307 static gboolean
308 gst_vaapi_display_drm_get_display_info (GstVaapiDisplay * display,
309     GstVaapiDisplayInfo * info)
310 {
311   GstVaapiDisplayDRMPrivate *const priv =
312       GST_VAAPI_DISPLAY_DRM_PRIVATE (display);
313
314   info->native_display = GINT_TO_POINTER (priv->drm_device);
315   info->display_name = priv->device_path;
316   if (!info->va_display) {
317     info->va_display = vaGetDisplayDRM (priv->drm_device);
318     if (!info->va_display)
319       return FALSE;
320   }
321   return TRUE;
322 }
323
324 static GstVaapiWindow *
325 gst_vaapi_display_drm_create_window (GstVaapiDisplay * display, GstVaapiID id,
326     guint width, guint height)
327 {
328   return id != GST_VAAPI_ID_INVALID ?
329       NULL : gst_vaapi_window_drm_new (display, width, height);
330 }
331
332 static void
333 gst_vaapi_display_drm_init (GstVaapiDisplayDRM * display)
334 {
335   GstVaapiDisplayDRMPrivate *const priv =
336       gst_vaapi_display_drm_get_instance_private (display);
337
338   display->priv = priv;
339   priv->drm_device = -1;
340 }
341
342 static void
343 gst_vaapi_display_drm_class_init (GstVaapiDisplayDRMClass * klass)
344 {
345   GstVaapiDisplayClass *const dpy_class = GST_VAAPI_DISPLAY_CLASS (klass);
346
347   dpy_class->display_type = GST_VAAPI_DISPLAY_TYPE_DRM;
348   dpy_class->bind_display = gst_vaapi_display_drm_bind_display;
349   dpy_class->open_display = gst_vaapi_display_drm_open_display;
350   dpy_class->close_display = gst_vaapi_display_drm_close_display;
351   dpy_class->get_display = gst_vaapi_display_drm_get_display_info;
352   dpy_class->create_window = gst_vaapi_display_drm_create_window;
353 }
354
355 /**
356  * gst_vaapi_display_drm_new:
357  * @device_path: the DRM device path
358  *
359  * Opens an DRM file descriptor using @device_path and returns a newly
360  * allocated #GstVaapiDisplay object. The DRM display will be cloed
361  * when the reference count of the object reaches zero.
362  *
363  * If @device_path is NULL, the DRM device path will be automatically
364  * determined as the first positive match in the list of available DRM
365  * devices.
366  *
367  * Return value: a newly allocated #GstVaapiDisplay object
368  */
369 GstVaapiDisplay *
370 gst_vaapi_display_drm_new (const gchar * device_path)
371 {
372   GstVaapiDisplay *display;
373   guint types[2], i, num_types = 0;
374
375   g_mutex_lock (&g_drm_device_type_lock);
376   if (device_path)
377     types[num_types++] = 0;
378   else if (g_drm_device_type)
379     types[num_types++] = g_drm_device_type;
380   else {
381     types[num_types++] = DRM_DEVICE_RENDERNODES;
382     types[num_types++] = DRM_DEVICE_LEGACY;
383   }
384
385   for (i = 0; i < num_types; i++) {
386     g_drm_device_type = types[i];
387     display = g_object_new (GST_TYPE_VAAPI_DISPLAY_DRM, NULL);
388     display = gst_vaapi_display_config (display,
389         GST_VAAPI_DISPLAY_INIT_FROM_DISPLAY_NAME, (gpointer) device_path);
390     if (display || device_path)
391       break;
392   }
393   g_mutex_unlock (&g_drm_device_type_lock);
394   return display;
395 }
396
397 /**
398  * gst_vaapi_display_drm_new_with_device:
399  * @device: an open DRM device (file descriptor)
400  *
401  * Creates a #GstVaapiDisplay based on the open DRM @device. The
402  * caller still owns the device file descriptor and must call close()
403  * when all #GstVaapiDisplay references are released. Doing so too
404  * early can yield undefined behaviour.
405  *
406  * Return value: a newly allocated #GstVaapiDisplay object
407  */
408 GstVaapiDisplay *
409 gst_vaapi_display_drm_new_with_device (gint device)
410 {
411   GstVaapiDisplay *display;
412
413   g_return_val_if_fail (device >= 0, NULL);
414
415   display = g_object_new (GST_TYPE_VAAPI_DISPLAY_DRM, NULL);
416   return gst_vaapi_display_config (display,
417       GST_VAAPI_DISPLAY_INIT_FROM_NATIVE_DISPLAY, GINT_TO_POINTER (device));
418 }
419
420 /**
421  * gst_vaapi_display_drm_get_device:
422  * @display: a #GstVaapiDisplayDRM
423  *
424  * Returns the underlying DRM device file descriptor that was created
425  * by gst_vaapi_display_drm_new() or that was bound from
426  * gst_vaapi_display_drm_new_with_device().
427  *
428  * Return value: the DRM file descriptor attached to @display
429  */
430 gint
431 gst_vaapi_display_drm_get_device (GstVaapiDisplayDRM * display)
432 {
433   g_return_val_if_fail (GST_VAAPI_IS_DISPLAY_DRM (display), -1);
434
435   return GST_VAAPI_DISPLAY_DRM_DEVICE (display);
436 }
437
438 /**
439  * gst_vaapi_display_drm_get_device_path:
440  * @display: a #GstVaapiDisplayDRM
441  *
442  * Returns the underlying DRM device path name was created by
443  * gst_vaapi_display_drm_new() or that was bound from
444  * gst_vaapi_display_drm_new_with_device().
445  *
446  * Note: the #GstVaapiDisplayDRM object owns the resulting string, so
447  * it shall not be deallocated.
448  *
449  * Return value: the DRM device path name attached to @display
450  */
451 const gchar *
452 gst_vaapi_display_drm_get_device_path (GstVaapiDisplayDRM * display)
453 {
454   g_return_val_if_fail (GST_VAAPI_IS_DISPLAY_DRM (display), NULL);
455
456   return get_device_path (GST_VAAPI_DISPLAY_CAST (display));
457 }