apply tizen coding style
[platform/core/uifw/libtdm.git] / src / tdm_vblank.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 <sc1.lim@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.h"
41 #include "tdm_private.h"
42 #include "tdm_list.h"
43
44 /* CAUTION:
45  * tdm vblank doesn't care about thread things.
46  * However, to use tdm_event_loop_xxx functions, have to use the internal function.
47  * So need to lock/unlock the mutex of private_display.
48  */
49
50 /* TDM vblank
51  * - aligned by HW vblank
52  * - use a tdm_event_loop_source object only.
53  */
54
55 #define VER(fmt, arg...)   TDM_ERR("[%p] "fmt, private_vblank, ##arg)
56 #define VWR(fmt, arg...)   TDM_WRN("[%p] "fmt, private_vblank, ##arg)
57 #define VIN(fmt, arg...)   TDM_INFO("[%p] "fmt, private_vblank, ##arg)
58 #define VDB(fmt, arg...)   TDM_DBG("[%p] "fmt, private_vblank, ##arg)
59
60 typedef struct _tdm_vblank_wait_info tdm_vblank_wait_info;
61
62 typedef struct _tdm_private_vblank {
63         struct list_head link;
64
65         tdm_display *dpy;
66         tdm_output *output;
67         tdm_output_dpms dpms;
68         unsigned int vrefresh;
69
70         unsigned int check_HW_or_SW;
71         unsigned int fps;
72         int offset;
73         unsigned int enable_fake;
74
75         double vblank_gap;
76         unsigned int last_seq;
77         unsigned int last_tv_sec;
78         unsigned int last_tv_usec;
79
80         /* for HW */
81         double HW_vblank_gap;
82         unsigned int HW_enable;
83         unsigned int HW_quotient;
84         struct list_head HW_wait_list;
85
86         /* for SW */
87         tdm_event_loop_source *SW_timer;
88         struct list_head SW_pending_wait_list;
89         struct list_head SW_wait_list;
90 #if 0
91         tdm_vblank_wait_info *SW_align_wait;
92         double SW_align_offset;
93         unsigned int SW_align_sec;
94         unsigned int SW_align_usec;
95 #endif
96 } tdm_private_vblank;
97
98 struct _tdm_vblank_wait_info {
99         struct list_head link;
100         struct list_head valid_link;
101
102         unsigned int stamp;
103
104         unsigned int req_sec;
105         unsigned int req_usec;
106         unsigned int interval;
107
108         tdm_vblank_handler func;
109         void *user_data;
110         tdm_private_vblank *private_vblank;
111
112         /* target_sec can be 0 when last_tv_sec is 0 because we can't calculate
113          * target_sec without last_tv_sec. So we have to call tdm_output_wait_vblank
114          * to fill last_tv_sec at the first time.
115          */
116         unsigned int target_sec;
117         unsigned int target_usec;
118         unsigned int target_seq;
119         int target_hw_interval;
120 };
121
122 static struct list_head vblank_list;
123 static struct list_head valid_wait_list;
124 static unsigned int vblank_list_inited;
125 static unsigned int stamp = 0;
126
127 static tdm_error _tdm_vblank_wait_SW(tdm_vblank_wait_info *wait_info);
128 #if 0
129 static void _tdm_vblank_sw_timer_align(tdm_private_vblank *private_vblank);
130 #endif
131
132 #if 0
133 static void
134 _print_list(struct list_head *list)
135 {
136         tdm_vblank_wait_info *w = NULL;
137
138         LIST_FOR_EACH_ENTRY(w, list, link) {
139                 printf(" %d", w->interval);
140         }
141         printf("\n");
142 }
143 #endif
144
145 static inline unsigned int
146 _tdm_vblank_check_valid(tdm_vblank_wait_info *wait_info)
147 {
148         tdm_vblank_wait_info *w = NULL;
149
150         if (!wait_info)
151                 return 0;
152
153         LIST_FOR_EACH_ENTRY(w, &valid_wait_list, valid_link) {
154                 if (w->stamp == wait_info->stamp)
155                         return 1;
156         }
157
158         return 0;
159 }
160
161 static inline unsigned int
162 _tdm_vblank_find_wait(tdm_vblank_wait_info *wait_info, struct list_head *list)
163 {
164         tdm_vblank_wait_info *w = NULL;
165
166         if (!wait_info)
167                 return 0;
168
169         LIST_FOR_EACH_ENTRY(w, list, link) {
170                 if (w->stamp == wait_info->stamp)
171                         return 1;
172         }
173
174         return 0;
175 }
176
177 static inline void
178 _tdm_vblank_insert_wait(tdm_vblank_wait_info *wait_info, struct list_head *list)
179 {
180         tdm_vblank_wait_info *w = NULL;
181         tdm_vblank_wait_info *found = NULL;
182
183         if (LIST_IS_EMPTY(list)) {
184                 LIST_ADDTAIL(&wait_info->link, list);
185                 return;
186         }
187
188         LIST_FOR_EACH_ENTRY(w, list, link) {
189                 /* If last_tv_sec == 0, we can't calculate target_sec. */
190                 if (wait_info->target_sec == 0) {
191                         if (w->interval <= wait_info->interval) {
192                                 found = w;
193                                 continue;
194                         }
195                 } else {
196                         if (w->target_sec < wait_info->target_sec) {
197                                 found = w;
198                                 continue;
199                         }
200                         if (w->target_sec == wait_info->target_sec && w->target_usec <= wait_info->target_usec) {
201                                 found = w;
202                                 continue;
203                         }
204                 }
205         }
206
207         if (found)
208                 LIST_ADD(&wait_info->link, &found->link);
209         else
210                 LIST_ADDTAIL(&wait_info->link, list->next);
211 }
212
213 static void
214 _tdm_vblank_change_to_SW(tdm_private_vblank *private_vblank)
215 {
216         tdm_vblank_wait_info *w = NULL, *ww = NULL;
217
218         VIN("Change to SW");
219
220         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->HW_wait_list, link) {
221                 LIST_DEL(&w->link);
222                 _tdm_vblank_wait_SW(w);
223         }
224 }
225
226 static void
227 _tdm_vblank_free_HW_wait(tdm_private_vblank *private_vblank, tdm_error error, unsigned int call_cb)
228 {
229         tdm_vblank_wait_info *w = NULL, *ww = NULL;
230
231         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->HW_wait_list, link) {
232                 LIST_DEL(&w->link);
233                 LIST_DEL(&w->valid_link);
234
235                 if (call_cb && w->func)
236                         w->func(private_vblank, error, 0, 0, 0, w->user_data);
237
238                 free(w);
239         }
240 }
241
242 static void
243 _tdm_vblank_cb_output_change(tdm_output *output, tdm_output_change_type type,
244                                                          tdm_value value, void *user_data)
245 {
246         tdm_private_vblank *private_vblank = user_data;
247
248         TDM_RETURN_IF_FAIL(private_vblank != NULL);
249
250         switch (type) {
251         case TDM_OUTPUT_CHANGE_DPMS:
252                 if (private_vblank->dpms == value.u32)
253                         break;
254                 VIN("dpms %s", tdm_dpms_str(value.u32));
255                 private_vblank->dpms = value.u32;
256                 private_vblank->check_HW_or_SW = 1;
257                 if (private_vblank->dpms != TDM_OUTPUT_DPMS_ON) {
258 #if 0
259                         if (private_vblank->SW_align_wait) {
260                                 LIST_DEL(&private_vblank->SW_align_wait->valid_link);
261                                 free(private_vblank->SW_align_wait);
262                                 private_vblank->SW_align_wait = NULL;
263                         }
264 #endif
265
266                         if (private_vblank->enable_fake)
267                                 _tdm_vblank_change_to_SW(private_vblank);
268                         else
269                                 _tdm_vblank_free_HW_wait(private_vblank, TDM_ERROR_DPMS_OFF, 1);
270                 }
271                 break;
272         case TDM_OUTPUT_CHANGE_CONNECTION:
273                 VIN("output %s", tdm_status_str(value.u32));
274                 if (value.u32 == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
275                         _tdm_vblank_free_HW_wait(private_vblank, 0, 0);
276                 break;
277         default:
278                 break;
279         }
280 }
281
282 tdm_vblank*
283 tdm_vblank_create(tdm_display *dpy, tdm_output *output, tdm_error *error)
284 {
285         tdm_private_vblank *private_vblank;
286         const tdm_output_mode *mode = NULL;
287         tdm_output_dpms dpms = TDM_OUTPUT_DPMS_ON;
288         tdm_error ret;
289
290         TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(dpy != NULL, TDM_ERROR_INVALID_PARAMETER, NULL);
291         TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(output != NULL, TDM_ERROR_INVALID_PARAMETER, NULL);
292
293         if (error)
294                 *error = TDM_ERROR_NONE;
295
296         if (!vblank_list_inited) {
297                 LIST_INITHEAD(&vblank_list);
298                 LIST_INITHEAD(&valid_wait_list);
299                 vblank_list_inited = 1;
300         }
301
302         tdm_output_get_mode(output, &mode);
303         if (!mode) {
304                 if (error)
305                         *error = TDM_ERROR_OPERATION_FAILED;
306                 TDM_ERR("no mode");
307                 return NULL;
308         }
309
310         tdm_output_get_dpms(output, &dpms);
311
312         private_vblank = calloc(1, sizeof *private_vblank);
313         if (!private_vblank) {
314                 if (error)
315                         *error = TDM_ERROR_OUT_OF_MEMORY;
316                 VER("alloc failed");
317                 return NULL;
318         }
319
320         tdm_output_add_change_handler(output, _tdm_vblank_cb_output_change, private_vblank);
321
322         private_vblank->dpy = dpy;
323         private_vblank->output = output;
324         private_vblank->dpms = dpms;
325         private_vblank->vrefresh = mode->vrefresh;
326         private_vblank->HW_vblank_gap = (double)1000000 / private_vblank->vrefresh;
327
328         private_vblank->check_HW_or_SW = 1;
329         private_vblank->fps = mode->vrefresh;
330
331         LIST_INITHEAD(&private_vblank->HW_wait_list);
332
333         LIST_INITHEAD(&private_vblank->SW_pending_wait_list);
334         LIST_INITHEAD(&private_vblank->SW_wait_list);
335
336         LIST_ADD(&private_vblank->link, &vblank_list);
337
338         if (tdm_debug_module & TDM_DEBUG_VBLANK)
339                 VIN("created. vrefresh(%d) dpms(%d)",
340                         private_vblank->vrefresh, private_vblank->dpms);
341
342         return (tdm_vblank*)private_vblank;
343 }
344
345 void
346 tdm_vblank_destroy(tdm_vblank *vblank)
347 {
348         tdm_private_vblank *private_vblank = vblank;
349         tdm_vblank_wait_info *w = NULL, *ww = NULL;
350
351         if (!private_vblank)
352                 return;
353
354         LIST_DEL(&private_vblank->link);
355
356 #if 0
357         if (private_vblank->SW_align_wait) {
358                 LIST_DEL(&private_vblank->SW_align_wait->valid_link);
359                 free(private_vblank->SW_align_wait);
360         }
361 #endif
362
363         if (private_vblank->SW_timer) {
364                 tdm_display_lock(private_vblank->dpy);
365                 tdm_event_loop_source_remove(private_vblank->SW_timer);
366                 tdm_display_unlock(private_vblank->dpy);
367         }
368
369         tdm_output_remove_change_handler(private_vblank->output,
370                                                                          _tdm_vblank_cb_output_change, private_vblank);
371
372         _tdm_vblank_free_HW_wait(private_vblank, 0, 0);
373
374         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_pending_wait_list, link) {
375                 LIST_DEL(&w->link);
376                 LIST_DEL(&w->valid_link);
377                 free(w);
378         }
379
380         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_wait_list, link) {
381                 LIST_DEL(&w->link);
382                 LIST_DEL(&w->valid_link);
383                 free(w);
384         }
385
386         if (tdm_debug_module & TDM_DEBUG_VBLANK)
387                 VIN("destroyed");
388
389         free(private_vblank);
390 }
391
392 tdm_error
393 tdm_vblank_set_fps(tdm_vblank *vblank, unsigned int fps)
394 {
395         tdm_private_vblank *private_vblank = vblank;
396
397         TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
398         TDM_RETURN_VAL_IF_FAIL(fps > 0, TDM_ERROR_INVALID_PARAMETER);
399
400         if (private_vblank->fps == fps)
401                 return TDM_ERROR_NONE;
402
403         private_vblank->fps = fps;
404         private_vblank->check_HW_or_SW = 1;
405
406         if (tdm_debug_module & TDM_DEBUG_VBLANK)
407                 VIN("fps(%d)", private_vblank->fps);
408
409         return TDM_ERROR_NONE;
410 }
411
412 tdm_error
413 tdm_vblank_set_offset(tdm_vblank *vblank, int offset)
414 {
415         tdm_private_vblank *private_vblank = vblank;
416
417         TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
418
419         if (private_vblank->offset == offset)
420                 return TDM_ERROR_NONE;
421
422         private_vblank->offset = offset;
423         private_vblank->check_HW_or_SW = 1;
424
425         if (tdm_debug_module & TDM_DEBUG_VBLANK)
426                 VIN("offset(%d)", private_vblank->offset);
427
428         return TDM_ERROR_NONE;
429 }
430
431 tdm_error
432 tdm_vblank_set_enable_fake(tdm_vblank *vblank, unsigned int enable_fake)
433 {
434         tdm_private_vblank *private_vblank = vblank;
435
436         TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
437
438         if (private_vblank->enable_fake == enable_fake)
439                 return TDM_ERROR_NONE;
440
441         private_vblank->enable_fake = enable_fake;
442
443         if (tdm_debug_module & TDM_DEBUG_VBLANK)
444                 VIN("enable_fake(%d)", private_vblank->enable_fake);
445
446         return TDM_ERROR_NONE;
447 }
448
449 static void
450 _tdm_vblank_cb_vblank_HW(tdm_output *output, unsigned int sequence,
451                                                  unsigned int tv_sec, unsigned int tv_usec,
452                                                  void *user_data)
453 {
454         tdm_vblank_wait_info *wait_info = user_data;
455         tdm_private_vblank *private_vblank;
456
457         if (!_tdm_vblank_check_valid(wait_info)) {
458                 TDM_DBG("can't find wait(%p) from valid_wait_list", wait_info);
459                 return;
460         }
461
462         private_vblank = wait_info->private_vblank;
463         TDM_RETURN_IF_FAIL(private_vblank != NULL);
464
465         if (!_tdm_vblank_find_wait(wait_info, &private_vblank->HW_wait_list)) {
466                 VDB("can't find wait(%p)", wait_info);
467                 return;
468         }
469
470         LIST_DEL(&wait_info->link);
471         LIST_DEL(&wait_info->valid_link);
472
473         private_vblank->last_seq = wait_info->target_seq;
474         private_vblank->last_tv_sec = tv_sec;
475         private_vblank->last_tv_usec = tv_usec;
476
477         if (wait_info->func)
478                 wait_info->func(private_vblank, TDM_ERROR_NONE, private_vblank->last_seq,
479                                                 tv_sec, tv_usec, wait_info->user_data);
480
481         if (tdm_debug_module & TDM_DEBUG_VBLANK)
482                 VIN("wait(%p) done", wait_info);
483
484         free(wait_info);
485 }
486
487 static tdm_error
488 _tdm_vblank_wait_HW(tdm_vblank_wait_info *wait_info)
489 {
490         tdm_private_vblank *private_vblank = wait_info->private_vblank;
491         tdm_error ret;
492
493         TDM_RETURN_VAL_IF_FAIL(wait_info->target_hw_interval > 0, TDM_ERROR_OPERATION_FAILED);
494
495         _tdm_vblank_insert_wait(wait_info, &private_vblank->HW_wait_list);
496
497         ret = tdm_output_wait_vblank(private_vblank->output, wait_info->target_hw_interval, 0,
498                                                                  _tdm_vblank_cb_vblank_HW, wait_info);
499
500         if (ret != TDM_ERROR_NONE) {
501                 VWR("wait(%p) failed", wait_info);
502                 LIST_DEL(&wait_info->link);
503                 return ret;
504         }
505
506         if (tdm_debug_module & TDM_DEBUG_VBLANK)
507                 VIN("wait(%p) waiting", wait_info);
508
509         return TDM_ERROR_NONE;
510 }
511
512 static tdm_error
513 _tdm_vblank_cb_vblank_SW(void *user_data)
514 {
515         tdm_private_vblank *private_vblank = user_data;
516         tdm_vblank_wait_info *first_wait_info = NULL, *w = NULL, *ww = NULL;
517
518         TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_OPERATION_FAILED);
519
520         if (LIST_IS_EMPTY(&private_vblank->SW_wait_list)) {
521                 VER("no wait_info");
522                 return TDM_ERROR_OPERATION_FAILED;
523         }
524
525         first_wait_info = container_of(private_vblank->SW_wait_list.next, first_wait_info, link);
526         TDM_RETURN_VAL_IF_FAIL(first_wait_info != NULL, TDM_ERROR_OPERATION_FAILED);
527
528         if (tdm_debug_module & TDM_DEBUG_VBLANK)
529                 VIN("wait(%p) done", first_wait_info);
530
531         private_vblank->last_seq = first_wait_info->target_seq;
532         private_vblank->last_tv_sec = first_wait_info->target_sec;
533         private_vblank->last_tv_usec = first_wait_info->target_usec;
534
535         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_wait_list, link) {
536                 if (w->target_sec != first_wait_info->target_sec ||
537                         w->target_usec != first_wait_info->target_usec)
538                         break;
539
540                 LIST_DEL(&w->link);
541                 LIST_DEL(&w->valid_link);
542
543                 if (w->func)
544                         w->func(private_vblank, TDM_ERROR_NONE, w->target_seq,
545                                         w->target_sec, w->target_usec,
546                                         w->user_data);
547
548                 free(w);
549         }
550
551         return TDM_ERROR_NONE;
552 }
553
554 static void
555 _tdm_vblank_cb_vblank_SW_first(tdm_output *output, unsigned int sequence,
556                                                            unsigned int tv_sec, unsigned int tv_usec,
557                                                            void *user_data)
558 {
559         tdm_vblank_wait_info *wait_info = user_data;
560         tdm_private_vblank *private_vblank;
561         tdm_vblank_wait_info *w = NULL, *ww = NULL;
562         unsigned int min_interval = 0;
563         unsigned long last;
564
565         if (!_tdm_vblank_check_valid(wait_info))
566                 return;
567
568         private_vblank = wait_info->private_vblank;
569         TDM_RETURN_IF_FAIL(private_vblank != NULL);
570
571         if (LIST_IS_EMPTY(&private_vblank->SW_pending_wait_list)) {
572                 VER("no wait_info");
573                 return;
574         }
575
576         w = container_of((&private_vblank->SW_pending_wait_list)->next, w, link);
577         TDM_RETURN_IF_FAIL(w != NULL);
578
579         if (tdm_debug_module & TDM_DEBUG_VBLANK)
580                 VIN("wait(%p) done", w);
581
582         min_interval = w->interval;
583
584         last = (unsigned long)tv_sec * 1000000 + tv_usec;
585         last -= private_vblank->offset * 1000;
586
587         private_vblank->last_seq = min_interval;
588         private_vblank->last_tv_sec = last / 1000000;
589         private_vblank->last_tv_usec = last % 1000000;
590
591         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_pending_wait_list, link) {
592                 if (w->interval == min_interval) {
593                         LIST_DEL(&w->link);
594                         LIST_DEL(&w->valid_link);
595
596                         if (w->func)
597                                 w->func(private_vblank, TDM_ERROR_NONE, private_vblank->last_seq,
598                                                 tv_sec, tv_usec, w->user_data);
599                         free(w);
600                 } else {
601                         LIST_DEL(&w->link);
602                         w->interval -= min_interval;
603                         _tdm_vblank_wait_SW(w);
604                 }
605         }
606 }
607
608 static tdm_error
609 _tdm_vblank_sw_timer_update(tdm_private_vblank *private_vblank)
610 {
611         tdm_vblank_wait_info *first_wait_info = NULL;
612         unsigned long curr, target;
613         int ms_delay;
614         tdm_error ret;
615
616         if (LIST_IS_EMPTY(&private_vblank->SW_wait_list)) {
617                 VER("no wait_info");
618                 return TDM_ERROR_OPERATION_FAILED;
619         }
620
621         first_wait_info = container_of(private_vblank->SW_wait_list.next, first_wait_info, link);
622         curr = tdm_helper_get_time_in_micros();
623         target = first_wait_info->target_sec * 1000000 + first_wait_info->target_usec;
624
625         if (target < curr)
626                 ms_delay = 1;
627         else
628                 ms_delay = ceil((double)(target - curr) / 1000);
629
630         if (ms_delay < 1)
631                 ms_delay = 1;
632
633         if (tdm_debug_module & TDM_DEBUG_VBLANK)
634                 VIN("wait(%p) curr(%4lu) target(%4lu) ms_delay(%d)",
635                         first_wait_info, curr, target, ms_delay);
636
637         tdm_display_lock(private_vblank->dpy);
638
639         if (!private_vblank->SW_timer) {
640                 private_vblank->SW_timer =
641                         tdm_event_loop_add_timer_handler(private_vblank->dpy,
642                                                                                          _tdm_vblank_cb_vblank_SW,
643                                                                                          private_vblank,
644                                                                                          &ret);
645                 if (!private_vblank->SW_timer) {
646                         tdm_display_unlock(private_vblank->dpy);
647                         VER("couldn't add timer");
648                         return ret;
649                 }
650                 VIN("Use SW vblank");
651         }
652
653         ret = tdm_event_loop_source_timer_update(private_vblank->SW_timer, ms_delay);
654         if (ret != TDM_ERROR_NONE) {
655                 tdm_display_unlock(private_vblank->dpy);
656                 VER("couldn't update timer");
657                 return ret;
658         }
659
660         tdm_display_unlock(private_vblank->dpy);
661
662         return TDM_ERROR_NONE;
663 }
664
665 #if 0
666 static void
667 _tdm_vblank_cb_vblank_align(tdm_output *output, unsigned int sequence,
668                                                         unsigned int tv_sec, unsigned int tv_usec,
669                                                         void *user_data)
670 {
671         tdm_vblank_wait_info *align_info = user_data;
672         tdm_private_vblank *private_vblank;
673         unsigned int diff_sec, diff_usec;
674
675         if (!_tdm_vblank_check_valid(align_info))
676                 return;
677
678         private_vblank = align_info->private_vblank;
679         TDM_RETURN_IF_FAIL(private_vblank != NULL);
680
681         private_vblank->SW_align_wait = NULL;
682         private_vblank->SW_align_sec = tv_sec;
683         private_vblank->SW_align_usec = tv_usec;
684
685         LIST_DEL(&align_info->valid_link);
686
687         if (tv_usec > align_info->req_usec) {
688                 diff_usec = tv_usec - align_info->req_usec;
689                 diff_sec = tv_sec - align_info->req_sec;
690         } else {
691                 diff_usec = 1000000 + tv_usec - align_info->req_usec;
692                 diff_sec = tv_sec - align_info->req_sec - 1;
693         }
694
695         private_vblank->SW_align_offset = (double)(1000000 - diff_sec * 1000000 - diff_usec) / private_vblank->vrefresh;
696
697         free(align_info);
698
699         /* align vblank continously only if non HW and DPMS on */
700         if (!private_vblank->HW_enable && private_vblank->dpms == TDM_OUTPUT_DPMS_ON)
701                 _tdm_vblank_sw_timer_align(private_vblank);
702 }
703
704 static void
705 _tdm_vblank_sw_timer_align(tdm_private_vblank *private_vblank)
706 {
707         tdm_vblank_wait_info *align_info;
708         unsigned long curr;
709         tdm_error ret;
710
711         if (private_vblank->SW_align_wait)
712                 return;
713
714         TDM_RETURN_IF_FAIL(private_vblank->dpms == TDM_OUTPUT_DPMS_ON);
715
716         align_info = calloc(1, sizeof *align_info);
717         if (!align_info) {
718                 VER("alloc failed");
719                 return;
720         }
721
722         LIST_ADDTAIL(&align_info->valid_link, &valid_wait_list);
723         align_info->stamp = ++stamp;
724         align_info->private_vblank = private_vblank;
725
726         curr = tdm_helper_get_time_in_micros();
727         align_info->req_sec = curr / 1000000;
728         align_info->req_usec = curr % 1000000;
729
730         ret = tdm_output_wait_vblank(private_vblank->output, private_vblank->vrefresh, 0,
731                                                                  _tdm_vblank_cb_vblank_align, align_info);
732         if (ret != TDM_ERROR_NONE) {
733                 LIST_DEL(&align_info->valid_link);
734                 free(align_info);
735                 return;
736         }
737
738         private_vblank->SW_align_wait = align_info;
739 }
740 #endif
741
742 static tdm_error
743 _tdm_vblank_wait_SW(tdm_vblank_wait_info *wait_info)
744 {
745         tdm_private_vblank *private_vblank = wait_info->private_vblank;
746         tdm_error ret;
747
748         if (private_vblank->last_tv_sec == 0 && private_vblank->dpms == TDM_OUTPUT_DPMS_ON) {
749                 unsigned int do_wait = LIST_IS_EMPTY(&private_vblank->SW_pending_wait_list);
750                 _tdm_vblank_insert_wait(wait_info, &private_vblank->SW_pending_wait_list);
751                 if (do_wait) {
752                         ret = tdm_output_wait_vblank(private_vblank->output, 1, 0,
753                                                                                  _tdm_vblank_cb_vblank_SW_first, wait_info);
754                         if (ret == TDM_ERROR_DPMS_OFF) {
755                                 TDM_WRN("use SW");
756                                 goto use_sw;
757                         }
758                         if (ret != TDM_ERROR_NONE) {
759                                 LIST_DEL(&wait_info->link);
760                                 return ret;
761                         }
762                         if (tdm_debug_module & TDM_DEBUG_VBLANK)
763                                 VIN("wait(%p) waiting", wait_info);
764                 }
765                 return TDM_ERROR_NONE;
766         }
767
768 use_sw:
769         TDM_RETURN_VAL_IF_FAIL(wait_info->target_sec > 0, TDM_ERROR_OPERATION_FAILED);
770
771         _tdm_vblank_insert_wait(wait_info, &private_vblank->SW_wait_list);
772
773         ret = _tdm_vblank_sw_timer_update(private_vblank);
774         if (ret != TDM_ERROR_NONE) {
775                 LIST_DEL(&wait_info->link);
776                 VER("couldn't update sw timer");
777                 return ret;
778         }
779
780         return TDM_ERROR_NONE;
781 }
782
783 static void
784 _tdm_vblank_calculate_target(tdm_vblank_wait_info *wait_info)
785 {
786         tdm_private_vblank *private_vblank = wait_info->private_vblank;
787         unsigned long last, prev, req, curr, target;
788         unsigned int skip = 0;
789
790         curr = tdm_helper_get_time_in_micros();
791
792         if (!private_vblank->HW_enable) {
793                 if (private_vblank->last_tv_sec == 0) {
794                         /* If last == 0 and DPMS == on, we will use HW vblank to sync with HW vblank. */
795                         if (private_vblank->dpms == TDM_OUTPUT_DPMS_ON) {
796                                 return;
797                         } else {
798                                 private_vblank->last_tv_sec = curr / 1000000;
799                                 private_vblank->last_tv_usec = curr % 1000000;
800                         }
801                 }
802         }
803
804         /* last can be 0 when HW enable. But it doesn't matter if HW enable. */
805         if (!private_vblank->HW_enable)
806                 TDM_RETURN_IF_FAIL(private_vblank->last_tv_sec != 0);
807
808         last = (unsigned long)private_vblank->last_tv_sec * 1000000 + private_vblank->last_tv_usec;
809         req = (unsigned long)wait_info->req_sec * 1000000 + wait_info->req_usec;
810         skip = (unsigned int)((req - last) / private_vblank->vblank_gap);
811         prev = last + skip * private_vblank->vblank_gap;
812
813         if (private_vblank->last_seq == 0)
814                 skip = 0;
815
816         skip += wait_info->interval;
817
818         if (private_vblank->HW_enable) {
819                 unsigned int hw_skip = (unsigned int)((curr - prev) / private_vblank->HW_vblank_gap);
820
821                 wait_info->target_hw_interval = wait_info->interval * private_vblank->HW_quotient;
822                 wait_info->target_hw_interval -= hw_skip;
823
824                 if (wait_info->target_hw_interval < 1)
825                         wait_info->target_hw_interval = 1;
826
827                 target = prev + wait_info->target_hw_interval * private_vblank->HW_vblank_gap;
828         } else {
829                 target = prev + (unsigned long)(private_vblank->vblank_gap * wait_info->interval);
830
831                 while (target < curr) {
832                         target += (unsigned long)private_vblank->vblank_gap;
833                         skip++;
834                 }
835         }
836
837         if (tdm_debug_module & TDM_DEBUG_VBLANK)
838                 VIN("target_seq(%d) last_seq(%d) skip(%d)",
839                         wait_info->target_seq, private_vblank->last_seq, skip);
840
841 #if 0
842         target -= (private_vblank->SW_align_offset * skip * private_vblank->HW_quotient);
843 #endif
844
845         wait_info->target_seq = private_vblank->last_seq + skip;
846         wait_info->target_sec = target / 1000000;
847         wait_info->target_usec = target % 1000000;
848
849         if (tdm_debug_module & TDM_DEBUG_VBLANK)
850                 VIN("wait(%p) last(%4lu) req(%4lu) prev(%4lu) curr(%4lu) skip(%d) hw_interval(%d) target(%4lu,%4lu)",
851                         wait_info, last, req - last, prev - last, curr - last,
852                         skip, wait_info->target_hw_interval, target, target - last);
853 }
854
855 tdm_error
856 tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec,
857                                 unsigned int interval, tdm_vblank_handler func, void *user_data)
858 {
859         tdm_private_vblank *private_vblank = vblank;
860         tdm_vblank_wait_info *wait_info;
861         tdm_error ret;
862
863         TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
864         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
865
866         if (private_vblank->dpms != TDM_OUTPUT_DPMS_ON && !private_vblank->enable_fake) {
867                 VIN("can't wait a vblank because of DPMS off");
868                 return TDM_ERROR_DPMS_OFF;
869         }
870
871 #if 0
872         if (!private_vblank->SW_align_wait && private_vblank->dpms == TDM_OUTPUT_DPMS_ON)
873                 _tdm_vblank_sw_timer_align(private_vblank);
874 #endif
875
876         if (private_vblank->check_HW_or_SW) {
877                 private_vblank->check_HW_or_SW = 0;
878                 private_vblank->vblank_gap = (double)1000000 / private_vblank->fps;
879                 private_vblank->HW_quotient = private_vblank->vrefresh / private_vblank->fps;
880
881                 if (private_vblank->dpms == TDM_OUTPUT_DPMS_ON &&
882                         !(private_vblank->vrefresh % private_vblank->fps)) {
883                         private_vblank->HW_enable = 1;
884                         VIN("Use HW vblank");
885                 } else {
886                         private_vblank->HW_enable = 0;
887                         VIN("Use SW vblank");
888                 }
889         }
890
891         wait_info = calloc(1, sizeof *wait_info);
892         if (!wait_info) {
893                 VER("alloc failed");
894                 return TDM_ERROR_OUT_OF_MEMORY;
895         }
896
897         LIST_INITHEAD(&wait_info->link);
898         LIST_ADDTAIL(&wait_info->valid_link, &valid_wait_list);
899         wait_info->stamp = ++stamp;
900         wait_info->req_sec = req_sec;
901         wait_info->req_usec = req_usec;
902         wait_info->interval = interval;
903         wait_info->func = func;
904         wait_info->user_data = user_data;
905         wait_info->private_vblank = private_vblank;
906
907         _tdm_vblank_calculate_target(wait_info);
908
909         if (private_vblank->HW_enable) {
910                 ret = _tdm_vblank_wait_HW(wait_info);
911                 if (ret == TDM_ERROR_DPMS_OFF) {
912                         TDM_WRN("try to use SW");
913                         ret = _tdm_vblank_wait_SW(wait_info);
914                 }
915         } else
916                 ret = _tdm_vblank_wait_SW(wait_info);
917
918         if (ret != TDM_ERROR_NONE) {
919                 LIST_DEL(&wait_info->link);
920                 LIST_DEL(&wait_info->valid_link);
921                 free(wait_info);
922                 return ret;
923         }
924
925         return TDM_ERROR_NONE;
926 }