ee760c16b75847d01423fca9a358524ca67e72d1
[platform/core/uifw/libtdm.git] / src / tdm_output.c
1 /**************************************************************************
2  *
3  * libtdm
4  *
5  * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved.
6  *
7  * Contact: Eunchul Kim <chulspro.kim@samsung.com>,
8  *          JinYoung Jeon <jy0.jeon@samsung.com>,
9  *          Taeheon Kim <th908.kim@samsung.com>,
10  *          YoungJun Cho <yj44.cho@samsung.com>,
11  *          SooChan Lim <sc1.lim@samsung.com>,
12  *          Boram Park <boram1288.park@samsung.com>
13  *
14  * Permission is hereby granted, free of charge, to any person obtaining a
15  * copy of this software and associated documentation files (the
16  * "Software"), to deal in the Software without restriction, including
17  * without limitation the rights to use, copy, modify, merge, publish,
18  * distribute, sub license, and/or sell copies of the Software, and to
19  * permit persons to whom the Software is furnished to do so, subject to
20  * the following conditions:
21  *
22  * The above copyright notice and this permission notice (including the
23  * next paragraph) shall be included in all copies or substantial portions
24  * of the Software.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
27  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
29  * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
30  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
31  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
32  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33  *
34 **************************************************************************/
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include "tdm_private.h"
41
42 #define COUNT_MAX   10
43
44 #define OUTPUT_FUNC_ENTRY() \
45         tdm_private_display *private_display; \
46         tdm_private_output *private_output; \
47         tdm_error ret = TDM_ERROR_NONE; /* default TDM_ERROR_NONE */\
48         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER); \
49         private_output = (tdm_private_output*)output; \
50         private_display = private_output->private_display
51
52 #define OUTPUT_FUNC_ENTRY_ERROR() \
53         tdm_private_display *private_display; \
54         tdm_private_output *private_output; \
55         tdm_error ret = TDM_ERROR_NONE; /* default TDM_ERROR_NONE */\
56         TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(output != NULL, TDM_ERROR_INVALID_PARAMETER, NULL); \
57         private_output = (tdm_private_output*)output; \
58         private_display = private_output->private_display
59
60 static void
61 _tdm_output_vblank_timeout_update(tdm_private_output *private_output, int ms_delay);
62
63 static tdm_error
64 _tdm_output_vblank_timeout_cb(void *user_data)
65 {
66         tdm_private_output *private_output = user_data;
67         tdm_private_output_vblank_handler *v = NULL;
68
69         TDM_RETURN_VAL_IF_FAIL(private_output != NULL, TDM_ERROR_OPERATION_FAILED);
70
71         private_output->vblank_timeout_timer_expired++;
72
73         TDM_ERR("TDM output(%d) vblank TIMEOUT!! (%d time%s)",
74                         private_output->pipe,
75                         private_output->vblank_timeout_timer_expired,
76                         (private_output->vblank_timeout_timer_expired > 1) ? "s" : "");
77
78         LIST_FOR_EACH_ENTRY(v, &private_output->vblank_handler_list, link) {
79                 TDM_ERR("vblank_handler(%p) interval(%d) sync(%d) sent_to_frontend(%u) owner_tid(%d)",
80                                 v, v->interval, v->sync, v->sent_to_frontend, v->owner_tid);
81         }
82
83         return TDM_ERROR_NONE;
84 }
85
86 INTERN void
87 tdm_output_vblank_print_wait_information(tdm_private_output *private_output, void *user_data)
88 {
89         tdm_private_output_vblank_handler *v = NULL;
90
91         TDM_RETURN_IF_FAIL(private_output != NULL);
92         TDM_RETURN_IF_FAIL(user_data != NULL);
93
94         TDM_ERR("TDM output(%d) vblank user_data(%p) info!!", private_output->pipe, user_data);
95
96         LIST_FOR_EACH_ENTRY(v, &private_output->vblank_handler_list, link) {
97                 if (v->user_data != user_data)
98                         continue;
99                 TDM_ERR("vblank_handler(%p) interval(%d) sync(%d) sent_to_frontend(%u) owner_tid(%d)",
100                                 v, v->interval, v->sync, v->sent_to_frontend, v->owner_tid);
101         }
102 }
103
104 static void
105 _tdm_output_vblank_timeout_update(tdm_private_output *private_output, int ms_delay)
106 {
107         tdm_error ret;
108
109         if (!private_output->vblank_timeout_timer) {
110                 private_output->vblank_timeout_timer =
111                         tdm_event_loop_add_timer_handler(private_output->private_display,
112                                                                                          _tdm_output_vblank_timeout_cb,
113                                                                                          private_output,
114                                                                                          &ret);
115                 if (!private_output->vblank_timeout_timer) {
116                         TDM_ERR("output(%d) couldn't add timer", private_output->pipe);
117                         return;
118                 }
119                 TDM_INFO("output(%d) create vblank timeout timer", private_output->pipe);
120                 private_output->vblank_timeout_timer_expired = 0;
121         }
122
123         ret = tdm_event_loop_source_timer_update(private_output->vblank_timeout_timer, ms_delay);
124         if (ret != TDM_ERROR_NONE) {
125                 TDM_ERR("output(%d) couldn't update timer", private_output->pipe);
126                 return;
127         }
128 }
129
130 INTERN tdm_error
131 tdm_output_init(tdm_private_display *private_display)
132 {
133         tdm_thread_cb_set_find_func(TDM_THREAD_CB_OUTPUT_COMMIT, tdm_display_find_output_stamp);
134         tdm_thread_cb_set_find_func(TDM_THREAD_CB_OUTPUT_VBLANK, tdm_display_find_output_stamp);
135         tdm_thread_cb_set_find_func(TDM_THREAD_CB_OUTPUT_STATUS, tdm_display_find_output_stamp);
136         tdm_thread_cb_set_find_func(TDM_THREAD_CB_OUTPUT_DPMS, tdm_display_find_output_stamp);
137
138
139         return TDM_ERROR_NONE;
140 }
141
142 EXTERN tdm_module *
143 tdm_output_get_backend_module(tdm_output *output, tdm_error *error)
144 {
145         tdm_private_module *private_module;
146
147         OUTPUT_FUNC_ENTRY_ERROR();
148
149         _pthread_mutex_lock(&private_display->lock);
150
151         private_module = private_output->private_module;
152
153         if (error)
154                 *error = TDM_ERROR_NONE;
155
156         _pthread_mutex_unlock(&private_display->lock);
157
158         return private_module;
159 }
160
161 EXTERN tdm_error
162 tdm_output_get_model_info(tdm_output *output, const char **maker,
163                                                   const char **model, const char **name)
164 {
165         OUTPUT_FUNC_ENTRY();
166
167         _pthread_mutex_lock(&private_display->lock);
168
169         if (maker)
170                 *maker = private_output->caps.maker;
171         if (model)
172                 *model = private_output->caps.model;
173         if (name)
174                 *name = private_output->caps.name;
175
176         _pthread_mutex_unlock(&private_display->lock);
177
178         return ret;
179 }
180
181 EXTERN tdm_error
182 tdm_output_get_capabilities(tdm_output *output, tdm_output_capability *capabilities)
183 {
184         OUTPUT_FUNC_ENTRY();
185
186         TDM_RETURN_VAL_IF_FAIL(capabilities != NULL, TDM_ERROR_INVALID_PARAMETER);
187
188         _pthread_mutex_lock(&private_display->lock);
189
190         *capabilities = private_output->caps.capabilities;
191
192         _pthread_mutex_unlock(&private_display->lock);
193
194         return ret;
195 }
196
197 EXTERN tdm_error
198 tdm_output_get_conn_status(tdm_output *output, tdm_output_conn_status *status)
199 {
200         OUTPUT_FUNC_ENTRY();
201
202         TDM_RETURN_VAL_IF_FAIL(status != NULL, TDM_ERROR_INVALID_PARAMETER);
203
204         _pthread_mutex_lock(&private_display->lock);
205
206         *status = private_output->caps.status;
207
208         _pthread_mutex_unlock(&private_display->lock);
209
210         return ret;
211 }
212
213 INTERN void
214 tdm_output_thread_cb_change(tdm_private_display *private_display, void *object, tdm_thread_cb_base *cb_base, void *user_data)
215 {
216         tdm_private_output *private_output = object;
217         tdm_private_output_change_handler *change_handler = user_data;
218         tdm_output_change_type type = TDM_OUTPUT_CHANGE_CONNECTION;
219         tdm_value value = {.u32 = 0 };
220
221         TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
222
223         assert(change_handler->owner_tid == syscall(SYS_gettid));
224
225         if (cb_base->type == TDM_THREAD_CB_OUTPUT_STATUS) {
226                 tdm_thread_cb_output_status *output_status = (tdm_thread_cb_output_status *)cb_base;
227                 type = TDM_OUTPUT_CHANGE_CONNECTION;
228                 value.u32 = output_status->status;
229         } else if (cb_base->type == TDM_THREAD_CB_OUTPUT_DPMS) {
230                 tdm_thread_cb_output_dpms *output_dpms = (tdm_thread_cb_output_dpms *)cb_base;
231                 type = TDM_OUTPUT_CHANGE_DPMS;
232                 value.u32 = output_dpms->dpms;
233         } else {
234                 TDM_NEVER_GET_HERE();
235                 return;
236         }
237
238         _pthread_mutex_unlock(&private_display->lock);
239         change_handler->func(private_output, type, value, change_handler->user_data);
240         _pthread_mutex_lock(&private_display->lock);
241
242 }
243
244 static tdm_error
245 _tdm_output_call_thread_cb_status(tdm_private_output *private_output, tdm_output_conn_status status)
246 {
247         tdm_thread_cb_output_status output_status;
248         pid_t caller_tid = syscall(SYS_gettid);
249         tdm_error ret;
250
251         memset(&output_status, 0, sizeof output_status);
252         output_status.base.type = TDM_THREAD_CB_OUTPUT_STATUS;
253         output_status.base.length = sizeof output_status;
254         output_status.base.object_stamp = private_output->stamp;
255         output_status.base.data = NULL;
256         if (tdm_thread_in_display_thread(caller_tid))
257                 output_status.base.sync = 0;
258         else
259                 output_status.base.sync = 1;
260         output_status.status = status;
261
262         ret = tdm_thread_cb_call(private_output, &output_status.base, 1);
263         TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE);
264
265         return TDM_ERROR_NONE;
266 }
267
268 static tdm_error
269 _tdm_output_call_thread_cb_dpms(tdm_private_output *private_output, tdm_output_dpms dpms)
270 {
271         tdm_thread_cb_output_dpms output_dpms;
272         tdm_error ret;
273
274         memset(&output_dpms, 0, sizeof output_dpms);
275         output_dpms.base.type = TDM_THREAD_CB_OUTPUT_DPMS;
276         output_dpms.base.length = sizeof output_dpms;
277         output_dpms.base.object_stamp = private_output->stamp;
278         output_dpms.base.data = NULL;
279         output_dpms.base.sync = 0;
280         output_dpms.dpms = dpms;
281
282         ret = tdm_thread_cb_call(private_output, &output_dpms.base, 1);
283         TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE);
284
285         return TDM_ERROR_NONE;
286 }
287
288 INTERN void
289 tdm_output_cb_status(tdm_output *output_backend, tdm_output_conn_status status, void *user_data)
290 {
291         tdm_private_output *private_output = user_data;
292         tdm_error ret;
293
294         TDM_RETURN_IF_FAIL(private_output);
295
296         TDM_INFO("output(%d) %s", private_output->pipe, tdm_status_str(status));
297
298         if ((private_output->caps.status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED && status != TDM_OUTPUT_CONN_STATUS_DISCONNECTED) ||
299                 (private_output->caps.status != TDM_OUTPUT_CONN_STATUS_DISCONNECTED && status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)) {
300                 ret = tdm_display_update_output(private_output->private_module, output_backend, private_output->pipe, 1);
301                 TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE);
302         } else {
303                 private_output->caps.status = status;
304         }
305
306         ret = _tdm_output_call_thread_cb_status(private_output, status);
307         TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE);
308 }
309
310 INTERN void
311 tdm_output_cb_dpms(tdm_output *output_backend, tdm_output_dpms dpms, void *user_data)
312 {
313         tdm_private_output *private_output = user_data;
314         tdm_error ret;
315
316         TDM_INFO("output(%d) %s", private_output->pipe, tdm_status_str(dpms));
317
318         private_output->current_dpms_value = dpms;
319         private_output->waiting_dpms_change = 0;
320         TDM_INFO("output(%d) dpms async '%s' done", private_output->pipe, tdm_dpms_str(dpms));
321
322         ret = _tdm_output_call_thread_cb_dpms(private_output, dpms);
323         TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE);
324 }
325
326 EXTERN tdm_error
327 tdm_output_add_change_handler(tdm_output *output,
328                                                           tdm_output_change_handler func,
329                                                           void *user_data)
330 {
331         tdm_private_output_change_handler *change_handler = NULL;
332         OUTPUT_FUNC_ENTRY();
333
334         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
335
336         _pthread_mutex_lock(&private_display->lock);
337
338         LIST_FOR_EACH_ENTRY(change_handler, &private_output->change_handler_list, link) {
339                 if (change_handler->func == func && change_handler->user_data == user_data) {
340                         TDM_ERR("can't add twice");
341                         _pthread_mutex_unlock(&private_display->lock);
342                         return TDM_ERROR_BAD_REQUEST;
343                 }
344         }
345
346         change_handler = calloc(1, sizeof(tdm_private_output_change_handler));
347         if (!change_handler) {
348                 /* LCOV_EXCL_START */
349                 TDM_ERR("failed: alloc memory");
350                 _pthread_mutex_unlock(&private_display->lock);
351                 return TDM_ERROR_OUT_OF_MEMORY;
352                 /* LCOV_EXCL_STOP */
353         }
354
355         ret = tdm_thread_cb_add(private_output, TDM_THREAD_CB_OUTPUT_DPMS, NULL, tdm_output_thread_cb_change, change_handler);
356         if (ret != TDM_ERROR_NONE) {
357                 /* LCOV_EXCL_START */
358                 TDM_ERR("tdm_thread_cb_add failed");
359                 free(change_handler);
360                 _pthread_mutex_unlock(&private_display->lock);
361                 return TDM_ERROR_OPERATION_FAILED;
362                 /* LCOV_EXCL_STOP */
363         }
364
365         ret = tdm_thread_cb_add(private_output, TDM_THREAD_CB_OUTPUT_STATUS, NULL, tdm_output_thread_cb_change, change_handler);
366         if (ret != TDM_ERROR_NONE) {
367                 /* LCOV_EXCL_START */
368                 TDM_ERR("tdm_thread_cb_add failed");
369                 tdm_thread_cb_remove(private_output, TDM_THREAD_CB_OUTPUT_DPMS, NULL, tdm_output_thread_cb_change, change_handler);
370                 free(change_handler);
371                 _pthread_mutex_unlock(&private_display->lock);
372                 return TDM_ERROR_OPERATION_FAILED;
373                 /* LCOV_EXCL_STOP */
374         }
375
376         change_handler->private_output = private_output;
377         change_handler->func = func;
378         change_handler->user_data = user_data;
379         change_handler->owner_tid = syscall(SYS_gettid);
380
381         LIST_ADDTAIL(&change_handler->link, &private_output->change_handler_list);
382
383         _pthread_mutex_unlock(&private_display->lock);
384
385         return ret;
386 }
387
388 EXTERN void
389 tdm_output_remove_change_handler(tdm_output *output,
390                                                                  tdm_output_change_handler func,
391                                                                  void *user_data)
392 {
393         tdm_private_display *private_display;
394         tdm_private_output *private_output;
395         tdm_private_output_change_handler *change_handler = NULL, *hh = NULL;
396
397         TDM_RETURN_IF_FAIL(output != NULL);
398         TDM_RETURN_IF_FAIL(func != NULL);
399
400         private_output = (tdm_private_output*)output;
401         private_display = private_output->private_display;
402
403         _pthread_mutex_lock(&private_display->lock);
404
405         LIST_FOR_EACH_ENTRY_SAFE(change_handler, hh, &private_output->change_handler_list, link) {
406                 if (change_handler->func != func || change_handler->user_data != user_data)
407                         continue;
408
409                 tdm_thread_cb_remove(private_output, TDM_THREAD_CB_OUTPUT_DPMS, NULL, tdm_output_thread_cb_change, change_handler);
410                 tdm_thread_cb_remove(private_output, TDM_THREAD_CB_OUTPUT_STATUS, NULL, tdm_output_thread_cb_change, change_handler);
411
412                 LIST_DEL(&change_handler->link);
413                 free(change_handler);
414
415                 _pthread_mutex_unlock(&private_display->lock);
416
417                 return;
418         }
419
420         _pthread_mutex_unlock(&private_display->lock);
421 }
422
423 EXTERN tdm_error
424 tdm_output_get_output_type(tdm_output *output, tdm_output_type *type)
425 {
426         OUTPUT_FUNC_ENTRY();
427
428         TDM_RETURN_VAL_IF_FAIL(type != NULL, TDM_ERROR_INVALID_PARAMETER);
429
430         _pthread_mutex_lock(&private_display->lock);
431
432         *type = private_output->caps.type;
433
434         _pthread_mutex_unlock(&private_display->lock);
435
436         return ret;
437 }
438
439 EXTERN tdm_error
440 tdm_output_get_layer_count(tdm_output *output, int *count)
441 {
442         tdm_private_layer *private_layer = NULL;
443
444         OUTPUT_FUNC_ENTRY();
445
446         TDM_RETURN_VAL_IF_FAIL(count != NULL, TDM_ERROR_INVALID_PARAMETER);
447
448         _pthread_mutex_lock(&private_display->lock);
449
450         if (private_output->caps.capabilities & TDM_OUTPUT_CAPABILITY_HWC) {
451                 TDM_ERR("output(%p) support HWC. Use HWC functions", private_output);
452                 *count = 0;
453                 _pthread_mutex_unlock(&private_display->lock);
454                 return TDM_ERROR_BAD_REQUEST;
455         }
456
457         *count = 0;
458         LIST_FOR_EACH_ENTRY(private_layer, &private_output->layer_list, link)
459         (*count)++;
460         if (*count == 0) {
461                 _pthread_mutex_unlock(&private_display->lock);
462                 return TDM_ERROR_NONE;
463         }
464
465         _pthread_mutex_unlock(&private_display->lock);
466
467         return ret;
468 }
469
470
471 EXTERN tdm_layer *
472 tdm_output_get_layer(tdm_output *output, int index, tdm_error *error)
473 {
474         tdm_private_layer *private_layer = NULL;
475
476         OUTPUT_FUNC_ENTRY_ERROR();
477
478         _pthread_mutex_lock(&private_display->lock);
479
480         if (error)
481                 *error = TDM_ERROR_NONE;
482
483         if (private_output->caps.capabilities & TDM_OUTPUT_CAPABILITY_HWC) {
484                 _pthread_mutex_unlock(&private_display->lock);
485                 TDM_ERR("output(%p) support HWC. Use HWC functions", private_output);
486                 if (error)
487                         *error = TDM_ERROR_BAD_REQUEST;
488                 return NULL;
489         }
490
491         LIST_FOR_EACH_ENTRY(private_layer, &private_output->layer_list, link) {
492                 if (private_layer->index == index) {
493                         _pthread_mutex_unlock(&private_display->lock);
494                         return private_layer;
495                 }
496         }
497
498         _pthread_mutex_unlock(&private_display->lock);
499
500         return NULL;
501 }
502
503 EXTERN tdm_error
504 tdm_output_get_available_properties(tdm_output *output, const tdm_prop **props,
505                                                                         int *count)
506 {
507         OUTPUT_FUNC_ENTRY();
508
509         TDM_RETURN_VAL_IF_FAIL(props != NULL, TDM_ERROR_INVALID_PARAMETER);
510         TDM_RETURN_VAL_IF_FAIL(count != NULL, TDM_ERROR_INVALID_PARAMETER);
511
512         _pthread_mutex_lock(&private_display->lock);
513
514         *props = (const tdm_prop *)private_output->caps.props;
515         *count = private_output->caps.prop_count;
516
517         _pthread_mutex_unlock(&private_display->lock);
518
519         return ret;
520 }
521
522 EXTERN tdm_error
523 tdm_output_get_available_modes(tdm_output *output,
524                                                            const tdm_output_mode **modes, int *count)
525 {
526         OUTPUT_FUNC_ENTRY();
527
528         TDM_RETURN_VAL_IF_FAIL(modes != NULL, TDM_ERROR_INVALID_PARAMETER);
529         TDM_RETURN_VAL_IF_FAIL(count != NULL, TDM_ERROR_INVALID_PARAMETER);
530
531         _pthread_mutex_lock(&private_display->lock);
532
533         *modes = (const tdm_output_mode *)private_output->caps.modes;
534         *count = private_output->caps.mode_count;
535
536         _pthread_mutex_unlock(&private_display->lock);
537
538         return ret;
539 }
540
541 EXTERN tdm_error
542 tdm_output_get_available_size(tdm_output *output, int *min_w, int *min_h,
543                                                           int *max_w, int *max_h, int *preferred_align)
544 {
545         OUTPUT_FUNC_ENTRY();
546
547         _pthread_mutex_lock(&private_display->lock);
548
549         if (min_w)
550                 *min_w = TDM_FRONT_VALUE(private_output->caps.min_w);
551         if (min_h)
552                 *min_h = TDM_FRONT_VALUE(private_output->caps.min_h);
553         if (max_w)
554                 *max_w = TDM_FRONT_VALUE(private_output->caps.max_w);
555         if (max_h)
556                 *max_h = TDM_FRONT_VALUE(private_output->caps.max_h);
557         if (preferred_align)
558                 *preferred_align = TDM_FRONT_VALUE(private_output->caps.preferred_align);
559
560         _pthread_mutex_unlock(&private_display->lock);
561
562         return ret;
563 }
564
565 EXTERN tdm_error
566 tdm_output_get_cursor_available_size(tdm_output *output, int *min_w, int *min_h,
567                                                                          int *max_w, int *max_h, int *preferred_align)
568 {
569         OUTPUT_FUNC_ENTRY();
570
571         _pthread_mutex_lock(&private_display->lock);
572
573         if (!tdm_module_check_abi(private_output->private_module, 1, 5)) {
574                 _pthread_mutex_unlock(&private_display->lock);
575                 return TDM_ERROR_BAD_REQUEST;
576         }
577
578         if (min_w)
579                 *min_w = TDM_FRONT_VALUE(private_output->caps.cursor_min_w);
580         if (min_h)
581                 *min_h = TDM_FRONT_VALUE(private_output->caps.cursor_min_h);
582         if (max_w)
583                 *max_w = TDM_FRONT_VALUE(private_output->caps.cursor_max_w);
584         if (max_h)
585                 *max_h = TDM_FRONT_VALUE(private_output->caps.cursor_max_h);
586         if (preferred_align)
587                 *preferred_align = TDM_FRONT_VALUE(private_output->caps.cursor_preferred_align);
588
589         _pthread_mutex_unlock(&private_display->lock);
590
591         return ret;
592 }
593
594 EXTERN tdm_error
595 tdm_output_get_physical_size(tdm_output *output, unsigned int *mmWidth,
596                                                          unsigned int *mmHeight)
597 {
598         OUTPUT_FUNC_ENTRY();
599
600         _pthread_mutex_lock(&private_display->lock);
601
602         if (mmWidth)
603                 *mmWidth = private_output->caps.mmWidth;
604         if (mmHeight)
605                 *mmHeight = private_output->caps.mmHeight;
606
607         _pthread_mutex_unlock(&private_display->lock);
608
609         return ret;
610 }
611
612 EXTERN tdm_error
613 tdm_output_get_subpixel(tdm_output *output, unsigned int *subpixel)
614 {
615         OUTPUT_FUNC_ENTRY();
616         TDM_RETURN_VAL_IF_FAIL(subpixel != NULL, TDM_ERROR_INVALID_PARAMETER);
617
618         _pthread_mutex_lock(&private_display->lock);
619
620         *subpixel = private_output->caps.subpixel;
621
622         _pthread_mutex_unlock(&private_display->lock);
623
624         return ret;
625 }
626
627 EXTERN tdm_error
628 tdm_output_get_pipe(tdm_output *output, unsigned int *pipe)
629 {
630         OUTPUT_FUNC_ENTRY();
631         TDM_RETURN_VAL_IF_FAIL(pipe != NULL, TDM_ERROR_INVALID_PARAMETER);
632
633         _pthread_mutex_lock(&private_display->lock);
634
635         *pipe = private_output->pipe;
636
637         _pthread_mutex_unlock(&private_display->lock);
638
639         return ret;
640 }
641
642 EXTERN tdm_error
643 tdm_output_get_primary_index(tdm_output *output, int *index)
644 {
645         tdm_private_layer *private_layer = NULL;
646
647         OUTPUT_FUNC_ENTRY();
648         TDM_RETURN_VAL_IF_FAIL(index != NULL, TDM_ERROR_INVALID_PARAMETER);
649
650         _pthread_mutex_lock(&private_display->lock);
651
652         LIST_FOR_EACH_ENTRY(private_layer, &private_output->layer_list, link) {
653                 if (private_layer->caps.capabilities & TDM_LAYER_CAPABILITY_PRIMARY) {
654                         *index = private_layer->index;
655                         break;
656                 }
657         }
658
659         _pthread_mutex_unlock(&private_display->lock);
660
661         return ret;
662 }
663
664 EXTERN tdm_error
665 tdm_output_set_property(tdm_output *output, unsigned int id, tdm_value value)
666 {
667         tdm_private_module *private_module;
668         tdm_func_output *func_output;
669         OUTPUT_FUNC_ENTRY();
670
671         _pthread_mutex_lock(&private_display->lock);
672
673         private_module = private_output->private_module;
674         func_output = &private_module->func_output;
675
676         if (!func_output->output_set_property) {
677                 /* LCOV_EXCL_START */
678                 _pthread_mutex_unlock(&private_display->lock);
679                 TDM_WRN("not implemented!!");
680                 return TDM_ERROR_NOT_IMPLEMENTED;
681                 /* LCOV_EXCL_STOP */
682         }
683
684         ret = func_output->output_set_property(private_output->output_backend, id,
685                                                                                    value);
686
687         _pthread_mutex_unlock(&private_display->lock);
688
689         return ret;
690 }
691
692 EXTERN tdm_error
693 tdm_output_get_property(tdm_output *output, unsigned int id, tdm_value *value)
694 {
695         tdm_private_module *private_module;
696         tdm_func_output *func_output;
697         OUTPUT_FUNC_ENTRY();
698
699         TDM_RETURN_VAL_IF_FAIL(value != NULL, TDM_ERROR_INVALID_PARAMETER);
700
701         _pthread_mutex_lock(&private_display->lock);
702
703         private_module = private_output->private_module;
704         func_output = &private_module->func_output;
705
706         if (!func_output->output_get_property) {
707                 /* LCOV_EXCL_START */
708                 _pthread_mutex_unlock(&private_display->lock);
709                 TDM_WRN("not implemented!!");
710                 return TDM_ERROR_NOT_IMPLEMENTED;
711                 /* LCOV_EXCL_STOP */
712         }
713
714         ret = func_output->output_get_property(private_output->output_backend, id,
715                                                                                    value);
716
717         _pthread_mutex_unlock(&private_display->lock);
718
719         return ret;
720 }
721
722 static void
723 _tdm_output_thread_cb_vblank(tdm_private_display *private_display, void *object, tdm_thread_cb_base *cb_base, void *user_data)
724 {
725         tdm_thread_cb_output_vblank *output_vblank = (tdm_thread_cb_output_vblank *)cb_base;
726         tdm_private_output_vblank_handler *vblank_handler = output_vblank->base.data;
727         tdm_private_output_vblank_handler *v = NULL, *vv = NULL;
728         tdm_private_output *private_output = object;
729         struct list_head clone_list;
730         int interval, sync;
731         pid_t tid = syscall(SYS_gettid);
732
733         TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
734
735         assert(vblank_handler->owner_tid == tid);
736
737         vblank_handler->sent_to_frontend = 0;
738
739         _tdm_output_vblank_timeout_update(private_output, 0);
740
741         tdm_thread_cb_remove(private_output, TDM_THREAD_CB_OUTPUT_VBLANK, vblank_handler, _tdm_output_thread_cb_vblank, NULL);
742
743         interval = vblank_handler->interval;
744         sync = vblank_handler->sync;
745
746         LIST_INITHEAD(&clone_list);
747
748         LIST_FOR_EACH_ENTRY_SAFE(v, vv, &private_output->vblank_handler_list, link) {
749                 if (v->interval != interval || v->sync != sync || v->owner_tid != tid)
750                         continue;
751
752                 LIST_DEL(&v->link);
753                 LIST_ADDTAIL(&v->link, &clone_list);
754         }
755
756         if (tdm_debug_module & TDM_DEBUG_COMMIT)
757                 TDM_INFO("----------------------------------------- output(%d) got vblank", private_output->pipe);
758
759         _pthread_mutex_unlock(&private_display->lock);
760         LIST_FOR_EACH_ENTRY_SAFE(v, vv, &clone_list, link) {
761                 if (tdm_debug_module & TDM_DEBUG_COMMIT)
762                         TDM_INFO("handler(%p)", v);
763
764                 LIST_DEL(&v->link);
765
766                 if (v->func)
767                         v->func(v->private_output,
768                                         output_vblank->sequence,
769                                         output_vblank->tv_sec,
770                                         output_vblank->tv_usec,
771                                         v->user_data);
772
773                 free(v);
774         }
775         _pthread_mutex_lock(&private_display->lock);
776
777         if (tdm_debug_module & TDM_DEBUG_COMMIT)
778                 TDM_INFO("-----------------------------------------...");
779 }
780
781 static void
782 _tdm_output_cb_vblank(tdm_output *output_backend, unsigned int sequence,
783                                           unsigned int tv_sec, unsigned int tv_usec, void *user_data)
784 {
785         tdm_private_output_vblank_handler *vblank_handler = user_data;
786         tdm_thread_cb_output_vblank output_vblank;
787         tdm_error ret;
788
789         memset(&output_vblank, 0, sizeof output_vblank);
790         output_vblank.base.type = TDM_THREAD_CB_OUTPUT_VBLANK;
791         output_vblank.base.length = sizeof output_vblank;
792         output_vblank.base.object_stamp = vblank_handler->private_output->stamp;
793         output_vblank.base.data = vblank_handler;
794         output_vblank.base.sync = 0;
795         output_vblank.sequence = sequence;
796         output_vblank.tv_sec = tv_sec;
797         output_vblank.tv_usec = tv_usec;
798
799         vblank_handler->sent_to_frontend = 1;
800
801         if (tdm_debug_module & TDM_DEBUG_COMMIT)
802                 TDM_INFO("output(%d) wait_vblank: handler(%p)", vblank_handler->private_output->pipe, vblank_handler);
803
804         ret = tdm_thread_cb_call(vblank_handler->private_output, &output_vblank.base, 1);
805         TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE);
806 }
807
808 static void
809 _tdm_output_thread_cb_commit(tdm_private_display *private_display, void *object, tdm_thread_cb_base *cb_base, void *user_data)
810 {
811         tdm_thread_cb_output_commit *output_commit = (tdm_thread_cb_output_commit *)cb_base;
812         tdm_private_output_commit_handler *output_commit_handler = output_commit->base.data;
813         tdm_private_output *private_output = object;
814         tdm_private_layer *private_layer = NULL;
815
816         TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
817
818         if (!output_commit_handler)
819                 return;
820
821         assert(output_commit_handler->owner_tid == syscall(SYS_gettid));
822
823         tdm_thread_cb_remove(private_output, TDM_THREAD_CB_OUTPUT_COMMIT, output_commit_handler, _tdm_output_thread_cb_commit, NULL);
824
825         LIST_DEL(&output_commit_handler->link);
826
827         if (tdm_debug_module & TDM_DEBUG_COMMIT) {
828                 TDM_INFO("----------------------------------------- output(%d) committed", private_output->pipe);
829                 TDM_INFO("handler(%p)", output_commit_handler);
830         }
831
832         if (private_output->commit_type == TDM_COMMIT_TYPE_OUTPUT) {
833                 /* In case of layer commit, the below will be handled in the layer commit callback */
834                 LIST_FOR_EACH_ENTRY(private_layer, &private_output->layer_list, link) {
835                         if (private_layer->committed_buffer)
836                                 tdm_layer_committed(private_layer, &private_layer->committed_buffer);
837                 }
838         }
839
840         if (output_commit_handler->func) {
841                 _pthread_mutex_unlock(&private_display->lock);
842                 output_commit_handler->func(private_output,
843                                                                         output_commit->sequence,
844                                                                         output_commit->tv_sec,
845                                                                         output_commit->tv_usec,
846                                                                         output_commit_handler->user_data);
847                 _pthread_mutex_lock(&private_display->lock);
848         }
849
850         free(output_commit_handler);
851
852         if (tdm_debug_module & TDM_DEBUG_COMMIT)
853                 TDM_INFO("-----------------------------------------...");
854 }
855
856 static void
857 _tdm_output_cb_commit(tdm_output *output_backend, unsigned int sequence,
858                                           unsigned int tv_sec, unsigned int tv_usec, void *user_data)
859 {
860         tdm_private_output_commit_handler *output_commit_handler = user_data;
861         tdm_private_output *private_output;
862         tdm_thread_cb_output_commit output_commit;
863         tdm_error ret;
864
865         if (output_commit_handler)
866                 private_output = output_commit_handler->private_output;
867         else
868                 private_output = tdm_display_find_private_output(tdm_display_get(), output_backend);
869
870         memset(&output_commit, 0, sizeof output_commit);
871         output_commit.base.type = TDM_THREAD_CB_OUTPUT_COMMIT;
872         output_commit.base.length = sizeof output_commit;
873         output_commit.base.object_stamp = private_output->stamp;
874         output_commit.base.data = output_commit_handler;
875         output_commit.base.sync = 0;
876         output_commit.sequence = sequence;
877         output_commit.tv_sec = tv_sec;
878         output_commit.tv_usec = tv_usec;
879
880         ret = tdm_thread_cb_call(private_output, &output_commit.base, 1);
881         TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE);
882 }
883
884 /* add_front: To distinguish between the user vblank handlers and the layer
885  *            commit vblank handlers. The layer commit handlers will be called
886  *            before calling the user vblank handlers.
887  */
888 static tdm_error
889 _tdm_output_wait_vblank(tdm_private_output *private_output, int interval, int sync,
890                                                 tdm_output_vblank_handler func, void *user_data,
891                                                 unsigned int add_front)
892 {
893         tdm_private_module *private_module;
894         tdm_func_output *func_output;
895         tdm_private_output_vblank_handler *vblank_handler = NULL, *v = NULL;
896         unsigned int skip_request = 0;
897         pid_t tid = syscall(SYS_gettid);
898         tdm_error ret = TDM_ERROR_NONE;
899
900         private_module = private_output->private_module;
901         func_output = &private_module->func_output;
902
903         /* interval SHOULD be at least 1 */
904         if (interval <= 0)
905                 interval = 1;
906
907         if (!func_output->output_wait_vblank) {
908                 /* LCOV_EXCL_START */
909                 TDM_WRN("not implemented!!");
910                 return TDM_ERROR_NOT_IMPLEMENTED;
911                 /* LCOV_EXCL_STOP */
912         }
913
914         if (!private_output->regist_vblank_cb) {
915                 private_output->regist_vblank_cb = 1;
916                 ret = func_output->output_set_vblank_handler(private_output->output_backend,
917                                 _tdm_output_cb_vblank);
918         }
919
920         vblank_handler = calloc(1, sizeof(tdm_private_output_vblank_handler));
921         if (!vblank_handler) {
922                 /* LCOV_EXCL_START */
923                 TDM_ERR("failed: alloc memory");
924                 return TDM_ERROR_OUT_OF_MEMORY;
925                 /* LCOV_EXCL_STOP */
926         }
927
928         if (tdm_debug_module & TDM_DEBUG_COMMIT)
929                 TDM_INFO("output(%d) wait_vblank: handler(%p)", private_output->pipe, vblank_handler);
930
931         LIST_FOR_EACH_ENTRY(v, &private_output->vblank_handler_list, link) {
932                 if (v->interval == interval && v->sync == sync && v->owner_tid == tid) {
933                         skip_request = 1;
934                         break;
935                 }
936         }
937
938         if (add_front)
939                 LIST_ADD(&vblank_handler->link, &private_output->vblank_handler_list);
940         else
941                 LIST_ADDTAIL(&vblank_handler->link, &private_output->vblank_handler_list);
942
943         vblank_handler->private_output = private_output;
944         vblank_handler->interval = interval;
945         vblank_handler->sync = sync;
946         vblank_handler->func = func;
947         vblank_handler->user_data = user_data;
948         vblank_handler->owner_tid = tid;
949
950         /* If there is the previous request, we can skip to call output_wait_vblank() */
951         if (!skip_request) {
952                 ret = tdm_thread_cb_add(private_output, TDM_THREAD_CB_OUTPUT_VBLANK, vblank_handler, _tdm_output_thread_cb_vblank, NULL);
953                 if (ret != TDM_ERROR_NONE) {
954                         TDM_ERR("tdm_thread_cb_add failed");
955                         goto wait_failed;
956                 }
957
958                 ret = func_output->output_wait_vblank(private_output->output_backend, interval,
959                                                                                           sync, vblank_handler);
960                 TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, wait_failed);
961
962                 _tdm_output_vblank_timeout_update(private_output, 1000);
963
964                 if (tdm_debug_module & TDM_DEBUG_COMMIT)
965                         TDM_INFO("output(%d) backend wait_vblank", private_output->pipe);
966         }
967
968         return ret;
969
970 wait_failed:
971         /* LCOV_EXCL_START */
972         if (vblank_handler) {
973                 tdm_thread_cb_remove(private_output, TDM_THREAD_CB_OUTPUT_VBLANK, vblank_handler, _tdm_output_thread_cb_vblank, NULL);
974                 LIST_DEL(&vblank_handler->link);
975                 free(vblank_handler);
976         }
977         return ret;
978         /* LCOV_EXCL_STOP */
979 }
980
981 EXTERN tdm_error
982 tdm_output_wait_vblank(tdm_output *output, int interval, int sync,
983                                            tdm_output_vblank_handler func, void *user_data)
984 {
985         OUTPUT_FUNC_ENTRY();
986         TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_ERROR_INVALID_PARAMETER);
987
988         _pthread_mutex_lock(&private_display->lock);
989
990         if (TDM_OUTPUT_DPMS_VSYNC_IS_OFF(private_output->current_dpms_value)) {
991                 TDM_WRN("output(%d) dpms: %s", private_output->pipe,
992                                 tdm_dpms_str(private_output->current_dpms_value));
993                 _pthread_mutex_unlock(&private_display->lock);
994                 return TDM_ERROR_DPMS_OFF;
995         }
996
997         ret = _tdm_output_wait_vblank(private_output, interval, sync, func, user_data, 0);
998
999         _pthread_mutex_unlock(&private_display->lock);
1000
1001         return ret;
1002 }
1003
1004 /* LCOV_EXCL_START */
1005 EXTERN tdm_error
1006 tdm_output_wait_vblank_add_front(tdm_output *output, int interval, int sync,
1007                                                                  tdm_output_vblank_handler func, void *user_data)
1008 {
1009         OUTPUT_FUNC_ENTRY();
1010
1011         _pthread_mutex_lock(&private_display->lock);
1012
1013         if (TDM_OUTPUT_DPMS_VSYNC_IS_OFF(private_output->current_dpms_value)) {
1014                 TDM_WRN("output(%d) dpms: %s", private_output->pipe,
1015                                 tdm_dpms_str(private_output->current_dpms_value));
1016                 _pthread_mutex_unlock(&private_display->lock);
1017                 return TDM_ERROR_DPMS_OFF;
1018         }
1019
1020         ret = _tdm_output_wait_vblank(private_output, interval, sync, func, user_data, 1);
1021
1022         _pthread_mutex_unlock(&private_display->lock);
1023
1024         return ret;
1025 }
1026 /* LCOV_EXCL_STOP */
1027
1028 INTERN void
1029 tdm_output_remove_vblank_handler_internal(tdm_output *output, tdm_output_vblank_handler func, void *user_data)
1030 {
1031         tdm_private_output *private_output = (tdm_private_output*)output;
1032         tdm_private_output_vblank_handler *v = NULL;
1033
1034         TDM_RETURN_IF_FAIL(private_output != NULL);
1035         TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
1036
1037         LIST_FOR_EACH_ENTRY(v, &private_output->vblank_handler_list, link) {
1038                 if (v->func == func && v->user_data == user_data) {
1039                         /* only set func & user_data to NULL. It will be freed when an event occurs */
1040                         v->func = NULL;
1041                         v->user_data = NULL;
1042                         break;
1043                 }
1044         }
1045 }
1046
1047 INTERN void
1048 tdm_output_remove_commit_handler_internal(tdm_output *output, tdm_output_commit_handler func, void *user_data)
1049 {
1050         tdm_private_output *private_output = (tdm_private_output*)output;
1051         tdm_private_output_commit_handler *c = NULL;
1052
1053         TDM_RETURN_IF_FAIL(private_output != NULL);
1054         TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
1055
1056         LIST_FOR_EACH_ENTRY(c, &private_output->output_commit_handler_list, link) {
1057                 if (c->func == func && c->user_data == user_data) {
1058                         /* only set func & user_data to NULL. It will be freed when an event occurs */
1059                         c->func = NULL;
1060                         c->user_data = NULL;
1061                         break;
1062                 }
1063         }
1064 }
1065
1066 EXTERN tdm_error
1067 tdm_output_remove_vblank_handler(tdm_output *output, tdm_output_vblank_handler func, void *user_data)
1068 {
1069         OUTPUT_FUNC_ENTRY();
1070
1071         _pthread_mutex_lock(&private_display->lock);
1072
1073         tdm_output_remove_vblank_handler_internal(output, func, user_data);
1074
1075         _pthread_mutex_unlock(&private_display->lock);
1076
1077         return ret;
1078 }
1079
1080 EXTERN tdm_error
1081 tdm_output_remove_commit_handler(tdm_output *output, tdm_output_commit_handler func, void *user_data)
1082 {
1083         OUTPUT_FUNC_ENTRY();
1084
1085         _pthread_mutex_lock(&private_display->lock);
1086
1087         tdm_output_remove_commit_handler_internal(output, func, user_data);
1088
1089         _pthread_mutex_unlock(&private_display->lock);
1090
1091         return ret;
1092 }
1093
1094 INTERN tdm_error
1095 tdm_output_commit_internal(tdm_output *output, int sync, tdm_output_commit_handler func, void *user_data)
1096 {
1097         tdm_private_output *private_output;
1098         tdm_private_module *private_module;
1099         tdm_func_output *func_output;
1100         tdm_private_output_commit_handler *output_commit_handler = NULL;
1101         tdm_private_layer *private_layer = NULL;
1102         tdm_output_dpms dpms_value = TDM_OUTPUT_DPMS_ON;
1103         tdm_error ret = TDM_ERROR_NONE;
1104
1105         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
1106
1107         private_output = (tdm_private_output*)output;
1108         private_module = private_output->private_module;
1109         func_output = &private_module->func_output;
1110
1111         if (!func_output->output_commit) {
1112                 /* LCOV_EXCL_START */
1113                 TDM_WRN("not implemented!!");
1114                 return TDM_ERROR_NOT_IMPLEMENTED;
1115                 /* LCOV_EXCL_STOP */
1116         }
1117
1118         ret = tdm_output_get_dpms_internal(output, &dpms_value);
1119         TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret);
1120
1121         if (!TDM_OUTPUT_DPMS_VSYNC_IS_OFF(dpms_value)) {
1122                 if (!private_output->regist_commit_cb) {
1123                         private_output->regist_commit_cb = 1;
1124                         ret = func_output->output_set_commit_handler(private_output->output_backend, _tdm_output_cb_commit);
1125                         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, commit_failed);
1126                 }
1127
1128                 output_commit_handler = calloc(1, sizeof(tdm_private_output_commit_handler));
1129                 if (!output_commit_handler) {
1130                         /* LCOV_EXCL_START */
1131                         TDM_ERR("failed: alloc memory");
1132                         return TDM_ERROR_OUT_OF_MEMORY;
1133                         /* LCOV_EXCL_STOP */
1134                 }
1135
1136                 ret = tdm_thread_cb_add(private_output, TDM_THREAD_CB_OUTPUT_COMMIT, output_commit_handler, _tdm_output_thread_cb_commit, NULL);
1137                 if (ret != TDM_ERROR_NONE) {
1138                         TDM_ERR("tdm_thread_cb_add failed");
1139                         free(output_commit_handler);
1140                         return ret;
1141                 }
1142
1143                 LIST_ADDTAIL(&output_commit_handler->link, &private_output->output_commit_handler_list);
1144                 output_commit_handler->private_output = private_output;
1145                 output_commit_handler->func = func;
1146                 output_commit_handler->user_data = user_data;
1147                 output_commit_handler->owner_tid = syscall(SYS_gettid);
1148
1149                 ret = func_output->output_commit(private_output->output_backend, sync,
1150                                 output_commit_handler);
1151                 TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, commit_failed);
1152
1153                 if (tdm_debug_module & TDM_DEBUG_COMMIT)
1154                         TDM_INFO("output(%d) backend commit: handle(%p) func(%p) user_data(%p)",
1155                                         private_output->pipe, output_commit_handler, func, user_data);
1156         }
1157
1158         /* Even if DPMS is off, committed_buffer should be changed because it will be referred
1159          * for tdm_layer_committed() function.
1160          */
1161         LIST_FOR_EACH_ENTRY(private_layer, &private_output->layer_list, link) {
1162                 if (!private_layer->waiting_buffer)
1163                         continue;
1164
1165                 if (private_layer->committed_buffer) {
1166                         tdm_layer_free_buffer(private_layer, private_layer->committed_buffer);
1167                         private_layer->committed_buffer = NULL;
1168                         if (tdm_debug_module & TDM_DEBUG_BUFFER)
1169                                 TDM_INFO("layer(%p) waiting_buffer(%p) committed_buffer(%p)",
1170                                                  private_layer, private_layer->waiting_buffer->buffer,
1171                                                  private_layer->committed_buffer);
1172                 }
1173
1174                 private_layer->committed_buffer = private_layer->waiting_buffer;
1175                 private_layer->waiting_buffer = NULL;
1176                 if (tdm_debug_module & TDM_DEBUG_BUFFER)
1177                         TDM_INFO("layer(%p) waiting_buffer(%p) committed_buffer(%p)",
1178                                          private_layer, private_layer->waiting_buffer,
1179                                          private_layer->committed_buffer->buffer);
1180         }
1181
1182         if (TDM_OUTPUT_DPMS_VSYNC_IS_OFF(dpms_value)) {
1183                 TDM_WRN("dpms %s. Directly call commit handler instead of commit.", tdm_dpms_str(dpms_value));
1184                 if (func)
1185                         func(output, 0, 0, 0, user_data);
1186         }
1187
1188         return ret;
1189
1190 commit_failed:
1191         /* LCOV_EXCL_START */
1192         if (output_commit_handler) {
1193                 tdm_thread_cb_remove(private_output, TDM_THREAD_CB_OUTPUT_COMMIT, output_commit_handler, _tdm_output_thread_cb_commit, NULL);
1194                 LIST_DEL(&output_commit_handler->link);
1195                 free(output_commit_handler);
1196         }
1197         return ret;
1198         /* LCOV_EXCL_STOP */
1199 }
1200
1201 EXTERN tdm_error
1202 tdm_output_commit(tdm_output *output, int sync, tdm_output_commit_handler func,
1203                                   void *user_data)
1204 {
1205         tdm_private_layer *private_layer = NULL;
1206
1207         OUTPUT_FUNC_ENTRY();
1208
1209         _pthread_mutex_lock(&private_display->lock);
1210
1211         if (private_output->commit_type == TDM_COMMIT_TYPE_NONE)
1212                 private_output->commit_type = TDM_COMMIT_TYPE_OUTPUT;
1213         else if (private_output->commit_type == TDM_COMMIT_TYPE_LAYER) {
1214                 TDM_ERR("Can't supported. Use tdm_layer_commit");
1215                 _pthread_mutex_unlock(&private_display->lock);
1216                 return TDM_ERROR_BAD_REQUEST;
1217         }
1218
1219         if (private_output->commit_per_vblank) {
1220                 TDM_ERR("Use tdm_layer_commit");
1221                 _pthread_mutex_unlock(&private_display->lock);
1222                 return TDM_ERROR_BAD_REQUEST;
1223         }
1224
1225         if (TDM_OUTPUT_DPMS_VSYNC_IS_OFF(private_output->current_dpms_value)) {
1226                 TDM_ERR("output(%d) dpms: %s", private_output->pipe,
1227                                 tdm_dpms_str(private_output->current_dpms_value));
1228                 _pthread_mutex_unlock(&private_display->lock);
1229                 return TDM_ERROR_DPMS_OFF;
1230         }
1231
1232         if (tdm_debug_module & TDM_DEBUG_COMMIT)
1233                 TDM_INFO("output(%d) commit", private_output->pipe);
1234
1235         /* apply the pending data of all layers */
1236         LIST_FOR_EACH_ENTRY(private_layer, &private_output->layer_list, link) {
1237                 tdm_layer_commit_pending_data(private_layer);
1238         }
1239
1240         ret = tdm_output_commit_internal(output, sync, func, user_data);
1241
1242         _pthread_mutex_unlock(&private_display->lock);
1243
1244         return ret;
1245 }
1246
1247 EXTERN tdm_error
1248 tdm_output_set_mode(tdm_output *output, const tdm_output_mode *mode)
1249 {
1250         tdm_private_module *private_module;
1251         tdm_func_output *func_output;
1252         OUTPUT_FUNC_ENTRY();
1253
1254         TDM_RETURN_VAL_IF_FAIL(mode != NULL, TDM_ERROR_INVALID_PARAMETER);
1255
1256         _pthread_mutex_lock(&private_display->lock);
1257
1258         private_module = private_output->private_module;
1259         func_output = &private_module->func_output;
1260
1261         if (!func_output->output_set_mode) {
1262                 /* LCOV_EXCL_START */
1263                 _pthread_mutex_unlock(&private_display->lock);
1264                 TDM_WRN("not implemented!!");
1265                 return TDM_ERROR_NOT_IMPLEMENTED;
1266                 /* LCOV_EXCL_STOP */
1267         }
1268
1269         ret = func_output->output_set_mode(private_output->output_backend, mode);
1270         if (ret == TDM_ERROR_NONE) {
1271                 private_output->current_mode = mode;
1272                 private_output->need_set_target_info = 1;
1273                 TDM_INFO("mode: %dx%d %dhz", mode->hdisplay, mode->vdisplay, mode->vrefresh);
1274         }
1275
1276         _pthread_mutex_unlock(&private_display->lock);
1277
1278         return ret;
1279 }
1280
1281 EXTERN tdm_error
1282 tdm_output_get_mode(tdm_output *output, const tdm_output_mode **mode)
1283 {
1284         OUTPUT_FUNC_ENTRY();
1285
1286         TDM_RETURN_VAL_IF_FAIL(mode != NULL, TDM_ERROR_INVALID_PARAMETER);
1287
1288         _pthread_mutex_lock(&private_display->lock);
1289
1290         *mode = private_output->current_mode;
1291
1292         _pthread_mutex_unlock(&private_display->lock);
1293
1294         return ret;
1295 }
1296
1297 EXTERN tdm_error
1298 tdm_output_set_dpms(tdm_output *output, tdm_output_dpms dpms_value)
1299 {
1300         tdm_private_module *private_module;
1301         tdm_func_output *func_output;
1302         OUTPUT_FUNC_ENTRY();
1303
1304         if (dpms_value > TDM_OUTPUT_DPMS_OFF) {
1305                 if (dpms_value & TDM_OUTPUT_DPMS_DEFAULT_MASK) {
1306                         TDM_ERR("Don't use the low-4bit for an extended DPMS mode: dpms_value(%x)", dpms_value);
1307                         return TDM_ERROR_BAD_REQUEST;
1308                 }
1309
1310                 if (!(private_output->caps.capabilities & TDM_OUTPUT_CAPABILITY_EXTENDED_DPMS)) {
1311                         TDM_ERR("output(%d) doesn't support the extended DPMS control: '%s'",
1312                                         private_output->pipe, tdm_dpms_str(dpms_value));
1313                         return TDM_ERROR_BAD_REQUEST;
1314                 }
1315         }
1316
1317         _pthread_mutex_lock(&private_display->lock);
1318
1319         if (private_output->waiting_dpms_change) {
1320                 TDM_ERR("DPMS is not changed yet. Can't be changed twice");
1321                 _pthread_mutex_unlock(&private_display->lock);
1322                 return TDM_ERROR_BAD_REQUEST;
1323         }
1324
1325         private_module = private_output->private_module;
1326         func_output = &private_module->func_output;
1327
1328         TDM_INFO("output(%d) dpms '%s'", private_output->pipe, tdm_dpms_str(dpms_value));
1329
1330         if (func_output->output_set_dpms)
1331                 ret = func_output->output_set_dpms(private_output->output_backend, dpms_value);
1332         else {
1333                 /* LCOV_EXCL_START */
1334                 ret = TDM_ERROR_NONE;
1335                 TDM_WRN("not implemented!!");
1336                 goto done;
1337                 /* LCOV_EXCL_STOP */
1338         }
1339
1340 done:
1341         if (ret == TDM_ERROR_NONE) {
1342                 if (private_output->current_dpms_value != dpms_value) {
1343                         private_output->current_dpms_value = dpms_value;
1344                         _tdm_output_call_thread_cb_dpms(private_output, dpms_value);
1345                 }
1346                 TDM_INFO("output(%d) dpms '%s' done", private_output->pipe, tdm_dpms_str(dpms_value));
1347         } else {
1348                 tdm_output_dpms temp = TDM_OUTPUT_DPMS_OFF;
1349
1350                 /* update current_dpms_value forcely */
1351                 tdm_output_get_dpms_internal(output, &temp);
1352
1353                 TDM_ERR("output(%d) set_dpms failed: dpms '%s'", private_output->pipe, tdm_dpms_str(temp));
1354         }
1355
1356         _pthread_mutex_unlock(&private_display->lock);
1357
1358         return ret;
1359 }
1360
1361 /* LCOV_EXCL_START */
1362 EXTERN tdm_error
1363 tdm_output_set_dpms_async(tdm_output *output, tdm_output_dpms dpms_value)
1364 {
1365         tdm_private_module *private_module;
1366         tdm_func_output *func_output;
1367         OUTPUT_FUNC_ENTRY();
1368
1369         if (!(private_output->caps.capabilities & TDM_OUTPUT_CAPABILITY_ASYNC_DPMS)) {
1370                 TDM_ERR("output doesn't support the asynchronous DPMS control!");
1371                 return TDM_ERROR_BAD_REQUEST;
1372         }
1373
1374         if (dpms_value > TDM_OUTPUT_DPMS_OFF) {
1375                 if (dpms_value & TDM_OUTPUT_DPMS_DEFAULT_MASK) {
1376                         TDM_ERR("Don't use the low-4bit for an extended DPMS mode: dpms_value(%x)", dpms_value);
1377                         return TDM_ERROR_BAD_REQUEST;
1378                 }
1379
1380                 if (!(private_output->caps.capabilities & TDM_OUTPUT_CAPABILITY_EXTENDED_DPMS)) {
1381                         TDM_ERR("output(%d) doesn't support the extended DPMS control: '%s'",
1382                                         private_output->pipe, tdm_dpms_str(dpms_value));
1383                         return TDM_ERROR_BAD_REQUEST;
1384                 }
1385         }
1386
1387         _pthread_mutex_lock(&private_display->lock);
1388
1389         if (private_output->waiting_dpms_change) {
1390                 TDM_ERR("DPMS is not changed yet. Can't be changed twice");
1391                 _pthread_mutex_unlock(&private_display->lock);
1392                 return TDM_ERROR_BAD_REQUEST;
1393         }
1394
1395         private_module = private_output->private_module;
1396         func_output = &private_module->func_output;
1397         if (!func_output->output_set_dpms_handler) {
1398                 TDM_WRN("not implemented: output_set_dpms_handler");
1399                 _pthread_mutex_unlock(&private_display->lock);
1400                 return TDM_ERROR_NOT_IMPLEMENTED;
1401         }
1402
1403         if (!func_output->output_set_dpms_async) {
1404                 TDM_WRN("not implemented: output_set_dpms_async");
1405                 _pthread_mutex_unlock(&private_display->lock);
1406                 return TDM_ERROR_NOT_IMPLEMENTED;
1407         }
1408
1409         if (!private_output->regist_dpms_cb) {
1410                 private_output->regist_dpms_cb = 1;
1411                 ret = func_output->output_set_dpms_handler(private_output->output_backend,
1412                                 tdm_output_cb_dpms, private_output);
1413                 if (ret != TDM_ERROR_NONE) {
1414                         _pthread_mutex_unlock(&private_display->lock);
1415                         TDM_ERR("Can't set the dpms handler!!");
1416                         return ret;
1417                 }
1418         }
1419
1420         TDM_INFO("output(%d) dpms async '%s'", private_output->pipe, tdm_dpms_str(dpms_value));
1421
1422         ret = func_output->output_set_dpms_async(private_output->output_backend, dpms_value);
1423
1424         if (ret == TDM_ERROR_NONE) {
1425                 private_output->waiting_dpms_change = 1;
1426                 TDM_INFO("output(%d) dpms async '%s' waiting", private_output->pipe, tdm_dpms_str(dpms_value));
1427         } else {
1428                 tdm_output_dpms temp = TDM_OUTPUT_DPMS_OFF;
1429
1430                 /* update current_dpms_value forcely */
1431                 tdm_output_get_dpms_internal(output, &temp);
1432
1433                 TDM_ERR("output(%d) set_dpms_async failed: dpms '%s'", private_output->pipe, tdm_dpms_str(temp));
1434         }
1435
1436         _pthread_mutex_unlock(&private_display->lock);
1437
1438         return ret;
1439 }
1440 /* LCOV_EXCL_STOP */
1441
1442 INTERN tdm_error
1443 tdm_output_get_dpms_internal(tdm_output *output, tdm_output_dpms *dpms_value)
1444 {
1445         tdm_private_output *private_output;
1446         tdm_private_module *private_module;
1447         tdm_func_output *func_output;
1448         tdm_error ret = TDM_ERROR_NONE;
1449
1450         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
1451
1452         private_output = (tdm_private_output*)output;
1453
1454         /* TODO: this is ugly. But before calling backend's output_get_dpms(), we have
1455          * to check if all backends's DPMS operation has no problem. In future, we'd
1456          * better use new env instead of using commit_per_vblank variable to distinguish
1457          * whether we use the stored value or backend's output_get_dpms.
1458          */
1459         if (!private_output->commit_per_vblank) {
1460                 *dpms_value = private_output->current_dpms_value;
1461                 return TDM_ERROR_NONE;
1462         }
1463
1464         private_module = private_output->private_module;
1465         func_output = &private_module->func_output;
1466
1467         if (!func_output->output_get_dpms) {
1468                 /* LCOV_EXCL_START */
1469                 *dpms_value = private_output->current_dpms_value;
1470                 TDM_WRN("not implemented!!");
1471                 return TDM_ERROR_NONE;
1472                 /* LCOV_EXCL_STOP */
1473         }
1474
1475         ret = func_output->output_get_dpms(private_output->output_backend, dpms_value);
1476         if (ret != TDM_ERROR_NONE) {
1477                 /* LCOV_EXCL_START */
1478                 TDM_ERR("output_get_dpms failed");
1479                 *dpms_value = TDM_OUTPUT_DPMS_OFF;
1480                 /* LCOV_EXCL_STOP */
1481         }
1482
1483         /* checking with backend's value */
1484         if (*dpms_value != private_output->current_dpms_value) {
1485                 TDM_ERR("output(%d) dpms changed suddenly: %s -> %s",
1486                                 private_output->pipe, tdm_dpms_str(private_output->current_dpms_value),
1487                                 tdm_dpms_str(*dpms_value));
1488                 private_output->current_dpms_value = *dpms_value;
1489                 _tdm_output_call_thread_cb_dpms(private_output, *dpms_value);
1490         }
1491
1492         return ret;
1493 }
1494
1495 EXTERN tdm_error
1496 tdm_output_get_dpms(tdm_output *output, tdm_output_dpms *dpms_value)
1497 {
1498         OUTPUT_FUNC_ENTRY();
1499
1500         TDM_RETURN_VAL_IF_FAIL(dpms_value != NULL, TDM_ERROR_INVALID_PARAMETER);
1501
1502         _pthread_mutex_lock(&private_display->lock);
1503
1504         ret = tdm_output_get_dpms_internal(output, dpms_value);
1505
1506         _pthread_mutex_unlock(&private_display->lock);
1507
1508         return ret;
1509 }
1510
1511 EXTERN tdm_error
1512 tdm_output_has_capture_capability(tdm_output *output, unsigned int *has_capability)
1513 {
1514         tdm_private_module *private_module;
1515
1516         OUTPUT_FUNC_ENTRY();
1517
1518         TDM_RETURN_VAL_IF_FAIL(has_capability != NULL, TDM_ERROR_INVALID_PARAMETER);
1519
1520         _pthread_mutex_lock(&private_display->lock);
1521
1522         private_module = private_output->private_module;
1523
1524         if (!(private_module->capabilities & TDM_DISPLAY_CAPABILITY_CAPTURE))
1525                 *has_capability = 0;
1526         else if (!(private_module->caps_capture.capabilities & TDM_CAPTURE_CAPABILITY_OUTPUT))
1527                 *has_capability = 0;
1528         else
1529                 *has_capability = 1;
1530
1531         _pthread_mutex_unlock(&private_display->lock);
1532
1533         return ret;
1534 }
1535
1536 EXTERN tdm_capture *
1537 tdm_output_create_capture(tdm_output *output, tdm_error *error)
1538 {
1539         tdm_capture *capture = NULL;
1540
1541         OUTPUT_FUNC_ENTRY_ERROR();
1542
1543         _pthread_mutex_lock(&private_display->lock);
1544
1545         capture = (tdm_capture *)tdm_capture_create_output_internal(private_output, error);
1546
1547         _pthread_mutex_unlock(&private_display->lock);
1548
1549         return capture;
1550 }
1551
1552 EXTERN tdm_hwc *
1553 tdm_output_get_hwc(tdm_output *output, tdm_error *error)
1554 {
1555         tdm_private_hwc *private_hwc = NULL;
1556
1557         OUTPUT_FUNC_ENTRY_ERROR();
1558
1559         _pthread_mutex_lock(&private_display->lock);
1560
1561         private_hwc = private_output->private_hwc;
1562         if (!private_hwc) {
1563                 if (error)
1564                         *error = TDM_ERROR_BAD_REQUEST;
1565                 _pthread_mutex_unlock(&private_display->lock);
1566                 return NULL;
1567         }
1568
1569         if (error)
1570                 *error = TDM_ERROR_NONE;
1571
1572         _pthread_mutex_unlock(&private_display->lock);
1573
1574         return private_hwc;
1575 }
1576
1577 INTERN tdm_error
1578 tdm_output_choose_commit_per_vblank_mode(tdm_private_output *private_output, int mode)
1579 {
1580         if (!private_output)
1581                 return TDM_ERROR_INVALID_PARAMETER;
1582
1583         if (mode < 0 || mode > 2)
1584                 return TDM_ERROR_INVALID_PARAMETER;
1585
1586         private_output->commit_per_vblank = mode;
1587
1588         if (private_output->commit_per_vblank == 0)
1589                 TDM_INFO("commit per vblank: disable");
1590         else if (private_output->commit_per_vblank == 1)
1591                 TDM_INFO("commit per vblank: enable (1 layer)");
1592         else if (private_output->commit_per_vblank == 2)
1593                 TDM_INFO("commit per vblank: enable (previous commit)");
1594
1595         return TDM_ERROR_NONE;
1596 }
1597 /* LCOV_EXCL_STOP */