hwc: implement the first draft of HWC based on exynos implamantation.
[platform/adaptation/broadcom/libtdm-vc4.git] / src / tdm_vc4.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_vc4.h"
10 #include <tdm_helper.h>
11 #include <tbm_drm_helper.h>
12
13 #define TDM_DRM_NAME "vc4-drm"
14
15 static tdm_vc4_data *vc4_data;
16
17 #ifdef HAVE_UDEV
18 static struct udev_device *
19 _tdm_find_primary_gpu(void)
20 {
21         struct udev *udev;
22         struct udev_enumerate *e;
23         struct udev_list_entry *entry;
24         const char *path, *id;
25         struct udev_device *device, *drm_device, *pci;
26
27         udev = udev_new();
28         if (!udev) {
29                 TDM_ERR("fail to initialize udev context\n");
30                 return NULL;
31         }
32
33         e = udev_enumerate_new(udev);
34         udev_enumerate_add_match_subsystem(e, "drm");
35         udev_enumerate_add_match_sysname(e, "card[0-9]*");
36
37         udev_enumerate_scan_devices(e);
38         drm_device = NULL;
39         udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
40                 path = udev_list_entry_get_name(entry);
41                 device = udev_device_new_from_syspath(udev, path);
42                 if (!device)
43                         continue;
44
45                 pci = udev_device_get_parent_with_subsystem_devtype(device, "pci", NULL);
46                 if (pci) {
47                         id = udev_device_get_sysattr_value(pci, "boot_vga");
48                         if (id && !strcmp(id, "1")) {
49                                 if (drm_device)
50                                         udev_device_unref(drm_device);
51                                 drm_device = device;
52                                 break;
53                         }
54                 }
55
56                 if (!drm_device)
57                         drm_device = device;
58                 else
59                         udev_device_unref(device);
60         }
61
62         udev_enumerate_unref(e);
63         return drm_device;
64 }
65
66 static tdm_error
67 _tdm_vc4_udev_fd_handler(int fd, tdm_event_loop_mask mask, void *user_data)
68 {
69         tdm_vc4_data *edata = (tdm_vc4_data*)user_data;
70         struct udev_device *dev;
71         const char *hotplug;
72         struct stat s;
73         dev_t udev_devnum;
74         int ret;
75
76         dev = udev_monitor_receive_device(edata->uevent_monitor);
77         if (!dev) {
78                 TDM_ERR("couldn't receive device");
79                 return TDM_ERROR_OPERATION_FAILED;
80         }
81
82         udev_devnum = udev_device_get_devnum(dev);
83
84         ret = fstat(edata->drm_fd, &s);
85         if (ret == -1) {
86                 TDM_ERR("fstat failed");
87                 return 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_INFO("HotPlug");
95                 tdm_vc4_display_update_output_status(edata);
96         }
97
98         udev_device_unref(dev);
99
100         return TDM_ERROR_NONE;
101 }
102
103 static void
104 _tdm_vc4_udev_init(tdm_vc4_data *edata)
105 {
106         struct udev *u = NULL;
107         struct udev_monitor *mon = NULL;
108
109         u = udev_new();
110         if (!u) {
111                 TDM_ERR("couldn't create udev");
112                 goto failed;
113         }
114
115         mon = udev_monitor_new_from_netlink(u, "udev");
116         if (!mon) {
117                 TDM_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_ERR("add match subsystem failed");
124                 goto failed;
125         }
126
127         edata->uevent_source =
128                 tdm_event_loop_add_fd_handler(edata->dpy, udev_monitor_get_fd(mon),
129                                                                           TDM_EVENT_LOOP_READABLE,
130                                                                           _tdm_vc4_udev_fd_handler,
131                                                                           edata, NULL);
132         if (!edata->uevent_source) {
133                 TDM_ERR("couldn't create udev event source");
134                 goto failed;
135         }
136
137         edata->uevent_monitor = mon;
138
139         TDM_INFO("hotplug monitor created");
140
141         return;
142 failed:
143         if (mon)
144                 udev_monitor_unref(mon);
145         if (u)
146                 udev_unref(u);
147 }
148
149 static void
150 _tdm_vc4_udev_deinit(tdm_vc4_data *edata)
151 {
152         if (edata->uevent_source) {
153                 tdm_event_loop_source_remove(edata->uevent_source);
154                 edata->uevent_source = NULL;
155         }
156
157         if (edata->uevent_monitor) {
158                 struct udev *u = udev_monitor_get_udev(edata->uevent_monitor);
159                 udev_monitor_unref(edata->uevent_monitor);
160                 udev_unref(u);
161                 edata->uevent_monitor = NULL;
162                 TDM_INFO("hotplug monitor destroyed");
163         }
164 }
165 #endif
166
167 static int
168 _tdm_vc4_open_drm(void)
169 {
170         int fd = -1;
171
172         fd = drmOpen(TDM_DRM_NAME, NULL);
173         if (fd < 0)
174                 TDM_WRN("Cannot open '%s' drm", TDM_DRM_NAME);
175
176 #ifdef HAVE_UDEV
177         if (fd < 0) {
178                 struct udev_device *drm_device = NULL;
179                 const char *filename;
180                 TDM_WRN("Cannot open drm device.. search by udev");
181
182                 drm_device = _tdm_find_primary_gpu();
183                 if (drm_device == NULL) {
184                         TDM_ERR("fail to find drm device\n");
185                         goto close_l;
186                 }
187
188                 filename = udev_device_get_devnode(drm_device);
189
190                 fd = open(filename, O_RDWR | O_CLOEXEC);
191                 if (fd < 0)
192                         TDM_ERR("Cannot open drm device(%s)\n", filename);
193
194                 TDM_DBG("open drm device (name:%s, fd:%d)", filename, fd);
195
196                 udev_device_unref(drm_device);
197         }
198 close_l:
199 #endif
200         return fd;
201 }
202
203 void
204 tdm_vc4_deinit(tdm_backend_data *bdata)
205 {
206         if (vc4_data != bdata)
207                 return;
208
209         TDM_INFO("deinit");
210
211 #ifdef HAVE_UDEV
212         _tdm_vc4_udev_deinit(vc4_data);
213 #endif
214
215         tdm_vc4_display_destroy_output_list(vc4_data);
216
217         if (vc4_data->plane_res)
218                 drmModeFreePlaneResources(vc4_data->plane_res);
219         if (vc4_data->mode_res)
220                 drmModeFreeResources(vc4_data->mode_res);
221         if (vc4_data->drm_fd >= 0)
222                 close(vc4_data->drm_fd);
223
224         free(vc4_data);
225         vc4_data = NULL;
226 }
227
228 tdm_backend_data *
229 tdm_vc4_init(tdm_display *dpy, tdm_error *error)
230 {
231         tdm_func_display vc4_func_display;
232         tdm_func_output vc4_func_output;
233         tdm_func_layer vc4_func_layer;
234         tdm_func_hwc_window vc4_func_hwc_window;
235         tdm_error ret;
236         char *str;
237
238         if (!dpy) {
239                 TDM_ERR("display is null");
240                 if (error)
241                         *error = TDM_ERROR_INVALID_PARAMETER;
242                 return NULL;
243         }
244
245         if (vc4_data) {
246                 TDM_ERR("failed: init twice");
247                 if (error)
248                         *error = TDM_ERROR_BAD_REQUEST;
249                 return NULL;
250         }
251
252         vc4_data = calloc(1, sizeof(tdm_vc4_data));
253         if (!vc4_data) {
254                 TDM_ERR("alloc failed");
255                 if (error)
256                         *error = TDM_ERROR_OUT_OF_MEMORY;
257                 return NULL;
258         }
259
260         str = getenv("TDM_HWC");
261         if (str) {
262                 char *end;
263                 vc4_data->hwc_mode = strtol(str, &end, 10);;
264         }
265
266         LIST_INITHEAD(&vc4_data->output_list);
267         LIST_INITHEAD(&vc4_data->buffer_list);
268
269         memset(&vc4_func_display, 0, sizeof(vc4_func_display));
270         vc4_func_display.display_get_capability = vc4_display_get_capability;
271         vc4_func_display.display_get_outputs = vc4_display_get_outputs;
272         vc4_func_display.display_get_fd = vc4_display_get_fd;
273         vc4_func_display.display_handle_events = vc4_display_handle_events;
274
275         memset(&vc4_func_output, 0, sizeof(vc4_func_output));
276         vc4_func_output.output_get_capability = vc4_output_get_capability;
277         vc4_func_output.output_get_layers = vc4_output_get_layers;
278         vc4_func_output.output_set_property = vc4_output_set_property;
279         vc4_func_output.output_get_property = vc4_output_get_property;
280         vc4_func_output.output_wait_vblank = vc4_output_wait_vblank;
281         vc4_func_output.output_set_vblank_handler = vc4_output_set_vblank_handler;
282         vc4_func_output.output_commit = vc4_output_commit;
283         vc4_func_output.output_set_commit_handler = vc4_output_set_commit_handler;
284         vc4_func_output.output_set_dpms = vc4_output_set_dpms;
285         vc4_func_output.output_get_dpms = vc4_output_get_dpms;
286         vc4_func_output.output_set_mode = vc4_output_set_mode;
287         vc4_func_output.output_get_mode = vc4_output_get_mode;
288 #ifdef HAVE_UDEV
289         vc4_func_output.output_set_status_handler = vc4_output_set_status_handler;
290 #endif
291         if (vc4_data->hwc_mode) {
292                 vc4_func_output.output_hwc_create_window = vc4_output_hwc_window_create;
293                 vc4_func_output.output_hwc_destroy_window = vc4_output_hwc_window_destroy;
294                 vc4_func_output.output_hwc_validate = vc4_output_hwc_validate;
295                 vc4_func_output.output_hwc_get_changed_composition_types = vc4_output_hwc_get_changed_composition_types;
296                 vc4_func_output.output_hwc_accept_changes = vc4_output_hwc_accept_changes;
297                 vc4_func_output.output_hwc_get_target_buffer_queue = vc4_output_hwc_get_target_buffer_queue;
298                 vc4_func_output.output_hwc_set_client_target_buffer = vc4_output_hwc_set_client_target_buffer;
299
300                 memset(&vc4_func_hwc_window, 0, sizeof(vc4_func_hwc_window));
301                 vc4_func_hwc_window.hwc_window_get_tbm_buffer_queue = vc4_hwc_window_get_tbm_buffer_queue;
302                 vc4_func_hwc_window.hwc_window_set_buffer = vc4_hwc_window_set_buffer;
303                 vc4_func_hwc_window.hwc_window_set_composition_type = vc4_hwc_window_set_composition_type;
304                 vc4_func_hwc_window.hwc_window_set_info = vc4_hwc_window_set_info;
305                 vc4_func_hwc_window.hwc_window_set_buffer_damage = vc4_hwc_window_set_buffer_damage;
306                 vc4_func_hwc_window.hwc_window_set_zpos = vc4_hwc_window_set_zpos;
307         }
308
309         memset(&vc4_func_layer, 0, sizeof(vc4_func_layer));
310         vc4_func_layer.layer_get_capability = vc4_layer_get_capability;
311         vc4_func_layer.layer_set_property = vc4_layer_set_property;
312         vc4_func_layer.layer_get_property = vc4_layer_get_property;
313         vc4_func_layer.layer_set_info = vc4_layer_set_info;
314         vc4_func_layer.layer_get_info = vc4_layer_get_info;
315         vc4_func_layer.layer_set_buffer = vc4_layer_set_buffer;
316         vc4_func_layer.layer_unset_buffer = vc4_layer_unset_buffer;
317
318         ret = tdm_backend_register_func_display(dpy, &vc4_func_display);
319         if (ret != TDM_ERROR_NONE)
320                 goto failed;
321
322         ret = tdm_backend_register_func_output(dpy, &vc4_func_output);
323         if (ret != TDM_ERROR_NONE)
324                 goto failed;
325
326         ret = tdm_backend_register_func_layer(dpy, &vc4_func_layer);
327         if (ret != TDM_ERROR_NONE)
328                 goto failed;
329
330         if (vc4_data->hwc_mode) {
331                 ret = tdm_backend_register_func_hwc_window(dpy, &vc4_func_hwc_window);
332                 if (ret != TDM_ERROR_NONE)
333                         goto failed;
334         }
335         vc4_data->dpy = dpy;
336
337         /* The drm master fd can be opened by a tbm backend module in
338          * tbm_bufmgr_init() time. In this case, we just get it from tbm.
339          */
340         vc4_data->drm_fd = tbm_drm_helper_get_master_fd();
341         if (vc4_data->drm_fd < 0) {
342                 vc4_data->drm_fd = _tdm_vc4_open_drm();
343
344                 if (vc4_data->drm_fd < 0) {
345                         ret = TDM_ERROR_OPERATION_FAILED;
346                         goto failed;
347                 }
348
349                 tbm_drm_helper_set_tbm_master_fd(vc4_data->drm_fd);
350         }
351
352         TDM_INFO("master fd(%d)", vc4_data->drm_fd);
353
354 #ifdef HAVE_UDEV
355         _tdm_vc4_udev_init(vc4_data);
356 #endif
357
358 #if LIBDRM_MAJOR_VERSION >= 2 && LIBDRM_MINOR_VERSION >= 4  && LIBDRM_MICRO_VERSION >= 47
359         if (drmSetClientCap(vc4_data->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) < 0) {
360                 TDM_WRN("Set DRM_CLIENT_CAP_UNIVERSAL_PLANES failed");
361         } else {
362                 TDM_INFO("has universal planes");
363                 vc4_data->has_universal_plane = 1;
364         }
365 #endif
366
367         vc4_data->mode_res = drmModeGetResources(vc4_data->drm_fd);
368         if (!vc4_data->mode_res) {
369                 TDM_ERR("no drm resource: %m");
370                 ret = TDM_ERROR_OPERATION_FAILED;
371                 goto failed;
372         }
373
374         vc4_data->plane_res = drmModeGetPlaneResources(vc4_data->drm_fd);
375         if (!vc4_data->plane_res) {
376                 TDM_ERR("no drm plane resource: %m");
377                 ret = TDM_ERROR_OPERATION_FAILED;
378                 goto failed;
379         }
380
381         if (vc4_data->plane_res->count_planes <= 0) {
382                 TDM_ERR("no drm plane resource");
383                 ret = TDM_ERROR_OPERATION_FAILED;
384                 goto failed;
385         }
386
387         ret = tdm_vc4_display_create_output_list(vc4_data);
388         if (ret != TDM_ERROR_NONE)
389                 goto failed;
390
391         ret = tdm_vc4_display_create_layer_list(vc4_data);
392         if (ret != TDM_ERROR_NONE)
393                 goto failed;
394
395         if (error)
396                 *error = TDM_ERROR_NONE;
397
398         TDM_INFO("init success!");
399
400         return (tdm_backend_data *)vc4_data;
401 failed:
402         if (error)
403                 *error = ret;
404
405         tdm_vc4_deinit(vc4_data);
406
407         TDM_ERR("init failed!");
408         return NULL;
409 }
410
411 tdm_backend_module tdm_backend_module_data = {
412         "drm",
413         "Samsung",
414         TDM_BACKEND_SET_ABI_VERSION(1, 1),
415         tdm_vc4_init,
416         tdm_vc4_deinit
417 };