libs: display: drm: fix set_device_path_from_fd
[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 #ifndef MAXPATHLEN
45 #if defined(PATH_MAX)
46 #define MAXPATHLEN PATH_MAX
47 #elif defined(_PC_PATH_MAX)
48 #define MAXPATHLEN sysconf(_PC_PATH_MAX)
49 #else
50 #define MAXPATHLEN 2048
51 #endif
52 #endif
53
54 G_DEFINE_TYPE_WITH_PRIVATE (GstVaapiDisplayDRM, gst_vaapi_display_drm,
55     GST_TYPE_VAAPI_DISPLAY);
56
57 typedef enum
58 {
59   DRM_DEVICE_LEGACY = 1,
60   DRM_DEVICE_RENDERNODES,
61 } DRMDeviceType;
62
63 static DRMDeviceType g_drm_device_type;
64 static GMutex g_drm_device_type_lock;
65 static const gchar *allowed_subsystems[] = { "pci", "platform", NULL };
66
67 static gboolean
68 supports_vaapi (int fd)
69 {
70   gboolean ret;
71   VADisplay va_dpy;
72
73   va_dpy = vaGetDisplayDRM (fd);
74   if (!va_dpy)
75     return FALSE;
76
77   ret = vaapi_initialize (va_dpy);
78   vaTerminate (va_dpy);
79   return ret;
80 }
81
82 /* Get default device path. Actually, the first match in the DRM subsystem */
83 static const gchar *
84 get_default_device_path (GstVaapiDisplay * display)
85 {
86   GstVaapiDisplayDRMPrivate *const priv =
87       GST_VAAPI_DISPLAY_DRM_PRIVATE (display);
88   const gchar *syspath, *devpath;
89   struct udev *udev = NULL;
90   struct udev_device *device, *parent;
91   struct udev_enumerate *e = NULL;
92   struct udev_list_entry *l;
93   gint i;
94   int fd;
95
96   if (!priv->device_path_default) {
97     udev = udev_new ();
98     if (!udev)
99       goto end;
100
101     e = udev_enumerate_new (udev);
102     if (!e)
103       goto end;
104
105     udev_enumerate_add_match_subsystem (e, "drm");
106     switch (g_drm_device_type) {
107       case DRM_DEVICE_LEGACY:
108         udev_enumerate_add_match_sysname (e, "card[0-9]*");
109         break;
110       case DRM_DEVICE_RENDERNODES:
111         udev_enumerate_add_match_sysname (e, "renderD[0-9]*");
112         break;
113       default:
114         GST_ERROR ("unknown drm device type (%d)", g_drm_device_type);
115         goto end;
116     }
117     udev_enumerate_scan_devices (e);
118     udev_list_entry_foreach (l, udev_enumerate_get_list_entry (e)) {
119       syspath = udev_list_entry_get_name (l);
120       device = udev_device_new_from_syspath (udev, syspath);
121       parent = udev_device_get_parent (device);
122
123       for (i = 0; allowed_subsystems[i] != NULL; i++)
124         if (g_strcmp0 (udev_device_get_subsystem (parent),
125                 allowed_subsystems[i]) == 0)
126           break;
127
128       if (allowed_subsystems[i] == NULL) {
129         udev_device_unref (device);
130         continue;
131       }
132
133       devpath = udev_device_get_devnode (device);
134       fd = open (devpath, O_RDWR | O_CLOEXEC);
135       if (fd < 0) {
136         udev_device_unref (device);
137         continue;
138       }
139
140       if (supports_vaapi (fd))
141         priv->device_path_default = g_strdup (devpath);
142       close (fd);
143       udev_device_unref (device);
144       if (priv->device_path_default)
145         break;
146     }
147
148   end:
149     if (e)
150       udev_enumerate_unref (e);
151     if (udev)
152       udev_unref (udev);
153   }
154   return priv->device_path_default;
155 }
156
157 /* Reconstruct a device path without our prefix */
158 static const gchar *
159 get_device_path (GstVaapiDisplay * display)
160 {
161   GstVaapiDisplayDRMPrivate *const priv =
162       GST_VAAPI_DISPLAY_DRM_PRIVATE (display);
163   const gchar *device_path = priv->device_path;
164
165   if (!device_path || *device_path == '\0')
166     return NULL;
167   return device_path;
168 }
169
170 /* Mangle device path with our prefix */
171 static gboolean
172 set_device_path (GstVaapiDisplay * display, const gchar * device_path)
173 {
174   GstVaapiDisplayDRMPrivate *const priv =
175       GST_VAAPI_DISPLAY_DRM_PRIVATE (display);
176
177   g_free (priv->device_path);
178   priv->device_path = NULL;
179
180   if (!device_path) {
181     device_path = get_default_device_path (display);
182     if (!device_path)
183       return FALSE;
184   }
185   priv->device_path = g_strdup (device_path);
186   return priv->device_path != NULL;
187 }
188
189 /* Set device path from file descriptor */
190 static gboolean
191 set_device_path_from_fd (GstVaapiDisplay * display, gint drm_device)
192 {
193   GstVaapiDisplayDRMPrivate *const priv =
194       GST_VAAPI_DISPLAY_DRM_PRIVATE (display);
195   gboolean success = FALSE;
196   gchar fd_name[MAXPATHLEN];
197   GError *error = NULL;
198
199   g_free (priv->device_path);
200   priv->device_path = NULL;
201
202   if (drm_device < 0)
203     goto end;
204
205   sprintf (fd_name, "/proc/%d/fd/%d", getpid (), drm_device);
206   priv->device_path = g_file_read_link (fd_name, &error);
207
208   if (error) {
209     g_error_free (error);
210     goto end;
211   }
212
213   if (g_str_has_prefix (priv->device_path, "/dev/dri/card") ||
214       g_str_has_prefix (priv->device_path, "/dev/dri/renderD"))
215     success = TRUE;
216   else {
217     g_free (priv->device_path);
218     priv->device_path = NULL;
219   }
220
221 end:
222   return success;
223 }
224
225 static gboolean
226 gst_vaapi_display_drm_bind_display (GstVaapiDisplay * display,
227     gpointer native_display)
228 {
229   GstVaapiDisplayDRMPrivate *const priv =
230       GST_VAAPI_DISPLAY_DRM_PRIVATE (display);
231
232   priv->drm_device = GPOINTER_TO_INT (native_display);
233   priv->use_foreign_display = TRUE;
234
235   if (!set_device_path_from_fd (display, priv->drm_device))
236     return FALSE;
237   return TRUE;
238 }
239
240 static gboolean
241 gst_vaapi_display_drm_open_display (GstVaapiDisplay * display,
242     const gchar * name)
243 {
244   GstVaapiDisplayDRMPrivate *const priv =
245       GST_VAAPI_DISPLAY_DRM_PRIVATE (display);
246
247   if (!set_device_path (display, name))
248     return FALSE;
249
250   priv->drm_device = open (get_device_path (display), O_RDWR | O_CLOEXEC);
251   if (priv->drm_device < 0)
252     return FALSE;
253   priv->use_foreign_display = FALSE;
254
255   return TRUE;
256 }
257
258 static void
259 gst_vaapi_display_drm_close_display (GstVaapiDisplay * display)
260 {
261   GstVaapiDisplayDRMPrivate *const priv =
262       GST_VAAPI_DISPLAY_DRM_PRIVATE (display);
263
264   if (priv->drm_device >= 0) {
265     if (!priv->use_foreign_display)
266       close (priv->drm_device);
267     priv->drm_device = -1;
268   }
269
270   g_clear_pointer (&priv->device_path, g_free);
271   g_clear_pointer (&priv->device_path_default, g_free);
272 }
273
274 static gboolean
275 gst_vaapi_display_drm_get_display_info (GstVaapiDisplay * display,
276     GstVaapiDisplayInfo * info)
277 {
278   GstVaapiDisplayDRMPrivate *const priv =
279       GST_VAAPI_DISPLAY_DRM_PRIVATE (display);
280
281   info->native_display = GINT_TO_POINTER (priv->drm_device);
282   info->display_name = priv->device_path;
283   if (!info->va_display) {
284     info->va_display = vaGetDisplayDRM (priv->drm_device);
285     if (!info->va_display)
286       return FALSE;
287   }
288   return TRUE;
289 }
290
291 static GstVaapiWindow *
292 gst_vaapi_display_drm_create_window (GstVaapiDisplay * display, GstVaapiID id,
293     guint width, guint height)
294 {
295   return id != GST_VAAPI_ID_INVALID ?
296       NULL : gst_vaapi_window_drm_new (display, width, height);
297 }
298
299 static void
300 gst_vaapi_display_drm_init (GstVaapiDisplayDRM * display)
301 {
302   GstVaapiDisplayDRMPrivate *const priv =
303       gst_vaapi_display_drm_get_instance_private (display);
304
305   display->priv = priv;
306   priv->drm_device = -1;
307 }
308
309 static void
310 gst_vaapi_display_drm_class_init (GstVaapiDisplayDRMClass * klass)
311 {
312   GstVaapiDisplayClass *const dpy_class = GST_VAAPI_DISPLAY_CLASS (klass);
313
314   dpy_class->display_type = GST_VAAPI_DISPLAY_TYPE_DRM;
315   dpy_class->bind_display = gst_vaapi_display_drm_bind_display;
316   dpy_class->open_display = gst_vaapi_display_drm_open_display;
317   dpy_class->close_display = gst_vaapi_display_drm_close_display;
318   dpy_class->get_display = gst_vaapi_display_drm_get_display_info;
319   dpy_class->create_window = gst_vaapi_display_drm_create_window;
320 }
321
322 /**
323  * gst_vaapi_display_drm_new:
324  * @device_path: the DRM device path
325  *
326  * Opens an DRM file descriptor using @device_path and returns a newly
327  * allocated #GstVaapiDisplay object. The DRM display will be cloed
328  * when the reference count of the object reaches zero.
329  *
330  * If @device_path is NULL, the DRM device path will be automatically
331  * determined as the first positive match in the list of available DRM
332  * devices.
333  *
334  * Return value: a newly allocated #GstVaapiDisplay object
335  */
336 GstVaapiDisplay *
337 gst_vaapi_display_drm_new (const gchar * device_path)
338 {
339   GstVaapiDisplay *display;
340   guint types[2], i, num_types = 0;
341
342   g_mutex_lock (&g_drm_device_type_lock);
343   if (device_path)
344     types[num_types++] = 0;
345   else if (g_drm_device_type)
346     types[num_types++] = g_drm_device_type;
347   else {
348     types[num_types++] = DRM_DEVICE_RENDERNODES;
349     types[num_types++] = DRM_DEVICE_LEGACY;
350   }
351
352   for (i = 0; i < num_types; i++) {
353     g_drm_device_type = types[i];
354     display = g_object_new (GST_TYPE_VAAPI_DISPLAY_DRM, NULL);
355     display = gst_vaapi_display_config (display,
356         GST_VAAPI_DISPLAY_INIT_FROM_DISPLAY_NAME, (gpointer) device_path);
357     if (display || device_path)
358       break;
359   }
360   g_mutex_unlock (&g_drm_device_type_lock);
361   return display;
362 }
363
364 /**
365  * gst_vaapi_display_drm_new_with_device:
366  * @device: an open DRM device (file descriptor)
367  *
368  * Creates a #GstVaapiDisplay based on the open DRM @device. The
369  * caller still owns the device file descriptor and must call close()
370  * when all #GstVaapiDisplay references are released. Doing so too
371  * early can yield undefined behaviour.
372  *
373  * Return value: a newly allocated #GstVaapiDisplay object
374  */
375 GstVaapiDisplay *
376 gst_vaapi_display_drm_new_with_device (gint device)
377 {
378   GstVaapiDisplay *display;
379
380   g_return_val_if_fail (device >= 0, NULL);
381
382   display = g_object_new (GST_TYPE_VAAPI_DISPLAY_DRM, NULL);
383   return gst_vaapi_display_config (display,
384       GST_VAAPI_DISPLAY_INIT_FROM_NATIVE_DISPLAY, GINT_TO_POINTER (device));
385 }
386
387 /**
388  * gst_vaapi_display_drm_get_device:
389  * @display: a #GstVaapiDisplayDRM
390  *
391  * Returns the underlying DRM device file descriptor that was created
392  * by gst_vaapi_display_drm_new() or that was bound from
393  * gst_vaapi_display_drm_new_with_device().
394  *
395  * Return value: the DRM file descriptor attached to @display
396  */
397 gint
398 gst_vaapi_display_drm_get_device (GstVaapiDisplayDRM * display)
399 {
400   g_return_val_if_fail (GST_VAAPI_IS_DISPLAY_DRM (display), -1);
401
402   return GST_VAAPI_DISPLAY_DRM_DEVICE (display);
403 }
404
405 /**
406  * gst_vaapi_display_drm_get_device_path:
407  * @display: a #GstVaapiDisplayDRM
408  *
409  * Returns the underlying DRM device path name was created by
410  * gst_vaapi_display_drm_new() or that was bound from
411  * gst_vaapi_display_drm_new_with_device().
412  *
413  * Note: the #GstVaapiDisplayDRM object owns the resulting string, so
414  * it shall not be deallocated.
415  *
416  * Return value: the DRM device path name attached to @display
417  */
418 const gchar *
419 gst_vaapi_display_drm_get_device_path (GstVaapiDisplayDRM * display)
420 {
421   g_return_val_if_fail (GST_VAAPI_IS_DISPLAY_DRM (display), NULL);
422
423   return get_device_path (GST_VAAPI_DISPLAY_CAST (display));
424 }