hyperv_drm_drv.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright 2021 Microsoft
  4. */
  5. #include <linux/efi.h>
  6. #include <linux/hyperv.h>
  7. #include <linux/module.h>
  8. #include <linux/pci.h>
  9. #include <drm/drm_aperture.h>
  10. #include <drm/drm_atomic_helper.h>
  11. #include <drm/drm_drv.h>
  12. #include <drm/drm_fbdev_shmem.h>
  13. #include <drm/drm_gem_shmem_helper.h>
  14. #include <drm/drm_simple_kms_helper.h>
  15. #include "hyperv_drm.h"
  16. #define DRIVER_NAME "hyperv_drm"
  17. #define DRIVER_DESC "DRM driver for Hyper-V synthetic video device"
  18. #define DRIVER_DATE "2020"
  19. #define DRIVER_MAJOR 1
  20. #define DRIVER_MINOR 0
  21. DEFINE_DRM_GEM_FOPS(hv_fops);
  22. static struct drm_driver hyperv_driver = {
  23. .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
  24. .name = DRIVER_NAME,
  25. .desc = DRIVER_DESC,
  26. .date = DRIVER_DATE,
  27. .major = DRIVER_MAJOR,
  28. .minor = DRIVER_MINOR,
  29. .fops = &hv_fops,
  30. DRM_GEM_SHMEM_DRIVER_OPS,
  31. };
  32. static int hyperv_pci_probe(struct pci_dev *pdev,
  33. const struct pci_device_id *ent)
  34. {
  35. return 0;
  36. }
  37. static void hyperv_pci_remove(struct pci_dev *pdev)
  38. {
  39. }
  40. static const struct pci_device_id hyperv_pci_tbl[] = {
  41. {
  42. .vendor = PCI_VENDOR_ID_MICROSOFT,
  43. .device = PCI_DEVICE_ID_HYPERV_VIDEO,
  44. },
  45. { /* end of list */ }
  46. };
  47. /*
  48. * PCI stub to support gen1 VM.
  49. */
  50. static struct pci_driver hyperv_pci_driver = {
  51. .name = KBUILD_MODNAME,
  52. .id_table = hyperv_pci_tbl,
  53. .probe = hyperv_pci_probe,
  54. .remove = hyperv_pci_remove,
  55. };
  56. static int hyperv_setup_vram(struct hyperv_drm_device *hv,
  57. struct hv_device *hdev)
  58. {
  59. struct drm_device *dev = &hv->dev;
  60. int ret;
  61. hv->fb_size = (unsigned long)hv->mmio_megabytes * 1024 * 1024;
  62. ret = vmbus_allocate_mmio(&hv->mem, hdev, 0, -1, hv->fb_size, 0x100000,
  63. true);
  64. if (ret) {
  65. drm_err(dev, "Failed to allocate mmio\n");
  66. return -ENOMEM;
  67. }
  68. /*
  69. * Map the VRAM cacheable for performance. This is also required for VM
  70. * connect to display properly for ARM64 Linux VM, as the host also maps
  71. * the VRAM cacheable.
  72. */
  73. hv->vram = ioremap_cache(hv->mem->start, hv->fb_size);
  74. if (!hv->vram) {
  75. drm_err(dev, "Failed to map vram\n");
  76. ret = -ENOMEM;
  77. goto error;
  78. }
  79. hv->fb_base = hv->mem->start;
  80. return 0;
  81. error:
  82. vmbus_free_mmio(hv->mem->start, hv->fb_size);
  83. return ret;
  84. }
  85. static int hyperv_vmbus_probe(struct hv_device *hdev,
  86. const struct hv_vmbus_device_id *dev_id)
  87. {
  88. struct hyperv_drm_device *hv;
  89. struct drm_device *dev;
  90. int ret;
  91. hv = devm_drm_dev_alloc(&hdev->device, &hyperv_driver,
  92. struct hyperv_drm_device, dev);
  93. if (IS_ERR(hv))
  94. return PTR_ERR(hv);
  95. dev = &hv->dev;
  96. init_completion(&hv->wait);
  97. hv_set_drvdata(hdev, hv);
  98. hv->hdev = hdev;
  99. ret = hyperv_connect_vsp(hdev);
  100. if (ret) {
  101. drm_err(dev, "Failed to connect to vmbus.\n");
  102. goto err_hv_set_drv_data;
  103. }
  104. drm_aperture_remove_framebuffers(&hyperv_driver);
  105. ret = hyperv_setup_vram(hv, hdev);
  106. if (ret)
  107. goto err_vmbus_close;
  108. /*
  109. * Should be done only once during init and resume. Failing to update
  110. * vram location is not fatal. Device will update dirty area till
  111. * preferred resolution only.
  112. */
  113. ret = hyperv_update_vram_location(hdev, hv->fb_base);
  114. if (ret)
  115. drm_warn(dev, "Failed to update vram location.\n");
  116. ret = hyperv_mode_config_init(hv);
  117. if (ret)
  118. goto err_free_mmio;
  119. ret = drm_dev_register(dev, 0);
  120. if (ret) {
  121. drm_err(dev, "Failed to register drm driver.\n");
  122. goto err_free_mmio;
  123. }
  124. drm_fbdev_shmem_setup(dev, 0);
  125. return 0;
  126. err_free_mmio:
  127. iounmap(hv->vram);
  128. vmbus_free_mmio(hv->mem->start, hv->fb_size);
  129. err_vmbus_close:
  130. vmbus_close(hdev->channel);
  131. err_hv_set_drv_data:
  132. hv_set_drvdata(hdev, NULL);
  133. return ret;
  134. }
  135. static void hyperv_vmbus_remove(struct hv_device *hdev)
  136. {
  137. struct drm_device *dev = hv_get_drvdata(hdev);
  138. struct hyperv_drm_device *hv = to_hv(dev);
  139. drm_dev_unplug(dev);
  140. drm_atomic_helper_shutdown(dev);
  141. vmbus_close(hdev->channel);
  142. hv_set_drvdata(hdev, NULL);
  143. iounmap(hv->vram);
  144. vmbus_free_mmio(hv->mem->start, hv->fb_size);
  145. }
  146. static void hyperv_vmbus_shutdown(struct hv_device *hdev)
  147. {
  148. drm_atomic_helper_shutdown(hv_get_drvdata(hdev));
  149. }
  150. static int hyperv_vmbus_suspend(struct hv_device *hdev)
  151. {
  152. struct drm_device *dev = hv_get_drvdata(hdev);
  153. int ret;
  154. ret = drm_mode_config_helper_suspend(dev);
  155. if (ret)
  156. return ret;
  157. vmbus_close(hdev->channel);
  158. return 0;
  159. }
  160. static int hyperv_vmbus_resume(struct hv_device *hdev)
  161. {
  162. struct drm_device *dev = hv_get_drvdata(hdev);
  163. struct hyperv_drm_device *hv = to_hv(dev);
  164. int ret;
  165. ret = hyperv_connect_vsp(hdev);
  166. if (ret)
  167. return ret;
  168. ret = hyperv_update_vram_location(hdev, hv->fb_base);
  169. if (ret)
  170. return ret;
  171. return drm_mode_config_helper_resume(dev);
  172. }
  173. static const struct hv_vmbus_device_id hyperv_vmbus_tbl[] = {
  174. /* Synthetic Video Device GUID */
  175. {HV_SYNTHVID_GUID},
  176. {}
  177. };
  178. static struct hv_driver hyperv_hv_driver = {
  179. .name = KBUILD_MODNAME,
  180. .id_table = hyperv_vmbus_tbl,
  181. .probe = hyperv_vmbus_probe,
  182. .remove = hyperv_vmbus_remove,
  183. .shutdown = hyperv_vmbus_shutdown,
  184. .suspend = hyperv_vmbus_suspend,
  185. .resume = hyperv_vmbus_resume,
  186. .driver = {
  187. .probe_type = PROBE_PREFER_ASYNCHRONOUS,
  188. },
  189. };
  190. static int __init hyperv_init(void)
  191. {
  192. int ret;
  193. if (drm_firmware_drivers_only())
  194. return -ENODEV;
  195. ret = pci_register_driver(&hyperv_pci_driver);
  196. if (ret != 0)
  197. return ret;
  198. return vmbus_driver_register(&hyperv_hv_driver);
  199. }
  200. static void __exit hyperv_exit(void)
  201. {
  202. vmbus_driver_unregister(&hyperv_hv_driver);
  203. pci_unregister_driver(&hyperv_pci_driver);
  204. }
  205. module_init(hyperv_init);
  206. module_exit(hyperv_exit);
  207. MODULE_DEVICE_TABLE(pci, hyperv_pci_tbl);
  208. MODULE_DEVICE_TABLE(vmbus, hyperv_vmbus_tbl);
  209. MODULE_LICENSE("GPL");
  210. MODULE_AUTHOR("Deepak Rawat <drawat.floss@gmail.com>");
  211. MODULE_DESCRIPTION("DRM driver for Hyper-V synthetic video device");