diff mbox

modesetting: Support native primary plane rotation

Message ID 1405060907-18674-1-git-send-email-chris@chris-wilson.co.uk (mailing list archive)
State New, archived
Headers show

Commit Message

Chris Wilson July 11, 2014, 6:41 a.m. UTC
With the advent of universal drm planes and the introduction of generic
plane properties for rotations, we can query and program the hardware
for native rotation support.

NOTE: this depends upon the next release of libdrm to remove one
opencoded define.

v2: Use enum to determine primary plane, suggested by Pekka Paalanen.
    Use libobj for replacement ffs(), suggested by walter harms
v3: Support combinations of rotations and reflections
    Eliminate use of ffs() and so remove need for libobj
v4: And remove the vestigal HAVE_FFS, spotted by Mark Kettenis

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Pekka Paalanen <ppaalanen@gmail.com>
Cc: walter harms <wharms@bfs.de>
Cc: Mark Kettenis <mark.kettenis@xs4all.nl>
---
 configure.ac          |   2 +-
 src/drmmode_display.c | 258 ++++++++++++++++++++++++++++++++++++++++++++------
 src/drmmode_display.h |   7 +-
 3 files changed, 234 insertions(+), 33 deletions(-)
diff mbox

Patch

diff --git a/configure.ac b/configure.ac
index 1c1a36d..783c243 100644
--- a/configure.ac
+++ b/configure.ac
@@ -74,7 +74,7 @@  AM_CONDITIONAL(HAVE_XEXTPROTO_71, [ test "$HAVE_XEXTPROTO_71" = "yes" ])
 # Checks for header files.
 AC_HEADER_STDC
 
-PKG_CHECK_MODULES(DRM, [libdrm >= 2.4.46])
+PKG_CHECK_MODULES(DRM, [libdrm >= 2.4.47])
 PKG_CHECK_MODULES([PCIACCESS], [pciaccess >= 0.10])
 AM_CONDITIONAL(DRM, test "x$DRM" = xyes)
 
diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index c533324..93c48ac 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -56,6 +56,8 @@ 
 
 #include "driver.h"
 
+#define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2 /* from libdrm 2.4.55 */
+
 static struct dumb_bo *dumb_bo_create(int fd,
 			  const unsigned width, const unsigned height,
 			  const unsigned bpp)
@@ -300,6 +302,171 @@  create_pixmap_for_fbcon(drmmode_ptr drmmode,
 
 #endif
 
+static void
+rotation_init(xf86CrtcPtr crtc)
+{
+	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+	drmmode_ptr drmmode = drmmode_crtc->drmmode;
+	drmModePlaneRes *plane_resources;
+	int i, j, k;
+
+	drmSetClientCap(drmmode->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+
+	plane_resources = drmModeGetPlaneResources(drmmode->fd);
+	if (plane_resources == NULL)
+		return;
+
+	for (i = 0; drmmode_crtc->primary_plane_id == 0 && i < plane_resources->count_planes; i++) {
+		drmModePlane *drm_plane;
+		drmModeObjectPropertiesPtr proplist;
+		int is_primary = -1;
+
+		drm_plane = drmModeGetPlane(drmmode->fd,
+					    plane_resources->planes[i]);
+		if (drm_plane == NULL)
+			continue;
+
+		if (!(drm_plane->possible_crtcs & (1 << drmmode_crtc->index)))
+			goto free_plane;
+
+		proplist = drmModeObjectGetProperties(drmmode->fd,
+						      drm_plane->plane_id,
+						      DRM_MODE_OBJECT_PLANE);
+		if (proplist == NULL)
+			goto free_plane;
+
+		for (j = 0; is_primary == -1 && j < proplist->count_props; j++) {
+			drmModePropertyPtr prop;
+
+			prop = drmModeGetProperty(drmmode->fd, proplist->props[j]);
+			if (!prop)
+				continue;
+
+			if (strcmp(prop->name, "type") == 0) {
+				for (k = 0; k < prop->count_enums; k++) {
+					if (prop->enums[k].value != proplist->prop_values[j])
+						continue;
+
+					is_primary = strcmp(prop->enums[k].name, "Primary") == 0;
+					break;
+				}
+			}
+
+			drmModeFreeProperty(prop);
+		}
+
+		if (is_primary) {
+			drmmode_crtc->primary_plane_id = drm_plane->plane_id;
+
+			for (j = 0; drmmode_crtc->rotation_prop_id == 0 && j < proplist->count_props; j++) {
+				drmModePropertyPtr prop;
+
+				prop = drmModeGetProperty(drmmode->fd, proplist->props[j]);
+				if (!prop)
+					continue;
+
+				if (strcmp(prop->name, "rotation") == 0) {
+					drmmode_crtc->rotation_prop_id = proplist->props[j];
+					drmmode_crtc->current_rotation = proplist->prop_values[j];
+					for (k = 0; k < prop->count_enums; k++) {
+						int rr = -1;
+						if (strcmp(prop->enums[k].name, "rotate-0") == 0)
+							rr = 0; /* RR_Rotate_0 */
+						else if (strcmp(prop->enums[k].name, "rotate-90") == 0)
+							rr = 1; /* RR_Rotate_90 */
+						else if (strcmp(prop->enums[k].name, "rotate-180") == 0)
+							rr = 2; /* RR_Rotate_180 */
+						else if (strcmp(prop->enums[k].name, "rotate-270") == 0)
+							rr = 3; /* RR_Rotate_270 */
+						else if (strcmp(prop->enums[k].name, "reflect-x") == 0)
+							rr = 4; /* RR_Reflect_X */
+						else if (strcmp(prop->enums[k].name, "reflect-y") == 0)
+							rr = 5; /* RR_Reflect_Y */
+						if (rr != -1) {
+							drmmode_crtc->map_rotations[rr] = prop->enums[k].value;
+							drmmode_crtc->supported_rotations |= 1 << rr;
+						}
+					}
+				}
+
+				drmModeFreeProperty(prop);
+			}
+		}
+
+		drmModeFreeObjectProperties(proplist);
+free_plane:
+		drmModeFreePlane(drm_plane);
+	}
+}
+
+static unsigned int
+rotation_to_kernel(xf86CrtcPtr crtc, unsigned rotation)
+{
+	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+	unsigned mapped = 0;
+	int i;
+
+	for (i = 0; rotation; i++) {
+		if (rotation & (1 << i)) {
+			mapped |= 1 << drmmode_crtc->map_rotations[i];
+			rotation &= ~(1 << i);
+		}
+	}
+
+	return mapped;
+}
+
+static Bool
+rotation_set(xf86CrtcPtr crtc, unsigned rotation)
+{
+	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+	drmmode_ptr drmmode = drmmode_crtc->drmmode;
+
+	if (drmmode_crtc->current_rotation == rotation)
+		return TRUE;
+
+	if ((drmmode_crtc->supported_rotations & rotation) != rotation)
+		return FALSE;
+
+	if (drmModeObjectSetProperty(drmmode->fd,
+				     drmmode_crtc->primary_plane_id,
+				     DRM_MODE_OBJECT_PLANE,
+				     drmmode_crtc->rotation_prop_id,
+				     rotation_to_kernel(crtc, rotation)))
+		return FALSE;
+
+	drmmode_crtc->current_rotation = rotation;
+	return TRUE;
+}
+
+static unsigned
+rotation_reduce(xf86CrtcPtr crtc, unsigned rotation)
+{
+	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+	unsigned unsupported_rotations = rotation & ~drmmode_crtc->supported_rotations;
+
+	if (unsupported_rotations == 0)
+		return rotation;
+
+#define RR_Reflect_XY (RR_Reflect_X | RR_Reflect_Y)
+
+	if ((unsupported_rotations & RR_Reflect_XY) == RR_Reflect_XY &&
+	    drmmode_crtc->supported_rotations & RR_Rotate_180) {
+		rotation &= ~RR_Reflect_XY;
+		rotation ^= RR_Rotate_180;
+	}
+
+	if ((unsupported_rotations & RR_Rotate_180) &&
+	    (drmmode_crtc->supported_rotations & RR_Reflect_XY) == RR_Reflect_XY) {
+		rotation ^= RR_Reflect_XY;
+		rotation &= ~RR_Rotate_180;
+	}
+
+#undef RR_Reflect_XY
+
+	return rotation;
+}
+
 static Bool
 drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
 		     Rotation rotation, int x, int y)
@@ -307,13 +474,14 @@  drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
 	ScrnInfoPtr pScrn = crtc->scrn;
 	xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
 	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+	unsigned supported_rotations = drmmode_crtc->supported_rotations;
 	drmmode_ptr drmmode = drmmode_crtc->drmmode;
 	int saved_x, saved_y;
 	Rotation saved_rotation;
 	DisplayModeRec saved_mode;
 	uint32_t *output_ids;
 	int output_count = 0;
-	Bool ret = TRUE;
+	int ret;
 	int i;
 	uint32_t fb_id;
 	drmModeModeInfo kmode;
@@ -324,15 +492,16 @@  drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
 	if (drmmode->fb_id == 0) {
 		ret = drmModeAddFB(drmmode->fd,
 				   pScrn->virtualX, height,
-                                   pScrn->depth, pScrn->bitsPerPixel,
+				   pScrn->depth, pScrn->bitsPerPixel,
 				   drmmode->front_bo->pitch,
 				   drmmode->front_bo->handle,
-                                   &drmmode->fb_id);
-                if (ret < 0) {
-                        ErrorF("failed to add fb %d\n", ret);
-                        return FALSE;
-                }
-        }
+				   &drmmode->fb_id);
+		if (ret) {
+			xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
+				   "failed to create framebuffer: %s\n", strerror(-ret));
+			return FALSE;
+		}
+	}
 
 	saved_mode = crtc->mode;
 	saved_x = crtc->x;
@@ -349,11 +518,11 @@  drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
 #endif
 	}
 
+	rotation = rotation_reduce(crtc, rotation);
+
 	output_ids = calloc(sizeof(uint32_t), xf86_config->num_output);
-	if (!output_ids) {
-		ret = FALSE;
-		goto done;
-	}
+	if (!output_ids)
+		goto err;
 
 	if (mode) {
 		for (i = 0; i < xf86_config->num_output; i++) {
@@ -368,9 +537,17 @@  drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
 			output_count++;
 		}
 
-		if (!xf86CrtcRotate(crtc)) {
-			goto done;
-		}
+		rotation_set(crtc, RR_Rotate_0);
+
+again:
+		crtc->driverIsPerformingTransform = FALSE;
+		if (!crtc->transformPresent &&
+		    (rotation & supported_rotations) == rotation)
+			crtc->driverIsPerformingTransform = TRUE;
+
+		if (!xf86CrtcRotate(crtc))
+			goto err;
+
 #if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,7,0,0,0)
 		crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
 				       crtc->gamma_blue, crtc->gamma_size);
@@ -392,11 +569,20 @@  drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
 		}
 		ret = drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
 				     fb_id, x, y, output_ids, output_count, &kmode);
-		if (ret)
+		if (ret) {
 			xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
-				   "failed to set mode: %s", strerror(-ret));
-		else
-			ret = TRUE;
+				   "failed to set mode: %s\n", strerror(-ret));
+			goto err;
+		}
+
+		if (crtc->driverIsPerformingTransform) {
+			if (!rotation_set(crtc, rotation)) {
+				xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
+					   "failed to set rotation: %s\n", strerror(errno));
+				supported_rotations &= ~rotation;
+				goto again;
+			}
+		}
 
 		if (crtc->scrn->pScreen)
 			xf86CrtcSetScreenSubpixelOrder(crtc->scrn->pScreen);
@@ -409,26 +595,33 @@  drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
 
 			output->funcs->dpms(output, DPMSModeOn);
 		}
+	} else {
+		ret = drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
+				     0, 0, 0, NULL, 0, NULL) == 0;
+		if (ret) {
+			xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
+				   "failed to set mode: %s\n", strerror(-ret));
+			goto err;
+		}
 	}
 
+#if defined(XF86_CRTC_VERSION) && XF86_CRTC_VERSION >= 3
+	crtc->active = mode != NULL;
+#endif
+
 #if 0
 	if (pScrn->pScreen &&
 		!xf86ReturnOptValBool(info->Options, OPTION_SW_CURSOR, FALSE))
 		xf86_reload_cursors(pScrn->pScreen);
 #endif
-done:
-	if (!ret) {
-		crtc->x = saved_x;
-		crtc->y = saved_y;
-		crtc->rotation = saved_rotation;
-		crtc->mode = saved_mode;
-	}
-#if defined(XF86_CRTC_VERSION) && XF86_CRTC_VERSION >= 3
-	else
-		crtc->active = TRUE;
-#endif
+	return TRUE;
 
-	return ret;
+err:
+	crtc->x = saved_x;
+	crtc->y = saved_y;
+	crtc->rotation = saved_rotation;
+	crtc->mode = saved_mode;
+	return FALSE;
 }
 
 static void
@@ -610,7 +803,10 @@  drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num)
 	drmmode_crtc = xnfcalloc(sizeof(drmmode_crtc_private_rec), 1);
 	drmmode_crtc->mode_crtc = drmModeGetCrtc(drmmode->fd, drmmode->mode_res->crtcs[num]);
 	drmmode_crtc->drmmode = drmmode;
+	drmmode_crtc->index = num;
 	crtc->driver_private = drmmode_crtc;
+
+	rotation_init(crtc);
 }
 
 static xf86OutputStatus
diff --git a/src/drmmode_display.h b/src/drmmode_display.h
index 745c484..8eb25fd 100644
--- a/src/drmmode_display.h
+++ b/src/drmmode_display.h
@@ -75,8 +75,13 @@  typedef struct {
 typedef struct {
     drmmode_ptr drmmode;
     drmModeCrtcPtr mode_crtc;
-    int hw_id;
+    int index;
     struct dumb_bo *cursor_bo;
+    unsigned primary_plane_id;
+    unsigned rotation_prop_id;
+    unsigned supported_rotations;
+    unsigned map_rotations[6];
+    unsigned current_rotation;
     unsigned rotate_fb_id;
     uint16_t lut_r[256], lut_g[256], lut_b[256];
     DamagePtr slave_damage;