2 * Frame buffer driver for Trident Blade and Image series
4 * Copyright 2001,2002 - Jani Monoses <jani@iv.ro>
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
12 * Francesco Salvestrini <salvestrini@users.sf.net> XP support,code,suggestions
14 * timing value tweaking so it looks good on every monitor in every mode
18 #include <linux/config.h>
19 #include <linux/module.h>
21 #include <linux/init.h>
22 #include <linux/pci.h>
24 #include <video/fbcon.h>
25 #include <video/fbcon-cfb8.h>
26 #include <video/fbcon-cfb16.h>
27 #include <video/fbcon-cfb24.h>
28 #include <video/fbcon-cfb32.h>
30 #include "tridentfb.h"
32 #define VERSION "0.7.5"
34 struct tridentfb_par {
35 struct fb_var_screeninfo var;
57 struct tridentfb_info {
58 struct fb_info_gen gen;
59 unsigned long fbmem_virt; //framebuffer virtual memory address
60 unsigned long fbmem; //framebuffer physical memory address
61 unsigned int memsize; //size of fbmem
62 unsigned long io; //io space address
63 unsigned long io_virt; //iospace virtual memory address
64 unsigned int nativex; //flat panel xres
65 struct tridentfb_par currentmode;
66 unsigned char eng_oper; //engine operation...
69 static struct fb_ops tridentfb_ops;
71 static struct tridentfb_info fb_info;
72 static struct display disp;
74 static struct { unsigned char red,green,blue,transp; } palette[256];
76 static struct fb_var_screeninfo default_var;
78 static char * tridentfb_name = "Trident";
82 static int defaultaccel;
83 static int displaytype;
85 static int pseudo_pal[16];
87 /* defaults which are normally overriden by user values */
90 static char * mode = "640x480";
106 MODULE_PARM(mode,"s");
107 MODULE_PARM(bpp,"i");
108 MODULE_PARM(center,"i");
109 MODULE_PARM(stretch,"i");
110 MODULE_PARM(noaccel,"i");
111 MODULE_PARM(memsize,"i");
112 MODULE_PARM(memdiff,"i");
113 MODULE_PARM(nativex,"i");
115 MODULE_PARM(crt,"i");
120 return ((id == BLADE3D) || (id == CYBERBLADEE4) ||
121 (id == CYBERBLADEi7) || (id == CYBERBLADEi7D) ||
122 (id == CYBER9397) || (id == CYBER9397DVD) ||
123 (id == CYBER9520) || (id == CYBER9525DVD) ||
124 (id == IMAGE975) || (id == IMAGE985) ||
125 (id == CYBERBLADEi1) || (id == CYBERBLADEi1D) ||
126 (id == CYBERBLADEAi1) || (id == CYBERBLADEAi1D) ||
127 (id == CYBERBLADEXPm8) || (id == CYBERBLADEXPm16) ||
128 (id == CYBERBLADEXPAi1));
131 #define CRT 0x3D0 //CRTC registers offset for color display
134 #define TRIDENT_MMIO 1
138 #define t_outb(val,reg) writeb(val,fb_info.io_virt + reg)
139 #define t_inb(reg) readb(fb_info.io_virt + reg)
141 #define t_outb(val,reg) outb(val,reg)
142 #define t_inb(reg) inb(reg)
146 static struct accel_switch {
147 void (*init_accel)(int,int);
148 void (*wait_engine)(void);
149 void (*fill_rect)(int,int,int,int,int);
150 void (*copy_rect)(int,int,int,int,int,int);
153 #define writemmr(r,v) writel(v, fb_info.io_virt + r)
154 #define readmmr(r) readl(fb_info.io_virt + r)
159 * Blade specific acceleration.
162 #define point(x,y) ((y)<<16|(x))
172 #define REPL(x) x = x | x<<16
175 static void blade_init_accel(int pitch,int bpp)
177 int v1 = (pitch>>3)<<20;
180 case 8:tmp = 0;break;
181 case 15:tmp = 5;break;
182 case 16:tmp = 1;break;
184 case 32:tmp = 2;break;
198 static void blade_wait_engine(void)
200 while(readmmr(STA) & 0xFA800000);
203 static void blade_fill_rect(int x,int y,int w,int h,int c)
207 writemmr(CMD,0x20000000|1<<19|1<<4|2<<2);
209 writemmr(DR1,point(x,y));
210 writemmr(DR2,point(x+w-1,y+h-1));
213 static void blade_copy_rect(int x1,int y1,int x2,int y2,int w,int h)
218 s2 = point(x1+w-1,y1+h-1);
220 d2 = point(x2+w-1,y2+h-1);
222 if ((y1 > y2) || ((y1 == y2) && (x1 > x2)))
227 writemmr(CMD,0xE0000000|1<<19|1<<4|1<<2|direction);
229 writemmr(SR1,direction?s2:s1);
230 writemmr(SR2,direction?s1:s2);
231 writemmr(DR1,direction?d2:d1);
232 writemmr(DR2,direction?d1:d2);
235 static struct accel_switch accel_blade = {
244 * BladeXP specific acceleration functions
248 #define masked_point(x,y) ((y & 0xffff)<<16|(x & 0xffff))
250 static void xp_init_accel(int pitch,int bpp)
256 case 8: x = 0; break;
257 case 16: x = 1; break;
258 case 24: x = 3; break;
259 case 32: x = 2; break;
262 switch (pitch << (bpp >> 3)) {
264 case 512: x |= 0x00; break;
265 case 1024: x |= 0x04; break;
266 case 2048: x |= 0x08; break;
267 case 4096: x |= 0x0C; break;
272 fb_info.eng_oper = x | 0x40;
275 case 8: tmp = 18; break;
277 case 16: tmp = 19; break;
279 case 32: tmp = 20; break;
289 static void xp_wait_engine(void)
297 busy = t_inb(STA) & 0x80;
301 if (count == 10000000) {
307 t_outb(0x00, 0x2120);
314 static void xp_fill_rect(int x,int y,int w,int h,int c)
316 writemmr(0x2127,ROP_P);
318 writemmr(0x2128,0x4000);
319 writemmr(0x2140,masked_point(h,w));
320 writemmr(0x2138,masked_point(y,x));
322 t_outb(fb_info.eng_oper,0x2125);
325 static void xp_copy_rect(int x1,int y1,int x2,int y2,int w,int h)
328 int x1_tmp, x2_tmp, y1_tmp, y2_tmp;
332 if ((x1 < x2) && (y1 == y2)) {
350 writemmr(0x2128,direction);
351 t_outb(ROP_S,0x2127);
352 writemmr(0x213C,masked_point(y1_tmp,x1_tmp));
353 writemmr(0x2138,masked_point(y2_tmp,x2_tmp));
354 writemmr(0x2140,masked_point(h,w));
358 static struct accel_switch accel_xp = {
367 * Image specific acceleration functions
369 static void image_init_accel(int pitch,int bpp)
373 case 8:tmp = 0;break;
374 case 15:tmp = 5;break;
375 case 16:tmp = 1;break;
377 case 32:tmp = 2;break;
379 writemmr(0x2120, 0xF0000000);
380 writemmr(0x2120, 0x40000000|tmp);
381 writemmr(0x2120, 0x80000000);
382 writemmr(0x2144, 0x00000000);
383 writemmr(0x2148, 0x00000000);
384 writemmr(0x2150, 0x00000000);
385 writemmr(0x2154, 0x00000000);
386 writemmr(0x2120, 0x60000000|(pitch<<16) |pitch);
387 writemmr(0x216C, 0x00000000);
388 writemmr(0x2170, 0x00000000);
389 writemmr(0x217C, 0x00000000);
390 writemmr(0x2120, 0x10000000);
391 writemmr(0x2130, (2047 << 16) | 2047);
394 static void image_wait_engine(void)
396 while(readmmr(0x2164) & 0xF0000000);
399 static void image_fill_rect(int x,int y,int w,int h,int c)
401 writemmr(0x2120,0x80000000);
402 writemmr(0x2120,0x90000000|ROP_S);
406 writemmr(DR1,point(x,y));
407 writemmr(DR2,point(x+w-1,y+h-1));
409 writemmr(0x2124,0x80000000|3<<22|1<<10|1<<9);
412 static void image_copy_rect(int x1,int y1,int x2,int y2,int w,int h)
417 s2 = point(x1+w-1,y1+h-1);
419 d2 = point(x2+w-1,y2+h-1);
421 if ((y1 > y2) || ((y1 == y2) && (x1 >x2)))
424 writemmr(0x2120,0x80000000);
425 writemmr(0x2120,0x90000000|ROP_S);
427 writemmr(SR1,direction?s2:s1);
428 writemmr(SR2,direction?s1:s2);
429 writemmr(DR1,direction?d2:d1);
430 writemmr(DR2,direction?d1:d2);
431 writemmr(0x2124,0x80000000|1<<22|1<<10|1<<7|direction);
435 static struct accel_switch accel_image = {
443 * Accel functions called by the upper layers
446 static void trident_bmove (struct display *p, int sy, int sx,
447 int dy, int dx, int height, int width)
451 width *= fontwidth(p);
454 height *= fontheight(p);
455 acc->copy_rect(sx,sy,dx,dy,width,height);
458 static void trident_clear_helper (int c, struct display *p,
459 int sy, int sx, int height, int width)
463 width *= fontwidth(p);
464 height *= fontheight(p);
465 acc->fill_rect(sx,sy,width,height,c);
470 #ifdef FBCON_HAS_CFB8
471 static void trident_8bpp_clear (struct vc_data *conp, struct display *p,
472 int sy, int sx, int height, int width)
475 c = attr_bgcol_ec(p,conp) & 0xFF;
478 trident_clear_helper(c,p,sy,sx,height,width);
481 static struct display_switch trident_8bpp = {
482 setup: fbcon_cfb8_setup,
483 bmove: trident_bmove,
484 clear: trident_8bpp_clear,
485 putc: fbcon_cfb8_putc,
486 putcs: fbcon_cfb8_putcs,
487 revc: fbcon_cfb8_revc,
488 clear_margins: fbcon_cfb8_clear_margins,
489 fontwidthmask: FONTWIDTH (4) | FONTWIDTH (8) | FONTWIDTH (12) | FONTWIDTH (16)
492 #ifdef FBCON_HAS_CFB16
493 static void trident_16bpp_clear (struct vc_data *conp, struct display *p,
494 int sy, int sx, int height, int width)
497 c = ((u16*)p->dispsw_data)[attr_bgcol_ec(p,conp)];
499 trident_clear_helper(c,p,sy,sx,height,width);
502 static struct display_switch trident_16bpp = {
503 setup: fbcon_cfb16_setup,
504 bmove: trident_bmove,
505 clear: trident_16bpp_clear,
506 putc: fbcon_cfb16_putc,
507 putcs: fbcon_cfb16_putcs,
508 revc: fbcon_cfb16_revc,
509 clear_margins: fbcon_cfb16_clear_margins,
510 fontwidthmask: FONTWIDTH (4) | FONTWIDTH (8) | FONTWIDTH (12) | FONTWIDTH (16)
513 #ifdef FBCON_HAS_CFB32
514 static void trident_32bpp_clear (struct vc_data *conp, struct display *p,
515 int sy, int sx, int height, int width)
518 c = ((u32*)p->dispsw_data)[attr_bgcol_ec(p,conp)];
519 trident_clear_helper(c,p,sy,sx,height,width);
522 static struct display_switch trident_32bpp = {
523 setup: fbcon_cfb32_setup,
524 bmove: trident_bmove,
525 clear: trident_32bpp_clear,
526 putc: fbcon_cfb32_putc,
527 putcs: fbcon_cfb32_putcs,
528 revc: fbcon_cfb32_revc,
529 clear_margins: fbcon_cfb32_clear_margins,
530 fontwidthmask: FONTWIDTH (4) | FONTWIDTH (8) | FONTWIDTH (12) | FONTWIDTH (16)
535 * Hardware access functions
538 static inline unsigned char read3X4(int reg)
540 writeb(reg, fb_info.io_virt + CRT + 4);
541 return readb(fb_info.io_virt + CRT + 5);
544 static inline void write3X4(int reg, unsigned char val)
546 writeb(reg, fb_info.io_virt + CRT + 4);
547 writeb(val, fb_info.io_virt + CRT + 5);
550 static inline unsigned char read3C4(int reg)
556 static inline void write3C4(int reg, unsigned char val)
562 static inline unsigned char read3CE(int reg)
568 static inline void writeAttr(int reg, unsigned char val)
570 readb(fb_info.io_virt + CRT + 0x0A); //flip-flop to index
575 static inline unsigned char readAttr(int reg)
577 readb(fb_info.io_virt + CRT + 0x0A); //flip-flop to index
582 static inline void write3CE(int reg, unsigned char val)
588 #define bios_reg(reg) write3CE(BiosReg, reg)
590 static inline void unprotect_all(void)
592 outb(Protection, 0x3C4);
596 static inline void enable_mmio(void)
602 /* Unprotect registers */
603 outb(NewMode1, 0x3C4);
608 outb(inb(0x3D5) | 0x01, 0x3D5);
612 #define crtc_unlock() write3X4(CRTVSyncEnd, read3X4(CRTVSyncEnd) & 0x7F)
614 /* Return flat panel's maximum x resolution */
615 static int __init get_nativex(void)
622 tmp = (read3CE(VertStretch) >> 4) & 3;
625 case 0: x = 1280; y = 1024; break;
626 case 2: x = 1024; y = 768; break;
627 case 3: x = 800; y = 600; break;
628 case 4: x = 1400; y = 1050; break;
630 default:x = 640; y = 480; break;
633 output("%dx%d flat panel found\n", x, y);
638 static void set_lwidth(int width)
640 write3X4(Offset, width & 0xFF);
641 write3X4(AddColReg, (read3X4(AddColReg) & 0xCF) | ((width & 0x300) >>4));
644 /* For resolutions smaller than FP resolution stretch */
645 static void screen_stretch(void)
647 write3CE(VertStretch,(read3CE(VertStretch) & 0x7C) | 1);
648 write3CE(HorStretch,(read3CE(HorStretch) & 0x7C) | 1);
651 /* For resolutions smaller than FP resolution center */
652 static void screen_center(void)
654 bios_reg(0); // no stretch
655 write3CE(VertStretch,(read3CE(VertStretch) & 0x7C) | 0x80);
656 write3CE(HorStretch,(read3CE(HorStretch) & 0x7C) | 0x80);
659 /* Address of first shown pixel in display memory */
660 static void set_screen_start(int base)
662 write3X4(StartAddrLow, base & 0xFF);
663 write3X4(StartAddrHigh, (base & 0xFF00) >> 8);
664 write3X4(CRTCModuleTest, (read3X4(CRTCModuleTest) & 0xDF) | ((base & 0x10000) >> 11));
665 write3X4(CRTHiOrd, (read3X4(CRTHiOrd) & 0xF8) | ((base & 0xE0000) >> 17));
668 /* Use 20.12 fixed-point for NTSC value and frequency calculation */
669 #define calc_freq(n,m,k) ( ((unsigned long)0xE517 * (n+8) / ((m+2)*(1<<k))) >> 12 )
671 /* Set dotclock frequency */
672 static void set_vclk(int freq)
676 unsigned char lo=0,hi=0;
681 for(n = 0;n<128;n++) {
682 fi = calc_freq(n,m,k);
683 if ((di = abs(fi - freq)) < d) {
691 write3C4(ClockHigh,hi);
692 write3C4(ClockLow,lo);
697 debug("VCLK = %X %X\n",hi,lo);
700 /* Set number of lines for flat panels*/
701 static void set_number_of_lines(int lines)
703 int tmp = read3CE(CyberEnhance) & 0x8F;
706 else if (lines > 600)
708 else if (lines > 480)
710 write3CE(CyberEnhance, tmp);
714 * If we see that FP is active we assume we have one.
715 * Otherwise we have a CRT display.User can override.
717 static unsigned int __init get_displaytype(void)
723 return (read3CE(FPConfig) & 0x10)?DISPLAY_FP:DISPLAY_CRT;
726 /* Try detecting the video memory size */
727 static unsigned int __init get_memsize(void)
729 unsigned char tmp, tmp2;
732 /* If memory size provided by user */
737 case CYBER9525DVD: k = 2560 * Kb; break;
739 tmp = read3X4(SPR) & 0x0F;
742 case 0x01: k = 512; break;
743 case 0x02: k = 6 * Mb; break; /* XP */
744 case 0x03: k = 1 * Mb; break;
745 case 0x04: k = 8 * Mb; break;
746 case 0x06: k = 10 * Mb; break; /* XP */
747 case 0x07: k = 2 * Mb; break;
748 case 0x08: k = 12 * Mb; break; /* XP */
749 case 0x0A: k = 14 * Mb; break; /* XP */
750 case 0x0C: k = 16 * Mb; break; /* XP */
753 tmp2 = read3C4(0xC1);
755 case 0x00: k = 20 * Mb; break;
756 case 0x01: k = 24 * Mb; break;
757 case 0x10: k = 28 * Mb; break;
758 case 0x11: k = 32 * Mb; break;
759 default: k = 1 * Mb; break;
763 case 0x0F: k = 4 * Mb; break;
769 output("framebuffer size = %d Kb\n", k/Kb);
774 static int trident_encode_fix(struct fb_fix_screeninfo *fix,
776 struct fb_info_gen *info)
778 struct tridentfb_info * i = (struct tridentfb_info *)info;
779 struct tridentfb_par * p = (struct tridentfb_par *)par;
783 memset(fix, 0, sizeof(struct fb_fix_screeninfo));
784 strcpy(fix->id,tridentfb_name);
786 fix->smem_start = i->fbmem;
787 fix->smem_len = i->memsize;
789 fix->type = FB_TYPE_PACKED_PIXELS;
792 fix->visual = p->bpp==8 ? FB_VISUAL_PSEUDOCOLOR:FB_VISUAL_TRUECOLOR;
794 fix->xpanstep = fix->ywrapstep = 0;
796 fix->line_length = p->linelength;
800 fix->accel = FB_ACCEL_NONE;
806 /* Fill in par from var */
807 static int trident_decode_var(const struct fb_var_screeninfo *var,
809 struct fb_info_gen *info)
811 struct tridentfb_par * p = (struct tridentfb_par *)par;
812 struct tridentfb_info * i = (struct tridentfb_info *)info;
813 int vres,vfront,vback,vsync;
816 p->bpp = var->bits_per_pixel;
821 p->linelength = var->xres_virtual * p->bpp/8;
825 p->var.red.offset = 0;
826 p->var.green.offset = 0;
827 p->var.blue.offset = 0;
828 p->var.red.length = 6;
829 p->var.green.length = 6;
830 p->var.blue.length = 6;
833 p->var.red.offset = 11;
834 p->var.green.offset = 5;
835 p->var.blue.offset = 0;
836 p->var.red.length = 5;
837 p->var.green.length = 6;
838 p->var.blue.length = 5;
841 p->var.red.offset = 16;
842 p->var.green.offset = 8;
843 p->var.blue.offset = 0;
844 p->var.red.length = 8;
845 p->var.green.length = 8;
846 p->var.blue.length = 8;
852 /* convert from picoseconds to MHz */
853 p->vclk = 1000000/var->pixclock;
859 vres = p->vres = var->yres;
861 /* See if requested resolution is larger than flat panel */
862 if (p->hres > i->nativex && flatpanel) {
866 /* See if requested resolution fits in available memory */
867 if (p->hres * p->vres * p->bpp/8 > i->memsize) {
871 vfront = var->upper_margin;
872 vback = var->lower_margin;
873 vsync = var->vsync_len;
875 /* Compute horizontal and vertical VGA CRTC timing values */
876 if (var->vmode & FB_VMODE_INTERLACED) {
883 if (var->vmode & FB_VMODE_DOUBLE) {
890 p->htotal = (p->hres + var->left_margin + var->right_margin + var->hsync_len)/8 - 10;
891 p->hdispend = p->hres/8 - 1;
892 p->hsyncstart = (p->hres + var->right_margin)/8;
893 p->hsyncend = var->hsync_len/8;
894 p->hblankstart = p->hdispend + 1;
895 p->hblankend = p->htotal + 5;
897 p->vtotal = vres + vfront + vback + vsync - 2;
898 p->vdispend = vres - 1;
899 p->vsyncstart = vres + vback;
901 p->vblankstart = vres;
902 p->vblankend = p->vtotal + 2;
910 /* Fill in var from info */
911 static int trident_encode_var(struct fb_var_screeninfo *var,
913 struct fb_info_gen *info)
915 struct tridentfb_par * p = (struct tridentfb_par *)par;
918 var->bits_per_pixel = p->bpp;
923 /* Fill in par from hardware */
924 static void trident_get_par(void *par, struct fb_info_gen *info)
926 struct tridentfb_par * p = (struct tridentfb_par *)par;
927 struct tridentfb_info * i = (struct tridentfb_info *)info;
934 /* Pan the display */
935 static int trident_pan_display(const struct fb_var_screeninfo *var,
936 struct fb_info_gen *info)
939 struct tridentfb_info * i = (struct tridentfb_info *)info;
943 offset = (var->xoffset + (var->yoffset * var->xres))
944 * var->bits_per_pixel/32;
945 i->currentmode.var.xoffset = var->xoffset;
946 i->currentmode.var.yoffset = var->yoffset;
947 set_screen_start(offset);
952 /* Set the hardware from par */
953 static void trident_set_par(const void *par, struct fb_info_gen *info)
955 struct tridentfb_par * p = (struct tridentfb_par *)par;
956 struct tridentfb_info * i = (struct tridentfb_info *)info;
964 write3CE(CyberControl,8);
965 if (flatpanel && p->hres < i->nativex) {
967 * on flat panels with native size larger
968 * than requested resolution decide whether
969 * we stretch or center
972 write3CE(CyberControl,0x81);
980 write3CE(CyberControl,8);
983 /* vertical timing values */
984 write3X4(CRTVTotal, p->vtotal & 0xFF);
985 write3X4(CRTVDispEnd, p->vdispend & 0xFF);
986 write3X4(CRTVSyncStart, p->vsyncstart & 0xFF);
987 write3X4(CRTVSyncEnd, (p->vsyncend & 0x0F));
988 write3X4(CRTVBlankStart, p->vblankstart & 0xFF);
989 write3X4(CRTVBlankEnd, 0/*p->vblankend & 0xFF*/);
991 /* horizontal timing values */
992 write3X4(CRTHTotal, p->htotal & 0xFF);
993 write3X4(CRTHDispEnd, p->hdispend & 0xFF);
994 write3X4(CRTHSyncStart, p->hsyncstart & 0xFF);
995 write3X4(CRTHSyncEnd, (p->hsyncend & 0x1F) | ((p->hblankend & 0x20)<<2));
996 write3X4(CRTHBlankStart, p->hblankstart & 0xFF);
997 write3X4(CRTHBlankEnd, 0/*(p->hblankend & 0x1F)*/);
999 /* higher bits of vertical timing values */
1001 if (p->vtotal & 0x100) tmp |= 0x01;
1002 if (p->vdispend & 0x100) tmp |= 0x02;
1003 if (p->vsyncstart & 0x100) tmp |= 0x04;
1004 if (p->vblankstart & 0x100) tmp |= 0x08;
1006 if (p->vtotal & 0x200) tmp |= 0x20;
1007 if (p->vdispend & 0x200) tmp |= 0x40;
1008 if (p->vsyncstart & 0x200) tmp |= 0x80;
1010 write3X4(CRTOverflow, tmp);
1013 tmp = read3X4(CRTHiOrd) | 0x08; //line compare bit 10
1015 if (p->vtotal & 0x400) tmp |= 0x80;
1016 if (p->vblankstart & 0x400) tmp |= 0x40;
1017 if (p->vsyncstart & 0x400) tmp |= 0x20;
1018 if (p->vdispend & 0x400) tmp |= 0x10;
1019 write3X4(CRTHiOrd, tmp);
1021 write3X4(HorizOverflow, 0);
1024 if (p->vblankstart & 0x200) tmp |= 0x20;
1025 if (p->var.vmode & FB_VMODE_DOUBLE) tmp |= 0x80; //double scan for 200 line modes
1026 write3X4(CRTMaxScanLine, tmp);
1027 write3X4(CRTLineCompare,0xFF);
1028 write3X4(CRTPRowScan,0);
1029 write3X4(CRTModeControl,0xC3);
1030 write3X4(LinearAddReg,0x20); //enable linear addressing
1031 tmp = (p->var.vmode & FB_VMODE_INTERLACED) ? 0x84:0x80;
1032 write3X4(CRTCModuleTest,tmp); //enable access extended memory
1033 write3X4(GraphEngReg, 0x80); //enable GE for text acceleration
1035 if (p->var.accel_flags & FB_ACCELF_TEXT)
1036 acc->init_accel(p->hres,p->bpp);
1040 case 16:tmp=5;break;
1042 /* tmp=0x29;break; */
1043 /* seems like 24bpp is same as 32bpp when using vesafb */
1044 case 32:tmp=9;break;
1046 write3X4(PixelBusReg, tmp);
1048 write3X4(InterfaceSel, 0x5B); //32bit internal data path
1049 write3X4(DRAMControl, 0x30); //both IO,linear enable
1050 write3X4(Performance, 0xBF);
1051 write3X4(PCIReg,0x07); //MMIO & PCI read and write burst enable
1056 write3C4(1,1); //set char clock 8 dots wide
1057 write3C4(2,0x0F); //enable 4 maps because needed in chain4 mode
1059 write3C4(4,0x0E); //memory mode enable bitmaps ??
1061 write3CE(MiscExtFunc,(p->bpp==32)?0x1A:0x12); //divide clock by 2 if 32bpp
1062 //chain4 mode display and CPU path
1063 write3CE(0x5,0x40); //no CGA compat,allow 256 col
1064 write3CE(0x6,0x05); //graphics mode
1065 write3CE(0x7,0x0F); //planes?
1067 writeAttr(0x10,0x41); //graphics mode and support 256 color modes
1068 writeAttr(0x12,0x0F); //planes
1069 writeAttr(0x13,0); //horizontal pel panning
1072 for(tmp = 0;tmp < 0x10;tmp++)
1074 readb(fb_info.io_virt + CRT + 0x0A); //flip-flop to index
1075 t_outb(0x20, 0x3C0); //enable attr
1078 case 8: tmp = 0;break; //256 colors
1079 case 15: tmp = 0x10;break;
1080 case 16: tmp = 0x30;break; //hicolor
1081 case 24: //truecolor
1082 case 32: tmp = 0xD0;break;
1094 set_number_of_lines(p->vres);
1095 set_lwidth(p->hres*p->bpp/(4*16));
1097 trident_pan_display(&p->var,info);
1101 /* Get value of one color register */
1102 static int trident_getcolreg(unsigned regno, unsigned *red,
1103 unsigned *green, unsigned *blue,
1104 unsigned *transp, struct fb_info *info)
1106 struct tridentfb_info * i = (struct tridentfb_info *)info;
1107 int m = i->currentmode.bpp==8?256:16;
1109 debug("enter %d\n",regno);
1114 *red = palette[regno].red;
1115 *green = palette[regno].green;
1116 *blue = palette[regno].blue;
1117 *transp = palette[regno].transp;
1123 /* Set one color register */
1124 static int trident_setcolreg(unsigned regno, unsigned red, unsigned green,
1125 unsigned blue, unsigned transp,
1126 struct fb_info *info)
1128 struct tridentfb_info * i = (struct tridentfb_info *)info;
1129 int bpp = i->currentmode.bpp;
1130 int m = bpp==8?256:16;
1132 debug("enter %d\n",regno);
1137 palette[regno].red = red;
1138 palette[regno].green = green;
1139 palette[regno].blue = blue;
1140 palette[regno].transp = transp;
1144 t_outb(regno,0x3C8);
1146 t_outb(red>>10,0x3C9);
1147 t_outb(green>>10,0x3C9);
1148 t_outb(blue>>10,0x3C9);
1151 if (bpp == 16) /* RGB 565 */
1152 ((u16*)info->pseudo_palette)[regno] = (red & 0xF800) |
1153 ((green & 0xFC00) >> 5) | ((blue & 0xF800) >> 11);
1155 if (bpp == 32) /* ARGB 8888 */
1156 ((u32*)info->pseudo_palette)[regno] =
1157 ((transp & 0xFF00) <<16) |
1158 ((red & 0xFF00) << 8) |
1159 ((green & 0xFF00)) |
1160 ((blue & 0xFF00)>>8);
1166 /* Try blanking the screen.For flat panels it does nothing */
1167 static int trident_blank(int blank_mode, struct fb_info_gen *info)
1169 unsigned char PMCont,DPMSCont;
1174 t_outb(0x04,0x83C8); /* Read DPMS Control */
1175 PMCont = t_inb(0x83C6) & 0xFC;
1176 DPMSCont = read3CE(PowerStatus) & 0xFC;
1179 case VESA_NO_BLANKING:
1180 /* Screen: On, HSync: On, VSync: On */
1184 case VESA_HSYNC_SUSPEND:
1185 /* Screen: Off, HSync: Off, VSync: On */
1189 case VESA_VSYNC_SUSPEND:
1190 /* Screen: Off, HSync: On, VSync: Off */
1194 case VESA_POWERDOWN:
1195 /* Screen: Off, HSync: Off, VSync: Off */
1201 write3CE(PowerStatus,DPMSCont);
1203 t_outb(PMCont,0x83C6);
1209 /* Set display switch used by console */
1210 static void trident_set_disp(const void *par, struct display *disp,
1211 struct fb_info_gen *info)
1213 struct tridentfb_info * i = (struct tridentfb_info *)info;
1214 struct fb_info * ii = (struct fb_info *)info;
1215 struct tridentfb_par * p = (struct tridentfb_par *)par;
1216 int isaccel = p->var.accel_flags & FB_ACCELF_TEXT;
1218 disp->screen_base = (char *)i->fbmem_virt;
1220 #ifdef FBCON_HAS_CFB8
1223 disp->dispsw = &trident_8bpp;
1225 disp->dispsw = &fbcon_cfb8;
1228 #ifdef FBCON_HAS_CFB16
1231 disp->dispsw = &trident_16bpp;
1233 disp->dispsw = &fbcon_cfb16;
1234 disp->dispsw_data =ii->pseudo_palette; /* console palette */
1237 #ifdef FBCON_HAS_CFB32
1240 disp->dispsw = &trident_32bpp;
1242 disp->dispsw = &fbcon_cfb32;
1243 disp->dispsw_data =ii->pseudo_palette; /* console palette */
1246 disp->dispsw = &fbcon_dummy;
1250 static struct fbgen_hwswitch trident_hwswitch = {
1251 NULL, /* detect not needed */
1259 trident_pan_display,
1264 static int trident_iosize;
1266 static int __devinit trident_pci_probe(struct pci_dev * dev, const struct pci_device_id * id)
1269 unsigned char revision;
1271 err = pci_enable_device(dev);
1275 chip_id = id->device;
1277 /* If PCI id is 0x9660 then further detect chip type */
1279 if (chip_id == TGUI9660) {
1280 outb(RevisionID,0x3C4);
1281 revision = inb(0x3C5);
1285 case 0x23: chip_id = CYBER9397;break;
1286 case 0x2A: chip_id = CYBER9397DVD;break;
1293 case 0xB3: chip_id = CYBER9385;break;
1294 case 0x40 ... 0x43: chip_id = CYBER9382;break;
1295 case 0x4A: chip_id = CYBER9388;break;
1300 chip3D = is3Dchip(chip_id);
1302 if (is_xp(chip_id)) {
1305 if (is_blade(chip_id)) {
1311 /* acceleration is on by default for 3D chips */
1312 defaultaccel = chip3D && !noaccel;
1314 fb_info.io = pci_resource_start(dev,1);
1315 fb_info.fbmem = pci_resource_start(dev,0);
1317 trident_iosize = chip3D ? 0x20000:0x10000;
1319 if (!request_mem_region(fb_info.io, trident_iosize, "tridentfb")) {
1320 debug("request_region failed!\n");
1324 fb_info.io_virt = (unsigned long)ioremap_nocache(fb_info.io, trident_iosize);
1326 if (!fb_info.io_virt) {
1327 release_region(fb_info.io, trident_iosize);
1328 debug("ioremap failed\n");
1334 fb_info.memsize = get_memsize();
1335 if (!request_mem_region(fb_info.fbmem, fb_info.memsize, "tridentfb")) {
1336 debug("request_mem_region failed!\n");
1340 fb_info.fbmem_virt = (unsigned long)ioremap_nocache(fb_info.fbmem, fb_info.memsize);
1342 if (!fb_info.fbmem_virt) {
1343 release_mem_region(fb_info.fbmem, fb_info.memsize);
1344 debug("ioremap failed\n");
1348 output("%s board found\n", dev->name);
1349 debug("Trident board found : mem = %X,io = %X, mem_v = %X, io_v = %X\n",
1350 fb_info.fbmem, fb_info.io, fb_info.fbmem_virt, fb_info.io_virt);
1352 fb_info.gen.parsize = sizeof (struct tridentfb_par);
1353 fb_info.gen.fbhw = &trident_hwswitch;
1355 strcpy(fb_info.gen.info.modename, tridentfb_name);
1356 displaytype = get_displaytype();
1359 fb_info.nativex = get_nativex();
1361 fb_info.gen.info.changevar = NULL;
1362 fb_info.gen.info.node = NODEV;
1363 fb_info.gen.info.fbops = &tridentfb_ops;
1364 fb_info.gen.info.disp = &disp;
1366 fb_info.gen.info.switch_con = &fbgen_switch;
1367 fb_info.gen.info.updatevar = &fbgen_update_var;
1368 fb_info.gen.info.blank = &fbgen_blank;
1370 fb_info.gen.info.flags = FBINFO_FLAG_DEFAULT;
1371 fb_info.gen.info.fontname[0] = '\0';
1372 fb_info.gen.info.pseudo_palette = pseudo_pal;
1374 /* This should give a reasonable default video mode */
1375 fb_find_mode(&default_var,&fb_info.gen.info,mode,NULL,0,NULL,bpp);
1377 if (defaultaccel && acc)
1378 default_var.accel_flags |= FB_ACCELF_TEXT;
1380 default_var.accel_flags &= ~FB_ACCELF_TEXT;
1382 trident_decode_var(&default_var, &fb_info.currentmode, &fb_info.gen);
1383 fbgen_get_var(&disp.var, -1, &fb_info.gen.info);
1384 default_var.activate |= FB_ACTIVATE_NOW;
1385 fbgen_set_disp(-1, &fb_info.gen);
1387 if (register_framebuffer(&fb_info.gen.info) < 0) {
1388 printk("Could not register Trident framebuffer\n");
1392 output("fb%d: %s frame buffer device %dx%d-%dbpp\n",
1393 GET_FB_IDX(fb_info.gen.info.node), fb_info.gen.info.modename,default_var.xres,
1394 default_var.yres,default_var.bits_per_pixel);
1398 static void __devexit trident_pci_remove(struct pci_dev * dev)
1400 unregister_framebuffer(&fb_info.gen.info);
1401 iounmap((void *)fb_info.io_virt);
1402 iounmap((void *)fb_info.fbmem_virt);
1403 release_mem_region(fb_info.fbmem, fb_info.memsize);
1404 release_region(fb_info.io, trident_iosize);
1407 /* List of boards that we are trying to support */
1408 static struct pci_device_id trident_devices[] __devinitdata = {
1409 {PCI_VENDOR_ID_TRIDENT, BLADE3D, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1410 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi7, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1411 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi7D, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1412 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi1, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1413 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi1D, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1414 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEAi1, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1415 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEAi1D, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1416 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEE4, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1417 {PCI_VENDOR_ID_TRIDENT, TGUI9660, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1418 {PCI_VENDOR_ID_TRIDENT, IMAGE975, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1419 {PCI_VENDOR_ID_TRIDENT, IMAGE985, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1420 {PCI_VENDOR_ID_TRIDENT, CYBER9320, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1421 {PCI_VENDOR_ID_TRIDENT, CYBER9388, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1422 {PCI_VENDOR_ID_TRIDENT, CYBER9520, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1423 {PCI_VENDOR_ID_TRIDENT, CYBER9525DVD, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1424 {PCI_VENDOR_ID_TRIDENT, CYBER9397, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1425 {PCI_VENDOR_ID_TRIDENT, CYBER9397DVD, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1426 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEXPAi1, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1427 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEXPm8, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1428 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEXPm16, PCI_ANY_ID,PCI_ANY_ID,0,0,0},
1432 MODULE_DEVICE_TABLE(pci,trident_devices);
1434 static struct pci_driver tridentfb_pci_driver = {
1436 id_table:trident_devices,
1437 probe:trident_pci_probe,
1438 remove:__devexit_p(trident_pci_remove),
1441 int __init tridentfb_init(void)
1443 output("Trident framebuffer %s initializing\n", VERSION);
1444 return pci_module_init(&tridentfb_pci_driver);
1447 void __exit tridentfb_exit(void)
1449 pci_unregister_driver(&tridentfb_pci_driver);
1454 * Parse user specified options (`video=trident:')
1456 * video=trident:800x600,bpp=16,noaccel
1458 int tridentfb_setup(char *options)
1461 if (!options || !*options)
1463 while((opt = strsep(&options,",")) != NULL ) {
1464 if (!*opt) continue;
1465 if (!strncmp(opt,"noaccel",7))
1467 else if (!strncmp(opt,"fp",2))
1468 displaytype = DISPLAY_FP;
1469 else if (!strncmp(opt,"crt",3))
1470 displaytype = DISPLAY_CRT;
1471 else if (!strncmp(opt,"bpp=",4))
1472 bpp = simple_strtoul(opt+4,NULL,0);
1473 else if (!strncmp(opt,"center",6))
1475 else if (!strncmp(opt,"stretch",7))
1477 else if (!strncmp(opt,"memsize=",8))
1478 memsize = simple_strtoul(opt+8,NULL,0);
1479 else if (!strncmp(opt,"memdiff=",8))
1480 memdiff = simple_strtoul(opt+8,NULL,0);
1481 else if (!strncmp(opt,"nativex=",8))
1482 nativex = simple_strtoul(opt+8,NULL,0);
1489 static struct fb_ops tridentfb_ops = {
1490 fb_get_fix:fbgen_get_fix,
1491 fb_get_var:fbgen_get_var,
1492 fb_set_var:fbgen_set_var,
1493 fb_get_cmap:fbgen_get_cmap,
1494 fb_set_cmap:fbgen_set_cmap,
1495 fb_pan_display:fbgen_pan_display,
1499 module_init(tridentfb_init);
1501 module_exit(tridentfb_exit);
1503 MODULE_AUTHOR("Jani Monoses <jani@iv.ro>");
1504 MODULE_DESCRIPTION("Framebuffer driver for Trident cards");
1505 MODULE_LICENSE("GPL");