make libds-tizen-backend-tdm
[platform/core/uifw/libds-tizen.git] / src / backend / tdm / tdm_output_hwc.c
1 #include <stdint.h>
2 #include <wayland-server.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 #include <libds/log.h>
7 #include <libds-tizen/tbm_server.h>
8
9
10 #include "tdm_internal.h"
11 #include "tdm_output_hwc.h"
12
13 static void
14 hwc_window_update_front_buffer(struct ds_tdm_output_hwc_window *hwc_window)
15 {
16     if (hwc_window->front_buffer == hwc_window->back_buffer) {
17         if (hwc_window->back_buffer) {
18             ds_buffer_unlock(hwc_window->back_buffer);
19             hwc_window->back_buffer = NULL;
20         }
21
22         return;
23     }
24
25     ds_tdm_output_hwc_window_lock(hwc_window);
26
27     if (hwc_window->front_buffer) {
28         ds_buffer_unlock(hwc_window->front_buffer);
29         hwc_window->front_buffer = NULL;
30         ds_tdm_output_hwc_window_unlock(hwc_window);
31     }
32
33     hwc_window->front_buffer = hwc_window->back_buffer;
34     hwc_window->back_buffer = NULL;
35
36     if (hwc_window->front_buffer)
37         ds_tdm_output_hwc_window_lock(hwc_window);
38
39     ds_tdm_output_hwc_window_unlock(hwc_window);
40 }
41
42 static void
43 hwc_window_attach_back_buffer(struct ds_tdm_output_hwc_window *hwc_window,
44         struct ds_buffer *buffer)
45 {
46     if (hwc_window->back_buffer) {
47         ds_buffer_unlock(hwc_window->back_buffer);
48         hwc_window->back_buffer = NULL;
49     }
50
51     if (buffer)
52         hwc_window->back_buffer = ds_buffer_lock(buffer);
53 }
54
55 static unsigned int
56 get_horizontal_get(tbm_surface_h tsurface)
57 {
58     unsigned int horizontal = 0;
59     tbm_surface_info_s surf_info;
60
61     tbm_surface_get_info(tsurface, &surf_info);
62
63     switch (surf_info.format) {
64         case TBM_FORMAT_YUV420:
65         case TBM_FORMAT_YVU420:
66         case TBM_FORMAT_YUV422:
67         case TBM_FORMAT_YVU422:
68         case TBM_FORMAT_NV12:
69         case TBM_FORMAT_NV21:
70             horizontal = surf_info.planes[0].stride;
71             break;
72         case TBM_FORMAT_YUYV:
73         case TBM_FORMAT_UYVY:
74             horizontal = surf_info.planes[0].stride >> 1;
75             break;
76         case TBM_FORMAT_XRGB8888:
77         case TBM_FORMAT_XBGR8888:
78         case TBM_FORMAT_RGBX8888:
79         case TBM_FORMAT_BGRX8888:
80         case TBM_FORMAT_ARGB8888:
81         case TBM_FORMAT_ABGR8888:
82         case TBM_FORMAT_RGBA8888:
83         case TBM_FORMAT_BGRA8888:
84         case TBM_FORMAT_XRGB2101010:
85         case TBM_FORMAT_XBGR2101010:
86         case TBM_FORMAT_RGBX1010102:
87         case TBM_FORMAT_BGRX1010102:
88         case TBM_FORMAT_ARGB2101010:
89         case TBM_FORMAT_ABGR2101010:
90         case TBM_FORMAT_RGBA1010102:
91         case TBM_FORMAT_BGRA1010102:
92             horizontal = surf_info.planes[0].stride >> 2;
93             break;
94         default:
95             ds_err("not supported format");
96     }
97
98     return horizontal;
99 }
100
101 static tdm_transform
102 get_tdm_transform(enum wl_output_transform output_transform)
103 {
104     switch (output_transform) {
105         case WL_OUTPUT_TRANSFORM_90:
106             return TDM_TRANSFORM_90;
107         case WL_OUTPUT_TRANSFORM_180:
108             return TDM_TRANSFORM_180;
109         case WL_OUTPUT_TRANSFORM_270:
110             return TDM_TRANSFORM_270;
111         case WL_OUTPUT_TRANSFORM_FLIPPED:
112             return TDM_TRANSFORM_FLIPPED;
113         case WL_OUTPUT_TRANSFORM_FLIPPED_90:
114             return TDM_TRANSFORM_FLIPPED_90;
115         case WL_OUTPUT_TRANSFORM_FLIPPED_180:
116             return TDM_TRANSFORM_FLIPPED_180;
117         case WL_OUTPUT_TRANSFORM_FLIPPED_270:
118             return TDM_TRANSFORM_FLIPPED_270;
119             case WL_OUTPUT_TRANSFORM_NORMAL:
120         default:
121             return TDM_TRANSFORM_NORMAL;
122     }
123 }
124
125 static tdm_hwc_window_composition
126 get_tdm_composition(enum ds_tdm_output_hwc_window_composition composition)
127 {
128     switch (composition) {
129         case DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_CLIENT:
130             return TDM_HWC_WIN_COMPOSITION_CLIENT;
131         case DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_CURSOR:
132             return TDM_HWC_WIN_COMPOSITION_CURSOR;
133         case DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_DEVICE:
134             return TDM_HWC_WIN_COMPOSITION_DEVICE;
135         case DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_VIDEO:
136             return TDM_HWC_WIN_COMPOSITION_VIDEO;
137         case DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_NONE:
138         default:
139             return TDM_HWC_WIN_COMPOSITION_NONE;
140     }
141 }
142
143 static enum ds_tdm_output_hwc_window_composition
144 get_composition(tdm_hwc_window_composition composition)
145 {
146     switch (composition) {
147         case TDM_HWC_WIN_COMPOSITION_CLIENT:
148             return DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_CLIENT;
149         case TDM_HWC_WIN_COMPOSITION_CURSOR:
150             return DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_CURSOR;
151         case TDM_HWC_WIN_COMPOSITION_DEVICE:
152             return DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_DEVICE;
153         case TDM_HWC_WIN_COMPOSITION_VIDEO:
154             return DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_VIDEO;
155         case TDM_HWC_WIN_COMPOSITION_NONE:
156         default:
157             return DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_NONE;
158     }
159 }
160
161 struct ds_tdm_output_hwc *
162 ds_tdm_output_hwc_create(tdm_hwc *thwc)
163 {
164     struct ds_tdm_output_hwc *hwc;
165
166     hwc = calloc(1, sizeof *hwc);
167     if (!hwc)
168         return NULL;
169
170     hwc->thwc = thwc;
171
172     wl_list_init(&hwc->hwc_windows);
173     wl_signal_init(&hwc->events.commit_handler);
174     wl_signal_init(&hwc->events.destroy);
175
176     return hwc;
177 }
178
179 void
180 ds_tdm_output_hwc_destroy(struct ds_tdm_output_hwc *hwc)
181 {
182     wl_signal_emit(&hwc->events.destroy, hwc);
183
184     free(hwc);
185 }
186
187 bool
188 ds_tdm_output_hwc_set_client_target_buffer(struct ds_tdm_output_hwc *hwc, tbm_surface_h tsurface)
189 {
190     tdm_error terr;
191     tdm_region fb_damage;
192
193     memset(&fb_damage, 0, sizeof(fb_damage));
194
195     hwc->target_buffer_info.src_config.pos.x = 0;
196     hwc->target_buffer_info.src_config.pos.y = 0;
197     hwc->target_buffer_info.src_config.pos.w = tbm_surface_get_width(tsurface);
198     hwc->target_buffer_info.src_config.pos.h = tbm_surface_get_height(tsurface);
199     hwc->target_buffer_info.src_config.size.h = get_horizontal_get(tsurface);
200     hwc->target_buffer_info.src_config.size.v = tbm_surface_get_height(tsurface);
201     hwc->target_buffer_info.src_config.format = tbm_surface_get_format(tsurface);
202
203     hwc->target_buffer_info.dst_pos.x = 0;
204     hwc->target_buffer_info.dst_pos.y = 0;
205     hwc->target_buffer_info.dst_pos.w = tbm_surface_get_width(tsurface);
206     hwc->target_buffer_info.dst_pos.h = tbm_surface_get_height(tsurface);
207
208     hwc->target_buffer_info.transform = TDM_TRANSFORM_NORMAL;
209
210     tdm_hwc_set_client_target_buffer_info(hwc->thwc, &hwc->target_buffer_info);
211
212     terr = tdm_hwc_set_client_target_buffer(hwc->thwc, tsurface, fb_damage);
213     if (terr != TDM_ERROR_NONE) {
214         ds_err("Could not set hwc client target buffer");
215         return false;
216     }
217
218     return true;
219 }
220
221 void
222 ds_tdm_output_hwc_window_set_accepted_composition(struct ds_tdm_output_hwc_window *hwc_window,
223         enum ds_tdm_output_hwc_window_composition composition)
224 {
225     if (hwc_window->accepted_composition == composition)
226         return;
227
228     hwc_window->accepted_composition = composition;
229 }
230
231 static void
232 hwc_window_free(struct ds_tdm_output_hwc_window *hwc_window)
233 {
234     if (hwc_window->buffer)
235         ds_buffer_unlock(hwc_window->buffer);
236
237     if (hwc_window->set_buffer)
238         ds_buffer_unlock(hwc_window->set_buffer);
239
240     if (hwc_window->front_buffer)
241         ds_buffer_unlock(hwc_window->front_buffer);
242
243     if (hwc_window->back_buffer)
244         ds_buffer_unlock(hwc_window->back_buffer);
245
246     wl_list_remove(&hwc_window->link);
247
248     tdm_hwc_window_destroy(hwc_window->twindow);
249
250     free(hwc_window);
251 }
252
253 static void
254 hwc_window_consider_destroy(struct ds_tdm_output_hwc_window *hwc_window)
255 {
256     if (!hwc_window->dropped || hwc_window->n_locks > 0)
257         return;
258
259     hwc_window_free(hwc_window);
260 }
261
262 struct ds_tdm_output_hwc_window *
263 ds_tdm_output_hwc_window_lock(struct ds_tdm_output_hwc_window *hwc_window)
264 {
265     hwc_window->n_locks++;
266     ds_dbg("hwc_window(%p) n_locks(%zu)", hwc_window, hwc_window->n_locks);
267     return hwc_window;
268 }
269
270 void
271 ds_tdm_output_hwc_window_unlock(struct ds_tdm_output_hwc_window *hwc_window)
272 {
273     hwc_window->n_locks--;
274     ds_dbg("hwc_window(%p) n_locks(%zu)", hwc_window, hwc_window->n_locks);
275
276     hwc_window_consider_destroy(hwc_window);
277 }
278
279 WL_EXPORT void
280 ds_tdm_output_hwc_set_enabled(struct ds_tdm_output_hwc *hwc, bool enabled)
281 {
282     if (hwc->enabled == enabled)
283         return;
284
285     hwc->enabled = enabled;
286 }
287
288 WL_EXPORT struct ds_tdm_output_hwc_window *
289 ds_tdm_output_hwc_window_create(struct ds_tdm_output_hwc *hwc)
290 {
291     tdm_error terr;
292     tdm_hwc_window *thwc_window;
293     struct ds_tdm_output_hwc_window *hwc_window;
294
295     hwc_window = calloc(1, sizeof *hwc_window);
296     if (!hwc_window)
297         return NULL;
298
299     thwc_window = tdm_hwc_create_window(hwc->thwc, &terr);
300     if (terr != TDM_ERROR_NONE) {
301         ds_err("Could not create tdm_hwc_window error:%d", terr);
302         free(hwc_window);
303         return NULL;
304     }
305
306     hwc_window->twindow = thwc_window;
307
308     wl_list_insert(hwc->hwc_windows.prev, &hwc_window->link);
309
310     return hwc_window;
311 }
312
313 WL_EXPORT void
314 ds_tdm_output_hwc_window_destroy(struct ds_tdm_output_hwc_window *hwc_window)
315 {
316     hwc_window->dropped = true;
317     ds_dbg("hwc_window(%p) dropped: n_locks(%zu)", hwc_window, hwc_window->n_locks);
318     hwc_window_consider_destroy(hwc_window);
319 }
320
321 WL_EXPORT bool
322 ds_tdm_output_hwc_window_set_buffer(struct ds_tdm_output_hwc_window *hwc_window,
323         struct ds_buffer *buffer)
324 {
325     if (hwc_window->buffer == buffer)
326         return true;
327
328     if (hwc_window->buffer) {
329         ds_buffer_unlock(hwc_window->set_buffer);
330         hwc_window->buffer = NULL;
331     }
332
333     if (buffer)
334         hwc_window->buffer = ds_buffer_lock(buffer);
335
336     return true;
337 }
338
339 WL_EXPORT void
340 ds_tdm_output_hwc_window_set_src_box(struct ds_tdm_output_hwc_window *hwc_window,
341         const struct ds_tdm_box *src_box)
342 {
343     if (src_box != NULL && !memcmp(&hwc_window->src_box, src_box, sizeof(*src_box)))
344         return;
345
346     if (src_box != NULL) {
347         memcpy(&hwc_window->src_box, src_box, sizeof(*src_box));
348     } else {
349         memset(&hwc_window->src_box, 0, sizeof(hwc_window->src_box));
350     }
351 }
352
353 WL_EXPORT void
354 ds_tdm_output_hwc_window_set_transform(struct ds_tdm_output_hwc_window *hwc_window,
355         enum wl_output_transform transform)
356 {
357     if (hwc_window->transform == transform)
358         return;
359
360     hwc_window->transform = transform;
361 }
362
363 WL_EXPORT void
364 ds_tdm_output_hwc_window_set_position(struct ds_tdm_output_hwc_window *hwc_window,
365         int x, int y)
366 {
367     if ((hwc_window->x == x) && (hwc_window->y == y))
368         return;
369
370     hwc_window->x = x;
371     hwc_window->y = y;
372 }
373
374 WL_EXPORT void
375 ds_tdm_output_hwc_window_set_dest_size(struct ds_tdm_output_hwc_window *hwc_window,
376         int width, int height)
377 {
378     if ((hwc_window->dest_width == width) && (hwc_window->dest_height == height))
379         return;
380
381     hwc_window->dest_width = width;
382     hwc_window->dest_height = height;
383 }
384
385 WL_EXPORT void
386 ds_tdm_output_hwc_window_set_composition(struct ds_tdm_output_hwc_window *hwc_window,
387         enum ds_tdm_output_hwc_window_composition composition)
388 {
389     if (hwc_window->composition == composition)
390         return;
391
392     hwc_window->composition = composition;
393 }
394
395 WL_EXPORT enum ds_tdm_output_hwc_window_composition
396 ds_tdm_output_hwc_window_get_composition(struct ds_tdm_output_hwc_window *hwc_window)
397 {
398     return hwc_window->composition;
399 }
400
401 static void
402 hwc_window_update(struct ds_tdm_output_hwc_window *hwc_window)
403 {
404     tdm_error terr;
405     tbm_surface_h tsurface = NULL;
406     tdm_hwc_window_info tinfo = {0, };
407     struct ds_tbm_client_buffer *client_buffer;
408
409     if (hwc_window->buffer) {
410         client_buffer = ds_tbm_client_buffer_from_buffer(hwc_window->buffer);
411         if (client_buffer)
412             tsurface = ds_tbm_client_buffer_get_tbm_surface(client_buffer);
413     }
414
415     if (hwc_window->set_buffer != hwc_window->buffer) {
416         if (hwc_window->set_buffer) {
417             ds_buffer_unlock(hwc_window->set_buffer);
418             hwc_window->set_buffer = NULL;
419         }
420
421         if (hwc_window->buffer)
422             hwc_window->set_buffer = ds_buffer_lock(hwc_window->buffer);
423
424         terr = tdm_hwc_window_set_buffer(hwc_window->twindow, tsurface);
425         if (terr != TDM_ERROR_NONE)
426             ds_err("Could not set buffer hwc_window:%p tdm_error:%d", hwc_window, terr);
427     }
428
429     if (tsurface) {
430         tinfo.src_config.pos.x = 0;
431         tinfo.src_config.pos.y = 0;
432         tinfo.src_config.pos.w = tbm_surface_get_width(tsurface);
433         tinfo.src_config.pos.h = tbm_surface_get_height(tsurface);
434         tinfo.src_config.size.h = get_horizontal_get(tsurface);
435         tinfo.src_config.size.v = tbm_surface_get_height(tsurface);
436         tinfo.src_config.format = tbm_surface_get_format(tsurface);
437         tinfo.dst_pos.x = hwc_window->x;
438         tinfo.dst_pos.y = hwc_window->y;
439         tinfo.dst_pos.w = hwc_window->dest_width;
440         tinfo.dst_pos.h = hwc_window->dest_height;
441         tinfo.transform = get_tdm_transform(hwc_window->transform);
442     }
443
444     if (memcmp(&hwc_window->tinfo, &tinfo, sizeof tinfo)) {
445         memcpy(&hwc_window->tinfo, &tinfo, sizeof tinfo);
446
447         terr = tdm_hwc_window_set_info(hwc_window->twindow, &hwc_window->tinfo);
448         if (terr != TDM_ERROR_NONE)
449             ds_err("Could not set info hwc_window:%p tdm_error:%d", hwc_window, terr);
450     }
451
452     terr = tdm_hwc_window_set_composition_type(hwc_window->twindow,
453             get_tdm_composition(hwc_window->composition));
454     if (terr != TDM_ERROR_NONE)
455         ds_err("Could not set composition type hwc_window:%p tdm_error:%d", hwc_window, terr);
456 }
457
458 WL_EXPORT bool
459 ds_tdm_output_hwc_validate(struct ds_tdm_output_hwc *hwc,
460         struct ds_tdm_output_hwc_window **composited_windows, uint32_t num_windows, uint32_t *num_changed)
461 {
462     tdm_error terr;
463     tdm_hwc_window **compositied_hwc_windows = NULL;
464     struct ds_tdm_output_hwc_window *hwc_window;
465     int i;
466
467     wl_list_for_each(hwc_window, &hwc->hwc_windows, link)
468         hwc_window_update(hwc_window);
469
470     if (num_windows > 0) {
471         compositied_hwc_windows = calloc(num_windows, sizeof(tdm_hwc_window *));
472         if (!compositied_hwc_windows)
473             return false;
474
475         for (i = 0; i < num_windows; i++)
476             compositied_hwc_windows[i] = composited_windows[i]->twindow;
477     }
478
479     terr = tdm_hwc_validate(hwc->thwc, compositied_hwc_windows, num_windows, num_changed);
480     if (terr != TDM_ERROR_NONE) {
481         if (compositied_hwc_windows)
482             free(compositied_hwc_windows);
483         ds_err("Could not hwc validate");
484         return false;
485     }
486
487     if (compositied_hwc_windows)
488         free(compositied_hwc_windows);
489
490     hwc->validate_num_changed = *num_changed;
491
492     return true;
493 }
494
495 static struct ds_tdm_output_hwc_window *
496 hwc_get_hwc_window_from_tdm_hwc_window(struct ds_tdm_output_hwc *hwc, tdm_hwc_window *thwc_window)
497 {
498     struct ds_tdm_output_hwc_window *hwc_window;
499
500     wl_list_for_each(hwc_window, &hwc->hwc_windows, link) {
501         if (hwc_window->twindow == thwc_window)
502             return hwc_window;
503     }
504
505     return NULL;
506 }
507
508 WL_EXPORT bool
509 ds_tdm_output_hwc_get_changed_composition(struct ds_tdm_output_hwc *hwc, uint32_t *num_changed,
510         struct ds_tdm_output_hwc_window **changed_windows)
511 {
512     tdm_hwc_window **changed_thwc_windows = NULL;
513     tdm_hwc_window_composition *compositions = NULL;
514     struct ds_tdm_output_hwc_window *hwc_window;
515     tdm_error terr;
516     int i;
517
518     if (!hwc->validate_num_changed) {
519         *num_changed = 0;
520         return true;
521     }
522
523     changed_thwc_windows = calloc(hwc->validate_num_changed, sizeof *changed_thwc_windows);
524     if (!changed_thwc_windows)
525         return false;
526
527     compositions = calloc(hwc->validate_num_changed, sizeof *compositions);
528     if (!compositions) {
529         free(changed_thwc_windows);
530         return false;
531     }
532
533     terr = tdm_hwc_get_changed_composition_types(hwc->thwc, &hwc->validate_num_changed,
534             changed_thwc_windows, compositions);
535     if (terr != TDM_ERROR_NONE) {
536         ds_err("Could not hwc get changed composition types");
537         free(changed_thwc_windows);
538         free(compositions);
539         return false;
540     }
541
542     for (i = 0; i < hwc->validate_num_changed; i++) {
543         if (i >= *num_changed)
544             break;
545
546         hwc_window = hwc_get_hwc_window_from_tdm_hwc_window(hwc, changed_thwc_windows[i]);
547         if (!hwc_window)
548             continue;
549
550         ds_tdm_output_hwc_window_set_composition(hwc_window, get_composition(compositions[i]));
551         changed_windows[i] = hwc_window;
552     }
553
554     free(changed_thwc_windows);
555     free(compositions);
556
557     *num_changed = i;
558
559     return true;
560 }
561
562 WL_EXPORT bool
563 ds_tdm_output_hwc_accept_validation(struct ds_tdm_output_hwc *hwc)
564 {
565     tdm_error terr;
566     struct ds_tdm_output_hwc_window *hwc_window;
567     enum ds_tdm_output_hwc_window_composition composition;
568
569     terr = tdm_hwc_accept_validation(hwc->thwc);
570     if (terr != TDM_ERROR_NONE) {
571         ds_err("Could not hwc accept validation");
572         return false;
573     }
574
575     wl_list_for_each(hwc_window, &hwc->hwc_windows, link) {
576         composition = ds_tdm_output_hwc_window_get_composition(hwc_window);
577         ds_tdm_output_hwc_window_set_accepted_composition(hwc_window,
578                 composition);
579         if ((composition == DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_DEVICE) ||
580             (composition == DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_VIDEO)) {
581             hwc_window_attach_back_buffer(hwc_window, hwc_window->set_buffer);
582         } else {
583             hwc_window_attach_back_buffer(hwc_window, NULL);
584         }
585     }
586
587     return true;
588 }
589
590 static void
591 ds_tdm_output_hwc_commit_handler(tdm_hwc *thwc, unsigned int sequence,
592         unsigned int tv_sec, unsigned int tv_usec, void *user_data)
593 {
594     struct ds_tdm_output_hwc *hwc = user_data;
595     struct ds_tdm_output_hwc_window *hwc_window, *tmp;
596
597     wl_list_for_each_safe(hwc_window, tmp, &hwc->hwc_windows, link)
598         hwc_window_update_front_buffer(hwc_window);
599
600     wl_signal_emit(&hwc->events.commit_handler, hwc);
601 }
602
603 bool
604 ds_tdm_output_hwc_commit(struct ds_tdm_output_hwc *hwc)
605 {
606     tdm_error terr;
607     uint32_t num_changes;
608
609     if (!hwc->enabled) {
610         terr = tdm_hwc_validate(hwc->thwc, NULL, 0, &num_changes);
611         if (terr != TDM_ERROR_NONE) {
612             ds_err("Could not hwc validate");
613             return false;
614         }
615
616         terr = tdm_hwc_accept_validation(hwc->thwc);
617         if (terr != TDM_ERROR_NONE) {
618             ds_err("Could not hwc accept validation");
619             return false;
620         }
621     }
622
623     terr = tdm_hwc_commit(hwc->thwc, 0, ds_tdm_output_hwc_commit_handler, hwc);
624     if (terr != TDM_ERROR_NONE) {
625         ds_err("Could not hwc commit");
626         return false;
627     }
628
629     return true;
630 }
631
632 void
633 ds_tdm_output_hwc_add_commit_handler_listener(struct ds_tdm_output_hwc *hwc,
634         struct wl_listener *listener)
635 {
636     wl_signal_add(&hwc->events.commit_handler, listener);
637 }