845477f289f680691cd3e5ce609acd24bf80c470
[platform/adaptation/libtdm-drm.git] / src / libtdm-drm / tdm_drm.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5 #if HAVE_UDEV
6 #include <libudev.h>
7 #endif
8
9 #include "tdm_drm.h"
10 #include <tdm_helper.h>
11 #include <tbm_drm_helper.h>
12
13 #define ENABLE_PP
14
15 #define TDM_DRM_NAME "vigs"
16
17 #define TDM_HWC 1
18
19 static tdm_drm_data *drm_data;
20
21 #ifdef HAVE_UDEV
22 static struct udev_device *
23 _tdm_find_primary_gpu(void)
24 {
25         struct udev *udev;
26         struct udev_enumerate *e;
27         struct udev_list_entry *entry;
28         const char *path, *id;
29         struct udev_device *device, *drm_device, *pci;
30
31         udev = udev_new();
32         if (!udev) {
33                 TDM_ERR("fail to initialize udev context\n");
34                 return NULL;
35         }
36
37         e = udev_enumerate_new(udev);
38         udev_enumerate_add_match_subsystem(e, "drm");
39         udev_enumerate_add_match_sysname(e, "card[0-9]*");
40
41         udev_enumerate_scan_devices(e);
42         drm_device = NULL;
43         udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
44                 path = udev_list_entry_get_name(entry);
45                 device = udev_device_new_from_syspath(udev, path);
46                 if (!device)
47                         continue;
48
49                 pci = udev_device_get_parent_with_subsystem_devtype(device,
50                                                 "pci", NULL);
51                 if (pci) {
52                         id = udev_device_get_sysattr_value(pci, "boot_vga");
53                         if (id && !strcmp(id, "1")) {
54                                 if (drm_device)
55                                         udev_device_unref(drm_device);
56                                 drm_device = device;
57                                 break;
58                         }
59                 }
60
61                 if (!drm_device)
62                         drm_device = device;
63                 else
64                         udev_device_unref(device);
65         }
66
67         udev_enumerate_unref(e);
68         return drm_device;
69 }
70
71 static tdm_error
72 _tdm_drm_udev_fd_handler(int fd, tdm_event_loop_mask mask, void *user_data)
73 {
74         tdm_drm_data *edata = (tdm_drm_data*)user_data;
75         struct udev_device *dev;
76         const char *hotplug;
77         struct stat s;
78         dev_t udev_devnum;
79         int ret;
80
81         dev = udev_monitor_receive_device(edata->uevent_monitor);
82         if (!dev) {
83                 TDM_ERR("couldn't receive device");
84                 return TDM_ERROR_OPERATION_FAILED;
85         }
86
87         udev_devnum = udev_device_get_devnum(dev);
88
89         ret = fstat(edata->drm_fd, &s);
90         if (ret == -1) {
91                 TDM_ERR("fstat failed");
92                 udev_device_unref(dev);
93                 return TDM_ERROR_OPERATION_FAILED;
94         }
95
96         hotplug = udev_device_get_property_value(dev, "HOTPLUG");
97
98         if (memcmp(&s.st_rdev, &udev_devnum, sizeof(dev_t)) == 0 &&
99                         hotplug && atoi(hotplug) == 1) {
100                 TDM_INFO("HotPlug");
101                 tdm_drm_display_update_output_status(edata);
102         }
103
104         udev_device_unref(dev);
105
106         return TDM_ERROR_NONE;
107 }
108
109 static void
110 _tdm_drm_udev_init(tdm_drm_data *edata)
111 {
112         struct udev *u = NULL;
113         struct udev_monitor *mon = NULL;
114
115         u = udev_new();
116         if (!u) {
117                 TDM_ERR("couldn't create udev");
118                 goto failed;
119         }
120
121         mon = udev_monitor_new_from_netlink(u, "udev");
122         if (!mon) {
123                 TDM_ERR("couldn't create udev monitor");
124                 goto failed;
125         }
126
127         if (udev_monitor_filter_add_match_subsystem_devtype(mon, "drm", "drm_minor") > 0 ||
128                 udev_monitor_enable_receiving(mon) < 0) {
129                 TDM_ERR("add match subsystem failed");
130                 goto failed;
131         }
132
133         edata->uevent_source =
134                 tdm_event_loop_add_fd_handler(edata->dpy, udev_monitor_get_fd(mon),
135                                                                           TDM_EVENT_LOOP_READABLE,
136                                                                           _tdm_drm_udev_fd_handler,
137                                                                           edata, NULL);
138         if (!edata->uevent_source) {
139                 TDM_ERR("couldn't create udev event source");
140                 goto failed;
141         }
142
143         edata->uevent_monitor = mon;
144
145         TDM_INFO("hotplug monitor created");
146
147         return;
148 failed:
149         if (mon)
150                 udev_monitor_unref(mon);
151         if (u)
152                 udev_unref(u);
153 }
154
155 static void
156 _tdm_drm_udev_deinit(tdm_drm_data *edata)
157 {
158         if (edata->uevent_source) {
159                 tdm_event_loop_source_remove(edata->uevent_source);
160                 edata->uevent_source = NULL;
161         }
162
163         if (edata->uevent_monitor) {
164                 struct udev *u = udev_monitor_get_udev(edata->uevent_monitor);
165                 udev_monitor_unref(edata->uevent_monitor);
166                 udev_unref(u);
167                 edata->uevent_monitor = NULL;
168                 TDM_INFO("hotplug monitor destroyed");
169         }
170 }
171 #endif
172
173 static int
174 _tdm_drm_open_drm(void)
175 {
176         int fd = -1;
177
178         fd = drmOpen(TDM_DRM_NAME, NULL);
179         if (fd < 0)
180                 TDM_WRN("Cannot open '%s' drm", TDM_DRM_NAME);
181
182 #ifdef HAVE_UDEV
183         if (fd < 0) {
184                 struct udev_device *drm_device = NULL;
185                 const char *filename;
186                 TDM_WRN("Cannot open drm device.. search by udev");
187
188                 drm_device = _tdm_find_primary_gpu();
189                 if (drm_device == NULL) {
190                         TDM_ERR("fail to find drm device\n");
191                         goto close_l;
192                 }
193
194                 filename = udev_device_get_devnode(drm_device);
195
196                 fd = open(filename, O_RDWR | O_CLOEXEC);
197                 if (fd < 0)
198                         TDM_ERR("Cannot open drm device(%s)\n", filename);
199
200                 TDM_DBG("open drm device (name:%s, fd:%d)", filename, fd);
201
202                 udev_device_unref(drm_device);
203         }
204 close_l:
205 #endif
206         return fd;
207 }
208
209 void
210 tdm_drm_deinit(tdm_backend_data *bdata)
211 {
212         if (drm_data != bdata)
213                 return;
214
215         TDM_INFO("deinit");
216
217 #ifdef HAVE_UDEV
218         _tdm_drm_udev_deinit(drm_data);
219 #endif
220
221         tdm_drm_display_destroy_output_list(drm_data);
222
223         if (drm_data->plane_res)
224                 drmModeFreePlaneResources(drm_data->plane_res);
225         if (drm_data->mode_res)
226                 drmModeFreeResources(drm_data->mode_res);
227         if (drm_data->drm_fd >= 0)
228                 close(drm_data->drm_fd);
229
230         free(drm_data);
231         drm_data = NULL;
232 }
233
234 tdm_backend_data *
235 tdm_drm_init(tdm_display *dpy, tdm_error *error)
236 {
237         tdm_func_display drm_func_display;
238         tdm_func_output drm_func_output;
239         tdm_func_layer drm_func_layer;
240         tdm_func_hwc drm_func_hwc;
241         tdm_func_hwc_window drm_func_hwc_window;
242 #ifdef ENABLE_PP
243         tdm_func_pp drm_func_pp;
244 #endif
245         tdm_error ret;
246
247         if (!dpy) {
248                 TDM_ERR("display is null");
249                 if (error)
250                         *error = TDM_ERROR_INVALID_PARAMETER;
251                 return NULL;
252         }
253
254         if (drm_data) {
255                 TDM_ERR("failed: init twice");
256                 if (error)
257                         *error = TDM_ERROR_BAD_REQUEST;
258                 return NULL;
259         }
260
261         drm_data = calloc(1, sizeof(tdm_drm_data));
262         if (!drm_data) {
263                 TDM_ERR("alloc failed");
264                 if (error)
265                         *error = TDM_ERROR_OUT_OF_MEMORY;
266                 return NULL;
267         }
268
269 #if TDM_HWC
270         /* enable the tdm_hwc */
271         drm_data->hwc_mode = 1;
272 #endif
273
274         LIST_INITHEAD(&drm_data->output_list);
275         LIST_INITHEAD(&drm_data->buffer_list);
276
277         memset(&drm_func_display, 0, sizeof(drm_func_display));
278         drm_func_display.display_get_capability = drm_display_get_capability;
279         drm_func_display.display_get_pp_capability = drm_display_get_pp_capability;
280         drm_func_display.display_get_outputs = drm_display_get_outputs;
281         drm_func_display.display_get_fd = drm_display_get_fd;
282         drm_func_display.display_handle_events = drm_display_handle_events;
283         drm_func_display.display_create_pp = drm_display_create_pp;
284
285         memset(&drm_func_output, 0, sizeof(drm_func_output));
286         drm_func_output.output_get_capability = drm_output_get_capability;
287         drm_func_output.output_get_layers = drm_output_get_layers;
288         drm_func_output.output_set_property = drm_output_set_property;
289         drm_func_output.output_get_property = drm_output_get_property;
290         drm_func_output.output_wait_vblank = drm_output_wait_vblank;
291         drm_func_output.output_set_vblank_handler = drm_output_set_vblank_handler;
292         drm_func_output.output_commit = drm_output_commit;
293         drm_func_output.output_set_commit_handler = drm_output_set_commit_handler;
294         drm_func_output.output_set_dpms = drm_output_set_dpms;
295         drm_func_output.output_get_dpms = drm_output_get_dpms;
296         drm_func_output.output_set_mode = drm_output_set_mode;
297         drm_func_output.output_get_mode = drm_output_get_mode;
298 #ifdef HAVE_UDEV
299         drm_func_output.output_set_status_handler = drm_output_set_status_handler;
300 #endif
301
302         if (drm_data->hwc_mode) {
303                 drm_func_output.output_get_hwc = drm_output_get_hwc;
304
305                 memset(&drm_func_hwc, 0, sizeof(drm_func_hwc));
306                 drm_func_hwc.hwc_create_window = drm_hwc_create_window;
307                 drm_func_hwc.hwc_get_video_supported_formats = drm_hwc_get_video_supported_formats;
308                 drm_func_hwc.hwc_get_video_available_properties = NULL;
309                 drm_func_hwc.hwc_get_capabilities = drm_hwc_get_capabilities;
310                 drm_func_hwc.hwc_get_available_properties = drm_hwc_get_available_properties;
311                 drm_func_hwc.hwc_get_client_target_buffer_queue = drm_hwc_get_client_target_buffer_queue;
312                 drm_func_hwc.hwc_set_client_target_buffer = drm_hwc_set_client_target_buffer;
313                 drm_func_hwc.hwc_validate = drm_hwc_validate;
314                 drm_func_hwc.hwc_get_changed_composition_types = drm_hwc_get_changed_composition_types;
315                 drm_func_hwc.hwc_accept_validation = drm_hwc_accept_validation;
316                 drm_func_hwc.hwc_commit = drm_hwc_commit;
317                 drm_func_hwc.hwc_set_commit_handler = drm_hwc_set_commit_handler;
318
319                 memset(&drm_func_hwc_window, 0, sizeof(drm_func_hwc_window));
320                 drm_func_hwc_window.hwc_window_destroy = drm_hwc_window_destroy;
321                 drm_func_hwc_window.hwc_window_acquire_buffer_queue = NULL; // no need
322                 drm_func_hwc_window.hwc_window_release_buffer_queue = NULL; // no need
323                 drm_func_hwc_window.hwc_window_set_composition_type = drm_hwc_window_set_composition_type;
324                 drm_func_hwc_window.hwc_window_set_buffer_damage = drm_hwc_window_set_buffer_damage;
325                 drm_func_hwc_window.hwc_window_set_info = drm_hwc_window_set_info;
326                 drm_func_hwc_window.hwc_window_set_buffer = drm_hwc_window_set_buffer;
327                 drm_func_hwc_window.hwc_window_set_property = drm_hwc_window_set_property;
328                 drm_func_hwc_window.hwc_window_get_property = drm_hwc_window_get_property;
329                 drm_func_hwc_window.hwc_window_get_constraints = drm_hwc_window_get_constraints;
330                 drm_func_hwc_window.hwc_window_set_name = drm_hwc_window_set_name;
331                 drm_func_hwc_window.hwc_window_set_cursor_image = drm_hwc_window_set_cursor_image;
332         }
333
334         memset(&drm_func_layer, 0, sizeof(drm_func_layer));
335         drm_func_layer.layer_get_capability = drm_layer_get_capability;
336         drm_func_layer.layer_set_property = drm_layer_set_property;
337         drm_func_layer.layer_get_property = drm_layer_get_property;
338         drm_func_layer.layer_set_info = drm_layer_set_info;
339         drm_func_layer.layer_get_info = drm_layer_get_info;
340         drm_func_layer.layer_set_buffer = drm_layer_set_buffer;
341         drm_func_layer.layer_unset_buffer = drm_layer_unset_buffer;
342
343 #ifdef ENABLE_PP
344         memset(&drm_func_pp, 0, sizeof(drm_func_pp));
345         drm_func_pp.pp_destroy = drm_pp_destroy;
346         drm_func_pp.pp_set_info = drm_pp_set_info;
347         drm_func_pp.pp_attach = drm_pp_attach;
348         drm_func_pp.pp_commit = drm_pp_commit;
349         drm_func_pp.pp_set_done_handler = drm_pp_set_done_handler;
350 #endif
351
352         ret = tdm_backend_register_func_display(dpy, &drm_func_display);
353         if (ret != TDM_ERROR_NONE)
354                 goto failed;
355
356         ret = tdm_backend_register_func_output(dpy, &drm_func_output);
357         if (ret != TDM_ERROR_NONE)
358                 goto failed;
359
360         ret = tdm_backend_register_func_layer(dpy, &drm_func_layer);
361         if (ret != TDM_ERROR_NONE)
362                 goto failed;
363
364         if (drm_data->hwc_mode) {
365                 ret = tdm_backend_register_func_hwc(dpy, &drm_func_hwc);
366                 if (ret != TDM_ERROR_NONE)
367                         goto failed;
368
369                 ret = tdm_backend_register_func_hwc_window(dpy, &drm_func_hwc_window);
370                 if (ret != TDM_ERROR_NONE)
371                         goto failed;
372         }
373
374 #ifdef ENABLE_PP
375         ret = tdm_backend_register_func_pp(dpy, &drm_func_pp);
376         if (ret != TDM_ERROR_NONE)
377                 goto failed;
378 #endif
379
380         drm_data->dpy = dpy;
381
382         /* The drm master fd can be opened by a tbm backend module in
383          * tbm_bufmgr_init() time. In this case, we just get it from tbm.
384          */
385         drm_data->drm_fd = tbm_drm_helper_get_master_fd();
386         if (drm_data->drm_fd < 0) {
387                 drm_data->drm_fd = _tdm_drm_open_drm();
388
389                 if (drm_data->drm_fd < 0) {
390                         ret = TDM_ERROR_OPERATION_FAILED;
391                         goto failed;
392                 }
393
394                 tbm_drm_helper_set_tbm_master_fd(drm_data->drm_fd);
395         }
396
397         TDM_INFO("master fd: %d", drm_data->drm_fd);
398
399 #ifdef HAVE_UDEV
400         _tdm_drm_udev_init(drm_data);
401 #endif
402
403 #if LIBDRM_MAJOR_VERSION >= 2 && LIBDRM_MINOR_VERSION >= 4  && LIBDRM_MICRO_VERSION >= 47
404         if (drmSetClientCap(drm_data->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) < 0) {
405                 TDM_WRN("Set DRM_CLIENT_CAP_UNIVERSAL_PLANES failed");
406         } else {
407                 TDM_INFO("has universal planes");
408                 drm_data->has_universal_plane = 1;
409         }
410 #endif
411
412         drm_data->mode_res = drmModeGetResources(drm_data->drm_fd);
413         if (!drm_data->mode_res) {
414                 TDM_ERR("no drm resource: %m");
415                 ret = TDM_ERROR_OPERATION_FAILED;
416                 goto failed;
417         }
418
419         drm_data->plane_res = drmModeGetPlaneResources(drm_data->drm_fd);
420         if (!drm_data->plane_res) {
421                 TDM_ERR("no drm plane resource: %m");
422                 ret = TDM_ERROR_OPERATION_FAILED;
423                 goto failed;
424         }
425
426         if (drm_data->plane_res->count_planes <= 0) {
427                 TDM_ERR("no drm plane resource");
428                 ret = TDM_ERROR_OPERATION_FAILED;
429                 goto failed;
430         }
431
432         ret = tdm_drm_display_create_output_list(drm_data);
433         if (ret != TDM_ERROR_NONE)
434                 goto failed;
435
436         ret = tdm_drm_display_create_layer_list(drm_data);
437         if (ret != TDM_ERROR_NONE)
438                 goto failed;
439
440         if (error)
441                 *error = TDM_ERROR_NONE;
442
443         TDM_INFO("init success!");
444
445         return (tdm_backend_data *)drm_data;
446 failed:
447         if (error)
448                 *error = ret;
449
450         tdm_drm_deinit(drm_data);
451
452         TDM_ERR("init failed!");
453         return NULL;
454 }
455
456 tdm_backend_module tdm_backend_module_data = {
457         "drm",
458         "Samsung",
459         TDM_BACKEND_SET_ABI_VERSION(1, 1),
460         tdm_drm_init,
461         tdm_drm_deinit
462 };