From c1256560d16726078ec1b0541bd081fc048e74c0 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 15 Oct 2012 13:22:42 +0200 Subject: [PATCH] uterm: video: use generic timer if vblank events are not available fbdev does not provide any vertical-blanks so this introduces a generic framework for uterm-videos as replacement. We simply schedule a timer event that fires after a fixed timeout. This allows other subsystems to rely on the page-flip event to occur either at the next real page-flip or at a fixed timeout. As this is what we want in most places, we just implement it inside of uterm. So relying on page-flips no longer hogs the CPU because swap() generates the right away. Signed-off-by: David Herrmann --- src/uterm_video.c | 61 +++++++++++++++++++++++++++++++++++++++-- src/uterm_video.h | 10 ++++++- src/uterm_video_drm.c | 3 +- src/uterm_video_dumb.c | 3 +- src/uterm_video_fbdev.c | 30 ++++++++++++++------ 5 files changed, 91 insertions(+), 16 deletions(-) diff --git a/src/uterm_video.c b/src/uterm_video.c index 32f6eca..f3e029f 100644 --- a/src/uterm_video.c +++ b/src/uterm_video.c @@ -255,12 +255,49 @@ unsigned int uterm_mode_get_height(const struct uterm_mode *mode) return VIDEO_CALL(mode->ops->get_height, 0, mode); } -int display_new(struct uterm_display **out, const struct display_ops *ops) +int display_schedule_vblank_timer(struct uterm_display *disp) +{ + int ret; + + if (disp->vblank_scheduled) + return 0; + + ret = ev_timer_update(disp->vblank_timer, &disp->vblank_spec); + if (ret) + return ret; + + disp->vblank_scheduled = true; + return 0; +} + +void display_set_vblank_timer(struct uterm_display *disp, + unsigned int msecs) +{ + if (msecs >= 1000) + msecs = 999; + else if (msecs == 0) + msecs = 15; + + disp->vblank_spec.it_value.tv_nsec = msecs * 1000 * 1000; +} + +static void display_vblank_timer_event(struct ev_timer *timer, + uint64_t expirations, + void *data) +{ + struct uterm_display *disp = data; + + disp->vblank_scheduled = false; + DISPLAY_CB(disp, UTERM_PAGE_FLIP); +} + +int display_new(struct uterm_display **out, const struct display_ops *ops, + struct uterm_video *video) { struct uterm_display *disp; int ret; - if (!out || !ops) + if (!out || !ops || !video) return -EINVAL; disp = malloc(sizeof(*disp)); @@ -269,19 +306,35 @@ int display_new(struct uterm_display **out, const struct display_ops *ops) memset(disp, 0, sizeof(*disp)); disp->ref = 1; disp->ops = ops; + disp->video = video; ret = shl_hook_new(&disp->hook); if (ret) goto err_free; - ret = VIDEO_CALL(disp->ops->init, 0, disp); + disp->vblank_spec.it_value.tv_nsec = 15 * 1000 * 1000; + + ret = ev_timer_new(&disp->vblank_timer, NULL, + display_vblank_timer_event, disp, NULL); if (ret) goto err_hook; + ret = ev_eloop_add_timer(video->eloop, disp->vblank_timer); + if (ret) + goto err_timer; + + ret = VIDEO_CALL(disp->ops->init, 0, disp); + if (ret) + goto err_eloop_timer; + log_info("new display %p", disp); *out = disp; return 0; +err_eloop_timer: + ev_eloop_rm_timer(disp->vblank_timer); +err_timer: + ev_timer_unref(disp->vblank_timer); err_hook: shl_hook_free(disp->hook); err_free: @@ -313,6 +366,8 @@ void uterm_display_unref(struct uterm_display *disp) mode->next = NULL; uterm_mode_unref(mode); } + ev_eloop_rm_timer(disp->vblank_timer); + ev_timer_unref(disp->vblank_timer); shl_hook_free(disp->hook); free(disp); } diff --git a/src/uterm_video.h b/src/uterm_video.h index 3bbdaef..6815cae 100644 --- a/src/uterm_video.h +++ b/src/uterm_video.h @@ -343,6 +343,10 @@ struct uterm_display { struct uterm_mode *current_mode; int dpms; + bool vblank_scheduled; + struct itimerspec vblank_spec; + struct ev_timer *vblank_timer; + const struct display_ops *ops; union { struct drm_display drm; @@ -351,7 +355,11 @@ struct uterm_display { }; }; -int display_new(struct uterm_display **out, const struct display_ops *ops); +int display_new(struct uterm_display **out, const struct display_ops *ops, + struct uterm_video *video); +void display_set_vblank_timer(struct uterm_display *disp, + unsigned int msecs); +int display_schedule_vblank_timer(struct uterm_display *disp); #define DISPLAY_CB(disp, act) shl_hook_call((disp)->hook, (disp), \ &(struct uterm_display_event){ \ diff --git a/src/uterm_video_drm.c b/src/uterm_video_drm.c index 9ca0d51..fd62ece 100644 --- a/src/uterm_video_drm.c +++ b/src/uterm_video_drm.c @@ -928,7 +928,7 @@ static void bind_display(struct uterm_video *video, drmModeRes *res, struct uterm_mode *mode; int ret, i; - ret = display_new(&disp, &drm_display_ops); + ret = display_new(&disp, &drm_display_ops, video); if (ret) return; @@ -951,7 +951,6 @@ static void bind_display(struct uterm_video *video, drmModeRes *res, return; } - disp->video = video; disp->drm.conn_id = conn->connector_id; disp->flags |= DISPLAY_AVAILABLE; disp->next = video->displays; diff --git a/src/uterm_video_dumb.c b/src/uterm_video_dumb.c index 3633a4a..8df6202 100644 --- a/src/uterm_video_dumb.c +++ b/src/uterm_video_dumb.c @@ -691,7 +691,7 @@ static void bind_display(struct uterm_video *video, drmModeRes *res, struct uterm_mode *mode; int ret, i; - ret = display_new(&disp, &dumb_display_ops); + ret = display_new(&disp, &dumb_display_ops, video); if (ret) return; @@ -714,7 +714,6 @@ static void bind_display(struct uterm_video *video, drmModeRes *res, return; } - disp->video = video; disp->dumb.conn_id = conn->connector_id; disp->flags |= DISPLAY_AVAILABLE; disp->next = video->displays; diff --git a/src/uterm_video_fbdev.c b/src/uterm_video_fbdev.c index c9c733e..94672b3 100644 --- a/src/uterm_video_fbdev.c +++ b/src/uterm_video_fbdev.c @@ -88,6 +88,7 @@ static int display_activate_force(struct uterm_display *disp, int ret, i; uint64_t quot; size_t len; + unsigned int val; if (!disp->video || !video_is_awake(disp->video)) return -EINVAL; @@ -211,10 +212,26 @@ static int display_activate_force(struct uterm_display *disp, quot = (vinfo->upper_margin + vinfo->lower_margin + vinfo->yres); quot *= (vinfo->left_margin + vinfo->right_margin + vinfo->xres); quot *= vinfo->pixclock; - if (quot) + if (quot) { disp->fbdev.rate = 1000000000000000LLU / quot; - else + } else { disp->fbdev.rate = 60 * 1000; + log_warning("cannot read monitor refresh rate, forcing 60 Hz"); + } + + if (disp->fbdev.rate == 0) { + log_warning("monitor refresh rate is 0 Hz, forcing it to 1 Hz"); + disp->fbdev.rate = 1; + } else if (disp->fbdev.rate > 200000) { + log_warning("monitor refresh rate is >200 Hz (%u Hz), forcing it to 200 Hz", + disp->fbdev.rate / 1000); + disp->fbdev.rate = 200000; + } + + val = 1000000 / disp->fbdev.rate; + display_set_vblank_timer(disp, val); + log_debug("vblank timer: %u ms, monitor refresh rate: %u Hz", val, + disp->fbdev.rate / 1000); len = finfo->line_length * vinfo->yres; if (disp->flags & DISPLAY_DBUF) @@ -344,8 +361,7 @@ static int display_swap(struct uterm_display *disp) return -EINVAL; if (!(disp->flags & DISPLAY_DBUF)) { - DISPLAY_CB(disp, UTERM_PAGE_FLIP); - return 0; + return display_schedule_vblank_timer(disp); } vinfo = &disp->fbdev.vinfo; @@ -364,8 +380,7 @@ static int display_swap(struct uterm_display *disp) } disp->fbdev.bufid ^= 1; - DISPLAY_CB(disp, UTERM_PAGE_FLIP); - return 0; + return display_schedule_vblank_timer(disp); } static int clamp_value(int val, int low, int up) @@ -869,7 +884,7 @@ static int video_init(struct uterm_video *video, const char *node) int ret; struct uterm_display *disp; - ret = display_new(&disp, &fbdev_display_ops); + ret = display_new(&disp, &fbdev_display_ops, video); if (ret) return ret; @@ -894,7 +909,6 @@ static int video_init(struct uterm_video *video, const char *node) goto err_node; } - disp->video = video; disp->dpms = UTERM_DPMS_UNKNOWN; video->displays = disp;