162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2021 Microsoft 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Portions of this code is derived from hyperv_fb.c 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/hyperv.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <drm/drm_print.h> 1162306a36Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "hyperv_drm.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define VMBUS_RING_BUFSIZE (256 * 1024) 1662306a36Sopenharmony_ci#define VMBUS_VSP_TIMEOUT (10 * HZ) 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major)) 1962306a36Sopenharmony_ci#define SYNTHVID_VER_GET_MAJOR(ver) (ver & 0x0000ffff) 2062306a36Sopenharmony_ci#define SYNTHVID_VER_GET_MINOR(ver) ((ver & 0xffff0000) >> 16) 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* Support for VERSION_WIN7 is removed. #define is retained for reference. */ 2362306a36Sopenharmony_ci#define SYNTHVID_VERSION_WIN7 SYNTHVID_VERSION(3, 0) 2462306a36Sopenharmony_ci#define SYNTHVID_VERSION_WIN8 SYNTHVID_VERSION(3, 2) 2562306a36Sopenharmony_ci#define SYNTHVID_VERSION_WIN10 SYNTHVID_VERSION(3, 5) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define SYNTHVID_DEPTH_WIN8 32 2862306a36Sopenharmony_ci#define SYNTHVID_WIDTH_WIN8 1600 2962306a36Sopenharmony_ci#define SYNTHVID_HEIGHT_WIN8 1200 3062306a36Sopenharmony_ci#define SYNTHVID_FB_SIZE_WIN8 (8 * 1024 * 1024) 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cienum pipe_msg_type { 3362306a36Sopenharmony_ci PIPE_MSG_INVALID, 3462306a36Sopenharmony_ci PIPE_MSG_DATA, 3562306a36Sopenharmony_ci PIPE_MSG_MAX 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cienum synthvid_msg_type { 3962306a36Sopenharmony_ci SYNTHVID_ERROR = 0, 4062306a36Sopenharmony_ci SYNTHVID_VERSION_REQUEST = 1, 4162306a36Sopenharmony_ci SYNTHVID_VERSION_RESPONSE = 2, 4262306a36Sopenharmony_ci SYNTHVID_VRAM_LOCATION = 3, 4362306a36Sopenharmony_ci SYNTHVID_VRAM_LOCATION_ACK = 4, 4462306a36Sopenharmony_ci SYNTHVID_SITUATION_UPDATE = 5, 4562306a36Sopenharmony_ci SYNTHVID_SITUATION_UPDATE_ACK = 6, 4662306a36Sopenharmony_ci SYNTHVID_POINTER_POSITION = 7, 4762306a36Sopenharmony_ci SYNTHVID_POINTER_SHAPE = 8, 4862306a36Sopenharmony_ci SYNTHVID_FEATURE_CHANGE = 9, 4962306a36Sopenharmony_ci SYNTHVID_DIRT = 10, 5062306a36Sopenharmony_ci SYNTHVID_RESOLUTION_REQUEST = 13, 5162306a36Sopenharmony_ci SYNTHVID_RESOLUTION_RESPONSE = 14, 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci SYNTHVID_MAX = 15 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistruct pipe_msg_hdr { 5762306a36Sopenharmony_ci u32 type; 5862306a36Sopenharmony_ci u32 size; /* size of message after this field */ 5962306a36Sopenharmony_ci} __packed; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct hvd_screen_info { 6262306a36Sopenharmony_ci u16 width; 6362306a36Sopenharmony_ci u16 height; 6462306a36Sopenharmony_ci} __packed; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistruct synthvid_msg_hdr { 6762306a36Sopenharmony_ci u32 type; 6862306a36Sopenharmony_ci u32 size; /* size of this header + payload after this field */ 6962306a36Sopenharmony_ci} __packed; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistruct synthvid_version_req { 7262306a36Sopenharmony_ci u32 version; 7362306a36Sopenharmony_ci} __packed; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistruct synthvid_version_resp { 7662306a36Sopenharmony_ci u32 version; 7762306a36Sopenharmony_ci u8 is_accepted; 7862306a36Sopenharmony_ci u8 max_video_outputs; 7962306a36Sopenharmony_ci} __packed; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistruct synthvid_vram_location { 8262306a36Sopenharmony_ci u64 user_ctx; 8362306a36Sopenharmony_ci u8 is_vram_gpa_specified; 8462306a36Sopenharmony_ci u64 vram_gpa; 8562306a36Sopenharmony_ci} __packed; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistruct synthvid_vram_location_ack { 8862306a36Sopenharmony_ci u64 user_ctx; 8962306a36Sopenharmony_ci} __packed; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistruct video_output_situation { 9262306a36Sopenharmony_ci u8 active; 9362306a36Sopenharmony_ci u32 vram_offset; 9462306a36Sopenharmony_ci u8 depth_bits; 9562306a36Sopenharmony_ci u32 width_pixels; 9662306a36Sopenharmony_ci u32 height_pixels; 9762306a36Sopenharmony_ci u32 pitch_bytes; 9862306a36Sopenharmony_ci} __packed; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistruct synthvid_situation_update { 10162306a36Sopenharmony_ci u64 user_ctx; 10262306a36Sopenharmony_ci u8 video_output_count; 10362306a36Sopenharmony_ci struct video_output_situation video_output[1]; 10462306a36Sopenharmony_ci} __packed; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistruct synthvid_situation_update_ack { 10762306a36Sopenharmony_ci u64 user_ctx; 10862306a36Sopenharmony_ci} __packed; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistruct synthvid_pointer_position { 11162306a36Sopenharmony_ci u8 is_visible; 11262306a36Sopenharmony_ci u8 video_output; 11362306a36Sopenharmony_ci s32 image_x; 11462306a36Sopenharmony_ci s32 image_y; 11562306a36Sopenharmony_ci} __packed; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#define SYNTHVID_CURSOR_MAX_X 96 11862306a36Sopenharmony_ci#define SYNTHVID_CURSOR_MAX_Y 96 11962306a36Sopenharmony_ci#define SYNTHVID_CURSOR_ARGB_PIXEL_SIZE 4 12062306a36Sopenharmony_ci#define SYNTHVID_CURSOR_MAX_SIZE (SYNTHVID_CURSOR_MAX_X * \ 12162306a36Sopenharmony_ci SYNTHVID_CURSOR_MAX_Y * SYNTHVID_CURSOR_ARGB_PIXEL_SIZE) 12262306a36Sopenharmony_ci#define SYNTHVID_CURSOR_COMPLETE (-1) 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistruct synthvid_pointer_shape { 12562306a36Sopenharmony_ci u8 part_idx; 12662306a36Sopenharmony_ci u8 is_argb; 12762306a36Sopenharmony_ci u32 width; /* SYNTHVID_CURSOR_MAX_X at most */ 12862306a36Sopenharmony_ci u32 height; /* SYNTHVID_CURSOR_MAX_Y at most */ 12962306a36Sopenharmony_ci u32 hot_x; /* hotspot relative to upper-left of pointer image */ 13062306a36Sopenharmony_ci u32 hot_y; 13162306a36Sopenharmony_ci u8 data[4]; 13262306a36Sopenharmony_ci} __packed; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistruct synthvid_feature_change { 13562306a36Sopenharmony_ci u8 is_dirt_needed; 13662306a36Sopenharmony_ci u8 is_ptr_pos_needed; 13762306a36Sopenharmony_ci u8 is_ptr_shape_needed; 13862306a36Sopenharmony_ci u8 is_situ_needed; 13962306a36Sopenharmony_ci} __packed; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistruct rect { 14262306a36Sopenharmony_ci s32 x1, y1; /* top left corner */ 14362306a36Sopenharmony_ci s32 x2, y2; /* bottom right corner, exclusive */ 14462306a36Sopenharmony_ci} __packed; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistruct synthvid_dirt { 14762306a36Sopenharmony_ci u8 video_output; 14862306a36Sopenharmony_ci u8 dirt_count; 14962306a36Sopenharmony_ci struct rect rect[1]; 15062306a36Sopenharmony_ci} __packed; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci#define SYNTHVID_EDID_BLOCK_SIZE 128 15362306a36Sopenharmony_ci#define SYNTHVID_MAX_RESOLUTION_COUNT 64 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistruct synthvid_supported_resolution_req { 15662306a36Sopenharmony_ci u8 maximum_resolution_count; 15762306a36Sopenharmony_ci} __packed; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistruct synthvid_supported_resolution_resp { 16062306a36Sopenharmony_ci u8 edid_block[SYNTHVID_EDID_BLOCK_SIZE]; 16162306a36Sopenharmony_ci u8 resolution_count; 16262306a36Sopenharmony_ci u8 default_resolution_index; 16362306a36Sopenharmony_ci u8 is_standard; 16462306a36Sopenharmony_ci struct hvd_screen_info supported_resolution[SYNTHVID_MAX_RESOLUTION_COUNT]; 16562306a36Sopenharmony_ci} __packed; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistruct synthvid_msg { 16862306a36Sopenharmony_ci struct pipe_msg_hdr pipe_hdr; 16962306a36Sopenharmony_ci struct synthvid_msg_hdr vid_hdr; 17062306a36Sopenharmony_ci union { 17162306a36Sopenharmony_ci struct synthvid_version_req ver_req; 17262306a36Sopenharmony_ci struct synthvid_version_resp ver_resp; 17362306a36Sopenharmony_ci struct synthvid_vram_location vram; 17462306a36Sopenharmony_ci struct synthvid_vram_location_ack vram_ack; 17562306a36Sopenharmony_ci struct synthvid_situation_update situ; 17662306a36Sopenharmony_ci struct synthvid_situation_update_ack situ_ack; 17762306a36Sopenharmony_ci struct synthvid_pointer_position ptr_pos; 17862306a36Sopenharmony_ci struct synthvid_pointer_shape ptr_shape; 17962306a36Sopenharmony_ci struct synthvid_feature_change feature_chg; 18062306a36Sopenharmony_ci struct synthvid_dirt dirt; 18162306a36Sopenharmony_ci struct synthvid_supported_resolution_req resolution_req; 18262306a36Sopenharmony_ci struct synthvid_supported_resolution_resp resolution_resp; 18362306a36Sopenharmony_ci }; 18462306a36Sopenharmony_ci} __packed; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic inline bool hyperv_version_ge(u32 ver1, u32 ver2) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci if (SYNTHVID_VER_GET_MAJOR(ver1) > SYNTHVID_VER_GET_MAJOR(ver2) || 18962306a36Sopenharmony_ci (SYNTHVID_VER_GET_MAJOR(ver1) == SYNTHVID_VER_GET_MAJOR(ver2) && 19062306a36Sopenharmony_ci SYNTHVID_VER_GET_MINOR(ver1) >= SYNTHVID_VER_GET_MINOR(ver2))) 19162306a36Sopenharmony_ci return true; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return false; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic inline int hyperv_sendpacket(struct hv_device *hdev, struct synthvid_msg *msg) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci static atomic64_t request_id = ATOMIC64_INIT(0); 19962306a36Sopenharmony_ci struct hyperv_drm_device *hv = hv_get_drvdata(hdev); 20062306a36Sopenharmony_ci int ret; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci msg->pipe_hdr.type = PIPE_MSG_DATA; 20362306a36Sopenharmony_ci msg->pipe_hdr.size = msg->vid_hdr.size; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci ret = vmbus_sendpacket(hdev->channel, msg, 20662306a36Sopenharmony_ci msg->vid_hdr.size + sizeof(struct pipe_msg_hdr), 20762306a36Sopenharmony_ci atomic64_inc_return(&request_id), 20862306a36Sopenharmony_ci VM_PKT_DATA_INBAND, 0); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (ret) 21162306a36Sopenharmony_ci drm_err_ratelimited(&hv->dev, "Unable to send packet via vmbus; error %d\n", ret); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return ret; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic int hyperv_negotiate_version(struct hv_device *hdev, u32 ver) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct hyperv_drm_device *hv = hv_get_drvdata(hdev); 21962306a36Sopenharmony_ci struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf; 22062306a36Sopenharmony_ci struct drm_device *dev = &hv->dev; 22162306a36Sopenharmony_ci unsigned long t; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci memset(msg, 0, sizeof(struct synthvid_msg)); 22462306a36Sopenharmony_ci msg->vid_hdr.type = SYNTHVID_VERSION_REQUEST; 22562306a36Sopenharmony_ci msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 22662306a36Sopenharmony_ci sizeof(struct synthvid_version_req); 22762306a36Sopenharmony_ci msg->ver_req.version = ver; 22862306a36Sopenharmony_ci hyperv_sendpacket(hdev, msg); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT); 23162306a36Sopenharmony_ci if (!t) { 23262306a36Sopenharmony_ci drm_err(dev, "Time out on waiting version response\n"); 23362306a36Sopenharmony_ci return -ETIMEDOUT; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (!msg->ver_resp.is_accepted) { 23762306a36Sopenharmony_ci drm_err(dev, "Version request not accepted\n"); 23862306a36Sopenharmony_ci return -ENODEV; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci hv->synthvid_version = ver; 24262306a36Sopenharmony_ci drm_info(dev, "Synthvid Version major %d, minor %d\n", 24362306a36Sopenharmony_ci SYNTHVID_VER_GET_MAJOR(ver), SYNTHVID_VER_GET_MINOR(ver)); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ciint hyperv_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct hyperv_drm_device *hv = hv_get_drvdata(hdev); 25162306a36Sopenharmony_ci struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf; 25262306a36Sopenharmony_ci struct drm_device *dev = &hv->dev; 25362306a36Sopenharmony_ci unsigned long t; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci memset(msg, 0, sizeof(struct synthvid_msg)); 25662306a36Sopenharmony_ci msg->vid_hdr.type = SYNTHVID_VRAM_LOCATION; 25762306a36Sopenharmony_ci msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 25862306a36Sopenharmony_ci sizeof(struct synthvid_vram_location); 25962306a36Sopenharmony_ci msg->vram.user_ctx = vram_pp; 26062306a36Sopenharmony_ci msg->vram.vram_gpa = vram_pp; 26162306a36Sopenharmony_ci msg->vram.is_vram_gpa_specified = 1; 26262306a36Sopenharmony_ci hyperv_sendpacket(hdev, msg); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT); 26562306a36Sopenharmony_ci if (!t) { 26662306a36Sopenharmony_ci drm_err(dev, "Time out on waiting vram location ack\n"); 26762306a36Sopenharmony_ci return -ETIMEDOUT; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci if (msg->vram_ack.user_ctx != vram_pp) { 27062306a36Sopenharmony_ci drm_err(dev, "Unable to set VRAM location\n"); 27162306a36Sopenharmony_ci return -ENODEV; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci return 0; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ciint hyperv_update_situation(struct hv_device *hdev, u8 active, u32 bpp, 27862306a36Sopenharmony_ci u32 w, u32 h, u32 pitch) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct synthvid_msg msg; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci memset(&msg, 0, sizeof(struct synthvid_msg)); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci msg.vid_hdr.type = SYNTHVID_SITUATION_UPDATE; 28562306a36Sopenharmony_ci msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 28662306a36Sopenharmony_ci sizeof(struct synthvid_situation_update); 28762306a36Sopenharmony_ci msg.situ.user_ctx = 0; 28862306a36Sopenharmony_ci msg.situ.video_output_count = 1; 28962306a36Sopenharmony_ci msg.situ.video_output[0].active = active; 29062306a36Sopenharmony_ci /* vram_offset should always be 0 */ 29162306a36Sopenharmony_ci msg.situ.video_output[0].vram_offset = 0; 29262306a36Sopenharmony_ci msg.situ.video_output[0].depth_bits = bpp; 29362306a36Sopenharmony_ci msg.situ.video_output[0].width_pixels = w; 29462306a36Sopenharmony_ci msg.situ.video_output[0].height_pixels = h; 29562306a36Sopenharmony_ci msg.situ.video_output[0].pitch_bytes = pitch; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci hyperv_sendpacket(hdev, &msg); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci/* 30362306a36Sopenharmony_ci * Hyper-V supports a hardware cursor feature. It's not used by Linux VM, 30462306a36Sopenharmony_ci * but the Hyper-V host still draws a point as an extra mouse pointer, 30562306a36Sopenharmony_ci * which is unwanted, especially when Xorg is running. 30662306a36Sopenharmony_ci * 30762306a36Sopenharmony_ci * The hyperv_fb driver uses synthvid_send_ptr() to hide the unwanted 30862306a36Sopenharmony_ci * pointer, by setting msg.ptr_pos.is_visible = 1 and setting the 30962306a36Sopenharmony_ci * msg.ptr_shape.data. Note: setting msg.ptr_pos.is_visible to 0 doesn't 31062306a36Sopenharmony_ci * work in tests. 31162306a36Sopenharmony_ci * 31262306a36Sopenharmony_ci * Copy synthvid_send_ptr() to hyperv_drm and rename it to 31362306a36Sopenharmony_ci * hyperv_hide_hw_ptr(). Note: hyperv_hide_hw_ptr() is also called in the 31462306a36Sopenharmony_ci * handler of the SYNTHVID_FEATURE_CHANGE event, otherwise the host still 31562306a36Sopenharmony_ci * draws an extra unwanted mouse pointer after the VM Connection window is 31662306a36Sopenharmony_ci * closed and reopened. 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_ciint hyperv_hide_hw_ptr(struct hv_device *hdev) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct synthvid_msg msg; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci memset(&msg, 0, sizeof(struct synthvid_msg)); 32362306a36Sopenharmony_ci msg.vid_hdr.type = SYNTHVID_POINTER_POSITION; 32462306a36Sopenharmony_ci msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 32562306a36Sopenharmony_ci sizeof(struct synthvid_pointer_position); 32662306a36Sopenharmony_ci msg.ptr_pos.is_visible = 1; 32762306a36Sopenharmony_ci msg.ptr_pos.video_output = 0; 32862306a36Sopenharmony_ci msg.ptr_pos.image_x = 0; 32962306a36Sopenharmony_ci msg.ptr_pos.image_y = 0; 33062306a36Sopenharmony_ci hyperv_sendpacket(hdev, &msg); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci memset(&msg, 0, sizeof(struct synthvid_msg)); 33362306a36Sopenharmony_ci msg.vid_hdr.type = SYNTHVID_POINTER_SHAPE; 33462306a36Sopenharmony_ci msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 33562306a36Sopenharmony_ci sizeof(struct synthvid_pointer_shape); 33662306a36Sopenharmony_ci msg.ptr_shape.part_idx = SYNTHVID_CURSOR_COMPLETE; 33762306a36Sopenharmony_ci msg.ptr_shape.is_argb = 1; 33862306a36Sopenharmony_ci msg.ptr_shape.width = 1; 33962306a36Sopenharmony_ci msg.ptr_shape.height = 1; 34062306a36Sopenharmony_ci msg.ptr_shape.hot_x = 0; 34162306a36Sopenharmony_ci msg.ptr_shape.hot_y = 0; 34262306a36Sopenharmony_ci msg.ptr_shape.data[0] = 0; 34362306a36Sopenharmony_ci msg.ptr_shape.data[1] = 1; 34462306a36Sopenharmony_ci msg.ptr_shape.data[2] = 1; 34562306a36Sopenharmony_ci msg.ptr_shape.data[3] = 1; 34662306a36Sopenharmony_ci hyperv_sendpacket(hdev, &msg); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return 0; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ciint hyperv_update_dirt(struct hv_device *hdev, struct drm_rect *rect) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct hyperv_drm_device *hv = hv_get_drvdata(hdev); 35462306a36Sopenharmony_ci struct synthvid_msg msg; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (!hv->dirt_needed) 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci memset(&msg, 0, sizeof(struct synthvid_msg)); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci msg.vid_hdr.type = SYNTHVID_DIRT; 36262306a36Sopenharmony_ci msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 36362306a36Sopenharmony_ci sizeof(struct synthvid_dirt); 36462306a36Sopenharmony_ci msg.dirt.video_output = 0; 36562306a36Sopenharmony_ci msg.dirt.dirt_count = 1; 36662306a36Sopenharmony_ci msg.dirt.rect[0].x1 = rect->x1; 36762306a36Sopenharmony_ci msg.dirt.rect[0].y1 = rect->y1; 36862306a36Sopenharmony_ci msg.dirt.rect[0].x2 = rect->x2; 36962306a36Sopenharmony_ci msg.dirt.rect[0].y2 = rect->y2; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci hyperv_sendpacket(hdev, &msg); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return 0; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic int hyperv_get_supported_resolution(struct hv_device *hdev) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct hyperv_drm_device *hv = hv_get_drvdata(hdev); 37962306a36Sopenharmony_ci struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf; 38062306a36Sopenharmony_ci struct drm_device *dev = &hv->dev; 38162306a36Sopenharmony_ci unsigned long t; 38262306a36Sopenharmony_ci u8 index; 38362306a36Sopenharmony_ci int i; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci memset(msg, 0, sizeof(struct synthvid_msg)); 38662306a36Sopenharmony_ci msg->vid_hdr.type = SYNTHVID_RESOLUTION_REQUEST; 38762306a36Sopenharmony_ci msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 38862306a36Sopenharmony_ci sizeof(struct synthvid_supported_resolution_req); 38962306a36Sopenharmony_ci msg->resolution_req.maximum_resolution_count = 39062306a36Sopenharmony_ci SYNTHVID_MAX_RESOLUTION_COUNT; 39162306a36Sopenharmony_ci hyperv_sendpacket(hdev, msg); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT); 39462306a36Sopenharmony_ci if (!t) { 39562306a36Sopenharmony_ci drm_err(dev, "Time out on waiting resolution response\n"); 39662306a36Sopenharmony_ci return -ETIMEDOUT; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (msg->resolution_resp.resolution_count == 0) { 40062306a36Sopenharmony_ci drm_err(dev, "No supported resolutions\n"); 40162306a36Sopenharmony_ci return -ENODEV; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci index = msg->resolution_resp.default_resolution_index; 40562306a36Sopenharmony_ci if (index >= msg->resolution_resp.resolution_count) { 40662306a36Sopenharmony_ci drm_err(dev, "Invalid resolution index: %d\n", index); 40762306a36Sopenharmony_ci return -ENODEV; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci for (i = 0; i < msg->resolution_resp.resolution_count; i++) { 41162306a36Sopenharmony_ci hv->screen_width_max = max_t(u32, hv->screen_width_max, 41262306a36Sopenharmony_ci msg->resolution_resp.supported_resolution[i].width); 41362306a36Sopenharmony_ci hv->screen_height_max = max_t(u32, hv->screen_height_max, 41462306a36Sopenharmony_ci msg->resolution_resp.supported_resolution[i].height); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci hv->preferred_width = 41862306a36Sopenharmony_ci msg->resolution_resp.supported_resolution[index].width; 41962306a36Sopenharmony_ci hv->preferred_height = 42062306a36Sopenharmony_ci msg->resolution_resp.supported_resolution[index].height; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci return 0; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic void hyperv_receive_sub(struct hv_device *hdev) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct hyperv_drm_device *hv = hv_get_drvdata(hdev); 42862306a36Sopenharmony_ci struct synthvid_msg *msg; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (!hv) 43162306a36Sopenharmony_ci return; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci msg = (struct synthvid_msg *)hv->recv_buf; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* Complete the wait event */ 43662306a36Sopenharmony_ci if (msg->vid_hdr.type == SYNTHVID_VERSION_RESPONSE || 43762306a36Sopenharmony_ci msg->vid_hdr.type == SYNTHVID_RESOLUTION_RESPONSE || 43862306a36Sopenharmony_ci msg->vid_hdr.type == SYNTHVID_VRAM_LOCATION_ACK) { 43962306a36Sopenharmony_ci memcpy(hv->init_buf, msg, VMBUS_MAX_PACKET_SIZE); 44062306a36Sopenharmony_ci complete(&hv->wait); 44162306a36Sopenharmony_ci return; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (msg->vid_hdr.type == SYNTHVID_FEATURE_CHANGE) { 44562306a36Sopenharmony_ci hv->dirt_needed = msg->feature_chg.is_dirt_needed; 44662306a36Sopenharmony_ci if (hv->dirt_needed) 44762306a36Sopenharmony_ci hyperv_hide_hw_ptr(hv->hdev); 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic void hyperv_receive(void *ctx) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct hv_device *hdev = ctx; 45462306a36Sopenharmony_ci struct hyperv_drm_device *hv = hv_get_drvdata(hdev); 45562306a36Sopenharmony_ci struct synthvid_msg *recv_buf; 45662306a36Sopenharmony_ci u32 bytes_recvd; 45762306a36Sopenharmony_ci u64 req_id; 45862306a36Sopenharmony_ci int ret; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (!hv) 46162306a36Sopenharmony_ci return; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci recv_buf = (struct synthvid_msg *)hv->recv_buf; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci do { 46662306a36Sopenharmony_ci ret = vmbus_recvpacket(hdev->channel, recv_buf, 46762306a36Sopenharmony_ci VMBUS_MAX_PACKET_SIZE, 46862306a36Sopenharmony_ci &bytes_recvd, &req_id); 46962306a36Sopenharmony_ci if (bytes_recvd > 0 && 47062306a36Sopenharmony_ci recv_buf->pipe_hdr.type == PIPE_MSG_DATA) 47162306a36Sopenharmony_ci hyperv_receive_sub(hdev); 47262306a36Sopenharmony_ci } while (bytes_recvd > 0 && ret == 0); 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ciint hyperv_connect_vsp(struct hv_device *hdev) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct hyperv_drm_device *hv = hv_get_drvdata(hdev); 47862306a36Sopenharmony_ci struct drm_device *dev = &hv->dev; 47962306a36Sopenharmony_ci int ret; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci ret = vmbus_open(hdev->channel, VMBUS_RING_BUFSIZE, VMBUS_RING_BUFSIZE, 48262306a36Sopenharmony_ci NULL, 0, hyperv_receive, hdev); 48362306a36Sopenharmony_ci if (ret) { 48462306a36Sopenharmony_ci drm_err(dev, "Unable to open vmbus channel\n"); 48562306a36Sopenharmony_ci return ret; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* Negotiate the protocol version with host */ 48962306a36Sopenharmony_ci switch (vmbus_proto_version) { 49062306a36Sopenharmony_ci case VERSION_WIN10: 49162306a36Sopenharmony_ci case VERSION_WIN10_V5: 49262306a36Sopenharmony_ci ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN10); 49362306a36Sopenharmony_ci if (!ret) 49462306a36Sopenharmony_ci break; 49562306a36Sopenharmony_ci fallthrough; 49662306a36Sopenharmony_ci case VERSION_WIN8: 49762306a36Sopenharmony_ci case VERSION_WIN8_1: 49862306a36Sopenharmony_ci ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN8); 49962306a36Sopenharmony_ci break; 50062306a36Sopenharmony_ci default: 50162306a36Sopenharmony_ci ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN10); 50262306a36Sopenharmony_ci break; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (ret) { 50662306a36Sopenharmony_ci drm_err(dev, "Synthetic video device version not accepted %d\n", ret); 50762306a36Sopenharmony_ci goto error; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci hv->screen_depth = SYNTHVID_DEPTH_WIN8; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (hyperv_version_ge(hv->synthvid_version, SYNTHVID_VERSION_WIN10)) { 51362306a36Sopenharmony_ci ret = hyperv_get_supported_resolution(hdev); 51462306a36Sopenharmony_ci if (ret) 51562306a36Sopenharmony_ci drm_err(dev, "Failed to get supported resolution from host, use default\n"); 51662306a36Sopenharmony_ci } else { 51762306a36Sopenharmony_ci hv->screen_width_max = SYNTHVID_WIDTH_WIN8; 51862306a36Sopenharmony_ci hv->screen_height_max = SYNTHVID_HEIGHT_WIN8; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci hv->mmio_megabytes = hdev->channel->offermsg.offer.mmio_megabytes; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci return 0; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cierror: 52662306a36Sopenharmony_ci vmbus_close(hdev->channel); 52762306a36Sopenharmony_ci return ret; 52862306a36Sopenharmony_ci} 529