drm/hyperv: Fix double mouse pointers
authorDexuan Cui <decui@microsoft.com>
Thu, 16 Sep 2021 19:36:44 +0000 (12:36 -0700)
committerDave Airlie <airlied@redhat.com>
Fri, 15 Oct 2021 05:04:44 +0000 (15:04 +1000)
Hyper-V supports a hardware cursor feature. It is not used by Linux VM,
but the Hyper-V host still draws a point as an extra mouse pointer,
which is unwanted, especially when Xorg is running.

The hyperv_fb driver uses synthvid_send_ptr() to hide the unwanted pointer.
When the hyperv_drm driver was developed, the function synthvid_send_ptr()
was not copied from the hyperv_fb driver. Fix the issue by adding the
function into hyperv_drm.

Fixes: 76c56a5affeb ("drm/hyperv: Add DRM driver for hyperv synthetic video device")
Signed-off-by: Dexuan Cui <decui@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Deepak Rawat <drawat.floss@gmail.com>
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210916193644.45650-1-decui@microsoft.com
Signed-off-by: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/hyperv/hyperv_drm.h
drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
drivers/gpu/drm/hyperv/hyperv_drm_proto.c

index 886add4..d2d8582 100644 (file)
@@ -46,6 +46,7 @@ int hyperv_mode_config_init(struct hyperv_drm_device *hv);
 int hyperv_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp);
 int hyperv_update_situation(struct hv_device *hdev, u8 active, u32 bpp,
                            u32 w, u32 h, u32 pitch);
+int hyperv_hide_hw_ptr(struct hv_device *hdev);
 int hyperv_update_dirt(struct hv_device *hdev, struct drm_rect *rect);
 int hyperv_connect_vsp(struct hv_device *hdev);
 
index 6dd4717..8c97a20 100644 (file)
@@ -101,6 +101,7 @@ static void hyperv_pipe_enable(struct drm_simple_display_pipe *pipe,
        struct hyperv_drm_device *hv = to_hv(pipe->crtc.dev);
        struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
 
+       hyperv_hide_hw_ptr(hv->hdev);
        hyperv_update_situation(hv->hdev, 1,  hv->screen_depth,
                                crtc_state->mode.hdisplay,
                                crtc_state->mode.vdisplay,
index 6d4bdcc..c0155c6 100644 (file)
@@ -299,6 +299,55 @@ int hyperv_update_situation(struct hv_device *hdev, u8 active, u32 bpp,
        return 0;
 }
 
+/*
+ * Hyper-V supports a hardware cursor feature. It's not used by Linux VM,
+ * but the Hyper-V host still draws a point as an extra mouse pointer,
+ * which is unwanted, especially when Xorg is running.
+ *
+ * The hyperv_fb driver uses synthvid_send_ptr() to hide the unwanted
+ * pointer, by setting msg.ptr_pos.is_visible = 1 and setting the
+ * msg.ptr_shape.data. Note: setting msg.ptr_pos.is_visible to 0 doesn't
+ * work in tests.
+ *
+ * Copy synthvid_send_ptr() to hyperv_drm and rename it to
+ * hyperv_hide_hw_ptr(). Note: hyperv_hide_hw_ptr() is also called in the
+ * handler of the SYNTHVID_FEATURE_CHANGE event, otherwise the host still
+ * draws an extra unwanted mouse pointer after the VM Connection window is
+ * closed and reopened.
+ */
+int hyperv_hide_hw_ptr(struct hv_device *hdev)
+{
+       struct synthvid_msg msg;
+
+       memset(&msg, 0, sizeof(struct synthvid_msg));
+       msg.vid_hdr.type = SYNTHVID_POINTER_POSITION;
+       msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
+               sizeof(struct synthvid_pointer_position);
+       msg.ptr_pos.is_visible = 1;
+       msg.ptr_pos.video_output = 0;
+       msg.ptr_pos.image_x = 0;
+       msg.ptr_pos.image_y = 0;
+       hyperv_sendpacket(hdev, &msg);
+
+       memset(&msg, 0, sizeof(struct synthvid_msg));
+       msg.vid_hdr.type = SYNTHVID_POINTER_SHAPE;
+       msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
+               sizeof(struct synthvid_pointer_shape);
+       msg.ptr_shape.part_idx = SYNTHVID_CURSOR_COMPLETE;
+       msg.ptr_shape.is_argb = 1;
+       msg.ptr_shape.width = 1;
+       msg.ptr_shape.height = 1;
+       msg.ptr_shape.hot_x = 0;
+       msg.ptr_shape.hot_y = 0;
+       msg.ptr_shape.data[0] = 0;
+       msg.ptr_shape.data[1] = 1;
+       msg.ptr_shape.data[2] = 1;
+       msg.ptr_shape.data[3] = 1;
+       hyperv_sendpacket(hdev, &msg);
+
+       return 0;
+}
+
 int hyperv_update_dirt(struct hv_device *hdev, struct drm_rect *rect)
 {
        struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
@@ -392,8 +441,11 @@ static void hyperv_receive_sub(struct hv_device *hdev)
                return;
        }
 
-       if (msg->vid_hdr.type == SYNTHVID_FEATURE_CHANGE)
+       if (msg->vid_hdr.type == SYNTHVID_FEATURE_CHANGE) {
                hv->dirt_needed = msg->feature_chg.is_dirt_needed;
+               if (hv->dirt_needed)
+                       hyperv_hide_hw_ptr(hv->hdev);
+       }
 }
 
 static void hyperv_receive(void *ctx)