2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2013-12-17 18:04:46 +01:00
/*
*/
# include <linux/module.h>
2019-06-30 08:19:20 +02:00
# include <linux/pci.h>
# include <drm/drm_drv.h>
2019-05-21 15:28:39 +10:00
# include <drm/drm_atomic_helper.h>
2013-12-17 18:04:46 +01:00
# include "bochs.h"
2017-01-18 19:10:12 +01:00
static int bochs_modeset = - 1 ;
module_param_named ( modeset , bochs_modeset , int , 0444 ) ;
MODULE_PARM_DESC ( modeset , " enable/disable kernel modesetting " ) ;
2013-12-17 18:04:46 +01:00
/* ---------------------------------------------------------------------- */
/* drm interface */
2017-01-06 15:57:31 -02:00
static void bochs_unload ( struct drm_device * dev )
2013-12-17 18:04:46 +01:00
{
struct bochs_device * bochs = dev - > dev_private ;
bochs_kms_fini ( bochs ) ;
bochs_mm_fini ( bochs ) ;
bochs_hw_fini ( dev ) ;
kfree ( bochs ) ;
dev - > dev_private = NULL ;
}
2018-09-07 00:18:09 +02:00
static int bochs_load ( struct drm_device * dev )
2013-12-17 18:04:46 +01:00
{
struct bochs_device * bochs ;
int ret ;
bochs = kzalloc ( sizeof ( * bochs ) , GFP_KERNEL ) ;
if ( bochs = = NULL )
return - ENOMEM ;
dev - > dev_private = bochs ;
bochs - > dev = dev ;
2018-09-07 00:18:09 +02:00
ret = bochs_hw_init ( dev ) ;
2013-12-17 18:04:46 +01:00
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 ;
}
static const struct file_operations bochs_fops = {
. owner = THIS_MODULE ,
2019-05-08 10:26:23 +02:00
DRM_VRAM_MM_FILE_OPERATIONS
2013-12-17 18:04:46 +01:00
} ;
static struct drm_driver bochs_driver = {
2019-06-17 17:39:24 +02:00
. driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC ,
2013-12-17 18:04:46 +01:00
. fops = & bochs_fops ,
. name = " bochs-drm " ,
. desc = " bochs dispi vga interface (qemu stdvga) " ,
. date = " 20130925 " ,
. major = 1 ,
. minor = 0 ,
2019-05-08 10:26:23 +02:00
DRM_GEM_VRAM_DRIVER ,
2013-12-17 18:04:46 +01:00
} ;
2014-04-14 11:34:49 +02:00
/* ---------------------------------------------------------------------- */
/* pm interface */
2014-07-12 11:01:38 +01:00
# ifdef CONFIG_PM_SLEEP
2014-04-14 11:34:49 +02:00
static int bochs_pm_suspend ( struct device * dev )
{
2019-07-23 18:11:04 +08:00
struct drm_device * drm_dev = dev_get_drvdata ( dev ) ;
2014-04-14 11:34:49 +02:00
2019-01-11 06:37:44 +01:00
return drm_mode_config_helper_suspend ( drm_dev ) ;
2014-04-14 11:34:49 +02:00
}
static int bochs_pm_resume ( struct device * dev )
{
2019-07-23 18:11:04 +08:00
struct drm_device * drm_dev = dev_get_drvdata ( dev ) ;
2014-04-14 11:34:49 +02:00
2019-01-11 06:37:44 +01:00
return drm_mode_config_helper_resume ( drm_dev ) ;
2014-04-14 11:34:49 +02:00
}
2014-07-12 11:01:38 +01:00
# endif
2014-04-14 11:34:49 +02:00
static const struct dev_pm_ops bochs_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( bochs_pm_suspend ,
bochs_pm_resume )
} ;
2013-12-17 18:04:46 +01:00
/* ---------------------------------------------------------------------- */
/* pci interface */
static int bochs_pci_probe ( struct pci_dev * pdev ,
const struct pci_device_id * ent )
{
2018-09-07 00:18:09 +02:00
struct drm_device * dev ;
2014-12-16 12:29:59 +01:00
unsigned long fbsize ;
2013-12-17 18:04:46 +01:00
int ret ;
2014-12-16 12:29:59 +01:00
fbsize = pci_resource_len ( pdev , 0 ) ;
if ( fbsize < 4 * 1024 * 1024 ) {
DRM_ERROR ( " less than 4 MB video memory, ignoring device \n " ) ;
return - ENOMEM ;
}
2018-09-01 16:08:46 +02:00
ret = drm_fb_helper_remove_conflicting_pci_framebuffers ( pdev , 0 , " bochsdrmfb " ) ;
2013-12-17 18:04:46 +01:00
if ( ret )
return ret ;
2018-09-07 00:18:09 +02:00
dev = drm_dev_alloc ( & bochs_driver , & pdev - > dev ) ;
if ( IS_ERR ( dev ) )
return PTR_ERR ( dev ) ;
2019-02-21 00:33:03 +00:00
ret = pci_enable_device ( pdev ) ;
if ( ret )
goto err_free_dev ;
2018-09-07 00:18:09 +02:00
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 ;
2019-01-11 06:37:50 +01:00
drm_fbdev_generic_setup ( dev , 32 ) ;
2018-09-07 00:18:09 +02:00
return ret ;
err_unload :
bochs_unload ( dev ) ;
err_free_dev :
drm_dev_put ( dev ) ;
return ret ;
2013-12-17 18:04:46 +01:00
}
static void bochs_pci_remove ( struct pci_dev * pdev )
{
struct drm_device * dev = pci_get_drvdata ( pdev ) ;
2019-05-21 15:28:39 +10:00
drm_atomic_helper_shutdown ( dev ) ;
2018-09-07 00:18:09 +02:00
drm_dev_unregister ( dev ) ;
bochs_unload ( dev ) ;
drm_dev_put ( dev ) ;
2013-12-17 18:04:46 +01:00
}
2014-08-08 15:56:03 +02:00
static const struct pci_device_id bochs_pci_tbl [ ] = {
2013-12-17 18:04:46 +01:00
{
. vendor = 0x1234 ,
. device = 0x1111 ,
2016-03-06 22:02:30 +00:00
. subvendor = PCI_SUBVENDOR_ID_REDHAT_QUMRANET ,
. subdevice = PCI_SUBDEVICE_ID_QEMU ,
2013-12-17 18:04:46 +01:00
. 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 ,
2014-04-14 11:34:49 +02:00
. driver . pm = & bochs_pm_ops ,
2013-12-17 18:04:46 +01:00
} ;
/* ---------------------------------------------------------------------- */
/* module init/exit */
static int __init bochs_init ( void )
{
2017-01-18 19:10:12 +01:00
if ( vgacon_text_force ( ) & & bochs_modeset = = - 1 )
return - EINVAL ;
if ( bochs_modeset = = 0 )
return - EINVAL ;
2017-05-24 16:51:40 +02:00
return pci_register_driver ( & bochs_pci_driver ) ;
2013-12-17 18:04:46 +01:00
}
static void __exit bochs_exit ( void )
{
2017-05-24 16:51:40 +02:00
pci_unregister_driver ( & bochs_pci_driver ) ;
2013-12-17 18:04:46 +01:00
}
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 " ) ;