1/* 2 * This program is free software; you can redistribute it and/or modify 3 * it under the terms of the GNU General Public License as published by 4 * the Free Software Foundation; either version 2 of the License, or 5 * (at your option) any later version. 6 */ 7 8#include <linux/mm.h> 9#include <linux/module.h> 10#include <linux/slab.h> 11 12#include "bochs.h" 13 14static bool enable_fbdev = true; 15module_param_named(fbdev, enable_fbdev, bool, 0444); 16MODULE_PARM_DESC(fbdev, "register fbdev device"); 17 18/* ---------------------------------------------------------------------- */ 19/* drm interface */ 20 21static int bochs_unload(struct drm_device *dev) 22{ 23 struct bochs_device *bochs = dev->dev_private; 24 25 bochs_fbdev_fini(bochs); 26 bochs_kms_fini(bochs); 27 bochs_mm_fini(bochs); 28 bochs_hw_fini(dev); 29 kfree(bochs); 30 dev->dev_private = NULL; 31 return 0; 32} 33 34static int bochs_load(struct drm_device *dev, unsigned long flags) 35{ 36 struct bochs_device *bochs; 37 int ret; 38 39 bochs = kzalloc(sizeof(*bochs), GFP_KERNEL); 40 if (bochs == NULL) 41 return -ENOMEM; 42 dev->dev_private = bochs; 43 bochs->dev = dev; 44 45 ret = bochs_hw_init(dev, flags); 46 if (ret) 47 goto err; 48 49 ret = bochs_mm_init(bochs); 50 if (ret) 51 goto err; 52 53 ret = bochs_kms_init(bochs); 54 if (ret) 55 goto err; 56 57 if (enable_fbdev) 58 bochs_fbdev_init(bochs); 59 60 return 0; 61 62err: 63 bochs_unload(dev); 64 return ret; 65} 66 67static const struct file_operations bochs_fops = { 68 .owner = THIS_MODULE, 69 .open = drm_open, 70 .release = drm_release, 71 .unlocked_ioctl = drm_ioctl, 72#ifdef CONFIG_COMPAT 73 .compat_ioctl = drm_compat_ioctl, 74#endif 75 .poll = drm_poll, 76 .read = drm_read, 77 .llseek = no_llseek, 78 .mmap = bochs_mmap, 79}; 80 81static struct drm_driver bochs_driver = { 82 .driver_features = DRIVER_GEM | DRIVER_MODESET, 83 .load = bochs_load, 84 .unload = bochs_unload, 85 .set_busid = drm_pci_set_busid, 86 .fops = &bochs_fops, 87 .name = "bochs-drm", 88 .desc = "bochs dispi vga interface (qemu stdvga)", 89 .date = "20130925", 90 .major = 1, 91 .minor = 0, 92 .gem_free_object = bochs_gem_free_object, 93 .dumb_create = bochs_dumb_create, 94 .dumb_map_offset = bochs_dumb_mmap_offset, 95 .dumb_destroy = drm_gem_dumb_destroy, 96}; 97 98/* ---------------------------------------------------------------------- */ 99/* pm interface */ 100 101#ifdef CONFIG_PM_SLEEP 102static int bochs_pm_suspend(struct device *dev) 103{ 104 struct pci_dev *pdev = to_pci_dev(dev); 105 struct drm_device *drm_dev = pci_get_drvdata(pdev); 106 struct bochs_device *bochs = drm_dev->dev_private; 107 108 drm_kms_helper_poll_disable(drm_dev); 109 110 if (bochs->fb.initialized) { 111 console_lock(); 112 drm_fb_helper_set_suspend(&bochs->fb.helper, 1); 113 console_unlock(); 114 } 115 116 return 0; 117} 118 119static int bochs_pm_resume(struct device *dev) 120{ 121 struct pci_dev *pdev = to_pci_dev(dev); 122 struct drm_device *drm_dev = pci_get_drvdata(pdev); 123 struct bochs_device *bochs = drm_dev->dev_private; 124 125 drm_helper_resume_force_mode(drm_dev); 126 127 if (bochs->fb.initialized) { 128 console_lock(); 129 drm_fb_helper_set_suspend(&bochs->fb.helper, 0); 130 console_unlock(); 131 } 132 133 drm_kms_helper_poll_enable(drm_dev); 134 return 0; 135} 136#endif 137 138static const struct dev_pm_ops bochs_pm_ops = { 139 SET_SYSTEM_SLEEP_PM_OPS(bochs_pm_suspend, 140 bochs_pm_resume) 141}; 142 143/* ---------------------------------------------------------------------- */ 144/* pci interface */ 145 146static int bochs_kick_out_firmware_fb(struct pci_dev *pdev) 147{ 148 struct apertures_struct *ap; 149 150 ap = alloc_apertures(1); 151 if (!ap) 152 return -ENOMEM; 153 154 ap->ranges[0].base = pci_resource_start(pdev, 0); 155 ap->ranges[0].size = pci_resource_len(pdev, 0); 156 remove_conflicting_framebuffers(ap, "bochsdrmfb", false); 157 kfree(ap); 158 159 return 0; 160} 161 162static int bochs_pci_probe(struct pci_dev *pdev, 163 const struct pci_device_id *ent) 164{ 165 int ret; 166 167 ret = bochs_kick_out_firmware_fb(pdev); 168 if (ret) 169 return ret; 170 171 return drm_get_pci_dev(pdev, ent, &bochs_driver); 172} 173 174static void bochs_pci_remove(struct pci_dev *pdev) 175{ 176 struct drm_device *dev = pci_get_drvdata(pdev); 177 178 drm_put_dev(dev); 179} 180 181static const struct pci_device_id bochs_pci_tbl[] = { 182 { 183 .vendor = 0x1234, 184 .device = 0x1111, 185 .subvendor = 0x1af4, 186 .subdevice = 0x1100, 187 .driver_data = BOCHS_QEMU_STDVGA, 188 }, 189 { 190 .vendor = 0x1234, 191 .device = 0x1111, 192 .subvendor = PCI_ANY_ID, 193 .subdevice = PCI_ANY_ID, 194 .driver_data = BOCHS_UNKNOWN, 195 }, 196 { /* end of list */ } 197}; 198 199static struct pci_driver bochs_pci_driver = { 200 .name = "bochs-drm", 201 .id_table = bochs_pci_tbl, 202 .probe = bochs_pci_probe, 203 .remove = bochs_pci_remove, 204 .driver.pm = &bochs_pm_ops, 205}; 206 207/* ---------------------------------------------------------------------- */ 208/* module init/exit */ 209 210static int __init bochs_init(void) 211{ 212 return drm_pci_init(&bochs_driver, &bochs_pci_driver); 213} 214 215static void __exit bochs_exit(void) 216{ 217 drm_pci_exit(&bochs_driver, &bochs_pci_driver); 218} 219 220module_init(bochs_init); 221module_exit(bochs_exit); 222 223MODULE_DEVICE_TABLE(pci, bochs_pci_tbl); 224MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>"); 225MODULE_LICENSE("GPL"); 226