18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2012, Microsoft Corporation. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: 68c2ecf20Sopenharmony_ci * Haiyang Zhang <haiyangz@microsoft.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* 108c2ecf20Sopenharmony_ci * Hyper-V Synthetic Video Frame Buffer Driver 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This is the driver for the Hyper-V Synthetic Video, which supports 138c2ecf20Sopenharmony_ci * screen resolution up to Full HD 1920x1080 with 32 bit color on Windows 148c2ecf20Sopenharmony_ci * Server 2012, and 1600x1200 with 16 bit color on Windows Server 2008 R2 158c2ecf20Sopenharmony_ci * or earlier. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * It also solves the double mouse cursor issue of the emulated video mode. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * The default screen resolution is 1152x864, which may be changed by a 208c2ecf20Sopenharmony_ci * kernel parameter: 218c2ecf20Sopenharmony_ci * video=hyperv_fb:<width>x<height> 228c2ecf20Sopenharmony_ci * For example: video=hyperv_fb:1280x1024 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * Portrait orientation is also supported: 258c2ecf20Sopenharmony_ci * For example: video=hyperv_fb:864x1152 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * When a Windows 10 RS5+ host is used, the virtual machine screen 288c2ecf20Sopenharmony_ci * resolution is obtained from the host. The "video=hyperv_fb" option is 298c2ecf20Sopenharmony_ci * not needed, but still can be used to overwrite what the host specifies. 308c2ecf20Sopenharmony_ci * The VM resolution on the host could be set by executing the powershell 318c2ecf20Sopenharmony_ci * "set-vmvideo" command. For example 328c2ecf20Sopenharmony_ci * set-vmvideo -vmname name -horizontalresolution:1920 \ 338c2ecf20Sopenharmony_ci * -verticalresolution:1200 -resolutiontype single 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * Gen 1 VMs also support direct using VM's physical memory for framebuffer. 368c2ecf20Sopenharmony_ci * It could improve the efficiency and performance for framebuffer and VM. 378c2ecf20Sopenharmony_ci * This requires to allocate contiguous physical memory from Linux kernel's 388c2ecf20Sopenharmony_ci * CMA memory allocator. To enable this, supply a kernel parameter to give 398c2ecf20Sopenharmony_ci * enough memory space to CMA allocator for framebuffer. For example: 408c2ecf20Sopenharmony_ci * cma=130m 418c2ecf20Sopenharmony_ci * This gives 130MB memory to CMA allocator that can be allocated to 428c2ecf20Sopenharmony_ci * framebuffer. For reference, 8K resolution (7680x4320) takes about 438c2ecf20Sopenharmony_ci * 127MB memory. 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include <linux/module.h> 498c2ecf20Sopenharmony_ci#include <linux/kernel.h> 508c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 518c2ecf20Sopenharmony_ci#include <linux/init.h> 528c2ecf20Sopenharmony_ci#include <linux/completion.h> 538c2ecf20Sopenharmony_ci#include <linux/fb.h> 548c2ecf20Sopenharmony_ci#include <linux/pci.h> 558c2ecf20Sopenharmony_ci#include <linux/efi.h> 568c2ecf20Sopenharmony_ci#include <linux/console.h> 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#include <linux/hyperv.h> 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* Hyper-V Synthetic Video Protocol definitions and structures */ 628c2ecf20Sopenharmony_ci#define MAX_VMBUS_PKT_SIZE 0x4000 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major)) 658c2ecf20Sopenharmony_ci#define SYNTHVID_VERSION_WIN7 SYNTHVID_VERSION(3, 0) 668c2ecf20Sopenharmony_ci#define SYNTHVID_VERSION_WIN8 SYNTHVID_VERSION(3, 2) 678c2ecf20Sopenharmony_ci#define SYNTHVID_VERSION_WIN10 SYNTHVID_VERSION(3, 5) 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define SYNTHVID_VER_GET_MAJOR(ver) (ver & 0x0000ffff) 708c2ecf20Sopenharmony_ci#define SYNTHVID_VER_GET_MINOR(ver) ((ver & 0xffff0000) >> 16) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define SYNTHVID_DEPTH_WIN7 16 738c2ecf20Sopenharmony_ci#define SYNTHVID_DEPTH_WIN8 32 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define SYNTHVID_FB_SIZE_WIN7 (4 * 1024 * 1024) 768c2ecf20Sopenharmony_ci#define SYNTHVID_WIDTH_MAX_WIN7 1600 778c2ecf20Sopenharmony_ci#define SYNTHVID_HEIGHT_MAX_WIN7 1200 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define SYNTHVID_FB_SIZE_WIN8 (8 * 1024 * 1024) 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#define PCI_VENDOR_ID_MICROSOFT 0x1414 828c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_HYPERV_VIDEO 0x5353 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cienum pipe_msg_type { 868c2ecf20Sopenharmony_ci PIPE_MSG_INVALID, 878c2ecf20Sopenharmony_ci PIPE_MSG_DATA, 888c2ecf20Sopenharmony_ci PIPE_MSG_MAX 898c2ecf20Sopenharmony_ci}; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistruct pipe_msg_hdr { 928c2ecf20Sopenharmony_ci u32 type; 938c2ecf20Sopenharmony_ci u32 size; /* size of message after this field */ 948c2ecf20Sopenharmony_ci} __packed; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cienum synthvid_msg_type { 988c2ecf20Sopenharmony_ci SYNTHVID_ERROR = 0, 998c2ecf20Sopenharmony_ci SYNTHVID_VERSION_REQUEST = 1, 1008c2ecf20Sopenharmony_ci SYNTHVID_VERSION_RESPONSE = 2, 1018c2ecf20Sopenharmony_ci SYNTHVID_VRAM_LOCATION = 3, 1028c2ecf20Sopenharmony_ci SYNTHVID_VRAM_LOCATION_ACK = 4, 1038c2ecf20Sopenharmony_ci SYNTHVID_SITUATION_UPDATE = 5, 1048c2ecf20Sopenharmony_ci SYNTHVID_SITUATION_UPDATE_ACK = 6, 1058c2ecf20Sopenharmony_ci SYNTHVID_POINTER_POSITION = 7, 1068c2ecf20Sopenharmony_ci SYNTHVID_POINTER_SHAPE = 8, 1078c2ecf20Sopenharmony_ci SYNTHVID_FEATURE_CHANGE = 9, 1088c2ecf20Sopenharmony_ci SYNTHVID_DIRT = 10, 1098c2ecf20Sopenharmony_ci SYNTHVID_RESOLUTION_REQUEST = 13, 1108c2ecf20Sopenharmony_ci SYNTHVID_RESOLUTION_RESPONSE = 14, 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci SYNTHVID_MAX = 15 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci#define SYNTHVID_EDID_BLOCK_SIZE 128 1168c2ecf20Sopenharmony_ci#define SYNTHVID_MAX_RESOLUTION_COUNT 64 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistruct hvd_screen_info { 1198c2ecf20Sopenharmony_ci u16 width; 1208c2ecf20Sopenharmony_ci u16 height; 1218c2ecf20Sopenharmony_ci} __packed; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistruct synthvid_msg_hdr { 1248c2ecf20Sopenharmony_ci u32 type; 1258c2ecf20Sopenharmony_ci u32 size; /* size of this header + payload after this field*/ 1268c2ecf20Sopenharmony_ci} __packed; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistruct synthvid_version_req { 1298c2ecf20Sopenharmony_ci u32 version; 1308c2ecf20Sopenharmony_ci} __packed; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistruct synthvid_version_resp { 1338c2ecf20Sopenharmony_ci u32 version; 1348c2ecf20Sopenharmony_ci u8 is_accepted; 1358c2ecf20Sopenharmony_ci u8 max_video_outputs; 1368c2ecf20Sopenharmony_ci} __packed; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistruct synthvid_supported_resolution_req { 1398c2ecf20Sopenharmony_ci u8 maximum_resolution_count; 1408c2ecf20Sopenharmony_ci} __packed; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistruct synthvid_supported_resolution_resp { 1438c2ecf20Sopenharmony_ci u8 edid_block[SYNTHVID_EDID_BLOCK_SIZE]; 1448c2ecf20Sopenharmony_ci u8 resolution_count; 1458c2ecf20Sopenharmony_ci u8 default_resolution_index; 1468c2ecf20Sopenharmony_ci u8 is_standard; 1478c2ecf20Sopenharmony_ci struct hvd_screen_info 1488c2ecf20Sopenharmony_ci supported_resolution[SYNTHVID_MAX_RESOLUTION_COUNT]; 1498c2ecf20Sopenharmony_ci} __packed; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistruct synthvid_vram_location { 1528c2ecf20Sopenharmony_ci u64 user_ctx; 1538c2ecf20Sopenharmony_ci u8 is_vram_gpa_specified; 1548c2ecf20Sopenharmony_ci u64 vram_gpa; 1558c2ecf20Sopenharmony_ci} __packed; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistruct synthvid_vram_location_ack { 1588c2ecf20Sopenharmony_ci u64 user_ctx; 1598c2ecf20Sopenharmony_ci} __packed; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistruct video_output_situation { 1628c2ecf20Sopenharmony_ci u8 active; 1638c2ecf20Sopenharmony_ci u32 vram_offset; 1648c2ecf20Sopenharmony_ci u8 depth_bits; 1658c2ecf20Sopenharmony_ci u32 width_pixels; 1668c2ecf20Sopenharmony_ci u32 height_pixels; 1678c2ecf20Sopenharmony_ci u32 pitch_bytes; 1688c2ecf20Sopenharmony_ci} __packed; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistruct synthvid_situation_update { 1718c2ecf20Sopenharmony_ci u64 user_ctx; 1728c2ecf20Sopenharmony_ci u8 video_output_count; 1738c2ecf20Sopenharmony_ci struct video_output_situation video_output[1]; 1748c2ecf20Sopenharmony_ci} __packed; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistruct synthvid_situation_update_ack { 1778c2ecf20Sopenharmony_ci u64 user_ctx; 1788c2ecf20Sopenharmony_ci} __packed; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistruct synthvid_pointer_position { 1818c2ecf20Sopenharmony_ci u8 is_visible; 1828c2ecf20Sopenharmony_ci u8 video_output; 1838c2ecf20Sopenharmony_ci s32 image_x; 1848c2ecf20Sopenharmony_ci s32 image_y; 1858c2ecf20Sopenharmony_ci} __packed; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci#define CURSOR_MAX_X 96 1898c2ecf20Sopenharmony_ci#define CURSOR_MAX_Y 96 1908c2ecf20Sopenharmony_ci#define CURSOR_ARGB_PIXEL_SIZE 4 1918c2ecf20Sopenharmony_ci#define CURSOR_MAX_SIZE (CURSOR_MAX_X * CURSOR_MAX_Y * CURSOR_ARGB_PIXEL_SIZE) 1928c2ecf20Sopenharmony_ci#define CURSOR_COMPLETE (-1) 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistruct synthvid_pointer_shape { 1958c2ecf20Sopenharmony_ci u8 part_idx; 1968c2ecf20Sopenharmony_ci u8 is_argb; 1978c2ecf20Sopenharmony_ci u32 width; /* CURSOR_MAX_X at most */ 1988c2ecf20Sopenharmony_ci u32 height; /* CURSOR_MAX_Y at most */ 1998c2ecf20Sopenharmony_ci u32 hot_x; /* hotspot relative to upper-left of pointer image */ 2008c2ecf20Sopenharmony_ci u32 hot_y; 2018c2ecf20Sopenharmony_ci u8 data[4]; 2028c2ecf20Sopenharmony_ci} __packed; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistruct synthvid_feature_change { 2058c2ecf20Sopenharmony_ci u8 is_dirt_needed; 2068c2ecf20Sopenharmony_ci u8 is_ptr_pos_needed; 2078c2ecf20Sopenharmony_ci u8 is_ptr_shape_needed; 2088c2ecf20Sopenharmony_ci u8 is_situ_needed; 2098c2ecf20Sopenharmony_ci} __packed; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistruct rect { 2128c2ecf20Sopenharmony_ci s32 x1, y1; /* top left corner */ 2138c2ecf20Sopenharmony_ci s32 x2, y2; /* bottom right corner, exclusive */ 2148c2ecf20Sopenharmony_ci} __packed; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistruct synthvid_dirt { 2178c2ecf20Sopenharmony_ci u8 video_output; 2188c2ecf20Sopenharmony_ci u8 dirt_count; 2198c2ecf20Sopenharmony_ci struct rect rect[1]; 2208c2ecf20Sopenharmony_ci} __packed; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistruct synthvid_msg { 2238c2ecf20Sopenharmony_ci struct pipe_msg_hdr pipe_hdr; 2248c2ecf20Sopenharmony_ci struct synthvid_msg_hdr vid_hdr; 2258c2ecf20Sopenharmony_ci union { 2268c2ecf20Sopenharmony_ci struct synthvid_version_req ver_req; 2278c2ecf20Sopenharmony_ci struct synthvid_version_resp ver_resp; 2288c2ecf20Sopenharmony_ci struct synthvid_vram_location vram; 2298c2ecf20Sopenharmony_ci struct synthvid_vram_location_ack vram_ack; 2308c2ecf20Sopenharmony_ci struct synthvid_situation_update situ; 2318c2ecf20Sopenharmony_ci struct synthvid_situation_update_ack situ_ack; 2328c2ecf20Sopenharmony_ci struct synthvid_pointer_position ptr_pos; 2338c2ecf20Sopenharmony_ci struct synthvid_pointer_shape ptr_shape; 2348c2ecf20Sopenharmony_ci struct synthvid_feature_change feature_chg; 2358c2ecf20Sopenharmony_ci struct synthvid_dirt dirt; 2368c2ecf20Sopenharmony_ci struct synthvid_supported_resolution_req resolution_req; 2378c2ecf20Sopenharmony_ci struct synthvid_supported_resolution_resp resolution_resp; 2388c2ecf20Sopenharmony_ci }; 2398c2ecf20Sopenharmony_ci} __packed; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* FB driver definitions and structures */ 2438c2ecf20Sopenharmony_ci#define HVFB_WIDTH 1152 /* default screen width */ 2448c2ecf20Sopenharmony_ci#define HVFB_HEIGHT 864 /* default screen height */ 2458c2ecf20Sopenharmony_ci#define HVFB_WIDTH_MIN 640 2468c2ecf20Sopenharmony_ci#define HVFB_HEIGHT_MIN 480 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci#define RING_BUFSIZE (256 * 1024) 2498c2ecf20Sopenharmony_ci#define VSP_TIMEOUT (10 * HZ) 2508c2ecf20Sopenharmony_ci#define HVFB_UPDATE_DELAY (HZ / 20) 2518c2ecf20Sopenharmony_ci#define HVFB_ONDEMAND_THROTTLE (HZ / 20) 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistruct hvfb_par { 2548c2ecf20Sopenharmony_ci struct fb_info *info; 2558c2ecf20Sopenharmony_ci struct resource *mem; 2568c2ecf20Sopenharmony_ci bool fb_ready; /* fb device is ready */ 2578c2ecf20Sopenharmony_ci struct completion wait; 2588c2ecf20Sopenharmony_ci u32 synthvid_version; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci struct delayed_work dwork; 2618c2ecf20Sopenharmony_ci bool update; 2628c2ecf20Sopenharmony_ci bool update_saved; /* The value of 'update' before hibernation */ 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci u32 pseudo_palette[16]; 2658c2ecf20Sopenharmony_ci u8 init_buf[MAX_VMBUS_PKT_SIZE]; 2668c2ecf20Sopenharmony_ci u8 recv_buf[MAX_VMBUS_PKT_SIZE]; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* If true, the VSC notifies the VSP on every framebuffer change */ 2698c2ecf20Sopenharmony_ci bool synchronous_fb; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* If true, need to copy from deferred IO mem to framebuffer mem */ 2728c2ecf20Sopenharmony_ci bool need_docopy; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci struct notifier_block hvfb_panic_nb; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* Memory for deferred IO and frame buffer itself */ 2778c2ecf20Sopenharmony_ci unsigned char *dio_vp; 2788c2ecf20Sopenharmony_ci unsigned char *mmio_vp; 2798c2ecf20Sopenharmony_ci phys_addr_t mmio_pp; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* Dirty rectangle, protected by delayed_refresh_lock */ 2828c2ecf20Sopenharmony_ci int x1, y1, x2, y2; 2838c2ecf20Sopenharmony_ci bool delayed_refresh; 2848c2ecf20Sopenharmony_ci spinlock_t delayed_refresh_lock; 2858c2ecf20Sopenharmony_ci}; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic uint screen_width = HVFB_WIDTH; 2888c2ecf20Sopenharmony_cistatic uint screen_height = HVFB_HEIGHT; 2898c2ecf20Sopenharmony_cistatic uint screen_depth; 2908c2ecf20Sopenharmony_cistatic uint screen_fb_size; 2918c2ecf20Sopenharmony_cistatic uint dio_fb_size; /* FB size for deferred IO */ 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci/* Send message to Hyper-V host */ 2948c2ecf20Sopenharmony_cistatic inline int synthvid_send(struct hv_device *hdev, 2958c2ecf20Sopenharmony_ci struct synthvid_msg *msg) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci static atomic64_t request_id = ATOMIC64_INIT(0); 2988c2ecf20Sopenharmony_ci int ret; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci msg->pipe_hdr.type = PIPE_MSG_DATA; 3018c2ecf20Sopenharmony_ci msg->pipe_hdr.size = msg->vid_hdr.size; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci ret = vmbus_sendpacket(hdev->channel, msg, 3048c2ecf20Sopenharmony_ci msg->vid_hdr.size + sizeof(struct pipe_msg_hdr), 3058c2ecf20Sopenharmony_ci atomic64_inc_return(&request_id), 3068c2ecf20Sopenharmony_ci VM_PKT_DATA_INBAND, 0); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (ret) 3098c2ecf20Sopenharmony_ci pr_err("Unable to send packet via vmbus\n"); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci return ret; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci/* Send screen resolution info to host */ 3168c2ecf20Sopenharmony_cistatic int synthvid_send_situ(struct hv_device *hdev) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 3198c2ecf20Sopenharmony_ci struct synthvid_msg msg; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (!info) 3228c2ecf20Sopenharmony_ci return -ENODEV; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(struct synthvid_msg)); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci msg.vid_hdr.type = SYNTHVID_SITUATION_UPDATE; 3278c2ecf20Sopenharmony_ci msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 3288c2ecf20Sopenharmony_ci sizeof(struct synthvid_situation_update); 3298c2ecf20Sopenharmony_ci msg.situ.user_ctx = 0; 3308c2ecf20Sopenharmony_ci msg.situ.video_output_count = 1; 3318c2ecf20Sopenharmony_ci msg.situ.video_output[0].active = 1; 3328c2ecf20Sopenharmony_ci msg.situ.video_output[0].vram_offset = 0; 3338c2ecf20Sopenharmony_ci msg.situ.video_output[0].depth_bits = info->var.bits_per_pixel; 3348c2ecf20Sopenharmony_ci msg.situ.video_output[0].width_pixels = info->var.xres; 3358c2ecf20Sopenharmony_ci msg.situ.video_output[0].height_pixels = info->var.yres; 3368c2ecf20Sopenharmony_ci msg.situ.video_output[0].pitch_bytes = info->fix.line_length; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci synthvid_send(hdev, &msg); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return 0; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci/* Send mouse pointer info to host */ 3448c2ecf20Sopenharmony_cistatic int synthvid_send_ptr(struct hv_device *hdev) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct synthvid_msg msg; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(struct synthvid_msg)); 3498c2ecf20Sopenharmony_ci msg.vid_hdr.type = SYNTHVID_POINTER_POSITION; 3508c2ecf20Sopenharmony_ci msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 3518c2ecf20Sopenharmony_ci sizeof(struct synthvid_pointer_position); 3528c2ecf20Sopenharmony_ci msg.ptr_pos.is_visible = 1; 3538c2ecf20Sopenharmony_ci msg.ptr_pos.video_output = 0; 3548c2ecf20Sopenharmony_ci msg.ptr_pos.image_x = 0; 3558c2ecf20Sopenharmony_ci msg.ptr_pos.image_y = 0; 3568c2ecf20Sopenharmony_ci synthvid_send(hdev, &msg); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(struct synthvid_msg)); 3598c2ecf20Sopenharmony_ci msg.vid_hdr.type = SYNTHVID_POINTER_SHAPE; 3608c2ecf20Sopenharmony_ci msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 3618c2ecf20Sopenharmony_ci sizeof(struct synthvid_pointer_shape); 3628c2ecf20Sopenharmony_ci msg.ptr_shape.part_idx = CURSOR_COMPLETE; 3638c2ecf20Sopenharmony_ci msg.ptr_shape.is_argb = 1; 3648c2ecf20Sopenharmony_ci msg.ptr_shape.width = 1; 3658c2ecf20Sopenharmony_ci msg.ptr_shape.height = 1; 3668c2ecf20Sopenharmony_ci msg.ptr_shape.hot_x = 0; 3678c2ecf20Sopenharmony_ci msg.ptr_shape.hot_y = 0; 3688c2ecf20Sopenharmony_ci msg.ptr_shape.data[0] = 0; 3698c2ecf20Sopenharmony_ci msg.ptr_shape.data[1] = 1; 3708c2ecf20Sopenharmony_ci msg.ptr_shape.data[2] = 1; 3718c2ecf20Sopenharmony_ci msg.ptr_shape.data[3] = 1; 3728c2ecf20Sopenharmony_ci synthvid_send(hdev, &msg); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci return 0; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/* Send updated screen area (dirty rectangle) location to host */ 3788c2ecf20Sopenharmony_cistatic int 3798c2ecf20Sopenharmony_cisynthvid_update(struct fb_info *info, int x1, int y1, int x2, int y2) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci struct hv_device *hdev = device_to_hv_device(info->device); 3828c2ecf20Sopenharmony_ci struct synthvid_msg msg; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(struct synthvid_msg)); 3858c2ecf20Sopenharmony_ci if (x2 == INT_MAX) 3868c2ecf20Sopenharmony_ci x2 = info->var.xres; 3878c2ecf20Sopenharmony_ci if (y2 == INT_MAX) 3888c2ecf20Sopenharmony_ci y2 = info->var.yres; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci msg.vid_hdr.type = SYNTHVID_DIRT; 3918c2ecf20Sopenharmony_ci msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 3928c2ecf20Sopenharmony_ci sizeof(struct synthvid_dirt); 3938c2ecf20Sopenharmony_ci msg.dirt.video_output = 0; 3948c2ecf20Sopenharmony_ci msg.dirt.dirt_count = 1; 3958c2ecf20Sopenharmony_ci msg.dirt.rect[0].x1 = (x1 > x2) ? 0 : x1; 3968c2ecf20Sopenharmony_ci msg.dirt.rect[0].y1 = (y1 > y2) ? 0 : y1; 3978c2ecf20Sopenharmony_ci msg.dirt.rect[0].x2 = 3988c2ecf20Sopenharmony_ci (x2 < x1 || x2 > info->var.xres) ? info->var.xres : x2; 3998c2ecf20Sopenharmony_ci msg.dirt.rect[0].y2 = 4008c2ecf20Sopenharmony_ci (y2 < y1 || y2 > info->var.yres) ? info->var.yres : y2; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci synthvid_send(hdev, &msg); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci return 0; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic void hvfb_docopy(struct hvfb_par *par, 4088c2ecf20Sopenharmony_ci unsigned long offset, 4098c2ecf20Sopenharmony_ci unsigned long size) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci if (!par || !par->mmio_vp || !par->dio_vp || !par->fb_ready || 4128c2ecf20Sopenharmony_ci size == 0 || offset >= dio_fb_size) 4138c2ecf20Sopenharmony_ci return; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (offset + size > dio_fb_size) 4168c2ecf20Sopenharmony_ci size = dio_fb_size - offset; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci memcpy(par->mmio_vp + offset, par->dio_vp + offset, size); 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci/* Deferred IO callback */ 4228c2ecf20Sopenharmony_cistatic void synthvid_deferred_io(struct fb_info *p, 4238c2ecf20Sopenharmony_ci struct list_head *pagelist) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct hvfb_par *par = p->par; 4268c2ecf20Sopenharmony_ci struct page *page; 4278c2ecf20Sopenharmony_ci unsigned long start, end; 4288c2ecf20Sopenharmony_ci int y1, y2, miny, maxy; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci miny = INT_MAX; 4318c2ecf20Sopenharmony_ci maxy = 0; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* 4348c2ecf20Sopenharmony_ci * Merge dirty pages. It is possible that last page cross 4358c2ecf20Sopenharmony_ci * over the end of frame buffer row yres. This is taken care of 4368c2ecf20Sopenharmony_ci * in synthvid_update function by clamping the y2 4378c2ecf20Sopenharmony_ci * value to yres. 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_ci list_for_each_entry(page, pagelist, lru) { 4408c2ecf20Sopenharmony_ci start = page->index << PAGE_SHIFT; 4418c2ecf20Sopenharmony_ci end = start + PAGE_SIZE - 1; 4428c2ecf20Sopenharmony_ci y1 = start / p->fix.line_length; 4438c2ecf20Sopenharmony_ci y2 = end / p->fix.line_length; 4448c2ecf20Sopenharmony_ci miny = min_t(int, miny, y1); 4458c2ecf20Sopenharmony_ci maxy = max_t(int, maxy, y2); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* Copy from dio space to mmio address */ 4488c2ecf20Sopenharmony_ci if (par->fb_ready && par->need_docopy) 4498c2ecf20Sopenharmony_ci hvfb_docopy(par, start, PAGE_SIZE); 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (par->fb_ready && par->update) 4538c2ecf20Sopenharmony_ci synthvid_update(p, 0, miny, p->var.xres, maxy + 1); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic struct fb_deferred_io synthvid_defio = { 4578c2ecf20Sopenharmony_ci .delay = HZ / 20, 4588c2ecf20Sopenharmony_ci .deferred_io = synthvid_deferred_io, 4598c2ecf20Sopenharmony_ci}; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci/* 4628c2ecf20Sopenharmony_ci * Actions on received messages from host: 4638c2ecf20Sopenharmony_ci * Complete the wait event. 4648c2ecf20Sopenharmony_ci * Or, reply with screen and cursor info. 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_cistatic void synthvid_recv_sub(struct hv_device *hdev) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 4698c2ecf20Sopenharmony_ci struct hvfb_par *par; 4708c2ecf20Sopenharmony_ci struct synthvid_msg *msg; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (!info) 4738c2ecf20Sopenharmony_ci return; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci par = info->par; 4768c2ecf20Sopenharmony_ci msg = (struct synthvid_msg *)par->recv_buf; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* Complete the wait event */ 4798c2ecf20Sopenharmony_ci if (msg->vid_hdr.type == SYNTHVID_VERSION_RESPONSE || 4808c2ecf20Sopenharmony_ci msg->vid_hdr.type == SYNTHVID_RESOLUTION_RESPONSE || 4818c2ecf20Sopenharmony_ci msg->vid_hdr.type == SYNTHVID_VRAM_LOCATION_ACK) { 4828c2ecf20Sopenharmony_ci memcpy(par->init_buf, msg, MAX_VMBUS_PKT_SIZE); 4838c2ecf20Sopenharmony_ci complete(&par->wait); 4848c2ecf20Sopenharmony_ci return; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* Reply with screen and cursor info */ 4888c2ecf20Sopenharmony_ci if (msg->vid_hdr.type == SYNTHVID_FEATURE_CHANGE) { 4898c2ecf20Sopenharmony_ci if (par->fb_ready) { 4908c2ecf20Sopenharmony_ci synthvid_send_ptr(hdev); 4918c2ecf20Sopenharmony_ci synthvid_send_situ(hdev); 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci par->update = msg->feature_chg.is_dirt_needed; 4958c2ecf20Sopenharmony_ci if (par->update) 4968c2ecf20Sopenharmony_ci schedule_delayed_work(&par->dwork, HVFB_UPDATE_DELAY); 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci/* Receive callback for messages from the host */ 5018c2ecf20Sopenharmony_cistatic void synthvid_receive(void *ctx) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci struct hv_device *hdev = ctx; 5048c2ecf20Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 5058c2ecf20Sopenharmony_ci struct hvfb_par *par; 5068c2ecf20Sopenharmony_ci struct synthvid_msg *recv_buf; 5078c2ecf20Sopenharmony_ci u32 bytes_recvd; 5088c2ecf20Sopenharmony_ci u64 req_id; 5098c2ecf20Sopenharmony_ci int ret; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (!info) 5128c2ecf20Sopenharmony_ci return; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci par = info->par; 5158c2ecf20Sopenharmony_ci recv_buf = (struct synthvid_msg *)par->recv_buf; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci do { 5188c2ecf20Sopenharmony_ci ret = vmbus_recvpacket(hdev->channel, recv_buf, 5198c2ecf20Sopenharmony_ci MAX_VMBUS_PKT_SIZE, 5208c2ecf20Sopenharmony_ci &bytes_recvd, &req_id); 5218c2ecf20Sopenharmony_ci if (bytes_recvd > 0 && 5228c2ecf20Sopenharmony_ci recv_buf->pipe_hdr.type == PIPE_MSG_DATA) 5238c2ecf20Sopenharmony_ci synthvid_recv_sub(hdev); 5248c2ecf20Sopenharmony_ci } while (bytes_recvd > 0 && ret == 0); 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci/* Check if the ver1 version is equal or greater than ver2 */ 5288c2ecf20Sopenharmony_cistatic inline bool synthvid_ver_ge(u32 ver1, u32 ver2) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci if (SYNTHVID_VER_GET_MAJOR(ver1) > SYNTHVID_VER_GET_MAJOR(ver2) || 5318c2ecf20Sopenharmony_ci (SYNTHVID_VER_GET_MAJOR(ver1) == SYNTHVID_VER_GET_MAJOR(ver2) && 5328c2ecf20Sopenharmony_ci SYNTHVID_VER_GET_MINOR(ver1) >= SYNTHVID_VER_GET_MINOR(ver2))) 5338c2ecf20Sopenharmony_ci return true; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci return false; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci/* Check synthetic video protocol version with the host */ 5398c2ecf20Sopenharmony_cistatic int synthvid_negotiate_ver(struct hv_device *hdev, u32 ver) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 5428c2ecf20Sopenharmony_ci struct hvfb_par *par = info->par; 5438c2ecf20Sopenharmony_ci struct synthvid_msg *msg = (struct synthvid_msg *)par->init_buf; 5448c2ecf20Sopenharmony_ci int ret = 0; 5458c2ecf20Sopenharmony_ci unsigned long t; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci memset(msg, 0, sizeof(struct synthvid_msg)); 5488c2ecf20Sopenharmony_ci msg->vid_hdr.type = SYNTHVID_VERSION_REQUEST; 5498c2ecf20Sopenharmony_ci msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 5508c2ecf20Sopenharmony_ci sizeof(struct synthvid_version_req); 5518c2ecf20Sopenharmony_ci msg->ver_req.version = ver; 5528c2ecf20Sopenharmony_ci synthvid_send(hdev, msg); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci t = wait_for_completion_timeout(&par->wait, VSP_TIMEOUT); 5558c2ecf20Sopenharmony_ci if (!t) { 5568c2ecf20Sopenharmony_ci pr_err("Time out on waiting version response\n"); 5578c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 5588c2ecf20Sopenharmony_ci goto out; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci if (!msg->ver_resp.is_accepted) { 5618c2ecf20Sopenharmony_ci ret = -ENODEV; 5628c2ecf20Sopenharmony_ci goto out; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci par->synthvid_version = ver; 5668c2ecf20Sopenharmony_ci pr_info("Synthvid Version major %d, minor %d\n", 5678c2ecf20Sopenharmony_ci SYNTHVID_VER_GET_MAJOR(ver), SYNTHVID_VER_GET_MINOR(ver)); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ciout: 5708c2ecf20Sopenharmony_ci return ret; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci/* Get current resolution from the host */ 5748c2ecf20Sopenharmony_cistatic int synthvid_get_supported_resolution(struct hv_device *hdev) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 5778c2ecf20Sopenharmony_ci struct hvfb_par *par = info->par; 5788c2ecf20Sopenharmony_ci struct synthvid_msg *msg = (struct synthvid_msg *)par->init_buf; 5798c2ecf20Sopenharmony_ci int ret = 0; 5808c2ecf20Sopenharmony_ci unsigned long t; 5818c2ecf20Sopenharmony_ci u8 index; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci memset(msg, 0, sizeof(struct synthvid_msg)); 5848c2ecf20Sopenharmony_ci msg->vid_hdr.type = SYNTHVID_RESOLUTION_REQUEST; 5858c2ecf20Sopenharmony_ci msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 5868c2ecf20Sopenharmony_ci sizeof(struct synthvid_supported_resolution_req); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci msg->resolution_req.maximum_resolution_count = 5898c2ecf20Sopenharmony_ci SYNTHVID_MAX_RESOLUTION_COUNT; 5908c2ecf20Sopenharmony_ci synthvid_send(hdev, msg); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci t = wait_for_completion_timeout(&par->wait, VSP_TIMEOUT); 5938c2ecf20Sopenharmony_ci if (!t) { 5948c2ecf20Sopenharmony_ci pr_err("Time out on waiting resolution response\n"); 5958c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 5968c2ecf20Sopenharmony_ci goto out; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (msg->resolution_resp.resolution_count == 0) { 6008c2ecf20Sopenharmony_ci pr_err("No supported resolutions\n"); 6018c2ecf20Sopenharmony_ci ret = -ENODEV; 6028c2ecf20Sopenharmony_ci goto out; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci index = msg->resolution_resp.default_resolution_index; 6068c2ecf20Sopenharmony_ci if (index >= msg->resolution_resp.resolution_count) { 6078c2ecf20Sopenharmony_ci pr_err("Invalid resolution index: %d\n", index); 6088c2ecf20Sopenharmony_ci ret = -ENODEV; 6098c2ecf20Sopenharmony_ci goto out; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci screen_width = 6138c2ecf20Sopenharmony_ci msg->resolution_resp.supported_resolution[index].width; 6148c2ecf20Sopenharmony_ci screen_height = 6158c2ecf20Sopenharmony_ci msg->resolution_resp.supported_resolution[index].height; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ciout: 6188c2ecf20Sopenharmony_ci return ret; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci/* Connect to VSP (Virtual Service Provider) on host */ 6228c2ecf20Sopenharmony_cistatic int synthvid_connect_vsp(struct hv_device *hdev) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 6258c2ecf20Sopenharmony_ci struct hvfb_par *par = info->par; 6268c2ecf20Sopenharmony_ci int ret; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci ret = vmbus_open(hdev->channel, RING_BUFSIZE, RING_BUFSIZE, 6298c2ecf20Sopenharmony_ci NULL, 0, synthvid_receive, hdev); 6308c2ecf20Sopenharmony_ci if (ret) { 6318c2ecf20Sopenharmony_ci pr_err("Unable to open vmbus channel\n"); 6328c2ecf20Sopenharmony_ci return ret; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci /* Negotiate the protocol version with host */ 6368c2ecf20Sopenharmony_ci switch (vmbus_proto_version) { 6378c2ecf20Sopenharmony_ci case VERSION_WIN10: 6388c2ecf20Sopenharmony_ci case VERSION_WIN10_V5: 6398c2ecf20Sopenharmony_ci ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN10); 6408c2ecf20Sopenharmony_ci if (!ret) 6418c2ecf20Sopenharmony_ci break; 6428c2ecf20Sopenharmony_ci fallthrough; 6438c2ecf20Sopenharmony_ci case VERSION_WIN8: 6448c2ecf20Sopenharmony_ci case VERSION_WIN8_1: 6458c2ecf20Sopenharmony_ci ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN8); 6468c2ecf20Sopenharmony_ci if (!ret) 6478c2ecf20Sopenharmony_ci break; 6488c2ecf20Sopenharmony_ci fallthrough; 6498c2ecf20Sopenharmony_ci case VERSION_WS2008: 6508c2ecf20Sopenharmony_ci case VERSION_WIN7: 6518c2ecf20Sopenharmony_ci ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN7); 6528c2ecf20Sopenharmony_ci break; 6538c2ecf20Sopenharmony_ci default: 6548c2ecf20Sopenharmony_ci ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN10); 6558c2ecf20Sopenharmony_ci break; 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (ret) { 6598c2ecf20Sopenharmony_ci pr_err("Synthetic video device version not accepted\n"); 6608c2ecf20Sopenharmony_ci goto error; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (par->synthvid_version == SYNTHVID_VERSION_WIN7) 6648c2ecf20Sopenharmony_ci screen_depth = SYNTHVID_DEPTH_WIN7; 6658c2ecf20Sopenharmony_ci else 6668c2ecf20Sopenharmony_ci screen_depth = SYNTHVID_DEPTH_WIN8; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci if (synthvid_ver_ge(par->synthvid_version, SYNTHVID_VERSION_WIN10)) { 6698c2ecf20Sopenharmony_ci ret = synthvid_get_supported_resolution(hdev); 6708c2ecf20Sopenharmony_ci if (ret) 6718c2ecf20Sopenharmony_ci pr_info("Failed to get supported resolution from host, use default\n"); 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci screen_fb_size = hdev->channel->offermsg.offer. 6758c2ecf20Sopenharmony_ci mmio_megabytes * 1024 * 1024; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci return 0; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cierror: 6808c2ecf20Sopenharmony_ci vmbus_close(hdev->channel); 6818c2ecf20Sopenharmony_ci return ret; 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci/* Send VRAM and Situation messages to the host */ 6858c2ecf20Sopenharmony_cistatic int synthvid_send_config(struct hv_device *hdev) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 6888c2ecf20Sopenharmony_ci struct hvfb_par *par = info->par; 6898c2ecf20Sopenharmony_ci struct synthvid_msg *msg = (struct synthvid_msg *)par->init_buf; 6908c2ecf20Sopenharmony_ci int ret = 0; 6918c2ecf20Sopenharmony_ci unsigned long t; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci /* Send VRAM location */ 6948c2ecf20Sopenharmony_ci memset(msg, 0, sizeof(struct synthvid_msg)); 6958c2ecf20Sopenharmony_ci msg->vid_hdr.type = SYNTHVID_VRAM_LOCATION; 6968c2ecf20Sopenharmony_ci msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 6978c2ecf20Sopenharmony_ci sizeof(struct synthvid_vram_location); 6988c2ecf20Sopenharmony_ci msg->vram.user_ctx = msg->vram.vram_gpa = par->mmio_pp; 6998c2ecf20Sopenharmony_ci msg->vram.is_vram_gpa_specified = 1; 7008c2ecf20Sopenharmony_ci synthvid_send(hdev, msg); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci t = wait_for_completion_timeout(&par->wait, VSP_TIMEOUT); 7038c2ecf20Sopenharmony_ci if (!t) { 7048c2ecf20Sopenharmony_ci pr_err("Time out on waiting vram location ack\n"); 7058c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 7068c2ecf20Sopenharmony_ci goto out; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci if (msg->vram_ack.user_ctx != par->mmio_pp) { 7098c2ecf20Sopenharmony_ci pr_err("Unable to set VRAM location\n"); 7108c2ecf20Sopenharmony_ci ret = -ENODEV; 7118c2ecf20Sopenharmony_ci goto out; 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci /* Send pointer and situation update */ 7158c2ecf20Sopenharmony_ci synthvid_send_ptr(hdev); 7168c2ecf20Sopenharmony_ci synthvid_send_situ(hdev); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ciout: 7198c2ecf20Sopenharmony_ci return ret; 7208c2ecf20Sopenharmony_ci} 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci/* 7248c2ecf20Sopenharmony_ci * Delayed work callback: 7258c2ecf20Sopenharmony_ci * It is scheduled to call whenever update request is received and it has 7268c2ecf20Sopenharmony_ci * not been called in last HVFB_ONDEMAND_THROTTLE time interval. 7278c2ecf20Sopenharmony_ci */ 7288c2ecf20Sopenharmony_cistatic void hvfb_update_work(struct work_struct *w) 7298c2ecf20Sopenharmony_ci{ 7308c2ecf20Sopenharmony_ci struct hvfb_par *par = container_of(w, struct hvfb_par, dwork.work); 7318c2ecf20Sopenharmony_ci struct fb_info *info = par->info; 7328c2ecf20Sopenharmony_ci unsigned long flags; 7338c2ecf20Sopenharmony_ci int x1, x2, y1, y2; 7348c2ecf20Sopenharmony_ci int j; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci spin_lock_irqsave(&par->delayed_refresh_lock, flags); 7378c2ecf20Sopenharmony_ci /* Reset the request flag */ 7388c2ecf20Sopenharmony_ci par->delayed_refresh = false; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci /* Store the dirty rectangle to local variables */ 7418c2ecf20Sopenharmony_ci x1 = par->x1; 7428c2ecf20Sopenharmony_ci x2 = par->x2; 7438c2ecf20Sopenharmony_ci y1 = par->y1; 7448c2ecf20Sopenharmony_ci y2 = par->y2; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci /* Clear dirty rectangle */ 7478c2ecf20Sopenharmony_ci par->x1 = par->y1 = INT_MAX; 7488c2ecf20Sopenharmony_ci par->x2 = par->y2 = 0; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&par->delayed_refresh_lock, flags); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci if (x1 > info->var.xres || x2 > info->var.xres || 7538c2ecf20Sopenharmony_ci y1 > info->var.yres || y2 > info->var.yres || x2 <= x1) 7548c2ecf20Sopenharmony_ci return; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* Copy the dirty rectangle to frame buffer memory */ 7578c2ecf20Sopenharmony_ci if (par->need_docopy) 7588c2ecf20Sopenharmony_ci for (j = y1; j < y2; j++) 7598c2ecf20Sopenharmony_ci hvfb_docopy(par, 7608c2ecf20Sopenharmony_ci j * info->fix.line_length + 7618c2ecf20Sopenharmony_ci (x1 * screen_depth / 8), 7628c2ecf20Sopenharmony_ci (x2 - x1) * screen_depth / 8); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* Refresh */ 7658c2ecf20Sopenharmony_ci if (par->fb_ready && par->update) 7668c2ecf20Sopenharmony_ci synthvid_update(info, x1, y1, x2, y2); 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci/* 7708c2ecf20Sopenharmony_ci * Control the on-demand refresh frequency. It schedules a delayed 7718c2ecf20Sopenharmony_ci * screen update if it has not yet. 7728c2ecf20Sopenharmony_ci */ 7738c2ecf20Sopenharmony_cistatic void hvfb_ondemand_refresh_throttle(struct hvfb_par *par, 7748c2ecf20Sopenharmony_ci int x1, int y1, int w, int h) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci unsigned long flags; 7778c2ecf20Sopenharmony_ci int x2 = x1 + w; 7788c2ecf20Sopenharmony_ci int y2 = y1 + h; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci spin_lock_irqsave(&par->delayed_refresh_lock, flags); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci /* Merge dirty rectangle */ 7838c2ecf20Sopenharmony_ci par->x1 = min_t(int, par->x1, x1); 7848c2ecf20Sopenharmony_ci par->y1 = min_t(int, par->y1, y1); 7858c2ecf20Sopenharmony_ci par->x2 = max_t(int, par->x2, x2); 7868c2ecf20Sopenharmony_ci par->y2 = max_t(int, par->y2, y2); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci /* Schedule a delayed screen update if not yet */ 7898c2ecf20Sopenharmony_ci if (par->delayed_refresh == false) { 7908c2ecf20Sopenharmony_ci schedule_delayed_work(&par->dwork, 7918c2ecf20Sopenharmony_ci HVFB_ONDEMAND_THROTTLE); 7928c2ecf20Sopenharmony_ci par->delayed_refresh = true; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&par->delayed_refresh_lock, flags); 7968c2ecf20Sopenharmony_ci} 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_cistatic int hvfb_on_panic(struct notifier_block *nb, 7998c2ecf20Sopenharmony_ci unsigned long e, void *p) 8008c2ecf20Sopenharmony_ci{ 8018c2ecf20Sopenharmony_ci struct hv_device *hdev; 8028c2ecf20Sopenharmony_ci struct hvfb_par *par; 8038c2ecf20Sopenharmony_ci struct fb_info *info; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci par = container_of(nb, struct hvfb_par, hvfb_panic_nb); 8068c2ecf20Sopenharmony_ci info = par->info; 8078c2ecf20Sopenharmony_ci hdev = device_to_hv_device(info->device); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci if (hv_ringbuffer_spinlock_busy(hdev->channel)) 8108c2ecf20Sopenharmony_ci return NOTIFY_DONE; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci par->synchronous_fb = true; 8138c2ecf20Sopenharmony_ci if (par->need_docopy) 8148c2ecf20Sopenharmony_ci hvfb_docopy(par, 0, dio_fb_size); 8158c2ecf20Sopenharmony_ci synthvid_update(info, 0, 0, INT_MAX, INT_MAX); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci return NOTIFY_DONE; 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci/* Framebuffer operation handlers */ 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_cistatic int hvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci if (var->xres < HVFB_WIDTH_MIN || var->yres < HVFB_HEIGHT_MIN || 8258c2ecf20Sopenharmony_ci var->xres > screen_width || var->yres > screen_height || 8268c2ecf20Sopenharmony_ci var->bits_per_pixel != screen_depth) 8278c2ecf20Sopenharmony_ci return -EINVAL; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci var->xres_virtual = var->xres; 8308c2ecf20Sopenharmony_ci var->yres_virtual = var->yres; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci return 0; 8338c2ecf20Sopenharmony_ci} 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_cistatic int hvfb_set_par(struct fb_info *info) 8368c2ecf20Sopenharmony_ci{ 8378c2ecf20Sopenharmony_ci struct hv_device *hdev = device_to_hv_device(info->device); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci return synthvid_send_situ(hdev); 8408c2ecf20Sopenharmony_ci} 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_cistatic inline u32 chan_to_field(u32 chan, struct fb_bitfield *bf) 8448c2ecf20Sopenharmony_ci{ 8458c2ecf20Sopenharmony_ci return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset; 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_cistatic int hvfb_setcolreg(unsigned regno, unsigned red, unsigned green, 8498c2ecf20Sopenharmony_ci unsigned blue, unsigned transp, struct fb_info *info) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci u32 *pal = info->pseudo_palette; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (regno > 15) 8548c2ecf20Sopenharmony_ci return -EINVAL; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci pal[regno] = chan_to_field(red, &info->var.red) 8578c2ecf20Sopenharmony_ci | chan_to_field(green, &info->var.green) 8588c2ecf20Sopenharmony_ci | chan_to_field(blue, &info->var.blue) 8598c2ecf20Sopenharmony_ci | chan_to_field(transp, &info->var.transp); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci return 0; 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_cistatic int hvfb_blank(int blank, struct fb_info *info) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci return 1; /* get fb_blank to set the colormap to all black */ 8678c2ecf20Sopenharmony_ci} 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_cistatic void hvfb_cfb_fillrect(struct fb_info *p, 8708c2ecf20Sopenharmony_ci const struct fb_fillrect *rect) 8718c2ecf20Sopenharmony_ci{ 8728c2ecf20Sopenharmony_ci struct hvfb_par *par = p->par; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci cfb_fillrect(p, rect); 8758c2ecf20Sopenharmony_ci if (par->synchronous_fb) 8768c2ecf20Sopenharmony_ci synthvid_update(p, 0, 0, INT_MAX, INT_MAX); 8778c2ecf20Sopenharmony_ci else 8788c2ecf20Sopenharmony_ci hvfb_ondemand_refresh_throttle(par, rect->dx, rect->dy, 8798c2ecf20Sopenharmony_ci rect->width, rect->height); 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic void hvfb_cfb_copyarea(struct fb_info *p, 8838c2ecf20Sopenharmony_ci const struct fb_copyarea *area) 8848c2ecf20Sopenharmony_ci{ 8858c2ecf20Sopenharmony_ci struct hvfb_par *par = p->par; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci cfb_copyarea(p, area); 8888c2ecf20Sopenharmony_ci if (par->synchronous_fb) 8898c2ecf20Sopenharmony_ci synthvid_update(p, 0, 0, INT_MAX, INT_MAX); 8908c2ecf20Sopenharmony_ci else 8918c2ecf20Sopenharmony_ci hvfb_ondemand_refresh_throttle(par, area->dx, area->dy, 8928c2ecf20Sopenharmony_ci area->width, area->height); 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_cistatic void hvfb_cfb_imageblit(struct fb_info *p, 8968c2ecf20Sopenharmony_ci const struct fb_image *image) 8978c2ecf20Sopenharmony_ci{ 8988c2ecf20Sopenharmony_ci struct hvfb_par *par = p->par; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci cfb_imageblit(p, image); 9018c2ecf20Sopenharmony_ci if (par->synchronous_fb) 9028c2ecf20Sopenharmony_ci synthvid_update(p, 0, 0, INT_MAX, INT_MAX); 9038c2ecf20Sopenharmony_ci else 9048c2ecf20Sopenharmony_ci hvfb_ondemand_refresh_throttle(par, image->dx, image->dy, 9058c2ecf20Sopenharmony_ci image->width, image->height); 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cistatic const struct fb_ops hvfb_ops = { 9098c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 9108c2ecf20Sopenharmony_ci .fb_check_var = hvfb_check_var, 9118c2ecf20Sopenharmony_ci .fb_set_par = hvfb_set_par, 9128c2ecf20Sopenharmony_ci .fb_setcolreg = hvfb_setcolreg, 9138c2ecf20Sopenharmony_ci .fb_fillrect = hvfb_cfb_fillrect, 9148c2ecf20Sopenharmony_ci .fb_copyarea = hvfb_cfb_copyarea, 9158c2ecf20Sopenharmony_ci .fb_imageblit = hvfb_cfb_imageblit, 9168c2ecf20Sopenharmony_ci .fb_blank = hvfb_blank, 9178c2ecf20Sopenharmony_ci}; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci/* Get options from kernel paramenter "video=" */ 9218c2ecf20Sopenharmony_cistatic void hvfb_get_option(struct fb_info *info) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci struct hvfb_par *par = info->par; 9248c2ecf20Sopenharmony_ci char *opt = NULL, *p; 9258c2ecf20Sopenharmony_ci uint x = 0, y = 0; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci if (fb_get_options(KBUILD_MODNAME, &opt) || !opt || !*opt) 9288c2ecf20Sopenharmony_ci return; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci p = strsep(&opt, "x"); 9318c2ecf20Sopenharmony_ci if (!*p || kstrtouint(p, 0, &x) || 9328c2ecf20Sopenharmony_ci !opt || !*opt || kstrtouint(opt, 0, &y)) { 9338c2ecf20Sopenharmony_ci pr_err("Screen option is invalid: skipped\n"); 9348c2ecf20Sopenharmony_ci return; 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (x < HVFB_WIDTH_MIN || y < HVFB_HEIGHT_MIN || 9388c2ecf20Sopenharmony_ci (synthvid_ver_ge(par->synthvid_version, SYNTHVID_VERSION_WIN10) && 9398c2ecf20Sopenharmony_ci (x * y * screen_depth / 8 > screen_fb_size)) || 9408c2ecf20Sopenharmony_ci (par->synthvid_version == SYNTHVID_VERSION_WIN8 && 9418c2ecf20Sopenharmony_ci x * y * screen_depth / 8 > SYNTHVID_FB_SIZE_WIN8) || 9428c2ecf20Sopenharmony_ci (par->synthvid_version == SYNTHVID_VERSION_WIN7 && 9438c2ecf20Sopenharmony_ci (x > SYNTHVID_WIDTH_MAX_WIN7 || y > SYNTHVID_HEIGHT_MAX_WIN7))) { 9448c2ecf20Sopenharmony_ci pr_err("Screen resolution option is out of range: skipped\n"); 9458c2ecf20Sopenharmony_ci return; 9468c2ecf20Sopenharmony_ci } 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci screen_width = x; 9498c2ecf20Sopenharmony_ci screen_height = y; 9508c2ecf20Sopenharmony_ci return; 9518c2ecf20Sopenharmony_ci} 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci/* 9548c2ecf20Sopenharmony_ci * Allocate enough contiguous physical memory. 9558c2ecf20Sopenharmony_ci * Return physical address if succeeded or -1 if failed. 9568c2ecf20Sopenharmony_ci */ 9578c2ecf20Sopenharmony_cistatic phys_addr_t hvfb_get_phymem(struct hv_device *hdev, 9588c2ecf20Sopenharmony_ci unsigned int request_size) 9598c2ecf20Sopenharmony_ci{ 9608c2ecf20Sopenharmony_ci struct page *page = NULL; 9618c2ecf20Sopenharmony_ci dma_addr_t dma_handle; 9628c2ecf20Sopenharmony_ci void *vmem; 9638c2ecf20Sopenharmony_ci phys_addr_t paddr = 0; 9648c2ecf20Sopenharmony_ci unsigned int order = get_order(request_size); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci if (request_size == 0) 9678c2ecf20Sopenharmony_ci return -1; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci if (order < MAX_ORDER) { 9708c2ecf20Sopenharmony_ci /* Call alloc_pages if the size is less than 2^MAX_ORDER */ 9718c2ecf20Sopenharmony_ci page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order); 9728c2ecf20Sopenharmony_ci if (!page) 9738c2ecf20Sopenharmony_ci return -1; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci paddr = (page_to_pfn(page) << PAGE_SHIFT); 9768c2ecf20Sopenharmony_ci } else { 9778c2ecf20Sopenharmony_ci /* Allocate from CMA */ 9788c2ecf20Sopenharmony_ci hdev->device.coherent_dma_mask = DMA_BIT_MASK(64); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci vmem = dma_alloc_coherent(&hdev->device, 9818c2ecf20Sopenharmony_ci round_up(request_size, PAGE_SIZE), 9828c2ecf20Sopenharmony_ci &dma_handle, 9838c2ecf20Sopenharmony_ci GFP_KERNEL | __GFP_NOWARN); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci if (!vmem) 9868c2ecf20Sopenharmony_ci return -1; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci paddr = virt_to_phys(vmem); 9898c2ecf20Sopenharmony_ci } 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci return paddr; 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci/* Release contiguous physical memory */ 9958c2ecf20Sopenharmony_cistatic void hvfb_release_phymem(struct hv_device *hdev, 9968c2ecf20Sopenharmony_ci phys_addr_t paddr, unsigned int size) 9978c2ecf20Sopenharmony_ci{ 9988c2ecf20Sopenharmony_ci unsigned int order = get_order(size); 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci if (order < MAX_ORDER) 10018c2ecf20Sopenharmony_ci __free_pages(pfn_to_page(paddr >> PAGE_SHIFT), order); 10028c2ecf20Sopenharmony_ci else 10038c2ecf20Sopenharmony_ci dma_free_coherent(&hdev->device, 10048c2ecf20Sopenharmony_ci round_up(size, PAGE_SIZE), 10058c2ecf20Sopenharmony_ci phys_to_virt(paddr), 10068c2ecf20Sopenharmony_ci paddr); 10078c2ecf20Sopenharmony_ci} 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci/* Get framebuffer memory from Hyper-V video pci space */ 10118c2ecf20Sopenharmony_cistatic int hvfb_getmem(struct hv_device *hdev, struct fb_info *info) 10128c2ecf20Sopenharmony_ci{ 10138c2ecf20Sopenharmony_ci struct hvfb_par *par = info->par; 10148c2ecf20Sopenharmony_ci struct pci_dev *pdev = NULL; 10158c2ecf20Sopenharmony_ci void __iomem *fb_virt; 10168c2ecf20Sopenharmony_ci int gen2vm = efi_enabled(EFI_BOOT); 10178c2ecf20Sopenharmony_ci phys_addr_t paddr; 10188c2ecf20Sopenharmony_ci int ret; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci info->apertures = alloc_apertures(1); 10218c2ecf20Sopenharmony_ci if (!info->apertures) 10228c2ecf20Sopenharmony_ci return -ENOMEM; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci if (!gen2vm) { 10258c2ecf20Sopenharmony_ci pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT, 10268c2ecf20Sopenharmony_ci PCI_DEVICE_ID_HYPERV_VIDEO, NULL); 10278c2ecf20Sopenharmony_ci if (!pdev) { 10288c2ecf20Sopenharmony_ci pr_err("Unable to find PCI Hyper-V video\n"); 10298c2ecf20Sopenharmony_ci return -ENODEV; 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci info->apertures->ranges[0].base = pci_resource_start(pdev, 0); 10338c2ecf20Sopenharmony_ci info->apertures->ranges[0].size = pci_resource_len(pdev, 0); 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci /* 10368c2ecf20Sopenharmony_ci * For Gen 1 VM, we can directly use the contiguous memory 10378c2ecf20Sopenharmony_ci * from VM. If we succeed, deferred IO happens directly 10388c2ecf20Sopenharmony_ci * on this allocated framebuffer memory, avoiding extra 10398c2ecf20Sopenharmony_ci * memory copy. 10408c2ecf20Sopenharmony_ci */ 10418c2ecf20Sopenharmony_ci paddr = hvfb_get_phymem(hdev, screen_fb_size); 10428c2ecf20Sopenharmony_ci if (paddr != (phys_addr_t) -1) { 10438c2ecf20Sopenharmony_ci par->mmio_pp = paddr; 10448c2ecf20Sopenharmony_ci par->mmio_vp = par->dio_vp = __va(paddr); 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci info->fix.smem_start = paddr; 10478c2ecf20Sopenharmony_ci info->fix.smem_len = screen_fb_size; 10488c2ecf20Sopenharmony_ci info->screen_base = par->mmio_vp; 10498c2ecf20Sopenharmony_ci info->screen_size = screen_fb_size; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci par->need_docopy = false; 10528c2ecf20Sopenharmony_ci goto getmem_done; 10538c2ecf20Sopenharmony_ci } 10548c2ecf20Sopenharmony_ci pr_info("Unable to allocate enough contiguous physical memory on Gen 1 VM. Using MMIO instead.\n"); 10558c2ecf20Sopenharmony_ci } else { 10568c2ecf20Sopenharmony_ci info->apertures->ranges[0].base = screen_info.lfb_base; 10578c2ecf20Sopenharmony_ci info->apertures->ranges[0].size = screen_info.lfb_size; 10588c2ecf20Sopenharmony_ci } 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci /* 10618c2ecf20Sopenharmony_ci * Cannot use the contiguous physical memory. 10628c2ecf20Sopenharmony_ci * Allocate mmio space for framebuffer. 10638c2ecf20Sopenharmony_ci */ 10648c2ecf20Sopenharmony_ci dio_fb_size = 10658c2ecf20Sopenharmony_ci screen_width * screen_height * screen_depth / 8; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci ret = vmbus_allocate_mmio(&par->mem, hdev, 0, -1, 10688c2ecf20Sopenharmony_ci screen_fb_size, 0x100000, true); 10698c2ecf20Sopenharmony_ci if (ret != 0) { 10708c2ecf20Sopenharmony_ci pr_err("Unable to allocate framebuffer memory\n"); 10718c2ecf20Sopenharmony_ci goto err1; 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci /* 10758c2ecf20Sopenharmony_ci * Map the VRAM cacheable for performance. This is also required for 10768c2ecf20Sopenharmony_ci * VM Connect to display properly for ARM64 Linux VM, as the host also 10778c2ecf20Sopenharmony_ci * maps the VRAM cacheable. 10788c2ecf20Sopenharmony_ci */ 10798c2ecf20Sopenharmony_ci fb_virt = ioremap_cache(par->mem->start, screen_fb_size); 10808c2ecf20Sopenharmony_ci if (!fb_virt) 10818c2ecf20Sopenharmony_ci goto err2; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci /* Allocate memory for deferred IO */ 10848c2ecf20Sopenharmony_ci par->dio_vp = vzalloc(round_up(dio_fb_size, PAGE_SIZE)); 10858c2ecf20Sopenharmony_ci if (par->dio_vp == NULL) 10868c2ecf20Sopenharmony_ci goto err3; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci /* Physical address of FB device */ 10898c2ecf20Sopenharmony_ci par->mmio_pp = par->mem->start; 10908c2ecf20Sopenharmony_ci /* Virtual address of FB device */ 10918c2ecf20Sopenharmony_ci par->mmio_vp = (unsigned char *) fb_virt; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci info->fix.smem_start = par->mem->start; 10948c2ecf20Sopenharmony_ci info->fix.smem_len = dio_fb_size; 10958c2ecf20Sopenharmony_ci info->screen_base = par->dio_vp; 10968c2ecf20Sopenharmony_ci info->screen_size = dio_fb_size; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_cigetmem_done: 10998c2ecf20Sopenharmony_ci remove_conflicting_framebuffers(info->apertures, 11008c2ecf20Sopenharmony_ci KBUILD_MODNAME, false); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci if (gen2vm) { 11038c2ecf20Sopenharmony_ci /* framebuffer is reallocated, clear screen_info to avoid misuse from kexec */ 11048c2ecf20Sopenharmony_ci screen_info.lfb_size = 0; 11058c2ecf20Sopenharmony_ci screen_info.lfb_base = 0; 11068c2ecf20Sopenharmony_ci screen_info.orig_video_isVGA = 0; 11078c2ecf20Sopenharmony_ci } else { 11088c2ecf20Sopenharmony_ci pci_dev_put(pdev); 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci return 0; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_cierr3: 11148c2ecf20Sopenharmony_ci iounmap(fb_virt); 11158c2ecf20Sopenharmony_cierr2: 11168c2ecf20Sopenharmony_ci vmbus_free_mmio(par->mem->start, screen_fb_size); 11178c2ecf20Sopenharmony_ci par->mem = NULL; 11188c2ecf20Sopenharmony_cierr1: 11198c2ecf20Sopenharmony_ci if (!gen2vm) 11208c2ecf20Sopenharmony_ci pci_dev_put(pdev); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci return -ENOMEM; 11238c2ecf20Sopenharmony_ci} 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci/* Release the framebuffer */ 11268c2ecf20Sopenharmony_cistatic void hvfb_putmem(struct hv_device *hdev, struct fb_info *info) 11278c2ecf20Sopenharmony_ci{ 11288c2ecf20Sopenharmony_ci struct hvfb_par *par = info->par; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci if (par->need_docopy) { 11318c2ecf20Sopenharmony_ci vfree(par->dio_vp); 11328c2ecf20Sopenharmony_ci iounmap(info->screen_base); 11338c2ecf20Sopenharmony_ci vmbus_free_mmio(par->mem->start, screen_fb_size); 11348c2ecf20Sopenharmony_ci } else { 11358c2ecf20Sopenharmony_ci hvfb_release_phymem(hdev, info->fix.smem_start, 11368c2ecf20Sopenharmony_ci screen_fb_size); 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci par->mem = NULL; 11408c2ecf20Sopenharmony_ci} 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_cistatic int hvfb_probe(struct hv_device *hdev, 11448c2ecf20Sopenharmony_ci const struct hv_vmbus_device_id *dev_id) 11458c2ecf20Sopenharmony_ci{ 11468c2ecf20Sopenharmony_ci struct fb_info *info; 11478c2ecf20Sopenharmony_ci struct hvfb_par *par; 11488c2ecf20Sopenharmony_ci int ret; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(struct hvfb_par), &hdev->device); 11518c2ecf20Sopenharmony_ci if (!info) 11528c2ecf20Sopenharmony_ci return -ENOMEM; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci par = info->par; 11558c2ecf20Sopenharmony_ci par->info = info; 11568c2ecf20Sopenharmony_ci par->fb_ready = false; 11578c2ecf20Sopenharmony_ci par->need_docopy = true; 11588c2ecf20Sopenharmony_ci init_completion(&par->wait); 11598c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&par->dwork, hvfb_update_work); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci par->delayed_refresh = false; 11628c2ecf20Sopenharmony_ci spin_lock_init(&par->delayed_refresh_lock); 11638c2ecf20Sopenharmony_ci par->x1 = par->y1 = INT_MAX; 11648c2ecf20Sopenharmony_ci par->x2 = par->y2 = 0; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci /* Connect to VSP */ 11678c2ecf20Sopenharmony_ci hv_set_drvdata(hdev, info); 11688c2ecf20Sopenharmony_ci ret = synthvid_connect_vsp(hdev); 11698c2ecf20Sopenharmony_ci if (ret) { 11708c2ecf20Sopenharmony_ci pr_err("Unable to connect to VSP\n"); 11718c2ecf20Sopenharmony_ci goto error1; 11728c2ecf20Sopenharmony_ci } 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci hvfb_get_option(info); 11758c2ecf20Sopenharmony_ci pr_info("Screen resolution: %dx%d, Color depth: %d, Frame buffer size: %d\n", 11768c2ecf20Sopenharmony_ci screen_width, screen_height, screen_depth, screen_fb_size); 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci ret = hvfb_getmem(hdev, info); 11798c2ecf20Sopenharmony_ci if (ret) { 11808c2ecf20Sopenharmony_ci pr_err("No memory for framebuffer\n"); 11818c2ecf20Sopenharmony_ci goto error2; 11828c2ecf20Sopenharmony_ci } 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci /* Set up fb_info */ 11858c2ecf20Sopenharmony_ci info->flags = FBINFO_DEFAULT; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci info->var.xres_virtual = info->var.xres = screen_width; 11888c2ecf20Sopenharmony_ci info->var.yres_virtual = info->var.yres = screen_height; 11898c2ecf20Sopenharmony_ci info->var.bits_per_pixel = screen_depth; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci if (info->var.bits_per_pixel == 16) { 11928c2ecf20Sopenharmony_ci info->var.red = (struct fb_bitfield){11, 5, 0}; 11938c2ecf20Sopenharmony_ci info->var.green = (struct fb_bitfield){5, 6, 0}; 11948c2ecf20Sopenharmony_ci info->var.blue = (struct fb_bitfield){0, 5, 0}; 11958c2ecf20Sopenharmony_ci info->var.transp = (struct fb_bitfield){0, 0, 0}; 11968c2ecf20Sopenharmony_ci } else { 11978c2ecf20Sopenharmony_ci info->var.red = (struct fb_bitfield){16, 8, 0}; 11988c2ecf20Sopenharmony_ci info->var.green = (struct fb_bitfield){8, 8, 0}; 11998c2ecf20Sopenharmony_ci info->var.blue = (struct fb_bitfield){0, 8, 0}; 12008c2ecf20Sopenharmony_ci info->var.transp = (struct fb_bitfield){24, 8, 0}; 12018c2ecf20Sopenharmony_ci } 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci info->var.activate = FB_ACTIVATE_NOW; 12048c2ecf20Sopenharmony_ci info->var.height = -1; 12058c2ecf20Sopenharmony_ci info->var.width = -1; 12068c2ecf20Sopenharmony_ci info->var.vmode = FB_VMODE_NONINTERLACED; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci strcpy(info->fix.id, KBUILD_MODNAME); 12098c2ecf20Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 12108c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 12118c2ecf20Sopenharmony_ci info->fix.line_length = screen_width * screen_depth / 8; 12128c2ecf20Sopenharmony_ci info->fix.accel = FB_ACCEL_NONE; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci info->fbops = &hvfb_ops; 12158c2ecf20Sopenharmony_ci info->pseudo_palette = par->pseudo_palette; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci /* Initialize deferred IO */ 12188c2ecf20Sopenharmony_ci info->fbdefio = &synthvid_defio; 12198c2ecf20Sopenharmony_ci fb_deferred_io_init(info); 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci /* Send config to host */ 12228c2ecf20Sopenharmony_ci ret = synthvid_send_config(hdev); 12238c2ecf20Sopenharmony_ci if (ret) 12248c2ecf20Sopenharmony_ci goto error; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci ret = register_framebuffer(info); 12278c2ecf20Sopenharmony_ci if (ret) { 12288c2ecf20Sopenharmony_ci pr_err("Unable to register framebuffer\n"); 12298c2ecf20Sopenharmony_ci goto error; 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci par->fb_ready = true; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci par->synchronous_fb = false; 12358c2ecf20Sopenharmony_ci par->hvfb_panic_nb.notifier_call = hvfb_on_panic; 12368c2ecf20Sopenharmony_ci atomic_notifier_chain_register(&panic_notifier_list, 12378c2ecf20Sopenharmony_ci &par->hvfb_panic_nb); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci return 0; 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_cierror: 12428c2ecf20Sopenharmony_ci fb_deferred_io_cleanup(info); 12438c2ecf20Sopenharmony_ci hvfb_putmem(hdev, info); 12448c2ecf20Sopenharmony_cierror2: 12458c2ecf20Sopenharmony_ci vmbus_close(hdev->channel); 12468c2ecf20Sopenharmony_cierror1: 12478c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&par->dwork); 12488c2ecf20Sopenharmony_ci hv_set_drvdata(hdev, NULL); 12498c2ecf20Sopenharmony_ci framebuffer_release(info); 12508c2ecf20Sopenharmony_ci return ret; 12518c2ecf20Sopenharmony_ci} 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_cistatic int hvfb_remove(struct hv_device *hdev) 12558c2ecf20Sopenharmony_ci{ 12568c2ecf20Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 12578c2ecf20Sopenharmony_ci struct hvfb_par *par = info->par; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci atomic_notifier_chain_unregister(&panic_notifier_list, 12608c2ecf20Sopenharmony_ci &par->hvfb_panic_nb); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci par->update = false; 12638c2ecf20Sopenharmony_ci par->fb_ready = false; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci fb_deferred_io_cleanup(info); 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci unregister_framebuffer(info); 12688c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&par->dwork); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci vmbus_close(hdev->channel); 12718c2ecf20Sopenharmony_ci hv_set_drvdata(hdev, NULL); 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci hvfb_putmem(hdev, info); 12748c2ecf20Sopenharmony_ci framebuffer_release(info); 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci return 0; 12778c2ecf20Sopenharmony_ci} 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_cistatic int hvfb_suspend(struct hv_device *hdev) 12808c2ecf20Sopenharmony_ci{ 12818c2ecf20Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 12828c2ecf20Sopenharmony_ci struct hvfb_par *par = info->par; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci console_lock(); 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci /* 1 means do suspend */ 12878c2ecf20Sopenharmony_ci fb_set_suspend(info, 1); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&par->dwork); 12908c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&info->deferred_work); 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci par->update_saved = par->update; 12938c2ecf20Sopenharmony_ci par->update = false; 12948c2ecf20Sopenharmony_ci par->fb_ready = false; 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci vmbus_close(hdev->channel); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci console_unlock(); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci return 0; 13018c2ecf20Sopenharmony_ci} 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_cistatic int hvfb_resume(struct hv_device *hdev) 13048c2ecf20Sopenharmony_ci{ 13058c2ecf20Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 13068c2ecf20Sopenharmony_ci struct hvfb_par *par = info->par; 13078c2ecf20Sopenharmony_ci int ret; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci console_lock(); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci ret = synthvid_connect_vsp(hdev); 13128c2ecf20Sopenharmony_ci if (ret != 0) 13138c2ecf20Sopenharmony_ci goto out; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci ret = synthvid_send_config(hdev); 13168c2ecf20Sopenharmony_ci if (ret != 0) { 13178c2ecf20Sopenharmony_ci vmbus_close(hdev->channel); 13188c2ecf20Sopenharmony_ci goto out; 13198c2ecf20Sopenharmony_ci } 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci par->fb_ready = true; 13228c2ecf20Sopenharmony_ci par->update = par->update_saved; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci schedule_delayed_work(&info->deferred_work, info->fbdefio->delay); 13258c2ecf20Sopenharmony_ci schedule_delayed_work(&par->dwork, HVFB_UPDATE_DELAY); 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci /* 0 means do resume */ 13288c2ecf20Sopenharmony_ci fb_set_suspend(info, 0); 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ciout: 13318c2ecf20Sopenharmony_ci console_unlock(); 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci return ret; 13348c2ecf20Sopenharmony_ci} 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_cistatic const struct pci_device_id pci_stub_id_table[] = { 13388c2ecf20Sopenharmony_ci { 13398c2ecf20Sopenharmony_ci .vendor = PCI_VENDOR_ID_MICROSOFT, 13408c2ecf20Sopenharmony_ci .device = PCI_DEVICE_ID_HYPERV_VIDEO, 13418c2ecf20Sopenharmony_ci }, 13428c2ecf20Sopenharmony_ci { /* end of list */ } 13438c2ecf20Sopenharmony_ci}; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_cistatic const struct hv_vmbus_device_id id_table[] = { 13468c2ecf20Sopenharmony_ci /* Synthetic Video Device GUID */ 13478c2ecf20Sopenharmony_ci {HV_SYNTHVID_GUID}, 13488c2ecf20Sopenharmony_ci {} 13498c2ecf20Sopenharmony_ci}; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pci_stub_id_table); 13528c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(vmbus, id_table); 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_cistatic struct hv_driver hvfb_drv = { 13558c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 13568c2ecf20Sopenharmony_ci .id_table = id_table, 13578c2ecf20Sopenharmony_ci .probe = hvfb_probe, 13588c2ecf20Sopenharmony_ci .remove = hvfb_remove, 13598c2ecf20Sopenharmony_ci .suspend = hvfb_suspend, 13608c2ecf20Sopenharmony_ci .resume = hvfb_resume, 13618c2ecf20Sopenharmony_ci .driver = { 13628c2ecf20Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 13638c2ecf20Sopenharmony_ci }, 13648c2ecf20Sopenharmony_ci}; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_cistatic int hvfb_pci_stub_probe(struct pci_dev *pdev, 13678c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 13688c2ecf20Sopenharmony_ci{ 13698c2ecf20Sopenharmony_ci return 0; 13708c2ecf20Sopenharmony_ci} 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_cistatic void hvfb_pci_stub_remove(struct pci_dev *pdev) 13738c2ecf20Sopenharmony_ci{ 13748c2ecf20Sopenharmony_ci} 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_cistatic struct pci_driver hvfb_pci_stub_driver = { 13778c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 13788c2ecf20Sopenharmony_ci .id_table = pci_stub_id_table, 13798c2ecf20Sopenharmony_ci .probe = hvfb_pci_stub_probe, 13808c2ecf20Sopenharmony_ci .remove = hvfb_pci_stub_remove, 13818c2ecf20Sopenharmony_ci .driver = { 13828c2ecf20Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 13838c2ecf20Sopenharmony_ci } 13848c2ecf20Sopenharmony_ci}; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_cistatic int __init hvfb_drv_init(void) 13878c2ecf20Sopenharmony_ci{ 13888c2ecf20Sopenharmony_ci int ret; 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci ret = vmbus_driver_register(&hvfb_drv); 13918c2ecf20Sopenharmony_ci if (ret != 0) 13928c2ecf20Sopenharmony_ci return ret; 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci ret = pci_register_driver(&hvfb_pci_stub_driver); 13958c2ecf20Sopenharmony_ci if (ret != 0) { 13968c2ecf20Sopenharmony_ci vmbus_driver_unregister(&hvfb_drv); 13978c2ecf20Sopenharmony_ci return ret; 13988c2ecf20Sopenharmony_ci } 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci return 0; 14018c2ecf20Sopenharmony_ci} 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_cistatic void __exit hvfb_drv_exit(void) 14048c2ecf20Sopenharmony_ci{ 14058c2ecf20Sopenharmony_ci pci_unregister_driver(&hvfb_pci_stub_driver); 14068c2ecf20Sopenharmony_ci vmbus_driver_unregister(&hvfb_drv); 14078c2ecf20Sopenharmony_ci} 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_cimodule_init(hvfb_drv_init); 14108c2ecf20Sopenharmony_cimodule_exit(hvfb_drv_exit); 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 14138c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Microsoft Hyper-V Synthetic Video Frame Buffer Driver"); 1414