1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright (C) 2013-2017 Oracle Corporation 4 * This file is based on ast_drv.c 5 * Copyright 2012 Red Hat Inc. 6 * Authors: Dave Airlie <airlied@redhat.com> 7 * Michael Thayer <michael.thayer@oracle.com, 8 * Hans de Goede <hdegoede@redhat.com> 9 */ 10 #include <linux/module.h> 11 #include <linux/pci.h> 12 #include <linux/vt_kern.h> 13 14 #include <drm/drm_aperture.h> 15 #include <drm/drm_atomic_helper.h> 16 #include <drm/drm_drv.h> 17 #include <drm/drm_fbdev_ttm.h> 18 #include <drm/drm_file.h> 19 #include <drm/drm_ioctl.h> 20 #include <drm/drm_managed.h> 21 #include <drm/drm_modeset_helper.h> 22 #include <drm/drm_module.h> 23 24 #include "vbox_drv.h" 25 26 static int vbox_modeset = -1; 27 28 MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); 29 module_param_named(modeset, vbox_modeset, int, 0400); 30 31 static const struct drm_driver driver; 32 33 static const struct pci_device_id pciidlist[] = { 34 { PCI_DEVICE(0x80ee, 0xbeef) }, 35 { } 36 }; 37 MODULE_DEVICE_TABLE(pci, pciidlist); 38 39 static int vbox_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 40 { 41 struct vbox_private *vbox; 42 int ret = 0; 43 44 if (!vbox_check_supported(VBE_DISPI_ID_HGSMI)) 45 return -ENODEV; 46 47 ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, &driver); 48 if (ret) 49 return ret; 50 51 vbox = devm_drm_dev_alloc(&pdev->dev, &driver, 52 struct vbox_private, ddev); 53 if (IS_ERR(vbox)) 54 return PTR_ERR(vbox); 55 56 pci_set_drvdata(pdev, vbox); 57 mutex_init(&vbox->hw_mutex); 58 59 ret = pcim_enable_device(pdev); 60 if (ret) 61 return ret; 62 63 ret = vbox_hw_init(vbox); 64 if (ret) 65 return ret; 66 67 ret = vbox_mm_init(vbox); 68 if (ret) 69 goto err_hw_fini; 70 71 ret = vbox_mode_init(vbox); 72 if (ret) 73 goto err_hw_fini; 74 75 ret = vbox_irq_init(vbox); 76 if (ret) 77 goto err_mode_fini; 78 79 ret = drm_dev_register(&vbox->ddev, 0); 80 if (ret) 81 goto err_irq_fini; 82 83 drm_fbdev_ttm_setup(&vbox->ddev, 32); 84 85 return 0; 86 87 err_irq_fini: 88 vbox_irq_fini(vbox); 89 err_mode_fini: 90 vbox_mode_fini(vbox); 91 err_hw_fini: 92 vbox_hw_fini(vbox); 93 return ret; 94 } 95 96 static void vbox_pci_remove(struct pci_dev *pdev) 97 { 98 struct vbox_private *vbox = pci_get_drvdata(pdev); 99 100 drm_dev_unregister(&vbox->ddev); 101 drm_atomic_helper_shutdown(&vbox->ddev); 102 vbox_irq_fini(vbox); 103 vbox_mode_fini(vbox); 104 vbox_hw_fini(vbox); 105 } 106 107 static void vbox_pci_shutdown(struct pci_dev *pdev) 108 { 109 struct vbox_private *vbox = pci_get_drvdata(pdev); 110 111 drm_atomic_helper_shutdown(&vbox->ddev); 112 } 113 114 static int vbox_pm_suspend(struct device *dev) 115 { 116 struct vbox_private *vbox = dev_get_drvdata(dev); 117 struct pci_dev *pdev = to_pci_dev(dev); 118 int error; 119 120 error = drm_mode_config_helper_suspend(&vbox->ddev); 121 if (error) 122 return error; 123 124 pci_save_state(pdev); 125 pci_disable_device(pdev); 126 pci_set_power_state(pdev, PCI_D3hot); 127 128 return 0; 129 } 130 131 static int vbox_pm_resume(struct device *dev) 132 { 133 struct vbox_private *vbox = dev_get_drvdata(dev); 134 struct pci_dev *pdev = to_pci_dev(dev); 135 136 if (pci_enable_device(pdev)) 137 return -EIO; 138 139 return drm_mode_config_helper_resume(&vbox->ddev); 140 } 141 142 static int vbox_pm_freeze(struct device *dev) 143 { 144 struct vbox_private *vbox = dev_get_drvdata(dev); 145 146 return drm_mode_config_helper_suspend(&vbox->ddev); 147 } 148 149 static int vbox_pm_thaw(struct device *dev) 150 { 151 struct vbox_private *vbox = dev_get_drvdata(dev); 152 153 return drm_mode_config_helper_resume(&vbox->ddev); 154 } 155 156 static int vbox_pm_poweroff(struct device *dev) 157 { 158 struct vbox_private *vbox = dev_get_drvdata(dev); 159 160 return drm_mode_config_helper_suspend(&vbox->ddev); 161 } 162 163 static const struct dev_pm_ops vbox_pm_ops = { 164 .suspend = vbox_pm_suspend, 165 .resume = vbox_pm_resume, 166 .freeze = vbox_pm_freeze, 167 .thaw = vbox_pm_thaw, 168 .poweroff = vbox_pm_poweroff, 169 .restore = vbox_pm_resume, 170 }; 171 172 static struct pci_driver vbox_pci_driver = { 173 .name = DRIVER_NAME, 174 .id_table = pciidlist, 175 .probe = vbox_pci_probe, 176 .remove = vbox_pci_remove, 177 .shutdown = vbox_pci_shutdown, 178 .driver.pm = pm_sleep_ptr(&vbox_pm_ops), 179 }; 180 181 DEFINE_DRM_GEM_FOPS(vbox_fops); 182 183 static const struct drm_driver driver = { 184 .driver_features = 185 DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC | DRIVER_CURSOR_HOTSPOT, 186 187 .fops = &vbox_fops, 188 .name = DRIVER_NAME, 189 .desc = DRIVER_DESC, 190 .date = DRIVER_DATE, 191 .major = DRIVER_MAJOR, 192 .minor = DRIVER_MINOR, 193 .patchlevel = DRIVER_PATCHLEVEL, 194 195 DRM_GEM_VRAM_DRIVER, 196 }; 197 198 drm_module_pci_driver_if_modeset(vbox_pci_driver, vbox_modeset); 199 200 MODULE_AUTHOR("Oracle Corporation"); 201 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 202 MODULE_DESCRIPTION(DRIVER_DESC); 203 MODULE_LICENSE("GPL and additional rights"); 204