fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / video / sis / sis_main.c
index 960cdbb..baaf495 100644 (file)
@@ -1,8 +1,10 @@
 /*
- * SiS 300/630/730/540/315/550/650/651/M650/661FX/M661FX/740/741/M741/330/760
- * frame buffer driver for Linux kernels 2.4.x and 2.5.x
+ * SiS 300/540/630[S]/730[S],
+ * SiS 315[E|PRO]/550/[M]65x/[M]66x[F|M|G]X/[M]74x[GX]/330/[M]76x[GX],
+ * XGI V3XT/V5/V8, Z7
+ * frame buffer driver for Linux kernels >= 2.4.14 and >=2.6.3
  *
- * Copyright (C) 2001-2004 Thomas Winischhofer, Vienna, Austria.
+ * Copyright (C) 2001-2005 Thomas Winischhofer, Vienna, Austria.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
  *
- * Author:     Thomas Winischhofer <thomas@winischhofer.net>
+ * Author:     Thomas Winischhofer <thomas@winischhofer.net>
  *
- * Author of code base:
- *             SiS (www.sis.com.tw)
- *             Copyright (C) 1999 Silicon Integrated Systems, Inc.
+ * Author of (practically wiped) code base:
+ *             SiS (www.sis.com)
+ *             Copyright (C) 1999 Silicon Integrated Systems, Inc.
  *
  * See http://www.winischhofer.net/ for more information and updates
  *
  *
  */
 
-#include <linux/config.h>
 #include <linux/version.h>
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/kernel.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/mm.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
 #include <linux/tty.h>
+#else
+#include <linux/screen_info.h>
+#endif
+
 #include <linux/slab.h>
-#include <linux/delay.h>
 #include <linux/fb.h>
-#include <linux/console.h>
 #include <linux/selection.h>
 #include <linux/ioport.h>
 #include <linux/init.h>
 #include <linux/pci.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
 #include <linux/vmalloc.h>
-#endif
-#include <linux/vt_kern.h>
 #include <linux/capability.h>
 #include <linux/fs.h>
-#include <linux/agp_backend.h>
 #include <linux/types.h>
 #include <asm/uaccess.h>
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-#include <linux/spinlock.h>
-#endif
-
-#include "osdef.h"
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-#include <video/sisfb.h>
-#else
-#include <linux/sisfb.h>
-#endif
-
 #include <asm/io.h>
 #ifdef CONFIG_MTRR
 #include <asm/mtrr.h>
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-#include <video/fbcon.h>
-#include <video/fbcon-cfb8.h>
-#include <video/fbcon-cfb16.h>
-#include <video/fbcon-cfb24.h>
-#include <video/fbcon-cfb32.h>
-#endif
-
-#include "vgatypes.h"
-#include "sis_main.h"
 #include "sis.h"
+#include "sis_main.h"
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
-#error "This version of sisfb requires at least 2.6.0"
-#else
-#if 0
-#define NEWFBDEV               /* Define this as soon as new fvdev code has been merged */
-#endif
-#endif
+static void sisfb_handle_command(struct sis_video_info *ivideo,
+                                struct sisfb_cmd *sisfb_command);
+
+/* ------------------ Internal helper routines ----------------- */
+
+static void __init
+sisfb_setdefaultparms(void)
+{
+       sisfb_off               = 0;
+       sisfb_parm_mem          = 0;
+       sisfb_accel             = -1;
+       sisfb_ypan              = -1;
+       sisfb_max               = -1;
+       sisfb_userom            = -1;
+       sisfb_useoem            = -1;
+       sisfb_mode_idx          = -1;
+       sisfb_parm_rate         = -1;
+       sisfb_crt1off           = 0;
+       sisfb_forcecrt1         = -1;
+       sisfb_crt2type          = -1;
+       sisfb_crt2flags         = 0;
+       sisfb_pdc               = 0xff;
+       sisfb_pdca              = 0xff;
+       sisfb_scalelcd          = -1;
+       sisfb_specialtiming     = CUT_NONE;
+       sisfb_lvdshl            = -1;
+       sisfb_dstn              = 0;
+       sisfb_fstn              = 0;
+       sisfb_tvplug            = -1;
+       sisfb_tvstd             = -1;
+       sisfb_tvxposoffset      = 0;
+       sisfb_tvyposoffset      = 0;
+       sisfb_nocrt2rate        = 0;
+#if !defined(__i386__) && !defined(__x86_64__)
+       sisfb_resetcard         = 0;
+       sisfb_videoram          = 0;
 #endif
+}
 
-/* -------------------- Macro definitions ---------------------------- */
+/* ------------- Parameter parsing -------------- */
 
-#undef SISFBDEBUG      /* TW: no debugging */
+static void __devinit
+sisfb_search_vesamode(unsigned int vesamode, BOOLEAN quiet)
+{
+       int i = 0, j = 0;
 
-#ifdef SISFBDEBUG
-#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
-#else
-#define DPRINTK(fmt, args...)
-#endif
+       /* We don't know the hardware specs yet and there is no ivideo */
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-#ifdef SISFBACCEL
-#ifdef FBCON_HAS_CFB8
-extern struct display_switch fbcon_sis8;
-#endif
-#ifdef FBCON_HAS_CFB16
-extern struct display_switch fbcon_sis16;
-#endif
-#ifdef FBCON_HAS_CFB32
-extern struct display_switch fbcon_sis32;
-#endif
-#endif
-#endif
+       if(vesamode == 0) {
+               if(!quiet)
+                       printk(KERN_ERR "sisfb: Invalid mode. Using default.\n");
 
-/* --------------- Hardware Access Routines -------------------------- */
+               sisfb_mode_idx = DEFAULT_MODE;
 
-void sisfb_set_reg4(u16 port, unsigned long data)
-{
-       outl((u32) (data & 0xffffffff), port);
+               return;
+       }
+
+       vesamode &= 0x1dff;  /* Clean VESA mode number from other flags */
+
+       while(sisbios_mode[i++].mode_no[0] != 0) {
+               if( (sisbios_mode[i-1].vesa_mode_no_1 == vesamode) ||
+                   (sisbios_mode[i-1].vesa_mode_no_2 == vesamode) ) {
+                       if(sisfb_fstn) {
+                               if(sisbios_mode[i-1].mode_no[1] == 0x50 ||
+                                  sisbios_mode[i-1].mode_no[1] == 0x56 ||
+                                  sisbios_mode[i-1].mode_no[1] == 0x53)
+                                       continue;
+                       } else {
+                               if(sisbios_mode[i-1].mode_no[1] == 0x5a ||
+                                  sisbios_mode[i-1].mode_no[1] == 0x5b)
+                                       continue;
+                       }
+                       sisfb_mode_idx = i - 1;
+                       j = 1;
+                       break;
+               }
+       }
+       if((!j) && !quiet)
+               printk(KERN_ERR "sisfb: Invalid VESA mode 0x%x'\n", vesamode);
 }
 
-u32 sisfb_get_reg3(u16 port)
+static void __devinit
+sisfb_search_mode(char *name, BOOLEAN quiet)
 {
-       u32 data;
+       unsigned int j = 0, xres = 0, yres = 0, depth = 0, rate = 0;
+       int i = 0;
+       char strbuf[16], strbuf1[20];
+       char *nameptr = name;
 
-       data = inl(port);
-       return (data);
-}
+       /* We don't know the hardware specs yet and there is no ivideo */
 
-/* ------------ Interface for init & mode switching code ------------- */
+       if(name == NULL) {
+               if(!quiet)
+                       printk(KERN_ERR "sisfb: Internal error, using default mode.\n");
 
-BOOLEAN
-sisfb_query_VGA_config_space(PSIS_HW_INFO psishw_ext,
-       unsigned long offset, unsigned long set, unsigned long *value)
-{
-       static struct pci_dev *pdev = NULL;
-       static unsigned char init = 0, valid_pdev = 0;
+               sisfb_mode_idx = DEFAULT_MODE;
+               return;
+       }
 
-       if (!set)
-               DPRINTK("sisfb: Get VGA offset 0x%lx\n", offset);
-       else
-               DPRINTK("sisfb: Set offset 0x%lx to 0x%lx\n", offset, *value);
+       if(!strnicmp(name, sisbios_mode[MODE_INDEX_NONE].name, strlen(name))) {
+               if(!quiet)
+                       printk(KERN_ERR "sisfb: Mode 'none' not supported anymore. Using default.\n");
 
-       if (!init) {
-               init = TRUE;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,74)
-               pci_for_each_dev(pdev) {
-#else
-               while((pdev = pci_find_device(PCI_VENDOR_ID_SI, PCI_ANY_ID, pdev))) {
-#endif
-                       DPRINTK("sisfb: Current: 0x%x, target: 0x%x\n",
-                                pdev->device, ivideo.chip_id);
-                       if ((pdev->vendor == PCI_VENDOR_ID_SI)
-                                  && (pdev->device == ivideo.chip_id)) {
-                               valid_pdev = TRUE;
-                               break;
+               sisfb_mode_idx = DEFAULT_MODE;
+               return;
+       }
+
+       if(strlen(name) <= 19) {
+               strcpy(strbuf1, name);
+               for(i = 0; i < strlen(strbuf1); i++) {
+                       if(strbuf1[i] < '0' || strbuf1[i] > '9') strbuf1[i] = ' ';
+               }
+
+               /* This does some fuzzy mode naming detection */
+               if(sscanf(strbuf1, "%u %u %u %u", &xres, &yres, &depth, &rate) == 4) {
+                       if((rate <= 32) || (depth > 32)) {
+                               j = rate; rate = depth; depth = j;
+                       }
+                       sprintf(strbuf, "%ux%ux%u", xres, yres, depth);
+                       nameptr = strbuf;
+                       sisfb_parm_rate = rate;
+               } else if(sscanf(strbuf1, "%u %u %u", &xres, &yres, &depth) == 3) {
+                       sprintf(strbuf, "%ux%ux%u", xres, yres, depth);
+                       nameptr = strbuf;
+               } else {
+                       xres = 0;
+                       if((sscanf(strbuf1, "%u %u", &xres, &yres) == 2) && (xres != 0)) {
+                               sprintf(strbuf, "%ux%ux8", xres, yres);
+                               nameptr = strbuf;
+                       } else {
+                               sisfb_search_vesamode(simple_strtoul(name, NULL, 0), quiet);
+                               return;
                        }
                }
        }
 
-       if (!valid_pdev) {
-               printk(KERN_DEBUG "sisfb: Can't find SiS %d VGA device.\n",
-                               ivideo.chip_id);
-               return FALSE;
+       i = 0; j = 0;
+       while(sisbios_mode[i].mode_no[0] != 0) {
+               if(!strnicmp(nameptr, sisbios_mode[i++].name, strlen(nameptr))) {
+                       if(sisfb_fstn) {
+                               if(sisbios_mode[i-1].mode_no[1] == 0x50 ||
+                                  sisbios_mode[i-1].mode_no[1] == 0x56 ||
+                                  sisbios_mode[i-1].mode_no[1] == 0x53)
+                                       continue;
+                       } else {
+                               if(sisbios_mode[i-1].mode_no[1] == 0x5a ||
+                                  sisbios_mode[i-1].mode_no[1] == 0x5b)
+                                       continue;
+                       }
+                       sisfb_mode_idx = i - 1;
+                       j = 1;
+                       break;
+               }
        }
 
-       if (set == 0)
-               pci_read_config_dword(pdev, offset, (u32 *)value);
-       else
-               pci_write_config_dword(pdev, offset, (u32)(*value));
-
-       return TRUE;
+       if((!j) && !quiet)
+               printk(KERN_ERR "sisfb: Invalid mode '%s'\n", nameptr);
 }
 
-BOOLEAN sisfb_query_north_bridge_space(PSIS_HW_INFO psishw_ext,
-       unsigned long offset, unsigned long set, unsigned long *value)
+#ifndef MODULE
+static void __devinit
+sisfb_get_vga_mode_from_kernel(void)
 {
-       static struct pci_dev *pdev = NULL;
-       static unsigned char init = 0, valid_pdev = 0;
-       u16 nbridge_id = 0;
+#ifdef CONFIG_X86
+       char mymode[32];
+       int  mydepth = screen_info.lfb_depth;
 
-       if (!init) {
-               init = TRUE;
-               switch (ivideo.chip) {
-#ifdef CONFIG_FB_SIS_300
-               case SIS_540:
-                       nbridge_id = PCI_DEVICE_ID_SI_540;
-                       break;
-               case SIS_630:
-                       nbridge_id = PCI_DEVICE_ID_SI_630;
-                       break;
-               case SIS_730:
-                       nbridge_id = PCI_DEVICE_ID_SI_730;
-                       break;
+       if(screen_info.orig_video_isVGA != VIDEO_TYPE_VLFB) return;
+
+       if( (screen_info.lfb_width >= 320) && (screen_info.lfb_width <= 2048) &&
+           (screen_info.lfb_height >= 200) && (screen_info.lfb_height <= 1536) &&
+           (mydepth >= 8) && (mydepth <= 32) ) {
+
+               if(mydepth == 24) mydepth = 32;
+
+               sprintf(mymode, "%ux%ux%u", screen_info.lfb_width,
+                                       screen_info.lfb_height,
+                                       mydepth);
+
+               printk(KERN_DEBUG
+                       "sisfb: Using vga mode %s pre-set by kernel as default\n",
+                       mymode);
+
+               sisfb_search_mode(mymode, TRUE);
+       }
 #endif
-#ifdef CONFIG_FB_SIS_315
-               case SIS_550:
-                       nbridge_id = PCI_DEVICE_ID_SI_550;
-                       break;
-               case SIS_650:
-                       nbridge_id = PCI_DEVICE_ID_SI_650;
-                       break;
-               case SIS_740:
-                       nbridge_id = PCI_DEVICE_ID_SI_740;
-                       break;
-               case SIS_661:
-                       nbridge_id = PCI_DEVICE_ID_SI_660;
-                       break;
-               case SIS_741:
-                       nbridge_id = PCI_DEVICE_ID_SI_741;
-                       break;
-               case SIS_660:
-                       nbridge_id = PCI_DEVICE_ID_SI_660;
-                       break;
-               case SIS_760:
-                       nbridge_id = PCI_DEVICE_ID_SI_760;
-                       break;
+       return;
+}
 #endif
-               default:
-                       nbridge_id = 0;
-                       break;
-               }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,74)
-               pci_for_each_dev(pdev) {
-#else
-               while((pdev = pci_find_device(PCI_VENDOR_ID_SI, PCI_ANY_ID, pdev))) {
-#endif
-                       DPRINTK("Current: 0x%x, target: 0x%x\n",
-                                       pdev->device, ivideo.chip_id);
-                       if ((pdev->vendor == PCI_VENDOR_ID_SI)
-                                       && (pdev->device == nbridge_id)) {
-                               valid_pdev = TRUE;
-                               break;
-                       }
-               }
-       }
+static void __init
+sisfb_search_crt2type(const char *name)
+{
+       int i = 0;
 
-       if (!valid_pdev) {
-               printk(KERN_DEBUG "sisfb: Can't find SiS %d North Bridge device.\n",
-                               nbridge_id);
-               return FALSE;
+       /* We don't know the hardware specs yet and there is no ivideo */
+
+       if(name == NULL) return;
+
+       while(sis_crt2type[i].type_no != -1) {
+               if(!strnicmp(name, sis_crt2type[i].name, strlen(sis_crt2type[i].name))) {
+                       sisfb_crt2type = sis_crt2type[i].type_no;
+                       sisfb_tvplug = sis_crt2type[i].tvplug_no;
+                       sisfb_crt2flags = sis_crt2type[i].flags;
+                       break;
+               }
+               i++;
        }
 
-       if (set == 0)
-               pci_read_config_dword(pdev, offset, (u32 *)value);
-       else
-               pci_write_config_dword(pdev, offset, (u32)(*value));
+       sisfb_dstn = (sisfb_crt2flags & FL_550_DSTN) ? 1 : 0;
+       sisfb_fstn = (sisfb_crt2flags & FL_550_FSTN) ? 1 : 0;
 
-       return TRUE;
+       if(sisfb_crt2type < 0)
+               printk(KERN_ERR "sisfb: Invalid CRT2 type: %s\n", name);
 }
 
-/* ------------------ Internal helper routines ----------------- */
-
-static BOOLEAN sisfb_verify_rate(struct sisfb_monitor *monitor, int mode_idx, int rate_idx, int rate)
+static void __init
+sisfb_search_tvstd(const char *name)
 {
-       int htotal, vtotal;
-       unsigned int dclock, hsync;
+       int i = 0;
+
+       /* We don't know the hardware specs yet and there is no ivideo */
+
+       if(name == NULL)
+               return;
+
+       while(sis_tvtype[i].type_no != -1) {
+               if(!strnicmp(name, sis_tvtype[i].name, strlen(sis_tvtype[i].name))) {
+                       sisfb_tvstd = sis_tvtype[i].type_no;
+                       break;
+               }
+               i++;
+       }
+}
 
-       if(!monitor->datavalid) return TRUE;
+static void __init
+sisfb_search_specialtiming(const char *name)
+{
+       int i = 0;
+       BOOLEAN found = FALSE;
 
-       if(mode_idx < 0) return FALSE;
+       /* We don't know the hardware specs yet and there is no ivideo */
 
-       if(rate < (monitor->vmin - 1)) return FALSE;
-       if(rate > (monitor->vmax + 1)) return FALSE;
+       if(name == NULL)
+               return;
 
-       if(sisfb_gettotalfrommode(&SiS_Pr, &sishw_ext, sisbios_mode[mode_idx].mode_no,
-                                 &htotal, &vtotal, rate_idx)) {
-               dclock = (htotal * vtotal * rate) / 1000;
-               if(dclock > (monitor->dclockmax + 1000)) return FALSE;
-               hsync = dclock / htotal;
-               if(hsync < (monitor->hmin - 1)) return FALSE;
-               if(hsync > (monitor->hmax + 1)) return FALSE;
-        } else {
-               return FALSE;
+       if(!strnicmp(name, "none", 4)) {
+               sisfb_specialtiming = CUT_FORCENONE;
+               printk(KERN_DEBUG "sisfb: Special timing disabled\n");
+       } else {
+               while(mycustomttable[i].chipID != 0) {
+                       if(!strnicmp(name,mycustomttable[i].optionName,
+                          strlen(mycustomttable[i].optionName))) {
+                               sisfb_specialtiming = mycustomttable[i].SpecialID;
+                               found = TRUE;
+                               printk(KERN_INFO "sisfb: Special timing for %s %s forced (\"%s\")\n",
+                                       mycustomttable[i].vendorName,
+                                       mycustomttable[i].cardName,
+                                       mycustomttable[i].optionName);
+                               break;
+                       }
+                       i++;
+               }
+               if(!found) {
+                       printk(KERN_WARNING "sisfb: Invalid SpecialTiming parameter, valid are:");
+                       printk(KERN_WARNING "\t\"none\" (to disable special timings)\n");
+                       i = 0;
+                       while(mycustomttable[i].chipID != 0) {
+                               printk(KERN_WARNING "\t\"%s\" (for %s %s)\n",
+                                       mycustomttable[i].optionName,
+                                       mycustomttable[i].vendorName,
+                                       mycustomttable[i].cardName);
+                               i++;
+                       }
+               }
        }
-       return TRUE;
-};
+}
+
+/* ----------- Various detection routines ----------- */
+
+static void __devinit
+sisfb_detect_custom_timing(struct sis_video_info *ivideo)
+{
+       unsigned char *biosver = NULL;
+       unsigned char *biosdate = NULL;
+       BOOLEAN footprint;
+       u32 chksum = 0;
+       int i, j;
+
+       if(ivideo->SiS_Pr.UseROM) {
+               biosver = ivideo->SiS_Pr.VirtualRomBase + 0x06;
+               biosdate = ivideo->SiS_Pr.VirtualRomBase + 0x2c;
+               for(i = 0; i < 32768; i++)
+                       chksum += ivideo->SiS_Pr.VirtualRomBase[i];
+       }
+
+       i = 0;
+       do {
+               if( (mycustomttable[i].chipID == ivideo->chip)                  &&
+                   ((!strlen(mycustomttable[i].biosversion)) ||
+                    (ivideo->SiS_Pr.UseROM &&
+                     (!strncmp(mycustomttable[i].biosversion, biosver,
+                               strlen(mycustomttable[i].biosversion)))))       &&
+                   ((!strlen(mycustomttable[i].biosdate)) ||
+                    (ivideo->SiS_Pr.UseROM &&
+                     (!strncmp(mycustomttable[i].biosdate, biosdate,
+                               strlen(mycustomttable[i].biosdate)))))          &&
+                   ((!mycustomttable[i].bioschksum) ||
+                    (ivideo->SiS_Pr.UseROM &&
+                     (mycustomttable[i].bioschksum == chksum)))                &&
+                   (mycustomttable[i].pcisubsysvendor == ivideo->subsysvendor) &&
+                   (mycustomttable[i].pcisubsyscard == ivideo->subsysdevice) ) {
+                       footprint = TRUE;
+                       for(j = 0; j < 5; j++) {
+                               if(mycustomttable[i].biosFootprintAddr[j]) {
+                                       if(ivideo->SiS_Pr.UseROM) {
+                                               if(ivideo->SiS_Pr.VirtualRomBase[mycustomttable[i].biosFootprintAddr[j]] !=
+                                                       mycustomttable[i].biosFootprintData[j]) {
+                                                       footprint = FALSE;
+                                               }
+                                       } else
+                                               footprint = FALSE;
+                               }
+                       }
+                       if(footprint) {
+                               ivideo->SiS_Pr.SiS_CustomT = mycustomttable[i].SpecialID;
+                               printk(KERN_DEBUG "sisfb: Identified [%s %s], special timing applies\n",
+                                       mycustomttable[i].vendorName,
+                               mycustomttable[i].cardName);
+                               printk(KERN_DEBUG "sisfb: [specialtiming parameter name: %s]\n",
+                                       mycustomttable[i].optionName);
+                               break;
+                       }
+               }
+               i++;
+       } while(mycustomttable[i].chipID);
+}
 
-static BOOLEAN sisfb_interpret_edid(struct sisfb_monitor *monitor, u8 *buffer)
+static BOOLEAN __devinit
+sisfb_interpret_edid(struct sisfb_monitor *monitor, u8 *buffer)
 {
        int i, j, xres, yres, refresh, index;
        u32 emodes;
@@ -294,22 +416,23 @@ static BOOLEAN sisfb_interpret_edid(struct sisfb_monitor *monitor, u8 *buffer)
           buffer[2] != 0xff || buffer[3] != 0xff ||
           buffer[4] != 0xff || buffer[5] != 0xff ||
           buffer[6] != 0xff || buffer[7] != 0x00) {
-          printk(KERN_DEBUG "sisfb: Bad EDID header\n");
-          return FALSE;
+               printk(KERN_DEBUG "sisfb: Bad EDID header\n");
+               return FALSE;
        }
 
        if(buffer[0x12] != 0x01) {
-          printk(KERN_INFO "sisfb: EDID version %d not supported\n",
-               buffer[0x12]);
-          return FALSE;
+               printk(KERN_INFO "sisfb: EDID version %d not supported\n",
+                       buffer[0x12]);
+               return FALSE;
        }
 
        monitor->feature = buffer[0x18];
 
        if(!buffer[0x14] & 0x80) {
-          if(!(buffer[0x14] & 0x08)) {
-             printk(KERN_INFO "sisfb: WARNING: Monitor does not support separate syncs\n");
-          }
+               if(!(buffer[0x14] & 0x08)) {
+                       printk(KERN_INFO
+                               "sisfb: WARNING: Monitor does not support separate syncs\n");
+               }
        }
 
        if(buffer[0x13] >= 0x01) {
@@ -319,7 +442,7 @@ static BOOLEAN sisfb_interpret_edid(struct sisfb_monitor *monitor, u8 *buffer)
            j = 0x36;
            for(i=0; i<4; i++) {
               if(buffer[j]     == 0x00 && buffer[j + 1] == 0x00 &&
-                 buffer[j + 2] == 0x00 && buffer[j + 3] == 0xfd &&
+                 buffer[j + 2] == 0x00 && buffer[j + 3] == 0xfd &&
                  buffer[j + 4] == 0x00) {
                  monitor->hmin = buffer[j + 7];
                  monitor->hmax = buffer[j + 8];
@@ -345,7 +468,7 @@ static BOOLEAN sisfb_interpret_edid(struct sisfb_monitor *monitor, u8 *buffer)
           emodes = buffer[0x23] | (buffer[0x24] << 8) | (buffer[0x25] << 16);
           for(i = 0; i < 13; i++) {
              if(emodes & sisfb_ddcsmodes[i].mask) {
-                if(monitor->hmin > sisfb_ddcsmodes[i].h) monitor->hmin = sisfb_ddcsmodes[i].h;
+                if(monitor->hmin > sisfb_ddcsmodes[i].h) monitor->hmin = sisfb_ddcsmodes[i].h;
                 if(monitor->hmax < sisfb_ddcsmodes[i].h) monitor->hmax = sisfb_ddcsmodes[i].h + 1;
                 if(monitor->vmin > sisfb_ddcsmodes[i].v) monitor->vmin = sisfb_ddcsmodes[i].v;
                 if(monitor->vmax < sisfb_ddcsmodes[i].v) monitor->vmax = sisfb_ddcsmodes[i].v;
@@ -356,78 +479,81 @@ static BOOLEAN sisfb_interpret_edid(struct sisfb_monitor *monitor, u8 *buffer)
           for(i = 0; i < 8; i++) {
              xres = (buffer[index] + 31) * 8;
              switch(buffer[index + 1] & 0xc0) {
-                case 0xc0: yres = (xres * 9) / 16; break;
-                case 0x80: yres = (xres * 4) /  5; break;
-                case 0x40: yres = (xres * 3) /  4; break;
-                default:   yres = xres;            break;
+                case 0xc0: yres = (xres * 9) / 16; break;
+                case 0x80: yres = (xres * 4) /  5; break;
+                case 0x40: yres = (xres * 3) /  4; break;
+                default:   yres = xres;            break;
              }
              refresh = (buffer[index + 1] & 0x3f) + 60;
              if((xres >= 640) && (yres >= 480)) {
-                 for(j = 0; j < 8; j++) {
-                   if((xres == sisfb_ddcfmodes[j].x) &&
-                      (yres == sisfb_ddcfmodes[j].y) &&
+                for(j = 0; j < 8; j++) {
+                   if((xres == sisfb_ddcfmodes[j].x) &&
+                      (yres == sisfb_ddcfmodes[j].y) &&
                       (refresh == sisfb_ddcfmodes[j].v)) {
                      if(monitor->hmin > sisfb_ddcfmodes[j].h) monitor->hmin = sisfb_ddcfmodes[j].h;
                      if(monitor->hmax < sisfb_ddcfmodes[j].h) monitor->hmax = sisfb_ddcfmodes[j].h + 1;
                      if(monitor->vmin > sisfb_ddcsmodes[j].v) monitor->vmin = sisfb_ddcsmodes[j].v;
                      if(monitor->vmax < sisfb_ddcsmodes[j].v) monitor->vmax = sisfb_ddcsmodes[j].v;
-                     if(monitor->dclockmax < sisfb_ddcsmodes[j].d) monitor->dclockmax = sisfb_ddcsmodes[i].d;
-                   }
-                }
+                     if(monitor->dclockmax < sisfb_ddcsmodes[j].d) monitor->dclockmax = sisfb_ddcsmodes[j].d;
+                   }
+                }
              }
              index += 2;
-           }
+          }
           if((monitor->hmin <= monitor->hmax) && (monitor->vmin <= monitor->vmax)) {
              monitor->datavalid = TRUE;
           }
        }
 
-       return(monitor->datavalid);
+       return monitor->datavalid;
 }
 
-static void sisfb_handle_ddc(struct sisfb_monitor *monitor, int crtno)
+static void __devinit
+sisfb_handle_ddc(struct sis_video_info *ivideo, struct sisfb_monitor *monitor, int crtno)
 {
-       USHORT  temp, i, realcrtno = crtno;
-       u8      buffer[256];
+       unsigned short temp, i, realcrtno = crtno;
+       unsigned char  buffer[256];
 
        monitor->datavalid = FALSE;
 
        if(crtno) {
-                  if(ivideo.vbflags & CRT2_LCD)      realcrtno = 1;
-          else if(ivideo.vbflags & CRT2_VGA) realcrtno = 2;
-          else return;
-       }
+          if(ivideo->vbflags & CRT2_LCD)      realcrtno = 1;
+          else if(ivideo->vbflags & CRT2_VGA) realcrtno = 2;
+          else return;
+       }
 
-       if((sisfb_crt1off) && (!crtno)) return;
+       if((ivideo->sisfb_crt1off) && (!crtno))
+               return;
 
-       temp = SiS_HandleDDC(&SiS_Pr, ivideo.vbflags, sisvga_engine, realcrtno, 0, &buffer[0]);
-       if((!temp) || (temp == 0xffff)) {
-          printk(KERN_INFO "sisfb: CRT%d DDC probing failed\n", crtno + 1);
+       temp = SiS_HandleDDC(&ivideo->SiS_Pr, ivideo->vbflags, ivideo->sisvga_engine,
+                               realcrtno, 0, &buffer[0], ivideo->vbflags2);
+       if((!temp) || (temp == 0xffff)) {
+          printk(KERN_INFO "sisfb: CRT%d DDC probing failed\n", crtno + 1);
           return;
-       } else {
-          printk(KERN_INFO "sisfb: CRT%d DDC supported\n", crtno + 1);
-          printk(KERN_INFO "sisfb: CRT%d DDC level: %s%s%s%s\n",
-               crtno + 1,
-               (temp & 0x1a) ? "" : "[none of the supported]",
-               (temp & 0x02) ? "2 " : "",
-               (temp & 0x08) ? "D&P" : "",
-               (temp & 0x10) ? "FPDI-2" : "");
-          if(temp & 0x02) {
+       } else {
+          printk(KERN_INFO "sisfb: CRT%d DDC supported\n", crtno + 1);
+          printk(KERN_INFO "sisfb: CRT%d DDC level: %s%s%s%s\n",
+               crtno + 1,
+               (temp & 0x1a) ? "" : "[none of the supported]",
+               (temp & 0x02) ? "2 " : "",
+               (temp & 0x08) ? "D&P" : "",
+               (temp & 0x10) ? "FPDI-2" : "");
+          if(temp & 0x02) {
              i = 3;  /* Number of retrys */
              do {
-                temp = SiS_HandleDDC(&SiS_Pr, ivideo.vbflags, sisvga_engine,
-                                    realcrtno, 1, &buffer[0]);
+                temp = SiS_HandleDDC(&ivideo->SiS_Pr, ivideo->vbflags, ivideo->sisvga_engine,
+                                    realcrtno, 1, &buffer[0], ivideo->vbflags2);
              } while((temp) && i--);
-              if(!temp) {
-                if(sisfb_interpret_edid(monitor, &buffer[0])) {
+             if(!temp) {
+                if(sisfb_interpret_edid(monitor, &buffer[0])) {
                    printk(KERN_INFO "sisfb: Monitor range H %d-%dKHz, V %d-%dHz, Max. dotclock %dMHz\n",
-                       monitor->hmin, monitor->hmax, monitor->vmin, monitor->vmax,
+                       monitor->hmin, monitor->hmax, monitor->vmin, monitor->vmax,
                        monitor->dclockmax / 1000);
                 } else {
-                   printk(KERN_INFO "sisfb: CRT%d DDC EDID corrupt\n", crtno + 1);
-                }
+                   printk(KERN_INFO "sisfb: CRT%d DDC EDID corrupt\n", crtno + 1);
+                }
              } else {
-                printk(KERN_INFO "sisfb: CRT%d DDC reading failed\n", crtno + 1);
+                printk(KERN_INFO "sisfb: CRT%d DDC reading failed\n", crtno + 1);
              }
           } else {
              printk(KERN_INFO "sisfb: VESA D&P and FPDI-2 not supported yet\n");
@@ -435,578 +561,643 @@ static void sisfb_handle_ddc(struct sisfb_monitor *monitor, int crtno)
        }
 }
 
-static void sisfb_search_vesamode(unsigned int vesamode, BOOLEAN quiet)
+/* -------------- Mode validation --------------- */
+
+static BOOLEAN
+sisfb_verify_rate(struct sis_video_info *ivideo, struct sisfb_monitor *monitor,
+               int mode_idx, int rate_idx, int rate)
 {
-       int i = 0, j = 0;
+       int htotal, vtotal;
+       unsigned int dclock, hsync;
 
-       if(vesamode == 0) {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-               sisfb_mode_idx = MODE_INDEX_NONE;
-#else
-               if(!quiet)
-                  printk(KERN_ERR "sisfb: Mode 'none' not supported anymore. Using default.\n");
-               sisfb_mode_idx = DEFAULT_MODE;
+       if(!monitor->datavalid)
+               return TRUE;
+
+       if(mode_idx < 0)
+               return FALSE;
+
+       /* Skip for 320x200, 320x240, 640x400 */
+       switch(sisbios_mode[mode_idx].mode_no[ivideo->mni]) {
+       case 0x59:
+       case 0x41:
+       case 0x4f:
+       case 0x50:
+       case 0x56:
+       case 0x53:
+       case 0x2f:
+       case 0x5d:
+       case 0x5e:
+               return TRUE;
+#ifdef CONFIG_FB_SIS_315
+       case 0x5a:
+       case 0x5b:
+               if(ivideo->sisvga_engine == SIS_315_VGA) return TRUE;
 #endif
-               return;
        }
 
-       vesamode &= 0x1dff;  /* Clean VESA mode number from other flags */
+       if(rate < (monitor->vmin - 1))
+               return FALSE;
+       if(rate > (monitor->vmax + 1))
+               return FALSE;
 
-       while(sisbios_mode[i++].mode_no != 0) {
-               if( (sisbios_mode[i-1].vesa_mode_no_1 == vesamode) ||
-                   (sisbios_mode[i-1].vesa_mode_no_2 == vesamode) ) {
-                   if(sisfb_fstn) {
-                      if(sisbios_mode[i-1].mode_no == 0x50 ||
-                         sisbios_mode[i-1].mode_no == 0x56 ||
-                         sisbios_mode[i-1].mode_no == 0x53) continue;
-                   } else {
-                      if(sisbios_mode[i-1].mode_no == 0x5a ||
-                         sisbios_mode[i-1].mode_no == 0x5b) continue;
-                   }
-                   sisfb_mode_idx = i - 1;
-                   j = 1;
-                   break;
-               }
+       if(sisfb_gettotalfrommode(&ivideo->SiS_Pr,
+                                 sisbios_mode[mode_idx].mode_no[ivideo->mni],
+                                 &htotal, &vtotal, rate_idx)) {
+               dclock = (htotal * vtotal * rate) / 1000;
+               if(dclock > (monitor->dclockmax + 1000))
+                       return FALSE;
+               hsync = dclock / htotal;
+               if(hsync < (monitor->hmin - 1))
+                       return FALSE;
+               if(hsync > (monitor->hmax + 1))
+                       return FALSE;
+        } else {
+               return FALSE;
        }
-       if((!j) && !quiet) printk(KERN_ERR "sisfb: Invalid VESA mode 0x%x'\n", vesamode);
+       return TRUE;
 }
 
-static void sisfb_search_mode(char *name, BOOLEAN quiet)
+static int
+sisfb_validate_mode(struct sis_video_info *ivideo, int myindex, u32 vbflags)
 {
-       int i = 0;
-       unsigned int j = 0, xres = 0, yres = 0, depth = 0, rate = 0;
-       char strbuf[16], strbuf1[20];
-       char *nameptr = name;
+       u16 xres=0, yres, myres;
 
-       if(name == NULL) {
-          if(!quiet)
-             printk(KERN_ERR "sisfb: Internal error, using default mode.\n");
-          sisfb_mode_idx = DEFAULT_MODE;
-          return;
+#ifdef CONFIG_FB_SIS_300
+       if(ivideo->sisvga_engine == SIS_300_VGA) {
+               if(!(sisbios_mode[myindex].chipset & MD_SIS300))
+                       return -1 ;
        }
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-        if (!strnicmp(name, sisbios_mode[MODE_INDEX_NONE].name, strlen(name))) {
-          if(!quiet)
-             printk(KERN_ERR "sisfb: Mode 'none' not supported anymore. Using default.\n");
-          sisfb_mode_idx = DEFAULT_MODE;
-          return;
+#endif
+#ifdef CONFIG_FB_SIS_315
+       if(ivideo->sisvga_engine == SIS_315_VGA) {
+               if(!(sisbios_mode[myindex].chipset & MD_SIS315))
+                       return -1;
        }
 #endif
-       if(strlen(name) <= 19) {
-          strcpy(strbuf1, name);
-          for(i=0; i<strlen(strbuf1); i++) {
-             if(strbuf1[i] < '0' || strbuf1[i] > '9') strbuf1[i] = ' ';
-          }
 
-          /* This does some fuzzy mode naming detection */
-          if(sscanf(strbuf1, "%u %u %u %u", &xres, &yres, &depth, &rate) == 4) {
-             if((rate <= 32) || (depth > 32)) {
-                j = rate; rate = depth; depth = j;
-             }
-             sprintf(strbuf, "%ux%ux%u", xres, yres, depth);
-             nameptr = strbuf;
-             ivideo.refresh_rate = sisfb_parm_rate = rate;
-          } else if(sscanf(strbuf1, "%u %u %u", &xres, &yres, &depth) == 3) {
-             sprintf(strbuf, "%ux%ux%u", xres, yres, depth);
-             nameptr = strbuf;
-          } else {
-             xres = 0;
-             if((sscanf(strbuf1, "%u %u", &xres, &yres) == 2) && (xres != 0)) {
-                sprintf(strbuf, "%ux%ux8", xres, yres);
-                nameptr = strbuf;
-             } else {
-                sisfb_search_vesamode(simple_strtoul(name, NULL, 0), quiet);
-                return;
-             }
-          }
-       }
+       myres = sisbios_mode[myindex].yres;
 
-       i = 0; j = 0;
-       while(sisbios_mode[i].mode_no != 0) {
-               if(!strnicmp(nameptr, sisbios_mode[i++].name, strlen(nameptr))) {
-                  if(sisfb_fstn) {
-                     if(sisbios_mode[i-1].mode_no == 0x50 ||
-                        sisbios_mode[i-1].mode_no == 0x56 ||
-                        sisbios_mode[i-1].mode_no == 0x53) continue;
-                  } else {
-                     if(sisbios_mode[i-1].mode_no == 0x5a ||
-                        sisbios_mode[i-1].mode_no == 0x5b) continue;
-                  }
-                  sisfb_mode_idx = i - 1;
-                  j = 1;
-                  break;
+       switch(vbflags & VB_DISPTYPE_DISP2) {
+
+       case CRT2_LCD:
+               xres = ivideo->lcdxres; yres = ivideo->lcdyres;
+
+               if((ivideo->SiS_Pr.SiS_CustomT != CUT_PANEL848) &&
+                  (ivideo->SiS_Pr.SiS_CustomT != CUT_PANEL856)) {
+                       if(sisbios_mode[myindex].xres > xres)
+                               return -1;
+                       if(myres > yres)
+                               return -1;
+               }
+
+               if(ivideo->sisfb_fstn) {
+                       if(sisbios_mode[myindex].xres == 320) {
+                               if(myres == 240) {
+                                       switch(sisbios_mode[myindex].mode_no[1]) {
+                                               case 0x50: myindex = MODE_FSTN_8;  break;
+                                               case 0x56: myindex = MODE_FSTN_16; break;
+                                               case 0x53: return -1;
+                                       }
+                               }
+                       }
+               }
+
+               if(SiS_GetModeID_LCD(ivideo->sisvga_engine, vbflags, sisbios_mode[myindex].xres,
+                               sisbios_mode[myindex].yres, 0, ivideo->sisfb_fstn,
+                               ivideo->SiS_Pr.SiS_CustomT, xres, yres, ivideo->vbflags2) < 0x14) {
+                       return -1;
+               }
+               break;
+
+       case CRT2_TV:
+               if(SiS_GetModeID_TV(ivideo->sisvga_engine, vbflags, sisbios_mode[myindex].xres,
+                               sisbios_mode[myindex].yres, 0, ivideo->vbflags2) < 0x14) {
+                       return -1;
+               }
+               break;
+
+       case CRT2_VGA:
+               if(SiS_GetModeID_VGA2(ivideo->sisvga_engine, vbflags, sisbios_mode[myindex].xres,
+                               sisbios_mode[myindex].yres, 0, ivideo->vbflags2) < 0x14) {
+                       return -1;
                }
+               break;
        }
-       if((!j) && !quiet) printk(KERN_ERR "sisfb: Invalid mode '%s'\n", nameptr);
 
+       return myindex;
 }
 
-static int sisfb_validate_mode(int myindex, unsigned long vbflags)
+static u8
+sisfb_search_refresh_rate(struct sis_video_info *ivideo, unsigned int rate, int mode_idx)
 {
-   u16 xres, yres, myres;
-
-#ifdef CONFIG_FB_SIS_300
-   if(sisvga_engine == SIS_300_VGA) {
-      if(!(sisbios_mode[myindex].chipset & MD_SIS300)) return(-1);
-   }
-#endif
-#ifdef CONFIG_FB_SIS_315
-   if(sisvga_engine == SIS_315_VGA) {
-      if(!(sisbios_mode[myindex].chipset & MD_SIS315)) return(-1);
-   }
-#endif
-
-   myres = sisbios_mode[myindex].yres;
-
-   switch (vbflags & VB_DISPTYPE_DISP2) {
-     case CRT2_LCD:
-       switch (sishw_ext.ulCRT2LCDType) {
-       case LCD_640x480:  xres =  640; yres =  480;  break;
-       case LCD_800x600:  xres =  800; yres =  600;  break;
-        case LCD_1024x600: xres = 1024; yres =  600;  break;
-       case LCD_1024x768: xres = 1024; yres =  768;  break;
-       case LCD_1152x768: xres = 1152; yres =  768;  break;
-       case LCD_1280x960: xres = 1280; yres =  960;  break;
-       case LCD_1280x768: xres = 1280; yres =  768;  break;
-       case LCD_1280x1024:xres = 1280; yres = 1024;  break;
-       case LCD_1400x1050:xres = 1400; yres = 1050;  break;
-       case LCD_1600x1200:xres = 1600; yres = 1200;  break;
-       case LCD_320x480:  xres =  320; yres =  480;  break; /* FSTN (old) */
-       case LCD_640x480_2:
-       case LCD_640x480_3:xres =  640; yres =  480;  break; /* FSTN (new) */
-       default:           xres =    0; yres =    0;  break;
-       }
-
-       if(SiS_Pr.SiS_CustomT == CUT_BARCO1366) {
-          xres = 1360; yres = 1024;
-       }
-
-       if(SiS_Pr.SiS_CustomT == CUT_PANEL848) {
-          xres = 848;  yres =  480;
+       int i = 0;
+       u16 xres = sisbios_mode[mode_idx].xres;
+       u16 yres = sisbios_mode[mode_idx].yres;
+
+       ivideo->rate_idx = 0;
+       while((sisfb_vrate[i].idx != 0) && (sisfb_vrate[i].xres <= xres)) {
+               if((sisfb_vrate[i].xres == xres) && (sisfb_vrate[i].yres == yres)) {
+                       if(sisfb_vrate[i].refresh == rate) {
+                               ivideo->rate_idx = sisfb_vrate[i].idx;
+                               break;
+                       } else if(sisfb_vrate[i].refresh > rate) {
+                               if((sisfb_vrate[i].refresh - rate) <= 3) {
+                                       DPRINTK("sisfb: Adjusting rate from %d up to %d\n",
+                                               rate, sisfb_vrate[i].refresh);
+                                       ivideo->rate_idx = sisfb_vrate[i].idx;
+                                       ivideo->refresh_rate = sisfb_vrate[i].refresh;
+                               } else if(((rate - sisfb_vrate[i-1].refresh) <= 2)
+                                               && (sisfb_vrate[i].idx != 1)) {
+                                       DPRINTK("sisfb: Adjusting rate from %d down to %d\n",
+                                               rate, sisfb_vrate[i-1].refresh);
+                                       ivideo->rate_idx = sisfb_vrate[i-1].idx;
+                                       ivideo->refresh_rate = sisfb_vrate[i-1].refresh;
+                               }
+                               break;
+                       } else if((rate - sisfb_vrate[i].refresh) <= 2) {
+                               DPRINTK("sisfb: Adjusting rate from %d down to %d\n",
+                                               rate, sisfb_vrate[i].refresh);
+                               ivideo->rate_idx = sisfb_vrate[i].idx;
+                               break;
+                       }
+               }
+               i++;
+       }
+       if(ivideo->rate_idx > 0) {
+               return ivideo->rate_idx;
        } else {
-          if(sisbios_mode[myindex].xres > xres) return(-1);
-           if(myres > yres) return(-1);
+               printk(KERN_INFO "sisfb: Unsupported rate %d for %dx%d\n",
+                               rate, xres, yres);
+               return 0;
        }
+}
 
-       if(vbflags & (VB_LVDS | VB_30xBDH)) {
-          if(sisbios_mode[myindex].xres == 320) {
-             if((myres == 240) || (myres == 480)) {
-                if(!sisfb_fstn) {
-                   if(sisbios_mode[myindex].mode_no == 0x5a ||
-                      sisbios_mode[myindex].mode_no == 0x5b)
-                      return(-1);
-                } else {
-                   if(sisbios_mode[myindex].mode_no == 0x50 ||
-                      sisbios_mode[myindex].mode_no == 0x56 ||
-                      sisbios_mode[myindex].mode_no == 0x53)
-                      return(-1);
-                }
-             }
-          }
-       }
+static BOOLEAN
+sisfb_bridgeisslave(struct sis_video_info *ivideo)
+{
+       unsigned char P1_00;
 
-       if(SiS_GetModeID_LCD(sisvga_engine, vbflags, sisbios_mode[myindex].xres, sisbios_mode[myindex].yres,
-                            0, sisfb_fstn, SiS_Pr.SiS_CustomT, xres, yres) < 0x14) {
-          return(-1);
-       }
-       break;
+       if(!(ivideo->vbflags2 & VB2_VIDEOBRIDGE))
+               return FALSE;
 
-     case CRT2_TV:
-       if(SiS_GetModeID_TV(sisvga_engine, vbflags, sisbios_mode[myindex].xres,
-                           sisbios_mode[myindex].yres, 0) < 0x14) {
-          return(-1);
+       inSISIDXREG(SISPART1,0x00,P1_00);
+       if( ((ivideo->sisvga_engine == SIS_300_VGA) && (P1_00 & 0xa0) == 0x20) ||
+           ((ivideo->sisvga_engine == SIS_315_VGA) && (P1_00 & 0x50) == 0x10) ) {
+               return TRUE;
+       } else {
+               return FALSE;
        }
-       break;
+}
 
-     case CRT2_VGA:
-        if(SiS_GetModeID_VGA2(sisvga_engine, vbflags, sisbios_mode[myindex].xres,
-                           sisbios_mode[myindex].yres, 0) < 0x14) {
-          return(-1);
-       }
-       break;
-     }
+static BOOLEAN
+sisfballowretracecrt1(struct sis_video_info *ivideo)
+{
+       u8 temp;
+
+       inSISIDXREG(SISCR,0x17,temp);
+       if(!(temp & 0x80))
+               return FALSE;
 
-     return(myindex);
+       inSISIDXREG(SISSR,0x1f,temp);
+       if(temp & 0xc0)
+               return FALSE;
+
+       return TRUE;
 }
 
-static void sisfb_search_crt2type(const char *name)
+static BOOLEAN
+sisfbcheckvretracecrt1(struct sis_video_info *ivideo)
 {
-       int i = 0;
+       if(!sisfballowretracecrt1(ivideo))
+               return FALSE;
 
-       if(name == NULL)
+       if(inSISREG(SISINPSTAT) & 0x08)
+               return TRUE;
+       else
+               return FALSE;
+}
+
+static void
+sisfbwaitretracecrt1(struct sis_video_info *ivideo)
+{
+       int watchdog;
+
+       if(!sisfballowretracecrt1(ivideo))
                return;
 
-       while(sis_crt2type[i].type_no != -1) {
-               if (!strnicmp(name, sis_crt2type[i].name, strlen(sis_crt2type[i].name))) {
-                       sisfb_crt2type = sis_crt2type[i].type_no;
-                       sisfb_tvplug = sis_crt2type[i].tvplug_no;
-                       sisfb_dstn = (sis_crt2type[i].flags & FL_550_DSTN) ? 1 : 0;
-                       sisfb_fstn = (sis_crt2type[i].flags & FL_550_FSTN) ? 1 : 0;
-                       break;
-               }
-               i++;
-       }
-       if(sisfb_crt2type < 0)
-               printk(KERN_ERR "sisfb: Invalid CRT2 type: %s\n", name);
-        if(ivideo.chip != SIS_550) {
-          sisfb_dstn = sisfb_fstn = 0;
-       }
+       watchdog = 65536;
+       while((!(inSISREG(SISINPSTAT) & 0x08)) && --watchdog);
+       watchdog = 65536;
+       while((inSISREG(SISINPSTAT) & 0x08) && --watchdog);
 }
 
-static void sisfb_search_queuemode(const char *name)
+static BOOLEAN
+sisfbcheckvretracecrt2(struct sis_video_info *ivideo)
 {
-       int i = 0;
+       unsigned char temp, reg;
 
-       if(name == NULL)
-               return;
+       switch(ivideo->sisvga_engine) {
+       case SIS_300_VGA: reg = 0x25; break;
+       case SIS_315_VGA: reg = 0x30; break;
+       default:          return FALSE;
+       }
 
-       while (sis_queuemode[i].type_no != -1) {
-               if (!strnicmp(name, sis_queuemode[i].name, strlen(sis_queuemode[i].name))) {
-                       sisfb_queuemode = sis_queuemode[i].type_no;
-                       break;
+       inSISIDXREG(SISPART1, reg, temp);
+       if(temp & 0x02)
+               return TRUE;
+       else
+               return FALSE;
+}
+
+static BOOLEAN
+sisfb_CheckVBRetrace(struct sis_video_info *ivideo)
+{
+       if(ivideo->currentvbflags & VB_DISPTYPE_DISP2) {
+               if(!sisfb_bridgeisslave(ivideo)) {
+                       return sisfbcheckvretracecrt2(ivideo);
                }
-               i++;
        }
-       if (sisfb_queuemode < 0)
-               printk(KERN_ERR "sisfb: Invalid queuemode type: %s\n", name);
+       return sisfbcheckvretracecrt1(ivideo);
 }
 
-static u8 sisfb_search_refresh_rate(unsigned int rate, int mode_idx)
+static u32
+sisfb_setupvbblankflags(struct sis_video_info *ivideo, u32 *vcount, u32 *hcount)
 {
-       u16 xres, yres;
-       int i = 0;
+       u8 idx, reg1, reg2, reg3, reg4;
+       u32 ret = 0;
+
+       (*vcount) = (*hcount) = 0;
+
+       if((ivideo->currentvbflags & VB_DISPTYPE_DISP2) && (!(sisfb_bridgeisslave(ivideo)))) {
+
+               ret |= (FB_VBLANK_HAVE_VSYNC  |
+                       FB_VBLANK_HAVE_HBLANK |
+                       FB_VBLANK_HAVE_VBLANK |
+                       FB_VBLANK_HAVE_VCOUNT |
+                       FB_VBLANK_HAVE_HCOUNT);
+               switch(ivideo->sisvga_engine) {
+                       case SIS_300_VGA: idx = 0x25; break;
+                       default:
+                       case SIS_315_VGA: idx = 0x30; break;
+               }
+               inSISIDXREG(SISPART1,(idx+0),reg1); /* 30 */
+               inSISIDXREG(SISPART1,(idx+1),reg2); /* 31 */
+               inSISIDXREG(SISPART1,(idx+2),reg3); /* 32 */
+               inSISIDXREG(SISPART1,(idx+3),reg4); /* 33 */
+               if(reg1 & 0x01) ret |= FB_VBLANK_VBLANKING;
+               if(reg1 & 0x02) ret |= FB_VBLANK_VSYNCING;
+               if(reg4 & 0x80) ret |= FB_VBLANK_HBLANKING;
+               (*vcount) = reg3 | ((reg4 & 0x70) << 4);
+               (*hcount) = reg2 | ((reg4 & 0x0f) << 8);
+
+       } else if(sisfballowretracecrt1(ivideo)) {
+
+               ret |= (FB_VBLANK_HAVE_VSYNC  |
+                       FB_VBLANK_HAVE_VBLANK |
+                       FB_VBLANK_HAVE_VCOUNT |
+                       FB_VBLANK_HAVE_HCOUNT);
+               reg1 = inSISREG(SISINPSTAT);
+               if(reg1 & 0x08) ret |= FB_VBLANK_VSYNCING;
+               if(reg1 & 0x01) ret |= FB_VBLANK_VBLANKING;
+               inSISIDXREG(SISCR,0x20,reg1);
+               inSISIDXREG(SISCR,0x1b,reg1);
+               inSISIDXREG(SISCR,0x1c,reg2);
+               inSISIDXREG(SISCR,0x1d,reg3);
+               (*vcount) = reg2 | ((reg3 & 0x07) << 8);
+               (*hcount) = (reg1 | ((reg3 & 0x10) << 4)) << 3;
+       }
+
+       return ret;
+}
 
-       xres = sisbios_mode[mode_idx].xres;
-       yres = sisbios_mode[mode_idx].yres;
+static int
+sisfb_myblank(struct sis_video_info *ivideo, int blank)
+{
+       u8 sr01, sr11, sr1f, cr63=0, p2_0, p1_13;
+       BOOLEAN backlight = TRUE;
+
+       switch(blank) {
+               case FB_BLANK_UNBLANK:  /* on */
+                       sr01  = 0x00;
+                       sr11  = 0x00;
+                       sr1f  = 0x00;
+                       cr63  = 0x00;
+                       p2_0  = 0x20;
+                       p1_13 = 0x00;
+                       backlight = TRUE;
+                       break;
+               case FB_BLANK_NORMAL:   /* blank */
+                       sr01  = 0x20;
+                       sr11  = 0x00;
+                       sr1f  = 0x00;
+                       cr63  = 0x00;
+                       p2_0  = 0x20;
+                       p1_13 = 0x00;
+                       backlight = TRUE;
+                       break;
+               case FB_BLANK_VSYNC_SUSPEND:    /* no vsync */
+                       sr01  = 0x20;
+                       sr11  = 0x08;
+                       sr1f  = 0x80;
+                       cr63  = 0x40;
+                       p2_0  = 0x40;
+                       p1_13 = 0x80;
+                       backlight = FALSE;
+                       break;
+               case FB_BLANK_HSYNC_SUSPEND:    /* no hsync */
+                       sr01  = 0x20;
+                       sr11  = 0x08;
+                       sr1f  = 0x40;
+                       cr63  = 0x40;
+                       p2_0  = 0x80;
+                       p1_13 = 0x40;
+                       backlight = FALSE;
+                       break;
+               case FB_BLANK_POWERDOWN:        /* off */
+                       sr01  = 0x20;
+                       sr11  = 0x08;
+                       sr1f  = 0xc0;
+                       cr63  = 0x40;
+                       p2_0  = 0xc0;
+                       p1_13 = 0xc0;
+                       backlight = FALSE;
+                       break;
+               default:
+                       return 1;
+       }
 
-       sisfb_rate_idx = 0;
-       while ((sisfb_vrate[i].idx != 0) && (sisfb_vrate[i].xres <= xres)) {
-               if ((sisfb_vrate[i].xres == xres) && (sisfb_vrate[i].yres == yres)) {
-                       if (sisfb_vrate[i].refresh == rate) {
-                               sisfb_rate_idx = sisfb_vrate[i].idx;
-                               break;
-                       } else if (sisfb_vrate[i].refresh > rate) {
-                               if ((sisfb_vrate[i].refresh - rate) <= 3) {
-                                       DPRINTK("sisfb: Adjusting rate from %d up to %d\n",
-                                               rate, sisfb_vrate[i].refresh);
-                                       sisfb_rate_idx = sisfb_vrate[i].idx;
-                                       ivideo.refresh_rate = sisfb_vrate[i].refresh;
-                               } else if (((rate - sisfb_vrate[i-1].refresh) <= 2)
-                                               && (sisfb_vrate[i].idx != 1)) {
-                                       DPRINTK("sisfb: Adjusting rate from %d down to %d\n",
-                                               rate, sisfb_vrate[i-1].refresh);
-                                       sisfb_rate_idx = sisfb_vrate[i-1].idx;
-                                       ivideo.refresh_rate = sisfb_vrate[i-1].refresh;
-                               } 
-                               break;
-                       } else if((rate - sisfb_vrate[i].refresh) <= 2) {
-                               DPRINTK("sisfb: Adjusting rate from %d down to %d\n",
-                                               rate, sisfb_vrate[i].refresh);
-                               sisfb_rate_idx = sisfb_vrate[i].idx;
-                               break;
-                       }
+       if(ivideo->currentvbflags & VB_DISPTYPE_CRT1) {
+
+               if( (!ivideo->sisfb_thismonitor.datavalid) ||
+                   ((ivideo->sisfb_thismonitor.datavalid) &&
+                    (ivideo->sisfb_thismonitor.feature & 0xe0))) {
+
+                       if(ivideo->sisvga_engine == SIS_315_VGA) {
+                               setSISIDXREG(SISCR, ivideo->SiS_Pr.SiS_MyCR63, 0xbf, cr63);
+                       }
+
+                       if(!(sisfb_bridgeisslave(ivideo))) {
+                               setSISIDXREG(SISSR, 0x01, ~0x20, sr01);
+                               setSISIDXREG(SISSR, 0x1f, 0x3f, sr1f);
+                       }
                }
-               i++;
-       }
-       if (sisfb_rate_idx > 0) {
-               return sisfb_rate_idx;
-       } else {
-               printk(KERN_INFO
-                       "sisfb: Unsupported rate %d for %dx%d\n", rate, xres, yres);
-               return 0;
+
        }
-}
 
-static void sisfb_search_tvstd(const char *name)
-{
-       int i = 0;
+       if(ivideo->currentvbflags & CRT2_LCD) {
 
-       if(name == NULL)
-               return;
+               if(ivideo->vbflags2 & VB2_SISLVDSBRIDGE) {
+                       if(backlight) {
+                               SiS_SiS30xBLOn(&ivideo->SiS_Pr);
+                       } else {
+                               SiS_SiS30xBLOff(&ivideo->SiS_Pr);
+                       }
+               } else if(ivideo->sisvga_engine == SIS_315_VGA) {
+#ifdef CONFIG_FB_SIS_315
+                       if(ivideo->vbflags2 & VB2_CHRONTEL) {
+                               if(backlight) {
+                                       SiS_Chrontel701xBLOn(&ivideo->SiS_Pr);
+                               } else {
+                                       SiS_Chrontel701xBLOff(&ivideo->SiS_Pr);
+                               }
+                       }
+#endif
+               }
 
-       while (sis_tvtype[i].type_no != -1) {
-               if (!strnicmp(name, sis_tvtype[i].name, strlen(sis_tvtype[i].name))) {
-                       ivideo.vbflags |= sis_tvtype[i].type_no;
-                       break;
+               if(((ivideo->sisvga_engine == SIS_300_VGA) &&
+                   (ivideo->vbflags2 & (VB2_301|VB2_30xBDH|VB2_LVDS))) ||
+                  ((ivideo->sisvga_engine == SIS_315_VGA) &&
+                   ((ivideo->vbflags2 & (VB2_LVDS | VB2_CHRONTEL)) == VB2_LVDS))) {
+                       setSISIDXREG(SISSR, 0x11, ~0x0c, sr11);
                }
-               i++;
+
+               if(ivideo->sisvga_engine == SIS_300_VGA) {
+                       if((ivideo->vbflags2 & VB2_30xB) &&
+                          (!(ivideo->vbflags2 & VB2_30xBDH))) {
+                               setSISIDXREG(SISPART1, 0x13, 0x3f, p1_13);
+                       }
+               } else if(ivideo->sisvga_engine == SIS_315_VGA) {
+                       if((ivideo->vbflags2 & VB2_30xB) &&
+                          (!(ivideo->vbflags2 & VB2_30xBDH))) {
+                               setSISIDXREG(SISPART2, 0x00, 0x1f, p2_0);
+                       }
+               }
+
+       } else if(ivideo->currentvbflags & CRT2_VGA) {
+
+               if(ivideo->vbflags2 & VB2_30xB) {
+                       setSISIDXREG(SISPART2, 0x00, 0x1f, p2_0);
+               }
+
        }
+
+       return 0;
 }
 
-static void sisfb_search_specialtiming(const char *name)
+/* ------------- Callbacks from init.c/init301.c  -------------- */
+
+#ifdef CONFIG_FB_SIS_300
+unsigned int
+sisfb_read_nbridge_pci_dword(struct SiS_Private *SiS_Pr, int reg)
 {
-       int i = 0;
-       BOOLEAN found = FALSE;
+   struct sis_video_info *ivideo = (struct sis_video_info *)SiS_Pr->ivideo;
+   u32 val = 0;
 
-       if(name == NULL)
-               return;
+   pci_read_config_dword(ivideo->nbridge, reg, &val);
+   return (unsigned int)val;
+}
 
-       if(!strnicmp(name, "none", 4)) {
-               SiS_Pr.SiS_CustomT = CUT_FORCENONE;
-               printk(KERN_DEBUG "sisfb: Special timing disabled\n");
-       } else {
-          while(mycustomttable[i].chipID != 0) {
-             if(!strnicmp(name,mycustomttable[i].optionName, strlen(mycustomttable[i].optionName))) {
-                SiS_Pr.SiS_CustomT = mycustomttable[i].SpecialID;
-                found = TRUE;
-                printk(KERN_INFO "sisfb: Special timing for %s %s forced (\"%s\")\n",
-                       mycustomttable[i].vendorName, mycustomttable[i].cardName,
-                       mycustomttable[i].optionName);
-                break;
-             }
-             i++;
-          }
-          if(!found) {
-             printk(KERN_WARNING "sisfb: Invalid SpecialTiming parameter, valid are:");
-             printk(KERN_WARNING "\t\"none\" (to disable special timings)\n");
-             i = 0;
-             while(mycustomttable[i].chipID != 0) {
-                printk(KERN_WARNING "\t\"%s\" (for %s %s)\n",
-                    mycustomttable[i].optionName,
-                    mycustomttable[i].vendorName,
-                    mycustomttable[i].cardName);
-                i++;
-             }
-           }
-       }
+void
+sisfb_write_nbridge_pci_dword(struct SiS_Private *SiS_Pr, int reg, unsigned int val)
+{
+   struct sis_video_info *ivideo = (struct sis_video_info *)SiS_Pr->ivideo;
+
+   pci_write_config_dword(ivideo->nbridge, reg, (u32)val);
 }
 
-static BOOLEAN sisfb_bridgeisslave(void)
+unsigned int
+sisfb_read_lpc_pci_dword(struct SiS_Private *SiS_Pr, int reg)
 {
-   unsigned char P1_00;
+   struct sis_video_info *ivideo = (struct sis_video_info *)SiS_Pr->ivideo;
+   u32 val = 0;
 
-   if(!(ivideo.vbflags & VB_VIDEOBRIDGE)) return FALSE;
+   if(!ivideo->lpcdev) return 0;
 
-   inSISIDXREG(SISPART1,0x00,P1_00);
-   if( ((sisvga_engine == SIS_300_VGA) && (P1_00 & 0xa0) == 0x20) ||
-       ((sisvga_engine == SIS_315_VGA) && (P1_00 & 0x50) == 0x10) ) {
-          return TRUE;
-   } else {
-           return FALSE;
-   }
+   pci_read_config_dword(ivideo->lpcdev, reg, &val);
+   return (unsigned int)val;
 }
+#endif
 
-static BOOLEAN sisfballowretracecrt1(void)
+#ifdef CONFIG_FB_SIS_315
+void
+sisfb_write_nbridge_pci_byte(struct SiS_Private *SiS_Pr, int reg, unsigned char val)
 {
-   unsigned char temp;
+   struct sis_video_info *ivideo = (struct sis_video_info *)SiS_Pr->ivideo;
 
-   inSISIDXREG(SISCR,0x17,temp);
-   if(!(temp & 0x80)) return FALSE;
+   pci_write_config_byte(ivideo->nbridge, reg, (u8)val);
+}
+
+unsigned int
+sisfb_read_mio_pci_word(struct SiS_Private *SiS_Pr, int reg)
+{
+   struct sis_video_info *ivideo = (struct sis_video_info *)SiS_Pr->ivideo;
+   u16 val = 0;
 
-   inSISIDXREG(SISSR,0x1f,temp);
-   if(temp & 0xc0) return FALSE;
+   if(!ivideo->lpcdev) return 0;
 
-   return TRUE;
+   pci_read_config_word(ivideo->lpcdev, reg, &val);
+   return (unsigned int)val;
 }
+#endif
 
-static BOOLEAN sisfbcheckvretracecrt1(void)
-{
+/* ----------- FBDev related routines for all series ----------- */
 
-   if(!sisfballowretracecrt1()) return FALSE;
+static int
+sisfb_get_cmap_len(const struct fb_var_screeninfo *var)
+{
+       return (var->bits_per_pixel == 8) ? 256 : 16;
+}
 
-   if(inSISREG(SISINPSTAT) & 0x08) return TRUE;
-   else                           return FALSE;
+static void
+sisfb_set_vparms(struct sis_video_info *ivideo)
+{
+       switch(ivideo->video_bpp) {
+       case 8:
+               ivideo->DstColor = 0x0000;
+               ivideo->SiS310_AccelDepth = 0x00000000;
+               ivideo->video_cmap_len = 256;
+               break;
+       case 16:
+               ivideo->DstColor = 0x8000;
+               ivideo->SiS310_AccelDepth = 0x00010000;
+               ivideo->video_cmap_len = 16;
+               break;
+       case 32:
+               ivideo->DstColor = 0xC000;
+               ivideo->SiS310_AccelDepth = 0x00020000;
+               ivideo->video_cmap_len = 16;
+               break;
+       default:
+               ivideo->video_cmap_len = 16;
+               printk(KERN_ERR "sisfb: Unsupported depth %d", ivideo->video_bpp);
+               ivideo->accel = 0;
+       }
 }
 
-static void sisfbwaitretracecrt1(void)
+static int
+sisfb_calc_maxyres(struct sis_video_info *ivideo, struct fb_var_screeninfo *var)
 {
-   int watchdog;
+       int maxyres = ivideo->sisfb_mem / (var->xres_virtual * (var->bits_per_pixel >> 3));
 
-   if(!sisfballowretracecrt1()) return;
+       if(maxyres > 32767) maxyres = 32767;
 
-   watchdog = 65536;
-   while((!(inSISREG(SISINPSTAT) & 0x08)) && --watchdog);
-   watchdog = 65536;
-   while((inSISREG(SISINPSTAT) & 0x08) && --watchdog);
+       return maxyres;
 }
 
-static BOOLEAN sisfbcheckvretracecrt2(void)
+static void
+sisfb_calc_pitch(struct sis_video_info *ivideo, struct fb_var_screeninfo *var)
 {
-   unsigned char temp, reg;
-
-   switch(sisvga_engine) {
-   case SIS_300_VGA:
-       reg = 0x25;
-       break;
-   case SIS_315_VGA:
-       reg = 0x30;
-       break;
-   default:
-        return FALSE;
-   }
-
-   inSISIDXREG(SISPART1, reg, temp);
-   if(temp & 0x02) return FALSE;
-   else           return TRUE;
+       ivideo->video_linelength = var->xres_virtual * (var->bits_per_pixel >> 3);
+       ivideo->scrnpitchCRT1 = ivideo->video_linelength;
+       if(!(ivideo->currentvbflags & CRT1_LCDA)) {
+               if((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
+                       ivideo->scrnpitchCRT1 <<= 1;
+               }
+       }
 }
 
-static BOOLEAN sisfb_CheckVBRetrace(void)
+static void
+sisfb_set_pitch(struct sis_video_info *ivideo)
 {
-   if(ivideo.currentvbflags & VB_DISPTYPE_DISP2) {
-      if(sisfb_bridgeisslave()) {
-         return(sisfbcheckvretracecrt1());
-      } else {
-         return(sisfbcheckvretracecrt2());
-      }
-   } 
-   return(sisfbcheckvretracecrt1());
+       BOOLEAN isslavemode = FALSE;
+       unsigned short HDisplay1 = ivideo->scrnpitchCRT1 >> 3;
+       unsigned short HDisplay2 = ivideo->video_linelength >> 3;
+
+       if(sisfb_bridgeisslave(ivideo)) isslavemode = TRUE;
+
+       /* We need to set pitch for CRT1 if bridge is in slave mode, too */
+       if((ivideo->currentvbflags & VB_DISPTYPE_DISP1) || (isslavemode)) {
+               outSISIDXREG(SISCR,0x13,(HDisplay1 & 0xFF));
+               setSISIDXREG(SISSR,0x0E,0xF0,(HDisplay1 >> 8));
+       }
+
+       /* We must not set the pitch for CRT2 if bridge is in slave mode */
+       if((ivideo->currentvbflags & VB_DISPTYPE_DISP2) && (!isslavemode)) {
+               orSISIDXREG(SISPART1,ivideo->CRT2_write_enable,0x01);
+               outSISIDXREG(SISPART1,0x07,(HDisplay2 & 0xFF));
+               setSISIDXREG(SISPART1,0x09,0xF0,(HDisplay2 >> 8));
+       }
 }
 
-static int sisfb_myblank(int blank)
+static void
+sisfb_bpp_to_var(struct sis_video_info *ivideo, struct fb_var_screeninfo *var)
 {
-   u8 sr01, sr11, sr1f, cr63=0, p2_0, p1_13;
-   BOOLEAN backlight = TRUE;
-
-   switch(blank) {
-   case 0:     /* on */
-      sr01  = 0x00;
-      sr11  = 0x00;
-      sr1f  = 0x00;
-      cr63  = 0x00;
-      p2_0  = 0x20;
-      p1_13 = 0x00;
-      backlight = TRUE;
-      break;
-   case 1:     /* blank */
-      sr01  = 0x20;
-      sr11  = 0x00;
-      sr1f  = 0x00;
-      cr63  = 0x00;
-      p2_0  = 0x20;
-      p1_13 = 0x00;
-      backlight = TRUE;
-      break;
-   case 2:     /* no vsync */
-      sr01  = 0x20;
-      sr11  = 0x08;
-      sr1f  = 0x80;
-      cr63  = 0x40;
-      p2_0  = 0x40;
-      p1_13 = 0x80;
-      backlight = FALSE;
-      break;
-   case 3:     /* no hsync */
-      sr01  = 0x20;
-      sr11  = 0x08;
-      sr1f  = 0x40;
-      cr63  = 0x40;
-      p2_0  = 0x80;
-      p1_13 = 0x40;
-      backlight = FALSE;
-      break;
-   case 4:     /* off */
-      sr01  = 0x20;
-      sr11  = 0x08;
-      sr1f  = 0xc0;
-      cr63  = 0x40;
-      p2_0  = 0xc0;
-      p1_13 = 0xc0;
-      backlight = FALSE;
-      break;
-   default:
-      return 1;
-   }
-
-   if(ivideo.currentvbflags & VB_DISPTYPE_CRT1) {
-
-      setSISIDXREG(SISSR, 0x01, ~0x20, sr01);
-
-      if( (!sisfb_thismonitor.datavalid) ||
-          ((sisfb_thismonitor.datavalid) &&
-           (sisfb_thismonitor.feature & 0xe0))) {
-
-        if(sisvga_engine == SIS_315_VGA) {
-           setSISIDXREG(SISCR, SiS_Pr.SiS_MyCR63, 0xbf, cr63);
-        }
-
-        setSISIDXREG(SISSR, 0x1f, 0x3f, sr1f);
-      }
-
-   }
-
-   if(ivideo.currentvbflags & CRT2_LCD) {
-
-      if(ivideo.vbflags & (VB_301LV|VB_302LV|VB_302ELV)) {
-        if(backlight) {
-           SiS_SiS30xBLOn(&SiS_Pr, &sishw_ext);
-        } else {
-           SiS_SiS30xBLOff(&SiS_Pr, &sishw_ext);
-        }
-      } else if(sisvga_engine == SIS_315_VGA) {
-        if(ivideo.vbflags & VB_CHRONTEL) {
-           if(backlight) {
-              SiS_Chrontel701xBLOn(&SiS_Pr,&sishw_ext);
-           } else {
-              SiS_Chrontel701xBLOff(&SiS_Pr);
-           }
-        }
-      }
-
-      if(((sisvga_engine == SIS_300_VGA) &&
-          (ivideo.vbflags & (VB_301|VB_30xBDH|VB_LVDS))) ||
-         ((sisvga_engine == SIS_315_VGA) &&
-          ((ivideo.vbflags & (VB_LVDS | VB_CHRONTEL)) == VB_LVDS))) {
-          setSISIDXREG(SISSR, 0x11, ~0x0c, sr11);
-      }
-
-      if(sisvga_engine == SIS_300_VGA) {
-         if((ivideo.vbflags & (VB_301B|VB_301C|VB_302B)) &&
-            (!(ivideo.vbflags & VB_30xBDH))) {
-           setSISIDXREG(SISPART1, 0x13, 0x3f, p1_13);
-        }
-      } else if(sisvga_engine == SIS_315_VGA) {
-         if((ivideo.vbflags & (VB_301B|VB_301C|VB_302B)) &&
-            (!(ivideo.vbflags & VB_30xBDH))) {
-           setSISIDXREG(SISPART2, 0x00, 0x1f, p2_0);
-        }
-      }
-
-   } else if(ivideo.currentvbflags & CRT2_VGA) {
-
-      if(ivideo.vbflags & (VB_301B|VB_301C|VB_302B)) {
-         setSISIDXREG(SISPART2, 0x00, 0x1f, p2_0);
-      }
-
-   }
-
-   return(0);
-}
+       ivideo->video_cmap_len = sisfb_get_cmap_len(var);
 
-/* ----------- FBDev related routines for all series ----------- */
+       switch(var->bits_per_pixel) {
+       case 8:
+               var->red.offset = var->green.offset = var->blue.offset = 0;
+               var->red.length = var->green.length = var->blue.length = 6;
+               break;
+       case 16:
+               var->red.offset = 11;
+               var->red.length = 5;
+               var->green.offset = 5;
+               var->green.length = 6;
+               var->blue.offset = 0;
+               var->blue.length = 5;
+               var->transp.offset = 0;
+               var->transp.length = 0;
+               break;
+       case 32:
+               var->red.offset = 16;
+               var->red.length = 8;
+               var->green.offset = 8;
+               var->green.length = 8;
+               var->blue.offset = 0;
+               var->blue.length = 8;
+               var->transp.offset = 24;
+               var->transp.length = 8;
+               break;
+       }
+}
 
-static void sisfb_set_vparms(void)
+static int
+sisfb_set_mode(struct sis_video_info *ivideo, int clrscrn)
 {
-   switch(ivideo.video_bpp) {
-   case 8:
-               ivideo.DstColor = 0x0000;
-       ivideo.SiS310_AccelDepth = 0x00000000;
-       ivideo.video_cmap_len = 256;
-               break;
-   case 16:
-               ivideo.DstColor = 0x8000;
-               ivideo.SiS310_AccelDepth = 0x00010000;
-       ivideo.video_cmap_len = 16;
-               break;
-   case 32:
-               ivideo.DstColor = 0xC000;
-       ivideo.SiS310_AccelDepth = 0x00020000;
-       ivideo.video_cmap_len = 16;
-               break;
-   default:
-       ivideo.video_cmap_len = 16;
-       printk(KERN_ERR "sisfb: Unsupported depth %d", ivideo.video_bpp);
-       ivideo.accel = 0;
-       break;
-   }
+       unsigned short modeno = ivideo->mode_no;
+
+       /* >=2.6.12's fbcon clears the screen anyway */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
+       if(!clrscrn) modeno |= 0x80;
+#else
+       modeno |= 0x80;
+#endif
+
+       outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
+
+       sisfb_pre_setmode(ivideo);
+
+       if(SiSSetMode(&ivideo->SiS_Pr, modeno) == 0) {
+               printk(KERN_ERR "sisfb: Setting mode[0x%x] failed\n", ivideo->mode_no);
+               return -EINVAL;
+       }
+
+       outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
+
+       sisfb_post_setmode(ivideo);
+
+       return 0;
 }
 
-static int sisfb_do_set_var(struct fb_var_screeninfo *var, int isactive,
-                     struct fb_info *info)
+
+static int
+sisfb_do_set_var(struct fb_var_screeninfo *var, int isactive, struct fb_info *info)
 {
+       struct sis_video_info *ivideo = (struct sis_video_info *)info->par;
        unsigned int htotal = 0, vtotal = 0;
        unsigned int drate = 0, hrate = 0;
-       int found_mode = 0;
+       int found_mode = 0, ret;
        int old_mode;
        u32 pixclock;
 
@@ -1033,4169 +1224,5464 @@ static int sisfb_do_set_var(struct fb_var_screeninfo *var, int isactive,
        }
 
        if(pixclock && htotal && vtotal) {
-          drate = 1000000000 / pixclock;
-          hrate = (drate * 1000) / htotal;
-          ivideo.refresh_rate = (unsigned int) (hrate * 2 / vtotal);
-       } else ivideo.refresh_rate = 60;
-
-#if 0
-       printk(KERN_DEBUG "sisfb: Change mode to %dx%dx%d-%dHz\n",
-               var->xres,var->yres,var->bits_per_pixel,ivideo.refresh_rate);
-#endif
+               drate = 1000000000 / pixclock;
+               hrate = (drate * 1000) / htotal;
+               ivideo->refresh_rate = (unsigned int) (hrate * 2 / vtotal);
+       } else {
+               ivideo->refresh_rate = 60;
+       }
 
-       old_mode = sisfb_mode_idx;
-       sisfb_mode_idx = 0;
+       old_mode = ivideo->sisfb_mode_idx;
+       ivideo->sisfb_mode_idx = 0;
 
-       while( (sisbios_mode[sisfb_mode_idx].mode_no != 0) &&
-              (sisbios_mode[sisfb_mode_idx].xres <= var->xres) ) {
-               if( (sisbios_mode[sisfb_mode_idx].xres == var->xres) &&
-                   (sisbios_mode[sisfb_mode_idx].yres == var->yres) &&
-                   (sisbios_mode[sisfb_mode_idx].bpp == var->bits_per_pixel)) {
-                       sisfb_mode_no = sisbios_mode[sisfb_mode_idx].mode_no;
+       while( (sisbios_mode[ivideo->sisfb_mode_idx].mode_no[0] != 0) &&
+              (sisbios_mode[ivideo->sisfb_mode_idx].xres <= var->xres) ) {
+               if( (sisbios_mode[ivideo->sisfb_mode_idx].xres == var->xres) &&
+                   (sisbios_mode[ivideo->sisfb_mode_idx].yres == var->yres) &&
+                   (sisbios_mode[ivideo->sisfb_mode_idx].bpp == var->bits_per_pixel)) {
+                       ivideo->mode_no = sisbios_mode[ivideo->sisfb_mode_idx].mode_no[ivideo->mni];
                        found_mode = 1;
                        break;
                }
-               sisfb_mode_idx++;
+               ivideo->sisfb_mode_idx++;
        }
 
-       if(found_mode)
-               sisfb_mode_idx = sisfb_validate_mode(sisfb_mode_idx, ivideo.currentvbflags);
-       else
-               sisfb_mode_idx = -1;
+       if(found_mode) {
+               ivideo->sisfb_mode_idx = sisfb_validate_mode(ivideo,
+                               ivideo->sisfb_mode_idx, ivideo->currentvbflags);
+               ivideo->mode_no = sisbios_mode[ivideo->sisfb_mode_idx].mode_no[ivideo->mni];
+       } else {
+               ivideo->sisfb_mode_idx = -1;
+       }
 
-               if(sisfb_mode_idx < 0) {
+               if(ivideo->sisfb_mode_idx < 0) {
                printk(KERN_ERR "sisfb: Mode %dx%dx%d not supported\n", var->xres,
                       var->yres, var->bits_per_pixel);
-               sisfb_mode_idx = old_mode;
+               ivideo->sisfb_mode_idx = old_mode;
                return -EINVAL;
        }
 
-       if(sisfb_search_refresh_rate(ivideo.refresh_rate, sisfb_mode_idx) == 0) {
-               sisfb_rate_idx = sisbios_mode[sisfb_mode_idx].rate_idx;
-               ivideo.refresh_rate = 60;
+       if(sisfb_search_refresh_rate(ivideo, ivideo->refresh_rate, ivideo->sisfb_mode_idx) == 0) {
+               ivideo->rate_idx = sisbios_mode[ivideo->sisfb_mode_idx].rate_idx;
+               ivideo->refresh_rate = 60;
        }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-       if(sisfb_thismonitor.datavalid) {
-          if(!sisfb_verify_rate(&sisfb_thismonitor, sisfb_mode_idx,
-                                sisfb_rate_idx, ivideo.refresh_rate)) {
-             printk(KERN_INFO "sisfb: WARNING: Refresh rate exceeds monitor specs!\n");
-          }
-       }
+       if(isactive) {
+               /* If acceleration to be used? Need to know
+                * before pre/post_set_mode()
+                */
+               ivideo->accel = 0;
+#if defined(FBINFO_HWACCEL_DISABLED) && defined(FBINFO_HWACCEL_XPAN)
+#ifdef STUPID_ACCELF_TEXT_SHIT
+               if(var->accel_flags & FB_ACCELF_TEXT) {
+                       info->flags &= ~FBINFO_HWACCEL_DISABLED;
+               } else {
+                       info->flags |= FBINFO_HWACCEL_DISABLED;
+               }
 #endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-       if(((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) && isactive) {
+               if(!(info->flags & FBINFO_HWACCEL_DISABLED)) ivideo->accel = -1;
 #else
-       if(isactive) {
+               if(var->accel_flags & FB_ACCELF_TEXT) ivideo->accel = -1;
 #endif
-               sisfb_pre_setmode();
 
-               if(SiSSetMode(&SiS_Pr, &sishw_ext, sisfb_mode_no) == 0) {
-                       printk(KERN_ERR "sisfb: Setting mode[0x%x] failed\n", sisfb_mode_no);
-                       return -EINVAL;
+               if((ret = sisfb_set_mode(ivideo, 1))) {
+                       return ret;
                }
 
-               outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
+               ivideo->video_bpp    = sisbios_mode[ivideo->sisfb_mode_idx].bpp;
+               ivideo->video_width  = sisbios_mode[ivideo->sisfb_mode_idx].xres;
+               ivideo->video_height = sisbios_mode[ivideo->sisfb_mode_idx].yres;
 
-               ivideo.video_bpp = sisbios_mode[sisfb_mode_idx].bpp;
-               ivideo.video_vwidth = ivideo.video_width = sisbios_mode[sisfb_mode_idx].xres;
-               ivideo.video_vheight = ivideo.video_height = sisbios_mode[sisfb_mode_idx].yres;
-               ivideo.org_x = ivideo.org_y = 0;
-               ivideo.video_linelength = ivideo.video_width * (ivideo.video_bpp >> 3);
-               ivideo.accel = 0;
-               if(sisfb_accel) {
-                  ivideo.accel = (var->accel_flags & FB_ACCELF_TEXT) ? -1 : 0;
-               }
-
-               sisfb_set_vparms();
-
-               ivideo.current_width = ivideo.video_width;
-               ivideo.current_height = ivideo.video_height;
-               ivideo.current_bpp = ivideo.video_bpp;
-               ivideo.current_htotal = htotal;
-               ivideo.current_vtotal = vtotal;
-               ivideo.current_pixclock = var->pixclock;
-               ivideo.current_refresh_rate = ivideo.refresh_rate;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-                sisfb_lastrates[sisfb_mode_no] = ivideo.refresh_rate;
-#endif
+               sisfb_calc_pitch(ivideo, var);
+               sisfb_set_pitch(ivideo);
 
-               sisfb_post_setmode();
+               sisfb_set_vparms(ivideo);
 
+               ivideo->current_width = ivideo->video_width;
+               ivideo->current_height = ivideo->video_height;
+               ivideo->current_bpp = ivideo->video_bpp;
+               ivideo->current_htotal = htotal;
+               ivideo->current_vtotal = vtotal;
+               ivideo->current_linelength = ivideo->video_linelength;
+               ivideo->current_pixclock = var->pixclock;
+               ivideo->current_refresh_rate = ivideo->refresh_rate;
+               ivideo->sisfb_lastrates[ivideo->mode_no] = ivideo->refresh_rate;
        }
+
        return 0;
 }
 
-static int sisfb_pan_var(struct fb_var_screeninfo *var)
+static void
+sisfb_set_base_CRT1(struct sis_video_info *ivideo, unsigned int base)
 {
-       unsigned int base;
+       outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
 
-       if (var->xoffset > (var->xres_virtual - var->xres)) {
-               return -EINVAL;
-       }
-       if(var->yoffset > (var->yres_virtual - var->yres)) {
-               return -EINVAL;
-       }
-
-       base = var->yoffset * var->xres_virtual + var->xoffset;
-
-        /* calculate base bpp dep. */
-        switch(var->bits_per_pixel) {
-        case 16:
-               base >>= 1;
-               break;
-       case 32:
-               break;
-       case 8:
-        default:
-               base >>= 2;
-               break;
-        }
-       
-       outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
-
-        outSISIDXREG(SISCR, 0x0D, base & 0xFF);
+       outSISIDXREG(SISCR, 0x0D, base & 0xFF);
        outSISIDXREG(SISCR, 0x0C, (base >> 8) & 0xFF);
        outSISIDXREG(SISSR, 0x0D, (base >> 16) & 0xFF);
-       if(sisvga_engine == SIS_315_VGA) {
+       if(ivideo->sisvga_engine == SIS_315_VGA) {
                setSISIDXREG(SISSR, 0x37, 0xFE, (base >> 24) & 0x01);
        }
-        if(ivideo.currentvbflags & VB_DISPTYPE_DISP2) {
-               orSISIDXREG(SISPART1, sisfb_CRT2_write_enable, 0x01);
-               outSISIDXREG(SISPART1, 0x06, (base & 0xFF));
-               outSISIDXREG(SISPART1, 0x05, ((base >> 8) & 0xFF));
-               outSISIDXREG(SISPART1, 0x04, ((base >> 16) & 0xFF));
-               if(sisvga_engine == SIS_315_VGA) {
-                       setSISIDXREG(SISPART1, 0x02, 0x7F, ((base >> 24) & 0x01) << 7);
-               }
-        }
-       return 0;
 }
 
-static void sisfb_bpp_to_var(struct fb_var_screeninfo *var)
+static void
+sisfb_set_base_CRT2(struct sis_video_info *ivideo, unsigned int base)
 {
-       switch(var->bits_per_pixel) {
-          case 8:
-               var->red.offset = var->green.offset = var->blue.offset = 0;
-               var->red.length = var->green.length = var->blue.length = 6;
-               ivideo.video_cmap_len = 256;
-               break;
-          case 16:
-               var->red.offset = 11;
-               var->red.length = 5;
-               var->green.offset = 5;
-               var->green.length = 6;
-               var->blue.offset = 0;
-               var->blue.length = 5;
-               var->transp.offset = 0;
-               var->transp.length = 0;
-               ivideo.video_cmap_len = 16;
-               break;
-          case 32:
-               var->red.offset = 16;
-               var->red.length = 8;
-               var->green.offset = 8;
-               var->green.length = 8;
-               var->blue.offset = 0;
-               var->blue.length = 8;
-               var->transp.offset = 24;
-               var->transp.length = 8;
-               ivideo.video_cmap_len = 16;
-               break;
+       if(ivideo->currentvbflags & VB_DISPTYPE_DISP2) {
+               orSISIDXREG(SISPART1, ivideo->CRT2_write_enable, 0x01);
+               outSISIDXREG(SISPART1, 0x06, (base & 0xFF));
+               outSISIDXREG(SISPART1, 0x05, ((base >> 8) & 0xFF));
+               outSISIDXREG(SISPART1, 0x04, ((base >> 16) & 0xFF));
+               if(ivideo->sisvga_engine == SIS_315_VGA) {
+                       setSISIDXREG(SISPART1, 0x02, 0x7F, ((base >> 24) & 0x01) << 7);
+               }
        }
 }
 
-void sis_dispinfo(struct ap_data *rec)
-{
-       rec->minfo.bpp      = ivideo.video_bpp;
-       rec->minfo.xres     = ivideo.video_width;
-       rec->minfo.yres     = ivideo.video_height;
-       rec->minfo.v_xres   = ivideo.video_vwidth;
-       rec->minfo.v_yres   = ivideo.video_vheight;
-       rec->minfo.org_x    = ivideo.org_x;
-       rec->minfo.org_y    = ivideo.org_y;
-       rec->minfo.vrate    = ivideo.refresh_rate;
-       rec->iobase         = ivideo.vga_base - 0x30;
-       rec->mem_size       = ivideo.video_size;
-       rec->disp_state     = ivideo.disp_state;
-       rec->version        = (VER_MAJOR << 24) | (VER_MINOR << 16) | VER_LEVEL;
-       rec->hasVB          = ivideo.hasVB;
-       rec->TV_type        = ivideo.TV_type;
-       rec->TV_plug        = ivideo.TV_plug;
-       rec->chip           = ivideo.chip;
-       rec->vbflags        = ivideo.vbflags;
-       rec->currentvbflags = ivideo.currentvbflags;
-}
-
-/* ------------ FBDev related routines for 2.4 series ----------- */
-
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
-
-static void sisfb_crtc_to_var(struct fb_var_screeninfo *var)
+static int
+sisfb_pan_var(struct sis_video_info *ivideo, struct fb_var_screeninfo *var)
 {
-       u16 VRE, VBE, VRS, VBS, VDE, VT;
-       u16 HRE, HBE, HRS, HBS, HDE, HT;
-       u8  sr_data, cr_data, cr_data2, cr_data3, mr_data;
-       int A, B, C, D, E, F, temp;
-       unsigned int hrate, drate, maxyres;
-
-       inSISIDXREG(SISSR, IND_SIS_COLOR_MODE, sr_data);
+       if(var->xoffset > (var->xres_virtual - var->xres)) {
+               return -EINVAL;
+       }
+       if(var->yoffset > (var->yres_virtual - var->yres)) {
+               return -EINVAL;
+       }
 
-       if(sr_data & SIS_INTERLACED_MODE)
-          var->vmode = FB_VMODE_INTERLACED;
-       else
-          var->vmode = FB_VMODE_NONINTERLACED;
+       ivideo->current_base = (var->yoffset * var->xres_virtual) + var->xoffset;
 
-       switch ((sr_data & 0x1C) >> 2) {
-          case SIS_8BPP_COLOR_MODE:
-               var->bits_per_pixel = 8;
+       /* calculate base bpp dep. */
+       switch(var->bits_per_pixel) {
+       case 32:
                break;
-          case SIS_16BPP_COLOR_MODE:
-               var->bits_per_pixel = 16;
+       case 16:
+               ivideo->current_base >>= 1;
                break;
-          case SIS_32BPP_COLOR_MODE:
-               var->bits_per_pixel = 32;
+       case 8:
+       default:
+               ivideo->current_base >>= 2;
                break;
        }
 
-       sisfb_bpp_to_var(var);
-       
-       inSISIDXREG(SISSR, 0x0A, sr_data);
-
-        inSISIDXREG(SISCR, 0x06, cr_data);
-
-        inSISIDXREG(SISCR, 0x07, cr_data2);
-
-       VT = (cr_data & 0xFF) | ((u16) (cr_data2 & 0x01) << 8) |
-            ((u16) (cr_data2 & 0x20) << 4) | ((u16) (sr_data & 0x01) << 10);
-       A = VT + 2;
-
-       inSISIDXREG(SISCR, 0x12, cr_data);
-
-       VDE = (cr_data & 0xff) | ((u16) (cr_data2 & 0x02) << 7) |
-             ((u16) (cr_data2 & 0x40) << 3) | ((u16) (sr_data & 0x02) << 9);
-       E = VDE + 1;
-
-       inSISIDXREG(SISCR, 0x10, cr_data);
-
-       VRS = (cr_data & 0xff) | ((u16) (cr_data2 & 0x04) << 6) |
-             ((u16) (cr_data2 & 0x80) << 2) | ((u16) (sr_data & 0x08) << 7);
-       F = VRS + 1 - E;
-
-       inSISIDXREG(SISCR, 0x15, cr_data);
-
-       inSISIDXREG(SISCR, 0x09, cr_data3);
-
-       if(cr_data3 & 0x80) var->vmode = FB_VMODE_DOUBLE;
-
-       VBS = (cr_data & 0xff) | ((u16) (cr_data2 & 0x08) << 5) |
-             ((u16) (cr_data3 & 0x20) << 4) | ((u16) (sr_data & 0x04) << 8);
-
-       inSISIDXREG(SISCR, 0x16, cr_data);
-
-       VBE = (cr_data & 0xff) | ((u16) (sr_data & 0x10) << 4);
-       temp = VBE - ((E - 1) & 511);
-       B = (temp > 0) ? temp : (temp + 512);
-
-       inSISIDXREG(SISCR, 0x11, cr_data);
-
-       VRE = (cr_data & 0x0f) | ((sr_data & 0x20) >> 1);
-       temp = VRE - ((E + F - 1) & 31);
-       C = (temp > 0) ? temp : (temp + 32);
-
-       D = B - F - C;
-
-        var->yres = E;
-       var->upper_margin = D;
-       var->lower_margin = F;
-       var->vsync_len = C;
-
-       if((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
-          var->yres <<= 1;
-          var->upper_margin <<= 1;
-          var->lower_margin <<= 1;
-          var->vsync_len <<= 1;
-       } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
-          var->yres >>= 1;
-          var->upper_margin >>= 1;
-          var->lower_margin >>= 1;
-          var->vsync_len >>= 1;
-       }
-
-       inSISIDXREG(SISSR, 0x0b, sr_data);
-
-       inSISIDXREG(SISCR, 0x00, cr_data);
-
-       HT = (cr_data & 0xff) | ((u16) (sr_data & 0x03) << 8);
-       A = HT + 5;
-
-       inSISIDXREG(SISCR, 0x01, cr_data);
-
-       HDE = (cr_data & 0xff) | ((u16) (sr_data & 0x0C) << 6);
-       E = HDE + 1;
-
-       inSISIDXREG(SISCR, 0x04, cr_data);
-
-       HRS = (cr_data & 0xff) | ((u16) (sr_data & 0xC0) << 2);
-       F = HRS - E - 3;
-
-       inSISIDXREG(SISCR, 0x02, cr_data);
-
-       HBS = (cr_data & 0xff) | ((u16) (sr_data & 0x30) << 4);
-
-       inSISIDXREG(SISSR, 0x0c, sr_data);
-
-       inSISIDXREG(SISCR, 0x03, cr_data);
-
-       inSISIDXREG(SISCR, 0x05, cr_data2);
-
-       HBE = (cr_data & 0x1f) | ((u16) (cr_data2 & 0x80) >> 2) |
-             ((u16) (sr_data & 0x03) << 6);
-       HRE = (cr_data2 & 0x1f) | ((sr_data & 0x04) << 3);
-
-       temp = HBE - ((E - 1) & 255);
-       B = (temp > 0) ? temp : (temp + 256);
-
-       temp = HRE - ((E + F + 3) & 63);
-       C = (temp > 0) ? temp : (temp + 64);
-
-       D = B - F - C;
-
-       var->xres = var->xres_virtual = E * 8;
-
-       if((var->xres == 320) &&
-          (var->yres == 200 || var->yres == 240)) {
-               /* Terrible hack, but the correct CRTC data for
-                * these modes only produces a black screen...
-                */
-                       var->left_margin = (400 - 376);
-                       var->right_margin = (328 - 320);
-                       var->hsync_len = (376 - 328);
-       } else {
-               var->left_margin = D * 8;
-               var->right_margin = F * 8;
-               var->hsync_len = C * 8;
-       }
-       var->activate = FB_ACTIVATE_NOW;
-
-       var->sync = 0;
+       ivideo->current_base += (ivideo->video_offset >> 2);
 
-       mr_data = inSISREG(SISMISCR);
-       if(mr_data & 0x80)
-          var->sync &= ~FB_SYNC_VERT_HIGH_ACT;
-       else
-          var->sync |= FB_SYNC_VERT_HIGH_ACT;
-
-       if(mr_data & 0x40)
-          var->sync &= ~FB_SYNC_HOR_HIGH_ACT;
-       else
-          var->sync |= FB_SYNC_HOR_HIGH_ACT;
-
-       VT += 2;
-       VT <<= 1;
-       HT = (HT + 5) * 8;
-
-       if((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
-          VT <<= 1;
-       }
-       hrate = ivideo.refresh_rate * VT / 2;
-       drate = (hrate * HT) / 1000;
-       var->pixclock = (u32) (1000000000 / drate);
-
-       if(sisfb_ypan) {
-          maxyres = ivideo.heapstart / (var->xres * (var->bits_per_pixel >> 3));
-          if(maxyres > 32767) maxyres = 32767;
-          if(sisfb_max) {
-             var->yres_virtual = maxyres;
-          } else {
-             if(var->yres_virtual > maxyres) {
-                var->yres_virtual = maxyres;
-             }
-          }
-          if(var->yres_virtual <= var->yres) {
-             var->yres_virtual = var->yres;
-          }
-       } else
-          var->yres_virtual = var->yres;
+       sisfb_set_base_CRT1(ivideo, ivideo->current_base);
+       sisfb_set_base_CRT2(ivideo, ivideo->current_base);
 
+       return 0;
 }
 
-static int sis_getcolreg(unsigned regno, unsigned *red, unsigned *green, unsigned *blue,
-                        unsigned *transp, struct fb_info *fb_info)
+static int
+sisfb_open(struct fb_info *info, int user)
 {
-       if (regno >= ivideo.video_cmap_len)
-               return 1;
+       return 0;
+}
 
-       *red = sis_palette[regno].red;
-       *green = sis_palette[regno].green;
-       *blue = sis_palette[regno].blue;
-       *transp = 0;
+static int
+sisfb_release(struct fb_info *info, int user)
+{
        return 0;
 }
 
-static int sisfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue,
-                           unsigned transp, struct fb_info *fb_info)
+static int
+sisfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue,
+               unsigned transp, struct fb_info *info)
 {
-       if (regno >= ivideo.video_cmap_len)
-               return 1;
+       struct sis_video_info *ivideo = (struct sis_video_info *)info->par;
 
-       sis_palette[regno].red = red;
-       sis_palette[regno].green = green;
-       sis_palette[regno].blue = blue;
+       if(regno >= sisfb_get_cmap_len(&info->var))
+               return 1;
 
-       switch (ivideo.video_bpp) {
-#ifdef FBCON_HAS_CFB8
+       switch(info->var.bits_per_pixel) {
        case 8:
-               outSISREG(SISDACA, regno);
+               outSISREG(SISDACA, regno);
                outSISREG(SISDACD, (red >> 10));
                outSISREG(SISDACD, (green >> 10));
                outSISREG(SISDACD, (blue >> 10));
-               if (ivideo.currentvbflags & VB_DISPTYPE_DISP2) {
-                       outSISREG(SISDAC2A, regno);
+               if(ivideo->currentvbflags & VB_DISPTYPE_DISP2) {
+                       outSISREG(SISDAC2A, regno);
                        outSISREG(SISDAC2D, (red >> 8));
                        outSISREG(SISDAC2D, (green >> 8));
                        outSISREG(SISDAC2D, (blue >> 8));
                }
                break;
-#endif
-#ifdef FBCON_HAS_CFB16
        case 16:
-               sis_fbcon_cmap.cfb16[regno] =
-                   ((red & 0xf800)) | ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11);
+               ((u32 *)(info->pseudo_palette))[regno] =
+                               (red & 0xf800)          |
+                               ((green & 0xfc00) >> 5) |
+                               ((blue & 0xf800) >> 11);
                break;
-#endif
-#ifdef FBCON_HAS_CFB32
        case 32:
                red >>= 8;
                green >>= 8;
                blue >>= 8;
-               sis_fbcon_cmap.cfb32[regno] = (red << 16) | (green << 8) | (blue);
+               ((u32 *)(info->pseudo_palette))[regno] =
+                               (red << 16) | (green << 8) | (blue);
                break;
-#endif
        }
        return 0;
 }
 
-static void sisfb_set_disp(int con, struct fb_var_screeninfo *var,
-                           struct fb_info *info)
+static int
+sisfb_set_par(struct fb_info *info)
 {
-       struct fb_fix_screeninfo fix;
-       long   flags;
-       struct display *display;
-       struct display_switch *sw;
+       int err;
 
-       if(con >= 0)
-               display = &fb_display[con];
-       else
-               display = &sis_disp;
-
-       sisfb_get_fix(&fix, con, 0);
-
-       display->screen_base = ivideo.video_vbase;
-       display->visual = fix.visual;
-       display->type = fix.type;
-       display->type_aux = fix.type_aux;
-       display->ypanstep = fix.ypanstep;
-       display->ywrapstep = fix.ywrapstep;
-       display->line_length = fix.line_length;
-       display->next_line = fix.line_length;
-       display->can_soft_blank = 1;
-       display->inverse = sisfb_inverse;
-       display->var = *var;
-
-       save_flags(flags);
-
-       switch (ivideo.video_bpp) {
-#ifdef FBCON_HAS_CFB8
-          case 8:
-#ifdef SISFBACCEL
-               sw = ivideo.accel ? &fbcon_sis8 : &fbcon_cfb8;
-#else
-               sw = &fbcon_cfb8;
-#endif
-               break;
-#endif
-#ifdef FBCON_HAS_CFB16
-          case 16:
-#ifdef SISFBACCEL
-               sw = ivideo.accel ? &fbcon_sis16 : &fbcon_cfb16;
-#else
-               sw = &fbcon_cfb16;
-#endif
-               display->dispsw_data = sis_fbcon_cmap.cfb16;
-               break;
-#endif
-#ifdef FBCON_HAS_CFB32
-          case 32:
-#ifdef SISFBACCEL
-               sw = ivideo.accel ? &fbcon_sis32 : &fbcon_cfb32;
+       if((err = sisfb_do_set_var(&info->var, 1, info)))
+               return err;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
+       sisfb_get_fix(&info->fix, info->currcon, info);
 #else
-               sw = &fbcon_cfb32;
-#endif
-               display->dispsw_data = sis_fbcon_cmap.cfb32;
-               break;
+       sisfb_get_fix(&info->fix, -1, info);
 #endif
-          default:
-               sw = &fbcon_dummy;
-               return;
-       }
-       memcpy(&sisfb_sw, sw, sizeof(*sw));
-       display->dispsw = &sisfb_sw;
-       restore_flags(flags);
-
-        if(sisfb_ypan) {
-           /* display->scrollmode = 0;  */
-       } else {
-           display->scrollmode = SCROLL_YREDRAW;
-           sisfb_sw.bmove = fbcon_redraw_bmove;
-       }
+       return 0;
 }
 
-static void sisfb_do_install_cmap(int con, struct fb_info *info)
+static int
+sisfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
 {
-        if (con != currcon)
-               return;
+       struct sis_video_info *ivideo = (struct sis_video_info *)info->par;
+       unsigned int htotal = 0, vtotal = 0, myrateindex = 0;
+       unsigned int drate = 0, hrate = 0, maxyres;
+       int found_mode = 0;
+       int refresh_rate, search_idx, tidx;
+       BOOLEAN recalc_clock = FALSE;
+       u32 pixclock;
 
-        if (fb_display[con].cmap.len)
-               fb_set_cmap(&fb_display[con].cmap, 1, sisfb_setcolreg, info);
-        else
-               fb_set_cmap(fb_default_cmap(ivideo.video_cmap_len), 1,
-                           sisfb_setcolreg, info);
-}
+       htotal = var->left_margin + var->xres + var->right_margin + var->hsync_len;
 
+       vtotal = var->upper_margin + var->lower_margin + var->vsync_len;
 
-static int sisfb_get_var(struct fb_var_screeninfo *var, int con,
-                        struct fb_info *info)
-{
-       if(con == -1)
-               memcpy(var, &default_var, sizeof(struct fb_var_screeninfo));
-       else
-               *var = fb_display[con].var;
+       pixclock = var->pixclock;
 
-       if(sisfb_fstn) {
-          if (var->xres == 320 && var->yres == 480)
-               var->yres = 240;
-        }
+       if((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) {
+               vtotal += var->yres;
+               vtotal <<= 1;
+       } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
+               vtotal += var->yres;
+               vtotal <<= 2;
+       } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
+               vtotal += var->yres;
+               vtotal <<= 1;
+       } else
+               vtotal += var->yres;
 
-       return 0;
-}
+       if(!(htotal) || !(vtotal)) {
+               SISFAIL("sisfb: no valid timing data");
+       }
 
-static int sisfb_set_var(struct fb_var_screeninfo *var, int con,
-                        struct fb_info *info)
-{
-       int err;
-       unsigned int cols, rows;
+       search_idx = 0;
+       while( (sisbios_mode[search_idx].mode_no[0] != 0) &&
+              (sisbios_mode[search_idx].xres <= var->xres) ) {
+               if( (sisbios_mode[search_idx].xres == var->xres) &&
+                   (sisbios_mode[search_idx].yres == var->yres) &&
+                   (sisbios_mode[search_idx].bpp == var->bits_per_pixel)) {
+                       if((tidx = sisfb_validate_mode(ivideo, search_idx,
+                                               ivideo->currentvbflags)) > 0) {
+                               found_mode = 1;
+                               search_idx = tidx;
+                               break;
+                       }
+               }
+               search_idx++;
+       }
 
-       fb_display[con].var.activate = FB_ACTIVATE_NOW;
-        if(sisfb_do_set_var(var, con == currcon, info)) {
-               sisfb_crtc_to_var(var);
-               return -EINVAL;
+       if(!found_mode) {
+               search_idx = 0;
+               while(sisbios_mode[search_idx].mode_no[0] != 0) {
+                  if( (var->xres <= sisbios_mode[search_idx].xres) &&
+                      (var->yres <= sisbios_mode[search_idx].yres) &&
+                      (var->bits_per_pixel == sisbios_mode[search_idx].bpp) ) {
+                       if((tidx = sisfb_validate_mode(ivideo,search_idx,
+                                               ivideo->currentvbflags)) > 0) {
+                               found_mode = 1;
+                               search_idx = tidx;
+                               break;
+                       }
+                  }
+                  search_idx++;
+               }
+               if(found_mode) {
+                       printk(KERN_DEBUG
+                               "sisfb: Adapted from %dx%dx%d to %dx%dx%d\n",
+                               var->xres, var->yres, var->bits_per_pixel,
+                               sisbios_mode[search_idx].xres,
+                               sisbios_mode[search_idx].yres,
+                               var->bits_per_pixel);
+                       var->xres = sisbios_mode[search_idx].xres;
+                       var->yres = sisbios_mode[search_idx].yres;
+               } else {
+                       printk(KERN_ERR
+                               "sisfb: Failed to find supported mode near %dx%dx%d\n",
+                               var->xres, var->yres, var->bits_per_pixel);
+                       return -EINVAL;
+               }
+       }
+
+       if( ((ivideo->vbflags2 & VB2_LVDS) ||
+            ((ivideo->vbflags2 & VB2_30xBDH) && (ivideo->currentvbflags & CRT2_LCD))) &&
+           (var->bits_per_pixel == 8) ) {
+               /* Slave modes on LVDS and 301B-DH */
+               refresh_rate = 60;
+               recalc_clock = TRUE;
+       } else if( (ivideo->current_htotal == htotal) &&
+                  (ivideo->current_vtotal == vtotal) &&
+                  (ivideo->current_pixclock == pixclock) ) {
+               /* x=x & y=y & c=c -> assume depth change */
+               drate = 1000000000 / pixclock;
+               hrate = (drate * 1000) / htotal;
+               refresh_rate = (unsigned int) (hrate * 2 / vtotal);
+       } else if( ( (ivideo->current_htotal != htotal) ||
+                    (ivideo->current_vtotal != vtotal) ) &&
+                  (ivideo->current_pixclock == var->pixclock) ) {
+               /* x!=x | y!=y & c=c -> invalid pixclock */
+               if(ivideo->sisfb_lastrates[sisbios_mode[search_idx].mode_no[ivideo->mni]]) {
+                       refresh_rate =
+                               ivideo->sisfb_lastrates[sisbios_mode[search_idx].mode_no[ivideo->mni]];
+               } else if(ivideo->sisfb_parm_rate != -1) {
+                       /* Sic, sisfb_parm_rate - want to know originally desired rate here */
+                       refresh_rate = ivideo->sisfb_parm_rate;
+               } else {
+                       refresh_rate = 60;
+               }
+               recalc_clock = TRUE;
+       } else if((pixclock) && (htotal) && (vtotal)) {
+               drate = 1000000000 / pixclock;
+               hrate = (drate * 1000) / htotal;
+               refresh_rate = (unsigned int) (hrate * 2 / vtotal);
+       } else if(ivideo->current_refresh_rate) {
+               refresh_rate = ivideo->current_refresh_rate;
+               recalc_clock = TRUE;
+       } else {
+               refresh_rate = 60;
+               recalc_clock = TRUE;
        }
 
-       sisfb_crtc_to_var(var);
+       myrateindex = sisfb_search_refresh_rate(ivideo, refresh_rate, search_idx);
+
+       /* Eventually recalculate timing and clock */
+       if(recalc_clock) {
+               if(!myrateindex) myrateindex = sisbios_mode[search_idx].rate_idx;
+               var->pixclock = (u32) (1000000000 / sisfb_mode_rate_to_dclock(&ivideo->SiS_Pr,
+                                               sisbios_mode[search_idx].mode_no[ivideo->mni],
+                                               myrateindex));
+               sisfb_mode_rate_to_ddata(&ivideo->SiS_Pr,
+                                       sisbios_mode[search_idx].mode_no[ivideo->mni],
+                                       myrateindex, var);
+               if((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
+                       var->pixclock <<= 1;
+               }
+       }
 
-       sisfb_set_disp(con, var, info);
+       if(ivideo->sisfb_thismonitor.datavalid) {
+               if(!sisfb_verify_rate(ivideo, &ivideo->sisfb_thismonitor, search_idx,
+                               myrateindex, refresh_rate)) {
+                       printk(KERN_INFO
+                               "sisfb: WARNING: Refresh rate exceeds monitor specs!\n");
+               }
+       }
 
-       if(info->changevar)
-               (*info->changevar) (con);
+       /* Adapt RGB settings */
+       sisfb_bpp_to_var(ivideo, var);
 
-       if((err = fb_alloc_cmap(&fb_display[con].cmap, 0, 0)))
-               return err;
+       /* Sanity check for offsets */
+       if(var->xoffset < 0) var->xoffset = 0;
+       if(var->yoffset < 0) var->yoffset = 0;
 
-       sisfb_do_install_cmap(con, info);
+       if(var->xres > var->xres_virtual)
+               var->xres_virtual = var->xres;
 
-       cols = sisbios_mode[sisfb_mode_idx].cols;
-       rows = sisbios_mode[sisfb_mode_idx].rows;
-#if 0
-       /* Why was this called here? */
-       vc_resize_con(rows, cols, fb_display[con].conp->vc_num);
-#endif
+       if(ivideo->sisfb_ypan) {
+               maxyres = sisfb_calc_maxyres(ivideo, var);
+               if(ivideo->sisfb_max) {
+                       var->yres_virtual = maxyres;
+               } else {
+                       if(var->yres_virtual > maxyres) {
+                               var->yres_virtual = maxyres;
+                       }
+               }
+               if(var->yres_virtual <= var->yres) {
+                       var->yres_virtual = var->yres;
+               }
+       } else {
+               if(var->yres != var->yres_virtual) {
+                       var->yres_virtual = var->yres;
+               }
+               var->xoffset = 0;
+               var->yoffset = 0;
+       }
 
-       return 0;
-}
+       /* Truncate offsets to maximum if too high */
+       if(var->xoffset > var->xres_virtual - var->xres) {
+               var->xoffset = var->xres_virtual - var->xres - 1;
+       }
 
-static int sisfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
-                         struct fb_info *info)
-{
-        if (con == currcon)
-               return fb_get_cmap(cmap, kspc, sis_getcolreg, info);
+       if(var->yoffset > var->yres_virtual - var->yres) {
+               var->yoffset = var->yres_virtual - var->yres - 1;
+       }
 
-       else if (fb_display[con].cmap.len)
-               fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
-       else
-               fb_copy_cmap(fb_default_cmap(ivideo.video_cmap_len), cmap, kspc ? 0 : 2);
+       /* Set everything else to 0 */
+       var->red.msb_right =
+               var->green.msb_right =
+               var->blue.msb_right =
+               var->transp.offset =
+               var->transp.length =
+               var->transp.msb_right = 0;
 
        return 0;
 }
 
-static int sisfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-                         struct fb_info *info)
+static int
+sisfb_pan_display(struct fb_var_screeninfo *var, struct fb_info* info)
 {
+       struct sis_video_info *ivideo = (struct sis_video_info *)info->par;
        int err;
 
-       if (!fb_display[con].cmap.len) {
-               err = fb_alloc_cmap(&fb_display[con].cmap, ivideo.video_cmap_len, 0);
-               if (err)
-                       return err;
-       }
-        
-       if (con == currcon)
-               return fb_set_cmap(cmap, kspc, sisfb_setcolreg, info);
-
-       else
-               fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+       if(var->xoffset > (var->xres_virtual - var->xres))
+               return -EINVAL;
 
-       return 0;
-}
+       if(var->yoffset > (var->yres_virtual - var->yres))
+               return -EINVAL;
 
-static int sisfb_pan_display(struct fb_var_screeninfo *var, int con,
-                            struct fb_info* info)
-{
-       int err;
+       if(var->vmode & FB_VMODE_YWRAP)
+               return -EINVAL;
 
-       if (var->vmode & FB_VMODE_YWRAP) {
-               if (var->yoffset < 0 || var->yoffset >= fb_display[con].var.yres_virtual || var->xoffset)
-                       return -EINVAL;
-       } else {
-               if (var->xoffset+fb_display[con].var.xres > fb_display[con].var.xres_virtual ||
-                   var->yoffset+fb_display[con].var.yres > fb_display[con].var.yres_virtual)
-                       return -EINVAL;
-       }
+       if(var->xoffset + info->var.xres > info->var.xres_virtual ||
+          var->yoffset + info->var.yres > info->var.yres_virtual)
+               return -EINVAL;
 
-        if(con == currcon) {
-          if((err = sisfb_pan_var(var)) < 0) return err;
-       }
+       if((err = sisfb_pan_var(ivideo, var)) < 0)
+               return err;
 
-       fb_display[con].var.xoffset = var->xoffset;
-       fb_display[con].var.yoffset = var->yoffset;
-       if (var->vmode & FB_VMODE_YWRAP)
-               fb_display[con].var.vmode |= FB_VMODE_YWRAP;
-       else
-               fb_display[con].var.vmode &= ~FB_VMODE_YWRAP;
+       info->var.xoffset = var->xoffset;
+       info->var.yoffset = var->yoffset;
 
        return 0;
 }
 
-static int sisfb_mmap(struct fb_info *info, struct file *file,
-                     struct vm_area_struct *vma)
+static int
+sisfb_blank(int blank, struct fb_info *info)
 {
-       struct fb_var_screeninfo var;
-       unsigned long start;
-       unsigned long off;
-       u32 len, mmio_off;
+       struct sis_video_info *ivideo = (struct sis_video_info *)info->par;
 
-       if(vma->vm_pgoff > (~0UL >> PAGE_SHIFT))  return -EINVAL;
+       return sisfb_myblank(ivideo, blank);
+}
 
-       off = vma->vm_pgoff << PAGE_SHIFT;
+/* ----------- FBDev related routines for all series ---------- */
 
-       start = (unsigned long) ivideo.video_base;
-       len = PAGE_ALIGN((start & ~PAGE_MASK) + ivideo.video_size);
-#if 0
-       if (off >= len) {
-               off -= len;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+static int     sisfb_ioctl(struct fb_info *info, unsigned int cmd,
+                           unsigned long arg)
+#else
+static int     sisfb_ioctl(struct inode *inode, struct file *file,
+                               unsigned int cmd, unsigned long arg,
+                               struct fb_info *info)
 #endif
-       /* By Jake Page: Treat mmap request with offset beyond heapstart
-        *               as request for mapping the mmio area 
-        */
-       mmio_off = PAGE_ALIGN((start & ~PAGE_MASK) + ivideo.heapstart);
-       if(off >= mmio_off) {
-               off -= mmio_off;                
-               sisfb_get_var(&var, currcon, info);
-               if(var.accel_flags) return -EINVAL;
-
-               start = (unsigned long) ivideo.mmio_base;
-               len = PAGE_ALIGN((start & ~PAGE_MASK) + sisfb_mmio_size);
-       }
-
-       start &= PAGE_MASK;
-       if((vma->vm_end - vma->vm_start + off) > len)   return -EINVAL;
+{
+       struct sis_video_info   *ivideo = (struct sis_video_info *)info->par;
+       struct sis_memreq       sismemreq;
+       struct fb_vblank        sisvbblank;
+       u32                     gpu32 = 0;
+#ifndef __user
+#define __user
+#endif
+       u32 __user              *argp = (u32 __user *)arg;
 
-       off += start;
-       vma->vm_pgoff = off >> PAGE_SHIFT;
-       vma->vm_flags |= VM_IO;   /* by Jake Page; is that really needed? */
+       switch(cmd) {
+          case FBIO_ALLOC:
+               if(!capable(CAP_SYS_RAWIO))
+                       return -EPERM;
 
-#if defined(__i386__) || defined(__x86_64__)
-       if (boot_cpu_data.x86 > 3)
-               pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
-#endif
-        /* RedHat requires vma as the first paramater to the following call */
-       if (io_remap_page_range(vma->vm_start, off, vma->vm_end - vma->vm_start,
-                               vma->vm_page_prot))
-               return -EAGAIN;
+               if(copy_from_user(&sismemreq, (void __user *)arg, sizeof(sismemreq)))
+                       return -EFAULT;
 
-       return 0;
-}
+               sis_malloc(&sismemreq);
 
-static void sis_get_glyph(struct fb_info *info, SIS_GLYINFO *gly)
-{
-       struct display *p = &fb_display[currcon];
-       u16 c;
-       u8 *cdat;
-       int widthb;
-       u8 *gbuf = gly->gmask;
-       int size;
-
-       gly->fontheight = fontheight(p);
-       gly->fontwidth = fontwidth(p);
-       widthb = (fontwidth(p) + 7) / 8;
-
-       c = gly->ch & p->charmask;
-       if (fontwidth(p) <= 8)
-               cdat = p->fontdata + c * fontheight(p);
-       else
-               cdat = p->fontdata + (c * fontheight(p) << 1);
+               if(copy_to_user((void __user *)arg, &sismemreq, sizeof(sismemreq))) {
+                       sis_free((u32)sismemreq.offset);
+                       return -EFAULT;
+               }
+               break;
 
-       size = fontheight(p) * widthb;
-       memcpy(gbuf, cdat, size);
-       gly->ngmask = size;
-}
+          case FBIO_FREE:
+               if(!capable(CAP_SYS_RAWIO))
+                       return -EPERM;
 
-static int sisfb_update_var(int con, struct fb_info *info)
-{
-        return(sisfb_pan_var(&fb_display[con].var));
-}
+               if(get_user(gpu32, argp))
+                       return -EFAULT;
 
-static int sisfb_switch(int con, struct fb_info *info)
-{
-       int cols, rows;
+               sis_free(gpu32);
+               break;
 
-        if(fb_display[currcon].cmap.len)
-               fb_get_cmap(&fb_display[currcon].cmap, 1, sis_getcolreg, info);
+          case FBIOGET_VBLANK:
+               sisvbblank.count = 0;
+               sisvbblank.flags = sisfb_setupvbblankflags(ivideo, &sisvbblank.vcount, &sisvbblank.hcount);
 
-       fb_display[con].var.activate = FB_ACTIVATE_NOW;
+               if(copy_to_user((void __user *)arg, &sisvbblank, sizeof(sisvbblank)))
+                       return -EFAULT;
 
-       if(!memcmp(&fb_display[con].var, &fb_display[currcon].var,
-                                  sizeof(struct fb_var_screeninfo))) {
-               currcon = con;
-               return 1;
-       }
+               break;
 
-       currcon = con;
+          case SISFB_GET_INFO_SIZE:
+               return put_user(sizeof(struct sisfb_info), argp);
 
-       sisfb_do_set_var(&fb_display[con].var, 1, info);
+          case SISFB_GET_INFO_OLD:
+               if(ivideo->warncount++ < 10)
+                       printk(KERN_INFO
+                               "sisfb: Deprecated ioctl call received - update your application!\n");
+          case SISFB_GET_INFO:  /* For communication with X driver */
+               ivideo->sisfb_infoblock.sisfb_id         = SISFB_ID;
+               ivideo->sisfb_infoblock.sisfb_version    = VER_MAJOR;
+               ivideo->sisfb_infoblock.sisfb_revision   = VER_MINOR;
+               ivideo->sisfb_infoblock.sisfb_patchlevel = VER_LEVEL;
+               ivideo->sisfb_infoblock.chip_id = ivideo->chip_id;
+               ivideo->sisfb_infoblock.sisfb_pci_vendor = ivideo->chip_vendor;
+               ivideo->sisfb_infoblock.memory = ivideo->video_size / 1024;
+               ivideo->sisfb_infoblock.heapstart = ivideo->heapstart / 1024;
+               if(ivideo->modechanged) {
+                       ivideo->sisfb_infoblock.fbvidmode = ivideo->mode_no;
+               } else {
+                       ivideo->sisfb_infoblock.fbvidmode = ivideo->modeprechange;
+               }
+               ivideo->sisfb_infoblock.sisfb_caps = ivideo->caps;
+               ivideo->sisfb_infoblock.sisfb_tqlen = ivideo->cmdQueueSize / 1024;
+               ivideo->sisfb_infoblock.sisfb_pcibus = ivideo->pcibus;
+               ivideo->sisfb_infoblock.sisfb_pcislot = ivideo->pcislot;
+               ivideo->sisfb_infoblock.sisfb_pcifunc = ivideo->pcifunc;
+               ivideo->sisfb_infoblock.sisfb_lcdpdc = ivideo->detectedpdc;
+               ivideo->sisfb_infoblock.sisfb_lcdpdca = ivideo->detectedpdca;
+               ivideo->sisfb_infoblock.sisfb_lcda = ivideo->detectedlcda;
+               ivideo->sisfb_infoblock.sisfb_vbflags = ivideo->vbflags;
+               ivideo->sisfb_infoblock.sisfb_currentvbflags = ivideo->currentvbflags;
+               ivideo->sisfb_infoblock.sisfb_scalelcd = ivideo->SiS_Pr.UsePanelScaler;
+               ivideo->sisfb_infoblock.sisfb_specialtiming = ivideo->SiS_Pr.SiS_CustomT;
+               ivideo->sisfb_infoblock.sisfb_haveemi = ivideo->SiS_Pr.HaveEMI ? 1 : 0;
+               ivideo->sisfb_infoblock.sisfb_haveemilcd = ivideo->SiS_Pr.HaveEMILCD ? 1 : 0;
+               ivideo->sisfb_infoblock.sisfb_emi30 = ivideo->SiS_Pr.EMI_30;
+               ivideo->sisfb_infoblock.sisfb_emi31 = ivideo->SiS_Pr.EMI_31;
+               ivideo->sisfb_infoblock.sisfb_emi32 = ivideo->SiS_Pr.EMI_32;
+               ivideo->sisfb_infoblock.sisfb_emi33 = ivideo->SiS_Pr.EMI_33;
+               ivideo->sisfb_infoblock.sisfb_tvxpos = (u16)(ivideo->tvxpos + 32);
+               ivideo->sisfb_infoblock.sisfb_tvypos = (u16)(ivideo->tvypos + 32);
+               ivideo->sisfb_infoblock.sisfb_heapsize = ivideo->sisfb_heap_size / 1024;
+               ivideo->sisfb_infoblock.sisfb_videooffset = ivideo->video_offset;
+               ivideo->sisfb_infoblock.sisfb_curfstn = ivideo->curFSTN;
+               ivideo->sisfb_infoblock.sisfb_curdstn = ivideo->curDSTN;
+               ivideo->sisfb_infoblock.sisfb_vbflags2 = ivideo->vbflags2;
+               ivideo->sisfb_infoblock.sisfb_can_post = ivideo->sisfb_can_post ? 1 : 0;
+               ivideo->sisfb_infoblock.sisfb_card_posted = ivideo->sisfb_card_posted ? 1 : 0;
+               ivideo->sisfb_infoblock.sisfb_was_boot_device = ivideo->sisfb_was_boot_device ? 1 : 0;
+
+               if(copy_to_user((void __user *)arg, &ivideo->sisfb_infoblock,
+                                               sizeof(ivideo->sisfb_infoblock)))
+                       return -EFAULT;
 
-       sisfb_set_disp(con, &fb_display[con].var, info);
+               break;
 
-       sisfb_do_install_cmap(con, info);
+          case SISFB_GET_VBRSTATUS_OLD:
+               if(ivideo->warncount++ < 10)
+                       printk(KERN_INFO
+                               "sisfb: Deprecated ioctl call received - update your application!\n");
+          case SISFB_GET_VBRSTATUS:
+               if(sisfb_CheckVBRetrace(ivideo))
+                       return put_user((u32)1, argp);
+               else
+                       return put_user((u32)0, argp);
 
-       cols = sisbios_mode[sisfb_mode_idx].cols;
-       rows = sisbios_mode[sisfb_mode_idx].rows;
-       vc_resize_con(rows, cols, fb_display[con].conp->vc_num);
+          case SISFB_GET_AUTOMAXIMIZE_OLD:
+               if(ivideo->warncount++ < 10)
+                       printk(KERN_INFO
+                               "sisfb: Deprecated ioctl call received - update your application!\n");
+          case SISFB_GET_AUTOMAXIMIZE:
+               if(ivideo->sisfb_max)
+                       return put_user((u32)1, argp);
+               else
+                       return put_user((u32)0, argp);
 
-       sisfb_update_var(con, info);
+          case SISFB_SET_AUTOMAXIMIZE_OLD:
+               if(ivideo->warncount++ < 10)
+                       printk(KERN_INFO
+                               "sisfb: Deprecated ioctl call received - update your application!\n");
+          case SISFB_SET_AUTOMAXIMIZE:
+               if(get_user(gpu32, argp))
+                       return -EFAULT;
 
-       return 1;
-}
+               ivideo->sisfb_max = (gpu32) ? 1 : 0;
+               break;
 
-static void sisfb_blank(int blank, struct fb_info *info)
-{
-       sisfb_myblank(blank);
-}
-#endif
+          case SISFB_SET_TVPOSOFFSET:
+               if(get_user(gpu32, argp))
+                       return -EFAULT;
 
-/* ------------ FBDev related routines for 2.5 series ----------- */
+               sisfb_set_TVxposoffset(ivideo, ((int)(gpu32 >> 16)) - 32);
+               sisfb_set_TVyposoffset(ivideo, ((int)(gpu32 & 0xffff)) - 32);
+               break;
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+          case SISFB_GET_TVPOSOFFSET:
+               return put_user((u32)(((ivideo->tvxpos+32)<<16)|((ivideo->tvypos+32)&0xffff)),
+                                                       argp);
 
-static int sisfb_open(struct fb_info *info, int user)
-{
-    return 0;
-}
+          case SISFB_COMMAND:
+               if(copy_from_user(&ivideo->sisfb_command, (void __user *)arg,
+                                                       sizeof(struct sisfb_cmd)))
+                       return -EFAULT;
 
-static int sisfb_release(struct fb_info *info, int user)
-{
-    return 0;
-}
+               sisfb_handle_command(ivideo, &ivideo->sisfb_command);
 
-static int sisfb_get_cmap_len(const struct fb_var_screeninfo *var)
-{
-       int rc = 16;            
+               if(copy_to_user((void __user *)arg, &ivideo->sisfb_command,
+                                                       sizeof(struct sisfb_cmd)))
+                       return -EFAULT;
 
-       switch(var->bits_per_pixel) {
-       case 8:
-               rc = 256;       
                break;
-       case 16:
-       case 32:
-               rc = 16;
-               break;
-       }
-       return rc;
-}
 
-static int sisfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue,
-                           unsigned transp, struct fb_info *info)
-{
-       if (regno >= sisfb_get_cmap_len(&info->var))
-               return 1;
+          case SISFB_SET_LOCK:
+               if(get_user(gpu32, argp))
+                       return -EFAULT;
 
-       switch (info->var.bits_per_pixel) {
-       case 8:
-               outSISREG(SISDACA, regno);
-               outSISREG(SISDACD, (red >> 10));
-               outSISREG(SISDACD, (green >> 10));
-               outSISREG(SISDACD, (blue >> 10));
-               if (ivideo.currentvbflags & VB_DISPTYPE_DISP2) {
-                       outSISREG(SISDAC2A, regno);
-                       outSISREG(SISDAC2D, (red >> 8));
-                       outSISREG(SISDAC2D, (green >> 8));
-                       outSISREG(SISDAC2D, (blue >> 8));
-               }
-               break;
-       case 16:
-               ((u32 *)(info->pseudo_palette))[regno] =
-                   ((red & 0xf800)) | ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11);
-               break;
-       case 32:
-               red >>= 8;
-               green >>= 8;
-               blue >>= 8;
-               ((u32 *) (info->pseudo_palette))[regno] =
-                       (red << 16) | (green << 8) | (blue);
+               ivideo->sisfblocked = (gpu32) ? 1 : 0;
                break;
+
+          default:
+#ifdef SIS_NEW_CONFIG_COMPAT
+               return -ENOIOCTLCMD;
+#else
+               return -EINVAL;
+#endif
        }
        return 0;
 }
 
-static int sisfb_set_par(struct fb_info *info)
+static int
+sisfb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
 {
-       int err;
+       struct sis_video_info *ivideo = (struct sis_video_info *)info->par;
 
-        if((err = sisfb_do_set_var(&info->var, 1, info)))
-               return err;
+       memset(fix, 0, sizeof(struct fb_fix_screeninfo));
 
-       sisfb_get_fix(&info->fix, info->currcon, info);
+       strcpy(fix->id, ivideo->myid);
+
+       fix->smem_start  = ivideo->video_base + ivideo->video_offset;
+       fix->smem_len    = ivideo->sisfb_mem;
+       fix->type        = FB_TYPE_PACKED_PIXELS;
+       fix->type_aux    = 0;
+       fix->visual      = (ivideo->video_bpp == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
+       fix->xpanstep    = 1;
+       fix->ypanstep    = (ivideo->sisfb_ypan) ? 1 : 0;
+       fix->ywrapstep   = 0;
+       fix->line_length = ivideo->video_linelength;
+       fix->mmio_start  = ivideo->mmio_base;
+       fix->mmio_len    = ivideo->mmio_size;
+       if(ivideo->sisvga_engine == SIS_300_VGA) {
+               fix->accel = FB_ACCEL_SIS_GLAMOUR;
+       } else if((ivideo->chip == SIS_330) ||
+                 (ivideo->chip == SIS_760) ||
+                 (ivideo->chip == SIS_761)) {
+               fix->accel = FB_ACCEL_SIS_XABRE;
+       } else if(ivideo->chip == XGI_20) {
+               fix->accel = FB_ACCEL_XGI_VOLARI_Z;
+       } else if(ivideo->chip >= XGI_40) {
+               fix->accel = FB_ACCEL_XGI_VOLARI_V;
+       } else {
+               fix->accel = FB_ACCEL_SIS_GLAMOUR_2;
+       }
 
        return 0;
 }
 
-static int sisfb_check_var(struct fb_var_screeninfo *var,
-                           struct fb_info *info)
-{
-       unsigned int htotal = 0, vtotal = 0, myrateindex = 0;
-       unsigned int drate = 0, hrate = 0, maxyres;
-       int found_mode = 0;
-       int refresh_rate, search_idx;
-       BOOLEAN recalc_clock = FALSE;
-       u32 pixclock;
+/* ----------------  fb_ops structures ----------------- */
 
-       htotal = var->left_margin + var->xres + var->right_margin + var->hsync_len;
+static struct fb_ops sisfb_ops = {
+       .owner          = THIS_MODULE,
+       .fb_open        = sisfb_open,
+       .fb_release     = sisfb_release,
+       .fb_check_var   = sisfb_check_var,
+       .fb_set_par     = sisfb_set_par,
+       .fb_setcolreg   = sisfb_setcolreg,
+       .fb_pan_display = sisfb_pan_display,
+       .fb_blank       = sisfb_blank,
+       .fb_fillrect    = fbcon_sis_fillrect,
+       .fb_copyarea    = fbcon_sis_copyarea,
+       .fb_imageblit   = cfb_imageblit,
+#ifdef CONFIG_FB_SOFT_CURSOR
+       .fb_cursor      = soft_cursor,
+#endif
+       .fb_sync        = fbcon_sis_sync,
+#ifdef SIS_NEW_CONFIG_COMPAT
+       .fb_compat_ioctl= sisfb_ioctl,
+#endif
+       .fb_ioctl       = sisfb_ioctl
+};
 
-       vtotal = var->upper_margin + var->lower_margin + var->vsync_len;
+/* ---------------- Chip generation dependent routines ---------------- */
 
-       pixclock = var->pixclock;
+static struct pci_dev * __devinit
+sisfb_get_northbridge(int basechipid)
+{
+       struct pci_dev *pdev = NULL;
+       int nbridgenum, nbridgeidx, i;
+       static const unsigned short nbridgeids[] = {
+               PCI_DEVICE_ID_SI_540,   /* for SiS 540 VGA */
+               PCI_DEVICE_ID_SI_630,   /* for SiS 630/730 VGA */
+               PCI_DEVICE_ID_SI_730,
+               PCI_DEVICE_ID_SI_550,   /* for SiS 550 VGA */
+               PCI_DEVICE_ID_SI_650,   /* for SiS 650/651/740 VGA */
+               PCI_DEVICE_ID_SI_651,
+               PCI_DEVICE_ID_SI_740,
+               PCI_DEVICE_ID_SI_661,   /* for SiS 661/741/660/760/761 VGA */
+               PCI_DEVICE_ID_SI_741,
+               PCI_DEVICE_ID_SI_660,
+               PCI_DEVICE_ID_SI_760,
+               PCI_DEVICE_ID_SI_761
+       };
+
+       switch(basechipid) {
+#ifdef CONFIG_FB_SIS_300
+       case SIS_540:   nbridgeidx = 0; nbridgenum = 1; break;
+       case SIS_630:   nbridgeidx = 1; nbridgenum = 2; break;
+#endif
+#ifdef CONFIG_FB_SIS_315
+       case SIS_550:   nbridgeidx = 3; nbridgenum = 1; break;
+       case SIS_650:   nbridgeidx = 4; nbridgenum = 3; break;
+       case SIS_660:   nbridgeidx = 7; nbridgenum = 5; break;
+#endif
+       default:        return NULL;
+       }
+       for(i = 0; i < nbridgenum; i++) {
+               if((pdev = SIS_PCI_GET_DEVICE(PCI_VENDOR_ID_SI,
+                               nbridgeids[nbridgeidx+i], NULL)))
+                       break;
+       }
+       return pdev;
+}
 
-       if((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) {
-               vtotal += var->yres;
-               vtotal <<= 1;
-       } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
-               vtotal += var->yres;
-               vtotal <<= 2;
-       } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
-               vtotal += var->yres;
-               vtotal <<= 1;
-       } else  vtotal += var->yres;
+static int __devinit
+sisfb_get_dram_size(struct sis_video_info *ivideo)
+{
+#if defined(CONFIG_FB_SIS_300) || defined(CONFIG_FB_SIS_315)
+       u8 reg;
+#endif
 
-       if(!(htotal) || !(vtotal)) {
-               SISFAIL("sisfb: no valid timing data");
-       }
+       ivideo->video_size = 0;
+       ivideo->UMAsize = ivideo->LFBsize = 0;
 
-       search_idx = 0;
-       while( (sisbios_mode[search_idx].mode_no != 0) &&
-              (sisbios_mode[search_idx].xres <= var->xres) ) {
-               if( (sisbios_mode[search_idx].xres == var->xres) &&
-                   (sisbios_mode[search_idx].yres == var->yres) &&
-                   (sisbios_mode[search_idx].bpp == var->bits_per_pixel)) {
-                       if(sisfb_validate_mode(search_idx, ivideo.currentvbflags) > 0) {
-                          found_mode = 1;
-                          break;
+       switch(ivideo->chip) {
+#ifdef CONFIG_FB_SIS_300
+       case SIS_300:
+               inSISIDXREG(SISSR, 0x14, reg);
+               ivideo->video_size = ((reg & 0x3F) + 1) << 20;
+               break;
+       case SIS_540:
+       case SIS_630:
+       case SIS_730:
+               if(!ivideo->nbridge)
+                       return -1;
+               pci_read_config_byte(ivideo->nbridge, 0x63, &reg);
+               ivideo->video_size = 1 << (((reg & 0x70) >> 4) + 21);
+               break;
+#endif
+#ifdef CONFIG_FB_SIS_315
+       case SIS_315H:
+       case SIS_315PRO:
+       case SIS_315:
+               inSISIDXREG(SISSR, 0x14, reg);
+               ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20;
+               switch((reg >> 2) & 0x03) {
+               case 0x01:
+               case 0x03:
+                       ivideo->video_size <<= 1;
+                       break;
+               case 0x02:
+                       ivideo->video_size += (ivideo->video_size/2);
+               }
+               break;
+       case SIS_330:
+               inSISIDXREG(SISSR, 0x14, reg);
+               ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20;
+               if(reg & 0x0c) ivideo->video_size <<= 1;
+               break;
+       case SIS_550:
+       case SIS_650:
+       case SIS_740:
+               inSISIDXREG(SISSR, 0x14, reg);
+               ivideo->video_size = (((reg & 0x3f) + 1) << 2) << 20;
+               break;
+       case SIS_661:
+       case SIS_741:
+               inSISIDXREG(SISCR, 0x79, reg);
+               ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20;
+               break;
+       case SIS_660:
+       case SIS_760:
+       case SIS_761:
+               inSISIDXREG(SISCR, 0x79, reg);
+               reg = (reg & 0xf0) >> 4;
+               if(reg) {
+                       ivideo->video_size = (1 << reg) << 20;
+                       ivideo->UMAsize = ivideo->video_size;
+               }
+               inSISIDXREG(SISCR, 0x78, reg);
+               reg &= 0x30;
+               if(reg) {
+                       if(reg == 0x10) {
+                               ivideo->LFBsize = (32 << 20);
+                       } else {
+                               ivideo->LFBsize = (64 << 20);
                        }
+                       ivideo->video_size += ivideo->LFBsize;
                }
-               search_idx++;
+               break;
+       case SIS_340:
+       case XGI_20:
+       case XGI_40:
+               inSISIDXREG(SISSR, 0x14, reg);
+               ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20;
+               if(ivideo->chip != XGI_20) {
+                       reg = (reg & 0x0c) >> 2;
+                       if(ivideo->revision_id == 2) {
+                               if(reg & 0x01) reg = 0x02;
+                               else           reg = 0x00;
+                       }
+                       if(reg == 0x02)         ivideo->video_size <<= 1;
+                       else if(reg == 0x03)    ivideo->video_size <<= 2;
+               }
+               break;
+#endif
+       default:
+               return -1;
        }
+       return 0;
+}
 
-       if(!found_mode) {
-
-                search_idx = 0;
-               while(sisbios_mode[search_idx].mode_no != 0) {
-                  if( (var->xres <= sisbios_mode[search_idx].xres) &&
-                      (var->yres <= sisbios_mode[search_idx].yres) &&
-                      (var->bits_per_pixel == sisbios_mode[search_idx].bpp) ) {
-                         if(sisfb_validate_mode(search_idx, ivideo.currentvbflags) > 0) {
-                            found_mode = 1;
-                            break;
-                         }
-                  }
-                  search_idx++;
-               }
-               if(found_mode) {
-                       printk(KERN_DEBUG "sisfb: Adapted from %dx%dx%d to %dx%dx%d\n",
-                               var->xres, var->yres, var->bits_per_pixel,
-                               sisbios_mode[search_idx].xres,
-                               sisbios_mode[search_idx].yres,
-                               var->bits_per_pixel);
-                       var->xres = sisbios_mode[search_idx].xres;
-                       var->yres = sisbios_mode[search_idx].yres;
+/* -------------- video bridge device detection --------------- */
 
+static void __devinit
+sisfb_detect_VB_connect(struct sis_video_info *ivideo)
+{
+       u8 cr32, temp;
 
-               } else {
-                       printk(KERN_ERR "sisfb: Failed to find supported mode near %dx%dx%d\n",
-                               var->xres, var->yres, var->bits_per_pixel);
-                       return -EINVAL;
-               }
+       /* No CRT2 on XGI Z7 */
+       if(ivideo->chip == XGI_20) {
+               ivideo->sisfb_crt1off = 0;
+               return;
        }
 
-       if( ((ivideo.vbflags & VB_LVDS) ||                      /* Slave modes on LVDS and 301B-DH */
-            ((ivideo.vbflags & VB_30xBDH) && (ivideo.currentvbflags & CRT2_LCD))) &&
-           (var->bits_per_pixel == 8) ) {
-               refresh_rate = 60;
-               recalc_clock = TRUE;
-       } else if( (ivideo.current_htotal == htotal) &&         /* x=x & y=y & c=c -> assume depth change */
-                  (ivideo.current_vtotal == vtotal) &&
-                  (ivideo.current_pixclock == pixclock) ) {
-               drate = 1000000000 / pixclock;
-               hrate = (drate * 1000) / htotal;
-               refresh_rate = (unsigned int) (hrate * 2 / vtotal);
-       } else if( ( (ivideo.current_htotal != htotal) ||       /* x!=x | y!=y & c=c -> invalid pixclock */
-                    (ivideo.current_vtotal != vtotal) ) &&
-                  (ivideo.current_pixclock == var->pixclock) ) {
-               if(sisfb_lastrates[sisbios_mode[search_idx].mode_no]) {
-                       refresh_rate = sisfb_lastrates[sisbios_mode[search_idx].mode_no];
-               } else if(sisfb_parm_rate != -1) {
-                       refresh_rate = sisfb_parm_rate;
-               } else {
-                       refresh_rate = 60;
+#ifdef CONFIG_FB_SIS_300
+       if(ivideo->sisvga_engine == SIS_300_VGA) {
+               inSISIDXREG(SISSR, 0x17, temp);
+               if((temp & 0x0F) && (ivideo->chip != SIS_300)) {
+                       /* PAL/NTSC is stored on SR16 on such machines */
+                       if(!(ivideo->vbflags & (TV_PAL | TV_NTSC | TV_PALM | TV_PALN))) {
+                               inSISIDXREG(SISSR, 0x16, temp);
+                               if(temp & 0x20)
+                                       ivideo->vbflags |= TV_PAL;
+                               else
+                                       ivideo->vbflags |= TV_NTSC;
+                       }
                }
-               recalc_clock = TRUE;
-       } else if((pixclock) && (htotal) && (vtotal)) {
-               drate = 1000000000 / pixclock;
-               hrate = (drate * 1000) / htotal;
-               refresh_rate = (unsigned int) (hrate * 2 / vtotal);
-       } else if(ivideo.current_refresh_rate) {
-               refresh_rate = ivideo.current_refresh_rate;
-               recalc_clock = TRUE;
-       } else {
-               refresh_rate = 60;
-               recalc_clock = TRUE;
        }
+#endif
 
-       myrateindex = sisfb_search_refresh_rate(refresh_rate, search_idx);
+       inSISIDXREG(SISCR, 0x32, cr32);
 
-       /* Eventually recalculate timing and clock */
-       if(recalc_clock) {
-          if(!myrateindex) myrateindex = sisbios_mode[search_idx].rate_idx;
-          var->pixclock = (u32) (1000000000 / sisfb_mode_rate_to_dclock(&SiS_Pr, &sishw_ext,
-                                               sisbios_mode[search_idx].mode_no, myrateindex));
-          sisfb_mode_rate_to_ddata(&SiS_Pr, &sishw_ext,
-                                       sisbios_mode[search_idx].mode_no, myrateindex,
-                                       &var->left_margin, &var->right_margin,
-                                       &var->upper_margin, &var->lower_margin,
-                                       &var->hsync_len, &var->vsync_len,
-                                       &var->sync, &var->vmode);
-          if((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
-               var->pixclock <<= 1;
-          }
+       if(cr32 & SIS_CRT1) {
+               ivideo->sisfb_crt1off = 0;
+       } else {
+               ivideo->sisfb_crt1off = (cr32 & 0xDF) ? 1 : 0;
        }
 
-       if(sisfb_thismonitor.datavalid) {
-          if(!sisfb_verify_rate(&sisfb_thismonitor, search_idx,
-                                myrateindex, refresh_rate)) {
-             printk(KERN_INFO "sisfb: WARNING: Refresh rate exceeds monitor specs!\n");
-          }
-       }
+       ivideo->vbflags &= ~(CRT2_TV | CRT2_LCD | CRT2_VGA);
 
-       /* Adapt RGB settings */
-       sisfb_bpp_to_var(var);  
-       
-       /* Sanity check for offsets */
-       if (var->xoffset < 0)
-               var->xoffset = 0;
-       if (var->yoffset < 0)
-               var->yoffset = 0;
+       if(cr32 & SIS_VB_TV)   ivideo->vbflags |= CRT2_TV;
+       if(cr32 & SIS_VB_LCD)  ivideo->vbflags |= CRT2_LCD;
+       if(cr32 & SIS_VB_CRT2) ivideo->vbflags |= CRT2_VGA;
 
-       /* Horiz-panning not supported */
-       if(var->xres != var->xres_virtual)
-               var->xres_virtual = var->xres;
+       /* Check given parms for hardware compatibility.
+        * (Cannot do this in the search_xx routines since we don't
+        * know what hardware we are running on then)
+        */
 
-       if(sisfb_ypan) {
-          maxyres = ivideo.heapstart / (var->xres * (var->bits_per_pixel >> 3));
-          if(maxyres > 32767) maxyres = 32767;
-          if(sisfb_max) {
-             var->yres_virtual = maxyres;
-          } else {
-             if(var->yres_virtual > maxyres) {
-                var->yres_virtual = maxyres;
+       if(ivideo->chip != SIS_550) {
+          ivideo->sisfb_dstn = ivideo->sisfb_fstn = 0;
+       }
+
+       if(ivideo->sisfb_tvplug != -1) {
+          if( (ivideo->sisvga_engine != SIS_315_VGA) ||
+              (!(ivideo->vbflags2 & VB2_SISYPBPRBRIDGE)) ) {
+             if(ivideo->sisfb_tvplug & TV_YPBPR) {
+                ivideo->sisfb_tvplug = -1;
+                printk(KERN_ERR "sisfb: YPbPr not supported\n");
              }
           }
-          if(var->yres_virtual <= var->yres) {
-             var->yres_virtual = var->yres;
+       }
+       if(ivideo->sisfb_tvplug != -1) {
+          if( (ivideo->sisvga_engine != SIS_315_VGA) ||
+              (!(ivideo->vbflags2 & VB2_SISHIVISIONBRIDGE)) ) {
+             if(ivideo->sisfb_tvplug & TV_HIVISION) {
+                ivideo->sisfb_tvplug = -1;
+                printk(KERN_ERR "sisfb: HiVision not supported\n");
+             }
           }
-       } else {
-          if(var->yres != var->yres_virtual) {
-             var->yres_virtual = var->yres;
+       }
+       if(ivideo->sisfb_tvstd != -1) {
+          if( (!(ivideo->vbflags2 & VB2_SISBRIDGE)) &&
+              (!((ivideo->sisvga_engine == SIS_315_VGA) &&
+                       (ivideo->vbflags2 & VB2_CHRONTEL))) ) {
+             if(ivideo->sisfb_tvstd & (TV_PALN | TV_PALN | TV_NTSCJ)) {
+                ivideo->sisfb_tvstd = -1;
+                printk(KERN_ERR "sisfb: PALM/PALN/NTSCJ not supported\n");
+             }
           }
-          var->xoffset = 0;
-          var->yoffset = 0;
        }
-       
-       /* Truncate offsets to maximum if too high */
-       if(var->xoffset > var->xres_virtual - var->xres)
-               var->xoffset = var->xres_virtual - var->xres - 1;
 
-       if(var->yoffset > var->yres_virtual - var->yres)
-               var->yoffset = var->yres_virtual - var->yres - 1;
-       
-       /* Set everything else to 0 */
-       var->red.msb_right = 
-           var->green.msb_right =
-           var->blue.msb_right =
-           var->transp.offset = var->transp.length = var->transp.msb_right = 0;                
+       /* Detect/set TV plug & type */
+       if(ivideo->sisfb_tvplug != -1) {
+               ivideo->vbflags |= ivideo->sisfb_tvplug;
+       } else {
+               if(cr32 & SIS_VB_YPBPR)          ivideo->vbflags |= (TV_YPBPR|TV_YPBPR525I); /* default: 480i */
+               else if(cr32 & SIS_VB_HIVISION)  ivideo->vbflags |= TV_HIVISION;
+               else if(cr32 & SIS_VB_SCART)     ivideo->vbflags |= TV_SCART;
+               else {
+                       if(cr32 & SIS_VB_SVIDEO)    ivideo->vbflags |= TV_SVIDEO;
+                       if(cr32 & SIS_VB_COMPOSITE) ivideo->vbflags |= TV_AVIDEO;
+               }
+       }
 
-       return 0;
-}
-
-static int sisfb_pan_display(struct fb_var_screeninfo *var,
-                            struct fb_info* info)
-{
-       int err;
-
-       if (var->xoffset > (var->xres_virtual - var->xres))
-               return -EINVAL;
-       if (var->yoffset > (var->yres_virtual - var->yres))
-               return -EINVAL;
-
-       if (var->vmode & FB_VMODE_YWRAP) {
-               if (var->yoffset < 0 ||
-                   var->yoffset >= info->var.yres_virtual ||
-                   var->xoffset)
-                       return -EINVAL;
-       } else {
-               if (var->xoffset + info->var.xres > info->var.xres_virtual ||
-                   var->yoffset + info->var.yres > info->var.yres_virtual)
-                       return -EINVAL;
+       if(!(ivideo->vbflags & (TV_YPBPR | TV_HIVISION))) {
+           if(ivideo->sisfb_tvstd != -1) {
+              ivideo->vbflags &= ~(TV_NTSC | TV_PAL | TV_PALM | TV_PALN | TV_NTSCJ);
+              ivideo->vbflags |= ivideo->sisfb_tvstd;
+           }
+           if(ivideo->vbflags & TV_SCART) {
+              ivideo->vbflags &= ~(TV_NTSC | TV_PALM | TV_PALN | TV_NTSCJ);
+              ivideo->vbflags |= TV_PAL;
+           }
+           if(!(ivideo->vbflags & (TV_PAL | TV_NTSC | TV_PALM | TV_PALN | TV_NTSCJ))) {
+               if(ivideo->sisvga_engine == SIS_300_VGA) {
+                       inSISIDXREG(SISSR, 0x38, temp);
+                       if(temp & 0x01) ivideo->vbflags |= TV_PAL;
+                       else            ivideo->vbflags |= TV_NTSC;
+               } else if((ivideo->chip <= SIS_315PRO) || (ivideo->chip >= SIS_330)) {
+                       inSISIDXREG(SISSR, 0x38, temp);
+                       if(temp & 0x01) ivideo->vbflags |= TV_PAL;
+                       else            ivideo->vbflags |= TV_NTSC;
+               } else {
+                       inSISIDXREG(SISCR, 0x79, temp);
+                       if(temp & 0x20) ivideo->vbflags |= TV_PAL;
+                       else            ivideo->vbflags |= TV_NTSC;
+               }
+           }
        }
-    
-       if((err = sisfb_pan_var(var)) < 0) return err;
 
-       info->var.xoffset = var->xoffset;
-       info->var.yoffset = var->yoffset;
-       if (var->vmode & FB_VMODE_YWRAP)
-               info->var.vmode |= FB_VMODE_YWRAP;
-       else
-               info->var.vmode &= ~FB_VMODE_YWRAP;
-
-       return 0;
+       /* Copy forceCRT1 option to CRT1off if option is given */
+       if(ivideo->sisfb_forcecrt1 != -1) {
+          ivideo->sisfb_crt1off = (ivideo->sisfb_forcecrt1) ? 0 : 1;
+       }
 }
 
-static int sisfb_mmap(struct fb_info *info, struct file *file,
-                     struct vm_area_struct *vma)
-{
-       unsigned long start;
-       unsigned long off;
-       u32 len, mmio_off;
+/* ------------------ Sensing routines ------------------ */
 
-       if(vma->vm_pgoff > (~0UL >> PAGE_SHIFT))  return -EINVAL;
+static BOOLEAN __devinit
+sisfb_test_DDC1(struct sis_video_info *ivideo)
+{
+    unsigned short old;
+    int count = 48;
 
-       off = vma->vm_pgoff << PAGE_SHIFT;
+    old = SiS_ReadDDC1Bit(&ivideo->SiS_Pr);
+    do {
+       if(old != SiS_ReadDDC1Bit(&ivideo->SiS_Pr)) break;
+    } while(count--);
+    return (count == -1) ? FALSE : TRUE;
+}
 
-       start = (unsigned long) ivideo.video_base;
-       len = PAGE_ALIGN((start & ~PAGE_MASK) + ivideo.video_size);
-#if 0
-       if (off >= len) {
-               off -= len;
+static void __devinit
+sisfb_sense_crt1(struct sis_video_info *ivideo)
+{
+    BOOLEAN mustwait = FALSE;
+    u8  sr1F, cr17;
+#ifdef CONFIG_FB_SIS_315
+    u8  cr63=0;
 #endif
-       /* By Jake Page: Treat mmap request with offset beyond heapstart
-        *               as request for mapping the mmio area 
-        */
-       mmio_off = PAGE_ALIGN((start & ~PAGE_MASK) + ivideo.heapstart);
-       if(off >= mmio_off) {
-               off -= mmio_off;                
-               if(info->var.accel_flags) return -EINVAL;
-
-               start = (unsigned long) ivideo.mmio_base;
-               len = PAGE_ALIGN((start & ~PAGE_MASK) + sisfb_mmio_size);
-       }
-
-       start &= PAGE_MASK;
-       if((vma->vm_end - vma->vm_start + off) > len)   return -EINVAL;
+    u16 temp = 0xffff;
+    int i;
 
-       off += start;
-       vma->vm_pgoff = off >> PAGE_SHIFT;
-       vma->vm_flags |= VM_IO;   /* by Jake Page; is that really needed? */
+    inSISIDXREG(SISSR,0x1F,sr1F);
+    orSISIDXREG(SISSR,0x1F,0x04);
+    andSISIDXREG(SISSR,0x1F,0x3F);
+    if(sr1F & 0xc0) mustwait = TRUE;
 
-#if defined(__i386__) || defined(__x86_64__)
-       if (boot_cpu_data.x86 > 3)
-               pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
+#ifdef CONFIG_FB_SIS_315
+    if(ivideo->sisvga_engine == SIS_315_VGA) {
+       inSISIDXREG(SISCR,ivideo->SiS_Pr.SiS_MyCR63,cr63);
+       cr63 &= 0x40;
+       andSISIDXREG(SISCR,ivideo->SiS_Pr.SiS_MyCR63,0xBF);
+    }
 #endif
-       if (io_remap_page_range(vma, vma->vm_start, off, vma->vm_end - vma->vm_start,
-                               vma->vm_page_prot))
-               return -EAGAIN;
 
-       return 0;
-}
+    inSISIDXREG(SISCR,0x17,cr17);
+    cr17 &= 0x80;
+    if(!cr17) {
+       orSISIDXREG(SISCR,0x17,0x80);
+       mustwait = TRUE;
+       outSISIDXREG(SISSR, 0x00, 0x01);
+       outSISIDXREG(SISSR, 0x00, 0x03);
+    }
 
-static int sisfb_blank(int blank, struct fb_info *info)
-{
-       return(sisfb_myblank(blank));
-}
+    if(mustwait) {
+       for(i=0; i < 10; i++) sisfbwaitretracecrt1(ivideo);
+    }
 
+#ifdef CONFIG_FB_SIS_315
+    if(ivideo->chip >= SIS_330) {
+       andSISIDXREG(SISCR,0x32,~0x20);
+       if(ivideo->chip >= SIS_340) {
+          outSISIDXREG(SISCR, 0x57, 0x4a);
+       } else {
+          outSISIDXREG(SISCR, 0x57, 0x5f);
+       }
+       orSISIDXREG(SISCR, 0x53, 0x02);
+       while((inSISREG(SISINPSTAT)) & 0x01)    break;
+       while(!((inSISREG(SISINPSTAT)) & 0x01)) break;
+       if((inSISREG(SISMISCW)) & 0x10) temp = 1;
+       andSISIDXREG(SISCR, 0x53, 0xfd);
+       andSISIDXREG(SISCR, 0x57, 0x00);
+    }
 #endif
 
-/* ----------- FBDev related routines for all series ---------- */
+    if(temp == 0xffff) {
+       i = 3;
+       do {
+         temp = SiS_HandleDDC(&ivideo->SiS_Pr, ivideo->vbflags,
+               ivideo->sisvga_engine, 0, 0, NULL, ivideo->vbflags2);
+       } while(((temp == 0) || (temp == 0xffff)) && i--);
 
+       if((temp == 0) || (temp == 0xffff)) {
+          if(sisfb_test_DDC1(ivideo)) temp = 1;
+       }
+    }
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-static int sisfb_ioctl(struct inode *inode, struct file *file,
-                      unsigned int cmd, unsigned long arg,
-                      struct fb_info *info)
-#else
-static int sisfb_ioctl(struct inode *inode, struct file *file,
-                      unsigned int cmd, unsigned long arg, int con,
-                      struct fb_info *info)
-#endif
-{
-       struct sis_memreq sismemreq;
-       struct ap_data sisapdata;
-       unsigned long sismembase = 0;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-       SIS_GLYINFO sisglyinfo;
-#endif
+    if((temp) && (temp != 0xffff)) {
+       orSISIDXREG(SISCR,0x32,0x20);
+    }
 
-       switch (cmd) {
-          case FBIO_ALLOC:
-               if(!capable(CAP_SYS_RAWIO))
-                       return -EPERM;
-               if(copy_from_user(&sismemreq, (void *)arg, sizeof(sismemreq)))
-                       return -EFAULT;
-               sis_malloc(&sismemreq);
-               if(copy_to_user((void *)arg, &sismemreq, sizeof(sismemreq))) {
-                       sis_free(sismemreq.offset);
-                       return -EFAULT;
-               }
-               break;
-          case FBIO_FREE:
-               if(!capable(CAP_SYS_RAWIO))
-                       return -EPERM;
-               if(get_user(sismembase, (unsigned long *) arg))
-                       return -EFAULT;
-               sis_free(sismembase);
-               break;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-          case FBIOGET_GLYPH:
-               if(copy_from_user(&sisglyinfo, (void *)arg, sizeof(sisglyinfo)))
-                       return -EFAULT;
-                sis_get_glyph(info, &sisglyinfo);
-               break;
-          case FBIOPUT_MODEINFO:
-               {
-                       struct mode_info x;
-
-                       if(copy_from_user(&x, (void *)arg, sizeof(x)))
-                               return -EFAULT;
-
-                       ivideo.video_bpp        = x.bpp;
-                       ivideo.video_width      = x.xres;
-                       ivideo.video_height     = x.yres;
-                       ivideo.video_vwidth     = x.v_xres;
-                       ivideo.video_vheight    = x.v_yres;
-                       ivideo.org_x            = x.org_x;
-                       ivideo.org_y            = x.org_y;
-                       ivideo.refresh_rate     = x.vrate;
-                       ivideo.video_linelength = ivideo.video_vwidth * (ivideo.video_bpp >> 3);
-                       sisfb_set_vparms();
-                       break;
-               }
+#ifdef CONFIG_FB_SIS_315
+    if(ivideo->sisvga_engine == SIS_315_VGA) {
+       setSISIDXREG(SISCR,ivideo->SiS_Pr.SiS_MyCR63,0xBF,cr63);
+    }
 #endif
-          case FBIOGET_HWCINFO:
-               {
-                       unsigned long myhwcoffset = 0;
-
-                       if(sisfb_caps & HW_CURSOR_CAP)
-                               myhwcoffset = sisfb_hwcursor_vbase -
-                                   (unsigned long) ivideo.video_vbase;
-
-                       return put_user(myhwcoffset, (unsigned long *)arg);
-
-                       break;
-               }
-          case FBIOGET_DISPINFO:
-               sis_dispinfo(&sisapdata);
-               if(copy_to_user((void *)arg, &sisapdata, sizeof(sisapdata)))
-                       return -EFAULT;
-               break;
-          case SISFB_GET_INFO:  /* For communication with X driver */
-               {
-                       sisfb_info x;
-
-                       x.sisfb_id = SISFB_ID;
-                       x.sisfb_version = VER_MAJOR;
-                       x.sisfb_revision = VER_MINOR;
-                       x.sisfb_patchlevel = VER_LEVEL;
-                       x.chip_id = ivideo.chip_id;
-                       x.memory = ivideo.video_size / 1024;
-                       x.heapstart = ivideo.heapstart / 1024;
-                       x.fbvidmode = sisfb_mode_no;
-                       x.sisfb_caps = sisfb_caps;
-                       x.sisfb_tqlen = 512; /* yet fixed */
-                       x.sisfb_pcibus = ivideo.pcibus;
-                       x.sisfb_pcislot = ivideo.pcislot;
-                       x.sisfb_pcifunc = ivideo.pcifunc;
-                       x.sisfb_lcdpdc = sisfb_detectedpdc;
-                       x.sisfb_lcda = sisfb_detectedlcda;
-                       x.sisfb_vbflags = ivideo.vbflags;
-                       x.sisfb_currentvbflags = ivideo.currentvbflags;
-                       x.sisfb_scalelcd = SiS_Pr.UsePanelScaler;
-                       x.sisfb_specialtiming = SiS_Pr.SiS_CustomT;
-                       x.sisfb_haveemi = SiS_Pr.HaveEMI ? 1 : 0;
-                       x.sisfb_haveemilcd = SiS_Pr.HaveEMILCD ? 1 : 0;
-                       x.sisfb_emi30 = SiS_Pr.EMI_30;
-                       x.sisfb_emi31 = SiS_Pr.EMI_31;
-                       x.sisfb_emi32 = SiS_Pr.EMI_32;
-                       x.sisfb_emi33 = SiS_Pr.EMI_33;
-                       if(copy_to_user((void *)arg, &x, sizeof(x)))
-                               return -EFAULT;
-                       break;
-               }
-          case SISFB_GET_VBRSTATUS:
-               {
-                       if(sisfb_CheckVBRetrace())
-                               return put_user(1UL, (unsigned long *) arg);
-                       else
-                               return put_user(0UL, (unsigned long *) arg);
-                       break;
-               }
-          case SISFB_GET_AUTOMAXIMIZE:
-               {
-                       if(sisfb_max)
-                               return put_user(1UL, (unsigned long *) arg);
-                       else
-                               return put_user(0UL, (unsigned long *) arg);
-                       break;
-               }
-          case SISFB_SET_AUTOMAXIMIZE:
-               {
-                       unsigned long newmax;
 
-                       if(copy_from_user(&newmax, (unsigned long *)arg, sizeof(newmax)))
-                               return -EFAULT;
+    setSISIDXREG(SISCR,0x17,0x7F,cr17);
 
-                       if(newmax) sisfb_max = 1;
-                       else       sisfb_max = 0;
-                       break;
-               }
-          default:
-               return -EINVAL;
-       }
-       return 0;
+    outSISIDXREG(SISSR,0x1F,sr1F);
 }
 
-
-static int sisfb_get_fix(struct fb_fix_screeninfo *fix, int con,
-                        struct fb_info *info)
+/* Determine and detect attached devices on SiS30x */
+static void __devinit
+SiS_SenseLCD(struct sis_video_info *ivideo)
 {
-       memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+       unsigned char buffer[256];
+       unsigned short temp, realcrtno, i;
+       u8 reg, cr37 = 0, paneltype = 0;
+       u16 xres, yres;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) 
-       strcpy(fix->id, sis_fb_info->modename);
-#else
-       strcpy(fix->id, myid);
-#endif 
-
-       fix->smem_start = ivideo.video_base;
-
-        if((!sisfb_mem) || (sisfb_mem > (ivideo.video_size/1024))) {
-           if(sisvga_engine == SIS_300_VGA) {
-              if(ivideo.video_size > 0x1000000) {
-                       fix->smem_len = 0xc00000;
-              } else if(ivideo.video_size > 0x800000)
-                       fix->smem_len = 0x800000;
-              else
-                       fix->smem_len = 0x400000;
-            } else {
-               fix->smem_len = ivideo.video_size - 0x100000;
-           }
-        } else
-               fix->smem_len = sisfb_mem * 1024;
+       ivideo->SiS_Pr.PanelSelfDetected = FALSE;
 
-       fix->type        = FB_TYPE_PACKED_PIXELS;
-       fix->type_aux    = 0;
-       if(ivideo.video_bpp == 8)
-          fix->visual = FB_VISUAL_PSEUDOCOLOR;
-       else
-          fix->visual = FB_VISUAL_TRUECOLOR;
-       fix->xpanstep    = 0;
+       /* LCD detection only for TMDS bridges */
+       if(!(ivideo->vbflags2 & VB2_SISTMDSBRIDGE))
+               return;
+       if(ivideo->vbflags2 & VB2_30xBDH)
+               return;
 
-        if(sisfb_ypan)          fix->ypanstep = 1;
+       /* If LCD already set up by BIOS, skip it */
+       inSISIDXREG(SISCR, 0x32, reg);
+       if(reg & 0x08)
+               return;
 
-       fix->ywrapstep   = 0;
-       fix->line_length = ivideo.video_linelength;
-       fix->mmio_start  = ivideo.mmio_base;
-       fix->mmio_len    = sisfb_mmio_size;
-       if(sisvga_engine == SIS_300_VGA) 
-          fix->accel    = FB_ACCEL_SIS_GLAMOUR;
-       else if((ivideo.chip == SIS_330) || (ivideo.chip == SIS_760))
-          fix->accel    = FB_ACCEL_SIS_XABRE;
-       else
-          fix->accel    = FB_ACCEL_SIS_GLAMOUR_2;
+       realcrtno = 1;
+       if(ivideo->SiS_Pr.DDCPortMixup)
+               realcrtno = 0;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-       fix->reserved[0] = ivideo.video_size & 0xFFFF;
-       fix->reserved[1] = (ivideo.video_size >> 16) & 0xFFFF;
-       fix->reserved[2] = sisfb_caps;
-#endif
+       /* Check DDC capabilities */
+       temp = SiS_HandleDDC(&ivideo->SiS_Pr, ivideo->vbflags, ivideo->sisvga_engine,
+                               realcrtno, 0, &buffer[0], ivideo->vbflags2);
 
-       return 0;
-}
+       if((!temp) || (temp == 0xffff) || (!(temp & 0x02)))
+               return;
 
-/* ----------------  fb_ops structures ----------------- */
+       /* Read DDC data */
+       i = 3;  /* Number of retrys */
+       do {
+               temp = SiS_HandleDDC(&ivideo->SiS_Pr, ivideo->vbflags,
+                               ivideo->sisvga_engine, realcrtno, 1,
+                               &buffer[0], ivideo->vbflags2);
+       } while((temp) && i--);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-static struct fb_ops sisfb_ops = {
-       .owner          = THIS_MODULE,
-       .fb_get_fix     = sisfb_get_fix,
-       .fb_get_var     = sisfb_get_var,
-       .fb_set_var     = sisfb_set_var,
-       .fb_get_cmap    = sisfb_get_cmap,
-       .fb_set_cmap    = sisfb_set_cmap,
-        .fb_pan_display = sisfb_pan_display,
-       .fb_ioctl       = sisfb_ioctl,
-       .fb_mmap        = sisfb_mmap,
-};
-#endif
+       if(temp)
+               return;
 
+       /* No digital device */
+       if(!(buffer[0x14] & 0x80))
+               return;
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-static struct fb_ops sisfb_ops = {
-       .owner        = THIS_MODULE,
-       .fb_open      = sisfb_open,
-       .fb_release   = sisfb_release,
-       .fb_check_var = sisfb_check_var,
-       .fb_set_par   = sisfb_set_par,
-       .fb_setcolreg = sisfb_setcolreg,
-        .fb_pan_display = sisfb_pan_display,
-        .fb_blank     = sisfb_blank,
-       .fb_fillrect  = fbcon_sis_fillrect,
-       .fb_copyarea  = fbcon_sis_copyarea,
-       .fb_imageblit = cfb_imageblit,
-       .fb_cursor    = soft_cursor,    
-       .fb_sync      = fbcon_sis_sync,
-       .fb_ioctl     = sisfb_ioctl,
-       .fb_mmap      = sisfb_mmap,
-};
-#endif
+       /* First detailed timing preferred timing? */
+       if(!(buffer[0x18] & 0x02))
+               return;
 
+       xres = buffer[0x38] | ((buffer[0x3a] & 0xf0) << 4);
+       yres = buffer[0x3b] | ((buffer[0x3d] & 0xf0) << 4);
 
-/* ---------------- Chip generation dependent routines ---------------- */
+       switch(xres) {
+               case 1024:
+                       if(yres == 768)
+                               paneltype = 0x02;
+                       break;
+               case 1280:
+                       if(yres == 1024)
+                               paneltype = 0x03;
+                       break;
+               case 1600:
+                       if((yres == 1200) && (ivideo->vbflags2 & VB2_30xC))
+                               paneltype = 0x0b;
+                       break;
+       }
 
-#ifdef CONFIG_FB_SIS_300 /* for SiS 300/630/540/730 */
+       if(!paneltype)
+               return;
 
-static int sisfb_get_dram_size_300(void)
-{
-       struct pci_dev *pdev = NULL;
-       int pdev_valid = 0;
-       u8  pci_data, reg;
-       u16 nbridge_id;
+       if(buffer[0x23])
+               cr37 |= 0x10;
 
-       switch (ivideo.chip) {
-          case SIS_540:
-               nbridge_id = PCI_DEVICE_ID_SI_540;
-               break;
-          case SIS_630:
-               nbridge_id = PCI_DEVICE_ID_SI_630;
-               break;
-          case SIS_730:
-               nbridge_id = PCI_DEVICE_ID_SI_730;
-               break;
-          default:
-               nbridge_id = 0;
-               break;
-       }
+       if((buffer[0x47] & 0x18) == 0x18)
+               cr37 |= ((((buffer[0x47] & 0x06) ^ 0x06) << 5) | 0x20);
+       else
+               cr37 |= 0xc0;
 
-       if (nbridge_id == 0) {  /* 300 */
+       outSISIDXREG(SISCR, 0x36, paneltype);
+       cr37 &= 0xf1;
+       setSISIDXREG(SISCR, 0x37, 0x0c, cr37);
+       orSISIDXREG(SISCR, 0x32, 0x08);
 
-               inSISIDXREG(SISSR, IND_SIS_DRAM_SIZE,reg);
-               ivideo.video_size =
-                       ((unsigned int) ((reg & SIS_DRAM_SIZE_MASK) + 1) << 20);
+       ivideo->SiS_Pr.PanelSelfDetected = TRUE;
+}
 
-       } else {                /* 540, 630, 730 */
+static int __devinit
+SISDoSense(struct sis_video_info *ivideo, u16 type, u16 test)
+{
+    int temp, mytest, result, i, j;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,74)
-               pci_for_each_dev(pdev) {
-#else
-               while((pdev = pci_find_device(PCI_VENDOR_ID_SI, PCI_ANY_ID, pdev))) {
+    for(j = 0; j < 10; j++) {
+       result = 0;
+       for(i = 0; i < 3; i++) {
+          mytest = test;
+          outSISIDXREG(SISPART4,0x11,(type & 0x00ff));
+          temp = (type >> 8) | (mytest & 0x00ff);
+          setSISIDXREG(SISPART4,0x10,0xe0,temp);
+          SiS_DDC2Delay(&ivideo->SiS_Pr, 0x1500);
+          mytest >>= 8;
+          mytest &= 0x7f;
+          inSISIDXREG(SISPART4,0x03,temp);
+          temp ^= 0x0e;
+          temp &= mytest;
+          if(temp == mytest) result++;
+#if 1
+         outSISIDXREG(SISPART4,0x11,0x00);
+         andSISIDXREG(SISPART4,0x10,0xe0);
+         SiS_DDC2Delay(&ivideo->SiS_Pr, 0x1000);
 #endif
-                       if ((pdev->vendor == PCI_VENDOR_ID_SI)
-                                      && (pdev->device == nbridge_id)) {
-                               pci_read_config_byte(pdev, IND_BRI_DRAM_STATUS, &pci_data);
-                               pci_data = (pci_data & BRI_DRAM_SIZE_MASK) >> 4;
-                               ivideo.video_size = (unsigned int)(1 << (pci_data+21));
-                               pdev_valid = 1;
-
-                               reg = SIS_DATA_BUS_64 << 6;
-                               switch (pci_data) {
-                                  case BRI_DRAM_SIZE_2MB:
-                                       reg |= SIS_DRAM_SIZE_2MB;
-                                       break;
-                                  case BRI_DRAM_SIZE_4MB:
-                                       reg |= SIS_DRAM_SIZE_4MB;
-                                       break;
-                                  case BRI_DRAM_SIZE_8MB:
-                                       reg |= SIS_DRAM_SIZE_8MB;
-                                       break;
-                                  case BRI_DRAM_SIZE_16MB:
-                                       reg |= SIS_DRAM_SIZE_16MB;
-                                       break;
-                                  case BRI_DRAM_SIZE_32MB:
-                                       reg |= SIS_DRAM_SIZE_32MB;
-                                       break;
-                                  case BRI_DRAM_SIZE_64MB:
-                                       reg |= SIS_DRAM_SIZE_64MB;
-                                       break;
-                               }
-                               outSISIDXREG(SISSR, IND_SIS_DRAM_SIZE, reg);
-                               break;
-                       }  
-               }
-       
-               if (!pdev_valid)  return -1;
-       }
-       return 0;
+       }
+       if((result == 0) || (result >= 2)) break;
+    }
+    return result;
 }
 
-#endif  /* CONFIG_FB_SIS_300 */
-
-
-#ifdef CONFIG_FB_SIS_315    /* for SiS 315/550/650/740/330/661/741/760 */
-
-static int sisfb_get_dram_size_315(void)
+static void __devinit
+SiS_Sense30x(struct sis_video_info *ivideo)
 {
-       u8  reg = 0;
-
-       if(ivideo.chip == SIS_550 ||
-          ivideo.chip == SIS_650 ||
-          ivideo.chip == SIS_740) {
-
-                inSISIDXREG(SISSR, IND_SIS_DRAM_SIZE, reg);
-               reg &= 0x3f;
-               reg++;
-               reg <<= 2;
-               ivideo.video_size = reg << 20;
-               return 0;
+    u8  backupP4_0d,backupP2_00,backupP2_4d,backupSR_1e,biosflag=0;
+    u16 svhs=0, svhs_c=0;
+    u16 cvbs=0, cvbs_c=0;
+    u16 vga2=0, vga2_c=0;
+    int myflag, result;
+    char stdstr[] = "sisfb: Detected";
+    char tvstr[]  = "TV connected to";
+
+    if(ivideo->vbflags2 & VB2_301) {
+       svhs = 0x00b9; cvbs = 0x00b3; vga2 = 0x00d1;
+       inSISIDXREG(SISPART4,0x01,myflag);
+       if(myflag & 0x04) {
+         svhs = 0x00dd; cvbs = 0x00ee; vga2 = 0x00fd;
+       }
+    } else if(ivideo->vbflags2 & (VB2_301B | VB2_302B)) {
+       svhs = 0x016b; cvbs = 0x0174; vga2 = 0x0190;
+    } else if(ivideo->vbflags2 & (VB2_301LV | VB2_302LV)) {
+       svhs = 0x0200; cvbs = 0x0100;
+    } else if(ivideo->vbflags2 & (VB2_301C | VB2_302ELV | VB2_307T | VB2_307LV)) {
+       svhs = 0x016b; cvbs = 0x0110; vga2 = 0x0190;
+    } else
+       return;
+
+    vga2_c = 0x0e08; svhs_c = 0x0404; cvbs_c = 0x0804;
+    if(ivideo->vbflags & (VB2_301LV|VB2_302LV|VB2_302ELV|VB2_307LV)) {
+       svhs_c = 0x0408; cvbs_c = 0x0808;
+    }
 
-       } else if(ivideo.chip == SIS_661 ||
-                 ivideo.chip == SIS_741 ||
-                 ivideo.chip == SIS_660 ||
-                 ivideo.chip == SIS_760) {
+    biosflag = 2;
+    if(ivideo->haveXGIROM) {
+       biosflag = ivideo->bios_abase[0x58] & 0x03;
+    } else if(ivideo->newrom) {
+       if(ivideo->bios_abase[0x5d] & 0x04) biosflag |= 0x01;
+    } else if(ivideo->sisvga_engine == SIS_300_VGA) {
+       if(ivideo->bios_abase) {
+          biosflag = ivideo->bios_abase[0xfe] & 0x03;
+       }
+    }
 
-               inSISIDXREG(SISCR, 0x79, reg);
-               reg &= 0xf0;
-               reg >>= 4;
-               ivideo.video_size = (1 << reg) << 20;
-               return 0;
+    if(ivideo->chip == SIS_300) {
+       inSISIDXREG(SISSR,0x3b,myflag);
+       if(!(myflag & 0x01)) vga2 = vga2_c = 0;
+    }
 
-       } else {        /* 315, 330 */
+    if(!(ivideo->vbflags2 & VB2_SISVGA2BRIDGE)) {
+       vga2 = vga2_c = 0;
+    }
 
-               inSISIDXREG(SISSR, IND_SIS_DRAM_SIZE, reg);
-               ivideo.video_size = (1 << ((reg & 0xf0) >> 4)) << 20;
+    inSISIDXREG(SISSR,0x1e,backupSR_1e);
+    orSISIDXREG(SISSR,0x1e,0x20);
 
-               reg &= SIS315_DUAL_CHANNEL_MASK;
-               reg >>= 2;
+    inSISIDXREG(SISPART4,0x0d,backupP4_0d);
+    if(ivideo->vbflags2 & VB2_30xC) {
+       setSISIDXREG(SISPART4,0x0d,~0x07,0x01);
+    } else {
+       orSISIDXREG(SISPART4,0x0d,0x04);
+    }
+    SiS_DDC2Delay(&ivideo->SiS_Pr, 0x2000);
 
-               if(ivideo.chip == SIS_330) {
+    inSISIDXREG(SISPART2,0x00,backupP2_00);
+    outSISIDXREG(SISPART2,0x00,((backupP2_00 | 0x1c) & 0xfc));
 
-                  if(reg) ivideo.video_size <<= 1;
-               
-               } else {
-                  
-                  switch (reg) {
-                     case SIS315_SINGLE_CHANNEL_2_RANK:
-                          ivideo.video_size <<= 1;
-                          break;
-                     case SIS315_DUAL_CHANNEL_1_RANK:
-                          ivideo.video_size <<= 1;
-                          break;
-                     case SIS315_ASYM_DDR:             /* TW: DDR asymetric */
-                          ivideo.video_size += (ivideo.video_size/2);
-                          break;
-                  }
-               }
+    inSISIDXREG(SISPART2,0x4d,backupP2_4d);
+    if(ivideo->vbflags2 & VB2_SISYPBPRBRIDGE) {
+       outSISIDXREG(SISPART2,0x4d,(backupP2_4d & ~0x10));
+    }
 
-               return 0;
-       }
-       
-       return -1;
-       
-}
+    if(!(ivideo->vbflags2 & VB2_30xCLV)) {
+       SISDoSense(ivideo, 0, 0);
+    }
 
-#endif   /* CONFIG_FB_SIS_315 */
+    andSISIDXREG(SISCR, 0x32, ~0x14);
 
+    if(vga2_c || vga2) {
+       if(SISDoSense(ivideo, vga2, vga2_c)) {
+          if(biosflag & 0x01) {
+            printk(KERN_INFO "%s %s SCART output\n", stdstr, tvstr);
+            orSISIDXREG(SISCR, 0x32, 0x04);
+         } else {
+            printk(KERN_INFO "%s secondary VGA connection\n", stdstr);
+            orSISIDXREG(SISCR, 0x32, 0x10);
+         }
+       }
+    }
 
-/* -------------- video bridge detection --------------- */
+    andSISIDXREG(SISCR, 0x32, 0x3f);
 
-static void sisfb_detect_VB_connect()
-{
-       u8 sr16, sr17, cr32, temp;
+    if(ivideo->vbflags2 & VB2_30xCLV) {
+       orSISIDXREG(SISPART4,0x0d,0x04);
+    }
 
-       if(sisvga_engine == SIS_300_VGA) {
+    if((ivideo->sisvga_engine == SIS_315_VGA) && (ivideo->vbflags2 & VB2_SISYPBPRBRIDGE)) {
+       outSISIDXREG(SISPART2,0x4d,(backupP2_4d | 0x10));
+       SiS_DDC2Delay(&ivideo->SiS_Pr, 0x2000);
+       if((result = SISDoSense(ivideo, svhs, 0x0604))) {
+          if((result = SISDoSense(ivideo, cvbs, 0x0804))) {
+            printk(KERN_INFO "%s %s YPbPr component output\n", stdstr, tvstr);
+            orSISIDXREG(SISCR,0x32,0x80);
+         }
+       }
+       outSISIDXREG(SISPART2,0x4d,backupP2_4d);
+    }
 
-               inSISIDXREG(SISSR, IND_SIS_SCRATCH_REG_17, sr17);
+    andSISIDXREG(SISCR, 0x32, ~0x03);
 
-               if ((sr17 & 0x0F) && (ivideo.chip != SIS_300)) {
+    if(!(ivideo->vbflags & TV_YPBPR)) {
+       if((result = SISDoSense(ivideo, svhs, svhs_c))) {
+          printk(KERN_INFO "%s %s SVIDEO output\n", stdstr, tvstr);
+          orSISIDXREG(SISCR, 0x32, 0x02);
+       }
+       if((biosflag & 0x02) || (!result)) {
+          if(SISDoSense(ivideo, cvbs, cvbs_c)) {
+            printk(KERN_INFO "%s %s COMPOSITE output\n", stdstr, tvstr);
+            orSISIDXREG(SISCR, 0x32, 0x01);
+          }
+       }
+    }
 
-                       /* Old BIOSes store the detected CRT2 type in SR17
-                        * instead of CR32. However, since our detection
-                        * routines store their results to CR32, we now copy
-                        * the remaining bits (for LCD and VGA) to CR32 for
-                        * unified usage.
-                        * SR17[0] CRT1    [1] LCD     [2] TV    [3] VGA2
-                        *     [4] AVIDEO  [5] SVIDEO
-                        */
+    SISDoSense(ivideo, 0, 0);
 
-#if 0
-                       if (sr17 & 0x01) orSISIDXREG(SISCR, 0x32, SIS_CRT1);
-                       else             andSISIDXREG(SISCR, 0x32, ~SIS_CRT1);
+    outSISIDXREG(SISPART2,0x00,backupP2_00);
+    outSISIDXREG(SISPART4,0x0d,backupP4_0d);
+    outSISIDXREG(SISSR,0x1e,backupSR_1e);
+
+    if(ivideo->vbflags2 & VB2_30xCLV) {
+       inSISIDXREG(SISPART2,0x00,biosflag);
+       if(biosflag & 0x20) {
+          for(myflag = 2; myflag > 0; myflag--) {
+            biosflag ^= 0x20;
+            outSISIDXREG(SISPART2,0x00,biosflag);
+         }
+       }
+    }
 
-                       if (sr17 & 0x02) orSISIDXREG(SISCR, 0x32, SIS_VB_LCD);
-                       else             andSISIDXREG(SISCR, 0x32, ~SIS_VB_LCD);
+    outSISIDXREG(SISPART2,0x00,backupP2_00);
+}
 
-                       /* no HiVision and no DVI connector here */
-                       andSISIDXREG(SISCR, 0x32, ~0xc0);
+/* Determine and detect attached TV's on Chrontel */
+static void __devinit
+SiS_SenseCh(struct sis_video_info *ivideo)
+{
+#if defined(CONFIG_FB_SIS_300) || defined(CONFIG_FB_SIS_315)
+    u8 temp1, temp2;
+    char stdstr[] = "sisfb: Chrontel: Detected TV connected to";
+#endif
+#ifdef CONFIG_FB_SIS_300
+    unsigned char test[3];
+    int i;
 #endif
 
-                       /* PAL/NTSC is stored on SR16 on such machines */
-                       if (!(ivideo.vbflags & (TV_PAL | TV_NTSC))) {
-                               inSISIDXREG(SISSR, IND_SIS_SCRATCH_REG_16, sr16);
-                               if (sr16 & 0x20)
-                                       ivideo.vbflags |= TV_PAL;
-                               else
-                                       ivideo.vbflags |= TV_NTSC;
-                       }
-
-               }
-
-       }
-
-       inSISIDXREG(SISCR, IND_SIS_SCRATCH_REG_CR32, cr32);
-
-       if (cr32 & SIS_CRT1)
-               sisfb_crt1off = 0;
-       else {
-               if (cr32 & 0x5F)
-                       sisfb_crt1off = 1;
-               else
-                       sisfb_crt1off = 0;
-       }
-
-       ivideo.vbflags &= ~(CRT2_TV | CRT2_LCD | CRT2_VGA);
-
-       if (cr32 & SIS_VB_TV)
-               ivideo.vbflags |= CRT2_TV;
-       if (cr32 & SIS_VB_LCD)
-               ivideo.vbflags |= CRT2_LCD;
-       if (cr32 & SIS_VB_CRT2)
-               ivideo.vbflags |= CRT2_VGA;
-
-       /* TW: Detect/set TV plug & type */
-       if(sisfb_tvplug != -1)
-               ivideo.vbflags |= sisfb_tvplug;
-
-       if (cr32 & SIS_VB_SVIDEO)
-               ivideo.vbflags |= TV_SVIDEO;
-       else if (cr32 & SIS_VB_COMPOSITE)
-               ivideo.vbflags |= TV_AVIDEO;
-       else if (cr32 & SIS_VB_SCART)
-               ivideo.vbflags |= TV_SCART;
-
-       if (!(ivideo.vbflags & (TV_PAL | TV_NTSC))) {
-               if(sisvga_engine == SIS_300_VGA) {
-                       inSISIDXREG(SISSR, IND_SIS_POWER_ON_TRAP, temp);
-                       if (temp & 0x01)
-                               ivideo.vbflags |= TV_PAL;
-                       else
-                               ivideo.vbflags |= TV_NTSC;
-               } else if((ivideo.chip <= SIS_315PRO) || (ivideo.chip >= SIS_330)) {
-
-                       inSISIDXREG(SISSR, 0x38, temp);
-                       if(temp & 0x01)
-                               ivideo.vbflags |= TV_PAL;
-                       else
-                               ivideo.vbflags |= TV_NTSC;
+    if(ivideo->chip < SIS_315H) {
 
-               } else {
+#ifdef CONFIG_FB_SIS_300
+       ivideo->SiS_Pr.SiS_IF_DEF_CH70xx = 1;           /* Chrontel 700x */
+       SiS_SetChrontelGPIO(&ivideo->SiS_Pr, 0x9c);     /* Set general purpose IO for Chrontel communication */
+       SiS_DDC2Delay(&ivideo->SiS_Pr, 1000);
+       temp1 = SiS_GetCH700x(&ivideo->SiS_Pr, 0x25);
+       /* See Chrontel TB31 for explanation */
+       temp2 = SiS_GetCH700x(&ivideo->SiS_Pr, 0x0e);
+       if(((temp2 & 0x07) == 0x01) || (temp2 & 0x04)) {
+         SiS_SetCH700x(&ivideo->SiS_Pr, 0x0e, 0x0b);
+         SiS_DDC2Delay(&ivideo->SiS_Pr, 300);
+       }
+       temp2 = SiS_GetCH700x(&ivideo->SiS_Pr, 0x25);
+       if(temp2 != temp1) temp1 = temp2;
 
-                       inSISIDXREG(SISCR, 0x79, temp);
-                       if(temp & 0x20)
-                               ivideo.vbflags |= TV_PAL;
-                       else
-                               ivideo.vbflags |= TV_NTSC;
-               }
-       }
+       if((temp1 >= 0x22) && (temp1 <= 0x50)) {
+          /* Read power status */
+          temp1 = SiS_GetCH700x(&ivideo->SiS_Pr, 0x0e);
+          if((temp1 & 0x03) != 0x03) {
+               /* Power all outputs */
+               SiS_SetCH700x(&ivideo->SiS_Pr, 0x0e,0x0b);
+               SiS_DDC2Delay(&ivideo->SiS_Pr, 300);
+          }
+          /* Sense connected TV devices */
+          for(i = 0; i < 3; i++) {
+              SiS_SetCH700x(&ivideo->SiS_Pr, 0x10, 0x01);
+              SiS_DDC2Delay(&ivideo->SiS_Pr, 0x96);
+              SiS_SetCH700x(&ivideo->SiS_Pr, 0x10, 0x00);
+              SiS_DDC2Delay(&ivideo->SiS_Pr, 0x96);
+              temp1 = SiS_GetCH700x(&ivideo->SiS_Pr, 0x10);
+              if(!(temp1 & 0x08))       test[i] = 0x02;
+              else if(!(temp1 & 0x02))  test[i] = 0x01;
+              else                      test[i] = 0;
+              SiS_DDC2Delay(&ivideo->SiS_Pr, 0x96);
+          }
 
-       /* TW: Copy forceCRT1 option to CRT1off if option is given */
-       if (sisfb_forcecrt1 != -1) {
-               if(sisfb_forcecrt1) sisfb_crt1off = 0;
-               else                sisfb_crt1off = 1;
-       }
+          if(test[0] == test[1])      temp1 = test[0];
+          else if(test[0] == test[2]) temp1 = test[0];
+          else if(test[1] == test[2]) temp1 = test[1];
+          else {
+               printk(KERN_INFO
+                       "sisfb: TV detection unreliable - test results varied\n");
+               temp1 = test[2];
+          }
+          if(temp1 == 0x02) {
+               printk(KERN_INFO "%s SVIDEO output\n", stdstr);
+               ivideo->vbflags |= TV_SVIDEO;
+               orSISIDXREG(SISCR, 0x32, 0x02);
+               andSISIDXREG(SISCR, 0x32, ~0x05);
+          } else if (temp1 == 0x01) {
+               printk(KERN_INFO "%s CVBS output\n", stdstr);
+               ivideo->vbflags |= TV_AVIDEO;
+               orSISIDXREG(SISCR, 0x32, 0x01);
+               andSISIDXREG(SISCR, 0x32, ~0x06);
+          } else {
+               SiS_SetCH70xxANDOR(&ivideo->SiS_Pr, 0x0e, 0x01, 0xF8);
+               andSISIDXREG(SISCR, 0x32, ~0x07);
+          }
+       } else if(temp1 == 0) {
+         SiS_SetCH70xxANDOR(&ivideo->SiS_Pr, 0x0e, 0x01, 0xF8);
+         andSISIDXREG(SISCR, 0x32, ~0x07);
+       }
+       /* Set general purpose IO for Chrontel communication */
+       SiS_SetChrontelGPIO(&ivideo->SiS_Pr, 0x00);
+#endif
 
+    } else {
+
+#ifdef CONFIG_FB_SIS_315
+       ivideo->SiS_Pr.SiS_IF_DEF_CH70xx = 2;           /* Chrontel 7019 */
+       temp1 = SiS_GetCH701x(&ivideo->SiS_Pr, 0x49);
+       SiS_SetCH701x(&ivideo->SiS_Pr, 0x49, 0x20);
+       SiS_DDC2Delay(&ivideo->SiS_Pr, 0x96);
+       temp2 = SiS_GetCH701x(&ivideo->SiS_Pr, 0x20);
+       temp2 |= 0x01;
+       SiS_SetCH701x(&ivideo->SiS_Pr, 0x20, temp2);
+       SiS_DDC2Delay(&ivideo->SiS_Pr, 0x96);
+       temp2 ^= 0x01;
+       SiS_SetCH701x(&ivideo->SiS_Pr, 0x20, temp2);
+       SiS_DDC2Delay(&ivideo->SiS_Pr, 0x96);
+       temp2 = SiS_GetCH701x(&ivideo->SiS_Pr, 0x20);
+       SiS_SetCH701x(&ivideo->SiS_Pr, 0x49, temp1);
+       temp1 = 0;
+       if(temp2 & 0x02) temp1 |= 0x01;
+       if(temp2 & 0x10) temp1 |= 0x01;
+       if(temp2 & 0x04) temp1 |= 0x02;
+       if( (temp1 & 0x01) && (temp1 & 0x02) ) temp1 = 0x04;
+       switch(temp1) {
+       case 0x01:
+            printk(KERN_INFO "%s CVBS output\n", stdstr);
+            ivideo->vbflags |= TV_AVIDEO;
+            orSISIDXREG(SISCR, 0x32, 0x01);
+            andSISIDXREG(SISCR, 0x32, ~0x06);
+            break;
+       case 0x02:
+            printk(KERN_INFO "%s SVIDEO output\n", stdstr);
+            ivideo->vbflags |= TV_SVIDEO;
+            orSISIDXREG(SISCR, 0x32, 0x02);
+            andSISIDXREG(SISCR, 0x32, ~0x05);
+            break;
+       case 0x04:
+            printk(KERN_INFO "%s SCART output\n", stdstr);
+            orSISIDXREG(SISCR, 0x32, 0x04);
+            andSISIDXREG(SISCR, 0x32, ~0x03);
+            break;
+       default:
+            andSISIDXREG(SISCR, 0x32, ~0x07);
+       }
+#endif
+    }
 }
 
-static void sisfb_get_VB_type(void)
+static void __devinit
+sisfb_get_VB_type(struct sis_video_info *ivideo)
 {
-       u8 vb_chipid;
-       u8 reg;
        char stdstr[]    = "sisfb: Detected";
        char bridgestr[] = "video bridge";
-       char lvdsstr[]   = "LVDS transmitter";
-       char chrstr[]    = "Chrontel TV encoder";
+       u8 vb_chipid;
+       u8 reg;
 
-       ivideo.hasVB = HASVB_NONE;
-       sishw_ext.ujVBChipID = VB_CHIP_UNKNOWN;
-       sishw_ext.Is301BDH = FALSE;
-       sishw_ext.usExternalChip = 0;
+       /* No CRT2 on XGI Z7 */
+       if(ivideo->chip == XGI_20)
+               return;
 
        inSISIDXREG(SISPART4, 0x00, vb_chipid);
-       switch (vb_chipid) {
-          case 0x01:
-               ivideo.hasVB = HASVB_301;
+       switch(vb_chipid) {
+       case 0x01:
                inSISIDXREG(SISPART4, 0x01, reg);
                if(reg < 0xb0) {
-                       ivideo.vbflags |= VB_301;
-                       sishw_ext.ujVBChipID = VB_CHIP_301;
+                       ivideo->vbflags |= VB_301;      /* Deprecated */
+                       ivideo->vbflags2 |= VB2_301;
                        printk(KERN_INFO "%s SiS301 %s\n", stdstr, bridgestr);
                } else if(reg < 0xc0) {
-                       ivideo.vbflags |= VB_301B;
-                       sishw_ext.ujVBChipID = VB_CHIP_301B;
+                       ivideo->vbflags |= VB_301B;     /* Deprecated */
+                       ivideo->vbflags2 |= VB2_301B;
                        inSISIDXREG(SISPART4,0x23,reg);
                        if(!(reg & 0x02)) {
-                          sishw_ext.Is301BDH = TRUE;
-                          ivideo.vbflags |= VB_30xBDH;
+                          ivideo->vbflags |= VB_30xBDH;        /* Deprecated */
+                          ivideo->vbflags2 |= VB2_30xBDH;
                           printk(KERN_INFO "%s SiS301B-DH %s\n", stdstr, bridgestr);
                        } else {
                           printk(KERN_INFO "%s SiS301B %s\n", stdstr, bridgestr);
                        }
                } else if(reg < 0xd0) {
-                       ivideo.vbflags |= VB_301C;
-                       sishw_ext.ujVBChipID = VB_CHIP_301C;
+                       ivideo->vbflags |= VB_301C;     /* Deprecated */
+                       ivideo->vbflags2 |= VB2_301C;
                        printk(KERN_INFO "%s SiS301C %s\n", stdstr, bridgestr);
                } else if(reg < 0xe0) {
-                       ivideo.vbflags |= VB_301LV;
-                       sishw_ext.ujVBChipID = VB_CHIP_301LV;
+                       ivideo->vbflags |= VB_301LV;    /* Deprecated */
+                       ivideo->vbflags2 |= VB2_301LV;
                        printk(KERN_INFO "%s SiS301LV %s\n", stdstr, bridgestr);
                } else if(reg <= 0xe1) {
-                       inSISIDXREG(SISPART4,0x39,reg);
+                       inSISIDXREG(SISPART4,0x39,reg);
                        if(reg == 0xff) {
-                          ivideo.vbflags |= VB_302LV;
-                          sishw_ext.ujVBChipID = VB_CHIP_302LV;
+                          ivideo->vbflags |= VB_302LV; /* Deprecated */
+                          ivideo->vbflags2 |= VB2_302LV;
                           printk(KERN_INFO "%s SiS302LV %s\n", stdstr, bridgestr);
                        } else {
-                          ivideo.vbflags |= VB_302ELV;
-                          sishw_ext.ujVBChipID = VB_CHIP_302ELV;
+                          ivideo->vbflags |= VB_301C;  /* Deprecated */
+                          ivideo->vbflags2 |= VB2_301C;
+                          printk(KERN_INFO "%s SiS301C(P4) %s\n", stdstr, bridgestr);
+#if 0
+                          ivideo->vbflags |= VB_302ELV;        /* Deprecated */
+                          ivideo->vbflags2 |= VB2_302ELV;
                           printk(KERN_INFO "%s SiS302ELV %s\n", stdstr, bridgestr);
+#endif
                        }
                }
                break;
-          case 0x02:
-               ivideo.hasVB = HASVB_302;
-               inSISIDXREG(SISPART4, 0x01, reg);
-               if(reg < 0xd0) {
-                       ivideo.vbflags |= VB_302B;
-                       sishw_ext.ujVBChipID = VB_CHIP_302B;
-                       inSISIDXREG(SISPART4,0x23,reg);
-                       if(!(reg & 0x02)) {
-                          sishw_ext.Is301BDH = TRUE;
-                          ivideo.vbflags |= VB_30xBDH;
-                          printk(KERN_INFO "%s SiS302B-DH %s\n", stdstr, bridgestr);
-                       } else {
-                          printk(KERN_INFO "%s SiS302B %s\n", stdstr, bridgestr);
-                       }
-               } else if(reg < 0xe0) {
-                       ivideo.vbflags |= VB_301LV;
-                       sishw_ext.ujVBChipID = VB_CHIP_301LV;
-                       printk(KERN_INFO "%s SiS301LV %s\n", stdstr, bridgestr);
-               } else if(reg <= 0xe1) {
-                       ivideo.vbflags |= VB_302LV;
-                       sishw_ext.ujVBChipID = VB_CHIP_302LV;
-                       printk(KERN_INFO "%s SiS302LV %s\n", stdstr, bridgestr);
-               }
+       case 0x02:
+               ivideo->vbflags |= VB_302B;     /* Deprecated */
+               ivideo->vbflags2 |= VB2_302B;
+               printk(KERN_INFO "%s SiS302B %s\n", stdstr, bridgestr);
                break;
        }
 
-       if((!(ivideo.vbflags & VB_VIDEOBRIDGE)) && (ivideo.chip != SIS_300)) {
-               inSISIDXREG(SISCR, IND_SIS_SCRATCH_REG_CR37, reg);
+       if((!(ivideo->vbflags2 & VB2_VIDEOBRIDGE)) && (ivideo->chip != SIS_300)) {
+               inSISIDXREG(SISCR, 0x37, reg);
                reg &= SIS_EXTERNAL_CHIP_MASK;
                reg >>= 1;
-               if(sisvga_engine == SIS_300_VGA) {
-                       switch (reg) {
+               if(ivideo->sisvga_engine == SIS_300_VGA) {
+#ifdef CONFIG_FB_SIS_300
+                       switch(reg) {
                           case SIS_EXTERNAL_CHIP_LVDS:
-                               ivideo.hasVB = HASVB_LVDS;
-                               ivideo.vbflags |= VB_LVDS;
-                               sishw_ext.usExternalChip = 0x01;
-                               printk(KERN_INFO "%s %s\n", stdstr, lvdsstr);
+                               ivideo->vbflags |= VB_LVDS;     /* Deprecated */
+                               ivideo->vbflags2 |= VB2_LVDS;
                                break;
                           case SIS_EXTERNAL_CHIP_TRUMPION:
-                               ivideo.hasVB = HASVB_TRUMPION;
-                               sishw_ext.usExternalChip = 0x02;
-                               printk(KERN_INFO "%s Trumpion LCD scaler\n", stdstr);
+                               ivideo->vbflags |= (VB_LVDS | VB_TRUMPION);     /* Deprecated */
+                               ivideo->vbflags2 |= (VB2_LVDS | VB2_TRUMPION);
                                break;
                           case SIS_EXTERNAL_CHIP_CHRONTEL:
-                               ivideo.hasVB = HASVB_CHRONTEL;
-                               ivideo.vbflags |= VB_CHRONTEL;
-                               sishw_ext.usExternalChip = 0x04;
-                               printk(KERN_INFO "%s %s\n", stdstr, chrstr);
+                               ivideo->vbflags |= VB_CHRONTEL; /* Deprecated */
+                               ivideo->vbflags2 |= VB2_CHRONTEL;
                                break;
                           case SIS_EXTERNAL_CHIP_LVDS_CHRONTEL:
-                               ivideo.hasVB = HASVB_LVDS_CHRONTEL;
-                               ivideo.vbflags |= (VB_LVDS | VB_CHRONTEL);
-                               sishw_ext.usExternalChip = 0x05;
-                               printk(KERN_INFO "%s %s and %s\n", stdstr, lvdsstr, chrstr);
+                               ivideo->vbflags |= (VB_LVDS | VB_CHRONTEL);     /* Deprecated */
+                               ivideo->vbflags2 |= (VB2_LVDS | VB2_CHRONTEL);
                                break;
                        }
-               } else if(ivideo.chip < SIS_661) {
+                       if(ivideo->vbflags2 & VB2_CHRONTEL) ivideo->chronteltype = 1;
+#endif
+               } else if(ivideo->chip < SIS_661) {
+#ifdef CONFIG_FB_SIS_315
                        switch (reg) {
-                          case SIS310_EXTERNAL_CHIP_LVDS:
-                               ivideo.hasVB = HASVB_LVDS;
-                               ivideo.vbflags |= VB_LVDS;
-                               sishw_ext.usExternalChip = 0x01;
-                               printk(KERN_INFO "%s %s\n", stdstr, lvdsstr);
+                          case SIS310_EXTERNAL_CHIP_LVDS:
+                               ivideo->vbflags |= VB_LVDS;     /* Deprecated */
+                               ivideo->vbflags2 |= VB2_LVDS;
+                               break;
+                          case SIS310_EXTERNAL_CHIP_LVDS_CHRONTEL:
+                               ivideo->vbflags |= (VB_LVDS | VB_CHRONTEL);     /* Deprecated */
+                               ivideo->vbflags2 |= (VB2_LVDS | VB2_CHRONTEL);
                                break;
-                          case SIS310_EXTERNAL_CHIP_LVDS_CHRONTEL:
-                               ivideo.hasVB = HASVB_LVDS_CHRONTEL;
-                               ivideo.vbflags |= (VB_LVDS | VB_CHRONTEL);
-                               sishw_ext.usExternalChip = 0x05;
-                               printk(KERN_INFO "%s %s and %s\n", stdstr, lvdsstr, chrstr);
+                       }
+                       if(ivideo->vbflags2 & VB2_CHRONTEL) ivideo->chronteltype = 2;
+#endif
+               } else if(ivideo->chip >= SIS_661) {
+#ifdef CONFIG_FB_SIS_315
+                       inSISIDXREG(SISCR, 0x38, reg);
+                       reg >>= 5;
+                       switch(reg) {
+                          case 0x02:
+                               ivideo->vbflags |= VB_LVDS;     /* Deprecated */
+                               ivideo->vbflags2 |= VB2_LVDS;
+                               break;
+                          case 0x03:
+                               ivideo->vbflags |= (VB_LVDS | VB_CHRONTEL);     /* Deprecated */
+                               ivideo->vbflags2 |= (VB2_LVDS | VB2_CHRONTEL);
+                               break;
+                          case 0x04:
+                               ivideo->vbflags |= (VB_LVDS | VB_CONEXANT);     /* Deprecated */
+                               ivideo->vbflags2 |= (VB2_LVDS | VB2_CONEXANT);
                                break;
                        }
+                       if(ivideo->vbflags2 & VB2_CHRONTEL) ivideo->chronteltype = 2;
+#endif
+               }
+               if(ivideo->vbflags2 & VB2_LVDS) {
+                  printk(KERN_INFO "%s LVDS transmitter\n", stdstr);
+               }
+               if((ivideo->sisvga_engine == SIS_300_VGA) && (ivideo->vbflags2 & VB2_TRUMPION)) {
+                  printk(KERN_INFO "%s Trumpion Zurac LCD scaler\n", stdstr);
+               }
+               if(ivideo->vbflags2 & VB2_CHRONTEL) {
+                  printk(KERN_INFO "%s Chrontel TV encoder\n", stdstr);
+               }
+               if((ivideo->chip >= SIS_661) && (ivideo->vbflags2 & VB2_CONEXANT)) {
+                  printk(KERN_INFO "%s Conexant external device\n", stdstr);
                }
-
        }
 
-       if(ivideo.vbflags & VB_SISBRIDGE) {
-               SiS_Sense30x();
-       } else if(ivideo.vbflags & VB_CHRONTEL) {
-               SiS_SenseCh();
+       if(ivideo->vbflags2 & VB2_SISBRIDGE) {
+               SiS_SenseLCD(ivideo);
+               SiS_Sense30x(ivideo);
+       } else if(ivideo->vbflags2 & VB2_CHRONTEL) {
+               SiS_SenseCh(ivideo);
        }
-
 }
 
-/* ------------------ Sensing routines ------------------ */
-
-static BOOLEAN
-sisfb_test_DDC1(void)
-{
-    unsigned short old;
-    int count = 48;
-
-    old = SiS_ReadDDC1Bit(&SiS_Pr);
-    do {
-       if(old != SiS_ReadDDC1Bit(&SiS_Pr)) break;
-    } while(count--);
-    return (count == -1) ? FALSE : TRUE;
-}
+/* ---------- Engine initialization routines ------------ */
 
 static void
-sisfb_sense_crt1(void)
+sisfb_engine_init(struct sis_video_info *ivideo)
 {
-    unsigned char SR1F, CR63=0, CR17;
-    unsigned short temp = 0xffff;
-    int i;
-    BOOLEAN mustwait = FALSE;
-
-    inSISIDXREG(SISSR,0x1F,SR1F);
-    orSISIDXREG(SISSR,0x1F,0x04);
-    andSISIDXREG(SISSR,0x1F,0x3F);
-    if(SR1F & 0xc0) mustwait = TRUE;
 
-    if(sisvga_engine == SIS_315_VGA) {
-       inSISIDXREG(SISCR,SiS_Pr.SiS_MyCR63,CR63);
-       CR63 &= 0x40;
-       andSISIDXREG(SISCR,SiS_Pr.SiS_MyCR63,0xBF);
-    }
-
-    inSISIDXREG(SISCR,0x17,CR17);
-    CR17 &= 0x80;
-    if(!CR17) {
-       orSISIDXREG(SISCR,0x17,0x80);
-       mustwait = TRUE;
-       outSISIDXREG(SISSR, 0x00, 0x01);
-       outSISIDXREG(SISSR, 0x00, 0x03);
-    }
+       /* Initialize command queue (we use MMIO only) */
 
-    if(mustwait) {
-       for(i=0; i < 10; i++) sisfbwaitretracecrt1();
-    }
+       /* BEFORE THIS IS CALLED, THE ENGINES *MUST* BE SYNC'ED */
 
-    i = 3;
-    do {
-       temp = SiS_HandleDDC(&SiS_Pr, ivideo.vbflags, sisvga_engine, 0, 0, NULL);
-    } while(((temp == 0) || (temp == 0xffff)) && i--);
+       ivideo->caps &= ~(TURBO_QUEUE_CAP    |
+                         MMIO_CMD_QUEUE_CAP |
+                         VM_CMD_QUEUE_CAP   |
+                         AGP_CMD_QUEUE_CAP);
 
-    if((temp == 0) || (temp == 0xffff)) {
-       if(sisfb_test_DDC1()) temp = 1;
-    }
+#ifdef CONFIG_FB_SIS_300
+       if(ivideo->sisvga_engine == SIS_300_VGA) {
+               u32 tqueue_pos;
+               u8 tq_state;
 
-    if((temp) && (temp != 0xffff)) {
-       orSISIDXREG(SISCR,0x32,0x20);
-    }
+               tqueue_pos = (ivideo->video_size - ivideo->cmdQueueSize) / (64 * 1024);
 
-    if(sisvga_engine == SIS_315_VGA) {
-       setSISIDXREG(SISCR,SiS_Pr.SiS_MyCR63,0xBF,CR63);
-    }
+               inSISIDXREG(SISSR, IND_SIS_TURBOQUEUE_SET, tq_state);
+               tq_state |= 0xf0;
+               tq_state &= 0xfc;
+               tq_state |= (u8)(tqueue_pos >> 8);
+               outSISIDXREG(SISSR, IND_SIS_TURBOQUEUE_SET, tq_state);
 
-    setSISIDXREG(SISCR,0x17,0x7F,CR17);
+               outSISIDXREG(SISSR, IND_SIS_TURBOQUEUE_ADR, (u8)(tqueue_pos & 0xff));
 
-    outSISIDXREG(SISSR,0x1F,SR1F);
-}
+               ivideo->caps |= TURBO_QUEUE_CAP;
+       }
+#endif
 
-/* Determine and detect attached devices on SiS30x */
-static int
-SISDoSense(int tempbl, int tempbh, int tempcl, int tempch)
-{
-    int temp;
-
-    outSISIDXREG(SISPART4,0x11,tempbl);
-    temp = tempbh | tempcl;
-    setSISIDXREG(SISPART4,0x10,0xe0,temp);
-    SiS_DDC2Delay(&SiS_Pr, 0x1000);
-    tempch &= 0x7f;
-    inSISIDXREG(SISPART4,0x03,temp);
-    temp ^= 0x0e;
-    temp &= tempch;
-    return((temp == tempch));
-}
+#ifdef CONFIG_FB_SIS_315
+       if(ivideo->sisvga_engine == SIS_315_VGA) {
+               u32 tempq = 0, templ;
+               u8  temp;
+
+               if(ivideo->chip == XGI_20) {
+                       switch(ivideo->cmdQueueSize) {
+                       case (64 * 1024):
+                               temp = SIS_CMD_QUEUE_SIZE_Z7_64k;
+                               break;
+                       case (128 * 1024):
+                       default:
+                               temp = SIS_CMD_QUEUE_SIZE_Z7_128k;
+                       }
+               } else {
+                       switch(ivideo->cmdQueueSize) {
+                       case (4 * 1024 * 1024):
+                               temp = SIS_CMD_QUEUE_SIZE_4M;
+                               break;
+                       case (2 * 1024 * 1024):
+                               temp = SIS_CMD_QUEUE_SIZE_2M;
+                               break;
+                       case (1 * 1024 * 1024):
+                               temp = SIS_CMD_QUEUE_SIZE_1M;
+                               break;
+                       default:
+                       case (512 * 1024):
+                               temp = SIS_CMD_QUEUE_SIZE_512k;
+                       }
+               }
 
-static void
-SiS_Sense30x(void)
-{
-  u8 backupP4_0d,backupP2_00;
-  u8 svhs_bl, svhs_bh;
-  u8 svhs_cl, svhs_ch;
-  u8 cvbs_bl, cvbs_bh;
-  u8 cvbs_cl, cvbs_ch;
-  u8 vga2_bl, vga2_bh;
-  u8 vga2_cl, vga2_ch;
-  int myflag, result, haveresult, i, j;
-  char stdstr[] = "sisfb: Detected";
-  char tvstr[]  = "TV connected to";
-
-  inSISIDXREG(SISPART4,0x0d,backupP4_0d);
-  if(!(ivideo.vbflags & (VB_301C|VB_302ELV))) {
-     outSISIDXREG(SISPART4,0x0d,(backupP4_0d | 0x04));
-  }
-
-  inSISIDXREG(SISPART2,0x00,backupP2_00);
-  outSISIDXREG(SISPART2,0x00,(backupP2_00 | 0x1c));
-
-  if(sisvga_engine == SIS_300_VGA) {
-
-       if(ivideo.vbflags & (VB_301B|VB_301C|VB_302B|VB_301LV|VB_302LV)) {
-               vga2_bh = 0x01; vga2_bl = 0x90;
-               svhs_bh = 0x01; svhs_bl = 0x6b;
-               cvbs_bh = 0x01; cvbs_bl = 0x74;
-       } else {
-               vga2_bh = 0x00; vga2_bl = 0xd1;
-               svhs_bh = 0x00; svhs_bl = 0xb9;
-               cvbs_bh = 0x00; cvbs_bl = 0xb3;
-       }
-       inSISIDXREG(SISPART4,0x01,myflag);
-       if(myflag & 0x04) {
-          vga2_bh = 0x00; vga2_bl = 0xfd;
-          svhs_bh = 0x00; svhs_bl = 0xdd;
-          cvbs_bh = 0x00; cvbs_bl = 0xee;
-       }
-       vga2_ch = 0x0e; vga2_cl = 0x08;
-       svhs_ch = 0x04; svhs_cl = 0x04;
-       cvbs_ch = 0x08; cvbs_cl = 0x04;
-       if(ivideo.vbflags & (VB_301LV|VB_302LV)) {
-               vga2_bh = 0x00; vga2_bl = 0x00;
-               vga2_ch = 0x00; vga2_cl = 0x00;
-        }
-       if(ivideo.chip == SIS_300) {
-          inSISIDXREG(SISSR,0x3b,myflag);
-          if(!(myflag & 0x01)) {
-               vga2_bh = 0x00; vga2_bl = 0x00;
-               vga2_ch = 0x00; vga2_cl = 0x00;
-          }
-       }
+               outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_THRESHOLD, COMMAND_QUEUE_THRESHOLD);
+               outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, SIS_CMD_QUEUE_RESET);
 
-  } else {
-
-       if(ivideo.vbflags & (VB_301B|VB_302B)) {
-               vga2_bh = 0x01; vga2_bl = 0x90;
-               svhs_bh = 0x01; svhs_bl = 0x6b;
-               cvbs_bh = 0x01; cvbs_bl = 0x74;
-       } else if(ivideo.vbflags & (VB_301C|VB_302ELV)) {
-               vga2_bh = 0x01; vga2_bl = 0x90;
-               svhs_bh = 0x01; svhs_bl = 0x6b;
-               cvbs_bh = 0x01; cvbs_bl = 0x10;
-       } else if(ivideo.vbflags & (VB_301LV|VB_302LV)) {
-               vga2_bh = 0x00; vga2_bl = 0x00;
-               svhs_bh = 0x02; svhs_bl = 0x00;
-               cvbs_bh = 0x01; cvbs_bl = 0x00;
-       } else {
-               vga2_bh = 0x00; vga2_bl = 0xd1;
-               svhs_bh = 0x00; svhs_bl = 0xb9;
-               cvbs_bh = 0x00; cvbs_bl = 0xb3;
-               inSISIDXREG(SISPART4,0x01,myflag);
-               if(myflag & 0x04) {
-                  vga2_bh = 0x00; vga2_bl = 0xfd;
-                  svhs_bh = 0x00; svhs_bl = 0xdd;
-                  cvbs_bh = 0x00; cvbs_bl = 0xee;
-               }
-       }
-
-       if(ivideo.vbflags & (VB_301LV|VB_302LV|VB_302ELV)) {
-          vga2_bh = 0x00; vga2_bl = 0x00;
-          vga2_ch = 0x00; vga2_cl = 0x00;
-          svhs_ch = 0x04; svhs_cl = 0x08;
-          cvbs_ch = 0x08; cvbs_cl = 0x08;
-       } else {
-          vga2_ch = 0x0e; vga2_cl = 0x08;
-          svhs_ch = 0x04; svhs_cl = 0x04;
-          cvbs_ch = 0x08; cvbs_cl = 0x04;
-       }
-    } 
-
-    if(vga2_ch || vga2_cl || vga2_bh || vga2_bl) {
-       haveresult = 0;
-       for(j = 0; j < 10; j++) {
-          result = 0;
-          for(i = 0; i < 3; i++) {
-             if(SISDoSense(vga2_bl, vga2_bh, vga2_cl, vga2_ch))
-               result++;
-          }
-         if((result == 0) || (result >= 2)) break;
-       }
-       if(result) {
-          printk(KERN_INFO "%s secondary VGA connection\n", stdstr);
-         orSISIDXREG(SISCR, 0x32, 0x10);
-       } else {
-         andSISIDXREG(SISCR, 0x32, ~0x10);
-       }
-    }
+               if((ivideo->chip >= XGI_40) && ivideo->modechanged) {
+                       /* Must disable dual pipe on XGI_40. Can't do
+                        * this in MMIO mode, because it requires
+                        * setting/clearing a bit in the MMIO fire trigger
+                        * register.
+                        */
+                       if(!((templ = MMIO_IN32(ivideo->mmio_vbase, 0x8240)) & (1 << 10))) {
 
-    if(ivideo.vbflags & (VB_301C|VB_302ELV)) {
-       orSISIDXREG(SISPART4,0x0d,0x04);
-    }
+                               MMIO_OUT32(ivideo->mmio_vbase, Q_WRITE_PTR, 0);
 
-    haveresult = 0;
-    for(j = 0; j < 10; j++) {
-       result = 0;
-       for(i = 0; i < 3; i++) {
-          if(SISDoSense(svhs_bl, svhs_bh, svhs_cl, svhs_ch))
-               result++;
-       }
-       if((result == 0) || (result >= 2)) break;
-    }
-    if(result) {
-        printk(KERN_INFO "%s %s SVIDEO output\n", stdstr, tvstr);
-       ivideo.vbflags |= TV_SVIDEO;
-       orSISIDXREG(SISCR, 0x32, 0x02);
-       andSISIDXREG(SISCR, 0x32, ~0x05);
-    }
+                               outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, (temp | SIS_VRAM_CMDQUEUE_ENABLE));
 
-    if(!result) {
+                               tempq = MMIO_IN32(ivideo->mmio_vbase, Q_READ_PTR);
+                               MMIO_OUT32(ivideo->mmio_vbase, Q_WRITE_PTR, tempq);
 
-       haveresult = 0;
-               for(j = 0; j < 10; j++) {
-           result = 0;
-           for(i = 0; i < 3; i++) {
-              if(SISDoSense(cvbs_bl, cvbs_bh, cvbs_cl, cvbs_ch))
-               result++;
-           }
-           if((result == 0) || (result >= 2)) break;
-        }
-       if(result) {
-           printk(KERN_INFO "%s %s COMPOSITE output\n", stdstr, tvstr);
-           ivideo.vbflags |= TV_AVIDEO;
-           orSISIDXREG(SISCR, 0x32, 0x01);
-           andSISIDXREG(SISCR, 0x32, ~0x06);
-       } else {
-           andSISIDXREG(SISCR, 0x32, ~0x07);
-       }
-    }
-    SISDoSense(0, 0, 0, 0);
+                               tempq = (u32)(ivideo->video_size - ivideo->cmdQueueSize);
+                               MMIO_OUT32(ivideo->mmio_vbase, Q_BASE_ADDR, tempq);
 
-    outSISIDXREG(SISPART2,0x00,backupP2_00);
-    outSISIDXREG(SISPART4,0x0d,backupP4_0d);
-}
+                               writel(0x16800000 + 0x8240, ivideo->video_vbase + tempq);
+                               writel(templ | (1 << 10), ivideo->video_vbase + tempq + 4);
+                               writel(0x168F0000, ivideo->video_vbase + tempq + 8);
+                               writel(0x168F0000, ivideo->video_vbase + tempq + 12);
 
-/* Determine and detect attached TV's on Chrontel */
-static void
-SiS_SenseCh(void)
-{
+                               MMIO_OUT32(ivideo->mmio_vbase, Q_WRITE_PTR, (tempq + 16));
 
-   u8 temp1, temp2;
-#ifdef CONFIG_FB_SIS_300
-   unsigned char test[3];
-   int i;
-#endif
-   char stdstr[] = "sisfb: Chrontel: Detected TV connected to";
+                               sisfb_syncaccel(ivideo);
 
-   if(ivideo.chip < SIS_315H) {
+                               outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, SIS_CMD_QUEUE_RESET);
 
-#ifdef CONFIG_FB_SIS_300
-       SiS_Pr.SiS_IF_DEF_CH70xx = 1;           /* Chrontel 700x */
-       SiS_SetChrontelGPIO(&SiS_Pr, 0x9c);     /* Set general purpose IO for Chrontel communication */
-       SiS_DDC2Delay(&SiS_Pr, 1000);
-       temp1 = SiS_GetCH700x(&SiS_Pr, 0x25);
-       /* See Chrontel TB31 for explanation */
-       temp2 = SiS_GetCH700x(&SiS_Pr, 0x0e);
-       if(((temp2 & 0x07) == 0x01) || (temp2 & 0x04)) {
-         SiS_SetCH700x(&SiS_Pr, 0x0b0e);
-         SiS_DDC2Delay(&SiS_Pr, 300);
-       }
-       temp2 = SiS_GetCH700x(&SiS_Pr, 0x25);
-       if(temp2 != temp1) temp1 = temp2;
+                       }
+               }
 
-       if((temp1 >= 0x22) && (temp1 <= 0x50)) {
-          /* Read power status */
-          temp1 = SiS_GetCH700x(&SiS_Pr, 0x0e);
-          if((temp1 & 0x03) != 0x03) {
-               /* Power all outputs */
-               SiS_SetCH700x(&SiS_Pr, 0x0B0E);
-               SiS_DDC2Delay(&SiS_Pr, 300);
-          }
-          /* Sense connected TV devices */
-          for(i = 0; i < 3; i++) {
-              SiS_SetCH700x(&SiS_Pr, 0x0110);
-              SiS_DDC2Delay(&SiS_Pr, 0x96);
-              SiS_SetCH700x(&SiS_Pr, 0x0010);
-              SiS_DDC2Delay(&SiS_Pr, 0x96);
-              temp1 = SiS_GetCH700x(&SiS_Pr, 0x10);
-              if(!(temp1 & 0x08))       test[i] = 0x02;
-              else if(!(temp1 & 0x02))  test[i] = 0x01;
-              else                      test[i] = 0;
-              SiS_DDC2Delay(&SiS_Pr, 0x96);
-          }
+               tempq = MMIO_IN32(ivideo->mmio_vbase, MMIO_QUEUE_READPORT);
+               MMIO_OUT32(ivideo->mmio_vbase, MMIO_QUEUE_WRITEPORT, tempq);
 
-          if(test[0] == test[1])      temp1 = test[0];
-          else if(test[0] == test[2]) temp1 = test[0];
-          else if(test[1] == test[2]) temp1 = test[1];
-          else {
-               printk(KERN_INFO
-                       "sisfb: TV detection unreliable - test results varied\n");
-               temp1 = test[2];
-          }
-          if(temp1 == 0x02) {
-               printk(KERN_INFO "%s SVIDEO output\n", stdstr);
-               ivideo.vbflags |= TV_SVIDEO;
-               orSISIDXREG(SISCR, 0x32, 0x02);
-               andSISIDXREG(SISCR, 0x32, ~0x05);
-          } else if (temp1 == 0x01) {
-               printk(KERN_INFO "%s CVBS output\n", stdstr);
-               ivideo.vbflags |= TV_AVIDEO;
-               orSISIDXREG(SISCR, 0x32, 0x01);
-               andSISIDXREG(SISCR, 0x32, ~0x06);
-          } else {
-               SiS_SetCH70xxANDOR(&SiS_Pr, 0x010E,0xF8);
-               andSISIDXREG(SISCR, 0x32, ~0x07);
-          }
-       } else if(temp1 == 0) {
-         SiS_SetCH70xxANDOR(&SiS_Pr, 0x010E,0xF8);
-         andSISIDXREG(SISCR, 0x32, ~0x07);
-       }
-       /* Set general purpose IO for Chrontel communication */
-       SiS_SetChrontelGPIO(&SiS_Pr, 0x00);
-#endif
+               temp |= (SIS_MMIO_CMD_ENABLE | SIS_CMD_AUTO_CORR);
+               outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, temp);
 
-   } else {
+               tempq = (u32)(ivideo->video_size - ivideo->cmdQueueSize);
+               MMIO_OUT32(ivideo->mmio_vbase, MMIO_QUEUE_PHYBASE, tempq);
 
-#ifdef CONFIG_FB_SIS_315
-       SiS_Pr.SiS_IF_DEF_CH70xx = 2;           /* Chrontel 7019 */
-        temp1 = SiS_GetCH701x(&SiS_Pr, 0x49);
-       SiS_SetCH701x(&SiS_Pr, 0x2049);
-       SiS_DDC2Delay(&SiS_Pr, 0x96);
-       temp2 = SiS_GetCH701x(&SiS_Pr, 0x20);
-       temp2 |= 0x01;
-       SiS_SetCH701x(&SiS_Pr, (temp2 << 8) | 0x20);
-       SiS_DDC2Delay(&SiS_Pr, 0x96);
-       temp2 ^= 0x01;
-       SiS_SetCH701x(&SiS_Pr, (temp2 << 8) | 0x20);
-       SiS_DDC2Delay(&SiS_Pr, 0x96);
-       temp2 = SiS_GetCH701x(&SiS_Pr, 0x20);
-       SiS_SetCH701x(&SiS_Pr, (temp1 << 8) | 0x49);
-        temp1 = 0;
-       if(temp2 & 0x02) temp1 |= 0x01;
-       if(temp2 & 0x10) temp1 |= 0x01;
-       if(temp2 & 0x04) temp1 |= 0x02;
-       if( (temp1 & 0x01) && (temp1 & 0x02) ) temp1 = 0x04;
-       switch(temp1) {
-       case 0x01:
-            printk(KERN_INFO "%s CVBS output\n", stdstr);
-            ivideo.vbflags |= TV_AVIDEO;
-            orSISIDXREG(SISCR, 0x32, 0x01);
-            andSISIDXREG(SISCR, 0x32, ~0x06);
-             break;
-       case 0x02:
-            printk(KERN_INFO "%s SVIDEO output\n", stdstr);
-            ivideo.vbflags |= TV_SVIDEO;
-            orSISIDXREG(SISCR, 0x32, 0x02);
-            andSISIDXREG(SISCR, 0x32, ~0x05);
-             break;
-       case 0x04:
-            printk(KERN_INFO "%s SCART output\n", stdstr);
-            orSISIDXREG(SISCR, 0x32, 0x04);
-            andSISIDXREG(SISCR, 0x32, ~0x03);
-             break;
-       default:
-            andSISIDXREG(SISCR, 0x32, ~0x07);
+               ivideo->caps |= MMIO_CMD_QUEUE_CAP;
        }
 #endif
 
-   }
+       ivideo->engineok = 1;
 }
 
-
-/* ------------------------ Heap routines -------------------------- */
-
-static int sisfb_heap_init(void)
+static void __devinit
+sisfb_detect_lcd_type(struct sis_video_info *ivideo)
 {
-       SIS_OH *poh;
-       u8 temp=0;
-#ifdef CONFIG_FB_SIS_315
-       int            agp_enabled = 1;
-       u32            agp_size;
-       unsigned long *cmdq_baseport = 0;
-       unsigned long *read_port = 0;
-       unsigned long *write_port = 0;
-       SIS_CMDTYPE    cmd_type;
-#ifndef AGPOFF
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
-       struct agp_kern_info  *agp_info;
-       struct agp_memory     *agp;
-#else
-       agp_kern_info  *agp_info;
-       agp_memory     *agp;
-#endif
-       u32            agp_phys;
-#endif
-#endif
-/*     The heap start is either set manually using the "mem" parameter, or
- *     defaults as follows:
- *     -) If more than 16MB videoRAM available, let our heap start at 12MB.
- *     -) If more than  8MB videoRAM available, let our heap start at  8MB.
- *     -) If 4MB or less is available, let it start at 4MB.
- *     This is for avoiding a clash with X driver which uses the beginning
- *     of the videoRAM. To limit size of X framebuffer, use Option MaxXFBMem
- *     in XF86Config-4.
- *     The heap start can also be specified by parameter "mem" when starting the sisfb
- *     driver. sisfb mem=1024 lets heap starts at 1MB, etc.
- *
- *     On the 315 and Xabre series, the default is a 1MB heap since DRI is not
- *     supported there.
- */
-     if ((!sisfb_mem) || (sisfb_mem > (ivideo.video_size/1024))) {
-        if(sisvga_engine == SIS_300_VGA) {
-           if (ivideo.video_size > 0x1000000) {
-               ivideo.heapstart = 0xc00000;
-          } else if (ivideo.video_size > 0x800000) {
-               ivideo.heapstart = 0x800000;
-          } else {
-               ivideo.heapstart = 0x400000;
-          }
+       u8 reg;
+       int i;
+
+       inSISIDXREG(SISCR, 0x36, reg);
+       reg &= 0x0f;
+       if(ivideo->sisvga_engine == SIS_300_VGA) {
+               ivideo->CRT2LCDType = sis300paneltype[reg];
+       } else if(ivideo->chip >= SIS_661) {
+               ivideo->CRT2LCDType = sis661paneltype[reg];
        } else {
-          ivideo.heapstart = ivideo.video_size - 0x100000;
+               ivideo->CRT2LCDType = sis310paneltype[reg];
+               if((ivideo->chip == SIS_550) && (sisfb_fstn)) {
+                       if((ivideo->CRT2LCDType != LCD_320x240_2) &&
+                          (ivideo->CRT2LCDType != LCD_320x240_3)) {
+                               ivideo->CRT2LCDType = LCD_320x240;
+                       }
+               }
        }
-     } else {
-           ivideo.heapstart = sisfb_mem * 1024;
-     }
-     sisfb_heap_start = (unsigned long) (ivideo.video_vbase + ivideo.heapstart);
-     printk(KERN_INFO "sisfb: Memory heap starting at %dK\n",
-                                       (int)(ivideo.heapstart / 1024));
-
-     sisfb_heap_end = (unsigned long) ivideo.video_vbase + ivideo.video_size;
-     sisfb_heap_size = sisfb_heap_end - sisfb_heap_start;
 
-#ifdef CONFIG_FB_SIS_315
-     if (sisvga_engine == SIS_315_VGA) {
-        /* Now initialize the 315/330 series' command queue mode.
-        * On 315, there are three queue modes available which
-        * are chosen by setting bits 7:5 in SR26:
-        * 1. MMIO queue mode (bit 5, 0x20). The hardware will keep
-        *    track of the queue, the FIFO, command parsing and so
-        *    on. This is the one comparable to the 300 series.
-        * 2. VRAM queue mode (bit 6, 0x40). In this case, one will
-        *    have to do queue management himself. Register 0x85c4 will
-        *    hold the location of the next free queue slot, 0x85c8
-        *    is the "queue read pointer" whose way of working is
-        *    unknown to me. Anyway, this mode would require a
-        *    translation of the MMIO commands to some kind of
-        *    accelerator assembly and writing these commands
-        *    to the memory location pointed to by 0x85c4.
-        *    We will not use this, as nobody knows how this
-        *    "assembly" works, and as it would require a complete
-        *    re-write of the accelerator code.
-        * 3. AGP queue mode (bit 7, 0x80). Works as 2., but keeps the
-        *    queue in AGP memory space.
-        *
-        * SR26 bit 4 is called "Bypass H/W queue".
-        * SR26 bit 1 is called "Enable Command Queue Auto Correction"
-        * SR26 bit 0 resets the queue
-        * Size of queue memory is encoded in bits 3:2 like this:
-        *    00  (0x00)  512K
-        *    01  (0x04)  1M
-        *    10  (0x08)  2M
-        *    11  (0x0C)  4M
-        * The queue location is to be written to 0x85C0.
-        *
-         */
-       cmdq_baseport = (unsigned long *)(ivideo.mmio_vbase + MMIO_QUEUE_PHYBASE);
-       write_port    = (unsigned long *)(ivideo.mmio_vbase + MMIO_QUEUE_WRITEPORT);
-       read_port     = (unsigned long *)(ivideo.mmio_vbase + MMIO_QUEUE_READPORT);
+       if(ivideo->CRT2LCDType == LCD_UNKNOWN) {
+               /* For broken BIOSes: Assume 1024x768, RGB18 */
+               ivideo->CRT2LCDType = LCD_1024x768;
+               setSISIDXREG(SISCR,0x36,0xf0,0x02);
+               setSISIDXREG(SISCR,0x37,0xee,0x01);
+               printk(KERN_DEBUG "sisfb: Invalid panel ID (%02x), assuming 1024x768, RGB18\n", reg);
+       }
 
-       DPRINTK("AGP base: 0x%p, read: 0x%p, write: 0x%p\n", cmdq_baseport, read_port, write_port);
+       for(i = 0; i < SIS_LCD_NUMBER; i++) {
+               if(ivideo->CRT2LCDType == sis_lcd_data[i].lcdtype) {
+                       ivideo->lcdxres = sis_lcd_data[i].xres;
+                       ivideo->lcdyres = sis_lcd_data[i].yres;
+                       ivideo->lcddefmodeidx = sis_lcd_data[i].default_mode_idx;
+                       break;
+               }
+       }
 
-       agp_size  = COMMAND_QUEUE_AREA_SIZE;
+#ifdef CONFIG_FB_SIS_300
+       if(ivideo->SiS_Pr.SiS_CustomT == CUT_BARCO1366) {
+               ivideo->lcdxres = 1360; ivideo->lcdyres = 1024;
+               ivideo->lcddefmodeidx = DEFAULT_MODE_1360;
+       } else if(ivideo->SiS_Pr.SiS_CustomT == CUT_PANEL848) {
+               ivideo->lcdxres =  848; ivideo->lcdyres =  480;
+               ivideo->lcddefmodeidx = DEFAULT_MODE_848;
+       } else if(ivideo->SiS_Pr.SiS_CustomT == CUT_PANEL856) {
+               ivideo->lcdxres =  856; ivideo->lcdyres =  480;
+               ivideo->lcddefmodeidx = DEFAULT_MODE_856;
+       }
+#endif
 
-#ifndef AGPOFF
-       if (sisfb_queuemode == AGP_CMD_QUEUE) {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
-               agp_info = vmalloc(sizeof(*agp_info));
-               memset((void*)agp_info, 0x00, sizeof(*agp_info));
-#else
-               agp_info = vmalloc(sizeof(agp_kern_info));
-               memset((void*)agp_info, 0x00, sizeof(agp_kern_info));
+       printk(KERN_DEBUG "sisfb: Detected %dx%d flat panel\n",
+                       ivideo->lcdxres, ivideo->lcdyres);
+}
+
+static void __devinit
+sisfb_save_pdc_emi(struct sis_video_info *ivideo)
+{
+#ifdef CONFIG_FB_SIS_300
+       /* Save the current PanelDelayCompensation if the LCD is currently used */
+       if(ivideo->sisvga_engine == SIS_300_VGA) {
+               if(ivideo->vbflags2 & (VB2_LVDS | VB2_30xBDH)) {
+                       int tmp;
+                       inSISIDXREG(SISCR,0x30,tmp);
+                       if(tmp & 0x20) {
+                               /* Currently on LCD? If yes, read current pdc */
+                               inSISIDXREG(SISPART1,0x13,ivideo->detectedpdc);
+                               ivideo->detectedpdc &= 0x3c;
+                               if(ivideo->SiS_Pr.PDC == -1) {
+                                       /* Let option override detection */
+                                       ivideo->SiS_Pr.PDC = ivideo->detectedpdc;
+                               }
+                               printk(KERN_INFO "sisfb: Detected LCD PDC 0x%02x\n",
+                                       ivideo->detectedpdc);
+                       }
+                       if((ivideo->SiS_Pr.PDC != -1) &&
+                          (ivideo->SiS_Pr.PDC != ivideo->detectedpdc)) {
+                               printk(KERN_INFO "sisfb: Using LCD PDC 0x%02x\n",
+                                       ivideo->SiS_Pr.PDC);
+                       }
+               }
+       }
 #endif
-               agp_copy_info(agp_info);
 
-               agp_backend_acquire();
+#ifdef CONFIG_FB_SIS_315
+       if(ivideo->sisvga_engine == SIS_315_VGA) {
+
+               /* Try to find about LCDA */
+               if(ivideo->vbflags2 & VB2_SISLCDABRIDGE) {
+                       int tmp;
+                       inSISIDXREG(SISPART1,0x13,tmp);
+                       if(tmp & 0x04) {
+                               ivideo->SiS_Pr.SiS_UseLCDA = TRUE;
+                               ivideo->detectedlcda = 0x03;
+                       }
+               }
+
+               /* Save PDC */
+               if(ivideo->vbflags2 & VB2_SISLVDSBRIDGE) {
+                       int tmp;
+                       inSISIDXREG(SISCR,0x30,tmp);
+                       if((tmp & 0x20) || (ivideo->detectedlcda != 0xff)) {
+                               /* Currently on LCD? If yes, read current pdc */
+                               u8 pdc;
+                               inSISIDXREG(SISPART1,0x2D,pdc);
+                               ivideo->detectedpdc  = (pdc & 0x0f) << 1;
+                               ivideo->detectedpdca = (pdc & 0xf0) >> 3;
+                               inSISIDXREG(SISPART1,0x35,pdc);
+                               ivideo->detectedpdc |= ((pdc >> 7) & 0x01);
+                               inSISIDXREG(SISPART1,0x20,pdc);
+                               ivideo->detectedpdca |= ((pdc >> 6) & 0x01);
+                               if(ivideo->newrom) {
+                                       /* New ROM invalidates other PDC resp. */
+                                       if(ivideo->detectedlcda != 0xff) {
+                                               ivideo->detectedpdc = 0xff;
+                                       } else {
+                                               ivideo->detectedpdca = 0xff;
+                                       }
+                               }
+                               if(ivideo->SiS_Pr.PDC == -1) {
+                                       if(ivideo->detectedpdc != 0xff) {
+                                               ivideo->SiS_Pr.PDC = ivideo->detectedpdc;
+                                       }
+                               }
+                               if(ivideo->SiS_Pr.PDCA == -1) {
+                                       if(ivideo->detectedpdca != 0xff) {
+                                               ivideo->SiS_Pr.PDCA = ivideo->detectedpdca;
+                                       }
+                               }
+                               if(ivideo->detectedpdc != 0xff) {
+                                       printk(KERN_INFO
+                                               "sisfb: Detected LCD PDC 0x%02x (for LCD=CRT2)\n",
+                                               ivideo->detectedpdc);
+                               }
+                               if(ivideo->detectedpdca != 0xff) {
+                                       printk(KERN_INFO
+                                               "sisfb: Detected LCD PDC1 0x%02x (for LCD=CRT1)\n",
+                                               ivideo->detectedpdca);
+                               }
+                       }
 
-               agp = agp_allocate_memory(COMMAND_QUEUE_AREA_SIZE/PAGE_SIZE,
-                                         AGP_NORMAL_MEMORY);
-               if (agp == NULL) {
-                       DPRINTK("sisfb: Allocating AGP buffer failed.\n");
-                       agp_enabled = 0;
+                       /* Save EMI */
+                       if(ivideo->vbflags2 & VB2_SISEMIBRIDGE) {
+                               inSISIDXREG(SISPART4,0x30,ivideo->SiS_Pr.EMI_30);
+                               inSISIDXREG(SISPART4,0x31,ivideo->SiS_Pr.EMI_31);
+                               inSISIDXREG(SISPART4,0x32,ivideo->SiS_Pr.EMI_32);
+                               inSISIDXREG(SISPART4,0x33,ivideo->SiS_Pr.EMI_33);
+                               ivideo->SiS_Pr.HaveEMI = TRUE;
+                               if((tmp & 0x20) || (ivideo->detectedlcda != 0xff)) {
+                                       ivideo->SiS_Pr.HaveEMILCD = TRUE;
+                               }
+                       }
+               }
+
+               /* Let user override detected PDCs (all bridges) */
+               if(ivideo->vbflags2 & VB2_30xBLV) {
+                       if((ivideo->SiS_Pr.PDC != -1) &&
+                          (ivideo->SiS_Pr.PDC != ivideo->detectedpdc)) {
+                               printk(KERN_INFO "sisfb: Using LCD PDC 0x%02x (for LCD=CRT2)\n",
+                                       ivideo->SiS_Pr.PDC);
+                       }
+                       if((ivideo->SiS_Pr.PDCA != -1) &&
+                          (ivideo->SiS_Pr.PDCA != ivideo->detectedpdca)) {
+                               printk(KERN_INFO "sisfb: Using LCD PDC1 0x%02x (for LCD=CRT1)\n",
+                                ivideo->SiS_Pr.PDCA);
+                       }
+               }
+
+       }
+#endif
+}
+
+/* -------------------- Memory manager routines ---------------------- */
+
+static u32 __devinit
+sisfb_getheapstart(struct sis_video_info *ivideo)
+{
+       u32 ret = ivideo->sisfb_parm_mem * 1024;
+       u32 maxoffs = ivideo->video_size - ivideo->hwcursor_size - ivideo->cmdQueueSize;
+       u32 def;
+
+       /* Calculate heap start = end of memory for console
+        *
+        * CCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDHHHHQQQQQQQQQQ
+        * C = console, D = heap, H = HWCursor, Q = cmd-queue
+        *
+        * On 76x in UMA+LFB mode, the layout is as follows:
+        * DDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCHHHHQQQQQQQQQQQ
+        * where the heap is the entire UMA area, eventually
+        * into the LFB area if the given mem parameter is
+        * higher than the size of the UMA memory.
+        *
+        * Basically given by "mem" parameter
+        *
+        * maximum = videosize - cmd_queue - hwcursor
+        *           (results in a heap of size 0)
+        * default = SiS 300: depends on videosize
+        *           SiS 315/330/340/XGI: 32k below max
+        */
+
+       if(ivideo->sisvga_engine == SIS_300_VGA) {
+               if(ivideo->video_size > 0x1000000) {
+                       def = 0xc00000;
+               } else if(ivideo->video_size > 0x800000) {
+                       def = 0x800000;
                } else {
-                       if (agp_bind_memory(agp, agp->pg_start) != 0) {
-                               DPRINTK("sisfb: AGP: Failed to bind memory\n");
-                               /* TODO: Free AGP memory here */
-                               agp_enabled = 0;
-                       } else {
-                               agp_enable(0);
+                       def = 0x400000;
+               }
+       } else if(ivideo->UMAsize && ivideo->LFBsize) {
+               ret = def = 0;
+       } else {
+               def = maxoffs - 0x8000;
+       }
+
+       /* Use default for secondary card for now (FIXME) */
+       if((!ret) || (ret > maxoffs) || (ivideo->cardnumber != 0))
+               ret = def;
+
+       return ret;
+}
+
+static u32 __devinit
+sisfb_getheapsize(struct sis_video_info *ivideo)
+{
+       u32 max = ivideo->video_size - ivideo->hwcursor_size - ivideo->cmdQueueSize;
+       u32 ret = 0;
+
+       if(ivideo->UMAsize && ivideo->LFBsize) {
+               if( (!ivideo->sisfb_parm_mem)                   ||
+                   ((ivideo->sisfb_parm_mem * 1024) > max)     ||
+                   ((max - (ivideo->sisfb_parm_mem * 1024)) < ivideo->UMAsize) ) {
+                       ret = ivideo->UMAsize;
+                       max -= ivideo->UMAsize;
+               } else {
+                       ret = max - (ivideo->sisfb_parm_mem * 1024);
+                       max = ivideo->sisfb_parm_mem * 1024;
+               }
+               ivideo->video_offset = ret;
+               ivideo->sisfb_mem = max;
+       } else {
+               ret = max - ivideo->heapstart;
+               ivideo->sisfb_mem = ivideo->heapstart;
+       }
+
+       return ret;
+}
+
+static int __devinit
+sisfb_heap_init(struct sis_video_info *ivideo)
+{
+       struct SIS_OH *poh;
+
+       ivideo->video_offset = 0;
+       if(ivideo->sisfb_parm_mem) {
+               if( (ivideo->sisfb_parm_mem < (2 * 1024 * 1024)) ||
+                   (ivideo->sisfb_parm_mem > ivideo->video_size) ) {
+                       ivideo->sisfb_parm_mem = 0;
+               }
+       }
+
+       ivideo->heapstart = sisfb_getheapstart(ivideo);
+       ivideo->sisfb_heap_size = sisfb_getheapsize(ivideo);
+
+       ivideo->sisfb_heap_start = ivideo->video_vbase + ivideo->heapstart;
+       ivideo->sisfb_heap_end   = ivideo->sisfb_heap_start + ivideo->sisfb_heap_size;
+
+       printk(KERN_INFO "sisfb: Memory heap starting at %dK, size %dK\n",
+               (int)(ivideo->heapstart / 1024), (int)(ivideo->sisfb_heap_size / 1024));
+
+       ivideo->sisfb_heap.vinfo = ivideo;
+
+       ivideo->sisfb_heap.poha_chain = NULL;
+       ivideo->sisfb_heap.poh_freelist = NULL;
+
+       poh = sisfb_poh_new_node(&ivideo->sisfb_heap);
+       if(poh == NULL)
+               return 1;
+
+       poh->poh_next = &ivideo->sisfb_heap.oh_free;
+       poh->poh_prev = &ivideo->sisfb_heap.oh_free;
+       poh->size = ivideo->sisfb_heap_size;
+       poh->offset = ivideo->heapstart;
+
+       ivideo->sisfb_heap.oh_free.poh_next = poh;
+       ivideo->sisfb_heap.oh_free.poh_prev = poh;
+       ivideo->sisfb_heap.oh_free.size = 0;
+       ivideo->sisfb_heap.max_freesize = poh->size;
+
+       ivideo->sisfb_heap.oh_used.poh_next = &ivideo->sisfb_heap.oh_used;
+       ivideo->sisfb_heap.oh_used.poh_prev = &ivideo->sisfb_heap.oh_used;
+       ivideo->sisfb_heap.oh_used.size = SENTINEL;
+
+       if(ivideo->cardnumber == 0) {
+               /* For the first card, make this heap the "global" one
+                * for old DRM (which could handle only one card)
+                */
+               sisfb_heap = &ivideo->sisfb_heap;
+       }
+
+       return 0;
+}
+
+static struct SIS_OH *
+sisfb_poh_new_node(struct SIS_HEAP *memheap)
+{
+       struct SIS_OHALLOC      *poha;
+       struct SIS_OH           *poh;
+       unsigned long           cOhs;
+       int                     i;
+
+       if(memheap->poh_freelist == NULL) {
+               poha = kmalloc(SIS_OH_ALLOC_SIZE, GFP_KERNEL);
+               if(!poha)
+                       return NULL;
+
+               poha->poha_next = memheap->poha_chain;
+               memheap->poha_chain = poha;
+
+               cOhs = (SIS_OH_ALLOC_SIZE - sizeof(struct SIS_OHALLOC)) / sizeof(struct SIS_OH) + 1;
+
+               poh = &poha->aoh[0];
+               for(i = cOhs - 1; i != 0; i--) {
+                       poh->poh_next = poh + 1;
+                       poh = poh + 1;
+               }
+
+               poh->poh_next = NULL;
+               memheap->poh_freelist = &poha->aoh[0];
+       }
+
+       poh = memheap->poh_freelist;
+       memheap->poh_freelist = poh->poh_next;
+
+       return poh;
+}
+
+static struct SIS_OH *
+sisfb_poh_allocate(struct SIS_HEAP *memheap, u32 size)
+{
+       struct SIS_OH   *pohThis;
+       struct SIS_OH   *pohRoot;
+       int             bAllocated = 0;
+
+       if(size > memheap->max_freesize) {
+               DPRINTK("sisfb: Can't allocate %dk video memory\n",
+                       (unsigned int) size / 1024);
+               return NULL;
+       }
+
+       pohThis = memheap->oh_free.poh_next;
+
+       while(pohThis != &memheap->oh_free) {
+               if(size <= pohThis->size) {
+                       bAllocated = 1;
+                       break;
+               }
+               pohThis = pohThis->poh_next;
+       }
+
+       if(!bAllocated) {
+               DPRINTK("sisfb: Can't allocate %dk video memory\n",
+                       (unsigned int) size / 1024);
+               return NULL;
+       }
+
+       if(size == pohThis->size) {
+               pohRoot = pohThis;
+               sisfb_delete_node(pohThis);
+       } else {
+               pohRoot = sisfb_poh_new_node(memheap);
+               if(pohRoot == NULL)
+                       return NULL;
+
+               pohRoot->offset = pohThis->offset;
+               pohRoot->size = size;
+
+               pohThis->offset += size;
+               pohThis->size -= size;
+       }
+
+       memheap->max_freesize -= size;
+
+       pohThis = &memheap->oh_used;
+       sisfb_insert_node(pohThis, pohRoot);
+
+       return pohRoot;
+}
+
+static void
+sisfb_delete_node(struct SIS_OH *poh)
+{
+       poh->poh_prev->poh_next = poh->poh_next;
+       poh->poh_next->poh_prev = poh->poh_prev;
+}
+
+static void
+sisfb_insert_node(struct SIS_OH *pohList, struct SIS_OH *poh)
+{
+       struct SIS_OH *pohTemp = pohList->poh_next;
+
+       pohList->poh_next = poh;
+       pohTemp->poh_prev = poh;
+
+       poh->poh_prev = pohList;
+       poh->poh_next = pohTemp;
+}
+
+static struct SIS_OH *
+sisfb_poh_free(struct SIS_HEAP *memheap, u32 base)
+{
+       struct SIS_OH *pohThis;
+       struct SIS_OH *poh_freed;
+       struct SIS_OH *poh_prev;
+       struct SIS_OH *poh_next;
+       u32    ulUpper;
+       u32    ulLower;
+       int    foundNode = 0;
+
+       poh_freed = memheap->oh_used.poh_next;
+
+       while(poh_freed != &memheap->oh_used) {
+               if(poh_freed->offset == base) {
+                       foundNode = 1;
+                       break;
+               }
+
+               poh_freed = poh_freed->poh_next;
+       }
+
+       if(!foundNode)
+               return NULL;
+
+       memheap->max_freesize += poh_freed->size;
+
+       poh_prev = poh_next = NULL;
+       ulUpper = poh_freed->offset + poh_freed->size;
+       ulLower = poh_freed->offset;
+
+       pohThis = memheap->oh_free.poh_next;
+
+       while(pohThis != &memheap->oh_free) {
+               if(pohThis->offset == ulUpper) {
+                       poh_next = pohThis;
+               } else if((pohThis->offset + pohThis->size) == ulLower) {
+                       poh_prev = pohThis;
+               }
+               pohThis = pohThis->poh_next;
+       }
+
+       sisfb_delete_node(poh_freed);
+
+       if(poh_prev && poh_next) {
+               poh_prev->size += (poh_freed->size + poh_next->size);
+               sisfb_delete_node(poh_next);
+               sisfb_free_node(memheap, poh_freed);
+               sisfb_free_node(memheap, poh_next);
+               return poh_prev;
+       }
+
+       if(poh_prev) {
+               poh_prev->size += poh_freed->size;
+               sisfb_free_node(memheap, poh_freed);
+               return poh_prev;
+       }
+
+       if(poh_next) {
+               poh_next->size += poh_freed->size;
+               poh_next->offset = poh_freed->offset;
+               sisfb_free_node(memheap, poh_freed);
+               return poh_next;
+       }
+
+       sisfb_insert_node(&memheap->oh_free, poh_freed);
+
+       return poh_freed;
+}
+
+static void
+sisfb_free_node(struct SIS_HEAP *memheap, struct SIS_OH *poh)
+{
+       if(poh == NULL)
+               return;
+
+       poh->poh_next = memheap->poh_freelist;
+       memheap->poh_freelist = poh;
+}
+
+static void
+sis_int_malloc(struct sis_video_info *ivideo, struct sis_memreq *req)
+{
+       struct SIS_OH *poh = NULL;
+
+       if((ivideo) && (ivideo->sisfb_id == SISFB_ID) && (!ivideo->havenoheap))
+               poh = sisfb_poh_allocate(&ivideo->sisfb_heap, (u32)req->size);
+
+       if(poh == NULL) {
+               req->offset = req->size = 0;
+               DPRINTK("sisfb: Video RAM allocation failed\n");
+       } else {
+               req->offset = poh->offset;
+               req->size = poh->size;
+               DPRINTK("sisfb: Video RAM allocation succeeded: 0x%lx\n",
+                       (poh->offset + ivideo->video_vbase));
+       }
+}
+
+void
+sis_malloc(struct sis_memreq *req)
+{
+       struct sis_video_info *ivideo = sisfb_heap->vinfo;
+
+       if(&ivideo->sisfb_heap == sisfb_heap)
+               sis_int_malloc(ivideo, req);
+       else
+               req->offset = req->size = 0;
+}
+
+void
+sis_malloc_new(struct pci_dev *pdev, struct sis_memreq *req)
+{
+       struct sis_video_info *ivideo = pci_get_drvdata(pdev);
+
+       sis_int_malloc(ivideo, req);
+}
+
+/* sis_free: u32 because "base" is offset inside video ram, can never be >4GB */
+
+static void
+sis_int_free(struct sis_video_info *ivideo, u32 base)
+{
+       struct SIS_OH *poh;
+
+       if((!ivideo) || (ivideo->sisfb_id != SISFB_ID) || (ivideo->havenoheap))
+               return;
+
+       poh = sisfb_poh_free(&ivideo->sisfb_heap, base);
+
+       if(poh == NULL) {
+               DPRINTK("sisfb: sisfb_poh_free() failed at base 0x%x\n",
+                       (unsigned int) base);
+       }
+}
+
+void
+sis_free(u32 base)
+{
+       struct sis_video_info *ivideo = sisfb_heap->vinfo;
+
+       sis_int_free(ivideo, base);
+}
+
+void
+sis_free_new(struct pci_dev *pdev, u32 base)
+{
+       struct sis_video_info *ivideo = pci_get_drvdata(pdev);
+
+       sis_int_free(ivideo, base);
+}
+
+/* --------------------- SetMode routines ------------------------- */
+
+static void
+sisfb_check_engine_and_sync(struct sis_video_info *ivideo)
+{
+       u8 cr30, cr31;
+
+       /* Check if MMIO and engines are enabled,
+        * and sync in case they are. Can't use
+        * ivideo->accel here, as this might have
+        * been changed before this is called.
+        */
+       inSISIDXREG(SISSR, IND_SIS_PCI_ADDRESS_SET, cr30);
+       inSISIDXREG(SISSR, IND_SIS_MODULE_ENABLE, cr31);
+       /* MMIO and 2D/3D engine enabled? */
+       if((cr30 & SIS_MEM_MAP_IO_ENABLE) && (cr31 & 0x42)) {
+#ifdef CONFIG_FB_SIS_300
+               if(ivideo->sisvga_engine == SIS_300_VGA) {
+                       /* Don't care about TurboQueue. It's
+                        * enough to know that the engines
+                        * are enabled
+                        */
+                       sisfb_syncaccel(ivideo);
+               }
+#endif
+#ifdef CONFIG_FB_SIS_315
+               if(ivideo->sisvga_engine == SIS_315_VGA) {
+                       /* Check that any queue mode is
+                        * enabled, and that the queue
+                        * is not in the state of "reset"
+                        */
+                       inSISIDXREG(SISSR, 0x26, cr30);
+                       if((cr30 & 0xe0) && (!(cr30 & 0x01))) {
+                               sisfb_syncaccel(ivideo);
                        }
                }
+#endif
        }
-#else
-       agp_enabled = 0;
+}
+
+static void
+sisfb_pre_setmode(struct sis_video_info *ivideo)
+{
+       u8 cr30 = 0, cr31 = 0, cr33 = 0, cr35 = 0, cr38 = 0;
+       int tvregnum = 0;
+
+       ivideo->currentvbflags &= (VB_VIDEOBRIDGE | VB_DISPTYPE_DISP2);
+
+       outSISIDXREG(SISSR, 0x05, 0x86);
+
+       inSISIDXREG(SISCR, 0x31, cr31);
+       cr31 &= ~0x60;
+       cr31 |= 0x04;
+
+       cr33 = ivideo->rate_idx & 0x0F;
+
+#ifdef CONFIG_FB_SIS_315
+       if(ivideo->sisvga_engine == SIS_315_VGA) {
+          if(ivideo->chip >= SIS_661) {
+             inSISIDXREG(SISCR, 0x38, cr38);
+             cr38 &= ~0x07;  /* Clear LCDA/DualEdge and YPbPr bits */
+          } else {
+             tvregnum = 0x38;
+             inSISIDXREG(SISCR, tvregnum, cr38);
+             cr38 &= ~0x3b;  /* Clear LCDA/DualEdge and YPbPr bits */
+          }
+       }
+#endif
+#ifdef CONFIG_FB_SIS_300
+       if(ivideo->sisvga_engine == SIS_300_VGA) {
+          tvregnum = 0x35;
+          inSISIDXREG(SISCR, tvregnum, cr38);
+       }
+#endif
+
+       SiS_SetEnableDstn(&ivideo->SiS_Pr, FALSE);
+       SiS_SetEnableFstn(&ivideo->SiS_Pr, FALSE);
+       ivideo->curFSTN = ivideo->curDSTN = 0;
+
+       switch(ivideo->currentvbflags & VB_DISPTYPE_DISP2) {
+
+          case CRT2_TV:
+             cr38 &= ~0xc0;   /* Clear PAL-M / PAL-N bits */
+             if((ivideo->vbflags & TV_YPBPR) && (ivideo->vbflags2 & VB2_SISYPBPRBRIDGE)) {
+#ifdef CONFIG_FB_SIS_315
+                if(ivideo->chip >= SIS_661) {
+                   cr38 |= 0x04;
+                   if(ivideo->vbflags & TV_YPBPR525P)       cr35 |= 0x20;
+                   else if(ivideo->vbflags & TV_YPBPR750P)  cr35 |= 0x40;
+                   else if(ivideo->vbflags & TV_YPBPR1080I) cr35 |= 0x60;
+                   cr30 |= SIS_SIMULTANEOUS_VIEW_ENABLE;
+                   cr35 &= ~0x01;
+                   ivideo->currentvbflags |= (TV_YPBPR | (ivideo->vbflags & TV_YPBPRALL));
+                } else if(ivideo->sisvga_engine == SIS_315_VGA) {
+                   cr30 |= (0x80 | SIS_SIMULTANEOUS_VIEW_ENABLE);
+                   cr38 |= 0x08;
+                   if(ivideo->vbflags & TV_YPBPR525P)       cr38 |= 0x10;
+                   else if(ivideo->vbflags & TV_YPBPR750P)  cr38 |= 0x20;
+                   else if(ivideo->vbflags & TV_YPBPR1080I) cr38 |= 0x30;
+                   cr31 &= ~0x01;
+                   ivideo->currentvbflags |= (TV_YPBPR | (ivideo->vbflags & TV_YPBPRALL));
+                }
+#endif
+             } else if((ivideo->vbflags & TV_HIVISION) &&
+                               (ivideo->vbflags2 & VB2_SISHIVISIONBRIDGE)) {
+                if(ivideo->chip >= SIS_661) {
+                   cr38 |= 0x04;
+                   cr35 |= 0x60;
+                } else {
+                   cr30 |= 0x80;
+                }
+                cr30 |= SIS_SIMULTANEOUS_VIEW_ENABLE;
+                cr31 |= 0x01;
+                cr35 |= 0x01;
+                ivideo->currentvbflags |= TV_HIVISION;
+             } else if(ivideo->vbflags & TV_SCART) {
+                cr30 = (SIS_VB_OUTPUT_SCART | SIS_SIMULTANEOUS_VIEW_ENABLE);
+                cr31 |= 0x01;
+                cr35 |= 0x01;
+                ivideo->currentvbflags |= TV_SCART;
+             } else {
+                if(ivideo->vbflags & TV_SVIDEO) {
+                   cr30 = (SIS_VB_OUTPUT_SVIDEO | SIS_SIMULTANEOUS_VIEW_ENABLE);
+                   ivideo->currentvbflags |= TV_SVIDEO;
+                }
+                if(ivideo->vbflags & TV_AVIDEO) {
+                   cr30 = (SIS_VB_OUTPUT_COMPOSITE | SIS_SIMULTANEOUS_VIEW_ENABLE);
+                   ivideo->currentvbflags |= TV_AVIDEO;
+                }
+             }
+             cr31 |= SIS_DRIVER_MODE;
+
+             if(ivideo->vbflags & (TV_AVIDEO | TV_SVIDEO)) {
+                if(ivideo->vbflags & TV_PAL) {
+                   cr31 |= 0x01; cr35 |= 0x01;
+                   ivideo->currentvbflags |= TV_PAL;
+                   if(ivideo->vbflags & TV_PALM) {
+                      cr38 |= 0x40; cr35 |= 0x04;
+                      ivideo->currentvbflags |= TV_PALM;
+                   } else if(ivideo->vbflags & TV_PALN) {
+                      cr38 |= 0x80; cr35 |= 0x08;
+                      ivideo->currentvbflags |= TV_PALN;
+                   }
+                } else {
+                   cr31 &= ~0x01; cr35 &= ~0x01;
+                   ivideo->currentvbflags |= TV_NTSC;
+                   if(ivideo->vbflags & TV_NTSCJ) {
+                      cr38 |= 0x40; cr35 |= 0x02;
+                      ivideo->currentvbflags |= TV_NTSCJ;
+                   }
+                }
+             }
+             break;
+
+          case CRT2_LCD:
+             cr30  = (SIS_VB_OUTPUT_LCD | SIS_SIMULTANEOUS_VIEW_ENABLE);
+             cr31 |= SIS_DRIVER_MODE;
+             SiS_SetEnableDstn(&ivideo->SiS_Pr, ivideo->sisfb_dstn);
+             SiS_SetEnableFstn(&ivideo->SiS_Pr, ivideo->sisfb_fstn);
+             ivideo->curFSTN = ivideo->sisfb_fstn;
+             ivideo->curDSTN = ivideo->sisfb_dstn;
+             break;
+
+          case CRT2_VGA:
+             cr30 = (SIS_VB_OUTPUT_CRT2 | SIS_SIMULTANEOUS_VIEW_ENABLE);
+             cr31 |= SIS_DRIVER_MODE;
+             if(ivideo->sisfb_nocrt2rate) {
+                cr33 |= (sisbios_mode[ivideo->sisfb_mode_idx].rate_idx << 4);
+             } else {
+                cr33 |= ((ivideo->rate_idx & 0x0F) << 4);
+             }
+             break;
+
+          default:     /* disable CRT2 */
+             cr30 = 0x00;
+             cr31 |= (SIS_DRIVER_MODE | SIS_VB_OUTPUT_DISABLE);
+       }
+
+       outSISIDXREG(SISCR, 0x30, cr30);
+       outSISIDXREG(SISCR, 0x33, cr33);
+
+       if(ivideo->chip >= SIS_661) {
+#ifdef CONFIG_FB_SIS_315
+          cr31 &= ~0x01;                          /* Clear PAL flag (now in CR35) */
+          setSISIDXREG(SISCR, 0x35, ~0x10, cr35); /* Leave overscan bit alone */
+          cr38 &= 0x07;                           /* Use only LCDA and HiVision/YPbPr bits */
+          setSISIDXREG(SISCR, 0x38, 0xf8, cr38);
 #endif
+       } else if(ivideo->chip != SIS_300) {
+          outSISIDXREG(SISCR, tvregnum, cr38);
+       }
+       outSISIDXREG(SISCR, 0x31, cr31);
+
+       ivideo->SiS_Pr.SiS_UseOEM = ivideo->sisfb_useoem;
+
+       sisfb_check_engine_and_sync(ivideo);
+}
+
+/* Fix SR11 for 661 and later */
+#ifdef CONFIG_FB_SIS_315
+static void
+sisfb_fixup_SR11(struct sis_video_info *ivideo)
+{
+       u8  tmpreg;
+
+       if(ivideo->chip >= SIS_661) {
+               inSISIDXREG(SISSR,0x11,tmpreg);
+               if(tmpreg & 0x20) {
+                       inSISIDXREG(SISSR,0x3e,tmpreg);
+                       tmpreg = (tmpreg + 1) & 0xff;
+                       outSISIDXREG(SISSR,0x3e,tmpreg);
+                       inSISIDXREG(SISSR,0x11,tmpreg);
+               }
+               if(tmpreg & 0xf0) {
+                       andSISIDXREG(SISSR,0x11,0x0f);
+               }
+       }
+}
+#endif
+
+static void
+sisfb_set_TVxposoffset(struct sis_video_info *ivideo, int val)
+{
+       if(val > 32) val = 32;
+       if(val < -32) val = -32;
+       ivideo->tvxpos = val;
+
+       if(ivideo->sisfblocked) return;
+       if(!ivideo->modechanged) return;
+
+       if(ivideo->currentvbflags & CRT2_TV) {
+
+               if(ivideo->vbflags2 & VB2_CHRONTEL) {
+
+                       int x = ivideo->tvx;
+
+                       switch(ivideo->chronteltype) {
+                       case 1:
+                               x += val;
+                               if(x < 0) x = 0;
+                               outSISIDXREG(SISSR,0x05,0x86);
+                               SiS_SetCH700x(&ivideo->SiS_Pr, 0x0a, (x & 0xff));
+                               SiS_SetCH70xxANDOR(&ivideo->SiS_Pr, 0x08, ((x & 0x0100) >> 7), 0xFD);
+                               break;
+                       case 2:
+                               /* Not supported by hardware */
+                               break;
+                       }
+
+               } else if(ivideo->vbflags2 & VB2_SISBRIDGE) {
+
+                       u8 p2_1f,p2_20,p2_2b,p2_42,p2_43;
+                       unsigned short temp;
+
+                       p2_1f = ivideo->p2_1f;
+                       p2_20 = ivideo->p2_20;
+                       p2_2b = ivideo->p2_2b;
+                       p2_42 = ivideo->p2_42;
+                       p2_43 = ivideo->p2_43;
+
+                       temp = p2_1f | ((p2_20 & 0xf0) << 4);
+                       temp += (val * 2);
+                       p2_1f = temp & 0xff;
+                       p2_20 = (temp & 0xf00) >> 4;
+                       p2_2b = ((p2_2b & 0x0f) + (val * 2)) & 0x0f;
+                       temp = p2_43 | ((p2_42 & 0xf0) << 4);
+                       temp += (val * 2);
+                       p2_43 = temp & 0xff;
+                       p2_42 = (temp & 0xf00) >> 4;
+                       outSISIDXREG(SISPART2,0x1f,p2_1f);
+                       setSISIDXREG(SISPART2,0x20,0x0F,p2_20);
+                       setSISIDXREG(SISPART2,0x2b,0xF0,p2_2b);
+                       setSISIDXREG(SISPART2,0x42,0x0F,p2_42);
+                       outSISIDXREG(SISPART2,0x43,p2_43);
+               }
+       }
+}
+
+static void
+sisfb_set_TVyposoffset(struct sis_video_info *ivideo, int val)
+{
+       if(val > 32) val = 32;
+       if(val < -32) val = -32;
+       ivideo->tvypos = val;
+
+       if(ivideo->sisfblocked) return;
+       if(!ivideo->modechanged) return;
+
+       if(ivideo->currentvbflags & CRT2_TV) {
+
+               if(ivideo->vbflags2 & VB2_CHRONTEL) {
 
-       /* Now select the queue mode */
+                       int y = ivideo->tvy;
 
-       if ((agp_enabled) && (sisfb_queuemode == AGP_CMD_QUEUE)) {
-               cmd_type = AGP_CMD_QUEUE;
-               printk(KERN_INFO "sisfb: Using AGP queue mode\n");
-/*     } else if (sisfb_heap_size >= COMMAND_QUEUE_AREA_SIZE)  */
-        } else if (sisfb_queuemode == VM_CMD_QUEUE) {
-               cmd_type = VM_CMD_QUEUE;
-               printk(KERN_INFO "sisfb: Using VRAM queue mode\n");
+                       switch(ivideo->chronteltype) {
+                       case 1:
+                               y -= val;
+                               if(y < 0) y = 0;
+                               outSISIDXREG(SISSR,0x05,0x86);
+                               SiS_SetCH700x(&ivideo->SiS_Pr, 0x0b, (y & 0xff));
+                               SiS_SetCH70xxANDOR(&ivideo->SiS_Pr, 0x08, ((y & 0x0100) >> 8), 0xFE);
+                               break;
+                       case 2:
+                               /* Not supported by hardware */
+                               break;
+                       }
+
+               } else if(ivideo->vbflags2 & VB2_SISBRIDGE) {
+
+                       char p2_01, p2_02;
+                       val /= 2;
+                       p2_01 = ivideo->p2_01;
+                       p2_02 = ivideo->p2_02;
+
+                       p2_01 += val;
+                       p2_02 += val;
+                       if(!(ivideo->currentvbflags & (TV_HIVISION | TV_YPBPR))) {
+                               while((p2_01 <= 0) || (p2_02 <= 0)) {
+                                       p2_01 += 2;
+                                       p2_02 += 2;
+                               }
+                       }
+                       outSISIDXREG(SISPART2,0x01,p2_01);
+                       outSISIDXREG(SISPART2,0x02,p2_02);
+               }
+       }
+}
+
+static void
+sisfb_post_setmode(struct sis_video_info *ivideo)
+{
+       BOOLEAN crt1isoff = FALSE;
+       BOOLEAN doit = TRUE;
+#if defined(CONFIG_FB_SIS_300) || defined(CONFIG_FB_SIS_315)
+       u8 reg;
+#endif
+#ifdef CONFIG_FB_SIS_315
+       u8 reg1;
+#endif
+
+       outSISIDXREG(SISSR, 0x05, 0x86);
+
+#ifdef CONFIG_FB_SIS_315
+       sisfb_fixup_SR11(ivideo);
+#endif
+
+       /* Now we actually HAVE changed the display mode */
+       ivideo->modechanged = 1;
+
+       /* We can't switch off CRT1 if bridge is in slave mode */
+       if(ivideo->vbflags2 & VB2_VIDEOBRIDGE) {
+               if(sisfb_bridgeisslave(ivideo)) doit = FALSE;
+       } else
+               ivideo->sisfb_crt1off = 0;
+
+#ifdef CONFIG_FB_SIS_300
+       if(ivideo->sisvga_engine == SIS_300_VGA) {
+               if((ivideo->sisfb_crt1off) && (doit)) {
+                       crt1isoff = TRUE;
+                       reg = 0x00;
+               } else {
+                       crt1isoff = FALSE;
+                       reg = 0x80;
+               }
+               setSISIDXREG(SISCR, 0x17, 0x7f, reg);
+       }
+#endif
+#ifdef CONFIG_FB_SIS_315
+       if(ivideo->sisvga_engine == SIS_315_VGA) {
+               if((ivideo->sisfb_crt1off) && (doit)) {
+                       crt1isoff = TRUE;
+                       reg  = 0x40;
+                       reg1 = 0xc0;
+               } else {
+                       crt1isoff = FALSE;
+                       reg  = 0x00;
+                       reg1 = 0x00;
+               }
+               setSISIDXREG(SISCR, ivideo->SiS_Pr.SiS_MyCR63, ~0x40, reg);
+               setSISIDXREG(SISSR, 0x1f, ~0xc0, reg1);
+       }
+#endif
+
+       if(crt1isoff) {
+               ivideo->currentvbflags &= ~VB_DISPTYPE_CRT1;
+               ivideo->currentvbflags |= VB_SINGLE_MODE;
+       } else {
+               ivideo->currentvbflags |= VB_DISPTYPE_CRT1;
+               if(ivideo->currentvbflags & VB_DISPTYPE_CRT2) {
+                       ivideo->currentvbflags |= VB_MIRROR_MODE;
+               } else {
+                       ivideo->currentvbflags |= VB_SINGLE_MODE;
+               }
+       }
+
+       andSISIDXREG(SISSR, IND_SIS_RAMDAC_CONTROL, ~0x04);
+
+       if(ivideo->currentvbflags & CRT2_TV) {
+               if(ivideo->vbflags2 & VB2_SISBRIDGE) {
+                       inSISIDXREG(SISPART2,0x1f,ivideo->p2_1f);
+                       inSISIDXREG(SISPART2,0x20,ivideo->p2_20);
+                       inSISIDXREG(SISPART2,0x2b,ivideo->p2_2b);
+                       inSISIDXREG(SISPART2,0x42,ivideo->p2_42);
+                       inSISIDXREG(SISPART2,0x43,ivideo->p2_43);
+                       inSISIDXREG(SISPART2,0x01,ivideo->p2_01);
+                       inSISIDXREG(SISPART2,0x02,ivideo->p2_02);
+               } else if(ivideo->vbflags2 & VB2_CHRONTEL) {
+                       if(ivideo->chronteltype == 1) {
+                               ivideo->tvx = SiS_GetCH700x(&ivideo->SiS_Pr, 0x0a);
+                               ivideo->tvx |= (((SiS_GetCH700x(&ivideo->SiS_Pr, 0x08) & 0x02) >> 1) << 8);
+                               ivideo->tvy = SiS_GetCH700x(&ivideo->SiS_Pr, 0x0b);
+                               ivideo->tvy |= ((SiS_GetCH700x(&ivideo->SiS_Pr, 0x08) & 0x01) << 8);
+                       }
+               }
+       }
+
+       if(ivideo->tvxpos) {
+               sisfb_set_TVxposoffset(ivideo, ivideo->tvxpos);
+       }
+       if(ivideo->tvypos) {
+               sisfb_set_TVyposoffset(ivideo, ivideo->tvypos);
+       }
+
+       /* Eventually sync engines */
+       sisfb_check_engine_and_sync(ivideo);
+
+       /* (Re-)Initialize chip engines */
+       if(ivideo->accel) {
+               sisfb_engine_init(ivideo);
        } else {
-               printk(KERN_INFO "sisfb: Using MMIO queue mode\n");
-               cmd_type = MMIO_CMD;
+               ivideo->engineok = 0;
        }
+}
+
+static int
+sisfb_reset_mode(struct sis_video_info *ivideo)
+{
+       if(sisfb_set_mode(ivideo, 0))
+               return 1;
+
+       sisfb_set_pitch(ivideo);
+       sisfb_set_base_CRT1(ivideo, ivideo->current_base);
+       sisfb_set_base_CRT2(ivideo, ivideo->current_base);
+
+       return 0;
+}
+
+static void
+sisfb_handle_command(struct sis_video_info *ivideo, struct sisfb_cmd *sisfb_command)
+{
+       int mycrt1off;
+
+       switch(sisfb_command->sisfb_cmd) {
+       case SISFB_CMD_GETVBFLAGS:
+               if(!ivideo->modechanged) {
+                       sisfb_command->sisfb_result[0] = SISFB_CMD_ERR_EARLY;
+               } else {
+                       sisfb_command->sisfb_result[0] = SISFB_CMD_ERR_OK;
+                       sisfb_command->sisfb_result[1] = ivideo->currentvbflags;
+                       sisfb_command->sisfb_result[2] = ivideo->vbflags2;
+               }
+               break;
+       case SISFB_CMD_SWITCHCRT1:
+               /* arg[0]: 0 = off, 1 = on, 99 = query */
+               if(!ivideo->modechanged) {
+                       sisfb_command->sisfb_result[0] = SISFB_CMD_ERR_EARLY;
+               } else if(sisfb_command->sisfb_arg[0] == 99) {
+                       /* Query */
+                       sisfb_command->sisfb_result[1] = ivideo->sisfb_crt1off ? 0 : 1;
+                       sisfb_command->sisfb_result[0] = SISFB_CMD_ERR_OK;
+               } else if(ivideo->sisfblocked) {
+                       sisfb_command->sisfb_result[0] = SISFB_CMD_ERR_LOCKED;
+               } else if((!(ivideo->currentvbflags & CRT2_ENABLE)) &&
+                                       (sisfb_command->sisfb_arg[0] == 0)) {
+                       sisfb_command->sisfb_result[0] = SISFB_CMD_ERR_NOCRT2;
+               } else {
+                       sisfb_command->sisfb_result[0] = SISFB_CMD_ERR_OK;
+                       mycrt1off = sisfb_command->sisfb_arg[0] ? 0 : 1;
+                       if( ((ivideo->currentvbflags & VB_DISPTYPE_CRT1) && mycrt1off) ||
+                           ((!(ivideo->currentvbflags & VB_DISPTYPE_CRT1)) && !mycrt1off) ) {
+                               ivideo->sisfb_crt1off = mycrt1off;
+                               if(sisfb_reset_mode(ivideo)) {
+                                       sisfb_command->sisfb_result[0] = SISFB_CMD_ERR_OTHER;
+                               }
+                       }
+                       sisfb_command->sisfb_result[1] = ivideo->sisfb_crt1off ? 0 : 1;
+               }
+               break;
+       /* more to come */
+       default:
+               sisfb_command->sisfb_result[0] = SISFB_CMD_ERR_UNKNOWN;
+               printk(KERN_ERR "sisfb: Unknown command 0x%x\n",
+                       sisfb_command->sisfb_cmd);
+       }
+}
+
+#ifndef MODULE
+SISINITSTATIC int __init
+sisfb_setup(char *options)
+{
+       char *this_opt;
+
+       sisfb_setdefaultparms();
+
+       if(!options || !(*options))
+               return 0;
+
+       while((this_opt = strsep(&options, ",")) != NULL) {
+
+               if(!(*this_opt)) continue;
+
+               if(!strnicmp(this_opt, "off", 3)) {
+                       sisfb_off = 1;
+               } else if(!strnicmp(this_opt, "forcecrt2type:", 14)) {
+                       /* Need to check crt2 type first for fstn/dstn */
+                       sisfb_search_crt2type(this_opt + 14);
+               } else if(!strnicmp(this_opt, "tvmode:",7)) {
+                       sisfb_search_tvstd(this_opt + 7);
+               } else if(!strnicmp(this_opt, "tvstandard:",11)) {
+                       sisfb_search_tvstd(this_opt + 11);
+               } else if(!strnicmp(this_opt, "mode:", 5)) {
+                       sisfb_search_mode(this_opt + 5, FALSE);
+               } else if(!strnicmp(this_opt, "vesa:", 5)) {
+                       sisfb_search_vesamode(simple_strtoul(this_opt + 5, NULL, 0), FALSE);
+               } else if(!strnicmp(this_opt, "rate:", 5)) {
+                       sisfb_parm_rate = simple_strtoul(this_opt + 5, NULL, 0);
+               } else if(!strnicmp(this_opt, "forcecrt1:", 10)) {
+                       sisfb_forcecrt1 = (int)simple_strtoul(this_opt + 10, NULL, 0);
+               } else if(!strnicmp(this_opt, "mem:",4)) {
+                       sisfb_parm_mem = simple_strtoul(this_opt + 4, NULL, 0);
+               } else if(!strnicmp(this_opt, "pdc:", 4)) {
+                       sisfb_pdc = simple_strtoul(this_opt + 4, NULL, 0);
+               } else if(!strnicmp(this_opt, "pdc1:", 5)) {
+                       sisfb_pdca = simple_strtoul(this_opt + 5, NULL, 0);
+               } else if(!strnicmp(this_opt, "noaccel", 7)) {
+                       sisfb_accel = 0;
+               } else if(!strnicmp(this_opt, "accel", 5)) {
+                       sisfb_accel = -1;
+               } else if(!strnicmp(this_opt, "noypan", 6)) {
+                       sisfb_ypan = 0;
+               } else if(!strnicmp(this_opt, "ypan", 4)) {
+                       sisfb_ypan = -1;
+               } else if(!strnicmp(this_opt, "nomax", 5)) {
+                       sisfb_max = 0;
+               } else if(!strnicmp(this_opt, "max", 3)) {
+                       sisfb_max = -1;
+               } else if(!strnicmp(this_opt, "userom:", 7)) {
+                       sisfb_userom = (int)simple_strtoul(this_opt + 7, NULL, 0);
+               } else if(!strnicmp(this_opt, "useoem:", 7)) {
+                       sisfb_useoem = (int)simple_strtoul(this_opt + 7, NULL, 0);
+               } else if(!strnicmp(this_opt, "nocrt2rate", 10)) {
+                       sisfb_nocrt2rate = 1;
+               } else if(!strnicmp(this_opt, "scalelcd:", 9)) {
+                       unsigned long temp = 2;
+                       temp = simple_strtoul(this_opt + 9, NULL, 0);
+                       if((temp == 0) || (temp == 1)) {
+                          sisfb_scalelcd = temp ^ 1;
+                       }
+               } else if(!strnicmp(this_opt, "tvxposoffset:", 13)) {
+                       int temp = 0;
+                       temp = (int)simple_strtol(this_opt + 13, NULL, 0);
+                       if((temp >= -32) && (temp <= 32)) {
+                          sisfb_tvxposoffset = temp;
+                       }
+               } else if(!strnicmp(this_opt, "tvyposoffset:", 13)) {
+                       int temp = 0;
+                       temp = (int)simple_strtol(this_opt + 13, NULL, 0);
+                       if((temp >= -32) && (temp <= 32)) {
+                          sisfb_tvyposoffset = temp;
+                       }
+               } else if(!strnicmp(this_opt, "specialtiming:", 14)) {
+                       sisfb_search_specialtiming(this_opt + 14);
+               } else if(!strnicmp(this_opt, "lvdshl:", 7)) {
+                       int temp = 4;
+                       temp = simple_strtoul(this_opt + 7, NULL, 0);
+                       if((temp >= 0) && (temp <= 3)) {
+                          sisfb_lvdshl = temp;
+                       }
+               } else if(this_opt[0] >= '0' && this_opt[0] <= '9') {
+                       sisfb_search_mode(this_opt, TRUE);
+#if !defined(__i386__) && !defined(__x86_64__)
+               } else if(!strnicmp(this_opt, "resetcard", 9)) {
+                       sisfb_resetcard = 1;
+               } else if(!strnicmp(this_opt, "videoram:", 9)) {
+                       sisfb_videoram = simple_strtoul(this_opt + 9, NULL, 0);
+#endif
+               } else {
+                       printk(KERN_INFO "sisfb: Invalid option %s\n", this_opt);
+               }
 
-       switch (agp_size) {
-          case 0x80000:
-               temp = SIS_CMD_QUEUE_SIZE_512k;
-               break;
-          case 0x100000:
-               temp = SIS_CMD_QUEUE_SIZE_1M;
-               break;
-          case 0x200000:
-               temp = SIS_CMD_QUEUE_SIZE_2M;
-               break;
-          case 0x400000:
-               temp = SIS_CMD_QUEUE_SIZE_4M;
-               break;
        }
 
-       switch (cmd_type) {
-          case AGP_CMD_QUEUE:
-#ifndef AGPOFF
-               DPRINTK("sisfb: AGP buffer base = 0x%lx, offset = 0x%x, size = %dK\n",
-                       agp_info->aper_base, agp->physical, agp_size/1024);
+       return 0;
+}
+#endif
 
-               agp_phys = agp_info->aper_base + agp->physical;
+static int __devinit
+sisfb_check_rom(SIS_IOTYPE1 *rom_base, struct sis_video_info *ivideo)
+{
+       SIS_IOTYPE1 *rom;
+       int romptr;
 
-               outSISIDXREG(SISCR,  IND_SIS_AGP_IO_PAD, 0);
-               outSISIDXREG(SISCR,  IND_SIS_AGP_IO_PAD, SIS_AGP_2X);
+       if((readb(rom_base) != 0x55) || (readb(rom_base + 1) != 0xaa))
+               return 0;
 
-                outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_THRESHOLD, COMMAND_QUEUE_THRESHOLD);
+       romptr = (readb(rom_base + 0x18) | (readb(rom_base + 0x19) << 8));
+       if(romptr > (0x10000 - 8))
+               return 0;
 
-               outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, SIS_CMD_QUEUE_RESET);
+       rom = rom_base + romptr;
 
-               *write_port = *read_port;
+       if((readb(rom)     != 'P') || (readb(rom + 1) != 'C') ||
+          (readb(rom + 2) != 'I') || (readb(rom + 3) != 'R'))
+               return 0;
 
-               temp |= SIS_AGP_CMDQUEUE_ENABLE;
-               outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, temp);
+       if((readb(rom + 4) | (readb(rom + 5) << 8)) != ivideo->chip_vendor)
+               return 0;
 
-               *cmdq_baseport = agp_phys;
+       if((readb(rom + 6) | (readb(rom + 7) << 8)) != ivideo->chip_id)
+               return 0;
 
-               sisfb_caps |= AGP_CMD_QUEUE_CAP;
-#endif
-               break;
+       return 1;
+}
 
-          case VM_CMD_QUEUE:
-               sisfb_heap_end -= COMMAND_QUEUE_AREA_SIZE;
-               sisfb_heap_size -= COMMAND_QUEUE_AREA_SIZE;
+static unsigned char * __devinit
+sisfb_find_rom(struct pci_dev *pdev)
+{
+       struct sis_video_info *ivideo = pci_get_drvdata(pdev);
+       SIS_IOTYPE1 *rom_base;
+       unsigned char *myrombase = NULL;
+       u32 temp;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
+       size_t romsize;
+
+       /* First, try the official pci ROM functions (except
+        * on integrated chipsets which have no ROM).
+        */
 
-               outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_THRESHOLD, COMMAND_QUEUE_THRESHOLD);
+       if(!ivideo->nbridge) {
 
-               outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, SIS_CMD_QUEUE_RESET);
+               if((rom_base = pci_map_rom(pdev, &romsize))) {
 
-               *write_port = *read_port;
+                       if(sisfb_check_rom(rom_base, ivideo)) {
 
-               temp |= SIS_VRAM_CMDQUEUE_ENABLE;
-               outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, temp);
+                               if((myrombase = vmalloc(65536))) {
 
-               *cmdq_baseport = ivideo.video_size - COMMAND_QUEUE_AREA_SIZE;
+                                       /* Work around bug in pci/rom.c: Folks forgot to check
+                                        * whether the size retrieved from the BIOS image eventually
+                                        * is larger than the mapped size
+                                        */
+                                       if(pci_resource_len(pdev, PCI_ROM_RESOURCE) < romsize)
+                                               romsize = pci_resource_len(pdev, PCI_ROM_RESOURCE);
 
-               sisfb_caps |= VM_CMD_QUEUE_CAP;
+                                       memcpy_fromio(myrombase, rom_base,
+                                                       (romsize > 65536) ? 65536 : romsize);
+                               }
+                       }
+                       pci_unmap_rom(pdev, rom_base);
+               }
+       }
 
-               DPRINTK("sisfb: VM Cmd Queue offset = 0x%lx, size is %dK\n",
-                       *cmdq_baseport, COMMAND_QUEUE_AREA_SIZE/1024);
-               break;
+       if(myrombase) return myrombase;
+#endif
 
-          default:  /* MMIO */
-               sisfb_heap_end -= COMMAND_QUEUE_AREA_SIZE;
-               sisfb_heap_size -= COMMAND_QUEUE_AREA_SIZE;
+       /* Otherwise do it the conventional way. */
 
-               outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_THRESHOLD, COMMAND_QUEUE_THRESHOLD);
-               outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, SIS_CMD_QUEUE_RESET);
+#if defined(__i386__) || defined(__x86_64__)
 
-               *write_port = *read_port;
+       for(temp = 0x000c0000; temp < 0x000f0000; temp += 0x00001000) {
 
-               /* Set Auto_Correction bit */
-               temp |= (SIS_MMIO_CMD_ENABLE | SIS_CMD_AUTO_CORR);
-               outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, temp);
+               rom_base = ioremap(temp, 65536);
+               if(!rom_base)
+                       continue;
 
-               *cmdq_baseport = ivideo.video_size - COMMAND_QUEUE_AREA_SIZE;
+               if(!sisfb_check_rom(rom_base, ivideo)) {
+                       iounmap(rom_base);
+                       continue;
+               }
 
-               sisfb_caps |= MMIO_CMD_QUEUE_CAP;
+               if((myrombase = vmalloc(65536)))
+                       memcpy_fromio(myrombase, rom_base, 65536);
 
-               DPRINTK("sisfb: MMIO Cmd Queue offset = 0x%lx, size is %dK\n",
-                       *cmdq_baseport, COMMAND_QUEUE_AREA_SIZE/1024);
+               iounmap(rom_base);
                break;
-       }
-     } /* sisvga_engine = 315 */
-#endif
-
-#ifdef CONFIG_FB_SIS_300
-     if (sisvga_engine == SIS_300_VGA) {
-           /* Now initialize TurboQueue. TB is always located at the very
-            * top of the video RAM. */
-           if (sisfb_heap_size >= TURBO_QUEUE_AREA_SIZE) {
-               unsigned int  tqueue_pos;
-               u8 tq_state;
 
-               tqueue_pos = (ivideo.video_size -
-                      TURBO_QUEUE_AREA_SIZE) / (64 * 1024);
+        }
 
-               temp = (u8) (tqueue_pos & 0xff);
+#else
 
-               inSISIDXREG(SISSR, IND_SIS_TURBOQUEUE_SET, tq_state);
-               tq_state |= 0xf0;
-               tq_state &= 0xfc;
-               tq_state |= (u8) (tqueue_pos >> 8);
-               outSISIDXREG(SISSR, IND_SIS_TURBOQUEUE_SET, tq_state);
+       pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &temp);
+       pci_write_config_dword(pdev, PCI_ROM_ADDRESS,
+                       (ivideo->video_base & PCI_ROM_ADDRESS_MASK) | PCI_ROM_ADDRESS_ENABLE);
 
-               outSISIDXREG(SISSR, IND_SIS_TURBOQUEUE_ADR, temp);
+       rom_base = ioremap(ivideo->video_base, 65536);
+       if(rom_base) {
+               if(sisfb_check_rom(rom_base, ivideo)) {
+                       if((myrombase = vmalloc(65536)))
+                               memcpy_fromio(myrombase, rom_base, 65536);
+               }
+               iounmap(rom_base);
+       }
 
-               sisfb_caps |= TURBO_QUEUE_CAP;
+       pci_write_config_dword(pdev, PCI_ROM_ADDRESS, temp);
 
-               sisfb_heap_end -= TURBO_QUEUE_AREA_SIZE;
-               sisfb_heap_size -= TURBO_QUEUE_AREA_SIZE;
-               DPRINTK("sisfb: TurboQueue start at 0x%lx, size is %dK\n",
-                       sisfb_heap_end, TURBO_QUEUE_AREA_SIZE/1024);
-           }
-     }
 #endif
-     /* Now reserve memory for the HWCursor. It is always located at the very
-        top of the videoRAM, right below the TB memory area (if used). */
-     if (sisfb_heap_size >= sisfb_hwcursor_size) {
-               sisfb_heap_end -= sisfb_hwcursor_size;
-               sisfb_heap_size -= sisfb_hwcursor_size;
-               sisfb_hwcursor_vbase = sisfb_heap_end;
-
-               sisfb_caps |= HW_CURSOR_CAP;
-
-               DPRINTK("sisfb: Hardware Cursor start at 0x%lx, size is %dK\n",
-                       sisfb_heap_end, sisfb_hwcursor_size/1024);
-     }
 
-     sisfb_heap.poha_chain = NULL;
-     sisfb_heap.poh_freelist = NULL;
-
-     poh = sisfb_poh_new_node();
+       return myrombase;
+}
 
-     if(poh == NULL)  return 1;
-       
-     poh->poh_next = &sisfb_heap.oh_free;
-     poh->poh_prev = &sisfb_heap.oh_free;
-     poh->size = sisfb_heap_end - sisfb_heap_start + 1;
-     poh->offset = sisfb_heap_start - (unsigned long) ivideo.video_vbase;
+static void __devinit
+sisfb_post_map_vram(struct sis_video_info *ivideo, unsigned int *mapsize,
+                       unsigned int min)
+{
+       ivideo->video_vbase = ioremap(ivideo->video_base, (*mapsize));
+
+       if(!ivideo->video_vbase) {
+               printk(KERN_ERR
+                       "sisfb: Unable to map maximum video RAM for size detection\n");
+               (*mapsize) >>= 1;
+               while((!(ivideo->video_vbase = ioremap(ivideo->video_base, (*mapsize))))) {
+                       (*mapsize) >>= 1;
+                       if((*mapsize) < (min << 20))
+                               break;
+               }
+               if(ivideo->video_vbase) {
+                       printk(KERN_ERR
+                               "sisfb: Video RAM size detection limited to %dMB\n",
+                               (int)((*mapsize) >> 20));
+               }
+       }
+}
 
-     DPRINTK("sisfb: Heap start:0x%p, end:0x%p, len=%dk\n",
-               (char *) sisfb_heap_start, (char *) sisfb_heap_end,
-               (unsigned int) poh->size / 1024);
+#ifdef CONFIG_FB_SIS_300
+static int __devinit
+sisfb_post_300_buswidth(struct sis_video_info *ivideo)
+{
+       SIS_IOTYPE1 *FBAddress = ivideo->video_vbase;
+       unsigned short temp;
+       unsigned char reg;
+       int i, j;
+
+       andSISIDXREG(SISSR, 0x15, 0xFB);
+       orSISIDXREG(SISSR, 0x15, 0x04);
+       outSISIDXREG(SISSR, 0x13, 0x00);
+       outSISIDXREG(SISSR, 0x14, 0xBF);
+
+       for(i = 0; i < 2; i++) {
+               temp = 0x1234;
+               for(j = 0; j < 4; j++) {
+                       writew(temp, FBAddress);
+                       if(readw(FBAddress) == temp)
+                               break;
+                       orSISIDXREG(SISSR, 0x3c, 0x01);
+                       inSISIDXREG(SISSR, 0x05, reg);
+                       inSISIDXREG(SISSR, 0x05, reg);
+                       andSISIDXREG(SISSR, 0x3c, 0xfe);
+                       inSISIDXREG(SISSR, 0x05, reg);
+                       inSISIDXREG(SISSR, 0x05, reg);
+                       temp++;
+               }
+       }
 
-     DPRINTK("sisfb: First Node offset:0x%x, size:%dk\n",
-               (unsigned int) poh->offset, (unsigned int) poh->size / 1024);
+       writel(0x01234567L, FBAddress);
+       writel(0x456789ABL, (FBAddress + 4));
+       writel(0x89ABCDEFL, (FBAddress + 8));
+       writel(0xCDEF0123L, (FBAddress + 12));
 
-     sisfb_heap.oh_free.poh_next = poh;
-     sisfb_heap.oh_free.poh_prev = poh;
-     sisfb_heap.oh_free.size = 0;
-     sisfb_heap.max_freesize = poh->size;
+       inSISIDXREG(SISSR, 0x3b, reg);
+       if(reg & 0x01) {
+               if(readl((FBAddress + 12)) == 0xCDEF0123L)
+                       return 4;       /* Channel A 128bit */
+       }
 
-     sisfb_heap.oh_used.poh_next = &sisfb_heap.oh_used;
-     sisfb_heap.oh_used.poh_prev = &sisfb_heap.oh_used;
-     sisfb_heap.oh_used.size = SENTINEL;
+       if(readl((FBAddress + 4)) == 0x456789ABL)
+               return 2;               /* Channel B 64bit */
 
-     return 0;
+       return 1;                       /* 32bit */
 }
 
-static SIS_OH *sisfb_poh_new_node(void)
+static int __devinit
+sisfb_post_300_rwtest(struct sis_video_info *ivideo, int iteration, int buswidth,
+                       int PseudoRankCapacity, int PseudoAdrPinCount,
+                       unsigned int mapsize)
 {
-       int           i;
-       unsigned long cOhs;
-       SIS_OHALLOC   *poha;
-       SIS_OH        *poh;
-
-       if (sisfb_heap.poh_freelist == NULL) {
-               poha = kmalloc(OH_ALLOC_SIZE, GFP_KERNEL);
-               if(!poha) return NULL;
-
-               poha->poha_next = sisfb_heap.poha_chain;
-               sisfb_heap.poha_chain = poha;
-
-               cOhs = (OH_ALLOC_SIZE - sizeof(SIS_OHALLOC)) / sizeof(SIS_OH) + 1;
-
-               poh = &poha->aoh[0];
-               for (i = cOhs - 1; i != 0; i--) {
-                       poh->poh_next = poh + 1;
-                       poh = poh + 1;
+       SIS_IOTYPE1 *FBAddr = ivideo->video_vbase;
+       unsigned short sr14;
+       unsigned int k, RankCapacity, PageCapacity, BankNumHigh, BankNumMid;
+       unsigned int PhysicalAdrOtherPage, PhysicalAdrHigh, PhysicalAdrHalfPage;
+       static const unsigned short SiS_DRAMType[17][5] = {
+               {0x0C,0x0A,0x02,0x40,0x39},
+               {0x0D,0x0A,0x01,0x40,0x48},
+               {0x0C,0x09,0x02,0x20,0x35},
+               {0x0D,0x09,0x01,0x20,0x44},
+               {0x0C,0x08,0x02,0x10,0x31},
+               {0x0D,0x08,0x01,0x10,0x40},
+               {0x0C,0x0A,0x01,0x20,0x34},
+               {0x0C,0x09,0x01,0x08,0x32},
+               {0x0B,0x08,0x02,0x08,0x21},
+               {0x0C,0x08,0x01,0x08,0x30},
+               {0x0A,0x08,0x02,0x04,0x11},
+               {0x0B,0x0A,0x01,0x10,0x28},
+               {0x09,0x08,0x02,0x02,0x01},
+               {0x0B,0x09,0x01,0x08,0x24},
+               {0x0B,0x08,0x01,0x04,0x20},
+               {0x0A,0x08,0x01,0x02,0x10},
+               {0x09,0x08,0x01,0x01,0x00}
+       };
+
+        for(k = 0; k <= 16; k++) {
+
+               RankCapacity = buswidth * SiS_DRAMType[k][3];
+
+               if(RankCapacity != PseudoRankCapacity)
+                       continue;
+
+               if((SiS_DRAMType[k][2] + SiS_DRAMType[k][0]) > PseudoAdrPinCount)
+                       continue;
+
+               BankNumHigh = RankCapacity * 16 * iteration - 1;
+               if(iteration == 3) {             /* Rank No */
+                       BankNumMid  = RankCapacity * 16 - 1;
+               } else {
+                       BankNumMid  = RankCapacity * 16 * iteration / 2 - 1;
                }
 
-               poh->poh_next = NULL;
-               sisfb_heap.poh_freelist = &poha->aoh[0];
+               PageCapacity = (1 << SiS_DRAMType[k][1]) * buswidth * 4;
+               PhysicalAdrHigh = BankNumHigh;
+               PhysicalAdrHalfPage = (PageCapacity / 2 + PhysicalAdrHigh) % PageCapacity;
+               PhysicalAdrOtherPage = PageCapacity * SiS_DRAMType[k][2] + PhysicalAdrHigh;
+
+               andSISIDXREG(SISSR, 0x15, 0xFB); /* Test */
+               orSISIDXREG(SISSR, 0x15, 0x04);  /* Test */
+               sr14 = (SiS_DRAMType[k][3] * buswidth) - 1;
+               if(buswidth == 4)      sr14 |= 0x80;
+               else if(buswidth == 2) sr14 |= 0x40;
+               outSISIDXREG(SISSR, 0x13, SiS_DRAMType[k][4]);
+               outSISIDXREG(SISSR, 0x14, sr14);
+
+               BankNumHigh <<= 16;
+               BankNumMid <<= 16;
+
+               if((BankNumHigh + PhysicalAdrHigh      >= mapsize) ||
+                  (BankNumMid  + PhysicalAdrHigh      >= mapsize) ||
+                  (BankNumHigh + PhysicalAdrHalfPage  >= mapsize) ||
+                  (BankNumHigh + PhysicalAdrOtherPage >= mapsize))
+                       continue;
+
+               /* Write data */
+               writew(((unsigned short)PhysicalAdrHigh),
+                               (FBAddr + BankNumHigh + PhysicalAdrHigh));
+               writew(((unsigned short)BankNumMid),
+                               (FBAddr + BankNumMid  + PhysicalAdrHigh));
+               writew(((unsigned short)PhysicalAdrHalfPage),
+                               (FBAddr + BankNumHigh + PhysicalAdrHalfPage));
+               writew(((unsigned short)PhysicalAdrOtherPage),
+                               (FBAddr + BankNumHigh + PhysicalAdrOtherPage));
+
+               /* Read data */
+               if(readw(FBAddr + BankNumHigh + PhysicalAdrHigh) == PhysicalAdrHigh)
+                       return 1;
        }
 
-       poh = sisfb_heap.poh_freelist;
-       sisfb_heap.poh_freelist = poh->poh_next;
+       return 0;
+}
 
-       return (poh);
+static void __devinit
+sisfb_post_300_ramsize(struct pci_dev *pdev, unsigned int mapsize)
+{
+       struct  sis_video_info *ivideo = pci_get_drvdata(pdev);
+       int     i, j, buswidth;
+       int     PseudoRankCapacity, PseudoAdrPinCount;
+
+       buswidth = sisfb_post_300_buswidth(ivideo);
+
+       for(i = 6; i >= 0; i--) {
+               PseudoRankCapacity = 1 << i;
+               for(j = 4; j >= 1; j--) {
+                       PseudoAdrPinCount = 15 - j;
+                       if((PseudoRankCapacity * j) <= 64) {
+                               if(sisfb_post_300_rwtest(ivideo,
+                                               j,
+                                               buswidth,
+                                               PseudoRankCapacity,
+                                               PseudoAdrPinCount,
+                                               mapsize))
+                                       return;
+                       }
+               }
+       }
 }
 
-static SIS_OH *sisfb_poh_allocate(unsigned long size)
+static void __devinit
+sisfb_post_sis300(struct pci_dev *pdev)
 {
-       SIS_OH *pohThis;
-       SIS_OH *pohRoot;
-       int     bAllocated = 0;
+       struct sis_video_info *ivideo = pci_get_drvdata(pdev);
+       unsigned char *bios = ivideo->SiS_Pr.VirtualRomBase;
+       u8  reg, v1, v2, v3, v4, v5, v6, v7, v8;
+       u16 index, rindex, memtype = 0;
+       unsigned int mapsize;
 
-       if (size > sisfb_heap.max_freesize) {
-               DPRINTK("sisfb: Can't allocate %dk size on offscreen\n",
-                       (unsigned int) size / 1024);
-               return (NULL);
-       }
+       if(!ivideo->SiS_Pr.UseROM)
+               bios = NULL;
 
-       pohThis = sisfb_heap.oh_free.poh_next;
+       outSISIDXREG(SISSR, 0x05, 0x86);
 
-       while (pohThis != &sisfb_heap.oh_free) {
-               if (size <= pohThis->size) {
-                       bAllocated = 1;
-                       break;
+       if(bios) {
+               if(bios[0x52] & 0x80) {
+                       memtype = bios[0x52];
+               } else {
+                       inSISIDXREG(SISSR, 0x3a, memtype);
                }
-               pohThis = pohThis->poh_next;
+               memtype &= 0x07;
        }
 
-       if (!bAllocated) {
-               DPRINTK("sisfb: Can't allocate %dk size on offscreen\n",
-                       (unsigned int) size / 1024);
-               return (NULL);
+       v3 = 0x80; v6 = 0x80;
+       if(ivideo->revision_id <= 0x13) {
+               v1 = 0x44; v2 = 0x42;
+               v4 = 0x44; v5 = 0x42;
+       } else {
+               v1 = 0x68; v2 = 0x43; /* Assume 125Mhz MCLK */
+               v4 = 0x68; v5 = 0x43; /* Assume 125Mhz ECLK */
+               if(bios) {
+                       index = memtype * 5;
+                       rindex = index + 0x54;
+                       v1 = bios[rindex++];
+                       v2 = bios[rindex++];
+                       v3 = bios[rindex++];
+                       rindex = index + 0x7c;
+                       v4 = bios[rindex++];
+                       v5 = bios[rindex++];
+                       v6 = bios[rindex++];
+               }
        }
-
-       if (size == pohThis->size) {
-               pohRoot = pohThis;
-               sisfb_delete_node(pohThis);
+       outSISIDXREG(SISSR, 0x28, v1);
+       outSISIDXREG(SISSR, 0x29, v2);
+       outSISIDXREG(SISSR, 0x2a, v3);
+       outSISIDXREG(SISSR, 0x2e, v4);
+       outSISIDXREG(SISSR, 0x2f, v5);
+       outSISIDXREG(SISSR, 0x30, v6);
+
+       v1 = 0x10;
+       if(bios)
+               v1 = bios[0xa4];
+       outSISIDXREG(SISSR, 0x07, v1);       /* DAC speed */
+
+       outSISIDXREG(SISSR, 0x11, 0x0f);     /* DDC, power save */
+
+       v1 = 0x01; v2 = 0x43; v3 = 0x1e; v4 = 0x2a;
+       v5 = 0x06; v6 = 0x00; v7 = 0x00; v8 = 0x00;
+       if(bios) {
+               memtype += 0xa5;
+               v1 = bios[memtype];
+               v2 = bios[memtype + 8];
+               v3 = bios[memtype + 16];
+               v4 = bios[memtype + 24];
+               v5 = bios[memtype + 32];
+               v6 = bios[memtype + 40];
+               v7 = bios[memtype + 48];
+               v8 = bios[memtype + 56];
+       }
+       if(ivideo->revision_id >= 0x80)
+               v3 &= 0xfd;
+       outSISIDXREG(SISSR, 0x15, v1);       /* Ram type (assuming 0, BIOS 0xa5 step 8) */
+       outSISIDXREG(SISSR, 0x16, v2);
+       outSISIDXREG(SISSR, 0x17, v3);
+       outSISIDXREG(SISSR, 0x18, v4);
+       outSISIDXREG(SISSR, 0x19, v5);
+       outSISIDXREG(SISSR, 0x1a, v6);
+       outSISIDXREG(SISSR, 0x1b, v7);
+       outSISIDXREG(SISSR, 0x1c, v8);     /* ---- */
+       andSISIDXREG(SISSR, 0x15 ,0xfb);
+       orSISIDXREG(SISSR, 0x15, 0x04);
+       if(bios) {
+               if(bios[0x53] & 0x02) {
+                       orSISIDXREG(SISSR, 0x19, 0x20);
+               }
+       }
+       v1 = 0x04;                         /* DAC pedestal (BIOS 0xe5) */
+       if(ivideo->revision_id >= 0x80)
+               v1 |= 0x01;
+       outSISIDXREG(SISSR, 0x1f, v1);
+       outSISIDXREG(SISSR, 0x20, 0xa4);     /* linear & relocated io & disable a0000 */
+       v1 = 0xf6; v2 = 0x0d; v3 = 0x00;
+       if(bios) {
+               v1 = bios[0xe8];
+               v2 = bios[0xe9];
+               v3 = bios[0xea];
+       }
+       outSISIDXREG(SISSR, 0x23, v1);
+       outSISIDXREG(SISSR, 0x24, v2);
+       outSISIDXREG(SISSR, 0x25, v3);
+       outSISIDXREG(SISSR, 0x21, 0x84);
+       outSISIDXREG(SISSR, 0x22, 0x00);
+       outSISIDXREG(SISCR, 0x37, 0x00);
+       orSISIDXREG(SISPART1, 0x24, 0x01);   /* unlock crt2 */
+       outSISIDXREG(SISPART1, 0x00, 0x00);
+       v1 = 0x40; v2 = 0x11;
+       if(bios) {
+               v1 = bios[0xec];
+               v2 = bios[0xeb];
+       }
+       outSISIDXREG(SISPART1, 0x02, v1);
+
+       if(ivideo->revision_id >= 0x80)
+               v2 &= ~0x01;
+
+       inSISIDXREG(SISPART4, 0x00, reg);
+       if((reg == 1) || (reg == 2)) {
+               outSISIDXREG(SISCR, 0x37, 0x02);
+               outSISIDXREG(SISPART2, 0x00, 0x1c);
+               v4 = 0x00; v5 = 0x00; v6 = 0x10;
+               if(ivideo->SiS_Pr.UseROM) {
+                       v4 = bios[0xf5];
+                       v5 = bios[0xf6];
+                       v6 = bios[0xf7];
+               }
+               outSISIDXREG(SISPART4, 0x0d, v4);
+               outSISIDXREG(SISPART4, 0x0e, v5);
+               outSISIDXREG(SISPART4, 0x10, v6);
+               outSISIDXREG(SISPART4, 0x0f, 0x3f);
+               inSISIDXREG(SISPART4, 0x01, reg);
+               if(reg >= 0xb0) {
+                       inSISIDXREG(SISPART4, 0x23, reg);
+                       reg &= 0x20;
+                       reg <<= 1;
+                       outSISIDXREG(SISPART4, 0x23, reg);
+               }
        } else {
-               pohRoot = sisfb_poh_new_node();
+               v2 &= ~0x10;
+       }
+       outSISIDXREG(SISSR, 0x32, v2);
 
-               if (pohRoot == NULL) {
-                       return (NULL);
-               }
+       andSISIDXREG(SISPART1, 0x24, 0xfe);  /* Lock CRT2 */
 
-               pohRoot->offset = pohThis->offset;
-               pohRoot->size = size;
+       inSISIDXREG(SISSR, 0x16, reg);
+       reg &= 0xc3;
+       outSISIDXREG(SISCR, 0x35, reg);
+       outSISIDXREG(SISCR, 0x83, 0x00);
+#if !defined(__i386__) && !defined(__x86_64__)
+       if(sisfb_videoram) {
+               outSISIDXREG(SISSR, 0x13, 0x28);  /* ? */
+               reg = ((sisfb_videoram >> 10) - 1) | 0x40;
+               outSISIDXREG(SISSR, 0x14, reg);
+       } else {
+#endif
+               /* Need to map max FB size for finding out about RAM size */
+               mapsize = 64 << 20;
+               sisfb_post_map_vram(ivideo, &mapsize, 4);
 
-               pohThis->offset += size;
-               pohThis->size -= size;
+               if(ivideo->video_vbase) {
+                       sisfb_post_300_ramsize(pdev, mapsize);
+                       iounmap(ivideo->video_vbase);
+               } else {
+                       printk(KERN_DEBUG
+                               "sisfb: Failed to map memory for size detection, assuming 8MB\n");
+                       outSISIDXREG(SISSR, 0x13, 0x28);  /* ? */
+                       outSISIDXREG(SISSR, 0x14, 0x47);  /* 8MB, 64bit default */
+               }
+#if !defined(__i386__) && !defined(__x86_64__)
        }
+#endif
+       if(bios) {
+               v1 = bios[0xe6];
+               v2 = bios[0xe7];
+       } else {
+               inSISIDXREG(SISSR, 0x3a, reg);
+               if((reg & 0x30) == 0x30) {
+                       v1 = 0x04; /* PCI */
+                       v2 = 0x92;
+               } else {
+                       v1 = 0x14; /* AGP */
+                       v2 = 0xb2;
+               }
+       }
+       outSISIDXREG(SISSR, 0x21, v1);
+       outSISIDXREG(SISSR, 0x22, v2);
 
-       sisfb_heap.max_freesize -= size;
+       /* Sense CRT1 */
+       sisfb_sense_crt1(ivideo);
 
-       pohThis = &sisfb_heap.oh_used;
-       sisfb_insert_node(pohThis, pohRoot);
+       /* Set default mode, don't clear screen */
+       ivideo->SiS_Pr.SiS_UseOEM = FALSE;
+       SiS_SetEnableDstn(&ivideo->SiS_Pr, FALSE);
+       SiS_SetEnableFstn(&ivideo->SiS_Pr, FALSE);
+       ivideo->curFSTN = ivideo->curDSTN = 0;
+       ivideo->SiS_Pr.VideoMemorySize = 8 << 20;
+       SiSSetMode(&ivideo->SiS_Pr, 0x2e | 0x80);
 
-       return (pohRoot);
-}
+       outSISIDXREG(SISSR, 0x05, 0x86);
 
-static void sisfb_delete_node(SIS_OH *poh)
-{
-       SIS_OH *poh_prev;
-       SIS_OH *poh_next;
+       /* Display off */
+       orSISIDXREG(SISSR, 0x01, 0x20);
 
-       poh_prev = poh->poh_prev;
-       poh_next = poh->poh_next;
+       /* Save mode number in CR34 */
+       outSISIDXREG(SISCR, 0x34, 0x2e);
 
-       poh_prev->poh_next = poh_next;
-       poh_next->poh_prev = poh_prev;
+       /* Let everyone know what the current mode is */
+       ivideo->modeprechange = 0x2e;
+}
+#endif
 
+#ifdef CONFIG_FB_SIS_315
+#if 0
+static void __devinit
+sisfb_post_sis315330(struct pci_dev *pdev)
+{
+       /* TODO */
 }
+#endif
 
-static void sisfb_insert_node(SIS_OH *pohList, SIS_OH *poh)
+static void __devinit
+sisfb_post_xgi_delay(struct sis_video_info *ivideo, int delay)
 {
-       SIS_OH *pohTemp;
+       unsigned int i;
+       u8 reg;
 
-       pohTemp = pohList->poh_next;
+       for(i = 0; i <= (delay * 10 * 36); i++) {
+               inSISIDXREG(SISSR, 0x05, reg);
+               reg++;
+       }
+}
 
-       pohList->poh_next = poh;
-       pohTemp->poh_prev = poh;
+static int __devinit
+sisfb_find_host_bridge(struct sis_video_info *ivideo, struct pci_dev *mypdev,
+                               unsigned short pcivendor)
+{
+       struct pci_dev *pdev = NULL;
+       unsigned short temp;
+       int ret = 0;
+
+       while((pdev = SIS_PCI_GET_CLASS(PCI_CLASS_BRIDGE_HOST, pdev))) {
+               temp = pdev->vendor;
+               SIS_PCI_PUT_DEVICE(pdev);
+               if(temp == pcivendor) {
+                       ret = 1;
+                       break;
+               }
+       }
 
-       poh->poh_prev = pohList;
-       poh->poh_next = pohTemp;
+       return ret;
 }
 
-static SIS_OH *sisfb_poh_free(unsigned long base)
+static int __devinit
+sisfb_post_xgi_rwtest(struct sis_video_info *ivideo, int starta,
+                       unsigned int enda, unsigned int mapsize)
 {
-       SIS_OH *pohThis;
-       SIS_OH *poh_freed;
-       SIS_OH *poh_prev;
-       SIS_OH *poh_next;
-       unsigned long ulUpper;
-       unsigned long ulLower;
-       int foundNode = 0;
+       unsigned int pos;
+       int i;
 
-       poh_freed = sisfb_heap.oh_used.poh_next;
+       writel(0, ivideo->video_vbase);
 
-       while(poh_freed != &sisfb_heap.oh_used) {
-               if(poh_freed->offset == base) {
-                       foundNode = 1;
-                       break;
-               }
+       for(i = starta; i <= enda; i++) {
+               pos = 1 << i;
+               if(pos < mapsize)
+                       writel(pos, ivideo->video_vbase + pos);
+       }
 
-               poh_freed = poh_freed->poh_next;
+       sisfb_post_xgi_delay(ivideo, 150);
+
+       if(readl(ivideo->video_vbase) != 0)
+               return 0;
+
+       for(i = starta; i <= enda; i++) {
+               pos = 1 << i;
+               if(pos < mapsize) {
+                       if(readl(ivideo->video_vbase + pos) != pos)
+                               return 0;
+               } else
+                       return 0;
        }
 
-       if (!foundNode)  return (NULL);
+       return 1;
+}
 
-       sisfb_heap.max_freesize += poh_freed->size;
+static void __devinit
+sisfb_post_xgi_ramsize(struct sis_video_info *ivideo)
+{
+       unsigned int buswidth, ranksize, channelab, mapsize;
+       int i, j, k, l;
+       u8 reg, sr14;
+       static const u8 dramsr13[12 * 5] = {
+               0x02, 0x0e, 0x0b, 0x80, 0x5d,
+               0x02, 0x0e, 0x0a, 0x40, 0x59,
+               0x02, 0x0d, 0x0b, 0x40, 0x4d,
+               0x02, 0x0e, 0x09, 0x20, 0x55,
+               0x02, 0x0d, 0x0a, 0x20, 0x49,
+               0x02, 0x0c, 0x0b, 0x20, 0x3d,
+               0x02, 0x0e, 0x08, 0x10, 0x51,
+               0x02, 0x0d, 0x09, 0x10, 0x45,
+               0x02, 0x0c, 0x0a, 0x10, 0x39,
+               0x02, 0x0d, 0x08, 0x08, 0x41,
+               0x02, 0x0c, 0x09, 0x08, 0x35,
+               0x02, 0x0c, 0x08, 0x04, 0x31
+       };
+       static const u8 dramsr13_4[4 * 5] = {
+               0x02, 0x0d, 0x09, 0x40, 0x45,
+               0x02, 0x0c, 0x09, 0x20, 0x35,
+               0x02, 0x0c, 0x08, 0x10, 0x31,
+               0x02, 0x0b, 0x08, 0x08, 0x21
+       };
+
+       /* Enable linear mode, disable 0xa0000 address decoding */
+       /* We disable a0000 address decoding, because
+        * - if running on x86, if the card is disabled, it means
+        *   that another card is in the system. We don't want
+        *   to interphere with that primary card's textmode.
+        * - if running on non-x86, there usually is no VGA window
+        *   at a0000.
+        */
+       orSISIDXREG(SISSR, 0x20, (0x80 | 0x04));
 
-       poh_prev = poh_next = NULL;
-       ulUpper = poh_freed->offset + poh_freed->size;
-       ulLower = poh_freed->offset;
+       /* Need to map max FB size for finding out about RAM size */
+       mapsize = 256 << 20;
+       sisfb_post_map_vram(ivideo, &mapsize, 32);
 
-       pohThis = sisfb_heap.oh_free.poh_next;
+       if(!ivideo->video_vbase) {
+               printk(KERN_ERR "sisfb: Unable to detect RAM size. Setting default.\n");
+               outSISIDXREG(SISSR, 0x13, 0x35);
+               outSISIDXREG(SISSR, 0x14, 0x41);
+               /* TODO */
+               return;
+       }
 
-       while (pohThis != &sisfb_heap.oh_free) {
-               if (pohThis->offset == ulUpper) {
-                       poh_next = pohThis;
+       /* Non-interleaving */
+       outSISIDXREG(SISSR, 0x15, 0x00);
+       /* No tiling */
+       outSISIDXREG(SISSR, 0x1c, 0x00);
+
+       if(ivideo->chip == XGI_20) {
+
+               channelab = 1;
+               inSISIDXREG(SISCR, 0x97, reg);
+               if(!(reg & 0x01)) {     /* Single 32/16 */
+                       buswidth = 32;
+                       outSISIDXREG(SISSR, 0x13, 0xb1);
+                       outSISIDXREG(SISSR, 0x14, 0x52);
+                       sisfb_post_xgi_delay(ivideo, 1);
+                       sr14 = 0x02;
+                       if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize))
+                               goto bail_out;
+
+                       outSISIDXREG(SISSR, 0x13, 0x31);
+                       outSISIDXREG(SISSR, 0x14, 0x42);
+                       sisfb_post_xgi_delay(ivideo, 1);
+                       if(sisfb_post_xgi_rwtest(ivideo, 23, 23, mapsize))
+                               goto bail_out;
+
+                       buswidth = 16;
+                       outSISIDXREG(SISSR, 0x13, 0xb1);
+                       outSISIDXREG(SISSR, 0x14, 0x41);
+                       sisfb_post_xgi_delay(ivideo, 1);
+                       sr14 = 0x01;
+                       if(sisfb_post_xgi_rwtest(ivideo, 22, 23, mapsize))
+                               goto bail_out;
+                       else
+                               outSISIDXREG(SISSR, 0x13, 0x31);
+               } else {                /* Dual 16/8 */
+                       buswidth = 16;
+                       outSISIDXREG(SISSR, 0x13, 0xb1);
+                       outSISIDXREG(SISSR, 0x14, 0x41);
+                       sisfb_post_xgi_delay(ivideo, 1);
+                       sr14 = 0x01;
+                       if(sisfb_post_xgi_rwtest(ivideo, 22, 23, mapsize))
+                               goto bail_out;
+
+                       outSISIDXREG(SISSR, 0x13, 0x31);
+                       outSISIDXREG(SISSR, 0x14, 0x31);
+                       sisfb_post_xgi_delay(ivideo, 1);
+                       if(sisfb_post_xgi_rwtest(ivideo, 22, 22, mapsize))
+                               goto bail_out;
+
+                       buswidth = 8;
+                       outSISIDXREG(SISSR, 0x13, 0xb1);
+                       outSISIDXREG(SISSR, 0x14, 0x30);
+                       sisfb_post_xgi_delay(ivideo, 1);
+                       sr14 = 0x00;
+                       if(sisfb_post_xgi_rwtest(ivideo, 21, 22, mapsize))
+                               goto bail_out;
+                       else
+                               outSISIDXREG(SISSR, 0x13, 0x31);
                }
-                       else if ((pohThis->offset + pohThis->size) ==
-                                ulLower) {
-                       poh_prev = pohThis;
+
+       } else {        /* XGI_40 */
+
+               inSISIDXREG(SISCR, 0x97, reg);
+               if(!(reg & 0x10)) {
+                       inSISIDXREG(SISSR, 0x39, reg);
+                       reg >>= 1;
                }
-               pohThis = pohThis->poh_next;
-       }
 
-       sisfb_delete_node(poh_freed);
+               if(reg & 0x01) {        /* DDRII */
+                       buswidth = 32;
+                       if(ivideo->revision_id == 2) {
+                               channelab = 2;
+                               outSISIDXREG(SISSR, 0x13, 0xa1);
+                               outSISIDXREG(SISSR, 0x14, 0x44);
+                               sr14 = 0x04;
+                               sisfb_post_xgi_delay(ivideo, 1);
+                               if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize))
+                                       goto bail_out;
+
+                               outSISIDXREG(SISSR, 0x13, 0x21);
+                               outSISIDXREG(SISSR, 0x14, 0x34);
+                               if(sisfb_post_xgi_rwtest(ivideo, 22, 23, mapsize))
+                                       goto bail_out;
+
+                               channelab = 1;
+                               outSISIDXREG(SISSR, 0x13, 0xa1);
+                               outSISIDXREG(SISSR, 0x14, 0x40);
+                               sr14 = 0x00;
+                               if(sisfb_post_xgi_rwtest(ivideo, 22, 23, mapsize))
+                                       goto bail_out;
+
+                               outSISIDXREG(SISSR, 0x13, 0x21);
+                               outSISIDXREG(SISSR, 0x14, 0x30);
+                       } else {
+                               channelab = 3;
+                               outSISIDXREG(SISSR, 0x13, 0xa1);
+                               outSISIDXREG(SISSR, 0x14, 0x4c);
+                               sr14 = 0x0c;
+                               sisfb_post_xgi_delay(ivideo, 1);
+                               if(sisfb_post_xgi_rwtest(ivideo, 23, 25, mapsize))
+                                       goto bail_out;
+
+                               channelab = 2;
+                               outSISIDXREG(SISSR, 0x14, 0x48);
+                               sisfb_post_xgi_delay(ivideo, 1);
+                               sr14 = 0x08;
+                               if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize))
+                                       goto bail_out;
+
+                               outSISIDXREG(SISSR, 0x13, 0x21);
+                               outSISIDXREG(SISSR, 0x14, 0x3c);
+                               sr14 = 0x0c;
+
+                               if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize)) {
+                                       channelab = 3;
+                               } else {
+                                       channelab = 2;
+                                       outSISIDXREG(SISSR, 0x14, 0x38);
+                                       sr14 = 0x08;
+                               }
+                       }
+                       sisfb_post_xgi_delay(ivideo, 1);
+
+               } else {        /* DDR */
+
+                       buswidth = 64;
+                       if(ivideo->revision_id == 2) {
+                               channelab = 1;
+                               outSISIDXREG(SISSR, 0x13, 0xa1);
+                               outSISIDXREG(SISSR, 0x14, 0x52);
+                               sisfb_post_xgi_delay(ivideo, 1);
+                               sr14 = 0x02;
+                               if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize))
+                                       goto bail_out;
+
+                               outSISIDXREG(SISSR, 0x13, 0x21);
+                               outSISIDXREG(SISSR, 0x14, 0x42);
+                       } else {
+                               channelab = 2;
+                               outSISIDXREG(SISSR, 0x13, 0xa1);
+                               outSISIDXREG(SISSR, 0x14, 0x5a);
+                               sisfb_post_xgi_delay(ivideo, 1);
+                               sr14 = 0x0a;
+                               if(sisfb_post_xgi_rwtest(ivideo, 24, 25, mapsize))
+                                       goto bail_out;
+
+                               outSISIDXREG(SISSR, 0x13, 0x21);
+                               outSISIDXREG(SISSR, 0x14, 0x4a);
+                       }
+                       sisfb_post_xgi_delay(ivideo, 1);
 
-       if (poh_prev && poh_next) {
-               poh_prev->size += (poh_freed->size + poh_next->size);
-               sisfb_delete_node(poh_next);
-               sisfb_free_node(poh_freed);
-               sisfb_free_node(poh_next);
-               return (poh_prev);
+               }
        }
 
-       if (poh_prev) {
-               poh_prev->size += poh_freed->size;
-               sisfb_free_node(poh_freed);
-               return (poh_prev);
-       }
+bail_out:
+       setSISIDXREG(SISSR, 0x14, 0xf0, sr14);
+       sisfb_post_xgi_delay(ivideo, 1);
 
-       if (poh_next) {
-               poh_next->size += poh_freed->size;
-               poh_next->offset = poh_freed->offset;
-               sisfb_free_node(poh_freed);
-               return (poh_next);
-       }
+       j = (ivideo->chip == XGI_20) ? 5 : 9;
+       k = (ivideo->chip == XGI_20) ? 12 : 4;
 
-       sisfb_insert_node(&sisfb_heap.oh_free, poh_freed);
+       for(i = 0; i < k; i++) {
 
-       return (poh_freed);
-}
+               reg = (ivideo->chip == XGI_20) ?
+                               dramsr13[(i * 5) + 4] : dramsr13_4[(i * 5) + 4];
+               setSISIDXREG(SISSR, 0x13, 0x80, reg);
+               sisfb_post_xgi_delay(ivideo, 50);
 
-static void sisfb_free_node(SIS_OH *poh)
-{
-       if(poh == NULL) return;
+               ranksize = (ivideo->chip == XGI_20) ?
+                               dramsr13[(i * 5) + 3] : dramsr13_4[(i * 5) + 3];
 
-       poh->poh_next = sisfb_heap.poh_freelist;
-       sisfb_heap.poh_freelist = poh;
+               inSISIDXREG(SISSR, 0x13, reg);
+               if(reg & 0x80) ranksize <<= 1;
 
-}
+               if(ivideo->chip == XGI_20) {
+                       if(buswidth == 16)      ranksize <<= 1;
+                       else if(buswidth == 32) ranksize <<= 2;
+               } else {
+                       if(buswidth == 64)      ranksize <<= 1;
+               }
 
-void sis_malloc(struct sis_memreq *req)
-{
-       SIS_OH *poh;
+               reg = 0;
+               l = channelab;
+               if(l == 3) l = 4;
+               if((ranksize * l) <= 256) {
+                       while((ranksize >>= 1)) reg += 0x10;
+               }
 
-       poh = sisfb_poh_allocate(req->size);
+               if(!reg) continue;
 
-       if(poh == NULL) {
-               req->offset = 0;
-               req->size = 0;
-               DPRINTK("sisfb: Video RAM allocation failed\n");
-       } else {
-               DPRINTK("sisfb: Video RAM allocation succeeded: 0x%p\n",
-                       (char *) (poh->offset + (unsigned long) ivideo.video_vbase));
+               setSISIDXREG(SISSR, 0x14, 0x0f, (reg & 0xf0));
+               sisfb_post_xgi_delay(ivideo, 1);
 
-               req->offset = poh->offset;
-               req->size = poh->size;
+               if(sisfb_post_xgi_rwtest(ivideo, j, ((reg >> 4) + channelab - 2 + 20), mapsize))
+                       break;
        }
+
+       iounmap(ivideo->video_vbase);
 }
 
-void sis_free(unsigned long base)
+static void __devinit
+sisfb_post_xgi_setclocks(struct sis_video_info *ivideo, u8 regb)
 {
-       SIS_OH *poh;
+       u8 v1, v2, v3;
+       int index;
+       static const u8 cs90[8 * 3] = {
+               0x16, 0x01, 0x01,
+               0x3e, 0x03, 0x01,
+               0x7c, 0x08, 0x01,
+               0x79, 0x06, 0x01,
+               0x29, 0x01, 0x81,
+               0x5c, 0x23, 0x01,
+               0x5c, 0x23, 0x01,
+               0x5c, 0x23, 0x01
+       };
+       static const u8 csb8[8 * 3] = {
+               0x5c, 0x23, 0x01,
+               0x29, 0x01, 0x01,
+               0x7c, 0x08, 0x01,
+               0x79, 0x06, 0x01,
+               0x29, 0x01, 0x81,
+               0x5c, 0x23, 0x01,
+               0x5c, 0x23, 0x01,
+               0x5c, 0x23, 0x01
+       };
+
+       regb = 0;  /* ! */
+
+       index = regb * 3;
+       v1 = cs90[index]; v2 = cs90[index + 1]; v3 = cs90[index + 2];
+       if(ivideo->haveXGIROM) {
+               v1 = ivideo->bios_abase[0x90 + index];
+               v2 = ivideo->bios_abase[0x90 + index + 1];
+               v3 = ivideo->bios_abase[0x90 + index + 2];
+       }
+       outSISIDXREG(SISSR, 0x28, v1);
+       outSISIDXREG(SISSR, 0x29, v2);
+       outSISIDXREG(SISSR, 0x2a, v3);
+       sisfb_post_xgi_delay(ivideo, 0x43);
+       sisfb_post_xgi_delay(ivideo, 0x43);
+       sisfb_post_xgi_delay(ivideo, 0x43);
+       index = regb * 3;
+       v1 = csb8[index]; v2 = csb8[index + 1]; v3 = csb8[index + 2];
+       if(ivideo->haveXGIROM) {
+               v1 = ivideo->bios_abase[0xb8 + index];
+               v2 = ivideo->bios_abase[0xb8 + index + 1];
+               v3 = ivideo->bios_abase[0xb8 + index + 2];
+       }
+       outSISIDXREG(SISSR, 0x2e, v1);
+       outSISIDXREG(SISSR, 0x2f, v2);
+       outSISIDXREG(SISSR, 0x30, v3);
+       sisfb_post_xgi_delay(ivideo, 0x43);
+       sisfb_post_xgi_delay(ivideo, 0x43);
+       sisfb_post_xgi_delay(ivideo, 0x43);
+}
 
-       poh = sisfb_poh_free(base);
+static int __devinit
+sisfb_post_xgi(struct pci_dev *pdev)
+{
+       struct sis_video_info *ivideo = pci_get_drvdata(pdev);
+       unsigned char *bios = ivideo->bios_abase;
+       struct pci_dev *mypdev = NULL;
+       const u8 *ptr, *ptr2;
+       u8 v1, v2, v3, v4, v5, reg, ramtype;
+       u32 rega, regb, regd;
+       int i, j, k, index;
+       static const u8 cs78[3] = { 0xf6, 0x0d, 0x00 };
+       static const u8 cs76[2] = { 0xa3, 0xfb };
+       static const u8 cs7b[3] = { 0xc0, 0x11, 0x00 };
+       static const u8 cs158[8] = {
+               0x88, 0xaa, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00
+       };
+       static const u8 cs160[8] = {
+               0x44, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00
+       };
+       static const u8 cs168[8] = {
+               0x48, 0x78, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00
+       };
+       static const u8 cs128[3 * 8] = {
+               0x90, 0x28, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x77, 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x77, 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00
+       };
+       static const u8 cs148[2 * 8] = {
+               0x55, 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+       };
+       static const u8 cs31a[8 * 4] = {
+               0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+               0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+       };
+       static const u8 cs33a[8 * 4] = {
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+       };
+       static const u8 cs45a[8 * 2] = {
+               0x00, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+       };
+       static const u8 cs170[7 * 8] = {
+               0x54, 0x32, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x54, 0x43, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x0a, 0x05, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x44, 0x34, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x10, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x11, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x05, 0x05, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00
+       };
+       static const u8 cs1a8[3 * 8] = {
+               0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+       };
+       static const u8 cs100[2 * 8] = {
+               0xc4, 0x04, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0xc4, 0x04, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00
+       };
+
+       /* VGA enable */
+       reg = inSISREG(SISVGAENABLE) | 0x01;
+       outSISREG(SISVGAENABLE, reg);
+
+       /* Misc */
+       reg = inSISREG(SISMISCR) | 0x01;
+       outSISREG(SISMISCW, reg);
+
+       /* Unlock SR */
+       outSISIDXREG(SISSR, 0x05, 0x86);
+       inSISIDXREG(SISSR, 0x05, reg);
+       if(reg != 0xa1)
+               return 0;
 
-       if(poh == NULL) {
-               DPRINTK("sisfb: sisfb_poh_free() failed at base 0x%x\n",
-                       (unsigned int) base);
+       /* Clear some regs */
+       for(i = 0; i < 0x22; i++) {
+               if(0x06 + i == 0x20) continue;
+               outSISIDXREG(SISSR, 0x06 + i, 0x00);
+       }
+       for(i = 0; i < 0x0b; i++) {
+               outSISIDXREG(SISSR, 0x31 + i, 0x00);
+       }
+       for(i = 0; i < 0x10; i++) {
+               outSISIDXREG(SISCR, 0x30 + i, 0x00);
        }
-}
 
-/* --------------------- SetMode routines ------------------------- */
+       ptr = cs78;
+       if(ivideo->haveXGIROM) {
+               ptr = (const u8 *)&bios[0x78];
+       }
+       for(i = 0; i < 3; i++) {
+               outSISIDXREG(SISSR, 0x23 + i, ptr[i]);
+       }
 
-static void sisfb_pre_setmode(void)
-{
-       u8 cr30 = 0, cr31 = 0, cr33 = 0, cr35 = 0;
+       ptr = cs76;
+       if(ivideo->haveXGIROM) {
+               ptr = (const u8 *)&bios[0x76];
+       }
+       for(i = 0; i < 2; i++) {
+               outSISIDXREG(SISSR, 0x21 + i, ptr[i]);
+       }
 
-       ivideo.currentvbflags &= (VB_VIDEOBRIDGE | VB_DISPTYPE_DISP2);
+       v1 = 0x18; v2 = 0x00;
+       if(ivideo->haveXGIROM) {
+               v1 = bios[0x74];
+               v2 = bios[0x75];
+       }
+       outSISIDXREG(SISSR, 0x07, v1);
+       outSISIDXREG(SISSR, 0x11, 0x0f);
+       outSISIDXREG(SISSR, 0x1f, v2);
+       /* PCI linear mode, RelIO enabled, A0000 decoding disabled */
+       outSISIDXREG(SISSR, 0x20, 0x80 | 0x20 | 0x04);
+       outSISIDXREG(SISSR, 0x27, 0x74);
 
-       inSISIDXREG(SISCR, 0x31, cr31);
-       cr31 &= ~0x60;
-       cr31 |= 0x04;
+       ptr = cs7b;
+       if(ivideo->haveXGIROM) {
+               ptr = (const u8 *)&bios[0x7b];
+       }
+       for(i = 0; i < 3; i++) {
+               outSISIDXREG(SISSR, 0x31 + i, ptr[i]);
+       }
 
-       cr33 = sisfb_rate_idx & 0x0F;
+       if(ivideo->chip == XGI_40) {
+               if(ivideo->revision_id == 2) {
+                       setSISIDXREG(SISSR, 0x3b, 0x3f, 0xc0);
+               }
+               outSISIDXREG(SISCR, 0x7d, 0xfe);
+               outSISIDXREG(SISCR, 0x7e, 0x0f);
+       }
+       if(ivideo->revision_id == 0) {  /* 40 *and* 20? */
+               andSISIDXREG(SISCR, 0x58, 0xd7);
+               inSISIDXREG(SISCR, 0xcb, reg);
+               if(reg & 0x20) {
+                       setSISIDXREG(SISCR, 0x58, 0xd7, (reg & 0x10) ? 0x08 : 0x20); /* =0x28 Z7 ? */
+               }
+       }
 
-       SiS_SetEnableDstn(&SiS_Pr, FALSE);
-       SiS_SetEnableFstn(&SiS_Pr, FALSE);
+       reg = (ivideo->chip == XGI_40) ? 0x20 : 0x00;
+       setSISIDXREG(SISCR, 0x38, 0x1f, reg);
 
-       switch (ivideo.currentvbflags & VB_DISPTYPE_DISP2) {
-          case CRT2_TV:
-               ivideo.disp_state = DISPTYPE_TV;
-               if (ivideo.vbflags & TV_SVIDEO) {
-                       cr30 = (SIS_VB_OUTPUT_SVIDEO | SIS_SIMULTANEOUS_VIEW_ENABLE);
-                       ivideo.currentvbflags |= TV_SVIDEO;
-                       ivideo.TV_plug = TVPLUG_SVIDEO;
-               } else if (ivideo.vbflags & TV_AVIDEO) {
-                       cr30 = (SIS_VB_OUTPUT_COMPOSITE | SIS_SIMULTANEOUS_VIEW_ENABLE);
-                       ivideo.currentvbflags |= TV_AVIDEO;
-                       ivideo.TV_plug = TVPLUG_COMPOSITE;
-               } else if (ivideo.vbflags & TV_SCART) {
-                       cr30 = (SIS_VB_OUTPUT_SCART | SIS_SIMULTANEOUS_VIEW_ENABLE);
-                       ivideo.currentvbflags |= TV_SCART;
-                       ivideo.TV_plug = TVPLUG_SCART;
-               }
-               cr31 |= SIS_DRIVER_MODE;
-
-               if(!(ivideo.vbflags & TV_HIVISION)) {
-                       if (ivideo.vbflags & TV_PAL) {
-                               cr31 |= 0x01;
-                               cr35 |= 0x01;
-                               ivideo.currentvbflags |= TV_PAL;
-                               ivideo.TV_type = TVMODE_PAL;
-                       } else {
-                               cr31 &= ~0x01;
-                               cr35 &= ~0x01;
-                               ivideo.currentvbflags |= TV_NTSC;
-                               ivideo.TV_type = TVMODE_NTSC;
+       if(ivideo->chip == XGI_20) {
+               outSISIDXREG(SISSR, 0x36, 0x70);
+       } else {
+               outSISIDXREG(SISVID, 0x00, 0x86);
+               outSISIDXREG(SISVID, 0x32, 0x00);
+               outSISIDXREG(SISVID, 0x30, 0x00);
+               outSISIDXREG(SISVID, 0x32, 0x01);
+               outSISIDXREG(SISVID, 0x30, 0x00);
+               andSISIDXREG(SISVID, 0x2f, 0xdf);
+               andSISIDXREG(SISCAP, 0x00, 0x3f);
+
+               outSISIDXREG(SISPART1, 0x2f, 0x01);
+               outSISIDXREG(SISPART1, 0x00, 0x00);
+               outSISIDXREG(SISPART1, 0x02, bios[0x7e]);
+               outSISIDXREG(SISPART1, 0x2e, 0x08);
+               andSISIDXREG(SISPART1, 0x35, 0x7f);
+               andSISIDXREG(SISPART1, 0x50, 0xfe);
+
+               inSISIDXREG(SISPART4, 0x00, reg);
+               if(reg == 1 || reg == 2) {
+                       outSISIDXREG(SISPART2, 0x00, 0x1c);
+                       outSISIDXREG(SISPART4, 0x0d, bios[0x7f]);
+                       outSISIDXREG(SISPART4, 0x0e, bios[0x80]);
+                       outSISIDXREG(SISPART4, 0x10, bios[0x81]);
+                       andSISIDXREG(SISPART4, 0x0f, 0x3f);
+
+                       inSISIDXREG(SISPART4, 0x01, reg);
+                       if((reg & 0xf0) >= 0xb0) {
+                               inSISIDXREG(SISPART4, 0x23, reg);
+                               if(reg & 0x20) reg |= 0x40;
+                               outSISIDXREG(SISPART4, 0x23, reg);
+                               reg = (reg & 0x20) ? 0x02 : 0x00;
+                               setSISIDXREG(SISPART1, 0x1e, 0xfd, reg);
                        }
                }
-               break;
-          case CRT2_LCD:
-               ivideo.disp_state = DISPTYPE_LCD;
-               cr30  = (SIS_VB_OUTPUT_LCD | SIS_SIMULTANEOUS_VIEW_ENABLE);
-               cr31 |= SIS_DRIVER_MODE;
-               SiS_SetEnableDstn(&SiS_Pr, sisfb_dstn);
-               SiS_SetEnableFstn(&SiS_Pr, sisfb_fstn);
-               break;
-          case CRT2_VGA:
-               ivideo.disp_state = DISPTYPE_CRT2;
-               cr30 = (SIS_VB_OUTPUT_CRT2 | SIS_SIMULTANEOUS_VIEW_ENABLE);
-               cr31 |= SIS_DRIVER_MODE;
-               if(sisfb_nocrt2rate) {
-                       cr33 |= (sisbios_mode[sisfb_mode_idx].rate_idx << 4);
-               } else {
-                       cr33 |= ((sisfb_rate_idx & 0x0F) << 4);
+
+               v1 = bios[0x77];
+
+               inSISIDXREG(SISSR, 0x3b, reg);
+               if(reg & 0x02) {
+                       inSISIDXREG(SISSR, 0x3a, reg);
+                       v2 = (reg & 0x30) >> 3;
+                       if(!(v2 & 0x04)) v2 ^= 0x02;
+                       inSISIDXREG(SISSR, 0x39, reg);
+                       if(reg & 0x80) v2 |= 0x80;
+                       v2 |= 0x01;
+
+                       if((mypdev = SIS_PCI_GET_DEVICE(PCI_VENDOR_ID_SI, 0x0730, NULL))) {
+                               SIS_PCI_PUT_DEVICE(mypdev);
+                               if(((v2 & 0x06) == 2) || ((v2 & 0x06) == 4))
+                                       v2 &= 0xf9;
+                               v2 |= 0x08;
+                               v1 &= 0xfe;
+                       } else {
+                               mypdev = SIS_PCI_GET_DEVICE(PCI_VENDOR_ID_SI, 0x0735, NULL);
+                               if(!mypdev)
+                                       mypdev = SIS_PCI_GET_DEVICE(PCI_VENDOR_ID_SI, 0x0645, NULL);
+                               if(!mypdev)
+                                       mypdev = SIS_PCI_GET_DEVICE(PCI_VENDOR_ID_SI, 0x0650, NULL);
+                               if(mypdev) {
+                                       pci_read_config_dword(mypdev, 0x94, &regd);
+                                       regd &= 0xfffffeff;
+                                       pci_write_config_dword(mypdev, 0x94, regd);
+                                       v1 &= 0xfe;
+                                       SIS_PCI_PUT_DEVICE(mypdev);
+                               } else if(sisfb_find_host_bridge(ivideo, pdev, PCI_VENDOR_ID_SI)) {
+                                       v1 &= 0xfe;
+                               } else if(sisfb_find_host_bridge(ivideo, pdev, 0x1106) ||
+                                         sisfb_find_host_bridge(ivideo, pdev, 0x1022) ||
+                                         sisfb_find_host_bridge(ivideo, pdev, 0x700e) ||
+                                         sisfb_find_host_bridge(ivideo, pdev, 0x10de)) {
+                                       if((v2 & 0x06) == 4)
+                                               v2 ^= 0x06;
+                                       v2 |= 0x08;
+                               }
+                       }
+                       setSISIDXREG(SISCR, 0x5f, 0xf0, v2);
+               }
+               outSISIDXREG(SISSR, 0x22, v1);
+
+               if(ivideo->revision_id == 2) {
+                       inSISIDXREG(SISSR, 0x3b, v1);
+                       inSISIDXREG(SISSR, 0x3a, v2);
+                       regd = bios[0x90 + 3] | (bios[0x90 + 4] << 8);
+                       if( (!(v1 & 0x02)) && (v2 & 0x30) && (regd < 0xcf) )
+                               setSISIDXREG(SISCR, 0x5f, 0xf1, 0x01);
+
+                       if((mypdev = SIS_PCI_GET_DEVICE(0x10de, 0x01e0, NULL))) {
+                               /* TODO: set CR5f &0xf1 | 0x01 for version 6570
+                                * of nforce 2 ROM
+                                */
+                               if(0)
+                                       setSISIDXREG(SISCR, 0x5f, 0xf1, 0x01);
+                               SIS_PCI_PUT_DEVICE(mypdev);
+                       }
                }
-               break;
-          default:     /* disable CRT2 */
-               cr30 = 0x00;
-               cr31 |= (SIS_DRIVER_MODE | SIS_VB_OUTPUT_DISABLE);
-       }
 
-       if(ivideo.chip >= SIS_661) {
-          cr31 &= ~0x01;
-          /* Leave overscan bit alone */
-          setSISIDXREG(SISCR, 0x35, ~0x10, cr35);
-       }
-       outSISIDXREG(SISCR, IND_SIS_SCRATCH_REG_CR30, cr30);
-       outSISIDXREG(SISCR, IND_SIS_SCRATCH_REG_CR31, cr31);
-       outSISIDXREG(SISCR, IND_SIS_SCRATCH_REG_CR33, cr33);
+               v1 = 0x30;
+               inSISIDXREG(SISSR, 0x3b, reg);
+               inSISIDXREG(SISCR, 0x5f, v2);
+               if((!(reg & 0x02)) && (v2 & 0x0e))
+                       v1 |= 0x08;
+               outSISIDXREG(SISSR, 0x27, v1);
 
-#ifdef CONFIG_FB_SIS_315
-        if(sisvga_engine == SIS_315_VGA) {
-          /* Clear LCDA and PAL-N/M bits */
-          andSISIDXREG(SISCR,0x38,~0x03);
-          if(ivideo.chip < SIS_661) {
-             andSISIDXREG(SISCR,0x38,~0xc0);
-          }
+               if(bios[0x64] & 0x01) {
+                       setSISIDXREG(SISCR, 0x5f, 0xf0, bios[0x64]);
+               }
+
+               v1 = bios[0x4f7];
+               pci_read_config_dword(pdev, 0x50, &regd);
+               regd = (regd >> 20) & 0x0f;
+               if(regd == 1) {
+                       v1 &= 0xfc;
+                       orSISIDXREG(SISCR, 0x5f, 0x08);
+               }
+               outSISIDXREG(SISCR, 0x48, v1);
+
+               setSISIDXREG(SISCR, 0x47, 0x04, bios[0x4f6] & 0xfb);
+               setSISIDXREG(SISCR, 0x49, 0xf0, bios[0x4f8] & 0x0f);
+               setSISIDXREG(SISCR, 0x4a, 0x60, bios[0x4f9] & 0x9f);
+               setSISIDXREG(SISCR, 0x4b, 0x08, bios[0x4fa] & 0xf7);
+               setSISIDXREG(SISCR, 0x4c, 0x80, bios[0x4fb] & 0x7f);
+               outSISIDXREG(SISCR, 0x70, bios[0x4fc]);
+               setSISIDXREG(SISCR, 0x71, 0xf0, bios[0x4fd] & 0x0f);
+               outSISIDXREG(SISCR, 0x74, 0xd0);
+               setSISIDXREG(SISCR, 0x74, 0xcf, bios[0x4fe] & 0x30);
+               setSISIDXREG(SISCR, 0x75, 0xe0, bios[0x4ff] & 0x1f);
+               setSISIDXREG(SISCR, 0x76, 0xe0, bios[0x500] & 0x1f);
+               v1 = bios[0x501];
+               if((mypdev = SIS_PCI_GET_DEVICE(0x8086, 0x2530, NULL))) {
+                       v1 = 0xf0;
+                       SIS_PCI_PUT_DEVICE(mypdev);
+               }
+               outSISIDXREG(SISCR, 0x77, v1);
        }
-#endif
 
-       if(ivideo.accel) sisfb_syncaccel();
+       /* RAM type */
 
-       SiS_Pr.SiS_UseOEM = sisfb_useoem;
-}
+       regb = 0;       /* ! */
 
-static void sisfb_post_setmode(void)
-{
-       u8 reg;
-       BOOLEAN crt1isoff = FALSE;
-#ifdef CONFIG_FB_SIS_315
-       u8 reg1;
-#endif
-#ifdef CONFIG_FB_SIS_300
-       BOOLEAN doit = TRUE;
-#endif
-       /* We can't switch off CRT1 if bridge is in slave mode */
-       if(ivideo.vbflags & VB_VIDEOBRIDGE) {
-#ifdef CONFIG_FB_SIS_300
-               if(sisvga_engine == SIS_300_VGA) {
-                       inSISIDXREG(SISPART1, 0x00, reg);
-                       if((reg & 0xa0) == 0x20) {
-                               doit = FALSE;
-                       }
-               }
-#endif
-       } else sisfb_crt1off = 0;
+       v1 = 0xff;
+       if(ivideo->haveXGIROM) {
+               v1 = bios[0x140 + regb];
+       }
+       outSISIDXREG(SISCR, 0x6d, v1);
 
-       if(sisvga_engine == SIS_300_VGA) {
+       ptr = cs128;
+       if(ivideo->haveXGIROM) {
+               ptr = (const u8 *)&bios[0x128];
+       }
+       for(i = 0, j = 0; i < 3; i++, j += 8) {
+               outSISIDXREG(SISCR, 0x68 + i, ptr[j + regb]);
+       }
 
-#ifdef CONFIG_FB_SIS_300
-          if((sisfb_crt1off) && (doit)) {
-               crt1isoff = TRUE;
+       ptr  = cs31a;
+       ptr2 = cs33a;
+       if(ivideo->haveXGIROM) {
+               index = (ivideo->chip == XGI_20) ? 0x31a : 0x3a6;
+               ptr  = (const u8 *)&bios[index];
+               ptr2 = (const u8 *)&bios[index + 0x20];
+       }
+       for(i = 0; i < 2; i++) {
+               if(i == 0) {
+                       regd = le32_to_cpu(((u32 *)ptr)[regb]);
+                       rega = 0x6b;
+               } else {
+                       regd = le32_to_cpu(((u32 *)ptr2)[regb]);
+                       rega = 0x6e;
+               }
                reg = 0x00;
-          } else {
-               crt1isoff = FALSE;
-               reg = 0x80;
-          }
-          setSISIDXREG(SISCR, 0x17, 0x7f, reg);
-#endif
-
-       } else {
+               for(j = 0; j < 16; j++) {
+                       reg &= 0xf3;
+                       if(regd & 0x01) reg |= 0x04;
+                       if(regd & 0x02) reg |= 0x08;
+                       regd >>= 2;
+                       outSISIDXREG(SISCR, rega, reg);
+                       inSISIDXREG(SISCR, rega, reg);
+                       inSISIDXREG(SISCR, rega, reg);
+                       reg += 0x10;
+               }
+       }
 
-#ifdef CONFIG_FB_SIS_315
-          if(sisfb_crt1off) {
-               crt1isoff = TRUE;
-               reg  = 0x40;
-               reg1 = 0xc0;
-          } else {
-               crt1isoff = FALSE;
-               reg  = 0x00;
-               reg1 = 0x00;
+       andSISIDXREG(SISCR, 0x6e, 0xfc);
 
-          }
-          setSISIDXREG(SISCR, SiS_Pr.SiS_MyCR63, ~0x40, reg);
-          setSISIDXREG(SISSR, 0x1f, ~0xc0, reg1);
-#endif
+       ptr  = NULL;
+       if(ivideo->haveXGIROM) {
+               index = (ivideo->chip == XGI_20) ? 0x35a : 0x3e6;
+               ptr  = (const u8 *)&bios[index];
+       }
+       for(i = 0; i < 4; i++) {
+               setSISIDXREG(SISCR, 0x6e, 0xfc, i);
+               reg = 0x00;
+               for(j = 0; j < 2; j++) {
+                       regd = 0;
+                       if(ptr) {
+                               regd = le32_to_cpu(((u32 *)ptr)[regb * 8]);
+                               ptr += 4;
+                       }
+                       /* reg = 0x00; */
+                       for(k = 0; k < 16; k++) {
+                               reg &= 0xfc;
+                               if(regd & 0x01) reg |= 0x01;
+                               if(regd & 0x02) reg |= 0x02;
+                               regd >>= 2;
+                               outSISIDXREG(SISCR, 0x6f, reg);
+                               inSISIDXREG(SISCR, 0x6f, reg);
+                               inSISIDXREG(SISCR, 0x6f, reg);
+                               reg += 0x08;
+                       }
+               }
+       }
 
+       ptr  = cs148;
+       if(ivideo->haveXGIROM) {
+               ptr  = (const u8 *)&bios[0x148];
+       }
+       for(i = 0, j = 0; i < 2; i++, j += 8) {
+               outSISIDXREG(SISCR, 0x80 + i, ptr[j + regb]);
        }
 
-       if(crt1isoff) {
-          ivideo.currentvbflags &= ~VB_DISPTYPE_CRT1;
-          ivideo.currentvbflags |= VB_SINGLE_MODE;
-          ivideo.disp_state |= DISPMODE_SINGLE;
-       } else {
-          ivideo.currentvbflags |= VB_DISPTYPE_CRT1;
-          ivideo.disp_state |= DISPTYPE_CRT1;
-          if(ivideo.currentvbflags & VB_DISPTYPE_CRT2) {
-               ivideo.currentvbflags |= VB_MIRROR_MODE;
-               ivideo.disp_state |= DISPMODE_MIRROR;
-          } else {
-               ivideo.currentvbflags |= VB_SINGLE_MODE;
-               ivideo.disp_state |= DISPMODE_SINGLE;
-          }
+       andSISIDXREG(SISCR, 0x89, 0x8f);
+
+       ptr  = cs45a;
+       if(ivideo->haveXGIROM) {
+               index = (ivideo->chip == XGI_20) ? 0x45a : 0x4e6;
+               ptr  = (const u8 *)&bios[index];
+       }
+       regd = le16_to_cpu(((const u16 *)ptr)[regb]);
+       reg = 0x80;
+       for(i = 0; i < 5; i++) {
+               reg &= 0xfc;
+               if(regd & 0x01) reg |= 0x01;
+               if(regd & 0x02) reg |= 0x02;
+               regd >>= 2;
+               outSISIDXREG(SISCR, 0x89, reg);
+               inSISIDXREG(SISCR, 0x89, reg);
+               inSISIDXREG(SISCR, 0x89, reg);
+               reg += 0x10;
        }
 
-        andSISIDXREG(SISSR, IND_SIS_RAMDAC_CONTROL, ~0x04);
+       v1 = 0xb5; v2 = 0x20; v3 = 0xf0; v4 = 0x13;
+       if(ivideo->haveXGIROM) {
+               v1 = bios[0x118 + regb];
+               v2 = bios[0xf8 + regb];
+               v3 = bios[0x120 + regb];
+               v4 = bios[0x1ca];
+       }
+       outSISIDXREG(SISCR, 0x45, v1 & 0x0f);
+       outSISIDXREG(SISCR, 0x99, (v1 >> 4) & 0x07);
+       orSISIDXREG(SISCR, 0x40, v1 & 0x80);
+       outSISIDXREG(SISCR, 0x41, v2);
 
-       if((ivideo.currentvbflags & CRT2_TV) && (ivideo.vbflags & VB_301)) {  /* Set filter for SiS301 */
+       ptr  = cs170;
+       if(ivideo->haveXGIROM) {
+               ptr  = (const u8 *)&bios[0x170];
+       }
+       for(i = 0, j = 0; i < 7; i++, j += 8) {
+               outSISIDXREG(SISCR, 0x90 + i, ptr[j + regb]);
+       }
 
-               switch (ivideo.video_width) {
-                  case 320:
-                       filter_tb = (ivideo.vbflags & TV_NTSC) ? 4 : 12;
-                       break;
-                  case 640:
-                       filter_tb = (ivideo.vbflags & TV_NTSC) ? 5 : 13;
-                       break;
-                  case 720:
-                       filter_tb = (ivideo.vbflags & TV_NTSC) ? 6 : 14;
-                       break;
-                  case 400:
-                  case 800:
-                       filter_tb = (ivideo.vbflags & TV_NTSC) ? 7 : 15;
-                       break;
-                  default:
-                       filter = -1;
-                       break;
-               }
+       outSISIDXREG(SISCR, 0x59, v3);
 
-               orSISIDXREG(SISPART1, sisfb_CRT2_write_enable, 0x01);
+       ptr  = cs1a8;
+       if(ivideo->haveXGIROM) {
+               ptr  = (const u8 *)&bios[0x1a8];
+       }
+       for(i = 0, j = 0; i < 3; i++, j += 8) {
+               outSISIDXREG(SISCR, 0xc3 + i, ptr[j + regb]);
+       }
 
-               if(ivideo.vbflags & TV_NTSC) {
+       ptr  = cs100;
+       if(ivideo->haveXGIROM) {
+               ptr  = (const u8 *)&bios[0x100];
+       }
+       for(i = 0, j = 0; i < 2; i++, j += 8) {
+               outSISIDXREG(SISCR, 0x8a + i, ptr[j + regb]);
+       }
 
-                       andSISIDXREG(SISPART2, 0x3a, 0x1f);
+       outSISIDXREG(SISCR, 0xcf, v4);
 
-                       if (ivideo.vbflags & TV_SVIDEO) {
+       outSISIDXREG(SISCR, 0x83, 0x09);
+       outSISIDXREG(SISCR, 0x87, 0x00);
 
-                               andSISIDXREG(SISPART2, 0x30, 0xdf);
+       if(ivideo->chip == XGI_40) {
+               if( (ivideo->revision_id == 1) ||
+                   (ivideo->revision_id == 2) ) {
+                       outSISIDXREG(SISCR, 0x8c, 0x87);
+               }
+       }
 
-                       } else if (ivideo.vbflags & TV_AVIDEO) {
+       outSISIDXREG(SISSR, 0x17, 0x00);
+       outSISIDXREG(SISSR, 0x1a, 0x87);
 
-                               orSISIDXREG(SISPART2, 0x30, 0x20);
+       if(ivideo->chip == XGI_20) {
+               outSISIDXREG(SISSR, 0x15, 0x00);
+               outSISIDXREG(SISSR, 0x1c, 0x00);
+       }
 
-                               switch (ivideo.video_width) {
-                               case 640:
-                                       outSISIDXREG(SISPART2, 0x35, 0xEB);
-                                       outSISIDXREG(SISPART2, 0x36, 0x04);
-                                       outSISIDXREG(SISPART2, 0x37, 0x25);
-                                       outSISIDXREG(SISPART2, 0x38, 0x18);
-                                       break;
-                               case 720:
-                                       outSISIDXREG(SISPART2, 0x35, 0xEE);
-                                       outSISIDXREG(SISPART2, 0x36, 0x0C);
-                                       outSISIDXREG(SISPART2, 0x37, 0x22);
-                                       outSISIDXREG(SISPART2, 0x38, 0x08);
-                                       break;
-                               case 400:
-                               case 800:
-                                       outSISIDXREG(SISPART2, 0x35, 0xEB);
-                                       outSISIDXREG(SISPART2, 0x36, 0x15);
-                                       outSISIDXREG(SISPART2, 0x37, 0x25);
-                                       outSISIDXREG(SISPART2, 0x38, 0xF6);
-                                       break;
-                               }
+       ramtype = 0x00; v1 = 0x10;
+       if(ivideo->haveXGIROM) {
+               ramtype = bios[0x62];
+               v1 = bios[0x1d2];
+       }
+       if(!(ramtype & 0x80)) {
+               if(ivideo->chip == XGI_20) {
+                       outSISIDXREG(SISCR, 0x97, v1);
+                       inSISIDXREG(SISCR, 0x97, reg);
+                       if(reg & 0x10) {
+                               ramtype = (reg & 0x01) << 1;
+                       }
+               } else {
+                       inSISIDXREG(SISSR, 0x39, reg);
+                       ramtype = reg & 0x02;
+                       if(!(ramtype)) {
+                               inSISIDXREG(SISSR, 0x3a, reg);
+                               ramtype = (reg >> 1) & 0x01;
+                       }
+               }
+       }
+       ramtype &= 0x07;
+
+       regb = 0;       /* ! */
+
+       switch(ramtype) {
+       case 0:
+               sisfb_post_xgi_setclocks(ivideo, regb);
+               if((ivideo->chip == XGI_20) ||
+                  (ivideo->revision_id == 1)   ||
+                  (ivideo->revision_id == 2)) {
+                       v1 = cs158[regb]; v2 = cs160[regb]; v3 = cs168[regb];
+                       if(ivideo->haveXGIROM) {
+                               v1 = bios[regb + 0x158];
+                               v2 = bios[regb + 0x160];
+                               v3 = bios[regb + 0x168];
                        }
+                       outSISIDXREG(SISCR, 0x82, v1);
+                       outSISIDXREG(SISCR, 0x85, v2);
+                       outSISIDXREG(SISCR, 0x86, v3);
+               } else {
+                       outSISIDXREG(SISCR, 0x82, 0x88);
+                       outSISIDXREG(SISCR, 0x86, 0x00);
+                       inSISIDXREG(SISCR, 0x86, reg);
+                       outSISIDXREG(SISCR, 0x86, 0x88);
+                       inSISIDXREG(SISCR, 0x86, reg);
+                       outSISIDXREG(SISCR, 0x86, bios[regb + 0x168]);
+                       outSISIDXREG(SISCR, 0x82, 0x77);
+                       outSISIDXREG(SISCR, 0x85, 0x00);
+                       inSISIDXREG(SISCR, 0x85, reg);
+                       outSISIDXREG(SISCR, 0x85, 0x88);
+                       inSISIDXREG(SISCR, 0x85, reg);
+                       outSISIDXREG(SISCR, 0x85, bios[regb + 0x160]);
+                       outSISIDXREG(SISCR, 0x82, bios[regb + 0x158]);
+               }
+               if(ivideo->chip == XGI_40) {
+                       outSISIDXREG(SISCR, 0x97, 0x00);
+               }
+               outSISIDXREG(SISCR, 0x98, 0x01);
+               outSISIDXREG(SISCR, 0x9a, 0x02);
 
-               } else if(ivideo.vbflags & TV_PAL) {
-
-                       andSISIDXREG(SISPART2, 0x3A, 0x1F);
-
-                       if (ivideo.vbflags & TV_SVIDEO) {
-
-                               andSISIDXREG(SISPART2, 0x30, 0xDF);
-
-                       } else if (ivideo.vbflags & TV_AVIDEO) {
-
-                               orSISIDXREG(SISPART2, 0x30, 0x20);
-
-                               switch (ivideo.video_width) {
-                               case 640:
-                                       outSISIDXREG(SISPART2, 0x35, 0xF1);
-                                       outSISIDXREG(SISPART2, 0x36, 0xF7);
-                                       outSISIDXREG(SISPART2, 0x37, 0x1F);
-                                       outSISIDXREG(SISPART2, 0x38, 0x32);
-                                       break;
-                               case 720:
-                                       outSISIDXREG(SISPART2, 0x35, 0xF3);
-                                       outSISIDXREG(SISPART2, 0x36, 0x00);
-                                       outSISIDXREG(SISPART2, 0x37, 0x1D);
-                                       outSISIDXREG(SISPART2, 0x38, 0x20);
-                                       break;
-                               case 400:
-                               case 800:
-                                       outSISIDXREG(SISPART2, 0x35, 0xFC);
-                                       outSISIDXREG(SISPART2, 0x36, 0xFB);
-                                       outSISIDXREG(SISPART2, 0x37, 0x14);
-                                       outSISIDXREG(SISPART2, 0x38, 0x2A);
-                                       break;
-                               }
+               outSISIDXREG(SISSR, 0x18, 0x01);
+               if((ivideo->chip == XGI_20) ||
+                  (ivideo->revision_id == 2)) {
+                       outSISIDXREG(SISSR, 0x19, 0x40);
+               } else {
+                       outSISIDXREG(SISSR, 0x19, 0x20);
+               }
+               outSISIDXREG(SISSR, 0x16, 0x00);
+               outSISIDXREG(SISSR, 0x16, 0x80);
+               if((ivideo->chip == XGI_20) || (bios[0x1cb] != 0x0c)) {
+                       sisfb_post_xgi_delay(ivideo, 0x43);
+                       sisfb_post_xgi_delay(ivideo, 0x43);
+                       sisfb_post_xgi_delay(ivideo, 0x43);
+                       outSISIDXREG(SISSR, 0x18, 0x00);
+                       if((ivideo->chip == XGI_20) ||
+                          (ivideo->revision_id == 2)) {
+                               outSISIDXREG(SISSR, 0x19, 0x40);
+                       } else {
+                               outSISIDXREG(SISSR, 0x19, 0x20);
+                       }
+               } else if((ivideo->chip == XGI_40) && (bios[0x1cb] == 0x0c)) {
+                       /* outSISIDXREG(SISSR, 0x16, 0x0c); */ /* ? */
+               }
+               outSISIDXREG(SISSR, 0x16, 0x00);
+               outSISIDXREG(SISSR, 0x16, 0x80);
+               sisfb_post_xgi_delay(ivideo, 4);
+               v1 = 0x31; v2 = 0x03; v3 = 0x83; v4 = 0x03; v5 = 0x83;
+               if(ivideo->haveXGIROM) {
+                       v1 = bios[0xf0];
+                       index = (ivideo->chip == XGI_20) ? 0x4b2 : 0x53e;
+                       v2 = bios[index];
+                       v3 = bios[index + 1];
+                       v4 = bios[index + 2];
+                       v5 = bios[index + 3];
+               }
+               outSISIDXREG(SISSR, 0x18, v1);
+               outSISIDXREG(SISSR, 0x19, ((ivideo->chip == XGI_20) ? 0x02 : 0x01));
+               outSISIDXREG(SISSR, 0x16, v2);
+               outSISIDXREG(SISSR, 0x16, v3);
+               sisfb_post_xgi_delay(ivideo, 0x43);
+               outSISIDXREG(SISSR, 0x1b, 0x03);
+               sisfb_post_xgi_delay(ivideo, 0x22);
+               outSISIDXREG(SISSR, 0x18, v1);
+               outSISIDXREG(SISSR, 0x19, 0x00);
+               outSISIDXREG(SISSR, 0x16, v4);
+               outSISIDXREG(SISSR, 0x16, v5);
+               outSISIDXREG(SISSR, 0x1b, 0x00);
+               break;
+       case 1:
+               outSISIDXREG(SISCR, 0x82, 0x77);
+               outSISIDXREG(SISCR, 0x86, 0x00);
+               inSISIDXREG(SISCR, 0x86, reg);
+               outSISIDXREG(SISCR, 0x86, 0x88);
+               inSISIDXREG(SISCR, 0x86, reg);
+               v1 = cs168[regb]; v2 = cs160[regb]; v3 = cs158[regb];
+               if(ivideo->haveXGIROM) {
+                       v1 = bios[regb + 0x168];
+                       v2 = bios[regb + 0x160];
+                       v3 = bios[regb + 0x158];
+               }
+               outSISIDXREG(SISCR, 0x86, v1);
+               outSISIDXREG(SISCR, 0x82, 0x77);
+               outSISIDXREG(SISCR, 0x85, 0x00);
+               inSISIDXREG(SISCR, 0x85, reg);
+               outSISIDXREG(SISCR, 0x85, 0x88);
+               inSISIDXREG(SISCR, 0x85, reg);
+               outSISIDXREG(SISCR, 0x85, v2);
+               outSISIDXREG(SISCR, 0x82, v3);
+               outSISIDXREG(SISCR, 0x98, 0x01);
+               outSISIDXREG(SISCR, 0x9a, 0x02);
+
+               outSISIDXREG(SISSR, 0x28, 0x64);
+               outSISIDXREG(SISSR, 0x29, 0x63);
+               sisfb_post_xgi_delay(ivideo, 15);
+               outSISIDXREG(SISSR, 0x18, 0x00);
+               outSISIDXREG(SISSR, 0x19, 0x20);
+               outSISIDXREG(SISSR, 0x16, 0x00);
+               outSISIDXREG(SISSR, 0x16, 0x80);
+               outSISIDXREG(SISSR, 0x18, 0xc5);
+               outSISIDXREG(SISSR, 0x19, 0x23);
+               outSISIDXREG(SISSR, 0x16, 0x00);
+               outSISIDXREG(SISSR, 0x16, 0x80);
+               sisfb_post_xgi_delay(ivideo, 1);
+               outSISIDXREG(SISCR, 0x97,0x11);
+               sisfb_post_xgi_setclocks(ivideo, regb);
+               sisfb_post_xgi_delay(ivideo, 0x46);
+               outSISIDXREG(SISSR, 0x18, 0xc5);
+               outSISIDXREG(SISSR, 0x19, 0x23);
+               outSISIDXREG(SISSR, 0x16, 0x00);
+               outSISIDXREG(SISSR, 0x16, 0x80);
+               sisfb_post_xgi_delay(ivideo, 1);
+               outSISIDXREG(SISSR, 0x1b, 0x04);
+               sisfb_post_xgi_delay(ivideo, 1);
+               outSISIDXREG(SISSR, 0x1b, 0x00);
+               sisfb_post_xgi_delay(ivideo, 1);
+               v1 = 0x31;
+               if(ivideo->haveXGIROM) {
+                       v1 = bios[0xf0];
+               }
+               outSISIDXREG(SISSR, 0x18, v1);
+               outSISIDXREG(SISSR, 0x19, 0x06);
+               outSISIDXREG(SISSR, 0x16, 0x04);
+               outSISIDXREG(SISSR, 0x16, 0x84);
+               sisfb_post_xgi_delay(ivideo, 1);
+               break;
+       default:
+               sisfb_post_xgi_setclocks(ivideo, regb);
+               if((ivideo->chip == XGI_40) &&
+                  ((ivideo->revision_id == 1) ||
+                   (ivideo->revision_id == 2))) {
+                       outSISIDXREG(SISCR, 0x82, bios[regb + 0x158]);
+                       outSISIDXREG(SISCR, 0x85, bios[regb + 0x160]);
+                       outSISIDXREG(SISCR, 0x86, bios[regb + 0x168]);
+               } else {
+                       outSISIDXREG(SISCR, 0x82, 0x88);
+                       outSISIDXREG(SISCR, 0x86, 0x00);
+                       inSISIDXREG(SISCR, 0x86, reg);
+                       outSISIDXREG(SISCR, 0x86, 0x88);
+                       outSISIDXREG(SISCR, 0x82, 0x77);
+                       outSISIDXREG(SISCR, 0x85, 0x00);
+                       inSISIDXREG(SISCR, 0x85, reg);
+                       outSISIDXREG(SISCR, 0x85, 0x88);
+                       inSISIDXREG(SISCR, 0x85, reg);
+                       v1 = cs160[regb]; v2 = cs158[regb];
+                       if(ivideo->haveXGIROM) {
+                               v1 = bios[regb + 0x160];
+                               v2 = bios[regb + 0x158];
                        }
+                       outSISIDXREG(SISCR, 0x85, v1);
+                       outSISIDXREG(SISCR, 0x82, v2);
+               }
+               if(ivideo->chip == XGI_40) {
+                       outSISIDXREG(SISCR, 0x97, 0x11);
+               }
+               if((ivideo->chip == XGI_40) && (ivideo->revision_id == 2)) {
+                       outSISIDXREG(SISCR, 0x98, 0x01);
+               } else {
+                       outSISIDXREG(SISCR, 0x98, 0x03);
                }
+               outSISIDXREG(SISCR, 0x9a, 0x02);
 
-               if ((filter >= 0) && (filter <= 7)) {
-                       DPRINTK("FilterTable[%d]-%d: %02x %02x %02x %02x\n", filter_tb, filter,
-                               sis_TV_filter[filter_tb].filter[filter][0],
-                               sis_TV_filter[filter_tb].filter[filter][1],
-                               sis_TV_filter[filter_tb].filter[filter][2],
-                               sis_TV_filter[filter_tb].filter[filter][3]
-                       );
-                       outSISIDXREG(SISPART2, 0x35, (sis_TV_filter[filter_tb].filter[filter][0]));
-                       outSISIDXREG(SISPART2, 0x36, (sis_TV_filter[filter_tb].filter[filter][1]));
-                       outSISIDXREG(SISPART2, 0x37, (sis_TV_filter[filter_tb].filter[filter][2]));
-                       outSISIDXREG(SISPART2, 0x38, (sis_TV_filter[filter_tb].filter[filter][3]));
+               if(ivideo->chip == XGI_40) {
+                       outSISIDXREG(SISSR, 0x18, 0x01);
+               } else {
+                       outSISIDXREG(SISSR, 0x18, 0x00);
+               }
+               outSISIDXREG(SISSR, 0x19, 0x40);
+               outSISIDXREG(SISSR, 0x16, 0x00);
+               outSISIDXREG(SISSR, 0x16, 0x80);
+               if((ivideo->chip == XGI_40) && (bios[0x1cb] != 0x0c)) {
+                       sisfb_post_xgi_delay(ivideo, 0x43);
+                       sisfb_post_xgi_delay(ivideo, 0x43);
+                       sisfb_post_xgi_delay(ivideo, 0x43);
+                       outSISIDXREG(SISSR, 0x18, 0x00);
+                       outSISIDXREG(SISSR, 0x19, 0x40);
+                       outSISIDXREG(SISSR, 0x16, 0x00);
+                       outSISIDXREG(SISSR, 0x16, 0x80);
+               }
+               sisfb_post_xgi_delay(ivideo, 4);
+               v1 = 0x31;
+               if(ivideo->haveXGIROM) {
+                       v1 = bios[0xf0];
+               }
+               outSISIDXREG(SISSR, 0x18, v1);
+               outSISIDXREG(SISSR, 0x19, 0x01);
+               if(ivideo->chip == XGI_40) {
+                       outSISIDXREG(SISSR, 0x16, bios[0x53e]);
+                       outSISIDXREG(SISSR, 0x16, bios[0x53f]);
+               } else {
+                       outSISIDXREG(SISSR, 0x16, 0x05);
+                       outSISIDXREG(SISSR, 0x16, 0x85);
+               }
+               sisfb_post_xgi_delay(ivideo, 0x43);
+               if(ivideo->chip == XGI_40) {
+                       outSISIDXREG(SISSR, 0x1b, 0x01);
+               } else {
+                       outSISIDXREG(SISSR, 0x1b, 0x03);
                }
-         
+               sisfb_post_xgi_delay(ivideo, 0x22);
+               outSISIDXREG(SISSR, 0x18, v1);
+               outSISIDXREG(SISSR, 0x19, 0x00);
+               if(ivideo->chip == XGI_40) {
+                       outSISIDXREG(SISSR, 0x16, bios[0x540]);
+                       outSISIDXREG(SISSR, 0x16, bios[0x541]);
+               } else {
+                       outSISIDXREG(SISSR, 0x16, 0x05);
+                       outSISIDXREG(SISSR, 0x16, 0x85);
+               }
+               outSISIDXREG(SISSR, 0x1b, 0x00);
        }
 
-}
-
-#ifndef MODULE
-int sisfb_setup(char *options)
-{
-       char *this_opt;
-       
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-       sisfb_fontname[0] = '\0';
-#endif
+       regb = 0;       /* ! */
+       v1 = 0x03;
+       if(ivideo->haveXGIROM) {
+               v1 = bios[0x110 + regb];
+       }
+       outSISIDXREG(SISSR, 0x1b, v1);
 
-       ivideo.refresh_rate = 0;
-       SiS_Pr.SiS_CustomT = CUT_NONE;
-       SiS_Pr.UsePanelScaler = -1;
-       SiS_Pr.LVDSHL = -1;
+       /* RAM size */
+       v1 = 0x00; v2 = 0x00;
+       if(ivideo->haveXGIROM) {
+               v1 = bios[0x62];
+               v2 = bios[0x63];
+       }
+       regb = 0;       /* ! */
+       regd = 1 << regb;
+       if((v1 & 0x40) && (v2 & regd) && ivideo->haveXGIROM) {
 
-        printk(KERN_DEBUG "sisfb: Options %s\n", options);
+               outSISIDXREG(SISSR, 0x13, bios[regb + 0xe0]);
+               outSISIDXREG(SISSR, 0x14, bios[regb + 0xe0 + 8]);
 
-       if (!options || !*options)
-               return 0;
+       } else {
 
-       while((this_opt = strsep(&options, ",")) != NULL) {
+               /* Set default mode, don't clear screen */
+               ivideo->SiS_Pr.SiS_UseOEM = FALSE;
+               SiS_SetEnableDstn(&ivideo->SiS_Pr, FALSE);
+               SiS_SetEnableFstn(&ivideo->SiS_Pr, FALSE);
+               ivideo->curFSTN = ivideo->curDSTN = 0;
+               ivideo->SiS_Pr.VideoMemorySize = 8 << 20;
+               SiSSetMode(&ivideo->SiS_Pr, 0x2e | 0x80);
 
-               if (!*this_opt) continue;
+               outSISIDXREG(SISSR, 0x05, 0x86);
 
-               if (!strnicmp(this_opt, "mode:", 5)) {
-                       sisfb_search_mode(this_opt + 5, FALSE);
-               } else if (!strnicmp(this_opt, "vesa:", 5)) {
-                       sisfb_search_vesamode(simple_strtoul(this_opt + 5, NULL, 0), FALSE);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-               } else if (!strnicmp(this_opt, "inverse", 7)) {
-                       sisfb_inverse = 1;
-                       /* fb_invert_cmaps(); */
-               } else if (!strnicmp(this_opt, "font:", 5)) {
-                       strncpy(sisfb_fontname, this_opt + 5, sizeof(sisfb_fontname) - 1);
-                       sisfb_fontname[sizeof(sisfb_fontname) - 1] = '\0';
-#endif
-               } else if (!strnicmp(this_opt, "vrate:", 6)) {
-                       ivideo.refresh_rate = simple_strtoul(this_opt + 6, NULL, 0);
-                       sisfb_parm_rate = ivideo.refresh_rate;
-               } else if (!strnicmp(this_opt, "rate:", 5)) {
-                       ivideo.refresh_rate = simple_strtoul(this_opt + 5, NULL, 0);
-                       sisfb_parm_rate = ivideo.refresh_rate;
-               } else if (!strnicmp(this_opt, "off", 3)) {
-                       sisfb_off = 1;
-               } else if (!strnicmp(this_opt, "crt1off", 7)) {
-                       sisfb_crt1off = 1;
-               } else if (!strnicmp(this_opt, "filter:", 7)) {
-                       filter = (int)simple_strtoul(this_opt + 7, NULL, 0);
-               } else if (!strnicmp(this_opt, "forcecrt2type:", 14)) {
-                       sisfb_search_crt2type(this_opt + 14);
-               } else if (!strnicmp(this_opt, "forcecrt1:", 10)) {
-                       sisfb_forcecrt1 = (int)simple_strtoul(this_opt + 10, NULL, 0);
-                } else if (!strnicmp(this_opt, "tvmode:",7)) {
-                       sisfb_search_tvstd(this_opt + 7);
-                } else if (!strnicmp(this_opt, "tvstandard:",11)) {
-                       sisfb_search_tvstd(this_opt + 7);
-                } else if (!strnicmp(this_opt, "mem:",4)) {
-                       sisfb_mem = simple_strtoul(this_opt + 4, NULL, 0);
-               } else if (!strnicmp(this_opt, "queuemode:", 10)) {
-                       sisfb_search_queuemode(this_opt + 10);
-               } else if (!strnicmp(this_opt, "pdc:", 4)) {
-                       sisfb_pdc = simple_strtoul(this_opt + 4, NULL, 0);
-               } else if (!strnicmp(this_opt, "noaccel", 7)) {
-                       sisfb_accel = 0;
-               } else if (!strnicmp(this_opt, "noypan", 6)) {
-                       sisfb_ypan = 0;
-               } else if (!strnicmp(this_opt, "nomax", 5)) {
-                       sisfb_max = 0;
-               } else if (!strnicmp(this_opt, "userom:", 7)) {
-                       sisfb_userom = (int)simple_strtoul(this_opt + 7, NULL, 0);
-               } else if (!strnicmp(this_opt, "useoem:", 7)) {
-                       sisfb_useoem = (int)simple_strtoul(this_opt + 7, NULL, 0);
-               } else if (!strnicmp(this_opt, "nocrt2rate", 10)) {
-                       sisfb_nocrt2rate = 1;
-               } else if (!strnicmp(this_opt, "scalelcd:", 9)) {
-                       unsigned long temp = 2;
-                       temp = simple_strtoul(this_opt + 9, NULL, 0);
-                       if((temp == 0) || (temp == 1)) {
-                          SiS_Pr.UsePanelScaler = temp ^ 1;
-                       }
-               } else if (!strnicmp(this_opt, "specialtiming:", 14)) {
-                       sisfb_search_specialtiming(this_opt + 14);
-               } else if (!strnicmp(this_opt, "lvdshl:", 7)) {
-                       unsigned long temp = 4;
-                       temp = simple_strtoul(this_opt + 7, NULL, 0);
-                       if((temp >= 0) && (temp <= 3)) {
-                          SiS_Pr.LVDSHL = temp;
-                       }
-               } else if(this_opt[0] >= '0' && this_opt[0] <= '9') {
-                       sisfb_search_mode(this_opt, TRUE);
-               } else {
-                       printk(KERN_INFO "sisfb: Invalid option %s\n", this_opt);
-               }
+               /* Disable read-cache */
+               andSISIDXREG(SISSR, 0x21, 0xdf);
+               sisfb_post_xgi_ramsize(ivideo);
+               /* Enable read-cache */
+               orSISIDXREG(SISSR, 0x21, 0x20);
 
-               /* TW: Acceleration only with MMIO mode */
-               if((sisfb_queuemode != -1) && (sisfb_queuemode != MMIO_CMD)) {
-                       sisfb_accel = 0;
-               }
+       }
 
+#if 0
+       printk(KERN_DEBUG "-----------------\n");
+       for(i = 0; i < 0xff; i++) {
+               inSISIDXREG(SISCR, i, reg);
+               printk(KERN_DEBUG "CR%02x(%x) = 0x%02x\n", i, SISCR, reg);
        }
-       return 0;
-}
+       for(i = 0; i < 0x40; i++) {
+               inSISIDXREG(SISSR, i, reg);
+               printk(KERN_DEBUG "SR%02x(%x) = 0x%02x\n", i, SISSR, reg);
+       }
+       printk(KERN_DEBUG "-----------------\n");
 #endif
 
-static char *sis_find_rom(void)
-{
-#if defined(__i386__)
-        u32  segstart;
-        unsigned char *rom_base, *rom;
-        int  romptr;
-       unsigned short pciid;
-
-        for(segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) {
+       /* Sense CRT1 */
+       if(ivideo->chip == XGI_20) {
+               orSISIDXREG(SISCR, 0x32, 0x20);
+       } else {
+               inSISIDXREG(SISPART4, 0x00, reg);
+               if((reg == 1) || (reg == 2)) {
+                       sisfb_sense_crt1(ivideo);
+               } else {
+                       orSISIDXREG(SISCR, 0x32, 0x20);
+               }
+       }
 
-                rom_base = (char *)ioremap(segstart, 0x10000);
-               if(!rom_base) continue;
+       /* Set default mode, don't clear screen */
+       ivideo->SiS_Pr.SiS_UseOEM = FALSE;
+       SiS_SetEnableDstn(&ivideo->SiS_Pr, FALSE);
+       SiS_SetEnableFstn(&ivideo->SiS_Pr, FALSE);
+       ivideo->curFSTN = ivideo->curDSTN = 0;
+       SiSSetMode(&ivideo->SiS_Pr, 0x2e | 0x80);
 
-                if((*rom_base != 0x55) || (*(rom_base + 1) != 0xaa)) {
-                   iounmap(rom_base);
-                   continue;
-                }
+       outSISIDXREG(SISSR, 0x05, 0x86);
 
-               romptr = (unsigned short)(*(rom_base + 0x18) | (*(rom_base + 0x19) << 8));
-               if(romptr > (0x10000 - 8)) {
-                  iounmap(rom_base);
-                  continue;
-               }
+       /* Display off */
+       orSISIDXREG(SISSR, 0x01, 0x20);
 
-               rom = rom_base + romptr;
+       /* Save mode number in CR34 */
+       outSISIDXREG(SISCR, 0x34, 0x2e);
 
-               if((*rom != 'P') || (*(rom + 1) != 'C') || (*(rom + 2) != 'I') || (*(rom + 3) != 'R')) {
-                  iounmap(rom_base);
-                  continue;
-               }
+       /* Let everyone know what the current mode is */
+       ivideo->modeprechange = 0x2e;
 
-               pciid = (*(rom + 4)) | ((*(rom + 5)) << 8);
-               if(pciid != 0x1039) {
-                  iounmap(rom_base);
-                  continue;
+       if(ivideo->chip == XGI_40) {
+               inSISIDXREG(SISCR, 0xca, reg);
+               inSISIDXREG(SISCR, 0xcc, v1);
+               if((reg & 0x10) && (!(v1 & 0x04))) {
+                       printk(KERN_ERR
+                               "sisfb: Please connect power to the card.\n");
+                       return 0;
                }
+       }
 
-               pciid = (*(rom + 6)) | ((*(rom + 7)) << 8);
-               if(pciid == ivideo.chip_id) return rom_base;
-
-               iounmap(rom_base);
-        }
-#endif
-        return NULL;
+       return 1;
 }
+#endif
 
-
-
-int __init sisfb_init(void)
+static int __devinit
+sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
-       struct pci_dev *pdev = NULL;
-       struct board *b;
-       int pdev_valid = 0;
-       u32 reg32;
+       struct sisfb_chip_info  *chipinfo = &sisfb_chip_info[ent->driver_data];
+       struct sis_video_info   *ivideo = NULL;
+       struct fb_info          *sis_fb_info = NULL;
        u16 reg16;
        u8  reg;
+       int i, ret;
 
-#if 0
-       /* for DOC VB */
-       sisfb_set_reg4(0xcf8,0x800000e0);
-       reg32 = sisfb_get_reg3(0xcfc);
-       reg32 = reg32 | 0x00001000;
-       sisfb_set_reg4(0xcfc,reg32);
-       }
-#endif
-
-       if (sisfb_off)
+       if(sisfb_off)
                return -ENXIO;
 
-       sisfb_registered = 0;
-       sisfb_thismonitor.datavalid = FALSE;
+       sis_fb_info = framebuffer_alloc(sizeof(*ivideo), &pdev->dev);
+       if(!sis_fb_info)
+               return -ENOMEM;
 
-       memset(&sishw_ext, 0, sizeof(sishw_ext));
+       ivideo = (struct sis_video_info *)sis_fb_info->par;
+       ivideo->memyselfandi = sis_fb_info;
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-        memset(&sisfb_lastrates[0], 0, 128);
-#endif
-       
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) 
-       memset(&sis_disp, 0, sizeof(sis_disp));
-#endif
+       ivideo->sisfb_id = SISFB_ID;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,74)
-       pci_for_each_dev(pdev) {
-#else
-       while((pdev = pci_find_device(PCI_VENDOR_ID_SI, PCI_ANY_ID, pdev))) {
-#endif
-               for (b = sisdev_list; b->vendor; b++) {
-                       if ((b->vendor == pdev->vendor)
-                           && (b->device == pdev->device)) {
-                               pdev_valid = 1;
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) && defined(NEWFBDEV)
-                               sis_fb_info = framebuffer_alloc(0, &pdev->dev);
-#else
-                               sis_fb_info = kmalloc(sizeof(*sis_fb_info), GFP_KERNEL);
+       if(card_list == NULL) {
+               ivideo->cardnumber = 0;
+       } else {
+               struct sis_video_info *countvideo = card_list;
+               ivideo->cardnumber = 1;
+               while((countvideo = countvideo->next) != 0)
+                       ivideo->cardnumber++;
+       }
+
+       strncpy(ivideo->myid, chipinfo->chip_name, 30);
+
+       ivideo->warncount = 0;
+       ivideo->chip_id = pdev->device;
+       ivideo->chip_vendor = pdev->vendor;
+       pci_read_config_byte(pdev, PCI_REVISION_ID, &ivideo->revision_id);
+       ivideo->SiS_Pr.ChipRevision = ivideo->revision_id;
+       pci_read_config_word(pdev, PCI_COMMAND, &reg16);
+       ivideo->sisvga_enabled = reg16 & 0x01;
+       ivideo->pcibus = pdev->bus->number;
+       ivideo->pcislot = PCI_SLOT(pdev->devfn);
+       ivideo->pcifunc = PCI_FUNC(pdev->devfn);
+       ivideo->subsysvendor = pdev->subsystem_vendor;
+       ivideo->subsysdevice = pdev->subsystem_device;
+#ifdef SIS_OLD_CONFIG_COMPAT
+       ivideo->ioctl32registered = 0;
 #endif
-                               if(!sis_fb_info) return -ENOMEM;
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)) || (!(defined(NEWFBDEV)))
-                               memset(sis_fb_info, 0, sizeof(*sis_fb_info));
+
+#ifndef MODULE
+       if(sisfb_mode_idx == -1) {
+               sisfb_get_vga_mode_from_kernel();
+       }
 #endif
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
-                               strcpy(sis_fb_info->modename, b->name);
-#else                          
-                               strcpy(myid, b->name);
-#endif                         
-                               ivideo.chip_id = pdev->device;
-                               pci_read_config_byte(pdev, PCI_REVISION_ID,
-                                                    &ivideo.revision_id);
-                               pci_read_config_word(pdev, PCI_COMMAND, &reg16);
-                               sishw_ext.jChipRevision = ivideo.revision_id;
-                               sisvga_enabled = reg16 & 0x01;
-                               ivideo.pcibus = pdev->bus->number;
-                               ivideo.pcislot = PCI_SLOT(pdev->devfn);
-                               ivideo.pcifunc = PCI_FUNC(pdev->devfn);
-                               ivideo.subsysvendor = pdev->subsystem_vendor;
-                               ivideo.subsysdevice = pdev->subsystem_device;
-                               break;
-                       }
+
+       ivideo->chip = chipinfo->chip;
+       ivideo->sisvga_engine = chipinfo->vgaengine;
+       ivideo->hwcursor_size = chipinfo->hwcursor_size;
+       ivideo->CRT2_write_enable = chipinfo->CRT2_write_enable;
+       ivideo->mni = chipinfo->mni;
+
+       ivideo->detectedpdc  = 0xff;
+       ivideo->detectedpdca = 0xff;
+       ivideo->detectedlcda = 0xff;
+
+       ivideo->sisfb_thismonitor.datavalid = FALSE;
+
+       ivideo->current_base = 0;
+
+       ivideo->engineok = 0;
+
+       ivideo->sisfb_was_boot_device = 0;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12))
+       if(pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW) {
+               if(ivideo->sisvga_enabled)
+                       ivideo->sisfb_was_boot_device = 1;
+               else {
+                       printk(KERN_DEBUG "sisfb: PCI device is disabled, "
+                               "but marked as boot video device ???\n");
+                       printk(KERN_DEBUG "sisfb: I will not accept this "
+                               "as the primary VGA device\n");
                }
+       }
+#endif
 
-               if (pdev_valid)
-                       break;
+       ivideo->sisfb_parm_mem = sisfb_parm_mem;
+       ivideo->sisfb_accel = sisfb_accel;
+       ivideo->sisfb_ypan = sisfb_ypan;
+       ivideo->sisfb_max = sisfb_max;
+       ivideo->sisfb_userom = sisfb_userom;
+       ivideo->sisfb_useoem = sisfb_useoem;
+       ivideo->sisfb_mode_idx = sisfb_mode_idx;
+       ivideo->sisfb_parm_rate = sisfb_parm_rate;
+       ivideo->sisfb_crt1off = sisfb_crt1off;
+       ivideo->sisfb_forcecrt1 = sisfb_forcecrt1;
+       ivideo->sisfb_crt2type = sisfb_crt2type;
+       ivideo->sisfb_crt2flags = sisfb_crt2flags;
+       /* pdc(a), scalelcd, special timing, lvdshl handled below */
+       ivideo->sisfb_dstn = sisfb_dstn;
+       ivideo->sisfb_fstn = sisfb_fstn;
+       ivideo->sisfb_tvplug = sisfb_tvplug;
+       ivideo->sisfb_tvstd = sisfb_tvstd;
+       ivideo->tvxpos = sisfb_tvxposoffset;
+       ivideo->tvypos = sisfb_tvyposoffset;
+       ivideo->sisfb_nocrt2rate = sisfb_nocrt2rate;
+       ivideo->refresh_rate = 0;
+       if(ivideo->sisfb_parm_rate != -1) {
+               ivideo->refresh_rate = ivideo->sisfb_parm_rate;
+       }
+
+       ivideo->SiS_Pr.UsePanelScaler = sisfb_scalelcd;
+       ivideo->SiS_Pr.CenterScreen = -1;
+       ivideo->SiS_Pr.SiS_CustomT = sisfb_specialtiming;
+       ivideo->SiS_Pr.LVDSHL = sisfb_lvdshl;
+
+       ivideo->SiS_Pr.SiS_Backup70xx = 0xff;
+       ivideo->SiS_Pr.SiS_CHOverScan = -1;
+       ivideo->SiS_Pr.SiS_ChSW = FALSE;
+       ivideo->SiS_Pr.SiS_UseLCDA = FALSE;
+       ivideo->SiS_Pr.HaveEMI = FALSE;
+       ivideo->SiS_Pr.HaveEMILCD = FALSE;
+       ivideo->SiS_Pr.OverruleEMI = FALSE;
+       ivideo->SiS_Pr.SiS_SensibleSR11 = FALSE;
+       ivideo->SiS_Pr.SiS_MyCR63 = 0x63;
+       ivideo->SiS_Pr.PDC  = -1;
+       ivideo->SiS_Pr.PDCA = -1;
+       ivideo->SiS_Pr.DDCPortMixup = FALSE;
+#ifdef CONFIG_FB_SIS_315
+       if(ivideo->chip >= SIS_330) {
+               ivideo->SiS_Pr.SiS_MyCR63 = 0x53;
+               if(ivideo->chip >= SIS_661) {
+                       ivideo->SiS_Pr.SiS_SensibleSR11 = TRUE;
+               }
        }
+#endif
 
-       if (!pdev_valid)
-               return -ENODEV;
+       memcpy(&ivideo->default_var, &my_default_var, sizeof(my_default_var));
+
+       pci_set_drvdata(pdev, ivideo);
 
-       switch (ivideo.chip_id) {
+       /* Patch special cases */
+       if((ivideo->nbridge = sisfb_get_northbridge(ivideo->chip))) {
+               switch(ivideo->nbridge->device) {
 #ifdef CONFIG_FB_SIS_300
-          case PCI_DEVICE_ID_SI_300:
-               ivideo.chip = SIS_300;
-               sisvga_engine = SIS_300_VGA;
-               sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_300 * 2;  /* New X driver uses 2 buffers */
-               sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_300;
-               break;
-          case PCI_DEVICE_ID_SI_630_VGA:
-               {
-                       ivideo.chip = SIS_630;
-                       sisfb_set_reg4(0xCF8, 0x80000000);
-                       reg32 = sisfb_get_reg3(0xCFC);
-                       if(reg32 == 0x07301039) {
-                               ivideo.chip = SIS_730;
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
-                               strcpy(sis_fb_info->modename, "SIS 730");
-#else
-                               strcpy(myid, "SIS 730");
-#endif
-                       }
-                       sisvga_engine = SIS_300_VGA;
-                       sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_300 * 2;
-                       sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_300;
+               case PCI_DEVICE_ID_SI_730:
+                       ivideo->chip = SIS_730;
+                       strcpy(ivideo->myid, "SiS 730");
                        break;
-               }
-          case PCI_DEVICE_ID_SI_540_VGA:
-               ivideo.chip = SIS_540;
-               sisvga_engine = SIS_300_VGA;
-               sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_300 * 2;
-               sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_300;
-               break;
 #endif
 #ifdef CONFIG_FB_SIS_315
-          case PCI_DEVICE_ID_SI_315H:
-               ivideo.chip = SIS_315H;
-               sisvga_engine = SIS_315_VGA;
-               sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_315 * 2;
-               sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_315;
-               break;
-          case PCI_DEVICE_ID_SI_315:
-               ivideo.chip = SIS_315;
-               sisvga_engine = SIS_315_VGA;
-               sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_315 * 2;
-               sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_315;
-               break;
-          case PCI_DEVICE_ID_SI_315PRO:
-               ivideo.chip = SIS_315PRO;
-               sisvga_engine = SIS_315_VGA;
-               sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_315 * 2;
-               sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_315;
-               break;
-          case PCI_DEVICE_ID_SI_550_VGA:
-               ivideo.chip = SIS_550;
-               sisvga_engine = SIS_315_VGA;
-               sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_315 * 2;
-               sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_315;
-               break;
-          case PCI_DEVICE_ID_SI_650_VGA:
-               {
-                       ivideo.chip = SIS_650;  
-                       sisfb_set_reg4(0xCF8, 0x80000000);
-                       reg32 = sisfb_get_reg3(0xCFC);
-                       if(reg32 == 0x07401039) {
-                               ivideo.chip = SIS_740;
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
-                               strcpy(sis_fb_info->modename, "SIS 740");
-#else
-                               strcpy(myid, "SIS 740");
-#endif
-                       }
-                       sisvga_engine = SIS_315_VGA;
-                       sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_315 * 2;
-                       sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_315;
+               case PCI_DEVICE_ID_SI_651:
+                       /* ivideo->chip is ok */
+                       strcpy(ivideo->myid, "SiS 651");
+                       break;
+               case PCI_DEVICE_ID_SI_740:
+                       ivideo->chip = SIS_740;
+                       strcpy(ivideo->myid, "SiS 740");
+                       break;
+               case PCI_DEVICE_ID_SI_661:
+                       ivideo->chip = SIS_661;
+                       strcpy(ivideo->myid, "SiS 661");
+                       break;
+               case PCI_DEVICE_ID_SI_741:
+                       ivideo->chip = SIS_741;
+                       strcpy(ivideo->myid, "SiS 741");
+                       break;
+               case PCI_DEVICE_ID_SI_760:
+                       ivideo->chip = SIS_760;
+                       strcpy(ivideo->myid, "SiS 760");
+                       break;
+               case PCI_DEVICE_ID_SI_761:
+                       ivideo->chip = SIS_761;
+                       strcpy(ivideo->myid, "SiS 761");
                        break;
-               }
-          case PCI_DEVICE_ID_SI_330:
-               ivideo.chip = SIS_330;
-               sisvga_engine = SIS_315_VGA;
-               sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_315 * 2;
-               sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_315;
-               break;
-          case PCI_DEVICE_ID_SI_660_VGA:
-               {
-                       sisfb_set_reg4(0xCF8, 0x80000000);
-                       reg32 = sisfb_get_reg3(0xCFC);
-                       if(reg32 == 0x07601039) {
-                               ivideo.chip = SIS_760;
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
-                               strcpy(sis_fb_info->modename, "SIS 760");
-#else
-                               strcpy(myid, "SIS 760");
-#endif
-                       } else if(reg32 == 0x06601039) {
-                               ivideo.chip = SIS_660;
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
-                               strcpy(sis_fb_info->modename, "SIS 660");
-#else
-                               strcpy(myid, "SIS 660");
-#endif
-                       } else if(reg32 == 0x07411039) {
-                               ivideo.chip = SIS_741;
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
-                               strcpy(sis_fb_info->modename, "SIS 741");
-#else
-                               strcpy(myid, "SIS 741");
-#endif
-                       } else {
-                               ivideo.chip = SIS_661;
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
-                               strcpy(sis_fb_info->modename, "SIS 661");
-#else
-                               strcpy(myid, "SIS 661");
 #endif
-                       }
-                       sisvga_engine = SIS_315_VGA;
-                       sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_315 * 2;
-                       sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_315;
+               default:
                        break;
                }
-#endif
-           default:
-               kfree(sis_fb_info);
-               return -ENODEV;
        }
-       sishw_ext.jChipType = ivideo.chip;
 
-       /* for Debug */
-       if( (sishw_ext.jChipType == SIS_315PRO) ||
-           (sishw_ext.jChipType == SIS_315) )
-               sishw_ext.jChipType = SIS_315H;
+       ivideo->SiS_Pr.ChipType = ivideo->chip;
 
-       ivideo.video_base = pci_resource_start(pdev, 0);
-       ivideo.mmio_base = pci_resource_start(pdev, 1);
-       sishw_ext.ulIOAddress = SiS_Pr.RelIO = pci_resource_start(pdev, 2) + 0x30;
-       ivideo.vga_base = (unsigned short) sishw_ext.ulIOAddress;
+       ivideo->SiS_Pr.ivideo = (void *)ivideo;
 
-       sisfb_mmio_size =  pci_resource_len(pdev, 1);
-
-       if(!sisvga_enabled) {
-          if(pci_enable_device(pdev)) {
-             kfree(sis_fb_info);
-             return -EIO;
-          }
+#ifdef CONFIG_FB_SIS_315
+       if((ivideo->SiS_Pr.ChipType == SIS_315PRO) ||
+          (ivideo->SiS_Pr.ChipType == SIS_315)) {
+               ivideo->SiS_Pr.ChipType = SIS_315H;
        }
+#endif
 
-       SiS_Pr.SiS_Backup70xx = 0xff;
-        SiS_Pr.SiS_CHOverScan = -1;
-        SiS_Pr.SiS_ChSW = FALSE;
-       SiS_Pr.SiS_UseLCDA = FALSE;
-       SiS_Pr.HaveEMI = FALSE;
-       SiS_Pr.HaveEMILCD = FALSE;
-       SiS_Pr.OverruleEMI = FALSE;
-       SiS_Pr.SiS_SensibleSR11 = FALSE;
-       SiS_Pr.SiS_MyCR63 = 0x63;
-       if(ivideo.chip >= SIS_661) {
-          SiS_Pr.SiS_SensibleSR11 = TRUE;
-          SiS_Pr.SiS_MyCR63 = 0x53;
+       if(!ivideo->sisvga_enabled) {
+               if(pci_enable_device(pdev)) {
+                       if(ivideo->nbridge) SIS_PCI_PUT_DEVICE(ivideo->nbridge);
+                       pci_set_drvdata(pdev, NULL);
+                       kfree(sis_fb_info);
+                       return -EIO;
+               }
        }
-       SiSRegInit(&SiS_Pr, sishw_ext.ulIOAddress);
 
-#ifdef CONFIG_FB_SIS_300
-       /* TW: Find PCI systems for Chrontel/GPIO communication setup */
-       if(ivideo.chip == SIS_630) {
-          int i=0;
-           do {
-             if(mychswtable[i].subsysVendor == ivideo.subsysvendor &&
-                mychswtable[i].subsysCard   == ivideo.subsysdevice) {
-                SiS_Pr.SiS_ChSW = TRUE;
-                printk(KERN_DEBUG "sisfb: Identified [%s %s] requiring Chrontel/GPIO setup\n",
-                       mychswtable[i].vendorName, mychswtable[i].cardName);
-                break;
-              }
-              i++;
-           } while(mychswtable[i].subsysVendor != 0);
-       }
-#endif
+       ivideo->video_base = pci_resource_start(pdev, 0);
+       ivideo->mmio_base  = pci_resource_start(pdev, 1);
+       ivideo->mmio_size  = pci_resource_len(pdev, 1);
+       ivideo->SiS_Pr.RelIO = pci_resource_start(pdev, 2) + 0x30;
+       ivideo->SiS_Pr.IOAddress = ivideo->vga_base = ivideo->SiS_Pr.RelIO;
 
-        outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
+       SiSRegInit(&ivideo->SiS_Pr, ivideo->SiS_Pr.IOAddress);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)         
-#ifdef MODULE
-       inSISIDXREG(SISCR,0x34,reg);
-       if((reg & 0x80) && (reg != 0xff)) {
-          if((sisbios_mode[sisfb_mode_idx].mode_no) != 0xFF) {
-             printk(KERN_INFO "sisfb: Cannot initialize display mode, X server is active\n");
-             kfree(sis_fb_info);
-             return -EBUSY;
-          }
+#ifdef CONFIG_FB_SIS_300
+       /* Find PCI systems for Chrontel/GPIO communication setup */
+       if(ivideo->chip == SIS_630) {
+               i = 0;
+               do {
+                       if(mychswtable[i].subsysVendor == ivideo->subsysvendor &&
+                          mychswtable[i].subsysCard   == ivideo->subsysdevice) {
+                               ivideo->SiS_Pr.SiS_ChSW = TRUE;
+                               printk(KERN_DEBUG "sisfb: Identified [%s %s] "
+                                       "requiring Chrontel/GPIO setup\n",
+                                       mychswtable[i].vendorName,
+                                       mychswtable[i].cardName);
+                               ivideo->lpcdev = SIS_PCI_GET_DEVICE(PCI_VENDOR_ID_SI, 0x0008, NULL);
+                               break;
+                       }
+                       i++;
+               } while(mychswtable[i].subsysVendor != 0);
        }
-#endif 
 #endif
 
-       if (sisvga_engine == SIS_315_VGA) {
-               switch (ivideo.chip) {
-                  case SIS_315H:
-                  case SIS_315:
-                  case SIS_330:
-                       sishw_ext.bIntegratedMMEnabled = TRUE;
-                       break;
-                  case SIS_550:
-                  case SIS_650:
-                  case SIS_740:
-                  case SIS_661:
-                  case SIS_741:
-                  case SIS_660:
-                  case SIS_760:
-                       sishw_ext.bIntegratedMMEnabled = TRUE;
-                       break;
-                  default:
-                       break;
-               }
-       } else if (sisvga_engine == SIS_300_VGA) {
-               if (ivideo.chip == SIS_300) {
-                       sishw_ext.bIntegratedMMEnabled = TRUE;
-               } else {
-                       inSISIDXREG(SISSR, IND_SIS_SCRATCH_REG_1A, reg);
-                       if (reg & SIS_SCRATCH_REG_1A_MASK)
-                               sishw_ext.bIntegratedMMEnabled = TRUE;
-                       else
-                               sishw_ext.bIntegratedMMEnabled = FALSE;
-               }
+#ifdef CONFIG_FB_SIS_315
+       if((ivideo->chip == SIS_760) && (ivideo->nbridge)) {
+               ivideo->lpcdev = SIS_PCI_GET_SLOT(ivideo->nbridge->bus, (2 << 3));
        }
+#endif
 
-       if(sisfb_userom) {
-           sishw_ext.pjVirtualRomBase = sis_find_rom();
-           if(sishw_ext.pjVirtualRomBase) {
-               printk(KERN_INFO "sisfb: Video ROM found and mapped to %p\n",
-                       sishw_ext.pjVirtualRomBase);
-               sishw_ext.UseROM = TRUE;
-           } else {
-               sishw_ext.UseROM = FALSE;
-               printk(KERN_INFO "sisfb: Video ROM not found\n");
-           }
-       } else {
-           sishw_ext.pjVirtualRomBase = NULL;
-           sishw_ext.UseROM = FALSE;
-           printk(KERN_INFO "sisfb: Video ROM usage disabled\n");
-       }
-       sishw_ext.bSkipDramSizing = 0;
-       sishw_ext.pQueryVGAConfigSpace = &sisfb_query_VGA_config_space;
-       sishw_ext.pQueryNorthBridgeSpace = &sisfb_query_north_bridge_space;
-
-        /* Find systems for special custom timing */
-       if(SiS_Pr.SiS_CustomT == CUT_NONE) {
-          int i=0, j;
-          unsigned char *biosver = NULL;
-           unsigned char *biosdate = NULL;
-          BOOLEAN footprint;
-          unsigned long chksum = 0;
-
-          if(sishw_ext.UseROM) {
-             biosver = sishw_ext.pjVirtualRomBase + 0x06;
-             biosdate = sishw_ext.pjVirtualRomBase + 0x2c;
-              for(i=0; i<32768; i++) chksum += sishw_ext.pjVirtualRomBase[i];
-          }
+       outSISIDXREG(SISSR, 0x05, 0x86);
 
-          i=0;
-           do {
-             if( (mycustomttable[i].chipID == ivideo.chip) &&
-                 ((!strlen(mycustomttable[i].biosversion)) ||
-                  (sishw_ext.UseROM &&
-                  (!strncmp(mycustomttable[i].biosversion, biosver, strlen(mycustomttable[i].biosversion))))) &&
-                 ((!strlen(mycustomttable[i].biosdate)) ||
-                  (sishw_ext.UseROM &&
-                  (!strncmp(mycustomttable[i].biosdate, biosdate, strlen(mycustomttable[i].biosdate))))) &&
-                 ((!mycustomttable[i].bioschksum) ||
-                  (sishw_ext.UseROM &&
-                  (mycustomttable[i].bioschksum == chksum)))   &&
-                 (mycustomttable[i].pcisubsysvendor == ivideo.subsysvendor) &&
-                 (mycustomttable[i].pcisubsyscard == ivideo.subsysdevice) ) {
-                footprint = TRUE;
-                for(j=0; j<5; j++) {
-                   if(mycustomttable[i].biosFootprintAddr[j]) {
-                      if(sishw_ext.UseROM) {
-                         if(sishw_ext.pjVirtualRomBase[mycustomttable[i].biosFootprintAddr[j]] !=
-                               mycustomttable[i].biosFootprintData[j])
-                         footprint = FALSE;
-                      } else footprint = FALSE;
-                   }
-                }
-                if(footprint) {
-                   SiS_Pr.SiS_CustomT = mycustomttable[i].SpecialID;
-                   printk(KERN_DEBUG "sisfb: Identified [%s %s], special timing applies\n",
-                       mycustomttable[i].vendorName,
-                       mycustomttable[i].cardName);
-                   printk(KERN_DEBUG "sisfb: [specialtiming parameter name: %s]\n",
-                       mycustomttable[i].optionName);
-                   break;
-                 }
-             }
-              i++;
-           } while(mycustomttable[i].chipID);
+       if( (!ivideo->sisvga_enabled)
+#if !defined(__i386__) && !defined(__x86_64__)
+                             || (sisfb_resetcard)
+#endif
+                                                  ) {
+               for(i = 0x30; i <= 0x3f; i++) {
+                       outSISIDXREG(SISCR, i, 0x00);
+               }
        }
 
-#ifdef CONFIG_FB_SIS_300
-       /* Mode numbers for 1280x768 are different for 300 and 315 series */
-       if(sisvga_engine == SIS_300_VGA) {
-               sisbios_mode[MODEINDEX_1280x768].mode_no = 0x55;
-               sisbios_mode[MODEINDEX_1280x768+1].mode_no = 0x5a;
-               sisbios_mode[MODEINDEX_1280x768+2].mode_no = 0x5b;
-               sisbios_mode[MODEINDEX_1280x768+3].mode_no = 0x5b;
-       }
+       /* Find out about current video mode */
+       ivideo->modeprechange = 0x03;
+       inSISIDXREG(SISCR, 0x34, reg);
+       if(reg & 0x7f) {
+               ivideo->modeprechange = reg & 0x7f;
+       } else if(ivideo->sisvga_enabled) {
+#if defined(__i386__) || defined(__x86_64__)
+               unsigned char SIS_IOTYPE2 *tt = ioremap(0x400, 0x100);
+               if(tt) {
+                       ivideo->modeprechange = readb(tt + 0x49);
+                       iounmap(tt);
+               }
 #endif
+       }
 
-       sishw_ext.pSR = vmalloc(sizeof(SIS_DSReg) * SR_BUFFER_SIZE);
-       if (sishw_ext.pSR == NULL) {
-               printk(KERN_ERR "sisfb: Fatal error: Allocating SRReg space failed.\n");
-               kfree(sis_fb_info);
-               return -ENODEV;
+       /* Search and copy ROM image */
+       ivideo->bios_abase = NULL;
+       ivideo->SiS_Pr.VirtualRomBase = NULL;
+       ivideo->SiS_Pr.UseROM = FALSE;
+       ivideo->haveXGIROM = ivideo->SiS_Pr.SiS_XGIROM = FALSE;
+       if(ivideo->sisfb_userom) {
+               ivideo->SiS_Pr.VirtualRomBase = sisfb_find_rom(pdev);
+               ivideo->bios_abase = ivideo->SiS_Pr.VirtualRomBase;
+               ivideo->SiS_Pr.UseROM = (ivideo->SiS_Pr.VirtualRomBase) ? TRUE : FALSE;
+               printk(KERN_INFO "sisfb: Video ROM %sfound\n",
+                       ivideo->SiS_Pr.UseROM ? "" : "not ");
+               if((ivideo->SiS_Pr.UseROM) && (ivideo->chip >= XGI_20)) {
+                  ivideo->SiS_Pr.UseROM = FALSE;
+                  ivideo->haveXGIROM = ivideo->SiS_Pr.SiS_XGIROM = TRUE;
+                  if( (ivideo->revision_id == 2) &&
+                      (!(ivideo->bios_abase[0x1d1] & 0x01)) ) {
+                       ivideo->SiS_Pr.DDCPortMixup = TRUE;
+                  }
+               }
+       } else {
+               printk(KERN_INFO "sisfb: Video ROM usage disabled\n");
        }
-       sishw_ext.pSR[0].jIdx = sishw_ext.pSR[0].jVal = 0xFF;
 
-       sishw_ext.pCR = vmalloc(sizeof(SIS_DSReg) * CR_BUFFER_SIZE);
-       if (sishw_ext.pCR == NULL) {
-               vfree(sishw_ext.pSR);
-               printk(KERN_ERR "sisfb: Fatal error: Allocating CRReg space failed.\n");
-               kfree(sis_fb_info);
-               return -ENODEV;
+       /* Find systems for special custom timing */
+       if(ivideo->SiS_Pr.SiS_CustomT == CUT_NONE) {
+               sisfb_detect_custom_timing(ivideo);
        }
-       sishw_ext.pCR[0].jIdx = sishw_ext.pCR[0].jVal = 0xFF;
 
+       /* POST card in case this has not been done by the BIOS */
+       if( (!ivideo->sisvga_enabled)
+#if !defined(__i386__) && !defined(__x86_64__)
+                            || (sisfb_resetcard)
+#endif
+                                                ) {
 #ifdef CONFIG_FB_SIS_300
-       if(sisvga_engine == SIS_300_VGA) {
-               if(!sisvga_enabled) {
-                       /* Mapping Max FB Size for 300 Init */
-                       sishw_ext.pjVideoMemoryAddress = ioremap(ivideo.video_base, 0x4000000);
-                       if((sisfb_mode_idx < 0) || ((sisbios_mode[sisfb_mode_idx].mode_no) != 0xFF)) {
-                               outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
+               if(ivideo->sisvga_engine == SIS_300_VGA) {
+                       if(ivideo->chip == SIS_300) {
+                               sisfb_post_sis300(pdev);
+                               ivideo->sisfb_can_post = 1;
                        }
                }
-               if(sisfb_get_dram_size_300()) {
-                       vfree(sishw_ext.pSR);
-                       vfree(sishw_ext.pCR);
-                       printk(KERN_ERR "sisfb: Fatal error: Unable to determine RAM size\n");
-                       kfree(sis_fb_info);
-                       return -ENODEV;
-               }
-       }
 #endif
 
 #ifdef CONFIG_FB_SIS_315
-       if (sisvga_engine == SIS_315_VGA) {
-               if (!sisvga_enabled) {
-                       /* Mapping Max FB Size for 315 Init */
-                       sishw_ext.pjVideoMemoryAddress = ioremap(ivideo.video_base, 0x8000000);
-                       if((sisfb_mode_idx < 0) || ((sisbios_mode[sisfb_mode_idx].mode_no) != 0xFF)) {
-                               outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
-                               sishw_ext.bSkipDramSizing = TRUE;
-                               sishw_ext.pSR[0].jIdx = 0x13;
-                               sishw_ext.pSR[1].jIdx = 0x14;
-                               sishw_ext.pSR[2].jIdx = 0xFF;
-                               inSISIDXREG(SISSR, 0x13, sishw_ext.pSR[0].jVal);
-                               inSISIDXREG(SISSR, 0x14, sishw_ext.pSR[1].jVal);
-                               sishw_ext.pSR[2].jVal = 0xFF;
+               if(ivideo->sisvga_engine == SIS_315_VGA) {
+                       int result = 1;
+               /*      if((ivideo->chip == SIS_315H)   ||
+                          (ivideo->chip == SIS_315)    ||
+                          (ivideo->chip == SIS_315PRO) ||
+                          (ivideo->chip == SIS_330)) {
+                               sisfb_post_sis315330(pdev);
+                       } else */ if(ivideo->chip == XGI_20) {
+                               result = sisfb_post_xgi(pdev);
+                               ivideo->sisfb_can_post = 1;
+                       } else if((ivideo->chip == XGI_40) && ivideo->haveXGIROM) {
+                               result = sisfb_post_xgi(pdev);
+                               ivideo->sisfb_can_post = 1;
+                       } else {
+                               printk(KERN_INFO "sisfb: Card is not "
+                                       "POSTed and sisfb can't do this either.\n");
+                       }
+                       if(!result) {
+                               printk(KERN_ERR "sisfb: Failed to POST card\n");
+                               ret = -ENODEV;
+                               goto error_3;
                        }
                }
-               if(sisfb_get_dram_size_315()) {
-                       vfree(sishw_ext.pSR);
-                       vfree(sishw_ext.pCR);
-                       printk(KERN_INFO "sisfb: Fatal error: Unable to determine RAM size.\n");
-                       kfree(sis_fb_info);
-                       return -ENODEV;
-               }
-       }
 #endif
+       }
 
-       if((sisfb_mode_idx < 0) || ((sisbios_mode[sisfb_mode_idx].mode_no) != 0xFF)) { 
+       ivideo->sisfb_card_posted = 1;
 
-               /* Enable PCI_LINEAR_ADDRESSING and MMIO_ENABLE  */
-               orSISIDXREG(SISSR, IND_SIS_PCI_ADDRESS_SET, (SIS_PCI_ADDR_ENABLE | SIS_MEM_MAP_IO_ENABLE));
+       /* Find out about RAM size */
+       if(sisfb_get_dram_size(ivideo)) {
+               printk(KERN_INFO "sisfb: Fatal error: Unable to determine VRAM size.\n");
+               ret = -ENODEV;
+               goto error_3;
+       }
 
-                /* Enable 2D accelerator engine */
-               orSISIDXREG(SISSR, IND_SIS_MODULE_ENABLE, SIS_ENABLE_2D);
 
+       /* Enable PCI addressing and MMIO */
+       if((ivideo->sisfb_mode_idx < 0) ||
+          ((sisbios_mode[ivideo->sisfb_mode_idx].mode_no[ivideo->mni]) != 0xFF)) {
+               /* Enable PCI_LINEAR_ADDRESSING and MMIO_ENABLE  */
+               orSISIDXREG(SISSR, IND_SIS_PCI_ADDRESS_SET, (SIS_PCI_ADDR_ENABLE | SIS_MEM_MAP_IO_ENABLE));
+               /* Enable 2D accelerator engine */
+               orSISIDXREG(SISSR, IND_SIS_MODULE_ENABLE, SIS_ENABLE_2D);
        }
 
-       sishw_ext.ulVideoMemorySize = ivideo.video_size;
-
-       if(sisvga_engine == SIS_300_VGA) sisfb_pdc &= 0x3c;
-       if(sisfb_pdc) {
-           SiS_Pr.PDC = sisfb_pdc;
-       } else {
-           SiS_Pr.PDC = 0;
+       if(sisfb_pdc != 0xff) {
+               if(ivideo->sisvga_engine == SIS_300_VGA)
+                       sisfb_pdc &= 0x3c;
+               else
+                       sisfb_pdc &= 0x1f;
+               ivideo->SiS_Pr.PDC = sisfb_pdc;
+       }
+#ifdef CONFIG_FB_SIS_315
+       if(ivideo->sisvga_engine == SIS_315_VGA) {
+               if(sisfb_pdca != 0xff)
+                       ivideo->SiS_Pr.PDCA = sisfb_pdca & 0x1f;
        }
+#endif
 
-       if(!request_mem_region(ivideo.video_base, ivideo.video_size, "sisfb FB")) {
-               printk(KERN_ERR "sisfb: Fatal error: Unable to reserve frame buffer memory\n");
+       if(!request_mem_region(ivideo->video_base, ivideo->video_size, "sisfb FB")) {
+               printk(KERN_ERR "sisfb: Fatal error: Unable to reserve %dMB framebuffer memory\n",
+                               (int)(ivideo->video_size >> 20));
                printk(KERN_ERR "sisfb: Is there another framebuffer driver active?\n");
-               vfree(sishw_ext.pSR);
-               vfree(sishw_ext.pCR);
-               kfree(sis_fb_info);
-               return -ENODEV;
+               ret = -ENODEV;
+               goto error_3;
        }
 
-       if(!request_mem_region(ivideo.mmio_base, sisfb_mmio_size, "sisfb MMIO")) {
+       if(!request_mem_region(ivideo->mmio_base, ivideo->mmio_size, "sisfb MMIO")) {
                printk(KERN_ERR "sisfb: Fatal error: Unable to reserve MMIO region\n");
-               release_mem_region(ivideo.video_base, ivideo.video_size);
-               vfree(sishw_ext.pSR);
-               vfree(sishw_ext.pCR);
+               ret = -ENODEV;
+               goto error_2;
+       }
+
+       ivideo->video_vbase = ioremap(ivideo->video_base, ivideo->video_size);
+       ivideo->SiS_Pr.VideoMemoryAddress = ivideo->video_vbase;
+       if(!ivideo->video_vbase) {
+               printk(KERN_ERR "sisfb: Fatal error: Unable to map framebuffer memory\n");
+               ret = -ENODEV;
+               goto error_1;
+       }
+
+       ivideo->mmio_vbase = ioremap(ivideo->mmio_base, ivideo->mmio_size);
+       if(!ivideo->mmio_vbase) {
+               printk(KERN_ERR "sisfb: Fatal error: Unable to map MMIO region\n");
+               ret = -ENODEV;
+error_0:       iounmap(ivideo->video_vbase);
+error_1:       release_mem_region(ivideo->video_base, ivideo->video_size);
+error_2:       release_mem_region(ivideo->mmio_base, ivideo->mmio_size);
+error_3:       vfree(ivideo->bios_abase);
+               if(ivideo->lpcdev)
+                       SIS_PCI_PUT_DEVICE(ivideo->lpcdev);
+               if(ivideo->nbridge)
+                       SIS_PCI_PUT_DEVICE(ivideo->nbridge);
+               pci_set_drvdata(pdev, NULL);
+               if(!ivideo->sisvga_enabled)
+                       pci_disable_device(pdev);
                kfree(sis_fb_info);
-               return -ENODEV;
+               return ret;
        }
 
-       ivideo.video_vbase = sishw_ext.pjVideoMemoryAddress = ioremap(ivideo.video_base, ivideo.video_size);
-       if(!ivideo.video_vbase) {
-               printk(KERN_ERR "sisfb: Fatal error: Unable to map frame buffer memory\n");
-               release_mem_region(ivideo.video_base, ivideo.video_size);
-               release_mem_region(ivideo.mmio_base, sisfb_mmio_size);
-               vfree(sishw_ext.pSR);
-               vfree(sishw_ext.pCR);
-               kfree(sis_fb_info);
-               return -ENODEV;
-       }
+       printk(KERN_INFO "sisfb: Video RAM at 0x%lx, mapped to 0x%lx, size %ldk\n",
+               ivideo->video_base, (unsigned long)ivideo->video_vbase, ivideo->video_size / 1024);
 
-       ivideo.mmio_vbase = ioremap(ivideo.mmio_base, sisfb_mmio_size);
-       if(!ivideo.mmio_vbase) {
-               printk(KERN_ERR "sisfb: Fatal error: Unable to map MMIO region\n");
-               iounmap(ivideo.video_vbase);
-               release_mem_region(ivideo.video_base, ivideo.video_size);
-               release_mem_region(ivideo.mmio_base, sisfb_mmio_size);
-               vfree(sishw_ext.pSR);
-               vfree(sishw_ext.pCR);
-               kfree(sis_fb_info);
-               return -ENODEV;
+       if(ivideo->video_offset) {
+               printk(KERN_INFO "sisfb: Viewport offset %ldk\n",
+                       ivideo->video_offset / 1024);
        }
 
-       printk(KERN_INFO "sisfb: Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",
-               ivideo.video_base, ivideo.video_vbase, ivideo.video_size / 1024);
+       printk(KERN_INFO "sisfb: MMIO at 0x%lx, mapped to 0x%lx, size %ldk\n",
+               ivideo->mmio_base, (unsigned long)ivideo->mmio_vbase, ivideo->mmio_size / 1024);
 
-       printk(KERN_INFO "sisfb: MMIO at 0x%lx, mapped to 0x%p, size %ldk\n",
-               ivideo.mmio_base, ivideo.mmio_vbase, sisfb_mmio_size / 1024);
 
-       if(sisfb_heap_init()) {
-               printk(KERN_WARNING "sisfb: Failed to initialize offscreen memory heap\n");
+       /* Determine the size of the command queue */
+       if(ivideo->sisvga_engine == SIS_300_VGA) {
+               ivideo->cmdQueueSize = TURBO_QUEUE_AREA_SIZE;
+       } else {
+               if(ivideo->chip == XGI_20) {
+                       ivideo->cmdQueueSize = COMMAND_QUEUE_AREA_SIZE_Z7;
+               } else {
+                       ivideo->cmdQueueSize = COMMAND_QUEUE_AREA_SIZE;
+               }
        }
 
-       ivideo.mtrr = (unsigned int) 0;
+       /* Engines are no longer initialized here; this is
+        * now done after the first mode-switch (if the
+        * submitted var has its acceleration flags set).
+        */
 
-       ivideo.vbflags = 0;
+       /* Calculate the base of the (unused) hw cursor */
+       ivideo->hwcursor_vbase = ivideo->video_vbase
+                                + ivideo->video_size
+                                - ivideo->cmdQueueSize
+                                - ivideo->hwcursor_size;
+       ivideo->caps |= HW_CURSOR_CAP;
 
-       if((sisfb_mode_idx < 0) || ((sisbios_mode[sisfb_mode_idx].mode_no) != 0xFF)) {
+       /* Initialize offscreen memory manager */
+       if((ivideo->havenoheap = sisfb_heap_init(ivideo))) {
+               printk(KERN_WARNING "sisfb: Failed to initialize offscreen memory heap\n");
+       }
 
-               sishw_ext.ujVBChipID = VB_CHIP_UNKNOWN;
-               sishw_ext.Is301BDH = FALSE;
-               sishw_ext.usExternalChip = 0;
+       /* Used for clearing the screen only, therefore respect our mem limit */
+       ivideo->SiS_Pr.VideoMemoryAddress += ivideo->video_offset;
+       ivideo->SiS_Pr.VideoMemorySize = ivideo->sisfb_mem;
 
-               sisfb_sense_crt1();
+       ivideo->mtrr = -1;
 
-               sisfb_get_VB_type();
+       ivideo->vbflags = 0;
+       ivideo->lcddefmodeidx = DEFAULT_LCDMODE;
+       ivideo->tvdefmodeidx  = DEFAULT_TVMODE;
+       ivideo->defmodeidx    = DEFAULT_MODE;
 
-               if(ivideo.vbflags & VB_VIDEOBRIDGE) {
-                       sisfb_detect_VB_connect();
+       ivideo->newrom = 0;
+       if(ivideo->chip < XGI_20) {
+               if(ivideo->bios_abase) {
+                       ivideo->newrom = SiSDetermineROMLayout661(&ivideo->SiS_Pr);
                }
+       }
 
-               ivideo.currentvbflags = ivideo.vbflags & VB_VIDEOBRIDGE;
+       if((ivideo->sisfb_mode_idx < 0) ||
+          ((sisbios_mode[ivideo->sisfb_mode_idx].mode_no[ivideo->mni]) != 0xFF)) {
 
-               if(ivideo.vbflags & VB_VIDEOBRIDGE) {
-                  if(sisfb_crt2type != -1) {
-                     if((sisfb_crt2type == CRT2_LCD) && (ivideo.vbflags & CRT2_LCD)) {
-                        ivideo.currentvbflags |= CRT2_LCD;
-                     } else if(sisfb_crt2type != CRT2_LCD) {
-                        ivideo.currentvbflags |= sisfb_crt2type;
-                     }
-                  } else {
-                     /* Chrontel 700x TV detection often unreliable, therefore use a
-                      * different default order on such machines
-                      */
-                     if((sisvga_engine == SIS_300_VGA) && (ivideo.vbflags & VB_CHRONTEL)) {
-                        if(ivideo.vbflags & CRT2_LCD)      ivideo.currentvbflags |= CRT2_LCD;
-                        else if(ivideo.vbflags & CRT2_TV)  ivideo.currentvbflags |= CRT2_TV;
-                        else if(ivideo.vbflags & CRT2_VGA) ivideo.currentvbflags |= CRT2_VGA;
-                     } else {
-                        if(ivideo.vbflags & CRT2_TV)       ivideo.currentvbflags |= CRT2_TV;
-                        else if(ivideo.vbflags & CRT2_LCD) ivideo.currentvbflags |= CRT2_LCD;
-                        else if(ivideo.vbflags & CRT2_VGA) ivideo.currentvbflags |= CRT2_VGA;
-                     }
-                  }
-               }
+               sisfb_sense_crt1(ivideo);
 
-               if(ivideo.vbflags & CRT2_LCD) {
-                  inSISIDXREG(SISCR, IND_SIS_LCD_PANEL, reg);
-                  reg &= 0x0f;
-                  if(sisvga_engine == SIS_300_VGA) {
-                     sishw_ext.ulCRT2LCDType = sis300paneltype[reg];
-                  } else {
-                     sishw_ext.ulCRT2LCDType = sis310paneltype[reg];
-                  }
-               }
-               
-               sisfb_detectedpdc = 0;
+               sisfb_get_VB_type(ivideo);
 
-#ifdef CONFIG_FB_SIS_300
-                /* Save the current PanelDelayCompensation if the LCD is currently used */
-               if(sisvga_engine == SIS_300_VGA) {
-                  if(ivideo.vbflags & (VB_LVDS | VB_30xBDH)) {
-                      int tmp;
-                      inSISIDXREG(SISCR,0x30,tmp);
-                      if(tmp & 0x20) {
-                         /* Currently on LCD? If yes, read current pdc */
-                         inSISIDXREG(SISPART1,0x13,sisfb_detectedpdc);
-                         sisfb_detectedpdc &= 0x3c;
-                         if(SiS_Pr.PDC == 0) {
-                            /* Let option override detection */
-                            SiS_Pr.PDC = sisfb_detectedpdc;
-                         }
-                         printk(KERN_INFO
-                                "sisfb: Detected LCD PanelDelayCompensation %d\n",
-                                sisfb_detectedpdc);
-                      }
-                      if((SiS_Pr.PDC) && (SiS_Pr.PDC != sisfb_detectedpdc)) {
-                         printk(KERN_INFO
-                                "sisfb: Using LCD PanelDelayCompensation %d\n",
-                                SiS_Pr.PDC);
-                      }
-                  }
+               if(ivideo->vbflags2 & VB2_VIDEOBRIDGE) {
+                       sisfb_detect_VB_connect(ivideo);
                }
-#endif
-
-               sisfb_detectedlcda = 0xff;
 
-#ifdef CONFIG_FB_SIS_315
+               ivideo->currentvbflags = ivideo->vbflags & (VB_VIDEOBRIDGE | TV_STANDARD);
 
-               if(sisvga_engine == SIS_315_VGA) {
-                  /* Save PDC */
-                  if(ivideo.vbflags & (VB_301LV | VB_302LV | VB_302ELV)) {
-                     int tmp;
-                     inSISIDXREG(SISCR,0x30,tmp);
-                     if(tmp & 0x20) {
-                        /* Currently on LCD? If yes, read current pdc */
-                        inSISIDXREG(SISPART1,0x2D,sisfb_detectedpdc);
-                        if(SiS_Pr.PDC == 0) {
-                           /* Let option override detection */
-                           SiS_Pr.PDC = sisfb_detectedpdc;
-                        }
-                        printk(KERN_INFO
-                               "sisfb: Detected LCD PanelDelayCompensation %d\n",
-                                sisfb_detectedpdc);
-                     }
-                     if((SiS_Pr.PDC) && (SiS_Pr.PDC != sisfb_detectedpdc)) {
-                        printk(KERN_INFO
-                                "sisfb: Using LCD PanelDelayCompensation %d\n",
-                                SiS_Pr.PDC);
-                     }
-                     /* Save EMI */
-                     if(ivideo.vbflags & (VB_302LV | VB_302ELV)) {
-                        inSISIDXREG(SISPART4,0x30,SiS_Pr.EMI_30);
-                        inSISIDXREG(SISPART4,0x31,SiS_Pr.EMI_31);
-                        inSISIDXREG(SISPART4,0x32,SiS_Pr.EMI_32);
-                        inSISIDXREG(SISPART4,0x33,SiS_Pr.EMI_33);
-                        SiS_Pr.HaveEMI = TRUE;
-                        if(tmp & 0x20) SiS_Pr.HaveEMILCD = TRUE;
-                     }
-                  }
+               /* Decide on which CRT2 device to use */
+               if(ivideo->vbflags2 & VB2_VIDEOBRIDGE) {
+                       if(ivideo->sisfb_crt2type != -1) {
+                               if((ivideo->sisfb_crt2type == CRT2_LCD) &&
+                                  (ivideo->vbflags & CRT2_LCD)) {
+                                       ivideo->currentvbflags |= CRT2_LCD;
+                               } else if(ivideo->sisfb_crt2type != CRT2_LCD) {
+                                       ivideo->currentvbflags |= ivideo->sisfb_crt2type;
+                               }
+                       } else {
+                               /* Chrontel 700x TV detection often unreliable, therefore
+                                * use a different default order on such machines
+                                */
+                               if((ivideo->sisvga_engine == SIS_300_VGA) &&
+                                  (ivideo->vbflags2 & VB2_CHRONTEL)) {
+                                       if(ivideo->vbflags & CRT2_LCD)
+                                               ivideo->currentvbflags |= CRT2_LCD;
+                                       else if(ivideo->vbflags & CRT2_TV)
+                                               ivideo->currentvbflags |= CRT2_TV;
+                                       else if(ivideo->vbflags & CRT2_VGA)
+                                               ivideo->currentvbflags |= CRT2_VGA;
+                               } else {
+                                       if(ivideo->vbflags & CRT2_TV)
+                                               ivideo->currentvbflags |= CRT2_TV;
+                                       else if(ivideo->vbflags & CRT2_LCD)
+                                               ivideo->currentvbflags |= CRT2_LCD;
+                                       else if(ivideo->vbflags & CRT2_VGA)
+                                               ivideo->currentvbflags |= CRT2_VGA;
+                               }
+                       }
+               }
 
-                  /* Try to find about LCDA */
-                  if(ivideo.vbflags & (VB_301C | VB_302B | VB_301LV | VB_302LV | VB_302ELV)) {
-                     int tmp;
-                     inSISIDXREG(SISCR,0x34,tmp);
-                     if((tmp <= 0x13) || (tmp == 0xff)) {
-                        /* Currently on LCDA? (Some BIOSes leave CR38) */
-                        inSISIDXREG(SISCR,0x38,tmp);
-                        if((tmp & 0x03) == 0x03)  SiS_Pr.SiS_UseLCDA = TRUE;
-                        else {
-                           /* Currently on LCDA? (Some newer BIOSes set D0 in CR35) */
-                           inSISIDXREG(SISCR,0x35,tmp);
-                           if(tmp & 0x01) SiS_Pr.SiS_UseLCDA = TRUE;
-                           else {
-                              /* Currently on LCD? If so, we can find out
-                               * by peeking the mode register
-                               */
-                              inSISIDXREG(SISCR,0x30,tmp);
-                              if(tmp & 0x20) {
-                                 inSISIDXREG(SISPART1,0x13,tmp);
-                                 if(tmp & 0x04) SiS_Pr.SiS_UseLCDA = TRUE;
-                              }
-                           }
-                        }
-                     }
-                     if(SiS_Pr.SiS_UseLCDA) {
-                        sisfb_detectedlcda = 0x03;
-                        printk(KERN_DEBUG
-                               "sisfb: Bridge uses LCDA for low resolution and text modes\n");
-                     }
-                 }
+               if(ivideo->vbflags & CRT2_LCD) {
+                       sisfb_detect_lcd_type(ivideo);
                }
-#endif
 
-               if (!sisfb_crt1off) {
-                       sisfb_handle_ddc(&sisfb_thismonitor, 0);
+               sisfb_save_pdc_emi(ivideo);
+
+               if(!ivideo->sisfb_crt1off) {
+                       sisfb_handle_ddc(ivideo, &ivideo->sisfb_thismonitor, 0);
                } else {
-                       if ((ivideo.vbflags & (VB_301|VB_301B|VB_301C|VB_302B)) &&
-                           (ivideo.vbflags & (CRT2_VGA | CRT2_LCD))) {
-                               sisfb_handle_ddc(&sisfb_thismonitor, 1);
-                       }
+                       if((ivideo->vbflags2 & VB2_SISTMDSBRIDGE) &&
+                          (ivideo->vbflags & (CRT2_VGA | CRT2_LCD))) {
+                               sisfb_handle_ddc(ivideo, &ivideo->sisfb_thismonitor, 1);
+                       }
                }
 
-               if (sisfb_mode_idx >= 0)
-                       sisfb_mode_idx = sisfb_validate_mode(sisfb_mode_idx, ivideo.currentvbflags);
+               if(ivideo->sisfb_mode_idx >= 0) {
+                       int bu = ivideo->sisfb_mode_idx;
+                       ivideo->sisfb_mode_idx = sisfb_validate_mode(ivideo,
+                                       ivideo->sisfb_mode_idx, ivideo->currentvbflags);
+                       if(bu != ivideo->sisfb_mode_idx) {
+                               printk(KERN_ERR "Mode %dx%dx%d failed validation\n",
+                                       sisbios_mode[bu].xres,
+                                       sisbios_mode[bu].yres,
+                                       sisbios_mode[bu].bpp);
+                       }
+               }
 
-               if (sisfb_mode_idx < 0) {
-                       switch (ivideo.currentvbflags & VB_DISPTYPE_DISP2) {
+               if(ivideo->sisfb_mode_idx < 0) {
+                       switch(ivideo->currentvbflags & VB_DISPTYPE_DISP2) {
                           case CRT2_LCD:
-                               sisfb_mode_idx = DEFAULT_LCDMODE;
+                               ivideo->sisfb_mode_idx = ivideo->lcddefmodeidx;
                                break;
                           case CRT2_TV:
-                               sisfb_mode_idx = DEFAULT_TVMODE;
+                               ivideo->sisfb_mode_idx = ivideo->tvdefmodeidx;
                                break;
                           default:
-                               sisfb_mode_idx = DEFAULT_MODE;
+                               ivideo->sisfb_mode_idx = ivideo->defmodeidx;
                                break;
                        }
                }
 
-               sisfb_mode_no = sisbios_mode[sisfb_mode_idx].mode_no;
+               ivideo->mode_no = sisbios_mode[ivideo->sisfb_mode_idx].mode_no[ivideo->mni];
 
-               if (ivideo.refresh_rate != 0)
-                       sisfb_search_refresh_rate(ivideo.refresh_rate, sisfb_mode_idx);
+               if(ivideo->refresh_rate != 0) {
+                       sisfb_search_refresh_rate(ivideo, ivideo->refresh_rate,
+                                               ivideo->sisfb_mode_idx);
+               }
 
-               if (sisfb_rate_idx == 0) {
-                       sisfb_rate_idx = sisbios_mode[sisfb_mode_idx].rate_idx;
-                       ivideo.refresh_rate = 60;
+               if(ivideo->rate_idx == 0) {
+                       ivideo->rate_idx = sisbios_mode[ivideo->sisfb_mode_idx].rate_idx;
+                       ivideo->refresh_rate = 60;
                }
 
-               if (sisfb_thismonitor.datavalid) {
-                       if(!sisfb_verify_rate(&sisfb_thismonitor, sisfb_mode_idx,
-                                             sisfb_rate_idx, ivideo.refresh_rate)) {
-                               printk(KERN_INFO "sisfb: WARNING: Refresh rate exceeds monitor specs!\n");
+               if(ivideo->sisfb_thismonitor.datavalid) {
+                       if(!sisfb_verify_rate(ivideo, &ivideo->sisfb_thismonitor,
+                                               ivideo->sisfb_mode_idx,
+                                               ivideo->rate_idx,
+                                               ivideo->refresh_rate)) {
+                               printk(KERN_INFO "sisfb: WARNING: Refresh rate "
+                                                       "exceeds monitor specs!\n");
                        }
                }
 
-               ivideo.video_bpp = sisbios_mode[sisfb_mode_idx].bpp;
-               ivideo.video_vwidth = ivideo.video_width = sisbios_mode[sisfb_mode_idx].xres;
-               ivideo.video_vheight = ivideo.video_height = sisbios_mode[sisfb_mode_idx].yres;
-               ivideo.org_x = ivideo.org_y = 0;
-               ivideo.video_linelength = ivideo.video_width * (ivideo.video_bpp >> 3);
+               ivideo->video_bpp = sisbios_mode[ivideo->sisfb_mode_idx].bpp;
+               ivideo->video_width = sisbios_mode[ivideo->sisfb_mode_idx].xres;
+               ivideo->video_height = sisbios_mode[ivideo->sisfb_mode_idx].yres;
+
+               sisfb_set_vparms(ivideo);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+
+               /* ---------------- For 2.4: Now switch the mode ------------------ */
 
-               sisfb_set_vparms();
-               
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) 
+               printk(KERN_INFO "sisfb: Setting mode %dx%dx%d (%dHz)\n",
+                       ivideo->video_width, ivideo->video_height, ivideo->video_bpp,
+                       ivideo->refresh_rate);
 
-               /* ---------------- For 2.4: Now switch the mode ------------------ */          
-               
-               printk(KERN_INFO "sisfb: Mode is %dx%dx%d (%dHz)\n",
-                       ivideo.video_width, ivideo.video_height, ivideo.video_bpp,
-                       ivideo.refresh_rate);
+               /* Determine whether or not acceleration is to be
+                * used. Need to know before pre/post_set_mode()
+                */
+               ivideo->accel = 0;
+               ivideo->default_var.accel_flags &= ~FB_ACCELF_TEXT;
+               if(ivideo->sisfb_accel) {
+                       ivideo->accel = -1;
+                       ivideo->default_var.accel_flags |= FB_ACCELF_TEXT;
+               }
 
-               sisfb_pre_setmode();
+               /* Now switch the mode */
+               sisfb_pre_setmode(ivideo);
 
-               if (SiSSetMode(&SiS_Pr, &sishw_ext, sisfb_mode_no) == 0) {
+               if(SiSSetMode(&ivideo->SiS_Pr, ivideo->mode_no) == 0) {
                        printk(KERN_ERR "sisfb: Fatal error: Setting mode[0x%x] failed\n",
-                               sisfb_mode_no);
-                       vfree(sishw_ext.pSR);
-                       vfree(sishw_ext.pCR);
-                       release_mem_region(ivideo.video_base, ivideo.video_size);
-                       release_mem_region(ivideo.mmio_base, sisfb_mmio_size);
-                       kfree(sis_fb_info);
-                       return -EINVAL;
+                                                                       ivideo->mode_no);
+                       ret = -EINVAL;
+                       iounmap(ivideo->mmio_vbase);
+                       goto error_0;
                }
 
                outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
 
-               sisfb_post_setmode();
-
-               ivideo.accel = 0;
-               if(sisfb_accel) {
-                  ivideo.accel = -1;
-                  default_var.accel_flags |= FB_ACCELF_TEXT;
-                  sisfb_initaccel();
-               }
+               sisfb_post_setmode(ivideo);
 
                /* Maximize regardless of sisfb_max at startup */
-               default_var.yres_virtual = 32767;
-               sisfb_crtc_to_var(&default_var);
-               
-               sis_fb_info->node = -1;
+               ivideo->default_var.yres_virtual = 32767;
+
+               /* Force reset of x virtual in crtc_to_var */
+               ivideo->default_var.xres_virtual = 0;
+
+               /* Copy mode timing to var */
+               sisfb_crtc_to_var(ivideo, &ivideo->default_var);
+
+               /* Find out about screen pitch */
+               sisfb_calc_pitch(ivideo, &ivideo->default_var);
+               sisfb_set_pitch(ivideo);
+
+               /* Init the accelerator (does nothing currently) */
+               sisfb_initaccel(ivideo);
+
+               /* Init some fbinfo entries */
+               sis_fb_info->node  = -1;
                sis_fb_info->flags = FBINFO_FLAG_DEFAULT;
-               sis_fb_info->blank = &sisfb_blank;
                sis_fb_info->fbops = &sisfb_ops;
+               sis_fb_info->disp  = &ivideo->sis_disp;
+               sis_fb_info->blank = &sisfb_blank;
                sis_fb_info->switch_con = &sisfb_switch;
-               sis_fb_info->updatevar = &sisfb_update_var;
-               sis_fb_info->changevar = NULL;
-               sis_fb_info->disp = &sis_disp;
+               sis_fb_info->updatevar  = &sisfb_update_var;
+               sis_fb_info->changevar  = NULL;
                strcpy(sis_fb_info->fontname, sisfb_fontname);
 
-               sisfb_set_disp(-1, &default_var, sis_fb_info);
+               sisfb_set_disp(-1, &ivideo->default_var, sis_fb_info);
 
-#else          /* --------- For 2.5: Setup a somewhat sane default var ------------ */
+#else          /* --------- For 2.6: Setup a somewhat sane default var ------------ */
 
                printk(KERN_INFO "sisfb: Default mode is %dx%dx%d (%dHz)\n",
-                       ivideo.video_width, ivideo.video_height, ivideo.video_bpp,
-                       ivideo.refresh_rate);
-
-               default_var.xres = default_var.xres_virtual = ivideo.video_width;
-               default_var.yres = default_var.yres_virtual = ivideo.video_height;
-               default_var.bits_per_pixel = ivideo.video_bpp;
-
-               sisfb_bpp_to_var(&default_var);
-               
-               default_var.pixclock = (u32) (1000000000 /
-                               sisfb_mode_rate_to_dclock(&SiS_Pr, &sishw_ext,
-                                               sisfb_mode_no, sisfb_rate_idx));
-                                               
-               if(sisfb_mode_rate_to_ddata(&SiS_Pr, &sishw_ext,
-                        sisfb_mode_no, sisfb_rate_idx,
-                        &default_var.left_margin, &default_var.right_margin, 
-                        &default_var.upper_margin, &default_var.lower_margin,
-                        &default_var.hsync_len, &default_var.vsync_len,
-                        &default_var.sync, &default_var.vmode)) {
-                  if((default_var.vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
-                     default_var.pixclock <<= 1;
-                  }
-               }
-
-               ivideo.accel = 0;
-               if(sisfb_accel) {
-                  ivideo.accel = -1;
-                  default_var.accel_flags |= FB_ACCELF_TEXT;
-                  sisfb_initaccel();
-               }
-
-               if(sisfb_ypan) {
-                  /* Maximize regardless of sisfb_max at startup */
-                  default_var.yres_virtual =
-                               ivideo.heapstart / (default_var.xres * (default_var.bits_per_pixel >> 3));
-                  if(default_var.yres_virtual > 32767) default_var.yres_virtual = 32767;
-                  if(default_var.yres_virtual <= default_var.yres) {
-                     default_var.yres_virtual = default_var.yres;
-                  }
+                       ivideo->video_width, ivideo->video_height, ivideo->video_bpp,
+                       ivideo->refresh_rate);
+
+               /* Set up the default var according to chosen default display mode */
+               ivideo->default_var.xres = ivideo->default_var.xres_virtual = ivideo->video_width;
+               ivideo->default_var.yres = ivideo->default_var.yres_virtual = ivideo->video_height;
+               ivideo->default_var.bits_per_pixel = ivideo->video_bpp;
+
+               sisfb_bpp_to_var(ivideo, &ivideo->default_var);
+
+               ivideo->default_var.pixclock = (u32) (1000000000 /
+                       sisfb_mode_rate_to_dclock(&ivideo->SiS_Pr, ivideo->mode_no, ivideo->rate_idx));
+
+               if(sisfb_mode_rate_to_ddata(&ivideo->SiS_Pr, ivideo->mode_no,
+                                               ivideo->rate_idx, &ivideo->default_var)) {
+                       if((ivideo->default_var.vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
+                               ivideo->default_var.pixclock <<= 1;
+                       }
+               }
+
+               if(ivideo->sisfb_ypan) {
+                       /* Maximize regardless of sisfb_max at startup */
+                       ivideo->default_var.yres_virtual =
+                               sisfb_calc_maxyres(ivideo, &ivideo->default_var);
+                       if(ivideo->default_var.yres_virtual < ivideo->default_var.yres) {
+                               ivideo->default_var.yres_virtual = ivideo->default_var.yres;
+                       }
                }
 
+               sisfb_calc_pitch(ivideo, &ivideo->default_var);
+
+               ivideo->accel = 0;
+               if(ivideo->sisfb_accel) {
+                       ivideo->accel = -1;
+#ifdef STUPID_ACCELF_TEXT_SHIT
+                       ivideo->default_var.accel_flags |= FB_ACCELF_TEXT;
+#endif
+               }
+               sisfb_initaccel(ivideo);
+
+#if defined(FBINFO_HWACCEL_DISABLED) && defined(FBINFO_HWACCEL_XPAN)
+               sis_fb_info->flags = FBINFO_DEFAULT             |
+                                    FBINFO_HWACCEL_YPAN        |
+                                    FBINFO_HWACCEL_XPAN        |
+                                    FBINFO_HWACCEL_COPYAREA    |
+                                    FBINFO_HWACCEL_FILLRECT    |
+                                    ((ivideo->accel) ? 0 : FBINFO_HWACCEL_DISABLED);
+#else
                sis_fb_info->flags = FBINFO_FLAG_DEFAULT;
-               sis_fb_info->var = default_var;
-               sis_fb_info->fix = sisfb_fix;
-               sis_fb_info->par = &ivideo;
-               sis_fb_info->screen_base = ivideo.video_vbase;
-               sis_fb_info->fbops = &sisfb_ops;
-#ifdef NEWFBDEV
-               sis_fb_info->class_dev.dev = &pdev->dev;
 #endif
+               sis_fb_info->var = ivideo->default_var;
+               sis_fb_info->fix = ivideo->sisfb_fix;
+               sis_fb_info->screen_base = ivideo->video_vbase + ivideo->video_offset;
+               sis_fb_info->fbops = &sisfb_ops;
                sisfb_get_fix(&sis_fb_info->fix, -1, sis_fb_info);
-               sis_fb_info->pseudo_palette = pseudo_palette;
-               
+               sis_fb_info->pseudo_palette = ivideo->pseudo_palette;
+
                fb_alloc_cmap(&sis_fb_info->cmap, 256 , 0);
-#endif
+#endif         /* 2.6 */
 
-               printk(KERN_INFO "sisfb: Initial vbflags 0x%lx\n", ivideo.vbflags);
+               printk(KERN_DEBUG "sisfb: Initial vbflags 0x%x\n", (int)ivideo->vbflags);
 
 #ifdef CONFIG_MTRR
-               ivideo.mtrr = mtrr_add((unsigned int) ivideo.video_base,
-                               (unsigned int) ivideo.video_size,
-                               MTRR_TYPE_WRCOMB, 1);
-               if(ivideo.mtrr) {
-                       printk(KERN_INFO "sisfb: Added MTRRs\n");
+               ivideo->mtrr = mtrr_add(ivideo->video_base, ivideo->video_size,
+                                       MTRR_TYPE_WRCOMB, 1);
+               if(ivideo->mtrr < 0) {
+                       printk(KERN_DEBUG "sisfb: Failed to add MTRRs\n");
                }
-
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-               vc_resize_con(1, 1, 0);
 #endif
 
                if(register_framebuffer(sis_fb_info) < 0) {
-                       vfree(sishw_ext.pSR);
-                       vfree(sishw_ext.pCR);
-                       release_mem_region(ivideo.video_base, ivideo.video_size);
-                       release_mem_region(ivideo.mmio_base, sisfb_mmio_size);
                        printk(KERN_ERR "sisfb: Fatal error: Failed to register framebuffer\n");
-                       kfree(sis_fb_info);
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       iounmap(ivideo->mmio_vbase);
+                       goto error_0;
                }
 
-               sisfb_registered = 1;                   
+               ivideo->registered = 1;
 
-               printk(KERN_DEBUG "sisfb: Installed SISFB_GET_INFO ioctl (%x)\n", SISFB_GET_INFO);
-               printk(KERN_DEBUG "sisfb: Installed SISFB_GET_VBRSTATUS ioctl (%x)\n", SISFB_GET_VBRSTATUS);
-               
-               printk(KERN_INFO "sisfb: 2D acceleration is %s, scrolling mode %s\n",
-                    sisfb_accel ? "enabled" : "disabled",
-                    sisfb_ypan  ? (sisfb_max ? "ypan (auto-max)" : "ypan (no auto-max)") : "redraw");
+               /* Enlist us */
+               ivideo->next = card_list;
+               card_list = ivideo;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-               printk(KERN_INFO "fb%d: %s frame buffer device, Version %d.%d.%02d\n",
-                       GET_FB_IDX(sis_fb_info->node), sis_fb_info->modename, VER_MAJOR, VER_MINOR,
-                       VER_LEVEL);                  
-#else
-               printk(KERN_INFO "fb%d: %s frame buffer device, Version %d.%d.%02d\n",
-                       sis_fb_info->node, myid, VER_MAJOR, VER_MINOR, VER_LEVEL);
+#ifdef SIS_OLD_CONFIG_COMPAT
+               {
+               int ret;
+               /* Our ioctls are all "32/64bit compatible" */
+               ret =  register_ioctl32_conversion(FBIO_ALLOC,             NULL);
+               ret |= register_ioctl32_conversion(FBIO_FREE,              NULL);
+               ret |= register_ioctl32_conversion(FBIOGET_VBLANK,         NULL);
+               ret |= register_ioctl32_conversion(SISFB_GET_INFO_SIZE,    NULL);
+               ret |= register_ioctl32_conversion(SISFB_GET_INFO,         NULL);
+               ret |= register_ioctl32_conversion(SISFB_GET_TVPOSOFFSET,  NULL);
+               ret |= register_ioctl32_conversion(SISFB_SET_TVPOSOFFSET,  NULL);
+               ret |= register_ioctl32_conversion(SISFB_SET_LOCK,         NULL);
+               ret |= register_ioctl32_conversion(SISFB_GET_VBRSTATUS,    NULL);
+               ret |= register_ioctl32_conversion(SISFB_GET_AUTOMAXIMIZE, NULL);
+               ret |= register_ioctl32_conversion(SISFB_SET_AUTOMAXIMIZE, NULL);
+               ret |= register_ioctl32_conversion(SISFB_COMMAND,          NULL);
+               if(ret)
+                       printk(KERN_ERR
+                               "sisfb: Error registering ioctl32 translations\n");
+               else
+                       ivideo->ioctl32registered = 1;
+               }
 #endif
 
-               printk(KERN_INFO "sisfb: (C) 2001-2004 Thomas Winischhofer.\n");
+               printk(KERN_INFO "sisfb: 2D acceleration is %s, y-panning %s\n",
+                       ivideo->sisfb_accel ? "enabled" : "disabled",
+                       ivideo->sisfb_ypan  ?
+                               (ivideo->sisfb_max ? "enabled (auto-max)" :
+                                               "enabled (no auto-max)") :
+                                                                       "disabled");
+
+
+               printk(KERN_INFO "fb%d: %s frame buffer device version %d.%d.%d\n",
+                       sis_fb_info->node, ivideo->myid, VER_MAJOR, VER_MINOR, VER_LEVEL);
+
+               printk(KERN_INFO "sisfb: Copyright (C) 2001-2005 Thomas Winischhofer\n");
 
        }       /* if mode = "none" */
+
        return 0;
 }
 
+/*****************************************************/
+/*                PCI DEVICE HANDLING                */
+/*****************************************************/
 
-#ifdef MODULE
-
-static char         *mode = NULL;
-static int          vesa = -1;
-static unsigned int rate = 0;
-static unsigned int crt1off = 1;
-static unsigned int mem = 0;
-static char         *forcecrt2type = NULL;
-static int          forcecrt1 = -1;
-static char         *queuemode = NULL;
-static int          pdc = 0;
-static int          noaccel = -1;
-static int          noypan  = -1;
-static int         nomax = -1;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-static int          inverse = 0;
+static void __devexit sisfb_remove(struct pci_dev *pdev)
+{
+       struct sis_video_info   *ivideo = pci_get_drvdata(pdev);
+       struct fb_info          *sis_fb_info = ivideo->memyselfandi;
+       int                     registered = ivideo->registered;
+       int                     modechanged = ivideo->modechanged;
+
+#ifdef SIS_OLD_CONFIG_COMPAT
+       if(ivideo->ioctl32registered) {
+               int ret;
+               ret =  unregister_ioctl32_conversion(FBIO_ALLOC);
+               ret |= unregister_ioctl32_conversion(FBIO_FREE);
+               ret |= unregister_ioctl32_conversion(FBIOGET_VBLANK);
+               ret |= unregister_ioctl32_conversion(SISFB_GET_INFO_SIZE);
+               ret |= unregister_ioctl32_conversion(SISFB_GET_INFO);
+               ret |= unregister_ioctl32_conversion(SISFB_GET_TVPOSOFFSET);
+               ret |= unregister_ioctl32_conversion(SISFB_SET_TVPOSOFFSET);
+               ret |= unregister_ioctl32_conversion(SISFB_SET_LOCK);
+               ret |= unregister_ioctl32_conversion(SISFB_GET_VBRSTATUS);
+               ret |= unregister_ioctl32_conversion(SISFB_GET_AUTOMAXIMIZE);
+               ret |= unregister_ioctl32_conversion(SISFB_SET_AUTOMAXIMIZE);
+               ret |= unregister_ioctl32_conversion(SISFB_COMMAND);
+               if(ret)
+                       printk(KERN_ERR
+                            "sisfb: Error unregistering ioctl32 translations\n");
+       }
 #endif
-static int          userom = 1;
-static int          useoem = -1;
-static char         *tvstandard = NULL;
-static int         nocrt2rate = 0;
-static int          scalelcd = -1;
-static char        *specialtiming = NULL;
-static int         lvdshl = -1;
-
-MODULE_DESCRIPTION("SiS 300/540/630/730/315/550/650/651/661/740/741/330/760 framebuffer driver");
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Thomas Winischhofer <thomas@winischhofer.net>; SiS; Various others");
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-MODULE_PARM(mode, "s");
-MODULE_PARM_DESC(mode,
-       "\nSelects the desired display mode in the format [X]x[Y]x[Depth], eg.\n"
-         "1024x768x16. Other formats supported include XxY-Depth and\n"
-        "XxY-Depth@Rate. If the parameter is only one (decimal or hexadecimal)\n"
-        "number, it will be interpreted as a VESA mode number. (default: none if\n"
-        "sisfb is a module; this leaves the console untouched and the driver will\n"
-        "only do the video memory management for eg. DRM/DRI; 800x600x8 if sisfb\n"
-        "is in the kernel)");
-#endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)         
-MODULE_PARM(mode, "s");
-MODULE_PARM_DESC(mode,
-       "\nSelects the desired default display mode in the format XxYxDepth,\n"
-         "eg. 1024x768x16. Other formats supported include XxY-Depth and\n"
-        "XxY-Depth@Rate. If the parameter is only one (decimal or hexadecimal)\n"
-        "number, it will be interpreted as a VESA mode number. (default: 800x600x8)");
-#endif
+       /* Unmap */
+       iounmap(ivideo->mmio_vbase);
+       iounmap(ivideo->video_vbase);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-MODULE_PARM(vesa, "i");
-MODULE_PARM_DESC(vesa,
-       "\nSelects the desired display mode by VESA defined mode number, eg. 0x117\n"
-         "(default: 0x0000 if sisfb is a module; this leaves the console untouched\n"
-        "and the driver will only do the video memory management for eg. DRM/DRI;\n"
-        "0x0103 if sisfb is in the kernel)");
-#endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-MODULE_PARM(vesa, "i");
-MODULE_PARM_DESC(vesa,
-       "\nSelects the desired default display mode by VESA defined mode number, eg.\n"
-         "0x117 (default: 0x0103)");
-#endif
+       /* Release mem regions */
+       release_mem_region(ivideo->video_base, ivideo->video_size);
+       release_mem_region(ivideo->mmio_base, ivideo->mmio_size);
 
-MODULE_PARM(rate, "i");
-MODULE_PARM_DESC(rate,
-       "\nSelects the desired vertical refresh rate for CRT1 (external VGA) in Hz.\n"
-         "If the mode is specified in the format XxY-Depth@Rate, this parameter\n"
-         "will be ignored (default: 60)");
-
-MODULE_PARM(crt1off,   "i");
-MODULE_PARM_DESC(crt1off,
-       "(Deprecated, please use forcecrt1)");
-
-MODULE_PARM(filter, "i");
-MODULE_PARM_DESC(filter,
-       "\nSelects TV flicker filter type (only for systems with a SiS301 video bridge).\n"
-         "(Possible values 0-7, default: [no filter])");
-
-MODULE_PARM(queuemode,   "s");
-MODULE_PARM_DESC(queuemode,
-       "\nSelects the queue mode on 315/550/65x/74x/330/760. Possible choices are AGP, VRAM,\n"
-         "MMIO. AGP is only available if the kernel has AGP support. The queue mode is\n"
-         "important to programs using the 2D/3D accelerator of the SiS chip. The modes\n"
-         "require a totally different way of programming the engines. If any mode than\n"
-         "MMIO is selected, sisfb will disable its own 2D acceleration. On\n"
-         "300/540/630/730, this option is ignored. (default: MMIO)");
-
-/* TW: "Import" the options from the X driver */
-MODULE_PARM(mem,    "i");
-MODULE_PARM_DESC(mem,
-       "\nDetermines the beginning of the video memory heap in KB. This heap is used\n"
-         "for video RAM management for eg. DRM/DRI. On 300 series, the default depends\n"
-         "on the amount of video RAM available. If 8MB of video RAM or less is available,\n"
-         "the heap starts at 4096KB, if between 8 and 16MB are available at 8192KB,\n"
-         "otherwise at 12288KB. On 315 and Xabre series, the heap is 1MB by default. The\n"
-         "value is to be specified without 'KB' and should match the MaxXFBMem setting for\n"
-         "XFree 4.x (x>=2).");
+       vfree(ivideo->bios_abase);
 
-MODULE_PARM(forcecrt2type, "s");
-MODULE_PARM_DESC(forcecrt2type,
-       "\nIf this option is omitted, the driver autodetects CRT2 output devices, such as\n"
-         "LCD, TV or secondary VGA. With this option, this autodetection can be\n"
-         "overridden. Possible parameters are LCD, TV, VGA or NONE. NONE disables CRT2.\n"
-         "On systems with a 301(B/LV) bridge, parameters SVIDEO, COMPOSITE or SCART can be\n"
-         "used instead of TV to override the TV detection. (default: [autodetected])");
+       if(ivideo->lpcdev)
+               SIS_PCI_PUT_DEVICE(ivideo->lpcdev);
 
-MODULE_PARM(forcecrt1, "i");
-MODULE_PARM_DESC(forcecrt1,
-       "\nNormally, the driver autodetects whether or not CRT1 (external VGA) is \n"
-         "connected. With this option, the detection can be overridden (1=CRT1 ON,\n"
-         " 0=CRT1 off) (default: [autodetected])");
+       if(ivideo->nbridge)
+               SIS_PCI_PUT_DEVICE(ivideo->nbridge);
 
-MODULE_PARM(pdc, "i");
-MODULE_PARM_DESC(pdc,
-        "\nThis is for manually selecting the LCD panel delay compensation. The driver\n"
-         "should detect this correctly in most cases; however, sometimes this is not\n"
-         "possible. If you see 'small waves' on the LCD, try setting this to 4, 32 or 24\n"
-         "on a 300 series chipset; 3 or 51 on a 315 series chipset. If the problem persists,\n"
-         "try other values (on 300 series: between 4 and 60 in steps of 4; on 315 series:\n"
-         "and value from 0 to 255). (default: [autodetected])");
+#ifdef CONFIG_MTRR
+       /* Release MTRR region */
+       if(ivideo->mtrr >= 0)
+               mtrr_del(ivideo->mtrr, ivideo->video_base, ivideo->video_size);
+#endif
 
-MODULE_PARM(noaccel, "i");
-MODULE_PARM_DESC(noaccel,
-        "\nIf set to anything other than 0, 2D acceleration will be disabled.\n"
-         "(default: 0)");
+       pci_set_drvdata(pdev, NULL);
 
-MODULE_PARM(noypan, "i");
-MODULE_PARM_DESC(noypan,
-        "\nIf set to anything other than 0, y-panning will be disabled and scrolling\n"
-         "will be performed by redrawing the screen. (default: 0)");
+       /* If device was disabled when starting, disable
+        * it when quitting.
+        */
+       if(!ivideo->sisvga_enabled)
+               pci_disable_device(pdev);
 
-MODULE_PARM(nomax, "i");
-MODULE_PARM_DESC(nomax,
-        "\nIf y-panning is enabled, sisfb will by default use the entire available video\n"
-         "memory for the virtual screen in order to optimize scrolling performance. If this\n"
-         "is set to anything other than 0, sisfb will not do this and thereby enable the user\n"
-         "to positively specify a virtual Y size of the screen using fbset. (default: 0)\n");
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) 
-MODULE_PARM(inverse, "i");
-MODULE_PARM_DESC(inverse,
-        "\nSetting this to anything but 0 should invert the display colors, but this\n"
-         "does not seem to work. (default: 0)");
-#endif 
-
-MODULE_PARM(userom, "i");
-MODULE_PARM_DESC(userom,
-        "\nSetting this to 0 keeps sisfb from using the video BIOS data which is needed\n"
-         "for some LCD and TV setup. (default: 1)");
-
-MODULE_PARM(useoem, "i");
-MODULE_PARM_DESC(useoem,
-        "\nSetting this to 0 keeps sisfb from using its internel OEM data for some LCD\n"
-         "panels and TV connector types. (default: [auto])");
-
-MODULE_PARM(tvstandard, "s");
-MODULE_PARM_DESC(tvstandard,
-       "\nThis allows overriding the BIOS default for the TV standard. Valid choices are\n"
-         "pal and ntsc. (default: [auto])");
+       /* Unregister the framebuffer */
+       if(ivideo->registered) {
+               unregister_framebuffer(sis_fb_info);
+               framebuffer_release(sis_fb_info);
+       }
 
-MODULE_PARM(nocrt2rate, "i");
-MODULE_PARM_DESC(nocrt2rate,
-       "\nSetting this to 1 will force the driver to use the default refresh rate for\n"
-         "CRT2 if CRT2 type is VGA. (default: 0, use same rate as CRT1)");
+       /* OK, our ivideo is gone for good from here. */
 
-MODULE_PARM(scalelcd, "i");
-MODULE_PARM_DESC(scalelcd,
-       "\nSetting this to 1 will force the driver to scale the LCD image to the panel's\n"
-         "native resolution. Setting it to 0 will disable scaling; if the panel can scale\n"
-         "by itself, it will probably do this, otherwise you will see a black bar around\n"
-         "the screen image. Default: [autodetect if panel can scale]");
+       /* TODO: Restore the initial mode
+        * This sounds easy but is as good as impossible
+        * on many machines with SiS chip and video bridge
+        * since text modes are always set up differently
+        * from machine to machine. Depends on the type
+        * of integration between chipset and bridge.
+        */
+       if(registered && modechanged)
+               printk(KERN_INFO
+                       "sisfb: Restoring of text mode not supported yet\n");
+};
 
-MODULE_PARM(specialtiming, "s");
+static struct pci_driver sisfb_driver = {
+       .name           = "sisfb",
+       .id_table       = sisfb_pci_table,
+       .probe          = sisfb_probe,
+       .remove         = __devexit_p(sisfb_remove)
+};
 
-MODULE_PARM(lvdshl, "i");
+SISINITSTATIC int __init sisfb_init(void)
+{
+#ifndef MODULE
+       char *options = NULL;
 
+       if(fb_get_options("sisfb", &options))
+               return -ENODEV;
 
-int init_module(void)
-{
-       int err;
+       sisfb_setup(options);
+#endif
+       return pci_register_driver(&sisfb_driver);
+}
 
-       SiS_Pr.UsePanelScaler = -1;
-       SiS_Pr.SiS_CustomT = CUT_NONE;
-       SiS_Pr.LVDSHL = -1;
+#ifndef MODULE
+module_init(sisfb_init);
+#endif
 
-       ivideo.refresh_rate = sisfb_parm_rate = rate;
+/*****************************************************/
+/*                      MODULE                       */
+/*****************************************************/
 
-       if((scalelcd == 0) || (scalelcd == 1)) {
-          SiS_Pr.UsePanelScaler = scalelcd ^ 1;
-       }
+#ifdef MODULE
 
-       if(mode)
-               sisfb_search_mode(mode, FALSE);
-       else if(vesa != -1)
-               sisfb_search_vesamode(vesa, FALSE);
-       else
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-               /* For 2.4, set mode=none if no mode is given  */
-               sisfb_mode_idx = MODE_INDEX_NONE;
-#endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-               /* For 2.5, we don't need this "mode=none" stuff anymore */
-               sisfb_mode_idx = DEFAULT_MODE;
+static char            *mode = NULL;
+static int             vesa = -1;
+static unsigned int    rate = 0;
+static unsigned int    crt1off = 1;
+static unsigned int    mem = 0;
+static char            *forcecrt2type = NULL;
+static int             forcecrt1 = -1;
+static int             pdc = -1;
+static int             pdc1 = -1;
+static int             noaccel = -1;
+static int             noypan  = -1;
+static int             nomax = -1;
+static int             userom = -1;
+static int             useoem = -1;
+static char            *tvstandard = NULL;
+static int             nocrt2rate = 0;
+static int             scalelcd = -1;
+static char            *specialtiming = NULL;
+static int             lvdshl = -1;
+static int             tvxposoffset = 0, tvyposoffset = 0;
+#if !defined(__i386__) && !defined(__x86_64__)
+static int             resetcard = 0;
+static int             videoram = 0;
 #endif
 
+static int __init sisfb_init_module(void)
+{
+       sisfb_setdefaultparms();
+
+       if(rate)
+               sisfb_parm_rate = rate;
+
+       if((scalelcd == 0) || (scalelcd == 1))
+               sisfb_scalelcd = scalelcd ^ 1;
+
+       /* Need to check crt2 type first for fstn/dstn */
+
        if(forcecrt2type)
                sisfb_search_crt2type(forcecrt2type);
 
        if(tvstandard)
                sisfb_search_tvstd(tvstandard);
 
-       if(crt1off == 0)
-               sisfb_crt1off = 1;
-       else
-               sisfb_crt1off = 0;
+       if(mode)
+               sisfb_search_mode(mode, FALSE);
+       else if(vesa != -1)
+               sisfb_search_vesamode(vesa, FALSE);
+
+       sisfb_crt1off = (crt1off == 0) ? 1 : 0;
 
        sisfb_forcecrt1 = forcecrt1;
        if(forcecrt1 == 1)
@@ -5203,95 +6689,212 @@ int init_module(void)
        else if(forcecrt1 == 0)
                sisfb_crt1off = 1;
 
-       if(noaccel == 1)      sisfb_accel = 0;
-       else if(noaccel == 0) sisfb_accel = 1;
+       if(noaccel == 1)
+               sisfb_accel = 0;
+       else if(noaccel == 0)
+               sisfb_accel = 1;
 
-       if(noypan == 1)       sisfb_ypan = 0;
-       else if(noypan == 0)  sisfb_ypan = 1;
+       if(noypan == 1)
+               sisfb_ypan = 0;
+       else if(noypan == 0)
+               sisfb_ypan = 1;
 
-       if(nomax == 1)        sisfb_max = 0;
-       else if(nomax == 0)   sisfb_max = 1;
-       
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-       if(inverse)           sisfb_inverse = 1;
-       sisfb_fontname[0] = '\0';
-#endif
+       if(nomax == 1)
+               sisfb_max = 0;
+       else if(nomax == 0)
+               sisfb_max = 1;
 
-       if(mem)               sisfb_mem = mem;
+       if(mem)
+               sisfb_parm_mem = mem;
 
-       sisfb_userom = userom;
+       if(userom != -1)
+               sisfb_userom = userom;
 
-       sisfb_useoem = useoem;
+       if(useoem != -1)
+               sisfb_useoem = useoem;
 
-       if (queuemode)        sisfb_search_queuemode(queuemode);
-       
-       /* If other queuemode than MMIO, disable 2D accel and ypan */
-       if((sisfb_queuemode != -1) && (sisfb_queuemode != MMIO_CMD)) {
-               sisfb_accel = 0;
-       }
+        if(pdc != -1)
+               sisfb_pdc  = (pdc  & 0x7f);
 
-        if(pdc) sisfb_pdc = pdc & 0x3c;
+       if(pdc1 != -1)
+               sisfb_pdca = (pdc1 & 0x1f);
 
        sisfb_nocrt2rate = nocrt2rate;
 
        if(specialtiming)
                sisfb_search_specialtiming(specialtiming);
 
-       if((lvdshl >= 0) && (lvdshl <= 3)) SiS_Pr.LVDSHL = lvdshl;
+       if((lvdshl >= 0) && (lvdshl <= 3))
+               sisfb_lvdshl = lvdshl;
 
-       if((err = sisfb_init()) < 0) return err;
+       sisfb_tvxposoffset = tvxposoffset;
+       sisfb_tvyposoffset = tvyposoffset;
 
-       return 0;
+#if !defined(__i386__) && !defined(__x86_64__)
+       sisfb_resetcard = (resetcard) ? 1 : 0;
+       if(videoram)
+               sisfb_videoram = videoram;
+#endif
+
+       return sisfb_init();
 }
 
-void cleanup_module(void)
+static void __exit sisfb_remove_module(void)
 {
-       /* Unmap */
-       iounmap(ivideo.video_vbase);
-       iounmap(ivideo.mmio_vbase);
+       pci_unregister_driver(&sisfb_driver);
+       printk(KERN_DEBUG "sisfb: Module unloaded\n");
+}
 
-       /* Release mem regions */
-       release_mem_region(ivideo.video_base, ivideo.video_size);
-       release_mem_region(ivideo.mmio_base, sisfb_mmio_size);
+module_init(sisfb_init_module);
+module_exit(sisfb_remove_module);
 
-#ifdef CONFIG_MTRR
-       /* Release MTRR region */
-       if(ivideo.mtrr) {
-               mtrr_del(ivideo.mtrr,
-                     (unsigned int)ivideo.video_base,
-                     (unsigned int)ivideo.video_size);
-       }
+MODULE_DESCRIPTION("SiS 300/540/630/730/315/55x/65x/661/74x/330/76x/34x, XGI V3XT/V5/V8/Z7 framebuffer device driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Thomas Winischhofer <thomas@winischhofer.net>, Others");
+
+module_param(mem, int, 0);
+module_param(noaccel, int, 0);
+module_param(noypan, int, 0);
+module_param(nomax, int, 0);
+module_param(userom, int, 0);
+module_param(useoem, int, 0);
+module_param(mode, charp, 0);
+module_param(vesa, int, 0);
+module_param(rate, int, 0);
+module_param(forcecrt1, int, 0);
+module_param(forcecrt2type, charp, 0);
+module_param(scalelcd, int, 0);
+module_param(pdc, int, 0);
+module_param(pdc1, int, 0);
+module_param(specialtiming, charp, 0);
+module_param(lvdshl, int, 0);
+module_param(tvstandard, charp, 0);
+module_param(tvxposoffset, int, 0);
+module_param(tvyposoffset, int, 0);
+module_param(nocrt2rate, int, 0);
+#if !defined(__i386__) && !defined(__x86_64__)
+module_param(resetcard, int, 0);
+module_param(videoram, int, 0);
 #endif
 
-       /* Unregister the framebuffer */
-       if(sisfb_registered) {
-               unregister_framebuffer(sis_fb_info);
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) && (defined(NEWFBDEV))
-               framebuffer_release(sis_fb_info);
-#else
-               kfree(sis_fb_info);
+MODULE_PARM_DESC(mem,
+       "\nDetermines the beginning of the video memory heap in KB. This heap is used\n"
+         "for video RAM management for eg. DRM/DRI. On 300 series, the default depends\n"
+         "on the amount of video RAM available. If 8MB of video RAM or less is available,\n"
+         "the heap starts at 4096KB, if between 8 and 16MB are available at 8192KB,\n"
+         "otherwise at 12288KB. On 315/330/340 series, the heap size is 32KB by default.\n"
+         "The value is to be specified without 'KB'.\n");
+
+MODULE_PARM_DESC(noaccel,
+       "\nIf set to anything other than 0, 2D acceleration will be disabled.\n"
+         "(default: 0)\n");
+
+MODULE_PARM_DESC(noypan,
+       "\nIf set to anything other than 0, y-panning will be disabled and scrolling\n"
+         "will be performed by redrawing the screen. (default: 0)\n");
+
+MODULE_PARM_DESC(nomax,
+       "\nIf y-panning is enabled, sisfb will by default use the entire available video\n"
+         "memory for the virtual screen in order to optimize scrolling performance. If\n"
+         "this is set to anything other than 0, sisfb will not do this and thereby \n"
+         "enable the user to positively specify a virtual Y size of the screen using\n"
+         "fbset. (default: 0)\n");
+
+MODULE_PARM_DESC(mode,
+       "\nSelects the desired default display mode in the format XxYxDepth,\n"
+        "eg. 1024x768x16. Other formats supported include XxY-Depth and\n"
+        "XxY-Depth@Rate. If the parameter is only one (decimal or hexadecimal)\n"
+        "number, it will be interpreted as a VESA mode number. (default: 800x600x8)\n");
+
+MODULE_PARM_DESC(vesa,
+       "\nSelects the desired default display mode by VESA defined mode number, eg.\n"
+        "0x117 (default: 0x0103)\n");
+
+MODULE_PARM_DESC(rate,
+       "\nSelects the desired vertical refresh rate for CRT1 (external VGA) in Hz.\n"
+         "If the mode is specified in the format XxY-Depth@Rate, this parameter\n"
+         "will be ignored (default: 60)\n");
+
+MODULE_PARM_DESC(forcecrt1,
+       "\nNormally, the driver autodetects whether or not CRT1 (external VGA) is \n"
+         "connected. With this option, the detection can be overridden (1=CRT1 ON,\n"
+         "0=CRT1 OFF) (default: [autodetected])\n");
+
+MODULE_PARM_DESC(forcecrt2type,
+       "\nIf this option is omitted, the driver autodetects CRT2 output devices, such as\n"
+         "LCD, TV or secondary VGA. With this option, this autodetection can be\n"
+         "overridden. Possible parameters are LCD, TV, VGA or NONE. NONE disables CRT2.\n"
+         "On systems with a SiS video bridge, parameters SVIDEO, COMPOSITE or SCART can\n"
+         "be used instead of TV to override the TV detection. Furthermore, on systems\n"
+         "with a SiS video bridge, SVIDEO+COMPOSITE, HIVISION, YPBPR480I, YPBPR480P,\n"
+         "YPBPR720P and YPBPR1080I are understood. However, whether or not these work\n"
+         "depends on the very hardware in use. (default: [autodetected])\n");
+
+MODULE_PARM_DESC(scalelcd,
+       "\nSetting this to 1 will force the driver to scale the LCD image to the panel's\n"
+         "native resolution. Setting it to 0 will disable scaling; LVDS panels will\n"
+         "show black bars around the image, TMDS panels will probably do the scaling\n"
+         "themselves. Default: 1 on LVDS panels, 0 on TMDS panels\n");
+
+MODULE_PARM_DESC(pdc,
+       "\nThis is for manually selecting the LCD panel delay compensation. The driver\n"
+         "should detect this correctly in most cases; however, sometimes this is not\n"
+         "possible. If you see 'small waves' on the LCD, try setting this to 4, 32 or 24\n"
+         "on a 300 series chipset; 6 on other chipsets. If the problem persists, try\n"
+         "other values (on 300 series: between 4 and 60 in steps of 4; otherwise: any\n"
+         "value from 0 to 31). (default: autodetected, if LCD is active during start)\n");
+
+#ifdef CONFIG_FB_SIS_315
+MODULE_PARM_DESC(pdc1,
+       "\nThis is same as pdc, but for LCD-via CRT1. Hence, this is for the 315/330/340\n"
+         "series only. (default: autodetected if LCD is in LCD-via-CRT1 mode during\n"
+         "startup) - Note: currently, this has no effect because LCD-via-CRT1 is not\n"
+         "implemented yet.\n");
 #endif
-       }
 
-       if(sishw_ext.pSR) vfree(sishw_ext.pSR);
-       if(sishw_ext.pCR) vfree(sishw_ext.pCR);
-       
-       /* TODO: Restore the initial mode
-        * This sounds easy but is as good as impossible
-        * on many machines with SiS chip and video bridge
-        * since text modes are always set up differently
-        * from machine to machine. Depends on the type
-        * of integration between chipset and bridge.
-        */
-       
-       printk(KERN_INFO "sisfb: Module unloaded\n");
-}
+MODULE_PARM_DESC(specialtiming,
+       "\nPlease refer to documentation for more information on this option.\n");
+
+MODULE_PARM_DESC(lvdshl,
+       "\nPlease refer to documentation for more information on this option.\n");
+
+MODULE_PARM_DESC(tvstandard,
+       "\nThis allows overriding the BIOS default for the TV standard. Valid choices are\n"
+         "pal, ntsc, palm and paln. (default: [auto; pal or ntsc only])\n");
+
+MODULE_PARM_DESC(tvxposoffset,
+       "\nRelocate TV output horizontally. Possible parameters: -32 through 32.\n"
+         "Default: 0\n");
+
+MODULE_PARM_DESC(tvyposoffset,
+       "\nRelocate TV output vertically. Possible parameters: -32 through 32.\n"
+         "Default: 0\n");
 
+MODULE_PARM_DESC(nocrt2rate,
+       "\nSetting this to 1 will force the driver to use the default refresh rate for\n"
+         "CRT2 if CRT2 type is VGA. (default: 0, use same rate as CRT1)\n");
+
+#if !defined(__i386__) && !defined(__x86_64__)
+#ifdef CONFIG_FB_SIS_300
+MODULE_PARM_DESC(resetcard,
+       "\nSet this to 1 in order to reset (POST) the card on non-x86 machines where\n"
+         "the BIOS did not POST the card (only supported for SiS 300/305 and XGI cards\n"
+         "currently). Default: 0\n");
+
+MODULE_PARM_DESC(videoram,
+       "\nSet this to the amount of video RAM (in kilobyte) the card has. Required on\n"
+         "some non-x86 architectures where the memory auto detection fails. Only\n"
+         "relevant if resetcard is set, too. SiS300/305 only. Default: [auto-detect]\n");
 #endif
+#endif
+
+#endif            /*  /MODULE  */
 
+/* _GPL only for new symbols. */
 EXPORT_SYMBOL(sis_malloc);
 EXPORT_SYMBOL(sis_free);
-EXPORT_SYMBOL(sis_dispinfo);
+EXPORT_SYMBOL_GPL(sis_malloc_new);
+EXPORT_SYMBOL_GPL(sis_free_new);
+
+
 
-EXPORT_SYMBOL(ivideo);
-