drmmode: use one cursor per crtc
This is a pretty beefy patch, that completely refactors how the cursor
planes are chosen, initialized, and assigned to a crtc.
This patch paves the way for improved drm plane support in the exynos drm
kernel driver by adding format and possible_crtc checking and assigning
one cursor/plane per crtc.
Essentially nothing changes though.
The only side effect will be a subtle fix for the observation in
crosbug.com/p/26514#16, that if the X server was closed while the internal
crtc was off, then the cursor overlay was left on (and hence a little
cursor would still be visible on the internal monitor if one did 'stop ui'
in docked mode, and then opened the lid).
BUG=chrome-os-partner:26514, chromium:370411
TEST=sanity check ui w/ and w/ external monitor.
TEST=powerd_dbus_suspend w/ cursor visible & not visible
TEST=See crosbug.com/p/26514#16
=> No cursor visible on internal display after step (10)
Change-Id: I3f0911c10892ad83b653e1dd82b16e3a8704bc6f
Reviewed-on: https://chromium-review.googlesource.com/198391
Reviewed-by: Sean Paul <seanpaul@chromium.org>
Tested-by: Daniel Kurtz <djkurtz@chromium.org>
diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index 1708fcc..2bc41a8 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -103,12 +103,12 @@
int fd;
struct udev_monitor *uevent_monitor;
InputHandlerProc uevent_handler;
- drmmode_cursor_ptr cursor;
} drmmode_rec, *drmmode_ptr;
typedef struct {
drmmode_ptr drmmode;
uint32_t id;
+ drmmode_cursor_rec cursor;
} drmmode_crtc_private_rec, *drmmode_crtc_private_ptr;
typedef struct {
@@ -764,8 +764,6 @@
drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
Rotation rotation, int x, int y)
{
- drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
- drmmode_ptr drmmode = drmmode_crtc->drmmode;
ScrnInfoPtr pScrn = crtc->scrn;
OMAPPtr pOMAP = OMAPPTR(pScrn);
xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
@@ -815,8 +813,7 @@
* The screen has reconfigured, so reload hw cursor images as needed,
* and adjust cursor positions.
*/
- if (drmmode->cursor)
- xf86_reload_cursors(pScrn->pScreen);
+ xf86_reload_cursors(pScrn->pScreen);
done:
TRACE_EXIT();
@@ -849,7 +846,7 @@
ScrnInfoPtr pScrn = crtc->scrn;
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
drmmode_ptr drmmode = drmmode_crtc->drmmode;
- drmmode_cursor_ptr cursor = drmmode->cursor;
+ drmmode_cursor_ptr cursor = &drmmode_crtc->cursor;
int32_t crtc_x, crtc_y;
uint32_t src_w, src_h;
int ret;
@@ -890,7 +887,7 @@
{
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
drmmode_ptr drmmode = drmmode_crtc->drmmode;
- drmmode_cursor_ptr cursor = drmmode->cursor;
+ drmmode_cursor_ptr cursor = &drmmode_crtc->cursor;
if (!cursor)
return;
@@ -904,8 +901,7 @@
drmmode_show_cursor(xf86CrtcPtr crtc)
{
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
- drmmode_ptr drmmode = drmmode_crtc->drmmode;
- drmmode_cursor_ptr cursor = drmmode->cursor;
+ drmmode_cursor_ptr cursor = &drmmode_crtc->cursor;
if (!cursor)
return;
@@ -917,8 +913,7 @@
drmmode_load_cursor_argb(xf86CrtcPtr crtc, CARD32 *image)
{
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
- drmmode_ptr drmmode = drmmode_crtc->drmmode;
- drmmode_cursor_ptr cursor = drmmode->cursor;
+ drmmode_cursor_ptr cursor = &drmmode_crtc->cursor;
int row;
void* dst;
const char* src_row;
@@ -942,144 +937,82 @@
omap_bo_cpu_fini(cursor->bo, 0);
}
-Bool
-drmmode_cursor_init(ScreenPtr pScreen)
+static Bool
+drm_plane_supports_format(drmModePlanePtr plane, uint32_t format)
{
- ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
- OMAPPtr pOMAP = OMAPPTR(pScrn);
- drmmode_ptr drmmode = drmmode_from_scrn(pScrn);
- drmmode_cursor_ptr cursor;
- drmModePlaneRes *plane_resources;
- drmModeObjectPropertiesPtr props;
- int i;
- uint32_t plane_id;
- uint32_t zpos_prop_id;
- int rc;
- Bool ret;
+ uint32_t f;
+ for (f = 0; f < plane->count_formats; f++)
+ if (plane->formats[f] == format)
+ return TRUE;
+ return FALSE;
+}
- /* technically we probably don't have any size limit.. since we
- * are just using an overlay... but xserver will always create
- * cursor images in the max size, so don't use width/height values
- * that are too big
- */
- const int w = CURSORW, h = CURSORH;
+static Bool
+drm_plane_can_be_cursor(ScrnInfoPtr pScrn, int fd, uint32_t plane_id, int num)
+{
+ uint32_t crtc_mask = 1 << num;
+ drmModePlanePtr plane;
+ Bool ret = FALSE;
- TRACE_ENTER();
-
- if (drmmode->cursor) {
- INFO_MSG("HW Cursor already initialized");
- ret = FALSE;
- goto out;
+ plane = drmModeGetPlane(fd, plane_id);
+ if (!plane) {
+ ERROR_MSG("[PLANE:%u] drmModeGetPlane failed: %s", plane_id,
+ strerror(errno));
+ return FALSE;
}
- /* find an unused plane which can be used as a mouse cursor. Note
- * that we cheat a bit, in order to not burn one overlay per crtc,
- * and only show the mouse cursor on one crtc at a time
- */
- plane_resources = drmModeGetPlaneResources(drmmode->fd);
- if (!plane_resources) {
- ERROR_MSG("drmModeGetPlaneResources failed: %s", strerror(errno));
- ret = FALSE;
- goto out;
- }
+ if (!(plane->possible_crtcs & crtc_mask))
+ goto free_plane;
- if (plane_resources->count_planes < 1) {
- ERROR_MSG("HW Cursor: not enough planes");
- drmModeFreePlaneResources(plane_resources);
- ret = FALSE;
- goto out;
- }
+ /* Plane must NOT have a current CRTC or FB */
+ if (plane->crtc_id || plane->fb_id)
+ goto free_plane;
- /* HACK: HW Cursor always uses the first plane */
- plane_id = plane_resources->planes[0];
- INFO_MSG("HW Cursor using [PLANE:%u]", plane_id);
-
- drmModeFreePlaneResources(plane_resources);
-
- props = drmModeObjectGetProperties(drmmode->fd, plane_id,
- DRM_MODE_OBJECT_PLANE);
- if (!props) {
- ERROR_MSG("No properties found for DRM [PLANE:%u]", plane_id);
- ret = FALSE;
- goto out;
- }
-
- /* Find first "zpos" property for our HW Cursor plane */
- zpos_prop_id = 0;
- for (i = 0; i < props->count_props && !zpos_prop_id; i++) {
- uint32_t prop_id = props->props[i];
- drmModePropertyPtr prop =
- drmModeGetProperty(drmmode->fd, prop_id);
- if (!prop) {
- ERROR_MSG("HW Cursor: Failed to get zpos [PROPERTY:%u] for [PLANE:%u]",
- prop_id, plane_id);
- continue;
- }
-
- if (!strcmp(prop->name, "zpos"))
- zpos_prop_id = prop->prop_id;
- drmModeFreeProperty(prop);
- }
- drmModeFreeObjectProperties(props);
-
- if (!zpos_prop_id) {
- ERROR_MSG("No 'zpos' property found for [PLANE:%u]", plane_id);
- ret = FALSE;
- goto out;
- }
-
- rc = drmModeObjectSetProperty(drmmode->fd, plane_id,
- DRM_MODE_OBJECT_PLANE, zpos_prop_id, 1);
- if (rc) {
- ERROR_MSG("HW Cursor: Failed to set zpos [PROPERTY:%u] for [PLANE:%u]",
- zpos_prop_id, plane_id);
- ret = FALSE;
- goto out;
- }
-
- cursor = calloc(1, sizeof *cursor);
- if (!cursor) {
- ERROR_MSG("HW Cursor allocation failed");
- ret = FALSE;
- goto out;
- }
- cursor->plane_id = plane_id;
- cursor->bo = omap_bo_new_with_format(pOMAP->dev, w, h,
- DRM_FORMAT_ARGB8888, 32);
- if (!cursor->bo) {
- ERROR_MSG("error allocating hw cursor buffer");
- ret = FALSE;
- goto err_free_cursor;
- }
-
- INFO_MSG("HW Cursor using [FB:%u]", omap_bo_fb(cursor->bo));
-
- drmmode->cursor = cursor;
+ if (!drm_plane_supports_format(plane, DRM_FORMAT_ARGB8888))
+ goto free_plane;
ret = TRUE;
- goto out;
-err_free_cursor:
- free(cursor);
-out:
- TRACE_EXIT();
+free_plane:
+ drmModeFreePlane(plane);
return ret;
}
-void
-drmmode_cursor_fini(ScreenPtr pScreen)
+static Bool
+drm_plane_set_zpos(ScrnInfoPtr pScrn, int fd, uint32_t plane_id, uint64_t zpos)
{
- ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
- drmmode_ptr drmmode = drmmode_from_scrn(pScrn);
- drmmode_cursor_ptr cursor = drmmode->cursor;
+ drmModeObjectPropertiesPtr props;
+ uint32_t prop_id;
+ int rc;
+ Bool ret;
- if (!cursor)
- return;
+ props = drmModeObjectGetProperties(fd, plane_id,
+ DRM_MODE_OBJECT_PLANE);
+ if (!props) {
+ ERROR_MSG("[PLANE:%u] drmModeObjectGetProperties failed: %s",
+ plane_id, strerror(errno));
+ return FALSE;
+ }
- omap_bo_unreference(cursor->bo);
+ prop_id = drmmode_get_prop_id(fd, props->count_props, props->props,
+ "zpos", DRM_MODE_PROP_RANGE);
+ if (!prop_id) {
+ ERROR_MSG("[PLANE:%u] failed to find zpos enum property",
+ plane_id);
+ ret = FALSE;
+ goto free_properties;
+ }
- free(cursor);
- drmmode->cursor = NULL;
+ rc = drmModeObjectSetProperty(fd, plane_id, DRM_MODE_OBJECT_PLANE,
+ prop_id, zpos);
+ ret = (rc == 0);
+ if (!ret)
+ ERROR_MSG("[PLANE:%u] failed to set [PROPERTY:%u] = %llu: %s",
+ plane_id, prop_id, zpos, strerror(errno));
+
+free_properties:
+ drmModeFreeObjectProperties(props);
+ return ret;
}
#ifdef OMAP_SUPPORT_GAMMA
@@ -1100,9 +1033,19 @@
}
#endif
+static void
+drmmode_cursor_destroy(drmmode_cursor_ptr cursor)
+{
+ omap_bo_unreference(cursor->bo);
+ cursor->plane_id = 0;
+}
+
+
static void drmmode_crtc_destroy(xf86CrtcPtr crtc)
{
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+
+ drmmode_cursor_destroy(&drmmode_crtc->cursor);
free(drmmode_crtc);
crtc->driver_private = NULL;
}
@@ -1121,8 +1064,64 @@
};
static Bool
+drmmode_crtc_cursor_pre_init(ScrnInfoPtr pScrn,
+ drmmode_crtc_private_ptr drmmode_crtc,
+ const drmModePlaneRes *plane_res, int num)
+{
+ drmmode_cursor_ptr cursor = &drmmode_crtc->cursor;
+ drmmode_ptr drmmode = drmmode_crtc->drmmode;
+ int fd = drmmode->fd;
+ OMAPPtr pOMAP = OMAPPTR(pScrn);
+ int p;
+ uint32_t plane_id;
+ Bool ret;
+
+ plane_id = 0;
+ for (p = 0; p < plane_res->count_planes && !plane_id; p++) {
+ uint32_t id = plane_res->planes[p];
+
+ if (!drm_plane_can_be_cursor(pScrn, fd, id, num))
+ continue;
+
+ /* Set HW Cursor plane "zpos" property to 1 */
+ ret = drm_plane_set_zpos(pScrn, fd, id, 1);
+ if (!ret) {
+ ERROR_MSG("[CRTC:%u] failed to set zpos for [PLANE:%u]",
+ drmmode_crtc->id, id);
+ continue;
+ }
+
+ plane_id = id;
+ }
+
+ if (!plane_id) {
+ ERROR_MSG("[CRTC:%u] No plane found for HW Cursor",
+ drmmode_crtc->id);
+ ret = FALSE;
+ goto out;
+ }
+
+ cursor->plane_id = plane_id;
+ cursor->bo = omap_bo_new_with_format(pOMAP->dev, CURSORW, CURSORH,
+ DRM_FORMAT_ARGB8888, 32);
+ if (!cursor->bo) {
+ ERROR_MSG("error allocating hw cursor buffer");
+ ret = FALSE;
+ goto out;
+ }
+
+ INFO_MSG("[CRTC:%u] HW Cursor using [PLANE:%u] with [FB:%u] and [BO:%u]",
+ drmmode_crtc->id, plane_id, omap_bo_fb(cursor->bo),
+ omap_bo_handle(cursor->bo));
+
+out:
+ return ret;
+}
+
+static Bool
drmmode_crtc_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode,
- const drmModeResPtr mode_res, int num)
+ const drmModeResPtr mode_res,
+ const drmModePlaneResPtr plane_res, int num)
{
xf86CrtcPtr crtc;
drmmode_crtc_private_ptr drmmode_crtc;
@@ -1141,20 +1140,25 @@
drmmode_crtc->id = crtc_id;
drmmode_crtc->drmmode = drmmode;
+ ret = drmmode_crtc_cursor_pre_init(pScrn, drmmode_crtc, plane_res,
+ num);
+ if (!ret)
+ goto err_free_drmmode_crtc;
+
crtc = xf86CrtcCreate(pScrn, &drmmode_crtc_funcs);
if (crtc == NULL) {
ERROR_MSG("CRTC[%u]: Failed to create xf86Crtc", crtc_id);
ret = FALSE;
- goto err_free_drmmode_crtc;
+ goto err_destroy_cursor;
}
- // FIXME - potentially add code to allocate a HW cursor here.
-
crtc->driver_private = drmmode_crtc;
ret = TRUE;
goto out;
+err_destroy_cursor:
+ drmmode_cursor_destroy(&drmmode_crtc->cursor);
err_free_drmmode_crtc:
free(drmmode_crtc);
out:
@@ -1722,6 +1726,7 @@
{
drmmode_ptr drmmode;
drmModeResPtr mode_res;
+ drmModePlaneResPtr plane_res;
int i;
Bool ret;
@@ -1750,17 +1755,27 @@
xf86CrtcSetSizeRange(pScrn, 320, 200, mode_res->max_width,
mode_res->max_height);
+ plane_res = drmModeGetPlaneResources(fd);
+ if (!plane_res) {
+ ERROR_MSG("drmModeGetPlaneResources failed: %s",
+ strerror(errno));
+ ret = FALSE;
+ goto err_free_drm_resources;
+ }
+ DEBUG_MSG(" %d planes", plane_res->count_planes);
+
drmmode = calloc(1, sizeof *drmmode);
if (!drmmode) {
ERROR_MSG("Failed to allocate drmmode");
ret = FALSE;
- goto err_free_drm_resources;
+ goto err_free_drm_plane_resources;
}
drmmode->fd = fd;
ret = TRUE;
for (i = 0; i < mode_res->count_crtcs && ret; i++)
- ret = drmmode_crtc_pre_init(pScrn, drmmode, mode_res, i);
+ ret = drmmode_crtc_pre_init(pScrn, drmmode, mode_res,
+ plane_res, i);
if (!ret)
goto err_crtcs_destroy;
@@ -1775,6 +1790,7 @@
goto err_outputs_destroy;
}
+ drmModeFreePlaneResources(plane_res);
drmModeFreeResources(mode_res);
goto out;
@@ -1783,6 +1799,8 @@
err_crtcs_destroy:
drmmode_crtcs_destroy(pScrn);
free(drmmode);
+err_free_drm_plane_resources:
+ drmModeFreePlaneResources(plane_res);
err_free_drm_resources:
drmModeFreeResources(mode_res);
out:
diff --git a/src/omap_driver.c b/src/omap_driver.c
index 6c1b9a3..65fff85 100644
--- a/src/omap_driver.c
+++ b/src/omap_driver.c
@@ -702,10 +702,6 @@
/* Initialize the cursor: */
miDCInitialize(pScreen, xf86GetPointerScreenFuncs());
- if (!drmmode_cursor_init(pScreen)) {
- ERROR_MSG("Hardware cursor initialization failed");
- }
-
/* XXX -- Is this the right place for this? The Intel i830 driver says:
* "Must force it before EnterVT, so we are in control of VT..."
*/
@@ -785,11 +781,8 @@
drmmode_close_screen(pScrn);
- if (pScrn->vtSema == TRUE) {
+ if (pScrn->vtSema == TRUE)
OMAPLeaveVT(VT_FUNC_ARGS(0));
- }
-
- drmmode_cursor_fini(pScreen);
unwrap(pOMAP, pScreen, CloseScreen);
diff --git a/src/omap_driver.h b/src/omap_driver.h
index d09976f..a62255f 100644
--- a/src/omap_driver.h
+++ b/src/omap_driver.h
@@ -185,8 +185,6 @@
int drmmode_page_flip(DrawablePtr draw, uint32_t fb_id, void *priv,
int* num_flipped);
void drmmode_wait_for_event(ScrnInfoPtr pScrn);
-Bool drmmode_cursor_init(ScreenPtr pScreen);
-void drmmode_cursor_fini(ScreenPtr pScreen);
void drmmode_copy_fb(ScrnInfoPtr pScrn);
OMAPScanoutPtr drmmode_scanout_from_drawable(OMAPScanoutPtr scanouts,
DrawablePtr pDraw);