Olaf Hering b2a5cc
From: Wei Hu <weh@microsoft.com>
Olaf Hering b2a5cc
Date: Thu, 5 Sep 2019 09:11:53 +0000
Olaf Hering b2a5cc
Patch-mainline: v5.5-rc1
Olaf Hering b2a5cc
References: bsc#1175306
Olaf Hering b2a5cc
Subject: video: hyperv: hyperv_fb: Obtain screen resolution from Hyper-V host
Olaf Hering b2a5cc
Git-commit: 67e7cdb4829d3246c98f2ec9b771303ebe162eab
Olaf Hering b2a5cc
Olaf Hering b2a5cc
Beginning from Windows 10 RS5+, VM screen resolution is obtained from host.
Olaf Hering b2a5cc
The "video=hyperv_fb" boot time option is not needed, but still can be
Olaf Hering b2a5cc
used to overwrite what the host specifies. The VM resolution on the host
Olaf Hering b2a5cc
could be set by executing the powershell "set-vmvideo" command.
Olaf Hering b2a5cc
Olaf Hering b2a5cc
Signed-off-by: Iouri Tarassov <iourit@microsoft.com>
Olaf Hering b2a5cc
Signed-off-by: Wei Hu <weh@microsoft.com>
Olaf Hering b2a5cc
Reviewed-by: Michael Kelley <mikelley@microsoft.com>
Olaf Hering b2a5cc
Reviewed-by: Dexuan Cui <decui@microsoft.com>
Olaf Hering b2a5cc
Signed-off-by: Sasha Levin <sashal@kernel.org>
Olaf Hering b2a5cc
Acked-by: Olaf Hering <ohering@suse.de>
Olaf Hering b2a5cc
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Olaf Hering b2a5cc
---
Olaf Hering b2a5cc
 drivers/video/fbdev/hyperv_fb.c | 159 +++++++++++++++++++++++++++++++++++++---
Olaf Hering b2a5cc
 1 file changed, 147 insertions(+), 12 deletions(-)
Olaf Hering b2a5cc
Olaf Hering b2a5cc
diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c
Olaf Hering b2a5cc
--- a/drivers/video/fbdev/hyperv_fb.c
Olaf Hering b2a5cc
+++ b/drivers/video/fbdev/hyperv_fb.c
Olaf Hering b2a5cc
@@ -23,6 +23,14 @@
Olaf Hering b2a5cc
  *
Olaf Hering b2a5cc
  * Portrait orientation is also supported:
Olaf Hering b2a5cc
  *     For example: video=hyperv_fb:864x1152
Olaf Hering b2a5cc
+ *
Olaf Hering b2a5cc
+ * When a Windows 10 RS5+ host is used, the virtual machine screen
Olaf Hering b2a5cc
+ * resolution is obtained from the host. The "video=hyperv_fb" option is
Olaf Hering b2a5cc
+ * not needed, but still can be used to overwrite what the host specifies.
Olaf Hering b2a5cc
+ * The VM resolution on the host could be set by executing the powershell
Olaf Hering b2a5cc
+ * "set-vmvideo" command. For example
Olaf Hering b2a5cc
+ *     set-vmvideo -vmname name -horizontalresolution:1920 \
Olaf Hering b2a5cc
+ * -verticalresolution:1200 -resolutiontype single
Olaf Hering b2a5cc
  */
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
Olaf Hering b2a5cc
@@ -45,6 +53,10 @@
Olaf Hering b2a5cc
 #define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major))
Olaf Hering b2a5cc
 #define SYNTHVID_VERSION_WIN7 SYNTHVID_VERSION(3, 0)
Olaf Hering b2a5cc
 #define SYNTHVID_VERSION_WIN8 SYNTHVID_VERSION(3, 2)
Olaf Hering b2a5cc
+#define SYNTHVID_VERSION_WIN10 SYNTHVID_VERSION(3, 5)
Olaf Hering b2a5cc
+
Olaf Hering b2a5cc
+#define SYNTHVID_VER_GET_MAJOR(ver) (ver & 0x0000ffff)
Olaf Hering b2a5cc
+#define SYNTHVID_VER_GET_MINOR(ver) ((ver & 0xffff0000) >> 16)
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
 #define SYNTHVID_DEPTH_WIN7 16
Olaf Hering b2a5cc
 #define SYNTHVID_DEPTH_WIN8 32
Olaf Hering b2a5cc
@@ -83,16 +95,25 @@ enum synthvid_msg_type {
Olaf Hering b2a5cc
 	SYNTHVID_POINTER_SHAPE		= 8,
Olaf Hering b2a5cc
 	SYNTHVID_FEATURE_CHANGE		= 9,
Olaf Hering b2a5cc
 	SYNTHVID_DIRT			= 10,
Olaf Hering b2a5cc
+	SYNTHVID_RESOLUTION_REQUEST	= 13,
Olaf Hering b2a5cc
+	SYNTHVID_RESOLUTION_RESPONSE	= 14,
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
-	SYNTHVID_MAX			= 11
Olaf Hering b2a5cc
+	SYNTHVID_MAX			= 15
Olaf Hering b2a5cc
 };
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
+#define		SYNTHVID_EDID_BLOCK_SIZE	128
Olaf Hering b2a5cc
+#define		SYNTHVID_MAX_RESOLUTION_COUNT	64
Olaf Hering b2a5cc
+
Olaf Hering b2a5cc
+struct hvd_screen_info {
Olaf Hering b2a5cc
+	u16 width;
Olaf Hering b2a5cc
+	u16 height;
Olaf Hering b2a5cc
+} __packed;
Olaf Hering b2a5cc
+
Olaf Hering b2a5cc
 struct synthvid_msg_hdr {
Olaf Hering b2a5cc
 	u32 type;
Olaf Hering b2a5cc
 	u32 size;  /* size of this header + payload after this field*/
Olaf Hering b2a5cc
 } __packed;
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
-
Olaf Hering b2a5cc
 struct synthvid_version_req {
Olaf Hering b2a5cc
 	u32 version;
Olaf Hering b2a5cc
 } __packed;
Olaf Hering b2a5cc
@@ -103,6 +124,19 @@ struct synthvid_version_resp {
Olaf Hering b2a5cc
 	u8 max_video_outputs;
Olaf Hering b2a5cc
 } __packed;
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
+struct synthvid_supported_resolution_req {
Olaf Hering b2a5cc
+	u8 maximum_resolution_count;
Olaf Hering b2a5cc
+} __packed;
Olaf Hering b2a5cc
+
Olaf Hering b2a5cc
+struct synthvid_supported_resolution_resp {
Olaf Hering b2a5cc
+	u8 edid_block[SYNTHVID_EDID_BLOCK_SIZE];
Olaf Hering b2a5cc
+	u8 resolution_count;
Olaf Hering b2a5cc
+	u8 default_resolution_index;
Olaf Hering b2a5cc
+	u8 is_standard;
Olaf Hering b2a5cc
+	struct hvd_screen_info
Olaf Hering b2a5cc
+		supported_resolution[SYNTHVID_MAX_RESOLUTION_COUNT];
Olaf Hering b2a5cc
+} __packed;
Olaf Hering b2a5cc
+
Olaf Hering b2a5cc
 struct synthvid_vram_location {
Olaf Hering b2a5cc
 	u64 user_ctx;
Olaf Hering b2a5cc
 	u8 is_vram_gpa_specified;
Olaf Hering b2a5cc
@@ -188,6 +222,8 @@ struct synthvid_msg {
Olaf Hering b2a5cc
 		struct synthvid_pointer_shape ptr_shape;
Olaf Hering b2a5cc
 		struct synthvid_feature_change feature_chg;
Olaf Hering b2a5cc
 		struct synthvid_dirt dirt;
Olaf Hering b2a5cc
+		struct synthvid_supported_resolution_req resolution_req;
Olaf Hering b2a5cc
+		struct synthvid_supported_resolution_resp resolution_resp;
Olaf Hering b2a5cc
 	};
Olaf Hering b2a5cc
 } __packed;
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
@@ -226,6 +262,8 @@ struct hvfb_par {
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
 static uint screen_width = HVFB_WIDTH;
Olaf Hering b2a5cc
 static uint screen_height = HVFB_HEIGHT;
Olaf Hering b2a5cc
+static uint screen_width_max = HVFB_WIDTH;
Olaf Hering b2a5cc
+static uint screen_height_max = HVFB_HEIGHT;
Olaf Hering b2a5cc
 static uint screen_depth;
Olaf Hering b2a5cc
 static uint screen_fb_size;
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
@@ -356,6 +394,7 @@ static void synthvid_recv_sub(struct hv_device *hdev)
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
 	/* Complete the wait event */
Olaf Hering b2a5cc
 	if (msg->vid_hdr.type == SYNTHVID_VERSION_RESPONSE ||
Olaf Hering b2a5cc
+	    msg->vid_hdr.type == SYNTHVID_RESOLUTION_RESPONSE ||
Olaf Hering b2a5cc
 	    msg->vid_hdr.type == SYNTHVID_VRAM_LOCATION_ACK) {
Olaf Hering b2a5cc
 		memcpy(par->init_buf, msg, MAX_VMBUS_PKT_SIZE);
Olaf Hering b2a5cc
 		complete(&par->wait);
Olaf Hering b2a5cc
@@ -402,6 +441,17 @@ static void synthvid_receive(void *ctx)
Olaf Hering b2a5cc
 	} while (bytes_recvd > 0 && ret == 0);
Olaf Hering b2a5cc
 }
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
+/* Check if the ver1 version is equal or greater than ver2 */
Olaf Hering b2a5cc
+static inline bool synthvid_ver_ge(u32 ver1, u32 ver2)
Olaf Hering b2a5cc
+{
Olaf Hering b2a5cc
+	if (SYNTHVID_VER_GET_MAJOR(ver1) > SYNTHVID_VER_GET_MAJOR(ver2) ||
Olaf Hering b2a5cc
+	    (SYNTHVID_VER_GET_MAJOR(ver1) == SYNTHVID_VER_GET_MAJOR(ver2) &&
Olaf Hering b2a5cc
+	     SYNTHVID_VER_GET_MINOR(ver1) >= SYNTHVID_VER_GET_MINOR(ver2)))
Olaf Hering b2a5cc
+		return true;
Olaf Hering b2a5cc
+
Olaf Hering b2a5cc
+	return false;
Olaf Hering b2a5cc
+}
Olaf Hering b2a5cc
+
Olaf Hering b2a5cc
 /* Check synthetic video protocol version with the host */
Olaf Hering b2a5cc
 static int synthvid_negotiate_ver(struct hv_device *hdev, u32 ver)
Olaf Hering b2a5cc
 {
Olaf Hering b2a5cc
@@ -430,6 +480,64 @@ static int synthvid_negotiate_ver(struct hv_device *hdev, u32 ver)
Olaf Hering b2a5cc
 	}
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
 	par->synthvid_version = ver;
Olaf Hering b2a5cc
+	pr_info("Synthvid Version major %d, minor %d\n",
Olaf Hering b2a5cc
+		SYNTHVID_VER_GET_MAJOR(ver), SYNTHVID_VER_GET_MINOR(ver));
Olaf Hering b2a5cc
+
Olaf Hering b2a5cc
+out:
Olaf Hering b2a5cc
+	return ret;
Olaf Hering b2a5cc
+}
Olaf Hering b2a5cc
+
Olaf Hering b2a5cc
+/* Get current resolution from the host */
Olaf Hering b2a5cc
+static int synthvid_get_supported_resolution(struct hv_device *hdev)
Olaf Hering b2a5cc
+{
Olaf Hering b2a5cc
+	struct fb_info *info = hv_get_drvdata(hdev);
Olaf Hering b2a5cc
+	struct hvfb_par *par = info->par;
Olaf Hering b2a5cc
+	struct synthvid_msg *msg = (struct synthvid_msg *)par->init_buf;
Olaf Hering b2a5cc
+	int ret = 0;
Olaf Hering b2a5cc
+	unsigned long t;
Olaf Hering b2a5cc
+	u8 index;
Olaf Hering b2a5cc
+	int i;
Olaf Hering b2a5cc
+
Olaf Hering b2a5cc
+	memset(msg, 0, sizeof(struct synthvid_msg));
Olaf Hering b2a5cc
+	msg->vid_hdr.type = SYNTHVID_RESOLUTION_REQUEST;
Olaf Hering b2a5cc
+	msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
Olaf Hering b2a5cc
+		sizeof(struct synthvid_supported_resolution_req);
Olaf Hering b2a5cc
+
Olaf Hering b2a5cc
+	msg->resolution_req.maximum_resolution_count =
Olaf Hering b2a5cc
+		SYNTHVID_MAX_RESOLUTION_COUNT;
Olaf Hering b2a5cc
+	synthvid_send(hdev, msg);
Olaf Hering b2a5cc
+
Olaf Hering b2a5cc
+	t = wait_for_completion_timeout(&par->wait, VSP_TIMEOUT);
Olaf Hering b2a5cc
+	if (!t) {
Olaf Hering b2a5cc
+		pr_err("Time out on waiting resolution response\n");
Olaf Hering b2a5cc
+			ret = -ETIMEDOUT;
Olaf Hering b2a5cc
+			goto out;
Olaf Hering b2a5cc
+	}
Olaf Hering b2a5cc
+
Olaf Hering b2a5cc
+	if (msg->resolution_resp.resolution_count == 0) {
Olaf Hering b2a5cc
+		pr_err("No supported resolutions\n");
Olaf Hering b2a5cc
+		ret = -ENODEV;
Olaf Hering b2a5cc
+		goto out;
Olaf Hering b2a5cc
+	}
Olaf Hering b2a5cc
+
Olaf Hering b2a5cc
+	index = msg->resolution_resp.default_resolution_index;
Olaf Hering b2a5cc
+	if (index >= msg->resolution_resp.resolution_count) {
Olaf Hering b2a5cc
+		pr_err("Invalid resolution index: %d\n", index);
Olaf Hering b2a5cc
+		ret = -ENODEV;
Olaf Hering b2a5cc
+		goto out;
Olaf Hering b2a5cc
+	}
Olaf Hering b2a5cc
+
Olaf Hering b2a5cc
+	for (i = 0; i < msg->resolution_resp.resolution_count; i++) {
Olaf Hering b2a5cc
+		screen_width_max = max_t(unsigned int, screen_width_max,
Olaf Hering b2a5cc
+		    msg->resolution_resp.supported_resolution[i].width);
Olaf Hering b2a5cc
+		screen_height_max = max_t(unsigned int, screen_height_max,
Olaf Hering b2a5cc
+		    msg->resolution_resp.supported_resolution[i].height);
Olaf Hering b2a5cc
+	}
Olaf Hering b2a5cc
+
Olaf Hering b2a5cc
+	screen_width =
Olaf Hering b2a5cc
+		msg->resolution_resp.supported_resolution[index].width;
Olaf Hering b2a5cc
+	screen_height =
Olaf Hering b2a5cc
+		msg->resolution_resp.supported_resolution[index].height;
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
 out:
Olaf Hering b2a5cc
 	return ret;
Olaf Hering b2a5cc
@@ -450,11 +558,27 @@ static int synthvid_connect_vsp(struct hv_device *hdev)
Olaf Hering b2a5cc
 	}
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
 	/* Negotiate the protocol version with host */
Olaf Hering b2a5cc
-	if (vmbus_proto_version == VERSION_WS2008 ||
Olaf Hering b2a5cc
-	    vmbus_proto_version == VERSION_WIN7)
Olaf Hering b2a5cc
-		ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN7);
Olaf Hering b2a5cc
-	else
Olaf Hering b2a5cc
+	switch (vmbus_proto_version) {
Olaf Hering b2a5cc
+	case VERSION_WIN10:
Olaf Hering b2a5cc
+	case VERSION_WIN10_V5:
Olaf Hering b2a5cc
+		ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN10);
Olaf Hering b2a5cc
+		if (!ret)
Olaf Hering b2a5cc
+			break;
Olaf Hering b2a5cc
+		/* Fallthrough */
Olaf Hering b2a5cc
+	case VERSION_WIN8:
Olaf Hering b2a5cc
+	case VERSION_WIN8_1:
Olaf Hering b2a5cc
 		ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN8);
Olaf Hering b2a5cc
+		if (!ret)
Olaf Hering b2a5cc
+			break;
Olaf Hering b2a5cc
+		/* Fallthrough */
Olaf Hering b2a5cc
+	case VERSION_WS2008:
Olaf Hering b2a5cc
+	case VERSION_WIN7:
Olaf Hering b2a5cc
+		ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN7);
Olaf Hering b2a5cc
+		break;
Olaf Hering b2a5cc
+	default:
Olaf Hering b2a5cc
+		ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN10);
Olaf Hering b2a5cc
+		break;
Olaf Hering b2a5cc
+	}
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
 	if (ret) {
Olaf Hering b2a5cc
 		pr_err("Synthetic video device version not accepted\n");
Olaf Hering b2a5cc
@@ -466,6 +590,12 @@ static int synthvid_connect_vsp(struct hv_device *hdev)
Olaf Hering b2a5cc
 	else
Olaf Hering b2a5cc
 		screen_depth = SYNTHVID_DEPTH_WIN8;
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
+	if (synthvid_ver_ge(par->synthvid_version, SYNTHVID_VERSION_WIN10)) {
Olaf Hering b2a5cc
+		ret = synthvid_get_supported_resolution(hdev);
Olaf Hering b2a5cc
+		if (ret)
Olaf Hering b2a5cc
+			pr_info("Failed to get supported resolution from host, use default\n");
Olaf Hering b2a5cc
+	}
Olaf Hering b2a5cc
+
Olaf Hering b2a5cc
 	screen_fb_size = hdev->channel->offermsg.offer.
Olaf Hering b2a5cc
 				mmio_megabytes * 1024 * 1024;
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
@@ -655,6 +785,8 @@ static void hvfb_get_option(struct fb_info *info)
Olaf Hering b2a5cc
 	}
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
 	if (x < HVFB_WIDTH_MIN || y < HVFB_HEIGHT_MIN ||
Olaf Hering b2a5cc
+	    (synthvid_ver_ge(par->synthvid_version, SYNTHVID_VERSION_WIN10) &&
Olaf Hering b2a5cc
+	    (x > screen_width_max || y > screen_height_max)) ||
Olaf Hering b2a5cc
 	    (par->synthvid_version == SYNTHVID_VERSION_WIN8 &&
Olaf Hering b2a5cc
 	     x * y * screen_depth / 8 > SYNTHVID_FB_SIZE_WIN8) ||
Olaf Hering b2a5cc
 	    (par->synthvid_version == SYNTHVID_VERSION_WIN7 &&
Olaf Hering b2a5cc
@@ -691,8 +823,12 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info)
Olaf Hering b2a5cc
 		}
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
 		if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) ||
Olaf Hering b2a5cc
-		    pci_resource_len(pdev, 0) < screen_fb_size)
Olaf Hering b2a5cc
+		    pci_resource_len(pdev, 0) < screen_fb_size) {
Olaf Hering b2a5cc
+			pr_err("Resource not available or (0x%lx < 0x%lx)\n",
Olaf Hering b2a5cc
+			       (unsigned long) pci_resource_len(pdev, 0),
Olaf Hering b2a5cc
+			       (unsigned long) screen_fb_size);
Olaf Hering b2a5cc
 			goto err1;
Olaf Hering b2a5cc
+		}
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
 		pot_end = pci_resource_end(pdev, 0);
Olaf Hering b2a5cc
 		pot_start = pot_end - screen_fb_size + 1;
Olaf Hering b2a5cc
@@ -781,17 +917,16 @@ static int hvfb_probe(struct hv_device *hdev,
Olaf Hering b2a5cc
 		goto error1;
Olaf Hering b2a5cc
 	}
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
+	hvfb_get_option(info);
Olaf Hering b2a5cc
+	pr_info("Screen resolution: %dx%d, Color depth: %d\n",
Olaf Hering b2a5cc
+		screen_width, screen_height, screen_depth);
Olaf Hering b2a5cc
+
Olaf Hering b2a5cc
 	ret = hvfb_getmem(hdev, info);
Olaf Hering b2a5cc
 	if (ret) {
Olaf Hering b2a5cc
 		pr_err("No memory for framebuffer\n");
Olaf Hering b2a5cc
 		goto error2;
Olaf Hering b2a5cc
 	}
Olaf Hering b2a5cc
 
Olaf Hering b2a5cc
-	hvfb_get_option(info);
Olaf Hering b2a5cc
-	pr_info("Screen resolution: %dx%d, Color depth: %d\n",
Olaf Hering b2a5cc
-		screen_width, screen_height, screen_depth);
Olaf Hering b2a5cc
-
Olaf Hering b2a5cc
-
Olaf Hering b2a5cc
 	/* Set up fb_info */
Olaf Hering b2a5cc
 	info->flags = FBINFO_DEFAULT;
Olaf Hering b2a5cc