diff mbox

modesetting: Support native primary plane rotation

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

Commit Message

Chris Wilson July 9, 2014, 8:19 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

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Pekka Paalanen <ppaalanen@gmail.com>
Cc: walter harms <wharms@bfs.de>
---
 configure.ac          |   5 +-
 libobj/ffs.c          |  14 ++++
 src/drmmode_display.c | 216 ++++++++++++++++++++++++++++++++++++++++++--------
 src/drmmode_display.h |  10 ++-
 4 files changed, 212 insertions(+), 33 deletions(-)
 create mode 100644 libobj/ffs.c

Comments

Pekka Paalanen July 9, 2014, 9:57 a.m. UTC | #1
On Wed,  9 Jul 2014 09:19:08 +0100
Chris Wilson <chris@chris-wilson.co.uk> wrote:

> 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
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Pekka Paalanen <ppaalanen@gmail.com>
> Cc: walter harms <wharms@bfs.de>

My concerns have been addressed. On a second read, I found another
suspicious thing below.

> ---
>  configure.ac          |   5 +-
>  libobj/ffs.c          |  14 ++++
>  src/drmmode_display.c | 216 ++++++++++++++++++++++++++++++++++++++++++--------
>  src/drmmode_display.h |  10 ++-
>  4 files changed, 212 insertions(+), 33 deletions(-)
>  create mode 100644 libobj/ffs.c
> 
> diff --git a/configure.ac b/configure.ac
> index 1c1a36d..1694465 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -74,10 +74,13 @@ 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)
>  
> +AC_CONFIG_LIBOBJ_DIR(libobj)
> +AC_REPLACE_FUNCS(ffs)
> +
>  PKG_CHECK_MODULES(UDEV, [libudev], [udev=yes], [udev=no])
>  if test x"$udev" = xyes; then
>          AC_DEFINE(HAVE_UDEV,1,[Enable udev-based monitor hotplug detection])
> diff --git a/libobj/ffs.c b/libobj/ffs.c
> new file mode 100644
> index 0000000..2d44dcc
> --- /dev/null
> +++ b/libobj/ffs.c
> @@ -0,0 +1,14 @@
> +extern int ffs(unsigned int value);
> +
> +int ffs(unsigned int value)
> +{
> +	int bit;
> +
> +	if (value == 0)
> +		return 0;
> +
> +	bit = 0;
> +	while ((value & (1 << bit++)) == 0)
> +		;
> +	return bit;
> +}
> diff --git a/src/drmmode_display.c b/src/drmmode_display.c
> index c533324..e854502 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,132 @@ create_pixmap_for_fbcon(drmmode_ptr drmmode,
>  
>  #endif
>  
> +static unsigned
> +rotation_index(unsigned rotation)
> +{
> +	return ffs(rotation) - 1;
> +}
> +
> +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 = RR_Rotate_0;
> +						else if (strcmp(prop->enums[k].name, "rotate-90") == 0)
> +							rr = RR_Rotate_90;
> +						else if (strcmp(prop->enums[k].name, "rotate-180") == 0)
> +							rr = RR_Rotate_180;
> +						else if (strcmp(prop->enums[k].name, "rotate-270") == 0)
> +							rr = RR_Rotate_270;
> +						else if (strcmp(prop->enums[k].name, "reflect-x") == 0)
> +							rr = RR_Reflect_X;
> +						else if (strcmp(prop->enums[k].name, "reflect-y") == 0)
> +							rr = RR_Reflect_Y;
> +						if (rr != -1) {
> +							drmmode_crtc->map_rotations[rotation_index(rr)] = 1 << prop->enums[k].value;
> +							drmmode_crtc->supported_rotations |= rr;

Comparing the above assignments to...

> +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) == 0)
> +		return FALSE;
> +
> +	if (drmModeObjectSetProperty(drmmode->fd,
> +				     drmmode_crtc->primary_plane_id,
> +				     DRM_MODE_OBJECT_PLANE,
> +				     drmmode_crtc->rotation_prop_id,
> +				     drmmode_crtc->map_rotations[rotation_index(rotation)]))

...the use here, it does not look like you are passing
prop->enums[k].value here. It is like you are missing ffs() here or
having a 1<< too much in the assignment.

Btw. would it be possible to do e.g. rotate-90 with reflect?


Thanks,
pq
Chris Wilson July 9, 2014, 10:02 a.m. UTC | #2
On Wed, Jul 09, 2014 at 12:57:12PM +0300, Pekka Paalanen wrote:
> On Wed,  9 Jul 2014 09:19:08 +0100
> Chris Wilson <chris@chris-wilson.co.uk> wrote:
> 
> > 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
> > 
> > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> > Cc: Pekka Paalanen <ppaalanen@gmail.com>
> > Cc: walter harms <wharms@bfs.de>
> 
> My concerns have been addressed. On a second read, I found another
> suspicious thing below.
> 
> > +				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 = RR_Rotate_0;
> > +						else if (strcmp(prop->enums[k].name, "rotate-90") == 0)
> > +							rr = RR_Rotate_90;
> > +						else if (strcmp(prop->enums[k].name, "rotate-180") == 0)
> > +							rr = RR_Rotate_180;
> > +						else if (strcmp(prop->enums[k].name, "rotate-270") == 0)
> > +							rr = RR_Rotate_270;
> > +						else if (strcmp(prop->enums[k].name, "reflect-x") == 0)
> > +							rr = RR_Reflect_X;
> > +						else if (strcmp(prop->enums[k].name, "reflect-y") == 0)
> > +							rr = RR_Reflect_Y;
> > +						if (rr != -1) {
> > +							drmmode_crtc->map_rotations[rotation_index(rr)] = 1 << prop->enums[k].value;
> > +							drmmode_crtc->supported_rotations |= rr;
> 
> Comparing the above assignments to...
> 
> > +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) == 0)
> > +		return FALSE;
> > +
> > +	if (drmModeObjectSetProperty(drmmode->fd,
> > +				     drmmode_crtc->primary_plane_id,
> > +				     DRM_MODE_OBJECT_PLANE,
> > +				     drmmode_crtc->rotation_prop_id,
> > +				     drmmode_crtc->map_rotations[rotation_index(rotation)]))
> 
> ...the use here, it does not look like you are passing
> prop->enums[k].value here. It is like you are missing ffs() here or
> having a 1<< too much in the assignment.

It doesn't take the enum.value but 1 << enum.value.
 
> Btw. would it be possible to do e.g. rotate-90 with reflect?

Indeed. That's an issue from only using the first index...
-Chris
Pekka Paalanen July 10, 2014, 6:09 p.m. UTC | #3
On Wed, 9 Jul 2014 11:02:59 +0100
Chris Wilson <chris@chris-wilson.co.uk> wrote:

> On Wed, Jul 09, 2014 at 12:57:12PM +0300, Pekka Paalanen wrote:
> > On Wed,  9 Jul 2014 09:19:08 +0100
> > Chris Wilson <chris@chris-wilson.co.uk> wrote:
> > 
> > > 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
> > > 
> > > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> > > Cc: Pekka Paalanen <ppaalanen@gmail.com>
> > > Cc: walter harms <wharms@bfs.de>
> > 
> > My concerns have been addressed. On a second read, I found another
> > suspicious thing below.
> > 
> > > +				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 = RR_Rotate_0;
> > > +						else if (strcmp(prop->enums[k].name, "rotate-90") == 0)
> > > +							rr = RR_Rotate_90;
> > > +						else if (strcmp(prop->enums[k].name, "rotate-180") == 0)
> > > +							rr = RR_Rotate_180;
> > > +						else if (strcmp(prop->enums[k].name, "rotate-270") == 0)
> > > +							rr = RR_Rotate_270;
> > > +						else if (strcmp(prop->enums[k].name, "reflect-x") == 0)
> > > +							rr = RR_Reflect_X;
> > > +						else if (strcmp(prop->enums[k].name, "reflect-y") == 0)
> > > +							rr = RR_Reflect_Y;
> > > +						if (rr != -1) {
> > > +							drmmode_crtc->map_rotations[rotation_index(rr)] = 1 << prop->enums[k].value;
> > > +							drmmode_crtc->supported_rotations |= rr;
> > 
> > Comparing the above assignments to...
> > 
> > > +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) == 0)
> > > +		return FALSE;
> > > +
> > > +	if (drmModeObjectSetProperty(drmmode->fd,
> > > +				     drmmode_crtc->primary_plane_id,
> > > +				     DRM_MODE_OBJECT_PLANE,
> > > +				     drmmode_crtc->rotation_prop_id,
> > > +				     drmmode_crtc->map_rotations[rotation_index(rotation)]))
> > 
> > ...the use here, it does not look like you are passing
> > prop->enums[k].value here. It is like you are missing ffs() here or
> > having a 1<< too much in the assignment.
> 
> It doesn't take the enum.value but 1 << enum.value.

Aah, it is not an 'enum', it is a 'bitmask'! Okay, I see it now, I
think.


Thanks,
pq
diff mbox

Patch

diff --git a/configure.ac b/configure.ac
index 1c1a36d..1694465 100644
--- a/configure.ac
+++ b/configure.ac
@@ -74,10 +74,13 @@  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)
 
+AC_CONFIG_LIBOBJ_DIR(libobj)
+AC_REPLACE_FUNCS(ffs)
+
 PKG_CHECK_MODULES(UDEV, [libudev], [udev=yes], [udev=no])
 if test x"$udev" = xyes; then
         AC_DEFINE(HAVE_UDEV,1,[Enable udev-based monitor hotplug detection])
diff --git a/libobj/ffs.c b/libobj/ffs.c
new file mode 100644
index 0000000..2d44dcc
--- /dev/null
+++ b/libobj/ffs.c
@@ -0,0 +1,14 @@ 
+extern int ffs(unsigned int value);
+
+int ffs(unsigned int value)
+{
+	int bit;
+
+	if (value == 0)
+		return 0;
+
+	bit = 0;
+	while ((value & (1 << bit++)) == 0)
+		;
+	return bit;
+}
diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index c533324..e854502 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,132 @@  create_pixmap_for_fbcon(drmmode_ptr drmmode,
 
 #endif
 
+static unsigned
+rotation_index(unsigned rotation)
+{
+	return ffs(rotation) - 1;
+}
+
+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 = RR_Rotate_0;
+						else if (strcmp(prop->enums[k].name, "rotate-90") == 0)
+							rr = RR_Rotate_90;
+						else if (strcmp(prop->enums[k].name, "rotate-180") == 0)
+							rr = RR_Rotate_180;
+						else if (strcmp(prop->enums[k].name, "rotate-270") == 0)
+							rr = RR_Rotate_270;
+						else if (strcmp(prop->enums[k].name, "reflect-x") == 0)
+							rr = RR_Reflect_X;
+						else if (strcmp(prop->enums[k].name, "reflect-y") == 0)
+							rr = RR_Reflect_Y;
+						if (rr != -1) {
+							drmmode_crtc->map_rotations[rotation_index(rr)] = 1 << prop->enums[k].value;
+							drmmode_crtc->supported_rotations |= rr;
+						}
+					}
+				}
+
+				drmModeFreeProperty(prop);
+			}
+		}
+
+		drmModeFreeObjectProperties(proplist);
+free_plane:
+		drmModeFreePlane(drm_plane);
+	}
+}
+
+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) == 0)
+		return FALSE;
+
+	if (drmModeObjectSetProperty(drmmode->fd,
+				     drmmode_crtc->primary_plane_id,
+				     DRM_MODE_OBJECT_PLANE,
+				     drmmode_crtc->rotation_prop_id,
+				     drmmode_crtc->map_rotations[rotation_index(rotation)]))
+		return FALSE;
+
+	drmmode_crtc->current_rotation = rotation;
+	return TRUE;
+}
+
 static Bool
 drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
 		     Rotation rotation, int x, int y)
@@ -307,13 +435,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 +453,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;
@@ -350,10 +480,8 @@  drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
 	}
 
 	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 +496,16 @@  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 (rotation & supported_rotations && !crtc->transformPresent)
+			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 +527,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, crtc->rotation)) {
+				xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
+					   "failed to set rotation: %s\n", strerror(errno));
+				supported_rotations &= ~crtc->rotation;
+				goto again;
+			}
+		}
 
 		if (crtc->scrn->pScreen)
 			xf86CrtcSetScreenSubpixelOrder(crtc->scrn->pScreen);
@@ -409,26 +553,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 +761,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..73fabfd 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;
@@ -145,5 +150,8 @@  void drmmode_get_default_bpp(ScrnInfoPtr pScrn, drmmode_ptr drmmmode, int *depth
 
 #define MS_ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
 
+#ifndef HAVE_FFS
+extern int ffs(unsigned int value);
+#endif
 
 #endif