/***************************************************************************************\

File Name:      mtx_drv.h

Description:    Main header file for MTX kernel module.

References:     None.

Author:         Karl Lessard    <klessard@matrox.com>

    Copyright (c) 2001, Matrox Graphics Inc.
    All Rights Reserved.

\***************************************************************************************/

#ifndef INC_MTXDRV_H
#define INC_MTXDRV_H

#ifdef __KERNEL__ 

struct mtx_core_driver;

/*******************************************
 *
 * Included headers
 *
 ******************************************/

#ifndef __cplusplus
#include <linux/config.h>
#include <linux/version.h>

#ifndef LINUX_VERSION_CODE
  #define LINUX_VERSION_CODE KERNEL_VERSION_CODE
#endif


/* A 2.5 kernel is considered as a 2.6, but there's no guarantees it will work */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)
#  error 
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
#  define KERNEL_2_4
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 7, 0)
#  define KERNEL_2_6
#endif

#include <linux/types.h>
#include <linux/init.h>
#include <linux/module.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 0) 
#include <linux/moduleparam.h>
#endif
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/fs.h> 
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/agp_backend.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <asm/mtrr.h>
#include <asm/uaccess.h>

#endif /* #ifndef __cplusplus */

#include "MtxTypesLnx.h"
#include "mtx_agp_flags.h"

#include "MtxCpu.h"
#include "MtxMem.h"
#include "MtxCs.h"

#include "mtx_os.h"
#include "mtx_strintf.h"
#include "mtx_client.h"
#include "mtx_ioctl.h"
#include "mtx_coreintf.h"
#include "mtx_mem.h"

#ifndef __cplusplus


#ifdef KERNEL_2_6

#define MOD_CAN_QUERY(mod)      1
#define MemReserve(page)        SetPageReserved(page)
#define MemUnreserve(page)      ClearPageReserved(page)
#define ValidPage(page)         pfn_valid( page_to_pfn( page ))

#else

#define MemReserve(page)        mem_map_reserve(page)
#define MemUnreserve(page)      mem_map_unreserve(page)
#define ValidPage(page)         VALID_PAGE(page)

#endif

/* Special macros for linux kernel 2.6.10 and upwards */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10))

    #define MTX_AGPGART_BACKEND_ACQUIRE(o) agp_backend_acquire()
    #define MTX_AGPGART_BACKEND_ENABLE(o,mode) agp_enable(mode)
    #define MTX_AGPGART_BACKEND_RELEASE(o) agp_backend_release()
    #define MTX_AGPGART_COPY_INFO(o,p) agp_copy_info(p)
    #define MTX_AGPGART_ALLOCATE_MEMORY(o,count,type) agp_allocate_memory(count,type)
    #define MTX_AGPGART_FREE_MEMORY(o,p) agp_free_memory(p)
    #define MTX_AGPGART_BIND_MEMORY(o,p,offset) agp_bind_memory(p,offset)
    #define MTX_AGPGART_UNBIND_MEMORY(o,p) agp_unbind_memory(p)

#else

    #define AGPBACKEND
    
    #define MTX_AGPGART_BACKEND_ACQUIRE(o) (o)->acquire() 
    #define MTX_AGPGART_BACKEND_ENABLE(o,mode) (o)->enable(mode)
    #define MTX_AGPGART_BACKEND_RELEASE(o) (o) ->release()
    #define MTX_AGPGART_COPY_INFO(o,p) (o)->copy_info(p)
    #define MTX_AGPGART_ALLOCATE_MEMORY(o,count,type) (o)->allocate_memory(count,type)
    #define MTX_AGPGART_FREE_MEMORY(o,p) (o)->free_memory(p)
    #define MTX_AGPGART_BIND_MEMORY(o,p,offset) (o)->bind_memory(p,offset)
    #define MTX_AGPGART_UNBIND_MEMORY(o,p) (o)->unbind_memory(p)
#endif

/******************************************
 *
 * Constants and types
 *
 *****************************************/

/* PCI ids */
#ifndef PCI_DEVICE_ID_PSERIES_FAMILY_A
#define PCI_DEVICE_ID_PSERIES_FAMILY_A   0x0527L
#endif
#ifndef PCI_DEVICE_ID_PSERIES_FAMILY_B
#define PCI_DEVICE_ID_PSERIES_FAMILY_B   0x0528L
#endif
#ifndef PCI_DEVICE_ID_PSERIES_FAMILY_C
#define PCI_DEVICE_ID_PSERIES_FAMILY_C   0x2537L
#endif
#ifndef PCI_DEVICE_ID_PSERIES_FAMILY_D
#define PCI_DEVICE_ID_PSERIES_FAMILY_D   0x2538L
#endif
#ifndef PCI_DEVICE_ID_PSERIES_FAMILY_E
#define PCI_DEVICE_ID_PSERIES_FAMILY_E   0x0529L
#endif


#define MTX_PCI_DEVICE_ID(id, subid)                                \
    { PCI_VENDOR_ID_MATROX,             /* vendor id */             \
      id,                               /* device id */             \
      PCI_ANY_ID,                       /* vendor subid */          \
      subid,                            /* device subid */          \
      (PCI_CLASS_DISPLAY_VGA << 8),     /* device class */          \
      0xffffff00,                       /* class mask */            \
      0 }                               /* private_data */

/* Matrox PCI indexes */
#define MTX_PCI_MGABASE2                    0x10L
#define MTX_PCI_MGABASE1                    0x14L
#define MTX_PCI_MGABASE3                    0x18L
#define MTX_PCI_SUBSYSID_RO                 0x2CL
#define MTX_PCI_OPTION                      0x40L
#define MTX_PCI_MGAINDEX                    0x44L
#define MTX_PCI_MGADATA                     0x48L
#define MTX_PCI_SUBSYSID_WO                 0x4CL
#define MTX_PCI_OPTION2                     0x50L
#define MTX_PCI_OPTION3                     0x54L
#define MTX_PCI_PM_IDENT                    0xDCL
#define MTX_PCI_PM_CSR                      0xE0L
#define MTX_PCI_AGP_IDENT                   0xF0L
#define MTX_PCI_AGP_STS                     0xF4L
#define MTX_PCI_AGP_CMD                     0xF8L

/* Matrox PCI options */
#define MTX_PCI_OPTION_SYSCLKDIS_SHIFT           2
#define MTX_PCI_OPTION_SYSCLKDIS_MASK            (1L << MTX_PCI_OPTION_SYSCLKDIS_SHIFT)
#define MTX_PCI_OPTION_SYSCLKDIS_DIS             (1L << MTX_PCI_OPTION_SYSCLKDIS_SHIFT)
#define MTX_PCI_OPTION_SYSPLLPDN_SHIFT           5
#define MTX_PCI_OPTION_SYSPLLPDN_MASK            (1L << MTX_PCI_OPTION_SYSPLLPDN_SHIFT)
#define MTX_PCI_OPTION_PLLSEL_SHIFT              6
#define MTX_PCI_OPTION_PLLSEL_MASK               (1L << MTX_PCI_OPTION_PLLSEL_SHIFT)
#define MTX_PCI_OPTION_VGAIOEN_SHIFT             8
#define MTX_PCI_OPTION_VGAIOEN_MASK              (1L << MTX_PCI_OPTION_VGAIOEN_SHIFT)
#define MTX_PCI_OPTION_MBLKTYPE_SHIFT            9
#define MTX_PCI_OPTION_MBLKTYPE_MASK             (1L << MTX_PCI_OPTION_MBLKTYPE_SHIFT)
#define MTX_PCI_OPTION_MEMCONFIG_SHIFT           10
#define MTX_PCI_OPTION_MEMCONFIG_MASK            (7L << MTX_PCI_OPTION_MEMCONFIG_SHIFT)
#define MTX_PCI_OPTION_MDSFEN_SHIFT              13
#define MTX_PCI_OPTION_MDSFEN_MASK               (1L << MTX_PCI_OPTION_MDSFEN_SHIFT)
#define MTX_PCI_OPTION_HARDPWMSK_SHIFT           14
#define MTX_PCI_OPTION_HARDPWMSK_MASK            (1L << MTX_PCI_OPTION_HARDPWMSK_SHIFT)
#define MTX_PCI_OPTION_RFHCNT_SHIFT              15
#define MTX_PCI_OPTION_RFHCNT_MASK               (0x3fL << MTX_PCI_OPTION_RFHCNT_SHIFT)
#define MTX_PCI_OPTION_MDSREN_SHIFT              21
#define MTX_PCI_OPTION_MDSREN_MASK               (1L << MTX_PCI_OPTION_MDSREN_SHIFT)
#define MTX_PCI_OPTION_ENHMEMACC_SHIFT           22
#define MTX_PCI_OPTION_ENHMEMACC_MASK            (1L << MTX_PCI_OPTION_ENHMEMACC_SHIFT)
#define MTX_PCI_OPTION_NOHIREQ_SHIFT             28
#define MTX_PCI_OPTION_NOHIREQ_MASK              (1L << MTX_PCI_OPTION_NOHIREQ_SHIFT)
#define MTX_PCI_OPTION_NORETRY_SHIFT             29
#define MTX_PCI_OPTION_NORETRY_MASK              (1L << MTX_PCI_OPTION_NORETRY_SHIFT)
#define MTX_PCI_OPTION_BIOSEN_SHIFT              30
#define MTX_PCI_OPTION_BIOSEN_MASK               (1L << MTX_PCI_OPTION_BIOSEN_SHIFT)
#define MTX_PCI_OPTION_POWERPC_SHIFT             31
#define MTX_PCI_OPTION_POWERPC_MASK              (1L << MTX_PCI_OPTION_POWERPC_SHIFT)

#define MAX_CONTEXTS_PER_DEVICE         (PAGE_SIZE * 8)


#if LINUX_VERSION_CODE < 0x020410
#define list_for_each_safe(pos, n, head) \
	for (pos = (head)->next, n = pos->next; pos != (head); \
		pos = n, n = pos->next)
#endif    

/******************************************
 *
 * Data structures
 *
 *****************************************/

struct mtx_device;
struct mtx_region;
    
#if defined (KERNEL_2_6) 
typedef struct  agp_kern_info  AGP_KernInfo;
typedef struct  agp_memory     AGP_Memory;
typedef struct  kernel_symbol  ModuleSymbol;
#else
typedef         agp_kern_info  AGP_KernInfo;
typedef         agp_memory     AGP_Memory;
typedef struct  module_symbol  ModuleSymbol;
#endif

/*
 * mtx_agp_driver_t
 *
 * Holds information about AGP driver backend
 */
typedef struct mtx_agp_driver {
    
    struct mtx_device  *dev;            /* MTX device that control AGP driver */
    
    AGP_KernInfo        kern_info;      /* AGP information retrieve from kernel driver */
    off_t               aperture_base;  /* Shortcut to kern_info->aper_base */
    size_t              aperture_size;  /* Size in bytes of AGP aperture */
    
    int                 rate;           /* Current transfer rate */
    int                 enabled;        /* Set to 1 when AGP controller has been enabled once */

    int                 mtrr;           /* MTRR number used for AGP aperture (-1 if none) */

} mtx_agp_driver_t;

/*
 * mtx_context_t
 *
 * MTX process context, identified by a unique id
 */
typedef struct mtx_context {

    int                 unique_id;      /* Unique ID for this context */

    struct mtx_device  *dev;            /* MTX device */
    MTXHANDLE           core_data;      /* Data private to core driver */

  #if MEMORY_STATS
    int64_t             first_allocation_mark;
  #endif

} mtx_context_t;

/*
 * mtx_file_t
 *
 * MTX file private data
 */
typedef struct mtx_file {
    
    int                  auth;          /* Authorization received by driver (boolean value)
                                           (always true for a root process) */
    int                  admin;         /* Shortcut to capable(SYS_ADMIN) */

    struct mtx_device   *dev;           /* MTX device this file refers to */
    mtx_context_t        ctx;           /* MTX context */

} mtx_file_t;
    
/*
 * mtx_region_t
 *
 * Physical memory space used by the driver. There is only
 * one instance of region, while there may be more than one heap
 * block referring to it (when region is shared).
 */
typedef enum {  /* region flags */

    MTX_REGION_DYNAMIC      = 0x01,     /* region has been dynamically allocated */
    MTX_REGION_ALLOCATED    = 0x02,     /* memory has been allocated for this region */
        
} mtx_region_flags;

typedef struct mtx_region {

    struct list_head    list_node;      /* For device list maintenance */
    
    int                 pid;         /* ID of owner ctx (-1 if allocated by kernel or shared) */

    unsigned long       base;           /* base address */
    unsigned long       busaddr;        /* bus address */
    void*               kaddr;          /* kernel virtual/logical address */
    unsigned long       size;           /* size of the region (desired) */
    unsigned long       real_size;      /* size of the region (allocated) */

    mtx_memory_type     type;           /* Type of the region */
    mtx_memory_scope    scope;          /* Region memory scope */
    unsigned long       io_flags;       /* PCI flags (for I/O regions) */
    u8                  flags;          /* region flags */
    
    atomic_t            ref_count;      /* number of reference to this buffer(0 if private region)*/
    int                 mtrr;           /* Memory type range register used (negative if unused) */
    
} mtx_region_t;
    
/*
 * mtx_device_t
 *
 * logical representation of a physical
 * device
 */
typedef struct mtx_device {

    struct list_head            list_node;      /* For list maintenance */
   
    struct pci_dev             *pci_dev;        /* PCI device structure */
    const struct pci_device_id *pci_id;         /* PCI device id */

    int                         agp_cap_offset; /* AGP pci capability offset, -1 if PCI board */

    spinlock_t                  count_lock;     /* spinlock used to access count value */
    int                         open_count;     /* count of opened reference to this device */

    struct semaphore            semaphore;      /* For some other data accesses to this device */

    struct list_head            memory_regions; /* Device memory regions, global to any contexts */
    mtx_region_t                framebuffer;    /* Framebuffer region */
    mtx_region_t                registers;      /* Control registers region */
    
    struct mtx_core_driver     *core;           /* Core driver interface */
    MTXHANDLE                   core_data;      /* Core device data handle */

    void*                       context_bitmap; /* Bitmap for context unique id's */ 

    u32                         bm_flags;       /* Results of busmastering tests */

    int                         irq_enable;     /* 0 if interrupts cannot be used */
    struct tasklet_struct       irq_bh;         /* Device interrupt bottom-half routine */

 } mtx_device_t;

 
INLINE char* GetPciName(struct pci_dev* pPciDev )
{
#ifdef KERNEL_2_6
    return pci_pretty_name(pPciDev);
#else
    return pPciDev->name;
#endif
}

INLINE UINT fops_GetINodeMinor(struct inode* pINode )
{
#ifdef KERNEL_2_6
    return iminor(pINode);
#else
    return (unsigned int) pINode->i_rdev;
#endif
}

INLINE MEMHANDLE GetRegisterHandle( HDEVICE pDevice )
{   
    return pDevice->registers.kaddr;
}

/******************************************
 *
 * Helping macros
 *
 *****************************************/
#ifndef module_init
#define module_init(x) int init_module(void) { return x(); }
#define module_exit(x) void cleanup_module(void) { x(); }
#endif

/*** module paramaters ***/
#define MTX_MODULE_PARM_PREFIX  ""
#define MTX_MODULE_PARM(name)  name

/* Only int parameters supported for now*/
#ifdef KERNEL_2_4
  #define MTX_MODULE_PARM_DECL(name, def) int name = def; MODULE_PARM(name, "i")
#elif defined (KERNEL_2_6)
  #define MTX_MODULE_PARM_DECL(name, def) int name = def; module_param(name, int , 0)
#endif

#ifdef KERNEL_2_4
#define MTX_PUBLIC_MODULE_PARM_DECL(name, def, desc) MTX_MODULE_PARM_DECL(name, def); \
                                                     MODULE_PARM_DESC(name, desc);    
#else
#define MTX_PUBLIC_MODULE_PARM_DECL(name, def, desc) MTX_MODULE_PARM_DECL(name, def); \
                                                     MODULE_PARM_DESC(name, desc);    \
                                                     EXPORT_SYMBOL(name)
#endif
            
/*** region manipulation ***/

    /* Read region specifications from PCI config space */
#define READ_PCI_REGION(dev, reg, bar)                                          do {\
    reg.base  = pci_resource_start(dev->pci_dev, bar);                              \
    reg.size  = pci_resource_end(dev->pci_dev, bar) - reg.base + 1;                 \
    reg.io_flags = pci_resource_flags(dev->pci_dev, bar);                           \
    reg.real_size = reg.size;                                                       \
                                                                                    } while(0)

    /* Add region to device memory region list */
#define ADD_REGION_TO_LIST(dev_or_ctx, reg)                                         \
    list_add_tail(&((reg)->list_node), &((dev_or_ctx)->memory_regions));
#define DEL_REGION_FROM_LIST(dev_or_ctx, reg)                                       \
	list_del(&((reg)->list_node))

/* align a value with a alignment of a power of 2 */
#define MTX_ALIGN_POW_2(value, alignment)                                           \
   (((value) + (alignment) - 1) & ~((alignment) - 1))

/**** Inline methods ****/

/* 
 * (mtx_get_pg_order)
 *
 * return the number of pages required 
 * to hold this size 
 */
#define MTX_MAX_ORDER   9   /* seems to be reasonable, and old-kernel compatible */
static __inline__ int 
mtx_get_pg_order(size_t size) {

    int order;

    for (order = 0; order <= MTX_MAX_ORDER; order++)
        if ((PAGE_SIZE << order) >= size)
            return order;

    return -1;
}

/*
 * (mtx_is_pow_of_2)
 *
 * return if the value is a power of 2 
 */
static __inline__ int
mtx_is_pow_of_2(unsigned long value) {

    int bit_count = 0;
    unsigned long tmpvalue = value;

    while (tmpvalue != 0) {
        bit_count += (tmpvalue & 0x1);
        tmpvalue = tmpvalue >> 1;
    }
  
    /* the value is a pow of 2 if only one of its bits is set to 1 */
    return (bit_count == 1);
}

/******************************************
 *
 * Internal functions prototype
 *
 *****************************************/

/* drv */
STACK_LINKAGE   void*   mtx_find_symbol(const char *symbol);

/* fops */
int     mtx_open(struct inode *inode, struct file *filp);
int     mtx_release(struct inode *inode, struct file *filp);
int     mtx_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
int     mtx_mmap(struct file *filp, struct vm_area_struct *vma);

/* vm */
void    mtx_vm_open_stub(struct vm_area_struct*);
void    mtx_vm_close_stub(struct vm_area_struct*);
    
#ifdef KERNEL_2_6
struct page* mtx_vm_nopage_stub(struct vm_area_struct*, unsigned long, int*);
struct page* mtx_vm_nopage_sys(struct vm_area_struct*, unsigned long, int*);
#else
struct page* mtx_vm_nopage_sys(struct vm_area_struct*, unsigned long, int);
struct page* mtx_vm_nopage_stub(struct vm_area_struct*, unsigned long, int);    
#endif

int     mtx_vm_map_io(mtx_device_t*, mtx_region_t*, struct vm_area_struct*);
int     mtx_vm_map_agp(mtx_device_t*, mtx_region_t*, struct vm_area_struct*);
int     mtx_vm_map_pci(mtx_device_t*, mtx_region_t*, struct vm_area_struct*);
int     mtx_vm_map_sys(mtx_device_t*, mtx_region_t*, struct vm_area_struct*);

/* devices */
int     mtx_dev_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id);
void    mtx_dev_remove(struct pci_dev *pci_dev);
mtx_device_t* mtx_dev_find(unsigned int pci_bus, unsigned int pci_dev);
int     mtx_dev_init(mtx_device_t*);
int     mtx_dev_cleanup(mtx_device_t*);

/* contexts */
int     mtx_ctx_init(mtx_device_t*, mtx_context_t*);
int     mtx_ctx_cleanup(mtx_device_t*, mtx_context_t*);

/* agp driver */
int     mtx_agp_init(void);
void    mtx_agp_cleanup(void);
int     mtx_agp_acquire(mtx_device_t*);
void    mtx_agp_release(mtx_device_t*);
int     mtx_agp_enable(mtx_device_t*);
void    mtx_agp_disable(mtx_device_t*);
void*   mtx_agp_alloc_page_block(unsigned int);
int     mtx_agp_bind_pages(void*, unsigned long);
int     mtx_agp_test_device(mtx_device_t*);
void    mtx_agp_apply_rate_patches(void);

/* ioctls */
int     mtx_ioctl_hard_reset(mtx_file_t*, unsigned int, unsigned long);
int     mtx_ioctl_get_memory_info(mtx_file_t*, unsigned int, unsigned long);
int     mtx_ioctl_get_buffer(mtx_file_t*, unsigned int, unsigned long);
int     mtx_ioctl_release_buffer(mtx_file_t*, unsigned int, unsigned long);
int     mtx_ioctl_driver_value_request(mtx_file_t*, unsigned int, unsigned long);
#if 0
int     mtx_ioctl_add_context(mtx_file_t*, unsigned int, unsigned long);
int     mtx_ioctl_remove_context(mtx_file_t*, unsigned int, unsigned long);
#endif

/* irq */
#ifdef KERNEL_2_6
typedef irqreturn_t IRQReturnType;
#else
typedef void        IRQReturnType;
#endif

int           mtx_irq_init(mtx_device_t* dev);
void          mtx_irq_cleanup(mtx_device_t* dev);
IRQReturnType mtx_irq_interrupt(int irq, void *dev_id, struct pt_regs *regs);   
    
/******************************************
 *
 * Global variables
 *
 *****************************************/
extern mtx_agp_driver_t* agp_drv;
extern int MTX_MODULE_PARM(localbm);
extern int MTX_MODULE_PARM(fw);
extern int MTX_MODULE_PARM(agp);
extern int MTX_MODULE_PARM(agprate);
extern int MTX_MODULE_PARM(enhmemacc);

extern u32 sys_flags;
extern struct pci_dev *pci_host_dev;
extern struct pci_dev *pci_isa_dev;

#endif /* __cplusplus */

/*
 * mtx_ioctl_t
 *
 * definition of an I/O control in MTX.
 * This needs to be exported to all core drivers, so they could register themself
 * their private ioctls through to MTX_ADD_IOCTL macro.
 */
typedef struct mtx_ioctl {

    union {
        int     (*mtx)(mtx_file_t*, unsigned int, unsigned long);
        STACK_LINKAGE int     (*core)(MTXHANDLE, unsigned int, unsigned long);
        void*   vd;
    } func;             /* Function to call depending on dispatch type */
    
    int dispatch;       /* True if this ioctl should be processed by the device core driver */

    int admin_only;     /* True if this ioctl can only be called by a root (admin) process */
    int auth_needed;    /* True if this ioctl can only be called by an authorized process */

} mtx_ioctl_t;

extern mtx_ioctl_t mtx_ioctls[];

/* Utility macro to add an ioctl from a core driver. */
#define MTX_ADD_IOCTL(_n, _ioctl, _core, _auth, _root)                      do {\
    int nr = _IOC_NR(_n);                                                       \
    mtx_ioctls[nr].auth_needed = _auth;                                         \
    mtx_ioctls[nr].admin_only  = _root;                                         \
    mtx_ioctls[nr].dispatch    = (_core ? 1 : 0);                               \
    mtx_ioctls[nr].func.vd     = (void*)(_ioctl);                 } while(0)

#endif /* __KERNEL__ */

#endif  /* #ifndef INC_MTXDRV_H */
