2013-12-17 18:04:46 +01:00
/*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/mm.h>
# include <linux/module.h>
# include <linux/slab.h>
2016-08-10 18:52:34 +02:00
# include <drm/drm_fb_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
static bool enable_fbdev = true ;
module_param_named ( fbdev , enable_fbdev , bool , 0444 ) ;
MODULE_PARM_DESC ( fbdev , " register fbdev device " ) ;
/* ---------------------------------------------------------------------- */
/* 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_fbdev_fini ( bochs ) ;
bochs_kms_fini ( bochs ) ;
bochs_mm_fini ( bochs ) ;
bochs_hw_fini ( dev ) ;
kfree ( bochs ) ;
dev - > dev_private = NULL ;
}
static int bochs_load ( struct drm_device * dev , unsigned long flags )
{
struct bochs_device * bochs ;
int ret ;
bochs = kzalloc ( sizeof ( * bochs ) , GFP_KERNEL ) ;
if ( bochs = = NULL )
return - ENOMEM ;
dev - > dev_private = bochs ;
bochs - > dev = dev ;
ret = bochs_hw_init ( dev , flags ) ;
if ( ret )
goto err ;
ret = bochs_mm_init ( bochs ) ;
if ( ret )
goto err ;
ret = bochs_kms_init ( bochs ) ;
if ( ret )
goto err ;
if ( enable_fbdev )
bochs_fbdev_init ( bochs ) ;
return 0 ;
err :
bochs_unload ( dev ) ;
return ret ;
}
static const struct file_operations bochs_fops = {
. owner = THIS_MODULE ,
. open = drm_open ,
. release = drm_release ,
. unlocked_ioctl = drm_ioctl ,
. compat_ioctl = drm_compat_ioctl ,
. poll = drm_poll ,
. read = drm_read ,
. llseek = no_llseek ,
. mmap = bochs_mmap ,
} ;
static struct drm_driver bochs_driver = {
. driver_features = DRIVER_GEM | DRIVER_MODESET ,
. load = bochs_load ,
. unload = bochs_unload ,
. fops = & bochs_fops ,
. name = " bochs-drm " ,
. desc = " bochs dispi vga interface (qemu stdvga) " ,
. date = " 20130925 " ,
. major = 1 ,
. minor = 0 ,
2016-05-30 19:52:56 +02:00
. gem_free_object_unlocked = bochs_gem_free_object ,
2013-12-17 18:04:46 +01:00
. dumb_create = bochs_dumb_create ,
. dumb_map_offset = bochs_dumb_mmap_offset ,
} ;
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 )
{
struct pci_dev * pdev = to_pci_dev ( dev ) ;
struct drm_device * drm_dev = pci_get_drvdata ( pdev ) ;
struct bochs_device * bochs = drm_dev - > dev_private ;
drm_kms_helper_poll_disable ( drm_dev ) ;
if ( bochs - > fb . initialized ) {
console_lock ( ) ;
2015-07-31 16:21:59 +05:30
drm_fb_helper_set_suspend ( & bochs - > fb . helper , 1 ) ;
2014-04-14 11:34:49 +02:00
console_unlock ( ) ;
}
return 0 ;
}
static int bochs_pm_resume ( struct device * dev )
{
struct pci_dev * pdev = to_pci_dev ( dev ) ;
struct drm_device * drm_dev = pci_get_drvdata ( pdev ) ;
struct bochs_device * bochs = drm_dev - > dev_private ;
drm_helper_resume_force_mode ( drm_dev ) ;
if ( bochs - > fb . initialized ) {
console_lock ( ) ;
2015-07-31 16:21:59 +05:30
drm_fb_helper_set_suspend ( & bochs - > fb . helper , 0 ) ;
2014-04-14 11:34:49 +02:00
console_unlock ( ) ;
}
drm_kms_helper_poll_enable ( drm_dev ) ;
return 0 ;
}
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_kick_out_firmware_fb ( struct pci_dev * pdev )
{
struct apertures_struct * ap ;
ap = alloc_apertures ( 1 ) ;
if ( ! ap )
return - ENOMEM ;
ap - > ranges [ 0 ] . base = pci_resource_start ( pdev , 0 ) ;
ap - > ranges [ 0 ] . size = pci_resource_len ( pdev , 0 ) ;
2016-08-10 18:52:34 +02:00
drm_fb_helper_remove_conflicting_framebuffers ( ap , " bochsdrmfb " , false ) ;
2013-12-17 18:04:46 +01:00
kfree ( ap ) ;
return 0 ;
}
static int bochs_pci_probe ( struct pci_dev * pdev ,
const struct pci_device_id * ent )
{
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 ;
}
2013-12-17 18:04:46 +01:00
ret = bochs_kick_out_firmware_fb ( pdev ) ;
if ( ret )
return ret ;
return drm_get_pci_dev ( pdev , ent , & bochs_driver ) ;
}
static void bochs_pci_remove ( struct pci_dev * pdev )
{
struct drm_device * dev = pci_get_drvdata ( pdev ) ;
drm_put_dev ( dev ) ;
}
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 " ) ;