/* * Trio64 driver for x86. Most of this code originates from Amiga Cybervision * driver, and some from the Amiga S3 driver, but has been modified to work * in a x86/PCI environment, and after PC BIOS has trashed several registers * Cybervision apparently doesn't need to touch. Slight code cleanup has * been done, and a rewrite of the PLL value calculation routine. * Initially I wanted to do a platform-independent version of the Cybervision * driver, but the debugging process got too complicated (and I don't have * an Amiga to test it), so I ended up with a x86-only version. * * So far, acceleration works only through programmed i/o, which means no * memory mapping of acceleration engine registers, which means no support * for userland graphics acceleration, which actually sucks. As far as my * documentation tells me, Trio supports mmio in a fixed memory area (from * 0xA8000 to 0xAFFFF), but apparently (reading from the sources) the * Cybervision implementation behaves differently. Anyone willing to * clarify this? * * Version: 0.1 (initial release), released 1999-09-17 * BUGS/TODO: * - no mmaping of acceleration registers * - poor support for 16 bpp, no support for 24bpp * - trio32 support * - modularization * - virge support (and rename the driver ;-) ? * - hwcursor support? * - more options? * - where does that white bg come from in the beginning? * I have a hunch fbcon does something I'm not prepared for... * * -- Hannu Mallat <hmallat@cs.hut.fi> */ /*#include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/tty.h> #include <linux/malloc.h> #include <linux/delay.h> #include <linux/fb.h> #include <linux/init.h> #include <asm/uaccess.h> #include <asm/system.h> #include <asm/irq.h> #include <asm/pgtable.h> #include <asm/io.h> #include <linux/ioport.h> #include <linux/pci.h> #include <video/fbcon.h> #include <video/fbcon-cfb8.h> #include <video/fbcon-cfb16.h>*/ #define TRIO_ACCEL #include <string.h> #include <stdio.h> #include "fb.h" #include "driver.h" #include "pci.h" void __declspec(naked) _ftol() { __asm { push ebp mov ebp,esp add esp,0F4h wait fnstcw word ptr [ebp-2] wait mov ax,word ptr [ebp-2] or ah,0Ch mov word ptr [ebp-4],ax fldcw word ptr [ebp-4] fistp qword ptr [ebp-0Ch] fldcw word ptr [ebp-2] mov eax,dword ptr [ebp-0Ch] mov edx,dword ptr [ebp-8] leave ret } } #define __initdata #define writeb(val, addr) (*((byte*) (addr)) = val) #define PCI_VENDOR_ID_S3 0x5333 #define PCI_DEVICE_ID_S3_TRIO 0x8811 #define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL) int _fltused = 0x9875; /* s3 commands */ #define S3_BITBLT 0xc011 #define S3_TWOPOINTLINE 0x2811 #define S3_FILLEDRECT 0x40b1 /* Drawing modes */ #define S3_NOTCUR 0x0000 #define S3_LOGICALZERO 0x0001 #define S3_LOGICALONE 0x0002 #define S3_LEAVEASIS 0x0003 #define S3_NOTNEW 0x0004 #define S3_CURXORNEW 0x0005 #define S3_NOT_CURXORNEW 0x0006 #define S3_NEW 0x0007 #define S3_NOTCURORNOTNEW 0x0008 #define S3_CURORNOTNEW 0x0009 #define S3_NOTCURORNEW 0x000a #define S3_CURORNEW 0x000b #define S3_CURANDNEW 0x000c #define S3_NOTCURANDNEW 0x000d #define S3_CURANDNOTNEW 0x000e #define S3_NOTCURANDNOTNEW 0x000f /* stuff... */ #define S3_FIFO_EMPTY 0x0400 #define S3_HDW_BUSY 0x0200 #define S3_VIDEO_SUBS_ENABLE 0x46e8 #define S3_OPTION_SELECT 0x0102 #define S3_SUBSYS_CNTL 0x42e8 #define S3_ADVFUNC_CNTL 0x4ae8 #define S3_CUR_Y 0x82e8 #define S3_CUR_Y2 0x82ea #define S3_CUR_X 0x86e8 #define S3_CUR_X2 0x86ea #define S3_DESTY_AX 0x8ae8 #define S3_DESTY2_AX2 0x8AEA #define S3_DESTX_DIA 0x8ee8 #define S3_DESTX2_DIA2 0x8EEA #define S3_ERR_TERM 0x92e8 #define S3_READ_REG_DATA 0xbee8 #define S3_READ_SEL 0xbee8 /* offset f */ #define S3_MULT_MISC 0xbee8 /* offset e */ #define S3_MULT_MISC_2 0xbee8 /* offset d */ #define S3_FRGD_COLOR 0xa6e8 #define S3_BKGD_COLOR 0xa2e8 #define S3_PIXEL_CNTL 0xbee8 /* offset a */ #define S3_FRGD_MIX 0xbae8 #define S3_BKGD_MIX 0xb6e8 #define S3_DEST_Y_AX 0x8AE8 #define S3_DEST_X_DIA 0x8EE8 #define S3_MIN_AXIS_PCNT 0xbee8 /* offset 0 */ #define S3_MAJ_AXIS_PCNT 0x96e8 #define S3_CMD 0x9ae8 #define S3_GP_STAT 0x9ae8 #define S3_WRT_MASK 0xaae8 #define S3_RD_MASK 0xaee8 #define S3_SHORT_STROKE 0x9EE8 /* General Registers: */ #define GREG_MISC_OUTPUT_R 0x03CC #define GREG_MISC_OUTPUT_W 0x03C2 #define GREG_FEATURE_CONTROL_R 0x03CA #define GREG_FEATURE_CONTROL_W 0x03DA #define GREG_INPUT_STATUS0_R 0x03C2 #define GREG_INPUT_STATUS1_R 0x03DA /* Attribute Controller: */ #define ACT_ADDRESS 0x03C0 #define ACT_ADDRESS_R 0x03C1 #define ACT_ADDRESS_W 0x03C0 #define ACT_ADDRESS_RESET 0x03DA #define ACT_ID_PALETTE0 0x00 #define ACT_ID_PALETTE1 0x01 #define ACT_ID_PALETTE2 0x02 #define ACT_ID_PALETTE3 0x03 #define ACT_ID_PALETTE4 0x04 #define ACT_ID_PALETTE5 0x05 #define ACT_ID_PALETTE6 0x06 #define ACT_ID_PALETTE7 0x07 #define ACT_ID_PALETTE8 0x08 #define ACT_ID_PALETTE9 0x09 #define ACT_ID_PALETTE10 0x0A #define ACT_ID_PALETTE11 0x0B #define ACT_ID_PALETTE12 0x0C #define ACT_ID_PALETTE13 0x0D #define ACT_ID_PALETTE14 0x0E #define ACT_ID_PALETTE15 0x0F #define ACT_ID_ATTR_MODE_CNTL 0x10 #define ACT_ID_OVERSCAN_COLOR 0x11 #define ACT_ID_COLOR_PLANE_ENA 0x12 #define ACT_ID_HOR_PEL_PANNING 0x13 #define ACT_ID_COLOR_SELECT 0x14 /* Graphics Controller: */ #define GCT_ADDRESS 0x03CE #define GCT_ADDRESS_R 0x03CF #define GCT_ADDRESS_W 0x03CF #define GCT_ID_SET_RESET 0x00 #define GCT_ID_ENABLE_SET_RESET 0x01 #define GCT_ID_COLOR_COMPARE 0x02 #define GCT_ID_DATA_ROTATE 0x03 #define GCT_ID_READ_MAP_SELECT 0x04 #define GCT_ID_GRAPHICS_MODE 0x05 #define GCT_ID_MISC 0x06 #define GCT_ID_COLOR_XCARE 0x07 #define GCT_ID_BITMASK 0x08 /* Sequencer: */ #define SEQ_ADDRESS 0x03C4 #define SEQ_ADDRESS_R 0x03C5 #define SEQ_ADDRESS_W 0x03C5 #define SEQ_ID_RESET 0x00 #define SEQ_ID_CLOCKING_MODE 0x01 #define SEQ_ID_MAP_MASK 0x02 #define SEQ_ID_CHAR_MAP_SELECT 0x03 #define SEQ_ID_MEMORY_MODE 0x04 #define SEQ_ID_UNKNOWN1 0x05 #define SEQ_ID_UNKNOWN2 0x06 #define SEQ_ID_UNKNOWN3 0x07 /* S3 extensions */ #define SEQ_ID_UNLOCK_EXT 0x08 #define SEQ_ID_EXT_SEQ_REG9 0x09 #define SEQ_ID_BUS_REQ_CNTL 0x0A #define SEQ_ID_EXT_MISC_SEQ 0x0B #define SEQ_ID_UNKNOWN4 0x0C #define SEQ_ID_EXT_SEQ 0x0D #define SEQ_ID_UNKNOWN5 0x0E #define SEQ_ID_UNKNOWN6 0x0F #define SEQ_ID_MCLK_LO 0x10 #define SEQ_ID_MCLK_HI 0x11 #define SEQ_ID_DCLK_LO 0x12 #define SEQ_ID_DCLK_HI 0x13 #define SEQ_ID_CLKSYN_CNTL_1 0x14 #define SEQ_ID_CLKSYN_CNTL_2 0x15 #define SEQ_ID_CLKSYN_TEST_HI 0x16 /* reserved for S3 testing of the */ #define SEQ_ID_CLKSYN_TEST_LO 0x17 /* internal clock synthesizer */ #define SEQ_ID_RAMDAC_CNTL 0x18 #define SEQ_ID_MORE_MAGIC 0x1A /* CRT Controller: */ #define CRT_ADDRESS 0x03D4 #define CRT_ADDRESS_R 0x03D5 #define CRT_ADDRESS_W 0x03D5 #define CRT_ID_HOR_TOTAL 0x00 #define CRT_ID_HOR_DISP_ENA_END 0x01 #define CRT_ID_START_HOR_BLANK 0x02 #define CRT_ID_END_HOR_BLANK 0x03 #define CRT_ID_START_HOR_RETR 0x04 #define CRT_ID_END_HOR_RETR 0x05 #define CRT_ID_VER_TOTAL 0x06 #define CRT_ID_OVERFLOW 0x07 #define CRT_ID_PRESET_ROW_SCAN 0x08 #define CRT_ID_MAX_SCAN_LINE 0x09 #define CRT_ID_CURSOR_START 0x0A #define CRT_ID_CURSOR_END 0x0B #define CRT_ID_START_ADDR_HIGH 0x0C #define CRT_ID_START_ADDR_LOW 0x0D #define CRT_ID_CURSOR_LOC_HIGH 0x0E #define CRT_ID_CURSOR_LOC_LOW 0x0F #define CRT_ID_START_VER_RETR 0x10 #define CRT_ID_END_VER_RETR 0x11 #define CRT_ID_VER_DISP_ENA_END 0x12 #define CRT_ID_SCREEN_OFFSET 0x13 #define CRT_ID_UNDERLINE_LOC 0x14 #define CRT_ID_START_VER_BLANK 0x15 #define CRT_ID_END_VER_BLANK 0x16 #define CRT_ID_MODE_CONTROL 0x17 #define CRT_ID_LINE_COMPARE 0x18 #define CRT_ID_GD_LATCH_RBACK 0x22 #define CRT_ID_ACT_TOGGLE_RBACK 0x24 #define CRT_ID_ACT_INDEX_RBACK 0x26 /* S3 extensions: S3 VGA Registers */ #define CRT_ID_DEVICE_HIGH 0x2D #define CRT_ID_DEVICE_LOW 0x2E #define CRT_ID_REVISION 0x2F #define CRT_ID_CHIP_ID_REV 0x30 #define CRT_ID_MEMORY_CONF 0x31 #define CRT_ID_BACKWAD_COMP_1 0x32 #define CRT_ID_BACKWAD_COMP_2 0x33 #define CRT_ID_BACKWAD_COMP_3 0x34 #define CRT_ID_REGISTER_LOCK 0x35 #define CRT_ID_CONFIG_1 0x36 #define CRT_ID_CONFIG_2 0x37 #define CRT_ID_REGISTER_LOCK_1 0x38 #define CRT_ID_REGISTER_LOCK_2 0x39 #define CRT_ID_MISC_1 0x3A #define CRT_ID_DISPLAY_FIFO 0x3B #define CRT_ID_LACE_RETR_START 0x3C /* S3 extensions: System Control Registers */ #define CRT_ID_SYSTEM_CONFIG 0x40 #define CRT_ID_BIOS_FLAG 0x41 #define CRT_ID_LACE_CONTROL 0x42 #define CRT_ID_EXT_MODE 0x43 #define CRT_ID_HWGC_MODE 0x45 /* HWGC = Hardware Graphics Cursor */ #define CRT_ID_HWGC_ORIGIN_X_HI 0x46 #define CRT_ID_HWGC_ORIGIN_X_LO 0x47 #define CRT_ID_HWGC_ORIGIN_Y_HI 0x48 #define CRT_ID_HWGC_ORIGIN_Y_LO 0x49 #define CRT_ID_HWGC_FG_STACK 0x4A #define CRT_ID_HWGC_BG_STACK 0x4B #define CRT_ID_HWGC_START_AD_HI 0x4C #define CRT_ID_HWGC_START_AD_LO 0x4D #define CRT_ID_HWGC_DSTART_X 0x4E #define CRT_ID_HWGC_DSTART_Y 0x4F /* S3 extensions: System Extension Registers */ #define CRT_ID_EXT_SYS_CNTL_1 0x50 #define CRT_ID_EXT_SYS_CNTL_2 0x51 #define CRT_ID_EXT_BIOS_FLAG_1 0x52 #define CRT_ID_EXT_MEM_CNTL_1 0x53 #define CRT_ID_EXT_MEM_CNTL_2 0x54 #define CRT_ID_EXT_DAC_CNTL 0x55 #define CRT_ID_EX_SYNC_1 0x56 #define CRT_ID_EX_SYNC_2 0x57 #define CRT_ID_LAW_CNTL 0x58 /* LAW = Linear Address Window */ #define CRT_ID_LAW_POS_HI 0x59 #define CRT_ID_LAW_POS_LO 0x5A #define CRT_ID_GOUT_PORT 0x5C #define CRT_ID_EXT_HOR_OVF 0x5D #define CRT_ID_EXT_VER_OVF 0x5E #define CRT_ID_EXT_MEM_CNTL_3 0x60 #define CRT_ID_EX_SYNC_3 0x63 #define CRT_ID_EXT_MISC_CNTL 0x65 #define CRT_ID_EXT_MISC_CNTL_1 0x66 #define CRT_ID_EXT_MISC_CNTL_2 0x67 #define CRT_ID_CONFIG_3 0x68 #define CRT_ID_EXT_SYS_CNTL_3 0x69 #define CRT_ID_EXT_SYS_CNTL_4 0x6A #define CRT_ID_EXT_BIOS_FLAG_3 0x6B #define CRT_ID_EXT_BIOS_FLAG_4 0x6C /* Video DAC */ #define VDAC_ADDRESS 0x03c8 #define VDAC_ADDRESS_W 0x03c8 #define VDAC_ADDRESS_R 0x03c7 #define VDAC_STATE 0x03c7 #define VDAC_DATA 0x03c9 #define VDAC_MASK 0x03c6 #define TRIO_ACCEL /* #define TRIO_MMIO */ /* does not compute */ static wchar_t triofb_name[16] = L"S3 Trio 64"; static unsigned char Trio_colour_table [256][3]; static unsigned long TrioMem = 0; static unsigned long TrioSize = 0; static unsigned long TrioMem_phys = 0; #ifdef TRIO_MMIO static unsigned long TrioRegs = 0; #endif /* TRIO_MMIO */ long trio_memclk = 45000000; /* default (?) */ //#define vga_inb(reg) inb(reg) //#define vga_outb(reg,dat) outb((dat) & 0xff, reg) #define vga_inb(reg) in(reg) #define vga_outb(reg, dat) out(reg, dat) #ifdef TRIO_MMIO void trio_outw(unsigned long idx, unsigned short val) { writew(val, TrioRegs + idx); } unsigned short trio_inw(unsigned long idx) { return readw(TrioRegs + idx); } #else /* TRIO_MMIO */ /*void trio_outw(unsigned short idx, unsigned short val) { __asm__ volatile ("outw %0,%1" ::"a" ((unsigned short) val), "d"((unsigned short) idx)); } unsigned short trio_inw(unsigned short idx) { unsigned short val; __asm__ volatile ("inw %1,%0" :"=a" (val) :"d"((unsigned short) idx)); return val; }*/ #define trio_outw(idx, val) out16(idx, val) #define trio_inw(idx) in16(idx) #endif /* TRIO_MMIO */ void gra_outb(unsigned short idx, unsigned char val) { vga_outb(GCT_ADDRESS, idx); vga_outb(GCT_ADDRESS_W, val); } void seq_outb(unsigned short idx, unsigned char val) { vga_outb(SEQ_ADDRESS, idx); vga_outb(SEQ_ADDRESS_W, val); } void crt_outb(unsigned short idx, unsigned char val) { vga_outb(CRT_ADDRESS, idx); vga_outb(CRT_ADDRESS_W, val); } void att_outb(unsigned short idx, unsigned char val) { unsigned char tmp; tmp = vga_inb(ACT_ADDRESS_RESET); vga_outb(ACT_ADDRESS_W, idx); vga_outb(ACT_ADDRESS_W, val); } unsigned char att_inb(unsigned short idx) { vga_outb(ACT_ADDRESS_W, idx); msleep(1); //udelay(100); return vga_inb(ACT_ADDRESS_R); } unsigned char seq_inb(unsigned short idx) { vga_outb(SEQ_ADDRESS, idx); return vga_inb(SEQ_ADDRESS_R); } unsigned char crt_inb(unsigned short idx) { vga_outb(CRT_ADDRESS, idx); return vga_inb(CRT_ADDRESS_R); } unsigned char gra_inb(unsigned short idx) { vga_outb(GCT_ADDRESS, idx); return vga_inb(GCT_ADDRESS_R); } struct triofb_par { struct fb_var_screeninfo var; dword type; dword type_aux; dword visual; dword line_length; }; static struct triofb_par current_par; static int current_par_valid = 0; static int currcon = 0; //static struct display disp; //static struct fb_info fb_info; static struct fb_hwswitch { int (*init)(void); int (*encode_fix)(struct fb_fix_screeninfo *fix, struct triofb_par *par); int (*decode_var)(struct fb_var_screeninfo *var, struct triofb_par *par); int (*encode_var)(struct fb_var_screeninfo *var, struct triofb_par *par); int (*getcolreg)(dword regno, dword *red, dword *green, dword *blue, dword *transp, struct fb_info *info); int (*setcolreg)(dword regno, dword red, dword green, dword blue, dword transp, struct fb_info *info); void (*blank)(int blank); } *fbhw; struct fb_videomode { wchar_t* name; struct fb_var_screeninfo var; }; static struct fb_videomode triofb_predefined[] __initdata = { { L"640x480-8", { /* Default 8 BPP mode (trio8) */ 640, 480, 640, 480, 0, 0, 8, 0, {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, 0, 0, -1, -1, FB_ACCELF_TEXT, 39722, 40, 24, 32, 11, 96, 2, FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }}, { L"640x480-16", { /* Default 16 BPP mode (trio16) */ 640, 480, 640, 480, 0, 0, 16, 0, {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0}, 0, 0, -1, -1, FB_ACCELF_TEXT, 39722, 40, 24, 32, 11, 96, 2, FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }}, { L"800x600-8", { /* Trio 8 bpp */ 800, 600, 800, 600, 0, 0, 8, 0, {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, 0, 0, -1, -1, FB_ACCELF_TEXT, 27778, 64, 24, 22, 1, 72, 2, FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }}, { L"1024x768-8", { /* Trio 8 bpp */ 1024, 768, 1024, 768, 0, 0, 8, 0, {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, 0, 0, -1, -1, FB_ACCELF_TEXT, 16667, 224, 72, 60, 12, 168, 4, FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }} }; #define NUM_TOTAL_MODES sizeof(triofb_predefined)/sizeof(struct fb_videomode) #define TRIO8_DEFMODE (0) #define TRIO16_DEFMODE (1) static struct fb_var_screeninfo triofb_default = { /* Default 8 BPP mode (trio8) */ 640, 480, 640, 480, 0, 0, 8, 0, {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, 0, 0, -1, -1, FB_ACCELF_TEXT, 39722, 40, 24, 32, 11, 96, 2, FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }; static int Triofb_inverse = 0; void triofb_setup(char *options, int *ints); static int triofb_open(struct fb_info *info, int user); static int triofb_release(struct fb_info *info, int user); static int triofb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info); static int triofb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info); static int triofb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info); static int triofb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info); static int triofb_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info); static int triofb_pan_display(struct fb_var_screeninfo *var, int con, struct fb_info *info); static int triofb_ioctl(struct inode *inode, struct file *file, dword cmd, dword arg, int con, struct fb_info *info); void triofb_init(void); static int Triofb_switch(int con, struct fb_info *info); static int Triofb_updatevar(int con, struct fb_info *info); static void Triofb_blank(int blank, struct fb_info *info); static int Trio_init(void); static int Trio_encode_fix(struct fb_fix_screeninfo *fix, struct triofb_par *par); static int Trio_decode_var(struct fb_var_screeninfo *var, struct triofb_par *par); static int Trio_encode_var(struct fb_var_screeninfo *var, struct triofb_par *par); static int Trio_getcolreg(dword regno, dword *red, dword *green, dword *blue, dword *transp, struct fb_info *info); static int Trio_setcolreg(dword regno, dword red, dword green, dword blue, dword transp, struct fb_info *info); static void Trio_blank(int blank); static void triofb_get_par(struct triofb_par *par); static void triofb_set_par(struct triofb_par *par); static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive); static void do_install_cmap(int con, struct fb_info *info); static void triofb_set_disp(int con, struct fb_info *info); static int get_video_mode(const char *name); static unsigned short trio_compute_clock(unsigned long); static void trio_load_video_mode (struct fb_var_screeninfo *); inline void trio_video_disable(int toggle); #ifdef TRIO_ACCEL static void Trio_WaitQueue(void); static void Trio_WaitBlit(void); static void Trio_WaitIdle(void); static void Trio_BitBLT(word curx, word cury, word destx, word desty, word width, word height, word mode); static void Trio_RectFill(word x, word y, word width, word height, word color); static void Trio_MoveCursor(word x, word y); #ifdef FBCON_HAS_CFB8 static struct display_switch fbcon_trio8; #endif /* FBCON_HAS_CFB8 */ #endif /* TRIO_ACCEL */ static int Trio_init(void) { int i; unsigned char test; unsigned int clockpar; volatile dword *CursorBase; /* make sure 0x46e8 accesses are responded to */ crt_outb(CRT_ID_EXT_MISC_CNTL, crt_inb(CRT_ID_EXT_MISC_CNTL) & 0xfb); vga_outb(S3_VIDEO_SUBS_ENABLE, 0x10); vga_outb(S3_OPTION_SELECT, 0x01); vga_outb(S3_VIDEO_SUBS_ENABLE, 0x08); out16(S3_SUBSYS_CNTL, 0x8000); in16(S3_SUBSYS_CNTL);/* reset accelerator */ out16(S3_SUBSYS_CNTL, 0x4000); in16(S3_SUBSYS_CNTL);/* enable accelerator */ #ifdef TRIO_MMIO out16(S3_ADVFUNC_CNTL, 0x0031); crt_outb(CRT_ID_EXT_MEM_CNTL_1, 0x18); #else /* TRIO_MMIO */ out16(S3_ADVFUNC_CNTL, 0x0011); crt_outb(CRT_ID_EXT_MEM_CNTL_1, 0x00); #endif /* TRIO_MMIO */ Trio_WaitIdle(); out16(0xe000, S3_MULT_MISC); out16(0xd000, S3_MULT_MISC_2); if(TrioSize == 4096*1024) { crt_outb(CRT_ID_LAW_CNTL, 0x13); } else { crt_outb(CRT_ID_LAW_CNTL, 0x12); } seq_outb(SEQ_ID_CLOCKING_MODE, 0x01); seq_outb(SEQ_ID_MAP_MASK, 0x0f); seq_outb(SEQ_ID_CHAR_MAP_SELECT, 0x00); seq_outb(SEQ_ID_MEMORY_MODE, 0x0e); seq_outb(SEQ_ID_EXT_SEQ_REG9, 0x00); if((crt_inb(0x36) & 0x0c) == 0x0c && /* fast page mode */ (crt_inb(0x36) & 0xe0) == 0x00) { /* 4Mb buffer */ seq_outb(SEQ_ID_BUS_REQ_CNTL, seq_inb(SEQ_ID_BUS_REQ_CNTL) | 0x40); } /* Clear immediate clock load bit */ test = seq_inb(SEQ_ID_CLKSYN_CNTL_2); test = test & 0xDF; /* If > 55MHz, enable 2 cycle memory write */ if (trio_memclk >= 55000000) { test |= 0x80; } seq_outb(SEQ_ID_CLKSYN_CNTL_2, test); /* Set MCLK value */ clockpar = trio_compute_clock (trio_memclk); test = (clockpar & 0xFF00) >> 8; seq_outb(SEQ_ID_MCLK_HI, test); test = clockpar & 0xFF; seq_outb(SEQ_ID_MCLK_LO, test); /* Chip rev specific: Not in my Trio manual!!! */ if(crt_inb(CRT_ID_REVISION) == 0x10) seq_outb(SEQ_ID_MORE_MAGIC, test); /* Set DCLK value */ seq_outb(SEQ_ID_DCLK_HI, 0x13); seq_outb(SEQ_ID_DCLK_LO, 0x41); /* Load DCLK (and MCLK?) immediately */ test = seq_inb(SEQ_ID_CLKSYN_CNTL_2); test = test | 0x22; seq_outb(SEQ_ID_CLKSYN_CNTL_2, test); /* Enable loading of DCLK */ test = vga_inb(GREG_MISC_OUTPUT_R); test = test | 0x0C; vga_outb(GREG_MISC_OUTPUT_W, test); /* Turn off immediate xCLK load */ seq_outb(SEQ_ID_CLKSYN_CNTL_2, 0x2); gra_outb(GCT_ID_SET_RESET, 0x0); gra_outb(GCT_ID_ENABLE_SET_RESET, 0x0); gra_outb(GCT_ID_COLOR_COMPARE, 0x0); gra_outb(GCT_ID_DATA_ROTATE, 0x0); gra_outb(GCT_ID_READ_MAP_SELECT, 0x0); gra_outb(GCT_ID_GRAPHICS_MODE, 0x40); gra_outb(GCT_ID_MISC, 0x01); gra_outb(GCT_ID_COLOR_XCARE, 0x0F); gra_outb(GCT_ID_BITMASK, 0xFF); /* Colors for text mode */ for (i = 0; i < 0xf; i++) att_outb(i, i); att_outb(ACT_ID_ATTR_MODE_CNTL, 0x41); att_outb(ACT_ID_OVERSCAN_COLOR, 0x01); att_outb(ACT_ID_COLOR_PLANE_ENA, 0x0F); att_outb(ACT_ID_HOR_PEL_PANNING, 0x0); att_outb(ACT_ID_COLOR_SELECT, 0x0); vga_outb(VDAC_MASK, 0xFF); /* Colors initially set to grayscale */ vga_outb(VDAC_ADDRESS_W, 0); for (i = 255; i >= 0; i--) { vga_outb(VDAC_DATA, i); vga_outb(VDAC_DATA, i); vga_outb(VDAC_DATA, i); } crt_outb(CRT_ID_BACKWAD_COMP_3, 0x10); /* FIFO enabled */ crt_outb(CRT_ID_HWGC_MODE, 0x00); /* GFx hardware cursor off */ crt_outb(CRT_ID_HWGC_DSTART_X, 0x00); crt_outb(CRT_ID_HWGC_DSTART_Y, 0x00); att_outb(0x33, 0); trio_video_disable(0); /* Init local cmap as greyscale levels */ for (i = 0; i < 256; i++) { Trio_colour_table [i][0] = i; Trio_colour_table [i][1] = i; Trio_colour_table [i][2] = i; } /* Clear framebuffer memory */ for(i = 0; i < TrioSize; i++) writeb(0, TrioMem + i); /* Initialize hardware cursor */ CursorBase = (dword *)((char *)(TrioMem) + TrioSize - 0x400); #if 0 for (i=0; i < 8; i++) { *(CursorBase +(i*4)) = 0xffffff00; *(CursorBase+1+(i*4)) = 0xffff0000; *(CursorBase+2+(i*4)) = 0xffff0000; *(CursorBase+3+(i*4)) = 0xffff0000; } for (i=8; i < 64; i++) { *(CursorBase +(i*4)) = 0xffff0000; *(CursorBase+1+(i*4)) = 0xffff0000; *(CursorBase+2+(i*4)) = 0xffff0000; *(CursorBase+3+(i*4)) = 0xffff0000; } Trio_setcolreg (255, 56<<8, 100<<8, 160<<8, 0, NULL /* unused */); Trio_setcolreg (254, 0, 0, 0, 0, NULL /* unused */); #endif return 0; } static int Trio_encode_fix(struct fb_fix_screeninfo *fix, struct triofb_par *par) { memset(fix, 0, sizeof(struct fb_fix_screeninfo)); wcscpy(fix->id, triofb_name); fix->smem_start = (char*)TrioMem_phys; fix->smem_len = TrioSize; #ifdef TRIO_MMIO fix->mmio_start = (char*)TrioRegs; fix->mmio_len = 0x10000; #else /* TRIO_MMIO */ fix->mmio_start = 0; fix->mmio_len = 0; #endif /* TRIO_MMIO */ fix->type = FB_TYPE_PACKED_PIXELS; fix->type_aux = 0; if(par->var.bits_per_pixel == 15 || par->var.bits_per_pixel == 16) { fix->visual = FB_VISUAL_DIRECTCOLOR; } else { fix->visual = FB_VISUAL_PSEUDOCOLOR; } fix->xpanstep = 0; fix->ypanstep = 0; fix->ywrapstep = 0; fix->line_length = triofb_default.xres*(triofb_default.bits_per_pixel == 8 ? 1 : triofb_default.bits_per_pixel == 15 ? 2 : 2); fix->accel = FB_ACCEL_S3_TRIO64; return(0); } static int Trio_decode_var(struct fb_var_screeninfo *var, struct triofb_par *par) { if(var->bits_per_pixel != 8 && var->bits_per_pixel != 15 && var->bits_per_pixel != 16) return 1; par->var.xres = var->xres; par->var.yres = var->yres; par->var.xres_virtual = var->xres_virtual; par->var.yres_virtual = var->yres_virtual; par->var.xoffset = var->xoffset; par->var.yoffset = var->yoffset; par->var.bits_per_pixel = var->bits_per_pixel; par->var.grayscale = var->grayscale; switch(var->bits_per_pixel) { case 8: par->var.red.offset = 0; par->var.red.length = 8; par->var.red.msb_right = 0; par->var.green.offset = 0; par->var.green.length = 8; par->var.green.msb_right = 0; par->var.blue.offset = 0; par->var.blue.length = 8; par->var.blue.msb_right = 0; break; case 15: par->var.red.offset = 10; par->var.red.length = 5; par->var.red.msb_right = 0; par->var.green.offset = 5; par->var.green.length = 5; par->var.green.msb_right = 0; par->var.blue.offset = 0; par->var.blue.length = 5; par->var.blue.msb_right = 0; break; case 16: par->var.red.offset = 11; par->var.red.length = 5; par->var.red.msb_right = 0; par->var.green.offset = 5; par->var.green.length = 6; par->var.green.msb_right = 0; par->var.blue.offset = 0; par->var.blue.length = 5; par->var.blue.msb_right = 0; break; } par->var.transp.offset = 0; par->var.transp.length = 8; par->var.transp.msb_right = 0; par->var.nonstd = var->nonstd; par->var.activate = var->activate; par->var.height = var->height; par->var.width = var->width; if (var->accel_flags & FB_ACCELF_TEXT) { par->var.accel_flags = FB_ACCELF_TEXT; } else { par->var.accel_flags = 0; } par->var.pixclock = var->pixclock; par->var.left_margin = var->left_margin; par->var.right_margin = var->right_margin; par->var.upper_margin = var->upper_margin; par->var.lower_margin = var->lower_margin; par->var.hsync_len = var->hsync_len; par->var.vsync_len = var->vsync_len; par->var.sync = var->sync; par->var.vmode = var->vmode; return(0); } static int Trio_encode_var(struct fb_var_screeninfo *var, struct triofb_par *par) { var->xres = par->var.xres; var->yres = par->var.yres; var->xres_virtual = par->var.xres_virtual; var->yres_virtual = par->var.yres_virtual; var->xoffset = par->var.xoffset; var->yoffset = par->var.yoffset; var->bits_per_pixel = par->var.bits_per_pixel; var->grayscale = par->var.grayscale; var->red = par->var.red; var->green = par->var.green; var->blue = par->var.blue; var->transp = par->var.transp; var->nonstd = par->var.nonstd; var->activate = par->var.activate; var->height = par->var.height; var->width = par->var.width; var->accel_flags = par->var.accel_flags; var->pixclock = par->var.pixclock; var->left_margin = par->var.left_margin; var->right_margin = par->var.right_margin; var->upper_margin = par->var.upper_margin; var->lower_margin = par->var.lower_margin; var->hsync_len = par->var.hsync_len; var->vsync_len = par->var.vsync_len; var->sync = par->var.sync; var->vmode = par->var.vmode; return(0); } static int Trio_setcolreg(dword regno, dword red, dword green, dword blue, dword transp, struct fb_info *info) { if (regno > 255) { return (1); } vga_outb(0x3c8, (unsigned char) regno); red >>= 10; green >>= 10; blue >>= 10; Trio_colour_table [regno][0] = red; Trio_colour_table [regno][1] = green; Trio_colour_table [regno][2] = blue; vga_outb(0x3c9, red); vga_outb(0x3c9, green); vga_outb(0x3c9, blue); return (0); } static int Trio_getcolreg(dword regno, dword *red, dword *green, dword *blue, dword *transp, struct fb_info *info) { int t; if (regno > 255) { return (1); } /* ARB This shifting & oring seems VERY strange */ t = Trio_colour_table [regno][0]; *red = (t<<10) | (t<<4) | (t>>2); t = Trio_colour_table [regno][1]; *green = (t<<10) | (t<<4) | (t>>2); t = Trio_colour_table [regno][2]; *blue = (t<<10) | (t<<4) | (t>>2); *transp = 0; return (0); } void Trio_blank(int blank) { int i; if (blank) { for (i = 0; i < 256; i++) { vga_outb(0x3c8, (unsigned char) i); vga_outb(0x3c9, 0); vga_outb(0x3c9, 0); vga_outb(0x3c9, 0); } } else { for (i = 0; i < 256; i++) { vga_outb(0x3c8, (unsigned char) i); vga_outb(0x3c9, Trio_colour_table[i][0]); vga_outb(0x3c9, Trio_colour_table[i][1]); vga_outb(0x3c9, Trio_colour_table[i][2]); } } } static void Trio_MoveCursor (word x, word y) { crt_outb(CRT_ID_HWGC_ORIGIN_X_HI, (char)((x & 0x0700) >> 8)); crt_outb(CRT_ID_HWGC_ORIGIN_X_LO, (char)(x & 0x00ff)); crt_outb(CRT_ID_HWGC_ORIGIN_Y_HI, (char)((y & 0x0700) >> 8)); crt_outb(CRT_ID_HWGC_ORIGIN_Y_LO, (char)(y & 0x00ff)); } static struct fb_hwswitch Trio_switch = { Trio_init, Trio_encode_fix, Trio_decode_var, Trio_encode_var, Trio_getcolreg, Trio_setcolreg, Trio_blank }; static void triofb_get_par(struct triofb_par *par) { if (current_par_valid) { *par = current_par; } else { fbhw->decode_var(&triofb_default, par); } } static void triofb_set_par(struct triofb_par *par) { current_par = *par; current_par_valid = 1; } static void trio_set_video(struct fb_var_screeninfo *var) { /* Load the video mode defined by the 'var' data */ trio_load_video_mode (var); } static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive) { int err, activate; struct triofb_par par; if ((err = fbhw->decode_var(var, &par))) { return(err); } activate = var->activate; if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW && isactive) triofb_set_par(&par); fbhw->encode_var(var, &par); var->activate = activate; trio_set_video(var); return 0; } static void do_install_cmap(int con, struct fb_info *info) { if (con != currcon) { return; } /*if (fb_display[con].cmap.len) { fb_set_cmap(&fb_display[con].cmap, 1, fbhw->setcolreg, info); } else { fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), 1, fbhw->setcolreg, info); }*/ } static int triofb_open(struct fb_info *info, int user) { //MOD_INC_USE_COUNT; return(0); } static int triofb_release(struct fb_info *info, int user) { //MOD_DEC_USE_COUNT; return(0); } static int triofb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info) { struct triofb_par par; int error = 0; if (con == -1) { triofb_get_par(&par); } else { //error = fbhw->decode_var(&fb_display[con].var, &par); } return(error ? error : fbhw->encode_fix(fix, &par)); } static int triofb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info) { struct triofb_par par; int error = 0; if (con == -1) { triofb_get_par(&par); error = fbhw->encode_var(var, &par); //disp.var = *var; /* ++Andre: don't know if this is the right place */ } else { //*var = fb_display[con].var; } return(error); } static void triofb_set_disp(int con, struct fb_info *info) { #if 0 struct fb_fix_screeninfo fix; struct display *display; if (con >= 0) display = &fb_display[con]; else display = &disp; /* used during initialization */ triofb_get_fix(&fix, con, info); if (con == -1) con = 0; display->screen_base = (char*)TrioMem; display->visual = fix.visual; display->type = fix.type; display->type_aux = fix.type_aux; display->ypanstep = fix.ypanstep; display->ywrapstep = fix.ywrapstep; display->can_soft_blank = 1; display->inverse = Triofb_inverse; switch (display->var.bits_per_pixel) { #ifdef FBCON_HAS_CFB8 case 8: display->dispsw = &fbcon_cfb8; #ifdef TRIO_ACCEL if(display->var.accel_flags & FB_ACCELF_TEXT) display->dispsw = &fbcon_trio8; #endif /* TRIO_ACCEL */ break; #endif /* FBCON_HAS_CFB8 */ #ifdef FBCON_HAS_CFB16 case 16: display->dispsw = &fbcon_cfb16; break; #endif /* FBCON_HAS_CFB16 */ default: display->dispsw = NULL; break; } #endif } static int triofb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info) { int err;//, oldxres, oldyres, oldvxres, oldvyres, oldbpp, oldaccel; if ((err = do_fb_set_var(var, con == currcon))) { return(err); } if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { /*oldxres = fb_display[con].var.xres; oldyres = fb_display[con].var.yres; oldvxres = fb_display[con].var.xres_virtual; oldvyres = fb_display[con].var.yres_virtual; oldbpp = fb_display[con].var.bits_per_pixel; oldaccel = fb_display[con].var.accel_flags; fb_display[con].var = *var; if (oldxres != var->xres || oldyres != var->yres || oldvxres != var->xres_virtual || oldvyres != var->yres_virtual || oldbpp != var->bits_per_pixel || oldaccel != var->accel_flags) { triofb_set_disp(con, info); (*fb_info.changevar)(con); fb_alloc_cmap(&fb_display[con].cmap, 0, 0); do_install_cmap(con, info); }*/ } var->activate = 0; return(0); } static int triofb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info) { #if 0 if (con == currcon) { /* current console? */ return(fb_get_cmap(cmap, kspc, fbhw->getcolreg, info)); } else if (fb_display[con].cmap.len) { /* non default colormap? */ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); } else { fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), cmap, kspc ? 0 : 2); } #endif return(0); } static int triofb_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info) { #if 0 int err; if (!fb_display[con].cmap.len) { /* no colormap allocated? */ if ((err = fb_alloc_cmap(&fb_display[con].cmap, 1<<fb_display[con].var.bits_per_pixel, 0))) { return(err); } } if (con == currcon) { /* current console? */ return(fb_set_cmap(cmap, kspc, fbhw->setcolreg, info)); } else { fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); } #endif return(0); } static int triofb_pan_display(struct fb_var_screeninfo *var, int con, struct fb_info *info) { return 1; } static int triofb_ioctl(struct inode *inode, struct file *file, dword cmd, dword arg, int con, struct fb_info *info) { return(1); } static struct { int (*open)(struct fb_info *info, int user); int (*release)(struct fb_info *info, int user); int (*get_fix)(struct fb_fix_screeninfo *fix, int con, struct fb_info *info); int (*get_var)(struct fb_var_screeninfo *var, int con, struct fb_info *info); int (*set_var)(struct fb_var_screeninfo *var, int con, struct fb_info *info); int (*get_cmap)(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info); int (*set_cmap)(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info); int (*pan_display)(struct fb_var_screeninfo *var, int con, struct fb_info *info); int (*ioctl)(struct inode *inode, struct file *file, dword cmd, dword arg, int con, struct fb_info *info); } triofb_ops = { triofb_open, triofb_release, triofb_get_fix, triofb_get_var, triofb_set_var, triofb_get_cmap, triofb_set_cmap, triofb_pan_display, triofb_ioctl }; /*void trio_setup(char *options, int *ints) { char *this_opt; fb_info.fontname[0] = '\0'; if (!options || !*options) { return; } for (this_opt = strtok(options, ","); this_opt; this_opt = strtok(NULL, ",")) { if (!strcmp(this_opt, "inverse")) { Triofb_inverse = 1; fb_invert_cmaps(); } else if (!strncmp(this_opt, "font:", 5)) { strcpy(fb_info.fontname, this_opt+5); } else if (!strcmp (this_opt, "trio8")) { triofb_default = triofb_predefined[TRIO8_DEFMODE].var; } else if (!strcmp (this_opt, "trio16")) { triofb_default = triofb_predefined[TRIO16_DEFMODE].var; } else get_video_mode(this_opt); } }*/ void trio_init(void) { /*struct pci_dev *pdev; for(pdev = pci_devices; pdev; pdev = pdev->next) { if(((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY) && (pdev->vendor == PCI_VENDOR_ID_S3) && (pdev->device == PCI_DEVICE_ID_S3_TRIO)) {*/ struct triofb_par par; unsigned long board_addr; unsigned long board_size; unsigned char tmp; pci_cfg_t cfg; if (!pci_find(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_TRIO, &cfg)) { wprintf(L"No S3Trio device found\n"); return; } /* goto color emulation, enable CPU access */ vga_outb(GREG_MISC_OUTPUT_W, vga_inb(GREG_MISC_OUTPUT_R) | 0x03); /* unlock registers */ crt_outb(CRT_ID_REGISTER_LOCK_1, 0x48); crt_outb(CRT_ID_REGISTER_LOCK_2, 0xa5); crt_outb(CRT_ID_SYSTEM_CONFIG, crt_inb(CRT_ID_SYSTEM_CONFIG) | 0x01); crt_outb(CRT_ID_REGISTER_LOCK, crt_inb(CRT_ID_REGISTER_LOCK) & ~0x30); seq_outb(SEQ_ID_UNLOCK_EXT, 0x06); crt_outb(CRT_ID_BACKWAD_COMP_2, (crt_inb(CRT_ID_BACKWAD_COMP_2) & ~(0x2 | 0x10 | 0x40)) | 0x20); /* get Trio identification, 0x10 = trio32, 0x11 = trio64 */ if(crt_inb(CRT_ID_DEVICE_LOW) != 0x11) { wprintf(L"Not a Trio64 device\n"); return; } tmp = crt_inb(CRT_ID_CONFIG_1); board_size = (tmp & 0xe0) == 0x00 ? 4096*1024 : (tmp & 0xe0) == 0x80 ? 2048*1024 : (tmp & 0xe0) == 0xc0 ? 1024*1024 : 512*1024; //board_size = 65536; //board_addr = pdev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK; board_addr = cfg.base[0] & PCI_BASE_ADDRESS_MEM_MASK; //TrioMem = (unsigned long)ioremap_nocache(board_addr, //64*1024*1024); TrioMem = board_addr; TrioMem_phys = board_addr; TrioSize = board_size; #ifdef TRIO_MMIO TrioRegs = 0xa0000; #endif fbhw = &Trio_switch; /*strcpy(fb_info.modename, triofb_name); fb_info.changevar = NULL; fb_info.node = -1; fb_info.fbops = &triofb_ops; fb_info.disp = &disp; fb_info.switch_con = &Triofb_switch; fb_info.updatevar = &Triofb_updatevar; fb_info.blank = &Triofb_blank;*/ fbhw->init(); fbhw->decode_var(&triofb_default, &par); fbhw->encode_var(&triofb_default, &par); do_fb_set_var(&triofb_default, 1); //triofb_get_var(&fb_display[0].var, -1, &fb_info); //triofb_set_disp(-1, &fb_info); //do_install_cmap(0, &fb_info); /*if (register_framebuffer(&fb_info) < 0) { return; }*/ wprintf(L"frame buffer device, using %ldK of video memory\n", /*GET_FB_IDX(fb_info.node), fb_info.modename, */TrioSize>>10); /* TODO: This driver cannot be unloaded yet */ //MOD_INC_USE_COUNT; } //} //} static int Triofb_switch(int con, struct fb_info *info) { #if 0 /* Do we have to save the colormap? */ if (fb_display[currcon].cmap.len) { fb_get_cmap(&fb_display[currcon].cmap, 1, fbhw->getcolreg, info); } do_fb_set_var(&fb_display[con].var, 1); currcon = con; /* Install new colormap */ do_install_cmap(con, info); #endif return(0); } static int Triofb_updatevar(int con, struct fb_info *info) { return(0); } static void Triofb_blank(int blank, struct fb_info *info) { fbhw->blank(blank); } static int get_video_mode(const char *name) { int i; for (i = 0; i < NUM_TOTAL_MODES; i++) { if (!strcmp(name, triofb_predefined[i].name)) { triofb_default = triofb_predefined[i].var; return(i); } } /* ++Andre: set triofb default mode */ triofb_default = triofb_predefined[TRIO8_DEFMODE].var; return(0); } #ifdef TRIO_ACCEL static void Trio_WaitQueue(void) { word status; do { status = trio_inw(S3_GP_STAT); } while(!(status & S3_FIFO_EMPTY)); } static void Trio_WaitBlit (void) { word status; do { status = trio_inw(S3_GP_STAT); } while (status & S3_HDW_BUSY); } static void Trio_WaitIdle (void) { Trio_WaitQueue(); Trio_WaitBlit(); } static void Trio_BitBLT (word curx, word cury, word destx, word desty, word width, word height, word mode) { word blitcmd = S3_BITBLT; /* Set drawing direction */ /* -Y, X maj, -X (default) */ if (curx > destx) { blitcmd |= 0x0020; /* Drawing direction +X */ } else { curx += (width - 1); destx += (width - 1); } if (cury > desty) { blitcmd |= 0x0080; /* Drawing direction +Y */ } else { cury += (height - 1); desty += (height - 1); } Trio_WaitIdle(); trio_outw(S3_PIXEL_CNTL, 0xa000); trio_outw(S3_FRGD_MIX, 0x0060 | mode); Trio_WaitIdle(); trio_outw(S3_CUR_X, curx); trio_outw(S3_CUR_Y, cury); trio_outw(S3_DESTX_DIA, destx); trio_outw(S3_DESTY_AX, desty); trio_outw(S3_MIN_AXIS_PCNT, height - 1); trio_outw(S3_MAJ_AXIS_PCNT, width - 1); Trio_WaitIdle(); trio_outw(S3_CMD, blitcmd); } static void Trio_RectFill (word x, word y, word width, word height, word color) { Trio_WaitIdle(); trio_outw(S3_PIXEL_CNTL, 0xa000); trio_outw(S3_FRGD_COLOR, color); trio_outw(S3_FRGD_MIX, 0x0027); trio_outw(S3_WRT_MASK, 0xffff); Trio_WaitIdle(); trio_outw(S3_CUR_X, x & 0x0fff); trio_outw(S3_CUR_Y, y & 0x0fff); trio_outw(S3_MIN_AXIS_PCNT, (height - 1) & 0x0fff); trio_outw(S3_MAJ_AXIS_PCNT, (width - 1) & 0x0fff); trio_outw(S3_CMD, 0x40b1); } #ifdef FBCON_HAS_CFB8 static void fbcon_trio8_bmove(struct display *p, int sy, int sx, int dy, int dx, int height, int width) { sx *= 8; dx *= 8; width *= 8; Trio_BitBLT((word)sx, (word)(sy*fontheight(p)), (word)dx, (word)(dy*fontheight(p)), (word)width, (word)(height*fontheight(p)), (word)S3_NEW); } static void fbcon_trio8_clear(struct vc_data *conp, struct display *p, int sy, int sx, int height, int width) { unsigned char bg; sx *= 8; width *= 8; bg = attr_bgcol_ec(p,conp); Trio_RectFill((word)sx, (word)(sy*fontheight(p)), (word)width, (word)(height*fontheight(p)), (word)bg); } static void fbcon_trio8_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx) { Trio_WaitBlit(); fbcon_cfb8_putc(conp, p, c, yy, xx); } static void fbcon_trio8_putcs(struct vc_data *conp, struct display *p, const unsigned short *s, int count, int yy, int xx) { Trio_WaitBlit(); fbcon_cfb8_putcs(conp, p, s, count, yy, xx); } static void fbcon_trio8_revc(struct display *p, int xx, int yy) { Trio_WaitBlit(); fbcon_cfb8_revc(p, xx, yy); } static struct display_switch fbcon_trio8 = { fbcon_cfb8_setup, fbcon_trio8_bmove, fbcon_trio8_clear, fbcon_trio8_putc, fbcon_trio8_putcs, fbcon_trio8_revc, NULL, NULL, fbcon_cfb8_clear_margins, FONTWIDTH(8) }; #endif /* FBCON_HAS_CFB8 */ #endif /* TRIO_ACCEL */ #ifdef MODULE int init_module(void) { triofb_init(); return 0; } void cleanup_module(void) { /* Not reached because the usecount will never be decremented to zero */ unregister_framebuffer(&fb_info); /* TODO: clean up ... */ } #endif /* MODULE */ #define MAXPIXELCLOCK 135000000 /* safety */ /* Console colors */ unsigned char cvconscolors[16][3] = { /* background, foreground, hilite */ /* R G B */ {0x30, 0x30, 0x30}, {0x00, 0x00, 0x00}, {0x80, 0x00, 0x00}, {0x00, 0x80, 0x00}, {0x00, 0x00, 0x80}, {0x80, 0x80, 0x00}, {0x00, 0x80, 0x80}, {0x80, 0x00, 0x80}, {0xff, 0xff, 0xff}, {0x40, 0x40, 0x40}, {0xff, 0x00, 0x00}, {0x00, 0xff, 0x00}, {0x00, 0x00, 0xff}, {0xff, 0xff, 0x00}, {0x00, 0xff, 0xff}, {0x00, 0x00, 0xff} }; inline void trio_video_disable(int toggle) { int r; toggle &= 0x1; toggle = toggle << 5; r = (int) seq_inb(SEQ_ID_CLOCKING_MODE); r &= 0xdf; /* Set bit 5 to 0 */ seq_outb(SEQ_ID_CLOCKING_MODE, r | toggle); } /* * Computes M, N, and R values from * given input frequency. It uses a table of * precomputed values, to keep CPU time low. * * The return value consist of: * lower byte: Bits 4-0: N Divider Value * Bits 5-6: R Value for e.g. SR10 or SR12 * higher byte: Bits 0-6: M divider value for e.g. SR11 or SR13 */ static unsigned short trio_compute_clock(unsigned long freq) { /* * Calculate the video clocks; * * Fout = Fref * (M + 2) / ((N + 2) * 2^R) * * where 0 <= R <= 3, 1 <= N <= 31, 1 <= M <= 127, and * 135 MHz <= ((M + 2)*Fref) / (N + 2) <= 270 MHz * */ double Fref = 14.31818; double Fout = ((double)freq) / 1.0e6; dword N, M, R; word mnr; do_clocks: for(R = 0; R < 4; R++) if(((double)(1 << R))*Fout > 135.0 && ((double)(1 << R))*Fout <= 270.0) break; if(R == 4) { wprintf(L"TRIO driver: Unsupported clock freq %ld, using 25MHz\n", (long)(Fout*1.0e6)); Fout = 25.000; goto do_clocks; } for(N = 1; N < 32; N++) { double Ftry; M = (int)(Fout*((double)(N + 2))*((double)(1 << R))/Fref - 2.0); Ftry = ((double)(M + 2))*Fref/(((double)(N + 2))*((double)(1 << R))); if(0.995*Fout < Ftry && 1.005*Fout > Ftry) break; } if(N == 32) { wprintf(L"TRIO driver: Unsupported clock freq %ld, using 25MHz\n", (long)(Fout*1.0e6)); Fout = 25.000; goto do_clocks; } mnr = ((M & 0x7f) << 8) | ((R & 0x03) << 5) | (N & 0x1f); return mnr; } static void trio_load_video_mode (struct fb_var_screeninfo *video_mode) { int fx, fy; unsigned short mnr; unsigned char HT, HDE, HBS, HBE, HSS, HSE, VDE, VBS, VBE, VSS, VSE, VT; unsigned char FIFO; int cr50, sr15, sr18, clock_mode, test; int m, n; int tfillm, temptym; int hmul; /* ---------------- */ int xres, hfront, hsync, hback; int yres, vfront, vsync, vback; int bpp; long freq; /* ---------------- */ fx = fy = 8; /* force 8x8 font */ /* GRF - Disable interrupts */ switch (video_mode->bits_per_pixel) { case 15: case 16: hmul = 2; break; default: hmul = 1; break; } trio_video_disable(1); bpp = video_mode->bits_per_pixel; xres = video_mode->xres; hfront = video_mode->right_margin; hsync = video_mode->hsync_len; hback = video_mode->left_margin; yres = video_mode->yres; vfront = video_mode->lower_margin; vsync = video_mode->vsync_len; vback = video_mode->upper_margin; HBS = HDE = hmul*(xres)/8 - 1; HSS = hmul*(xres + hfront)/8 - 1; HSE = hmul*(xres + hfront + hsync)/8 - 1; HBE = HT = hmul*(xres + hfront + hsync + hback)/8 - 1; HBE = HSE; /* weirdness appeared if HBE = HT */ FIFO = (HBS + (HBE - 5))/2; VBS = VDE = yres - 1; VSS = yres + vfront - 1; VSE = yres + vfront + vsync - 1; VBE = VT = yres + vfront + vsync + vback - 2; crt_outb(0x11, crt_inb(0x11) & 0x7f); /* unlock crt 0-7 */ crt_outb(CRT_ID_HWGC_MODE, 0x00); crt_outb(CRT_ID_EXT_DAC_CNTL, 0x00); /* sequential addressing, chain-4 */ seq_outb(SEQ_ID_MEMORY_MODE, 0x0e); gra_outb(GCT_ID_READ_MAP_SELECT, 0x00); seq_outb(SEQ_ID_MAP_MASK, 0xff); seq_outb(SEQ_ID_CHAR_MAP_SELECT, 0x00); /* trio_compute_clock accepts arguments in Hz */ /* pixclock is in ps ... convert to Hz */ freq = (1000000000 / video_mode->pixclock) * 1000; mnr = trio_compute_clock (freq); seq_outb(SEQ_ID_DCLK_HI, ((mnr & 0xFF00) >> 8)); seq_outb(SEQ_ID_DCLK_LO, (mnr & 0xFF)); crt_outb(CRT_ID_MEMORY_CONF, 0x08); crt_outb(CRT_ID_BACKWAD_COMP_1, 0x00); /* - */ crt_outb(CRT_ID_REGISTER_LOCK, 0x00); crt_outb(CRT_ID_EXT_MODE, 0x00); /* Load display parameters into board */ crt_outb(CRT_ID_EXT_HOR_OVF, (((HT - 4) & 0x100) ? 0x01 : 0x00) | ((HDE & 0x100) ? 0x02 : 0x00) | ((HBS & 0x100) ? 0x04 : 0x00) | /* ((HBE & 0x40) ? 0x08 : 0x00) | */ ((HSS & 0x100) ? 0x10 : 0x00) | /* ((HSE & 0x20) ? 0x20 : 0x00) | */ ((FIFO & 0x100) ? 0x40 : 0x00) ); crt_outb(CRT_ID_EXT_VER_OVF, 0x40 | ((VT & 0x400) ? 0x01 : 0x00) | ((VDE & 0x400) ? 0x02 : 0x00) | ((VBS & 0x400) ? 0x04 : 0x00) | ((VSS & 0x400) ? 0x10 : 0x00) ); crt_outb(CRT_ID_HOR_TOTAL, HT - 4); crt_outb(CRT_ID_DISPLAY_FIFO, FIFO); crt_outb(CRT_ID_HOR_DISP_ENA_END, HDE); crt_outb(CRT_ID_START_HOR_BLANK, HBS); crt_outb(CRT_ID_END_HOR_BLANK, (HBE & 0x1F)); crt_outb(CRT_ID_START_HOR_RETR, HSS); crt_outb(CRT_ID_END_HOR_RETR, (HSE & 0x1F) | ((HBE & 0x20) ? 0x80 : 0x00) ); crt_outb(CRT_ID_VER_TOTAL, VT); crt_outb(CRT_ID_OVERFLOW, 0x10 | ((VT & 0x100) ? 0x01 : 0x00) | ((VDE & 0x100) ? 0x02 : 0x00) | ((VSS & 0x100) ? 0x04 : 0x00) | ((VBS & 0x100) ? 0x08 : 0x00) | ((VT & 0x200) ? 0x20 : 0x00) | ((VDE & 0x200) ? 0x40 : 0x00) | ((VSS & 0x200) ? 0x80 : 0x00) ); crt_outb(CRT_ID_MAX_SCAN_LINE, 0x40 | ((VBS & 0x200) ? 0x20 : 0x00) ); crt_outb(CRT_ID_MODE_CONTROL, 0xe3); /* . */ crt_outb(CRT_ID_UNDERLINE_LOC, 0x00); /* start address */ crt_outb(CRT_ID_EXT_SYS_CNTL_3, 0x00); crt_outb(CRT_ID_START_ADDR_HIGH, 0x00); crt_outb(CRT_ID_START_ADDR_LOW, 0x00); crt_outb(CRT_ID_START_VER_RETR, VSS); crt_outb(CRT_ID_END_VER_RETR, (VSE & 0x0F)); crt_outb(CRT_ID_VER_DISP_ENA_END, VDE); crt_outb(CRT_ID_START_VER_BLANK, VBS); crt_outb(CRT_ID_END_VER_BLANK, VBE); crt_outb(CRT_ID_LINE_COMPARE, 0xFF); crt_outb(CRT_ID_LACE_RETR_START, HT / 2); crt_outb(CRT_ID_LACE_CONTROL, 0x00); gra_outb(GCT_ID_GRAPHICS_MODE, 0x40); gra_outb(GCT_ID_MISC, 0x01); seq_outb(SEQ_ID_MEMORY_MODE, 0x02); vga_outb(VDAC_MASK, 0xFF); /* Blank border */ test = crt_inb(CRT_ID_BACKWAD_COMP_2); crt_outb(CRT_ID_BACKWAD_COMP_2, test | 0x20); /* - */ sr15 = seq_inb(SEQ_ID_CLKSYN_CNTL_2); sr15 &= 0xEF; sr18 = seq_inb(SEQ_ID_RAMDAC_CNTL); sr18 &= 0x7F; clock_mode = 0x00; cr50 = 0x00; test = crt_inb(CRT_ID_EXT_MISC_CNTL_2); test &= 0xD; switch (video_mode->bits_per_pixel) { case 8: if (freq > 80000000) { clock_mode = 0x10 | 0x02; sr15 |= 0x10; sr18 |= 0x80; } HDE = video_mode->xres/8; cr50 |= 0x00; break; case 15: clock_mode = 0x30; HDE = video_mode->xres / 4; cr50 |= 0x10; break; case 16: clock_mode = 0x50; HDE = video_mode->xres / 4; cr50 |= 0x10; break; } crt_outb(CRT_ID_EXT_MISC_CNTL_2, clock_mode | test); seq_outb(SEQ_ID_CLKSYN_CNTL_2, sr15); seq_outb(SEQ_ID_RAMDAC_CNTL, sr18); crt_outb(CRT_ID_SCREEN_OFFSET, HDE); crt_outb(CRT_ID_MISC_1, 0xb5); test = (HDE >> 4) & 0x30; crt_outb(CRT_ID_EXT_SYS_CNTL_2, test); /* Set up graphics engine */ switch (video_mode->xres) { case 1024: cr50 |= 0x00; break; case 640: cr50 |= 0x40; break; case 800: cr50 |= 0x80; break; case 1280: cr50 |= 0xC0; break; case 1152: cr50 |= 0x01; break; case 1600: cr50 |= 0x81; break; default: /* XXX */ break; } crt_outb(CRT_ID_EXT_SYS_CNTL_1, cr50); att_outb(ACT_ID_ATTR_MODE_CNTL, 0x41); att_outb(ACT_ID_COLOR_PLANE_ENA, 0x0f); tfillm = (96 * (trio_memclk / 1000)) / 240000; switch (video_mode->bits_per_pixel) { case 15: case 16: temptym = (48 * (trio_memclk / 1000)) / (freq / 1000); break; default: temptym = (96 * (trio_memclk / 1000)) / (freq / 1000); break; } m = (temptym - tfillm - 9) / 2; if (m < 0) m = 0; m = (m & 0x1F) << 3; if (m < 0x18) m = 0x18; n = 0xFF; crt_outb(CRT_ID_EXT_MEM_CNTL_2, m); crt_outb(CRT_ID_EXT_MEM_CNTL_3, n); att_outb(0x33, 0); /* Turn gfx on again */ trio_video_disable(0); #ifdef TRIO_ACCEL Trio_WaitIdle(); trio_outw(S3_FRGD_MIX, 0x0007); trio_outw(S3_WRT_MASK, 0x0027); Trio_WaitIdle(); trio_outw(S3_READ_REG_DATA, 0x1000); /* set clip rect */ trio_outw(S3_READ_REG_DATA, 0x2000); trio_outw(S3_READ_REG_DATA, 0x3fff); trio_outw(S3_READ_REG_DATA, 0x4fff); Trio_WaitIdle(); trio_outw(S3_RD_MASK, 0xffff); /* set masks */ trio_outw(S3_WRT_MASK, 0xffff); #endif /* TRIO_ACCEL */ }