1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 */ 4 5#include <linux/module.h> 6#include <linux/pci.h> 7 8#include <drm/drm_drv.h> 9#include <drm/drm_atomic_helper.h> 10#include <drm/drm_managed.h> 11 12#include "bochs.h" 13 14static int bochs_modeset = -1; 15module_param_named(modeset, bochs_modeset, int, 0444); 16MODULE_PARM_DESC(modeset, "enable/disable kernel modesetting"); 17 18/* ---------------------------------------------------------------------- */ 19/* drm interface */ 20 21static void bochs_unload(struct drm_device *dev) 22{ 23 struct bochs_device *bochs = dev->dev_private; 24 25 bochs_mm_fini(bochs); 26} 27 28static int bochs_load(struct drm_device *dev) 29{ 30 struct bochs_device *bochs; 31 int ret; 32 33 bochs = drmm_kzalloc(dev, sizeof(*bochs), GFP_KERNEL); 34 if (bochs == NULL) 35 return -ENOMEM; 36 dev->dev_private = bochs; 37 bochs->dev = dev; 38 39 ret = bochs_hw_init(dev); 40 if (ret) 41 goto err; 42 43 ret = bochs_mm_init(bochs); 44 if (ret) 45 goto err; 46 47 ret = bochs_kms_init(bochs); 48 if (ret) 49 goto err; 50 51 return 0; 52 53err: 54 bochs_unload(dev); 55 return ret; 56} 57 58DEFINE_DRM_GEM_FOPS(bochs_fops); 59 60static struct drm_driver bochs_driver = { 61 .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 62 .fops = &bochs_fops, 63 .name = "bochs-drm", 64 .desc = "bochs dispi vga interface (qemu stdvga)", 65 .date = "20130925", 66 .major = 1, 67 .minor = 0, 68 DRM_GEM_VRAM_DRIVER, 69 .release = bochs_unload, 70}; 71 72/* ---------------------------------------------------------------------- */ 73/* pm interface */ 74 75#ifdef CONFIG_PM_SLEEP 76static int bochs_pm_suspend(struct device *dev) 77{ 78 struct drm_device *drm_dev = dev_get_drvdata(dev); 79 80 return drm_mode_config_helper_suspend(drm_dev); 81} 82 83static int bochs_pm_resume(struct device *dev) 84{ 85 struct drm_device *drm_dev = dev_get_drvdata(dev); 86 87 return drm_mode_config_helper_resume(drm_dev); 88} 89#endif 90 91static const struct dev_pm_ops bochs_pm_ops = { 92 SET_SYSTEM_SLEEP_PM_OPS(bochs_pm_suspend, 93 bochs_pm_resume) 94}; 95 96/* ---------------------------------------------------------------------- */ 97/* pci interface */ 98 99static int bochs_pci_probe(struct pci_dev *pdev, 100 const struct pci_device_id *ent) 101{ 102 struct drm_device *dev; 103 unsigned long fbsize; 104 int ret; 105 106 fbsize = pci_resource_len(pdev, 0); 107 if (fbsize < 4 * 1024 * 1024) { 108 DRM_ERROR("less than 4 MB video memory, ignoring device\n"); 109 return -ENOMEM; 110 } 111 112 ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, "bochsdrmfb"); 113 if (ret) 114 return ret; 115 116 dev = drm_dev_alloc(&bochs_driver, &pdev->dev); 117 if (IS_ERR(dev)) 118 return PTR_ERR(dev); 119 120 ret = pci_enable_device(pdev); 121 if (ret) 122 goto err_free_dev; 123 124 dev->pdev = pdev; 125 pci_set_drvdata(pdev, dev); 126 127 ret = bochs_load(dev); 128 if (ret) 129 goto err_free_dev; 130 131 ret = drm_dev_register(dev, 0); 132 if (ret) 133 goto err_unload; 134 135 drm_fbdev_generic_setup(dev, 32); 136 return ret; 137 138err_unload: 139 bochs_unload(dev); 140err_free_dev: 141 drm_dev_put(dev); 142 return ret; 143} 144 145static void bochs_pci_remove(struct pci_dev *pdev) 146{ 147 struct drm_device *dev = pci_get_drvdata(pdev); 148 149 drm_dev_unplug(dev); 150 drm_atomic_helper_shutdown(dev); 151 bochs_hw_fini(dev); 152 drm_dev_put(dev); 153} 154 155static const struct pci_device_id bochs_pci_tbl[] = { 156 { 157 .vendor = 0x1234, 158 .device = 0x1111, 159 .subvendor = PCI_SUBVENDOR_ID_REDHAT_QUMRANET, 160 .subdevice = PCI_SUBDEVICE_ID_QEMU, 161 .driver_data = BOCHS_QEMU_STDVGA, 162 }, 163 { 164 .vendor = 0x1234, 165 .device = 0x1111, 166 .subvendor = PCI_ANY_ID, 167 .subdevice = PCI_ANY_ID, 168 .driver_data = BOCHS_UNKNOWN, 169 }, 170 { /* end of list */ } 171}; 172 173static struct pci_driver bochs_pci_driver = { 174 .name = "bochs-drm", 175 .id_table = bochs_pci_tbl, 176 .probe = bochs_pci_probe, 177 .remove = bochs_pci_remove, 178 .driver.pm = &bochs_pm_ops, 179}; 180 181/* ---------------------------------------------------------------------- */ 182/* module init/exit */ 183 184static int __init bochs_init(void) 185{ 186 if (vgacon_text_force() && bochs_modeset == -1) 187 return -EINVAL; 188 189 if (bochs_modeset == 0) 190 return -EINVAL; 191 192 return pci_register_driver(&bochs_pci_driver); 193} 194 195static void __exit bochs_exit(void) 196{ 197 pci_unregister_driver(&bochs_pci_driver); 198} 199 200module_init(bochs_init); 201module_exit(bochs_exit); 202 203MODULE_DEVICE_TABLE(pci, bochs_pci_tbl); 204MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>"); 205MODULE_LICENSE("GPL"); 206