From mboxrd@z Thu Jan 1 00:00:00 1970 From: Wei Hu Subject: [PATCH] video: hyperv: hyperv_fb: Obtain screen resolution from Hyper-V host Date: Tue, 13 Aug 2019 09:54:42 +0000 Message-ID: <20190813095351.1780-1-weh@microsoft.com> Reply-To: Wei Hu Mime-Version: 1.0 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable Return-path: Content-Language: en-US Sender: linux-kernel-owner@vger.kernel.org To: "b.zolnierkie@samsung.com" , "linux-hyperv@vger.kernel.org" , "dri-devel@lists.freedesktop.org" , "linux-fbdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , "sashal@kernel.org" , Stephen Hemminger , Haiyang Zhang , KY Srinivasan , Dexuan Cui , Iouri Tarassov , Michael Kelley , Wei Hu List-Id: dri-devel@lists.freedesktop.org Beginning from Windows 10 RS5+, VM screen resolution is obtained from host. The "video=3Dhyperv_fb" boot time option is not needed, but still can be used to overwrite the VM resolution. The VM resolution on the host could be set by executing the powershell "set-vmvideo" command. Signed-off-by: Iouri Tarassov Signed-off-by: Wei Hu --- drivers/video/fbdev/hyperv_fb.c | 136 +++++++++++++++++++++++++++++--- 1 file changed, 125 insertions(+), 11 deletions(-) diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_f= b.c index 00f5bdcc6c6f..1042f3311fa2 100644 --- a/drivers/video/fbdev/hyperv_fb.c +++ b/drivers/video/fbdev/hyperv_fb.c @@ -23,6 +23,14 @@ * * Portrait orientation is also supported: * For example: video=3Dhyperv_fb:864x1152 + * + * When a Windows 10 RS5+ host is used, the virtual machine screen + * resolution is obtained from the host. The "video=3Dhyperv_fb" option is + * not needed, but still can be used to overwrite the VM resolution. The + * VM resolution on the host could be set by executing the powershell + * "set-vmvideo" command. For example + * set-vmvideo -vmname name -horizontalresolution:1920 \ + * -verticalresolution:1200 -resolutiontype single */ =20 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -44,6 +52,7 @@ #define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major)) #define SYNTHVID_VERSION_WIN7 SYNTHVID_VERSION(3, 0) #define SYNTHVID_VERSION_WIN8 SYNTHVID_VERSION(3, 2) +#define SYNTHVID_VERSION_WIN10 SYNTHVID_VERSION(3, 5) =20 #define SYNTHVID_DEPTH_WIN7 16 #define SYNTHVID_DEPTH_WIN8 32 @@ -82,16 +91,25 @@ enum synthvid_msg_type { SYNTHVID_POINTER_SHAPE =3D 8, SYNTHVID_FEATURE_CHANGE =3D 9, SYNTHVID_DIRT =3D 10, + SYNTHVID_RESOLUTION_REQUEST =3D 13, + SYNTHVID_RESOLUTION_RESPONSE =3D 14, =20 - SYNTHVID_MAX =3D 11 + SYNTHVID_MAX =3D 15 }; =20 +#define SYNTHVID_EDID_BLOCK_SIZE 128 +#define SYNTHVID_MAX_RESOLUTION_COUNT 64 + +struct hvd_screen_info { + u16 width; + u16 height; +} __packed; + struct synthvid_msg_hdr { u32 type; u32 size; /* size of this header + payload after this field*/ } __packed; =20 - struct synthvid_version_req { u32 version; } __packed; @@ -102,6 +120,18 @@ struct synthvid_version_resp { u8 max_video_outputs; } __packed; =20 +struct synthvid_supported_resolution_req { + u8 maximum_resolution_count; +} __packed; + +struct synthvid_supported_resolution_resp { + u8 edid_block[SYNTHVID_EDID_BLOCK_SIZE]; + u8 resolution_count; + u8 default_resolution_index; + u8 is_standard; + struct hvd_screen_info supported_resolution[1]; +} __packed; + struct synthvid_vram_location { u64 user_ctx; u8 is_vram_gpa_specified; @@ -187,6 +217,8 @@ struct synthvid_msg { struct synthvid_pointer_shape ptr_shape; struct synthvid_feature_change feature_chg; struct synthvid_dirt dirt; + struct synthvid_supported_resolution_req resolution_req; + struct synthvid_supported_resolution_resp resolution_resp; }; } __packed; =20 @@ -224,6 +256,8 @@ struct hvfb_par { =20 static uint screen_width =3D HVFB_WIDTH; static uint screen_height =3D HVFB_HEIGHT; +static uint screen_width_max =3D HVFB_WIDTH; +static uint screen_height_max =3D HVFB_HEIGHT; static uint screen_depth; static uint screen_fb_size; =20 @@ -354,6 +388,7 @@ static void synthvid_recv_sub(struct hv_device *hdev) =20 /* Complete the wait event */ if (msg->vid_hdr.type =3D=3D SYNTHVID_VERSION_RESPONSE || + msg->vid_hdr.type =3D=3D SYNTHVID_RESOLUTION_RESPONSE || msg->vid_hdr.type =3D=3D SYNTHVID_VRAM_LOCATION_ACK) { memcpy(par->init_buf, msg, MAX_VMBUS_PKT_SIZE); complete(&par->wait); @@ -428,6 +463,64 @@ static int synthvid_negotiate_ver(struct hv_device *hd= ev, u32 ver) } =20 par->synthvid_version =3D ver; + pr_info("Synthvid Version major %d, minor %d\n", + ver & 0x0000ffff, (ver & 0xffff0000) >> 16); + +out: + return ret; +} + +/* Get current resolution from the host */ +static int synthvid_get_supported_resolution(struct hv_device *hdev) +{ + struct fb_info *info =3D hv_get_drvdata(hdev); + struct hvfb_par *par =3D info->par; + struct synthvid_msg *msg =3D (struct synthvid_msg *)par->init_buf; + int ret =3D 0; + unsigned long t; + u8 index; + int i; + + memset(msg, 0, sizeof(struct synthvid_msg)); + msg->vid_hdr.type =3D SYNTHVID_RESOLUTION_REQUEST; + msg->vid_hdr.size =3D sizeof(struct synthvid_msg_hdr) + + sizeof(struct synthvid_supported_resolution_req); + + msg->resolution_req.maximum_resolution_count =3D + SYNTHVID_MAX_RESOLUTION_COUNT; + synthvid_send(hdev, msg); + + t =3D wait_for_completion_timeout(&par->wait, VSP_TIMEOUT); + if (!t) { + pr_err("Time out on waiting resolution response\n"); + ret =3D -ETIMEDOUT; + goto out; + } + + if (msg->resolution_resp.resolution_count =3D=3D 0) { + pr_err("No supported resolutions\n"); + ret =3D -ENODEV; + goto out; + } + + index =3D msg->resolution_resp.default_resolution_index; + if (index >=3D msg->resolution_resp.resolution_count) { + pr_err("Invalid resolution index: %d\n", index); + ret =3D -ENODEV; + goto out; + } + + for (i =3D 0; i < msg->resolution_resp.resolution_count; i++) { + screen_width_max =3D max_t(unsigned int, screen_width_max, + msg->resolution_resp.supported_resolution[i].width); + screen_height_max =3D max_t(unsigned int, screen_height_max, + msg->resolution_resp.supported_resolution[i].height); + } + + screen_width =3D + msg->resolution_resp.supported_resolution[index].width; + screen_height =3D + msg->resolution_resp.supported_resolution[index].height; =20 out: return ret; @@ -448,11 +541,21 @@ static int synthvid_connect_vsp(struct hv_device *hde= v) } =20 /* Negotiate the protocol version with host */ - if (vmbus_proto_version =3D=3D VERSION_WS2008 || - vmbus_proto_version =3D=3D VERSION_WIN7) + switch (vmbus_proto_version) { + case VERSION_WS2008: + case VERSION_WIN7: ret =3D synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN7); - else + break; + case VERSION_WIN8: + case VERSION_WIN8_1: ret =3D synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN8); + break; + case VERSION_WIN10: + case VERSION_WIN10_V5: + default: + ret =3D synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN10); + break; + } =20 if (ret) { pr_err("Synthetic video device version not accepted\n"); @@ -464,6 +567,12 @@ static int synthvid_connect_vsp(struct hv_device *hdev= ) else screen_depth =3D SYNTHVID_DEPTH_WIN8; =20 + if (par->synthvid_version >=3D SYNTHVID_VERSION_WIN10) { + ret =3D synthvid_get_supported_resolution(hdev); + if (ret) + pr_info("Failed to get supported resolution from host, use default\n"); + } + screen_fb_size =3D hdev->channel->offermsg.offer. mmio_megabytes * 1024 * 1024; =20 @@ -653,6 +762,8 @@ static void hvfb_get_option(struct fb_info *info) } =20 if (x < HVFB_WIDTH_MIN || y < HVFB_HEIGHT_MIN || + (par->synthvid_version >=3D SYNTHVID_VERSION_WIN10 && + (x > screen_width_max || y > screen_height_max)) || (par->synthvid_version =3D=3D SYNTHVID_VERSION_WIN8 && x * y * screen_depth / 8 > SYNTHVID_FB_SIZE_WIN8) || (par->synthvid_version =3D=3D SYNTHVID_VERSION_WIN7 && @@ -689,8 +800,12 @@ static int hvfb_getmem(struct hv_device *hdev, struct = fb_info *info) } =20 if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) || - pci_resource_len(pdev, 0) < screen_fb_size) + pci_resource_len(pdev, 0) < screen_fb_size) { + pr_err("Resource not available or (0x%lx < 0x%lx)\n", + (unsigned long) pci_resource_len(pdev, 0), + (unsigned long) screen_fb_size); goto err1; + } =20 pot_end =3D pci_resource_end(pdev, 0); pot_start =3D pot_end - screen_fb_size + 1; @@ -781,17 +896,16 @@ static int hvfb_probe(struct hv_device *hdev, goto error1; } =20 + hvfb_get_option(info); + pr_info("Screen resolution: %dx%d, Color depth: %d\n", + screen_width, screen_height, screen_depth); + ret =3D hvfb_getmem(hdev, info); if (ret) { pr_err("No memory for framebuffer\n"); goto error2; } =20 - hvfb_get_option(info); - pr_info("Screen resolution: %dx%d, Color depth: %d\n", - screen_width, screen_height, screen_depth); - - /* Set up fb_info */ info->flags =3D FBINFO_DEFAULT; =20 --=20 2.20.1