Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright 2021 Microsoft
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     { /* end of list */ }
0057 };
0058 
0059 /*
0060  * PCI stub to support gen1 VM.
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      * Map the VRAM cacheable for performance. This is also required for VM
0091      * connect to display properly for ARM64 Linux VM, as the host also maps
0092      * the VRAM cacheable.
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      * Should be done only once during init and resume. Failing to update
0138      * vram location is not fatal. Device will update dirty area till
0139      * preferred resolution only.
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     /* Synthetic Video Device GUID */
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");