All of lore.kernel.org
 help / color / mirror / Atom feed
From: Benjamin Gaignard <benjamin.gaignard@linaro.org>
To: dri-devel@lists.freedesktop.org, eric.engestrom@intel.com,
	robclark@freedesktop.org, daniels@collabora.com,
	philippe.cornu@st.com, emil.l.velikov@gmail.com
Subject: [PATCH libdrm v3] tests/modetest: Add atomic support
Date: Wed, 25 Jul 2018 16:00:16 +0200	[thread overview]
Message-ID: <20180725140016.12921-1-benjamin.gaignard@linaro.org> (raw)

If "-a" option is set this make modetest use atomic API instead
of legacy API.

Test the frame rate ("-v") it does a loop and swap between two
framebuffer for each active planes.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
---

version 3:
- merge atomic code into modetest itself
- do not change build systems

version 2 :
- make modetest_atomic compile with meson and Android

version 1:
- add dedicated modetest_atomic tool to test atomic API

 tests/modetest/modetest.c | 349 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 321 insertions(+), 28 deletions(-)

diff --git a/tests/modetest/modetest.c b/tests/modetest/modetest.c
index 62957d84..975dcbcd 100644
--- a/tests/modetest/modetest.c
+++ b/tests/modetest/modetest.c
@@ -119,6 +119,9 @@ struct device {
 		struct bo *bo;
 		struct bo *cursor_bo;
 	} mode;
+
+	int use_atomic;
+	drmModeAtomicReq *req;
 };
 
 static inline int64_t U642I64(uint64_t val)
@@ -805,7 +808,9 @@ struct plane_arg {
 	uint32_t w, h;
 	double scale;
 	unsigned int fb_id;
+	unsigned int old_fb_id;
 	struct bo *bo;
+	struct bo *old_bo;
 	char format_str[5]; /* need to leave room for terminating \0 */
 	unsigned int fourcc;
 };
@@ -999,8 +1004,12 @@ static void set_property(struct device *dev, struct property_arg *p)
 
 	p->prop_id = props->props[i];
 
-	ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type,
-				       p->prop_id, p->value);
+	if (!dev->use_atomic)
+		ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type,
+									   p->prop_id, p->value);
+	else
+		ret = drmModeAtomicAddProperty(dev->req, p->obj_id, p->prop_id, p->value);
+
 	if (ret < 0)
 		fprintf(stderr, "failed to set %s %i property %s to %" PRIu64 ": %s\n",
 			obj_type, p->obj_id, p->name, p->value, strerror(errno));
@@ -1049,6 +1058,94 @@ static bool format_support(const drmModePlanePtr ovr, uint32_t fmt)
 	return false;
 }
 
+static void add_property(struct device *dev, uint32_t obj_id,
+			       const char *name, uint64_t value)
+{
+	struct property_arg p;
+
+	p.obj_id = obj_id;
+	strcpy(p.name, name);
+	p.value = value;
+
+	set_property(dev, &p);
+}
+
+static int atomic_set_plane(struct device *dev, struct plane_arg *p,
+							int pattern, bool update)
+{
+	uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
+	struct bo *plane_bo;
+	int crtc_x, crtc_y, crtc_w, crtc_h;
+	struct crtc *crtc = NULL;
+	unsigned int i;
+	unsigned int old_fb_id;
+
+	/* Find an unused plane which can be connected to our CRTC. Find the
+	 * CRTC index first, then iterate over available planes.
+	 */
+	for (i = 0; i < (unsigned int)dev->resources->res->count_crtcs; i++) {
+		if (p->crtc_id == dev->resources->res->crtcs[i]) {
+			crtc = &dev->resources->crtcs[i];
+			break;
+		}
+	}
+
+	if (!crtc) {
+		fprintf(stderr, "CRTC %u not found\n", p->crtc_id);
+		return -1;
+	}
+
+	if (!update)
+		fprintf(stderr, "testing %dx%d@%s on plane %u, crtc %u\n",
+			p->w, p->h, p->format_str, p->plane_id, p->crtc_id);
+
+	plane_bo = p->old_bo;
+	p->old_bo = p->bo;
+
+	if (!plane_bo) {
+		plane_bo = bo_create(dev->fd, p->fourcc, p->w, p->h,
+				     handles, pitches, offsets, pattern);
+
+		if (plane_bo == NULL)
+			return -1;
+
+		if (drmModeAddFB2(dev->fd, p->w, p->h, p->fourcc,
+			handles, pitches, offsets, &p->fb_id, 0)) {
+			fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
+			return -1;
+		}
+	}
+
+	p->bo = plane_bo;
+
+	old_fb_id = p->fb_id;
+	p->old_fb_id = old_fb_id;
+
+	crtc_w = p->w * p->scale;
+	crtc_h = p->h * p->scale;
+	if (!p->has_position) {
+		/* Default to the middle of the screen */
+		crtc_x = (crtc->mode->hdisplay - crtc_w) / 2;
+		crtc_y = (crtc->mode->vdisplay - crtc_h) / 2;
+	} else {
+		crtc_x = p->x;
+		crtc_y = p->y;
+	}
+
+	add_property(dev, p->plane_id, "FB_ID", p->fb_id);
+	add_property(dev, p->plane_id, "CRTC_ID", p->crtc_id);
+	add_property(dev, p->plane_id, "SRC_X", 0);
+	add_property(dev, p->plane_id, "SRC_Y", 0);
+	add_property(dev, p->plane_id, "SRC_W", p->w << 16);
+	add_property(dev, p->plane_id, "SRC_H", p->h << 16);
+	add_property(dev, p->plane_id, "CRTC_X", crtc_x);
+	add_property(dev, p->plane_id, "CRTC_Y", crtc_y);
+	add_property(dev, p->plane_id, "CRTC_W", crtc_w);
+	add_property(dev, p->plane_id, "CRTC_H", crtc_h);
+
+	return 0;
+}
+
 static int set_plane(struct device *dev, struct plane_arg *p)
 {
 	drmModePlane *ovr;
@@ -1145,6 +1242,64 @@ static int set_plane(struct device *dev, struct plane_arg *p)
 	return 0;
 }
 
+static void atomic_set_planes(struct device *dev, struct plane_arg *p,
+			      unsigned int count, bool update)
+{
+	unsigned int i, pattern = UTIL_PATTERN_SMPTE;
+
+	/* set up planes */
+	for (i = 0; i < count; i++) {
+		if (i > 0)
+			pattern = UTIL_PATTERN_TILES;
+
+		if (atomic_set_plane(dev, &p[i], pattern, update))
+			return;
+	}
+}
+
+static void atomic_clear_planes(struct device *dev, struct plane_arg *p, unsigned int count)
+{
+	unsigned int i;
+
+	for (i = 0; i < count; i++) {
+		add_property(dev, p[i].plane_id, "FB_ID", 0);
+		add_property(dev, p[i].plane_id, "CRTC_ID", 0);
+		add_property(dev, p[i].plane_id, "SRC_X", 0);
+		add_property(dev, p[i].plane_id, "SRC_Y", 0);
+		add_property(dev, p[i].plane_id, "SRC_W", 0);
+		add_property(dev, p[i].plane_id, "SRC_H", 0);
+		add_property(dev, p[i].plane_id, "CRTC_X", 0);
+		add_property(dev, p[i].plane_id, "CRTC_Y", 0);
+		add_property(dev, p[i].plane_id, "CRTC_W", 0);
+		add_property(dev, p[i].plane_id, "CRTC_H", 0);
+	}
+}
+
+static void atomic_clear_FB(struct device *dev, struct plane_arg *p, unsigned int count)
+{
+	unsigned int i;
+
+	for (i = 0; i < count; i++) {
+		if (p[i].fb_id) {
+			drmModeRmFB(dev->fd, p[i].fb_id);
+			p[i].fb_id = 0;
+		}
+		if (p[i].old_fb_id) {
+			drmModeRmFB(dev->fd, p[i].old_fb_id);
+			p[i].old_fb_id = 0;
+		}
+		if (p[i].bo) {
+			bo_destroy(p[i].bo);
+			p[i].bo = NULL;
+		}
+		if (p[i].old_bo) {
+			bo_destroy(p[i].old_bo);
+			p[i].old_bo = NULL;
+		}
+
+	}
+}
+
 static void clear_planes(struct device *dev, struct plane_arg *p, unsigned int count)
 {
 	unsigned int i;
@@ -1157,6 +1312,59 @@ static void clear_planes(struct device *dev, struct plane_arg *p, unsigned int c
 	}
 }
 
+static void atomic_set_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
+{
+	unsigned int i;
+	unsigned int j;
+	int ret;
+
+	for (i = 0; i < count; i++) {
+		struct pipe_arg *pipe = &pipes[i];
+
+		ret = pipe_find_crtc_and_mode(dev, pipe);
+		if (ret < 0)
+			continue;
+	}
+
+	for (i = 0; i < count; i++) {
+		struct pipe_arg *pipe = &pipes[i];
+		uint32_t blob_id;
+
+		if (pipe->mode == NULL)
+			continue;
+
+		printf("setting mode %s-%dHz@%s on connectors ",
+		       pipe->mode_str, pipe->mode->vrefresh, pipe->format_str);
+		for (j = 0; j < pipe->num_cons; ++j) {
+			printf("%s, ", pipe->cons[j]);
+			add_property(dev, pipe->con_ids[j], "CRTC_ID", pipe->crtc->crtc->crtc_id);
+		}
+		printf("crtc %d\n", pipe->crtc->crtc->crtc_id);
+
+		drmModeCreatePropertyBlob(dev->fd, pipe->mode, sizeof(*pipe->mode), &blob_id);
+		add_property(dev, pipe->crtc->crtc->crtc_id, "MODE_ID", blob_id);
+		add_property(dev, pipe->crtc->crtc->crtc_id, "ACTIVE", 1);
+	}
+}
+
+static void atomic_clear_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
+{
+	unsigned int i;
+	unsigned int j;
+
+	for (i = 0; i < count; i++) {
+		struct pipe_arg *pipe = &pipes[i];
+
+		if (pipe->mode == NULL)
+			continue;
+
+		for (j = 0; j < pipe->num_cons; ++j)
+			add_property(dev, pipe->con_ids[j], "CRTC_ID",0);
+
+		add_property(dev, pipe->crtc->crtc->crtc_id, "MODE_ID", 0);
+		add_property(dev, pipe->crtc->crtc->crtc_id, "ACTIVE", 0);
+	}
+}
 
 static void set_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
 {
@@ -1532,7 +1740,7 @@ static int parse_property(struct property_arg *p, const char *arg)
 
 static void usage(char *name)
 {
-	fprintf(stderr, "usage: %s [-cDdefMPpsCvw]\n", name);
+	fprintf(stderr, "usage: %s [-acDdefMPpsCvw]\n", name);
 
 	fprintf(stderr, "\n Query options:\n\n");
 	fprintf(stderr, "\t-c\tlist connectors\n");
@@ -1546,6 +1754,7 @@ static void usage(char *name)
 	fprintf(stderr, "\t-C\ttest hw cursor\n");
 	fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
 	fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property\n");
+	fprintf(stderr, "\t-a \tuse atomic API\n");
 
 	fprintf(stderr, "\n Generic options:\n\n");
 	fprintf(stderr, "\t-d\tdrop master after mode set\n");
@@ -1609,7 +1818,7 @@ static int pipe_resolve_connectors(struct device *dev, struct pipe_arg *pipe)
 	return 0;
 }
 
-static char optstr[] = "cdD:efM:P:ps:Cvw:";
+static char optstr[] = "acdD:efM:P:ps:Cvw:";
 
 int main(int argc, char **argv)
 {
@@ -1620,6 +1829,7 @@ int main(int argc, char **argv)
 	int drop_master = 0;
 	int test_vsync = 0;
 	int test_cursor = 0;
+	int use_atomic = 0;
 	char *device = NULL;
 	char *module = NULL;
 	unsigned int i;
@@ -1638,6 +1848,9 @@ int main(int argc, char **argv)
 		args++;
 
 		switch (c) {
+		case 'a':
+			use_atomic = 1;
+			break;
 		case 'c':
 			connectors = 1;
 			break;
@@ -1717,13 +1930,22 @@ int main(int argc, char **argv)
 		}
 	}
 
-	if (!args)
+	if (!args || (args == 1 && use_atomic))
 		encoders = connectors = crtcs = planes = framebuffers = 1;
 
 	dev.fd = util_open(device, module);
 	if (dev.fd < 0)
 		return -1;
 
+	ret = drmSetClientCap(dev.fd, DRM_CLIENT_CAP_ATOMIC, 1);
+	if (ret && use_atomic) {
+		fprintf(stderr, "no atomic modesetting support: %s\n", strerror(errno));
+		drmClose(dev.fd);
+		return -1;
+	}
+
+	dev.use_atomic = use_atomic;
+
 	if (test_vsync && !page_flipping_supported()) {
 		fprintf(stderr, "page flipping not supported by drm.\n");
 		return -1;
@@ -1764,40 +1986,111 @@ int main(int argc, char **argv)
 	for (i = 0; i < prop_count; ++i)
 		set_property(&dev, &prop_args[i]);
 
-	if (count || plane_count) {
-		uint64_t cap = 0;
+	if (dev.use_atomic) {
+		dev.req = drmModeAtomicAlloc();
 
-		ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap);
-		if (ret || cap == 0) {
-			fprintf(stderr, "driver doesn't support the dumb buffer API\n");
-			return 1;
+		if (count && plane_count) {
+			uint64_t cap = 0;
+
+			ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap);
+			if (ret || cap == 0) {
+				fprintf(stderr, "driver doesn't support the dumb buffer API\n");
+				return 1;
+			}
+
+			atomic_set_mode(&dev, pipe_args, count);
+			atomic_set_planes(&dev, plane_args, plane_count, false);
+
+			ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
+			if (ret) {
+				fprintf(stderr, "Atomic Commit failed [1]\n");
+				return 1;
+			}
+
+			gettimeofday(&pipe_args->start, NULL);
+			pipe_args->swap_count = 0;
+
+			while (test_vsync) {
+				drmModeAtomicFree(dev.req);
+				dev.req = drmModeAtomicAlloc();
+				atomic_set_planes(&dev, plane_args, plane_count, true);
+
+				ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
+				if (ret) {
+					fprintf(stderr, "Atomic Commit failed [2]\n");
+					return 1;
+				}
+
+				pipe_args->swap_count++;
+				if (pipe_args->swap_count == 60) {
+					struct timeval end;
+					double t;
+
+					gettimeofday(&end, NULL);
+					t = end.tv_sec + end.tv_usec * 1e-6 -
+				    (pipe_args->start.tv_sec + pipe_args->start.tv_usec * 1e-6);
+					fprintf(stderr, "freq: %.02fHz\n", pipe_args->swap_count / t);
+					pipe_args->swap_count = 0;
+					pipe_args->start = end;
+				}
+			}
+
+			if (drop_master)
+				drmDropMaster(dev.fd);
+
+			getchar();
+
+			drmModeAtomicFree(dev.req);
+			dev.req = drmModeAtomicAlloc();
+
+			atomic_clear_mode(&dev, pipe_args, count);
+			atomic_clear_planes(&dev, plane_args, plane_count);
+			ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
+			if (ret) {
+				fprintf(stderr, "Atomic Commit failed\n");
+				return 1;
+			}
+
+			atomic_clear_FB(&dev, plane_args, plane_count);
 		}
 
-		if (count)
-			set_mode(&dev, pipe_args, count);
+		drmModeAtomicFree(dev.req);
+	} else {
+		if (count || plane_count) {
+			uint64_t cap = 0;
 
-		if (plane_count)
-			set_planes(&dev, plane_args, plane_count);
+			ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap);
+			if (ret || cap == 0) {
+				fprintf(stderr, "driver doesn't support the dumb buffer API\n");
+				return 1;
+			}
+
+			if (count)
+				set_mode(&dev, pipe_args, count);
 
-		if (test_cursor)
-			set_cursors(&dev, pipe_args, count);
+			if (plane_count)
+				set_planes(&dev, plane_args, plane_count);
 
-		if (test_vsync)
-			test_page_flip(&dev, pipe_args, count);
+			if (test_cursor)
+				set_cursors(&dev, pipe_args, count);
 
-		if (drop_master)
-			drmDropMaster(dev.fd);
+			if (test_vsync)
+				test_page_flip(&dev, pipe_args, count);
 
-		getchar();
+			if (drop_master)
+				drmDropMaster(dev.fd);
 
-		if (test_cursor)
-			clear_cursors(&dev);
+			getchar();
 
-		if (plane_count)
-			clear_planes(&dev, plane_args, plane_count);
+			if (test_cursor)
+				clear_cursors(&dev);
 
-		if (count)
-			clear_mode(&dev);
+			if (plane_count)
+				clear_planes(&dev, plane_args, plane_count);
+
+			if (count)
+				clear_mode(&dev);
+		}
 	}
 
 	free_resources(dev.resources);
-- 
2.15.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

             reply	other threads:[~2018-07-25 14:00 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-07-25 14:00 Benjamin Gaignard [this message]
2018-08-06  7:17 ` [PATCH libdrm v3] tests/modetest: Add atomic support Benjamin Gaignard
2018-08-07 13:46 ` Emil Velikov
2018-08-07 13:53   ` Daniel Stone
2018-08-07 14:15     ` Emil Velikov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180725140016.12921-1-benjamin.gaignard@linaro.org \
    --to=benjamin.gaignard@linaro.org \
    --cc=daniels@collabora.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=emil.l.velikov@gmail.com \
    --cc=eric.engestrom@intel.com \
    --cc=philippe.cornu@st.com \
    --cc=robclark@freedesktop.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.