diff --git a/src/compositor-drm.c b/src/compositor-drm.c
index e4496e7609d230b23d2132eedb814739ff60896a..626a2debd2aedf2e6a354f1157e12c59080d004e 100644
--- a/src/compositor-drm.c
+++ b/src/compositor-drm.c
@@ -118,7 +118,6 @@ struct drm_compositor {
 
 	uint32_t prev_state;
 
-	clockid_t clock;
 	struct udev_input input;
 
 	uint32_t cursor_width;
@@ -700,7 +699,6 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
 	struct drm_compositor *compositor = (struct drm_compositor *)
 		output_base->compositor;
 	uint32_t fb_id;
-	uint32_t msec;
 	struct timespec ts;
 
 	if (output->destroy_pending)
@@ -723,9 +721,8 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
 
 finish_frame:
 	/* if we cannot page-flip, immediately finish frame */
-	clock_gettime(compositor->clock, &ts);
-	msec = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
-	weston_output_finish_frame(output_base, msec);
+	clock_gettime(compositor->base.presentation_clock, &ts);
+	weston_output_finish_frame(output_base, &ts);
 }
 
 static void
@@ -734,7 +731,7 @@ vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
 {
 	struct drm_sprite *s = (struct drm_sprite *)data;
 	struct drm_output *output = s->output;
-	uint32_t msecs;
+	struct timespec ts;
 
 	output->vblank_pending = 0;
 
@@ -743,8 +740,9 @@ vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
 	s->next = NULL;
 
 	if (!output->page_flip_pending) {
-		msecs = sec * 1000 + usec / 1000;
-		weston_output_finish_frame(&output->base, msecs);
+		ts.tv_sec = sec;
+		ts.tv_nsec = usec * 1000;
+		weston_output_finish_frame(&output->base, &ts);
 	}
 }
 
@@ -756,7 +754,7 @@ page_flip_handler(int fd, unsigned int frame,
 		  unsigned int sec, unsigned int usec, void *data)
 {
 	struct drm_output *output = (struct drm_output *) data;
-	uint32_t msecs;
+	struct timespec ts;
 
 	/* We don't set page_flip_pending on start_repaint_loop, in that case
 	 * we just want to page flip to the current buffer to get an accurate
@@ -772,8 +770,9 @@ page_flip_handler(int fd, unsigned int frame,
 	if (output->destroy_pending)
 		drm_output_destroy(&output->base);
 	else if (!output->vblank_pending) {
-		msecs = sec * 1000 + usec / 1000;
-		weston_output_finish_frame(&output->base, msecs);
+		ts.tv_sec = sec;
+		ts.tv_nsec = usec * 1000;
+		weston_output_finish_frame(&output->base, &ts);
 
 		/* We can't call this from frame_notify, because the output's
 		 * repaint needed flag is cleared just after that */
@@ -1282,6 +1281,7 @@ init_drm(struct drm_compositor *ec, struct udev_device *device)
 	const char *filename, *sysnum;
 	uint64_t cap;
 	int fd, ret;
+	clockid_t clk_id;
 
 	sysnum = udev_device_get_sysnum(device);
 	if (sysnum)
@@ -1307,9 +1307,15 @@ init_drm(struct drm_compositor *ec, struct udev_device *device)
 
 	ret = drmGetCap(fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap);
 	if (ret == 0 && cap == 1)
-		ec->clock = CLOCK_MONOTONIC;
+		clk_id = CLOCK_MONOTONIC;
 	else
-		ec->clock = CLOCK_REALTIME;
+		clk_id = CLOCK_REALTIME;
+
+	if (weston_compositor_set_presentation_clock(&ec->base, clk_id) < 0) {
+		weston_log("Error: failed to set presentation clock %d.\n",
+			   clk_id);
+		return -1;
+	}
 
 	ret = drmGetCap(fd, DRM_CAP_CURSOR_WIDTH, &cap);
 	if (ret == 0)
diff --git a/src/compositor-fbdev.c b/src/compositor-fbdev.c
index e703e0ece74c588d745711b6c449afb67c0fbc77..138aaab5e81c7478c85b577cf178e78ca9cac4a1 100644
--- a/src/compositor-fbdev.c
+++ b/src/compositor-fbdev.c
@@ -114,12 +114,10 @@ to_fbdev_compositor(struct weston_compositor *base)
 static void
 fbdev_output_start_repaint_loop(struct weston_output *output)
 {
-	uint32_t msec;
-	struct timeval tv;
+	struct timespec ts;
 
-	gettimeofday(&tv, NULL);
-	msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
-	weston_output_finish_frame(output, msec);
+	clock_gettime(output->compositor->presentation_clock, &ts);
+	weston_output_finish_frame(output, &ts);
 }
 
 static void
@@ -883,6 +881,10 @@ fbdev_compositor_create(struct wl_display *display, int *argc, char *argv[],
 	                           config) < 0)
 		goto out_free;
 
+	if (weston_compositor_set_presentation_clock_software(
+							&compositor->base) < 0)
+		goto out_compositor;
+
 	compositor->udev = udev_new();
 	if (compositor->udev == NULL) {
 		weston_log("Failed to initialize udev context.\n");
diff --git a/src/compositor-headless.c b/src/compositor-headless.c
index 4ecb8d4d84f70dff3eb3f28fa869429434599583..f883aaf45d1f8174f799aafdf99ac3302c4d2474 100644
--- a/src/compositor-headless.c
+++ b/src/compositor-headless.c
@@ -44,12 +44,10 @@ struct headless_output {
 static void
 headless_output_start_repaint_loop(struct weston_output *output)
 {
-	uint32_t msec;
-	struct timeval tv;
+	struct timespec ts;
 
-	gettimeofday(&tv, NULL);
-	msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
-	weston_output_finish_frame(output, msec);
+	clock_gettime(output->compositor->presentation_clock, &ts);
+	weston_output_finish_frame(output, &ts);
 }
 
 static int
@@ -181,6 +179,9 @@ headless_compositor_create(struct wl_display *display,
 	if (weston_compositor_init(&c->base, display, argc, argv, config) < 0)
 		goto err_free;
 
+	if (weston_compositor_set_presentation_clock_software(&c->base) < 0)
+		goto err_compositor;
+
 	if (headless_input_create(c) < 0)
 		goto err_compositor;
 
diff --git a/src/compositor-rdp.c b/src/compositor-rdp.c
index b749129723e8d77b209d1cd01c9b34f25b0c391f..9098396ee94df13d7671592e69e3698dc470f3aa 100644
--- a/src/compositor-rdp.c
+++ b/src/compositor-rdp.c
@@ -305,12 +305,10 @@ rdp_peer_refresh_region(pixman_region32_t *region, freerdp_peer *peer)
 static void
 rdp_output_start_repaint_loop(struct weston_output *output)
 {
-	uint32_t msec;
-	struct timeval tv;
+	struct timespec ts;
 
-	gettimeofday(&tv, NULL);
-	msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
-	weston_output_finish_frame(output, msec);
+	clock_gettime(output->compositor->presentation_clock, &ts);
+	weston_output_finish_frame(output, &ts);
 }
 
 static int
@@ -1115,6 +1113,9 @@ rdp_compositor_create(struct wl_display *display,
 		c->tls_enabled = 1;
 	}
 
+	if (weston_compositor_set_presentation_clock_software(&c->base) < 0)
+		goto err_compositor;
+
 	if (pixman_renderer_init(&c->base) < 0)
 		goto err_compositor;
 
diff --git a/src/compositor-rpi.c b/src/compositor-rpi.c
index 287451d100a88afb55ef1d09ab1be4f5b680944b..97f07d99f621f3a6d2453a521ac7f360038ee82d 100644
--- a/src/compositor-rpi.c
+++ b/src/compositor-rpi.c
@@ -61,6 +61,7 @@ struct rpi_output;
 struct rpi_flippipe {
 	int readfd;
 	int writefd;
+	clockid_t clk_id;
 	struct wl_event_source *source;
 };
 
@@ -113,29 +114,19 @@ to_rpi_compositor(struct weston_compositor *base)
 	return container_of(base, struct rpi_compositor, base);
 }
 
-static uint64_t
-rpi_get_current_time(void)
-{
-	struct timeval tv;
-
-	/* XXX: use CLOCK_MONOTONIC instead? */
-	gettimeofday(&tv, NULL);
-	return (uint64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;
-}
-
 static void
 rpi_flippipe_update_complete(DISPMANX_UPDATE_HANDLE_T update, void *data)
 {
 	/* This function runs in a different thread. */
 	struct rpi_flippipe *flippipe = data;
-	uint64_t time;
+	struct timespec ts;
 	ssize_t ret;
 
 	/* manufacture flip completion timestamp */
-	time = rpi_get_current_time();
+	clock_gettime(flippipe->clk_id, &ts);
 
-	ret = write(flippipe->writefd, &time, sizeof time);
-	if (ret != sizeof time)
+	ret = write(flippipe->writefd, &ts, sizeof ts);
+	if (ret != sizeof ts)
 		weston_log("ERROR: %s failed to write, ret %zd, errno %d\n",
 			   __func__, ret, errno);
 }
@@ -159,26 +150,27 @@ rpi_dispmanx_update_submit(DISPMANX_UPDATE_HANDLE_T update,
 }
 
 static void
-rpi_output_update_complete(struct rpi_output *output, uint64_t time);
+rpi_output_update_complete(struct rpi_output *output,
+			   const struct timespec *stamp);
 
 static int
 rpi_flippipe_handler(int fd, uint32_t mask, void *data)
 {
 	struct rpi_output *output = data;
 	ssize_t ret;
-	uint64_t time;
+	struct timespec ts;
 
 	if (mask != WL_EVENT_READABLE)
 		weston_log("ERROR: unexpected mask 0x%x in %s\n",
 			   mask, __func__);
 
-	ret = read(fd, &time, sizeof time);
-	if (ret != sizeof time) {
+	ret = read(fd, &ts, sizeof ts);
+	if (ret != sizeof ts) {
 		weston_log("ERROR: %s failed to read, ret %zd, errno %d\n",
 			   __func__, ret, errno);
 	}
 
-	rpi_output_update_complete(output, time);
+	rpi_output_update_complete(output, &ts);
 
 	return 1;
 }
@@ -194,6 +186,7 @@ rpi_flippipe_init(struct rpi_flippipe *flippipe, struct rpi_output *output)
 
 	flippipe->readfd = fd[0];
 	flippipe->writefd = fd[1];
+	flippipe->clk_id = output->compositor->base.presentation_clock;
 
 	loop = wl_display_get_event_loop(output->compositor->base.wl_display);
 	flippipe->source = wl_event_loop_add_fd(loop, flippipe->readfd,
@@ -220,10 +213,10 @@ rpi_flippipe_release(struct rpi_flippipe *flippipe)
 static void
 rpi_output_start_repaint_loop(struct weston_output *output)
 {
-	uint64_t time;
+	struct timespec ts;
 
-	time = rpi_get_current_time();
-	weston_output_finish_frame(output, time);
+	clock_gettime(output->compositor->presentation_clock, &ts);
+	weston_output_finish_frame(output, &ts);
 }
 
 static int
@@ -254,11 +247,13 @@ rpi_output_repaint(struct weston_output *base, pixman_region32_t *damage)
 }
 
 static void
-rpi_output_update_complete(struct rpi_output *output, uint64_t time)
+rpi_output_update_complete(struct rpi_output *output,
+			   const struct timespec *stamp)
 {
-	DBG("frame update complete(%" PRIu64 ")\n", time);
+	DBG("frame update complete(%ld.%09ld)\n",
+	    (long)stamp->tv_sec, (long)stamp->tv_nsec);
 	rpi_renderer_finish_frame(&output->base);
-	weston_output_finish_frame(&output->base, time);
+	weston_output_finish_frame(&output->base, stamp);
 }
 
 static void
@@ -503,6 +498,10 @@ rpi_compositor_create(struct wl_display *display, int *argc, char *argv[],
 				   config) < 0)
 		goto out_free;
 
+	if (weston_compositor_set_presentation_clock_software(
+							&compositor->base) < 0)
+		goto out_compositor;
+
 	compositor->udev = udev_new();
 	if (compositor->udev == NULL) {
 		weston_log("Failed to initialize udev context.\n");
diff --git a/src/compositor-wayland.c b/src/compositor-wayland.c
index 5f73c78740397378c84d6920b00343c42a735978..bf71a76895f0a575c59a8218d7e9a92c39886063 100644
--- a/src/compositor-wayland.c
+++ b/src/compositor-wayland.c
@@ -306,9 +306,14 @@ static void
 frame_done(void *data, struct wl_callback *callback, uint32_t time)
 {
 	struct weston_output *output = data;
+	struct timespec ts;
 
 	wl_callback_destroy(callback);
-	weston_output_finish_frame(output, time);
+
+	/* XXX: use the presentation extension for proper timings */
+	ts.tv_sec = time / 1000;
+	ts.tv_nsec = (time % 1000) * 1000000;
+	weston_output_finish_frame(output, &ts);
 }
 
 static const struct wl_callback_listener frame_listener = {
@@ -1943,8 +1948,10 @@ wayland_compositor_create(struct wl_display *display, int use_pixman,
 				   config) < 0)
 		goto err_free;
 
-	c->parent.wl_display = wl_display_connect(display_name);
+	if (weston_compositor_set_presentation_clock_software(&c->base) < 0)
+		goto err_compositor;
 
+	c->parent.wl_display = wl_display_connect(display_name);
 	if (c->parent.wl_display == NULL) {
 		weston_log("failed to create display: %m\n");
 		goto err_compositor;
diff --git a/src/compositor-x11.c b/src/compositor-x11.c
index b602bc9ae640d5b7ba694126d4e28866ae409d73..1baee29ed47ed9cbac7cd02d8f80fbea10cfa8f9 100644
--- a/src/compositor-x11.c
+++ b/src/compositor-x11.c
@@ -338,12 +338,10 @@ x11_input_destroy(struct x11_compositor *compositor)
 static void
 x11_output_start_repaint_loop(struct weston_output *output)
 {
-	uint32_t msec;
-	struct timeval tv;
+	struct timespec ts;
 
-	gettimeofday(&tv, NULL);
-	msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
-	weston_output_finish_frame(output, msec);
+	clock_gettime(output->compositor->presentation_clock, &ts);
+	weston_output_finish_frame(output, &ts);
 }
 
 static int
@@ -1498,6 +1496,9 @@ x11_compositor_create(struct wl_display *display,
 	if (weston_compositor_init(&c->base, display, argc, argv, config) < 0)
 		goto err_free;
 
+	if (weston_compositor_set_presentation_clock_software(&c->base) < 0)
+		goto err_free;
+
 	c->dpy = XOpenDisplay(NULL);
 	if (c->dpy == NULL)
 		goto err_free;
diff --git a/src/compositor.c b/src/compositor.c
index d7895084906542c48ed555badee2c385fda394fc..f34d712205a8846a4d83723deefc5caaae5d1f08 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -1896,7 +1896,7 @@ weston_compositor_build_view_list(struct weston_compositor *compositor)
 }
 
 static int
-weston_output_repaint(struct weston_output *output, uint32_t msecs)
+weston_output_repaint(struct weston_output *output)
 {
 	struct weston_compositor *ec = output->compositor;
 	struct weston_view *ev;
@@ -1951,13 +1951,13 @@ weston_output_repaint(struct weston_output *output, uint32_t msecs)
 	wl_event_loop_dispatch(ec->input_loop, 0);
 
 	wl_list_for_each_safe(cb, cnext, &frame_callback_list, link) {
-		wl_callback_send_done(cb->resource, msecs);
+		wl_callback_send_done(cb->resource, output->frame_time);
 		wl_resource_destroy(cb->resource);
 	}
 
 	wl_list_for_each_safe(animation, next, &output->animation_list, link) {
 		animation->frame_counter++;
-		animation->frame(animation, output, msecs);
+		animation->frame(animation, output, output->frame_time);
 	}
 
 	return r;
@@ -1974,19 +1974,20 @@ weston_compositor_read_input(int fd, uint32_t mask, void *data)
 }
 
 WL_EXPORT void
-weston_output_finish_frame(struct weston_output *output, uint32_t msecs)
+weston_output_finish_frame(struct weston_output *output,
+			   const struct timespec *stamp)
 {
 	struct weston_compositor *compositor = output->compositor;
 	struct wl_event_loop *loop =
 		wl_display_get_event_loop(compositor->wl_display);
 	int fd, r;
 
-	output->frame_time = msecs;
+	output->frame_time = stamp->tv_sec * 1000 + stamp->tv_nsec / 1000000;
 
 	if (output->repaint_needed &&
 	    compositor->state != WESTON_COMPOSITOR_SLEEPING &&
 	    compositor->state != WESTON_COMPOSITOR_OFFSCREEN) {
-		r = weston_output_repaint(output, msecs);
+		r = weston_output_repaint(output);
 		if (!r)
 			return;
 	}
@@ -3773,7 +3774,7 @@ bind_presentation(struct wl_client *client,
 
 	wl_resource_set_implementation(resource, &presentation_implementation,
 				       compositor, NULL);
-	presentation_send_clock_id(resource, CLOCK_MONOTONIC);
+	presentation_send_clock_id(resource, compositor->presentation_clock);
 }
 
 static void
@@ -3974,6 +3975,48 @@ weston_compositor_set_default_pointer_grab(struct weston_compositor *ec,
 	}
 }
 
+WL_EXPORT int
+weston_compositor_set_presentation_clock(struct weston_compositor *compositor,
+					 clockid_t clk_id)
+{
+	struct timespec ts;
+
+	if (clock_gettime(clk_id, &ts) < 0)
+		return -1;
+
+	compositor->presentation_clock = clk_id;
+
+	return 0;
+}
+
+/*
+ * For choosing the software clock, when the display hardware or API
+ * does not expose a compatible presentation timestamp.
+ */
+WL_EXPORT int
+weston_compositor_set_presentation_clock_software(
+					struct weston_compositor *compositor)
+{
+	/* In order of preference */
+	static const clockid_t clocks[] = {
+		CLOCK_MONOTONIC_RAW,	/* no jumps, no crawling */
+		CLOCK_MONOTONIC_COARSE,	/* no jumps, may crawl, fast & coarse */
+		CLOCK_MONOTONIC,	/* no jumps, may crawl */
+		CLOCK_REALTIME_COARSE,	/* may jump and crawl, fast & coarse */
+		CLOCK_REALTIME		/* may jump and crawl */
+	};
+	unsigned i;
+
+	for (i = 0; i < ARRAY_LENGTH(clocks); i++)
+		if (weston_compositor_set_presentation_clock(compositor,
+							     clocks[i]) == 0)
+			return 0;
+
+	weston_log("Error: no suitable presentation clock available.\n");
+
+	return -1;
+}
+
 WL_EXPORT void
 weston_version(int *major, int *minor, int *micro)
 {
@@ -3982,6 +4025,24 @@ weston_version(int *major, int *minor, int *micro)
 	*micro = WESTON_VERSION_MICRO;
 }
 
+static const char *
+clock_name(clockid_t clk_id)
+{
+	static const char *names[] = {
+		[CLOCK_REALTIME] =		"CLOCK_REALTIME",
+		[CLOCK_MONOTONIC] =		"CLOCK_MONOTONIC",
+		[CLOCK_MONOTONIC_RAW] =		"CLOCK_MONOTONIC_RAW",
+		[CLOCK_REALTIME_COARSE] =	"CLOCK_REALTIME_COARSE",
+		[CLOCK_MONOTONIC_COARSE] =	"CLOCK_MONOTONIC_COARSE",
+		[CLOCK_BOOTTIME] =		"CLOCK_BOOTTIME",
+	};
+
+	if (clk_id < 0 || (unsigned)clk_id >= ARRAY_LENGTH(names))
+		return "unknown";
+
+	return names[clk_id];
+}
+
 static const struct {
 	uint32_t bit; /* enum weston_capability */
 	const char *desc;
@@ -4003,6 +4064,10 @@ weston_compositor_log_capabilities(struct weston_compositor *compositor)
 				    capability_strings[i].desc,
 				    yes ? "yes" : "no");
 	}
+
+	weston_log_continue(STAMP_SPACE "presentation clock: %s, id %d\n",
+			    clock_name(compositor->presentation_clock),
+			    compositor->presentation_clock);
 }
 
 static int on_term_signal(int signal_number, void *data)
diff --git a/src/compositor.h b/src/compositor.h
index f4263d8f63d70563814167eb7d343ed198774da4..61b374f734730924fa3b235df88a994b5206fa54 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -28,6 +28,7 @@
 extern "C" {
 #endif
 
+#include <time.h>
 #include <pixman.h>
 #include <xkbcommon/xkbcommon.h>
 
@@ -201,7 +202,7 @@ struct weston_output {
 	struct wl_signal frame_signal;
 	struct wl_signal destroy_signal;
 	int move_x, move_y;
-	uint32_t frame_time;
+	uint32_t frame_time; /* presentation timestamp in milliseconds */
 	int disable_planes;
 	int destroying;
 
@@ -663,6 +664,8 @@ struct weston_compositor {
 
 	int32_t kb_repeat_rate;
 	int32_t kb_repeat_delay;
+
+	clockid_t presentation_clock;
 };
 
 struct weston_buffer {
@@ -1046,7 +1049,8 @@ weston_compositor_stack_plane(struct weston_compositor *ec,
 			      struct weston_plane *above);
 
 void
-weston_output_finish_frame(struct weston_output *output, uint32_t msecs);
+weston_output_finish_frame(struct weston_output *output,
+			   const struct timespec *stamp);
 void
 weston_output_schedule_repaint(struct weston_output *output);
 void
@@ -1234,6 +1238,12 @@ weston_compositor_get_time(void);
 int
 weston_compositor_init(struct weston_compositor *ec, struct wl_display *display,
 		       int *argc, char *argv[], struct weston_config *config);
+int
+weston_compositor_set_presentation_clock(struct weston_compositor *compositor,
+					 clockid_t clk_id);
+int
+weston_compositor_set_presentation_clock_software(
+					struct weston_compositor *compositor);
 void
 weston_compositor_shutdown(struct weston_compositor *ec);
 void