drm-tests: add support for EGL_EXT_platform_device to null_platform_test and bsdrm.

Add two new command line options to select display device and renderer device.
Modify bsdrm to query list of devices from egl and initialize egl on requested
device.

BUG=chromium:970850
TEST=run null_platform_test -d i915 -r amdgpu -m DRM_FORMAT_MOD_LINEAR

Change-Id: I8ce5b089f396b098351aedc53353f24f856c6dac
Signed-off-by: Dominik Behr <dbehr@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/drm-tests/+/1641631
Reviewed-by: Zach Reizner <zachr@chromium.org>
diff --git a/bsdrm/include/bs_drm.h b/bsdrm/include/bs_drm.h
index 255e9dc..1b16eb9 100644
--- a/bsdrm/include/bs_drm.h
+++ b/bsdrm/include/bs_drm.h
@@ -8,9 +8,15 @@
 #define __BS_DRM_H__
 
 #define _GNU_SOURCE
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
 #include <assert.h>
+#include <drm_fourcc.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <gbm.h>
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stddef.h>
@@ -21,13 +27,6 @@
 #include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <drm_fourcc.h>
-#include <gbm.h>
 #include <xf86drm.h>
 #include <xf86drmMode.h>
 
@@ -97,7 +96,7 @@
 
 // drm_pipe.c
 struct bs_drm_pipe {
-	int fd;  // Always owned by the user of this library
+	int fd;	 // Always owned by the user of this library
 	uint32_t connector_id;
 	uint32_t encoder_id;
 	uint32_t crtc_id;
@@ -156,7 +155,10 @@
 
 struct bs_egl *bs_egl_new();
 void bs_egl_destroy(struct bs_egl **egl);
-bool bs_egl_setup(struct bs_egl *self);
+// initialize opengl.
+// use null platform when render_driver is null.
+// render_driver is drm driver name for GL driver to use
+bool bs_egl_setup(struct bs_egl *self, const char *render_driver);
 bool bs_egl_make_current(struct bs_egl *self);
 
 EGLImageKHR bs_egl_image_create_gbm(struct bs_egl *self, struct gbm_bo *bo);
diff --git a/bsdrm/src/egl.c b/bsdrm/src/egl.c
index 46e204d..d4b4228 100644
--- a/bsdrm/src/egl.c
+++ b/bsdrm/src/egl.c
@@ -15,6 +15,7 @@
 	EGLContext ctx;
 	bool use_image_flush_external;
 	bool use_dma_buf_import_modifiers;
+	bool has_platform_device;
 
 	// Names are the original gl/egl function names with the prefix chopped off.
 	PFNEGLCREATEIMAGEKHRPROC CreateImageKHR;
@@ -24,6 +25,9 @@
 	PFNEGLCREATESYNCKHRPROC CreateSyncKHR;
 	PFNEGLCLIENTWAITSYNCKHRPROC ClientWaitSyncKHR;
 	PFNEGLDESTROYSYNCKHRPROC DestroySyncKHR;
+	PFNEGLQUERYDEVICESEXTPROC QueryDevicesEXT;
+	PFNEGLQUERYDEVICESTRINGEXTPROC QueryDeviceStringEXT;
+	PFNEGLGETPLATFORMDISPLAYEXTPROC GetPlatformDisplayEXT;
 };
 
 struct bs_egl_fb {
@@ -59,10 +63,35 @@
 	*egl = NULL;
 }
 
-bool bs_egl_setup(struct bs_egl *self)
+static int bs_check_drm_driver(const char *device_file_name, const char *driver)
+{
+	drmVersionPtr version;
+	int fd = open(device_file_name, O_RDWR);
+	if (fd < 0)
+		return -1;
+
+	version = drmGetVersion(fd);
+	if (!version) {
+		close(fd);
+		return -1;
+	}
+
+	if (strcmp(driver, version->name) == 0) {
+		drmFreeVersion(version);
+		close(fd);
+		return 1;
+	}
+
+	drmFreeVersion(version);
+	close(fd);
+	return 0;
+}
+
+bool bs_egl_setup(struct bs_egl *self, const char *render_driver)
 {
 	assert(self);
 	assert(!self->setup);
+	const char *egl_client_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
 
 	self->CreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR");
 	self->DestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress("eglDestroyImageKHR");
@@ -81,7 +110,57 @@
 	    (PFNEGLCLIENTWAITSYNCKHRPROC)eglGetProcAddress("eglClientWaitSyncKHR");
 	self->DestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC)eglGetProcAddress("eglDestroySyncKHR");
 
-	self->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+	if (egl_client_extensions &&
+	    bs_egl_has_extension("EGL_EXT_device_base", egl_client_extensions) &&
+	    bs_egl_has_extension("EGL_EXT_device_enumeration", egl_client_extensions) &&
+	    bs_egl_has_extension("EGL_EXT_device_query", egl_client_extensions) &&
+	    bs_egl_has_extension("EGL_EXT_platform_base", egl_client_extensions) &&
+	    bs_egl_has_extension("EGL_EXT_platform_device", egl_client_extensions)) {
+		self->QueryDevicesEXT =
+		    (PFNEGLQUERYDEVICESEXTPROC)eglGetProcAddress("eglQueryDevicesEXT");
+		self->QueryDeviceStringEXT =
+		    (PFNEGLQUERYDEVICESTRINGEXTPROC)eglGetProcAddress("eglQueryDeviceStringEXT");
+		self->GetPlatformDisplayEXT =
+		    (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
+		self->has_platform_device = true;
+	} else {
+		self->has_platform_device = false;
+	}
+
+	if (render_driver && !self->has_platform_device) {
+		bs_debug_error("requested driver %s but required extensions are missing",
+			       render_driver);
+		return false;
+	}
+
+	self->display = EGL_NO_DISPLAY;
+
+	if (!self->has_platform_device || !render_driver) {
+		self->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+	} else {
+		EGLDeviceEXT devices[DRM_MAX_MINOR];
+		EGLint num_devices = 0;
+		EGLint d;
+
+		self->QueryDevicesEXT(DRM_MAX_MINOR, devices, &num_devices);
+		for (d = 0; d < num_devices; d++) {
+			const char *fn =
+			    self->QueryDeviceStringEXT(devices[d], EGL_DRM_DEVICE_FILE_EXT);
+			// We could query if display has EGL_EXT_device_drm. Or we can be lazy and
+			// just query the device string and ignore the devices where it fails.
+			if (!fn) {
+				continue;
+			}
+			if (bs_check_drm_driver(fn, render_driver) == 1) {
+				self->display = self->GetPlatformDisplayEXT(
+				    EGL_PLATFORM_DEVICE_EXT, (void *)devices[d], NULL);
+				break;
+			}
+		}
+		if (d == num_devices)
+			bs_debug_error("failed to find requested EGL driver %s.", render_driver);
+	}
+
 	if (self->display == EGL_NO_DISPLAY) {
 		bs_debug_error("failed to get egl display");
 		return false;
diff --git a/linear_bo_test.c b/linear_bo_test.c
index 8b3dcc4..6521de6 100644
--- a/linear_bo_test.c
+++ b/linear_bo_test.c
@@ -303,7 +303,7 @@
 	uint32_t height = mode->vdisplay;
 
 	struct bs_egl *egl = bs_egl_new();
-	if (!bs_egl_setup(egl)) {
+	if (!bs_egl_setup(egl, NULL)) {
 		bs_debug_error("failed to setup egl context");
 		return 1;
 	}
diff --git a/mapped_texture_test.c b/mapped_texture_test.c
index e1cbac3..25a7ffa 100644
--- a/mapped_texture_test.c
+++ b/mapped_texture_test.c
@@ -314,7 +314,7 @@
 	height = mode->vdisplay;
 
 	struct bs_egl *egl = bs_egl_new();
-	if (!bs_egl_setup(egl)) {
+	if (!bs_egl_setup(egl, NULL)) {
 		bs_debug_error("failed to setup egl context");
 		goto destroy_gbm_device;
 	}
diff --git a/null_platform_test.c b/null_platform_test.c
index da7fe73..175ce82 100644
--- a/null_platform_test.c
+++ b/null_platform_test.c
@@ -31,12 +31,15 @@
 	GBM_FORMAT_ABGR2101010,
 };
 
+
 static const struct option longopts[] = {
 	{ "format", required_argument, NULL, 'f' },
 	{ "modifier", required_argument, NULL, 'm' },
 	{ "test-page-flip-format-change", required_argument, NULL, 'p' },
 	{ "banding", no_argument, NULL, 'b' },
 	{ "help", no_argument, NULL, 'h' },
+	{ "display", required_argument, NULL, 'd' },
+	{ "renderer", required_argument, NULL, 'r' },
 	{ 0, 0, 0, 0 },
 };
 
@@ -121,6 +124,8 @@
 	printf("  -p, --test-page-flip-format-change <format>  test page flips alternating formats.\n");
 	printf("  -b, --banding                                show a pattern that makes banding easy to spot if present.\n");
 	printf("  -h, --help                                   show help\n");
+	printf("  -d, --display <drm_driver_name>              dri driver name for display.\n");
+	printf("  -r, --renderer <drm_driver_name>             dri driver name for renderer.\n");
 	printf("\n");
 	printf(" <format> must be one of [ %s ].\n", allowed_formats_string);
 	printf(" <modifier> must be one of ");
@@ -350,6 +355,30 @@
 	return true;
 }
 
+struct check_driver_data {
+	char *driver;
+	int fd;
+};
+
+static bool check_driver(void *user, int fd)
+{
+	struct check_driver_data *data = (struct check_driver_data *)user;
+	drmVersionPtr version;
+
+	version = drmGetVersion(fd);
+	if (!version)
+		return false;
+
+	if (strcmp(data->driver, version->name) != 0) {
+		drmFreeVersion(version);
+		return false;
+	}
+
+	drmFreeVersion(version);
+	data->fd = dup(fd);
+	return true;
+}
+
 int main(int argc, char **argv)
 {
 	bool help_flag = false;
@@ -357,9 +386,11 @@
 	uint32_t test_page_flip_format_change = 0;
 	uint64_t modifier = DRM_FORMAT_MOD_INVALID;
 	bool banding_pattern = false;
+	char *display_driver = NULL;
+	char *render_driver = NULL;
 
 	int c = -1;
-	while ((c = getopt_long(argc, argv, "m:hp:f:bl", longopts, NULL)) != -1) {
+	while ((c = getopt_long(argc, argv, "m:hp:f:bld:r:", longopts, NULL)) != -1) {
 		switch (c) {
 			case 'p':
 				test_page_flip_format_change = find_format(optarg);
@@ -386,6 +417,20 @@
 			case 'h':
 				help_flag = true;
 				break;
+			case 'd':
+				if (display_driver) {
+					free(display_driver);
+					display_driver = NULL;
+				}
+				display_driver = strdup(optarg);
+				break;
+			case 'r':
+				if (render_driver) {
+					free(render_driver);
+					render_driver = NULL;
+				}
+				render_driver = strdup(optarg);
+				break;
 		}
 	}
 
@@ -397,7 +442,16 @@
 			return 1;
 		}
 	} else {
-		drm_device_fd = bs_drm_open_main_display();
+		if (display_driver) {
+			struct check_driver_data data;
+			data.driver = display_driver;
+			data.fd = -1;
+			bs_open_enumerate("/dev/dri/card%u", 0, DRM_MAX_MINOR,
+					  check_driver, (void *)&data);
+			drm_device_fd = data.fd;
+		} else {
+			drm_device_fd = bs_drm_open_main_display();
+		}
 		if (drm_device_fd < 0) {
 			bs_debug_error("failed to open card for display");
 			return 1;
@@ -426,7 +480,7 @@
 	drmModeModeInfo *mode = &connector->modes[0];
 
 	egl = bs_egl_new();
-	if (!bs_egl_setup(egl)) {
+	if (!bs_egl_setup(egl, render_driver)) {
 		bs_debug_error("failed to setup egl context");
 		return 1;
 	}