2005-04-16 15:20:36 -07:00
/**
* \ file drm_stub . h
* Stub support
*
* \ author Rickard E . ( Rik ) Faith < faith @ valinux . com >
*/
/*
* Created : Fri Jan 19 10 : 48 : 35 2001 by faith @ acm . org
*
* Copyright 2001 VA Linux Systems , Inc . , Sunnyvale , California .
* All Rights Reserved .
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice ( including the next
* paragraph ) shall be included in all copies or substantial portions of the
* Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* PRECISION INSIGHT AND / OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM , DAMAGES OR
* OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE ,
* ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE .
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include "drmP.h"
# include "drm_core.h"
2005-09-25 14:28:13 +10:00
unsigned int drm_debug = 0 ; /* 1 to enable debug output */
2005-04-16 15:20:36 -07:00
EXPORT_SYMBOL ( drm_debug ) ;
2005-09-25 14:28:13 +10:00
MODULE_AUTHOR ( CORE_AUTHOR ) ;
MODULE_DESCRIPTION ( CORE_DESC ) ;
2005-04-16 15:20:36 -07:00
MODULE_LICENSE ( " GPL and additional rights " ) ;
MODULE_PARM_DESC ( debug , " Enable debug output " ) ;
2005-10-03 15:02:20 -04:00
module_param_named ( debug , drm_debug , int , 0600 ) ;
2005-04-16 15:20:36 -07:00
2008-04-21 16:47:32 +10:00
struct idr drm_minors_idr ;
2006-01-20 14:08:59 -08:00
struct class * drm_class ;
2005-04-16 15:20:36 -07:00
struct proc_dir_entry * drm_proc_root ;
2009-02-17 20:08:49 -05:00
struct dentry * drm_debugfs_root ;
2005-04-16 15:20:36 -07:00
2008-04-21 16:47:32 +10:00
static int drm_minor_get_id ( struct drm_device * dev , int type )
{
int new_id ;
int ret ;
int base = 0 , limit = 63 ;
2008-11-07 14:05:41 -08:00
if ( type = = DRM_MINOR_CONTROL ) {
base + = 64 ;
limit = base + 127 ;
} else if ( type = = DRM_MINOR_RENDER ) {
base + = 128 ;
limit = base + 255 ;
}
2008-04-21 16:47:32 +10:00
again :
if ( idr_pre_get ( & drm_minors_idr , GFP_KERNEL ) = = 0 ) {
DRM_ERROR ( " Out of memory expanding drawable idr \n " ) ;
return - ENOMEM ;
}
mutex_lock ( & dev - > struct_mutex ) ;
ret = idr_get_new_above ( & drm_minors_idr , NULL ,
base , & new_id ) ;
mutex_unlock ( & dev - > struct_mutex ) ;
if ( ret = = - EAGAIN ) {
goto again ;
} else if ( ret ) {
return ret ;
}
if ( new_id > = limit ) {
idr_remove ( & drm_minors_idr , new_id ) ;
return - EINVAL ;
}
return new_id ;
}
2008-11-28 14:22:24 +10:00
struct drm_master * drm_master_create ( struct drm_minor * minor )
{
struct drm_master * master ;
master = drm_calloc ( 1 , sizeof ( * master ) , DRM_MEM_DRIVER ) ;
if ( ! master )
return NULL ;
kref_init ( & master - > refcount ) ;
spin_lock_init ( & master - > lock . spinlock ) ;
init_waitqueue_head ( & master - > lock . lock_queue ) ;
drm_ht_create ( & master - > magiclist , DRM_MAGIC_HASH_ORDER ) ;
INIT_LIST_HEAD ( & master - > magicfree ) ;
master - > minor = minor ;
list_add_tail ( & master - > head , & minor - > master_list ) ;
return master ;
}
struct drm_master * drm_master_get ( struct drm_master * master )
{
kref_get ( & master - > refcount ) ;
return master ;
}
static void drm_master_destroy ( struct kref * kref )
{
struct drm_master * master = container_of ( kref , struct drm_master , refcount ) ;
struct drm_magic_entry * pt , * next ;
struct drm_device * dev = master - > minor - > dev ;
2009-01-19 17:17:58 +10:00
struct drm_map_list * r_list , * list_temp ;
2008-11-28 14:22:24 +10:00
list_del ( & master - > head ) ;
if ( dev - > driver - > master_destroy )
dev - > driver - > master_destroy ( dev , master ) ;
2009-01-19 17:17:58 +10:00
list_for_each_entry_safe ( r_list , list_temp , & dev - > maplist , head ) {
if ( r_list - > master = = master ) {
drm_rmmap_locked ( dev , r_list - > map ) ;
r_list = NULL ;
}
}
2008-11-28 14:22:24 +10:00
if ( master - > unique ) {
2008-12-02 13:38:47 +10:00
drm_free ( master - > unique , master - > unique_size , DRM_MEM_DRIVER ) ;
2008-11-28 14:22:24 +10:00
master - > unique = NULL ;
master - > unique_len = 0 ;
}
list_for_each_entry_safe ( pt , next , & master - > magicfree , head ) {
list_del ( & pt - > head ) ;
drm_ht_remove_item ( & master - > magiclist , & pt - > hash_item ) ;
drm_free ( pt , sizeof ( * pt ) , DRM_MEM_MAGIC ) ;
}
drm_ht_remove ( & master - > magiclist ) ;
drm_free ( master , sizeof ( * master ) , DRM_MEM_DRIVER ) ;
}
void drm_master_put ( struct drm_master * * master )
{
kref_put ( & ( * master ) - > refcount , drm_master_destroy ) ;
* master = NULL ;
}
int drm_setmaster_ioctl ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
2009-04-16 09:00:02 +02:00
if ( file_priv - > is_master )
return 0 ;
2008-11-28 14:22:24 +10:00
if ( file_priv - > minor - > master & & file_priv - > minor - > master ! = file_priv - > master )
return - EINVAL ;
if ( ! file_priv - > master )
return - EINVAL ;
if ( ! file_priv - > minor - > master & &
file_priv - > minor - > master ! = file_priv - > master ) {
mutex_lock ( & dev - > struct_mutex ) ;
file_priv - > minor - > master = drm_master_get ( file_priv - > master ) ;
2009-04-16 09:00:02 +02:00
file_priv - > is_master = 1 ;
2009-03-04 21:49:14 +10:00
mutex_unlock ( & dev - > struct_mutex ) ;
2008-11-28 14:22:24 +10:00
}
return 0 ;
}
int drm_dropmaster_ioctl ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
2009-04-16 09:00:02 +02:00
if ( ! file_priv - > is_master )
2008-11-28 14:22:24 +10:00
return - EINVAL ;
2009-04-16 09:00:02 +02:00
2009-04-20 09:32:50 +10:00
if ( ! file_priv - > minor - > master )
return - EINVAL ;
2008-11-28 14:22:24 +10:00
mutex_lock ( & dev - > struct_mutex ) ;
drm_master_put ( & file_priv - > minor - > master ) ;
2009-04-16 09:00:02 +02:00
file_priv - > is_master = 0 ;
2008-11-28 14:22:24 +10:00
mutex_unlock ( & dev - > struct_mutex ) ;
return 0 ;
}
2007-07-11 15:53:27 +10:00
static int drm_fill_in_dev ( struct drm_device * dev , struct pci_dev * pdev ,
2005-09-25 14:28:13 +10:00
const struct pci_device_id * ent ,
struct drm_driver * driver )
2005-04-16 15:20:36 -07:00
{
int retcode ;
2007-05-26 05:01:51 +10:00
INIT_LIST_HEAD ( & dev - > filelist ) ;
2007-07-11 12:05:36 +10:00
INIT_LIST_HEAD ( & dev - > ctxlist ) ;
INIT_LIST_HEAD ( & dev - > vmalist ) ;
INIT_LIST_HEAD ( & dev - > maplist ) ;
2005-04-16 15:20:36 -07:00
spin_lock_init ( & dev - > count_lock ) ;
2006-10-24 23:04:19 +10:00
spin_lock_init ( & dev - > drw_lock ) ;
2005-09-25 14:28:13 +10:00
init_timer ( & dev - > timer ) ;
2006-02-02 19:37:46 +11:00
mutex_init ( & dev - > struct_mutex ) ;
mutex_init ( & dev - > ctxlist_mutex ) ;
2005-04-16 15:20:36 -07:00
2007-07-17 14:20:07 +10:00
idr_init ( & dev - > drw_idr ) ;
2005-09-25 14:28:13 +10:00
dev - > pdev = pdev ;
2006-09-22 04:19:34 +10:00
dev - > pci_device = pdev - > device ;
dev - > pci_vendor = pdev - > vendor ;
2005-04-16 15:20:36 -07:00
# ifdef __alpha__
2005-09-25 14:28:13 +10:00
dev - > hose = pdev - > sysdata ;
2005-04-16 15:20:36 -07:00
# endif
2006-08-07 22:36:47 +10:00
if ( drm_ht_create ( & dev - > map_hash , 12 ) ) {
return - ENOMEM ;
}
2005-07-10 19:27:04 +10:00
2005-04-16 15:20:36 -07:00
/* the DRM has 6 basic counters */
dev - > counters = 6 ;
2005-09-25 14:28:13 +10:00
dev - > types [ 0 ] = _DRM_STAT_LOCK ;
dev - > types [ 1 ] = _DRM_STAT_OPENS ;
dev - > types [ 2 ] = _DRM_STAT_CLOSES ;
dev - > types [ 3 ] = _DRM_STAT_IOCTLS ;
dev - > types [ 4 ] = _DRM_STAT_LOCKS ;
dev - > types [ 5 ] = _DRM_STAT_UNLOCKS ;
2005-04-16 15:20:36 -07:00
dev - > driver = driver ;
2005-09-25 14:28:13 +10:00
2005-04-16 15:20:36 -07:00
if ( drm_core_has_AGP ( dev ) ) {
2005-07-10 17:31:26 +10:00
if ( drm_device_is_agp ( dev ) )
dev - > agp = drm_agp_init ( dev ) ;
2005-09-25 14:28:13 +10:00
if ( drm_core_check_feature ( dev , DRIVER_REQUIRE_AGP )
& & ( dev - > agp = = NULL ) ) {
DRM_ERROR ( " Cannot initialize the agpgart module. \n " ) ;
2005-04-16 15:20:36 -07:00
retcode = - EINVAL ;
goto error_out_unreg ;
}
if ( drm_core_has_MTRR ( dev ) ) {
if ( dev - > agp )
2005-09-25 14:28:13 +10:00
dev - > agp - > agp_mtrr =
mtrr_add ( dev - > agp - > agp_info . aper_base ,
dev - > agp - > agp_info . aper_size *
1024 * 1024 , MTRR_TYPE_WRCOMB , 1 ) ;
2005-04-16 15:20:36 -07:00
}
}
2007-11-22 18:23:13 +10:00
2005-09-25 14:28:13 +10:00
retcode = drm_ctxbitmap_init ( dev ) ;
if ( retcode ) {
DRM_ERROR ( " Cannot allocate memory for context bitmap. \n " ) ;
2005-04-16 15:20:36 -07:00
goto error_out_unreg ;
}
2008-07-30 12:06:12 -07:00
if ( driver - > driver_features & DRIVER_GEM ) {
retcode = drm_gem_init ( dev ) ;
if ( retcode ) {
DRM_ERROR ( " Cannot initialize graphics execution "
" manager (GEM) \n " ) ;
goto error_out_unreg ;
}
}
2005-04-16 15:20:36 -07:00
return 0 ;
2005-09-25 14:28:13 +10:00
error_out_unreg :
2005-11-10 22:16:34 +11:00
drm_lastclose ( dev ) ;
2005-04-16 15:20:36 -07:00
return retcode ;
}
/**
* Get a secondary minor number .
*
* \ param dev device data structure
* \ param sec - minor structure to hold the assigned minor
* \ return negative number on failure .
*
* Search an empty entry and initialize it to the given parameters , and
* create the proc init entry via proc_init ( ) . This routines assigns
* minor numbers to secondary heads of multi - headed cards
*/
2008-04-21 16:47:32 +10:00
static int drm_get_minor ( struct drm_device * dev , struct drm_minor * * minor , int type )
2005-04-16 15:20:36 -07:00
{
2008-04-21 16:47:32 +10:00
struct drm_minor * new_minor ;
2005-04-16 15:20:36 -07:00
int ret ;
2008-04-21 16:47:32 +10:00
int minor_id ;
2005-04-16 15:20:36 -07:00
DRM_DEBUG ( " \n " ) ;
2008-04-21 16:47:32 +10:00
minor_id = drm_minor_get_id ( dev , type ) ;
if ( minor_id < 0 )
return minor_id ;
new_minor = kzalloc ( sizeof ( struct drm_minor ) , GFP_KERNEL ) ;
if ( ! new_minor ) {
ret = - ENOMEM ;
goto err_idr ;
}
new_minor - > type = type ;
new_minor - > device = MKDEV ( DRM_MAJOR , minor_id ) ;
new_minor - > dev = dev ;
new_minor - > index = minor_id ;
2008-11-28 14:22:24 +10:00
INIT_LIST_HEAD ( & new_minor - > master_list ) ;
2008-04-21 16:47:32 +10:00
idr_replace ( & drm_minors_idr , new_minor , minor_id ) ;
if ( type = = DRM_MINOR_LEGACY ) {
ret = drm_proc_init ( new_minor , minor_id , drm_proc_root ) ;
if ( ret ) {
DRM_ERROR ( " DRM: Failed to initialize /proc/dri. \n " ) ;
goto err_mem ;
2005-04-16 15:20:36 -07:00
}
2008-04-21 16:47:32 +10:00
} else
2009-02-17 20:08:49 -05:00
new_minor - > proc_root = NULL ;
# if defined(CONFIG_DEBUG_FS)
ret = drm_debugfs_init ( new_minor , minor_id , drm_debugfs_root ) ;
if ( ret ) {
DRM_ERROR ( " DRM: Failed to initialize /debugfs/dri. \n " ) ;
goto err_g2 ;
}
# endif
2008-04-21 16:47:32 +10:00
ret = drm_sysfs_device_add ( new_minor ) ;
if ( ret ) {
printk ( KERN_ERR
" DRM: Error sysfs_device_add. \n " ) ;
goto err_g2 ;
2005-04-16 15:20:36 -07:00
}
2008-04-21 16:47:32 +10:00
* minor = new_minor ;
DRM_DEBUG ( " new minor assigned %d \n " , minor_id ) ;
return 0 ;
err_g2 :
if ( new_minor - > type = = DRM_MINOR_LEGACY )
drm_proc_cleanup ( new_minor , drm_proc_root ) ;
err_mem :
kfree ( new_minor ) ;
err_idr :
idr_remove ( & drm_minors_idr , minor_id ) ;
* minor = NULL ;
2005-04-16 15:20:36 -07:00
return ret ;
}
2005-09-25 14:28:13 +10:00
2005-07-07 21:03:38 +10:00
/**
* Register .
*
* \ param pdev - PCI device structure
* \ param ent entry from the PCI ID table with device type flags
* \ return zero on success or a negative number on failure .
*
* Attempt to gets inter module " drm " information . If we are first
* then register the character device and inter module information .
* Try and register , if we fail to register , backout previous work .
*/
int drm_get_dev ( struct pci_dev * pdev , const struct pci_device_id * ent ,
2005-09-25 14:28:13 +10:00
struct drm_driver * driver )
2005-07-07 21:03:38 +10:00
{
2007-07-11 15:53:27 +10:00
struct drm_device * dev ;
2005-07-07 21:03:38 +10:00
int ret ;
DRM_DEBUG ( " \n " ) ;
dev = drm_calloc ( 1 , sizeof ( * dev ) , DRM_MEM_STUB ) ;
if ( ! dev )
return - ENOMEM ;
2006-12-09 10:50:22 +11:00
ret = pci_enable_device ( pdev ) ;
if ( ret )
goto err_g1 ;
2005-07-07 21:03:38 +10:00
2008-02-07 14:48:32 +10:00
pci_set_master ( pdev ) ;
2005-07-07 21:03:38 +10:00
if ( ( ret = drm_fill_in_dev ( dev , pdev , ent , driver ) ) ) {
printk ( KERN_ERR " DRM: Fill_in_dev failed. \n " ) ;
2006-12-09 10:50:22 +11:00
goto err_g2 ;
2005-07-07 21:03:38 +10:00
}
2008-11-07 14:05:41 -08:00
if ( drm_core_check_feature ( dev , DRIVER_MODESET ) ) {
2009-01-04 16:55:33 -05:00
pci_set_drvdata ( pdev , dev ) ;
2008-11-07 14:05:41 -08:00
ret = drm_get_minor ( dev , & dev - > control , DRM_MINOR_CONTROL ) ;
if ( ret )
goto err_g2 ;
}
2008-04-21 16:47:32 +10:00
if ( ( ret = drm_get_minor ( dev , & dev - > primary , DRM_MINOR_LEGACY ) ) )
2008-11-07 14:05:41 -08:00
goto err_g3 ;
2007-11-05 12:50:58 +10:00
2008-11-07 14:05:41 -08:00
if ( dev - > driver - > load ) {
ret = dev - > driver - > load ( dev , ent - > driver_data ) ;
if ( ret )
2008-12-07 12:02:21 +10:00
goto err_g3 ;
2008-11-07 14:05:41 -08:00
}
/* setup the grouping for the legacy output */
if ( drm_core_check_feature ( dev , DRIVER_MODESET ) ) {
ret = drm_mode_group_init_legacy_group ( dev , & dev - > primary - > mode_group ) ;
if ( ret )
goto err_g3 ;
}
2008-12-07 12:02:21 +10:00
2008-11-28 13:43:47 +10:00
list_add_tail ( & dev - > driver_item , & driver - > device_list ) ;
2009-02-06 16:46:27 +11:00
DRM_INFO ( " Initialized %s %d.%d.%d %s for %s on minor %d \n " ,
2005-11-10 22:16:34 +11:00
driver - > name , driver - > major , driver - > minor , driver - > patchlevel ,
2009-02-06 16:46:27 +11:00
driver - > date , pci_name ( pdev ) , dev - > primary - > index ) ;
2005-07-07 21:03:38 +10:00
return 0 ;
2008-12-07 12:02:21 +10:00
err_g3 :
drm_put_minor ( & dev - > primary ) ;
2006-12-09 10:50:22 +11:00
err_g2 :
pci_disable_device ( pdev ) ;
err_g1 :
2005-07-07 21:03:38 +10:00
drm_free ( dev , sizeof ( * dev ) , DRM_MEM_STUB ) ;
return ret ;
}
2009-01-04 16:55:33 -05:00
EXPORT_SYMBOL ( drm_get_dev ) ;
2005-04-16 15:20:36 -07:00
/**
* Put a secondary minor number .
*
* \ param sec_minor - structure to be released
* \ return always zero
*
* Cleans up the proc resources . Not legal for this to be the
* last minor released .
*
*/
2008-04-21 16:47:32 +10:00
int drm_put_minor ( struct drm_minor * * minor_p )
2005-04-16 15:20:36 -07:00
{
2008-04-21 16:47:32 +10:00
struct drm_minor * minor = * minor_p ;
2008-07-30 12:06:12 -07:00
2008-04-21 16:47:32 +10:00
DRM_DEBUG ( " release secondary minor %d \n " , minor - > index ) ;
2005-09-25 14:28:13 +10:00
2008-04-21 16:47:32 +10:00
if ( minor - > type = = DRM_MINOR_LEGACY )
drm_proc_cleanup ( minor , drm_proc_root ) ;
2009-02-17 20:08:49 -05:00
# if defined(CONFIG_DEBUG_FS)
drm_debugfs_cleanup ( minor ) ;
# endif
2008-04-21 16:47:32 +10:00
drm_sysfs_device_remove ( minor ) ;
2005-04-16 15:20:36 -07:00
2008-04-21 16:47:32 +10:00
idr_remove ( & drm_minors_idr , minor - > index ) ;
2005-09-25 14:28:13 +10:00
2008-04-21 16:47:32 +10:00
kfree ( minor ) ;
* minor_p = NULL ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2009-01-04 16:55:33 -05:00
/**
* Called via drm_exit ( ) at module unload time or when pci device is
* unplugged .
*
* Cleans up all DRM device , calling drm_lastclose ( ) .
*
* \ sa drm_init
*/
void drm_put_dev ( struct drm_device * dev )
{
struct drm_driver * driver = dev - > driver ;
struct drm_map_list * r_list , * list_temp ;
DRM_DEBUG ( " \n " ) ;
if ( ! dev ) {
DRM_ERROR ( " cleanup called no dev \n " ) ;
return ;
}
drm_vblank_cleanup ( dev ) ;
drm_lastclose ( dev ) ;
if ( drm_core_has_MTRR ( dev ) & & drm_core_has_AGP ( dev ) & &
dev - > agp & & dev - > agp - > agp_mtrr > = 0 ) {
int retval ;
retval = mtrr_del ( dev - > agp - > agp_mtrr ,
dev - > agp - > agp_info . aper_base ,
dev - > agp - > agp_info . aper_size * 1024 * 1024 ) ;
DRM_DEBUG ( " mtrr_del=%d \n " , retval ) ;
}
if ( dev - > driver - > unload )
dev - > driver - > unload ( dev ) ;
if ( drm_core_has_AGP ( dev ) & & dev - > agp ) {
drm_free ( dev - > agp , sizeof ( * dev - > agp ) , DRM_MEM_AGPLISTS ) ;
dev - > agp = NULL ;
}
drm_ht_remove ( & dev - > map_hash ) ;
drm_ctxbitmap_cleanup ( dev ) ;
list_for_each_entry_safe ( r_list , list_temp , & dev - > maplist , head )
drm_rmmap ( dev , r_list - > map ) ;
if ( drm_core_check_feature ( dev , DRIVER_MODESET ) )
drm_put_minor ( & dev - > control ) ;
if ( driver - > driver_features & DRIVER_GEM )
drm_gem_destroy ( dev ) ;
drm_put_minor ( & dev - > primary ) ;
if ( dev - > devname ) {
drm_free ( dev - > devname , strlen ( dev - > devname ) + 1 ,
DRM_MEM_DRIVER ) ;
dev - > devname = NULL ;
}
drm_free ( dev , sizeof ( * dev ) , DRM_MEM_STUB ) ;
}
EXPORT_SYMBOL ( drm_put_dev ) ;