Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/hyperv: Add DRM driver for hyperv synthetic video device

DRM driver for hyperv synthetic video device, based on hyperv_fb
framebuffer driver. Also added config option "DRM_HYPERV" to enabled
this driver.

v2:
- Add support for gen2 VM
- Fixed review comments

v3:
- Split into multiple files as suggested by Thomas Zimmermann
- Fixed hibernation issue as suggested by Dexuan Cui
- Use ioremap_cache as suggested by Dexuan Cui
- Incorporated other review comments

v4:
- Fix bitrotted code
- Review comments
- Updated the copyright and license to match hyperv_fb

v5:
- Address review comments and rebased with drm-misc-next

v6:
- Minor code/comment improvement as suggested by Dexuan Cui

Signed-off-by: Deepak Rawat <drawat.floss@gmail.com>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210527112230.1274-1-drawat.floss@gmail.com

+1091
+13
drivers/gpu/drm/Kconfig
··· 379 379 380 380 source "drivers/gpu/drm/gud/Kconfig" 381 381 382 + config DRM_HYPERV 383 + tristate "DRM Support for Hyper-V synthetic video device" 384 + depends on DRM && PCI && MMU && HYPERV 385 + select DRM_KMS_HELPER 386 + select DRM_GEM_SHMEM_HELPER 387 + help 388 + This is a KMS driver for Hyper-V synthetic video device. Choose this 389 + option if you would like to enable drm driver for Hyper-V virtual 390 + machine. Unselect Hyper-V framebuffer driver (CONFIG_FB_HYPERV) so 391 + that DRM driver is used by default. 392 + 393 + If M is selected the module will be called hyperv_drm. 394 + 382 395 # Keep legacy drivers last 383 396 384 397 menuconfig DRM_LEGACY
+1
drivers/gpu/drm/Makefile
··· 126 126 obj-$(CONFIG_DRM_TIDSS) += tidss/ 127 127 obj-y += xlnx/ 128 128 obj-y += gud/ 129 + obj-$(CONFIG_DRM_HYPERV) += hyperv/
+8
drivers/gpu/drm/hyperv/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + 3 + hyperv_drm-y := \ 4 + hyperv_drm_drv.o \ 5 + hyperv_drm_modeset.o \ 6 + hyperv_drm_proto.o 7 + 8 + obj-$(CONFIG_DRM_HYPERV) += hyperv_drm.o
+51
drivers/gpu/drm/hyperv/hyperv_drm.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright 2021 Microsoft 4 + */ 5 + 6 + #ifndef _HYPERV_DRM_H_ 7 + #define _HYPERV_DRM_H_ 8 + 9 + #define VMBUS_MAX_PACKET_SIZE 0x4000 10 + 11 + struct hyperv_drm_device { 12 + /* drm */ 13 + struct drm_device dev; 14 + struct drm_simple_display_pipe pipe; 15 + struct drm_connector connector; 16 + 17 + /* mode */ 18 + u32 screen_width_max; 19 + u32 screen_height_max; 20 + u32 preferred_width; 21 + u32 preferred_height; 22 + u32 screen_depth; 23 + 24 + /* hw */ 25 + struct resource *mem; 26 + void __iomem *vram; 27 + unsigned long fb_base; 28 + unsigned long fb_size; 29 + struct completion wait; 30 + u32 synthvid_version; 31 + u32 mmio_megabytes; 32 + 33 + u8 init_buf[VMBUS_MAX_PACKET_SIZE]; 34 + u8 recv_buf[VMBUS_MAX_PACKET_SIZE]; 35 + 36 + struct hv_device *hdev; 37 + }; 38 + 39 + #define to_hv(_dev) container_of(_dev, struct hyperv_drm_device, dev) 40 + 41 + /* hyperv_drm_modeset */ 42 + int hyperv_mode_config_init(struct hyperv_drm_device *hv); 43 + 44 + /* hyperv_drm_proto */ 45 + int hyperv_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp); 46 + int hyperv_update_situation(struct hv_device *hdev, u8 active, u32 bpp, 47 + u32 w, u32 h, u32 pitch); 48 + int hyperv_update_dirt(struct hv_device *hdev, struct drm_rect *rect); 49 + int hyperv_connect_vsp(struct hv_device *hdev); 50 + 51 + #endif
+309
drivers/gpu/drm/hyperv/hyperv_drm_drv.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright 2021 Microsoft 4 + */ 5 + 6 + #include <linux/efi.h> 7 + #include <linux/hyperv.h> 8 + #include <linux/module.h> 9 + #include <linux/pci.h> 10 + 11 + #include <drm/drm_aperture.h> 12 + #include <drm/drm_atomic_helper.h> 13 + #include <drm/drm_drv.h> 14 + #include <drm/drm_fb_helper.h> 15 + #include <drm/drm_gem_shmem_helper.h> 16 + #include <drm/drm_simple_kms_helper.h> 17 + 18 + #include "hyperv_drm.h" 19 + 20 + #define DRIVER_NAME "hyperv_drm" 21 + #define DRIVER_DESC "DRM driver for Hyper-V synthetic video device" 22 + #define DRIVER_DATE "2020" 23 + #define DRIVER_MAJOR 1 24 + #define DRIVER_MINOR 0 25 + 26 + #define PCI_VENDOR_ID_MICROSOFT 0x1414 27 + #define PCI_DEVICE_ID_HYPERV_VIDEO 0x5353 28 + 29 + DEFINE_DRM_GEM_FOPS(hv_fops); 30 + 31 + static struct drm_driver hyperv_driver = { 32 + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 33 + 34 + .name = DRIVER_NAME, 35 + .desc = DRIVER_DESC, 36 + .date = DRIVER_DATE, 37 + .major = DRIVER_MAJOR, 38 + .minor = DRIVER_MINOR, 39 + 40 + .fops = &hv_fops, 41 + DRM_GEM_SHMEM_DRIVER_OPS, 42 + }; 43 + 44 + static int hyperv_pci_probe(struct pci_dev *pdev, 45 + const struct pci_device_id *ent) 46 + { 47 + return 0; 48 + } 49 + 50 + static void hyperv_pci_remove(struct pci_dev *pdev) 51 + { 52 + } 53 + 54 + static const struct pci_device_id hyperv_pci_tbl[] = { 55 + { 56 + .vendor = PCI_VENDOR_ID_MICROSOFT, 57 + .device = PCI_DEVICE_ID_HYPERV_VIDEO, 58 + }, 59 + { /* end of list */ } 60 + }; 61 + 62 + /* 63 + * PCI stub to support gen1 VM. 64 + */ 65 + static struct pci_driver hyperv_pci_driver = { 66 + .name = KBUILD_MODNAME, 67 + .id_table = hyperv_pci_tbl, 68 + .probe = hyperv_pci_probe, 69 + .remove = hyperv_pci_remove, 70 + }; 71 + 72 + static int hyperv_setup_gen1(struct hyperv_drm_device *hv) 73 + { 74 + struct drm_device *dev = &hv->dev; 75 + struct pci_dev *pdev; 76 + int ret; 77 + 78 + pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT, 79 + PCI_DEVICE_ID_HYPERV_VIDEO, NULL); 80 + if (!pdev) { 81 + drm_err(dev, "Unable to find PCI Hyper-V video\n"); 82 + return -ENODEV; 83 + } 84 + 85 + ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, "hypervdrmfb"); 86 + if (ret) { 87 + drm_err(dev, "Not able to remove boot fb\n"); 88 + return ret; 89 + } 90 + 91 + if (pci_request_region(pdev, 0, DRIVER_NAME) != 0) 92 + drm_warn(dev, "Cannot request framebuffer, boot fb still active?\n"); 93 + 94 + if ((pdev->resource[0].flags & IORESOURCE_MEM) == 0) { 95 + drm_err(dev, "Resource at bar 0 is not IORESOURCE_MEM\n"); 96 + ret = -ENODEV; 97 + goto error; 98 + } 99 + 100 + hv->fb_base = pci_resource_start(pdev, 0); 101 + hv->fb_size = pci_resource_len(pdev, 0); 102 + if (!hv->fb_base) { 103 + drm_err(dev, "Resource not available\n"); 104 + ret = -ENODEV; 105 + goto error; 106 + } 107 + 108 + hv->fb_size = min(hv->fb_size, 109 + (unsigned long)(hv->mmio_megabytes * 1024 * 1024)); 110 + hv->vram = devm_ioremap(&pdev->dev, hv->fb_base, hv->fb_size); 111 + if (!hv->vram) { 112 + drm_err(dev, "Failed to map vram\n"); 113 + ret = -ENOMEM; 114 + } 115 + 116 + error: 117 + pci_dev_put(pdev); 118 + return ret; 119 + } 120 + 121 + static int hyperv_setup_gen2(struct hyperv_drm_device *hv, 122 + struct hv_device *hdev) 123 + { 124 + struct drm_device *dev = &hv->dev; 125 + int ret; 126 + 127 + drm_aperture_remove_conflicting_framebuffers(screen_info.lfb_base, 128 + screen_info.lfb_size, 129 + false, 130 + "hypervdrmfb"); 131 + 132 + hv->fb_size = (unsigned long)hv->mmio_megabytes * 1024 * 1024; 133 + 134 + ret = vmbus_allocate_mmio(&hv->mem, hdev, 0, -1, hv->fb_size, 0x100000, 135 + true); 136 + if (ret) { 137 + drm_err(dev, "Failed to allocate mmio\n"); 138 + return -ENOMEM; 139 + } 140 + 141 + /* 142 + * Map the VRAM cacheable for performance. This is also required for VM 143 + * connect to display properly for ARM64 Linux VM, as the host also maps 144 + * the VRAM cacheable. 145 + */ 146 + hv->vram = ioremap_cache(hv->mem->start, hv->fb_size); 147 + if (!hv->vram) { 148 + drm_err(dev, "Failed to map vram\n"); 149 + ret = -ENOMEM; 150 + goto error; 151 + } 152 + 153 + hv->fb_base = hv->mem->start; 154 + return 0; 155 + 156 + error: 157 + vmbus_free_mmio(hv->mem->start, hv->fb_size); 158 + return ret; 159 + } 160 + 161 + static int hyperv_vmbus_probe(struct hv_device *hdev, 162 + const struct hv_vmbus_device_id *dev_id) 163 + { 164 + struct hyperv_drm_device *hv; 165 + struct drm_device *dev; 166 + int ret; 167 + 168 + hv = devm_drm_dev_alloc(&hdev->device, &hyperv_driver, 169 + struct hyperv_drm_device, dev); 170 + if (IS_ERR(hv)) 171 + return PTR_ERR(hv); 172 + 173 + dev = &hv->dev; 174 + init_completion(&hv->wait); 175 + hv_set_drvdata(hdev, hv); 176 + hv->hdev = hdev; 177 + 178 + ret = hyperv_connect_vsp(hdev); 179 + if (ret) { 180 + drm_err(dev, "Failed to connect to vmbus.\n"); 181 + goto err_hv_set_drv_data; 182 + } 183 + 184 + if (efi_enabled(EFI_BOOT)) 185 + ret = hyperv_setup_gen2(hv, hdev); 186 + else 187 + ret = hyperv_setup_gen1(hv); 188 + 189 + if (ret) 190 + goto err_vmbus_close; 191 + 192 + /* 193 + * Should be done only once during init and resume. Failing to update 194 + * vram location is not fatal. Device will update dirty area till 195 + * preferred resolution only. 196 + */ 197 + ret = hyperv_update_vram_location(hdev, hv->fb_base); 198 + if (ret) 199 + drm_warn(dev, "Failed to update vram location.\n"); 200 + 201 + ret = hyperv_mode_config_init(hv); 202 + if (ret) 203 + goto err_vmbus_close; 204 + 205 + ret = drm_dev_register(dev, 0); 206 + if (ret) { 207 + drm_err(dev, "Failed to register drm driver.\n"); 208 + goto err_vmbus_close; 209 + } 210 + 211 + drm_fbdev_generic_setup(dev, 0); 212 + 213 + return 0; 214 + 215 + err_vmbus_close: 216 + vmbus_close(hdev->channel); 217 + err_hv_set_drv_data: 218 + hv_set_drvdata(hdev, NULL); 219 + return ret; 220 + } 221 + 222 + static int hyperv_vmbus_remove(struct hv_device *hdev) 223 + { 224 + struct drm_device *dev = hv_get_drvdata(hdev); 225 + struct hyperv_drm_device *hv = to_hv(dev); 226 + 227 + drm_dev_unplug(dev); 228 + drm_atomic_helper_shutdown(dev); 229 + vmbus_close(hdev->channel); 230 + hv_set_drvdata(hdev, NULL); 231 + vmbus_free_mmio(hv->mem->start, hv->fb_size); 232 + 233 + return 0; 234 + } 235 + 236 + static int hyperv_vmbus_suspend(struct hv_device *hdev) 237 + { 238 + struct drm_device *dev = hv_get_drvdata(hdev); 239 + int ret; 240 + 241 + ret = drm_mode_config_helper_suspend(dev); 242 + if (ret) 243 + return ret; 244 + 245 + vmbus_close(hdev->channel); 246 + 247 + return 0; 248 + } 249 + 250 + static int hyperv_vmbus_resume(struct hv_device *hdev) 251 + { 252 + struct drm_device *dev = hv_get_drvdata(hdev); 253 + struct hyperv_drm_device *hv = to_hv(dev); 254 + int ret; 255 + 256 + ret = hyperv_connect_vsp(hdev); 257 + if (ret) 258 + return ret; 259 + 260 + ret = hyperv_update_vram_location(hdev, hv->fb_base); 261 + if (ret) 262 + return ret; 263 + 264 + return drm_mode_config_helper_resume(dev); 265 + } 266 + 267 + static const struct hv_vmbus_device_id hyperv_vmbus_tbl[] = { 268 + /* Synthetic Video Device GUID */ 269 + {HV_SYNTHVID_GUID}, 270 + {} 271 + }; 272 + 273 + static struct hv_driver hyperv_hv_driver = { 274 + .name = KBUILD_MODNAME, 275 + .id_table = hyperv_vmbus_tbl, 276 + .probe = hyperv_vmbus_probe, 277 + .remove = hyperv_vmbus_remove, 278 + .suspend = hyperv_vmbus_suspend, 279 + .resume = hyperv_vmbus_resume, 280 + .driver = { 281 + .probe_type = PROBE_PREFER_ASYNCHRONOUS, 282 + }, 283 + }; 284 + 285 + static int __init hyperv_init(void) 286 + { 287 + int ret; 288 + 289 + ret = pci_register_driver(&hyperv_pci_driver); 290 + if (ret != 0) 291 + return ret; 292 + 293 + return vmbus_driver_register(&hyperv_hv_driver); 294 + } 295 + 296 + static void __exit hyperv_exit(void) 297 + { 298 + vmbus_driver_unregister(&hyperv_hv_driver); 299 + pci_unregister_driver(&hyperv_pci_driver); 300 + } 301 + 302 + module_init(hyperv_init); 303 + module_exit(hyperv_exit); 304 + 305 + MODULE_DEVICE_TABLE(pci, hyperv_pci_tbl); 306 + MODULE_DEVICE_TABLE(vmbus, hyperv_vmbus_tbl); 307 + MODULE_LICENSE("GPL"); 308 + MODULE_AUTHOR("Deepak Rawat <drawat.floss@gmail.com>"); 309 + MODULE_DESCRIPTION("DRM driver for Hyper-V synthetic video device");
+231
drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright 2021 Microsoft 4 + */ 5 + 6 + #include <linux/hyperv.h> 7 + 8 + #include <drm/drm_damage_helper.h> 9 + #include <drm/drm_drv.h> 10 + #include <drm/drm_fb_helper.h> 11 + #include <drm/drm_format_helper.h> 12 + #include <drm/drm_fourcc.h> 13 + #include <drm/drm_gem_atomic_helper.h> 14 + #include <drm/drm_gem_framebuffer_helper.h> 15 + #include <drm/drm_gem_shmem_helper.h> 16 + #include <drm/drm_probe_helper.h> 17 + #include <drm/drm_simple_kms_helper.h> 18 + 19 + #include "hyperv_drm.h" 20 + 21 + static int hyperv_blit_to_vram_rect(struct drm_framebuffer *fb, 22 + const struct dma_buf_map *map, 23 + struct drm_rect *rect) 24 + { 25 + struct hyperv_drm_device *hv = to_hv(fb->dev); 26 + void *vmap = map->vaddr; /* TODO: Use mapping abstraction properly */ 27 + int idx; 28 + 29 + if (!drm_dev_enter(&hv->dev, &idx)) 30 + return -ENODEV; 31 + 32 + drm_fb_memcpy_dstclip(hv->vram, fb->pitches[0], vmap, fb, rect); 33 + drm_dev_exit(idx); 34 + 35 + return 0; 36 + } 37 + 38 + static int hyperv_blit_to_vram_fullscreen(struct drm_framebuffer *fb, const struct dma_buf_map *map) 39 + { 40 + struct drm_rect fullscreen = { 41 + .x1 = 0, 42 + .x2 = fb->width, 43 + .y1 = 0, 44 + .y2 = fb->height, 45 + }; 46 + return hyperv_blit_to_vram_rect(fb, map, &fullscreen); 47 + } 48 + 49 + static int hyperv_connector_get_modes(struct drm_connector *connector) 50 + { 51 + struct hyperv_drm_device *hv = to_hv(connector->dev); 52 + int count; 53 + 54 + count = drm_add_modes_noedid(connector, 55 + connector->dev->mode_config.max_width, 56 + connector->dev->mode_config.max_height); 57 + drm_set_preferred_mode(connector, hv->preferred_width, 58 + hv->preferred_height); 59 + 60 + return count; 61 + } 62 + 63 + static const struct drm_connector_helper_funcs hyperv_connector_helper_funcs = { 64 + .get_modes = hyperv_connector_get_modes, 65 + }; 66 + 67 + static const struct drm_connector_funcs hyperv_connector_funcs = { 68 + .fill_modes = drm_helper_probe_single_connector_modes, 69 + .destroy = drm_connector_cleanup, 70 + .reset = drm_atomic_helper_connector_reset, 71 + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 72 + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 73 + }; 74 + 75 + static inline int hyperv_conn_init(struct hyperv_drm_device *hv) 76 + { 77 + drm_connector_helper_add(&hv->connector, &hyperv_connector_helper_funcs); 78 + return drm_connector_init(&hv->dev, &hv->connector, 79 + &hyperv_connector_funcs, 80 + DRM_MODE_CONNECTOR_VIRTUAL); 81 + } 82 + 83 + static int hyperv_check_size(struct hyperv_drm_device *hv, int w, int h, 84 + struct drm_framebuffer *fb) 85 + { 86 + u32 pitch = w * (hv->screen_depth / 8); 87 + 88 + if (fb) 89 + pitch = fb->pitches[0]; 90 + 91 + if (pitch * h > hv->fb_size) 92 + return -EINVAL; 93 + 94 + return 0; 95 + } 96 + 97 + static void hyperv_pipe_enable(struct drm_simple_display_pipe *pipe, 98 + struct drm_crtc_state *crtc_state, 99 + struct drm_plane_state *plane_state) 100 + { 101 + struct hyperv_drm_device *hv = to_hv(pipe->crtc.dev); 102 + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 103 + 104 + hyperv_update_situation(hv->hdev, 1, hv->screen_depth, 105 + crtc_state->mode.hdisplay, 106 + crtc_state->mode.vdisplay, 107 + plane_state->fb->pitches[0]); 108 + hyperv_blit_to_vram_fullscreen(plane_state->fb, &shadow_plane_state->map[0]); 109 + } 110 + 111 + static int hyperv_pipe_check(struct drm_simple_display_pipe *pipe, 112 + struct drm_plane_state *plane_state, 113 + struct drm_crtc_state *crtc_state) 114 + { 115 + struct hyperv_drm_device *hv = to_hv(pipe->crtc.dev); 116 + struct drm_framebuffer *fb = plane_state->fb; 117 + 118 + if (fb->format->format != DRM_FORMAT_XRGB8888) 119 + return -EINVAL; 120 + 121 + if (fb->pitches[0] * fb->height > hv->fb_size) 122 + return -EINVAL; 123 + 124 + return 0; 125 + } 126 + 127 + static void hyperv_pipe_update(struct drm_simple_display_pipe *pipe, 128 + struct drm_plane_state *old_state) 129 + { 130 + struct hyperv_drm_device *hv = to_hv(pipe->crtc.dev); 131 + struct drm_plane_state *state = pipe->plane.state; 132 + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state); 133 + struct drm_rect rect; 134 + 135 + if (drm_atomic_helper_damage_merged(old_state, state, &rect)) { 136 + hyperv_blit_to_vram_rect(state->fb, &shadow_plane_state->map[0], &rect); 137 + hyperv_update_dirt(hv->hdev, &rect); 138 + } 139 + } 140 + 141 + static const struct drm_simple_display_pipe_funcs hyperv_pipe_funcs = { 142 + .enable = hyperv_pipe_enable, 143 + .check = hyperv_pipe_check, 144 + .update = hyperv_pipe_update, 145 + DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS, 146 + }; 147 + 148 + static const uint32_t hyperv_formats[] = { 149 + DRM_FORMAT_XRGB8888, 150 + }; 151 + 152 + static const uint64_t hyperv_modifiers[] = { 153 + DRM_FORMAT_MOD_LINEAR, 154 + DRM_FORMAT_MOD_INVALID 155 + }; 156 + 157 + static inline int hyperv_pipe_init(struct hyperv_drm_device *hv) 158 + { 159 + int ret; 160 + 161 + ret = drm_simple_display_pipe_init(&hv->dev, 162 + &hv->pipe, 163 + &hyperv_pipe_funcs, 164 + hyperv_formats, 165 + ARRAY_SIZE(hyperv_formats), 166 + NULL, 167 + &hv->connector); 168 + if (ret) 169 + return ret; 170 + 171 + drm_plane_enable_fb_damage_clips(&hv->pipe.plane); 172 + 173 + return 0; 174 + } 175 + 176 + static enum drm_mode_status 177 + hyperv_mode_valid(struct drm_device *dev, 178 + const struct drm_display_mode *mode) 179 + { 180 + struct hyperv_drm_device *hv = to_hv(dev); 181 + 182 + if (hyperv_check_size(hv, mode->hdisplay, mode->vdisplay, NULL)) 183 + return MODE_BAD; 184 + 185 + return MODE_OK; 186 + } 187 + 188 + static const struct drm_mode_config_funcs hyperv_mode_config_funcs = { 189 + .fb_create = drm_gem_fb_create_with_dirty, 190 + .mode_valid = hyperv_mode_valid, 191 + .atomic_check = drm_atomic_helper_check, 192 + .atomic_commit = drm_atomic_helper_commit, 193 + }; 194 + 195 + int hyperv_mode_config_init(struct hyperv_drm_device *hv) 196 + { 197 + struct drm_device *dev = &hv->dev; 198 + int ret; 199 + 200 + ret = drmm_mode_config_init(dev); 201 + if (ret) { 202 + drm_err(dev, "Failed to initialized mode setting.\n"); 203 + return ret; 204 + } 205 + 206 + dev->mode_config.min_width = 0; 207 + dev->mode_config.min_height = 0; 208 + dev->mode_config.max_width = hv->screen_width_max; 209 + dev->mode_config.max_height = hv->screen_height_max; 210 + 211 + dev->mode_config.preferred_depth = hv->screen_depth; 212 + dev->mode_config.prefer_shadow = 0; 213 + 214 + dev->mode_config.funcs = &hyperv_mode_config_funcs; 215 + 216 + ret = hyperv_conn_init(hv); 217 + if (ret) { 218 + drm_err(dev, "Failed to initialized connector.\n"); 219 + return ret; 220 + } 221 + 222 + ret = hyperv_pipe_init(hv); 223 + if (ret) { 224 + drm_err(dev, "Failed to initialized pipe.\n"); 225 + return ret; 226 + } 227 + 228 + drm_mode_config_reset(dev); 229 + 230 + return 0; 231 + }
+478
drivers/gpu/drm/hyperv/hyperv_drm_proto.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright 2021 Microsoft 4 + * 5 + * Portions of this code is derived from hyperv_fb.c 6 + */ 7 + 8 + #include <linux/hyperv.h> 9 + 10 + #include <drm/drm_print.h> 11 + #include <drm/drm_simple_kms_helper.h> 12 + 13 + #include "hyperv_drm.h" 14 + 15 + #define VMBUS_RING_BUFSIZE (256 * 1024) 16 + #define VMBUS_VSP_TIMEOUT (10 * HZ) 17 + 18 + #define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major)) 19 + #define SYNTHVID_VER_GET_MAJOR(ver) (ver & 0x0000ffff) 20 + #define SYNTHVID_VER_GET_MINOR(ver) ((ver & 0xffff0000) >> 16) 21 + #define SYNTHVID_VERSION_WIN7 SYNTHVID_VERSION(3, 0) 22 + #define SYNTHVID_VERSION_WIN8 SYNTHVID_VERSION(3, 2) 23 + #define SYNTHVID_VERSION_WIN10 SYNTHVID_VERSION(3, 5) 24 + 25 + #define SYNTHVID_DEPTH_WIN7 16 26 + #define SYNTHVID_DEPTH_WIN8 32 27 + #define SYNTHVID_FB_SIZE_WIN7 (4 * 1024 * 1024) 28 + #define SYNTHVID_FB_SIZE_WIN8 (8 * 1024 * 1024) 29 + #define SYNTHVID_WIDTH_MAX_WIN7 1600 30 + #define SYNTHVID_HEIGHT_MAX_WIN7 1200 31 + 32 + enum pipe_msg_type { 33 + PIPE_MSG_INVALID, 34 + PIPE_MSG_DATA, 35 + PIPE_MSG_MAX 36 + }; 37 + 38 + enum synthvid_msg_type { 39 + SYNTHVID_ERROR = 0, 40 + SYNTHVID_VERSION_REQUEST = 1, 41 + SYNTHVID_VERSION_RESPONSE = 2, 42 + SYNTHVID_VRAM_LOCATION = 3, 43 + SYNTHVID_VRAM_LOCATION_ACK = 4, 44 + SYNTHVID_SITUATION_UPDATE = 5, 45 + SYNTHVID_SITUATION_UPDATE_ACK = 6, 46 + SYNTHVID_POINTER_POSITION = 7, 47 + SYNTHVID_POINTER_SHAPE = 8, 48 + SYNTHVID_FEATURE_CHANGE = 9, 49 + SYNTHVID_DIRT = 10, 50 + SYNTHVID_RESOLUTION_REQUEST = 13, 51 + SYNTHVID_RESOLUTION_RESPONSE = 14, 52 + 53 + SYNTHVID_MAX = 15 54 + }; 55 + 56 + struct pipe_msg_hdr { 57 + u32 type; 58 + u32 size; /* size of message after this field */ 59 + } __packed; 60 + 61 + struct hvd_screen_info { 62 + u16 width; 63 + u16 height; 64 + } __packed; 65 + 66 + struct synthvid_msg_hdr { 67 + u32 type; 68 + u32 size; /* size of this header + payload after this field */ 69 + } __packed; 70 + 71 + struct synthvid_version_req { 72 + u32 version; 73 + } __packed; 74 + 75 + struct synthvid_version_resp { 76 + u32 version; 77 + u8 is_accepted; 78 + u8 max_video_outputs; 79 + } __packed; 80 + 81 + struct synthvid_vram_location { 82 + u64 user_ctx; 83 + u8 is_vram_gpa_specified; 84 + u64 vram_gpa; 85 + } __packed; 86 + 87 + struct synthvid_vram_location_ack { 88 + u64 user_ctx; 89 + } __packed; 90 + 91 + struct video_output_situation { 92 + u8 active; 93 + u32 vram_offset; 94 + u8 depth_bits; 95 + u32 width_pixels; 96 + u32 height_pixels; 97 + u32 pitch_bytes; 98 + } __packed; 99 + 100 + struct synthvid_situation_update { 101 + u64 user_ctx; 102 + u8 video_output_count; 103 + struct video_output_situation video_output[1]; 104 + } __packed; 105 + 106 + struct synthvid_situation_update_ack { 107 + u64 user_ctx; 108 + } __packed; 109 + 110 + struct synthvid_pointer_position { 111 + u8 is_visible; 112 + u8 video_output; 113 + s32 image_x; 114 + s32 image_y; 115 + } __packed; 116 + 117 + #define SYNTHVID_CURSOR_MAX_X 96 118 + #define SYNTHVID_CURSOR_MAX_Y 96 119 + #define SYNTHVID_CURSOR_ARGB_PIXEL_SIZE 4 120 + #define SYNTHVID_CURSOR_MAX_SIZE (SYNTHVID_CURSOR_MAX_X * \ 121 + SYNTHVID_CURSOR_MAX_Y * SYNTHVID_CURSOR_ARGB_PIXEL_SIZE) 122 + #define SYNTHVID_CURSOR_COMPLETE (-1) 123 + 124 + struct synthvid_pointer_shape { 125 + u8 part_idx; 126 + u8 is_argb; 127 + u32 width; /* SYNTHVID_CURSOR_MAX_X at most */ 128 + u32 height; /* SYNTHVID_CURSOR_MAX_Y at most */ 129 + u32 hot_x; /* hotspot relative to upper-left of pointer image */ 130 + u32 hot_y; 131 + u8 data[4]; 132 + } __packed; 133 + 134 + struct synthvid_feature_change { 135 + u8 is_dirt_needed; 136 + u8 is_ptr_pos_needed; 137 + u8 is_ptr_shape_needed; 138 + u8 is_situ_needed; 139 + } __packed; 140 + 141 + struct rect { 142 + s32 x1, y1; /* top left corner */ 143 + s32 x2, y2; /* bottom right corner, exclusive */ 144 + } __packed; 145 + 146 + struct synthvid_dirt { 147 + u8 video_output; 148 + u8 dirt_count; 149 + struct rect rect[1]; 150 + } __packed; 151 + 152 + #define SYNTHVID_EDID_BLOCK_SIZE 128 153 + #define SYNTHVID_MAX_RESOLUTION_COUNT 64 154 + 155 + struct synthvid_supported_resolution_req { 156 + u8 maximum_resolution_count; 157 + } __packed; 158 + 159 + struct synthvid_supported_resolution_resp { 160 + u8 edid_block[SYNTHVID_EDID_BLOCK_SIZE]; 161 + u8 resolution_count; 162 + u8 default_resolution_index; 163 + u8 is_standard; 164 + struct hvd_screen_info supported_resolution[SYNTHVID_MAX_RESOLUTION_COUNT]; 165 + } __packed; 166 + 167 + struct synthvid_msg { 168 + struct pipe_msg_hdr pipe_hdr; 169 + struct synthvid_msg_hdr vid_hdr; 170 + union { 171 + struct synthvid_version_req ver_req; 172 + struct synthvid_version_resp ver_resp; 173 + struct synthvid_vram_location vram; 174 + struct synthvid_vram_location_ack vram_ack; 175 + struct synthvid_situation_update situ; 176 + struct synthvid_situation_update_ack situ_ack; 177 + struct synthvid_pointer_position ptr_pos; 178 + struct synthvid_pointer_shape ptr_shape; 179 + struct synthvid_feature_change feature_chg; 180 + struct synthvid_dirt dirt; 181 + struct synthvid_supported_resolution_req resolution_req; 182 + struct synthvid_supported_resolution_resp resolution_resp; 183 + }; 184 + } __packed; 185 + 186 + static inline bool hyperv_version_ge(u32 ver1, u32 ver2) 187 + { 188 + if (SYNTHVID_VER_GET_MAJOR(ver1) > SYNTHVID_VER_GET_MAJOR(ver2) || 189 + (SYNTHVID_VER_GET_MAJOR(ver1) == SYNTHVID_VER_GET_MAJOR(ver2) && 190 + SYNTHVID_VER_GET_MINOR(ver1) >= SYNTHVID_VER_GET_MINOR(ver2))) 191 + return true; 192 + 193 + return false; 194 + } 195 + 196 + static inline int hyperv_sendpacket(struct hv_device *hdev, struct synthvid_msg *msg) 197 + { 198 + static atomic64_t request_id = ATOMIC64_INIT(0); 199 + struct hyperv_drm_device *hv = hv_get_drvdata(hdev); 200 + int ret; 201 + 202 + msg->pipe_hdr.type = PIPE_MSG_DATA; 203 + msg->pipe_hdr.size = msg->vid_hdr.size; 204 + 205 + ret = vmbus_sendpacket(hdev->channel, msg, 206 + msg->vid_hdr.size + sizeof(struct pipe_msg_hdr), 207 + atomic64_inc_return(&request_id), 208 + VM_PKT_DATA_INBAND, 0); 209 + 210 + if (ret) 211 + drm_err(&hv->dev, "Unable to send packet via vmbus\n"); 212 + 213 + return ret; 214 + } 215 + 216 + static int hyperv_negotiate_version(struct hv_device *hdev, u32 ver) 217 + { 218 + struct hyperv_drm_device *hv = hv_get_drvdata(hdev); 219 + struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf; 220 + struct drm_device *dev = &hv->dev; 221 + unsigned long t; 222 + 223 + memset(msg, 0, sizeof(struct synthvid_msg)); 224 + msg->vid_hdr.type = SYNTHVID_VERSION_REQUEST; 225 + msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 226 + sizeof(struct synthvid_version_req); 227 + msg->ver_req.version = ver; 228 + hyperv_sendpacket(hdev, msg); 229 + 230 + t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT); 231 + if (!t) { 232 + drm_err(dev, "Time out on waiting version response\n"); 233 + return -ETIMEDOUT; 234 + } 235 + 236 + if (!msg->ver_resp.is_accepted) { 237 + drm_err(dev, "Version request not accepted\n"); 238 + return -ENODEV; 239 + } 240 + 241 + hv->synthvid_version = ver; 242 + drm_info(dev, "Synthvid Version major %d, minor %d\n", 243 + SYNTHVID_VER_GET_MAJOR(ver), SYNTHVID_VER_GET_MINOR(ver)); 244 + 245 + return 0; 246 + } 247 + 248 + int hyperv_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp) 249 + { 250 + struct hyperv_drm_device *hv = hv_get_drvdata(hdev); 251 + struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf; 252 + struct drm_device *dev = &hv->dev; 253 + unsigned long t; 254 + 255 + memset(msg, 0, sizeof(struct synthvid_msg)); 256 + msg->vid_hdr.type = SYNTHVID_VRAM_LOCATION; 257 + msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 258 + sizeof(struct synthvid_vram_location); 259 + msg->vram.user_ctx = vram_pp; 260 + msg->vram.vram_gpa = vram_pp; 261 + msg->vram.is_vram_gpa_specified = 1; 262 + hyperv_sendpacket(hdev, msg); 263 + 264 + t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT); 265 + if (!t) { 266 + drm_err(dev, "Time out on waiting vram location ack\n"); 267 + return -ETIMEDOUT; 268 + } 269 + if (msg->vram_ack.user_ctx != vram_pp) { 270 + drm_err(dev, "Unable to set VRAM location\n"); 271 + return -ENODEV; 272 + } 273 + 274 + return 0; 275 + } 276 + 277 + int hyperv_update_situation(struct hv_device *hdev, u8 active, u32 bpp, 278 + u32 w, u32 h, u32 pitch) 279 + { 280 + struct synthvid_msg msg; 281 + 282 + memset(&msg, 0, sizeof(struct synthvid_msg)); 283 + 284 + msg.vid_hdr.type = SYNTHVID_SITUATION_UPDATE; 285 + msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 286 + sizeof(struct synthvid_situation_update); 287 + msg.situ.user_ctx = 0; 288 + msg.situ.video_output_count = 1; 289 + msg.situ.video_output[0].active = active; 290 + /* vram_offset should always be 0 */ 291 + msg.situ.video_output[0].vram_offset = 0; 292 + msg.situ.video_output[0].depth_bits = bpp; 293 + msg.situ.video_output[0].width_pixels = w; 294 + msg.situ.video_output[0].height_pixels = h; 295 + msg.situ.video_output[0].pitch_bytes = pitch; 296 + 297 + hyperv_sendpacket(hdev, &msg); 298 + 299 + return 0; 300 + } 301 + 302 + int hyperv_update_dirt(struct hv_device *hdev, struct drm_rect *rect) 303 + { 304 + struct synthvid_msg msg; 305 + 306 + memset(&msg, 0, sizeof(struct synthvid_msg)); 307 + 308 + msg.vid_hdr.type = SYNTHVID_DIRT; 309 + msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 310 + sizeof(struct synthvid_dirt); 311 + msg.dirt.video_output = 0; 312 + msg.dirt.dirt_count = 1; 313 + msg.dirt.rect[0].x1 = rect->x1; 314 + msg.dirt.rect[0].y1 = rect->y1; 315 + msg.dirt.rect[0].x2 = rect->x2; 316 + msg.dirt.rect[0].y2 = rect->y2; 317 + 318 + hyperv_sendpacket(hdev, &msg); 319 + 320 + return 0; 321 + } 322 + 323 + static int hyperv_get_supported_resolution(struct hv_device *hdev) 324 + { 325 + struct hyperv_drm_device *hv = hv_get_drvdata(hdev); 326 + struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf; 327 + struct drm_device *dev = &hv->dev; 328 + unsigned long t; 329 + u8 index; 330 + int i; 331 + 332 + memset(msg, 0, sizeof(struct synthvid_msg)); 333 + msg->vid_hdr.type = SYNTHVID_RESOLUTION_REQUEST; 334 + msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) + 335 + sizeof(struct synthvid_supported_resolution_req); 336 + msg->resolution_req.maximum_resolution_count = 337 + SYNTHVID_MAX_RESOLUTION_COUNT; 338 + hyperv_sendpacket(hdev, msg); 339 + 340 + t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT); 341 + if (!t) { 342 + drm_err(dev, "Time out on waiting resolution response\n"); 343 + return -ETIMEDOUT; 344 + } 345 + 346 + if (msg->resolution_resp.resolution_count == 0) { 347 + drm_err(dev, "No supported resolutions\n"); 348 + return -ENODEV; 349 + } 350 + 351 + index = msg->resolution_resp.default_resolution_index; 352 + if (index >= msg->resolution_resp.resolution_count) { 353 + drm_err(dev, "Invalid resolution index: %d\n", index); 354 + return -ENODEV; 355 + } 356 + 357 + for (i = 0; i < msg->resolution_resp.resolution_count; i++) { 358 + hv->screen_width_max = max_t(u32, hv->screen_width_max, 359 + msg->resolution_resp.supported_resolution[i].width); 360 + hv->screen_height_max = max_t(u32, hv->screen_height_max, 361 + msg->resolution_resp.supported_resolution[i].height); 362 + } 363 + 364 + hv->preferred_width = 365 + msg->resolution_resp.supported_resolution[index].width; 366 + hv->preferred_height = 367 + msg->resolution_resp.supported_resolution[index].height; 368 + 369 + return 0; 370 + } 371 + 372 + static void hyperv_receive_sub(struct hv_device *hdev) 373 + { 374 + struct hyperv_drm_device *hv = hv_get_drvdata(hdev); 375 + struct synthvid_msg *msg; 376 + 377 + if (!hv) 378 + return; 379 + 380 + msg = (struct synthvid_msg *)hv->recv_buf; 381 + 382 + /* Complete the wait event */ 383 + if (msg->vid_hdr.type == SYNTHVID_VERSION_RESPONSE || 384 + msg->vid_hdr.type == SYNTHVID_RESOLUTION_RESPONSE || 385 + msg->vid_hdr.type == SYNTHVID_VRAM_LOCATION_ACK) { 386 + memcpy(hv->init_buf, msg, VMBUS_MAX_PACKET_SIZE); 387 + complete(&hv->wait); 388 + return; 389 + } 390 + } 391 + 392 + static void hyperv_receive(void *ctx) 393 + { 394 + struct hv_device *hdev = ctx; 395 + struct hyperv_drm_device *hv = hv_get_drvdata(hdev); 396 + struct synthvid_msg *recv_buf; 397 + u32 bytes_recvd; 398 + u64 req_id; 399 + int ret; 400 + 401 + if (!hv) 402 + return; 403 + 404 + recv_buf = (struct synthvid_msg *)hv->recv_buf; 405 + 406 + do { 407 + ret = vmbus_recvpacket(hdev->channel, recv_buf, 408 + VMBUS_MAX_PACKET_SIZE, 409 + &bytes_recvd, &req_id); 410 + if (bytes_recvd > 0 && 411 + recv_buf->pipe_hdr.type == PIPE_MSG_DATA) 412 + hyperv_receive_sub(hdev); 413 + } while (bytes_recvd > 0 && ret == 0); 414 + } 415 + 416 + int hyperv_connect_vsp(struct hv_device *hdev) 417 + { 418 + struct hyperv_drm_device *hv = hv_get_drvdata(hdev); 419 + struct drm_device *dev = &hv->dev; 420 + int ret; 421 + 422 + ret = vmbus_open(hdev->channel, VMBUS_RING_BUFSIZE, VMBUS_RING_BUFSIZE, 423 + NULL, 0, hyperv_receive, hdev); 424 + if (ret) { 425 + drm_err(dev, "Unable to open vmbus channel\n"); 426 + return ret; 427 + } 428 + 429 + /* Negotiate the protocol version with host */ 430 + switch (vmbus_proto_version) { 431 + case VERSION_WIN10: 432 + case VERSION_WIN10_V5: 433 + ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN10); 434 + if (!ret) 435 + break; 436 + fallthrough; 437 + case VERSION_WIN8: 438 + case VERSION_WIN8_1: 439 + ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN8); 440 + if (!ret) 441 + break; 442 + fallthrough; 443 + case VERSION_WS2008: 444 + case VERSION_WIN7: 445 + ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN7); 446 + break; 447 + default: 448 + ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN10); 449 + break; 450 + } 451 + 452 + if (ret) { 453 + drm_err(dev, "Synthetic video device version not accepted %d\n", ret); 454 + goto error; 455 + } 456 + 457 + if (hv->synthvid_version == SYNTHVID_VERSION_WIN7) 458 + hv->screen_depth = SYNTHVID_DEPTH_WIN7; 459 + else 460 + hv->screen_depth = SYNTHVID_DEPTH_WIN8; 461 + 462 + if (hyperv_version_ge(hv->synthvid_version, SYNTHVID_VERSION_WIN10)) { 463 + ret = hyperv_get_supported_resolution(hdev); 464 + if (ret) 465 + drm_err(dev, "Failed to get supported resolution from host, use default\n"); 466 + } else { 467 + hv->screen_width_max = SYNTHVID_WIDTH_MAX_WIN7; 468 + hv->screen_height_max = SYNTHVID_HEIGHT_MAX_WIN7; 469 + } 470 + 471 + hv->mmio_megabytes = hdev->channel->offermsg.offer.mmio_megabytes; 472 + 473 + return 0; 474 + 475 + error: 476 + vmbus_close(hdev->channel); 477 + return ret; 478 + }