7736186
From 92777b4dfc3920edb449d0be6ead65d8460653cc Mon Sep 17 00:00:00 2001
d5efb68
From: Ray Strode <rstrode@redhat.com>
d5efb68
Date: Tue, 12 May 2015 12:51:18 -0400
d5efb68
Subject: [PATCH] drm/qxl: reapply cursor after SetCrtc calls
d5efb68
d5efb68
The qxl driver currently destroys and recreates the
d5efb68
qxl "primary" any time the first crtc is set.
d5efb68
d5efb68
A side-effect of destroying the primary is mouse state
d5efb68
associated with the crtc is lost, which leads to
d5efb68
disappearing mouse cursors on wayland sessions.
d5efb68
d5efb68
This commit changes the driver to reapply the cursor
d5efb68
any time SetCrtc is called. It achieves this by keeping
d5efb68
a copy of the cursor data on the qxl_crtc struct.
d5efb68
d5efb68
Signed-off-by: Ray Strode <rstrode@redhat.com>
d5efb68
d5efb68
https://bugzilla.redhat.com/show_bug.cgi?id=1200901
d5efb68
---
d5efb68
 drivers/gpu/drm/qxl/qxl_display.c | 98 +++++++++++++++++++++++++++++++++++++++
d5efb68
 drivers/gpu/drm/qxl/qxl_drv.h     |  1 +
d5efb68
 2 files changed, 99 insertions(+)
d5efb68
d5efb68
diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c
7736186
index 3aef127..34ab444 100644
d5efb68
--- a/drivers/gpu/drm/qxl/qxl_display.c
d5efb68
+++ b/drivers/gpu/drm/qxl/qxl_display.c
7736186
@@ -184,60 +184,61 @@ static struct mode_size {
7736186
 	{1440,  900},
7736186
 	{1400, 1050},
7736186
 	{1680, 1050},
7736186
 	{1600, 1200},
7736186
 	{1920, 1080},
7736186
 	{1920, 1200}
7736186
 };
7736186
 
7736186
 static int qxl_add_common_modes(struct drm_connector *connector,
7736186
                                 unsigned pwidth,
7736186
                                 unsigned pheight)
7736186
 {
7736186
 	struct drm_device *dev = connector->dev;
7736186
 	struct drm_display_mode *mode = NULL;
7736186
 	int i;
7736186
 	for (i = 0; i < ARRAY_SIZE(common_modes); i++) {
7736186
 		mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h,
7736186
 				    60, false, false, false);
7736186
 		if (common_modes[i].w == pwidth && common_modes[i].h == pheight)
7736186
 			mode->type |= DRM_MODE_TYPE_PREFERRED;
7736186
 		drm_mode_probed_add(connector, mode);
7736186
 	}
7736186
 	return i - 1;
7736186
 }
7736186
 
7736186
 static void qxl_crtc_destroy(struct drm_crtc *crtc)
7736186
 {
d5efb68
 	struct qxl_crtc *qxl_crtc = to_qxl_crtc(crtc);
7736186
 
d5efb68
 	drm_crtc_cleanup(crtc);
d5efb68
+	kfree(qxl_crtc->cursor);
d5efb68
 	kfree(qxl_crtc);
d5efb68
 }
7736186
 
7736186
 static int qxl_crtc_page_flip(struct drm_crtc *crtc,
7736186
                               struct drm_framebuffer *fb,
7736186
                               struct drm_pending_vblank_event *event,
7736186
                               uint32_t page_flip_flags)
7736186
 {
7736186
 	struct drm_device *dev = crtc->dev;
7736186
 	struct qxl_device *qdev = dev->dev_private;
7736186
 	struct qxl_framebuffer *qfb_src = to_qxl_framebuffer(fb);
7736186
 	struct qxl_framebuffer *qfb_old = to_qxl_framebuffer(crtc->primary->fb);
7736186
 	struct qxl_bo *bo_old = gem_to_qxl_bo(qfb_old->obj);
7736186
 	struct qxl_bo *bo = gem_to_qxl_bo(qfb_src->obj);
7736186
 	unsigned long flags;
7736186
 	struct drm_clip_rect norect = {
7736186
 	    .x1 = 0,
7736186
 	    .y1 = 0,
7736186
 	    .x2 = fb->width,
7736186
 	    .y2 = fb->height
7736186
 	};
7736186
 	int inc = 1;
7736186
 	int one_clip_rect = 1;
7736186
 	int ret = 0;
7736186
 
7736186
 	crtc->primary->fb = fb;
7736186
 	bo_old->is_primary = false;
7736186
 	bo->is_primary = true;
7736186
 
7736186
 	ret = qxl_bo_reserve(bo, false);
7736186
@@ -269,60 +270,145 @@ static int qxl_crtc_page_flip(struct drm_crtc *crtc,
d5efb68
 	return 0;
d5efb68
 }
7736186
 
7736186
 static int
7736186
 qxl_hide_cursor(struct qxl_device *qdev)
7736186
 {
7736186
 	struct qxl_release *release;
7736186
 	struct qxl_cursor_cmd *cmd;
7736186
 	int ret;
7736186
 
7736186
 	ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), QXL_RELEASE_CURSOR_CMD,
7736186
 					 &release, NULL);
7736186
 	if (ret)
7736186
 		return ret;
7736186
 
7736186
 	ret = qxl_release_reserve_list(release, true);
7736186
 	if (ret) {
7736186
 		qxl_release_free(qdev, release);
7736186
 		return ret;
7736186
 	}
7736186
 
7736186
 	cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release);
7736186
 	cmd->type = QXL_CURSOR_HIDE;
7736186
 	qxl_release_unmap(qdev, release, &cmd->release_info);
7736186
 
7736186
 	qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
7736186
 	qxl_release_fence_buffer_objects(release);
7736186
 	return 0;
7736186
 }
7736186
 
d5efb68
+static int qxl_crtc_stash_cursor(struct drm_crtc *crtc,
d5efb68
+				struct qxl_cursor *cursor)
d5efb68
+{
d5efb68
+	struct qxl_crtc *qcrtc = to_qxl_crtc(crtc);
d5efb68
+	size_t cursor_size;
d5efb68
+
d5efb68
+	cursor_size = sizeof(struct qxl_cursor) + cursor->data_size;
d5efb68
+
d5efb68
+	if (!qcrtc->cursor || qcrtc->cursor->data_size != cursor->data_size) {
d5efb68
+		kfree(qcrtc->cursor);
d5efb68
+		qcrtc->cursor = kmalloc(cursor_size, GFP_KERNEL);
d5efb68
+
d5efb68
+		if (!qcrtc->cursor)
d5efb68
+			return -ENOMEM;
d5efb68
+	}
d5efb68
+
d5efb68
+	memcpy(qcrtc->cursor, cursor, cursor_size);
d5efb68
+
d5efb68
+	return 0;
d5efb68
+}
d5efb68
+
d5efb68
+static int qxl_crtc_apply_cursor(struct drm_crtc *crtc)
d5efb68
+{
d5efb68
+	struct qxl_crtc *qcrtc = to_qxl_crtc(crtc);
d5efb68
+	struct drm_device *dev = crtc->dev;
d5efb68
+	struct qxl_device *qdev = dev->dev_private;
d5efb68
+	struct qxl_cursor *cursor;
d5efb68
+	struct qxl_cursor_cmd *cmd;
d5efb68
+	struct qxl_bo *cursor_bo;
d5efb68
+	struct qxl_release *release;
d5efb68
+	size_t cursor_size;
d5efb68
+	int ret = 0;
d5efb68
+
d5efb68
+	if (!qcrtc->cursor)
d5efb68
+		return 0;
d5efb68
+
d5efb68
+	cursor_size = sizeof(*qcrtc->cursor) + qcrtc->cursor->data_size;
d5efb68
+
d5efb68
+	ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd),
d5efb68
+					 QXL_RELEASE_CURSOR_CMD,
d5efb68
+					 &release, NULL);
d5efb68
+	if (ret)
d5efb68
+		return ret;
d5efb68
+
d5efb68
+	ret = qxl_alloc_bo_reserved(qdev, release, cursor_size, &cursor_bo);
d5efb68
+	if (ret)
d5efb68
+		goto out_free_release;
d5efb68
+
d5efb68
+	ret = qxl_release_reserve_list(release, false);
d5efb68
+	if (ret)
d5efb68
+		goto out_free_bo;
d5efb68
+
d5efb68
+	ret = qxl_bo_kmap(cursor_bo, (void **)&cursor);
d5efb68
+	if (ret)
d5efb68
+		goto out_backoff;
d5efb68
+
d5efb68
+	memcpy(cursor, qcrtc->cursor, cursor_size);
d5efb68
+
d5efb68
+	qxl_bo_kunmap(cursor_bo);
d5efb68
+
d5efb68
+	cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release);
d5efb68
+	cmd->type = QXL_CURSOR_SET;
7736186
+	cmd->u.set.position.x = qcrtc->cur_x + qcrtc->hot_spot_x;
7736186
+	cmd->u.set.position.y = qcrtc->cur_y + qcrtc->hot_spot_y;
d5efb68
+
d5efb68
+	cmd->u.set.shape = qxl_bo_physical_address(qdev, cursor_bo, 0);
d5efb68
+
d5efb68
+	cmd->u.set.visible = 1;
d5efb68
+	qxl_release_unmap(qdev, release, &cmd->release_info);
d5efb68
+
d5efb68
+	qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
d5efb68
+	qxl_release_fence_buffer_objects(release);
d5efb68
+	qxl_bo_unref(&cursor_bo);
d5efb68
+
d5efb68
+	return ret;
d5efb68
+
d5efb68
+out_backoff:
d5efb68
+	qxl_release_backoff_reserve_list(release);
d5efb68
+out_free_bo:
d5efb68
+	qxl_bo_unref(&cursor_bo);
d5efb68
+out_free_release:
d5efb68
+	qxl_release_free(qdev, release);
d5efb68
+	return ret;
d5efb68
+}
d5efb68
+
d5efb68
 static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
d5efb68
 				struct drm_file *file_priv,
d5efb68
 				uint32_t handle,
7736186
 				uint32_t width,
7736186
 				uint32_t height, int32_t hot_x, int32_t hot_y)
7736186
 {
7736186
 	struct drm_device *dev = crtc->dev;
7736186
 	struct qxl_device *qdev = dev->dev_private;
7736186
 	struct qxl_crtc *qcrtc = to_qxl_crtc(crtc);
7736186
 	struct drm_gem_object *obj;
7736186
 	struct qxl_cursor *cursor;
7736186
 	struct qxl_cursor_cmd *cmd;
7736186
 	struct qxl_bo *cursor_bo, *user_bo;
7736186
 	struct qxl_release *release;
7736186
 	void *user_ptr;
7736186
 
7736186
 	int size = 64*64*4;
7736186
 	int ret = 0;
7736186
 	if (!handle)
7736186
 		return qxl_hide_cursor(qdev);
7736186
 
7736186
 	obj = drm_gem_object_lookup(file_priv, handle);
7736186
 	if (!obj) {
7736186
 		DRM_ERROR("cannot find cursor object\n");
7736186
 		return -ENOENT;
7736186
 	}
7736186
 
7736186
 	user_bo = gem_to_qxl_bo(obj);
7736186
 
7736186
 	ret = qxl_bo_reserve(user_bo, false);
7736186
@@ -343,60 +429,66 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
7736186
 					 &release, NULL);
7736186
 	if (ret)
7736186
 		goto out_kunmap;
7736186
 
7736186
 	ret = qxl_alloc_bo_reserved(qdev, release, sizeof(struct qxl_cursor) + size,
7736186
 			   &cursor_bo);
7736186
 	if (ret)
7736186
 		goto out_free_release;
7736186
 
7736186
 	ret = qxl_release_reserve_list(release, false);
7736186
 	if (ret)
7736186
 		goto out_free_bo;
7736186
 
7736186
 	ret = qxl_bo_kmap(cursor_bo, (void **)&cursor);
7736186
 	if (ret)
7736186
 		goto out_backoff;
7736186
 
7736186
 	cursor->header.unique = 0;
7736186
 	cursor->header.type = SPICE_CURSOR_TYPE_ALPHA;
7736186
 	cursor->header.width = 64;
7736186
 	cursor->header.height = 64;
7736186
 	cursor->header.hot_spot_x = hot_x;
7736186
 	cursor->header.hot_spot_y = hot_y;
7736186
 	cursor->data_size = size;
7736186
 	cursor->chunk.next_chunk = 0;
7736186
 	cursor->chunk.prev_chunk = 0;
7736186
 	cursor->chunk.data_size = size;
7736186
 
d5efb68
 	memcpy(cursor->chunk.data, user_ptr, size);
7736186
 
d5efb68
+	ret = qxl_crtc_stash_cursor(crtc, cursor);
d5efb68
+	if (ret) {
d5efb68
+		DRM_ERROR("cannot save cursor, may be lost on next mode set\n");
d5efb68
+		ret = 0;
d5efb68
+	}
d5efb68
+
d5efb68
 	qxl_bo_kunmap(cursor_bo);
7736186
 
d5efb68
 	qxl_bo_kunmap(user_bo);
7736186
 
7736186
 	qcrtc->cur_x += qcrtc->hot_spot_x - hot_x;
7736186
 	qcrtc->cur_y += qcrtc->hot_spot_y - hot_y;
7736186
 	qcrtc->hot_spot_x = hot_x;
7736186
 	qcrtc->hot_spot_y = hot_y;
7736186
 
7736186
 	cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release);
7736186
 	cmd->type = QXL_CURSOR_SET;
7736186
 	cmd->u.set.position.x = qcrtc->cur_x + qcrtc->hot_spot_x;
7736186
 	cmd->u.set.position.y = qcrtc->cur_y + qcrtc->hot_spot_y;
7736186
 
7736186
 	cmd->u.set.shape = qxl_bo_physical_address(qdev, cursor_bo, 0);
7736186
 
7736186
 	cmd->u.set.visible = 1;
7736186
 	qxl_release_unmap(qdev, release, &cmd->release_info);
7736186
 
7736186
 	qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
7736186
 	qxl_release_fence_buffer_objects(release);
7736186
 
7736186
 	/* finish with the userspace bo */
7736186
 	ret = qxl_bo_reserve(user_bo, false);
7736186
 	if (!ret) {
7736186
 		qxl_bo_unpin(user_bo);
7736186
 		qxl_bo_unreserve(user_bo);
7736186
 	}
7736186
 	drm_gem_object_unreference_unlocked(obj);
7736186
 
7736186
@@ -628,60 +720,66 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc,
7736186
 		  x, y,
7736186
 		  mode->hdisplay, mode->vdisplay,
7736186
 		  adjusted_mode->hdisplay,
7736186
 		  adjusted_mode->vdisplay);
7736186
 
7736186
 	if (bo->is_primary == false)
7736186
 		recreate_primary = true;
7736186
 
7736186
 	if (bo->surf.stride * bo->surf.height > qdev->vram_size) {
7736186
 		DRM_ERROR("Mode doesn't fit in vram size (vgamem)");
7736186
 		return -EINVAL;
7736186
         }
7736186
 
7736186
 	ret = qxl_bo_reserve(bo, false);
7736186
 	if (ret != 0)
7736186
 		return ret;
7736186
 	ret = qxl_bo_pin(bo, bo->type, NULL);
7736186
 	if (ret != 0) {
7736186
 		qxl_bo_unreserve(bo);
7736186
 		return -EINVAL;
7736186
 	}
7736186
 	qxl_bo_unreserve(bo);
7736186
 	if (recreate_primary) {
7736186
 		qxl_io_destroy_primary(qdev);
7736186
 		qxl_io_log(qdev,
7736186
 			   "recreate primary: %dx%d,%d,%d\n",
7736186
 			   bo->surf.width, bo->surf.height,
d5efb68
 			   bo->surf.stride, bo->surf.format);
d5efb68
 		qxl_io_create_primary(qdev, 0, bo);
d5efb68
 		bo->is_primary = true;
d5efb68
+
d5efb68
+		ret = qxl_crtc_apply_cursor(crtc);
d5efb68
+		if (ret) {
d5efb68
+			DRM_ERROR("could not set cursor after modeset");
d5efb68
+			ret = 0;
d5efb68
+		}
d5efb68
 	}
7736186
 
d5efb68
 	if (bo->is_primary) {
7736186
 		DRM_DEBUG_KMS("setting surface_id to 0 for primary surface %d on crtc %d\n", bo->surface_id, qcrtc->index);
7736186
 		surf_id = 0;
7736186
 	} else {
7736186
 		surf_id = bo->surface_id;
7736186
 	}
7736186
 
7736186
 	if (old_bo && old_bo != bo) {
7736186
 		old_bo->is_primary = false;
7736186
 		ret = qxl_bo_reserve(old_bo, false);
7736186
 		qxl_bo_unpin(old_bo);
7736186
 		qxl_bo_unreserve(old_bo);
7736186
 	}
7736186
 
7736186
 	qxl_monitors_config_set(qdev, qcrtc->index, x, y,
7736186
 				mode->hdisplay,
7736186
 				mode->vdisplay, surf_id);
7736186
 	return 0;
7736186
 }
7736186
 
7736186
 static void qxl_crtc_prepare(struct drm_crtc *crtc)
7736186
 {
7736186
 	DRM_DEBUG("current: %dx%d+%d+%d (%d).\n",
7736186
 		  crtc->mode.hdisplay, crtc->mode.vdisplay,
7736186
 		  crtc->x, crtc->y, crtc->enabled);
7736186
 }
7736186
 
7736186
 static void qxl_crtc_commit(struct drm_crtc *crtc)
d5efb68
diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h
d5efb68
index 8e633ca..6b63c54 100644
d5efb68
--- a/drivers/gpu/drm/qxl/qxl_drv.h
d5efb68
+++ b/drivers/gpu/drm/qxl/qxl_drv.h
7736186
@@ -110,60 +110,61 @@ struct qxl_bo {
7736186
 	void				*kptr;
7736186
 	int                             type;
7736186
 
7736186
 	/* Constant after initialization */
7736186
 	struct drm_gem_object		gem_base;
7736186
 	bool is_primary; /* is this now a primary surface */
7736186
 	bool hw_surf_alloc;
7736186
 	struct qxl_surface surf;
7736186
 	uint32_t surface_id;
7736186
 	struct qxl_release *surf_create;
7736186
 };
7736186
 #define gem_to_qxl_bo(gobj) container_of((gobj), struct qxl_bo, gem_base)
7736186
 #define to_qxl_bo(tobj) container_of((tobj), struct qxl_bo, tbo)
7736186
 
7736186
 struct qxl_gem {
7736186
 	struct mutex		mutex;
7736186
 	struct list_head	objects;
7736186
 };
7736186
 
7736186
 struct qxl_bo_list {
7736186
 	struct ttm_validate_buffer tv;
7736186
 };
7736186
 
7736186
 struct qxl_crtc {
7736186
 	struct drm_crtc base;
7736186
 	int index;
7736186
 	int cur_x;
d5efb68
 	int cur_y;
d5efb68
 	int hot_spot_x;
d5efb68
 	int hot_spot_y;
d5efb68
+	struct qxl_cursor *cursor;
d5efb68
 };
7736186
 
d5efb68
 struct qxl_output {
7736186
 	int index;
7736186
 	struct drm_connector base;
7736186
 	struct drm_encoder enc;
7736186
 };
7736186
 
7736186
 struct qxl_framebuffer {
7736186
 	struct drm_framebuffer base;
7736186
 	struct drm_gem_object *obj;
7736186
 };
7736186
 
7736186
 #define to_qxl_crtc(x) container_of(x, struct qxl_crtc, base)
7736186
 #define drm_connector_to_qxl_output(x) container_of(x, struct qxl_output, base)
7736186
 #define drm_encoder_to_qxl_output(x) container_of(x, struct qxl_output, enc)
7736186
 #define to_qxl_framebuffer(x) container_of(x, struct qxl_framebuffer, base)
7736186
 
7736186
 struct qxl_mman {
7736186
 	struct ttm_bo_global_ref        bo_global_ref;
7736186
 	struct drm_global_reference	mem_global_ref;
7736186
 	bool				mem_global_referenced;
7736186
 	struct ttm_bo_device		bdev;
7736186
 };
7736186
 
7736186
 struct qxl_mode_info {
7736186
 	int num_modes;
7736186
 	struct qxl_mode *modes;
7736186
 	bool mode_config_initialized;
7736186
 
7736186
-- 
7736186
2.7.4
7736186