sky2: Fix suspend/hibernation/shutdown regression with WOL enabled (rev. 2)
[linux-2.6] / drivers / video / cyblafb.c
1 /*
2  * Frame buffer driver for Trident Cyberblade/i1 graphics core
3  *
4  * Copyright 2005 Knut Petersen <Knut_Petersen@t-online.de>
5  *
6  * CREDITS:
7  *      tridentfb.c by Jani Monoses
8  *      see files above for further credits
9  *
10  */
11
12 #define CYBLAFB_DEBUG 0
13 #define CYBLAFB_KD_GRAPHICS_QUIRK 1
14
15 #define CYBLAFB_PIXMAPSIZE 8192
16
17 #include <linux/module.h>
18 #include <linux/string.h>
19 #include <linux/fb.h>
20 #include <linux/init.h>
21 #include <linux/pci.h>
22 #include <asm/types.h>
23 #include <video/cyblafb.h>
24
25 #define VERSION "0.62"
26
27 struct cyblafb_par {
28         u32 pseudo_pal[16];
29         struct fb_ops ops;
30 };
31
32 static struct fb_fix_screeninfo cyblafb_fix __devinitdata = {
33         .id = "CyBla",
34         .type = FB_TYPE_PACKED_PIXELS,
35         .xpanstep = 1,
36         .ypanstep = 1,
37         .ywrapstep = 1,
38         .visual = FB_VISUAL_PSEUDOCOLOR,
39         .accel = FB_ACCEL_NONE,
40 };
41
42 static char *mode __devinitdata = NULL;
43 static int bpp __devinitdata = 8;
44 static int ref __devinitdata = 75;
45 static int fp __devinitdata;
46 static int crt __devinitdata;
47 static int memsize __devinitdata;
48
49 static int basestride;
50 static int vesafb;
51 static int nativex;
52 static int center;
53 static int stretch;
54 static int pciwb = 1;
55 static int pcirb = 1;
56 static int pciwr = 1;
57 static int pcirr = 1;
58 static int disabled;
59 static int verbosity;
60 static int displaytype;
61
62 static void __iomem *io_virt;   // iospace virtual memory address
63
64 module_param(mode, charp, 0);
65 module_param(bpp, int, 0);
66 module_param(ref, int, 0);
67 module_param(fp, int, 0);
68 module_param(crt, int, 0);
69 module_param(nativex, int, 0);
70 module_param(center, int, 0);
71 module_param(stretch, int, 0);
72 module_param(pciwb, int, 0);
73 module_param(pcirb, int, 0);
74 module_param(pciwr, int, 0);
75 module_param(pcirr, int, 0);
76 module_param(memsize, int, 0);
77 module_param(verbosity, int, 0);
78
79 //=========================================
80 //
81 // Well, we have to fix the upper layers.
82 // Until this has been done, we work around
83 // the bugs.
84 //
85 //=========================================
86
87 #if (CYBLAFB_KD_GRAPHICS_QUIRK && CYBLAFB_DEBUG)
88         if (disabled) { \
89                 printk("********\n");\
90                 dump_stack();\
91                 return val;\
92         }
93
94 #elif CYBLAFB_KD_GRAPHICS_QUIRK
95 #define KD_GRAPHICS_RETURN(val)\
96         if (disabled) {\
97                 return val;\
98         }
99 #else
100 #define KD_GRAPHICS_RETURN(val)
101 #endif
102
103 //=========================================
104 //
105 // Port access macros for memory mapped io
106 //
107 //=========================================
108
109 #define out8(r, v) writeb(v, io_virt + r)
110 #define out32(r, v) writel(v, io_virt + r)
111 #define in8(r) readb(io_virt + r)
112 #define in32(r) readl(io_virt + r)
113
114 //======================================
115 //
116 // Hardware access inline functions
117 //
118 //======================================
119
120 static inline u8 read3X4(u32 reg)
121 {
122         out8(0x3D4, reg);
123         return in8(0x3D5);
124 }
125
126 static inline u8 read3C4(u32 reg)
127 {
128         out8(0x3C4, reg);
129         return in8(0x3C5);
130 }
131
132 static inline u8 read3CE(u32 reg)
133 {
134         out8(0x3CE, reg);
135         return in8(0x3CF);
136 }
137
138 static inline void write3X4(u32 reg, u8 val)
139 {
140         out8(0x3D4, reg);
141         out8(0x3D5, val);
142 }
143
144 static inline void write3C4(u32 reg, u8 val)
145 {
146         out8(0x3C4, reg);
147         out8(0x3C5, val);
148 }
149
150 static inline void write3CE(u32 reg, u8 val)
151 {
152         out8(0x3CE, reg);
153         out8(0x3CF, val);
154 }
155
156 static inline void write3C0(u32 reg, u8 val)
157 {
158         in8(0x3DA);             // read to reset index
159         out8(0x3C0, reg);
160         out8(0x3C0, val);
161 }
162
163 //=================================================
164 //
165 // Enable memory mapped io and unprotect registers
166 //
167 //=================================================
168
169 static void enable_mmio(void)
170 {
171         u8 tmp;
172
173         outb(0x0B, 0x3C4);
174         inb(0x3C5);             // Set NEW mode
175         outb(SR0E, 0x3C4);      // write enable a lot of extended ports
176         outb(0x80, 0x3C5);
177
178         outb(SR11, 0x3C4);      // write enable those extended ports that
179         outb(0x87, 0x3C5);      // are not affected by SR0E_New
180
181         outb(CR1E, 0x3d4);      // clear write protect bit for port 0x3c2
182         tmp = inb(0x3d5) & 0xBF;
183         outb(CR1E, 0x3d4);
184         outb(tmp, 0x3d5);
185
186         outb(CR39, 0x3D4);
187         outb(inb(0x3D5) | 0x01, 0x3D5); // Enable mmio
188 }
189
190 //=================================================
191 //
192 // Set pixel clock VCLK1
193 // - multipliers set elswhere
194 // - freq in units of 0.01 MHz
195 //
196 // Hardware bug: SR18 >= 250 is broken for the
197 //               cyberblade/i1
198 //
199 //=================================================
200
201 static void set_vclk(struct cyblafb_par *par, int freq)
202 {
203         u32 m, n, k;
204         int f, fi, d, di;
205         u8 lo = 0, hi = 0;
206
207         d = 2000;
208         k = freq >= 10000 ? 0 : freq >= 5000 ? 1 : freq >= 2500 ? 2 : 3;
209         for (m = 0; m < 64; m++)
210                 for (n = 0; n < 250; n++) {
211                         fi = (int)(((5864727 * (n + 8)) /
212                                     ((m + 2) * (1 << k))) >> 12);
213                         if ((di = abs(fi - freq)) < d) {
214                                 d = di;
215                                 f = fi;
216                                 lo = (u8) n;
217                                 hi = (u8) ((k << 6) | m);
218                         }
219                 }
220         write3C4(SR19, hi);
221         write3C4(SR18, lo);
222         if (verbosity > 0)
223                 output("pixclock = %d.%02d MHz, k/m/n %x %x %x\n",
224                        freq / 100, freq % 100, (hi & 0xc0) >> 6, hi & 0x3f, lo);
225 }
226
227 //================================================
228 //
229 // Cyberblade specific Graphics Engine (GE) setup
230 //
231 //================================================
232
233 static void cyblafb_setup_GE(int pitch, int bpp)
234 {
235         KD_GRAPHICS_RETURN();
236
237         switch (bpp) {
238         case 8:
239                 basestride = ((pitch >> 3) << 20) | (0 << 29);
240                 break;
241         case 15:
242                 basestride = ((pitch >> 3) << 20) | (5 << 29);
243                 break;
244         case 16:
245                 basestride = ((pitch >> 3) << 20) | (1 << 29);
246                 break;
247         case 24:
248         case 32:
249                 basestride = ((pitch >> 3) << 20) | (2 << 29);
250                 break;
251         }
252
253         write3X4(CR36, 0x90);   // reset GE
254         write3X4(CR36, 0x80);   // enable GE
255         out32(GE24, 1 << 7);    // reset all GE pointers by toggling
256         out32(GE24, 0);         //   d7 of GE24
257         write3X4(CR2D, 0x00);   // GE Timinigs, no delays
258         out32(GE6C, 0);         // Pattern and Style, p 129, ok
259 }
260
261 //=====================================================================
262 //
263 // Cyberblade specific syncing
264 //
265 //   A timeout might be caused by disabled mmio.
266 //   Cause:
267 //     - bit CR39 & 1 == 0 upon return, X trident driver bug
268 //     - kdm bug (KD_GRAPHICS not set on first switch)
269 //     - kernel design flaw (it believes in the correctness
270 //       of kdm/X
271 //   First we try to sync ignoring that problem, as most of the
272 //   time that will succeed immediately and the enable_mmio()
273 //   would only degrade performance.
274 //
275 //=====================================================================
276
277 static int cyblafb_sync(struct fb_info *info)
278 {
279         u32 status, i = 100000;
280
281         KD_GRAPHICS_RETURN(0);
282
283         while (((status = in32(GE20)) & 0xFe800000) && i != 0)
284                 i--;
285
286         if (i == 0) {
287                 enable_mmio();
288                 i = 1000000;
289                 while (((status = in32(GE20)) & 0xFA800000) && i != 0)
290                         i--;
291                 if (i == 0) {
292                         output("GE Timeout, status: %x\n", status);
293                         if (status & 0x80000000)
294                                 output("Bresenham Engine : Busy\n");
295                         if (status & 0x40000000)
296                                 output("Setup Engine     : Busy\n");
297                         if (status & 0x20000000)
298                                 output("SP / DPE         : Busy\n");
299                         if (status & 0x10000000)
300                                 output("Memory Interface : Busy\n");
301                         if (status & 0x08000000)
302                                 output("Com Lst Proc     : Busy\n");
303                         if (status & 0x04000000)
304                                 output("Block Write      : Busy\n");
305                         if (status & 0x02000000)
306                                 output("Command Buffer   : Full\n");
307                         if (status & 0x01000000)
308                                 output("RESERVED         : Busy\n");
309                         if (status & 0x00800000)
310                                 output("PCI Write Buffer : Busy\n");
311                         cyblafb_setup_GE(info->var.xres,
312                                          info->var.bits_per_pixel);
313                 }
314         }
315
316         return 0;
317 }
318
319 //==============================
320 //
321 // Cyberblade specific fillrect
322 //
323 //==============================
324
325 static void cyblafb_fillrect(struct fb_info *info, const struct fb_fillrect *fr)
326 {
327         u32 bpp = info->var.bits_per_pixel, col, desty, height;
328
329         KD_GRAPHICS_RETURN();
330
331         switch (bpp) {
332         default:
333         case 8:
334                 col = fr->color;
335                 col |= col << 8;
336                 col |= col << 16;
337                 break;
338         case 16:
339                 col = ((u32 *) (info->pseudo_palette))[fr->color];
340                 col |= col << 16;
341                 break;
342         case 32:
343                 col = ((u32 *) (info->pseudo_palette))[fr->color];
344                 break;
345         }
346
347         desty = fr->dy;
348         height = fr->height;
349         while (height) {
350                 out32(GEB8, basestride | ((desty * info->var.xres_virtual *
351                                            bpp) >> 6));
352                 out32(GE60, col);
353                 out32(GE48, fr->rop ? 0x66 : ROP_S);
354                 out32(GE44, 0x20000000 | 1 << 19 | 1 << 4 | 2 << 2);
355                 out32(GE08, point(fr->dx, 0));
356                 out32(GE0C, point(fr->dx + fr->width - 1,
357                                   height > 4096 ? 4095 : height - 1));
358                 if (likely(height <= 4096))
359                         return;
360                 desty += 4096;
361                 height -= 4096;
362         }
363 }
364
365 //================================================
366 //
367 // Cyberblade specific copyarea
368 //
369 // This function silently assumes that it never
370 // will be called with width or height exceeding
371 // 4096.
372 //
373 //================================================
374
375 static void cyblafb_copyarea(struct fb_info *info, const struct fb_copyarea *ca)
376 {
377         u32 s1, s2, d1, d2, direction;
378
379         KD_GRAPHICS_RETURN();
380
381         s1 = point(ca->sx, 0);
382         s2 = point(ca->sx + ca->width - 1, ca->height - 1);
383         d1 = point(ca->dx, 0);
384         d2 = point(ca->dx + ca->width - 1, ca->height - 1);
385
386         if ((ca->sy > ca->dy) || ((ca->sy == ca->dy) && (ca->sx > ca->dx)))
387                 direction = 0;
388         else
389                 direction = 2;
390
391         out32(GEB8, basestride | ((ca->dy * info->var.xres_virtual *
392                                    info->var.bits_per_pixel) >> 6));
393         out32(GEC8, basestride | ((ca->sy * info->var.xres_virtual *
394                                    info->var.bits_per_pixel) >> 6));
395         out32(GE44, 0xa0000000 | 1 << 19 | 1 << 2 | direction);
396         out32(GE00, direction ? s2 : s1);
397         out32(GE04, direction ? s1 : s2);
398         out32(GE08, direction ? d2 : d1);
399         out32(GE0C, direction ? d1 : d2);
400 }
401
402 //=======================================================================
403 //
404 // Cyberblade specific imageblit
405 //
406 // Accelerated for the most usual case, blitting 1 - bit deep
407 // character images. Everything else is passed to the generic imageblit
408 // unless it is so insane that it is better to printk an alert.
409 //
410 // Hardware bug: _Never_ blit across pixel column 2048, that will lock
411 // the system. We split those blit requests into three blitting
412 // operations.
413 //
414 //=======================================================================
415
416 static void cyblafb_imageblit(struct fb_info *info,
417                               const struct fb_image *image)
418 {
419         u32 fgcol, bgcol;
420         u32 *pd = (u32 *) image->data;
421         u32 bpp = info->var.bits_per_pixel;
422
423         KD_GRAPHICS_RETURN();
424
425         // Used only for drawing the penguine (image->depth > 1)
426         if (image->depth != 1) {
427                 cfb_imageblit(info, image);
428                 return;
429         }
430         // That should never happen, but it would be fatal
431         if (image->width == 0 || image->height == 0) {
432                 output("imageblit: width/height 0 detected\n");
433                 return;
434         }
435
436         if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
437             info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
438                 fgcol = ((u32 *) (info->pseudo_palette))[image->fg_color];
439                 bgcol = ((u32 *) (info->pseudo_palette))[image->bg_color];
440         } else {
441                 fgcol = image->fg_color;
442                 bgcol = image->bg_color;
443         }
444
445         switch (bpp) {
446         case 8:
447                 fgcol |= fgcol << 8;
448                 bgcol |= bgcol << 8;
449         case 16:
450                 fgcol |= fgcol << 16;
451                 bgcol |= bgcol << 16;
452         default:
453                 break;
454         }
455
456         out32(GEB8, basestride | ((image->dy * info->var.xres_virtual *
457                                    bpp) >> 6));
458         out32(GE60, fgcol);
459         out32(GE64, bgcol);
460
461         if (!(image->dx < 2048 && (image->dx + image->width - 1) >= 2048)) {
462                 u32 dds = ((image->width + 31) >> 5) * image->height;
463                 out32(GE44, 0xa0000000 | 1 << 20 | 1 << 19);
464                 out32(GE08, point(image->dx, 0));
465                 out32(GE0C, point(image->dx + image->width - 1,
466                                   image->height - 1));
467                 while (dds--)
468                         out32(GE9C, *pd++);
469         } else {
470                 int i, j;
471                 u32 ddstotal = (image->width + 31) >> 5;
472                 u32 ddsleft = (2048 - image->dx + 31) >> 5;
473                 u32 skipleft = ddstotal - ddsleft;
474
475                 out32(GE44, 0xa0000000 | 1 << 20 | 1 << 19);
476                 out32(GE08, point(image->dx, 0));
477                 out32(GE0C, point(2048 - 1, image->height - 1));
478                 for (i = 0; i < image->height; i++) {
479                         for (j = 0; j < ddsleft; j++)
480                                 out32(GE9C, *pd++);
481                         pd += skipleft;
482                 }
483
484                 if (image->dx % 32) {
485                         out32(GE44, 0xa0000000 | 1 << 20 | 1 << 19);
486                         out32(GE08, point(2048, 0));
487                         if (image->width > ddsleft << 5)
488                                 out32(GE0C, point(image->dx + (ddsleft << 5) -
489                                                   1, image->height - 1));
490                         else
491                                 out32(GE0C, point(image->dx + image->width - 1,
492                                                   image->height - 1));
493                         pd = ((u32 *) image->data) + ddstotal - skipleft - 1;
494                         for (i = 0; i < image->height; i++) {
495                                 out32(GE9C, swab32(swab32(*pd) << ((32 -
496                                             (image->dx & 31)) & 31)));
497                                 pd += ddstotal;
498                         }
499                 }
500
501                 if (skipleft) {
502                         out32(GE44, 0xa0000000 | 1 << 20 | 1 << 19);
503                         out32(GE08, point(image->dx + (ddsleft << 5), 0));
504                         out32(GE0C, point(image->dx + image->width - 1,
505                                           image->height - 1));
506                         pd = (u32 *) image->data;
507                         for (i = 0; i < image->height; i++) {
508                                 pd += ddsleft;
509                                 for (j = 0; j < skipleft; j++)
510                                         out32(GE9C, *pd++);
511                         }
512                 }
513         }
514 }
515
516 //==========================================================
517 //
518 // Check if video mode is acceptable. We change var->??? if
519 // video mode is slightly off or return error otherwise.
520 // info->??? must not be changed!
521 //
522 //==========================================================
523
524 static int cyblafb_check_var(struct fb_var_screeninfo *var,
525                              struct fb_info *info)
526 {
527         int bpp = var->bits_per_pixel;
528
529         //
530         // we try to support 8, 16, 24 and 32 bpp modes,
531         // default to 8
532         //
533         // there is a 24 bpp mode, but for now we change requests to 32 bpp
534         // (This is what tridentfb does ... will be changed in the future)
535         //
536         //
537         if (bpp % 8 != 0 || bpp < 8 || bpp > 32)
538                 bpp = 8;
539         if (bpp == 24)
540                 bpp = var->bits_per_pixel = 32;
541
542         //
543         // interlaced modes are broken, fail if one is requested
544         //
545         if (var->vmode & FB_VMODE_INTERLACED)
546                 return -EINVAL;
547
548         //
549         // fail if requested resolution is higher than physical
550         // flatpanel resolution
551         //
552         if ((displaytype == DISPLAY_FP) && nativex && var->xres > nativex)
553                 return -EINVAL;
554
555         //
556         // we do not allow vclk to exceed 230 MHz. If the requested
557         // vclk is too high, we default to 200 MHz
558         //
559         if ((bpp == 32 ? 200000000 : 100000000) / var->pixclock > 23000)
560                 var->pixclock = (bpp == 32 ? 200000000 : 100000000) / 20000;
561
562         //
563         // enforce (h|v)sync_len limits
564         //
565         var->hsync_len &= ~7;
566         if(var->hsync_len > 248)
567                 var->hsync_len = 248;
568
569         var->vsync_len &= 15;
570
571         //
572         // Enforce horizontal and vertical hardware limits.
573         // 1600x1200 is mentioned as a maximum, but higher resolutions could
574         // work with slow refresh, small margins and short sync.
575         //
576         var->xres &= ~7;
577
578         if (((var->xres + var->left_margin + var->right_margin +
579                         var->hsync_len) > (bpp == 32 ? 2040 : 4088)) ||
580                         ((var->yres + var->upper_margin + var->lower_margin +
581                         var->vsync_len) > 2047))
582                 return -EINVAL;
583
584         if ((var->xres > 1600) || (var->yres > 1200))
585                 output("Mode %dx%d exceeds documented limits.\n",
586                                            var->xres, var->yres);
587         //
588         // try to be smart about (x|y)res_virtual problems.
589         //
590         if (var->xres > var->xres_virtual)
591                 var->xres_virtual = var->xres;
592         if (var->yres > var->yres_virtual)
593                 var->yres_virtual = var->yres;
594
595         if (bpp == 8 || bpp == 16) {
596                 if (var->xres_virtual > 4088)
597                         var->xres_virtual = 4088;
598         } else {
599                 if (var->xres_virtual > 2040)
600                         var->xres_virtual = 2040;
601         }
602         var->xres_virtual &= ~7;
603         while (var->xres_virtual * var->yres_virtual * bpp / 8 >
604                info->fix.smem_len) {
605                 if (var->yres_virtual > var->yres)
606                         var->yres_virtual--;
607                 else if (var->xres_virtual > var->xres)
608                         var->xres_virtual -= 8;
609                 else
610                         return -EINVAL;
611         }
612
613         switch (bpp) {
614         case 8:
615                 var->red.offset = 0;
616                 var->green.offset = 0;
617                 var->blue.offset = 0;
618                 var->red.length = 6;
619                 var->green.length = 6;
620                 var->blue.length = 6;
621                 break;
622         case 16:
623                 var->red.offset = 11;
624                 var->green.offset = 5;
625                 var->blue.offset = 0;
626                 var->red.length = 5;
627                 var->green.length = 6;
628                 var->blue.length = 5;
629                 break;
630         case 32:
631                 var->red.offset = 16;
632                 var->green.offset = 8;
633                 var->blue.offset = 0;
634                 var->red.length = 8;
635                 var->green.length = 8;
636                 var->blue.length = 8;
637                 break;
638         default:
639                 return -EINVAL;
640         }
641
642         return 0;
643 }
644
645 //=====================================================================
646 //
647 // Pan the display
648 //
649 // The datasheets defines crt start address to be 20 bits wide and
650 // to be programmed to CR0C, CR0D, CR1E and CR27. Actually there is
651 // CR2B[5] as an undocumented extension bit. Epia BIOS 2.07 does use
652 // it, so it is also safe to be used here. BTW: datasheet CR0E on page
653 // 90 really is CR1E, the real CRE is documented on page 72.
654 //
655 // BUT:
656 //
657 // As of internal version 0.60 we do not use vga panning any longer.
658 // Vga panning did not allow us the use of all available video memory
659 // and thus prevented ywrap scrolling. We do use the "right view"
660 // register now.
661 //
662 //
663 //=====================================================================
664
665 static int cyblafb_pan_display(struct fb_var_screeninfo *var,
666                                struct fb_info *info)
667 {
668         KD_GRAPHICS_RETURN(0);
669
670         info->var.xoffset = var->xoffset;
671         info->var.yoffset = var->yoffset;
672         out32(GE10, 0x80000000 | ((var->xoffset + (var->yoffset *
673                     var->xres_virtual)) * var->bits_per_pixel / 32));
674         return 0;
675 }
676
677 //============================================
678 //
679 // This will really help in case of a bug ...
680 // dump most gaphics core registers.
681 //
682 //============================================
683
684 static void regdump(struct cyblafb_par *par)
685 {
686         int i;
687
688         if (verbosity < 2)
689                 return;
690
691         printk("\n");
692         for (i = 0; i <= 0xff; i++) {
693                 outb(i, 0x3d4);
694                 printk("CR%02x=%02x ", i, inb(0x3d5));
695                 if (i % 16 == 15)
696                         printk("\n");
697         }
698
699         outb(0x30, 0x3ce);
700         outb(inb(0x3cf) | 0x40, 0x3cf);
701         for (i = 0; i <= 0x1f; i++) {
702                 if (i == 0 || (i > 2 && i < 8) || i == 0x10 || i == 0x11
703                     || i == 0x16) {
704                         outb(i, 0x3d4);
705                         printk("CR%02x=%02x ", i, inb(0x3d5));
706                 } else
707                         printk("------- ");
708                 if (i % 16 == 15)
709                         printk("\n");
710         }
711         outb(0x30, 0x3ce);
712         outb(inb(0x3cf) & 0xbf, 0x3cf);
713
714         printk("\n");
715         for (i = 0; i <= 0x7f; i++) {
716                 outb(i, 0x3ce);
717                 printk("GR%02x=%02x ", i, inb(0x3cf));
718                 if (i % 16 == 15)
719                         printk("\n");
720         }
721
722         printk("\n");
723         for (i = 0; i <= 0xff; i++) {
724                 outb(i, 0x3c4);
725                 printk("SR%02x=%02x ", i, inb(0x3c5));
726                 if (i % 16 == 15)
727                         printk("\n");
728         }
729
730         printk("\n");
731         for (i = 0; i <= 0x1F; i++) {
732                 inb(0x3da);     // next access is index!
733                 outb(i, 0x3c0);
734                 printk("AR%02x=%02x ", i, inb(0x3c1));
735                 if (i % 16 == 15)
736                         printk("\n");
737         }
738         printk("\n");
739
740         inb(0x3DA);             // reset internal flag to 3c0 index
741         outb(0x20, 0x3C0);      // enable attr
742
743         return;
744 }
745
746 //=======================================================================
747 //
748 // Save State
749 //
750 // This function is called while a switch to KD_TEXT is in progress,
751 // before any of the other functions are called.
752 //
753 //=======================================================================
754
755 static void cyblafb_save_state(struct fb_info *info)
756 {
757         struct cyblafb_par *par = info->par;
758         if (verbosity > 0)
759                 output("Switching to KD_TEXT\n");
760         disabled = 0;
761         regdump(par);
762         enable_mmio();
763         return;
764 }
765
766 //=======================================================================
767 //
768 // Restore State
769 //
770 // This function is called while a switch to KD_GRAPHICS is in progress,
771 // We have to turn on vga style panning registers again because the
772 // trident driver of X does not know about GE10.
773 //
774 //=======================================================================
775
776 static void cyblafb_restore_state(struct fb_info *info)
777 {
778         if (verbosity > 0)
779                 output("Switching to KD_GRAPHICS\n");
780         out32(GE10, 0);
781         disabled = 1;
782         return;
783 }
784
785 //======================================
786 //
787 // Set hardware to requested video mode
788 //
789 //======================================
790
791 static int cyblafb_set_par(struct fb_info *info)
792 {
793         struct cyblafb_par *par = info->par;
794         u32 htotal, hdispend, hsyncstart, hsyncend, hblankstart,
795             hblankend, preendfetch, vtotal, vdispend, vsyncstart,
796             vsyncend, vblankstart, vblankend;
797         struct fb_var_screeninfo *var = &info->var;
798         int bpp = var->bits_per_pixel;
799         int i;
800
801         KD_GRAPHICS_RETURN(0);
802
803         if (verbosity > 0)
804                 output("Switching to new mode: "
805                        "fbset -g %d %d %d %d %d -t %d %d %d %d %d %d %d\n",
806                        var->xres, var->yres, var->xres_virtual,
807                        var->yres_virtual, var->bits_per_pixel, var->pixclock,
808                        var->left_margin, var->right_margin, var->upper_margin,
809                        var->lower_margin, var->hsync_len, var->vsync_len);
810
811         htotal = (var->xres + var->left_margin + var->right_margin +
812                   var->hsync_len) / 8 - 5;
813         hdispend = var->xres / 8 - 1;
814         hsyncstart = (var->xres + var->right_margin) / 8;
815         hsyncend = var->hsync_len / 8;
816         hblankstart = hdispend + 1;
817         hblankend = htotal + 3; // should be htotal + 5, bios does it this way
818         preendfetch = ((var->xres >> 3) + 1) * ((bpp + 1) >> 3);
819
820         vtotal = var->yres + var->upper_margin + var->lower_margin +
821                                                         var->vsync_len - 2;
822         vdispend = var->yres - 1;
823         vsyncstart = var->yres + var->lower_margin;
824         vblankstart = var->yres;
825         vblankend = vtotal; // should be vtotal + 2, but bios does it this way
826         vsyncend = var->vsync_len;
827
828         enable_mmio();          // necessary! ... check X ...
829
830         write3X4(CR11, read3X4(CR11) & 0x7F);   // unlock cr00 .. cr07
831
832         write3CE(GR30, 8);
833
834         if ((displaytype == DISPLAY_FP) && var->xres < nativex) {
835
836                 // stretch or center ?
837
838                 out8(0x3C2, 0xEB);
839
840                 write3CE(GR30, read3CE(GR30) | 0x81);   // shadow mode on
841
842                 if (center) {
843                         write3CE(GR52, (read3CE(GR52) & 0x7C) | 0x80);
844                         write3CE(GR53, (read3CE(GR53) & 0x7C) | 0x80);
845                 } else if (stretch) {
846                         write3CE(GR5D, 0);
847                         write3CE(GR52, (read3CE(GR52) & 0x7C) | 1);
848                         write3CE(GR53, (read3CE(GR53) & 0x7C) | 1);
849                 }
850
851         } else {
852                 out8(0x3C2, 0x2B);
853                 write3CE(GR30, 8);
854         }
855
856         //
857         // Setup CRxx regs
858         //
859
860         write3X4(CR00, htotal & 0xFF);
861         write3X4(CR01, hdispend & 0xFF);
862         write3X4(CR02, hblankstart & 0xFF);
863         write3X4(CR03, hblankend & 0x1F);
864         write3X4(CR04, hsyncstart & 0xFF);
865         write3X4(CR05, (hsyncend & 0x1F) | ((hblankend & 0x20) << 2));
866         write3X4(CR06, vtotal & 0xFF);
867         write3X4(CR07, (vtotal & 0x100) >> 8 |
868                        (vdispend & 0x100) >> 7 |
869                        (vsyncstart & 0x100) >> 6 |
870                        (vblankstart & 0x100) >> 5 |
871                        0x10 |
872                        (vtotal & 0x200) >> 4 |
873                        (vdispend & 0x200) >> 3 | (vsyncstart & 0x200) >> 2);
874         write3X4(CR08, 0);
875         write3X4(CR09, (vblankstart & 0x200) >> 4 | 0x40 |      // FIX !!!
876                        ((info->var.vmode & FB_VMODE_DOUBLE) ? 0x80 : 0));
877         write3X4(CR0A, 0);      // Init to some reasonable default
878         write3X4(CR0B, 0);      // Init to some reasonable default
879         write3X4(CR0C, 0);      // Offset 0
880         write3X4(CR0D, 0);      // Offset 0
881         write3X4(CR0E, 0);      // Init to some reasonable default
882         write3X4(CR0F, 0);      // Init to some reasonable default
883         write3X4(CR10, vsyncstart & 0xFF);
884         write3X4(CR11, (vsyncend & 0x0F));
885         write3X4(CR12, vdispend & 0xFF);
886         write3X4(CR13, ((info->var.xres_virtual * bpp) / (4 * 16)) & 0xFF);
887         write3X4(CR14, 0x40);   // double word mode
888         write3X4(CR15, vblankstart & 0xFF);
889         write3X4(CR16, vblankend & 0xFF);
890         write3X4(CR17, 0xE3);
891         write3X4(CR18, 0xFF);
892         //       CR19: needed for interlaced modes ... ignore it for now
893         write3X4(CR1A, 0x07);   // Arbitration Control Counter 1
894         write3X4(CR1B, 0x07);   // Arbitration Control Counter 2
895         write3X4(CR1C, 0x07);   // Arbitration Control Counter 3
896         write3X4(CR1D, 0x00);   // Don't know, doesn't hurt ; -)
897         write3X4(CR1E, (info->var.vmode & FB_VMODE_INTERLACED) ? 0x84 : 0x80);
898         //       CR1F: do not set, contains BIOS info about memsize
899         write3X4(CR20, 0x20);   // enabe wr buf, disable 16bit planar mode
900         write3X4(CR21, 0x20);   // enable linear memory access
901         //       CR22: RO cpu latch readback
902         //       CR23: ???
903         //       CR24: RO AR flag state
904         //       CR25: RAMDAC rw timing, pclk buffer tristate control ????
905         //       CR26: ???
906         write3X4(CR27, (vdispend & 0x400) >> 6 |
907                        (vsyncstart & 0x400) >> 5 |
908                        (vblankstart & 0x400) >> 4 |
909                        (vtotal & 0x400) >> 3 |
910                        0x8);
911         //       CR28: ???
912         write3X4(CR29, (read3X4(CR29) & 0xCF) | ((((info->var.xres_virtual *
913                         bpp) / (4 * 16)) & 0x300) >> 4));
914         write3X4(CR2A, read3X4(CR2A) | 0x40);
915         write3X4(CR2B, (htotal & 0x100) >> 8 |
916                        (hdispend & 0x100) >> 7 |
917                        // (0x00 & 0x100) >> 6 |   hinterlace para bit 8 ???
918                        (hsyncstart & 0x100) >> 5 |
919                        (hblankstart & 0x100) >> 4);
920         //       CR2C: ???
921         //       CR2D: initialized in cyblafb_setup_GE()
922         write3X4(CR2F, 0x92);   // conservative, better signal quality
923         //       CR30: reserved
924         //       CR31: reserved
925         //       CR32: reserved
926         //       CR33: reserved
927         //       CR34: disabled in CR36
928         //       CR35: disabled in CR36
929         //       CR36: initialized in cyblafb_setup_GE
930         //       CR37: i2c, ignore for now
931         write3X4(CR38, (bpp == 8) ? 0x00 :      //
932                        (bpp == 16) ? 0x05 :     // highcolor
933                        (bpp == 24) ? 0x29 :     // packed 24bit truecolor
934                        (bpp == 32) ? 0x09 : 0); // truecolor, 16 bit pixelbus
935         write3X4(CR39, 0x01 |   // MMIO enable
936                        (pcirb ? 0x02 : 0) |     // pci read burst enable
937                        (pciwb ? 0x04 : 0));     // pci write burst enable
938         write3X4(CR55, 0x1F | // pci clocks * 2 for STOP# during 1st data phase
939                        (pcirr ? 0x40 : 0) |     // pci read retry enable
940                        (pciwr ? 0x80 : 0));     // pci write retry enable
941         write3X4(CR56, preendfetch >> 8 < 2 ? (preendfetch >> 8 & 0x01) | 2
942                                             : 0);
943         write3X4(CR57, preendfetch >> 8 < 2 ? preendfetch & 0xff : 0);
944         write3X4(CR58, 0x82);   // Bios does this .... don't know more
945         //
946         // Setup SRxx regs
947         //
948         write3C4(SR00, 3);
949         write3C4(SR01, 1);      //set char clock 8 dots wide
950         write3C4(SR02, 0x0F);   //enable 4 maps needed in chain4 mode
951         write3C4(SR03, 0);      //no character map select
952         write3C4(SR04, 0x0E);   //memory mode: ext mem, even, chain4
953
954         out8(0x3C4, 0x0b);
955         in8(0x3C5);             // Set NEW mode
956         write3C4(SR0D, 0x00);   // test ... check
957
958         set_vclk(par, (bpp == 32 ? 200000000 : 100000000)
959                                         / info->var.pixclock);  //SR18, SR19
960
961         //
962         // Setup GRxx regs
963         //
964         write3CE(GR00, 0x00);   // test ... check
965         write3CE(GR01, 0x00);   // test ... check
966         write3CE(GR02, 0x00);   // test ... check
967         write3CE(GR03, 0x00);   // test ... check
968         write3CE(GR04, 0x00);   // test ... check
969         write3CE(GR05, 0x40);   // no CGA compat, allow 256 col
970         write3CE(GR06, 0x05);   // graphics mode
971         write3CE(GR07, 0x0F);   // planes?
972         write3CE(GR08, 0xFF);   // test ... check
973         write3CE(GR0F, (bpp == 32) ? 0x1A : 0x12); // vclk / 2 if 32bpp, chain4
974         write3CE(GR20, 0xC0);   // test ... check
975         write3CE(GR2F, 0xA0);   // PCLK = VCLK, no skew,
976
977         //
978         // Setup ARxx regs
979         //
980         for (i = 0; i < 0x10; i++)      // set AR00 .. AR0f
981                 write3C0(i, i);
982         write3C0(AR10, 0x41);   // graphics mode and support 256 color modes
983         write3C0(AR12, 0x0F);   // planes
984         write3C0(AR13, 0);      // horizontal pel panning
985         in8(0x3DA);             // reset internal flag to 3c0 index
986         out8(0x3C0, 0x20);      // enable attr
987
988         //
989         // Setup hidden RAMDAC command register
990         //
991         in8(0x3C8);             // these reads are
992         in8(0x3C6);             // necessary to
993         in8(0x3C6);             // unmask the RAMDAC
994         in8(0x3C6);             // command reg, otherwise
995         in8(0x3C6);             // we would write the pixelmask reg!
996         out8(0x3C6, (bpp == 8) ? 0x00 : // 256 colors
997              (bpp == 15) ? 0x10 :       //
998              (bpp == 16) ? 0x30 :       // hicolor
999              (bpp == 24) ? 0xD0 :       // truecolor
1000              (bpp == 32) ? 0xD0 : 0);   // truecolor
1001         in8(0x3C8);
1002
1003         //
1004         // GR31 is not mentioned in the datasheet
1005         //
1006         if (displaytype == DISPLAY_FP)
1007                 write3CE(GR31, (read3CE(GR31) & 0x8F) |
1008                          ((info->var.yres > 1024) ? 0x50 :
1009                           (info->var.yres > 768) ? 0x30 :
1010                           (info->var.yres > 600) ? 0x20 :
1011                           (info->var.yres > 480) ? 0x10 : 0));
1012
1013         info->fix.visual = (bpp == 8) ? FB_VISUAL_PSEUDOCOLOR
1014                                       : FB_VISUAL_TRUECOLOR;
1015         info->fix.line_length = info->var.xres_virtual * (bpp >> 3);
1016         info->cmap.len = (bpp == 8) ? 256 : 16;
1017
1018         //
1019         // init acceleration engine
1020         //
1021         cyblafb_setup_GE(info->var.xres_virtual, info->var.bits_per_pixel);
1022
1023         //
1024         // Set/clear flags to allow proper scroll mode selection.
1025         //
1026         if (var->xres == var->xres_virtual)
1027                 info->flags &= ~FBINFO_HWACCEL_XPAN;
1028         else
1029                 info->flags |= FBINFO_HWACCEL_XPAN;
1030
1031         if (var->yres == var->yres_virtual)
1032                 info->flags &= ~FBINFO_HWACCEL_YPAN;
1033         else
1034                 info->flags |= FBINFO_HWACCEL_YPAN;
1035
1036         if (info->fix.smem_len !=
1037             var->xres_virtual * var->yres_virtual * bpp / 8)
1038                 info->flags &= ~FBINFO_HWACCEL_YWRAP;
1039         else
1040                 info->flags |= FBINFO_HWACCEL_YWRAP;
1041
1042         regdump(par);
1043
1044         return 0;
1045 }
1046
1047 //========================
1048 //
1049 // Set one color register
1050 //
1051 //========================
1052
1053 static int cyblafb_setcolreg(unsigned regno, unsigned red, unsigned green,
1054                              unsigned blue, unsigned transp,
1055                              struct fb_info *info)
1056 {
1057         int bpp = info->var.bits_per_pixel;
1058
1059         KD_GRAPHICS_RETURN(0);
1060
1061         if (regno >= info->cmap.len)
1062                 return 1;
1063
1064         if (bpp == 8) {
1065                 out8(0x3C6, 0xFF);
1066                 out8(0x3C8, regno);
1067                 out8(0x3C9, red >> 10);
1068                 out8(0x3C9, green >> 10);
1069                 out8(0x3C9, blue >> 10);
1070
1071         } else if (regno < 16) {
1072                 if (bpp == 16)  // RGB 565
1073                         ((u32 *) info->pseudo_palette)[regno] =
1074                                 (red & 0xF800) |
1075                                 ((green & 0xFC00) >> 5) |
1076                                 ((blue & 0xF800) >> 11);
1077                 else if (bpp == 32)     // ARGB 8888
1078                         ((u32 *) info->pseudo_palette)[regno] =
1079                                 ((transp & 0xFF00) << 16) |
1080                                 ((red & 0xFF00) << 8) |
1081                                 ((green & 0xFF00)) | ((blue & 0xFF00) >> 8);
1082         }
1083
1084         return 0;
1085 }
1086
1087 //==========================================================
1088 //
1089 // Try blanking the screen. For flat panels it does nothing
1090 //
1091 //==========================================================
1092
1093 static int cyblafb_blank(int blank_mode, struct fb_info *info)
1094 {
1095         unsigned char PMCont, DPMSCont;
1096
1097         KD_GRAPHICS_RETURN(0);
1098
1099         if (displaytype == DISPLAY_FP)
1100                 return 0;
1101
1102         out8(0x83C8, 0x04);     // DPMS Control
1103         PMCont = in8(0x83C6) & 0xFC;
1104
1105         DPMSCont = read3CE(GR23) & 0xFC;
1106
1107         switch (blank_mode) {
1108         case FB_BLANK_UNBLANK:  // Screen: On, HSync: On, VSync: On
1109         case FB_BLANK_NORMAL:   // Screen: Off, HSync: On, VSync: On
1110                 PMCont |= 0x03;
1111                 DPMSCont |= 0x00;
1112                 break;
1113         case FB_BLANK_HSYNC_SUSPEND:    // Screen: Off, HSync: Off, VSync: On
1114                 PMCont |= 0x02;
1115                 DPMSCont |= 0x01;
1116                 break;
1117         case FB_BLANK_VSYNC_SUSPEND:    // Screen: Off, HSync: On, VSync: Off
1118                 PMCont |= 0x02;
1119                 DPMSCont |= 0x02;
1120                 break;
1121         case FB_BLANK_POWERDOWN:        // Screen: Off, HSync: Off, VSync: Off
1122                 PMCont |= 0x00;
1123                 DPMSCont |= 0x03;
1124                 break;
1125         }
1126
1127         write3CE(GR23, DPMSCont);
1128         out8(0x83C8, 4);
1129         out8(0x83C6, PMCont);
1130         //
1131         // let fbcon do a softblank for us
1132         //
1133         return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1134 }
1135
1136 static struct fb_ops cyblafb_ops __devinitdata = {
1137         .owner = THIS_MODULE,
1138         .fb_setcolreg = cyblafb_setcolreg,
1139         .fb_pan_display = cyblafb_pan_display,
1140         .fb_blank = cyblafb_blank,
1141         .fb_check_var = cyblafb_check_var,
1142         .fb_set_par = cyblafb_set_par,
1143         .fb_fillrect = cyblafb_fillrect,
1144         .fb_copyarea = cyblafb_copyarea,
1145         .fb_imageblit = cyblafb_imageblit,
1146         .fb_sync = cyblafb_sync,
1147         .fb_restore_state = cyblafb_restore_state,
1148         .fb_save_state = cyblafb_save_state,
1149 };
1150
1151 //==========================================================================
1152 //
1153 // getstartupmode() decides about the inital video mode
1154 //
1155 // There is no reason to use modedb, a lot of video modes there would
1156 // need altered timings to display correctly. So I decided that it is much
1157 // better to provide a limited optimized set of modes plus the option of
1158 // using the mode in effect at startup time (might be selected using the
1159 // vga=??? parameter). After that the user might use fbset to select any
1160 // mode he likes, check_var will not try to alter geometry parameters as
1161 // it would be necessary otherwise.
1162 //
1163 //==========================================================================
1164
1165 static int __devinit getstartupmode(struct fb_info *info)
1166 {
1167         u32 htotal, hdispend, hsyncstart, hsyncend, hblankstart, hblankend,
1168             vtotal, vdispend, vsyncstart, vsyncend, vblankstart, vblankend,
1169             cr00, cr01, cr02, cr03, cr04, cr05, cr2b,
1170             cr06, cr07, cr09, cr10, cr11, cr12, cr15, cr16, cr27,
1171             cr38, sr0d, sr18, sr19, gr0f, fi, pxclkdiv, vclkdiv, tmp, i;
1172
1173         struct modus {
1174                 int xres; int vxres; int yres; int vyres;
1175                 int bpp; int pxclk;
1176                 int left_margin; int right_margin;
1177                 int upper_margin; int lower_margin;
1178                 int hsync_len; int vsync_len;
1179         } modedb[5] = {
1180                 {
1181                 0, 2048, 0, 4096, 0, 0, 0, 0, 0, 0, 0, 0}, {
1182                 640, 2048, 480, 4096, 0, 0, -40, 24, 17, 0, 216, 3}, {
1183                 800, 2048, 600, 4096, 0, 0, 96, 24, 14, 0, 136, 11}, {
1184                 1024, 2048, 768, 4096, 0, 0, 144, 24, 29, 0, 120, 3}, {
1185                 1280, 2048, 1024, 4096, 0, 0, 232, 16, 39, 0, 160, 3}
1186         };
1187
1188         outb(0x00, 0x3d4); cr00 = inb(0x3d5);
1189         outb(0x01, 0x3d4); cr01 = inb(0x3d5);
1190         outb(0x02, 0x3d4); cr02 = inb(0x3d5);
1191         outb(0x03, 0x3d4); cr03 = inb(0x3d5);
1192         outb(0x04, 0x3d4); cr04 = inb(0x3d5);
1193         outb(0x05, 0x3d4); cr05 = inb(0x3d5);
1194         outb(0x06, 0x3d4); cr06 = inb(0x3d5);
1195         outb(0x07, 0x3d4); cr07 = inb(0x3d5);
1196         outb(0x09, 0x3d4); cr09 = inb(0x3d5);
1197         outb(0x10, 0x3d4); cr10 = inb(0x3d5);
1198         outb(0x11, 0x3d4); cr11 = inb(0x3d5);
1199         outb(0x12, 0x3d4); cr12 = inb(0x3d5);
1200         outb(0x15, 0x3d4); cr15 = inb(0x3d5);
1201         outb(0x16, 0x3d4); cr16 = inb(0x3d5);
1202         outb(0x27, 0x3d4); cr27 = inb(0x3d5);
1203         outb(0x2b, 0x3d4); cr2b = inb(0x3d5);
1204         outb(0x38, 0x3d4); cr38 = inb(0x3d5);
1205
1206         outb(0x0b, 0x3c4);
1207         inb(0x3c5);
1208
1209         outb(0x0d, 0x3c4); sr0d = inb(0x3c5);
1210         outb(0x18, 0x3c4); sr18 = inb(0x3c5);
1211         outb(0x19, 0x3c4); sr19 = inb(0x3c5);
1212         outb(0x0f, 0x3ce); gr0f = inb(0x3cf);
1213
1214         htotal = cr00 | (cr2b & 0x01) << 8;
1215         hdispend = cr01 | (cr2b & 0x02) << 7;
1216         hblankstart = cr02 | (cr2b & 0x10) << 4;
1217         hblankend = (cr03 & 0x1f) | (cr05 & 0x80) >> 2;
1218         hsyncstart = cr04 | (cr2b & 0x08) << 5;
1219         hsyncend = cr05 & 0x1f;
1220
1221         modedb[0].xres = hblankstart * 8;
1222         modedb[0].hsync_len = hsyncend * 8;
1223         modedb[0].right_margin = hsyncstart * 8 - modedb[0].xres;
1224         modedb[0].left_margin = (htotal + 5) * 8 - modedb[0].xres -
1225             modedb[0].right_margin - modedb[0].hsync_len;
1226
1227         vtotal = cr06 | (cr07 & 0x01) << 8 | (cr07 & 0x20) << 4
1228             | (cr27 & 0x80) << 3;
1229         vdispend = cr12 | (cr07 & 0x02) << 7 | (cr07 & 0x40) << 3
1230             | (cr27 & 0x10) << 6;
1231         vsyncstart = cr10 | (cr07 & 0x04) << 6 | (cr07 & 0x80) << 2
1232             | (cr27 & 0x20) << 5;
1233         vsyncend = cr11 & 0x0f;
1234         vblankstart = cr15 | (cr07 & 0x08) << 5 | (cr09 & 0x20) << 4
1235             | (cr27 & 0x40) << 4;
1236         vblankend = cr16;
1237
1238         modedb[0].yres = vdispend + 1;
1239         modedb[0].vsync_len = vsyncend;
1240         modedb[0].lower_margin = vsyncstart - modedb[0].yres;
1241         modedb[0].upper_margin = vtotal - modedb[0].yres -
1242             modedb[0].lower_margin - modedb[0].vsync_len + 2;
1243
1244         tmp = cr38 & 0x3c;
1245         modedb[0].bpp = tmp == 0 ? 8 : tmp == 4 ? 16 : tmp == 28 ? 24 :
1246             tmp == 8 ? 32 : 8;
1247
1248         fi = ((5864727 * (sr18 + 8)) /
1249               (((sr19 & 0x3f) + 2) * (1 << ((sr19 & 0xc0) >> 6)))) >> 12;
1250         pxclkdiv = ((gr0f & 0x08) >> 3 | (gr0f & 0x40) >> 5) + 1;
1251         tmp = sr0d & 0x06;
1252         vclkdiv = tmp == 0 ? 2 : tmp == 2 ? 4 : tmp == 4 ? 8 : 3; // * 2 !
1253         modedb[0].pxclk = ((100000000 * pxclkdiv * vclkdiv) >> 1) / fi;
1254
1255         if (verbosity > 0)
1256                 output("detected startup mode: "
1257                        "fbset -g %d %d %d ??? %d -t %d %d %d %d %d %d %d\n",
1258                        modedb[0].xres, modedb[0].yres, modedb[0].xres,
1259                        modedb[0].bpp, modedb[0].pxclk, modedb[0].left_margin,
1260                        modedb[0].right_margin, modedb[0].upper_margin,
1261                        modedb[0].lower_margin, modedb[0].hsync_len,
1262                        modedb[0].vsync_len);
1263
1264         //
1265         // We use this goto target in case of a failed check_var. No, I really
1266         // do not want to do it in another way!
1267         //
1268
1269       tryagain:
1270
1271         i = (mode == NULL) ? 0 :
1272             !strncmp(mode, "640x480", 7) ? 1 :
1273             !strncmp(mode, "800x600", 7) ? 2 :
1274             !strncmp(mode, "1024x768", 8) ? 3 :
1275             !strncmp(mode, "1280x1024", 9) ? 4 : 0;
1276
1277         ref = (ref < 50) ? 50 : (ref > 85) ? 85 : ref;
1278
1279         if (i == 0) {
1280                 info->var.pixclock = modedb[i].pxclk;
1281                 info->var.bits_per_pixel = modedb[i].bpp;
1282         } else {
1283                 info->var.pixclock = (100000000 /
1284                                       ((modedb[i].left_margin +
1285                                         modedb[i].xres +
1286                                         modedb[i].right_margin +
1287                                         modedb[i].hsync_len) *
1288                                        (modedb[i].upper_margin +
1289                                         modedb[i].yres +
1290                                         modedb[i].lower_margin +
1291                                         modedb[i].vsync_len) * ref / 10000));
1292                 info->var.bits_per_pixel = bpp;
1293         }
1294
1295         info->var.left_margin = modedb[i].left_margin;
1296         info->var.right_margin = modedb[i].right_margin;
1297         info->var.xres = modedb[i].xres;
1298         if (!(modedb[i].yres == 1280 && modedb[i].bpp == 32))
1299                 info->var.xres_virtual = modedb[i].vxres;
1300         else
1301                 info->var.xres_virtual = modedb[i].xres;
1302         info->var.xoffset = 0;
1303         info->var.hsync_len = modedb[i].hsync_len;
1304         info->var.upper_margin = modedb[i].upper_margin;
1305         info->var.yres = modedb[i].yres;
1306         info->var.yres_virtual = modedb[i].vyres;
1307         info->var.yoffset = 0;
1308         info->var.lower_margin = modedb[i].lower_margin;
1309         info->var.vsync_len = modedb[i].vsync_len;
1310         info->var.sync = 0;
1311         info->var.vmode = FB_VMODE_NONINTERLACED;
1312
1313         if (cyblafb_check_var(&info->var, info)) {
1314                 // 640x480 - 8@75 should really never fail. One case would
1315                 // be fp == 1 and nativex < 640 ... give up then
1316                 if (i == 1 && bpp == 8 && ref == 75) {
1317                         output("Can't find a valid mode :-(\n");
1318                         return -EINVAL;
1319                 }
1320                 // Our detected mode is unlikely to fail. If it does,
1321                 // try 640x480 - 8@75 ...
1322                 if (i == 0) {
1323                         mode = "640x480";
1324                         bpp = 8;
1325                         ref = 75;
1326                         output("Detected mode failed check_var! "
1327                                "Trying 640x480 - 8@75\n");
1328                         goto tryagain;
1329                 }
1330                 // A specified video mode failed for some reason.
1331                 // Try the startup mode first
1332                 output("Specified mode '%s' failed check! "
1333                        "Falling back to startup mode.\n", mode);
1334                 mode = NULL;
1335                 goto tryagain;
1336         }
1337
1338         return 0;
1339 }
1340
1341 //========================================================
1342 //
1343 // Detect activated memory size. Undefined values require
1344 // memsize parameter.
1345 //
1346 //========================================================
1347
1348 static unsigned int __devinit get_memsize(void)
1349 {
1350         unsigned char tmp;
1351         unsigned int k;
1352
1353         if (memsize)
1354                 k = memsize * Kb;
1355         else {
1356                 tmp = read3X4(CR1F) & 0x0F;
1357                 switch (tmp) {
1358                 case 0x03:
1359                         k = 1 * 1024 * 1024;
1360                         break;
1361                 case 0x07:
1362                         k = 2 * 1024 * 1024;
1363                         break;
1364                 case 0x0F:
1365                         k = 4 * 1024 * 1024;
1366                         break;
1367                 case 0x04:
1368                         k = 8 * 1024 * 1024;
1369                         break;
1370                 default:
1371                         k = 1 * 1024 * 1024;
1372                         output("Unknown memory size code %x in CR1F."
1373                                " We default to 1 Mb for now, please"
1374                                " do provide a memsize parameter!\n", tmp);
1375                 }
1376         }
1377
1378         if (verbosity > 0)
1379                 output("framebuffer size = %d Kb\n", k / Kb);
1380         return k;
1381 }
1382
1383 //=========================================================
1384 //
1385 // Detect if a flat panel monitor connected to the special
1386 // interface is active. Override is possible by fp and crt
1387 // parameters.
1388 //
1389 //=========================================================
1390
1391 static unsigned int __devinit get_displaytype(void)
1392 {
1393         if (fp)
1394                 return DISPLAY_FP;
1395         if (crt)
1396                 return DISPLAY_CRT;
1397         return (read3CE(GR33) & 0x10) ? DISPLAY_FP : DISPLAY_CRT;
1398 }
1399
1400 //=====================================
1401 //
1402 // Get native resolution of flat panel
1403 //
1404 //=====================================
1405
1406 static int __devinit get_nativex(void)
1407 {
1408         int x, y, tmp;
1409
1410         if (nativex)
1411                 return nativex;
1412
1413         tmp = (read3CE(GR52) >> 4) & 3;
1414
1415         switch (tmp) {
1416         case 0: x = 1280; y = 1024;
1417                 break;
1418         case 2: x = 1024; y = 768;
1419                 break;
1420         case 3: x = 800;  y = 600;
1421                 break;
1422         case 4: x = 1400; y = 1050;
1423                 break;
1424         case 1:
1425         default:
1426                 x = 640; y = 480;
1427                 break;
1428         }
1429
1430         if (verbosity > 0)
1431                 output("%dx%d flat panel found\n", x, y);
1432         return x;
1433 }
1434
1435 static int __devinit cybla_pci_probe(struct pci_dev *dev,
1436                                      const struct pci_device_id *id)
1437 {
1438         struct fb_info *info;
1439         struct cyblafb_par *par;
1440
1441         info = framebuffer_alloc(sizeof(struct cyblafb_par), &dev->dev);
1442         if (!info)
1443                 goto errout_alloc_info;
1444
1445         info->pixmap.addr = kzalloc(CYBLAFB_PIXMAPSIZE, GFP_KERNEL);
1446         if (!info->pixmap.addr) {
1447                 output("allocation of pixmap buffer failed!\n");
1448                 goto errout_alloc_pixmap;
1449         }
1450         info->pixmap.size = CYBLAFB_PIXMAPSIZE - 4;
1451         info->pixmap.buf_align = 4;
1452         info->pixmap.access_align = 32;
1453         info->pixmap.flags = FB_PIXMAP_SYSTEM;
1454         info->pixmap.scan_align = 4;
1455
1456         par = info->par;
1457         par->ops = cyblafb_ops;
1458
1459         info->fix = cyblafb_fix;
1460         info->fbops = &par->ops;
1461         info->fix = cyblafb_fix;
1462
1463         if (pci_enable_device(dev)) {
1464                 output("could not enable device!\n");
1465                 goto errout_enable;
1466         }
1467         // might already be requested by vga console or vesafb,
1468         // so we do care about success
1469         if (!request_region(0x3c0, 0x20, "cyblafb")) {
1470                 output("region 0x3c0/0x20 already reserved\n");
1471                 vesafb |= 1;
1472
1473         }
1474         //
1475         // Graphics Engine Registers
1476         //
1477         if (!request_region(GEBase, 0x100, "cyblafb")) {
1478                 output("region %#x/0x100 already reserved\n", GEBase);
1479                 vesafb |= 2;
1480         }
1481
1482         regdump(par);
1483
1484         enable_mmio();
1485
1486         // setup MMIO region
1487         info->fix.mmio_start = pci_resource_start(dev, 1);
1488         info->fix.mmio_len = 0x20000;
1489
1490         if (!request_mem_region(info->fix.mmio_start,
1491                                 info->fix.mmio_len, "cyblafb")) {
1492                 output("request_mem_region failed for mmio region!\n");
1493                 goto errout_mmio_reqmem;
1494         }
1495
1496         io_virt = ioremap_nocache(info->fix.mmio_start, info->fix.mmio_len);
1497
1498         if (!io_virt) {
1499                 output("ioremap failed for mmio region\n");
1500                 goto errout_mmio_remap;
1501         }
1502         // setup framebuffer memory ... might already be requested
1503         // by vesafb. Not to fail in case of an unsuccessful request
1504         // is useful if both are loaded.
1505         info->fix.smem_start = pci_resource_start(dev, 0);
1506         info->fix.smem_len = get_memsize();
1507
1508         if (!request_mem_region(info->fix.smem_start,
1509                                 info->fix.smem_len, "cyblafb")) {
1510                 output("region %#lx/%#x already reserved\n",
1511                        info->fix.smem_start, info->fix.smem_len);
1512                 vesafb |= 4;
1513         }
1514
1515         info->screen_base = ioremap_nocache(info->fix.smem_start,
1516                                             info->fix.smem_len);
1517
1518         if (!info->screen_base) {
1519                 output("ioremap failed for smem region\n");
1520                 goto errout_smem_remap;
1521         }
1522
1523         displaytype = get_displaytype();
1524
1525         if (displaytype == DISPLAY_FP)
1526                 nativex = get_nativex();
1527
1528         info->flags = FBINFO_DEFAULT
1529                     | FBINFO_HWACCEL_COPYAREA
1530                     | FBINFO_HWACCEL_FILLRECT
1531                     | FBINFO_HWACCEL_IMAGEBLIT
1532                     | FBINFO_READS_FAST
1533 //                  | FBINFO_PARTIAL_PAN_OK
1534                     | FBINFO_MISC_ALWAYS_SETPAR;
1535
1536         info->pseudo_palette = par->pseudo_pal;
1537
1538         if (getstartupmode(info))
1539                 goto errout_findmode;
1540
1541         fb_alloc_cmap(&info->cmap, 256, 0);
1542
1543         if (register_framebuffer(info)) {
1544                 output("Could not register CyBla framebuffer\n");
1545                 goto errout_register;
1546         }
1547
1548         pci_set_drvdata(dev, info);
1549
1550         //
1551         // normal exit and error paths
1552         //
1553
1554         return 0;
1555
1556       errout_register:
1557       errout_findmode:
1558         iounmap(info->screen_base);
1559       errout_smem_remap:
1560         if (!(vesafb & 4))
1561                 release_mem_region(info->fix.smem_start, info->fix.smem_len);
1562         iounmap(io_virt);
1563       errout_mmio_remap:
1564         release_mem_region(info->fix.mmio_start, info->fix.mmio_len);
1565       errout_mmio_reqmem:
1566         if (!(vesafb & 1))
1567                 release_region(0x3c0, 32);
1568       errout_enable:
1569         kfree(info->pixmap.addr);
1570       errout_alloc_pixmap:
1571         framebuffer_release(info);
1572       errout_alloc_info:
1573         output("CyblaFB version %s aborting init.\n", VERSION);
1574         return -ENODEV;
1575 }
1576
1577 static void __devexit cybla_pci_remove(struct pci_dev *dev)
1578 {
1579         struct fb_info *info = pci_get_drvdata(dev);
1580
1581         unregister_framebuffer(info);
1582         iounmap(io_virt);
1583         iounmap(info->screen_base);
1584         if (!(vesafb & 4))
1585                 release_mem_region(info->fix.smem_start, info->fix.smem_len);
1586         release_mem_region(info->fix.mmio_start, info->fix.mmio_len);
1587         fb_dealloc_cmap(&info->cmap);
1588         if (!(vesafb & 2))
1589                 release_region(GEBase, 0x100);
1590         if (!(vesafb & 1))
1591                 release_region(0x3c0, 32);
1592         kfree(info->pixmap.addr);
1593         framebuffer_release(info);
1594         output("CyblaFB version %s normal exit.\n", VERSION);
1595 }
1596
1597 //
1598 // List of boards that we are trying to support
1599 //
1600 static struct pci_device_id cybla_devices[] = {
1601         {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1602         {0,}
1603 };
1604
1605 MODULE_DEVICE_TABLE(pci, cybla_devices);
1606
1607 static struct pci_driver cyblafb_pci_driver = {
1608         .name = "cyblafb",
1609         .id_table = cybla_devices,
1610         .probe = cybla_pci_probe,
1611         .remove = __devexit_p(cybla_pci_remove)
1612 };
1613
1614 //=============================================================
1615 //
1616 // kernel command line example:
1617 //
1618 //      video=cyblafb:1280x1024, bpp=16, ref=50 ...
1619 //
1620 // modprobe command line example:
1621 //
1622 //      modprobe cyblafb mode=1280x1024 bpp=16 ref=50 ...
1623 //
1624 //=============================================================
1625
1626 static int __devinit cyblafb_init(void)
1627 {
1628 #ifndef MODULE
1629         char *options = NULL;
1630         char *opt;
1631
1632         if (fb_get_options("cyblafb", &options))
1633                 return -ENODEV;
1634
1635         if (options && *options)
1636                 while ((opt = strsep(&options, ",")) != NULL) {
1637                         if (!*opt)
1638                                 continue;
1639                         else if (!strncmp(opt, "bpp=", 4))
1640                                 bpp = simple_strtoul(opt + 4, NULL, 0);
1641                         else if (!strncmp(opt, "ref=", 4))
1642                                 ref = simple_strtoul(opt + 4, NULL, 0);
1643                         else if (!strncmp(opt, "fp", 2))
1644                                 displaytype = DISPLAY_FP;
1645                         else if (!strncmp(opt, "crt", 3))
1646                                 displaytype = DISPLAY_CRT;
1647                         else if (!strncmp(opt, "nativex=", 8))
1648                                 nativex = simple_strtoul(opt + 8, NULL, 0);
1649                         else if (!strncmp(opt, "center", 6))
1650                                 center = 1;
1651                         else if (!strncmp(opt, "stretch", 7))
1652                                 stretch = 1;
1653                         else if (!strncmp(opt, "pciwb=", 6))
1654                                 pciwb = simple_strtoul(opt + 6, NULL, 0);
1655                         else if (!strncmp(opt, "pcirb=", 6))
1656                                 pcirb = simple_strtoul(opt + 6, NULL, 0);
1657                         else if (!strncmp(opt, "pciwr=", 6))
1658                                 pciwr = simple_strtoul(opt + 6, NULL, 0);
1659                         else if (!strncmp(opt, "pcirr=", 6))
1660                                 pcirr = simple_strtoul(opt + 6, NULL, 0);
1661                         else if (!strncmp(opt, "memsize=", 8))
1662                                 memsize = simple_strtoul(opt + 8, NULL, 0);
1663                         else if (!strncmp(opt, "verbosity=", 10))
1664                                 verbosity = simple_strtoul(opt + 10, NULL, 0);
1665                         else
1666                                 mode = opt;
1667                 }
1668 #endif
1669         output("CyblaFB version %s initializing\n", VERSION);
1670         return pci_register_driver(&cyblafb_pci_driver);
1671 }
1672
1673 static void __exit cyblafb_exit(void)
1674 {
1675         pci_unregister_driver(&cyblafb_pci_driver);
1676 }
1677
1678 module_init(cyblafb_init);
1679 module_exit(cyblafb_exit);
1680
1681 MODULE_AUTHOR("Knut Petersen <knut_petersen@t-online.de>");
1682 MODULE_DESCRIPTION("Framebuffer driver for Cyberblade/i1 graphics core");
1683 MODULE_LICENSE("GPL");