1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright 2021 Microsoft 4 */ 5 6 #include <linux/aperture.h> 7 #include <linux/efi.h> 8 #include <linux/hyperv.h> 9 #include <linux/module.h> 10 #include <linux/pci.h> 11 12 #include <drm/clients/drm_client_setup.h> 13 #include <drm/drm_atomic_helper.h> 14 #include <drm/drm_drv.h> 15 #include <drm/drm_fbdev_shmem.h> 16 #include <drm/drm_gem_shmem_helper.h> 17 #include <drm/drm_print.h> 18 #include <drm/drm_simple_kms_helper.h> 19 20 #include "hyperv_drm.h" 21 22 #define DRIVER_NAME "hyperv_drm" 23 #define DRIVER_DESC "DRM driver for Hyper-V synthetic video device" 24 #define DRIVER_MAJOR 1 25 #define DRIVER_MINOR 0 26 27 DEFINE_DRM_GEM_FOPS(hv_drm_fops); 28 29 static struct drm_driver hv_drm_driver = { 30 .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 31 32 .name = DRIVER_NAME, 33 .desc = DRIVER_DESC, 34 .major = DRIVER_MAJOR, 35 .minor = DRIVER_MINOR, 36 37 .fops = &hv_drm_fops, 38 DRM_GEM_SHMEM_DRIVER_OPS, 39 DRM_FBDEV_SHMEM_DRIVER_OPS, 40 }; 41 42 static int hv_drm_pci_probe(struct pci_dev *pdev, 43 const struct pci_device_id *ent) 44 { 45 return 0; 46 } 47 48 static void hv_drm_pci_remove(struct pci_dev *pdev) 49 { 50 } 51 52 static const struct pci_device_id hv_drm_pci_tbl[] = { 53 { 54 PCI_VDEVICE_SUB(MICROSOFT, PCI_DEVICE_ID_HYPERV_VIDEO, 55 0, 0), 56 }, 57 { /* end of list */ } 58 }; 59 MODULE_DEVICE_TABLE(pci, hv_drm_pci_tbl); 60 61 /* 62 * PCI stub to support gen1 VM. 63 */ 64 static struct pci_driver hv_drm_pci_driver = { 65 .name = KBUILD_MODNAME, 66 .id_table = hv_drm_pci_tbl, 67 .probe = hv_drm_pci_probe, 68 .remove = hv_drm_pci_remove, 69 }; 70 71 static int hv_drm_setup_vram(struct hv_drm_device *hv, 72 struct hv_device *hdev) 73 { 74 struct drm_device *dev = &hv->dev; 75 int ret; 76 77 hv->fb_size = (unsigned long)hv->mmio_megabytes * 1024 * 1024; 78 79 ret = vmbus_allocate_mmio(&hv->mem, hdev, 0, -1, hv->fb_size, 0x100000, 80 true); 81 if (ret) { 82 drm_err(dev, "Failed to allocate mmio\n"); 83 return -ENOMEM; 84 } 85 86 /* 87 * Map the VRAM cacheable for performance. This is also required for VM 88 * connect to display properly for ARM64 Linux VM, as the host also maps 89 * the VRAM cacheable. 90 */ 91 hv->vram = ioremap_cache(hv->mem->start, hv->fb_size); 92 if (!hv->vram) { 93 drm_err(dev, "Failed to map vram\n"); 94 ret = -ENOMEM; 95 goto error; 96 } 97 98 hv->fb_base = hv->mem->start; 99 return 0; 100 101 error: 102 vmbus_free_mmio(hv->mem->start, hv->fb_size); 103 return ret; 104 } 105 106 static int hv_drm_vmbus_probe(struct hv_device *hdev, 107 const struct hv_vmbus_device_id *dev_id) 108 { 109 struct hv_drm_device *hv; 110 struct drm_device *dev; 111 int ret; 112 113 hv = devm_drm_dev_alloc(&hdev->device, &hv_drm_driver, 114 struct hv_drm_device, dev); 115 if (IS_ERR(hv)) 116 return PTR_ERR(hv); 117 118 dev = &hv->dev; 119 init_completion(&hv->wait); 120 hv_set_drvdata(hdev, hv); 121 hv->hdev = hdev; 122 123 ret = hv_drm_connect_vsp(hdev); 124 if (ret) { 125 drm_err(dev, "Failed to connect to vmbus.\n"); 126 goto err_hv_set_drv_data; 127 } 128 129 aperture_remove_all_conflicting_devices(hv_drm_driver.name); 130 131 ret = hv_drm_setup_vram(hv, hdev); 132 if (ret) 133 goto err_vmbus_close; 134 135 /* 136 * Should be done only once during init and resume. Failing to update 137 * vram location is not fatal. Device will update dirty area till 138 * preferred resolution only. 139 */ 140 ret = hv_drm_update_vram_location(hdev, hv->fb_base); 141 if (ret) 142 drm_warn(dev, "Failed to update vram location.\n"); 143 144 ret = hv_drm_mode_config_init(hv); 145 if (ret) 146 goto err_free_mmio; 147 148 ret = drm_dev_register(dev, 0); 149 if (ret) { 150 drm_err(dev, "Failed to register drm driver.\n"); 151 goto err_free_mmio; 152 } 153 154 /* If DRM panic path is stubbed out VMBus code must do the unload */ 155 if (IS_ENABLED(CONFIG_DRM_PANIC)) 156 vmbus_set_skip_unload(true); 157 158 drm_client_setup(dev, NULL); 159 160 return 0; 161 162 err_free_mmio: 163 iounmap(hv->vram); 164 vmbus_free_mmio(hv->mem->start, hv->fb_size); 165 err_vmbus_close: 166 vmbus_close(hdev->channel); 167 err_hv_set_drv_data: 168 hv_set_drvdata(hdev, NULL); 169 return ret; 170 } 171 172 static void hv_drm_vmbus_remove(struct hv_device *hdev) 173 { 174 struct drm_device *dev = hv_get_drvdata(hdev); 175 struct hv_drm_device *hv = to_hv_drm(dev); 176 177 vmbus_set_skip_unload(false); 178 drm_dev_unplug(dev); 179 drm_atomic_helper_shutdown(dev); 180 vmbus_close(hdev->channel); 181 hv_set_drvdata(hdev, NULL); 182 183 iounmap(hv->vram); 184 vmbus_free_mmio(hv->mem->start, hv->fb_size); 185 } 186 187 static void hv_drm_vmbus_shutdown(struct hv_device *hdev) 188 { 189 drm_atomic_helper_shutdown(hv_get_drvdata(hdev)); 190 } 191 192 static int hv_drm_vmbus_suspend(struct hv_device *hdev) 193 { 194 struct drm_device *dev = hv_get_drvdata(hdev); 195 int ret; 196 197 ret = drm_mode_config_helper_suspend(dev); 198 if (ret) 199 return ret; 200 201 vmbus_close(hdev->channel); 202 203 return 0; 204 } 205 206 static int hv_drm_vmbus_resume(struct hv_device *hdev) 207 { 208 struct drm_device *dev = hv_get_drvdata(hdev); 209 struct hv_drm_device *hv = to_hv_drm(dev); 210 int ret; 211 212 ret = hv_drm_connect_vsp(hdev); 213 if (ret) 214 return ret; 215 216 ret = hv_drm_update_vram_location(hdev, hv->fb_base); 217 if (ret) 218 return ret; 219 220 return drm_mode_config_helper_resume(dev); 221 } 222 223 static const struct hv_vmbus_device_id hv_drm_vmbus_tbl[] = { 224 /* Synthetic Video Device GUID */ 225 {HV_SYNTHVID_GUID}, 226 {} 227 }; 228 MODULE_DEVICE_TABLE(vmbus, hv_drm_vmbus_tbl); 229 230 static struct hv_driver hv_drm_hv_driver = { 231 .name = KBUILD_MODNAME, 232 .id_table = hv_drm_vmbus_tbl, 233 .probe = hv_drm_vmbus_probe, 234 .remove = hv_drm_vmbus_remove, 235 .shutdown = hv_drm_vmbus_shutdown, 236 .suspend = hv_drm_vmbus_suspend, 237 .resume = hv_drm_vmbus_resume, 238 .driver = { 239 .probe_type = PROBE_PREFER_ASYNCHRONOUS, 240 }, 241 }; 242 243 static int __init hv_drm_init(void) 244 { 245 int ret; 246 247 if (drm_firmware_drivers_only()) 248 return -ENODEV; 249 250 ret = pci_register_driver(&hv_drm_pci_driver); 251 if (ret != 0) 252 return ret; 253 254 ret = vmbus_driver_register(&hv_drm_hv_driver); 255 if (ret) 256 pci_unregister_driver(&hv_drm_pci_driver); 257 258 return ret; 259 } 260 261 static void __exit hv_drm_exit(void) 262 { 263 vmbus_driver_unregister(&hv_drm_hv_driver); 264 pci_unregister_driver(&hv_drm_pci_driver); 265 } 266 267 module_init(hv_drm_init); 268 module_exit(hv_drm_exit); 269 270 MODULE_LICENSE("GPL"); 271 MODULE_AUTHOR("Deepak Rawat <drawat.floss@gmail.com>"); 272 MODULE_DESCRIPTION("DRM driver for Hyper-V synthetic video device"); 273