Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright 2021 Microsoft
0004  *
0005  * Portions of this code is derived from hyperv_fb.c
0006  */
0007 
0008 #include <linux/hyperv.h>
0009 
0010 #include <drm/drm_print.h>
0011 #include <drm/drm_simple_kms_helper.h>
0012 
0013 #include "hyperv_drm.h"
0014 
0015 #define VMBUS_RING_BUFSIZE (256 * 1024)
0016 #define VMBUS_VSP_TIMEOUT (10 * HZ)
0017 
0018 #define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major))
0019 #define SYNTHVID_VER_GET_MAJOR(ver) (ver & 0x0000ffff)
0020 #define SYNTHVID_VER_GET_MINOR(ver) ((ver & 0xffff0000) >> 16)
0021 
0022 /* Support for VERSION_WIN7 is removed. #define is retained for reference. */
0023 #define SYNTHVID_VERSION_WIN7 SYNTHVID_VERSION(3, 0)
0024 #define SYNTHVID_VERSION_WIN8 SYNTHVID_VERSION(3, 2)
0025 #define SYNTHVID_VERSION_WIN10 SYNTHVID_VERSION(3, 5)
0026 
0027 #define SYNTHVID_DEPTH_WIN8 32
0028 #define SYNTHVID_WIDTH_WIN8 1600
0029 #define SYNTHVID_HEIGHT_WIN8 1200
0030 #define SYNTHVID_FB_SIZE_WIN8 (8 * 1024 * 1024)
0031 
0032 enum pipe_msg_type {
0033     PIPE_MSG_INVALID,
0034     PIPE_MSG_DATA,
0035     PIPE_MSG_MAX
0036 };
0037 
0038 enum synthvid_msg_type {
0039     SYNTHVID_ERROR          = 0,
0040     SYNTHVID_VERSION_REQUEST    = 1,
0041     SYNTHVID_VERSION_RESPONSE   = 2,
0042     SYNTHVID_VRAM_LOCATION      = 3,
0043     SYNTHVID_VRAM_LOCATION_ACK  = 4,
0044     SYNTHVID_SITUATION_UPDATE   = 5,
0045     SYNTHVID_SITUATION_UPDATE_ACK   = 6,
0046     SYNTHVID_POINTER_POSITION   = 7,
0047     SYNTHVID_POINTER_SHAPE      = 8,
0048     SYNTHVID_FEATURE_CHANGE     = 9,
0049     SYNTHVID_DIRT           = 10,
0050     SYNTHVID_RESOLUTION_REQUEST = 13,
0051     SYNTHVID_RESOLUTION_RESPONSE    = 14,
0052 
0053     SYNTHVID_MAX            = 15
0054 };
0055 
0056 struct pipe_msg_hdr {
0057     u32 type;
0058     u32 size; /* size of message after this field */
0059 } __packed;
0060 
0061 struct hvd_screen_info {
0062     u16 width;
0063     u16 height;
0064 } __packed;
0065 
0066 struct synthvid_msg_hdr {
0067     u32 type;
0068     u32 size;  /* size of this header + payload after this field */
0069 } __packed;
0070 
0071 struct synthvid_version_req {
0072     u32 version;
0073 } __packed;
0074 
0075 struct synthvid_version_resp {
0076     u32 version;
0077     u8 is_accepted;
0078     u8 max_video_outputs;
0079 } __packed;
0080 
0081 struct synthvid_vram_location {
0082     u64 user_ctx;
0083     u8 is_vram_gpa_specified;
0084     u64 vram_gpa;
0085 } __packed;
0086 
0087 struct synthvid_vram_location_ack {
0088     u64 user_ctx;
0089 } __packed;
0090 
0091 struct video_output_situation {
0092     u8 active;
0093     u32 vram_offset;
0094     u8 depth_bits;
0095     u32 width_pixels;
0096     u32 height_pixels;
0097     u32 pitch_bytes;
0098 } __packed;
0099 
0100 struct synthvid_situation_update {
0101     u64 user_ctx;
0102     u8 video_output_count;
0103     struct video_output_situation video_output[1];
0104 } __packed;
0105 
0106 struct synthvid_situation_update_ack {
0107     u64 user_ctx;
0108 } __packed;
0109 
0110 struct synthvid_pointer_position {
0111     u8 is_visible;
0112     u8 video_output;
0113     s32 image_x;
0114     s32 image_y;
0115 } __packed;
0116 
0117 #define SYNTHVID_CURSOR_MAX_X 96
0118 #define SYNTHVID_CURSOR_MAX_Y 96
0119 #define SYNTHVID_CURSOR_ARGB_PIXEL_SIZE 4
0120 #define SYNTHVID_CURSOR_MAX_SIZE (SYNTHVID_CURSOR_MAX_X * \
0121     SYNTHVID_CURSOR_MAX_Y * SYNTHVID_CURSOR_ARGB_PIXEL_SIZE)
0122 #define SYNTHVID_CURSOR_COMPLETE (-1)
0123 
0124 struct synthvid_pointer_shape {
0125     u8 part_idx;
0126     u8 is_argb;
0127     u32 width; /* SYNTHVID_CURSOR_MAX_X at most */
0128     u32 height; /* SYNTHVID_CURSOR_MAX_Y at most */
0129     u32 hot_x; /* hotspot relative to upper-left of pointer image */
0130     u32 hot_y;
0131     u8 data[4];
0132 } __packed;
0133 
0134 struct synthvid_feature_change {
0135     u8 is_dirt_needed;
0136     u8 is_ptr_pos_needed;
0137     u8 is_ptr_shape_needed;
0138     u8 is_situ_needed;
0139 } __packed;
0140 
0141 struct rect {
0142     s32 x1, y1; /* top left corner */
0143     s32 x2, y2; /* bottom right corner, exclusive */
0144 } __packed;
0145 
0146 struct synthvid_dirt {
0147     u8 video_output;
0148     u8 dirt_count;
0149     struct rect rect[1];
0150 } __packed;
0151 
0152 #define SYNTHVID_EDID_BLOCK_SIZE    128
0153 #define SYNTHVID_MAX_RESOLUTION_COUNT   64
0154 
0155 struct synthvid_supported_resolution_req {
0156     u8 maximum_resolution_count;
0157 } __packed;
0158 
0159 struct synthvid_supported_resolution_resp {
0160     u8 edid_block[SYNTHVID_EDID_BLOCK_SIZE];
0161     u8 resolution_count;
0162     u8 default_resolution_index;
0163     u8 is_standard;
0164     struct hvd_screen_info supported_resolution[SYNTHVID_MAX_RESOLUTION_COUNT];
0165 } __packed;
0166 
0167 struct synthvid_msg {
0168     struct pipe_msg_hdr pipe_hdr;
0169     struct synthvid_msg_hdr vid_hdr;
0170     union {
0171         struct synthvid_version_req ver_req;
0172         struct synthvid_version_resp ver_resp;
0173         struct synthvid_vram_location vram;
0174         struct synthvid_vram_location_ack vram_ack;
0175         struct synthvid_situation_update situ;
0176         struct synthvid_situation_update_ack situ_ack;
0177         struct synthvid_pointer_position ptr_pos;
0178         struct synthvid_pointer_shape ptr_shape;
0179         struct synthvid_feature_change feature_chg;
0180         struct synthvid_dirt dirt;
0181         struct synthvid_supported_resolution_req resolution_req;
0182         struct synthvid_supported_resolution_resp resolution_resp;
0183     };
0184 } __packed;
0185 
0186 static inline bool hyperv_version_ge(u32 ver1, u32 ver2)
0187 {
0188     if (SYNTHVID_VER_GET_MAJOR(ver1) > SYNTHVID_VER_GET_MAJOR(ver2) ||
0189         (SYNTHVID_VER_GET_MAJOR(ver1) == SYNTHVID_VER_GET_MAJOR(ver2) &&
0190          SYNTHVID_VER_GET_MINOR(ver1) >= SYNTHVID_VER_GET_MINOR(ver2)))
0191         return true;
0192 
0193     return false;
0194 }
0195 
0196 static inline int hyperv_sendpacket(struct hv_device *hdev, struct synthvid_msg *msg)
0197 {
0198     static atomic64_t request_id = ATOMIC64_INIT(0);
0199     struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
0200     int ret;
0201 
0202     msg->pipe_hdr.type = PIPE_MSG_DATA;
0203     msg->pipe_hdr.size = msg->vid_hdr.size;
0204 
0205     ret = vmbus_sendpacket(hdev->channel, msg,
0206                    msg->vid_hdr.size + sizeof(struct pipe_msg_hdr),
0207                    atomic64_inc_return(&request_id),
0208                    VM_PKT_DATA_INBAND, 0);
0209 
0210     if (ret)
0211         drm_err(&hv->dev, "Unable to send packet via vmbus\n");
0212 
0213     return ret;
0214 }
0215 
0216 static int hyperv_negotiate_version(struct hv_device *hdev, u32 ver)
0217 {
0218     struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
0219     struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf;
0220     struct drm_device *dev = &hv->dev;
0221     unsigned long t;
0222 
0223     memset(msg, 0, sizeof(struct synthvid_msg));
0224     msg->vid_hdr.type = SYNTHVID_VERSION_REQUEST;
0225     msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
0226         sizeof(struct synthvid_version_req);
0227     msg->ver_req.version = ver;
0228     hyperv_sendpacket(hdev, msg);
0229 
0230     t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT);
0231     if (!t) {
0232         drm_err(dev, "Time out on waiting version response\n");
0233         return -ETIMEDOUT;
0234     }
0235 
0236     if (!msg->ver_resp.is_accepted) {
0237         drm_err(dev, "Version request not accepted\n");
0238         return -ENODEV;
0239     }
0240 
0241     hv->synthvid_version = ver;
0242     drm_info(dev, "Synthvid Version major %d, minor %d\n",
0243          SYNTHVID_VER_GET_MAJOR(ver), SYNTHVID_VER_GET_MINOR(ver));
0244 
0245     return 0;
0246 }
0247 
0248 int hyperv_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp)
0249 {
0250     struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
0251     struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf;
0252     struct drm_device *dev = &hv->dev;
0253     unsigned long t;
0254 
0255     memset(msg, 0, sizeof(struct synthvid_msg));
0256     msg->vid_hdr.type = SYNTHVID_VRAM_LOCATION;
0257     msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
0258         sizeof(struct synthvid_vram_location);
0259     msg->vram.user_ctx = vram_pp;
0260     msg->vram.vram_gpa = vram_pp;
0261     msg->vram.is_vram_gpa_specified = 1;
0262     hyperv_sendpacket(hdev, msg);
0263 
0264     t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT);
0265     if (!t) {
0266         drm_err(dev, "Time out on waiting vram location ack\n");
0267         return -ETIMEDOUT;
0268     }
0269     if (msg->vram_ack.user_ctx != vram_pp) {
0270         drm_err(dev, "Unable to set VRAM location\n");
0271         return -ENODEV;
0272     }
0273 
0274     return 0;
0275 }
0276 
0277 int hyperv_update_situation(struct hv_device *hdev, u8 active, u32 bpp,
0278                 u32 w, u32 h, u32 pitch)
0279 {
0280     struct synthvid_msg msg;
0281 
0282     memset(&msg, 0, sizeof(struct synthvid_msg));
0283 
0284     msg.vid_hdr.type = SYNTHVID_SITUATION_UPDATE;
0285     msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
0286         sizeof(struct synthvid_situation_update);
0287     msg.situ.user_ctx = 0;
0288     msg.situ.video_output_count = 1;
0289     msg.situ.video_output[0].active = active;
0290     /* vram_offset should always be 0 */
0291     msg.situ.video_output[0].vram_offset = 0;
0292     msg.situ.video_output[0].depth_bits = bpp;
0293     msg.situ.video_output[0].width_pixels = w;
0294     msg.situ.video_output[0].height_pixels = h;
0295     msg.situ.video_output[0].pitch_bytes = pitch;
0296 
0297     hyperv_sendpacket(hdev, &msg);
0298 
0299     return 0;
0300 }
0301 
0302 /*
0303  * Hyper-V supports a hardware cursor feature. It's not used by Linux VM,
0304  * but the Hyper-V host still draws a point as an extra mouse pointer,
0305  * which is unwanted, especially when Xorg is running.
0306  *
0307  * The hyperv_fb driver uses synthvid_send_ptr() to hide the unwanted
0308  * pointer, by setting msg.ptr_pos.is_visible = 1 and setting the
0309  * msg.ptr_shape.data. Note: setting msg.ptr_pos.is_visible to 0 doesn't
0310  * work in tests.
0311  *
0312  * Copy synthvid_send_ptr() to hyperv_drm and rename it to
0313  * hyperv_hide_hw_ptr(). Note: hyperv_hide_hw_ptr() is also called in the
0314  * handler of the SYNTHVID_FEATURE_CHANGE event, otherwise the host still
0315  * draws an extra unwanted mouse pointer after the VM Connection window is
0316  * closed and reopened.
0317  */
0318 int hyperv_hide_hw_ptr(struct hv_device *hdev)
0319 {
0320     struct synthvid_msg msg;
0321 
0322     memset(&msg, 0, sizeof(struct synthvid_msg));
0323     msg.vid_hdr.type = SYNTHVID_POINTER_POSITION;
0324     msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
0325         sizeof(struct synthvid_pointer_position);
0326     msg.ptr_pos.is_visible = 1;
0327     msg.ptr_pos.video_output = 0;
0328     msg.ptr_pos.image_x = 0;
0329     msg.ptr_pos.image_y = 0;
0330     hyperv_sendpacket(hdev, &msg);
0331 
0332     memset(&msg, 0, sizeof(struct synthvid_msg));
0333     msg.vid_hdr.type = SYNTHVID_POINTER_SHAPE;
0334     msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
0335         sizeof(struct synthvid_pointer_shape);
0336     msg.ptr_shape.part_idx = SYNTHVID_CURSOR_COMPLETE;
0337     msg.ptr_shape.is_argb = 1;
0338     msg.ptr_shape.width = 1;
0339     msg.ptr_shape.height = 1;
0340     msg.ptr_shape.hot_x = 0;
0341     msg.ptr_shape.hot_y = 0;
0342     msg.ptr_shape.data[0] = 0;
0343     msg.ptr_shape.data[1] = 1;
0344     msg.ptr_shape.data[2] = 1;
0345     msg.ptr_shape.data[3] = 1;
0346     hyperv_sendpacket(hdev, &msg);
0347 
0348     return 0;
0349 }
0350 
0351 int hyperv_update_dirt(struct hv_device *hdev, struct drm_rect *rect)
0352 {
0353     struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
0354     struct synthvid_msg msg;
0355 
0356     if (!hv->dirt_needed)
0357         return 0;
0358 
0359     memset(&msg, 0, sizeof(struct synthvid_msg));
0360 
0361     msg.vid_hdr.type = SYNTHVID_DIRT;
0362     msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
0363         sizeof(struct synthvid_dirt);
0364     msg.dirt.video_output = 0;
0365     msg.dirt.dirt_count = 1;
0366     msg.dirt.rect[0].x1 = rect->x1;
0367     msg.dirt.rect[0].y1 = rect->y1;
0368     msg.dirt.rect[0].x2 = rect->x2;
0369     msg.dirt.rect[0].y2 = rect->y2;
0370 
0371     hyperv_sendpacket(hdev, &msg);
0372 
0373     return 0;
0374 }
0375 
0376 static int hyperv_get_supported_resolution(struct hv_device *hdev)
0377 {
0378     struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
0379     struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf;
0380     struct drm_device *dev = &hv->dev;
0381     unsigned long t;
0382     u8 index;
0383     int i;
0384 
0385     memset(msg, 0, sizeof(struct synthvid_msg));
0386     msg->vid_hdr.type = SYNTHVID_RESOLUTION_REQUEST;
0387     msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
0388         sizeof(struct synthvid_supported_resolution_req);
0389     msg->resolution_req.maximum_resolution_count =
0390         SYNTHVID_MAX_RESOLUTION_COUNT;
0391     hyperv_sendpacket(hdev, msg);
0392 
0393     t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT);
0394     if (!t) {
0395         drm_err(dev, "Time out on waiting resolution response\n");
0396         return -ETIMEDOUT;
0397     }
0398 
0399     if (msg->resolution_resp.resolution_count == 0) {
0400         drm_err(dev, "No supported resolutions\n");
0401         return -ENODEV;
0402     }
0403 
0404     index = msg->resolution_resp.default_resolution_index;
0405     if (index >= msg->resolution_resp.resolution_count) {
0406         drm_err(dev, "Invalid resolution index: %d\n", index);
0407         return -ENODEV;
0408     }
0409 
0410     for (i = 0; i < msg->resolution_resp.resolution_count; i++) {
0411         hv->screen_width_max = max_t(u32, hv->screen_width_max,
0412             msg->resolution_resp.supported_resolution[i].width);
0413         hv->screen_height_max = max_t(u32, hv->screen_height_max,
0414             msg->resolution_resp.supported_resolution[i].height);
0415     }
0416 
0417     hv->preferred_width =
0418         msg->resolution_resp.supported_resolution[index].width;
0419     hv->preferred_height =
0420         msg->resolution_resp.supported_resolution[index].height;
0421 
0422     return 0;
0423 }
0424 
0425 static void hyperv_receive_sub(struct hv_device *hdev)
0426 {
0427     struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
0428     struct synthvid_msg *msg;
0429 
0430     if (!hv)
0431         return;
0432 
0433     msg = (struct synthvid_msg *)hv->recv_buf;
0434 
0435     /* Complete the wait event */
0436     if (msg->vid_hdr.type == SYNTHVID_VERSION_RESPONSE ||
0437         msg->vid_hdr.type == SYNTHVID_RESOLUTION_RESPONSE ||
0438         msg->vid_hdr.type == SYNTHVID_VRAM_LOCATION_ACK) {
0439         memcpy(hv->init_buf, msg, VMBUS_MAX_PACKET_SIZE);
0440         complete(&hv->wait);
0441         return;
0442     }
0443 
0444     if (msg->vid_hdr.type == SYNTHVID_FEATURE_CHANGE) {
0445         hv->dirt_needed = msg->feature_chg.is_dirt_needed;
0446         if (hv->dirt_needed)
0447             hyperv_hide_hw_ptr(hv->hdev);
0448     }
0449 }
0450 
0451 static void hyperv_receive(void *ctx)
0452 {
0453     struct hv_device *hdev = ctx;
0454     struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
0455     struct synthvid_msg *recv_buf;
0456     u32 bytes_recvd;
0457     u64 req_id;
0458     int ret;
0459 
0460     if (!hv)
0461         return;
0462 
0463     recv_buf = (struct synthvid_msg *)hv->recv_buf;
0464 
0465     do {
0466         ret = vmbus_recvpacket(hdev->channel, recv_buf,
0467                        VMBUS_MAX_PACKET_SIZE,
0468                        &bytes_recvd, &req_id);
0469         if (bytes_recvd > 0 &&
0470             recv_buf->pipe_hdr.type == PIPE_MSG_DATA)
0471             hyperv_receive_sub(hdev);
0472     } while (bytes_recvd > 0 && ret == 0);
0473 }
0474 
0475 int hyperv_connect_vsp(struct hv_device *hdev)
0476 {
0477     struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
0478     struct drm_device *dev = &hv->dev;
0479     int ret;
0480 
0481     ret = vmbus_open(hdev->channel, VMBUS_RING_BUFSIZE, VMBUS_RING_BUFSIZE,
0482              NULL, 0, hyperv_receive, hdev);
0483     if (ret) {
0484         drm_err(dev, "Unable to open vmbus channel\n");
0485         return ret;
0486     }
0487 
0488     /* Negotiate the protocol version with host */
0489     switch (vmbus_proto_version) {
0490     case VERSION_WIN10:
0491     case VERSION_WIN10_V5:
0492         ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN10);
0493         if (!ret)
0494             break;
0495         fallthrough;
0496     case VERSION_WIN8:
0497     case VERSION_WIN8_1:
0498         ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN8);
0499         break;
0500     default:
0501         ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN10);
0502         break;
0503     }
0504 
0505     if (ret) {
0506         drm_err(dev, "Synthetic video device version not accepted %d\n", ret);
0507         goto error;
0508     }
0509 
0510     hv->screen_depth = SYNTHVID_DEPTH_WIN8;
0511 
0512     if (hyperv_version_ge(hv->synthvid_version, SYNTHVID_VERSION_WIN10)) {
0513         ret = hyperv_get_supported_resolution(hdev);
0514         if (ret)
0515             drm_err(dev, "Failed to get supported resolution from host, use default\n");
0516     } else {
0517         hv->screen_width_max = SYNTHVID_WIDTH_WIN8;
0518         hv->screen_height_max = SYNTHVID_HEIGHT_WIN8;
0519     }
0520 
0521     hv->mmio_megabytes = hdev->channel->offermsg.offer.mmio_megabytes;
0522 
0523     return 0;
0524 
0525 error:
0526     vmbus_close(hdev->channel);
0527     return ret;
0528 }