[PATCH] modesetting: Support native primary plane rotation
Chris Wilson
chris at chris-wilson.co.uk
Thu Jul 10 23:41:47 PDT 2014
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 at chris-wilson.co.uk>
Cc: Pekka Paalanen <ppaalanen at gmail.com>
Cc: walter harms <wharms at bfs.de>
Cc: Mark Kettenis <mark.kettenis at xs4all.nl>
---
configure.ac | 2 +-
src/drmmode_display.c | 258 ++++++++++++++++++++++++++++++++++++++++++++------
src/drmmode_display.h | 7 +-
3 files changed, 234 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..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;
--
2.0.1
More information about the dri-devel
mailing list