0001
0002
0003
0004
0005
0006 #include <linux/efi.h>
0007 #include <linux/hyperv.h>
0008 #include <linux/module.h>
0009 #include <linux/pci.h>
0010
0011 #include <drm/drm_aperture.h>
0012 #include <drm/drm_atomic_helper.h>
0013 #include <drm/drm_drv.h>
0014 #include <drm/drm_fb_helper.h>
0015 #include <drm/drm_gem_shmem_helper.h>
0016 #include <drm/drm_simple_kms_helper.h>
0017
0018 #include "hyperv_drm.h"
0019
0020 #define DRIVER_NAME "hyperv_drm"
0021 #define DRIVER_DESC "DRM driver for Hyper-V synthetic video device"
0022 #define DRIVER_DATE "2020"
0023 #define DRIVER_MAJOR 1
0024 #define DRIVER_MINOR 0
0025
0026 DEFINE_DRM_GEM_FOPS(hv_fops);
0027
0028 static struct drm_driver hyperv_driver = {
0029 .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
0030
0031 .name = DRIVER_NAME,
0032 .desc = DRIVER_DESC,
0033 .date = DRIVER_DATE,
0034 .major = DRIVER_MAJOR,
0035 .minor = DRIVER_MINOR,
0036
0037 .fops = &hv_fops,
0038 DRM_GEM_SHMEM_DRIVER_OPS,
0039 };
0040
0041 static int hyperv_pci_probe(struct pci_dev *pdev,
0042 const struct pci_device_id *ent)
0043 {
0044 return 0;
0045 }
0046
0047 static void hyperv_pci_remove(struct pci_dev *pdev)
0048 {
0049 }
0050
0051 static const struct pci_device_id hyperv_pci_tbl[] = {
0052 {
0053 .vendor = PCI_VENDOR_ID_MICROSOFT,
0054 .device = PCI_DEVICE_ID_HYPERV_VIDEO,
0055 },
0056 { }
0057 };
0058
0059
0060
0061
0062 static struct pci_driver hyperv_pci_driver = {
0063 .name = KBUILD_MODNAME,
0064 .id_table = hyperv_pci_tbl,
0065 .probe = hyperv_pci_probe,
0066 .remove = hyperv_pci_remove,
0067 };
0068
0069 static int hyperv_setup_vram(struct hyperv_drm_device *hv,
0070 struct hv_device *hdev)
0071 {
0072 struct drm_device *dev = &hv->dev;
0073 int ret;
0074
0075 drm_aperture_remove_conflicting_framebuffers(screen_info.lfb_base,
0076 screen_info.lfb_size,
0077 false,
0078 &hyperv_driver);
0079
0080 hv->fb_size = (unsigned long)hv->mmio_megabytes * 1024 * 1024;
0081
0082 ret = vmbus_allocate_mmio(&hv->mem, hdev, 0, -1, hv->fb_size, 0x100000,
0083 true);
0084 if (ret) {
0085 drm_err(dev, "Failed to allocate mmio\n");
0086 return -ENOMEM;
0087 }
0088
0089
0090
0091
0092
0093
0094 hv->vram = ioremap_cache(hv->mem->start, hv->fb_size);
0095 if (!hv->vram) {
0096 drm_err(dev, "Failed to map vram\n");
0097 ret = -ENOMEM;
0098 goto error;
0099 }
0100
0101 hv->fb_base = hv->mem->start;
0102 return 0;
0103
0104 error:
0105 vmbus_free_mmio(hv->mem->start, hv->fb_size);
0106 return ret;
0107 }
0108
0109 static int hyperv_vmbus_probe(struct hv_device *hdev,
0110 const struct hv_vmbus_device_id *dev_id)
0111 {
0112 struct hyperv_drm_device *hv;
0113 struct drm_device *dev;
0114 int ret;
0115
0116 hv = devm_drm_dev_alloc(&hdev->device, &hyperv_driver,
0117 struct hyperv_drm_device, dev);
0118 if (IS_ERR(hv))
0119 return PTR_ERR(hv);
0120
0121 dev = &hv->dev;
0122 init_completion(&hv->wait);
0123 hv_set_drvdata(hdev, hv);
0124 hv->hdev = hdev;
0125
0126 ret = hyperv_connect_vsp(hdev);
0127 if (ret) {
0128 drm_err(dev, "Failed to connect to vmbus.\n");
0129 goto err_hv_set_drv_data;
0130 }
0131
0132 ret = hyperv_setup_vram(hv, hdev);
0133 if (ret)
0134 goto err_vmbus_close;
0135
0136
0137
0138
0139
0140
0141 ret = hyperv_update_vram_location(hdev, hv->fb_base);
0142 if (ret)
0143 drm_warn(dev, "Failed to update vram location.\n");
0144
0145 hv->dirt_needed = true;
0146
0147 ret = hyperv_mode_config_init(hv);
0148 if (ret)
0149 goto err_free_mmio;
0150
0151 ret = drm_dev_register(dev, 0);
0152 if (ret) {
0153 drm_err(dev, "Failed to register drm driver.\n");
0154 goto err_free_mmio;
0155 }
0156
0157 drm_fbdev_generic_setup(dev, 0);
0158
0159 return 0;
0160
0161 err_free_mmio:
0162 vmbus_free_mmio(hv->mem->start, hv->fb_size);
0163 err_vmbus_close:
0164 vmbus_close(hdev->channel);
0165 err_hv_set_drv_data:
0166 hv_set_drvdata(hdev, NULL);
0167 return ret;
0168 }
0169
0170 static int hyperv_vmbus_remove(struct hv_device *hdev)
0171 {
0172 struct drm_device *dev = hv_get_drvdata(hdev);
0173 struct hyperv_drm_device *hv = to_hv(dev);
0174
0175 drm_dev_unplug(dev);
0176 drm_atomic_helper_shutdown(dev);
0177 vmbus_close(hdev->channel);
0178 hv_set_drvdata(hdev, NULL);
0179
0180 vmbus_free_mmio(hv->mem->start, hv->fb_size);
0181
0182 return 0;
0183 }
0184
0185 static int hyperv_vmbus_suspend(struct hv_device *hdev)
0186 {
0187 struct drm_device *dev = hv_get_drvdata(hdev);
0188 int ret;
0189
0190 ret = drm_mode_config_helper_suspend(dev);
0191 if (ret)
0192 return ret;
0193
0194 vmbus_close(hdev->channel);
0195
0196 return 0;
0197 }
0198
0199 static int hyperv_vmbus_resume(struct hv_device *hdev)
0200 {
0201 struct drm_device *dev = hv_get_drvdata(hdev);
0202 struct hyperv_drm_device *hv = to_hv(dev);
0203 int ret;
0204
0205 ret = hyperv_connect_vsp(hdev);
0206 if (ret)
0207 return ret;
0208
0209 ret = hyperv_update_vram_location(hdev, hv->fb_base);
0210 if (ret)
0211 return ret;
0212
0213 return drm_mode_config_helper_resume(dev);
0214 }
0215
0216 static const struct hv_vmbus_device_id hyperv_vmbus_tbl[] = {
0217
0218 {HV_SYNTHVID_GUID},
0219 {}
0220 };
0221
0222 static struct hv_driver hyperv_hv_driver = {
0223 .name = KBUILD_MODNAME,
0224 .id_table = hyperv_vmbus_tbl,
0225 .probe = hyperv_vmbus_probe,
0226 .remove = hyperv_vmbus_remove,
0227 .suspend = hyperv_vmbus_suspend,
0228 .resume = hyperv_vmbus_resume,
0229 .driver = {
0230 .probe_type = PROBE_PREFER_ASYNCHRONOUS,
0231 },
0232 };
0233
0234 static int __init hyperv_init(void)
0235 {
0236 int ret;
0237
0238 if (drm_firmware_drivers_only())
0239 return -ENODEV;
0240
0241 ret = pci_register_driver(&hyperv_pci_driver);
0242 if (ret != 0)
0243 return ret;
0244
0245 return vmbus_driver_register(&hyperv_hv_driver);
0246 }
0247
0248 static void __exit hyperv_exit(void)
0249 {
0250 vmbus_driver_unregister(&hyperv_hv_driver);
0251 pci_unregister_driver(&hyperv_pci_driver);
0252 }
0253
0254 module_init(hyperv_init);
0255 module_exit(hyperv_exit);
0256
0257 MODULE_DEVICE_TABLE(pci, hyperv_pci_tbl);
0258 MODULE_DEVICE_TABLE(vmbus, hyperv_vmbus_tbl);
0259 MODULE_LICENSE("GPL");
0260 MODULE_AUTHOR("Deepak Rawat <drawat.floss@gmail.com>");
0261 MODULE_DESCRIPTION("DRM driver for Hyper-V synthetic video device");