2019-05-31 01:09:37 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2012-04-17 15:01:25 +01:00
/*
* Copyright 2012 Red Hat
*
* Authors : Matthew Garrett
* Dave Airlie
*/
2019-06-23 12:35:42 +02:00
2012-04-17 15:01:25 +01:00
# include <linux/console.h>
2019-12-05 10:02:52 +01:00
# include <linux/module.h>
# include <linux/pci.h>
2020-08-07 20:05:47 +02:00
# include <linux/vmalloc.h>
2012-04-17 15:01:25 +01:00
2019-06-23 12:35:42 +02:00
# include <drm/drm_drv.h>
# include <drm/drm_file.h>
# include <drm/drm_ioctl.h>
2012-10-02 18:01:07 +01:00
# include <drm/drm_pciids.h>
2012-04-17 15:01:25 +01:00
2019-06-23 12:35:42 +02:00
# include "mgag200_drv.h"
2020-05-15 10:32:33 +02:00
int mgag200_modeset = - 1 ;
2012-04-17 15:01:25 +01:00
MODULE_PARM_DESC ( modeset , " Disable/Enable modesetting " ) ;
module_param_named ( modeset , mgag200_modeset , int , 0400 ) ;
2020-06-05 15:57:57 +02:00
/*
* DRM driver
*/
DEFINE_DRM_GEM_FOPS ( mgag200_driver_fops ) ;
2020-11-04 11:04:24 +01:00
static const struct drm_driver mgag200_driver = {
2020-06-05 15:57:57 +02:00
. driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET ,
. fops = & mgag200_driver_fops ,
. name = DRIVER_NAME ,
. desc = DRIVER_DESC ,
. date = DRIVER_DATE ,
. major = DRIVER_MAJOR ,
. minor = DRIVER_MINOR ,
. patchlevel = DRIVER_PATCHLEVEL ,
2020-07-30 12:28:37 +02:00
. gem_create_object = drm_gem_shmem_create_object_cached ,
2020-06-05 15:57:57 +02:00
DRM_GEM_SHMEM_DRIVER_OPS ,
} ;
2020-06-05 15:57:59 +02:00
/*
* DRM device
*/
2020-07-30 12:28:38 +02:00
static bool mgag200_has_sgram ( struct mga_device * mdev )
2020-06-05 15:57:59 +02:00
{
2020-06-05 15:58:02 +02:00
struct drm_device * dev = & mdev - > base ;
2020-07-30 12:28:38 +02:00
u32 option ;
int ret ;
2020-06-05 15:57:59 +02:00
2020-07-30 12:28:38 +02:00
ret = pci_read_config_dword ( dev - > pdev , PCI_MGA_OPTION , & option ) ;
if ( drm_WARN ( dev , ret , " failed to read PCI config dword: %d \n " , ret ) )
return false ;
return ! ! ( option & PCI_MGA_OPTION_HARDPWMSK ) ;
}
2020-06-05 15:57:59 +02:00
2020-07-30 12:28:38 +02:00
static int mgag200_regs_init ( struct mga_device * mdev )
{
struct drm_device * dev = & mdev - > base ;
2020-07-30 12:28:39 +02:00
u32 option , option2 ;
2020-07-30 12:28:40 +02:00
u8 crtcext3 ;
2020-07-30 12:28:39 +02:00
switch ( mdev - > type ) {
2020-07-30 12:28:44 +02:00
case G200_PCI :
case G200_AGP :
if ( mgag200_has_sgram ( mdev ) )
option = 0x4049cd21 ;
else
option = 0x40499121 ;
option2 = 0x00008000 ;
break ;
2020-07-30 12:28:39 +02:00
case G200_SE_A :
case G200_SE_B :
2020-08-04 08:51:58 +02:00
option = 0x40049120 ;
2020-07-30 12:28:39 +02:00
if ( mgag200_has_sgram ( mdev ) )
option | = PCI_MGA_OPTION_HARDPWMSK ;
option2 = 0x00008000 ;
break ;
case G200_WB :
case G200_EW3 :
option = 0x41049120 ;
option2 = 0x0000b000 ;
break ;
case G200_EV :
option = 0x00000120 ;
option2 = 0x0000b000 ;
break ;
case G200_EH :
case G200_EH3 :
option = 0x00000120 ;
option2 = 0x0000b000 ;
break ;
default :
option = 0 ;
option2 = 0 ;
}
2020-07-30 12:28:38 +02:00
2020-07-30 12:28:39 +02:00
if ( option )
pci_write_config_dword ( dev - > pdev , PCI_MGA_OPTION , option ) ;
if ( option2 )
pci_write_config_dword ( dev - > pdev , PCI_MGA_OPTION2 , option2 ) ;
2020-06-05 15:57:59 +02:00
2020-07-30 12:28:38 +02:00
/* BAR 1 contains registers */
2020-06-05 15:57:59 +02:00
mdev - > rmmio_base = pci_resource_start ( dev - > pdev , 1 ) ;
mdev - > rmmio_size = pci_resource_len ( dev - > pdev , 1 ) ;
if ( ! devm_request_mem_region ( dev - > dev , mdev - > rmmio_base ,
mdev - > rmmio_size , " mgadrmfb_mmio " ) ) {
drm_err ( dev , " can't reserve mmio registers \n " ) ;
return - ENOMEM ;
}
mdev - > rmmio = pcim_iomap ( dev - > pdev , 1 , 0 ) ;
if ( mdev - > rmmio = = NULL )
return - ENOMEM ;
2020-07-30 12:28:40 +02:00
RREG_ECRT ( 0x03 , crtcext3 ) ;
crtcext3 | = MGAREG_CRTCEXT3_MGAMODE ;
WREG_ECRT ( 0x03 , crtcext3 ) ;
2020-07-30 12:28:38 +02:00
return 0 ;
}
2020-07-30 12:28:44 +02:00
static void mgag200_g200_interpret_bios ( struct mga_device * mdev ,
const unsigned char * bios ,
size_t size )
{
static const char matrox [ ] = { ' M ' , ' A ' , ' T ' , ' R ' , ' O ' , ' X ' } ;
static const unsigned int expected_length [ 6 ] = {
0 , 64 , 64 , 64 , 128 , 128
} ;
struct drm_device * dev = & mdev - > base ;
const unsigned char * pins ;
unsigned int pins_len , version ;
int offset ;
int tmp ;
/* Test for MATROX string. */
if ( size < 45 + sizeof ( matrox ) )
return ;
if ( memcmp ( & bios [ 45 ] , matrox , sizeof ( matrox ) ) ! = 0 )
return ;
/* Get the PInS offset. */
if ( size < MGA_BIOS_OFFSET + 2 )
return ;
offset = ( bios [ MGA_BIOS_OFFSET + 1 ] < < 8 ) | bios [ MGA_BIOS_OFFSET ] ;
/* Get PInS data structure. */
if ( size < offset + 6 )
return ;
pins = bios + offset ;
if ( pins [ 0 ] = = 0x2e & & pins [ 1 ] = = 0x41 ) {
version = pins [ 5 ] ;
pins_len = pins [ 2 ] ;
} else {
version = 1 ;
pins_len = pins [ 0 ] + ( pins [ 1 ] < < 8 ) ;
}
if ( version < 1 | | version > 5 ) {
drm_warn ( dev , " Unknown BIOS PInS version: %d \n " , version ) ;
return ;
}
if ( pins_len ! = expected_length [ version ] ) {
2020-08-26 09:47:27 +01:00
drm_warn ( dev , " Unexpected BIOS PInS size: %d expected: %d \n " ,
2020-07-30 12:28:44 +02:00
pins_len , expected_length [ version ] ) ;
return ;
}
if ( size < offset + pins_len )
return ;
drm_dbg_kms ( dev , " MATROX BIOS PInS version %d size: %d found \n " ,
version , pins_len ) ;
/* Extract the clock values */
switch ( version ) {
case 1 :
tmp = pins [ 24 ] + ( pins [ 25 ] < < 8 ) ;
if ( tmp )
mdev - > model . g200 . pclk_max = tmp * 10 ;
break ;
case 2 :
if ( pins [ 41 ] ! = 0xff )
mdev - > model . g200 . pclk_max = ( pins [ 41 ] + 100 ) * 1000 ;
break ;
case 3 :
if ( pins [ 36 ] ! = 0xff )
mdev - > model . g200 . pclk_max = ( pins [ 36 ] + 100 ) * 1000 ;
if ( pins [ 52 ] & 0x20 )
mdev - > model . g200 . ref_clk = 14318 ;
break ;
case 4 :
if ( pins [ 39 ] ! = 0xff )
mdev - > model . g200 . pclk_max = pins [ 39 ] * 4 * 1000 ;
if ( pins [ 92 ] & 0x01 )
mdev - > model . g200 . ref_clk = 14318 ;
break ;
case 5 :
tmp = pins [ 4 ] ? 8000 : 6000 ;
if ( pins [ 123 ] ! = 0xff )
mdev - > model . g200 . pclk_min = pins [ 123 ] * tmp ;
if ( pins [ 38 ] ! = 0xff )
mdev - > model . g200 . pclk_max = pins [ 38 ] * tmp ;
if ( pins [ 110 ] & 0x01 )
mdev - > model . g200 . ref_clk = 14318 ;
break ;
default :
break ;
}
}
static void mgag200_g200_init_refclk ( struct mga_device * mdev )
{
struct drm_device * dev = & mdev - > base ;
unsigned char __iomem * rom ;
unsigned char * bios ;
size_t size ;
mdev - > model . g200 . pclk_min = 50000 ;
mdev - > model . g200 . pclk_max = 230000 ;
mdev - > model . g200 . ref_clk = 27050 ;
rom = pci_map_rom ( dev - > pdev , & size ) ;
if ( ! rom )
return ;
bios = vmalloc ( size ) ;
if ( ! bios )
goto out ;
memcpy_fromio ( bios , rom , size ) ;
if ( size ! = 0 & & bios [ 0 ] = = 0x55 & & bios [ 1 ] = = 0xaa )
mgag200_g200_interpret_bios ( mdev , bios , size ) ;
drm_dbg_kms ( dev , " pclk_min: %ld pclk_max: %ld ref_clk: %ld \n " ,
mdev - > model . g200 . pclk_min , mdev - > model . g200 . pclk_max ,
mdev - > model . g200 . ref_clk ) ;
vfree ( bios ) ;
out :
pci_unmap_rom ( dev - > pdev , rom ) ;
}
2020-07-30 12:28:43 +02:00
static void mgag200_g200se_init_unique_id ( struct mga_device * mdev )
{
struct drm_device * dev = & mdev - > base ;
/* stash G200 SE model number for later use */
mdev - > model . g200se . unique_rev_id = RREG32 ( 0x1e24 ) ;
drm_dbg ( dev , " G200 SE unique revision id is 0x%x \n " ,
mdev - > model . g200se . unique_rev_id ) ;
}
2020-07-30 12:28:38 +02:00
static int mgag200_device_init ( struct mga_device * mdev , unsigned long flags )
{
struct drm_device * dev = & mdev - > base ;
int ret ;
mdev - > flags = mgag200_flags_from_driver_data ( flags ) ;
mdev - > type = mgag200_type_from_driver_data ( flags ) ;
ret = mgag200_regs_init ( mdev ) ;
if ( ret )
return ret ;
2020-07-30 12:28:44 +02:00
if ( mdev - > type = = G200_PCI | | mdev - > type = = G200_AGP )
mgag200_g200_init_refclk ( mdev ) ;
else if ( IS_G200_SE ( mdev ) )
2020-07-30 12:28:43 +02:00
mgag200_g200se_init_unique_id ( mdev ) ;
2020-06-05 15:57:59 +02:00
ret = mgag200_mm_init ( mdev ) ;
if ( ret )
2020-06-05 15:58:00 +02:00
return ret ;
2020-06-05 15:57:59 +02:00
ret = mgag200_modeset_init ( mdev ) ;
if ( ret ) {
drm_err ( dev , " Fatal error during modeset init: %d \n " , ret ) ;
2020-06-05 15:58:00 +02:00
return ret ;
2020-06-05 15:57:59 +02:00
}
return 0 ;
2020-06-05 15:58:00 +02:00
}
2020-06-05 15:58:01 +02:00
static struct mga_device *
2020-06-05 15:58:02 +02:00
mgag200_device_create ( struct pci_dev * pdev , unsigned long flags )
2020-06-05 15:58:00 +02:00
{
2020-06-05 15:58:01 +02:00
struct drm_device * dev ;
2020-06-05 15:58:00 +02:00
struct mga_device * mdev ;
int ret ;
2020-06-05 15:58:03 +02:00
mdev = devm_drm_dev_alloc ( & pdev - > dev , & mgag200_driver ,
struct mga_device , base ) ;
if ( IS_ERR ( mdev ) )
return mdev ;
2020-06-05 15:58:02 +02:00
dev = & mdev - > base ;
2020-06-05 15:58:01 +02:00
dev - > pdev = pdev ;
pci_set_drvdata ( pdev , dev ) ;
2020-06-05 15:58:00 +02:00
ret = mgag200_device_init ( mdev , flags ) ;
if ( ret )
2020-06-05 15:58:03 +02:00
return ERR_PTR ( ret ) ;
2020-06-05 15:58:00 +02:00
2020-06-05 15:58:01 +02:00
return mdev ;
2020-06-05 15:57:59 +02:00
}
2020-06-05 15:57:57 +02:00
/*
* PCI driver
*/
2012-04-17 15:01:25 +01:00
2020-06-05 15:57:58 +02:00
static const struct pci_device_id mgag200_pciidlist [ ] = {
2020-07-30 12:28:44 +02:00
{ PCI_VENDOR_ID_MATROX , 0x520 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , G200_PCI } ,
{ PCI_VENDOR_ID_MATROX , 0x521 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , G200_AGP } ,
2019-12-06 09:19:01 +01:00
{ PCI_VENDOR_ID_MATROX , 0x522 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 ,
2019-11-26 11:15:29 +01:00
G200_SE_A | MGAG200_FLAG_HW_BUG_NO_STARTADD } ,
2012-04-17 15:01:25 +01:00
{ PCI_VENDOR_ID_MATROX , 0x524 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , G200_SE_B } ,
{ PCI_VENDOR_ID_MATROX , 0x530 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , G200_EV } ,
{ PCI_VENDOR_ID_MATROX , 0x532 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , G200_WB } ,
{ PCI_VENDOR_ID_MATROX , 0x533 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , G200_EH } ,
{ PCI_VENDOR_ID_MATROX , 0x534 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , G200_ER } ,
2015-08-21 09:24:05 -04:00
{ PCI_VENDOR_ID_MATROX , 0x536 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , G200_EW3 } ,
2016-10-21 12:47:07 -04:00
{ PCI_VENDOR_ID_MATROX , 0x538 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , G200_EH3 } ,
2012-04-17 15:01:25 +01:00
{ 0 , }
} ;
2020-06-05 15:57:58 +02:00
MODULE_DEVICE_TABLE ( pci , mgag200_pciidlist ) ;
2012-04-17 15:01:25 +01:00
2020-06-05 15:57:58 +02:00
static int
mgag200_pci_probe ( struct pci_dev * pdev , const struct pci_device_id * ent )
2012-04-17 15:01:25 +01:00
{
2020-06-05 15:58:01 +02:00
struct mga_device * mdev ;
2019-12-05 10:02:52 +01:00
struct drm_device * dev ;
int ret ;
2019-08-22 11:06:44 +02:00
drm_fb_helper_remove_conflicting_pci_framebuffers ( pdev , " mgag200drmfb " ) ;
2012-06-01 11:12:39 +01:00
2020-06-05 15:57:52 +02:00
ret = pcim_enable_device ( pdev ) ;
2019-12-05 10:02:52 +01:00
if ( ret )
return ret ;
2020-06-05 15:58:02 +02:00
mdev = mgag200_device_create ( pdev , ent - > driver_data ) ;
2020-06-05 15:58:03 +02:00
if ( IS_ERR ( mdev ) )
return PTR_ERR ( mdev ) ;
2020-06-05 15:58:02 +02:00
dev = & mdev - > base ;
2019-12-05 10:02:52 +01:00
ret = drm_dev_register ( dev , ent - > driver_data ) ;
if ( ret )
2020-06-05 15:58:03 +02:00
return ret ;
2019-12-05 10:02:52 +01:00
2020-04-08 10:26:37 +02:00
drm_fbdev_generic_setup ( dev , 0 ) ;
2019-12-05 10:02:52 +01:00
return 0 ;
2012-04-17 15:01:25 +01:00
}
2020-06-05 15:57:58 +02:00
static void mgag200_pci_remove ( struct pci_dev * pdev )
2012-04-17 15:01:25 +01:00
{
struct drm_device * dev = pci_get_drvdata ( pdev ) ;
2019-12-05 10:02:52 +01:00
drm_dev_unregister ( dev ) ;
2012-04-17 15:01:25 +01:00
}
static struct pci_driver mgag200_pci_driver = {
. name = DRIVER_NAME ,
2020-06-05 15:57:58 +02:00
. id_table = mgag200_pciidlist ,
. probe = mgag200_pci_probe ,
. remove = mgag200_pci_remove ,
2012-04-17 15:01:25 +01:00
} ;
static int __init mgag200_init ( void )
{
if ( vgacon_text_force ( ) & & mgag200_modeset = = - 1 )
return - EINVAL ;
if ( mgag200_modeset = = 0 )
return - EINVAL ;
2017-05-24 16:51:40 +02:00
return pci_register_driver ( & mgag200_pci_driver ) ;
2012-04-17 15:01:25 +01:00
}
static void __exit mgag200_exit ( void )
{
2017-05-24 16:51:40 +02:00
pci_unregister_driver ( & mgag200_pci_driver ) ;
2012-04-17 15:01:25 +01:00
}
module_init ( mgag200_init ) ;
module_exit ( mgag200_exit ) ;
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;