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

Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
Cc: Pekka Paalanen <ppaalanen at gmail.com>
Cc: walter harms <wharms at bfs.de>
---
 configure.ac          |   2 +-
 src/drmmode_display.c | 258 ++++++++++++++++++++++++++++++++++++++++++++------
 src/drmmode_display.h |  10 +-
 3 files changed, 237 insertions(+), 33 deletions(-)

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..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
-- 
2.0.1

Reply via email to