637cabf55cff94204a0d43fb98ce7631d33b09c0
[platform/adaptation/nexell/libtdm-nexell.git] / src / libtdm-nexell / tdm_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 #if HAVE_UDEV
36 #include <libudev.h>
37 #endif
38
39 #include "tdm_nexell.h"
40 #include <tdm_helper.h>
41 #include <tbm_drm_helper.h>
42
43 #define ENABLE_PP
44 #define TDM_NEXELL_ENABLE_HWC 1
45
46 #define TDM_NEXELL_NAME "nexell"
47
48 static tdm_nexell_data *nexell_data;
49
50 #ifdef HAVE_UDEV
51 static struct udev_device *
52 _tdm_find_primary_gpu(void)
53 {
54         struct udev *udev;
55         struct udev_enumerate *e;
56         struct udev_list_entry *entry;
57         const char *path, *id;
58         struct udev_device *device, *drm_device, *pci;
59
60         udev = udev_new();
61         if (!udev) {
62                 TDM_ERR("fail to initialize udev context\n");
63                 return NULL;
64         }
65
66         e = udev_enumerate_new(udev);
67         udev_enumerate_add_match_subsystem(e, "drm");
68         udev_enumerate_add_match_sysname(e, "card[0-9]*");
69
70         udev_enumerate_scan_devices(e);
71         drm_device = NULL;
72         udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
73                 path = udev_list_entry_get_name(entry);
74                 device = udev_device_new_from_syspath(udev, path);
75                 if (!device)
76                         continue;
77
78                 pci = udev_device_get_parent_with_subsystem_devtype(device,
79                                 "pci", NULL);
80                 if (pci) {
81                         id = udev_device_get_sysattr_value(pci, "boot_vga");
82                         if (id && !strcmp(id, "1")) {
83                                 if (drm_device)
84                                         udev_device_unref(drm_device);
85                                 drm_device = device;
86                                 break;
87                         }
88                 }
89
90                 if (!drm_device)
91                         drm_device = device;
92                 else
93                         udev_device_unref(device);
94         }
95
96         udev_enumerate_unref(e);
97         return drm_device;
98 }
99
100 static tdm_error
101 _tdm_nexell_udev_fd_handler(int fd, tdm_event_loop_mask mask, void *user_data)
102 {
103         tdm_nexell_data *display_data = (tdm_nexell_data*)user_data;
104         struct udev_device *dev;
105         const char *hotplug;
106         struct stat s;
107         dev_t udev_devnum;
108         int ret;
109
110         dev = udev_monitor_receive_device(display_data->uevent_monitor);
111         if (!dev) {
112                 TDM_ERR("couldn't receive device");
113                 return TDM_ERROR_OPERATION_FAILED;
114         }
115
116         udev_devnum = udev_device_get_devnum(dev);
117
118         ret = fstat(display_data->drm_fd, &s);
119         if (ret == -1) {
120                 TDM_ERR("fstat failed");
121                 udev_device_unref(dev);
122                 return TDM_ERROR_OPERATION_FAILED;
123         }
124
125         hotplug = udev_device_get_property_value(dev, "HOTPLUG");
126
127         if (memcmp(&s.st_rdev, &udev_devnum, sizeof (dev_t)) == 0 &&
128                 hotplug && atoi(hotplug) == 1)
129         {
130                 TDM_INFO("HotPlug");
131                 tdm_nexell_display_update_output_status(display_data);
132         }
133
134         udev_device_unref(dev);
135
136         return TDM_ERROR_NONE;
137 }
138
139 static void
140 _tdm_nexell_udev_init(tdm_nexell_data *display_data)
141 {
142         struct udev *u = NULL;
143         struct udev_monitor *mon = NULL;
144
145         u = udev_new();
146         if (!u) {
147                 TDM_ERR("couldn't create udev");
148                 goto failed;
149         }
150
151         mon = udev_monitor_new_from_netlink(u, "udev");
152         if (!mon) {
153                 TDM_ERR("couldn't create udev monitor");
154                 goto failed;
155         }
156
157         if (udev_monitor_filter_add_match_subsystem_devtype(mon, "drm", "drm_minor") > 0 ||
158             udev_monitor_enable_receiving(mon) < 0) {
159                 TDM_ERR("add match subsystem failed");
160                 goto failed;
161         }
162
163         display_data->uevent_source =
164                 tdm_event_loop_add_fd_handler(display_data->dpy, udev_monitor_get_fd(mon),
165                                               TDM_EVENT_LOOP_READABLE,
166                                               _tdm_nexell_udev_fd_handler,
167                                               display_data, NULL);
168         if (!display_data->uevent_source) {
169                 TDM_ERR("couldn't create udev event source");
170                 goto failed;
171         }
172
173         display_data->uevent_monitor = mon;
174
175         TDM_INFO("hotplug monitor created");
176
177         return;
178 failed:
179         if (mon)
180                 udev_monitor_unref(mon);
181         if (u)
182                 udev_unref(u);
183 }
184
185 static void
186 _tdm_nexell_udev_deinit(tdm_nexell_data *display_data)
187 {
188         if (display_data->uevent_source) {
189                 tdm_event_loop_source_remove(display_data->uevent_source);
190                 display_data->uevent_source = NULL;
191         }
192
193         if (display_data->uevent_monitor) {
194                 struct udev *u = udev_monitor_get_udev(display_data->uevent_monitor);
195                 udev_monitor_unref(display_data->uevent_monitor);
196                 udev_unref(u);
197                 display_data->uevent_monitor = NULL;
198                 TDM_INFO("hotplug monitor destroyed");
199         }
200 }
201 #endif
202
203 static int
204 _tdm_nexell_open_drm(void)
205 {
206         int fd = -1;
207
208         fd = drmOpen(TDM_NEXELL_NAME, NULL);
209         if (fd < 0) {
210                 TDM_WRN("Cannot open '%s' drm", TDM_NEXELL_NAME);
211         }
212
213 #ifdef HAVE_UDEV
214         if (fd < 0) {
215                 struct udev_device *drm_device = NULL;
216                 const char *filename;
217                 TDM_WRN("Cannot open drm device.. search by udev");
218
219                 drm_device = _tdm_find_primary_gpu();
220                 if (drm_device == NULL) {
221                         TDM_ERR("fail to find drm device\n");
222                         goto close_l;
223                 }
224
225                 filename = udev_device_get_devnode(drm_device);
226
227                 fd = open(filename, O_RDWR | O_CLOEXEC);
228                 if (fd < 0)
229                         TDM_ERR("Cannot open drm device(%s)\n", filename);
230
231                 TDM_DBG("open drm device (name:%s, fd:%d)", filename, fd);
232
233                 udev_device_unref(drm_device);
234         }
235 close_l:
236 #endif
237         return fd;
238 }
239
240 void
241 tdm_nexell_deinit(tdm_backend_data *bdata)
242 {
243         if (nexell_data != bdata)
244                 return;
245
246         TDM_INFO("deinit");
247
248 #ifdef HAVE_UDEV
249         _tdm_nexell_udev_deinit(nexell_data);
250 #endif
251
252         tdm_nexell_display_destroy_output_list(nexell_data);
253         tdm_nexell_data_destroy_buffer_list(nexell_data);
254
255         if (nexell_data->plane_res)
256                 drmModeFreePlaneResources(nexell_data->plane_res);
257         if (nexell_data->mode_res)
258                 drmModeFreeResources(nexell_data->mode_res);
259         if (nexell_data->drm_fd >= 0)
260                 close(nexell_data->drm_fd);
261         if (nexell_data->scaler_fd >= 0)
262                 close(nexell_data->scaler_fd);
263
264         free(nexell_data);
265         nexell_data = NULL;
266 }
267
268 tdm_backend_data *
269 tdm_nexell_init(tdm_display *dpy, tdm_error *error)
270 {
271         tdm_func_display nexell_func_display;
272         tdm_func_output nexell_func_output;
273         tdm_func_layer nexell_func_layer;
274         tdm_func_hwc nexell_func_hwc;
275         tdm_func_hwc_window nexell_func_hwc_window;
276 #ifdef ENABLE_PP
277         tdm_func_pp nexell_func_pp;
278 #endif
279         tdm_error ret;
280         char *str;
281
282         if (!dpy) {
283                 TDM_ERR("display is null");
284                 if (error)
285                         *error = TDM_ERROR_INVALID_PARAMETER;
286                 return NULL;
287         }
288
289         if (nexell_data) {
290                 TDM_ERR("failed: init twice");
291                 if (error)
292                         *error = TDM_ERROR_BAD_REQUEST;
293                 return NULL;
294         }
295
296         nexell_data = calloc(1, sizeof(tdm_nexell_data));
297         if (!nexell_data) {
298                 TDM_ERR("alloc failed");
299                 if (error)
300                         *error = TDM_ERROR_OUT_OF_MEMORY;
301                 return NULL;
302         }
303
304         str = getenv("TDM_HWC");
305         if (str) {
306                 char *end;
307                 nexell_data->hwc_mode = strtol(str, &end, 10);;
308         }
309
310         /* enable the tdm_hwc */
311         nexell_data->hwc_mode = TDM_NEXELL_ENABLE_HWC;
312
313         LIST_INITHEAD(&nexell_data->output_list);
314         LIST_INITHEAD(&nexell_data->buffer_list);
315
316         memset(&nexell_func_display, 0, sizeof(nexell_func_display));
317         nexell_func_display.display_get_capability = nexell_display_get_capability;
318         nexell_func_display.display_get_pp_capability = nexell_display_get_pp_capability;
319         nexell_func_display.display_get_outputs = nexell_display_get_outputs;
320         nexell_func_display.display_get_fd = nexell_display_get_fd;
321         nexell_func_display.display_handle_events = nexell_display_handle_events;
322         nexell_func_display.display_create_pp = nexell_display_create_pp;
323
324         memset(&nexell_func_output, 0, sizeof(nexell_func_output));
325         nexell_func_output.output_get_capability = nexell_output_get_capability;
326         nexell_func_output.output_get_layers = nexell_output_get_layers;
327         nexell_func_output.output_set_property = nexell_output_set_property;
328         nexell_func_output.output_get_property = nexell_output_get_property;
329         nexell_func_output.output_wait_vblank = nexell_output_wait_vblank;
330         nexell_func_output.output_set_vblank_handler = nexell_output_set_vblank_handler;
331         nexell_func_output.output_commit = nexell_output_commit;
332         nexell_func_output.output_set_commit_handler = nexell_output_set_commit_handler;
333         nexell_func_output.output_set_dpms = nexell_output_set_dpms;
334         nexell_func_output.output_get_dpms = nexell_output_get_dpms;
335         nexell_func_output.output_set_mode = nexell_output_set_mode;
336         nexell_func_output.output_get_mode = nexell_output_get_mode;
337         nexell_func_output.output_set_mirror = NULL;
338         nexell_func_output.output_unset_mirror = NULL;
339 #ifdef HAVE_UDEV
340         nexell_func_output.output_set_status_handler = nexell_output_set_status_handler;
341 #endif
342
343         if (nexell_data->hwc_mode) {
344                 nexell_func_output.output_get_hwc = nexell_output_get_hwc;
345
346                 memset(&nexell_func_hwc, 0, sizeof(nexell_func_hwc));
347                 nexell_func_hwc.hwc_create_window = nexell_hwc_create_window;
348                 nexell_func_hwc.hwc_get_video_supported_formats = nexell_hwc_get_video_supported_formats;
349                 nexell_func_hwc.hwc_get_video_available_properties = NULL;
350                 nexell_func_hwc.hwc_get_capabilities = nexell_hwc_get_capabilities;
351                 nexell_func_hwc.hwc_get_available_properties = nexell_hwc_get_available_properties;
352                 nexell_func_hwc.hwc_get_client_target_buffer_queue = nexell_hwc_get_client_target_buffer_queue;
353                 nexell_func_hwc.hwc_set_client_target_buffer = nexell_hwc_set_client_target_buffer;
354                 nexell_func_hwc.hwc_set_client_target_acquire_fence = NULL;
355                 nexell_func_hwc.hwc_validate = nexell_hwc_validate;
356                 nexell_func_hwc.hwc_get_changed_composition_types = nexell_hwc_get_changed_composition_types;
357                 nexell_func_hwc.hwc_accept_validation = nexell_hwc_accept_validation;
358                 nexell_func_hwc.hwc_commit = nexell_hwc_commit;
359                 nexell_func_hwc.hwc_set_commit_handler = nexell_hwc_set_commit_handler;
360                 nexell_func_hwc.hwc_get_commit_fence = NULL;
361                 nexell_func_hwc.hwc_get_release_fences = NULL;
362
363                 memset(&nexell_func_hwc_window, 0, sizeof(nexell_func_hwc_window));
364                 nexell_func_hwc_window.hwc_window_destroy = nexell_hwc_window_destroy;
365                 nexell_func_hwc_window.hwc_window_acquire_buffer_queue = nexell_hwc_window_acquire_buffer_queue;
366                 nexell_func_hwc_window.hwc_window_release_buffer_queue = nexell_hwc_window_release_buffer_queue;
367                 nexell_func_hwc_window.hwc_window_set_composition_type = nexell_hwc_window_set_composition_type;
368                 nexell_func_hwc_window.hwc_window_set_buffer_damage = nexell_hwc_window_set_buffer_damage;
369                 nexell_func_hwc_window.hwc_window_set_info = nexell_hwc_window_set_info;
370                 nexell_func_hwc_window.hwc_window_set_buffer = nexell_hwc_window_set_buffer;
371                 nexell_func_hwc_window.hwc_window_set_property = nexell_hwc_window_set_property;
372                 nexell_func_hwc_window.hwc_window_get_property = nexell_hwc_window_get_property;
373                 nexell_func_hwc_window.hwc_window_get_constraints = nexell_hwc_window_get_constraints;
374                 nexell_func_hwc_window.hwc_window_set_name = nexell_hwc_window_set_name;
375                 nexell_func_hwc_window.hwc_window_set_cursor_image = nexell_hwc_window_set_cursor_image;
376         }
377
378         memset(&nexell_func_layer, 0, sizeof(nexell_func_layer));
379         nexell_func_layer.layer_get_capability = nexell_layer_get_capability;
380         nexell_func_layer.layer_set_property = nexell_layer_set_property;
381         nexell_func_layer.layer_get_property = nexell_layer_get_property;
382         nexell_func_layer.layer_set_info = nexell_layer_set_info;
383         nexell_func_layer.layer_get_info = nexell_layer_get_info;
384         nexell_func_layer.layer_set_buffer = nexell_layer_set_buffer;
385         nexell_func_layer.layer_unset_buffer = nexell_layer_unset_buffer;
386
387 #ifdef ENABLE_PP
388         memset(&nexell_func_pp, 0, sizeof(nexell_func_pp));
389         nexell_func_pp.pp_destroy = nexell_pp_destroy;
390         nexell_func_pp.pp_set_info = nexell_pp_set_info;
391         nexell_func_pp.pp_attach = nexell_pp_attach;
392         nexell_func_pp.pp_commit = nexell_pp_commit;
393         nexell_func_pp.pp_set_done_handler = nexell_pp_set_done_handler;
394 #endif
395
396         ret = tdm_backend_register_func_display(dpy, &nexell_func_display);
397         if (ret != TDM_ERROR_NONE)
398                 goto failed;
399
400         ret = tdm_backend_register_func_output(dpy, &nexell_func_output);
401         if (ret != TDM_ERROR_NONE)
402                 goto failed;
403
404         if (nexell_data->hwc_mode) {
405                 ret = tdm_backend_register_func_hwc(dpy, &nexell_func_hwc);
406                 if (ret != TDM_ERROR_NONE)
407                         goto failed;
408
409                 ret = tdm_backend_register_func_hwc_window(dpy, &nexell_func_hwc_window);
410                 if (ret != TDM_ERROR_NONE)
411                         goto failed;
412         }
413
414         ret = tdm_backend_register_func_layer(dpy, &nexell_func_layer);
415         if (ret != TDM_ERROR_NONE)
416                 goto failed;
417
418 #ifdef ENABLE_PP
419         ret = tdm_backend_register_func_pp(dpy, &nexell_func_pp);
420         if (ret != TDM_ERROR_NONE)
421                 goto failed;
422 #endif
423
424         nexell_data->dpy = dpy;
425
426         /* The drm master fd can be opened by a tbm backend module in
427          * tbm_bufmgr_init() time. In this case, we just get it from tbm.
428          */
429         nexell_data->drm_fd = tbm_drm_helper_get_master_fd();
430         if (nexell_data->drm_fd < 0) {
431                 nexell_data->drm_fd = _tdm_nexell_open_drm();
432
433                 if (nexell_data->drm_fd < 0) {
434                         ret = TDM_ERROR_OPERATION_FAILED;
435                         goto failed;
436                 }
437
438                 tbm_drm_helper_set_tbm_master_fd(nexell_data->drm_fd);
439         }
440
441         TDM_INFO("master fd(%d)", nexell_data->drm_fd);
442
443         nexell_data->scaler_fd = open("/dev/scaler", O_RDWR);
444         if (nexell_data->scaler_fd < 0)
445                 TDM_ERR("scaler open fail. cannot use pp.");
446
447 #ifdef HAVE_UDEV
448         _tdm_nexell_udev_init(nexell_data);
449 #endif
450
451 #if LIBDRM_MAJOR_VERSION >= 2 && LIBDRM_MINOR_VERSION >= 4  && LIBDRM_MICRO_VERSION >= 47
452         if (drmSetClientCap(nexell_data->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) < 0) {
453                 TDM_WRN("Set DRM_CLIENT_CAP_UNIVERSAL_PLANES failed");
454         } else {
455                 TDM_INFO("has universal planes");
456                 nexell_data->has_universal_plane = 1;
457         }
458
459         if (drmSetClientCap(nexell_data->drm_fd, DRM_CLIENT_CAP_ATOMIC, 1) < 0) {
460                 TDM_WRN("Set DRM_CLIENT_CAP_ATOMIC failed");
461         } else {
462                 TDM_INFO("has atomic");
463                 nexell_data->has_atomic = 1;
464         }
465 #endif
466
467         nexell_data->mode_res = drmModeGetResources(nexell_data->drm_fd);
468         if (!nexell_data->mode_res) {
469                 TDM_ERR("no drm resource: %m");
470                 ret = TDM_ERROR_OPERATION_FAILED;
471                 goto failed;
472         }
473
474         nexell_data->plane_res = drmModeGetPlaneResources(nexell_data->drm_fd);
475         if (!nexell_data->plane_res) {
476                 TDM_ERR("no drm plane resource: %m");
477                 ret = TDM_ERROR_OPERATION_FAILED;
478                 goto failed;
479         }
480
481         if (nexell_data->plane_res->count_planes <= 0) {
482                 TDM_ERR("no drm plane resource");
483                 ret = TDM_ERROR_OPERATION_FAILED;
484                 goto failed;
485         }
486
487         ret = tdm_nexell_display_create_output_list(nexell_data);
488         if (ret != TDM_ERROR_NONE)
489                 goto failed;
490
491         ret = tdm_nexell_display_create_layer_list(nexell_data);
492         if (ret != TDM_ERROR_NONE)
493                 goto failed;
494
495         if (error)
496                 *error = TDM_ERROR_NONE;
497
498         TDM_INFO("init success!");
499
500         return (tdm_backend_data *)nexell_data;
501 failed:
502         if (error)
503                 *error = ret;
504
505         tdm_nexell_deinit(nexell_data);
506
507         TDM_ERR("init failed!");
508         return NULL;
509 }
510
511 tdm_backend_module tdm_backend_module_data = {
512         "drm",
513         "Samsung",
514         TDM_BACKEND_SET_ABI_VERSION(2, 0),
515         tdm_nexell_init,
516         tdm_nexell_deinit
517 };