Merge branch 'pageflip' of git://people.freedesktop.org/~jbarnes/drm

Conflicts:
	include/drm/drm.h - RMFB had its signature changed to avoid uint32_t
diff --git a/include/drm/drm.h b/include/drm/drm.h
index 649c46f..5408c08 100644
--- a/include/drm/drm.h
+++ b/include/drm/drm.h
@@ -696,6 +696,9 @@
 #define DRM_IOCTL_MODE_GETFB		DRM_IOWR(0xAD, struct drm_mode_fb_cmd)
 #define DRM_IOCTL_MODE_ADDFB		DRM_IOWR(0xAE, struct drm_mode_fb_cmd)
 #define DRM_IOCTL_MODE_RMFB		DRM_IOWR(0xAF, unsigned int)
+#define DRM_IOCTL_MODE_PAGE_FLIP	DRM_IOWR(0xB0, struct drm_mode_crtc_page_flip)
+
+/*@}*/
 
 /**
  * Device specific ioctls should only be in their respective headers
@@ -726,6 +729,7 @@
 };
 
 #define DRM_EVENT_VBLANK 0x01
+#define DRM_EVENT_FLIP_COMPLETE 0x02
 
 struct drm_event_vblank {
 	struct drm_event base;
diff --git a/include/drm/drm_mode.h b/include/drm/drm_mode.h
index 852505e..1fd3026 100644
--- a/include/drm/drm_mode.h
+++ b/include/drm/drm_mode.h
@@ -265,4 +265,15 @@
 	__u64 blue;
 };
 
+#define DRM_MODE_PAGE_FLIP_EVENT 0x01
+#define DRM_MODE_PAGE_FLIP_FLAGS DRM_MODE_PAGE_FLIP_EVENT
+
+struct drm_mode_crtc_page_flip {
+	uint32_t crtc_id;
+	uint32_t fb_id;
+	uint32_t flags;
+	uint32_t reserved;
+	uint64_t user_data;
+};
+
 #endif
diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h
index 25ff7b7..75b0e1d 100644
--- a/include/drm/i915_drm.h
+++ b/include/drm/i915_drm.h
@@ -271,6 +271,7 @@
 #define I915_PARAM_HAS_GEM               5
 #define I915_PARAM_NUM_FENCES_AVAIL      6
 #define I915_PARAM_HAS_OVERLAY           7
+#define I915_PARAM_HAS_PAGEFLIPPING      8
 
 typedef struct drm_i915_getparam {
 	int param;
diff --git a/tests/modetest/modetest.c b/tests/modetest/modetest.c
index 6c69a57..4739a78 100644
--- a/tests/modetest/modetest.c
+++ b/tests/modetest/modetest.c
@@ -46,6 +46,7 @@
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
+#include <sys/poll.h>
 
 #include "xf86drm.h"
 #include "xf86drmMode.h"
@@ -172,7 +173,7 @@
 	int i, j;
 
 	printf("Connectors:\n");
-	printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\n");
+	printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\tencoders\n");
 	for (i = 0; i < resources->count_connectors; i++) {
 		connector = drmModeGetConnector(fd, resources->connectors[i]);
 
@@ -182,7 +183,7 @@
 			continue;
 		}
 
-		printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\n",
+		printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\t",
 		       connector->connector_id,
 		       connector->encoder_id,
 		       connector_status_str(connector->connection),
@@ -190,6 +191,10 @@
 		       connector->mmWidth, connector->mmHeight,
 		       connector->count_modes);
 
+		for (j = 0; j < connector->count_encoders; j++)
+			printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]);
+		printf("\n");
+
 		if (!connector->count_modes)
 			continue;
 
@@ -271,6 +276,10 @@
 	drmModeModeInfo *mode;
 	drmModeEncoder *encoder;
 	int crtc;
+	unsigned int fb_id[2], current_fb_id;
+	struct timeval start;
+
+	int swap_count;
 };	
 
 static void
@@ -457,16 +466,83 @@
 
 #endif
 
+static int
+create_grey_buffer(drm_intel_bufmgr *bufmgr,
+		   int width, int height, int *stride_out, drm_intel_bo **bo_out)
+{
+	drm_intel_bo *bo;
+	unsigned int *fb_ptr;
+	int size, ret, i, stride;
+	div_t d;
+
+	/* Mode size at 32 bpp */
+	stride = width * 4;
+	size = stride * height;
+
+	bo = drm_intel_bo_alloc(bufmgr, "frontbuffer", size, 4096);
+	if (!bo) {
+		fprintf(stderr, "failed to alloc buffer: %s\n",
+			strerror(errno));
+		return -1;
+	}
+
+	ret = drm_intel_gem_bo_map_gtt(bo);
+	if (ret) {
+		fprintf(stderr, "failed to GTT map buffer: %s\n",
+			strerror(errno));
+		return -1;
+	}
+
+	memset(bo->virtual, 0x77, size);
+	drm_intel_gem_bo_unmap_gtt(bo);
+
+	*bo_out = bo;
+	*stride_out = stride;
+
+	return 0;
+}
+
+void
+page_flip_handler(int fd, unsigned int frame,
+		  unsigned int sec, unsigned int usec, void *data)
+{
+	struct connector *c;
+	unsigned int new_fb_id;
+	int len, ms;
+	struct timeval end;
+	double t;
+
+	c = data;
+	if (c->current_fb_id == c->fb_id[0])
+		new_fb_id = c->fb_id[1];
+	else
+		new_fb_id = c->fb_id[0];
+			
+	drmModePageFlip(fd, c->crtc, new_fb_id,
+			DRM_MODE_PAGE_FLIP_EVENT, c);
+	c->current_fb_id = new_fb_id;
+	c->swap_count++;
+	if (c->swap_count == 60) {
+		gettimeofday(&end, NULL);
+		t = end.tv_sec + end.tv_usec * 1e-6 -
+			(c->start.tv_sec + c->start.tv_usec * 1e-6);
+		fprintf(stderr, "freq: %.02fHz\n", c->swap_count / t);
+		c->swap_count = 0;
+		c->start = end;
+	}
+}
+
 static void
-set_mode(struct connector *c, int count)
+set_mode(struct connector *c, int count, int page_flip)
 {
 	drmModeConnector *connector;
 	drmModeEncoder *encoder = NULL;
 	struct drm_mode_modeinfo *mode = NULL;
 	drm_intel_bufmgr *bufmgr;
-	drm_intel_bo *bo;
-	unsigned int fb_id;
+	drm_intel_bo *bo, *other_bo;
+	unsigned int fb_id, other_fb_id;
 	int i, j, ret, width, height, x, stride;
+	drmEventContext evctx;
 
 	width = 0;
 	height = 0;
@@ -497,7 +573,6 @@
 
 	x = 0;
 	for (i = 0; i < count; i++) {
-		int crtc_id;
 		if (c[i].mode == NULL)
 			continue;
 
@@ -513,11 +588,61 @@
 			return;
 		}
 	}
+
+	if (!page_flip)
+		return;
+
+	if (create_grey_buffer(bufmgr, width, height, &stride, &other_bo))
+		return;
+
+	ret = drmModeAddFB(fd, width, height, 32, 32, stride, other_bo->handle,
+			   &other_fb_id);
+	if (ret) {
+		fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
+		return;
+	}
+
+	for (i = 0; i < count; i++) {
+		if (c[i].mode == NULL)
+			continue;
+
+		drmModePageFlip(fd, c[i].crtc, other_fb_id,
+				DRM_MODE_PAGE_FLIP_EVENT, &c[i]);
+		gettimeofday(&c[i].start, NULL);
+		c[i].swap_count = 0;
+ 		c[i].fb_id[0] = fb_id;
+ 		c[i].fb_id[1] = other_fb_id;
+ 		c[i].current_fb_id = fb_id;
+	}
+
+	memset(&evctx, 0, sizeof evctx);
+	evctx.version = DRM_EVENT_CONTEXT_VERSION;
+	evctx.vblank_handler = NULL;
+	evctx.pageflip_handler = page_flip_handler;
+	
+	while (1) {
+		struct pollfd pfd[2];
+
+		pfd[0].fd = 0;
+		pfd[0].events = POLLIN;
+		pfd[1].fd = fd;
+		pfd[1].events = POLLIN;
+
+		if (poll(pfd, 2, -1) < 0) {
+			fprintf(stderr, "poll error\n");
+			break;
+		}
+
+		if (pfd[0].revents)
+			break;
+
+		drmHandleEvent(fd, &evctx);
+	}
 }
 
 extern char *optarg;
 extern int optind, opterr, optopt;
-static char optstr[] = "ecpmfs:";
+static char optstr[] = "ecpmfs:v";
 
 void usage(char *name)
 {
@@ -527,6 +652,7 @@
 	fprintf(stderr, "\t-p\tlist CRTCs (pipes)\n");
 	fprintf(stderr, "\t-m\tlist modes\n");
 	fprintf(stderr, "\t-f\tlist framebuffers\n");
+	fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
 	fprintf(stderr, "\t-s <connector_id>:<mode>\tset a mode\n");
 	fprintf(stderr, "\t-s <connector_id>@<crtc_id>:<mode>\tset a mode\n");
 	fprintf(stderr, "\n\tDefault is to dump all info.\n");
@@ -539,6 +665,7 @@
 {
 	int c;
 	int encoders = 0, connectors = 0, crtcs = 0, framebuffers = 0;
+	int test_vsync = 0;
 	char *modules[] = { "i915", "radeon" };
 	char *modeset = NULL, *mode, *connector;
 	int i, connector_id, count = 0;
@@ -562,6 +689,9 @@
 		case 'f':
 			framebuffers = 1;
 			break;
+		case 'v':
+			test_vsync = 1;
+			break;
 		case 's':
 			modeset = strdup(optarg);
 			con_args[count].crtc = -1;
@@ -614,7 +744,7 @@
 	dump_resource(framebuffers);
 
 	if (count > 0) {
-		set_mode(con_args, count);
+		set_mode(con_args, count, test_vsync);
 		getchar();
 	}
 
diff --git a/xf86drm.h b/xf86drm.h
index 496d95d..89f1db1 100644
--- a/xf86drm.h
+++ b/xf86drm.h
@@ -707,6 +707,12 @@
 			       unsigned int tv_usec,
 			       void *user_data);
 
+	void (*page_flip_handler)(int fd,
+				  unsigned int sequence,
+				  unsigned int tv_sec,
+				  unsigned int tv_usec,
+				  void *user_data);
+
 } drmEventContext, *drmEventContextPtr;
 
 extern int drmHandleEvent(int fd, drmEventContextPtr evctx);
diff --git a/xf86drmMode.c b/xf86drmMode.c
index 6d85113..e951685 100644
--- a/xf86drmMode.c
+++ b/xf86drmMode.c
@@ -700,7 +700,17 @@
 					      vblank->tv_usec,
 					      U642VOID (vblank->user_data));
 			break;
-			
+		case DRM_EVENT_FLIP_COMPLETE:
+			if (evctx->version < 1 ||
+			    evctx->page_flip_handler == NULL)
+				break;
+			vblank = (struct drm_event_vblank *) e;
+			evctx->page_flip_handler(fd,
+						 vblank->sequence,
+						 vblank->tv_sec,
+						 vblank->tv_usec,
+						 U642VOID (vblank->user_data));
+			break;
 		default:
 			break;
 		}
@@ -710,3 +720,16 @@
 	return 0;
 }
 
+int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id,
+		    uint32_t flags, void *user_data)
+{
+	struct drm_mode_crtc_page_flip flip;
+
+	flip.fb_id = fb_id;
+	flip.crtc_id = crtc_id;
+	flip.user_data = VOID2U64(user_data);
+	flip.flags = flags;
+	flip.reserved = 0;
+
+	return drmIoctl(fd, DRM_IOCTL_MODE_PAGE_FLIP, &flip);
+}
diff --git a/xf86drmMode.h b/xf86drmMode.h
index 62304bb..705369f 100644
--- a/xf86drmMode.h
+++ b/xf86drmMode.h
@@ -362,3 +362,5 @@
 			       uint16_t *red, uint16_t *green, uint16_t *blue);
 extern int drmModeCrtcGetGamma(int fd, uint32_t crtc_id, uint32_t size,
 			       uint16_t *red, uint16_t *green, uint16_t *blue);
+extern int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id,
+			   uint32_t flags, void *user_data);