a0411596acc3b424e1ab658fb6e50663683080a8
[platform/adaptation/nexell/libtdm-nexell.git] / src / libhal-backend-tdm-nexell / tdm_backend_nexell.c
1 /**************************************************************************
2
3 libtdm_nexell
4
5 Copyright 2017 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_nexell.h"
36
37 #if HAVE_UDEV
38 #include <libudev.h>
39 #endif
40
41 #define ENABLE_PP
42 #define HAL_TDM_NEXELL_ENABLE_HWC 1
43
44 #define HAL_TDM_NEXELL_NAME "nexell"
45
46 #ifdef HAVE_UDEV
47 static struct udev_device *
48 _tdm_find_primary_gpu(void)
49 {
50         struct udev *udev;
51         struct udev_enumerate *e;
52         struct udev_list_entry *entry;
53         const char *path, *id;
54         struct udev_device *device, *drm_device, *pci;
55
56         udev = udev_new();
57         if (!udev) {
58                 TDM_BACKEND_ERR("fail to initialize udev context\n");
59                 return NULL;
60         }
61
62         e = udev_enumerate_new(udev);
63         udev_enumerate_add_match_subsystem(e, "drm");
64         udev_enumerate_add_match_sysname(e, "card[0-9]*");
65
66         udev_enumerate_scan_devices(e);
67         drm_device = NULL;
68         udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
69                 path = udev_list_entry_get_name(entry);
70                 device = udev_device_new_from_syspath(udev, path);
71                 if (!device)
72                         continue;
73
74                 pci = udev_device_get_parent_with_subsystem_devtype(device, "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
93         return drm_device;
94 }
95
96 static hal_tdm_error
97 _tdm_nexell_udev_fd_handler(int fd, hal_tdm_event_loop_mask mask, void *user_data)
98 {
99         tdm_nexell_display *display_data = (tdm_nexell_display*)user_data;
100         struct udev_device *dev;
101         const char *hotplug;
102         struct stat s;
103         dev_t udev_devnum;
104         int ret;
105
106         dev = udev_monitor_receive_device(display_data->uevent_monitor);
107         if (!dev) {
108                 TDM_BACKEND_ERR("couldn't receive device");
109                 return HAL_TDM_ERROR_OPERATION_FAILED;
110         }
111
112         udev_devnum = udev_device_get_devnum(dev);
113
114         ret = fstat(display_data->drm_fd, &s);
115         if (ret == -1) {
116                 TDM_BACKEND_ERR("fstat failed");
117                 udev_device_unref(dev);
118                 return HAL_TDM_ERROR_OPERATION_FAILED;
119         }
120
121         hotplug = udev_device_get_property_value(dev, "HOTPLUG");
122
123         if (memcmp(&s.st_rdev, &udev_devnum, sizeof (dev_t)) == 0 &&
124                 hotplug && atoi(hotplug) == 1)
125         {
126                 TDM_BACKEND_INFO("HotPlug");
127                 tdm_nexell_display_update_output_status(display_data);
128         }
129
130         udev_device_unref(dev);
131
132         return HAL_TDM_ERROR_NONE;
133 }
134
135 static struct udev_monitor *
136 _tdm_nexell_create_udev_monitor()
137 {
138         struct udev *u = NULL;
139         struct udev_monitor *mon = NULL;
140
141         u = udev_new();
142         if (!u) {
143                 TDM_BACKEND_ERR("couldn't create udev");
144                 goto failed;
145         }
146
147         mon = udev_monitor_new_from_netlink(u, "udev");
148         if (!mon) {
149                 TDM_BACKEND_ERR("couldn't create udev monitor");
150                 goto failed;
151         }
152
153         if (udev_monitor_filter_add_match_subsystem_devtype(mon, "drm", "drm_minor") > 0 ||
154                 udev_monitor_enable_receiving(mon) < 0) {
155                 TDM_BACKEND_ERR("add match subsystem failed");
156                 goto failed;
157         }
158
159         TDM_BACKEND_INFO("hotplug monitor created");
160
161         return mon;
162
163 failed:
164         if (mon)
165                 udev_monitor_unref(mon);
166         if (u)
167                 udev_unref(u);
168
169         return NULL;
170 }
171
172 static void
173 _tdm_nexell_destroy_udev_monitor(struct udev_monitor *mon)
174 {
175         struct udev *u;
176
177         if (!mon)
178                 return;
179
180         u = udev_monitor_get_udev(mon);
181         udev_monitor_unref(mon);
182         udev_unref(u);
183
184         TDM_BACKEND_INFO("hotplug monitor destroyed");
185 }
186 #endif
187
188 static int
189 _tdm_nexell_open_drm(void)
190 {
191         int fd = -1;
192
193         fd = drmOpen(HAL_TDM_NEXELL_NAME, NULL);
194         if (fd < 0)
195                 TDM_BACKEND_WRN("Cannot open '%s' drm", HAL_TDM_NEXELL_NAME);
196
197 #ifdef HAVE_UDEV
198         if (fd < 0) {
199                 struct udev_device *drm_device = NULL;
200                 const char *filename;
201                 TDM_BACKEND_WRN("Cannot open drm device.. search by udev");
202
203                 drm_device = _tdm_find_primary_gpu();
204                 if (drm_device == NULL) {
205                         TDM_BACKEND_ERR("fail to find drm device\n");
206                         goto close_l;
207                 }
208
209                 filename = udev_device_get_devnode(drm_device);
210
211                 fd = open(filename, O_RDWR | O_CLOEXEC);
212                 if (fd < 0)
213                         TDM_BACKEND_ERR("Cannot open drm device(%s)\n", filename);
214
215                 TDM_BACKEND_DBG("open drm device (name:%s, fd:%d)", filename, fd);
216
217                 udev_device_unref(drm_device);
218         }
219 close_l:
220 #endif
221
222         return fd;
223 }
224
225 static void
226 _tdm_nexell_display_deinitialize(tdm_nexell_display *display_data)
227 {
228         tdm_nexell_display_destroy_output_list(display_data);
229         tdm_nexell_display_destroy_buffer_list(display_data);
230
231         if (display_data->plane_res)
232                 drmModeFreePlaneResources(display_data->plane_res);
233         if (display_data->mode_res)
234                 drmModeFreeResources(display_data->mode_res);
235 }
236
237 static hal_tdm_error
238 _tdm_nexell_display_initialize(tdm_nexell_display *display_data)
239 {
240         hal_tdm_error ret;
241
242         display_data->scaler_fd = open("/dev/scaler", O_RDWR);
243         if (display_data->scaler_fd < 0)
244                 TDM_BACKEND_ERR("scaler open fail. cannot use pp.");
245
246 #if LIBDRM_MAJOR_VERSION >= 2 && LIBDRM_MINOR_VERSION >= 4  && LIBDRM_MICRO_VERSION >= 47
247         if (drmSetClientCap(display_data->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) < 0) {
248                 TDM_BACKEND_WRN("Set DRM_CLIENT_CAP_UNIVERSAL_PLANES failed");
249         } else {
250                 TDM_BACKEND_INFO("has universal planes");
251                 display_data->has_universal_plane = 1;
252         }
253
254         if (drmSetClientCap(display_data->drm_fd, DRM_CLIENT_CAP_ATOMIC, 1) < 0) {
255                 TDM_BACKEND_WRN("Set DRM_CLIENT_CAP_ATOMIC failed");
256         } else {
257                 TDM_BACKEND_INFO("has atomic");
258                 display_data->has_atomic = 1;
259         }
260 #endif
261
262         display_data->mode_res = drmModeGetResources(display_data->drm_fd);
263         if (!display_data->mode_res) {
264                 TDM_BACKEND_ERR("no drm resource: %m");
265                 ret = HAL_TDM_ERROR_OPERATION_FAILED;
266                 goto failed;
267         }
268
269         display_data->plane_res = drmModeGetPlaneResources(display_data->drm_fd);
270         if (!display_data->plane_res) {
271                 TDM_BACKEND_ERR("no drm plane resource: %m");
272                 ret = HAL_TDM_ERROR_OPERATION_FAILED;
273                 goto failed;
274         }
275
276         if (display_data->plane_res->count_planes <= 0) {
277                 TDM_BACKEND_ERR("no drm plane resource");
278                 ret = HAL_TDM_ERROR_OPERATION_FAILED;
279                 goto failed;
280         }
281
282         ret = tdm_nexell_display_create_output_list(display_data);
283         if (ret != HAL_TDM_ERROR_NONE)
284                 goto failed;
285
286         ret = tdm_nexell_display_create_layer_list(display_data);
287         if (ret != HAL_TDM_ERROR_NONE)
288                 goto failed;
289
290         return ret;
291
292 failed:
293         _tdm_nexell_display_deinitialize(display_data);
294
295         return ret;
296 }
297
298 static hal_tdm_error
299 _tdm_nexell_master_drm_fd_handler(hal_tdm_fd master_drm_fd, void *user_data)
300 {
301         tdm_nexell_display *display_data = (tdm_nexell_display *) user_data;
302         hal_tdm_error ret;
303
304         TDM_BACKEND_RETURN_VAL_IF_FAIL(display_data != NULL, HAL_TDM_ERROR_INVALID_PARAMETER);
305
306         display_data->drm_fd = master_drm_fd;
307         TDM_BACKEND_INFO("Get the master drm_fd(%d)!\n", display_data->drm_fd);
308
309         // initialize display with a master drm_fd
310         ret = _tdm_nexell_display_initialize(display_data);
311         if (ret != HAL_TDM_ERROR_NONE) {
312                 TDM_BACKEND_ERR("fail to _tdm_nexell_display_initialize!\n");
313
314                 return HAL_TDM_ERROR_OPERATION_FAILED;
315         }
316
317         return HAL_TDM_ERROR_NONE;
318 }
319
320 int
321 hal_backend_tdm_nexell_exit(void *data)
322 {
323         hal_tdm_backend_data *backend_data = (hal_tdm_backend_data *)data;
324         tdm_nexell_display *display_data;
325
326         TDM_BACKEND_INFO("deinit");
327
328         TDM_BACKEND_RETURN_VAL_IF_FAIL(backend_data != NULL, -1);
329
330         display_data = (tdm_nexell_display *)backend_data->display;
331         TDM_BACKEND_RETURN_VAL_IF_FAIL(display_data != NULL, -1);
332
333         if (backend_data->pp_funcs) {
334                 free(backend_data->pp_funcs);
335                 backend_data->pp_funcs = NULL;
336         }
337         if (backend_data->hwc_window_funcs) {
338                 free(backend_data->hwc_window_funcs);
339                 backend_data->hwc_window_funcs = NULL;
340         }
341         if (backend_data->hwc_funcs) {
342                 free(backend_data->hwc_funcs);
343                 backend_data->hwc_funcs = NULL;
344         }
345         if (backend_data->output_funcs) {
346                 free(backend_data->output_funcs);
347                 backend_data->output_funcs = NULL;
348         }
349         if (backend_data->display_funcs) {
350                 free(backend_data->display_funcs);
351                 backend_data->display_funcs = NULL;
352         }
353
354         _tdm_nexell_display_deinitialize(display_data);
355
356 #ifdef HAVE_UDEV
357         if (display_data->uevent_monitor)
358                 _tdm_nexell_destroy_udev_monitor(display_data->uevent_monitor);
359 #endif
360
361         if (display_data->drm_fd >= 0)
362                 close(display_data->drm_fd);
363
364         free(display_data);
365         free(backend_data);
366
367         return HAL_TDM_ERROR_NONE;
368 }
369
370 static int
371 hal_backend_tdm_nexell_init(void **data)
372 {
373         hal_tdm_backend_data *backend_data = NULL;
374         hal_tdm_display_funcs *display_funcs = NULL;
375         hal_tdm_output_funcs *output_funcs = NULL;
376         hal_tdm_hwc_funcs *hwc_funcs = NULL;
377         hal_tdm_hwc_window_funcs *hwc_window_funcs = NULL;
378 #ifdef ENABLE_PP
379         hal_tdm_pp_funcs *pp_funcs = NULL;
380 #endif
381         tdm_nexell_display *display_data = NULL;
382         hal_tdm_error ret;
383         int drm_fd;
384 #ifdef HAVE_UDEV
385         static struct udev_monitor *mon;
386         hal_tdm_event_source *udev_event_source;
387 #endif
388
389         /* allocate a hal_tdm_backend_data */
390         backend_data = calloc(1, sizeof(struct _hal_tdm_backend_data));
391         if (!backend_data) {
392                 TDM_BACKEND_ERR("fail to alloc backend_data!\n");
393                 *data = NULL;
394                 return -1;
395         }
396         *data = backend_data;
397
398         /* allocate a hal_tdm_display */
399         display_data = calloc(1, sizeof(struct _tdm_nexell_display));
400         if (!display_data) {
401                 TDM_BACKEND_ERR("fail to alloc display_data!\n");
402                 goto failed;
403         }
404         backend_data->display = (hal_tdm_display *)display_data;
405
406         LIST_INITHEAD(&display_data->output_list);
407         LIST_INITHEAD(&display_data->buffer_list);
408
409         // check if drm_fd is master fd.
410         drm_fd = _tdm_nexell_open_drm();
411         if (drm_fd < 0) {
412                 ret = HAL_TDM_ERROR_OPERATION_FAILED;
413                 goto failed;
414         }
415
416         // set true when backend has a drm_device.
417         backend_data->has_drm_device = 1;
418
419         if (drmIsMaster(drm_fd)) {
420                 // drm_fd is a master drm_fd.
421                 backend_data->drm_info.drm_fd = drm_fd;
422                 backend_data->drm_info.is_master = 1;
423
424                 display_data->drm_fd = drm_fd;
425                 TDM_BACKEND_INFO("Get the master drm_fd(%d)!\n", display_data->drm_fd);
426
427                 // initialize display with a master drm_fd
428                 ret = _tdm_nexell_display_initialize(display_data);
429                 if (ret != HAL_TDM_ERROR_NONE) {
430                         TDM_BACKEND_ERR("fail to _tdm_nexell_display_initialize!\n");
431                         goto failed;
432                 }
433         } else {
434                 // drm_fd is not a master drm_fd.
435                 // request a master drm_fd
436                 close(drm_fd);
437                 backend_data->drm_info.drm_fd = -1;
438                 backend_data->drm_info.is_master = 0;
439                 backend_data->drm_info.master_drm_fd_func = _tdm_nexell_master_drm_fd_handler;
440                 backend_data->drm_info.user_data = display_data;
441
442                 TDM_BACKEND_INFO("A backend requests an master drm_fd.\n");
443         }
444
445 #ifdef HAVE_UDEV
446         mon = _tdm_nexell_create_udev_monitor();
447         if (!mon) {
448                 ret = HAL_TDM_ERROR_OPERATION_FAILED;
449                 goto failed;
450         }
451         display_data->uevent_monitor = mon;
452
453         /* alloc and register udev_event_source */
454         udev_event_source = calloc(1, sizeof(struct _hal_tdm_event_source));
455         if (!udev_event_source) {
456                 TDM_BACKEND_ERR("fail to alloc udev_event_source!\n");
457                 goto failed;
458         }
459         udev_event_source->event_fd = udev_monitor_get_fd(mon);
460         udev_event_source->func = _tdm_nexell_udev_fd_handler;
461         udev_event_source->user_data = display_data;
462
463         backend_data->event_sources[0] = udev_event_source;
464         backend_data->num_event_sources++;
465 #endif
466
467         /* alloc and register display_funcs */
468         display_funcs = calloc(1, sizeof(struct _hal_tdm_display_funcs));
469         if (!display_funcs) {
470                 TDM_BACKEND_ERR("fail to alloc display_funcs!\n");
471                 goto failed;
472         }
473         backend_data->display_funcs = display_funcs;
474
475         display_funcs->display_get_capability = nexell_display_get_capability;
476         display_funcs->display_get_pp_capability = nexell_display_get_pp_capability;
477         display_funcs->display_get_outputs = nexell_display_get_outputs;
478         display_funcs->display_get_fd = nexell_display_get_fd;
479         display_funcs->display_handle_events = nexell_display_handle_events;
480         display_funcs->display_create_pp = nexell_display_create_pp;
481
482         /* alloc and register output_funcs */
483         output_funcs = calloc(1, sizeof(struct _hal_tdm_output_funcs));
484         if (!output_funcs) {
485                 TDM_BACKEND_ERR("fail to alloc output_funcs!\n");
486                 goto failed;
487         }
488         backend_data->output_funcs = output_funcs;
489
490         output_funcs->output_get_capability = nexell_output_get_capability;
491         output_funcs->output_set_property = nexell_output_set_property;
492         output_funcs->output_get_property = nexell_output_get_property;
493         output_funcs->output_wait_vblank = nexell_output_wait_vblank;
494         output_funcs->output_set_vblank_handler = nexell_output_set_vblank_handler;
495         output_funcs->output_set_dpms = nexell_output_set_dpms;
496         output_funcs->output_get_dpms = nexell_output_get_dpms;
497         output_funcs->output_set_mode = nexell_output_set_mode;
498         output_funcs->output_get_mode = nexell_output_get_mode;
499         output_funcs->output_set_mirror = NULL;
500         output_funcs->output_unset_mirror = NULL;
501 #ifdef HAVE_UDEV
502         output_funcs->output_set_status_handler = nexell_output_set_status_handler;
503 #endif
504         output_funcs->output_get_hwc = nexell_output_get_hwc;
505
506         /* alloc and register hwc_funcs */
507         hwc_funcs = calloc(1, sizeof(struct _hal_tdm_hwc_funcs));
508         if (!hwc_funcs) {
509                 TDM_BACKEND_ERR("fail to alloc hwc_funcs!\n");
510                 goto failed;
511         }
512         backend_data->hwc_funcs = hwc_funcs;
513
514         hwc_funcs->hwc_create_window = nexell_hwc_create_window;
515         hwc_funcs->hwc_get_video_supported_formats = nexell_hwc_get_video_supported_formats;
516         hwc_funcs->hwc_get_video_available_properties = NULL;
517         hwc_funcs->hwc_get_capabilities = nexell_hwc_get_capabilities;
518         hwc_funcs->hwc_get_available_properties = nexell_hwc_get_available_properties;
519         hwc_funcs->hwc_get_client_target_buffer_queue = nexell_hwc_get_client_target_buffer_queue;
520         hwc_funcs->hwc_set_client_target_buffer = nexell_hwc_set_client_target_buffer;
521         hwc_funcs->hwc_set_client_target_acquire_fence = NULL;
522         hwc_funcs->hwc_validate = nexell_hwc_validate;
523         hwc_funcs->hwc_get_changed_composition_types = nexell_hwc_get_changed_composition_types;
524         hwc_funcs->hwc_accept_validation = nexell_hwc_accept_validation;
525         hwc_funcs->hwc_commit = nexell_hwc_commit;
526         hwc_funcs->hwc_set_commit_handler = nexell_hwc_set_commit_handler;
527         hwc_funcs->hwc_get_commit_fence = NULL;
528         hwc_funcs->hwc_get_release_fences = NULL;
529
530         /* alloc and register hwc_window_funcs */
531         hwc_window_funcs = calloc(1, sizeof(struct _hal_tdm_hwc_window_funcs));
532         if (!hwc_window_funcs) {
533                 TDM_BACKEND_ERR("fail to alloc hwc_window_funcs!\n");
534                 goto failed;
535         }
536         backend_data->hwc_window_funcs = hwc_window_funcs;
537
538         hwc_window_funcs->hwc_window_destroy = nexell_hwc_window_destroy;
539         hwc_window_funcs->hwc_window_acquire_buffer_queue = nexell_hwc_window_acquire_buffer_queue;
540         hwc_window_funcs->hwc_window_release_buffer_queue = nexell_hwc_window_release_buffer_queue;
541         hwc_window_funcs->hwc_window_set_composition_type = nexell_hwc_window_set_composition_type;
542         hwc_window_funcs->hwc_window_set_buffer_damage = nexell_hwc_window_set_buffer_damage;
543         hwc_window_funcs->hwc_window_set_info = nexell_hwc_window_set_info;
544         hwc_window_funcs->hwc_window_set_buffer = nexell_hwc_window_set_buffer;
545         hwc_window_funcs->hwc_window_set_property = nexell_hwc_window_set_property;
546         hwc_window_funcs->hwc_window_get_property = nexell_hwc_window_get_property;
547         hwc_window_funcs->hwc_window_get_constraints = nexell_hwc_window_get_constraints;
548         hwc_window_funcs->hwc_window_set_name = nexell_hwc_window_set_name;
549         hwc_window_funcs->hwc_window_set_cursor_image = nexell_hwc_window_set_cursor_image;
550
551 #ifdef ENABLE_PP
552         /* alloc and register pp_funcs */
553         pp_funcs = calloc(1, sizeof(struct _hal_tdm_pp_funcs));
554         if (!pp_funcs) {
555                 TDM_BACKEND_ERR("fail to alloc pp_funcs!\n");
556                 goto failed;
557         }
558         backend_data->pp_funcs = pp_funcs;
559
560         pp_funcs->pp_destroy = nexell_pp_destroy;
561         pp_funcs->pp_set_info = nexell_pp_set_info;
562         pp_funcs->pp_attach = nexell_pp_attach;
563         pp_funcs->pp_commit = nexell_pp_commit;
564         pp_funcs->pp_set_done_handler = nexell_pp_set_done_handler;
565 #endif
566
567         TDM_BACKEND_INFO("init success!");
568
569         return HAL_TDM_ERROR_NONE;
570
571 failed:
572         TDM_BACKEND_ERR("init failed!");
573
574         hal_backend_tdm_nexell_exit((void *)backend_data);
575
576         return -1;
577 }
578
579 hal_backend hal_backend_tdm_data = {
580         "nexell",
581         "Samsung",
582         HAL_ABI_VERSION_TIZEN_6_5,
583         hal_backend_tdm_nexell_init,
584         hal_backend_tdm_nexell_exit
585 };