162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2012, Microsoft Corporation. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: 662306a36Sopenharmony_ci * Haiyang Zhang <haiyangz@microsoft.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci * Hyper-V Synthetic Video Frame Buffer Driver 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * This is the driver for the Hyper-V Synthetic Video, which supports 1362306a36Sopenharmony_ci * screen resolution up to Full HD 1920x1080 with 32 bit color on Windows 1462306a36Sopenharmony_ci * Server 2012, and 1600x1200 with 16 bit color on Windows Server 2008 R2 1562306a36Sopenharmony_ci * or earlier. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * It also solves the double mouse cursor issue of the emulated video mode. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * The default screen resolution is 1152x864, which may be changed by a 2062306a36Sopenharmony_ci * kernel parameter: 2162306a36Sopenharmony_ci * video=hyperv_fb:<width>x<height> 2262306a36Sopenharmony_ci * For example: video=hyperv_fb:1280x1024 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * Portrait orientation is also supported: 2562306a36Sopenharmony_ci * For example: video=hyperv_fb:864x1152 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * When a Windows 10 RS5+ host is used, the virtual machine screen 2862306a36Sopenharmony_ci * resolution is obtained from the host. The "video=hyperv_fb" option is 2962306a36Sopenharmony_ci * not needed, but still can be used to overwrite what the host specifies. 3062306a36Sopenharmony_ci * The VM resolution on the host could be set by executing the powershell 3162306a36Sopenharmony_ci * "set-vmvideo" command. For example 3262306a36Sopenharmony_ci * set-vmvideo -vmname name -horizontalresolution:1920 \ 3362306a36Sopenharmony_ci * -verticalresolution:1200 -resolutiontype single 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * Gen 1 VMs also support direct using VM's physical memory for framebuffer. 3662306a36Sopenharmony_ci * It could improve the efficiency and performance for framebuffer and VM. 3762306a36Sopenharmony_ci * This requires to allocate contiguous physical memory from Linux kernel's 3862306a36Sopenharmony_ci * CMA memory allocator. To enable this, supply a kernel parameter to give 3962306a36Sopenharmony_ci * enough memory space to CMA allocator for framebuffer. For example: 4062306a36Sopenharmony_ci * cma=130m 4162306a36Sopenharmony_ci * This gives 130MB memory to CMA allocator that can be allocated to 4262306a36Sopenharmony_ci * framebuffer. For reference, 8K resolution (7680x4320) takes about 4362306a36Sopenharmony_ci * 127MB memory. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#include <linux/aperture.h> 4962306a36Sopenharmony_ci#include <linux/module.h> 5062306a36Sopenharmony_ci#include <linux/kernel.h> 5162306a36Sopenharmony_ci#include <linux/screen_info.h> 5262306a36Sopenharmony_ci#include <linux/vmalloc.h> 5362306a36Sopenharmony_ci#include <linux/init.h> 5462306a36Sopenharmony_ci#include <linux/completion.h> 5562306a36Sopenharmony_ci#include <linux/fb.h> 5662306a36Sopenharmony_ci#include <linux/pci.h> 5762306a36Sopenharmony_ci#include <linux/panic_notifier.h> 5862306a36Sopenharmony_ci#include <linux/efi.h> 5962306a36Sopenharmony_ci#include <linux/console.h> 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#include <linux/hyperv.h> 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* Hyper-V Synthetic Video Protocol definitions and structures */ 6462306a36Sopenharmony_ci#define MAX_VMBUS_PKT_SIZE 0x4000 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major)) 6762306a36Sopenharmony_ci/* Support for VERSION_WIN7 is removed. #define is retained for reference. */ 6862306a36Sopenharmony_ci#define SYNTHVID_VERSION_WIN7 SYNTHVID_VERSION(3, 0) 6962306a36Sopenharmony_ci#define SYNTHVID_VERSION_WIN8 SYNTHVID_VERSION(3, 2) 7062306a36Sopenharmony_ci#define SYNTHVID_VERSION_WIN10 SYNTHVID_VERSION(3, 5) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define SYNTHVID_VER_GET_MAJOR(ver) (ver & 0x0000ffff) 7362306a36Sopenharmony_ci#define SYNTHVID_VER_GET_MINOR(ver) ((ver & 0xffff0000) >> 16) 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#define SYNTHVID_DEPTH_WIN8 32 7662306a36Sopenharmony_ci#define SYNTHVID_FB_SIZE_WIN8 (8 * 1024 * 1024) 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cienum pipe_msg_type { 7962306a36Sopenharmony_ci PIPE_MSG_INVALID, 8062306a36Sopenharmony_ci PIPE_MSG_DATA, 8162306a36Sopenharmony_ci PIPE_MSG_MAX 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistruct pipe_msg_hdr { 8562306a36Sopenharmony_ci u32 type; 8662306a36Sopenharmony_ci u32 size; /* size of message after this field */ 8762306a36Sopenharmony_ci} __packed; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cienum synthvid_msg_type { 9162306a36Sopenharmony_ci SYNTHVID_ERROR = 0, 9262306a36Sopenharmony_ci SYNTHVID_VERSION_REQUEST = 1, 9362306a36Sopenharmony_ci SYNTHVID_VERSION_RESPONSE = 2, 9462306a36Sopenharmony_ci SYNTHVID_VRAM_LOCATION = 3, 9562306a36Sopenharmony_ci SYNTHVID_VRAM_LOCATION_ACK = 4, 9662306a36Sopenharmony_ci SYNTHVID_SITUATION_UPDATE = 5, 9762306a36Sopenharmony_ci SYNTHVID_SITUATION_UPDATE_ACK = 6, 9862306a36Sopenharmony_ci SYNTHVID_POINTER_POSITION = 7, 9962306a36Sopenharmony_ci SYNTHVID_POINTER_SHAPE = 8, 10062306a36Sopenharmony_ci SYNTHVID_FEATURE_CHANGE = 9, 10162306a36Sopenharmony_ci SYNTHVID_DIRT = 10, 10262306a36Sopenharmony_ci SYNTHVID_RESOLUTION_REQUEST = 13, 10362306a36Sopenharmony_ci SYNTHVID_RESOLUTION_RESPONSE = 14, 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci SYNTHVID_MAX = 15 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define SYNTHVID_EDID_BLOCK_SIZE 128 10962306a36Sopenharmony_ci#define SYNTHVID_MAX_RESOLUTION_COUNT 64 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistruct hvd_screen_info { 11262306a36Sopenharmony_ci u16 width; 11362306a36Sopenharmony_ci u16 height; 11462306a36Sopenharmony_ci} __packed; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistruct synthvid_msg_hdr { 11762306a36Sopenharmony_ci u32 type; 11862306a36Sopenharmony_ci u32 size; /* size of this header + payload after this field*/ 11962306a36Sopenharmony_ci} __packed; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistruct synthvid_version_req { 12262306a36Sopenharmony_ci u32 version; 12362306a36Sopenharmony_ci} __packed; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistruct synthvid_version_resp { 12662306a36Sopenharmony_ci u32 version; 12762306a36Sopenharmony_ci u8 is_accepted; 12862306a36Sopenharmony_ci u8 max_video_outputs; 12962306a36Sopenharmony_ci} __packed; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistruct synthvid_supported_resolution_req { 13262306a36Sopenharmony_ci u8 maximum_resolution_count; 13362306a36Sopenharmony_ci} __packed; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistruct synthvid_supported_resolution_resp { 13662306a36Sopenharmony_ci u8 edid_block[SYNTHVID_EDID_BLOCK_SIZE]; 13762306a36Sopenharmony_ci u8 resolution_count; 13862306a36Sopenharmony_ci u8 default_resolution_index; 13962306a36Sopenharmony_ci u8 is_standard; 14062306a36Sopenharmony_ci struct hvd_screen_info 14162306a36Sopenharmony_ci supported_resolution[SYNTHVID_MAX_RESOLUTION_COUNT]; 14262306a36Sopenharmony_ci} __packed; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistruct synthvid_vram_location { 14562306a36Sopenharmony_ci u64 user_ctx; 14662306a36Sopenharmony_ci u8 is_vram_gpa_specified; 14762306a36Sopenharmony_ci u64 vram_gpa; 14862306a36Sopenharmony_ci} __packed; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistruct synthvid_vram_location_ack { 15162306a36Sopenharmony_ci u64 user_ctx; 15262306a36Sopenharmony_ci} __packed; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistruct video_output_situation { 15562306a36Sopenharmony_ci u8 active; 15662306a36Sopenharmony_ci u32 vram_offset; 15762306a36Sopenharmony_ci u8 depth_bits; 15862306a36Sopenharmony_ci u32 width_pixels; 15962306a36Sopenharmony_ci u32 height_pixels; 16062306a36Sopenharmony_ci u32 pitch_bytes; 16162306a36Sopenharmony_ci} __packed; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistruct synthvid_situation_update { 16462306a36Sopenharmony_ci u64 user_ctx; 16562306a36Sopenharmony_ci u8 video_output_count; 16662306a36Sopenharmony_ci struct video_output_situation video_output[1]; 16762306a36Sopenharmony_ci} __packed; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistruct synthvid_situation_update_ack { 17062306a36Sopenharmony_ci u64 user_ctx; 17162306a36Sopenharmony_ci} __packed; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistruct synthvid_pointer_position { 17462306a36Sopenharmony_ci u8 is_visible; 17562306a36Sopenharmony_ci u8 video_output; 17662306a36Sopenharmony_ci s32 image_x; 17762306a36Sopenharmony_ci s32 image_y; 17862306a36Sopenharmony_ci} __packed; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci#define CURSOR_MAX_X 96 18262306a36Sopenharmony_ci#define CURSOR_MAX_Y 96 18362306a36Sopenharmony_ci#define CURSOR_ARGB_PIXEL_SIZE 4 18462306a36Sopenharmony_ci#define CURSOR_MAX_SIZE (CURSOR_MAX_X * CURSOR_MAX_Y * CURSOR_ARGB_PIXEL_SIZE) 18562306a36Sopenharmony_ci#define CURSOR_COMPLETE (-1) 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistruct synthvid_pointer_shape { 18862306a36Sopenharmony_ci u8 part_idx; 18962306a36Sopenharmony_ci u8 is_argb; 19062306a36Sopenharmony_ci u32 width; /* CURSOR_MAX_X at most */ 19162306a36Sopenharmony_ci u32 height; /* CURSOR_MAX_Y at most */ 19262306a36Sopenharmony_ci u32 hot_x; /* hotspot relative to upper-left of pointer image */ 19362306a36Sopenharmony_ci u32 hot_y; 19462306a36Sopenharmony_ci u8 data[4]; 19562306a36Sopenharmony_ci} __packed; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistruct synthvid_feature_change { 19862306a36Sopenharmony_ci u8 is_dirt_needed; 19962306a36Sopenharmony_ci u8 is_ptr_pos_needed; 20062306a36Sopenharmony_ci u8 is_ptr_shape_needed; 20162306a36Sopenharmony_ci u8 is_situ_needed; 20262306a36Sopenharmony_ci} __packed; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistruct rect { 20562306a36Sopenharmony_ci s32 x1, y1; /* top left corner */ 20662306a36Sopenharmony_ci s32 x2, y2; /* bottom right corner, exclusive */ 20762306a36Sopenharmony_ci} __packed; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistruct synthvid_dirt { 21062306a36Sopenharmony_ci u8 video_output; 21162306a36Sopenharmony_ci u8 dirt_count; 21262306a36Sopenharmony_ci struct rect rect[1]; 21362306a36Sopenharmony_ci} __packed; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistruct synthvid_msg { 21662306a36Sopenharmony_ci struct pipe_msg_hdr pipe_hdr; 21762306a36Sopenharmony_ci struct synthvid_msg_hdr vid_hdr; 21862306a36Sopenharmony_ci union { 21962306a36Sopenharmony_ci struct synthvid_version_req ver_req; 22062306a36Sopenharmony_ci struct synthvid_version_resp ver_resp; 22162306a36Sopenharmony_ci struct synthvid_vram_location vram; 22262306a36Sopenharmony_ci struct synthvid_vram_location_ack vram_ack; 22362306a36Sopenharmony_ci struct synthvid_situation_update situ; 22462306a36Sopenharmony_ci struct synthvid_situation_update_ack situ_ack; 22562306a36Sopenharmony_ci struct synthvid_pointer_position ptr_pos; 22662306a36Sopenharmony_ci struct synthvid_pointer_shape ptr_shape; 22762306a36Sopenharmony_ci struct synthvid_feature_change feature_chg; 22862306a36Sopenharmony_ci struct synthvid_dirt dirt; 22962306a36Sopenharmony_ci struct synthvid_supported_resolution_req resolution_req; 23062306a36Sopenharmony_ci struct synthvid_supported_resolution_resp resolution_resp; 23162306a36Sopenharmony_ci }; 23262306a36Sopenharmony_ci} __packed; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/* FB driver definitions and structures */ 23662306a36Sopenharmony_ci#define HVFB_WIDTH 1152 /* default screen width */ 23762306a36Sopenharmony_ci#define HVFB_HEIGHT 864 /* default screen height */ 23862306a36Sopenharmony_ci#define HVFB_WIDTH_MIN 640 23962306a36Sopenharmony_ci#define HVFB_HEIGHT_MIN 480 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci#define RING_BUFSIZE (256 * 1024) 24262306a36Sopenharmony_ci#define VSP_TIMEOUT (10 * HZ) 24362306a36Sopenharmony_ci#define HVFB_UPDATE_DELAY (HZ / 20) 24462306a36Sopenharmony_ci#define HVFB_ONDEMAND_THROTTLE (HZ / 20) 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistruct hvfb_par { 24762306a36Sopenharmony_ci struct fb_info *info; 24862306a36Sopenharmony_ci struct resource *mem; 24962306a36Sopenharmony_ci bool fb_ready; /* fb device is ready */ 25062306a36Sopenharmony_ci struct completion wait; 25162306a36Sopenharmony_ci u32 synthvid_version; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci struct delayed_work dwork; 25462306a36Sopenharmony_ci bool update; 25562306a36Sopenharmony_ci bool update_saved; /* The value of 'update' before hibernation */ 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci u32 pseudo_palette[16]; 25862306a36Sopenharmony_ci u8 init_buf[MAX_VMBUS_PKT_SIZE]; 25962306a36Sopenharmony_ci u8 recv_buf[MAX_VMBUS_PKT_SIZE]; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* If true, the VSC notifies the VSP on every framebuffer change */ 26262306a36Sopenharmony_ci bool synchronous_fb; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* If true, need to copy from deferred IO mem to framebuffer mem */ 26562306a36Sopenharmony_ci bool need_docopy; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci struct notifier_block hvfb_panic_nb; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* Memory for deferred IO and frame buffer itself */ 27062306a36Sopenharmony_ci unsigned char *dio_vp; 27162306a36Sopenharmony_ci unsigned char *mmio_vp; 27262306a36Sopenharmony_ci phys_addr_t mmio_pp; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* Dirty rectangle, protected by delayed_refresh_lock */ 27562306a36Sopenharmony_ci int x1, y1, x2, y2; 27662306a36Sopenharmony_ci bool delayed_refresh; 27762306a36Sopenharmony_ci spinlock_t delayed_refresh_lock; 27862306a36Sopenharmony_ci}; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic uint screen_width = HVFB_WIDTH; 28162306a36Sopenharmony_cistatic uint screen_height = HVFB_HEIGHT; 28262306a36Sopenharmony_cistatic uint screen_depth; 28362306a36Sopenharmony_cistatic uint screen_fb_size; 28462306a36Sopenharmony_cistatic uint dio_fb_size; /* FB size for deferred IO */ 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci/* Send message to Hyper-V host */ 28762306a36Sopenharmony_cistatic inline int synthvid_send(struct hv_device *hdev, 28862306a36Sopenharmony_ci struct synthvid_msg *msg) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci static atomic64_t request_id = ATOMIC64_INIT(0); 29162306a36Sopenharmony_ci int ret; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci msg->pipe_hdr.type = PIPE_MSG_DATA; 29462306a36Sopenharmony_ci msg->pipe_hdr.size = msg->vid_hdr.size; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci ret = vmbus_sendpacket(hdev->channel, msg, 29762306a36Sopenharmony_ci msg->vid_hdr.size + sizeof(struct pipe_msg_hdr), 29862306a36Sopenharmony_ci atomic64_inc_return(&request_id), 29962306a36Sopenharmony_ci VM_PKT_DATA_INBAND, 0); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (ret) 30262306a36Sopenharmony_ci pr_err_ratelimited("Unable to send packet via vmbus; error %d\n", ret); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return ret; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci/* Send screen resolution info to host */ 30962306a36Sopenharmony_cistatic int synthvid_send_situ(struct hv_device *hdev) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 31262306a36Sopenharmony_ci struct synthvid_msg msg; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (!info) 31562306a36Sopenharmony_ci return -ENODEV; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci memset(&msg, 0, sizeof(struct synthvid_msg)); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci msg.vid_hdr.type = SYNTHVID_SITUATION_UPDATE; 32062306a36Sopenharmony_ci msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 32162306a36Sopenharmony_ci sizeof(struct synthvid_situation_update); 32262306a36Sopenharmony_ci msg.situ.user_ctx = 0; 32362306a36Sopenharmony_ci msg.situ.video_output_count = 1; 32462306a36Sopenharmony_ci msg.situ.video_output[0].active = 1; 32562306a36Sopenharmony_ci msg.situ.video_output[0].vram_offset = 0; 32662306a36Sopenharmony_ci msg.situ.video_output[0].depth_bits = info->var.bits_per_pixel; 32762306a36Sopenharmony_ci msg.situ.video_output[0].width_pixels = info->var.xres; 32862306a36Sopenharmony_ci msg.situ.video_output[0].height_pixels = info->var.yres; 32962306a36Sopenharmony_ci msg.situ.video_output[0].pitch_bytes = info->fix.line_length; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci synthvid_send(hdev, &msg); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci return 0; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci/* Send mouse pointer info to host */ 33762306a36Sopenharmony_cistatic int synthvid_send_ptr(struct hv_device *hdev) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct synthvid_msg msg; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci memset(&msg, 0, sizeof(struct synthvid_msg)); 34262306a36Sopenharmony_ci msg.vid_hdr.type = SYNTHVID_POINTER_POSITION; 34362306a36Sopenharmony_ci msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 34462306a36Sopenharmony_ci sizeof(struct synthvid_pointer_position); 34562306a36Sopenharmony_ci msg.ptr_pos.is_visible = 1; 34662306a36Sopenharmony_ci msg.ptr_pos.video_output = 0; 34762306a36Sopenharmony_ci msg.ptr_pos.image_x = 0; 34862306a36Sopenharmony_ci msg.ptr_pos.image_y = 0; 34962306a36Sopenharmony_ci synthvid_send(hdev, &msg); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci memset(&msg, 0, sizeof(struct synthvid_msg)); 35262306a36Sopenharmony_ci msg.vid_hdr.type = SYNTHVID_POINTER_SHAPE; 35362306a36Sopenharmony_ci msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 35462306a36Sopenharmony_ci sizeof(struct synthvid_pointer_shape); 35562306a36Sopenharmony_ci msg.ptr_shape.part_idx = CURSOR_COMPLETE; 35662306a36Sopenharmony_ci msg.ptr_shape.is_argb = 1; 35762306a36Sopenharmony_ci msg.ptr_shape.width = 1; 35862306a36Sopenharmony_ci msg.ptr_shape.height = 1; 35962306a36Sopenharmony_ci msg.ptr_shape.hot_x = 0; 36062306a36Sopenharmony_ci msg.ptr_shape.hot_y = 0; 36162306a36Sopenharmony_ci msg.ptr_shape.data[0] = 0; 36262306a36Sopenharmony_ci msg.ptr_shape.data[1] = 1; 36362306a36Sopenharmony_ci msg.ptr_shape.data[2] = 1; 36462306a36Sopenharmony_ci msg.ptr_shape.data[3] = 1; 36562306a36Sopenharmony_ci synthvid_send(hdev, &msg); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci/* Send updated screen area (dirty rectangle) location to host */ 37162306a36Sopenharmony_cistatic int 37262306a36Sopenharmony_cisynthvid_update(struct fb_info *info, int x1, int y1, int x2, int y2) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct hv_device *hdev = device_to_hv_device(info->device); 37562306a36Sopenharmony_ci struct synthvid_msg msg; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci memset(&msg, 0, sizeof(struct synthvid_msg)); 37862306a36Sopenharmony_ci if (x2 == INT_MAX) 37962306a36Sopenharmony_ci x2 = info->var.xres; 38062306a36Sopenharmony_ci if (y2 == INT_MAX) 38162306a36Sopenharmony_ci y2 = info->var.yres; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci msg.vid_hdr.type = SYNTHVID_DIRT; 38462306a36Sopenharmony_ci msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 38562306a36Sopenharmony_ci sizeof(struct synthvid_dirt); 38662306a36Sopenharmony_ci msg.dirt.video_output = 0; 38762306a36Sopenharmony_ci msg.dirt.dirt_count = 1; 38862306a36Sopenharmony_ci msg.dirt.rect[0].x1 = (x1 > x2) ? 0 : x1; 38962306a36Sopenharmony_ci msg.dirt.rect[0].y1 = (y1 > y2) ? 0 : y1; 39062306a36Sopenharmony_ci msg.dirt.rect[0].x2 = 39162306a36Sopenharmony_ci (x2 < x1 || x2 > info->var.xres) ? info->var.xres : x2; 39262306a36Sopenharmony_ci msg.dirt.rect[0].y2 = 39362306a36Sopenharmony_ci (y2 < y1 || y2 > info->var.yres) ? info->var.yres : y2; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci synthvid_send(hdev, &msg); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return 0; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic void hvfb_docopy(struct hvfb_par *par, 40162306a36Sopenharmony_ci unsigned long offset, 40262306a36Sopenharmony_ci unsigned long size) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci if (!par || !par->mmio_vp || !par->dio_vp || !par->fb_ready || 40562306a36Sopenharmony_ci size == 0 || offset >= dio_fb_size) 40662306a36Sopenharmony_ci return; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (offset + size > dio_fb_size) 40962306a36Sopenharmony_ci size = dio_fb_size - offset; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci memcpy(par->mmio_vp + offset, par->dio_vp + offset, size); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci/* Deferred IO callback */ 41562306a36Sopenharmony_cistatic void synthvid_deferred_io(struct fb_info *p, struct list_head *pagereflist) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct hvfb_par *par = p->par; 41862306a36Sopenharmony_ci struct fb_deferred_io_pageref *pageref; 41962306a36Sopenharmony_ci unsigned long start, end; 42062306a36Sopenharmony_ci int y1, y2, miny, maxy; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci miny = INT_MAX; 42362306a36Sopenharmony_ci maxy = 0; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* 42662306a36Sopenharmony_ci * Merge dirty pages. It is possible that last page cross 42762306a36Sopenharmony_ci * over the end of frame buffer row yres. This is taken care of 42862306a36Sopenharmony_ci * in synthvid_update function by clamping the y2 42962306a36Sopenharmony_ci * value to yres. 43062306a36Sopenharmony_ci */ 43162306a36Sopenharmony_ci list_for_each_entry(pageref, pagereflist, list) { 43262306a36Sopenharmony_ci start = pageref->offset; 43362306a36Sopenharmony_ci end = start + PAGE_SIZE - 1; 43462306a36Sopenharmony_ci y1 = start / p->fix.line_length; 43562306a36Sopenharmony_ci y2 = end / p->fix.line_length; 43662306a36Sopenharmony_ci miny = min_t(int, miny, y1); 43762306a36Sopenharmony_ci maxy = max_t(int, maxy, y2); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* Copy from dio space to mmio address */ 44062306a36Sopenharmony_ci if (par->fb_ready && par->need_docopy) 44162306a36Sopenharmony_ci hvfb_docopy(par, start, PAGE_SIZE); 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (par->fb_ready && par->update) 44562306a36Sopenharmony_ci synthvid_update(p, 0, miny, p->var.xres, maxy + 1); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic struct fb_deferred_io synthvid_defio = { 44962306a36Sopenharmony_ci .delay = HZ / 20, 45062306a36Sopenharmony_ci .deferred_io = synthvid_deferred_io, 45162306a36Sopenharmony_ci}; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/* 45462306a36Sopenharmony_ci * Actions on received messages from host: 45562306a36Sopenharmony_ci * Complete the wait event. 45662306a36Sopenharmony_ci * Or, reply with screen and cursor info. 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_cistatic void synthvid_recv_sub(struct hv_device *hdev) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 46162306a36Sopenharmony_ci struct hvfb_par *par; 46262306a36Sopenharmony_ci struct synthvid_msg *msg; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (!info) 46562306a36Sopenharmony_ci return; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci par = info->par; 46862306a36Sopenharmony_ci msg = (struct synthvid_msg *)par->recv_buf; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* Complete the wait event */ 47162306a36Sopenharmony_ci if (msg->vid_hdr.type == SYNTHVID_VERSION_RESPONSE || 47262306a36Sopenharmony_ci msg->vid_hdr.type == SYNTHVID_RESOLUTION_RESPONSE || 47362306a36Sopenharmony_ci msg->vid_hdr.type == SYNTHVID_VRAM_LOCATION_ACK) { 47462306a36Sopenharmony_ci memcpy(par->init_buf, msg, MAX_VMBUS_PKT_SIZE); 47562306a36Sopenharmony_ci complete(&par->wait); 47662306a36Sopenharmony_ci return; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* Reply with screen and cursor info */ 48062306a36Sopenharmony_ci if (msg->vid_hdr.type == SYNTHVID_FEATURE_CHANGE) { 48162306a36Sopenharmony_ci if (par->fb_ready) { 48262306a36Sopenharmony_ci synthvid_send_ptr(hdev); 48362306a36Sopenharmony_ci synthvid_send_situ(hdev); 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci par->update = msg->feature_chg.is_dirt_needed; 48762306a36Sopenharmony_ci if (par->update) 48862306a36Sopenharmony_ci schedule_delayed_work(&par->dwork, HVFB_UPDATE_DELAY); 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci/* Receive callback for messages from the host */ 49362306a36Sopenharmony_cistatic void synthvid_receive(void *ctx) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci struct hv_device *hdev = ctx; 49662306a36Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 49762306a36Sopenharmony_ci struct hvfb_par *par; 49862306a36Sopenharmony_ci struct synthvid_msg *recv_buf; 49962306a36Sopenharmony_ci u32 bytes_recvd; 50062306a36Sopenharmony_ci u64 req_id; 50162306a36Sopenharmony_ci int ret; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (!info) 50462306a36Sopenharmony_ci return; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci par = info->par; 50762306a36Sopenharmony_ci recv_buf = (struct synthvid_msg *)par->recv_buf; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci do { 51062306a36Sopenharmony_ci ret = vmbus_recvpacket(hdev->channel, recv_buf, 51162306a36Sopenharmony_ci MAX_VMBUS_PKT_SIZE, 51262306a36Sopenharmony_ci &bytes_recvd, &req_id); 51362306a36Sopenharmony_ci if (bytes_recvd > 0 && 51462306a36Sopenharmony_ci recv_buf->pipe_hdr.type == PIPE_MSG_DATA) 51562306a36Sopenharmony_ci synthvid_recv_sub(hdev); 51662306a36Sopenharmony_ci } while (bytes_recvd > 0 && ret == 0); 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci/* Check if the ver1 version is equal or greater than ver2 */ 52062306a36Sopenharmony_cistatic inline bool synthvid_ver_ge(u32 ver1, u32 ver2) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci if (SYNTHVID_VER_GET_MAJOR(ver1) > SYNTHVID_VER_GET_MAJOR(ver2) || 52362306a36Sopenharmony_ci (SYNTHVID_VER_GET_MAJOR(ver1) == SYNTHVID_VER_GET_MAJOR(ver2) && 52462306a36Sopenharmony_ci SYNTHVID_VER_GET_MINOR(ver1) >= SYNTHVID_VER_GET_MINOR(ver2))) 52562306a36Sopenharmony_ci return true; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return false; 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci/* Check synthetic video protocol version with the host */ 53162306a36Sopenharmony_cistatic int synthvid_negotiate_ver(struct hv_device *hdev, u32 ver) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 53462306a36Sopenharmony_ci struct hvfb_par *par = info->par; 53562306a36Sopenharmony_ci struct synthvid_msg *msg = (struct synthvid_msg *)par->init_buf; 53662306a36Sopenharmony_ci int ret = 0; 53762306a36Sopenharmony_ci unsigned long t; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci memset(msg, 0, sizeof(struct synthvid_msg)); 54062306a36Sopenharmony_ci msg->vid_hdr.type = SYNTHVID_VERSION_REQUEST; 54162306a36Sopenharmony_ci msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 54262306a36Sopenharmony_ci sizeof(struct synthvid_version_req); 54362306a36Sopenharmony_ci msg->ver_req.version = ver; 54462306a36Sopenharmony_ci synthvid_send(hdev, msg); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci t = wait_for_completion_timeout(&par->wait, VSP_TIMEOUT); 54762306a36Sopenharmony_ci if (!t) { 54862306a36Sopenharmony_ci pr_err("Time out on waiting version response\n"); 54962306a36Sopenharmony_ci ret = -ETIMEDOUT; 55062306a36Sopenharmony_ci goto out; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci if (!msg->ver_resp.is_accepted) { 55362306a36Sopenharmony_ci ret = -ENODEV; 55462306a36Sopenharmony_ci goto out; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci par->synthvid_version = ver; 55862306a36Sopenharmony_ci pr_info("Synthvid Version major %d, minor %d\n", 55962306a36Sopenharmony_ci SYNTHVID_VER_GET_MAJOR(ver), SYNTHVID_VER_GET_MINOR(ver)); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ciout: 56262306a36Sopenharmony_ci return ret; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci/* Get current resolution from the host */ 56662306a36Sopenharmony_cistatic int synthvid_get_supported_resolution(struct hv_device *hdev) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 56962306a36Sopenharmony_ci struct hvfb_par *par = info->par; 57062306a36Sopenharmony_ci struct synthvid_msg *msg = (struct synthvid_msg *)par->init_buf; 57162306a36Sopenharmony_ci int ret = 0; 57262306a36Sopenharmony_ci unsigned long t; 57362306a36Sopenharmony_ci u8 index; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci memset(msg, 0, sizeof(struct synthvid_msg)); 57662306a36Sopenharmony_ci msg->vid_hdr.type = SYNTHVID_RESOLUTION_REQUEST; 57762306a36Sopenharmony_ci msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 57862306a36Sopenharmony_ci sizeof(struct synthvid_supported_resolution_req); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci msg->resolution_req.maximum_resolution_count = 58162306a36Sopenharmony_ci SYNTHVID_MAX_RESOLUTION_COUNT; 58262306a36Sopenharmony_ci synthvid_send(hdev, msg); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci t = wait_for_completion_timeout(&par->wait, VSP_TIMEOUT); 58562306a36Sopenharmony_ci if (!t) { 58662306a36Sopenharmony_ci pr_err("Time out on waiting resolution response\n"); 58762306a36Sopenharmony_ci ret = -ETIMEDOUT; 58862306a36Sopenharmony_ci goto out; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (msg->resolution_resp.resolution_count == 0) { 59262306a36Sopenharmony_ci pr_err("No supported resolutions\n"); 59362306a36Sopenharmony_ci ret = -ENODEV; 59462306a36Sopenharmony_ci goto out; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci index = msg->resolution_resp.default_resolution_index; 59862306a36Sopenharmony_ci if (index >= msg->resolution_resp.resolution_count) { 59962306a36Sopenharmony_ci pr_err("Invalid resolution index: %d\n", index); 60062306a36Sopenharmony_ci ret = -ENODEV; 60162306a36Sopenharmony_ci goto out; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci screen_width = 60562306a36Sopenharmony_ci msg->resolution_resp.supported_resolution[index].width; 60662306a36Sopenharmony_ci screen_height = 60762306a36Sopenharmony_ci msg->resolution_resp.supported_resolution[index].height; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ciout: 61062306a36Sopenharmony_ci return ret; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci/* Connect to VSP (Virtual Service Provider) on host */ 61462306a36Sopenharmony_cistatic int synthvid_connect_vsp(struct hv_device *hdev) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 61762306a36Sopenharmony_ci struct hvfb_par *par = info->par; 61862306a36Sopenharmony_ci int ret; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci ret = vmbus_open(hdev->channel, RING_BUFSIZE, RING_BUFSIZE, 62162306a36Sopenharmony_ci NULL, 0, synthvid_receive, hdev); 62262306a36Sopenharmony_ci if (ret) { 62362306a36Sopenharmony_ci pr_err("Unable to open vmbus channel\n"); 62462306a36Sopenharmony_ci return ret; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* Negotiate the protocol version with host */ 62862306a36Sopenharmony_ci switch (vmbus_proto_version) { 62962306a36Sopenharmony_ci case VERSION_WIN10: 63062306a36Sopenharmony_ci case VERSION_WIN10_V5: 63162306a36Sopenharmony_ci ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN10); 63262306a36Sopenharmony_ci if (!ret) 63362306a36Sopenharmony_ci break; 63462306a36Sopenharmony_ci fallthrough; 63562306a36Sopenharmony_ci case VERSION_WIN8: 63662306a36Sopenharmony_ci case VERSION_WIN8_1: 63762306a36Sopenharmony_ci ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN8); 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci default: 64062306a36Sopenharmony_ci ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN10); 64162306a36Sopenharmony_ci break; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (ret) { 64562306a36Sopenharmony_ci pr_err("Synthetic video device version not accepted\n"); 64662306a36Sopenharmony_ci goto error; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci screen_depth = SYNTHVID_DEPTH_WIN8; 65062306a36Sopenharmony_ci if (synthvid_ver_ge(par->synthvid_version, SYNTHVID_VERSION_WIN10)) { 65162306a36Sopenharmony_ci ret = synthvid_get_supported_resolution(hdev); 65262306a36Sopenharmony_ci if (ret) 65362306a36Sopenharmony_ci pr_info("Failed to get supported resolution from host, use default\n"); 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci screen_fb_size = hdev->channel->offermsg.offer. 65762306a36Sopenharmony_ci mmio_megabytes * 1024 * 1024; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci return 0; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cierror: 66262306a36Sopenharmony_ci vmbus_close(hdev->channel); 66362306a36Sopenharmony_ci return ret; 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci/* Send VRAM and Situation messages to the host */ 66762306a36Sopenharmony_cistatic int synthvid_send_config(struct hv_device *hdev) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 67062306a36Sopenharmony_ci struct hvfb_par *par = info->par; 67162306a36Sopenharmony_ci struct synthvid_msg *msg = (struct synthvid_msg *)par->init_buf; 67262306a36Sopenharmony_ci int ret = 0; 67362306a36Sopenharmony_ci unsigned long t; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci /* Send VRAM location */ 67662306a36Sopenharmony_ci memset(msg, 0, sizeof(struct synthvid_msg)); 67762306a36Sopenharmony_ci msg->vid_hdr.type = SYNTHVID_VRAM_LOCATION; 67862306a36Sopenharmony_ci msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 67962306a36Sopenharmony_ci sizeof(struct synthvid_vram_location); 68062306a36Sopenharmony_ci msg->vram.user_ctx = msg->vram.vram_gpa = par->mmio_pp; 68162306a36Sopenharmony_ci msg->vram.is_vram_gpa_specified = 1; 68262306a36Sopenharmony_ci synthvid_send(hdev, msg); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci t = wait_for_completion_timeout(&par->wait, VSP_TIMEOUT); 68562306a36Sopenharmony_ci if (!t) { 68662306a36Sopenharmony_ci pr_err("Time out on waiting vram location ack\n"); 68762306a36Sopenharmony_ci ret = -ETIMEDOUT; 68862306a36Sopenharmony_ci goto out; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci if (msg->vram_ack.user_ctx != par->mmio_pp) { 69162306a36Sopenharmony_ci pr_err("Unable to set VRAM location\n"); 69262306a36Sopenharmony_ci ret = -ENODEV; 69362306a36Sopenharmony_ci goto out; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci /* Send pointer and situation update */ 69762306a36Sopenharmony_ci synthvid_send_ptr(hdev); 69862306a36Sopenharmony_ci synthvid_send_situ(hdev); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ciout: 70162306a36Sopenharmony_ci return ret; 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci/* 70662306a36Sopenharmony_ci * Delayed work callback: 70762306a36Sopenharmony_ci * It is scheduled to call whenever update request is received and it has 70862306a36Sopenharmony_ci * not been called in last HVFB_ONDEMAND_THROTTLE time interval. 70962306a36Sopenharmony_ci */ 71062306a36Sopenharmony_cistatic void hvfb_update_work(struct work_struct *w) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci struct hvfb_par *par = container_of(w, struct hvfb_par, dwork.work); 71362306a36Sopenharmony_ci struct fb_info *info = par->info; 71462306a36Sopenharmony_ci unsigned long flags; 71562306a36Sopenharmony_ci int x1, x2, y1, y2; 71662306a36Sopenharmony_ci int j; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci spin_lock_irqsave(&par->delayed_refresh_lock, flags); 71962306a36Sopenharmony_ci /* Reset the request flag */ 72062306a36Sopenharmony_ci par->delayed_refresh = false; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* Store the dirty rectangle to local variables */ 72362306a36Sopenharmony_ci x1 = par->x1; 72462306a36Sopenharmony_ci x2 = par->x2; 72562306a36Sopenharmony_ci y1 = par->y1; 72662306a36Sopenharmony_ci y2 = par->y2; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* Clear dirty rectangle */ 72962306a36Sopenharmony_ci par->x1 = par->y1 = INT_MAX; 73062306a36Sopenharmony_ci par->x2 = par->y2 = 0; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci spin_unlock_irqrestore(&par->delayed_refresh_lock, flags); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (x1 > info->var.xres || x2 > info->var.xres || 73562306a36Sopenharmony_ci y1 > info->var.yres || y2 > info->var.yres || x2 <= x1) 73662306a36Sopenharmony_ci return; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* Copy the dirty rectangle to frame buffer memory */ 73962306a36Sopenharmony_ci if (par->need_docopy) 74062306a36Sopenharmony_ci for (j = y1; j < y2; j++) 74162306a36Sopenharmony_ci hvfb_docopy(par, 74262306a36Sopenharmony_ci j * info->fix.line_length + 74362306a36Sopenharmony_ci (x1 * screen_depth / 8), 74462306a36Sopenharmony_ci (x2 - x1) * screen_depth / 8); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* Refresh */ 74762306a36Sopenharmony_ci if (par->fb_ready && par->update) 74862306a36Sopenharmony_ci synthvid_update(info, x1, y1, x2, y2); 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci/* 75262306a36Sopenharmony_ci * Control the on-demand refresh frequency. It schedules a delayed 75362306a36Sopenharmony_ci * screen update if it has not yet. 75462306a36Sopenharmony_ci */ 75562306a36Sopenharmony_cistatic void hvfb_ondemand_refresh_throttle(struct hvfb_par *par, 75662306a36Sopenharmony_ci int x1, int y1, int w, int h) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci unsigned long flags; 75962306a36Sopenharmony_ci int x2 = x1 + w; 76062306a36Sopenharmony_ci int y2 = y1 + h; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci spin_lock_irqsave(&par->delayed_refresh_lock, flags); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci /* Merge dirty rectangle */ 76562306a36Sopenharmony_ci par->x1 = min_t(int, par->x1, x1); 76662306a36Sopenharmony_ci par->y1 = min_t(int, par->y1, y1); 76762306a36Sopenharmony_ci par->x2 = max_t(int, par->x2, x2); 76862306a36Sopenharmony_ci par->y2 = max_t(int, par->y2, y2); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* Schedule a delayed screen update if not yet */ 77162306a36Sopenharmony_ci if (par->delayed_refresh == false) { 77262306a36Sopenharmony_ci schedule_delayed_work(&par->dwork, 77362306a36Sopenharmony_ci HVFB_ONDEMAND_THROTTLE); 77462306a36Sopenharmony_ci par->delayed_refresh = true; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci spin_unlock_irqrestore(&par->delayed_refresh_lock, flags); 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic int hvfb_on_panic(struct notifier_block *nb, 78162306a36Sopenharmony_ci unsigned long e, void *p) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci struct hv_device *hdev; 78462306a36Sopenharmony_ci struct hvfb_par *par; 78562306a36Sopenharmony_ci struct fb_info *info; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci par = container_of(nb, struct hvfb_par, hvfb_panic_nb); 78862306a36Sopenharmony_ci info = par->info; 78962306a36Sopenharmony_ci hdev = device_to_hv_device(info->device); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (hv_ringbuffer_spinlock_busy(hdev->channel)) 79262306a36Sopenharmony_ci return NOTIFY_DONE; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci par->synchronous_fb = true; 79562306a36Sopenharmony_ci if (par->need_docopy) 79662306a36Sopenharmony_ci hvfb_docopy(par, 0, dio_fb_size); 79762306a36Sopenharmony_ci synthvid_update(info, 0, 0, INT_MAX, INT_MAX); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci return NOTIFY_DONE; 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci/* Framebuffer operation handlers */ 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cistatic int hvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci if (var->xres < HVFB_WIDTH_MIN || var->yres < HVFB_HEIGHT_MIN || 80762306a36Sopenharmony_ci var->xres > screen_width || var->yres > screen_height || 80862306a36Sopenharmony_ci var->bits_per_pixel != screen_depth) 80962306a36Sopenharmony_ci return -EINVAL; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci var->xres_virtual = var->xres; 81262306a36Sopenharmony_ci var->yres_virtual = var->yres; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci return 0; 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cistatic int hvfb_set_par(struct fb_info *info) 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci struct hv_device *hdev = device_to_hv_device(info->device); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci return synthvid_send_situ(hdev); 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic inline u32 chan_to_field(u32 chan, struct fb_bitfield *bf) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset; 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_cistatic int hvfb_setcolreg(unsigned regno, unsigned red, unsigned green, 83162306a36Sopenharmony_ci unsigned blue, unsigned transp, struct fb_info *info) 83262306a36Sopenharmony_ci{ 83362306a36Sopenharmony_ci u32 *pal = info->pseudo_palette; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if (regno > 15) 83662306a36Sopenharmony_ci return -EINVAL; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci pal[regno] = chan_to_field(red, &info->var.red) 83962306a36Sopenharmony_ci | chan_to_field(green, &info->var.green) 84062306a36Sopenharmony_ci | chan_to_field(blue, &info->var.blue) 84162306a36Sopenharmony_ci | chan_to_field(transp, &info->var.transp); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci return 0; 84462306a36Sopenharmony_ci} 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_cistatic int hvfb_blank(int blank, struct fb_info *info) 84762306a36Sopenharmony_ci{ 84862306a36Sopenharmony_ci return 1; /* get fb_blank to set the colormap to all black */ 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic void hvfb_cfb_fillrect(struct fb_info *p, 85262306a36Sopenharmony_ci const struct fb_fillrect *rect) 85362306a36Sopenharmony_ci{ 85462306a36Sopenharmony_ci struct hvfb_par *par = p->par; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci cfb_fillrect(p, rect); 85762306a36Sopenharmony_ci if (par->synchronous_fb) 85862306a36Sopenharmony_ci synthvid_update(p, 0, 0, INT_MAX, INT_MAX); 85962306a36Sopenharmony_ci else 86062306a36Sopenharmony_ci hvfb_ondemand_refresh_throttle(par, rect->dx, rect->dy, 86162306a36Sopenharmony_ci rect->width, rect->height); 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cistatic void hvfb_cfb_copyarea(struct fb_info *p, 86562306a36Sopenharmony_ci const struct fb_copyarea *area) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci struct hvfb_par *par = p->par; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci cfb_copyarea(p, area); 87062306a36Sopenharmony_ci if (par->synchronous_fb) 87162306a36Sopenharmony_ci synthvid_update(p, 0, 0, INT_MAX, INT_MAX); 87262306a36Sopenharmony_ci else 87362306a36Sopenharmony_ci hvfb_ondemand_refresh_throttle(par, area->dx, area->dy, 87462306a36Sopenharmony_ci area->width, area->height); 87562306a36Sopenharmony_ci} 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_cistatic void hvfb_cfb_imageblit(struct fb_info *p, 87862306a36Sopenharmony_ci const struct fb_image *image) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci struct hvfb_par *par = p->par; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci cfb_imageblit(p, image); 88362306a36Sopenharmony_ci if (par->synchronous_fb) 88462306a36Sopenharmony_ci synthvid_update(p, 0, 0, INT_MAX, INT_MAX); 88562306a36Sopenharmony_ci else 88662306a36Sopenharmony_ci hvfb_ondemand_refresh_throttle(par, image->dx, image->dy, 88762306a36Sopenharmony_ci image->width, image->height); 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_cistatic const struct fb_ops hvfb_ops = { 89162306a36Sopenharmony_ci .owner = THIS_MODULE, 89262306a36Sopenharmony_ci .fb_check_var = hvfb_check_var, 89362306a36Sopenharmony_ci .fb_set_par = hvfb_set_par, 89462306a36Sopenharmony_ci .fb_setcolreg = hvfb_setcolreg, 89562306a36Sopenharmony_ci .fb_fillrect = hvfb_cfb_fillrect, 89662306a36Sopenharmony_ci .fb_copyarea = hvfb_cfb_copyarea, 89762306a36Sopenharmony_ci .fb_imageblit = hvfb_cfb_imageblit, 89862306a36Sopenharmony_ci .fb_blank = hvfb_blank, 89962306a36Sopenharmony_ci .fb_mmap = fb_deferred_io_mmap, 90062306a36Sopenharmony_ci}; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci/* Get options from kernel paramenter "video=" */ 90462306a36Sopenharmony_cistatic void hvfb_get_option(struct fb_info *info) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci struct hvfb_par *par = info->par; 90762306a36Sopenharmony_ci char *opt = NULL, *p; 90862306a36Sopenharmony_ci uint x = 0, y = 0; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci if (fb_get_options(KBUILD_MODNAME, &opt) || !opt || !*opt) 91162306a36Sopenharmony_ci return; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci p = strsep(&opt, "x"); 91462306a36Sopenharmony_ci if (!*p || kstrtouint(p, 0, &x) || 91562306a36Sopenharmony_ci !opt || !*opt || kstrtouint(opt, 0, &y)) { 91662306a36Sopenharmony_ci pr_err("Screen option is invalid: skipped\n"); 91762306a36Sopenharmony_ci return; 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci if (x < HVFB_WIDTH_MIN || y < HVFB_HEIGHT_MIN || 92162306a36Sopenharmony_ci (synthvid_ver_ge(par->synthvid_version, SYNTHVID_VERSION_WIN10) && 92262306a36Sopenharmony_ci (x * y * screen_depth / 8 > screen_fb_size)) || 92362306a36Sopenharmony_ci (par->synthvid_version == SYNTHVID_VERSION_WIN8 && 92462306a36Sopenharmony_ci x * y * screen_depth / 8 > SYNTHVID_FB_SIZE_WIN8)) { 92562306a36Sopenharmony_ci pr_err("Screen resolution option is out of range: skipped\n"); 92662306a36Sopenharmony_ci return; 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci screen_width = x; 93062306a36Sopenharmony_ci screen_height = y; 93162306a36Sopenharmony_ci return; 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci/* 93562306a36Sopenharmony_ci * Allocate enough contiguous physical memory. 93662306a36Sopenharmony_ci * Return physical address if succeeded or -1 if failed. 93762306a36Sopenharmony_ci */ 93862306a36Sopenharmony_cistatic phys_addr_t hvfb_get_phymem(struct hv_device *hdev, 93962306a36Sopenharmony_ci unsigned int request_size) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci struct page *page = NULL; 94262306a36Sopenharmony_ci dma_addr_t dma_handle; 94362306a36Sopenharmony_ci void *vmem; 94462306a36Sopenharmony_ci phys_addr_t paddr = 0; 94562306a36Sopenharmony_ci unsigned int order = get_order(request_size); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci if (request_size == 0) 94862306a36Sopenharmony_ci return -1; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci if (order <= MAX_ORDER) { 95162306a36Sopenharmony_ci /* Call alloc_pages if the size is less than 2^MAX_ORDER */ 95262306a36Sopenharmony_ci page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order); 95362306a36Sopenharmony_ci if (!page) 95462306a36Sopenharmony_ci return -1; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci paddr = (page_to_pfn(page) << PAGE_SHIFT); 95762306a36Sopenharmony_ci } else { 95862306a36Sopenharmony_ci /* Allocate from CMA */ 95962306a36Sopenharmony_ci hdev->device.coherent_dma_mask = DMA_BIT_MASK(64); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci vmem = dma_alloc_coherent(&hdev->device, 96262306a36Sopenharmony_ci round_up(request_size, PAGE_SIZE), 96362306a36Sopenharmony_ci &dma_handle, 96462306a36Sopenharmony_ci GFP_KERNEL | __GFP_NOWARN); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (!vmem) 96762306a36Sopenharmony_ci return -1; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci paddr = virt_to_phys(vmem); 97062306a36Sopenharmony_ci } 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci return paddr; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci/* Release contiguous physical memory */ 97662306a36Sopenharmony_cistatic void hvfb_release_phymem(struct hv_device *hdev, 97762306a36Sopenharmony_ci phys_addr_t paddr, unsigned int size) 97862306a36Sopenharmony_ci{ 97962306a36Sopenharmony_ci unsigned int order = get_order(size); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci if (order <= MAX_ORDER) 98262306a36Sopenharmony_ci __free_pages(pfn_to_page(paddr >> PAGE_SHIFT), order); 98362306a36Sopenharmony_ci else 98462306a36Sopenharmony_ci dma_free_coherent(&hdev->device, 98562306a36Sopenharmony_ci round_up(size, PAGE_SIZE), 98662306a36Sopenharmony_ci phys_to_virt(paddr), 98762306a36Sopenharmony_ci paddr); 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci/* Get framebuffer memory from Hyper-V video pci space */ 99262306a36Sopenharmony_cistatic int hvfb_getmem(struct hv_device *hdev, struct fb_info *info) 99362306a36Sopenharmony_ci{ 99462306a36Sopenharmony_ci struct hvfb_par *par = info->par; 99562306a36Sopenharmony_ci struct pci_dev *pdev = NULL; 99662306a36Sopenharmony_ci void __iomem *fb_virt; 99762306a36Sopenharmony_ci int gen2vm = efi_enabled(EFI_BOOT); 99862306a36Sopenharmony_ci resource_size_t base, size; 99962306a36Sopenharmony_ci phys_addr_t paddr; 100062306a36Sopenharmony_ci int ret; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci if (!gen2vm) { 100362306a36Sopenharmony_ci pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT, 100462306a36Sopenharmony_ci PCI_DEVICE_ID_HYPERV_VIDEO, NULL); 100562306a36Sopenharmony_ci if (!pdev) { 100662306a36Sopenharmony_ci pr_err("Unable to find PCI Hyper-V video\n"); 100762306a36Sopenharmony_ci return -ENODEV; 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci base = pci_resource_start(pdev, 0); 101162306a36Sopenharmony_ci size = pci_resource_len(pdev, 0); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci /* 101462306a36Sopenharmony_ci * For Gen 1 VM, we can directly use the contiguous memory 101562306a36Sopenharmony_ci * from VM. If we succeed, deferred IO happens directly 101662306a36Sopenharmony_ci * on this allocated framebuffer memory, avoiding extra 101762306a36Sopenharmony_ci * memory copy. 101862306a36Sopenharmony_ci */ 101962306a36Sopenharmony_ci paddr = hvfb_get_phymem(hdev, screen_fb_size); 102062306a36Sopenharmony_ci if (paddr != (phys_addr_t) -1) { 102162306a36Sopenharmony_ci par->mmio_pp = paddr; 102262306a36Sopenharmony_ci par->mmio_vp = par->dio_vp = __va(paddr); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci info->fix.smem_start = paddr; 102562306a36Sopenharmony_ci info->fix.smem_len = screen_fb_size; 102662306a36Sopenharmony_ci info->screen_base = par->mmio_vp; 102762306a36Sopenharmony_ci info->screen_size = screen_fb_size; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci par->need_docopy = false; 103062306a36Sopenharmony_ci goto getmem_done; 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci pr_info("Unable to allocate enough contiguous physical memory on Gen 1 VM. Using MMIO instead.\n"); 103362306a36Sopenharmony_ci } else { 103462306a36Sopenharmony_ci base = screen_info.lfb_base; 103562306a36Sopenharmony_ci size = screen_info.lfb_size; 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci /* 103962306a36Sopenharmony_ci * Cannot use the contiguous physical memory. 104062306a36Sopenharmony_ci * Allocate mmio space for framebuffer. 104162306a36Sopenharmony_ci */ 104262306a36Sopenharmony_ci dio_fb_size = 104362306a36Sopenharmony_ci screen_width * screen_height * screen_depth / 8; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci ret = vmbus_allocate_mmio(&par->mem, hdev, 0, -1, 104662306a36Sopenharmony_ci screen_fb_size, 0x100000, true); 104762306a36Sopenharmony_ci if (ret != 0) { 104862306a36Sopenharmony_ci pr_err("Unable to allocate framebuffer memory\n"); 104962306a36Sopenharmony_ci goto err1; 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci /* 105362306a36Sopenharmony_ci * Map the VRAM cacheable for performance. This is also required for 105462306a36Sopenharmony_ci * VM Connect to display properly for ARM64 Linux VM, as the host also 105562306a36Sopenharmony_ci * maps the VRAM cacheable. 105662306a36Sopenharmony_ci */ 105762306a36Sopenharmony_ci fb_virt = ioremap_cache(par->mem->start, screen_fb_size); 105862306a36Sopenharmony_ci if (!fb_virt) 105962306a36Sopenharmony_ci goto err2; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci /* Allocate memory for deferred IO */ 106262306a36Sopenharmony_ci par->dio_vp = vzalloc(round_up(dio_fb_size, PAGE_SIZE)); 106362306a36Sopenharmony_ci if (par->dio_vp == NULL) 106462306a36Sopenharmony_ci goto err3; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci /* Physical address of FB device */ 106762306a36Sopenharmony_ci par->mmio_pp = par->mem->start; 106862306a36Sopenharmony_ci /* Virtual address of FB device */ 106962306a36Sopenharmony_ci par->mmio_vp = (unsigned char *) fb_virt; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci info->fix.smem_start = par->mem->start; 107262306a36Sopenharmony_ci info->fix.smem_len = dio_fb_size; 107362306a36Sopenharmony_ci info->screen_base = par->dio_vp; 107462306a36Sopenharmony_ci info->screen_size = dio_fb_size; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_cigetmem_done: 107762306a36Sopenharmony_ci aperture_remove_conflicting_devices(base, size, KBUILD_MODNAME); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci if (gen2vm) { 108062306a36Sopenharmony_ci /* framebuffer is reallocated, clear screen_info to avoid misuse from kexec */ 108162306a36Sopenharmony_ci screen_info.lfb_size = 0; 108262306a36Sopenharmony_ci screen_info.lfb_base = 0; 108362306a36Sopenharmony_ci screen_info.orig_video_isVGA = 0; 108462306a36Sopenharmony_ci } else { 108562306a36Sopenharmony_ci pci_dev_put(pdev); 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci return 0; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_cierr3: 109162306a36Sopenharmony_ci iounmap(fb_virt); 109262306a36Sopenharmony_cierr2: 109362306a36Sopenharmony_ci vmbus_free_mmio(par->mem->start, screen_fb_size); 109462306a36Sopenharmony_ci par->mem = NULL; 109562306a36Sopenharmony_cierr1: 109662306a36Sopenharmony_ci if (!gen2vm) 109762306a36Sopenharmony_ci pci_dev_put(pdev); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci return -ENOMEM; 110062306a36Sopenharmony_ci} 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci/* Release the framebuffer */ 110362306a36Sopenharmony_cistatic void hvfb_putmem(struct hv_device *hdev, struct fb_info *info) 110462306a36Sopenharmony_ci{ 110562306a36Sopenharmony_ci struct hvfb_par *par = info->par; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci if (par->need_docopy) { 110862306a36Sopenharmony_ci vfree(par->dio_vp); 110962306a36Sopenharmony_ci iounmap(info->screen_base); 111062306a36Sopenharmony_ci vmbus_free_mmio(par->mem->start, screen_fb_size); 111162306a36Sopenharmony_ci } else { 111262306a36Sopenharmony_ci hvfb_release_phymem(hdev, info->fix.smem_start, 111362306a36Sopenharmony_ci screen_fb_size); 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci par->mem = NULL; 111762306a36Sopenharmony_ci} 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_cistatic int hvfb_probe(struct hv_device *hdev, 112162306a36Sopenharmony_ci const struct hv_vmbus_device_id *dev_id) 112262306a36Sopenharmony_ci{ 112362306a36Sopenharmony_ci struct fb_info *info; 112462306a36Sopenharmony_ci struct hvfb_par *par; 112562306a36Sopenharmony_ci int ret; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci info = framebuffer_alloc(sizeof(struct hvfb_par), &hdev->device); 112862306a36Sopenharmony_ci if (!info) 112962306a36Sopenharmony_ci return -ENOMEM; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci par = info->par; 113262306a36Sopenharmony_ci par->info = info; 113362306a36Sopenharmony_ci par->fb_ready = false; 113462306a36Sopenharmony_ci par->need_docopy = true; 113562306a36Sopenharmony_ci init_completion(&par->wait); 113662306a36Sopenharmony_ci INIT_DELAYED_WORK(&par->dwork, hvfb_update_work); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci par->delayed_refresh = false; 113962306a36Sopenharmony_ci spin_lock_init(&par->delayed_refresh_lock); 114062306a36Sopenharmony_ci par->x1 = par->y1 = INT_MAX; 114162306a36Sopenharmony_ci par->x2 = par->y2 = 0; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci /* Connect to VSP */ 114462306a36Sopenharmony_ci hv_set_drvdata(hdev, info); 114562306a36Sopenharmony_ci ret = synthvid_connect_vsp(hdev); 114662306a36Sopenharmony_ci if (ret) { 114762306a36Sopenharmony_ci pr_err("Unable to connect to VSP\n"); 114862306a36Sopenharmony_ci goto error1; 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci hvfb_get_option(info); 115262306a36Sopenharmony_ci pr_info("Screen resolution: %dx%d, Color depth: %d, Frame buffer size: %d\n", 115362306a36Sopenharmony_ci screen_width, screen_height, screen_depth, screen_fb_size); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci ret = hvfb_getmem(hdev, info); 115662306a36Sopenharmony_ci if (ret) { 115762306a36Sopenharmony_ci pr_err("No memory for framebuffer\n"); 115862306a36Sopenharmony_ci goto error2; 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci /* Set up fb_info */ 116262306a36Sopenharmony_ci info->var.xres_virtual = info->var.xres = screen_width; 116362306a36Sopenharmony_ci info->var.yres_virtual = info->var.yres = screen_height; 116462306a36Sopenharmony_ci info->var.bits_per_pixel = screen_depth; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci if (info->var.bits_per_pixel == 16) { 116762306a36Sopenharmony_ci info->var.red = (struct fb_bitfield){11, 5, 0}; 116862306a36Sopenharmony_ci info->var.green = (struct fb_bitfield){5, 6, 0}; 116962306a36Sopenharmony_ci info->var.blue = (struct fb_bitfield){0, 5, 0}; 117062306a36Sopenharmony_ci info->var.transp = (struct fb_bitfield){0, 0, 0}; 117162306a36Sopenharmony_ci } else { 117262306a36Sopenharmony_ci info->var.red = (struct fb_bitfield){16, 8, 0}; 117362306a36Sopenharmony_ci info->var.green = (struct fb_bitfield){8, 8, 0}; 117462306a36Sopenharmony_ci info->var.blue = (struct fb_bitfield){0, 8, 0}; 117562306a36Sopenharmony_ci info->var.transp = (struct fb_bitfield){24, 8, 0}; 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci info->var.activate = FB_ACTIVATE_NOW; 117962306a36Sopenharmony_ci info->var.height = -1; 118062306a36Sopenharmony_ci info->var.width = -1; 118162306a36Sopenharmony_ci info->var.vmode = FB_VMODE_NONINTERLACED; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci strcpy(info->fix.id, KBUILD_MODNAME); 118462306a36Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 118562306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 118662306a36Sopenharmony_ci info->fix.line_length = screen_width * screen_depth / 8; 118762306a36Sopenharmony_ci info->fix.accel = FB_ACCEL_NONE; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci info->fbops = &hvfb_ops; 119062306a36Sopenharmony_ci info->pseudo_palette = par->pseudo_palette; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci /* Initialize deferred IO */ 119362306a36Sopenharmony_ci info->fbdefio = &synthvid_defio; 119462306a36Sopenharmony_ci fb_deferred_io_init(info); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci /* Send config to host */ 119762306a36Sopenharmony_ci ret = synthvid_send_config(hdev); 119862306a36Sopenharmony_ci if (ret) 119962306a36Sopenharmony_ci goto error; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci ret = register_framebuffer(info); 120262306a36Sopenharmony_ci if (ret) { 120362306a36Sopenharmony_ci pr_err("Unable to register framebuffer\n"); 120462306a36Sopenharmony_ci goto error; 120562306a36Sopenharmony_ci } 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci par->fb_ready = true; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci par->synchronous_fb = false; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci /* 121262306a36Sopenharmony_ci * We need to be sure this panic notifier runs _before_ the 121362306a36Sopenharmony_ci * vmbus disconnect, so order it by priority. It must execute 121462306a36Sopenharmony_ci * before the function hv_panic_vmbus_unload() [drivers/hv/vmbus_drv.c], 121562306a36Sopenharmony_ci * which is almost at the end of list, with priority = INT_MIN + 1. 121662306a36Sopenharmony_ci */ 121762306a36Sopenharmony_ci par->hvfb_panic_nb.notifier_call = hvfb_on_panic; 121862306a36Sopenharmony_ci par->hvfb_panic_nb.priority = INT_MIN + 10, 121962306a36Sopenharmony_ci atomic_notifier_chain_register(&panic_notifier_list, 122062306a36Sopenharmony_ci &par->hvfb_panic_nb); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci return 0; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_cierror: 122562306a36Sopenharmony_ci fb_deferred_io_cleanup(info); 122662306a36Sopenharmony_ci hvfb_putmem(hdev, info); 122762306a36Sopenharmony_cierror2: 122862306a36Sopenharmony_ci vmbus_close(hdev->channel); 122962306a36Sopenharmony_cierror1: 123062306a36Sopenharmony_ci cancel_delayed_work_sync(&par->dwork); 123162306a36Sopenharmony_ci hv_set_drvdata(hdev, NULL); 123262306a36Sopenharmony_ci framebuffer_release(info); 123362306a36Sopenharmony_ci return ret; 123462306a36Sopenharmony_ci} 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_cistatic void hvfb_remove(struct hv_device *hdev) 123762306a36Sopenharmony_ci{ 123862306a36Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 123962306a36Sopenharmony_ci struct hvfb_par *par = info->par; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci atomic_notifier_chain_unregister(&panic_notifier_list, 124262306a36Sopenharmony_ci &par->hvfb_panic_nb); 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci par->update = false; 124562306a36Sopenharmony_ci par->fb_ready = false; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci fb_deferred_io_cleanup(info); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci unregister_framebuffer(info); 125062306a36Sopenharmony_ci cancel_delayed_work_sync(&par->dwork); 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci vmbus_close(hdev->channel); 125362306a36Sopenharmony_ci hv_set_drvdata(hdev, NULL); 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci hvfb_putmem(hdev, info); 125662306a36Sopenharmony_ci framebuffer_release(info); 125762306a36Sopenharmony_ci} 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_cistatic int hvfb_suspend(struct hv_device *hdev) 126062306a36Sopenharmony_ci{ 126162306a36Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 126262306a36Sopenharmony_ci struct hvfb_par *par = info->par; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci console_lock(); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci /* 1 means do suspend */ 126762306a36Sopenharmony_ci fb_set_suspend(info, 1); 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci cancel_delayed_work_sync(&par->dwork); 127062306a36Sopenharmony_ci cancel_delayed_work_sync(&info->deferred_work); 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci par->update_saved = par->update; 127362306a36Sopenharmony_ci par->update = false; 127462306a36Sopenharmony_ci par->fb_ready = false; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci vmbus_close(hdev->channel); 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci console_unlock(); 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci return 0; 128162306a36Sopenharmony_ci} 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_cistatic int hvfb_resume(struct hv_device *hdev) 128462306a36Sopenharmony_ci{ 128562306a36Sopenharmony_ci struct fb_info *info = hv_get_drvdata(hdev); 128662306a36Sopenharmony_ci struct hvfb_par *par = info->par; 128762306a36Sopenharmony_ci int ret; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci console_lock(); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci ret = synthvid_connect_vsp(hdev); 129262306a36Sopenharmony_ci if (ret != 0) 129362306a36Sopenharmony_ci goto out; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci ret = synthvid_send_config(hdev); 129662306a36Sopenharmony_ci if (ret != 0) { 129762306a36Sopenharmony_ci vmbus_close(hdev->channel); 129862306a36Sopenharmony_ci goto out; 129962306a36Sopenharmony_ci } 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci par->fb_ready = true; 130262306a36Sopenharmony_ci par->update = par->update_saved; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci schedule_delayed_work(&info->deferred_work, info->fbdefio->delay); 130562306a36Sopenharmony_ci schedule_delayed_work(&par->dwork, HVFB_UPDATE_DELAY); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci /* 0 means do resume */ 130862306a36Sopenharmony_ci fb_set_suspend(info, 0); 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ciout: 131162306a36Sopenharmony_ci console_unlock(); 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci return ret; 131462306a36Sopenharmony_ci} 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_cistatic const struct pci_device_id pci_stub_id_table[] = { 131862306a36Sopenharmony_ci { 131962306a36Sopenharmony_ci .vendor = PCI_VENDOR_ID_MICROSOFT, 132062306a36Sopenharmony_ci .device = PCI_DEVICE_ID_HYPERV_VIDEO, 132162306a36Sopenharmony_ci }, 132262306a36Sopenharmony_ci { /* end of list */ } 132362306a36Sopenharmony_ci}; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_cistatic const struct hv_vmbus_device_id id_table[] = { 132662306a36Sopenharmony_ci /* Synthetic Video Device GUID */ 132762306a36Sopenharmony_ci {HV_SYNTHVID_GUID}, 132862306a36Sopenharmony_ci {} 132962306a36Sopenharmony_ci}; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pci_stub_id_table); 133262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(vmbus, id_table); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_cistatic struct hv_driver hvfb_drv = { 133562306a36Sopenharmony_ci .name = KBUILD_MODNAME, 133662306a36Sopenharmony_ci .id_table = id_table, 133762306a36Sopenharmony_ci .probe = hvfb_probe, 133862306a36Sopenharmony_ci .remove = hvfb_remove, 133962306a36Sopenharmony_ci .suspend = hvfb_suspend, 134062306a36Sopenharmony_ci .resume = hvfb_resume, 134162306a36Sopenharmony_ci .driver = { 134262306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 134362306a36Sopenharmony_ci }, 134462306a36Sopenharmony_ci}; 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_cistatic int hvfb_pci_stub_probe(struct pci_dev *pdev, 134762306a36Sopenharmony_ci const struct pci_device_id *ent) 134862306a36Sopenharmony_ci{ 134962306a36Sopenharmony_ci return 0; 135062306a36Sopenharmony_ci} 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_cistatic void hvfb_pci_stub_remove(struct pci_dev *pdev) 135362306a36Sopenharmony_ci{ 135462306a36Sopenharmony_ci} 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_cistatic struct pci_driver hvfb_pci_stub_driver = { 135762306a36Sopenharmony_ci .name = KBUILD_MODNAME, 135862306a36Sopenharmony_ci .id_table = pci_stub_id_table, 135962306a36Sopenharmony_ci .probe = hvfb_pci_stub_probe, 136062306a36Sopenharmony_ci .remove = hvfb_pci_stub_remove, 136162306a36Sopenharmony_ci .driver = { 136262306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 136362306a36Sopenharmony_ci } 136462306a36Sopenharmony_ci}; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_cistatic int __init hvfb_drv_init(void) 136762306a36Sopenharmony_ci{ 136862306a36Sopenharmony_ci int ret; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci if (fb_modesetting_disabled("hyper_fb")) 137162306a36Sopenharmony_ci return -ENODEV; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci ret = vmbus_driver_register(&hvfb_drv); 137462306a36Sopenharmony_ci if (ret != 0) 137562306a36Sopenharmony_ci return ret; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci ret = pci_register_driver(&hvfb_pci_stub_driver); 137862306a36Sopenharmony_ci if (ret != 0) { 137962306a36Sopenharmony_ci vmbus_driver_unregister(&hvfb_drv); 138062306a36Sopenharmony_ci return ret; 138162306a36Sopenharmony_ci } 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci return 0; 138462306a36Sopenharmony_ci} 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_cistatic void __exit hvfb_drv_exit(void) 138762306a36Sopenharmony_ci{ 138862306a36Sopenharmony_ci pci_unregister_driver(&hvfb_pci_stub_driver); 138962306a36Sopenharmony_ci vmbus_driver_unregister(&hvfb_drv); 139062306a36Sopenharmony_ci} 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_cimodule_init(hvfb_drv_init); 139362306a36Sopenharmony_cimodule_exit(hvfb_drv_exit); 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 139662306a36Sopenharmony_ciMODULE_DESCRIPTION("Microsoft Hyper-V Synthetic Video Frame Buffer Driver"); 1397