Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/video/fbdev/sis/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 183 kB image not shown  

Quelle  sis_main.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * 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-2005 Thomas Winischhofer, Vienna, Austria.
 *
 * Author: Thomas Winischhofer <thomas@winischhofer.net>
 *
 * 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
 *
 * Originally based on the VBE 2.0 compliant graphic boards framebuffer driver,
 * which is (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
 */


#include <linux/aperture.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/fb.h>
#include <linux/selection.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/vmalloc.h>
#include <linux/capability.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <asm/io.h>

#include "sis.h"
#include "sis_main.h"
#include "init301.h"

#if !defined(CONFIG_FB_SIS_300) && !defined(CONFIG_FB_SIS_315)
#warning Neither CONFIG_FB_SIS_300 nor CONFIG_FB_SIS_315 is set
#warning sisfb will not work!
#endif

/* ---------------------- Prototypes ------------------------- */

/* Interface used by the world */
#ifndef MODULE
static int sisfb_setup(char *options);
#endif

/* Interface to the low level console driver */
static int sisfb_init(void);

/* fbdev routines */
static int sisfb_get_fix(struct fb_fix_screeninfo *fix, int con,
    struct fb_info *info);

static int sisfb_ioctl(struct fb_info *info, unsigned int cmd,
       unsigned long arg);
static int sisfb_set_par(struct fb_info *info);
static int sisfb_blank(int blank,
    struct fb_info *info);

static void sisfb_handle_command(struct sis_video_info *ivideo,
     struct sisfb_cmd *sisfb_command);

static void sisfb_search_mode(char *name, bool quiet);
static int sisfb_validate_mode(struct sis_video_info *ivideo, int modeindex, u32 vbflags);
static u8 sisfb_search_refresh_rate(struct sis_video_info *ivideo, unsigned int rate,
    int index);
static int sisfb_setcolreg(unsigned regno, unsigned red, unsigned green,
    unsigned blue, unsigned transp,
    struct fb_info *fb_info);
static int sisfb_do_set_var(struct fb_var_screeninfo *var, int isactive,
    struct fb_info *info);
static void sisfb_pre_setmode(struct sis_video_info *ivideo);
static void sisfb_post_setmode(struct sis_video_info *ivideo);
static bool sisfb_CheckVBRetrace(struct sis_video_info *ivideo);
static bool sisfbcheckvretracecrt2(struct sis_video_info *ivideo);
static bool sisfbcheckvretracecrt1(struct sis_video_info *ivideo);
static bool sisfb_bridgeisslave(struct sis_video_info *ivideo);
static void sisfb_detect_VB_connect(struct sis_video_info *ivideo);
static void sisfb_get_VB_type(struct sis_video_info *ivideo);
static void sisfb_set_TVxposoffset(struct sis_video_info *ivideo, int val);
static void sisfb_set_TVyposoffset(struct sis_video_info *ivideo, int val);

/* Internal heap routines */
static int  sisfb_heap_init(struct sis_video_info *ivideo);
static struct SIS_OH * sisfb_poh_new_node(struct SIS_HEAP *memheap);
static struct SIS_OH * sisfb_poh_allocate(struct SIS_HEAP *memheap, u32 size);
static void  sisfb_delete_node(struct SIS_OH *poh);
static void  sisfb_insert_node(struct SIS_OH *pohList, struct SIS_OH *poh);
static struct SIS_OH * sisfb_poh_free(struct SIS_HEAP *memheap, u32 base);
static void  sisfb_free_node(struct SIS_HEAP *memheap, struct SIS_OH *poh);


/* ------------------ 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
}

/* ------------- Parameter parsing -------------- */

static void sisfb_search_vesamode(unsigned int vesamode, bool quiet)
{
 int i = 0, j = 0;

 /* We don't know the hardware specs yet and there is no ivideo */

 if(vesamode == 0) {
  if(!quiet)
   printk(KERN_ERR "sisfb: Invalid mode. Using default.\n");

  sisfb_mode_idx = DEFAULT_MODE;

  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);
}

static void sisfb_search_mode(char *name, bool quiet)
{
 unsigned int j = 0, xres = 0, yres = 0, depth = 0, rate = 0;
 int i = 0;
 char strbuf[24], strbuf1[20];
 char *nameptr = name;

 /* We don't know the hardware specs yet and there is no ivideo */

 if(name == NULL) {
  if(!quiet)
   printk(KERN_ERR "sisfb: Internal error, using default mode.\n");

  sisfb_mode_idx = DEFAULT_MODE;
  return;
 }

 if(!strncasecmp(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;
 }

 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)) {
    swap(rate, depth);
   }
   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;
   }
  }
 }

 i = 0; j = 0;
 while(sisbios_mode[i].mode_no[0] != 0) {
  if(!strncasecmp(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((!j) && !quiet)
  printk(KERN_ERR "sisfb: Invalid mode '%s'\n", nameptr);
}

static void __init
sisfb_search_crt2type(const char *name)
{
 int i = 0;

 /* 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(!strncasecmp(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++;
 }

 sisfb_dstn = (sisfb_crt2flags & FL_550_DSTN) ? 1 : 0;
 sisfb_fstn = (sisfb_crt2flags & FL_550_FSTN) ? 1 : 0;

 if(sisfb_crt2type < 0)
  printk(KERN_ERR "sisfb: Invalid CRT2 type: %s\n", name);
}

static void __init
sisfb_search_tvstd(const char *name)
{
 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(!strncasecmp(name, sis_tvtype[i].name, strlen(sis_tvtype[i].name))) {
   sisfb_tvstd = sis_tvtype[i].type_no;
   break;
  }
  i++;
 }
}

static void __init
sisfb_search_specialtiming(const char *name)
{
 int i = 0;
 bool found = false;

 /* We don't know the hardware specs yet and there is no ivideo */

 if(name == NULL)
  return;

 if(!strncasecmp(name, "none", 4)) {
  sisfb_specialtiming = CUT_FORCENONE;
  printk(KERN_DEBUG "sisfb: Special timing disabled\n");
 } else {
  while(mycustomttable[i].chipID != 0) {
   if(!strncasecmp(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++;
   }
  }
 }
}

/* ----------- Various detection routines ----------- */

static void sisfb_detect_custom_timing(struct sis_video_info *ivideo)
{
 unsigned char *biosver = NULL;
 unsigned char *biosdate = NULL;
 bool 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 bool sisfb_interpret_edid(struct sisfb_monitor *monitor, u8 *buffer)
{
 int i, j, xres, yres, refresh, index;
 u32 emodes;

 if(buffer[0] != 0x00 || buffer[1] != 0xff ||
    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;
 }

 if(buffer[0x12] != 0x01) {
  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[0x13] >= 0x01) {
    /* EDID V1 rev 1 and 2: Search for monitor descriptor
    * to extract ranges
    */

     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 + 4] == 0x00) {
    monitor->hmin = buffer[j + 7];
    monitor->hmax = buffer[j + 8];
    monitor->vmin = buffer[j + 5];
    monitor->vmax = buffer[j + 6];
    monitor->dclockmax = buffer[j + 9] * 10 * 1000;
    monitor->datavalid = true;
    break;
        }
        j += 18;
     }
 }

 if(!monitor->datavalid) {
    /* Otherwise: Get a range from the list of supported
    * Estabished Timings. This is not entirely accurate,
    * because fixed frequency monitors are not supported
    * that way.
    */

    monitor->hmin = 65535; monitor->hmax = 0;
    monitor->vmin = 65535; monitor->vmax = 0;
    monitor->dclockmax = 0;
    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->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;
   if(monitor->dclockmax < sisfb_ddcsmodes[i].d) monitor->dclockmax = sisfb_ddcsmodes[i].d;
       }
    }
    index = 0x26;
    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;
       }
       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) &&
         (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[j].d;
      }
   }
       }
       index += 2;
    }
    if((monitor->hmin <= monitor->hmax) && (monitor->vmin <= monitor->vmax)) {
       monitor->datavalid = true;
    }
 }

 return monitor->datavalid;
}

static void sisfb_handle_ddc(struct sis_video_info *ivideo,
        struct sisfb_monitor *monitor, int crtno)
{
 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->sisfb_crt1off) && (!crtno))
  return;

 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) {
       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(!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->dclockmax / 1000);
   } else {
      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);
       }
    } else {
       printk(KERN_INFO "sisfb: VESA D&P and FPDI-2 not supported yet\n");
    }
 }
}

/* -------------- Mode validation --------------- */

static bool
sisfb_verify_rate(struct sis_video_info *ivideo, struct sisfb_monitor *monitor,
  int mode_idx, int rate_idx, int rate)
{
 int htotal, vtotal;
 unsigned int dclock, hsync;

 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
 }

 if(rate < (monitor->vmin - 1))
  return false;
 if(rate > (monitor->vmax + 1))
  return false;

 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;
 }
 return true;
}

static int
sisfb_validate_mode(struct sis_video_info *ivideo, int myindex, u32 vbflags)
{
 u16 xres=0, yres, myres;

#ifdef CONFIG_FB_SIS_300
 if (ivideo->sisvga_engine == SIS_300_VGA) {
  if (!(sisbios_mode[myindex].chipset & MD_SIS300))
   return -1 ;
 }
#endif
#ifdef CONFIG_FB_SIS_315
 if (ivideo->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:
  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;
 }

 return myindex;
}

static u8
sisfb_search_refresh_rate(struct sis_video_info *ivideo, unsigned int rate, int mode_idx)
{
 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((sisfb_vrate[i].idx != 1) &&
      ((rate - sisfb_vrate[i-1].refresh) <= 2)) {
     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 {
  printk(KERN_INFO "sisfb: Unsupported rate %d for %dx%d\n",
    rate, xres, yres);
  return 0;
 }
}

static bool
sisfb_bridgeisslave(struct sis_video_info *ivideo)
{
 unsigned char P1_00;

 if(!(ivideo->vbflags2 & VB2_VIDEOBRIDGE))
  return false;

 P1_00 = SiS_GetReg(SISPART1, 0x00);
 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;
 }
}

static bool
sisfballowretracecrt1(struct sis_video_info *ivideo)
{
 u8 temp;

 temp = SiS_GetReg(SISCR, 0x17);
 if(!(temp & 0x80))
  return false;

 temp = SiS_GetReg(SISSR, 0x1f);
 if(temp & 0xc0)
  return false;

 return true;
}

static bool
sisfbcheckvretracecrt1(struct sis_video_info *ivideo)
{
 if(!sisfballowretracecrt1(ivideo))
  return false;

 if (SiS_GetRegByte(SISINPSTAT) & 0x08)
  return true;
 else
  return false;
}

static void
sisfbwaitretracecrt1(struct sis_video_info *ivideo)
{
 int watchdog;

 if(!sisfballowretracecrt1(ivideo))
  return;

 watchdog = 65536;
 while ((!(SiS_GetRegByte(SISINPSTAT) & 0x08)) && --watchdog);
 watchdog = 65536;
 while ((SiS_GetRegByte(SISINPSTAT) & 0x08) && --watchdog);
}

static bool
sisfbcheckvretracecrt2(struct sis_video_info *ivideo)
{
 unsigned char temp, reg;

 switch(ivideo->sisvga_engine) {
 case SIS_300_VGA: reg = 0x25; break;
 case SIS_315_VGA: reg = 0x30; break;
 default:   return false;
 }

 temp = SiS_GetReg(SISPART1, reg);
 if(temp & 0x02)
  return true;
 else
  return false;
}

static bool
sisfb_CheckVBRetrace(struct sis_video_info *ivideo)
{
 if(ivideo->currentvbflags & VB_DISPTYPE_DISP2) {
  if(!sisfb_bridgeisslave(ivideo)) {
   return sisfbcheckvretracecrt2(ivideo);
  }
 }
 return sisfbcheckvretracecrt1(ivideo);
}

static u32
sisfb_setupvbblankflags(struct sis_video_info *ivideo, u32 *vcount, u32 *hcount)
{
 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;
  }
  reg1 = SiS_GetReg(SISPART1, (idx+0)); /* 30 */
  reg2 = SiS_GetReg(SISPART1, (idx+1)); /* 31 */
  reg3 = SiS_GetReg(SISPART1, (idx+2)); /* 32 */
  reg4 = SiS_GetReg(SISPART1, (idx+3)); /* 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 = SiS_GetRegByte(SISINPSTAT);
  if(reg1 & 0x08) ret |= FB_VBLANK_VSYNCING;
  if(reg1 & 0x01) ret |= FB_VBLANK_VBLANKING;
  reg1 = SiS_GetReg(SISCR, 0x20);
  reg1 = SiS_GetReg(SISCR, 0x1b);
  reg2 = SiS_GetReg(SISCR, 0x1c);
  reg3 = SiS_GetReg(SISCR, 0x1d);
  (*vcount) = reg2 | ((reg3 & 0x07) << 8);
  (*hcount) = (reg1 | ((reg3 & 0x10) << 4)) << 3;
 }

 return ret;
}

static int
sisfb_myblank(struct sis_video_info *ivideo, int blank)
{
 u8 sr01, sr11, sr1f, cr63=0, p2_0, p1_13;
 bool 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;
 }

 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) {
    SiS_SetRegANDOR(SISCR, ivideo->SiS_Pr.SiS_MyCR63, 0xbf, cr63);
   }

   if(!(sisfb_bridgeisslave(ivideo))) {
    SiS_SetRegANDOR(SISSR, 0x01, ~0x20, sr01);
    SiS_SetRegANDOR(SISSR, 0x1f, 0x3f, sr1f);
   }
  }

 }

 if(ivideo->currentvbflags & CRT2_LCD) {

  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
  }

  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))) {
   SiS_SetRegANDOR(SISSR, 0x11, ~0x0c, sr11);
  }

  if(ivideo->sisvga_engine == SIS_300_VGA) {
   if((ivideo->vbflags2 & VB2_30xB) &&
      (!(ivideo->vbflags2 & VB2_30xBDH))) {
    SiS_SetRegANDOR(SISPART1, 0x13, 0x3f, p1_13);
   }
  } else if(ivideo->sisvga_engine == SIS_315_VGA) {
   if((ivideo->vbflags2 & VB2_30xB) &&
      (!(ivideo->vbflags2 & VB2_30xBDH))) {
    SiS_SetRegANDOR(SISPART2, 0x00, 0x1f, p2_0);
   }
  }

 } else if(ivideo->currentvbflags & CRT2_VGA) {

  if(ivideo->vbflags2 & VB2_30xB) {
   SiS_SetRegANDOR(SISPART2, 0x00, 0x1f, p2_0);
  }

 }

 return 0;
}

/* ------------- 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)
{
   struct sis_video_info *ivideo = (struct sis_video_info *)SiS_Pr->ivideo;
   u32 val = 0;

   pci_read_config_dword(ivideo->nbridge, reg, &val);
   return (unsigned int)val;
}

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);
}

unsigned int
sisfb_read_lpc_pci_dword(struct SiS_Private *SiS_Pr, int reg)
{
   struct sis_video_info *ivideo = (struct sis_video_info *)SiS_Pr->ivideo;
   u32 val = 0;

   if(!ivideo->lpcdev) return 0;

   pci_read_config_dword(ivideo->lpcdev, reg, &val);
   return (unsigned int)val;
}
#endif

#ifdef CONFIG_FB_SIS_315
void
sisfb_write_nbridge_pci_byte(struct SiS_Private *SiS_Pr, int reg, unsigned char val)
{
   struct sis_video_info *ivideo = (struct sis_video_info *)SiS_Pr->ivideo;

   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;

   if(!ivideo->lpcdev) return 0;

   pci_read_config_word(ivideo->lpcdev, reg, &val);
   return (unsigned int)val;
}
#endif

/* ----------- FBDev related routines for all series ----------- */

static int
sisfb_get_cmap_len(const struct fb_var_screeninfo *var)
{
 return (var->bits_per_pixel == 8) ? 256 : 16;
}

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 int
sisfb_calc_maxyres(struct sis_video_info *ivideo, struct fb_var_screeninfo *var)
{
 int maxyres = ivideo->sisfb_mem / (var->xres_virtual * (var->bits_per_pixel >> 3));

 if(maxyres > 32767) maxyres = 32767;

 return maxyres;
}

static void
sisfb_calc_pitch(struct sis_video_info *ivideo, struct fb_var_screeninfo *var)
{
 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 void
sisfb_set_pitch(struct sis_video_info *ivideo)
{
 bool 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)) {
  SiS_SetReg(SISCR, 0x13, (HDisplay1 & 0xFF));
  SiS_SetRegANDOR(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)) {
  SiS_SetRegOR(SISPART1, ivideo->CRT2_write_enable, 0x01);
  SiS_SetReg(SISPART1, 0x07, (HDisplay2 & 0xFF));
  SiS_SetRegANDOR(SISPART1, 0x09, 0xF0, (HDisplay2 >> 8));
 }
}

static void
sisfb_bpp_to_var(struct sis_video_info *ivideo, struct fb_var_screeninfo *var)
{
 ivideo->video_cmap_len = sisfb_get_cmap_len(var);

 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 = 8;
  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 int
sisfb_set_mode(struct sis_video_info *ivideo, int clrscrn)
{
 unsigned short modeno = ivideo->mode_no;

 /* >=2.6.12's fbcon clears the screen anyway */
 modeno |= 0x80;

 SiS_SetReg(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);

 sisfb_pre_setmode(ivideo);

 if(!SiSSetMode(&ivideo->SiS_Pr, modeno)) {
  printk(KERN_ERR "sisfb: Setting mode[0x%x] failed\n", ivideo->mode_no);
  return -EINVAL;
 }

 SiS_SetReg(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)
{
 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, ret;
 int old_mode;
 u32 pixclock;

 htotal = var->left_margin + var->xres + var->right_margin + var->hsync_len;

 vtotal = var->upper_margin + var->lower_margin + var->vsync_len;

 pixclock = var->pixclock;

 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;

 if(!(htotal) || !(vtotal)) {
  DPRINTK("sisfb: Invalid 'var' information\n");
  return -EINVAL;
 }

 if(pixclock && htotal && vtotal) {
  drate = 1000000000 / pixclock;
  hrate = (drate * 1000) / htotal;
  ivideo->refresh_rate = (unsigned int) (hrate * 2 / vtotal);
 } else {
  ivideo->refresh_rate = 60;
 }

 old_mode = ivideo->sisfb_mode_idx;
 ivideo->sisfb_mode_idx = 0;

 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;
  }
  ivideo->sisfb_mode_idx++;
 }

 if(found_mode) {
  ivideo->sisfb_mode_idx = sisfb_validate_mode(ivideo,
    ivideo->sisfb_mode_idx, ivideo->currentvbflags);
 } else {
  ivideo->sisfb_mode_idx = -1;
 }

        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);
  ivideo->sisfb_mode_idx = old_mode;
  return -EINVAL;
 }

 ivideo->mode_no = sisbios_mode[ivideo->sisfb_mode_idx].mode_no[ivideo->mni];

 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(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(!(info->flags & FBINFO_HWACCEL_DISABLED)) ivideo->accel = -1;
#else
  if(var->accel_flags & FB_ACCELF_TEXT) ivideo->accel = -1;
#endif

  if((ret = sisfb_set_mode(ivideo, 1))) {
   return ret;
  }

  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_calc_pitch(ivideo, var);
  sisfb_set_pitch(ivideo);

  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 void
sisfb_set_base_CRT1(struct sis_video_info *ivideo, unsigned int base)
{
 SiS_SetReg(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);

 SiS_SetReg(SISCR, 0x0D, base & 0xFF);
 SiS_SetReg(SISCR, 0x0C, (base >> 8) & 0xFF);
 SiS_SetReg(SISSR, 0x0D, (base >> 16) & 0xFF);
 if(ivideo->sisvga_engine == SIS_315_VGA) {
  SiS_SetRegANDOR(SISSR, 0x37, 0xFE, (base >> 24) & 0x01);
 }
}

static void
sisfb_set_base_CRT2(struct sis_video_info *ivideo, unsigned int base)
{
 if(ivideo->currentvbflags & VB_DISPTYPE_DISP2) {
  SiS_SetRegOR(SISPART1, ivideo->CRT2_write_enable, 0x01);
  SiS_SetReg(SISPART1, 0x06, (base & 0xFF));
  SiS_SetReg(SISPART1, 0x05, ((base >> 8) & 0xFF));
  SiS_SetReg(SISPART1, 0x04, ((base >> 16) & 0xFF));
  if(ivideo->sisvga_engine == SIS_315_VGA) {
   SiS_SetRegANDOR(SISPART1, 0x02, 0x7F, ((base >> 24) & 0x01) << 7);
  }
 }
}

static int
sisfb_pan_var(struct sis_video_info *ivideo, struct fb_info *info,
       struct fb_var_screeninfo *var)
{
 ivideo->current_base = var->yoffset * info->var.xres_virtual
        + var->xoffset;

 /* calculate base bpp dep. */
 switch (info->var.bits_per_pixel) {
 case 32:
  break;
 case 16:
  ivideo->current_base >>= 1;
  break;
 case 8:
 default:
  ivideo->current_base >>= 2;
  break;
 }

 ivideo->current_base += (ivideo->video_offset >> 2);

 sisfb_set_base_CRT1(ivideo, ivideo->current_base);
 sisfb_set_base_CRT2(ivideo, ivideo->current_base);

 return 0;
}

static int
sisfb_open(struct fb_info *info, int user)
{
 return 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 *info)
{
 struct sis_video_info *ivideo = (struct sis_video_info *)info->par;

 if(regno >= sisfb_get_cmap_len(&info->var))
  return 1;

 switch(info->var.bits_per_pixel) {
 case 8:
  SiS_SetRegByte(SISDACA, regno);
  SiS_SetRegByte(SISDACD, (red >> 10));
  SiS_SetRegByte(SISDACD, (green >> 10));
  SiS_SetRegByte(SISDACD, (blue >> 10));
  if(ivideo->currentvbflags & VB_DISPTYPE_DISP2) {
   SiS_SetRegByte(SISDAC2A, regno);
   SiS_SetRegByte(SISDAC2D, (red >> 8));
   SiS_SetRegByte(SISDAC2D, (green >> 8));
   SiS_SetRegByte(SISDAC2D, (blue >> 8));
  }
  break;
 case 16:
  if (regno >= 16)
   break;

  ((u32 *)(info->pseudo_palette))[regno] =
    (red & 0xf800)          |
    ((green & 0xfc00) >> 5) |
    ((blue & 0xf800) >> 11);
  break;
 case 32:
  if (regno >= 16)
   break;

  red >>= 8;
  green >>= 8;
  blue >>= 8;
  ((u32 *)(info->pseudo_palette))[regno] =
    (red << 16) | (green << 8) | (blue);
  break;
 }
 return 0;
}

static int
sisfb_set_par(struct fb_info *info)
{
 int err;

 if((err = sisfb_do_set_var(&info->var, 1, info)))
  return err;

 sisfb_get_fix(&info->fix, -1, info);

 return 0;
}

static int
sisfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
 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;
 bool recalc_clock = false;
 u32 pixclock;

 htotal = var->left_margin + var->xres + var->right_margin + var->hsync_len;

 vtotal = var->upper_margin + var->lower_margin + var->vsync_len;

 if (!var->pixclock)
  return -EINVAL;
 pixclock = var->pixclock;

 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;

 if(!(htotal) || !(vtotal)) {
  SISFAIL("sisfb: no valid timing data");
 }

 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++;
 }

 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;
 }

 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;
  }
 }

 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");
  }
 }

 /* Adapt RGB settings */
 sisfb_bpp_to_var(ivideo, var);

 if(var->xres > var->xres_virtual)
  var->xres_virtual = var->xres;

 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;
 }

 /* 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;

 return 0;
}

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 (var->vmode & FB_VMODE_YWRAP)
  return -EINVAL;

 if (var->xoffset + info->var.xres > info->var.xres_virtual ||
     var->yoffset + info->var.yres > info->var.yres_virtual)
  return -EINVAL;

 err = sisfb_pan_var(ivideo, info, var);
 if (err < 0)
  return err;

 info->var.xoffset = var->xoffset;
 info->var.yoffset = var->yoffset;

 return 0;
}

static int
sisfb_blank(int blank, struct fb_info *info)
{
 struct sis_video_info *ivideo = (struct sis_video_info *)info->par;

 return sisfb_myblank(ivideo, blank);
}

/* ----------- FBDev related routines for all series ---------- */

static int sisfb_ioctl(struct fb_info *info, unsigned int cmd,
       unsigned long arg)
{
 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;

 switch(cmd) {
    case FBIO_ALLOC:
  if(!capable(CAP_SYS_RAWIO))
   return -EPERM;

  if(copy_from_user(&sismemreq, (void __user *)arg, sizeof(sismemreq)))
   return -EFAULT;

  sis_malloc(&sismemreq);

  if(copy_to_user((void __user *)arg, &sismemreq, sizeof(sismemreq))) {
   sis_free((u32)sismemreq.offset);
   return -EFAULT;
  }
  break;

    case FBIO_FREE:
  if(!capable(CAP_SYS_RAWIO))
   return -EPERM;

  if(get_user(gpu32, argp))
   return -EFAULT;

  sis_free(gpu32);
  break;

    case FBIOGET_VBLANK:

  memset(&sisvbblank, 0, sizeof(struct fb_vblank));

  sisvbblank.count = 0;
  sisvbblank.flags = sisfb_setupvbblankflags(ivideo, &sisvbblank.vcount, &sisvbblank.hcount);

  if(copy_to_user((void __user *)arg, &sisvbblank, sizeof(sisvbblank)))
   return -EFAULT;

  break;

    case SISFB_GET_INFO_SIZE:
  return put_user(sizeof(struct sisfb_info), argp);

    case SISFB_GET_INFO_OLD:
  if(ivideo->warncount++ < 10)
   printk(KERN_INFO
    "sisfb: Deprecated ioctl call received - update your application!\n");
  fallthrough;
    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;

         break;

    case SISFB_GET_VBRSTATUS_OLD:
  if(ivideo->warncount++ < 10)
   printk(KERN_INFO
    "sisfb: Deprecated ioctl call received - update your application!\n");
  fallthrough;
    case SISFB_GET_VBRSTATUS:
  if(sisfb_CheckVBRetrace(ivideo))
   return put_user((u32)1, argp);
  else
   return put_user((u32)0, argp);

    case SISFB_GET_AUTOMAXIMIZE_OLD:
  if(ivideo->warncount++ < 10)
   printk(KERN_INFO
    "sisfb: Deprecated ioctl call received - update your application!\n");
  fallthrough;
    case SISFB_GET_AUTOMAXIMIZE:
  if(ivideo->sisfb_max)
   return put_user((u32)1, argp);
  else
   return put_user((u32)0, argp);

    case SISFB_SET_AUTOMAXIMIZE_OLD:
  if(ivideo->warncount++ < 10)
   printk(KERN_INFO
    "sisfb: Deprecated ioctl call received - update your application!\n");
  fallthrough;
    case SISFB_SET_AUTOMAXIMIZE:
  if(get_user(gpu32, argp))
   return -EFAULT;

  ivideo->sisfb_max = (gpu32) ? 1 : 0;
  break;

    case SISFB_SET_TVPOSOFFSET:
  if(get_user(gpu32, argp))
   return -EFAULT;

  sisfb_set_TVxposoffset(ivideo, ((int)(gpu32 >> 16)) - 32);
  sisfb_set_TVyposoffset(ivideo, ((int)(gpu32 & 0xffff)) - 32);
  break;

    case SISFB_GET_TVPOSOFFSET:
  return put_user((u32)(((ivideo->tvxpos+32)<<16)|((ivideo->tvypos+32)&0xffff)),
       argp);

    case SISFB_COMMAND:
  if(copy_from_user(&ivideo->sisfb_command, (void __user *)arg,
       sizeof(struct sisfb_cmd)))
   return -EFAULT;

  sisfb_handle_command(ivideo, &ivideo->sisfb_command);

  if(copy_to_user((void __user *)arg, &ivideo->sisfb_command,
       sizeof(struct sisfb_cmd)))
   return -EFAULT;

  break;

    case SISFB_SET_LOCK:
  if(get_user(gpu32, argp))
   return -EFAULT;

  ivideo->sisfblocked = (gpu32) ? 1 : 0;
  break;

    default:
#ifdef SIS_NEW_CONFIG_COMPAT
  return -ENOIOCTLCMD;
#else
  return -EINVAL;
#endif
 }
 return 0;
}

static int
sisfb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
{
 struct sis_video_info *ivideo = (struct sis_video_info *)info->par;

 memset(fix, 0, sizeof(struct fb_fix_screeninfo));

 strscpy(fix->id, ivideo->myid, sizeof(fix->id));

 mutex_lock(&info->mm_lock);
 fix->smem_start  = ivideo->video_base + ivideo->video_offset;
 fix->smem_len    = ivideo->sisfb_mem;
 mutex_unlock(&info->mm_lock);
 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;
}

/* ----------------  fb_ops structures ----------------- */

static const struct fb_ops sisfb_ops = {
 .owner  = THIS_MODULE,
 .fb_open = sisfb_open,
 .fb_release = sisfb_release,
 __FB_DEFAULT_IOMEM_OPS_RDWR,
 .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_sync = fbcon_sis_sync,
#ifdef SIS_NEW_CONFIG_COMPAT
 .fb_compat_ioctl= sisfb_ioctl,
#endif
 .fb_ioctl = sisfb_ioctl,
 __FB_DEFAULT_IOMEM_OPS_MMAP,
};

/* ---------------- Chip generation dependent routines ---------------- */

static struct pci_dev *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
 defaultreturn NULL;
 }
 for(i = 0; i < nbridgenum; i++) {
  if((pdev = pci_get_device(PCI_VENDOR_ID_SI,
    nbridgeids[nbridgeidx+i], NULL)))
   break;
 }
 return pdev;
}

static int sisfb_get_dram_size(struct sis_video_info *ivideo)
{
#if defined(CONFIG_FB_SIS_300) || defined(CONFIG_FB_SIS_315)
 u8 reg;
#endif

 ivideo->video_size = 0;
 ivideo->UMAsize = ivideo->LFBsize = 0;

 switch(ivideo->chip) {
#ifdef CONFIG_FB_SIS_300
 case SIS_300:
  reg = SiS_GetReg(SISSR, 0x14);
  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, ®);
  ivideo->video_size = 1 << (((reg & 0x70) >> 4) + 21);
  break;
#endif
#ifdef CONFIG_FB_SIS_315
 case SIS_315H:
 case SIS_315PRO:
 case SIS_315:
  reg = SiS_GetReg(SISSR, 0x14);
  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:
  reg = SiS_GetReg(SISSR, 0x14);
  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:
  reg = SiS_GetReg(SISSR, 0x14);
  ivideo->video_size = (((reg & 0x3f) + 1) << 2) << 20;
  break;
 case SIS_661:
 case SIS_741:
  reg = SiS_GetReg(SISCR, 0x79);
  ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20;
  break;
 case SIS_660:
 case SIS_760:
 case SIS_761:
  reg = SiS_GetReg(SISCR, 0x79);
  reg = (reg & 0xf0) >> 4;
  if(reg) {
   ivideo->video_size = (1 << reg) << 20;
   ivideo->UMAsize = ivideo->video_size;
  }
  reg = SiS_GetReg(SISCR, 0x78);
  reg &= 0x30;
  if(reg) {
   if(reg == 0x10) {
    ivideo->LFBsize = (32 << 20);
   } else {
    ivideo->LFBsize = (64 << 20);
   }
   ivideo->video_size += ivideo->LFBsize;
  }
  break;
 case SIS_340:
 case XGI_20:
 case XGI_40:
  reg = SiS_GetReg(SISSR, 0x14);
  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;
}

/* -------------- video bridge device detection --------------- */

static void sisfb_detect_VB_connect(struct sis_video_info *ivideo)
{
 u8 cr32, temp;

 /* No CRT2 on XGI Z7 */
 if(ivideo->chip == XGI_20) {
  ivideo->sisfb_crt1off = 0;
  return;
 }

#ifdef CONFIG_FB_SIS_300
 if(ivideo->sisvga_engine == SIS_300_VGA) {
  temp = SiS_GetReg(SISSR, 0x17);
  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))) {
    temp = SiS_GetReg(SISSR, 0x16);
    if(temp & 0x20)
     ivideo->vbflags |= TV_PAL;
    else
     ivideo->vbflags |= TV_NTSC;
   }
  }
 }
#endif

 cr32 = SiS_GetReg(SISCR, 0x32);

 if(cr32 & SIS_CRT1) {
  ivideo->sisfb_crt1off = 0;
 } else {
  ivideo->sisfb_crt1off = (cr32 & 0xDF) ? 1 : 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;

 /* 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(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(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");
       }
    }
 }
 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_PALM | TV_PALN | TV_NTSCJ)) {
   ivideo->sisfb_tvstd = -1;
   printk(KERN_ERR "sisfb: PALM/PALN/NTSCJ not supported\n");
       }
    }
 }

 /* 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;
  }
 }

 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) {
   temp = SiS_GetReg(SISSR, 0x38);
   if(temp & 0x01) ivideo->vbflags |= TV_PAL;
   else  ivideo->vbflags |= TV_NTSC;
  } else if((ivideo->chip <= SIS_315PRO) || (ivideo->chip >= SIS_330)) {
   temp = SiS_GetReg(SISSR, 0x38);
   if(temp & 0x01) ivideo->vbflags |= TV_PAL;
   else  ivideo->vbflags |= TV_NTSC;
  } else {
   temp = SiS_GetReg(SISCR, 0x79);
   if(temp & 0x20) ivideo->vbflags |= TV_PAL;
   else  ivideo->vbflags |= TV_NTSC;
  }
     }
 }

 /* Copy forceCRT1 option to CRT1off if option is given */
 if(ivideo->sisfb_forcecrt1 != -1) {
    ivideo->sisfb_crt1off = (ivideo->sisfb_forcecrt1) ? 0 : 1;
 }
}

/* ------------------ Sensing routines ------------------ */

static bool sisfb_test_DDC1(struct sis_video_info *ivideo)
{
    unsigned short old;
    int count = 48;

    old = SiS_ReadDDC1Bit(&ivideo->SiS_Pr);
    do {
 if(old != SiS_ReadDDC1Bit(&ivideo->SiS_Pr)) break;
    } while(count--);
    return (count != -1);
}

static void sisfb_sense_crt1(struct sis_video_info *ivideo)
{
 bool mustwait = false;
 u8  sr1F, cr17;
#ifdef CONFIG_FB_SIS_315
 u8  cr63 = 0;
#endif
 u16 temp = 0xffff;
 int i;

 sr1F = SiS_GetReg(SISSR, 0x1F);
 SiS_SetRegOR(SISSR, 0x1F, 0x04);
 SiS_SetRegAND(SISSR, 0x1F, 0x3F);

 if (sr1F & 0xc0)
  mustwait = true;

#ifdef CONFIG_FB_SIS_315
 if (ivideo->sisvga_engine == SIS_315_VGA) {
  cr63 = SiS_GetReg(SISCR, ivideo->SiS_Pr.SiS_MyCR63);
  cr63 &= 0x40;
  SiS_SetRegAND(SISCR, ivideo->SiS_Pr.SiS_MyCR63, 0xBF);
 }
#endif

 cr17 = SiS_GetReg(SISCR, 0x17);
 cr17 &= 0x80;

 if (!cr17) {
  SiS_SetRegOR(SISCR, 0x17, 0x80);
  mustwait = true;
  SiS_SetReg(SISSR, 0x00, 0x01);
  SiS_SetReg(SISSR, 0x00, 0x03);
 }

 if (mustwait) {
  for (i = 0; i < 10; i++)
   sisfbwaitretracecrt1(ivideo);
 }
#ifdef CONFIG_FB_SIS_315
 if (ivideo->chip >= SIS_330) {
  SiS_SetRegAND(SISCR, 0x32, ~0x20);
  if (ivideo->chip >= SIS_340)
   SiS_SetReg(SISCR, 0x57, 0x4a);
  else
   SiS_SetReg(SISCR, 0x57, 0x5f);

  SiS_SetRegOR(SISCR, 0x53, 0x02);
  while ((SiS_GetRegByte(SISINPSTAT)) & 0x01)
   break;
  while (!((SiS_GetRegByte(SISINPSTAT)) & 0x01))
   break;
  if ((SiS_GetRegByte(SISMISCW)) & 0x10)
   temp = 1;

  SiS_SetRegAND(SISCR, 0x53, 0xfd);
  SiS_SetRegAND(SISCR, 0x57, 0x00);
 }
#endif

 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 ((temp) && (temp != 0xffff))
  SiS_SetRegOR(SISCR, 0x32, 0x20);

#ifdef CONFIG_FB_SIS_315
 if (ivideo->sisvga_engine == SIS_315_VGA)
  SiS_SetRegANDOR(SISCR, ivideo->SiS_Pr.SiS_MyCR63, 0xBF, cr63);
#endif

 SiS_SetRegANDOR(SISCR, 0x17, 0x7F, cr17);
 SiS_SetReg(SISSR, 0x1F, sr1F);
}

/* Determine and detect attached devices on SiS30x */
static void SiS_SenseLCD(struct sis_video_info *ivideo)
{
 unsigned char buffer[256];
 unsigned short temp, realcrtno, i;
 u8 reg, cr37 = 0, paneltype = 0;
 u16 xres, yres;

 ivideo->SiS_Pr.PanelSelfDetected = false;

 /* LCD detection only for TMDS bridges */
 if (!(ivideo->vbflags2 & VB2_SISTMDSBRIDGE))
  return;
 if (ivideo->vbflags2 & VB2_30xBDH)
  return;

 /* If LCD already set up by BIOS, skip it */
 reg = SiS_GetReg(SISCR, 0x32);
 if (reg & 0x08)
  return;

 realcrtno = 1;
 if (ivideo->SiS_Pr.DDCPortMixup)
  realcrtno = 0;

 /* Check DDC capabilities */
 temp = SiS_HandleDDC(&ivideo->SiS_Pr, ivideo->vbflags, ivideo->sisvga_engine,
    realcrtno, 0, &buffer[0], ivideo->vbflags2);

 if ((!temp) || (temp == 0xffff) || (!(temp & 0x02)))
  return;

 /* 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 (temp)
  return;

 /* No digital device */
 if (!(buffer[0x14] & 0x80))
  return;

 /* First detailed timing preferred timing? */
 if (!(buffer[0x18] & 0x02))
  return;

 xres = buffer[0x38] | ((buffer[0x3a] & 0xf0) << 4);
 yres = buffer[0x3b] | ((buffer[0x3d] & 0xf0) << 4);

 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;
 }

 if (!paneltype)
  return;

 if (buffer[0x23])
  cr37 |= 0x10;

 if ((buffer[0x47] & 0x18) == 0x18)
  cr37 |= ((((buffer[0x47] & 0x06) ^ 0x06) << 5) | 0x20);
 else
  cr37 |= 0xc0;

 SiS_SetReg(SISCR, 0x36, paneltype);
 cr37 &= 0xf1;
 SiS_SetRegANDOR(SISCR, 0x37, 0x0c, cr37);
 SiS_SetRegOR(SISCR, 0x32, 0x08);

 ivideo->SiS_Pr.PanelSelfDetected = true;
}

static int SISDoSense(struct sis_video_info *ivideo, u16 type, u16 test)
{
 int temp, mytest, result, i, j;

 for (j = 0; j < 10; j++) {
  result = 0;
  for (i = 0; i < 3; i++) {
   mytest = test;
   SiS_SetReg(SISPART4, 0x11, (type & 0x00ff));
   temp = (type >> 8) | (mytest & 0x00ff);
   SiS_SetRegANDOR(SISPART4, 0x10, 0xe0, temp);
   SiS_DDC2Delay(&ivideo->SiS_Pr, 0x1500);
   mytest >>= 8;
   mytest &= 0x7f;
   temp = SiS_GetReg(SISPART4, 0x03);
   temp ^= 0x0e;
   temp &= mytest;
   if (temp == mytest)
    result++;
#if 1
   SiS_SetReg(SISPART4, 0x11, 0x00);
   SiS_SetRegAND(SISPART4, 0x10, 0xe0);
   SiS_DDC2Delay(&ivideo->SiS_Pr, 0x1000);
#endif
  }

  if ((result == 0) || (result >= 2))
   break;
 }
 return result;
}

static void SiS_Sense30x(struct sis_video_info *ivideo)
{
    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;
       myflag = SiS_GetReg(SISPART4, 0x01);
       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;
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=94 G=95

¤ Dauer der Verarbeitung: 0.40 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.