]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/video/savage/savagefb_driver.c
Merge rsync://bughost.org/repos/ieee80211-delta/
[linux-2.6-omap-h63xx.git] / drivers / video / savage / savagefb_driver.c
1 /*
2  * linux/drivers/video/savagefb.c -- S3 Savage Framebuffer Driver
3  *
4  * Copyright (c) 2001-2002  Denis Oliver Kropp <dok@directfb.org>
5  *                          Sven Neumann <neo@directfb.org>
6  *
7  *
8  * Card specific code is based on XFree86's savage driver.
9  * Framebuffer framework code is based on code of cyber2000fb and tdfxfb.
10  *
11  * This file is subject to the terms and conditions of the GNU General
12  * Public License.  See the file COPYING in the main directory of this
13  * archive for more details.
14  *
15  * 0.4.0 (neo)
16  *  - hardware accelerated clear and move
17  *
18  * 0.3.2 (dok)
19  *  - wait for vertical retrace before writing to cr67
20  *    at the beginning of savagefb_set_par
21  *  - use synchronization registers cr23 and cr26
22  *
23  * 0.3.1 (dok)
24  *  - reset 3D engine
25  *  - don't return alpha bits for 32bit format
26  *
27  * 0.3.0 (dok)
28  *  - added WaitIdle functions for all Savage types
29  *  - do WaitIdle before mode switching
30  *  - code cleanup
31  *
32  * 0.2.0 (dok)
33  *  - first working version
34  *
35  *
36  * TODO
37  * - clock validations in decode_var
38  *
39  * BUGS
40  * - white margin on bootup
41  *
42  */
43
44 #include <linux/config.h>
45 #include <linux/module.h>
46 #include <linux/kernel.h>
47 #include <linux/errno.h>
48 #include <linux/string.h>
49 #include <linux/mm.h>
50 #include <linux/tty.h>
51 #include <linux/slab.h>
52 #include <linux/delay.h>
53 #include <linux/fb.h>
54 #include <linux/pci.h>
55 #include <linux/init.h>
56 #include <linux/console.h>
57
58 #include <asm/io.h>
59 #include <asm/irq.h>
60 #include <asm/pgtable.h>
61 #include <asm/system.h>
62 #include <asm/uaccess.h>
63
64 #ifdef CONFIG_MTRR
65 #include <asm/mtrr.h>
66 #endif
67
68 #include "savagefb.h"
69
70
71 #define SAVAGEFB_VERSION "0.4.0_2.6"
72
73 /* --------------------------------------------------------------------- */
74
75
76 static char *mode_option __initdata = NULL;
77
78 #ifdef MODULE
79
80 MODULE_AUTHOR("(c) 2001-2002  Denis Oliver Kropp <dok@directfb.org>");
81 MODULE_LICENSE("GPL");
82 MODULE_DESCRIPTION("FBDev driver for S3 Savage PCI/AGP Chips");
83
84 #endif
85
86
87 /* --------------------------------------------------------------------- */
88
89 static void vgaHWSeqReset (struct savagefb_par *par, int start)
90 {
91         if (start)
92                 VGAwSEQ (0x00, 0x01, par);      /* Synchronous Reset */
93         else
94                 VGAwSEQ (0x00, 0x03, par);      /* End Reset */
95 }
96
97 static void vgaHWProtect (struct savagefb_par *par, int on)
98 {
99         unsigned char tmp;
100
101         if (on) {
102                 /*
103                  * Turn off screen and disable sequencer.
104                  */
105                 tmp = VGArSEQ (0x01, par);
106
107                 vgaHWSeqReset (par, 1);         /* start synchronous reset */
108                 VGAwSEQ (0x01, tmp | 0x20, par);/* disable the display */
109
110                 VGAenablePalette(par);
111         } else {
112                 /*
113                  * Reenable sequencer, then turn on screen.
114                  */
115
116                 tmp = VGArSEQ (0x01, par);
117
118                 VGAwSEQ (0x01, tmp & ~0x20, par);/* reenable display */
119                 vgaHWSeqReset (par, 0);         /* clear synchronous reset */
120
121                 VGAdisablePalette(par);
122         }
123 }
124
125 static void vgaHWRestore (struct savagefb_par  *par)
126 {
127         int i;
128
129         VGAwMISC (par->MiscOutReg, par);
130
131         for (i = 1; i < 5; i++)
132                 VGAwSEQ (i, par->Sequencer[i], par);
133
134         /* Ensure CRTC registers 0-7 are unlocked by clearing bit 7 or
135            CRTC[17] */
136         VGAwCR (17, par->CRTC[17] & ~0x80, par);
137
138         for (i = 0; i < 25; i++)
139                 VGAwCR (i, par->CRTC[i], par);
140
141         for (i = 0; i < 9; i++)
142                 VGAwGR (i, par->Graphics[i], par);
143
144         VGAenablePalette(par);
145
146         for (i = 0; i < 21; i++)
147                 VGAwATTR (i, par->Attribute[i], par);
148
149         VGAdisablePalette(par);
150 }
151
152 static void vgaHWInit (struct fb_var_screeninfo *var,
153                        struct savagefb_par            *par,
154                        struct xtimings                *timings)
155 {
156         par->MiscOutReg = 0x23;
157
158         if (!(timings->sync & FB_SYNC_HOR_HIGH_ACT))
159                 par->MiscOutReg |= 0x40;
160
161         if (!(timings->sync & FB_SYNC_VERT_HIGH_ACT))
162                 par->MiscOutReg |= 0x80;
163
164         /*
165          * Time Sequencer
166          */
167         par->Sequencer[0x00] = 0x00;
168         par->Sequencer[0x01] = 0x01;
169         par->Sequencer[0x02] = 0x0F;
170         par->Sequencer[0x03] = 0x00;          /* Font select */
171         par->Sequencer[0x04] = 0x0E;          /* Misc */
172
173         /*
174          * CRTC Controller
175          */
176         par->CRTC[0x00] = (timings->HTotal >> 3) - 5;
177         par->CRTC[0x01] = (timings->HDisplay >> 3) - 1;
178         par->CRTC[0x02] = (timings->HSyncStart >> 3) - 1;
179         par->CRTC[0x03] = (((timings->HSyncEnd >> 3)  - 1) & 0x1f) | 0x80;
180         par->CRTC[0x04] = (timings->HSyncStart >> 3);
181         par->CRTC[0x05] = ((((timings->HSyncEnd >> 3) - 1) & 0x20) << 2) |
182                 (((timings->HSyncEnd >> 3)) & 0x1f);
183         par->CRTC[0x06] = (timings->VTotal - 2) & 0xFF;
184         par->CRTC[0x07] = (((timings->VTotal - 2) & 0x100) >> 8) |
185                 (((timings->VDisplay - 1) & 0x100) >> 7) |
186                 ((timings->VSyncStart & 0x100) >> 6) |
187                 (((timings->VSyncStart - 1) & 0x100) >> 5) |
188                 0x10 |
189                 (((timings->VTotal - 2) & 0x200) >> 4) |
190                 (((timings->VDisplay - 1) & 0x200) >> 3) |
191                 ((timings->VSyncStart & 0x200) >> 2);
192         par->CRTC[0x08] = 0x00;
193         par->CRTC[0x09] = (((timings->VSyncStart - 1) & 0x200) >> 4) | 0x40;
194
195         if (timings->dblscan)
196                 par->CRTC[0x09] |= 0x80;
197
198         par->CRTC[0x0a] = 0x00;
199         par->CRTC[0x0b] = 0x00;
200         par->CRTC[0x0c] = 0x00;
201         par->CRTC[0x0d] = 0x00;
202         par->CRTC[0x0e] = 0x00;
203         par->CRTC[0x0f] = 0x00;
204         par->CRTC[0x10] = timings->VSyncStart & 0xff;
205         par->CRTC[0x11] = (timings->VSyncEnd & 0x0f) | 0x20;
206         par->CRTC[0x12] = (timings->VDisplay - 1) & 0xff;
207         par->CRTC[0x13] = var->xres_virtual >> 4;
208         par->CRTC[0x14] = 0x00;
209         par->CRTC[0x15] = (timings->VSyncStart - 1) & 0xff;
210         par->CRTC[0x16] = (timings->VSyncEnd - 1) & 0xff;
211         par->CRTC[0x17] = 0xc3;
212         par->CRTC[0x18] = 0xff;
213
214         /*
215          * are these unnecessary?
216          * vgaHWHBlankKGA(mode, regp, 0, KGA_FIX_OVERSCAN|KGA_ENABLE_ON_ZERO);
217          * vgaHWVBlankKGA(mode, regp, 0, KGA_FIX_OVERSCAN|KGA_ENABLE_ON_ZERO);
218          */
219
220         /*
221          * Graphics Display Controller
222          */
223         par->Graphics[0x00] = 0x00;
224         par->Graphics[0x01] = 0x00;
225         par->Graphics[0x02] = 0x00;
226         par->Graphics[0x03] = 0x00;
227         par->Graphics[0x04] = 0x00;
228         par->Graphics[0x05] = 0x40;
229         par->Graphics[0x06] = 0x05;   /* only map 64k VGA memory !!!! */
230         par->Graphics[0x07] = 0x0F;
231         par->Graphics[0x08] = 0xFF;
232
233
234         par->Attribute[0x00]  = 0x00; /* standard colormap translation */
235         par->Attribute[0x01]  = 0x01;
236         par->Attribute[0x02]  = 0x02;
237         par->Attribute[0x03]  = 0x03;
238         par->Attribute[0x04]  = 0x04;
239         par->Attribute[0x05]  = 0x05;
240         par->Attribute[0x06]  = 0x06;
241         par->Attribute[0x07]  = 0x07;
242         par->Attribute[0x08]  = 0x08;
243         par->Attribute[0x09]  = 0x09;
244         par->Attribute[0x0a] = 0x0A;
245         par->Attribute[0x0b] = 0x0B;
246         par->Attribute[0x0c] = 0x0C;
247         par->Attribute[0x0d] = 0x0D;
248         par->Attribute[0x0e] = 0x0E;
249         par->Attribute[0x0f] = 0x0F;
250         par->Attribute[0x10] = 0x41;
251         par->Attribute[0x11] = 0xFF;
252         par->Attribute[0x12] = 0x0F;
253         par->Attribute[0x13] = 0x00;
254         par->Attribute[0x14] = 0x00;
255 }
256
257 /* -------------------- Hardware specific routines ------------------------- */
258
259 /*
260  * Hardware Acceleration for SavageFB
261  */
262
263 /* Wait for fifo space */
264 static void
265 savage3D_waitfifo(struct savagefb_par *par, int space)
266 {
267         int slots = MAXFIFO - space;
268
269         while ((savage_in32(0x48C00, par) & 0x0000ffff) > slots);
270 }
271
272 static void
273 savage4_waitfifo(struct savagefb_par *par, int space)
274 {
275         int slots = MAXFIFO - space;
276
277         while ((savage_in32(0x48C60, par) & 0x001fffff) > slots);
278 }
279
280 static void
281 savage2000_waitfifo(struct savagefb_par *par, int space)
282 {
283         int slots = MAXFIFO - space;
284
285         while ((savage_in32(0x48C60, par) & 0x0000ffff) > slots);
286 }
287
288 /* Wait for idle accelerator */
289 static void
290 savage3D_waitidle(struct savagefb_par *par)
291 {
292         while ((savage_in32(0x48C00, par) & 0x0008ffff) != 0x80000);
293 }
294
295 static void
296 savage4_waitidle(struct savagefb_par *par)
297 {
298         while ((savage_in32(0x48C60, par) & 0x00a00000) != 0x00a00000);
299 }
300
301 static void
302 savage2000_waitidle(struct savagefb_par *par)
303 {
304         while ((savage_in32(0x48C60, par) & 0x009fffff));
305 }
306
307
308 static void
309 SavageSetup2DEngine (struct savagefb_par  *par)
310 {
311         unsigned long GlobalBitmapDescriptor;
312
313         GlobalBitmapDescriptor = 1 | 8 | BCI_BD_BW_DISABLE;
314         BCI_BD_SET_BPP (GlobalBitmapDescriptor, par->depth);
315         BCI_BD_SET_STRIDE (GlobalBitmapDescriptor, par->vwidth);
316
317         switch(par->chip) {
318         case S3_SAVAGE3D:
319         case S3_SAVAGE_MX:
320                 /* Disable BCI */
321                 savage_out32(0x48C18, savage_in32(0x48C18, par) & 0x3FF0, par);
322                 /* Setup BCI command overflow buffer */
323                 savage_out32(0x48C14,
324                              (par->cob_offset >> 11) | (par->cob_index << 29),
325                              par);
326                 /* Program shadow status update. */
327                 savage_out32(0x48C10, 0x78207220, par);
328                 savage_out32(0x48C0C, 0, par);
329                 /* Enable BCI and command overflow buffer */
330                 savage_out32(0x48C18, savage_in32(0x48C18, par) | 0x0C, par);
331                 break;
332         case S3_SAVAGE4:
333         case S3_PROSAVAGE:
334         case S3_SUPERSAVAGE:
335                 /* Disable BCI */
336                 savage_out32(0x48C18, savage_in32(0x48C18, par) & 0x3FF0, par);
337                 /* Program shadow status update */
338                 savage_out32(0x48C10, 0x00700040, par);
339                 savage_out32(0x48C0C, 0, par);
340                 /* Enable BCI without the COB */
341                 savage_out32(0x48C18, savage_in32(0x48C18, par) | 0x08, par);
342                 break;
343         case S3_SAVAGE2000:
344                 /* Disable BCI */
345                 savage_out32(0x48C18, 0, par);
346                 /* Setup BCI command overflow buffer */
347                 savage_out32(0x48C18,
348                              (par->cob_offset >> 7) | (par->cob_index),
349                              par);
350                 /* Disable shadow status update */
351                 savage_out32(0x48A30, 0, par);
352                 /* Enable BCI and command overflow buffer */
353                 savage_out32(0x48C18, savage_in32(0x48C18, par) | 0x00280000,
354                              par);
355                 break;
356             default:
357                 break;
358         }
359         /* Turn on 16-bit register access. */
360         vga_out8(0x3d4, 0x31, par);
361         vga_out8(0x3d5, 0x0c, par);
362
363         /* Set stride to use GBD. */
364         vga_out8 (0x3d4, 0x50, par);
365         vga_out8 (0x3d5, vga_in8(0x3d5, par) | 0xC1, par);
366
367         /* Enable 2D engine. */
368         vga_out8 (0x3d4, 0x40, par);
369         vga_out8 (0x3d5, 0x01, par);
370
371         savage_out32 (MONO_PAT_0, ~0, par);
372         savage_out32 (MONO_PAT_1, ~0, par);
373
374         /* Setup plane masks */
375         savage_out32 (0x8128, ~0, par); /* enable all write planes */
376         savage_out32 (0x812C, ~0, par); /* enable all read planes */
377         savage_out16 (0x8134, 0x27, par);
378         savage_out16 (0x8136, 0x07, par);
379
380         /* Now set the GBD */
381         par->bci_ptr = 0;
382         par->SavageWaitFifo (par, 4);
383
384         BCI_SEND( BCI_CMD_SETREG | (1 << 16) | BCI_GBD1 );
385         BCI_SEND( 0 );
386         BCI_SEND( BCI_CMD_SETREG | (1 << 16) | BCI_GBD2 );
387         BCI_SEND( GlobalBitmapDescriptor );
388 }
389
390
391 static void SavageCalcClock(long freq, int min_m, int min_n1, int max_n1,
392                             int min_n2, int max_n2, long freq_min,
393                             long freq_max, unsigned int *mdiv,
394                             unsigned int *ndiv, unsigned int *r)
395 {
396         long diff, best_diff;
397         unsigned int m;
398         unsigned char n1, n2, best_n1=16+2, best_n2=2, best_m=125+2;
399
400         if (freq < freq_min / (1 << max_n2)) {
401                 printk (KERN_ERR "invalid frequency %ld Khz\n", freq);
402                 freq = freq_min / (1 << max_n2);
403         }
404         if (freq > freq_max / (1 << min_n2)) {
405                 printk (KERN_ERR "invalid frequency %ld Khz\n", freq);
406                 freq = freq_max / (1 << min_n2);
407         }
408
409         /* work out suitable timings */
410         best_diff = freq;
411
412         for (n2=min_n2; n2<=max_n2; n2++) {
413                 for (n1=min_n1+2; n1<=max_n1+2; n1++) {
414                         m = (freq * n1 * (1 << n2) + HALF_BASE_FREQ) /
415                                 BASE_FREQ;
416                         if (m < min_m+2 || m > 127+2)
417                                 continue;
418                         if ((m * BASE_FREQ >= freq_min * n1) &&
419                             (m * BASE_FREQ <= freq_max * n1)) {
420                                 diff = freq * (1 << n2) * n1 - BASE_FREQ * m;
421                                 if (diff < 0)
422                                         diff = -diff;
423                                 if (diff < best_diff) {
424                                         best_diff = diff;
425                                         best_m = m;
426                                         best_n1 = n1;
427                                         best_n2 = n2;
428                                 }
429                         }
430                 }
431         }
432
433         *ndiv = best_n1 - 2;
434         *r = best_n2;
435         *mdiv = best_m - 2;
436 }
437
438 static int common_calc_clock(long freq, int min_m, int min_n1, int max_n1,
439                              int min_n2, int max_n2, long freq_min,
440                              long freq_max, unsigned char *mdiv,
441                              unsigned char *ndiv)
442 {
443         long diff, best_diff;
444         unsigned int m;
445         unsigned char n1, n2;
446         unsigned char best_n1 = 16+2, best_n2 = 2, best_m = 125+2;
447
448         best_diff = freq;
449
450         for (n2 = min_n2; n2 <= max_n2; n2++) {
451                 for (n1 = min_n1+2; n1 <= max_n1+2; n1++) {
452                         m = (freq * n1 * (1 << n2) + HALF_BASE_FREQ) /
453                                 BASE_FREQ;
454                         if (m < min_m + 2 || m > 127+2)
455                                 continue;
456                         if((m * BASE_FREQ >= freq_min * n1) &&
457                            (m * BASE_FREQ <= freq_max * n1)) {
458                                 diff = freq * (1 << n2) * n1 - BASE_FREQ * m;
459                                 if(diff < 0)
460                                         diff = -diff;
461                                 if(diff < best_diff) {
462                                         best_diff = diff;
463                                         best_m = m;
464                                         best_n1 = n1;
465                                         best_n2 = n2;
466                                 }
467                         }
468                 }
469         }
470
471         if(max_n1 == 63)
472                 *ndiv = (best_n1 - 2) | (best_n2 << 6);
473         else
474                 *ndiv = (best_n1 - 2) | (best_n2 << 5);
475
476         *mdiv = best_m - 2;
477
478         return 0;
479 }
480
481 #ifdef SAVAGEFB_DEBUG
482 /* This function is used to debug, it prints out the contents of s3 regs */
483
484 static void SavagePrintRegs(void)
485 {
486         unsigned char i;
487         int vgaCRIndex = 0x3d4;
488         int vgaCRReg = 0x3d5;
489
490         printk(KERN_DEBUG "SR    x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE "
491                "xF" );
492
493         for( i = 0; i < 0x70; i++ ) {
494                 if( !(i % 16) )
495                         printk(KERN_DEBUG "\nSR%xx ", i >> 4 );
496                 vga_out8( 0x3c4, i, par);
497                 printk(KERN_DEBUG " %02x", vga_in8(0x3c5, par) );
498         }
499
500         printk(KERN_DEBUG "\n\nCR    x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC "
501                "xD xE xF" );
502
503         for( i = 0; i < 0xB7; i++ ) {
504                 if( !(i % 16) )
505                         printk(KERN_DEBUG "\nCR%xx ", i >> 4 );
506                 vga_out8( vgaCRIndex, i, par);
507                 printk(KERN_DEBUG " %02x", vga_in8(vgaCRReg, par) );
508         }
509
510         printk(KERN_DEBUG "\n\n");
511 }
512 #endif
513
514 /* --------------------------------------------------------------------- */
515
516 static void savage_get_default_par(struct savagefb_par *par)
517 {
518         unsigned char cr3a, cr53, cr66;
519
520         vga_out16 (0x3d4, 0x4838, par);
521         vga_out16 (0x3d4, 0xa039, par);
522         vga_out16 (0x3c4, 0x0608, par);
523
524         vga_out8 (0x3d4, 0x66, par);
525         cr66 = vga_in8 (0x3d5, par);
526         vga_out8 (0x3d5, cr66 | 0x80, par);
527         vga_out8 (0x3d4, 0x3a, par);
528         cr3a = vga_in8 (0x3d5, par);
529         vga_out8 (0x3d5, cr3a | 0x80, par);
530         vga_out8 (0x3d4, 0x53, par);
531         cr53 = vga_in8 (0x3d5, par);
532         vga_out8 (0x3d5, cr53 & 0x7f, par);
533
534         vga_out8 (0x3d4, 0x66, par);
535         vga_out8 (0x3d5, cr66, par);
536         vga_out8 (0x3d4, 0x3a, par);
537         vga_out8 (0x3d5, cr3a, par);
538
539         vga_out8 (0x3d4, 0x66, par);
540         vga_out8 (0x3d5, cr66, par);
541         vga_out8 (0x3d4, 0x3a, par);
542         vga_out8 (0x3d5, cr3a, par);
543
544         /* unlock extended seq regs */
545         vga_out8 (0x3c4, 0x08, par);
546         par->SR08 = vga_in8 (0x3c5, par);
547         vga_out8 (0x3c5, 0x06, par);
548
549         /* now save all the extended regs we need */
550         vga_out8 (0x3d4, 0x31, par);
551         par->CR31 = vga_in8 (0x3d5, par);
552         vga_out8 (0x3d4, 0x32, par);
553         par->CR32 = vga_in8 (0x3d5, par);
554         vga_out8 (0x3d4, 0x34, par);
555         par->CR34 = vga_in8 (0x3d5, par);
556         vga_out8 (0x3d4, 0x36, par);
557         par->CR36 = vga_in8 (0x3d5, par);
558         vga_out8 (0x3d4, 0x3a, par);
559         par->CR3A = vga_in8 (0x3d5, par);
560         vga_out8 (0x3d4, 0x40, par);
561         par->CR40 = vga_in8 (0x3d5, par);
562         vga_out8 (0x3d4, 0x42, par);
563         par->CR42 = vga_in8 (0x3d5, par);
564         vga_out8 (0x3d4, 0x45, par);
565         par->CR45 = vga_in8 (0x3d5, par);
566         vga_out8 (0x3d4, 0x50, par);
567         par->CR50 = vga_in8 (0x3d5, par);
568         vga_out8 (0x3d4, 0x51, par);
569         par->CR51 = vga_in8 (0x3d5, par);
570         vga_out8 (0x3d4, 0x53, par);
571         par->CR53 = vga_in8 (0x3d5, par);
572         vga_out8 (0x3d4, 0x58, par);
573         par->CR58 = vga_in8 (0x3d5, par);
574         vga_out8 (0x3d4, 0x60, par);
575         par->CR60 = vga_in8 (0x3d5, par);
576         vga_out8 (0x3d4, 0x66, par);
577         par->CR66 = vga_in8 (0x3d5, par);
578         vga_out8 (0x3d4, 0x67, par);
579         par->CR67 = vga_in8 (0x3d5, par);
580         vga_out8 (0x3d4, 0x68, par);
581         par->CR68 = vga_in8 (0x3d5, par);
582         vga_out8 (0x3d4, 0x69, par);
583         par->CR69 = vga_in8 (0x3d5, par);
584         vga_out8 (0x3d4, 0x6f, par);
585         par->CR6F = vga_in8 (0x3d5, par);
586
587         vga_out8 (0x3d4, 0x33, par);
588         par->CR33 = vga_in8 (0x3d5, par);
589         vga_out8 (0x3d4, 0x86, par);
590         par->CR86 = vga_in8 (0x3d5, par);
591         vga_out8 (0x3d4, 0x88, par);
592         par->CR88 = vga_in8 (0x3d5, par);
593         vga_out8 (0x3d4, 0x90, par);
594         par->CR90 = vga_in8 (0x3d5, par);
595         vga_out8 (0x3d4, 0x91, par);
596         par->CR91 = vga_in8 (0x3d5, par);
597         vga_out8 (0x3d4, 0xb0, par);
598         par->CRB0 = vga_in8 (0x3d5, par) | 0x80;
599
600         /* extended mode timing regs */
601         vga_out8 (0x3d4, 0x3b, par);
602         par->CR3B = vga_in8 (0x3d5, par);
603         vga_out8 (0x3d4, 0x3c, par);
604         par->CR3C = vga_in8 (0x3d5, par);
605         vga_out8 (0x3d4, 0x43, par);
606         par->CR43 = vga_in8 (0x3d5, par);
607         vga_out8 (0x3d4, 0x5d, par);
608         par->CR5D = vga_in8 (0x3d5, par);
609         vga_out8 (0x3d4, 0x5e, par);
610         par->CR5E = vga_in8 (0x3d5, par);
611         vga_out8 (0x3d4, 0x65, par);
612         par->CR65 = vga_in8 (0x3d5, par);
613
614         /* save seq extended regs for DCLK PLL programming */
615         vga_out8 (0x3c4, 0x0e, par);
616         par->SR0E = vga_in8 (0x3c5, par);
617         vga_out8 (0x3c4, 0x0f, par);
618         par->SR0F = vga_in8 (0x3c5, par);
619         vga_out8 (0x3c4, 0x10, par);
620         par->SR10 = vga_in8 (0x3c5, par);
621         vga_out8 (0x3c4, 0x11, par);
622         par->SR11 = vga_in8 (0x3c5, par);
623         vga_out8 (0x3c4, 0x12, par);
624         par->SR12 = vga_in8 (0x3c5, par);
625         vga_out8 (0x3c4, 0x13, par);
626         par->SR13 = vga_in8 (0x3c5, par);
627         vga_out8 (0x3c4, 0x29, par);
628         par->SR29 = vga_in8 (0x3c5, par);
629
630         vga_out8 (0x3c4, 0x15, par);
631         par->SR15 = vga_in8 (0x3c5, par);
632         vga_out8 (0x3c4, 0x30, par);
633         par->SR30 = vga_in8 (0x3c5, par);
634         vga_out8 (0x3c4, 0x18, par);
635         par->SR18 = vga_in8 (0x3c5, par);
636
637         /* Save flat panel expansion regsters. */
638         if (par->chip == S3_SAVAGE_MX) {
639                 int i;
640
641                 for (i = 0; i < 8; i++) {
642                         vga_out8 (0x3c4, 0x54+i, par);
643                         par->SR54[i] = vga_in8 (0x3c5, par);
644                 }
645         }
646
647         vga_out8 (0x3d4, 0x66, par);
648         cr66 = vga_in8 (0x3d5, par);
649         vga_out8 (0x3d5, cr66 | 0x80, par);
650         vga_out8 (0x3d4, 0x3a, par);
651         cr3a = vga_in8 (0x3d5, par);
652         vga_out8 (0x3d5, cr3a | 0x80, par);
653
654         /* now save MIU regs */
655         if (par->chip != S3_SAVAGE_MX) {
656                 par->MMPR0 = savage_in32(FIFO_CONTROL_REG, par);
657                 par->MMPR1 = savage_in32(MIU_CONTROL_REG, par);
658                 par->MMPR2 = savage_in32(STREAMS_TIMEOUT_REG, par);
659                 par->MMPR3 = savage_in32(MISC_TIMEOUT_REG, par);
660         }
661
662         vga_out8 (0x3d4, 0x3a, par);
663         vga_out8 (0x3d5, cr3a, par);
664         vga_out8 (0x3d4, 0x66, par);
665         vga_out8 (0x3d5, cr66, par);
666 }
667
668 static void savage_update_var(struct fb_var_screeninfo *var, struct fb_videomode *modedb)
669 {
670         var->xres = var->xres_virtual = modedb->xres;
671         var->yres = modedb->yres;
672         if (var->yres_virtual < var->yres)
673             var->yres_virtual = var->yres;
674         var->xoffset = var->yoffset = 0;
675         var->pixclock = modedb->pixclock;
676         var->left_margin = modedb->left_margin;
677         var->right_margin = modedb->right_margin;
678         var->upper_margin = modedb->upper_margin;
679         var->lower_margin = modedb->lower_margin;
680         var->hsync_len = modedb->hsync_len;
681         var->vsync_len = modedb->vsync_len;
682         var->sync = modedb->sync;
683         var->vmode = modedb->vmode;
684 }
685
686 static int savagefb_check_var (struct fb_var_screeninfo   *var,
687                                struct fb_info *info)
688 {
689         struct savagefb_par *par = (struct savagefb_par *)info->par;
690         int memlen, vramlen, mode_valid = 0;
691
692         DBG("savagefb_check_var");
693
694         var->transp.offset = 0;
695         var->transp.length = 0;
696         switch (var->bits_per_pixel) {
697         case 8:
698                 var->red.offset = var->green.offset =
699                         var->blue.offset = 0;
700                 var->red.length = var->green.length =
701                         var->blue.length = var->bits_per_pixel;
702                 break;
703         case 16:
704                 var->red.offset = 11;
705                 var->red.length = 5;
706                 var->green.offset = 5;
707                 var->green.length = 6;
708                 var->blue.offset = 0;
709                 var->blue.length = 5;
710                 break;
711         case 32:
712                 var->transp.offset = 24;
713                 var->transp.length = 8;
714                 var->red.offset = 16;
715                 var->red.length = 8;
716                 var->green.offset = 8;
717                 var->green.length = 8;
718                 var->blue.offset = 0;
719                 var->blue.length = 8;
720                 break;
721
722         default:
723                 return -EINVAL;
724         }
725
726         if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
727             !info->monspecs.dclkmax || !fb_validate_mode(var, info))
728                 mode_valid = 1;
729
730         /* calculate modeline if supported by monitor */
731         if (!mode_valid && info->monspecs.gtf) {
732                 if (!fb_get_mode(FB_MAXTIMINGS, 0, var, info))
733                         mode_valid = 1;
734         }
735
736         if (!mode_valid) {
737                 struct fb_videomode *mode;
738
739                 mode = fb_find_best_mode(var, &info->modelist);
740                 if (mode) {
741                         savage_update_var(var, mode);
742                         mode_valid = 1;
743                 }
744         }
745
746         if (!mode_valid && info->monspecs.modedb_len)
747                 return -EINVAL;
748
749         /* Is the mode larger than the LCD panel? */
750         if (par->SavagePanelWidth &&
751             (var->xres > par->SavagePanelWidth ||
752              var->yres > par->SavagePanelHeight)) {
753                 printk (KERN_INFO "Mode (%dx%d) larger than the LCD panel "
754                         "(%dx%d)\n", var->xres,  var->yres,
755                         par->SavagePanelWidth,
756                         par->SavagePanelHeight);
757                 return -1;
758         }
759
760         if (var->yres_virtual < var->yres)
761                 var->yres_virtual = var->yres;
762         if (var->xres_virtual < var->xres)
763                 var->xres_virtual = var->xres;
764
765         vramlen = info->fix.smem_len;
766
767         memlen = var->xres_virtual * var->bits_per_pixel *
768                 var->yres_virtual / 8;
769         if (memlen > vramlen) {
770                 var->yres_virtual = vramlen * 8 /
771                         (var->xres_virtual * var->bits_per_pixel);
772                 memlen = var->xres_virtual * var->bits_per_pixel *
773                         var->yres_virtual / 8;
774         }
775
776         /* we must round yres/xres down, we already rounded y/xres_virtual up
777            if it was possible. We should return -EINVAL, but I disagree */
778         if (var->yres_virtual < var->yres)
779                 var->yres = var->yres_virtual;
780         if (var->xres_virtual < var->xres)
781                 var->xres = var->xres_virtual;
782         if (var->xoffset + var->xres > var->xres_virtual)
783                 var->xoffset = var->xres_virtual - var->xres;
784         if (var->yoffset + var->yres > var->yres_virtual)
785                 var->yoffset = var->yres_virtual - var->yres;
786
787         return 0;
788 }
789
790
791 static int savagefb_decode_var (struct fb_var_screeninfo   *var,
792                                 struct savagefb_par        *par)
793 {
794         struct xtimings timings;
795         int width, dclk, i, j; /*, refresh; */
796         unsigned int m, n, r;
797         unsigned char tmp = 0;
798         unsigned int pixclock = var->pixclock;
799
800         DBG("savagefb_decode_var");
801
802         memset (&timings, 0, sizeof(timings));
803
804         if (!pixclock) pixclock = 10000;        /* 10ns = 100MHz */
805         timings.Clock = 1000000000 / pixclock;
806         if (timings.Clock < 1) timings.Clock = 1;
807         timings.dblscan = var->vmode & FB_VMODE_DOUBLE;
808         timings.interlaced = var->vmode & FB_VMODE_INTERLACED;
809         timings.HDisplay = var->xres;
810         timings.HSyncStart = timings.HDisplay + var->right_margin;
811         timings.HSyncEnd = timings.HSyncStart + var->hsync_len;
812         timings.HTotal = timings.HSyncEnd + var->left_margin;
813         timings.VDisplay = var->yres;
814         timings.VSyncStart = timings.VDisplay + var->lower_margin;
815         timings.VSyncEnd = timings.VSyncStart + var->vsync_len;
816         timings.VTotal = timings.VSyncEnd + var->upper_margin;
817         timings.sync = var->sync;
818
819
820         par->depth  = var->bits_per_pixel;
821         par->vwidth = var->xres_virtual;
822
823         if (var->bits_per_pixel == 16  &&  par->chip == S3_SAVAGE3D) {
824                 timings.HDisplay *= 2;
825                 timings.HSyncStart *= 2;
826                 timings.HSyncEnd *= 2;
827                 timings.HTotal *= 2;
828         }
829
830         /*
831          * This will allocate the datastructure and initialize all of the
832          * generic VGA registers.
833          */
834         vgaHWInit (var, par, &timings);
835
836         /* We need to set CR67 whether or not we use the BIOS. */
837
838         dclk = timings.Clock;
839         par->CR67 = 0x00;
840
841         switch( var->bits_per_pixel ) {
842         case 8:
843                 if( (par->chip == S3_SAVAGE2000) && (dclk >= 230000) )
844                         par->CR67 = 0x10;       /* 8bpp, 2 pixels/clock */
845                 else
846                         par->CR67 = 0x00;       /* 8bpp, 1 pixel/clock */
847                 break;
848         case 15:
849                 if ( S3_SAVAGE_MOBILE_SERIES(par->chip) ||
850                      ((par->chip == S3_SAVAGE2000) && (dclk >= 230000)) )
851                         par->CR67 = 0x30;       /* 15bpp, 2 pixel/clock */
852                 else
853                         par->CR67 = 0x20;       /* 15bpp, 1 pixels/clock */
854                 break;
855         case 16:
856                 if( S3_SAVAGE_MOBILE_SERIES(par->chip) ||
857                     ((par->chip == S3_SAVAGE2000) && (dclk >= 230000)) )
858                         par->CR67 = 0x50;       /* 16bpp, 2 pixel/clock */
859                 else
860                         par->CR67 = 0x40;       /* 16bpp, 1 pixels/clock */
861                 break;
862         case 24:
863                 par->CR67 = 0x70;
864                 break;
865         case 32:
866                 par->CR67 = 0xd0;
867                 break;
868         }
869
870         /*
871          * Either BIOS use is disabled, or we failed to find a suitable
872          * match.  Fall back to traditional register-crunching.
873          */
874
875         vga_out8 (0x3d4, 0x3a, par);
876         tmp = vga_in8 (0x3d5, par);
877         if (1 /*FIXME:psav->pci_burst*/)
878                 par->CR3A = (tmp & 0x7f) | 0x15;
879         else
880                 par->CR3A = tmp | 0x95;
881
882         par->CR53 = 0x00;
883         par->CR31 = 0x8c;
884         par->CR66 = 0x89;
885
886         vga_out8 (0x3d4, 0x58, par);
887         par->CR58 = vga_in8 (0x3d5, par) & 0x80;
888         par->CR58 |= 0x13;
889
890         par->SR15 = 0x03 | 0x80;
891         par->SR18 = 0x00;
892         par->CR43 = par->CR45 = par->CR65 = 0x00;
893
894         vga_out8 (0x3d4, 0x40, par);
895         par->CR40 = vga_in8 (0x3d5, par) & ~0x01;
896
897         par->MMPR0 = 0x010400;
898         par->MMPR1 = 0x00;
899         par->MMPR2 = 0x0808;
900         par->MMPR3 = 0x08080810;
901
902         SavageCalcClock (dclk, 1, 1, 127, 0, 4, 180000, 360000, &m, &n, &r);
903         /* m = 107; n = 4; r = 2; */
904
905         if (par->MCLK <= 0) {
906                 par->SR10 = 255;
907                 par->SR11 = 255;
908         } else {
909                 common_calc_clock (par->MCLK, 1, 1, 31, 0, 3, 135000, 270000,
910                                    &par->SR11, &par->SR10);
911                 /*      par->SR10 = 80; // MCLK == 286000 */
912                 /*      par->SR11 = 125; */
913         }
914
915         par->SR12 = (r << 6) | (n & 0x3f);
916         par->SR13 = m & 0xff;
917         par->SR29 = (r & 4) | (m & 0x100) >> 5 | (n & 0x40) >> 2;
918
919         if (var->bits_per_pixel < 24)
920                 par->MMPR0 -= 0x8000;
921         else
922                 par->MMPR0 -= 0x4000;
923
924         if (timings.interlaced)
925                 par->CR42 = 0x20;
926         else
927                 par->CR42 = 0x00;
928
929         par->CR34 = 0x10; /* display fifo */
930
931         i = ((((timings.HTotal >> 3) - 5) & 0x100) >> 8) |
932                 ((((timings.HDisplay >> 3) - 1) & 0x100) >> 7) |
933                 ((((timings.HSyncStart >> 3) - 1) & 0x100) >> 6) |
934                 ((timings.HSyncStart & 0x800) >> 7);
935
936         if ((timings.HSyncEnd >> 3) - (timings.HSyncStart >> 3) > 64)
937                 i |= 0x08;
938         if ((timings.HSyncEnd >> 3) - (timings.HSyncStart >> 3) > 32)
939                 i |= 0x20;
940
941         j = (par->CRTC[0] + ((i & 0x01) << 8) +
942              par->CRTC[4] + ((i & 0x10) << 4) + 1) / 2;
943
944         if (j - (par->CRTC[4] + ((i & 0x10) << 4)) < 4) {
945                 if (par->CRTC[4] + ((i & 0x10) << 4) + 4 <=
946                     par->CRTC[0] + ((i & 0x01) << 8))
947                         j = par->CRTC[4] + ((i & 0x10) << 4) + 4;
948                 else
949                         j = par->CRTC[0] + ((i & 0x01) << 8) + 1;
950         }
951
952         par->CR3B = j & 0xff;
953         i |= (j & 0x100) >> 2;
954         par->CR3C = (par->CRTC[0] + ((i & 0x01) << 8)) / 2;
955         par->CR5D = i;
956         par->CR5E = (((timings.VTotal - 2) & 0x400) >> 10) |
957                 (((timings.VDisplay - 1) & 0x400) >> 9) |
958                 (((timings.VSyncStart) & 0x400) >> 8) |
959                 (((timings.VSyncStart) & 0x400) >> 6) | 0x40;
960         width = (var->xres_virtual * ((var->bits_per_pixel+7) / 8)) >> 3;
961         par->CR91 = par->CRTC[19] = 0xff & width;
962         par->CR51 = (0x300 & width) >> 4;
963         par->CR90 = 0x80 | (width >> 8);
964         par->MiscOutReg |= 0x0c;
965
966         /* Set frame buffer description. */
967
968         if (var->bits_per_pixel <= 8)
969                 par->CR50 = 0;
970         else if (var->bits_per_pixel <= 16)
971                 par->CR50 = 0x10;
972         else
973                 par->CR50 = 0x30;
974
975         if (var->xres_virtual <= 640)
976                 par->CR50 |= 0x40;
977         else if (var->xres_virtual == 800)
978                 par->CR50 |= 0x80;
979         else if (var->xres_virtual == 1024)
980                 par->CR50 |= 0x00;
981         else if (var->xres_virtual == 1152)
982                 par->CR50 |= 0x01;
983         else if (var->xres_virtual == 1280)
984                 par->CR50 |= 0xc0;
985         else if (var->xres_virtual == 1600)
986                 par->CR50 |= 0x81;
987         else
988                 par->CR50 |= 0xc1;      /* Use GBD */
989
990         if( par->chip == S3_SAVAGE2000 )
991                 par->CR33 = 0x08;
992         else
993                 par->CR33 = 0x20;
994
995         par->CRTC[0x17] = 0xeb;
996
997         par->CR67 |= 1;
998
999         vga_out8(0x3d4, 0x36, par);
1000         par->CR36 = vga_in8 (0x3d5, par);
1001         vga_out8 (0x3d4, 0x68, par);
1002         par->CR68 = vga_in8 (0x3d5, par);
1003         par->CR69 = 0;
1004         vga_out8 (0x3d4, 0x6f, par);
1005         par->CR6F = vga_in8 (0x3d5, par);
1006         vga_out8 (0x3d4, 0x86, par);
1007         par->CR86 = vga_in8 (0x3d5, par);
1008         vga_out8 (0x3d4, 0x88, par);
1009         par->CR88 = vga_in8 (0x3d5, par) | 0x08;
1010         vga_out8 (0x3d4, 0xb0, par);
1011         par->CRB0 = vga_in8 (0x3d5, par) | 0x80;
1012
1013         return 0;
1014 }
1015
1016 /* --------------------------------------------------------------------- */
1017
1018 /*
1019  *    Set a single color register. Return != 0 for invalid regno.
1020  */
1021 static int savagefb_setcolreg(unsigned        regno,
1022                               unsigned        red,
1023                               unsigned        green,
1024                               unsigned        blue,
1025                               unsigned        transp,
1026                               struct fb_info *info)
1027 {
1028         struct savagefb_par *par = (struct savagefb_par *)info->par;
1029
1030         if (regno >= NR_PALETTE)
1031                 return -EINVAL;
1032
1033         par->palette[regno].red    = red;
1034         par->palette[regno].green  = green;
1035         par->palette[regno].blue   = blue;
1036         par->palette[regno].transp = transp;
1037
1038         switch (info->var.bits_per_pixel) {
1039         case 8:
1040                 vga_out8 (0x3c8, regno, par);
1041
1042                 vga_out8 (0x3c9, red   >> 10, par);
1043                 vga_out8 (0x3c9, green >> 10, par);
1044                 vga_out8 (0x3c9, blue  >> 10, par);
1045                 break;
1046
1047         case 16:
1048                 if (regno < 16)
1049                         ((u32 *)info->pseudo_palette)[regno] =
1050                                 ((red   & 0xf800)      ) |
1051                                 ((green & 0xfc00) >>  5) |
1052                                 ((blue  & 0xf800) >> 11);
1053                 break;
1054
1055         case 24:
1056                 if (regno < 16)
1057                         ((u32 *)info->pseudo_palette)[regno] =
1058                                 ((red    & 0xff00) <<  8) |
1059                                 ((green  & 0xff00)      ) |
1060                                 ((blue   & 0xff00) >>  8);
1061                 break;
1062         case 32:
1063                 if (regno < 16)
1064                         ((u32 *)info->pseudo_palette)[regno] =
1065                                 ((transp & 0xff00) << 16) |
1066                                 ((red    & 0xff00) <<  8) |
1067                                 ((green  & 0xff00)      ) |
1068                                 ((blue   & 0xff00) >>  8);
1069                 break;
1070
1071         default:
1072                 return 1;
1073         }
1074
1075         return 0;
1076 }
1077
1078 static void savagefb_set_par_int (struct savagefb_par  *par)
1079 {
1080         unsigned char tmp, cr3a, cr66, cr67;
1081
1082         DBG ("savagefb_set_par_int");
1083
1084         par->SavageWaitIdle (par);
1085
1086         vga_out8 (0x3c2, 0x23, par);
1087
1088         vga_out16 (0x3d4, 0x4838, par);
1089         vga_out16 (0x3d4, 0xa539, par);
1090         vga_out16 (0x3c4, 0x0608, par);
1091
1092         vgaHWProtect (par, 1);
1093
1094         /*
1095          * Some Savage/MX and /IX systems go nuts when trying to exit the
1096          * server after WindowMaker has displayed a gradient background.  I
1097          * haven't been able to find what causes it, but a non-destructive
1098          * switch to mode 3 here seems to eliminate the issue.
1099          */
1100
1101         VerticalRetraceWait(par);
1102         vga_out8 (0x3d4, 0x67, par);
1103         cr67 = vga_in8 (0x3d5, par);
1104         vga_out8 (0x3d5, cr67/*par->CR67*/ & ~0x0c, par); /* no STREAMS yet */
1105
1106         vga_out8 (0x3d4, 0x23, par);
1107         vga_out8 (0x3d5, 0x00, par);
1108         vga_out8 (0x3d4, 0x26, par);
1109         vga_out8 (0x3d5, 0x00, par);
1110
1111         /* restore extended regs */
1112         vga_out8 (0x3d4, 0x66, par);
1113         vga_out8 (0x3d5, par->CR66, par);
1114         vga_out8 (0x3d4, 0x3a, par);
1115         vga_out8 (0x3d5, par->CR3A, par);
1116         vga_out8 (0x3d4, 0x31, par);
1117         vga_out8 (0x3d5, par->CR31, par);
1118         vga_out8 (0x3d4, 0x32, par);
1119         vga_out8 (0x3d5, par->CR32, par);
1120         vga_out8 (0x3d4, 0x58, par);
1121         vga_out8 (0x3d5, par->CR58, par);
1122         vga_out8 (0x3d4, 0x53, par);
1123         vga_out8 (0x3d5, par->CR53 & 0x7f, par);
1124
1125         vga_out16 (0x3c4, 0x0608, par);
1126
1127         /* Restore DCLK registers. */
1128
1129         vga_out8 (0x3c4, 0x0e, par);
1130         vga_out8 (0x3c5, par->SR0E, par);
1131         vga_out8 (0x3c4, 0x0f, par);
1132         vga_out8 (0x3c5, par->SR0F, par);
1133         vga_out8 (0x3c4, 0x29, par);
1134         vga_out8 (0x3c5, par->SR29, par);
1135         vga_out8 (0x3c4, 0x15, par);
1136         vga_out8 (0x3c5, par->SR15, par);
1137
1138         /* Restore flat panel expansion regsters. */
1139         if( par->chip == S3_SAVAGE_MX ) {
1140                 int i;
1141
1142                 for( i = 0; i < 8; i++ ) {
1143                         vga_out8 (0x3c4, 0x54+i, par);
1144                         vga_out8 (0x3c5, par->SR54[i], par);
1145                 }
1146         }
1147
1148         vgaHWRestore (par);
1149
1150         /* extended mode timing registers */
1151         vga_out8 (0x3d4, 0x53, par);
1152         vga_out8 (0x3d5, par->CR53, par);
1153         vga_out8 (0x3d4, 0x5d, par);
1154         vga_out8 (0x3d5, par->CR5D, par);
1155         vga_out8 (0x3d4, 0x5e, par);
1156         vga_out8 (0x3d5, par->CR5E, par);
1157         vga_out8 (0x3d4, 0x3b, par);
1158         vga_out8 (0x3d5, par->CR3B, par);
1159         vga_out8 (0x3d4, 0x3c, par);
1160         vga_out8 (0x3d5, par->CR3C, par);
1161         vga_out8 (0x3d4, 0x43, par);
1162         vga_out8 (0x3d5, par->CR43, par);
1163         vga_out8 (0x3d4, 0x65, par);
1164         vga_out8 (0x3d5, par->CR65, par);
1165
1166         /* restore the desired video mode with cr67 */
1167         vga_out8 (0x3d4, 0x67, par);
1168         /* following part not present in X11 driver */
1169         cr67 = vga_in8 (0x3d5, par) & 0xf;
1170         vga_out8 (0x3d5, 0x50 | cr67, par);
1171         udelay (10000);
1172         vga_out8 (0x3d4, 0x67, par);
1173         /* end of part */
1174         vga_out8 (0x3d5, par->CR67 & ~0x0c, par);
1175
1176         /* other mode timing and extended regs */
1177         vga_out8 (0x3d4, 0x34, par);
1178         vga_out8 (0x3d5, par->CR34, par);
1179         vga_out8 (0x3d4, 0x40, par);
1180         vga_out8 (0x3d5, par->CR40, par);
1181         vga_out8 (0x3d4, 0x42, par);
1182         vga_out8 (0x3d5, par->CR42, par);
1183         vga_out8 (0x3d4, 0x45, par);
1184         vga_out8 (0x3d5, par->CR45, par);
1185         vga_out8 (0x3d4, 0x50, par);
1186         vga_out8 (0x3d5, par->CR50, par);
1187         vga_out8 (0x3d4, 0x51, par);
1188         vga_out8 (0x3d5, par->CR51, par);
1189
1190         /* memory timings */
1191         vga_out8 (0x3d4, 0x36, par);
1192         vga_out8 (0x3d5, par->CR36, par);
1193         vga_out8 (0x3d4, 0x60, par);
1194         vga_out8 (0x3d5, par->CR60, par);
1195         vga_out8 (0x3d4, 0x68, par);
1196         vga_out8 (0x3d5, par->CR68, par);
1197         vga_out8 (0x3d4, 0x69, par);
1198         vga_out8 (0x3d5, par->CR69, par);
1199         vga_out8 (0x3d4, 0x6f, par);
1200         vga_out8 (0x3d5, par->CR6F, par);
1201
1202         vga_out8 (0x3d4, 0x33, par);
1203         vga_out8 (0x3d5, par->CR33, par);
1204         vga_out8 (0x3d4, 0x86, par);
1205         vga_out8 (0x3d5, par->CR86, par);
1206         vga_out8 (0x3d4, 0x88, par);
1207         vga_out8 (0x3d5, par->CR88, par);
1208         vga_out8 (0x3d4, 0x90, par);
1209         vga_out8 (0x3d5, par->CR90, par);
1210         vga_out8 (0x3d4, 0x91, par);
1211         vga_out8 (0x3d5, par->CR91, par);
1212
1213         if (par->chip == S3_SAVAGE4) {
1214                 vga_out8 (0x3d4, 0xb0, par);
1215                 vga_out8 (0x3d5, par->CRB0, par);
1216         }
1217
1218         vga_out8 (0x3d4, 0x32, par);
1219         vga_out8 (0x3d5, par->CR32, par);
1220
1221         /* unlock extended seq regs */
1222         vga_out8 (0x3c4, 0x08, par);
1223         vga_out8 (0x3c5, 0x06, par);
1224
1225         /* Restore extended sequencer regs for MCLK. SR10 == 255 indicates
1226          * that we should leave the default SR10 and SR11 values there.
1227          */
1228         if (par->SR10 != 255) {
1229                 vga_out8 (0x3c4, 0x10, par);
1230                 vga_out8 (0x3c5, par->SR10, par);
1231                 vga_out8 (0x3c4, 0x11, par);
1232                 vga_out8 (0x3c5, par->SR11, par);
1233         }
1234
1235         /* restore extended seq regs for dclk */
1236         vga_out8 (0x3c4, 0x0e, par);
1237         vga_out8 (0x3c5, par->SR0E, par);
1238         vga_out8 (0x3c4, 0x0f, par);
1239         vga_out8 (0x3c5, par->SR0F, par);
1240         vga_out8 (0x3c4, 0x12, par);
1241         vga_out8 (0x3c5, par->SR12, par);
1242         vga_out8 (0x3c4, 0x13, par);
1243         vga_out8 (0x3c5, par->SR13, par);
1244         vga_out8 (0x3c4, 0x29, par);
1245         vga_out8 (0x3c5, par->SR29, par);
1246
1247         vga_out8 (0x3c4, 0x18, par);
1248         vga_out8 (0x3c5, par->SR18, par);
1249
1250         /* load new m, n pll values for dclk & mclk */
1251         vga_out8 (0x3c4, 0x15, par);
1252         tmp = vga_in8 (0x3c5, par) & ~0x21;
1253
1254         vga_out8 (0x3c5, tmp | 0x03, par);
1255         vga_out8 (0x3c5, tmp | 0x23, par);
1256         vga_out8 (0x3c5, tmp | 0x03, par);
1257         vga_out8 (0x3c5, par->SR15, par);
1258         udelay (100);
1259
1260         vga_out8 (0x3c4, 0x30, par);
1261         vga_out8 (0x3c5, par->SR30, par);
1262         vga_out8 (0x3c4, 0x08, par);
1263         vga_out8 (0x3c5, par->SR08, par);
1264
1265         /* now write out cr67 in full, possibly starting STREAMS */
1266         VerticalRetraceWait(par);
1267         vga_out8 (0x3d4, 0x67, par);
1268         vga_out8 (0x3d5, par->CR67, par);
1269
1270         vga_out8 (0x3d4, 0x66, par);
1271         cr66 = vga_in8 (0x3d5, par);
1272         vga_out8 (0x3d5, cr66 | 0x80, par);
1273         vga_out8 (0x3d4, 0x3a, par);
1274         cr3a = vga_in8 (0x3d5, par);
1275         vga_out8 (0x3d5, cr3a | 0x80, par);
1276
1277         if (par->chip != S3_SAVAGE_MX) {
1278                 VerticalRetraceWait(par);
1279                 savage_out32 (FIFO_CONTROL_REG, par->MMPR0, par);
1280                 par->SavageWaitIdle (par);
1281                 savage_out32 (MIU_CONTROL_REG, par->MMPR1, par);
1282                 par->SavageWaitIdle (par);
1283                 savage_out32 (STREAMS_TIMEOUT_REG, par->MMPR2, par);
1284                 par->SavageWaitIdle (par);
1285                 savage_out32 (MISC_TIMEOUT_REG, par->MMPR3, par);
1286         }
1287
1288         vga_out8 (0x3d4, 0x66, par);
1289         vga_out8 (0x3d5, cr66, par);
1290         vga_out8 (0x3d4, 0x3a, par);
1291         vga_out8 (0x3d5, cr3a, par);
1292
1293         SavageSetup2DEngine (par);
1294         vgaHWProtect (par, 0);
1295 }
1296
1297 static void savagefb_update_start (struct savagefb_par      *par,
1298                                    struct fb_var_screeninfo *var)
1299 {
1300         int base;
1301
1302         base = ((var->yoffset * var->xres_virtual + (var->xoffset & ~1))
1303                 * ((var->bits_per_pixel+7) / 8)) >> 2;
1304
1305         /* now program the start address registers */
1306         vga_out16(0x3d4, (base & 0x00ff00) | 0x0c, par);
1307         vga_out16(0x3d4, ((base & 0x00ff) << 8) | 0x0d, par);
1308         vga_out8 (0x3d4, 0x69, par);
1309         vga_out8 (0x3d5, (base & 0x7f0000) >> 16, par);
1310 }
1311
1312
1313 static void savagefb_set_fix(struct fb_info *info)
1314 {
1315         info->fix.line_length = info->var.xres_virtual *
1316                 info->var.bits_per_pixel / 8;
1317
1318         if (info->var.bits_per_pixel == 8)
1319                 info->fix.visual      = FB_VISUAL_PSEUDOCOLOR;
1320         else
1321                 info->fix.visual      = FB_VISUAL_TRUECOLOR;
1322 }
1323
1324 #if defined(CONFIG_FB_SAVAGE_ACCEL)
1325 static void savagefb_set_clip(struct fb_info *info)
1326 {
1327     struct savagefb_par *par = (struct savagefb_par *)info->par;
1328     int cmd;
1329
1330     cmd = BCI_CMD_NOP | BCI_CMD_CLIP_NEW;
1331     par->bci_ptr = 0;
1332     par->SavageWaitFifo(par,3);
1333     BCI_SEND(cmd);
1334     BCI_SEND(BCI_CLIP_TL(0, 0));
1335     BCI_SEND(BCI_CLIP_BR(0xfff, 0xfff));
1336 }
1337 #endif
1338
1339 static int savagefb_set_par (struct fb_info *info)
1340 {
1341         struct savagefb_par *par = (struct savagefb_par *)info->par;
1342         struct fb_var_screeninfo *var = &info->var;
1343         int err;
1344
1345         DBG("savagefb_set_par");
1346         err = savagefb_decode_var (var, par);
1347         if (err)
1348                 return err;
1349
1350         if (par->dacSpeedBpp <= 0) {
1351                 if (var->bits_per_pixel > 24)
1352                         par->dacSpeedBpp = par->clock[3];
1353                 else if (var->bits_per_pixel >= 24)
1354                         par->dacSpeedBpp = par->clock[2];
1355                 else if ((var->bits_per_pixel > 8) && (var->bits_per_pixel < 24))
1356                         par->dacSpeedBpp = par->clock[1];
1357                 else if (var->bits_per_pixel <= 8)
1358                         par->dacSpeedBpp = par->clock[0];
1359         }
1360
1361         /* Set ramdac limits */
1362         par->maxClock = par->dacSpeedBpp;
1363         par->minClock = 10000;
1364
1365         savagefb_set_par_int (par);
1366         savagefb_update_start (par, var);
1367         fb_set_cmap (&info->cmap, info);
1368         savagefb_set_fix(info);
1369         savagefb_set_clip(info);
1370
1371         SavagePrintRegs();
1372         return 0;
1373 }
1374
1375 /*
1376  *    Pan or Wrap the Display
1377  */
1378 static int savagefb_pan_display (struct fb_var_screeninfo *var,
1379                                  struct fb_info           *info)
1380 {
1381         struct savagefb_par *par = (struct savagefb_par *)info->par;
1382         u_int y_bottom;
1383
1384         y_bottom = var->yoffset;
1385
1386         if (!(var->vmode & FB_VMODE_YWRAP))
1387                 y_bottom += var->yres;
1388
1389         if (var->xoffset > (var->xres_virtual - var->xres))
1390                 return -EINVAL;
1391         if (y_bottom > info->var.yres_virtual)
1392                 return -EINVAL;
1393
1394         savagefb_update_start (par, var);
1395
1396         info->var.xoffset = var->xoffset;
1397         info->var.yoffset = var->yoffset;
1398
1399         if (var->vmode & FB_VMODE_YWRAP)
1400                 info->var.vmode |= FB_VMODE_YWRAP;
1401         else
1402                 info->var.vmode &= ~FB_VMODE_YWRAP;
1403
1404         return 0;
1405 }
1406
1407 static int savagefb_blank(int blank, struct fb_info *info)
1408 {
1409         struct savagefb_par *par = info->par;
1410         u8 sr8 = 0, srd = 0;
1411
1412         if (par->display_type == DISP_CRT) {
1413                 vga_out8(0x3c4, 0x08, par);
1414                 sr8 = vga_in8(0x3c5, par);
1415                 sr8 |= 0x06;
1416                 vga_out8(0x3c5, sr8, par);
1417                 vga_out8(0x3c4, 0x0d, par);
1418                 srd = vga_in8(0x3c5, par);
1419                 srd &= 0x03;
1420
1421                 switch (blank) {
1422                 case FB_BLANK_UNBLANK:
1423                 case FB_BLANK_NORMAL:
1424                         break;
1425                 case FB_BLANK_VSYNC_SUSPEND:
1426                         srd |= 0x10;
1427                         break;
1428                 case FB_BLANK_HSYNC_SUSPEND:
1429                         srd |= 0x40;
1430                         break;
1431                 case FB_BLANK_POWERDOWN:
1432                         srd |= 0x50;
1433                         break;
1434                 }
1435
1436                 vga_out8(0x3c4, 0x0d, par);
1437                 vga_out8(0x3c5, srd, par);
1438         }
1439
1440         if (par->display_type == DISP_LCD ||
1441             par->display_type == DISP_DFP) {
1442                 switch(blank) {
1443                 case FB_BLANK_UNBLANK:
1444                 case FB_BLANK_NORMAL:
1445                         vga_out8(0x3c4, 0x31, par); /* SR31 bit 4 - FP enable */
1446                         vga_out8(0x3c5, vga_in8(0x3c5, par) | 0x10, par);
1447                         break;
1448                 case FB_BLANK_VSYNC_SUSPEND:
1449                 case FB_BLANK_HSYNC_SUSPEND:
1450                 case FB_BLANK_POWERDOWN:
1451                         vga_out8(0x3c4, 0x31, par); /* SR31 bit 4 - FP enable */
1452                         vga_out8(0x3c5, vga_in8(0x3c5, par) & ~0x10, par);
1453                         break;
1454                 }
1455         }
1456
1457         return (blank == FB_BLANK_NORMAL) ? 1 : 0;
1458 }
1459
1460 static struct fb_ops savagefb_ops = {
1461         .owner          = THIS_MODULE,
1462         .fb_check_var   = savagefb_check_var,
1463         .fb_set_par     = savagefb_set_par,
1464         .fb_setcolreg   = savagefb_setcolreg,
1465         .fb_pan_display = savagefb_pan_display,
1466         .fb_blank       = savagefb_blank,
1467 #if defined(CONFIG_FB_SAVAGE_ACCEL)
1468         .fb_fillrect    = savagefb_fillrect,
1469         .fb_copyarea    = savagefb_copyarea,
1470         .fb_imageblit   = savagefb_imageblit,
1471         .fb_sync        = savagefb_sync,
1472 #else
1473         .fb_fillrect    = cfb_fillrect,
1474         .fb_copyarea    = cfb_copyarea,
1475         .fb_imageblit   = cfb_imageblit,
1476 #endif
1477 };
1478
1479 /* --------------------------------------------------------------------- */
1480
1481 static struct fb_var_screeninfo __devinitdata savagefb_var800x600x8 = {
1482         .accel_flags =  FB_ACCELF_TEXT,
1483         .xres =         800,
1484         .yres =         600,
1485         .xres_virtual =  800,
1486         .yres_virtual =  600,
1487         .bits_per_pixel = 8,
1488         .pixclock =     25000,
1489         .left_margin =  88,
1490         .right_margin = 40,
1491         .upper_margin = 23,
1492         .lower_margin = 1,
1493         .hsync_len =    128,
1494         .vsync_len =    4,
1495         .sync =         FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
1496         .vmode =        FB_VMODE_NONINTERLACED
1497 };
1498
1499 static void savage_enable_mmio (struct savagefb_par *par)
1500 {
1501         unsigned char val;
1502
1503         DBG ("savage_enable_mmio\n");
1504
1505         val = vga_in8 (0x3c3, par);
1506         vga_out8 (0x3c3, val | 0x01, par);
1507         val = vga_in8 (0x3cc, par);
1508         vga_out8 (0x3c2, val | 0x01, par);
1509
1510         if (par->chip >= S3_SAVAGE4) {
1511                 vga_out8 (0x3d4, 0x40, par);
1512                 val = vga_in8 (0x3d5, par);
1513                 vga_out8 (0x3d5, val | 1, par);
1514         }
1515 }
1516
1517
1518 static void savage_disable_mmio (struct savagefb_par *par)
1519 {
1520         unsigned char val;
1521
1522         DBG ("savage_disable_mmio\n");
1523
1524         if(par->chip >= S3_SAVAGE4 ) {
1525                 vga_out8 (0x3d4, 0x40, par);
1526                 val = vga_in8 (0x3d5, par);
1527                 vga_out8 (0x3d5, val | 1, par);
1528         }
1529 }
1530
1531
1532 static int __devinit savage_map_mmio (struct fb_info *info)
1533 {
1534         struct savagefb_par *par = (struct savagefb_par *)info->par;
1535         DBG ("savage_map_mmio");
1536
1537         if (S3_SAVAGE3D_SERIES (par->chip))
1538                 par->mmio.pbase = pci_resource_start (par->pcidev, 0) +
1539                         SAVAGE_NEWMMIO_REGBASE_S3;
1540         else
1541                 par->mmio.pbase = pci_resource_start (par->pcidev, 0) +
1542                         SAVAGE_NEWMMIO_REGBASE_S4;
1543
1544         par->mmio.len = SAVAGE_NEWMMIO_REGSIZE;
1545
1546         par->mmio.vbase = ioremap (par->mmio.pbase, par->mmio.len);
1547         if (!par->mmio.vbase) {
1548                 printk ("savagefb: unable to map memory mapped IO\n");
1549                 return -ENOMEM;
1550         } else
1551                 printk (KERN_INFO "savagefb: mapped io at %p\n",
1552                         par->mmio.vbase);
1553
1554         info->fix.mmio_start = par->mmio.pbase;
1555         info->fix.mmio_len   = par->mmio.len;
1556
1557         par->bci_base = (u32 __iomem *)(par->mmio.vbase + BCI_BUFFER_OFFSET);
1558         par->bci_ptr  = 0;
1559
1560         savage_enable_mmio (par);
1561
1562         return 0;
1563 }
1564
1565 static void __devinit savage_unmap_mmio (struct fb_info *info)
1566 {
1567         struct savagefb_par *par = (struct savagefb_par *)info->par;
1568         DBG ("savage_unmap_mmio");
1569
1570         savage_disable_mmio(par);
1571
1572         if (par->mmio.vbase) {
1573                 iounmap(par->mmio.vbase);
1574                 par->mmio.vbase = NULL;
1575         }
1576 }
1577
1578 static int __devinit savage_map_video (struct fb_info *info,
1579                                        int video_len)
1580 {
1581         struct savagefb_par *par = (struct savagefb_par *)info->par;
1582         int resource;
1583
1584         DBG("savage_map_video");
1585
1586         if (S3_SAVAGE3D_SERIES (par->chip))
1587                 resource = 0;
1588         else
1589                 resource = 1;
1590
1591         par->video.pbase = pci_resource_start (par->pcidev, resource);
1592         par->video.len   = video_len;
1593         par->video.vbase = ioremap (par->video.pbase, par->video.len);
1594
1595         if (!par->video.vbase) {
1596                 printk ("savagefb: unable to map screen memory\n");
1597                 return -ENOMEM;
1598         } else
1599                 printk (KERN_INFO "savagefb: mapped framebuffer at %p, "
1600                         "pbase == %x\n", par->video.vbase, par->video.pbase);
1601
1602         info->fix.smem_start = par->video.pbase;
1603         info->fix.smem_len   = par->video.len - par->cob_size;
1604         info->screen_base    = par->video.vbase;
1605
1606 #ifdef CONFIG_MTRR
1607         par->video.mtrr = mtrr_add (par->video.pbase, video_len,
1608                                      MTRR_TYPE_WRCOMB, 1);
1609 #endif
1610
1611         /* Clear framebuffer, it's all white in memory after boot */
1612         memset_io (par->video.vbase, 0, par->video.len);
1613
1614         return 0;
1615 }
1616
1617 static void __devinit savage_unmap_video (struct fb_info *info)
1618 {
1619         struct savagefb_par *par = (struct savagefb_par *)info->par;
1620
1621         DBG("savage_unmap_video");
1622
1623         if (par->video.vbase) {
1624 #ifdef CONFIG_MTRR
1625                 mtrr_del (par->video.mtrr, par->video.pbase, par->video.len);
1626 #endif
1627
1628                 iounmap (par->video.vbase);
1629                 par->video.vbase = NULL;
1630                 info->screen_base = NULL;
1631         }
1632 }
1633
1634 static int __devinit savage_init_hw (struct savagefb_par *par)
1635 {
1636         unsigned char config1, m, n, n1, n2, sr8, cr3f, cr66 = 0, tmp;
1637
1638         static unsigned char RamSavage3D[] = { 8, 4, 4, 2 };
1639         static unsigned char RamSavage4[] =  { 2, 4, 8, 12, 16, 32, 64, 32 };
1640         static unsigned char RamSavageMX[] = { 2, 8, 4, 16, 8, 16, 4, 16 };
1641         static unsigned char RamSavageNB[] = { 0, 2, 4, 8, 16, 32, 2, 2 };
1642         int videoRam, videoRambytes, dvi;
1643
1644         DBG("savage_init_hw");
1645
1646         /* unprotect CRTC[0-7] */
1647         vga_out8(0x3d4, 0x11, par);
1648         tmp = vga_in8(0x3d5, par);
1649         vga_out8(0x3d5, tmp & 0x7f, par);
1650
1651         /* unlock extended regs */
1652         vga_out16(0x3d4, 0x4838, par);
1653         vga_out16(0x3d4, 0xa039, par);
1654         vga_out16(0x3c4, 0x0608, par);
1655
1656         vga_out8(0x3d4, 0x40, par);
1657         tmp = vga_in8(0x3d5, par);
1658         vga_out8(0x3d5, tmp & ~0x01, par);
1659
1660         /* unlock sys regs */
1661         vga_out8(0x3d4, 0x38, par);
1662         vga_out8(0x3d5, 0x48, par);
1663
1664         /* Unlock system registers. */
1665         vga_out16(0x3d4, 0x4838, par);
1666
1667         /* Next go on to detect amount of installed ram */
1668
1669         vga_out8(0x3d4, 0x36, par);            /* for register CR36 (CONFG_REG1), */
1670         config1 = vga_in8(0x3d5, par);    /* get amount of vram installed */
1671
1672         /* Compute the amount of video memory and offscreen memory. */
1673
1674         switch  (par->chip) {
1675         case S3_SAVAGE3D:
1676                 videoRam = RamSavage3D[ (config1 & 0xC0) >> 6 ] * 1024;
1677                 break;
1678
1679         case S3_SAVAGE4:
1680                 /*
1681                  * The Savage4 has one ugly special case to consider.  On
1682                  * systems with 4 banks of 2Mx32 SDRAM, the BIOS says 4MB
1683                  * when it really means 8MB.  Why do it the same when you
1684                  * can do it different...
1685                  */
1686                 vga_out8(0x3d4, 0x68, par);     /* memory control 1 */
1687                 if( (vga_in8(0x3d5, par) & 0xC0) == (0x01 << 6) )
1688                         RamSavage4[1] = 8;
1689
1690                 /*FALLTHROUGH*/
1691
1692         case S3_SAVAGE2000:
1693                 videoRam = RamSavage4[ (config1 & 0xE0) >> 5 ] * 1024;
1694                 break;
1695
1696         case S3_SAVAGE_MX:
1697         case S3_SUPERSAVAGE:
1698                 videoRam = RamSavageMX[ (config1 & 0x0E) >> 1 ] * 1024;
1699                 break;
1700
1701         case S3_PROSAVAGE:
1702                 videoRam = RamSavageNB[ (config1 & 0xE0) >> 5 ] * 1024;
1703                 break;
1704
1705         default:
1706                 /* How did we get here? */
1707                 videoRam = 0;
1708                 break;
1709         }
1710
1711         videoRambytes = videoRam * 1024;
1712
1713         printk (KERN_INFO "savagefb: probed videoram:  %dk\n", videoRam);
1714
1715         /* reset graphics engine to avoid memory corruption */
1716         vga_out8 (0x3d4, 0x66, par);
1717         cr66 = vga_in8 (0x3d5, par);
1718         vga_out8 (0x3d5, cr66 | 0x02, par);
1719         udelay (10000);
1720
1721         vga_out8 (0x3d4, 0x66, par);
1722         vga_out8 (0x3d5, cr66 & ~0x02, par);    /* clear reset flag */
1723         udelay (10000);
1724
1725
1726         /*
1727          * reset memory interface, 3D engine, AGP master, PCI master,
1728          * master engine unit, motion compensation/LPB
1729          */
1730         vga_out8 (0x3d4, 0x3f, par);
1731         cr3f = vga_in8 (0x3d5, par);
1732         vga_out8 (0x3d5, cr3f | 0x08, par);
1733         udelay (10000);
1734
1735         vga_out8 (0x3d4, 0x3f, par);
1736         vga_out8 (0x3d5, cr3f & ~0x08, par);    /* clear reset flags */
1737         udelay (10000);
1738
1739         /* Savage ramdac speeds */
1740         par->numClocks = 4;
1741         par->clock[0] = 250000;
1742         par->clock[1] = 250000;
1743         par->clock[2] = 220000;
1744         par->clock[3] = 220000;
1745
1746         /* detect current mclk */
1747         vga_out8(0x3c4, 0x08, par);
1748         sr8 = vga_in8(0x3c5, par);
1749         vga_out8(0x3c5, 0x06, par);
1750         vga_out8(0x3c4, 0x10, par);
1751         n = vga_in8(0x3c5, par);
1752         vga_out8(0x3c4, 0x11, par);
1753         m = vga_in8(0x3c5, par);
1754         vga_out8(0x3c4, 0x08, par);
1755         vga_out8(0x3c5, sr8, par);
1756         m &= 0x7f;
1757         n1 = n & 0x1f;
1758         n2 = (n >> 5) & 0x03;
1759         par->MCLK = ((1431818 * (m+2)) / (n1+2) / (1 << n2) + 50) / 100;
1760         printk (KERN_INFO "savagefb: Detected current MCLK value of %d kHz\n",
1761                 par->MCLK);
1762
1763         /* check for DVI/flat panel */
1764         dvi = 0;
1765
1766         if (par->chip == S3_SAVAGE4) {
1767                 unsigned char sr30 = 0x00;
1768
1769                 vga_out8(0x3c4, 0x30, par);
1770                 /* clear bit 1 */
1771                 vga_out8(0x3c5, vga_in8(0x3c5, par) & ~0x02, par);
1772                 sr30 = vga_in8(0x3c5, par);
1773                 if (sr30 & 0x02 /*0x04 */) {
1774                         dvi = 1;
1775                         printk("savagefb: Digital Flat Panel Detected\n");
1776                 }
1777         }
1778
1779         if (S3_SAVAGE_MOBILE_SERIES(par->chip) && !par->crtonly)
1780                 par->display_type = DISP_LCD;
1781         else if (dvi || (par->chip == S3_SAVAGE4 && par->dvi))
1782                 par->display_type = DISP_DFP;
1783         else
1784                 par->display_type = DISP_CRT;
1785
1786         /* Check LCD panel parrmation */
1787
1788         if (par->display_type == DISP_LCD) {
1789                 unsigned char cr6b = VGArCR( 0x6b, par);
1790
1791                 int panelX = (VGArSEQ (0x61, par) +
1792                               ((VGArSEQ (0x66, par) & 0x02) << 7) + 1) * 8;
1793                 int panelY = (VGArSEQ (0x69, par) +
1794                               ((VGArSEQ (0x6e, par) & 0x70) << 4) + 1);
1795
1796                 char * sTechnology = "Unknown";
1797
1798                 /* OK, I admit it.  I don't know how to limit the max dot clock
1799                  * for LCD panels of various sizes.  I thought I copied the
1800                  * formula from the BIOS, but many users have parrmed me of
1801                  * my folly.
1802                  *
1803                  * Instead, I'll abandon any attempt to automatically limit the
1804                  * clock, and add an LCDClock option to XF86Config.  Some day,
1805                  * I should come back to this.
1806                  */
1807
1808                 enum ACTIVE_DISPLAYS { /* These are the bits in CR6B */
1809                         ActiveCRT = 0x01,
1810                         ActiveLCD = 0x02,
1811                         ActiveTV = 0x04,
1812                         ActiveCRT2 = 0x20,
1813                         ActiveDUO = 0x80
1814                 };
1815
1816                 if ((VGArSEQ (0x39, par) & 0x03) == 0) {
1817                         sTechnology = "TFT";
1818                 } else if ((VGArSEQ (0x30, par) & 0x01) == 0) {
1819                         sTechnology = "DSTN";
1820                 } else  {
1821                         sTechnology = "STN";
1822                 }
1823
1824                 printk (KERN_INFO "savagefb: %dx%d %s LCD panel detected %s\n",
1825                         panelX, panelY, sTechnology,
1826                         cr6b & ActiveLCD ? "and active" : "but not active");
1827
1828                 if( cr6b & ActiveLCD )  {
1829                         /*
1830                          * If the LCD is active and panel expansion is enabled,
1831                          * we probably want to kill the HW cursor.
1832                          */
1833
1834                         printk (KERN_INFO "savagefb: Limiting video mode to "
1835                                 "%dx%d\n", panelX, panelY );
1836
1837                         par->SavagePanelWidth = panelX;
1838                         par->SavagePanelHeight = panelY;
1839
1840                 } else
1841                         par->display_type = DISP_CRT;
1842         }
1843
1844         savage_get_default_par (par);
1845
1846         if( S3_SAVAGE4_SERIES(par->chip) ) {
1847                 /*
1848                  * The Savage4 and ProSavage have COB coherency bugs which
1849                  * render the buffer useless.  We disable it.
1850                  */
1851                 par->cob_index = 2;
1852                 par->cob_size = 0x8000 << par->cob_index;
1853                 par->cob_offset = videoRambytes;
1854         } else {
1855                 /* We use 128kB for the COB on all chips. */
1856
1857                 par->cob_index  = 7;
1858                 par->cob_size   = 0x400 << par->cob_index;
1859                 par->cob_offset = videoRambytes - par->cob_size;
1860         }
1861
1862         return videoRambytes;
1863 }
1864
1865 static int __devinit savage_init_fb_info (struct fb_info *info,
1866                                           struct pci_dev *dev,
1867                                           const struct pci_device_id *id)
1868 {
1869         struct savagefb_par *par = (struct savagefb_par *)info->par;
1870         int err = 0;
1871
1872         par->pcidev  = dev;
1873
1874         info->fix.type     = FB_TYPE_PACKED_PIXELS;
1875         info->fix.type_aux         = 0;
1876         info->fix.xpanstep         = 2;
1877         info->fix.ypanstep         = 1;
1878         info->fix.ywrapstep   = 0;
1879         info->fix.accel       = id->driver_data;
1880
1881         switch (info->fix.accel) {
1882         case FB_ACCEL_SUPERSAVAGE:
1883                 par->chip = S3_SUPERSAVAGE;
1884                 snprintf (info->fix.id, 16, "SuperSavage");
1885                 break;
1886         case FB_ACCEL_SAVAGE4:
1887                 par->chip = S3_SAVAGE4;
1888                 snprintf (info->fix.id, 16, "Savage4");
1889                 break;
1890         case FB_ACCEL_SAVAGE3D:
1891                 par->chip = S3_SAVAGE3D;
1892                 snprintf (info->fix.id, 16, "Savage3D");
1893                 break;
1894         case FB_ACCEL_SAVAGE3D_MV:
1895                 par->chip = S3_SAVAGE3D;
1896                 snprintf (info->fix.id, 16, "Savage3D-MV");
1897                 break;
1898         case FB_ACCEL_SAVAGE2000:
1899                 par->chip = S3_SAVAGE2000;
1900                 snprintf (info->fix.id, 16, "Savage2000");
1901                 break;
1902         case FB_ACCEL_SAVAGE_MX_MV:
1903                 par->chip = S3_SAVAGE_MX;
1904                 snprintf (info->fix.id, 16, "Savage/MX-MV");
1905                 break;
1906         case FB_ACCEL_SAVAGE_MX:
1907                 par->chip = S3_SAVAGE_MX;
1908                 snprintf (info->fix.id, 16, "Savage/MX");
1909                 break;
1910         case FB_ACCEL_SAVAGE_IX_MV:
1911                 par->chip = S3_SAVAGE_MX;
1912                 snprintf (info->fix.id, 16, "Savage/IX-MV");
1913                 break;
1914         case FB_ACCEL_SAVAGE_IX:
1915                 par->chip = S3_SAVAGE_MX;
1916                 snprintf (info->fix.id, 16, "Savage/IX");
1917                 break;
1918         case FB_ACCEL_PROSAVAGE_PM:
1919                 par->chip = S3_PROSAVAGE;
1920                 snprintf (info->fix.id, 16, "ProSavagePM");
1921                 break;
1922         case FB_ACCEL_PROSAVAGE_KM:
1923                 par->chip = S3_PROSAVAGE;
1924                 snprintf (info->fix.id, 16, "ProSavageKM");
1925                 break;
1926         case FB_ACCEL_S3TWISTER_P:
1927                 par->chip = S3_PROSAVAGE;
1928                 snprintf (info->fix.id, 16, "TwisterP");
1929                 break;
1930         case FB_ACCEL_S3TWISTER_K:
1931                 par->chip = S3_PROSAVAGE;
1932                 snprintf (info->fix.id, 16, "TwisterK");
1933                 break;
1934         case FB_ACCEL_PROSAVAGE_DDR:
1935                 par->chip = S3_PROSAVAGE;
1936                 snprintf (info->fix.id, 16, "ProSavageDDR");
1937                 break;
1938         case FB_ACCEL_PROSAVAGE_DDRK:
1939                 par->chip = S3_PROSAVAGE;
1940                 snprintf (info->fix.id, 16, "ProSavage8");
1941                 break;
1942         }
1943
1944         if (S3_SAVAGE3D_SERIES(par->chip)) {
1945                 par->SavageWaitIdle = savage3D_waitidle;
1946                 par->SavageWaitFifo = savage3D_waitfifo;
1947         } else if (S3_SAVAGE4_SERIES(par->chip) ||
1948                    S3_SUPERSAVAGE == par->chip) {
1949                 par->SavageWaitIdle = savage4_waitidle;
1950                 par->SavageWaitFifo = savage4_waitfifo;
1951         } else {
1952                 par->SavageWaitIdle = savage2000_waitidle;
1953                 par->SavageWaitFifo = savage2000_waitfifo;
1954         }
1955
1956         info->var.nonstd      = 0;
1957         info->var.activate    = FB_ACTIVATE_NOW;
1958         info->var.width       = -1;
1959         info->var.height      = -1;
1960         info->var.accel_flags = 0;
1961
1962         info->fbops          = &savagefb_ops;
1963         info->flags          = FBINFO_DEFAULT |
1964                                FBINFO_HWACCEL_YPAN |
1965                                FBINFO_HWACCEL_XPAN;
1966
1967         info->pseudo_palette = par->pseudo_palette;
1968
1969 #if defined(CONFIG_FB_SAVAGE_ACCEL)
1970         /* FIFO size + padding for commands */
1971         info->pixmap.addr = kmalloc(8*1024, GFP_KERNEL);
1972
1973         err = -ENOMEM;
1974         if (info->pixmap.addr) {
1975                 memset(info->pixmap.addr, 0, 8*1024);
1976                 info->pixmap.size = 8*1024;
1977                 info->pixmap.scan_align = 4;
1978                 info->pixmap.buf_align = 4;
1979                 info->pixmap.access_align = 32;
1980
1981                 err = fb_alloc_cmap (&info->cmap, NR_PALETTE, 0);
1982                 if (!err)
1983                 info->flags |= FBINFO_HWACCEL_COPYAREA |
1984                                FBINFO_HWACCEL_FILLRECT |
1985                                FBINFO_HWACCEL_IMAGEBLIT;
1986         }
1987 #endif
1988         return err;
1989 }
1990
1991 /* --------------------------------------------------------------------- */
1992
1993 static int __devinit savagefb_probe (struct pci_dev* dev,
1994                                      const struct pci_device_id* id)
1995 {
1996         struct fb_info *info;
1997         struct savagefb_par *par;
1998         u_int h_sync, v_sync;
1999         int err, lpitch;
2000         int video_len;
2001
2002         DBG("savagefb_probe");
2003         SavagePrintRegs();
2004
2005         info = framebuffer_alloc(sizeof(struct savagefb_par), &dev->dev);
2006         if (!info)
2007                 return -ENOMEM;
2008         par = info->par;
2009         err = pci_enable_device(dev);
2010         if (err)
2011                 goto failed_enable;
2012
2013         if ((err = pci_request_regions(dev, "savagefb"))) {
2014                 printk(KERN_ERR "cannot request PCI regions\n");
2015                 goto failed_enable;
2016         }
2017
2018         err = -ENOMEM;
2019
2020         if ((err = savage_init_fb_info(info, dev, id)))
2021                 goto failed_init;
2022
2023         err = savage_map_mmio(info);
2024         if (err)
2025                 goto failed_mmio;
2026
2027         video_len = savage_init_hw(par);
2028         /* FIXME: cant be negative */
2029         if (video_len < 0) {
2030                 err = video_len;
2031                 goto failed_mmio;
2032         }
2033
2034         err = savage_map_video(info, video_len);
2035         if (err)
2036                 goto failed_video;
2037
2038         INIT_LIST_HEAD(&info->modelist);
2039 #if defined(CONFIG_FB_SAVAGE_I2C)
2040         savagefb_create_i2c_busses(info);
2041         savagefb_probe_i2c_connector(info, &par->edid);
2042         kfree(par->edid);
2043         fb_edid_to_monspecs(par->edid, &info->monspecs);
2044         fb_videomode_to_modelist(info->monspecs.modedb,
2045                                  info->monspecs.modedb_len,
2046                                  &info->modelist);
2047 #endif
2048         info->var = savagefb_var800x600x8;
2049
2050         if (mode_option) {
2051                 fb_find_mode(&info->var, info, mode_option,
2052                              info->monspecs.modedb, info->monspecs.modedb_len,
2053                              NULL, 8);
2054         } else if (info->monspecs.modedb != NULL) {
2055                 struct fb_videomode *modedb;
2056
2057                 modedb = fb_find_best_display(&info->monspecs,
2058                                               &info->modelist);
2059                 savage_update_var(&info->var, modedb);
2060         }
2061
2062         /* maximize virtual vertical length */
2063         lpitch = info->var.xres_virtual*((info->var.bits_per_pixel + 7) >> 3);
2064         info->var.yres_virtual = info->fix.smem_len/lpitch;
2065
2066         if (info->var.yres_virtual < info->var.yres)
2067                 goto failed;
2068
2069 #if defined(CONFIG_FB_SAVAGE_ACCEL)
2070         /*
2071          * The clipping coordinates are masked with 0xFFF, so limit our
2072          * virtual resolutions to these sizes.
2073          */
2074         if (info->var.yres_virtual > 0x1000)
2075                 info->var.yres_virtual = 0x1000;
2076
2077         if (info->var.xres_virtual > 0x1000)
2078                 info->var.xres_virtual = 0x1000;
2079 #endif
2080         savagefb_check_var(&info->var, info);
2081         savagefb_set_fix(info);
2082
2083         /*
2084          * Calculate the hsync and vsync frequencies.  Note that
2085          * we split the 1e12 constant up so that we can preserve
2086          * the precision and fit the results into 32-bit registers.
2087          *  (1953125000 * 512 = 1e12)
2088          */
2089         h_sync = 1953125000 / info->var.pixclock;
2090         h_sync = h_sync * 512 / (info->var.xres + info->var.left_margin +
2091                                  info->var.right_margin +
2092                                  info->var.hsync_len);
2093         v_sync = h_sync / (info->var.yres + info->var.upper_margin +
2094                            info->var.lower_margin + info->var.vsync_len);
2095
2096         printk(KERN_INFO "savagefb v" SAVAGEFB_VERSION ": "
2097                "%dkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n",
2098                info->fix.smem_len >> 10,
2099                info->var.xres, info->var.yres,
2100                h_sync / 1000, h_sync % 1000, v_sync);
2101
2102
2103         fb_destroy_modedb(info->monspecs.modedb);
2104         info->monspecs.modedb = NULL;
2105
2106         err = register_framebuffer (info);
2107         if (err < 0)
2108                 goto failed;
2109
2110         printk (KERN_INFO "fb: S3 %s frame buffer device\n",
2111                 info->fix.id);
2112
2113         /*
2114          * Our driver data
2115          */
2116         pci_set_drvdata(dev, info);
2117
2118         return 0;
2119
2120  failed:
2121 #ifdef CONFIG_FB_SAVAGE_I2C
2122         savagefb_delete_i2c_busses(info);
2123 #endif
2124         fb_alloc_cmap (&info->cmap, 0, 0);
2125         savage_unmap_video(info);
2126  failed_video:
2127         savage_unmap_mmio (info);
2128  failed_mmio:
2129         kfree(info->pixmap.addr);
2130  failed_init:
2131         pci_release_regions(dev);
2132  failed_enable:
2133         framebuffer_release(info);
2134
2135         return err;
2136 }
2137
2138 static void __devexit savagefb_remove (struct pci_dev *dev)
2139 {
2140         struct fb_info *info =
2141                 (struct fb_info *)pci_get_drvdata(dev);
2142
2143         DBG("savagefb_remove");
2144
2145         if (info) {
2146                 /*
2147                  * If unregister_framebuffer fails, then
2148                  * we will be leaving hooks that could cause
2149                  * oopsen laying around.
2150                  */
2151                 if (unregister_framebuffer (info))
2152                         printk (KERN_WARNING "savagefb: danger danger! "
2153                                 "Oopsen imminent!\n");
2154
2155 #ifdef CONFIG_FB_SAVAGE_I2C
2156                 savagefb_delete_i2c_busses(info);
2157 #endif
2158                 fb_alloc_cmap (&info->cmap, 0, 0);
2159                 savage_unmap_video (info);
2160                 savage_unmap_mmio (info);
2161                 kfree(info->pixmap.addr);
2162                 pci_release_regions(dev);
2163                 framebuffer_release(info);
2164
2165                 /*
2166                  * Ensure that the driver data is no longer
2167                  * valid.
2168                  */
2169                 pci_set_drvdata(dev, NULL);
2170         }
2171 }
2172
2173 static int savagefb_suspend (struct pci_dev* dev, pm_message_t state)
2174 {
2175         struct fb_info *info =
2176                 (struct fb_info *)pci_get_drvdata(dev);
2177         struct savagefb_par *par = (struct savagefb_par *)info->par;
2178
2179         DBG("savagefb_suspend");
2180
2181
2182         par->pm_state = state.event;
2183
2184         /*
2185          * For PM_EVENT_FREEZE, do not power down so the console
2186          * can remain active.
2187          */
2188         if (state.event == PM_EVENT_FREEZE) {
2189                 dev->dev.power.power_state = state;
2190                 return 0;
2191         }
2192
2193         acquire_console_sem();
2194         fb_set_suspend(info, 1);
2195
2196         if (info->fbops->fb_sync)
2197                 info->fbops->fb_sync(info);
2198
2199         savagefb_blank(FB_BLANK_POWERDOWN, info);
2200         savage_disable_mmio(par);
2201         pci_save_state(dev);
2202         pci_disable_device(dev);
2203         pci_set_power_state(dev, pci_choose_state(dev, state));
2204         release_console_sem();
2205
2206         return 0;
2207 }
2208
2209 static int savagefb_resume (struct pci_dev* dev)
2210 {
2211         struct fb_info *info =
2212                 (struct fb_info *)pci_get_drvdata(dev);
2213         struct savagefb_par *par = (struct savagefb_par *)info->par;
2214         int cur_state = par->pm_state;
2215
2216         DBG("savage_resume");
2217
2218         par->pm_state = PM_EVENT_ON;
2219
2220         /*
2221          * The adapter was not powered down coming back from a
2222          * PM_EVENT_FREEZE.
2223          */
2224         if (cur_state == PM_EVENT_FREEZE) {
2225                 pci_set_power_state(dev, PCI_D0);
2226                 return 0;
2227         }
2228
2229         acquire_console_sem();
2230
2231         pci_set_power_state(dev, PCI_D0);
2232         pci_restore_state(dev);
2233
2234         if(pci_enable_device(dev))
2235                 DBG("err");
2236
2237         pci_set_master(dev);
2238         savage_enable_mmio(par);
2239         savage_init_hw(par);
2240         savagefb_set_par (info);
2241         savagefb_blank(FB_BLANK_UNBLANK, info);
2242         fb_set_suspend (info, 0);
2243         release_console_sem();
2244
2245         return 0;
2246 }
2247
2248
2249 static struct pci_device_id savagefb_devices[] __devinitdata = {
2250         {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX128,
2251          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
2252
2253         {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX64,
2254          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
2255
2256         {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX64C,
2257          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
2258
2259         {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX128SDR,
2260          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
2261
2262         {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX128DDR,
2263          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
2264
2265         {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX64SDR,
2266          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
2267
2268         {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX64DDR,
2269          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
2270
2271         {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IXCSDR,
2272          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
2273
2274         {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IXCDDR,
2275          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
2276
2277         {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE4,
2278          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE4},
2279
2280         {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE3D,
2281          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE3D},
2282
2283         {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE3D_MV,
2284          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE3D_MV},
2285
2286         {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE2000,
2287          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE2000},
2288
2289         {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_MX_MV,
2290          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_MX_MV},
2291
2292         {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_MX,
2293          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_MX},
2294
2295         {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_IX_MV,
2296          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_IX_MV},
2297
2298         {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_IX,
2299          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_IX},
2300
2301         {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_PM,
2302          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_PM},
2303
2304         {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_KM,
2305          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_KM},
2306
2307         {PCI_VENDOR_ID_S3, PCI_CHIP_S3TWISTER_P,
2308          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_S3TWISTER_P},
2309
2310         {PCI_VENDOR_ID_S3, PCI_CHIP_S3TWISTER_K,
2311          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_S3TWISTER_K},
2312
2313         {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_DDR,
2314          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_DDR},
2315
2316         {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_DDRK,
2317          PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_DDRK},
2318
2319         {0, 0, 0, 0, 0, 0, 0}
2320 };
2321
2322 MODULE_DEVICE_TABLE(pci, savagefb_devices);
2323
2324 static struct pci_driver savagefb_driver = {
2325         .name =     "savagefb",
2326         .id_table = savagefb_devices,
2327         .probe =    savagefb_probe,
2328         .suspend =  savagefb_suspend,
2329         .resume =   savagefb_resume,
2330         .remove =   __devexit_p(savagefb_remove)
2331 };
2332
2333 /* **************************** exit-time only **************************** */
2334
2335 static void __exit savage_done (void)
2336 {
2337         DBG("savage_done");
2338         pci_unregister_driver (&savagefb_driver);
2339 }
2340
2341
2342 /* ************************* init in-kernel code ************************** */
2343
2344 static int __init savagefb_setup(char *options)
2345 {
2346 #ifndef MODULE
2347         char *this_opt;
2348
2349         if (!options || !*options)
2350                 return 0;
2351
2352         while ((this_opt = strsep(&options, ",")) != NULL) {
2353                 mode_option = this_opt;
2354         }
2355 #endif /* !MODULE */
2356         return 0;
2357 }
2358
2359 static int __init savagefb_init(void)
2360 {
2361         char *option;
2362
2363         DBG("savagefb_init");
2364
2365         if (fb_get_options("savagefb", &option))
2366                 return -ENODEV;
2367
2368         savagefb_setup(option);
2369         return pci_register_driver (&savagefb_driver);
2370
2371 }
2372
2373 module_init(savagefb_init);
2374 module_exit(savage_done);
2375
2376 module_param(mode_option, charp, 0);
2377 MODULE_PARM_DESC(mode_option, "Specify initial video mode");