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