usb-serial: possible irq lock inversion (PPP vs. usb/serial)
[linux-2.6] / drivers / video / tridentfb.c
1 /*
2  * Frame buffer driver for Trident Blade and Image series
3  *
4  * Copyright 2001,2002 - Jani Monoses   <jani@iv.ro>
5  *
6  *
7  * CREDITS:(in order of appearance)
8  *      skeletonfb.c by Geert Uytterhoeven and other fb code in drivers/video
9  *      Special thanks ;) to Mattia Crivellini <tia@mclink.it>
10  *      much inspired by the XFree86 4.x Trident driver sources by Alan Hourihane
11  *      the FreeVGA project
12  *      Francesco Salvestrini <salvestrini@users.sf.net> XP support,code,suggestions
13  * TODO:
14  *      timing value tweaking so it looks good on every monitor in every mode
15  *      TGUI acceleration       
16  */
17
18 #include <linux/module.h>
19 #include <linux/fb.h>
20 #include <linux/init.h>
21 #include <linux/pci.h>
22
23 #include <linux/delay.h>
24 #include <video/trident.h>
25
26 #define VERSION         "0.7.8-NEWAPI"
27
28 struct tridentfb_par {
29         int vclk;               //in MHz
30         void __iomem * io_virt; //iospace virtual memory address
31 };
32
33 static unsigned char eng_oper;          //engine operation...
34 static struct fb_ops tridentfb_ops;
35
36 static struct tridentfb_par default_par;
37
38 /* FIXME:kmalloc these 3 instead */
39 static struct fb_info fb_info;
40 static u32 pseudo_pal[16];
41
42
43 static struct fb_var_screeninfo default_var;
44
45 static struct fb_fix_screeninfo tridentfb_fix = {
46         .id = "Trident",        
47         .type = FB_TYPE_PACKED_PIXELS,
48         .ypanstep = 1,
49         .visual = FB_VISUAL_PSEUDOCOLOR,
50         .accel = FB_ACCEL_NONE,
51 };
52
53 static int chip_id;
54
55 static int defaultaccel;
56 static int displaytype;
57
58
59 /* defaults which are normally overriden by user values */
60
61 /* video mode */
62 static char * mode = "640x480";
63 static int bpp = 8;
64
65 static int noaccel;
66
67 static int center;
68 static int stretch;
69
70 static int fp;
71 static int crt;
72
73 static int memsize;
74 static int memdiff;
75 static int nativex;
76
77
78 module_param(mode, charp, 0);
79 module_param(bpp, int, 0);
80 module_param(center, int, 0);
81 module_param(stretch, int, 0);
82 module_param(noaccel, int, 0);
83 module_param(memsize, int, 0);
84 module_param(memdiff, int, 0);
85 module_param(nativex, int, 0);
86 module_param(fp, int, 0);
87 module_param(crt, int, 0);
88
89
90 static int chip3D;
91 static int chipcyber;
92
93 static int is3Dchip(int id)
94 {
95         return  ((id == BLADE3D) || (id == CYBERBLADEE4) ||
96                  (id == CYBERBLADEi7) || (id == CYBERBLADEi7D) ||
97                  (id == CYBER9397) || (id == CYBER9397DVD) ||
98                  (id == CYBER9520) || (id == CYBER9525DVD) ||
99                  (id == IMAGE975) || (id == IMAGE985) ||
100                  (id == CYBERBLADEi1) || (id == CYBERBLADEi1D) ||
101                  (id == CYBERBLADEAi1) || (id == CYBERBLADEAi1D) ||
102                  (id == CYBERBLADEXPm8) || (id == CYBERBLADEXPm16) ||
103                  (id == CYBERBLADEXPAi1));
104 }
105
106 static int iscyber(int id)
107 {
108         switch (id) {
109                 case CYBER9388:         
110                 case CYBER9382:
111                 case CYBER9385:
112                 case CYBER9397:
113                 case CYBER9397DVD:
114                 case CYBER9520:
115                 case CYBER9525DVD:
116                 case CYBERBLADEE4:
117                 case CYBERBLADEi7D:
118                 case CYBERBLADEi1:
119                 case CYBERBLADEi1D: 
120                 case CYBERBLADEAi1: 
121                 case CYBERBLADEAi1D:
122                 case CYBERBLADEXPAi1:
123                         return 1;
124                 
125                 case CYBER9320:
126                 case TGUI9660:     
127                 case IMAGE975:
128                 case IMAGE985:
129                 case BLADE3D:
130                 case CYBERBLADEi7: /* VIA MPV4 integrated version */
131
132                 default:
133                         /* case CYBERBLDAEXPm8:  Strange */
134                         /* case CYBERBLDAEXPm16: Strange */
135                         return 0;
136         }
137 }
138
139 #define CRT 0x3D0               //CRTC registers offset for color display
140
141 #ifndef TRIDENT_MMIO
142         #define TRIDENT_MMIO 1
143 #endif
144
145 #if TRIDENT_MMIO
146         #define t_outb(val,reg) writeb(val,((struct tridentfb_par *)(fb_info.par))->io_virt + reg)
147         #define t_inb(reg)      readb(((struct tridentfb_par*)(fb_info.par))->io_virt + reg)
148 #else
149         #define t_outb(val,reg) outb(val,reg)
150         #define t_inb(reg) inb(reg)
151 #endif
152
153
154 static struct accel_switch {
155         void (*init_accel)(int,int);
156         void (*wait_engine)(void);
157         void (*fill_rect)(__u32,__u32,__u32,__u32,__u32,__u32);
158         void (*copy_rect)(__u32,__u32,__u32,__u32,__u32,__u32);
159 } *acc;
160
161 #define writemmr(r,v)   writel(v, ((struct tridentfb_par *)fb_info.par)->io_virt + r)
162 #define readmmr(r)      readl(((struct tridentfb_par *)fb_info.par)->io_virt + r)
163
164
165
166 /*
167  * Blade specific acceleration.
168  */
169
170 #define point(x,y) ((y)<<16|(x))
171 #define STA     0x2120
172 #define CMD     0x2144
173 #define ROP     0x2148
174 #define CLR     0x2160
175 #define SR1     0x2100
176 #define SR2     0x2104
177 #define DR1     0x2108
178 #define DR2     0x210C
179
180 #define ROP_S   0xCC
181
182 static void blade_init_accel(int pitch,int bpp)
183 {
184         int v1 = (pitch>>3)<<20;
185         int tmp = 0,v2;
186         switch (bpp) {
187                 case 8:tmp = 0;break;
188                 case 15:tmp = 5;break;
189                 case 16:tmp = 1;break;
190                 case 24:
191                 case 32:tmp = 2;break;
192         }
193         v2 = v1 | (tmp<<29);
194         writemmr(0x21C0,v2);
195         writemmr(0x21C4,v2);
196         writemmr(0x21B8,v2);
197         writemmr(0x21BC,v2);
198         writemmr(0x21D0,v1);
199         writemmr(0x21D4,v1);
200         writemmr(0x21C8,v1);
201         writemmr(0x21CC,v1);
202         writemmr(0x216C,0);
203 }
204
205 static void blade_wait_engine(void)
206 {
207         while(readmmr(STA) & 0xFA800000);
208 }
209
210 static void blade_fill_rect(__u32 x,__u32 y,__u32 w,__u32 h,__u32 c,__u32 rop)
211 {
212         writemmr(CLR,c);
213         writemmr(ROP,rop ? 0x66:ROP_S);
214         writemmr(CMD,0x20000000|1<<19|1<<4|2<<2);
215
216         writemmr(DR1,point(x,y));
217         writemmr(DR2,point(x+w-1,y+h-1));
218 }
219
220 static void blade_copy_rect(__u32 x1,__u32 y1,__u32 x2,__u32 y2,__u32 w,__u32 h)
221 {
222         __u32 s1,s2,d1,d2;
223         int direction = 2;
224         s1 = point(x1,y1);
225         s2 = point(x1+w-1,y1+h-1);
226         d1 = point(x2,y2);
227         d2 = point(x2+w-1,y2+h-1);
228
229         if ((y1 > y2) || ((y1 == y2) && (x1 > x2)))
230                         direction = 0;
231
232
233         writemmr(ROP,ROP_S);
234         writemmr(CMD,0xE0000000|1<<19|1<<4|1<<2|direction);
235
236         writemmr(SR1,direction?s2:s1);
237         writemmr(SR2,direction?s1:s2);
238         writemmr(DR1,direction?d2:d1);
239         writemmr(DR2,direction?d1:d2);
240 }
241
242 static struct accel_switch accel_blade = {
243         blade_init_accel,
244         blade_wait_engine,
245         blade_fill_rect,
246         blade_copy_rect,
247 };
248
249
250 /*
251  * BladeXP specific acceleration functions
252  */
253
254 #define ROP_P 0xF0
255 #define masked_point(x,y) ((y & 0xffff)<<16|(x & 0xffff))
256
257 static void xp_init_accel(int pitch,int bpp)
258 {
259         int tmp = 0,v1;
260         unsigned char x = 0;
261
262         switch (bpp) {
263                 case 8:  x = 0; break;
264                 case 16: x = 1; break;
265                 case 24: x = 3; break;
266                 case 32: x = 2; break;
267         }
268
269         switch (pitch << (bpp >> 3)) {
270                 case 8192:
271                 case 512:  x |= 0x00; break;
272                 case 1024: x |= 0x04; break;
273                 case 2048: x |= 0x08; break;
274                 case 4096: x |= 0x0C; break;
275         }
276
277         t_outb(x,0x2125);
278
279         eng_oper = x | 0x40;
280
281         switch (bpp) {
282                 case 8:  tmp = 18; break;
283                 case 15:
284                 case 16: tmp = 19; break;
285                 case 24:
286                 case 32: tmp = 20; break;
287         }
288
289         v1 = pitch << tmp;
290
291         writemmr(0x2154,v1);
292         writemmr(0x2150,v1);
293         t_outb(3,0x2126);
294 }
295
296 static void xp_wait_engine(void)
297 {
298         int busy;
299         int count, timeout;
300
301         count = 0;
302         timeout = 0;
303         for (;;) {
304                 busy = t_inb(STA) & 0x80;
305                 if (busy != 0x80)
306                         return;
307                 count++;
308                 if (count == 10000000) {
309                         /* Timeout */
310                         count = 9990000;
311                         timeout++;
312                         if (timeout == 8) {
313                                 /* Reset engine */
314                                 t_outb(0x00, 0x2120);
315                                 return;
316                         }
317                 }
318         }
319 }
320
321 static void xp_fill_rect(__u32 x,__u32 y,__u32 w,__u32 h,__u32 c,__u32 rop)
322 {
323         writemmr(0x2127,ROP_P);
324         writemmr(0x2158,c);
325         writemmr(0x2128,0x4000);
326         writemmr(0x2140,masked_point(h,w));
327         writemmr(0x2138,masked_point(y,x));
328         t_outb(0x01,0x2124);
329         t_outb(eng_oper,0x2125);
330 }
331
332 static void xp_copy_rect(__u32 x1,__u32 y1,__u32 x2,__u32 y2,__u32 w,__u32 h)
333 {
334         int direction;
335         __u32 x1_tmp, x2_tmp, y1_tmp, y2_tmp;
336
337         direction = 0x0004;
338         
339         if ((x1 < x2) && (y1 == y2)) {
340                 direction |= 0x0200;
341                 x1_tmp = x1 + w - 1;
342                 x2_tmp = x2 + w - 1;
343         } else {
344                 x1_tmp = x1;
345                 x2_tmp = x2;
346         }
347   
348         if (y1 < y2) {
349                 direction |= 0x0100;
350                 y1_tmp = y1 + h - 1;
351                 y2_tmp = y2 + h - 1;
352         } else {
353                 y1_tmp = y1;
354                 y2_tmp = y2;
355         }
356
357         writemmr(0x2128,direction);     
358         t_outb(ROP_S,0x2127);
359         writemmr(0x213C,masked_point(y1_tmp,x1_tmp));
360         writemmr(0x2138,masked_point(y2_tmp,x2_tmp));
361         writemmr(0x2140,masked_point(h,w));
362         t_outb(0x01,0x2124);
363 }
364
365 static struct accel_switch accel_xp = {
366         xp_init_accel,
367         xp_wait_engine,
368         xp_fill_rect,
369         xp_copy_rect,
370 };
371
372
373 /*
374  * Image specific acceleration functions
375  */
376 static void image_init_accel(int pitch,int bpp)
377 {
378         int tmp = 0;
379         switch (bpp) {
380                 case 8:tmp = 0;break;
381                 case 15:tmp = 5;break;
382                 case 16:tmp = 1;break;
383                 case 24:
384                 case 32:tmp = 2;break;
385         }
386         writemmr(0x2120, 0xF0000000);
387         writemmr(0x2120, 0x40000000|tmp);
388         writemmr(0x2120, 0x80000000);
389         writemmr(0x2144, 0x00000000);
390         writemmr(0x2148, 0x00000000);
391         writemmr(0x2150, 0x00000000);
392         writemmr(0x2154, 0x00000000);
393         writemmr(0x2120, 0x60000000|(pitch<<16) |pitch);
394         writemmr(0x216C, 0x00000000);
395         writemmr(0x2170, 0x00000000);
396         writemmr(0x217C, 0x00000000);
397         writemmr(0x2120, 0x10000000);
398         writemmr(0x2130, (2047 << 16) | 2047);
399 }
400
401 static void image_wait_engine(void)
402 {
403         while(readmmr(0x2164) & 0xF0000000);
404 }
405
406 static void image_fill_rect(__u32 x, __u32 y, __u32 w, __u32 h, __u32 c, __u32 rop)
407 {
408         writemmr(0x2120,0x80000000);
409         writemmr(0x2120,0x90000000|ROP_S);
410
411         writemmr(0x2144,c);
412
413         writemmr(DR1,point(x,y));
414         writemmr(DR2,point(x+w-1,y+h-1));
415
416         writemmr(0x2124,0x80000000|3<<22|1<<10|1<<9);
417 }
418
419 static void image_copy_rect(__u32 x1,__u32 y1,__u32 x2,__u32 y2,__u32 w,__u32 h)
420 {
421         __u32 s1,s2,d1,d2;
422         int direction = 2;
423         s1 = point(x1,y1);
424         s2 = point(x1+w-1,y1+h-1);
425         d1 = point(x2,y2);
426         d2 = point(x2+w-1,y2+h-1);
427
428         if ((y1 > y2) || ((y1 == y2) && (x1 >x2)))
429                         direction = 0;
430
431         writemmr(0x2120,0x80000000);
432         writemmr(0x2120,0x90000000|ROP_S);
433
434         writemmr(SR1,direction?s2:s1);
435         writemmr(SR2,direction?s1:s2);
436         writemmr(DR1,direction?d2:d1);
437         writemmr(DR2,direction?d1:d2);
438         writemmr(0x2124,0x80000000|1<<22|1<<10|1<<7|direction);
439 }
440
441
442 static struct accel_switch accel_image = {
443         image_init_accel,
444         image_wait_engine,
445         image_fill_rect,
446         image_copy_rect,
447 };
448
449 /*
450  * Accel functions called by the upper layers
451  */
452 #ifdef CONFIG_FB_TRIDENT_ACCEL
453 static void tridentfb_fillrect(struct fb_info * info, const struct fb_fillrect *fr)
454 {
455         int bpp = info->var.bits_per_pixel;
456         int col = 0;
457         
458         switch (bpp) {
459                 default:
460                 case 8: col |= fr->color;
461                         col |= col << 8;
462                         col |= col << 16;
463                         break;
464                 case 16: col = ((u32 *)(info->pseudo_palette))[fr->color];
465                         
466                          break;
467                 case 32: col = ((u32 *)(info->pseudo_palette))[fr->color];
468                          break;
469         }                
470                         
471         acc->fill_rect(fr->dx, fr->dy, fr->width, fr->height, col, fr->rop);
472         acc->wait_engine();
473 }
474 static void tridentfb_copyarea(struct fb_info *info, const struct fb_copyarea *ca)
475 {
476         acc->copy_rect(ca->sx,ca->sy,ca->dx,ca->dy,ca->width,ca->height);
477         acc->wait_engine();
478 }
479 #else /* !CONFIG_FB_TRIDENT_ACCEL */
480 #define tridentfb_fillrect cfb_fillrect
481 #define tridentfb_copyarea cfb_copyarea
482 #endif /* CONFIG_FB_TRIDENT_ACCEL */
483
484
485 /*
486  * Hardware access functions
487  */
488
489 static inline unsigned char read3X4(int reg)
490 {
491         struct tridentfb_par * par = (struct tridentfb_par *)fb_info.par;
492         writeb(reg, par->io_virt + CRT + 4);
493         return readb( par->io_virt + CRT + 5);
494 }
495
496 static inline void write3X4(int reg, unsigned char val)
497 {
498         struct tridentfb_par * par = (struct tridentfb_par *)fb_info.par;
499         writeb(reg, par->io_virt + CRT + 4);
500         writeb(val, par->io_virt + CRT + 5);
501 }
502
503 static inline unsigned char read3C4(int reg)
504 {
505         t_outb(reg, 0x3C4);
506         return t_inb(0x3C5);
507 }
508
509 static inline void write3C4(int reg, unsigned char val)
510 {
511         t_outb(reg, 0x3C4);
512         t_outb(val, 0x3C5);
513 }
514
515 static inline unsigned char read3CE(int reg)
516 {
517         t_outb(reg, 0x3CE);
518         return t_inb(0x3CF);
519 }
520
521 static inline void writeAttr(int reg, unsigned char val)
522 {
523         readb(((struct tridentfb_par *)fb_info.par)->io_virt + CRT + 0x0A);     //flip-flop to index
524         t_outb(reg, 0x3C0);
525         t_outb(val, 0x3C0);
526 }
527
528 static inline void write3CE(int reg, unsigned char val)
529 {
530         t_outb(reg, 0x3CE);
531         t_outb(val, 0x3CF);
532 }
533
534 static inline void enable_mmio(void)
535 {
536         /* Goto New Mode */
537         outb(0x0B, 0x3C4);
538         inb(0x3C5);
539
540         /* Unprotect registers */
541         outb(NewMode1, 0x3C4);
542         outb(0x80, 0x3C5);
543   
544         /* Enable MMIO */
545         outb(PCIReg, 0x3D4); 
546         outb(inb(0x3D5) | 0x01, 0x3D5);
547 }
548
549
550 #define crtc_unlock()   write3X4(CRTVSyncEnd, read3X4(CRTVSyncEnd) & 0x7F)
551
552 /*  Return flat panel's maximum x resolution */
553 static int __devinit get_nativex(void)
554 {
555         int x,y,tmp;
556
557         if (nativex)
558                 return nativex;
559
560         tmp = (read3CE(VertStretch) >> 4) & 3;
561
562         switch (tmp) {
563                 case 0: x = 1280; y = 1024; break;
564                 case 2: x = 1024; y = 768;  break;
565                 case 3: x = 800;  y = 600;  break; 
566                 case 4: x = 1400; y = 1050; break;
567                 case 1: 
568                 default:x = 640;  y = 480;  break;
569         }
570
571         output("%dx%d flat panel found\n", x, y);
572         return x;
573 }
574
575 /* Set pitch */
576 static void set_lwidth(int width)
577 {
578         write3X4(Offset, width & 0xFF);
579         write3X4(AddColReg, (read3X4(AddColReg) & 0xCF) | ((width & 0x300) >>4));
580 }
581
582 /* For resolutions smaller than FP resolution stretch */
583 static void screen_stretch(void)
584 {
585         if (chip_id != CYBERBLADEXPAi1)
586                 write3CE(BiosReg,0);
587         else
588                 write3CE(BiosReg,8);
589         write3CE(VertStretch,(read3CE(VertStretch) & 0x7C) | 1);
590         write3CE(HorStretch,(read3CE(HorStretch) & 0x7C) | 1);
591 }
592
593 /* For resolutions smaller than FP resolution center */
594 static void screen_center(void)
595 {
596         write3CE(VertStretch,(read3CE(VertStretch) & 0x7C) | 0x80);
597         write3CE(HorStretch,(read3CE(HorStretch) & 0x7C) | 0x80);
598 }
599
600 /* Address of first shown pixel in display memory */
601 static void set_screen_start(int base)
602 {
603         write3X4(StartAddrLow, base & 0xFF);
604         write3X4(StartAddrHigh, (base & 0xFF00) >> 8);
605         write3X4(CRTCModuleTest, (read3X4(CRTCModuleTest) & 0xDF) | ((base & 0x10000) >> 11));
606         write3X4(CRTHiOrd, (read3X4(CRTHiOrd) & 0xF8) | ((base & 0xE0000) >> 17));
607 }
608
609 /* Use 20.12 fixed-point for NTSC value and frequency calculation */
610 #define calc_freq(n,m,k)  ( ((unsigned long)0xE517 * (n+8) / ((m+2)*(1<<k))) >> 12 )
611
612 /* Set dotclock frequency */
613 static void set_vclk(int freq)
614 {
615         int m,n,k;
616         int f,fi,d,di;
617         unsigned char lo=0,hi=0;
618
619         d = 20;
620         for(k = 2;k>=0;k--)
621         for(m = 0;m<63;m++)
622         for(n = 0;n<128;n++) {
623                 fi = calc_freq(n,m,k);
624                 if ((di = abs(fi - freq)) < d) {
625                         d = di;
626                         f = fi;
627                         lo = n;
628                         hi = (k<<6) | m;
629                 }
630         }
631         if (chip3D) {
632                 write3C4(ClockHigh,hi);
633                 write3C4(ClockLow,lo);
634         } else {
635                 outb(lo,0x43C8);
636                 outb(hi,0x43C9);
637         }
638         debug("VCLK = %X %X\n",hi,lo);
639 }
640
641 /* Set number of lines for flat panels*/
642 static void set_number_of_lines(int lines)
643 {
644         int tmp = read3CE(CyberEnhance) & 0x8F;
645         if (lines > 1024)
646                 tmp |= 0x50;
647         else if (lines > 768)
648                 tmp |= 0x30;
649         else if (lines > 600)
650                 tmp |= 0x20;
651         else if (lines > 480)
652                 tmp |= 0x10;
653         write3CE(CyberEnhance, tmp);
654 }
655
656 /*
657  * If we see that FP is active we assume we have one.
658  * Otherwise we have a CRT display.User can override.
659  */
660 static unsigned int __devinit get_displaytype(void)
661 {
662         if (fp)
663                 return DISPLAY_FP;
664         if (crt || !chipcyber)
665                 return DISPLAY_CRT;
666         return (read3CE(FPConfig) & 0x10)?DISPLAY_FP:DISPLAY_CRT;
667 }
668
669 /* Try detecting the video memory size */
670 static unsigned int __devinit get_memsize(void)
671 {
672         unsigned char tmp, tmp2;
673         unsigned int k;
674
675         /* If memory size provided by user */
676         if (memsize)
677                 k = memsize * Kb;
678         else
679         switch (chip_id) {
680                 case CYBER9525DVD:    k = 2560 * Kb; break;
681                 default:
682                         tmp = read3X4(SPR) & 0x0F;
683                         switch (tmp) {
684
685                                 case 0x01: k = 512;     break;
686                                 case 0x02: k = 6 * Mb;  break; /* XP */
687                                 case 0x03: k = 1 * Mb;  break;
688                                 case 0x04: k = 8 * Mb;  break;
689                                 case 0x06: k = 10 * Mb; break; /* XP */
690                                 case 0x07: k = 2 * Mb;  break;
691                                 case 0x08: k = 12 * Mb; break; /* XP */
692                                 case 0x0A: k = 14 * Mb; break; /* XP */
693                                 case 0x0C: k = 16 * Mb; break; /* XP */
694                                 case 0x0E:                     /* XP */
695   
696                                         tmp2 = read3C4(0xC1);
697                                         switch (tmp2) {
698                                                 case 0x00: k = 20 * Mb; break;
699                                                 case 0x01: k = 24 * Mb; break;
700                                                 case 0x10: k = 28 * Mb; break;
701                                                 case 0x11: k = 32 * Mb; break;
702                                                 default:   k = 1 * Mb;  break;
703                                         }
704                                 break;
705         
706                                 case 0x0F: k = 4 * Mb; break;
707                                 default:   k = 1 * Mb;
708                         }
709         }
710
711         k -= memdiff * Kb;
712         output("framebuffer size = %d Kb\n", k/Kb);
713         return k;
714 }
715
716 /* See if we can handle the video mode described in var */
717 static int tridentfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
718 {
719         int bpp = var->bits_per_pixel;
720         debug("enter\n");
721
722         /* check color depth */
723         if (bpp == 24 )
724                 bpp = var->bits_per_pixel = 32;
725         /* check whether resolution fits on panel and in memory*/
726         if (flatpanel && nativex && var->xres > nativex)
727                 return -EINVAL;
728         if (var->xres * var->yres_virtual * bpp/8 > info->fix.smem_len)
729                 return -EINVAL;
730
731         switch (bpp) {
732                 case 8:
733                         var->red.offset = 0;
734                         var->green.offset = 0;
735                         var->blue.offset = 0;
736                         var->red.length = 6;
737                         var->green.length = 6;
738                         var->blue.length = 6;
739                         break;
740                 case 16:
741                         var->red.offset = 11;
742                         var->green.offset = 5;
743                         var->blue.offset = 0;
744                         var->red.length = 5;
745                         var->green.length = 6;
746                         var->blue.length = 5;
747                         break;
748                 case 32:
749                         var->red.offset = 16;
750                         var->green.offset = 8;
751                         var->blue.offset = 0;
752                         var->red.length = 8;
753                         var->green.length = 8;
754                         var->blue.length = 8;
755                         break;
756                 default:
757                         return -EINVAL; 
758         }
759         debug("exit\n");
760
761         return 0;
762
763 }
764 /* Pan the display */
765 static int tridentfb_pan_display(struct fb_var_screeninfo *var,
766                                    struct fb_info *info)
767 {
768         unsigned int offset;
769
770         debug("enter\n");
771         offset = (var->xoffset + (var->yoffset * var->xres))
772                         * var->bits_per_pixel/32;
773         info->var.xoffset = var->xoffset;
774         info->var.yoffset = var->yoffset;
775         set_screen_start(offset);
776         debug("exit\n");
777         return 0;
778 }
779
780 #define shadowmode_on()  write3CE(CyberControl,read3CE(CyberControl) | 0x81)
781 #define shadowmode_off() write3CE(CyberControl,read3CE(CyberControl) & 0x7E)
782
783 /* Set the hardware to the requested video mode */
784 static int tridentfb_set_par(struct fb_info *info)
785 {
786         struct tridentfb_par * par = (struct tridentfb_par *)(info->par);
787         u32     htotal,hdispend,hsyncstart,hsyncend,hblankstart,hblankend,
788                 vtotal,vdispend,vsyncstart,vsyncend,vblankstart,vblankend;
789         struct fb_var_screeninfo *var = &info->var;     
790         int bpp = var->bits_per_pixel;
791         unsigned char tmp;
792         debug("enter\n");
793         htotal = (var->xres + var->left_margin + var->right_margin + var->hsync_len)/8 - 10;
794         hdispend = var->xres/8 - 1;
795         hsyncstart = (var->xres + var->right_margin)/8;
796         hsyncend = var->hsync_len/8;
797         hblankstart = hdispend + 1;
798         hblankend = htotal + 5;
799
800         vtotal = var->yres + var->upper_margin + var->lower_margin + var->vsync_len - 2;
801         vdispend = var->yres - 1;
802         vsyncstart = var->yres + var->lower_margin;
803         vsyncend = var->vsync_len;
804         vblankstart = var->yres;
805         vblankend = vtotal + 2;
806
807         enable_mmio();
808         crtc_unlock();
809         write3CE(CyberControl,8);
810
811         if (flatpanel && var->xres < nativex) {
812                 /*
813                  * on flat panels with native size larger
814                  * than requested resolution decide whether
815                  * we stretch or center
816                  */
817                 t_outb(0xEB,0x3C2);
818
819                 shadowmode_on();
820
821                 if (center) 
822                         screen_center();
823                 else if (stretch)
824                         screen_stretch();
825
826         } else {
827                 t_outb(0x2B,0x3C2);
828                 write3CE(CyberControl,8);
829         }
830
831         /* vertical timing values */
832         write3X4(CRTVTotal, vtotal & 0xFF);
833         write3X4(CRTVDispEnd, vdispend & 0xFF);
834         write3X4(CRTVSyncStart, vsyncstart & 0xFF);
835         write3X4(CRTVSyncEnd, (vsyncend & 0x0F));
836         write3X4(CRTVBlankStart, vblankstart & 0xFF);
837         write3X4(CRTVBlankEnd, 0/*p->vblankend & 0xFF*/);
838
839         /* horizontal timing values */
840         write3X4(CRTHTotal, htotal & 0xFF);
841         write3X4(CRTHDispEnd, hdispend & 0xFF);
842         write3X4(CRTHSyncStart, hsyncstart & 0xFF);
843         write3X4(CRTHSyncEnd, (hsyncend & 0x1F) | ((hblankend & 0x20)<<2));
844         write3X4(CRTHBlankStart, hblankstart & 0xFF);
845         write3X4(CRTHBlankEnd, 0/*(p->hblankend & 0x1F)*/);
846
847         /* higher bits of vertical timing values */
848         tmp = 0x10;
849         if (vtotal & 0x100) tmp |= 0x01;
850         if (vdispend & 0x100) tmp |= 0x02;
851         if (vsyncstart & 0x100) tmp |= 0x04;
852         if (vblankstart & 0x100) tmp |= 0x08;
853
854         if (vtotal & 0x200) tmp |= 0x20;
855         if (vdispend & 0x200) tmp |= 0x40;
856         if (vsyncstart & 0x200) tmp |= 0x80;
857         write3X4(CRTOverflow, tmp);
858
859         tmp = read3X4(CRTHiOrd) | 0x08; //line compare bit 10
860         if (vtotal & 0x400) tmp |= 0x80;
861         if (vblankstart & 0x400) tmp |= 0x40;
862         if (vsyncstart & 0x400) tmp |= 0x20;
863         if (vdispend & 0x400) tmp |= 0x10;
864         write3X4(CRTHiOrd, tmp);
865
866         tmp = 0;
867         if (htotal & 0x800) tmp |= 0x800 >> 11;
868         if (hblankstart & 0x800) tmp |= 0x800 >> 7;
869         write3X4(HorizOverflow, tmp);
870         
871         tmp = 0x40;
872         if (vblankstart & 0x200) tmp |= 0x20;
873 //FIXME if (info->var.vmode & FB_VMODE_DOUBLE) tmp |= 0x80;  //double scan for 200 line modes
874         write3X4(CRTMaxScanLine, tmp);
875
876         write3X4(CRTLineCompare,0xFF);
877         write3X4(CRTPRowScan,0);
878         write3X4(CRTModeControl,0xC3);
879
880         write3X4(LinearAddReg,0x20);    //enable linear addressing
881
882         tmp = (info->var.vmode & FB_VMODE_INTERLACED) ? 0x84:0x80;
883         write3X4(CRTCModuleTest,tmp);   //enable access extended memory
884
885         write3X4(GraphEngReg, 0x80);    //enable GE for text acceleration
886
887 #ifdef CONFIG_FB_TRIDENT_ACCEL  
888         acc->init_accel(info->var.xres,bpp);
889 #endif
890         
891         switch (bpp) {
892                 case 8:  tmp = 0x00; break;
893                 case 16: tmp = 0x05; break;
894                 case 24: tmp = 0x29; break;
895                 case 32: tmp = 0x09; 
896         }
897
898         write3X4(PixelBusReg, tmp);
899
900         tmp = 0x10;
901         if (chipcyber)
902             tmp |= 0x20;
903         write3X4(DRAMControl, tmp);     //both IO,linear enable
904
905         write3X4(InterfaceSel, read3X4(InterfaceSel) | 0x40);
906         write3X4(Performance,0x92);
907         write3X4(PCIReg,0x07);          //MMIO & PCI read and write burst enable
908
909         /* convert from picoseconds to MHz */
910         par->vclk = 1000000/info->var.pixclock;
911         if (bpp == 32)
912                 par->vclk *=2;
913         set_vclk(par->vclk);
914
915         write3C4(0,3);
916         write3C4(1,1);          //set char clock 8 dots wide
917         write3C4(2,0x0F);       //enable 4 maps because needed in chain4 mode
918         write3C4(3,0);
919         write3C4(4,0x0E);       //memory mode enable bitmaps ??
920
921         write3CE(MiscExtFunc,(bpp==32)?0x1A:0x12);      //divide clock by 2 if 32bpp
922                                                         //chain4 mode display and CPU path
923         write3CE(0x5,0x40);     //no CGA compat,allow 256 col
924         write3CE(0x6,0x05);     //graphics mode
925         write3CE(0x7,0x0F);     //planes?
926
927         if (chip_id == CYBERBLADEXPAi1) {
928                 /* This fixes snow-effect in 32 bpp */
929                 write3X4(CRTHSyncStart,0x84);
930         }
931
932         writeAttr(0x10,0x41);   //graphics mode and support 256 color modes
933         writeAttr(0x12,0x0F);   //planes
934         writeAttr(0x13,0);      //horizontal pel panning
935
936         //colors
937         for(tmp = 0;tmp < 0x10;tmp++)
938                 writeAttr(tmp,tmp);
939         readb(par->io_virt + CRT + 0x0A);       //flip-flop to index
940         t_outb(0x20, 0x3C0);                    //enable attr
941
942         switch (bpp) {
943                 case 8: tmp = 0;break;          //256 colors
944                 case 15: tmp = 0x10;break;
945                 case 16: tmp = 0x30;break;      //hicolor
946                 case 24:                        //truecolor
947                 case 32: tmp = 0xD0;break;
948         }
949
950         t_inb(0x3C8);
951         t_inb(0x3C6);
952         t_inb(0x3C6);
953         t_inb(0x3C6);
954         t_inb(0x3C6);
955         t_outb(tmp,0x3C6);
956         t_inb(0x3C8);
957
958         if (flatpanel)
959                 set_number_of_lines(info->var.yres);
960         set_lwidth(info->var.xres * bpp/(4*16));
961         info->fix.visual = (bpp == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
962         info->fix.line_length = info->var.xres * (bpp >> 3);  
963         info->cmap.len = (bpp == 8) ? 256: 16;
964         debug("exit\n");
965         return 0;
966 }
967
968 /* Set one color register */
969 static int tridentfb_setcolreg(unsigned regno, unsigned red, unsigned green,
970                                  unsigned blue, unsigned transp,
971                                  struct fb_info *info)
972 {
973         int bpp = info->var.bits_per_pixel;
974
975         if (regno >= info->cmap.len)
976                 return 1;
977
978
979         if (bpp==8) {
980                 t_outb(0xFF,0x3C6);
981                 t_outb(regno,0x3C8);
982
983                 t_outb(red>>10,0x3C9);
984                 t_outb(green>>10,0x3C9);
985                 t_outb(blue>>10,0x3C9);
986
987         } else if (bpp == 16) { /* RGB 565 */
988                 u32 col;
989
990                 col = (red & 0xF800) | ((green & 0xFC00) >> 5) |
991                         ((blue & 0xF800) >> 11);
992                 col |= col << 16;       
993                 ((u32 *)(info->pseudo_palette))[regno] = col;
994         } else if (bpp == 32)           /* ARGB 8888 */
995                 ((u32*)info->pseudo_palette)[regno] =
996                         ((transp & 0xFF00) <<16)        |
997                         ((red & 0xFF00) << 8)           |
998                         ((green & 0xFF00))              |
999                         ((blue & 0xFF00)>>8);
1000
1001 //      debug("exit\n");
1002         return 0;
1003 }
1004
1005 /* Try blanking the screen.For flat panels it does nothing */
1006 static int tridentfb_blank(int blank_mode, struct fb_info *info)
1007 {
1008         unsigned char PMCont,DPMSCont;
1009
1010         debug("enter\n");
1011         if (flatpanel)
1012                 return 0;
1013         t_outb(0x04,0x83C8); /* Read DPMS Control */
1014         PMCont = t_inb(0x83C6) & 0xFC;
1015         DPMSCont = read3CE(PowerStatus) & 0xFC;
1016         switch (blank_mode)
1017         {
1018         case FB_BLANK_UNBLANK:
1019                 /* Screen: On, HSync: On, VSync: On */
1020         case FB_BLANK_NORMAL:
1021                 /* Screen: Off, HSync: On, VSync: On */
1022                 PMCont |= 0x03;
1023                 DPMSCont |= 0x00;
1024                 break;
1025         case FB_BLANK_HSYNC_SUSPEND:
1026                 /* Screen: Off, HSync: Off, VSync: On */
1027                 PMCont |= 0x02;
1028                 DPMSCont |= 0x01;
1029                 break;
1030         case FB_BLANK_VSYNC_SUSPEND:
1031                 /* Screen: Off, HSync: On, VSync: Off */
1032                 PMCont |= 0x02;
1033                 DPMSCont |= 0x02;
1034                 break;
1035         case FB_BLANK_POWERDOWN:
1036                 /* Screen: Off, HSync: Off, VSync: Off */
1037                 PMCont |= 0x00;
1038                 DPMSCont |= 0x03;
1039                 break;
1040         }
1041
1042         write3CE(PowerStatus,DPMSCont);
1043         t_outb(4,0x83C8);
1044         t_outb(PMCont,0x83C6);
1045
1046         debug("exit\n");
1047
1048         /* let fbcon do a softblank for us */
1049         return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1050 }
1051
1052 static int __devinit trident_pci_probe(struct pci_dev * dev, const struct pci_device_id * id)
1053 {
1054         int err;
1055         unsigned char revision;
1056
1057         err = pci_enable_device(dev);
1058         if (err)
1059                 return err;
1060
1061         chip_id = id->device;
1062
1063         if(chip_id == CYBERBLADEi1)
1064                 output("*** Please do use cyblafb, Cyberblade/i1 support "
1065                        "will soon be removed from tridentfb!\n");
1066
1067
1068         /* If PCI id is 0x9660 then further detect chip type */
1069         
1070         if (chip_id == TGUI9660) {
1071                 outb(RevisionID,0x3C4);
1072                 revision = inb(0x3C5);  
1073         
1074                 switch (revision) {
1075                         case 0x22:
1076                         case 0x23: chip_id = CYBER9397;break;
1077                         case 0x2A: chip_id = CYBER9397DVD;break;
1078                         case 0x30:
1079                         case 0x33:
1080                         case 0x34:
1081                         case 0x35:
1082                         case 0x38:
1083                         case 0x3A:
1084                         case 0xB3: chip_id = CYBER9385;break;
1085                         case 0x40 ... 0x43: chip_id = CYBER9382;break;
1086                         case 0x4A: chip_id = CYBER9388;break;
1087                         default:break;  
1088                 }
1089         }
1090
1091         chip3D = is3Dchip(chip_id);
1092         chipcyber = iscyber(chip_id);
1093
1094         if (is_xp(chip_id)) {
1095                 acc = &accel_xp;
1096         } else 
1097         if (is_blade(chip_id)) {
1098                 acc = &accel_blade;
1099         } else {
1100                 acc = &accel_image;
1101         }
1102
1103         /* acceleration is on by default for 3D chips */
1104         defaultaccel = chip3D && !noaccel;
1105
1106         fb_info.par = &default_par;
1107
1108         /* setup MMIO region */
1109         tridentfb_fix.mmio_start = pci_resource_start(dev,1);
1110         tridentfb_fix.mmio_len = chip3D ? 0x20000:0x10000;
1111
1112         if (!request_mem_region(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len, "tridentfb")) {
1113                 debug("request_region failed!\n");
1114                 return -1;
1115         }
1116
1117         default_par.io_virt = ioremap_nocache(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len);
1118
1119         if (!default_par.io_virt) {
1120                 release_region(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len);
1121                 debug("ioremap failed\n");
1122                 return -1;
1123         }
1124
1125         enable_mmio();
1126         
1127         /* setup framebuffer memory */
1128         tridentfb_fix.smem_start = pci_resource_start(dev,0);
1129         tridentfb_fix.smem_len = get_memsize();
1130         
1131         if (!request_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len, "tridentfb")) {
1132                 debug("request_mem_region failed!\n");
1133                 return -1;
1134         }
1135
1136         fb_info.screen_base = ioremap_nocache(tridentfb_fix.smem_start,
1137                                         tridentfb_fix.smem_len);
1138
1139         if (!fb_info.screen_base) {
1140                 release_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len);
1141                 debug("ioremap failed\n");
1142                 return -1;
1143         }
1144
1145         output("%s board found\n", pci_name(dev));
1146 #if 0   
1147         output("Trident board found : mem = %X,io = %X, mem_v = %X, io_v = %X\n",
1148                 tridentfb_fix.smem_start, tridentfb_fix.mmio_start, fb_info.screen_base, default_par.io_virt);
1149 #endif
1150         displaytype = get_displaytype();
1151
1152         if(flatpanel)
1153                 nativex = get_nativex();
1154
1155         fb_info.fix = tridentfb_fix;
1156         fb_info.fbops = &tridentfb_ops;
1157
1158
1159         fb_info.flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
1160 #ifdef CONFIG_FB_TRIDENT_ACCEL
1161         fb_info.flags |= FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT;
1162 #endif
1163         fb_info.pseudo_palette = pseudo_pal;
1164
1165         if (!fb_find_mode(&default_var,&fb_info,mode,NULL,0,NULL,bpp))
1166                 return -EINVAL;
1167         fb_alloc_cmap(&fb_info.cmap,256,0);
1168         if (defaultaccel && acc)
1169                 default_var.accel_flags |= FB_ACCELF_TEXT;
1170         else
1171                 default_var.accel_flags &= ~FB_ACCELF_TEXT;
1172         default_var.activate |= FB_ACTIVATE_NOW;
1173         fb_info.var = default_var;
1174         fb_info.device = &dev->dev;
1175         if (register_framebuffer(&fb_info) < 0) {
1176                 printk(KERN_ERR "tridentfb: could not register Trident framebuffer\n");
1177                 return -EINVAL;
1178         }
1179         output("fb%d: %s frame buffer device %dx%d-%dbpp\n",
1180            fb_info.node, fb_info.fix.id,default_var.xres,
1181            default_var.yres,default_var.bits_per_pixel);
1182         return 0;
1183 }
1184
1185 static void __devexit trident_pci_remove(struct pci_dev * dev)
1186 {
1187         struct tridentfb_par *par = (struct tridentfb_par*)fb_info.par;
1188         unregister_framebuffer(&fb_info);
1189         iounmap(par->io_virt);
1190         iounmap(fb_info.screen_base);
1191         release_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len);
1192         release_region(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len);
1193 }
1194
1195 /* List of boards that we are trying to support */
1196 static struct pci_device_id trident_devices[] = {
1197         {PCI_VENDOR_ID_TRIDENT, BLADE3D, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1198         {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi7, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1199         {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi7D, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1200         {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi1, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1201         {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi1D, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1202         {PCI_VENDOR_ID_TRIDENT, CYBERBLADEAi1, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1203         {PCI_VENDOR_ID_TRIDENT, CYBERBLADEAi1D, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1204         {PCI_VENDOR_ID_TRIDENT, CYBERBLADEE4, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1205         {PCI_VENDOR_ID_TRIDENT, TGUI9660, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1206         {PCI_VENDOR_ID_TRIDENT, IMAGE975, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1207         {PCI_VENDOR_ID_TRIDENT, IMAGE985, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1208         {PCI_VENDOR_ID_TRIDENT, CYBER9320, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1209         {PCI_VENDOR_ID_TRIDENT, CYBER9388, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1210         {PCI_VENDOR_ID_TRIDENT, CYBER9520, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1211         {PCI_VENDOR_ID_TRIDENT, CYBER9525DVD, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1212         {PCI_VENDOR_ID_TRIDENT, CYBER9397, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1213         {PCI_VENDOR_ID_TRIDENT, CYBER9397DVD, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1214         {PCI_VENDOR_ID_TRIDENT, CYBERBLADEXPAi1, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1215         {PCI_VENDOR_ID_TRIDENT, CYBERBLADEXPm8, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1216         {PCI_VENDOR_ID_TRIDENT, CYBERBLADEXPm16, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1217         {0,}
1218 };      
1219         
1220 MODULE_DEVICE_TABLE(pci,trident_devices); 
1221
1222 static struct pci_driver tridentfb_pci_driver = {
1223         .name           = "tridentfb",
1224         .id_table       = trident_devices,
1225         .probe          = trident_pci_probe,
1226         .remove         = __devexit_p(trident_pci_remove)
1227 };
1228
1229 /*
1230  * Parse user specified options (`video=trident:')
1231  * example:
1232  *      video=trident:800x600,bpp=16,noaccel
1233  */
1234 #ifndef MODULE
1235 static int tridentfb_setup(char *options)
1236 {
1237         char * opt;
1238         if (!options || !*options)
1239                 return 0;
1240         while((opt = strsep(&options,",")) != NULL ) {
1241                 if (!*opt) continue;
1242                 if (!strncmp(opt,"noaccel",7))
1243                         noaccel = 1;
1244                 else if (!strncmp(opt,"fp",2))
1245                         displaytype = DISPLAY_FP;
1246                 else if (!strncmp(opt,"crt",3))
1247                         displaytype = DISPLAY_CRT;
1248                 else if (!strncmp(opt,"bpp=",4))
1249                         bpp = simple_strtoul(opt+4,NULL,0);
1250                 else if (!strncmp(opt,"center",6))
1251                         center = 1;
1252                 else if (!strncmp(opt,"stretch",7))
1253                         stretch = 1;
1254                 else if (!strncmp(opt,"memsize=",8))
1255                         memsize = simple_strtoul(opt+8,NULL,0);
1256                 else if (!strncmp(opt,"memdiff=",8))
1257                         memdiff = simple_strtoul(opt+8,NULL,0);
1258                 else if (!strncmp(opt,"nativex=",8))
1259                         nativex = simple_strtoul(opt+8,NULL,0);
1260                 else
1261                         mode = opt;
1262         }
1263         return 0;
1264 }
1265 #endif
1266
1267 static int __init tridentfb_init(void)
1268 {
1269 #ifndef MODULE
1270         char *option = NULL;
1271
1272         if (fb_get_options("tridentfb", &option))
1273                 return -ENODEV;
1274         tridentfb_setup(option);
1275 #endif
1276         output("Trident framebuffer %s initializing\n", VERSION);
1277         return pci_register_driver(&tridentfb_pci_driver);
1278 }
1279
1280 static void __exit tridentfb_exit(void)
1281 {
1282         pci_unregister_driver(&tridentfb_pci_driver);
1283 }
1284
1285 static struct fb_ops tridentfb_ops = {
1286         .owner  = THIS_MODULE,
1287         .fb_setcolreg = tridentfb_setcolreg,
1288         .fb_pan_display = tridentfb_pan_display,
1289         .fb_blank = tridentfb_blank,
1290         .fb_check_var = tridentfb_check_var,
1291         .fb_set_par = tridentfb_set_par,
1292         .fb_fillrect = tridentfb_fillrect,
1293         .fb_copyarea= tridentfb_copyarea,
1294         .fb_imageblit = cfb_imageblit,
1295 };
1296
1297 module_init(tridentfb_init);
1298 module_exit(tridentfb_exit);
1299
1300 MODULE_AUTHOR("Jani Monoses <jani@iv.ro>");
1301 MODULE_DESCRIPTION("Framebuffer driver for Trident cards");
1302 MODULE_LICENSE("GPL");
1303