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