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 <dh.herrmann@googlemail.com>
This commit is contained in:
David Herrmann 2012-10-15 13:22:42 +02:00
parent efd00b154e
commit c1256560d1
5 changed files with 91 additions and 16 deletions

View File

@ -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);
}

View File

@ -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){ \

View File

@ -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;

View File

@ -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;

View File

@ -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;