ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / video / sis / sis_main.c
1 /*
2  * SiS 300/630/730/540/315/550/650/651/M650/661FX/M661FX/740/741/M741/330/760
3  * frame buffer driver for Linux kernels 2.4.x and 2.5.x
4  *
5  * Copyright (C) 2001-2004 Thomas Winischhofer, Vienna, Austria.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the named License,
10  * or any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
20  *
21  * Author:      Thomas Winischhofer <thomas@winischhofer.net>
22  *
23  * Author of code base:
24  *              SiS (www.sis.com.tw)
25  *              Copyright (C) 1999 Silicon Integrated Systems, Inc.
26  *
27  * See http://www.winischhofer.net/ for more information and updates
28  *
29  * Originally based on the VBE 2.0 compliant graphic boards framebuffer driver,
30  * which is (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
31  *
32  */
33
34 #include <linux/config.h>
35 #include <linux/version.h>
36 #include <linux/module.h>
37 #include <linux/kernel.h>
38 #include <linux/errno.h>
39 #include <linux/string.h>
40 #include <linux/mm.h>
41 #include <linux/tty.h>
42 #include <linux/slab.h>
43 #include <linux/delay.h>
44 #include <linux/fb.h>
45 #include <linux/console.h>
46 #include <linux/selection.h>
47 #include <linux/ioport.h>
48 #include <linux/init.h>
49 #include <linux/pci.h>
50 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
51 #include <linux/vmalloc.h>
52 #endif
53 #include <linux/vt_kern.h>
54 #include <linux/capability.h>
55 #include <linux/fs.h>
56 #include <linux/agp_backend.h>
57 #include <linux/types.h>
58 #include <asm/uaccess.h>
59
60 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
61 #include <linux/spinlock.h>
62 #endif
63
64 #include "osdef.h"
65
66 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
67 #include <video/sisfb.h>
68 #else
69 #include <linux/sisfb.h>
70 #endif
71
72 #include <asm/io.h>
73 #ifdef CONFIG_MTRR
74 #include <asm/mtrr.h>
75 #endif
76
77 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
78 #include <video/fbcon.h>
79 #include <video/fbcon-cfb8.h>
80 #include <video/fbcon-cfb16.h>
81 #include <video/fbcon-cfb24.h>
82 #include <video/fbcon-cfb32.h>
83 #endif
84
85 #include "vgatypes.h"
86 #include "sis_main.h"
87 #include "sis.h"
88
89 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
90 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
91 #error "This version of sisfb requires at least 2.6.0"
92 #else
93 #if 0
94 #define NEWFBDEV                /* Define this as soon as new fvdev code has been merged */
95 #endif
96 #endif
97 #endif
98
99 /* -------------------- Macro definitions ---------------------------- */
100
101 #undef SISFBDEBUG       /* TW: no debugging */
102
103 #ifdef SISFBDEBUG
104 #define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
105 #else
106 #define DPRINTK(fmt, args...)
107 #endif
108
109 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
110 #ifdef SISFBACCEL
111 #ifdef FBCON_HAS_CFB8
112 extern struct display_switch fbcon_sis8;
113 #endif
114 #ifdef FBCON_HAS_CFB16
115 extern struct display_switch fbcon_sis16;
116 #endif
117 #ifdef FBCON_HAS_CFB32
118 extern struct display_switch fbcon_sis32;
119 #endif
120 #endif
121 #endif
122
123 /* --------------- Hardware Access Routines -------------------------- */
124
125 void sisfb_set_reg4(u16 port, unsigned long data)
126 {
127         outl((u32) (data & 0xffffffff), port);
128 }
129
130 u32 sisfb_get_reg3(u16 port)
131 {
132         u32 data;
133
134         data = inl(port);
135         return (data);
136 }
137
138 /* ------------ Interface for init & mode switching code ------------- */
139
140 BOOLEAN
141 sisfb_query_VGA_config_space(PSIS_HW_INFO psishw_ext,
142         unsigned long offset, unsigned long set, unsigned long *value)
143 {
144         static struct pci_dev *pdev = NULL;
145         static unsigned char init = 0, valid_pdev = 0;
146
147         if (!set)
148                 DPRINTK("sisfb: Get VGA offset 0x%lx\n", offset);
149         else
150                 DPRINTK("sisfb: Set offset 0x%lx to 0x%lx\n", offset, *value);
151
152         if (!init) {
153                 init = TRUE;
154 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,74)
155                 pci_for_each_dev(pdev) {
156 #else
157                 while((pdev = pci_find_device(PCI_VENDOR_ID_SI, PCI_ANY_ID, pdev))) {
158 #endif
159                         DPRINTK("sisfb: Current: 0x%x, target: 0x%x\n",
160                                  pdev->device, ivideo.chip_id);
161                         if ((pdev->vendor == PCI_VENDOR_ID_SI)
162                                    && (pdev->device == ivideo.chip_id)) {
163                                 valid_pdev = TRUE;
164                                 break;
165                         }
166                 }
167         }
168
169         if (!valid_pdev) {
170                 printk(KERN_DEBUG "sisfb: Can't find SiS %d VGA device.\n",
171                                 ivideo.chip_id);
172                 return FALSE;
173         }
174
175         if (set == 0)
176                 pci_read_config_dword(pdev, offset, (u32 *)value);
177         else
178                 pci_write_config_dword(pdev, offset, (u32)(*value));
179
180         return TRUE;
181 }
182
183 BOOLEAN sisfb_query_north_bridge_space(PSIS_HW_INFO psishw_ext,
184         unsigned long offset, unsigned long set, unsigned long *value)
185 {
186         static struct pci_dev *pdev = NULL;
187         static unsigned char init = 0, valid_pdev = 0;
188         u16 nbridge_id = 0;
189
190         if (!init) {
191                 init = TRUE;
192                 switch (ivideo.chip) {
193 #ifdef CONFIG_FB_SIS_300
194                 case SIS_540:
195                         nbridge_id = PCI_DEVICE_ID_SI_540;
196                         break;
197                 case SIS_630:
198                         nbridge_id = PCI_DEVICE_ID_SI_630;
199                         break;
200                 case SIS_730:
201                         nbridge_id = PCI_DEVICE_ID_SI_730;
202                         break;
203 #endif
204 #ifdef CONFIG_FB_SIS_315
205                 case SIS_550:
206                         nbridge_id = PCI_DEVICE_ID_SI_550;
207                         break;
208                 case SIS_650:
209                         nbridge_id = PCI_DEVICE_ID_SI_650;
210                         break;
211                 case SIS_740:
212                         nbridge_id = PCI_DEVICE_ID_SI_740;
213                         break;
214                 case SIS_661:
215                         nbridge_id = PCI_DEVICE_ID_SI_660;
216                         break;
217                 case SIS_741:
218                         nbridge_id = PCI_DEVICE_ID_SI_741;
219                         break;
220                 case SIS_660:
221                         nbridge_id = PCI_DEVICE_ID_SI_660;
222                         break;
223                 case SIS_760:
224                         nbridge_id = PCI_DEVICE_ID_SI_760;
225                         break;
226 #endif
227                 default:
228                         nbridge_id = 0;
229                         break;
230                 }
231
232 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,74)
233                 pci_for_each_dev(pdev) {
234 #else
235                 while((pdev = pci_find_device(PCI_VENDOR_ID_SI, PCI_ANY_ID, pdev))) {
236 #endif
237                         DPRINTK("Current: 0x%x, target: 0x%x\n",
238                                         pdev->device, ivideo.chip_id);
239                         if ((pdev->vendor == PCI_VENDOR_ID_SI)
240                                         && (pdev->device == nbridge_id)) {
241                                 valid_pdev = TRUE;
242                                 break;
243                         }
244                 }
245         }
246
247         if (!valid_pdev) {
248                 printk(KERN_DEBUG "sisfb: Can't find SiS %d North Bridge device.\n",
249                                 nbridge_id);
250                 return FALSE;
251         }
252
253         if (set == 0)
254                 pci_read_config_dword(pdev, offset, (u32 *)value);
255         else
256                 pci_write_config_dword(pdev, offset, (u32)(*value));
257
258         return TRUE;
259 }
260
261 /* ------------------ Internal helper routines ----------------- */
262
263 static BOOLEAN sisfb_verify_rate(struct sisfb_monitor *monitor, int mode_idx, int rate_idx, int rate)
264 {
265         int htotal, vtotal;
266         unsigned int dclock, hsync;
267
268         if(!monitor->datavalid) return TRUE;
269
270         if(mode_idx < 0) return FALSE;
271
272         if(rate < (monitor->vmin - 1)) return FALSE;
273         if(rate > (monitor->vmax + 1)) return FALSE;
274
275         if(sisfb_gettotalfrommode(&SiS_Pr, &sishw_ext, sisbios_mode[mode_idx].mode_no,
276                                   &htotal, &vtotal, rate_idx)) {
277                 dclock = (htotal * vtotal * rate) / 1000;
278                 if(dclock > (monitor->dclockmax + 1000)) return FALSE;
279                 hsync = dclock / htotal;
280                 if(hsync < (monitor->hmin - 1)) return FALSE;
281                 if(hsync > (monitor->hmax + 1)) return FALSE;
282         } else {
283                 return FALSE;
284         }
285         return TRUE;
286 };
287
288 static BOOLEAN sisfb_interpret_edid(struct sisfb_monitor *monitor, u8 *buffer)
289 {
290         int i, j, xres, yres, refresh, index;
291         u32 emodes;
292
293         if(buffer[0] != 0x00 || buffer[1] != 0xff ||
294            buffer[2] != 0xff || buffer[3] != 0xff ||
295            buffer[4] != 0xff || buffer[5] != 0xff ||
296            buffer[6] != 0xff || buffer[7] != 0x00) {
297            printk(KERN_DEBUG "sisfb: Bad EDID header\n");
298            return FALSE;
299         }
300
301         if(buffer[0x12] != 0x01) {
302            printk(KERN_INFO "sisfb: EDID version %d not supported\n",
303                 buffer[0x12]);
304            return FALSE;
305         }
306
307         monitor->feature = buffer[0x18];
308
309         if(!buffer[0x14] & 0x80) {
310            if(!(buffer[0x14] & 0x08)) {
311               printk(KERN_INFO "sisfb: WARNING: Monitor does not support separate syncs\n");
312            }
313         }
314
315         if(buffer[0x13] >= 0x01) {
316            /* EDID V1 rev 1 and 2: Search for monitor descriptor
317             * to extract ranges
318             */
319             j = 0x36;
320             for(i=0; i<4; i++) {
321                if(buffer[j]     == 0x00 && buffer[j + 1] == 0x00 &&
322                   buffer[j + 2] == 0x00 && buffer[j + 3] == 0xfd &&
323                   buffer[j + 4] == 0x00) {
324                   monitor->hmin = buffer[j + 7];
325                   monitor->hmax = buffer[j + 8];
326                   monitor->vmin = buffer[j + 5];
327                   monitor->vmax = buffer[j + 6];
328                   monitor->dclockmax = buffer[j + 9] * 10 * 1000;
329                   monitor->datavalid = TRUE;
330                   break;
331                }
332                j += 18;
333             }
334         }
335
336         if(!monitor->datavalid) {
337            /* Otherwise: Get a range from the list of supported
338             * Estabished Timings. This is not entirely accurate,
339             * because fixed frequency monitors are not supported
340             * that way.
341             */
342            monitor->hmin = 65535; monitor->hmax = 0;
343            monitor->vmin = 65535; monitor->vmax = 0;
344            monitor->dclockmax = 0;
345            emodes = buffer[0x23] | (buffer[0x24] << 8) | (buffer[0x25] << 16);
346            for(i = 0; i < 13; i++) {
347               if(emodes & sisfb_ddcsmodes[i].mask) {
348                  if(monitor->hmin > sisfb_ddcsmodes[i].h) monitor->hmin = sisfb_ddcsmodes[i].h;
349                  if(monitor->hmax < sisfb_ddcsmodes[i].h) monitor->hmax = sisfb_ddcsmodes[i].h + 1;
350                  if(monitor->vmin > sisfb_ddcsmodes[i].v) monitor->vmin = sisfb_ddcsmodes[i].v;
351                  if(monitor->vmax < sisfb_ddcsmodes[i].v) monitor->vmax = sisfb_ddcsmodes[i].v;
352                  if(monitor->dclockmax < sisfb_ddcsmodes[i].d) monitor->dclockmax = sisfb_ddcsmodes[i].d;
353               }
354            }
355            index = 0x26;
356            for(i = 0; i < 8; i++) {
357               xres = (buffer[index] + 31) * 8;
358               switch(buffer[index + 1] & 0xc0) {
359                  case 0xc0: yres = (xres * 9) / 16; break;
360                  case 0x80: yres = (xres * 4) /  5; break;
361                  case 0x40: yres = (xres * 3) /  4; break;
362                  default:   yres = xres;            break;
363               }
364               refresh = (buffer[index + 1] & 0x3f) + 60;
365               if((xres >= 640) && (yres >= 480)) {
366                  for(j = 0; j < 8; j++) {
367                     if((xres == sisfb_ddcfmodes[j].x) &&
368                        (yres == sisfb_ddcfmodes[j].y) &&
369                        (refresh == sisfb_ddcfmodes[j].v)) {
370                       if(monitor->hmin > sisfb_ddcfmodes[j].h) monitor->hmin = sisfb_ddcfmodes[j].h;
371                       if(monitor->hmax < sisfb_ddcfmodes[j].h) monitor->hmax = sisfb_ddcfmodes[j].h + 1;
372                       if(monitor->vmin > sisfb_ddcsmodes[j].v) monitor->vmin = sisfb_ddcsmodes[j].v;
373                       if(monitor->vmax < sisfb_ddcsmodes[j].v) monitor->vmax = sisfb_ddcsmodes[j].v;
374                       if(monitor->dclockmax < sisfb_ddcsmodes[j].d) monitor->dclockmax = sisfb_ddcsmodes[i].d;
375                     }
376                  }
377               }
378               index += 2;
379            }
380            if((monitor->hmin <= monitor->hmax) && (monitor->vmin <= monitor->vmax)) {
381               monitor->datavalid = TRUE;
382            }
383         }
384
385         return(monitor->datavalid);
386 }
387
388 static void sisfb_handle_ddc(struct sisfb_monitor *monitor, int crtno)
389 {
390         USHORT  temp, i, realcrtno = crtno;
391         u8      buffer[256];
392
393         monitor->datavalid = FALSE;
394
395         if(crtno) {
396            if(ivideo.vbflags & CRT2_LCD)      realcrtno = 1;
397            else if(ivideo.vbflags & CRT2_VGA) realcrtno = 2;
398            else return;
399         }
400
401         if((sisfb_crt1off) && (!crtno)) return;
402
403         temp = SiS_HandleDDC(&SiS_Pr, ivideo.vbflags, sisvga_engine, realcrtno, 0, &buffer[0]);
404         if((!temp) || (temp == 0xffff)) {
405            printk(KERN_INFO "sisfb: CRT%d DDC probing failed\n", crtno + 1);
406            return;
407         } else {
408            printk(KERN_INFO "sisfb: CRT%d DDC supported\n", crtno + 1);
409            printk(KERN_INFO "sisfb: CRT%d DDC level: %s%s%s%s\n",
410                 crtno + 1,
411                 (temp & 0x1a) ? "" : "[none of the supported]",
412                 (temp & 0x02) ? "2 " : "",
413                 (temp & 0x08) ? "D&P" : "",
414                 (temp & 0x10) ? "FPDI-2" : "");
415            if(temp & 0x02) {
416               i = 3;  /* Number of retrys */
417               do {
418                  temp = SiS_HandleDDC(&SiS_Pr, ivideo.vbflags, sisvga_engine,
419                                      realcrtno, 1, &buffer[0]);
420               } while((temp) && i--);
421               if(!temp) {
422                  if(sisfb_interpret_edid(monitor, &buffer[0])) {
423                     printk(KERN_INFO "sisfb: Monitor range H %d-%dKHz, V %d-%dHz, Max. dotclock %dMHz\n",
424                         monitor->hmin, monitor->hmax, monitor->vmin, monitor->vmax,
425                         monitor->dclockmax / 1000);
426                  } else {
427                     printk(KERN_INFO "sisfb: CRT%d DDC EDID corrupt\n", crtno + 1);
428                  }
429               } else {
430                  printk(KERN_INFO "sisfb: CRT%d DDC reading failed\n", crtno + 1);
431               }
432            } else {
433               printk(KERN_INFO "sisfb: VESA D&P and FPDI-2 not supported yet\n");
434            }
435         }
436 }
437
438 static void sisfb_search_vesamode(unsigned int vesamode, BOOLEAN quiet)
439 {
440         int i = 0, j = 0;
441
442         if(vesamode == 0) {
443 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
444                 sisfb_mode_idx = MODE_INDEX_NONE;
445 #else
446                 if(!quiet)
447                    printk(KERN_ERR "sisfb: Mode 'none' not supported anymore. Using default.\n");
448                 sisfb_mode_idx = DEFAULT_MODE;
449 #endif
450                 return;
451         }
452
453         vesamode &= 0x1dff;  /* Clean VESA mode number from other flags */
454
455         while(sisbios_mode[i++].mode_no != 0) {
456                 if( (sisbios_mode[i-1].vesa_mode_no_1 == vesamode) ||
457                     (sisbios_mode[i-1].vesa_mode_no_2 == vesamode) ) {
458                     if(sisfb_fstn) {
459                        if(sisbios_mode[i-1].mode_no == 0x50 ||
460                           sisbios_mode[i-1].mode_no == 0x56 ||
461                           sisbios_mode[i-1].mode_no == 0x53) continue;
462                     } else {
463                        if(sisbios_mode[i-1].mode_no == 0x5a ||
464                           sisbios_mode[i-1].mode_no == 0x5b) continue;
465                     }
466                     sisfb_mode_idx = i - 1;
467                     j = 1;
468                     break;
469                 }
470         }
471         if((!j) && !quiet) printk(KERN_ERR "sisfb: Invalid VESA mode 0x%x'\n", vesamode);
472 }
473
474 static void sisfb_search_mode(char *name, BOOLEAN quiet)
475 {
476         int i = 0;
477         unsigned int j = 0, xres = 0, yres = 0, depth = 0, rate = 0;
478         char strbuf[16], strbuf1[20];
479         char *nameptr = name;
480
481         if(name == NULL) {
482            if(!quiet)
483               printk(KERN_ERR "sisfb: Internal error, using default mode.\n");
484            sisfb_mode_idx = DEFAULT_MODE;
485            return;
486         }
487
488 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
489         if (!strnicmp(name, sisbios_mode[MODE_INDEX_NONE].name, strlen(name))) {
490            if(!quiet)
491               printk(KERN_ERR "sisfb: Mode 'none' not supported anymore. Using default.\n");
492            sisfb_mode_idx = DEFAULT_MODE;
493            return;
494         }
495 #endif
496         if(strlen(name) <= 19) {
497            strcpy(strbuf1, name);
498            for(i=0; i<strlen(strbuf1); i++) {
499               if(strbuf1[i] < '0' || strbuf1[i] > '9') strbuf1[i] = ' ';
500            }
501
502            /* This does some fuzzy mode naming detection */
503            if(sscanf(strbuf1, "%u %u %u %u", &xres, &yres, &depth, &rate) == 4) {
504               if((rate <= 32) || (depth > 32)) {
505                  j = rate; rate = depth; depth = j;
506               }
507               sprintf(strbuf, "%ux%ux%u", xres, yres, depth);
508               nameptr = strbuf;
509               ivideo.refresh_rate = sisfb_parm_rate = rate;
510            } else if(sscanf(strbuf1, "%u %u %u", &xres, &yres, &depth) == 3) {
511               sprintf(strbuf, "%ux%ux%u", xres, yres, depth);
512               nameptr = strbuf;
513            } else {
514               xres = 0;
515               if((sscanf(strbuf1, "%u %u", &xres, &yres) == 2) && (xres != 0)) {
516                  sprintf(strbuf, "%ux%ux8", xres, yres);
517                  nameptr = strbuf;
518               } else {
519                  sisfb_search_vesamode(simple_strtoul(name, NULL, 0), quiet);
520                  return;
521               }
522            }
523         }
524
525         i = 0; j = 0;
526         while(sisbios_mode[i].mode_no != 0) {
527                 if(!strnicmp(nameptr, sisbios_mode[i++].name, strlen(nameptr))) {
528                    if(sisfb_fstn) {
529                       if(sisbios_mode[i-1].mode_no == 0x50 ||
530                          sisbios_mode[i-1].mode_no == 0x56 ||
531                          sisbios_mode[i-1].mode_no == 0x53) continue;
532                    } else {
533                       if(sisbios_mode[i-1].mode_no == 0x5a ||
534                          sisbios_mode[i-1].mode_no == 0x5b) continue;
535                    }
536                    sisfb_mode_idx = i - 1;
537                    j = 1;
538                    break;
539                 }
540         }
541         if((!j) && !quiet) printk(KERN_ERR "sisfb: Invalid mode '%s'\n", nameptr);
542
543 }
544
545 static int sisfb_validate_mode(int myindex, unsigned long vbflags)
546 {
547    u16 xres, yres, myres;
548
549 #ifdef CONFIG_FB_SIS_300
550    if(sisvga_engine == SIS_300_VGA) {
551       if(!(sisbios_mode[myindex].chipset & MD_SIS300)) return(-1);
552    }
553 #endif
554 #ifdef CONFIG_FB_SIS_315
555    if(sisvga_engine == SIS_315_VGA) {
556       if(!(sisbios_mode[myindex].chipset & MD_SIS315)) return(-1);
557    }
558 #endif
559
560    myres = sisbios_mode[myindex].yres;
561
562    switch (vbflags & VB_DISPTYPE_DISP2) {
563      case CRT2_LCD:
564         switch (sishw_ext.ulCRT2LCDType) {
565         case LCD_640x480:  xres =  640; yres =  480;  break;
566         case LCD_800x600:  xres =  800; yres =  600;  break;
567         case LCD_1024x600: xres = 1024; yres =  600;  break;
568         case LCD_1024x768: xres = 1024; yres =  768;  break;
569         case LCD_1152x768: xres = 1152; yres =  768;  break;
570         case LCD_1280x960: xres = 1280; yres =  960;  break;
571         case LCD_1280x768: xres = 1280; yres =  768;  break;
572         case LCD_1280x1024:xres = 1280; yres = 1024;  break;
573         case LCD_1400x1050:xres = 1400; yres = 1050;  break;
574         case LCD_1600x1200:xres = 1600; yres = 1200;  break;
575         case LCD_320x480:  xres =  320; yres =  480;  break; /* FSTN (old) */
576         case LCD_640x480_2:
577         case LCD_640x480_3:xres =  640; yres =  480;  break; /* FSTN (new) */
578         default:           xres =    0; yres =    0;  break;
579         }
580
581         if(SiS_Pr.SiS_CustomT == CUT_BARCO1366) {
582            xres = 1360; yres = 1024;
583         }
584
585         if(SiS_Pr.SiS_CustomT == CUT_PANEL848) {
586            xres = 848;  yres =  480;
587         } else {
588            if(sisbios_mode[myindex].xres > xres) return(-1);
589            if(myres > yres) return(-1);
590         }
591
592         if(vbflags & (VB_LVDS | VB_30xBDH)) {
593            if(sisbios_mode[myindex].xres == 320) {
594               if((myres == 240) || (myres == 480)) {
595                  if(!sisfb_fstn) {
596                     if(sisbios_mode[myindex].mode_no == 0x5a ||
597                        sisbios_mode[myindex].mode_no == 0x5b)
598                        return(-1);
599                  } else {
600                     if(sisbios_mode[myindex].mode_no == 0x50 ||
601                        sisbios_mode[myindex].mode_no == 0x56 ||
602                        sisbios_mode[myindex].mode_no == 0x53)
603                        return(-1);
604                  }
605               }
606            }
607         }
608
609         if(SiS_GetModeID_LCD(sisvga_engine, vbflags, sisbios_mode[myindex].xres, sisbios_mode[myindex].yres,
610                              0, sisfb_fstn, SiS_Pr.SiS_CustomT, xres, yres) < 0x14) {
611            return(-1);
612         }
613         break;
614
615      case CRT2_TV:
616         if(SiS_GetModeID_TV(sisvga_engine, vbflags, sisbios_mode[myindex].xres,
617                             sisbios_mode[myindex].yres, 0) < 0x14) {
618            return(-1);
619         }
620         break;
621
622      case CRT2_VGA:
623         if(SiS_GetModeID_VGA2(sisvga_engine, vbflags, sisbios_mode[myindex].xres,
624                             sisbios_mode[myindex].yres, 0) < 0x14) {
625            return(-1);
626         }
627         break;
628      }
629
630      return(myindex);
631 }
632
633 static void sisfb_search_crt2type(const char *name)
634 {
635         int i = 0;
636
637         if(name == NULL)
638                 return;
639
640         while(sis_crt2type[i].type_no != -1) {
641                 if (!strnicmp(name, sis_crt2type[i].name, strlen(sis_crt2type[i].name))) {
642                         sisfb_crt2type = sis_crt2type[i].type_no;
643                         sisfb_tvplug = sis_crt2type[i].tvplug_no;
644                         sisfb_dstn = (sis_crt2type[i].flags & FL_550_DSTN) ? 1 : 0;
645                         sisfb_fstn = (sis_crt2type[i].flags & FL_550_FSTN) ? 1 : 0;
646                         break;
647                 }
648                 i++;
649         }
650         if(sisfb_crt2type < 0)
651                 printk(KERN_ERR "sisfb: Invalid CRT2 type: %s\n", name);
652         if(ivideo.chip != SIS_550) {
653            sisfb_dstn = sisfb_fstn = 0;
654         }
655 }
656
657 static void sisfb_search_queuemode(const char *name)
658 {
659         int i = 0;
660
661         if(name == NULL)
662                 return;
663
664         while (sis_queuemode[i].type_no != -1) {
665                 if (!strnicmp(name, sis_queuemode[i].name, strlen(sis_queuemode[i].name))) {
666                         sisfb_queuemode = sis_queuemode[i].type_no;
667                         break;
668                 }
669                 i++;
670         }
671         if (sisfb_queuemode < 0)
672                 printk(KERN_ERR "sisfb: Invalid queuemode type: %s\n", name);
673 }
674
675 static u8 sisfb_search_refresh_rate(unsigned int rate, int mode_idx)
676 {
677         u16 xres, yres;
678         int i = 0;
679
680         xres = sisbios_mode[mode_idx].xres;
681         yres = sisbios_mode[mode_idx].yres;
682
683         sisfb_rate_idx = 0;
684         while ((sisfb_vrate[i].idx != 0) && (sisfb_vrate[i].xres <= xres)) {
685                 if ((sisfb_vrate[i].xres == xres) && (sisfb_vrate[i].yres == yres)) {
686                         if (sisfb_vrate[i].refresh == rate) {
687                                 sisfb_rate_idx = sisfb_vrate[i].idx;
688                                 break;
689                         } else if (sisfb_vrate[i].refresh > rate) {
690                                 if ((sisfb_vrate[i].refresh - rate) <= 3) {
691                                         DPRINTK("sisfb: Adjusting rate from %d up to %d\n",
692                                                 rate, sisfb_vrate[i].refresh);
693                                         sisfb_rate_idx = sisfb_vrate[i].idx;
694                                         ivideo.refresh_rate = sisfb_vrate[i].refresh;
695                                 } else if (((rate - sisfb_vrate[i-1].refresh) <= 2)
696                                                 && (sisfb_vrate[i].idx != 1)) {
697                                         DPRINTK("sisfb: Adjusting rate from %d down to %d\n",
698                                                 rate, sisfb_vrate[i-1].refresh);
699                                         sisfb_rate_idx = sisfb_vrate[i-1].idx;
700                                         ivideo.refresh_rate = sisfb_vrate[i-1].refresh;
701                                 } 
702                                 break;
703                         } else if((rate - sisfb_vrate[i].refresh) <= 2) {
704                                 DPRINTK("sisfb: Adjusting rate from %d down to %d\n",
705                                                 rate, sisfb_vrate[i].refresh);
706                                 sisfb_rate_idx = sisfb_vrate[i].idx;
707                                 break;
708                         }
709                 }
710                 i++;
711         }
712         if (sisfb_rate_idx > 0) {
713                 return sisfb_rate_idx;
714         } else {
715                 printk(KERN_INFO
716                         "sisfb: Unsupported rate %d for %dx%d\n", rate, xres, yres);
717                 return 0;
718         }
719 }
720
721 static void sisfb_search_tvstd(const char *name)
722 {
723         int i = 0;
724
725         if(name == NULL)
726                 return;
727
728         while (sis_tvtype[i].type_no != -1) {
729                 if (!strnicmp(name, sis_tvtype[i].name, strlen(sis_tvtype[i].name))) {
730                         ivideo.vbflags |= sis_tvtype[i].type_no;
731                         break;
732                 }
733                 i++;
734         }
735 }
736
737 static void sisfb_search_specialtiming(const char *name)
738 {
739         int i = 0;
740         BOOLEAN found = FALSE;
741
742         if(name == NULL)
743                 return;
744
745         if(!strnicmp(name, "none", 4)) {
746                 SiS_Pr.SiS_CustomT = CUT_FORCENONE;
747                 printk(KERN_DEBUG "sisfb: Special timing disabled\n");
748         } else {
749            while(mycustomttable[i].chipID != 0) {
750               if(!strnicmp(name,mycustomttable[i].optionName, strlen(mycustomttable[i].optionName))) {
751                  SiS_Pr.SiS_CustomT = mycustomttable[i].SpecialID;
752                  found = TRUE;
753                  printk(KERN_INFO "sisfb: Special timing for %s %s forced (\"%s\")\n",
754                         mycustomttable[i].vendorName, mycustomttable[i].cardName,
755                         mycustomttable[i].optionName);
756                  break;
757               }
758               i++;
759            }
760            if(!found) {
761               printk(KERN_WARNING "sisfb: Invalid SpecialTiming parameter, valid are:");
762               printk(KERN_WARNING "\t\"none\" (to disable special timings)\n");
763               i = 0;
764               while(mycustomttable[i].chipID != 0) {
765                  printk(KERN_WARNING "\t\"%s\" (for %s %s)\n",
766                      mycustomttable[i].optionName,
767                      mycustomttable[i].vendorName,
768                      mycustomttable[i].cardName);
769                  i++;
770               }
771            }
772         }
773 }
774
775 static BOOLEAN sisfb_bridgeisslave(void)
776 {
777    unsigned char P1_00;
778
779    if(!(ivideo.vbflags & VB_VIDEOBRIDGE)) return FALSE;
780
781    inSISIDXREG(SISPART1,0x00,P1_00);
782    if( ((sisvga_engine == SIS_300_VGA) && (P1_00 & 0xa0) == 0x20) ||
783        ((sisvga_engine == SIS_315_VGA) && (P1_00 & 0x50) == 0x10) ) {
784            return TRUE;
785    } else {
786            return FALSE;
787    }
788 }
789
790 static BOOLEAN sisfballowretracecrt1(void)
791 {
792    unsigned char temp;
793
794    inSISIDXREG(SISCR,0x17,temp);
795    if(!(temp & 0x80)) return FALSE;
796
797    inSISIDXREG(SISSR,0x1f,temp);
798    if(temp & 0xc0) return FALSE;
799
800    return TRUE;
801 }
802
803 static BOOLEAN sisfbcheckvretracecrt1(void)
804 {
805
806    if(!sisfballowretracecrt1()) return FALSE;
807
808    if(inSISREG(SISINPSTAT) & 0x08) return TRUE;
809    else                            return FALSE;
810 }
811
812 static void sisfbwaitretracecrt1(void)
813 {
814    int watchdog;
815
816    if(!sisfballowretracecrt1()) return;
817
818    watchdog = 65536;
819    while((!(inSISREG(SISINPSTAT) & 0x08)) && --watchdog);
820    watchdog = 65536;
821    while((inSISREG(SISINPSTAT) & 0x08) && --watchdog);
822 }
823
824 static BOOLEAN sisfbcheckvretracecrt2(void)
825 {
826    unsigned char temp, reg;
827
828    switch(sisvga_engine) {
829    case SIS_300_VGA:
830         reg = 0x25;
831         break;
832    case SIS_315_VGA:
833         reg = 0x30;
834         break;
835    default:
836         return FALSE;
837    }
838
839    inSISIDXREG(SISPART1, reg, temp);
840    if(temp & 0x02) return FALSE;
841    else            return TRUE;
842 }
843
844 static BOOLEAN sisfb_CheckVBRetrace(void)
845 {
846    if(ivideo.currentvbflags & VB_DISPTYPE_DISP2) {
847       if(sisfb_bridgeisslave()) {
848          return(sisfbcheckvretracecrt1());
849       } else {
850          return(sisfbcheckvretracecrt2());
851       }
852    } 
853    return(sisfbcheckvretracecrt1());
854 }
855
856 static int sisfb_myblank(int blank)
857 {
858    u8 sr01, sr11, sr1f, cr63=0, p2_0, p1_13;
859    BOOLEAN backlight = TRUE;
860
861    switch(blank) {
862    case 0:      /* on */
863       sr01  = 0x00;
864       sr11  = 0x00;
865       sr1f  = 0x00;
866       cr63  = 0x00;
867       p2_0  = 0x20;
868       p1_13 = 0x00;
869       backlight = TRUE;
870       break;
871    case 1:      /* blank */
872       sr01  = 0x20;
873       sr11  = 0x00;
874       sr1f  = 0x00;
875       cr63  = 0x00;
876       p2_0  = 0x20;
877       p1_13 = 0x00;
878       backlight = TRUE;
879       break;
880    case 2:      /* no vsync */
881       sr01  = 0x20;
882       sr11  = 0x08;
883       sr1f  = 0x80;
884       cr63  = 0x40;
885       p2_0  = 0x40;
886       p1_13 = 0x80;
887       backlight = FALSE;
888       break;
889    case 3:      /* no hsync */
890       sr01  = 0x20;
891       sr11  = 0x08;
892       sr1f  = 0x40;
893       cr63  = 0x40;
894       p2_0  = 0x80;
895       p1_13 = 0x40;
896       backlight = FALSE;
897       break;
898    case 4:      /* off */
899       sr01  = 0x20;
900       sr11  = 0x08;
901       sr1f  = 0xc0;
902       cr63  = 0x40;
903       p2_0  = 0xc0;
904       p1_13 = 0xc0;
905       backlight = FALSE;
906       break;
907    default:
908       return 1;
909    }
910
911    if(ivideo.currentvbflags & VB_DISPTYPE_CRT1) {
912
913       setSISIDXREG(SISSR, 0x01, ~0x20, sr01);
914
915       if( (!sisfb_thismonitor.datavalid) ||
916           ((sisfb_thismonitor.datavalid) &&
917            (sisfb_thismonitor.feature & 0xe0))) {
918
919          if(sisvga_engine == SIS_315_VGA) {
920             setSISIDXREG(SISCR, SiS_Pr.SiS_MyCR63, 0xbf, cr63);
921          }
922
923          setSISIDXREG(SISSR, 0x1f, 0x3f, sr1f);
924       }
925
926    }
927
928    if(ivideo.currentvbflags & CRT2_LCD) {
929
930       if(ivideo.vbflags & (VB_301LV|VB_302LV|VB_302ELV)) {
931          if(backlight) {
932             SiS_SiS30xBLOn(&SiS_Pr, &sishw_ext);
933          } else {
934             SiS_SiS30xBLOff(&SiS_Pr, &sishw_ext);
935          }
936       } else if(sisvga_engine == SIS_315_VGA) {
937          if(ivideo.vbflags & VB_CHRONTEL) {
938             if(backlight) {
939                SiS_Chrontel701xBLOn(&SiS_Pr,&sishw_ext);
940             } else {
941                SiS_Chrontel701xBLOff(&SiS_Pr);
942             }
943          }
944       }
945
946       if(((sisvga_engine == SIS_300_VGA) &&
947           (ivideo.vbflags & (VB_301|VB_30xBDH|VB_LVDS))) ||
948          ((sisvga_engine == SIS_315_VGA) &&
949           ((ivideo.vbflags & (VB_LVDS | VB_CHRONTEL)) == VB_LVDS))) {
950           setSISIDXREG(SISSR, 0x11, ~0x0c, sr11);
951       }
952
953       if(sisvga_engine == SIS_300_VGA) {
954          if((ivideo.vbflags & (VB_301B|VB_301C|VB_302B)) &&
955             (!(ivideo.vbflags & VB_30xBDH))) {
956             setSISIDXREG(SISPART1, 0x13, 0x3f, p1_13);
957          }
958       } else if(sisvga_engine == SIS_315_VGA) {
959          if((ivideo.vbflags & (VB_301B|VB_301C|VB_302B)) &&
960             (!(ivideo.vbflags & VB_30xBDH))) {
961             setSISIDXREG(SISPART2, 0x00, 0x1f, p2_0);
962          }
963       }
964
965    } else if(ivideo.currentvbflags & CRT2_VGA) {
966
967       if(ivideo.vbflags & (VB_301B|VB_301C|VB_302B)) {
968          setSISIDXREG(SISPART2, 0x00, 0x1f, p2_0);
969       }
970
971    }
972
973    return(0);
974 }
975
976 /* ----------- FBDev related routines for all series ----------- */
977
978 static void sisfb_set_vparms(void)
979 {
980    switch(ivideo.video_bpp) {
981    case 8:
982         ivideo.DstColor = 0x0000;
983         ivideo.SiS310_AccelDepth = 0x00000000;
984         ivideo.video_cmap_len = 256;
985         break;
986    case 16:
987         ivideo.DstColor = 0x8000;
988         ivideo.SiS310_AccelDepth = 0x00010000;
989         ivideo.video_cmap_len = 16;
990         break;
991    case 32:
992         ivideo.DstColor = 0xC000;
993         ivideo.SiS310_AccelDepth = 0x00020000;
994         ivideo.video_cmap_len = 16;
995         break;
996    default:
997         ivideo.video_cmap_len = 16;
998         printk(KERN_ERR "sisfb: Unsupported depth %d", ivideo.video_bpp);
999         ivideo.accel = 0;
1000         break;
1001    }
1002 }
1003
1004 static int sisfb_do_set_var(struct fb_var_screeninfo *var, int isactive,
1005                       struct fb_info *info)
1006 {
1007         unsigned int htotal = 0, vtotal = 0;
1008         unsigned int drate = 0, hrate = 0;
1009         int found_mode = 0;
1010         int old_mode;
1011         u32 pixclock;
1012
1013         htotal = var->left_margin + var->xres + var->right_margin + var->hsync_len;
1014
1015         vtotal = var->upper_margin + var->lower_margin + var->vsync_len;
1016
1017         pixclock = var->pixclock;
1018
1019         if((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) {
1020                 vtotal += var->yres;
1021                 vtotal <<= 1;
1022         } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
1023                 vtotal += var->yres;
1024                 vtotal <<= 2;
1025         } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
1026                 vtotal += var->yres;
1027                 vtotal <<= 1;
1028         } else  vtotal += var->yres;
1029
1030         if(!(htotal) || !(vtotal)) {
1031                 DPRINTK("sisfb: Invalid 'var' information\n");
1032                 return -EINVAL;
1033         }
1034
1035         if(pixclock && htotal && vtotal) {
1036            drate = 1000000000 / pixclock;
1037            hrate = (drate * 1000) / htotal;
1038            ivideo.refresh_rate = (unsigned int) (hrate * 2 / vtotal);
1039         } else ivideo.refresh_rate = 60;
1040
1041 #if 0
1042         printk(KERN_DEBUG "sisfb: Change mode to %dx%dx%d-%dHz\n",
1043                 var->xres,var->yres,var->bits_per_pixel,ivideo.refresh_rate);
1044 #endif
1045
1046         old_mode = sisfb_mode_idx;
1047         sisfb_mode_idx = 0;
1048
1049         while( (sisbios_mode[sisfb_mode_idx].mode_no != 0) &&
1050                (sisbios_mode[sisfb_mode_idx].xres <= var->xres) ) {
1051                 if( (sisbios_mode[sisfb_mode_idx].xres == var->xres) &&
1052                     (sisbios_mode[sisfb_mode_idx].yres == var->yres) &&
1053                     (sisbios_mode[sisfb_mode_idx].bpp == var->bits_per_pixel)) {
1054                         sisfb_mode_no = sisbios_mode[sisfb_mode_idx].mode_no;
1055                         found_mode = 1;
1056                         break;
1057                 }
1058                 sisfb_mode_idx++;
1059         }
1060
1061         if(found_mode)
1062                 sisfb_mode_idx = sisfb_validate_mode(sisfb_mode_idx, ivideo.currentvbflags);
1063         else
1064                 sisfb_mode_idx = -1;
1065
1066         if(sisfb_mode_idx < 0) {
1067                 printk(KERN_ERR "sisfb: Mode %dx%dx%d not supported\n", var->xres,
1068                        var->yres, var->bits_per_pixel);
1069                 sisfb_mode_idx = old_mode;
1070                 return -EINVAL;
1071         }
1072
1073         if(sisfb_search_refresh_rate(ivideo.refresh_rate, sisfb_mode_idx) == 0) {
1074                 sisfb_rate_idx = sisbios_mode[sisfb_mode_idx].rate_idx;
1075                 ivideo.refresh_rate = 60;
1076         }
1077
1078 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
1079         if(sisfb_thismonitor.datavalid) {
1080            if(!sisfb_verify_rate(&sisfb_thismonitor, sisfb_mode_idx,
1081                                  sisfb_rate_idx, ivideo.refresh_rate)) {
1082               printk(KERN_INFO "sisfb: WARNING: Refresh rate exceeds monitor specs!\n");
1083            }
1084         }
1085 #endif
1086
1087 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
1088         if(((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) && isactive) {
1089 #else
1090         if(isactive) {
1091 #endif
1092                 sisfb_pre_setmode();
1093
1094                 if(SiSSetMode(&SiS_Pr, &sishw_ext, sisfb_mode_no) == 0) {
1095                         printk(KERN_ERR "sisfb: Setting mode[0x%x] failed\n", sisfb_mode_no);
1096                         return -EINVAL;
1097                 }
1098
1099                 outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
1100
1101                 ivideo.video_bpp = sisbios_mode[sisfb_mode_idx].bpp;
1102                 ivideo.video_vwidth = ivideo.video_width = sisbios_mode[sisfb_mode_idx].xres;
1103                 ivideo.video_vheight = ivideo.video_height = sisbios_mode[sisfb_mode_idx].yres;
1104                 ivideo.org_x = ivideo.org_y = 0;
1105                 ivideo.video_linelength = ivideo.video_width * (ivideo.video_bpp >> 3);
1106                 ivideo.accel = 0;
1107                 if(sisfb_accel) {
1108                    ivideo.accel = (var->accel_flags & FB_ACCELF_TEXT) ? -1 : 0;
1109                 }
1110
1111                 sisfb_set_vparms();
1112
1113                 ivideo.current_width = ivideo.video_width;
1114                 ivideo.current_height = ivideo.video_height;
1115                 ivideo.current_bpp = ivideo.video_bpp;
1116                 ivideo.current_htotal = htotal;
1117                 ivideo.current_vtotal = vtotal;
1118                 ivideo.current_pixclock = var->pixclock;
1119                 ivideo.current_refresh_rate = ivideo.refresh_rate;
1120 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
1121                 sisfb_lastrates[sisfb_mode_no] = ivideo.refresh_rate;
1122 #endif
1123
1124                 sisfb_post_setmode();
1125
1126         }
1127         return 0;
1128 }
1129
1130 static int sisfb_pan_var(struct fb_var_screeninfo *var)
1131 {
1132         unsigned int base;
1133
1134         if (var->xoffset > (var->xres_virtual - var->xres)) {
1135                 return -EINVAL;
1136         }
1137         if(var->yoffset > (var->yres_virtual - var->yres)) {
1138                 return -EINVAL;
1139         }
1140
1141         base = var->yoffset * var->xres_virtual + var->xoffset;
1142
1143         /* calculate base bpp dep. */
1144         switch(var->bits_per_pixel) {
1145         case 16:
1146                 base >>= 1;
1147                 break;
1148         case 32:
1149                 break;
1150         case 8:
1151         default:
1152                 base >>= 2;
1153                 break;
1154         }
1155         
1156         outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
1157
1158         outSISIDXREG(SISCR, 0x0D, base & 0xFF);
1159         outSISIDXREG(SISCR, 0x0C, (base >> 8) & 0xFF);
1160         outSISIDXREG(SISSR, 0x0D, (base >> 16) & 0xFF);
1161         if(sisvga_engine == SIS_315_VGA) {
1162                 setSISIDXREG(SISSR, 0x37, 0xFE, (base >> 24) & 0x01);
1163         }
1164         if(ivideo.currentvbflags & VB_DISPTYPE_DISP2) {
1165                 orSISIDXREG(SISPART1, sisfb_CRT2_write_enable, 0x01);
1166                 outSISIDXREG(SISPART1, 0x06, (base & 0xFF));
1167                 outSISIDXREG(SISPART1, 0x05, ((base >> 8) & 0xFF));
1168                 outSISIDXREG(SISPART1, 0x04, ((base >> 16) & 0xFF));
1169                 if(sisvga_engine == SIS_315_VGA) {
1170                         setSISIDXREG(SISPART1, 0x02, 0x7F, ((base >> 24) & 0x01) << 7);
1171                 }
1172         }
1173         return 0;
1174 }
1175
1176 static void sisfb_bpp_to_var(struct fb_var_screeninfo *var)
1177 {
1178         switch(var->bits_per_pixel) {
1179            case 8:
1180                 var->red.offset = var->green.offset = var->blue.offset = 0;
1181                 var->red.length = var->green.length = var->blue.length = 6;
1182                 ivideo.video_cmap_len = 256;
1183                 break;
1184            case 16:
1185                 var->red.offset = 11;
1186                 var->red.length = 5;
1187                 var->green.offset = 5;
1188                 var->green.length = 6;
1189                 var->blue.offset = 0;
1190                 var->blue.length = 5;
1191                 var->transp.offset = 0;
1192                 var->transp.length = 0;
1193                 ivideo.video_cmap_len = 16;
1194                 break;
1195            case 32:
1196                 var->red.offset = 16;
1197                 var->red.length = 8;
1198                 var->green.offset = 8;
1199                 var->green.length = 8;
1200                 var->blue.offset = 0;
1201                 var->blue.length = 8;
1202                 var->transp.offset = 24;
1203                 var->transp.length = 8;
1204                 ivideo.video_cmap_len = 16;
1205                 break;
1206         }
1207 }
1208
1209 void sis_dispinfo(struct ap_data *rec)
1210 {
1211         rec->minfo.bpp      = ivideo.video_bpp;
1212         rec->minfo.xres     = ivideo.video_width;
1213         rec->minfo.yres     = ivideo.video_height;
1214         rec->minfo.v_xres   = ivideo.video_vwidth;
1215         rec->minfo.v_yres   = ivideo.video_vheight;
1216         rec->minfo.org_x    = ivideo.org_x;
1217         rec->minfo.org_y    = ivideo.org_y;
1218         rec->minfo.vrate    = ivideo.refresh_rate;
1219         rec->iobase         = ivideo.vga_base - 0x30;
1220         rec->mem_size       = ivideo.video_size;
1221         rec->disp_state     = ivideo.disp_state;
1222         rec->version        = (VER_MAJOR << 24) | (VER_MINOR << 16) | VER_LEVEL;
1223         rec->hasVB          = ivideo.hasVB;
1224         rec->TV_type        = ivideo.TV_type;
1225         rec->TV_plug        = ivideo.TV_plug;
1226         rec->chip           = ivideo.chip;
1227         rec->vbflags        = ivideo.vbflags;
1228         rec->currentvbflags = ivideo.currentvbflags;
1229 }
1230
1231 /* ------------ FBDev related routines for 2.4 series ----------- */
1232
1233 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
1234
1235 static void sisfb_crtc_to_var(struct fb_var_screeninfo *var)
1236 {
1237         u16 VRE, VBE, VRS, VBS, VDE, VT;
1238         u16 HRE, HBE, HRS, HBS, HDE, HT;
1239         u8  sr_data, cr_data, cr_data2, cr_data3, mr_data;
1240         int A, B, C, D, E, F, temp;
1241         unsigned int hrate, drate, maxyres;
1242
1243         inSISIDXREG(SISSR, IND_SIS_COLOR_MODE, sr_data);
1244
1245         if(sr_data & SIS_INTERLACED_MODE)
1246            var->vmode = FB_VMODE_INTERLACED;
1247         else
1248            var->vmode = FB_VMODE_NONINTERLACED;
1249
1250         switch ((sr_data & 0x1C) >> 2) {
1251            case SIS_8BPP_COLOR_MODE:
1252                 var->bits_per_pixel = 8;
1253                 break;
1254            case SIS_16BPP_COLOR_MODE:
1255                 var->bits_per_pixel = 16;
1256                 break;
1257            case SIS_32BPP_COLOR_MODE:
1258                 var->bits_per_pixel = 32;
1259                 break;
1260         }
1261
1262         sisfb_bpp_to_var(var);
1263         
1264         inSISIDXREG(SISSR, 0x0A, sr_data);
1265
1266         inSISIDXREG(SISCR, 0x06, cr_data);
1267
1268         inSISIDXREG(SISCR, 0x07, cr_data2);
1269
1270         VT = (cr_data & 0xFF) | ((u16) (cr_data2 & 0x01) << 8) |
1271              ((u16) (cr_data2 & 0x20) << 4) | ((u16) (sr_data & 0x01) << 10);
1272         A = VT + 2;
1273
1274         inSISIDXREG(SISCR, 0x12, cr_data);
1275
1276         VDE = (cr_data & 0xff) | ((u16) (cr_data2 & 0x02) << 7) |
1277               ((u16) (cr_data2 & 0x40) << 3) | ((u16) (sr_data & 0x02) << 9);
1278         E = VDE + 1;
1279
1280         inSISIDXREG(SISCR, 0x10, cr_data);
1281
1282         VRS = (cr_data & 0xff) | ((u16) (cr_data2 & 0x04) << 6) |
1283               ((u16) (cr_data2 & 0x80) << 2) | ((u16) (sr_data & 0x08) << 7);
1284         F = VRS + 1 - E;
1285
1286         inSISIDXREG(SISCR, 0x15, cr_data);
1287
1288         inSISIDXREG(SISCR, 0x09, cr_data3);
1289
1290         if(cr_data3 & 0x80) var->vmode = FB_VMODE_DOUBLE;
1291
1292         VBS = (cr_data & 0xff) | ((u16) (cr_data2 & 0x08) << 5) |
1293               ((u16) (cr_data3 & 0x20) << 4) | ((u16) (sr_data & 0x04) << 8);
1294
1295         inSISIDXREG(SISCR, 0x16, cr_data);
1296
1297         VBE = (cr_data & 0xff) | ((u16) (sr_data & 0x10) << 4);
1298         temp = VBE - ((E - 1) & 511);
1299         B = (temp > 0) ? temp : (temp + 512);
1300
1301         inSISIDXREG(SISCR, 0x11, cr_data);
1302
1303         VRE = (cr_data & 0x0f) | ((sr_data & 0x20) >> 1);
1304         temp = VRE - ((E + F - 1) & 31);
1305         C = (temp > 0) ? temp : (temp + 32);
1306
1307         D = B - F - C;
1308
1309         var->yres = E;
1310         var->upper_margin = D;
1311         var->lower_margin = F;
1312         var->vsync_len = C;
1313
1314         if((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
1315            var->yres <<= 1;
1316            var->upper_margin <<= 1;
1317            var->lower_margin <<= 1;
1318            var->vsync_len <<= 1;
1319         } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
1320            var->yres >>= 1;
1321            var->upper_margin >>= 1;
1322            var->lower_margin >>= 1;
1323            var->vsync_len >>= 1;
1324         }
1325
1326         inSISIDXREG(SISSR, 0x0b, sr_data);
1327
1328         inSISIDXREG(SISCR, 0x00, cr_data);
1329
1330         HT = (cr_data & 0xff) | ((u16) (sr_data & 0x03) << 8);
1331         A = HT + 5;
1332
1333         inSISIDXREG(SISCR, 0x01, cr_data);
1334
1335         HDE = (cr_data & 0xff) | ((u16) (sr_data & 0x0C) << 6);
1336         E = HDE + 1;
1337
1338         inSISIDXREG(SISCR, 0x04, cr_data);
1339
1340         HRS = (cr_data & 0xff) | ((u16) (sr_data & 0xC0) << 2);
1341         F = HRS - E - 3;
1342
1343         inSISIDXREG(SISCR, 0x02, cr_data);
1344
1345         HBS = (cr_data & 0xff) | ((u16) (sr_data & 0x30) << 4);
1346
1347         inSISIDXREG(SISSR, 0x0c, sr_data);
1348
1349         inSISIDXREG(SISCR, 0x03, cr_data);
1350
1351         inSISIDXREG(SISCR, 0x05, cr_data2);
1352
1353         HBE = (cr_data & 0x1f) | ((u16) (cr_data2 & 0x80) >> 2) |
1354               ((u16) (sr_data & 0x03) << 6);
1355         HRE = (cr_data2 & 0x1f) | ((sr_data & 0x04) << 3);
1356
1357         temp = HBE - ((E - 1) & 255);
1358         B = (temp > 0) ? temp : (temp + 256);
1359
1360         temp = HRE - ((E + F + 3) & 63);
1361         C = (temp > 0) ? temp : (temp + 64);
1362
1363         D = B - F - C;
1364
1365         var->xres = var->xres_virtual = E * 8;
1366
1367         if((var->xres == 320) &&
1368            (var->yres == 200 || var->yres == 240)) {
1369                 /* Terrible hack, but the correct CRTC data for
1370                  * these modes only produces a black screen...
1371                  */
1372                 var->left_margin = (400 - 376);
1373                 var->right_margin = (328 - 320);
1374                 var->hsync_len = (376 - 328);
1375         } else {
1376                 var->left_margin = D * 8;
1377                 var->right_margin = F * 8;
1378                 var->hsync_len = C * 8;
1379         }
1380         var->activate = FB_ACTIVATE_NOW;
1381
1382         var->sync = 0;
1383
1384         mr_data = inSISREG(SISMISCR);
1385         if(mr_data & 0x80)
1386            var->sync &= ~FB_SYNC_VERT_HIGH_ACT;
1387         else
1388            var->sync |= FB_SYNC_VERT_HIGH_ACT;
1389
1390         if(mr_data & 0x40)
1391            var->sync &= ~FB_SYNC_HOR_HIGH_ACT;
1392         else
1393            var->sync |= FB_SYNC_HOR_HIGH_ACT;
1394
1395         VT += 2;
1396         VT <<= 1;
1397         HT = (HT + 5) * 8;
1398
1399         if((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
1400            VT <<= 1;
1401         }
1402         hrate = ivideo.refresh_rate * VT / 2;
1403         drate = (hrate * HT) / 1000;
1404         var->pixclock = (u32) (1000000000 / drate);
1405
1406         if(sisfb_ypan) {
1407            maxyres = ivideo.heapstart / (var->xres * (var->bits_per_pixel >> 3));
1408            if(maxyres > 32767) maxyres = 32767;
1409            if(sisfb_max) {
1410               var->yres_virtual = maxyres;
1411            } else {
1412               if(var->yres_virtual > maxyres) {
1413                  var->yres_virtual = maxyres;
1414               }
1415            }
1416            if(var->yres_virtual <= var->yres) {
1417               var->yres_virtual = var->yres;
1418            }
1419         } else
1420            var->yres_virtual = var->yres;
1421
1422 }
1423
1424 static int sis_getcolreg(unsigned regno, unsigned *red, unsigned *green, unsigned *blue,
1425                          unsigned *transp, struct fb_info *fb_info)
1426 {
1427         if (regno >= ivideo.video_cmap_len)
1428                 return 1;
1429
1430         *red = sis_palette[regno].red;
1431         *green = sis_palette[regno].green;
1432         *blue = sis_palette[regno].blue;
1433         *transp = 0;
1434         return 0;
1435 }
1436
1437 static int sisfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue,
1438                            unsigned transp, struct fb_info *fb_info)
1439 {
1440         if (regno >= ivideo.video_cmap_len)
1441                 return 1;
1442
1443         sis_palette[regno].red = red;
1444         sis_palette[regno].green = green;
1445         sis_palette[regno].blue = blue;
1446
1447         switch (ivideo.video_bpp) {
1448 #ifdef FBCON_HAS_CFB8
1449         case 8:
1450                 outSISREG(SISDACA, regno);
1451                 outSISREG(SISDACD, (red >> 10));
1452                 outSISREG(SISDACD, (green >> 10));
1453                 outSISREG(SISDACD, (blue >> 10));
1454                 if (ivideo.currentvbflags & VB_DISPTYPE_DISP2) {
1455                         outSISREG(SISDAC2A, regno);
1456                         outSISREG(SISDAC2D, (red >> 8));
1457                         outSISREG(SISDAC2D, (green >> 8));
1458                         outSISREG(SISDAC2D, (blue >> 8));
1459                 }
1460                 break;
1461 #endif
1462 #ifdef FBCON_HAS_CFB16
1463         case 16:
1464                 sis_fbcon_cmap.cfb16[regno] =
1465                     ((red & 0xf800)) | ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11);
1466                 break;
1467 #endif
1468 #ifdef FBCON_HAS_CFB32
1469         case 32:
1470                 red >>= 8;
1471                 green >>= 8;
1472                 blue >>= 8;
1473                 sis_fbcon_cmap.cfb32[regno] = (red << 16) | (green << 8) | (blue);
1474                 break;
1475 #endif
1476         }
1477         return 0;
1478 }
1479
1480 static void sisfb_set_disp(int con, struct fb_var_screeninfo *var,
1481                            struct fb_info *info)
1482 {
1483         struct fb_fix_screeninfo fix;
1484         long   flags;
1485         struct display *display;
1486         struct display_switch *sw;
1487
1488         if(con >= 0)
1489                 display = &fb_display[con];
1490         else
1491                 display = &sis_disp;
1492
1493         sisfb_get_fix(&fix, con, 0);
1494
1495         display->screen_base = ivideo.video_vbase;
1496         display->visual = fix.visual;
1497         display->type = fix.type;
1498         display->type_aux = fix.type_aux;
1499         display->ypanstep = fix.ypanstep;
1500         display->ywrapstep = fix.ywrapstep;
1501         display->line_length = fix.line_length;
1502         display->next_line = fix.line_length;
1503         display->can_soft_blank = 1;
1504         display->inverse = sisfb_inverse;
1505         display->var = *var;
1506
1507         save_flags(flags);
1508
1509         switch (ivideo.video_bpp) {
1510 #ifdef FBCON_HAS_CFB8
1511            case 8:
1512 #ifdef SISFBACCEL
1513                 sw = ivideo.accel ? &fbcon_sis8 : &fbcon_cfb8;
1514 #else
1515                 sw = &fbcon_cfb8;
1516 #endif
1517                 break;
1518 #endif
1519 #ifdef FBCON_HAS_CFB16
1520            case 16:
1521 #ifdef SISFBACCEL
1522                 sw = ivideo.accel ? &fbcon_sis16 : &fbcon_cfb16;
1523 #else
1524                 sw = &fbcon_cfb16;
1525 #endif
1526                 display->dispsw_data = sis_fbcon_cmap.cfb16;
1527                 break;
1528 #endif
1529 #ifdef FBCON_HAS_CFB32
1530            case 32:
1531 #ifdef SISFBACCEL
1532                 sw = ivideo.accel ? &fbcon_sis32 : &fbcon_cfb32;
1533 #else
1534                 sw = &fbcon_cfb32;
1535 #endif
1536                 display->dispsw_data = sis_fbcon_cmap.cfb32;
1537                 break;
1538 #endif
1539            default:
1540                 sw = &fbcon_dummy;
1541                 return;
1542         }
1543         memcpy(&sisfb_sw, sw, sizeof(*sw));
1544         display->dispsw = &sisfb_sw;
1545         restore_flags(flags);
1546
1547         if(sisfb_ypan) {
1548             /* display->scrollmode = 0;  */
1549         } else {
1550             display->scrollmode = SCROLL_YREDRAW;
1551             sisfb_sw.bmove = fbcon_redraw_bmove;
1552         }
1553 }
1554
1555 static void sisfb_do_install_cmap(int con, struct fb_info *info)
1556 {
1557         if (con != currcon)
1558                 return;
1559
1560         if (fb_display[con].cmap.len)
1561                 fb_set_cmap(&fb_display[con].cmap, 1, sisfb_setcolreg, info);
1562         else
1563                 fb_set_cmap(fb_default_cmap(ivideo.video_cmap_len), 1,
1564                             sisfb_setcolreg, info);
1565 }
1566
1567
1568 static int sisfb_get_var(struct fb_var_screeninfo *var, int con,
1569                          struct fb_info *info)
1570 {
1571         if(con == -1)
1572                 memcpy(var, &default_var, sizeof(struct fb_var_screeninfo));
1573         else
1574                 *var = fb_display[con].var;
1575
1576         if(sisfb_fstn) {
1577            if (var->xres == 320 && var->yres == 480)
1578                 var->yres = 240;
1579         }
1580
1581         return 0;
1582 }
1583
1584 static int sisfb_set_var(struct fb_var_screeninfo *var, int con,
1585                          struct fb_info *info)
1586 {
1587         int err;
1588         unsigned int cols, rows;
1589
1590         fb_display[con].var.activate = FB_ACTIVATE_NOW;
1591         if(sisfb_do_set_var(var, con == currcon, info)) {
1592                 sisfb_crtc_to_var(var);
1593                 return -EINVAL;
1594         }
1595
1596         sisfb_crtc_to_var(var);
1597
1598         sisfb_set_disp(con, var, info);
1599
1600         if(info->changevar)
1601                 (*info->changevar) (con);
1602
1603         if((err = fb_alloc_cmap(&fb_display[con].cmap, 0, 0)))
1604                 return err;
1605
1606         sisfb_do_install_cmap(con, info);
1607
1608         cols = sisbios_mode[sisfb_mode_idx].cols;
1609         rows = sisbios_mode[sisfb_mode_idx].rows;
1610 #if 0
1611         /* Why was this called here? */
1612         vc_resize_con(rows, cols, fb_display[con].conp->vc_num);
1613 #endif
1614
1615         return 0;
1616 }
1617
1618 static int sisfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
1619                           struct fb_info *info)
1620 {
1621         if (con == currcon)
1622                 return fb_get_cmap(cmap, kspc, sis_getcolreg, info);
1623
1624         else if (fb_display[con].cmap.len)
1625                 fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
1626         else
1627                 fb_copy_cmap(fb_default_cmap(ivideo.video_cmap_len), cmap, kspc ? 0 : 2);
1628
1629         return 0;
1630 }
1631
1632 static int sisfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
1633                           struct fb_info *info)
1634 {
1635         int err;
1636
1637         if (!fb_display[con].cmap.len) {
1638                 err = fb_alloc_cmap(&fb_display[con].cmap, ivideo.video_cmap_len, 0);
1639                 if (err)
1640                         return err;
1641         }
1642         
1643         if (con == currcon)
1644                 return fb_set_cmap(cmap, kspc, sisfb_setcolreg, info);
1645
1646         else
1647                 fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
1648
1649         return 0;
1650 }
1651
1652 static int sisfb_pan_display(struct fb_var_screeninfo *var, int con,
1653                              struct fb_info* info)
1654 {
1655         int err;
1656
1657         if (var->vmode & FB_VMODE_YWRAP) {
1658                 if (var->yoffset < 0 || var->yoffset >= fb_display[con].var.yres_virtual || var->xoffset)
1659                         return -EINVAL;
1660         } else {
1661                 if (var->xoffset+fb_display[con].var.xres > fb_display[con].var.xres_virtual ||
1662                     var->yoffset+fb_display[con].var.yres > fb_display[con].var.yres_virtual)
1663                         return -EINVAL;
1664         }
1665
1666         if(con == currcon) {
1667            if((err = sisfb_pan_var(var)) < 0) return err;
1668         }
1669
1670         fb_display[con].var.xoffset = var->xoffset;
1671         fb_display[con].var.yoffset = var->yoffset;
1672         if (var->vmode & FB_VMODE_YWRAP)
1673                 fb_display[con].var.vmode |= FB_VMODE_YWRAP;
1674         else
1675                 fb_display[con].var.vmode &= ~FB_VMODE_YWRAP;
1676
1677         return 0;
1678 }
1679
1680 static int sisfb_mmap(struct fb_info *info, struct file *file,
1681                       struct vm_area_struct *vma)
1682 {
1683         struct fb_var_screeninfo var;
1684         unsigned long start;
1685         unsigned long off;
1686         u32 len, mmio_off;
1687
1688         if(vma->vm_pgoff > (~0UL >> PAGE_SHIFT))  return -EINVAL;
1689
1690         off = vma->vm_pgoff << PAGE_SHIFT;
1691
1692         start = (unsigned long) ivideo.video_base;
1693         len = PAGE_ALIGN((start & ~PAGE_MASK) + ivideo.video_size);
1694 #if 0
1695         if (off >= len) {
1696                 off -= len;
1697 #endif
1698         /* By Jake Page: Treat mmap request with offset beyond heapstart
1699          *               as request for mapping the mmio area 
1700          */
1701         mmio_off = PAGE_ALIGN((start & ~PAGE_MASK) + ivideo.heapstart);
1702         if(off >= mmio_off) {
1703                 off -= mmio_off;                
1704                 sisfb_get_var(&var, currcon, info);
1705                 if(var.accel_flags) return -EINVAL;
1706
1707                 start = (unsigned long) ivideo.mmio_base;
1708                 len = PAGE_ALIGN((start & ~PAGE_MASK) + sisfb_mmio_size);
1709         }
1710
1711         start &= PAGE_MASK;
1712         if((vma->vm_end - vma->vm_start + off) > len)   return -EINVAL;
1713
1714         off += start;
1715         vma->vm_pgoff = off >> PAGE_SHIFT;
1716         vma->vm_flags |= VM_IO;   /* by Jake Page; is that really needed? */
1717
1718 #if defined(__i386__) || defined(__x86_64__)
1719         if (boot_cpu_data.x86 > 3)
1720                 pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
1721 #endif
1722         /* RedHat requires vma as the first paramater to the following call */
1723         if (io_remap_page_range(vma->vm_start, off, vma->vm_end - vma->vm_start,
1724                                 vma->vm_page_prot))
1725                 return -EAGAIN;
1726
1727         return 0;
1728 }
1729
1730 static void sis_get_glyph(struct fb_info *info, SIS_GLYINFO *gly)
1731 {
1732         struct display *p = &fb_display[currcon];
1733         u16 c;
1734         u8 *cdat;
1735         int widthb;
1736         u8 *gbuf = gly->gmask;
1737         int size;
1738
1739         gly->fontheight = fontheight(p);
1740         gly->fontwidth = fontwidth(p);
1741         widthb = (fontwidth(p) + 7) / 8;
1742
1743         c = gly->ch & p->charmask;
1744         if (fontwidth(p) <= 8)
1745                 cdat = p->fontdata + c * fontheight(p);
1746         else
1747                 cdat = p->fontdata + (c * fontheight(p) << 1);
1748
1749         size = fontheight(p) * widthb;
1750         memcpy(gbuf, cdat, size);
1751         gly->ngmask = size;
1752 }
1753
1754 static int sisfb_update_var(int con, struct fb_info *info)
1755 {
1756         return(sisfb_pan_var(&fb_display[con].var));
1757 }
1758
1759 static int sisfb_switch(int con, struct fb_info *info)
1760 {
1761         int cols, rows;
1762
1763         if(fb_display[currcon].cmap.len)
1764                 fb_get_cmap(&fb_display[currcon].cmap, 1, sis_getcolreg, info);
1765
1766         fb_display[con].var.activate = FB_ACTIVATE_NOW;
1767
1768         if(!memcmp(&fb_display[con].var, &fb_display[currcon].var,
1769                                    sizeof(struct fb_var_screeninfo))) {
1770                 currcon = con;
1771                 return 1;
1772         }
1773
1774         currcon = con;
1775
1776         sisfb_do_set_var(&fb_display[con].var, 1, info);
1777
1778         sisfb_set_disp(con, &fb_display[con].var, info);
1779
1780         sisfb_do_install_cmap(con, info);
1781
1782         cols = sisbios_mode[sisfb_mode_idx].cols;
1783         rows = sisbios_mode[sisfb_mode_idx].rows;
1784         vc_resize_con(rows, cols, fb_display[con].conp->vc_num);
1785
1786         sisfb_update_var(con, info);
1787
1788         return 1;
1789 }
1790
1791 static void sisfb_blank(int blank, struct fb_info *info)
1792 {
1793         sisfb_myblank(blank);
1794 }
1795 #endif
1796
1797 /* ------------ FBDev related routines for 2.5 series ----------- */
1798
1799 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
1800
1801 static int sisfb_open(struct fb_info *info, int user)
1802 {
1803     return 0;
1804 }
1805
1806 static int sisfb_release(struct fb_info *info, int user)
1807 {
1808     return 0;
1809 }
1810
1811 static int sisfb_get_cmap_len(const struct fb_var_screeninfo *var)
1812 {
1813         int rc = 16;            
1814
1815         switch(var->bits_per_pixel) {
1816         case 8:
1817                 rc = 256;       
1818                 break;
1819         case 16:
1820         case 32:
1821                 rc = 16;
1822                 break;
1823         }
1824         return rc;
1825 }
1826
1827 static int sisfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue,
1828                            unsigned transp, struct fb_info *info)
1829 {
1830         if (regno >= sisfb_get_cmap_len(&info->var))
1831                 return 1;
1832
1833         switch (info->var.bits_per_pixel) {
1834         case 8:
1835                 outSISREG(SISDACA, regno);
1836                 outSISREG(SISDACD, (red >> 10));
1837                 outSISREG(SISDACD, (green >> 10));
1838                 outSISREG(SISDACD, (blue >> 10));
1839                 if (ivideo.currentvbflags & VB_DISPTYPE_DISP2) {
1840                         outSISREG(SISDAC2A, regno);
1841                         outSISREG(SISDAC2D, (red >> 8));
1842                         outSISREG(SISDAC2D, (green >> 8));
1843                         outSISREG(SISDAC2D, (blue >> 8));
1844                 }
1845                 break;
1846         case 16:
1847                 ((u32 *)(info->pseudo_palette))[regno] =
1848                     ((red & 0xf800)) | ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11);
1849                 break;
1850         case 32:
1851                 red >>= 8;
1852                 green >>= 8;
1853                 blue >>= 8;
1854                 ((u32 *) (info->pseudo_palette))[regno] =
1855                         (red << 16) | (green << 8) | (blue);
1856                 break;
1857         }
1858         return 0;
1859 }
1860
1861 static int sisfb_set_par(struct fb_info *info)
1862 {
1863         int err;
1864
1865         if((err = sisfb_do_set_var(&info->var, 1, info)))
1866                 return err;
1867
1868         sisfb_get_fix(&info->fix, info->currcon, info);
1869
1870         return 0;
1871 }
1872
1873 static int sisfb_check_var(struct fb_var_screeninfo *var,
1874                            struct fb_info *info)
1875 {
1876         unsigned int htotal = 0, vtotal = 0, myrateindex = 0;
1877         unsigned int drate = 0, hrate = 0, maxyres;
1878         int found_mode = 0;
1879         int refresh_rate, search_idx;
1880         BOOLEAN recalc_clock = FALSE;
1881         u32 pixclock;
1882
1883         htotal = var->left_margin + var->xres + var->right_margin + var->hsync_len;
1884
1885         vtotal = var->upper_margin + var->lower_margin + var->vsync_len;
1886
1887         pixclock = var->pixclock;
1888
1889         if((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) {
1890                 vtotal += var->yres;
1891                 vtotal <<= 1;
1892         } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
1893                 vtotal += var->yres;
1894                 vtotal <<= 2;
1895         } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
1896                 vtotal += var->yres;
1897                 vtotal <<= 1;
1898         } else  vtotal += var->yres;
1899
1900         if(!(htotal) || !(vtotal)) {
1901                 SISFAIL("sisfb: no valid timing data");
1902         }
1903
1904         search_idx = 0;
1905         while( (sisbios_mode[search_idx].mode_no != 0) &&
1906                (sisbios_mode[search_idx].xres <= var->xres) ) {
1907                 if( (sisbios_mode[search_idx].xres == var->xres) &&
1908                     (sisbios_mode[search_idx].yres == var->yres) &&
1909                     (sisbios_mode[search_idx].bpp == var->bits_per_pixel)) {
1910                         if(sisfb_validate_mode(search_idx, ivideo.currentvbflags) > 0) {
1911                            found_mode = 1;
1912                            break;
1913                         }
1914                 }
1915                 search_idx++;
1916         }
1917
1918         if(!found_mode) {
1919
1920                 search_idx = 0;
1921                 while(sisbios_mode[search_idx].mode_no != 0) {
1922                    if( (var->xres <= sisbios_mode[search_idx].xres) &&
1923                        (var->yres <= sisbios_mode[search_idx].yres) &&
1924                        (var->bits_per_pixel == sisbios_mode[search_idx].bpp) ) {
1925                           if(sisfb_validate_mode(search_idx, ivideo.currentvbflags) > 0) {
1926                              found_mode = 1;
1927                              break;
1928                           }
1929                    }
1930                    search_idx++;
1931                 }
1932                 if(found_mode) {
1933                         printk(KERN_DEBUG "sisfb: Adapted from %dx%dx%d to %dx%dx%d\n",
1934                                 var->xres, var->yres, var->bits_per_pixel,
1935                                 sisbios_mode[search_idx].xres,
1936                                 sisbios_mode[search_idx].yres,
1937                                 var->bits_per_pixel);
1938                         var->xres = sisbios_mode[search_idx].xres;
1939                         var->yres = sisbios_mode[search_idx].yres;
1940
1941
1942                 } else {
1943                         printk(KERN_ERR "sisfb: Failed to find supported mode near %dx%dx%d\n",
1944                                 var->xres, var->yres, var->bits_per_pixel);
1945                         return -EINVAL;
1946                 }
1947         }
1948
1949         if( ((ivideo.vbflags & VB_LVDS) ||                      /* Slave modes on LVDS and 301B-DH */
1950              ((ivideo.vbflags & VB_30xBDH) && (ivideo.currentvbflags & CRT2_LCD))) &&
1951             (var->bits_per_pixel == 8) ) {
1952                 refresh_rate = 60;
1953                 recalc_clock = TRUE;
1954         } else if( (ivideo.current_htotal == htotal) &&         /* x=x & y=y & c=c -> assume depth change */
1955                    (ivideo.current_vtotal == vtotal) &&
1956                    (ivideo.current_pixclock == pixclock) ) {
1957                 drate = 1000000000 / pixclock;
1958                 hrate = (drate * 1000) / htotal;
1959                 refresh_rate = (unsigned int) (hrate * 2 / vtotal);
1960         } else if( ( (ivideo.current_htotal != htotal) ||       /* x!=x | y!=y & c=c -> invalid pixclock */
1961                      (ivideo.current_vtotal != vtotal) ) &&
1962                    (ivideo.current_pixclock == var->pixclock) ) {
1963                 if(sisfb_lastrates[sisbios_mode[search_idx].mode_no]) {
1964                         refresh_rate = sisfb_lastrates[sisbios_mode[search_idx].mode_no];
1965                 } else if(sisfb_parm_rate != -1) {
1966                         refresh_rate = sisfb_parm_rate;
1967                 } else {
1968                         refresh_rate = 60;
1969                 }
1970                 recalc_clock = TRUE;
1971         } else if((pixclock) && (htotal) && (vtotal)) {
1972                 drate = 1000000000 / pixclock;
1973                 hrate = (drate * 1000) / htotal;
1974                 refresh_rate = (unsigned int) (hrate * 2 / vtotal);
1975         } else if(ivideo.current_refresh_rate) {
1976                 refresh_rate = ivideo.current_refresh_rate;
1977                 recalc_clock = TRUE;
1978         } else {
1979                 refresh_rate = 60;
1980                 recalc_clock = TRUE;
1981         }
1982
1983         myrateindex = sisfb_search_refresh_rate(refresh_rate, search_idx);
1984
1985         /* Eventually recalculate timing and clock */
1986         if(recalc_clock) {
1987            if(!myrateindex) myrateindex = sisbios_mode[search_idx].rate_idx;
1988            var->pixclock = (u32) (1000000000 / sisfb_mode_rate_to_dclock(&SiS_Pr, &sishw_ext,
1989                                                 sisbios_mode[search_idx].mode_no, myrateindex));
1990            sisfb_mode_rate_to_ddata(&SiS_Pr, &sishw_ext,
1991                                         sisbios_mode[search_idx].mode_no, myrateindex,
1992                                         &var->left_margin, &var->right_margin,
1993                                         &var->upper_margin, &var->lower_margin,
1994                                         &var->hsync_len, &var->vsync_len,
1995                                         &var->sync, &var->vmode);
1996            if((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
1997                 var->pixclock <<= 1;
1998            }
1999         }
2000
2001         if(sisfb_thismonitor.datavalid) {
2002            if(!sisfb_verify_rate(&sisfb_thismonitor, search_idx,
2003                                  myrateindex, refresh_rate)) {
2004               printk(KERN_INFO "sisfb: WARNING: Refresh rate exceeds monitor specs!\n");
2005            }
2006         }
2007
2008         /* Adapt RGB settings */
2009         sisfb_bpp_to_var(var);  
2010         
2011         /* Sanity check for offsets */
2012         if (var->xoffset < 0)
2013                 var->xoffset = 0;
2014         if (var->yoffset < 0)
2015                 var->yoffset = 0;
2016
2017         /* Horiz-panning not supported */
2018         if(var->xres != var->xres_virtual)
2019                 var->xres_virtual = var->xres;
2020
2021         if(sisfb_ypan) {
2022            maxyres = ivideo.heapstart / (var->xres * (var->bits_per_pixel >> 3));
2023            if(maxyres > 32767) maxyres = 32767;
2024            if(sisfb_max) {
2025               var->yres_virtual = maxyres;
2026            } else {
2027               if(var->yres_virtual > maxyres) {
2028                  var->yres_virtual = maxyres;
2029               }
2030            }
2031            if(var->yres_virtual <= var->yres) {
2032               var->yres_virtual = var->yres;
2033            }
2034         } else {
2035            if(var->yres != var->yres_virtual) {
2036               var->yres_virtual = var->yres;
2037            }
2038            var->xoffset = 0;
2039            var->yoffset = 0;
2040         }
2041         
2042         /* Truncate offsets to maximum if too high */
2043         if(var->xoffset > var->xres_virtual - var->xres)
2044                 var->xoffset = var->xres_virtual - var->xres - 1;
2045
2046         if(var->yoffset > var->yres_virtual - var->yres)
2047                 var->yoffset = var->yres_virtual - var->yres - 1;
2048         
2049         /* Set everything else to 0 */
2050         var->red.msb_right = 
2051             var->green.msb_right =
2052             var->blue.msb_right =
2053             var->transp.offset = var->transp.length = var->transp.msb_right = 0;                
2054
2055         return 0;
2056 }
2057
2058 static int sisfb_pan_display(struct fb_var_screeninfo *var,
2059                              struct fb_info* info)
2060 {
2061         int err;
2062
2063         if (var->xoffset > (var->xres_virtual - var->xres))
2064                 return -EINVAL;
2065         if (var->yoffset > (var->yres_virtual - var->yres))
2066                 return -EINVAL;
2067
2068         if (var->vmode & FB_VMODE_YWRAP) {
2069                 if (var->yoffset < 0 ||
2070                     var->yoffset >= info->var.yres_virtual ||
2071                     var->xoffset)
2072                         return -EINVAL;
2073         } else {
2074                 if (var->xoffset + info->var.xres > info->var.xres_virtual ||
2075                     var->yoffset + info->var.yres > info->var.yres_virtual)
2076                         return -EINVAL;
2077         }
2078     
2079         if((err = sisfb_pan_var(var)) < 0) return err;
2080
2081         info->var.xoffset = var->xoffset;
2082         info->var.yoffset = var->yoffset;
2083         if (var->vmode & FB_VMODE_YWRAP)
2084                 info->var.vmode |= FB_VMODE_YWRAP;
2085         else
2086                 info->var.vmode &= ~FB_VMODE_YWRAP;
2087
2088         return 0;
2089 }
2090
2091 static int sisfb_mmap(struct fb_info *info, struct file *file,
2092                       struct vm_area_struct *vma)
2093 {
2094         unsigned long start;
2095         unsigned long off;
2096         u32 len, mmio_off;
2097
2098         if(vma->vm_pgoff > (~0UL >> PAGE_SHIFT))  return -EINVAL;
2099
2100         off = vma->vm_pgoff << PAGE_SHIFT;
2101
2102         start = (unsigned long) ivideo.video_base;
2103         len = PAGE_ALIGN((start & ~PAGE_MASK) + ivideo.video_size);
2104 #if 0
2105         if (off >= len) {
2106                 off -= len;
2107 #endif
2108         /* By Jake Page: Treat mmap request with offset beyond heapstart
2109          *               as request for mapping the mmio area 
2110          */
2111         mmio_off = PAGE_ALIGN((start & ~PAGE_MASK) + ivideo.heapstart);
2112         if(off >= mmio_off) {
2113                 off -= mmio_off;                
2114                 if(info->var.accel_flags) return -EINVAL;
2115
2116                 start = (unsigned long) ivideo.mmio_base;
2117                 len = PAGE_ALIGN((start & ~PAGE_MASK) + sisfb_mmio_size);
2118         }
2119
2120         start &= PAGE_MASK;
2121         if((vma->vm_end - vma->vm_start + off) > len)   return -EINVAL;
2122
2123         off += start;
2124         vma->vm_pgoff = off >> PAGE_SHIFT;
2125         vma->vm_flags |= VM_IO;   /* by Jake Page; is that really needed? */
2126
2127 #if defined(__i386__) || defined(__x86_64__)
2128         if (boot_cpu_data.x86 > 3)
2129                 pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
2130 #endif
2131         if (io_remap_page_range(vma, vma->vm_start, off, vma->vm_end - vma->vm_start,
2132                                 vma->vm_page_prot))
2133                 return -EAGAIN;
2134
2135         return 0;
2136 }
2137
2138 static int sisfb_blank(int blank, struct fb_info *info)
2139 {
2140         return(sisfb_myblank(blank));
2141 }
2142
2143 #endif
2144
2145 /* ----------- FBDev related routines for all series ---------- */
2146
2147
2148 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
2149 static int sisfb_ioctl(struct inode *inode, struct file *file,
2150                        unsigned int cmd, unsigned long arg,
2151                        struct fb_info *info)
2152 #else
2153 static int sisfb_ioctl(struct inode *inode, struct file *file,
2154                        unsigned int cmd, unsigned long arg, int con,
2155                        struct fb_info *info)
2156 #endif
2157 {
2158         struct sis_memreq sismemreq;
2159         struct ap_data sisapdata;
2160         unsigned long sismembase = 0;
2161 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
2162         SIS_GLYINFO sisglyinfo;
2163 #endif
2164
2165         switch (cmd) {
2166            case FBIO_ALLOC:
2167                 if(!capable(CAP_SYS_RAWIO))
2168                         return -EPERM;
2169                 if(copy_from_user(&sismemreq, (void *)arg, sizeof(sismemreq)))
2170                         return -EFAULT;
2171                 sis_malloc(&sismemreq);
2172                 if(copy_to_user((void *)arg, &sismemreq, sizeof(sismemreq))) {
2173                         sis_free(sismemreq.offset);
2174                         return -EFAULT;
2175                 }
2176                 break;
2177            case FBIO_FREE:
2178                 if(!capable(CAP_SYS_RAWIO))
2179                         return -EPERM;
2180                 if(get_user(sismembase, (unsigned long *) arg))
2181                         return -EFAULT;
2182                 sis_free(sismembase);
2183                 break;
2184 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
2185            case FBIOGET_GLYPH:
2186                 if(copy_from_user(&sisglyinfo, (void *)arg, sizeof(sisglyinfo)))
2187                         return -EFAULT;
2188                 sis_get_glyph(info, &sisglyinfo);
2189                 break;
2190            case FBIOPUT_MODEINFO:
2191                 {
2192                         struct mode_info x;
2193
2194                         if(copy_from_user(&x, (void *)arg, sizeof(x)))
2195                                 return -EFAULT;
2196
2197                         ivideo.video_bpp        = x.bpp;
2198                         ivideo.video_width      = x.xres;
2199                         ivideo.video_height     = x.yres;
2200                         ivideo.video_vwidth     = x.v_xres;
2201                         ivideo.video_vheight    = x.v_yres;
2202                         ivideo.org_x            = x.org_x;
2203                         ivideo.org_y            = x.org_y;
2204                         ivideo.refresh_rate     = x.vrate;
2205                         ivideo.video_linelength = ivideo.video_vwidth * (ivideo.video_bpp >> 3);
2206                         sisfb_set_vparms();
2207                         break;
2208                 }
2209 #endif
2210            case FBIOGET_HWCINFO:
2211                 {
2212                         unsigned long myhwcoffset = 0;
2213
2214                         if(sisfb_caps & HW_CURSOR_CAP)
2215                                 myhwcoffset = sisfb_hwcursor_vbase -
2216                                     (unsigned long) ivideo.video_vbase;
2217
2218                         return put_user(myhwcoffset, (unsigned long *)arg);
2219
2220                         break;
2221                 }
2222            case FBIOGET_DISPINFO:
2223                 sis_dispinfo(&sisapdata);
2224                 if(copy_to_user((void *)arg, &sisapdata, sizeof(sisapdata)))
2225                         return -EFAULT;
2226                 break;
2227            case SISFB_GET_INFO:  /* For communication with X driver */
2228                 {
2229                         sisfb_info x;
2230
2231                         x.sisfb_id = SISFB_ID;
2232                         x.sisfb_version = VER_MAJOR;
2233                         x.sisfb_revision = VER_MINOR;
2234                         x.sisfb_patchlevel = VER_LEVEL;
2235                         x.chip_id = ivideo.chip_id;
2236                         x.memory = ivideo.video_size / 1024;
2237                         x.heapstart = ivideo.heapstart / 1024;
2238                         x.fbvidmode = sisfb_mode_no;
2239                         x.sisfb_caps = sisfb_caps;
2240                         x.sisfb_tqlen = 512; /* yet fixed */
2241                         x.sisfb_pcibus = ivideo.pcibus;
2242                         x.sisfb_pcislot = ivideo.pcislot;
2243                         x.sisfb_pcifunc = ivideo.pcifunc;
2244                         x.sisfb_lcdpdc = sisfb_detectedpdc;
2245                         x.sisfb_lcda = sisfb_detectedlcda;
2246                         x.sisfb_vbflags = ivideo.vbflags;
2247                         x.sisfb_currentvbflags = ivideo.currentvbflags;
2248                         x.sisfb_scalelcd = SiS_Pr.UsePanelScaler;
2249                         x.sisfb_specialtiming = SiS_Pr.SiS_CustomT;
2250                         x.sisfb_haveemi = SiS_Pr.HaveEMI ? 1 : 0;
2251                         x.sisfb_haveemilcd = SiS_Pr.HaveEMILCD ? 1 : 0;
2252                         x.sisfb_emi30 = SiS_Pr.EMI_30;
2253                         x.sisfb_emi31 = SiS_Pr.EMI_31;
2254                         x.sisfb_emi32 = SiS_Pr.EMI_32;
2255                         x.sisfb_emi33 = SiS_Pr.EMI_33;
2256                         if(copy_to_user((void *)arg, &x, sizeof(x)))
2257                                 return -EFAULT;
2258                         break;
2259                 }
2260            case SISFB_GET_VBRSTATUS:
2261                 {
2262                         if(sisfb_CheckVBRetrace())
2263                                 return put_user(1UL, (unsigned long *) arg);
2264                         else
2265                                 return put_user(0UL, (unsigned long *) arg);
2266                         break;
2267                 }
2268            case SISFB_GET_AUTOMAXIMIZE:
2269                 {
2270                         if(sisfb_max)
2271                                 return put_user(1UL, (unsigned long *) arg);
2272                         else
2273                                 return put_user(0UL, (unsigned long *) arg);
2274                         break;
2275                 }
2276            case SISFB_SET_AUTOMAXIMIZE:
2277                 {
2278                         unsigned long newmax;
2279
2280                         if(copy_from_user(&newmax, (unsigned long *)arg, sizeof(newmax)))
2281                                 return -EFAULT;
2282
2283                         if(newmax) sisfb_max = 1;
2284                         else       sisfb_max = 0;
2285                         break;
2286                 }
2287            default:
2288                 return -EINVAL;
2289         }
2290         return 0;
2291 }
2292
2293
2294 static int sisfb_get_fix(struct fb_fix_screeninfo *fix, int con,
2295                          struct fb_info *info)
2296 {
2297         memset(fix, 0, sizeof(struct fb_fix_screeninfo));
2298
2299 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)  
2300         strcpy(fix->id, sis_fb_info->modename);
2301 #else
2302         strcpy(fix->id, myid);
2303 #endif  
2304
2305         fix->smem_start = ivideo.video_base;
2306
2307         if((!sisfb_mem) || (sisfb_mem > (ivideo.video_size/1024))) {
2308             if(sisvga_engine == SIS_300_VGA) {
2309                if(ivideo.video_size > 0x1000000) {
2310                         fix->smem_len = 0xc00000;
2311                } else if(ivideo.video_size > 0x800000)
2312                         fix->smem_len = 0x800000;
2313                else
2314                         fix->smem_len = 0x400000;
2315             } else {
2316                 fix->smem_len = ivideo.video_size - 0x100000;
2317             }
2318         } else
2319                 fix->smem_len = sisfb_mem * 1024;
2320
2321         fix->type        = FB_TYPE_PACKED_PIXELS;
2322         fix->type_aux    = 0;
2323         if(ivideo.video_bpp == 8)
2324            fix->visual = FB_VISUAL_PSEUDOCOLOR;
2325         else
2326            fix->visual = FB_VISUAL_TRUECOLOR;
2327         fix->xpanstep    = 0;
2328
2329         if(sisfb_ypan)   fix->ypanstep = 1;
2330
2331         fix->ywrapstep   = 0;
2332         fix->line_length = ivideo.video_linelength;
2333         fix->mmio_start  = ivideo.mmio_base;
2334         fix->mmio_len    = sisfb_mmio_size;
2335         if(sisvga_engine == SIS_300_VGA) 
2336            fix->accel    = FB_ACCEL_SIS_GLAMOUR;
2337         else if((ivideo.chip == SIS_330) || (ivideo.chip == SIS_760))
2338            fix->accel    = FB_ACCEL_SIS_XABRE;
2339         else
2340            fix->accel    = FB_ACCEL_SIS_GLAMOUR_2;
2341
2342 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
2343         fix->reserved[0] = ivideo.video_size & 0xFFFF;
2344         fix->reserved[1] = (ivideo.video_size >> 16) & 0xFFFF;
2345         fix->reserved[2] = sisfb_caps;
2346 #endif
2347
2348         return 0;
2349 }
2350
2351 /* ----------------  fb_ops structures ----------------- */
2352
2353 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
2354 static struct fb_ops sisfb_ops = {
2355         .owner          = THIS_MODULE,
2356         .fb_get_fix     = sisfb_get_fix,
2357         .fb_get_var     = sisfb_get_var,
2358         .fb_set_var     = sisfb_set_var,
2359         .fb_get_cmap    = sisfb_get_cmap,
2360         .fb_set_cmap    = sisfb_set_cmap,
2361         .fb_pan_display = sisfb_pan_display,
2362         .fb_ioctl       = sisfb_ioctl,
2363         .fb_mmap        = sisfb_mmap,
2364 };
2365 #endif
2366
2367
2368 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
2369 static struct fb_ops sisfb_ops = {
2370         .owner        = THIS_MODULE,
2371         .fb_open      = sisfb_open,
2372         .fb_release   = sisfb_release,
2373         .fb_check_var = sisfb_check_var,
2374         .fb_set_par   = sisfb_set_par,
2375         .fb_setcolreg = sisfb_setcolreg,
2376         .fb_pan_display = sisfb_pan_display,
2377         .fb_blank     = sisfb_blank,
2378         .fb_fillrect  = fbcon_sis_fillrect,
2379         .fb_copyarea  = fbcon_sis_copyarea,
2380         .fb_imageblit = cfb_imageblit,
2381         .fb_cursor    = soft_cursor,    
2382         .fb_sync      = fbcon_sis_sync,
2383         .fb_ioctl     = sisfb_ioctl,
2384         .fb_mmap      = sisfb_mmap,
2385 };
2386 #endif
2387
2388
2389 /* ---------------- Chip generation dependent routines ---------------- */
2390
2391 #ifdef CONFIG_FB_SIS_300 /* for SiS 300/630/540/730 */
2392
2393 static int sisfb_get_dram_size_300(void)
2394 {
2395         struct pci_dev *pdev = NULL;
2396         int pdev_valid = 0;
2397         u8  pci_data, reg;
2398         u16 nbridge_id;
2399
2400         switch (ivideo.chip) {
2401            case SIS_540:
2402                 nbridge_id = PCI_DEVICE_ID_SI_540;
2403                 break;
2404            case SIS_630:
2405                 nbridge_id = PCI_DEVICE_ID_SI_630;
2406                 break;
2407            case SIS_730:
2408                 nbridge_id = PCI_DEVICE_ID_SI_730;
2409                 break;
2410            default:
2411                 nbridge_id = 0;
2412                 break;
2413         }
2414
2415         if (nbridge_id == 0) {  /* 300 */
2416
2417                 inSISIDXREG(SISSR, IND_SIS_DRAM_SIZE,reg);
2418                 ivideo.video_size =
2419                         ((unsigned int) ((reg & SIS_DRAM_SIZE_MASK) + 1) << 20);
2420
2421         } else {                /* 540, 630, 730 */
2422
2423 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,74)
2424                 pci_for_each_dev(pdev) {
2425 #else
2426                 while((pdev = pci_find_device(PCI_VENDOR_ID_SI, PCI_ANY_ID, pdev))) {
2427 #endif
2428                         if ((pdev->vendor == PCI_VENDOR_ID_SI)
2429                                        && (pdev->device == nbridge_id)) {
2430                                 pci_read_config_byte(pdev, IND_BRI_DRAM_STATUS, &pci_data);
2431                                 pci_data = (pci_data & BRI_DRAM_SIZE_MASK) >> 4;
2432                                 ivideo.video_size = (unsigned int)(1 << (pci_data+21));
2433                                 pdev_valid = 1;
2434
2435                                 reg = SIS_DATA_BUS_64 << 6;
2436                                 switch (pci_data) {
2437                                    case BRI_DRAM_SIZE_2MB:
2438                                         reg |= SIS_DRAM_SIZE_2MB;
2439                                         break;
2440                                    case BRI_DRAM_SIZE_4MB:
2441                                         reg |= SIS_DRAM_SIZE_4MB;
2442                                         break;
2443                                    case BRI_DRAM_SIZE_8MB:
2444                                         reg |= SIS_DRAM_SIZE_8MB;
2445                                         break;
2446                                    case BRI_DRAM_SIZE_16MB:
2447                                         reg |= SIS_DRAM_SIZE_16MB;
2448                                         break;
2449                                    case BRI_DRAM_SIZE_32MB:
2450                                         reg |= SIS_DRAM_SIZE_32MB;
2451                                         break;
2452                                    case BRI_DRAM_SIZE_64MB:
2453                                         reg |= SIS_DRAM_SIZE_64MB;
2454                                         break;
2455                                 }
2456                                 outSISIDXREG(SISSR, IND_SIS_DRAM_SIZE, reg);
2457                                 break;
2458                         }  
2459                 }
2460         
2461                 if (!pdev_valid)  return -1;
2462         }
2463         return 0;
2464 }
2465
2466 #endif  /* CONFIG_FB_SIS_300 */
2467
2468
2469 #ifdef CONFIG_FB_SIS_315    /* for SiS 315/550/650/740/330/661/741/760 */
2470
2471 static int sisfb_get_dram_size_315(void)
2472 {
2473         u8  reg = 0;
2474
2475         if(ivideo.chip == SIS_550 ||
2476            ivideo.chip == SIS_650 ||
2477            ivideo.chip == SIS_740) {
2478
2479                 inSISIDXREG(SISSR, IND_SIS_DRAM_SIZE, reg);
2480                 reg &= 0x3f;
2481                 reg++;
2482                 reg <<= 2;
2483                 ivideo.video_size = reg << 20;
2484                 return 0;
2485
2486         } else if(ivideo.chip == SIS_661 ||
2487                   ivideo.chip == SIS_741 ||
2488                   ivideo.chip == SIS_660 ||
2489                   ivideo.chip == SIS_760) {
2490
2491                 inSISIDXREG(SISCR, 0x79, reg);
2492                 reg &= 0xf0;
2493                 reg >>= 4;
2494                 ivideo.video_size = (1 << reg) << 20;
2495                 return 0;
2496
2497         } else {        /* 315, 330 */
2498
2499                 inSISIDXREG(SISSR, IND_SIS_DRAM_SIZE, reg);
2500                 ivideo.video_size = (1 << ((reg & 0xf0) >> 4)) << 20;
2501
2502                 reg &= SIS315_DUAL_CHANNEL_MASK;
2503                 reg >>= 2;
2504
2505                 if(ivideo.chip == SIS_330) {
2506
2507                    if(reg) ivideo.video_size <<= 1;
2508                 
2509                 } else {
2510                    
2511                    switch (reg) {
2512                       case SIS315_SINGLE_CHANNEL_2_RANK:
2513                            ivideo.video_size <<= 1;
2514                            break;
2515                       case SIS315_DUAL_CHANNEL_1_RANK:
2516                            ivideo.video_size <<= 1;
2517                            break;
2518                       case SIS315_ASYM_DDR:             /* TW: DDR asymetric */
2519                            ivideo.video_size += (ivideo.video_size/2);
2520                            break;
2521                    }
2522                 }
2523
2524                 return 0;
2525         }
2526         
2527         return -1;
2528         
2529 }
2530
2531 #endif   /* CONFIG_FB_SIS_315 */
2532
2533
2534 /* -------------- video bridge detection --------------- */
2535
2536 static void sisfb_detect_VB_connect()
2537 {
2538         u8 sr16, sr17, cr32, temp;
2539
2540         if(sisvga_engine == SIS_300_VGA) {
2541
2542                 inSISIDXREG(SISSR, IND_SIS_SCRATCH_REG_17, sr17);
2543
2544                 if ((sr17 & 0x0F) && (ivideo.chip != SIS_300)) {
2545
2546                         /* Old BIOSes store the detected CRT2 type in SR17
2547                          * instead of CR32. However, since our detection
2548                          * routines store their results to CR32, we now copy
2549                          * the remaining bits (for LCD and VGA) to CR32 for
2550                          * unified usage.
2551                          * SR17[0] CRT1    [1] LCD     [2] TV    [3] VGA2
2552                          *     [4] AVIDEO  [5] SVIDEO
2553                          */
2554
2555 #if 0
2556                         if (sr17 & 0x01) orSISIDXREG(SISCR, 0x32, SIS_CRT1);
2557                         else             andSISIDXREG(SISCR, 0x32, ~SIS_CRT1);
2558
2559                         if (sr17 & 0x02) orSISIDXREG(SISCR, 0x32, SIS_VB_LCD);
2560                         else             andSISIDXREG(SISCR, 0x32, ~SIS_VB_LCD);
2561
2562                         /* no HiVision and no DVI connector here */
2563                         andSISIDXREG(SISCR, 0x32, ~0xc0);
2564 #endif
2565
2566                         /* PAL/NTSC is stored on SR16 on such machines */
2567                         if (!(ivideo.vbflags & (TV_PAL | TV_NTSC))) {
2568                                 inSISIDXREG(SISSR, IND_SIS_SCRATCH_REG_16, sr16);
2569                                 if (sr16 & 0x20)
2570                                         ivideo.vbflags |= TV_PAL;
2571                                 else
2572                                         ivideo.vbflags |= TV_NTSC;
2573                         }
2574
2575                 }
2576
2577         }
2578
2579         inSISIDXREG(SISCR, IND_SIS_SCRATCH_REG_CR32, cr32);
2580
2581         if (cr32 & SIS_CRT1)
2582                 sisfb_crt1off = 0;
2583         else {
2584                 if (cr32 & 0x5F)
2585                         sisfb_crt1off = 1;
2586                 else
2587                         sisfb_crt1off = 0;
2588         }
2589
2590         ivideo.vbflags &= ~(CRT2_TV | CRT2_LCD | CRT2_VGA);
2591
2592         if (cr32 & SIS_VB_TV)
2593                 ivideo.vbflags |= CRT2_TV;
2594         if (cr32 & SIS_VB_LCD)
2595                 ivideo.vbflags |= CRT2_LCD;
2596         if (cr32 & SIS_VB_CRT2)
2597                 ivideo.vbflags |= CRT2_VGA;
2598
2599         /* TW: Detect/set TV plug & type */
2600         if(sisfb_tvplug != -1)
2601                 ivideo.vbflags |= sisfb_tvplug;
2602
2603         if (cr32 & SIS_VB_SVIDEO)
2604                 ivideo.vbflags |= TV_SVIDEO;
2605         else if (cr32 & SIS_VB_COMPOSITE)
2606                 ivideo.vbflags |= TV_AVIDEO;
2607         else if (cr32 & SIS_VB_SCART)
2608                 ivideo.vbflags |= TV_SCART;
2609
2610         if (!(ivideo.vbflags & (TV_PAL | TV_NTSC))) {
2611                 if(sisvga_engine == SIS_300_VGA) {
2612                         inSISIDXREG(SISSR, IND_SIS_POWER_ON_TRAP, temp);
2613                         if (temp & 0x01)
2614                                 ivideo.vbflags |= TV_PAL;
2615                         else
2616                                 ivideo.vbflags |= TV_NTSC;
2617                 } else if((ivideo.chip <= SIS_315PRO) || (ivideo.chip >= SIS_330)) {
2618
2619                         inSISIDXREG(SISSR, 0x38, temp);
2620                         if(temp & 0x01)
2621                                 ivideo.vbflags |= TV_PAL;
2622                         else
2623                                 ivideo.vbflags |= TV_NTSC;
2624
2625                 } else {
2626
2627                         inSISIDXREG(SISCR, 0x79, temp);
2628                         if(temp & 0x20)
2629                                 ivideo.vbflags |= TV_PAL;
2630                         else
2631                                 ivideo.vbflags |= TV_NTSC;
2632                 }
2633         }
2634
2635         /* TW: Copy forceCRT1 option to CRT1off if option is given */
2636         if (sisfb_forcecrt1 != -1) {
2637                 if(sisfb_forcecrt1) sisfb_crt1off = 0;
2638                 else                sisfb_crt1off = 1;
2639         }
2640
2641 }
2642
2643 static void sisfb_get_VB_type(void)
2644 {
2645         u8 vb_chipid;
2646         u8 reg;
2647         char stdstr[]    = "sisfb: Detected";
2648         char bridgestr[] = "video bridge";
2649         char lvdsstr[]   = "LVDS transmitter";
2650         char chrstr[]    = "Chrontel TV encoder";
2651
2652         ivideo.hasVB = HASVB_NONE;
2653         sishw_ext.ujVBChipID = VB_CHIP_UNKNOWN;
2654         sishw_ext.Is301BDH = FALSE;
2655         sishw_ext.usExternalChip = 0;
2656
2657         inSISIDXREG(SISPART4, 0x00, vb_chipid);
2658         switch (vb_chipid) {
2659            case 0x01:
2660                 ivideo.hasVB = HASVB_301;
2661                 inSISIDXREG(SISPART4, 0x01, reg);
2662                 if(reg < 0xb0) {
2663                         ivideo.vbflags |= VB_301;
2664                         sishw_ext.ujVBChipID = VB_CHIP_301;
2665                         printk(KERN_INFO "%s SiS301 %s\n", stdstr, bridgestr);
2666                 } else if(reg < 0xc0) {
2667                         ivideo.vbflags |= VB_301B;
2668                         sishw_ext.ujVBChipID = VB_CHIP_301B;
2669                         inSISIDXREG(SISPART4,0x23,reg);
2670                         if(!(reg & 0x02)) {
2671                            sishw_ext.Is301BDH = TRUE;
2672                            ivideo.vbflags |= VB_30xBDH;
2673                            printk(KERN_INFO "%s SiS301B-DH %s\n", stdstr, bridgestr);
2674                         } else {
2675                            printk(KERN_INFO "%s SiS301B %s\n", stdstr, bridgestr);
2676                         }
2677                 } else if(reg < 0xd0) {
2678                         ivideo.vbflags |= VB_301C;
2679                         sishw_ext.ujVBChipID = VB_CHIP_301C;
2680                         printk(KERN_INFO "%s SiS301C %s\n", stdstr, bridgestr);
2681                 } else if(reg < 0xe0) {
2682                         ivideo.vbflags |= VB_301LV;
2683                         sishw_ext.ujVBChipID = VB_CHIP_301LV;
2684                         printk(KERN_INFO "%s SiS301LV %s\n", stdstr, bridgestr);
2685                 } else if(reg <= 0xe1) {
2686                         inSISIDXREG(SISPART4,0x39,reg);
2687                         if(reg == 0xff) {
2688                            ivideo.vbflags |= VB_302LV;
2689                            sishw_ext.ujVBChipID = VB_CHIP_302LV;
2690                            printk(KERN_INFO "%s SiS302LV %s\n", stdstr, bridgestr);
2691                         } else {
2692                            ivideo.vbflags |= VB_302ELV;
2693                            sishw_ext.ujVBChipID = VB_CHIP_302ELV;
2694                            printk(KERN_INFO "%s SiS302ELV %s\n", stdstr, bridgestr);
2695                         }
2696                 }
2697                 break;
2698            case 0x02:
2699                 ivideo.hasVB = HASVB_302;
2700                 inSISIDXREG(SISPART4, 0x01, reg);
2701                 if(reg < 0xd0) {
2702                         ivideo.vbflags |= VB_302B;
2703                         sishw_ext.ujVBChipID = VB_CHIP_302B;
2704                         inSISIDXREG(SISPART4,0x23,reg);
2705                         if(!(reg & 0x02)) {
2706                            sishw_ext.Is301BDH = TRUE;
2707                            ivideo.vbflags |= VB_30xBDH;
2708                            printk(KERN_INFO "%s SiS302B-DH %s\n", stdstr, bridgestr);
2709                         } else {
2710                            printk(KERN_INFO "%s SiS302B %s\n", stdstr, bridgestr);
2711                         }
2712                 } else if(reg < 0xe0) {
2713                         ivideo.vbflags |= VB_301LV;
2714                         sishw_ext.ujVBChipID = VB_CHIP_301LV;
2715                         printk(KERN_INFO "%s SiS301LV %s\n", stdstr, bridgestr);
2716                 } else if(reg <= 0xe1) {
2717                         ivideo.vbflags |= VB_302LV;
2718                         sishw_ext.ujVBChipID = VB_CHIP_302LV;
2719                         printk(KERN_INFO "%s SiS302LV %s\n", stdstr, bridgestr);
2720                 }
2721                 break;
2722         }
2723
2724         if((!(ivideo.vbflags & VB_VIDEOBRIDGE)) && (ivideo.chip != SIS_300)) {
2725                 inSISIDXREG(SISCR, IND_SIS_SCRATCH_REG_CR37, reg);
2726                 reg &= SIS_EXTERNAL_CHIP_MASK;
2727                 reg >>= 1;
2728                 if(sisvga_engine == SIS_300_VGA) {
2729                         switch (reg) {
2730                            case SIS_EXTERNAL_CHIP_LVDS:
2731                                 ivideo.hasVB = HASVB_LVDS;
2732                                 ivideo.vbflags |= VB_LVDS;
2733                                 sishw_ext.usExternalChip = 0x01;
2734                                 printk(KERN_INFO "%s %s\n", stdstr, lvdsstr);
2735                                 break;
2736                            case SIS_EXTERNAL_CHIP_TRUMPION:
2737                                 ivideo.hasVB = HASVB_TRUMPION;
2738                                 sishw_ext.usExternalChip = 0x02;
2739                                 printk(KERN_INFO "%s Trumpion LCD scaler\n", stdstr);
2740                                 break;
2741                            case SIS_EXTERNAL_CHIP_CHRONTEL:
2742                                 ivideo.hasVB = HASVB_CHRONTEL;
2743                                 ivideo.vbflags |= VB_CHRONTEL;
2744                                 sishw_ext.usExternalChip = 0x04;
2745                                 printk(KERN_INFO "%s %s\n", stdstr, chrstr);
2746                                 break;
2747                            case SIS_EXTERNAL_CHIP_LVDS_CHRONTEL:
2748                                 ivideo.hasVB = HASVB_LVDS_CHRONTEL;
2749                                 ivideo.vbflags |= (VB_LVDS | VB_CHRONTEL);
2750                                 sishw_ext.usExternalChip = 0x05;
2751                                 printk(KERN_INFO "%s %s and %s\n", stdstr, lvdsstr, chrstr);
2752                                 break;
2753                         }
2754                 } else if(ivideo.chip < SIS_661) {
2755                         switch (reg) {
2756                            case SIS310_EXTERNAL_CHIP_LVDS:
2757                                 ivideo.hasVB = HASVB_LVDS;
2758                                 ivideo.vbflags |= VB_LVDS;
2759                                 sishw_ext.usExternalChip = 0x01;
2760                                 printk(KERN_INFO "%s %s\n", stdstr, lvdsstr);
2761                                 break;
2762                            case SIS310_EXTERNAL_CHIP_LVDS_CHRONTEL:
2763                                 ivideo.hasVB = HASVB_LVDS_CHRONTEL;
2764                                 ivideo.vbflags |= (VB_LVDS | VB_CHRONTEL);
2765                                 sishw_ext.usExternalChip = 0x05;
2766                                 printk(KERN_INFO "%s %s and %s\n", stdstr, lvdsstr, chrstr);
2767                                 break;
2768                         }
2769                 }
2770
2771         }
2772
2773         if(ivideo.vbflags & VB_SISBRIDGE) {
2774                 SiS_Sense30x();
2775         } else if(ivideo.vbflags & VB_CHRONTEL) {
2776                 SiS_SenseCh();
2777         }
2778
2779 }
2780
2781 /* ------------------ Sensing routines ------------------ */
2782
2783 static BOOLEAN
2784 sisfb_test_DDC1(void)
2785 {
2786     unsigned short old;
2787     int count = 48;
2788
2789     old = SiS_ReadDDC1Bit(&SiS_Pr);
2790     do {
2791        if(old != SiS_ReadDDC1Bit(&SiS_Pr)) break;
2792     } while(count--);
2793     return (count == -1) ? FALSE : TRUE;
2794 }
2795
2796 static void
2797 sisfb_sense_crt1(void)
2798 {
2799     unsigned char SR1F, CR63=0, CR17;
2800     unsigned short temp = 0xffff;
2801     int i;
2802     BOOLEAN mustwait = FALSE;
2803
2804     inSISIDXREG(SISSR,0x1F,SR1F);
2805     orSISIDXREG(SISSR,0x1F,0x04);
2806     andSISIDXREG(SISSR,0x1F,0x3F);
2807     if(SR1F & 0xc0) mustwait = TRUE;
2808
2809     if(sisvga_engine == SIS_315_VGA) {
2810        inSISIDXREG(SISCR,SiS_Pr.SiS_MyCR63,CR63);
2811        CR63 &= 0x40;
2812        andSISIDXREG(SISCR,SiS_Pr.SiS_MyCR63,0xBF);
2813     }
2814
2815     inSISIDXREG(SISCR,0x17,CR17);
2816     CR17 &= 0x80;
2817     if(!CR17) {
2818        orSISIDXREG(SISCR,0x17,0x80);
2819        mustwait = TRUE;
2820        outSISIDXREG(SISSR, 0x00, 0x01);
2821        outSISIDXREG(SISSR, 0x00, 0x03);
2822     }
2823
2824     if(mustwait) {
2825        for(i=0; i < 10; i++) sisfbwaitretracecrt1();
2826     }
2827
2828     i = 3;
2829     do {
2830        temp = SiS_HandleDDC(&SiS_Pr, ivideo.vbflags, sisvga_engine, 0, 0, NULL);
2831     } while(((temp == 0) || (temp == 0xffff)) && i--);
2832
2833     if((temp == 0) || (temp == 0xffff)) {
2834        if(sisfb_test_DDC1()) temp = 1;
2835     }
2836
2837     if((temp) && (temp != 0xffff)) {
2838        orSISIDXREG(SISCR,0x32,0x20);
2839     }
2840
2841     if(sisvga_engine == SIS_315_VGA) {
2842        setSISIDXREG(SISCR,SiS_Pr.SiS_MyCR63,0xBF,CR63);
2843     }
2844
2845     setSISIDXREG(SISCR,0x17,0x7F,CR17);
2846
2847     outSISIDXREG(SISSR,0x1F,SR1F);
2848 }
2849
2850 /* Determine and detect attached devices on SiS30x */
2851 static int
2852 SISDoSense(int tempbl, int tempbh, int tempcl, int tempch)
2853 {
2854     int temp;
2855
2856     outSISIDXREG(SISPART4,0x11,tempbl);
2857     temp = tempbh | tempcl;
2858     setSISIDXREG(SISPART4,0x10,0xe0,temp);
2859     SiS_DDC2Delay(&SiS_Pr, 0x1000);
2860     tempch &= 0x7f;
2861     inSISIDXREG(SISPART4,0x03,temp);
2862     temp ^= 0x0e;
2863     temp &= tempch;
2864     return((temp == tempch));
2865 }
2866
2867 static void
2868 SiS_Sense30x(void)
2869 {
2870   u8 backupP4_0d,backupP2_00;
2871   u8 svhs_bl, svhs_bh;
2872   u8 svhs_cl, svhs_ch;
2873   u8 cvbs_bl, cvbs_bh;
2874   u8 cvbs_cl, cvbs_ch;
2875   u8 vga2_bl, vga2_bh;
2876   u8 vga2_cl, vga2_ch;
2877   int myflag, result, haveresult, i, j;
2878   char stdstr[] = "sisfb: Detected";
2879   char tvstr[]  = "TV connected to";
2880
2881   inSISIDXREG(SISPART4,0x0d,backupP4_0d);
2882   if(!(ivideo.vbflags & (VB_301C|VB_302ELV))) {
2883      outSISIDXREG(SISPART4,0x0d,(backupP4_0d | 0x04));
2884   }
2885
2886   inSISIDXREG(SISPART2,0x00,backupP2_00);
2887   outSISIDXREG(SISPART2,0x00,(backupP2_00 | 0x1c));
2888
2889   if(sisvga_engine == SIS_300_VGA) {
2890
2891         if(ivideo.vbflags & (VB_301B|VB_301C|VB_302B|VB_301LV|VB_302LV)) {
2892                 vga2_bh = 0x01; vga2_bl = 0x90;
2893                 svhs_bh = 0x01; svhs_bl = 0x6b;
2894                 cvbs_bh = 0x01; cvbs_bl = 0x74;
2895         } else {
2896                 vga2_bh = 0x00; vga2_bl = 0xd1;
2897                 svhs_bh = 0x00; svhs_bl = 0xb9;
2898                 cvbs_bh = 0x00; cvbs_bl = 0xb3;
2899         }
2900         inSISIDXREG(SISPART4,0x01,myflag);
2901         if(myflag & 0x04) {
2902            vga2_bh = 0x00; vga2_bl = 0xfd;
2903            svhs_bh = 0x00; svhs_bl = 0xdd;
2904            cvbs_bh = 0x00; cvbs_bl = 0xee;
2905         }
2906         vga2_ch = 0x0e; vga2_cl = 0x08;
2907         svhs_ch = 0x04; svhs_cl = 0x04;
2908         cvbs_ch = 0x08; cvbs_cl = 0x04;
2909         if(ivideo.vbflags & (VB_301LV|VB_302LV)) {
2910                 vga2_bh = 0x00; vga2_bl = 0x00;
2911                 vga2_ch = 0x00; vga2_cl = 0x00;
2912          }
2913         if(ivideo.chip == SIS_300) {
2914            inSISIDXREG(SISSR,0x3b,myflag);
2915            if(!(myflag & 0x01)) {
2916                 vga2_bh = 0x00; vga2_bl = 0x00;
2917                 vga2_ch = 0x00; vga2_cl = 0x00;
2918            }
2919         }
2920
2921   } else {
2922
2923         if(ivideo.vbflags & (VB_301B|VB_302B)) {
2924                 vga2_bh = 0x01; vga2_bl = 0x90;
2925                 svhs_bh = 0x01; svhs_bl = 0x6b;
2926                 cvbs_bh = 0x01; cvbs_bl = 0x74;
2927         } else if(ivideo.vbflags & (VB_301C|VB_302ELV)) {
2928                 vga2_bh = 0x01; vga2_bl = 0x90;
2929                 svhs_bh = 0x01; svhs_bl = 0x6b;
2930                 cvbs_bh = 0x01; cvbs_bl = 0x10;
2931         } else if(ivideo.vbflags & (VB_301LV|VB_302LV)) {
2932                 vga2_bh = 0x00; vga2_bl = 0x00;
2933                 svhs_bh = 0x02; svhs_bl = 0x00;
2934                 cvbs_bh = 0x01; cvbs_bl = 0x00;
2935         } else {
2936                 vga2_bh = 0x00; vga2_bl = 0xd1;
2937                 svhs_bh = 0x00; svhs_bl = 0xb9;
2938                 cvbs_bh = 0x00; cvbs_bl = 0xb3;
2939                 inSISIDXREG(SISPART4,0x01,myflag);
2940                 if(myflag & 0x04) {
2941                    vga2_bh = 0x00; vga2_bl = 0xfd;
2942                    svhs_bh = 0x00; svhs_bl = 0xdd;
2943                    cvbs_bh = 0x00; cvbs_bl = 0xee;
2944                 }
2945         }
2946
2947         if(ivideo.vbflags & (VB_301LV|VB_302LV|VB_302ELV)) {
2948            vga2_bh = 0x00; vga2_bl = 0x00;
2949            vga2_ch = 0x00; vga2_cl = 0x00;
2950            svhs_ch = 0x04; svhs_cl = 0x08;
2951            cvbs_ch = 0x08; cvbs_cl = 0x08;
2952         } else {
2953            vga2_ch = 0x0e; vga2_cl = 0x08;
2954            svhs_ch = 0x04; svhs_cl = 0x04;
2955            cvbs_ch = 0x08; cvbs_cl = 0x04;
2956         }
2957     } 
2958
2959     if(vga2_ch || vga2_cl || vga2_bh || vga2_bl) {
2960        haveresult = 0;
2961        for(j = 0; j < 10; j++) {
2962           result = 0;
2963           for(i = 0; i < 3; i++) {
2964              if(SISDoSense(vga2_bl, vga2_bh, vga2_cl, vga2_ch))
2965                 result++;
2966           }
2967           if((result == 0) || (result >= 2)) break;
2968        }
2969        if(result) {
2970           printk(KERN_INFO "%s secondary VGA connection\n", stdstr);
2971           orSISIDXREG(SISCR, 0x32, 0x10);
2972        } else {
2973           andSISIDXREG(SISCR, 0x32, ~0x10);
2974        }
2975     }
2976
2977     if(ivideo.vbflags & (VB_301C|VB_302ELV)) {
2978        orSISIDXREG(SISPART4,0x0d,0x04);
2979     }
2980
2981     haveresult = 0;
2982     for(j = 0; j < 10; j++) {
2983        result = 0;
2984        for(i = 0; i < 3; i++) {
2985           if(SISDoSense(svhs_bl, svhs_bh, svhs_cl, svhs_ch))
2986                 result++;
2987        }
2988        if((result == 0) || (result >= 2)) break;
2989     }
2990     if(result) {
2991         printk(KERN_INFO "%s %s SVIDEO output\n", stdstr, tvstr);
2992         ivideo.vbflags |= TV_SVIDEO;
2993         orSISIDXREG(SISCR, 0x32, 0x02);
2994         andSISIDXREG(SISCR, 0x32, ~0x05);
2995     }
2996
2997     if(!result) {
2998
2999         haveresult = 0;
3000         for(j = 0; j < 10; j++) {
3001            result = 0;
3002            for(i = 0; i < 3; i++) {
3003               if(SISDoSense(cvbs_bl, cvbs_bh, cvbs_cl, cvbs_ch))
3004                 result++;
3005            }
3006            if((result == 0) || (result >= 2)) break;
3007         }
3008         if(result) {
3009             printk(KERN_INFO "%s %s COMPOSITE output\n", stdstr, tvstr);
3010             ivideo.vbflags |= TV_AVIDEO;
3011             orSISIDXREG(SISCR, 0x32, 0x01);
3012             andSISIDXREG(SISCR, 0x32, ~0x06);
3013         } else {
3014             andSISIDXREG(SISCR, 0x32, ~0x07);
3015         }
3016     }
3017     SISDoSense(0, 0, 0, 0);
3018
3019     outSISIDXREG(SISPART2,0x00,backupP2_00);
3020     outSISIDXREG(SISPART4,0x0d,backupP4_0d);
3021 }
3022
3023 /* Determine and detect attached TV's on Chrontel */
3024 static void
3025 SiS_SenseCh(void)
3026 {
3027
3028    u8 temp1, temp2;
3029 #ifdef CONFIG_FB_SIS_300
3030    unsigned char test[3];
3031    int i;
3032 #endif
3033    char stdstr[] = "sisfb: Chrontel: Detected TV connected to";
3034
3035    if(ivideo.chip < SIS_315H) {
3036
3037 #ifdef CONFIG_FB_SIS_300
3038        SiS_Pr.SiS_IF_DEF_CH70xx = 1;            /* Chrontel 700x */
3039        SiS_SetChrontelGPIO(&SiS_Pr, 0x9c);      /* Set general purpose IO for Chrontel communication */
3040        SiS_DDC2Delay(&SiS_Pr, 1000);
3041        temp1 = SiS_GetCH700x(&SiS_Pr, 0x25);
3042        /* See Chrontel TB31 for explanation */
3043        temp2 = SiS_GetCH700x(&SiS_Pr, 0x0e);
3044        if(((temp2 & 0x07) == 0x01) || (temp2 & 0x04)) {
3045           SiS_SetCH700x(&SiS_Pr, 0x0b0e);
3046           SiS_DDC2Delay(&SiS_Pr, 300);
3047        }
3048        temp2 = SiS_GetCH700x(&SiS_Pr, 0x25);
3049        if(temp2 != temp1) temp1 = temp2;
3050
3051        if((temp1 >= 0x22) && (temp1 <= 0x50)) {
3052            /* Read power status */
3053            temp1 = SiS_GetCH700x(&SiS_Pr, 0x0e);
3054            if((temp1 & 0x03) != 0x03) {
3055                 /* Power all outputs */
3056                 SiS_SetCH700x(&SiS_Pr, 0x0B0E);
3057                 SiS_DDC2Delay(&SiS_Pr, 300);
3058            }
3059            /* Sense connected TV devices */
3060            for(i = 0; i < 3; i++) {
3061                SiS_SetCH700x(&SiS_Pr, 0x0110);
3062                SiS_DDC2Delay(&SiS_Pr, 0x96);
3063                SiS_SetCH700x(&SiS_Pr, 0x0010);
3064                SiS_DDC2Delay(&SiS_Pr, 0x96);
3065                temp1 = SiS_GetCH700x(&SiS_Pr, 0x10);
3066                if(!(temp1 & 0x08))       test[i] = 0x02;
3067                else if(!(temp1 & 0x02))  test[i] = 0x01;
3068                else                      test[i] = 0;
3069                SiS_DDC2Delay(&SiS_Pr, 0x96);
3070            }
3071
3072            if(test[0] == test[1])      temp1 = test[0];
3073            else if(test[0] == test[2]) temp1 = test[0];
3074            else if(test[1] == test[2]) temp1 = test[1];
3075            else {
3076                 printk(KERN_INFO
3077                         "sisfb: TV detection unreliable - test results varied\n");
3078                 temp1 = test[2];
3079            }
3080            if(temp1 == 0x02) {
3081                 printk(KERN_INFO "%s SVIDEO output\n", stdstr);
3082                 ivideo.vbflags |= TV_SVIDEO;
3083                 orSISIDXREG(SISCR, 0x32, 0x02);
3084                 andSISIDXREG(SISCR, 0x32, ~0x05);
3085            } else if (temp1 == 0x01) {
3086                 printk(KERN_INFO "%s CVBS output\n", stdstr);
3087                 ivideo.vbflags |= TV_AVIDEO;
3088                 orSISIDXREG(SISCR, 0x32, 0x01);
3089                 andSISIDXREG(SISCR, 0x32, ~0x06);
3090            } else {
3091                 SiS_SetCH70xxANDOR(&SiS_Pr, 0x010E,0xF8);
3092                 andSISIDXREG(SISCR, 0x32, ~0x07);
3093            }
3094        } else if(temp1 == 0) {
3095           SiS_SetCH70xxANDOR(&SiS_Pr, 0x010E,0xF8);
3096           andSISIDXREG(SISCR, 0x32, ~0x07);
3097        }
3098        /* Set general purpose IO for Chrontel communication */
3099        SiS_SetChrontelGPIO(&SiS_Pr, 0x00);
3100 #endif
3101
3102    } else {
3103
3104 #ifdef CONFIG_FB_SIS_315
3105         SiS_Pr.SiS_IF_DEF_CH70xx = 2;           /* Chrontel 7019 */
3106         temp1 = SiS_GetCH701x(&SiS_Pr, 0x49);
3107         SiS_SetCH701x(&SiS_Pr, 0x2049);
3108         SiS_DDC2Delay(&SiS_Pr, 0x96);
3109         temp2 = SiS_GetCH701x(&SiS_Pr, 0x20);
3110         temp2 |= 0x01;
3111         SiS_SetCH701x(&SiS_Pr, (temp2 << 8) | 0x20);
3112         SiS_DDC2Delay(&SiS_Pr, 0x96);
3113         temp2 ^= 0x01;
3114         SiS_SetCH701x(&SiS_Pr, (temp2 << 8) | 0x20);
3115         SiS_DDC2Delay(&SiS_Pr, 0x96);
3116         temp2 = SiS_GetCH701x(&SiS_Pr, 0x20);
3117         SiS_SetCH701x(&SiS_Pr, (temp1 << 8) | 0x49);
3118         temp1 = 0;
3119         if(temp2 & 0x02) temp1 |= 0x01;
3120         if(temp2 & 0x10) temp1 |= 0x01;
3121         if(temp2 & 0x04) temp1 |= 0x02;
3122         if( (temp1 & 0x01) && (temp1 & 0x02) ) temp1 = 0x04;
3123         switch(temp1) {
3124         case 0x01:
3125              printk(KERN_INFO "%s CVBS output\n", stdstr);
3126              ivideo.vbflags |= TV_AVIDEO;
3127              orSISIDXREG(SISCR, 0x32, 0x01);
3128              andSISIDXREG(SISCR, 0x32, ~0x06);
3129              break;
3130         case 0x02:
3131              printk(KERN_INFO "%s SVIDEO output\n", stdstr);
3132              ivideo.vbflags |= TV_SVIDEO;
3133              orSISIDXREG(SISCR, 0x32, 0x02);
3134              andSISIDXREG(SISCR, 0x32, ~0x05);
3135              break;
3136         case 0x04:
3137              printk(KERN_INFO "%s SCART output\n", stdstr);
3138              orSISIDXREG(SISCR, 0x32, 0x04);
3139              andSISIDXREG(SISCR, 0x32, ~0x03);
3140              break;
3141         default:
3142              andSISIDXREG(SISCR, 0x32, ~0x07);
3143         }
3144 #endif
3145
3146    }
3147 }
3148
3149
3150 /* ------------------------ Heap routines -------------------------- */
3151
3152 static int sisfb_heap_init(void)
3153 {
3154         SIS_OH *poh;
3155         u8 temp=0;
3156 #ifdef CONFIG_FB_SIS_315
3157         int            agp_enabled = 1;
3158         u32            agp_size;
3159         unsigned long *cmdq_baseport = 0;
3160         unsigned long *read_port = 0;
3161         unsigned long *write_port = 0;
3162         SIS_CMDTYPE    cmd_type;
3163 #ifndef AGPOFF
3164 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
3165         struct agp_kern_info  *agp_info;
3166         struct agp_memory     *agp;
3167 #else
3168         agp_kern_info  *agp_info;
3169         agp_memory     *agp;
3170 #endif
3171         u32            agp_phys;
3172 #endif
3173 #endif
3174 /*     The heap start is either set manually using the "mem" parameter, or
3175  *     defaults as follows:
3176  *     -) If more than 16MB videoRAM available, let our heap start at 12MB.
3177  *     -) If more than  8MB videoRAM available, let our heap start at  8MB.
3178  *     -) If 4MB or less is available, let it start at 4MB.
3179  *     This is for avoiding a clash with X driver which uses the beginning
3180  *     of the videoRAM. To limit size of X framebuffer, use Option MaxXFBMem
3181  *     in XF86Config-4.
3182  *     The heap start can also be specified by parameter "mem" when starting the sisfb
3183  *     driver. sisfb mem=1024 lets heap starts at 1MB, etc.
3184  *
3185  *     On the 315 and Xabre series, the default is a 1MB heap since DRI is not
3186  *     supported there.
3187  */
3188      if ((!sisfb_mem) || (sisfb_mem > (ivideo.video_size/1024))) {
3189         if(sisvga_engine == SIS_300_VGA) {
3190            if (ivideo.video_size > 0x1000000) {
3191                 ivideo.heapstart = 0xc00000;
3192            } else if (ivideo.video_size > 0x800000) {
3193                 ivideo.heapstart = 0x800000;
3194            } else {
3195                 ivideo.heapstart = 0x400000;
3196            }
3197         } else {
3198            ivideo.heapstart = ivideo.video_size - 0x100000;
3199         }
3200      } else {
3201            ivideo.heapstart = sisfb_mem * 1024;
3202      }
3203      sisfb_heap_start = (unsigned long) (ivideo.video_vbase + ivideo.heapstart);
3204      printk(KERN_INFO "sisfb: Memory heap starting at %dK\n",
3205                                         (int)(ivideo.heapstart / 1024));
3206
3207      sisfb_heap_end = (unsigned long) ivideo.video_vbase + ivideo.video_size;
3208      sisfb_heap_size = sisfb_heap_end - sisfb_heap_start;
3209
3210 #ifdef CONFIG_FB_SIS_315
3211      if (sisvga_engine == SIS_315_VGA) {
3212         /* Now initialize the 315/330 series' command queue mode.
3213          * On 315, there are three queue modes available which
3214          * are chosen by setting bits 7:5 in SR26:
3215          * 1. MMIO queue mode (bit 5, 0x20). The hardware will keep
3216          *    track of the queue, the FIFO, command parsing and so
3217          *    on. This is the one comparable to the 300 series.
3218          * 2. VRAM queue mode (bit 6, 0x40). In this case, one will
3219          *    have to do queue management himself. Register 0x85c4 will
3220          *    hold the location of the next free queue slot, 0x85c8
3221          *    is the "queue read pointer" whose way of working is
3222          *    unknown to me. Anyway, this mode would require a
3223          *    translation of the MMIO commands to some kind of
3224          *    accelerator assembly and writing these commands
3225          *    to the memory location pointed to by 0x85c4.
3226          *    We will not use this, as nobody knows how this
3227          *    "assembly" works, and as it would require a complete
3228          *    re-write of the accelerator code.
3229          * 3. AGP queue mode (bit 7, 0x80). Works as 2., but keeps the
3230          *    queue in AGP memory space.
3231          *
3232          * SR26 bit 4 is called "Bypass H/W queue".
3233          * SR26 bit 1 is called "Enable Command Queue Auto Correction"
3234          * SR26 bit 0 resets the queue
3235          * Size of queue memory is encoded in bits 3:2 like this:
3236          *    00  (0x00)  512K
3237          *    01  (0x04)  1M
3238          *    10  (0x08)  2M
3239          *    11  (0x0C)  4M
3240          * The queue location is to be written to 0x85C0.
3241          *
3242          */
3243         cmdq_baseport = (unsigned long *)(ivideo.mmio_vbase + MMIO_QUEUE_PHYBASE);
3244         write_port    = (unsigned long *)(ivideo.mmio_vbase + MMIO_QUEUE_WRITEPORT);
3245         read_port     = (unsigned long *)(ivideo.mmio_vbase + MMIO_QUEUE_READPORT);
3246
3247         DPRINTK("AGP base: 0x%p, read: 0x%p, write: 0x%p\n", cmdq_baseport, read_port, write_port);
3248
3249         agp_size  = COMMAND_QUEUE_AREA_SIZE;
3250
3251 #ifndef AGPOFF
3252         if (sisfb_queuemode == AGP_CMD_QUEUE) {
3253 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
3254                 agp_info = vmalloc(sizeof(*agp_info));
3255                 memset((void*)agp_info, 0x00, sizeof(*agp_info));
3256 #else
3257                 agp_info = vmalloc(sizeof(agp_kern_info));
3258                 memset((void*)agp_info, 0x00, sizeof(agp_kern_info));
3259 #endif
3260                 agp_copy_info(agp_info);
3261
3262                 agp_backend_acquire();
3263
3264                 agp = agp_allocate_memory(COMMAND_QUEUE_AREA_SIZE/PAGE_SIZE,
3265                                           AGP_NORMAL_MEMORY);
3266                 if (agp == NULL) {
3267                         DPRINTK("sisfb: Allocating AGP buffer failed.\n");
3268                         agp_enabled = 0;
3269                 } else {
3270                         if (agp_bind_memory(agp, agp->pg_start) != 0) {
3271                                 DPRINTK("sisfb: AGP: Failed to bind memory\n");
3272                                 /* TODO: Free AGP memory here */
3273                                 agp_enabled = 0;
3274                         } else {
3275                                 agp_enable(0);
3276                         }
3277                 }
3278         }
3279 #else
3280         agp_enabled = 0;
3281 #endif
3282
3283         /* Now select the queue mode */
3284
3285         if ((agp_enabled) && (sisfb_queuemode == AGP_CMD_QUEUE)) {
3286                 cmd_type = AGP_CMD_QUEUE;
3287                 printk(KERN_INFO "sisfb: Using AGP queue mode\n");
3288 /*      } else if (sisfb_heap_size >= COMMAND_QUEUE_AREA_SIZE)  */
3289         } else if (sisfb_queuemode == VM_CMD_QUEUE) {
3290                 cmd_type = VM_CMD_QUEUE;
3291                 printk(KERN_INFO "sisfb: Using VRAM queue mode\n");
3292         } else {
3293                 printk(KERN_INFO "sisfb: Using MMIO queue mode\n");
3294                 cmd_type = MMIO_CMD;
3295         }
3296
3297         switch (agp_size) {
3298            case 0x80000:
3299                 temp = SIS_CMD_QUEUE_SIZE_512k;
3300                 break;
3301            case 0x100000:
3302                 temp = SIS_CMD_QUEUE_SIZE_1M;
3303                 break;
3304            case 0x200000:
3305                 temp = SIS_CMD_QUEUE_SIZE_2M;
3306                 break;
3307            case 0x400000:
3308                 temp = SIS_CMD_QUEUE_SIZE_4M;
3309                 break;
3310         }
3311
3312         switch (cmd_type) {
3313            case AGP_CMD_QUEUE:
3314 #ifndef AGPOFF
3315                 DPRINTK("sisfb: AGP buffer base = 0x%lx, offset = 0x%x, size = %dK\n",
3316                         agp_info->aper_base, agp->physical, agp_size/1024);
3317
3318                 agp_phys = agp_info->aper_base + agp->physical;
3319
3320                 outSISIDXREG(SISCR,  IND_SIS_AGP_IO_PAD, 0);
3321                 outSISIDXREG(SISCR,  IND_SIS_AGP_IO_PAD, SIS_AGP_2X);
3322
3323                 outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_THRESHOLD, COMMAND_QUEUE_THRESHOLD);
3324
3325                 outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, SIS_CMD_QUEUE_RESET);
3326
3327                 *write_port = *read_port;
3328
3329                 temp |= SIS_AGP_CMDQUEUE_ENABLE;
3330                 outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, temp);
3331
3332                 *cmdq_baseport = agp_phys;
3333
3334                 sisfb_caps |= AGP_CMD_QUEUE_CAP;
3335 #endif
3336                 break;
3337
3338            case VM_CMD_QUEUE:
3339                 sisfb_heap_end -= COMMAND_QUEUE_AREA_SIZE;
3340                 sisfb_heap_size -= COMMAND_QUEUE_AREA_SIZE;
3341
3342                 outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_THRESHOLD, COMMAND_QUEUE_THRESHOLD);
3343
3344                 outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, SIS_CMD_QUEUE_RESET);
3345
3346                 *write_port = *read_port;
3347
3348                 temp |= SIS_VRAM_CMDQUEUE_ENABLE;
3349                 outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, temp);
3350
3351                 *cmdq_baseport = ivideo.video_size - COMMAND_QUEUE_AREA_SIZE;
3352
3353                 sisfb_caps |= VM_CMD_QUEUE_CAP;
3354
3355                 DPRINTK("sisfb: VM Cmd Queue offset = 0x%lx, size is %dK\n",
3356                         *cmdq_baseport, COMMAND_QUEUE_AREA_SIZE/1024);
3357                 break;
3358
3359            default:  /* MMIO */
3360                 sisfb_heap_end -= COMMAND_QUEUE_AREA_SIZE;
3361                 sisfb_heap_size -= COMMAND_QUEUE_AREA_SIZE;
3362
3363                 outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_THRESHOLD, COMMAND_QUEUE_THRESHOLD);
3364                 outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, SIS_CMD_QUEUE_RESET);
3365
3366                 *write_port = *read_port;
3367
3368                 /* Set Auto_Correction bit */
3369                 temp |= (SIS_MMIO_CMD_ENABLE | SIS_CMD_AUTO_CORR);
3370                 outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, temp);
3371
3372                 *cmdq_baseport = ivideo.video_size - COMMAND_QUEUE_AREA_SIZE;
3373
3374                 sisfb_caps |= MMIO_CMD_QUEUE_CAP;
3375
3376                 DPRINTK("sisfb: MMIO Cmd Queue offset = 0x%lx, size is %dK\n",
3377                         *cmdq_baseport, COMMAND_QUEUE_AREA_SIZE/1024);
3378                 break;
3379         }
3380      } /* sisvga_engine = 315 */
3381 #endif
3382
3383 #ifdef CONFIG_FB_SIS_300
3384      if (sisvga_engine == SIS_300_VGA) {
3385             /* Now initialize TurboQueue. TB is always located at the very
3386              * top of the video RAM. */
3387             if (sisfb_heap_size >= TURBO_QUEUE_AREA_SIZE) {
3388                 unsigned int  tqueue_pos;
3389                 u8 tq_state;
3390
3391                 tqueue_pos = (ivideo.video_size -
3392                        TURBO_QUEUE_AREA_SIZE) / (64 * 1024);
3393
3394                 temp = (u8) (tqueue_pos & 0xff);
3395
3396                 inSISIDXREG(SISSR, IND_SIS_TURBOQUEUE_SET, tq_state);
3397                 tq_state |= 0xf0;
3398                 tq_state &= 0xfc;
3399                 tq_state |= (u8) (tqueue_pos >> 8);
3400                 outSISIDXREG(SISSR, IND_SIS_TURBOQUEUE_SET, tq_state);
3401
3402                 outSISIDXREG(SISSR, IND_SIS_TURBOQUEUE_ADR, temp);
3403
3404                 sisfb_caps |= TURBO_QUEUE_CAP;
3405
3406                 sisfb_heap_end -= TURBO_QUEUE_AREA_SIZE;
3407                 sisfb_heap_size -= TURBO_QUEUE_AREA_SIZE;
3408                 DPRINTK("sisfb: TurboQueue start at 0x%lx, size is %dK\n",
3409                         sisfb_heap_end, TURBO_QUEUE_AREA_SIZE/1024);
3410             }
3411      }
3412 #endif
3413      /* Now reserve memory for the HWCursor. It is always located at the very
3414         top of the videoRAM, right below the TB memory area (if used). */
3415      if (sisfb_heap_size >= sisfb_hwcursor_size) {
3416                 sisfb_heap_end -= sisfb_hwcursor_size;
3417                 sisfb_heap_size -= sisfb_hwcursor_size;
3418                 sisfb_hwcursor_vbase = sisfb_heap_end;
3419
3420                 sisfb_caps |= HW_CURSOR_CAP;
3421
3422                 DPRINTK("sisfb: Hardware Cursor start at 0x%lx, size is %dK\n",
3423                         sisfb_heap_end, sisfb_hwcursor_size/1024);
3424      }
3425
3426      sisfb_heap.poha_chain = NULL;
3427      sisfb_heap.poh_freelist = NULL;
3428
3429      poh = sisfb_poh_new_node();
3430
3431      if(poh == NULL)  return 1;
3432         
3433      poh->poh_next = &sisfb_heap.oh_free;
3434      poh->poh_prev = &sisfb_heap.oh_free;
3435      poh->size = sisfb_heap_end - sisfb_heap_start + 1;
3436      poh->offset = sisfb_heap_start - (unsigned long) ivideo.video_vbase;
3437
3438      DPRINTK("sisfb: Heap start:0x%p, end:0x%p, len=%dk\n",
3439                 (char *) sisfb_heap_start, (char *) sisfb_heap_end,
3440                 (unsigned int) poh->size / 1024);
3441
3442      DPRINTK("sisfb: First Node offset:0x%x, size:%dk\n",
3443                 (unsigned int) poh->offset, (unsigned int) poh->size / 1024);
3444
3445      sisfb_heap.oh_free.poh_next = poh;
3446      sisfb_heap.oh_free.poh_prev = poh;
3447      sisfb_heap.oh_free.size = 0;
3448      sisfb_heap.max_freesize = poh->size;
3449
3450      sisfb_heap.oh_used.poh_next = &sisfb_heap.oh_used;
3451      sisfb_heap.oh_used.poh_prev = &sisfb_heap.oh_used;
3452      sisfb_heap.oh_used.size = SENTINEL;
3453
3454      return 0;
3455 }
3456
3457 static SIS_OH *sisfb_poh_new_node(void)
3458 {
3459         int           i;
3460         unsigned long cOhs;
3461         SIS_OHALLOC   *poha;
3462         SIS_OH        *poh;
3463
3464         if (sisfb_heap.poh_freelist == NULL) {
3465                 poha = kmalloc(OH_ALLOC_SIZE, GFP_KERNEL);
3466                 if(!poha) return NULL;
3467
3468                 poha->poha_next = sisfb_heap.poha_chain;
3469                 sisfb_heap.poha_chain = poha;
3470
3471                 cOhs = (OH_ALLOC_SIZE - sizeof(SIS_OHALLOC)) / sizeof(SIS_OH) + 1;
3472
3473                 poh = &poha->aoh[0];
3474                 for (i = cOhs - 1; i != 0; i--) {
3475                         poh->poh_next = poh + 1;
3476                         poh = poh + 1;
3477                 }
3478
3479                 poh->poh_next = NULL;
3480                 sisfb_heap.poh_freelist = &poha->aoh[0];
3481         }
3482
3483         poh = sisfb_heap.poh_freelist;
3484         sisfb_heap.poh_freelist = poh->poh_next;
3485
3486         return (poh);
3487 }
3488
3489 static SIS_OH *sisfb_poh_allocate(unsigned long size)
3490 {
3491         SIS_OH *pohThis;
3492         SIS_OH *pohRoot;
3493         int     bAllocated = 0;
3494
3495         if (size > sisfb_heap.max_freesize) {
3496                 DPRINTK("sisfb: Can't allocate %dk size on offscreen\n",
3497                         (unsigned int) size / 1024);
3498                 return (NULL);
3499         }
3500
3501         pohThis = sisfb_heap.oh_free.poh_next;
3502
3503         while (pohThis != &sisfb_heap.oh_free) {
3504                 if (size <= pohThis->size) {
3505                         bAllocated = 1;
3506                         break;
3507                 }
3508                 pohThis = pohThis->poh_next;
3509         }
3510
3511         if (!bAllocated) {
3512                 DPRINTK("sisfb: Can't allocate %dk size on offscreen\n",
3513                         (unsigned int) size / 1024);
3514                 return (NULL);
3515         }
3516
3517         if (size == pohThis->size) {
3518                 pohRoot = pohThis;
3519                 sisfb_delete_node(pohThis);
3520         } else {
3521                 pohRoot = sisfb_poh_new_node();
3522
3523                 if (pohRoot == NULL) {
3524                         return (NULL);
3525                 }
3526
3527                 pohRoot->offset = pohThis->offset;
3528                 pohRoot->size = size;
3529
3530                 pohThis->offset += size;
3531                 pohThis->size -= size;
3532         }
3533
3534         sisfb_heap.max_freesize -= size;
3535
3536         pohThis = &sisfb_heap.oh_used;
3537         sisfb_insert_node(pohThis, pohRoot);
3538
3539         return (pohRoot);
3540 }
3541
3542 static void sisfb_delete_node(SIS_OH *poh)
3543 {
3544         SIS_OH *poh_prev;
3545         SIS_OH *poh_next;
3546
3547         poh_prev = poh->poh_prev;
3548         poh_next = poh->poh_next;
3549
3550         poh_prev->poh_next = poh_next;
3551         poh_next->poh_prev = poh_prev;
3552
3553 }
3554
3555 static void sisfb_insert_node(SIS_OH *pohList, SIS_OH *poh)
3556 {
3557         SIS_OH *pohTemp;
3558
3559         pohTemp = pohList->poh_next;
3560
3561         pohList->poh_next = poh;
3562         pohTemp->poh_prev = poh;
3563
3564         poh->poh_prev = pohList;
3565         poh->poh_next = pohTemp;
3566 }
3567
3568 static SIS_OH *sisfb_poh_free(unsigned long base)
3569 {
3570         SIS_OH *pohThis;
3571         SIS_OH *poh_freed;
3572         SIS_OH *poh_prev;
3573         SIS_OH *poh_next;
3574         unsigned long ulUpper;
3575         unsigned long ulLower;
3576         int foundNode = 0;
3577
3578         poh_freed = sisfb_heap.oh_used.poh_next;
3579
3580         while(poh_freed != &sisfb_heap.oh_used) {
3581                 if(poh_freed->offset == base) {
3582                         foundNode = 1;
3583                         break;
3584                 }
3585
3586                 poh_freed = poh_freed->poh_next;
3587         }
3588
3589         if (!foundNode)  return (NULL);
3590
3591         sisfb_heap.max_freesize += poh_freed->size;
3592
3593         poh_prev = poh_next = NULL;
3594         ulUpper = poh_freed->offset + poh_freed->size;
3595         ulLower = poh_freed->offset;
3596
3597         pohThis = sisfb_heap.oh_free.poh_next;
3598
3599         while (pohThis != &sisfb_heap.oh_free) {
3600                 if (pohThis->offset == ulUpper) {
3601                         poh_next = pohThis;
3602                 }
3603                         else if ((pohThis->offset + pohThis->size) ==
3604                                  ulLower) {
3605                         poh_prev = pohThis;
3606                 }
3607                 pohThis = pohThis->poh_next;
3608         }
3609
3610         sisfb_delete_node(poh_freed);
3611
3612         if (poh_prev && poh_next) {
3613                 poh_prev->size += (poh_freed->size + poh_next->size);
3614                 sisfb_delete_node(poh_next);
3615                 sisfb_free_node(poh_freed);
3616                 sisfb_free_node(poh_next);
3617                 return (poh_prev);
3618         }
3619
3620         if (poh_prev) {
3621                 poh_prev->size += poh_freed->size;
3622                 sisfb_free_node(poh_freed);
3623                 return (poh_prev);
3624         }
3625
3626         if (poh_next) {
3627                 poh_next->size += poh_freed->size;
3628                 poh_next->offset = poh_freed->offset;
3629                 sisfb_free_node(poh_freed);
3630                 return (poh_next);
3631         }
3632
3633         sisfb_insert_node(&sisfb_heap.oh_free, poh_freed);
3634
3635         return (poh_freed);
3636 }
3637
3638 static void sisfb_free_node(SIS_OH *poh)
3639 {
3640         if(poh == NULL) return;
3641
3642         poh->poh_next = sisfb_heap.poh_freelist;
3643         sisfb_heap.poh_freelist = poh;
3644
3645 }
3646
3647 void sis_malloc(struct sis_memreq *req)
3648 {
3649         SIS_OH *poh;
3650
3651         poh = sisfb_poh_allocate(req->size);
3652
3653         if(poh == NULL) {
3654                 req->offset = 0;
3655                 req->size = 0;
3656                 DPRINTK("sisfb: Video RAM allocation failed\n");
3657         } else {
3658                 DPRINTK("sisfb: Video RAM allocation succeeded: 0x%p\n",
3659                         (char *) (poh->offset + (unsigned long) ivideo.video_vbase));
3660
3661                 req->offset = poh->offset;
3662                 req->size = poh->size;
3663         }
3664 }
3665
3666 void sis_free(unsigned long base)
3667 {
3668         SIS_OH *poh;
3669
3670         poh = sisfb_poh_free(base);
3671
3672         if(poh == NULL) {
3673                 DPRINTK("sisfb: sisfb_poh_free() failed at base 0x%x\n",
3674                         (unsigned int) base);
3675         }
3676 }
3677
3678 /* --------------------- SetMode routines ------------------------- */
3679
3680 static void sisfb_pre_setmode(void)
3681 {
3682         u8 cr30 = 0, cr31 = 0, cr33 = 0, cr35 = 0;
3683
3684         ivideo.currentvbflags &= (VB_VIDEOBRIDGE | VB_DISPTYPE_DISP2);
3685
3686         inSISIDXREG(SISCR, 0x31, cr31);
3687         cr31 &= ~0x60;
3688         cr31 |= 0x04;
3689
3690         cr33 = sisfb_rate_idx & 0x0F;
3691
3692         SiS_SetEnableDstn(&SiS_Pr, FALSE);
3693         SiS_SetEnableFstn(&SiS_Pr, FALSE);
3694
3695         switch (ivideo.currentvbflags & VB_DISPTYPE_DISP2) {
3696            case CRT2_TV:
3697                 ivideo.disp_state = DISPTYPE_TV;
3698                 if (ivideo.vbflags & TV_SVIDEO) {
3699                         cr30 = (SIS_VB_OUTPUT_SVIDEO | SIS_SIMULTANEOUS_VIEW_ENABLE);
3700                         ivideo.currentvbflags |= TV_SVIDEO;
3701                         ivideo.TV_plug = TVPLUG_SVIDEO;
3702                 } else if (ivideo.vbflags & TV_AVIDEO) {
3703                         cr30 = (SIS_VB_OUTPUT_COMPOSITE | SIS_SIMULTANEOUS_VIEW_ENABLE);
3704                         ivideo.currentvbflags |= TV_AVIDEO;
3705                         ivideo.TV_plug = TVPLUG_COMPOSITE;
3706                 } else if (ivideo.vbflags & TV_SCART) {
3707                         cr30 = (SIS_VB_OUTPUT_SCART | SIS_SIMULTANEOUS_VIEW_ENABLE);
3708                         ivideo.currentvbflags |= TV_SCART;
3709                         ivideo.TV_plug = TVPLUG_SCART;
3710                 }
3711                 cr31 |= SIS_DRIVER_MODE;
3712
3713                 if(!(ivideo.vbflags & TV_HIVISION)) {
3714                         if (ivideo.vbflags & TV_PAL) {
3715                                 cr31 |= 0x01;
3716                                 cr35 |= 0x01;
3717                                 ivideo.currentvbflags |= TV_PAL;
3718                                 ivideo.TV_type = TVMODE_PAL;
3719                         } else {
3720                                 cr31 &= ~0x01;
3721                                 cr35 &= ~0x01;
3722                                 ivideo.currentvbflags |= TV_NTSC;
3723                                 ivideo.TV_type = TVMODE_NTSC;
3724                         }
3725                 }
3726                 break;
3727            case CRT2_LCD:
3728                 ivideo.disp_state = DISPTYPE_LCD;
3729                 cr30  = (SIS_VB_OUTPUT_LCD | SIS_SIMULTANEOUS_VIEW_ENABLE);
3730                 cr31 |= SIS_DRIVER_MODE;
3731                 SiS_SetEnableDstn(&SiS_Pr, sisfb_dstn);
3732                 SiS_SetEnableFstn(&SiS_Pr, sisfb_fstn);
3733                 break;
3734            case CRT2_VGA:
3735                 ivideo.disp_state = DISPTYPE_CRT2;
3736                 cr30 = (SIS_VB_OUTPUT_CRT2 | SIS_SIMULTANEOUS_VIEW_ENABLE);
3737                 cr31 |= SIS_DRIVER_MODE;
3738                 if(sisfb_nocrt2rate) {
3739                         cr33 |= (sisbios_mode[sisfb_mode_idx].rate_idx << 4);
3740                 } else {
3741                         cr33 |= ((sisfb_rate_idx & 0x0F) << 4);
3742                 }
3743                 break;
3744            default:     /* disable CRT2 */
3745                 cr30 = 0x00;
3746                 cr31 |= (SIS_DRIVER_MODE | SIS_VB_OUTPUT_DISABLE);
3747         }
3748
3749         if(ivideo.chip >= SIS_661) {
3750            cr31 &= ~0x01;
3751            /* Leave overscan bit alone */
3752            setSISIDXREG(SISCR, 0x35, ~0x10, cr35);
3753         }
3754         outSISIDXREG(SISCR, IND_SIS_SCRATCH_REG_CR30, cr30);
3755         outSISIDXREG(SISCR, IND_SIS_SCRATCH_REG_CR31, cr31);
3756         outSISIDXREG(SISCR, IND_SIS_SCRATCH_REG_CR33, cr33);
3757
3758 #ifdef CONFIG_FB_SIS_315
3759         if(sisvga_engine == SIS_315_VGA) {
3760            /* Clear LCDA and PAL-N/M bits */
3761            andSISIDXREG(SISCR,0x38,~0x03);
3762            if(ivideo.chip < SIS_661) {
3763               andSISIDXREG(SISCR,0x38,~0xc0);
3764            }
3765         }
3766 #endif
3767
3768         if(ivideo.accel) sisfb_syncaccel();
3769
3770         SiS_Pr.SiS_UseOEM = sisfb_useoem;
3771 }
3772
3773 static void sisfb_post_setmode(void)
3774 {
3775         u8 reg;
3776         BOOLEAN crt1isoff = FALSE;
3777 #ifdef CONFIG_FB_SIS_315
3778         u8 reg1;
3779 #endif
3780 #ifdef CONFIG_FB_SIS_300
3781         BOOLEAN doit = TRUE;
3782 #endif
3783         /* We can't switch off CRT1 if bridge is in slave mode */
3784         if(ivideo.vbflags & VB_VIDEOBRIDGE) {
3785 #ifdef CONFIG_FB_SIS_300
3786                 if(sisvga_engine == SIS_300_VGA) {
3787                         inSISIDXREG(SISPART1, 0x00, reg);
3788                         if((reg & 0xa0) == 0x20) {
3789                                 doit = FALSE;
3790                         }
3791                 }
3792 #endif
3793         } else sisfb_crt1off = 0;
3794
3795         if(sisvga_engine == SIS_300_VGA) {
3796
3797 #ifdef CONFIG_FB_SIS_300
3798            if((sisfb_crt1off) && (doit)) {
3799                 crt1isoff = TRUE;
3800                 reg = 0x00;
3801            } else {
3802                 crt1isoff = FALSE;
3803                 reg = 0x80;
3804            }
3805            setSISIDXREG(SISCR, 0x17, 0x7f, reg);
3806 #endif
3807
3808         } else {
3809
3810 #ifdef CONFIG_FB_SIS_315
3811            if(sisfb_crt1off) {
3812                 crt1isoff = TRUE;
3813                 reg  = 0x40;
3814                 reg1 = 0xc0;
3815            } else {
3816                 crt1isoff = FALSE;
3817                 reg  = 0x00;
3818                 reg1 = 0x00;
3819
3820            }
3821            setSISIDXREG(SISCR, SiS_Pr.SiS_MyCR63, ~0x40, reg);
3822            setSISIDXREG(SISSR, 0x1f, ~0xc0, reg1);
3823 #endif
3824
3825         }
3826
3827         if(crt1isoff) {
3828            ivideo.currentvbflags &= ~VB_DISPTYPE_CRT1;
3829            ivideo.currentvbflags |= VB_SINGLE_MODE;
3830            ivideo.disp_state |= DISPMODE_SINGLE;
3831         } else {
3832            ivideo.currentvbflags |= VB_DISPTYPE_CRT1;
3833            ivideo.disp_state |= DISPTYPE_CRT1;
3834            if(ivideo.currentvbflags & VB_DISPTYPE_CRT2) {
3835                 ivideo.currentvbflags |= VB_MIRROR_MODE;
3836                 ivideo.disp_state |= DISPMODE_MIRROR;
3837            } else {
3838                 ivideo.currentvbflags |= VB_SINGLE_MODE;
3839                 ivideo.disp_state |= DISPMODE_SINGLE;
3840            }
3841         }
3842
3843         andSISIDXREG(SISSR, IND_SIS_RAMDAC_CONTROL, ~0x04);
3844
3845         if((ivideo.currentvbflags & CRT2_TV) && (ivideo.vbflags & VB_301)) {  /* Set filter for SiS301 */
3846
3847                 switch (ivideo.video_width) {
3848                    case 320:
3849                         filter_tb = (ivideo.vbflags & TV_NTSC) ? 4 : 12;
3850                         break;
3851                    case 640:
3852                         filter_tb = (ivideo.vbflags & TV_NTSC) ? 5 : 13;
3853                         break;
3854                    case 720:
3855                         filter_tb = (ivideo.vbflags & TV_NTSC) ? 6 : 14;
3856                         break;
3857                    case 400:
3858                    case 800:
3859                         filter_tb = (ivideo.vbflags & TV_NTSC) ? 7 : 15;
3860                         break;
3861                    default:
3862                         filter = -1;
3863                         break;
3864                 }
3865
3866                 orSISIDXREG(SISPART1, sisfb_CRT2_write_enable, 0x01);
3867
3868                 if(ivideo.vbflags & TV_NTSC) {
3869
3870                         andSISIDXREG(SISPART2, 0x3a, 0x1f);
3871
3872                         if (ivideo.vbflags & TV_SVIDEO) {
3873
3874                                 andSISIDXREG(SISPART2, 0x30, 0xdf);
3875
3876                         } else if (ivideo.vbflags & TV_AVIDEO) {
3877
3878                                 orSISIDXREG(SISPART2, 0x30, 0x20);
3879
3880                                 switch (ivideo.video_width) {
3881                                 case 640:
3882                                         outSISIDXREG(SISPART2, 0x35, 0xEB);
3883                                         outSISIDXREG(SISPART2, 0x36, 0x04);
3884                                         outSISIDXREG(SISPART2, 0x37, 0x25);
3885                                         outSISIDXREG(SISPART2, 0x38, 0x18);
3886                                         break;
3887                                 case 720:
3888                                         outSISIDXREG(SISPART2, 0x35, 0xEE);
3889                                         outSISIDXREG(SISPART2, 0x36, 0x0C);
3890                                         outSISIDXREG(SISPART2, 0x37, 0x22);
3891                                         outSISIDXREG(SISPART2, 0x38, 0x08);
3892                                         break;
3893                                 case 400:
3894                                 case 800:
3895                                         outSISIDXREG(SISPART2, 0x35, 0xEB);
3896                                         outSISIDXREG(SISPART2, 0x36, 0x15);
3897                                         outSISIDXREG(SISPART2, 0x37, 0x25);
3898                                         outSISIDXREG(SISPART2, 0x38, 0xF6);
3899                                         break;
3900                                 }
3901                         }
3902
3903                 } else if(ivideo.vbflags & TV_PAL) {
3904
3905                         andSISIDXREG(SISPART2, 0x3A, 0x1F);
3906
3907                         if (ivideo.vbflags & TV_SVIDEO) {
3908
3909                                 andSISIDXREG(SISPART2, 0x30, 0xDF);
3910
3911                         } else if (ivideo.vbflags & TV_AVIDEO) {
3912
3913                                 orSISIDXREG(SISPART2, 0x30, 0x20);
3914
3915                                 switch (ivideo.video_width) {
3916                                 case 640:
3917                                         outSISIDXREG(SISPART2, 0x35, 0xF1);
3918                                         outSISIDXREG(SISPART2, 0x36, 0xF7);
3919                                         outSISIDXREG(SISPART2, 0x37, 0x1F);
3920                                         outSISIDXREG(SISPART2, 0x38, 0x32);
3921                                         break;
3922                                 case 720:
3923                                         outSISIDXREG(SISPART2, 0x35, 0xF3);
3924                                         outSISIDXREG(SISPART2, 0x36, 0x00);
3925                                         outSISIDXREG(SISPART2, 0x37, 0x1D);
3926                                         outSISIDXREG(SISPART2, 0x38, 0x20);
3927                                         break;
3928                                 case 400:
3929                                 case 800:
3930                                         outSISIDXREG(SISPART2, 0x35, 0xFC);
3931                                         outSISIDXREG(SISPART2, 0x36, 0xFB);
3932                                         outSISIDXREG(SISPART2, 0x37, 0x14);
3933                                         outSISIDXREG(SISPART2, 0x38, 0x2A);
3934                                         break;
3935                                 }
3936                         }
3937                 }
3938
3939                 if ((filter >= 0) && (filter <= 7)) {
3940                         DPRINTK("FilterTable[%d]-%d: %02x %02x %02x %02x\n", filter_tb, filter,
3941                                 sis_TV_filter[filter_tb].filter[filter][0],
3942                                 sis_TV_filter[filter_tb].filter[filter][1],
3943                                 sis_TV_filter[filter_tb].filter[filter][2],
3944                                 sis_TV_filter[filter_tb].filter[filter][3]
3945                         );
3946                         outSISIDXREG(SISPART2, 0x35, (sis_TV_filter[filter_tb].filter[filter][0]));
3947                         outSISIDXREG(SISPART2, 0x36, (sis_TV_filter[filter_tb].filter[filter][1]));
3948                         outSISIDXREG(SISPART2, 0x37, (sis_TV_filter[filter_tb].filter[filter][2]));
3949                         outSISIDXREG(SISPART2, 0x38, (sis_TV_filter[filter_tb].filter[filter][3]));
3950                 }
3951           
3952         }
3953
3954 }
3955
3956 #ifndef MODULE
3957 int sisfb_setup(char *options)
3958 {
3959         char *this_opt;
3960         
3961 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
3962         sisfb_fontname[0] = '\0';
3963 #endif
3964
3965         ivideo.refresh_rate = 0;
3966         SiS_Pr.SiS_CustomT = CUT_NONE;
3967         SiS_Pr.UsePanelScaler = -1;
3968         SiS_Pr.LVDSHL = -1;
3969
3970         printk(KERN_DEBUG "sisfb: Options %s\n", options);
3971
3972         if (!options || !*options)
3973                 return 0;
3974
3975         while((this_opt = strsep(&options, ",")) != NULL) {
3976
3977                 if (!*this_opt) continue;
3978
3979                 if (!strnicmp(this_opt, "mode:", 5)) {
3980                         sisfb_search_mode(this_opt + 5, FALSE);
3981                 } else if (!strnicmp(this_opt, "vesa:", 5)) {
3982                         sisfb_search_vesamode(simple_strtoul(this_opt + 5, NULL, 0), FALSE);
3983 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
3984                 } else if (!strnicmp(this_opt, "inverse", 7)) {
3985                         sisfb_inverse = 1;
3986                         /* fb_invert_cmaps(); */
3987                 } else if (!strnicmp(this_opt, "font:", 5)) {
3988                         strncpy(sisfb_fontname, this_opt + 5, sizeof(sisfb_fontname) - 1);
3989                         sisfb_fontname[sizeof(sisfb_fontname) - 1] = '\0';
3990 #endif
3991                 } else if (!strnicmp(this_opt, "vrate:", 6)) {
3992                         ivideo.refresh_rate = simple_strtoul(this_opt + 6, NULL, 0);
3993                         sisfb_parm_rate = ivideo.refresh_rate;
3994                 } else if (!strnicmp(this_opt, "rate:", 5)) {
3995                         ivideo.refresh_rate = simple_strtoul(this_opt + 5, NULL, 0);
3996                         sisfb_parm_rate = ivideo.refresh_rate;
3997                 } else if (!strnicmp(this_opt, "off", 3)) {
3998                         sisfb_off = 1;
3999                 } else if (!strnicmp(this_opt, "crt1off", 7)) {
4000                         sisfb_crt1off = 1;
4001                 } else if (!strnicmp(this_opt, "filter:", 7)) {
4002                         filter = (int)simple_strtoul(this_opt + 7, NULL, 0);
4003                 } else if (!strnicmp(this_opt, "forcecrt2type:", 14)) {
4004                         sisfb_search_crt2type(this_opt + 14);
4005                 } else if (!strnicmp(this_opt, "forcecrt1:", 10)) {
4006                         sisfb_forcecrt1 = (int)simple_strtoul(this_opt + 10, NULL, 0);
4007                 } else if (!strnicmp(this_opt, "tvmode:",7)) {
4008                         sisfb_search_tvstd(this_opt + 7);
4009                 } else if (!strnicmp(this_opt, "tvstandard:",11)) {
4010                         sisfb_search_tvstd(this_opt + 7);
4011                 } else if (!strnicmp(this_opt, "mem:",4)) {
4012                         sisfb_mem = simple_strtoul(this_opt + 4, NULL, 0);
4013                 } else if (!strnicmp(this_opt, "queuemode:", 10)) {
4014                         sisfb_search_queuemode(this_opt + 10);
4015                 } else if (!strnicmp(this_opt, "pdc:", 4)) {
4016                         sisfb_pdc = simple_strtoul(this_opt + 4, NULL, 0);
4017                 } else if (!strnicmp(this_opt, "noaccel", 7)) {
4018                         sisfb_accel = 0;
4019                 } else if (!strnicmp(this_opt, "noypan", 6)) {
4020                         sisfb_ypan = 0;
4021                 } else if (!strnicmp(this_opt, "nomax", 5)) {
4022                         sisfb_max = 0;
4023                 } else if (!strnicmp(this_opt, "userom:", 7)) {
4024                         sisfb_userom = (int)simple_strtoul(this_opt + 7, NULL, 0);
4025                 } else if (!strnicmp(this_opt, "useoem:", 7)) {
4026                         sisfb_useoem = (int)simple_strtoul(this_opt + 7, NULL, 0);
4027                 } else if (!strnicmp(this_opt, "nocrt2rate", 10)) {
4028                         sisfb_nocrt2rate = 1;
4029                 } else if (!strnicmp(this_opt, "scalelcd:", 9)) {
4030                         unsigned long temp = 2;
4031                         temp = simple_strtoul(this_opt + 9, NULL, 0);
4032                         if((temp == 0) || (temp == 1)) {
4033                            SiS_Pr.UsePanelScaler = temp ^ 1;
4034                         }
4035                 } else if (!strnicmp(this_opt, "specialtiming:", 14)) {
4036                         sisfb_search_specialtiming(this_opt + 14);
4037                 } else if (!strnicmp(this_opt, "lvdshl:", 7)) {
4038                         unsigned long temp = 4;
4039                         temp = simple_strtoul(this_opt + 7, NULL, 0);
4040                         if((temp >= 0) && (temp <= 3)) {
4041                            SiS_Pr.LVDSHL = temp;
4042                         }
4043                 } else if(this_opt[0] >= '0' && this_opt[0] <= '9') {
4044                         sisfb_search_mode(this_opt, TRUE);
4045                 } else {
4046                         printk(KERN_INFO "sisfb: Invalid option %s\n", this_opt);
4047                 }
4048
4049                 /* TW: Acceleration only with MMIO mode */
4050                 if((sisfb_queuemode != -1) && (sisfb_queuemode != MMIO_CMD)) {
4051                         sisfb_accel = 0;
4052                 }
4053
4054         }
4055         return 0;
4056 }
4057 #endif
4058
4059 static char *sis_find_rom(void)
4060 {
4061 #if defined(__i386__)
4062         u32  segstart;
4063         unsigned char *rom_base, *rom;
4064         int  romptr;
4065         unsigned short pciid;
4066
4067         for(segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) {
4068
4069                 rom_base = (char *)ioremap(segstart, 0x10000);
4070                 if(!rom_base) continue;
4071
4072                 if((*rom_base != 0x55) || (*(rom_base + 1) != 0xaa)) {
4073                    iounmap(rom_base);
4074                    continue;
4075                 }
4076
4077                 romptr = (unsigned short)(*(rom_base + 0x18) | (*(rom_base + 0x19) << 8));
4078                 if(romptr > (0x10000 - 8)) {
4079                    iounmap(rom_base);
4080                    continue;
4081                 }
4082
4083                 rom = rom_base + romptr;
4084
4085                 if((*rom != 'P') || (*(rom + 1) != 'C') || (*(rom + 2) != 'I') || (*(rom + 3) != 'R')) {
4086                    iounmap(rom_base);
4087                    continue;
4088                 }
4089
4090                 pciid = (*(rom + 4)) | ((*(rom + 5)) << 8);
4091                 if(pciid != 0x1039) {
4092                    iounmap(rom_base);
4093                    continue;
4094                 }
4095
4096                 pciid = (*(rom + 6)) | ((*(rom + 7)) << 8);
4097                 if(pciid == ivideo.chip_id) return rom_base;
4098
4099                 iounmap(rom_base);
4100         }
4101 #endif
4102         return NULL;
4103 }
4104
4105
4106
4107 int __init sisfb_init(void)
4108 {
4109         struct pci_dev *pdev = NULL;
4110         struct board *b;
4111         int pdev_valid = 0;
4112         u32 reg32;
4113         u16 reg16;
4114         u8  reg;
4115
4116 #if 0
4117         /* for DOC VB */
4118         sisfb_set_reg4(0xcf8,0x800000e0);
4119         reg32 = sisfb_get_reg3(0xcfc);
4120         reg32 = reg32 | 0x00001000;
4121         sisfb_set_reg4(0xcfc,reg32);
4122         }
4123 #endif
4124
4125         if (sisfb_off)
4126                 return -ENXIO;
4127
4128         sisfb_registered = 0;
4129         sisfb_thismonitor.datavalid = FALSE;
4130
4131         memset(&sishw_ext, 0, sizeof(sishw_ext));
4132
4133 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
4134         memset(&sisfb_lastrates[0], 0, 128);
4135 #endif
4136         
4137 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)  
4138         memset(&sis_disp, 0, sizeof(sis_disp));
4139 #endif
4140
4141 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,74)
4142         pci_for_each_dev(pdev) {
4143 #else
4144         while((pdev = pci_find_device(PCI_VENDOR_ID_SI, PCI_ANY_ID, pdev))) {
4145 #endif
4146                 for (b = sisdev_list; b->vendor; b++) {
4147                         if ((b->vendor == pdev->vendor)
4148                             && (b->device == pdev->device)) {
4149                                 pdev_valid = 1;
4150 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) && defined(NEWFBDEV)
4151                                 sis_fb_info = framebuffer_alloc(0, &pdev->dev);
4152 #else
4153                                 sis_fb_info = kmalloc(sizeof(*sis_fb_info), GFP_KERNEL);
4154 #endif
4155                                 if(!sis_fb_info) return -ENOMEM;
4156 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)) || (!(defined(NEWFBDEV)))
4157                                 memset(sis_fb_info, 0, sizeof(*sis_fb_info));
4158 #endif
4159 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
4160                                 strcpy(sis_fb_info->modename, b->name);
4161 #else                           
4162                                 strcpy(myid, b->name);
4163 #endif                          
4164                                 ivideo.chip_id = pdev->device;
4165                                 pci_read_config_byte(pdev, PCI_REVISION_ID,
4166                                                      &ivideo.revision_id);
4167                                 pci_read_config_word(pdev, PCI_COMMAND, &reg16);
4168                                 sishw_ext.jChipRevision = ivideo.revision_id;
4169                                 sisvga_enabled = reg16 & 0x01;
4170                                 ivideo.pcibus = pdev->bus->number;
4171                                 ivideo.pcislot = PCI_SLOT(pdev->devfn);
4172                                 ivideo.pcifunc = PCI_FUNC(pdev->devfn);
4173                                 ivideo.subsysvendor = pdev->subsystem_vendor;
4174                                 ivideo.subsysdevice = pdev->subsystem_device;
4175                                 break;
4176                         }
4177                 }
4178
4179                 if (pdev_valid)
4180                         break;
4181         }
4182
4183         if (!pdev_valid)
4184                 return -ENODEV;
4185
4186         switch (ivideo.chip_id) {
4187 #ifdef CONFIG_FB_SIS_300
4188            case PCI_DEVICE_ID_SI_300:
4189                 ivideo.chip = SIS_300;
4190                 sisvga_engine = SIS_300_VGA;
4191                 sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_300 * 2;  /* New X driver uses 2 buffers */
4192                 sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_300;
4193                 break;
4194            case PCI_DEVICE_ID_SI_630_VGA:
4195                 {
4196                         ivideo.chip = SIS_630;
4197                         sisfb_set_reg4(0xCF8, 0x80000000);
4198                         reg32 = sisfb_get_reg3(0xCFC);
4199                         if(reg32 == 0x07301039) {
4200                                 ivideo.chip = SIS_730;
4201 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
4202                                 strcpy(sis_fb_info->modename, "SIS 730");
4203 #else
4204                                 strcpy(myid, "SIS 730");
4205 #endif
4206                         }
4207                         sisvga_engine = SIS_300_VGA;
4208                         sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_300 * 2;
4209                         sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_300;
4210                         break;
4211                 }
4212            case PCI_DEVICE_ID_SI_540_VGA:
4213                 ivideo.chip = SIS_540;
4214                 sisvga_engine = SIS_300_VGA;
4215                 sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_300 * 2;
4216                 sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_300;
4217                 break;
4218 #endif
4219 #ifdef CONFIG_FB_SIS_315
4220            case PCI_DEVICE_ID_SI_315H:
4221                 ivideo.chip = SIS_315H;
4222                 sisvga_engine = SIS_315_VGA;
4223                 sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_315 * 2;
4224                 sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_315;
4225                 break;
4226            case PCI_DEVICE_ID_SI_315:
4227                 ivideo.chip = SIS_315;
4228                 sisvga_engine = SIS_315_VGA;
4229                 sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_315 * 2;
4230                 sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_315;
4231                 break;
4232            case PCI_DEVICE_ID_SI_315PRO:
4233                 ivideo.chip = SIS_315PRO;
4234                 sisvga_engine = SIS_315_VGA;
4235                 sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_315 * 2;
4236                 sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_315;
4237                 break;
4238            case PCI_DEVICE_ID_SI_550_VGA:
4239                 ivideo.chip = SIS_550;
4240                 sisvga_engine = SIS_315_VGA;
4241                 sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_315 * 2;
4242                 sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_315;
4243                 break;
4244            case PCI_DEVICE_ID_SI_650_VGA:
4245                 {
4246                         ivideo.chip = SIS_650;  
4247                         sisfb_set_reg4(0xCF8, 0x80000000);
4248                         reg32 = sisfb_get_reg3(0xCFC);
4249                         if(reg32 == 0x07401039) {
4250                                 ivideo.chip = SIS_740;
4251 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
4252                                 strcpy(sis_fb_info->modename, "SIS 740");
4253 #else
4254                                 strcpy(myid, "SIS 740");
4255 #endif
4256                         }
4257                         sisvga_engine = SIS_315_VGA;
4258                         sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_315 * 2;
4259                         sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_315;
4260                         break;
4261                 }
4262            case PCI_DEVICE_ID_SI_330:
4263                 ivideo.chip = SIS_330;
4264                 sisvga_engine = SIS_315_VGA;
4265                 sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_315 * 2;
4266                 sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_315;
4267                 break;
4268            case PCI_DEVICE_ID_SI_660_VGA:
4269                 {
4270                         sisfb_set_reg4(0xCF8, 0x80000000);
4271                         reg32 = sisfb_get_reg3(0xCFC);
4272                         if(reg32 == 0x07601039) {
4273                                 ivideo.chip = SIS_760;
4274 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
4275                                 strcpy(sis_fb_info->modename, "SIS 760");
4276 #else
4277                                 strcpy(myid, "SIS 760");
4278 #endif
4279                         } else if(reg32 == 0x06601039) {
4280                                 ivideo.chip = SIS_660;
4281 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
4282                                 strcpy(sis_fb_info->modename, "SIS 660");
4283 #else
4284                                 strcpy(myid, "SIS 660");
4285 #endif
4286                         } else if(reg32 == 0x07411039) {
4287                                 ivideo.chip = SIS_741;
4288 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
4289                                 strcpy(sis_fb_info->modename, "SIS 741");
4290 #else
4291                                 strcpy(myid, "SIS 741");
4292 #endif
4293                         } else {
4294                                 ivideo.chip = SIS_661;
4295 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
4296                                 strcpy(sis_fb_info->modename, "SIS 661");
4297 #else
4298                                 strcpy(myid, "SIS 661");
4299 #endif
4300                         }
4301                         sisvga_engine = SIS_315_VGA;
4302                         sisfb_hwcursor_size = HW_CURSOR_AREA_SIZE_315 * 2;
4303                         sisfb_CRT2_write_enable = IND_SIS_CRT2_WRITE_ENABLE_315;
4304                         break;
4305                 }
4306 #endif
4307            default:
4308                 kfree(sis_fb_info);
4309                 return -ENODEV;
4310         }
4311         sishw_ext.jChipType = ivideo.chip;
4312
4313         /* for Debug */
4314         if( (sishw_ext.jChipType == SIS_315PRO) ||
4315             (sishw_ext.jChipType == SIS_315) )
4316                 sishw_ext.jChipType = SIS_315H;
4317
4318         ivideo.video_base = pci_resource_start(pdev, 0);
4319         ivideo.mmio_base = pci_resource_start(pdev, 1);
4320         sishw_ext.ulIOAddress = SiS_Pr.RelIO = pci_resource_start(pdev, 2) + 0x30;
4321         ivideo.vga_base = (unsigned short) sishw_ext.ulIOAddress;
4322
4323         sisfb_mmio_size =  pci_resource_len(pdev, 1);
4324
4325         if(!sisvga_enabled) {
4326            if(pci_enable_device(pdev)) {
4327               kfree(sis_fb_info);
4328               return -EIO;
4329            }
4330         }
4331
4332         SiS_Pr.SiS_Backup70xx = 0xff;
4333         SiS_Pr.SiS_CHOverScan = -1;
4334         SiS_Pr.SiS_ChSW = FALSE;
4335         SiS_Pr.SiS_UseLCDA = FALSE;
4336         SiS_Pr.HaveEMI = FALSE;
4337         SiS_Pr.HaveEMILCD = FALSE;
4338         SiS_Pr.OverruleEMI = FALSE;
4339         SiS_Pr.SiS_SensibleSR11 = FALSE;
4340         SiS_Pr.SiS_MyCR63 = 0x63;
4341         if(ivideo.chip >= SIS_661) {
4342            SiS_Pr.SiS_SensibleSR11 = TRUE;
4343            SiS_Pr.SiS_MyCR63 = 0x53;
4344         }
4345         SiSRegInit(&SiS_Pr, sishw_ext.ulIOAddress);
4346
4347 #ifdef CONFIG_FB_SIS_300
4348         /* TW: Find PCI systems for Chrontel/GPIO communication setup */
4349         if(ivideo.chip == SIS_630) {
4350            int i=0;
4351            do {
4352               if(mychswtable[i].subsysVendor == ivideo.subsysvendor &&
4353                  mychswtable[i].subsysCard   == ivideo.subsysdevice) {
4354                  SiS_Pr.SiS_ChSW = TRUE;
4355                  printk(KERN_DEBUG "sisfb: Identified [%s %s] requiring Chrontel/GPIO setup\n",
4356                         mychswtable[i].vendorName, mychswtable[i].cardName);
4357                  break;
4358               }
4359               i++;
4360            } while(mychswtable[i].subsysVendor != 0);
4361         }
4362 #endif
4363
4364         outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
4365
4366 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)          
4367 #ifdef MODULE
4368         inSISIDXREG(SISCR,0x34,reg);
4369         if((reg & 0x80) && (reg != 0xff)) {
4370            if((sisbios_mode[sisfb_mode_idx].mode_no) != 0xFF) {
4371               printk(KERN_INFO "sisfb: Cannot initialize display mode, X server is active\n");
4372               kfree(sis_fb_info);
4373               return -EBUSY;
4374            }
4375         }
4376 #endif  
4377 #endif
4378
4379         if (sisvga_engine == SIS_315_VGA) {
4380                 switch (ivideo.chip) {
4381                    case SIS_315H:
4382                    case SIS_315:
4383                    case SIS_330:
4384                         sishw_ext.bIntegratedMMEnabled = TRUE;
4385                         break;
4386                    case SIS_550:
4387                    case SIS_650:
4388                    case SIS_740:
4389                    case SIS_661:
4390                    case SIS_741:
4391                    case SIS_660:
4392                    case SIS_760:
4393                         sishw_ext.bIntegratedMMEnabled = TRUE;
4394                         break;
4395                    default:
4396                         break;
4397                 }
4398         } else if (sisvga_engine == SIS_300_VGA) {
4399                 if (ivideo.chip == SIS_300) {
4400                         sishw_ext.bIntegratedMMEnabled = TRUE;
4401                 } else {
4402                         inSISIDXREG(SISSR, IND_SIS_SCRATCH_REG_1A, reg);
4403                         if (reg & SIS_SCRATCH_REG_1A_MASK)
4404                                 sishw_ext.bIntegratedMMEnabled = TRUE;
4405                         else
4406                                 sishw_ext.bIntegratedMMEnabled = FALSE;
4407                 }
4408         }
4409
4410         if(sisfb_userom) {
4411             sishw_ext.pjVirtualRomBase = sis_find_rom();
4412             if(sishw_ext.pjVirtualRomBase) {
4413                 printk(KERN_INFO "sisfb: Video ROM found and mapped to %p\n",
4414                         sishw_ext.pjVirtualRomBase);
4415                 sishw_ext.UseROM = TRUE;
4416             } else {
4417                 sishw_ext.UseROM = FALSE;
4418                 printk(KERN_INFO "sisfb: Video ROM not found\n");
4419             }
4420         } else {
4421             sishw_ext.pjVirtualRomBase = NULL;
4422             sishw_ext.UseROM = FALSE;
4423             printk(KERN_INFO "sisfb: Video ROM usage disabled\n");
4424         }
4425         sishw_ext.bSkipDramSizing = 0;
4426         sishw_ext.pQueryVGAConfigSpace = &sisfb_query_VGA_config_space;
4427         sishw_ext.pQueryNorthBridgeSpace = &sisfb_query_north_bridge_space;
4428
4429         /* Find systems for special custom timing */
4430         if(SiS_Pr.SiS_CustomT == CUT_NONE) {
4431            int i=0, j;
4432            unsigned char *biosver = NULL;
4433            unsigned char *biosdate = NULL;
4434            BOOLEAN footprint;
4435            unsigned long chksum = 0;
4436
4437            if(sishw_ext.UseROM) {
4438               biosver = sishw_ext.pjVirtualRomBase + 0x06;
4439               biosdate = sishw_ext.pjVirtualRomBase + 0x2c;
4440               for(i=0; i<32768; i++) chksum += sishw_ext.pjVirtualRomBase[i];
4441            }
4442
4443            i=0;
4444            do {
4445               if( (mycustomttable[i].chipID == ivideo.chip) &&
4446                   ((!strlen(mycustomttable[i].biosversion)) ||
4447                    (sishw_ext.UseROM &&
4448                    (!strncmp(mycustomttable[i].biosversion, biosver, strlen(mycustomttable[i].biosversion))))) &&
4449                   ((!strlen(mycustomttable[i].biosdate)) ||
4450                    (sishw_ext.UseROM &&
4451                    (!strncmp(mycustomttable[i].biosdate, biosdate, strlen(mycustomttable[i].biosdate))))) &&
4452                   ((!mycustomttable[i].bioschksum) ||
4453                    (sishw_ext.UseROM &&
4454                    (mycustomttable[i].bioschksum == chksum)))   &&
4455                   (mycustomttable[i].pcisubsysvendor == ivideo.subsysvendor) &&
4456                   (mycustomttable[i].pcisubsyscard == ivideo.subsysdevice) ) {
4457                  footprint = TRUE;
4458                  for(j=0; j<5; j++) {
4459                     if(mycustomttable[i].biosFootprintAddr[j]) {
4460                        if(sishw_ext.UseROM) {
4461                           if(sishw_ext.pjVirtualRomBase[mycustomttable[i].biosFootprintAddr[j]] !=
4462                                 mycustomttable[i].biosFootprintData[j])
4463                           footprint = FALSE;
4464                        } else footprint = FALSE;
4465                     }
4466                  }
4467                  if(footprint) {
4468                     SiS_Pr.SiS_CustomT = mycustomttable[i].SpecialID;
4469                     printk(KERN_DEBUG "sisfb: Identified [%s %s], special timing applies\n",
4470                         mycustomttable[i].vendorName,
4471                         mycustomttable[i].cardName);
4472                     printk(KERN_DEBUG "sisfb: [specialtiming parameter name: %s]\n",
4473                         mycustomttable[i].optionName);
4474                     break;
4475                  }
4476               }
4477               i++;
4478            } while(mycustomttable[i].chipID);
4479         }
4480
4481 #ifdef CONFIG_FB_SIS_300
4482         /* Mode numbers for 1280x768 are different for 300 and 315 series */
4483         if(sisvga_engine == SIS_300_VGA) {
4484                 sisbios_mode[MODEINDEX_1280x768].mode_no = 0x55;
4485                 sisbios_mode[MODEINDEX_1280x768+1].mode_no = 0x5a;
4486                 sisbios_mode[MODEINDEX_1280x768+2].mode_no = 0x5b;
4487                 sisbios_mode[MODEINDEX_1280x768+3].mode_no = 0x5b;
4488         }
4489 #endif
4490
4491         sishw_ext.pSR = vmalloc(sizeof(SIS_DSReg) * SR_BUFFER_SIZE);
4492         if (sishw_ext.pSR == NULL) {
4493                 printk(KERN_ERR "sisfb: Fatal error: Allocating SRReg space failed.\n");
4494                 kfree(sis_fb_info);
4495                 return -ENODEV;
4496         }
4497         sishw_ext.pSR[0].jIdx = sishw_ext.pSR[0].jVal = 0xFF;
4498
4499         sishw_ext.pCR = vmalloc(sizeof(SIS_DSReg) * CR_BUFFER_SIZE);
4500         if (sishw_ext.pCR == NULL) {
4501                 vfree(sishw_ext.pSR);
4502                 printk(KERN_ERR "sisfb: Fatal error: Allocating CRReg space failed.\n");
4503                 kfree(sis_fb_info);
4504                 return -ENODEV;
4505         }
4506         sishw_ext.pCR[0].jIdx = sishw_ext.pCR[0].jVal = 0xFF;
4507
4508 #ifdef CONFIG_FB_SIS_300
4509         if(sisvga_engine == SIS_300_VGA) {
4510                 if(!sisvga_enabled) {
4511                         /* Mapping Max FB Size for 300 Init */
4512                         sishw_ext.pjVideoMemoryAddress = ioremap(ivideo.video_base, 0x4000000);
4513                         if((sisfb_mode_idx < 0) || ((sisbios_mode[sisfb_mode_idx].mode_no) != 0xFF)) {
4514                                 outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
4515                         }
4516                 }
4517                 if(sisfb_get_dram_size_300()) {
4518                         vfree(sishw_ext.pSR);
4519                         vfree(sishw_ext.pCR);
4520                         printk(KERN_ERR "sisfb: Fatal error: Unable to determine RAM size\n");
4521                         kfree(sis_fb_info);
4522                         return -ENODEV;
4523                 }
4524         }
4525 #endif
4526
4527 #ifdef CONFIG_FB_SIS_315
4528         if (sisvga_engine == SIS_315_VGA) {
4529                 if (!sisvga_enabled) {
4530                         /* Mapping Max FB Size for 315 Init */
4531                         sishw_ext.pjVideoMemoryAddress = ioremap(ivideo.video_base, 0x8000000);
4532                         if((sisfb_mode_idx < 0) || ((sisbios_mode[sisfb_mode_idx].mode_no) != 0xFF)) {
4533                                 outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
4534                                 sishw_ext.bSkipDramSizing = TRUE;
4535                                 sishw_ext.pSR[0].jIdx = 0x13;
4536                                 sishw_ext.pSR[1].jIdx = 0x14;
4537                                 sishw_ext.pSR[2].jIdx = 0xFF;
4538                                 inSISIDXREG(SISSR, 0x13, sishw_ext.pSR[0].jVal);
4539                                 inSISIDXREG(SISSR, 0x14, sishw_ext.pSR[1].jVal);
4540                                 sishw_ext.pSR[2].jVal = 0xFF;
4541                         }
4542                 }
4543                 if(sisfb_get_dram_size_315()) {
4544                         vfree(sishw_ext.pSR);
4545                         vfree(sishw_ext.pCR);
4546                         printk(KERN_INFO "sisfb: Fatal error: Unable to determine RAM size.\n");
4547                         kfree(sis_fb_info);
4548                         return -ENODEV;
4549                 }
4550         }
4551 #endif
4552
4553         if((sisfb_mode_idx < 0) || ((sisbios_mode[sisfb_mode_idx].mode_no) != 0xFF)) { 
4554
4555                 /* Enable PCI_LINEAR_ADDRESSING and MMIO_ENABLE  */
4556                 orSISIDXREG(SISSR, IND_SIS_PCI_ADDRESS_SET, (SIS_PCI_ADDR_ENABLE | SIS_MEM_MAP_IO_ENABLE));
4557
4558                 /* Enable 2D accelerator engine */
4559                 orSISIDXREG(SISSR, IND_SIS_MODULE_ENABLE, SIS_ENABLE_2D);
4560
4561         }
4562
4563         sishw_ext.ulVideoMemorySize = ivideo.video_size;
4564
4565         if(sisvga_engine == SIS_300_VGA) sisfb_pdc &= 0x3c;
4566         if(sisfb_pdc) {
4567             SiS_Pr.PDC = sisfb_pdc;
4568         } else {
4569             SiS_Pr.PDC = 0;
4570         }
4571
4572         if(!request_mem_region(ivideo.video_base, ivideo.video_size, "sisfb FB")) {
4573                 printk(KERN_ERR "sisfb: Fatal error: Unable to reserve frame buffer memory\n");
4574                 printk(KERN_ERR "sisfb: Is there another framebuffer driver active?\n");
4575                 vfree(sishw_ext.pSR);
4576                 vfree(sishw_ext.pCR);
4577                 kfree(sis_fb_info);
4578                 return -ENODEV;
4579         }
4580
4581         if(!request_mem_region(ivideo.mmio_base, sisfb_mmio_size, "sisfb MMIO")) {
4582                 printk(KERN_ERR "sisfb: Fatal error: Unable to reserve MMIO region\n");
4583                 release_mem_region(ivideo.video_base, ivideo.video_size);
4584                 vfree(sishw_ext.pSR);
4585                 vfree(sishw_ext.pCR);
4586                 kfree(sis_fb_info);
4587                 return -ENODEV;
4588         }
4589
4590         ivideo.video_vbase = sishw_ext.pjVideoMemoryAddress = ioremap(ivideo.video_base, ivideo.video_size);
4591         if(!ivideo.video_vbase) {
4592                 printk(KERN_ERR "sisfb: Fatal error: Unable to map frame buffer memory\n");
4593                 release_mem_region(ivideo.video_base, ivideo.video_size);
4594                 release_mem_region(ivideo.mmio_base, sisfb_mmio_size);
4595                 vfree(sishw_ext.pSR);
4596                 vfree(sishw_ext.pCR);
4597                 kfree(sis_fb_info);
4598                 return -ENODEV;
4599         }
4600
4601         ivideo.mmio_vbase = ioremap(ivideo.mmio_base, sisfb_mmio_size);
4602         if(!ivideo.mmio_vbase) {
4603                 printk(KERN_ERR "sisfb: Fatal error: Unable to map MMIO region\n");
4604                 iounmap(ivideo.video_vbase);
4605                 release_mem_region(ivideo.video_base, ivideo.video_size);
4606                 release_mem_region(ivideo.mmio_base, sisfb_mmio_size);
4607                 vfree(sishw_ext.pSR);
4608                 vfree(sishw_ext.pCR);
4609                 kfree(sis_fb_info);
4610                 return -ENODEV;
4611         }
4612
4613         printk(KERN_INFO "sisfb: Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",
4614                 ivideo.video_base, ivideo.video_vbase, ivideo.video_size / 1024);
4615
4616         printk(KERN_INFO "sisfb: MMIO at 0x%lx, mapped to 0x%p, size %ldk\n",
4617                 ivideo.mmio_base, ivideo.mmio_vbase, sisfb_mmio_size / 1024);
4618
4619         if(sisfb_heap_init()) {
4620                 printk(KERN_WARNING "sisfb: Failed to initialize offscreen memory heap\n");
4621         }
4622
4623         ivideo.mtrr = (unsigned int) 0;
4624
4625         ivideo.vbflags = 0;
4626
4627         if((sisfb_mode_idx < 0) || ((sisbios_mode[sisfb_mode_idx].mode_no) != 0xFF)) {
4628
4629                 sishw_ext.ujVBChipID = VB_CHIP_UNKNOWN;
4630                 sishw_ext.Is301BDH = FALSE;
4631                 sishw_ext.usExternalChip = 0;
4632
4633                 sisfb_sense_crt1();
4634
4635                 sisfb_get_VB_type();
4636
4637                 if(ivideo.vbflags & VB_VIDEOBRIDGE) {
4638                         sisfb_detect_VB_connect();
4639                 }
4640
4641                 ivideo.currentvbflags = ivideo.vbflags & VB_VIDEOBRIDGE;
4642
4643                 if(ivideo.vbflags & VB_VIDEOBRIDGE) {
4644                    if(sisfb_crt2type != -1) {
4645                       if((sisfb_crt2type == CRT2_LCD) && (ivideo.vbflags & CRT2_LCD)) {
4646                          ivideo.currentvbflags |= CRT2_LCD;
4647                       } else if(sisfb_crt2type != CRT2_LCD) {
4648                          ivideo.currentvbflags |= sisfb_crt2type;
4649                       }
4650                    } else {
4651                       /* Chrontel 700x TV detection often unreliable, therefore use a
4652                        * different default order on such machines
4653                        */
4654                       if((sisvga_engine == SIS_300_VGA) && (ivideo.vbflags & VB_CHRONTEL)) {
4655                          if(ivideo.vbflags & CRT2_LCD)      ivideo.currentvbflags |= CRT2_LCD;
4656                          else if(ivideo.vbflags & CRT2_TV)  ivideo.currentvbflags |= CRT2_TV;
4657                          else if(ivideo.vbflags & CRT2_VGA) ivideo.currentvbflags |= CRT2_VGA;
4658                       } else {
4659                          if(ivideo.vbflags & CRT2_TV)       ivideo.currentvbflags |= CRT2_TV;
4660                          else if(ivideo.vbflags & CRT2_LCD) ivideo.currentvbflags |= CRT2_LCD;
4661                          else if(ivideo.vbflags & CRT2_VGA) ivideo.currentvbflags |= CRT2_VGA;
4662                       }
4663                    }
4664                 }
4665
4666                 if(ivideo.vbflags & CRT2_LCD) {
4667                    inSISIDXREG(SISCR, IND_SIS_LCD_PANEL, reg);
4668                    reg &= 0x0f;
4669                    if(sisvga_engine == SIS_300_VGA) {
4670                       sishw_ext.ulCRT2LCDType = sis300paneltype[reg];
4671                    } else {
4672                       sishw_ext.ulCRT2LCDType = sis310paneltype[reg];
4673                    }
4674                 }
4675                 
4676                 sisfb_detectedpdc = 0;
4677
4678 #ifdef CONFIG_FB_SIS_300
4679                 /* Save the current PanelDelayCompensation if the LCD is currently used */
4680                 if(sisvga_engine == SIS_300_VGA) {
4681                    if(ivideo.vbflags & (VB_LVDS | VB_30xBDH)) {
4682                        int tmp;
4683                        inSISIDXREG(SISCR,0x30,tmp);
4684                        if(tmp & 0x20) {
4685                           /* Currently on LCD? If yes, read current pdc */
4686                           inSISIDXREG(SISPART1,0x13,sisfb_detectedpdc);
4687                           sisfb_detectedpdc &= 0x3c;
4688                           if(SiS_Pr.PDC == 0) {
4689                              /* Let option override detection */
4690                              SiS_Pr.PDC = sisfb_detectedpdc;
4691                           }
4692                           printk(KERN_INFO
4693                                  "sisfb: Detected LCD PanelDelayCompensation %d\n",
4694                                  sisfb_detectedpdc);
4695                        }
4696                        if((SiS_Pr.PDC) && (SiS_Pr.PDC != sisfb_detectedpdc)) {
4697                           printk(KERN_INFO
4698                                  "sisfb: Using LCD PanelDelayCompensation %d\n",
4699                                  SiS_Pr.PDC);
4700                        }
4701                    }
4702                 }
4703 #endif
4704
4705                 sisfb_detectedlcda = 0xff;
4706
4707 #ifdef CONFIG_FB_SIS_315
4708
4709                 if(sisvga_engine == SIS_315_VGA) {
4710                    /* Save PDC */
4711                    if(ivideo.vbflags & (VB_301LV | VB_302LV | VB_302ELV)) {
4712                       int tmp;
4713                       inSISIDXREG(SISCR,0x30,tmp);
4714                       if(tmp & 0x20) {
4715                          /* Currently on LCD? If yes, read current pdc */
4716                          inSISIDXREG(SISPART1,0x2D,sisfb_detectedpdc);
4717                          if(SiS_Pr.PDC == 0) {
4718                             /* Let option override detection */
4719                             SiS_Pr.PDC = sisfb_detectedpdc;
4720                          }
4721                          printk(KERN_INFO
4722                                 "sisfb: Detected LCD PanelDelayCompensation %d\n",
4723                                  sisfb_detectedpdc);
4724                       }
4725                       if((SiS_Pr.PDC) && (SiS_Pr.PDC != sisfb_detectedpdc)) {
4726                          printk(KERN_INFO
4727                                  "sisfb: Using LCD PanelDelayCompensation %d\n",
4728                                  SiS_Pr.PDC);
4729                       }
4730                       /* Save EMI */
4731                       if(ivideo.vbflags & (VB_302LV | VB_302ELV)) {
4732                          inSISIDXREG(SISPART4,0x30,SiS_Pr.EMI_30);
4733                          inSISIDXREG(SISPART4,0x31,SiS_Pr.EMI_31);
4734                          inSISIDXREG(SISPART4,0x32,SiS_Pr.EMI_32);
4735                          inSISIDXREG(SISPART4,0x33,SiS_Pr.EMI_33);
4736                          SiS_Pr.HaveEMI = TRUE;
4737                          if(tmp & 0x20) SiS_Pr.HaveEMILCD = TRUE;
4738                       }
4739                    }
4740
4741                    /* Try to find about LCDA */
4742                    if(ivideo.vbflags & (VB_301C | VB_302B | VB_301LV | VB_302LV | VB_302ELV)) {
4743                       int tmp;
4744                       inSISIDXREG(SISCR,0x34,tmp);
4745                       if((tmp <= 0x13) || (tmp == 0xff)) {
4746                          /* Currently on LCDA? (Some BIOSes leave CR38) */
4747                          inSISIDXREG(SISCR,0x38,tmp);
4748                          if((tmp & 0x03) == 0x03)  SiS_Pr.SiS_UseLCDA = TRUE;
4749                          else {
4750                             /* Currently on LCDA? (Some newer BIOSes set D0 in CR35) */
4751                             inSISIDXREG(SISCR,0x35,tmp);
4752                             if(tmp & 0x01) SiS_Pr.SiS_UseLCDA = TRUE;
4753                             else {
4754                                /* Currently on LCD? If so, we can find out
4755                                 * by peeking the mode register
4756                                 */
4757                                inSISIDXREG(SISCR,0x30,tmp);
4758                                if(tmp & 0x20) {
4759                                   inSISIDXREG(SISPART1,0x13,tmp);
4760                                   if(tmp & 0x04) SiS_Pr.SiS_UseLCDA = TRUE;
4761                                }
4762                             }
4763                          }
4764                       }
4765                       if(SiS_Pr.SiS_UseLCDA) {
4766                          sisfb_detectedlcda = 0x03;
4767                          printk(KERN_DEBUG
4768                                 "sisfb: Bridge uses LCDA for low resolution and text modes\n");
4769                       }
4770                   }
4771                 }
4772 #endif
4773
4774                 if (!sisfb_crt1off) {
4775                         sisfb_handle_ddc(&sisfb_thismonitor, 0);
4776                 } else {
4777                         if ((ivideo.vbflags & (VB_301|VB_301B|VB_301C|VB_302B)) &&
4778                             (ivideo.vbflags & (CRT2_VGA | CRT2_LCD))) {
4779                                 sisfb_handle_ddc(&sisfb_thismonitor, 1);
4780                         }
4781                 }
4782
4783                 if (sisfb_mode_idx >= 0)
4784                         sisfb_mode_idx = sisfb_validate_mode(sisfb_mode_idx, ivideo.currentvbflags);
4785
4786                 if (sisfb_mode_idx < 0) {
4787                         switch (ivideo.currentvbflags & VB_DISPTYPE_DISP2) {
4788                            case CRT2_LCD:
4789                                 sisfb_mode_idx = DEFAULT_LCDMODE;
4790                                 break;
4791                            case CRT2_TV:
4792                                 sisfb_mode_idx = DEFAULT_TVMODE;
4793                                 break;
4794                            default:
4795                                 sisfb_mode_idx = DEFAULT_MODE;
4796                                 break;
4797                         }
4798                 }
4799
4800                 sisfb_mode_no = sisbios_mode[sisfb_mode_idx].mode_no;
4801
4802                 if (ivideo.refresh_rate != 0)
4803                         sisfb_search_refresh_rate(ivideo.refresh_rate, sisfb_mode_idx);
4804
4805                 if (sisfb_rate_idx == 0) {
4806                         sisfb_rate_idx = sisbios_mode[sisfb_mode_idx].rate_idx;
4807                         ivideo.refresh_rate = 60;
4808                 }
4809
4810                 if (sisfb_thismonitor.datavalid) {
4811                         if(!sisfb_verify_rate(&sisfb_thismonitor, sisfb_mode_idx,
4812                                               sisfb_rate_idx, ivideo.refresh_rate)) {
4813                                 printk(KERN_INFO "sisfb: WARNING: Refresh rate exceeds monitor specs!\n");
4814                         }
4815                 }
4816
4817                 ivideo.video_bpp = sisbios_mode[sisfb_mode_idx].bpp;
4818                 ivideo.video_vwidth = ivideo.video_width = sisbios_mode[sisfb_mode_idx].xres;
4819                 ivideo.video_vheight = ivideo.video_height = sisbios_mode[sisfb_mode_idx].yres;
4820                 ivideo.org_x = ivideo.org_y = 0;
4821                 ivideo.video_linelength = ivideo.video_width * (ivideo.video_bpp >> 3);
4822
4823                 sisfb_set_vparms();
4824                 
4825 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)  
4826
4827                 /* ---------------- For 2.4: Now switch the mode ------------------ */          
4828                 
4829                 printk(KERN_INFO "sisfb: Mode is %dx%dx%d (%dHz)\n",
4830                         ivideo.video_width, ivideo.video_height, ivideo.video_bpp,
4831                         ivideo.refresh_rate);
4832
4833                 sisfb_pre_setmode();
4834
4835                 if (SiSSetMode(&SiS_Pr, &sishw_ext, sisfb_mode_no) == 0) {
4836                         printk(KERN_ERR "sisfb: Fatal error: Setting mode[0x%x] failed\n",
4837                                 sisfb_mode_no);
4838                         vfree(sishw_ext.pSR);
4839                         vfree(sishw_ext.pCR);
4840                         release_mem_region(ivideo.video_base, ivideo.video_size);
4841                         release_mem_region(ivideo.mmio_base, sisfb_mmio_size);
4842                         kfree(sis_fb_info);
4843                         return -EINVAL;
4844                 }
4845
4846                 outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
4847
4848                 sisfb_post_setmode();
4849
4850                 ivideo.accel = 0;
4851                 if(sisfb_accel) {
4852                    ivideo.accel = -1;
4853                    default_var.accel_flags |= FB_ACCELF_TEXT;
4854                    sisfb_initaccel();
4855                 }
4856
4857                 /* Maximize regardless of sisfb_max at startup */
4858                 default_var.yres_virtual = 32767;
4859                 sisfb_crtc_to_var(&default_var);
4860                 
4861                 sis_fb_info->node = -1;
4862                 sis_fb_info->flags = FBINFO_FLAG_DEFAULT;
4863                 sis_fb_info->blank = &sisfb_blank;
4864                 sis_fb_info->fbops = &sisfb_ops;
4865                 sis_fb_info->switch_con = &sisfb_switch;
4866                 sis_fb_info->updatevar = &sisfb_update_var;
4867                 sis_fb_info->changevar = NULL;
4868                 sis_fb_info->disp = &sis_disp;
4869                 strcpy(sis_fb_info->fontname, sisfb_fontname);
4870
4871                 sisfb_set_disp(-1, &default_var, sis_fb_info);
4872
4873 #else           /* --------- For 2.5: Setup a somewhat sane default var ------------ */
4874
4875                 printk(KERN_INFO "sisfb: Default mode is %dx%dx%d (%dHz)\n",
4876                         ivideo.video_width, ivideo.video_height, ivideo.video_bpp,
4877                         ivideo.refresh_rate);
4878
4879                 default_var.xres = default_var.xres_virtual = ivideo.video_width;
4880                 default_var.yres = default_var.yres_virtual = ivideo.video_height;
4881                 default_var.bits_per_pixel = ivideo.video_bpp;
4882
4883                 sisfb_bpp_to_var(&default_var);
4884                 
4885                 default_var.pixclock = (u32) (1000000000 /
4886                                 sisfb_mode_rate_to_dclock(&SiS_Pr, &sishw_ext,
4887                                                 sisfb_mode_no, sisfb_rate_idx));
4888                                                 
4889                 if(sisfb_mode_rate_to_ddata(&SiS_Pr, &sishw_ext,
4890                          sisfb_mode_no, sisfb_rate_idx,
4891                          &default_var.left_margin, &default_var.right_margin, 
4892                          &default_var.upper_margin, &default_var.lower_margin,
4893                          &default_var.hsync_len, &default_var.vsync_len,
4894                          &default_var.sync, &default_var.vmode)) {
4895                    if((default_var.vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
4896                       default_var.pixclock <<= 1;
4897                    }
4898                 }
4899
4900                 ivideo.accel = 0;
4901                 if(sisfb_accel) {
4902                    ivideo.accel = -1;
4903                    default_var.accel_flags |= FB_ACCELF_TEXT;
4904                    sisfb_initaccel();
4905                 }
4906
4907                 if(sisfb_ypan) {
4908                    /* Maximize regardless of sisfb_max at startup */
4909                    default_var.yres_virtual =
4910                                 ivideo.heapstart / (default_var.xres * (default_var.bits_per_pixel >> 3));
4911                    if(default_var.yres_virtual > 32767) default_var.yres_virtual = 32767;
4912                    if(default_var.yres_virtual <= default_var.yres) {
4913                       default_var.yres_virtual = default_var.yres;
4914                    }
4915                 }
4916
4917                 sis_fb_info->flags = FBINFO_FLAG_DEFAULT;
4918                 sis_fb_info->var = default_var;
4919                 sis_fb_info->fix = sisfb_fix;
4920                 sis_fb_info->par = &ivideo;
4921                 sis_fb_info->screen_base = ivideo.video_vbase;
4922                 sis_fb_info->fbops = &sisfb_ops;
4923 #ifdef NEWFBDEV
4924                 sis_fb_info->class_dev.dev = &pdev->dev;
4925 #endif
4926                 sisfb_get_fix(&sis_fb_info->fix, -1, sis_fb_info);
4927                 sis_fb_info->pseudo_palette = pseudo_palette;
4928                 
4929                 fb_alloc_cmap(&sis_fb_info->cmap, 256 , 0);
4930 #endif
4931
4932                 printk(KERN_INFO "sisfb: Initial vbflags 0x%lx\n", ivideo.vbflags);
4933
4934 #ifdef CONFIG_MTRR
4935                 ivideo.mtrr = mtrr_add((unsigned int) ivideo.video_base,
4936                                 (unsigned int) ivideo.video_size,
4937                                 MTRR_TYPE_WRCOMB, 1);
4938                 if(ivideo.mtrr) {
4939                         printk(KERN_INFO "sisfb: Added MTRRs\n");
4940                 }
4941
4942 #endif
4943
4944 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
4945                 vc_resize_con(1, 1, 0);
4946 #endif
4947
4948                 if(register_framebuffer(sis_fb_info) < 0) {
4949                         vfree(sishw_ext.pSR);
4950                         vfree(sishw_ext.pCR);
4951                         release_mem_region(ivideo.video_base, ivideo.video_size);
4952                         release_mem_region(ivideo.mmio_base, sisfb_mmio_size);
4953                         printk(KERN_ERR "sisfb: Fatal error: Failed to register framebuffer\n");
4954                         kfree(sis_fb_info);
4955                         return -EINVAL;
4956                 }
4957
4958                 sisfb_registered = 1;                   
4959
4960                 printk(KERN_DEBUG "sisfb: Installed SISFB_GET_INFO ioctl (%x)\n", SISFB_GET_INFO);
4961                 printk(KERN_DEBUG "sisfb: Installed SISFB_GET_VBRSTATUS ioctl (%x)\n", SISFB_GET_VBRSTATUS);
4962                 
4963                 printk(KERN_INFO "sisfb: 2D acceleration is %s, scrolling mode %s\n",
4964                      sisfb_accel ? "enabled" : "disabled",
4965                      sisfb_ypan  ? (sisfb_max ? "ypan (auto-max)" : "ypan (no auto-max)") : "redraw");
4966
4967 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
4968                 printk(KERN_INFO "fb%d: %s frame buffer device, Version %d.%d.%02d\n",
4969                         GET_FB_IDX(sis_fb_info->node), sis_fb_info->modename, VER_MAJOR, VER_MINOR,
4970                         VER_LEVEL);                  
4971 #else
4972                 printk(KERN_INFO "fb%d: %s frame buffer device, Version %d.%d.%02d\n",
4973                         sis_fb_info->node, myid, VER_MAJOR, VER_MINOR, VER_LEVEL);
4974 #endif
4975
4976                 printk(KERN_INFO "sisfb: (C) 2001-2004 Thomas Winischhofer.\n");
4977
4978         }       /* if mode = "none" */
4979         return 0;
4980 }
4981
4982
4983 #ifdef MODULE
4984
4985 static char         *mode = NULL;
4986 static int          vesa = -1;
4987 static unsigned int rate = 0;
4988 static unsigned int crt1off = 1;
4989 static unsigned int mem = 0;
4990 static char         *forcecrt2type = NULL;
4991 static int          forcecrt1 = -1;
4992 static char         *queuemode = NULL;
4993 static int          pdc = 0;
4994 static int          noaccel = -1;
4995 static int          noypan  = -1;
4996 static int          nomax = -1;
4997 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
4998 static int          inverse = 0;
4999 #endif
5000 static int          userom = 1;
5001 static int          useoem = -1;
5002 static char         *tvstandard = NULL;
5003 static int          nocrt2rate = 0;
5004 static int          scalelcd = -1;
5005 static char         *specialtiming = NULL;
5006 static int          lvdshl = -1;
5007
5008 MODULE_DESCRIPTION("SiS 300/540/630/730/315/550/650/651/661/740/741/330/760 framebuffer driver");
5009 MODULE_LICENSE("GPL");
5010 MODULE_AUTHOR("Thomas Winischhofer <thomas@winischhofer.net>; SiS; Various others");
5011
5012 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
5013 MODULE_PARM(mode, "s");
5014 MODULE_PARM_DESC(mode,
5015        "\nSelects the desired display mode in the format [X]x[Y]x[Depth], eg.\n"
5016          "1024x768x16. Other formats supported include XxY-Depth and\n"
5017          "XxY-Depth@Rate. If the parameter is only one (decimal or hexadecimal)\n"
5018          "number, it will be interpreted as a VESA mode number. (default: none if\n"
5019          "sisfb is a module; this leaves the console untouched and the driver will\n"
5020          "only do the video memory management for eg. DRM/DRI; 800x600x8 if sisfb\n"
5021          "is in the kernel)");
5022 #endif
5023 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)  
5024 MODULE_PARM(mode, "s");
5025 MODULE_PARM_DESC(mode,
5026        "\nSelects the desired default display mode in the format XxYxDepth,\n"
5027          "eg. 1024x768x16. Other formats supported include XxY-Depth and\n"
5028          "XxY-Depth@Rate. If the parameter is only one (decimal or hexadecimal)\n"
5029          "number, it will be interpreted as a VESA mode number. (default: 800x600x8)");
5030 #endif
5031
5032 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
5033 MODULE_PARM(vesa, "i");
5034 MODULE_PARM_DESC(vesa,
5035        "\nSelects the desired display mode by VESA defined mode number, eg. 0x117\n"
5036          "(default: 0x0000 if sisfb is a module; this leaves the console untouched\n"
5037          "and the driver will only do the video memory management for eg. DRM/DRI;\n"
5038          "0x0103 if sisfb is in the kernel)");
5039 #endif
5040 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
5041 MODULE_PARM(vesa, "i");
5042 MODULE_PARM_DESC(vesa,
5043        "\nSelects the desired default display mode by VESA defined mode number, eg.\n"
5044          "0x117 (default: 0x0103)");
5045 #endif
5046
5047 MODULE_PARM(rate, "i");
5048 MODULE_PARM_DESC(rate,
5049         "\nSelects the desired vertical refresh rate for CRT1 (external VGA) in Hz.\n"
5050           "If the mode is specified in the format XxY-Depth@Rate, this parameter\n"
5051           "will be ignored (default: 60)");
5052
5053 MODULE_PARM(crt1off,   "i");
5054 MODULE_PARM_DESC(crt1off,
5055         "(Deprecated, please use forcecrt1)");
5056
5057 MODULE_PARM(filter, "i");
5058 MODULE_PARM_DESC(filter,
5059         "\nSelects TV flicker filter type (only for systems with a SiS301 video bridge).\n"
5060           "(Possible values 0-7, default: [no filter])");
5061
5062 MODULE_PARM(queuemode,   "s");
5063 MODULE_PARM_DESC(queuemode,
5064         "\nSelects the queue mode on 315/550/65x/74x/330/760. Possible choices are AGP, VRAM,\n"
5065           "MMIO. AGP is only available if the kernel has AGP support. The queue mode is\n"
5066           "important to programs using the 2D/3D accelerator of the SiS chip. The modes\n"
5067           "require a totally different way of programming the engines. If any mode than\n"
5068           "MMIO is selected, sisfb will disable its own 2D acceleration. On\n"
5069           "300/540/630/730, this option is ignored. (default: MMIO)");
5070
5071 /* TW: "Import" the options from the X driver */
5072 MODULE_PARM(mem,    "i");
5073 MODULE_PARM_DESC(mem,
5074         "\nDetermines the beginning of the video memory heap in KB. This heap is used\n"
5075           "for video RAM management for eg. DRM/DRI. On 300 series, the default depends\n"
5076           "on the amount of video RAM available. If 8MB of video RAM or less is available,\n"
5077           "the heap starts at 4096KB, if between 8 and 16MB are available at 8192KB,\n"
5078           "otherwise at 12288KB. On 315 and Xabre series, the heap is 1MB by default. The\n"
5079           "value is to be specified without 'KB' and should match the MaxXFBMem setting for\n"
5080           "XFree 4.x (x>=2).");
5081
5082 MODULE_PARM(forcecrt2type, "s");
5083 MODULE_PARM_DESC(forcecrt2type,
5084         "\nIf this option is omitted, the driver autodetects CRT2 output devices, such as\n"
5085           "LCD, TV or secondary VGA. With this option, this autodetection can be\n"
5086           "overridden. Possible parameters are LCD, TV, VGA or NONE. NONE disables CRT2.\n"
5087           "On systems with a 301(B/LV) bridge, parameters SVIDEO, COMPOSITE or SCART can be\n"
5088           "used instead of TV to override the TV detection. (default: [autodetected])");
5089
5090 MODULE_PARM(forcecrt1, "i");
5091 MODULE_PARM_DESC(forcecrt1,
5092         "\nNormally, the driver autodetects whether or not CRT1 (external VGA) is \n"
5093           "connected. With this option, the detection can be overridden (1=CRT1 ON,\n"
5094           " 0=CRT1 off) (default: [autodetected])");
5095
5096 MODULE_PARM(pdc, "i");
5097 MODULE_PARM_DESC(pdc,
5098         "\nThis is for manually selecting the LCD panel delay compensation. The driver\n"
5099           "should detect this correctly in most cases; however, sometimes this is not\n"
5100           "possible. If you see 'small waves' on the LCD, try setting this to 4, 32 or 24\n"
5101           "on a 300 series chipset; 3 or 51 on a 315 series chipset. If the problem persists,\n"
5102           "try other values (on 300 series: between 4 and 60 in steps of 4; on 315 series:\n"
5103           "and value from 0 to 255). (default: [autodetected])");
5104
5105 MODULE_PARM(noaccel, "i");
5106 MODULE_PARM_DESC(noaccel,
5107         "\nIf set to anything other than 0, 2D acceleration will be disabled.\n"
5108           "(default: 0)");
5109
5110 MODULE_PARM(noypan, "i");
5111 MODULE_PARM_DESC(noypan,
5112         "\nIf set to anything other than 0, y-panning will be disabled and scrolling\n"
5113           "will be performed by redrawing the screen. (default: 0)");
5114
5115 MODULE_PARM(nomax, "i");
5116 MODULE_PARM_DESC(nomax,
5117         "\nIf y-panning is enabled, sisfb will by default use the entire available video\n"
5118           "memory for the virtual screen in order to optimize scrolling performance. If this\n"
5119           "is set to anything other than 0, sisfb will not do this and thereby enable the user\n"
5120           "to positively specify a virtual Y size of the screen using fbset. (default: 0)\n");
5121
5122 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)  
5123 MODULE_PARM(inverse, "i");
5124 MODULE_PARM_DESC(inverse,
5125         "\nSetting this to anything but 0 should invert the display colors, but this\n"
5126           "does not seem to work. (default: 0)");
5127 #endif  
5128
5129 MODULE_PARM(userom, "i");
5130 MODULE_PARM_DESC(userom,
5131         "\nSetting this to 0 keeps sisfb from using the video BIOS data which is needed\n"
5132           "for some LCD and TV setup. (default: 1)");
5133
5134 MODULE_PARM(useoem, "i");
5135 MODULE_PARM_DESC(useoem,
5136         "\nSetting this to 0 keeps sisfb from using its internel OEM data for some LCD\n"
5137           "panels and TV connector types. (default: [auto])");
5138
5139 MODULE_PARM(tvstandard, "s");
5140 MODULE_PARM_DESC(tvstandard,
5141         "\nThis allows overriding the BIOS default for the TV standard. Valid choices are\n"
5142           "pal and ntsc. (default: [auto])");
5143
5144 MODULE_PARM(nocrt2rate, "i");
5145 MODULE_PARM_DESC(nocrt2rate,
5146         "\nSetting this to 1 will force the driver to use the default refresh rate for\n"
5147           "CRT2 if CRT2 type is VGA. (default: 0, use same rate as CRT1)");
5148
5149 MODULE_PARM(scalelcd, "i");
5150 MODULE_PARM_DESC(scalelcd,
5151         "\nSetting this to 1 will force the driver to scale the LCD image to the panel's\n"
5152           "native resolution. Setting it to 0 will disable scaling; if the panel can scale\n"
5153           "by itself, it will probably do this, otherwise you will see a black bar around\n"
5154           "the screen image. Default: [autodetect if panel can scale]");
5155
5156 MODULE_PARM(specialtiming, "s");
5157
5158 MODULE_PARM(lvdshl, "i");
5159
5160
5161 int init_module(void)
5162 {
5163         int err;
5164
5165         SiS_Pr.UsePanelScaler = -1;
5166         SiS_Pr.SiS_CustomT = CUT_NONE;
5167         SiS_Pr.LVDSHL = -1;
5168
5169         ivideo.refresh_rate = sisfb_parm_rate = rate;
5170
5171         if((scalelcd == 0) || (scalelcd == 1)) {
5172            SiS_Pr.UsePanelScaler = scalelcd ^ 1;
5173         }
5174
5175         if(mode)
5176                 sisfb_search_mode(mode, FALSE);
5177         else if(vesa != -1)
5178                 sisfb_search_vesamode(vesa, FALSE);
5179         else
5180 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
5181                 /* For 2.4, set mode=none if no mode is given  */
5182                 sisfb_mode_idx = MODE_INDEX_NONE;
5183 #endif
5184 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
5185                 /* For 2.5, we don't need this "mode=none" stuff anymore */
5186                 sisfb_mode_idx = DEFAULT_MODE;
5187 #endif
5188
5189         if(forcecrt2type)
5190                 sisfb_search_crt2type(forcecrt2type);
5191
5192         if(tvstandard)
5193                 sisfb_search_tvstd(tvstandard);
5194
5195         if(crt1off == 0)
5196                 sisfb_crt1off = 1;
5197         else
5198                 sisfb_crt1off = 0;
5199
5200         sisfb_forcecrt1 = forcecrt1;
5201         if(forcecrt1 == 1)
5202                 sisfb_crt1off = 0;
5203         else if(forcecrt1 == 0)
5204                 sisfb_crt1off = 1;
5205
5206         if(noaccel == 1)      sisfb_accel = 0;
5207         else if(noaccel == 0) sisfb_accel = 1;
5208
5209         if(noypan == 1)       sisfb_ypan = 0;
5210         else if(noypan == 0)  sisfb_ypan = 1;
5211
5212         if(nomax == 1)        sisfb_max = 0;
5213         else if(nomax == 0)   sisfb_max = 1;
5214         
5215 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
5216         if(inverse)           sisfb_inverse = 1;
5217         sisfb_fontname[0] = '\0';
5218 #endif
5219
5220         if(mem)               sisfb_mem = mem;
5221
5222         sisfb_userom = userom;
5223
5224         sisfb_useoem = useoem;
5225
5226         if (queuemode)        sisfb_search_queuemode(queuemode);
5227         
5228         /* If other queuemode than MMIO, disable 2D accel and ypan */
5229         if((sisfb_queuemode != -1) && (sisfb_queuemode != MMIO_CMD)) {
5230                 sisfb_accel = 0;
5231         }
5232
5233         if(pdc) sisfb_pdc = pdc & 0x3c;
5234
5235         sisfb_nocrt2rate = nocrt2rate;
5236
5237         if(specialtiming)
5238                 sisfb_search_specialtiming(specialtiming);
5239
5240         if((lvdshl >= 0) && (lvdshl <= 3)) SiS_Pr.LVDSHL = lvdshl;
5241
5242         if((err = sisfb_init()) < 0) return err;
5243
5244         return 0;
5245 }
5246
5247 void cleanup_module(void)
5248 {
5249         /* Unmap */
5250         iounmap(ivideo.video_vbase);
5251         iounmap(ivideo.mmio_vbase);
5252
5253         /* Release mem regions */
5254         release_mem_region(ivideo.video_base, ivideo.video_size);
5255         release_mem_region(ivideo.mmio_base, sisfb_mmio_size);
5256
5257 #ifdef CONFIG_MTRR
5258         /* Release MTRR region */
5259         if(ivideo.mtrr) {
5260                 mtrr_del(ivideo.mtrr,
5261                       (unsigned int)ivideo.video_base,
5262                       (unsigned int)ivideo.video_size);
5263         }
5264 #endif
5265
5266         /* Unregister the framebuffer */
5267         if(sisfb_registered) {
5268                 unregister_framebuffer(sis_fb_info);
5269 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) && (defined(NEWFBDEV))
5270                 framebuffer_release(sis_fb_info);
5271 #else
5272                 kfree(sis_fb_info);
5273 #endif
5274         }
5275
5276         if(sishw_ext.pSR) vfree(sishw_ext.pSR);
5277         if(sishw_ext.pCR) vfree(sishw_ext.pCR);
5278         
5279         /* TODO: Restore the initial mode
5280          * This sounds easy but is as good as impossible
5281          * on many machines with SiS chip and video bridge
5282          * since text modes are always set up differently
5283          * from machine to machine. Depends on the type
5284          * of integration between chipset and bridge.
5285          */
5286         
5287         printk(KERN_INFO "sisfb: Module unloaded\n");
5288 }
5289
5290 #endif
5291
5292 EXPORT_SYMBOL(sis_malloc);
5293 EXPORT_SYMBOL(sis_free);
5294 EXPORT_SYMBOL(sis_dispinfo);
5295
5296 EXPORT_SYMBOL(ivideo);
5297