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);
|
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;
|
struct uterm_display *disp;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!out || !ops)
|
if (!out || !ops || !video)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
disp = malloc(sizeof(*disp));
|
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));
|
memset(disp, 0, sizeof(*disp));
|
||||||
disp->ref = 1;
|
disp->ref = 1;
|
||||||
disp->ops = ops;
|
disp->ops = ops;
|
||||||
|
disp->video = video;
|
||||||
|
|
||||||
ret = shl_hook_new(&disp->hook);
|
ret = shl_hook_new(&disp->hook);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_free;
|
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)
|
if (ret)
|
||||||
goto err_hook;
|
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);
|
log_info("new display %p", disp);
|
||||||
*out = disp;
|
*out = disp;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_eloop_timer:
|
||||||
|
ev_eloop_rm_timer(disp->vblank_timer);
|
||||||
|
err_timer:
|
||||||
|
ev_timer_unref(disp->vblank_timer);
|
||||||
err_hook:
|
err_hook:
|
||||||
shl_hook_free(disp->hook);
|
shl_hook_free(disp->hook);
|
||||||
err_free:
|
err_free:
|
||||||
@ -313,6 +366,8 @@ void uterm_display_unref(struct uterm_display *disp)
|
|||||||
mode->next = NULL;
|
mode->next = NULL;
|
||||||
uterm_mode_unref(mode);
|
uterm_mode_unref(mode);
|
||||||
}
|
}
|
||||||
|
ev_eloop_rm_timer(disp->vblank_timer);
|
||||||
|
ev_timer_unref(disp->vblank_timer);
|
||||||
shl_hook_free(disp->hook);
|
shl_hook_free(disp->hook);
|
||||||
free(disp);
|
free(disp);
|
||||||
}
|
}
|
||||||
|
@ -343,6 +343,10 @@ struct uterm_display {
|
|||||||
struct uterm_mode *current_mode;
|
struct uterm_mode *current_mode;
|
||||||
int dpms;
|
int dpms;
|
||||||
|
|
||||||
|
bool vblank_scheduled;
|
||||||
|
struct itimerspec vblank_spec;
|
||||||
|
struct ev_timer *vblank_timer;
|
||||||
|
|
||||||
const struct display_ops *ops;
|
const struct display_ops *ops;
|
||||||
union {
|
union {
|
||||||
struct drm_display drm;
|
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), \
|
#define DISPLAY_CB(disp, act) shl_hook_call((disp)->hook, (disp), \
|
||||||
&(struct uterm_display_event){ \
|
&(struct uterm_display_event){ \
|
||||||
|
@ -928,7 +928,7 @@ static void bind_display(struct uterm_video *video, drmModeRes *res,
|
|||||||
struct uterm_mode *mode;
|
struct uterm_mode *mode;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
|
|
||||||
ret = display_new(&disp, &drm_display_ops);
|
ret = display_new(&disp, &drm_display_ops, video);
|
||||||
if (ret)
|
if (ret)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -951,7 +951,6 @@ static void bind_display(struct uterm_video *video, drmModeRes *res,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
disp->video = video;
|
|
||||||
disp->drm.conn_id = conn->connector_id;
|
disp->drm.conn_id = conn->connector_id;
|
||||||
disp->flags |= DISPLAY_AVAILABLE;
|
disp->flags |= DISPLAY_AVAILABLE;
|
||||||
disp->next = video->displays;
|
disp->next = video->displays;
|
||||||
|
@ -691,7 +691,7 @@ static void bind_display(struct uterm_video *video, drmModeRes *res,
|
|||||||
struct uterm_mode *mode;
|
struct uterm_mode *mode;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
|
|
||||||
ret = display_new(&disp, &dumb_display_ops);
|
ret = display_new(&disp, &dumb_display_ops, video);
|
||||||
if (ret)
|
if (ret)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -714,7 +714,6 @@ static void bind_display(struct uterm_video *video, drmModeRes *res,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
disp->video = video;
|
|
||||||
disp->dumb.conn_id = conn->connector_id;
|
disp->dumb.conn_id = conn->connector_id;
|
||||||
disp->flags |= DISPLAY_AVAILABLE;
|
disp->flags |= DISPLAY_AVAILABLE;
|
||||||
disp->next = video->displays;
|
disp->next = video->displays;
|
||||||
|
@ -88,6 +88,7 @@ static int display_activate_force(struct uterm_display *disp,
|
|||||||
int ret, i;
|
int ret, i;
|
||||||
uint64_t quot;
|
uint64_t quot;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
unsigned int val;
|
||||||
|
|
||||||
if (!disp->video || !video_is_awake(disp->video))
|
if (!disp->video || !video_is_awake(disp->video))
|
||||||
return -EINVAL;
|
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->upper_margin + vinfo->lower_margin + vinfo->yres);
|
||||||
quot *= (vinfo->left_margin + vinfo->right_margin + vinfo->xres);
|
quot *= (vinfo->left_margin + vinfo->right_margin + vinfo->xres);
|
||||||
quot *= vinfo->pixclock;
|
quot *= vinfo->pixclock;
|
||||||
if (quot)
|
if (quot) {
|
||||||
disp->fbdev.rate = 1000000000000000LLU / quot;
|
disp->fbdev.rate = 1000000000000000LLU / quot;
|
||||||
else
|
} else {
|
||||||
disp->fbdev.rate = 60 * 1000;
|
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;
|
len = finfo->line_length * vinfo->yres;
|
||||||
if (disp->flags & DISPLAY_DBUF)
|
if (disp->flags & DISPLAY_DBUF)
|
||||||
@ -344,8 +361,7 @@ static int display_swap(struct uterm_display *disp)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (!(disp->flags & DISPLAY_DBUF)) {
|
if (!(disp->flags & DISPLAY_DBUF)) {
|
||||||
DISPLAY_CB(disp, UTERM_PAGE_FLIP);
|
return display_schedule_vblank_timer(disp);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vinfo = &disp->fbdev.vinfo;
|
vinfo = &disp->fbdev.vinfo;
|
||||||
@ -364,8 +380,7 @@ static int display_swap(struct uterm_display *disp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
disp->fbdev.bufid ^= 1;
|
disp->fbdev.bufid ^= 1;
|
||||||
DISPLAY_CB(disp, UTERM_PAGE_FLIP);
|
return display_schedule_vblank_timer(disp);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int clamp_value(int val, int low, int up)
|
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;
|
int ret;
|
||||||
struct uterm_display *disp;
|
struct uterm_display *disp;
|
||||||
|
|
||||||
ret = display_new(&disp, &fbdev_display_ops);
|
ret = display_new(&disp, &fbdev_display_ops, video);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -894,7 +909,6 @@ static int video_init(struct uterm_video *video, const char *node)
|
|||||||
goto err_node;
|
goto err_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
disp->video = video;
|
|
||||||
disp->dpms = UTERM_DPMS_UNKNOWN;
|
disp->dpms = UTERM_DPMS_UNKNOWN;
|
||||||
video->displays = disp;
|
video->displays = disp;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user