minigbm: Fix cursor and scanout flags

We were incorrectly advertising the scanout and cursor flags in
the drivers.

Chrome never used the BO_USE_CURSOR flag (see chromium:666488), and we
set it for incorrect formats.  Let's only advertise ARGB8888/XRGB8888
cursors if they were previously advertised, and prevent cursors from being
used as render targets.

For scanout, formats can vary from kernel version to kernel version.
For example, the v3.18 i915 driver can't scanout NV12, but the upstream v4.4
i915 driver can.  Let's query the KMS API in those cases.

In addition, we would also like to move to a place where our backends can
determine if a specific {format, usage, format modifier} tuple is supported.
The plan is to add modifiers to the properties that are exposed in KMS, which
can help with optimization.

BUG=b:31942635, chromium:666488
TEST=Ran graphics_Gbm and checked if Chrome boots on cyan, minnie, and
nyan_big

CQ-DEPEND=CL:413325

Change-Id: Ifd3fd1c8063db97b3f1fe057ace82d22def76943
Reviewed-on: https://chromium-review.googlesource.com/405019
Commit-Ready: Gurchetan Singh <gurchetansingh@chromium.org>
Tested-by: Gurchetan Singh <gurchetansingh@chromium.org>
Reviewed-by: Ilja H. Friedel <ihf@chromium.org>
diff --git a/helpers.c b/helpers.c
index 1e35936..8ad0411 100644
--- a/helpers.c
+++ b/helpers.c
@@ -11,6 +11,7 @@
 #include <string.h>
 #include <sys/mman.h>
 #include <xf86drm.h>
+#include <xf86drmMode.h>
 
 #include "drv_priv.h"
 #include "helpers.h"
@@ -274,3 +275,132 @@
 
 	return ret;
 }
+
+void drv_insert_supported_combination(struct driver *drv, uint32_t format,
+			              uint64_t usage, uint64_t modifier)
+{
+	int found = 0;
+	struct combination_list_element *elem;
+
+	pthread_mutex_lock(&drv->table_lock);
+
+	list_for_each_entry(struct combination_list_element,
+			    elem, &drv->backend->combinations, link) {
+		if (elem->combination.format == format &&
+		    elem->combination.modifier == modifier) {
+			elem->combination.usage |= usage;
+			found = 1;
+		}
+	}
+
+	if (found)
+		goto out;
+
+	elem = calloc(1, sizeof(*elem));
+	elem->combination.format = format;
+	elem->combination.modifier = modifier;
+	elem->combination.usage = usage;
+	LIST_ADD(&elem->link, &drv->backend->combinations);
+
+out:
+	pthread_mutex_unlock(&drv->table_lock);
+}
+
+void drv_insert_combinations(struct driver *drv, struct supported_combination *combos,
+			     uint32_t size)
+{
+	unsigned int i;
+	for (i = 0; i < size; i++)
+		drv_insert_supported_combination(drv, combos[i].format,
+						 combos[i].usage,
+						 combos[i].modifier);
+}
+
+int drv_add_kms_flags(struct driver *drv)
+{
+	int ret;
+	uint32_t i, j;
+	uint64_t flag, usage;
+	drmModePlanePtr plane;
+	drmModePropertyPtr prop;
+	drmModePlaneResPtr resources;
+	drmModeObjectPropertiesPtr props;
+
+	/*
+	 * All current drivers can scanout XRGB8888/ARGB8888 as a primary plane.
+	 * Some older kernel versions can only return overlay planes, so add the
+	 * combination here. Note that the kernel disregards the alpha component
+	 * of ARGB unless it's an overlay plane.
+	 */
+	drv_insert_supported_combination(drv, DRM_FORMAT_XRGB8888,
+					 DRV_BO_USE_SCANOUT,
+					 0);
+	drv_insert_supported_combination(drv, DRM_FORMAT_ARGB8888,
+					 DRV_BO_USE_SCANOUT,
+					 0);
+
+	/*
+	 * The ability to return universal planes is only complete on
+	 * ChromeOS kernel versions >= v3.18.  The SET_CLIENT_CAP ioctl
+	 * therefore might return an error code, so don't check it.  If it
+	 * fails, it'll just return the plane list as overlay planes, which is
+	 * fine in our case (our drivers already have cursor bits set).
+	 * modetest in libdrm does the same thing.
+	 */
+	drmSetClientCap(drv->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+
+	resources = drmModeGetPlaneResources(drv->fd);
+	if (!resources)
+		goto err;
+
+	for (i = 0; i < resources->count_planes; i++) {
+
+		plane = drmModeGetPlane(drv->fd, resources->planes[i]);
+
+		if (!plane)
+			goto err;
+
+		props = drmModeObjectGetProperties(drv->fd, plane->plane_id,
+						   DRM_MODE_OBJECT_PLANE);
+		if (!props)
+			goto err;
+
+		for (j = 0; j < props->count_props; j++) {
+
+			prop = drmModeGetProperty(drv->fd, props->props[j]);
+			if (prop) {
+				if (strcmp(prop->name, "type") == 0) {
+					flag = props->prop_values[j];
+				}
+				drmModeFreeProperty(prop);
+			}
+		}
+
+		switch (flag) {
+		case DRM_PLANE_TYPE_OVERLAY:
+		case DRM_PLANE_TYPE_PRIMARY:
+			usage = DRV_BO_USE_SCANOUT;
+			break;
+		case DRM_PLANE_TYPE_CURSOR:
+			usage = DRV_BO_USE_CURSOR;
+			break;
+		default:
+			assert(0);
+		}
+
+		for (j = 0; j < plane->count_formats; j++)
+			drv_insert_supported_combination(drv, plane->formats[j],
+							 usage, 0);
+
+		drmModeFreeObjectProperties(props);
+		drmModeFreePlane(plane);
+
+	}
+
+	drmModeFreePlaneResources(resources);
+	return 0;
+
+err:
+	ret = -1;
+	return ret;
+}