Add missing boilerplate
[platform/adaptation/libtdm-drm.git] / src / libhal-backend-tdm-drm / tdm_backend_drm.c
1 /**************************************************************************
2
3 libtdm_drm
4
5 Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved.
6
7 Contact: SooChan Lim <sc1.lim@samsung.com>
8
9 Permission is hereby granted, free of charge, to any person obtaining a
10 copy of this software and associated documentation files (the
11 "Software"), to deal in the Software without restriction, including
12 without limitation the rights to use, copy, modify, merge, publish,
13 distribute, sub license, and/or sell copies of the Software, and to
14 permit persons to whom the Software is furnished to do so, subject to
15 the following conditions:
16
17 The above copyright notice and this permission notice (including the
18 next paragraph) shall be included in all copies or substantial portions
19 of the Software.
20
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
24 IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
25 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
26 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
29 **************************************************************************/
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #include "tdm_backend_drm.h"
36
37 #if HAVE_UDEV
38 #include <libudev.h>
39 #endif
40
41 #define ENABLE_PP
42
43 #define TDM_DRM_NAME "vigs"
44
45 #ifdef HAVE_UDEV
46 static struct udev_device *
47 _tdm_find_primary_gpu(void)
48 {
49         struct udev *udev;
50         struct udev_enumerate *e;
51         struct udev_list_entry *entry;
52         const char *path, *id;
53         struct udev_device *device, *drm_device, *pci;
54
55         udev = udev_new();
56         if (!udev) {
57                 TDM_BACKEND_ERR("fail to initialize udev context\n");
58                 return NULL;
59         }
60
61         e = udev_enumerate_new(udev);
62         udev_enumerate_add_match_subsystem(e, "drm");
63         udev_enumerate_add_match_sysname(e, "card[0-9]*");
64
65         udev_enumerate_scan_devices(e);
66         drm_device = NULL;
67         udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
68                 path = udev_list_entry_get_name(entry);
69                 device = udev_device_new_from_syspath(udev, path);
70                 if (!device)
71                         continue;
72
73                 pci = udev_device_get_parent_with_subsystem_devtype(device,
74                                                 "pci", NULL);
75                 if (pci) {
76                         id = udev_device_get_sysattr_value(pci, "boot_vga");
77                         if (id && !strcmp(id, "1")) {
78                                 if (drm_device)
79                                         udev_device_unref(drm_device);
80                                 drm_device = device;
81                                 break;
82                         }
83                 }
84
85                 if (!drm_device)
86                         drm_device = device;
87                 else
88                         udev_device_unref(device);
89         }
90
91         udev_enumerate_unref(e);
92         return drm_device;
93 }
94
95 static hal_tdm_error
96 _tdm_drm_udev_fd_handler(int fd, hal_tdm_event_loop_mask mask, void *user_data)
97 {
98         tdm_drm_display *display_data = (tdm_drm_display*)user_data;
99         struct udev_device *dev;
100         const char *hotplug;
101         struct stat s;
102         dev_t udev_devnum;
103         int ret;
104
105         dev = udev_monitor_receive_device(display_data->uevent_monitor);
106         if (!dev) {
107                 TDM_BACKEND_ERR("couldn't receive device");
108                 return HAL_TDM_ERROR_OPERATION_FAILED;
109         }
110
111         udev_devnum = udev_device_get_devnum(dev);
112
113         ret = fstat(display_data->drm_fd, &s);
114         if (ret == -1) {
115                 TDM_BACKEND_ERR("fstat failed");
116                 udev_device_unref(dev);
117                 return HAL_TDM_ERROR_OPERATION_FAILED;
118         }
119
120         hotplug = udev_device_get_property_value(dev, "HOTPLUG");
121
122         if (memcmp(&s.st_rdev, &udev_devnum, sizeof(dev_t)) == 0 &&
123                         hotplug && atoi(hotplug) == 1) {
124                 TDM_BACKEND_INFO("HotPlug");
125                 tdm_drm_display_update_output_status(display_data);
126         }
127
128         udev_device_unref(dev);
129
130         return HAL_TDM_ERROR_NONE;
131 }
132
133 static struct udev_monitor *
134 _tdm_drm_create_udev_monitor()
135 {
136         struct udev *u = NULL;
137         struct udev_monitor *mon = NULL;
138
139         u = udev_new();
140         if (!u) {
141                 TDM_BACKEND_ERR("couldn't create udev");
142                 goto failed;
143         }
144
145         mon = udev_monitor_new_from_netlink(u, "udev");
146         if (!mon) {
147                 TDM_BACKEND_ERR("couldn't create udev monitor");
148                 goto failed;
149         }
150
151         if (udev_monitor_filter_add_match_subsystem_devtype(mon, "drm", "drm_minor") > 0 ||
152                 udev_monitor_enable_receiving(mon) < 0) {
153                 TDM_BACKEND_ERR("add match subsystem failed");
154                 goto failed;
155         }
156
157         TDM_BACKEND_INFO("hotplug monitor created");
158
159         return mon;
160
161 failed:
162         if (mon)
163                 udev_monitor_unref(mon);
164         if (u)
165                 udev_unref(u);
166
167         return NULL;
168 }
169
170 static void
171 _tdm_drm_destroy_udev_monitor(struct udev_monitor *mon)
172 {
173         struct udev *u;
174
175         if (!mon)
176                 return;
177
178         u = udev_monitor_get_udev(mon);
179         udev_monitor_unref(mon);
180         udev_unref(u);
181
182         TDM_BACKEND_INFO("hotplug monitor destroyed");
183 }
184 #endif
185
186 static int
187 _tdm_drm_open_drm(void)
188 {
189         int fd = -1;
190
191         fd = drmOpen(TDM_DRM_NAME, NULL);
192         if (fd < 0)
193                 TDM_BACKEND_WRN("Cannot open '%s' drm", TDM_DRM_NAME);
194
195 #ifdef HAVE_UDEV
196         if (fd < 0) {
197                 struct udev_device *drm_device = NULL;
198                 const char *filename;
199                 TDM_BACKEND_WRN("Cannot open drm device.. search by udev");
200
201                 drm_device = _tdm_find_primary_gpu();
202                 if (drm_device == NULL) {
203                         TDM_BACKEND_ERR("fail to find drm device\n");
204                         goto close_l;
205                 }
206
207                 filename = udev_device_get_devnode(drm_device);
208
209                 fd = open(filename, O_RDWR | O_CLOEXEC);
210                 if (fd < 0)
211                         TDM_BACKEND_ERR("Cannot open drm device(%s)\n", filename);
212
213                 TDM_BACKEND_DBG("open drm device (name:%s, fd:%d)", filename, fd);
214
215                 udev_device_unref(drm_device);
216         }
217 close_l:
218 #endif
219         return fd;
220 }
221
222 static void
223 _tdm_drm_display_deinitialize(tdm_drm_display *display_data)
224 {
225         tdm_drm_display_destroy_output_list(display_data);
226
227         if (display_data->plane_res)
228                 drmModeFreePlaneResources(display_data->plane_res);
229         if (display_data->mode_res)
230                 drmModeFreeResources(display_data->mode_res);
231 }
232
233 static hal_tdm_error
234 _tdm_drm_display_initialize(tdm_drm_display *display_data)
235 {
236         hal_tdm_error ret;
237
238 #if LIBDRM_MAJOR_VERSION >= 2 && LIBDRM_MINOR_VERSION >= 4  && LIBDRM_MICRO_VERSION >= 47
239         if (drmSetClientCap(display_data->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) < 0) {
240                 TDM_BACKEND_WRN("Set DRM_CLIENT_CAP_UNIVERSAL_PLANES failed");
241         } else {
242                 TDM_BACKEND_INFO("has universal planes");
243                 display_data->has_universal_plane = 1;
244         }
245 #endif
246
247         display_data->mode_res = drmModeGetResources(display_data->drm_fd);
248         if (!display_data->mode_res) {
249                 TDM_BACKEND_ERR("no drm resource: %m");
250                 ret = HAL_TDM_ERROR_OPERATION_FAILED;
251                 goto failed;
252         }
253
254         display_data->plane_res = drmModeGetPlaneResources(display_data->drm_fd);
255         if (!display_data->plane_res) {
256                 TDM_BACKEND_ERR("no drm plane resource: %m");
257                 ret = HAL_TDM_ERROR_OPERATION_FAILED;
258                 goto failed;
259         }
260
261         if (display_data->plane_res->count_planes <= 0) {
262                 TDM_BACKEND_ERR("no drm plane resource");
263                 ret = HAL_TDM_ERROR_OPERATION_FAILED;
264                 goto failed;
265         }
266
267         ret = tdm_drm_display_create_output_list(display_data);
268         if (ret != HAL_TDM_ERROR_NONE)
269                 goto failed;
270
271         ret = tdm_drm_display_create_layer_list(display_data);
272         if (ret != HAL_TDM_ERROR_NONE)
273                 goto failed;
274
275         return ret;
276
277 failed:
278         _tdm_drm_display_deinitialize(display_data);
279
280         return ret;
281 }
282
283 static hal_tdm_error
284 _tdm_drm_master_drm_fd_handler(hal_tdm_fd master_drm_fd, void *user_data)
285 {
286         tdm_drm_display *display_data = (tdm_drm_display *) user_data;
287         hal_tdm_error ret;
288
289         TDM_BACKEND_RETURN_VAL_IF_FAIL(display_data != NULL, HAL_TDM_ERROR_INVALID_PARAMETER);
290
291         display_data->drm_fd = master_drm_fd;
292         TDM_BACKEND_INFO("Get the master drm_fd(%d)!\n", display_data->drm_fd);
293
294         // initialize display with a master drm_fd
295         ret = _tdm_drm_display_initialize(display_data);
296         if (ret != HAL_TDM_ERROR_NONE) {
297                 TDM_BACKEND_ERR("fail to _tdm_drm_display_initialize!\n");
298                 _tdm_drm_display_deinitialize(display_data);
299
300                 return HAL_TDM_ERROR_OPERATION_FAILED;
301         }
302
303         return HAL_TDM_ERROR_NONE;
304 }
305
306 int
307 hal_backend_tdm_drm_exit(void *data)
308 {
309         hal_tdm_backend_data *backend_data = (hal_tdm_backend_data *)data;
310         tdm_drm_display *display_data;
311
312         TDM_BACKEND_INFO("deinit");
313
314         TDM_BACKEND_RETURN_VAL_IF_FAIL(backend_data != NULL, -1);
315
316         display_data = (tdm_drm_display *)backend_data->display;
317         TDM_BACKEND_RETURN_VAL_IF_FAIL(display_data != NULL, -1);
318
319         if (backend_data->hwc_window_funcs) {
320                 free(backend_data->hwc_window_funcs);
321                 backend_data->hwc_window_funcs = NULL;
322         }
323         if (backend_data->hwc_funcs) {
324                 free(backend_data->hwc_funcs);
325                 backend_data->hwc_funcs = NULL;
326         }
327         if (backend_data->output_funcs) {
328                 free(backend_data->output_funcs);
329                 backend_data->output_funcs = NULL;
330         }
331         if (backend_data->display_funcs) {
332                 free(backend_data->display_funcs);
333                 backend_data->display_funcs = NULL;
334         }
335
336         _tdm_drm_display_deinitialize(display_data);
337
338 #ifdef HAVE_UDEV
339         if (display_data->uevent_monitor)
340                 _tdm_drm_destroy_udev_monitor(display_data->uevent_monitor);
341 #endif
342
343         if (display_data->drm_fd >= 0)
344                 close(display_data->drm_fd);
345
346         free(display_data);
347         display_data = NULL;
348
349         free(backend_data);
350
351         return HAL_TDM_ERROR_NONE;
352 }
353
354 static int
355 hal_backend_tdm_drm_init(void **data)
356 {
357         hal_tdm_backend_data *backend_data = NULL;
358         hal_tdm_display_funcs *display_funcs = NULL;
359         hal_tdm_output_funcs *output_funcs = NULL;
360         hal_tdm_hwc_funcs *hwc_funcs = NULL;
361         hal_tdm_hwc_window_funcs *hwc_window_funcs = NULL;
362         hal_tdm_pp_funcs *pp_funcs = NULL;
363         tdm_drm_display *display_data = NULL;
364         hal_tdm_error ret;
365         int drm_fd;
366 #ifdef HAVE_UDEV
367         static struct udev_monitor *mon;
368         hal_tdm_event_source *udev_event_source;
369 #endif
370 #if 0
371         if (!*data) {
372                 TDM_BACKEND_ERR("*data is null.\n");
373                 return -1;
374         }
375 #endif
376         /* allocate a hal_tdm_backend_data */
377         backend_data = calloc(1, sizeof(struct _hal_tdm_backend_data));
378         if (!backend_data) {
379                 TDM_BACKEND_ERR("fail to alloc backend_data!\n");
380                 *data = NULL;
381                 return -1;
382         }
383         *data = backend_data;
384
385         /* allocate a hal_tdm_display */
386         display_data = calloc(1, sizeof(struct _tdm_drm_display));
387         if (!display_data) {
388                 TDM_BACKEND_ERR("fail to alloc display_data!\n");
389                 goto failed;
390         }
391         backend_data->display = (hal_tdm_display *)display_data;
392
393         LIST_INITHEAD(&display_data->output_list);
394         LIST_INITHEAD(&display_data->buffer_list);
395
396         // check if drm_fd is master fd.
397         drm_fd = _tdm_drm_open_drm();
398         if (drm_fd < 0) {
399                 ret = HAL_TDM_ERROR_OPERATION_FAILED;
400                 goto failed;
401         }
402
403         // set true when backend has a drm_device.
404         backend_data->has_drm_device = 1;
405
406         if (drmIsMaster(drm_fd)) {
407                 // drm_fd is a master drm_fd.
408                 backend_data->drm_info.drm_fd = drm_fd;
409                 backend_data->drm_info.is_master = 1;
410
411                 display_data->drm_fd = drm_fd;
412                 TDM_BACKEND_INFO("Get the master drm_fd(%d)!\n", display_data->drm_fd);
413
414                 // initialize display with a master drm_fd
415                 ret = _tdm_drm_display_initialize(display_data);
416                 if (ret != HAL_TDM_ERROR_NONE) {
417                         TDM_BACKEND_ERR("fail to _tdm_drm_display_initialize!\n");
418                         goto failed;
419                 }
420         } else {
421                 // drm_fd is not a master drm_fd.
422                 // request a master drm_fd
423                 close(drm_fd);
424                 backend_data->drm_info.drm_fd = -1;
425                 backend_data->drm_info.is_master = 0;
426                 backend_data->drm_info.master_drm_fd_func = _tdm_drm_master_drm_fd_handler;
427                 backend_data->drm_info.user_data = display_data;
428
429                 TDM_BACKEND_INFO("A backend requests an master drm_fd.\n");
430         }
431
432 #ifdef HAVE_UDEV
433         mon = _tdm_drm_create_udev_monitor();
434         if (!mon) {
435                 ret = HAL_TDM_ERROR_OPERATION_FAILED;
436                 goto failed;
437         }
438         display_data->uevent_monitor = mon;
439
440         /* alloc and register udev_event_source */
441         udev_event_source = calloc(1, sizeof(struct _hal_tdm_event_source));
442         if (!udev_event_source) {
443                 TDM_BACKEND_ERR("fail to alloc udev_event_source!\n");
444                 goto failed;
445         }
446         udev_event_source->event_fd = udev_monitor_get_fd(mon);
447         udev_event_source->func = _tdm_drm_udev_fd_handler;
448         udev_event_source->user_data = display_data;
449
450         backend_data->event_sources[0] = udev_event_source;
451         backend_data->num_event_sources++;
452 #endif
453
454         /* alloc and register display_funcs */
455         display_funcs = calloc(1, sizeof(struct _hal_tdm_display_funcs));
456         if (!display_funcs) {
457                 TDM_BACKEND_ERR("fail to alloc display_funcs!\n");
458                 goto failed;
459         }
460         backend_data->display_funcs = display_funcs;
461
462         display_funcs->display_get_capability = drm_display_get_capability;
463         display_funcs->display_get_pp_capability = drm_display_get_pp_capability;
464         display_funcs->display_get_outputs = drm_display_get_outputs;
465         display_funcs->display_get_fd = drm_display_get_fd;
466         display_funcs->display_handle_events = drm_display_handle_events;
467         display_funcs->display_create_pp = drm_display_create_pp;
468
469         /* alloc and register output_funcs */
470         output_funcs = calloc(1, sizeof(struct _hal_tdm_output_funcs));
471         if (!output_funcs) {
472                 TDM_BACKEND_ERR("fail to alloc output_funcs!\n");
473                 goto failed;
474         }
475         backend_data->output_funcs = output_funcs;
476
477         output_funcs->output_get_capability = drm_output_get_capability;
478         output_funcs->output_set_property = drm_output_set_property;
479         output_funcs->output_get_property = drm_output_get_property;
480         output_funcs->output_wait_vblank = drm_output_wait_vblank;
481         output_funcs->output_set_vblank_handler = drm_output_set_vblank_handler;
482         output_funcs->output_commit = drm_output_commit;
483         output_funcs->output_set_commit_handler = drm_output_set_commit_handler;
484         output_funcs->output_set_dpms = drm_output_set_dpms;
485         output_funcs->output_get_dpms = drm_output_get_dpms;
486         output_funcs->output_set_mode = drm_output_set_mode;
487         output_funcs->output_get_mode = drm_output_get_mode;
488 #ifdef HAVE_UDEV
489         output_funcs->output_set_status_handler = drm_output_set_status_handler;
490 #endif
491         output_funcs->output_get_hwc = drm_output_get_hwc;
492
493         /* alloc and register hwc_funcs */
494         hwc_funcs = calloc(1, sizeof(struct _hal_tdm_hwc_funcs));
495         if (!hwc_funcs) {
496                 TDM_BACKEND_ERR("fail to alloc hwc_funcs!\n");
497                 goto failed;
498         }
499         backend_data->hwc_funcs = hwc_funcs;
500
501         hwc_funcs->hwc_create_window = drm_hwc_create_window;
502         hwc_funcs->hwc_get_video_supported_formats = drm_hwc_get_video_supported_formats;
503         hwc_funcs->hwc_get_video_available_properties = NULL;
504         hwc_funcs->hwc_get_capabilities = drm_hwc_get_capabilities;
505         hwc_funcs->hwc_get_available_properties = drm_hwc_get_available_properties;
506         hwc_funcs->hwc_get_client_target_buffer_queue = drm_hwc_get_client_target_buffer_queue;
507         hwc_funcs->hwc_set_client_target_buffer = drm_hwc_set_client_target_buffer;
508         hwc_funcs->hwc_validate = drm_hwc_validate;
509         hwc_funcs->hwc_get_changed_composition_types = drm_hwc_get_changed_composition_types;
510         hwc_funcs->hwc_accept_validation = drm_hwc_accept_validation;
511         hwc_funcs->hwc_commit = drm_hwc_commit;
512         hwc_funcs->hwc_set_commit_handler = drm_hwc_set_commit_handler;
513
514         /* alloc and register hwc_window_funcs */
515         hwc_window_funcs = calloc(1, sizeof(struct _hal_tdm_hwc_window_funcs));
516         if (!hwc_funcs) {
517                 TDM_BACKEND_ERR("fail to alloc hwc_window_funcs!\n");
518                 goto failed;
519         }
520         backend_data->hwc_window_funcs = hwc_window_funcs;
521
522         hwc_window_funcs->hwc_window_destroy = drm_hwc_window_destroy;
523         hwc_window_funcs->hwc_window_acquire_buffer_queue = NULL; // no need
524         hwc_window_funcs->hwc_window_release_buffer_queue = NULL; // no need
525         hwc_window_funcs->hwc_window_set_composition_type = drm_hwc_window_set_composition_type;
526         hwc_window_funcs->hwc_window_set_buffer_damage = drm_hwc_window_set_buffer_damage;
527         hwc_window_funcs->hwc_window_set_info = drm_hwc_window_set_info;
528         hwc_window_funcs->hwc_window_set_buffer = drm_hwc_window_set_buffer;
529         hwc_window_funcs->hwc_window_set_property = drm_hwc_window_set_property;
530         hwc_window_funcs->hwc_window_get_property = drm_hwc_window_get_property;
531         hwc_window_funcs->hwc_window_get_constraints = drm_hwc_window_get_constraints;
532         hwc_window_funcs->hwc_window_set_name = drm_hwc_window_set_name;
533         hwc_window_funcs->hwc_window_set_cursor_image = drm_hwc_window_set_cursor_image;
534
535 #ifdef ENABLE_PP
536         /* alloc and register pp_funcs */
537         pp_funcs = calloc(1, sizeof(struct _hal_tdm_pp_funcs));
538         if (!pp_funcs) {
539                 TDM_BACKEND_ERR("fail to alloc pp_funcs!\n");
540                 goto failed;
541         }
542         backend_data->pp_funcs = pp_funcs;
543
544         pp_funcs->pp_destroy = drm_pp_destroy;
545         pp_funcs->pp_set_info = drm_pp_set_info;
546         pp_funcs->pp_attach = drm_pp_attach;
547         pp_funcs->pp_commit = drm_pp_commit;
548         pp_funcs->pp_set_done_handler = drm_pp_set_done_handler;
549 #endif
550
551         TDM_BACKEND_INFO("init success!");
552
553         return HAL_TDM_ERROR_NONE;
554
555 failed:
556         TDM_BACKEND_ERR("init failed!");
557
558         hal_backend_tdm_drm_exit((void *)backend_data);
559
560         return -1;
561 }
562
563 hal_backend hal_backend_tdm_data = {
564         "drm",
565         "Samsung",
566         HAL_ABI_VERSION_TIZEN_6_5,
567         hal_backend_tdm_drm_init,
568         hal_backend_tdm_drm_exit
569 };