From 78194c62535a9485933d59c7da969ddc17a2583c Mon Sep 17 00:00:00 2001 From: Boram Park Date: Tue, 30 Aug 2016 16:02:54 +0900 Subject: [PATCH] refactoring tdm vblank Change-Id: Id688bf2108f54cff797c96f67a8f5ed31ab23ba2 --- src/tdm_private.h | 2 + src/tdm_server.c | 5 + src/tdm_vblank.c | 577 ++++++++++++++++++---------------------------- 3 files changed, 229 insertions(+), 355 deletions(-) diff --git a/src/tdm_private.h b/src/tdm_private.h index 8678e256..44af51f2 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -600,6 +600,8 @@ tdm_error tdm_vblank_set_offset(tdm_vblank *vblank, int offset); tdm_error tdm_vblank_set_enable_fake(tdm_vblank *vblank, unsigned int enable_fake); +unsigned int +tdm_vblank_get_enable_fake(tdm_vblank *vblank); tdm_error tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, unsigned int interval, tdm_vblank_handler func, void *user_data); diff --git a/src/tdm_server.c b/src/tdm_server.c index 90f46ad8..9e887877 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -262,11 +262,16 @@ _tdm_server_vblank_cb_wait_vblank(struct wl_client *client, struct wl_resource * TDM_INFO("req_id(%d) wait", req_id); ret = tdm_vblank_wait(vblank_info->vblank, req_sec, req_usec, interval, _tdm_server_cb_vblank, wait_info); + + if (!tdm_vblank_get_enable_fake(vblank_info->vblank) && ret == TDM_ERROR_DPMS_OFF) + goto wait_failed; + TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, wait_failed); return; wait_failed: wl_tdm_vblank_send_done(vblank_info->resource, req_id, 0, 0, 0, ret); + destroy_wait(wait_info); } static const struct wl_tdm_vblank_interface tdm_vblank_implementation = { diff --git a/src/tdm_vblank.c b/src/tdm_vblank.c index 53554fa0..e63c14cf 100644 --- a/src/tdm_vblank.c +++ b/src/tdm_vblank.c @@ -57,6 +57,13 @@ #define VIN(fmt, arg...) TDM_INFO("[%p] "fmt, private_vblank, ##arg) #define VDB(fmt, arg...) TDM_DBG("[%p] "fmt, private_vblank, ##arg) +typedef enum { + VBLANK_TYPE_SW, + VBLANK_TYPE_SW_FAKE, + VBLANK_TYPE_HW, + VBLANK_TYPE_HW_SW, +} tdm_vblank_wait_type; + typedef struct _tdm_vblank_wait_info tdm_vblank_wait_info; typedef struct _tdm_private_vblank { @@ -73,26 +80,20 @@ typedef struct _tdm_private_vblank { unsigned int enable_fake; double vblank_gap; + unsigned int quotient; + unsigned int last_seq; unsigned int last_tv_sec; unsigned int last_tv_usec; /* for HW */ double HW_vblank_gap; - unsigned int HW_enable; - unsigned int HW_quotient; struct list_head HW_wait_list; + unsigned int HW_seq_margin; /* for SW */ tdm_event_loop_source *SW_timer; - struct list_head SW_pending_wait_list; struct list_head SW_wait_list; -#if 0 - tdm_vblank_wait_info *SW_align_wait; - double SW_align_offset; - unsigned int SW_align_sec; - unsigned int SW_align_usec; -#endif } tdm_private_vblank; struct _tdm_vblank_wait_info { @@ -109,14 +110,11 @@ struct _tdm_vblank_wait_info { void *user_data; tdm_private_vblank *private_vblank; - /* target_sec can be 0 when last_tv_sec is 0 because we can't calculate - * target_sec without last_tv_sec. So we have to call tdm_output_wait_vblank - * to fill last_tv_sec at the first time. - */ + tdm_vblank_wait_type type; + unsigned int target_sec; unsigned int target_usec; unsigned int target_seq; - int target_hw_interval; }; static struct list_head vblank_list; @@ -124,10 +122,8 @@ static struct list_head valid_wait_list; static unsigned int vblank_list_inited; static unsigned int stamp = 0; +static tdm_error _tdm_vblank_cb_vblank_SW(void *user_data); static tdm_error _tdm_vblank_wait_SW(tdm_vblank_wait_info *wait_info); -#if 0 -static void _tdm_vblank_sw_timer_align(tdm_private_vblank *private_vblank); -#endif #if 0 static void @@ -186,18 +182,19 @@ _tdm_vblank_insert_wait(tdm_vblank_wait_info *wait_info, struct list_head *list) } LIST_FOR_EACH_ENTRY(w, list, link) { - /* If last_tv_sec == 0, we can't calculate target_sec. */ - if (wait_info->target_sec == 0) { - if (w->interval <= wait_info->interval) { + if (wait_info->type == VBLANK_TYPE_SW) { + if (wait_info->target_sec == 0) + TDM_NEVER_GET_HERE(); + if (w->target_sec < wait_info->target_sec) { found = w; continue; } - } else { - if (w->target_sec < wait_info->target_sec) { + if (w->target_sec == wait_info->target_sec && w->target_usec <= wait_info->target_usec) { found = w; continue; } - if (w->target_sec == wait_info->target_sec && w->target_usec <= wait_info->target_usec) { + } else { + if (w->interval <= wait_info->interval) { found = w; continue; } @@ -215,10 +212,9 @@ _tdm_vblank_change_to_SW(tdm_private_vblank *private_vblank) { tdm_vblank_wait_info *w = NULL, *ww = NULL; - VIN("Change to SW"); - LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->HW_wait_list, link) { LIST_DEL(&w->link); + w->type = VBLANK_TYPE_SW_FAKE; _tdm_vblank_wait_SW(w); } } @@ -255,14 +251,6 @@ _tdm_vblank_cb_output_change(tdm_output *output, tdm_output_change_type type, private_vblank->dpms = value.u32; private_vblank->check_HW_or_SW = 1; if (private_vblank->dpms != TDM_OUTPUT_DPMS_ON) { -#if 0 - if (private_vblank->SW_align_wait) { - LIST_DEL(&private_vblank->SW_align_wait->valid_link); - free(private_vblank->SW_align_wait); - private_vblank->SW_align_wait = NULL; - } -#endif - if (private_vblank->enable_fake) _tdm_vblank_change_to_SW(private_vblank); else @@ -329,8 +317,6 @@ tdm_vblank_create(tdm_display *dpy, tdm_output *output, tdm_error *error) private_vblank->fps = mode->vrefresh; LIST_INITHEAD(&private_vblank->HW_wait_list); - - LIST_INITHEAD(&private_vblank->SW_pending_wait_list); LIST_INITHEAD(&private_vblank->SW_wait_list); LIST_ADD(&private_vblank->link, &vblank_list); @@ -353,13 +339,6 @@ tdm_vblank_destroy(tdm_vblank *vblank) LIST_DEL(&private_vblank->link); -#if 0 - if (private_vblank->SW_align_wait) { - LIST_DEL(&private_vblank->SW_align_wait->valid_link); - free(private_vblank->SW_align_wait); - } -#endif - if (private_vblank->SW_timer) { tdm_display_lock(private_vblank->dpy); tdm_event_loop_source_remove(private_vblank->SW_timer); @@ -371,12 +350,6 @@ tdm_vblank_destroy(tdm_vblank *vblank) _tdm_vblank_free_HW_wait(private_vblank, 0, 0); - LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_pending_wait_list, link) { - LIST_DEL(&w->link); - LIST_DEL(&w->valid_link); - free(w); - } - LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_wait_list, link) { LIST_DEL(&w->link); LIST_DEL(&w->valid_link); @@ -446,163 +419,14 @@ tdm_vblank_set_enable_fake(tdm_vblank *vblank, unsigned int enable_fake) return TDM_ERROR_NONE; } -static void -_tdm_vblank_cb_vblank_HW(tdm_output *output, unsigned int sequence, - unsigned int tv_sec, unsigned int tv_usec, - void *user_data) -{ - tdm_vblank_wait_info *wait_info = user_data; - tdm_private_vblank *private_vblank; - - if (!_tdm_vblank_check_valid(wait_info)) { - TDM_DBG("can't find wait(%p) from valid_wait_list", wait_info); - return; - } - - private_vblank = wait_info->private_vblank; - TDM_RETURN_IF_FAIL(private_vblank != NULL); - - if (!_tdm_vblank_find_wait(wait_info, &private_vblank->HW_wait_list)) { - VDB("can't find wait(%p)", wait_info); - return; - } - - LIST_DEL(&wait_info->link); - LIST_DEL(&wait_info->valid_link); - - private_vblank->last_seq = wait_info->target_seq; - private_vblank->last_tv_sec = tv_sec; - private_vblank->last_tv_usec = tv_usec; - - if (wait_info->func) - wait_info->func(private_vblank, TDM_ERROR_NONE, private_vblank->last_seq, - tv_sec, tv_usec, wait_info->user_data); - - if (tdm_debug_module & TDM_DEBUG_VBLANK) - VIN("wait(%p) done", wait_info); - - free(wait_info); -} - -static tdm_error -_tdm_vblank_wait_HW(tdm_vblank_wait_info *wait_info) -{ - tdm_private_vblank *private_vblank = wait_info->private_vblank; - tdm_error ret; - - TDM_RETURN_VAL_IF_FAIL(wait_info->target_hw_interval > 0, TDM_ERROR_OPERATION_FAILED); - - _tdm_vblank_insert_wait(wait_info, &private_vblank->HW_wait_list); - - ret = tdm_output_wait_vblank(private_vblank->output, wait_info->target_hw_interval, 0, - _tdm_vblank_cb_vblank_HW, wait_info); - - if (ret != TDM_ERROR_NONE) { - VWR("wait(%p) failed", wait_info); - LIST_DEL(&wait_info->link); - return ret; - } - - if (tdm_debug_module & TDM_DEBUG_VBLANK) - VIN("wait(%p) waiting", wait_info); - - return TDM_ERROR_NONE; -} - -static tdm_error -_tdm_vblank_cb_vblank_SW(void *user_data) +unsigned int +tdm_vblank_get_enable_fake(tdm_vblank *vblank) { - tdm_private_vblank *private_vblank = user_data; - tdm_vblank_wait_info *first_wait_info = NULL, *w = NULL, *ww = NULL; - - TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_OPERATION_FAILED); - - if (LIST_IS_EMPTY(&private_vblank->SW_wait_list)) { - VER("no wait_info"); - return TDM_ERROR_OPERATION_FAILED; - } - - first_wait_info = container_of(private_vblank->SW_wait_list.next, first_wait_info, link); - TDM_RETURN_VAL_IF_FAIL(first_wait_info != NULL, TDM_ERROR_OPERATION_FAILED); - - if (tdm_debug_module & TDM_DEBUG_VBLANK) - VIN("wait(%p) done", first_wait_info); - - private_vblank->last_seq = first_wait_info->target_seq; - private_vblank->last_tv_sec = first_wait_info->target_sec; - private_vblank->last_tv_usec = first_wait_info->target_usec; - - LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_wait_list, link) { - if (w->target_sec != first_wait_info->target_sec || - w->target_usec != first_wait_info->target_usec) - break; - - LIST_DEL(&w->link); - LIST_DEL(&w->valid_link); - - if (w->func) - w->func(private_vblank, TDM_ERROR_NONE, w->target_seq, - w->target_sec, w->target_usec, - w->user_data); - - free(w); - } - - return TDM_ERROR_NONE; -} - -static void -_tdm_vblank_cb_vblank_SW_first(tdm_output *output, unsigned int sequence, - unsigned int tv_sec, unsigned int tv_usec, - void *user_data) -{ - tdm_vblank_wait_info *wait_info = user_data; - tdm_private_vblank *private_vblank; - tdm_vblank_wait_info *w = NULL, *ww = NULL; - unsigned int min_interval = 0; - unsigned long last; - - if (!_tdm_vblank_check_valid(wait_info)) - return; - - private_vblank = wait_info->private_vblank; - TDM_RETURN_IF_FAIL(private_vblank != NULL); - - if (LIST_IS_EMPTY(&private_vblank->SW_pending_wait_list)) { - VER("no wait_info"); - return; - } - - w = container_of((&private_vblank->SW_pending_wait_list)->next, w, link); - TDM_RETURN_IF_FAIL(w != NULL); - - if (tdm_debug_module & TDM_DEBUG_VBLANK) - VIN("wait(%p) done", w); - - min_interval = w->interval; - - last = (unsigned long)tv_sec * 1000000 + tv_usec; - last -= private_vblank->offset * 1000; - - private_vblank->last_seq = min_interval; - private_vblank->last_tv_sec = last / 1000000; - private_vblank->last_tv_usec = last % 1000000; + tdm_private_vblank *private_vblank = vblank; - LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_pending_wait_list, link) { - if (w->interval == min_interval) { - LIST_DEL(&w->link); - LIST_DEL(&w->valid_link); + TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, 0); - if (w->func) - w->func(private_vblank, TDM_ERROR_NONE, private_vblank->last_seq, - tv_sec, tv_usec, w->user_data); - free(w); - } else { - LIST_DEL(&w->link); - w->interval -= min_interval; - _tdm_vblank_wait_SW(w); - } - } + return private_vblank->enable_fake; } static tdm_error @@ -622,6 +446,7 @@ _tdm_vblank_sw_timer_update(tdm_private_vblank *private_vblank) curr = tdm_helper_get_time_in_micros(); target = first_wait_info->target_sec * 1000000 + first_wait_info->target_usec; + /* ms_delay should be more that 1 */ if (target < curr) ms_delay = 1; else @@ -662,194 +487,238 @@ _tdm_vblank_sw_timer_update(tdm_private_vblank *private_vblank) return TDM_ERROR_NONE; } -#if 0 static void -_tdm_vblank_cb_vblank_align(tdm_output *output, unsigned int sequence, - unsigned int tv_sec, unsigned int tv_usec, - void *user_data) +_tdm_vblank_cb_vblank_HW(tdm_output *output, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, + void *user_data) { - tdm_vblank_wait_info *align_info = user_data; + tdm_vblank_wait_info *wait_info = user_data; tdm_private_vblank *private_vblank; - unsigned int diff_sec, diff_usec; - if (!_tdm_vblank_check_valid(align_info)) + if (!_tdm_vblank_check_valid(wait_info)) { + TDM_DBG("can't find wait(%p) from valid_wait_list", wait_info); return; + } - private_vblank = align_info->private_vblank; + private_vblank = wait_info->private_vblank; TDM_RETURN_IF_FAIL(private_vblank != NULL); - private_vblank->SW_align_wait = NULL; - private_vblank->SW_align_sec = tv_sec; - private_vblank->SW_align_usec = tv_usec; + if (!_tdm_vblank_find_wait(wait_info, &private_vblank->HW_wait_list)) { + VDB("can't find wait(%p)", wait_info); + return; + } - LIST_DEL(&align_info->valid_link); + /* sequence is the relative value of fps. If fps = 10, sequence should be + * increased by 10 during 1 second. + */ + sequence /= private_vblank->quotient; - if (tv_usec > align_info->req_usec) { - diff_usec = tv_usec - align_info->req_usec; - diff_sec = tv_sec - align_info->req_sec; - } else { - diff_usec = 1000000 + tv_usec - align_info->req_usec; - diff_sec = tv_sec - align_info->req_sec - 1; + /* If VBLANK_TYPE_SW_FAKE, HW sequeuce can become less than SW sequeuce. + * so we will correct it with HW_seq_margin. + */ + if (private_vblank->last_seq > sequence) { + unsigned long last, tv; + last = (unsigned long)private_vblank->last_tv_sec * 1000000 + private_vblank->last_tv_usec; + tv = (unsigned long)tv_sec * 1000000 + tv_usec; + private_vblank->HW_seq_margin = ((tv - last) / (unsigned long)private_vblank->vblank_gap) + 1; + private_vblank->HW_seq_margin += private_vblank->last_seq - sequence; } - private_vblank->SW_align_offset = (double)(1000000 - diff_sec * 1000000 - diff_usec) / private_vblank->vrefresh; + sequence += private_vblank->HW_seq_margin; - free(align_info); + if (wait_info->type == VBLANK_TYPE_HW_SW) { + unsigned long target; + tdm_error ret; - /* align vblank continously only if non HW and DPMS on */ - if (!private_vblank->HW_enable && private_vblank->dpms == TDM_OUTPUT_DPMS_ON) - _tdm_vblank_sw_timer_align(private_vblank); -} + LIST_DEL(&wait_info->link); -static void -_tdm_vblank_sw_timer_align(tdm_private_vblank *private_vblank) -{ - tdm_vblank_wait_info *align_info; - unsigned long curr; - tdm_error ret; + target = (unsigned long)tv_sec * 1000000 + tv_usec; + target += (private_vblank->offset * 1000); - if (private_vblank->SW_align_wait) - return; + wait_info->target_seq = sequence; + wait_info->target_sec = target / 1000000; + wait_info->target_usec = target % 1000000; - TDM_RETURN_IF_FAIL(private_vblank->dpms == TDM_OUTPUT_DPMS_ON); + _tdm_vblank_insert_wait(wait_info, &private_vblank->SW_wait_list); - align_info = calloc(1, sizeof *align_info); - if (!align_info) { - VER("alloc failed"); - return; + ret = _tdm_vblank_sw_timer_update(private_vblank); + + /* wait_info will be freed in _tdm_vblank_cb_vblank_SW() */ + if (ret == TDM_ERROR_NONE) { + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("wait(%p) SW timer", wait_info); + return; + } + + VWR("couldn't update sw timer"); } - LIST_ADDTAIL(&align_info->valid_link, &valid_wait_list); - align_info->stamp = ++stamp; - align_info->private_vblank = private_vblank; + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("wait(%p) sequence(%u) done", wait_info, sequence); - curr = tdm_helper_get_time_in_micros(); - align_info->req_sec = curr / 1000000; - align_info->req_usec = curr % 1000000; + private_vblank->last_seq = sequence; + private_vblank->last_tv_sec = tv_sec; + private_vblank->last_tv_usec = tv_usec; - ret = tdm_output_wait_vblank(private_vblank->output, private_vblank->vrefresh, 0, - _tdm_vblank_cb_vblank_align, align_info); - if (ret != TDM_ERROR_NONE) { - LIST_DEL(&align_info->valid_link); - free(align_info); - return; - } + if (wait_info->func) + wait_info->func(private_vblank, TDM_ERROR_NONE, sequence, + tv_sec, tv_usec, wait_info->user_data); - private_vblank->SW_align_wait = align_info; + LIST_DEL(&wait_info->link); + LIST_DEL(&wait_info->valid_link); + free(wait_info); } -#endif static tdm_error -_tdm_vblank_wait_SW(tdm_vblank_wait_info *wait_info) +_tdm_vblank_wait_HW(tdm_vblank_wait_info *wait_info) { tdm_private_vblank *private_vblank = wait_info->private_vblank; + int hw_interval; tdm_error ret; - if (private_vblank->last_tv_sec == 0 && private_vblank->dpms == TDM_OUTPUT_DPMS_ON) { - unsigned int do_wait = LIST_IS_EMPTY(&private_vblank->SW_pending_wait_list); - _tdm_vblank_insert_wait(wait_info, &private_vblank->SW_pending_wait_list); - if (do_wait) { - ret = tdm_output_wait_vblank(private_vblank->output, 1, 0, - _tdm_vblank_cb_vblank_SW_first, wait_info); - if (ret == TDM_ERROR_DPMS_OFF) { - TDM_WRN("use SW"); - goto use_sw; - } - if (ret != TDM_ERROR_NONE) { - LIST_DEL(&wait_info->link); - return ret; - } - if (tdm_debug_module & TDM_DEBUG_VBLANK) - VIN("wait(%p) waiting", wait_info); - } - return TDM_ERROR_NONE; + _tdm_vblank_insert_wait(wait_info, &private_vblank->HW_wait_list); + + hw_interval = wait_info->interval * private_vblank->quotient; + + if (private_vblank->last_tv_sec != 0) { + unsigned long last, prev, req, curr; + unsigned int skip = 0; + unsigned int hw_skip; + + last = (unsigned long)private_vblank->last_tv_sec * 1000000 + private_vblank->last_tv_usec; + req = (unsigned long)wait_info->req_sec * 1000000 + wait_info->req_usec; + + skip = (unsigned int)((req - last) / private_vblank->vblank_gap); + prev = last + skip * private_vblank->vblank_gap; + + curr = tdm_helper_get_time_in_micros(); + hw_skip = (unsigned int)((curr - prev) / private_vblank->HW_vblank_gap); + + hw_interval -= hw_skip; + + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("wait(%p) last(%4lu) req(%4lu) prev(%4lu) curr(%4lu) skip(%d) hw_interval(%d)", + wait_info, last, req - last, prev - last, curr - last, + skip, hw_interval); } -use_sw: - TDM_RETURN_VAL_IF_FAIL(wait_info->target_sec > 0, TDM_ERROR_OPERATION_FAILED); + if (hw_interval < 1) + hw_interval = 1; - _tdm_vblank_insert_wait(wait_info, &private_vblank->SW_wait_list); + ret = tdm_output_wait_vblank(private_vblank->output, hw_interval, 0, + _tdm_vblank_cb_vblank_HW, wait_info); - ret = _tdm_vblank_sw_timer_update(private_vblank); if (ret != TDM_ERROR_NONE) { LIST_DEL(&wait_info->link); - VER("couldn't update sw timer"); return ret; } + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("wait(%p) waiting", wait_info); + return TDM_ERROR_NONE; } -static void -_tdm_vblank_calculate_target(tdm_vblank_wait_info *wait_info) +static tdm_error +_tdm_vblank_cb_vblank_SW(void *user_data) { - tdm_private_vblank *private_vblank = wait_info->private_vblank; - unsigned long last, prev, req, curr, target; - unsigned int skip = 0; + tdm_private_vblank *private_vblank = user_data; + tdm_vblank_wait_info *first_wait_info = NULL, *w = NULL, *ww = NULL; - curr = tdm_helper_get_time_in_micros(); + TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_OPERATION_FAILED); - if (!private_vblank->HW_enable) { - if (private_vblank->last_tv_sec == 0) { - /* If last == 0 and DPMS == on, we will use HW vblank to sync with HW vblank. */ - if (private_vblank->dpms == TDM_OUTPUT_DPMS_ON) { - return; - } else { - private_vblank->last_tv_sec = curr / 1000000; - private_vblank->last_tv_usec = curr % 1000000; - } - } + if (LIST_IS_EMPTY(&private_vblank->SW_wait_list)) { + VER("no wait_info"); + return TDM_ERROR_OPERATION_FAILED; } - /* last can be 0 when HW enable. But it doesn't matter if HW enable. */ - if (!private_vblank->HW_enable) - TDM_RETURN_IF_FAIL(private_vblank->last_tv_sec != 0); + first_wait_info = container_of(private_vblank->SW_wait_list.next, first_wait_info, link); + TDM_RETURN_VAL_IF_FAIL(first_wait_info != NULL, TDM_ERROR_OPERATION_FAILED); + + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("wait(%p) sequence(%u) done", first_wait_info, first_wait_info->target_seq); + + private_vblank->last_seq = first_wait_info->target_seq; + private_vblank->last_tv_sec = first_wait_info->target_sec; + private_vblank->last_tv_usec = first_wait_info->target_usec; + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_wait_list, link) { + if (w->target_sec != first_wait_info->target_sec || + w->target_usec != first_wait_info->target_usec) + break; + + LIST_DEL(&w->link); + LIST_DEL(&w->valid_link); + + if (w->func) + w->func(private_vblank, TDM_ERROR_NONE, w->target_seq, + w->target_sec, w->target_usec, + w->user_data); - last = (unsigned long)private_vblank->last_tv_sec * 1000000 + private_vblank->last_tv_usec; - req = (unsigned long)wait_info->req_sec * 1000000 + wait_info->req_usec; - skip = (unsigned int)((req - last) / private_vblank->vblank_gap); - prev = last + skip * private_vblank->vblank_gap; + free(w); + } - if (private_vblank->last_seq == 0) - skip = 0; + return TDM_ERROR_NONE; +} - skip += wait_info->interval; +static tdm_error +_tdm_vblank_wait_SW(tdm_vblank_wait_info *wait_info) +{ + tdm_private_vblank *private_vblank = wait_info->private_vblank; + tdm_error ret; - if (private_vblank->HW_enable) { - unsigned int hw_skip = (unsigned int)((curr - prev) / private_vblank->HW_vblank_gap); + if (private_vblank->last_tv_sec == 0) { + unsigned long curr = tdm_helper_get_time_in_micros(); - wait_info->target_hw_interval = wait_info->interval * private_vblank->HW_quotient; - wait_info->target_hw_interval -= hw_skip; + /* SW vblank starts from now. SW vblank doesn't need to be aligned with HW vblank. */ + private_vblank->last_seq = 0; + private_vblank->last_tv_sec = curr / 1000000; + private_vblank->last_tv_usec = curr % 1000000; - if (wait_info->target_hw_interval < 1) - wait_info->target_hw_interval = 1; + /* +1 ms to call the handler ASAP at the first. no matter for SW timer. */ + curr += 1000; - target = prev + wait_info->target_hw_interval * private_vblank->HW_vblank_gap; + wait_info->target_seq = 1; + wait_info->target_sec = curr / 1000000; + wait_info->target_usec = curr % 1000000; } else { + unsigned long last, prev, req, curr, target; + unsigned int skip; + + last = (unsigned long)private_vblank->last_tv_sec * 1000000 + private_vblank->last_tv_usec; + req = (unsigned long)wait_info->req_sec * 1000000 + wait_info->req_usec; + + skip = (unsigned int)((req - last) / private_vblank->vblank_gap); + prev = last + skip * private_vblank->vblank_gap; + + curr = tdm_helper_get_time_in_micros(); target = prev + (unsigned long)(private_vblank->vblank_gap * wait_info->interval); - while (target < curr) { + while (target < curr) target += (unsigned long)private_vblank->vblank_gap; - skip++; - } - } - if (tdm_debug_module & TDM_DEBUG_VBLANK) - VIN("target_seq(%d) last_seq(%d) skip(%d)", - wait_info->target_seq, private_vblank->last_seq, skip); + wait_info->target_seq = private_vblank->last_seq; + wait_info->target_seq += (target - last) / (unsigned long)private_vblank->vblank_gap; -#if 0 - target -= (private_vblank->SW_align_offset * skip * private_vblank->HW_quotient); -#endif + wait_info->target_sec = target / 1000000; + wait_info->target_usec = target % 1000000; - wait_info->target_seq = private_vblank->last_seq + skip; - wait_info->target_sec = target / 1000000; - wait_info->target_usec = target % 1000000; + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("wait(%p) last(%4lu) req(%4lu) prev(%4lu) curr(%4lu) target(%4lu,%4lu)", + wait_info, last, req - last, prev - last, curr - last, + target, target - last); + } - if (tdm_debug_module & TDM_DEBUG_VBLANK) - VIN("wait(%p) last(%4lu) req(%4lu) prev(%4lu) curr(%4lu) skip(%d) hw_interval(%d) target(%4lu,%4lu)", - wait_info, last, req - last, prev - last, curr - last, - skip, wait_info->target_hw_interval, target, target - last); + _tdm_vblank_insert_wait(wait_info, &private_vblank->SW_wait_list); + + ret = _tdm_vblank_sw_timer_update(private_vblank); + if (ret != TDM_ERROR_NONE) { + LIST_DEL(&wait_info->link); + VER("couldn't update sw timer"); + return ret; + } + + return TDM_ERROR_NONE; } tdm_error @@ -868,26 +737,6 @@ tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, return TDM_ERROR_DPMS_OFF; } -#if 0 - if (!private_vblank->SW_align_wait && private_vblank->dpms == TDM_OUTPUT_DPMS_ON) - _tdm_vblank_sw_timer_align(private_vblank); -#endif - - if (private_vblank->check_HW_or_SW) { - private_vblank->check_HW_or_SW = 0; - private_vblank->vblank_gap = (double)1000000 / private_vblank->fps; - private_vblank->HW_quotient = private_vblank->vrefresh / private_vblank->fps; - - if (private_vblank->dpms == TDM_OUTPUT_DPMS_ON && - !(private_vblank->vrefresh % private_vblank->fps)) { - private_vblank->HW_enable = 1; - VIN("Use HW vblank"); - } else { - private_vblank->HW_enable = 0; - VIN("Use SW vblank"); - } - } - wait_info = calloc(1, sizeof *wait_info); if (!wait_info) { VER("alloc failed"); @@ -904,16 +753,34 @@ tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, wait_info->user_data = user_data; wait_info->private_vblank = private_vblank; - _tdm_vblank_calculate_target(wait_info); + if (private_vblank->check_HW_or_SW) { + private_vblank->check_HW_or_SW = 0; + private_vblank->vblank_gap = (double)1000000 / private_vblank->fps; + private_vblank->quotient = private_vblank->vrefresh / private_vblank->fps; + } + + /* 1) if fps != factor of vrefresh, SW timer + * 2) if fps == factor of vrefresh && dpms == off, SW timer (Fake HW vblank) + * 2) if fps == factor of vrefresh && dpms == on && offset == 0, HW vblank + * 3) if fps == factor of vrefresh && dpms == on && offset != 0, HW vblank + SW timer + * In case of 1), we really don't need to align with HW vblank. + */ + if (private_vblank->vrefresh % private_vblank->fps) + wait_info->type = VBLANK_TYPE_SW; + else if (private_vblank->dpms == TDM_OUTPUT_DPMS_OFF) + wait_info->type = VBLANK_TYPE_SW_FAKE; + else if (private_vblank->offset == 0) + wait_info->type = VBLANK_TYPE_HW; + else + wait_info->type = VBLANK_TYPE_HW_SW; - if (private_vblank->HW_enable) { + if (wait_info->type == VBLANK_TYPE_SW || wait_info->type == VBLANK_TYPE_SW_FAKE) + ret = _tdm_vblank_wait_SW(wait_info); + else { ret = _tdm_vblank_wait_HW(wait_info); - if (ret == TDM_ERROR_DPMS_OFF) { - TDM_WRN("try to use SW"); + if (ret == TDM_ERROR_DPMS_OFF) ret = _tdm_vblank_wait_SW(wait_info); - } - } else - ret = _tdm_vblank_wait_SW(wait_info); + } if (ret != TDM_ERROR_NONE) { LIST_DEL(&wait_info->link); -- 2.34.1