diff options
Diffstat (limited to 'drivers/gpu/drm/bochs/bochs_drv.c')
-rw-r--r-- | drivers/gpu/drm/bochs/bochs_drv.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/drivers/gpu/drm/bochs/bochs_drv.c b/drivers/gpu/drm/bochs/bochs_drv.c new file mode 100644 index 000000000..e18c51de1 --- /dev/null +++ b/drivers/gpu/drm/bochs/bochs_drv.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + */ + +#include <linux/module.h> +#include <linux/pci.h> + +#include <drm/drm_drv.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_managed.h> + +#include "bochs.h" + +static int bochs_modeset = -1; +module_param_named(modeset, bochs_modeset, int, 0444); +MODULE_PARM_DESC(modeset, "enable/disable kernel modesetting"); + +/* ---------------------------------------------------------------------- */ +/* drm interface */ + +static void bochs_unload(struct drm_device *dev) +{ + struct bochs_device *bochs = dev->dev_private; + + bochs_mm_fini(bochs); +} + +static int bochs_load(struct drm_device *dev) +{ + struct bochs_device *bochs; + int ret; + + bochs = drmm_kzalloc(dev, sizeof(*bochs), GFP_KERNEL); + if (bochs == NULL) + return -ENOMEM; + dev->dev_private = bochs; + bochs->dev = dev; + + ret = bochs_hw_init(dev); + if (ret) + goto err; + + ret = bochs_mm_init(bochs); + if (ret) + goto err; + + ret = bochs_kms_init(bochs); + if (ret) + goto err; + + return 0; + +err: + bochs_unload(dev); + return ret; +} + +DEFINE_DRM_GEM_FOPS(bochs_fops); + +static struct drm_driver bochs_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, + .fops = &bochs_fops, + .name = "bochs-drm", + .desc = "bochs dispi vga interface (qemu stdvga)", + .date = "20130925", + .major = 1, + .minor = 0, + DRM_GEM_VRAM_DRIVER, + .release = bochs_unload, +}; + +/* ---------------------------------------------------------------------- */ +/* pm interface */ + +#ifdef CONFIG_PM_SLEEP +static int bochs_pm_suspend(struct device *dev) +{ + struct drm_device *drm_dev = dev_get_drvdata(dev); + + return drm_mode_config_helper_suspend(drm_dev); +} + +static int bochs_pm_resume(struct device *dev) +{ + struct drm_device *drm_dev = dev_get_drvdata(dev); + + return drm_mode_config_helper_resume(drm_dev); +} +#endif + +static const struct dev_pm_ops bochs_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(bochs_pm_suspend, + bochs_pm_resume) +}; + +/* ---------------------------------------------------------------------- */ +/* pci interface */ + +static int bochs_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct drm_device *dev; + unsigned long fbsize; + int ret; + + fbsize = pci_resource_len(pdev, 0); + if (fbsize < 4 * 1024 * 1024) { + DRM_ERROR("less than 4 MB video memory, ignoring device\n"); + return -ENOMEM; + } + + ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, "bochsdrmfb"); + if (ret) + return ret; + + dev = drm_dev_alloc(&bochs_driver, &pdev->dev); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + ret = pci_enable_device(pdev); + if (ret) + goto err_free_dev; + + dev->pdev = pdev; + pci_set_drvdata(pdev, dev); + + ret = bochs_load(dev); + if (ret) + goto err_free_dev; + + ret = drm_dev_register(dev, 0); + if (ret) + goto err_unload; + + drm_fbdev_generic_setup(dev, 32); + return ret; + +err_unload: + bochs_unload(dev); +err_free_dev: + drm_dev_put(dev); + return ret; +} + +static void bochs_pci_remove(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + + drm_dev_unplug(dev); + drm_atomic_helper_shutdown(dev); + bochs_hw_fini(dev); + drm_dev_put(dev); +} + +static const struct pci_device_id bochs_pci_tbl[] = { + { + .vendor = 0x1234, + .device = 0x1111, + .subvendor = PCI_SUBVENDOR_ID_REDHAT_QUMRANET, + .subdevice = PCI_SUBDEVICE_ID_QEMU, + .driver_data = BOCHS_QEMU_STDVGA, + }, + { + .vendor = 0x1234, + .device = 0x1111, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = BOCHS_UNKNOWN, + }, + { /* end of list */ } +}; + +static struct pci_driver bochs_pci_driver = { + .name = "bochs-drm", + .id_table = bochs_pci_tbl, + .probe = bochs_pci_probe, + .remove = bochs_pci_remove, + .driver.pm = &bochs_pm_ops, +}; + +/* ---------------------------------------------------------------------- */ +/* module init/exit */ + +static int __init bochs_init(void) +{ + if (vgacon_text_force() && bochs_modeset == -1) + return -EINVAL; + + if (bochs_modeset == 0) + return -EINVAL; + + return pci_register_driver(&bochs_pci_driver); +} + +static void __exit bochs_exit(void) +{ + pci_unregister_driver(&bochs_pci_driver); +} + +module_init(bochs_init); +module_exit(bochs_exit); + +MODULE_DEVICE_TABLE(pci, bochs_pci_tbl); +MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>"); +MODULE_LICENSE("GPL"); |