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:
parent
efd00b154e
commit
c1256560d1
@ -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);
|
||||
}
|
||||
|
@ -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){ \
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user