blob: baaf495a0a6dff7513fd1c054ec8551d59d09910 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002 * SiS 300/540/630[S]/730[S],
3 * SiS 315[E|PRO]/550/[M]65x/[M]66x[F|M|G]X/[M]74x[GX]/330/[M]76x[GX],
4 * XGI V3XT/V5/V8, Z7
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * frame buffer driver for Linux kernels >= 2.4.14 and >=2.6.3
6 *
Thomas Winischhofer544393f2005-09-09 13:04:45 -07007 * Copyright (C) 2001-2005 Thomas Winischhofer, Vienna, Austria.
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the named License,
12 * or any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
22 *
Thomas Winischhofer544393f2005-09-09 13:04:45 -070023 * Author: Thomas Winischhofer <thomas@winischhofer.net>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024 *
25 * Author of (practically wiped) code base:
26 * SiS (www.sis.com)
Thomas Winischhofer544393f2005-09-09 13:04:45 -070027 * Copyright (C) 1999 Silicon Integrated Systems, Inc.
Linus Torvalds1da177e2005-04-16 15:20:36 -070028 *
29 * See http://www.winischhofer.net/ for more information and updates
30 *
31 * Originally based on the VBE 2.0 compliant graphic boards framebuffer driver,
32 * which is (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
33 *
34 */
35
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include <linux/version.h>
37#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070038#include <linux/moduleparam.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <linux/kernel.h>
40#include <linux/smp_lock.h>
41#include <linux/spinlock.h>
42#include <linux/errno.h>
43#include <linux/string.h>
44#include <linux/mm.h>
Jon Smirla8f340e2006-07-10 04:44:12 -070045
Jon Smirl894673e2006-07-10 04:44:13 -070046#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
Linus Torvalds1da177e2005-04-16 15:20:36 -070047#include <linux/tty.h>
Jon Smirla8f340e2006-07-10 04:44:12 -070048#else
49#include <linux/screen_info.h>
50#endif
51
Linus Torvalds1da177e2005-04-16 15:20:36 -070052#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070053#include <linux/fb.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070054#include <linux/selection.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070055#include <linux/ioport.h>
56#include <linux/init.h>
57#include <linux/pci.h>
58#include <linux/vmalloc.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070059#include <linux/capability.h>
60#include <linux/fs.h>
61#include <linux/types.h>
62#include <asm/uaccess.h>
63#include <asm/io.h>
64#ifdef CONFIG_MTRR
65#include <asm/mtrr.h>
66#endif
67
Linus Torvalds1da177e2005-04-16 15:20:36 -070068#include "sis.h"
69#include "sis_main.h"
70
Thomas Winischhofer544393f2005-09-09 13:04:45 -070071static void sisfb_handle_command(struct sis_video_info *ivideo,
72 struct sisfb_cmd *sisfb_command);
73
Linus Torvalds1da177e2005-04-16 15:20:36 -070074/* ------------------ Internal helper routines ----------------- */
75
76static void __init
77sisfb_setdefaultparms(void)
78{
Thomas Winischhofer544393f2005-09-09 13:04:45 -070079 sisfb_off = 0;
80 sisfb_parm_mem = 0;
81 sisfb_accel = -1;
82 sisfb_ypan = -1;
83 sisfb_max = -1;
84 sisfb_userom = -1;
85 sisfb_useoem = -1;
Thomas Winischhofer544393f2005-09-09 13:04:45 -070086 sisfb_mode_idx = -1;
Thomas Winischhofer544393f2005-09-09 13:04:45 -070087 sisfb_parm_rate = -1;
88 sisfb_crt1off = 0;
89 sisfb_forcecrt1 = -1;
90 sisfb_crt2type = -1;
91 sisfb_crt2flags = 0;
92 sisfb_pdc = 0xff;
93 sisfb_pdca = 0xff;
94 sisfb_scalelcd = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 sisfb_specialtiming = CUT_NONE;
Thomas Winischhofer544393f2005-09-09 13:04:45 -070096 sisfb_lvdshl = -1;
97 sisfb_dstn = 0;
98 sisfb_fstn = 0;
99 sisfb_tvplug = -1;
100 sisfb_tvstd = -1;
101 sisfb_tvxposoffset = 0;
102 sisfb_tvyposoffset = 0;
103 sisfb_nocrt2rate = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104#if !defined(__i386__) && !defined(__x86_64__)
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700105 sisfb_resetcard = 0;
106 sisfb_videoram = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107#endif
108}
109
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700110/* ------------- Parameter parsing -------------- */
111
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112static void __devinit
113sisfb_search_vesamode(unsigned int vesamode, BOOLEAN quiet)
114{
115 int i = 0, j = 0;
116
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700117 /* We don't know the hardware specs yet and there is no ivideo */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118
119 if(vesamode == 0) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700120 if(!quiet)
121 printk(KERN_ERR "sisfb: Invalid mode. Using default.\n");
122
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 sisfb_mode_idx = DEFAULT_MODE;
Michal Piotrowski43704092006-10-03 01:15:00 -0700124
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 return;
126 }
127
128 vesamode &= 0x1dff; /* Clean VESA mode number from other flags */
129
130 while(sisbios_mode[i++].mode_no[0] != 0) {
131 if( (sisbios_mode[i-1].vesa_mode_no_1 == vesamode) ||
132 (sisbios_mode[i-1].vesa_mode_no_2 == vesamode) ) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700133 if(sisfb_fstn) {
134 if(sisbios_mode[i-1].mode_no[1] == 0x50 ||
135 sisbios_mode[i-1].mode_no[1] == 0x56 ||
136 sisbios_mode[i-1].mode_no[1] == 0x53)
137 continue;
138 } else {
139 if(sisbios_mode[i-1].mode_no[1] == 0x5a ||
140 sisbios_mode[i-1].mode_no[1] == 0x5b)
141 continue;
142 }
143 sisfb_mode_idx = i - 1;
144 j = 1;
145 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 }
147 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700148 if((!j) && !quiet)
149 printk(KERN_ERR "sisfb: Invalid VESA mode 0x%x'\n", vesamode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150}
151
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700152static void __devinit
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153sisfb_search_mode(char *name, BOOLEAN quiet)
154{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 unsigned int j = 0, xres = 0, yres = 0, depth = 0, rate = 0;
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700156 int i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 char strbuf[16], strbuf1[20];
158 char *nameptr = name;
159
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700160 /* We don't know the hardware specs yet and there is no ivideo */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161
162 if(name == NULL) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700163 if(!quiet)
164 printk(KERN_ERR "sisfb: Internal error, using default mode.\n");
165
166 sisfb_mode_idx = DEFAULT_MODE;
167 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 }
169
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700170 if(!strnicmp(name, sisbios_mode[MODE_INDEX_NONE].name, strlen(name))) {
171 if(!quiet)
172 printk(KERN_ERR "sisfb: Mode 'none' not supported anymore. Using default.\n");
173
174 sisfb_mode_idx = DEFAULT_MODE;
175 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 }
Michal Piotrowski43704092006-10-03 01:15:00 -0700177
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 if(strlen(name) <= 19) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700179 strcpy(strbuf1, name);
180 for(i = 0; i < strlen(strbuf1); i++) {
181 if(strbuf1[i] < '0' || strbuf1[i] > '9') strbuf1[i] = ' ';
182 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700184 /* This does some fuzzy mode naming detection */
185 if(sscanf(strbuf1, "%u %u %u %u", &xres, &yres, &depth, &rate) == 4) {
186 if((rate <= 32) || (depth > 32)) {
187 j = rate; rate = depth; depth = j;
188 }
189 sprintf(strbuf, "%ux%ux%u", xres, yres, depth);
190 nameptr = strbuf;
191 sisfb_parm_rate = rate;
192 } else if(sscanf(strbuf1, "%u %u %u", &xres, &yres, &depth) == 3) {
193 sprintf(strbuf, "%ux%ux%u", xres, yres, depth);
194 nameptr = strbuf;
195 } else {
196 xres = 0;
197 if((sscanf(strbuf1, "%u %u", &xres, &yres) == 2) && (xres != 0)) {
198 sprintf(strbuf, "%ux%ux8", xres, yres);
199 nameptr = strbuf;
200 } else {
201 sisfb_search_vesamode(simple_strtoul(name, NULL, 0), quiet);
202 return;
203 }
204 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 }
206
207 i = 0; j = 0;
208 while(sisbios_mode[i].mode_no[0] != 0) {
209 if(!strnicmp(nameptr, sisbios_mode[i++].name, strlen(nameptr))) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700210 if(sisfb_fstn) {
211 if(sisbios_mode[i-1].mode_no[1] == 0x50 ||
212 sisbios_mode[i-1].mode_no[1] == 0x56 ||
213 sisbios_mode[i-1].mode_no[1] == 0x53)
214 continue;
215 } else {
216 if(sisbios_mode[i-1].mode_no[1] == 0x5a ||
217 sisbios_mode[i-1].mode_no[1] == 0x5b)
218 continue;
219 }
220 sisfb_mode_idx = i - 1;
221 j = 1;
222 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 }
224 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700225
226 if((!j) && !quiet)
227 printk(KERN_ERR "sisfb: Invalid mode '%s'\n", nameptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228}
229
230#ifndef MODULE
231static void __devinit
232sisfb_get_vga_mode_from_kernel(void)
233{
Adrian Bunk31c5cdb2006-06-26 00:26:28 -0700234#ifdef CONFIG_X86
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700235 char mymode[32];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 int mydepth = screen_info.lfb_depth;
237
238 if(screen_info.orig_video_isVGA != VIDEO_TYPE_VLFB) return;
239
240 if( (screen_info.lfb_width >= 320) && (screen_info.lfb_width <= 2048) &&
241 (screen_info.lfb_height >= 200) && (screen_info.lfb_height <= 1536) &&
242 (mydepth >= 8) && (mydepth <= 32) ) {
243
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700244 if(mydepth == 24) mydepth = 32;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700246 sprintf(mymode, "%ux%ux%u", screen_info.lfb_width,
247 screen_info.lfb_height,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 mydepth);
249
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700250 printk(KERN_DEBUG
251 "sisfb: Using vga mode %s pre-set by kernel as default\n",
252 mymode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700254 sisfb_search_mode(mymode, TRUE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 }
256#endif
257 return;
258}
259#endif
260
261static void __init
262sisfb_search_crt2type(const char *name)
263{
264 int i = 0;
265
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700266 /* We don't know the hardware specs yet and there is no ivideo */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267
268 if(name == NULL) return;
269
270 while(sis_crt2type[i].type_no != -1) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700271 if(!strnicmp(name, sis_crt2type[i].name, strlen(sis_crt2type[i].name))) {
272 sisfb_crt2type = sis_crt2type[i].type_no;
273 sisfb_tvplug = sis_crt2type[i].tvplug_no;
274 sisfb_crt2flags = sis_crt2type[i].flags;
275 break;
276 }
277 i++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 }
279
280 sisfb_dstn = (sisfb_crt2flags & FL_550_DSTN) ? 1 : 0;
281 sisfb_fstn = (sisfb_crt2flags & FL_550_FSTN) ? 1 : 0;
282
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700283 if(sisfb_crt2type < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 printk(KERN_ERR "sisfb: Invalid CRT2 type: %s\n", name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285}
286
287static void __init
288sisfb_search_tvstd(const char *name)
289{
290 int i = 0;
291
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700292 /* We don't know the hardware specs yet and there is no ivideo */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700294 if(name == NULL)
295 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296
297 while(sis_tvtype[i].type_no != -1) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700298 if(!strnicmp(name, sis_tvtype[i].name, strlen(sis_tvtype[i].name))) {
299 sisfb_tvstd = sis_tvtype[i].type_no;
300 break;
301 }
302 i++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 }
304}
305
306static void __init
307sisfb_search_specialtiming(const char *name)
308{
309 int i = 0;
310 BOOLEAN found = FALSE;
311
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700312 /* We don't know the hardware specs yet and there is no ivideo */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700314 if(name == NULL)
315 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316
317 if(!strnicmp(name, "none", 4)) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700318 sisfb_specialtiming = CUT_FORCENONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 printk(KERN_DEBUG "sisfb: Special timing disabled\n");
320 } else {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700321 while(mycustomttable[i].chipID != 0) {
322 if(!strnicmp(name,mycustomttable[i].optionName,
323 strlen(mycustomttable[i].optionName))) {
324 sisfb_specialtiming = mycustomttable[i].SpecialID;
325 found = TRUE;
326 printk(KERN_INFO "sisfb: Special timing for %s %s forced (\"%s\")\n",
327 mycustomttable[i].vendorName,
328 mycustomttable[i].cardName,
329 mycustomttable[i].optionName);
330 break;
331 }
332 i++;
333 }
334 if(!found) {
335 printk(KERN_WARNING "sisfb: Invalid SpecialTiming parameter, valid are:");
336 printk(KERN_WARNING "\t\"none\" (to disable special timings)\n");
337 i = 0;
338 while(mycustomttable[i].chipID != 0) {
339 printk(KERN_WARNING "\t\"%s\" (for %s %s)\n",
340 mycustomttable[i].optionName,
341 mycustomttable[i].vendorName,
342 mycustomttable[i].cardName);
343 i++;
344 }
345 }
346 }
347}
348
349/* ----------- Various detection routines ----------- */
350
351static void __devinit
352sisfb_detect_custom_timing(struct sis_video_info *ivideo)
353{
354 unsigned char *biosver = NULL;
355 unsigned char *biosdate = NULL;
356 BOOLEAN footprint;
357 u32 chksum = 0;
358 int i, j;
359
360 if(ivideo->SiS_Pr.UseROM) {
361 biosver = ivideo->SiS_Pr.VirtualRomBase + 0x06;
362 biosdate = ivideo->SiS_Pr.VirtualRomBase + 0x2c;
363 for(i = 0; i < 32768; i++)
364 chksum += ivideo->SiS_Pr.VirtualRomBase[i];
365 }
366
367 i = 0;
368 do {
369 if( (mycustomttable[i].chipID == ivideo->chip) &&
370 ((!strlen(mycustomttable[i].biosversion)) ||
371 (ivideo->SiS_Pr.UseROM &&
372 (!strncmp(mycustomttable[i].biosversion, biosver,
373 strlen(mycustomttable[i].biosversion))))) &&
374 ((!strlen(mycustomttable[i].biosdate)) ||
375 (ivideo->SiS_Pr.UseROM &&
376 (!strncmp(mycustomttable[i].biosdate, biosdate,
377 strlen(mycustomttable[i].biosdate))))) &&
378 ((!mycustomttable[i].bioschksum) ||
379 (ivideo->SiS_Pr.UseROM &&
380 (mycustomttable[i].bioschksum == chksum))) &&
381 (mycustomttable[i].pcisubsysvendor == ivideo->subsysvendor) &&
382 (mycustomttable[i].pcisubsyscard == ivideo->subsysdevice) ) {
383 footprint = TRUE;
384 for(j = 0; j < 5; j++) {
385 if(mycustomttable[i].biosFootprintAddr[j]) {
386 if(ivideo->SiS_Pr.UseROM) {
387 if(ivideo->SiS_Pr.VirtualRomBase[mycustomttable[i].biosFootprintAddr[j]] !=
388 mycustomttable[i].biosFootprintData[j]) {
389 footprint = FALSE;
390 }
391 } else
392 footprint = FALSE;
393 }
394 }
395 if(footprint) {
396 ivideo->SiS_Pr.SiS_CustomT = mycustomttable[i].SpecialID;
397 printk(KERN_DEBUG "sisfb: Identified [%s %s], special timing applies\n",
398 mycustomttable[i].vendorName,
399 mycustomttable[i].cardName);
400 printk(KERN_DEBUG "sisfb: [specialtiming parameter name: %s]\n",
401 mycustomttable[i].optionName);
402 break;
403 }
404 }
405 i++;
406 } while(mycustomttable[i].chipID);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407}
408
409static BOOLEAN __devinit
410sisfb_interpret_edid(struct sisfb_monitor *monitor, u8 *buffer)
411{
412 int i, j, xres, yres, refresh, index;
413 u32 emodes;
414
415 if(buffer[0] != 0x00 || buffer[1] != 0xff ||
416 buffer[2] != 0xff || buffer[3] != 0xff ||
417 buffer[4] != 0xff || buffer[5] != 0xff ||
418 buffer[6] != 0xff || buffer[7] != 0x00) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700419 printk(KERN_DEBUG "sisfb: Bad EDID header\n");
420 return FALSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 }
422
423 if(buffer[0x12] != 0x01) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700424 printk(KERN_INFO "sisfb: EDID version %d not supported\n",
425 buffer[0x12]);
426 return FALSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 }
428
429 monitor->feature = buffer[0x18];
430
431 if(!buffer[0x14] & 0x80) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700432 if(!(buffer[0x14] & 0x08)) {
433 printk(KERN_INFO
434 "sisfb: WARNING: Monitor does not support separate syncs\n");
435 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 }
437
438 if(buffer[0x13] >= 0x01) {
439 /* EDID V1 rev 1 and 2: Search for monitor descriptor
440 * to extract ranges
441 */
442 j = 0x36;
443 for(i=0; i<4; i++) {
444 if(buffer[j] == 0x00 && buffer[j + 1] == 0x00 &&
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700445 buffer[j + 2] == 0x00 && buffer[j + 3] == 0xfd &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 buffer[j + 4] == 0x00) {
447 monitor->hmin = buffer[j + 7];
448 monitor->hmax = buffer[j + 8];
449 monitor->vmin = buffer[j + 5];
450 monitor->vmax = buffer[j + 6];
451 monitor->dclockmax = buffer[j + 9] * 10 * 1000;
452 monitor->datavalid = TRUE;
453 break;
454 }
455 j += 18;
456 }
457 }
458
459 if(!monitor->datavalid) {
460 /* Otherwise: Get a range from the list of supported
461 * Estabished Timings. This is not entirely accurate,
462 * because fixed frequency monitors are not supported
463 * that way.
464 */
465 monitor->hmin = 65535; monitor->hmax = 0;
466 monitor->vmin = 65535; monitor->vmax = 0;
467 monitor->dclockmax = 0;
468 emodes = buffer[0x23] | (buffer[0x24] << 8) | (buffer[0x25] << 16);
469 for(i = 0; i < 13; i++) {
470 if(emodes & sisfb_ddcsmodes[i].mask) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700471 if(monitor->hmin > sisfb_ddcsmodes[i].h) monitor->hmin = sisfb_ddcsmodes[i].h;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 if(monitor->hmax < sisfb_ddcsmodes[i].h) monitor->hmax = sisfb_ddcsmodes[i].h + 1;
473 if(monitor->vmin > sisfb_ddcsmodes[i].v) monitor->vmin = sisfb_ddcsmodes[i].v;
474 if(monitor->vmax < sisfb_ddcsmodes[i].v) monitor->vmax = sisfb_ddcsmodes[i].v;
475 if(monitor->dclockmax < sisfb_ddcsmodes[i].d) monitor->dclockmax = sisfb_ddcsmodes[i].d;
476 }
477 }
478 index = 0x26;
479 for(i = 0; i < 8; i++) {
480 xres = (buffer[index] + 31) * 8;
481 switch(buffer[index + 1] & 0xc0) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700482 case 0xc0: yres = (xres * 9) / 16; break;
483 case 0x80: yres = (xres * 4) / 5; break;
484 case 0x40: yres = (xres * 3) / 4; break;
485 default: yres = xres; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 }
487 refresh = (buffer[index + 1] & 0x3f) + 60;
488 if((xres >= 640) && (yres >= 480)) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700489 for(j = 0; j < 8; j++) {
490 if((xres == sisfb_ddcfmodes[j].x) &&
491 (yres == sisfb_ddcfmodes[j].y) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 (refresh == sisfb_ddcfmodes[j].v)) {
493 if(monitor->hmin > sisfb_ddcfmodes[j].h) monitor->hmin = sisfb_ddcfmodes[j].h;
494 if(monitor->hmax < sisfb_ddcfmodes[j].h) monitor->hmax = sisfb_ddcfmodes[j].h + 1;
495 if(monitor->vmin > sisfb_ddcsmodes[j].v) monitor->vmin = sisfb_ddcsmodes[j].v;
496 if(monitor->vmax < sisfb_ddcsmodes[j].v) monitor->vmax = sisfb_ddcsmodes[j].v;
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700497 if(monitor->dclockmax < sisfb_ddcsmodes[j].d) monitor->dclockmax = sisfb_ddcsmodes[j].d;
498 }
499 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 }
501 index += 2;
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700502 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 if((monitor->hmin <= monitor->hmax) && (monitor->vmin <= monitor->vmax)) {
504 monitor->datavalid = TRUE;
505 }
506 }
507
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700508 return monitor->datavalid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509}
510
511static void __devinit
512sisfb_handle_ddc(struct sis_video_info *ivideo, struct sisfb_monitor *monitor, int crtno)
513{
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700514 unsigned short temp, i, realcrtno = crtno;
515 unsigned char buffer[256];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516
517 monitor->datavalid = FALSE;
518
519 if(crtno) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700520 if(ivideo->vbflags & CRT2_LCD) realcrtno = 1;
521 else if(ivideo->vbflags & CRT2_VGA) realcrtno = 2;
522 else return;
523 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700525 if((ivideo->sisfb_crt1off) && (!crtno))
526 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700528 temp = SiS_HandleDDC(&ivideo->SiS_Pr, ivideo->vbflags, ivideo->sisvga_engine,
529 realcrtno, 0, &buffer[0], ivideo->vbflags2);
530 if((!temp) || (temp == 0xffff)) {
531 printk(KERN_INFO "sisfb: CRT%d DDC probing failed\n", crtno + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 return;
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700533 } else {
534 printk(KERN_INFO "sisfb: CRT%d DDC supported\n", crtno + 1);
535 printk(KERN_INFO "sisfb: CRT%d DDC level: %s%s%s%s\n",
536 crtno + 1,
537 (temp & 0x1a) ? "" : "[none of the supported]",
538 (temp & 0x02) ? "2 " : "",
539 (temp & 0x08) ? "D&P" : "",
540 (temp & 0x10) ? "FPDI-2" : "");
541 if(temp & 0x02) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 i = 3; /* Number of retrys */
543 do {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700544 temp = SiS_HandleDDC(&ivideo->SiS_Pr, ivideo->vbflags, ivideo->sisvga_engine,
545 realcrtno, 1, &buffer[0], ivideo->vbflags2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 } while((temp) && i--);
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700547 if(!temp) {
548 if(sisfb_interpret_edid(monitor, &buffer[0])) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 printk(KERN_INFO "sisfb: Monitor range H %d-%dKHz, V %d-%dHz, Max. dotclock %dMHz\n",
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700550 monitor->hmin, monitor->hmax, monitor->vmin, monitor->vmax,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 monitor->dclockmax / 1000);
552 } else {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700553 printk(KERN_INFO "sisfb: CRT%d DDC EDID corrupt\n", crtno + 1);
554 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 } else {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700556 printk(KERN_INFO "sisfb: CRT%d DDC reading failed\n", crtno + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 }
558 } else {
559 printk(KERN_INFO "sisfb: VESA D&P and FPDI-2 not supported yet\n");
560 }
561 }
562}
563
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700564/* -------------- Mode validation --------------- */
565
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566static BOOLEAN
567sisfb_verify_rate(struct sis_video_info *ivideo, struct sisfb_monitor *monitor,
568 int mode_idx, int rate_idx, int rate)
569{
570 int htotal, vtotal;
571 unsigned int dclock, hsync;
572
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700573 if(!monitor->datavalid)
574 return TRUE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700576 if(mode_idx < 0)
577 return FALSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578
579 /* Skip for 320x200, 320x240, 640x400 */
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700580 switch(sisbios_mode[mode_idx].mode_no[ivideo->mni]) {
581 case 0x59:
582 case 0x41:
583 case 0x4f:
584 case 0x50:
585 case 0x56:
586 case 0x53:
587 case 0x2f:
588 case 0x5d:
589 case 0x5e:
590 return TRUE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591#ifdef CONFIG_FB_SIS_315
592 case 0x5a:
593 case 0x5b:
594 if(ivideo->sisvga_engine == SIS_315_VGA) return TRUE;
595#endif
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700596 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700598 if(rate < (monitor->vmin - 1))
599 return FALSE;
600 if(rate > (monitor->vmax + 1))
601 return FALSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700603 if(sisfb_gettotalfrommode(&ivideo->SiS_Pr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 sisbios_mode[mode_idx].mode_no[ivideo->mni],
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700605 &htotal, &vtotal, rate_idx)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 dclock = (htotal * vtotal * rate) / 1000;
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700607 if(dclock > (monitor->dclockmax + 1000))
608 return FALSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 hsync = dclock / htotal;
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700610 if(hsync < (monitor->hmin - 1))
611 return FALSE;
612 if(hsync > (monitor->hmax + 1))
613 return FALSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 } else {
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700615 return FALSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 }
617 return TRUE;
618}
619
620static int
621sisfb_validate_mode(struct sis_video_info *ivideo, int myindex, u32 vbflags)
622{
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700623 u16 xres=0, yres, myres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624
625#ifdef CONFIG_FB_SIS_300
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700626 if(ivideo->sisvga_engine == SIS_300_VGA) {
627 if(!(sisbios_mode[myindex].chipset & MD_SIS300))
628 return -1 ;
629 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630#endif
631#ifdef CONFIG_FB_SIS_315
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700632 if(ivideo->sisvga_engine == SIS_315_VGA) {
633 if(!(sisbios_mode[myindex].chipset & MD_SIS315))
634 return -1;
635 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636#endif
637
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700638 myres = sisbios_mode[myindex].yres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700640 switch(vbflags & VB_DISPTYPE_DISP2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700642 case CRT2_LCD:
643 xres = ivideo->lcdxres; yres = ivideo->lcdyres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700645 if((ivideo->SiS_Pr.SiS_CustomT != CUT_PANEL848) &&
646 (ivideo->SiS_Pr.SiS_CustomT != CUT_PANEL856)) {
647 if(sisbios_mode[myindex].xres > xres)
648 return -1;
649 if(myres > yres)
650 return -1;
651 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700653 if(ivideo->sisfb_fstn) {
654 if(sisbios_mode[myindex].xres == 320) {
655 if(myres == 240) {
656 switch(sisbios_mode[myindex].mode_no[1]) {
657 case 0x50: myindex = MODE_FSTN_8; break;
658 case 0x56: myindex = MODE_FSTN_16; break;
659 case 0x53: return -1;
660 }
661 }
662 }
663 }
664
665 if(SiS_GetModeID_LCD(ivideo->sisvga_engine, vbflags, sisbios_mode[myindex].xres,
666 sisbios_mode[myindex].yres, 0, ivideo->sisfb_fstn,
667 ivideo->SiS_Pr.SiS_CustomT, xres, yres, ivideo->vbflags2) < 0x14) {
668 return -1;
669 }
670 break;
671
672 case CRT2_TV:
673 if(SiS_GetModeID_TV(ivideo->sisvga_engine, vbflags, sisbios_mode[myindex].xres,
674 sisbios_mode[myindex].yres, 0, ivideo->vbflags2) < 0x14) {
675 return -1;
676 }
677 break;
678
679 case CRT2_VGA:
680 if(SiS_GetModeID_VGA2(ivideo->sisvga_engine, vbflags, sisbios_mode[myindex].xres,
681 sisbios_mode[myindex].yres, 0, ivideo->vbflags2) < 0x14) {
682 return -1;
683 }
684 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 }
686
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700687 return myindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688}
689
690static u8
691sisfb_search_refresh_rate(struct sis_video_info *ivideo, unsigned int rate, int mode_idx)
692{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 int i = 0;
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700694 u16 xres = sisbios_mode[mode_idx].xres;
695 u16 yres = sisbios_mode[mode_idx].yres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696
697 ivideo->rate_idx = 0;
698 while((sisfb_vrate[i].idx != 0) && (sisfb_vrate[i].xres <= xres)) {
699 if((sisfb_vrate[i].xres == xres) && (sisfb_vrate[i].yres == yres)) {
700 if(sisfb_vrate[i].refresh == rate) {
701 ivideo->rate_idx = sisfb_vrate[i].idx;
702 break;
703 } else if(sisfb_vrate[i].refresh > rate) {
704 if((sisfb_vrate[i].refresh - rate) <= 3) {
705 DPRINTK("sisfb: Adjusting rate from %d up to %d\n",
706 rate, sisfb_vrate[i].refresh);
707 ivideo->rate_idx = sisfb_vrate[i].idx;
708 ivideo->refresh_rate = sisfb_vrate[i].refresh;
709 } else if(((rate - sisfb_vrate[i-1].refresh) <= 2)
710 && (sisfb_vrate[i].idx != 1)) {
711 DPRINTK("sisfb: Adjusting rate from %d down to %d\n",
712 rate, sisfb_vrate[i-1].refresh);
713 ivideo->rate_idx = sisfb_vrate[i-1].idx;
714 ivideo->refresh_rate = sisfb_vrate[i-1].refresh;
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700715 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 break;
717 } else if((rate - sisfb_vrate[i].refresh) <= 2) {
718 DPRINTK("sisfb: Adjusting rate from %d down to %d\n",
719 rate, sisfb_vrate[i].refresh);
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700720 ivideo->rate_idx = sisfb_vrate[i].idx;
721 break;
722 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 }
724 i++;
725 }
726 if(ivideo->rate_idx > 0) {
727 return ivideo->rate_idx;
728 } else {
729 printk(KERN_INFO "sisfb: Unsupported rate %d for %dx%d\n",
730 rate, xres, yres);
731 return 0;
732 }
733}
734
735static BOOLEAN
736sisfb_bridgeisslave(struct sis_video_info *ivideo)
737{
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700738 unsigned char P1_00;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700740 if(!(ivideo->vbflags2 & VB2_VIDEOBRIDGE))
741 return FALSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700743 inSISIDXREG(SISPART1,0x00,P1_00);
744 if( ((ivideo->sisvga_engine == SIS_300_VGA) && (P1_00 & 0xa0) == 0x20) ||
745 ((ivideo->sisvga_engine == SIS_315_VGA) && (P1_00 & 0x50) == 0x10) ) {
746 return TRUE;
747 } else {
748 return FALSE;
749 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750}
751
752static BOOLEAN
753sisfballowretracecrt1(struct sis_video_info *ivideo)
754{
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700755 u8 temp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700757 inSISIDXREG(SISCR,0x17,temp);
758 if(!(temp & 0x80))
759 return FALSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700761 inSISIDXREG(SISSR,0x1f,temp);
762 if(temp & 0xc0)
763 return FALSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700765 return TRUE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766}
767
768static BOOLEAN
769sisfbcheckvretracecrt1(struct sis_video_info *ivideo)
770{
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700771 if(!sisfballowretracecrt1(ivideo))
772 return FALSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700774 if(inSISREG(SISINPSTAT) & 0x08)
775 return TRUE;
776 else
777 return FALSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778}
779
780static void
781sisfbwaitretracecrt1(struct sis_video_info *ivideo)
782{
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700783 int watchdog;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700785 if(!sisfballowretracecrt1(ivideo))
786 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700788 watchdog = 65536;
789 while((!(inSISREG(SISINPSTAT) & 0x08)) && --watchdog);
790 watchdog = 65536;
791 while((inSISREG(SISINPSTAT) & 0x08) && --watchdog);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792}
793
794static BOOLEAN
795sisfbcheckvretracecrt2(struct sis_video_info *ivideo)
796{
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700797 unsigned char temp, reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700799 switch(ivideo->sisvga_engine) {
800 case SIS_300_VGA: reg = 0x25; break;
801 case SIS_315_VGA: reg = 0x30; break;
802 default: return FALSE;
803 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700805 inSISIDXREG(SISPART1, reg, temp);
806 if(temp & 0x02)
807 return TRUE;
808 else
809 return FALSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810}
811
812static BOOLEAN
813sisfb_CheckVBRetrace(struct sis_video_info *ivideo)
814{
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700815 if(ivideo->currentvbflags & VB_DISPTYPE_DISP2) {
816 if(!sisfb_bridgeisslave(ivideo)) {
817 return sisfbcheckvretracecrt2(ivideo);
818 }
819 }
820 return sisfbcheckvretracecrt1(ivideo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821}
822
823static u32
824sisfb_setupvbblankflags(struct sis_video_info *ivideo, u32 *vcount, u32 *hcount)
825{
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700826 u8 idx, reg1, reg2, reg3, reg4;
827 u32 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700829 (*vcount) = (*hcount) = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700831 if((ivideo->currentvbflags & VB_DISPTYPE_DISP2) && (!(sisfb_bridgeisslave(ivideo)))) {
832
833 ret |= (FB_VBLANK_HAVE_VSYNC |
834 FB_VBLANK_HAVE_HBLANK |
835 FB_VBLANK_HAVE_VBLANK |
836 FB_VBLANK_HAVE_VCOUNT |
837 FB_VBLANK_HAVE_HCOUNT);
838 switch(ivideo->sisvga_engine) {
839 case SIS_300_VGA: idx = 0x25; break;
840 default:
841 case SIS_315_VGA: idx = 0x30; break;
842 }
843 inSISIDXREG(SISPART1,(idx+0),reg1); /* 30 */
844 inSISIDXREG(SISPART1,(idx+1),reg2); /* 31 */
845 inSISIDXREG(SISPART1,(idx+2),reg3); /* 32 */
846 inSISIDXREG(SISPART1,(idx+3),reg4); /* 33 */
847 if(reg1 & 0x01) ret |= FB_VBLANK_VBLANKING;
848 if(reg1 & 0x02) ret |= FB_VBLANK_VSYNCING;
849 if(reg4 & 0x80) ret |= FB_VBLANK_HBLANKING;
850 (*vcount) = reg3 | ((reg4 & 0x70) << 4);
851 (*hcount) = reg2 | ((reg4 & 0x0f) << 8);
852
853 } else if(sisfballowretracecrt1(ivideo)) {
854
855 ret |= (FB_VBLANK_HAVE_VSYNC |
856 FB_VBLANK_HAVE_VBLANK |
857 FB_VBLANK_HAVE_VCOUNT |
858 FB_VBLANK_HAVE_HCOUNT);
859 reg1 = inSISREG(SISINPSTAT);
860 if(reg1 & 0x08) ret |= FB_VBLANK_VSYNCING;
861 if(reg1 & 0x01) ret |= FB_VBLANK_VBLANKING;
862 inSISIDXREG(SISCR,0x20,reg1);
863 inSISIDXREG(SISCR,0x1b,reg1);
864 inSISIDXREG(SISCR,0x1c,reg2);
865 inSISIDXREG(SISCR,0x1d,reg3);
866 (*vcount) = reg2 | ((reg3 & 0x07) << 8);
867 (*hcount) = (reg1 | ((reg3 & 0x10) << 4)) << 3;
868 }
869
870 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871}
872
873static int
874sisfb_myblank(struct sis_video_info *ivideo, int blank)
875{
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700876 u8 sr01, sr11, sr1f, cr63=0, p2_0, p1_13;
877 BOOLEAN backlight = TRUE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700879 switch(blank) {
880 case FB_BLANK_UNBLANK: /* on */
881 sr01 = 0x00;
882 sr11 = 0x00;
883 sr1f = 0x00;
884 cr63 = 0x00;
885 p2_0 = 0x20;
886 p1_13 = 0x00;
887 backlight = TRUE;
888 break;
889 case FB_BLANK_NORMAL: /* blank */
890 sr01 = 0x20;
891 sr11 = 0x00;
892 sr1f = 0x00;
893 cr63 = 0x00;
894 p2_0 = 0x20;
895 p1_13 = 0x00;
896 backlight = TRUE;
897 break;
898 case FB_BLANK_VSYNC_SUSPEND: /* no vsync */
899 sr01 = 0x20;
900 sr11 = 0x08;
901 sr1f = 0x80;
902 cr63 = 0x40;
903 p2_0 = 0x40;
904 p1_13 = 0x80;
905 backlight = FALSE;
906 break;
907 case FB_BLANK_HSYNC_SUSPEND: /* no hsync */
908 sr01 = 0x20;
909 sr11 = 0x08;
910 sr1f = 0x40;
911 cr63 = 0x40;
912 p2_0 = 0x80;
913 p1_13 = 0x40;
914 backlight = FALSE;
915 break;
916 case FB_BLANK_POWERDOWN: /* off */
917 sr01 = 0x20;
918 sr11 = 0x08;
919 sr1f = 0xc0;
920 cr63 = 0x40;
921 p2_0 = 0xc0;
922 p1_13 = 0xc0;
923 backlight = FALSE;
924 break;
925 default:
926 return 1;
927 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700929 if(ivideo->currentvbflags & VB_DISPTYPE_CRT1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700931 if( (!ivideo->sisfb_thismonitor.datavalid) ||
932 ((ivideo->sisfb_thismonitor.datavalid) &&
933 (ivideo->sisfb_thismonitor.feature & 0xe0))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700935 if(ivideo->sisvga_engine == SIS_315_VGA) {
936 setSISIDXREG(SISCR, ivideo->SiS_Pr.SiS_MyCR63, 0xbf, cr63);
937 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700939 if(!(sisfb_bridgeisslave(ivideo))) {
940 setSISIDXREG(SISSR, 0x01, ~0x20, sr01);
941 setSISIDXREG(SISSR, 0x1f, 0x3f, sr1f);
942 }
943 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700945 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700947 if(ivideo->currentvbflags & CRT2_LCD) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700949 if(ivideo->vbflags2 & VB2_SISLVDSBRIDGE) {
950 if(backlight) {
951 SiS_SiS30xBLOn(&ivideo->SiS_Pr);
952 } else {
953 SiS_SiS30xBLOff(&ivideo->SiS_Pr);
954 }
955 } else if(ivideo->sisvga_engine == SIS_315_VGA) {
956#ifdef CONFIG_FB_SIS_315
957 if(ivideo->vbflags2 & VB2_CHRONTEL) {
958 if(backlight) {
959 SiS_Chrontel701xBLOn(&ivideo->SiS_Pr);
960 } else {
961 SiS_Chrontel701xBLOff(&ivideo->SiS_Pr);
962 }
963 }
964#endif
965 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700967 if(((ivideo->sisvga_engine == SIS_300_VGA) &&
968 (ivideo->vbflags2 & (VB2_301|VB2_30xBDH|VB2_LVDS))) ||
969 ((ivideo->sisvga_engine == SIS_315_VGA) &&
970 ((ivideo->vbflags2 & (VB2_LVDS | VB2_CHRONTEL)) == VB2_LVDS))) {
971 setSISIDXREG(SISSR, 0x11, ~0x0c, sr11);
972 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700974 if(ivideo->sisvga_engine == SIS_300_VGA) {
975 if((ivideo->vbflags2 & VB2_30xB) &&
976 (!(ivideo->vbflags2 & VB2_30xBDH))) {
977 setSISIDXREG(SISPART1, 0x13, 0x3f, p1_13);
978 }
979 } else if(ivideo->sisvga_engine == SIS_315_VGA) {
980 if((ivideo->vbflags2 & VB2_30xB) &&
981 (!(ivideo->vbflags2 & VB2_30xBDH))) {
982 setSISIDXREG(SISPART2, 0x00, 0x1f, p2_0);
983 }
984 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700986 } else if(ivideo->currentvbflags & CRT2_VGA) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700988 if(ivideo->vbflags2 & VB2_30xB) {
989 setSISIDXREG(SISPART2, 0x00, 0x1f, p2_0);
990 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700992 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700994 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995}
996
Thomas Winischhofer544393f2005-09-09 13:04:45 -0700997/* ------------- Callbacks from init.c/init301.c -------------- */
998
999#ifdef CONFIG_FB_SIS_300
1000unsigned int
1001sisfb_read_nbridge_pci_dword(struct SiS_Private *SiS_Pr, int reg)
1002{
1003 struct sis_video_info *ivideo = (struct sis_video_info *)SiS_Pr->ivideo;
1004 u32 val = 0;
1005
1006 pci_read_config_dword(ivideo->nbridge, reg, &val);
1007 return (unsigned int)val;
1008}
1009
1010void
1011sisfb_write_nbridge_pci_dword(struct SiS_Private *SiS_Pr, int reg, unsigned int val)
1012{
1013 struct sis_video_info *ivideo = (struct sis_video_info *)SiS_Pr->ivideo;
1014
1015 pci_write_config_dword(ivideo->nbridge, reg, (u32)val);
1016}
1017
1018unsigned int
1019sisfb_read_lpc_pci_dword(struct SiS_Private *SiS_Pr, int reg)
1020{
1021 struct sis_video_info *ivideo = (struct sis_video_info *)SiS_Pr->ivideo;
1022 u32 val = 0;
1023
1024 if(!ivideo->lpcdev) return 0;
1025
1026 pci_read_config_dword(ivideo->lpcdev, reg, &val);
1027 return (unsigned int)val;
1028}
1029#endif
1030
1031#ifdef CONFIG_FB_SIS_315
1032void
1033sisfb_write_nbridge_pci_byte(struct SiS_Private *SiS_Pr, int reg, unsigned char val)
1034{
1035 struct sis_video_info *ivideo = (struct sis_video_info *)SiS_Pr->ivideo;
1036
1037 pci_write_config_byte(ivideo->nbridge, reg, (u8)val);
1038}
1039
1040unsigned int
1041sisfb_read_mio_pci_word(struct SiS_Private *SiS_Pr, int reg)
1042{
1043 struct sis_video_info *ivideo = (struct sis_video_info *)SiS_Pr->ivideo;
1044 u16 val = 0;
1045
1046 if(!ivideo->lpcdev) return 0;
1047
1048 pci_read_config_word(ivideo->lpcdev, reg, &val);
1049 return (unsigned int)val;
1050}
1051#endif
1052
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053/* ----------- FBDev related routines for all series ----------- */
1054
1055static int
1056sisfb_get_cmap_len(const struct fb_var_screeninfo *var)
1057{
1058 return (var->bits_per_pixel == 8) ? 256 : 16;
1059}
1060
1061static void
1062sisfb_set_vparms(struct sis_video_info *ivideo)
1063{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001064 switch(ivideo->video_bpp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 case 8:
1066 ivideo->DstColor = 0x0000;
1067 ivideo->SiS310_AccelDepth = 0x00000000;
1068 ivideo->video_cmap_len = 256;
1069 break;
1070 case 16:
1071 ivideo->DstColor = 0x8000;
1072 ivideo->SiS310_AccelDepth = 0x00010000;
1073 ivideo->video_cmap_len = 16;
1074 break;
1075 case 32:
1076 ivideo->DstColor = 0xC000;
1077 ivideo->SiS310_AccelDepth = 0x00020000;
1078 ivideo->video_cmap_len = 16;
1079 break;
1080 default:
1081 ivideo->video_cmap_len = 16;
1082 printk(KERN_ERR "sisfb: Unsupported depth %d", ivideo->video_bpp);
1083 ivideo->accel = 0;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001084 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085}
1086
1087static int
1088sisfb_calc_maxyres(struct sis_video_info *ivideo, struct fb_var_screeninfo *var)
1089{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001090 int maxyres = ivideo->sisfb_mem / (var->xres_virtual * (var->bits_per_pixel >> 3));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091
1092 if(maxyres > 32767) maxyres = 32767;
1093
1094 return maxyres;
1095}
1096
1097static void
1098sisfb_calc_pitch(struct sis_video_info *ivideo, struct fb_var_screeninfo *var)
1099{
1100 ivideo->video_linelength = var->xres_virtual * (var->bits_per_pixel >> 3);
1101 ivideo->scrnpitchCRT1 = ivideo->video_linelength;
1102 if(!(ivideo->currentvbflags & CRT1_LCDA)) {
1103 if((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
1104 ivideo->scrnpitchCRT1 <<= 1;
1105 }
1106 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107}
1108
1109static void
1110sisfb_set_pitch(struct sis_video_info *ivideo)
1111{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001112 BOOLEAN isslavemode = FALSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 unsigned short HDisplay1 = ivideo->scrnpitchCRT1 >> 3;
1114 unsigned short HDisplay2 = ivideo->video_linelength >> 3;
1115
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001116 if(sisfb_bridgeisslave(ivideo)) isslavemode = TRUE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001118 /* We need to set pitch for CRT1 if bridge is in slave mode, too */
1119 if((ivideo->currentvbflags & VB_DISPTYPE_DISP1) || (isslavemode)) {
1120 outSISIDXREG(SISCR,0x13,(HDisplay1 & 0xFF));
1121 setSISIDXREG(SISSR,0x0E,0xF0,(HDisplay1 >> 8));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 }
1123
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001124 /* We must not set the pitch for CRT2 if bridge is in slave mode */
1125 if((ivideo->currentvbflags & VB_DISPTYPE_DISP2) && (!isslavemode)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 orSISIDXREG(SISPART1,ivideo->CRT2_write_enable,0x01);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001127 outSISIDXREG(SISPART1,0x07,(HDisplay2 & 0xFF));
1128 setSISIDXREG(SISPART1,0x09,0xF0,(HDisplay2 >> 8));
1129 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130}
1131
1132static void
1133sisfb_bpp_to_var(struct sis_video_info *ivideo, struct fb_var_screeninfo *var)
1134{
1135 ivideo->video_cmap_len = sisfb_get_cmap_len(var);
1136
1137 switch(var->bits_per_pixel) {
1138 case 8:
1139 var->red.offset = var->green.offset = var->blue.offset = 0;
1140 var->red.length = var->green.length = var->blue.length = 6;
1141 break;
1142 case 16:
1143 var->red.offset = 11;
1144 var->red.length = 5;
1145 var->green.offset = 5;
1146 var->green.length = 6;
1147 var->blue.offset = 0;
1148 var->blue.length = 5;
1149 var->transp.offset = 0;
1150 var->transp.length = 0;
1151 break;
1152 case 32:
1153 var->red.offset = 16;
1154 var->red.length = 8;
1155 var->green.offset = 8;
1156 var->green.length = 8;
1157 var->blue.offset = 0;
1158 var->blue.length = 8;
1159 var->transp.offset = 24;
1160 var->transp.length = 8;
1161 break;
1162 }
1163}
1164
1165static int
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001166sisfb_set_mode(struct sis_video_info *ivideo, int clrscrn)
1167{
1168 unsigned short modeno = ivideo->mode_no;
1169
1170 /* >=2.6.12's fbcon clears the screen anyway */
1171#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
1172 if(!clrscrn) modeno |= 0x80;
1173#else
1174 modeno |= 0x80;
1175#endif
1176
1177 outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
1178
1179 sisfb_pre_setmode(ivideo);
1180
1181 if(SiSSetMode(&ivideo->SiS_Pr, modeno) == 0) {
1182 printk(KERN_ERR "sisfb: Setting mode[0x%x] failed\n", ivideo->mode_no);
1183 return -EINVAL;
1184 }
1185
1186 outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
1187
1188 sisfb_post_setmode(ivideo);
1189
1190 return 0;
1191}
1192
1193
1194static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195sisfb_do_set_var(struct fb_var_screeninfo *var, int isactive, struct fb_info *info)
1196{
1197 struct sis_video_info *ivideo = (struct sis_video_info *)info->par;
1198 unsigned int htotal = 0, vtotal = 0;
1199 unsigned int drate = 0, hrate = 0;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001200 int found_mode = 0, ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 int old_mode;
1202 u32 pixclock;
1203
1204 htotal = var->left_margin + var->xres + var->right_margin + var->hsync_len;
1205
1206 vtotal = var->upper_margin + var->lower_margin + var->vsync_len;
1207
1208 pixclock = var->pixclock;
1209
1210 if((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) {
1211 vtotal += var->yres;
1212 vtotal <<= 1;
1213 } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
1214 vtotal += var->yres;
1215 vtotal <<= 2;
1216 } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
1217 vtotal += var->yres;
1218 vtotal <<= 1;
1219 } else vtotal += var->yres;
1220
1221 if(!(htotal) || !(vtotal)) {
1222 DPRINTK("sisfb: Invalid 'var' information\n");
1223 return -EINVAL;
1224 }
1225
1226 if(pixclock && htotal && vtotal) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001227 drate = 1000000000 / pixclock;
1228 hrate = (drate * 1000) / htotal;
1229 ivideo->refresh_rate = (unsigned int) (hrate * 2 / vtotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230 } else {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001231 ivideo->refresh_rate = 60;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232 }
1233
1234 old_mode = ivideo->sisfb_mode_idx;
1235 ivideo->sisfb_mode_idx = 0;
1236
1237 while( (sisbios_mode[ivideo->sisfb_mode_idx].mode_no[0] != 0) &&
1238 (sisbios_mode[ivideo->sisfb_mode_idx].xres <= var->xres) ) {
1239 if( (sisbios_mode[ivideo->sisfb_mode_idx].xres == var->xres) &&
1240 (sisbios_mode[ivideo->sisfb_mode_idx].yres == var->yres) &&
1241 (sisbios_mode[ivideo->sisfb_mode_idx].bpp == var->bits_per_pixel)) {
1242 ivideo->mode_no = sisbios_mode[ivideo->sisfb_mode_idx].mode_no[ivideo->mni];
1243 found_mode = 1;
1244 break;
1245 }
1246 ivideo->sisfb_mode_idx++;
1247 }
1248
1249 if(found_mode) {
1250 ivideo->sisfb_mode_idx = sisfb_validate_mode(ivideo,
1251 ivideo->sisfb_mode_idx, ivideo->currentvbflags);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001252 ivideo->mode_no = sisbios_mode[ivideo->sisfb_mode_idx].mode_no[ivideo->mni];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 } else {
1254 ivideo->sisfb_mode_idx = -1;
1255 }
1256
1257 if(ivideo->sisfb_mode_idx < 0) {
1258 printk(KERN_ERR "sisfb: Mode %dx%dx%d not supported\n", var->xres,
1259 var->yres, var->bits_per_pixel);
1260 ivideo->sisfb_mode_idx = old_mode;
1261 return -EINVAL;
1262 }
1263
1264 if(sisfb_search_refresh_rate(ivideo, ivideo->refresh_rate, ivideo->sisfb_mode_idx) == 0) {
1265 ivideo->rate_idx = sisbios_mode[ivideo->sisfb_mode_idx].rate_idx;
1266 ivideo->refresh_rate = 60;
1267 }
1268
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 if(isactive) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001270 /* If acceleration to be used? Need to know
1271 * before pre/post_set_mode()
1272 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273 ivideo->accel = 0;
1274#if defined(FBINFO_HWACCEL_DISABLED) && defined(FBINFO_HWACCEL_XPAN)
1275#ifdef STUPID_ACCELF_TEXT_SHIT
1276 if(var->accel_flags & FB_ACCELF_TEXT) {
1277 info->flags &= ~FBINFO_HWACCEL_DISABLED;
1278 } else {
1279 info->flags |= FBINFO_HWACCEL_DISABLED;
1280 }
1281#endif
1282 if(!(info->flags & FBINFO_HWACCEL_DISABLED)) ivideo->accel = -1;
1283#else
1284 if(var->accel_flags & FB_ACCELF_TEXT) ivideo->accel = -1;
1285#endif
1286
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001287 if((ret = sisfb_set_mode(ivideo, 1))) {
1288 return ret;
1289 }
1290
1291 ivideo->video_bpp = sisbios_mode[ivideo->sisfb_mode_idx].bpp;
1292 ivideo->video_width = sisbios_mode[ivideo->sisfb_mode_idx].xres;
1293 ivideo->video_height = sisbios_mode[ivideo->sisfb_mode_idx].yres;
1294
1295 sisfb_calc_pitch(ivideo, var);
1296 sisfb_set_pitch(ivideo);
1297
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 sisfb_set_vparms(ivideo);
1299
1300 ivideo->current_width = ivideo->video_width;
1301 ivideo->current_height = ivideo->video_height;
1302 ivideo->current_bpp = ivideo->video_bpp;
1303 ivideo->current_htotal = htotal;
1304 ivideo->current_vtotal = vtotal;
1305 ivideo->current_linelength = ivideo->video_linelength;
1306 ivideo->current_pixclock = var->pixclock;
1307 ivideo->current_refresh_rate = ivideo->refresh_rate;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001308 ivideo->sisfb_lastrates[ivideo->mode_no] = ivideo->refresh_rate;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 }
1310
1311 return 0;
1312}
1313
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001314static void
1315sisfb_set_base_CRT1(struct sis_video_info *ivideo, unsigned int base)
1316{
1317 outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
1318
1319 outSISIDXREG(SISCR, 0x0D, base & 0xFF);
1320 outSISIDXREG(SISCR, 0x0C, (base >> 8) & 0xFF);
1321 outSISIDXREG(SISSR, 0x0D, (base >> 16) & 0xFF);
1322 if(ivideo->sisvga_engine == SIS_315_VGA) {
1323 setSISIDXREG(SISSR, 0x37, 0xFE, (base >> 24) & 0x01);
1324 }
1325}
1326
1327static void
1328sisfb_set_base_CRT2(struct sis_video_info *ivideo, unsigned int base)
1329{
1330 if(ivideo->currentvbflags & VB_DISPTYPE_DISP2) {
1331 orSISIDXREG(SISPART1, ivideo->CRT2_write_enable, 0x01);
1332 outSISIDXREG(SISPART1, 0x06, (base & 0xFF));
1333 outSISIDXREG(SISPART1, 0x05, ((base >> 8) & 0xFF));
1334 outSISIDXREG(SISPART1, 0x04, ((base >> 16) & 0xFF));
1335 if(ivideo->sisvga_engine == SIS_315_VGA) {
1336 setSISIDXREG(SISPART1, 0x02, 0x7F, ((base >> 24) & 0x01) << 7);
1337 }
1338 }
1339}
1340
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341static int
1342sisfb_pan_var(struct sis_video_info *ivideo, struct fb_var_screeninfo *var)
1343{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 if(var->xoffset > (var->xres_virtual - var->xres)) {
1345 return -EINVAL;
1346 }
1347 if(var->yoffset > (var->yres_virtual - var->yres)) {
1348 return -EINVAL;
1349 }
1350
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001351 ivideo->current_base = (var->yoffset * var->xres_virtual) + var->xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001353 /* calculate base bpp dep. */
1354 switch(var->bits_per_pixel) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 case 32:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001356 break;
1357 case 16:
1358 ivideo->current_base >>= 1;
1359 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 case 8:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001361 default:
1362 ivideo->current_base >>= 2;
1363 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001365
1366 ivideo->current_base += (ivideo->video_offset >> 2);
1367
1368 sisfb_set_base_CRT1(ivideo, ivideo->current_base);
1369 sisfb_set_base_CRT2(ivideo, ivideo->current_base);
1370
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 return 0;
1372}
1373
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374static int
1375sisfb_open(struct fb_info *info, int user)
1376{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001377 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378}
1379
1380static int
1381sisfb_release(struct fb_info *info, int user)
1382{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001383 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384}
1385
1386static int
1387sisfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue,
1388 unsigned transp, struct fb_info *info)
1389{
1390 struct sis_video_info *ivideo = (struct sis_video_info *)info->par;
1391
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001392 if(regno >= sisfb_get_cmap_len(&info->var))
1393 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394
1395 switch(info->var.bits_per_pixel) {
1396 case 8:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001397 outSISREG(SISDACA, regno);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 outSISREG(SISDACD, (red >> 10));
1399 outSISREG(SISDACD, (green >> 10));
1400 outSISREG(SISDACD, (blue >> 10));
1401 if(ivideo->currentvbflags & VB_DISPTYPE_DISP2) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001402 outSISREG(SISDAC2A, regno);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403 outSISREG(SISDAC2D, (red >> 8));
1404 outSISREG(SISDAC2D, (green >> 8));
1405 outSISREG(SISDAC2D, (blue >> 8));
1406 }
1407 break;
1408 case 16:
1409 ((u32 *)(info->pseudo_palette))[regno] =
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001410 (red & 0xf800) |
1411 ((green & 0xfc00) >> 5) |
1412 ((blue & 0xf800) >> 11);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413 break;
1414 case 32:
1415 red >>= 8;
1416 green >>= 8;
1417 blue >>= 8;
1418 ((u32 *)(info->pseudo_palette))[regno] =
1419 (red << 16) | (green << 8) | (blue);
1420 break;
1421 }
1422 return 0;
1423}
1424
1425static int
1426sisfb_set_par(struct fb_info *info)
1427{
1428 int err;
1429
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001430 if((err = sisfb_do_set_var(&info->var, 1, info)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431 return err;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001432
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
1434 sisfb_get_fix(&info->fix, info->currcon, info);
1435#else
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001436 sisfb_get_fix(&info->fix, -1, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437#endif
1438 return 0;
1439}
1440
1441static int
1442sisfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
1443{
1444 struct sis_video_info *ivideo = (struct sis_video_info *)info->par;
1445 unsigned int htotal = 0, vtotal = 0, myrateindex = 0;
1446 unsigned int drate = 0, hrate = 0, maxyres;
1447 int found_mode = 0;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001448 int refresh_rate, search_idx, tidx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 BOOLEAN recalc_clock = FALSE;
1450 u32 pixclock;
1451
1452 htotal = var->left_margin + var->xres + var->right_margin + var->hsync_len;
1453
1454 vtotal = var->upper_margin + var->lower_margin + var->vsync_len;
1455
1456 pixclock = var->pixclock;
1457
1458 if((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) {
1459 vtotal += var->yres;
1460 vtotal <<= 1;
1461 } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
1462 vtotal += var->yres;
1463 vtotal <<= 2;
1464 } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
1465 vtotal += var->yres;
1466 vtotal <<= 1;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001467 } else
1468 vtotal += var->yres;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469
1470 if(!(htotal) || !(vtotal)) {
1471 SISFAIL("sisfb: no valid timing data");
1472 }
1473
1474 search_idx = 0;
1475 while( (sisbios_mode[search_idx].mode_no[0] != 0) &&
1476 (sisbios_mode[search_idx].xres <= var->xres) ) {
1477 if( (sisbios_mode[search_idx].xres == var->xres) &&
1478 (sisbios_mode[search_idx].yres == var->yres) &&
1479 (sisbios_mode[search_idx].bpp == var->bits_per_pixel)) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001480 if((tidx = sisfb_validate_mode(ivideo, search_idx,
1481 ivideo->currentvbflags)) > 0) {
1482 found_mode = 1;
1483 search_idx = tidx;
1484 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485 }
1486 }
1487 search_idx++;
1488 }
1489
1490 if(!found_mode) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001491 search_idx = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492 while(sisbios_mode[search_idx].mode_no[0] != 0) {
1493 if( (var->xres <= sisbios_mode[search_idx].xres) &&
1494 (var->yres <= sisbios_mode[search_idx].yres) &&
1495 (var->bits_per_pixel == sisbios_mode[search_idx].bpp) ) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001496 if((tidx = sisfb_validate_mode(ivideo,search_idx,
1497 ivideo->currentvbflags)) > 0) {
1498 found_mode = 1;
1499 search_idx = tidx;
1500 break;
1501 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502 }
1503 search_idx++;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001504 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 if(found_mode) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001506 printk(KERN_DEBUG
1507 "sisfb: Adapted from %dx%dx%d to %dx%dx%d\n",
1508 var->xres, var->yres, var->bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509 sisbios_mode[search_idx].xres,
1510 sisbios_mode[search_idx].yres,
1511 var->bits_per_pixel);
1512 var->xres = sisbios_mode[search_idx].xres;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001513 var->yres = sisbios_mode[search_idx].yres;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514 } else {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001515 printk(KERN_ERR
1516 "sisfb: Failed to find supported mode near %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 var->xres, var->yres, var->bits_per_pixel);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001518 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 }
1520 }
1521
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001522 if( ((ivideo->vbflags2 & VB2_LVDS) ||
1523 ((ivideo->vbflags2 & VB2_30xBDH) && (ivideo->currentvbflags & CRT2_LCD))) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 (var->bits_per_pixel == 8) ) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001525 /* Slave modes on LVDS and 301B-DH */
1526 refresh_rate = 60;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 recalc_clock = TRUE;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001528 } else if( (ivideo->current_htotal == htotal) &&
1529 (ivideo->current_vtotal == vtotal) &&
1530 (ivideo->current_pixclock == pixclock) ) {
1531 /* x=x & y=y & c=c -> assume depth change */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532 drate = 1000000000 / pixclock;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001533 hrate = (drate * 1000) / htotal;
1534 refresh_rate = (unsigned int) (hrate * 2 / vtotal);
1535 } else if( ( (ivideo->current_htotal != htotal) ||
1536 (ivideo->current_vtotal != vtotal) ) &&
1537 (ivideo->current_pixclock == var->pixclock) ) {
1538 /* x!=x | y!=y & c=c -> invalid pixclock */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 if(ivideo->sisfb_lastrates[sisbios_mode[search_idx].mode_no[ivideo->mni]]) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001540 refresh_rate =
1541 ivideo->sisfb_lastrates[sisbios_mode[search_idx].mode_no[ivideo->mni]];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 } else if(ivideo->sisfb_parm_rate != -1) {
1543 /* Sic, sisfb_parm_rate - want to know originally desired rate here */
1544 refresh_rate = ivideo->sisfb_parm_rate;
1545 } else {
1546 refresh_rate = 60;
1547 }
1548 recalc_clock = TRUE;
1549 } else if((pixclock) && (htotal) && (vtotal)) {
1550 drate = 1000000000 / pixclock;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001551 hrate = (drate * 1000) / htotal;
1552 refresh_rate = (unsigned int) (hrate * 2 / vtotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553 } else if(ivideo->current_refresh_rate) {
1554 refresh_rate = ivideo->current_refresh_rate;
1555 recalc_clock = TRUE;
1556 } else {
1557 refresh_rate = 60;
1558 recalc_clock = TRUE;
1559 }
1560
1561 myrateindex = sisfb_search_refresh_rate(ivideo, refresh_rate, search_idx);
1562
1563 /* Eventually recalculate timing and clock */
1564 if(recalc_clock) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001565 if(!myrateindex) myrateindex = sisbios_mode[search_idx].rate_idx;
1566 var->pixclock = (u32) (1000000000 / sisfb_mode_rate_to_dclock(&ivideo->SiS_Pr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 sisbios_mode[search_idx].mode_no[ivideo->mni],
1568 myrateindex));
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001569 sisfb_mode_rate_to_ddata(&ivideo->SiS_Pr,
1570 sisbios_mode[search_idx].mode_no[ivideo->mni],
1571 myrateindex, var);
1572 if((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
1573 var->pixclock <<= 1;
1574 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575 }
1576
1577 if(ivideo->sisfb_thismonitor.datavalid) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001578 if(!sisfb_verify_rate(ivideo, &ivideo->sisfb_thismonitor, search_idx,
1579 myrateindex, refresh_rate)) {
1580 printk(KERN_INFO
1581 "sisfb: WARNING: Refresh rate exceeds monitor specs!\n");
1582 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 }
1584
1585 /* Adapt RGB settings */
1586 sisfb_bpp_to_var(ivideo, var);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001587
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588 /* Sanity check for offsets */
1589 if(var->xoffset < 0) var->xoffset = 0;
1590 if(var->yoffset < 0) var->yoffset = 0;
1591
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001592 if(var->xres > var->xres_virtual)
1593 var->xres_virtual = var->xres;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594
1595 if(ivideo->sisfb_ypan) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001596 maxyres = sisfb_calc_maxyres(ivideo, var);
1597 if(ivideo->sisfb_max) {
1598 var->yres_virtual = maxyres;
1599 } else {
1600 if(var->yres_virtual > maxyres) {
1601 var->yres_virtual = maxyres;
1602 }
1603 }
1604 if(var->yres_virtual <= var->yres) {
1605 var->yres_virtual = var->yres;
1606 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607 } else {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001608 if(var->yres != var->yres_virtual) {
1609 var->yres_virtual = var->yres;
1610 }
1611 var->xoffset = 0;
1612 var->yoffset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001614
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615 /* Truncate offsets to maximum if too high */
1616 if(var->xoffset > var->xres_virtual - var->xres) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001617 var->xoffset = var->xres_virtual - var->xres - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618 }
1619
1620 if(var->yoffset > var->yres_virtual - var->yres) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001621 var->yoffset = var->yres_virtual - var->yres - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001623
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624 /* Set everything else to 0 */
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001625 var->red.msb_right =
1626 var->green.msb_right =
1627 var->blue.msb_right =
1628 var->transp.offset =
1629 var->transp.length =
1630 var->transp.msb_right = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631
1632 return 0;
1633}
1634
1635static int
1636sisfb_pan_display(struct fb_var_screeninfo *var, struct fb_info* info)
1637{
1638 struct sis_video_info *ivideo = (struct sis_video_info *)info->par;
1639 int err;
1640
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001641 if(var->xoffset > (var->xres_virtual - var->xres))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001644 if(var->yoffset > (var->yres_virtual - var->yres))
1645 return -EINVAL;
1646
1647 if(var->vmode & FB_VMODE_YWRAP)
1648 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649
1650 if(var->xoffset + info->var.xres > info->var.xres_virtual ||
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001651 var->yoffset + info->var.yres > info->var.yres_virtual)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001654 if((err = sisfb_pan_var(ivideo, var)) < 0)
1655 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
1657 info->var.xoffset = var->xoffset;
1658 info->var.yoffset = var->yoffset;
1659
1660 return 0;
1661}
1662
1663static int
1664sisfb_blank(int blank, struct fb_info *info)
1665{
1666 struct sis_video_info *ivideo = (struct sis_video_info *)info->par;
1667
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001668 return sisfb_myblank(ivideo, blank);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669}
1670
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671/* ----------- FBDev related routines for all series ---------- */
1672
Christoph Hellwig67a66802006-01-14 13:21:25 -08001673#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
1674static int sisfb_ioctl(struct fb_info *info, unsigned int cmd,
1675 unsigned long arg)
1676#else
1677static int sisfb_ioctl(struct inode *inode, struct file *file,
1678 unsigned int cmd, unsigned long arg,
1679 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681{
1682 struct sis_video_info *ivideo = (struct sis_video_info *)info->par;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001683 struct sis_memreq sismemreq;
1684 struct fb_vblank sisvbblank;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685 u32 gpu32 = 0;
1686#ifndef __user
1687#define __user
1688#endif
1689 u32 __user *argp = (u32 __user *)arg;
1690
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001691 switch(cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692 case FBIO_ALLOC:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001693 if(!capable(CAP_SYS_RAWIO))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 return -EPERM;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001695
1696 if(copy_from_user(&sismemreq, (void __user *)arg, sizeof(sismemreq)))
1697 return -EFAULT;
1698
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699 sis_malloc(&sismemreq);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001700
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701 if(copy_to_user((void __user *)arg, &sismemreq, sizeof(sismemreq))) {
1702 sis_free((u32)sismemreq.offset);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001703 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 }
1705 break;
1706
1707 case FBIO_FREE:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001708 if(!capable(CAP_SYS_RAWIO))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709 return -EPERM;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001710
1711 if(get_user(gpu32, argp))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712 return -EFAULT;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001713
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714 sis_free(gpu32);
1715 break;
1716
1717 case FBIOGET_VBLANK:
1718 sisvbblank.count = 0;
1719 sisvbblank.flags = sisfb_setupvbblankflags(ivideo, &sisvbblank.vcount, &sisvbblank.hcount);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001720
1721 if(copy_to_user((void __user *)arg, &sisvbblank, sizeof(sisvbblank)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722 return -EFAULT;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001723
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724 break;
1725
1726 case SISFB_GET_INFO_SIZE:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001727 return put_user(sizeof(struct sisfb_info), argp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728
1729 case SISFB_GET_INFO_OLD:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001730 if(ivideo->warncount++ < 10)
1731 printk(KERN_INFO
1732 "sisfb: Deprecated ioctl call received - update your application!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 case SISFB_GET_INFO: /* For communication with X driver */
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001734 ivideo->sisfb_infoblock.sisfb_id = SISFB_ID;
1735 ivideo->sisfb_infoblock.sisfb_version = VER_MAJOR;
1736 ivideo->sisfb_infoblock.sisfb_revision = VER_MINOR;
1737 ivideo->sisfb_infoblock.sisfb_patchlevel = VER_LEVEL;
1738 ivideo->sisfb_infoblock.chip_id = ivideo->chip_id;
1739 ivideo->sisfb_infoblock.sisfb_pci_vendor = ivideo->chip_vendor;
1740 ivideo->sisfb_infoblock.memory = ivideo->video_size / 1024;
1741 ivideo->sisfb_infoblock.heapstart = ivideo->heapstart / 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742 if(ivideo->modechanged) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001743 ivideo->sisfb_infoblock.fbvidmode = ivideo->mode_no;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744 } else {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001745 ivideo->sisfb_infoblock.fbvidmode = ivideo->modeprechange;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001747 ivideo->sisfb_infoblock.sisfb_caps = ivideo->caps;
1748 ivideo->sisfb_infoblock.sisfb_tqlen = ivideo->cmdQueueSize / 1024;
1749 ivideo->sisfb_infoblock.sisfb_pcibus = ivideo->pcibus;
1750 ivideo->sisfb_infoblock.sisfb_pcislot = ivideo->pcislot;
1751 ivideo->sisfb_infoblock.sisfb_pcifunc = ivideo->pcifunc;
1752 ivideo->sisfb_infoblock.sisfb_lcdpdc = ivideo->detectedpdc;
1753 ivideo->sisfb_infoblock.sisfb_lcdpdca = ivideo->detectedpdca;
1754 ivideo->sisfb_infoblock.sisfb_lcda = ivideo->detectedlcda;
1755 ivideo->sisfb_infoblock.sisfb_vbflags = ivideo->vbflags;
1756 ivideo->sisfb_infoblock.sisfb_currentvbflags = ivideo->currentvbflags;
1757 ivideo->sisfb_infoblock.sisfb_scalelcd = ivideo->SiS_Pr.UsePanelScaler;
1758 ivideo->sisfb_infoblock.sisfb_specialtiming = ivideo->SiS_Pr.SiS_CustomT;
1759 ivideo->sisfb_infoblock.sisfb_haveemi = ivideo->SiS_Pr.HaveEMI ? 1 : 0;
1760 ivideo->sisfb_infoblock.sisfb_haveemilcd = ivideo->SiS_Pr.HaveEMILCD ? 1 : 0;
1761 ivideo->sisfb_infoblock.sisfb_emi30 = ivideo->SiS_Pr.EMI_30;
1762 ivideo->sisfb_infoblock.sisfb_emi31 = ivideo->SiS_Pr.EMI_31;
1763 ivideo->sisfb_infoblock.sisfb_emi32 = ivideo->SiS_Pr.EMI_32;
1764 ivideo->sisfb_infoblock.sisfb_emi33 = ivideo->SiS_Pr.EMI_33;
1765 ivideo->sisfb_infoblock.sisfb_tvxpos = (u16)(ivideo->tvxpos + 32);
1766 ivideo->sisfb_infoblock.sisfb_tvypos = (u16)(ivideo->tvypos + 32);
1767 ivideo->sisfb_infoblock.sisfb_heapsize = ivideo->sisfb_heap_size / 1024;
1768 ivideo->sisfb_infoblock.sisfb_videooffset = ivideo->video_offset;
1769 ivideo->sisfb_infoblock.sisfb_curfstn = ivideo->curFSTN;
1770 ivideo->sisfb_infoblock.sisfb_curdstn = ivideo->curDSTN;
1771 ivideo->sisfb_infoblock.sisfb_vbflags2 = ivideo->vbflags2;
1772 ivideo->sisfb_infoblock.sisfb_can_post = ivideo->sisfb_can_post ? 1 : 0;
1773 ivideo->sisfb_infoblock.sisfb_card_posted = ivideo->sisfb_card_posted ? 1 : 0;
1774 ivideo->sisfb_infoblock.sisfb_was_boot_device = ivideo->sisfb_was_boot_device ? 1 : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001775
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001776 if(copy_to_user((void __user *)arg, &ivideo->sisfb_infoblock,
1777 sizeof(ivideo->sisfb_infoblock)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001778 return -EFAULT;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001779
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780 break;
1781
1782 case SISFB_GET_VBRSTATUS_OLD:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001783 if(ivideo->warncount++ < 10)
1784 printk(KERN_INFO
1785 "sisfb: Deprecated ioctl call received - update your application!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786 case SISFB_GET_VBRSTATUS:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001787 if(sisfb_CheckVBRetrace(ivideo))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788 return put_user((u32)1, argp);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001789 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790 return put_user((u32)0, argp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791
1792 case SISFB_GET_AUTOMAXIMIZE_OLD:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001793 if(ivideo->warncount++ < 10)
1794 printk(KERN_INFO
1795 "sisfb: Deprecated ioctl call received - update your application!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796 case SISFB_GET_AUTOMAXIMIZE:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001797 if(ivideo->sisfb_max)
1798 return put_user((u32)1, argp);
1799 else
1800 return put_user((u32)0, argp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001801
1802 case SISFB_SET_AUTOMAXIMIZE_OLD:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001803 if(ivideo->warncount++ < 10)
1804 printk(KERN_INFO
1805 "sisfb: Deprecated ioctl call received - update your application!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806 case SISFB_SET_AUTOMAXIMIZE:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001807 if(get_user(gpu32, argp))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808 return -EFAULT;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001809
Linus Torvalds1da177e2005-04-16 15:20:36 -07001810 ivideo->sisfb_max = (gpu32) ? 1 : 0;
1811 break;
1812
1813 case SISFB_SET_TVPOSOFFSET:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001814 if(get_user(gpu32, argp))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001815 return -EFAULT;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001816
Linus Torvalds1da177e2005-04-16 15:20:36 -07001817 sisfb_set_TVxposoffset(ivideo, ((int)(gpu32 >> 16)) - 32);
1818 sisfb_set_TVyposoffset(ivideo, ((int)(gpu32 & 0xffff)) - 32);
1819 break;
1820
1821 case SISFB_GET_TVPOSOFFSET:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001822 return put_user((u32)(((ivideo->tvxpos+32)<<16)|((ivideo->tvypos+32)&0xffff)),
1823 argp);
1824
1825 case SISFB_COMMAND:
1826 if(copy_from_user(&ivideo->sisfb_command, (void __user *)arg,
1827 sizeof(struct sisfb_cmd)))
1828 return -EFAULT;
1829
1830 sisfb_handle_command(ivideo, &ivideo->sisfb_command);
1831
1832 if(copy_to_user((void __user *)arg, &ivideo->sisfb_command,
1833 sizeof(struct sisfb_cmd)))
1834 return -EFAULT;
1835
1836 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837
1838 case SISFB_SET_LOCK:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001839 if(get_user(gpu32, argp))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001840 return -EFAULT;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001841
Linus Torvalds1da177e2005-04-16 15:20:36 -07001842 ivideo->sisfblocked = (gpu32) ? 1 : 0;
1843 break;
1844
1845 default:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001846#ifdef SIS_NEW_CONFIG_COMPAT
Linus Torvalds1da177e2005-04-16 15:20:36 -07001847 return -ENOIOCTLCMD;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001848#else
1849 return -EINVAL;
1850#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851 }
1852 return 0;
1853}
1854
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855static int
1856sisfb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
1857{
1858 struct sis_video_info *ivideo = (struct sis_video_info *)info->par;
1859
1860 memset(fix, 0, sizeof(struct fb_fix_screeninfo));
1861
1862 strcpy(fix->id, ivideo->myid);
1863
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001864 fix->smem_start = ivideo->video_base + ivideo->video_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865 fix->smem_len = ivideo->sisfb_mem;
1866 fix->type = FB_TYPE_PACKED_PIXELS;
1867 fix->type_aux = 0;
1868 fix->visual = (ivideo->video_bpp == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
1869 fix->xpanstep = 1;
1870 fix->ypanstep = (ivideo->sisfb_ypan) ? 1 : 0;
1871 fix->ywrapstep = 0;
1872 fix->line_length = ivideo->video_linelength;
1873 fix->mmio_start = ivideo->mmio_base;
1874 fix->mmio_len = ivideo->mmio_size;
1875 if(ivideo->sisvga_engine == SIS_300_VGA) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001876 fix->accel = FB_ACCEL_SIS_GLAMOUR;
1877 } else if((ivideo->chip == SIS_330) ||
1878 (ivideo->chip == SIS_760) ||
1879 (ivideo->chip == SIS_761)) {
1880 fix->accel = FB_ACCEL_SIS_XABRE;
1881 } else if(ivideo->chip == XGI_20) {
1882 fix->accel = FB_ACCEL_XGI_VOLARI_Z;
1883 } else if(ivideo->chip >= XGI_40) {
1884 fix->accel = FB_ACCEL_XGI_VOLARI_V;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885 } else {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001886 fix->accel = FB_ACCEL_SIS_GLAMOUR_2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887 }
1888
1889 return 0;
1890}
1891
1892/* ---------------- fb_ops structures ----------------- */
1893
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894static struct fb_ops sisfb_ops = {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001895 .owner = THIS_MODULE,
1896 .fb_open = sisfb_open,
1897 .fb_release = sisfb_release,
1898 .fb_check_var = sisfb_check_var,
1899 .fb_set_par = sisfb_set_par,
1900 .fb_setcolreg = sisfb_setcolreg,
1901 .fb_pan_display = sisfb_pan_display,
1902 .fb_blank = sisfb_blank,
1903 .fb_fillrect = fbcon_sis_fillrect,
1904 .fb_copyarea = fbcon_sis_copyarea,
1905 .fb_imageblit = cfb_imageblit,
Antonino A. Daplasc465e052005-11-07 01:00:35 -08001906#ifdef CONFIG_FB_SOFT_CURSOR
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001907 .fb_cursor = soft_cursor,
Antonino A. Daplasc465e052005-11-07 01:00:35 -08001908#endif
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001909 .fb_sync = fbcon_sis_sync,
1910#ifdef SIS_NEW_CONFIG_COMPAT
Christoph Hellwig67a66802006-01-14 13:21:25 -08001911 .fb_compat_ioctl= sisfb_ioctl,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912#endif
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001913 .fb_ioctl = sisfb_ioctl
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915
1916/* ---------------- Chip generation dependent routines ---------------- */
1917
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001918static struct pci_dev * __devinit
1919sisfb_get_northbridge(int basechipid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920{
1921 struct pci_dev *pdev = NULL;
1922 int nbridgenum, nbridgeidx, i;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001923 static const unsigned short nbridgeids[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924 PCI_DEVICE_ID_SI_540, /* for SiS 540 VGA */
1925 PCI_DEVICE_ID_SI_630, /* for SiS 630/730 VGA */
1926 PCI_DEVICE_ID_SI_730,
1927 PCI_DEVICE_ID_SI_550, /* for SiS 550 VGA */
1928 PCI_DEVICE_ID_SI_650, /* for SiS 650/651/740 VGA */
1929 PCI_DEVICE_ID_SI_651,
1930 PCI_DEVICE_ID_SI_740,
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001931 PCI_DEVICE_ID_SI_661, /* for SiS 661/741/660/760/761 VGA */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932 PCI_DEVICE_ID_SI_741,
1933 PCI_DEVICE_ID_SI_660,
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001934 PCI_DEVICE_ID_SI_760,
1935 PCI_DEVICE_ID_SI_761
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936 };
1937
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001938 switch(basechipid) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939#ifdef CONFIG_FB_SIS_300
1940 case SIS_540: nbridgeidx = 0; nbridgenum = 1; break;
1941 case SIS_630: nbridgeidx = 1; nbridgenum = 2; break;
1942#endif
1943#ifdef CONFIG_FB_SIS_315
1944 case SIS_550: nbridgeidx = 3; nbridgenum = 1; break;
1945 case SIS_650: nbridgeidx = 4; nbridgenum = 3; break;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001946 case SIS_660: nbridgeidx = 7; nbridgenum = 5; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947#endif
1948 default: return NULL;
1949 }
1950 for(i = 0; i < nbridgenum; i++) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001951 if((pdev = SIS_PCI_GET_DEVICE(PCI_VENDOR_ID_SI,
1952 nbridgeids[nbridgeidx+i], NULL)))
1953 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954 }
1955 return pdev;
1956}
1957
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001958static int __devinit
1959sisfb_get_dram_size(struct sis_video_info *ivideo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960{
1961#if defined(CONFIG_FB_SIS_300) || defined(CONFIG_FB_SIS_315)
1962 u8 reg;
1963#endif
1964
1965 ivideo->video_size = 0;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001966 ivideo->UMAsize = ivideo->LFBsize = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967
1968 switch(ivideo->chip) {
1969#ifdef CONFIG_FB_SIS_300
1970 case SIS_300:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001971 inSISIDXREG(SISSR, 0x14, reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001972 ivideo->video_size = ((reg & 0x3F) + 1) << 20;
1973 break;
1974 case SIS_540:
1975 case SIS_630:
1976 case SIS_730:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001977 if(!ivideo->nbridge)
1978 return -1;
1979 pci_read_config_byte(ivideo->nbridge, 0x63, &reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980 ivideo->video_size = 1 << (((reg & 0x70) >> 4) + 21);
1981 break;
1982#endif
1983#ifdef CONFIG_FB_SIS_315
1984 case SIS_315H:
1985 case SIS_315PRO:
1986 case SIS_315:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001987 inSISIDXREG(SISSR, 0x14, reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001988 ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20;
1989 switch((reg >> 2) & 0x03) {
1990 case 0x01:
1991 case 0x03:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001992 ivideo->video_size <<= 1;
1993 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994 case 0x02:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001995 ivideo->video_size += (ivideo->video_size/2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001997 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998 case SIS_330:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07001999 inSISIDXREG(SISSR, 0x14, reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000 ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20;
2001 if(reg & 0x0c) ivideo->video_size <<= 1;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002002 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003 case SIS_550:
2004 case SIS_650:
2005 case SIS_740:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002006 inSISIDXREG(SISSR, 0x14, reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007 ivideo->video_size = (((reg & 0x3f) + 1) << 2) << 20;
2008 break;
2009 case SIS_661:
2010 case SIS_741:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002011 inSISIDXREG(SISCR, 0x79, reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012 ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002013 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014 case SIS_660:
2015 case SIS_760:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002016 case SIS_761:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002017 inSISIDXREG(SISCR, 0x79, reg);
2018 reg = (reg & 0xf0) >> 4;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002019 if(reg) {
2020 ivideo->video_size = (1 << reg) << 20;
2021 ivideo->UMAsize = ivideo->video_size;
2022 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023 inSISIDXREG(SISCR, 0x78, reg);
2024 reg &= 0x30;
2025 if(reg) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002026 if(reg == 0x10) {
2027 ivideo->LFBsize = (32 << 20);
2028 } else {
2029 ivideo->LFBsize = (64 << 20);
2030 }
2031 ivideo->video_size += ivideo->LFBsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002033 break;
2034 case SIS_340:
2035 case XGI_20:
2036 case XGI_40:
2037 inSISIDXREG(SISSR, 0x14, reg);
2038 ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20;
2039 if(ivideo->chip != XGI_20) {
2040 reg = (reg & 0x0c) >> 2;
2041 if(ivideo->revision_id == 2) {
2042 if(reg & 0x01) reg = 0x02;
2043 else reg = 0x00;
2044 }
2045 if(reg == 0x02) ivideo->video_size <<= 1;
2046 else if(reg == 0x03) ivideo->video_size <<= 2;
2047 }
2048 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049#endif
2050 default:
2051 return -1;
2052 }
2053 return 0;
2054}
2055
2056/* -------------- video bridge device detection --------------- */
2057
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002058static void __devinit
2059sisfb_detect_VB_connect(struct sis_video_info *ivideo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060{
2061 u8 cr32, temp;
2062
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002063 /* No CRT2 on XGI Z7 */
2064 if(ivideo->chip == XGI_20) {
2065 ivideo->sisfb_crt1off = 0;
2066 return;
2067 }
2068
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069#ifdef CONFIG_FB_SIS_300
2070 if(ivideo->sisvga_engine == SIS_300_VGA) {
2071 inSISIDXREG(SISSR, 0x17, temp);
2072 if((temp & 0x0F) && (ivideo->chip != SIS_300)) {
2073 /* PAL/NTSC is stored on SR16 on such machines */
2074 if(!(ivideo->vbflags & (TV_PAL | TV_NTSC | TV_PALM | TV_PALN))) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002075 inSISIDXREG(SISSR, 0x16, temp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076 if(temp & 0x20)
2077 ivideo->vbflags |= TV_PAL;
2078 else
2079 ivideo->vbflags |= TV_NTSC;
2080 }
2081 }
2082 }
2083#endif
2084
2085 inSISIDXREG(SISCR, 0x32, cr32);
2086
2087 if(cr32 & SIS_CRT1) {
2088 ivideo->sisfb_crt1off = 0;
2089 } else {
2090 ivideo->sisfb_crt1off = (cr32 & 0xDF) ? 1 : 0;
2091 }
2092
2093 ivideo->vbflags &= ~(CRT2_TV | CRT2_LCD | CRT2_VGA);
2094
2095 if(cr32 & SIS_VB_TV) ivideo->vbflags |= CRT2_TV;
2096 if(cr32 & SIS_VB_LCD) ivideo->vbflags |= CRT2_LCD;
2097 if(cr32 & SIS_VB_CRT2) ivideo->vbflags |= CRT2_VGA;
2098
2099 /* Check given parms for hardware compatibility.
2100 * (Cannot do this in the search_xx routines since we don't
2101 * know what hardware we are running on then)
2102 */
2103
2104 if(ivideo->chip != SIS_550) {
2105 ivideo->sisfb_dstn = ivideo->sisfb_fstn = 0;
2106 }
2107
2108 if(ivideo->sisfb_tvplug != -1) {
2109 if( (ivideo->sisvga_engine != SIS_315_VGA) ||
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002110 (!(ivideo->vbflags2 & VB2_SISYPBPRBRIDGE)) ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111 if(ivideo->sisfb_tvplug & TV_YPBPR) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002112 ivideo->sisfb_tvplug = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113 printk(KERN_ERR "sisfb: YPbPr not supported\n");
2114 }
2115 }
2116 }
2117 if(ivideo->sisfb_tvplug != -1) {
2118 if( (ivideo->sisvga_engine != SIS_315_VGA) ||
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002119 (!(ivideo->vbflags2 & VB2_SISHIVISIONBRIDGE)) ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002120 if(ivideo->sisfb_tvplug & TV_HIVISION) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002121 ivideo->sisfb_tvplug = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002122 printk(KERN_ERR "sisfb: HiVision not supported\n");
2123 }
2124 }
2125 }
2126 if(ivideo->sisfb_tvstd != -1) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002127 if( (!(ivideo->vbflags2 & VB2_SISBRIDGE)) &&
2128 (!((ivideo->sisvga_engine == SIS_315_VGA) &&
2129 (ivideo->vbflags2 & VB2_CHRONTEL))) ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130 if(ivideo->sisfb_tvstd & (TV_PALN | TV_PALN | TV_NTSCJ)) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002131 ivideo->sisfb_tvstd = -1;
2132 printk(KERN_ERR "sisfb: PALM/PALN/NTSCJ not supported\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133 }
2134 }
2135 }
2136
2137 /* Detect/set TV plug & type */
2138 if(ivideo->sisfb_tvplug != -1) {
2139 ivideo->vbflags |= ivideo->sisfb_tvplug;
2140 } else {
2141 if(cr32 & SIS_VB_YPBPR) ivideo->vbflags |= (TV_YPBPR|TV_YPBPR525I); /* default: 480i */
2142 else if(cr32 & SIS_VB_HIVISION) ivideo->vbflags |= TV_HIVISION;
2143 else if(cr32 & SIS_VB_SCART) ivideo->vbflags |= TV_SCART;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002144 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145 if(cr32 & SIS_VB_SVIDEO) ivideo->vbflags |= TV_SVIDEO;
2146 if(cr32 & SIS_VB_COMPOSITE) ivideo->vbflags |= TV_AVIDEO;
2147 }
2148 }
2149
2150 if(!(ivideo->vbflags & (TV_YPBPR | TV_HIVISION))) {
2151 if(ivideo->sisfb_tvstd != -1) {
2152 ivideo->vbflags &= ~(TV_NTSC | TV_PAL | TV_PALM | TV_PALN | TV_NTSCJ);
2153 ivideo->vbflags |= ivideo->sisfb_tvstd;
2154 }
2155 if(ivideo->vbflags & TV_SCART) {
2156 ivideo->vbflags &= ~(TV_NTSC | TV_PALM | TV_PALN | TV_NTSCJ);
2157 ivideo->vbflags |= TV_PAL;
2158 }
2159 if(!(ivideo->vbflags & (TV_PAL | TV_NTSC | TV_PALM | TV_PALN | TV_NTSCJ))) {
2160 if(ivideo->sisvga_engine == SIS_300_VGA) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002161 inSISIDXREG(SISSR, 0x38, temp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162 if(temp & 0x01) ivideo->vbflags |= TV_PAL;
2163 else ivideo->vbflags |= TV_NTSC;
2164 } else if((ivideo->chip <= SIS_315PRO) || (ivideo->chip >= SIS_330)) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002165 inSISIDXREG(SISSR, 0x38, temp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002166 if(temp & 0x01) ivideo->vbflags |= TV_PAL;
2167 else ivideo->vbflags |= TV_NTSC;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002168 } else {
2169 inSISIDXREG(SISCR, 0x79, temp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170 if(temp & 0x20) ivideo->vbflags |= TV_PAL;
2171 else ivideo->vbflags |= TV_NTSC;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002172 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002173 }
2174 }
2175
2176 /* Copy forceCRT1 option to CRT1off if option is given */
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002177 if(ivideo->sisfb_forcecrt1 != -1) {
2178 ivideo->sisfb_crt1off = (ivideo->sisfb_forcecrt1) ? 0 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179 }
2180}
2181
2182/* ------------------ Sensing routines ------------------ */
2183
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002184static BOOLEAN __devinit
2185sisfb_test_DDC1(struct sis_video_info *ivideo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002186{
2187 unsigned short old;
2188 int count = 48;
2189
2190 old = SiS_ReadDDC1Bit(&ivideo->SiS_Pr);
2191 do {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002192 if(old != SiS_ReadDDC1Bit(&ivideo->SiS_Pr)) break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002193 } while(count--);
2194 return (count == -1) ? FALSE : TRUE;
2195}
2196
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002197static void __devinit
2198sisfb_sense_crt1(struct sis_video_info *ivideo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199{
2200 BOOLEAN mustwait = FALSE;
2201 u8 sr1F, cr17;
2202#ifdef CONFIG_FB_SIS_315
2203 u8 cr63=0;
2204#endif
2205 u16 temp = 0xffff;
2206 int i;
2207
2208 inSISIDXREG(SISSR,0x1F,sr1F);
2209 orSISIDXREG(SISSR,0x1F,0x04);
2210 andSISIDXREG(SISSR,0x1F,0x3F);
2211 if(sr1F & 0xc0) mustwait = TRUE;
2212
2213#ifdef CONFIG_FB_SIS_315
2214 if(ivideo->sisvga_engine == SIS_315_VGA) {
2215 inSISIDXREG(SISCR,ivideo->SiS_Pr.SiS_MyCR63,cr63);
2216 cr63 &= 0x40;
2217 andSISIDXREG(SISCR,ivideo->SiS_Pr.SiS_MyCR63,0xBF);
2218 }
2219#endif
2220
2221 inSISIDXREG(SISCR,0x17,cr17);
2222 cr17 &= 0x80;
2223 if(!cr17) {
2224 orSISIDXREG(SISCR,0x17,0x80);
2225 mustwait = TRUE;
2226 outSISIDXREG(SISSR, 0x00, 0x01);
2227 outSISIDXREG(SISSR, 0x00, 0x03);
2228 }
2229
2230 if(mustwait) {
2231 for(i=0; i < 10; i++) sisfbwaitretracecrt1(ivideo);
2232 }
2233
2234#ifdef CONFIG_FB_SIS_315
2235 if(ivideo->chip >= SIS_330) {
2236 andSISIDXREG(SISCR,0x32,~0x20);
2237 if(ivideo->chip >= SIS_340) {
2238 outSISIDXREG(SISCR, 0x57, 0x4a);
2239 } else {
2240 outSISIDXREG(SISCR, 0x57, 0x5f);
2241 }
2242 orSISIDXREG(SISCR, 0x53, 0x02);
2243 while((inSISREG(SISINPSTAT)) & 0x01) break;
2244 while(!((inSISREG(SISINPSTAT)) & 0x01)) break;
2245 if((inSISREG(SISMISCW)) & 0x10) temp = 1;
2246 andSISIDXREG(SISCR, 0x53, 0xfd);
2247 andSISIDXREG(SISCR, 0x57, 0x00);
2248 }
2249#endif
2250
2251 if(temp == 0xffff) {
2252 i = 3;
2253 do {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002254 temp = SiS_HandleDDC(&ivideo->SiS_Pr, ivideo->vbflags,
2255 ivideo->sisvga_engine, 0, 0, NULL, ivideo->vbflags2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002256 } while(((temp == 0) || (temp == 0xffff)) && i--);
2257
2258 if((temp == 0) || (temp == 0xffff)) {
2259 if(sisfb_test_DDC1(ivideo)) temp = 1;
2260 }
2261 }
2262
2263 if((temp) && (temp != 0xffff)) {
2264 orSISIDXREG(SISCR,0x32,0x20);
2265 }
2266
2267#ifdef CONFIG_FB_SIS_315
2268 if(ivideo->sisvga_engine == SIS_315_VGA) {
2269 setSISIDXREG(SISCR,ivideo->SiS_Pr.SiS_MyCR63,0xBF,cr63);
2270 }
2271#endif
2272
2273 setSISIDXREG(SISCR,0x17,0x7F,cr17);
2274
2275 outSISIDXREG(SISSR,0x1F,sr1F);
2276}
2277
2278/* Determine and detect attached devices on SiS30x */
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002279static void __devinit
2280SiS_SenseLCD(struct sis_video_info *ivideo)
2281{
2282 unsigned char buffer[256];
2283 unsigned short temp, realcrtno, i;
2284 u8 reg, cr37 = 0, paneltype = 0;
2285 u16 xres, yres;
2286
2287 ivideo->SiS_Pr.PanelSelfDetected = FALSE;
2288
2289 /* LCD detection only for TMDS bridges */
2290 if(!(ivideo->vbflags2 & VB2_SISTMDSBRIDGE))
2291 return;
2292 if(ivideo->vbflags2 & VB2_30xBDH)
2293 return;
2294
2295 /* If LCD already set up by BIOS, skip it */
2296 inSISIDXREG(SISCR, 0x32, reg);
2297 if(reg & 0x08)
2298 return;
2299
2300 realcrtno = 1;
2301 if(ivideo->SiS_Pr.DDCPortMixup)
2302 realcrtno = 0;
2303
2304 /* Check DDC capabilities */
2305 temp = SiS_HandleDDC(&ivideo->SiS_Pr, ivideo->vbflags, ivideo->sisvga_engine,
2306 realcrtno, 0, &buffer[0], ivideo->vbflags2);
2307
2308 if((!temp) || (temp == 0xffff) || (!(temp & 0x02)))
2309 return;
2310
2311 /* Read DDC data */
2312 i = 3; /* Number of retrys */
2313 do {
2314 temp = SiS_HandleDDC(&ivideo->SiS_Pr, ivideo->vbflags,
2315 ivideo->sisvga_engine, realcrtno, 1,
2316 &buffer[0], ivideo->vbflags2);
2317 } while((temp) && i--);
2318
2319 if(temp)
2320 return;
2321
2322 /* No digital device */
2323 if(!(buffer[0x14] & 0x80))
2324 return;
2325
2326 /* First detailed timing preferred timing? */
2327 if(!(buffer[0x18] & 0x02))
2328 return;
2329
2330 xres = buffer[0x38] | ((buffer[0x3a] & 0xf0) << 4);
2331 yres = buffer[0x3b] | ((buffer[0x3d] & 0xf0) << 4);
2332
2333 switch(xres) {
2334 case 1024:
2335 if(yres == 768)
2336 paneltype = 0x02;
2337 break;
2338 case 1280:
2339 if(yres == 1024)
2340 paneltype = 0x03;
2341 break;
2342 case 1600:
2343 if((yres == 1200) && (ivideo->vbflags2 & VB2_30xC))
2344 paneltype = 0x0b;
2345 break;
2346 }
2347
2348 if(!paneltype)
2349 return;
2350
2351 if(buffer[0x23])
2352 cr37 |= 0x10;
2353
2354 if((buffer[0x47] & 0x18) == 0x18)
2355 cr37 |= ((((buffer[0x47] & 0x06) ^ 0x06) << 5) | 0x20);
2356 else
2357 cr37 |= 0xc0;
2358
2359 outSISIDXREG(SISCR, 0x36, paneltype);
2360 cr37 &= 0xf1;
2361 setSISIDXREG(SISCR, 0x37, 0x0c, cr37);
2362 orSISIDXREG(SISCR, 0x32, 0x08);
2363
2364 ivideo->SiS_Pr.PanelSelfDetected = TRUE;
2365}
2366
2367static int __devinit
2368SISDoSense(struct sis_video_info *ivideo, u16 type, u16 test)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002369{
2370 int temp, mytest, result, i, j;
2371
2372 for(j = 0; j < 10; j++) {
2373 result = 0;
2374 for(i = 0; i < 3; i++) {
2375 mytest = test;
2376 outSISIDXREG(SISPART4,0x11,(type & 0x00ff));
2377 temp = (type >> 8) | (mytest & 0x00ff);
2378 setSISIDXREG(SISPART4,0x10,0xe0,temp);
2379 SiS_DDC2Delay(&ivideo->SiS_Pr, 0x1500);
2380 mytest >>= 8;
2381 mytest &= 0x7f;
2382 inSISIDXREG(SISPART4,0x03,temp);
2383 temp ^= 0x0e;
2384 temp &= mytest;
2385 if(temp == mytest) result++;
2386#if 1
2387 outSISIDXREG(SISPART4,0x11,0x00);
2388 andSISIDXREG(SISPART4,0x10,0xe0);
2389 SiS_DDC2Delay(&ivideo->SiS_Pr, 0x1000);
2390#endif
2391 }
2392 if((result == 0) || (result >= 2)) break;
2393 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002394 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002395}
2396
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002397static void __devinit
2398SiS_Sense30x(struct sis_video_info *ivideo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399{
2400 u8 backupP4_0d,backupP2_00,backupP2_4d,backupSR_1e,biosflag=0;
2401 u16 svhs=0, svhs_c=0;
2402 u16 cvbs=0, cvbs_c=0;
2403 u16 vga2=0, vga2_c=0;
2404 int myflag, result;
2405 char stdstr[] = "sisfb: Detected";
2406 char tvstr[] = "TV connected to";
2407
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002408 if(ivideo->vbflags2 & VB2_301) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002409 svhs = 0x00b9; cvbs = 0x00b3; vga2 = 0x00d1;
2410 inSISIDXREG(SISPART4,0x01,myflag);
2411 if(myflag & 0x04) {
2412 svhs = 0x00dd; cvbs = 0x00ee; vga2 = 0x00fd;
2413 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002414 } else if(ivideo->vbflags2 & (VB2_301B | VB2_302B)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415 svhs = 0x016b; cvbs = 0x0174; vga2 = 0x0190;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002416 } else if(ivideo->vbflags2 & (VB2_301LV | VB2_302LV)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002417 svhs = 0x0200; cvbs = 0x0100;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002418 } else if(ivideo->vbflags2 & (VB2_301C | VB2_302ELV | VB2_307T | VB2_307LV)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002419 svhs = 0x016b; cvbs = 0x0110; vga2 = 0x0190;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002420 } else
2421 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002422
2423 vga2_c = 0x0e08; svhs_c = 0x0404; cvbs_c = 0x0804;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002424 if(ivideo->vbflags & (VB2_301LV|VB2_302LV|VB2_302ELV|VB2_307LV)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002425 svhs_c = 0x0408; cvbs_c = 0x0808;
2426 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002427
Linus Torvalds1da177e2005-04-16 15:20:36 -07002428 biosflag = 2;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002429 if(ivideo->haveXGIROM) {
2430 biosflag = ivideo->bios_abase[0x58] & 0x03;
2431 } else if(ivideo->newrom) {
2432 if(ivideo->bios_abase[0x5d] & 0x04) biosflag |= 0x01;
2433 } else if(ivideo->sisvga_engine == SIS_300_VGA) {
2434 if(ivideo->bios_abase) {
2435 biosflag = ivideo->bios_abase[0xfe] & 0x03;
2436 }
2437 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002438
2439 if(ivideo->chip == SIS_300) {
2440 inSISIDXREG(SISSR,0x3b,myflag);
2441 if(!(myflag & 0x01)) vga2 = vga2_c = 0;
2442 }
2443
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002444 if(!(ivideo->vbflags2 & VB2_SISVGA2BRIDGE)) {
2445 vga2 = vga2_c = 0;
2446 }
2447
Linus Torvalds1da177e2005-04-16 15:20:36 -07002448 inSISIDXREG(SISSR,0x1e,backupSR_1e);
2449 orSISIDXREG(SISSR,0x1e,0x20);
2450
2451 inSISIDXREG(SISPART4,0x0d,backupP4_0d);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002452 if(ivideo->vbflags2 & VB2_30xC) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002453 setSISIDXREG(SISPART4,0x0d,~0x07,0x01);
2454 } else {
2455 orSISIDXREG(SISPART4,0x0d,0x04);
2456 }
2457 SiS_DDC2Delay(&ivideo->SiS_Pr, 0x2000);
2458
2459 inSISIDXREG(SISPART2,0x00,backupP2_00);
2460 outSISIDXREG(SISPART2,0x00,((backupP2_00 | 0x1c) & 0xfc));
2461
2462 inSISIDXREG(SISPART2,0x4d,backupP2_4d);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002463 if(ivideo->vbflags2 & VB2_SISYPBPRBRIDGE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002464 outSISIDXREG(SISPART2,0x4d,(backupP2_4d & ~0x10));
2465 }
2466
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002467 if(!(ivideo->vbflags2 & VB2_30xCLV)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002468 SISDoSense(ivideo, 0, 0);
2469 }
2470
2471 andSISIDXREG(SISCR, 0x32, ~0x14);
2472
2473 if(vga2_c || vga2) {
2474 if(SISDoSense(ivideo, vga2, vga2_c)) {
2475 if(biosflag & 0x01) {
2476 printk(KERN_INFO "%s %s SCART output\n", stdstr, tvstr);
2477 orSISIDXREG(SISCR, 0x32, 0x04);
2478 } else {
2479 printk(KERN_INFO "%s secondary VGA connection\n", stdstr);
2480 orSISIDXREG(SISCR, 0x32, 0x10);
2481 }
2482 }
2483 }
2484
2485 andSISIDXREG(SISCR, 0x32, 0x3f);
2486
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002487 if(ivideo->vbflags2 & VB2_30xCLV) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002488 orSISIDXREG(SISPART4,0x0d,0x04);
2489 }
2490
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002491 if((ivideo->sisvga_engine == SIS_315_VGA) && (ivideo->vbflags2 & VB2_SISYPBPRBRIDGE)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002492 outSISIDXREG(SISPART2,0x4d,(backupP2_4d | 0x10));
2493 SiS_DDC2Delay(&ivideo->SiS_Pr, 0x2000);
2494 if((result = SISDoSense(ivideo, svhs, 0x0604))) {
2495 if((result = SISDoSense(ivideo, cvbs, 0x0804))) {
2496 printk(KERN_INFO "%s %s YPbPr component output\n", stdstr, tvstr);
2497 orSISIDXREG(SISCR,0x32,0x80);
2498 }
2499 }
2500 outSISIDXREG(SISPART2,0x4d,backupP2_4d);
2501 }
2502
2503 andSISIDXREG(SISCR, 0x32, ~0x03);
2504
2505 if(!(ivideo->vbflags & TV_YPBPR)) {
2506 if((result = SISDoSense(ivideo, svhs, svhs_c))) {
2507 printk(KERN_INFO "%s %s SVIDEO output\n", stdstr, tvstr);
2508 orSISIDXREG(SISCR, 0x32, 0x02);
2509 }
2510 if((biosflag & 0x02) || (!result)) {
2511 if(SISDoSense(ivideo, cvbs, cvbs_c)) {
2512 printk(KERN_INFO "%s %s COMPOSITE output\n", stdstr, tvstr);
2513 orSISIDXREG(SISCR, 0x32, 0x01);
2514 }
2515 }
2516 }
2517
2518 SISDoSense(ivideo, 0, 0);
2519
2520 outSISIDXREG(SISPART2,0x00,backupP2_00);
2521 outSISIDXREG(SISPART4,0x0d,backupP4_0d);
2522 outSISIDXREG(SISSR,0x1e,backupSR_1e);
2523
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002524 if(ivideo->vbflags2 & VB2_30xCLV) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002525 inSISIDXREG(SISPART2,0x00,biosflag);
2526 if(biosflag & 0x20) {
2527 for(myflag = 2; myflag > 0; myflag--) {
2528 biosflag ^= 0x20;
2529 outSISIDXREG(SISPART2,0x00,biosflag);
2530 }
2531 }
2532 }
2533
2534 outSISIDXREG(SISPART2,0x00,backupP2_00);
2535}
2536
2537/* Determine and detect attached TV's on Chrontel */
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002538static void __devinit
2539SiS_SenseCh(struct sis_video_info *ivideo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002540{
2541#if defined(CONFIG_FB_SIS_300) || defined(CONFIG_FB_SIS_315)
2542 u8 temp1, temp2;
2543 char stdstr[] = "sisfb: Chrontel: Detected TV connected to";
2544#endif
2545#ifdef CONFIG_FB_SIS_300
2546 unsigned char test[3];
2547 int i;
2548#endif
2549
2550 if(ivideo->chip < SIS_315H) {
2551
2552#ifdef CONFIG_FB_SIS_300
2553 ivideo->SiS_Pr.SiS_IF_DEF_CH70xx = 1; /* Chrontel 700x */
2554 SiS_SetChrontelGPIO(&ivideo->SiS_Pr, 0x9c); /* Set general purpose IO for Chrontel communication */
2555 SiS_DDC2Delay(&ivideo->SiS_Pr, 1000);
2556 temp1 = SiS_GetCH700x(&ivideo->SiS_Pr, 0x25);
2557 /* See Chrontel TB31 for explanation */
2558 temp2 = SiS_GetCH700x(&ivideo->SiS_Pr, 0x0e);
2559 if(((temp2 & 0x07) == 0x01) || (temp2 & 0x04)) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002560 SiS_SetCH700x(&ivideo->SiS_Pr, 0x0e, 0x0b);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002561 SiS_DDC2Delay(&ivideo->SiS_Pr, 300);
2562 }
2563 temp2 = SiS_GetCH700x(&ivideo->SiS_Pr, 0x25);
2564 if(temp2 != temp1) temp1 = temp2;
2565
2566 if((temp1 >= 0x22) && (temp1 <= 0x50)) {
2567 /* Read power status */
2568 temp1 = SiS_GetCH700x(&ivideo->SiS_Pr, 0x0e);
2569 if((temp1 & 0x03) != 0x03) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002570 /* Power all outputs */
2571 SiS_SetCH700x(&ivideo->SiS_Pr, 0x0e,0x0b);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572 SiS_DDC2Delay(&ivideo->SiS_Pr, 300);
2573 }
2574 /* Sense connected TV devices */
2575 for(i = 0; i < 3; i++) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002576 SiS_SetCH700x(&ivideo->SiS_Pr, 0x10, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002577 SiS_DDC2Delay(&ivideo->SiS_Pr, 0x96);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002578 SiS_SetCH700x(&ivideo->SiS_Pr, 0x10, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002579 SiS_DDC2Delay(&ivideo->SiS_Pr, 0x96);
2580 temp1 = SiS_GetCH700x(&ivideo->SiS_Pr, 0x10);
2581 if(!(temp1 & 0x08)) test[i] = 0x02;
2582 else if(!(temp1 & 0x02)) test[i] = 0x01;
2583 else test[i] = 0;
2584 SiS_DDC2Delay(&ivideo->SiS_Pr, 0x96);
2585 }
2586
2587 if(test[0] == test[1]) temp1 = test[0];
2588 else if(test[0] == test[2]) temp1 = test[0];
2589 else if(test[1] == test[2]) temp1 = test[1];
2590 else {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002591 printk(KERN_INFO
Linus Torvalds1da177e2005-04-16 15:20:36 -07002592 "sisfb: TV detection unreliable - test results varied\n");
2593 temp1 = test[2];
2594 }
2595 if(temp1 == 0x02) {
2596 printk(KERN_INFO "%s SVIDEO output\n", stdstr);
2597 ivideo->vbflags |= TV_SVIDEO;
2598 orSISIDXREG(SISCR, 0x32, 0x02);
2599 andSISIDXREG(SISCR, 0x32, ~0x05);
2600 } else if (temp1 == 0x01) {
2601 printk(KERN_INFO "%s CVBS output\n", stdstr);
2602 ivideo->vbflags |= TV_AVIDEO;
2603 orSISIDXREG(SISCR, 0x32, 0x01);
2604 andSISIDXREG(SISCR, 0x32, ~0x06);
2605 } else {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002606 SiS_SetCH70xxANDOR(&ivideo->SiS_Pr, 0x0e, 0x01, 0xF8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002607 andSISIDXREG(SISCR, 0x32, ~0x07);
2608 }
2609 } else if(temp1 == 0) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002610 SiS_SetCH70xxANDOR(&ivideo->SiS_Pr, 0x0e, 0x01, 0xF8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002611 andSISIDXREG(SISCR, 0x32, ~0x07);
2612 }
2613 /* Set general purpose IO for Chrontel communication */
2614 SiS_SetChrontelGPIO(&ivideo->SiS_Pr, 0x00);
2615#endif
2616
2617 } else {
2618
2619#ifdef CONFIG_FB_SIS_315
2620 ivideo->SiS_Pr.SiS_IF_DEF_CH70xx = 2; /* Chrontel 7019 */
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002621 temp1 = SiS_GetCH701x(&ivideo->SiS_Pr, 0x49);
2622 SiS_SetCH701x(&ivideo->SiS_Pr, 0x49, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002623 SiS_DDC2Delay(&ivideo->SiS_Pr, 0x96);
2624 temp2 = SiS_GetCH701x(&ivideo->SiS_Pr, 0x20);
2625 temp2 |= 0x01;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002626 SiS_SetCH701x(&ivideo->SiS_Pr, 0x20, temp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002627 SiS_DDC2Delay(&ivideo->SiS_Pr, 0x96);
2628 temp2 ^= 0x01;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002629 SiS_SetCH701x(&ivideo->SiS_Pr, 0x20, temp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002630 SiS_DDC2Delay(&ivideo->SiS_Pr, 0x96);
2631 temp2 = SiS_GetCH701x(&ivideo->SiS_Pr, 0x20);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002632 SiS_SetCH701x(&ivideo->SiS_Pr, 0x49, temp1);
2633 temp1 = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002634 if(temp2 & 0x02) temp1 |= 0x01;
2635 if(temp2 & 0x10) temp1 |= 0x01;
2636 if(temp2 & 0x04) temp1 |= 0x02;
2637 if( (temp1 & 0x01) && (temp1 & 0x02) ) temp1 = 0x04;
2638 switch(temp1) {
2639 case 0x01:
2640 printk(KERN_INFO "%s CVBS output\n", stdstr);
2641 ivideo->vbflags |= TV_AVIDEO;
2642 orSISIDXREG(SISCR, 0x32, 0x01);
2643 andSISIDXREG(SISCR, 0x32, ~0x06);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002644 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002645 case 0x02:
2646 printk(KERN_INFO "%s SVIDEO output\n", stdstr);
2647 ivideo->vbflags |= TV_SVIDEO;
2648 orSISIDXREG(SISCR, 0x32, 0x02);
2649 andSISIDXREG(SISCR, 0x32, ~0x05);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002650 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651 case 0x04:
2652 printk(KERN_INFO "%s SCART output\n", stdstr);
2653 orSISIDXREG(SISCR, 0x32, 0x04);
2654 andSISIDXREG(SISCR, 0x32, ~0x03);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002655 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002656 default:
2657 andSISIDXREG(SISCR, 0x32, ~0x07);
2658 }
2659#endif
2660 }
2661}
2662
Thomas Winischhofer544393f2005-09-09 13:04:45 -07002663static void __devinit
2664sisfb_get_VB_type(struct sis_video_info *ivideo)
2665{
2666 char stdstr[] = "sisfb: Detected";
2667 char bridgestr[] = "video bridge";
2668 u8 vb_chipid;
2669 u8 reg;
2670
2671 /* No CRT2 on XGI Z7 */
2672 if(ivideo->chip == XGI_20)
2673 return;
2674
2675 inSISIDXREG(SISPART4, 0x00, vb_chipid);
2676 switch(vb_chipid) {
2677 case 0x01:
2678 inSISIDXREG(SISPART4, 0x01, reg);
2679 if(reg < 0xb0) {
2680 ivideo->vbflags |= VB_301; /* Deprecated */
2681 ivideo->vbflags2 |= VB2_301;
2682 printk(KERN_INFO "%s SiS301 %s\n", stdstr, bridgestr);
2683 } else if(reg < 0xc0) {
2684 ivideo->vbflags |= VB_301B; /* Deprecated */
2685 ivideo->vbflags2 |= VB2_301B;
2686 inSISIDXREG(SISPART4,0x23,reg);
2687 if(!(reg & 0x02)) {
2688 ivideo->vbflags |= VB_30xBDH; /* Deprecated */
2689 ivideo->vbflags2 |= VB2_30xBDH;
2690 printk(KERN_INFO "%s SiS301B-DH %s\n", stdstr, bridgestr);
2691 } else {
2692 printk(KERN_INFO "%s SiS301B %s\n", stdstr, bridgestr);
2693 }
2694 } else if(reg < 0xd0) {
2695 ivideo->vbflags |= VB_301C; /* Deprecated */
2696 ivideo->vbflags2 |= VB2_301C;
2697 printk(KERN_INFO "%s SiS301C %s\n", stdstr, bridgestr);
2698 } else if(reg < 0xe0) {
2699 ivideo->vbflags |= VB_301LV; /* Deprecated */
2700 ivideo->vbflags2 |= VB2_301LV;
2701 printk(KERN_INFO "%s SiS301LV %s\n", stdstr, bridgestr);
2702 } else if(reg <= 0xe1) {
2703 inSISIDXREG(SISPART4,0x39,reg);
2704 if(reg == 0xff) {
2705 ivideo->vbflags |= VB_302LV; /* Deprecated */
2706 ivideo->vbflags2 |= VB2_302LV;
2707 printk(KERN_INFO "%s SiS302LV %s\n", stdstr, bridgestr);
2708 } else {
2709 ivideo->vbflags |= VB_301C; /* Deprecated */
2710 ivideo->vbflags2 |= VB2_301C;
2711 printk(KERN_INFO "%s SiS301C(P4) %s\n", stdstr, bridgestr);
2712#if 0
2713 ivideo->vbflags |= VB_302ELV; /* Deprecated */
2714 ivideo->vbflags2 |= VB2_302ELV;
2715 printk(KERN_INFO "%s SiS302ELV %s\n", stdstr, bridgestr);
2716#endif
2717 }
2718 }
2719 break;
2720 case 0x02:
2721 ivideo->vbflags |= VB_302B; /* Deprecated */
2722 ivideo->vbflags2 |= VB2_302B;
2723 printk(KERN_INFO "%s SiS302B %s\n", stdstr, bridgestr);
2724 break;
2725 }
2726
2727 if((!(ivideo->vbflags2 & VB2_VIDEOBRIDGE)) && (ivideo->chip != SIS_300)) {
2728 inSISIDXREG(SISCR, 0x37, reg);
2729 reg &= SIS_EXTERNAL_CHIP_MASK;
2730 reg >>= 1;
2731 if(ivideo->sisvga_engine == SIS_300_VGA) {
2732#ifdef CONFIG_FB_SIS_300
2733 switch(reg) {
2734 case SIS_EXTERNAL_CHIP_LVDS:
2735 ivideo->vbflags |= VB_LVDS; /* Deprecated */
2736 ivideo->vbflags2 |= VB2_LVDS;
2737 break;
2738 case SIS_EXTERNAL_CHIP_TRUMPION:
2739 ivideo->vbflags |= (VB_LVDS | VB_TRUMPION); /* Deprecated */
2740 ivideo->vbflags2 |= (VB2_LVDS | VB2_TRUMPION);
2741 break;
2742 case SIS_EXTERNAL_CHIP_CHRONTEL:
2743 ivideo->vbflags |= VB_CHRONTEL; /* Deprecated */
2744 ivideo->vbflags2 |= VB2_CHRONTEL;
2745 break;
2746 case SIS_EXTERNAL_CHIP_LVDS_CHRONTEL:
2747 ivideo->vbflags |= (VB_LVDS | VB_CHRONTEL); /* Deprecated */
2748 ivideo->vbflags2 |= (VB2_LVDS | VB2_CHRONTEL);
2749 break;
2750 }
2751 if(ivideo->vbflags2 & VB2_CHRONTEL) ivideo->chronteltype = 1;
2752#endif
2753 } else if(ivideo->chip < SIS_661) {
2754#ifdef CONFIG_FB_SIS_315
2755 switch (reg) {
2756 case SIS310_EXTERNAL_CHIP_LVDS:
2757 ivideo->vbflags |= VB_LVDS; /* Deprecated */
2758 ivideo->vbflags2 |= VB2_LVDS;
2759 break;
2760 case SIS310_EXTERNAL_CHIP_LVDS_CHRONTEL:
2761 ivideo->vbflags |= (VB_LVDS | VB_CHRONTEL); /* Deprecated */
2762 ivideo->vbflags2 |= (VB2_LVDS | VB2_CHRONTEL);
2763 break;
2764 }
2765 if(ivideo->vbflags2 & VB2_CHRONTEL) ivideo->chronteltype = 2;
2766#endif
2767 } else if(ivideo->chip >= SIS_661) {
2768#ifdef CONFIG_FB_SIS_315
2769 inSISIDXREG(SISCR, 0x38, reg);
2770 reg >>= 5;
2771 switch(reg) {
2772 case 0x02:
2773 ivideo->vbflags |= VB_LVDS; /* Deprecated */
2774 ivideo->vbflags2 |= VB2_LVDS;
2775 break;
2776 case 0x03:
2777 ivideo->vbflags |= (VB_LVDS | VB_CHRONTEL); /* Deprecated */
2778 ivideo->vbflags2 |= (VB2_LVDS | VB2_CHRONTEL);
2779 break;
2780 case 0x04:
2781 ivideo->vbflags |= (VB_LVDS | VB_CONEXANT); /* Deprecated */
2782 ivideo->vbflags2 |= (VB2_LVDS | VB2_CONEXANT);
2783 break;
2784 }
2785 if(ivideo->vbflags2 & VB2_CHRONTEL) ivideo->chronteltype = 2;
2786#endif
2787 }
2788 if(ivideo->vbflags2 & VB2_LVDS) {
2789 printk(KERN_INFO "%s LVDS transmitter\n", stdstr);
2790 }
2791 if((ivideo->sisvga_engine == SIS_300_VGA) && (ivideo->vbflags2 & VB2_TRUMPION)) {
2792 printk(KERN_INFO "%s Trumpion Zurac LCD scaler\n", stdstr);
2793 }
2794 if(ivideo->vbflags2 & VB2_CHRONTEL) {
2795 printk(KERN_INFO "%s Chrontel TV encoder\n", stdstr);
2796 }
2797 if((ivideo->chip >= SIS_661) && (ivideo->vbflags2 & VB2_CONEXANT)) {
2798 printk(KERN_INFO "%s Conexant external device\n", stdstr);
2799 }
2800 }
2801
2802 if(ivideo->vbflags2 & VB2_SISBRIDGE) {
2803 SiS_SenseLCD(ivideo);
2804 SiS_Sense30x(ivideo);
2805 } else if(ivideo->vbflags2 & VB2_CHRONTEL) {
2806 SiS_SenseCh(ivideo);
2807 }
2808}
2809
2810/* ---------- Engine initialization routines ------------ */
2811
2812static void
2813sisfb_engine_init(struct sis_video_info *ivideo)
2814{
2815
2816 /* Initialize command queue (we use MMIO only) */
2817
2818 /* BEFORE THIS IS CALLED, THE ENGINES *MUST* BE SYNC'ED */
2819
2820 ivideo->caps &= ~(TURBO_QUEUE_CAP |
2821 MMIO_CMD_QUEUE_CAP |
2822 VM_CMD_QUEUE_CAP |
2823 AGP_CMD_QUEUE_CAP);
2824
2825#ifdef CONFIG_FB_SIS_300
2826 if(ivideo->sisvga_engine == SIS_300_VGA) {
2827 u32 tqueue_pos;
2828 u8 tq_state;
2829
2830 tqueue_pos = (ivideo->video_size - ivideo->cmdQueueSize) / (64 * 1024);
2831
2832 inSISIDXREG(SISSR, IND_SIS_TURBOQUEUE_SET, tq_state);
2833 tq_state |= 0xf0;
2834 tq_state &= 0xfc;
2835 tq_state |= (u8)(tqueue_pos >> 8);
2836 outSISIDXREG(SISSR, IND_SIS_TURBOQUEUE_SET, tq_state);
2837
2838 outSISIDXREG(SISSR, IND_SIS_TURBOQUEUE_ADR, (u8)(tqueue_pos & 0xff));
2839
2840 ivideo->caps |= TURBO_QUEUE_CAP;
2841 }
2842#endif
2843
2844#ifdef CONFIG_FB_SIS_315
2845 if(ivideo->sisvga_engine == SIS_315_VGA) {
2846 u32 tempq = 0, templ;
2847 u8 temp;
2848
2849 if(ivideo->chip == XGI_20) {
2850 switch(ivideo->cmdQueueSize) {
2851 case (64 * 1024):
2852 temp = SIS_CMD_QUEUE_SIZE_Z7_64k;
2853 break;
2854 case (128 * 1024):
2855 default:
2856 temp = SIS_CMD_QUEUE_SIZE_Z7_128k;
2857 }
2858 } else {
2859 switch(ivideo->cmdQueueSize) {
2860 case (4 * 1024 * 1024):
2861 temp = SIS_CMD_QUEUE_SIZE_4M;
2862 break;
2863 case (2 * 1024 * 1024):
2864 temp = SIS_CMD_QUEUE_SIZE_2M;
2865 break;
2866 case (1 * 1024 * 1024):
2867 temp = SIS_CMD_QUEUE_SIZE_1M;
2868 break;
2869 default:
2870 case (512 * 1024):
2871 temp = SIS_CMD_QUEUE_SIZE_512k;
2872 }
2873 }
2874
2875 outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_THRESHOLD, COMMAND_QUEUE_THRESHOLD);
2876 outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, SIS_CMD_QUEUE_RESET);
2877
2878 if((ivideo->chip >= XGI_40) && ivideo->modechanged) {
2879 /* Must disable dual pipe on XGI_40. Can't do
2880 * this in MMIO mode, because it requires
2881 * setting/clearing a bit in the MMIO fire trigger
2882 * register.
2883 */
2884 if(!((templ = MMIO_IN32(ivideo->mmio_vbase, 0x8240)) & (1 << 10))) {
2885
2886 MMIO_OUT32(ivideo->mmio_vbase, Q_WRITE_PTR, 0);
2887
2888 outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, (temp | SIS_VRAM_CMDQUEUE_ENABLE));
2889
2890 tempq = MMIO_IN32(ivideo->mmio_vbase, Q_READ_PTR);
2891 MMIO_OUT32(ivideo->mmio_vbase, Q_WRITE_PTR, tempq);
2892
2893 tempq = (u32)(ivideo->video_size - ivideo->cmdQueueSize);
2894 MMIO_OUT32(ivideo->mmio_vbase, Q_BASE_ADDR, tempq);
2895
2896 writel(0x16800000 + 0x8240, ivideo->video_vbase + tempq);
2897 writel(templ | (1 << 10), ivideo->video_vbase + tempq + 4);
2898 writel(0x168F0000, ivideo->video_vbase + tempq + 8);
2899 writel(0x168F0000, ivideo->video_vbase + tempq + 12);
2900
2901 MMIO_OUT32(ivideo->mmio_vbase, Q_WRITE_PTR, (tempq + 16));
2902
2903 sisfb_syncaccel(ivideo);
2904
2905 outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, SIS_CMD_QUEUE_RESET);
2906
2907 }
2908 }
2909
2910 tempq = MMIO_IN32(ivideo->mmio_vbase, MMIO_QUEUE_READPORT);
2911 MMIO_OUT32(ivideo->mmio_vbase, MMIO_QUEUE_WRITEPORT, tempq);
2912
2913 temp |= (SIS_MMIO_CMD_ENABLE | SIS_CMD_AUTO_CORR);
2914 outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, temp);
2915
2916 tempq = (u32)(ivideo->video_size - ivideo->cmdQueueSize);
2917 MMIO_OUT32(ivideo->mmio_vbase, MMIO_QUEUE_PHYBASE, tempq);
2918
2919 ivideo->caps |= MMIO_CMD_QUEUE_CAP;
2920 }
2921#endif
2922
2923 ivideo->engineok = 1;
2924}
2925
2926static void __devinit
2927sisfb_detect_lcd_type(struct sis_video_info *ivideo)
2928{
2929 u8 reg;
2930 int i;
2931
2932 inSISIDXREG(SISCR, 0x36, reg);
2933 reg &= 0x0f;
2934 if(ivideo->sisvga_engine == SIS_300_VGA) {
2935 ivideo->CRT2LCDType = sis300paneltype[reg];
2936 } else if(ivideo->chip >= SIS_661) {
2937 ivideo->CRT2LCDType = sis661paneltype[reg];
2938 } else {
2939 ivideo->CRT2LCDType = sis310paneltype[reg];
2940 if((ivideo->chip == SIS_550) && (sisfb_fstn)) {
2941 if((ivideo->CRT2LCDType != LCD_320x240_2) &&
2942 (ivideo->CRT2LCDType != LCD_320x240_3)) {
2943 ivideo->CRT2LCDType = LCD_320x240;
2944 }
2945 }
2946 }
2947
2948 if(ivideo->CRT2LCDType == LCD_UNKNOWN) {
2949 /* For broken BIOSes: Assume 1024x768, RGB18 */
2950 ivideo->CRT2LCDType = LCD_1024x768;
2951 setSISIDXREG(SISCR,0x36,0xf0,0x02);
2952 setSISIDXREG(SISCR,0x37,0xee,0x01);
2953 printk(KERN_DEBUG "sisfb: Invalid panel ID (%02x), assuming 1024x768, RGB18\n", reg);
2954 }
2955
2956 for(i = 0; i < SIS_LCD_NUMBER; i++) {
2957 if(ivideo->CRT2LCDType == sis_lcd_data[i].lcdtype) {
2958 ivideo->lcdxres = sis_lcd_data[i].xres;
2959 ivideo->lcdyres = sis_lcd_data[i].yres;
2960 ivideo->lcddefmodeidx = sis_lcd_data[i].default_mode_idx;
2961 break;
2962 }
2963 }
2964
2965#ifdef CONFIG_FB_SIS_300
2966 if(ivideo->SiS_Pr.SiS_CustomT == CUT_BARCO1366) {
2967 ivideo->lcdxres = 1360; ivideo->lcdyres = 1024;
2968 ivideo->lcddefmodeidx = DEFAULT_MODE_1360;
2969 } else if(ivideo->SiS_Pr.SiS_CustomT == CUT_PANEL848) {
2970 ivideo->lcdxres = 848; ivideo->lcdyres = 480;
2971 ivideo->lcddefmodeidx = DEFAULT_MODE_848;
2972 } else if(ivideo->SiS_Pr.SiS_CustomT == CUT_PANEL856) {
2973 ivideo->lcdxres = 856; ivideo->lcdyres = 480;
2974 ivideo->lcddefmodeidx = DEFAULT_MODE_856;
2975 }
2976#endif
2977
2978 printk(KERN_DEBUG "sisfb: Detected %dx%d flat panel\n",
2979 ivideo->lcdxres, ivideo->lcdyres);
2980}
2981
2982static void __devinit
2983sisfb_save_pdc_emi(struct sis_video_info *ivideo)
2984{
2985#ifdef CONFIG_FB_SIS_300
2986 /* Save the current PanelDelayCompensation if the LCD is currently used */
2987 if(ivideo->sisvga_engine == SIS_300_VGA) {
2988 if(ivideo->vbflags2 & (VB2_LVDS | VB2_30xBDH)) {
2989 int tmp;
2990 inSISIDXREG(SISCR,0x30,tmp);
2991 if(tmp & 0x20) {
2992 /* Currently on LCD? If yes, read current pdc */
2993 inSISIDXREG(SISPART1,0x13,ivideo->detectedpdc);
2994 ivideo->detectedpdc &= 0x3c;
2995 if(ivideo->SiS_Pr.PDC == -1) {
2996 /* Let option override detection */
2997 ivideo->SiS_Pr.PDC = ivideo->detectedpdc;
2998 }
2999 printk(KERN_INFO "sisfb: Detected LCD PDC 0x%02x\n",
3000 ivideo->detectedpdc);
3001 }
3002 if((ivideo->SiS_Pr.PDC != -1) &&
3003 (ivideo->SiS_Pr.PDC != ivideo->detectedpdc)) {
3004 printk(KERN_INFO "sisfb: Using LCD PDC 0x%02x\n",
3005 ivideo->SiS_Pr.PDC);
3006 }
3007 }
3008 }
3009#endif
3010
3011#ifdef CONFIG_FB_SIS_315
3012 if(ivideo->sisvga_engine == SIS_315_VGA) {
3013
3014 /* Try to find about LCDA */
3015 if(ivideo->vbflags2 & VB2_SISLCDABRIDGE) {
3016 int tmp;
3017 inSISIDXREG(SISPART1,0x13,tmp);
3018 if(tmp & 0x04) {
3019 ivideo->SiS_Pr.SiS_UseLCDA = TRUE;
3020 ivideo->detectedlcda = 0x03;
3021 }
3022 }
3023
3024 /* Save PDC */
3025 if(ivideo->vbflags2 & VB2_SISLVDSBRIDGE) {
3026 int tmp;
3027 inSISIDXREG(SISCR,0x30,tmp);
3028 if((tmp & 0x20) || (ivideo->detectedlcda != 0xff)) {
3029 /* Currently on LCD? If yes, read current pdc */
3030 u8 pdc;
3031 inSISIDXREG(SISPART1,0x2D,pdc);
3032 ivideo->detectedpdc = (pdc & 0x0f) << 1;
3033 ivideo->detectedpdca = (pdc & 0xf0) >> 3;
3034 inSISIDXREG(SISPART1,0x35,pdc);
3035 ivideo->detectedpdc |= ((pdc >> 7) & 0x01);
3036 inSISIDXREG(SISPART1,0x20,pdc);
3037 ivideo->detectedpdca |= ((pdc >> 6) & 0x01);
3038 if(ivideo->newrom) {
3039 /* New ROM invalidates other PDC resp. */
3040 if(ivideo->detectedlcda != 0xff) {
3041 ivideo->detectedpdc = 0xff;
3042 } else {
3043 ivideo->detectedpdca = 0xff;
3044 }
3045 }
3046 if(ivideo->SiS_Pr.PDC == -1) {
3047 if(ivideo->detectedpdc != 0xff) {
3048 ivideo->SiS_Pr.PDC = ivideo->detectedpdc;
3049 }
3050 }
3051 if(ivideo->SiS_Pr.PDCA == -1) {
3052 if(ivideo->detectedpdca != 0xff) {
3053 ivideo->SiS_Pr.PDCA = ivideo->detectedpdca;
3054 }
3055 }
3056 if(ivideo->detectedpdc != 0xff) {
3057 printk(KERN_INFO
3058 "sisfb: Detected LCD PDC 0x%02x (for LCD=CRT2)\n",
3059 ivideo->detectedpdc);
3060 }
3061 if(ivideo->detectedpdca != 0xff) {
3062 printk(KERN_INFO
3063 "sisfb: Detected LCD PDC1 0x%02x (for LCD=CRT1)\n",
3064 ivideo->detectedpdca);
3065 }
3066 }
3067
3068 /* Save EMI */
3069 if(ivideo->vbflags2 & VB2_SISEMIBRIDGE) {
3070 inSISIDXREG(SISPART4,0x30,ivideo->SiS_Pr.EMI_30);
3071 inSISIDXREG(SISPART4,0x31,ivideo->SiS_Pr.EMI_31);
3072 inSISIDXREG(SISPART4,0x32,ivideo->SiS_Pr.EMI_32);
3073 inSISIDXREG(SISPART4,0x33,ivideo->SiS_Pr.EMI_33);
3074 ivideo->SiS_Pr.HaveEMI = TRUE;
3075 if((tmp & 0x20) || (ivideo->detectedlcda != 0xff)) {
3076 ivideo->SiS_Pr.HaveEMILCD = TRUE;
3077 }
3078 }
3079 }
3080
3081 /* Let user override detected PDCs (all bridges) */
3082 if(ivideo->vbflags2 & VB2_30xBLV) {
3083 if((ivideo->SiS_Pr.PDC != -1) &&
3084 (ivideo->SiS_Pr.PDC != ivideo->detectedpdc)) {
3085 printk(KERN_INFO "sisfb: Using LCD PDC 0x%02x (for LCD=CRT2)\n",
3086 ivideo->SiS_Pr.PDC);
3087 }
3088 if((ivideo->SiS_Pr.PDCA != -1) &&
3089 (ivideo->SiS_Pr.PDCA != ivideo->detectedpdca)) {
3090 printk(KERN_INFO "sisfb: Using LCD PDC1 0x%02x (for LCD=CRT1)\n",
3091 ivideo->SiS_Pr.PDCA);
3092 }
3093 }
3094
3095 }
3096#endif
3097}
3098
3099/* -------------------- Memory manager routines ---------------------- */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003100
3101static u32 __devinit
3102sisfb_getheapstart(struct sis_video_info *ivideo)
3103{
3104 u32 ret = ivideo->sisfb_parm_mem * 1024;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003105 u32 maxoffs = ivideo->video_size - ivideo->hwcursor_size - ivideo->cmdQueueSize;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003106 u32 def;
3107
3108 /* Calculate heap start = end of memory for console
3109 *
3110 * CCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDHHHHQQQQQQQQQQ
3111 * C = console, D = heap, H = HWCursor, Q = cmd-queue
3112 *
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003113 * On 76x in UMA+LFB mode, the layout is as follows:
3114 * DDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCHHHHQQQQQQQQQQQ
3115 * where the heap is the entire UMA area, eventually
3116 * into the LFB area if the given mem parameter is
3117 * higher than the size of the UMA memory.
3118 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07003119 * Basically given by "mem" parameter
3120 *
3121 * maximum = videosize - cmd_queue - hwcursor
3122 * (results in a heap of size 0)
3123 * default = SiS 300: depends on videosize
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003124 * SiS 315/330/340/XGI: 32k below max
Linus Torvalds1da177e2005-04-16 15:20:36 -07003125 */
3126
3127 if(ivideo->sisvga_engine == SIS_300_VGA) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003128 if(ivideo->video_size > 0x1000000) {
3129 def = 0xc00000;
3130 } else if(ivideo->video_size > 0x800000) {
3131 def = 0x800000;
3132 } else {
3133 def = 0x400000;
3134 }
3135 } else if(ivideo->UMAsize && ivideo->LFBsize) {
3136 ret = def = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003137 } else {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003138 def = maxoffs - 0x8000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003139 }
3140
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003141 /* Use default for secondary card for now (FIXME) */
3142 if((!ret) || (ret > maxoffs) || (ivideo->cardnumber != 0))
3143 ret = def;
3144
3145 return ret;
3146}
3147
3148static u32 __devinit
3149sisfb_getheapsize(struct sis_video_info *ivideo)
3150{
3151 u32 max = ivideo->video_size - ivideo->hwcursor_size - ivideo->cmdQueueSize;
3152 u32 ret = 0;
3153
3154 if(ivideo->UMAsize && ivideo->LFBsize) {
3155 if( (!ivideo->sisfb_parm_mem) ||
3156 ((ivideo->sisfb_parm_mem * 1024) > max) ||
3157 ((max - (ivideo->sisfb_parm_mem * 1024)) < ivideo->UMAsize) ) {
3158 ret = ivideo->UMAsize;
3159 max -= ivideo->UMAsize;
3160 } else {
3161 ret = max - (ivideo->sisfb_parm_mem * 1024);
3162 max = ivideo->sisfb_parm_mem * 1024;
3163 }
3164 ivideo->video_offset = ret;
3165 ivideo->sisfb_mem = max;
3166 } else {
3167 ret = max - ivideo->heapstart;
3168 ivideo->sisfb_mem = ivideo->heapstart;
3169 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003170
3171 return ret;
3172}
3173
3174static int __devinit
3175sisfb_heap_init(struct sis_video_info *ivideo)
3176{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003177 struct SIS_OH *poh;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003178
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003179 ivideo->video_offset = 0;
3180 if(ivideo->sisfb_parm_mem) {
3181 if( (ivideo->sisfb_parm_mem < (2 * 1024 * 1024)) ||
3182 (ivideo->sisfb_parm_mem > ivideo->video_size) ) {
3183 ivideo->sisfb_parm_mem = 0;
3184 }
3185 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003186
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003187 ivideo->heapstart = sisfb_getheapstart(ivideo);
3188 ivideo->sisfb_heap_size = sisfb_getheapsize(ivideo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003189
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003190 ivideo->sisfb_heap_start = ivideo->video_vbase + ivideo->heapstart;
3191 ivideo->sisfb_heap_end = ivideo->sisfb_heap_start + ivideo->sisfb_heap_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003192
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003193 printk(KERN_INFO "sisfb: Memory heap starting at %dK, size %dK\n",
3194 (int)(ivideo->heapstart / 1024), (int)(ivideo->sisfb_heap_size / 1024));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003195
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003196 ivideo->sisfb_heap.vinfo = ivideo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003197
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003198 ivideo->sisfb_heap.poha_chain = NULL;
3199 ivideo->sisfb_heap.poh_freelist = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003200
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003201 poh = sisfb_poh_new_node(&ivideo->sisfb_heap);
3202 if(poh == NULL)
3203 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003204
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003205 poh->poh_next = &ivideo->sisfb_heap.oh_free;
3206 poh->poh_prev = &ivideo->sisfb_heap.oh_free;
3207 poh->size = ivideo->sisfb_heap_size;
3208 poh->offset = ivideo->heapstart;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003209
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003210 ivideo->sisfb_heap.oh_free.poh_next = poh;
3211 ivideo->sisfb_heap.oh_free.poh_prev = poh;
3212 ivideo->sisfb_heap.oh_free.size = 0;
3213 ivideo->sisfb_heap.max_freesize = poh->size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003214
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003215 ivideo->sisfb_heap.oh_used.poh_next = &ivideo->sisfb_heap.oh_used;
3216 ivideo->sisfb_heap.oh_used.poh_prev = &ivideo->sisfb_heap.oh_used;
3217 ivideo->sisfb_heap.oh_used.size = SENTINEL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003218
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003219 if(ivideo->cardnumber == 0) {
3220 /* For the first card, make this heap the "global" one
3221 * for old DRM (which could handle only one card)
3222 */
3223 sisfb_heap = &ivideo->sisfb_heap;
3224 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003225
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003226 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003227}
3228
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003229static struct SIS_OH *
3230sisfb_poh_new_node(struct SIS_HEAP *memheap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003231{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003232 struct SIS_OHALLOC *poha;
3233 struct SIS_OH *poh;
3234 unsigned long cOhs;
3235 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003236
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003237 if(memheap->poh_freelist == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003238 poha = kmalloc(SIS_OH_ALLOC_SIZE, GFP_KERNEL);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003239 if(!poha)
3240 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003241
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003242 poha->poha_next = memheap->poha_chain;
3243 memheap->poha_chain = poha;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003244
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003245 cOhs = (SIS_OH_ALLOC_SIZE - sizeof(struct SIS_OHALLOC)) / sizeof(struct SIS_OH) + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003246
3247 poh = &poha->aoh[0];
3248 for(i = cOhs - 1; i != 0; i--) {
3249 poh->poh_next = poh + 1;
3250 poh = poh + 1;
3251 }
3252
3253 poh->poh_next = NULL;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003254 memheap->poh_freelist = &poha->aoh[0];
Linus Torvalds1da177e2005-04-16 15:20:36 -07003255 }
3256
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003257 poh = memheap->poh_freelist;
3258 memheap->poh_freelist = poh->poh_next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003259
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003260 return poh;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003261}
3262
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003263static struct SIS_OH *
3264sisfb_poh_allocate(struct SIS_HEAP *memheap, u32 size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003265{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003266 struct SIS_OH *pohThis;
3267 struct SIS_OH *pohRoot;
3268 int bAllocated = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003269
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003270 if(size > memheap->max_freesize) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003271 DPRINTK("sisfb: Can't allocate %dk video memory\n",
3272 (unsigned int) size / 1024);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003273 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003274 }
3275
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003276 pohThis = memheap->oh_free.poh_next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003277
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003278 while(pohThis != &memheap->oh_free) {
3279 if(size <= pohThis->size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003280 bAllocated = 1;
3281 break;
3282 }
3283 pohThis = pohThis->poh_next;
3284 }
3285
3286 if(!bAllocated) {
3287 DPRINTK("sisfb: Can't allocate %dk video memory\n",
3288 (unsigned int) size / 1024);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003289 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003290 }
3291
3292 if(size == pohThis->size) {
3293 pohRoot = pohThis;
3294 sisfb_delete_node(pohThis);
3295 } else {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003296 pohRoot = sisfb_poh_new_node(memheap);
3297 if(pohRoot == NULL)
3298 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003299
3300 pohRoot->offset = pohThis->offset;
3301 pohRoot->size = size;
3302
3303 pohThis->offset += size;
3304 pohThis->size -= size;
3305 }
3306
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003307 memheap->max_freesize -= size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003308
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003309 pohThis = &memheap->oh_used;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003310 sisfb_insert_node(pohThis, pohRoot);
3311
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003312 return pohRoot;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003313}
3314
3315static void
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003316sisfb_delete_node(struct SIS_OH *poh)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003317{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003318 poh->poh_prev->poh_next = poh->poh_next;
3319 poh->poh_next->poh_prev = poh->poh_prev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003320}
3321
3322static void
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003323sisfb_insert_node(struct SIS_OH *pohList, struct SIS_OH *poh)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003324{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003325 struct SIS_OH *pohTemp = pohList->poh_next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003326
3327 pohList->poh_next = poh;
3328 pohTemp->poh_prev = poh;
3329
3330 poh->poh_prev = pohList;
3331 poh->poh_next = pohTemp;
3332}
3333
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003334static struct SIS_OH *
3335sisfb_poh_free(struct SIS_HEAP *memheap, u32 base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003336{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003337 struct SIS_OH *pohThis;
3338 struct SIS_OH *poh_freed;
3339 struct SIS_OH *poh_prev;
3340 struct SIS_OH *poh_next;
3341 u32 ulUpper;
3342 u32 ulLower;
3343 int foundNode = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003344
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003345 poh_freed = memheap->oh_used.poh_next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003346
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003347 while(poh_freed != &memheap->oh_used) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003348 if(poh_freed->offset == base) {
3349 foundNode = 1;
3350 break;
3351 }
3352
3353 poh_freed = poh_freed->poh_next;
3354 }
3355
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003356 if(!foundNode)
3357 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003358
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003359 memheap->max_freesize += poh_freed->size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003360
3361 poh_prev = poh_next = NULL;
3362 ulUpper = poh_freed->offset + poh_freed->size;
3363 ulLower = poh_freed->offset;
3364
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003365 pohThis = memheap->oh_free.poh_next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003366
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003367 while(pohThis != &memheap->oh_free) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003368 if(pohThis->offset == ulUpper) {
3369 poh_next = pohThis;
3370 } else if((pohThis->offset + pohThis->size) == ulLower) {
3371 poh_prev = pohThis;
3372 }
3373 pohThis = pohThis->poh_next;
3374 }
3375
3376 sisfb_delete_node(poh_freed);
3377
3378 if(poh_prev && poh_next) {
3379 poh_prev->size += (poh_freed->size + poh_next->size);
3380 sisfb_delete_node(poh_next);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003381 sisfb_free_node(memheap, poh_freed);
3382 sisfb_free_node(memheap, poh_next);
3383 return poh_prev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003384 }
3385
3386 if(poh_prev) {
3387 poh_prev->size += poh_freed->size;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003388 sisfb_free_node(memheap, poh_freed);
3389 return poh_prev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003390 }
3391
3392 if(poh_next) {
3393 poh_next->size += poh_freed->size;
3394 poh_next->offset = poh_freed->offset;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003395 sisfb_free_node(memheap, poh_freed);
3396 return poh_next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003397 }
3398
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003399 sisfb_insert_node(&memheap->oh_free, poh_freed);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003400
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003401 return poh_freed;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003402}
3403
3404static void
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003405sisfb_free_node(struct SIS_HEAP *memheap, struct SIS_OH *poh)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003406{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003407 if(poh == NULL)
3408 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003409
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003410 poh->poh_next = memheap->poh_freelist;
3411 memheap->poh_freelist = poh;
3412}
3413
3414static void
3415sis_int_malloc(struct sis_video_info *ivideo, struct sis_memreq *req)
3416{
3417 struct SIS_OH *poh = NULL;
3418
3419 if((ivideo) && (ivideo->sisfb_id == SISFB_ID) && (!ivideo->havenoheap))
3420 poh = sisfb_poh_allocate(&ivideo->sisfb_heap, (u32)req->size);
3421
3422 if(poh == NULL) {
3423 req->offset = req->size = 0;
3424 DPRINTK("sisfb: Video RAM allocation failed\n");
3425 } else {
3426 req->offset = poh->offset;
3427 req->size = poh->size;
3428 DPRINTK("sisfb: Video RAM allocation succeeded: 0x%lx\n",
3429 (poh->offset + ivideo->video_vbase));
3430 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003431}
3432
3433void
3434sis_malloc(struct sis_memreq *req)
3435{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003436 struct sis_video_info *ivideo = sisfb_heap->vinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003437
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003438 if(&ivideo->sisfb_heap == sisfb_heap)
3439 sis_int_malloc(ivideo, req);
3440 else
3441 req->offset = req->size = 0;
3442}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003443
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003444void
3445sis_malloc_new(struct pci_dev *pdev, struct sis_memreq *req)
3446{
3447 struct sis_video_info *ivideo = pci_get_drvdata(pdev);
3448
3449 sis_int_malloc(ivideo, req);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003450}
3451
3452/* sis_free: u32 because "base" is offset inside video ram, can never be >4GB */
3453
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003454static void
3455sis_int_free(struct sis_video_info *ivideo, u32 base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003456{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003457 struct SIS_OH *poh;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003458
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003459 if((!ivideo) || (ivideo->sisfb_id != SISFB_ID) || (ivideo->havenoheap))
3460 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003461
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003462 poh = sisfb_poh_free(&ivideo->sisfb_heap, base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003463
3464 if(poh == NULL) {
3465 DPRINTK("sisfb: sisfb_poh_free() failed at base 0x%x\n",
3466 (unsigned int) base);
3467 }
3468}
3469
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003470void
3471sis_free(u32 base)
3472{
3473 struct sis_video_info *ivideo = sisfb_heap->vinfo;
3474
3475 sis_int_free(ivideo, base);
3476}
3477
3478void
3479sis_free_new(struct pci_dev *pdev, u32 base)
3480{
3481 struct sis_video_info *ivideo = pci_get_drvdata(pdev);
3482
3483 sis_int_free(ivideo, base);
3484}
3485
Linus Torvalds1da177e2005-04-16 15:20:36 -07003486/* --------------------- SetMode routines ------------------------- */
3487
3488static void
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003489sisfb_check_engine_and_sync(struct sis_video_info *ivideo)
3490{
3491 u8 cr30, cr31;
3492
3493 /* Check if MMIO and engines are enabled,
3494 * and sync in case they are. Can't use
3495 * ivideo->accel here, as this might have
3496 * been changed before this is called.
3497 */
3498 inSISIDXREG(SISSR, IND_SIS_PCI_ADDRESS_SET, cr30);
3499 inSISIDXREG(SISSR, IND_SIS_MODULE_ENABLE, cr31);
3500 /* MMIO and 2D/3D engine enabled? */
3501 if((cr30 & SIS_MEM_MAP_IO_ENABLE) && (cr31 & 0x42)) {
3502#ifdef CONFIG_FB_SIS_300
3503 if(ivideo->sisvga_engine == SIS_300_VGA) {
3504 /* Don't care about TurboQueue. It's
3505 * enough to know that the engines
3506 * are enabled
3507 */
3508 sisfb_syncaccel(ivideo);
3509 }
3510#endif
3511#ifdef CONFIG_FB_SIS_315
3512 if(ivideo->sisvga_engine == SIS_315_VGA) {
3513 /* Check that any queue mode is
3514 * enabled, and that the queue
3515 * is not in the state of "reset"
3516 */
3517 inSISIDXREG(SISSR, 0x26, cr30);
3518 if((cr30 & 0xe0) && (!(cr30 & 0x01))) {
3519 sisfb_syncaccel(ivideo);
3520 }
3521 }
3522#endif
3523 }
3524}
3525
3526static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07003527sisfb_pre_setmode(struct sis_video_info *ivideo)
3528{
3529 u8 cr30 = 0, cr31 = 0, cr33 = 0, cr35 = 0, cr38 = 0;
3530 int tvregnum = 0;
3531
3532 ivideo->currentvbflags &= (VB_VIDEOBRIDGE | VB_DISPTYPE_DISP2);
3533
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003534 outSISIDXREG(SISSR, 0x05, 0x86);
3535
Linus Torvalds1da177e2005-04-16 15:20:36 -07003536 inSISIDXREG(SISCR, 0x31, cr31);
3537 cr31 &= ~0x60;
3538 cr31 |= 0x04;
3539
3540 cr33 = ivideo->rate_idx & 0x0F;
3541
3542#ifdef CONFIG_FB_SIS_315
3543 if(ivideo->sisvga_engine == SIS_315_VGA) {
3544 if(ivideo->chip >= SIS_661) {
3545 inSISIDXREG(SISCR, 0x38, cr38);
3546 cr38 &= ~0x07; /* Clear LCDA/DualEdge and YPbPr bits */
3547 } else {
3548 tvregnum = 0x38;
3549 inSISIDXREG(SISCR, tvregnum, cr38);
3550 cr38 &= ~0x3b; /* Clear LCDA/DualEdge and YPbPr bits */
3551 }
3552 }
3553#endif
3554#ifdef CONFIG_FB_SIS_300
3555 if(ivideo->sisvga_engine == SIS_300_VGA) {
3556 tvregnum = 0x35;
3557 inSISIDXREG(SISCR, tvregnum, cr38);
3558 }
3559#endif
3560
3561 SiS_SetEnableDstn(&ivideo->SiS_Pr, FALSE);
3562 SiS_SetEnableFstn(&ivideo->SiS_Pr, FALSE);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003563 ivideo->curFSTN = ivideo->curDSTN = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003564
3565 switch(ivideo->currentvbflags & VB_DISPTYPE_DISP2) {
3566
3567 case CRT2_TV:
3568 cr38 &= ~0xc0; /* Clear PAL-M / PAL-N bits */
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003569 if((ivideo->vbflags & TV_YPBPR) && (ivideo->vbflags2 & VB2_SISYPBPRBRIDGE)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003570#ifdef CONFIG_FB_SIS_315
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003571 if(ivideo->chip >= SIS_661) {
3572 cr38 |= 0x04;
3573 if(ivideo->vbflags & TV_YPBPR525P) cr35 |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003574 else if(ivideo->vbflags & TV_YPBPR750P) cr35 |= 0x40;
3575 else if(ivideo->vbflags & TV_YPBPR1080I) cr35 |= 0x60;
3576 cr30 |= SIS_SIMULTANEOUS_VIEW_ENABLE;
3577 cr35 &= ~0x01;
3578 ivideo->currentvbflags |= (TV_YPBPR | (ivideo->vbflags & TV_YPBPRALL));
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003579 } else if(ivideo->sisvga_engine == SIS_315_VGA) {
3580 cr30 |= (0x80 | SIS_SIMULTANEOUS_VIEW_ENABLE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003581 cr38 |= 0x08;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003582 if(ivideo->vbflags & TV_YPBPR525P) cr38 |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003583 else if(ivideo->vbflags & TV_YPBPR750P) cr38 |= 0x20;
3584 else if(ivideo->vbflags & TV_YPBPR1080I) cr38 |= 0x30;
3585 cr31 &= ~0x01;
3586 ivideo->currentvbflags |= (TV_YPBPR | (ivideo->vbflags & TV_YPBPRALL));
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003587 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003588#endif
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003589 } else if((ivideo->vbflags & TV_HIVISION) &&
3590 (ivideo->vbflags2 & VB2_SISHIVISIONBRIDGE)) {
3591 if(ivideo->chip >= SIS_661) {
3592 cr38 |= 0x04;
3593 cr35 |= 0x60;
3594 } else {
3595 cr30 |= 0x80;
3596 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003597 cr30 |= SIS_SIMULTANEOUS_VIEW_ENABLE;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003598 cr31 |= 0x01;
3599 cr35 |= 0x01;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003600 ivideo->currentvbflags |= TV_HIVISION;
3601 } else if(ivideo->vbflags & TV_SCART) {
3602 cr30 = (SIS_VB_OUTPUT_SCART | SIS_SIMULTANEOUS_VIEW_ENABLE);
3603 cr31 |= 0x01;
3604 cr35 |= 0x01;
3605 ivideo->currentvbflags |= TV_SCART;
3606 } else {
3607 if(ivideo->vbflags & TV_SVIDEO) {
3608 cr30 = (SIS_VB_OUTPUT_SVIDEO | SIS_SIMULTANEOUS_VIEW_ENABLE);
3609 ivideo->currentvbflags |= TV_SVIDEO;
3610 }
3611 if(ivideo->vbflags & TV_AVIDEO) {
3612 cr30 = (SIS_VB_OUTPUT_COMPOSITE | SIS_SIMULTANEOUS_VIEW_ENABLE);
3613 ivideo->currentvbflags |= TV_AVIDEO;
3614 }
3615 }
3616 cr31 |= SIS_DRIVER_MODE;
3617
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003618 if(ivideo->vbflags & (TV_AVIDEO | TV_SVIDEO)) {
3619 if(ivideo->vbflags & TV_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003620 cr31 |= 0x01; cr35 |= 0x01;
3621 ivideo->currentvbflags |= TV_PAL;
3622 if(ivideo->vbflags & TV_PALM) {
3623 cr38 |= 0x40; cr35 |= 0x04;
3624 ivideo->currentvbflags |= TV_PALM;
3625 } else if(ivideo->vbflags & TV_PALN) {
3626 cr38 |= 0x80; cr35 |= 0x08;
3627 ivideo->currentvbflags |= TV_PALN;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003628 }
3629 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003630 cr31 &= ~0x01; cr35 &= ~0x01;
3631 ivideo->currentvbflags |= TV_NTSC;
3632 if(ivideo->vbflags & TV_NTSCJ) {
3633 cr38 |= 0x40; cr35 |= 0x02;
3634 ivideo->currentvbflags |= TV_NTSCJ;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003635 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003636 }
3637 }
3638 break;
3639
3640 case CRT2_LCD:
3641 cr30 = (SIS_VB_OUTPUT_LCD | SIS_SIMULTANEOUS_VIEW_ENABLE);
3642 cr31 |= SIS_DRIVER_MODE;
3643 SiS_SetEnableDstn(&ivideo->SiS_Pr, ivideo->sisfb_dstn);
3644 SiS_SetEnableFstn(&ivideo->SiS_Pr, ivideo->sisfb_fstn);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003645 ivideo->curFSTN = ivideo->sisfb_fstn;
3646 ivideo->curDSTN = ivideo->sisfb_dstn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003647 break;
3648
3649 case CRT2_VGA:
3650 cr30 = (SIS_VB_OUTPUT_CRT2 | SIS_SIMULTANEOUS_VIEW_ENABLE);
3651 cr31 |= SIS_DRIVER_MODE;
3652 if(ivideo->sisfb_nocrt2rate) {
3653 cr33 |= (sisbios_mode[ivideo->sisfb_mode_idx].rate_idx << 4);
3654 } else {
3655 cr33 |= ((ivideo->rate_idx & 0x0F) << 4);
3656 }
3657 break;
3658
3659 default: /* disable CRT2 */
3660 cr30 = 0x00;
3661 cr31 |= (SIS_DRIVER_MODE | SIS_VB_OUTPUT_DISABLE);
3662 }
3663
3664 outSISIDXREG(SISCR, 0x30, cr30);
3665 outSISIDXREG(SISCR, 0x33, cr33);
3666
3667 if(ivideo->chip >= SIS_661) {
3668#ifdef CONFIG_FB_SIS_315
3669 cr31 &= ~0x01; /* Clear PAL flag (now in CR35) */
3670 setSISIDXREG(SISCR, 0x35, ~0x10, cr35); /* Leave overscan bit alone */
3671 cr38 &= 0x07; /* Use only LCDA and HiVision/YPbPr bits */
3672 setSISIDXREG(SISCR, 0x38, 0xf8, cr38);
3673#endif
3674 } else if(ivideo->chip != SIS_300) {
3675 outSISIDXREG(SISCR, tvregnum, cr38);
3676 }
3677 outSISIDXREG(SISCR, 0x31, cr31);
3678
Linus Torvalds1da177e2005-04-16 15:20:36 -07003679 ivideo->SiS_Pr.SiS_UseOEM = ivideo->sisfb_useoem;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003680
3681 sisfb_check_engine_and_sync(ivideo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003682}
3683
3684/* Fix SR11 for 661 and later */
3685#ifdef CONFIG_FB_SIS_315
3686static void
3687sisfb_fixup_SR11(struct sis_video_info *ivideo)
3688{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003689 u8 tmpreg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003690
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003691 if(ivideo->chip >= SIS_661) {
3692 inSISIDXREG(SISSR,0x11,tmpreg);
3693 if(tmpreg & 0x20) {
3694 inSISIDXREG(SISSR,0x3e,tmpreg);
3695 tmpreg = (tmpreg + 1) & 0xff;
3696 outSISIDXREG(SISSR,0x3e,tmpreg);
3697 inSISIDXREG(SISSR,0x11,tmpreg);
3698 }
3699 if(tmpreg & 0xf0) {
3700 andSISIDXREG(SISSR,0x11,0x0f);
3701 }
3702 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003703}
3704#endif
3705
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003706static void
3707sisfb_set_TVxposoffset(struct sis_video_info *ivideo, int val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003708{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003709 if(val > 32) val = 32;
3710 if(val < -32) val = -32;
3711 ivideo->tvxpos = val;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003712
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003713 if(ivideo->sisfblocked) return;
3714 if(!ivideo->modechanged) return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003715
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003716 if(ivideo->currentvbflags & CRT2_TV) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003717
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003718 if(ivideo->vbflags2 & VB2_CHRONTEL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003719
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003720 int x = ivideo->tvx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003721
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003722 switch(ivideo->chronteltype) {
3723 case 1:
3724 x += val;
3725 if(x < 0) x = 0;
3726 outSISIDXREG(SISSR,0x05,0x86);
3727 SiS_SetCH700x(&ivideo->SiS_Pr, 0x0a, (x & 0xff));
3728 SiS_SetCH70xxANDOR(&ivideo->SiS_Pr, 0x08, ((x & 0x0100) >> 7), 0xFD);
3729 break;
3730 case 2:
3731 /* Not supported by hardware */
3732 break;
3733 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003734
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003735 } else if(ivideo->vbflags2 & VB2_SISBRIDGE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003736
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003737 u8 p2_1f,p2_20,p2_2b,p2_42,p2_43;
3738 unsigned short temp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003739
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003740 p2_1f = ivideo->p2_1f;
3741 p2_20 = ivideo->p2_20;
3742 p2_2b = ivideo->p2_2b;
3743 p2_42 = ivideo->p2_42;
3744 p2_43 = ivideo->p2_43;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003745
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003746 temp = p2_1f | ((p2_20 & 0xf0) << 4);
3747 temp += (val * 2);
3748 p2_1f = temp & 0xff;
3749 p2_20 = (temp & 0xf00) >> 4;
3750 p2_2b = ((p2_2b & 0x0f) + (val * 2)) & 0x0f;
3751 temp = p2_43 | ((p2_42 & 0xf0) << 4);
3752 temp += (val * 2);
3753 p2_43 = temp & 0xff;
3754 p2_42 = (temp & 0xf00) >> 4;
3755 outSISIDXREG(SISPART2,0x1f,p2_1f);
3756 setSISIDXREG(SISPART2,0x20,0x0F,p2_20);
3757 setSISIDXREG(SISPART2,0x2b,0xF0,p2_2b);
3758 setSISIDXREG(SISPART2,0x42,0x0F,p2_42);
3759 outSISIDXREG(SISPART2,0x43,p2_43);
3760 }
3761 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003762}
3763
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003764static void
3765sisfb_set_TVyposoffset(struct sis_video_info *ivideo, int val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003766{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003767 if(val > 32) val = 32;
3768 if(val < -32) val = -32;
3769 ivideo->tvypos = val;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003770
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003771 if(ivideo->sisfblocked) return;
3772 if(!ivideo->modechanged) return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003773
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003774 if(ivideo->currentvbflags & CRT2_TV) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003775
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003776 if(ivideo->vbflags2 & VB2_CHRONTEL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003777
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003778 int y = ivideo->tvy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003779
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003780 switch(ivideo->chronteltype) {
3781 case 1:
3782 y -= val;
3783 if(y < 0) y = 0;
3784 outSISIDXREG(SISSR,0x05,0x86);
3785 SiS_SetCH700x(&ivideo->SiS_Pr, 0x0b, (y & 0xff));
3786 SiS_SetCH70xxANDOR(&ivideo->SiS_Pr, 0x08, ((y & 0x0100) >> 8), 0xFE);
3787 break;
3788 case 2:
3789 /* Not supported by hardware */
3790 break;
3791 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003792
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003793 } else if(ivideo->vbflags2 & VB2_SISBRIDGE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003794
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003795 char p2_01, p2_02;
3796 val /= 2;
3797 p2_01 = ivideo->p2_01;
3798 p2_02 = ivideo->p2_02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003799
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003800 p2_01 += val;
3801 p2_02 += val;
3802 if(!(ivideo->currentvbflags & (TV_HIVISION | TV_YPBPR))) {
3803 while((p2_01 <= 0) || (p2_02 <= 0)) {
3804 p2_01 += 2;
3805 p2_02 += 2;
3806 }
3807 }
3808 outSISIDXREG(SISPART2,0x01,p2_01);
3809 outSISIDXREG(SISPART2,0x02,p2_02);
3810 }
3811 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003812}
3813
3814static void
3815sisfb_post_setmode(struct sis_video_info *ivideo)
3816{
3817 BOOLEAN crt1isoff = FALSE;
3818 BOOLEAN doit = TRUE;
3819#if defined(CONFIG_FB_SIS_300) || defined(CONFIG_FB_SIS_315)
3820 u8 reg;
3821#endif
3822#ifdef CONFIG_FB_SIS_315
3823 u8 reg1;
3824#endif
3825
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003826 outSISIDXREG(SISSR, 0x05, 0x86);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003827
3828#ifdef CONFIG_FB_SIS_315
3829 sisfb_fixup_SR11(ivideo);
3830#endif
3831
3832 /* Now we actually HAVE changed the display mode */
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003833 ivideo->modechanged = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003834
3835 /* We can't switch off CRT1 if bridge is in slave mode */
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003836 if(ivideo->vbflags2 & VB2_VIDEOBRIDGE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003837 if(sisfb_bridgeisslave(ivideo)) doit = FALSE;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003838 } else
3839 ivideo->sisfb_crt1off = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003840
3841#ifdef CONFIG_FB_SIS_300
3842 if(ivideo->sisvga_engine == SIS_300_VGA) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003843 if((ivideo->sisfb_crt1off) && (doit)) {
3844 crt1isoff = TRUE;
3845 reg = 0x00;
3846 } else {
3847 crt1isoff = FALSE;
3848 reg = 0x80;
3849 }
3850 setSISIDXREG(SISCR, 0x17, 0x7f, reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003851 }
3852#endif
3853#ifdef CONFIG_FB_SIS_315
3854 if(ivideo->sisvga_engine == SIS_315_VGA) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003855 if((ivideo->sisfb_crt1off) && (doit)) {
3856 crt1isoff = TRUE;
3857 reg = 0x40;
3858 reg1 = 0xc0;
3859 } else {
3860 crt1isoff = FALSE;
3861 reg = 0x00;
3862 reg1 = 0x00;
3863 }
3864 setSISIDXREG(SISCR, ivideo->SiS_Pr.SiS_MyCR63, ~0x40, reg);
3865 setSISIDXREG(SISSR, 0x1f, ~0xc0, reg1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003866 }
3867#endif
3868
3869 if(crt1isoff) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003870 ivideo->currentvbflags &= ~VB_DISPTYPE_CRT1;
3871 ivideo->currentvbflags |= VB_SINGLE_MODE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003872 } else {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003873 ivideo->currentvbflags |= VB_DISPTYPE_CRT1;
3874 if(ivideo->currentvbflags & VB_DISPTYPE_CRT2) {
3875 ivideo->currentvbflags |= VB_MIRROR_MODE;
3876 } else {
3877 ivideo->currentvbflags |= VB_SINGLE_MODE;
3878 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003879 }
3880
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003881 andSISIDXREG(SISSR, IND_SIS_RAMDAC_CONTROL, ~0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003882
3883 if(ivideo->currentvbflags & CRT2_TV) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003884 if(ivideo->vbflags2 & VB2_SISBRIDGE) {
3885 inSISIDXREG(SISPART2,0x1f,ivideo->p2_1f);
3886 inSISIDXREG(SISPART2,0x20,ivideo->p2_20);
3887 inSISIDXREG(SISPART2,0x2b,ivideo->p2_2b);
3888 inSISIDXREG(SISPART2,0x42,ivideo->p2_42);
3889 inSISIDXREG(SISPART2,0x43,ivideo->p2_43);
3890 inSISIDXREG(SISPART2,0x01,ivideo->p2_01);
3891 inSISIDXREG(SISPART2,0x02,ivideo->p2_02);
3892 } else if(ivideo->vbflags2 & VB2_CHRONTEL) {
3893 if(ivideo->chronteltype == 1) {
3894 ivideo->tvx = SiS_GetCH700x(&ivideo->SiS_Pr, 0x0a);
3895 ivideo->tvx |= (((SiS_GetCH700x(&ivideo->SiS_Pr, 0x08) & 0x02) >> 1) << 8);
3896 ivideo->tvy = SiS_GetCH700x(&ivideo->SiS_Pr, 0x0b);
3897 ivideo->tvy |= ((SiS_GetCH700x(&ivideo->SiS_Pr, 0x08) & 0x01) << 8);
3898 }
3899 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003900 }
3901
3902 if(ivideo->tvxpos) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003903 sisfb_set_TVxposoffset(ivideo, ivideo->tvxpos);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003904 }
3905 if(ivideo->tvypos) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003906 sisfb_set_TVyposoffset(ivideo, ivideo->tvypos);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003907 }
3908
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003909 /* Eventually sync engines */
3910 sisfb_check_engine_and_sync(ivideo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003911
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003912 /* (Re-)Initialize chip engines */
3913 if(ivideo->accel) {
3914 sisfb_engine_init(ivideo);
3915 } else {
3916 ivideo->engineok = 0;
3917 }
3918}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003919
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003920static int
3921sisfb_reset_mode(struct sis_video_info *ivideo)
3922{
3923 if(sisfb_set_mode(ivideo, 0))
3924 return 1;
3925
3926 sisfb_set_pitch(ivideo);
3927 sisfb_set_base_CRT1(ivideo, ivideo->current_base);
3928 sisfb_set_base_CRT2(ivideo, ivideo->current_base);
3929
3930 return 0;
3931}
3932
3933static void
3934sisfb_handle_command(struct sis_video_info *ivideo, struct sisfb_cmd *sisfb_command)
3935{
3936 int mycrt1off;
3937
3938 switch(sisfb_command->sisfb_cmd) {
3939 case SISFB_CMD_GETVBFLAGS:
3940 if(!ivideo->modechanged) {
3941 sisfb_command->sisfb_result[0] = SISFB_CMD_ERR_EARLY;
3942 } else {
3943 sisfb_command->sisfb_result[0] = SISFB_CMD_ERR_OK;
3944 sisfb_command->sisfb_result[1] = ivideo->currentvbflags;
3945 sisfb_command->sisfb_result[2] = ivideo->vbflags2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003946 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003947 break;
3948 case SISFB_CMD_SWITCHCRT1:
3949 /* arg[0]: 0 = off, 1 = on, 99 = query */
3950 if(!ivideo->modechanged) {
3951 sisfb_command->sisfb_result[0] = SISFB_CMD_ERR_EARLY;
3952 } else if(sisfb_command->sisfb_arg[0] == 99) {
3953 /* Query */
3954 sisfb_command->sisfb_result[1] = ivideo->sisfb_crt1off ? 0 : 1;
3955 sisfb_command->sisfb_result[0] = SISFB_CMD_ERR_OK;
3956 } else if(ivideo->sisfblocked) {
3957 sisfb_command->sisfb_result[0] = SISFB_CMD_ERR_LOCKED;
3958 } else if((!(ivideo->currentvbflags & CRT2_ENABLE)) &&
3959 (sisfb_command->sisfb_arg[0] == 0)) {
3960 sisfb_command->sisfb_result[0] = SISFB_CMD_ERR_NOCRT2;
3961 } else {
3962 sisfb_command->sisfb_result[0] = SISFB_CMD_ERR_OK;
3963 mycrt1off = sisfb_command->sisfb_arg[0] ? 0 : 1;
3964 if( ((ivideo->currentvbflags & VB_DISPTYPE_CRT1) && mycrt1off) ||
3965 ((!(ivideo->currentvbflags & VB_DISPTYPE_CRT1)) && !mycrt1off) ) {
3966 ivideo->sisfb_crt1off = mycrt1off;
3967 if(sisfb_reset_mode(ivideo)) {
3968 sisfb_command->sisfb_result[0] = SISFB_CMD_ERR_OTHER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003969 }
3970 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003971 sisfb_command->sisfb_result[1] = ivideo->sisfb_crt1off ? 0 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003972 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003973 break;
3974 /* more to come */
3975 default:
3976 sisfb_command->sisfb_result[0] = SISFB_CMD_ERR_UNKNOWN;
3977 printk(KERN_ERR "sisfb: Unknown command 0x%x\n",
3978 sisfb_command->sisfb_cmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003979 }
3980}
3981
3982#ifndef MODULE
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003983SISINITSTATIC int __init
3984sisfb_setup(char *options)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003985{
3986 char *this_opt;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003987
Linus Torvalds1da177e2005-04-16 15:20:36 -07003988 sisfb_setdefaultparms();
3989
Thomas Winischhofer544393f2005-09-09 13:04:45 -07003990 if(!options || !(*options))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003991 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003992
3993 while((this_opt = strsep(&options, ",")) != NULL) {
3994
3995 if(!(*this_opt)) continue;
3996
3997 if(!strnicmp(this_opt, "off", 3)) {
3998 sisfb_off = 1;
3999 } else if(!strnicmp(this_opt, "forcecrt2type:", 14)) {
4000 /* Need to check crt2 type first for fstn/dstn */
4001 sisfb_search_crt2type(this_opt + 14);
4002 } else if(!strnicmp(this_opt, "tvmode:",7)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004003 sisfb_search_tvstd(this_opt + 7);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004004 } else if(!strnicmp(this_opt, "tvstandard:",11)) {
4005 sisfb_search_tvstd(this_opt + 11);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004006 } else if(!strnicmp(this_opt, "mode:", 5)) {
4007 sisfb_search_mode(this_opt + 5, FALSE);
4008 } else if(!strnicmp(this_opt, "vesa:", 5)) {
4009 sisfb_search_vesamode(simple_strtoul(this_opt + 5, NULL, 0), FALSE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004010 } else if(!strnicmp(this_opt, "rate:", 5)) {
4011 sisfb_parm_rate = simple_strtoul(this_opt + 5, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004012 } else if(!strnicmp(this_opt, "forcecrt1:", 10)) {
4013 sisfb_forcecrt1 = (int)simple_strtoul(this_opt + 10, NULL, 0);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004014 } else if(!strnicmp(this_opt, "mem:",4)) {
4015 sisfb_parm_mem = simple_strtoul(this_opt + 4, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004016 } else if(!strnicmp(this_opt, "pdc:", 4)) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004017 sisfb_pdc = simple_strtoul(this_opt + 4, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004018 } else if(!strnicmp(this_opt, "pdc1:", 5)) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004019 sisfb_pdca = simple_strtoul(this_opt + 5, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004020 } else if(!strnicmp(this_opt, "noaccel", 7)) {
4021 sisfb_accel = 0;
4022 } else if(!strnicmp(this_opt, "accel", 5)) {
4023 sisfb_accel = -1;
4024 } else if(!strnicmp(this_opt, "noypan", 6)) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004025 sisfb_ypan = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004026 } else if(!strnicmp(this_opt, "ypan", 4)) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004027 sisfb_ypan = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004028 } else if(!strnicmp(this_opt, "nomax", 5)) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004029 sisfb_max = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004030 } else if(!strnicmp(this_opt, "max", 3)) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004031 sisfb_max = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004032 } else if(!strnicmp(this_opt, "userom:", 7)) {
4033 sisfb_userom = (int)simple_strtoul(this_opt + 7, NULL, 0);
4034 } else if(!strnicmp(this_opt, "useoem:", 7)) {
4035 sisfb_useoem = (int)simple_strtoul(this_opt + 7, NULL, 0);
4036 } else if(!strnicmp(this_opt, "nocrt2rate", 10)) {
4037 sisfb_nocrt2rate = 1;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004038 } else if(!strnicmp(this_opt, "scalelcd:", 9)) {
4039 unsigned long temp = 2;
4040 temp = simple_strtoul(this_opt + 9, NULL, 0);
4041 if((temp == 0) || (temp == 1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004042 sisfb_scalelcd = temp ^ 1;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004043 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004044 } else if(!strnicmp(this_opt, "tvxposoffset:", 13)) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004045 int temp = 0;
4046 temp = (int)simple_strtol(this_opt + 13, NULL, 0);
4047 if((temp >= -32) && (temp <= 32)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004048 sisfb_tvxposoffset = temp;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004049 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004050 } else if(!strnicmp(this_opt, "tvyposoffset:", 13)) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004051 int temp = 0;
4052 temp = (int)simple_strtol(this_opt + 13, NULL, 0);
4053 if((temp >= -32) && (temp <= 32)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004054 sisfb_tvyposoffset = temp;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004055 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004056 } else if(!strnicmp(this_opt, "specialtiming:", 14)) {
4057 sisfb_search_specialtiming(this_opt + 14);
4058 } else if(!strnicmp(this_opt, "lvdshl:", 7)) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004059 int temp = 4;
4060 temp = simple_strtoul(this_opt + 7, NULL, 0);
4061 if((temp >= 0) && (temp <= 3)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004062 sisfb_lvdshl = temp;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004063 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004064 } else if(this_opt[0] >= '0' && this_opt[0] <= '9') {
4065 sisfb_search_mode(this_opt, TRUE);
4066#if !defined(__i386__) && !defined(__x86_64__)
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004067 } else if(!strnicmp(this_opt, "resetcard", 9)) {
4068 sisfb_resetcard = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004069 } else if(!strnicmp(this_opt, "videoram:", 9)) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004070 sisfb_videoram = simple_strtoul(this_opt + 9, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004071#endif
4072 } else {
4073 printk(KERN_INFO "sisfb: Invalid option %s\n", this_opt);
4074 }
4075
4076 }
4077
Linus Torvalds1da177e2005-04-16 15:20:36 -07004078 return 0;
4079}
4080#endif
4081
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004082static int __devinit
4083sisfb_check_rom(SIS_IOTYPE1 *rom_base, struct sis_video_info *ivideo)
4084{
4085 SIS_IOTYPE1 *rom;
4086 int romptr;
4087
4088 if((readb(rom_base) != 0x55) || (readb(rom_base + 1) != 0xaa))
4089 return 0;
4090
4091 romptr = (readb(rom_base + 0x18) | (readb(rom_base + 0x19) << 8));
4092 if(romptr > (0x10000 - 8))
4093 return 0;
4094
4095 rom = rom_base + romptr;
4096
4097 if((readb(rom) != 'P') || (readb(rom + 1) != 'C') ||
4098 (readb(rom + 2) != 'I') || (readb(rom + 3) != 'R'))
4099 return 0;
4100
4101 if((readb(rom + 4) | (readb(rom + 5) << 8)) != ivideo->chip_vendor)
4102 return 0;
4103
4104 if((readb(rom + 6) | (readb(rom + 7) << 8)) != ivideo->chip_id)
4105 return 0;
4106
4107 return 1;
4108}
4109
4110static unsigned char * __devinit
4111sisfb_find_rom(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004112{
4113 struct sis_video_info *ivideo = pci_get_drvdata(pdev);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004114 SIS_IOTYPE1 *rom_base;
4115 unsigned char *myrombase = NULL;
4116 u32 temp;
4117#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
4118 size_t romsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004119
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004120 /* First, try the official pci ROM functions (except
4121 * on integrated chipsets which have no ROM).
4122 */
4123
4124 if(!ivideo->nbridge) {
4125
4126 if((rom_base = pci_map_rom(pdev, &romsize))) {
4127
4128 if(sisfb_check_rom(rom_base, ivideo)) {
4129
4130 if((myrombase = vmalloc(65536))) {
4131
4132 /* Work around bug in pci/rom.c: Folks forgot to check
4133 * whether the size retrieved from the BIOS image eventually
4134 * is larger than the mapped size
4135 */
4136 if(pci_resource_len(pdev, PCI_ROM_RESOURCE) < romsize)
4137 romsize = pci_resource_len(pdev, PCI_ROM_RESOURCE);
4138
4139 memcpy_fromio(myrombase, rom_base,
4140 (romsize > 65536) ? 65536 : romsize);
4141 }
4142 }
4143 pci_unmap_rom(pdev, rom_base);
4144 }
4145 }
4146
4147 if(myrombase) return myrombase;
4148#endif
4149
4150 /* Otherwise do it the conventional way. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004151
4152#if defined(__i386__) || defined(__x86_64__)
4153
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004154 for(temp = 0x000c0000; temp < 0x000f0000; temp += 0x00001000) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004155
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004156 rom_base = ioremap(temp, 65536);
4157 if(!rom_base)
4158 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004159
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004160 if(!sisfb_check_rom(rom_base, ivideo)) {
4161 iounmap(rom_base);
4162 continue;
4163 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004164
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004165 if((myrombase = vmalloc(65536)))
4166 memcpy_fromio(myrombase, rom_base, 65536);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004167
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004168 iounmap(rom_base);
4169 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004170
Linus Torvalds1da177e2005-04-16 15:20:36 -07004171 }
4172
4173#else
4174
4175 pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &temp);
4176 pci_write_config_dword(pdev, PCI_ROM_ADDRESS,
4177 (ivideo->video_base & PCI_ROM_ADDRESS_MASK) | PCI_ROM_ADDRESS_ENABLE);
4178
4179 rom_base = ioremap(ivideo->video_base, 65536);
4180 if(rom_base) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004181 if(sisfb_check_rom(rom_base, ivideo)) {
4182 if((myrombase = vmalloc(65536)))
4183 memcpy_fromio(myrombase, rom_base, 65536);
4184 }
4185 iounmap(rom_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004186 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004187
4188 pci_write_config_dword(pdev, PCI_ROM_ADDRESS, temp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004189
4190#endif
4191
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004192 return myrombase;
4193}
4194
4195static void __devinit
4196sisfb_post_map_vram(struct sis_video_info *ivideo, unsigned int *mapsize,
4197 unsigned int min)
4198{
4199 ivideo->video_vbase = ioremap(ivideo->video_base, (*mapsize));
4200
4201 if(!ivideo->video_vbase) {
4202 printk(KERN_ERR
4203 "sisfb: Unable to map maximum video RAM for size detection\n");
4204 (*mapsize) >>= 1;
4205 while((!(ivideo->video_vbase = ioremap(ivideo->video_base, (*mapsize))))) {
4206 (*mapsize) >>= 1;
4207 if((*mapsize) < (min << 20))
4208 break;
4209 }
4210 if(ivideo->video_vbase) {
4211 printk(KERN_ERR
4212 "sisfb: Video RAM size detection limited to %dMB\n",
4213 (int)((*mapsize) >> 20));
4214 }
4215 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004216}
4217
4218#ifdef CONFIG_FB_SIS_300
4219static int __devinit
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004220sisfb_post_300_buswidth(struct sis_video_info *ivideo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004221{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004222 SIS_IOTYPE1 *FBAddress = ivideo->video_vbase;
4223 unsigned short temp;
4224 unsigned char reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004225 int i, j;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004226
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004227 andSISIDXREG(SISSR, 0x15, 0xFB);
4228 orSISIDXREG(SISSR, 0x15, 0x04);
4229 outSISIDXREG(SISSR, 0x13, 0x00);
4230 outSISIDXREG(SISSR, 0x14, 0xBF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004231
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004232 for(i = 0; i < 2; i++) {
4233 temp = 0x1234;
4234 for(j = 0; j < 4; j++) {
4235 writew(temp, FBAddress);
4236 if(readw(FBAddress) == temp)
4237 break;
4238 orSISIDXREG(SISSR, 0x3c, 0x01);
4239 inSISIDXREG(SISSR, 0x05, reg);
4240 inSISIDXREG(SISSR, 0x05, reg);
4241 andSISIDXREG(SISSR, 0x3c, 0xfe);
4242 inSISIDXREG(SISSR, 0x05, reg);
4243 inSISIDXREG(SISSR, 0x05, reg);
4244 temp++;
4245 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004246 }
4247
4248 writel(0x01234567L, FBAddress);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004249 writel(0x456789ABL, (FBAddress + 4));
4250 writel(0x89ABCDEFL, (FBAddress + 8));
4251 writel(0xCDEF0123L, (FBAddress + 12));
4252
4253 inSISIDXREG(SISSR, 0x3b, reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004254 if(reg & 0x01) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004255 if(readl((FBAddress + 12)) == 0xCDEF0123L)
4256 return 4; /* Channel A 128bit */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004257 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004258
4259 if(readl((FBAddress + 4)) == 0x456789ABL)
4260 return 2; /* Channel B 64bit */
4261
4262 return 1; /* 32bit */
4263}
4264
4265static int __devinit
4266sisfb_post_300_rwtest(struct sis_video_info *ivideo, int iteration, int buswidth,
4267 int PseudoRankCapacity, int PseudoAdrPinCount,
4268 unsigned int mapsize)
4269{
4270 SIS_IOTYPE1 *FBAddr = ivideo->video_vbase;
4271 unsigned short sr14;
4272 unsigned int k, RankCapacity, PageCapacity, BankNumHigh, BankNumMid;
4273 unsigned int PhysicalAdrOtherPage, PhysicalAdrHigh, PhysicalAdrHalfPage;
4274 static const unsigned short SiS_DRAMType[17][5] = {
4275 {0x0C,0x0A,0x02,0x40,0x39},
4276 {0x0D,0x0A,0x01,0x40,0x48},
4277 {0x0C,0x09,0x02,0x20,0x35},
4278 {0x0D,0x09,0x01,0x20,0x44},
4279 {0x0C,0x08,0x02,0x10,0x31},
4280 {0x0D,0x08,0x01,0x10,0x40},
4281 {0x0C,0x0A,0x01,0x20,0x34},
4282 {0x0C,0x09,0x01,0x08,0x32},
4283 {0x0B,0x08,0x02,0x08,0x21},
4284 {0x0C,0x08,0x01,0x08,0x30},
4285 {0x0A,0x08,0x02,0x04,0x11},
4286 {0x0B,0x0A,0x01,0x10,0x28},
4287 {0x09,0x08,0x02,0x02,0x01},
4288 {0x0B,0x09,0x01,0x08,0x24},
4289 {0x0B,0x08,0x01,0x04,0x20},
4290 {0x0A,0x08,0x01,0x02,0x10},
4291 {0x09,0x08,0x01,0x01,0x00}
4292 };
4293
4294 for(k = 0; k <= 16; k++) {
4295
4296 RankCapacity = buswidth * SiS_DRAMType[k][3];
4297
4298 if(RankCapacity != PseudoRankCapacity)
4299 continue;
4300
4301 if((SiS_DRAMType[k][2] + SiS_DRAMType[k][0]) > PseudoAdrPinCount)
4302 continue;
4303
4304 BankNumHigh = RankCapacity * 16 * iteration - 1;
4305 if(iteration == 3) { /* Rank No */
4306 BankNumMid = RankCapacity * 16 - 1;
4307 } else {
4308 BankNumMid = RankCapacity * 16 * iteration / 2 - 1;
4309 }
4310
4311 PageCapacity = (1 << SiS_DRAMType[k][1]) * buswidth * 4;
4312 PhysicalAdrHigh = BankNumHigh;
4313 PhysicalAdrHalfPage = (PageCapacity / 2 + PhysicalAdrHigh) % PageCapacity;
4314 PhysicalAdrOtherPage = PageCapacity * SiS_DRAMType[k][2] + PhysicalAdrHigh;
4315
4316 andSISIDXREG(SISSR, 0x15, 0xFB); /* Test */
4317 orSISIDXREG(SISSR, 0x15, 0x04); /* Test */
4318 sr14 = (SiS_DRAMType[k][3] * buswidth) - 1;
4319 if(buswidth == 4) sr14 |= 0x80;
4320 else if(buswidth == 2) sr14 |= 0x40;
4321 outSISIDXREG(SISSR, 0x13, SiS_DRAMType[k][4]);
4322 outSISIDXREG(SISSR, 0x14, sr14);
4323
4324 BankNumHigh <<= 16;
4325 BankNumMid <<= 16;
4326
4327 if((BankNumHigh + PhysicalAdrHigh >= mapsize) ||
4328 (BankNumMid + PhysicalAdrHigh >= mapsize) ||
4329 (BankNumHigh + PhysicalAdrHalfPage >= mapsize) ||
4330 (BankNumHigh + PhysicalAdrOtherPage >= mapsize))
4331 continue;
4332
4333 /* Write data */
4334 writew(((unsigned short)PhysicalAdrHigh),
4335 (FBAddr + BankNumHigh + PhysicalAdrHigh));
4336 writew(((unsigned short)BankNumMid),
4337 (FBAddr + BankNumMid + PhysicalAdrHigh));
4338 writew(((unsigned short)PhysicalAdrHalfPage),
4339 (FBAddr + BankNumHigh + PhysicalAdrHalfPage));
4340 writew(((unsigned short)PhysicalAdrOtherPage),
4341 (FBAddr + BankNumHigh + PhysicalAdrOtherPage));
4342
4343 /* Read data */
4344 if(readw(FBAddr + BankNumHigh + PhysicalAdrHigh) == PhysicalAdrHigh)
4345 return 1;
4346 }
4347
4348 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004349}
4350
4351static void __devinit
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004352sisfb_post_300_ramsize(struct pci_dev *pdev, unsigned int mapsize)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004353{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004354 struct sis_video_info *ivideo = pci_get_drvdata(pdev);
4355 int i, j, buswidth;
4356 int PseudoRankCapacity, PseudoAdrPinCount;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004357
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004358 buswidth = sisfb_post_300_buswidth(ivideo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004359
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004360 for(i = 6; i >= 0; i--) {
4361 PseudoRankCapacity = 1 << i;
4362 for(j = 4; j >= 1; j--) {
4363 PseudoAdrPinCount = 15 - j;
4364 if((PseudoRankCapacity * j) <= 64) {
4365 if(sisfb_post_300_rwtest(ivideo,
4366 j,
4367 buswidth,
4368 PseudoRankCapacity,
4369 PseudoAdrPinCount,
4370 mapsize))
4371 return;
4372 }
4373 }
4374 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004375}
4376
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004377static void __devinit
4378sisfb_post_sis300(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004379{
4380 struct sis_video_info *ivideo = pci_get_drvdata(pdev);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004381 unsigned char *bios = ivideo->SiS_Pr.VirtualRomBase;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004382 u8 reg, v1, v2, v3, v4, v5, v6, v7, v8;
4383 u16 index, rindex, memtype = 0;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004384 unsigned int mapsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004385
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004386 if(!ivideo->SiS_Pr.UseROM)
4387 bios = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004388
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004389 outSISIDXREG(SISSR, 0x05, 0x86);
4390
4391 if(bios) {
4392 if(bios[0x52] & 0x80) {
4393 memtype = bios[0x52];
4394 } else {
4395 inSISIDXREG(SISSR, 0x3a, memtype);
4396 }
4397 memtype &= 0x07;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004398 }
4399
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004400 v3 = 0x80; v6 = 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004401 if(ivideo->revision_id <= 0x13) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004402 v1 = 0x44; v2 = 0x42;
4403 v4 = 0x44; v5 = 0x42;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004404 } else {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004405 v1 = 0x68; v2 = 0x43; /* Assume 125Mhz MCLK */
4406 v4 = 0x68; v5 = 0x43; /* Assume 125Mhz ECLK */
4407 if(bios) {
4408 index = memtype * 5;
4409 rindex = index + 0x54;
4410 v1 = bios[rindex++];
4411 v2 = bios[rindex++];
4412 v3 = bios[rindex++];
4413 rindex = index + 0x7c;
4414 v4 = bios[rindex++];
4415 v5 = bios[rindex++];
4416 v6 = bios[rindex++];
4417 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004418 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004419 outSISIDXREG(SISSR, 0x28, v1);
4420 outSISIDXREG(SISSR, 0x29, v2);
4421 outSISIDXREG(SISSR, 0x2a, v3);
4422 outSISIDXREG(SISSR, 0x2e, v4);
4423 outSISIDXREG(SISSR, 0x2f, v5);
4424 outSISIDXREG(SISSR, 0x30, v6);
4425
Linus Torvalds1da177e2005-04-16 15:20:36 -07004426 v1 = 0x10;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004427 if(bios)
4428 v1 = bios[0xa4];
4429 outSISIDXREG(SISSR, 0x07, v1); /* DAC speed */
4430
4431 outSISIDXREG(SISSR, 0x11, 0x0f); /* DDC, power save */
4432
Linus Torvalds1da177e2005-04-16 15:20:36 -07004433 v1 = 0x01; v2 = 0x43; v3 = 0x1e; v4 = 0x2a;
4434 v5 = 0x06; v6 = 0x00; v7 = 0x00; v8 = 0x00;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004435 if(bios) {
4436 memtype += 0xa5;
4437 v1 = bios[memtype];
4438 v2 = bios[memtype + 8];
4439 v3 = bios[memtype + 16];
4440 v4 = bios[memtype + 24];
4441 v5 = bios[memtype + 32];
4442 v6 = bios[memtype + 40];
4443 v7 = bios[memtype + 48];
4444 v8 = bios[memtype + 56];
Linus Torvalds1da177e2005-04-16 15:20:36 -07004445 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004446 if(ivideo->revision_id >= 0x80)
4447 v3 &= 0xfd;
4448 outSISIDXREG(SISSR, 0x15, v1); /* Ram type (assuming 0, BIOS 0xa5 step 8) */
4449 outSISIDXREG(SISSR, 0x16, v2);
4450 outSISIDXREG(SISSR, 0x17, v3);
4451 outSISIDXREG(SISSR, 0x18, v4);
4452 outSISIDXREG(SISSR, 0x19, v5);
4453 outSISIDXREG(SISSR, 0x1a, v6);
4454 outSISIDXREG(SISSR, 0x1b, v7);
4455 outSISIDXREG(SISSR, 0x1c, v8); /* ---- */
4456 andSISIDXREG(SISSR, 0x15 ,0xfb);
4457 orSISIDXREG(SISSR, 0x15, 0x04);
4458 if(bios) {
4459 if(bios[0x53] & 0x02) {
4460 orSISIDXREG(SISSR, 0x19, 0x20);
4461 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004462 }
4463 v1 = 0x04; /* DAC pedestal (BIOS 0xe5) */
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004464 if(ivideo->revision_id >= 0x80)
4465 v1 |= 0x01;
4466 outSISIDXREG(SISSR, 0x1f, v1);
4467 outSISIDXREG(SISSR, 0x20, 0xa4); /* linear & relocated io & disable a0000 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004468 v1 = 0xf6; v2 = 0x0d; v3 = 0x00;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004469 if(bios) {
4470 v1 = bios[0xe8];
4471 v2 = bios[0xe9];
4472 v3 = bios[0xea];
Linus Torvalds1da177e2005-04-16 15:20:36 -07004473 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004474 outSISIDXREG(SISSR, 0x23, v1);
4475 outSISIDXREG(SISSR, 0x24, v2);
4476 outSISIDXREG(SISSR, 0x25, v3);
4477 outSISIDXREG(SISSR, 0x21, 0x84);
4478 outSISIDXREG(SISSR, 0x22, 0x00);
4479 outSISIDXREG(SISCR, 0x37, 0x00);
4480 orSISIDXREG(SISPART1, 0x24, 0x01); /* unlock crt2 */
4481 outSISIDXREG(SISPART1, 0x00, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004482 v1 = 0x40; v2 = 0x11;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004483 if(bios) {
4484 v1 = bios[0xec];
4485 v2 = bios[0xeb];
Linus Torvalds1da177e2005-04-16 15:20:36 -07004486 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004487 outSISIDXREG(SISPART1, 0x02, v1);
4488
4489 if(ivideo->revision_id >= 0x80)
4490 v2 &= ~0x01;
4491
4492 inSISIDXREG(SISPART4, 0x00, reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004493 if((reg == 1) || (reg == 2)) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004494 outSISIDXREG(SISCR, 0x37, 0x02);
4495 outSISIDXREG(SISPART2, 0x00, 0x1c);
4496 v4 = 0x00; v5 = 0x00; v6 = 0x10;
4497 if(ivideo->SiS_Pr.UseROM) {
4498 v4 = bios[0xf5];
4499 v5 = bios[0xf6];
4500 v6 = bios[0xf7];
4501 }
4502 outSISIDXREG(SISPART4, 0x0d, v4);
4503 outSISIDXREG(SISPART4, 0x0e, v5);
4504 outSISIDXREG(SISPART4, 0x10, v6);
4505 outSISIDXREG(SISPART4, 0x0f, 0x3f);
4506 inSISIDXREG(SISPART4, 0x01, reg);
4507 if(reg >= 0xb0) {
4508 inSISIDXREG(SISPART4, 0x23, reg);
4509 reg &= 0x20;
4510 reg <<= 1;
4511 outSISIDXREG(SISPART4, 0x23, reg);
4512 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004513 } else {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004514 v2 &= ~0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004515 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004516 outSISIDXREG(SISSR, 0x32, v2);
4517
4518 andSISIDXREG(SISPART1, 0x24, 0xfe); /* Lock CRT2 */
4519
4520 inSISIDXREG(SISSR, 0x16, reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004521 reg &= 0xc3;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004522 outSISIDXREG(SISCR, 0x35, reg);
4523 outSISIDXREG(SISCR, 0x83, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004524#if !defined(__i386__) && !defined(__x86_64__)
4525 if(sisfb_videoram) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004526 outSISIDXREG(SISSR, 0x13, 0x28); /* ? */
4527 reg = ((sisfb_videoram >> 10) - 1) | 0x40;
4528 outSISIDXREG(SISSR, 0x14, reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004529 } else {
4530#endif
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004531 /* Need to map max FB size for finding out about RAM size */
4532 mapsize = 64 << 20;
4533 sisfb_post_map_vram(ivideo, &mapsize, 4);
4534
4535 if(ivideo->video_vbase) {
4536 sisfb_post_300_ramsize(pdev, mapsize);
4537 iounmap(ivideo->video_vbase);
4538 } else {
4539 printk(KERN_DEBUG
4540 "sisfb: Failed to map memory for size detection, assuming 8MB\n");
4541 outSISIDXREG(SISSR, 0x13, 0x28); /* ? */
4542 outSISIDXREG(SISSR, 0x14, 0x47); /* 8MB, 64bit default */
4543 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004544#if !defined(__i386__) && !defined(__x86_64__)
4545 }
4546#endif
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004547 if(bios) {
4548 v1 = bios[0xe6];
4549 v2 = bios[0xe7];
Linus Torvalds1da177e2005-04-16 15:20:36 -07004550 } else {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004551 inSISIDXREG(SISSR, 0x3a, reg);
4552 if((reg & 0x30) == 0x30) {
4553 v1 = 0x04; /* PCI */
4554 v2 = 0x92;
4555 } else {
4556 v1 = 0x14; /* AGP */
4557 v2 = 0xb2;
4558 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004559 }
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004560 outSISIDXREG(SISSR, 0x21, v1);
4561 outSISIDXREG(SISSR, 0x22, v2);
4562
4563 /* Sense CRT1 */
4564 sisfb_sense_crt1(ivideo);
4565
4566 /* Set default mode, don't clear screen */
4567 ivideo->SiS_Pr.SiS_UseOEM = FALSE;
4568 SiS_SetEnableDstn(&ivideo->SiS_Pr, FALSE);
4569 SiS_SetEnableFstn(&ivideo->SiS_Pr, FALSE);
4570 ivideo->curFSTN = ivideo->curDSTN = 0;
4571 ivideo->SiS_Pr.VideoMemorySize = 8 << 20;
4572 SiSSetMode(&ivideo->SiS_Pr, 0x2e | 0x80);
4573
4574 outSISIDXREG(SISSR, 0x05, 0x86);
4575
4576 /* Display off */
4577 orSISIDXREG(SISSR, 0x01, 0x20);
4578
4579 /* Save mode number in CR34 */
4580 outSISIDXREG(SISCR, 0x34, 0x2e);
4581
4582 /* Let everyone know what the current mode is */
4583 ivideo->modeprechange = 0x2e;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004584}
4585#endif
4586
4587#ifdef CONFIG_FB_SIS_315
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004588#if 0
4589static void __devinit
4590sisfb_post_sis315330(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004591{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004592 /* TODO */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004593}
4594#endif
4595
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004596static void __devinit
4597sisfb_post_xgi_delay(struct sis_video_info *ivideo, int delay)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004598{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07004599 unsigned int i;
4600 u8 reg;
4601
4602 for(i = 0; i <= (delay * 10 * 36); i++) {
4603 inSISIDXREG(SISSR, 0x05, reg);
4604 reg++;
4605 }
4606}
4607
4608static int __devinit
4609sisfb_find_host_bridge(struct sis_video_info *ivideo, struct pci_dev *mypdev,
4610 unsigned short pcivendor)
4611{
4612 struct pci_dev *pdev = NULL;
4613 unsigned short temp;
4614 int ret = 0;
4615
4616 while((pdev = SIS_PCI_GET_CLASS(PCI_CLASS_BRIDGE_HOST, pdev))) {
4617 temp = pdev->vendor;
4618 SIS_PCI_PUT_DEVICE(pdev);
4619 if(temp == pcivendor) {
4620 ret = 1;
4621 break;
4622 }
4623 }
4624
4625 return ret;
4626}
4627
4628static int __devinit
4629sisfb_post_xgi_rwtest(struct sis_video_info *ivideo, int starta,
4630 unsigned int enda, unsigned int mapsize)
4631{
4632 unsigned int pos;
4633 int i;
4634
4635 writel(0, ivideo->video_vbase);
4636
4637 for(i = starta; i <= enda; i++) {
4638 pos = 1 << i;
4639 if(pos < mapsize)
4640 writel(pos, ivideo->video_vbase + pos);
4641 }
4642
4643 sisfb_post_xgi_delay(ivideo, 150);
4644
4645 if(readl(ivideo->video_vbase) != 0)
4646 return 0;
4647
4648 for(i = starta; i <= enda; i++) {
4649 pos = 1 << i;
4650 if(pos < mapsize) {
4651 if(readl(ivideo->video_vbase + pos) != pos)
4652 return 0;
4653 } else
4654 return 0;
4655 }
4656
4657 return 1;
4658}
4659
4660static void __devinit
4661sisfb_post_xgi_ramsize(struct sis_video_info *ivideo)
4662{
4663 unsigned int buswidth, ranksize, channelab, mapsize;
4664 int i, j, k, l;
4665 u8 reg, sr14;
4666 static const u8 dramsr13[12 * 5] = {
4667 0x02, 0x0e, 0x0b, 0x80, 0x5d,
4668 0x02, 0x0e, 0x0a, 0x40, 0x59,
4669 0x02, 0x0d, 0x0b, 0x40, 0x4d,
4670 0x02, 0x0e, 0x09, 0x20, 0x55,
4671 0x02, 0x0d, 0x0a, 0x20, 0x49,
4672 0x02, 0x0c, 0x0b, 0x20, 0x3d,
4673 0x02, 0x0e, 0x08, 0x10, 0x51,
4674 0x02, 0x0d, 0x09, 0x10, 0x45,
4675 0x02, 0x0c, 0x0a, 0x10, 0x39,
4676 0x02, 0x0d, 0x08, 0x08, 0x41,
4677 0x02, 0x0c, 0x09, 0x08, 0x35,
4678 0x02, 0x0c, 0x08, 0x04, 0x31
4679 };
4680 static const u8 dramsr13_4[4 * 5] = {
4681 0x02, 0x0d, 0x09, 0x40, 0x45,
4682 0x02, 0x0c, 0x09, 0x20, 0x35,
4683 0x02, 0x0c, 0x08, 0x10, 0x31,
4684 0x02, 0x0b, 0x08, 0x08, 0x21
4685 };
4686
4687 /* Enable linear mode, disable 0xa0000 address decoding */
4688 /* We disable a0000 address decoding, because
4689 * - if running on x86, if the card is disabled, it means
4690 * that another card is in the system. We don't want
4691 * to interphere with that primary card's textmode.
4692 * - if running on non-x86, there usually is no VGA window
4693 * at a0000.
4694 */
4695 orSISIDXREG(SISSR, 0x20, (0x80 | 0x04));
4696
4697 /* Need to map max FB size for finding out about RAM size */
4698 mapsize = 256 << 20;
4699 sisfb_post_map_vram(ivideo, &mapsize, 32);
4700
4701 if(!ivideo->video_vbase) {
4702 printk(KERN_ERR "sisfb: Unable to detect RAM size. Setting default.\n");
4703 outSISIDXREG(SISSR, 0x13, 0x35);
4704 outSISIDXREG(SISSR, 0x14, 0x41);
4705 /* TODO */
4706 return;
4707 }
4708
4709 /* Non-interleaving */
4710 outSISIDXREG(SISSR, 0x15, 0x00);
4711 /* No tiling */
4712 outSISIDXREG(SISSR, 0x1c, 0x00);
4713
4714 if(ivideo->chip == XGI_20) {
4715
4716 channelab = 1;
4717 inSISIDXREG(SISCR, 0x97, reg);
4718 if(!(reg & 0x01)) { /* Single 32/16 */
4719 buswidth = 32;
4720 outSISIDXREG(SISSR, 0x13, 0xb1);
4721 outSISIDXREG(SISSR, 0x14, 0x52);
4722 sisfb_post_xgi_delay(ivideo, 1);
4723 sr14 = 0x02;
4724 if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize))
4725 goto bail_out;
4726
4727 outSISIDXREG(SISSR, 0x13, 0x31);
4728 outSISIDXREG(SISSR, 0x14, 0x42);
4729 sisfb_post_xgi_delay(ivideo, 1);
4730 if(sisfb_post_xgi_rwtest(ivideo, 23, 23, mapsize))
4731 goto bail_out;
4732
4733 buswidth = 16;
4734 outSISIDXREG(SISSR, 0x13, 0xb1);
4735 outSISIDXREG(SISSR, 0x14, 0x41);
4736 sisfb_post_xgi_delay(ivideo, 1);
4737 sr14 = 0x01;
4738 if(sisfb_post_xgi_rwtest(ivideo, 22, 23, mapsize))
4739 goto bail_out;
4740 else
4741 outSISIDXREG(SISSR, 0x13, 0x31);
4742 } else { /* Dual 16/8 */
4743 buswidth = 16;
4744 outSISIDXREG(SISSR, 0x13, 0xb1);
4745 outSISIDXREG(SISSR, 0x14, 0x41);
4746 sisfb_post_xgi_delay(ivideo, 1);
4747 sr14 = 0x01;
4748 if(sisfb_post_xgi_rwtest(ivideo, 22, 23, mapsize))
4749 goto bail_out;
4750
4751 outSISIDXREG(SISSR, 0x13, 0x31);
4752 outSISIDXREG(SISSR, 0x14, 0x31);
4753 sisfb_post_xgi_delay(ivideo, 1);
4754 if(sisfb_post_xgi_rwtest(ivideo, 22, 22, mapsize))
4755 goto bail_out;
4756
4757 buswidth = 8;
4758 outSISIDXREG(SISSR, 0x13, 0xb1);
4759 outSISIDXREG(SISSR, 0x14, 0x30);
4760 sisfb_post_xgi_delay(ivideo, 1);
4761 sr14 = 0x00;
4762 if(sisfb_post_xgi_rwtest(ivideo, 21, 22, mapsize))
4763 goto bail_out;
4764 else
4765 outSISIDXREG(SISSR, 0x13, 0x31);
4766 }
4767
4768 } else { /* XGI_40 */
4769
4770 inSISIDXREG(SISCR, 0x97, reg);
4771 if(!(reg & 0x10)) {
4772 inSISIDXREG(SISSR, 0x39, reg);
4773 reg >>= 1;
4774 }
4775
4776 if(reg & 0x01) { /* DDRII */
4777 buswidth = 32;
4778 if(ivideo->revision_id == 2) {
4779 channelab = 2;
4780 outSISIDXREG(SISSR, 0x13, 0xa1);
4781 outSISIDXREG(SISSR, 0x14, 0x44);
4782 sr14 = 0x04;
4783 sisfb_post_xgi_delay(ivideo, 1);
4784 if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize))
4785 goto bail_out;
4786
4787 outSISIDXREG(SISSR, 0x13, 0x21);
4788 outSISIDXREG(SISSR, 0x14, 0x34);
4789 if(sisfb_post_xgi_rwtest(ivideo, 22, 23, mapsize))
4790 goto bail_out;
4791
4792 channelab = 1;
4793 outSISIDXREG(SISSR, 0x13, 0xa1);
4794 outSISIDXREG(SISSR, 0x14, 0x40);
4795 sr14 = 0x00;
4796 if(sisfb_post_xgi_rwtest(ivideo, 22, 23, mapsize))
4797 goto bail_out;
4798
4799 outSISIDXREG(SISSR, 0x13, 0x21);
4800 outSISIDXREG(SISSR, 0x14, 0x30);
4801 } else {
4802 channelab = 3;
4803 outSISIDXREG(SISSR, 0x13, 0xa1);
4804 outSISIDXREG(SISSR, 0x14, 0x4c);
4805 sr14 = 0x0c;
4806 sisfb_post_xgi_delay(ivideo, 1);
4807 if(sisfb_post_xgi_rwtest(ivideo, 23, 25, mapsize))
4808 goto bail_out;
4809
4810 channelab = 2;
4811 outSISIDXREG(SISSR, 0x14, 0x48);
4812 sisfb_post_xgi_delay(ivideo, 1);
4813 sr14 = 0x08;
4814 if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize))
4815 goto bail_out;
4816
4817 outSISIDXREG(SISSR, 0x13, 0x21);
4818 outSISIDXREG(SISSR, 0x14, 0x3c);
4819 sr14 = 0x0c;
4820
4821 if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize)) {
4822 channelab = 3;
4823 } else {
4824 channelab = 2;
4825 outSISIDXREG(SISSR, 0x14, 0x38);
4826 sr14 = 0x08;
4827 }
4828 }
4829 sisfb_post_xgi_delay(ivideo, 1);
4830
4831 } else { /* DDR */
4832
4833 buswidth = 64;
4834 if(ivideo->revision_id == 2) {
4835 channelab = 1;
4836 outSISIDXREG(SISSR, 0x13, 0xa1);
4837 outSISIDXREG(SISSR, 0x14, 0x52);
4838 sisfb_post_xgi_delay(ivideo, 1);
4839 sr14 = 0x02;
4840 if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize))
4841 goto bail_out;
4842
4843 outSISIDXREG(SISSR, 0x13, 0x21);
4844 outSISIDXREG(SISSR, 0x14, 0x42);
4845 } else {
4846 channelab = 2;
4847 outSISIDXREG(SISSR, 0x13, 0xa1);
4848 outSISIDXREG(SISSR, 0x14, 0x5a);
4849 sisfb_post_xgi_delay(ivideo, 1);
4850 sr14 = 0x0a;
4851 if(sisfb_post_xgi_rwtest(ivideo, 24, 25, mapsize))
4852 goto bail_out;
4853
4854 outSISIDXREG(SISSR, 0x13, 0x21);
4855 outSISIDXREG(SISSR, 0x14, 0x4a);
4856 }
4857 sisfb_post_xgi_delay(ivideo, 1);
4858
4859 }
4860 }
4861
4862bail_out:
4863 setSISIDXREG(SISSR, 0x14, 0xf0, sr14);
4864 sisfb_post_xgi_delay(ivideo, 1);
4865
4866 j = (ivideo->chip == XGI_20) ? 5 : 9;
4867 k = (ivideo->chip == XGI_20) ? 12 : 4;
4868
4869 for(i = 0; i < k; i++) {
4870
4871 reg = (ivideo->chip == XGI_20) ?
4872 dramsr13[(i * 5) + 4] : dramsr13_4[(i * 5) + 4];
4873 setSISIDXREG(SISSR, 0x13, 0x80, reg);
4874 sisfb_post_xgi_delay(ivideo, 50);
4875
4876 ranksize = (ivideo->chip == XGI_20) ?
4877 dramsr13[(i * 5) + 3] : dramsr13_4[(i * 5) + 3];
4878
4879 inSISIDXREG(SISSR, 0x13, reg);
4880 if(reg & 0x80) ranksize <<= 1;
4881
4882 if(ivideo->chip == XGI_20) {
4883 if(buswidth == 16) ranksize <<= 1;
4884 else if(buswidth == 32) ranksize <<= 2;
4885 } else {
4886 if(buswidth == 64) ranksize <<= 1;
4887 }
4888
4889 reg = 0;
4890 l = channelab;
4891 if(l == 3) l = 4;
4892 if((ranksize * l) <= 256) {
4893 while((ranksize >>= 1)) reg += 0x10;
4894 }
4895
4896 if(!reg) continue;
4897
4898 setSISIDXREG(SISSR, 0x14, 0x0f, (reg & 0xf0));
4899 sisfb_post_xgi_delay(ivideo, 1);
4900
4901 if(sisfb_post_xgi_rwtest(ivideo, j, ((reg >> 4) + channelab - 2 + 20), mapsize))
4902 break;
4903 }
4904
4905 iounmap(ivideo->video_vbase);
4906}
4907
4908static void __devinit
4909sisfb_post_xgi_setclocks(struct sis_video_info *ivideo, u8 regb)
4910{
4911 u8 v1, v2, v3;
4912 int index;
4913 static const u8 cs90[8 * 3] = {
4914 0x16, 0x01, 0x01,
4915 0x3e, 0x03, 0x01,
4916 0x7c, 0x08, 0x01,
4917 0x79, 0x06, 0x01,
4918 0x29, 0x01, 0x81,
4919 0x5c, 0x23, 0x01,
4920 0x5c, 0x23, 0x01,
4921 0x5c, 0x23, 0x01
4922 };
4923 static const u8 csb8[8 * 3] = {
4924 0x5c, 0x23, 0x01,
4925 0x29, 0x01, 0x01,
4926 0x7c, 0x08, 0x01,
4927 0x79, 0x06, 0x01,
4928 0x29, 0x01, 0x81,
4929 0x5c, 0x23, 0x01,
4930 0x5c, 0x23, 0x01,
4931 0x5c, 0x23, 0x01
4932 };
4933
4934 regb = 0; /* ! */
4935
4936 index = regb * 3;
4937 v1 = cs90[index]; v2 = cs90[index + 1]; v3 = cs90[index + 2];
4938 if(ivideo->haveXGIROM) {
4939 v1 = ivideo->bios_abase[0x90 + index];
4940 v2 = ivideo->bios_abase[0x90 + index + 1];
4941 v3 = ivideo->bios_abase[0x90 + index + 2];
4942 }
4943 outSISIDXREG(SISSR, 0x28, v1);
4944 outSISIDXREG(SISSR, 0x29, v2);
4945 outSISIDXREG(SISSR, 0x2a, v3);
4946 sisfb_post_xgi_delay(ivideo, 0x43);
4947 sisfb_post_xgi_delay(ivideo, 0x43);
4948 sisfb_post_xgi_delay(ivideo, 0x43);
4949 index = regb * 3;
4950 v1 = csb8[index]; v2 = csb8[index + 1]; v3 = csb8[index + 2];
4951 if(ivideo->haveXGIROM) {
4952 v1 = ivideo->bios_abase[0xb8 + index];
4953 v2 = ivideo->bios_abase[0xb8 + index + 1];
4954 v3 = ivideo->bios_abase[0xb8 + index + 2];
4955 }
4956 outSISIDXREG(SISSR, 0x2e, v1);
4957 outSISIDXREG(SISSR, 0x2f, v2);
4958 outSISIDXREG(SISSR, 0x30, v3);
4959 sisfb_post_xgi_delay(ivideo, 0x43);
4960 sisfb_post_xgi_delay(ivideo, 0x43);
4961 sisfb_post_xgi_delay(ivideo, 0x43);
4962}
4963
4964static int __devinit
4965sisfb_post_xgi(struct pci_dev *pdev)
4966{
4967 struct sis_video_info *ivideo = pci_get_drvdata(pdev);
4968 unsigned char *bios = ivideo->bios_abase;
4969 struct pci_dev *mypdev = NULL;
4970 const u8 *ptr, *ptr2;
4971 u8 v1, v2, v3, v4, v5, reg, ramtype;
4972 u32 rega, regb, regd;
4973 int i, j, k, index;
4974 static const u8 cs78[3] = { 0xf6, 0x0d, 0x00 };
4975 static const u8 cs76[2] = { 0xa3, 0xfb };
4976 static const u8 cs7b[3] = { 0xc0, 0x11, 0x00 };
4977 static const u8 cs158[8] = {
4978 0x88, 0xaa, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00
4979 };
4980 static const u8 cs160[8] = {
4981 0x44, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00
4982 };
4983 static const u8 cs168[8] = {
4984 0x48, 0x78, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00
4985 };
4986 static const u8 cs128[3 * 8] = {
4987 0x90, 0x28, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00,
4988 0x77, 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00,
4989 0x77, 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00
4990 };
4991 static const u8 cs148[2 * 8] = {
4992 0x55, 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00,
4993 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
4994 };
4995 static const u8 cs31a[8 * 4] = {
4996 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
4997 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00,
4998 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4999 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
5000 };
5001 static const u8 cs33a[8 * 4] = {
5002 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5003 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5004 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5005 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
5006 };
5007 static const u8 cs45a[8 * 2] = {
5008 0x00, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0x00, 0x00,
5009 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
5010 };
5011 static const u8 cs170[7 * 8] = {
5012 0x54, 0x32, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00,
5013 0x54, 0x43, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00,
5014 0x0a, 0x05, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
5015 0x44, 0x34, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00,
5016 0x10, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
5017 0x11, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
5018 0x05, 0x05, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00
5019 };
5020 static const u8 cs1a8[3 * 8] = {
5021 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
5022 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
5023 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
5024 };
5025 static const u8 cs100[2 * 8] = {
5026 0xc4, 0x04, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
5027 0xc4, 0x04, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00
5028 };
5029
5030 /* VGA enable */
5031 reg = inSISREG(SISVGAENABLE) | 0x01;
5032 outSISREG(SISVGAENABLE, reg);
5033
5034 /* Misc */
5035 reg = inSISREG(SISMISCR) | 0x01;
5036 outSISREG(SISMISCW, reg);
5037
5038 /* Unlock SR */
5039 outSISIDXREG(SISSR, 0x05, 0x86);
5040 inSISIDXREG(SISSR, 0x05, reg);
5041 if(reg != 0xa1)
5042 return 0;
5043
5044 /* Clear some regs */
5045 for(i = 0; i < 0x22; i++) {
5046 if(0x06 + i == 0x20) continue;
5047 outSISIDXREG(SISSR, 0x06 + i, 0x00);
5048 }
5049 for(i = 0; i < 0x0b; i++) {
5050 outSISIDXREG(SISSR, 0x31 + i, 0x00);
5051 }
5052 for(i = 0; i < 0x10; i++) {
5053 outSISIDXREG(SISCR, 0x30 + i, 0x00);
5054 }
5055
5056 ptr = cs78;
5057 if(ivideo->haveXGIROM) {
5058 ptr = (const u8 *)&bios[0x78];
5059 }
5060 for(i = 0; i < 3; i++) {
5061 outSISIDXREG(SISSR, 0x23 + i, ptr[i]);
5062 }
5063
5064 ptr = cs76;
5065 if(ivideo->haveXGIROM) {
5066 ptr = (const u8 *)&bios[0x76];
5067 }
5068 for(i = 0; i < 2; i++) {
5069 outSISIDXREG(SISSR, 0x21 + i, ptr[i]);
5070 }
5071
5072 v1 = 0x18; v2 = 0x00;
5073 if(ivideo->haveXGIROM) {
5074 v1 = bios[0x74];
5075 v2 = bios[0x75];
5076 }
5077 outSISIDXREG(SISSR, 0x07, v1);
5078 outSISIDXREG(SISSR, 0x11, 0x0f);
5079 outSISIDXREG(SISSR, 0x1f, v2);
5080 /* PCI linear mode, RelIO enabled, A0000 decoding disabled */
5081 outSISIDXREG(SISSR, 0x20, 0x80 | 0x20 | 0x04);
5082 outSISIDXREG(SISSR, 0x27, 0x74);
5083
5084 ptr = cs7b;
5085 if(ivideo->haveXGIROM) {
5086 ptr = (const u8 *)&bios[0x7b];
5087 }
5088 for(i = 0; i < 3; i++) {
5089 outSISIDXREG(SISSR, 0x31 + i, ptr[i]);
5090 }
5091
5092 if(ivideo->chip == XGI_40) {
5093 if(ivideo->revision_id == 2) {
5094 setSISIDXREG(SISSR, 0x3b, 0x3f, 0xc0);
5095 }
5096 outSISIDXREG(SISCR, 0x7d, 0xfe);
5097 outSISIDXREG(SISCR, 0x7e, 0x0f);
5098 }
5099 if(ivideo->revision_id == 0) { /* 40 *and* 20? */
5100 andSISIDXREG(SISCR, 0x58, 0xd7);
5101 inSISIDXREG(SISCR, 0xcb, reg);
5102 if(reg & 0x20) {
5103 setSISIDXREG(SISCR, 0x58, 0xd7, (reg & 0x10) ? 0x08 : 0x20); /* =0x28 Z7 ? */
5104 }
5105 }
5106
5107 reg = (ivideo->chip == XGI_40) ? 0x20 : 0x00;
5108 setSISIDXREG(SISCR, 0x38, 0x1f, reg);
5109
5110 if(ivideo->chip == XGI_20) {
5111 outSISIDXREG(SISSR, 0x36, 0x70);
5112 } else {
5113 outSISIDXREG(SISVID, 0x00, 0x86);
5114 outSISIDXREG(SISVID, 0x32, 0x00);
5115 outSISIDXREG(SISVID, 0x30, 0x00);
5116 outSISIDXREG(SISVID, 0x32, 0x01);
5117 outSISIDXREG(SISVID, 0x30, 0x00);
5118 andSISIDXREG(SISVID, 0x2f, 0xdf);
5119 andSISIDXREG(SISCAP, 0x00, 0x3f);
5120
5121 outSISIDXREG(SISPART1, 0x2f, 0x01);
5122 outSISIDXREG(SISPART1, 0x00, 0x00);
5123 outSISIDXREG(SISPART1, 0x02, bios[0x7e]);
5124 outSISIDXREG(SISPART1, 0x2e, 0x08);
5125 andSISIDXREG(SISPART1, 0x35, 0x7f);
5126 andSISIDXREG(SISPART1, 0x50, 0xfe);
5127
5128 inSISIDXREG(SISPART4, 0x00, reg);
5129 if(reg == 1 || reg == 2) {
5130 outSISIDXREG(SISPART2, 0x00, 0x1c);
5131 outSISIDXREG(SISPART4, 0x0d, bios[0x7f]);
5132 outSISIDXREG(SISPART4, 0x0e, bios[0x80]);
5133 outSISIDXREG(SISPART4, 0x10, bios[0x81]);
5134 andSISIDXREG(SISPART4, 0x0f, 0x3f);
5135
5136 inSISIDXREG(SISPART4, 0x01, reg);
5137 if((reg & 0xf0) >= 0xb0) {
5138 inSISIDXREG(SISPART4, 0x23, reg);
5139 if(reg & 0x20) reg |= 0x40;
5140 outSISIDXREG(SISPART4, 0x23, reg);
5141 reg = (reg & 0x20) ? 0x02 : 0x00;
5142 setSISIDXREG(SISPART1, 0x1e, 0xfd, reg);
5143 }
5144 }
5145
5146 v1 = bios[0x77];
5147
5148 inSISIDXREG(SISSR, 0x3b, reg);
5149 if(reg & 0x02) {
5150 inSISIDXREG(SISSR, 0x3a, reg);
5151 v2 = (reg & 0x30) >> 3;
5152 if(!(v2 & 0x04)) v2 ^= 0x02;
5153 inSISIDXREG(SISSR, 0x39, reg);
5154 if(reg & 0x80) v2 |= 0x80;
5155 v2 |= 0x01;
5156
5157 if((mypdev = SIS_PCI_GET_DEVICE(PCI_VENDOR_ID_SI, 0x0730, NULL))) {
5158 SIS_PCI_PUT_DEVICE(mypdev);
5159 if(((v2 & 0x06) == 2) || ((v2 & 0x06) == 4))
5160 v2 &= 0xf9;
5161 v2 |= 0x08;
5162 v1 &= 0xfe;
5163 } else {
5164 mypdev = SIS_PCI_GET_DEVICE(PCI_VENDOR_ID_SI, 0x0735, NULL);
5165 if(!mypdev)
5166 mypdev = SIS_PCI_GET_DEVICE(PCI_VENDOR_ID_SI, 0x0645, NULL);
5167 if(!mypdev)
5168 mypdev = SIS_PCI_GET_DEVICE(PCI_VENDOR_ID_SI, 0x0650, NULL);
5169 if(mypdev) {
5170 pci_read_config_dword(mypdev, 0x94, &regd);
5171 regd &= 0xfffffeff;
5172 pci_write_config_dword(mypdev, 0x94, regd);
5173 v1 &= 0xfe;
5174 SIS_PCI_PUT_DEVICE(mypdev);
5175 } else if(sisfb_find_host_bridge(ivideo, pdev, PCI_VENDOR_ID_SI)) {
5176 v1 &= 0xfe;
5177 } else if(sisfb_find_host_bridge(ivideo, pdev, 0x1106) ||
5178 sisfb_find_host_bridge(ivideo, pdev, 0x1022) ||
5179 sisfb_find_host_bridge(ivideo, pdev, 0x700e) ||
5180 sisfb_find_host_bridge(ivideo, pdev, 0x10de)) {
5181 if((v2 & 0x06) == 4)
5182 v2 ^= 0x06;
5183 v2 |= 0x08;
5184 }
5185 }
5186 setSISIDXREG(SISCR, 0x5f, 0xf0, v2);
5187 }
5188 outSISIDXREG(SISSR, 0x22, v1);
5189
5190 if(ivideo->revision_id == 2) {
5191 inSISIDXREG(SISSR, 0x3b, v1);
5192 inSISIDXREG(SISSR, 0x3a, v2);
5193 regd = bios[0x90 + 3] | (bios[0x90 + 4] << 8);
5194 if( (!(v1 & 0x02)) && (v2 & 0x30) && (regd < 0xcf) )
5195 setSISIDXREG(SISCR, 0x5f, 0xf1, 0x01);
5196
5197 if((mypdev = SIS_PCI_GET_DEVICE(0x10de, 0x01e0, NULL))) {
5198 /* TODO: set CR5f &0xf1 | 0x01 for version 6570
5199 * of nforce 2 ROM
5200 */
5201 if(0)
5202 setSISIDXREG(SISCR, 0x5f, 0xf1, 0x01);
5203 SIS_PCI_PUT_DEVICE(mypdev);
5204 }
5205 }
5206
5207 v1 = 0x30;
5208 inSISIDXREG(SISSR, 0x3b, reg);
5209 inSISIDXREG(SISCR, 0x5f, v2);
5210 if((!(reg & 0x02)) && (v2 & 0x0e))
5211 v1 |= 0x08;
5212 outSISIDXREG(SISSR, 0x27, v1);
5213
5214 if(bios[0x64] & 0x01) {
5215 setSISIDXREG(SISCR, 0x5f, 0xf0, bios[0x64]);
5216 }
5217
5218 v1 = bios[0x4f7];
5219 pci_read_config_dword(pdev, 0x50, &regd);
5220 regd = (regd >> 20) & 0x0f;
5221 if(regd == 1) {
5222 v1 &= 0xfc;
5223 orSISIDXREG(SISCR, 0x5f, 0x08);
5224 }
5225 outSISIDXREG(SISCR, 0x48, v1);
5226
5227 setSISIDXREG(SISCR, 0x47, 0x04, bios[0x4f6] & 0xfb);
5228 setSISIDXREG(SISCR, 0x49, 0xf0, bios[0x4f8] & 0x0f);
5229 setSISIDXREG(SISCR, 0x4a, 0x60, bios[0x4f9] & 0x9f);
5230 setSISIDXREG(SISCR, 0x4b, 0x08, bios[0x4fa] & 0xf7);
5231 setSISIDXREG(SISCR, 0x4c, 0x80, bios[0x4fb] & 0x7f);
5232 outSISIDXREG(SISCR, 0x70, bios[0x4fc]);
5233 setSISIDXREG(SISCR, 0x71, 0xf0, bios[0x4fd] & 0x0f);
5234 outSISIDXREG(SISCR, 0x74, 0xd0);
5235 setSISIDXREG(SISCR, 0x74, 0xcf, bios[0x4fe] & 0x30);
5236 setSISIDXREG(SISCR, 0x75, 0xe0, bios[0x4ff] & 0x1f);
5237 setSISIDXREG(SISCR, 0x76, 0xe0, bios[0x500] & 0x1f);
5238 v1 = bios[0x501];
5239 if((mypdev = SIS_PCI_GET_DEVICE(0x8086, 0x2530, NULL))) {
5240 v1 = 0xf0;
5241 SIS_PCI_PUT_DEVICE(mypdev);
5242 }
5243 outSISIDXREG(SISCR, 0x77, v1);
5244 }
5245
5246 /* RAM type */
5247
5248 regb = 0; /* ! */
5249
5250 v1 = 0xff;
5251 if(ivideo->haveXGIROM) {
5252 v1 = bios[0x140 + regb];
5253 }
5254 outSISIDXREG(SISCR, 0x6d, v1);
5255
5256 ptr = cs128;
5257 if(ivideo->haveXGIROM) {
5258 ptr = (const u8 *)&bios[0x128];
5259 }
5260 for(i = 0, j = 0; i < 3; i++, j += 8) {
5261 outSISIDXREG(SISCR, 0x68 + i, ptr[j + regb]);
5262 }
5263
5264 ptr = cs31a;
5265 ptr2 = cs33a;
5266 if(ivideo->haveXGIROM) {
5267 index = (ivideo->chip == XGI_20) ? 0x31a : 0x3a6;
5268 ptr = (const u8 *)&bios[index];
5269 ptr2 = (const u8 *)&bios[index + 0x20];
5270 }
5271 for(i = 0; i < 2; i++) {
5272 if(i == 0) {
5273 regd = le32_to_cpu(((u32 *)ptr)[regb]);
5274 rega = 0x6b;
5275 } else {
5276 regd = le32_to_cpu(((u32 *)ptr2)[regb]);
5277 rega = 0x6e;
5278 }
5279 reg = 0x00;
5280 for(j = 0; j < 16; j++) {
5281 reg &= 0xf3;
5282 if(regd & 0x01) reg |= 0x04;
5283 if(regd & 0x02) reg |= 0x08;
5284 regd >>= 2;
5285 outSISIDXREG(SISCR, rega, reg);
5286 inSISIDXREG(SISCR, rega, reg);
5287 inSISIDXREG(SISCR, rega, reg);
5288 reg += 0x10;
5289 }
5290 }
5291
5292 andSISIDXREG(SISCR, 0x6e, 0xfc);
5293
5294 ptr = NULL;
5295 if(ivideo->haveXGIROM) {
5296 index = (ivideo->chip == XGI_20) ? 0x35a : 0x3e6;
5297 ptr = (const u8 *)&bios[index];
5298 }
5299 for(i = 0; i < 4; i++) {
5300 setSISIDXREG(SISCR, 0x6e, 0xfc, i);
5301 reg = 0x00;
5302 for(j = 0; j < 2; j++) {
5303 regd = 0;
5304 if(ptr) {
5305 regd = le32_to_cpu(((u32 *)ptr)[regb * 8]);
5306 ptr += 4;
5307 }
5308 /* reg = 0x00; */
5309 for(k = 0; k < 16; k++) {
5310 reg &= 0xfc;
5311 if(regd & 0x01) reg |= 0x01;
5312 if(regd & 0x02) reg |= 0x02;
5313 regd >>= 2;
5314 outSISIDXREG(SISCR, 0x6f, reg);
5315 inSISIDXREG(SISCR, 0x6f, reg);
5316 inSISIDXREG(SISCR, 0x6f, reg);
5317 reg += 0x08;
5318 }
5319 }
5320 }
5321
5322 ptr = cs148;
5323 if(ivideo->haveXGIROM) {
5324 ptr = (const u8 *)&bios[0x148];
5325 }
5326 for(i = 0, j = 0; i < 2; i++, j += 8) {
5327 outSISIDXREG(SISCR, 0x80 + i, ptr[j + regb]);
5328 }
5329
5330 andSISIDXREG(SISCR, 0x89, 0x8f);
5331
5332 ptr = cs45a;
5333 if(ivideo->haveXGIROM) {
5334 index = (ivideo->chip == XGI_20) ? 0x45a : 0x4e6;
5335 ptr = (const u8 *)&bios[index];
5336 }
5337 regd = le16_to_cpu(((const u16 *)ptr)[regb]);
5338 reg = 0x80;
5339 for(i = 0; i < 5; i++) {
5340 reg &= 0xfc;
5341 if(regd & 0x01) reg |= 0x01;
5342 if(regd & 0x02) reg |= 0x02;
5343 regd >>= 2;
5344 outSISIDXREG(SISCR, 0x89, reg);
5345 inSISIDXREG(SISCR, 0x89, reg);
5346 inSISIDXREG(SISCR, 0x89, reg);
5347 reg += 0x10;
5348 }
5349
5350 v1 = 0xb5; v2 = 0x20; v3 = 0xf0; v4 = 0x13;
5351 if(ivideo->haveXGIROM) {
5352 v1 = bios[0x118 + regb];
5353 v2 = bios[0xf8 + regb];
5354 v3 = bios[0x120 + regb];
5355 v4 = bios[0x1ca];
5356 }
5357 outSISIDXREG(SISCR, 0x45, v1 & 0x0f);
5358 outSISIDXREG(SISCR, 0x99, (v1 >> 4) & 0x07);
5359 orSISIDXREG(SISCR, 0x40, v1 & 0x80);
5360 outSISIDXREG(SISCR, 0x41, v2);
5361
5362 ptr = cs170;
5363 if(ivideo->haveXGIROM) {
5364 ptr = (const u8 *)&bios[0x170];
5365 }
5366 for(i = 0, j = 0; i < 7; i++, j += 8) {
5367 outSISIDXREG(SISCR, 0x90 + i, ptr[j + regb]);
5368 }
5369
5370 outSISIDXREG(SISCR, 0x59, v3);
5371
5372 ptr = cs1a8;
5373 if(ivideo->haveXGIROM) {
5374 ptr = (const u8 *)&bios[0x1a8];
5375 }
5376 for(i = 0, j = 0; i < 3; i++, j += 8) {
5377 outSISIDXREG(SISCR, 0xc3 + i, ptr[j + regb]);
5378 }
5379
5380 ptr = cs100;
5381 if(ivideo->haveXGIROM) {
5382 ptr = (const u8 *)&bios[0x100];
5383 }
5384 for(i = 0, j = 0; i < 2; i++, j += 8) {
5385 outSISIDXREG(SISCR, 0x8a + i, ptr[j + regb]);
5386 }
5387
5388 outSISIDXREG(SISCR, 0xcf, v4);
5389
5390 outSISIDXREG(SISCR, 0x83, 0x09);
5391 outSISIDXREG(SISCR, 0x87, 0x00);
5392
5393 if(ivideo->chip == XGI_40) {
5394 if( (ivideo->revision_id == 1) ||
5395 (ivideo->revision_id == 2) ) {
5396 outSISIDXREG(SISCR, 0x8c, 0x87);
5397 }
5398 }
5399
5400 outSISIDXREG(SISSR, 0x17, 0x00);
5401 outSISIDXREG(SISSR, 0x1a, 0x87);
5402
5403 if(ivideo->chip == XGI_20) {
5404 outSISIDXREG(SISSR, 0x15, 0x00);
5405 outSISIDXREG(SISSR, 0x1c, 0x00);
5406 }
5407
5408 ramtype = 0x00; v1 = 0x10;
5409 if(ivideo->haveXGIROM) {
5410 ramtype = bios[0x62];
5411 v1 = bios[0x1d2];
5412 }
5413 if(!(ramtype & 0x80)) {
5414 if(ivideo->chip == XGI_20) {
5415 outSISIDXREG(SISCR, 0x97, v1);
5416 inSISIDXREG(SISCR, 0x97, reg);
5417 if(reg & 0x10) {
5418 ramtype = (reg & 0x01) << 1;
5419 }
5420 } else {
5421 inSISIDXREG(SISSR, 0x39, reg);
5422 ramtype = reg & 0x02;
5423 if(!(ramtype)) {
5424 inSISIDXREG(SISSR, 0x3a, reg);
5425 ramtype = (reg >> 1) & 0x01;
5426 }
5427 }
5428 }
5429 ramtype &= 0x07;
5430
5431 regb = 0; /* ! */
5432
5433 switch(ramtype) {
5434 case 0:
5435 sisfb_post_xgi_setclocks(ivideo, regb);
5436 if((ivideo->chip == XGI_20) ||
5437 (ivideo->revision_id == 1) ||
5438 (ivideo->revision_id == 2)) {
5439 v1 = cs158[regb]; v2 = cs160[regb]; v3 = cs168[regb];
5440 if(ivideo->haveXGIROM) {
5441 v1 = bios[regb + 0x158];
5442 v2 = bios[regb + 0x160];
5443 v3 = bios[regb + 0x168];
5444 }
5445 outSISIDXREG(SISCR, 0x82, v1);
5446 outSISIDXREG(SISCR, 0x85, v2);
5447 outSISIDXREG(SISCR, 0x86, v3);
5448 } else {
5449 outSISIDXREG(SISCR, 0x82, 0x88);
5450 outSISIDXREG(SISCR, 0x86, 0x00);
5451 inSISIDXREG(SISCR, 0x86, reg);
5452 outSISIDXREG(SISCR, 0x86, 0x88);
5453 inSISIDXREG(SISCR, 0x86, reg);
5454 outSISIDXREG(SISCR, 0x86, bios[regb + 0x168]);
5455 outSISIDXREG(SISCR, 0x82, 0x77);
5456 outSISIDXREG(SISCR, 0x85, 0x00);
5457 inSISIDXREG(SISCR, 0x85, reg);
5458 outSISIDXREG(SISCR, 0x85, 0x88);
5459 inSISIDXREG(SISCR, 0x85, reg);
5460 outSISIDXREG(SISCR, 0x85, bios[regb + 0x160]);
5461 outSISIDXREG(SISCR, 0x82, bios[regb + 0x158]);
5462 }
5463 if(ivideo->chip == XGI_40) {
5464 outSISIDXREG(SISCR, 0x97, 0x00);
5465 }
5466 outSISIDXREG(SISCR, 0x98, 0x01);
5467 outSISIDXREG(SISCR, 0x9a, 0x02);
5468
5469 outSISIDXREG(SISSR, 0x18, 0x01);
5470 if((ivideo->chip == XGI_20) ||
5471 (ivideo->revision_id == 2)) {
5472 outSISIDXREG(SISSR, 0x19, 0x40);
5473 } else {
5474 outSISIDXREG(SISSR, 0x19, 0x20);
5475 }
5476 outSISIDXREG(SISSR, 0x16, 0x00);
5477 outSISIDXREG(SISSR, 0x16, 0x80);
5478 if((ivideo->chip == XGI_20) || (bios[0x1cb] != 0x0c)) {
5479 sisfb_post_xgi_delay(ivideo, 0x43);
5480 sisfb_post_xgi_delay(ivideo, 0x43);
5481 sisfb_post_xgi_delay(ivideo, 0x43);
5482 outSISIDXREG(SISSR, 0x18, 0x00);
5483 if((ivideo->chip == XGI_20) ||
5484 (ivideo->revision_id == 2)) {
5485 outSISIDXREG(SISSR, 0x19, 0x40);
5486 } else {
5487 outSISIDXREG(SISSR, 0x19, 0x20);
5488 }
5489 } else if((ivideo->chip == XGI_40) && (bios[0x1cb] == 0x0c)) {
5490 /* outSISIDXREG(SISSR, 0x16, 0x0c); */ /* ? */
5491 }
5492 outSISIDXREG(SISSR, 0x16, 0x00);
5493 outSISIDXREG(SISSR, 0x16, 0x80);
5494 sisfb_post_xgi_delay(ivideo, 4);
5495 v1 = 0x31; v2 = 0x03; v3 = 0x83; v4 = 0x03; v5 = 0x83;
5496 if(ivideo->haveXGIROM) {
5497 v1 = bios[0xf0];
5498 index = (ivideo->chip == XGI_20) ? 0x4b2 : 0x53e;
5499 v2 = bios[index];
5500 v3 = bios[index + 1];
5501 v4 = bios[index + 2];
5502 v5 = bios[index + 3];
5503 }
5504 outSISIDXREG(SISSR, 0x18, v1);
5505 outSISIDXREG(SISSR, 0x19, ((ivideo->chip == XGI_20) ? 0x02 : 0x01));
5506 outSISIDXREG(SISSR, 0x16, v2);
5507 outSISIDXREG(SISSR, 0x16, v3);
5508 sisfb_post_xgi_delay(ivideo, 0x43);
5509 outSISIDXREG(SISSR, 0x1b, 0x03);
5510 sisfb_post_xgi_delay(ivideo, 0x22);
5511 outSISIDXREG(SISSR, 0x18, v1);
5512 outSISIDXREG(SISSR, 0x19, 0x00);
5513 outSISIDXREG(SISSR, 0x16, v4);
5514 outSISIDXREG(SISSR, 0x16, v5);
5515 outSISIDXREG(SISSR, 0x1b, 0x00);
5516 break;
5517 case 1:
5518 outSISIDXREG(SISCR, 0x82, 0x77);
5519 outSISIDXREG(SISCR, 0x86, 0x00);
5520 inSISIDXREG(SISCR, 0x86, reg);
5521 outSISIDXREG(SISCR, 0x86, 0x88);
5522 inSISIDXREG(SISCR, 0x86, reg);
5523 v1 = cs168[regb]; v2 = cs160[regb]; v3 = cs158[regb];
5524 if(ivideo->haveXGIROM) {
5525 v1 = bios[regb + 0x168];
5526 v2 = bios[regb + 0x160];
5527 v3 = bios[regb + 0x158];
5528 }
5529 outSISIDXREG(SISCR, 0x86, v1);
5530 outSISIDXREG(SISCR, 0x82, 0x77);
5531 outSISIDXREG(SISCR, 0x85, 0x00);
5532 inSISIDXREG(SISCR, 0x85, reg);
5533 outSISIDXREG(SISCR, 0x85, 0x88);
5534 inSISIDXREG(SISCR, 0x85, reg);
5535 outSISIDXREG(SISCR, 0x85, v2);
5536 outSISIDXREG(SISCR, 0x82, v3);
5537 outSISIDXREG(SISCR, 0x98, 0x01);
5538 outSISIDXREG(SISCR, 0x9a, 0x02);
5539
5540 outSISIDXREG(SISSR, 0x28, 0x64);
5541 outSISIDXREG(SISSR, 0x29, 0x63);
5542 sisfb_post_xgi_delay(ivideo, 15);
5543 outSISIDXREG(SISSR, 0x18, 0x00);
5544 outSISIDXREG(SISSR, 0x19, 0x20);
5545 outSISIDXREG(SISSR, 0x16, 0x00);
5546 outSISIDXREG(SISSR, 0x16, 0x80);
5547 outSISIDXREG(SISSR, 0x18, 0xc5);
5548 outSISIDXREG(SISSR, 0x19, 0x23);
5549 outSISIDXREG(SISSR, 0x16, 0x00);
5550 outSISIDXREG(SISSR, 0x16, 0x80);
5551 sisfb_post_xgi_delay(ivideo, 1);
5552 outSISIDXREG(SISCR, 0x97,0x11);
5553 sisfb_post_xgi_setclocks(ivideo, regb);
5554 sisfb_post_xgi_delay(ivideo, 0x46);
5555 outSISIDXREG(SISSR, 0x18, 0xc5);
5556 outSISIDXREG(SISSR, 0x19, 0x23);
5557 outSISIDXREG(SISSR, 0x16, 0x00);
5558 outSISIDXREG(SISSR, 0x16, 0x80);
5559 sisfb_post_xgi_delay(ivideo, 1);
5560 outSISIDXREG(SISSR, 0x1b, 0x04);
5561 sisfb_post_xgi_delay(ivideo, 1);
5562 outSISIDXREG(SISSR, 0x1b, 0x00);
5563 sisfb_post_xgi_delay(ivideo, 1);
5564 v1 = 0x31;
5565 if(ivideo->haveXGIROM) {
5566 v1 = bios[0xf0];
5567 }
5568 outSISIDXREG(SISSR, 0x18, v1);
5569 outSISIDXREG(SISSR, 0x19, 0x06);
5570 outSISIDXREG(SISSR, 0x16, 0x04);
5571 outSISIDXREG(SISSR, 0x16, 0x84);
5572 sisfb_post_xgi_delay(ivideo, 1);
5573 break;
5574 default:
5575 sisfb_post_xgi_setclocks(ivideo, regb);
5576 if((ivideo->chip == XGI_40) &&
5577 ((ivideo->revision_id == 1) ||
5578 (ivideo->revision_id == 2))) {
5579 outSISIDXREG(SISCR, 0x82, bios[regb + 0x158]);
5580 outSISIDXREG(SISCR, 0x85, bios[regb + 0x160]);
5581 outSISIDXREG(SISCR, 0x86, bios[regb + 0x168]);
5582 } else {
5583 outSISIDXREG(SISCR, 0x82, 0x88);
5584 outSISIDXREG(SISCR, 0x86, 0x00);
5585 inSISIDXREG(SISCR, 0x86, reg);
5586 outSISIDXREG(SISCR, 0x86, 0x88);
5587 outSISIDXREG(SISCR, 0x82, 0x77);
5588 outSISIDXREG(SISCR, 0x85, 0x00);
5589 inSISIDXREG(SISCR, 0x85, reg);
5590 outSISIDXREG(SISCR, 0x85, 0x88);
5591 inSISIDXREG(SISCR, 0x85, reg);
5592 v1 = cs160[regb]; v2 = cs158[regb];
5593 if(ivideo->haveXGIROM) {
5594 v1 = bios[regb + 0x160];
5595 v2 = bios[regb + 0x158];
5596 }
5597 outSISIDXREG(SISCR, 0x85, v1);
5598 outSISIDXREG(SISCR, 0x82, v2);
5599 }
5600 if(ivideo->chip == XGI_40) {
5601 outSISIDXREG(SISCR, 0x97, 0x11);
5602 }
5603 if((ivideo->chip == XGI_40) && (ivideo->revision_id == 2)) {
5604 outSISIDXREG(SISCR, 0x98, 0x01);
5605 } else {
5606 outSISIDXREG(SISCR, 0x98, 0x03);
5607 }
5608 outSISIDXREG(SISCR, 0x9a, 0x02);
5609
5610 if(ivideo->chip == XGI_40) {
5611 outSISIDXREG(SISSR, 0x18, 0x01);
5612 } else {
5613 outSISIDXREG(SISSR, 0x18, 0x00);
5614 }
5615 outSISIDXREG(SISSR, 0x19, 0x40);
5616 outSISIDXREG(SISSR, 0x16, 0x00);
5617 outSISIDXREG(SISSR, 0x16, 0x80);
5618 if((ivideo->chip == XGI_40) && (bios[0x1cb] != 0x0c)) {
5619 sisfb_post_xgi_delay(ivideo, 0x43);
5620 sisfb_post_xgi_delay(ivideo, 0x43);
5621 sisfb_post_xgi_delay(ivideo, 0x43);
5622 outSISIDXREG(SISSR, 0x18, 0x00);
5623 outSISIDXREG(SISSR, 0x19, 0x40);
5624 outSISIDXREG(SISSR, 0x16, 0x00);
5625 outSISIDXREG(SISSR, 0x16, 0x80);
5626 }
5627 sisfb_post_xgi_delay(ivideo, 4);
5628 v1 = 0x31;
5629 if(ivideo->haveXGIROM) {
5630 v1 = bios[0xf0];
5631 }
5632 outSISIDXREG(SISSR, 0x18, v1);
5633 outSISIDXREG(SISSR, 0x19, 0x01);
5634 if(ivideo->chip == XGI_40) {
5635 outSISIDXREG(SISSR, 0x16, bios[0x53e]);
5636 outSISIDXREG(SISSR, 0x16, bios[0x53f]);
5637 } else {
5638 outSISIDXREG(SISSR, 0x16, 0x05);
5639 outSISIDXREG(SISSR, 0x16, 0x85);
5640 }
5641 sisfb_post_xgi_delay(ivideo, 0x43);
5642 if(ivideo->chip == XGI_40) {
5643 outSISIDXREG(SISSR, 0x1b, 0x01);
5644 } else {
5645 outSISIDXREG(SISSR, 0x1b, 0x03);
5646 }
5647 sisfb_post_xgi_delay(ivideo, 0x22);
5648 outSISIDXREG(SISSR, 0x18, v1);
5649 outSISIDXREG(SISSR, 0x19, 0x00);
5650 if(ivideo->chip == XGI_40) {
5651 outSISIDXREG(SISSR, 0x16, bios[0x540]);
5652 outSISIDXREG(SISSR, 0x16, bios[0x541]);
5653 } else {
5654 outSISIDXREG(SISSR, 0x16, 0x05);
5655 outSISIDXREG(SISSR, 0x16, 0x85);
5656 }
5657 outSISIDXREG(SISSR, 0x1b, 0x00);
5658 }
5659
5660 regb = 0; /* ! */
5661 v1 = 0x03;
5662 if(ivideo->haveXGIROM) {
5663 v1 = bios[0x110 + regb];
5664 }
5665 outSISIDXREG(SISSR, 0x1b, v1);
5666
5667 /* RAM size */
5668 v1 = 0x00; v2 = 0x00;
5669 if(ivideo->haveXGIROM) {
5670 v1 = bios[0x62];
5671 v2 = bios[0x63];
5672 }
5673 regb = 0; /* ! */
5674 regd = 1 << regb;
5675 if((v1 & 0x40) && (v2 & regd) && ivideo->haveXGIROM) {
5676
5677 outSISIDXREG(SISSR, 0x13, bios[regb + 0xe0]);
5678 outSISIDXREG(SISSR, 0x14, bios[regb + 0xe0 + 8]);
5679
5680 } else {
5681
5682 /* Set default mode, don't clear screen */
5683 ivideo->SiS_Pr.SiS_UseOEM = FALSE;
5684 SiS_SetEnableDstn(&ivideo->SiS_Pr, FALSE);
5685 SiS_SetEnableFstn(&ivideo->SiS_Pr, FALSE);
5686 ivideo->curFSTN = ivideo->curDSTN = 0;
5687 ivideo->SiS_Pr.VideoMemorySize = 8 << 20;
5688 SiSSetMode(&ivideo->SiS_Pr, 0x2e | 0x80);
5689
5690 outSISIDXREG(SISSR, 0x05, 0x86);
5691
5692 /* Disable read-cache */
5693 andSISIDXREG(SISSR, 0x21, 0xdf);
5694 sisfb_post_xgi_ramsize(ivideo);
5695 /* Enable read-cache */
5696 orSISIDXREG(SISSR, 0x21, 0x20);
5697
5698 }
5699
5700#if 0
5701 printk(KERN_DEBUG "-----------------\n");
5702 for(i = 0; i < 0xff; i++) {
5703 inSISIDXREG(SISCR, i, reg);
5704 printk(KERN_DEBUG "CR%02x(%x) = 0x%02x\n", i, SISCR, reg);
5705 }
5706 for(i = 0; i < 0x40; i++) {
5707 inSISIDXREG(SISSR, i, reg);
5708 printk(KERN_DEBUG "SR%02x(%x) = 0x%02x\n", i, SISSR, reg);
5709 }
5710 printk(KERN_DEBUG "-----------------\n");
5711#endif
5712
5713 /* Sense CRT1 */
5714 if(ivideo->chip == XGI_20) {
5715 orSISIDXREG(SISCR, 0x32, 0x20);
5716 } else {
5717 inSISIDXREG(SISPART4, 0x00, reg);
5718 if((reg == 1) || (reg == 2)) {
5719 sisfb_sense_crt1(ivideo);
5720 } else {
5721 orSISIDXREG(SISCR, 0x32, 0x20);
5722 }
5723 }
5724
5725 /* Set default mode, don't clear screen */
5726 ivideo->SiS_Pr.SiS_UseOEM = FALSE;
5727 SiS_SetEnableDstn(&ivideo->SiS_Pr, FALSE);
5728 SiS_SetEnableFstn(&ivideo->SiS_Pr, FALSE);
5729 ivideo->curFSTN = ivideo->curDSTN = 0;
5730 SiSSetMode(&ivideo->SiS_Pr, 0x2e | 0x80);
5731
5732 outSISIDXREG(SISSR, 0x05, 0x86);
5733
5734 /* Display off */
5735 orSISIDXREG(SISSR, 0x01, 0x20);
5736
5737 /* Save mode number in CR34 */
5738 outSISIDXREG(SISCR, 0x34, 0x2e);
5739
5740 /* Let everyone know what the current mode is */
5741 ivideo->modeprechange = 0x2e;
5742
5743 if(ivideo->chip == XGI_40) {
5744 inSISIDXREG(SISCR, 0xca, reg);
5745 inSISIDXREG(SISCR, 0xcc, v1);
5746 if((reg & 0x10) && (!(v1 & 0x04))) {
5747 printk(KERN_ERR
5748 "sisfb: Please connect power to the card.\n");
5749 return 0;
5750 }
5751 }
5752
5753 return 1;
5754}
5755#endif
5756
5757static int __devinit
5758sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
5759{
5760 struct sisfb_chip_info *chipinfo = &sisfb_chip_info[ent->driver_data];
5761 struct sis_video_info *ivideo = NULL;
5762 struct fb_info *sis_fb_info = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005763 u16 reg16;
5764 u8 reg;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005765 int i, ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005766
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005767 if(sisfb_off)
5768 return -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005769
Linus Torvalds1da177e2005-04-16 15:20:36 -07005770 sis_fb_info = framebuffer_alloc(sizeof(*ivideo), &pdev->dev);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005771 if(!sis_fb_info)
5772 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005773
5774 ivideo = (struct sis_video_info *)sis_fb_info->par;
5775 ivideo->memyselfandi = sis_fb_info;
5776
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005777 ivideo->sisfb_id = SISFB_ID;
5778
Linus Torvalds1da177e2005-04-16 15:20:36 -07005779 if(card_list == NULL) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005780 ivideo->cardnumber = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005781 } else {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005782 struct sis_video_info *countvideo = card_list;
5783 ivideo->cardnumber = 1;
5784 while((countvideo = countvideo->next) != 0)
5785 ivideo->cardnumber++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005786 }
5787
5788 strncpy(ivideo->myid, chipinfo->chip_name, 30);
5789
5790 ivideo->warncount = 0;
5791 ivideo->chip_id = pdev->device;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005792 ivideo->chip_vendor = pdev->vendor;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005793 pci_read_config_byte(pdev, PCI_REVISION_ID, &ivideo->revision_id);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005794 ivideo->SiS_Pr.ChipRevision = ivideo->revision_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005795 pci_read_config_word(pdev, PCI_COMMAND, &reg16);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005796 ivideo->sisvga_enabled = reg16 & 0x01;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005797 ivideo->pcibus = pdev->bus->number;
5798 ivideo->pcislot = PCI_SLOT(pdev->devfn);
5799 ivideo->pcifunc = PCI_FUNC(pdev->devfn);
5800 ivideo->subsysvendor = pdev->subsystem_vendor;
5801 ivideo->subsysdevice = pdev->subsystem_device;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005802#ifdef SIS_OLD_CONFIG_COMPAT
5803 ivideo->ioctl32registered = 0;
5804#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07005805
5806#ifndef MODULE
5807 if(sisfb_mode_idx == -1) {
5808 sisfb_get_vga_mode_from_kernel();
5809 }
5810#endif
5811
5812 ivideo->chip = chipinfo->chip;
5813 ivideo->sisvga_engine = chipinfo->vgaengine;
5814 ivideo->hwcursor_size = chipinfo->hwcursor_size;
5815 ivideo->CRT2_write_enable = chipinfo->CRT2_write_enable;
5816 ivideo->mni = chipinfo->mni;
5817
5818 ivideo->detectedpdc = 0xff;
5819 ivideo->detectedpdca = 0xff;
5820 ivideo->detectedlcda = 0xff;
5821
5822 ivideo->sisfb_thismonitor.datavalid = FALSE;
5823
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005824 ivideo->current_base = 0;
5825
5826 ivideo->engineok = 0;
5827
5828 ivideo->sisfb_was_boot_device = 0;
5829#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12))
5830 if(pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW) {
5831 if(ivideo->sisvga_enabled)
5832 ivideo->sisfb_was_boot_device = 1;
5833 else {
5834 printk(KERN_DEBUG "sisfb: PCI device is disabled, "
5835 "but marked as boot video device ???\n");
5836 printk(KERN_DEBUG "sisfb: I will not accept this "
5837 "as the primary VGA device\n");
5838 }
5839 }
5840#endif
5841
Linus Torvalds1da177e2005-04-16 15:20:36 -07005842 ivideo->sisfb_parm_mem = sisfb_parm_mem;
5843 ivideo->sisfb_accel = sisfb_accel;
5844 ivideo->sisfb_ypan = sisfb_ypan;
5845 ivideo->sisfb_max = sisfb_max;
5846 ivideo->sisfb_userom = sisfb_userom;
5847 ivideo->sisfb_useoem = sisfb_useoem;
5848 ivideo->sisfb_mode_idx = sisfb_mode_idx;
5849 ivideo->sisfb_parm_rate = sisfb_parm_rate;
5850 ivideo->sisfb_crt1off = sisfb_crt1off;
5851 ivideo->sisfb_forcecrt1 = sisfb_forcecrt1;
5852 ivideo->sisfb_crt2type = sisfb_crt2type;
5853 ivideo->sisfb_crt2flags = sisfb_crt2flags;
5854 /* pdc(a), scalelcd, special timing, lvdshl handled below */
5855 ivideo->sisfb_dstn = sisfb_dstn;
5856 ivideo->sisfb_fstn = sisfb_fstn;
5857 ivideo->sisfb_tvplug = sisfb_tvplug;
5858 ivideo->sisfb_tvstd = sisfb_tvstd;
5859 ivideo->tvxpos = sisfb_tvxposoffset;
5860 ivideo->tvypos = sisfb_tvyposoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005861 ivideo->sisfb_nocrt2rate = sisfb_nocrt2rate;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005862 ivideo->refresh_rate = 0;
5863 if(ivideo->sisfb_parm_rate != -1) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005864 ivideo->refresh_rate = ivideo->sisfb_parm_rate;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005865 }
5866
5867 ivideo->SiS_Pr.UsePanelScaler = sisfb_scalelcd;
5868 ivideo->SiS_Pr.CenterScreen = -1;
5869 ivideo->SiS_Pr.SiS_CustomT = sisfb_specialtiming;
5870 ivideo->SiS_Pr.LVDSHL = sisfb_lvdshl;
5871
5872 ivideo->SiS_Pr.SiS_Backup70xx = 0xff;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005873 ivideo->SiS_Pr.SiS_CHOverScan = -1;
5874 ivideo->SiS_Pr.SiS_ChSW = FALSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005875 ivideo->SiS_Pr.SiS_UseLCDA = FALSE;
5876 ivideo->SiS_Pr.HaveEMI = FALSE;
5877 ivideo->SiS_Pr.HaveEMILCD = FALSE;
5878 ivideo->SiS_Pr.OverruleEMI = FALSE;
5879 ivideo->SiS_Pr.SiS_SensibleSR11 = FALSE;
5880 ivideo->SiS_Pr.SiS_MyCR63 = 0x63;
5881 ivideo->SiS_Pr.PDC = -1;
5882 ivideo->SiS_Pr.PDCA = -1;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005883 ivideo->SiS_Pr.DDCPortMixup = FALSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005884#ifdef CONFIG_FB_SIS_315
5885 if(ivideo->chip >= SIS_330) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005886 ivideo->SiS_Pr.SiS_MyCR63 = 0x53;
5887 if(ivideo->chip >= SIS_661) {
5888 ivideo->SiS_Pr.SiS_SensibleSR11 = TRUE;
5889 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005890 }
5891#endif
5892
5893 memcpy(&ivideo->default_var, &my_default_var, sizeof(my_default_var));
5894
5895 pci_set_drvdata(pdev, ivideo);
5896
5897 /* Patch special cases */
5898 if((ivideo->nbridge = sisfb_get_northbridge(ivideo->chip))) {
5899 switch(ivideo->nbridge->device) {
5900#ifdef CONFIG_FB_SIS_300
5901 case PCI_DEVICE_ID_SI_730:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005902 ivideo->chip = SIS_730;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005903 strcpy(ivideo->myid, "SiS 730");
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005904 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005905#endif
5906#ifdef CONFIG_FB_SIS_315
5907 case PCI_DEVICE_ID_SI_651:
5908 /* ivideo->chip is ok */
5909 strcpy(ivideo->myid, "SiS 651");
5910 break;
5911 case PCI_DEVICE_ID_SI_740:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005912 ivideo->chip = SIS_740;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005913 strcpy(ivideo->myid, "SiS 740");
5914 break;
5915 case PCI_DEVICE_ID_SI_661:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005916 ivideo->chip = SIS_661;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005917 strcpy(ivideo->myid, "SiS 661");
5918 break;
5919 case PCI_DEVICE_ID_SI_741:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005920 ivideo->chip = SIS_741;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005921 strcpy(ivideo->myid, "SiS 741");
5922 break;
5923 case PCI_DEVICE_ID_SI_760:
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005924 ivideo->chip = SIS_760;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005925 strcpy(ivideo->myid, "SiS 760");
5926 break;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005927 case PCI_DEVICE_ID_SI_761:
5928 ivideo->chip = SIS_761;
5929 strcpy(ivideo->myid, "SiS 761");
5930 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005931#endif
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005932 default:
5933 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005934 }
5935 }
5936
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005937 ivideo->SiS_Pr.ChipType = ivideo->chip;
5938
5939 ivideo->SiS_Pr.ivideo = (void *)ivideo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005940
5941#ifdef CONFIG_FB_SIS_315
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005942 if((ivideo->SiS_Pr.ChipType == SIS_315PRO) ||
5943 (ivideo->SiS_Pr.ChipType == SIS_315)) {
5944 ivideo->SiS_Pr.ChipType = SIS_315H;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005945 }
5946#endif
5947
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005948 if(!ivideo->sisvga_enabled) {
5949 if(pci_enable_device(pdev)) {
5950 if(ivideo->nbridge) SIS_PCI_PUT_DEVICE(ivideo->nbridge);
5951 pci_set_drvdata(pdev, NULL);
5952 kfree(sis_fb_info);
5953 return -EIO;
5954 }
5955 }
5956
Linus Torvalds1da177e2005-04-16 15:20:36 -07005957 ivideo->video_base = pci_resource_start(pdev, 0);
5958 ivideo->mmio_base = pci_resource_start(pdev, 1);
5959 ivideo->mmio_size = pci_resource_len(pdev, 1);
5960 ivideo->SiS_Pr.RelIO = pci_resource_start(pdev, 2) + 0x30;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005961 ivideo->SiS_Pr.IOAddress = ivideo->vga_base = ivideo->SiS_Pr.RelIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005962
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005963 SiSRegInit(&ivideo->SiS_Pr, ivideo->SiS_Pr.IOAddress);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005964
5965#ifdef CONFIG_FB_SIS_300
5966 /* Find PCI systems for Chrontel/GPIO communication setup */
5967 if(ivideo->chip == SIS_630) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005968 i = 0;
5969 do {
5970 if(mychswtable[i].subsysVendor == ivideo->subsysvendor &&
5971 mychswtable[i].subsysCard == ivideo->subsysdevice) {
5972 ivideo->SiS_Pr.SiS_ChSW = TRUE;
5973 printk(KERN_DEBUG "sisfb: Identified [%s %s] "
5974 "requiring Chrontel/GPIO setup\n",
5975 mychswtable[i].vendorName,
5976 mychswtable[i].cardName);
5977 ivideo->lpcdev = SIS_PCI_GET_DEVICE(PCI_VENDOR_ID_SI, 0x0008, NULL);
5978 break;
5979 }
5980 i++;
5981 } while(mychswtable[i].subsysVendor != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005982 }
5983#endif
5984
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005985#ifdef CONFIG_FB_SIS_315
5986 if((ivideo->chip == SIS_760) && (ivideo->nbridge)) {
5987 ivideo->lpcdev = SIS_PCI_GET_SLOT(ivideo->nbridge->bus, (2 << 3));
5988 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005989#endif
Thomas Winischhofer544393f2005-09-09 13:04:45 -07005990
5991 outSISIDXREG(SISSR, 0x05, 0x86);
5992
5993 if( (!ivideo->sisvga_enabled)
5994#if !defined(__i386__) && !defined(__x86_64__)
5995 || (sisfb_resetcard)
5996#endif
5997 ) {
5998 for(i = 0x30; i <= 0x3f; i++) {
5999 outSISIDXREG(SISCR, i, 0x00);
6000 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07006001 }
6002
6003 /* Find out about current video mode */
6004 ivideo->modeprechange = 0x03;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006005 inSISIDXREG(SISCR, 0x34, reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006006 if(reg & 0x7f) {
6007 ivideo->modeprechange = reg & 0x7f;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006008 } else if(ivideo->sisvga_enabled) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006009#if defined(__i386__) || defined(__x86_64__)
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006010 unsigned char SIS_IOTYPE2 *tt = ioremap(0x400, 0x100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006011 if(tt) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006012 ivideo->modeprechange = readb(tt + 0x49);
6013 iounmap(tt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006014 }
6015#endif
6016 }
6017
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006018 /* Search and copy ROM image */
6019 ivideo->bios_abase = NULL;
6020 ivideo->SiS_Pr.VirtualRomBase = NULL;
6021 ivideo->SiS_Pr.UseROM = FALSE;
6022 ivideo->haveXGIROM = ivideo->SiS_Pr.SiS_XGIROM = FALSE;
6023 if(ivideo->sisfb_userom) {
6024 ivideo->SiS_Pr.VirtualRomBase = sisfb_find_rom(pdev);
6025 ivideo->bios_abase = ivideo->SiS_Pr.VirtualRomBase;
6026 ivideo->SiS_Pr.UseROM = (ivideo->SiS_Pr.VirtualRomBase) ? TRUE : FALSE;
6027 printk(KERN_INFO "sisfb: Video ROM %sfound\n",
6028 ivideo->SiS_Pr.UseROM ? "" : "not ");
6029 if((ivideo->SiS_Pr.UseROM) && (ivideo->chip >= XGI_20)) {
6030 ivideo->SiS_Pr.UseROM = FALSE;
6031 ivideo->haveXGIROM = ivideo->SiS_Pr.SiS_XGIROM = TRUE;
6032 if( (ivideo->revision_id == 2) &&
6033 (!(ivideo->bios_abase[0x1d1] & 0x01)) ) {
6034 ivideo->SiS_Pr.DDCPortMixup = TRUE;
6035 }
6036 }
6037 } else {
6038 printk(KERN_INFO "sisfb: Video ROM usage disabled\n");
6039 }
6040
6041 /* Find systems for special custom timing */
6042 if(ivideo->SiS_Pr.SiS_CustomT == CUT_NONE) {
6043 sisfb_detect_custom_timing(ivideo);
6044 }
6045
6046 /* POST card in case this has not been done by the BIOS */
6047 if( (!ivideo->sisvga_enabled)
6048#if !defined(__i386__) && !defined(__x86_64__)
6049 || (sisfb_resetcard)
6050#endif
6051 ) {
6052#ifdef CONFIG_FB_SIS_300
6053 if(ivideo->sisvga_engine == SIS_300_VGA) {
6054 if(ivideo->chip == SIS_300) {
6055 sisfb_post_sis300(pdev);
6056 ivideo->sisfb_can_post = 1;
6057 }
6058 }
6059#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07006060
6061#ifdef CONFIG_FB_SIS_315
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006062 if(ivideo->sisvga_engine == SIS_315_VGA) {
6063 int result = 1;
6064 /* if((ivideo->chip == SIS_315H) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07006065 (ivideo->chip == SIS_315) ||
6066 (ivideo->chip == SIS_315PRO) ||
6067 (ivideo->chip == SIS_330)) {
6068 sisfb_post_sis315330(pdev);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006069 } else */ if(ivideo->chip == XGI_20) {
6070 result = sisfb_post_xgi(pdev);
6071 ivideo->sisfb_can_post = 1;
6072 } else if((ivideo->chip == XGI_40) && ivideo->haveXGIROM) {
6073 result = sisfb_post_xgi(pdev);
6074 ivideo->sisfb_can_post = 1;
6075 } else {
6076 printk(KERN_INFO "sisfb: Card is not "
6077 "POSTed and sisfb can't do this either.\n");
6078 }
6079 if(!result) {
6080 printk(KERN_ERR "sisfb: Failed to POST card\n");
6081 ret = -ENODEV;
6082 goto error_3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006083 }
6084 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07006085#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07006086 }
6087
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006088 ivideo->sisfb_card_posted = 1;
6089
6090 /* Find out about RAM size */
6091 if(sisfb_get_dram_size(ivideo)) {
6092 printk(KERN_INFO "sisfb: Fatal error: Unable to determine VRAM size.\n");
6093 ret = -ENODEV;
6094 goto error_3;
6095 }
6096
6097
6098 /* Enable PCI addressing and MMIO */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006099 if((ivideo->sisfb_mode_idx < 0) ||
6100 ((sisbios_mode[ivideo->sisfb_mode_idx].mode_no[ivideo->mni]) != 0xFF)) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006101 /* Enable PCI_LINEAR_ADDRESSING and MMIO_ENABLE */
6102 orSISIDXREG(SISSR, IND_SIS_PCI_ADDRESS_SET, (SIS_PCI_ADDR_ENABLE | SIS_MEM_MAP_IO_ENABLE));
6103 /* Enable 2D accelerator engine */
6104 orSISIDXREG(SISSR, IND_SIS_MODULE_ENABLE, SIS_ENABLE_2D);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006105 }
6106
6107 if(sisfb_pdc != 0xff) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006108 if(ivideo->sisvga_engine == SIS_300_VGA)
6109 sisfb_pdc &= 0x3c;
6110 else
6111 sisfb_pdc &= 0x1f;
6112 ivideo->SiS_Pr.PDC = sisfb_pdc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006113 }
6114#ifdef CONFIG_FB_SIS_315
6115 if(ivideo->sisvga_engine == SIS_315_VGA) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006116 if(sisfb_pdca != 0xff)
6117 ivideo->SiS_Pr.PDCA = sisfb_pdca & 0x1f;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006118 }
6119#endif
6120
6121 if(!request_mem_region(ivideo->video_base, ivideo->video_size, "sisfb FB")) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006122 printk(KERN_ERR "sisfb: Fatal error: Unable to reserve %dMB framebuffer memory\n",
6123 (int)(ivideo->video_size >> 20));
Linus Torvalds1da177e2005-04-16 15:20:36 -07006124 printk(KERN_ERR "sisfb: Is there another framebuffer driver active?\n");
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006125 ret = -ENODEV;
6126 goto error_3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006127 }
6128
6129 if(!request_mem_region(ivideo->mmio_base, ivideo->mmio_size, "sisfb MMIO")) {
6130 printk(KERN_ERR "sisfb: Fatal error: Unable to reserve MMIO region\n");
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006131 ret = -ENODEV;
6132 goto error_2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006133 }
6134
6135 ivideo->video_vbase = ioremap(ivideo->video_base, ivideo->video_size);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006136 ivideo->SiS_Pr.VideoMemoryAddress = ivideo->video_vbase;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006137 if(!ivideo->video_vbase) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006138 printk(KERN_ERR "sisfb: Fatal error: Unable to map framebuffer memory\n");
6139 ret = -ENODEV;
6140 goto error_1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006141 }
6142
6143 ivideo->mmio_vbase = ioremap(ivideo->mmio_base, ivideo->mmio_size);
6144 if(!ivideo->mmio_vbase) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006145 printk(KERN_ERR "sisfb: Fatal error: Unable to map MMIO region\n");
6146 ret = -ENODEV;
6147error_0: iounmap(ivideo->video_vbase);
6148error_1: release_mem_region(ivideo->video_base, ivideo->video_size);
6149error_2: release_mem_region(ivideo->mmio_base, ivideo->mmio_size);
6150error_3: vfree(ivideo->bios_abase);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006151 if(ivideo->lpcdev)
6152 SIS_PCI_PUT_DEVICE(ivideo->lpcdev);
6153 if(ivideo->nbridge)
6154 SIS_PCI_PUT_DEVICE(ivideo->nbridge);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006155 pci_set_drvdata(pdev, NULL);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006156 if(!ivideo->sisvga_enabled)
6157 pci_disable_device(pdev);
6158 kfree(sis_fb_info);
6159 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006160 }
6161
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006162 printk(KERN_INFO "sisfb: Video RAM at 0x%lx, mapped to 0x%lx, size %ldk\n",
6163 ivideo->video_base, (unsigned long)ivideo->video_vbase, ivideo->video_size / 1024);
6164
6165 if(ivideo->video_offset) {
6166 printk(KERN_INFO "sisfb: Viewport offset %ldk\n",
6167 ivideo->video_offset / 1024);
6168 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07006169
6170 printk(KERN_INFO "sisfb: MMIO at 0x%lx, mapped to 0x%lx, size %ldk\n",
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006171 ivideo->mmio_base, (unsigned long)ivideo->mmio_vbase, ivideo->mmio_size / 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006172
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006173
6174 /* Determine the size of the command queue */
6175 if(ivideo->sisvga_engine == SIS_300_VGA) {
6176 ivideo->cmdQueueSize = TURBO_QUEUE_AREA_SIZE;
6177 } else {
6178 if(ivideo->chip == XGI_20) {
6179 ivideo->cmdQueueSize = COMMAND_QUEUE_AREA_SIZE_Z7;
6180 } else {
6181 ivideo->cmdQueueSize = COMMAND_QUEUE_AREA_SIZE;
6182 }
6183 }
6184
6185 /* Engines are no longer initialized here; this is
6186 * now done after the first mode-switch (if the
6187 * submitted var has its acceleration flags set).
6188 */
6189
6190 /* Calculate the base of the (unused) hw cursor */
6191 ivideo->hwcursor_vbase = ivideo->video_vbase
6192 + ivideo->video_size
6193 - ivideo->cmdQueueSize
6194 - ivideo->hwcursor_size;
6195 ivideo->caps |= HW_CURSOR_CAP;
6196
6197 /* Initialize offscreen memory manager */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006198 if((ivideo->havenoheap = sisfb_heap_init(ivideo))) {
6199 printk(KERN_WARNING "sisfb: Failed to initialize offscreen memory heap\n");
6200 }
6201
6202 /* Used for clearing the screen only, therefore respect our mem limit */
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006203 ivideo->SiS_Pr.VideoMemoryAddress += ivideo->video_offset;
6204 ivideo->SiS_Pr.VideoMemorySize = ivideo->sisfb_mem;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006205
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006206 ivideo->mtrr = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006207
6208 ivideo->vbflags = 0;
6209 ivideo->lcddefmodeidx = DEFAULT_LCDMODE;
6210 ivideo->tvdefmodeidx = DEFAULT_TVMODE;
6211 ivideo->defmodeidx = DEFAULT_MODE;
6212
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006213 ivideo->newrom = 0;
6214 if(ivideo->chip < XGI_20) {
6215 if(ivideo->bios_abase) {
6216 ivideo->newrom = SiSDetermineROMLayout661(&ivideo->SiS_Pr);
6217 }
6218 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07006219
6220 if((ivideo->sisfb_mode_idx < 0) ||
6221 ((sisbios_mode[ivideo->sisfb_mode_idx].mode_no[ivideo->mni]) != 0xFF)) {
6222
6223 sisfb_sense_crt1(ivideo);
6224
6225 sisfb_get_VB_type(ivideo);
6226
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006227 if(ivideo->vbflags2 & VB2_VIDEOBRIDGE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006228 sisfb_detect_VB_connect(ivideo);
6229 }
6230
6231 ivideo->currentvbflags = ivideo->vbflags & (VB_VIDEOBRIDGE | TV_STANDARD);
6232
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006233 /* Decide on which CRT2 device to use */
6234 if(ivideo->vbflags2 & VB2_VIDEOBRIDGE) {
6235 if(ivideo->sisfb_crt2type != -1) {
6236 if((ivideo->sisfb_crt2type == CRT2_LCD) &&
6237 (ivideo->vbflags & CRT2_LCD)) {
6238 ivideo->currentvbflags |= CRT2_LCD;
6239 } else if(ivideo->sisfb_crt2type != CRT2_LCD) {
6240 ivideo->currentvbflags |= ivideo->sisfb_crt2type;
6241 }
6242 } else {
6243 /* Chrontel 700x TV detection often unreliable, therefore
6244 * use a different default order on such machines
6245 */
6246 if((ivideo->sisvga_engine == SIS_300_VGA) &&
6247 (ivideo->vbflags2 & VB2_CHRONTEL)) {
6248 if(ivideo->vbflags & CRT2_LCD)
6249 ivideo->currentvbflags |= CRT2_LCD;
6250 else if(ivideo->vbflags & CRT2_TV)
6251 ivideo->currentvbflags |= CRT2_TV;
6252 else if(ivideo->vbflags & CRT2_VGA)
6253 ivideo->currentvbflags |= CRT2_VGA;
6254 } else {
6255 if(ivideo->vbflags & CRT2_TV)
6256 ivideo->currentvbflags |= CRT2_TV;
6257 else if(ivideo->vbflags & CRT2_LCD)
6258 ivideo->currentvbflags |= CRT2_LCD;
6259 else if(ivideo->vbflags & CRT2_VGA)
6260 ivideo->currentvbflags |= CRT2_VGA;
6261 }
6262 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07006263 }
6264
6265 if(ivideo->vbflags & CRT2_LCD) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006266 sisfb_detect_lcd_type(ivideo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006267 }
6268
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006269 sisfb_save_pdc_emi(ivideo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006270
6271 if(!ivideo->sisfb_crt1off) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006272 sisfb_handle_ddc(ivideo, &ivideo->sisfb_thismonitor, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006273 } else {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006274 if((ivideo->vbflags2 & VB2_SISTMDSBRIDGE) &&
6275 (ivideo->vbflags & (CRT2_VGA | CRT2_LCD))) {
6276 sisfb_handle_ddc(ivideo, &ivideo->sisfb_thismonitor, 1);
6277 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07006278 }
6279
6280 if(ivideo->sisfb_mode_idx >= 0) {
6281 int bu = ivideo->sisfb_mode_idx;
6282 ivideo->sisfb_mode_idx = sisfb_validate_mode(ivideo,
6283 ivideo->sisfb_mode_idx, ivideo->currentvbflags);
6284 if(bu != ivideo->sisfb_mode_idx) {
6285 printk(KERN_ERR "Mode %dx%dx%d failed validation\n",
6286 sisbios_mode[bu].xres,
6287 sisbios_mode[bu].yres,
6288 sisbios_mode[bu].bpp);
6289 }
6290 }
6291
6292 if(ivideo->sisfb_mode_idx < 0) {
6293 switch(ivideo->currentvbflags & VB_DISPTYPE_DISP2) {
6294 case CRT2_LCD:
6295 ivideo->sisfb_mode_idx = ivideo->lcddefmodeidx;
6296 break;
6297 case CRT2_TV:
6298 ivideo->sisfb_mode_idx = ivideo->tvdefmodeidx;
6299 break;
6300 default:
6301 ivideo->sisfb_mode_idx = ivideo->defmodeidx;
6302 break;
6303 }
6304 }
6305
6306 ivideo->mode_no = sisbios_mode[ivideo->sisfb_mode_idx].mode_no[ivideo->mni];
6307
6308 if(ivideo->refresh_rate != 0) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006309 sisfb_search_refresh_rate(ivideo, ivideo->refresh_rate,
6310 ivideo->sisfb_mode_idx);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006311 }
6312
6313 if(ivideo->rate_idx == 0) {
6314 ivideo->rate_idx = sisbios_mode[ivideo->sisfb_mode_idx].rate_idx;
6315 ivideo->refresh_rate = 60;
6316 }
6317
6318 if(ivideo->sisfb_thismonitor.datavalid) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006319 if(!sisfb_verify_rate(ivideo, &ivideo->sisfb_thismonitor,
6320 ivideo->sisfb_mode_idx,
6321 ivideo->rate_idx,
6322 ivideo->refresh_rate)) {
6323 printk(KERN_INFO "sisfb: WARNING: Refresh rate "
6324 "exceeds monitor specs!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07006325 }
6326 }
6327
6328 ivideo->video_bpp = sisbios_mode[ivideo->sisfb_mode_idx].bpp;
6329 ivideo->video_width = sisbios_mode[ivideo->sisfb_mode_idx].xres;
6330 ivideo->video_height = sisbios_mode[ivideo->sisfb_mode_idx].yres;
6331
6332 sisfb_set_vparms(ivideo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006333
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006334#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
6335
6336 /* ---------------- For 2.4: Now switch the mode ------------------ */
6337
6338 printk(KERN_INFO "sisfb: Setting mode %dx%dx%d (%dHz)\n",
6339 ivideo->video_width, ivideo->video_height, ivideo->video_bpp,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006340 ivideo->refresh_rate);
6341
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006342 /* Determine whether or not acceleration is to be
6343 * used. Need to know before pre/post_set_mode()
6344 */
6345 ivideo->accel = 0;
6346 ivideo->default_var.accel_flags &= ~FB_ACCELF_TEXT;
6347 if(ivideo->sisfb_accel) {
6348 ivideo->accel = -1;
6349 ivideo->default_var.accel_flags |= FB_ACCELF_TEXT;
6350 }
6351
6352 /* Now switch the mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006353 sisfb_pre_setmode(ivideo);
6354
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006355 if(SiSSetMode(&ivideo->SiS_Pr, ivideo->mode_no) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006356 printk(KERN_ERR "sisfb: Fatal error: Setting mode[0x%x] failed\n",
6357 ivideo->mode_no);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006358 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006359 iounmap(ivideo->mmio_vbase);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006360 goto error_0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006361 }
6362
6363 outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
6364
6365 sisfb_post_setmode(ivideo);
6366
6367 /* Maximize regardless of sisfb_max at startup */
6368 ivideo->default_var.yres_virtual = 32767;
6369
6370 /* Force reset of x virtual in crtc_to_var */
6371 ivideo->default_var.xres_virtual = 0;
6372
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006373 /* Copy mode timing to var */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006374 sisfb_crtc_to_var(ivideo, &ivideo->default_var);
6375
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006376 /* Find out about screen pitch */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006377 sisfb_calc_pitch(ivideo, &ivideo->default_var);
6378 sisfb_set_pitch(ivideo);
6379
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006380 /* Init the accelerator (does nothing currently) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006381 sisfb_initaccel(ivideo);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006382
6383 /* Init some fbinfo entries */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006384 sis_fb_info->node = -1;
6385 sis_fb_info->flags = FBINFO_FLAG_DEFAULT;
6386 sis_fb_info->fbops = &sisfb_ops;
6387 sis_fb_info->disp = &ivideo->sis_disp;
6388 sis_fb_info->blank = &sisfb_blank;
6389 sis_fb_info->switch_con = &sisfb_switch;
6390 sis_fb_info->updatevar = &sisfb_update_var;
6391 sis_fb_info->changevar = NULL;
6392 strcpy(sis_fb_info->fontname, sisfb_fontname);
6393
6394 sisfb_set_disp(-1, &ivideo->default_var, sis_fb_info);
6395
6396#else /* --------- For 2.6: Setup a somewhat sane default var ------------ */
6397
6398 printk(KERN_INFO "sisfb: Default mode is %dx%dx%d (%dHz)\n",
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006399 ivideo->video_width, ivideo->video_height, ivideo->video_bpp,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006400 ivideo->refresh_rate);
6401
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006402 /* Set up the default var according to chosen default display mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006403 ivideo->default_var.xres = ivideo->default_var.xres_virtual = ivideo->video_width;
6404 ivideo->default_var.yres = ivideo->default_var.yres_virtual = ivideo->video_height;
6405 ivideo->default_var.bits_per_pixel = ivideo->video_bpp;
6406
6407 sisfb_bpp_to_var(ivideo, &ivideo->default_var);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006408
Linus Torvalds1da177e2005-04-16 15:20:36 -07006409 ivideo->default_var.pixclock = (u32) (1000000000 /
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006410 sisfb_mode_rate_to_dclock(&ivideo->SiS_Pr, ivideo->mode_no, ivideo->rate_idx));
6411
6412 if(sisfb_mode_rate_to_ddata(&ivideo->SiS_Pr, ivideo->mode_no,
6413 ivideo->rate_idx, &ivideo->default_var)) {
6414 if((ivideo->default_var.vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
6415 ivideo->default_var.pixclock <<= 1;
6416 }
6417 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07006418
6419 if(ivideo->sisfb_ypan) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006420 /* Maximize regardless of sisfb_max at startup */
6421 ivideo->default_var.yres_virtual =
6422 sisfb_calc_maxyres(ivideo, &ivideo->default_var);
6423 if(ivideo->default_var.yres_virtual < ivideo->default_var.yres) {
6424 ivideo->default_var.yres_virtual = ivideo->default_var.yres;
6425 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07006426 }
6427
6428 sisfb_calc_pitch(ivideo, &ivideo->default_var);
6429
6430 ivideo->accel = 0;
6431 if(ivideo->sisfb_accel) {
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006432 ivideo->accel = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006433#ifdef STUPID_ACCELF_TEXT_SHIT
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006434 ivideo->default_var.accel_flags |= FB_ACCELF_TEXT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006435#endif
6436 }
6437 sisfb_initaccel(ivideo);
6438
6439#if defined(FBINFO_HWACCEL_DISABLED) && defined(FBINFO_HWACCEL_XPAN)
6440 sis_fb_info->flags = FBINFO_DEFAULT |
6441 FBINFO_HWACCEL_YPAN |
6442 FBINFO_HWACCEL_XPAN |
6443 FBINFO_HWACCEL_COPYAREA |
6444 FBINFO_HWACCEL_FILLRECT |
6445 ((ivideo->accel) ? 0 : FBINFO_HWACCEL_DISABLED);
6446#else
6447 sis_fb_info->flags = FBINFO_FLAG_DEFAULT;
6448#endif
6449 sis_fb_info->var = ivideo->default_var;
6450 sis_fb_info->fix = ivideo->sisfb_fix;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006451 sis_fb_info->screen_base = ivideo->video_vbase + ivideo->video_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006452 sis_fb_info->fbops = &sisfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006453 sisfb_get_fix(&sis_fb_info->fix, -1, sis_fb_info);
6454 sis_fb_info->pseudo_palette = ivideo->pseudo_palette;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006455
Linus Torvalds1da177e2005-04-16 15:20:36 -07006456 fb_alloc_cmap(&sis_fb_info->cmap, 256 , 0);
6457#endif /* 2.6 */
6458
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006459 printk(KERN_DEBUG "sisfb: Initial vbflags 0x%x\n", (int)ivideo->vbflags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006460
6461#ifdef CONFIG_MTRR
6462 ivideo->mtrr = mtrr_add(ivideo->video_base, ivideo->video_size,
6463 MTRR_TYPE_WRCOMB, 1);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006464 if(ivideo->mtrr < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006465 printk(KERN_DEBUG "sisfb: Failed to add MTRRs\n");
6466 }
6467#endif
6468
Linus Torvalds1da177e2005-04-16 15:20:36 -07006469 if(register_framebuffer(sis_fb_info) < 0) {
6470 printk(KERN_ERR "sisfb: Fatal error: Failed to register framebuffer\n");
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006471 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006472 iounmap(ivideo->mmio_vbase);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006473 goto error_0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006474 }
6475
6476 ivideo->registered = 1;
6477
6478 /* Enlist us */
6479 ivideo->next = card_list;
6480 card_list = ivideo;
6481
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006482#ifdef SIS_OLD_CONFIG_COMPAT
6483 {
6484 int ret;
6485 /* Our ioctls are all "32/64bit compatible" */
6486 ret = register_ioctl32_conversion(FBIO_ALLOC, NULL);
6487 ret |= register_ioctl32_conversion(FBIO_FREE, NULL);
6488 ret |= register_ioctl32_conversion(FBIOGET_VBLANK, NULL);
6489 ret |= register_ioctl32_conversion(SISFB_GET_INFO_SIZE, NULL);
6490 ret |= register_ioctl32_conversion(SISFB_GET_INFO, NULL);
6491 ret |= register_ioctl32_conversion(SISFB_GET_TVPOSOFFSET, NULL);
6492 ret |= register_ioctl32_conversion(SISFB_SET_TVPOSOFFSET, NULL);
6493 ret |= register_ioctl32_conversion(SISFB_SET_LOCK, NULL);
6494 ret |= register_ioctl32_conversion(SISFB_GET_VBRSTATUS, NULL);
6495 ret |= register_ioctl32_conversion(SISFB_GET_AUTOMAXIMIZE, NULL);
6496 ret |= register_ioctl32_conversion(SISFB_SET_AUTOMAXIMIZE, NULL);
6497 ret |= register_ioctl32_conversion(SISFB_COMMAND, NULL);
6498 if(ret)
6499 printk(KERN_ERR
6500 "sisfb: Error registering ioctl32 translations\n");
6501 else
6502 ivideo->ioctl32registered = 1;
6503 }
6504#endif
6505
Linus Torvalds1da177e2005-04-16 15:20:36 -07006506 printk(KERN_INFO "sisfb: 2D acceleration is %s, y-panning %s\n",
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006507 ivideo->sisfb_accel ? "enabled" : "disabled",
6508 ivideo->sisfb_ypan ?
6509 (ivideo->sisfb_max ? "enabled (auto-max)" :
6510 "enabled (no auto-max)") :
6511 "disabled");
Linus Torvalds1da177e2005-04-16 15:20:36 -07006512
6513
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006514 printk(KERN_INFO "fb%d: %s frame buffer device version %d.%d.%d\n",
Michal Piotrowski43704092006-10-03 01:15:00 -07006515 sis_fb_info->node, ivideo->myid, VER_MAJOR, VER_MINOR, VER_LEVEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006516
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006517 printk(KERN_INFO "sisfb: Copyright (C) 2001-2005 Thomas Winischhofer\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07006518
6519 } /* if mode = "none" */
6520
6521 return 0;
6522}
6523
6524/*****************************************************/
6525/* PCI DEVICE HANDLING */
6526/*****************************************************/
6527
6528static void __devexit sisfb_remove(struct pci_dev *pdev)
6529{
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006530 struct sis_video_info *ivideo = pci_get_drvdata(pdev);
6531 struct fb_info *sis_fb_info = ivideo->memyselfandi;
6532 int registered = ivideo->registered;
6533 int modechanged = ivideo->modechanged;
6534
6535#ifdef SIS_OLD_CONFIG_COMPAT
6536 if(ivideo->ioctl32registered) {
6537 int ret;
6538 ret = unregister_ioctl32_conversion(FBIO_ALLOC);
6539 ret |= unregister_ioctl32_conversion(FBIO_FREE);
6540 ret |= unregister_ioctl32_conversion(FBIOGET_VBLANK);
6541 ret |= unregister_ioctl32_conversion(SISFB_GET_INFO_SIZE);
6542 ret |= unregister_ioctl32_conversion(SISFB_GET_INFO);
6543 ret |= unregister_ioctl32_conversion(SISFB_GET_TVPOSOFFSET);
6544 ret |= unregister_ioctl32_conversion(SISFB_SET_TVPOSOFFSET);
6545 ret |= unregister_ioctl32_conversion(SISFB_SET_LOCK);
6546 ret |= unregister_ioctl32_conversion(SISFB_GET_VBRSTATUS);
6547 ret |= unregister_ioctl32_conversion(SISFB_GET_AUTOMAXIMIZE);
6548 ret |= unregister_ioctl32_conversion(SISFB_SET_AUTOMAXIMIZE);
6549 ret |= unregister_ioctl32_conversion(SISFB_COMMAND);
6550 if(ret)
6551 printk(KERN_ERR
6552 "sisfb: Error unregistering ioctl32 translations\n");
6553 }
6554#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07006555
6556 /* Unmap */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006557 iounmap(ivideo->mmio_vbase);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006558 iounmap(ivideo->video_vbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006559
6560 /* Release mem regions */
6561 release_mem_region(ivideo->video_base, ivideo->video_size);
6562 release_mem_region(ivideo->mmio_base, ivideo->mmio_size);
6563
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006564 vfree(ivideo->bios_abase);
6565
6566 if(ivideo->lpcdev)
6567 SIS_PCI_PUT_DEVICE(ivideo->lpcdev);
6568
6569 if(ivideo->nbridge)
6570 SIS_PCI_PUT_DEVICE(ivideo->nbridge);
6571
Linus Torvalds1da177e2005-04-16 15:20:36 -07006572#ifdef CONFIG_MTRR
6573 /* Release MTRR region */
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006574 if(ivideo->mtrr >= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006575 mtrr_del(ivideo->mtrr, ivideo->video_base, ivideo->video_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006576#endif
6577
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006578 pci_set_drvdata(pdev, NULL);
6579
6580 /* If device was disabled when starting, disable
6581 * it when quitting.
6582 */
6583 if(!ivideo->sisvga_enabled)
6584 pci_disable_device(pdev);
6585
Linus Torvalds1da177e2005-04-16 15:20:36 -07006586 /* Unregister the framebuffer */
6587 if(ivideo->registered) {
6588 unregister_framebuffer(sis_fb_info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006589 framebuffer_release(sis_fb_info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006590 }
6591
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006592 /* OK, our ivideo is gone for good from here. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006593
6594 /* TODO: Restore the initial mode
6595 * This sounds easy but is as good as impossible
6596 * on many machines with SiS chip and video bridge
6597 * since text modes are always set up differently
6598 * from machine to machine. Depends on the type
6599 * of integration between chipset and bridge.
6600 */
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006601 if(registered && modechanged)
6602 printk(KERN_INFO
6603 "sisfb: Restoring of text mode not supported yet\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07006604};
6605
6606static struct pci_driver sisfb_driver = {
6607 .name = "sisfb",
6608 .id_table = sisfb_pci_table,
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006609 .probe = sisfb_probe,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006610 .remove = __devexit_p(sisfb_remove)
6611};
6612
6613SISINITSTATIC int __init sisfb_init(void)
6614{
Linus Torvalds1da177e2005-04-16 15:20:36 -07006615#ifndef MODULE
6616 char *options = NULL;
6617
6618 if(fb_get_options("sisfb", &options))
6619 return -ENODEV;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006620
Linus Torvalds1da177e2005-04-16 15:20:36 -07006621 sisfb_setup(options);
6622#endif
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006623 return pci_register_driver(&sisfb_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006624}
6625
Linus Torvalds1da177e2005-04-16 15:20:36 -07006626#ifndef MODULE
6627module_init(sisfb_init);
6628#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07006629
6630/*****************************************************/
6631/* MODULE */
6632/*****************************************************/
6633
6634#ifdef MODULE
6635
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006636static char *mode = NULL;
6637static int vesa = -1;
6638static unsigned int rate = 0;
6639static unsigned int crt1off = 1;
6640static unsigned int mem = 0;
6641static char *forcecrt2type = NULL;
6642static int forcecrt1 = -1;
6643static int pdc = -1;
6644static int pdc1 = -1;
6645static int noaccel = -1;
6646static int noypan = -1;
6647static int nomax = -1;
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006648static int userom = -1;
6649static int useoem = -1;
6650static char *tvstandard = NULL;
6651static int nocrt2rate = 0;
6652static int scalelcd = -1;
6653static char *specialtiming = NULL;
6654static int lvdshl = -1;
6655static int tvxposoffset = 0, tvyposoffset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006656#if !defined(__i386__) && !defined(__x86_64__)
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006657static int resetcard = 0;
6658static int videoram = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006659#endif
6660
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006661static int __init sisfb_init_module(void)
6662{
6663 sisfb_setdefaultparms();
6664
6665 if(rate)
6666 sisfb_parm_rate = rate;
6667
6668 if((scalelcd == 0) || (scalelcd == 1))
6669 sisfb_scalelcd = scalelcd ^ 1;
6670
6671 /* Need to check crt2 type first for fstn/dstn */
6672
6673 if(forcecrt2type)
6674 sisfb_search_crt2type(forcecrt2type);
6675
6676 if(tvstandard)
6677 sisfb_search_tvstd(tvstandard);
6678
6679 if(mode)
6680 sisfb_search_mode(mode, FALSE);
6681 else if(vesa != -1)
6682 sisfb_search_vesamode(vesa, FALSE);
6683
6684 sisfb_crt1off = (crt1off == 0) ? 1 : 0;
6685
6686 sisfb_forcecrt1 = forcecrt1;
6687 if(forcecrt1 == 1)
6688 sisfb_crt1off = 0;
6689 else if(forcecrt1 == 0)
6690 sisfb_crt1off = 1;
6691
6692 if(noaccel == 1)
6693 sisfb_accel = 0;
6694 else if(noaccel == 0)
6695 sisfb_accel = 1;
6696
6697 if(noypan == 1)
6698 sisfb_ypan = 0;
6699 else if(noypan == 0)
6700 sisfb_ypan = 1;
6701
6702 if(nomax == 1)
6703 sisfb_max = 0;
6704 else if(nomax == 0)
6705 sisfb_max = 1;
6706
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006707 if(mem)
6708 sisfb_parm_mem = mem;
6709
6710 if(userom != -1)
6711 sisfb_userom = userom;
6712
6713 if(useoem != -1)
6714 sisfb_useoem = useoem;
6715
6716 if(pdc != -1)
6717 sisfb_pdc = (pdc & 0x7f);
6718
6719 if(pdc1 != -1)
6720 sisfb_pdca = (pdc1 & 0x1f);
6721
6722 sisfb_nocrt2rate = nocrt2rate;
6723
6724 if(specialtiming)
6725 sisfb_search_specialtiming(specialtiming);
6726
6727 if((lvdshl >= 0) && (lvdshl <= 3))
6728 sisfb_lvdshl = lvdshl;
6729
6730 sisfb_tvxposoffset = tvxposoffset;
6731 sisfb_tvyposoffset = tvyposoffset;
6732
6733#if !defined(__i386__) && !defined(__x86_64__)
6734 sisfb_resetcard = (resetcard) ? 1 : 0;
6735 if(videoram)
6736 sisfb_videoram = videoram;
6737#endif
6738
6739 return sisfb_init();
6740}
6741
6742static void __exit sisfb_remove_module(void)
6743{
6744 pci_unregister_driver(&sisfb_driver);
6745 printk(KERN_DEBUG "sisfb: Module unloaded\n");
6746}
6747
6748module_init(sisfb_init_module);
6749module_exit(sisfb_remove_module);
6750
6751MODULE_DESCRIPTION("SiS 300/540/630/730/315/55x/65x/661/74x/330/76x/34x, XGI V3XT/V5/V8/Z7 framebuffer device driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -07006752MODULE_LICENSE("GPL");
6753MODULE_AUTHOR("Thomas Winischhofer <thomas@winischhofer.net>, Others");
6754
Linus Torvalds1da177e2005-04-16 15:20:36 -07006755module_param(mem, int, 0);
6756module_param(noaccel, int, 0);
6757module_param(noypan, int, 0);
6758module_param(nomax, int, 0);
6759module_param(userom, int, 0);
6760module_param(useoem, int, 0);
6761module_param(mode, charp, 0);
6762module_param(vesa, int, 0);
6763module_param(rate, int, 0);
6764module_param(forcecrt1, int, 0);
6765module_param(forcecrt2type, charp, 0);
6766module_param(scalelcd, int, 0);
6767module_param(pdc, int, 0);
6768module_param(pdc1, int, 0);
6769module_param(specialtiming, charp, 0);
6770module_param(lvdshl, int, 0);
6771module_param(tvstandard, charp, 0);
6772module_param(tvxposoffset, int, 0);
6773module_param(tvyposoffset, int, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006774module_param(nocrt2rate, int, 0);
6775#if !defined(__i386__) && !defined(__x86_64__)
6776module_param(resetcard, int, 0);
6777module_param(videoram, int, 0);
6778#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07006779
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006780MODULE_PARM_DESC(mem,
6781 "\nDetermines the beginning of the video memory heap in KB. This heap is used\n"
6782 "for video RAM management for eg. DRM/DRI. On 300 series, the default depends\n"
6783 "on the amount of video RAM available. If 8MB of video RAM or less is available,\n"
6784 "the heap starts at 4096KB, if between 8 and 16MB are available at 8192KB,\n"
6785 "otherwise at 12288KB. On 315/330/340 series, the heap size is 32KB by default.\n"
6786 "The value is to be specified without 'KB'.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07006787
6788MODULE_PARM_DESC(noaccel,
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006789 "\nIf set to anything other than 0, 2D acceleration will be disabled.\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -07006790 "(default: 0)\n");
6791
6792MODULE_PARM_DESC(noypan,
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006793 "\nIf set to anything other than 0, y-panning will be disabled and scrolling\n"
6794 "will be performed by redrawing the screen. (default: 0)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07006795
6796MODULE_PARM_DESC(nomax,
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006797 "\nIf y-panning is enabled, sisfb will by default use the entire available video\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -07006798 "memory for the virtual screen in order to optimize scrolling performance. If\n"
6799 "this is set to anything other than 0, sisfb will not do this and thereby \n"
6800 "enable the user to positively specify a virtual Y size of the screen using\n"
6801 "fbset. (default: 0)\n");
6802
Linus Torvalds1da177e2005-04-16 15:20:36 -07006803MODULE_PARM_DESC(mode,
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006804 "\nSelects the desired default display mode in the format XxYxDepth,\n"
6805 "eg. 1024x768x16. Other formats supported include XxY-Depth and\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -07006806 "XxY-Depth@Rate. If the parameter is only one (decimal or hexadecimal)\n"
6807 "number, it will be interpreted as a VESA mode number. (default: 800x600x8)\n");
6808
6809MODULE_PARM_DESC(vesa,
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006810 "\nSelects the desired default display mode by VESA defined mode number, eg.\n"
6811 "0x117 (default: 0x0103)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07006812
6813MODULE_PARM_DESC(rate,
6814 "\nSelects the desired vertical refresh rate for CRT1 (external VGA) in Hz.\n"
6815 "If the mode is specified in the format XxY-Depth@Rate, this parameter\n"
6816 "will be ignored (default: 60)\n");
6817
6818MODULE_PARM_DESC(forcecrt1,
6819 "\nNormally, the driver autodetects whether or not CRT1 (external VGA) is \n"
6820 "connected. With this option, the detection can be overridden (1=CRT1 ON,\n"
6821 "0=CRT1 OFF) (default: [autodetected])\n");
6822
6823MODULE_PARM_DESC(forcecrt2type,
6824 "\nIf this option is omitted, the driver autodetects CRT2 output devices, such as\n"
6825 "LCD, TV or secondary VGA. With this option, this autodetection can be\n"
6826 "overridden. Possible parameters are LCD, TV, VGA or NONE. NONE disables CRT2.\n"
6827 "On systems with a SiS video bridge, parameters SVIDEO, COMPOSITE or SCART can\n"
6828 "be used instead of TV to override the TV detection. Furthermore, on systems\n"
6829 "with a SiS video bridge, SVIDEO+COMPOSITE, HIVISION, YPBPR480I, YPBPR480P,\n"
6830 "YPBPR720P and YPBPR1080I are understood. However, whether or not these work\n"
6831 "depends on the very hardware in use. (default: [autodetected])\n");
6832
6833MODULE_PARM_DESC(scalelcd,
6834 "\nSetting this to 1 will force the driver to scale the LCD image to the panel's\n"
6835 "native resolution. Setting it to 0 will disable scaling; LVDS panels will\n"
6836 "show black bars around the image, TMDS panels will probably do the scaling\n"
6837 "themselves. Default: 1 on LVDS panels, 0 on TMDS panels\n");
6838
6839MODULE_PARM_DESC(pdc,
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006840 "\nThis is for manually selecting the LCD panel delay compensation. The driver\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -07006841 "should detect this correctly in most cases; however, sometimes this is not\n"
6842 "possible. If you see 'small waves' on the LCD, try setting this to 4, 32 or 24\n"
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006843 "on a 300 series chipset; 6 on other chipsets. If the problem persists, try\n"
6844 "other values (on 300 series: between 4 and 60 in steps of 4; otherwise: any\n"
6845 "value from 0 to 31). (default: autodetected, if LCD is active during start)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07006846
6847#ifdef CONFIG_FB_SIS_315
6848MODULE_PARM_DESC(pdc1,
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006849 "\nThis is same as pdc, but for LCD-via CRT1. Hence, this is for the 315/330/340\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -07006850 "series only. (default: autodetected if LCD is in LCD-via-CRT1 mode during\n"
6851 "startup) - Note: currently, this has no effect because LCD-via-CRT1 is not\n"
6852 "implemented yet.\n");
6853#endif
6854
6855MODULE_PARM_DESC(specialtiming,
6856 "\nPlease refer to documentation for more information on this option.\n");
6857
6858MODULE_PARM_DESC(lvdshl,
6859 "\nPlease refer to documentation for more information on this option.\n");
6860
6861MODULE_PARM_DESC(tvstandard,
6862 "\nThis allows overriding the BIOS default for the TV standard. Valid choices are\n"
6863 "pal, ntsc, palm and paln. (default: [auto; pal or ntsc only])\n");
6864
6865MODULE_PARM_DESC(tvxposoffset,
6866 "\nRelocate TV output horizontally. Possible parameters: -32 through 32.\n"
6867 "Default: 0\n");
6868
6869MODULE_PARM_DESC(tvyposoffset,
6870 "\nRelocate TV output vertically. Possible parameters: -32 through 32.\n"
6871 "Default: 0\n");
6872
Linus Torvalds1da177e2005-04-16 15:20:36 -07006873MODULE_PARM_DESC(nocrt2rate,
6874 "\nSetting this to 1 will force the driver to use the default refresh rate for\n"
6875 "CRT2 if CRT2 type is VGA. (default: 0, use same rate as CRT1)\n");
6876
Linus Torvalds1da177e2005-04-16 15:20:36 -07006877#if !defined(__i386__) && !defined(__x86_64__)
6878#ifdef CONFIG_FB_SIS_300
6879MODULE_PARM_DESC(resetcard,
6880 "\nSet this to 1 in order to reset (POST) the card on non-x86 machines where\n"
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006881 "the BIOS did not POST the card (only supported for SiS 300/305 and XGI cards\n"
6882 "currently). Default: 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07006883
6884MODULE_PARM_DESC(videoram,
6885 "\nSet this to the amount of video RAM (in kilobyte) the card has. Required on\n"
6886 "some non-x86 architectures where the memory auto detection fails. Only\n"
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006887 "relevant if resetcard is set, too. SiS300/305 only. Default: [auto-detect]\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07006888#endif
6889#endif
6890
Linus Torvalds1da177e2005-04-16 15:20:36 -07006891#endif /* /MODULE */
6892
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006893/* _GPL only for new symbols. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006894EXPORT_SYMBOL(sis_malloc);
6895EXPORT_SYMBOL(sis_free);
Thomas Winischhofer544393f2005-09-09 13:04:45 -07006896EXPORT_SYMBOL_GPL(sis_malloc_new);
6897EXPORT_SYMBOL_GPL(sis_free_new);
6898
Linus Torvalds1da177e2005-04-16 15:20:36 -07006899
6900