summaryrefslogtreecommitdiff
path: root/linux-core/xgi_drv.c
diff options
context:
space:
mode:
authorIan Romanick <idr@us.ibm.com>2007-06-26 13:05:29 -0700
committerIan Romanick <idr@us.ibm.com>2007-06-26 13:05:29 -0700
commit7af9d670371de868f0642148fe2d594bc9a7dea3 (patch)
tree4db18af75f7136329aaa652aabb75bf6c15585cd /linux-core/xgi_drv.c
parent215787e4297ed4f6364bcc98869a347fc4cad00d (diff)
Initial XP10 code drop from XGI.
See attachment 10246 on https://bugs.freedesktop.org/show_bug.cgi?id=5921
Diffstat (limited to 'linux-core/xgi_drv.c')
-rw-r--r--linux-core/xgi_drv.c1610
1 files changed, 1610 insertions, 0 deletions
diff --git a/linux-core/xgi_drv.c b/linux-core/xgi_drv.c
new file mode 100644
index 00000000..5e80d417
--- /dev/null
+++ b/linux-core/xgi_drv.c
@@ -0,0 +1,1610 @@
+
+/****************************************************************************
+ * Copyright (C) 2003-2006 by XGI Technology, Taiwan.
+ * *
+ * 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 on 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
+ * NON-INFRINGEMENT. IN NO EVENT SHALL XGI 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 "xgi_types.h"
+#include "xgi_linux.h"
+#include "xgi_drv.h"
+#include "xgi_regs.h"
+#include "xgi_pcie.h"
+#include "xgi_misc.h"
+#include "xgi_cmdlist.h"
+
+/* for debug */
+static int xgi_temp = 1;
+/*
+ * global parameters
+ */
+static struct xgi_dev {
+ u16 vendor;
+ u16 device;
+ const char *name;
+} xgidev_list[] = {
+ {PCI_VENDOR_ID_XGI, PCI_DEVICE_ID_XP5, "XP5"},
+ {PCI_VENDOR_ID_XGI, PCI_DEVICE_ID_XG47, "XG47"},
+ {0, 0, NULL}
+};
+
+int xgi_major = XGI_DEV_MAJOR; /* xgi reserved major device number. */
+
+static int xgi_num_devices = 0;
+
+xgi_info_t xgi_devices[XGI_MAX_DEVICES];
+
+#if defined(XGI_PM_SUPPORT_APM)
+static struct pm_dev *apm_xgi_dev[XGI_MAX_DEVICES] = { 0 };
+#endif
+
+/* add one for the control device */
+xgi_info_t xgi_ctl_device;
+wait_queue_head_t xgi_ctl_waitqueue;
+
+#ifdef CONFIG_PROC_FS
+struct proc_dir_entry *proc_xgi;
+#endif
+
+#ifdef CONFIG_DEVFS_FS
+devfs_handle_t xgi_devfs_handles[XGI_MAX_DEVICES];
+#endif
+
+struct list_head xgi_mempid_list;
+
+/* xgi_ functions.. do not take a state device parameter */
+static int xgi_post_vbios(xgi_ioctl_post_vbios_t *info);
+static void xgi_proc_create(void);
+static void xgi_proc_remove_all(struct proc_dir_entry *);
+static void xgi_proc_remove(void);
+
+/* xgi_kern_ functions, interfaces used by linux kernel */
+int xgi_kern_probe(struct pci_dev *, const struct pci_device_id *);
+
+unsigned int xgi_kern_poll(struct file *, poll_table *);
+int xgi_kern_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+int xgi_kern_mmap(struct file *, struct vm_area_struct *);
+int xgi_kern_open(struct inode *, struct file *);
+int xgi_kern_release(struct inode *inode, struct file *filp);
+
+void xgi_kern_vma_open(struct vm_area_struct *vma);
+void xgi_kern_vma_release(struct vm_area_struct *vma);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 1))
+struct page *xgi_kern_vma_nopage(struct vm_area_struct *vma,
+ unsigned long address, int *type);
+#else
+struct page *xgi_kern_vma_nopage(struct vm_area_struct *vma,
+ unsigned long address, int write_access);
+#endif
+
+int xgi_kern_read_card_info(char *, char **, off_t off, int, int *, void *);
+int xgi_kern_read_status(char *, char **, off_t off, int, int *, void *);
+int xgi_kern_read_pcie_info(char *, char **, off_t off, int, int *, void *);
+int xgi_kern_read_version(char *, char **, off_t off, int, int *, void *);
+
+int xgi_kern_ctl_open(struct inode *, struct file *);
+int xgi_kern_ctl_close(struct inode *, struct file *);
+unsigned int xgi_kern_ctl_poll(struct file *, poll_table *);
+
+void xgi_kern_isr_bh(unsigned long);
+irqreturn_t xgi_kern_isr(int, void *, struct pt_regs *);
+
+static void xgi_lock_init(xgi_info_t *info);
+
+#if defined(XGI_PM_SUPPORT_ACPI)
+int xgi_kern_acpi_standby(struct pci_dev *, u32);
+int xgi_kern_acpi_resume(struct pci_dev *);
+#endif
+
+/*
+ * verify access to pci config space wasn't disabled behind our back
+ * unfortunately, XFree86 enables/disables memory access in pci config space at
+ * various times (such as restoring initial pci config space settings during vt
+ * switches or when doing mulicard). As a result, all of our register accesses
+ * are garbage at this point. add a check to see if access was disabled and
+ * reenable any such access.
+ */
+#define XGI_CHECK_PCI_CONFIG(xgi) \
+ xgi_check_pci_config(xgi, __LINE__)
+
+static inline void xgi_check_pci_config(xgi_info_t *info, int line)
+{
+ unsigned short cmd, flag = 0;
+
+ // don't do this on the control device, only the actual devices
+ if (info->flags & XGI_FLAG_CONTROL)
+ return;
+
+ pci_read_config_word(info->dev, PCI_COMMAND, &cmd);
+ if (!(cmd & PCI_COMMAND_MASTER))
+ {
+ XGI_INFO("restoring bus mastering! (%d)\n", line);
+ cmd |= PCI_COMMAND_MASTER;
+ flag = 1;
+ }
+
+ if (!(cmd & PCI_COMMAND_MEMORY))
+ {
+ XGI_INFO("restoring MEM access! (%d)\n", line);
+ cmd |= PCI_COMMAND_MEMORY;
+ flag = 1;
+ }
+
+ if (flag)
+ pci_write_config_word(info->dev, PCI_COMMAND, cmd);
+}
+
+static int xgi_post_vbios(xgi_ioctl_post_vbios_t *info)
+{
+ return 1;
+}
+
+/*
+ * struct pci_device_id {
+ * unsigned int vendor, device; // Vendor and device ID or PCI_ANY_ID
+ * unsigned int subvendor, subdevice; // Subsystem ID's or PCI_ANY_ID
+ * unsigned int class, class_mask; // (class,subclass,prog-if) triplet
+ * unsigned long driver_data; // Data private to the driver
+ * };
+ */
+
+static struct pci_device_id xgi_dev_table[] = {
+ {
+ .vendor = PCI_VENDOR_ID_XGI,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .class = (PCI_CLASS_DISPLAY_VGA << 8),
+ .class_mask = ~0,
+ },
+ { }
+};
+
+/*
+ * #define MODULE_DEVICE_TABLE(type,name) \
+ * MODULE_GENERIC_TABLE(type##_device,name)
+ */
+ MODULE_DEVICE_TABLE(pci, xgi_dev_table);
+
+/*
+ * struct pci_driver {
+ * struct list_head node;
+ * char *name;
+ * const struct pci_device_id *id_table; // NULL if wants all devices
+ * int (*probe)(struct pci_dev *dev, const struct pci_device_id *id); // New device inserted
+ * void (*remove)(struct pci_dev *dev); // Device removed (NULL if not a hot-plug capable driver)
+ * int (*save_state)(struct pci_dev *dev, u32 state); // Save Device Context
+ * int (*suspend)(struct pci_dev *dev, u32 state); // Device suspended
+ * int (*resume)(struct pci_dev *dev); // Device woken up
+ * int (*enable_wake)(struct pci_dev *dev, u32 state, int enable); // Enable wake event
+ * };
+ */
+static struct pci_driver xgi_pci_driver = {
+ .name = "xgi",
+ .id_table = xgi_dev_table,
+ .probe = xgi_kern_probe,
+#if defined(XGI_SUPPORT_ACPI)
+ .suspend = xgi_kern_acpi_standby,
+ .resume = xgi_kern_acpi_resume,
+#endif
+};
+
+/*
+ * find xgi devices and set initial state
+ */
+int xgi_kern_probe(struct pci_dev *dev, const struct pci_device_id *id_table)
+{
+ xgi_info_t *info;
+
+ if ((dev->vendor != PCI_VENDOR_ID_XGI)
+ || (dev->class != (PCI_CLASS_DISPLAY_VGA << 8)))
+ {
+ return -1;
+ }
+
+ if (xgi_num_devices == XGI_MAX_DEVICES)
+ {
+ XGI_INFO("maximum device number (%d) reached!\n", xgi_num_devices);
+ return -1;
+ }
+
+ /* enable io, mem, and bus-mastering in pci config space */
+ if (pci_enable_device(dev) != 0)
+ {
+ XGI_INFO("pci_enable_device failed, aborting\n");
+ return -1;
+ }
+
+ XGI_INFO("maximum device number (%d) reached \n", xgi_num_devices);
+
+ pci_set_master(dev);
+
+ info = &xgi_devices[xgi_num_devices];
+ info->dev = dev;
+ info->vendor_id = dev->vendor;
+ info->device_id = dev->device;
+ info->bus = dev->bus->number;
+ info->slot = PCI_SLOT((dev)->devfn);
+
+ xgi_lock_init(info);
+
+ info->mmio.base = XGI_PCI_RESOURCE_START(dev, 1);
+ info->mmio.size = XGI_PCI_RESOURCE_SIZE(dev, 1);
+
+ /* check IO region */
+ if (!request_mem_region(info->mmio.base, info->mmio.size, "xgi"))
+ {
+ XGI_ERROR("cannot reserve MMIO memory\n");
+ goto error_disable_dev;
+ }
+
+ XGI_INFO("info->mmio.base: 0x%lx \n", info->mmio.base);
+ XGI_INFO("info->mmio.size: 0x%lx \n", info->mmio.size);
+
+ info->mmio.vbase = (unsigned char *)ioremap_nocache(info->mmio.base,
+ info->mmio.size);
+ if (!info->mmio.vbase)
+ {
+ release_mem_region(info->mmio.base, info->mmio.size);
+ XGI_ERROR("info->mmio.vbase failed\n");
+ goto error_disable_dev;
+ }
+ xgi_enable_mmio(info);
+
+ //xgi_enable_ge(info);
+
+ XGI_INFO("info->mmio.vbase: 0x%p \n", info->mmio.vbase);
+
+ info->fb.base = XGI_PCI_RESOURCE_START(dev, 0);
+ info->fb.size = XGI_PCI_RESOURCE_SIZE(dev, 0);
+
+ XGI_INFO("info->fb.base: 0x%lx \n", info->fb.base);
+ XGI_INFO("info->fb.size: 0x%lx \n", info->fb.size);
+
+ info->fb.size = bIn3cf(0x54) * 8 * 1024 * 1024;
+ XGI_INFO("info->fb.size: 0x%lx \n", info->fb.size);
+
+ /* check frame buffer region
+ if (!request_mem_region(info->fb.base, info->fb.size, "xgi"))
+ {
+ release_mem_region(info->mmio.base, info->mmio.size);
+ XGI_ERROR("cannot reserve frame buffer memory\n");
+ goto error_disable_dev;
+ }
+
+
+ info->fb.vbase = (unsigned char *)ioremap_nocache(info->fb.base,
+ info->fb.size);
+
+ if (!info->fb.vbase)
+ {
+ release_mem_region(info->mmio.base, info->mmio.size);
+ release_mem_region(info->fb.base, info->fb.size);
+ XGI_ERROR("info->fb.vbase failed\n");
+ goto error_disable_dev;
+ }
+ */
+ info->fb.vbase = NULL;
+ XGI_INFO("info->fb.vbase: 0x%p \n", info->fb.vbase);
+
+ info->irq = dev->irq;
+
+ /* check common error condition */
+ if (info->irq == 0)
+ {
+ XGI_ERROR("Can't find an IRQ for your XGI card! \n");
+ goto error_zero_dev;
+ }
+ XGI_INFO("info->irq: %lx \n", info->irq);
+
+ //xgi_enable_dvi_interrupt(info);
+
+ /* sanity check the IO apertures */
+ if ((info->mmio.base == 0) || (info->mmio.size == 0)
+ || (info->fb.base == 0) || (info->fb.size == 0))
+ {
+ XGI_ERROR("The IO regions for your XGI card are invalid.\n");
+
+ if ((info->mmio.base == 0) || (info->mmio.size == 0))
+ {
+ XGI_ERROR("mmio appears to be wrong: 0x%lx 0x%lx\n",
+ info->mmio.base,
+ info->mmio.size);
+ }
+
+ if ((info->fb.base == 0) || (info->fb.size == 0))
+ {
+ XGI_ERROR("frame buffer appears to be wrong: 0x%lx 0x%lx\n",
+ info->fb.base,
+ info->fb.size);
+ }
+
+ goto error_zero_dev;
+ }
+
+ //xgi_num_devices++;
+
+ return 0;
+
+error_zero_dev:
+ release_mem_region(info->fb.base, info->fb.size);
+ release_mem_region(info->mmio.base, info->mmio.size);
+
+error_disable_dev:
+ pci_disable_device(dev);
+ return -1;
+
+}
+
+/*
+ * vma operations...
+ * this is only called when the vmas are duplicated. this
+ * appears to only happen when the process is cloned to create
+ * a new process, and not when the process is threaded.
+ *
+ * increment the usage count for the physical pages, so when
+ * this clone unmaps the mappings, the pages are not
+ * deallocated under the original process.
+ */
+struct vm_operations_struct xgi_vm_ops = {
+ .open = xgi_kern_vma_open,
+ .close = xgi_kern_vma_release,
+ .nopage = xgi_kern_vma_nopage,
+};
+
+void xgi_kern_vma_open(struct vm_area_struct *vma)
+{
+ XGI_INFO("VM: vma_open for 0x%lx - 0x%lx, offset 0x%lx\n",
+ vma->vm_start,
+ vma->vm_end,
+ XGI_VMA_OFFSET(vma));
+
+ if (XGI_VMA_PRIVATE(vma))
+ {
+ xgi_pcie_block_t *block = (xgi_pcie_block_t *)XGI_VMA_PRIVATE(vma);
+ XGI_ATOMIC_INC(block->use_count);
+ }
+}
+
+void xgi_kern_vma_release(struct vm_area_struct *vma)
+{
+ XGI_INFO("VM: vma_release for 0x%lx - 0x%lx, offset 0x%lx\n",
+ vma->vm_start,
+ vma->vm_end,
+ XGI_VMA_OFFSET(vma));
+
+ if (XGI_VMA_PRIVATE(vma))
+ {
+ xgi_pcie_block_t *block = (xgi_pcie_block_t *)XGI_VMA_PRIVATE(vma);
+ XGI_ATOMIC_DEC(block->use_count);
+
+ /*
+ * if use_count is down to 0, the kernel virtual mapping was freed
+ * but the underlying physical pages were not, we need to clear the
+ * bit and free the physical pages.
+ */
+ if (XGI_ATOMIC_READ(block->use_count) == 0)
+ {
+ // Need TO Finish
+ XGI_VMA_PRIVATE(vma) = NULL;
+ }
+ }
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 1))
+struct page *xgi_kern_vma_nopage(struct vm_area_struct *vma,
+ unsigned long address, int *type)
+{
+ xgi_pcie_block_t *block = (xgi_pcie_block_t *)XGI_VMA_PRIVATE(vma);
+ struct page *page = NOPAGE_SIGBUS;
+ unsigned long offset = 0;
+ unsigned long page_addr = 0;
+/*
+ XGI_INFO("VM: mmap([0x%lx-0x%lx] off=0x%lx) address: 0x%lx \n",
+ vma->vm_start,
+ vma->vm_end,
+ XGI_VMA_OFFSET(vma),
+ address);
+*/
+ offset = (address - vma->vm_start) + XGI_VMA_OFFSET(vma);
+
+ offset = offset - block->bus_addr;
+
+ offset >>= PAGE_SHIFT;
+
+ page_addr = block->page_table[offset].virt_addr;
+
+ if (xgi_temp)
+ {
+ XGI_INFO("block->bus_addr: 0x%lx block->hw_addr: 0x%lx"
+ "block->page_count: 0x%lx block->page_order: 0x%lx"
+ "block->page_table[0x%lx].virt_addr: 0x%lx\n",
+ block->bus_addr, block->hw_addr,
+ block->page_count, block->page_order,
+ offset,
+ block->page_table[offset].virt_addr);
+ xgi_temp = 0;
+ }
+
+ if (!page_addr) goto out; /* hole or end-of-file */
+ page = virt_to_page(page_addr);
+
+ /* got it, now increment the count */
+ get_page(page);
+out:
+ return page;
+
+}
+#else
+struct page *xgi_kern_vma_nopage(struct vm_area_struct *vma,
+ unsigned long address, int write_access)
+{
+ xgi_pcie_block_t *block = (xgi_pcie_block_t *)XGI_VMA_PRIVATE(vma);
+ struct page *page = NOPAGE_SIGBUS;
+ unsigned long offset = 0;
+ unsigned long page_addr = 0;
+/*
+ XGI_INFO("VM: mmap([0x%lx-0x%lx] off=0x%lx) address: 0x%lx \n",
+ vma->vm_start,
+ vma->vm_end,
+ XGI_VMA_OFFSET(vma),
+ address);
+*/
+ offset = (address - vma->vm_start) + XGI_VMA_OFFSET(vma);
+
+ offset = offset - block->bus_addr;
+
+ offset >>= PAGE_SHIFT;
+
+ page_addr = block->page_table[offset].virt_addr;
+
+ if (xgi_temp)
+ {
+ XGI_INFO("block->bus_addr: 0x%lx block->hw_addr: 0x%lx"
+ "block->page_count: 0x%lx block->page_order: 0x%lx"
+ "block->page_table[0x%lx].virt_addr: 0x%lx\n",
+ block->bus_addr, block->hw_addr,
+ block->page_count, block->page_order,
+ offset,
+ block->page_table[offset].virt_addr);
+ xgi_temp = 0;
+ }
+
+ if (!page_addr) goto out; /* hole or end-of-file */
+ page = virt_to_page(page_addr);
+
+ /* got it, now increment the count */
+ get_page(page);
+out:
+ return page;
+}
+#endif
+
+#if 0
+static struct file_operations xgi_fops = {
+ /* owner: THIS_MODULE, */
+ poll: xgi_kern_poll,
+ ioctl: xgi_kern_ioctl,
+ mmap: xgi_kern_mmap,
+ open: xgi_kern_open,
+ release: xgi_kern_release,
+};
+#endif
+
+static struct file_operations xgi_fops = {
+ .owner = THIS_MODULE,
+ .poll = xgi_kern_poll,
+ .ioctl = xgi_kern_ioctl,
+ .mmap = xgi_kern_mmap,
+ .open = xgi_kern_open,
+ .release = xgi_kern_release,
+};
+
+static xgi_file_private_t * xgi_alloc_file_private(void)
+{
+ xgi_file_private_t *fp;
+
+ XGI_KMALLOC(fp, sizeof(xgi_file_private_t));
+ if (!fp)
+ return NULL;
+
+ memset(fp, 0, sizeof(xgi_file_private_t));
+
+ /* initialize this file's event queue */
+ init_waitqueue_head(&fp->wait_queue);
+
+ xgi_init_lock(fp->fp_lock);
+
+ return fp;
+}
+
+static void xgi_free_file_private(xgi_file_private_t *fp)
+{
+ if (fp == NULL)
+ return;
+
+ XGI_KFREE(fp, sizeof(xgi_file_private_t));
+}
+
+int xgi_kern_open(struct inode *inode, struct file *filp)
+{
+ xgi_info_t *info = NULL;
+ int dev_num;
+ int result = 0, status;
+
+ /*
+ * the type and num values are only valid if we are not using devfs.
+ * However, since we use them to retrieve the device pointer, we
+ * don't need them with devfs as filp->private_data is already
+ * initialized
+ */
+ filp->private_data = xgi_alloc_file_private();
+ if (filp->private_data == NULL)
+ return -ENOMEM;
+
+ XGI_INFO("filp->private_data %p\n", filp->private_data);
+ /*
+ * for control device, just jump to its open routine
+ * after setting up the private data
+ */
+ if (XGI_IS_CONTROL_DEVICE(inode))
+ return xgi_kern_ctl_open(inode, filp);
+
+ /* what device are we talking about? */
+ dev_num = XGI_DEVICE_NUMBER(inode);
+ if (dev_num >= XGI_MAX_DEVICES)
+ {
+ xgi_free_file_private(filp->private_data);
+ filp->private_data = NULL;
+ return -ENODEV;
+ }
+
+ info = &xgi_devices[dev_num];
+
+ XGI_INFO("Jong-xgi_kern_open on device %d\n", dev_num);
+
+ xgi_down(info->info_sem);
+ XGI_CHECK_PCI_CONFIG(info);
+
+ XGI_INFO_FROM_FP(filp) = info;
+
+ /*
+ * map the memory and allocate isr on first open
+ */
+
+ if (!(info->flags & XGI_FLAG_OPEN))
+ {
+ XGI_INFO("info->flags & XGI_FLAG_OPEN \n");
+
+ if (info->device_id == 0)
+ {
+ XGI_INFO("open of nonexistent device %d\n", dev_num);
+ result = -ENXIO;
+ goto failed;
+ }
+
+ /* initialize struct irqaction */
+ status = request_irq(info->irq, xgi_kern_isr,
+ SA_INTERRUPT | SA_SHIRQ, "xgi",
+ (void *) info);
+ if (status != 0)
+ {
+ if (info->irq && (status == -EBUSY))
+ {
+ XGI_ERROR("Tried to get irq %d, but another driver",
+ (unsigned int) info->irq);
+ XGI_ERROR("has it and is not sharing it.\n");
+ }
+ XGI_ERROR("isr request failed 0x%x\n", status);
+ result = -EIO;
+ goto failed;
+ }
+
+ /*
+ * #define DECLARE_TASKLET(name, func, data) \
+ * struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
+ */
+ info->tasklet.func = xgi_kern_isr_bh;
+ info->tasklet.data = (unsigned long) info;
+ tasklet_enable(&info->tasklet);
+
+ /* Alloc 1M bytes for cmdbuffer which is flush2D batch array */
+ xgi_cmdlist_initialize(info, 0x100000);
+
+ info->flags |= XGI_FLAG_OPEN;
+ }
+
+ XGI_ATOMIC_INC(info->use_count);
+
+failed:
+ xgi_up(info->info_sem);
+
+ if ((result) && filp->private_data)
+ {
+ xgi_free_file_private(filp->private_data);
+ filp->private_data = NULL;
+ }
+
+ return result;
+}
+
+int xgi_kern_release(struct inode *inode, struct file *filp)
+{
+ xgi_info_t *info = XGI_INFO_FROM_FP(filp);
+
+ XGI_CHECK_PCI_CONFIG(info);
+
+ /*
+ * for control device, just jump to its open routine
+ * after setting up the private data
+ */
+ if (XGI_IS_CONTROL_DEVICE(inode))
+ return xgi_kern_ctl_close(inode, filp);
+
+ XGI_INFO("Jong-xgi_kern_release on device %d\n", XGI_DEVICE_NUMBER(inode));
+
+ xgi_down(info->info_sem);
+ if (XGI_ATOMIC_DEC_AND_TEST(info->use_count))
+ {
+
+ /*
+ * The usage count for this device has dropped to zero, it can be shut
+ * down safely; disable its interrupts.
+ */
+
+ /*
+ * Disable this device's tasklet to make sure that no bottom half will
+ * run with undefined device state.
+ */
+ tasklet_disable(&info->tasklet);
+
+ /*
+ * Free the IRQ, which may block until all pending interrupt processing
+ * has completed.
+ */
+ free_irq(info->irq, (void *)info);
+
+ xgi_cmdlist_cleanup(info);
+
+ /* leave INIT flag alone so we don't reinit every time */
+ info->flags &= ~XGI_FLAG_OPEN;
+ }
+
+ xgi_up(info->info_sem);
+
+ if (FILE_PRIVATE(filp))
+ {
+ xgi_free_file_private(FILE_PRIVATE(filp));
+ FILE_PRIVATE(filp) = NULL;
+ }
+
+ return 0;
+}
+
+int xgi_kern_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ //struct inode *inode = INODE_FROM_FP(filp);
+ xgi_info_t *info = XGI_INFO_FROM_FP(filp);
+ xgi_pcie_block_t *block;
+ int pages = 0;
+ unsigned long prot;
+
+ XGI_INFO("Jong-VM: mmap([0x%lx-0x%lx] off=0x%lx)\n",
+ vma->vm_start,
+ vma->vm_end,
+ XGI_VMA_OFFSET(vma));
+
+ XGI_CHECK_PCI_CONFIG(info);
+
+ if (XGI_MASK_OFFSET(vma->vm_start)
+ || XGI_MASK_OFFSET(vma->vm_end))
+ {
+ XGI_ERROR("VM: bad mmap range: %lx - %lx\n",
+ vma->vm_start, vma->vm_end);
+ return -ENXIO;
+ }
+
+ pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+
+ vma->vm_ops = &xgi_vm_ops;
+
+ /* XGI IO(reg) space */
+ if (IS_IO_OFFSET(info, XGI_VMA_OFFSET(vma), vma->vm_end - vma->vm_start))
+ {
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ if (XGI_REMAP_PAGE_RANGE(vma->vm_start,
+ XGI_VMA_OFFSET(vma),
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot))
+ return -EAGAIN;
+
+ /* mark it as IO so that we don't dump it on core dump */
+ vma->vm_flags |= VM_IO;
+ XGI_INFO("VM: mmap io space \n");
+ }
+ /* XGI fb space */
+ /* Jong 06/14/2006; moved behind PCIE or modify IS_FB_OFFSET */
+ else if (IS_FB_OFFSET(info, XGI_VMA_OFFSET(vma), vma->vm_end - vma->vm_start))
+ {
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ if (XGI_REMAP_PAGE_RANGE(vma->vm_start,
+ XGI_VMA_OFFSET(vma),
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot))
+ return -EAGAIN;
+
+ // mark it as IO so that we don't dump it on core dump
+ vma->vm_flags |= VM_IO;
+ XGI_INFO("VM: mmap fb space \n");
+ }
+ /* PCIE allocator */
+ /* XGI_VMA_OFFSET(vma) is offset based on pcie.base (HW address space) */
+ else if (IS_PCIE_OFFSET(info, XGI_VMA_OFFSET(vma), vma->vm_end - vma->vm_start))
+ {
+ xgi_down(info->pcie_sem);
+
+ block = (xgi_pcie_block_t *)xgi_find_pcie_block(info, XGI_VMA_OFFSET(vma));
+
+ if (block == NULL)
+ {
+ XGI_ERROR("couldn't find pre-allocated PCIE memory!\n");
+ xgi_up(info->pcie_sem);
+ return -EAGAIN;
+ }
+
+ if (block->page_count != pages)
+ {
+ XGI_ERROR("pre-allocated PCIE memory has wrong number of pages!\n");
+ xgi_up(info->pcie_sem);
+ return -EAGAIN;
+ }
+
+ vma->vm_private_data = block;
+ XGI_ATOMIC_INC(block->use_count);
+ xgi_up(info->pcie_sem);
+
+ /*
+ * prevent the swapper from swapping it out
+ * mark the memory i/o so the buffers aren't
+ * dumped on core dumps */
+ vma->vm_flags |= (VM_LOCKED | VM_IO);
+
+ /* un-cached */
+ prot = pgprot_val(vma->vm_page_prot);
+ /*
+ if (boot_cpu_data.x86 > 3)
+ prot |= _PAGE_PCD | _PAGE_PWT;
+ */
+ vma->vm_page_prot = __pgprot(prot);
+
+ XGI_INFO("VM: mmap pcie space \n");
+ }
+#if 0
+ else if (IS_FB_OFFSET(info, XGI_VMA_OFFSET(vma), vma->vm_end - vma->vm_start))
+ {
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ if (XGI_REMAP_PAGE_RANGE(vma->vm_start,
+ XGI_VMA_OFFSET(vma),
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot))
+ return -EAGAIN;
+
+ // mark it as IO so that we don't dump it on core dump
+ vma->vm_flags |= VM_IO;
+ XGI_INFO("VM: mmap fb space \n");
+ }
+#endif
+ else
+ {
+ vma->vm_flags |= (VM_IO | VM_LOCKED);
+ XGI_ERROR("VM: mmap wrong range \n");
+ }
+
+ vma->vm_file = filp;
+
+ return 0;
+}
+
+unsigned int xgi_kern_poll(struct file *filp, struct poll_table_struct *wait)
+{
+ xgi_file_private_t *fp;
+ xgi_info_t *info;
+ unsigned int mask = 0;
+ unsigned long eflags;
+
+ info = XGI_INFO_FROM_FP(filp);
+
+ if (info->device_number == XGI_CONTROL_DEVICE_NUMBER)
+ return xgi_kern_ctl_poll(filp, wait);
+
+ fp = XGI_GET_FP(filp);
+
+ if (!(filp->f_flags & O_NONBLOCK))
+ {
+ /* add us to the list */
+ poll_wait(filp, &fp->wait_queue, wait);
+ }
+
+ xgi_lock_irqsave(fp->fp_lock, eflags);
+
+ /* wake the user on any event */
+ if (fp->num_events)
+ {
+ XGI_INFO("Hey, an event occured!\n");
+ /*
+ * trigger the client, when they grab the event,
+ * we'll decrement the event count
+ */
+ mask |= (POLLPRI|POLLIN);
+ }
+ xgi_unlock_irqsave(fp->fp_lock, eflags);
+
+ return mask;
+}
+
+int xgi_kern_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ xgi_info_t *info;
+ xgi_mem_alloc_t *alloc = NULL;
+
+ int status = 0;
+ void *arg_copy;
+ int arg_size;
+ int err = 0;
+
+ info = XGI_INFO_FROM_FP(filp);
+
+ XGI_INFO("Jong-ioctl(0x%x, 0x%x, 0x%lx, 0x%x)\n", _IOC_TYPE(cmd), _IOC_NR(cmd), arg, _IOC_SIZE(cmd));
+ /*
+ * extract the type and number bitfields, and don't decode
+ * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
+ */
+ if (_IOC_TYPE(cmd) != XGI_IOCTL_MAGIC) return -ENOTTY;
+ if (_IOC_NR(cmd) > XGI_IOCTL_MAXNR) return -ENOTTY;
+
+ /*
+ * the direction is a bitmask, and VERIFY_WRITE catches R/W
+ * transfers. `Type' is user-oriented, while
+ * access_ok is kernel-oriented, so the concept of "read" and
+ * "write" is reversed
+ */
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ {
+ err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
+ }
+ else if (_IOC_DIR(cmd) & _IOC_WRITE)
+ {
+ err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
+ }
+ if (err) return -EFAULT;
+
+ XGI_CHECK_PCI_CONFIG(info);
+
+ arg_size = _IOC_SIZE(cmd);
+ XGI_KMALLOC(arg_copy, arg_size);
+ if (arg_copy == NULL)
+ {
+ XGI_ERROR("failed to allocate ioctl memory\n");
+ return -ENOMEM;
+ }
+
+ /* Jong 05/25/2006 */
+ /* copy_from_user(arg_copy, (void *)arg, arg_size); */
+ if(copy_from_user(arg_copy, (void *)arg, arg_size))
+ {
+ XGI_ERROR("failed to copyin ioctl data\n");
+ XGI_INFO("Jong-copy_from_user-fail! \n");
+ }
+ else
+ XGI_INFO("Jong-copy_from_user-OK! \n");
+
+ alloc = (xgi_mem_alloc_t *)arg_copy;
+ XGI_INFO("Jong-succeeded in copy_from_user 0x%lx, 0x%x bytes.\n", arg, arg_size);
+
+ switch (_IOC_NR(cmd))
+ {
+ case XGI_ESC_DEVICE_INFO:
+ XGI_INFO("Jong-xgi_ioctl_get_device_info \n");
+ xgi_get_device_info(info, (struct xgi_chip_info_s *) arg_copy);
+ break;
+ case XGI_ESC_POST_VBIOS:
+ XGI_INFO("Jong-xgi_ioctl_post_vbios \n");
+ break;
+ case XGI_ESC_FB_ALLOC:
+ XGI_INFO("Jong-xgi_ioctl_fb_alloc \n");
+ xgi_fb_alloc(info, (struct xgi_mem_req_s *)arg_copy, alloc);
+ break;
+ case XGI_ESC_FB_FREE:
+ XGI_INFO("Jong-xgi_ioctl_fb_free \n");
+ xgi_fb_free(info, *(unsigned long *) arg_copy);
+ break;
+ case XGI_ESC_MEM_COLLECT:
+ XGI_INFO("Jong-xgi_ioctl_mem_collect \n");
+ xgi_mem_collect(info, (unsigned int *) arg_copy);
+ break;
+ case XGI_ESC_PCIE_ALLOC:
+ XGI_INFO("Jong-xgi_ioctl_pcie_alloc \n");
+ xgi_pcie_alloc(info, ((xgi_mem_req_t *)arg_copy)->size,
+ ((xgi_mem_req_t *)arg_copy)->owner, alloc);
+ break;
+ case XGI_ESC_PCIE_FREE:
+ XGI_INFO("Jong-xgi_ioctl_pcie_free: bus_addr = 0x%lx \n", *((unsigned long *) arg_copy));
+ xgi_pcie_free(info, *((unsigned long *) arg_copy));
+ break;
+ case XGI_ESC_PCIE_CHECK:
+ XGI_INFO("Jong-xgi_pcie_heap_check \n");
+ xgi_pcie_heap_check();
+ break;
+ case XGI_ESC_GET_SCREEN_INFO:
+ XGI_INFO("Jong-xgi_get_screen_info \n");
+ xgi_get_screen_info(info, (struct xgi_screen_info_s *) arg_copy);
+ break;
+ case XGI_ESC_PUT_SCREEN_INFO:
+ XGI_INFO("Jong-xgi_put_screen_info \n");
+ xgi_put_screen_info(info, (struct xgi_screen_info_s *) arg_copy);
+ break;
+ case XGI_ESC_MMIO_INFO:
+ XGI_INFO("Jong-xgi_ioctl_get_mmio_info \n");
+ xgi_get_mmio_info(info, (struct xgi_mmio_info_s *) arg_copy);
+ break;
+ case XGI_ESC_GE_RESET:
+ XGI_INFO("Jong-xgi_ioctl_ge_reset \n");
+ xgi_ge_reset(info);
+ break;
+ case XGI_ESC_SAREA_INFO:
+ XGI_INFO("Jong-xgi_ioctl_sarea_info \n");
+ xgi_sarea_info(info, (struct xgi_sarea_info_s *) arg_copy);
+ break;
+ case XGI_ESC_DUMP_REGISTER:
+ XGI_INFO("Jong-xgi_ioctl_dump_register \n");
+ xgi_dump_register(info);
+ break;
+ case XGI_ESC_DEBUG_INFO:
+ XGI_INFO("Jong-xgi_ioctl_restore_registers \n");
+ xgi_restore_registers(info);
+ //xgi_write_pcie_mem(info, (struct xgi_mem_req_s *) arg_copy);
+ //xgi_read_pcie_mem(info, (struct xgi_mem_req_s *) arg_copy);
+ break;
+ case XGI_ESC_SUBMIT_CMDLIST:
+ XGI_INFO("Jong-xgi_ioctl_submit_cmdlist \n");
+ xgi_submit_cmdlist(info, (xgi_cmd_info_t *) arg_copy);
+ break;
+ case XGI_ESC_TEST_RWINKERNEL:
+ XGI_INFO("Jong-xgi_test_rwinkernel \n");
+ xgi_test_rwinkernel(info, *(unsigned long*) arg_copy);
+ break;
+ case XGI_ESC_STATE_CHANGE:
+ XGI_INFO("Jong-xgi_state_change \n");
+ xgi_state_change(info, (xgi_state_info_t *) arg_copy);
+ break;
+ case XGI_ESC_CPUID:
+ XGI_INFO("Jong-XGI_ESC_CPUID \n");
+ xgi_get_cpu_id((struct cpu_info_s*) arg_copy);
+ break;
+ default:
+ XGI_INFO("Jong-xgi_ioctl_default \n");
+ status = -EINVAL;
+ break;
+ }
+
+ if (copy_to_user((void *)arg, arg_copy, arg_size))
+ {
+ XGI_ERROR("failed to copyout ioctl data\n");
+ XGI_INFO("Jong-copy_to_user-fail! \n");
+ }
+ else
+ XGI_INFO("Jong-copy_to_user-OK! \n");
+
+ XGI_KFREE(arg_copy, arg_size);
+ return status;
+}
+
+
+/*
+ * xgi control driver operations defined here
+ */
+int xgi_kern_ctl_open(struct inode *inode, struct file *filp)
+{
+ xgi_info_t *info = &xgi_ctl_device;
+
+ int rc = 0;
+
+ XGI_INFO("Jong-xgi_kern_ctl_open\n");
+
+ xgi_down(info->info_sem);
+ info->device_number = XGI_CONTROL_DEVICE_NUMBER;
+
+ /* save the xgi info in file->private_data */
+ filp->private_data = info;
+
+ if (XGI_ATOMIC_READ(info->use_count) == 0)
+ {
+ init_waitqueue_head(&xgi_ctl_waitqueue);
+ }
+
+ info->flags |= XGI_FLAG_OPEN + XGI_FLAG_CONTROL;
+
+ XGI_ATOMIC_INC(info->use_count);
+ xgi_up(info->info_sem);
+
+ return rc;
+}
+
+int xgi_kern_ctl_close(struct inode *inode, struct file *filp)
+{
+ xgi_info_t *info = XGI_INFO_FROM_FP(filp);
+
+ XGI_INFO("Jong-xgi_kern_ctl_close\n");
+
+ xgi_down(info->info_sem);
+ if (XGI_ATOMIC_DEC_AND_TEST(info->use_count))
+ {
+ info->flags = 0;
+ }
+ xgi_up(info->info_sem);
+
+ if (FILE_PRIVATE(filp))
+ {
+ xgi_free_file_private(FILE_PRIVATE(filp));
+ FILE_PRIVATE(filp) = NULL;
+ }
+
+ return 0;
+}
+
+unsigned int xgi_kern_ctl_poll(struct file *filp, poll_table *wait)
+{
+ //xgi_info_t *info = XGI_INFO_FROM_FP(filp);;
+ unsigned int ret = 0;
+
+ if (!(filp->f_flags & O_NONBLOCK))
+ {
+ poll_wait(filp, &xgi_ctl_waitqueue, wait);
+ }
+
+ return ret;
+}
+
+/*
+ * xgi proc system
+ */
+static u8 xgi_find_pcie_capability(struct pci_dev *dev)
+{
+ u16 status;
+ u8 cap_ptr, cap_id;
+
+ pci_read_config_word(dev, PCI_STATUS, &status);
+ status &= PCI_STATUS_CAP_LIST;
+ if (!status)
+ return 0;
+
+ switch (dev->hdr_type)
+ {
+ case PCI_HEADER_TYPE_NORMAL:
+ case PCI_HEADER_TYPE_BRIDGE:
+ pci_read_config_byte(dev, PCI_CAPABILITY_LIST, &cap_ptr);
+ break;
+ default:
+ return 0;
+ }
+
+ do
+ {
+ cap_ptr &= 0xFC;
+ pci_read_config_byte(dev, cap_ptr + PCI_CAP_LIST_ID, &cap_id);
+ pci_read_config_byte(dev, cap_ptr + PCI_CAP_LIST_NEXT, &cap_ptr);
+ } while (cap_ptr && cap_id != 0xFF);
+
+ return 0;
+}
+
+static struct pci_dev* xgi_get_pci_device(xgi_info_t *info)
+{
+ struct pci_dev *dev;
+
+ dev = XGI_PCI_GET_DEVICE(info->vendor_id, info->device_id, NULL);
+ while (dev)
+ {
+ if (XGI_PCI_SLOT_NUMBER(dev) == info->slot
+ && XGI_PCI_BUS_NUMBER(dev) == info->bus)
+ return dev;
+ dev = XGI_PCI_GET_DEVICE(info->vendor_id, info->device_id, dev);
+ }
+
+ return NULL;
+}
+
+int xgi_kern_read_card_info(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct pci_dev *dev;
+ char *type;
+ int len = 0;
+
+ xgi_info_t *info;
+ info = (xgi_info_t *) data;
+
+ dev = xgi_get_pci_device(info);
+ if (!dev)
+ return 0;
+
+ type = xgi_find_pcie_capability(dev) ? "PCIE" : "PCI";
+ len += sprintf(page+len, "Card Type: \t %s\n", type);
+
+ XGI_PCI_DEV_PUT(dev);
+ return len;
+}
+
+int xgi_kern_read_version(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += sprintf(page+len, "XGI version: %s\n", "1.0");
+ len += sprintf(page+len, "GCC version: %s\n", "3.0");
+
+ return len;
+}
+
+int xgi_kern_read_pcie_info(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ return 0;
+}
+
+int xgi_kern_read_status(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ return 0;
+}
+
+
+static void xgi_proc_create(void)
+{
+#ifdef CONFIG_PROC_FS
+
+ struct pci_dev *dev;
+ int i = 0;
+ char name[6];
+
+ struct proc_dir_entry *entry;
+ struct proc_dir_entry *proc_xgi_pcie, *proc_xgi_cards;
+
+ xgi_info_t *info;
+ xgi_info_t *xgi_max_devices;
+
+ /* world readable directory */
+ int flags = S_IFDIR | S_IRUGO | S_IXUGO;
+
+ proc_xgi = create_proc_entry("xgi", flags, proc_root_driver);
+ if (!proc_xgi)
+ goto failed;
+
+ proc_xgi_cards = create_proc_entry("cards", flags, proc_xgi);
+ if (!proc_xgi_cards)
+ goto failed;
+
+ proc_xgi_pcie = create_proc_entry("pcie", flags, proc_xgi);
+ if (!proc_xgi_pcie)
+ goto failed;
+
+ /*
+ * Set the module owner to ensure that the reference
+ * count reflects accesses to the proc files.
+ */
+ proc_xgi->owner = THIS_MODULE;
+ proc_xgi_cards->owner = THIS_MODULE;
+ proc_xgi_pcie->owner = THIS_MODULE;
+
+ xgi_max_devices = xgi_devices + XGI_MAX_DEVICES;
+ for (info = xgi_devices; info < xgi_max_devices; info++)
+ {
+ if (info->device_id == 0)
+ break;
+
+ /* world readable file */
+ flags = S_IFREG | S_IRUGO;
+
+ dev = xgi_get_pci_device(info);
+ if (!dev)
+ break;
+
+ sprintf(name, "%d", i++);
+ entry = create_proc_entry(name, flags, proc_xgi_cards);
+ if (!entry)
+ {
+ XGI_PCI_DEV_PUT(dev);
+ goto failed;
+ }
+
+ entry->data = info;
+ entry->read_proc = xgi_kern_read_card_info;
+ entry->owner = THIS_MODULE;
+
+ if (xgi_find_pcie_capability(dev))
+ {
+ entry = create_proc_entry("status", flags, proc_xgi_pcie);
+ if (!entry)
+ {
+ XGI_PCI_DEV_PUT(dev);
+ goto failed;
+ }
+
+ entry->data = info;
+ entry->read_proc = xgi_kern_read_status;
+ entry->owner = THIS_MODULE;
+
+ entry = create_proc_entry("card", flags, proc_xgi_pcie);
+ if (!entry)
+ {
+ XGI_PCI_DEV_PUT(dev);
+ goto failed;
+ }
+
+ entry->data = info;
+ entry->read_proc = xgi_kern_read_pcie_info;
+ entry->owner = THIS_MODULE;
+ }
+
+ XGI_PCI_DEV_PUT(dev);
+ }
+
+ entry = create_proc_entry("version", flags, proc_xgi);
+ if (!entry)
+ goto failed;
+
+ entry->read_proc = xgi_kern_read_version;
+ entry->owner = THIS_MODULE;
+
+ entry = create_proc_entry("host-bridge", flags, proc_xgi_pcie);
+ if (!entry)
+ goto failed;
+
+ entry->data = NULL;
+ entry->read_proc = xgi_kern_read_pcie_info;
+ entry->owner = THIS_MODULE;
+
+ return;
+
+failed:
+ XGI_ERROR("failed to create /proc entries!\n");
+ xgi_proc_remove_all(proc_xgi);
+#endif
+}
+
+#ifdef CONFIG_PROC_FS
+static void xgi_proc_remove_all(struct proc_dir_entry *entry)
+{
+ while (entry)
+ {
+ struct proc_dir_entry *next = entry->next;
+ if (entry->subdir)
+ xgi_proc_remove_all(entry->subdir);
+ remove_proc_entry(entry->name, entry->parent);
+ if (entry == proc_xgi)
+ break;
+ entry = next;
+ }
+}
+#endif
+
+static void xgi_proc_remove(void)
+{
+#ifdef CONFIG_PROC_FS
+ xgi_proc_remove_all(proc_xgi);
+#endif
+}
+
+/*
+ * driver receives an interrupt if someone waiting, then hand it off.
+ */
+irqreturn_t xgi_kern_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ xgi_info_t *info = (xgi_info_t *) dev_id;
+ u32 need_to_run_bottom_half = 0;
+
+ //XGI_INFO("xgi_kern_isr \n");
+
+ //XGI_CHECK_PCI_CONFIG(info);
+
+ //xgi_dvi_irq_handler(info);
+
+ if (need_to_run_bottom_half)
+ {
+ tasklet_schedule(&info->tasklet);
+ }
+
+ return IRQ_HANDLED;
+}
+
+void xgi_kern_isr_bh(unsigned long data)
+{
+ xgi_info_t *info = (xgi_info_t *) data;
+
+ XGI_INFO("xgi_kern_isr_bh \n");
+
+ //xgi_dvi_irq_handler(info);
+
+ XGI_CHECK_PCI_CONFIG(info);
+}
+
+static void xgi_lock_init(xgi_info_t *info)
+{
+ if (info == NULL) return;
+
+ spin_lock_init(&info->info_lock);
+
+ sema_init(&info->info_sem, 1);
+ sema_init(&info->fb_sem, 1);
+ sema_init(&info->pcie_sem, 1);
+
+ XGI_ATOMIC_SET(info->use_count, 0);
+}
+
+static void xgi_dev_init(xgi_info_t *info)
+{
+ struct pci_dev *pdev = NULL;
+ struct xgi_dev *dev;
+ int found = 0;
+ u16 pci_cmd;
+
+ XGI_INFO("Enter xgi_dev_init \n");
+
+ //XGI_PCI_FOR_EACH_DEV(pdev)
+ {
+ for (dev = xgidev_list; dev->vendor; dev++)
+ {
+ if ((dev->vendor == pdev->vendor) && (dev->device == pdev->device))
+ {
+ XGI_INFO("dev->vendor = pdev->vendor= %x \n", dev->vendor);
+ XGI_INFO("dev->device = pdev->device= %x \n", dev->device);
+
+ xgi_devices[found].device_id = pdev->device;
+
+ pci_read_config_byte(pdev, PCI_REVISION_ID, &xgi_devices[found].revision_id);
+
+ XGI_INFO("PCI_REVISION_ID= %x \n", xgi_devices[found].revision_id);
+
+ pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd);
+
+ XGI_INFO("PCI_COMMAND = %x \n", pci_cmd);
+
+ break;
+ }
+ }
+ }
+}
+/*
+ * Export to Linux Kernel
+ */
+
+static int __init xgi_init_module(void)
+{
+ xgi_info_t *info = &xgi_devices[xgi_num_devices];
+ int i, result;
+
+ XGI_INFO("Jong-xgi kernel driver %s initializing\n", XGI_DRV_VERSION);
+ //SET_MODULE_OWNER(&xgi_fops);
+
+ memset(xgi_devices, 0, sizeof(xgi_devices));
+
+ if (pci_register_driver(&xgi_pci_driver) < 0)
+ {
+ pci_unregister_driver(&xgi_pci_driver);
+ XGI_ERROR("no XGI graphics adapter found\n");
+ return -ENODEV;
+ }
+
+ XGI_INFO("Jong-xgi_devices[%d].fb.base.: 0x%lx \n", xgi_num_devices, xgi_devices[xgi_num_devices].fb.base);
+ XGI_INFO("Jong-xgi_devices[%d].fb.size.: 0x%lx \n", xgi_num_devices, xgi_devices[xgi_num_devices].fb.size);
+
+/* Jong 07/27/2006; test for ubuntu */
+/*
+#ifdef CONFIG_DEVFS_FS
+
+ XGI_INFO("Jong-Use devfs \n");
+ do
+ {
+ xgi_devfs_handles[0] = XGI_DEVFS_REGISTER("xgi", 0);
+ if (xgi_devfs_handles[0] == NULL)
+ {
+ result = -ENOMEM;
+ XGI_ERROR("devfs register failed\n");
+ goto failed;
+ }
+ } while(0);
+#else */ /* no devfs, do it the "classic" way */
+
+
+ XGI_INFO("Jong-Use non-devfs \n");
+ /*
+ * Register your major, and accept a dynamic number. This is the
+ * first thing to do, in order to avoid releasing other module's
+ * fops in scull_cleanup_module()
+ */
+ result = XGI_REGISTER_CHRDEV(xgi_major, "xgi", &xgi_fops);
+ if (result < 0)
+ {
+ XGI_ERROR("register chrdev failed\n");
+ pci_unregister_driver(&xgi_pci_driver);
+ return result;
+ }
+ if (xgi_major == 0) xgi_major = result; /* dynamic */
+
+/* #endif */ /* CONFIG_DEVFS_FS */
+
+ XGI_INFO("Jong-major number %d\n", xgi_major);
+
+ /* instantiate tasklets */
+ for (i = 0; i < XGI_MAX_DEVICES; i++)
+ {
+ /*
+ * We keep one tasklet per card to avoid latency issues with more
+ * than one device; no two instances of a single tasklet are ever
+ * executed concurrently.
+ */
+ XGI_ATOMIC_SET(xgi_devices[i].tasklet.count, 1);
+ }
+
+ /* init the xgi control device */
+ {
+ xgi_info_t *info_ctl = &xgi_ctl_device;
+ xgi_lock_init(info_ctl);
+ }
+
+ /* Init the resource manager */
+ INIT_LIST_HEAD(&xgi_mempid_list);
+ if (!xgi_fb_heap_init(info))
+ {
+ XGI_ERROR("xgi_fb_heap_init() failed\n");
+ result = -EIO;
+ goto failed;
+ }
+
+ /* Init the resource manager */
+ if (!xgi_pcie_heap_init(info))
+ {
+ XGI_ERROR("xgi_pcie_heap_init() failed\n");
+ result = -EIO;
+ goto failed;
+ }
+
+ /* create /proc/driver/xgi */
+ xgi_proc_create();
+
+#if defined(DEBUG)
+ inter_module_register("xgi_devices", THIS_MODULE, xgi_devices);
+#endif
+
+ return 0;
+
+failed:
+#ifdef CONFIG_DEVFS_FS
+ XGI_DEVFS_REMOVE_CONTROL();
+ XGI_DEVFS_REMOVE_DEVICE(xgi_num_devices);
+#endif
+
+ if (XGI_UNREGISTER_CHRDEV(xgi_major, "xgi") < 0)
+ XGI_ERROR("unregister xgi chrdev failed\n");
+
+ for (i = 0; i < xgi_num_devices; i++)
+ {
+ if (xgi_devices[i].dev)
+ {
+ release_mem_region(xgi_devices[i].fb.base, xgi_devices[i].fb.size);
+ release_mem_region(xgi_devices[i].mmio.base, xgi_devices[i].mmio.size);
+ }
+ }
+
+ pci_unregister_driver(&xgi_pci_driver);
+ return result;
+
+ return 1;
+}
+
+void __exit xgi_exit_module(void)
+{
+ int i;
+ xgi_info_t *info, *max_devices;
+
+#ifdef CONFIG_DEVFS_FS
+ /*
+ XGI_DEVFS_REMOVE_CONTROL();
+ for (i = 0; i < XGI_MAX_DEVICES; i++)
+ XGI_DEVFS_REMOVE_DEVICE(i);
+ */
+ XGI_DEVFS_REMOVE_DEVICE(xgi_num_devices);
+#endif
+
+ if (XGI_UNREGISTER_CHRDEV(xgi_major, "xgi") < 0)
+ XGI_ERROR("unregister xgi chrdev failed\n");
+
+ XGI_INFO("Jong-unregister xgi chrdev scceeded\n");
+ for (i = 0; i < XGI_MAX_DEVICES; i++)
+ {
+ if (xgi_devices[i].dev)
+ {
+ /* clean up the flush2D batch array */
+ xgi_cmdlist_cleanup(&xgi_devices[i]);
+
+ if(xgi_devices[i].fb.vbase != NULL)
+ {
+ iounmap((void *)xgi_devices[i].fb.vbase);
+ xgi_devices[i].fb.vbase = NULL;
+ }
+ if(xgi_devices[i].mmio.vbase != NULL)
+ {
+ iounmap((void *)xgi_devices[i].mmio.vbase);
+ xgi_devices[i].mmio.vbase = NULL;
+ }
+
+ //release_mem_region(xgi_devices[i].fb.base, xgi_devices[i].fb.size);
+ //XGI_INFO("release frame buffer mem region scceeded\n");
+
+ release_mem_region(xgi_devices[i].mmio.base, xgi_devices[i].mmio.size);
+ XGI_INFO("release MMIO mem region scceeded\n");
+
+ xgi_fb_heap_cleanup(&xgi_devices[i]);
+ XGI_INFO("xgi_fb_heap_cleanup scceeded\n");
+
+ xgi_pcie_heap_cleanup(&xgi_devices[i]);
+ XGI_INFO("xgi_pcie_heap_cleanup scceeded\n");
+
+ XGI_PCI_DISABLE_DEVICE(xgi_devices[i].dev);
+ }
+ }
+
+ pci_unregister_driver(&xgi_pci_driver);
+
+ /* remove /proc/driver/xgi */
+ xgi_proc_remove();
+
+#if defined(DEBUG)
+ inter_module_unregister("xgi_devices");
+#endif
+}
+
+module_init(xgi_init_module);
+module_exit(xgi_exit_module);
+
+#if defined(XGI_PM_SUPPORT_ACPI)
+int xgi_acpi_event(struct pci_dev *dev, u32 state)
+{
+ return 1;
+}
+
+int xgi_kern_acpi_standby(struct pci_dev *dev, u32 state)
+{
+ return 1;
+}
+
+int xgi_kern_acpi_resume(struct pci_dev *dev)
+{
+ return 1;
+}
+#endif
+
+MODULE_AUTHOR("Andrea Zhang <andrea_zhang@macrosynergy.com>");
+MODULE_DESCRIPTION("xgi kernel driver for xgi cards");
+MODULE_LICENSE("GPL");