18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: MIT 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2013-2017 Oracle Corporation 48c2ecf20Sopenharmony_ci * This file is based on ast_drv.c 58c2ecf20Sopenharmony_ci * Copyright 2012 Red Hat Inc. 68c2ecf20Sopenharmony_ci * Authors: Dave Airlie <airlied@redhat.com> 78c2ecf20Sopenharmony_ci * Michael Thayer <michael.thayer@oracle.com, 88c2ecf20Sopenharmony_ci * Hans de Goede <hdegoede@redhat.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/console.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/pci.h> 138c2ecf20Sopenharmony_ci#include <linux/vt_kern.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h> 168c2ecf20Sopenharmony_ci#include <drm/drm_drv.h> 178c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 188c2ecf20Sopenharmony_ci#include <drm/drm_file.h> 198c2ecf20Sopenharmony_ci#include <drm/drm_ioctl.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_managed.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "vbox_drv.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int vbox_modeset = -1; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciMODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); 278c2ecf20Sopenharmony_cimodule_param_named(modeset, vbox_modeset, int, 0400); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic struct drm_driver driver; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic const struct pci_device_id pciidlist[] = { 328c2ecf20Sopenharmony_ci { PCI_DEVICE(0x80ee, 0xbeef) }, 338c2ecf20Sopenharmony_ci { } 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pciidlist); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int vbox_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct vbox_private *vbox; 408c2ecf20Sopenharmony_ci int ret = 0; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (!vbox_check_supported(VBE_DISPI_ID_HGSMI)) 438c2ecf20Sopenharmony_ci return -ENODEV; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, "vboxvideodrmfb"); 468c2ecf20Sopenharmony_ci if (ret) 478c2ecf20Sopenharmony_ci return ret; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci vbox = devm_drm_dev_alloc(&pdev->dev, &driver, 508c2ecf20Sopenharmony_ci struct vbox_private, ddev); 518c2ecf20Sopenharmony_ci if (IS_ERR(vbox)) 528c2ecf20Sopenharmony_ci return PTR_ERR(vbox); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci vbox->ddev.pdev = pdev; 558c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, vbox); 568c2ecf20Sopenharmony_ci mutex_init(&vbox->hw_mutex); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci ret = pcim_enable_device(pdev); 598c2ecf20Sopenharmony_ci if (ret) 608c2ecf20Sopenharmony_ci return ret; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci ret = vbox_hw_init(vbox); 638c2ecf20Sopenharmony_ci if (ret) 648c2ecf20Sopenharmony_ci return ret; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci ret = vbox_mm_init(vbox); 678c2ecf20Sopenharmony_ci if (ret) 688c2ecf20Sopenharmony_ci goto err_hw_fini; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci ret = vbox_mode_init(vbox); 718c2ecf20Sopenharmony_ci if (ret) 728c2ecf20Sopenharmony_ci goto err_mm_fini; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci ret = vbox_irq_init(vbox); 758c2ecf20Sopenharmony_ci if (ret) 768c2ecf20Sopenharmony_ci goto err_mode_fini; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci ret = drm_dev_register(&vbox->ddev, 0); 798c2ecf20Sopenharmony_ci if (ret) 808c2ecf20Sopenharmony_ci goto err_irq_fini; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci drm_fbdev_generic_setup(&vbox->ddev, 32); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cierr_irq_fini: 878c2ecf20Sopenharmony_ci vbox_irq_fini(vbox); 888c2ecf20Sopenharmony_cierr_mode_fini: 898c2ecf20Sopenharmony_ci vbox_mode_fini(vbox); 908c2ecf20Sopenharmony_cierr_mm_fini: 918c2ecf20Sopenharmony_ci vbox_mm_fini(vbox); 928c2ecf20Sopenharmony_cierr_hw_fini: 938c2ecf20Sopenharmony_ci vbox_hw_fini(vbox); 948c2ecf20Sopenharmony_ci return ret; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void vbox_pci_remove(struct pci_dev *pdev) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct vbox_private *vbox = pci_get_drvdata(pdev); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci drm_dev_unregister(&vbox->ddev); 1028c2ecf20Sopenharmony_ci vbox_irq_fini(vbox); 1038c2ecf20Sopenharmony_ci vbox_mode_fini(vbox); 1048c2ecf20Sopenharmony_ci vbox_mm_fini(vbox); 1058c2ecf20Sopenharmony_ci vbox_hw_fini(vbox); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 1098c2ecf20Sopenharmony_cistatic int vbox_pm_suspend(struct device *dev) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct vbox_private *vbox = dev_get_drvdata(dev); 1128c2ecf20Sopenharmony_ci int error; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci error = drm_mode_config_helper_suspend(&vbox->ddev); 1158c2ecf20Sopenharmony_ci if (error) 1168c2ecf20Sopenharmony_ci return error; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci pci_save_state(vbox->ddev.pdev); 1198c2ecf20Sopenharmony_ci pci_disable_device(vbox->ddev.pdev); 1208c2ecf20Sopenharmony_ci pci_set_power_state(vbox->ddev.pdev, PCI_D3hot); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return 0; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int vbox_pm_resume(struct device *dev) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct vbox_private *vbox = dev_get_drvdata(dev); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (pci_enable_device(vbox->ddev.pdev)) 1308c2ecf20Sopenharmony_ci return -EIO; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return drm_mode_config_helper_resume(&vbox->ddev); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic int vbox_pm_freeze(struct device *dev) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct vbox_private *vbox = dev_get_drvdata(dev); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return drm_mode_config_helper_suspend(&vbox->ddev); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int vbox_pm_thaw(struct device *dev) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct vbox_private *vbox = dev_get_drvdata(dev); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return drm_mode_config_helper_resume(&vbox->ddev); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int vbox_pm_poweroff(struct device *dev) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct vbox_private *vbox = dev_get_drvdata(dev); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return drm_mode_config_helper_suspend(&vbox->ddev); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic const struct dev_pm_ops vbox_pm_ops = { 1578c2ecf20Sopenharmony_ci .suspend = vbox_pm_suspend, 1588c2ecf20Sopenharmony_ci .resume = vbox_pm_resume, 1598c2ecf20Sopenharmony_ci .freeze = vbox_pm_freeze, 1608c2ecf20Sopenharmony_ci .thaw = vbox_pm_thaw, 1618c2ecf20Sopenharmony_ci .poweroff = vbox_pm_poweroff, 1628c2ecf20Sopenharmony_ci .restore = vbox_pm_resume, 1638c2ecf20Sopenharmony_ci}; 1648c2ecf20Sopenharmony_ci#endif 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic struct pci_driver vbox_pci_driver = { 1678c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 1688c2ecf20Sopenharmony_ci .id_table = pciidlist, 1698c2ecf20Sopenharmony_ci .probe = vbox_pci_probe, 1708c2ecf20Sopenharmony_ci .remove = vbox_pci_remove, 1718c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 1728c2ecf20Sopenharmony_ci .driver.pm = &vbox_pm_ops, 1738c2ecf20Sopenharmony_ci#endif 1748c2ecf20Sopenharmony_ci}; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ciDEFINE_DRM_GEM_FOPS(vbox_fops); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic struct drm_driver driver = { 1798c2ecf20Sopenharmony_ci .driver_features = 1808c2ecf20Sopenharmony_ci DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci .lastclose = drm_fb_helper_lastclose, 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci .fops = &vbox_fops, 1858c2ecf20Sopenharmony_ci .irq_handler = vbox_irq_handler, 1868c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 1878c2ecf20Sopenharmony_ci .desc = DRIVER_DESC, 1888c2ecf20Sopenharmony_ci .date = DRIVER_DATE, 1898c2ecf20Sopenharmony_ci .major = DRIVER_MAJOR, 1908c2ecf20Sopenharmony_ci .minor = DRIVER_MINOR, 1918c2ecf20Sopenharmony_ci .patchlevel = DRIVER_PATCHLEVEL, 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci DRM_GEM_VRAM_DRIVER, 1948c2ecf20Sopenharmony_ci}; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int __init vbox_init(void) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci#ifdef CONFIG_VGA_CONSOLE 1998c2ecf20Sopenharmony_ci if (vgacon_text_force() && vbox_modeset == -1) 2008c2ecf20Sopenharmony_ci return -EINVAL; 2018c2ecf20Sopenharmony_ci#endif 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (vbox_modeset == 0) 2048c2ecf20Sopenharmony_ci return -EINVAL; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return pci_register_driver(&vbox_pci_driver); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic void __exit vbox_exit(void) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci pci_unregister_driver(&vbox_pci_driver); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cimodule_init(vbox_init); 2158c2ecf20Sopenharmony_cimodule_exit(vbox_exit); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ciMODULE_AUTHOR("Oracle Corporation"); 2188c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 2198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 2208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL and additional rights"); 221