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