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